@oystehr/sdk 4.3.3 → 4.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cjs/config.d.ts +15 -0
- package/dist/cjs/index.min.cjs +1 -1
- package/dist/cjs/index.min.cjs.map +1 -1
- package/dist/cjs/resources/classes/fhir-ext.cjs +294 -19
- package/dist/cjs/resources/classes/fhir-ext.cjs.map +1 -1
- package/dist/cjs/resources/classes/fhir-ext.d.ts +2 -1
- package/dist/cjs/resources/classes/fhir.cjs +1 -0
- package/dist/cjs/resources/classes/fhir.cjs.map +1 -1
- package/dist/cjs/resources/classes/fhir.d.ts +1 -0
- package/dist/cjs/resources/classes/index.cjs +7 -0
- package/dist/cjs/resources/classes/index.cjs.map +1 -1
- package/dist/cjs/resources/types/RcmListPayersParams.d.ts +4 -0
- package/dist/cjs/resources/types/RcmListPayersResponse.d.ts +9 -1
- package/dist/cjs/resources/types/TelemedCreateMeetingParams.d.ts +4 -0
- package/dist/cjs/resources/types/fhir.d.ts +16 -3
- package/dist/esm/config.d.ts +15 -0
- package/dist/esm/index.min.js +1 -1
- package/dist/esm/index.min.js.map +1 -1
- package/dist/esm/resources/classes/fhir-ext.d.ts +2 -1
- package/dist/esm/resources/classes/fhir-ext.js +294 -20
- package/dist/esm/resources/classes/fhir-ext.js.map +1 -1
- package/dist/esm/resources/classes/fhir.d.ts +1 -0
- package/dist/esm/resources/classes/fhir.js +2 -1
- package/dist/esm/resources/classes/fhir.js.map +1 -1
- package/dist/esm/resources/classes/index.js +7 -0
- package/dist/esm/resources/classes/index.js.map +1 -1
- package/dist/esm/resources/types/RcmListPayersParams.d.ts +4 -0
- package/dist/esm/resources/types/RcmListPayersResponse.d.ts +9 -1
- package/dist/esm/resources/types/TelemedCreateMeetingParams.d.ts +4 -0
- package/dist/esm/resources/types/fhir.d.ts +16 -3
- package/package.json +1 -1
- package/src/config.ts +16 -0
- package/src/resources/classes/fhir-ext.ts +331 -19
- package/src/resources/classes/fhir.ts +1 -0
- package/src/resources/classes/index.ts +7 -0
- package/src/resources/types/RcmListPayersParams.ts +4 -0
- package/src/resources/types/RcmListPayersResponse.ts +9 -1
- package/src/resources/types/TelemedCreateMeetingParams.ts +4 -0
- package/src/resources/types/fhir.ts +20 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../../src/resources/classes/index.ts"],"sourcesContent":["// AUTOGENERATED -- DO NOT EDIT\n\nimport { OystehrConfig } from '../../config';\nimport { Application } from './application';\nimport { Charge } from './charge';\nimport { Conversation } from './conversation';\nimport { Developer } from './developer';\nimport { Erx } from './erx';\nimport { Fax } from './fax';\nimport { Fhir } from './fhir';\nimport { Lab } from './lab';\nimport { M2m } from './m2m';\nimport { Messaging } from './messaging';\nimport { PaymentMethod } from './paymentMethod';\nimport { Project } from './project';\nimport { Rcm } from './rcm';\nimport { Role } from './role';\nimport { Secret } from './secret';\nimport { Telemed } from './telemed';\nimport { Terminology } from './terminology';\nimport { TransactionalSMS } from './transactionalSMS';\nimport { User } from './user';\nimport { Version } from './version';\nimport { Z3 } from './z3';\nimport { Zambda } from './zambda';\nimport { ZambdaLogStream } from './zambdaLogStream';\n\nexport class Oystehr {\n readonly config: OystehrConfig;\n readonly application: Application;\n readonly developer: Developer;\n readonly m2m: M2m;\n readonly messaging: Messaging;\n readonly conversation: Conversation;\n readonly transactionalSMS: TransactionalSMS;\n readonly paymentMethod: PaymentMethod;\n readonly charge: Charge;\n readonly project: Project;\n readonly role: Role;\n readonly secret: Secret;\n readonly telemed: Telemed;\n readonly user: User;\n readonly version: Version;\n readonly z3: Z3;\n readonly fax: Fax;\n readonly lab: Lab;\n readonly erx: Erx;\n readonly terminology: Terminology;\n readonly zambda: Zambda;\n readonly zambdaLogStream: ZambdaLogStream;\n readonly rcm: Rcm;\n readonly fhir: Fhir;\n constructor(config: OystehrConfig) {\n this.config = config;\n this.config.services ??= {};\n this.config.services['projectApiUrl'] ??= config.projectApiUrl;\n this.config.services['fhirApiUrl'] ??= config.fhirApiUrl;\n this.application = new Application(config);\n this.developer = new Developer(config);\n this.m2m = new M2m(config);\n this.messaging = new Messaging(config);\n this.conversation = new Conversation(config);\n this.transactionalSMS = new TransactionalSMS(config);\n this.paymentMethod = new PaymentMethod(config);\n this.charge = new Charge(config);\n this.project = new Project(config);\n this.role = new Role(config);\n this.secret = new Secret(config);\n this.telemed = new Telemed(config);\n this.user = new User(config);\n this.version = new Version(config);\n this.z3 = new Z3(config);\n this.fax = new Fax(config);\n this.lab = new Lab(config);\n this.erx = new Erx(config);\n this.terminology = new Terminology(config);\n this.zambda = new Zambda(config);\n this.zambdaLogStream = new ZambdaLogStream(config);\n this.rcm = new Rcm(config);\n this.fhir = new Fhir(config);\n }\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../src/resources/classes/index.ts"],"sourcesContent":["// AUTOGENERATED -- DO NOT EDIT\n\nimport { OystehrConfig } from '../../config';\nimport { OystehrSdkError } from '../../errors';\nimport { Application } from './application';\nimport { Charge } from './charge';\nimport { Conversation } from './conversation';\nimport { Developer } from './developer';\nimport { Erx } from './erx';\nimport { Fax } from './fax';\nimport { Fhir } from './fhir';\nimport { Lab } from './lab';\nimport { M2m } from './m2m';\nimport { Messaging } from './messaging';\nimport { PaymentMethod } from './paymentMethod';\nimport { Project } from './project';\nimport { Rcm } from './rcm';\nimport { Role } from './role';\nimport { Secret } from './secret';\nimport { Telemed } from './telemed';\nimport { Terminology } from './terminology';\nimport { TransactionalSMS } from './transactionalSMS';\nimport { User } from './user';\nimport { Version } from './version';\nimport { Z3 } from './z3';\nimport { Zambda } from './zambda';\nimport { ZambdaLogStream } from './zambdaLogStream';\n\nexport class Oystehr {\n readonly config: OystehrConfig;\n readonly application: Application;\n readonly developer: Developer;\n readonly m2m: M2m;\n readonly messaging: Messaging;\n readonly conversation: Conversation;\n readonly transactionalSMS: TransactionalSMS;\n readonly paymentMethod: PaymentMethod;\n readonly charge: Charge;\n readonly project: Project;\n readonly role: Role;\n readonly secret: Secret;\n readonly telemed: Telemed;\n readonly user: User;\n readonly version: Version;\n readonly z3: Z3;\n readonly fax: Fax;\n readonly lab: Lab;\n readonly erx: Erx;\n readonly terminology: Terminology;\n readonly zambda: Zambda;\n readonly zambdaLogStream: ZambdaLogStream;\n readonly rcm: Rcm;\n readonly fhir: Fhir;\n constructor(config: OystehrConfig) {\n if (config.workspaceTag && config.ignoreTags) {\n throw new OystehrSdkError({\n message: 'workspaceTag and ignoreTags are mutually exclusive and cannot both be set in config',\n code: 400,\n });\n }\n this.config = config;\n this.config.services ??= {};\n this.config.services['projectApiUrl'] ??= config.projectApiUrl;\n this.config.services['fhirApiUrl'] ??= config.fhirApiUrl;\n this.application = new Application(config);\n this.developer = new Developer(config);\n this.m2m = new M2m(config);\n this.messaging = new Messaging(config);\n this.conversation = new Conversation(config);\n this.transactionalSMS = new TransactionalSMS(config);\n this.paymentMethod = new PaymentMethod(config);\n this.charge = new Charge(config);\n this.project = new Project(config);\n this.role = new Role(config);\n this.secret = new Secret(config);\n this.telemed = new Telemed(config);\n this.user = new User(config);\n this.version = new Version(config);\n this.z3 = new Z3(config);\n this.fax = new Fax(config);\n this.lab = new Lab(config);\n this.erx = new Erx(config);\n this.terminology = new Terminology(config);\n this.zambda = new Zambda(config);\n this.zambdaLogStream = new ZambdaLogStream(config);\n this.rcm = new Rcm(config);\n this.fhir = new Fhir(config);\n }\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAAA;sBA4Ba,OAAO,CAAA;AACT,IAAA,MAAM;AACN,IAAA,WAAW;AACX,IAAA,SAAS;AACT,IAAA,GAAG;AACH,IAAA,SAAS;AACT,IAAA,YAAY;AACZ,IAAA,gBAAgB;AAChB,IAAA,aAAa;AACb,IAAA,MAAM;AACN,IAAA,OAAO;AACP,IAAA,IAAI;AACJ,IAAA,MAAM;AACN,IAAA,OAAO;AACP,IAAA,IAAI;AACJ,IAAA,OAAO;AACP,IAAA,EAAE;AACF,IAAA,GAAG;AACH,IAAA,GAAG;AACH,IAAA,GAAG;AACH,IAAA,WAAW;AACX,IAAA,MAAM;AACN,IAAA,eAAe;AACf,IAAA,GAAG;AACH,IAAA,IAAI;AACb,IAAA,WAAA,CAAY,MAAqB,EAAA;QAC/B,IAAI,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,UAAU,EAAE;YAC5C,MAAM,IAAI,eAAe,CAAC;AACxB,gBAAA,OAAO,EAAE,qFAAqF;AAC9F,gBAAA,IAAI,EAAE,GAAG;AACV,aAAA,CAAC;QACJ;AACA,QAAA,IAAI,CAAC,MAAM,GAAG,MAAM;AACpB,QAAA,IAAI,CAAC,MAAM,CAAC,QAAQ,KAAK,EAAE;QAC3B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,KAAK,MAAM,CAAC,aAAa;QAC9D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,KAAK,MAAM,CAAC,UAAU;QACxD,IAAI,CAAC,WAAW,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC;QAC1C,IAAI,CAAC,SAAS,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC;QACtC,IAAI,CAAC,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,SAAS,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC;QACtC,IAAI,CAAC,YAAY,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC;QAC5C,IAAI,CAAC,gBAAgB,GAAG,IAAI,gBAAgB,CAAC,MAAM,CAAC;QACpD,IAAI,CAAC,aAAa,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC;QAC9C,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC;QAChC,IAAI,CAAC,OAAO,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;QAClC,IAAI,CAAC,IAAI,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC;QAChC,IAAI,CAAC,OAAO,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;QAClC,IAAI,CAAC,IAAI,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC;QAC5B,IAAI,CAAC,OAAO,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;QAClC,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,MAAM,CAAC;QACxB,IAAI,CAAC,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,WAAW,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC;QAC1C,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC;QAChC,IAAI,CAAC,eAAe,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC;QAClD,IAAI,CAAC,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC;IAC9B;AACD;;;;"}
|
|
@@ -2,4 +2,12 @@ import { Organization } from 'fhir/r4b';
|
|
|
2
2
|
/**
|
|
3
3
|
* List of payers matching the search criteria.
|
|
4
4
|
*/
|
|
5
|
-
export
|
|
5
|
+
export interface RcmListPayersResponse {
|
|
6
|
+
data: Organization[];
|
|
7
|
+
metadata: {
|
|
8
|
+
/**
|
|
9
|
+
* Cursor to fetch the next page of results. Null if there are no further pages.
|
|
10
|
+
*/
|
|
11
|
+
nextCursor: string | null;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -4,4 +4,8 @@ export interface TelemedCreateMeetingParams {
|
|
|
4
4
|
* Well formed Encounter with the https://extensions.fhir.oystehr.com/encounter-other-participants extension specified optionally.
|
|
5
5
|
*/
|
|
6
6
|
encounter: Encounter;
|
|
7
|
+
/**
|
|
8
|
+
* If true, audio from the meeting will be recorded and stored as an MP4 file in Z3. Defaults to false.
|
|
9
|
+
*/
|
|
10
|
+
recordAudio?: boolean;
|
|
7
11
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Operation } from 'fast-json-patch';
|
|
2
|
-
import type { Binary as BinaryR4B, Bundle as BundleR4B, BundleEntry as BundleEntryR4B, FhirResource as FhirResourceR4B, OperationOutcome as OperationOutcomeR4B } from 'fhir/r4b';
|
|
3
|
-
import type { Binary as BinaryR5, Bundle as BundleR5, BundleEntry as BundleEntryR5, FhirResource as FhirResourceR5, OperationOutcome as OperationOutcomeR5 } from 'fhir/r5';
|
|
2
|
+
import type { Binary as BinaryR4B, Bundle as BundleR4B, BundleEntry as BundleEntryR4B, Coding as CodingR4B, FhirResource as FhirResourceR4B, OperationOutcome as OperationOutcomeR4B, Resource as ResourceR4B } from 'fhir/r4b';
|
|
3
|
+
import type { Binary as BinaryR5, Bundle as BundleR5, BundleEntry as BundleEntryR5, Coding as CodingR5, FhirResource as FhirResourceR5, OperationOutcome as OperationOutcomeR5, Resource as ResourceR5 } from 'fhir/r5';
|
|
4
4
|
export type FhirResource = FhirResourceR4B | FhirResourceR5;
|
|
5
5
|
export type FhirBundle<F extends FhirResource> = F extends FhirResourceR4B ? BundleR4B<F> : BundleR5<F>;
|
|
6
6
|
export type EntrylessFhirBundle<F extends FhirResource> = F extends FhirResourceR4B ? Omit<BundleR4B<F>, 'entry'> : Omit<BundleR5<F>, 'entry'>;
|
|
@@ -28,6 +28,8 @@ export type TransactionBundle<F extends FhirResource> = EntrylessFhirBundle<F> &
|
|
|
28
28
|
export type BundleEntry<F extends FhirResource> = F extends FhirResourceR4B ? BundleEntryR4B<F> : BundleEntryR5<F>;
|
|
29
29
|
export type Binary<F extends FhirResource> = F extends FhirResourceR4B ? BinaryR4B : BinaryR5;
|
|
30
30
|
export type OperationOutcome = OperationOutcomeR4B | OperationOutcomeR5;
|
|
31
|
+
export type Coding = CodingR4B | CodingR5;
|
|
32
|
+
export type Resource = ResourceR4B | ResourceR5;
|
|
31
33
|
export type SearchParam = {
|
|
32
34
|
name: string;
|
|
33
35
|
value: string | number;
|
|
@@ -56,6 +58,9 @@ export interface FhirDeleteParams<T extends FhirResource> {
|
|
|
56
58
|
resourceType: T['resourceType'];
|
|
57
59
|
id: string;
|
|
58
60
|
}
|
|
61
|
+
export interface GenerateFriendlyPatientIdParams {
|
|
62
|
+
id: string;
|
|
63
|
+
}
|
|
59
64
|
export interface FhirHistorySearchParams<T extends FhirResource> {
|
|
60
65
|
resourceType: T['resourceType'];
|
|
61
66
|
id: string;
|
|
@@ -89,6 +94,14 @@ export interface BatchInputPostRequest<F extends FhirResource> extends BatchInpu
|
|
|
89
94
|
resource: F;
|
|
90
95
|
fullUrl?: string;
|
|
91
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* A batch POST to a `_search` endpoint (e.g. `Patient/_search`). Search parameters
|
|
99
|
+
* may be appended to the URL as query parameters. Unlike a create POST there is no
|
|
100
|
+
* resource body.
|
|
101
|
+
*/
|
|
102
|
+
export interface BatchInputSearchPostRequest extends BatchInputBaseRequest {
|
|
103
|
+
method: 'POST';
|
|
104
|
+
}
|
|
92
105
|
export interface BatchInputBinaryPatchRequest<F extends FhirResource> extends BatchInputBaseRequest {
|
|
93
106
|
method: 'PATCH';
|
|
94
107
|
ifMatch?: string;
|
|
@@ -100,7 +113,7 @@ export interface BatchInputJSONPatchRequest extends BatchInputBaseRequest {
|
|
|
100
113
|
operations: Operation[];
|
|
101
114
|
}
|
|
102
115
|
export type BatchInputPatchRequest<F extends FhirResource> = BatchInputBinaryPatchRequest<F> | BatchInputJSONPatchRequest;
|
|
103
|
-
export type BatchInputRequest<F extends FhirResource> = BatchInputGetRequest | BatchInputHeadRequest | BatchInputPutRequest<F> | BatchInputPatchRequest<F> | BatchInputPostRequest<F> | BatchInputDeleteRequest;
|
|
116
|
+
export type BatchInputRequest<F extends FhirResource> = BatchInputGetRequest | BatchInputHeadRequest | BatchInputPutRequest<F> | BatchInputPatchRequest<F> | BatchInputPostRequest<F> | BatchInputSearchPostRequest | BatchInputDeleteRequest;
|
|
104
117
|
export interface BatchInput<F extends FhirResource> {
|
|
105
118
|
requests: BatchInputRequest<F>[];
|
|
106
119
|
}
|
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { Coding } from './resources/types/fhir';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Configuration for the Oystehr SDK client
|
|
3
5
|
*/
|
|
@@ -62,6 +64,20 @@ export interface OystehrConfig {
|
|
|
62
64
|
*/
|
|
63
65
|
retryOn?: number[];
|
|
64
66
|
};
|
|
67
|
+
/**
|
|
68
|
+
* Optional workspace tag configuration. When set, all FHIR searches will be
|
|
69
|
+
* filtered to only return resources with this tag, and all FHIR mutations
|
|
70
|
+
* (create, update, patch) will have this tag injected automatically.
|
|
71
|
+
* Mutually exclusive with `ignoreTags`.
|
|
72
|
+
*/
|
|
73
|
+
workspaceTag?: Coding;
|
|
74
|
+
/**
|
|
75
|
+
* Optional list of tags to ignore. When set, all FHIR searches will be
|
|
76
|
+
* filtered to exclude resources matching any of these tags, and mutations
|
|
77
|
+
* will throw an error if the resource carries any of these tags.
|
|
78
|
+
* Mutually exclusive with `workspaceTag`.
|
|
79
|
+
*/
|
|
80
|
+
ignoreTags?: Coding[];
|
|
65
81
|
}
|
|
66
82
|
|
|
67
83
|
let globalConfig: OystehrConfig;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Operation } from 'fast-json-patch';
|
|
1
2
|
import { Address as AddressR4B, HumanName as HumanNameR4B } from 'fhir/r4b';
|
|
2
3
|
import { Address as AddressR5, HumanName as HumanNameR5 } from 'fhir/r5';
|
|
3
4
|
import {
|
|
@@ -7,6 +8,7 @@ import {
|
|
|
7
8
|
Binary,
|
|
8
9
|
Bundle,
|
|
9
10
|
BundleEntry,
|
|
11
|
+
Coding,
|
|
10
12
|
FhirBundle,
|
|
11
13
|
FhirCreateParams,
|
|
12
14
|
FhirDeleteParams,
|
|
@@ -18,10 +20,15 @@ import {
|
|
|
18
20
|
FhirResourceReturnValue,
|
|
19
21
|
FhirSearchParams,
|
|
20
22
|
FhirUpdateParams,
|
|
23
|
+
GenerateFriendlyPatientIdParams,
|
|
21
24
|
OperationOutcome,
|
|
25
|
+
Resource,
|
|
26
|
+
SearchParam,
|
|
22
27
|
TransactionBundle,
|
|
23
28
|
} from '../..';
|
|
24
29
|
import { addParamsToSearch, FhirFetcherResponse, OystehrClientRequest, SDKResource } from '../../client/client';
|
|
30
|
+
import { OystehrConfig } from '../../config';
|
|
31
|
+
import { OystehrFHIRError, OystehrSdkError } from '../../errors';
|
|
25
32
|
|
|
26
33
|
// Code adapted from https://github.com/sindresorhus/uint8array-extras
|
|
27
34
|
const MAX_BLOCK_SIZE = 65_535;
|
|
@@ -37,6 +44,209 @@ function stringToBase64(input: string): string {
|
|
|
37
44
|
return base64;
|
|
38
45
|
}
|
|
39
46
|
|
|
47
|
+
function base64ToString(input: string): string {
|
|
48
|
+
const binaryString = globalThis.atob(input);
|
|
49
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
50
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
51
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
52
|
+
}
|
|
53
|
+
return new globalThis.TextDecoder().decode(bytes);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ─── Tag-mode helpers ────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
/** Converts a Coding to the FHIR _tag parameter value format "system|code". */
|
|
59
|
+
function codingToTagValue(coding: Coding): string {
|
|
60
|
+
return coding.system ? `${coding.system}|${coding.code ?? ''}` : coding.code ?? '';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Returns true if two Codings match on system (treating absent as empty string) and code. */
|
|
64
|
+
function codingMatches(a: Coding, b: Coding): boolean {
|
|
65
|
+
return (a.system ?? '') === (b.system ?? '') && (a.code ?? '') === (b.code ?? '');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Appends _tag (tag-workspace mode) or _tag:not (ignore-tags mode) search
|
|
70
|
+
* params to an existing SearchParam array and returns the new array.
|
|
71
|
+
*/
|
|
72
|
+
function applyTagSearchParams(config: OystehrConfig, params: SearchParam[] | undefined): SearchParam[] {
|
|
73
|
+
const out: SearchParam[] = params ? [...params] : [];
|
|
74
|
+
if (config.workspaceTag) {
|
|
75
|
+
out.push({ name: '_tag', value: codingToTagValue(config.workspaceTag) });
|
|
76
|
+
} else if (config.ignoreTags?.length) {
|
|
77
|
+
for (const tag of config.ignoreTags) {
|
|
78
|
+
out.push({ name: '_tag:not', value: codingToTagValue(tag) });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* In ignore-tags mode: throws OystehrSdkError if the resource carries any ignored tag.
|
|
86
|
+
* In tag-workspace mode: injects the workspace tag into resource.meta.tag if not already present.
|
|
87
|
+
* Returns the (possibly cloned) resource.
|
|
88
|
+
*/
|
|
89
|
+
function applyTagToResource<T extends FhirResource>(config: OystehrConfig, resource: T): T {
|
|
90
|
+
const resourceLike = resource as unknown as Resource;
|
|
91
|
+
|
|
92
|
+
if (config.ignoreTags?.length) {
|
|
93
|
+
const resourceTags = (resourceLike.meta?.tag ?? []) as Coding[];
|
|
94
|
+
for (const ignored of config.ignoreTags) {
|
|
95
|
+
if (resourceTags.some((t) => codingMatches(t, ignored))) {
|
|
96
|
+
throw new OystehrSdkError({
|
|
97
|
+
message: `Resource has an ignored tag (system: "${ignored.system ?? ''}", code: "${
|
|
98
|
+
ignored.code ?? ''
|
|
99
|
+
}") and cannot be mutated in ignoreTags mode`,
|
|
100
|
+
code: 400,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return resource;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (config.workspaceTag) {
|
|
108
|
+
const tag = config.workspaceTag;
|
|
109
|
+
const existingTags = (resourceLike.meta?.tag ?? []) as Coding[];
|
|
110
|
+
if (!existingTags.some((t) => codingMatches(t, tag))) {
|
|
111
|
+
return {
|
|
112
|
+
...resource,
|
|
113
|
+
meta: {
|
|
114
|
+
...resourceLike.meta,
|
|
115
|
+
tag: [...existingTags, tag],
|
|
116
|
+
},
|
|
117
|
+
} as T;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return resource;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* In ignore-tags mode: throws OystehrSdkError if any operation value resembles an ignored tag.
|
|
126
|
+
* In tag-workspace mode: guards against removal or loss of the workspace tag:
|
|
127
|
+
* - add/replace of the tag array: ensures workspace tag is present in the value
|
|
128
|
+
* - add/replace of meta: ensures workspace tag is present in meta.tag
|
|
129
|
+
* - remove of /meta/tag or /meta: keeps the remove and appends an operation to restore the workspace tag
|
|
130
|
+
* - add of a single tag (path ends in /-): left unchanged (workspace tag already on the resource)
|
|
131
|
+
*/
|
|
132
|
+
function applyTagToPatchOperations(config: OystehrConfig, operations: Operation[]): Operation[] {
|
|
133
|
+
if (config.ignoreTags?.length) {
|
|
134
|
+
for (const op of operations) {
|
|
135
|
+
if (op.op === 'add' || op.op === 'replace' || op.op === 'test') {
|
|
136
|
+
const opValue: unknown = op.value;
|
|
137
|
+
if (opValue !== null && opValue !== undefined && typeof opValue === 'object') {
|
|
138
|
+
const v = opValue as Coding;
|
|
139
|
+
if (v.code !== undefined) {
|
|
140
|
+
for (const ignored of config.ignoreTags) {
|
|
141
|
+
if (codingMatches(v, ignored)) {
|
|
142
|
+
throw new OystehrSdkError({
|
|
143
|
+
message: `Patch operation contains an ignored tag (system: "${ignored.system ?? ''}", code: "${
|
|
144
|
+
ignored.code ?? ''
|
|
145
|
+
}") and cannot be applied in ignoreTags mode`,
|
|
146
|
+
code: 400,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return operations;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (config.workspaceTag) {
|
|
158
|
+
const tag = config.workspaceTag;
|
|
159
|
+
return operations.reduce<Operation[]>((result, op) => {
|
|
160
|
+
// remove /meta/tag — restore workspace tag afterwards
|
|
161
|
+
if (op.op === 'remove' && op.path === '/meta/tag') {
|
|
162
|
+
result.push(op);
|
|
163
|
+
result.push({ op: 'add' as const, path: '/meta/tag', value: [tag] });
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// remove /meta — restore workspace tag afterwards
|
|
168
|
+
if (op.op === 'remove' && op.path === '/meta') {
|
|
169
|
+
result.push(op);
|
|
170
|
+
result.push({ op: 'add' as const, path: '/meta', value: { tag: [tag] } });
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// add/replace the entire tag array — ensure workspace tag is present
|
|
175
|
+
if ((op.op === 'add' || op.op === 'replace') && op.path === '/meta/tag') {
|
|
176
|
+
const tags: Coding[] = Array.isArray(op.value) ? (op.value as Coding[]) : [];
|
|
177
|
+
if (!tags.some((t) => codingMatches(t, tag))) {
|
|
178
|
+
result.push({ ...op, value: [...tags, tag] });
|
|
179
|
+
} else {
|
|
180
|
+
result.push(op);
|
|
181
|
+
}
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// add/replace the entire meta object — ensure workspace tag is present in meta.tag
|
|
186
|
+
if ((op.op === 'add' || op.op === 'replace') && op.path === '/meta') {
|
|
187
|
+
const metaValue: { tag?: Coding[] } =
|
|
188
|
+
op.value != null && typeof op.value === 'object' ? (op.value as { tag?: Coding[] }) : {};
|
|
189
|
+
const tags = metaValue.tag ?? [];
|
|
190
|
+
if (!tags.some((t) => codingMatches(t, tag))) {
|
|
191
|
+
result.push({ ...op, value: { ...metaValue, tag: [...tags, tag] } });
|
|
192
|
+
} else {
|
|
193
|
+
result.push(op);
|
|
194
|
+
}
|
|
195
|
+
return result;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// All other operations (including add /meta/tag/- for individual tags) pass through unchanged.
|
|
199
|
+
// Workspace tag is assumed already present on the resource from create/update.
|
|
200
|
+
result.push(op);
|
|
201
|
+
return result;
|
|
202
|
+
}, []);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return operations;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Throws OystehrFHIRError (404) if the retrieved resource violates the tag configuration:
|
|
210
|
+
* - workspaceTag mode: resource must carry the workspace tag
|
|
211
|
+
* - ignoreTags mode: resource must not carry any ignored tag
|
|
212
|
+
*/
|
|
213
|
+
function assertRetrievedResource(config: OystehrConfig, resource: FhirResource): void {
|
|
214
|
+
const resourceLike = resource as unknown as Resource;
|
|
215
|
+
const resourceTags = (resourceLike.meta?.tag ?? []) as Coding[];
|
|
216
|
+
|
|
217
|
+
if (config.workspaceTag) {
|
|
218
|
+
const tag = config.workspaceTag;
|
|
219
|
+
if (!resourceTags.some((t) => codingMatches(t, tag))) {
|
|
220
|
+
throw new OystehrFHIRError({
|
|
221
|
+
error: {
|
|
222
|
+
resourceType: 'OperationOutcome',
|
|
223
|
+
id: 'not-found',
|
|
224
|
+
issue: [{ severity: 'error', code: 'not-found', details: { text: 'Not found' } }],
|
|
225
|
+
},
|
|
226
|
+
code: 404,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (config.ignoreTags?.length) {
|
|
233
|
+
for (const ignored of config.ignoreTags) {
|
|
234
|
+
if (resourceTags.some((t) => codingMatches(t, ignored))) {
|
|
235
|
+
throw new OystehrFHIRError({
|
|
236
|
+
error: {
|
|
237
|
+
resourceType: 'OperationOutcome',
|
|
238
|
+
id: 'not-found',
|
|
239
|
+
issue: [{ severity: 'error', code: 'not-found', details: { text: 'Not found' } }],
|
|
240
|
+
},
|
|
241
|
+
code: 404,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
249
|
+
|
|
40
250
|
/**
|
|
41
251
|
* Optional parameter that can be passed to the client methods. It allows
|
|
42
252
|
* overriding the access token or project ID, and setting various headers,
|
|
@@ -64,10 +274,11 @@ export async function search<T extends FhirResource>(
|
|
|
64
274
|
params: FhirSearchParams<T>,
|
|
65
275
|
request?: OystehrClientRequest
|
|
66
276
|
): Promise<FhirFetcherResponse<Bundle<T>>> {
|
|
67
|
-
const { resourceType
|
|
277
|
+
const { resourceType } = params;
|
|
278
|
+
const taggedParams = applyTagSearchParams(this.config, params.params);
|
|
68
279
|
let paramMap: Record<string, (string | number)[]> | undefined;
|
|
69
|
-
if (
|
|
70
|
-
paramMap =
|
|
280
|
+
if (taggedParams.length) {
|
|
281
|
+
paramMap = taggedParams.reduce((acc, param) => {
|
|
71
282
|
if (!acc[param.name]) {
|
|
72
283
|
acc[param.name] = [];
|
|
73
284
|
}
|
|
@@ -96,8 +307,9 @@ export async function create<T extends FhirResource>(
|
|
|
96
307
|
params: FhirCreateParams<T>,
|
|
97
308
|
request?: OystehrClientRequest
|
|
98
309
|
): Promise<FhirFetcherResponse<FhirResourceReturnValue<T>>> {
|
|
99
|
-
const
|
|
100
|
-
|
|
310
|
+
const tagged = applyTagToResource(this.config, params);
|
|
311
|
+
const { resourceType } = tagged;
|
|
312
|
+
return this.fhirRequest(`/${resourceType}`, 'POST')(tagged as unknown as Record<string, unknown>, request);
|
|
101
313
|
}
|
|
102
314
|
|
|
103
315
|
export async function get<T extends FhirResource>(
|
|
@@ -105,7 +317,9 @@ export async function get<T extends FhirResource>(
|
|
|
105
317
|
{ resourceType, id }: FhirGetParams<T>,
|
|
106
318
|
request?: OystehrClientRequest
|
|
107
319
|
): Promise<FhirFetcherResponse<FhirResourceReturnValue<T>>> {
|
|
108
|
-
|
|
320
|
+
const result = await this.fhirRequest<FhirResourceReturnValue<T>>(`/${resourceType}/${id}`, 'GET')({}, request);
|
|
321
|
+
assertRetrievedResource(this.config, result);
|
|
322
|
+
return result;
|
|
109
323
|
}
|
|
110
324
|
|
|
111
325
|
export async function update<T extends FhirResource>(
|
|
@@ -113,8 +327,9 @@ export async function update<T extends FhirResource>(
|
|
|
113
327
|
params: FhirUpdateParams<T>,
|
|
114
328
|
request?: OystehrFHIRUpdateClientRequest
|
|
115
329
|
): Promise<FhirFetcherResponse<FhirResourceReturnValue<T>>> {
|
|
116
|
-
const
|
|
117
|
-
|
|
330
|
+
const tagged = applyTagToResource(this.config, params);
|
|
331
|
+
const { id, resourceType } = tagged;
|
|
332
|
+
return this.fhirRequest(`/${resourceType}/${id}`, 'PUT')(tagged as unknown as Record<string, unknown>, {
|
|
118
333
|
...request,
|
|
119
334
|
ifMatch: request?.optimisticLockingVersionId ? `W/"${request.optimisticLockingVersionId}"` : undefined,
|
|
120
335
|
});
|
|
@@ -125,7 +340,8 @@ export async function patch<T extends FhirResource>(
|
|
|
125
340
|
{ resourceType, id, operations }: FhirPatchParams<T>,
|
|
126
341
|
request?: OystehrFHIRUpdateClientRequest
|
|
127
342
|
): Promise<FhirFetcherResponse<FhirResourceReturnValue<T>>> {
|
|
128
|
-
|
|
343
|
+
const taggedOperations = applyTagToPatchOperations(this.config, operations);
|
|
344
|
+
return this.fhirRequest(`/${resourceType}/${id}`, 'PATCH')(taggedOperations, {
|
|
129
345
|
...request,
|
|
130
346
|
contentType: 'application/json-patch+json',
|
|
131
347
|
ifMatch: request?.optimisticLockingVersionId ? `W/"${request.optimisticLockingVersionId}"` : undefined,
|
|
@@ -180,10 +396,50 @@ export async function history<T extends FhirResource>(
|
|
|
180
396
|
return this.fhirRequest(`/${resourceType}/${id}/_history?_total=accurate`, 'GET')({}, request);
|
|
181
397
|
}
|
|
182
398
|
|
|
399
|
+
/**
|
|
400
|
+
* Returns true when a batch GET/HEAD URL is a search (e.g. "Patient" or "Patient?name=foo")
|
|
401
|
+
* rather than a direct retrieval (e.g. "Patient/abc-123").
|
|
402
|
+
* A retrieval URL has a path segment after the resource type that is not a FHIR operation
|
|
403
|
+
* (operations start with "_", e.g. "_search" or "_history").
|
|
404
|
+
*/
|
|
405
|
+
function isBatchSearchUrl(url: string): boolean {
|
|
406
|
+
const path = url.split('?')[0];
|
|
407
|
+
const segments = path.split('/').filter(Boolean);
|
|
408
|
+
// Only one segment → bare resource type, always a search
|
|
409
|
+
if (segments.length <= 1) return true;
|
|
410
|
+
// Two or more segments: second segment is an ID if it doesn't start with '_'
|
|
411
|
+
return segments[1].startsWith('_');
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/** Returns true when a batch POST URL targets a `_search` endpoint (e.g. "Patient/_search"). */
|
|
415
|
+
function isBatchSearchPostUrl(url: string): boolean {
|
|
416
|
+
return url.split('?')[0].endsWith('/_search');
|
|
417
|
+
}
|
|
418
|
+
|
|
183
419
|
function batchInputRequestToBundleEntryItem<T extends FhirResource>(
|
|
184
|
-
request: BatchInputRequest<T
|
|
420
|
+
request: BatchInputRequest<T>,
|
|
421
|
+
config: OystehrConfig
|
|
185
422
|
): BundleEntry<T | Binary<T>> {
|
|
186
|
-
const { method
|
|
423
|
+
const { method } = request;
|
|
424
|
+
let url = request.url;
|
|
425
|
+
|
|
426
|
+
// Inject tag search params into search request URLs before URL encoding.
|
|
427
|
+
// GET/HEAD: only for search URLs (not retrievals like Patient/<id>).
|
|
428
|
+
// POST: only for _search endpoints (not creates).
|
|
429
|
+
if (
|
|
430
|
+
((method === 'GET' || method === 'HEAD') && isBatchSearchUrl(url)) ||
|
|
431
|
+
(method === 'POST' && isBatchSearchPostUrl(url))
|
|
432
|
+
) {
|
|
433
|
+
if (config.workspaceTag) {
|
|
434
|
+
url += (url.includes('?') ? '&' : '?') + `_tag=${codingToTagValue(config.workspaceTag)}`;
|
|
435
|
+
}
|
|
436
|
+
if (config.ignoreTags?.length) {
|
|
437
|
+
for (const tag of config.ignoreTags) {
|
|
438
|
+
url += (url.includes('?') ? '&' : '?') + `_tag:not=${codingToTagValue(tag)}`;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
187
443
|
const baseRequest = {
|
|
188
444
|
request: {
|
|
189
445
|
method,
|
|
@@ -222,7 +478,7 @@ function batchInputRequestToBundleEntryItem<T extends FhirResource>(
|
|
|
222
478
|
|
|
223
479
|
// PUT updates require a full resource
|
|
224
480
|
if (method === 'PUT') {
|
|
225
|
-
const
|
|
481
|
+
const resource = applyTagToResource(config, request.resource);
|
|
226
482
|
return {
|
|
227
483
|
request: {
|
|
228
484
|
...baseRequest.request,
|
|
@@ -235,15 +491,24 @@ function batchInputRequestToBundleEntryItem<T extends FhirResource>(
|
|
|
235
491
|
// PATCH can be Binary resource or JSON patch
|
|
236
492
|
if (method === 'PATCH') {
|
|
237
493
|
if ('resource' in request) {
|
|
494
|
+
// Binary patch — decode operations, apply tag transforms, re-encode
|
|
495
|
+
let patchResource = request.resource;
|
|
496
|
+
const binaryData = patchResource.data;
|
|
497
|
+
if (binaryData) {
|
|
498
|
+
const operations = JSON.parse(base64ToString(binaryData)) as Operation[];
|
|
499
|
+
const taggedOperations = applyTagToPatchOperations(config, operations);
|
|
500
|
+
patchResource = { ...patchResource, data: stringToBase64(JSON.stringify(taggedOperations)) } as Binary<T>;
|
|
501
|
+
}
|
|
238
502
|
return {
|
|
239
503
|
request: {
|
|
240
504
|
...baseRequest.request,
|
|
241
505
|
ifMatch: request.ifMatch,
|
|
242
506
|
},
|
|
243
|
-
resource:
|
|
507
|
+
resource: patchResource,
|
|
244
508
|
} as BundleEntry<Binary<T>>;
|
|
245
509
|
}
|
|
246
|
-
const
|
|
510
|
+
const operations = applyTagToPatchOperations(config, request.operations);
|
|
511
|
+
const data = stringToBase64(JSON.stringify(operations));
|
|
247
512
|
return {
|
|
248
513
|
...baseRequest,
|
|
249
514
|
resource: {
|
|
@@ -254,9 +519,15 @@ function batchInputRequestToBundleEntryItem<T extends FhirResource>(
|
|
|
254
519
|
} as BundleEntry<Binary<T>>;
|
|
255
520
|
}
|
|
256
521
|
|
|
522
|
+
// POST _search — no resource body; tag params were already injected into the URL above
|
|
523
|
+
if (method === 'POST' && isBatchSearchPostUrl(url)) {
|
|
524
|
+
return baseRequest as BundleEntry<T>;
|
|
525
|
+
}
|
|
526
|
+
|
|
257
527
|
// POST creates require a full resource
|
|
258
|
-
if (method === 'POST') {
|
|
259
|
-
const
|
|
528
|
+
if (method === 'POST' && 'resource' in request) {
|
|
529
|
+
const resource = applyTagToResource(config, request.resource);
|
|
530
|
+
const { fullUrl } = request;
|
|
260
531
|
return {
|
|
261
532
|
...baseRequest,
|
|
262
533
|
resource: resource as T,
|
|
@@ -284,13 +555,35 @@ export async function batch<BundleContentType extends FhirResource>(
|
|
|
284
555
|
{
|
|
285
556
|
resourceType: 'Bundle',
|
|
286
557
|
type: 'batch',
|
|
287
|
-
entry: input.requests.map(batchInputRequestToBundleEntryItem),
|
|
558
|
+
entry: input.requests.map((req) => batchInputRequestToBundleEntryItem(req, this.config)),
|
|
288
559
|
},
|
|
289
560
|
request
|
|
290
561
|
);
|
|
562
|
+
// Validate each GET/HEAD retrieval entry against the tag config.
|
|
563
|
+
// Violations are replaced with a synthetic 404 OperationOutcome entry; batch entries are independent.
|
|
564
|
+
const rawEntries = resp.entry as Array<BundleEntry<BundleContentType>> | undefined;
|
|
565
|
+
const processedEntries: Array<BundleEntry<BundleContentType>> | undefined =
|
|
566
|
+
this.config.workspaceTag || this.config.ignoreTags?.length
|
|
567
|
+
? rawEntries?.map((entry, i) => {
|
|
568
|
+
const req = input.requests[i];
|
|
569
|
+
if (!req || !entry?.resource) return entry;
|
|
570
|
+
if ((req.method === 'GET' || req.method === 'HEAD') && !isBatchSearchUrl(req.url)) {
|
|
571
|
+
try {
|
|
572
|
+
assertRetrievedResource(this.config, entry.resource);
|
|
573
|
+
} catch (err) {
|
|
574
|
+
if (!(err instanceof OystehrFHIRError)) throw err;
|
|
575
|
+
return {
|
|
576
|
+
request: entry.request,
|
|
577
|
+
response: { status: '404', outcome: err.cause },
|
|
578
|
+
} as unknown as BundleEntry<BundleContentType>;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return entry;
|
|
582
|
+
})
|
|
583
|
+
: rawEntries;
|
|
291
584
|
const bundle: BatchBundle<BundleContentType> = {
|
|
292
585
|
...resp,
|
|
293
|
-
entry:
|
|
586
|
+
entry: processedEntries,
|
|
294
587
|
unbundle: function (this: { entry?: Array<BundleEntry<BundleContentType>> | undefined }) {
|
|
295
588
|
return (
|
|
296
589
|
this.entry
|
|
@@ -317,10 +610,21 @@ export async function transaction<BundleContentType extends FhirResource>(
|
|
|
317
610
|
{
|
|
318
611
|
resourceType: 'Bundle',
|
|
319
612
|
type: 'transaction',
|
|
320
|
-
entry: input.requests.map(batchInputRequestToBundleEntryItem),
|
|
613
|
+
entry: input.requests.map((req) => batchInputRequestToBundleEntryItem(req, this.config)),
|
|
321
614
|
},
|
|
322
615
|
request
|
|
323
616
|
);
|
|
617
|
+
// Validate each GET/HEAD retrieval entry against the tag config.
|
|
618
|
+
// A violation throws OystehrFHIRError(404) — transactions are all-or-nothing.
|
|
619
|
+
if (this.config.workspaceTag || this.config.ignoreTags?.length) {
|
|
620
|
+
(resp.entry as Array<BundleEntry<BundleContentType>> | undefined)?.forEach((entry, i) => {
|
|
621
|
+
const req = input.requests[i];
|
|
622
|
+
if (!req || !entry?.resource) return;
|
|
623
|
+
if ((req.method === 'GET' || req.method === 'HEAD') && !isBatchSearchUrl(req.url)) {
|
|
624
|
+
assertRetrievedResource(this.config, entry.resource);
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
}
|
|
324
628
|
const bundle: TransactionBundle<BundleContentType> = {
|
|
325
629
|
...resp,
|
|
326
630
|
entry: resp.entry as Array<BundleEntry<BundleContentType>> | undefined,
|
|
@@ -335,6 +639,14 @@ export async function transaction<BundleContentType extends FhirResource>(
|
|
|
335
639
|
return bundle;
|
|
336
640
|
}
|
|
337
641
|
|
|
642
|
+
export async function generateFriendlyPatientId(
|
|
643
|
+
this: SDKResource,
|
|
644
|
+
{ id }: GenerateFriendlyPatientIdParams,
|
|
645
|
+
request?: OystehrClientRequest
|
|
646
|
+
): Promise<FhirFetcherResponse<FhirResource>> {
|
|
647
|
+
return this.fhirRequest(`/Patient/${id}/$generate-friendly-patient-id`, 'POST')({}, request);
|
|
648
|
+
}
|
|
649
|
+
|
|
338
650
|
export function formatAddress(
|
|
339
651
|
address: AddressR4B | AddressR5,
|
|
340
652
|
options?: { all?: boolean; use?: boolean; lineSeparator?: string }
|
|
@@ -26,6 +26,7 @@ export class Fhir extends SDKResource {
|
|
|
26
26
|
history = ext.history;
|
|
27
27
|
batch = ext.batch;
|
|
28
28
|
transaction = ext.transaction;
|
|
29
|
+
generateFriendlyPatientId = ext.generateFriendlyPatientId;
|
|
29
30
|
formatAddress = ext.formatAddress;
|
|
30
31
|
formatHumanName = ext.formatHumanName;
|
|
31
32
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// AUTOGENERATED -- DO NOT EDIT
|
|
2
2
|
|
|
3
3
|
import { OystehrConfig } from '../../config';
|
|
4
|
+
import { OystehrSdkError } from '../../errors';
|
|
4
5
|
import { Application } from './application';
|
|
5
6
|
import { Charge } from './charge';
|
|
6
7
|
import { Conversation } from './conversation';
|
|
@@ -51,6 +52,12 @@ export class Oystehr {
|
|
|
51
52
|
readonly rcm: Rcm;
|
|
52
53
|
readonly fhir: Fhir;
|
|
53
54
|
constructor(config: OystehrConfig) {
|
|
55
|
+
if (config.workspaceTag && config.ignoreTags) {
|
|
56
|
+
throw new OystehrSdkError({
|
|
57
|
+
message: 'workspaceTag and ignoreTags are mutually exclusive and cannot both be set in config',
|
|
58
|
+
code: 400,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
54
61
|
this.config = config;
|
|
55
62
|
this.config.services ??= {};
|
|
56
63
|
this.config.services['projectApiUrl'] ??= config.projectApiUrl;
|