@openhi/constructs 0.0.83 → 0.0.85
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/package.json +6 -5
- package/scripts/generate-route-tests.mjs +0 -227
package/package.json
CHANGED
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
"constructs": "10.6.0"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@aws-sdk/client-dynamodb": "^3.
|
|
35
|
-
"@aws-sdk/client-eventbridge": "^3.
|
|
36
|
-
"@aws-sdk/client-s3": "^3.
|
|
37
|
-
"@aws-sdk/client-ssm": "^3.
|
|
34
|
+
"@aws-sdk/client-dynamodb": "^3.1033.0",
|
|
35
|
+
"@aws-sdk/client-eventbridge": "^3.1033.0",
|
|
36
|
+
"@aws-sdk/client-s3": "^3.1033.0",
|
|
37
|
+
"@aws-sdk/client-ssm": "^3.1033.0",
|
|
38
38
|
"@codedrifters/utils": "^0.0.20",
|
|
39
39
|
"@codegenie/serverless-express": "^4.17.1",
|
|
40
40
|
"@types/aws-lambda": "^8.10.161",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"publishConfig": {
|
|
61
61
|
"access": "public"
|
|
62
62
|
},
|
|
63
|
-
"version": "0.0.
|
|
63
|
+
"version": "0.0.85",
|
|
64
64
|
"types": "lib/index.d.ts",
|
|
65
65
|
"//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"pnpm exec projen\".",
|
|
66
66
|
"scripts": {
|
|
@@ -78,6 +78,7 @@
|
|
|
78
78
|
"reset": "projen reset",
|
|
79
79
|
"run-lambda-local": "projen run-lambda-local",
|
|
80
80
|
"test": "projen test",
|
|
81
|
+
"test:coverage": "projen test:coverage",
|
|
81
82
|
"test:watch": "projen test:watch",
|
|
82
83
|
"unbump": "projen unbump",
|
|
83
84
|
"upgrade": "projen upgrade",
|
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Generate one route test file per FHIR type that has rest-api routes.
|
|
4
|
-
* Run from constructs package: node scripts/generate-route-tests.mjs
|
|
5
|
-
* Ensures routes/data/<folder>/<folder>.test.ts exists for each type.
|
|
6
|
-
*/
|
|
7
|
-
import fs from "node:fs";
|
|
8
|
-
import path from "node:path";
|
|
9
|
-
import { fileURLToPath } from "node:url";
|
|
10
|
-
|
|
11
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
-
const ROOT = path.resolve(__dirname, "..");
|
|
13
|
-
const ROUTES_DATA = path.join(ROOT, "src/data/rest-api/routes/data");
|
|
14
|
-
const OPERATIONS = path.join(ROOT, "src/data/operations/data");
|
|
15
|
-
|
|
16
|
-
const dirs = fs.readdirSync(OPERATIONS).filter((d) => {
|
|
17
|
-
const p = path.join(OPERATIONS, d);
|
|
18
|
-
return fs.statSync(p).isDirectory() && fs.existsSync(path.join(p, `${d}-create-operation.ts`));
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
const types = [];
|
|
22
|
-
for (const dir of dirs) {
|
|
23
|
-
const createPath = path.join(OPERATIONS, dir, `${dir}-create-operation.ts`);
|
|
24
|
-
const line = fs.readFileSync(createPath, "utf8").split("\n")[0];
|
|
25
|
-
const match = line.match(/import type \{ ([^}]+) \}/);
|
|
26
|
-
if (!match) continue;
|
|
27
|
-
const imports = match[1].split(",").map((s) => s.trim());
|
|
28
|
-
const type = imports.find((t) => t !== "Meta");
|
|
29
|
-
if (type) types.push({ type, folder: dir });
|
|
30
|
-
}
|
|
31
|
-
types.sort((a, b) => a.type.localeCompare(b.type));
|
|
32
|
-
|
|
33
|
-
function basePathKey(type) {
|
|
34
|
-
return type.toUpperCase().replace(/-/g, "");
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function pluralRoute(type) {
|
|
38
|
-
return type + "s";
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function testContent(type, folder) {
|
|
42
|
-
const key = basePathKey(type);
|
|
43
|
-
const plural = pluralRoute(type);
|
|
44
|
-
const createOp = `create${type}Operation`;
|
|
45
|
-
const updateOp = `update${type}Operation`;
|
|
46
|
-
const listOp = `list${plural}Operation`;
|
|
47
|
-
const getByIdOp = `get${type}ByIdOperation`;
|
|
48
|
-
const deleteOp = `delete${type}Operation`;
|
|
49
|
-
const createRoute = `create${type}Route`;
|
|
50
|
-
const updateRoute = `update${type}Route`;
|
|
51
|
-
const listRoute = `list${plural}Route`;
|
|
52
|
-
const getByIdRoute = `get${type}ByIdRoute`;
|
|
53
|
-
const deleteRoute = `delete${type}Route`;
|
|
54
|
-
|
|
55
|
-
return `import type { Request, Response } from "express";
|
|
56
|
-
import { NotFoundError } from "../../../../errors";
|
|
57
|
-
import { ${createRoute} } from "./${folder}-create-route";
|
|
58
|
-
import { ${deleteRoute} } from "./${folder}-delete-route";
|
|
59
|
-
import { ${getByIdRoute} } from "./${folder}-get-by-id-route";
|
|
60
|
-
import { ${listRoute} } from "./${folder}-list-route";
|
|
61
|
-
import { ${updateRoute} } from "./${folder}-update-route";
|
|
62
|
-
import { create${type}Operation } from "../../../../operations/data/${folder}/${folder}-create-operation";
|
|
63
|
-
import { delete${type}Operation } from "../../../../operations/data/${folder}/${folder}-delete-operation";
|
|
64
|
-
import { get${type}ByIdOperation } from "../../../../operations/data/${folder}/${folder}-get-by-id-operation";
|
|
65
|
-
import { list${plural}Operation } from "../../../../operations/data/${folder}/${folder}-list-operation";
|
|
66
|
-
import { update${type}Operation } from "../../../../operations/data/${folder}/${folder}-update-operation";
|
|
67
|
-
|
|
68
|
-
jest.mock("../../../../operations/data/${folder}/${folder}-create-operation");
|
|
69
|
-
jest.mock("../../../../operations/data/${folder}/${folder}-update-operation");
|
|
70
|
-
jest.mock("../../../../operations/data/${folder}/${folder}-list-operation");
|
|
71
|
-
jest.mock("../../../../operations/data/${folder}/${folder}-get-by-id-operation");
|
|
72
|
-
jest.mock("../../../../operations/data/${folder}/${folder}-delete-operation");
|
|
73
|
-
|
|
74
|
-
const defaultContext = {
|
|
75
|
-
tenantId: "tenant-1",
|
|
76
|
-
workspaceId: "ws-1",
|
|
77
|
-
date: "2026-02-26T12:00:00.000Z",
|
|
78
|
-
actorId: "actor-1",
|
|
79
|
-
actorName: "Test Actor",
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
function mockRes(): Response {
|
|
83
|
-
const res = {} as Response;
|
|
84
|
-
res.status = jest.fn().mockReturnThis();
|
|
85
|
-
res.json = jest.fn().mockReturnThis();
|
|
86
|
-
res.location = jest.fn().mockReturnThis();
|
|
87
|
-
res.send = jest.fn().mockReturnThis();
|
|
88
|
-
return res;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function mockReq(overrides: Partial<{ params: Record<string, string>; body: unknown }> = {}): Request {
|
|
92
|
-
const req = {
|
|
93
|
-
openhiContext: defaultContext,
|
|
94
|
-
params: {},
|
|
95
|
-
body: {},
|
|
96
|
-
...overrides,
|
|
97
|
-
} as unknown as Request;
|
|
98
|
-
return req;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
describe("${type} routes", () => {
|
|
102
|
-
beforeEach(() => {
|
|
103
|
-
jest.clearAllMocks();
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
describe("${createRoute}", () => {
|
|
107
|
-
it("returns 201 and location when create succeeds", async () => {
|
|
108
|
-
const id = "01HXYZ";
|
|
109
|
-
(create${type}Operation as jest.Mock).mockResolvedValue({
|
|
110
|
-
id,
|
|
111
|
-
resource: { resourceType: "${type}", id },
|
|
112
|
-
});
|
|
113
|
-
const req = mockReq({
|
|
114
|
-
body: { resourceType: "${type}" },
|
|
115
|
-
});
|
|
116
|
-
const res = mockRes();
|
|
117
|
-
|
|
118
|
-
await ${createRoute}(req, res);
|
|
119
|
-
|
|
120
|
-
expect(create${type}Operation).toHaveBeenCalledWith({
|
|
121
|
-
context: defaultContext,
|
|
122
|
-
body: expect.objectContaining({ resourceType: "${type}" }),
|
|
123
|
-
});
|
|
124
|
-
expect(res.status).toHaveBeenCalledWith(201);
|
|
125
|
-
expect(res.location).toHaveBeenCalled();
|
|
126
|
-
expect(res.json).toHaveBeenCalledWith(expect.objectContaining({ id }));
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
describe("${listRoute}", () => {
|
|
131
|
-
it("returns bundle when list succeeds", async () => {
|
|
132
|
-
(list${plural}Operation as jest.Mock).mockResolvedValue({
|
|
133
|
-
entries: [{ id: "01HXYZ", resource: { resourceType: "${type}", id: "01HXYZ" } }],
|
|
134
|
-
});
|
|
135
|
-
const req = mockReq();
|
|
136
|
-
const res = mockRes();
|
|
137
|
-
|
|
138
|
-
await ${listRoute}(req, res);
|
|
139
|
-
|
|
140
|
-
expect(list${plural}Operation).toHaveBeenCalledWith({ context: defaultContext });
|
|
141
|
-
expect(res.json).toHaveBeenCalledWith(
|
|
142
|
-
expect.objectContaining({
|
|
143
|
-
resourceType: "Bundle",
|
|
144
|
-
type: "searchset",
|
|
145
|
-
entry: expect.any(Array),
|
|
146
|
-
}),
|
|
147
|
-
);
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
describe("${getByIdRoute}", () => {
|
|
152
|
-
it("returns 404 when resource not found", async () => {
|
|
153
|
-
(get${type}ByIdOperation as jest.Mock).mockRejectedValue(new NotFoundError("not found"));
|
|
154
|
-
const req = mockReq({ params: { id: "01HXYZ" } });
|
|
155
|
-
const res = mockRes();
|
|
156
|
-
|
|
157
|
-
await ${getByIdRoute}(req, res);
|
|
158
|
-
|
|
159
|
-
expect(get${type}ByIdOperation).toHaveBeenCalledWith({
|
|
160
|
-
context: defaultContext,
|
|
161
|
-
id: "01HXYZ",
|
|
162
|
-
});
|
|
163
|
-
expect(res.status).toHaveBeenCalledWith(404);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it("returns resource when found", async () => {
|
|
167
|
-
const resource = { resourceType: "${type}", id: "01HXYZ" };
|
|
168
|
-
(get${type}ByIdOperation as jest.Mock).mockResolvedValue({ resource });
|
|
169
|
-
const req = mockReq({ params: { id: "01HXYZ" } });
|
|
170
|
-
const res = mockRes();
|
|
171
|
-
|
|
172
|
-
await ${getByIdRoute}(req, res);
|
|
173
|
-
|
|
174
|
-
expect(res.json).toHaveBeenCalledWith(resource);
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
describe("${updateRoute}", () => {
|
|
179
|
-
it("returns 200 and resource when update succeeds", async () => {
|
|
180
|
-
const resource = { resourceType: "${type}", id: "01HXYZ" };
|
|
181
|
-
(update${type}Operation as jest.Mock).mockResolvedValue({ resource });
|
|
182
|
-
const req = mockReq({ params: { id: "01HXYZ" }, body: { resourceType: "${type}" } });
|
|
183
|
-
const res = mockRes();
|
|
184
|
-
|
|
185
|
-
await ${updateRoute}(req, res);
|
|
186
|
-
|
|
187
|
-
expect(update${type}Operation).toHaveBeenCalledWith({
|
|
188
|
-
context: defaultContext,
|
|
189
|
-
id: "01HXYZ",
|
|
190
|
-
body: expect.objectContaining({ resourceType: "${type}", id: "01HXYZ" }),
|
|
191
|
-
});
|
|
192
|
-
expect(res.json).toHaveBeenCalledWith(resource);
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
describe("${deleteRoute}", () => {
|
|
197
|
-
it("returns 204 when delete succeeds", async () => {
|
|
198
|
-
(delete${type}Operation as jest.Mock).mockResolvedValue(undefined);
|
|
199
|
-
const req = mockReq({ params: { id: "01HXYZ" } });
|
|
200
|
-
const res = mockRes();
|
|
201
|
-
|
|
202
|
-
await ${deleteRoute}(req, res);
|
|
203
|
-
|
|
204
|
-
expect(delete${type}Operation).toHaveBeenCalledWith({
|
|
205
|
-
context: defaultContext,
|
|
206
|
-
id: "01HXYZ",
|
|
207
|
-
});
|
|
208
|
-
expect(res.status).toHaveBeenCalledWith(204);
|
|
209
|
-
expect(res.send).toHaveBeenCalled();
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
`;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
for (const { type, folder } of types) {
|
|
217
|
-
const routeDir = path.join(ROUTES_DATA, folder);
|
|
218
|
-
const testFile = path.join(routeDir, `${folder}.test.ts`);
|
|
219
|
-
if (fs.existsSync(testFile)) {
|
|
220
|
-
console.log(`Skip ${type} (test exists)`);
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
fs.writeFileSync(testFile, testContent(type, folder), "utf8");
|
|
224
|
-
console.log(`Generated ${folder}.test.ts for ${type}`);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
console.log(`Total: ${types.length} route types; ensured tests for all.`);
|