@xitkov/core 2.4.9 → 2.5.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/lib/services/register-services.d.ts +13 -0
- package/lib/services/register-services.js +44 -18
- package/lib/services/register-services.js.map +1 -1
- package/lib/services/service.d.ts +8 -0
- package/lib/services/service.js.map +1 -1
- package/package.json +2 -1
- package/src/services/register-services.ts +48 -18
- package/src/services/service.ts +9 -0
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import type { Application } from 'express';
|
|
2
2
|
import { Config, IAuth } from '..';
|
|
3
|
+
type Functions = 'find' | 'get' | 'create' | 'update' | 'patch' | 'remove';
|
|
4
|
+
/**
|
|
5
|
+
* Validate one method's input and return the coerced result.
|
|
6
|
+
*
|
|
7
|
+
* Joi converts as well as checks: `"25"` becomes `25`, and a declared default
|
|
8
|
+
* fills a missing field. The caller must therefore use the returned value —
|
|
9
|
+
* discarding it would validate the request and then hand the service the raw
|
|
10
|
+
* one, so defaults would silently never arrive.
|
|
11
|
+
*/
|
|
12
|
+
export declare function validate(Validator: any, func: string, value: any): Promise<any>;
|
|
13
|
+
/** Where a method's validated input sits in the service call. */
|
|
14
|
+
export declare const VALIDATED_INDEX: Record<Functions, number>;
|
|
3
15
|
export declare function registerServices(app: Application, auth?: IAuth, database?: Config['database']): Promise<{
|
|
4
16
|
[serviceName: string]: any;
|
|
5
17
|
}>;
|
|
18
|
+
export {};
|
|
@@ -27,7 +27,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
27
27
|
};
|
|
28
28
|
var _a, _b, _c, _d, _e;
|
|
29
29
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
-
exports.registerServices = void 0;
|
|
30
|
+
exports.registerServices = exports.VALIDATED_INDEX = exports.validate = void 0;
|
|
31
31
|
// eslint-disable-next-line node/no-unsupported-features/node-builtins
|
|
32
32
|
const fs_1 = require("fs");
|
|
33
33
|
const path_1 = __importDefault(require("path"));
|
|
@@ -35,6 +35,14 @@ const express_1 = require("express");
|
|
|
35
35
|
const __1 = require("..");
|
|
36
36
|
const errors_1 = require("../errors");
|
|
37
37
|
const auth_1 = require("./auth");
|
|
38
|
+
/**
|
|
39
|
+
* Validate one method's input and return the coerced result.
|
|
40
|
+
*
|
|
41
|
+
* Joi converts as well as checks: `"25"` becomes `25`, and a declared default
|
|
42
|
+
* fills a missing field. The caller must therefore use the returned value —
|
|
43
|
+
* discarding it would validate the request and then hand the service the raw
|
|
44
|
+
* one, so defaults would silently never arrive.
|
|
45
|
+
*/
|
|
38
46
|
async function validate(Validator, func, value) {
|
|
39
47
|
const schema = await Validator[func];
|
|
40
48
|
if (schema) {
|
|
@@ -48,6 +56,16 @@ async function validate(Validator, func, value) {
|
|
|
48
56
|
}
|
|
49
57
|
return value;
|
|
50
58
|
}
|
|
59
|
+
exports.validate = validate;
|
|
60
|
+
/** Where a method's validated input sits in the service call. */
|
|
61
|
+
exports.VALIDATED_INDEX = {
|
|
62
|
+
find: 0,
|
|
63
|
+
get: 1,
|
|
64
|
+
create: 0,
|
|
65
|
+
patch: 1,
|
|
66
|
+
update: 1,
|
|
67
|
+
remove: 1,
|
|
68
|
+
};
|
|
51
69
|
async function hooks(Hooks, func, parameters, methodName) {
|
|
52
70
|
var _a;
|
|
53
71
|
const allHooks = Hooks[func];
|
|
@@ -146,7 +164,9 @@ async function registerServices(app, auth, database) {
|
|
|
146
164
|
serviceConfig.dialect = (_c = database === null || database === void 0 ? void 0 : database.dialect) !== null && _c !== void 0 ? _c : 'mongodb';
|
|
147
165
|
const service = new (await (_e = servicePath, Promise.resolve().then(() => __importStar(require(_e))))).default(serviceConfig);
|
|
148
166
|
services[serviceName] = service;
|
|
149
|
-
|
|
167
|
+
// `mergeParams` lets a nested handler read the parent id from the mount
|
|
168
|
+
// path rather than only its own parameters.
|
|
169
|
+
const router = (0, express_1.Router)({ mergeParams: true });
|
|
150
170
|
for (const { method, url, func } of methods) {
|
|
151
171
|
const callback = async (req, res) => {
|
|
152
172
|
var _a, _b;
|
|
@@ -185,47 +205,48 @@ async function registerServices(app, auth, database) {
|
|
|
185
205
|
const user = req.user;
|
|
186
206
|
const { id } = params;
|
|
187
207
|
let parameters = [];
|
|
188
|
-
let validatorValue;
|
|
189
208
|
switch (func) {
|
|
190
209
|
case 'find':
|
|
191
210
|
parameters = [query, user, req];
|
|
192
|
-
validatorValue = query;
|
|
193
211
|
break;
|
|
194
212
|
case 'get':
|
|
195
213
|
parameters = [id, query, user, req];
|
|
196
|
-
validatorValue = query;
|
|
197
214
|
break;
|
|
198
215
|
case 'create':
|
|
199
216
|
parameters = [body, query, user, req];
|
|
200
|
-
validatorValue = body;
|
|
201
217
|
break;
|
|
202
218
|
case 'patch':
|
|
203
219
|
parameters = [id, body, query, user, req];
|
|
204
|
-
validatorValue = body;
|
|
205
220
|
break;
|
|
206
221
|
case 'update':
|
|
207
222
|
parameters = [id, body, query, user, req];
|
|
208
|
-
validatorValue = body;
|
|
209
223
|
break;
|
|
210
224
|
case 'remove':
|
|
211
225
|
parameters = [id, query, user, req];
|
|
212
|
-
validatorValue = query;
|
|
213
226
|
break;
|
|
214
227
|
}
|
|
215
|
-
if (Hooks) {
|
|
216
|
-
await hooks(Hooks, 'all', parameters, func);
|
|
217
|
-
await hooks(Hooks, func, parameters, func);
|
|
218
|
-
}
|
|
219
|
-
if (service.filtering !== false && func === 'find') {
|
|
220
|
-
parameters[0] = (0, __1.addQuerySupport)(...parameters, func);
|
|
221
|
-
}
|
|
222
228
|
if (service[func] == undefined) {
|
|
223
229
|
return res.status(404).send({
|
|
224
230
|
message: "Not found"
|
|
225
231
|
});
|
|
226
232
|
}
|
|
233
|
+
// Validation runs before the hooks so a hook inspects the same
|
|
234
|
+
// coerced value the service receives, and writing the result back
|
|
235
|
+
// into `parameters` is what carries Joi's defaults and type
|
|
236
|
+
// conversions through to the service.
|
|
227
237
|
if (Validator) {
|
|
228
|
-
|
|
238
|
+
const index = exports.VALIDATED_INDEX[func];
|
|
239
|
+
parameters[index] = await validate(Validator, func, parameters[index]);
|
|
240
|
+
}
|
|
241
|
+
if (Hooks) {
|
|
242
|
+
await hooks(Hooks, 'all', parameters, func);
|
|
243
|
+
await hooks(Hooks, func, parameters, func);
|
|
244
|
+
}
|
|
245
|
+
// Filter translation comes last: it rewrites the query in place, so
|
|
246
|
+
// running it before validation would validate a value the service
|
|
247
|
+
// never sees.
|
|
248
|
+
if (service.filtering !== false && func === 'find') {
|
|
249
|
+
parameters[0] = (0, __1.addQuerySupport)(...parameters, func);
|
|
229
250
|
}
|
|
230
251
|
const response = await service[func](...parameters);
|
|
231
252
|
let data = response;
|
|
@@ -264,7 +285,12 @@ async function registerServices(app, auth, database) {
|
|
|
264
285
|
router[method](url, callback);
|
|
265
286
|
}
|
|
266
287
|
app.set('services', services);
|
|
267
|
-
|
|
288
|
+
// `parent = 'agents'` mounts at /agents/:parentId/<service> instead of
|
|
289
|
+
// /<service>, so a child is only reachable through its parent.
|
|
290
|
+
const mountPath = service.parent
|
|
291
|
+
? `/${service.parent}/:parentId/${serviceName}`
|
|
292
|
+
: `/${serviceName}`;
|
|
293
|
+
app.use(mountPath, router);
|
|
268
294
|
}
|
|
269
295
|
return services;
|
|
270
296
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register-services.js","sourceRoot":"","sources":["../../src/services/register-services.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,sEAAsE;AACtE,2BAA4B;AAC5B,gDAAwB;AACxB,qCAA+B;AAC/B,0BAAmE;AAEnE,sCAAqD;AACrD,iCAAqC;AAcrC,KAAK,UAAU,QAAQ,CAAC,SAAc,EAAE,IAAY,EAAE,KAAU;
|
|
1
|
+
{"version":3,"file":"register-services.js","sourceRoot":"","sources":["../../src/services/register-services.ts"],"names":[],"mappings":"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"}
|
|
@@ -24,6 +24,14 @@ export declare class XitkovService<T, U extends Dialect = DefaultDialect> implem
|
|
|
24
24
|
private service;
|
|
25
25
|
private Model;
|
|
26
26
|
pagination?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Mount beneath another service, by name.
|
|
29
|
+
*
|
|
30
|
+
* `parent = "agents"` serves this resource at
|
|
31
|
+
* `/agents/:parentId/<service>`, and the handler reads `req.params.parentId`.
|
|
32
|
+
* Unset means a top-level resource.
|
|
33
|
+
*/
|
|
34
|
+
parent?: string;
|
|
27
35
|
static defaultDialect: Dialect;
|
|
28
36
|
static setDefaultDialect(dialect: Dialect): void;
|
|
29
37
|
constructor({ Model, dialect, pagination }: IXitovServiceProps);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/services/service.ts"],"names":[],"mappings":";;;AAIA,2CAAkH;AAkClH,MAAa,aAAa;
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/services/service.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xitkov/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"@types/node": "^14.11.2",
|
|
32
32
|
"gts": "^3.1.0",
|
|
33
33
|
"jest": "^30.4.2",
|
|
34
|
+
"joi": "^18.2.9",
|
|
34
35
|
"sqlite3": "^6.0.1",
|
|
35
36
|
"ts-jest": "^29.4.11",
|
|
36
37
|
"typescript": "^4.0.3"
|
|
@@ -20,7 +20,15 @@ interface Methods {
|
|
|
20
20
|
func: Functions;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Validate one method's input and return the coerced result.
|
|
25
|
+
*
|
|
26
|
+
* Joi converts as well as checks: `"25"` becomes `25`, and a declared default
|
|
27
|
+
* fills a missing field. The caller must therefore use the returned value —
|
|
28
|
+
* discarding it would validate the request and then hand the service the raw
|
|
29
|
+
* one, so defaults would silently never arrive.
|
|
30
|
+
*/
|
|
31
|
+
export async function validate(Validator: any, func: string, value: any) {
|
|
24
32
|
const schema = await Validator[func]
|
|
25
33
|
if (schema) {
|
|
26
34
|
try {
|
|
@@ -33,6 +41,16 @@ async function validate(Validator: any, func: string, value: any) {
|
|
|
33
41
|
return value
|
|
34
42
|
}
|
|
35
43
|
|
|
44
|
+
/** Where a method's validated input sits in the service call. */
|
|
45
|
+
export const VALIDATED_INDEX: Record<Functions, number> = {
|
|
46
|
+
find: 0,
|
|
47
|
+
get: 1,
|
|
48
|
+
create: 0,
|
|
49
|
+
patch: 1,
|
|
50
|
+
update: 1,
|
|
51
|
+
remove: 1,
|
|
52
|
+
}
|
|
53
|
+
|
|
36
54
|
async function hooks(Hooks: any, func: string, parameters: any[], methodName: string) {
|
|
37
55
|
const allHooks = Hooks[func]
|
|
38
56
|
|
|
@@ -137,7 +155,9 @@ export async function registerServices(app: Application, auth?: IAuth, database?
|
|
|
137
155
|
|
|
138
156
|
const service = new (await import(servicePath)).default(serviceConfig);
|
|
139
157
|
services[serviceName] = service
|
|
140
|
-
|
|
158
|
+
// `mergeParams` lets a nested handler read the parent id from the mount
|
|
159
|
+
// path rather than only its own parameters.
|
|
160
|
+
const router = Router({mergeParams: true});
|
|
141
161
|
for (const {method, url, func} of methods) {
|
|
142
162
|
const callback = async (req: Request, res: Response) => {
|
|
143
163
|
const { required, baseURL } = auth ?? {}
|
|
@@ -180,48 +200,52 @@ export async function registerServices(app: Application, auth?: IAuth, database?
|
|
|
180
200
|
const user = (req as any).user
|
|
181
201
|
const {id} = params;
|
|
182
202
|
let parameters: any[] = [];
|
|
183
|
-
let validatorValue
|
|
184
203
|
switch (func) {
|
|
185
204
|
case 'find':
|
|
186
205
|
parameters = [query, user, req];
|
|
187
|
-
validatorValue = query
|
|
188
206
|
break;
|
|
189
207
|
case 'get':
|
|
190
208
|
parameters = [id, query, user, req];
|
|
191
|
-
validatorValue = query
|
|
192
209
|
break;
|
|
193
210
|
case 'create':
|
|
194
211
|
parameters = [body, query, user, req];
|
|
195
|
-
validatorValue = body
|
|
196
212
|
break;
|
|
197
213
|
case 'patch':
|
|
198
214
|
parameters = [id, body, query, user, req];
|
|
199
|
-
validatorValue = body
|
|
200
215
|
break;
|
|
201
216
|
case 'update':
|
|
202
217
|
parameters = [id, body, query, user, req];
|
|
203
|
-
validatorValue = body
|
|
204
218
|
break;
|
|
205
219
|
case 'remove':
|
|
206
220
|
parameters = [id, query, user, req];
|
|
207
|
-
validatorValue = query
|
|
208
221
|
break;
|
|
209
222
|
}
|
|
210
|
-
if (Hooks) {
|
|
211
|
-
await hooks(Hooks, 'all', parameters, func)
|
|
212
|
-
await hooks(Hooks, func, parameters, func)
|
|
213
|
-
}
|
|
214
|
-
if (service.filtering !== false && func === 'find') {
|
|
215
|
-
parameters[0] = addQuerySupport(...parameters, func)
|
|
216
|
-
}
|
|
217
223
|
|
|
218
224
|
if(service[func] == undefined) {
|
|
219
225
|
return res.status(404).send({
|
|
220
226
|
message: "Not found"
|
|
221
227
|
});
|
|
222
228
|
}
|
|
229
|
+
|
|
230
|
+
// Validation runs before the hooks so a hook inspects the same
|
|
231
|
+
// coerced value the service receives, and writing the result back
|
|
232
|
+
// into `parameters` is what carries Joi's defaults and type
|
|
233
|
+
// conversions through to the service.
|
|
223
234
|
if (Validator) {
|
|
224
|
-
|
|
235
|
+
const index = VALIDATED_INDEX[func]
|
|
236
|
+
parameters[index] = await validate(Validator, func, parameters[index])
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (Hooks) {
|
|
240
|
+
await hooks(Hooks, 'all', parameters, func)
|
|
241
|
+
await hooks(Hooks, func, parameters, func)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Filter translation comes last: it rewrites the query in place, so
|
|
245
|
+
// running it before validation would validate a value the service
|
|
246
|
+
// never sees.
|
|
247
|
+
if (service.filtering !== false && func === 'find') {
|
|
248
|
+
parameters[0] = addQuerySupport(...parameters, func)
|
|
225
249
|
}
|
|
226
250
|
|
|
227
251
|
const response = await service[func](...parameters)
|
|
@@ -262,7 +286,13 @@ export async function registerServices(app: Application, auth?: IAuth, database?
|
|
|
262
286
|
}
|
|
263
287
|
app.set('services', services)
|
|
264
288
|
|
|
265
|
-
|
|
289
|
+
// `parent = 'agents'` mounts at /agents/:parentId/<service> instead of
|
|
290
|
+
// /<service>, so a child is only reachable through its parent.
|
|
291
|
+
const mountPath = service.parent
|
|
292
|
+
? `/${service.parent}/:parentId/${serviceName}`
|
|
293
|
+
: `/${serviceName}`;
|
|
294
|
+
|
|
295
|
+
app.use(mountPath, router);
|
|
266
296
|
}
|
|
267
297
|
return services;
|
|
268
298
|
}
|
package/src/services/service.ts
CHANGED
|
@@ -41,6 +41,15 @@ export class XitkovService<T, U extends Dialect = DefaultDialect> implements Int
|
|
|
41
41
|
private Model: ModelType<T, U>
|
|
42
42
|
pagination?: boolean;
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Mount beneath another service, by name.
|
|
46
|
+
*
|
|
47
|
+
* `parent = "agents"` serves this resource at
|
|
48
|
+
* `/agents/:parentId/<service>`, and the handler reads `req.params.parentId`.
|
|
49
|
+
* Unset means a top-level resource.
|
|
50
|
+
*/
|
|
51
|
+
parent?: string;
|
|
52
|
+
|
|
44
53
|
static defaultDialect: Dialect = 'mongodb';
|
|
45
54
|
|
|
46
55
|
static setDefaultDialect(dialect: Dialect) {
|