adding Base64 methods
Adding tests for BaseClient Adding Fhir401Client Adding fixtures for BaseClient and Fhir401Client
This commit is contained in:
parent
0a5d71691f
commit
6425ea48f0
|
@ -6,3 +6,6 @@ Find & replace the following
|
|||
- `fastenhealth` - find and replace this with your binary name
|
||||
- make sure you rename the folder as well.
|
||||
|
||||
# Running tests
|
||||
|
||||
- ng test --include='**/base_client.spec.ts'
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
|
||||
import {BaseClient} from './base_client';
|
||||
import {Source} from '../../../models/database/source';
|
||||
import * as BaseClient_GetRequest from './fixtures/BaseClient_GetRequest.json';
|
||||
import * as BaseClient_GetFhirVersion from './fixtures/BaseClient_GetFhirVersion.json';
|
||||
|
||||
|
||||
class TestClient extends BaseClient {
|
||||
constructor(source: Source) {
|
||||
|
@ -13,25 +15,19 @@ describe('BaseClient', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
client = new TestClient(new Source({
|
||||
"authorization_endpoint": "https://auth.logicahealth.org/authorize",
|
||||
"token_endpoint": "https://auth.logicahealth.org/token",
|
||||
"introspection_endpoint": "https://auth.logicahealth.org/introspect",
|
||||
"userinfo_endpoint": "",
|
||||
"scopes_supported": ["openid", "fhirUser", "patient/*.read", "offline_access"],
|
||||
"issuer": "https://auth.logicahealth.org",
|
||||
"grant_types_supported": ["authorization_code"],
|
||||
"response_types_supported": ["code"],
|
||||
"aud": "",
|
||||
"code_challenge_methods_supported": ["S256"],
|
||||
"api_endpoint_base_url": "https://api.logicahealth.org/fastenhealth/data",
|
||||
"client_id": "12b14c49-a4da-42f7-9e6f-2f19db622962",
|
||||
"redirect_uri": "https://lighthouse.fastenhealth.com/sandbox/callback/logica",
|
||||
"authorization_endpoint": "https://fhirsandbox.healthit.gov/open/r4/authorize",
|
||||
"token_endpoint": "https://fhirsandbox.healthit.gov/open/r4/token",
|
||||
"introspection_endpoint": "",
|
||||
"issuer": "https://fhirsandbox.healthit.go",
|
||||
"api_endpoint_base_url": "https://fhirsandbox.healthit.gov/secure/r4/fhir",
|
||||
"client_id": "9ad3ML0upIMiawLVdM5-DiPinGcv7M",
|
||||
"redirect_uri": "https://lighthouse.fastenhealth.com/sandbox/callback/healthit",
|
||||
"confidential": false,
|
||||
"source_type": "logica",
|
||||
"patient": "smart-1288992",
|
||||
"access_token": "xxx.xxx.xxx",
|
||||
"refresh_token": "xxx.xxx.",
|
||||
"expires_at": 1664949030,
|
||||
"source_type": "healthit",
|
||||
"patient": "placeholder",
|
||||
"access_token": "2e1be8c72d4d5225aae264a1fb7e1d3e",
|
||||
"refresh_token": "",
|
||||
"expires_at": 16649837100, //aug 11, 2497 (for testing)
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -41,14 +37,35 @@ describe('BaseClient', () => {
|
|||
|
||||
describe('GetRequest', () => {
|
||||
it('should make an authorized request', async () => {
|
||||
const resp = await client.GetRequest("Patient/smart-1288992")
|
||||
|
||||
expect(resp).toEqual({
|
||||
resourceType: "Patient",
|
||||
id: "source:aetna:patient"
|
||||
});
|
||||
//setup
|
||||
let response = new Response(JSON.stringify(BaseClient_GetRequest));
|
||||
Object.defineProperty(response, "url", { value: `${client.source.api_endpoint_base_url}/Patient/${client.source.patient}`});
|
||||
spyOn(window, "fetch").and.returnValue(Promise.resolve(response));
|
||||
|
||||
//test
|
||||
const resp = await client.GetRequest(`Patient/${client.source.patient}`)
|
||||
|
||||
//expect
|
||||
expect(resp.resourceType).toEqual("Patient");
|
||||
expect(resp.id).toEqual("123d41e1-0f71-4e9f-8eb2-d1b1330201a6");
|
||||
});
|
||||
})
|
||||
|
||||
describe('GetFhirVersion', () => {
|
||||
it('should make an authorized request', async () => {
|
||||
//setup
|
||||
let response = new Response(JSON.stringify(BaseClient_GetFhirVersion));
|
||||
Object.defineProperty(response, "url", { value: `${client.source.api_endpoint_base_url}/metadata`});
|
||||
spyOn(window, "fetch").and.returnValue(Promise.resolve(response));
|
||||
|
||||
//test
|
||||
const resp = await client.GetFhirVersion()
|
||||
|
||||
//expect
|
||||
expect(resp).toEqual("4.0.1");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {Source} from '../../../models/database/source';
|
||||
import * as Oauth from '@panva/oauth4webapi';
|
||||
import {IResourceInterface} from '../../interface';
|
||||
import {IResourceRaw} from '../../interface';
|
||||
|
||||
// BaseClient is an abstract/partial class, its intended to be used by FHIR clients, and generically handle OAuth requests.
|
||||
export abstract class BaseClient {
|
||||
|
@ -26,8 +26,14 @@ export abstract class BaseClient {
|
|||
}
|
||||
}
|
||||
|
||||
public async GetFhirVersion(): Promise<any> {
|
||||
return this.GetRequest("metadata")
|
||||
.then((resp) => {
|
||||
return resp.fhirVersion
|
||||
})
|
||||
}
|
||||
|
||||
public async GetRequest(resourceSubpathOrNext: string): Promise<IResourceInterface> {
|
||||
public async GetRequest(resourceSubpathOrNext: string): Promise<any> {
|
||||
|
||||
//check if the url is absolute
|
||||
let resourceUrl: string
|
||||
|
@ -53,6 +59,22 @@ export abstract class BaseClient {
|
|||
// return err
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Protected methods
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
protected GetPatientBundle(patientId: string): Promise<any> {
|
||||
return this.GetRequest(`Patient/${patientId}/$everything`)
|
||||
}
|
||||
|
||||
protected GetPatient(patientId: string): Promise<IResourceRaw> {
|
||||
return this.GetRequest(`Patient/${patientId}`)
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Private methods
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private async refreshExpiredTokenIfRequired(source: Source): Promise<Source> {
|
||||
//check if token has expired, and a refreshtoken is available
|
||||
// Note: source.expires_at is in seconds, Date.now() is in milliseconds.
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
import {FHIR401Client} from './fhir401_r4_client';
|
||||
import {Source} from '../../../models/database/source';
|
||||
import * as FHIR401Client_ProcessBundle from './fixtures/FHIR401Client_ProcessBundle.json';
|
||||
import {IResourceBundleRaw} from '../../interface';
|
||||
import {ResourceFhir} from '../../../models/database/resource_fhir';
|
||||
import {NewRepositiory} from '../../../database/pouchdb_repository';
|
||||
import {Base64} from '../../../utils/base64';
|
||||
|
||||
class TestClient extends FHIR401Client {
|
||||
constructor(source: Source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
public async ProcessBundle(bundle: IResourceBundleRaw): Promise<ResourceFhir[]> {
|
||||
return super.ProcessBundle(bundle);
|
||||
}
|
||||
}
|
||||
|
||||
describe('FHIR401Client', () => {
|
||||
let client: TestClient;
|
||||
|
||||
beforeEach(async () => {
|
||||
client = new TestClient(new Source({
|
||||
"_id": "source:aetna:12345",
|
||||
"authorization_endpoint": "https://fhirsandbox.healthit.gov/open/r4/authorize",
|
||||
"token_endpoint": "https://fhirsandbox.healthit.gov/open/r4/token",
|
||||
"introspection_endpoint": "",
|
||||
"issuer": "https://fhirsandbox.healthit.go",
|
||||
"api_endpoint_base_url": "https://fhirsandbox.healthit.gov/secure/r4/fhir",
|
||||
"client_id": "9ad3ML0upIMiawLVdM5-DiPinGcv7M",
|
||||
"redirect_uri": "https://lighthouse.fastenhealth.com/sandbox/callback/healthit",
|
||||
"confidential": false,
|
||||
"source_type": "healthit",
|
||||
"patient": "placeholder",
|
||||
"access_token": "2e1be8c72d4d5225aae264a1fb7e1d3e",
|
||||
"refresh_token": "",
|
||||
"expires_at": 16649837100, //aug 11, 2497 (for testing)
|
||||
}));
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(client).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('ProcessBundle', () => {
|
||||
it('should correctly wrap each BundleEntry with ResourceFhir', async () => {
|
||||
|
||||
//setup
|
||||
|
||||
//test
|
||||
const resp = await client.ProcessBundle(FHIR401Client_ProcessBundle)
|
||||
//expect
|
||||
expect(resp.length).toEqual(206);
|
||||
expect(resp[0].source_resource_id).toEqual("c088b7af-fc41-43cc-ab80-4a9ab8d47cd9");
|
||||
expect(resp[0].source_resource_type).toEqual("Patient");
|
||||
});
|
||||
})
|
||||
|
||||
describe('SyncAll', () => {
|
||||
it('should correctly add resources to the database', async () => {
|
||||
//setup
|
||||
let response = new Response(JSON.stringify(FHIR401Client_ProcessBundle));
|
||||
Object.defineProperty(response, "url", { value: `${client.source.api_endpoint_base_url}/Patient/${client.source.patient}/$everything`});
|
||||
spyOn(window, "fetch").and.returnValue(Promise.resolve(response));
|
||||
const db = NewRepositiory("fastentest")
|
||||
|
||||
//test
|
||||
const resp = await client.SyncAll(db)
|
||||
const firstResourceFhir = resp[0]
|
||||
const resourceIdParts = firstResourceFhir.split(":")
|
||||
|
||||
//expect
|
||||
expect(resp.length).toEqual(206);
|
||||
expect(firstResourceFhir).toEqual('resource_fhir:c291cmNlOmFldG5hOjEyMzQ1:Patient:c088b7af-fc41-43cc-ab80-4a9ab8d47cd9');
|
||||
expect(Base64.Decode(resourceIdParts[1])).toEqual("source:aetna:12345");
|
||||
|
||||
});
|
||||
})
|
||||
|
||||
})
|
|
@ -0,0 +1,62 @@
|
|||
import {IClient, IResourceBundleRaw, IResourceRaw} from '../../interface';
|
||||
import {BaseClient} from './base_client';
|
||||
import {Source} from '../../../models/database/source';
|
||||
import {IDatabaseRepository} from '../../../database/interface';
|
||||
import {ResourceFhir} from '../../../models/database/resource_fhir';
|
||||
|
||||
export class FHIR401Client extends BaseClient implements IClient {
|
||||
|
||||
//clients extending this class must validate fhirVersion matches using conformance/metadata url.
|
||||
fhirVersion = "4.0.1"
|
||||
|
||||
constructor(source: Source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
public async SyncAll(db: IDatabaseRepository): Promise<string[]> {
|
||||
const bundle = await this.GetPatientBundle(this.source.patient)
|
||||
|
||||
const wrappedResourceModels = await this.ProcessBundle(bundle)
|
||||
//todo, create the resources in dependency order
|
||||
|
||||
//TODO bulk insert
|
||||
// for(let dbModel of wrappedResourceModels){
|
||||
// db.CreateResource(dbModel)
|
||||
// }
|
||||
console.log(wrappedResourceModels)
|
||||
return db.CreateResources(wrappedResourceModels)
|
||||
|
||||
}
|
||||
|
||||
public async SyncAllBundle(db: IDatabaseRepository, bundleFile: any): Promise<any> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Protected methods
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
protected async ProcessBundle(bundle: IResourceBundleRaw): Promise<ResourceFhir[]> {
|
||||
// console.log(bundle)
|
||||
// process each entry in bundle
|
||||
return bundle.entry
|
||||
.filter((bundleEntry) => {
|
||||
return bundleEntry.resource.id // keep this entry if it has an ID, skip otherwise.
|
||||
})
|
||||
.map((bundleEntry) => {
|
||||
const wrappedResourceModel = new ResourceFhir()
|
||||
wrappedResourceModel.source_id = this.source._id
|
||||
wrappedResourceModel.source_resource_id = bundleEntry.resource.id
|
||||
wrappedResourceModel.source_resource_type = bundleEntry.resource.resourceType
|
||||
wrappedResourceModel.resource_raw = bundleEntry.resource
|
||||
// TODO find a way to safely/consistently get the resource updated date (and other metadata) which shoudl be added to the model.
|
||||
// wrappedResourceModel.updated_at = bundleEntry.resource.meta?.lastUpdated
|
||||
return wrappedResourceModel
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Private methods
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,159 @@
|
|||
{
|
||||
"resourceType": "Patient",
|
||||
"id": "123d41e1-0f71-4e9f-8eb2-d1b1330201a6",
|
||||
"meta": {
|
||||
"versionId": "1.0"
|
||||
},
|
||||
"extension": [
|
||||
{
|
||||
"url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race",
|
||||
"extension": [
|
||||
{
|
||||
"url": "ombCategory",
|
||||
"valueCoding": {
|
||||
"system": "urn:oid:2.16.840.1.113883.6.238",
|
||||
"code": "2054-5",
|
||||
"display": "Black or African American"
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "text",
|
||||
"valueString": "Black or African American"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity",
|
||||
"extension": [
|
||||
{
|
||||
"url": "ombCategory",
|
||||
"valueCoding": {
|
||||
"system": "urn:oid:2.16.840.1.113883.6.238",
|
||||
"code": "2135-2",
|
||||
"display": "Hispanic or Latino"
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "text",
|
||||
"valueString": "Hispanic or Latino"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "http://hl7.org/fhir/StructureDefinition/patient-mothersMaidenName",
|
||||
"valueString": "Ramona980 Franco581"
|
||||
},
|
||||
{
|
||||
"url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-birthsex",
|
||||
"valueCode": "M"
|
||||
},
|
||||
{
|
||||
"url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason",
|
||||
"valueCode": "masked"
|
||||
}
|
||||
],
|
||||
"identifier": [
|
||||
{
|
||||
"system": "https://github.com/synthetichealth/synthea",
|
||||
"value": "123d41e1-0f71-4e9f-8eb2-d1b1330201a6"
|
||||
},
|
||||
{
|
||||
"type": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
|
||||
"code": "MR",
|
||||
"display": "Medical Record Number"
|
||||
}
|
||||
]
|
||||
},
|
||||
"system": "http://hospital.smarthealthit.org",
|
||||
"value": "123d41e1-0f71-4e9f-8eb2-d1b1330201a6"
|
||||
},
|
||||
{
|
||||
"type": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
|
||||
"code": "SS",
|
||||
"display": "Social Security Number"
|
||||
}
|
||||
]
|
||||
},
|
||||
"system": "http://hl7.org/fhir/sid/us-ssn",
|
||||
"value": "999-50-6731"
|
||||
},
|
||||
{
|
||||
"type": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
|
||||
"code": "DL",
|
||||
"display": "Drivers License"
|
||||
}
|
||||
]
|
||||
},
|
||||
"system": "urn:oid:2.16.840.1.113883.4.3.25",
|
||||
"value": "S99957478"
|
||||
}
|
||||
],
|
||||
"name": [
|
||||
{
|
||||
"use": "official",
|
||||
"family": "Gutiérrez115",
|
||||
"given": [
|
||||
"Hugo693"
|
||||
],
|
||||
"prefix": [
|
||||
"Mr."
|
||||
]
|
||||
}
|
||||
],
|
||||
"telecom": [
|
||||
{
|
||||
"system": "phone",
|
||||
"value": "555-914-6475",
|
||||
"use": "home"
|
||||
}
|
||||
],
|
||||
"gender": "male",
|
||||
"birthDate": "1969-01-12",
|
||||
"address": [
|
||||
{
|
||||
"line": [
|
||||
"[\"293 Douglas Ramp\"]"
|
||||
],
|
||||
"city": "Natick",
|
||||
"state": "MA",
|
||||
"postalCode": "01999",
|
||||
"country": "US",
|
||||
"period": {
|
||||
"start": "1969-01-12T00:00:00+00:00"
|
||||
}
|
||||
}
|
||||
],
|
||||
"maritalStatus": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://terminology.hl7.org/CodeSystem/v3-MaritalStatus",
|
||||
"code": "M",
|
||||
"display": "M"
|
||||
}
|
||||
],
|
||||
"text": "M"
|
||||
},
|
||||
"multipleBirthBoolean": false,
|
||||
"communication": [
|
||||
{
|
||||
"language": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "urn:ietf:bcp:47",
|
||||
"code": "es",
|
||||
"display": "Spanish"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,14 +1,45 @@
|
|||
import {IDatabaseRepository} from '../database/interface';
|
||||
|
||||
export interface IClient {
|
||||
fhirVersion: string
|
||||
GetRequest(resourceSubpath: string): Promise<any>
|
||||
GetFhirVersion(): Promise<any>
|
||||
SyncAll(db: IDatabaseRepository): Promise<any>
|
||||
|
||||
//Manual client ONLY functions
|
||||
SyncAllBundle(db: IDatabaseRepository, bundleFile: any): Promise<any>
|
||||
}
|
||||
|
||||
export interface IResourceInterface {
|
||||
//This is the "raw" Fhir resource
|
||||
export interface IResourceRaw {
|
||||
resourceType: string
|
||||
id?: string
|
||||
meta?: IResourceMetaRaw
|
||||
}
|
||||
// This is the "raw" Fhir Bundle resource
|
||||
export interface IResourceBundleRaw {
|
||||
resourceType: string
|
||||
id?: string
|
||||
entry: IResourceBundleEntryRaw[]
|
||||
total?: number
|
||||
link?: IResourceBundleLinkRaw[]
|
||||
meta?: IResourceMetaRaw
|
||||
}
|
||||
|
||||
export interface IResourceBundleLinkRaw {
|
||||
id?: string
|
||||
relation: string
|
||||
url: string
|
||||
}
|
||||
|
||||
export interface IResourceBundleEntryRaw {
|
||||
id?: string
|
||||
fullUrl?: string
|
||||
resource: IResourceRaw
|
||||
}
|
||||
|
||||
export interface IResourceMetaRaw {
|
||||
id?: string
|
||||
versionId?: string
|
||||
lastUpdated: string
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export enum DocType {
|
||||
Source= "source",
|
||||
Source = "source",
|
||||
ResourceFhir = "resource_fhir"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import {Source} from '../models/database/source';
|
||||
import {ResourceFhir} from '../models/database/resource_fhir';
|
||||
// import {SourceSummary} from '../../app/models/fasten/source-summary';
|
||||
|
||||
export interface IDatabaseDocument {
|
||||
_id?: string
|
||||
_rev?: string
|
||||
doc_type: string
|
||||
populateId(): void
|
||||
}
|
||||
|
||||
|
@ -31,4 +35,10 @@ export interface IDatabaseRepository {
|
|||
DeleteSource(source_id: string): Promise<boolean>
|
||||
// GetSourceSummary(source_id: string): Promise<SourceSummary>
|
||||
GetSources(): Promise<IDatabasePaginatedResponse>
|
||||
|
||||
|
||||
CreateResource(resource: ResourceFhir): Promise<string>
|
||||
CreateResources(resources: ResourceFhir[]): Promise<string[]>
|
||||
GetResource(resource_id: string): Promise<ResourceFhir>
|
||||
GetResources(): Promise<IDatabasePaginatedResponse>
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ describe('PouchdbRepository', () => {
|
|||
|
||||
const createdSource = await repository.GetSource(createdId)
|
||||
|
||||
expect(createdSource.docType).toEqual(DocType.Source);
|
||||
expect(createdSource.doc_type).toEqual(DocType.Source);
|
||||
expect(createdSource.patient).toEqual('patient');
|
||||
expect(createdSource.source_type).toEqual(SourceType.Aetna);
|
||||
expect(createdSource.access_token).toEqual('hello-world');
|
||||
|
|
|
@ -3,6 +3,7 @@ import {IDatabasePaginatedResponse, IDatabaseDocument, IDatabaseRepository} from
|
|||
import * as PouchDB from 'pouchdb/dist/pouchdb';
|
||||
// import * as PouchDB from 'pouchdb';
|
||||
import {DocType} from './constants';
|
||||
import {ResourceFhir} from '../models/database/resource_fhir';
|
||||
|
||||
export function NewRepositiory(databaseName: string = 'fasten'): IDatabaseRepository {
|
||||
return new PouchdbRepository(databaseName)
|
||||
|
@ -44,6 +45,32 @@ export class PouchdbRepository implements IDatabaseRepository {
|
|||
return this.deleteDocument(source_id)
|
||||
}
|
||||
|
||||
public async CreateResource(resource: ResourceFhir): Promise<string> {
|
||||
return this.createDocument(resource);
|
||||
}
|
||||
|
||||
public async CreateResources(resources: ResourceFhir[]): Promise<string[]> {
|
||||
return this.createBulk(resources);
|
||||
}
|
||||
|
||||
public async GetResource(resource_id: string): Promise<ResourceFhir> {
|
||||
return this.getDocument(resource_id)
|
||||
.then((doc) => {
|
||||
return new ResourceFhir(doc)
|
||||
})
|
||||
}
|
||||
|
||||
public async GetResources(): Promise<IDatabasePaginatedResponse> {
|
||||
return this.findDocumentByDocType(DocType.ResourceFhir)
|
||||
.then((docWrapper) => {
|
||||
|
||||
docWrapper.rows = docWrapper.rows.map((result) => {
|
||||
return new ResourceFhir(result.doc)
|
||||
})
|
||||
return docWrapper
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// CRUD Operators
|
||||
|
@ -73,6 +100,15 @@ export class PouchdbRepository implements IDatabaseRepository {
|
|||
);
|
||||
}
|
||||
|
||||
// create multiple documents, returns a list of generated ids
|
||||
private createBulk(docs: IDatabaseDocument[]): Promise<string[]> {
|
||||
return this.GetDB()
|
||||
.bulkDocs(docs.map((doc) => { doc.populateId(); return doc }))
|
||||
.then((results): string[] => {
|
||||
return results.map((result) => result.id)
|
||||
})
|
||||
}
|
||||
|
||||
private getDocument(id: string): Promise<any> {
|
||||
return this.GetDB()
|
||||
.get(id)
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import {DocType} from '../../database/constants';
|
||||
import {IResourceRaw} from '../../conduit/interface';
|
||||
import {Base64} from '../../utils/base64';
|
||||
|
||||
export class ResourceFhir {
|
||||
_id?: string
|
||||
_rev?: string
|
||||
doc_type: string
|
||||
|
||||
created_at?: Date
|
||||
updated_at?: Date
|
||||
source_id: string
|
||||
source_resource_type: string
|
||||
source_resource_id: string
|
||||
|
||||
resource_raw: IResourceRaw
|
||||
|
||||
constructor(object?: any) {
|
||||
if(object){
|
||||
object.doc_type = DocType.ResourceFhir
|
||||
return Object.assign(this, object)
|
||||
} else{
|
||||
this.doc_type = DocType.ResourceFhir
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
populateId(){
|
||||
//TODO: source_id should be base64 encoded (otherwise we get nested : chars)
|
||||
this._id = `${this.doc_type}:${Base64.Encode(this.source_id)}:${this.source_resource_type}:${this.source_resource_id}`
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ import {DocType} from '../../../lib/database/constants';
|
|||
export class Source extends LighthouseSourceMetadata{
|
||||
_id?: string
|
||||
_rev?: string
|
||||
docType: string
|
||||
doc_type: string
|
||||
source_type: SourceType
|
||||
|
||||
patient: string
|
||||
|
@ -16,11 +16,11 @@ export class Source extends LighthouseSourceMetadata{
|
|||
|
||||
constructor(object: any) {
|
||||
super()
|
||||
object.docType = DocType.Source
|
||||
object.doc_type = DocType.Source
|
||||
return Object.assign(this, object)
|
||||
}
|
||||
|
||||
populateId(){
|
||||
this._id = `source:${this.source_type}:${this.patient}`
|
||||
this._id = `${this.doc_type}:${this.source_type}:${this.patient}`
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
export class Base64 {
|
||||
public static Encode(data: string): string {
|
||||
return btoa(data)
|
||||
}
|
||||
public static Decode(data: string): string {
|
||||
return atob(data)
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"resolveJsonModule": true,
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine",
|
||||
|
|
Loading…
Reference in New Issue