@@ -0,0 +1,34 @@
|
||||
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
||||
import { implement } from "@conusai-kit/core"
|
||||
import * as contract from "../contract/index.js"
|
||||
import * as tasks from "../core/tasks.js"
|
||||
import { activitiesServiceOf } from "./service.js"
|
||||
export const ACTIVITIES_PROCEDURES = [
|
||||
implement(contract.taskList, ({ ctx, input }) =>
|
||||
tasks.listTasks(activitiesServiceOf(ctx.container, ctx.kind), ctx.scope, input),
|
||||
),
|
||||
implement(contract.taskGet, ({ ctx, input }) =>
|
||||
tasks.ownedTask(activitiesServiceOf(ctx.container, ctx.kind), ctx.scope, input.taskId),
|
||||
),
|
||||
implement(contract.taskCreate, ({ ctx, input }) =>
|
||||
tasks.createTask(activitiesServiceOf(ctx.container, "effect"), ctx.scope, input),
|
||||
),
|
||||
implement(contract.taskUpdate, ({ ctx, input }) =>
|
||||
tasks.updateTask(activitiesServiceOf(ctx.container, "effect"), ctx.scope, input),
|
||||
),
|
||||
implement(contract.taskComplete, ({ ctx, input }) =>
|
||||
tasks.changeTask(activitiesServiceOf(ctx.container, "effect"), ctx.scope, input, "done"),
|
||||
),
|
||||
implement(contract.taskReopen, ({ ctx, input }) =>
|
||||
tasks.changeTask(activitiesServiceOf(ctx.container, "effect"), ctx.scope, input, "open"),
|
||||
),
|
||||
implement(contract.commentList, ({ ctx, input }) =>
|
||||
tasks.listComments(activitiesServiceOf(ctx.container, ctx.kind), ctx.scope, input),
|
||||
),
|
||||
implement(contract.commentAdd, ({ ctx, input }) =>
|
||||
tasks.addComment(activitiesServiceOf(ctx.container, "effect"), ctx.scope, input),
|
||||
),
|
||||
implement(contract.timeline, ({ ctx, input }) =>
|
||||
tasks.projectTimeline(activitiesServiceOf(ctx.container, ctx.kind), ctx.scope, input.after),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,201 @@
|
||||
// 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)
|
||||
}
|
||||
Reference in New Issue
Block a user