Follow-up to 012-admin-list-views, discovered while building supporthub- web's own knowledge-governance screen (001-agent-admin-ui User Story 7): GET /knowledge/retrieve only ever returns published entries (its own AI-consumption purpose), so a governance screen that needs to see and publish a draft entry had no endpoint to list it. Adds a small admin-list-views-style read query scoped to the knowledge module itself. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
69 lines
2.5 KiB
TypeScript
69 lines
2.5 KiB
TypeScript
import { KnowledgeEntry } from '@prisma/client';
|
|
import { NotFoundError, ConflictError } from '@/common/errors';
|
|
import {
|
|
knowledgeRepository,
|
|
KnowledgeRepository,
|
|
CreateKnowledgeEntryData,
|
|
RetrieveFilters,
|
|
} from '../repository';
|
|
|
|
export class KnowledgeService {
|
|
constructor(private readonly repo: KnowledgeRepository = knowledgeRepository) {}
|
|
|
|
/** FR-002: new entries always default to status: draft, version: 1 — set by the repository. */
|
|
async create(data: CreateKnowledgeEntryData): Promise<KnowledgeEntry> {
|
|
return this.repo.create(data);
|
|
}
|
|
|
|
async publish(code: string, effectiveDate?: Date): Promise<KnowledgeEntry> {
|
|
const updated = await this.repo.publish(code, effectiveDate);
|
|
if (!updated) throw new NotFoundError('Knowledge entry not found.');
|
|
return updated;
|
|
}
|
|
|
|
async unpublish(code: string): Promise<KnowledgeEntry> {
|
|
const updated = await this.repo.unpublish(code);
|
|
if (!updated) throw new NotFoundError('Knowledge entry not found.');
|
|
return updated;
|
|
}
|
|
|
|
async setValidationStatus(code: string, validationStatus: string): Promise<KnowledgeEntry> {
|
|
const updated = await this.repo.setValidationStatus(code, validationStatus);
|
|
if (!updated) throw new NotFoundError('Knowledge entry not found.');
|
|
return updated;
|
|
}
|
|
|
|
/** FR-004: an edit always creates a new version; a stale `expectedVersion` is rejected. */
|
|
async edit(
|
|
code: string,
|
|
expectedVersion: number,
|
|
content: Omit<CreateKnowledgeEntryData, 'code' | 'productId'>,
|
|
): Promise<KnowledgeEntry> {
|
|
const updated = await this.repo.createNewVersion(code, expectedVersion, content);
|
|
if (!updated) {
|
|
throw new ConflictError(
|
|
'Knowledge entry was modified by another request — refresh and retry.',
|
|
);
|
|
}
|
|
return updated;
|
|
}
|
|
|
|
async listVersions(code: string): Promise<KnowledgeEntry[]> {
|
|
const versions = await this.repo.findAllVersionsByCode(code);
|
|
if (versions.length === 0) throw new NotFoundError('Knowledge entry not found.');
|
|
return versions;
|
|
}
|
|
|
|
async retrieve(filters: RetrieveFilters): Promise<KnowledgeEntry[]> {
|
|
return this.repo.retrieve(filters);
|
|
}
|
|
|
|
/** 012-admin-list-views follow-up: every entry for a product, any status — the governance
|
|
* screen's own data source (unlike `retrieve`, which is published-only). */
|
|
async listForGovernance(productId: string): Promise<KnowledgeEntry[]> {
|
|
return this.repo.findAllForProduct(productId);
|
|
}
|
|
}
|
|
|
|
export const knowledgeService = new KnowledgeService();
|