@medyll/idae-api 0.137.0 → 0.139.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -0
- package/dist/__tests__/README.md +583 -0
- package/dist/__tests__/fixtures/mockData.d.ts +219 -0
- package/dist/__tests__/fixtures/mockData.js +243 -0
- package/dist/__tests__/helpers/testUtils.d.ts +153 -0
- package/dist/__tests__/helpers/testUtils.js +328 -0
- package/dist/client/IdaeApiClient.d.ts +7 -0
- package/dist/client/IdaeApiClient.js +15 -2
- package/dist/client/IdaeApiClientCollection.d.ts +17 -9
- package/dist/client/IdaeApiClientCollection.js +32 -11
- package/dist/client/IdaeApiClientConfig.d.ts +2 -0
- package/dist/client/IdaeApiClientConfig.js +10 -2
- package/dist/client/IdaeApiClientRequest.js +30 -15
- package/dist/config/routeDefinitions.d.ts +8 -0
- package/dist/config/routeDefinitions.js +63 -15
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/openApi/redoc.html +12 -0
- package/dist/openApi/swagger-ui.html +23 -0
- package/dist/server/IdaeApi.d.ts +15 -0
- package/dist/server/IdaeApi.js +94 -22
- package/dist/server/engine/requestDatabaseManager.d.ts +1 -1
- package/dist/server/engine/requestDatabaseManager.js +6 -10
- package/dist/server/engine/routeManager.d.ts +1 -0
- package/dist/server/engine/routeManager.js +33 -17
- package/dist/server/middleware/README.md +46 -0
- package/dist/server/middleware/authMiddleware.d.ts +63 -0
- package/dist/server/middleware/authMiddleware.js +121 -12
- package/dist/server/middleware/authorizationMiddleware.d.ts +18 -0
- package/dist/server/middleware/authorizationMiddleware.js +38 -0
- package/dist/server/middleware/databaseMiddleware.d.ts +6 -1
- package/dist/server/middleware/databaseMiddleware.js +111 -6
- package/dist/server/middleware/docsMiddleware.d.ts +13 -0
- package/dist/server/middleware/docsMiddleware.js +30 -0
- package/dist/server/middleware/healthMiddleware.d.ts +15 -0
- package/dist/server/middleware/healthMiddleware.js +19 -0
- package/dist/server/middleware/mcpMiddleware.d.ts +10 -0
- package/dist/server/middleware/mcpMiddleware.js +14 -0
- package/dist/server/middleware/openApiMiddleware.d.ts +7 -0
- package/dist/server/middleware/openApiMiddleware.js +20 -0
- package/dist/server/middleware/tenantContextMiddleware.d.ts +25 -0
- package/dist/server/middleware/tenantContextMiddleware.js +31 -0
- package/dist/server/middleware/validationLayer.d.ts +8 -0
- package/dist/server/middleware/validationLayer.js +23 -0
- package/dist/server/middleware/validationMiddleware.d.ts +16 -0
- package/dist/server/middleware/validationMiddleware.js +30 -0
- package/package.json +18 -4
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Utilities & Helpers
|
|
3
|
+
* Common patterns and utilities for consistent testing
|
|
4
|
+
*/
|
|
5
|
+
import { vi } from 'vitest';
|
|
6
|
+
import jwt from 'jsonwebtoken';
|
|
7
|
+
/**
|
|
8
|
+
* Generate a mock JWT token for testing
|
|
9
|
+
*/
|
|
10
|
+
export function generateMockToken(payload = {}, secret = 'test-secret', expiresIn = '1h') {
|
|
11
|
+
return jwt.sign({
|
|
12
|
+
username: 'testuser',
|
|
13
|
+
role: 'user',
|
|
14
|
+
...payload
|
|
15
|
+
}, secret, { expiresIn });
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Create a mock Express Request object
|
|
19
|
+
*/
|
|
20
|
+
export function createMockRequest(overrides = {}) {
|
|
21
|
+
return {
|
|
22
|
+
headers: {},
|
|
23
|
+
params: {},
|
|
24
|
+
query: {},
|
|
25
|
+
body: {},
|
|
26
|
+
user: { username: 'testuser', role: 'user' },
|
|
27
|
+
idaeDb: undefined,
|
|
28
|
+
dbName: 'testdb',
|
|
29
|
+
collectionName: 'users',
|
|
30
|
+
connectedCollection: undefined,
|
|
31
|
+
...overrides
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Create a mock Express Response object
|
|
36
|
+
*/
|
|
37
|
+
export function createMockResponse() {
|
|
38
|
+
let statusCode = 200;
|
|
39
|
+
let responseData;
|
|
40
|
+
const headers = new Map();
|
|
41
|
+
return {
|
|
42
|
+
status: (code) => {
|
|
43
|
+
statusCode = code;
|
|
44
|
+
return {
|
|
45
|
+
json: (data) => {
|
|
46
|
+
responseData = data;
|
|
47
|
+
return {};
|
|
48
|
+
},
|
|
49
|
+
send: (data) => {
|
|
50
|
+
responseData = data;
|
|
51
|
+
return {};
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
json: (data) => {
|
|
56
|
+
responseData = data;
|
|
57
|
+
return {};
|
|
58
|
+
},
|
|
59
|
+
send: (data) => {
|
|
60
|
+
responseData = data;
|
|
61
|
+
return {};
|
|
62
|
+
},
|
|
63
|
+
setHeader: (key, value) => {
|
|
64
|
+
headers.set(key, value);
|
|
65
|
+
return {};
|
|
66
|
+
},
|
|
67
|
+
getHeader: (key) => headers.get(key),
|
|
68
|
+
getStatusCode: () => statusCode,
|
|
69
|
+
getResponseData: () => responseData,
|
|
70
|
+
statusCode,
|
|
71
|
+
responseData,
|
|
72
|
+
headers
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Create a mock NextFunction
|
|
77
|
+
*/
|
|
78
|
+
export function createMockNextFunction() {
|
|
79
|
+
return vi.fn((err) => {
|
|
80
|
+
if (err)
|
|
81
|
+
throw err;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Create a complete mock middleware context
|
|
86
|
+
*/
|
|
87
|
+
export function createMockMiddlewareContext(overrides = {}) {
|
|
88
|
+
return {
|
|
89
|
+
req: createMockRequest(overrides.req),
|
|
90
|
+
res: createMockResponse(),
|
|
91
|
+
next: createMockNextFunction()
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Wait for async operations to complete
|
|
96
|
+
*/
|
|
97
|
+
export async function waitFor(condition, timeout = 1000) {
|
|
98
|
+
const start = Date.now();
|
|
99
|
+
while (!condition()) {
|
|
100
|
+
if (Date.now() - start > timeout) {
|
|
101
|
+
throw new Error('Timeout waiting for condition');
|
|
102
|
+
}
|
|
103
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Create a mock IdaeDb collection adapter
|
|
108
|
+
*/
|
|
109
|
+
export function createMockCollection(defaultData = []) {
|
|
110
|
+
return {
|
|
111
|
+
find: vi.fn(async (filter) => {
|
|
112
|
+
return defaultData.filter(item => {
|
|
113
|
+
if (!filter)
|
|
114
|
+
return true;
|
|
115
|
+
for (const key in filter) {
|
|
116
|
+
if (item[key] !== filter[key])
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
return true;
|
|
120
|
+
});
|
|
121
|
+
}),
|
|
122
|
+
findById: vi.fn(async (id) => {
|
|
123
|
+
return defaultData.find((item) => item.id === id) || null;
|
|
124
|
+
}),
|
|
125
|
+
findAll: vi.fn(async () => defaultData),
|
|
126
|
+
create: vi.fn(async (data) => {
|
|
127
|
+
const newItem = { id: String(defaultData.length + 1), ...data };
|
|
128
|
+
defaultData.push(newItem);
|
|
129
|
+
return newItem;
|
|
130
|
+
}),
|
|
131
|
+
update: vi.fn(async (id, data) => {
|
|
132
|
+
const item = defaultData.find((item) => item.id === id);
|
|
133
|
+
if (item) {
|
|
134
|
+
Object.assign(item, data);
|
|
135
|
+
return item;
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}),
|
|
139
|
+
deleteById: vi.fn(async (id) => {
|
|
140
|
+
const index = defaultData.findIndex((item) => item.id === id);
|
|
141
|
+
if (index > -1) {
|
|
142
|
+
defaultData.splice(index, 1);
|
|
143
|
+
}
|
|
144
|
+
}),
|
|
145
|
+
deleteWhere: vi.fn(async (filter) => {
|
|
146
|
+
const before = defaultData.length;
|
|
147
|
+
// Simple filter implementation
|
|
148
|
+
for (const key in filter) {
|
|
149
|
+
const newData = defaultData.filter((item) => item[key] !== filter[key]);
|
|
150
|
+
defaultData.length = 0;
|
|
151
|
+
defaultData.push(...newData);
|
|
152
|
+
}
|
|
153
|
+
return before - defaultData.length;
|
|
154
|
+
})
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Create a mock IdaeDb database adapter
|
|
159
|
+
*/
|
|
160
|
+
export function createMockIdaeDb(collections = {}) {
|
|
161
|
+
return {
|
|
162
|
+
init: vi.fn(async (uri, options) => {
|
|
163
|
+
return {
|
|
164
|
+
db: vi.fn(async (dbName) => {
|
|
165
|
+
return {
|
|
166
|
+
collection: vi.fn((collectionName) => {
|
|
167
|
+
return collections[collectionName] || createMockCollection();
|
|
168
|
+
})
|
|
169
|
+
};
|
|
170
|
+
})
|
|
171
|
+
};
|
|
172
|
+
}),
|
|
173
|
+
collection: vi.fn((collectionName) => {
|
|
174
|
+
return collections[collectionName] || createMockCollection();
|
|
175
|
+
})
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Create a mock HTTP fetch response
|
|
180
|
+
*/
|
|
181
|
+
export function createMockFetchResponse(data = {}, options = {}) {
|
|
182
|
+
return Promise.resolve({
|
|
183
|
+
ok: options.ok !== false,
|
|
184
|
+
status: options.status || 200,
|
|
185
|
+
headers: new Map(options.headers || []),
|
|
186
|
+
json: async () => data,
|
|
187
|
+
text: async () => JSON.stringify(data),
|
|
188
|
+
blob: async () => new Blob([JSON.stringify(data)]),
|
|
189
|
+
clone: () => createMockFetchResponse(data, options)
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Assert that a middleware was called with specific arguments
|
|
194
|
+
*/
|
|
195
|
+
export function assertMiddlewareCalled(middleware, argIndex, expected) {
|
|
196
|
+
const call = middleware.mock.calls[0];
|
|
197
|
+
if (!call) {
|
|
198
|
+
throw new Error('Middleware was not called');
|
|
199
|
+
}
|
|
200
|
+
const actual = call[argIndex];
|
|
201
|
+
if (typeof expected === 'object') {
|
|
202
|
+
if (!Object.keys(expected).every(key => actual[key] === expected[key])) {
|
|
203
|
+
throw new Error(`Middleware argument ${argIndex} does not match expected`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
else if (actual !== expected) {
|
|
207
|
+
throw new Error(`Middleware argument ${argIndex} does not match expected`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Mock an external module
|
|
212
|
+
*/
|
|
213
|
+
export function mockModule(moduleName, implementation) {
|
|
214
|
+
return vi.mock(moduleName, () => implementation);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Restore all mocks
|
|
218
|
+
*/
|
|
219
|
+
export function resetAllMocks() {
|
|
220
|
+
vi.clearAllMocks();
|
|
221
|
+
vi.resetAllMocks();
|
|
222
|
+
vi.restoreAllMocks();
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Create a test database URI
|
|
226
|
+
*/
|
|
227
|
+
export function createTestDatabaseUri(dbType = 'mongodb', dbName = 'testdb') {
|
|
228
|
+
if (dbType === 'mongodb') {
|
|
229
|
+
return `mongodb://localhost:27017/${dbName}`;
|
|
230
|
+
}
|
|
231
|
+
else if (dbType === 'mysql') {
|
|
232
|
+
return `mysql://localhost:3306/${dbName}`;
|
|
233
|
+
}
|
|
234
|
+
return `${dbType}://localhost/${dbName}`;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Extract bearer token from Authorization header
|
|
238
|
+
*/
|
|
239
|
+
export function extractBearerToken(authHeader) {
|
|
240
|
+
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
return authHeader.substring(7);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Validate token format
|
|
247
|
+
*/
|
|
248
|
+
export function isValidTokenFormat(token) {
|
|
249
|
+
const parts = token.split('.');
|
|
250
|
+
return parts.length === 3;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Create assertion helpers for common patterns
|
|
254
|
+
*/
|
|
255
|
+
export const testAssertions = {
|
|
256
|
+
/**
|
|
257
|
+
* Assert middleware sets status code
|
|
258
|
+
*/
|
|
259
|
+
statusCode: (res, expectedCode) => {
|
|
260
|
+
if (res.statusCode !== expectedCode && res.status) {
|
|
261
|
+
// Response might be from mock with chained methods
|
|
262
|
+
throw new Error(`Expected status ${expectedCode}, got ${res.statusCode}`);
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
/**
|
|
266
|
+
* Assert middleware sets Authorization header
|
|
267
|
+
*/
|
|
268
|
+
hasAuthHeader: (req) => {
|
|
269
|
+
if (!req.headers?.authorization) {
|
|
270
|
+
throw new Error('Authorization header not set');
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
/**
|
|
274
|
+
* Assert response has error
|
|
275
|
+
*/
|
|
276
|
+
hasError: (res) => {
|
|
277
|
+
if (!res.responseData?.error) {
|
|
278
|
+
throw new Error('Response does not contain error');
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
/**
|
|
282
|
+
* Assert response has success data
|
|
283
|
+
*/
|
|
284
|
+
hasData: (res) => {
|
|
285
|
+
if (!res.responseData) {
|
|
286
|
+
throw new Error('Response does not contain data');
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
/**
|
|
291
|
+
* Performance testing helper
|
|
292
|
+
*/
|
|
293
|
+
export async function measurePerformance(fn, iterations = 100) {
|
|
294
|
+
const times = [];
|
|
295
|
+
for (let i = 0; i < iterations; i++) {
|
|
296
|
+
const start = performance.now();
|
|
297
|
+
await fn();
|
|
298
|
+
const end = performance.now();
|
|
299
|
+
times.push(end - start);
|
|
300
|
+
}
|
|
301
|
+
return {
|
|
302
|
+
average: times.reduce((a, b) => a + b) / times.length,
|
|
303
|
+
min: Math.min(...times),
|
|
304
|
+
max: Math.max(...times),
|
|
305
|
+
times
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Test data sanitizer - remove sensitive info before assertions
|
|
310
|
+
*/
|
|
311
|
+
export function sanitizeForAssertion(data, keysToRemove = ['password', 'token', 'secret']) {
|
|
312
|
+
if (typeof data !== 'object' || data === null) {
|
|
313
|
+
return data;
|
|
314
|
+
}
|
|
315
|
+
const sanitized = Array.isArray(data) ? [...data] : { ...data };
|
|
316
|
+
const remove = (obj) => {
|
|
317
|
+
for (const key in obj) {
|
|
318
|
+
if (keysToRemove.includes(key)) {
|
|
319
|
+
delete obj[key];
|
|
320
|
+
}
|
|
321
|
+
else if (typeof obj[key] === 'object') {
|
|
322
|
+
remove(obj[key]);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
remove(sanitized);
|
|
327
|
+
return sanitized;
|
|
328
|
+
}
|
|
@@ -3,12 +3,19 @@ import { IdaeApiClientCollection } from "./IdaeApiClientCollection.js";
|
|
|
3
3
|
import { IdaeApiClientRequest } from "./IdaeApiClientRequest.js";
|
|
4
4
|
type IdaeApiClientRequestParams<T = any> = Record<string, T>;
|
|
5
5
|
declare class IdaeApiClient {
|
|
6
|
+
private static instance;
|
|
6
7
|
private clientConfig;
|
|
7
8
|
private _request;
|
|
8
9
|
constructor(clientConfig?: IdaeApiClientConfigCoreOptions & {
|
|
9
10
|
baseUrl?: string;
|
|
10
11
|
});
|
|
12
|
+
static getInstance(clientConfig?: IdaeApiClientConfigCoreOptions & {
|
|
13
|
+
baseUrl?: string;
|
|
14
|
+
}): IdaeApiClient;
|
|
11
15
|
get request(): IdaeApiClientRequest;
|
|
16
|
+
setOptions(options?: IdaeApiClientConfigCoreOptions & {
|
|
17
|
+
baseUrl?: string;
|
|
18
|
+
}): void;
|
|
12
19
|
getDbList(): Promise<Response>;
|
|
13
20
|
getCollections(dbName: string): Promise<Response>;
|
|
14
21
|
db(dbName: string): {
|
|
@@ -8,9 +8,21 @@ class IdaeApiClient {
|
|
|
8
8
|
this.clientConfig.setOptions(clientConfig);
|
|
9
9
|
this._request = new IdaeApiClientRequest(this.clientConfig);
|
|
10
10
|
}
|
|
11
|
+
static getInstance(clientConfig) {
|
|
12
|
+
if (!IdaeApiClient.instance) {
|
|
13
|
+
IdaeApiClient.instance = new IdaeApiClient(clientConfig);
|
|
14
|
+
}
|
|
15
|
+
else if (clientConfig) {
|
|
16
|
+
IdaeApiClientConfig.setOptions(clientConfig);
|
|
17
|
+
}
|
|
18
|
+
return IdaeApiClient.instance;
|
|
19
|
+
}
|
|
11
20
|
get request() {
|
|
12
21
|
return this._request;
|
|
13
22
|
}
|
|
23
|
+
setOptions(options) {
|
|
24
|
+
this.clientConfig.setOptions(options);
|
|
25
|
+
}
|
|
14
26
|
async getDbList() {
|
|
15
27
|
return this._request.doRequest({ routeNamespace: "methods/dbs" });
|
|
16
28
|
}
|
|
@@ -22,15 +34,16 @@ class IdaeApiClient {
|
|
|
22
34
|
}
|
|
23
35
|
db(dbName) {
|
|
24
36
|
return {
|
|
25
|
-
collection: (collectionName) => new IdaeApiClientCollection(this.clientConfig, dbName, collectionName),
|
|
37
|
+
collection: (collectionName) => new IdaeApiClientCollection(this.clientConfig, dbName, collectionName, this._request),
|
|
26
38
|
getCollections: () => this.getCollections(dbName),
|
|
27
39
|
};
|
|
28
40
|
}
|
|
29
41
|
collection(collectionName, dbName) {
|
|
30
42
|
dbName = dbName || this.clientConfig.defaultDb;
|
|
31
|
-
return new IdaeApiClientCollection(this.clientConfig, dbName, collectionName);
|
|
43
|
+
return new IdaeApiClientCollection(this.clientConfig, dbName, collectionName, this._request);
|
|
32
44
|
}
|
|
33
45
|
}
|
|
46
|
+
IdaeApiClient.instance = null;
|
|
34
47
|
// fonctionne
|
|
35
48
|
export { IdaeApiClient };
|
|
36
49
|
// ne fonctionne pas quand multiligne
|
|
@@ -1,15 +1,23 @@
|
|
|
1
|
-
import { IdaeApiClient } from "./IdaeApiClient.js";
|
|
2
1
|
import type { IdaeApiClientRequestParams } from "./IdaeApiClient.js";
|
|
3
|
-
import type {
|
|
2
|
+
import type { IdaeApiClientConfigCore } from "./IdaeApiClientConfig.js";
|
|
3
|
+
import { IdaeApiClientRequest } from "./IdaeApiClientRequest.js";
|
|
4
4
|
import type { IdaeDbApiMethods } from "@medyll/idae-db";
|
|
5
|
-
declare class IdaeApiClientCollection
|
|
5
|
+
declare class IdaeApiClientCollection implements IdaeDbApiMethods<object> {
|
|
6
6
|
private meta;
|
|
7
|
-
|
|
7
|
+
private requestClient;
|
|
8
|
+
constructor(clientConfig: IdaeApiClientConfigCore, dbName: string, collectionName: string, requestClient?: IdaeApiClientRequest);
|
|
8
9
|
find<T extends object>(params?: IdaeApiClientRequestParams): Promise<T[]>;
|
|
9
|
-
findById<T>(id: string): Promise<
|
|
10
|
-
create<T>(body: T): Promise<
|
|
11
|
-
update<T>(id: string, body: T): Promise<
|
|
12
|
-
deleteById<T>(id: string): Promise<
|
|
13
|
-
deleteManyByQuery<T>(params: IdaeApiClientRequestParams): Promise<
|
|
10
|
+
findById<T>(id: string): Promise<T[]>;
|
|
11
|
+
create<T>(body: T): Promise<T>;
|
|
12
|
+
update<T>(id: string, body: T): Promise<T>;
|
|
13
|
+
deleteById<T>(id: string): Promise<T>;
|
|
14
|
+
deleteManyByQuery<T>(params: IdaeApiClientRequestParams): Promise<T[]>;
|
|
15
|
+
createIndex(fieldOrSpec: unknown, options?: unknown): Promise<string>;
|
|
16
|
+
findOne<T>(params?: any): Promise<T | null>;
|
|
17
|
+
updateWhere(params?: any, update?: any): Promise<void>;
|
|
18
|
+
deleteWhere(params: any): Promise<{
|
|
19
|
+
deletedCount?: number;
|
|
20
|
+
}>;
|
|
21
|
+
transaction<TResult>(callback: (session: unknown) => Promise<TResult>): Promise<TResult>;
|
|
14
22
|
}
|
|
15
23
|
export { IdaeApiClientCollection };
|
|
@@ -1,53 +1,74 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
super(clientConfig);
|
|
1
|
+
import { IdaeApiClientRequest } from "./IdaeApiClientRequest.js";
|
|
2
|
+
class IdaeApiClientCollection {
|
|
3
|
+
constructor(clientConfig, dbName, collectionName, requestClient) {
|
|
4
|
+
this.requestClient = requestClient ?? new IdaeApiClientRequest(clientConfig);
|
|
6
5
|
this.meta = {
|
|
7
6
|
dbName,
|
|
8
7
|
collectionName,
|
|
9
8
|
};
|
|
10
9
|
}
|
|
11
10
|
async find(params) {
|
|
12
|
-
|
|
11
|
+
const response = await this.requestClient.doRequest({
|
|
13
12
|
...this.meta,
|
|
14
13
|
params,
|
|
15
14
|
});
|
|
15
|
+
return await response.json();
|
|
16
16
|
}
|
|
17
17
|
async findById(id) {
|
|
18
|
-
|
|
18
|
+
const response = await this.requestClient.doRequest({
|
|
19
19
|
...this.meta,
|
|
20
20
|
slug: id,
|
|
21
21
|
});
|
|
22
|
+
return await response.json();
|
|
22
23
|
}
|
|
23
24
|
async create(body) {
|
|
24
|
-
|
|
25
|
+
const response = await this.requestClient.doRequest({
|
|
25
26
|
method: "POST",
|
|
26
27
|
...this.meta,
|
|
27
28
|
body,
|
|
28
29
|
});
|
|
30
|
+
return await response.json();
|
|
29
31
|
}
|
|
30
32
|
async update(id, body) {
|
|
31
|
-
|
|
33
|
+
const response = await this.requestClient.doRequest({
|
|
32
34
|
method: "PUT",
|
|
33
35
|
...this.meta,
|
|
34
36
|
body,
|
|
35
37
|
slug: id,
|
|
36
38
|
});
|
|
39
|
+
return await response.json();
|
|
37
40
|
}
|
|
38
41
|
async deleteById(id) {
|
|
39
|
-
|
|
42
|
+
const response = await this.requestClient.doRequest({
|
|
40
43
|
method: "DELETE",
|
|
41
44
|
...this.meta,
|
|
42
45
|
slug: id,
|
|
43
46
|
});
|
|
47
|
+
return await response.json();
|
|
44
48
|
}
|
|
45
49
|
async deleteManyByQuery(params) {
|
|
46
|
-
|
|
50
|
+
const response = await this.requestClient.doRequest({
|
|
47
51
|
method: "DELETE",
|
|
48
52
|
...this.meta,
|
|
49
53
|
params,
|
|
50
54
|
});
|
|
55
|
+
return await response.json();
|
|
56
|
+
}
|
|
57
|
+
// Stubs for missing interface methods
|
|
58
|
+
async createIndex(fieldOrSpec, options) {
|
|
59
|
+
throw new Error("Not implemented");
|
|
60
|
+
}
|
|
61
|
+
async findOne(params) {
|
|
62
|
+
throw new Error("Not implemented");
|
|
63
|
+
}
|
|
64
|
+
async updateWhere(params, update) {
|
|
65
|
+
throw new Error("Not implemented");
|
|
66
|
+
}
|
|
67
|
+
async deleteWhere(params) {
|
|
68
|
+
throw new Error("Not implemented");
|
|
69
|
+
}
|
|
70
|
+
async transaction(callback) {
|
|
71
|
+
throw new Error("Not implemented");
|
|
51
72
|
}
|
|
52
73
|
}
|
|
53
74
|
export { IdaeApiClientCollection };
|
|
@@ -4,6 +4,7 @@ export interface IdaeApiClientConfigCoreOptions {
|
|
|
4
4
|
method: 'http' | 'https';
|
|
5
5
|
defaultDb: string;
|
|
6
6
|
separator: string;
|
|
7
|
+
token?: string;
|
|
7
8
|
}
|
|
8
9
|
export declare class IdaeApiClientConfigCore {
|
|
9
10
|
private static instance;
|
|
@@ -18,6 +19,7 @@ export declare class IdaeApiClientConfigCore {
|
|
|
18
19
|
get port(): number;
|
|
19
20
|
get method(): 'http' | 'https';
|
|
20
21
|
get separator(): string;
|
|
22
|
+
get token(): string | undefined;
|
|
21
23
|
get defaultDb(): string;
|
|
22
24
|
}
|
|
23
25
|
export declare const IdaeApiClientConfig: IdaeApiClientConfigCore;
|
|
@@ -5,7 +5,8 @@ export class IdaeApiClientConfigCore {
|
|
|
5
5
|
port: 3000,
|
|
6
6
|
method: 'https',
|
|
7
7
|
defaultDb: 'idaenext_sitebase_app',
|
|
8
|
-
separator: '//'
|
|
8
|
+
separator: '//',
|
|
9
|
+
token: undefined,
|
|
9
10
|
};
|
|
10
11
|
}
|
|
11
12
|
static getInstance() {
|
|
@@ -15,7 +16,11 @@ export class IdaeApiClientConfigCore {
|
|
|
15
16
|
return IdaeApiClientConfigCore.instance;
|
|
16
17
|
}
|
|
17
18
|
setOptions(options = {}) {
|
|
18
|
-
|
|
19
|
+
const normalized = { ...options };
|
|
20
|
+
if (options?.protocol) {
|
|
21
|
+
normalized.method = options.protocol;
|
|
22
|
+
}
|
|
23
|
+
this.options = { ...this.options, ...normalized };
|
|
19
24
|
this._baseUrl = this.forgeBaseUrl();
|
|
20
25
|
}
|
|
21
26
|
get baseUrl() {
|
|
@@ -36,6 +41,9 @@ export class IdaeApiClientConfigCore {
|
|
|
36
41
|
get separator() {
|
|
37
42
|
return this.options.separator;
|
|
38
43
|
}
|
|
44
|
+
get token() {
|
|
45
|
+
return this.options.token;
|
|
46
|
+
}
|
|
39
47
|
get defaultDb() {
|
|
40
48
|
return this.options.defaultDb;
|
|
41
49
|
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
// packages\idae-api\src\lib\client\IdaeApiClientRequest.ts
|
|
2
2
|
import { IdaeApiClientConfigCore } from "./IdaeApiClientConfig.js";
|
|
3
|
-
import { encode } from "querystring";
|
|
4
3
|
class IdaeApiClientRequest {
|
|
5
4
|
constructor(clientConfig) {
|
|
6
5
|
this.clientConfig = clientConfig;
|
|
7
|
-
this.baseUrl =
|
|
6
|
+
this.baseUrl =
|
|
7
|
+
this.clientConfig.baseUrl ||
|
|
8
|
+
`${this.clientConfig.method}:${this.clientConfig.separator}${this.clientConfig.host}:${this.clientConfig.port}`;
|
|
8
9
|
}
|
|
9
|
-
async doRequest({ baseUrl = this.baseUrl
|
|
10
|
+
async doRequest({ baseUrl = this.clientConfig.baseUrl ||
|
|
11
|
+
`${this.clientConfig.method}:${this.clientConfig.separator}${this.clientConfig.host}:${this.clientConfig.port}`, method = "GET", body, headers = {
|
|
10
12
|
"Content-Type": "application/json",
|
|
11
13
|
}, dbName = this.clientConfig.defaultDb, collectionName, slug, params, routeNamespace, }) {
|
|
12
14
|
const url = this.buildUrl({
|
|
@@ -15,16 +17,25 @@ class IdaeApiClientRequest {
|
|
|
15
17
|
collectionName,
|
|
16
18
|
slug,
|
|
17
19
|
params,
|
|
18
|
-
})
|
|
19
|
-
const
|
|
20
|
+
});
|
|
21
|
+
const authHeader = this.clientConfig.token
|
|
22
|
+
? { Authorization: `Bearer ${this.clientConfig.token}` }
|
|
23
|
+
: {};
|
|
24
|
+
const response = (await fetch(`${baseUrl}${url}`, {
|
|
20
25
|
method,
|
|
21
|
-
headers,
|
|
26
|
+
headers: Object.assign({}, headers, authHeader),
|
|
22
27
|
body: body ? JSON.stringify(body) : undefined,
|
|
23
|
-
})
|
|
28
|
+
})) || {
|
|
29
|
+
ok: true,
|
|
30
|
+
json: async () => ({}),
|
|
31
|
+
};
|
|
24
32
|
if (!response.ok) {
|
|
25
33
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
26
34
|
}
|
|
27
35
|
try {
|
|
36
|
+
if (typeof response.json !== "function") {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
28
39
|
return response.json();
|
|
29
40
|
}
|
|
30
41
|
catch (e) {
|
|
@@ -33,16 +44,20 @@ class IdaeApiClientRequest {
|
|
|
33
44
|
}
|
|
34
45
|
}
|
|
35
46
|
buildUrl({ dbName, collectionName, slug, params, routeNamespace, }) {
|
|
36
|
-
const
|
|
47
|
+
const parts = [];
|
|
37
48
|
if (routeNamespace)
|
|
38
|
-
|
|
39
|
-
if (dbName
|
|
40
|
-
|
|
49
|
+
parts.push(routeNamespace);
|
|
50
|
+
if (dbName)
|
|
51
|
+
parts.push(dbName);
|
|
52
|
+
if (collectionName)
|
|
53
|
+
parts.push(collectionName);
|
|
41
54
|
if (slug)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
55
|
+
parts.push(slug);
|
|
56
|
+
const path = `/${parts.join("/")}`.replace(/\/\/+/, "/");
|
|
57
|
+
const query = params
|
|
58
|
+
? `?encoded=true¶ms=${encodeURIComponent(JSON.stringify(params))}`
|
|
59
|
+
: "";
|
|
60
|
+
return `${path}${query}`;
|
|
46
61
|
}
|
|
47
62
|
}
|
|
48
63
|
export { IdaeApiClientRequest, };
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import type { IdaeDbAdapter } from "@medyll/idae-db";
|
|
2
|
+
import { z } from "zod";
|
|
2
3
|
type RouteHandler = (service: IdaeDbAdapter<object>, params: any, body?: any, query?: any) => Promise<any>;
|
|
4
|
+
import type { AuthorizationOptions } from "../server/middleware/authorizationMiddleware.js";
|
|
3
5
|
export interface RouteDefinition {
|
|
4
6
|
method: string | string[];
|
|
5
7
|
path: string;
|
|
6
8
|
handler: RouteHandler;
|
|
7
9
|
disabled?: boolean;
|
|
8
10
|
requiresAuth?: boolean;
|
|
11
|
+
authorization?: AuthorizationOptions;
|
|
12
|
+
validation?: {
|
|
13
|
+
bodySchema?: z.ZodSchema<any>;
|
|
14
|
+
querySchema?: z.ZodSchema<any>;
|
|
15
|
+
paramsSchema?: z.ZodSchema<any>;
|
|
16
|
+
};
|
|
9
17
|
}
|
|
10
18
|
export declare const routes: RouteDefinition[];
|
|
11
19
|
export {};
|