Files
simstudioai--sim/apps/sim/tools/trello/shared.ts
T
wehub-resource-sync d25d482dc2
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:20:55 +08:00

278 lines
7.4 KiB
TypeScript

import { isRecordLike } from '@sim/utils/object'
import type {
TrelloAction,
TrelloActionBoardTarget,
TrelloActionCardTarget,
TrelloActionListTarget,
TrelloBoard,
TrelloCard,
TrelloChecklist,
TrelloChecklistItem,
TrelloComment,
TrelloLabel,
TrelloList,
TrelloMember,
} from '@/tools/trello/types'
export const TRELLO_API_BASE_URL = 'https://api.trello.com/1'
function getRequiredString(value: unknown, field: string): string {
if (typeof value === 'string' && value.trim().length > 0) {
return value
}
throw new Error(`Trello response is missing required field: ${field}`)
}
function getOptionalString(value: unknown): string | null {
return typeof value === 'string' ? value : null
}
function getOptionalBoolean(value: unknown): boolean | null {
return typeof value === 'boolean' ? value : null
}
function getNumber(value: unknown): number {
if (typeof value === 'number' && Number.isFinite(value)) {
return value
}
if (typeof value === 'string' && value.trim().length > 0) {
const parsed = Number(value)
if (Number.isFinite(parsed)) {
return parsed
}
}
return 0
}
function getOptionalNumber(value: unknown): number | null {
if (typeof value === 'number' && Number.isFinite(value)) {
return value
}
return null
}
export function getIdArray(value: unknown): string[] {
if (!Array.isArray(value)) {
return []
}
return value.flatMap((item) => {
if (typeof item === 'string' && item.trim().length > 0) {
return [item]
}
if (isRecordLike(item) && typeof item.id === 'string' && item.id.trim().length > 0) {
return [item.id]
}
return []
})
}
function mapTrelloLabel(value: unknown): TrelloLabel | null {
if (!isRecordLike(value) || typeof value.id !== 'string') {
return null
}
return {
id: value.id,
name: typeof value.name === 'string' ? value.name : '',
color: getOptionalString(value.color),
}
}
export function mapTrelloMember(value: unknown): TrelloMember | null {
if (!isRecordLike(value) || typeof value.id !== 'string') {
return null
}
return {
id: value.id,
fullName: getOptionalString(value.fullName),
username: getOptionalString(value.username),
}
}
function mapActionCardTarget(value: unknown): TrelloActionCardTarget | null {
if (!isRecordLike(value) || typeof value.id !== 'string' || typeof value.name !== 'string') {
return null
}
return {
id: value.id,
name: value.name,
shortLink: getOptionalString(value.shortLink),
idShort: getOptionalNumber(value.idShort),
due: getOptionalString(value.due),
}
}
function mapActionBoardTarget(value: unknown): TrelloActionBoardTarget | null {
if (!isRecordLike(value) || typeof value.id !== 'string' || typeof value.name !== 'string') {
return null
}
return {
id: value.id,
name: value.name,
shortLink: getOptionalString(value.shortLink),
}
}
function mapActionListTarget(value: unknown): TrelloActionListTarget | null {
if (!isRecordLike(value) || typeof value.id !== 'string' || typeof value.name !== 'string') {
return null
}
return {
id: value.id,
name: value.name,
}
}
export function mapTrelloList(value: unknown): TrelloList {
if (!isRecordLike(value)) {
throw new Error('Trello returned an invalid list object')
}
return {
id: getRequiredString(value.id, 'id'),
name: getRequiredString(value.name, 'name'),
closed: typeof value.closed === 'boolean' ? value.closed : false,
pos: getNumber(value.pos),
idBoard: getRequiredString(value.idBoard, 'idBoard'),
}
}
export function mapTrelloCard(value: unknown): TrelloCard {
if (!isRecordLike(value)) {
throw new Error('Trello returned an invalid card object')
}
const rawLabels = Array.isArray(value.labels) ? value.labels : []
const labels = rawLabels
.map((label) => mapTrelloLabel(label))
.filter((label): label is TrelloLabel => label !== null)
const labelIds = getIdArray(value.idLabels)
if (labelIds.length === 0) {
labelIds.push(...rawLabels.filter((label): label is string => typeof label === 'string'))
}
return {
id: getRequiredString(value.id, 'id'),
name: getRequiredString(value.name, 'name'),
desc: typeof value.desc === 'string' ? value.desc : '',
url: getRequiredString(value.url, 'url'),
idBoard: getRequiredString(value.idBoard, 'idBoard'),
idList: getRequiredString(value.idList, 'idList'),
closed: typeof value.closed === 'boolean' ? value.closed : false,
labelIds,
labels,
due: getOptionalString(value.due),
dueComplete: getOptionalBoolean(value.dueComplete),
}
}
export function mapTrelloBoard(value: unknown): TrelloBoard {
if (!isRecordLike(value)) {
throw new Error('Trello returned an invalid board object')
}
return {
id: getRequiredString(value.id, 'id'),
name: getRequiredString(value.name, 'name'),
desc: typeof value.desc === 'string' ? value.desc : '',
url: getRequiredString(value.url, 'url'),
closed: typeof value.closed === 'boolean' ? value.closed : false,
idOrganization: getOptionalString(value.idOrganization),
}
}
export function mapTrelloChecklist(value: unknown): TrelloChecklist {
if (!isRecordLike(value)) {
throw new Error('Trello returned an invalid checklist object')
}
return {
id: getRequiredString(value.id, 'id'),
name: getRequiredString(value.name, 'name'),
idCard: getRequiredString(value.idCard, 'idCard'),
idBoard: getOptionalString(value.idBoard),
pos: getNumber(value.pos),
}
}
export function mapTrelloChecklistItem(value: unknown): TrelloChecklistItem {
if (!isRecordLike(value)) {
throw new Error('Trello returned an invalid checklist item object')
}
return {
id: getRequiredString(value.id, 'id'),
name: getRequiredString(value.name, 'name'),
state: getRequiredString(value.state, 'state'),
pos: getNumber(value.pos),
idChecklist: getOptionalString(value.idChecklist),
}
}
export function mapTrelloAction(value: unknown): TrelloAction {
if (!isRecordLike(value)) {
throw new Error('Trello returned an invalid action object')
}
const data = isRecordLike(value.data) ? value.data : null
return {
id: getRequiredString(value.id, 'id'),
type: getRequiredString(value.type, 'type'),
date: getRequiredString(value.date, 'date'),
idMemberCreator: getRequiredString(value.idMemberCreator, 'idMemberCreator'),
text: data ? getOptionalString(data.text) : null,
memberCreator: mapTrelloMember(value.memberCreator),
card: data ? mapActionCardTarget(data.card) : null,
board: data ? mapActionBoardTarget(data.board) : null,
list: data ? mapActionListTarget(data.list) : null,
}
}
export function mapTrelloComment(value: unknown): TrelloComment {
return mapTrelloAction(value)
}
export function extractTrelloErrorMessage(
response: Response,
data: unknown,
fallback: string
): string {
const parts: string[] = []
if (isRecordLike(data)) {
const message = data.message
const error = data.error
if (typeof message === 'string' && message.trim().length > 0) {
parts.push(message)
}
if (typeof error === 'string' && error.trim().length > 0 && error !== message) {
parts.push(error)
}
}
if (parts.length > 0) {
return `${fallback}: ${parts.join(' - ')}`
}
if (response.statusText) {
return `${fallback}: ${response.status} ${response.statusText}`
}
return fallback
}