Add API & UI for ID & VERSIONS.

This commit is contained in:
Emmanuel 2022-08-13 14:46:39 +02:00
parent 6842108f49
commit ced57a197f
11 changed files with 454 additions and 10 deletions

View file

@ -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)
}

View file

@ -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
}

View file

@ -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",
},

View file

@ -0,0 +1,39 @@
<script lang="ts">
import { TreeView, SummaryView } from "augmented-data-viewer"
import ErrorAlert from "$lib/components/errors/ErrorAlert.svelte"
import type { IdWrapper } from "$lib/data"
import {
summarizeIdWrapperProperties,
summarizeLegalObject,
} from "$lib/summaries"
export let error: unknown
let idWrapper: IdWrapper
export { idWrapper as id }
const summary = summarizeLegalObject({ key: "id" }, "id", idWrapper)
</script>
<header class="prose my-6 max-w-full">
<h2>ID</h2>
{#if summary !== undefined}
<h1>
<SummaryView {summary} />
</h1>
{/if}
</header>
{#if error != null}
<ErrorAlert {error} />
{/if}
{#if error == null}
<TreeView
access={{ key: "article" }}
frame={false}
open
summarize={summarizeIdWrapperProperties}
value={idWrapper}
/>
{/if}

22
src/routes/id/[...eli].ts Normal file
View file

@ -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 },
}
}

View file

@ -0,0 +1,68 @@
<script lang="ts">
// import Icon from "@iconify/svelte"
// import searchIcon from "@iconify-icons/codicon/search"
import { TreeView } from "augmented-data-viewer"
// import { page } from "$app/stores"
import ErrorAlert from "$lib/components/errors/ErrorAlert.svelte"
import Pagination from "$lib/components/Pagination.svelte"
import type { IdWrapper } from "$lib/data"
import { summarizeIdWrapperProperties } from "$lib/summaries"
let idsWrapper: IdWrapper[]
export { idsWrapper as id }
export let error: unknown
</script>
<header class="prose my-6 max-w-full">
<h1>ID</h1>
</header>
<!-- <form action={$page.url.pathname} class="mx-auto max-w-sm" method="get">
<div class="form-control">
<div class="input-group">
<input
class="input input-bordered"
name="q"
type="search"
bind:value={q}
/>
<button class="btn btn-square" type="submit">
<Icon icon={searchIcon} />
</button>
</div>
</div>
<div class="form-control">
<label class="label">
<span class="label-text">Législature</span>
<select
class="select select-bordered"
name="legislature"
bind:value={legislature}
>
<option value="">Toutes</option>
{#each Object.entries(Legislature) as [key, value]}
{#if value !== "*"}
<option {value}>{key}</option>
{/if}
{/each}
</select>
</label>
</div>
</form> -->
{#if error != null}
<ErrorAlert {error} />
{/if}
{#if error == null}
<TreeView
access={{ key: "id" }}
frame={false}
open
summarize={summarizeIdWrapperProperties}
value={idsWrapper}
/>
<Pagination currentPageCount={idsWrapper.length ?? 0} />
{/if}

70
src/routes/id/index.ts Normal file
View file

@ -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<IdWrapper[]>`
SELECT eli, id FROM id
OFFSET ${offset}
LIMIT ${limit}
`
return {
headers: { "Access-Control-Allow-Origin": "*" },
body: { id: idsWrapper as unknown as JSONObject[] },
}
}

View file

@ -0,0 +1,43 @@
<script lang="ts">
import { TreeView, SummaryView } from "augmented-data-viewer"
import ErrorAlert from "$lib/components/errors/ErrorAlert.svelte"
import type { VersionsWrapper } from "$lib/data"
import {
summarizeVersionsWrapperProperties,
summarizeLegalObject,
} from "$lib/summaries"
export let error: unknown
let versionsWrapper: VersionsWrapper
export { versionsWrapper as versions }
const summary = summarizeLegalObject(
{ key: "versions" },
"versions",
versionsWrapper,
)
</script>
<header class="prose my-6 max-w-full">
<h2>VERSIONS</h2>
{#if summary !== undefined}
<h1>
<SummaryView {summary} />
</h1>
{/if}
</header>
{#if error != null}
<ErrorAlert {error} />
{/if}
{#if error == null}
<TreeView
access={{ key: "article" }}
frame={false}
open
summarize={summarizeVersionsWrapperProperties}
value={versionsWrapper}
/>
{/if}

View file

@ -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 },
}
}

View file

@ -0,0 +1,68 @@
<script lang="ts">
// import Icon from "@iconify/svelte"
// import searchIcon from "@iconify-icons/codicon/search"
import { TreeView } from "augmented-data-viewer"
// import { page } from "$app/stores"
import ErrorAlert from "$lib/components/errors/ErrorAlert.svelte"
import Pagination from "$lib/components/Pagination.svelte"
import type { VersionsWrapper } from "$lib/data"
import { summarizeVersionsWrapperProperties } from "$lib/summaries"
let versionsWrapper: VersionsWrapper[]
export { versionsWrapper as versions }
export let error: unknown
</script>
<header class="prose my-6 max-w-full">
<h1>VERSIONS</h1>
</header>
<!-- <form action={$page.url.pathname} class="mx-auto max-w-sm" method="get">
<div class="form-control">
<div class="input-group">
<input
class="input input-bordered"
name="q"
type="search"
bind:value={q}
/>
<button class="btn btn-square" type="submit">
<Icon icon={searchIcon} />
</button>
</div>
</div>
<div class="form-control">
<label class="label">
<span class="label-text">Législature</span>
<select
class="select select-bordered"
name="legislature"
bind:value={legislature}
>
<option value="">Toutes</option>
{#each Object.entries(Legislature) as [key, value]}
{#if value !== "*"}
<option {value}>{key}</option>
{/if}
{/each}
</select>
</label>
</div>
</form> -->
{#if error != null}
<ErrorAlert {error} />
{/if}
{#if error == null}
<TreeView
access={{ key: "versions" }}
frame={false}
open
summarize={summarizeVersionsWrapperProperties}
value={versionsWrapper}
/>
<Pagination currentPageCount={versionsWrapper.length ?? 0} />
{/if}

View file

@ -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<VersionsWrapper[]>`
SELECT eli, id, data FROM versions
OFFSET ${offset}
LIMIT ${limit}
`
return {
headers: { "Access-Control-Allow-Origin": "*" },
body: { versions: versionsWrapper as unknown as JSONObject[] },
}
}