@smile-cdr/fhirts 2.1.0 → 2.1.2
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/CHANGELOG.md +12 -0
- package/GETTINGSTARTED.md +4 -4
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -1
- package/dist/library/QueryBuilder/QueryBuilder.d.ts +59 -0
- package/dist/library/QueryBuilder/QueryBuilder.js +112 -0
- package/dist/library/QueryBuilder/QueryBuilder.spec.d.ts +1 -0
- package/dist/library/QueryBuilder/QueryBuilder.spec.js +135 -0
- package/dist/library/ResourceUtils/ResourceUtils.js +6 -1
- package/dist/library/ResourceUtils/ResourceUtils.spec.js +9 -0
- package/dist/library/constants.d.ts +4 -0
- package/dist/library/constants.js +8 -0
- package/package.json +4 -6
- package/src/index.ts +2 -1
- package/src/library/QueryBuilder/QueryBuilder.spec.ts +149 -0
- package/src/library/QueryBuilder/QueryBuilder.ts +119 -0
- package/src/library/ResourceUtils/ResourceUtils.spec.ts +10 -0
- package/src/library/ResourceUtils/ResourceUtils.ts +6 -2
- package/src/library/constants.ts +4 -0
- package/src/test-resources/Patient-R4.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.2.2
|
|
4
|
+
|
|
5
|
+
* Add `QueryBuilder` for ease of generating queries.
|
|
6
|
+
|
|
7
|
+
## 2.1.1
|
|
8
|
+
|
|
9
|
+
* Fix issue with `BundleUtils.getResourceFromBundle` not being able get the values inside nested array under array of objects.
|
|
10
|
+
|
|
11
|
+
## 2.1.0
|
|
12
|
+
|
|
13
|
+
* Remove static classes for `BundleUtilities` & `ResourceUtilities` to non static classes `BundleUtils` & `ResourceUtils.`
|
|
14
|
+
|
|
3
15
|
## 2.0.7
|
|
4
16
|
|
|
5
17
|
### Updates (R4)
|
package/GETTINGSTARTED.md
CHANGED
|
@@ -97,7 +97,7 @@ If you try writing this out you will see that the variable `resource` inside the
|
|
|
97
97
|
### `v2.1.0`
|
|
98
98
|
#### BundleUtils usage
|
|
99
99
|
```js
|
|
100
|
-
import { BundleUtils } from '@
|
|
100
|
+
import { BundleUtils } from '@smile-cdr/fhirts';
|
|
101
101
|
|
|
102
102
|
const bundleUtils = new BundleUtils();
|
|
103
103
|
// returns arrayof Claim resources from Bundle.entry
|
|
@@ -108,7 +108,7 @@ const resource = bundleUtils.getResourceFromBundle(Bundle.entry, '123');
|
|
|
108
108
|
|
|
109
109
|
#### ResourceUtils usage
|
|
110
110
|
```js
|
|
111
|
-
import { ResourceUtils } from '@
|
|
111
|
+
import { ResourceUtils } from '@smile-cdr/fhirts';
|
|
112
112
|
const resourceUtils = new ResourceUtils();
|
|
113
113
|
// returns deserialized Patient resource
|
|
114
114
|
const deserializedPatientResource = resourceUtils.deserializeResource(jsonPatientPayload, new Patient());
|
|
@@ -136,7 +136,7 @@ const references = resourceUtils.getAllReferencesFromResource(resourcePayload);
|
|
|
136
136
|
### `v2.0.0`
|
|
137
137
|
#### BundleUtilities usage
|
|
138
138
|
```js
|
|
139
|
-
import { BundleUtilities } from '@
|
|
139
|
+
import { BundleUtilities } from '@smile-cdr/fhirts';
|
|
140
140
|
|
|
141
141
|
// returns arrayof Claim resources from Bundle.entry
|
|
142
142
|
const claimsList = BundleUtilities.getResourcesFromBundle(Bundle.entry, 'Claim');
|
|
@@ -144,7 +144,7 @@ const claimsList = BundleUtilities.getResourcesFromBundle(Bundle.entry, 'Claim')
|
|
|
144
144
|
|
|
145
145
|
#### ResourceUtilities usage
|
|
146
146
|
```js
|
|
147
|
-
import { ResourceUtilities } from '@
|
|
147
|
+
import { ResourceUtilities } from '@smile-cdr/fhirts';
|
|
148
148
|
|
|
149
149
|
// returns deserialized Patient resource
|
|
150
150
|
const deserializedPatientResource = ResourceUtilities.deserializeResource(jsonPatientPayload, new Patient());
|
package/dist/index.d.ts
CHANGED
|
@@ -4,4 +4,5 @@ import * as fhirR3 from './FHIR-R3';
|
|
|
4
4
|
import * as dstu2 from './FHIR-DSTU2';
|
|
5
5
|
import { ResourceUtils } from './library/ResourceUtils/ResourceUtils';
|
|
6
6
|
import { BundleUtils } from './library/BundleUtils/BundleUtils';
|
|
7
|
-
|
|
7
|
+
import { QueryBuilder } from './library/QueryBuilder/QueryBuilder';
|
|
8
|
+
export { fhirR4, fhirR3, IfhirR4, dstu2, ResourceUtils, BundleUtils, QueryBuilder };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.BundleUtils = exports.ResourceUtils = exports.dstu2 = exports.IfhirR4 = exports.fhirR3 = exports.fhirR4 = void 0;
|
|
3
|
+
exports.QueryBuilder = exports.BundleUtils = exports.ResourceUtils = exports.dstu2 = exports.IfhirR4 = exports.fhirR3 = exports.fhirR4 = void 0;
|
|
4
4
|
const fhirR4 = require("./FHIR-R4/classes/models-r4");
|
|
5
5
|
exports.fhirR4 = fhirR4;
|
|
6
6
|
const IfhirR4 = require("./FHIR-R4/interfaces/IModel");
|
|
@@ -13,3 +13,5 @@ const ResourceUtils_1 = require("./library/ResourceUtils/ResourceUtils");
|
|
|
13
13
|
Object.defineProperty(exports, "ResourceUtils", { enumerable: true, get: function () { return ResourceUtils_1.ResourceUtils; } });
|
|
14
14
|
const BundleUtils_1 = require("./library/BundleUtils/BundleUtils");
|
|
15
15
|
Object.defineProperty(exports, "BundleUtils", { enumerable: true, get: function () { return BundleUtils_1.BundleUtils; } });
|
|
16
|
+
const QueryBuilder_1 = require("./library/QueryBuilder/QueryBuilder");
|
|
17
|
+
Object.defineProperty(exports, "QueryBuilder", { enumerable: true, get: function () { return QueryBuilder_1.QueryBuilder; } });
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { SORT_ORDER } from "../constants";
|
|
2
|
+
export declare class QueryBuilder {
|
|
3
|
+
readonly REV_INCLUDE_KEYWORD = "_revinclude";
|
|
4
|
+
readonly INCLUDE_KEYWORD = "_include";
|
|
5
|
+
readonly SORT_KEYWORD = "_sort";
|
|
6
|
+
readonly WILDCARD_ASTERIK = "*";
|
|
7
|
+
readonly EQUALS = "=";
|
|
8
|
+
readonly COLON = ":";
|
|
9
|
+
readonly QUERY_DELIMETER = "&";
|
|
10
|
+
readonly COMMA = ",";
|
|
11
|
+
private baseResource;
|
|
12
|
+
private singularQueries;
|
|
13
|
+
private sortQueries;
|
|
14
|
+
/**
|
|
15
|
+
* @returns base resource for the query
|
|
16
|
+
*/
|
|
17
|
+
getBaseResource(): String;
|
|
18
|
+
/**
|
|
19
|
+
* @param resourceType Base ResourceType for the query
|
|
20
|
+
* sets base resource type for the query
|
|
21
|
+
* i.e. which resource the query will performed for
|
|
22
|
+
*/
|
|
23
|
+
setBaseResource(resourceType: String): this;
|
|
24
|
+
/**
|
|
25
|
+
* @param searchParameter SearchParameter reference to targeted resource
|
|
26
|
+
*/
|
|
27
|
+
include(searchParameter: String): this;
|
|
28
|
+
/**
|
|
29
|
+
* @param resourceType Source ResourceType
|
|
30
|
+
* @param searchParameter SearchParameter reference from target resource to baseResource
|
|
31
|
+
*/
|
|
32
|
+
revinclude(resourceType: String, searchParameter: String): this;
|
|
33
|
+
/**
|
|
34
|
+
* _include all references in query
|
|
35
|
+
*/
|
|
36
|
+
includeAll(): this;
|
|
37
|
+
/**
|
|
38
|
+
* _revinclude all references in query
|
|
39
|
+
*/
|
|
40
|
+
revincludeAll(): this;
|
|
41
|
+
/**
|
|
42
|
+
* @param searchParameter search parameter for the element to sort on
|
|
43
|
+
* @param sortOrder ASCENDING or DESCENDING
|
|
44
|
+
*/
|
|
45
|
+
sort(searchParameter: String, sortOrder: SORT_ORDER): this;
|
|
46
|
+
/**
|
|
47
|
+
* Resets queries to it empty state
|
|
48
|
+
*/
|
|
49
|
+
resetQuery(): this;
|
|
50
|
+
/**
|
|
51
|
+
* @returns complete generated query with encoded parameter values
|
|
52
|
+
*/
|
|
53
|
+
getCompleteUrl(): String;
|
|
54
|
+
/**
|
|
55
|
+
* @returns complete generated query with decoded parameter values
|
|
56
|
+
*/
|
|
57
|
+
getCompleteUrlDecoded(): String;
|
|
58
|
+
private createSortQuery;
|
|
59
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.QueryBuilder = void 0;
|
|
4
|
+
const constants_1 = require("../constants");
|
|
5
|
+
class QueryBuilder {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.REV_INCLUDE_KEYWORD = "_revinclude";
|
|
8
|
+
this.INCLUDE_KEYWORD = "_include";
|
|
9
|
+
this.SORT_KEYWORD = "_sort";
|
|
10
|
+
this.WILDCARD_ASTERIK = "*";
|
|
11
|
+
this.EQUALS = "=";
|
|
12
|
+
this.COLON = ":";
|
|
13
|
+
this.QUERY_DELIMETER = "&";
|
|
14
|
+
this.COMMA = ",";
|
|
15
|
+
this.baseResource = "";
|
|
16
|
+
this.singularQueries = [];
|
|
17
|
+
this.sortQueries = [];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* @returns base resource for the query
|
|
21
|
+
*/
|
|
22
|
+
getBaseResource() {
|
|
23
|
+
return this.baseResource;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* @param resourceType Base ResourceType for the query
|
|
27
|
+
* sets base resource type for the query
|
|
28
|
+
* i.e. which resource the query will performed for
|
|
29
|
+
*/
|
|
30
|
+
setBaseResource(resourceType) {
|
|
31
|
+
this.baseResource = resourceType;
|
|
32
|
+
return this;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* @param searchParameter SearchParameter reference to targeted resource
|
|
36
|
+
*/
|
|
37
|
+
include(searchParameter) {
|
|
38
|
+
this.singularQueries.push(this.INCLUDE_KEYWORD + this.EQUALS + encodeURIComponent(this.baseResource + this.COLON + searchParameter));
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* @param resourceType Source ResourceType
|
|
43
|
+
* @param searchParameter SearchParameter reference from target resource to baseResource
|
|
44
|
+
*/
|
|
45
|
+
revinclude(resourceType, searchParameter) {
|
|
46
|
+
this.singularQueries.push(this.REV_INCLUDE_KEYWORD + this.EQUALS + encodeURIComponent(resourceType + this.COLON + searchParameter));
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* _include all references in query
|
|
51
|
+
*/
|
|
52
|
+
includeAll() {
|
|
53
|
+
this.singularQueries.push(this.INCLUDE_KEYWORD + this.EQUALS + this.WILDCARD_ASTERIK);
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* _revinclude all references in query
|
|
58
|
+
*/
|
|
59
|
+
revincludeAll() {
|
|
60
|
+
this.singularQueries.push(this.REV_INCLUDE_KEYWORD + this.EQUALS + this.WILDCARD_ASTERIK);
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* @param searchParameter search parameter for the element to sort on
|
|
65
|
+
* @param sortOrder ASCENDING or DESCENDING
|
|
66
|
+
*/
|
|
67
|
+
sort(searchParameter, sortOrder) {
|
|
68
|
+
if (sortOrder === constants_1.SORT_ORDER.ASCENDING) {
|
|
69
|
+
this.sortQueries.push(searchParameter);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
this.sortQueries.push("-" + searchParameter);
|
|
73
|
+
}
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Resets queries to it empty state
|
|
78
|
+
*/
|
|
79
|
+
resetQuery() {
|
|
80
|
+
this.sortQueries = [];
|
|
81
|
+
this.singularQueries = [];
|
|
82
|
+
this.baseResource = "";
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* @returns complete generated query with encoded parameter values
|
|
87
|
+
*/
|
|
88
|
+
getCompleteUrl() {
|
|
89
|
+
let completeUrl = this.baseResource + "?";
|
|
90
|
+
const singularQueriesLength = this.singularQueries.length;
|
|
91
|
+
if (singularQueriesLength > 0) {
|
|
92
|
+
completeUrl += this.singularQueries.join(this.QUERY_DELIMETER);
|
|
93
|
+
}
|
|
94
|
+
completeUrl += this.createSortQuery(singularQueriesLength);
|
|
95
|
+
return completeUrl;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* @returns complete generated query with decoded parameter values
|
|
99
|
+
*/
|
|
100
|
+
getCompleteUrlDecoded() {
|
|
101
|
+
return decodeURIComponent(this.getCompleteUrl().toString());
|
|
102
|
+
}
|
|
103
|
+
createSortQuery(singularQueriesLength) {
|
|
104
|
+
let sortQuery = "";
|
|
105
|
+
const sortDelimeter = singularQueriesLength > 0 ? this.QUERY_DELIMETER : "";
|
|
106
|
+
if (this.sortQueries.length > 0) {
|
|
107
|
+
sortQuery += sortDelimeter + this.SORT_KEYWORD + this.EQUALS + encodeURIComponent(this.sortQueries.join(this.COMMA));
|
|
108
|
+
}
|
|
109
|
+
return sortQuery;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
exports.QueryBuilder = QueryBuilder;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const constants_1 = require("../constants");
|
|
4
|
+
const QueryBuilder_1 = require("./QueryBuilder");
|
|
5
|
+
describe("QueryBuilder", () => {
|
|
6
|
+
let queryBuilder;
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
queryBuilder = new QueryBuilder_1.QueryBuilder();
|
|
9
|
+
});
|
|
10
|
+
it('setBaseResource() should set base resource and getBaseResource() should return correct base resource', () => {
|
|
11
|
+
// setup
|
|
12
|
+
const expected = "Patient";
|
|
13
|
+
// execute
|
|
14
|
+
queryBuilder.setBaseResource(expected);
|
|
15
|
+
// validate
|
|
16
|
+
expect(queryBuilder.getBaseResource()).toEqual(expected);
|
|
17
|
+
});
|
|
18
|
+
it('include() should make query for _include', () => {
|
|
19
|
+
// setup
|
|
20
|
+
const expected = "Patient?_include=Patient%3Aorganization";
|
|
21
|
+
// execute
|
|
22
|
+
queryBuilder.setBaseResource("Patient").include("organization");
|
|
23
|
+
// validate
|
|
24
|
+
expect(queryBuilder.getCompleteUrl()).toEqual(expected);
|
|
25
|
+
});
|
|
26
|
+
it('include() should make query for multiple _include', () => {
|
|
27
|
+
// setup
|
|
28
|
+
const expected = "Patient?_include=Patient%3Aorganization&_include=Patient%3Alink";
|
|
29
|
+
// execute
|
|
30
|
+
queryBuilder.setBaseResource("Patient")
|
|
31
|
+
.include("organization")
|
|
32
|
+
.include("link");
|
|
33
|
+
// validate
|
|
34
|
+
expect(queryBuilder.getCompleteUrl()).toEqual(expected);
|
|
35
|
+
});
|
|
36
|
+
it('revinclude() should make query for _revinclude', () => {
|
|
37
|
+
// setup
|
|
38
|
+
const expected = "MedicationRequest?_revinclude=CarePlan%3Aactivity-reference";
|
|
39
|
+
// execute
|
|
40
|
+
queryBuilder.setBaseResource("MedicationRequest")
|
|
41
|
+
.revinclude("CarePlan", "activity-reference");
|
|
42
|
+
// validate
|
|
43
|
+
expect(queryBuilder.getCompleteUrl()).toEqual(expected);
|
|
44
|
+
});
|
|
45
|
+
it('revinclude() should make query for multiple _revinclude', () => {
|
|
46
|
+
// setup
|
|
47
|
+
const expected = "MedicationRequest?_revinclude=CarePlan%3Aactivity-reference&_revinclude=Observation%3Abased-on";
|
|
48
|
+
// execute
|
|
49
|
+
queryBuilder.setBaseResource("MedicationRequest")
|
|
50
|
+
.revinclude("CarePlan", "activity-reference")
|
|
51
|
+
.revinclude("Observation", "based-on");
|
|
52
|
+
// validate
|
|
53
|
+
expect(queryBuilder.getCompleteUrl()).toEqual(expected);
|
|
54
|
+
});
|
|
55
|
+
it('include() and revinclude() should make query for _include & _revinclude', () => {
|
|
56
|
+
// setup
|
|
57
|
+
const expected = "MedicationRequest?_revinclude=CarePlan%3Aactivity-reference&_include=MedicationRequest%3Aencounter";
|
|
58
|
+
// execute
|
|
59
|
+
queryBuilder.setBaseResource("MedicationRequest")
|
|
60
|
+
.revinclude("CarePlan", "activity-reference")
|
|
61
|
+
.include("encounter");
|
|
62
|
+
// validate
|
|
63
|
+
expect(queryBuilder.getCompleteUrl()).toEqual(expected);
|
|
64
|
+
});
|
|
65
|
+
it('includeAll() should make query for _include all', () => {
|
|
66
|
+
// setup
|
|
67
|
+
const expected = "MedicationRequest?_include=*";
|
|
68
|
+
// execute
|
|
69
|
+
queryBuilder.setBaseResource("MedicationRequest").includeAll();
|
|
70
|
+
// validate
|
|
71
|
+
expect(queryBuilder.getCompleteUrl()).toEqual(expected);
|
|
72
|
+
});
|
|
73
|
+
it('revincludeAll() should make query for _revinclude all', () => {
|
|
74
|
+
// setup
|
|
75
|
+
const expected = "MedicationRequest?_revinclude=*";
|
|
76
|
+
// execute
|
|
77
|
+
queryBuilder.setBaseResource("MedicationRequest").revincludeAll();
|
|
78
|
+
// validate
|
|
79
|
+
expect(queryBuilder.getCompleteUrl()).toEqual(expected);
|
|
80
|
+
});
|
|
81
|
+
it('includeAll() & revincludeAll() should make query for _include all & _revinclude all', () => {
|
|
82
|
+
// setup
|
|
83
|
+
const expected = "MedicationRequest?_revinclude=*&_include=*";
|
|
84
|
+
// execute
|
|
85
|
+
queryBuilder.setBaseResource("MedicationRequest")
|
|
86
|
+
.revincludeAll()
|
|
87
|
+
.includeAll();
|
|
88
|
+
// validate
|
|
89
|
+
expect(queryBuilder.getCompleteUrl()).toEqual(expected);
|
|
90
|
+
});
|
|
91
|
+
it('sort() should make query for _sort', () => {
|
|
92
|
+
// setup
|
|
93
|
+
const expected = "Observation?_sort=status%2C-date";
|
|
94
|
+
// execute
|
|
95
|
+
queryBuilder.setBaseResource("Observation")
|
|
96
|
+
.sort("status", constants_1.SORT_ORDER.ASCENDING)
|
|
97
|
+
.sort("date", constants_1.SORT_ORDER.DESCENDING);
|
|
98
|
+
// validate
|
|
99
|
+
expect(queryBuilder.getCompleteUrl()).toEqual(expected);
|
|
100
|
+
});
|
|
101
|
+
it('sort(), include() and revincludeAll() should make query for _sort, _include & _revinclude', () => {
|
|
102
|
+
// setup
|
|
103
|
+
const expected = "Observation?_revinclude=*&_include=Observation%3Abased-on&_sort=status";
|
|
104
|
+
// execute
|
|
105
|
+
queryBuilder.setBaseResource("Observation")
|
|
106
|
+
.revincludeAll()
|
|
107
|
+
.include("based-on")
|
|
108
|
+
.sort("status", constants_1.SORT_ORDER.ASCENDING);
|
|
109
|
+
// validate
|
|
110
|
+
expect(queryBuilder.getCompleteUrl()).toEqual(expected);
|
|
111
|
+
});
|
|
112
|
+
it('resetQuery() should reset query', () => {
|
|
113
|
+
// setup
|
|
114
|
+
const expected = "";
|
|
115
|
+
queryBuilder.setBaseResource("Observation")
|
|
116
|
+
.revincludeAll()
|
|
117
|
+
.include("based-on")
|
|
118
|
+
.sort("status", constants_1.SORT_ORDER.ASCENDING);
|
|
119
|
+
// execute
|
|
120
|
+
queryBuilder.resetQuery();
|
|
121
|
+
// validate
|
|
122
|
+
expect(queryBuilder.getBaseResource()).toEqual(expected);
|
|
123
|
+
});
|
|
124
|
+
it('getCompleteUrlDecoded() should get uri decoded query', () => {
|
|
125
|
+
// setup
|
|
126
|
+
const expected = "Observation?_revinclude=*&_include=Observation:based-on&_sort=status";
|
|
127
|
+
// execute
|
|
128
|
+
queryBuilder.setBaseResource("Observation")
|
|
129
|
+
.revincludeAll()
|
|
130
|
+
.include("based-on")
|
|
131
|
+
.sort("status", constants_1.SORT_ORDER.ASCENDING);
|
|
132
|
+
// validate
|
|
133
|
+
expect(queryBuilder.getCompleteUrlDecoded()).toEqual(expected);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
@@ -72,7 +72,12 @@ class ResourceUtils {
|
|
|
72
72
|
let resultSet = [];
|
|
73
73
|
for (let subPathIndex = 0; subPathIndex < resourcePathValue.length; subPathIndex++) {
|
|
74
74
|
const subPathValue = resourcePathValue[subPathIndex];
|
|
75
|
-
|
|
75
|
+
if (this.isPrimitive(subPathValue)) {
|
|
76
|
+
resultSet.push(subPathValue);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
resultSet.push(...this.getValuesAtResourcePath(subPathValue, pathSections.slice(index).join(".")));
|
|
80
|
+
}
|
|
76
81
|
}
|
|
77
82
|
return resultSet;
|
|
78
83
|
}
|
|
@@ -187,6 +187,15 @@ describe("ResourceUtils", () => {
|
|
|
187
187
|
expect(pathValues.length).toEqual(1);
|
|
188
188
|
expect(pathValues[0]).toEqual("male");
|
|
189
189
|
});
|
|
190
|
+
it("should return array with values for a array under object", () => {
|
|
191
|
+
// execute
|
|
192
|
+
const pathValues = resourceUtils.getValuesAtResourcePath(patientPayload, "Patient.name.given");
|
|
193
|
+
// validate
|
|
194
|
+
expect(pathValues.length).toEqual(3);
|
|
195
|
+
expect(pathValues[0]).toEqual("Pieter");
|
|
196
|
+
expect(pathValues[1]).toEqual("Peter");
|
|
197
|
+
expect(pathValues[2]).toEqual("Pieter");
|
|
198
|
+
});
|
|
190
199
|
it("should return array with values if path exists for a deep array element", () => {
|
|
191
200
|
// execute
|
|
192
201
|
const pathValues = resourceUtils.getValuesAtResourcePath(patientPayload, "Patient.contact.relationship.coding.system");
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SORT_ORDER = void 0;
|
|
4
|
+
var SORT_ORDER;
|
|
5
|
+
(function (SORT_ORDER) {
|
|
6
|
+
SORT_ORDER[SORT_ORDER["ASCENDING"] = 0] = "ASCENDING";
|
|
7
|
+
SORT_ORDER[SORT_ORDER["DESCENDING"] = 1] = "DESCENDING";
|
|
8
|
+
})(SORT_ORDER = exports.SORT_ORDER || (exports.SORT_ORDER = {}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smile-cdr/fhirts",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"description": "Fhir ts/js library for frontend apps",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -14,13 +14,11 @@
|
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@types/jasmine": "^4.3.1",
|
|
17
|
+
"@types/node": "^20.12.12",
|
|
18
|
+
"better-npm-audit": "^3.7.3",
|
|
17
19
|
"jasmine": "^4.5.0",
|
|
20
|
+
"nyc": "^15.1.0",
|
|
18
21
|
"ts-node": "^10.9.1",
|
|
19
22
|
"typescript": "^4.0.2"
|
|
20
|
-
},
|
|
21
|
-
"dependencies": {
|
|
22
|
-
"@types/node": "^18.11.15",
|
|
23
|
-
"better-npm-audit": "^3.7.3",
|
|
24
|
-
"nyc": "^15.1.0"
|
|
25
23
|
}
|
|
26
24
|
}
|
package/src/index.ts
CHANGED
|
@@ -4,5 +4,6 @@ import * as fhirR3 from './FHIR-R3';
|
|
|
4
4
|
import * as dstu2 from './FHIR-DSTU2';
|
|
5
5
|
import { ResourceUtils } from './library/ResourceUtils/ResourceUtils';
|
|
6
6
|
import { BundleUtils } from './library/BundleUtils/BundleUtils';
|
|
7
|
-
|
|
7
|
+
import { QueryBuilder } from './library/QueryBuilder/QueryBuilder';
|
|
8
|
+
export { fhirR4, fhirR3, IfhirR4, dstu2, ResourceUtils, BundleUtils, QueryBuilder };
|
|
8
9
|
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { SORT_ORDER } from "../constants";
|
|
2
|
+
import { QueryBuilder } from "./QueryBuilder";
|
|
3
|
+
|
|
4
|
+
describe("QueryBuilder", () => {
|
|
5
|
+
|
|
6
|
+
let queryBuilder: QueryBuilder;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
queryBuilder = new QueryBuilder();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('setBaseResource() should set base resource and getBaseResource() should return correct base resource', () => {
|
|
13
|
+
// setup
|
|
14
|
+
const expected: String = "Patient";
|
|
15
|
+
// execute
|
|
16
|
+
queryBuilder.setBaseResource(expected);
|
|
17
|
+
// validate
|
|
18
|
+
expect(queryBuilder.getBaseResource()).toEqual(expected);
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('include() should make query for _include', () => {
|
|
22
|
+
// setup
|
|
23
|
+
const expected: String = "Patient?_include=Patient%3Aorganization"
|
|
24
|
+
// execute
|
|
25
|
+
queryBuilder.setBaseResource("Patient").include("organization");
|
|
26
|
+
// validate
|
|
27
|
+
expect(queryBuilder.getCompleteUrl()).toEqual(expected);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('include() should make query for multiple _include', () => {
|
|
31
|
+
// setup
|
|
32
|
+
const expected: String = "Patient?_include=Patient%3Aorganization&_include=Patient%3Alink"
|
|
33
|
+
// execute
|
|
34
|
+
queryBuilder.setBaseResource("Patient")
|
|
35
|
+
.include("organization")
|
|
36
|
+
.include("link");
|
|
37
|
+
// validate
|
|
38
|
+
expect(queryBuilder.getCompleteUrl()).toEqual(expected);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('revinclude() should make query for _revinclude', () => {
|
|
42
|
+
// setup
|
|
43
|
+
const expected: String = "MedicationRequest?_revinclude=CarePlan%3Aactivity-reference"
|
|
44
|
+
// execute
|
|
45
|
+
queryBuilder.setBaseResource("MedicationRequest")
|
|
46
|
+
.revinclude("CarePlan", "activity-reference");
|
|
47
|
+
// validate
|
|
48
|
+
expect(queryBuilder.getCompleteUrl()).toEqual(expected);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('revinclude() should make query for multiple _revinclude', () => {
|
|
52
|
+
// setup
|
|
53
|
+
const expected: String = "MedicationRequest?_revinclude=CarePlan%3Aactivity-reference&_revinclude=Observation%3Abased-on"
|
|
54
|
+
// execute
|
|
55
|
+
queryBuilder.setBaseResource("MedicationRequest")
|
|
56
|
+
.revinclude("CarePlan", "activity-reference")
|
|
57
|
+
.revinclude("Observation", "based-on");
|
|
58
|
+
// validate
|
|
59
|
+
expect(queryBuilder.getCompleteUrl()).toEqual(expected);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('include() and revinclude() should make query for _include & _revinclude', () => {
|
|
63
|
+
// setup
|
|
64
|
+
const expected: String = "MedicationRequest?_revinclude=CarePlan%3Aactivity-reference&_include=MedicationRequest%3Aencounter"
|
|
65
|
+
// execute
|
|
66
|
+
queryBuilder.setBaseResource("MedicationRequest")
|
|
67
|
+
.revinclude("CarePlan", "activity-reference")
|
|
68
|
+
.include("encounter");
|
|
69
|
+
// validate
|
|
70
|
+
expect(queryBuilder.getCompleteUrl()).toEqual(expected);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('includeAll() should make query for _include all', () => {
|
|
74
|
+
// setup
|
|
75
|
+
const expected: String = "MedicationRequest?_include=*"
|
|
76
|
+
// execute
|
|
77
|
+
queryBuilder.setBaseResource("MedicationRequest").includeAll();
|
|
78
|
+
// validate
|
|
79
|
+
expect(queryBuilder.getCompleteUrl()).toEqual(expected);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('revincludeAll() should make query for _revinclude all', () => {
|
|
83
|
+
// setup
|
|
84
|
+
const expected: String = "MedicationRequest?_revinclude=*"
|
|
85
|
+
// execute
|
|
86
|
+
queryBuilder.setBaseResource("MedicationRequest").revincludeAll();
|
|
87
|
+
// validate
|
|
88
|
+
expect(queryBuilder.getCompleteUrl()).toEqual(expected);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('includeAll() & revincludeAll() should make query for _include all & _revinclude all', () => {
|
|
92
|
+
// setup
|
|
93
|
+
const expected: String = "MedicationRequest?_revinclude=*&_include=*"
|
|
94
|
+
// execute
|
|
95
|
+
queryBuilder.setBaseResource("MedicationRequest")
|
|
96
|
+
.revincludeAll()
|
|
97
|
+
.includeAll();
|
|
98
|
+
// validate
|
|
99
|
+
expect(queryBuilder.getCompleteUrl()).toEqual(expected);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('sort() should make query for _sort', () => {
|
|
103
|
+
// setup
|
|
104
|
+
const expected: String = "Observation?_sort=status%2C-date"
|
|
105
|
+
// execute
|
|
106
|
+
queryBuilder.setBaseResource("Observation")
|
|
107
|
+
.sort("status", SORT_ORDER.ASCENDING)
|
|
108
|
+
.sort("date", SORT_ORDER.DESCENDING);
|
|
109
|
+
// validate
|
|
110
|
+
expect(queryBuilder.getCompleteUrl()).toEqual(expected);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('sort(), include() and revincludeAll() should make query for _sort, _include & _revinclude', () => {
|
|
114
|
+
// setup
|
|
115
|
+
const expected: String = "Observation?_revinclude=*&_include=Observation%3Abased-on&_sort=status"
|
|
116
|
+
// execute
|
|
117
|
+
queryBuilder.setBaseResource("Observation")
|
|
118
|
+
.revincludeAll()
|
|
119
|
+
.include("based-on")
|
|
120
|
+
.sort("status", SORT_ORDER.ASCENDING);
|
|
121
|
+
// validate
|
|
122
|
+
expect(queryBuilder.getCompleteUrl()).toEqual(expected);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('resetQuery() should reset query', () => {
|
|
126
|
+
// setup
|
|
127
|
+
const expected: String = ""
|
|
128
|
+
queryBuilder.setBaseResource("Observation")
|
|
129
|
+
.revincludeAll()
|
|
130
|
+
.include("based-on")
|
|
131
|
+
.sort("status", SORT_ORDER.ASCENDING);
|
|
132
|
+
// execute
|
|
133
|
+
queryBuilder.resetQuery();
|
|
134
|
+
// validate
|
|
135
|
+
expect(queryBuilder.getBaseResource()).toEqual(expected);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('getCompleteUrlDecoded() should get uri decoded query', () => {
|
|
139
|
+
// setup
|
|
140
|
+
const expected: String = "Observation?_revinclude=*&_include=Observation:based-on&_sort=status"
|
|
141
|
+
// execute
|
|
142
|
+
queryBuilder.setBaseResource("Observation")
|
|
143
|
+
.revincludeAll()
|
|
144
|
+
.include("based-on")
|
|
145
|
+
.sort("status", SORT_ORDER.ASCENDING);
|
|
146
|
+
// validate
|
|
147
|
+
expect(queryBuilder.getCompleteUrlDecoded()).toEqual(expected);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { SORT_ORDER } from "../constants";
|
|
2
|
+
|
|
3
|
+
export class QueryBuilder {
|
|
4
|
+
|
|
5
|
+
readonly REV_INCLUDE_KEYWORD = "_revinclude";
|
|
6
|
+
readonly INCLUDE_KEYWORD = "_include";
|
|
7
|
+
readonly SORT_KEYWORD = "_sort";
|
|
8
|
+
readonly WILDCARD_ASTERIK = "*";
|
|
9
|
+
readonly EQUALS = "=";
|
|
10
|
+
readonly COLON = ":";
|
|
11
|
+
readonly QUERY_DELIMETER = "&";
|
|
12
|
+
readonly COMMA = ",";
|
|
13
|
+
private baseResource: String = "";
|
|
14
|
+
private singularQueries: String[] = [];
|
|
15
|
+
private sortQueries: String[] = [];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @returns base resource for the query
|
|
19
|
+
*/
|
|
20
|
+
getBaseResource(): String {
|
|
21
|
+
return this.baseResource;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param resourceType Base ResourceType for the query
|
|
26
|
+
* sets base resource type for the query
|
|
27
|
+
* i.e. which resource the query will performed for
|
|
28
|
+
*/
|
|
29
|
+
setBaseResource(resourceType: String) {
|
|
30
|
+
this.baseResource = resourceType;
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param searchParameter SearchParameter reference to targeted resource
|
|
36
|
+
*/
|
|
37
|
+
include(searchParameter: String) {
|
|
38
|
+
this.singularQueries.push(this.INCLUDE_KEYWORD + this.EQUALS + encodeURIComponent(this.baseResource + this.COLON + searchParameter));
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param resourceType Source ResourceType
|
|
44
|
+
* @param searchParameter SearchParameter reference from target resource to baseResource
|
|
45
|
+
*/
|
|
46
|
+
revinclude(resourceType: String, searchParameter: String) {
|
|
47
|
+
this.singularQueries.push(this.REV_INCLUDE_KEYWORD + this.EQUALS + encodeURIComponent(resourceType + this.COLON + searchParameter));
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* _include all references in query
|
|
53
|
+
*/
|
|
54
|
+
includeAll() {
|
|
55
|
+
this.singularQueries.push(this.INCLUDE_KEYWORD + this.EQUALS + this.WILDCARD_ASTERIK);
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* _revinclude all references in query
|
|
61
|
+
*/
|
|
62
|
+
revincludeAll() {
|
|
63
|
+
this.singularQueries.push(this.REV_INCLUDE_KEYWORD + this.EQUALS + this.WILDCARD_ASTERIK);
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @param searchParameter search parameter for the element to sort on
|
|
69
|
+
* @param sortOrder ASCENDING or DESCENDING
|
|
70
|
+
*/
|
|
71
|
+
sort(searchParameter: String, sortOrder: SORT_ORDER) {
|
|
72
|
+
if(sortOrder === SORT_ORDER.ASCENDING) {
|
|
73
|
+
this.sortQueries.push(searchParameter);
|
|
74
|
+
} else {
|
|
75
|
+
this.sortQueries.push("-" + searchParameter);
|
|
76
|
+
}
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Resets queries to it empty state
|
|
82
|
+
*/
|
|
83
|
+
resetQuery() {
|
|
84
|
+
this.sortQueries = [];
|
|
85
|
+
this.singularQueries = [];
|
|
86
|
+
this.baseResource = "";
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @returns complete generated query with encoded parameter values
|
|
92
|
+
*/
|
|
93
|
+
getCompleteUrl(): String {
|
|
94
|
+
let completeUrl = this.baseResource + "?";
|
|
95
|
+
const singularQueriesLength = this.singularQueries.length;
|
|
96
|
+
if(singularQueriesLength > 0) {
|
|
97
|
+
completeUrl += this.singularQueries.join(this.QUERY_DELIMETER);
|
|
98
|
+
}
|
|
99
|
+
completeUrl += this.createSortQuery(singularQueriesLength);
|
|
100
|
+
return completeUrl;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @returns complete generated query with decoded parameter values
|
|
105
|
+
*/
|
|
106
|
+
getCompleteUrlDecoded(): String {
|
|
107
|
+
return decodeURIComponent(this.getCompleteUrl().toString());
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private createSortQuery(singularQueriesLength: number): String {
|
|
111
|
+
let sortQuery = "";
|
|
112
|
+
const sortDelimeter = singularQueriesLength > 0 ? this.QUERY_DELIMETER : "";
|
|
113
|
+
if(this.sortQueries.length > 0) {
|
|
114
|
+
sortQuery += sortDelimeter + this.SORT_KEYWORD + this.EQUALS + encodeURIComponent(this.sortQueries.join(this.COMMA));
|
|
115
|
+
}
|
|
116
|
+
return sortQuery;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
}
|
|
@@ -253,6 +253,16 @@ describe("ResourceUtils", () => {
|
|
|
253
253
|
expect(pathValues[0]).toEqual("male");
|
|
254
254
|
});
|
|
255
255
|
|
|
256
|
+
it("should return array with values for a array under object", () => {
|
|
257
|
+
// execute
|
|
258
|
+
const pathValues = resourceUtils.getValuesAtResourcePath(patientPayload, "Patient.name.given");
|
|
259
|
+
// validate
|
|
260
|
+
expect(pathValues.length).toEqual(3);
|
|
261
|
+
expect(pathValues[0]).toEqual("Pieter");
|
|
262
|
+
expect(pathValues[1]).toEqual("Peter");
|
|
263
|
+
expect(pathValues[2]).toEqual("Pieter");
|
|
264
|
+
});
|
|
265
|
+
|
|
256
266
|
it("should return array with values if path exists for a deep array element", () => {
|
|
257
267
|
// execute
|
|
258
268
|
const pathValues = resourceUtils.getValuesAtResourcePath(patientPayload, "Patient.contact.relationship.coding.system");
|
|
@@ -80,8 +80,12 @@ export class ResourceUtils {
|
|
|
80
80
|
let resultSet = [];
|
|
81
81
|
for (let subPathIndex = 0; subPathIndex < resourcePathValue.length; subPathIndex++) {
|
|
82
82
|
const subPathValue = resourcePathValue[subPathIndex];
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
if(this.isPrimitive(subPathValue)) {
|
|
84
|
+
resultSet.push(subPathValue);
|
|
85
|
+
} else {
|
|
86
|
+
resultSet.push(...this.getValuesAtResourcePath(subPathValue,
|
|
87
|
+
pathSections.slice(index).join(".")));
|
|
88
|
+
}
|
|
85
89
|
}
|
|
86
90
|
return resultSet;
|
|
87
91
|
} else if (typeof(resourcePathValue) === 'object') {
|