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
278 lines
7.4 KiB
TypeScript
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
|
|
}
|