From ced57a197f19a863e74db61e9405070a1cff1071 Mon Sep 17 00:00:00 2001 From: Emmanuel Raviart Date: Sat, 13 Aug 2022 14:46:39 +0200 Subject: [PATCH] Add API & UI for ID & VERSIONS. --- src/lib/data.ts | 25 ++++++++--- src/lib/summaries.ts | 33 +++++++++++++- src/routes/__layout.svelte | 4 +- src/routes/id/[...eli].svelte | 39 ++++++++++++++++ src/routes/id/[...eli].ts | 22 +++++++++ src/routes/id/index.svelte | 68 ++++++++++++++++++++++++++++ src/routes/id/index.ts | 70 +++++++++++++++++++++++++++++ src/routes/versions/[...eli].svelte | 43 ++++++++++++++++++ src/routes/versions/[...eli].ts | 22 +++++++++ src/routes/versions/index.svelte | 68 ++++++++++++++++++++++++++++ src/routes/versions/index.ts | 70 +++++++++++++++++++++++++++++ 11 files changed, 454 insertions(+), 10 deletions(-) create mode 100644 src/routes/id/[...eli].svelte create mode 100644 src/routes/id/[...eli].ts create mode 100644 src/routes/id/index.svelte create mode 100644 src/routes/id/index.ts create mode 100644 src/routes/versions/[...eli].svelte create mode 100644 src/routes/versions/[...eli].ts create mode 100644 src/routes/versions/index.svelte create mode 100644 src/routes/versions/index.ts diff --git a/src/lib/data.ts b/src/lib/data.ts index fae6c82..10006cf 100644 --- a/src/lib/data.ts +++ b/src/lib/data.ts @@ -31,6 +31,11 @@ export interface Contexte { export type Etat = "MODIFIE" | "VIGUEUR" +export interface IdWrapper { + eli: string + id: string +} + export interface Jo { META: { META_SPEC: { @@ -46,11 +51,12 @@ export interface Jo { export type LegalObject = | Article + | IdWrapper | Jo | SectionTa | Textelr | TexteVersion - | Versions + | VersionsWrapper export type LegalObjectType = | "article" @@ -116,6 +122,11 @@ export interface Versions { } } +export interface VersionsWrapper { + eli: string + versions: Versions +} + export interface XmlHeader { "@encoding": "UTF-8" "@version": "1.0" @@ -149,17 +160,17 @@ export function pathnameFromLegalObject( case "article": return `/article/${(object as Article).META.META_COMMUN.ID}` case "id": - return `/eli/ids/TODO` + return `/id/${(object as IdWrapper).eli}` case "jo": return `/jo/${(object as Jo).META.META_COMMUN.ID}` case "section_ta": - return `/section_ta/${(object as unknown as SectionTa).ID}` + return `/section_ta/${(object as SectionTa).ID}` case "texte_version": return `/texte_version/${(object as TexteVersion).META.META_COMMUN.ID}` case "textelr": return `/textelr/${(object as Textelr).META.META_COMMUN.ID}` case "versions": - return `/eli/versions/TODO` + return `/versions/${(object as VersionsWrapper).eli}` default: assertNeverLegalObjectType(type) } @@ -173,7 +184,8 @@ export function pathnameFromLegalObjectId( case "article": return `/article/${id}` case "id": - return `/eli/ids/TODO` + // Here, id is an ELI. + return `/id/{id}` case "jo": return `/jo/${id}` case "section_ta": @@ -183,7 +195,8 @@ export function pathnameFromLegalObjectId( case "textelr": return `/textelr/${id}` case "versions": - return `/eli/versions/TODO` + // Here, id is an ELI. + return `/versions/${id}` default: assertNeverLegalObjectType(type) } diff --git a/src/lib/summaries.ts b/src/lib/summaries.ts index eb3eb0e..fd763a1 100644 --- a/src/lib/summaries.ts +++ b/src/lib/summaries.ts @@ -5,6 +5,7 @@ import { type Article, assertNeverLegalObjectType, bestItemForDate, + type IdWrapper, type Jo, type LegalObject, type LegalObjectType, @@ -14,6 +15,7 @@ import { type Textelr, type TexteVersion, type LienArt, + type VersionsWrapper, } from "$lib/data" export const summarizeArticleProperties: Summarizer = (access, value) => { @@ -78,6 +80,17 @@ export const summarizeArticleProperties: Summarizer = (access, value) => { return undefined } +export const summarizeIdWrapperProperties: Summarizer = (access, value) => { + if (access?.key === "id" && typeof value !== "number") { + return summarizeLegalObject(access, "id", value) + } + if (typeof access?.key === "number" && access?.access?.key === "id") { + return summarizeLegalObjectToLink(access, "id", value) + } + + return undefined +} + export const summarizeJoProperties: Summarizer = (access, value) => { if (access?.key === "jo" && typeof value !== "number") { return summarizeLegalObject(access, "jo", value) @@ -136,7 +149,8 @@ export function summarizeLegalObject( } } case "id": - return `/eli/ids/TODO` + const idWrapper = value as IdWrapper | undefined + return idWrapper?.eli case "jo": { const jo = value as Jo | undefined return jo?.META.META_SPEC.META_CONTENEUR.TITRE @@ -160,7 +174,8 @@ export function summarizeLegalObject( return textelr?.META.META_COMMUN.ID } case "versions": - return `/eli/versions/TODO` + const versionsWrapper = value as VersionsWrapper | undefined + return versionsWrapper?.eli default: assertNeverLegalObjectType(type) } @@ -260,3 +275,17 @@ export const summarizeTexteVersionProperties: Summarizer = (access, value) => { return undefined } + +export const summarizeVersionsWrapperProperties: Summarizer = ( + access, + value, +) => { + if (access?.key === "versions" && typeof value !== "number") { + return summarizeLegalObject(access, "versions", value) + } + if (typeof access?.key === "number" && access?.access?.key === "versions") { + return summarizeLegalObjectToLink(access, "versions", value) + } + + return undefined +} diff --git a/src/routes/__layout.svelte b/src/routes/__layout.svelte index 13ab40c..6b07f41 100644 --- a/src/routes/__layout.svelte +++ b/src/routes/__layout.svelte @@ -30,12 +30,12 @@ { items: [ { href: "/article", label: "ARTICLE" }, - // { href: "/eli/ids", label: "ID" }, + { href: "/id", label: "ID" }, { href: "/jo", label: "JO" }, { href: "/section_ta", label: "SECTION_TA" }, { href: "/texte_version", label: "TEXTE_VERSION" }, { href: "/textelr", label: "TEXTELR" }, - // { href: "/eli/versions", label: "VERSIONS" }, + { href: "/versions", label: "VERSIONS" }, ], label: "Données", }, diff --git a/src/routes/id/[...eli].svelte b/src/routes/id/[...eli].svelte new file mode 100644 index 0000000..d4d760f --- /dev/null +++ b/src/routes/id/[...eli].svelte @@ -0,0 +1,39 @@ + + +
+

ID

+ {#if summary !== undefined} +

+ +

+ {/if} +
+ +{#if error != null} + +{/if} + +{#if error == null} + +{/if} diff --git a/src/routes/id/[...eli].ts b/src/routes/id/[...eli].ts new file mode 100644 index 0000000..356955f --- /dev/null +++ b/src/routes/id/[...eli].ts @@ -0,0 +1,22 @@ +import type { RequestHandler } from "@sveltejs/kit" +import type { JSONObject } from "@sveltejs/kit/types/private" + +import type { IdWrapper } from "$lib/data" +import { db } from "$lib/server/database" + +export const GET: RequestHandler = async ({ params }) => { + const idWrapper = ( + await db<{ data: IdWrapper }[]>` + SELECT eli, id FROM id + WHERE eli = ${params.eli} + ` + )[0] + + if (idWrapper === undefined) { + return { headers: { "Access-Control-Allow-Origin": "*" }, status: 404 } + } + return { + headers: { "Access-Control-Allow-Origin": "*" }, + body: { id: idWrapper as unknown as JSONObject }, + } +} diff --git a/src/routes/id/index.svelte b/src/routes/id/index.svelte new file mode 100644 index 0000000..cb9f5b6 --- /dev/null +++ b/src/routes/id/index.svelte @@ -0,0 +1,68 @@ + + +
+

ID

+
+ + + +{#if error != null} + +{/if} + +{#if error == null} + + + +{/if} diff --git a/src/routes/id/index.ts b/src/routes/id/index.ts new file mode 100644 index 0000000..c2c25f9 --- /dev/null +++ b/src/routes/id/index.ts @@ -0,0 +1,70 @@ +import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core" +import type { RequestHandler } from "@sveltejs/kit" +import type { JSONObject } from "@sveltejs/kit/types/private" + +import { auditSearchQueryContent } from "$lib/auditors/queries" +import type { IdWrapper } from "$lib/data" +import { db } from "$lib/server/database" + +export function auditQuery( + 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)) + + auditSearchQueryContent(audit, data, errors, remainingKeys) + + return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({})) +} + +export const GET: RequestHandler = async ({ url }) => { + const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [ + { limit: number; offset: number; q?: string }, + 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)}`, + ) + return { + // status: 400, + headers: { "Access-Control-Allow-Origin": "*" }, + body: { + error: { + query: queryError as unknown as JSONObject, + }, + }, + } + } + const { limit, offset } = query + + const idsWrapper = await db` + SELECT eli, id FROM id + OFFSET ${offset} + LIMIT ${limit} + ` + return { + headers: { "Access-Control-Allow-Origin": "*" }, + body: { id: idsWrapper as unknown as JSONObject[] }, + } +} diff --git a/src/routes/versions/[...eli].svelte b/src/routes/versions/[...eli].svelte new file mode 100644 index 0000000..c519f58 --- /dev/null +++ b/src/routes/versions/[...eli].svelte @@ -0,0 +1,43 @@ + + +
+

VERSIONS

+ {#if summary !== undefined} +

+ +

+ {/if} +
+ +{#if error != null} + +{/if} + +{#if error == null} + +{/if} diff --git a/src/routes/versions/[...eli].ts b/src/routes/versions/[...eli].ts new file mode 100644 index 0000000..32107f1 --- /dev/null +++ b/src/routes/versions/[...eli].ts @@ -0,0 +1,22 @@ +import type { RequestHandler } from "@sveltejs/kit" +import type { JSONObject } from "@sveltejs/kit/types/private" + +import type { VersionsWrapper } from "$lib/data" +import { db } from "$lib/server/database" + +export const GET: RequestHandler = async ({ params }) => { + const versionsWrapper = ( + await db<{ data: VersionsWrapper }[]>` + SELECT eli, id, data FROM versions + WHERE eli = ${params.eli} + ` + )[0] + + if (versionsWrapper === undefined) { + return { headers: { "Access-Control-Allow-Origin": "*" }, status: 404 } + } + return { + headers: { "Access-Control-Allow-Origin": "*" }, + body: { versions: versionsWrapper as unknown as JSONObject }, + } +} diff --git a/src/routes/versions/index.svelte b/src/routes/versions/index.svelte new file mode 100644 index 0000000..4773e66 --- /dev/null +++ b/src/routes/versions/index.svelte @@ -0,0 +1,68 @@ + + +
+

VERSIONS

+
+ + + +{#if error != null} + +{/if} + +{#if error == null} + + + +{/if} diff --git a/src/routes/versions/index.ts b/src/routes/versions/index.ts new file mode 100644 index 0000000..dfc81ff --- /dev/null +++ b/src/routes/versions/index.ts @@ -0,0 +1,70 @@ +import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core" +import type { RequestHandler } from "@sveltejs/kit" +import type { JSONObject } from "@sveltejs/kit/types/private" + +import { auditSearchQueryContent } from "$lib/auditors/queries" +import type { VersionsWrapper } from "$lib/data" +import { db } from "$lib/server/database" + +export function auditQuery( + 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)) + + auditSearchQueryContent(audit, data, errors, remainingKeys) + + return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({})) +} + +export const GET: RequestHandler = async ({ url }) => { + const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [ + { limit: number; offset: number; q?: string }, + 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)}`, + ) + return { + // status: 400, + headers: { "Access-Control-Allow-Origin": "*" }, + body: { + error: { + query: queryError as unknown as JSONObject, + }, + }, + } + } + const { limit, offset } = query + + const versionsWrapper = await db` + SELECT eli, id, data FROM versions + OFFSET ${offset} + LIMIT ${limit} + ` + return { + headers: { "Access-Control-Allow-Origin": "*" }, + body: { versions: versionsWrapper as unknown as JSONObject[] }, + } +}