@appweaver/core 1.1.6 → 1.2.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.
Files changed (42) hide show
  1. package/cache/eviction/lfu-eviction-index.js +3 -0
  2. package/export/export-service.d.ts +4 -2
  3. package/export/export-service.js +27 -17
  4. package/factory/create-model.js +72 -36
  5. package/factory/create-service.js +1 -1
  6. package/package.json +1 -1
  7. package/resource/index.d.ts +1 -0
  8. package/resource/index.js +1 -0
  9. package/resource/resource-schema.d.ts +0 -5
  10. package/resource/resource-schema.js +25 -10
  11. package/resource/resource-service.d.ts +214 -36
  12. package/resource/resource-service.js +247 -404
  13. package/resource/schemas/index.d.ts +3 -0
  14. package/resource/schemas/index.js +19 -0
  15. package/resource/schemas/resource-aggregate-schema.d.ts +54 -0
  16. package/resource/schemas/resource-aggregate-schema.js +131 -0
  17. package/resource/schemas/resource-filter-schema.d.ts +39 -0
  18. package/resource/schemas/resource-filter-schema.js +153 -0
  19. package/resource/schemas/resource-sort-schema.d.ts +37 -0
  20. package/resource/schemas/resource-sort-schema.js +114 -0
  21. package/resource/utils/aggregate-util.d.ts +113 -0
  22. package/resource/utils/aggregate-util.js +323 -0
  23. package/resource/utils/filter-util.d.ts +24 -0
  24. package/resource/utils/filter-util.js +283 -0
  25. package/resource/utils/index.d.ts +4 -0
  26. package/resource/utils/index.js +20 -0
  27. package/resource/utils/relation-util.d.ts +84 -0
  28. package/resource/utils/relation-util.js +344 -0
  29. package/resource/utils/sort-util.d.ts +20 -0
  30. package/resource/utils/sort-util.js +218 -0
  31. package/security/create-auth-resources.js +2 -2
  32. package/security/helper.js +1 -1
  33. package/security/resources/api-key/model.js +1 -0
  34. package/security/resources/api-key/service.d.ts +1 -1
  35. package/security/resources/role/model.js +1 -1
  36. package/server/create-server.js +4 -1
  37. package/server/register-route.js +1 -0
  38. package/server/swagger.js +87 -21
  39. package/server/virtual-projection.js +10 -8
  40. package/types/generated.d.ts +110 -4
  41. package/utils/schema-util.js +1 -1
  42. package/utils/virtual-util.js +1 -1
