Files
support_backend/src/modules/ai-support/knowledge/service/knowledge.service.ts
T
saqib mirandClaude Sonnet 5 7948182988 feat(012-admin-list-views): add GET /admin/products/:id/knowledge for governance
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>
2026-09-07 16:35:25 +05:30

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();