diff --git a/scripts/post-install.js b/scripts/post-install.js index 06a444f..f135857 100644 --- a/scripts/post-install.js +++ b/scripts/post-install.js @@ -19,6 +19,8 @@ if (fs.existsSync(quicktype) && process.env.npm_config_production !== "true") { generate({ path: "sg/gov/tech/geekout/1.0", rootTypeName: "Geekout" }); generate({ path: "sg/gov/tech/notarise/1.0", rootTypeName: "Notarise" }); generate({ path: "sg/gov/moh/healthcert/1.0", rootTypeName: "HealthCert" }); + generate({ path: "sg/gov/moh/pdt-healthcert/1.0", rootTypeName: "PDTHealthCert" }); + generate({ path: "sg/gov/moh/vaccination-healthcert/1.0", rootTypeName: "VaccinationHealthCert" }); } else { console.log("Not running quicktype"); } diff --git a/src/index.ts b/src/index.ts index 8f35a7c..59e27f1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,7 @@ import * as geekout from "./__generated__/sg/gov/tech/geekout/1.0/schema"; import * as notarise from "./__generated__/sg/gov/tech/notarise/1.0/schema"; import * as healthcert from "./__generated__/sg/gov/moh/healthcert/1.0/schema"; +import * as pdtHealthcert from "./__generated__/sg/gov/moh/pdt-healthcert/1.0/schema"; +import * as vaccinationHealthcert from "./__generated__/sg/gov/moh/healthcert/1.0/schema"; -export { geekout, notarise, healthcert }; +export { geekout, notarise, healthcert, pdtHealthcert, vaccinationHealthcert }; diff --git a/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-endorsed-document.json b/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-endorsed-document.json new file mode 100644 index 0000000..f4dbbcb --- /dev/null +++ b/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-endorsed-document.json @@ -0,0 +1,153 @@ +{ + "id": "ffb1e61f-30ec-419c-9e0c-baa844d876b1", + "name": "HealthCert", + "validFrom": "2021-05-18T06:43:12.152Z", + "fhirVersion": "4.0.1", + "fhirBundle": { + "resourceType": "Bundle", + "type": "collection", + "entry": [ + { + "resourceType": "Patient", + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/patient-nationality", + "code": { "text": "SG" } + } + ], + "identifier": [ + { + "type": "PPN", + "value": "E7831177G" + }, + { + "type": { "text": "NRIC" }, + "value": "S9098989Z" + } + ], + "name": [{ "text": "Tan Chen Chen" }], + "gender": "female", + "birthDate": "1990-01-15" + }, + { + "resourceType": "Specimen", + "type": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "258500001", + "display": "Nasopharyngeal swab" + } + ] + }, + "collection": { "collectedDateTime": "2020-09-27T06:15:00Z" } + }, + { + "resourceType": "Observation", + "identifier": [ + { + "value": "123456789", + "type": "ACSN" + } + ], + "code": { + "coding": [ + { + "system": "http://loinc.org", + "code": "94531-1", + "display": "Reverse transcription polymerase chain reaction (rRT-PCR) test" + } + ] + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "260385009", + "display": "Negative" + } + ] + }, + "effectiveDateTime": "2020-09-28T06:15:00Z", + "status": "final", + "performer": { "name": [{ "text": "Dr Michael Lim" }] }, + "qualification": [ + { + "identifier": "MCR 123214", + "issuer": "MOH" + } + ] + }, + { + "resourceType": "Organization", + "name": "MacRitchie Medical Clinic", + "type": "Licensed Healthcare Provider", + "endpoint": { + "address": "https://www.macritchieclinic.com.sg" + }, + "contact": { + "telecom": [ + { + "system": "phone", + "value": "+6563113111" + } + ], + "address": { + "type": "physical", + "use": "work", + "text": "MacRitchie Hospital Thomson Road Singapore 123000" + } + } + }, + { + "resourceType": "Organization", + "name": "MacRitchie Laboratory", + "type": "Accredited Laboratory", + "contact": { + "telecom": [ + { + "system": "phone", + "value": "+6562711188" + } + ], + "address": { + "type": "physical", + "use": "work", + "text": "2 Thomson Avenue 4 Singapore 098888" + } + } + } + ] + }, + "issuers": [ + { + "name": "MINISTRY OF HEALTH (SINGAPORE)", + "id": "did:ethr:0x82948a537e886Ea8ffF5Bd29DDA95224bF74c35F", + "revocation": { "type": "NONE" }, + "identityProof": { + "type": "DNS-DID", + "location": "moh.gov.sg", + "key": "did:ethr:0x82948a537e886Ea8ffF5Bd29DDA95224bF74c35F#controller" + } + } + ], + "$template": { + "name": "HEALTH_CERT", + "type": "EMBEDDED_RENDERER", + "url": "https://healthcert.renderer.moh.gov.sg/" + }, + "notarisationMetadata": { + "reference": "ffb1e61f-30ec-419c-9e0c-baa844d876b1", + "notarisedOn": "2021-05-21T06:29:14.216Z", + "passportNumber": "E7831177G", + "url": "https://action.openattestation.com/?q=%7B%22type%22%3A%22DOCUMENT%22%2C%22payload%22%3A%7B%22uri%22%3A%22https%3A%2F%2Fapi-vaccine.storage.staging.notarise.io%2Fdocument%2F6cfbbcbf-85a1-4644-b61a-952c12376502%22%2C%22key%22%3A%222b1236683c3a842ed4a0bb032c1cf668e24bcaf8ce599aeef502c93cb628152c%22%2C%22permittedActions%22%3A%5B%22VIEW%22%2C%22STORE%22%5D%2C%22redirect%22%3A%22https%3A%2F%2Fwww.verify.gov.sg%2Fverify%22%7D%7D" + }, + "logo": "data:image/png;base64,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", + "attachments": [ + { + "filename": "healthcert.txt", + "type": "text/open-attestation", + "data": "eyJ2ZXJzaW9uIjoiaHR0cHM6Ly9zY2hlbWEub3BlbmF0dGVzdGF0aW9uLmNvbS8yLjAvc2NoZW1hLmpzb24iLCJkYXRhIjp7ImlkIjoiOWJhZjQ4MWQtNjJjNy00NjEzLWE2MGYtYmM4ZTljMjk3NjQwOnN0cmluZzpURVNUMDAxIiwibmFtZSI6IjYxYWM0ZTY3LWQ5ZDgtNDMyMi1iYWI4LWE5MTI4ZjBjY2ZhZDpzdHJpbmc6SGVhbHRoQ2VydCIsInZhbGlkRnJvbSI6IjM1NTc3NTg0LTk3NDAtNDRjOC1iMDdiLThhMzU0NTkyMDNhMDpzdHJpbmc6MjAyMS0wNS0xOFQwNjo0MzoxMi4xNTJaIiwiZmhpclZlcnNpb24iOiI5ODQ2M2NjNC1mNGViLTQxN2QtOTY1ZC05MzUxMjBkOTFmMGU6c3RyaW5nOjQuMC4xIiwiZmhpckJ1bmRsZSI6eyJyZXNvdXJjZVR5cGUiOiI3ZGViN2QwOS1kNmIyLTRlMDMtOGY1Yy04MTE4NGViNzYyNWE6c3RyaW5nOkJ1bmRsZSIsInR5cGUiOiI0Y2ZmOTAyMy0xYjdlLTRkODEtYjJhNS03M2UzOGM4Zjg5N2U6c3RyaW5nOmNvbGxlY3Rpb24iLCJlbnRyeSI6W3sicmVzb3VyY2VUeXBlIjoiZDFlOGUwMWItN2Q5MS00ZDI5LWIyMzgtYTkzNWYwMjIyZmY3OnN0cmluZzpQYXRpZW50IiwiZXh0ZW5zaW9uIjpbeyJ1cmwiOiI1YmUwNGI1Zi0wMzkwLTQxZmItOGFmOC01NmY5MGM0Mzg3NmM6c3RyaW5nOmh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9wYXRpZW50LW5hdGlvbmFsaXR5IiwiY29kZSI6eyJ0ZXh0IjoiYmYzNDA3YmMtNjU1Mi00YzhiLWI5YzItYzc2YTRmOGU1OTRlOnN0cmluZzpTRyJ9fV0sImlkZW50aWZpZXIiOlt7InR5cGUiOiI4OTZmZjc1Ni03ODY0LTQyYjQtYTQ1Ny1mMjNlM2U5MTZkNmY6c3RyaW5nOlBQTiIsInZhbHVlIjoiMjY0OGNhOGItYWE2Yy00M2I4LTlhMWItYjU1NDg0MGEyN2Q0OnN0cmluZzpFNzgzMTE3N0cifSx7InR5cGUiOnsidGV4dCI6ImUxYzMxY2RiLTdmMTEtNGRlNy1hZTZjLWE2MTk2NmIyYWVhZDpzdHJpbmc6TlJJQyJ9LCJ2YWx1ZSI6IjJhNDVjZDFhLTQzOTUtNGVjMS1hM2EzLWUzNGY0NjdiMzBhYzpzdHJpbmc6UzkwOTg5ODlaIn1dLCJuYW1lIjpbeyJ0ZXh0IjoiZTk0OTIxNzctZDg3ZC00NzU1LTk5YWQtNTk3ZjRjOTFjZGI3OnN0cmluZzpUYW4gQ2hlbiBDaGVuIn1dLCJnZW5kZXIiOiI3YmMxNTY1My1hMjJmLTQyMmEtODk3ZS01ZjA2NzI4MjdhYjI6c3RyaW5nOmZlbWFsZSIsImJpcnRoRGF0ZSI6IjA4NGZhNGE3LTAzZTQtNDJmYS1iMDQwLTYyNjk4ZTVjNjIwODpzdHJpbmc6MTk5MC0wMS0xNSJ9LHsicmVzb3VyY2VUeXBlIjoiYzZkNmVjNmEtZjIzNi00OWY3LWJmYjktN2Y4NTVhN2FkNDI1OnN0cmluZzpTcGVjaW1lbiIsInR5cGUiOnsiY29kaW5nIjpbeyJzeXN0ZW0iOiIwZjFiMWE3OS00MzJhLTQ1OTEtOWM1YS1iNWZlZmNkMjVkNTA6c3RyaW5nOmh0dHA6Ly9zbm9tZWQuaW5mby9zY3QiLCJjb2RlIjoiYmExMzJjZDktZTkyNy00OTM4LWE3MDQtYmNlMWQ0NzdjZDgzOnN0cmluZzoyNTg1MDAwMDEiLCJkaXNwbGF5IjoiZmRhMmU4MTYtNzc0Mi00ZDQzLWI2OTctNTFiNzRkOGZmNDhlOnN0cmluZzpOYXNvcGhhcnluZ2VhbCBzd2FiIn1dfSwiY29sbGVjdGlvbiI6eyJjb2xsZWN0ZWREYXRlVGltZSI6IjI0MzRjMDIyLTA0NDctNGFmMS05YWMzLWU0ZjMzOWU3NzZjODpzdHJpbmc6MjAyMC0wOS0yN1QwNjoxNTowMFoifX0seyJyZXNvdXJjZVR5cGUiOiI3ODc1NjlmNy1iMGJmLTRmYWMtYWY3My1hNjhlYmExMjk4NWU6c3RyaW5nOk9ic2VydmF0aW9uIiwiaWRlbnRpZmllciI6W3sidmFsdWUiOiI1NjIzYzk3My1hYmE1LTQyM2UtYWUyZi1jOTRlZmRhODkyNGQ6c3RyaW5nOjEyMzQ1Njc4OSIsInR5cGUiOiI1Mjg0ZTJhMS0wMTYzLTQwNTktOWNlYS1mNTk3NzlkZmJkMGI6c3RyaW5nOkFDU04ifV0sImNvZGUiOnsiY29kaW5nIjpbeyJzeXN0ZW0iOiJlNmIxZjNjMS1lN2U1LTQ2ZTktYTM3Mi1mYjliNmFjYjc1MGQ6c3RyaW5nOmh0dHA6Ly9sb2luYy5vcmciLCJjb2RlIjoiZjJiYWVlODUtMThhYi00YzMyLWJlZTItMTRlNzUzZjNiYmY2OnN0cmluZzo5NDUzMS0xIiwiZGlzcGxheSI6ImMxMzE4ZjkyLTc4YzUtNDM3OC1iZTg4LTE4NzQ2YjFhZDM0ODpzdHJpbmc6UmV2ZXJzZSB0cmFuc2NyaXB0aW9uIHBvbHltZXJhc2UgY2hhaW4gcmVhY3Rpb24gKHJSVC1QQ1IpIHRlc3QifV19LCJ2YWx1ZUNvZGVhYmxlQ29uY2VwdCI6eyJjb2RpbmciOlt7InN5c3RlbSI6IjkyMTA3NGE1LTQ2OGYtNGVkMC04MGUyLTMyNmM5ODA5OTJmYzpzdHJpbmc6aHR0cDovL3Nub21lZC5pbmZvL3NjdCIsImNvZGUiOiI4NmFjNDk2ZS00MmI0LTRjMmMtOWZjMy0xNGNiZjUzYTQyYjI6c3RyaW5nOjI2MDM4NTAwOSIsImRpc3BsYXkiOiI4YjA2OWYzOC1iZTQyLTRkYmYtYWVlOS0yNTRjOWVjNGM0ODM6c3RyaW5nOk5lZ2F0aXZlIn1dfSwiZWZmZWN0aXZlRGF0ZVRpbWUiOiI3MWM1ZjM5Yy03YmE2LTRjYzUtYjkxOS03Zjk5ZDgyOTA1ZjQ6c3RyaW5nOjIwMjAtMDktMjhUMDY6MTU6MDBaIiwic3RhdHVzIjoiNGUyMjZkNDMtZTNmMi00YWExLTk4NzItMWEyYmMzZDdlZGE4OnN0cmluZzpmaW5hbCIsInBlcmZvcm1lciI6eyJuYW1lIjpbeyJ0ZXh0IjoiMzNjNmYzZTQtOGY0ZS00NmFkLWJlNzMtY2FmYjNmZTI3YTI5OnN0cmluZzpEciBNaWNoYWVsIExpbSJ9XX0sInF1YWxpZmljYXRpb24iOlt7ImlkZW50aWZpZXIiOiI3Yzg0NjUzZC00YjYzLTRhMjUtODJhZS1lOGUyNjE3NjQ2ZTk6c3RyaW5nOk1DUiAxMjMyMTQiLCJpc3N1ZXIiOiIxNzk4YmVkNC1kY2FjLTQwNmYtOTQyOC05NTgyMDVjYzk1MzU6c3RyaW5nOk1PSCJ9XX0seyJyZXNvdXJjZVR5cGUiOiJkN2E5MDMzNy0xYTdhLTRkNWQtYTAzZC03Nzk0ZTE1ZmQ3ODk6c3RyaW5nOk9yZ2FuaXphdGlvbiIsIm5hbWUiOiI1YTFhOTExNC1kZWE3LTQ2MGItYWNiZS04NWFmNjEzNzNmY2E6c3RyaW5nOk1hY1JpdGNoaWUgTWVkaWNhbCBDbGluaWMiLCJ0eXBlIjoiYWU0YmU1M2QtYzU0Yi00NzZhLWI1NTgtOWM5ZjE3NDk2N2ZmOnN0cmluZzpMaWNlbnNlZCBIZWFsdGhjYXJlIFByb3ZpZGVyIiwiZW5kcG9pbnQiOnsiYWRkcmVzcyI6IjU4NTFjODY0LTA1ZTItNDYxZi05MTBhLTc0Y2FhOWNiNDQ5YjpzdHJpbmc6aHR0cHM6Ly93d3cubWFjcml0Y2hpZWNsaW5pYy5jb20uc2cifSwiY29udGFjdCI6eyJ0ZWxlY29tIjpbeyJzeXN0ZW0iOiJmYzliZWQwYS1hYTNmLTQyMjUtYTdmYS00MzU5ZTJiNDAxMjQ6c3RyaW5nOnBob25lIiwidmFsdWUiOiJlOTZjZWEwNy0yZjE5LTQyMDMtOTg4ZC0zNzUwMjM3N2E3ZTU6c3RyaW5nOis2NTYzMTEzMTExIn1dLCJhZGRyZXNzIjp7InR5cGUiOiIwYTQ5OTA5Ni1jNDRkLTRkMmEtYmJkYi03ODM0YjI5ZjQ2ZGU6c3RyaW5nOnBoeXNpY2FsIiwidXNlIjoiOTQ5ZTUxM2QtYThmMS00ZWVlLTkwOWEtMDhjZTBlMWI3NDllOnN0cmluZzp3b3JrIiwidGV4dCI6IjQ5ODZiMTRmLWViODUtNDdhMy1iNzRlLTYxMWFmZDg0ZWUxZTpzdHJpbmc6TWFjUml0Y2hpZSBIb3NwaXRhbCBUaG9tc29uIFJvYWQgU2luZ2Fwb3JlIDEyMzAwMCJ9fX0seyJyZXNvdXJjZVR5cGUiOiI4YTE4MmQ5YS1hMDZhLTQ0MDItOWMyOC02YjQzMTRiZjY5ODU6c3RyaW5nOk9yZ2FuaXphdGlvbiIsIm5hbWUiOiJhMjZjNTY5NC0xYjg3LTRlMDItYmJlYi0wY2ZiYTM2YmM4ODc6c3RyaW5nOk1hY1JpdGNoaWUgTGFib3JhdG9yeSIsInR5cGUiOiIzMTdkZDJiNS02YThhLTQwZGItODM4Yi0zNDc5ZmVjZDM0NTI6c3RyaW5nOkFjY3JlZGl0ZWQgTGFib3JhdG9yeSIsImNvbnRhY3QiOnsidGVsZWNvbSI6W3sic3lzdGVtIjoiMjc1ZDYwYjUtOTk1OC00ZGJjLTg1MzUtZjExMDVjNDJhNjVmOnN0cmluZzpwaG9uZSIsInZhbHVlIjoiMjVjYTE3ODEtMDhkMS00NmYyLWFmZDUtZWUwY2U5OTExY2YzOnN0cmluZzorNjU2MjcxMTE4OCJ9XSwiYWRkcmVzcyI6eyJ0eXBlIjoiZTNmMGFkODQtZDFjMC00MDM4LTk1ZTctNGRlYzQ3YWY2ZmJjOnN0cmluZzpwaHlzaWNhbCIsInVzZSI6ImE1Yjg3ZGU0LTgyMjQtNDU0Zi1iNThkLWUyYWZkMzA5OGI2MTpzdHJpbmc6d29yayIsInRleHQiOiI0ODM4M2Q3MC1jNmU2LTRkNjUtYmRjMi03YTU0MjNjM2IyMWE6c3RyaW5nOjIgVGhvbXNvbiBBdmVudWUgNCBTaW5nYXBvcmUgMDk4ODg4In19fV19LCJpc3N1ZXJzIjpbeyJpZCI6IjZlNTdkYzZhLTRlZWEtNGE1NC04OTg2LWFkZWVhNzExZTEwMzpzdHJpbmc6ZGlkOmV0aHI6MHhFMzk0Nzk5MjhDYzRFZkZFNTA3NzQ0ODg3ODBCOWY2MTZiZDRCODMwIiwicmV2b2NhdGlvbiI6eyJ0eXBlIjoiMDM2Nzc5MGQtNjQzYy00YTA2LWJkYjMtNjM4YmZkMjE2Yzk4OnN0cmluZzpOT05FIn0sIm5hbWUiOiI3MDQ0OTljMC02ZjM4LTRjZmQtODQxMC0wM2Q0N2UyNDA1ZTY6c3RyaW5nOlNBTVBMRSBDTElOSUMiLCJpZGVudGl0eVByb29mIjp7InR5cGUiOiI5OWM4MTQwNy05MmI0LTRhMDctODFkMC0wZDdjZDc3YTJhNWM6c3RyaW5nOkROUy1ESUQiLCJsb2NhdGlvbiI6ImQwMGRhYjhmLTJmM2EtNDE4Mi05ODAyLWRmMDE1ZGM2YmIwYjpzdHJpbmc6ZG9ub3R2ZXJpZnkudGVzdGluZy52ZXJpZnkuZ292LnNnIiwia2V5IjoiMmNiMDA1NjEtOGJhNy00MTI4LWExYzMtMTdlMGRmZjBkZDgwOnN0cmluZzpkaWQ6ZXRocjoweEUzOTQ3OTkyOENjNEVmRkU1MDc3NDQ4ODc4MEI5ZjYxNmJkNEI4MzAjY29udHJvbGxlciJ9fV0sImxvZ28iOiJmZmRhZTk0MS1iNmUwLTQyYzctOTJhMS0yOGE3Mjg1NGU3ZDQ6c3RyaW5nOmRhdGE6aW1hZ2UvcG5nO2Jhc2U2NCxpVkJPUncwS0dnb0FBQUFOU1VoRVVnQUFBZlFBQUFESUNBTUFBQUFweCtQYUFBQUFNMUJNVkVVQUFBRE16TXpNek16TXpNek16TXpNek16TXpNek16TXpNek16TXpNek16TXpNek16TXpNek16TXpNek16TXpNek16TXplQ21pQUFBQUFFSFJTVGxNQVFMK0E3eEFnbjJEUDN6QndyMUNQRWwrSS9RQUFCd2RKUkVGVWVOcnNuZDEyMnlvUVJ2a0hJU0hOK3ovdHlVazlvVEVDUTFiVEJjMjNieU5zMEI1R0lEQVJBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBaytJaytJZHg0ZzVONEI5R1EvclBBOUovSVBmU2d3TC9NRUVBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBRHdQNVpQb1A1cjdGSktBZjdjdWZCaWhQTlNrWDVobEE5dStEc1A3ZFgvSksxUDJWUGlTSW9lYkVyTHdWaDVaeCs4QzFZMjJZdFAwRnBmNmhkZWErbXExV2xpeGZlajZSY0R4ajA5c3dYYmJlQlFwaWp1ZzIwYWovU0U4YnZvNWhFdWF2QXVTS3BRZkp4VEc5MWdVckNWNmpTUUUwb1BrZTR3dWtlNzA1RXFwTE5XeHRNdFNrNGp2WEdsZCt0TGx4dlZNTm5ha0Q3bUVuZFlUVldTblY4NjBXVVhsMzRSTXk3QmVtcHlHek43cEFibVhFQTZiZnZLMHUzMnVURktLVk0wcjBZdzFNVGNGdnA4aVZMUEQwKzlnSFF5KzdyU2YzZWVqcDJIdUZjc21sZGlFejBGektYZlNSdzNxZTA4WHFkOWRQNlFLT05ua3U0bEczTlNiL1JCdEt0S3QxdHRkQkppWWIyVkk3YnJjN3RjOElZb3RKekhVQjBjK08rVDNyVFF1TEtzWlJxcHprVFM3ZFpJNHZvK3FKbmRFR084RXplY3lqYWM2L0lUTjJLT1dhVUxJVC9hTGRlVW5xcGRpN1ZXMitLeWMyOUZMM3M3ZTNoaTVMVFNoZVdXcHlXbEg0WHptdldqbmlPaUZOM1lXRGl2V0k5Mld1azVjdDJDMHAzSnpsOVlONjZXSTVJVi9WeUY4NnIxYTE3cEg1VU1DMHBYL0R3WFZVNTI0S3M1WWdEWm1MNHpHejF3ODBwMzNQajFwTXZjaSt0YzJjRklqbWhIMmRXVmZ1YVZMdUxqeTllVHpncU9ycWV3djB2dW0vMUtSNCsyYTZEaDVwWE83VjlPK3M0S1JKUEFEdXhOanRqRkNDay9DbHRFemdmelN0ZXJTdmRaUVplRG95eXF4UWd1UjFsWG1CbEkvOVBTZWJacGJPZThiaXZ0MmJGSzlZYUs0ZUhlN05MTmF0TFAzcUdZTGZMNzFSb012QjZYdTk2SjNUV3Q5TFRvUU01em04WWZ4YkhJRVNQWlhYVy90b3ZUU28rUHFGeE5lc3dacWpPL1gwOU92QmdpOU9jSHc3bGxVdWtjditkaTBybmVxZjk5dVhvS2dsTU13YWxsN3gvbXkwbWxQNXBpVm52M2Z1WisxOTN4bnBUWUx6M1NqZWpQTFhwTzZUdFhielhwZklVY2VKSG1Qc1hBSnNiSSthTDdmdnNwcFZzT1g3dWFkSjlGdnVUNjNQeHNaQVEzVU14eWdMeVd2c2s2L2x1a3U0MGZiOHR0b2xERkZiMVpRUTYvbVJrdjFpVzlpMUo2Qy8xYWVqQWN2UVBWbVV0NkZCMmNuMjZKekRPNFRzYUxjV2VhVGJvN0luMDRYMDg2OTZYeFRucmttekdDSGltbUpwTHVOYVBpNzFmK0tPa3RlNUlLOU9yUzc0aW5nUFNmSmQxb0lTRDlaMG0vaFBoQjBvKy9MZDNNTUdVclNVNjhzOXlVelhTTzNzdWhXK0JoK0pqMG95ejJzblpxZ3BjemQ1aXdwdlJ2bUtmWHBZL1AweWVTZnNnSE9obGl3dExTN2NCU2lSMWFaRlAzMHErQnQzZlhiSzloUTJUcis0clNjKzhkZmxYQ08ybDZwWStQSXM1cEYxeHM0a21iWFZCNnowSldSUmRIKzZCMHc4VmVveWRlV2xWODR4YVVMbnZYMDh2RXpObitISk91K3RmVDFjU2JLUExld3ZXa2MvYzEvWXRzNFNsSitESHB1bnNGMzA2OVhTcnc3VmhRZWw0Z0hOM1F1SE84akVrL084Y0MrVW8vcFhSK3ZHMExTbi9aWHhsWHlJb2M2MFBTaGVsZHd2ZHpiNEhXM0k3MXBPLzB3SFlxT0lwOHY0MUpUNTJUTmpmNWp4MjRmbUU5NldMckc3L2Jzb002ZWhDR3BKOHMwL1pWM2s4cW5UT2RYMUI2NkhPZ2I0YjVLUmZ0bDU0ZkM3b3Z5dlpacFh0Nkp5NG8zWnFlZE92TVRkc2xQVWhEMHJsV3h2Vk1GdFMwUDFVT25QdldrODRYZGIwRElYVy9rSGlNU0xlbTdyTU1LRG10OUowSG1ndEsvM0JnN0doZ09HTENnUFQ4YWZwMXBkVEV4NDg4Nm5ndEtGMmM5T3BzZ1ZEYk9LQ0pPUWFraSsxVnJGaSt3cmlKcGZOYS9vclNoY3JXMjg2akxZc3l5ZlpMbDhTRXRuTTY1ajFTTEgrd1hWRzZqYzBEWUk5ODZGdWpLSm5RTFYwYzFNcnc3c081bi9md3dEZmtvajlnZkQ0b3poeUZBVVZNcUJSbFlyQ2Qwb1VuUnJraXlFek9QRk5MRnpUelQ1VmxCWGQzT204b3prQnRPT2REUFprVTlrOS9QQ3BMa0hhcm5aVWZJaFhPdjAvNklTdjBTT2N2ai8xYjl0emZrTjVHM3g3ZWJkSWgzNFdmRjZ0cERycllLNlBVcGQvNGZKUzNicFhhcnRPSk4rU1JEQlhPdjBsNm02RXpaMXozNWx3OWszUk8wMVdNRkJVNEg0KzIxbE1iYjhYczB2bHZZVkhwM1BVcUtDY2FPRFVzbmJOTFNSNWNUQytkWitwcFZlbENuS2ExMTdlTlROUWtTVkZpVTJ0UCtRclNPVnZaWmFVTHF3dnRQQ2gvamRNYjNSTjk5UU9rb2p2OExzUVMway9PNyt0S2YrTk1UOTZOUDBVdkx2aW5SbTlKbjI0d1ZyYkRDYkdJZEY0eFZCTkoveEpTZTZVZW8vQmovOUkvN0R5MFB2cm5KeTVvcFNJUlJaWDBhUVVBQVB6WDNoM1VBQUNBUUF4N1lBRC9hbkZCQ05kYW1JQUJBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQURBbW1vZUs5SHppQjVJOUVCWG54OEFBQUFBQUFBQUFMQm1BSVpLbXpXSW54eU9BQUFBQUVsRlRrU3VRbUNDIiwiJHRlbXBsYXRlIjp7Im5hbWUiOiI1NjhhNjdiNC0zNzhjLTQ5MjgtYjNhNi05NDc2ZDcxYjBhOWU6c3RyaW5nOkhFQUxUSENFUlQiLCJ0eXBlIjoiMWZlNTlmNmMtODhlNC00MTUzLTg1NjgtMzAwOTU5ZDZiNzlhOnN0cmluZzpFTUJFRERFRF9SRU5ERVJFUiIsInVybCI6ImZhZThkMGJkLTExMWQtNGNjNC1hZGUzLWYyMDhlMjRhNTFiOTpzdHJpbmc6aHR0cHM6Ly9tb2gtaGVhbHRoY2VydC1yZW5kZXJlci5uZXRsaWZ5LmFwcC8ifX0sInNpZ25hdHVyZSI6eyJ0eXBlIjoiU0hBM01lcmtsZVByb29mIiwidGFyZ2V0SGFzaCI6IjBjOTIwYTEzNmZhODc3YjkzMzNmYjYyOWFjNmY2YjU0ZTAyODc5OWY2NzVkNTAxYjE3YzM1MjVlZGY1NDZkZDEiLCJwcm9vZiI6W10sIm1lcmtsZVJvb3QiOiIwYzkyMGExMzZmYTg3N2I5MzMzZmI2MjlhYzZmNmI1NGUwMjg3OTlmNjc1ZDUwMWIxN2MzNTI1ZWRmNTQ2ZGQxIn0sInByb29mIjpbeyJ0eXBlIjoiT3BlbkF0dGVzdGF0aW9uU2lnbmF0dXJlMjAxOCIsImNyZWF0ZWQiOiIyMDIxLTA1LTE5VDAzOjA5OjQxLjIyN1oiLCJwcm9vZlB1cnBvc2UiOiJhc3NlcnRpb25NZXRob2QiLCJ2ZXJpZmljYXRpb25NZXRob2QiOiJkaWQ6ZXRocjoweEUzOTQ3OTkyOENjNEVmRkU1MDc3NDQ4ODc4MEI5ZjYxNmJkNEI4MzAjY29udHJvbGxlciIsInNpZ25hdHVyZSI6IjB4NzMwOTFlZjE2MjI2MzIyYzIxMDgyZmI2YWU3MjQzZTJkMDZjMWNhNzVjZTAxZDA5MDQ5YWI5ODQyNjcwNzdiMzY2Mzk3OWFiYWZkZWZlZjllMjNjMzRlOThlZDkyZjM2NDM4N2M1ZjZhYmIzZjk1YzBjYjE3MGE4ZTJhODJhODcxYyJ9XX0=" + } + ] +} diff --git a/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-endorsed-schema.json b/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-endorsed-schema.json new file mode 100644 index 0000000..6fe7e60 --- /dev/null +++ b/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-endorsed-schema.json @@ -0,0 +1,13 @@ +{ + "$id": "https://schemata.openattestation.com/sg/gov/moh/pdt-healthcert/1.0/healthcert-endorsed-schema", + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "allOf": [ + { + "$ref": "https://schemata.openattestation.com/sg/gov/moh/pdt-healthcert/1.0/schema.json" + }, + { + "$ref": "https://schemata.openattestation.com/sg/gov/tech/notarise/1.0/notarise-open-attestation-schema.json" + } + ] +} diff --git a/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-endorsed-wrapped.json b/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-endorsed-wrapped.json new file mode 100644 index 0000000..62f3736 --- /dev/null +++ b/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-endorsed-wrapped.json @@ -0,0 +1,171 @@ +{ + "version": "https://schema.openattestation.com/2.0/schema.json", + "data": { + "id": "09894096-cbda-4d5c-8fd7-cdc9f79eed75:string:ffb1e61f-30ec-419c-9e0c-baa844d876b1", + "name": "1a25cd82-4884-4486-b8f7-e1a3ebf6a103:string:HealthCert", + "validFrom": "755eac5c-977e-44c8-bb30-e315023bb45a:string:2021-05-18T06:43:12.152Z", + "fhirVersion": "98cf16af-a786-4e04-a38e-37fabd207b1f:string:4.0.1", + "fhirBundle": { + "resourceType": "bbed2126-d531-47d5-b5bf-4da8aa338001:string:Bundle", + "type": "0cf03c65-cb7d-463a-9a3a-fa5015a2c793:string:collection", + "entry": [ + { + "resourceType": "c1e3f685-8fb1-47a7-95ab-9d607169df86:string:Patient", + "extension": [ + { + "url": "9cea8630-37db-4cb8-a915-541e43e4945e:string:http://hl7.org/fhir/StructureDefinition/patient-nationality", + "code": { "text": "de059562-b61a-44fa-8950-fc5086bb7651:string:SG" } + } + ], + "identifier": [ + { + "type": "dedf56de-fae0-499b-808d-6fdb7502c248:string:PPN", + "value": "8fc59490-b0c3-407f-9ce0-fe3b8a8c7884:string:E7831177G" + }, + { + "type": { "text": "c973c7a2-7517-4a79-9082-f263a769e8bd:string:NRIC" }, + "value": "c7470872-e12e-4924-ad23-9944fa903862:string:S9098989Z" + } + ], + "name": [{ "text": "22bc0438-9388-4a2d-a862-af163b92913a:string:Tan Chen Chen" }], + "gender": "a58205b1-faae-4df7-aadc-bc8c71e6080f:string:female", + "birthDate": "d7b9d842-5014-48e0-8fff-51bd3618ab78:string:1990-01-15" + }, + { + "resourceType": "af17e95b-7547-474a-8d23-91bde8d3f7be:string:Specimen", + "type": { + "coding": [ + { + "system": "62fa505c-3673-484b-86ad-8a511318afd6:string:http://snomed.info/sct", + "code": "73112cfe-6bff-4635-9d6d-ad8db749f115:string:258500001", + "display": "045c14a3-5145-49e6-b2f1-9ebac61c54c8:string:Nasopharyngeal swab" + } + ] + }, + "collection": { "collectedDateTime": "342b7b48-746a-4962-9364-901292b8fb71:string:2020-09-27T06:15:00Z" } + }, + { + "resourceType": "bfacc952-4c08-4854-b8f1-81f2fcb5cc58:string:Observation", + "identifier": [ + { + "value": "95e48ef5-c746-48db-92eb-09cf9be8e039:string:123456789", + "type": "10a83586-8d69-4e91-a7d1-882e2f640f9e:string:ACSN" + } + ], + "code": { + "coding": [ + { + "system": "4b22f9a0-ad99-4b98-aaa6-5db04ddc7a55:string:http://loinc.org", + "code": "63a5760c-a1d6-40ae-8b86-717000253748:string:94531-1", + "display": "c0f0ca2e-a26f-4059-bea0-b4a9502200b7:string:Reverse transcription polymerase chain reaction (rRT-PCR) test" + } + ] + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "f9ba7e5f-a976-43e7-91c6-6e519c87f535:string:http://snomed.info/sct", + "code": "dd8f7aa9-cfe7-4cf7-a9cc-884ed75546a2:string:260385009", + "display": "05c241a2-bdbd-4fdb-92be-163f8e2055e7:string:Negative" + } + ] + }, + "effectiveDateTime": "134d8ba1-36cb-446a-bd1c-2b711b89fb83:string:2020-09-28T06:15:00Z", + "status": "aa5bac0d-d613-41a7-bb69-778d3029bf97:string:final", + "performer": { "name": [{ "text": "b2568018-4604-4c42-aee2-b528cef6803a:string:Dr Michael Lim" }] }, + "qualification": [ + { + "identifier": "c550dc62-60f1-410f-bfc6-37b3ea164592:string:MCR 123214", + "issuer": "492e5d8c-2fed-4390-8a7e-6d47d904997f:string:MOH" + } + ] + }, + { + "resourceType": "24644c5a-cf61-41e1-a25b-794e43653a9b:string:Organization", + "name": "0cd689c4-0aab-4fd7-89c1-655761fa8c2c:string:MacRitchie Medical Clinic", + "type": "33077839-ae30-4879-8679-f7bf5549288c:string:Licensed Healthcare Provider", + "endpoint": { + "address": "9969f663-7962-489f-90a1-62bc2a49558d:string:https://www.macritchieclinic.com.sg" + }, + "contact": { + "telecom": [ + { + "system": "a2c4071a-732e-4dc8-a4a7-9b05280f2fdd:string:phone", + "value": "68cb8b4d-880e-4b60-b36f-ab2b8ae0e0e1:string:+6563113111" + } + ], + "address": { + "type": "ee1aac1b-ce03-4a89-b0d6-9f5229744bbf:string:physical", + "use": "46d17443-8a48-4665-9d1c-82346f22b311:string:work", + "text": "2d69c324-7d78-44b4-9f70-a9085fc1d4cf:string:MacRitchie Hospital Thomson Road Singapore 123000" + } + } + }, + { + "resourceType": "cb65034e-4a7e-416c-93a2-e7593c4a29ab:string:Organization", + "name": "9b4d6527-086d-4285-94ab-d3346aa33518:string:MacRitchie Laboratory", + "type": "7b76500b-e51b-4d84-9610-2b9fa659c5a1:string:Accredited Laboratory", + "contact": { + "telecom": [ + { + "system": "d226a88f-7f67-4a6f-9ed3-79c8c8a7fd15:string:phone", + "value": "a950fb73-9d5b-4f38-ad6a-14448bfe6662:string:+6562711188" + } + ], + "address": { + "type": "f0fffbc6-abe6-456d-8e19-53bee5212fa2:string:physical", + "use": "e07e7196-6d2b-4adc-9620-a907134ec4f5:string:work", + "text": "621af883-939c-466d-aa77-3332f4d40a2f:string:2 Thomson Avenue 4 Singapore 098888" + } + } + } + ] + }, + "issuers": [ + { + "name": "2ec47a49-b7fc-4763-9a88-5df0aa71fe36:string:MINISTRY OF HEALTH (SINGAPORE)", + "id": "463dfd00-a439-449a-9306-1eb688ff3ea5:string:did:ethr:0x82948a537e886Ea8ffF5Bd29DDA95224bF74c35F", + "revocation": { "type": "bcfc443a-16a1-4635-9134-9b899dbcdc5e:string:NONE" }, + "identityProof": { + "type": "40927032-81e8-4634-8f4c-c571124c0f47:string:DNS-DID", + "location": "3a5ffca6-0860-48f7-9c09-e152ebf48fd7:string:moh.gov.sg", + "key": "44711b78-7187-4e94-a211-dc4afa63d131:string:did:ethr:0x82948a537e886Ea8ffF5Bd29DDA95224bF74c35F#controller" + } + } + ], + "$template": { + "name": "11235438-192e-42b3-85a0-e8e6548c65f2:string:HEALTH_CERT", + "type": "5d811fc4-ab73-4aa3-8338-1635eab125d8:string:EMBEDDED_RENDERER", + "url": "e9293423-9a43-44ce-bc00-dba92a6a0681:string:https://healthcert.renderer.moh.gov.sg/" + }, + "notarisationMetadata": { + "reference": "72439eaf-68b6-46f5-a637-9f46cf671770:string:ffb1e61f-30ec-419c-9e0c-baa844d876b1", + "notarisedOn": "34eb3354-dd33-4c76-9945-90691e21ad35:string:2021-05-21T06:29:14.216Z", + "passportNumber": "36a9b842-c105-4857-9e5c-b9b8cf680aa8:string:E7831177G", + "url": "693860b0-c754-4beb-9a7d-0c0c80fb3980:string:https://action.openattestation.com/?q=%7B%22type%22%3A%22DOCUMENT%22%2C%22payload%22%3A%7B%22uri%22%3A%22https%3A%2F%2Fapi-vaccine.storage.staging.notarise.io%2Fdocument%2F6cfbbcbf-85a1-4644-b61a-952c12376502%22%2C%22key%22%3A%222b1236683c3a842ed4a0bb032c1cf668e24bcaf8ce599aeef502c93cb628152c%22%2C%22permittedActions%22%3A%5B%22VIEW%22%2C%22STORE%22%5D%2C%22redirect%22%3A%22https%3A%2F%2Fwww.verify.gov.sg%2Fverify%22%7D%7D" + }, + "logo": "3efa5e81-42d9-46ae-ab7f-d9d240241734:string:data:image/png;base64,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", + "attachments": [ + { + "filename": "359a5220-ed53-4dd5-bfac-74219b8e48aa:string:healthcert.txt", + "type": "0185e0a0-5b55-4f50-ac22-cf2c837a0fe0:string:text/open-attestation", + "data": "e95b976c-0a58-4195-87c5-735c50455038:string:eyJ2ZXJzaW9uIjoiaHR0cHM6Ly9zY2hlbWEub3BlbmF0dGVzdGF0aW9uLmNvbS8yLjAvc2NoZW1hLmpzb24iLCJkYXRhIjp7ImlkIjoiOWJhZjQ4MWQtNjJjNy00NjEzLWE2MGYtYmM4ZTljMjk3NjQwOnN0cmluZzpURVNUMDAxIiwibmFtZSI6IjYxYWM0ZTY3LWQ5ZDgtNDMyMi1iYWI4LWE5MTI4ZjBjY2ZhZDpzdHJpbmc6SGVhbHRoQ2VydCIsInZhbGlkRnJvbSI6IjM1NTc3NTg0LTk3NDAtNDRjOC1iMDdiLThhMzU0NTkyMDNhMDpzdHJpbmc6MjAyMS0wNS0xOFQwNjo0MzoxMi4xNTJaIiwiZmhpclZlcnNpb24iOiI5ODQ2M2NjNC1mNGViLTQxN2QtOTY1ZC05MzUxMjBkOTFmMGU6c3RyaW5nOjQuMC4xIiwiZmhpckJ1bmRsZSI6eyJyZXNvdXJjZVR5cGUiOiI3ZGViN2QwOS1kNmIyLTRlMDMtOGY1Yy04MTE4NGViNzYyNWE6c3RyaW5nOkJ1bmRsZSIsInR5cGUiOiI0Y2ZmOTAyMy0xYjdlLTRkODEtYjJhNS03M2UzOGM4Zjg5N2U6c3RyaW5nOmNvbGxlY3Rpb24iLCJlbnRyeSI6W3sicmVzb3VyY2VUeXBlIjoiZDFlOGUwMWItN2Q5MS00ZDI5LWIyMzgtYTkzNWYwMjIyZmY3OnN0cmluZzpQYXRpZW50IiwiZXh0ZW5zaW9uIjpbeyJ1cmwiOiI1YmUwNGI1Zi0wMzkwLTQxZmItOGFmOC01NmY5MGM0Mzg3NmM6c3RyaW5nOmh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9wYXRpZW50LW5hdGlvbmFsaXR5IiwiY29kZSI6eyJ0ZXh0IjoiYmYzNDA3YmMtNjU1Mi00YzhiLWI5YzItYzc2YTRmOGU1OTRlOnN0cmluZzpTRyJ9fV0sImlkZW50aWZpZXIiOlt7InR5cGUiOiI4OTZmZjc1Ni03ODY0LTQyYjQtYTQ1Ny1mMjNlM2U5MTZkNmY6c3RyaW5nOlBQTiIsInZhbHVlIjoiMjY0OGNhOGItYWE2Yy00M2I4LTlhMWItYjU1NDg0MGEyN2Q0OnN0cmluZzpFNzgzMTE3N0cifSx7InR5cGUiOnsidGV4dCI6ImUxYzMxY2RiLTdmMTEtNGRlNy1hZTZjLWE2MTk2NmIyYWVhZDpzdHJpbmc6TlJJQyJ9LCJ2YWx1ZSI6IjJhNDVjZDFhLTQzOTUtNGVjMS1hM2EzLWUzNGY0NjdiMzBhYzpzdHJpbmc6UzkwOTg5ODlaIn1dLCJuYW1lIjpbeyJ0ZXh0IjoiZTk0OTIxNzctZDg3ZC00NzU1LTk5YWQtNTk3ZjRjOTFjZGI3OnN0cmluZzpUYW4gQ2hlbiBDaGVuIn1dLCJnZW5kZXIiOiI3YmMxNTY1My1hMjJmLTQyMmEtODk3ZS01ZjA2NzI4MjdhYjI6c3RyaW5nOmZlbWFsZSIsImJpcnRoRGF0ZSI6IjA4NGZhNGE3LTAzZTQtNDJmYS1iMDQwLTYyNjk4ZTVjNjIwODpzdHJpbmc6MTk5MC0wMS0xNSJ9LHsicmVzb3VyY2VUeXBlIjoiYzZkNmVjNmEtZjIzNi00OWY3LWJmYjktN2Y4NTVhN2FkNDI1OnN0cmluZzpTcGVjaW1lbiIsInR5cGUiOnsiY29kaW5nIjpbeyJzeXN0ZW0iOiIwZjFiMWE3OS00MzJhLTQ1OTEtOWM1YS1iNWZlZmNkMjVkNTA6c3RyaW5nOmh0dHA6Ly9zbm9tZWQuaW5mby9zY3QiLCJjb2RlIjoiYmExMzJjZDktZTkyNy00OTM4LWE3MDQtYmNlMWQ0NzdjZDgzOnN0cmluZzoyNTg1MDAwMDEiLCJkaXNwbGF5IjoiZmRhMmU4MTYtNzc0Mi00ZDQzLWI2OTctNTFiNzRkOGZmNDhlOnN0cmluZzpOYXNvcGhhcnluZ2VhbCBzd2FiIn1dfSwiY29sbGVjdGlvbiI6eyJjb2xsZWN0ZWREYXRlVGltZSI6IjI0MzRjMDIyLTA0NDctNGFmMS05YWMzLWU0ZjMzOWU3NzZjODpzdHJpbmc6MjAyMC0wOS0yN1QwNjoxNTowMFoifX0seyJyZXNvdXJjZVR5cGUiOiI3ODc1NjlmNy1iMGJmLTRmYWMtYWY3My1hNjhlYmExMjk4NWU6c3RyaW5nOk9ic2VydmF0aW9uIiwiaWRlbnRpZmllciI6W3sidmFsdWUiOiI1NjIzYzk3My1hYmE1LTQyM2UtYWUyZi1jOTRlZmRhODkyNGQ6c3RyaW5nOjEyMzQ1Njc4OSIsInR5cGUiOiI1Mjg0ZTJhMS0wMTYzLTQwNTktOWNlYS1mNTk3NzlkZmJkMGI6c3RyaW5nOkFDU04ifV0sImNvZGUiOnsiY29kaW5nIjpbeyJzeXN0ZW0iOiJlNmIxZjNjMS1lN2U1LTQ2ZTktYTM3Mi1mYjliNmFjYjc1MGQ6c3RyaW5nOmh0dHA6Ly9sb2luYy5vcmciLCJjb2RlIjoiZjJiYWVlODUtMThhYi00YzMyLWJlZTItMTRlNzUzZjNiYmY2OnN0cmluZzo5NDUzMS0xIiwiZGlzcGxheSI6ImMxMzE4ZjkyLTc4YzUtNDM3OC1iZTg4LTE4NzQ2YjFhZDM0ODpzdHJpbmc6UmV2ZXJzZSB0cmFuc2NyaXB0aW9uIHBvbHltZXJhc2UgY2hhaW4gcmVhY3Rpb24gKHJSVC1QQ1IpIHRlc3QifV19LCJ2YWx1ZUNvZGVhYmxlQ29uY2VwdCI6eyJjb2RpbmciOlt7InN5c3RlbSI6IjkyMTA3NGE1LTQ2OGYtNGVkMC04MGUyLTMyNmM5ODA5OTJmYzpzdHJpbmc6aHR0cDovL3Nub21lZC5pbmZvL3NjdCIsImNvZGUiOiI4NmFjNDk2ZS00MmI0LTRjMmMtOWZjMy0xNGNiZjUzYTQyYjI6c3RyaW5nOjI2MDM4NTAwOSIsImRpc3BsYXkiOiI4YjA2OWYzOC1iZTQyLTRkYmYtYWVlOS0yNTRjOWVjNGM0ODM6c3RyaW5nOk5lZ2F0aXZlIn1dfSwiZWZmZWN0aXZlRGF0ZVRpbWUiOiI3MWM1ZjM5Yy03YmE2LTRjYzUtYjkxOS03Zjk5ZDgyOTA1ZjQ6c3RyaW5nOjIwMjAtMDktMjhUMDY6MTU6MDBaIiwic3RhdHVzIjoiNGUyMjZkNDMtZTNmMi00YWExLTk4NzItMWEyYmMzZDdlZGE4OnN0cmluZzpmaW5hbCIsInBlcmZvcm1lciI6eyJuYW1lIjpbeyJ0ZXh0IjoiMzNjNmYzZTQtOGY0ZS00NmFkLWJlNzMtY2FmYjNmZTI3YTI5OnN0cmluZzpEciBNaWNoYWVsIExpbSJ9XX0sInF1YWxpZmljYXRpb24iOlt7ImlkZW50aWZpZXIiOiI3Yzg0NjUzZC00YjYzLTRhMjUtODJhZS1lOGUyNjE3NjQ2ZTk6c3RyaW5nOk1DUiAxMjMyMTQiLCJpc3N1ZXIiOiIxNzk4YmVkNC1kY2FjLTQwNmYtOTQyOC05NTgyMDVjYzk1MzU6c3RyaW5nOk1PSCJ9XX0seyJyZXNvdXJjZVR5cGUiOiJkN2E5MDMzNy0xYTdhLTRkNWQtYTAzZC03Nzk0ZTE1ZmQ3ODk6c3RyaW5nOk9yZ2FuaXphdGlvbiIsIm5hbWUiOiI1YTFhOTExNC1kZWE3LTQ2MGItYWNiZS04NWFmNjEzNzNmY2E6c3RyaW5nOk1hY1JpdGNoaWUgTWVkaWNhbCBDbGluaWMiLCJ0eXBlIjoiYWU0YmU1M2QtYzU0Yi00NzZhLWI1NTgtOWM5ZjE3NDk2N2ZmOnN0cmluZzpMaWNlbnNlZCBIZWFsdGhjYXJlIFByb3ZpZGVyIiwiZW5kcG9pbnQiOnsiYWRkcmVzcyI6IjU4NTFjODY0LTA1ZTItNDYxZi05MTBhLTc0Y2FhOWNiNDQ5YjpzdHJpbmc6aHR0cHM6Ly93d3cubWFjcml0Y2hpZWNsaW5pYy5jb20uc2cifSwiY29udGFjdCI6eyJ0ZWxlY29tIjpbeyJzeXN0ZW0iOiJmYzliZWQwYS1hYTNmLTQyMjUtYTdmYS00MzU5ZTJiNDAxMjQ6c3RyaW5nOnBob25lIiwidmFsdWUiOiJlOTZjZWEwNy0yZjE5LTQyMDMtOTg4ZC0zNzUwMjM3N2E3ZTU6c3RyaW5nOis2NTYzMTEzMTExIn1dLCJhZGRyZXNzIjp7InR5cGUiOiIwYTQ5OTA5Ni1jNDRkLTRkMmEtYmJkYi03ODM0YjI5ZjQ2ZGU6c3RyaW5nOnBoeXNpY2FsIiwidXNlIjoiOTQ5ZTUxM2QtYThmMS00ZWVlLTkwOWEtMDhjZTBlMWI3NDllOnN0cmluZzp3b3JrIiwidGV4dCI6IjQ5ODZiMTRmLWViODUtNDdhMy1iNzRlLTYxMWFmZDg0ZWUxZTpzdHJpbmc6TWFjUml0Y2hpZSBIb3NwaXRhbCBUaG9tc29uIFJvYWQgU2luZ2Fwb3JlIDEyMzAwMCJ9fX0seyJyZXNvdXJjZVR5cGUiOiI4YTE4MmQ5YS1hMDZhLTQ0MDItOWMyOC02YjQzMTRiZjY5ODU6c3RyaW5nOk9yZ2FuaXphdGlvbiIsIm5hbWUiOiJhMjZjNTY5NC0xYjg3LTRlMDItYmJlYi0wY2ZiYTM2YmM4ODc6c3RyaW5nOk1hY1JpdGNoaWUgTGFib3JhdG9yeSIsInR5cGUiOiIzMTdkZDJiNS02YThhLTQwZGItODM4Yi0zNDc5ZmVjZDM0NTI6c3RyaW5nOkFjY3JlZGl0ZWQgTGFib3JhdG9yeSIsImNvbnRhY3QiOnsidGVsZWNvbSI6W3sic3lzdGVtIjoiMjc1ZDYwYjUtOTk1OC00ZGJjLTg1MzUtZjExMDVjNDJhNjVmOnN0cmluZzpwaG9uZSIsInZhbHVlIjoiMjVjYTE3ODEtMDhkMS00NmYyLWFmZDUtZWUwY2U5OTExY2YzOnN0cmluZzorNjU2MjcxMTE4OCJ9XSwiYWRkcmVzcyI6eyJ0eXBlIjoiZTNmMGFkODQtZDFjMC00MDM4LTk1ZTctNGRlYzQ3YWY2ZmJjOnN0cmluZzpwaHlzaWNhbCIsInVzZSI6ImE1Yjg3ZGU0LTgyMjQtNDU0Zi1iNThkLWUyYWZkMzA5OGI2MTpzdHJpbmc6d29yayIsInRleHQiOiI0ODM4M2Q3MC1jNmU2LTRkNjUtYmRjMi03YTU0MjNjM2IyMWE6c3RyaW5nOjIgVGhvbXNvbiBBdmVudWUgNCBTaW5nYXBvcmUgMDk4ODg4In19fV19LCJpc3N1ZXJzIjpbeyJpZCI6IjZlNTdkYzZhLTRlZWEtNGE1NC04OTg2LWFkZWVhNzExZTEwMzpzdHJpbmc6ZGlkOmV0aHI6MHhFMzk0Nzk5MjhDYzRFZkZFNTA3NzQ0ODg3ODBCOWY2MTZiZDRCODMwIiwicmV2b2NhdGlvbiI6eyJ0eXBlIjoiMDM2Nzc5MGQtNjQzYy00YTA2LWJkYjMtNjM4YmZkMjE2Yzk4OnN0cmluZzpOT05FIn0sIm5hbWUiOiI3MDQ0OTljMC02ZjM4LTRjZmQtODQxMC0wM2Q0N2UyNDA1ZTY6c3RyaW5nOlNBTVBMRSBDTElOSUMiLCJpZGVudGl0eVByb29mIjp7InR5cGUiOiI5OWM4MTQwNy05MmI0LTRhMDctODFkMC0wZDdjZDc3YTJhNWM6c3RyaW5nOkROUy1ESUQiLCJsb2NhdGlvbiI6ImQwMGRhYjhmLTJmM2EtNDE4Mi05ODAyLWRmMDE1ZGM2YmIwYjpzdHJpbmc6ZG9ub3R2ZXJpZnkudGVzdGluZy52ZXJpZnkuZ292LnNnIiwia2V5IjoiMmNiMDA1NjEtOGJhNy00MTI4LWExYzMtMTdlMGRmZjBkZDgwOnN0cmluZzpkaWQ6ZXRocjoweEUzOTQ3OTkyOENjNEVmRkU1MDc3NDQ4ODc4MEI5ZjYxNmJkNEI4MzAjY29udHJvbGxlciJ9fV0sImxvZ28iOiJmZmRhZTk0MS1iNmUwLTQyYzctOTJhMS0yOGE3Mjg1NGU3ZDQ6c3RyaW5nOmRhdGE6aW1hZ2UvcG5nO2Jhc2U2NCxpVkJPUncwS0dnb0FBQUFOU1VoRVVnQUFBZlFBQUFESUNBTUFBQUFweCtQYUFBQUFNMUJNVkVVQUFBRE16TXpNek16TXpNek16TXpNek16TXpNek16TXpNek16TXpNek16TXpNek16TXpNek16TXpNek16TXpNek16TXplQ21pQUFBQUFFSFJTVGxNQVFMK0E3eEFnbjJEUDN6QndyMUNQRWwrSS9RQUFCd2RKUkVGVWVOcnNuZDEyMnlvUVJ2a0hJU0hOK3ovdHlVazlvVEVDUTFiVEJjMjNieU5zMEI1R0lEQVJBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBaytJaytJZHg0ZzVONEI5R1EvclBBOUovSVBmU2d3TC9NRUVBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBRHdQNVpQb1A1cjdGSktBZjdjdWZCaWhQTlNrWDVobEE5dStEc1A3ZFgvSksxUDJWUGlTSW9lYkVyTHdWaDVaeCs4QzFZMjJZdFAwRnBmNmhkZWErbXExV2xpeGZlajZSY0R4ajA5c3dYYmJlQlFwaWp1ZzIwYWovU0U4YnZvNWhFdWF2QXVTS3BRZkp4VEc5MWdVckNWNmpTUUUwb1BrZTR3dWtlNzA1RXFwTE5XeHRNdFNrNGp2WEdsZCt0TGx4dlZNTm5ha0Q3bUVuZFlUVldTblY4NjBXVVhsMzRSTXk3QmVtcHlHek43cEFibVhFQTZiZnZLMHUzMnVURktLVk0wcjBZdzFNVGNGdnA4aVZMUEQwKzlnSFF5KzdyU2YzZWVqcDJIdUZjc21sZGlFejBGektYZlNSdzNxZTA4WHFkOWRQNlFLT05ua3U0bEczTlNiL1JCdEt0S3QxdHRkQkppWWIyVkk3YnJjN3RjOElZb3RKekhVQjBjK08rVDNyVFF1TEtzWlJxcHprVFM3ZFpJNHZvK3FKbmRFR084RXplY3lqYWM2L0lUTjJLT1dhVUxJVC9hTGRlVW5xcGRpN1ZXMitLeWMyOUZMM3M3ZTNoaTVMVFNoZVdXcHlXbEg0WHptdldqbmlPaUZOM1lXRGl2V0k5Mld1azVjdDJDMHAzSnpsOVlONjZXSTVJVi9WeUY4NnIxYTE3cEg1VU1DMHBYL0R3WFZVNTI0S3M1WWdEWm1MNHpHejF3ODBwMzNQajFwTXZjaSt0YzJjRklqbWhIMmRXVmZ1YVZMdUxqeTllVHpncU9ycWV3djB2dW0vMUtSNCsyYTZEaDVwWE83VjlPK3M0S1JKUEFEdXhOanRqRkNDay9DbHRFemdmelN0ZXJTdmRaUVplRG95eXF4UWd1UjFsWG1CbEkvOVBTZWJacGJPZThiaXZ0MmJGSzlZYUs0ZUhlN05MTmF0TFAzcUdZTGZMNzFSb012QjZYdTk2SjNUV3Q5TFRvUU01em04WWZ4YkhJRVNQWlhYVy90b3ZUU28rUHFGeE5lc3dacWpPL1gwOU92QmdpOU9jSHc3bGxVdWtjditkaTBybmVxZjk5dVhvS2dsTU13YWxsN3gvbXkwbWxQNXBpVm52M2Z1WisxOTN4bnBUWUx6M1NqZWpQTFhwTzZUdFhielhwZklVY2VKSG1Qc1hBSnNiSSthTDdmdnNwcFZzT1g3dWFkSjlGdnVUNjNQeHNaQVEzVU14eWdMeVd2c2s2L2x1a3U0MGZiOHR0b2xERkZiMVpRUTYvbVJrdjFpVzlpMUo2Qy8xYWVqQWN2UVBWbVV0NkZCMmNuMjZKekRPNFRzYUxjV2VhVGJvN0luMDRYMDg2OTZYeFRucmttekdDSGltbUpwTHVOYVBpNzFmK0tPa3RlNUlLOU9yUzc0aW5nUFNmSmQxb0lTRDlaMG0vaFBoQjBvKy9MZDNNTUdVclNVNjhzOXlVelhTTzNzdWhXK0JoK0pqMG95ejJzblpxZ3BjemQ1aXdwdlJ2bUtmWHBZL1AweWVTZnNnSE9obGl3dExTN2NCU2lSMWFaRlAzMHErQnQzZlhiSzloUTJUcis0clNjKzhkZmxYQ08ybDZwWStQSXM1cEYxeHM0a21iWFZCNnowSldSUmRIKzZCMHc4VmVveWRlV2xWODR4YVVMbnZYMDh2RXpObitISk91K3RmVDFjU2JLUExld3ZXa2MvYzEvWXRzNFNsSitESHB1bnNGMzA2OVhTcnc3VmhRZWw0Z0hOM1F1SE84akVrL084Y0MrVW8vcFhSK3ZHMExTbi9aWHhsWHlJb2M2MFBTaGVsZHd2ZHpiNEhXM0k3MXBPLzB3SFlxT0lwOHY0MUpUNTJUTmpmNWp4MjRmbUU5NldMckc3L2Jzb002ZWhDR3BKOHMwL1pWM2s4cW5UT2RYMUI2NkhPZ2I0YjVLUmZ0bDU0ZkM3b3Z5dlpacFh0Nkp5NG8zWnFlZE92TVRkc2xQVWhEMHJsV3h2Vk1GdFMwUDFVT25QdldrODRYZGIwRElYVy9rSGlNU0xlbTdyTU1LRG10OUowSG1ndEsvM0JnN0doZ09HTENnUFQ4YWZwMXBkVEV4NDg4Nm5ndEtGMmM5T3BzZ1ZEYk9LQ0pPUWFraSsxVnJGaSt3cmlKcGZOYS9vclNoY3JXMjg2akxZc3l5ZlpMbDhTRXRuTTY1ajFTTEgrd1hWRzZqYzBEWUk5ODZGdWpLSm5RTFYwYzFNcnc3c081bi9md3dEZmtvajlnZkQ0b3poeUZBVVZNcUJSbFlyQ2Qwb1VuUnJraXlFek9QRk5MRnpUelQ1VmxCWGQzT204b3prQnRPT2REUFprVTlrOS9QQ3BMa0hhcm5aVWZJaFhPdjAvNklTdjBTT2N2ai8xYjl0emZrTjVHM3g3ZWJkSWgzNFdmRjZ0cERycllLNlBVcGQvNGZKUzNicFhhcnRPSk4rU1JEQlhPdjBsNm02RXpaMXozNWx3OWszUk8wMVdNRkJVNEg0KzIxbE1iYjhYczB2bHZZVkhwM1BVcUtDY2FPRFVzbmJOTFNSNWNUQytkWitwcFZlbENuS2ExMTdlTlROUWtTVkZpVTJ0UCtRclNPVnZaWmFVTHF3dnRQQ2gvamRNYjNSTjk5UU9rb2p2OExzUVMway9PNyt0S2YrTk1UOTZOUDBVdkx2aW5SbTlKbjI0d1ZyYkRDYkdJZEY0eFZCTkoveEpTZTZVZW8vQmovOUkvN0R5MFB2cm5KeTVvcFNJUlJaWDBhUVVBQVB6WDNoM1VBQUNBUUF4N1lBRC9hbkZCQ05kYW1JQUJBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQURBbW1vZUs5SHppQjVJOUVCWG54OEFBQUFBQUFBQUFMQm1BSVpLbXpXSW54eU9BQUFBQUVsRlRrU3VRbUNDIiwiJHRlbXBsYXRlIjp7Im5hbWUiOiI1NjhhNjdiNC0zNzhjLTQ5MjgtYjNhNi05NDc2ZDcxYjBhOWU6c3RyaW5nOkhFQUxUSENFUlQiLCJ0eXBlIjoiMWZlNTlmNmMtODhlNC00MTUzLTg1NjgtMzAwOTU5ZDZiNzlhOnN0cmluZzpFTUJFRERFRF9SRU5ERVJFUiIsInVybCI6ImZhZThkMGJkLTExMWQtNGNjNC1hZGUzLWYyMDhlMjRhNTFiOTpzdHJpbmc6aHR0cHM6Ly9tb2gtaGVhbHRoY2VydC1yZW5kZXJlci5uZXRsaWZ5LmFwcC8ifX0sInNpZ25hdHVyZSI6eyJ0eXBlIjoiU0hBM01lcmtsZVByb29mIiwidGFyZ2V0SGFzaCI6IjBjOTIwYTEzNmZhODc3YjkzMzNmYjYyOWFjNmY2YjU0ZTAyODc5OWY2NzVkNTAxYjE3YzM1MjVlZGY1NDZkZDEiLCJwcm9vZiI6W10sIm1lcmtsZVJvb3QiOiIwYzkyMGExMzZmYTg3N2I5MzMzZmI2MjlhYzZmNmI1NGUwMjg3OTlmNjc1ZDUwMWIxN2MzNTI1ZWRmNTQ2ZGQxIn0sInByb29mIjpbeyJ0eXBlIjoiT3BlbkF0dGVzdGF0aW9uU2lnbmF0dXJlMjAxOCIsImNyZWF0ZWQiOiIyMDIxLTA1LTE5VDAzOjA5OjQxLjIyN1oiLCJwcm9vZlB1cnBvc2UiOiJhc3NlcnRpb25NZXRob2QiLCJ2ZXJpZmljYXRpb25NZXRob2QiOiJkaWQ6ZXRocjoweEUzOTQ3OTkyOENjNEVmRkU1MDc3NDQ4ODc4MEI5ZjYxNmJkNEI4MzAjY29udHJvbGxlciIsInNpZ25hdHVyZSI6IjB4NzMwOTFlZjE2MjI2MzIyYzIxMDgyZmI2YWU3MjQzZTJkMDZjMWNhNzVjZTAxZDA5MDQ5YWI5ODQyNjcwNzdiMzY2Mzk3OWFiYWZkZWZlZjllMjNjMzRlOThlZDkyZjM2NDM4N2M1ZjZhYmIzZjk1YzBjYjE3MGE4ZTJhODJhODcxYyJ9XX0=" + } + ] + }, + "signature": { + "type": "SHA3MerkleProof", + "targetHash": "ed60bed7bd41a1c3053ed2f96bff2d8b597f3c34d44e5e31810f916eda5d31c1", + "proof": [], + "merkleRoot": "ed60bed7bd41a1c3053ed2f96bff2d8b597f3c34d44e5e31810f916eda5d31c1" + }, + "proof": [ + { + "type": "OpenAttestationSignature2018", + "created": "2021-05-21T06:29:14.260Z", + "proofPurpose": "assertionMethod", + "verificationMethod": "did:ethr:0x82948a537e886Ea8ffF5Bd29DDA95224bF74c35F#controller", + "signature": "0xca94013bd25c6a5b2eb91c16a1f101b10b1e42ae18136f854481cb6b3f2ec3d250c5a1912c67174b9913461e09712dc6ffd2696517e51a887c7c800c39500e111b" + } + ] +} diff --git a/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-endorsed.test.ts b/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-endorsed.test.ts new file mode 100644 index 0000000..4dc4ac4 --- /dev/null +++ b/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-endorsed.test.ts @@ -0,0 +1,80 @@ +import Ajv from "ajv"; +import schema from "./healthcert-endorsed-schema.json"; +import sampleDocJson from "./healthcert-endorsed-document.json"; +import axios from "axios"; +import { cloneDeep, omit } from "lodash"; + +function loadSchema(uri: string) { + return axios.get(uri).then(res => { + return res.data; + }); +} +const ajv = new Ajv({ allErrors: true, loadSchema: loadSchema }); +let validator: Ajv.ValidateFunction; + +// eslint-disable-next-line jest/no-disabled-tests +describe("schema", () => { + beforeAll(async () => { + validator = await ajv.compileAsync(schema); + }); + it("should work with valid json", () => { + expect(validator(sampleDocJson)).toBe(true); + }); + + //To test if healthcert schema is correctly merged + it("should return array of errors without fhirVersion", () => { + const badDoc = omit(cloneDeep(sampleDocJson), "fhirVersion"); + expect(validator(badDoc)).toBe(false); + expect(validator.errors).toMatchInlineSnapshot(` + Array [ + Object { + "dataPath": "", + "keyword": "required", + "message": "should have required property 'fhirVersion'", + "params": Object { + "missingProperty": "fhirVersion", + }, + "schemaPath": "#/required", + }, + ] + `); + }); + + //To test if oa schema is correctly merged + it("should return array of errors without issuers", () => { + const badDoc = omit(cloneDeep(sampleDocJson), "issuers"); + expect(validator(badDoc)).toBe(false); + expect(validator.errors).toMatchInlineSnapshot(` + Array [ + Object { + "dataPath": "", + "keyword": "required", + "message": "should have required property 'issuers'", + "params": Object { + "missingProperty": "issuers", + }, + "schemaPath": "#/required", + }, + ] + `); + }); + + //To test if notarized schema is correctly merged + it("should return array of errors without notarisationMetadata", () => { + const badDoc = omit(cloneDeep(sampleDocJson), "notarisationMetadata"); + expect(validator(badDoc)).toBe(false); + expect(validator.errors).toMatchInlineSnapshot(` + Array [ + Object { + "dataPath": "", + "keyword": "required", + "message": "should have required property 'notarisationMetadata'", + "params": Object { + "missingProperty": "notarisationMetadata", + }, + "schemaPath": "https://schemata.openattestation.com/sg/gov/tech/notarise/1.0/schema.json/required", + }, + ] + `); + }); +}); diff --git a/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-open-attestation-document.json b/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-open-attestation-document.json new file mode 100644 index 0000000..20d7d01 --- /dev/null +++ b/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-open-attestation-document.json @@ -0,0 +1,154 @@ +{ + "id": "TEST001", + "name": "HealthCert", + "validFrom": "2020-11-20", + "fhirVersion": "4.0.1", + "fhirBundle": { + "resourceType": "Bundle", + "type": "collection", + "entry": [ + { + "resourceType": "Patient", + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/patient-nationality", + "code": { + "text": "SG" + } + } + ], + "identifier": [ + { + "type": "PPN", + "value": "E7831177G" + }, + { + "type": { + "text": "NRIC" + }, + "value": "S9098989Z" + } + ], + "name": [ + { + "text": "Tan Chen Chen" + } + ], + "gender": "female", + "birthDate": "1990-01-15" + }, + { + "resourceType": "Specimen", + "type": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "258500001", + "display": "Nasopharyngeal swab" + } + ] + }, + "collection": { + "collectedDateTime": "2020-09-27T06:15:00Z" + } + }, + { + "resourceType": "Observation", + "identifier": [ + { + "value": "123456789", + "type": "ACSN" + } + ], + "code": { + "coding": [ + { + "system": "http://loinc.org", + "code": "94531-1", + "display": "Reverse transcription polymerase chain reaction (rRT-PCR) test" + } + ] + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "260385009", + "display": "Negative" + } + ] + }, + "effectiveDateTime": "2020-09-28T06:15:00Z", + "status": "final", + "performer": { + "name": [ + { + "text": "Dr Michael Lim" + } + ] + }, + "qualification": [ + { + "identifier": "MCR 123214", + "issuer": "MOH" + } + ] + }, + { + "resourceType": "Organization", + "name": "MacRitchie Medical Clinic", + "type": "Licensed Healthcare Provider", + "endpoint": { + "address": "https://www.macritchieclinic.com.sg" + }, + "contact": { + "telecom": [ + { + "system": "phone", + "value": "+6563113111" + } + ], + "address": { + "type": "physical", + "use": "work", + "text": "MacRitchie Hospital Thomson Road Singapore 123000" + } + } + }, + { + "resourceType": "Organization", + "name": "MacRitchie Laboratory", + "type": "Accredited Laboratory", + "contact": { + "telecom": [ + { + "system": "phone", + "value": "+6562711188" + } + ], + "address": { + "type": "physical", + "use": "work", + "text": "2 Thomson Avenue 4 Singapore 098888" + } + } + } + ] + }, + "issuers": [ + { + "name": "GovTech", + "documentStore": "0x8Fc57204c35fb9317D91285eF52D6b892EC08cD3", + "identityProof": { + "type": "DNS-TXT", + "location": "example.openattestation.com" + } + } + ], + "$template": { + "name": "HEALTH_CERT", + "type": "EMBEDDED_RENDERER", + "url": "https://healthcert.renderer.moh.gov.sg/" + }, + "logo": "data:image/png;base64,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" +} diff --git a/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-open-attestation-schema.json b/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-open-attestation-schema.json new file mode 100644 index 0000000..f9f3bc9 --- /dev/null +++ b/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-open-attestation-schema.json @@ -0,0 +1,13 @@ +{ + "$id": "https://schemata.openattestation.com/sg/gov/moh/pdt-healthcert/1.0/healthcert-open-attestation-schema", + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "allOf": [ + { + "$ref": "https://schema.openattestation.com/2.0/schema.json" + }, + { + "$ref": "https://schemata.openattestation.com/sg/gov/moh/pdt-healthcert/1.0/schema.json" + } + ] +} diff --git a/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-open-attestation-wrapped.json b/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-open-attestation-wrapped.json new file mode 100644 index 0000000..b10124f --- /dev/null +++ b/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-open-attestation-wrapped.json @@ -0,0 +1,145 @@ +{ + "version": "https://schema.openattestation.com/2.0/schema.json", + "data": { + "id": "40807d1a-f208-458b-bb28-c4292c9b2c03:string:TEST001", + "name": "eb911918-3943-44ed-9a8f-c556d7fab1ec:string:HealthCert", + "validFrom": "ac0af0a7-dce4-463e-b7ec-3ae663644e1c:string:2020-11-20", + "fhirVersion": "966a9137-e3cc-4024-bc62-2cd8b4c7648e:string:4.0.1", + "fhirBundle": { + "resourceType": "99429fa7-4631-4017-9559-85eafb0a85f2:string:Bundle", + "type": "fd3d0218-acfd-42f7-a214-e14e315c420b:string:collection", + "entry": [ + { + "resourceType": "1bab067d-5f68-4141-9d20-48434444ac1f:string:Patient", + "extension": [ + { + "url": "f18e96c6-4a1e-4d60-8c97-8066dcd38921:string:http://hl7.org/fhir/StructureDefinition/patient-nationality", + "code": { "text": "84de67db-93fe-4d34-8c60-61d8e1c5ac59:string:SG" } + } + ], + "identifier": [ + { + "type": "88bdd823-72e1-4625-b698-48d4c4ec8ed7:string:PPN", + "value": "02be76fe-fc54-444b-87ef-c328c36e93ea:string:E7831177G" + }, + { + "type": { "text": "5609f993-36aa-4e40-b4d5-130b366c0a7f:string:NRIC" }, + "value": "45b71d50-5e76-4b78-9dcc-8b54b5509319:string:S9098989Z" + } + ], + "name": [{ "text": "1782abe5-9058-4591-a639-519d19925c39:string:Tan Chen Chen" }], + "gender": "fc9bce33-3517-4999-acea-f66de915df82:string:female", + "birthDate": "7d97871a-dbd4-4062-b5fa-8aadbb4a0e35:string:1990-01-15" + }, + { + "resourceType": "c6532b9a-ef1b-428a-a5ab-76258456aa77:string:Specimen", + "type": { + "coding": [ + { + "system": "18c406fc-ec77-4fb8-b2ba-ae567fe99a71:string:http://snomed.info/sct", + "code": "8d86c5af-d784-4524-b759-411a57d41dd1:string:258500001", + "display": "a8cb9839-d2ec-442d-a448-84545428f1d4:string:Nasopharyngeal swab" + } + ] + }, + "collection": { "collectedDateTime": "523a5e1c-5685-49e6-a41f-de35a0459410:string:2020-09-27T06:15:00Z" } + }, + { + "resourceType": "7bb32736-d3da-4482-8822-24117197400a:string:Observation", + "identifier": [ + { + "value": "56fb05c5-6d94-4af6-836c-fbcca9ca1cfa:string:123456789", + "type": "068633b0-7d3e-4e3b-a1ba-6c23d1abb3f1:string:ACSN" + } + ], + "code": { + "coding": [ + { + "system": "78445249-1264-4f4e-9425-0431c976f048:string:http://loinc.org", + "code": "db3bf128-5e17-4b76-b994-deb2b5382a22:string:94531-1", + "display": "f4ba6bf0-7aae-4dc3-91ab-7a449d3b1dbc:string:Reverse transcription polymerase chain reaction (rRT-PCR) test" + } + ] + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "d932f2ea-e4c4-43fc-841c-1a68e9c2ef59:string:http://snomed.info/sct", + "code": "ee6d20d0-7db5-46f4-b31b-65966f001b70:string:260385009", + "display": "4ea1c272-2aca-4e92-8b6f-3d6fd9a39c4c:string:Negative" + } + ] + }, + "effectiveDateTime": "1f50f061-cb9b-41ad-9782-248ea846526c:string:2020-09-28T06:15:00Z", + "status": "1bcc192e-b884-4739-ac64-3bf35d14809c:string:final", + "performer": { "name": [{ "text": "9e817d90-94f5-474e-b87b-ff4f5bfa998c:string:Dr Michael Lim" }] }, + "qualification": [ + { + "identifier": "c1ecc812-dcbc-4b08-8040-8d97e5df3adf:string:MCR 123214", + "issuer": "fee7c8a1-a51c-4e05-aab9-5efa1852ab9a:string:MOH" + } + ] + }, + { + "resourceType": "7ea7ce9e-af7f-42f4-82b8-c8ca8c0cdffe:string:Organization", + "name": "db382cba-a524-49df-959d-8bad772fb54e:string:MacRitchie Medical Clinic", + "type": "e8b8e6d7-3eb0-4014-a505-c9b40f04f1bb:string:Licensed Healthcare Provider", + "endpoint": { "address": "3cc87a33-ce2e-4ea2-97e7-011c7657b832:string:https://www.macritchieclinic.com.sg" }, + "contact": { + "telecom": [ + { + "system": "6726dd0e-f1c5-4284-b2f7-f216147251ba:string:phone", + "value": "cbddf641-8ca1-497c-b710-7cea65f98a79:string:+6563113111" + } + ], + "address": { + "type": "2a82e81b-06c6-4701-855d-8f3924bf9263:string:physical", + "use": "c61ffb4a-586a-47c0-869b-5a18a2adbe31:string:work", + "text": "62b2b788-754b-4be6-b718-fd1fc71830d7:string:MacRitchie Hospital Thomson Road Singapore 123000" + } + } + }, + { + "resourceType": "d2243f32-4cf6-409a-9cdf-b98970569c46:string:Organization", + "name": "a4466d40-081a-4b52-b9f8-e137db454bdd:string:MacRitchie Laboratory", + "type": "544bc46a-a1a5-46e7-8cfe-2ee9041dda94:string:Accredited Laboratory", + "contact": { + "telecom": [ + { + "system": "63ee61a6-b07e-4cdf-9100-99c0a91a9121:string:phone", + "value": "a45ce31d-870e-4625-a619-2e420d47afd2:string:+6562711188" + } + ], + "address": { + "type": "c9643a1b-384f-48bf-bc3d-deb6d6428b31:string:physical", + "use": "a30f310d-f633-4608-846b-8cc9289f1ecc:string:work", + "text": "3ebc6f70-eee3-49cc-93e3-e05bf8b904ab:string:2 Thomson Avenue 4 Singapore 098888" + } + } + } + ] + }, + "issuers": [ + { + "name": "1f00ac06-9c39-42df-942a-93d28f47c3f5:string:GovTech", + "documentStore": "7bbda3dc-492e-43f5-871e-c11befa8ab73:string:0x8Fc57204c35fb9317D91285eF52D6b892EC08cD3", + "identityProof": { + "type": "fc146a29-6986-4c2e-b750-485b5aec999c:string:DNS-TXT", + "location": "9226e7a1-ada3-4d3a-9b1d-0857e724c5fd:string:example.openattestation.com" + } + } + ], + "$template": { + "name": "4fbeba97-7008-4a93-a818-3c95ab055c49:string:HEALTH_CERT", + "type": "21280482-aebb-4e12-a711-605cc80bd806:string:EMBEDDED_RENDERER", + "url": "a6fcfb4e-79d8-4b43-afa1-9fe56db38a22:string:https://healthcert.renderer.moh.gov.sg/" + }, + "logo": "413588af-882a-4c98-b151-3e381dff25ab:string:data:image/png;base64,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" + }, + "signature": { + "type": "SHA3MerkleProof", + "targetHash": "a368009ad4c73ee2ad15a8818c88940220e467795830b4fc80a4e8e472c12c95", + "proof": [], + "merkleRoot": "a368009ad4c73ee2ad15a8818c88940220e467795830b4fc80a4e8e472c12c95" + } +} diff --git a/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-open-attestation.test.ts b/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-open-attestation.test.ts new file mode 100644 index 0000000..43861ee --- /dev/null +++ b/src/sg/gov/moh/pdt-healthcert/1.0/healthcert-open-attestation.test.ts @@ -0,0 +1,61 @@ +import Ajv from "ajv"; +import { cloneDeep, omit } from "lodash"; +import schema from "./healthcert-open-attestation-schema.json"; +import sampleDocJson from "./healthcert-open-attestation-document.json"; +import axios from "axios"; + +function loadSchema(uri: string) { + return axios.get(uri).then(res => { + return res.data; + }); +} +const ajv = new Ajv({ allErrors: true, loadSchema: loadSchema }); +let validator: Ajv.ValidateFunction; + +// eslint-disable-next-line jest/no-disabled-tests +describe.skip("schema", () => { + beforeAll(async () => { + validator = await ajv.compileAsync(schema); + }); + it("should work with valid json", () => { + expect(validator(sampleDocJson)).toBe(true); + }); + + //To test if healthcert schema is correctly merged + it("should return array of errors without fhirVersion", () => { + const badDoc = omit(cloneDeep(sampleDocJson), "fhirVersion"); + expect(validator(badDoc)).toBe(false); + expect(validator.errors).toMatchInlineSnapshot(` + Array [ + Object { + "dataPath": "", + "keyword": "required", + "message": "should have required property 'fhirVersion'", + "params": Object { + "missingProperty": "fhirVersion", + }, + "schemaPath": "#/required", + }, + ] + `); + }); + + //To test if oa schema is correctly merged + it("should return array of errors without issuers", () => { + const badDoc = omit(cloneDeep(sampleDocJson), "issuers"); + expect(validator(badDoc)).toBe(false); + expect(validator.errors).toMatchInlineSnapshot(` + Array [ + Object { + "dataPath": "", + "keyword": "required", + "message": "should have required property 'issuers'", + "params": Object { + "missingProperty": "issuers", + }, + "schemaPath": "#/required", + }, + ] + `); + }); +}); diff --git a/src/sg/gov/moh/pdt-healthcert/1.0/sample-document-with-references.json b/src/sg/gov/moh/pdt-healthcert/1.0/sample-document-with-references.json new file mode 100644 index 0000000..2fa081d --- /dev/null +++ b/src/sg/gov/moh/pdt-healthcert/1.0/sample-document-with-references.json @@ -0,0 +1,154 @@ +{ + "id": "TEST001", + "name": "HealthCert", + "validFrom": "2021-05-18T06:43:12.152Z", + "fhirVersion": "4.0.1", + "fhirBundle": { + "resourceType": "Bundle", + "type": "collection", + "entry": [ + { + "fullUrl": "urn:uuid:aaaa1321-4af5-424c-a0e1-ed3aab1c349d", + "resourceType": "Patient", + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/patient-nationality", + "code": { + "text": "SG" + } + } + ], + "identifier": [ + { + "type": "PPN", + "value": "E7831177G" + }, + { + "type": { + "text": "NRIC" + }, + "value": "S9098989Z" + } + ], + "name": [ + { + "text": "Tan Chen Chen" + } + ], + "gender": "female", + "birthDate": "1990-01-15" + }, + { + "fullUrl": "urn:uuid:bbbb1321-4af5-424c-a0e1-ed3aab1c349d", + "resourceType": "Specimen", + "type": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "258500001", + "display": "Nasopharyngeal swab" + } + ] + }, + "collection": { + "collectedDateTime": "2020-09-27T06:15:00Z" + } + }, + { + "fullUrl": "urn:uuid:cccc1321-4af5-424c-a0e1-ed3aab1c349d", + "specimen": { + "reference": "urn:uuid:bbbb1321-4af5-424c-a0e1-ed3aab1c349d" + }, + "performerReference": [ + { + "reference": "urn:uuid:dddd1321-4af5-424c-a0e1-ed3aab1c349d" + }, + { + "reference": "urn:uuid:eeee1321-4af5-424c-a0e1-ed3aab1c349d" + } + ], + "resourceType": "Observation", + "identifier": [ + { + "value": "123456789", + "type": "ACSN" + } + ], + "code": { + "coding": [ + { + "system": "http://loinc.org", + "code": "94531-1", + "display": "Reverse transcription polymerase chain reaction (rRT-PCR) test" + } + ] + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "260385009", + "display": "Negative" + } + ] + }, + "effectiveDateTime": "2020-09-28T06:15:00Z", + "status": "final", + "performer": { + "name": [ + { + "text": "Dr Michael Lim" + } + ] + }, + "qualification": [ + { + "identifier": "MCR 123214", + "issuer": "MOH" + } + ] + }, + { + "fullUrl": "urn:uuid:dddd1321-4af5-424c-a0e1-ed3aab1c349d", + "resourceType": "Organization", + "name": "MacRitchie Medical Clinic", + "type": "Licensed Healthcare Provider", + "endpoint": { + "address": "https://www.macritchieclinic.com.sg" + }, + "contact": { + "telecom": [ + { + "system": "phone", + "value": "+6563113111" + } + ], + "address": { + "type": "physical", + "use": "work", + "text": "MacRitchie Hospital Thomson Road Singapore 123000" + } + } + }, + { + "fullUrl": "urn:uuid:eeee1321-4af5-424c-a0e1-ed3aab1c349d", + "resourceType": "Organization", + "name": "MacRitchie Laboratory", + "type": "Accredited Laboratory", + "contact": { + "telecom": [ + { + "system": "phone", + "value": "+6562711188" + } + ], + "address": { + "type": "physical", + "use": "work", + "text": "2 Thomson Avenue 4 Singapore 098888" + } + } + } + ] + } +} diff --git a/src/sg/gov/moh/pdt-healthcert/1.0/sample-document.json b/src/sg/gov/moh/pdt-healthcert/1.0/sample-document.json new file mode 100644 index 0000000..962cfe3 --- /dev/null +++ b/src/sg/gov/moh/pdt-healthcert/1.0/sample-document.json @@ -0,0 +1,138 @@ +{ + "id": "TEST001", + "name": "HealthCert", + "validFrom": "2021-05-18T06:43:12.152Z", + "fhirVersion": "4.0.1", + "fhirBundle": { + "resourceType": "Bundle", + "type": "collection", + "entry": [ + { + "resourceType": "Patient", + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/patient-nationality", + "code": { + "text": "SG" + } + } + ], + "identifier": [ + { + "type": "PPN", + "value": "E7831177G" + }, + { + "type": { + "text": "NRIC" + }, + "value": "S9098989Z" + } + ], + "name": [ + { + "text": "Tan Chen Chen" + } + ], + "gender": "female", + "birthDate": "1990-01-15" + }, + { + "resourceType": "Specimen", + "type": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "258500001", + "display": "Nasopharyngeal swab" + } + ] + }, + "collection": { + "collectedDateTime": "2020-09-27T06:15:00Z" + } + }, + { + "resourceType": "Observation", + "identifier": [ + { + "value": "123456789", + "type": "ACSN" + } + ], + "code": { + "coding": [ + { + "system": "http://loinc.org", + "code": "94531-1", + "display": "Reverse transcription polymerase chain reaction (rRT-PCR) test" + } + ] + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "260385009", + "display": "Negative" + } + ] + }, + "effectiveDateTime": "2020-09-28T06:15:00Z", + "status": "final", + "performer": { + "name": [ + { + "text": "Dr Michael Lim" + } + ] + }, + "qualification": [ + { + "identifier": "MCR 123214", + "issuer": "MOH" + } + ] + }, + { + "resourceType": "Organization", + "name": "MacRitchie Medical Clinic", + "type": "Licensed Healthcare Provider", + "endpoint": { + "address": "https://www.macritchieclinic.com.sg" + }, + "contact": { + "telecom": [ + { + "system": "phone", + "value": "+6563113111" + } + ], + "address": { + "type": "physical", + "use": "work", + "text": "MacRitchie Hospital Thomson Road Singapore 123000" + } + } + }, + { + "resourceType": "Organization", + "name": "MacRitchie Laboratory", + "type": "Accredited Laboratory", + "contact": { + "telecom": [ + { + "system": "phone", + "value": "+6562711188" + } + ], + "address": { + "type": "physical", + "use": "work", + "text": "2 Thomson Avenue 4 Singapore 098888" + } + } + } + ] + } +} diff --git a/src/sg/gov/moh/pdt-healthcert/1.0/schema.json b/src/sg/gov/moh/pdt-healthcert/1.0/schema.json new file mode 100644 index 0000000..d4402ef --- /dev/null +++ b/src/sg/gov/moh/pdt-healthcert/1.0/schema.json @@ -0,0 +1,471 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "https://schemata.openattestation.com/sg/gov/moh/pdt-healthcert/1.0", + "type": "object", + "definitions": { + "Reference": { + "description": "A reference from one resource to another", + "type": "object", + "required": ["reference"], + "properties": { + "reference": { + "description": "Literal reference, Relative, internal or absolute URL", + "type": "string" + } + }, + "additionalProperties": true + }, + "CodeableConcept": { + "description": "A concept that may be defined by a formal reference to a terminology or ontology or may be provided by text.", + "type": "object", + "required": ["coding"], + "properties": { + "coding": { + "description": "A reference to a code defined by a terminology system.", + "type": "array", + "items": { + "type": "object", + "required": ["system", "code", "display"], + "properties": { + "system": { + "description": "The identification of the code system that defines the meaning of the symbol in the code.", + "type": "string", + "examples": ["http://snomed.info/sct"], + "format": "uri" + }, + "code": { + "type": "string", + "examples": ["258500001"] + }, + "display": { + "description": "A representation of the meaning of the code in the system, following the rules of the system.", + "type": "string", + "examples": ["Nasopharyngeal swab"] + } + }, + "additionalProperties": true + }, + "minItems": 1 + } + }, + "additionalProperties": true + }, + "Patient": { + "description": "Demographics and other administrative information about an individual or animal receiving care or other health-related services.", + "type": "object", + "required": ["resourceType", "extension", "identifier", "name", "gender", "birthDate"], + "properties": { + "fullUrl": { + "type": "string", + "examples": ["urn:uuid:bbbb1321-4af5-424c-a0e1-ed3aab1c349d"] + }, + "resourceType": { + "type": "string", + "enum": ["Patient"] + }, + "extension": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": ["url", "code"], + "properties": { + "url": { + "type": "string", + "examples": ["http://hl7.org/fhir/StructureDefinition/patient-nationality"] + }, + "code": { + "type": "object", + "required": ["text"], + "properties": { + "text": { + "type": "string", + "examples": ["SG"] + } + }, + "additionalProperties": true + } + }, + "additionalProperties": true + } + }, + "identifier": { + "description": "An identifier for this patient.", + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": ["type", "value"], + "properties": { + "type": { + "oneOf": [ + { + "type": "string", + "examples": ["PPN"] + }, + { + "type": "object", + "required": ["text"], + "properties": { + "text": { + "type": "string", + "examples": ["NRIC"] + } + } + } + ] + }, + "value": { + "type": "string", + "examples": ["E7831177G"] + } + }, + "additionalProperties": true + } + }, + "name": { + "description": "A name associated with the individual.", + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": ["text"], + "properties": { + "text": { + "description": "Specifies the entire name as it should be displayed e.g. on an application UI. This may be provided instead of or as well as the specific parts.", + "type": "string", + "examples": ["Tan Chen Chen"] + } + }, + "additionalProperties": true + } + }, + "gender": { + "description": "Administrative Gender - the gender that the patient is considered to have for administration and record keeping purposes.", + "enum": ["male", "female"] + }, + "birthDate": { + "description": "The date of birth for the individual.", + "type": "string", + "format": "date" + } + }, + "additionalProperties": true + }, + "Specimen": { + "description": "A sample to be used for analysis.", + "type": "object", + "required": ["resourceType", "type", "collection"], + "properties": { + "fullUrl": { + "type": "string", + "examples": ["urn:uuid:bbbb1321-4af5-424c-a0e1-ed3aab1c349d"] + }, + "resourceType": { + "type": "string", + "enum": ["Specimen"] + }, + "type": { + "description": "The kind of material that forms the specimen.", + "$ref": "#/definitions/CodeableConcept" + }, + "collection": { + "description": "Details concerning the specimen collection.", + "type": "object", + "required": ["collectedDateTime"], + "properties": { + "collectedDateTime": { + "description": "Time when specimen was collected from subject - the physiologically relevant time.", + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": true + } + }, + "additionalProperties": true + }, + "Observation": { + "description": "Measurements and simple assertions made about a patient, device or other subject.", + "type": "object", + "required": [ + "resourceType", + "identifier", + "code", + "valueCodeableConcept", + "effectiveDateTime", + "qualification", + "status" + ], + "properties": { + "fullUrl": { + "type": "string", + "examples": ["urn:uuid:bbbb1321-4af5-424c-a0e1-ed3aab1c349d"] + }, + "resourceType": { + "type": "string", + "enum": ["Observation"] + }, + "identifier": { + "description": "A unique identifier assigned to this observation.", + "type": "array", + "items": { + "type": "object", + "required": ["value", "type"], + "properties": { + "value": { + "type": "string", + "description": "lab accession number", + "examples": ["123456789"] + }, + "type": { + "type": "string", + "examples": ["ACSN"] + } + }, + "additionalProperties": true + }, + "minItems": 1 + }, + "code": { + "description": "Describes what was observed. Sometimes this is called the observation \"name\".", + "$ref": "#/definitions/CodeableConcept" + }, + "valueCodeableConcept": { + "description": "The information determined as a result of making the observation, if the information has a simple value.", + "$ref": "#/definitions/CodeableConcept" + }, + "effectiveDateTime": { + "description": "The time or time-period the observed value is asserted as being true. For biological subjects - e.g. human patients - this is usually called the \"physiologically relevant time\". This is usually either the time of the procedure or of specimen collection, but very often the source of the date/time is not known, only the date/time itself.", + "type": "string", + "format": "date-time" + }, + "performer": { + "description": "Who was responsible for asserting the observed value as \"true\".", + "type": "object", + "required": ["name"], + "properties": { + "name": { + "type": "array", + "items": { + "type": "object", + "required": ["text"], + "properties": { + "text": { + "description": "Specifies the entire name as it should be displayed e.g. on an application UI. This may be provided instead of or as well as the specific parts.", + "type": "string", + "examples": ["Dr Michael Lim"] + } + }, + "additionalProperties": true + }, + "minItems": 1 + } + }, + "additionalProperties": true + }, + "qualification": { + "type": "array", + "items": { + "type": "object", + "required": ["identifier", "issuer"], + "properties": { + "identifier": { + "type": "string", + "examples": ["MCR 123214"] + }, + "issuer": { + "type": "string", + "examples": ["MOH"] + } + }, + "additionalProperties": true + }, + "minItems": 1 + }, + "status": { + "description": "The status of the result value.", + "enum": [ + "registered", + "preliminary", + "final", + "amended", + "corrected", + "cancelled", + "entered-in-error", + "unknown" + ] + }, + "specimen": { + "$ref": "#/definitions/Reference" + }, + "performerReference": { + "description": "Who is responsible for the observation", + "type": "array", + "items": { + "$ref": "#/definitions/Reference" + } + } + }, + "additionalProperties": true + }, + "Organization": { + "description": "A formally or informally recognized grouping of people or organizations formed for the purpose of achieving some form of collective action. Includes companies, institutions, corporations, departments, community groups, healthcare practice groups, payer/insurer, etc.", + "type": "object", + "required": ["resourceType", "name", "type", "contact"], + "properties": { + "fullUrl": { + "type": "string", + "examples": ["urn:uuid:bbbb1321-4af5-424c-a0e1-ed3aab1c349d"] + }, + "resourceType": { + "type": "string", + "enum": ["Organization"] + }, + "name": { + "description": "A name associated with the organization.", + "type": "string", + "examples": ["Raffles Medical Clinic", "MacRitchie Laboratory"] + }, + "type": { + "description": "The kind(s) of organization that this is.", + "type": "string", + "examples": ["Licensed Healthcare Provider", "Accredited Laboratory"] + }, + "endpoint": { + "description": "Technical endpoints providing access to services operated for the organization.", + "type": "object", + "required": ["address"], + "properties": { + "address": { + "type": "string", + "format": "uri", + "examples": ["https://www.rafflesmedical.com.sg"] + } + }, + "additionalProperties": true + }, + "contact": { + "description": "Contact for the organization for a certain purpose.", + "type": "object", + "required": ["telecom", "address"], + "properties": { + "telecom": { + "description": "A contact detail (e.g. a telephone number or an email address) by which the party may be contacted.", + "type": "array", + "items": { + "type": "object", + "required": ["system", "value"], + "properties": { + "system": { + "description": "Telecommunications form for contact point - what communications system is required to make use of the contact.", + "enum": ["phone", "fax", "email", "pager", "url", "sms", "other"], + "examples": ["phone"] + }, + "value": { + "description": "The actual contact point details, in a form that is meaningful to the designated communication system (i.e. phone number or email address).", + "type": "string", + "examples": ["+6563113111", "+6562711188"] + } + }, + "additionalProperties": true + }, + "minItems": 1 + }, + "address": { + "description": "Visiting or postal addresses for the contact.", + "type": "object", + "required": ["type", "use", "text"], + "properties": { + "type": { + "description": "Distinguishes between physical addresses (those you can visit) and mailing addresses (e.g. PO Boxes and care-of addresses). Most addresses are both.", + "enum": ["postal", "physical", "both"], + "examples": ["postal"] + }, + "use": { + "description": "The purpose of this address.", + "enum": ["home", "work", "temp", "old", "billing"], + "examples": ["home"] + }, + "text": { + "description": "Specifies the entire address as it should be displayed e.g. on a postal label. This may be provided instead of or as well as the specific parts.", + "type": "string", + "examples": [ + "MacRitchie Hospital Thomson Road Singapore 123000", + "2 Thomson Avenue 4 Singapore 098888" + ] + } + }, + "additionalProperties": true + } + }, + "additionalProperties": true + } + }, + "additionalProperties": true + } + }, + "required": ["id", "name", "validFrom", "fhirVersion", "fhirBundle"], + "properties": { + "id": { + "type": "string", + "examples": ["TEST001"] + }, + "name": { + "type": "string", + "examples": ["HealthCert"] + }, + "logo": { + "type": "string", + "description": "base64 encoded image" + }, + "validFrom": { + "type": "string", + "format": "date-time", + "description": "Date and time from which the document is considered valid" + }, + "fhirVersion": { + "type": "string", + "examples": ["4.0.1"] + }, + "fhirBundle": { + "type": "object", + "description": "A container for a collection of resources.", + "required": ["resourceType", "type", "entry"], + "properties": { + "resourceType": { + "description": "This is a Bundle resource", + "enum": ["Bundle"] + }, + "type": { + "description": "Indicates the purpose of this bundle - how it is intended to be used.", + "enum": ["collection"], + "examples": ["collection"] + }, + "entry": { + "description": "An entry in a bundle resource - will contain information about Patient, Speciment, Observation or Organization.", + "type": "array", + "minItems": 5, + "items": { + "oneOf": [ + { + "$ref": "#/definitions/Patient" + }, + { + "$ref": "#/definitions/Specimen" + }, + { + "$ref": "#/definitions/Observation" + }, + { + "$ref": "#/definitions/Organization" + } + ] + } + } + }, + "additionalProperties": true + } + }, + "additionalProperties": true +} diff --git a/src/sg/gov/moh/pdt-healthcert/1.0/schema.test.ts b/src/sg/gov/moh/pdt-healthcert/1.0/schema.test.ts new file mode 100644 index 0000000..95b36b7 --- /dev/null +++ b/src/sg/gov/moh/pdt-healthcert/1.0/schema.test.ts @@ -0,0 +1,1235 @@ +import Ajv from "ajv"; +import { cloneDeep, omit, set } from "lodash"; +import schema from "./schema.json"; +import sampleDocument from "./sample-document.json"; +import sampleDocumentWithReferences from "./sample-document-with-references.json"; +import axios from "axios"; + +function loadSchema(uri: string) { + return axios.get(uri).then(res => { + return res.data; + }); +} +const ajv = new Ajv({ allErrors: true, loadSchema: loadSchema }); +let validator: Ajv.ValidateFunction; + +describe("schema", () => { + beforeAll(async () => { + validator = await ajv.compileAsync(schema); + }); + it("should work with valid json", () => { + expect(validator(sampleDocument)).toBe(true); + }); + it("should work with valid json containing references", () => { + expect(validator(sampleDocumentWithReferences)).toBe(true); + }); + + describe("reference", () => { + it("should fail when specimen is not a valid reference", () => { + const document = omit(cloneDeep(sampleDocumentWithReferences), "fhirBundle.entry[2].specimen.reference"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].specimen", + keyword: "required", + message: "should have required property 'reference'", + params: { + missingProperty: "reference" + }, + schemaPath: "#/definitions/Reference/required" + }); + }); + it("should fail when performerReference is not a valid reference", () => { + const document = omit( + cloneDeep(sampleDocumentWithReferences), + "fhirBundle.entry[2].performerReference[0].reference" + ); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].performerReference[0]", + keyword: "required", + message: "should have required property 'reference'", + params: { + missingProperty: "reference" + }, + schemaPath: "#/definitions/Reference/required" + }); + }); + }); + + it("should fail when id is missing", () => { + const document = omit(cloneDeep(sampleDocument), "id"); + expect(validator(document)).toBe(false); + expect(validator.errors).toMatchInlineSnapshot(` + Array [ + Object { + "dataPath": "", + "keyword": "required", + "message": "should have required property 'id'", + "params": Object { + "missingProperty": "id", + }, + "schemaPath": "#/required", + }, + ] + `); + }); + + it("should fail when name is missing", () => { + const document = omit(cloneDeep(sampleDocument), "name"); + expect(validator(document)).toBe(false); + expect(validator.errors).toMatchInlineSnapshot(` + Array [ + Object { + "dataPath": "", + "keyword": "required", + "message": "should have required property 'name'", + "params": Object { + "missingProperty": "name", + }, + "schemaPath": "#/required", + }, + ] + `); + }); + + it("should fail when validFrom is missing", () => { + const document = omit(cloneDeep(sampleDocument), "validFrom"); + expect(validator(document)).toBe(false); + expect(validator.errors).toMatchInlineSnapshot(` + Array [ + Object { + "dataPath": "", + "keyword": "required", + "message": "should have required property 'validFrom'", + "params": Object { + "missingProperty": "validFrom", + }, + "schemaPath": "#/required", + }, + ] + `); + }); + + it("should fail when validFrom is not a date-time", () => { + const document = set(cloneDeep(sampleDocument), "validFrom", "FOO"); + expect(validator(document)).toBe(false); + expect(validator.errors).toMatchInlineSnapshot(` + Array [ + Object { + "dataPath": ".validFrom", + "keyword": "format", + "message": "should match format \\"date-time\\"", + "params": Object { + "format": "date-time", + }, + "schemaPath": "#/properties/validFrom/format", + }, + ] + `); + }); + + it("should fail when fhirVersion is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirVersion"); + expect(validator(document)).toBe(false); + expect(validator.errors).toMatchInlineSnapshot(` + Array [ + Object { + "dataPath": "", + "keyword": "required", + "message": "should have required property 'fhirVersion'", + "params": Object { + "missingProperty": "fhirVersion", + }, + "schemaPath": "#/required", + }, + ] + `); + }); + + it("should fail when fhirBundle is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle"); + expect(validator(document)).toBe(false); + expect(validator.errors).toMatchInlineSnapshot(` + Array [ + Object { + "dataPath": "", + "keyword": "required", + "message": "should have required property 'fhirBundle'", + "params": Object { + "missingProperty": "fhirBundle", + }, + "schemaPath": "#/required", + }, + ] + `); + }); + + describe("fhirBundle", () => { + it("should fail when resourceType is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.resourceType"); + expect(validator(document)).toBe(false); + expect(validator.errors).toMatchInlineSnapshot(` + Array [ + Object { + "dataPath": ".fhirBundle", + "keyword": "required", + "message": "should have required property 'resourceType'", + "params": Object { + "missingProperty": "resourceType", + }, + "schemaPath": "#/properties/fhirBundle/required", + }, + ] + `); + }); + it("it should fail when resourceType is not Bundle", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.resourceType", "FOO"); + expect(validator(document)).toBe(false); + expect(validator.errors).toMatchInlineSnapshot(` + Array [ + Object { + "dataPath": ".fhirBundle.resourceType", + "keyword": "enum", + "message": "should be equal to one of the allowed values", + "params": Object { + "allowedValues": Array [ + "Bundle", + ], + }, + "schemaPath": "#/properties/fhirBundle/properties/resourceType/enum", + }, + ] + `); + }); + + it("should fail when type is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.type"); + expect(validator(document)).toBe(false); + expect(validator.errors).toMatchInlineSnapshot(` + Array [ + Object { + "dataPath": ".fhirBundle", + "keyword": "required", + "message": "should have required property 'type'", + "params": Object { + "missingProperty": "type", + }, + "schemaPath": "#/properties/fhirBundle/required", + }, + ] + `); + }); + it("it should fail when type is not collection", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.type", "FOO"); + expect(validator(document)).toBe(false); + expect(validator.errors).toMatchInlineSnapshot(` + Array [ + Object { + "dataPath": ".fhirBundle.type", + "keyword": "enum", + "message": "should be equal to one of the allowed values", + "params": Object { + "allowedValues": Array [ + "collection", + ], + }, + "schemaPath": "#/properties/fhirBundle/properties/type/enum", + }, + ] + `); + }); + + it("should fail when entry is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry"); + expect(validator(document)).toBe(false); + expect(validator.errors).toMatchInlineSnapshot(` + Array [ + Object { + "dataPath": ".fhirBundle", + "keyword": "required", + "message": "should have required property 'entry'", + "params": Object { + "missingProperty": "entry", + }, + "schemaPath": "#/properties/fhirBundle/required", + }, + ] + `); + }); + + it("should fail when entry is an array with no items", () => { + const document = { + ...sampleDocument, + fhirBundle: { + ...sampleDocument.fhirBundle, + entry: [] + } + }; + expect(validator(document)).toBe(false); + expect(validator.errors).toMatchInlineSnapshot(` + Array [ + Object { + "dataPath": ".fhirBundle.entry", + "keyword": "minItems", + "message": "should NOT have fewer than 5 items", + "params": Object { + "limit": 5, + }, + "schemaPath": "#/properties/fhirBundle/properties/entry/minItems", + }, + ] + `); + }); + it("should fail when entry is missing a resourceType", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[0].resourceType"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[0]", + keyword: "required", + message: "should have required property 'resourceType'", + params: { + missingProperty: "resourceType" + }, + schemaPath: "#/definitions/Patient/required" + }); + // this is for specimen and observation, there is a problem in ajv with $ref + // https://github.com/ajv-validator/ajv/issues/512 + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[0]", + keyword: "required", + message: "should have required property 'resourceType'", + params: { + missingProperty: "resourceType" + }, + schemaPath: "#/required" + }); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[0]", + keyword: "required", + message: "should have required property 'resourceType'", + params: { + missingProperty: "resourceType" + }, + schemaPath: "#/definitions/Organization/required" + }); + }); + + describe("Patient", () => { + it("should fail when extension is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[0].extension"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[0]", + keyword: "required", + message: "should have required property 'extension'", + params: { + missingProperty: "extension" + }, + schemaPath: "#/definitions/Patient/required" + }); + }); + it("should fail when extension is empty", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.entry[0].extension", []); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[0].extension", + keyword: "minItems", + message: "should NOT have fewer than 1 items", + params: { limit: 1 }, + schemaPath: "#/definitions/Patient/properties/extension/minItems" + }); + }); + + it("should fail when extension url is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[0].extension[0].url"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + keyword: "required", + dataPath: ".fhirBundle.entry[0].extension[0]", + schemaPath: "#/definitions/Patient/properties/extension/items/required", + params: { missingProperty: "url" }, + message: "should have required property 'url'" + }); + }); + + it("should fail when extension code is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[0].extension[0].code"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + keyword: "required", + dataPath: ".fhirBundle.entry[0].extension[0]", + schemaPath: "#/definitions/Patient/properties/extension/items/required", + params: { missingProperty: "code" }, + message: "should have required property 'code'" + }); + }); + it("should fail when extension code text is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[0].extension[0].code.text"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + keyword: "required", + dataPath: ".fhirBundle.entry[0].extension[0].code", + schemaPath: "#/definitions/Patient/properties/extension/items/properties/code/required", + params: { missingProperty: "text" }, + message: "should have required property 'text'" + }); + }); + + it("should fail when identifier is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[0].identifier"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[0]", + keyword: "required", + message: "should have required property 'identifier'", + params: { + missingProperty: "identifier" + }, + schemaPath: "#/definitions/Patient/required" + }); + }); + it("should fail when identifier is empty", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.entry[0].identifier", []); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[0].identifier", + keyword: "minItems", + message: "should NOT have fewer than 1 items", + params: { limit: 1 }, + schemaPath: "#/definitions/Patient/properties/identifier/minItems" + }); + }); + + it("should fail when identifier type is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[0].identifier[0].type"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + keyword: "required", + dataPath: ".fhirBundle.entry[0].identifier[0]", + schemaPath: "#/definitions/Patient/properties/identifier/items/required", + params: { missingProperty: "type" }, + message: "should have required property 'type'" + }); + }); + it("should fail when identifier type text is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[0].identifier[1].type.text"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + keyword: "required", + dataPath: ".fhirBundle.entry[0].identifier[1].type", + schemaPath: "#/definitions/Patient/properties/identifier/items/properties/type/oneOf/1/required", + params: { missingProperty: "text" }, + message: "should have required property 'text'" + }); + }); + it("should fail when identifier value is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[0].identifier[0].value"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + keyword: "required", + dataPath: ".fhirBundle.entry[0].identifier[0]", + schemaPath: "#/definitions/Patient/properties/identifier/items/required", + params: { missingProperty: "value" }, + message: "should have required property 'value'" + }); + }); + + it("should fail when name is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[0].name"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[0]", + keyword: "required", + message: "should have required property 'name'", + params: { + missingProperty: "name" + }, + schemaPath: "#/definitions/Patient/required" + }); + }); + it("should fail when name is empty", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.entry[0].name", []); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[0].name", + keyword: "minItems", + message: "should NOT have fewer than 1 items", + params: { limit: 1 }, + schemaPath: "#/definitions/Patient/properties/name/minItems" + }); + }); + + it("should fail when name text is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[0].name[0].text"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[0].name[0]", + keyword: "required", + message: "should have required property 'text'", + params: { + missingProperty: "text" + }, + schemaPath: "#/definitions/Patient/properties/name/items/required" + }); + }); + it("should fail when gender is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[0].gender"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[0]", + keyword: "required", + message: "should have required property 'gender'", + params: { + missingProperty: "gender" + }, + schemaPath: "#/definitions/Patient/required" + }); + }); + it("should fail when gender is not male or female", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.entry[0].gender", "FOO"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[0].gender", + keyword: "enum", + message: "should be equal to one of the allowed values", + params: { allowedValues: ["male", "female"] }, + schemaPath: "#/definitions/Patient/properties/gender/enum" + }); + }); + + it("should fail when birthDate is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[0].birthDate"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[0]", + keyword: "required", + message: "should have required property 'birthDate'", + params: { + missingProperty: "birthDate" + }, + schemaPath: "#/definitions/Patient/required" + }); + }); + it("should fail when birthDate is not a date", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.entry[0].birthDate", "FOO"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[0].birthDate", + keyword: "format", + message: 'should match format "date"', + params: { format: "date" }, + schemaPath: "#/definitions/Patient/properties/birthDate/format" + }); + }); + }); + + describe("Specimen", () => { + it("should fail when type is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[1].type"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[1]", + keyword: "required", + message: "should have required property 'type'", + params: { + missingProperty: "type" + }, + schemaPath: "#/required" + }); + }); + it("should fail when type coding is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[1].type.coding"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[1].type", + keyword: "required", + message: "should have required property 'coding'", + params: { + missingProperty: "coding" + }, + schemaPath: "#/definitions/CodeableConcept/required" + }); + }); + it("should fail when type coding system is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[1].type.coding[0].system"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[1].type.coding[0]", + keyword: "required", + message: "should have required property 'system'", + params: { + missingProperty: "system" + }, + schemaPath: "#/definitions/CodeableConcept/properties/coding/items/required" + }); + }); + it("should fail when code coding system is not a URI", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.entry[1].type.coding[0].system", "FOO"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[1].type.coding[0].system", + keyword: "format", + message: 'should match format "uri"', + params: { format: "uri" }, + schemaPath: "#/definitions/CodeableConcept/properties/coding/items/properties/system/format" + }); + }); + it("should fail when type coding code is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[1].type.coding[0].code"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[1].type.coding[0]", + keyword: "required", + message: "should have required property 'code'", + params: { + missingProperty: "code" + }, + schemaPath: "#/definitions/CodeableConcept/properties/coding/items/required" + }); + }); + it("should fail when type coding display is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[1].type.coding[0].display"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[1].type.coding[0]", + keyword: "required", + message: "should have required property 'display'", + params: { + missingProperty: "display" + }, + schemaPath: "#/definitions/CodeableConcept/properties/coding/items/required" + }); + }); + it("should fail when coding is empty", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.entry[1].type.coding", []); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[1].type.coding", + keyword: "minItems", + message: "should NOT have fewer than 1 items", + params: { limit: 1 }, + schemaPath: "#/definitions/CodeableConcept/properties/coding/minItems" + }); + }); + + it("should fail when collection is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[1].collection"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[1]", + keyword: "required", + message: "should have required property 'collection'", + params: { + missingProperty: "collection" + }, + schemaPath: "#/required" + }); + }); + it("should fail when collection collectedDateTime is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[1].collection.collectedDateTime"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[1].collection", + keyword: "required", + message: "should have required property 'collectedDateTime'", + params: { + missingProperty: "collectedDateTime" + }, + schemaPath: "#/properties/collection/required" + }); + }); + it("should fail when collection collectedDateTime is not a date", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.entry[1].collection.collectedDateTime", "FOO"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[1].collection.collectedDateTime", + keyword: "format", + message: 'should match format "date-time"', + params: { format: "date-time" }, + schemaPath: "#/properties/collection/properties/collectedDateTime/format" + }); + }); + }); + + describe("Observation", () => { + it("should fail when identifier is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[2].identifier"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2]", + keyword: "required", + message: "should have required property 'identifier'", + params: { + missingProperty: "identifier" + }, + schemaPath: "#/required" + }); + }); + it("should fail when identifier is empty", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.entry[2].identifier", []); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].identifier", + keyword: "minItems", + message: "should NOT have fewer than 1 items", + params: { limit: 1 }, + schemaPath: "#/properties/identifier/minItems" + }); + }); + it("should fail when identifier value is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[2].identifier[0].value"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].identifier[0]", + keyword: "required", + message: "should have required property 'value'", + params: { + missingProperty: "value" + }, + schemaPath: "#/properties/identifier/items/required" + }); + }); + it("should fail when identifier type is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[2].identifier[0].type"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].identifier[0]", + keyword: "required", + message: "should have required property 'type'", + params: { + missingProperty: "type" + }, + schemaPath: "#/properties/identifier/items/required" + }); + }); + + it("should fail when code is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[2].code"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2]", + keyword: "required", + message: "should have required property 'code'", + params: { + missingProperty: "code" + }, + schemaPath: "#/required" + }); + }); + it("should fail when code coding is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[2].code.coding"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].code", + keyword: "required", + message: "should have required property 'coding'", + params: { + missingProperty: "coding" + }, + schemaPath: "#/definitions/CodeableConcept/required" + }); + }); + it("should fail when code coding system is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[2].code.coding[0].system"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].code.coding[0]", + keyword: "required", + message: "should have required property 'system'", + params: { + missingProperty: "system" + }, + schemaPath: "#/definitions/CodeableConcept/properties/coding/items/required" + }); + }); + it("should fail when code coding system is not a URI", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.entry[2].code.coding[0].system", "FOO"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].code.coding[0].system", + keyword: "format", + message: 'should match format "uri"', + params: { format: "uri" }, + schemaPath: "#/definitions/CodeableConcept/properties/coding/items/properties/system/format" + }); + }); + it("should fail when code coding code is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[2].code.coding[0].code"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].code.coding[0]", + keyword: "required", + message: "should have required property 'code'", + params: { + missingProperty: "code" + }, + schemaPath: "#/definitions/CodeableConcept/properties/coding/items/required" + }); + }); + it("should fail when code coding display is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[2].code.coding[0].display"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].code.coding[0]", + keyword: "required", + message: "should have required property 'display'", + params: { + missingProperty: "display" + }, + schemaPath: "#/definitions/CodeableConcept/properties/coding/items/required" + }); + }); + it("should fail when code coding is empty", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.entry[2].code.coding", []); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].code.coding", + keyword: "minItems", + message: "should NOT have fewer than 1 items", + params: { limit: 1 }, + schemaPath: "#/definitions/CodeableConcept/properties/coding/minItems" + }); + }); + + it("should fail when valueCodeableConcept is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[2].valueCodeableConcept"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2]", + keyword: "required", + message: "should have required property 'valueCodeableConcept'", + params: { + missingProperty: "valueCodeableConcept" + }, + schemaPath: "#/required" + }); + }); + it("should fail when valueCodeableConcept coding is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[2].valueCodeableConcept.coding"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].valueCodeableConcept", + keyword: "required", + message: "should have required property 'coding'", + params: { + missingProperty: "coding" + }, + schemaPath: "#/definitions/CodeableConcept/required" + }); + }); + it("should fail when valueCodeableConcept coding system is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[2].valueCodeableConcept.coding[0].system"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].valueCodeableConcept.coding[0]", + keyword: "required", + message: "should have required property 'system'", + params: { + missingProperty: "system" + }, + schemaPath: "#/definitions/CodeableConcept/properties/coding/items/required" + }); + }); + it("should fail when valueCodeableConcept coding system is not a URI", () => { + const document = set( + cloneDeep(sampleDocument), + "fhirBundle.entry[2].valueCodeableConcept.coding[0].system", + "FOO" + ); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].valueCodeableConcept.coding[0].system", + keyword: "format", + message: 'should match format "uri"', + params: { format: "uri" }, + schemaPath: "#/definitions/CodeableConcept/properties/coding/items/properties/system/format" + }); + }); + it("should fail when valueCodeableConcept coding code is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[2].valueCodeableConcept.coding[0].code"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].valueCodeableConcept.coding[0]", + keyword: "required", + message: "should have required property 'code'", + params: { + missingProperty: "code" + }, + schemaPath: "#/definitions/CodeableConcept/properties/coding/items/required" + }); + }); + it("should fail when valueCodeableConcept coding display is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[2].valueCodeableConcept.coding[0].display"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + keyword: "required", + dataPath: ".fhirBundle.entry[2].valueCodeableConcept.coding[0]", + schemaPath: "#/definitions/CodeableConcept/properties/coding/items/required", + params: { + missingProperty: "display" + }, + message: "should have required property 'display'" + }); + }); + it("should fail when valueCodeableConcept coding is empty", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.entry[2].valueCodeableConcept.coding", []); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].valueCodeableConcept.coding", + keyword: "minItems", + message: "should NOT have fewer than 1 items", + params: { limit: 1 }, + schemaPath: "#/definitions/CodeableConcept/properties/coding/minItems" + }); + }); + + it("should fail when effectiveDateTime is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[2].effectiveDateTime"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2]", + keyword: "required", + message: "should have required property 'effectiveDateTime'", + params: { + missingProperty: "effectiveDateTime" + }, + schemaPath: "#/required" + }); + }); + it("should fail when effectiveDateTime is not a date", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.entry[2].effectiveDateTime", "FOO"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].effectiveDateTime", + keyword: "format", + message: 'should match format "date-time"', + params: { format: "date-time" }, + schemaPath: "#/properties/effectiveDateTime/format" + }); + }); + + it("should fail when status is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[2].status"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2]", + keyword: "required", + message: "should have required property 'status'", + params: { + missingProperty: "status" + }, + schemaPath: "#/required" + }); + }); + it("should fail when status is not an allowed value", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.entry[2].status", "FOO"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].status", + keyword: "enum", + message: "should be equal to one of the allowed values", + params: { + allowedValues: [ + "registered", + "preliminary", + "final", + "amended", + "corrected", + "cancelled", + "entered-in-error", + "unknown" + ] + }, + schemaPath: "#/properties/status/enum" + }); + }); + + it("should fail when performer name is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[2].performer.name"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].performer", + keyword: "required", + message: "should have required property 'name'", + params: { + missingProperty: "name" + }, + schemaPath: "#/properties/performer/required" + }); + }); + it("should fail when performer name is empty", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.entry[2].performer.name", []); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].performer.name", + keyword: "minItems", + message: "should NOT have fewer than 1 items", + params: { limit: 1 }, + schemaPath: "#/properties/performer/properties/name/minItems" + }); + }); + it("should fail when performer name text is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[2].performer.name[0].text"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].performer.name[0]", + keyword: "required", + message: "should have required property 'text'", + params: { missingProperty: "text" }, + schemaPath: "#/properties/performer/properties/name/items/required" + }); + }); + + it("should fail when qualification is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[2].qualification"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2]", + keyword: "required", + message: "should have required property 'qualification'", + params: { + missingProperty: "qualification" + }, + schemaPath: "#/required" + }); + }); + it("should fail when qualification is empty", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.entry[2].qualification", []); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].qualification", + keyword: "minItems", + message: "should NOT have fewer than 1 items", + params: { limit: 1 }, + schemaPath: "#/properties/qualification/minItems" + }); + }); + it("should fail when qualification identifier is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[2].qualification[0].identifier"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].qualification[0]", + keyword: "required", + message: "should have required property 'identifier'", + params: { missingProperty: "identifier" }, + schemaPath: "#/properties/qualification/items/required" + }); + }); + it("should fail when qualification issuer is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[2].qualification[0].issuer"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[2].qualification[0]", + keyword: "required", + message: "should have required property 'issuer'", + params: { missingProperty: "issuer" }, + schemaPath: "#/properties/qualification/items/required" + }); + }); + }); + + describe("Organization", () => { + it("should fail when name is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[3].name"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[3]", + keyword: "required", + message: "should have required property 'name'", + params: { + missingProperty: "name" + }, + schemaPath: "#/definitions/Organization/required" + }); + }); + it("should fail when type is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[3].type"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[3]", + keyword: "required", + message: "should have required property 'type'", + params: { + missingProperty: "type" + }, + schemaPath: "#/definitions/Organization/required" + }); + }); + it("should fail when endpoint address is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[3].endpoint.address"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[3].endpoint", + keyword: "required", + message: "should have required property 'address'", + params: { + missingProperty: "address" + }, + schemaPath: "#/definitions/Organization/properties/endpoint/required" + }); + }); + it("should fail when endpoint address is not a URI", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.entry[3].endpoint.address", "FOO"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[3].endpoint.address", + keyword: "format", + message: 'should match format "uri"', + params: { format: "uri" }, + schemaPath: "#/definitions/Organization/properties/endpoint/properties/address/format" + }); + }); + + it("should fail when contact is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[3].contact"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[3]", + keyword: "required", + message: "should have required property 'contact'", + params: { + missingProperty: "contact" + }, + schemaPath: "#/definitions/Organization/required" + }); + }); + + it("should fail when contact telecom is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[3].contact.telecom"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[3].contact", + keyword: "required", + message: "should have required property 'telecom'", + params: { + missingProperty: "telecom" + }, + schemaPath: "#/definitions/Organization/properties/contact/required" + }); + }); + it("should fail when contact telecom is empty", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.entry[3].contact.telecom", []); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[3].contact.telecom", + keyword: "minItems", + message: "should NOT have fewer than 1 items", + params: { limit: 1 }, + schemaPath: "#/definitions/Organization/properties/contact/properties/telecom/minItems" + }); + }); + it("should fail when contact telecom system is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[3].contact.telecom[0].system"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[3].contact.telecom[0]", + keyword: "required", + message: "should have required property 'system'", + params: { + missingProperty: "system" + }, + schemaPath: "#/definitions/Organization/properties/contact/properties/telecom/items/required" + }); + }); + it("should fail when contact telecom system is not one of the allowed values", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.entry[3].contact.telecom[0].system", "FOO"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[3].contact.telecom[0].system", + keyword: "enum", + message: "should be equal to one of the allowed values", + params: { allowedValues: ["phone", "fax", "email", "pager", "url", "sms", "other"] }, + schemaPath: "#/definitions/Organization/properties/contact/properties/telecom/items/properties/system/enum" + }); + }); + it("should fail when contact telecom value is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[3].contact.telecom[0].value"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[3].contact.telecom[0]", + keyword: "required", + message: "should have required property 'value'", + params: { + missingProperty: "value" + }, + schemaPath: "#/definitions/Organization/properties/contact/properties/telecom/items/required" + }); + }); + + it("should fail when contact address is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[3].contact.address"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[3].contact", + keyword: "required", + message: "should have required property 'address'", + params: { + missingProperty: "address" + }, + schemaPath: "#/definitions/Organization/properties/contact/required" + }); + }); + it("should fail when contact address type is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[3].contact.address.type"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[3].contact.address", + keyword: "required", + message: "should have required property 'type'", + params: { + missingProperty: "type" + }, + schemaPath: "#/definitions/Organization/properties/contact/properties/address/required" + }); + }); + it("should fail when contact address use is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[3].contact.address.use"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[3].contact.address", + keyword: "required", + message: "should have required property 'use'", + params: { + missingProperty: "use" + }, + schemaPath: "#/definitions/Organization/properties/contact/properties/address/required" + }); + }); + it("should fail when contact address text is missing", () => { + const document = omit(cloneDeep(sampleDocument), "fhirBundle.entry[3].contact.address.text"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[3].contact.address", + keyword: "required", + message: "should have required property 'text'", + params: { + missingProperty: "text" + }, + schemaPath: "#/definitions/Organization/properties/contact/properties/address/required" + }); + }); + it("should fail when contact address type is not one of the allowed values", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.entry[3].contact.address.type", "FOO"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[3].contact.address.type", + keyword: "enum", + message: "should be equal to one of the allowed values", + params: { allowedValues: ["postal", "physical", "both"] }, + schemaPath: "#/definitions/Organization/properties/contact/properties/address/properties/type/enum" + }); + }); + it("should fail when contact address use is not physical", () => { + const document = set(cloneDeep(sampleDocument), "fhirBundle.entry[3].contact.address.use", "FOO"); + expect(validator(document)).toBe(false); + expect(validator.errors).toContainEqual({ + dataPath: ".fhirBundle.entry[3].contact.address.use", + keyword: "enum", + message: "should be equal to one of the allowed values", + params: { allowedValues: ["home", "work", "temp", "old", "billing"] }, + schemaPath: "#/definitions/Organization/properties/contact/properties/address/properties/use/enum" + }); + }); + }); + }); +}); diff --git a/src/sg/gov/moh/vaccination-healthcert/1.0/interim-vaccination-healthcert-unwrapped.json b/src/sg/gov/moh/vaccination-healthcert/1.0/interim-vaccination-healthcert-unwrapped.json new file mode 100644 index 0000000..fa65008 --- /dev/null +++ b/src/sg/gov/moh/vaccination-healthcert/1.0/interim-vaccination-healthcert-unwrapped.json @@ -0,0 +1,187 @@ +{ + "id": "e63b2496-2531-47ff-9155-c41e291af36a", + "name": "VaccinationHealthCert", + "validFrom": "2021-05-21T03:38:30.464Z", + "fhirVersion": "4.0.1", + "fhirBundle": { + "resourceType": "Bundle", + "type": "collection", + "entry": [ + { + "fullUrl": "urn:uuid:1420b5d3-86df-4157-b952-e75bf79ca2aa", + "resourceType": "Patient", + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/patient-nationality", + "code": { + "text": "SG" + } + } + ], + "identifier": [ + { + "type": { + "text": "NRIC" + }, + "value": "S9098989Z" + }, + { + "type": "PPN", + "value": "E7831177G" + } + ], + "name": [ + { + "text": "Tan Chen Chen" + } + ], + "gender": "female", + "birthDate": "1990-01-15" + }, + { + "fullUrl": "urn:uuid:5e2b4ebe-6d51-4b70-a36d-da70e1763a7c", + "resourceType": "Location", + "id": "HCI000", + "name": "Vaccination site approved by Ministry of Health (MOH), Singapore [HCI000]", + "address": { + "country": "SG" + } + }, + { + "fullUrl": "urn:uuid:5eca16e7-8870-4e8f-ac3b-45baeed5f203", + "resourceType": "Location", + "id": "HCI000", + "name": "Vaccination site approved by Ministry of Health (MOH), Singapore [HCI000]", + "address": { + "country": "SG" + } + }, + { + "fullUrl": "urn:uuid:f50fe4d9-03a0-47f5-a7de-3352986f8287", + "resourceType": "Immunization", + "vaccineCode": { + "coding": [ + { + "system": "http://standards.ihis.com.sg", + "code": "1234567890123456", + "display": "PFIZER-BIONTECH COVID-19 Vaccine [Tozinameran] Injection" + } + ] + }, + "lotNumber": "Lot12345", + "occurrenceDateTime": "2021-02-14", + "patient": { + "reference": "urn:uuid:1420b5d3-86df-4157-b952-e75bf79ca2aa" + }, + "location": { + "reference": "urn:uuid:5e2b4ebe-6d51-4b70-a36d-da70e1763a7c" + }, + "performer": [ + { + "actor": { + "display": "Designated vaccinator by MOH-approved vaccination site" + } + } + ] + }, + { + "fullUrl": "urn:uuid:37b8153e-8373-4fbd-9e4c-85a7c06eb144", + "resourceType": "Immunization", + "vaccineCode": { + "coding": [ + { + "system": "http://standards.ihis.com.sg", + "code": "1234567890123456", + "display": "PFIZER-BIONTECH COVID-19 Vaccine [Tozinameran] Injection" + } + ] + }, + "lotNumber": "Lot97531", + "occurrenceDateTime": "2021-03-03", + "patient": { + "reference": "urn:uuid:1420b5d3-86df-4157-b952-e75bf79ca2aa" + }, + "location": { + "reference": "urn:uuid:5eca16e7-8870-4e8f-ac3b-45baeed5f203" + }, + "performer": [ + { + "actor": { + "display": "Designated vaccinator by MOH-approved vaccination site" + } + } + ] + }, + { + "fullUrl": "urn:uuid:4809a8cc-5b10-4f14-a0c6-66e5c722d06c", + "resourceType": "ImmunizationRecommendation", + "recommendation": [ + { + "targetDisease": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "840539006", + "display": "COVID-19" + } + ] + }, + "forecastStatus": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "complete", + "display": "Complete" + } + ] + }, + "dateCriterion": [ + { + "code": { + "coding": [ + { + "system": "", + "code": "effective", + "display": "Effective" + } + ] + }, + "value": "2021-03-17" + } + ] + } + ], + "patient": { + "reference": "urn:uuid:1420b5d3-86df-4157-b952-e75bf79ca2aa" + } + } + ] + }, + "issuers": [ + { + "name": "MINISTRY OF HEALTH (SINGAPORE)", + "id": "did:ethr:0x82948a537e886Ea8ffF5Bd29DDA95224bF74c35F", + "revocation": { + "type": "REVOCATION_STORE", + "location": "0x7384702915962d70Ef202Ffb38152c4c89cD98dA" + }, + "identityProof": { + "type": "DNS-DID", + "location": "moh.gov.sg", + "key": "did:ethr:0x82948a537e886Ea8ffF5Bd29DDA95224bF74c35F#controller" + } + } + ], + "$template": { + "name": "VACCINATION_CERT", + "type": "EMBEDDED_RENDERER", + "url": "https://healthcert.renderer.moh.gov.sg/" + }, + "notarisationMetadata": { + "reference": "e63b2496-2531-47ff-9155-c41e291af36a", + "notarisedOn": "2021-05-21T03:38:30.464Z", + "passportNumber": "E7831177G", + "url": "https://www.verify.gov.sg/verify?q=%7B%22type%22%3A%22DOCUMENT%22%2C%22payload%22%3A%7B%22uri%22%3A%22https%3A%2F%2Fapi-vaccine.storage.staging.notarise.io%2Fdocument%2Fc8b5a22a-05c6-41d9-b5bd-e25b3c8ccdf5%22%2C%22key%22%3A%22a33d9f04a5f30ff970693c4b2d8fd8e205a80df7ad224905ec023110418616c9%22%2C%22permittedActions%22%3A%5B%22VIEW%22%2C%22STORE%22%5D%7D%7D" + }, + "logo": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOAAAAA6CAYAAACpiFWoAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAClySURBVHgB7V0HeBVFu363nJJeSUhC70VBBFF+eheQpoK/YsFesGFBOkFBVEQEVBT1F9uv0gNIDRCaNBUECdITQkgh9SQnp+7O/WY3PSchRO/zeK/n5VmyOzM7beebr83MEXCdiF3Rzgg1946A4Mh+TSLbdFQUn/Z2l5sxNe9EjvXisazCnF2G41c2xMZChRdeePHXYVpc1LjP9vZPSS36ijGWyQqLrMzhZBrsLjvLsiSy/WfeYot39rw4e1Xju+CFF178ebwZ3ybs9bgmW5JylxOpqSxubzq7Z1YcazBkNtv2SxKFMBZ/ycrOWhgroge3O5/tPfMOm7220brYXX1keOGFF3XDvG9NzZYc6JPE2El2IZOxgRO/YrjxUYb24xlixrA1+08wFxHgN4nJbPWZVLbmbBr7LUvnipeytrM3f2x78pWvOkTACy+8qAKxpsgpHwSE+Ubf+POzt21qvOtESzQb9QK279oDhAcCRgOEtk0R3rQxJErr42OGJAjwlQWczUnF5qRsNAwbgAl9VrZTjMZftv8cEgQvvPCiAqolQDKiiH7NIg893+dwyJpDLvS75wnATKRmoFdMRiz8dAaS4+aim5oPlp2N25tEoHmLKFgVFX6yDLfqxLrzGQjwbY+pQ75u8EtO6C544YUXtcOkTY2/c7B97HgyY+j8FEO/iQxdHmcxD81niVy+PLaPsQdHs4yb27DUzu1Y4SP3MXY5maVT1NrzaWwTXRvOX2Fx5zM1cfTQhQ3speXSZ/DCCy9qxmd7TMMOJb2pEY7PsFiGW4kA+7zIMHASu8QDN69gyQaw5HBfdrl9U5bSphG7aAS70qw+Y7/9zNIoyZpzaWxbUgZbezaV7U21aHmt/nUie2ld43/BCy+80OBRBM0ouGFV18ZT8MDifbCdOwPE1AOy8vDl8llo+PsBXBoyFlLrZpBbtITg6wcxIBCmLp3gLChA2p13oH5+Dpo1rg+LzYlA0hVTLHlIswN3dopFiBywHl544YUGqXLAy9/6T5884uNBx8+1wHOvzgRaNgIuX0X7uwfhk2E3IX/4EDgkBik0DFDL+drpXg6vB8f5czBmpqP+iNG45GJQ3S4yzEhIyi9Cm9BQGCTVt/2Q+Ktbv1WPwAsv/uGowAEX7ropuF2z297wk2/Hk59/T5ZOcuEZiEadLkx56A5gywrkHD8JOTqmIvEVgxyAMLZsjaw1q2E4nYjmUUEoondlUYRCRpnEXAXdmj8Oh9ozdsWKMRK88OIfjgoEmH41+ak7b34Oh06r+HXbdqBhJJBtQXjPzhgXY4Zj4SLI9SM9El8pTCaohVYocatBKSGJEsg3jwCjEaezrvIE6N367nopAesnwAsv/uGoQIDRwa2eDDaOwEtfrSN2RgEkOiKvELcPuI2o8zyyTpyEFEaiJ2PV58hF0ego5G/ZBN+iIgREhJNLgmkFMShItgIdGtyOPGuD5+CFF/9wlBLgzFWNhg/uNLZJVj7w0/69RI31SjndLW0aA6dOwmUtJJZ2bclRDA6B7ewZyPROQKABLsqHk6yfQUbi1Tz4mZqiW9vRLWLjorrgz2JMLUTZ2FhRu2qGgD6xco3x+lU5zDPujB2AUVPHY8T022vIr+b6lL/X6y+Uu68K3hdjptTDtTDkORMGx4aiNhj8ygAMn/4o7po6FnfOaoDa1bc24WWozbepXVhN4Tpq/sbXglDruFq2qbQyTHI91Dq8HxasTSaLZy4ZXxoCikJU44OWof7A3vMQOPHVxP1KwHU+WxHElCSEdL4FKar+Dl8pY3EUwI5gdIy+HbuOfTqRgsehrhgxpTuc4g8YNV2BKg/H+tjjVdIMe6YvfsOXUJ0CEcRwrHvzmBY+cnoSmHoB69/sp+c1bQKYcwrunrEHq964t2pZ01ZTl3VB3NxG2jP/kEHu89QhRyjs7tJ0d0zqT327Foo7gDqM+oLaPnaeDVlJL2HnJx9raca+SYqwdVm53HkHXaKrccVChTGIm7OKyl6PX123YuQ0nbiOuqj+03g83bi/hyJNwca5qQgJEXFF+BEjp9aD8WwLrFypVGnHvdO/hIXdi0DWi54OojqMe/tF2Ivmw+WWIVD13DReVMpuzOub6eYprIy9VNY300dQmg/oLhoeDHuEIhgNIfSOs0rMyOl346hzIUbNHI91r+/wEP8t/d8D+XJrJMTaMSI2mpq9n8J4X/jBEwRhHtbNmVolfPSUFVBcPTFqxhSse2N51bKmHaX/3fQ9b6kSN2rKWqjiqHJklkcXsatK3yxuroARMzpRm9bT+JyA9fOqWv1HT/0YqjCUjyWNALlBpCAie4DT3Qzvb/4BCA7QCU0hDhjghwiyeiLlEgSTGbUFN8ggKxP+WocUh4EvpJFwiard1LcDIiPaDCEXvUB0WQuq9gBRCKQ3Y7S3RcdD9P/LVdIofvdRZRpqlVDVsg8mUscpCCx9FoT6lCQaTvZvDJr8Kba9tbNSTu3palgxiHFiLCp9HBXbBIIaT0R3ler0POyWVAiGRpT3mwiKWErx6VgXuw5u62kq8HtKw2uu0ODleXeisJ8p5A/wQSzwf8JFvW7gXJQIQfgGmjRPH0egv6raAoL8AKXuRdyvOZYtc+GB2PdQoH6HwhZf0jv3V6juHRNupmHzICS2Fz/Mrp74etw/i2oVS3W/CsH5DtXvOFQpiN67h8zad8ElnECbrs3wx+FsvS/FemBKQ6rTGbpO6nUs6VdGX1fIxdXqtqexCErUgCbDkGri29J/jWgg6Xkyt4nKoH5GLl3bKpSlg/pOrToRj5gaSRPVGOICRGKO5ylkuYfCbkJ1YFI8lWcv983uBCc+QVhDIRRO7YSgT3iC2kBrExDuOS+hNYrHkkaA6SE7bhncanrQhbRAXD79OxGgv56QE2BgIHxtJHpeSYNgrj0BalywsFDrHYnuea05HZpJr7xksaBlUD00CLgx5L8Hkmm2ST+MOkFUNLI2GBgRzgh4IkCzaSR9XL10qdwg0Ek+s+xZdVFn0sCm6cDPyAdvJWJDFl2tquTPtPDie+csMGpxrhxNs7W7XKrPMWoagyyv1SqyZu4e+runNHbklJ7Ulj00Kz6DDW94cs8Q8eFXmtUfqBIz+rV5UAyT4Wg9jJ7W4+vY74kjjYaveRxuGDAXv8efKsslZAWZpIGIzP6oDg8u7krfPRaXfzuI3V90qxS7EkMn3wXZtApd7lxDBNi7uOEO/Y8wkeq4CdcDgRWBj12RBrFn6ETuk69/MeZSiofth1g7dwZqC1l8QhvPEImDi0Mw8PmW2L74bKVU/Fu6Pb4fN+dDrcwSjJxOmbEHqb1Vt92Jop+mvomCzWNeYDklXEmbPVLyfPq1Cr8VP5+j8nMLdOOLlg7avchFDyf1sXgdngPO1pSqEhAXQ/PtBeBfrEODvjif7eyBOkOVNE59NXUVJLkFOo9pVCF69PQ7iLBIVBG/0JOLNcjwYoDeJ85nSXRsgP6PzcX1wke8gSxOqER8xRCmwu2M8/geE2L0JKxhNTkr9C1MHmMEn5Xg84rIOpSGmULHw0STaKOO35eGjZ76CFxoDoP7RY1TVofctA/hLOT5DvQYv+kt8jG5jiC/qBd6PqaXWWIXENS673oRqiVAxXMwu77F/bJEInVBInKlETBQVwaFvYw/BVWXnobMCqwaxfTvr6oOz++KpeNDI8Aw/+BRIK6+9+RpjXOVpaMRabPDyUXPoGDA5UTt66dCJJcEp2GmSUxl4Pc51N1RoR1p7gy+DXWFyhUs+vhm4VtIbgdxu4pEozqmQjKTWCR9r80molqzqKvQ7BD39oeQbVvg03gqxnno3JrgkPZoOt+wSXMwanKTCnHr5pBeMneUx/eYUDwdsuomCC6Sev6Y7oK+EA30151QGrbyJRsyjr0Gv7AOeOzNnlqYTf0cPqZUrHxzEarDE08YiEt3gTXnDyR8VFhtOrc6jy/Ih+gcoAeI2oRL/ZeLukKpNsZznzDBitqi7yOtSWwOhdFvsTY5MtLdXI4H8VfA317DpF4NwxLUUiKT52/t4BfuH30TF1cPnCffn185MVOidPmFsBh8SCBrCGa343rAraF8qlUrjXvumM8izalNQAzCwxoNjF1hNsaOTbwO6i5tCCPjC4lUwUfhsH+A+jfwWa1MTHOzbmC212hwZsLI2+WuIS+aJZTiejYKuxvJ7kK4CjbQky5mcenlWgJAkWUufPyGQzJMI/Y/jYxDl2hQLifdaRcRYALqDgsNmjAyrgzW60pULtHs6ChoSfWaC0ldgY1v76vwxo6v38HIORORmvdfDHxuBbge70gbXWMpmdExmvTjyj1QYzpVPgY+yRsDm2rPzKVqkoiA90g0exUlPSVoxEP36uOlxq+qKP4obAsZm5Tid0rAPwhROnIqvcPDH6OyehO3KfdVBK7/FSLIOQxfv1tGoCHhszXJZMO8T/T6gwxg7FX0erYp9nxwEX81BLh1nYutIMOOAlRqk6q1SZvgRJvL2aZpREvD5SwFJ88nkdHFtywpJ8DCIqTa3DoBcjFUuLZVmX8M0UhsPrK+Ziaq/IoscjG0CLIhGIHmBsGh4ba2qCu4GpjvY4TTZylxA27p0vW0oa/dCCNNHGr2f8jy41urvLh+NDo2Au++akX2HwvgDuiF0TM66nGC4Zrvb12YQ4TWmnTx8fSUQIOjEXHdmdQhu4h4UnHH1JtRFwg4B83aJmzRLsa2knVyC+SAJZB9TJCdr3p8T80ZDaOxAYIjXyLDw1Js+Kjm5X/M4QsnzYwBYTVzF8nNNAu50azP5HyG1b4xa0aW0u4k9t+mXap6K11dyPhRg7hYMjoYGRlAVuUqFzdyeTLpc4f0LRTTufSSyJAlkCEly1FxqlSMZDyyxZc+G/NnaTRRL/Jp/G+AlUoy3MZwAVXbxIlPd0O4YG3ZpF5zHDtTADU9g8uFZRnxvrE7cfIKTUCt20Dr5VrYK5nLBZmMN0qzVuCqs0Gs2H9cDyyiDy3CF2G+MciyOltS8G+oKyQlBmvn7CZDBw0GAxctpsNkngHBwbBxWRaZuAdqJvRrcTDeJX4GPdXOL1/B6JkT4FbW0RPN9EJWrRrPE62dy404X2LMRB84/WkAinfTzPw8ZHUvxsRGkjm+ENcDBt4/SeAuG6YKGgck86c+4LEULkMCWUFbVnE7bHjvIO6asRl22xCYol67ZjlGUw7o28GlXMOXKDeBgSbxjDMp2qNkIvMWF2DU8cRlvsT1NU7SDWTkclkzt6plduS0jfT/sEqh1AdsEXHVF3Et9J/QCqLM1ZFO5EqI1/QqFwm8PExxPkMpJuGvhkhWIs3uRxPxure2VYkfOe0bFLvfZMnJmgT5RSAtx6mt+awicZsMSPiVDGnjboZ/RAQcDju5I0zVF07EpWSkI2jAIDibt0B+egERYMVMRU6ALjfsNIzqBURh9xmxCf4MFBZWXPiv9PAIOAEqyhhSQv+rBTNSmGvBuDUxymIts5SqzuE0KLdj8NPDiDP8AZPfkGrf1f2CsVTWbvItbtfCVi7kVrC92jXyFZoNfebA5ryVnnfg+kB+IeKCcXN/qhS+h8Swf1G9H4c1gpu9k6u8qSgpmi6fb7Nds5SV5CYZToZFSe1XYzqRPaYZ5YIa6GKv4tL7ThLdqCtUybMbQiBxzeO8x2o1GxJhz9BcD6LfFTjdnXQ9m9ngsiWRvtsEXe7rip//W0crfDVQueNU840Fe4wXmYnS6Lc0m0b7yjFIzS6Abq6vhLBA7N20B6k+4QgfOgjuK6kVDTWVIZD7Ic8C/+GjSHEh+SE/XyO4ylCorCL6XL7GcBhlZwz+DIRiEUURp9N9FIZPeUVT2lS3LvNLqlKXbBH3VjxJ84nEzjeSoaMX9U/1olkfXiCbRoPwC4/xongYXBeWZSOuH7z+nt8TOdFxAiv09xjPmO77rGf27LSuDJOD+yfr4Y5p1VunVZq9fYOzsWWBToAl3cuE6/BTVYKgepZPWDVTJ3cZXQtjVkgIjLofhRk7sDq2AzbMCaNJLBRxb8aQZNdfq3fzmx4pTa9yHxL7C4/TFKprUykBiT4+/hFGkrAOnyVpIiNXFzvLE4yvGex0Mt49RLrq5CmkNNr0FTKeQISpkvPdp1kT0sGG45JTpfFWkVgF6HNDETnqOQH6GMi/K/lE4s+AKXqnmW8gx6xEFM/mU2WysH6u7murG/npMLoHkR5FRGzoTIM5tdp0sbFUB+VHKisGI2f0rBDXZ7wZqvl9reU28RD+SihEDrxTw6Ic+CuQcfV5csDTEBH3Yvi0ihbqMa8FYfAzP8FAxuGDG8oMOoa6zCl/EiqqK7RsyaD79zuI01G3By+skmrH+xfom5I/LP+x0jBJUj0TDauN/FQnyJLgG+ZwGzBpVDsSF3vipz2kigXRZBpZLBFwh2J0OJYt+hbzV0xHg/EP4OKnX8GnSydN1yuFZoZW4LqYhgb/+QQ5/oFIOZcOf4O+2o23gFOJlURP7pjv37ohoog2T+X6EREKIXWrvhigDWrJoCv5K8cqGPryYZhDSOe7WibmCVKoNrG5hDJjjN6lrctlFqW3V6i4VnDl3FRyKcymriLFvZwjvtUVARn1eD7NS8MU83jykZGFkETDOyb9Sn7T08Tew+FSB2qTgNn9FE0KOVWaIajBmp1BFaszVkiV6lquC0ic4hJJfj6fyad6SFHsG7XXzom794urGD59AOmrO6gvDpC+kqStcGEsAC65G8zU5Urq87i4e2/pO8ytfz9Vrd360vJgYoj2DVXVs8uHkZ7Lx1a+pM/kssGgS5/CBKqb5x01DFupn2+HQfkMZAZA4NktntMJn5B4+gp6PP0E9i1dRt9LImd9OOVbkbv6E/0m3X8Q+78pW5ggkNqk1cOnar8KCNLFZtWzCFpu+RoZJOV6heRe6NOxCfYvnYhlcfsxf9VunDt+Xl8RE0ZjIiwARb+fwb+/3Y9VH3yJkP37YTmVCEOr1sWiuKDpAY5jpxA+/j7goSfwW45V00RFEkl5EqtL0frxhugwtPA14WRSBvZdLsK/2gdRW4wBqAtE5ThZ2BbAKZZZ90KcM5HnOg4p4NOyhGoCdfYCmjPPlQYp4gIavOVWwrDvqIJpNBvkVyln3VuzSdfypTRlImh0tIJ09wJqepkZe2NsFkZMaksEv4icvXfTR7gZdnKeitx6aZiLVZVcBWWFH6SBSBxS+dlzvPA2lZ3pMcpm2AizKwbB9fM9vyoupXocwtVyS+auhQ1zduLOWQ3JX/w6fcJR9P4gyoh8UGwzuXTmIG5xRV3Uqe6nwfs+WXx343rBGBEytd0keXZTiNLbJBq2QhNLsZtKzqZ+epcmVBqcAh/glUVRrpDo9SvC9zAIyR7XxHK4XW9SvwsIi8wpfqYJTOSDuqJExtMoOFGpmEVU9xs99qsq03hU3qf3PC/3E4QlVI6+iGHJjp7JRY40diSTsW0p+oGeCl1LV+9hQcMmM7R7iGHgy4x8Wgyt72dLTqRpaWx9OrMk8rMnBRtYUoDEkkhVLnrmES3u50InW/NHCotPzmTrzl1hq8+msnNWhxaXk29lL3+0nqHteNb++TUUcpa9veWGagaeF178/4YsSUazIBhhIgafV2THmnNX0Cw0EE/d2RNjBnbGhyt3Y9Y327VjKdA0Gs+Nj8XlmY9j3vYDaLxvC5w79qCIROTg3n1gHzgUR4pUpKRmwt9kRC65MFrVC0KHEH+k5lgQ+90OLPzxICwX07XlS62igzSPpSxK3tOzvfhHQha0ZVCCtgjEh8RsE18snVuA89kW3Eji4sxHhmBU7w4YPvlTXEpK03TDt6cswZrd3fHo2Nvx8BvDwRcA8iX8yekWWC0F8DPKsJB+2KtpfUSQW20REfG0b+NhJeJGFHkMWkQDV7LJxaZWWSXjhRf/JIgqc9tV1a25ikpIwYeIMIiIKDEtB2vPp6FD8xhc+GEWGjQjb8FVUjXaN8HZn45h8qgXsf9goraW6HDiJahkIQ00G5DncGNYyxiEkQX05iffw4szl8NaSCpEO9I9A32hmeP5LnkyHnDyV5lSd/+RF178H4boVl25DE5tFVZ5XsTv/Yl7mYlIVpxJ1TYSnFo+GTI3zBD3QgQZr5rU14iVI5AI1kAcNNvmRK/GETCStbPl/fNw9OApjWA1g46iViiAE30x4f81JnQvvPg/BlFRnVf5Vji+AKuyNKhvoBU04lp39gr8fYxYMYes3QVk+HErmuuhxGXI/1idbjQnx30UccFuUz/HxaNkdGzdSLeUVl64QG6BQB8fKrOQsnIVwAsv/oGQ7Yo126UUku4H7fSyytC2BBKVmYm7JVzJweiubTFwdE9sX0sWdX+fCun42S9dwgPxxY6jOLKJ/M1tG3l22nOqJWIN8fej2wK4VXvdt7EUY9nerq3ATFFFeerFF0fsv1ShDQzC7NkQ+I+Gavcr2xk87b7gP6U2q0+CUnmH/ryNPZqZJTTykXxSnxq0/WxN9fjk586GJ7v84qpc/rJfOsvGggAp1+QQ/LLChCdHbKzWLfDOjn7NzXA3F0Qx57m+CVUsxPwEg3T/QtkdnCf6ZTmFNLvNXbk9JWU6MiMqrIQI8LEJybsTnN4fUP17QHTYCzJziy4SlyM5kEREm1upsvZHczeSjJpu0cfM7Hv66rJjOZHS5ibia1APdocTj3ywVnfkV16Cptt7SITNAjLz0JTEWIZCcpW5M/AnsGR77x+LHObTNqeQIPiKyQu2915bPv79+N5fhvTopTmOF8V3bxgcFnppaXyf5eXTfLJ9QFCIol6at2Xg8PLhs9YPPmQyyudFg7jLITrOzIwbeurnpMZRnuqxYHv3xS6LX+p3+7pHlw9fuLPnLfY8v0sWRbVKRYYim4/F+sGuPuzDvT3mlU/30beNQpYeuv1XWVHPcWeyqqhHFsX3ci/Z0b10DSonviuhGQeZ0ZrD87L7+llDQsMdS3b1urJsZ7feJekW7+j1oC3P7zKls5e/qA426osH4MXfAiIT3VeSss8jmDjgvW0aoE39UOQTd+LEWJ5+OHMMNBnwS04hurZtjBCu1+Xri/p5MgcRblNfI+IOkT30AllLyZVRKnbyjPhqDb7UjSyhvbq1x+rvpuLxgQ1w5MJZclkoV1BHLNrZ6xsmsKGSIvdXJbGpapDvcjNp1Lc/3ZZQWncwvoJF21akygajoIqRFrf40Of7u5aubMgWGHeFREli2dkkn+zu/lt9f2tno6yMUiSxpSgIQ8P8ipoePN/woqcfHpVFw/1uRah3Ns80tny4wLTDiurT3auCINwnSdI4e4H1dXuBOvmDPf1nl6SzBjQ4ITiLOvmaXHeRYaoxMbGukIRNiipvWrq3Rx+eJhEn+dLiLtSzu6h7x6mUH/XvI2RHu1ToNiW8t6VPC55OFFhnShdBjvinydL9DOU1gV8U9SwTlYPw4m8BuWGoPSkoQMWWX9Kx76cDeIHcDne1jMa+9FyN4wWZyrbBcX0widwTnUP98XCfm/DezqNaOOeD3O/H8d2+37XfDiwFJzy+yyI5A81uao63HhiIMf3LtsWl51yCydd4EXWEqgrjDIIw+dnBO0sOUUp678db5mQW+E8vbaRRzGEuVVtJIiuKkREzJ2HUn3wwqyhIWwhudsuMb87yNbq0Xcevrx72jMVe1CE6Kit0XIcTJSLyuQ933dLE5vJLM1sFvkWm9IiJ5Qe6TMwvYFdp5MeF+Lr5xuD3K9f1hQG73yv/vGhbtz5Ou5sfbzbroyNDphbkKTFuhxL+7IA92cVJuCg94u0NA9J8zM7V4HvgEhPdavfefC3y2hcH7vlvuey+eD++F19NzM8oeRtM4FtWcl7on/AxvPjbQrS7Q866iAFFhvhg7vtxaHnnTKza8St61A9BR/ID5jlcpQcqaSjmaoM60URrkEkKZZobonFYAFLzCrHh6FltB0Xx9hSg0EYkkY7HHxyI88tf04hvZ2o2NlzIL9Yb03A1zXgOdYRb4ftL1HuWbv1X6XkkLw07MiPfYSm3+r9suZ6iSkbiDr4Bfs6hmdaQ6AXrhhfvk7MUxwuaXB0aaHnJaBJOlSM+DRP6Hkl/ZVCCYLdnbC4fnlkQ+LIksn0vDkh4mPJo8MGWntfcfOtmvoLR6NLyt2W654b5WzY9O2BHduV0kcGW1xwuQ+hXWzv4cd2Nnxslsop6KhdNuVbgVGVNmmCidn7X/9oiYi/+GsjZMP9x5sop94PdfOV2I/shcVM8xjy7CPf8uy++nzUeQc2ikEAiZTBxQv41+XES/EDECH50YbAfXKT7cTNLRIAvks5fhppToO+q58SXW6gdbf/ZW4/j0SFd8UeBDb+n8fHG0CosBg6XBfm2FEvr+u5TqCO6NLvYb2di211hvtaMRTt6ZbgFaaMomOZM7LslqSSN6nKVjkSB7L6QDIHP9Ni1953vbnxYign5goLftgw+kOe3rR8ZoxSNAyqS1ByF1v+U5LF4Z9+Oqqp0IeJV4GJHKVPOtTXDx/ytg/yMoj1GNYCfjUmSglrkksFP/n6YP4uQ7FxOWBTfcwpjYpYo8p1LQg+nm/UMM9u0s0mcBhXMpu731EbVZN7ltknIyjfxXRZbBAlWKMK9i3b0CRVUJtBzSK5w+U6aZj4vHLznW62dDOf4AqXF23ufZWXHPPCDKbJEp2+P54du9rp+/gYQY/smFqbkXT5OHnZ0a9pMPxOGnOg/fLcTnR6bjwgyvvRtWl/z7/GvqO3tdCoI4unICup0uzUR1NcgItdi08VNnshKY45E2K8WPKUR3z4ivN/JsR9M5la+PzDcjzPHy7A4Urc/3Dfp+g6bKYe+LZMSosPzI0l0fJXc+X+YVOejkmK9uDihTLeqDL576aN9HVpPuvfEcsHgj/9sajc/ltgoaYdEF0wz2wo0gbjdjtJNrIwJvWhKmUWRU0nXPAaz8d2SOD8mjOUjXnUJ8me7u3XyNTrOqapYelgvX+yg3wnPkeg4lYhwpkuRBgUFFLxy721Hv+YWSzO9X+CUPRJFRprLJMsqfIMCNCsYU1UrsbebqVKPUamPKW51fGZhYOtAX8dp3g6epsCunSFJ06N6hDH1ML+odYfo+tUUkem1gP5NoBkSZPFKnIqUm3u1a43PyQCjHTPXrgmO7TuBTk8vxNGlE3ETiaR/XM2HkQjSRUTHz/fkB/hwEZTLQlzIs/HtSWqxh53EzslTx+GBvjchPiULFrsTIWZDsUuQ0T2QknMMObbCOhsEXvyiT3BopE/Tp7tv5srou8UXvj78r0/TctSZ723otuyl4QdS+WmKrJw3hCkMRj9ffmjqaUd66mCXud7Wxdu7ryYKyqTKaVuWBKc7Xw4IubHknRf671xCf/iFxdt6HXZCaloS5zIWjVe184DU7YVOg49bNVtVyP6bf2vaekjHi6cdijnIR3bCJYkdXumbkFW5HdztsWSbArfR0MFTO40GNpCfGZVuVLVV/ppxhYwppFOWnlO5cFfPx/IK/T/9/lDXuH/feviMWXbxbUgFLw7cex+8+NtC8xEVFBjjz2T9jK6t6umnYnMnO98HSJbOY5sPYeIXm9GWxE25mOD4ENAUkHJaiH5bvJmXdLw2vTti3rj++I2sppz4gojz8VfdNJCCzP7gHsTEtAQYFfM+1BGtGkqNgkwFv76/uWeFgWtKinnKQMKWj2Ruwp+rTPfalk1B89VNGv3bNlFyJwoG43b+6zGKqOuAgmrYSJTay1O5xMFvYqqgbc5dcbhdfRIHe1mzcL+qGm5WZbG90cg6GI0qMtPNmptBlrVTk2leU6o92CnT5rfZANXjUXmBQfIUG5Ss2L4JbtIBtVOOheKJogRGSBu5Vyg5L0Dba2aQ3YpXB/z7Q+OAA8JbHf714uqCUR0fCohq1Q5pZ07qhhROMW0a4f2l6/EwEVTfxvWx/kxqzb+Kwd8hLvrZc/qG6ZPkeojwNZWusrHTuGhTL4i4aDYu5hzPF04k1fk8jsx9O34P7taD79GMi13RrnWJMzo78soG5iCCL9QPeqJ68eMatM2uZLKRODlYi8p+F8NkMPRnKtJo/PpLTD8ujgniJKrzuAXxPbapiu/oVwdvsy7Z1j2atMmVChSDySBpnOxsdqPp9cx5eGHMwW/L123rqXarD59vThbJUzAJdofKzxB2MJ/q2jLyhj/uPXIpJu/zvbcet7t9Bk3om5DORdPPD972IYm2DUMcZs1lEtp1iIGRCkhRFY6YUJ2qj0AOCj/JoR3Yw/TDHEIW7uh5M+mtAlP1Da002UiqYr744oAdf8r36sVfA+2j9KWZ9Xx64g5fYxKeHzKQjCeW4kWaTNcJC4rw1qo9NDr13RJqTefhkCU0kiyk3ds2wrE8q+Y7LEnNCZSvlmlMtJ2ZfxQ51qStf2ZFBn/XBPlGl+gICQ4Nc5DT+jIZJmwup9iPbLPDJoxN0IhJUAVuN8oqrgM32jr8zfbSlSN8sCsuZToNUyfpehpnfG7Q9ismWR1oFjHQINoLF8X3zlYlKVmSxYvEyt5xOfUzTEKNeaNdgnFD5bqlnClc6Gsuwgfx/cNUtyGFH6xrFI3VHo7RpfmFfMbcfS7nBDZzKSyN2lL4wa5eak6e/LQ1H2MeHxqvieo5jZJ5dzroy1U4WS0yJusq6Zd2typp1ldqB82i4DbiQ1BFrvsd4BdTpAOi4LwXXvwtULpMyaKKX13Iiccj/ckQExrEPet6BJdrIkOw5fh54l5uNAwJICKqgQDJ8DK0YzPt9kJWvraErQQKEa6/wQ8+RAUn0reBWbEQfxJPD074fWK//cEyM0wUBWmNorin58iS/8uD9pf+RgGJlQ+S/tWd32fL4inByRo+0ulohR3YLwzeM9flVhoKTt/S3d7P9N0XXy8rSlYE4QVRVT8h2/7wF/rtuf+Ffrtfs5w6PounYUa/nhP67BlRuV6Pjby032B0RQiS4niB6igYWcOI7LDLNTQFz/Q7sHvWyO3+kiQ/RvPfUiYaJ8kFiu/E4XtXlaSZ1T7Rxetvtlo/Kv/u2BsSC2FgjRRm4lZd0mH9PpMdYgOa3trR1VZxi234RfetFFFaDi/+fvhoZ+9kvmv9lkk/MNxEBrbR05l2PsaQ17Rd8SeTM1gKTc/H86zs0tU8hp7Ps+8SjrE8eofvpV+573eG6LHsvXX7td3vq85cZtuSMrRrO12r6flCAWMFtiQ2Z0Or8/DCi384KizUPZd5bpnF+SPee3C0ftRcyVpPvprF7iLp0gY/soJWJzPyowZBls5GpONpJ7mXW8umnVNKlsKmpI2dydyMooK0j+CFF/9wVCDAoR0tH6/9ZRF6tDegw4B+wOVMXRfUVHp+/L9as1lNO4tU1PYFlld2+DuF5B9sGcot/27sPr0i2yg3XAIvvPiHowIBDmhbkH0i5fgcmzsBHz9+P1Dk0Fy5GrS9f7X4JU3+GydqRULlx10IgowbQmX8cnEZeTh+eaNOP8bihRf/z1DliOsF92TM+Orgc65ubYAxj44nO/ulst8LrEsBRLS5Dge6xkQScRZhyx8f5hcOtni5nxdewPOvzuBqbtrYXy6/jxWv9IPUsLEuitaBCLkKWECiZ5R/EGLIA7bl+HQ47DkjS5ZLeeHFPx0eCXDGiOx1G0+9t8aNI/hp6RxySajaompZEmudMRdBtc29JHr2jgnEhcz12Jmx4ps37kzfDS+88EJD9RSVl3Lv/B2PpHRtDvxn8Tziglm4mm/FtX8kT7fbcG+2UxExrHkErEoSlu9/MnH+4FTvTmwvvCiHagkwdiycuad/7/rhnh7Wh/uG4cuVnxNhSciwOWFXqlpD9Z0S9H+OBdmF+uL/Me1i4HSex9Jd91wNsgf2hhdeeFEBNcqU8ycg3ZJ/ocsn+7unP9ivAENuaQV/GBHlY9LWdPJlZy5F0SyjXKnLJA4pN41GmwZNwffC5OX+hA/3DrwQZUm9+ZX7zmTBCy+8uH68va1h9Oz1bXcduPgxY0z/jYd8K2Phw2ezH3YnsgJ6TsxTWGYuYxYn09IknF7IZqxvtXn+1sja/S6dF154UTOmrAh99KOEwRl7zn7AcoqSWJ7VxpwujR5ZocPOnMoVdjpzOftoV/8rU9dG3Q8vvPCiRlz3frH5Wzv4FRXljvD3jRrQon6r9kBQOD+K0ObKu5pvO3M8pyBjR5jJP+75oee8Rx544cU18D+qbo3I0X6DowAAAABJRU5ErkJggg==\n" +} diff --git a/src/sg/gov/moh/vaccination-healthcert/1.0/interim-vaccination-healthcert-wrapped.json b/src/sg/gov/moh/vaccination-healthcert/1.0/interim-vaccination-healthcert-wrapped.json new file mode 100644 index 0000000..9fa426e --- /dev/null +++ b/src/sg/gov/moh/vaccination-healthcert/1.0/interim-vaccination-healthcert-wrapped.json @@ -0,0 +1,205 @@ +{ + "version": "https://schema.openattestation.com/2.0/schema.json", + "data": { + "id": "f1a6d266-eddc-4f5f-8ca4-4e8e2be398dc:string:e63b2496-2531-47ff-9155-c41e291af36a", + "name": "c1d1e9ed-b71d-4a2f-b851-70a313cb8682:string:VaccinationHealthCert", + "validFrom": "395dd73c-8d42-4200-bd69-540f346981f1:string:2021-05-21T03:38:30.464Z", + "fhirVersion": "db76aa9c-58fb-4518-8885-2fae874b85ae:string:4.0.1", + "fhirBundle": { + "resourceType": "0e80f2e9-9b71-4d70-a82a-6e4328f43a28:string:Bundle", + "type": "2df1860b-3d7c-44c7-a676-44bd815347df:string:collection", + "entry": [ + { + "fullUrl": "1b1cadb5-cc8b-4002-b4f0-b8723e7a7b94:string:urn:uuid:1420b5d3-86df-4157-b952-e75bf79ca2aa", + "resourceType": "3bdc48dd-4516-4120-a912-0ea25c04ad1b:string:Patient", + "extension": [ + { + "url": "4d4f38fb-9066-4a58-b401-a5697323e587:string:http://hl7.org/fhir/StructureDefinition/patient-nationality", + "code": { + "text": "8be58cc3-5c7f-4b80-9e2a-00aa02112f18:string:SG" + } + } + ], + "identifier": [ + { + "type": { + "text": "bd2916fd-903c-440a-8e13-daf94d5858fd:string:NRIC" + }, + "value": "2b070607-5b37-4447-b7fb-43466b78d06f:string:S9098989Z" + }, + { + "type": "ce8ae371-5fd9-4f24-b775-1290a0909380:string:PPN", + "value": "a3c07bd0-68ea-483f-9e37-33c2e380132c:string:E7831177G" + } + ], + "name": [ + { + "text": "a60737fc-d665-4dc0-b510-a4150a09d5df:string:Tan Chen Chen" + } + ], + "gender": "fdbfb244-4ae6-42bd-bc81-77846dc0e538:string:female", + "birthDate": "cc6d0e89-ad0a-4ee6-a43d-d7392f307190:string:1990-01-15" + }, + { + "fullUrl": "136e08c2-b5be-403c-9317-c82900023d61:string:urn:uuid:5e2b4ebe-6d51-4b70-a36d-da70e1763a7c", + "resourceType": "774eb666-6e04-49e6-82b5-5f040ec62f59:string:Location", + "id": "542beaa9-9c99-4293-b247-42ec1e24eb51:string:HCI000", + "name": "2e5e2942-5dea-489d-a57e-64e6bd891aed:string:Vaccination site approved by Ministry of Health (MOH), Singapore [HCI000]", + "address": { + "country": "3849283f-a4ac-4bd5-b413-e572a5ae0e50:string:SG" + } + }, + { + "fullUrl": "ae9a0d85-2e82-41ba-b365-f4b9ff6ac980:string:urn:uuid:5eca16e7-8870-4e8f-ac3b-45baeed5f203", + "resourceType": "d9330c97-f679-4ce2-a05a-5ee7dd9d620d:string:Location", + "id": "4cd3e698-b90f-42b6-b1f7-0bb70ace32dd:string:HCI000", + "name": "4664df7b-84c5-4f4a-a439-7a37ae6ea65d:string:Vaccination site approved by Ministry of Health (MOH), Singapore [HCI000]", + "address": { + "country": "ce98b74e-56ee-43c4-9c5b-e1b930efcbb3:string:SG" + } + }, + { + "fullUrl": "1b323f0c-3f2e-402f-a2c1-8383f62759b2:string:urn:uuid:f50fe4d9-03a0-47f5-a7de-3352986f8287", + "resourceType": "ff3c7051-d689-4001-ac80-f996225d6e59:string:Immunization", + "vaccineCode": { + "coding": [ + { + "system": "250b9874-3f00-40ac-9bd7-95a071b821c5:string:http://standards.ihis.com.sg", + "code": "6b6982ea-6b88-4c46-b718-986b6c47af8e:string:1234567890123456", + "display": "2b343af3-2132-4a3f-9ed0-404da45773d6:string:PFIZER-BIONTECH COVID-19 Vaccine [Tozinameran] Injection" + } + ] + }, + "lotNumber": "19e01f9b-0a24-4cfe-bc9f-50e712f664f0:string:Lot12345", + "occurrenceDateTime": "cfcea89d-42aa-4c57-8cef-afd767c44858:string:2021-02-14", + "patient": { + "reference": "2fade73a-b996-4a36-91f8-ce4a3f7a923c:string:urn:uuid:1420b5d3-86df-4157-b952-e75bf79ca2aa" + }, + "location": { + "reference": "dd8f72ca-41ea-4307-a890-4233a9471069:string:urn:uuid:5e2b4ebe-6d51-4b70-a36d-da70e1763a7c" + }, + "performer": [ + { + "actor": { + "display": "2ecaef09-0994-4a90-889f-422eecad8009:string:Designated vaccinator by MOH-approved vaccination site" + } + } + ] + }, + { + "fullUrl": "8f347dc5-e0fd-40d1-8e01-09b1ca6e7840:string:urn:uuid:37b8153e-8373-4fbd-9e4c-85a7c06eb144", + "resourceType": "9e038b52-6cfc-4dd2-bb16-24c07b0c4675:string:Immunization", + "vaccineCode": { + "coding": [ + { + "system": "2fbc25a4-323e-4818-99e2-b56706ebfe4c:string:http://standards.ihis.com.sg", + "code": "3f1d96f6-5a03-4ea8-a12a-37fdaae09308:string:1234567890123456", + "display": "143399cf-9839-4d8c-954b-6ee01432ee8b:string:PFIZER-BIONTECH COVID-19 Vaccine [Tozinameran] Injection" + } + ] + }, + "lotNumber": "fe5ef38e-2885-4070-addc-d538b99b2adb:string:Lot97531", + "occurrenceDateTime": "2d9be4ed-d6db-4cc1-b817-136b30a9ea29:string:2021-03-03", + "patient": { + "reference": "19fac652-5aee-4e39-9184-d578f540ec5e:string:urn:uuid:1420b5d3-86df-4157-b952-e75bf79ca2aa" + }, + "location": { + "reference": "d39dca68-2d6f-45bb-8001-018f8d1d598a:string:urn:uuid:5eca16e7-8870-4e8f-ac3b-45baeed5f203" + }, + "performer": [ + { + "actor": { + "display": "833f1cda-eb4c-466e-818b-a56638e3230b:string:Designated vaccinator by MOH-approved vaccination site" + } + } + ] + }, + { + "fullUrl": "b1c9de6d-7a14-4721-82af-a178a3bc4dd6:string:urn:uuid:4809a8cc-5b10-4f14-a0c6-66e5c722d06c", + "resourceType": "de776268-764f-4cf9-98fb-251c9b8803e3:string:ImmunizationRecommendation", + "recommendation": [ + { + "targetDisease": { + "coding": [ + { + "system": "cbfc0a2d-e30f-4276-abde-091478cd067e:string:http://snomed.info/sct", + "code": "2e06253f-f9ab-47c0-96e3-8bf4178d9703:string:840539006", + "display": "9ca63091-6868-468f-b25b-d2cde3ac99e3:string:COVID-19" + } + ] + }, + "forecastStatus": { + "coding": [ + { + "system": "e4b6f71d-99a2-46b2-af89-6ca26d311b6f:string:http://snomed.info/sct", + "code": "249e8a5e-3d35-4f82-9431-f4a9556fe82f:string:complete", + "display": "34277d70-ef98-4230-8ce2-4be5014e92a8:string:Complete" + } + ] + }, + "dateCriterion": [ + { + "code": { + "coding": [ + { + "system": "3aecb46d-1c93-489e-8adf-c88c633e95d7:string:", + "code": "ae7dab65-7fd9-4ead-88ec-542f9df30967:string:effective", + "display": "7b45bc83-e29e-4f90-8ace-55745c3c4356:string:Effective" + } + ] + }, + "value": "af5a129d-a1ca-4881-aeb5-ed2d891ad11a:string:2021-03-17" + } + ] + } + ], + "patient": { + "reference": "db3e2670-a15e-45c4-83da-2acb76cb23f1:string:urn:uuid:1420b5d3-86df-4157-b952-e75bf79ca2aa" + } + } + ] + }, + "issuers": [ + { + "name": "945d1b60-cb41-4633-be6d-98f434ecfd7a:string:MINISTRY OF HEALTH (SINGAPORE)", + "id": "644d2be0-b05c-411b-9c24-e3f649d33ceb:string:did:ethr:0x82948a537e886Ea8ffF5Bd29DDA95224bF74c35F", + "revocation": { + "type": "20045e0e-2470-4d01-8477-47f4749570cd:string:REVOCATION_STORE", + "location": "0cabcfd4-2188-4c2e-a79f-b639a701b152:string:0x7384702915962d70Ef202Ffb38152c4c89cD98dA" + }, + "identityProof": { + "type": "bfef03a0-2788-4917-994f-f56020e8ec81:string:DNS-DID", + "location": "4f30b3d3-64a2-4539-9d0a-12fcd99eb229:string:moh.gov.sg", + "key": "3294030d-9c17-4852-b722-d2f7983a4def:string:did:ethr:0x82948a537e886Ea8ffF5Bd29DDA95224bF74c35F#controller" + } + } + ], + "$template": { + "name": "416deed6-5bbe-48f9-ab76-68009af8a3ed:string:VACCINATION_CERT", + "type": "f965881b-2cc7-4d03-8301-e908ec59806b:string:EMBEDDED_RENDERER", + "url": "dc92c94e-6ca4-420d-9c9c-d32a7d61ff3f:string:https://healthcert.renderer.moh.gov.sg/" + }, + "notarisationMetadata": { + "reference": "6389dfdb-4c9c-4553-958e-1c60fefa36f1:string:e63b2496-2531-47ff-9155-c41e291af36a", + "notarisedOn": "23f44bb8-b9d6-42ea-a900-0a5738effc51:string:2021-05-21T03:38:30.464Z", + "passportNumber": "8df708fa-21da-4829-b2b6-4e063b83969c:string:E7831177G", + "url": "fc50b6c4-949d-4d4c-9880-ae9bb2f7917c:string:https://www.verify.gov.sg/verify?q=%7B%22type%22%3A%22DOCUMENT%22%2C%22payload%22%3A%7B%22uri%22%3A%22https%3A%2F%2Fapi-vaccine.storage.staging.notarise.io%2Fdocument%2Fc8b5a22a-05c6-41d9-b5bd-e25b3c8ccdf5%22%2C%22key%22%3A%22a33d9f04a5f30ff970693c4b2d8fd8e205a80df7ad224905ec023110418616c9%22%2C%22permittedActions%22%3A%5B%22VIEW%22%2C%22STORE%22%5D%7D%7D" + }, + "logo": "e0ce1020-f275-402c-89b0-368477e93a8e:string:data:image/png;base64,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\n" + }, + "signature": { + "type": "SHA3MerkleProof", + "targetHash": "5e5407eecc501b23f426ea8790fe00973b136513ea38827084061aebd312ee7a", + "proof": [], + "merkleRoot": "5e5407eecc501b23f426ea8790fe00973b136513ea38827084061aebd312ee7a" + }, + "proof": [ + { + "type": "OpenAttestationSignature2018", + "created": "2021-05-21T03:38:30.597Z", + "proofPurpose": "assertionMethod", + "verificationMethod": "did:ethr:0x82948a537e886Ea8ffF5Bd29DDA95224bF74c35F#controller", + "signature": "0x4a35f51aecb7f76714ccec083190aa91cca0db8142b8488b8cb47b397753f8996a162421ef632515aee9ea10caf4666023d82f00b9df5d1b51efa04427529aaf1c" + } + ] +} diff --git a/src/sg/gov/moh/vaccination-healthcert/1.0/schema.json b/src/sg/gov/moh/vaccination-healthcert/1.0/schema.json new file mode 100644 index 0000000..822f6c6 --- /dev/null +++ b/src/sg/gov/moh/vaccination-healthcert/1.0/schema.json @@ -0,0 +1,699 @@ +{ + "$id": "https://schemata.openattestation.com/sg/gov/moh/pdt-healthcert/1.0/interim-healthcert-schema.json", + "$schema": "http://json-schema.org/draft-07/schema", + "type": "object", + "title": "Interim Vaccination HealthCert Schema", + "required": ["id", "name", "validFrom", "fhirVersion", "fhirBundle", "issuers", "$template", "notarisationMetadata"], + "properties": { + "id": { + "type": "string", + "description": "Internal reference, usually serial number, of this document", + "examples": ["TEST001"] + }, + "name": { + "type": "string", + "examples": ["HealthCert"] + }, + "validFrom": { + "type": "string", + "format": "date-time", + "description": "Date and time from which the document is considered valid", + "examples": ["2021-04-27T03:18:51.972Z"] + }, + "fhirVersion": { + "type": "string", + "examples": ["4.0.1"] + }, + "fhirBundle": { + "type": "object", + "properties": { + "resourceType": { "const": "Bundle" }, + "type": { "type": "string", "default": "collection" }, + "entry": { + "type": "array", + "items": { + "anyOf": [ + { "$ref": "#/definitions/Patient" }, + { "$ref": "#/definitions/Specimen" }, + { "$ref": "#/definitions/Observation" }, + { "$ref": "#/definitions/Organization" }, + { "$ref": "#/definitions/Immunization" }, + { "$ref": "#/definitions/ImmunizationRecommendation" }, + { "$ref": "#/definitions/Location" } + ] + } + } + }, + "required": ["resourceType", "type", "entry"], + "additionalProperties": false + }, + "issuers": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": ["id", "name", "identityProof"], + "properties": { + "id": { + "type": "string" + }, + "revocation": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["NONE", "REVOCATION_STORE"] + }, + "location": { + "type": "string", + "description": "Smart contract address or url of certificate revocation list for Revocation Store type revocation" + } + }, + "additionalProperties": false + }, + "name": { + "type": "string", + "examples": ["SAMPLE CLINIC"] + }, + "identityProof": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["DNS-DID"] + }, + "key": { + "type": "string", + "description": "Public key associated" + }, + "location": { + "type": "string", + "description": "Url of the website referencing to document store" + } + }, + "required": ["type", "key", "location"], + "additionalProperties": false, + "examples": [ + { + "type": "DNS-DID", + "location": "donotverify.testing.verify.gov.sg", + "key": "did:ethr:0xE39479928Cc4EfFE50774488780B9f616bd4B830#controller" + } + ] + } + } + } + }, + "logo": { + "type": "string", + "description": "base64 encoded image" + }, + "$template": { + "type": "object", + "required": ["name", "type"], + "properties": { + "name": { + "type": "string", + "description": "Template name to be use by template renderer to determine the template to use", + "examples": ["HEALTHCERT"] + }, + "type": { + "type": "string", + "description": "Type of renderer template", + "enum": ["EMBEDDED_RENDERER"] + }, + "url": { + "type": "string", + "description": "URL of a decentralised renderer to render this document", + "examples": ["renderer.example.com"] + } + } + }, + "notarisationMetadata": { + "type": "object", + "properties": { + "reference": { "type": "string", "examples": ["e45acb48-a782-4883-b4df-c06129f6e783"] }, + "notarisedOn": { "type": "string", "format": "date-time", "examples": ["2021-05-17T09:01:49.836Z"] }, + "passportNumber": { "type": "string", "examples": ["E7831177G"] }, + "url": { + "type": "string", + "examples": [ + "https://www.verify.gov.sg/verify?q=%7B%22type%22%3A%22DOCUMENT%22%2C%22payload%22%3A%7B%22uri%22%3A%22https%3A%2F%2Fapi.storage.staging.notarise.io%2Fdocument%2F2d0cff63-8b0f-4056-a4b4-ef585057bd00%22%2C%22key%22%3A%2221a6e3251229bf7d0c4e4b8cc603d2026c310c24fbcd9e94cb24f87e25897c06%22%2C%22permittedActions%22%3A%5B%22VIEW%22%2C%22STORE%22%5D%7D%7D" + ] + } + }, + "additionalProperties": false + }, + "attachments": { + "type": "array", + "items": { + "properties": { + "filename": { "type": "string", "examples": ["healthcert.txt"] }, + "type": { "type": "string", "enum": ["text/open-attestation"] }, + "data": { "type": "string" } + }, + "additionalProperties": false + } + } + }, + "definitions": { + "Patient": { + "description": "Demographics and other administrative information about an individual or animal receiving care or other health-related services.", + "properties": { + "fullUrl": { "type": "string", "examples": ["urn:uuid:9d209e2f-03c5-4425-8ca8-5b3df935b2a5"] }, + "resourceType": { + "description": "This is a Patient resource", + "enum": ["Patient"], + "type": "string" + }, + "extension": { + "description": "May be used to represent additional information that is not part of the basic definition of the resource. To make the use of extensions safe and manageable, there is a strict set of governance applied to the definition and use of extensions. Though any implementer can define an extension, there is a set of requirements that SHALL be met as part of the definition of the extension.", + "items": { + "properties": { + "url": { "type": "string", "enum": ["http://hl7.org/fhir/StructureDefinition/patient-nationality"] }, + "code": { + "type": "object", + "properties": { + "text": { "type": "string", "examples": ["SG"] } + }, + "required": ["text"], + "additionalProperties": false + } + }, + "required": ["url", "code"], + "additionalProperties": false + }, + "type": "array", + "minItems": 1 + }, + "identifier": { + "description": "An identifier for this patient.", + "items": { + "properties": { + "type": { + "anyOf": [ + { "type": "string" }, + { + "properties": { "text": { "type": "string", "examples": ["NRIC"] } }, + "required": ["text"], + "additionalProperties": false + } + ] + }, + "value": { + "type": "string", + "examples": ["S9098989Z", "E7831177G"] + } + }, + "required": ["type", "value"], + "additionalProperties": false + }, + "type": "array", + "minItems": 1 + }, + "name": { + "description": "A name associated with the individual.", + "items": { + "properties": { "text": { "type": "string", "examples": ["Tan Chen Chen"] } }, + "required": ["text"], + "additionalProperties": false + }, + "type": "array", + "minItems": 1 + }, + "gender": { + "description": "Administrative Gender - the gender that the patient is considered to have for administration and record keeping purposes.", + "enum": ["male", "female", "other", "unknown"] + }, + "birthDate": { + "description": "The date of birth for the individual.", + "format": "date", + "type": "string", + "examples": ["1990-01-15"] + } + }, + "additionalProperties": false, + "required": ["resourceType", "extension", "identifier", "name", "birthDate"] + }, + "Specimen": { + "description": "A sample to be used for analysis.", + "properties": { + "resourceType": { + "description": "This is a Specimen resource", + "enum": ["Specimen"] + }, + "type": { + "description": "The kind of material that forms the specimen.", + "properties": { + "coding": { + "items": { + "properties": { + "system": { + "type": "string", + "examples": ["http://snomed.info/sct"] + }, + "code": { + "type": "string", + "examples": ["258500001"] + }, + "display": { + "type": "string", + "examples": ["Nasopharyngeal swab"] + } + }, + "required": ["system", "code", "display"], + "additionalProperties": false + }, + "type": "array", + "minItems": 1 + } + }, + "type": "object", + "required": ["coding"], + "additionalProperties": false + }, + "collection": { + "description": "Details concerning the specimen collection.", + "properties": { + "collectedDateTime": { "type": "string", "format": "date-time", "examples": ["2020-09-27T06:15:00Z"] } + }, + "required": ["collectedDateTime"], + "additionalProperties": false + } + }, + "required": ["resourceType", "type", "collection"], + "additionalProperties": false + }, + "Observation": { + "description": "Measurements and simple assertions made about a patient, device or other subject.", + "properties": { + "resourceType": { + "description": "This is a Observation resource", + "enum": ["Observation"] + }, + "identifier": { + "description": "A unique identifier assigned to this observation.", + "items": { + "properties": { + "value": { "type": "string", "examples": ["123456789"] }, + "type": { "type": "string", "examples": ["ACSN"] } + }, + "required": ["value", "type"], + "additionalProperties": false + }, + "type": "array", + "minItems": 1 + }, + "code": { + "description": "Describes what was observed. Sometimes this is called the observation \"name\".", + "properties": { + "coding": { + "items": { + "properties": { + "system": { "type": "string", "examples": ["http://loinc.org"] }, + "code": { "type": "string", "examples": ["94531-1"] }, + "display": { + "type": "string", + "examples": ["Reverse transcription polymerase chain reaction (rRT-PCR) test"] + } + }, + "required": ["system", "code", "display"], + "additionalProperties": false + }, + "type": "array", + "minItems": 1 + } + }, + "additionalProperties": false + }, + "valueCodeableConcept": { + "description": "The information determined as a result of making the observation, if the information has a simple value.", + "properties": { + "coding": { + "items": { + "properties": { + "system": { "type": "string", "examples": ["http://snomed.info/sct"] }, + "code": { "type": "string", "examples": ["260385009"] }, + "display": { + "type": "string", + "examples": ["Negative"] + } + }, + "required": ["system", "code", "display"], + "additionalProperties": false + }, + "type": "array", + "minItems": 1 + } + }, + "additionalProperties": false + }, + "effectiveDateTime": { + "description": "The time or time-period the observed value is asserted as being true. For biological subjects - e.g. human patients - this is usually called the \"physiologically relevant time\". This is usually either the time of the procedure or of specimen collection, but very often the source of the date/time is not known, only the date/time itself.", + "format": "date-time", + "examples": ["2020-09-28T06:15:00Z"], + "type": "string" + }, + "status": { + "description": "The status of the result value.", + "enum": [ + "registered", + "preliminary", + "final", + "amended", + "corrected", + "cancelled", + "entered-in-error", + "unknown" + ] + }, + "performer": { + "description": "Who was responsible for asserting the observed value as \"true\".", + "properties": { + "name": { + "items": { + "properties": { + "text": { "type": "string", "examples": ["Dr Michael Lim"] } + }, + "required": ["text"], + "additionalProperties": false + }, + "type": "array", + "minItems": 1 + } + }, + "additionalProperties": false + }, + "qualification": { + "items": { + "properties": { + "identifier": { "type": "string", "examples": ["MCR 123214"] }, + "issuer": { "type": "string", "examples": ["MOH"] } + }, + "required": ["identifier", "issuer"], + "additionalProperties": false + }, + "type": "array", + "minItems": 1 + } + }, + "additionalProperties": false, + "required": [ + "resourceType", + "identifier", + "code", + "valueCodeableConcept", + "effectiveDateTime", + "status", + "performer", + "qualification" + ] + }, + "Organization": { + "description": "A formally or informally recognized grouping of people or organizations formed for the purpose of achieving some form of collective action. Includes companies, institutions, corporations, departments, community groups, healthcare practice groups, payer/insurer, etc.", + "properties": { + "resourceType": { + "description": "This is a Organization resource", + "enum": ["Organization"] + }, + "name": { + "description": "A name associated with the organization.", + "type": "string", + "examples": ["MacRitchie Medical Clinic"] + }, + "type": { + "description": "The kind(s) of organization that this is.", + "type": "string", + "examples": ["Licensed Healthcare Provider"] + }, + "contact": { + "description": "Contact for the organization for a certain purpose.", + "properties": { + "telecom": { + "items": { + "properties": { + "system": { "type": "string", "examples": ["phone"] }, + "value": { "type": "string", "examples": ["+6563113111"] } + }, + "required": ["system", "value"], + "additionalProperties": false + }, + "type": "array" + }, + "address": { + "properties": { + "type": { + "type": "string", + "examples": ["physical"] + }, + "use": { "type": "string", "examples": ["work"] }, + "text": { "type": "string", "examples": ["MacRitchie Hospital Thomson Road Singapore 123000"] } + }, + "required": ["type", "use", "text"], + "additionalProperties": false + } + }, + "required": ["telecom", "address"], + "additionalProperties": false + }, + "endpoint": { + "description": "Technical endpoints providing access to services operated for the organization.", + "properties": { + "address": { "type": "string", "examples": ["https://www.macritchieclinic.com.sg"] } + }, + "required": ["address"], + "additionalProperties": false + } + }, + "additionalProperties": false, + "required": ["resourceType", "name", "type", "contact"] + }, + "Immunization": { + "description": "Describes the event of a patient being administered a vaccine or a record of an immunization as reported by a patient, a clinician or another party.", + "properties": { + "fullUrl": { "type": "string", "examples": ["urn:uuid:9d209e2f-03c5-4425-8ca8-5b3df935b2a5"] }, + "resourceType": { + "description": "This is a Immunization resource", + "enum": ["Immunization"] + }, + "vaccineCode": { + "description": "Vaccine that was administered or was to be administered.", + "type": "object", + "properties": { + "coding": { + "items": { + "properties": { + "system": { "type": "string", "examples": ["http://standards.ihis.com.sg"] }, + "code": { "type": "string", "examples": ["1234567890123456"] }, + "display": { + "type": "string", + "examples": ["PFIZER-BIONTECH COVID-19 Vaccine [Tozinameran] Injection"] + } + }, + "additionalProperties": false, + "required": ["system", "code", "display"] + }, + "type": "array", + "minItems": 1 + } + }, + "required": ["coding"], + "additionalProperties": false + }, + "occurrenceDateTime": { + "description": "Date vaccine administered or was to be administered.", + "type": "string", + "format": "date", + "examples": ["2021-02-14"] + }, + "lotNumber": { + "description": "Lot number of the vaccine product.", + "type": "string", + "examples": ["Lot12345"] + }, + "patient": { + "type": "object", + "properties": { + "reference": { "type": "string", "examples": ["urn:uuid:9d209e2f-03c5-4425-8ca8-5b3df935b2a5"] } + }, + "required": ["reference"], + "additionalProperties": false + }, + "location": { + "type": "object", + "properties": { + "reference": { "type": "string", "examples": ["urn:uuid:c7edd918-72b9-4d26-9c77-4424c83929ad"] } + }, + "required": ["reference"], + "additionalProperties": false + }, + "performer": { + "type": "array", + "items": { + "properties": { + "actor": { + "type": "object", + "properties": { + "display": { + "type": "string", + "examples": ["Designated vaccinator by MOH-approved vaccination site"] + } + }, + "required": ["display"], + "additionalProperties": false + } + }, + "required": ["actor"], + "additionalProperties": false + } + } + }, + "additionalProperties": false, + "required": ["resourceType", "vaccineCode", "occurrenceDateTime", "lotNumber"] + }, + "ImmunizationRecommendation": { + "description": "A patient's point-in-time set of recommendations (i.e. forecasting) according to a published schedule with optional supporting justification.", + "properties": { + "fullUrl": { "type": "string", "examples": ["urn:uuid:9d209e2f-03c5-4425-8ca8-5b3df935b2a5"] }, + "resourceType": { + "description": "This is a ImmunizationRecommendation resource", + "enum": ["ImmunizationRecommendation"] + }, + "patient": { + "description": "The patient the recommendation(s) are for.", + "properties": { + "reference": { "type": "string", "examples": ["urn:uuid:83d9a3c8-8a04-46da-93be-380e09445b47"] } + }, + "type": "object", + "additionalProperties": false, + "required": ["reference"] + }, + "recommendation": { + "description": "Vaccine administration recommendations.", + "items": { + "properties": { + "targetDisease": { + "type": "object", + "properties": { + "coding": { + "items": { + "properties": { + "system": { "type": "string", "examples": ["http://snomed.info/sct"] }, + "code": { "type": "string", "examples": ["840539006"] }, + "display": { + "type": "string", + "examples": ["COVID-19"] + } + }, + "additionalProperties": false, + "required": ["system", "code", "display"] + }, + "type": "array", + "minItems": 1 + } + }, + "required": ["coding"], + "additionalProperties": false + }, + "forecastStatus": { + "type": "object", + "properties": { + "coding": { + "items": { + "properties": { + "system": { "type": "string", "examples": ["http://snomed.info/sct"] }, + "code": { "type": "string", "examples": ["complete"] }, + "display": { + "type": "string", + "examples": ["Complete"] + } + }, + "additionalProperties": false, + "required": ["system", "code", "display"] + }, + "type": "array", + "minItems": 1 + } + }, + "required": ["coding"], + "additionalProperties": false + }, + "dateCriterion": { + "type": "array", + "items": { + "properties": { + "code": { + "type": "object", + "properties": { + "coding": { + "items": { + "properties": { + "system": { "type": "string", "examples": [""] }, + "code": { "type": "string", "examples": ["effective"] }, + "display": { + "type": "string", + "examples": ["Effective"] + } + }, + "additionalProperties": false, + "required": ["system", "code", "display"] + }, + "type": "array", + "minItems": 1 + } + }, + "required": ["coding"], + "additionalProperties": false + }, + "value": { + "type": "string", + "format": "date", + "examples": ["2021-03-17"] + } + } + }, + "required": ["code", "value"] + } + } + }, + "type": "array" + } + }, + "additionalProperties": false, + "required": ["resourceType", "recommendation", "patient"] + }, + "Location": { + "description": "Details and position information for a physical place where services are provided and resources and participants may be stored, found, contained, or accommodated.", + "properties": { + "fullUrl": { "type": "string", "examples": ["urn:uuid:9d209e2f-03c5-4425-8ca8-5b3df935b2a5"] }, + "resourceType": { + "description": "This is a Location resource", + "enum": ["Location"] + }, + "id": { + "description": "The logical id of the resource, as used in the URL for the resource. Once assigned, this value never changes.", + "type": "string", + "examples": ["HCI000"] + }, + "name": { + "description": "Name of the location as used by humans. Does not need to be unique.", + "type": "string", + "examples": ["Vaccination site approved by Ministry of Health (MOH), Singapore [HCI000]"] + }, + "address": { + "description": "Physical location.", + "type": "object", + "properties": { + "country": { "type": "string", "examples": ["SG"] } + }, + "required": ["country"], + "additionalProperties": false + } + }, + "additionalProperties": false, + "required": ["resourceType", "id", "name", "address"] + } + }, + "additionalProperties": false + } + \ No newline at end of file diff --git a/src/sg/gov/moh/vaccination-healthcert/1.0/schema.test.ts b/src/sg/gov/moh/vaccination-healthcert/1.0/schema.test.ts new file mode 100644 index 0000000..33e8ce5 --- /dev/null +++ b/src/sg/gov/moh/vaccination-healthcert/1.0/schema.test.ts @@ -0,0 +1,12 @@ +import Ajv from "ajv"; +import schema from "./schema.json"; +import vaccineHealthCert from "./interim-vaccination-healthcert-unwrapped.json"; + +const ajv = new Ajv({ allErrors: true }); +const validator = ajv.compile(schema); + +describe("interim vaccination healthcert schema", () => { + it("should work with valid vaccine healthcert", () => { + expect(validator(vaccineHealthCert)).toBe(true); + }); +});