Unverified Commit fe8bffa4 authored by ggrund-tsi's avatar ggrund-tsi Committed by GitHub
Browse files

Feat/new json schema (#94)



* changes for schema 1.3.0

* validation with modified schema

* release 3.1

* misc
Co-authored-by: default avatarfOppenheimer <Fatma.Krueger@T-Systems.com>
Co-authored-by: default avatarGordon Grund <gordon.grund@outlook.de>
parent d038d4e0
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://id.uvci.eu/DGC.combined-schema.json",
"title": "EU DGC",
"description": "EU Digital Green Certificate",
"$comment": "Schema version 1.0.0",
"required": [
"ver",
"nam",
"dob"
],
"$id": "https://id.uvci.eu/DCC.combined-schema.json",
"title": "EU DCC",
"description": "EU Digital Covid Certificate",
"$comment": "Schema version 1.3.0",
"type": "object",
"oneOf": [
{
"required": [
"ver",
"nam",
"dob",
"v"
]
},
{
"required": [
"ver",
"nam",
"dob",
"t"
]
},
{
"required": [
"ver",
"nam",
"dob",
"r"
]
}
],
"properties": {
"ver": {
"title": "Schema version",
......@@ -17,21 +38,23 @@
"type": "string",
"pattern": "^\\d+.\\d+.\\d+$",
"examples": [
"1.0.0"
"1.3.0"
]
},
"nam": {
"description": "Surname(s), given name(s) - in that order",
"description": "Surname(s), forename(s) - in that order",
"$ref": "#/$defs/person_name"
},
"dob": {
"title": "Date of birth",
"description": "Date of Birth of the person addressed in the DGC. ISO 8601 date format restricted to range 1900-2099",
"description": "Date of Birth of the person addressed in the DCC. ISO 8601 date format restricted to range 1900-2099 or empty",
"type": "string",
"format": "date",
"pattern": "(19|20)\\d{2}-\\d{2}-\\d{2}",
"pattern": "^((19|20)\\d\\d(-\\d\\d){0,2}){0,1}$",
"examples": [
"1979-04-14"
"1979-04-14",
"1950",
"1901-08",
""
]
},
"v": {
......@@ -40,7 +63,8 @@
"items": {
"$ref": "#/$defs/vaccination_entry"
},
"minItems": 1
"minItems": 1,
"maxItems": 1
},
"t": {
"description": "Test Group",
......@@ -48,7 +72,8 @@
"items": {
"$ref": "#/$defs/test_entry"
},
"minItems": 1
"minItems": 1,
"maxItems": 1
},
"r": {
"description": "Recovery Group",
......@@ -56,67 +81,62 @@
"items": {
"$ref": "#/$defs/recovery_entry"
},
"minItems": 1
"minItems": 1,
"maxItems": 1
}
},
"$defs": {
"dose_posint": {
"description": "Dose Number / Total doses in Series: positive integer, range: [1,9]",
"description": "Dose Number / Total doses in Series: positive integer",
"type": "integer",
"minimum": 1,
"maximum": 9
},
"country_vt": {
"description": "Country of Vaccination / Test, ISO 3166 where possible",
"type": "string",
"pattern": "[A-Z]{1,10}"
"minimum": 1
},
"issuer": {
"description": "Certificate Issuer",
"type": "string",
"maxLength": 50
"maxLength": 80
},
"person_name": {
"description": "Person name: Surname(s), given name(s) - in that order",
"description": "Person name: Surname(s), forename(s) - in that order",
"required": [
"fnt"
],
"type": "object",
"properties": {
"fn": {
"title": "Family name",
"description": "The family or primary name(s) of the person addressed in the certificate",
"title": "Surname",
"description": "The surname or primary name(s) of the person addressed in the certificate",
"type": "string",
"maxLength": 50,
"maxLength": 80,
"examples": [
"d'Červenková Panklová"
]
},
"fnt": {
"title": "Standardised family name",
"description": "The family name(s) of the person transliterated",
"title": "Standardised surname",
"description": "The surname(s) of the person, transliterated ICAO 9303",
"type": "string",
"pattern": "^[A-Z<]*$",
"maxLength": 50,
"maxLength": 80,
"examples": [
"DCERVENKOVA<PANKLOVA"
]
},
"gn": {
"title": "Given name",
"description": "The given name(s) of the person addressed in the certificate",
"title": "Forename",
"description": "The forename(s) of the person addressed in the certificate",
"type": "string",
"maxLength": 50,
"maxLength": 80,
"examples": [
"Jiřina-Maria Alena"
]
},
"gnt": {
"title": "Standardised given name",
"description": "The given name(s) of the person transliterated",
"title": "Standardised forename",
"description": "The forename(s) of the person, transliterated ICAO 9303",
"type": "string",
"pattern": "^[A-Z<]*$",
"maxLength": 50,
"maxLength": 80,
"examples": [
"JIRINA<MARIA<ALENA"
]
......@@ -126,7 +146,7 @@
"certificate_id": {
"description": "Certificate Identifier, format as per UVCI: Annex 2 in https://ec.europa.eu/health/sites/health/files/ehealth/docs/vaccination-proof_interoperability-guidelines_en.pdf",
"type": "string",
"maxLength": 50
"maxLength": 80
},
"vaccination_entry": {
"description": "Vaccination Entry",
......@@ -169,10 +189,9 @@
"$ref": "#/$defs/dose_posint"
},
"dt": {
"description": "Date of Vaccination",
"description": "ISO8601 complete date: Date of Vaccination",
"type": "string",
"format": "date",
"$comment": "SemanticSG: constrain to specific date range?"
"format": "date"
},
"co": {
"description": "Country of Vaccination",
......@@ -195,7 +214,6 @@
"tt",
"sc",
"tr",
"tc",
"co",
"is",
"ci"
......@@ -207,11 +225,12 @@
},
"tt": {
"description": "Type of Test",
"type": "string"
"$ref": "#/$defs/test-type"
},
"nm": {
"description": "NAA Test Name",
"type": "string"
"type": "string",
"maxLength": 80
},
"ma": {
"description": "RAT Test name and manufacturer",
......@@ -222,11 +241,6 @@
"type": "string",
"format": "date-time"
},
"dr": {
"description": "Date/Time of Test Result",
"type": "string",
"format": "date-time"
},
"tr": {
"description": "Test Result",
"$ref": "#/$defs/test-result"
......@@ -234,7 +248,7 @@
"tc": {
"description": "Testing Centre",
"type": "string",
"maxLength": 50
"maxLength": 80
},
"co": {
"description": "Country of Test",
......@@ -267,7 +281,7 @@
"$ref": "#/$defs/disease-agent-targeted"
},
"fr": {
"description": "ISO 8601 Date of First Positive Test Result",
"description": "ISO 8601 complete date of first positive NAA test result",
"type": "string",
"format": "date"
},
......@@ -280,12 +294,12 @@
"$ref": "#/$defs/issuer"
},
"df": {
"description": "ISO 8601 Date: Certificate Valid From",
"description": "ISO 8601 complete date: Certificate Valid From",
"type": "string",
"format": "date"
},
"du": {
"description": "Certificate Valid Until",
"description": "ISO 8601 complete date: Certificate Valid Until",
"type": "string",
"format": "date"
},
......@@ -296,34 +310,45 @@
}
},
"disease-agent-targeted": {
"description": "EU eHealthNetwork: Value Sets for Digital Green Certificates. version 1.0, 2021-04-16, section 2.1",
"description": "EU eHealthNetwork: Value Sets for Digital Covid Certificates. version 1.0, 2021-04-16, section 2.1",
"type": "string",
"valueset-uri": "valuesets/disease-agent-targeted.json"
},
"vaccine-prophylaxis": {
"description": "EU eHealthNetwork: Value Sets for Digital Green Certificates. version 1.0, 2021-04-16, section 2.2",
"description": "EU eHealthNetwork: Value Sets for Digital Covid Certificates. version 1.0, 2021-04-16, section 2.2",
"type": "string",
"valueset-uri": "valuesets/vaccine-prophylaxis.json"
},
"vaccine-medicinal-product": {
"description": "EU eHealthNetwork: Value Sets for Digital Green Certificates. version 1.0, 2021-04-16, section 2.3",
"description": "EU eHealthNetwork: Value Sets for Digital Covid Certificates. version 1.0, 2021-04-16, section 2.3",
"type": "string",
"valueset-uri": "valuesets/vaccine-medicinal-product.json"
},
"vaccine-mah-manf": {
"description": "EU eHealthNetwork: Value Sets for Digital Green Certificates. version 1.0, 2021-04-16, section 2.4",
"description": "EU eHealthNetwork: Value Sets for Digital Covid Certificates. version 1.0, 2021-04-16, section 2.4",
"type": "string",
"valueset-uri": "valuesets/vaccine-mah-manf.json"
},
"country_vt": {
"description": "Country of Vaccination / Test, ISO 3166 alpha-2 where possible",
"type": "string",
"pattern": "[A-Z]{1,10}",
"valueset-uri": "valuesets/country-2-codes.json"
},
"test-manf": {
"description": "EU eHealthNetwork: Value Sets for Digital Green Certificates. version 1.0, 2021-04-16, section 2.8",
"description": "EU eHealthNetwork: Value Sets for Digital Covid Certificates. version 1.0, 2021-04-16, section 2.8",
"type": "string",
"valueset-uri": "valuesets/test-manf.json"
},
"test-result": {
"description": "EU eHealthNetwork: Value Sets for Digital Green Certificates. version 1.0, 2021-04-16, section 2.9",
"description": "EU eHealthNetwork: Value Sets for Digital Covid Certificates. version 1.0, 2021-04-16, section 2.9",
"type": "string",
"valueset-uri": "valuesets/test-result.json"
},
"test-type": {
"description": "EU eHealthNetwork: Value Sets for Digital Covid Certificates. version 1.0, 2021-04-16, section 2.7",
"type": "string",
"valueset-uri": "valuesets/test-results.json"
"valueset-uri": "valuesets/test-type.json"
}
}
}
\ No newline at end of file
}
......@@ -215,14 +215,14 @@ export const PersonInputs = (props: any) => {
<FormGroupInput controlId='formGivenNameInput' title={t('translation:first-name')}
value={givenName}
onChange={(evt: any) => setGivenName(evt.target.value)}
maxLength={50}
maxLength={80}
/>
{/* name input */}
<FormGroupInput controlId='formNameInput' title={t('translation:name')}
value={familyName}
onChange={(evt: any) => setFamilyName(evt.target.value)}
maxLength={50}
maxLength={80}
/>
<hr />
......@@ -232,7 +232,7 @@ export const PersonInputs = (props: any) => {
value={standardisedGivenName}
onChange={(evt: any) => handleStandardisedNameChanged(evt.target.value, setStandardisedGivenName)}
pattern={utils.pattern.standardisedName}
maxLength={50}
maxLength={80}
/>
{/*standardised name input */}
......@@ -241,7 +241,7 @@ export const PersonInputs = (props: any) => {
onChange={(evt: any) => handleStandardisedNameChanged(evt.target.value, setStandardisedFamilyName)}
required
pattern={utils.pattern.standardisedName}
maxLength={50}
maxLength={80}
/>
<hr />
......@@ -274,7 +274,6 @@ export const PersonInputs = (props: any) => {
maxDate={new Date()}
minDate={new Date(1900, 0, 1, 12)}
openToDate={new Date(1990, 0, 1)}
required
/>
</Col>
</Form.Group>
......
......@@ -32,7 +32,7 @@ import flag_seperator from '../assets/images/flag_seperator.png';
import yellow_seperator from '../assets/images/yellow_seperator.png';
import folding_instruction from '../assets/images/folding-instruction.png';
import { EUDGC, RecoveryEntry, TestEntry, VaccinationEntry } from '../generated-files/dgc-combined-schema';
import { EUDCC1, RecoveryEntry, TestEntry, VaccinationEntry } from '../generated-files/dgc-combined-schema';
import {
useGetDiseaseAgents, useGetVaccineManufacturers, useGetVaccines,
useGetVaccinMedicalData, useGetTestManufacturers, useGetTestResult, useGetTestType
......@@ -87,7 +87,7 @@ interface IPageParameter {
space: number
}
const usePdfGenerator = (qrCodeCanvasElementProp: any, eudgcProp: EUDGC | undefined, onIsInit: (isInit: boolean) => void, onIsReady: (isReady: boolean) => void) => {
const usePdfGenerator = (qrCodeCanvasElementProp: any, eudccProp: EUDCC1 | undefined, onIsInit: (isInit: boolean) => void, onIsReady: (isReady: boolean) => void) => {
const { t } = useTranslation();
const french = i18n.getDataByLanguage('fr');
......@@ -153,7 +153,7 @@ const usePdfGenerator = (qrCodeCanvasElementProp: any, eudgcProp: EUDGC | undefi
const [pdf, setPdf] = React.useState<jsPDF>();
const [eudgc, setEudgc] = React.useState<EUDGC>();
const [eudcc, setEudcc] = React.useState<EUDCC1>();
const [vaccinationSet, setVaccinationSet] = React.useState<VaccinationEntry>();
const [testSet, setTestSet] = React.useState<TestEntry>();
const [recoverySet, setRecoverySet] = React.useState<RecoveryEntry>();
......@@ -206,15 +206,20 @@ const usePdfGenerator = (qrCodeCanvasElementProp: any, eudgcProp: EUDGC | undefi
}
}, [firstPageIsReady, secondPageIsReady, thirdPageIsReady, fourthPageIsReady])
// on receiving eudgc obj set specific ValueSet
// on receiving eudcc obj set specific ValueSet
React.useEffect(() => {
if (eudgcProp) {
setEudgc(eudgcProp);
setVaccinationSet(eudgcProp.v ? eudgcProp.v[0] : undefined);
setTestSet(eudgcProp.t ? eudgcProp.t[0] : undefined);
setRecoverySet(eudgcProp.r ? eudgcProp.r[0] : undefined);
if (eudccProp) {
setEudcc(eudccProp);
const vacc : [VaccinationEntry] = eudccProp.v as [VaccinationEntry];
const test : [TestEntry] = eudccProp.t as [TestEntry];
const recovery: [RecoveryEntry] = eudccProp.r as [RecoveryEntry];
setVaccinationSet(vacc ? vacc[0] : undefined);
setTestSet(test ? test[0] : undefined);
setRecoverySet(recovery ? recovery[0] : undefined);
}
}, [eudgcProp])
}, [eudccProp])
// set qrcode element from props
React.useEffect(() => {
......@@ -254,18 +259,18 @@ const usePdfGenerator = (qrCodeCanvasElementProp: any, eudgcProp: EUDGC | undefi
}, [recoverySet, isInit])
React.useEffect(() => {
if (qrCodeCanvasElement && ci && isInit && eudgc) {
if (qrCodeCanvasElement && ci && isInit && eudcc) {
prepareSecondPage();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [qrCodeCanvasElement, ci, isInit, eudgc]);
}, [qrCodeCanvasElement, ci, isInit, eudcc]);
React.useEffect(() => {
if (co && isInit && eudgc) {
if (co && isInit && eudcc) {
prepareFirstPage();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [co, isInit, eudgc]);
}, [co, isInit, eudcc]);
const printDottedLine = () => {
if (pdf) {
......@@ -308,7 +313,7 @@ const usePdfGenerator = (qrCodeCanvasElementProp: any, eudgcProp: EUDGC | undefi
}
const prepareFirstPage = () => {
if (pdf && french && eudgc && co) {
if (pdf && french && eudcc && co) {
for (let page = 1; page < 3; page++) {
let x = 0;
let y = 0;
......@@ -380,7 +385,7 @@ const usePdfGenerator = (qrCodeCanvasElementProp: any, eudgcProp: EUDGC | undefi
}
const prepareSecondPage = () => {
if (pdf && eudgc && ci && qrCodeCanvasElement && french) {
if (pdf && eudcc && ci && qrCodeCanvasElement && french) {
const space = mm2point(5);
......@@ -424,12 +429,12 @@ const usePdfGenerator = (qrCodeCanvasElementProp: any, eudgcProp: EUDGC | undefi
y = printHorizontalBlockPerson(x, y,
t('translation:pdfSurname'),
french.translation.pdfSurname,
(eudgc.nam.fnt + ' ') + (eudgc.nam.gnt ? eudgc.nam.gnt : ''));
(eudcc!.nam!.fnt + ' ') + (eudcc!.nam!.gnt ? eudcc!.nam!.gnt : ''));
y = printHorizontalBlockPerson(x, y,
t('translation:pdfDateOfBirth'),
french.translation.pdfDateOfBirth,
eudgc.dob);
eudcc.dob);
y = printHorizontalBlockPerson(x, y,
t('translation:pdfCi'),
......@@ -749,12 +754,6 @@ const usePdfGenerator = (qrCodeCanvasElementProp: any, eudgcProp: EUDGC | undefi
convertDateToOutputFormat(testSet.sc),
lineHeight, true);
y = printVerticalBlock(x, y,
t('translation:pdfDateTestResult'),
french.translation.pdfDateTestResult,
testSet.dr ? convertDateToOutputFormat(testSet.dr) : '',
lineHeight, true);
y = printVerticalBlock(x, y,
t('translation:pdfTestResult'),
french.translation.pdfTestResult,
......@@ -826,12 +825,6 @@ const usePdfGenerator = (qrCodeCanvasElementProp: any, eudgcProp: EUDGC | undefi
convertDateToOutputFormat(testSet.sc),
lineHeight, true);
y = printVerticalBlockRotated(x, y,
t('translation:pdfDateTestResult'),
french.translation.pdfDateTestResult,
convertDateToOutputFormat(testSet.dr ? testSet.dr : ' '),
lineHeight, true);
y = printVerticalBlockRotated(x, y,
t('translation:pdfTestResult'),
french.translation.pdfTestResult,
......
......@@ -32,7 +32,7 @@ import Spinner from './spinner/spinner.component';
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { EUDGC, RecoveryEntry } from '../generated-files/dgc-combined-schema';
import { EUDCC1, RecoveryEntry } from '../generated-files/dgc-combined-schema';
import { useGetDiseaseAgents } from '../api';
import schema from '../generated-files/DGC.combined-schema.json';
......@@ -136,7 +136,7 @@ const RecordRecoveryCertData = (props: any) => {
const form = event.currentTarget;
if (form.checkValidity()) {
if (form.checkValidity() && person) {
const r: RecoveryEntry = {
tg: disease,
......@@ -148,16 +148,17 @@ const RecordRecoveryCertData = (props: any) => {
ci: ''
};
const eudgc: EUDGC = {
ver: '1.0.0',
const eudgc: EUDCC1 = {
ver: '1.3.0',
nam: {
fn: person!.familyName,
fnt: person!.standardisedFamilyName!,
gn: person!.givenName,
gnt: person!.standardisedGivenName
fn: person.familyName,
fnt: person.standardisedFamilyName!,
gn: person.givenName,
gnt: person.standardisedGivenName
},
dob: moment(person!.dateOfBirth!)
.format(person!.dobFormat === 'yyyy-MM-dd' ? 'yyyy-MM-DD' : person!.dobFormat),
dob: person.dateOfBirth
? moment(person.dateOfBirth).format(person.dobFormat === 'yyyy-MM-dd' ? 'yyyy-MM-DD' : person.dobFormat)
: '',
r: [r]
}
......@@ -246,7 +247,7 @@ const RecordRecoveryCertData = (props: any) => {
value={certificateIssuer}
onChange={(evt: any) => setCertificateIssuer(evt.target.value)}
required
maxLength={50}
maxLength={80}
/>
{/* Date: Certificate Valid From - To */}
......
......@@ -31,7 +31,7 @@ import Spinner from './spinner/spinner.component';
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { EUDGC, TestEntry } from '../generated-files/dgc-combined-schema';
import { EUDCC1, TestEntry } from '../generated-files/dgc-combined-schema';
import { useGetDiseaseAgents, useGetTestManufacturers, useGetTestResult, useGetTestType } from '../api';
import schema from '../generated-files/DGC.combined-schema.json';
......@@ -61,7 +61,6 @@ const RecordTestCertData = (props: any) => {
const [testManufacturers, setTestManufacturers] = useLocalStorage('testManufacturers', '');
const [sampleDateTime, setSampleDateTime] = React.useState<Date>();
const [testDateTime, setTestDateTime] = React.useState<Date | undefined>();
const [testResult, setTestResult] = React.useState<string>('');
const [testCenter, setTestCenter] = useLocalStorage('testCenter', '');
......@@ -90,10 +89,6 @@ const RecordTestCertData = (props: any) => {
setSampleDateTime(new Date(test.sc));
if (test.dr) {
setTestDateTime(new Date(test.dr));
}
setTestResult(test.tr);
setTestCenter(test.tc);
......@@ -114,11 +109,6 @@ const RecordTestCertData = (props: any) => {
setSampleDateTime(date);
}
const handleTestDateTimeChange = (evt: Date | [Date, Date] | null) => {
const date = handleDateTimeChange(evt);
setTestDateTime(date);
}
const handleDateTimeChange = (evt: Date | [Date, Date] | null) => {
let date: Date;
......@@ -142,7 +132,7 @@ const RecordTestCertData = (props: any) => {
const form = event.currentTarget;
if (form.checkValidity()) {
if (form.checkValidity() && person) {
const test: TestEntry = {
tg: disease,
......@@ -150,7 +140,6 @@ const RecordTestCertData = (props: any) => {
nm: testName ? testName : undefined,
ma: testManufacturers ? testManufacturers : undefined,
sc: sampleDateTime!.toISOString(),