99 lines
3.8 KiB
TypeScript
99 lines
3.8 KiB
TypeScript
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
|
import { actorRef, projectRef } from "@conusai-kit/business"
|
|
import { conflict, notFound, type ProjectScope } from "@conusai-kit/core"
|
|
import type { ActivitiesReads, ActivitiesService } from "./ports.js"
|
|
|
|
const keyOf = (scope: ProjectScope, operation: string, requestId: string) =>
|
|
`activities:${scope.projectId}:${operation}:${requestId}`
|
|
const targetOf = (service: ActivitiesReads, scope: ProjectScope) => projectRef(service.bindingId, scope.projectId)
|
|
/** An app project is a narrower boundary than its organization partition. */
|
|
export async function ownedTask(service: ActivitiesReads, scope: ProjectScope, taskId: string) {
|
|
const task = await service.get(taskId)
|
|
if (!task.targets.includes(targetOf(service, scope))) throw notFound()
|
|
return task
|
|
}
|
|
function currentOrRetry(actual: string, requested: string) {
|
|
// A future revision could become valid after a concurrent target move. A
|
|
// prior revision must reach the service's idempotency check on a real retry.
|
|
if (BigInt(requested) > BigInt(actual)) throw conflict("The task changed; reload before editing.")
|
|
}
|
|
export const listTasks = (
|
|
service: ActivitiesReads,
|
|
scope: ProjectScope,
|
|
input: { status?: "open" | "done" | "cancelled" | undefined; after?: string | undefined },
|
|
) =>
|
|
service.list({
|
|
target: targetOf(service, scope),
|
|
status: input.status ?? "open",
|
|
...(input.after ? { after: input.after } : {}),
|
|
})
|
|
export const projectTimeline = (service: ActivitiesReads, scope: ProjectScope, after?: string) =>
|
|
service.timeline({ target: targetOf(service, scope), ...(after ? { after } : {}) })
|
|
export const createTask = (
|
|
service: ActivitiesService,
|
|
scope: ProjectScope,
|
|
input: { subject: string; body?: string | undefined; requestId: string },
|
|
) =>
|
|
service.create({
|
|
subject: input.subject.trim(),
|
|
body: input.body ?? "",
|
|
target: targetOf(service, scope),
|
|
participant: actorRef(service.bindingId, scope.actor.userId),
|
|
key: keyOf(scope, "create", input.requestId),
|
|
})
|
|
export async function updateTask(
|
|
service: ActivitiesService,
|
|
scope: ProjectScope,
|
|
input: { taskId: string; subject: string; body: string; expectedVersion: string; requestId: string },
|
|
) {
|
|
const task = await ownedTask(service, scope, input.taskId)
|
|
currentOrRetry(task.revision, input.expectedVersion)
|
|
return service.update({
|
|
task,
|
|
target: targetOf(service, scope),
|
|
subject: input.subject.trim(),
|
|
body: input.body,
|
|
expectedVersion: input.expectedVersion,
|
|
key: keyOf(scope, "update", input.requestId),
|
|
})
|
|
}
|
|
export async function changeTask(
|
|
service: ActivitiesService,
|
|
scope: ProjectScope,
|
|
input: { taskId: string; expectedVersion: string; requestId: string },
|
|
state: "done" | "open",
|
|
) {
|
|
const task = await ownedTask(service, scope, input.taskId)
|
|
currentOrRetry(task.revision, input.expectedVersion)
|
|
return service.change({
|
|
taskId: task.taskId,
|
|
state,
|
|
expectedVersion: input.expectedVersion,
|
|
key: keyOf(scope, state, input.requestId),
|
|
})
|
|
}
|
|
export async function listComments(
|
|
service: ActivitiesReads,
|
|
scope: ProjectScope,
|
|
input: { taskId: string; after?: string | undefined },
|
|
) {
|
|
await ownedTask(service, scope, input.taskId)
|
|
const comments = await service.comments({ taskId: input.taskId, ...(input.after ? { after: input.after } : {}) })
|
|
await ownedTask(service, scope, input.taskId)
|
|
return comments
|
|
}
|
|
export async function addComment(
|
|
service: ActivitiesService,
|
|
scope: ProjectScope,
|
|
input: { taskId: string; body: string; expectedVersion: string; requestId: string },
|
|
) {
|
|
const task = await ownedTask(service, scope, input.taskId)
|
|
currentOrRetry(task.revision, input.expectedVersion)
|
|
return service.comment({
|
|
taskId: input.taskId,
|
|
body: input.body.trim(),
|
|
expectedVersion: input.expectedVersion,
|
|
key: keyOf(scope, "comment", input.requestId),
|
|
})
|
|
}
|