/** * Work Packages Domain Service. */ import type { OpenProjectClient } from "../client/api-client.ts"; import { buildWorkPackageFilters } from "../client/filter-builder.ts"; import { normalizeWorkPackage, normalizeWorkPackageSummary, unpackCollection, } from "../client/hal-parser.ts"; import type { HalCollection, HalResource, PaginatedResult, WorkPackageDetail, WorkPackageFilterParams, WorkPackageSummary, } from "../client/types.ts"; import { resolveClient, resolveProjectId } from "./helper.ts"; export interface ListWorkPackagesParams extends WorkPackageFilterParams { pageSize?: number; offset?: number; sortBy?: string; } export interface SearchWorkPackagesOptions { projectId?: number | string; pageSize?: number; offset?: number; } /** * Lists work packages matching filters and pagination options. */ export async function listWorkPackages( params?: ListWorkPackagesParams, client?: OpenProjectClient ): Promise> { const opClient = resolveClient(client); const query: Record = {}; if (params?.pageSize !== undefined) query.pageSize = params.pageSize; if (params?.offset !== undefined) query.offset = params.offset; if (params?.sortBy !== undefined) query.sortBy = params.sortBy; if (params) { let filterParams = params; if (params.projectId !== undefined && params.projectId !== "") { const numericProjectId = await resolveProjectId(params.projectId, opClient); filterParams = { ...params, projectId: numericProjectId }; } const filters = buildWorkPackageFilters(filterParams); if (filters.length > 0) { query.filters = JSON.stringify(filters); } } const response = await opClient.get>("work_packages", query); return unpackCollection(response, normalizeWorkPackageSummary); } /** * Retrieves details for a specific work package by its numeric ID. */ export async function getWorkPackage( id: number, client?: OpenProjectClient ): Promise { const opClient = resolveClient(client); const response = await opClient.get(`work_packages/${id}`); return normalizeWorkPackage(response); } /** * Convenience helper to search work packages by subject text, optionally within a project. */ export async function searchWorkPackages( query: string, options?: SearchWorkPackagesOptions, client?: OpenProjectClient ): Promise> { return listWorkPackages( { subject: query, projectId: options?.projectId, pageSize: options?.pageSize, offset: options?.offset, }, client ); } export interface ActivityDetail { format: string; raw: string; html?: string; } export interface WorkPackageActivity { id: number; version: number; createdAt: string; user?: { id: number; name: string }; comment?: string; details: ActivityDetail[]; isComment: boolean; } export interface ListWorkPackageActivitiesOptions { workPackageId: number; onlyComments?: boolean; } /** * Lists activities (field changes, comments, status updates) for a specific work package. */ export async function listWorkPackageActivities( options: ListWorkPackageActivitiesOptions, client?: OpenProjectClient ): Promise { const opClient = resolveClient(client); const response = await opClient.get>( `/api/v3/work_packages/${options.workPackageId}/activities` ); const elements = (response._embedded as { elements?: unknown[] })?.elements ?? []; const activities: WorkPackageActivity[] = []; for (const item of elements) { const rawItem = item as Record; const commentObj = rawItem.comment as { raw?: string } | undefined; const commentRaw = typeof commentObj?.raw === "string" ? commentObj.raw.trim() : ""; const isComment = commentRaw.length > 0; if (options.onlyComments && !isComment) { continue; } const links = (rawItem._links as Record) ?? {}; const userLink = links.user; let user: { id: number; name: string } | undefined; if (userLink?.href) { const match = userLink.href.match(/\/users\/(\d+)/); if (match) { user = { id: Number(match[1]), name: userLink.title ?? `User #${match[1]}` }; } } const rawDetails = (rawItem.details as Array<{ format?: string; raw?: string; html?: string }>) ?? []; const details: ActivityDetail[] = rawDetails.map((d) => ({ format: d.format ?? "custom", raw: d.raw ?? "", html: d.html, })); activities.push({ id: Number(rawItem.id), version: Number(rawItem.version ?? 1), createdAt: String(rawItem.createdAt ?? ""), user, comment: commentRaw || undefined, details, isComment, }); } return activities; }