diff --git a/types/fhir-js-client/fhir-js-client-tests.ts b/types/fhir-js-client/fhir-js-client-tests.ts new file mode 100644 index 0000000000..d03a763031 --- /dev/null +++ b/types/fhir-js-client/fhir-js-client-tests.ts @@ -0,0 +1,681 @@ +class Component { + openFhirServiceUrl = 'https://sb-fhir-stu3.smarthealthit.org/smartstu3/open'; + closedFhirServiceUrl = 'https://sb-fhir-stu3.smarthealthit.org/smartstu3/data'; + + clientSettings: FHIR.SMART.OAuth2ClientSettings = { + client_id: '50a7f6ca-075a-47de-ab83-152814c3edb2', + scope: 'launch/patient user/*.* openid profile', + state: 'RandomStateOverride' + }; + + oauth2Configuration: FHIR.SMART.OAuth2Configuration = { + client: this.clientSettings, + server: this.closedFhirServiceUrl + }; + + authContext: FHIR.SMART.AuthContext = { + type: 'none' + }; + + context: FHIR.SMART.Context = { + serviceUrl: this.openFhirServiceUrl, + auth: this.authContext, + userId: 'tTFt1mD0vgox9npTe2ZA', + // Patient name: Abernathy,Francine + patientId: 'a509406a-5098-478c-b183-037d68a953d9' + }; + + observationEntry: FHIR.SMART.Entry = { + resource: { + 'resourceType': 'Observation', + 'id': 'c179f87f-e7d2-49b0-8838-c7cbf9b0da27', + 'meta': { + 'versionId': '1', + 'lastUpdated': '2018-01-12T19:22:57.000+00:00', + 'profile': [ + 'http://standardhealthrecord.org/fhir/StructureDefinition/shr-observation-Observation' + ], + 'tag': [ + { + 'system': 'https://smarthealthit.org/tags', + 'code': 'synthea-7-2017' + } + ] + }, + 'status': 'final', + 'category': [ + { + 'coding': [ + { + 'system': 'http://hl7.org/fhir/observation-category' + } + ] + } + ], + 'code': { + 'coding': [ + { + 'system': 'http://loinc.org', + 'code': '69453-9', + 'display': 'Cause of Death [US Standard Certificate of Death]' + } + ], + 'text': 'Cause of Death [US Standard Certificate of Death]' + }, + 'subject': { + 'reference': 'Patient/a509406a-5098-478c-b183-037d68a953d9' + }, + 'context': { + 'reference': 'Encounter/fbebfcf2-86d8-4c56-9539-314be0ce5bed' + }, + 'effectiveDateTime': '1989-07-26T17:51:45-04:00', + 'issued': '1989-07-26T17:51:45-04:00', + 'valueCodeableConcept': { + 'coding': [ + { + 'system': 'http://snomed.info/sct', + 'code': '185086009', + 'display': 'Chronic obstructive bronchitis (disorder)' + } + ], + 'text': 'Chronic obstructive bronchitis (disorder)' + } + } + }; + + weightObservationEntry: FHIR.SMART.Entry = { + 'resource': { + 'id': 'RandomId', + 'resourceType': 'Observation', + 'text': { + 'status': 'generated', + 'div': '
Human readable form
' + }, + 'status': 'final', + 'category': [ + { + 'coding': [ + { + 'system': 'http://hl7.org/fhir/observation-category', + 'code': 'vital-signs', + 'display': 'Vital Signs' + } + ] + } + ], + 'code': { + 'coding': [ + { + 'system': 'http://loinc.org', + 'code': '29463-7', + 'display': 'Body Weight' + }, + { + 'system': 'http://loinc.org', + 'code': '3141-9', + 'display': 'Body weight Measured' + }, + { + 'system': 'http://snomed.info/sct', + 'code': '27113001', + 'display': 'Body weight' + }, + { + 'system': 'http://acme.org/devices/clinical-codes', + 'code': 'body-weight', + 'display': 'Body Weight' + } + ] + }, + 'subject': { + 'reference': 'Patient/example' + }, + 'context': { + 'reference': 'Encounter/example' + }, + 'effectiveDateTime': '2016-03-28', + 'valueQuantity': { + 'value': 185, + 'unit': 'lbs', + 'system': 'http://unitsofmeasure.org', + 'code': '[lb_us]' + } + } + }; + + bundle: FHIR.SMART.Bundle = { + bundle: { + 'resourceType': 'Bundle', + 'id': '936286e3-cf22-44c8-be9f-b8d1b950a9a3', + 'meta': { + 'lastUpdated': '2018-05-22T22:08:19.000+00:00' + }, + 'type': 'searchset', + 'total': 1, + 'link': [ + { + 'relation': 'self', + // tslint:disable-next-line:max-line-length + 'url': 'https://sb-fhir-stu3.smarthealthit.org/smartstu3/open?_getpages=8bbd1c3c-a90f-49c6-8394-ca7524b46774&_getpagesoffset=10&_count=10&_pretty=true&_bundletype=searchset' + }, + { + 'relation': 'next', + // tslint:disable-next-line:max-line-length + 'url': 'https://sb-fhir-stu3.smarthealthit.org/smartstu3/open?_getpages=8bbd1c3c-a90f-49c6-8394-ca7524b46774&_getpagesoffset=20&_count=10&_format=json&_pretty=true&_bundletype=searchset' + }, + { + 'relation': 'prev', + // tslint:disable-next-line:max-line-length + 'url': 'https://sb-fhir-stu3.smarthealthit.org/smartstu3/open?_getpages=8bbd1c3c-a90f-49c6-8394-ca7524b46774&_getpagesoffset=0&_count=10&_format=json&_pretty=true&_bundletype=searchset' + } + ], + 'entry': [ + { + 'fullUrl': 'https://sb-fhir-stu3.smarthealthit.org/smartstu3/open/Observation/152759', + 'resource': { + 'resourceType': 'Observation', + 'id': '152759', + 'meta': { + 'versionId': '1', + 'lastUpdated': '2018-05-22T20:42:56.000+00:00', + 'profile': [ + 'http://standardhealthrecord.org/fhir/StructureDefinition/shr-observation-Observation' + ], + 'tag': [ + { + 'system': 'https://smarthealthit.org/tags', + 'code': 'synthea-7-2017' + } + ] + }, + 'status': 'final', + 'category': [ + { + 'coding': [ + { + 'system': 'http://hl7.org/fhir/observation-category' + } + ] + } + ], + 'code': { + 'coding': [ + { + 'system': 'http://loinc.org', + 'code': '69453-9', + 'display': 'Cause of Death [US Standard Certificate of Death]' + } + ], + 'text': 'Cause of Death [US Standard Certificate of Death]' + }, + 'subject': { + 'reference': 'Patient/a509406a-5098-478c-b183-037d68a953d9' + }, + 'context': { + 'reference': 'Encounter/fbebfcf2-86d8-4c56-9539-314be0ce5bed' + }, + 'effectiveDateTime': '1989-07-26T17:51:45-04:00', + 'issued': '1989-07-26T17:51:45-04:00', + 'valueCodeableConcept': { + 'coding': [ + { + 'system': 'http://snomed.info/sct', + 'code': '185086009', + 'display': 'Chronic obstructive bronchitis (disorder)' + } + ], + 'text': 'Chronic obstructive bronchitis (disorder)' + } + }, + 'search': { + 'mode': 'match' + } + } + ] + } + }; + + heightObservation: FHIR.SMART.Resource = { + 'resourceType': 'Observation', + 'id': 'body-height', + 'meta': { + 'profile': [ + 'http://hl7.org/fhir/StructureDefinition/vitalsigns' + ] + }, + 'text': { + 'status': 'generated', + 'div': '
Test
' + }, + 'status': 'final', + 'category': [ + { + 'coding': [ + { + 'system': 'http://hl7.org/fhir/observation-category', + 'code': 'vital-signs', + 'display': 'Vital Signs' + } + ], + 'text': 'Vital Signs' + } + ], + 'code': { + 'coding': [ + { + 'system': 'http://loinc.org', + 'code': '8302-2', + 'display': 'Body height' + } + ], + 'text': 'Body height' + }, + 'subject': { + 'reference': 'Patient/example' + }, + 'effectiveDateTime': '1999-07-02', + 'valueQuantity': { + 'value': 66.899999999999991, + 'unit': 'in', + 'system': 'http://unitsofmeasure.org', + 'code': '[in_i]' + } + }; + + transactionBundle: FHIR.SMART.Bundle = { + bundle: { + 'resourceType': 'Bundle', + 'id': '936286e3-cf22-44c8-be9f-b8d1b950a9a3', + 'meta': { + 'lastUpdated': '2018-05-22T22:08:19.000+00:00' + }, + 'type': 'transaction', + 'total': 1, + 'link': [ + { + 'relation': 'self', + // tslint:disable-next-line:max-line-length + 'url': 'https://sb-fhir-stu3.smarthealthit.org/smartstu3/open?_getpages=8bbd1c3c-a90f-49c6-8394-ca7524b46774&_getpagesoffset=10&_count=10&_pretty=true&_bundletype=searchset' + }, + { + 'relation': 'next', + // tslint:disable-next-line:max-line-length + 'url': 'https://sb-fhir-stu3.smarthealthit.org/smartstu3/open?_getpages=8bbd1c3c-a90f-49c6-8394-ca7524b46774&_getpagesoffset=20&_count=10&_format=json&_pretty=true&_bundletype=searchset' + }, + { + 'relation': 'prev', + // tslint:disable-next-line:max-line-length + 'url': 'https://sb-fhir-stu3.smarthealthit.org/smartstu3/open?_getpages=8bbd1c3c-a90f-49c6-8394-ca7524b46774&_getpagesoffset=0&_count=10&_format=json&_pretty=true&_bundletype=searchset' + } + ], + 'entry': [ + { + 'fullUrl': 'https://sb-fhir-stu3.smarthealthit.org/smartstu3/open/Observation/152759', + 'resource': { + 'resourceType': 'Observation', + 'id': '152759', + 'meta': { + 'versionId': '1', + 'lastUpdated': '2018-05-22T20:42:56.000+00:00', + 'profile': [ + 'http://standardhealthrecord.org/fhir/StructureDefinition/shr-observation-Observation' + ], + 'tag': [ + { + 'system': 'https://smarthealthit.org/tags', + 'code': 'synthea-7-2017' + } + ] + }, + 'status': 'final', + 'category': [ + { + 'coding': [ + { + 'system': 'http://hl7.org/fhir/observation-category' + } + ] + } + ], + 'code': { + 'coding': [ + { + 'system': 'http://loinc.org', + 'code': '69453-9', + 'display': 'Cause of Death [US Standard Certificate of Death]' + } + ], + 'text': 'Cause of Death [US Standard Certificate of Death]' + }, + 'subject': { + 'reference': 'Patient/a509406a-5098-478c-b183-037d68a953d9' + }, + 'context': { + 'reference': 'Encounter/fbebfcf2-86d8-4c56-9539-314be0ce5bed' + }, + 'effectiveDateTime': '1989-07-26T17:51:45-04:00', + 'issued': '1989-07-26T17:51:45-04:00', + 'valueCodeableConcept': { + 'coding': [ + { + 'system': 'http://snomed.info/sct', + 'code': '185086009', + 'display': 'Chronic obstructive bronchitis (disorder)' + } + ], + 'text': 'Chronic obstructive bronchitis (disorder)' + } + }, + 'request': { + 'method': 'POST' + }, + 'search': { + 'mode': 'match' + } + } + ] + } + }; + + searchParameter: FHIR.SMART.SearchParams = { + type: 'Observation', + query: { + code: '69453-9' + } + }; + + patientSpecificSearchParams: FHIR.SMART.SearchParams = { + patient: 'a509406a-5098-478c-b183-037d68a953d9', + type: 'Observation', + query: { + code: '69453-9' + } + }; + + resolveParameter: string[] = ['Observation.subject']; + + historyParameter: FHIR.SMART.HistoryParams = { + id: '152768', + type: 'Observation', + count: 10, + params: { _extraParam: 'extraParamValue' } + }; + + resourceType: FHIR.SMART.ResourceType = { + type: 'Observation' + }; + + readParameter: FHIR.SMART.ReadParams = { + type: 'Observation', + id: '152768' + }; + + vreadParameter: FHIR.SMART.VersionReadParams = { + type: 'Observation', + id: '152768', + versionId: '1' + }; + + resolveParameter2: FHIR.SMART.ResolveParams = { + reference: this.observationEntry.resource.subject, + resource: this.observationEntry.resource, + bundle: this.bundle.bundle + }; + + resourceParameter: FHIR.SMART.ResourceParameter = { + resource: 'Patient', + id: 'a509406a-5098-478c-b183-037d68a953d9' + }; + + oauth2ReadyCallback = (smartClient: FHIR.SMART.SMARTClient) => { + // Client interacting after a successful OAuth2 authorization workflow + const api: FHIR.SMART.Api = smartClient.api; + + // FHIR API methods exposed by fhir.js + api.conformance(this.context).then(this.apiSuccess, this.apiError); + api.create(this.observationEntry).then(this.apiSuccess, this.apiError); + api.delete(this.observationEntry).then(this.apiSuccess, this.apiError); + api.document(this.observationEntry).then(this.apiSuccess, this.apiError); + api.drain(this.searchParameter, this.drainProcess, this.drainDone, this.apiError); + api.fetchAll(this.searchParameter).then(this.fetchAllSuccess, this.apiError); + api.fetchAllWithReferences(this.searchParameter, this.resolveParameter) + .then(this.fetchAllWithReferencesSuccess, this.apiError); + api.history(this.historyParameter).then(this.apiSuccess, this.apiError); + api.nextPage(this.bundle).then(this.apiSuccess, this.apiError); + api.prevPage(this.bundle).then(this.apiSuccess, this.apiError); + api.profile(this.resourceType).then(this.apiSuccess, this.apiError); + api.read(this.readParameter).then(this.apiSuccess, this.apiError); + api.resolve(this.resolveParameter2).then(this.apiSuccess, this.apiError); + api.resourceHistory(this.historyParameter).then(this.apiSuccess, this.apiError); + api.search(this.searchParameter).then(this.apiSuccess, this.apiError); + api.search(this.patientSpecificSearchParams).then(this.apiSuccess, this.apiError); + api.transaction(this.transactionBundle).then(this.apiSuccess, this.apiError); + api.typeHistory(this.historyParameter).then(this.apiSuccess, this.apiError); + api.update(this.observationEntry).then(this.apiSuccess, this.apiError); + api.validate(this.observationEntry).then(this.apiSuccess, this.apiError); + api.vread(this.vreadParameter).then(this.apiSuccess, this.apiError); + + const objectPopulatedWithHeader = smartClient.authenticated({}); + smartClient.fetchBinary(this.observationEntry.resource.subject.reference).then(this.fetchBinarySuccess, this.apiError); + smartClient.get(this.resourceParameter).then(this.apiSuccess, this.apiError); + smartClient.getBinary(this.observationEntry.resource.subject.reference).then(this.fetchBinarySuccess, this.apiError); + + // Patient scoped FHIR API calls + if (smartClient.patient) { + const patient: FHIR.SMART.Patient = smartClient.patient; + const patientId: string = patient.id; + const patientApi: FHIR.SMART.Api = patient.api; + + patientApi.conformance(this.context).then(this.apiSuccess, this.apiError); + patientApi.create(this.observationEntry).then(this.apiSuccess, this.apiError); + patientApi.delete(this.observationEntry).then(this.apiSuccess, this.apiError); + patientApi.document(this.observationEntry).then(this.apiSuccess, this.apiError); + patientApi.drain(this.searchParameter, this.drainProcess, this.drainDone, this.apiError); + patientApi.fetchAll(this.searchParameter).then(this.fetchAllSuccess, this.apiError); + patientApi.fetchAllWithReferences(this.searchParameter, this.resolveParameter) + .then(this.fetchAllWithReferencesSuccess, this.apiError); + patientApi.history(this.historyParameter).then(this.apiSuccess, this.apiError); + patientApi.nextPage(this.bundle).then(this.apiSuccess, this.apiError); + patientApi.prevPage(this.bundle).then(this.apiSuccess, this.apiError); + patientApi.profile(this.resourceType).then(this.apiSuccess, this.apiError); + patientApi.read(this.readParameter).then(this.apiSuccess, this.apiError); + patientApi.resolve(this.resolveParameter2).then(this.apiSuccess, this.apiError); + patientApi.resourceHistory(this.historyParameter).then(this.apiSuccess, this.apiError); + patientApi.search(this.searchParameter).then(this.apiSuccess, this.apiError); + patientApi.search(this.patientSpecificSearchParams).then(this.apiSuccess, this.apiError); + patientApi.transaction(this.transactionBundle).then(this.apiSuccess, this.apiError); + patientApi.typeHistory(this.historyParameter).then(this.apiSuccess, this.apiError); + patientApi.update(this.observationEntry).then(this.apiSuccess, this.apiError); + patientApi.validate(this.observationEntry).then(this.apiSuccess, this.apiError); + patientApi.vread(this.vreadParameter).then(this.apiSuccess, this.apiError); + + patient.read().then(this.apiSuccess, this.apiError); + } + + // SMART client context helpers + const context: FHIR.SMART.Context = smartClient.server; + if (smartClient.server.auth) { + const auth: FHIR.SMART.AuthContext = smartClient.server.auth; + } + if (smartClient.server.patientId) { + const patientIdInContext: string = smartClient.server.patientId; + } + const fhirServiceUrl = smartClient.server.serviceUrl; + if (smartClient.server.userId) { + const userIdInContext: string = smartClient.server.userId; + } + const state: FHIR.SMART.OAuth2Configuration = smartClient.state; + const tokenResponse = smartClient.tokenResponse; + + // User in context + const userId = smartClient.userId; + const userInContext = smartClient.user; + smartClient.user.read().then(this.apiSuccess, this.apiError); + + // SMART client helper methods + // Observation helpers + const observationByCodesFn = smartClient.byCodes(this.observationEntry.resource, 'code'); + const observations = observationByCodesFn('29463-7'); + const observationsByCodes: FHIR.SMART.ObservationsByCode = smartClient.byCode(this.observationEntry.resource, 'code'); + // Unit helpers + const numericValue = smartClient.units.any(this.heightObservation.valueQuantity); + const convertedHeightValueInCm = smartClient.units.cm(this.heightObservation.valueQuantity); + const convertedWeightValueInKg = smartClient.units.kg(this.weightObservationEntry.resource.valueQuantity); + } + + oauth2ReadyErrback = (error: any) => { + FHIR.oauth2.authorize(this.oauth2Configuration, (authError) => { + console.log(authError); + }); + } + + oauth2ResolveAuthTypeCallback = (type: string) => { + console.log(type); + } + + oauth2ResolveAuthTypeErrback = (error: any) => { + console.log(error); + } + + apiSuccess = (response: FHIR.SMART.Response) => { + console.log('Response from the FHIR server:'); + console.log(response); + } + + apiError = (response: FHIR.SMART.Response) => { + console.log('Error response from the FHIR server:'); + console.log(response); + } + + drainProcess = (entries: FHIR.SMART.Entry[]) => { + console.log('Drain Process:'); + console.log(entries); + } + + drainDone = () => { + console.log('Drain Done'); + } + + fetchAllSuccess = (entries: FHIR.SMART.Entry[]) => { + console.log('Fetch all success:'); + console.log(entries); + } + + fetchAllWithReferencesSuccess = (entries: FHIR.SMART.Entry[], resolvedReferences: FHIR.SMART.ResolveFn) => { + console.log('Fetch all with references success:'); + console.log(entries); + console.log('Resolved References'); + console.log(resolvedReferences(this.observationEntry.resource, this.observationEntry.resource.subject)); + } + + genericCallback = (data: any) => { + console.log(data); + } + + fetchBinarySuccess = (blob: any) => { + // const reader = new FileReader(); + // reader.addEventListener('loadend', function () { + // console.log('Blob content'); + // console.log(reader.result); + // }); + // reader.readAsArrayBuffer(blob); + } + + openSmartFhir(): void { + // Open SMART on FHIR server interaction + const openSmartClient: FHIR.SMART.Client = FHIR.client(this.context); + const api: FHIR.SMART.Api = openSmartClient.api; + + api.conformance(this.context).then(this.apiSuccess, this.apiError); + api.create(this.observationEntry).then(this.apiSuccess, this.apiError); + api.delete(this.observationEntry).then(this.apiSuccess, this.apiError); + api.document(this.observationEntry).then(this.apiSuccess, this.apiError); + api.drain(this.searchParameter, this.drainProcess, this.drainDone, this.apiError); + api.fetchAll(this.searchParameter).then(this.fetchAllSuccess, this.apiError); + api.fetchAllWithReferences(this.searchParameter, this.resolveParameter) + .then(this.fetchAllWithReferencesSuccess, this.apiError); + api.history(this.historyParameter).then(this.apiSuccess, this.apiError); + api.nextPage(this.bundle).then(this.apiSuccess, this.apiError); + api.prevPage(this.bundle).then(this.apiSuccess, this.apiError); + api.profile(this.resourceType).then(this.apiSuccess, this.apiError); + api.read(this.readParameter).then(this.apiSuccess, this.apiError); + api.resolve(this.resolveParameter2).then(this.apiSuccess, this.apiError); + api.resourceHistory(this.historyParameter).then(this.apiSuccess, this.apiError); + api.search(this.searchParameter).then(this.apiSuccess, this.apiError); + api.search(this.patientSpecificSearchParams).then(this.apiSuccess, this.apiError); + api.transaction(this.transactionBundle).then(this.apiSuccess, this.apiError); + api.typeHistory(this.historyParameter).then(this.apiSuccess, this.apiError); + api.update(this.observationEntry).then(this.apiSuccess, this.apiError); + api.validate(this.observationEntry).then(this.apiSuccess, this.apiError); + api.vread(this.vreadParameter).then(this.apiSuccess, this.apiError); + + openSmartClient.fetchBinary(this.observationEntry.resource.subject.reference).then(this.fetchBinarySuccess, this.apiError); + openSmartClient.get(this.resourceParameter).then(this.apiSuccess, this.apiError); + openSmartClient.getBinary(this.observationEntry.resource.subject.reference).then(this.fetchBinarySuccess, this.apiError); + + if (openSmartClient.patient) { + const patient: FHIR.SMART.Patient = openSmartClient.patient; + const patientApi: FHIR.SMART.Api = patient.api; + const patientId: string = patient.id; + patientApi.conformance(this.context).then(this.apiSuccess, this.apiError); + patientApi.create(this.observationEntry).then(this.apiSuccess, this.apiError); + patientApi.delete(this.observationEntry).then(this.apiSuccess, this.apiError); + patientApi.document(this.observationEntry).then(this.apiSuccess, this.apiError); + patientApi.drain(this.searchParameter, this.drainProcess, this.drainDone, this.apiError); + patientApi.fetchAll(this.searchParameter).then(this.fetchAllSuccess, this.apiError); + patientApi.fetchAllWithReferences(this.searchParameter, this.resolveParameter) + .then(this.fetchAllWithReferencesSuccess, this.apiError); + patientApi.history(this.historyParameter).then(this.apiSuccess, this.apiError); + patientApi.nextPage(this.bundle).then(this.apiSuccess, this.apiError); + patientApi.prevPage(this.bundle).then(this.apiSuccess, this.apiError); + patientApi.profile(this.resourceType).then(this.apiSuccess, this.apiError); + patientApi.read(this.readParameter).then(this.apiSuccess, this.apiError); + patientApi.resolve(this.resolveParameter2).then(this.apiSuccess, this.apiError); + patientApi.resourceHistory(this.historyParameter).then(this.apiSuccess, this.apiError); + patientApi.search(this.searchParameter).then(this.apiSuccess, this.apiError); + patientApi.search(this.patientSpecificSearchParams).then(this.apiSuccess, this.apiError); + patientApi.transaction(this.transactionBundle).then(this.apiSuccess, this.apiError); + patientApi.typeHistory(this.historyParameter).then(this.apiSuccess, this.apiError); + patientApi.update(this.observationEntry).then(this.apiSuccess, this.apiError); + patientApi.validate(this.observationEntry).then(this.apiSuccess, this.apiError); + patientApi.vread(this.vreadParameter).then(this.apiSuccess, this.apiError); + patient.read().then(this.apiSuccess, this.apiError); + } + + const context: FHIR.SMART.Context = openSmartClient.server; + if (openSmartClient.server.auth) { + const auth: FHIR.SMART.AuthContext = openSmartClient.server.auth; + } + if (openSmartClient.server.patientId) { + const patientIdInContext: string = openSmartClient.server.patientId; + } + const fhirServiceUrl = openSmartClient.server.serviceUrl; + if (openSmartClient.server.userId) { + const userIdInContext: string = openSmartClient.server.userId; + } + + const userId = openSmartClient.userId; + const userInContext = openSmartClient.user; + openSmartClient.user.read().then(this.apiSuccess, this.apiError); + + const observationByCodesFn = openSmartClient.byCodes(this.observationEntry.resource, 'code'); + const observations = observationByCodesFn('29463-7'); + const observationsByCodes: FHIR.SMART.ObservationsByCode = openSmartClient.byCode(this.observationEntry.resource, 'code'); + + const numericValue = openSmartClient.units.any(this.heightObservation.valueQuantity); + const convertedHeightValueInCm = openSmartClient.units.cm(this.heightObservation.valueQuantity); + const convertedWeightValueInKg = openSmartClient.units.kg(this.weightObservationEntry.resource.valueQuantity); + } + + closedSmartOnFhir() { + // Closed FHIR server which requires user authorization + FHIR.oauth2.ready(this.oauth2ReadyCallback, this.oauth2ReadyErrback); + FHIR.oauth2.resolveAuthType(this.closedFhirServiceUrl, this.oauth2ResolveAuthTypeCallback, this.oauth2ResolveAuthTypeErrback); + } +} + +let component = new Component(); +// Perform the API interactions on the Open SMART on FHIR sandbox server +component.openSmartFhir(); +// Perform the API interactions on the Closed SMART on FHIR sandbox server(Requires user authorization) +component.closedSmartOnFhir(); diff --git a/types/fhir-js-client/index.d.ts b/types/fhir-js-client/index.d.ts new file mode 100644 index 0000000000..cf8ae2dbad --- /dev/null +++ b/types/fhir-js-client/index.d.ts @@ -0,0 +1,740 @@ +// Type definitions for fhir-js-client 1.0 +// Project: https://github.com/smart-on-fhir/client-js +// Definitions by: Ramachandran Gokulachandran +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.2 + +/** + * Namespace containing all global FHIR related definitions + */ +declare namespace FHIR { + /** + * Namespace containing all the SMART on FHIR related definitions + */ + namespace SMART { + /** + * FHIR API instance which can be used to make API calls to the FHIR server. + * This exposes all the methods implemented by jqFhir.js adapter of fhir.js client library + * https://github.com/FHIR/fhir.js + */ + interface Api { + /** + * GET BASEURL/metadata + * + * @param input : An override to the base config object or just an empty object {} + */ + conformance(input: any): Promise; + + /** + * POST BASEURL/{resourceType} BODY: {entry without id} + * @param entry : A valid FHIR entry containing the resource. + */ + create(entry: Entry): Promise; + + /** + * DELETE BASEURL/{resourceType} BODY {entry with id} + * @param entry : A valid FHIR entry object containing the resource object + * or an object containing the id property(id of the resource to be deleted) + */ + delete(entry: Entry): Promise; + + /** + * POST BASEURL/Document + * @param document + */ + document(document: Entry): Promise; + + /** + * GET BASEURL/{resourceType}?searchParams + * Fetch all the resources based on the search parameter (keeps on calling next method to gett all the pages) + * and process the result set of each page + * @param params Search Parameter, same as parameter of the Search method + * @param process Function called by passing the result set of each page fetched. + * @param done Function called once all the resources are fetched + * @param fail Function called when an error occurs in the process + */ + drain(params: SearchParams, process: (entries: Entry[]) => any, done: () => any, fail: (error: any) => any): void; + + /** + * GET BASEURL/{resourceType}?searchParams + * Fetch all the resources of a particular resource type and that meets the search filter. + * @param params Search Parameter, same as parameter of the Search method + * Returns a promise + * Resolve: (entries:any[]) => { console.log(entries); } + * Reject: (error:any) => { console.log(error); } + */ + fetchAll(params: SearchParams): Promise; + + /** + * GET BASEURL/{resourceType}?searchParams + * GET BASEURL/{referencedType} referencedType based on the resolveParams + * @param params Search Parameter, same as parameter of the Search method + * @param resolveParams Resolve Parameter, an array of string containing the each parameter in the format TargetResourceType.TargetElement + * eg. ['Observation.subject',Observation.context'] + * Returns a promise. + * Resolve: (entries:Entry[],resolvedReferences:[index: string]: Resource) => { console.log(entries); console.log(resolvedReferences);} + * Reject: (error:any) => { console.log(error); } + */ + fetchAllWithReferences(params: SearchParams, resolveParams: string[]): any; + + /** + * GET BASEURL/_history?historyParams + * Calls the _history API with the parameters specified + * @param params + */ + history(params: HistoryParams): Promise; + + /** + * GET BASEURL/_getpages=XXXX : Next link in the bundle + * Fetches the next page based on the input bundle resource's next link. + * @param bundle Input bundle resource containing the next link. + */ + nextPage(bundle: Bundle): Promise; + + /** + * GET BASEURL/_getpages=XXXX : Prev link in the bundle + * Fetches the previous page based on the input bundle resource's previous link. + * @param bundle Input bundle resource containing the previous link + */ + prevPage(bundle: Bundle): Promise; + + /** + * GET BASEURL/Profile/{resourceType} + * @param resourceType + */ + profile(resourceType: ResourceType): Promise; + + /** + * GET BASEURL/{resourceType}/{id} + * @param params An object with id and resourceType property + */ + read(params: ReadParams): Promise; + + /** + * Pass the node containing the property reference, which needs to be resolved. eg. myObservation.resource.subject + * The resource node should be present if the reference is contained within the resource. + * If a bundle resource is passed, will try to resolve within that bundle first before querying the server + * @param params The reference node to be resolved along with the resource and the bundle to be searched if the reference is a contained one. + */ + resolve(params: ResolveParams): Promise; + + /** + * GET BASEURL/{resourceType}/{id}/_history?otherHistoryParams + * @param params Should provide, resourceType and id either through id and type properties or the resource property + */ + resourceHistory(params: HistoryParams): Promise; + + /** + * GET BASEURL/{resourceType}?searchParams + * @param searchParams Filters to be applied to the API call + */ + search(params: SearchParams): Promise; + + /** + * POST BASEURL Body: Bundle with entries containing a node 'request': { 'method': 'POST' }. The value of 'method' can be DELETE,POST,PUT or GET. + * @param bundle + */ + transaction(bundle: Bundle): Promise; + + /** + * GET BASEURL/{resourceType}/_history?historyParams + * @param params + */ + typeHistory(params: HistoryParams): Promise; + + /** + * PUT BASEURL/{resourceType}/{id} + * @param entry : Entry containing the Resource to be updated + */ + update(entry: Entry): Promise; + + /** + * POST BASEURL/{resourceType}/_validate BODY: Resource to be validated + * PS: Cannot find the _validate endpoint on the SMART on FHIR open server. But $validate exists. + * @param entry + */ + validate(entry: Entry): Promise; + + /** + * GET BASEURL/{resourceType}/{id}/_history/{versionId} + * Fetches the specific historic version of the resource. + * @param params ResourceType, ResourceId and the VersionId + */ + vread(params: VersionReadParams): Promise; + } + + /** + * Reference within a resource + */ + interface Reference { + reference: string; + } + + /** + * Funtion type returned by the fetchAllWithReferences method + */ + type ResolveFn = (resource: Resource, reference: Reference) => Resource; + + /** + * Represents the parameter to be passed to the resolve api method + */ + interface ResolveParams { + /** + * Represents a reference within a resource. It can be a contained one(within the resource or a bundle) or a remote resource(eg {ResourceType}/{id}) + */ + reference: Reference; + /** + * The resource in which the reference will be resolved if the reference is a contained one. + */ + resource?: Resource; + /** + * The Bundle resource in which the reference will be resolved if the reference is a contained one within the bundle. + */ + bundle?: Resource; + } + + interface CommonQueryFilters { + /** + * Number of return records requested. The server is not bound to return the number requested, but cannot return more + */ + count?: number; + + /** + * Only include resource versions that were created at or after the given instant in time + * + * Type: instant An instant in time - known at least to the second and always includes a time zone. + * Note: This is intended for precisely observed times (typically system logs etc.), and not human-reported times - for them, + * use date and dateTime. instant is a more constrained dateTime xs:dateTime A JSON string - an xs:dateTime + * Note: This type is for system times, not human times (see date and dateTime below). + */ + since?: string; + + /** + * Addition filters to be applied on the history query. The object will be converted into query string parameters. + */ + params?: { + /** + * To specify alternative response formats by their MIME-types. + * This parameter allows a client to override the accept header value when it is unable to set it correctly due to internal limitations + * (e.g. XSLT usage). For the _format parameter, the values xml, text/xml, application/xml, and application/fhir+xml SHALL be interpreted + * to mean the XML format, the codes json, application/json and application/fhir+json SHALL be interpreted + * to mean the JSON format, and the codes ttl and text/turtle SHALL be interpreted to mean the Turtle RDF format. + * In addition, the values html and text/html are allowed. + */ + _format?: string; + /** + * Type: dateTime Only include resource versions that were current at some point during the time period specified + * in the date time value (may be more than one) + */ + _at?: string; + /** + * Making this interface extendable so that we can add more custom filter parameters + */ + [propName: string]: any; + }; + } + + interface HistoryParams extends CommonQueryFilters { + /** + * Resource ID, if the history to be fetched is resource specific. + */ + id?: string; + /** + * Type of the resource, if the history to be fetched is resource specific. + */ + type?: string; + } + + /** + * FHIR Search parameter : The parameter used to by the FHIR api methods to perform search on a particular resource. + */ + interface SearchParams extends CommonQueryFilters { + /** + * The name of the resource type on which the query is performed + */ + type: string; + /** + * The Search filter query object + * This object syntaxs adopts mongodb-like query syntax ([see](http://docs.mongodb.org/manual/tutorial/query-documents/) + * eg: + * {name: 'maud'} + * //=> name=maud + * + * {name: {$exact: 'maud'}} + * //=> name:exact=maud + * + * {name: {$or: ['maud','dave']}} + * //=> name=maud,dave + * + * {name: {$and: ['maud',{$exact: 'dave'}]}} + * //=> name=maud&name:exact=Dave + * + * {birthDate: {$gt: '1970', $lte: '1980'}} + * //=> birthDate=gt1970&birthDate=lte1980 + * + * {subject: {$type: 'Patient', name: 'maud', birthDate: {$gt: '1970'}}} + * //=> subject:Patient.name=maud&subject:Patient.birthDate=gt1970 + * + * {'subject.name': {$exact: 'maud'}} + * //=> subject.name:exact=maud + */ + query?: any; + + /** + * The patient id. If provided, will make the search query patient specific by adding the patien query parameter + */ + patient?: string; + } + + /** + * Used to specify the type of Resource which needs to be considered for the API call + */ + interface ResourceType { + type: string; + } + + /** + * Represents a FHIR entry + */ + interface Entry { + /** + * Version Stamp of resource + */ + versionId?: string; + /** + * FHIR Resource type name + */ + type?: string; + /** + * FHIR Resource + */ + resource: Resource; + /** + * Making this interface extendable since this is not a complete type definition of FHIR Entry + */ + [propName: string]: any; + } + + /** + * Represents a FHIR resource + */ + interface Resource { + /** + * The type of resource. All FHIR resources must have a resource type. + */ + resourceType: string; + /** + * Making this interface extendable since this is not a complete type definition of FHIR Resource + */ + [propName: string]: any; + } + + /** + * Represents a FHIR bundle + */ + interface Bundle { + /** + * The actual FHIR bundle, which is a FHIR resource itself with resourceType: 'Bundle' + */ + bundle: Resource; + } + + /** + * Parameter to be passed to the read API method + */ + interface ReadParams { + /** + * Type of the FHIR resource + */ + type: string; + /** + * ID of the FHIR resource + */ + id: string; + } + + /** + * Parameter to be passed to the vread API method + */ + interface VersionReadParams extends ReadParams { + /** + * Version ID of the resource to be fetched + */ + versionId: string; + } + + /** + * Structure of the response from the FHIR server + */ + interface Response { + /** + * Data returned from the FHIR server + */ + data?: any; + /** + * HTTP Status code string + */ + status?: any; + /** + * HTTP Response headers + */ + headers?: any; + /** + * Contains state information. Url, type, server configuration used, Id etc + */ + config?: any; + /** + * Error object returned by the client library. + */ + error?: any; + } + + /** + * The Patient in context + */ + interface Patient { + /** + * Patient ID + */ + id: string; + /** + * Patient scoped fhir.js FHIR API interface. This will ensure that the 'patient' query parameter is passed + * along with all the API calls which needs a patient context. + */ + api: Api; + /** + * Get the Patient resource in context + * GET BASEURL/Patient/{id} + */ + read(): Promise; + } + + /** + * Authorization Context which needs to be passed to create a SMART client directly + */ + interface AuthContext { + /** + * Type of Authorization 'none' | 'basic' | 'bearer' + */ + type: string; + /** + * Username if the type of authorization in 'basic' + */ + username?: string; + /** + * Password if the type of authorization in 'basic' + */ + password?: string; + /** + * Access token to be set if the type of authorization is 'bearer' + */ + token?: string; + } + + /** + * Context passed to create a SMART client directly without completing the SMART on FHIR OAuth2 authorization workflow + */ + interface Context { + /** + * FHIR service base url + */ + serviceUrl: string; + /** + * Authorization context + */ + auth?: AuthContext; + /** + * Patient in context + */ + patientId?: string; + /** + * User in context + */ + userId?: string; + } + + /** + * Represents a Resource type with ID + * Used by the get method + */ + interface ResourceParameter { + /** + * Name of the resource type + */ + resource: string; + /** + * Resource ID + */ + id?: string; + } + + /** + * User in context + */ + interface User { + /** + * Fetch the current user information from server. The library expects the userId to be of the format "ResourceType/Id" + */ + read(): Promise; + } + + interface SMARTClient extends Client { + /** + * Method to add the authorization headers based on the type of authorization Basic or Authorization + * @param input + * Returns: The object populated with Authorization header + */ + authenticated(input: any): any; + /** + * OAuth2 configuration used in context + */ + state: OAuth2Configuration; + + /** + * OAuth2 Access Token response + */ + tokenResponse?: any; + } + + interface Client { + /** + * fhir.js FHIR API interface + */ + api: Api; + + /** + * Fetch a resource as Binary/Blob + * @param path Absolute or relative path to the resource to be fetched + */ + fetchBinary(path: string): Promise; + + /** + * Get the resources based on the resource type and id + * @param params Resource type and id + */ + get(params: ResourceParameter): Promise; + + /** + * Fetch a resource as Binary/Blob (Same as fetchBinary) + * @param path Absolute url of the resource to be fetched + */ + getBinary(path: string): Promise; + + /** + * Current server, user and patient in context + */ + server: Context; + + /** + * Patient in context and interface to perform FHIR API calls within its context + */ + patient?: Patient; + + /** + * Id of the user in context + */ + userId?: string; + + /** + * User in context and exposes a method 'read' to get information. + */ + user: User; + + /** + * Split a set of observations based on codable concept properties. eg. code + * @param observations Observation resources + * @param property The name of the property which is a codable concept + * Returns a function to which you can pass code values and get a filtered array of observations. + */ + byCodes(observations: Resource | Resource[], ...property: string[]): (...codes: string[]) => Resource[]; + + /** + * Split a set of observations based on codable concept properties. eg. code + * @param observations Observation resources + * @param property The name of the property which is a codable concept + * Returns an object with code values as index and array of observations as values + */ + byCode(observations: Resource | Resource[], property: string): ObservationsByCode; + /** + * Helper method to convert units + */ + units: { + /** + * Converts the valueQuantity passed into cm based on the code + */ + cm(valueQuantity: any): number; + /** + * Converts the valueQuantity passed into kg based on the code + */ + kg(valueQuantity: any): number; + /** + * Returns the valueQuantity value as number. (As it is) + */ + any(valueQuantity: any): number; + }; + } + + /** + * Indexable type used to represent Observation FHIR resources indexed based on codes. + */ + interface ObservationsByCode { + [index: string]: Resource[]; + } + + /** + * SMART client settings for browser behaviour + */ + interface Settings { + /** + * Replaces the browser's current URL using window.history.replaceState API. + * Default to true + */ + replaceBrowserHistory: boolean; + /** + * When set to true, this variable will fully utilize HTML5 sessionStorage API. + * Default to true + * This variable can be overriden to false by setting FHIR.oauth2.settings.fullSessionStorageSupport = false. + * When set to false, the sessionStorage will be keyed by a state variable. This is to allow the embedded IE browser + * instances instantiated on a single thread to continue to function without having sessionStorage data shared + * across the embedded IE instances. + */ + fullSessionStorageSupport: boolean; + } + + /** + * Client or application specific settings provided on the FHIR server during the client/application registration. + */ + interface OAuth2ClientSettings { + /** + * Unique ID representing the client or application + */ + client_id: string; + /** + * URI to which the code is sent via query string during the OAuth2 authorization workflow. Defaults to the applications base url. + */ + redirect_uri?: string; + /** + * Scopes based on the access required by the application + */ + scope?: string; + /** + * If the application registered is a confidential application, the client_secret needs to be provided during the Token workflow. (Not recommended for public applications) + */ + secret?: string; + /** + * Override the state parameter set by the SMART JS Client library. The library generates a random string as state. + */ + state?: string; + } + + /** + * The OAuth2 endpoints (Registration URI, Authorize URI, Token URI) + */ + interface OAuth2Endpoints { + /** + * OAuth2 Registration URI + */ + registration_uri?: string; + /** + * OAuth2 Authorize URI + */ + authorize_uri?: string; + /** + * OAuth2 Token URI + */ + token_uri?: string; + } + + /** + * OAuth2 Provider information. If provided during the Authorize call, the library skips the conformance statement introspection + */ + interface OAuth2Provider { + /** + * FHIR Service URL + */ + url: string; + /** + * Unique name for the provider + */ + name?: string; + /** + * Description about the provider + */ + description?: string; + /** + * The OAuth2 endpoints (Registration URI, Authorize URI, Token URI) + */ + oauth2?: OAuth2Endpoints; + } + + /** + * OAuth2 Configuration of the SMART on FHIR Server + */ + interface OAuth2Configuration { + /** + * Set the response type based on the Authorization Grant eg. code(Authorization Code Grant), token(Implicit Grant) + */ + response_type?: string; + /** + * Client or application specific settings provided on the FHIR server during the client/application registration. + */ + client?: OAuth2ClientSettings; + /** + * FHIR Service URL + */ + server?: string; + /** + * Fake Access Token Response. Should contain the patient property to set the patient context. + */ + fake_token_response?: any; + /** + * OAuth2 Provider information. If provided during the Authorize call, the library skips the conformance statement introspection + */ + provider?: OAuth2Provider; + } + + /** + * SMART on FHIR OAuth2 authorization helper methods and settings + */ + interface OAuth2 { + /** + * Settings to drive the JS client browser behaviour + */ + settings: Settings; + + /** + * Handles the OAuth2 redirect + * If the URL contains the code parameter, it will complete the token call and pass the SMART client to the callback method + * @param callback Called once the SMART client is ready to be used. + * @param errback Called when either the OAuth2 workflow has not been started on an error occured during the OAuth2 workflow. + */ + ready(callback?: (smart: SMARTClient) => void, errback?: (...args: any[]) => void): void; + + /** + * Initiate the OAuth2 authorization workflow + * @param params OAuth2 Configuration + * @param errback Method which is triggered when an error occurs during the OAuth2 authorization workflow + */ + authorize(params: OAuth2Configuration, errback?: (...args: any[]) => void): void; + + /** + * Resolves the Authorization type of the FHIR server. Can be used to identify if a server supports SMART on FHIR. + * @param fhirServiceUrl FHIR Server Base URL + * @param callback Callled once the authorization type is retrieved from the conformance statement. + * @param errBack Called when an error occured while trying to fetch the conformance statement. + */ + resolveAuthType(fhirServiceUrl: string, callback?: (type: string) => void, errBack?: (type: string) => void): void; + } + } + + /** + * Construct the SMART client directly without using the Oauth2 workflow + * @param context Context required to construct the client. + */ + function client(context: SMART.Context): SMART.Client; + + /** + * Property which exposes the OAUth2 specific workflow helpers + */ + const oauth2: SMART.OAuth2; +} diff --git a/types/fhir-js-client/tsconfig.json b/types/fhir-js-client/tsconfig.json new file mode 100644 index 0000000000..79aa0c26b5 --- /dev/null +++ b/types/fhir-js-client/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6", + "dom" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "baseUrl": "../", + "typeRoots": [ + "../" + ], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.d.ts", + "fhir-js-client-tests.ts" + ] +} \ No newline at end of file diff --git a/types/fhir-js-client/tslint.json b/types/fhir-js-client/tslint.json new file mode 100644 index 0000000000..c56d9c1216 --- /dev/null +++ b/types/fhir-js-client/tslint.json @@ -0,0 +1,6 @@ +{ + "extends": "dtslint/dt.json", + "rules": { + "object-literal-key-quotes": false + } +} \ No newline at end of file