From d331d0ccf8bb0e2de82bb98dc21e7cdab051d3e4 Mon Sep 17 00:00:00 2001 From: Emmanuel Raviart Date: Mon, 29 Aug 2022 09:37:07 +0200 Subject: [PATCH] Show CREATION targets of articles. --- src/lib/aggregates.ts | 13 ++++ src/lib/components/ArticleView.svelte | 18 +++++ src/lib/components/LienView.svelte | 92 +++++++++++++++++++++++++ src/lib/components/SectionTaView.svelte | 2 +- src/lib/components/TextelrView.svelte | 2 +- src/lib/legal.ts | 9 ++- src/routes/api/articles/[id]/+server.ts | 91 ++++++++++++++++++++++++ src/routes/articles/[id]/+page.svelte | 44 ++++++++++++ src/routes/articles/[id]/+page.ts | 78 +++++++++++++++++++++ 9 files changed, 345 insertions(+), 4 deletions(-) create mode 100644 src/lib/components/LienView.svelte create mode 100644 src/routes/api/articles/[id]/+server.ts create mode 100644 src/routes/articles/[id]/+page.svelte create mode 100644 src/routes/articles/[id]/+page.ts diff --git a/src/lib/aggregates.ts b/src/lib/aggregates.ts index dce814b..5d09d6d 100644 --- a/src/lib/aggregates.ts +++ b/src/lib/aggregates.ts @@ -1,23 +1,36 @@ import type { Article, + DossierLegislatif, + Idcc, + Jo, SectionTa, Textekali, Textelr, TexteVersion, + Versions, } from "$lib/legal" export interface Aggregate { article?: { [id: string]: Article } + dossier_legislatif?: { [id: string]: DossierLegislatif } id?: string + idcc?: { [id: string]: Idcc } ids?: string[] + jo?: { [id: string]: Jo } section_ta?: { [id: string]: SectionTa } texte_version?: { [id: string]: TexteVersion } textekali?: { [id: string]: Textekali } textelr?: { [id: string]: Textelr } + versions?: { [eli: string]: Versions } } export type Follow = typeof allFollows[number] +export interface GetArticleResult extends Aggregate { + follow: Follow[] + id: string +} + export interface GetRechercheResult extends Aggregate { follow: Follow[] id?: string diff --git a/src/lib/components/ArticleView.svelte b/src/lib/components/ArticleView.svelte index 090267c..91e8e57 100644 --- a/src/lib/components/ArticleView.svelte +++ b/src/lib/components/ArticleView.svelte @@ -1,12 +1,20 @@ @@ -17,3 +25,13 @@ {/if} {@html article.BLOC_TEXTUEL.CONTENU} + +{#if ciblesCreation.length > 0} + +{/if} diff --git a/src/lib/components/LienView.svelte b/src/lib/components/LienView.svelte new file mode 100644 index 0000000..8c416e5 --- /dev/null +++ b/src/lib/components/LienView.svelte @@ -0,0 +1,92 @@ + + +{#if componentAndProperties === undefined} +
+ + + {lien["#text"]} + +
+{:else} +
+ + {lien["#text"]} +
+ {#if open} +
+ +
+ {/if} +{/if} diff --git a/src/lib/components/SectionTaView.svelte b/src/lib/components/SectionTaView.svelte index 20aca86..1f661ac 100644 --- a/src/lib/components/SectionTaView.svelte +++ b/src/lib/components/SectionTaView.svelte @@ -28,7 +28,7 @@ {#each [...iterArrayOrSingleton(sectionTa.STRUCTURE_TA.LIEN_ART)] as lienArt} {@const article = data.article?.[lienArt["@id"]]} {#if article !== undefined} - + {/if} {/each} {:else if sectionTa.STRUCTURE_TA.LIEN_ART !== undefined} diff --git a/src/lib/components/TextelrView.svelte b/src/lib/components/TextelrView.svelte index 389dd10..ff4c2d9 100644 --- a/src/lib/components/TextelrView.svelte +++ b/src/lib/components/TextelrView.svelte @@ -16,7 +16,7 @@ {#each [...iterArrayOrSingleton(textelr.STRUCT.LIEN_ART)] as lienArt} {@const article = data.article?.[lienArt["@id"]]} {#if article !== undefined} - + {/if} {/each} {:else if textelr.STRUCT.LIEN_ART !== undefined} diff --git a/src/lib/legal.ts b/src/lib/legal.ts index 5af66a1..7c502b2 100644 --- a/src/lib/legal.ts +++ b/src/lib/legal.ts @@ -395,6 +395,9 @@ export function pathnameFromLegalId(id: string): string | undefined { if (rootType === undefined) { return undefined } + if (rootType === "article") { + return `/articles/${id}` + } if (["texte_version", "textekali", "textelr"].includes(rootType)) { return `/textes/${id}` } @@ -407,7 +410,8 @@ export function pathnameFromLegalObject( ): string { switch (type) { case "article": - return `/article/${(object as Article).META.META_COMMUN.ID}` + // return `/article/${(object as Article).META.META_COMMUN.ID}` + return `/articles/${(object as Article).META.META_COMMUN.ID}` case "dossier_legislatif": return `/dossier_legislatif/${ (object as DossierLegislatif).META.META_COMMUN.ID @@ -442,7 +446,8 @@ export function pathnameFromLegalObjectId( ): string { switch (type) { case "article": - return `/article/${id}` + // return `/article/${id}` + return `/articles/${id}` case "dossier_legislatif": return `/dossier_legislatif/${id}` case "id": diff --git a/src/routes/api/articles/[id]/+server.ts b/src/routes/api/articles/[id]/+server.ts new file mode 100644 index 0000000..c569cfd --- /dev/null +++ b/src/routes/api/articles/[id]/+server.ts @@ -0,0 +1,91 @@ +import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core" +import { error } from "@sveltejs/kit" + +import { auditFollowSearchParams } from "$lib/auditors/search_params" + +import type { Follow } from "$lib/aggregates" +import type { Article } from "$lib/legal" +import { Aggregator } from "$lib/server/aggregates" +import { db } from "$lib/server/database" + +import type { RequestHandler } from "./$types" + +export function auditSearchParams( + audit: Audit, + query: URLSearchParams, +): [unknown, unknown] { + if (query == null) { + return [query, null] + } + if (!(query instanceof URLSearchParams)) { + return audit.unexpectedType(query, "URLSearchParams") + } + + const data: { [key: string]: unknown } = {} + for (const [key, value] of query.entries()) { + let values = data[key] as string[] | undefined + if (values === undefined) { + values = data[key] = [] + } + values.push(value) + } + const errors: { [key: string]: unknown } = {} + const remainingKeys = new Set(Object.keys(data)) + + auditFollowSearchParams(audit, data, errors, remainingKeys) + + return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({})) +} + +export const GET: RequestHandler = async ({ params, url }) => { + const [query, queryError] = auditSearchParams( + cleanAudit, + url.searchParams, + ) as [ + { + follow: Set + }, + unknown, + ] + if (queryError !== null) { + console.error( + `Error in ${url.pathname} query:\n${JSON.stringify( + query, + null, + 2, + )}\n\nError:\n${JSON.stringify(queryError, null, 2)}`, + ) + throw error(400, JSON.stringify(queryError, null, 2)) + } + const { follow } = query + const article = ( + await db<{ data: Article }[]>` + SELECT data FROM article + WHERE ID = ${params.id} + ` + ).map(({ data }) => data)[0] + if (article === undefined) { + throw error(404, `ARTICLE ${params.id} non trouvé`) + } + + const aggregator = new Aggregator(follow) + aggregator.addArticle(article) + await aggregator.getAll() + + return new Response( + JSON.stringify( + { + ...aggregator.toJson(), + follow: [...follow], + id: params.id, + }, + null, + 2, + ), + { + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }, + ) +} diff --git a/src/routes/articles/[id]/+page.svelte b/src/routes/articles/[id]/+page.svelte new file mode 100644 index 0000000..7e3c1fa --- /dev/null +++ b/src/routes/articles/[id]/+page.svelte @@ -0,0 +1,44 @@ + + + + +
+

Articles

+ {#if summary !== undefined} +

+ +

+ {/if} +
+ +
+
+ +
+
+ +{#if showRawData} + +{:else} + +{/if} diff --git a/src/routes/articles/[id]/+page.ts b/src/routes/articles/[id]/+page.ts new file mode 100644 index 0000000..25101e2 --- /dev/null +++ b/src/routes/articles/[id]/+page.ts @@ -0,0 +1,78 @@ +import { error } from "@sveltejs/kit" +import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core" + +import type { Follow, GetArticleResult } from "$lib/aggregates" +import { auditFollowWithFalseSearchParams } from "$lib/auditors/search_params" +import { urlFromUrlAndQuery } from "$lib/urls" + +import type { PageLoad } from "./$types" + +export function auditSearchParams( + audit: Audit, + query: URLSearchParams, +): [unknown, unknown] { + if (query == null) { + return [query, null] + } + if (!(query instanceof URLSearchParams)) { + return audit.unexpectedType(query, "URLSearchParams") + } + + const data: { [key: string]: unknown } = {} + for (const [key, value] of query.entries()) { + let values = data[key] as string[] | undefined + if (values === undefined) { + values = data[key] = [] + } + values.push(value) + } + const errors: { [key: string]: unknown } = {} + const remainingKeys = new Set(Object.keys(data)) + + auditFollowWithFalseSearchParams(audit, data, errors, remainingKeys) + + return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({})) +} + +export const load: PageLoad = async ({ fetch, url }) => { + const [query, queryError] = auditSearchParams( + cleanAudit, + url.searchParams, + ) as [ + { + follow: Set + }, + unknown, + ] + if (queryError !== null) { + console.error( + `Error in ${url.pathname} query:\n${JSON.stringify( + query, + null, + 2, + )}\n\nError:\n${JSON.stringify(queryError, null, 2)}`, + ) + throw error(400, JSON.stringify(queryError, null, 2)) + } + const { follow: requestedFollow } = query + + let follow = new Set(requestedFollow) + const followFalse = follow.delete("false") + if (follow.size === 0 && !followFalse) { + // Set default follow + follow = new Set(["LIENS.LIEN[@sens=cible,@typelien=CREATION].@id"]) + } + + const apiUrl = urlFromUrlAndQuery(`/api${url.pathname}`, { follow }) + const response = await fetch(apiUrl, { + headers: { Accept: "application/json" }, + }) + if (!response.ok) { + const text = await response.text() + console.error( + `Error in ${url.pathname} while calling ${apiUrl}:\n${response.status} ${response.statusText}\n\n${text}`, + ) + throw error(response.status, text) + } + return (await response.json()) as GetArticleResult +}