Add API & UI for ID & VERSIONS.
This commit is contained in:
parent
6842108f49
commit
ced57a197f
11 changed files with 454 additions and 10 deletions
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
|
|
39
src/routes/id/[...eli].svelte
Normal file
39
src/routes/id/[...eli].svelte
Normal 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
22
src/routes/id/[...eli].ts
Normal 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 },
|
||||
}
|
||||
}
|
68
src/routes/id/index.svelte
Normal file
68
src/routes/id/index.svelte
Normal 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
70
src/routes/id/index.ts
Normal 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[] },
|
||||
}
|
||||
}
|
43
src/routes/versions/[...eli].svelte
Normal file
43
src/routes/versions/[...eli].svelte
Normal 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}
|
22
src/routes/versions/[...eli].ts
Normal file
22
src/routes/versions/[...eli].ts
Normal 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 },
|
||||
}
|
||||
}
|
68
src/routes/versions/index.svelte
Normal file
68
src/routes/versions/index.svelte
Normal 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}
|
70
src/routes/versions/index.ts
Normal file
70
src/routes/versions/index.ts
Normal 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[] },
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue