@nsshunt/stsappframework 3.1.144 → 3.1.146
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/dist/fhir/STSFhirTypes.js +1 -0
- package/dist/fhir/STSFhirTypes.js.map +1 -1
- package/dist/fhir/dalFhirManager.js +4 -0
- package/dist/fhir/dalFhirManager.js.map +1 -1
- package/dist/fhir/dalFhirManagerIORedisJson.js +201 -0
- package/dist/fhir/dalFhirManagerIORedisJson.js.map +1 -0
- package/dist/fhir/dalFhirManagerIORedisJson.test.js +47 -0
- package/dist/fhir/dalFhirManagerIORedisJson.test.js.map +1 -0
- package/dist/fhir/dalFhirManagerPGRes.js +31 -5
- package/dist/fhir/dalFhirManagerPGRes.js.map +1 -1
- package/dist/fhir/dalFhirManagerPGRes.test.js +12 -0
- package/dist/fhir/dalFhirManagerPGRes.test.js.map +1 -1
- package/dist/fhir/dalFhirManagerPGResEntity.js +32 -5
- package/dist/fhir/dalFhirManagerPGResEntity.js.map +1 -1
- package/dist/fhir/dalFhirManagerPGResEntity.test.js +12 -0
- package/dist/fhir/dalFhirManagerPGResEntity.test.js.map +1 -1
- package/dist/fhir/dalFhirManagerRedisJson.js +124 -28
- package/dist/fhir/dalFhirManagerRedisJson.js.map +1 -1
- package/dist/fhir/dalFhirManagerRedisJson.test.js +14 -2
- package/dist/fhir/dalFhirManagerRedisJson.test.js.map +1 -1
- package/dist/fhir/dalFhirTestHelpers.js +23 -1
- package/dist/fhir/dalFhirTestHelpers.js.map +1 -1
- package/package.json +1 -1
- package/src/fhir/STSFhirTypes.ts +5 -4
- package/src/fhir/dalFhirManager.ts +6 -1
- package/src/fhir/dalFhirManagerIORedisJson.test.ts +58 -0
- package/src/fhir/dalFhirManagerIORedisJson.ts +218 -0
- package/src/fhir/dalFhirManagerPGRes.test.ts +12 -0
- package/src/fhir/dalFhirManagerPGRes.ts +37 -6
- package/src/fhir/dalFhirManagerPGResEntity.test.ts +12 -0
- package/src/fhir/dalFhirManagerPGResEntity.ts +55 -5
- package/src/fhir/dalFhirManagerRedisJson.test.ts +14 -2
- package/src/fhir/dalFhirManagerRedisJson.ts +131 -28
- package/src/fhir/dalFhirTestHelpers.ts +26 -1
- package/types/fhir/STSFhirTypes.d.ts +5 -4
- package/types/fhir/STSFhirTypes.d.ts.map +1 -1
- package/types/fhir/dalFhirManager.d.ts.map +1 -1
- package/types/fhir/dalFhirManagerIORedisJson.d.ts +19 -0
- package/types/fhir/dalFhirManagerIORedisJson.d.ts.map +1 -0
- package/types/fhir/dalFhirManagerIORedisJson.test.d.ts +2 -0
- package/types/fhir/dalFhirManagerIORedisJson.test.d.ts.map +1 -0
- package/types/fhir/dalFhirManagerPGRes.d.ts +3 -3
- package/types/fhir/dalFhirManagerPGRes.d.ts.map +1 -1
- package/types/fhir/dalFhirManagerPGResEntity.d.ts +3 -3
- package/types/fhir/dalFhirManagerPGResEntity.d.ts.map +1 -1
- package/types/fhir/dalFhirManagerRedisJson.d.ts +3 -3
- package/types/fhir/dalFhirManagerRedisJson.d.ts.map +1 -1
- package/types/fhir/dalFhirTestHelpers.d.ts +1 -0
- package/types/fhir/dalFhirTestHelpers.d.ts.map +1 -1
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/* eslint @typescript-eslint/no-unused-vars: 0 */ // --> OFF
|
|
2
|
+
/* eslint @typescript-eslint/no-explicit-any: 0 */ // --> OFF
|
|
3
|
+
import { IDomainResource, IDALFhirDataAccessManager, IFhirManagerRedisJsonOptions } from './STSFhirTypes'
|
|
4
|
+
|
|
5
|
+
import { Redis, RedisOptions } from "ioredis";
|
|
6
|
+
|
|
7
|
+
import chalk from 'chalk'
|
|
8
|
+
import { JSONObject } from '@nsshunt/stsutils';
|
|
9
|
+
|
|
10
|
+
export class DALFhirManagerIORedisJson<T> implements IDALFhirDataAccessManager<T> {
|
|
11
|
+
#options: IFhirManagerRedisJsonOptions;
|
|
12
|
+
#redis: Redis | null = null;
|
|
13
|
+
#indexName: string = 'idx:stsfhirresindex';
|
|
14
|
+
|
|
15
|
+
constructor(options: IFhirManagerRedisJsonOptions) {
|
|
16
|
+
this.#options = options;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get options() {
|
|
20
|
+
return this.#options;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
set options(options: IFhirManagerRedisJsonOptions) {
|
|
24
|
+
this.#options = options;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async Start() {
|
|
28
|
+
const redisOptions: RedisOptions = {
|
|
29
|
+
showFriendlyErrorStack: true,
|
|
30
|
+
maxRetriesPerRequest: 20
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
this.#redis = new Redis(this.#options.redisUrl, redisOptions);
|
|
34
|
+
await this.#CreateIndex();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async Stop() {
|
|
38
|
+
if (this.#redis) {
|
|
39
|
+
this.#redis.quit();
|
|
40
|
+
this.#redis.disconnect();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#CreateIndex = async () => {
|
|
45
|
+
if (!this.#redis) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
//const retVal = await this.#redis.ft.dropIndex(this.#indexName);
|
|
51
|
+
const retVal = await this.#redis.call('FT.DROPINDEX', this.#indexName);
|
|
52
|
+
|
|
53
|
+
console.log(retVal);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.log(chalk.red(error))
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const retVal = await this.#redis.call('FT.CREATE', this.#indexName, 'ON', 'HASH', 'PREFIX', '1', `${this.#options.resourceName}_`,
|
|
60
|
+
'SCHEMA', 'id', 'TEXT', 'SORTABLE');
|
|
61
|
+
console.log(retVal);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.log(chalk.red(error))
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
#SearchBy = async (id: string): Promise<any> => {
|
|
68
|
+
if (!this.#redis) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const query = `@id:${this.#options.resourceName}_${id}*`;
|
|
73
|
+
|
|
74
|
+
const retVal: any = await this.#redis.call('FT.SEARCH', this.#indexName, query,
|
|
75
|
+
'RETURN', '2', 'id', 'data', 'SORTBY', 'id', 'DESC', 'LIMIT', '0', '100');
|
|
76
|
+
|
|
77
|
+
return retVal;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
GetResourceType(fhir: Partial<T>): string {
|
|
81
|
+
return `${this.#options.resourceName}_${(fhir as Partial<IDomainResource>).id}`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async GetFhirResource(fhir: Partial<T>, ecb?: (error: Error) => void): Promise<T | null> {
|
|
85
|
+
if (this.#redis) {
|
|
86
|
+
const retValRaw = await this.#redis.hgetall(this.GetResourceType(fhir))
|
|
87
|
+
if (retValRaw) {
|
|
88
|
+
try {
|
|
89
|
+
const { id, data } = retValRaw;
|
|
90
|
+
return JSON.parse(data) as T;
|
|
91
|
+
} catch (error: unknown) {
|
|
92
|
+
if (ecb) {
|
|
93
|
+
ecb(error as Error);
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
106
|
+
async CreateFhirResource(fhir: T, ecb?: (error: Error) => void): Promise<T | null> {
|
|
107
|
+
if (this.#redis) {
|
|
108
|
+
const record = {
|
|
109
|
+
id: this.GetResourceType(fhir),
|
|
110
|
+
data: JSON.stringify(fhir)
|
|
111
|
+
} as any;
|
|
112
|
+
|
|
113
|
+
const retVal = await this.#redis.hmset(this.GetResourceType(fhir), record);
|
|
114
|
+
|
|
115
|
+
if (retVal) {
|
|
116
|
+
return fhir;
|
|
117
|
+
} else {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
126
|
+
async UpdateFhirResource(fhir: T, ecb?: (error: Error) => void): Promise<T | null> {
|
|
127
|
+
if (this.#redis) {
|
|
128
|
+
const record = {
|
|
129
|
+
id: this.GetResourceType(fhir),
|
|
130
|
+
data: JSON.stringify(fhir)
|
|
131
|
+
} as any;
|
|
132
|
+
|
|
133
|
+
const retVal = await this.#redis.hmset(this.GetResourceType(fhir), record);
|
|
134
|
+
|
|
135
|
+
if (retVal) {
|
|
136
|
+
return fhir;
|
|
137
|
+
} else {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
146
|
+
async DeleteFhirResource(fhir: Partial<T>, ecb?: (error: Error) => void): Promise<T | null> {
|
|
147
|
+
if (this.#redis) {
|
|
148
|
+
const deleteResource = await this.GetFhirResource(fhir);
|
|
149
|
+
if (deleteResource) {
|
|
150
|
+
const retVal = await this.#redis.del(this.GetResourceType(fhir));
|
|
151
|
+
if (retVal) {
|
|
152
|
+
return deleteResource;
|
|
153
|
+
} else {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
} else {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async GetFhirResources(filters: string[], ecb?: (error: Error) => void): Promise<T[] | null> {
|
|
165
|
+
const promArray: Promise<T[]>[] = [ ];
|
|
166
|
+
filters.forEach(filter => {
|
|
167
|
+
const prom = async (): Promise<T[]> => {
|
|
168
|
+
const retValArray: T[] = [ ];
|
|
169
|
+
const retVal = await this.#SearchBy(filter);
|
|
170
|
+
// First index is the total number of records in the search (does not consider limit)
|
|
171
|
+
//const listLength = retVal[0];
|
|
172
|
+
|
|
173
|
+
let index = 1;
|
|
174
|
+
const obj: JSONObject = { };
|
|
175
|
+
for (;;) {
|
|
176
|
+
if (index >= retVal.length) {
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
const id = retVal[index++];
|
|
180
|
+
obj[id] = { }
|
|
181
|
+
const values = retVal[index++];
|
|
182
|
+
let dataIndex = 0;
|
|
183
|
+
for (let j=0; j < values.length / 2; j++) {
|
|
184
|
+
const field = values[dataIndex++];
|
|
185
|
+
if ((field as string).localeCompare('data') === 0) {
|
|
186
|
+
const value = JSON.parse(values[dataIndex++]);
|
|
187
|
+
obj[id] = value;
|
|
188
|
+
} else {
|
|
189
|
+
dataIndex++;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
retValArray.push(obj[id]);
|
|
193
|
+
}
|
|
194
|
+
return retValArray;
|
|
195
|
+
}
|
|
196
|
+
promArray.push(prom());
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const promRetVal = await Promise.all(promArray);
|
|
200
|
+
let retVal: T[] = [ ];
|
|
201
|
+
promRetVal.forEach(prv => {
|
|
202
|
+
retVal = retVal.concat(prv);
|
|
203
|
+
});
|
|
204
|
+
return retVal;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async CreateFhirResources(fhir: T[], ecb?: (error: Error) => void): Promise<T[] | null> {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async UpdateFhirResources(fhir: T[], ecb?: (error: Error) => void): Promise<T[] | null> {
|
|
212
|
+
return null
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async DeleteFhirResources(fhir: Partial<T>[], ecb?: (error: Error) => void): Promise<T[] | null> {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -9,6 +9,7 @@ import { JestSleep } from '@nsshunt/stsutils'
|
|
|
9
9
|
import { TestHelpers } from './dalFhirTestHelpers'
|
|
10
10
|
|
|
11
11
|
import { DALFhirManager } from './dalFhirManager'
|
|
12
|
+
import chalk from 'chalk'
|
|
12
13
|
|
|
13
14
|
describe("Test hl7 fhir resource data access layer - PGRes", () =>
|
|
14
15
|
{
|
|
@@ -37,6 +38,17 @@ describe("Test hl7 fhir resource data access layer - PGRes", () =>
|
|
|
37
38
|
await testHelpers.TestFhirPerson(fhirManager.fhirDataAccessManager);
|
|
38
39
|
}, 30000);
|
|
39
40
|
|
|
41
|
+
test('Testing Person', async () => {
|
|
42
|
+
const iterations = 10;
|
|
43
|
+
expect.assertions(iterations + 1);
|
|
44
|
+
await testHelpers.AddFhirPersons(fhirManager.fhirDataAccessManager, iterations);
|
|
45
|
+
|
|
46
|
+
const fhirResources = await fhirManager.fhirDataAccessManager.GetFhirResources(['']);
|
|
47
|
+
expect(fhirResources?.length).toEqual(iterations);
|
|
48
|
+
|
|
49
|
+
console.log(chalk.yellow(JSON.stringify(fhirResources)));
|
|
50
|
+
}, 30000);
|
|
51
|
+
|
|
40
52
|
afterAll(async () =>
|
|
41
53
|
{
|
|
42
54
|
fhirManager.fhirDataAccessManager.Stop();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint @typescript-eslint/no-unused-vars: 0 */ // --> OFF
|
|
2
|
-
import { IDBAccessLayer, IDBResource } from '@nsshunt/stsdatamanagement'
|
|
2
|
+
import { IDBAccessLayer, IDBResource, IDBReturnData } from '@nsshunt/stsdatamanagement'
|
|
3
3
|
|
|
4
4
|
import { IDomainResource, IDALFhirDataAccessManager, IFhirManagerPGResOptions } from './STSFhirTypes'
|
|
5
5
|
|
|
@@ -20,11 +20,11 @@ export class DALFhirManagerPGRes<T> implements IDALFhirDataAccessManager<T> {
|
|
|
20
20
|
this.#options = options;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
Start() {
|
|
23
|
+
async Start() {
|
|
24
24
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
Stop() {
|
|
27
|
+
async Stop() {
|
|
28
28
|
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -115,11 +115,42 @@ export class DALFhirManagerPGRes<T> implements IDALFhirDataAccessManager<T> {
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
async
|
|
119
|
-
|
|
118
|
+
async GetFhirResources(filters: string[], ecb?: (error: Error) => void): Promise<T[] | null> {
|
|
119
|
+
if (!this.#accessLayer) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const promArray: Promise<T[]>[] = [ ];
|
|
124
|
+
filters.forEach(filter => {
|
|
125
|
+
const prom = async (): Promise<T[]> => {
|
|
126
|
+
const retVal: T[] = [ ];
|
|
127
|
+
|
|
128
|
+
const resources = await this.#accessLayer.GetResources<T>({
|
|
129
|
+
filters: {
|
|
130
|
+
filter: `${this.#options.resourceName}_${filter}%`,
|
|
131
|
+
limit: '20',
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
const dbResources = (resources.detail as IDBResource<T>[]);
|
|
136
|
+
|
|
137
|
+
dbResources.forEach(dbResource => {
|
|
138
|
+
retVal.push(dbResource.resdesc);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
return retVal;
|
|
142
|
+
}
|
|
143
|
+
promArray.push(prom());
|
|
144
|
+
})
|
|
145
|
+
const promRetVal = await Promise.all(promArray);
|
|
146
|
+
let retVal: T[] = [ ];
|
|
147
|
+
promRetVal.forEach(prv => {
|
|
148
|
+
retVal = retVal.concat(prv);
|
|
149
|
+
});
|
|
150
|
+
return retVal;
|
|
120
151
|
}
|
|
121
152
|
|
|
122
|
-
async
|
|
153
|
+
async CreateFhirResources(fhir: T[], ecb?: (error: Error) => void): Promise<T[] | null> {
|
|
123
154
|
return null;
|
|
124
155
|
}
|
|
125
156
|
|
|
@@ -10,6 +10,7 @@ import { TestHelpers } from './dalFhirTestHelpers'
|
|
|
10
10
|
import { JestSleep } from '@nsshunt/stsutils'
|
|
11
11
|
|
|
12
12
|
import { DALFhirManager } from './dalFhirManager'
|
|
13
|
+
import chalk from 'chalk'
|
|
13
14
|
|
|
14
15
|
describe("Test hl7 fhir resource data access layer - PGResEntity", () =>
|
|
15
16
|
{
|
|
@@ -39,6 +40,17 @@ describe("Test hl7 fhir resource data access layer - PGResEntity", () =>
|
|
|
39
40
|
await testHelpers.TestFhirPerson(fhirManager.fhirDataAccessManager);
|
|
40
41
|
}, 30000);
|
|
41
42
|
|
|
43
|
+
test('Testing Person', async () => {
|
|
44
|
+
const iterations = 10;
|
|
45
|
+
expect.assertions(iterations + 1);
|
|
46
|
+
await testHelpers.AddFhirPersons(fhirManager.fhirDataAccessManager, iterations);
|
|
47
|
+
|
|
48
|
+
const fhirResources = await fhirManager.fhirDataAccessManager.GetFhirResources(['']);
|
|
49
|
+
expect(fhirResources?.length).toEqual(iterations);
|
|
50
|
+
|
|
51
|
+
console.log(chalk.cyan(JSON.stringify(fhirResources)));
|
|
52
|
+
}, 30000);
|
|
53
|
+
|
|
42
54
|
afterAll(async () =>
|
|
43
55
|
{
|
|
44
56
|
fhirManager.fhirDataAccessManager.Stop();
|
|
@@ -21,11 +21,11 @@ export class DALFhirManagerPGResEntity<T> implements IDALFhirDataAccessManager<T
|
|
|
21
21
|
this.#options = options;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
Start() {
|
|
24
|
+
async Start() {
|
|
25
25
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
Stop() {
|
|
28
|
+
async Stop() {
|
|
29
29
|
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -146,11 +146,43 @@ export class DALFhirManagerPGResEntity<T> implements IDALFhirDataAccessManager<T
|
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
async
|
|
150
|
-
|
|
149
|
+
async GetFhirResources(filters: string[], ecb?: (error: Error) => void): Promise<T[] | null> {
|
|
150
|
+
if (!this.#accessLayer) {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const fhirResource = await this.#GetSTSFhirBaseResource();
|
|
155
|
+
const promArray: Promise<T[]>[] = [ ];
|
|
156
|
+
filters.forEach(filter => {
|
|
157
|
+
const prom = async (): Promise<T[]> => {
|
|
158
|
+
const retVal: T[] = [ ];
|
|
159
|
+
|
|
160
|
+
const entities = await this.#accessLayer.GetEntities<T>({
|
|
161
|
+
filters: {
|
|
162
|
+
resname: fhirResource.resname,
|
|
163
|
+
filter: `${filter}%`
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
const dbResources = (entities.detail as IDBEntity<T>[]);
|
|
168
|
+
|
|
169
|
+
dbResources.forEach(dbResource => {
|
|
170
|
+
retVal.push(dbResource.entvalue);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
return retVal;
|
|
174
|
+
}
|
|
175
|
+
promArray.push(prom());
|
|
176
|
+
})
|
|
177
|
+
const promRetVal = await Promise.all(promArray);
|
|
178
|
+
let retVal: T[] = [ ];
|
|
179
|
+
promRetVal.forEach(prv => {
|
|
180
|
+
retVal = retVal.concat(prv);
|
|
181
|
+
});
|
|
182
|
+
return retVal;
|
|
151
183
|
}
|
|
152
184
|
|
|
153
|
-
async
|
|
185
|
+
async CreateFhirResources(fhir: T[], ecb?: (error: Error) => void): Promise<T[] | null> {
|
|
154
186
|
return null;
|
|
155
187
|
}
|
|
156
188
|
|
|
@@ -161,4 +193,22 @@ export class DALFhirManagerPGResEntity<T> implements IDALFhirDataAccessManager<T
|
|
|
161
193
|
async DeleteFhirResources(fhir: Partial<T>[], ecb?: (error: Error) => void): Promise<T[] | null> {
|
|
162
194
|
return null;
|
|
163
195
|
}
|
|
196
|
+
|
|
197
|
+
/*
|
|
198
|
+
|
|
199
|
+
const resources = await accessLayer.GetResources<IUserResourceType>({
|
|
200
|
+
filters: {
|
|
201
|
+
filter: 'RESCP-%',
|
|
202
|
+
limit: '10',
|
|
203
|
+
}
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
const entities = await accessLayer.GetEntities<IUserEntity>({
|
|
207
|
+
filters: {
|
|
208
|
+
resname: 'user-t2',
|
|
209
|
+
filter: '%'
|
|
210
|
+
}
|
|
211
|
+
})
|
|
212
|
+
*/
|
|
213
|
+
|
|
164
214
|
}
|
|
@@ -8,6 +8,7 @@ import { TestHelpers } from './dalFhirTestHelpers'
|
|
|
8
8
|
import { JestSleep } from '@nsshunt/stsutils'
|
|
9
9
|
|
|
10
10
|
import { DALFhirManager } from './dalFhirManager'
|
|
11
|
+
import chalk from 'chalk'
|
|
11
12
|
|
|
12
13
|
describe.skip("Test hl7 fhir resource data access layer - RedisJson", () =>
|
|
13
14
|
{
|
|
@@ -28,7 +29,7 @@ describe.skip("Test hl7 fhir resource data access layer - RedisJson", () =>
|
|
|
28
29
|
}
|
|
29
30
|
});
|
|
30
31
|
|
|
31
|
-
fhirManager.fhirDataAccessManager.Start();
|
|
32
|
+
await fhirManager.fhirDataAccessManager.Start();
|
|
32
33
|
}, 30000);
|
|
33
34
|
|
|
34
35
|
test('Testing Person', async () => {
|
|
@@ -36,9 +37,20 @@ describe.skip("Test hl7 fhir resource data access layer - RedisJson", () =>
|
|
|
36
37
|
await testHelpers.TestFhirPerson(fhirManager.fhirDataAccessManager);
|
|
37
38
|
}, 30000);
|
|
38
39
|
|
|
40
|
+
test('Testing Person', async () => {
|
|
41
|
+
const iterations = 10;
|
|
42
|
+
expect.assertions(iterations + 1);
|
|
43
|
+
await testHelpers.AddFhirPersons(fhirManager.fhirDataAccessManager, iterations);
|
|
44
|
+
|
|
45
|
+
const fhirResources = await fhirManager.fhirDataAccessManager.GetFhirResources(['']);
|
|
46
|
+
expect(fhirResources?.length).toEqual(iterations);
|
|
47
|
+
|
|
48
|
+
console.log(chalk.green(JSON.stringify(fhirResources)));
|
|
49
|
+
}, 30000);
|
|
50
|
+
|
|
39
51
|
afterAll(async () =>
|
|
40
52
|
{
|
|
41
|
-
fhirManager.fhirDataAccessManager.Stop();
|
|
53
|
+
await fhirManager.fhirDataAccessManager.Stop();
|
|
42
54
|
await JestSleep();
|
|
43
55
|
await testHelpers.StopRedis();
|
|
44
56
|
}, 5000);
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
/* eslint @typescript-eslint/no-unused-vars: 0 */ // --> OFF
|
|
2
|
+
/* eslint @typescript-eslint/no-explicit-any: 0 */ // --> OFF
|
|
2
3
|
import { IDomainResource, IDALFhirDataAccessManager, IFhirManagerRedisJsonOptions } from './STSFhirTypes'
|
|
3
4
|
|
|
4
|
-
import {
|
|
5
|
+
import { SchemaFieldTypes, createClient, RedisClientType, AggregateSteps, AggregateGroupByReducers } from 'redis';
|
|
6
|
+
|
|
7
|
+
import chalk from 'chalk'
|
|
5
8
|
|
|
6
9
|
export class DALFhirManagerRedisJson<T> implements IDALFhirDataAccessManager<T> {
|
|
7
10
|
#options: IFhirManagerRedisJsonOptions;
|
|
8
|
-
#
|
|
11
|
+
#redis: RedisClientType | null = null;
|
|
12
|
+
#indexName: string = 'idx:stsfhirresindex';
|
|
9
13
|
|
|
10
14
|
constructor(options: IFhirManagerRedisJsonOptions) {
|
|
11
15
|
this.#options = options;
|
|
@@ -19,19 +23,71 @@ export class DALFhirManagerRedisJson<T> implements IDALFhirDataAccessManager<T>
|
|
|
19
23
|
this.#options = options;
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
#CreateIndex = async () => {
|
|
27
|
+
if (!this.#redis) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const retVal = await this.#redis.ft.dropIndex(this.#indexName);
|
|
33
|
+
console.log(retVal);
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.log(chalk.red(error))
|
|
26
36
|
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const retVal = await this.#redis.ft.create(this.#indexName, { // serviceIndexInstant
|
|
40
|
+
'id': { type: SchemaFieldTypes.TEXT, SORTABLE: true }
|
|
41
|
+
}, {
|
|
42
|
+
ON: 'HASH',
|
|
43
|
+
PREFIX: `${this.#options.resourceName}_`
|
|
44
|
+
});
|
|
45
|
+
console.log(retVal);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.log(chalk.red(error))
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#SearchBy = async (id: string) => {
|
|
52
|
+
if (!this.#redis) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const queryOptions = {
|
|
57
|
+
RETURN: [ 'id', 'data' ],
|
|
58
|
+
LIMIT: {
|
|
59
|
+
from: 0,
|
|
60
|
+
size: 100
|
|
61
|
+
},
|
|
62
|
+
SORTBY: {
|
|
63
|
+
BY: 'id',
|
|
64
|
+
DIRECTION: 'DESC'
|
|
65
|
+
}
|
|
66
|
+
} as any;
|
|
67
|
+
|
|
68
|
+
const query = `@id:${this.#options.resourceName}_${id}*`;
|
|
69
|
+
|
|
70
|
+
const retVal = await this.#redis.ft.search(this.#indexName, query, queryOptions);
|
|
27
71
|
|
|
28
|
-
|
|
72
|
+
return retVal;
|
|
29
73
|
}
|
|
30
74
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
75
|
+
async Start() {
|
|
76
|
+
this.#redis = await createClient({url: this.#options.redisUrl})
|
|
77
|
+
.on('error', error => console.error(chalk.red(`Error connecting to redis. Error: [${error}]`)))
|
|
78
|
+
.connect() as any
|
|
79
|
+
|
|
80
|
+
if (this.#redis) {
|
|
81
|
+
await this.#CreateIndex();
|
|
82
|
+
} else {
|
|
83
|
+
console.error(chalk.red(`this.#redis instance not created.`))
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async Stop() {
|
|
88
|
+
if (this.#redis) {
|
|
89
|
+
//await this.#redis.quit();
|
|
90
|
+
await this.#redis.disconnect();
|
|
35
91
|
}
|
|
36
92
|
}
|
|
37
93
|
|
|
@@ -40,11 +96,12 @@ export class DALFhirManagerRedisJson<T> implements IDALFhirDataAccessManager<T>
|
|
|
40
96
|
}
|
|
41
97
|
|
|
42
98
|
async GetFhirResource(fhir: Partial<T>, ecb?: (error: Error) => void): Promise<T | null> {
|
|
43
|
-
if (this.#
|
|
44
|
-
const retValRaw = await this.#
|
|
99
|
+
if (this.#redis) {
|
|
100
|
+
const retValRaw = await this.#redis.HGETALL(this.GetResourceType(fhir))
|
|
45
101
|
if (retValRaw) {
|
|
46
102
|
try {
|
|
47
|
-
|
|
103
|
+
const { id, data } = retValRaw;
|
|
104
|
+
return JSON.parse(data) as T;
|
|
48
105
|
} catch (error: unknown) {
|
|
49
106
|
if (ecb) {
|
|
50
107
|
ecb(error as Error);
|
|
@@ -61,19 +118,43 @@ export class DALFhirManagerRedisJson<T> implements IDALFhirDataAccessManager<T>
|
|
|
61
118
|
|
|
62
119
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
63
120
|
async CreateFhirResource(fhir: T, ecb?: (error: Error) => void): Promise<T | null> {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
121
|
+
if (this.#redis) {
|
|
122
|
+
const record = {
|
|
123
|
+
id: this.GetResourceType(fhir),
|
|
124
|
+
data: JSON.stringify(fhir)
|
|
125
|
+
} as any;
|
|
126
|
+
|
|
127
|
+
const keyValuePairs: any = Object.entries(record).flat();
|
|
128
|
+
|
|
129
|
+
const retVal = await this.#redis.HSET(this.GetResourceType(fhir), keyValuePairs);
|
|
130
|
+
|
|
131
|
+
if (retVal) {
|
|
132
|
+
return fhir;
|
|
133
|
+
} else {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
67
136
|
} else {
|
|
137
|
+
console.error(chalk.red(`this.#redis not defined.`))
|
|
68
138
|
return null;
|
|
69
139
|
}
|
|
70
140
|
}
|
|
71
141
|
|
|
72
142
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
73
143
|
async UpdateFhirResource(fhir: T, ecb?: (error: Error) => void): Promise<T | null> {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
144
|
+
if (this.#redis) {
|
|
145
|
+
const record = {
|
|
146
|
+
id: this.GetResourceType(fhir),
|
|
147
|
+
data: JSON.stringify(fhir)
|
|
148
|
+
} as any;
|
|
149
|
+
|
|
150
|
+
const keyValuePairs: any = Object.entries(record).flat();
|
|
151
|
+
|
|
152
|
+
const retVal = await this.#redis.HSET(this.GetResourceType(fhir), keyValuePairs);
|
|
153
|
+
if (retVal >= 0) {
|
|
154
|
+
return fhir;
|
|
155
|
+
} else {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
77
158
|
} else {
|
|
78
159
|
return null;
|
|
79
160
|
}
|
|
@@ -81,11 +162,15 @@ export class DALFhirManagerRedisJson<T> implements IDALFhirDataAccessManager<T>
|
|
|
81
162
|
|
|
82
163
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
83
164
|
async DeleteFhirResource(fhir: Partial<T>, ecb?: (error: Error) => void): Promise<T | null> {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
165
|
+
if (this.#redis) {
|
|
166
|
+
const deleteResource = await this.GetFhirResource(fhir);
|
|
167
|
+
if (deleteResource) {
|
|
168
|
+
const retVal = await this.#redis.del(this.GetResourceType(fhir));
|
|
169
|
+
if (retVal > 0) {
|
|
170
|
+
return deleteResource;
|
|
171
|
+
} else {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
89
174
|
} else {
|
|
90
175
|
return null;
|
|
91
176
|
}
|
|
@@ -94,11 +179,29 @@ export class DALFhirManagerRedisJson<T> implements IDALFhirDataAccessManager<T>
|
|
|
94
179
|
}
|
|
95
180
|
}
|
|
96
181
|
|
|
97
|
-
async
|
|
98
|
-
|
|
182
|
+
async GetFhirResources(filters: string[], ecb?: (error: Error) => void): Promise<T[] | null> {
|
|
183
|
+
const promArray: Promise<T[]>[] = [ ];
|
|
184
|
+
filters.forEach(filter => {
|
|
185
|
+
const prom = async (): Promise<T[]> => {
|
|
186
|
+
const retValArray: T[] = [ ];
|
|
187
|
+
const retVal = await this.#SearchBy(filter);
|
|
188
|
+
retVal?.documents.forEach(doc => {
|
|
189
|
+
retValArray.push(JSON.parse(doc.value.data as string) as T);
|
|
190
|
+
})
|
|
191
|
+
return retValArray;
|
|
192
|
+
}
|
|
193
|
+
promArray.push(prom());
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const promRetVal = await Promise.all(promArray);
|
|
197
|
+
let retVal: T[] = [ ];
|
|
198
|
+
promRetVal.forEach(prv => {
|
|
199
|
+
retVal = retVal.concat(prv);
|
|
200
|
+
});
|
|
201
|
+
return retVal;
|
|
99
202
|
}
|
|
100
203
|
|
|
101
|
-
async
|
|
204
|
+
async CreateFhirResources(fhir: T[], ecb?: (error: Error) => void): Promise<T[] | null> {
|
|
102
205
|
return null;
|
|
103
206
|
}
|
|
104
207
|
|