import { WikiContributionDraft, refuseWikiContribution, } from '../services/wiki-contribution-policy'; /** * Page ajoutee au wiki global par l'assistant. * * Le wiki publie vit dans les fichiers de traduction du frontend : il est fige * au build et ne peut pas grandir pendant qu'un client pose une question. Cette * entite est l'autre moitie du wiki — celle qui s'ecrit a l'execution, quand * l'assistant rencontre un sujet d'information generale que la documentation ne * couvre pas encore. * * Elle n'est pas un brouillon : une fois creee, elle est lue par la recherche * documentaire et citee sous les reponses, comme n'importe quelle page. C'est * pour cela que sa validation (`refuseWikiContribution`) est faite ici, a la * construction, et pas laissee au bon vouloir de l'appelant. * * Elle porte son auteur : la page est globale, mais on sait toujours quelle * question l'a fait naitre. */ export interface WikiContributionProps { id: string; locale: string; /** Sujet du wiki auquel la page se rattache, ex. `douanes`. */ topic: string; title: string; /** Intitule de la section, affiche sous le titre et indexe avec lui. */ section: string; body: string; /** Compte dont la question a declenche la contribution. */ authorUserId: string; authorOrganizationId: string; createdAt: Date; updatedAt: Date; } export class WikiContributionRejected extends Error {} export class WikiContribution { private constructor(private readonly props: WikiContributionProps) {} /** * Cree une page, ou refuse le brouillon. * * Le refus est une exception et non un `null` : l'appelant est une capacite * invoquee par un modele, et le motif doit lui revenir en toutes lettres pour * qu'il l'explique a l'utilisateur au lieu de reessayer. */ static create( props: Omit & { id: string } ): WikiContribution { const draft: WikiContributionDraft = { topic: props.topic, title: props.title, section: props.section, body: props.body, }; const refusal = refuseWikiContribution(draft); if (refusal) throw new WikiContributionRejected(refusal); const now = new Date(); return new WikiContribution({ ...props, createdAt: now, updatedAt: now }); } static fromPersistence(props: WikiContributionProps): WikiContribution { return new WikiContribution(props); } /** * Remplace le corps d'une page existante. * * Un sujet deja couvert ne donne pas une seconde page : le wiki grandirait en * doublons, et la recherche citerait deux fois la meme chose. Le meme titre * dans la meme langue est donc mis a jour, pas duplique. */ revise(section: string, body: string): WikiContribution { const refusal = refuseWikiContribution({ topic: this.props.topic, title: this.props.title, section, body, }); if (refusal) throw new WikiContributionRejected(refusal); return new WikiContribution({ ...this.props, section, body, updatedAt: new Date() }); } get id(): string { return this.props.id; } get locale(): string { return this.props.locale; } get topic(): string { return this.props.topic; } get title(): string { return this.props.title; } get section(): string { return this.props.section; } get body(): string { return this.props.body; } get authorUserId(): string { return this.props.authorUserId; } get authorOrganizationId(): string { return this.props.authorOrganizationId; } get createdAt(): Date { return this.props.createdAt; } get updatedAt(): Date { return this.props.updatedAt; } /** * Lien vers la page, dans le wiki. * * Les complements tiennent sur une seule page, groupee par sujet : l'ancre * amene le lecteur au bon paragraphe, sans creer une route par contribution * ni un sujet vide pour chaque page qui n'en a pas encore. */ get href(): string { return `/dashboard/wiki/complements#${this.props.id}`; } toObject(): WikiContributionProps { return { ...this.props }; } }