@nhtio/lucid-resourceful 0.1.0-master-0588a748
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/LICENSE.md +9 -0
- package/README.md +5 -0
- package/decorator_utils-1yWqd_Gg.cjs +6792 -0
- package/decorator_utils-1yWqd_Gg.cjs.map +1 -0
- package/decorator_utils-BUuBwQYK.js +6793 -0
- package/decorator_utils-BUuBwQYK.js.map +1 -0
- package/definitions-B66EPk0H.js +381 -0
- package/definitions-B66EPk0H.js.map +1 -0
- package/definitions-BrN-oCRI.cjs +380 -0
- package/definitions-BrN-oCRI.cjs.map +1 -0
- package/definitions.cjs +15 -0
- package/definitions.cjs.map +1 -0
- package/definitions.d.ts +5 -0
- package/definitions.mjs +15 -0
- package/definitions.mjs.map +1 -0
- package/errors-B1rr67uM.js +3004 -0
- package/errors-B1rr67uM.js.map +1 -0
- package/errors-D8jb9VxY.cjs +3000 -0
- package/errors-D8jb9VxY.cjs.map +1 -0
- package/errors.cjs +22 -0
- package/errors.cjs.map +1 -0
- package/errors.d.ts +94 -0
- package/errors.mjs +22 -0
- package/errors.mjs.map +1 -0
- package/index-Cv6KC1rC.cjs +670 -0
- package/index-Cv6KC1rC.cjs.map +1 -0
- package/index-DDaZ2qr2.js +671 -0
- package/index-DDaZ2qr2.js.map +1 -0
- package/index.cjs +12706 -0
- package/index.cjs.map +1 -0
- package/index.d.ts +14 -0
- package/index.mjs +12708 -0
- package/index.mjs.map +1 -0
- package/joi.cjs +5 -0
- package/joi.cjs.map +1 -0
- package/joi.d.ts +5 -0
- package/joi.mjs +5 -0
- package/joi.mjs.map +1 -0
- package/package.json +65 -0
- package/private/constants.d.ts +11 -0
- package/private/controller_factory.d.ts +1 -0
- package/private/data_type_schemas.d.ts +12 -0
- package/private/data_types.d.ts +437 -0
- package/private/decorator_schemas.d.ts +34 -0
- package/private/decorator_utils.d.ts +305 -0
- package/private/decorators.d.ts +209 -0
- package/private/helpers.d.ts +34 -0
- package/private/joi/bigint.d.ts +85 -0
- package/private/joi/index.d.ts +65 -0
- package/private/lucene_to_lucid_translator.d.ts +201 -0
- package/private/mixin.d.ts +563 -0
- package/private/schema_types.d.ts +157 -0
- package/private/type_guards.d.ts +42 -0
- package/private/types.d.ts +102 -0
- package/private/utils.d.ts +10 -0
- package/types.cjs +2 -0
- package/types.cjs.map +1 -0
- package/types.d.ts +28 -0
- package/types.mjs +2 -0
- package/types.mjs.map +1 -0
- package/utils/casters.d.ts +1 -0
- package/utils/consumers.d.ts +1 -0
- package/utils/preparers.d.ts +1 -0
- package/utils.cjs +50 -0
- package/utils.cjs.map +1 -0
- package/utils.d.ts +20 -0
- package/utils.mjs +51 -0
- package/utils.mjs.map +1 -0
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
import type { BaseModel } from '@adonisjs/lucid/orm';
|
|
2
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
3
|
+
import type { ApplicationService } from '@adonisjs/core/types';
|
|
4
|
+
import type { AnySchema, ObjectSchema } from 'joi';
|
|
5
|
+
import type { EventMap, Key, Listener } from '@nhtio/tiny-typed-emitter';
|
|
6
|
+
import type { NormalizeConstructor } from '@adonisjs/core/types/helpers';
|
|
7
|
+
import type { DatabaseQueryBuilderContract } from '@adonisjs/lucid/types/querybuilder';
|
|
8
|
+
import type { LucidModel } from '@adonisjs/lucid/types/model';
|
|
9
|
+
import type { ExternalDocumentationObject, PromiseAble, ResourcefulColumnDefinition, ResourcefulComputedAccessorDefinition, ResourcefulDataType, ResourcefulGeneralAccessControlFilter, ResourcefulModelOpenApiSchema, ResourcefulPropertySchema, ResourcefulRelationshipDefinition, ResourcefulResourceAccessControlFilter, ResourcefulModelSerializableAttributes } from '@nhtio/lucid-resourceful/types';
|
|
10
|
+
/**
|
|
11
|
+
* Result object returned by the resourceful index/list operation.
|
|
12
|
+
*
|
|
13
|
+
* Contains paginated records along with metadata about the query execution
|
|
14
|
+
* and pagination state. The records are filtered according to field-level
|
|
15
|
+
* access control and only contain the requested fields.
|
|
16
|
+
*
|
|
17
|
+
* @template ReturnType - The shape of records after field selection and ACL filtering
|
|
18
|
+
*/
|
|
19
|
+
export interface ResourcefulIndexResult<ReturnType = any> {
|
|
20
|
+
/** Array of records with only the requested and accessible fields */
|
|
21
|
+
records: Array<Partial<ReturnType>>;
|
|
22
|
+
/** Total number of records matching the filter (before pagination) */
|
|
23
|
+
total: number;
|
|
24
|
+
/** The current page number (1-based) */
|
|
25
|
+
page: number;
|
|
26
|
+
/** Number of records per page */
|
|
27
|
+
perPage: number;
|
|
28
|
+
/** SQL query string used for counting total records */
|
|
29
|
+
countQuery: string;
|
|
30
|
+
/** SQL query string used for fetching the paginated records */
|
|
31
|
+
recordsQuery: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Function type for generating payload validation schemas based on request context.
|
|
35
|
+
*
|
|
36
|
+
* These functions are called during create and update operations to generate
|
|
37
|
+
* context-specific validation schemas using Joi. They provide the core model-level
|
|
38
|
+
* validation that applies regardless of request-specific hooks.
|
|
39
|
+
*
|
|
40
|
+
* @param ctx - HTTP context containing request information and authentication
|
|
41
|
+
* @param app - Application service instance for accessing app-level services
|
|
42
|
+
* @returns Promise resolving to a Joi schema for validating the request payload
|
|
43
|
+
*/
|
|
44
|
+
export interface ResourcefulPayloadValidatorGetter {
|
|
45
|
+
(ctx: HttpContext, app: ApplicationService): PromiseAble<AnySchema>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Function type for generating request-specific payload validation schemas.
|
|
49
|
+
*
|
|
50
|
+
* These functions are provided via hooks during create and update operations
|
|
51
|
+
* to add additional validation constraints beyond the base model validation.
|
|
52
|
+
* They enable request-specific validation logic based on context.
|
|
53
|
+
*
|
|
54
|
+
* @param ctx - HTTP context containing request information and authentication
|
|
55
|
+
* @param app - Application service instance for accessing app-level services
|
|
56
|
+
* @returns Promise resolving to a Joi ObjectSchema for additional validation, or null to skip
|
|
57
|
+
*/
|
|
58
|
+
export interface ResourcefulPayloadSchemaGetter {
|
|
59
|
+
(ctx: HttpContext, app: ApplicationService): PromiseAble<ObjectSchema | null>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Function type for applying query-level scoping to database queries.
|
|
63
|
+
*
|
|
64
|
+
* These callbacks are applied to queries during CRUD operations to enforce
|
|
65
|
+
* data access boundaries based on request context. They modify the query
|
|
66
|
+
* in-place by adding WHERE clauses, JOINs, or other constraints.
|
|
67
|
+
*
|
|
68
|
+
* @param ctx - HTTP context containing request information and authentication
|
|
69
|
+
* @param app - Application service instance for accessing app-level services
|
|
70
|
+
* @param query - Database query builder to modify with scoping constraints
|
|
71
|
+
* @returns Promise that resolves when the query has been modified
|
|
72
|
+
*/
|
|
73
|
+
export interface ResourcefulQueryScopeCallback {
|
|
74
|
+
(ctx: HttpContext, app: ApplicationService, query: DatabaseQueryBuilderContract): PromiseAble<void>;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Array of query scope callback functions.
|
|
78
|
+
*
|
|
79
|
+
* Used for operations that need to apply query-level scoping (index, read, delete).
|
|
80
|
+
* Callbacks are applied in sequence to build up the complete set of access constraints.
|
|
81
|
+
*/
|
|
82
|
+
export type ResourcefulScopeHooks = ResourcefulQueryScopeCallback[];
|
|
83
|
+
/**
|
|
84
|
+
* Array of payload validation schema getter functions.
|
|
85
|
+
*
|
|
86
|
+
* Used for operations that accept request payloads (create, update).
|
|
87
|
+
* Each function can return additional validation constraints based on request context.
|
|
88
|
+
*/
|
|
89
|
+
export type ResourcefulValidationHooks = ResourcefulPayloadSchemaGetter[];
|
|
90
|
+
/**
|
|
91
|
+
* Combined hook interface providing both query scoping and payload validation.
|
|
92
|
+
*
|
|
93
|
+
* Used by the update operation which needs both query scoping (to find the record)
|
|
94
|
+
* and payload validation (to validate the update data).
|
|
95
|
+
*/
|
|
96
|
+
export interface ResourcefulHooks {
|
|
97
|
+
/** Query scope callbacks for constraining database queries */
|
|
98
|
+
queryScopeCallbacks: ResourcefulScopeHooks;
|
|
99
|
+
/** Payload validation schema getters for additional validation */
|
|
100
|
+
payloadValidationSchemas: ResourcefulValidationHooks;
|
|
101
|
+
}
|
|
102
|
+
export type ResourcefulMixinEventMap<Model extends LucidModel = LucidModel, ModelInstance = InstanceType<Model>> = EventMap<{
|
|
103
|
+
'acl:error': [unknown, HttpContext, ApplicationService, ModelInstance | undefined];
|
|
104
|
+
'validation:scope:error': [unknown, HttpContext, ApplicationService, string, ResourcefulDataType];
|
|
105
|
+
}>;
|
|
106
|
+
/**
|
|
107
|
+
* Enhanced Lucid model interface providing resourceful CRUD functionality.
|
|
108
|
+
*
|
|
109
|
+
* This interface extends the base LucidModel with metadata-driven CRUD operations,
|
|
110
|
+
* field-level access control, query scoping, OpenAPI schema generation, and event
|
|
111
|
+
* handling capabilities. Models implementing this interface gain static methods
|
|
112
|
+
* for handling HTTP requests with built-in validation, pagination, filtering,
|
|
113
|
+
* and comprehensive security features.
|
|
114
|
+
*
|
|
115
|
+
* The interface defines both metadata storage properties and operational methods
|
|
116
|
+
* that work together to provide a complete resourceful API layer on top of
|
|
117
|
+
* AdonisJS Lucid ORM models.
|
|
118
|
+
*
|
|
119
|
+
* @extends LucidModel
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```typescript
|
|
123
|
+
* import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'
|
|
124
|
+
* import { withResourceful, resourceful } from 'lucid-resourceful'
|
|
125
|
+
*
|
|
126
|
+
* class User extends withResourceful()(BaseModel) implements ResourcefulModel {
|
|
127
|
+
* @column({ isPrimary: true })
|
|
128
|
+
* @resourceful({ type: 'number', nullable: false })
|
|
129
|
+
* public id: number
|
|
130
|
+
*
|
|
131
|
+
* @column()
|
|
132
|
+
* @resourceful({ type: 'string', nullable: false })
|
|
133
|
+
* public name: string
|
|
134
|
+
*
|
|
135
|
+
* // Automatically gains all ResourcefulModel methods
|
|
136
|
+
* // User.$onResourcefulIndex, User.$onResourcefulRead, etc.
|
|
137
|
+
* }
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
export interface ResourcefulModel extends LucidModel {
|
|
141
|
+
/** The display name for this model used in API documentation and error messages */
|
|
142
|
+
$resourcefulName: string;
|
|
143
|
+
/** Optional description for OpenAPI schema documentation */
|
|
144
|
+
$resourcefulMetaDescription?: string;
|
|
145
|
+
/** Optional external documentation reference for OpenAPI schema */
|
|
146
|
+
$resourcefulMetaExternalDocs?: ExternalDocumentationObject;
|
|
147
|
+
/** Optional example value for OpenAPI schema documentation */
|
|
148
|
+
$resourcefulMetaExample?: string;
|
|
149
|
+
/** Map of column property names to their resourceful metadata definitions */
|
|
150
|
+
$resourcefulColumns: Map<string, ResourcefulColumnDefinition<ResourcefulPropertySchema>>;
|
|
151
|
+
/** Map of relationship property names to their resourceful metadata definitions */
|
|
152
|
+
$resourcefulRelationships: Map<string, ResourcefulRelationshipDefinition>;
|
|
153
|
+
/** Map of computed accessor property names to their resourceful metadata definitions */
|
|
154
|
+
$resourcefulComputedAccessors: Map<string, ResourcefulComputedAccessorDefinition<ResourcefulPropertySchema>>;
|
|
155
|
+
/** Access control filter functions organized by CRUD operation type */
|
|
156
|
+
$resourcefulAccessControlFilters: ResourcefulMixinOptions['accessControlFilters'];
|
|
157
|
+
/** Error handling strategy when ACL evaluation fails */
|
|
158
|
+
$resourcefulOnACLError: ResourcefulMixinOptions['onACLError'];
|
|
159
|
+
/** Query scope callback functions for constraining database queries */
|
|
160
|
+
$resourcefulQueryScopeCallbacks: ResourcefulMixinOptions['queryScopeCallbacks'];
|
|
161
|
+
/** Payload validation schema builder functions for create and update operations */
|
|
162
|
+
$resourcefulPayloadValidationSchemaBuilders: ResourcefulMixinOptions['payloadValidationSchemaBuilders'];
|
|
163
|
+
/**
|
|
164
|
+
* Registers an event listener for resourceful model events.
|
|
165
|
+
*
|
|
166
|
+
* This method provides a way to listen for events emitted during resourceful
|
|
167
|
+
* operations such as ACL errors or validation scope errors. Useful for logging,
|
|
168
|
+
* monitoring, or implementing custom error handling logic.
|
|
169
|
+
*
|
|
170
|
+
* @template K - The event key type from the resourceful event map
|
|
171
|
+
* @param event - The name of the event to listen for ('acl:error', 'validation:scope:error')
|
|
172
|
+
* @param listener - Function to call when the event is emitted
|
|
173
|
+
* @returns The model class for method chaining
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```typescript
|
|
177
|
+
* User.$onResourcefulEvent('acl:error', (error, ctx, app, instance) => {
|
|
178
|
+
* console.error('ACL error occurred:', error);
|
|
179
|
+
* });
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
$onResourcefulEvent<K>(event: Key<K, ResourcefulMixinEventMap<ResourcefulModel>>, listener: Listener<K, ResourcefulMixinEventMap<ResourcefulModel>>): this;
|
|
183
|
+
/**
|
|
184
|
+
* Registers a one-time event listener for resourceful model events.
|
|
185
|
+
*
|
|
186
|
+
* Similar to $onResourcefulEvent but the listener is automatically removed
|
|
187
|
+
* after being called once. Useful for handling events that should only
|
|
188
|
+
* trigger a single response.
|
|
189
|
+
*
|
|
190
|
+
* @template K - The event key type from the resourceful event map
|
|
191
|
+
* @param event - The name of the event to listen for
|
|
192
|
+
* @param listener - Function to call when the event is emitted
|
|
193
|
+
* @returns The model class for method chaining
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```typescript
|
|
197
|
+
* User.$onceResourcefulEvent('validation:scope:error', (error, ctx, app, key, datatype) => {
|
|
198
|
+
* console.warn('Validation scope error (one-time):', error);
|
|
199
|
+
* });
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
$onceResourcefulEvent<K>(event: Key<K, ResourcefulMixinEventMap<ResourcefulModel>>, listener: Listener<K, ResourcefulMixinEventMap<ResourcefulModel>>): this;
|
|
203
|
+
/**
|
|
204
|
+
* Removes an event listener for resourceful model events.
|
|
205
|
+
*
|
|
206
|
+
* Unregisters a previously registered event listener. If no listener is
|
|
207
|
+
* provided, all listeners for the specified event are removed.
|
|
208
|
+
*
|
|
209
|
+
* @template K - The event key type from the resourceful event map
|
|
210
|
+
* @param event - The name of the event to stop listening for
|
|
211
|
+
* @param listener - Optional specific listener function to remove
|
|
212
|
+
* @returns The model class for method chaining
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```typescript
|
|
216
|
+
* // Remove specific listener
|
|
217
|
+
* User.$offResourcefulEvent('acl:error', myErrorHandler);
|
|
218
|
+
*
|
|
219
|
+
* // Remove all listeners for an event
|
|
220
|
+
* User.$offResourcefulEvent('acl:error');
|
|
221
|
+
* ```
|
|
222
|
+
*/
|
|
223
|
+
$offResourcefulEvent<K>(event: Key<K, ResourcefulMixinEventMap<ResourcefulModel>>, listener?: Listener<K, ResourcefulMixinEventMap<ResourcefulModel>>): this;
|
|
224
|
+
/**
|
|
225
|
+
* Performs paginated listing and searching of model records with comprehensive filtering.
|
|
226
|
+
*
|
|
227
|
+
* This method provides the core implementation for resourceful index/list operations,
|
|
228
|
+
* supporting pagination, field selection, access control, and Lucene query syntax for
|
|
229
|
+
* filtering. It validates all inputs, applies ACL filters, executes the query with
|
|
230
|
+
* proper field mapping between serialized names and database columns, and returns
|
|
231
|
+
* structured results with query metadata.
|
|
232
|
+
*
|
|
233
|
+
* @template SelectedFields - Array of field names that can be selected from the model's serializable attributes
|
|
234
|
+
* @template ReturnType - The resulting type after picking the selected fields from the model's serializable attributes
|
|
235
|
+
*
|
|
236
|
+
* @param filter - Lucene-style query string for filtering records (e.g., "name:john AND email:*.com").
|
|
237
|
+
* If null or undefined, defaults to empty string (no filtering).
|
|
238
|
+
* @param page - The page number for pagination (must be ≥ 1). Used with perPage to calculate offset.
|
|
239
|
+
* @param perPage - Number of records per page (must be ≥ 1 and ≤ 100 by default).
|
|
240
|
+
* @param fields - Array of field names to include in the response. If null, undefined, or empty,
|
|
241
|
+
* defaults to just the primary key field. Field names should use serialized names
|
|
242
|
+
* (as they appear in API responses), not database column names.
|
|
243
|
+
* @param ctx - HTTP context containing request information, authentication, and other request-scoped data.
|
|
244
|
+
* @param app - Application service instance providing access to application-level services and configuration.
|
|
245
|
+
* @param hooks - Optional array of query scope callbacks to apply additional filtering constraints.
|
|
246
|
+
*
|
|
247
|
+
* @returns Promise resolving to ResourcefulIndexResult containing:
|
|
248
|
+
* - `records`: Array of partial record objects with only the requested fields
|
|
249
|
+
* - `total`: Total number of records matching the filter (before pagination)
|
|
250
|
+
* - `page`: The requested page number (echoed back)
|
|
251
|
+
* - `perPage`: The requested per-page limit (echoed back)
|
|
252
|
+
* - `countQuery`: SQL query string used for counting total records
|
|
253
|
+
* - `recordsQuery`: SQL query string used for fetching the actual records
|
|
254
|
+
*
|
|
255
|
+
* @throws {E_MISSING_PRIMARY_KEY_EXCEPTION} When the model has no identifiable primary key
|
|
256
|
+
* @throws {E_FORBIDDEN} When access is denied by model-level or field-level ACL filters
|
|
257
|
+
* @throws {E_INVALID_COLUMN_ACCESS} When no fields are available for access after ACL filtering
|
|
258
|
+
* @throws {E_INVALID_RESOUREFUL_INDEX_REQUEST_EXCEPTION} When input validation fails
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* ```typescript
|
|
262
|
+
* // Basic usage with pagination
|
|
263
|
+
* const result = await User.$onResourcefulIndex(
|
|
264
|
+
* 'name:john',
|
|
265
|
+
* 1,
|
|
266
|
+
* 10,
|
|
267
|
+
* ['id', 'name', 'email'],
|
|
268
|
+
* ctx,
|
|
269
|
+
* app
|
|
270
|
+
* );
|
|
271
|
+
*
|
|
272
|
+
* // Complex filtering with date ranges
|
|
273
|
+
* const result = await User.$onResourcefulIndex(
|
|
274
|
+
* 'name:john AND createdAt:[2021-01-01T00:00:00Z TO 2021-12-31T23:59:59Z]',
|
|
275
|
+
* 2,
|
|
276
|
+
* 25,
|
|
277
|
+
* ['id', 'name', 'createdAt'],
|
|
278
|
+
* ctx,
|
|
279
|
+
* app
|
|
280
|
+
* );
|
|
281
|
+
* ```
|
|
282
|
+
*/
|
|
283
|
+
$onResourcefulIndex<SelectedFields extends Array<keyof ResourcefulModelSerializableAttributes<InstanceType<LucidModel>>>, ReturnType = Pick<ResourcefulModelSerializableAttributes<InstanceType<LucidModel>>, SelectedFields[number]>>(filter: string | null | undefined, page: number, perPage: number, fields: SelectedFields | null | undefined, ctx: HttpContext, app: ApplicationService, hooks?: ResourcefulScopeHooks): Promise<ResourcefulIndexResult<ReturnType>>;
|
|
284
|
+
/**
|
|
285
|
+
* Retrieves a single model record by its unique identifier with access control.
|
|
286
|
+
*
|
|
287
|
+
* This method implements secure record retrieval by first applying query scope callbacks
|
|
288
|
+
* to verify the record exists within the user's access scope, then fetching the full
|
|
289
|
+
* model instance for field-level ACL evaluation. Only fields that pass ACL checks
|
|
290
|
+
* are included in the response.
|
|
291
|
+
*
|
|
292
|
+
* @param uid - The unique identifier of the record to retrieve
|
|
293
|
+
* @param ctx - HTTP context containing request information and authentication
|
|
294
|
+
* @param app - Application service instance for accessing app-level services
|
|
295
|
+
* @param hooks - Optional array of query scope callbacks to apply additional filtering constraints
|
|
296
|
+
*
|
|
297
|
+
* @returns Promise resolving to the record object with only accessible fields
|
|
298
|
+
*
|
|
299
|
+
* @throws {E_MISSING_PRIMARY_KEY_EXCEPTION} When the model has no identifiable primary key
|
|
300
|
+
* @throws {E_RECORD_NOT_FOUND_EXCEPTION} When no record exists with the given ID or user lacks access
|
|
301
|
+
* @throws {E_FORBIDDEN} When access is denied by model-level or field-level ACL filters
|
|
302
|
+
*
|
|
303
|
+
* @example
|
|
304
|
+
* ```typescript
|
|
305
|
+
* // Retrieve a user by ID
|
|
306
|
+
* const user = await User.$onResourcefulRead(123, ctx, app);
|
|
307
|
+
*
|
|
308
|
+
* // With additional query scoping
|
|
309
|
+
* const user = await User.$onResourcefulRead(123, ctx, app, [
|
|
310
|
+
* (ctx, app, query) => query.where('tenant_id', ctx.auth.user.tenantId)
|
|
311
|
+
* ]);
|
|
312
|
+
* ```
|
|
313
|
+
*/
|
|
314
|
+
$onResourcefulRead(uid: number, ctx: HttpContext, app: ApplicationService, hooks?: ResourcefulScopeHooks): Promise<any>;
|
|
315
|
+
/**
|
|
316
|
+
* Creates a new model record with payload validation and access control.
|
|
317
|
+
*
|
|
318
|
+
* This method handles secure record creation by validating the request payload
|
|
319
|
+
* against both model-level and request-specific validation schemas, checking
|
|
320
|
+
* field-level write permissions, and returning the created record with appropriate
|
|
321
|
+
* field filtering applied.
|
|
322
|
+
*
|
|
323
|
+
* @param payload - The data object containing field values for the new record
|
|
324
|
+
* @param ctx - HTTP context containing request information and authentication
|
|
325
|
+
* @param app - Application service instance for accessing app-level services
|
|
326
|
+
* @param hooks - Optional array of validation schema getters for additional payload validation
|
|
327
|
+
*
|
|
328
|
+
* @returns Promise resolving to the created record with only accessible fields
|
|
329
|
+
*
|
|
330
|
+
* @throws {E_MISSING_PRIMARY_KEY_EXCEPTION} When the model has no identifiable primary key
|
|
331
|
+
* @throws {E_INVALID_PAYLOAD_EXCEPTION} When core model validation fails
|
|
332
|
+
* @throws {E_FORBIDDEN_PAYLOAD_EXCEPTION} When request-specific validation fails
|
|
333
|
+
* @throws {E_FORBIDDEN} When access is denied by model-level or field-level ACL filters
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* ```typescript
|
|
337
|
+
* // Create a new user
|
|
338
|
+
* const user = await User.$onResourcefulCreate({
|
|
339
|
+
* name: 'John Doe',
|
|
340
|
+
* email: 'john@example.com'
|
|
341
|
+
* }, ctx, app);
|
|
342
|
+
*
|
|
343
|
+
* // With additional validation
|
|
344
|
+
* const user = await User.$onResourcefulCreate(payload, ctx, app, [
|
|
345
|
+
* (ctx, app) => joi.object({ email: joi.string().domain('company.com') })
|
|
346
|
+
* ]);
|
|
347
|
+
* ```
|
|
348
|
+
*/
|
|
349
|
+
$onResourcefulCreate(payload: any, ctx: HttpContext, app: ApplicationService, hooks?: ResourcefulValidationHooks): Promise<any>;
|
|
350
|
+
/**
|
|
351
|
+
* Updates an existing model record with payload validation and access control.
|
|
352
|
+
*
|
|
353
|
+
* This method implements secure record updates by first verifying the record exists
|
|
354
|
+
* and is accessible via the read operation, validating the update payload, checking
|
|
355
|
+
* field-level write permissions, and returning the updated record with appropriate
|
|
356
|
+
* field filtering applied.
|
|
357
|
+
*
|
|
358
|
+
* @param uid - The unique identifier of the record to update
|
|
359
|
+
* @param payload - The data object containing field values to update
|
|
360
|
+
* @param ctx - HTTP context containing request information and authentication
|
|
361
|
+
* @param app - Application service instance for accessing app-level services
|
|
362
|
+
* @param hooks - Optional object containing query scope callbacks and validation schema getters
|
|
363
|
+
*
|
|
364
|
+
* @returns Promise resolving to the updated record with only accessible fields
|
|
365
|
+
*
|
|
366
|
+
* @throws {E_MISSING_PRIMARY_KEY_EXCEPTION} When the model has no identifiable primary key
|
|
367
|
+
* @throws {E_RECORD_NOT_FOUND_EXCEPTION} When no record exists with the given ID or user lacks access
|
|
368
|
+
* @throws {E_INVALID_PAYLOAD_EXCEPTION} When core model validation fails
|
|
369
|
+
* @throws {E_FORBIDDEN_PAYLOAD_EXCEPTION} When request-specific validation fails
|
|
370
|
+
* @throws {E_FORBIDDEN} When access is denied by model-level or field-level ACL filters
|
|
371
|
+
*
|
|
372
|
+
* @example
|
|
373
|
+
* ```typescript
|
|
374
|
+
* // Update a user
|
|
375
|
+
* const user = await User.$onResourcefulUpdate(123, {
|
|
376
|
+
* name: 'Jane Doe'
|
|
377
|
+
* }, ctx, app);
|
|
378
|
+
*
|
|
379
|
+
* // With additional scoping and validation
|
|
380
|
+
* const user = await User.$onResourcefulUpdate(123, payload, ctx, app, {
|
|
381
|
+
* queryScopeCallbacks: [(ctx, app, query) => query.where('active', true)],
|
|
382
|
+
* payloadValidationSchemas: [(ctx, app) => customValidationSchema]
|
|
383
|
+
* });
|
|
384
|
+
* ```
|
|
385
|
+
*/
|
|
386
|
+
$onResourcefulUpdate(uid: number, payload: any, ctx: HttpContext, app: ApplicationService, hooks?: Partial<ResourcefulHooks>): Promise<any>;
|
|
387
|
+
/**
|
|
388
|
+
* Deletes an existing model record with access control.
|
|
389
|
+
*
|
|
390
|
+
* This method implements secure record deletion by applying query scope callbacks
|
|
391
|
+
* to verify the record exists within the user's access scope, checking delete
|
|
392
|
+
* permissions via ACL filters, and then removing the record from the database.
|
|
393
|
+
*
|
|
394
|
+
* @param uid - The unique identifier of the record to delete
|
|
395
|
+
* @param ctx - HTTP context containing request information and authentication
|
|
396
|
+
* @param app - Application service instance for accessing app-level services
|
|
397
|
+
* @param hooks - Optional array of query scope callbacks to apply additional filtering constraints
|
|
398
|
+
*
|
|
399
|
+
* @returns Promise that resolves when the record has been successfully deleted
|
|
400
|
+
*
|
|
401
|
+
* @throws {E_MISSING_PRIMARY_KEY_EXCEPTION} When the model has no identifiable primary key
|
|
402
|
+
* @throws {E_RECORD_NOT_FOUND_EXCEPTION} When no record exists with the given ID or user lacks access
|
|
403
|
+
* @throws {E_FORBIDDEN} When access is denied by model-level ACL filters
|
|
404
|
+
*
|
|
405
|
+
* @example
|
|
406
|
+
* ```typescript
|
|
407
|
+
* // Delete a user
|
|
408
|
+
* await User.$onResourcefulDelete(123, ctx, app);
|
|
409
|
+
*
|
|
410
|
+
* // With additional query scoping
|
|
411
|
+
* await User.$onResourcefulDelete(123, ctx, app, [
|
|
412
|
+
* (ctx, app, query) => query.where('tenant_id', ctx.auth.user.tenantId)
|
|
413
|
+
* ]);
|
|
414
|
+
* ```
|
|
415
|
+
*/
|
|
416
|
+
$onResourcefulDelete(uid: number, ctx: HttpContext, app: ApplicationService, hooks?: ResourcefulScopeHooks): Promise<void>;
|
|
417
|
+
/**
|
|
418
|
+
* Generates an OpenAPI schema object for this model with context-aware field filtering.
|
|
419
|
+
*
|
|
420
|
+
* This method creates a complete OpenAPI v3 schema representation of the model
|
|
421
|
+
* by evaluating field-level access control permissions for the given request context.
|
|
422
|
+
* Only fields that pass ACL checks are included in the generated schema, ensuring
|
|
423
|
+
* that API documentation accurately reflects what data is accessible to the current user.
|
|
424
|
+
*
|
|
425
|
+
* The method processes all resourceful properties (columns, computed accessors, and
|
|
426
|
+
* relationships) and converts them to their OpenAPI schema equivalents while respecting
|
|
427
|
+
* access control constraints and applying proper type mappings.
|
|
428
|
+
*
|
|
429
|
+
* @param ctx - HTTP context containing request information and authentication
|
|
430
|
+
* @param app - Application service instance for accessing app-level services
|
|
431
|
+
*
|
|
432
|
+
* @returns Promise resolving to a complete OpenAPI schema object with:
|
|
433
|
+
* - `type`: Always 'object' for model schemas
|
|
434
|
+
* - `title`: The model's resourceful name
|
|
435
|
+
* - `description`: Optional model description from metadata
|
|
436
|
+
* - `properties`: Object containing schema definitions for accessible fields
|
|
437
|
+
* - `required`: Array of required field names (non-nullable fields)
|
|
438
|
+
* - `externalDocs`: Optional external documentation reference
|
|
439
|
+
* - `example`: Optional example value for the schema
|
|
440
|
+
*
|
|
441
|
+
* @example
|
|
442
|
+
* ```typescript
|
|
443
|
+
* // Generate OpenAPI schema for current user context
|
|
444
|
+
* const schema = await User.$asOpenApiSchemaObject(ctx, app);
|
|
445
|
+
*
|
|
446
|
+
* // Result structure:
|
|
447
|
+
* {
|
|
448
|
+
* type: 'object',
|
|
449
|
+
* title: 'User',
|
|
450
|
+
* properties: {
|
|
451
|
+
* id: { type: 'number', readOnly: true },
|
|
452
|
+
* name: { type: 'string' },
|
|
453
|
+
* email: { type: 'string', format: 'email' }
|
|
454
|
+
* },
|
|
455
|
+
* required: ['id', 'name', 'email']
|
|
456
|
+
* }
|
|
457
|
+
* ```
|
|
458
|
+
*
|
|
459
|
+
* @see {@link ResourcefulModelOpenApiSchema} for the complete schema structure
|
|
460
|
+
*/
|
|
461
|
+
$asOpenApiSchemaObject(ctx: HttpContext, app: ApplicationService): Promise<ResourcefulModelOpenApiSchema>;
|
|
462
|
+
}
|
|
463
|
+
export declare const ResourcefulErrorHandlerMethod: readonly ["bubble", "pass", "fail"];
|
|
464
|
+
export type ResourcefulErrorHandlerMethod = (typeof ResourcefulErrorHandlerMethod)[number];
|
|
465
|
+
export declare const ResourcefulACLOperationType: readonly ["read", "write"];
|
|
466
|
+
export type ResourcefulACLOperationType = (typeof ResourcefulACLOperationType)[number];
|
|
467
|
+
export interface AdvancedResourcefulMixinOptions {
|
|
468
|
+
propertyEvaluationConcurrency: number;
|
|
469
|
+
aclEvaluationConcurrency: number;
|
|
470
|
+
}
|
|
471
|
+
export interface ResourcefulMixinOptions<Model extends LucidModel = LucidModel, ModelInstance = InstanceType<Model>> {
|
|
472
|
+
name: string;
|
|
473
|
+
readRequiredForWrite: boolean;
|
|
474
|
+
accessControlFilters: {
|
|
475
|
+
list: ResourcefulGeneralAccessControlFilter[];
|
|
476
|
+
create: ResourcefulGeneralAccessControlFilter[];
|
|
477
|
+
read: ResourcefulResourceAccessControlFilter<Model, ModelInstance>[];
|
|
478
|
+
update: ResourcefulResourceAccessControlFilter<Model, ModelInstance>[];
|
|
479
|
+
delete: ResourcefulResourceAccessControlFilter<Model, ModelInstance>[];
|
|
480
|
+
};
|
|
481
|
+
payloadValidationSchemaBuilders: {
|
|
482
|
+
create: ResourcefulPayloadValidatorGetter[];
|
|
483
|
+
update: ResourcefulPayloadValidatorGetter[];
|
|
484
|
+
};
|
|
485
|
+
onACLError: ResourcefulErrorHandlerMethod;
|
|
486
|
+
onValidationScopeError: ResourcefulErrorHandlerMethod;
|
|
487
|
+
queryScopeCallbacks: {
|
|
488
|
+
list: ResourcefulQueryScopeCallback[];
|
|
489
|
+
access: ResourcefulQueryScopeCallback[];
|
|
490
|
+
};
|
|
491
|
+
description?: string;
|
|
492
|
+
externalDocs?: ExternalDocumentationObject;
|
|
493
|
+
example?: string;
|
|
494
|
+
advanced: AdvancedResourcefulMixinOptions;
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Creates a mixin that adds resourceful CRUD functionality to Lucid models.
|
|
498
|
+
*
|
|
499
|
+
* This function implements the mixin pattern to enhance AdonisJS Lucid models with
|
|
500
|
+
* metadata-driven CRUD operations, field-level access control, query scoping,
|
|
501
|
+
* and OpenAPI schema generation capabilities. The resulting model gains static
|
|
502
|
+
* methods for handling HTTP requests with built-in validation, pagination,
|
|
503
|
+
* filtering, and security features.
|
|
504
|
+
*
|
|
505
|
+
* The mixin validates and normalizes configuration options, creates isolated
|
|
506
|
+
* metadata maps for each model class to prevent inheritance conflicts, and
|
|
507
|
+
* establishes an event emitter for monitoring ACL and validation operations.
|
|
508
|
+
*
|
|
509
|
+
* @param options - Configuration object for customizing mixin behavior
|
|
510
|
+
* @param options.name - Display name for the model (defaults to class name)
|
|
511
|
+
* @param options.readRequiredForWrite - Whether read access is required before write operations
|
|
512
|
+
* @param options.accessControlFilters - Object containing arrays of ACL filter functions for different operations
|
|
513
|
+
* @param options.accessControlFilters.list - Filters applied during listing/search operations
|
|
514
|
+
* @param options.accessControlFilters.create - Filters applied during record creation
|
|
515
|
+
* @param options.accessControlFilters.read - Filters applied during record reading
|
|
516
|
+
* @param options.accessControlFilters.update - Filters applied during record updates
|
|
517
|
+
* @param options.accessControlFilters.delete - Filters applied during record deletion
|
|
518
|
+
* @param options.onACLError - Error handling strategy when ACL evaluation fails ('throw', 'pass', 'fail')
|
|
519
|
+
* @param options.onValidationScopeError - Error handling strategy when validation scope functions fail
|
|
520
|
+
* @param options.queryScopeCallbacks - Object containing query scope callback functions
|
|
521
|
+
* @param options.queryScopeCallbacks.list - Callbacks applied to listing/search queries
|
|
522
|
+
* @param options.queryScopeCallbacks.access - Callbacks applied to individual record access queries
|
|
523
|
+
* @param options.description - Optional description for OpenAPI documentation
|
|
524
|
+
* @param options.externalDocs - Optional external documentation reference for OpenAPI
|
|
525
|
+
* @param options.example - Optional example value for OpenAPI documentation
|
|
526
|
+
* @param options.advanced - Advanced configuration options for performance tuning
|
|
527
|
+
* @param options.advanced.propertyEvaluationConcurrency - Concurrency limit for property ACL evaluation
|
|
528
|
+
* @param options.advanced.aclEvaluationConcurrency - Concurrency limit for ACL filter evaluation
|
|
529
|
+
*
|
|
530
|
+
* @returns A mixin function that accepts a Lucid model class and returns the enhanced model
|
|
531
|
+
*
|
|
532
|
+
* @throws {E_INVALID_RESOURCEFUL_MIXIN_OPTIONS} When the provided options fail validation
|
|
533
|
+
*
|
|
534
|
+
* @example
|
|
535
|
+
* ```typescript
|
|
536
|
+
* import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'
|
|
537
|
+
* import { withResourceful, resourceful } from 'lucid-resourceful'
|
|
538
|
+
*
|
|
539
|
+
* class User extends withResourceful({
|
|
540
|
+
* name: 'User',
|
|
541
|
+
* readRequiredForWrite: true,
|
|
542
|
+
* accessControlFilters: {
|
|
543
|
+
* read: [(ctx) => ctx.auth.user?.id === ctx.params.id],
|
|
544
|
+
* update: [(ctx) => ctx.auth.user?.isAdmin]
|
|
545
|
+
* }
|
|
546
|
+
* })(BaseModel) {
|
|
547
|
+
* @column({ isPrimary: true })
|
|
548
|
+
* @resourceful({ type: 'number', nullable: false })
|
|
549
|
+
* public id: number
|
|
550
|
+
*
|
|
551
|
+
* @column()
|
|
552
|
+
* @resourceful({ type: 'string', nullable: false })
|
|
553
|
+
* public name: string
|
|
554
|
+
* }
|
|
555
|
+
*
|
|
556
|
+
* // Generated controller with CRUD operations
|
|
557
|
+
* const UserController = User.generateController()
|
|
558
|
+
* ```
|
|
559
|
+
*
|
|
560
|
+
* @see {@link ResourcefulMixinOptions} for detailed configuration options
|
|
561
|
+
* @see {@link ResourcefulModel} for the enhanced model interface
|
|
562
|
+
*/
|
|
563
|
+
export declare function withResourceful(options?: Partial<ResourcefulMixinOptions>): <Model extends NormalizeConstructor<typeof BaseModel>>(superclass: Model) => Model & ResourcefulModel;
|