@@ -1,36 +1,179 @@
1
- import { ActionType, AggregateResponse, AggregateSelect, IResourceService, QueryResponse, Resource, ResourceClient, ResourceData } from '@appweaver/common';
2
- export declare abstract class ResourceService<ReadOne = Resource, ReadMany = Resource, Create = ResourceData<Resource>, Update = Partial<ResourceData<Resource>>, Query = any> implements IResourceService<ReadOne, ReadMany, Create, Update, Query> {
1
+ import { ActionType, AggregateResponse, AggregateSelect, IResourceService, QueryFilter, QueryResponse, QuerySort, Resource, ResourceClient, ResourceData } from '@appweaver/common';
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
+ /**
5
+ * Creates the service for the given resource model and resolves its database
6
+ * client from the Prisma client instance.
7
+ *
8
+ * @param {string} modelName The resource model name this service operates on,
9
+ * as defined by its model file (i.e. `User`, `Post`).
10
+ * @throws An error if no database client exists for the provided model name.
11
+ */
4
12
  constructor(modelName: string);
13
+ /**
14
+ * The underlying Prisma model delegate for this resource, useful for
15
+ * executing custom database operations that the service methods do not cover.
16
+ *
17
+ * @returns {ResourceClient} The resource client of the model this service was
18
+ * created for.
19
+ */
5
20
  get client(): ResourceClient;
21
+ /**
22
+ * Finds a single resource by its id, applying the read restrictions and the
23
+ * access check of this service, and including all relation and file fields
24
+ * configured for output on the find action. A resource event is emitted after
25
+ * a successful lookup.
26
+ *
27
+ * @param {number} id The id of the resource to find.
28
+ * @returns {Promise<Object>} The found resource with its virtual fields and
29
+ * relation counts projected.
30
+ * @throws {@link HttpError} 404 if the resource does not exist or is filtered
31
+ * out by the read restrictions, 403 if the access check denies it, and 500 on
32
+ * a database error.
33
+ */
6
34
  find(id: number): Promise<ReadOne>;
7
- query(filter?: Query, page?: number, size?: number, sort?: string): Promise<QueryResponse<ReadMany>>;
35
+ /**
36
+ * Queries a page of resources matching the provided filter. The filter is
37
+ * mapped to a database query, combined with the optional `searchText` full
38
+ * text search query and the read restrictions of this service, and executed
39
+ * together with the total count in a single transaction. A resource event is
40
+ * emitted after a successful query.
41
+ *
42
+ * @param {Object} [filter] The query filter object, supporting the logical
43
+ * (`_and`, `_or`, `_not`, `_nor`), comparison (`_eq`, `_ne`, `_gt`, `_gte`,
44
+ * `_lt`, `_lte`, `_in`, `_nin`, `_between`, `_like`, `_ilike`, `_starts`,
45
+ * `_ends`, `_contains`, `_exists`), list (`_has`, `_hasSome`, `_hasEvery`,
46
+ * `_isEmpty`), and relation (`_some`, `_every`, `_none`) operators, as well
47
+ * as plain field values. Its `searchText` property, if present, is passed to
48
+ * {@link ResourceService.textSearchQuery} instead of being matched as a
49
+ * field.
50
+ * @param {number} [page] The one-based page number of results to return.
51
+ * @param {number} [size] The maximum number of results per page.
52
+ * @param {QuerySort} [sort] The fields to sort by, either as a comma-separated
53
+ * list where a field prefixed with `-` is sorted in descending order
54
+ * (i.e. `-createdAt,id`), or as an object of field directions
55
+ * (i.e. `{ createdAt: 'desc', id: 'asc' }`). Both forms support the fields of
56
+ * the included to-one relations, given with a dot notation (`author.createdAt`)
57
+ * or as a nested object (`{ author: { createdAt: 'desc' } }`).
58
+ * @returns {Promise<QueryResponse<Object>>} The paged query response containing
59
+ * the returned resources, the count of the returned items and the total count
60
+ * of matching resources.
61
+ * @throws {@link HttpError} 400 if the sort input names a field that cannot be
62
+ * sorted by, and 500 on a database error.
63
+ */
64
+ query(filter?: Query, page?: number, size?: number, sort?: QuerySort<ReadMany>): Promise<QueryResponse<ReadMany>>;
65
+ /**
66
+ * Aggregates resources matching the provided filter over a date range, both
67
+ * as a single overall result and as a series of results for the equally sized
68
+ * periods the range is split into. All aggregations are executed in a single
69
+ * transaction.
70
+ *
71
+ * @param {Object} [filter] The query filter object, mapped the same way as in
72
+ * {@link ResourceService.query}.
73
+ * @param {AggregateSelect<Object>} select The aggregation operations to perform
74
+ * per field (i.e. `{ views: { count: true, sum: true } }`). Only the numeric
75
+ * fields of the model accept every operator, while its date fields accept
76
+ * `count`, `min`, `max`, `first` and `last`. The `first` and `last` operators
77
+ * take the value the earliest and the latest record of a period holds, which
78
+ * costs one additional query per period and boundary, skipped for the periods
79
+ * holding no record.
80
+ * @param {string} [dateField] The date field the range is applied on.
81
+ * @param {string} [from] The ISO date string of the range start. Defaults to
82
+ * seven days before the range end.
83
+ * @param {string} [to] The ISO date string of the range end. Defaults to the
84
+ * current date and time.
85
+ * @param {number} [step] The size of a single period in units of the
86
+ * automatically selected time unit. If not provided, one unit is used and the
87
+ * unit is derived from the range length (seconds up to a minute, minutes up to
88
+ * an hour, hours up to a day, days up to a month, months up to a year, and
89
+ * years beyond that).
90
+ * @param {boolean} [safeIncrement] Whether the period increments use the
91
+ * derived time unit and stay consistent across daylight saving time changes.
92
+ * When false, the step is interpreted in seconds.
93
+ * @returns {Promise<AggregateResponse<Object>>} The aggregation response with
94
+ * the overall total and one result per period, each labeled with the median
95
+ * date of its period.
96
+ * @throws {@link HttpError} 400 if the selection is empty or names a field or
97
+ * operator that cannot be aggregated, and 500 on a database error.
98
+ */
8
99
  aggregate(filter: Query | undefined, select: AggregateSelect<ReadOne>, dateField?: string, from?: string, to?: string, step?: number, safeIncrement?: boolean): Promise<AggregateResponse<ReadOne>>;
100
+ /**
101
+ * Creates a new resource. The provided data is merged with the write
102
+ * restrictions, checked for access, sanitized against the model configuration
103
+ * and mapped to the relation write actions (connect, create, or
104
+ * connect-or-create) before the record is created. The `createdBy` audit
105
+ * relation is connected to the currently authenticated user when configured.
106
+ * The resource cache is invalidated and a resource event is emitted after a
107
+ * successful create.
108
+ *
109
+ * @param {Object} data The data of the resource to create, including any inline
110
+ * relation and file payloads.
111
+ * @returns {Promise<Object>} The created resource with its virtual fields and
112
+ * relation counts projected.
113
+ * @throws {@link HttpError} 403 if the access check denies the action, 400 if
114
+ * an inline relation payload is missing required fields or the relation does
115
+ * not accept new records, and 500 on a database error.
116
+ */
9
117
  create(data: Create): Promise<ReadOne>;
118
+ /**
119
+ * Updates an existing resource by its id. The current record is loaded with
120
+ * the read restrictions applied and checked for access, then updated with the
121
+ * data merged with the write restrictions inside a single transaction.
122
+ * Relations missing from the new value are disconnected, or deleted when
123
+ * `orphanRemoval` is configured for them. The resource cache is invalidated
124
+ * and a resource event carrying both the previous and the current state is
125
+ * emitted after a successful update.
126
+ *
127
+ * @param {number} id The id of the resource to update.
128
+ * @param {Object} data The partial data to update the resource with, including
129
+ * any inline relation and file payloads.
130
+ * @returns {Promise<Object>} The updated resource with its virtual fields and
131
+ * relation counts projected.
132
+ * @throws {@link HttpError} 404 if the resource does not exist or is filtered
133
+ * out by the read restrictions, 403 if the access check denies the action, 400
134
+ * if an inline relation payload is missing required fields or the relation
135
+ * does not accept new records, and 500 on a database error.
136
+ */
10
137
  update(id: number, data: Update): Promise<ReadOne>;
138
+ /**
139
+ * Deletes an existing resource by its id. The current record is loaded with
140
+ * the read restrictions applied and checked for access before it is deleted
141
+ * inside a single transaction. The resource cache is invalidated and a
142
+ * resource event is emitted after a successful delete.
143
+ *
144
+ * @param {number} id The id of the resource to delete.
145
+ * @returns {Promise<Object>} The deleted resource with its virtual fields and
146
+ * relation counts projected.
147
+ * @throws {@link HttpError} 404 if the resource does not exist or is filtered
148
+ * out by the read restrictions, 403 if the access check denies the action, and
149
+ * 500 on a database error.
150
+ */
11
151
  delete(id: number): Promise<ReadOne>;
12
152
  /**
13
153
  * This method should be overridden with custom logic for restricting read
14
154
  * operations on specific data for currently logged-in user and other
15
155
  * authorization rules. The returned object will be applied as a filter on
16
156
  * all actions (except create action) which will prevent unwanted data access
17
- * and modifications. This method can also cancel current action by throwing
18
- * an error, recommended is {@link HttpError } with appropriate HTTP error
19
- * code.
157
+ * and modifications. This method can also cancel the current action by
158
+ * throwing an error, recommended is {@link HttpError } with appropriate HTTP
159
+ * error code.
20
160
  *
21
- * @param action The called action method on this service (find, query,
22
- * aggregate, update, or delete)
23
- * @param data The passed data to the called function, can be number or
24
- * object. If the data is a type of number, then it represents the resource
25
- * id, otherwise it depends on the action and can be one of the following:
161
+ * @param {ActionType} action The called action method on this service (find,
162
+ * query, aggregate, update, or delete)
163
+ * @param {Object|number} data The passed data to the called function can be
164
+ * number or object. If the data is a type of number, then it represents the
165
+ * resource id, otherwise it depends on the action and can be one of the
166
+ * following:
26
167
  *
27
168
  * - query and aggregate -> filter object
28
169
  * - update -> combined id and the data object (i.e. { id, ...data })
29
170
  *
30
171
  * For other actions (find and delete) it represents the resource id.
31
- * @return The filter containing additional query restrictions.
172
+ * @return {Promise<Object>} The database-level `where` conditions containing
173
+ * additional query restrictions. The returned object is applied directly to
174
+ * the database query, so it uses the native Prisma filter syntax.
32
175
  */
33
- protected readRestrictions(action: Exclude<ActionType, 'create'>, data: any): Promise<Query>;
176
+ protected readRestrictions(action: Exclude<ActionType, 'create'>, data: any): Promise<any>;
34
177
  /**
35
178
  * This method should be overridden with custom logic for restricting write
36
179
  * operations on specific data for currently logged-in users and other
@@ -40,13 +183,15 @@ export declare abstract class ResourceService<ReadOne = Resource, ReadMany = Res
40
183
  * current action by throwing an error, with the recommended type being
41
184
  * {@link HttpError} with the appropriate HTTP error code.
42
185
  *
43
- * @param action The called action method on this service (create or update)
44
- * @param data The passed data to the called function, which should be an
45
- * object representing the resource data to be created or updated. For
186
+ * @param {'create'|'update'} action The called action method on this service
187
+ * (create or update)
188
+ * @param {Object} data The passed data to the called function, which should be
189
+ * an object representing the resource data to be created or updated. For
46
190
  * update operations, this object will also include the resource ID.
47
191
  *
48
- * @return A partial object containing additional data restrictions to be
49
- * applied, or an empty object if no restrictions are necessary.
192
+ * @return {Promise<Object>} A partial object containing additional data
193
+ * restrictions to be applied, or an empty object if no restrictions are
194
+ * necessary.
50
195
  */
51
196
  protected writeRestrictions(action: 'create' | 'update', data: any): Promise<Partial<Create & Update>>;
52
197
  /**
@@ -55,10 +200,11 @@ export declare abstract class ResourceService<ReadOne = Resource, ReadMany = Res
55
200
  * a currently authenticated user or other logic, this method should return
56
201
  * false. Otherwise, it returns true and continues with the request execution.
57
202
  *
58
- * @param action The called action method on this service (find, query,
59
- * aggregate, create, update, or delete)
60
- * @param resource The resource object that is being check for access.
61
- * @returns True if the access for resource is granted, false otherwise.
203
+ * @param {ActionType} action The called action method on this service (find,
204
+ * query, aggregate, create, update, or delete)
205
+ * @param {Object} resource The resource object that is being checked for access.
206
+ * @returns {Promise<boolean>} True if the access for resource is granted, false
207
+ * otherwise.
62
208
  */
63
209
  protected checkAccess(action: ActionType, resource: ReadOne): Promise<boolean>;
64
210
  /**
@@ -67,22 +213,54 @@ export declare abstract class ResourceService<ReadOne = Resource, ReadMany = Res
67
213
  * search text into a format suitable for text search functionality, returning
68
214
  * a query object that can be used to filter results.
69
215
  *
70
- * @param searchText The text string used for searching resources.
71
- * @returns A query object that represents the conditions for the text
72
- * search operation. This will be used by the database query methods to
73
- * retrieve matching resources.
216
+ * @param {string} searchText The text string used for searching resources.
217
+ * @returns {Object} A query object that represents the conditions for the text
218
+ * search operation, using the native Prisma filter syntax. This will be used
219
+ * by the database query methods to retrieve matching resources.
220
+ */
221
+ protected textSearchQuery(searchText: string): any;
222
+ /**
223
+ * Removes the `searchText` property from the provided filter and converts it
224
+ * into a text search query using
225
+ * {@link ResourceService.textSearchQuery}. The filter object is mutated so the
226
+ * search text is not matched as a regular resource field.
227
+ *
228
+ * @param {Object} filter The request filter object, possibly containing a
229
+ * `searchText` property. The property is deleted from this object when present.
230
+ * @returns {Object} The text search query for the extracted search text, or an
231
+ * empty object if the filter contains no search text.
74
232
  */
75
- protected textSearchQuery(searchText: string): Query;
76
233
  private extractTextSearchQuery;
77
- private mapSortValues;
78
- private mapAggregationValues;
79
- private makeAggregationIterator;
234
+ /**
235
+ * Prepares a resource for the response by resolving the virtual fields of its
236
+ * model and flattening the Prisma `_count` aggregation into the individual
237
+ * relation count fields (i.e. `_count.posts` becomes `postsCount`).
238
+ *
239
+ * @param {Object} resource The resource object as returned by the database
240
+ * client.
241
+ * @returns {Object} The same resource with the virtual fields resolved and, if a
242
+ * `_count` selection was present, with a count property per counted relation
243
+ * and the `_count` property removed.
244
+ */
80
245
  private projectResource;
246
+ /**
247
+ * Prepares a write payload for the database by removing the virtual fields
248
+ * that have no column, filling in default values for the hidden required
249
+ * scalars of a create action, and recursively applying the same rules to the
250
+ * nested relation and file payloads. Unique key values, arrays of them and null
251
+ * values are left untouched, so the relation actions can still map them to the
252
+ * connect and disconnect operations.
253
+ *
254
+ * @param {'create'|'update'} action The write action the payload is sanitized
255
+ * for. Default values for the hidden required scalars are only applied on a
256
+ * create action.
257
+ * @param {Object} data The write payload to sanitize.
258
+ * @param {string} [resourceName] The name of the model the payload belongs to.
259
+ * Defaults to the model of this service and is set to the related model name
260
+ * when recursing into a nested relation or file payload.
261
+ * @returns {Object} A shallow copy of the payload without the virtual fields,
262
+ * with the missing hidden required scalars defaulted, and with the nested
263
+ * payloads sanitized against their own models.
264
+ */
81
265
  private sanitizeData;
82
- private mapQueryFilter;
83
- private mapRelationInclusions;
84
- private buildNestedInclusion;
85
- private shouldIncludeRelation;
86
- private mapRelationActions;
87
- private createdByConnect;
88
266
  }