202 lines
6.7 KiB
TypeScript
202 lines
6.7 KiB
TypeScript
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
|
// The generated client receives only the current server-prepared binding.
|
|
import {
|
|
businessAppError,
|
|
type RequestBusinessBinding,
|
|
requestBusinessBinding,
|
|
toBusinessError,
|
|
} from "@conusai-kit/business"
|
|
import { type Container, notFound, type ProcedureKind } from "@conusai-kit/core"
|
|
import {
|
|
type Activity,
|
|
activitiesAddComment,
|
|
activitiesCompleteTask,
|
|
activitiesCreateTask,
|
|
activitiesFindActivities,
|
|
activitiesGetActivity,
|
|
activitiesListComments,
|
|
activitiesReopenTask,
|
|
activitiesTimelineFor,
|
|
activitiesUpdateActivity,
|
|
type Comment,
|
|
type Fields,
|
|
type Receipt,
|
|
} from "@conusai-sdk/activities"
|
|
import { createClient } from "@conusai-sdk/activities/client"
|
|
import type { CommentView, TaskReceipt } from "../contract/index.js"
|
|
import {
|
|
ACTIVITIES_SERVICE,
|
|
type ActivitiesReads,
|
|
type ActivitiesService,
|
|
readsOf,
|
|
type TaskRecord,
|
|
} from "../core/ports.js"
|
|
|
|
async function answer<T>(
|
|
operation: Promise<{ data?: T; error?: unknown; response?: Response }>,
|
|
): Promise<Exclude<T, undefined>> {
|
|
let result: Awaited<typeof operation>
|
|
try {
|
|
result = await operation
|
|
} catch (error) {
|
|
throw businessAppError(toBusinessError(error, undefined))
|
|
}
|
|
if (result.error !== undefined || result.data === undefined)
|
|
throw businessAppError(toBusinessError(result.error, result.response))
|
|
return result.data as Exclude<T, undefined>
|
|
}
|
|
const view = (value: Activity): TaskRecord => {
|
|
if (value.kind !== "task" || value.status === undefined || value.status === null || value.erased || value.deleted)
|
|
throw notFound()
|
|
return {
|
|
taskId: value.id,
|
|
subject: value.subject,
|
|
body: value.body ?? "",
|
|
revision: value.revision.toString(),
|
|
status: value.status,
|
|
targets: value.targets,
|
|
createdAt: value.created_at,
|
|
}
|
|
}
|
|
const commentView = (value: Comment): CommentView => ({
|
|
commentId: value.id,
|
|
body: value.erased ? "" : value.body,
|
|
author: value.author ?? null,
|
|
erased: value.erased,
|
|
})
|
|
const receiptView = (value: Receipt): TaskReceipt => ({ taskId: value.id, revision: value.revision.toString() })
|
|
const fieldsOf = (row: Activity): Fields => ({
|
|
subject: row.subject,
|
|
targets: row.targets,
|
|
...(row.body === undefined ? {} : { body: row.body }),
|
|
...(row.assignee === undefined ? {} : { assignee: row.assignee }),
|
|
...(row.due_at === undefined ? {} : { due_at: row.due_at }),
|
|
...(row.ext === undefined ? {} : { ext: row.ext }),
|
|
...(row.participants === undefined ? {} : { participants: row.participants }),
|
|
...(row.pinned === undefined ? {} : { pinned: row.pinned }),
|
|
})
|
|
|
|
/** No org-wide client constructor or deployment singleton is supported. */
|
|
export function sdkActivitiesService(binding: RequestBusinessBinding): ActivitiesService {
|
|
const client = binding.client(createClient)
|
|
const org = { org_slug: binding.orgSlug }
|
|
const get = (id: string) => answer(activitiesGetActivity({ client, path: { ...org, activity_id: id } }))
|
|
return {
|
|
bindingId: binding.bindingId,
|
|
async list(input) {
|
|
const result = await answer(
|
|
activitiesFindActivities({
|
|
client,
|
|
path: org,
|
|
query: {
|
|
target: input.target,
|
|
...(input.status ? { status: input.status } : {}),
|
|
...(input.after ? { after: input.after } : {}),
|
|
limit: 50n,
|
|
},
|
|
}),
|
|
)
|
|
return {
|
|
rows: result.data.filter((row) => row.kind === "task" && !row.erased && !row.deleted).map(view),
|
|
...(result.next_after ? { after: result.next_after } : {}),
|
|
}
|
|
},
|
|
async get(id) {
|
|
return view(await get(id))
|
|
},
|
|
async timeline(input) {
|
|
const result = await answer(
|
|
activitiesTimelineFor({
|
|
client,
|
|
path: org,
|
|
query: { target: input.target, ...(input.after ? { after: input.after } : {}), limit: 50n },
|
|
}),
|
|
)
|
|
return {
|
|
rows: result.data.filter((row) => row.kind === "task" && !row.erased && !row.deleted).map(view),
|
|
...(result.next_after ? { after: result.next_after } : {}),
|
|
}
|
|
},
|
|
async comments(input) {
|
|
const result = await answer(
|
|
activitiesListComments({
|
|
client,
|
|
path: { ...org, activity_id: input.taskId },
|
|
query: {
|
|
...(input.after ? { after: input.after } : {}),
|
|
limit: 50n,
|
|
},
|
|
}),
|
|
)
|
|
return { rows: result.data.map(commentView), ...(result.next_after ? { after: result.next_after } : {}) }
|
|
},
|
|
async create(input) {
|
|
return receiptView(
|
|
await answer(
|
|
activitiesCreateTask({
|
|
client,
|
|
path: org,
|
|
body: {
|
|
kind: "task",
|
|
idempotency_key: input.key,
|
|
fields: {
|
|
subject: input.subject,
|
|
body: input.body,
|
|
targets: [input.target],
|
|
participants: [input.participant],
|
|
},
|
|
},
|
|
}),
|
|
),
|
|
)
|
|
},
|
|
async update(input) {
|
|
const current = await get(input.task.taskId)
|
|
if (!current.targets.includes(input.target)) throw notFound()
|
|
return receiptView(
|
|
await answer(
|
|
activitiesUpdateActivity({
|
|
client,
|
|
path: { ...org, activity_id: current.id },
|
|
body: {
|
|
fields: { ...fieldsOf(current), subject: input.subject, body: input.body },
|
|
revision: BigInt(input.expectedVersion),
|
|
idempotency_key: input.key,
|
|
},
|
|
}),
|
|
),
|
|
)
|
|
},
|
|
async change(input) {
|
|
const operation = input.state === "done" ? activitiesCompleteTask : activitiesReopenTask
|
|
return receiptView(
|
|
await answer(
|
|
operation({
|
|
client,
|
|
path: { ...org, activity_id: input.taskId },
|
|
body: { revision: BigInt(input.expectedVersion), idempotency_key: input.key },
|
|
}),
|
|
),
|
|
)
|
|
},
|
|
async comment(input) {
|
|
const result = await answer(
|
|
activitiesAddComment({
|
|
client,
|
|
path: { ...org, activity_id: input.taskId },
|
|
body: { body: input.body, revision: BigInt(input.expectedVersion), idempotency_key: input.key },
|
|
}),
|
|
)
|
|
return { commentId: result.id, revision: result.revision.toString() }
|
|
},
|
|
}
|
|
}
|
|
export function activitiesServiceOf(container: Container, kind: "effect"): ActivitiesService
|
|
export function activitiesServiceOf(container: Container, kind: ProcedureKind): ActivitiesReads
|
|
export function activitiesServiceOf(container: Container, kind: ProcedureKind): ActivitiesReads {
|
|
const service = container.has(ACTIVITIES_SERVICE)
|
|
? container.resolve(ACTIVITIES_SERVICE)
|
|
: sdkActivitiesService(requestBusinessBinding(container))
|
|
return kind === "effect" ? service : readsOf(service)
|
|
}
|