@nsshunt/stsappframework 3.1.136 → 3.1.138
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 +3 -0
- package/dist/fhir/STSFhirTypes.js.map +1 -0
- package/dist/fhir/dalFhirManagerPGRes.js +87 -0
- package/dist/fhir/dalFhirManagerPGRes.js.map +1 -0
- package/dist/fhir/dalFhirManagerPGRes.test.js +31 -0
- package/dist/fhir/dalFhirManagerPGRes.test.js.map +1 -0
- package/dist/fhir/dalFhirManagerPGResEntity.js +116 -0
- package/dist/fhir/dalFhirManagerPGResEntity.js.map +1 -0
- package/dist/fhir/dalFhirManagerPGResEntity.test.js +32 -0
- package/dist/fhir/dalFhirManagerPGResEntity.test.js.map +1 -0
- package/dist/fhir/dalFhirManagerRedisJson.js +87 -0
- package/dist/fhir/dalFhirManagerRedisJson.js.map +1 -0
- package/dist/fhir/dalFhirManagerRedisJson.test.js +33 -0
- package/dist/fhir/dalFhirManagerRedisJson.test.js.map +1 -0
- package/dist/fhir/dalFhirTestHelpers.js +130 -0
- package/dist/fhir/dalFhirTestHelpers.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/redisMessageHandler.js +23 -0
- package/dist/redisMessageHandler.js.map +1 -1
- package/dist/testing/app.js +9 -0
- package/dist/testing/app.js.map +1 -1
- package/package.json +1 -1
- package/src/fhir/STSFhirTypes.ts +501 -0
- package/src/fhir/dalFhirManagerPGRes.test.ts +42 -0
- package/src/fhir/dalFhirManagerPGRes.ts +92 -0
- package/src/fhir/dalFhirManagerPGResEntity.test.ts +43 -0
- package/src/fhir/dalFhirManagerPGResEntity.ts +123 -0
- package/src/fhir/dalFhirManagerRedisJson.test.ts +44 -0
- package/src/fhir/dalFhirManagerRedisJson.ts +92 -0
- package/src/fhir/dalFhirTestHelpers.ts +163 -0
- package/src/index.ts +2 -0
- package/src/redisMessageHandler.ts +23 -0
- package/src/testing/app.ts +10 -0
- package/types/fhir/STSFhirTypes.d.ts +318 -0
- package/types/fhir/STSFhirTypes.d.ts.map +1 -0
- package/types/fhir/dalFhirManagerPGRes.d.ts +16 -0
- package/types/fhir/dalFhirManagerPGRes.d.ts.map +1 -0
- package/types/fhir/dalFhirManagerPGRes.test.d.ts +2 -0
- package/types/fhir/dalFhirManagerPGRes.test.d.ts.map +1 -0
- package/types/fhir/dalFhirManagerPGResEntity.d.ts +15 -0
- package/types/fhir/dalFhirManagerPGResEntity.d.ts.map +1 -0
- package/types/fhir/dalFhirManagerPGResEntity.test.d.ts +2 -0
- package/types/fhir/dalFhirManagerPGResEntity.test.d.ts.map +1 -0
- package/types/fhir/dalFhirManagerRedisJson.d.ts +17 -0
- package/types/fhir/dalFhirManagerRedisJson.d.ts.map +1 -0
- package/types/fhir/dalFhirManagerRedisJson.test.d.ts +2 -0
- package/types/fhir/dalFhirManagerRedisJson.test.d.ts.map +1 -0
- package/types/fhir/dalFhirTestHelpers.d.ts +14 -0
- package/types/fhir/dalFhirTestHelpers.d.ts.map +1 -0
- package/types/index.d.ts +2 -0
- package/types/index.d.ts.map +1 -1
- package/types/redisMessageHandler.d.ts +1 -0
- package/types/redisMessageHandler.d.ts.map +1 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/* eslint @typescript-eslint/no-explicit-any: 0 */ // --> OFF
|
|
2
|
+
import { beforeAll, afterAll, test, describe, expect } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import { accessLayerType, IPGAccessLayerOptions, SYSTEM_USER_ID } from '@nsshunt/stsdatamanagement'
|
|
5
|
+
|
|
6
|
+
import { DALFhirManagerPGRes } from './dalFhirManagerPGRes'
|
|
7
|
+
|
|
8
|
+
import { IPerson } from './STSFhirTypes'
|
|
9
|
+
|
|
10
|
+
import { TestHelpers } from './dalFhirTestHelpers'
|
|
11
|
+
|
|
12
|
+
describe("Test hl7 fhir resource data access layer - PGRes", () =>
|
|
13
|
+
{
|
|
14
|
+
let testHelpers: TestHelpers;
|
|
15
|
+
|
|
16
|
+
beforeAll(async () =>
|
|
17
|
+
{
|
|
18
|
+
testHelpers = new TestHelpers();
|
|
19
|
+
await testHelpers.StartDatabase();
|
|
20
|
+
}, 30000);
|
|
21
|
+
|
|
22
|
+
test('Testing Person', async () => {
|
|
23
|
+
expect.assertions(6);
|
|
24
|
+
|
|
25
|
+
const personManager = new DALFhirManagerPGRes<IPerson>({
|
|
26
|
+
accessLayerType: accessLayerType.postgresql,
|
|
27
|
+
accessLayerOptions: {
|
|
28
|
+
logger: testHelpers.logger,
|
|
29
|
+
usedefaultdb: false
|
|
30
|
+
} as IPGAccessLayerOptions,
|
|
31
|
+
resourceName: 'STSFhirPerson',
|
|
32
|
+
dbactionuser: SYSTEM_USER_ID
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
await testHelpers.TestFhirPerson(personManager);
|
|
36
|
+
}, 30000);
|
|
37
|
+
|
|
38
|
+
afterAll(async () =>
|
|
39
|
+
{
|
|
40
|
+
await testHelpers.StopDatabase();
|
|
41
|
+
}, 5000);
|
|
42
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { DBAccessLayerManager, IDBAccessLayer, IDBAccessLayerManagerOptions, IDBResource } from '@nsshunt/stsdatamanagement'
|
|
2
|
+
|
|
3
|
+
import { IDomainResource, IDALFhirDataAccessManager } from './STSFhirTypes'
|
|
4
|
+
|
|
5
|
+
export interface IFhirManagerPGResOptions extends IDBAccessLayerManagerOptions {
|
|
6
|
+
resourceName: string
|
|
7
|
+
dbactionuser: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class DALFhirManagerPGRes<T> implements IDALFhirDataAccessManager<T> {
|
|
11
|
+
#options: IFhirManagerPGResOptions;
|
|
12
|
+
#accessLayer: IDBAccessLayer;
|
|
13
|
+
|
|
14
|
+
constructor(options: IFhirManagerPGResOptions) {
|
|
15
|
+
this.#options = options;
|
|
16
|
+
this.#accessLayer = new DBAccessLayerManager().CreateAccessLayer({
|
|
17
|
+
accessLayerType: options.accessLayerType,
|
|
18
|
+
accessLayerOptions: options.accessLayerOptions
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
GetResourceType(fhir: Partial<T>): string {
|
|
23
|
+
return `${this.#options.resourceName}_${(fhir as Partial<IDomainResource>).id}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async GetFhirResource(fhir: Partial<T>, ecb?: (error: Error) => void): Promise<T | null> {
|
|
27
|
+
const retVal = await this.#accessLayer.GetResource<T>({
|
|
28
|
+
filters: {
|
|
29
|
+
resname: this.GetResourceType(fhir)
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
if (retVal.error) {
|
|
33
|
+
if (ecb) {
|
|
34
|
+
ecb(retVal.error.error);
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
} else {
|
|
38
|
+
return (retVal.detail as IDBResource<T>).resdesc;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async CreateFhirResource(fhir: T, ecb?: (error: Error) => void): Promise<T | null> {
|
|
43
|
+
const retVal = await this.#accessLayer.CreateResource<T>({
|
|
44
|
+
filters: {
|
|
45
|
+
dbactionuser: this.#options.dbactionuser,
|
|
46
|
+
resname: this.GetResourceType(fhir)
|
|
47
|
+
}
|
|
48
|
+
}, fhir);
|
|
49
|
+
if (retVal.error) {
|
|
50
|
+
if (ecb) {
|
|
51
|
+
ecb(retVal.error.error);
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
} else {
|
|
55
|
+
return (retVal.detail as IDBResource<T>).resdesc;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async UpdateFhirResource(fhir: T, ecb?: (error: Error) => void): Promise<T | null> {
|
|
60
|
+
const retVal = await this.#accessLayer.UpdateResource<T>({
|
|
61
|
+
filters: {
|
|
62
|
+
dbactionuser: this.#options.dbactionuser,
|
|
63
|
+
resname: this.GetResourceType(fhir)
|
|
64
|
+
}
|
|
65
|
+
}, fhir);
|
|
66
|
+
if (retVal.error) {
|
|
67
|
+
if (ecb) {
|
|
68
|
+
ecb(retVal.error.error);
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
} else {
|
|
72
|
+
return (retVal.detail as IDBResource<T>).resdesc;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async DeleteFhirResource(fhir: Partial<T>, ecb?: (error: Error) => void): Promise<T | null> {
|
|
77
|
+
const retVal = await this.#accessLayer.DeleteResource<T>({
|
|
78
|
+
filters: {
|
|
79
|
+
dbactionuser: this.#options.dbactionuser,
|
|
80
|
+
resname: this.GetResourceType(fhir)
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
if (retVal.error) {
|
|
84
|
+
if (ecb) {
|
|
85
|
+
ecb(retVal.error.error);
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
} else {
|
|
89
|
+
return (retVal.detail as IDBResource<T>).resdesc;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/* eslint @typescript-eslint/no-explicit-any: 0 */ // --> OFF
|
|
2
|
+
import { beforeAll, afterAll, test, describe, expect } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import { accessLayerType, IPGAccessLayerOptions, SYSTEM_USER_ID } from '@nsshunt/stsdatamanagement'
|
|
5
|
+
|
|
6
|
+
import { DALFhirManagerPGResEntity } from './dalFhirManagerPGResEntity'
|
|
7
|
+
|
|
8
|
+
import { IPerson } from './STSFhirTypes'
|
|
9
|
+
|
|
10
|
+
import { TestHelpers } from './dalFhirTestHelpers'
|
|
11
|
+
|
|
12
|
+
describe("Test hl7 fhir resource data access layer - PGResEntity", () =>
|
|
13
|
+
{
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
+
let testHelpers: TestHelpers;
|
|
16
|
+
|
|
17
|
+
beforeAll(async () =>
|
|
18
|
+
{
|
|
19
|
+
testHelpers = new TestHelpers();
|
|
20
|
+
await testHelpers.StartDatabase();
|
|
21
|
+
}, 30000);
|
|
22
|
+
|
|
23
|
+
test('Testing Person', async () => {
|
|
24
|
+
expect.assertions(6);
|
|
25
|
+
|
|
26
|
+
const personManager = new DALFhirManagerPGResEntity<IPerson>({
|
|
27
|
+
accessLayerType: accessLayerType.postgresql,
|
|
28
|
+
accessLayerOptions: {
|
|
29
|
+
logger: testHelpers.logger,
|
|
30
|
+
usedefaultdb: false
|
|
31
|
+
} as IPGAccessLayerOptions,
|
|
32
|
+
resourceName: 'STSFhirPerson',
|
|
33
|
+
dbactionuser: SYSTEM_USER_ID
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
await testHelpers.TestFhirPerson(personManager);
|
|
37
|
+
}, 30000);
|
|
38
|
+
|
|
39
|
+
afterAll(async () =>
|
|
40
|
+
{
|
|
41
|
+
await testHelpers.StopDatabase();
|
|
42
|
+
}, 5000);
|
|
43
|
+
});
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { DBAccessLayerManager, IDBAccessLayer, IDBAccessLayerManagerOptions, IDBResource, IDBEntity } from '@nsshunt/stsdatamanagement'
|
|
2
|
+
|
|
3
|
+
import { IDomainResource, IFHIRResource, IDALFhirDataAccessManager } from './STSFhirTypes'
|
|
4
|
+
|
|
5
|
+
export interface IFhirManagerPGResEntityOptions extends IDBAccessLayerManagerOptions {
|
|
6
|
+
resourceName: string
|
|
7
|
+
dbactionuser: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class DALFhirManagerPGResEntity<T> implements IDALFhirDataAccessManager<T> {
|
|
11
|
+
#options: IFhirManagerPGResEntityOptions;
|
|
12
|
+
#accessLayer: IDBAccessLayer;
|
|
13
|
+
#fhirResource: IDBResource<IFHIRResource> | undefined;
|
|
14
|
+
|
|
15
|
+
constructor(options: IFhirManagerPGResEntityOptions) {
|
|
16
|
+
this.#options = options;
|
|
17
|
+
this.#accessLayer = new DBAccessLayerManager().CreateAccessLayer({
|
|
18
|
+
accessLayerType: options.accessLayerType,
|
|
19
|
+
accessLayerOptions: options.accessLayerOptions
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#GetSTSFhirBaseResource = async (): Promise<IDBResource<IFHIRResource>> => {
|
|
24
|
+
if (!this.#fhirResource) {
|
|
25
|
+
this.#fhirResource = ((await this.#accessLayer.GetResource<IFHIRResource>({
|
|
26
|
+
filters: {
|
|
27
|
+
resname: this.#options.resourceName
|
|
28
|
+
}
|
|
29
|
+
})).detail as IDBResource<IFHIRResource>);
|
|
30
|
+
|
|
31
|
+
if (!this.#fhirResource) {
|
|
32
|
+
const fhirResourceType: IFHIRResource = {
|
|
33
|
+
name: this.#options.resourceName,
|
|
34
|
+
description: `${this.#options.resourceName} description`,
|
|
35
|
+
notes: `${this.#options.resourceName} notes`,
|
|
36
|
+
}
|
|
37
|
+
this.#fhirResource = ((await this.#accessLayer.CreateResource<IFHIRResource>({
|
|
38
|
+
filters: {
|
|
39
|
+
dbactionuser: this.#options.dbactionuser,
|
|
40
|
+
resname: this.#options.resourceName
|
|
41
|
+
}
|
|
42
|
+
}, fhirResourceType)).detail as IDBResource<IFHIRResource>);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return this.#fhirResource;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async GetFhirResource(fhir: Partial<T>, ecb?: (error: Error) => void): Promise<T | null> {
|
|
50
|
+
const fhirResource = await this.#GetSTSFhirBaseResource();
|
|
51
|
+
const retVal = await this.#accessLayer.GetEntity<T>({
|
|
52
|
+
filters: {
|
|
53
|
+
resname: fhirResource.resname,
|
|
54
|
+
entname: (fhir as Partial<IDomainResource>).id
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
if (retVal.error) {
|
|
58
|
+
if (ecb) {
|
|
59
|
+
ecb(retVal.error.error);
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
} else {
|
|
63
|
+
return (retVal.detail as IDBEntity<T>).entvalue;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async CreateFhirResource(fhir: T, ecb?: (error: Error) => void): Promise<T | null> {
|
|
68
|
+
const fhirResource = await this.#GetSTSFhirBaseResource();
|
|
69
|
+
const retVal = await this.#accessLayer.CreateEntity<T>({
|
|
70
|
+
filters: {
|
|
71
|
+
dbactionuser: this.#options.dbactionuser,
|
|
72
|
+
resname: fhirResource.resname,
|
|
73
|
+
entname: (fhir as Partial<IDomainResource>).id
|
|
74
|
+
}
|
|
75
|
+
}, fhir);
|
|
76
|
+
if (retVal.error) {
|
|
77
|
+
if (ecb) {
|
|
78
|
+
ecb(retVal.error.error);
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
} else {
|
|
82
|
+
return (retVal.detail as IDBEntity<T>).entvalue;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async UpdateFhirResource(fhir: T, ecb?: (error: Error) => void): Promise<T | null> {
|
|
87
|
+
const fhirResource = await this.#GetSTSFhirBaseResource();
|
|
88
|
+
const retVal = await this.#accessLayer.UpdateEntity<T>({
|
|
89
|
+
filters: {
|
|
90
|
+
dbactionuser: this.#options.dbactionuser,
|
|
91
|
+
resname: fhirResource.resname,
|
|
92
|
+
entname: (fhir as Partial<IDomainResource>).id
|
|
93
|
+
}
|
|
94
|
+
}, fhir);
|
|
95
|
+
if (retVal.error) {
|
|
96
|
+
if (ecb) {
|
|
97
|
+
ecb(retVal.error.error);
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
} else {
|
|
101
|
+
return (retVal.detail as IDBEntity<T>).entvalue;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async DeleteFhirResource(fhir: Partial<T>, ecb?: (error: Error) => void): Promise<T | null> {
|
|
106
|
+
const fhirResource = await this.#GetSTSFhirBaseResource();
|
|
107
|
+
const retVal = await this.#accessLayer.DeleteEntity<T>({
|
|
108
|
+
filters: {
|
|
109
|
+
dbactionuser: this.#options.dbactionuser,
|
|
110
|
+
resname: fhirResource.resname,
|
|
111
|
+
entname: (fhir as Partial<IDomainResource>).id
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
if (retVal.error) {
|
|
115
|
+
if (ecb) {
|
|
116
|
+
ecb(retVal.error.error);
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
} else {
|
|
120
|
+
return (retVal.detail as IDBEntity<T>).entvalue;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/* eslint @typescript-eslint/no-explicit-any: 0 */ // --> OFF
|
|
2
|
+
import { beforeAll, afterAll, test, describe, expect } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import { DALFhirManagerRedisJson } from './dalFhirManagerRedisJson'
|
|
5
|
+
|
|
6
|
+
import { IPerson } from './STSFhirTypes'
|
|
7
|
+
|
|
8
|
+
import { TestHelpers } from './dalFhirTestHelpers'
|
|
9
|
+
|
|
10
|
+
import { JestSleep } from '@nsshunt/stsutils'
|
|
11
|
+
|
|
12
|
+
describe.skip("Test hl7 fhir resource data access layer - RedisJson", () =>
|
|
13
|
+
{
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
+
let personManager: DALFhirManagerRedisJson<IPerson>;
|
|
16
|
+
let testHelpers: TestHelpers;
|
|
17
|
+
|
|
18
|
+
beforeAll(async () =>
|
|
19
|
+
{
|
|
20
|
+
testHelpers = new TestHelpers();
|
|
21
|
+
await testHelpers.StartRedis();
|
|
22
|
+
|
|
23
|
+
personManager = new DALFhirManagerRedisJson<IPerson>({
|
|
24
|
+
//const personManager = new DALFhirManagerPGRes<IPerson>({
|
|
25
|
+
//const personManager = new DALFhirManagerPGResEntity<IPerson>({
|
|
26
|
+
resourceName: 'STSFhirPerson',
|
|
27
|
+
redisUrl: testHelpers.ioRedisMessageProcessorUrl
|
|
28
|
+
});
|
|
29
|
+
personManager.Start();
|
|
30
|
+
}, 30000);
|
|
31
|
+
|
|
32
|
+
test('Testing Person', async () => {
|
|
33
|
+
expect.assertions(6);
|
|
34
|
+
|
|
35
|
+
await testHelpers.TestFhirPerson(personManager);
|
|
36
|
+
}, 30000);
|
|
37
|
+
|
|
38
|
+
afterAll(async () =>
|
|
39
|
+
{
|
|
40
|
+
personManager.Stop();
|
|
41
|
+
await JestSleep();
|
|
42
|
+
await testHelpers.StopRedis();
|
|
43
|
+
}, 5000);
|
|
44
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { IDomainResource, IDALFhirDataAccessManager } from './STSFhirTypes'
|
|
2
|
+
|
|
3
|
+
import { Redis, RedisOptions } from "ioredis";
|
|
4
|
+
|
|
5
|
+
export interface IFhirManagerRedisJsonOptions {
|
|
6
|
+
redisUrl: string
|
|
7
|
+
resourceName: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class DALFhirManagerRedisJson<T> implements IDALFhirDataAccessManager<T> {
|
|
11
|
+
#options: IFhirManagerRedisJsonOptions;
|
|
12
|
+
#redisSubscriber: Redis | null = null;
|
|
13
|
+
|
|
14
|
+
constructor(options: IFhirManagerRedisJsonOptions) {
|
|
15
|
+
this.#options = options;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
Start() {
|
|
19
|
+
const redisOptions: RedisOptions = {
|
|
20
|
+
showFriendlyErrorStack: true,
|
|
21
|
+
maxRetriesPerRequest: 20
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
this.#redisSubscriber = new Redis(this.#options.redisUrl, redisOptions);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
Stop() {
|
|
28
|
+
if (this.#redisSubscriber) {
|
|
29
|
+
this.#redisSubscriber.quit();
|
|
30
|
+
this.#redisSubscriber.disconnect();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
GetResourceType(fhir: Partial<T>): string {
|
|
35
|
+
return `${this.#options.resourceName}_${(fhir as Partial<IDomainResource>).id}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async GetFhirResource(fhir: Partial<T>, ecb?: (error: Error) => void): Promise<T | null> {
|
|
39
|
+
if (this.#redisSubscriber) {
|
|
40
|
+
const retValRaw = await this.#redisSubscriber?.get(this.GetResourceType(fhir))
|
|
41
|
+
if (retValRaw) {
|
|
42
|
+
try {
|
|
43
|
+
return JSON.parse(retValRaw) as T;
|
|
44
|
+
} catch (error: unknown) {
|
|
45
|
+
if (ecb) {
|
|
46
|
+
ecb(error as Error);
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
59
|
+
async CreateFhirResource(fhir: T, ecb?: (error: Error) => void): Promise<T | null> {
|
|
60
|
+
const retVal = await this.#redisSubscriber?.set(this.GetResourceType(fhir), JSON.stringify(fhir));
|
|
61
|
+
if (retVal) {
|
|
62
|
+
return fhir;
|
|
63
|
+
} else {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
69
|
+
async UpdateFhirResource(fhir: T, ecb?: (error: Error) => void): Promise<T | null> {
|
|
70
|
+
const retVal = await this.#redisSubscriber?.set(this.GetResourceType(fhir), JSON.stringify(fhir));
|
|
71
|
+
if (retVal) {
|
|
72
|
+
return fhir;
|
|
73
|
+
} else {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
79
|
+
async DeleteFhirResource(fhir: Partial<T>, ecb?: (error: Error) => void): Promise<T | null> {
|
|
80
|
+
const deleteResource = await this.GetFhirResource(fhir);
|
|
81
|
+
if (deleteResource) {
|
|
82
|
+
const retVal = await this.#redisSubscriber?.del(this.GetResourceType(fhir));
|
|
83
|
+
if (retVal) {
|
|
84
|
+
return deleteResource;
|
|
85
|
+
} else {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { expect } from 'vitest';
|
|
2
|
+
import { IDALFhirDataAccessManager, INarrative, IPerson } from './STSFhirTypes'
|
|
3
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
4
|
+
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
|
|
7
|
+
import { goptions, $ResetOptions } from '@nsshunt/stsconfig'
|
|
8
|
+
|
|
9
|
+
import { JestSleep, defaultLogger, Sleep } from '@nsshunt/stsutils'
|
|
10
|
+
|
|
11
|
+
import { DatabaseUtils, DBAccessLayerManager, IDBAccessLayer, accessLayerType, IPGAccessLayerOptions } from '@nsshunt/stsdatamanagement'
|
|
12
|
+
|
|
13
|
+
import { GenericContainer } from "testcontainers";
|
|
14
|
+
|
|
15
|
+
import winston from 'winston'
|
|
16
|
+
|
|
17
|
+
export class TestHelpers {
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
+
#postgresContainer: any;
|
|
20
|
+
#accessLayer: IDBAccessLayer | null = null;
|
|
21
|
+
#logger: winston.Logger;
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
23
|
+
#ioRedisContainer: any;
|
|
24
|
+
#ioRedisMessageProcessorUrl = '';
|
|
25
|
+
|
|
26
|
+
constructor() {
|
|
27
|
+
winston.format.combine(
|
|
28
|
+
winston.format.colorize(),
|
|
29
|
+
winston.format.simple()
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
this.#logger = winston.createLogger({
|
|
33
|
+
level: 'silly',
|
|
34
|
+
format: winston.format.combine(
|
|
35
|
+
winston.format.colorize(),
|
|
36
|
+
winston.format.simple()
|
|
37
|
+
),
|
|
38
|
+
transports: [
|
|
39
|
+
new winston.transports.Console(),
|
|
40
|
+
]});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get logger(): winston.Logger {
|
|
44
|
+
return this.#logger;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
TestFhirPerson = async (personManager: IDALFhirDataAccessManager<IPerson>) => {
|
|
48
|
+
expect.assertions(6);
|
|
49
|
+
|
|
50
|
+
const id = uuidv4();
|
|
51
|
+
|
|
52
|
+
const personRecord: IPerson = {
|
|
53
|
+
id,
|
|
54
|
+
_resourceType: 'Person',
|
|
55
|
+
name: [
|
|
56
|
+
{
|
|
57
|
+
family: 'family',
|
|
58
|
+
given: [ 'given' ],
|
|
59
|
+
use: 'usual'
|
|
60
|
+
}
|
|
61
|
+
],
|
|
62
|
+
text: {
|
|
63
|
+
div: 'New Record',
|
|
64
|
+
status: 'generated'
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const newPerson01 = await personManager.CreateFhirResource(personRecord);
|
|
69
|
+
expect(newPerson01).toMatchObject(personRecord);
|
|
70
|
+
|
|
71
|
+
(personRecord.text as INarrative).div = 'user01 updated at ' + new Date().toString();
|
|
72
|
+
const updatedPerson01 = await personManager.UpdateFhirResource(personRecord);
|
|
73
|
+
expect(updatedPerson01).toMatchObject(personRecord);
|
|
74
|
+
|
|
75
|
+
const getPerson01 = await personManager.GetFhirResource({id})
|
|
76
|
+
expect(getPerson01).toMatchObject(personRecord);
|
|
77
|
+
|
|
78
|
+
const getPerson02 = await personManager.GetFhirResource({id:'willnotfind'})
|
|
79
|
+
expect(getPerson02).toBeNull();
|
|
80
|
+
|
|
81
|
+
const deletePerson01 = await personManager.DeleteFhirResource({id})
|
|
82
|
+
expect(deletePerson01).toMatchObject(personRecord);
|
|
83
|
+
|
|
84
|
+
const getPerson01AfterDelete = await personManager.GetFhirResource({id})
|
|
85
|
+
expect(getPerson01AfterDelete).toBeNull();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
StartDatabase = async() => {
|
|
89
|
+
this.#postgresContainer = await new GenericContainer("postgres")
|
|
90
|
+
.withExposedPorts(5432)
|
|
91
|
+
.withEnvironment({
|
|
92
|
+
POSTGRES_PASSWORD: "postgres",
|
|
93
|
+
//UV_THREADPOOL_SIZE: "64"
|
|
94
|
+
})
|
|
95
|
+
.withCommand(['-c', 'max_connections=20'])
|
|
96
|
+
.start();
|
|
97
|
+
|
|
98
|
+
await Sleep(1000);
|
|
99
|
+
|
|
100
|
+
const postgresHttpPort = this.#postgresContainer.getMappedPort(5432);
|
|
101
|
+
const postgresHost = this.#postgresContainer.getHost();
|
|
102
|
+
|
|
103
|
+
process.env.DB_HOST = `${postgresHost}:${postgresHttpPort}`;
|
|
104
|
+
process.env.POOL_SIZE = '10';
|
|
105
|
+
|
|
106
|
+
$ResetOptions();
|
|
107
|
+
|
|
108
|
+
this.logger.info(chalk.green(`httpPort: [${postgresHttpPort}]`));
|
|
109
|
+
this.logger.info(chalk.green(`host: [${postgresHost}]`));
|
|
110
|
+
this.logger.info(chalk.yellow(`connectionString: [${goptions.connectionString}]`));
|
|
111
|
+
this.logger.info(chalk.yellow(`defaultDatabaseConnectionString: [${goptions.defaultDatabaseConnectionString }]`));
|
|
112
|
+
|
|
113
|
+
const dbOptions = {
|
|
114
|
+
start: 0,
|
|
115
|
+
iterations: 10000,
|
|
116
|
+
minextradata: 0,
|
|
117
|
+
maxextradata: 1000,
|
|
118
|
+
workerScriptFolder: './dist', //@@
|
|
119
|
+
useMultiBar: false
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const dbUtils = new DatabaseUtils({
|
|
123
|
+
logger: this.logger
|
|
124
|
+
});
|
|
125
|
+
await dbUtils.CreateDatabase(dbOptions);
|
|
126
|
+
|
|
127
|
+
await Sleep(1000);
|
|
128
|
+
|
|
129
|
+
this.logger.info(chalk.green(`Starting accessLayer`));
|
|
130
|
+
|
|
131
|
+
this.#accessLayer = new DBAccessLayerManager().CreateAccessLayer({
|
|
132
|
+
accessLayerType: accessLayerType.postgresql,
|
|
133
|
+
accessLayerOptions: {
|
|
134
|
+
logger: defaultLogger,
|
|
135
|
+
usedefaultdb: false
|
|
136
|
+
} as IPGAccessLayerOptions
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
this.logger.info(chalk.green(`accessLayer:startdatabase()`));
|
|
140
|
+
await this.#accessLayer.StartDatabase();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
StopDatabase = async () => {
|
|
144
|
+
await this.#accessLayer?.EndDatabase();
|
|
145
|
+
await JestSleep();
|
|
146
|
+
await this.#postgresContainer.stop();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
StartRedis = async () => {
|
|
150
|
+
this.#ioRedisContainer = await new GenericContainer("redis")
|
|
151
|
+
.withExposedPorts(6379)
|
|
152
|
+
.start();
|
|
153
|
+
this.#ioRedisMessageProcessorUrl = `redis://${this.#ioRedisContainer.getHost()}:${this.#ioRedisContainer.getMappedPort(6379)}`;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
get ioRedisMessageProcessorUrl() {
|
|
157
|
+
return this.#ioRedisMessageProcessorUrl;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
StopRedis = async () => {
|
|
161
|
+
await this.#ioRedisContainer.stop();
|
|
162
|
+
}
|
|
163
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -341,6 +341,29 @@ export class RedisMessageHandler extends TinyEmitter {
|
|
|
341
341
|
return this;
|
|
342
342
|
}
|
|
343
343
|
|
|
344
|
+
emitWithError(event: string, args: JSONObject, responseCb: (response: JSONObject | undefined) => void, errorCb: (error: any) => void): this {
|
|
345
|
+
(async () => {
|
|
346
|
+
try {
|
|
347
|
+
const retVal = await this.#ipcMessageManager?.SendMessage({
|
|
348
|
+
__eventName: event,
|
|
349
|
+
args: [ args ]
|
|
350
|
+
} as IEventPayload);
|
|
351
|
+
// Invoke the response callback
|
|
352
|
+
responseCb(retVal);
|
|
353
|
+
} catch (error) {
|
|
354
|
+
if (this.#options.ignoreEvents) {
|
|
355
|
+
//console.log(chalk.red(`RedisMessageHandler:emit(): ignoreEvents: [${this.#options.ignoreEvents}]`))
|
|
356
|
+
if (this.#options.ignoreEvents.indexOf((error as any).__eventName) !== -1) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
//this.#options.logger.error(chalk.red(`RedisMessageHandler:emit(): Error: [${JSON.stringify(error)}]`));
|
|
361
|
+
errorCb(error);
|
|
362
|
+
}
|
|
363
|
+
})();
|
|
364
|
+
return this;
|
|
365
|
+
}
|
|
366
|
+
|
|
344
367
|
emitex = async(event: string, ...args: any[]): Promise<JSONObject> => {
|
|
345
368
|
return (this.#ipcMessageManager as IPCMessageManager).SendMessage({
|
|
346
369
|
__eventName: event,
|
package/src/testing/app.ts
CHANGED
|
@@ -526,8 +526,18 @@ if (cluster.isPrimary) {
|
|
|
526
526
|
worker.redisMessageHandler.emit('GetSystemInformation', {
|
|
527
527
|
group: 'primary'
|
|
528
528
|
}, (response: any) => {
|
|
529
|
+
console.log(`------------------------------------------------------------------------`);
|
|
529
530
|
console.log(response);
|
|
530
531
|
});
|
|
531
532
|
|
|
533
|
+
worker.redisMessageHandler.emitWithError('GetSystemInformation', {
|
|
534
|
+
group: 'primary'
|
|
535
|
+
}, (response: any) => {
|
|
536
|
+
console.log(`************************************************************************`);
|
|
537
|
+
console.log(response);
|
|
538
|
+
}, (error: any) => {
|
|
539
|
+
console.log(chalk.red(`GetSystemInformation Error: [${error}]`));
|
|
540
|
+
});
|
|
541
|
+
|
|
532
542
|
}, 2000);
|
|
533
543
|
}
|