unconventional 0.0.2 → 0.0.4
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/core/abstractbase.controller.d.ts +34 -1
- package/dist/core/abstractbase.controller.js +101 -10
- package/dist/core/api-error.d.ts +0 -1
- package/dist/core/api-error.js +5 -1
- package/dist/core/base.cache.d.ts +6 -1
- package/dist/core/base.cache.js +31 -14
- package/dist/core/base.model.d.ts +0 -1
- package/dist/core/base.model.js +51 -46
- package/dist/core/base.options.d.ts +12 -1
- package/dist/core/base.options.js +2 -1
- package/dist/core/base.service.d.ts +46 -1
- package/dist/core/base.service.js +82 -30
- package/dist/core/index.d.ts +0 -1
- package/dist/core/index.js +22 -6
- package/dist/core/pg.driver.d.ts +1 -1
- package/dist/core/pg.driver.js +6 -1
- package/dist/decorators/index.d.ts +0 -1
- package/dist/decorators/index.decorator.d.ts +10 -1
- package/dist/decorators/index.decorator.js +16 -2
- package/dist/decorators/index.js +20 -4
- package/dist/decorators/prop.decorator.d.ts +14 -1
- package/dist/decorators/prop.decorator.js +19 -1
- package/dist/decorators/timestamp.decorator.d.ts +8 -1
- package/dist/decorators/timestamp.decorator.js +14 -2
- package/dist/decorators/utils.d.ts +5 -1
- package/dist/decorators/utils.js +10 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +21 -5
- package/dist/server.d.ts +11 -1
- package/dist/server.js +30 -10
- package/dist/types/api.types.d.ts +0 -1
- package/dist/types/api.types.js +11 -8
- package/dist/types/db.types.d.ts +0 -1
- package/dist/types/db.types.js +10 -7
- package/dist/types/decorator.types.d.ts +31 -1
- package/dist/types/decorator.types.js +9 -2
- package/dist/types/index.d.ts +0 -1
- package/dist/types/index.js +20 -4
- package/dist/types/server.types.d.ts +0 -1
- package/dist/types/server.types.js +2 -1
- package/dist/utils/api.utils.d.ts +15 -1
- package/dist/utils/api.utils.js +34 -8
- package/dist/utils/array.utils.d.ts +2 -1
- package/dist/utils/array.utils.js +9 -2
- package/dist/utils/cache.utils.d.ts +0 -1
- package/dist/utils/cache.utils.js +9 -2
- package/dist/utils/crypto.utils.d.ts +1 -1
- package/dist/utils/crypto.utils.js +7 -1
- package/dist/utils/db.utils.d.ts +5 -1
- package/dist/utils/db.utils.js +55 -36
- package/dist/utils/function.utils.d.ts +1 -1
- package/dist/utils/function.utils.js +6 -3
- package/dist/utils/index.d.ts +0 -1
- package/dist/utils/index.js +24 -8
- package/dist/utils/middleware.utils.d.ts +4 -1
- package/dist/utils/middleware.utils.js +9 -1
- package/dist/utils/server.utils.d.ts +3 -1
- package/dist/utils/server.utils.js +20 -9
- package/package.json +1 -1
- package/dist/core/abstractbase.controller.d.ts.map +0 -1
- package/dist/core/api-error.d.ts.map +0 -1
- package/dist/core/base.cache.d.ts.map +0 -1
- package/dist/core/base.model.d.ts.map +0 -1
- package/dist/core/base.options.d.ts.map +0 -1
- package/dist/core/base.service.d.ts.map +0 -1
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/pg.driver.d.ts.map +0 -1
- package/dist/decorators/index.d.ts.map +0 -1
- package/dist/decorators/index.decorator.d.ts.map +0 -1
- package/dist/decorators/prop.decorator.d.ts.map +0 -1
- package/dist/decorators/timestamp.decorator.d.ts.map +0 -1
- package/dist/decorators/utils.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/types/api.types.d.ts.map +0 -1
- package/dist/types/db.types.d.ts.map +0 -1
- package/dist/types/decorator.types.d.ts.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/server.types.d.ts.map +0 -1
- package/dist/utils/api.utils.d.ts.map +0 -1
- package/dist/utils/array.utils.d.ts.map +0 -1
- package/dist/utils/cache.utils.d.ts.map +0 -1
- package/dist/utils/crypto.utils.d.ts.map +0 -1
- package/dist/utils/db.utils.d.ts.map +0 -1
- package/dist/utils/function.utils.d.ts.map +0 -1
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/middleware.utils.d.ts.map +0 -1
- package/dist/utils/server.utils.d.ts.map +0 -1
|
@@ -10,6 +10,7 @@ export default abstract class AbstractBaseController<M extends typeof BaseModel,
|
|
|
10
10
|
protected _pageLimitDefault: number;
|
|
11
11
|
protected _pageLimitMax: number;
|
|
12
12
|
protected _options: ControllerOptions;
|
|
13
|
+
/** A controller providing CRUD endpoints for any BaseModel */
|
|
13
14
|
constructor(model: M, options?: ControllerOptions);
|
|
14
15
|
create(options?: ControllerMethodOptions<M, E>): BaseHandler<E>;
|
|
15
16
|
createMany(options?: ControllerMethodOptions<M, E, PI<M>[], I<M>[]>): BaseHandler<E>;
|
|
@@ -19,18 +20,50 @@ export default abstract class AbstractBaseController<M extends typeof BaseModel,
|
|
|
19
20
|
updateMany(options?: ControllerMethodOptions<M, E, PI<M>[], I<M>[]>): BaseHandler<E>;
|
|
20
21
|
delete(options?: ControllerMethodOptions<M, E>): BaseHandler<E>;
|
|
21
22
|
deleteMany(options?: ControllerMethodOptions<M, E, PI<M>[], I<M>[]>): BaseHandler<E>;
|
|
23
|
+
/**
|
|
24
|
+
* Creates and returns a new instance of the model M from the provided data. Request body should be a Partial<InstanceType<M>>.
|
|
25
|
+
* @param req.body - A partial model instance containing all the fields to be modified and their new values.
|
|
26
|
+
*/
|
|
22
27
|
private _create;
|
|
28
|
+
/**
|
|
29
|
+
* Creates and returns an array of new instances of the model M from the provided data. Request body should be a Partial<InstanceType<M>>[].
|
|
30
|
+
* @param req.body - An array of partial model instances containing all the fields to be modified and their new values.
|
|
31
|
+
*/
|
|
23
32
|
private _createMany;
|
|
33
|
+
/** Gets one instance of the model M.
|
|
34
|
+
* @param req.params.id - The id or key of a model. If type is a valid ObjectId, lookup will be done with the model's idColumn. Otherwise, keyColumn is used.
|
|
35
|
+
*/
|
|
24
36
|
private _get;
|
|
37
|
+
/**
|
|
38
|
+
* Gets all instances (paginated) of the model. Supported query params: limit, cursor, sort.
|
|
39
|
+
* Default limit is 12. Max limit is 100.
|
|
40
|
+
*/
|
|
25
41
|
private _getAll;
|
|
42
|
+
/**
|
|
43
|
+
* Patches all fields in an indicated instance of model M. Certain fields may be restricted.
|
|
44
|
+
* @param req.params.id - The id or key of a model. If type is a valid ObjectId, lookup will be done with the model's idColumn. Otherwise, keyColumn is used.
|
|
45
|
+
* @param req.body - A Partial<InstanceType<M>> containing all the fields to be modified and their new values.
|
|
46
|
+
*/
|
|
26
47
|
private _update;
|
|
48
|
+
/**
|
|
49
|
+
* Patches all fields in the array of instances of model M. Certain fields may be restricted.
|
|
50
|
+
* @param req.body - An array of Partial<InstanceType<M>> containing all the fields to be modified and their new values.
|
|
51
|
+
*/
|
|
27
52
|
private _updateMany;
|
|
53
|
+
/**
|
|
54
|
+
* Deletes the model with the given identifier.
|
|
55
|
+
* @param req.params.id - The id or key of a model. If type is a valid ObjectId, lookup will be done with the model's idColumn. Otherwise, keyColumn is used.
|
|
56
|
+
*/
|
|
28
57
|
private _delete;
|
|
58
|
+
/** Deletes models matching the provided filter. */
|
|
29
59
|
private _deleteMany;
|
|
30
60
|
protected getRequestContext<T extends PI<M> | PI<M>[] = PI<M>>(ctx: Context<E>, hasBody?: boolean): Promise<RequestContext<M, E, T>>;
|
|
61
|
+
/** Gets the cache namespace for the given request. Route-level cache flags take precedence over controller-level. */
|
|
31
62
|
protected getCache(ctx: Context, controllerLevel: boolean | undefined, routeLevel: boolean | undefined): KVNamespace | undefined;
|
|
63
|
+
/** Removes the model's private fields from the given response object */
|
|
32
64
|
protected abstract maskPrivateFields(req: RequestContext<M, E>, response: InstanceType<M> | InstanceType<M>[]): Promise<void>;
|
|
65
|
+
/** Removes the model's uneditable fields from the given request body */
|
|
33
66
|
protected abstract removeSystemFields<T extends PI<M> | PI<M>[] = PI<M>>(req: RequestContext<M, E, T>): void;
|
|
67
|
+
/** Prevents insecure direct object references (IDOR) */
|
|
34
68
|
protected abstract preventIDOR<T extends PI<M> | PI<M>[] = PI<M>>(req: RequestContext<M, E, T>): Promise<void>;
|
|
35
69
|
}
|
|
36
|
-
//# sourceMappingURL=abstractbase.controller.d.ts.map
|
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const api_types_1 = require("../types/api.types");
|
|
7
|
+
const base_service_1 = __importDefault(require("./base.service"));
|
|
8
|
+
class AbstractBaseController {
|
|
4
9
|
_model;
|
|
5
10
|
_service;
|
|
6
11
|
_pageLimitDefault = 12;
|
|
7
12
|
_pageLimitMax = 100;
|
|
8
13
|
_options = {};
|
|
14
|
+
/** A controller providing CRUD endpoints for any BaseModel */
|
|
9
15
|
constructor(model, options) {
|
|
10
16
|
this._model = model;
|
|
11
|
-
this._service = new
|
|
17
|
+
this._service = new base_service_1.default(model);
|
|
12
18
|
this._options = options || {};
|
|
13
19
|
}
|
|
14
20
|
create(options) { return this._create.bind(this, options); }
|
|
@@ -19,17 +25,26 @@ export default class AbstractBaseController {
|
|
|
19
25
|
updateMany(options) { return this._updateMany.bind(this, options); }
|
|
20
26
|
delete(options) { return this._delete.bind(this, options); }
|
|
21
27
|
deleteMany(options) { return this._deleteMany.bind(this, options); }
|
|
28
|
+
/**
|
|
29
|
+
* Creates and returns a new instance of the model M from the provided data. Request body should be a Partial<InstanceType<M>>.
|
|
30
|
+
* @param req.body - A partial model instance containing all the fields to be modified and their new values.
|
|
31
|
+
*/
|
|
22
32
|
async _create(options, ctx) {
|
|
33
|
+
// Context
|
|
23
34
|
const reqContext = await this.getRequestContext(ctx);
|
|
35
|
+
// Pre-processing
|
|
24
36
|
await this.preventIDOR(reqContext);
|
|
25
37
|
if (options?.before)
|
|
26
38
|
await options.before(reqContext);
|
|
27
|
-
const isPrivileged = ctx.get(BaseEnvKey.isPrivileged);
|
|
39
|
+
const isPrivileged = ctx.get(api_types_1.BaseEnvKey.isPrivileged);
|
|
40
|
+
// Avoid overriding owner field if it's privileged user
|
|
28
41
|
if (this._model.ownerField && !(reqContext.body[this._model.ownerField] && isPrivileged)) {
|
|
29
|
-
|
|
42
|
+
// Add owner
|
|
43
|
+
const ownerId = ctx.get(api_types_1.BaseEnvKey.ownerId);
|
|
30
44
|
if (ownerId && this._model.ownerField)
|
|
31
45
|
reqContext.body[this._model.ownerField] = ownerId;
|
|
32
46
|
}
|
|
47
|
+
// Configure service
|
|
33
48
|
const config = {
|
|
34
49
|
cache: this.getCache(ctx, this._options.cache, options?.cache),
|
|
35
50
|
cacheTTL: this._options.cacheTTL ?? options?.cacheTTL,
|
|
@@ -37,24 +52,36 @@ export default class AbstractBaseController {
|
|
|
37
52
|
constraint: options?.upsertContraint
|
|
38
53
|
}
|
|
39
54
|
};
|
|
55
|
+
// Request
|
|
40
56
|
let response = await this._service.create(reqContext.body, config);
|
|
57
|
+
// Post-processing
|
|
41
58
|
if (options?.after)
|
|
42
59
|
await options.after(reqContext, response);
|
|
60
|
+
// Response
|
|
43
61
|
return ctx.json(response);
|
|
44
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Creates and returns an array of new instances of the model M from the provided data. Request body should be a Partial<InstanceType<M>>[].
|
|
65
|
+
* @param req.body - An array of partial model instances containing all the fields to be modified and their new values.
|
|
66
|
+
*/
|
|
45
67
|
async _createMany(options, ctx) {
|
|
68
|
+
// Context
|
|
46
69
|
const reqContext = await this.getRequestContext(ctx);
|
|
70
|
+
// Pre-processing
|
|
47
71
|
await this.preventIDOR(reqContext);
|
|
48
72
|
if (options?.before)
|
|
49
73
|
await options.before(reqContext);
|
|
50
|
-
const isPrivileged = ctx.get(BaseEnvKey.isPrivileged);
|
|
74
|
+
const isPrivileged = ctx.get(api_types_1.BaseEnvKey.isPrivileged);
|
|
75
|
+
// Avoid overriding owner field if it's privileged user
|
|
51
76
|
reqContext.body.forEach(x => {
|
|
52
77
|
if (this._model.ownerField && !(x[this._model.ownerField] && isPrivileged)) {
|
|
53
|
-
|
|
78
|
+
// Add owner
|
|
79
|
+
const ownerId = ctx.get(api_types_1.BaseEnvKey.ownerId);
|
|
54
80
|
if (ownerId && this._model.ownerField)
|
|
55
81
|
x[this._model.ownerField] = ownerId;
|
|
56
82
|
}
|
|
57
83
|
});
|
|
84
|
+
// Configure service
|
|
58
85
|
const config = {
|
|
59
86
|
cache: this.getCache(ctx, this._options.cache, options?.cache),
|
|
60
87
|
cacheTTL: this._options.cacheTTL ?? options?.cacheTTL,
|
|
@@ -62,25 +89,37 @@ export default class AbstractBaseController {
|
|
|
62
89
|
constraint: options?.upsertContraint
|
|
63
90
|
}
|
|
64
91
|
};
|
|
92
|
+
// Request
|
|
65
93
|
let response = await this._service.createMany(reqContext.body, config);
|
|
94
|
+
// Post-processing
|
|
66
95
|
if (options?.after)
|
|
67
96
|
await options.after(reqContext, response);
|
|
97
|
+
// Response
|
|
68
98
|
return ctx.json(response);
|
|
69
99
|
}
|
|
100
|
+
/** Gets one instance of the model M.
|
|
101
|
+
* @param req.params.id - The id or key of a model. If type is a valid ObjectId, lookup will be done with the model's idColumn. Otherwise, keyColumn is used.
|
|
102
|
+
*/
|
|
70
103
|
async _get(options, ctx) {
|
|
104
|
+
// Context
|
|
71
105
|
const reqContext = await this.getRequestContext(ctx, false);
|
|
106
|
+
// Pre-processing
|
|
72
107
|
if (options?.before)
|
|
73
108
|
await options.before(reqContext);
|
|
74
109
|
const identifier = reqContext.params.id;
|
|
110
|
+
// Configure service
|
|
75
111
|
const config = {
|
|
76
|
-
expand: reqContext.queries[Query.Expand] || undefined,
|
|
112
|
+
expand: reqContext.queries[api_types_1.Query.Expand] || undefined,
|
|
77
113
|
cache: this.getCache(ctx, this._options.cache, options?.cache),
|
|
78
114
|
cacheTTL: this._options.cacheTTL ?? options?.cacheTTL
|
|
79
115
|
};
|
|
116
|
+
// Request
|
|
80
117
|
let response = await this._service.get(identifier, config);
|
|
118
|
+
// Post-processing
|
|
81
119
|
if (options?.after)
|
|
82
120
|
await options.after(reqContext, response);
|
|
83
121
|
await this.maskPrivateFields(reqContext, response);
|
|
122
|
+
// Download hook
|
|
84
123
|
if (options?.download) {
|
|
85
124
|
const downloadRes = await options.download(reqContext, response);
|
|
86
125
|
const headers = {
|
|
@@ -90,89 +129,139 @@ export default class AbstractBaseController {
|
|
|
90
129
|
return ctx.body(downloadRes.buffer, 200, headers);
|
|
91
130
|
}
|
|
92
131
|
else
|
|
132
|
+
// Response
|
|
93
133
|
return ctx.json(response);
|
|
94
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Gets all instances (paginated) of the model. Supported query params: limit, cursor, sort.
|
|
137
|
+
* Default limit is 12. Max limit is 100.
|
|
138
|
+
*/
|
|
95
139
|
async _getAll(options, ctx) {
|
|
140
|
+
// Context
|
|
96
141
|
const reqContext = await this.getRequestContext(ctx, false);
|
|
142
|
+
// Pre-processing
|
|
97
143
|
if (options?.before)
|
|
98
144
|
await options.before(reqContext);
|
|
145
|
+
// Configuration
|
|
99
146
|
const queryParams = reqContext.queries;
|
|
100
147
|
let limit = parseInt(queryParams.limit) || this._pageLimitDefault;
|
|
101
148
|
if (limit > this._pageLimitMax && !options?.limitOverride)
|
|
102
149
|
limit = this._pageLimitMax;
|
|
150
|
+
// Request
|
|
103
151
|
let response = await this._service.getAll({
|
|
104
152
|
limit: limit,
|
|
105
153
|
cursor: queryParams.cursor,
|
|
106
154
|
sort: queryParams.orderBy,
|
|
107
155
|
filter: queryParams.filter,
|
|
108
|
-
expand: reqContext.queries[Query.Expand] || undefined,
|
|
156
|
+
expand: reqContext.queries[api_types_1.Query.Expand] || undefined,
|
|
109
157
|
cache: this.getCache(ctx, this._options.cache, options?.cache),
|
|
110
158
|
cacheTTL: this._options.cacheTTL ?? options?.cacheTTL
|
|
111
159
|
});
|
|
160
|
+
// Post-processing
|
|
112
161
|
if (options?.after)
|
|
113
162
|
await options.after(reqContext, response);
|
|
114
163
|
await this.maskPrivateFields(reqContext, response);
|
|
164
|
+
// Response
|
|
115
165
|
return ctx.json(response);
|
|
116
166
|
}
|
|
167
|
+
/**
|
|
168
|
+
* Patches all fields in an indicated instance of model M. Certain fields may be restricted.
|
|
169
|
+
* @param req.params.id - The id or key of a model. If type is a valid ObjectId, lookup will be done with the model's idColumn. Otherwise, keyColumn is used.
|
|
170
|
+
* @param req.body - A Partial<InstanceType<M>> containing all the fields to be modified and their new values.
|
|
171
|
+
*/
|
|
117
172
|
async _update(options, ctx) {
|
|
173
|
+
// Context
|
|
118
174
|
const reqContext = await this.getRequestContext(ctx);
|
|
175
|
+
// Pre-processing
|
|
119
176
|
await this.preventIDOR(reqContext);
|
|
120
177
|
this.removeSystemFields(reqContext);
|
|
121
178
|
if (options?.before)
|
|
122
179
|
await options.before(reqContext);
|
|
123
180
|
const identifier = reqContext.params.id;
|
|
181
|
+
// Configure service
|
|
124
182
|
const config = {
|
|
125
183
|
cache: this.getCache(ctx, this._options.cache, options?.cache),
|
|
126
184
|
cacheTTL: this._options.cacheTTL ?? options?.cacheTTL
|
|
127
185
|
};
|
|
186
|
+
// Request
|
|
128
187
|
let response = await this._service.update(identifier, reqContext.body, config);
|
|
188
|
+
// Post-processing
|
|
129
189
|
if (options?.after)
|
|
130
190
|
await options.after(reqContext, response);
|
|
191
|
+
// Response
|
|
131
192
|
return ctx.json(response);
|
|
132
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* Patches all fields in the array of instances of model M. Certain fields may be restricted.
|
|
196
|
+
* @param req.body - An array of Partial<InstanceType<M>> containing all the fields to be modified and their new values.
|
|
197
|
+
*/
|
|
133
198
|
async _updateMany(options, ctx) {
|
|
199
|
+
// Context
|
|
134
200
|
const reqContext = await this.getRequestContext(ctx);
|
|
201
|
+
// Pre-processing
|
|
135
202
|
await this.preventIDOR(reqContext);
|
|
136
203
|
this.removeSystemFields(reqContext);
|
|
137
204
|
if (options?.before)
|
|
138
205
|
await options.before(reqContext);
|
|
206
|
+
// Configure service
|
|
139
207
|
const config = {
|
|
140
208
|
cache: this.getCache(ctx, this._options.cache, options?.cache),
|
|
141
209
|
cacheTTL: this._options.cacheTTL ?? options?.cacheTTL
|
|
142
210
|
};
|
|
211
|
+
// Request
|
|
143
212
|
let response = await this._service.updateMany(reqContext.body, config);
|
|
213
|
+
// Post-processing
|
|
144
214
|
if (options?.after)
|
|
145
215
|
await options.after(reqContext, response);
|
|
216
|
+
// Response
|
|
146
217
|
return ctx.json(response);
|
|
147
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* Deletes the model with the given identifier.
|
|
221
|
+
* @param req.params.id - The id or key of a model. If type is a valid ObjectId, lookup will be done with the model's idColumn. Otherwise, keyColumn is used.
|
|
222
|
+
*/
|
|
148
223
|
async _delete(options, ctx) {
|
|
224
|
+
// Context
|
|
149
225
|
const reqContext = await this.getRequestContext(ctx);
|
|
226
|
+
// Pre-processing
|
|
150
227
|
if (options?.before)
|
|
151
228
|
await options.before(reqContext);
|
|
152
229
|
const identifier = reqContext.params.id;
|
|
230
|
+
// Configure service
|
|
153
231
|
const config = {
|
|
154
232
|
cache: this.getCache(ctx, this._options.cache, options?.cache),
|
|
155
233
|
cacheTTL: this._options.cacheTTL ?? options?.cacheTTL
|
|
156
234
|
};
|
|
235
|
+
// Request
|
|
157
236
|
let response = await this._service.delete(identifier, config);
|
|
237
|
+
// Post-processing
|
|
158
238
|
if (options?.after)
|
|
159
239
|
await options.after(reqContext, response);
|
|
240
|
+
// Response
|
|
160
241
|
return ctx.json({ success: !!response });
|
|
161
242
|
}
|
|
243
|
+
/** Deletes models matching the provided filter. */
|
|
162
244
|
async _deleteMany(options, ctx) {
|
|
245
|
+
// Context
|
|
163
246
|
const reqContext = await this.getRequestContext(ctx);
|
|
247
|
+
// Pre-processing
|
|
164
248
|
if (options?.before)
|
|
165
249
|
await options.before(reqContext);
|
|
250
|
+
// Configure service
|
|
166
251
|
const config = {
|
|
167
252
|
filter: reqContext.queries.filter,
|
|
168
253
|
cache: this.getCache(ctx, this._options.cache, options?.cache),
|
|
169
254
|
cacheTTL: this._options.cacheTTL ?? options?.cacheTTL
|
|
170
255
|
};
|
|
256
|
+
// Request
|
|
171
257
|
let response = await this._service.deleteMany(config);
|
|
258
|
+
// Post-processing
|
|
172
259
|
if (options?.after)
|
|
173
260
|
await options.after(reqContext, response);
|
|
261
|
+
// Response
|
|
174
262
|
return ctx.json({ success: !!response });
|
|
175
263
|
}
|
|
264
|
+
//#region Helpers
|
|
176
265
|
async getRequestContext(ctx, hasBody = true) {
|
|
177
266
|
let body = {};
|
|
178
267
|
if (hasBody) {
|
|
@@ -191,8 +280,10 @@ export default class AbstractBaseController {
|
|
|
191
280
|
get: (key) => { return ctx.get(key); }
|
|
192
281
|
};
|
|
193
282
|
}
|
|
283
|
+
/** Gets the cache namespace for the given request. Route-level cache flags take precedence over controller-level. */
|
|
194
284
|
getCache(ctx, controllerLevel, routeLevel) {
|
|
195
285
|
const useCache = routeLevel ?? controllerLevel;
|
|
196
286
|
return useCache ? ctx.env.CACHE : undefined;
|
|
197
287
|
}
|
|
198
288
|
}
|
|
289
|
+
exports.default = AbstractBaseController;
|
package/dist/core/api-error.d.ts
CHANGED
package/dist/core/api-error.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.APIError = void 0;
|
|
4
|
+
class APIError extends Error {
|
|
2
5
|
data;
|
|
3
6
|
internalProperties;
|
|
4
7
|
statusCode;
|
|
@@ -76,3 +79,4 @@ export class APIError extends Error {
|
|
|
76
79
|
return new APIError('Internal Server Error', 'Request could not be carried out.', 500, data, internalProperties);
|
|
77
80
|
}
|
|
78
81
|
}
|
|
82
|
+
exports.APIError = APIError;
|
|
@@ -5,12 +5,17 @@ export default class BaseCache {
|
|
|
5
5
|
static get<T>(cache: KVNamespace, key: string): Promise<T | null>;
|
|
6
6
|
static set<T>(cache: KVNamespace, key: string, value: T, ttl?: number): Promise<void>;
|
|
7
7
|
static delete(cache: KVNamespace, key: string): Promise<void>;
|
|
8
|
+
/** Sets a model in the cache. Use @param modifier for non-standard responses. */
|
|
8
9
|
static setModel<T extends BaseModel>(cache: KVNamespace, model: T, modifier?: object, ttl?: number): Promise<void>;
|
|
10
|
+
/** Gets a model from the cache. Use @param modifier for non-standard lookups. */
|
|
9
11
|
static getModel<U extends typeof BaseModel>(cache: KVNamespace, model: U, identifier: string | number, modifier?: object): Promise<InstanceType<U> | null>;
|
|
12
|
+
/** Deletes a model from the cache. Deletes all modified versions too. */
|
|
10
13
|
static clearModel<T extends BaseModel>(cache: KVNamespace, model: T | undefined): Promise<void>;
|
|
14
|
+
/** Sets a page in the cache. Use @param modifier for non-standard responses. */
|
|
11
15
|
static setPage<T extends BaseModel>(cache: KVNamespace, page: T[], modifier?: object, ttl?: number): Promise<void>;
|
|
16
|
+
/** Gets a page from the cache. Use @param modifier for non-standard lookups. */
|
|
12
17
|
static getPage<U extends typeof BaseModel>(cache: KVNamespace, model: U, modifier?: object): Promise<InstanceType<U>[] | null>;
|
|
18
|
+
/** Deletes a page from the cache. Deletes all modified versions too. */
|
|
13
19
|
static clearPage<T extends typeof BaseModel>(cache: KVNamespace, model: T): Promise<void>;
|
|
14
20
|
private static getModifier;
|
|
15
21
|
}
|
|
16
|
-
//# sourceMappingURL=base.cache.d.ts.map
|
package/dist/core/base.cache.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const api_types_1 = require("../types/api.types");
|
|
4
|
+
const array_utils_1 = require("../utils/array.utils");
|
|
5
|
+
const crypto_utils_1 = require("../utils/crypto.utils");
|
|
6
|
+
class BaseCache {
|
|
5
7
|
static kvKeySizeLimit = 512;
|
|
6
8
|
static async get(cache, key) {
|
|
7
9
|
if (key.length > this.kvKeySizeLimit)
|
|
@@ -11,62 +13,77 @@ export default class BaseCache {
|
|
|
11
13
|
return null;
|
|
12
14
|
return JSON.parse(response);
|
|
13
15
|
}
|
|
14
|
-
static async set(cache, key, value, ttl = DefaultCacheTTL) {
|
|
16
|
+
static async set(cache, key, value, ttl = api_types_1.DefaultCacheTTL) {
|
|
15
17
|
key.length <= this.kvKeySizeLimit && await cache.put(key, JSON.stringify(value), { expirationTtl: ttl });
|
|
16
18
|
}
|
|
17
19
|
static async delete(cache, key) {
|
|
18
20
|
key.length <= this.kvKeySizeLimit && await cache.delete(key);
|
|
19
21
|
}
|
|
22
|
+
//#region Model Methods
|
|
23
|
+
/** Sets a model in the cache. Use @param modifier for non-standard responses. */
|
|
20
24
|
static async setModel(cache, model, modifier, ttl) {
|
|
21
25
|
const identifier = model?.$id();
|
|
22
26
|
if (!identifier)
|
|
23
27
|
return;
|
|
24
28
|
const modifierTag = await this.getModifier(modifier);
|
|
25
|
-
await this.set(cache, `${CachePrefix.Record}${model.constructor.name}:${identifier.toString()}:${modifierTag}`, model, ttl);
|
|
29
|
+
await this.set(cache, `${api_types_1.CachePrefix.Record}${model.constructor.name}:${identifier.toString()}:${modifierTag}`, model, ttl);
|
|
26
30
|
if (model.$key()) {
|
|
27
|
-
await this.set(cache, `${CachePrefix.Record}${model.constructor.name}:${model.$key()}:${modifierTag}`, model, ttl);
|
|
31
|
+
await this.set(cache, `${api_types_1.CachePrefix.Record}${model.constructor.name}:${model.$key()}:${modifierTag}`, model, ttl);
|
|
28
32
|
}
|
|
29
33
|
}
|
|
34
|
+
/** Gets a model from the cache. Use @param modifier for non-standard lookups. */
|
|
30
35
|
static async getModel(cache, model, identifier, modifier) {
|
|
31
36
|
const modifierTag = await this.getModifier(modifier);
|
|
32
|
-
return await this.get(cache, `${CachePrefix.Record}${model.name}:${identifier.toString()}:${modifierTag}`);
|
|
37
|
+
return await this.get(cache, `${api_types_1.CachePrefix.Record}${model.name}:${identifier.toString()}:${modifierTag}`);
|
|
33
38
|
}
|
|
39
|
+
/** Deletes a model from the cache. Deletes all modified versions too. */
|
|
34
40
|
static async clearModel(cache, model) {
|
|
35
41
|
if (!model)
|
|
36
42
|
return;
|
|
37
43
|
const identifier = model?.$id();
|
|
38
44
|
if (!identifier)
|
|
39
45
|
return;
|
|
40
|
-
|
|
46
|
+
// Delete by ID
|
|
47
|
+
const idList = await cache.list({ prefix: `${api_types_1.CachePrefix.Record}${model.constructor.name}:${identifier.toString()}` });
|
|
41
48
|
const idPromises = idList.keys.map(async (key) => {
|
|
42
49
|
await this.delete(cache, key.name);
|
|
43
50
|
});
|
|
51
|
+
// Delete by Key
|
|
44
52
|
let keyPromises = [];
|
|
45
53
|
if (model.$key()) {
|
|
46
|
-
const keyList = await cache.list({ prefix: `${CachePrefix.Record}${model.constructor.name}:${model.$key()}` });
|
|
54
|
+
const keyList = await cache.list({ prefix: `${api_types_1.CachePrefix.Record}${model.constructor.name}:${model.$key()}` });
|
|
47
55
|
keyPromises = keyList.keys.map(async (key) => {
|
|
48
56
|
await this.delete(cache, key.name);
|
|
49
57
|
});
|
|
50
58
|
}
|
|
59
|
+
// Execute
|
|
51
60
|
await Promise.all([...idPromises, ...keyPromises]);
|
|
52
61
|
}
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region Page Methods
|
|
64
|
+
/** Sets a page in the cache. Use @param modifier for non-standard responses. */
|
|
53
65
|
static async setPage(cache, page, modifier, ttl) {
|
|
54
66
|
if (!page.length)
|
|
55
67
|
return;
|
|
56
68
|
const modifierTag = await this.getModifier(modifier);
|
|
57
|
-
await this.set(cache, `${CachePrefix.Page}${page[0].constructor.name}:${modifierTag}`, page, ttl);
|
|
69
|
+
await this.set(cache, `${api_types_1.CachePrefix.Page}${page[0].constructor.name}:${modifierTag}`, page, ttl);
|
|
58
70
|
}
|
|
71
|
+
/** Gets a page from the cache. Use @param modifier for non-standard lookups. */
|
|
59
72
|
static async getPage(cache, model, modifier) {
|
|
60
73
|
const modifierTag = await this.getModifier(modifier);
|
|
61
|
-
return await this.get(cache, `${CachePrefix.Page}${model.name}:${modifierTag}`);
|
|
74
|
+
return await this.get(cache, `${api_types_1.CachePrefix.Page}${model.name}:${modifierTag}`);
|
|
62
75
|
}
|
|
76
|
+
/** Deletes a page from the cache. Deletes all modified versions too. */
|
|
63
77
|
static async clearPage(cache, model) {
|
|
64
|
-
const list = await cache.list({ prefix: `${CachePrefix.Page}${model.name}` });
|
|
78
|
+
const list = await cache.list({ prefix: `${api_types_1.CachePrefix.Page}${model.name}` });
|
|
65
79
|
Promise.all(list.keys.map(async (key) => {
|
|
66
80
|
await this.delete(cache, key.name);
|
|
67
81
|
}));
|
|
68
82
|
}
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region Helpers
|
|
69
85
|
static async getModifier(modifier) {
|
|
70
|
-
return modifier && !isEmpty(modifier) ? await sha256(JSON.stringify(modifier)) : "";
|
|
86
|
+
return modifier && !(0, array_utils_1.isEmpty)(modifier) ? await (0, crypto_utils_1.sha256)(JSON.stringify(modifier)) : "";
|
|
71
87
|
}
|
|
72
88
|
}
|
|
89
|
+
exports.default = BaseCache;
|