Skip to content

Commit 70a7d81

Browse files
gh-PonyMPonyM
andauthored
Export jsonschema (#41)
* refactor schemas into own file for re-use in other scripts * feat: add schema export to exports folder * chore: add description to schemas - refactor labels that can be translated - add unique string for handles that are encoded in url * add history action enum since in code this is the only one handled * refactor name of generic variable string * fix: adapt variable regex to match comma and german umlauts * feat: add export test to gh pages * publish jsonschema in git - remove pushing to gh pages, use release process later * draft: add urls to docs for feature branch --------- Co-authored-by: PonyM <[email protected]>
1 parent 7a0ff00 commit 70a7d81

File tree

10 files changed

+512
-106
lines changed

10 files changed

+512
-106
lines changed

.github/workflows/test.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,14 @@ jobs:
1717
- name: Set up Node.js
1818
uses: actions/setup-node@v4
1919
with:
20-
node-version: 18
20+
node-version: 22
2121

2222
- name: Install dependencies
2323
run: npm install
2424

25+
- name: Run prettier
26+
run: npm run lint-ci
27+
continue-on-error: true
28+
2529
- name: Run tests
2630
run: npm run test

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ In diesem Repository werden die Daten für den Onlinegenerator für Datenauskunf
55

66
## Daten editieren
77
4 Datenstrukturen sind vorhanden:
8-
- Organisationen (`/data/orgs`)
9-
- Arten von Dienstleistungen/Firmen (`/data/types`)
10-
- Ereignisse (`/data/events`)
11-
- Begehren, insbes. Nachfassen (`/data/desires`)
8+
- Organisationen (`/data/orgs`) [JSONSchema](https://raw.githubusercontent.com/DigitaleGesellschaft/Datenauskunftsbegehren-Data/refs/heads/export_jsonschema/exports/OrgSchema.json) - [Daten-Dok](https://json-schema.app/view/%23?url=https%3A%2F%2Fraw.githubusercontent.com%2FDigitaleGesellschaft%2FDatenauskunftsbegehren-Data%2Frefs%2Fheads%2Fexport_jsonschema%2Fexports%2FOrgSchema.json)
9+
- Arten von Dienstleistungen/Firmen (`/data/types`) [JSONSchema](https://raw.githubusercontent.com/DigitaleGesellschaft/Datenauskunftsbegehren-Data/refs/heads/export_jsonschema/exports/TypeSchema.json) - [Daten-Dok](https://json-schema.app/view/%23?url=https%3A%2F%2Fraw.githubusercontent.com%2FDigitaleGesellschaft%2FDatenauskunftsbegehren-Data%2Frefs%2Fheads%2Fexport_jsonschema%2Fexports%2FTypeSchema.json)
10+
- Ereignisse (`/data/events`) [JSONSchema](https://raw.githubusercontent.com/DigitaleGesellschaft/Datenauskunftsbegehren-Data/refs/heads/export_jsonschema/exports/EventSchema.json) - [Daten-Dok](https://json-schema.app/view/%23?url=https%3A%2F%2Fraw.githubusercontent.com%2FDigitaleGesellschaft%2FDatenauskunftsbegehren-Data%2Frefs%2Fheads%2Fexport_jsonschema%2Fexports%2FEventSchema.json)
11+
- Begehren, insbes. Nachfassen (`/data/desires`) [JSONSchema](https://raw.githubusercontent.com/DigitaleGesellschaft/Datenauskunftsbegehren-Data/refs/heads/export_jsonschema/exports/DesireSchema.json) - [Daten-Dok](https://json-schema.app/view/%23?url=https%3A%2F%2Fraw.githubusercontent.com%2FDigitaleGesellschaft%2FDatenauskunftsbegehren-Data%2Frefs%2Fheads%2Fexport_jsonschema%2Fexports%2FDesireSchema.json)
1212

1313
Pro Organisation/Art/Ereignis wird ein `.yml` file angelegt. Der Filename ist nicht relevant.
1414

definitions/schemas.js

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
import * as z from "zod";
2+
3+
export const dateStringSchema = z.string().refine(
4+
(val) => {
5+
const date = new Date(val);
6+
return !isNaN(date.getTime()); // Checks if the date is valid
7+
},
8+
{
9+
message: "Invalid date string",
10+
},
11+
);
12+
13+
// models/Paragraph.js
14+
export const variableRegExp =
15+
/\{(?<type>string|number|tel|email|date)?(?::)?(?<name>[a-zA-Z]{1,})(?::)?(?<label>[a-zA-Z-äöüß,. ]{0,})\}/g;
16+
17+
const UniqueString = z.string().register(z.globalRegistry, {
18+
id: "unique_string",
19+
title: "A string that should be unique among each data type in data folder",
20+
});
21+
22+
const Label = z.string().register(z.globalRegistry, {
23+
id: "label",
24+
title: "A translatable label that appears on the website",
25+
});
26+
27+
export const VariableString = z
28+
.string()
29+
.refine(
30+
(val) => {
31+
const braceCount = (val.match(/\{/g) || []).length;
32+
const matches = [...val.matchAll(variableRegExp)];
33+
return matches.length === braceCount;
34+
},
35+
{
36+
message:
37+
"Inconsistent variable placeholders found. Check with the defined regex in the docs",
38+
},
39+
)
40+
.register(z.globalRegistry, {
41+
id: "variable_string",
42+
title: "Variable String",
43+
description: `A string that is used inside the letter that may contain variable placeholders. Variable placeholders must match: '${variableRegExp}'`,
44+
examples: [
45+
"Am {date:eventDate:Wann} habe ich von Ihnen ein Werbemail an die Adresse {email:eventEmail:E-Mail-Adresse} gesendet bekommen",
46+
"Unter personenbezogene Daten sind insbesondere auch die folgenden in Ihrer Datenschutzerklärung vom {string:privacyStatementDate} aufgeführten Kategorien...",
47+
],
48+
});
49+
50+
export const EventSchema = z
51+
.object({
52+
handle: UniqueString,
53+
label: Label,
54+
paragraphs: z.array(VariableString),
55+
})
56+
.register(z.globalRegistry, {
57+
title: "Trigger event for the data inquiry",
58+
description:
59+
"Event types that are displayed on the landing page as the reason for the data inquiry",
60+
});
61+
62+
export const TypeSchema = z
63+
.object({
64+
handle: UniqueString,
65+
label: Label,
66+
serviceLabel: Label,
67+
paragraphs: z.array(VariableString),
68+
})
69+
.register(z.globalRegistry, {
70+
title: "Type of Service or Service Category",
71+
description:
72+
"Schema for the types yml files. Used on the website to filter a company by service type encoded by the handle field",
73+
examples: ["Parking", "Gastronomy", "Mobile Provider", "Address Trade"],
74+
});
75+
76+
const HistoryAction = z.enum(["removed"]);
77+
78+
// models/History.js
79+
export const HistorySchema = z
80+
.object({
81+
action: HistoryAction,
82+
date: dateStringSchema,
83+
reason: z.string(),
84+
})
85+
.register(z.globalRegistry, {
86+
title: "History Record for an Org",
87+
description:
88+
"History records for keep old data but mark them as removed on organization not existing anymore",
89+
});
90+
91+
// models/Bullet.js
92+
export const BulletsSchema = z
93+
.union([
94+
z.string(), // Simple bullet
95+
z.object({
96+
text: z.string(),
97+
get bullets() {
98+
return z.array(BulletsSchema);
99+
},
100+
}), // Nested bullets
101+
])
102+
.register(z.globalRegistry, {
103+
id: "bullet",
104+
title: "A bullet point",
105+
description:
106+
"Bullet points for inline text as either a simple string or another bullet point",
107+
});
108+
109+
// models/PrivacyStatement.js
110+
export const PrivacyStatementSchema = z
111+
.object({
112+
paragraphs: z.array(VariableString).optional(),
113+
variables: z
114+
.object({
115+
privacyStatementDate: z.string(),
116+
})
117+
.optional(),
118+
bullets: z.array(BulletsSchema).optional(),
119+
})
120+
.register(z.globalRegistry, {
121+
title: "A companies privacy statement",
122+
description:
123+
"A summary of the privacy statements of a company together with an optional date when the privacy statement was in force",
124+
});
125+
126+
export const OrgType = z.enum([
127+
"gastro",
128+
"credit",
129+
"address",
130+
"online",
131+
"wlan",
132+
"mobility",
133+
"payback",
134+
"mobile",
135+
"parkingprovider",
136+
]);
137+
138+
// see svelte code models/org.js what is optional and what not
139+
export const OrgSchema = z
140+
.object({
141+
name: z.string(),
142+
address: z.string(),
143+
types: z.array(OrgType).optional(),
144+
privacyStatement: PrivacyStatementSchema.optional(),
145+
// sources is not used inside the frontend code and stripped
146+
sources: z.object({
147+
address: z.string().url(),
148+
privacyStatement: z.array(z.string().url()).optional(),
149+
}),
150+
history: z.array(HistorySchema).optional(),
151+
})
152+
.register(z.globalRegistry, {
153+
title: "Record for an organization for a yml file",
154+
description:
155+
"Data object containing necessary information for a company to construct letters",
156+
});
157+
158+
export const DesireSchema = z
159+
.object({
160+
handle: z.string(),
161+
label: z.string(),
162+
paragraphs: z.array(VariableString).optional(),
163+
})
164+
.register(z.globalRegistry, {
165+
title: "Desire schema for a yml file",
166+
description:
167+
"Defines the handle to appear in the url, the label that appears on the landing page and the paragraphs to render for the letter",
168+
});

exports/DesireSchema.json

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2020-12/schema",
3+
"type": "object",
4+
"properties": {
5+
"handle": {
6+
"type": "string"
7+
},
8+
"label": {
9+
"type": "string"
10+
},
11+
"paragraphs": {
12+
"type": "array",
13+
"items": {
14+
"$ref": "#/$defs/variable_string"
15+
}
16+
}
17+
},
18+
"required": [
19+
"handle",
20+
"label"
21+
],
22+
"additionalProperties": false,
23+
"title": "Desire schema for a yml file",
24+
"description": "Defines the handle to appear in the url, the label that appears on the landing page and the paragraphs to render for the letter",
25+
"$defs": {
26+
"variable_string": {
27+
"type": "string",
28+
"id": "variable_string",
29+
"title": "Variable String",
30+
"description": "A string that is used inside the letter that may contain variable placeholders. Variable placeholders must match: '/\\{(?<type>string|number|tel|email|date)?(?::)?(?<name>[a-zA-Z]{1,})(?::)?(?<label>[a-zA-Z-äöüß,. ]{0,})\\}/g'",
31+
"examples": [
32+
"Am {date:eventDate:Wann} habe ich von Ihnen ein Werbemail an die Adresse {email:eventEmail:E-Mail-Adresse} gesendet bekommen",
33+
"Unter personenbezogene Daten sind insbesondere auch die folgenden in Ihrer Datenschutzerklärung vom {string:privacyStatementDate} aufgeführten Kategorien..."
34+
]
35+
}
36+
}
37+
}

exports/EventSchema.json

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2020-12/schema",
3+
"type": "object",
4+
"properties": {
5+
"handle": {
6+
"$ref": "#/$defs/unique_string"
7+
},
8+
"label": {
9+
"$ref": "#/$defs/label"
10+
},
11+
"paragraphs": {
12+
"type": "array",
13+
"items": {
14+
"$ref": "#/$defs/variable_string"
15+
}
16+
}
17+
},
18+
"required": [
19+
"handle",
20+
"label",
21+
"paragraphs"
22+
],
23+
"additionalProperties": false,
24+
"title": "Trigger event for the data inquiry",
25+
"description": "Event types that are displayed on the landing page as the reason for the data inquiry",
26+
"$defs": {
27+
"unique_string": {
28+
"type": "string",
29+
"id": "unique_string",
30+
"title": "A string that should be unique among each data type in data folder"
31+
},
32+
"label": {
33+
"type": "string",
34+
"id": "label",
35+
"title": "A translatable label that appears on the website"
36+
},
37+
"variable_string": {
38+
"type": "string",
39+
"id": "variable_string",
40+
"title": "Variable String",
41+
"description": "A string that is used inside the letter that may contain variable placeholders. Variable placeholders must match: '/\\{(?<type>string|number|tel|email|date)?(?::)?(?<name>[a-zA-Z]{1,})(?::)?(?<label>[a-zA-Z-äöüß,. ]{0,})\\}/g'",
42+
"examples": [
43+
"Am {date:eventDate:Wann} habe ich von Ihnen ein Werbemail an die Adresse {email:eventEmail:E-Mail-Adresse} gesendet bekommen",
44+
"Unter personenbezogene Daten sind insbesondere auch die folgenden in Ihrer Datenschutzerklärung vom {string:privacyStatementDate} aufgeführten Kategorien..."
45+
]
46+
}
47+
}
48+
}

0 commit comments

Comments
 (0)