@appweaver/core 1.3.1 → 1.4.1
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/export/export-service.d.ts +4 -3
- package/export/export-service.js +39 -22
- package/factory/create-model.js +54 -22
- package/factory/create-service.js +5 -3
- package/package.json +2 -2
- package/prisma/client/commonInputTypes.d.ts +0 -50
- package/prisma/client/internal/class.js +3 -3
- package/prisma/client/models/File.d.ts +15 -28
- package/resource/resource-loader.js +6 -0
- package/resource/resource-routes.js +2 -2
- package/resource/resource-schema.d.ts +38 -12
- package/resource/resource-schema.js +63 -15
- package/resource/resource-service.d.ts +25 -16
- package/resource/resource-service.js +61 -26
- package/resource/schemas/resource-sort-schema.js +2 -2
- package/resource/utils/cursor-util.d.ts +60 -0
- package/resource/utils/cursor-util.js +117 -0
- package/resource/utils/index.d.ts +1 -0
- package/resource/utils/index.js +1 -0
- package/resource/utils/relation-util.d.ts +3 -3
- package/resource/utils/relation-util.js +52 -16
- package/resource/utils/sort-util.d.ts +11 -0
- package/resource/utils/sort-util.js +18 -0
- package/security/api-key/api-key-auth.js +4 -1
- package/security/auth-service.d.ts +7 -7
- package/security/auth-service.js +43 -5
- package/security/create-auth-resources.d.ts +2 -1
- package/security/oauth2/create-oauth2-plugin.js +3 -34
- package/security/oauth2/oauth2-microsoft.js +1 -1
- package/security/oauth2/oauth2-util.d.ts +9 -7
- package/security/oauth2/oauth2-util.js +17 -10
- package/security/resources/api-key/model.js +2 -0
- package/security/resources/connected-account/model.js +2 -0
- package/security/store/database-security-store.js +2 -1
- package/server/swagger.js +49 -1
- package/storage/file-service.d.ts +19 -4
- package/storage/file-service.js +199 -146
- package/storage/resources/file/model.js +4 -2
- package/types/auth.d.ts +8 -8
- package/types/generated.d.ts +8 -2
- package/types/index.d.ts +1 -0
- package/types/index.js +1 -0
- package/types/storage.d.ts +16 -0
- package/types/storage.js +2 -0
- package/utils/index.d.ts +1 -0
- package/utils/index.js +1 -0
- package/utils/model-util.d.ts +12 -0
- package/utils/model-util.js +113 -0
|
@@ -15,12 +15,10 @@ export type AggregateFile = {
|
|
|
15
15
|
export type FileAvgAggregateOutputType = {
|
|
16
16
|
id: number | null;
|
|
17
17
|
sizeBytes: number | null;
|
|
18
|
-
resourceId: number | null;
|
|
19
18
|
};
|
|
20
19
|
export type FileSumAggregateOutputType = {
|
|
21
20
|
id: number | null;
|
|
22
21
|
sizeBytes: number | null;
|
|
23
|
-
resourceId: number | null;
|
|
24
22
|
};
|
|
25
23
|
export type FileMinAggregateOutputType = {
|
|
26
24
|
id: number | null;
|
|
@@ -33,7 +31,7 @@ export type FileMinAggregateOutputType = {
|
|
|
33
31
|
description: string | null;
|
|
34
32
|
resourceField: string | null;
|
|
35
33
|
resourceName: string | null;
|
|
36
|
-
resourceId:
|
|
34
|
+
resourceId: string | null;
|
|
37
35
|
updatedAt: Date | null;
|
|
38
36
|
createdAt: Date | null;
|
|
39
37
|
};
|
|
@@ -48,7 +46,7 @@ export type FileMaxAggregateOutputType = {
|
|
|
48
46
|
description: string | null;
|
|
49
47
|
resourceField: string | null;
|
|
50
48
|
resourceName: string | null;
|
|
51
|
-
resourceId:
|
|
49
|
+
resourceId: string | null;
|
|
52
50
|
updatedAt: Date | null;
|
|
53
51
|
createdAt: Date | null;
|
|
54
52
|
};
|
|
@@ -71,12 +69,10 @@ export type FileCountAggregateOutputType = {
|
|
|
71
69
|
export type FileAvgAggregateInputType = {
|
|
72
70
|
id?: true;
|
|
73
71
|
sizeBytes?: true;
|
|
74
|
-
resourceId?: true;
|
|
75
72
|
};
|
|
76
73
|
export type FileSumAggregateInputType = {
|
|
77
74
|
id?: true;
|
|
78
75
|
sizeBytes?: true;
|
|
79
|
-
resourceId?: true;
|
|
80
76
|
};
|
|
81
77
|
export type FileMinAggregateInputType = {
|
|
82
78
|
id?: true;
|
|
@@ -211,7 +207,7 @@ export type FileGroupByOutputType = {
|
|
|
211
207
|
description: string | null;
|
|
212
208
|
resourceField: string | null;
|
|
213
209
|
resourceName: string | null;
|
|
214
|
-
resourceId:
|
|
210
|
+
resourceId: string | null;
|
|
215
211
|
updatedAt: Date;
|
|
216
212
|
createdAt: Date;
|
|
217
213
|
_count: FileCountAggregateOutputType | null;
|
|
@@ -237,7 +233,7 @@ export type FileWhereInput = {
|
|
|
237
233
|
description?: Prisma.StringNullableFilter<"File"> | string | null;
|
|
238
234
|
resourceField?: Prisma.StringNullableFilter<"File"> | string | null;
|
|
239
235
|
resourceName?: Prisma.StringNullableFilter<"File"> | string | null;
|
|
240
|
-
resourceId?: Prisma.
|
|
236
|
+
resourceId?: Prisma.StringNullableFilter<"File"> | string | null;
|
|
241
237
|
updatedAt?: Prisma.DateTimeFilter<"File"> | Date | string;
|
|
242
238
|
createdAt?: Prisma.DateTimeFilter<"File"> | Date | string;
|
|
243
239
|
};
|
|
@@ -270,7 +266,7 @@ export type FileWhereUniqueInput = Prisma.AtLeast<{
|
|
|
270
266
|
description?: Prisma.StringNullableFilter<"File"> | string | null;
|
|
271
267
|
resourceField?: Prisma.StringNullableFilter<"File"> | string | null;
|
|
272
268
|
resourceName?: Prisma.StringNullableFilter<"File"> | string | null;
|
|
273
|
-
resourceId?: Prisma.
|
|
269
|
+
resourceId?: Prisma.StringNullableFilter<"File"> | string | null;
|
|
274
270
|
updatedAt?: Prisma.DateTimeFilter<"File"> | Date | string;
|
|
275
271
|
createdAt?: Prisma.DateTimeFilter<"File"> | Date | string;
|
|
276
272
|
}, "id" | "name">;
|
|
@@ -308,7 +304,7 @@ export type FileScalarWhereWithAggregatesInput = {
|
|
|
308
304
|
description?: Prisma.StringNullableWithAggregatesFilter<"File"> | string | null;
|
|
309
305
|
resourceField?: Prisma.StringNullableWithAggregatesFilter<"File"> | string | null;
|
|
310
306
|
resourceName?: Prisma.StringNullableWithAggregatesFilter<"File"> | string | null;
|
|
311
|
-
resourceId?: Prisma.
|
|
307
|
+
resourceId?: Prisma.StringNullableWithAggregatesFilter<"File"> | string | null;
|
|
312
308
|
updatedAt?: Prisma.DateTimeWithAggregatesFilter<"File"> | Date | string;
|
|
313
309
|
createdAt?: Prisma.DateTimeWithAggregatesFilter<"File"> | Date | string;
|
|
314
310
|
};
|
|
@@ -322,7 +318,7 @@ export type FileCreateInput = {
|
|
|
322
318
|
description?: string | null;
|
|
323
319
|
resourceField?: string | null;
|
|
324
320
|
resourceName?: string | null;
|
|
325
|
-
resourceId?:
|
|
321
|
+
resourceId?: string | null;
|
|
326
322
|
updatedAt?: Date | string;
|
|
327
323
|
createdAt?: Date | string;
|
|
328
324
|
};
|
|
@@ -337,7 +333,7 @@ export type FileUncheckedCreateInput = {
|
|
|
337
333
|
description?: string | null;
|
|
338
334
|
resourceField?: string | null;
|
|
339
335
|
resourceName?: string | null;
|
|
340
|
-
resourceId?:
|
|
336
|
+
resourceId?: string | null;
|
|
341
337
|
updatedAt?: Date | string;
|
|
342
338
|
createdAt?: Date | string;
|
|
343
339
|
};
|
|
@@ -351,7 +347,7 @@ export type FileUpdateInput = {
|
|
|
351
347
|
description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
352
348
|
resourceField?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
353
349
|
resourceName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
354
|
-
resourceId?: Prisma.
|
|
350
|
+
resourceId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
355
351
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string;
|
|
356
352
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string;
|
|
357
353
|
};
|
|
@@ -366,7 +362,7 @@ export type FileUncheckedUpdateInput = {
|
|
|
366
362
|
description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
367
363
|
resourceField?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
368
364
|
resourceName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
369
|
-
resourceId?: Prisma.
|
|
365
|
+
resourceId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
370
366
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string;
|
|
371
367
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string;
|
|
372
368
|
};
|
|
@@ -381,7 +377,7 @@ export type FileCreateManyInput = {
|
|
|
381
377
|
description?: string | null;
|
|
382
378
|
resourceField?: string | null;
|
|
383
379
|
resourceName?: string | null;
|
|
384
|
-
resourceId?:
|
|
380
|
+
resourceId?: string | null;
|
|
385
381
|
updatedAt?: Date | string;
|
|
386
382
|
createdAt?: Date | string;
|
|
387
383
|
};
|
|
@@ -395,7 +391,7 @@ export type FileUpdateManyMutationInput = {
|
|
|
395
391
|
description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
396
392
|
resourceField?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
397
393
|
resourceName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
398
|
-
resourceId?: Prisma.
|
|
394
|
+
resourceId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
399
395
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string;
|
|
400
396
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string;
|
|
401
397
|
};
|
|
@@ -410,7 +406,7 @@ export type FileUncheckedUpdateManyInput = {
|
|
|
410
406
|
description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
411
407
|
resourceField?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
412
408
|
resourceName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
413
|
-
resourceId?: Prisma.
|
|
409
|
+
resourceId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
414
410
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string;
|
|
415
411
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string;
|
|
416
412
|
};
|
|
@@ -432,7 +428,6 @@ export type FileCountOrderByAggregateInput = {
|
|
|
432
428
|
export type FileAvgOrderByAggregateInput = {
|
|
433
429
|
id?: Prisma.SortOrder;
|
|
434
430
|
sizeBytes?: Prisma.SortOrder;
|
|
435
|
-
resourceId?: Prisma.SortOrder;
|
|
436
431
|
};
|
|
437
432
|
export type FileMaxOrderByAggregateInput = {
|
|
438
433
|
id?: Prisma.SortOrder;
|
|
@@ -467,14 +462,6 @@ export type FileMinOrderByAggregateInput = {
|
|
|
467
462
|
export type FileSumOrderByAggregateInput = {
|
|
468
463
|
id?: Prisma.SortOrder;
|
|
469
464
|
sizeBytes?: Prisma.SortOrder;
|
|
470
|
-
resourceId?: Prisma.SortOrder;
|
|
471
|
-
};
|
|
472
|
-
export type NullableIntFieldUpdateOperationsInput = {
|
|
473
|
-
set?: number | null;
|
|
474
|
-
increment?: number;
|
|
475
|
-
decrement?: number;
|
|
476
|
-
multiply?: number;
|
|
477
|
-
divide?: number;
|
|
478
465
|
};
|
|
479
466
|
export type FileSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
|
480
467
|
id?: boolean;
|
|
@@ -551,7 +538,7 @@ export type $FilePayload<ExtArgs extends runtime.Types.Extensions.InternalArgs =
|
|
|
551
538
|
description: string | null;
|
|
552
539
|
resourceField: string | null;
|
|
553
540
|
resourceName: string | null;
|
|
554
|
-
resourceId:
|
|
541
|
+
resourceId: string | null;
|
|
555
542
|
/**
|
|
556
543
|
* Audit columns
|
|
557
544
|
*/
|
|
@@ -921,7 +908,7 @@ export interface FileFieldRefs {
|
|
|
921
908
|
readonly description: Prisma.FieldRef<"File", 'String'>;
|
|
922
909
|
readonly resourceField: Prisma.FieldRef<"File", 'String'>;
|
|
923
910
|
readonly resourceName: Prisma.FieldRef<"File", 'String'>;
|
|
924
|
-
readonly resourceId: Prisma.FieldRef<"File", '
|
|
911
|
+
readonly resourceId: Prisma.FieldRef<"File", 'String'>;
|
|
925
912
|
readonly updatedAt: Prisma.FieldRef<"File", 'DateTime'>;
|
|
926
913
|
readonly createdAt: Prisma.FieldRef<"File", 'DateTime'>;
|
|
927
914
|
}
|
|
@@ -51,6 +51,12 @@ async function loadModels(baseDir, modelPattern) {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
+
// Resolve the schemas referencing the primary key of another model, now that
|
|
55
|
+
// every model is registered
|
|
56
|
+
(0, common_1.relinkResourceModels)(models);
|
|
57
|
+
// Reject a default value that its own field constraints would reject, since
|
|
58
|
+
// the records it produces cannot be serialized by the routes of their model
|
|
59
|
+
(0, utils_1.validateScalarDefaults)(models);
|
|
54
60
|
// Reject file name patterns writing into a reserved storage path before any
|
|
55
61
|
// upload can reach the storage layer.
|
|
56
62
|
(0, utils_1.validateFileNamePatterns)(models);
|
|
@@ -76,8 +76,8 @@ function resourceRoutes(name, routesConfig = {}) {
|
|
|
76
76
|
onRequest: routeAuth(queryConfig),
|
|
77
77
|
config: queryConfig
|
|
78
78
|
}, async (request, reply) => {
|
|
79
|
-
const { page, size, sort, ...body } = request.body;
|
|
80
|
-
const response = await service.query(body.filter, page, size, sort);
|
|
79
|
+
const { page, size, sort, cursor, totalCount, ...body } = request.body;
|
|
80
|
+
const response = await service.query(body.filter, page, size, sort, cursor, totalCount);
|
|
81
81
|
return reply.send(response);
|
|
82
82
|
});
|
|
83
83
|
}
|
|
@@ -1,24 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { TObject, TSchema } from '@sinclair/typebox';
|
|
2
|
+
import { AuthType, IdField, RecaptchaConfig, ResourceRoutesConfig, ResourceSchemaConfig } from '@appweaver/common';
|
|
3
|
+
export declare const ID_STRING_MAX_LENGTH = 36;
|
|
4
|
+
export declare const Id: TObject<{
|
|
3
5
|
id: import("@sinclair/typebox").TInteger;
|
|
4
6
|
}>;
|
|
5
|
-
export declare const IdString:
|
|
7
|
+
export declare const IdString: TObject<{
|
|
6
8
|
id: import("@sinclair/typebox").TString;
|
|
7
9
|
}>;
|
|
8
|
-
export declare const
|
|
9
|
-
updatedAt: import("@sinclair/typebox").TUnsafe<Date>;
|
|
10
|
-
createdAt: import("@sinclair/typebox").TUnsafe<Date>;
|
|
11
|
-
createdById: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnsafe<number | null>>;
|
|
12
|
-
}>;
|
|
13
|
-
export declare const QueryRequestData: import("@sinclair/typebox").TObject<{
|
|
10
|
+
export declare const QueryRequestData: TObject<{
|
|
14
11
|
page: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
15
12
|
size: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
13
|
+
cursor: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnsafe<string | null>>;
|
|
14
|
+
totalCount: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
16
15
|
}>;
|
|
17
|
-
export declare const QueryResponseData:
|
|
16
|
+
export declare const QueryResponseData: TObject<{
|
|
18
17
|
resultCount: import("@sinclair/typebox").TNumber;
|
|
19
|
-
totalCount: import("@sinclair/typebox").
|
|
18
|
+
totalCount: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnsafe<number | null>>;
|
|
19
|
+
nextCursor: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnsafe<string | null>>;
|
|
20
|
+
prevCursor: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnsafe<string | null>>;
|
|
20
21
|
}>;
|
|
21
|
-
export declare const AggregateRequestData:
|
|
22
|
+
export declare const AggregateRequestData: TObject<{
|
|
22
23
|
from: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnsafe<Date>>;
|
|
23
24
|
to: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnsafe<Date>>;
|
|
24
25
|
step: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
|
|
@@ -26,3 +27,28 @@ export declare const AggregateRequestData: import("@sinclair/typebox").TObject<{
|
|
|
26
27
|
}>;
|
|
27
28
|
export declare const AggregateResponseData: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TAny>;
|
|
28
29
|
export declare function createSchema(name: string, routeAuthTypes: Record<keyof ResourceRoutesConfig, AuthType[] | undefined>, routeRecaptcha: Record<keyof ResourceRoutesConfig, RecaptchaConfig>): ResourceSchemaConfig;
|
|
30
|
+
/**
|
|
31
|
+
* Builds the schema of a single primary key value, used wherever an id is
|
|
32
|
+
* accepted on its own (i.e. a relation input connecting an existing record).
|
|
33
|
+
*
|
|
34
|
+
* @param {IdField} [idField] - The id configuration, integer by default.
|
|
35
|
+
* @return {TSchema} The schema of the id value.
|
|
36
|
+
*/
|
|
37
|
+
export declare function idValueSchema(idField?: IdField): TSchema;
|
|
38
|
+
/**
|
|
39
|
+
* Builds the primary key schema of a model, used as the route path parameter
|
|
40
|
+
* and as the connection shape of the relation inputs pointing at the model. A
|
|
41
|
+
* fresh object per call, since each model annotates its own schemas.
|
|
42
|
+
*
|
|
43
|
+
* @param {IdField} [idField] - The id configuration, integer by default.
|
|
44
|
+
* @return {TObject} The primary key schema of the model.
|
|
45
|
+
*/
|
|
46
|
+
export declare function idSchema(idField?: IdField): TObject;
|
|
47
|
+
/**
|
|
48
|
+
* Builds the audit field schema of a model. The `createdById` field references
|
|
49
|
+
* the authentication model, so it follows that model's id type.
|
|
50
|
+
*
|
|
51
|
+
* @param {IdField} [authIdField] - The auth model id configuration, integer by default.
|
|
52
|
+
* @return {TObject} The schema holding all supported audit fields.
|
|
53
|
+
*/
|
|
54
|
+
export declare function auditSchema(authIdField?: IdField): TObject;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AggregateResponseData = exports.AggregateRequestData = exports.QueryResponseData = exports.QueryRequestData = exports.
|
|
3
|
+
exports.AggregateResponseData = exports.AggregateRequestData = exports.QueryResponseData = exports.QueryRequestData = exports.IdString = exports.Id = exports.ID_STRING_MAX_LENGTH = void 0;
|
|
4
4
|
exports.createSchema = createSchema;
|
|
5
|
+
exports.idValueSchema = idValueSchema;
|
|
6
|
+
exports.idSchema = idSchema;
|
|
7
|
+
exports.auditSchema = auditSchema;
|
|
5
8
|
const typebox_1 = require("@sinclair/typebox");
|
|
6
9
|
const common_1 = require("@appweaver/common");
|
|
7
10
|
const context_1 = require("../context");
|
|
@@ -9,26 +12,31 @@ const security_1 = require("../security");
|
|
|
9
12
|
const errors_1 = require("../errors");
|
|
10
13
|
const utils_1 = require("../utils");
|
|
11
14
|
const schemas_1 = require("./schemas");
|
|
15
|
+
// Maximum length of a string primary key, sized for the longest value the
|
|
16
|
+
// supported generators produce (a 36-character UUID)
|
|
17
|
+
exports.ID_STRING_MAX_LENGTH = 36;
|
|
12
18
|
exports.Id = typebox_1.Type.Object({
|
|
13
|
-
id: typebox_1.Type.Integer({ minimum: 1 })
|
|
19
|
+
id: typebox_1.Type.Integer({ minimum: 1, example: 1 })
|
|
14
20
|
});
|
|
15
21
|
exports.IdString = typebox_1.Type.Object({
|
|
16
|
-
id: typebox_1.Type.String({ maxLength:
|
|
17
|
-
});
|
|
18
|
-
exports.AuditData = typebox_1.Type.Object({
|
|
19
|
-
updatedAt: (0, common_1.StringDate)(),
|
|
20
|
-
createdAt: (0, common_1.StringDate)(),
|
|
21
|
-
createdById: (0, common_1.Nullable)(typebox_1.Type.Integer({ minimum: 1, example: 1 }))
|
|
22
|
+
id: typebox_1.Type.String({ maxLength: exports.ID_STRING_MAX_LENGTH, example: 'a1b2c3d4' })
|
|
22
23
|
});
|
|
24
|
+
const CURSOR_EXAMPLE = 'eyJpIjo0MiwiZiI6IkhkQjVfa2VMTVlyNyJ9';
|
|
23
25
|
// The sort property is declared per model instead, since its object form
|
|
24
26
|
// references the sortable fields of the queried resource
|
|
25
27
|
exports.QueryRequestData = typebox_1.Type.Object({
|
|
26
28
|
page: typebox_1.Type.Optional(typebox_1.Type.Number({ minimum: 1, example: 1 })),
|
|
27
|
-
size: typebox_1.Type.Optional(typebox_1.Type.Number({ minimum: 0, maximum:
|
|
29
|
+
size: typebox_1.Type.Optional(typebox_1.Type.Number({ minimum: 0, maximum: 2000, example: 50 })),
|
|
30
|
+
cursor: (0, common_1.Nullable)(typebox_1.Type.String({ example: CURSOR_EXAMPLE })),
|
|
31
|
+
totalCount: typebox_1.Type.Optional(typebox_1.Type.Boolean({ default: true, example: true }))
|
|
28
32
|
});
|
|
33
|
+
// The absent values are returned as null rather than omitted, so a client reads
|
|
34
|
+
// "no further page" and "not counted" off the response instead of a missing key
|
|
29
35
|
exports.QueryResponseData = typebox_1.Type.Object({
|
|
30
36
|
resultCount: typebox_1.Type.Number({ example: 10 }),
|
|
31
|
-
totalCount: typebox_1.Type.Number({ example: 100 })
|
|
37
|
+
totalCount: (0, common_1.Nullable)(typebox_1.Type.Number({ example: 100 })),
|
|
38
|
+
nextCursor: (0, common_1.Nullable)(typebox_1.Type.String({ example: CURSOR_EXAMPLE })),
|
|
39
|
+
prevCursor: (0, common_1.Nullable)(typebox_1.Type.String({ example: CURSOR_EXAMPLE }))
|
|
32
40
|
});
|
|
33
41
|
// The select and dateField properties are declared per model instead, since
|
|
34
42
|
// they reference the aggregatable fields of the aggregated resource
|
|
@@ -43,6 +51,9 @@ function createSchema(name, routeAuthTypes, routeRecaptcha) {
|
|
|
43
51
|
const resourceModel = (0, context_1.injectModel)(name);
|
|
44
52
|
const resourceName = (0, common_1.camelToSnakeCase)(name, ' ');
|
|
45
53
|
const tag = (0, common_1.plural)(name);
|
|
54
|
+
// The path parameter follows the primary key type of this model, so a string
|
|
55
|
+
// id is neither coerced to a number nor rejected by the request validation
|
|
56
|
+
const idParams = resourceModel.idModel;
|
|
46
57
|
// Register the recursive query filter and sort schemas of all loaded models,
|
|
47
58
|
// referenced by the query, aggregate, and export request bodies
|
|
48
59
|
(0, schemas_1.registerQueryFilterSchemas)();
|
|
@@ -85,7 +96,7 @@ function createSchema(name, routeAuthTypes, routeRecaptcha) {
|
|
|
85
96
|
200: resourceModel.readOneModel,
|
|
86
97
|
...errors_1.AllErrorResponses
|
|
87
98
|
},
|
|
88
|
-
params:
|
|
99
|
+
params: idParams
|
|
89
100
|
},
|
|
90
101
|
querySchema: {
|
|
91
102
|
tags: [tag],
|
|
@@ -134,7 +145,7 @@ function createSchema(name, routeAuthTypes, routeRecaptcha) {
|
|
|
134
145
|
...errors_1.AllErrorResponses
|
|
135
146
|
},
|
|
136
147
|
body: resourceModel.updateOneModel,
|
|
137
|
-
params:
|
|
148
|
+
params: idParams
|
|
138
149
|
},
|
|
139
150
|
deleteSchema: {
|
|
140
151
|
tags: [tag],
|
|
@@ -146,7 +157,7 @@ function createSchema(name, routeAuthTypes, routeRecaptcha) {
|
|
|
146
157
|
200: resourceModel.readOneModel,
|
|
147
158
|
...errors_1.AllErrorResponses
|
|
148
159
|
},
|
|
149
|
-
params:
|
|
160
|
+
params: idParams
|
|
150
161
|
},
|
|
151
162
|
exportSchema: {
|
|
152
163
|
tags: [tag],
|
|
@@ -178,7 +189,7 @@ function createSchema(name, routeAuthTypes, routeRecaptcha) {
|
|
|
178
189
|
...errors_1.AllErrorResponses
|
|
179
190
|
},
|
|
180
191
|
body: resourceModel.fileUploadModel,
|
|
181
|
-
params:
|
|
192
|
+
params: idParams
|
|
182
193
|
},
|
|
183
194
|
fileDeleteSchema: {
|
|
184
195
|
tags: [tag],
|
|
@@ -191,7 +202,7 @@ function createSchema(name, routeAuthTypes, routeRecaptcha) {
|
|
|
191
202
|
...errors_1.AllErrorResponses
|
|
192
203
|
},
|
|
193
204
|
body: resourceModel.fileDeleteModel,
|
|
194
|
-
params:
|
|
205
|
+
params: idParams
|
|
195
206
|
}
|
|
196
207
|
};
|
|
197
208
|
for (const resourceConfig of Object.values(resourceSchemaConfig)) {
|
|
@@ -199,3 +210,40 @@ function createSchema(name, routeAuthTypes, routeRecaptcha) {
|
|
|
199
210
|
}
|
|
200
211
|
return resourceSchemaConfig;
|
|
201
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Builds the schema of a single primary key value, used wherever an id is
|
|
215
|
+
* accepted on its own (i.e. a relation input connecting an existing record).
|
|
216
|
+
*
|
|
217
|
+
* @param {IdField} [idField] - The id configuration, integer by default.
|
|
218
|
+
* @return {TSchema} The schema of the id value.
|
|
219
|
+
*/
|
|
220
|
+
function idValueSchema(idField) {
|
|
221
|
+
return (0, common_1.idFieldType)(idField) === 'string'
|
|
222
|
+
? exports.IdString.properties.id
|
|
223
|
+
: exports.Id.properties.id;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Builds the primary key schema of a model, used as the route path parameter
|
|
227
|
+
* and as the connection shape of the relation inputs pointing at the model. A
|
|
228
|
+
* fresh object per call, since each model annotates its own schemas.
|
|
229
|
+
*
|
|
230
|
+
* @param {IdField} [idField] - The id configuration, integer by default.
|
|
231
|
+
* @return {TObject} The primary key schema of the model.
|
|
232
|
+
*/
|
|
233
|
+
function idSchema(idField) {
|
|
234
|
+
return typebox_1.Type.Object({ id: idValueSchema(idField) });
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Builds the audit field schema of a model. The `createdById` field references
|
|
238
|
+
* the authentication model, so it follows that model's id type.
|
|
239
|
+
*
|
|
240
|
+
* @param {IdField} [authIdField] - The auth model id configuration, integer by default.
|
|
241
|
+
* @return {TObject} The schema holding all supported audit fields.
|
|
242
|
+
*/
|
|
243
|
+
function auditSchema(authIdField) {
|
|
244
|
+
return typebox_1.Type.Object({
|
|
245
|
+
updatedAt: (0, common_1.StringDate)(),
|
|
246
|
+
createdAt: (0, common_1.StringDate)(),
|
|
247
|
+
createdById: (0, common_1.Nullable)(idValueSchema(authIdField))
|
|
248
|
+
});
|
|
249
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ActionType, AggregateResponse, AggregateSelect, IResourceService, QueryFilter, QueryResponse, QuerySort, Resource, ResourceClient, ResourceData } from '@appweaver/common';
|
|
1
|
+
import { ActionType, AggregateResponse, AggregateSelect, AggregateSelected, IResourceService, QueryFilter, QueryResponse, QuerySort, Resource, ResourceId, ResourceClient, ResourceData } from '@appweaver/common';
|
|
2
2
|
export declare abstract class ResourceService<ReadOne = Resource, ReadMany = Resource, Create = ResourceData<Resource>, Update = Partial<ResourceData<Resource>>, Query = QueryFilter<ReadOne>> implements IResourceService<ReadOne, ReadMany, Create, Update, Query> {
|
|
3
3
|
readonly modelName: string;
|
|
4
4
|
/**
|
|
@@ -24,14 +24,14 @@ export declare abstract class ResourceService<ReadOne = Resource, ReadMany = Res
|
|
|
24
24
|
* configured for output on the find action. A resource event is emitted after
|
|
25
25
|
* a successful lookup.
|
|
26
26
|
*
|
|
27
|
-
* @param {
|
|
27
|
+
* @param {ResourceId} id The id of the resource to find.
|
|
28
28
|
* @returns {Promise<Object>} The found resource with its virtual fields and
|
|
29
29
|
* relation counts projected.
|
|
30
30
|
* @throws {@link HttpError} 404 if the resource does not exist or is filtered
|
|
31
31
|
* out by the read restrictions, 403 if the access check denies it, and 500 on
|
|
32
32
|
* a database error.
|
|
33
33
|
*/
|
|
34
|
-
find(id:
|
|
34
|
+
find(id: ResourceId): Promise<ReadOne>;
|
|
35
35
|
/**
|
|
36
36
|
* Queries a page of resources matching the provided filter. The filter is
|
|
37
37
|
* mapped to a database query, combined with the optional `searchText` full
|
|
@@ -47,21 +47,28 @@ export declare abstract class ResourceService<ReadOne = Resource, ReadMany = Res
|
|
|
47
47
|
* as plain field values. Its `searchText` property, if present, is passed to
|
|
48
48
|
* {@link ResourceService.textSearchQuery} instead of being matched as a
|
|
49
49
|
* field.
|
|
50
|
-
* @param {number} [page] The one-based page number of results to return
|
|
50
|
+
* @param {number} [page] The one-based page number of results to return,
|
|
51
|
+
* ignored when a cursor is given.
|
|
51
52
|
* @param {number} [size] The maximum number of results per page.
|
|
52
53
|
* @param {QuerySort} [sort] The fields to sort by, either as a comma-separated
|
|
53
54
|
* list where a field prefixed with `-` is sorted in descending order
|
|
54
|
-
* (i.e. `-createdAt,
|
|
55
|
+
* (i.e. `-createdAt,title`), or as an object of field directions
|
|
55
56
|
* (i.e. `{ createdAt: 'desc', id: 'asc' }`). Both forms support the fields of
|
|
56
57
|
* the included to-one relations, given with a dot notation (`author.createdAt`)
|
|
57
58
|
* or as a nested object (`{ author: { createdAt: 'desc' } }`).
|
|
59
|
+
* @param {string} [cursor] The cursor of the page to return, as issued in the
|
|
60
|
+
* `nextCursor` or `prevCursor` of an earlier response, which also carries the
|
|
61
|
+
* direction the page runs in. Takes precedence over `page`.
|
|
62
|
+
* @param {boolean} [totalCount] Whether to count all matching resources, which
|
|
63
|
+
* costs a scan of every one of them.
|
|
58
64
|
* @returns {Promise<QueryResponse<Object>>} The paged query response containing
|
|
59
|
-
* the returned resources, the count of the returned items
|
|
60
|
-
*
|
|
65
|
+
* the returned resources, the count of the returned items, the cursors of the
|
|
66
|
+
* adjacent pages, and the total count unless it was opted out of.
|
|
61
67
|
* @throws {@link HttpError} 400 if the sort input names a field that cannot be
|
|
62
|
-
* sorted by
|
|
68
|
+
* sorted by or the cursor was issued for another filter or sort order, and 500
|
|
69
|
+
* on a database error.
|
|
63
70
|
*/
|
|
64
|
-
query(filter?: Query, page?: number, size?: number, sort?: QuerySort<ReadMany
|
|
71
|
+
query(filter?: Query, page?: number, size?: number, sort?: QuerySort<ReadMany>, cursor?: string | null, totalCount?: boolean): Promise<QueryResponse<ReadMany>>;
|
|
65
72
|
/**
|
|
66
73
|
* Aggregates resources matching the provided filter over a date range, both
|
|
67
74
|
* as a single overall result and as a series of results for the equally sized
|
|
@@ -92,11 +99,13 @@ export declare abstract class ResourceService<ReadOne = Resource, ReadMany = Res
|
|
|
92
99
|
* When false, the step is interpreted in seconds.
|
|
93
100
|
* @returns {Promise<AggregateResponse<Object>>} The aggregation response with
|
|
94
101
|
* the overall total and one result per period, each labeled with the median
|
|
95
|
-
* date of its period.
|
|
102
|
+
* date of its period. It is typed by the fields the selection named, not by
|
|
103
|
+
* the whole model, whenever the selection is passed as an object literal or
|
|
104
|
+
* annotated with `satisfies`.
|
|
96
105
|
* @throws {@link HttpError} 400 if the selection is empty or names a field or
|
|
97
106
|
* operator that cannot be aggregated, and 500 on a database error.
|
|
98
107
|
*/
|
|
99
|
-
aggregate(filter: Query | undefined, select:
|
|
108
|
+
aggregate<S extends AggregateSelect<ReadOne>>(filter: Query | undefined, select: S, dateField?: string, from?: string, to?: string, step?: number, safeIncrement?: boolean): Promise<AggregateResponse<AggregateSelected<ReadOne, S>>>;
|
|
100
109
|
/**
|
|
101
110
|
* Creates a new resource. The provided data is merged with the write
|
|
102
111
|
* restrictions, checked for access, sanitized against the model configuration
|
|
@@ -124,7 +133,7 @@ export declare abstract class ResourceService<ReadOne = Resource, ReadMany = Res
|
|
|
124
133
|
* and a resource event carrying both the previous and the current state is
|
|
125
134
|
* emitted after a successful update.
|
|
126
135
|
*
|
|
127
|
-
* @param {
|
|
136
|
+
* @param {ResourceId} id The id of the resource to update.
|
|
128
137
|
* @param {Object} data The partial data to update the resource with, including
|
|
129
138
|
* any inline relation and file payloads.
|
|
130
139
|
* @returns {Promise<Object>} The updated resource with its virtual fields and
|
|
@@ -134,21 +143,21 @@ export declare abstract class ResourceService<ReadOne = Resource, ReadMany = Res
|
|
|
134
143
|
* if an inline relation payload is missing required fields or the relation
|
|
135
144
|
* does not accept new records, and 500 on a database error.
|
|
136
145
|
*/
|
|
137
|
-
update(id:
|
|
146
|
+
update(id: ResourceId, data: Update): Promise<ReadOne>;
|
|
138
147
|
/**
|
|
139
148
|
* Deletes an existing resource by its id. The current record is loaded with
|
|
140
149
|
* the read restrictions applied and checked for access before it is deleted
|
|
141
150
|
* inside a single transaction. The resource cache is invalidated and a
|
|
142
151
|
* resource event is emitted after a successful delete.
|
|
143
152
|
*
|
|
144
|
-
* @param {
|
|
153
|
+
* @param {ResourceId} id The id of the resource to delete.
|
|
145
154
|
* @returns {Promise<Object>} The deleted resource with its virtual fields and
|
|
146
155
|
* relation counts projected.
|
|
147
156
|
* @throws {@link HttpError} 404 if the resource does not exist or is filtered
|
|
148
157
|
* out by the read restrictions, 403 if the access check denies the action, and
|
|
149
158
|
* 500 on a database error.
|
|
150
159
|
*/
|
|
151
|
-
delete(id:
|
|
160
|
+
delete(id: ResourceId): Promise<ReadOne>;
|
|
152
161
|
/**
|
|
153
162
|
* This method should be overridden with custom logic for restricting read
|
|
154
163
|
* operations on specific data for currently logged-in user and other
|
|
@@ -160,7 +169,7 @@ export declare abstract class ResourceService<ReadOne = Resource, ReadMany = Res
|
|
|
160
169
|
*
|
|
161
170
|
* @param {ActionType} action The called action method on this service (find,
|
|
162
171
|
* query, aggregate, update, or delete)
|
|
163
|
-
* @param {Object|
|
|
172
|
+
* @param {Object|ResourceId} data The passed data to the called function can be
|
|
164
173
|
* number or object. If the data is a type of number, then it represents the
|
|
165
174
|
* resource id, otherwise it depends on the action and can be one of the
|
|
166
175
|
* following:
|