orange-dragonfly-model 0.12.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -0
- package/dist/index.cjs +56 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +62 -8
- package/dist/index.d.ts +62 -8
- package/dist/index.js +57 -25
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@ An active-record model class for the Orange Dragonfly framework. Extends [`orang
|
|
|
7
7
|
- [Installation](#installation)
|
|
8
8
|
- [Quick start](#quick-start)
|
|
9
9
|
- [Defining a model](#defining-a-model)
|
|
10
|
+
- [Typed data access](#typed-data-access)
|
|
10
11
|
- [Field restrictions](#field-restrictions)
|
|
11
12
|
- [CRUD operations](#crud-operations)
|
|
12
13
|
- [Querying](#querying)
|
|
@@ -171,6 +172,65 @@ Default: `false` — unknown fields throw `OrangeDatabaseInputValidationError`.
|
|
|
171
172
|
|
|
172
173
|
---
|
|
173
174
|
|
|
175
|
+
## Typed data access
|
|
176
|
+
|
|
177
|
+
By default `this.data` is typed as `Record<string, unknown>`, so accessing individual fields gives `unknown`. The exported `ModelData<S>` utility type derives a precise TypeScript type directly from a `validation_rules` schema, making field access fully type-safe.
|
|
178
|
+
|
|
179
|
+
**Three steps to opt in:**
|
|
180
|
+
|
|
181
|
+
1. Declare the schema as a module-level `const` using `as const satisfies ODValidatorRulesSchema` to preserve literal type information.
|
|
182
|
+
2. Return that const from `validation_rules` (drop the explicit `: ODValidatorRulesSchema` return-type annotation so TypeScript keeps the narrow type).
|
|
183
|
+
3. Add `declare data: ModelData<typeof schema>` inside the subclass.
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
import Model, { type ModelData } from 'orange-dragonfly-model'
|
|
187
|
+
import type { ODValidatorRulesSchema } from 'orange-dragonfly-validator'
|
|
188
|
+
|
|
189
|
+
const articleSchema = {
|
|
190
|
+
id: { required: false, type: 'integer', min: 1 },
|
|
191
|
+
title: { required: true, type: 'string', min: 1, max: 255 },
|
|
192
|
+
body: { required: true, type: 'string', min: 1 },
|
|
193
|
+
author_id: { required: true, type: 'integer', min: 1 },
|
|
194
|
+
published: { required: false, type: 'boolean' },
|
|
195
|
+
created_at: { required: false, type: 'integer' },
|
|
196
|
+
updated_at: { required: false, type: 'integer' },
|
|
197
|
+
} as const satisfies ODValidatorRulesSchema
|
|
198
|
+
|
|
199
|
+
class Article extends Model {
|
|
200
|
+
static override get validation_rules() { return articleSchema }
|
|
201
|
+
declare data: ModelData<typeof articleSchema>
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Field access is now fully typed:
|
|
205
|
+
const article = await Article.find(1)
|
|
206
|
+
article.data.title // string
|
|
207
|
+
article.data.published // boolean
|
|
208
|
+
article.data.author_id // number
|
|
209
|
+
article.data.missing // TypeScript error — field not in schema
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**Schema type → TypeScript type mapping:**
|
|
213
|
+
|
|
214
|
+
| Validator type | TypeScript type |
|
|
215
|
+
|---|---|
|
|
216
|
+
| `'string'` | `string` |
|
|
217
|
+
| `'integer'`, `'number'` | `number` |
|
|
218
|
+
| `'boolean'` | `boolean` |
|
|
219
|
+
| `'null'` | `null` |
|
|
220
|
+
| `'object'` | `Record<string, unknown>` |
|
|
221
|
+
| `'array'` | `unknown[]` |
|
|
222
|
+
| `'function'` | `(...args: unknown[]) => unknown` |
|
|
223
|
+
|
|
224
|
+
When `type` is an array (e.g. `['string', 'null']`), the result is the union of the corresponding TypeScript types (`string | null`).
|
|
225
|
+
|
|
226
|
+
**Why `as const` is required.** Without it, TypeScript widens the string literal `'integer'` to the broad type `string`, and `ModelData` cannot resolve the mapping. `satisfies ODValidatorRulesSchema` validates the object against the schema without widening the inferred type.
|
|
227
|
+
|
|
228
|
+
**Why the schema lives outside the class.** `declare data: ModelData<typeof articleSchema>` inside the class body needs to reference a name that is already in scope. A static getter cannot be referenced this way without a circular reference.
|
|
229
|
+
|
|
230
|
+
The `declare data` line has no runtime cost — it is a type-only declaration that narrows the `Record<string, unknown>` inherited from `ActiveRecord`.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
174
234
|
## Field restrictions
|
|
175
235
|
|
|
176
236
|
These getters return an array of field names that are blocked in specific operations. Attempting to pass a restricted field throws `OrangeDatabaseInputValidationError`.
|
package/dist/index.cjs
CHANGED
|
@@ -138,28 +138,39 @@ var Model = class extends import_orange_dragonfly_orm2.ActiveRecord {
|
|
|
138
138
|
* @param basicQuery Query to be used for adding conditions. By default a new SelectQuery is created.
|
|
139
139
|
*/
|
|
140
140
|
static lookupQuery(data, basicQuery = null) {
|
|
141
|
-
const
|
|
142
|
-
const
|
|
141
|
+
const cls = this;
|
|
142
|
+
const rules = cls.validation_rules;
|
|
143
|
+
const q = basicQuery ?? cls.selectQuery();
|
|
143
144
|
const filtered_rules = {};
|
|
144
145
|
for (const field of Object.keys(data)) {
|
|
145
146
|
if (!Object.hasOwn(rules, field)) {
|
|
146
|
-
if (
|
|
147
|
+
if (cls.ignore_extra_fields) {
|
|
147
148
|
continue;
|
|
148
149
|
}
|
|
149
150
|
const ex = new OrangeDatabaseInputValidationError("Parameters error");
|
|
150
|
-
ex.info[field] = `Field "${field}" is not described for model ${
|
|
151
|
+
ex.info[field] = `Field "${field}" is not described for model ${cls.name}`;
|
|
151
152
|
throw ex;
|
|
152
153
|
}
|
|
153
|
-
if (
|
|
154
|
+
if (cls.restricted_for_lookup.includes(field)) {
|
|
154
155
|
const ex = new OrangeDatabaseInputValidationError("Parameters error");
|
|
155
|
-
ex.info[field] = `Field "${field}" is restricted for searching model ${
|
|
156
|
+
ex.info[field] = `Field "${field}" is restricted for searching model ${cls.name}`;
|
|
156
157
|
throw ex;
|
|
157
158
|
}
|
|
158
159
|
q.where(field, data[field]);
|
|
159
160
|
const rule = rules[field];
|
|
160
161
|
filtered_rules[field] = Array.isArray(data[field]) ? { type: "array", children: { "*": rule } } : rule;
|
|
161
162
|
}
|
|
162
|
-
|
|
163
|
+
try {
|
|
164
|
+
(0, import_orange_dragonfly_validator.parse)(filtered_rules, data, { strictMode: false });
|
|
165
|
+
} catch (e) {
|
|
166
|
+
if (e instanceof import_orange_dragonfly_validator.ODValidatorException) {
|
|
167
|
+
const message = `Model ${cls.name} lookup rules validation failed`;
|
|
168
|
+
console.error(message, e.info);
|
|
169
|
+
throw new OrangeDatabaseInputValidationError(message);
|
|
170
|
+
} else {
|
|
171
|
+
throw e;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
163
174
|
return q;
|
|
164
175
|
}
|
|
165
176
|
/**
|
|
@@ -167,20 +178,21 @@ var Model = class extends import_orange_dragonfly_orm2.ActiveRecord {
|
|
|
167
178
|
* @param data
|
|
168
179
|
*/
|
|
169
180
|
static async create(data) {
|
|
170
|
-
const
|
|
181
|
+
const cls = this;
|
|
182
|
+
const rules = cls.validation_rules;
|
|
171
183
|
const new_data = {};
|
|
172
184
|
for (const field of Object.keys(data)) {
|
|
173
185
|
if (!Object.hasOwn(rules, field)) {
|
|
174
|
-
if (
|
|
186
|
+
if (cls.ignore_extra_fields) {
|
|
175
187
|
continue;
|
|
176
188
|
}
|
|
177
189
|
const ex = new OrangeDatabaseInputValidationError("Parameters error");
|
|
178
|
-
ex.info[field] = `Field "${field}" is not described for model ${
|
|
190
|
+
ex.info[field] = `Field "${field}" is not described for model ${cls.name}`;
|
|
179
191
|
throw ex;
|
|
180
192
|
}
|
|
181
|
-
if (
|
|
193
|
+
if (cls.restricted_for_create.includes(field)) {
|
|
182
194
|
const ex = new OrangeDatabaseInputValidationError("Parameters error");
|
|
183
|
-
ex.info[field] = `Field "${field}" is restricted for creating model ${
|
|
195
|
+
ex.info[field] = `Field "${field}" is restricted for creating model ${cls.name}`;
|
|
184
196
|
throw ex;
|
|
185
197
|
}
|
|
186
198
|
new_data[field] = data[field];
|
|
@@ -276,7 +288,21 @@ var Model = class extends import_orange_dragonfly_orm2.ActiveRecord {
|
|
|
276
288
|
}
|
|
277
289
|
}
|
|
278
290
|
}
|
|
279
|
-
|
|
291
|
+
try {
|
|
292
|
+
this.data = (0, import_orange_dragonfly_validator.parse)(rules, this.data, { strictMode: false });
|
|
293
|
+
} catch (e) {
|
|
294
|
+
if (e instanceof import_orange_dragonfly_validator.ODValidatorException) {
|
|
295
|
+
const message = `Model ${cls.name} rules validation failed`;
|
|
296
|
+
console.error(message, e.info);
|
|
297
|
+
const ex = new OrangeDatabaseInputValidationError(message);
|
|
298
|
+
for (const [key, msgs] of Object.entries(e.info)) {
|
|
299
|
+
ex.info[key] = Array.isArray(msgs) ? msgs[0] : msgs;
|
|
300
|
+
}
|
|
301
|
+
throw ex;
|
|
302
|
+
} else {
|
|
303
|
+
throw e;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
280
306
|
const custom_validation_errors = await this.custom_validation();
|
|
281
307
|
if (custom_validation_errors && Object.keys(custom_validation_errors).length) {
|
|
282
308
|
const ex = new OrangeDatabaseInputValidationError("Validation failed");
|
|
@@ -294,12 +320,18 @@ var Model = class extends import_orange_dragonfly_orm2.ActiveRecord {
|
|
|
294
320
|
}
|
|
295
321
|
}
|
|
296
322
|
}
|
|
297
|
-
if (relation_errors.length) {
|
|
298
|
-
const ex = new OrangeDatabaseInputValidationError(`Some relations of the ${cls.name} are not found`);
|
|
299
|
-
for (const param of relation_errors) ex.info[param] = "Parent object not found";
|
|
300
|
-
throw ex;
|
|
301
|
-
}
|
|
302
323
|
}
|
|
324
|
+
if (relation_errors.length) {
|
|
325
|
+
const ex = new OrangeDatabaseInputValidationError(`Some relations of the ${cls.name} are not found`);
|
|
326
|
+
for (const param of relation_errors) ex.info[param] = "Parent object not found";
|
|
327
|
+
throw ex;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
static model(class_name) {
|
|
331
|
+
return super.model(class_name);
|
|
332
|
+
}
|
|
333
|
+
static registerModel(model_class) {
|
|
334
|
+
return super.registerModel(model_class);
|
|
303
335
|
}
|
|
304
336
|
/**
|
|
305
337
|
* Returns object by ID if it exists and accessible by user
|
|
@@ -308,12 +340,13 @@ var Model = class extends import_orange_dragonfly_orm2.ActiveRecord {
|
|
|
308
340
|
* @param mode
|
|
309
341
|
*/
|
|
310
342
|
static async findAndCheckAccessOrDie(id, user, mode = null) {
|
|
311
|
-
const
|
|
343
|
+
const cls = this;
|
|
344
|
+
const obj = await cls.find(id);
|
|
312
345
|
if (!obj) {
|
|
313
|
-
throw new OrangeDatabaseModelRuntimeError(`${
|
|
346
|
+
throw new OrangeDatabaseModelRuntimeError(`${cls.name} #${id} not found`);
|
|
314
347
|
}
|
|
315
348
|
if (!await obj.accessible(user, mode)) {
|
|
316
|
-
throw new OrangeDatabaseModelAccessError(`${
|
|
349
|
+
throw new OrangeDatabaseModelAccessError(`${cls.name} #${id} is not accessible${mode ? ` for ${mode}` : ""}`);
|
|
317
350
|
}
|
|
318
351
|
return obj;
|
|
319
352
|
}
|
|
@@ -322,7 +355,7 @@ var Model = class extends import_orange_dragonfly_orm2.ActiveRecord {
|
|
|
322
355
|
* @param user
|
|
323
356
|
* @param mode
|
|
324
357
|
*/
|
|
325
|
-
async accessible(
|
|
358
|
+
async accessible(_user, mode = null) {
|
|
326
359
|
return mode === null;
|
|
327
360
|
}
|
|
328
361
|
/**
|
|
@@ -337,8 +370,7 @@ var Model = class extends import_orange_dragonfly_orm2.ActiveRecord {
|
|
|
337
370
|
* Format output
|
|
338
371
|
* @param mode
|
|
339
372
|
*/
|
|
340
|
-
|
|
341
|
-
formatOutput(mode = null) {
|
|
373
|
+
formatOutput(_mode = null) {
|
|
342
374
|
return this.output;
|
|
343
375
|
}
|
|
344
376
|
/**
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/model.ts","../src/errors.ts"],"sourcesContent":["export { default } from './model'\nexport { default as Model } from './model'\nexport {\n OrangeDatabaseInputValidationError,\n OrangeDatabaseModelAccessError,\n OrangeDatabaseModelError,\n OrangeDatabaseModelRuntimeError,\n} from './errors'\n","import { ActiveRecord, Relation, SelectQuery } from 'orange-dragonfly-orm'\nimport { parse, type ODValidatorRuleSchema, type ODValidatorRulesSchema } from 'orange-dragonfly-validator'\nimport {\n OrangeDatabaseInputValidationError,\n OrangeDatabaseModelAccessError,\n OrangeDatabaseModelError,\n OrangeDatabaseModelRuntimeError,\n} from './errors'\n\nexport default class Model extends ActiveRecord {\n private get _mCls(): typeof Model {\n return this.constructor as typeof Model\n }\n\n static get ignore_extra_fields(): boolean {\n return false\n }\n\n /**\n * Returns list of unique keys\n * @returns List of unique keys\n */\n static get unique_keys(): string[][] {\n return []\n }\n\n /**\n * Returns list of fulltext indexes\n * @returns List of fulltext indexes\n */\n static get fulltext_indexes(): string[][] {\n return []\n }\n\n /** @deprecated Use `ignore_extra_fields` instead */\n static get IGNORE_EXTRA_FIELDS(): boolean {\n return this.ignore_extra_fields\n }\n\n /** @deprecated Use `unique_keys` instead */\n static get UNIQUE_KEYS(): string[][] {\n return this.unique_keys\n }\n\n /** @deprecated Use `fulltext_indexes` instead */\n static get FULLTEXT_INDEXES(): string[][] {\n return this.fulltext_indexes\n }\n\n /**\n * Returns schema for the model (Orange Dragonfly Validator format)\n */\n static get validation_rules(): ODValidatorRulesSchema {\n return {\n id: {\n required: false,\n type: 'integer',\n min: 1,\n },\n }\n }\n\n /**\n * Overridden method of ActiveRecord. Returns special fields list based on validation rules\n */\n static override get special_fields(): string[] {\n const rules = this.validation_rules\n const fields: string[] = []\n for (const field of ['created_at', 'updated_at', 'deleted_at']) {\n if (Object.hasOwn(rules, field)) fields.push(field)\n }\n return fields\n }\n\n /**\n * Returns list of relations restricted for extended output\n */\n static get restricted_for_output(): string[] {\n return []\n }\n\n /**\n * Returns list of fields restricted for lookup\n */\n static get restricted_for_lookup(): string[] {\n return []\n }\n\n /**\n * Returns list of fields restricted for create method\n */\n static get restricted_for_create(): string[] {\n return ['id'].concat(this.special_fields)\n }\n\n /**\n * Returns list of fields restricted for update method\n */\n static get restricted_for_update(): string[] {\n return ['id'].concat(this.special_fields)\n }\n\n /**\n * Lookup method\n * @param data\n * @param basicQuery Query to be used for adding conditions. By default a new SelectQuery is created.\n */\n static lookupQuery(data: Record<string, unknown>, basicQuery: SelectQuery | null = null): SelectQuery {\n const rules = this.validation_rules\n const q = basicQuery ?? this.selectQuery()\n const filtered_rules: ODValidatorRulesSchema = {}\n for (const field of Object.keys(data)) {\n if (!Object.hasOwn(rules, field)) {\n if (this.ignore_extra_fields) {\n continue\n }\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\n ex.info[field] = `Field \"${field}\" is not described for model ${this.name}`\n throw ex\n }\n if (this.restricted_for_lookup.includes(field)) {\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\n ex.info[field] = `Field \"${field}\" is restricted for searching model ${this.name}`\n throw ex\n }\n q.where(field, data[field])\n const rule = rules[field] as ODValidatorRuleSchema\n filtered_rules[field] = Array.isArray(data[field]) ? { type: 'array', children: { '*': rule } } : rule\n }\n parse(filtered_rules, data, { strictMode: false })\n return q\n }\n\n /**\n * Creates object\n * @param data\n */\n static async create(data: Record<string, unknown>): Promise<Model> {\n const rules = this.validation_rules\n const new_data: Record<string, unknown> = {}\n for (const field of Object.keys(data)) {\n if (!Object.hasOwn(rules, field)) {\n if (this.ignore_extra_fields) {\n continue\n }\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\n ex.info[field] = `Field \"${field}\" is not described for model ${this.name}`\n throw ex\n }\n if (this.restricted_for_create.includes(field)) {\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\n ex.info[field] = `Field \"${field}\" is restricted for creating model ${this.name}`\n throw ex\n }\n new_data[field] = data[field]\n }\n const Cls = this as unknown as new (data?: Record<string, unknown>) => Model\n return (new Cls(new_data)).save()\n }\n\n /**\n * Updates object\n * @param data\n */\n async update(data: Record<string, unknown>): Promise<this> {\n if (!this.id) {\n throw new OrangeDatabaseModelRuntimeError('You can update saved object only')\n }\n const cls = this._mCls\n const rules = cls.validation_rules\n const new_data: Record<string, unknown> = {}\n for (const field of Object.keys(data)) {\n if (!Object.hasOwn(rules, field)) {\n if (cls.ignore_extra_fields) {\n continue\n }\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\n ex.info[field] = `Field \"${field}\" is not described for model ${cls.name}`\n throw ex\n }\n if (cls.restricted_for_update.includes(field)) {\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\n ex.info[field] = `Field \"${field}\" is restricted for updating model ${cls.name}`\n throw ex\n }\n new_data[field] = data[field]\n }\n return this.save(new_data)\n }\n\n /**\n * Checks uniqueness of the object based on unique_keys\n */\n async checkUniqueness(exception_mode = false, ignore_null = false): Promise<boolean> {\n const cls = this._mCls\n for (const fields of cls.unique_keys) {\n if (!await this.isUnique(fields, ignore_null)) {\n if (exception_mode) {\n const ex = new OrangeDatabaseInputValidationError('Object is not unique')\n for (const field of fields) {\n ex.info[field] = 'Part of the unique key'\n }\n throw ex\n }\n return false\n }\n }\n return true\n }\n\n protected override async _preSave(_is_new?: boolean): Promise<void> {\n const cls = this._mCls\n if (cls.ignore_extra_fields) {\n const rules = cls.validation_rules\n for (const key of Object.keys(this.data).filter(v => !Object.hasOwn(rules, v))) {\n delete this.data[key]\n }\n }\n await super._preSave(_is_new)\n await this.checkUniqueness(true, true)\n await this.validate()\n }\n\n /**\n * Performs custom validation - returns null in case of success or an object of validation issues\n */\n async custom_validation(): Promise<Record<string, string> | null> {\n return null\n }\n\n /**\n * Validate object's data\n */\n async validate(): Promise<void> {\n const cls = this._mCls\n const rules = cls.validation_rules\n if (!rules) {\n throw new OrangeDatabaseModelError(`Validation rules are not defined for model ${cls.name}`)\n }\n for (const rule_name of Object.keys(rules)) {\n if (Object.hasOwn(this.data, rule_name)) {\n const rule = rules[rule_name] as ODValidatorRuleSchema\n if (rule?.type) {\n const types = Array.isArray(rule.type) ? (rule.type as string[]) : [rule.type as string]\n if (types.includes('boolean')) {\n if ((this.data[rule_name] === 1) || (this.data[rule_name] === 0)) {\n this.data[rule_name] = this.data[rule_name] === 1\n }\n }\n }\n }\n }\n this.data = parse(rules, this.data, { strictMode: false })\n const custom_validation_errors = await this.custom_validation()\n if (custom_validation_errors && Object.keys(custom_validation_errors).length) {\n const ex = new OrangeDatabaseInputValidationError('Validation failed')\n for (const [param, message] of Object.entries(custom_validation_errors)) ex.info[param] = message\n throw ex\n }\n const relation_errors: string[] = []\n for (const rel_name of Object.keys(cls.available_relations)) {\n const rel: Relation = cls.available_relations[rel_name]\n if (rel.mode === 'parent') {\n const aKey = (rel as unknown as { _a_key_by_mode: string })._a_key_by_mode\n if (Object.hasOwn(this.data, aKey) && (this.data[aKey] !== null) && (this.data[aKey] !== 0)) {\n if ((await this.rel(rel_name, true) as Model | null) === null) {\n relation_errors.push(aKey)\n }\n }\n }\n if (relation_errors.length) {\n const ex = new OrangeDatabaseInputValidationError(`Some relations of the ${cls.name} are not found`)\n for (const param of relation_errors) ex.info[param] = 'Parent object not found'\n throw ex\n }\n }\n }\n\n /**\n * Returns object by ID if it exists and accessible by user\n * @param id\n * @param user\n * @param mode\n */\n static async findAndCheckAccessOrDie(id: unknown, user: unknown, mode: string | null = null): Promise<Model> {\n const obj = await this.find(id) as Model | null\n if (!obj) {\n throw new OrangeDatabaseModelRuntimeError(`${this.name} #${id} not found`)\n }\n if (!(await obj.accessible(user, mode))) {\n throw new OrangeDatabaseModelAccessError(`${this.name} #${id} is not accessible${mode ? ` for ${mode}` : ''}`)\n }\n return obj\n }\n\n /**\n * Returns whether the object is accessible by user\n * @param user\n * @param mode\n */\n \n async accessible(user: unknown, mode: string | null = null): Promise<boolean> {\n return mode === null\n }\n\n /**\n * Returns public data of the object\n */\n get output(): Record<string, unknown> {\n return {\n id: this.id,\n }\n }\n\n /**\n * Format output\n * @param mode\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n formatOutput(mode: string | null = null): Record<string, unknown> {\n return this.output\n }\n\n /**\n * Returns public data of the object with relations\n * @param required_relations\n * @param mode\n */\n async getExtendedOutput(required_relations: string[] = [], mode: string | null = null): Promise<Record<string, unknown>> {\n const output = this.formatOutput(mode)\n const cls = this._mCls\n for (const name of required_relations) {\n if (name.split(':').length > 1) continue\n if (cls.restricted_for_output.includes(name)) {\n throw new OrangeDatabaseModelError(`Relation \"${name}\" is not allowed for extended output of model ${cls.name}`)\n }\n const rel_data = await this.rel(name) as Model | Model[] | null\n const rel_mode = `relation:${cls.name}.${name}`\n const rel_relations = required_relations.filter(v => v.startsWith(`${name}:`)).map(v => v.slice(name.length + 1))\n output[`:${name}`] = Array.isArray(rel_data)\n ? await Promise.all(rel_data.map(v => v.getExtendedOutput(rel_relations, rel_mode)))\n : (rel_data ? await rel_data.getExtendedOutput(rel_relations, rel_mode) : null)\n }\n return output\n }\n}\n","import { OrangeDatabaseError, OrangeDatabaseInputError } from 'orange-dragonfly-orm'\n\nexport class OrangeDatabaseInputValidationError extends OrangeDatabaseInputError {\n private _info: Record<string, string> = {}\n\n get info(): Record<string, string> {\n return this._info\n }\n\n set info(v: Record<string, string>) {\n this._info = v\n }\n}\n\nexport class OrangeDatabaseModelError extends OrangeDatabaseError {}\n\nexport class OrangeDatabaseModelRuntimeError extends OrangeDatabaseModelError {}\n\nexport class OrangeDatabaseModelAccessError extends OrangeDatabaseModelRuntimeError {}\n"],"mappings":"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","names":["import_orange_dragonfly_orm"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/model.ts","../src/errors.ts"],"sourcesContent":["export { default } from './model'\nexport { default as Model, type IModelConstructor, type ModelData } from './model'\nexport {\n OrangeDatabaseInputValidationError,\n OrangeDatabaseModelAccessError,\n OrangeDatabaseModelError,\n OrangeDatabaseModelRuntimeError,\n} from './errors'\n","import { ActiveRecord, type DeleteQuery, type InsertQuery, type IQueryBuilder, type Relation, SelectQuery, type UpdateQuery } from 'orange-dragonfly-orm'\r\nimport { ODValidatorException, parse, type ODValidatorRuleSchema, type ODValidatorRulesSchema } from 'orange-dragonfly-validator'\r\n\r\ntype ValidatorTSType<T extends string> =\r\n T extends 'string' ? string :\r\n T extends 'integer' | 'number' ? number :\r\n T extends 'boolean' ? boolean :\r\n T extends 'null' ? null :\r\n T extends 'object' ? Record<string, unknown> :\r\n T extends 'array' ? unknown[] :\r\n T extends 'function' ? (...args: unknown[]) => unknown :\r\n unknown\r\n\r\ntype FieldTS<R extends ODValidatorRuleSchema> =\r\n R['type'] extends string\r\n ? ValidatorTSType<R['type']>\r\n : R['type'] extends readonly (infer U extends string)[]\r\n ? ValidatorTSType<U>\r\n : unknown\r\n\r\n/**\r\n * Converts an `ODValidatorRulesSchema` to a TypeScript object type.\r\n * Requires the schema to be declared with `as const satisfies ODValidatorRulesSchema`\r\n * to preserve literal type information.\r\n *\r\n * @example\r\n * const schema = { name: { type: 'string', required: true } } as const satisfies ODValidatorRulesSchema\r\n * class MyModel extends Model {\r\n * static override get validation_rules() { return schema }\r\n * declare data: ModelData<typeof schema>\r\n * }\r\n */\r\nexport type ModelData<S extends ODValidatorRulesSchema> = {\r\n [K in Exclude<keyof S, '@' | '#' | '*'>]: S[K] extends ODValidatorRuleSchema ? FieldTS<S[K]> : never\r\n}\r\n\r\nimport {\r\n OrangeDatabaseInputValidationError,\r\n OrangeDatabaseModelAccessError,\r\n OrangeDatabaseModelError,\r\n OrangeDatabaseModelRuntimeError,\r\n} from './errors'\r\n\r\nexport interface IModelConstructor {\r\n new(data?: Record<string, unknown>): Model\r\n readonly id_key: string\r\n readonly table: string\r\n readonly name: string\r\n readonly special_fields: string[]\r\n readonly available_relations: Record<string, Relation>\r\n readonly ignore_extra_fields: boolean\r\n readonly unique_keys: string[][]\r\n readonly fulltext_indexes: string[][]\r\n /** @deprecated Use `ignore_extra_fields` instead */\r\n readonly IGNORE_EXTRA_FIELDS: boolean\r\n /** @deprecated Use `unique_keys` instead */\r\n readonly UNIQUE_KEYS: string[][]\r\n /** @deprecated Use `fulltext_indexes` instead */\r\n readonly FULLTEXT_INDEXES: string[][]\r\n readonly validation_rules: ODValidatorRulesSchema\r\n readonly restricted_for_output: string[]\r\n readonly restricted_for_lookup: string[]\r\n readonly restricted_for_create: string[]\r\n readonly restricted_for_update: string[]\r\n resetRegisteredModels(): void\r\n insertQuery(): InsertQuery\r\n selectQuery(include_deleted?: boolean): IQueryBuilder<Model>\r\n updateQuery(include_deleted?: boolean): UpdateQuery\r\n deleteQuery(include_deleted?: boolean): DeleteQuery\r\n loadRelations(objects: Model[], relations?: string[] | null): Promise<Model[]>\r\n find(id: unknown, include_deleted?: boolean): Promise<Model | null>\r\n all(include_deleted?: boolean): Promise<Model[]>\r\n lookupQuery(data: Record<string, unknown>, basicQuery?: SelectQuery | null): SelectQuery<Model>\r\n create(data: Record<string, unknown>): Promise<Model>\r\n findAndCheckAccessOrDie(id: unknown, user: unknown, mode?: string | null): Promise<Model>\r\n model(class_name: string): IModelConstructor\r\n registerModel(model_class: IModelConstructor): IModelConstructor\r\n}\r\n\r\nexport default class Model extends ActiveRecord {\r\n private get _mCls(): typeof Model {\r\n return this.constructor as typeof Model\r\n }\r\n\r\n static get ignore_extra_fields(): boolean {\r\n return false\r\n }\r\n\r\n /**\r\n * Returns list of unique keys\r\n * @returns List of unique keys\r\n */\r\n static get unique_keys(): string[][] {\r\n return []\r\n }\r\n\r\n /**\r\n * Returns list of fulltext indexes\r\n * @returns List of fulltext indexes\r\n */\r\n static get fulltext_indexes(): string[][] {\r\n return []\r\n }\r\n\r\n /** @deprecated Use `ignore_extra_fields` instead */\r\n static get IGNORE_EXTRA_FIELDS(): boolean {\r\n return this.ignore_extra_fields\r\n }\r\n\r\n /** @deprecated Use `unique_keys` instead */\r\n static get UNIQUE_KEYS(): string[][] {\r\n return this.unique_keys\r\n }\r\n\r\n /** @deprecated Use `fulltext_indexes` instead */\r\n static get FULLTEXT_INDEXES(): string[][] {\r\n return this.fulltext_indexes\r\n }\r\n\r\n /**\r\n * Returns schema for the model (Orange Dragonfly Validator format)\r\n */\r\n static get validation_rules(): ODValidatorRulesSchema {\r\n return {\r\n id: {\r\n required: false,\r\n type: 'integer',\r\n min: 1,\r\n },\r\n }\r\n }\r\n\r\n /**\r\n * Overridden method of ActiveRecord. Returns special fields list based on validation rules\r\n */\r\n static override get special_fields(): string[] {\r\n const rules = this.validation_rules\r\n const fields: string[] = []\r\n for (const field of ['created_at', 'updated_at', 'deleted_at']) {\r\n if (Object.hasOwn(rules, field)) fields.push(field)\r\n }\r\n return fields\r\n }\r\n\r\n /**\r\n * Returns list of relations restricted for extended output\r\n */\r\n static get restricted_for_output(): string[] {\r\n return []\r\n }\r\n\r\n /**\r\n * Returns list of fields restricted for lookup\r\n */\r\n static get restricted_for_lookup(): string[] {\r\n return []\r\n }\r\n\r\n /**\r\n * Returns list of fields restricted for create method\r\n */\r\n static get restricted_for_create(): string[] {\r\n return ['id'].concat(this.special_fields)\r\n }\r\n\r\n /**\r\n * Returns list of fields restricted for update method\r\n */\r\n static get restricted_for_update(): string[] {\r\n return ['id'].concat(this.special_fields)\r\n }\r\n\r\n /**\r\n * Lookup method\r\n * @param data\r\n * @param basicQuery Query to be used for adding conditions. By default a new SelectQuery is created.\r\n */\r\n static lookupQuery<T extends Model>(\r\n this: abstract new (data?: Record<string, unknown>) => T,\r\n data: Record<string, unknown>,\r\n basicQuery: SelectQuery | null = null,\r\n ): SelectQuery<T> {\r\n const cls = this as unknown as typeof Model\r\n const rules = cls.validation_rules\r\n const q = (basicQuery ?? cls.selectQuery()) as SelectQuery<T>\r\n const filtered_rules: ODValidatorRulesSchema = {}\r\n for (const field of Object.keys(data)) {\r\n if (!Object.hasOwn(rules, field)) {\r\n if (cls.ignore_extra_fields) {\r\n continue\r\n }\r\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\r\n ex.info[field] = `Field \"${field}\" is not described for model ${cls.name}`\r\n throw ex\r\n }\r\n if (cls.restricted_for_lookup.includes(field)) {\r\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\r\n ex.info[field] = `Field \"${field}\" is restricted for searching model ${cls.name}`\r\n throw ex\r\n }\r\n q.where(field, data[field])\r\n const rule = rules[field] as ODValidatorRuleSchema\r\n filtered_rules[field] = Array.isArray(data[field]) ? { type: 'array', children: { '*': rule } } : rule\r\n }\r\n try {\r\n parse(filtered_rules, data, { strictMode: false })\r\n } catch (e) {\r\n if (e instanceof ODValidatorException) {\r\n const message = `Model ${cls.name} lookup rules validation failed`\r\n console.error(message, e.info)\r\n throw new OrangeDatabaseInputValidationError(message)\r\n } else {\r\n throw e\r\n }\r\n }\r\n return q\r\n }\r\n\r\n /**\r\n * Creates object\r\n * @param data\r\n */\r\n static async create<T extends Model>(\r\n this: abstract new (data?: Record<string, unknown>) => T,\r\n data: Record<string, unknown>,\r\n ): Promise<T> {\r\n const cls = this as unknown as typeof Model\r\n const rules = cls.validation_rules\r\n const new_data: Record<string, unknown> = {}\r\n for (const field of Object.keys(data)) {\r\n if (!Object.hasOwn(rules, field)) {\r\n if (cls.ignore_extra_fields) {\r\n continue\r\n }\r\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\r\n ex.info[field] = `Field \"${field}\" is not described for model ${cls.name}`\r\n throw ex\r\n }\r\n if (cls.restricted_for_create.includes(field)) {\r\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\r\n ex.info[field] = `Field \"${field}\" is restricted for creating model ${cls.name}`\r\n throw ex\r\n }\r\n new_data[field] = data[field]\r\n }\r\n const Cls = this as unknown as new (data?: Record<string, unknown>) => T\r\n return (new Cls(new_data)).save()\r\n }\r\n\r\n /**\r\n * Updates object\r\n * @param data\r\n */\r\n async update(data: Record<string, unknown>): Promise<this> {\r\n if (!this.id) {\r\n throw new OrangeDatabaseModelRuntimeError('You can update saved object only')\r\n }\r\n const cls = this._mCls\r\n const rules = cls.validation_rules\r\n const new_data: Record<string, unknown> = {}\r\n for (const field of Object.keys(data)) {\r\n if (!Object.hasOwn(rules, field)) {\r\n if (cls.ignore_extra_fields) {\r\n continue\r\n }\r\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\r\n ex.info[field] = `Field \"${field}\" is not described for model ${cls.name}`\r\n throw ex\r\n }\r\n if (cls.restricted_for_update.includes(field)) {\r\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\r\n ex.info[field] = `Field \"${field}\" is restricted for updating model ${cls.name}`\r\n throw ex\r\n }\r\n new_data[field] = data[field]\r\n }\r\n return this.save(new_data)\r\n }\r\n\r\n /**\r\n * Checks uniqueness of the object based on unique_keys\r\n */\r\n async checkUniqueness(exception_mode = false, ignore_null = false): Promise<boolean> {\r\n const cls = this._mCls\r\n for (const fields of cls.unique_keys) {\r\n if (!await this.isUnique(fields, ignore_null)) {\r\n if (exception_mode) {\r\n const ex = new OrangeDatabaseInputValidationError('Object is not unique')\r\n for (const field of fields) {\r\n ex.info[field] = 'Part of the unique key'\r\n }\r\n throw ex\r\n }\r\n return false\r\n }\r\n }\r\n return true\r\n }\r\n\r\n protected override async _preSave(_is_new?: boolean): Promise<void> {\r\n const cls = this._mCls\r\n if (cls.ignore_extra_fields) {\r\n const rules = cls.validation_rules\r\n for (const key of Object.keys(this.data).filter(v => !Object.hasOwn(rules, v))) {\r\n delete this.data[key]\r\n }\r\n }\r\n await super._preSave(_is_new)\r\n await this.checkUniqueness(true, true)\r\n await this.validate()\r\n }\r\n\r\n /**\r\n * Performs custom validation - returns null in case of success or an object of validation issues\r\n */\r\n async custom_validation(): Promise<Record<string, string> | null> {\r\n return null\r\n }\r\n\r\n /**\r\n * Validate object's data\r\n */\r\n async validate(): Promise<void> {\r\n const cls = this._mCls\r\n const rules = cls.validation_rules\r\n if (!rules) {\r\n throw new OrangeDatabaseModelError(`Validation rules are not defined for model ${cls.name}`)\r\n }\r\n for (const rule_name of Object.keys(rules)) {\r\n if (Object.hasOwn(this.data, rule_name)) {\r\n const rule = rules[rule_name] as ODValidatorRuleSchema\r\n if (rule?.type) {\r\n const types = Array.isArray(rule.type) ? (rule.type as string[]) : [rule.type as string]\r\n if (types.includes('boolean')) {\r\n if ((this.data[rule_name] === 1) || (this.data[rule_name] === 0)) {\r\n this.data[rule_name] = this.data[rule_name] === 1\r\n }\r\n }\r\n }\r\n }\r\n }\r\n try {\r\n this.data = parse(rules, this.data, { strictMode: false })\r\n } catch (e) {\r\n if (e instanceof ODValidatorException) {\r\n const message = `Model ${cls.name} rules validation failed`\r\n console.error(message, e.info)\r\n const ex = new OrangeDatabaseInputValidationError(message)\r\n for (const [key, msgs] of Object.entries(e.info as Record<string, string | string[]>)) {\r\n ex.info[key] = Array.isArray(msgs) ? msgs[0] : msgs\r\n }\r\n throw ex\r\n } else {\r\n throw e\r\n }\r\n }\r\n const custom_validation_errors = await this.custom_validation()\r\n if (custom_validation_errors && Object.keys(custom_validation_errors).length) {\r\n const ex = new OrangeDatabaseInputValidationError('Validation failed')\r\n for (const [param, message] of Object.entries(custom_validation_errors)) ex.info[param] = message\r\n throw ex\r\n }\r\n const relation_errors: string[] = []\r\n for (const rel_name of Object.keys(cls.available_relations)) {\r\n const rel: Relation = cls.available_relations[rel_name]\r\n if (rel.mode === 'parent') {\r\n const aKey = (rel as unknown as { _a_key_by_mode: string })._a_key_by_mode\r\n if (Object.hasOwn(this.data, aKey) && (this.data[aKey] !== null) && (this.data[aKey] !== 0)) {\r\n if ((await this.rel(rel_name, true) as Model | null) === null) {\r\n relation_errors.push(aKey)\r\n }\r\n }\r\n }\r\n }\r\n if (relation_errors.length) {\r\n const ex = new OrangeDatabaseInputValidationError(`Some relations of the ${cls.name} are not found`)\r\n for (const param of relation_errors) ex.info[param] = 'Parent object not found'\r\n throw ex\r\n }\r\n }\r\n\r\n static override model(class_name: string): IModelConstructor {\r\n return super.model(class_name) as unknown as IModelConstructor\r\n }\r\n\r\n static override registerModel(model_class: Parameters<typeof ActiveRecord.registerModel>[0]): typeof Model {\r\n return super.registerModel(model_class) as unknown as typeof Model\r\n }\r\n\r\n /**\r\n * Returns object by ID if it exists and accessible by user\r\n * @param id\r\n * @param user\r\n * @param mode\r\n */\r\n static async findAndCheckAccessOrDie<T extends Model>(\r\n this: abstract new (data?: Record<string, unknown>) => T,\r\n id: number,\r\n user: unknown,\r\n mode: string | null = null,\r\n ): Promise<T> {\r\n const cls = this as unknown as typeof Model\r\n const obj = await cls.find(id) as T | null\r\n if (!obj) {\r\n throw new OrangeDatabaseModelRuntimeError(`${cls.name} #${id} not found`)\r\n }\r\n if (!(await obj.accessible(user, mode))) {\r\n throw new OrangeDatabaseModelAccessError(`${cls.name} #${id} is not accessible${mode ? ` for ${mode}` : ''}`)\r\n }\r\n return obj\r\n }\r\n\r\n /**\r\n * Returns whether the object is accessible by user\r\n * @param user\r\n * @param mode\r\n */\r\n \r\n async accessible(_user: unknown, mode: string | null = null): Promise<boolean> {\r\n return mode === null\r\n }\r\n\r\n /**\r\n * Returns public data of the object\r\n */\r\n get output(): Record<string, unknown> {\r\n return {\r\n id: this.id,\r\n }\r\n }\r\n\r\n /**\r\n * Format output\r\n * @param mode\r\n */\r\n formatOutput(_mode: string | null = null): Record<string, unknown> {\r\n return this.output\r\n }\r\n\r\n /**\r\n * Returns public data of the object with relations\r\n * @param required_relations\r\n * @param mode\r\n */\r\n async getExtendedOutput(required_relations: string[] = [], mode: string | null = null): Promise<Record<string, unknown>> {\r\n const output = this.formatOutput(mode)\r\n const cls = this._mCls\r\n for (const name of required_relations) {\r\n if (name.split(':').length > 1) continue\r\n if (cls.restricted_for_output.includes(name)) {\r\n throw new OrangeDatabaseModelError(`Relation \"${name}\" is not allowed for extended output of model ${cls.name}`)\r\n }\r\n const rel_data = await this.rel(name) as Model | Model[] | null\r\n const rel_mode = `relation:${cls.name}.${name}`\r\n const rel_relations = required_relations.filter(v => v.startsWith(`${name}:`)).map(v => v.slice(name.length + 1))\r\n output[`:${name}`] = Array.isArray(rel_data)\r\n ? await Promise.all(rel_data.map(v => v.getExtendedOutput(rel_relations, rel_mode)))\r\n : (rel_data ? await rel_data.getExtendedOutput(rel_relations, rel_mode) : null)\r\n }\r\n return output\r\n }\r\n}\r\n","import { OrangeDatabaseError, OrangeDatabaseInputError } from 'orange-dragonfly-orm'\n\nexport class OrangeDatabaseInputValidationError extends OrangeDatabaseInputError {\n private _info: Record<string, string> = {}\n\n get info(): Record<string, string> {\n return this._info\n }\n\n set info(v: Record<string, string>) {\n this._info = v\n }\n}\n\nexport class OrangeDatabaseModelError extends OrangeDatabaseError {}\n\nexport class OrangeDatabaseModelRuntimeError extends OrangeDatabaseModelError {}\n\nexport class OrangeDatabaseModelAccessError extends OrangeDatabaseModelRuntimeError {}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,+BAAmI;AACnI,wCAAqG;;;ACDrG,kCAA8D;AAEvD,IAAM,qCAAN,cAAiD,qDAAyB;AAAA,EACvE,QAAgC,CAAC;AAAA,EAEzC,IAAI,OAA+B;AACjC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,KAAK,GAA2B;AAClC,SAAK,QAAQ;AAAA,EACf;AACF;AAEO,IAAM,2BAAN,cAAuC,gDAAoB;AAAC;AAE5D,IAAM,kCAAN,cAA8C,yBAAyB;AAAC;AAExE,IAAM,iCAAN,cAA6C,gCAAgC;AAAC;;;AD6DrF,IAAqB,QAArB,cAAmC,0CAAa;AAAA,EAC9C,IAAY,QAAsB;AAChC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,WAAW,sBAA+B;AACxC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,WAAW,cAA0B;AACnC,WAAO,CAAC;AAAA,EACV;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,WAAW,mBAA+B;AACxC,WAAO,CAAC;AAAA,EACV;AAAA;AAAA,EAGA,WAAW,sBAA+B;AACxC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,WAAW,cAA0B;AACnC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,WAAW,mBAA+B;AACxC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,mBAA2C;AACpD,WAAO;AAAA,MACL,IAAI;AAAA,QACF,UAAU;AAAA,QACV,MAAM;AAAA,QACN,KAAK;AAAA,MACP;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,WAAoB,iBAA2B;AAC7C,UAAM,QAAQ,KAAK;AACnB,UAAM,SAAmB,CAAC;AAC1B,eAAW,SAAS,CAAC,cAAc,cAAc,YAAY,GAAG;AAC9D,UAAI,OAAO,OAAO,OAAO,KAAK,EAAG,QAAO,KAAK,KAAK;AAAA,IACpD;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,wBAAkC;AAC3C,WAAO,CAAC;AAAA,EACV;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,wBAAkC;AAC3C,WAAO,CAAC;AAAA,EACV;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,wBAAkC;AAC3C,WAAO,CAAC,IAAI,EAAE,OAAO,KAAK,cAAc;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,wBAAkC;AAC3C,WAAO,CAAC,IAAI,EAAE,OAAO,KAAK,cAAc;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,YAEL,MACA,aAAiC,MACjB;AAChB,UAAM,MAAM;AACZ,UAAM,QAAQ,IAAI;AAClB,UAAM,IAAK,cAAc,IAAI,YAAY;AACzC,UAAM,iBAAyC,CAAC;AAChD,eAAW,SAAS,OAAO,KAAK,IAAI,GAAG;AACrC,UAAI,CAAC,OAAO,OAAO,OAAO,KAAK,GAAG;AAChC,YAAI,IAAI,qBAAqB;AAC3B;AAAA,QACF;AACA,cAAM,KAAK,IAAI,mCAAmC,kBAAkB;AACpE,WAAG,KAAK,KAAK,IAAI,UAAU,KAAK,gCAAgC,IAAI,IAAI;AACxE,cAAM;AAAA,MACR;AACA,UAAI,IAAI,sBAAsB,SAAS,KAAK,GAAG;AAC7C,cAAM,KAAK,IAAI,mCAAmC,kBAAkB;AACpE,WAAG,KAAK,KAAK,IAAI,UAAU,KAAK,uCAAuC,IAAI,IAAI;AAC/E,cAAM;AAAA,MACR;AACA,QAAE,MAAM,OAAO,KAAK,KAAK,CAAC;AAC1B,YAAM,OAAO,MAAM,KAAK;AACxB,qBAAe,KAAK,IAAI,MAAM,QAAQ,KAAK,KAAK,CAAC,IAAI,EAAE,MAAM,SAAS,UAAU,EAAE,KAAK,KAAK,EAAE,IAAI;AAAA,IACpG;AACA,QAAI;AACF,mDAAM,gBAAgB,MAAM,EAAE,YAAY,MAAM,CAAC;AAAA,IACnD,SAAS,GAAG;AACV,UAAI,aAAa,wDAAsB;AACrC,cAAM,UAAU,SAAS,IAAI,IAAI;AACjC,gBAAQ,MAAM,SAAS,EAAE,IAAI;AAC7B,cAAM,IAAI,mCAAmC,OAAO;AAAA,MACtD,OAAO;AACL,cAAM;AAAA,MACR;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa,OAEX,MACY;AACZ,UAAM,MAAM;AACZ,UAAM,QAAQ,IAAI;AAClB,UAAM,WAAoC,CAAC;AAC3C,eAAW,SAAS,OAAO,KAAK,IAAI,GAAG;AACrC,UAAI,CAAC,OAAO,OAAO,OAAO,KAAK,GAAG;AAChC,YAAI,IAAI,qBAAqB;AAC3B;AAAA,QACF;AACA,cAAM,KAAK,IAAI,mCAAmC,kBAAkB;AACpE,WAAG,KAAK,KAAK,IAAI,UAAU,KAAK,gCAAgC,IAAI,IAAI;AACxE,cAAM;AAAA,MACR;AACA,UAAI,IAAI,sBAAsB,SAAS,KAAK,GAAG;AAC7C,cAAM,KAAK,IAAI,mCAAmC,kBAAkB;AACpE,WAAG,KAAK,KAAK,IAAI,UAAU,KAAK,sCAAsC,IAAI,IAAI;AAC9E,cAAM;AAAA,MACR;AACA,eAAS,KAAK,IAAI,KAAK,KAAK;AAAA,IAC9B;AACA,UAAM,MAAM;AACZ,WAAQ,IAAI,IAAI,QAAQ,EAAG,KAAK;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAO,MAA8C;AACzD,QAAI,CAAC,KAAK,IAAI;AACZ,YAAM,IAAI,gCAAgC,kCAAkC;AAAA,IAC9E;AACA,UAAM,MAAM,KAAK;AACjB,UAAM,QAAQ,IAAI;AAClB,UAAM,WAAoC,CAAC;AAC3C,eAAW,SAAS,OAAO,KAAK,IAAI,GAAG;AACrC,UAAI,CAAC,OAAO,OAAO,OAAO,KAAK,GAAG;AAChC,YAAI,IAAI,qBAAqB;AAC3B;AAAA,QACF;AACA,cAAM,KAAK,IAAI,mCAAmC,kBAAkB;AACpE,WAAG,KAAK,KAAK,IAAI,UAAU,KAAK,gCAAgC,IAAI,IAAI;AACxE,cAAM;AAAA,MACR;AACA,UAAI,IAAI,sBAAsB,SAAS,KAAK,GAAG;AAC7C,cAAM,KAAK,IAAI,mCAAmC,kBAAkB;AACpE,WAAG,KAAK,KAAK,IAAI,UAAU,KAAK,sCAAsC,IAAI,IAAI;AAC9E,cAAM;AAAA,MACR;AACA,eAAS,KAAK,IAAI,KAAK,KAAK;AAAA,IAC9B;AACA,WAAO,KAAK,KAAK,QAAQ;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAAgB,iBAAiB,OAAO,cAAc,OAAyB;AACnF,UAAM,MAAM,KAAK;AACjB,eAAW,UAAU,IAAI,aAAa;AACpC,UAAI,CAAC,MAAM,KAAK,SAAS,QAAQ,WAAW,GAAG;AAC7C,YAAI,gBAAgB;AAClB,gBAAM,KAAK,IAAI,mCAAmC,sBAAsB;AACxE,qBAAW,SAAS,QAAQ;AAC1B,eAAG,KAAK,KAAK,IAAI;AAAA,UACnB;AACA,gBAAM;AAAA,QACR;AACA,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAyB,SAAS,SAAkC;AAClE,UAAM,MAAM,KAAK;AACjB,QAAI,IAAI,qBAAqB;AAC3B,YAAM,QAAQ,IAAI;AAClB,iBAAW,OAAO,OAAO,KAAK,KAAK,IAAI,EAAE,OAAO,OAAK,CAAC,OAAO,OAAO,OAAO,CAAC,CAAC,GAAG;AAC9E,eAAO,KAAK,KAAK,GAAG;AAAA,MACtB;AAAA,IACF;AACA,UAAM,MAAM,SAAS,OAAO;AAC5B,UAAM,KAAK,gBAAgB,MAAM,IAAI;AACrC,UAAM,KAAK,SAAS;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,oBAA4D;AAChE,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAA0B;AAC9B,UAAM,MAAM,KAAK;AACjB,UAAM,QAAQ,IAAI;AAClB,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,yBAAyB,8CAA8C,IAAI,IAAI,EAAE;AAAA,IAC7F;AACA,eAAW,aAAa,OAAO,KAAK,KAAK,GAAG;AAC1C,UAAI,OAAO,OAAO,KAAK,MAAM,SAAS,GAAG;AACvC,cAAM,OAAO,MAAM,SAAS;AAC5B,YAAI,MAAM,MAAM;AACd,gBAAM,QAAQ,MAAM,QAAQ,KAAK,IAAI,IAAK,KAAK,OAAoB,CAAC,KAAK,IAAc;AACvF,cAAI,MAAM,SAAS,SAAS,GAAG;AAC7B,gBAAK,KAAK,KAAK,SAAS,MAAM,KAAO,KAAK,KAAK,SAAS,MAAM,GAAI;AAChE,mBAAK,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,MAAM;AAAA,YAClD;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,QAAI;AACF,WAAK,WAAO,yCAAM,OAAO,KAAK,MAAM,EAAE,YAAY,MAAM,CAAC;AAAA,IAC3D,SAAS,GAAG;AACV,UAAI,aAAa,wDAAsB;AACrC,cAAM,UAAU,SAAS,IAAI,IAAI;AACjC,gBAAQ,MAAM,SAAS,EAAE,IAAI;AAC7B,cAAM,KAAK,IAAI,mCAAmC,OAAO;AACzD,mBAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,EAAE,IAAyC,GAAG;AACrF,aAAG,KAAK,GAAG,IAAI,MAAM,QAAQ,IAAI,IAAI,KAAK,CAAC,IAAI;AAAA,QACjD;AACA,cAAM;AAAA,MACR,OAAO;AACL,cAAM;AAAA,MACR;AAAA,IACF;AACA,UAAM,2BAA2B,MAAM,KAAK,kBAAkB;AAC9D,QAAI,4BAA4B,OAAO,KAAK,wBAAwB,EAAE,QAAQ;AAC5E,YAAM,KAAK,IAAI,mCAAmC,mBAAmB;AACrE,iBAAW,CAAC,OAAO,OAAO,KAAK,OAAO,QAAQ,wBAAwB,EAAG,IAAG,KAAK,KAAK,IAAI;AAC1F,YAAM;AAAA,IACR;AACA,UAAM,kBAA4B,CAAC;AACnC,eAAW,YAAY,OAAO,KAAK,IAAI,mBAAmB,GAAG;AAC3D,YAAM,MAAgB,IAAI,oBAAoB,QAAQ;AACtD,UAAI,IAAI,SAAS,UAAU;AACzB,cAAM,OAAQ,IAA8C;AAC5D,YAAI,OAAO,OAAO,KAAK,MAAM,IAAI,KAAM,KAAK,KAAK,IAAI,MAAM,QAAU,KAAK,KAAK,IAAI,MAAM,GAAI;AAC3F,cAAK,MAAM,KAAK,IAAI,UAAU,IAAI,MAAuB,MAAM;AAC7D,4BAAgB,KAAK,IAAI;AAAA,UAC3B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,QAAI,gBAAgB,QAAQ;AAC1B,YAAM,KAAK,IAAI,mCAAmC,yBAAyB,IAAI,IAAI,gBAAgB;AACnG,iBAAW,SAAS,gBAAiB,IAAG,KAAK,KAAK,IAAI;AACtD,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,OAAgB,MAAM,YAAuC;AAC3D,WAAO,MAAM,MAAM,UAAU;AAAA,EAC/B;AAAA,EAEA,OAAgB,cAAc,aAA6E;AACzG,WAAO,MAAM,cAAc,WAAW;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,aAAa,wBAEX,IACA,MACA,OAAsB,MACV;AACZ,UAAM,MAAM;AACZ,UAAM,MAAM,MAAM,IAAI,KAAK,EAAE;AAC7B,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,gCAAgC,GAAG,IAAI,IAAI,KAAK,EAAE,YAAY;AAAA,IAC1E;AACA,QAAI,CAAE,MAAM,IAAI,WAAW,MAAM,IAAI,GAAI;AACvC,YAAM,IAAI,+BAA+B,GAAG,IAAI,IAAI,KAAK,EAAE,qBAAqB,OAAO,QAAQ,IAAI,KAAK,EAAE,EAAE;AAAA,IAC9G;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,WAAW,OAAgB,OAAsB,MAAwB;AAC7E,WAAO,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,SAAkC;AACpC,WAAO;AAAA,MACL,IAAI,KAAK;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa,QAAuB,MAA+B;AACjE,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,kBAAkB,qBAA+B,CAAC,GAAG,OAAsB,MAAwC;AACvH,UAAM,SAAS,KAAK,aAAa,IAAI;AACrC,UAAM,MAAM,KAAK;AACjB,eAAW,QAAQ,oBAAoB;AACrC,UAAI,KAAK,MAAM,GAAG,EAAE,SAAS,EAAG;AAChC,UAAI,IAAI,sBAAsB,SAAS,IAAI,GAAG;AAC5C,cAAM,IAAI,yBAAyB,aAAa,IAAI,iDAAiD,IAAI,IAAI,EAAE;AAAA,MACjH;AACA,YAAM,WAAW,MAAM,KAAK,IAAI,IAAI;AACpC,YAAM,WAAW,YAAY,IAAI,IAAI,IAAI,IAAI;AAC7C,YAAM,gBAAgB,mBAAmB,OAAO,OAAK,EAAE,WAAW,GAAG,IAAI,GAAG,CAAC,EAAE,IAAI,OAAK,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC;AAChH,aAAO,IAAI,IAAI,EAAE,IAAI,MAAM,QAAQ,QAAQ,IACvC,MAAM,QAAQ,IAAI,SAAS,IAAI,OAAK,EAAE,kBAAkB,eAAe,QAAQ,CAAC,CAAC,IAChF,WAAW,MAAM,SAAS,kBAAkB,eAAe,QAAQ,IAAI;AAAA,IAC9E;AACA,WAAO;AAAA,EACT;AACF;","names":["import_orange_dragonfly_orm"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,58 @@
|
|
|
1
|
-
import { ActiveRecord, SelectQuery, OrangeDatabaseInputError, OrangeDatabaseError } from 'orange-dragonfly-orm';
|
|
2
|
-
import { ODValidatorRulesSchema } from 'orange-dragonfly-validator';
|
|
1
|
+
import { ActiveRecord, SelectQuery, Relation, InsertQuery, IQueryBuilder, UpdateQuery, DeleteQuery, OrangeDatabaseInputError, OrangeDatabaseError } from 'orange-dragonfly-orm';
|
|
2
|
+
import { ODValidatorRulesSchema, ODValidatorRuleSchema } from 'orange-dragonfly-validator';
|
|
3
3
|
|
|
4
|
+
type ValidatorTSType<T extends string> = T extends 'string' ? string : T extends 'integer' | 'number' ? number : T extends 'boolean' ? boolean : T extends 'null' ? null : T extends 'object' ? Record<string, unknown> : T extends 'array' ? unknown[] : T extends 'function' ? (...args: unknown[]) => unknown : unknown;
|
|
5
|
+
type FieldTS<R extends ODValidatorRuleSchema> = R['type'] extends string ? ValidatorTSType<R['type']> : R['type'] extends readonly (infer U extends string)[] ? ValidatorTSType<U> : unknown;
|
|
6
|
+
/**
|
|
7
|
+
* Converts an `ODValidatorRulesSchema` to a TypeScript object type.
|
|
8
|
+
* Requires the schema to be declared with `as const satisfies ODValidatorRulesSchema`
|
|
9
|
+
* to preserve literal type information.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const schema = { name: { type: 'string', required: true } } as const satisfies ODValidatorRulesSchema
|
|
13
|
+
* class MyModel extends Model {
|
|
14
|
+
* static override get validation_rules() { return schema }
|
|
15
|
+
* declare data: ModelData<typeof schema>
|
|
16
|
+
* }
|
|
17
|
+
*/
|
|
18
|
+
type ModelData<S extends ODValidatorRulesSchema> = {
|
|
19
|
+
[K in Exclude<keyof S, '@' | '#' | '*'>]: S[K] extends ODValidatorRuleSchema ? FieldTS<S[K]> : never;
|
|
20
|
+
};
|
|
21
|
+
interface IModelConstructor {
|
|
22
|
+
new (data?: Record<string, unknown>): Model;
|
|
23
|
+
readonly id_key: string;
|
|
24
|
+
readonly table: string;
|
|
25
|
+
readonly name: string;
|
|
26
|
+
readonly special_fields: string[];
|
|
27
|
+
readonly available_relations: Record<string, Relation>;
|
|
28
|
+
readonly ignore_extra_fields: boolean;
|
|
29
|
+
readonly unique_keys: string[][];
|
|
30
|
+
readonly fulltext_indexes: string[][];
|
|
31
|
+
/** @deprecated Use `ignore_extra_fields` instead */
|
|
32
|
+
readonly IGNORE_EXTRA_FIELDS: boolean;
|
|
33
|
+
/** @deprecated Use `unique_keys` instead */
|
|
34
|
+
readonly UNIQUE_KEYS: string[][];
|
|
35
|
+
/** @deprecated Use `fulltext_indexes` instead */
|
|
36
|
+
readonly FULLTEXT_INDEXES: string[][];
|
|
37
|
+
readonly validation_rules: ODValidatorRulesSchema;
|
|
38
|
+
readonly restricted_for_output: string[];
|
|
39
|
+
readonly restricted_for_lookup: string[];
|
|
40
|
+
readonly restricted_for_create: string[];
|
|
41
|
+
readonly restricted_for_update: string[];
|
|
42
|
+
resetRegisteredModels(): void;
|
|
43
|
+
insertQuery(): InsertQuery;
|
|
44
|
+
selectQuery(include_deleted?: boolean): IQueryBuilder<Model>;
|
|
45
|
+
updateQuery(include_deleted?: boolean): UpdateQuery;
|
|
46
|
+
deleteQuery(include_deleted?: boolean): DeleteQuery;
|
|
47
|
+
loadRelations(objects: Model[], relations?: string[] | null): Promise<Model[]>;
|
|
48
|
+
find(id: unknown, include_deleted?: boolean): Promise<Model | null>;
|
|
49
|
+
all(include_deleted?: boolean): Promise<Model[]>;
|
|
50
|
+
lookupQuery(data: Record<string, unknown>, basicQuery?: SelectQuery | null): SelectQuery<Model>;
|
|
51
|
+
create(data: Record<string, unknown>): Promise<Model>;
|
|
52
|
+
findAndCheckAccessOrDie(id: unknown, user: unknown, mode?: string | null): Promise<Model>;
|
|
53
|
+
model(class_name: string): IModelConstructor;
|
|
54
|
+
registerModel(model_class: IModelConstructor): IModelConstructor;
|
|
55
|
+
}
|
|
4
56
|
declare class Model extends ActiveRecord {
|
|
5
57
|
private get _mCls();
|
|
6
58
|
static get ignore_extra_fields(): boolean;
|
|
@@ -49,12 +101,12 @@ declare class Model extends ActiveRecord {
|
|
|
49
101
|
* @param data
|
|
50
102
|
* @param basicQuery Query to be used for adding conditions. By default a new SelectQuery is created.
|
|
51
103
|
*/
|
|
52
|
-
static lookupQuery(data: Record<string, unknown>, basicQuery?: SelectQuery | null): SelectQuery
|
|
104
|
+
static lookupQuery<T extends Model>(this: abstract new (data?: Record<string, unknown>) => T, data: Record<string, unknown>, basicQuery?: SelectQuery | null): SelectQuery<T>;
|
|
53
105
|
/**
|
|
54
106
|
* Creates object
|
|
55
107
|
* @param data
|
|
56
108
|
*/
|
|
57
|
-
static create(data: Record<string, unknown>): Promise<
|
|
109
|
+
static create<T extends Model>(this: abstract new (data?: Record<string, unknown>) => T, data: Record<string, unknown>): Promise<T>;
|
|
58
110
|
/**
|
|
59
111
|
* Updates object
|
|
60
112
|
* @param data
|
|
@@ -73,19 +125,21 @@ declare class Model extends ActiveRecord {
|
|
|
73
125
|
* Validate object's data
|
|
74
126
|
*/
|
|
75
127
|
validate(): Promise<void>;
|
|
128
|
+
static model(class_name: string): IModelConstructor;
|
|
129
|
+
static registerModel(model_class: Parameters<typeof ActiveRecord.registerModel>[0]): typeof Model;
|
|
76
130
|
/**
|
|
77
131
|
* Returns object by ID if it exists and accessible by user
|
|
78
132
|
* @param id
|
|
79
133
|
* @param user
|
|
80
134
|
* @param mode
|
|
81
135
|
*/
|
|
82
|
-
static findAndCheckAccessOrDie(
|
|
136
|
+
static findAndCheckAccessOrDie<T extends Model>(this: abstract new (data?: Record<string, unknown>) => T, id: number, user: unknown, mode?: string | null): Promise<T>;
|
|
83
137
|
/**
|
|
84
138
|
* Returns whether the object is accessible by user
|
|
85
139
|
* @param user
|
|
86
140
|
* @param mode
|
|
87
141
|
*/
|
|
88
|
-
accessible(
|
|
142
|
+
accessible(_user: unknown, mode?: string | null): Promise<boolean>;
|
|
89
143
|
/**
|
|
90
144
|
* Returns public data of the object
|
|
91
145
|
*/
|
|
@@ -94,7 +148,7 @@ declare class Model extends ActiveRecord {
|
|
|
94
148
|
* Format output
|
|
95
149
|
* @param mode
|
|
96
150
|
*/
|
|
97
|
-
formatOutput(
|
|
151
|
+
formatOutput(_mode?: string | null): Record<string, unknown>;
|
|
98
152
|
/**
|
|
99
153
|
* Returns public data of the object with relations
|
|
100
154
|
* @param required_relations
|
|
@@ -115,4 +169,4 @@ declare class OrangeDatabaseModelRuntimeError extends OrangeDatabaseModelError {
|
|
|
115
169
|
declare class OrangeDatabaseModelAccessError extends OrangeDatabaseModelRuntimeError {
|
|
116
170
|
}
|
|
117
171
|
|
|
118
|
-
export { Model, OrangeDatabaseInputValidationError, OrangeDatabaseModelAccessError, OrangeDatabaseModelError, OrangeDatabaseModelRuntimeError, Model as default };
|
|
172
|
+
export { type IModelConstructor, Model, type ModelData, OrangeDatabaseInputValidationError, OrangeDatabaseModelAccessError, OrangeDatabaseModelError, OrangeDatabaseModelRuntimeError, Model as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,58 @@
|
|
|
1
|
-
import { ActiveRecord, SelectQuery, OrangeDatabaseInputError, OrangeDatabaseError } from 'orange-dragonfly-orm';
|
|
2
|
-
import { ODValidatorRulesSchema } from 'orange-dragonfly-validator';
|
|
1
|
+
import { ActiveRecord, SelectQuery, Relation, InsertQuery, IQueryBuilder, UpdateQuery, DeleteQuery, OrangeDatabaseInputError, OrangeDatabaseError } from 'orange-dragonfly-orm';
|
|
2
|
+
import { ODValidatorRulesSchema, ODValidatorRuleSchema } from 'orange-dragonfly-validator';
|
|
3
3
|
|
|
4
|
+
type ValidatorTSType<T extends string> = T extends 'string' ? string : T extends 'integer' | 'number' ? number : T extends 'boolean' ? boolean : T extends 'null' ? null : T extends 'object' ? Record<string, unknown> : T extends 'array' ? unknown[] : T extends 'function' ? (...args: unknown[]) => unknown : unknown;
|
|
5
|
+
type FieldTS<R extends ODValidatorRuleSchema> = R['type'] extends string ? ValidatorTSType<R['type']> : R['type'] extends readonly (infer U extends string)[] ? ValidatorTSType<U> : unknown;
|
|
6
|
+
/**
|
|
7
|
+
* Converts an `ODValidatorRulesSchema` to a TypeScript object type.
|
|
8
|
+
* Requires the schema to be declared with `as const satisfies ODValidatorRulesSchema`
|
|
9
|
+
* to preserve literal type information.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const schema = { name: { type: 'string', required: true } } as const satisfies ODValidatorRulesSchema
|
|
13
|
+
* class MyModel extends Model {
|
|
14
|
+
* static override get validation_rules() { return schema }
|
|
15
|
+
* declare data: ModelData<typeof schema>
|
|
16
|
+
* }
|
|
17
|
+
*/
|
|
18
|
+
type ModelData<S extends ODValidatorRulesSchema> = {
|
|
19
|
+
[K in Exclude<keyof S, '@' | '#' | '*'>]: S[K] extends ODValidatorRuleSchema ? FieldTS<S[K]> : never;
|
|
20
|
+
};
|
|
21
|
+
interface IModelConstructor {
|
|
22
|
+
new (data?: Record<string, unknown>): Model;
|
|
23
|
+
readonly id_key: string;
|
|
24
|
+
readonly table: string;
|
|
25
|
+
readonly name: string;
|
|
26
|
+
readonly special_fields: string[];
|
|
27
|
+
readonly available_relations: Record<string, Relation>;
|
|
28
|
+
readonly ignore_extra_fields: boolean;
|
|
29
|
+
readonly unique_keys: string[][];
|
|
30
|
+
readonly fulltext_indexes: string[][];
|
|
31
|
+
/** @deprecated Use `ignore_extra_fields` instead */
|
|
32
|
+
readonly IGNORE_EXTRA_FIELDS: boolean;
|
|
33
|
+
/** @deprecated Use `unique_keys` instead */
|
|
34
|
+
readonly UNIQUE_KEYS: string[][];
|
|
35
|
+
/** @deprecated Use `fulltext_indexes` instead */
|
|
36
|
+
readonly FULLTEXT_INDEXES: string[][];
|
|
37
|
+
readonly validation_rules: ODValidatorRulesSchema;
|
|
38
|
+
readonly restricted_for_output: string[];
|
|
39
|
+
readonly restricted_for_lookup: string[];
|
|
40
|
+
readonly restricted_for_create: string[];
|
|
41
|
+
readonly restricted_for_update: string[];
|
|
42
|
+
resetRegisteredModels(): void;
|
|
43
|
+
insertQuery(): InsertQuery;
|
|
44
|
+
selectQuery(include_deleted?: boolean): IQueryBuilder<Model>;
|
|
45
|
+
updateQuery(include_deleted?: boolean): UpdateQuery;
|
|
46
|
+
deleteQuery(include_deleted?: boolean): DeleteQuery;
|
|
47
|
+
loadRelations(objects: Model[], relations?: string[] | null): Promise<Model[]>;
|
|
48
|
+
find(id: unknown, include_deleted?: boolean): Promise<Model | null>;
|
|
49
|
+
all(include_deleted?: boolean): Promise<Model[]>;
|
|
50
|
+
lookupQuery(data: Record<string, unknown>, basicQuery?: SelectQuery | null): SelectQuery<Model>;
|
|
51
|
+
create(data: Record<string, unknown>): Promise<Model>;
|
|
52
|
+
findAndCheckAccessOrDie(id: unknown, user: unknown, mode?: string | null): Promise<Model>;
|
|
53
|
+
model(class_name: string): IModelConstructor;
|
|
54
|
+
registerModel(model_class: IModelConstructor): IModelConstructor;
|
|
55
|
+
}
|
|
4
56
|
declare class Model extends ActiveRecord {
|
|
5
57
|
private get _mCls();
|
|
6
58
|
static get ignore_extra_fields(): boolean;
|
|
@@ -49,12 +101,12 @@ declare class Model extends ActiveRecord {
|
|
|
49
101
|
* @param data
|
|
50
102
|
* @param basicQuery Query to be used for adding conditions. By default a new SelectQuery is created.
|
|
51
103
|
*/
|
|
52
|
-
static lookupQuery(data: Record<string, unknown>, basicQuery?: SelectQuery | null): SelectQuery
|
|
104
|
+
static lookupQuery<T extends Model>(this: abstract new (data?: Record<string, unknown>) => T, data: Record<string, unknown>, basicQuery?: SelectQuery | null): SelectQuery<T>;
|
|
53
105
|
/**
|
|
54
106
|
* Creates object
|
|
55
107
|
* @param data
|
|
56
108
|
*/
|
|
57
|
-
static create(data: Record<string, unknown>): Promise<
|
|
109
|
+
static create<T extends Model>(this: abstract new (data?: Record<string, unknown>) => T, data: Record<string, unknown>): Promise<T>;
|
|
58
110
|
/**
|
|
59
111
|
* Updates object
|
|
60
112
|
* @param data
|
|
@@ -73,19 +125,21 @@ declare class Model extends ActiveRecord {
|
|
|
73
125
|
* Validate object's data
|
|
74
126
|
*/
|
|
75
127
|
validate(): Promise<void>;
|
|
128
|
+
static model(class_name: string): IModelConstructor;
|
|
129
|
+
static registerModel(model_class: Parameters<typeof ActiveRecord.registerModel>[0]): typeof Model;
|
|
76
130
|
/**
|
|
77
131
|
* Returns object by ID if it exists and accessible by user
|
|
78
132
|
* @param id
|
|
79
133
|
* @param user
|
|
80
134
|
* @param mode
|
|
81
135
|
*/
|
|
82
|
-
static findAndCheckAccessOrDie(
|
|
136
|
+
static findAndCheckAccessOrDie<T extends Model>(this: abstract new (data?: Record<string, unknown>) => T, id: number, user: unknown, mode?: string | null): Promise<T>;
|
|
83
137
|
/**
|
|
84
138
|
* Returns whether the object is accessible by user
|
|
85
139
|
* @param user
|
|
86
140
|
* @param mode
|
|
87
141
|
*/
|
|
88
|
-
accessible(
|
|
142
|
+
accessible(_user: unknown, mode?: string | null): Promise<boolean>;
|
|
89
143
|
/**
|
|
90
144
|
* Returns public data of the object
|
|
91
145
|
*/
|
|
@@ -94,7 +148,7 @@ declare class Model extends ActiveRecord {
|
|
|
94
148
|
* Format output
|
|
95
149
|
* @param mode
|
|
96
150
|
*/
|
|
97
|
-
formatOutput(
|
|
151
|
+
formatOutput(_mode?: string | null): Record<string, unknown>;
|
|
98
152
|
/**
|
|
99
153
|
* Returns public data of the object with relations
|
|
100
154
|
* @param required_relations
|
|
@@ -115,4 +169,4 @@ declare class OrangeDatabaseModelRuntimeError extends OrangeDatabaseModelError {
|
|
|
115
169
|
declare class OrangeDatabaseModelAccessError extends OrangeDatabaseModelRuntimeError {
|
|
116
170
|
}
|
|
117
171
|
|
|
118
|
-
export { Model, OrangeDatabaseInputValidationError, OrangeDatabaseModelAccessError, OrangeDatabaseModelError, OrangeDatabaseModelRuntimeError, Model as default };
|
|
172
|
+
export { type IModelConstructor, Model, type ModelData, OrangeDatabaseInputValidationError, OrangeDatabaseModelAccessError, OrangeDatabaseModelError, OrangeDatabaseModelRuntimeError, Model as default };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/model.ts
|
|
2
2
|
import { ActiveRecord } from "orange-dragonfly-orm";
|
|
3
|
-
import { parse } from "orange-dragonfly-validator";
|
|
3
|
+
import { ODValidatorException, parse } from "orange-dragonfly-validator";
|
|
4
4
|
|
|
5
5
|
// src/errors.ts
|
|
6
6
|
import { OrangeDatabaseError, OrangeDatabaseInputError } from "orange-dragonfly-orm";
|
|
@@ -107,28 +107,39 @@ var Model = class extends ActiveRecord {
|
|
|
107
107
|
* @param basicQuery Query to be used for adding conditions. By default a new SelectQuery is created.
|
|
108
108
|
*/
|
|
109
109
|
static lookupQuery(data, basicQuery = null) {
|
|
110
|
-
const
|
|
111
|
-
const
|
|
110
|
+
const cls = this;
|
|
111
|
+
const rules = cls.validation_rules;
|
|
112
|
+
const q = basicQuery ?? cls.selectQuery();
|
|
112
113
|
const filtered_rules = {};
|
|
113
114
|
for (const field of Object.keys(data)) {
|
|
114
115
|
if (!Object.hasOwn(rules, field)) {
|
|
115
|
-
if (
|
|
116
|
+
if (cls.ignore_extra_fields) {
|
|
116
117
|
continue;
|
|
117
118
|
}
|
|
118
119
|
const ex = new OrangeDatabaseInputValidationError("Parameters error");
|
|
119
|
-
ex.info[field] = `Field "${field}" is not described for model ${
|
|
120
|
+
ex.info[field] = `Field "${field}" is not described for model ${cls.name}`;
|
|
120
121
|
throw ex;
|
|
121
122
|
}
|
|
122
|
-
if (
|
|
123
|
+
if (cls.restricted_for_lookup.includes(field)) {
|
|
123
124
|
const ex = new OrangeDatabaseInputValidationError("Parameters error");
|
|
124
|
-
ex.info[field] = `Field "${field}" is restricted for searching model ${
|
|
125
|
+
ex.info[field] = `Field "${field}" is restricted for searching model ${cls.name}`;
|
|
125
126
|
throw ex;
|
|
126
127
|
}
|
|
127
128
|
q.where(field, data[field]);
|
|
128
129
|
const rule = rules[field];
|
|
129
130
|
filtered_rules[field] = Array.isArray(data[field]) ? { type: "array", children: { "*": rule } } : rule;
|
|
130
131
|
}
|
|
131
|
-
|
|
132
|
+
try {
|
|
133
|
+
parse(filtered_rules, data, { strictMode: false });
|
|
134
|
+
} catch (e) {
|
|
135
|
+
if (e instanceof ODValidatorException) {
|
|
136
|
+
const message = `Model ${cls.name} lookup rules validation failed`;
|
|
137
|
+
console.error(message, e.info);
|
|
138
|
+
throw new OrangeDatabaseInputValidationError(message);
|
|
139
|
+
} else {
|
|
140
|
+
throw e;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
132
143
|
return q;
|
|
133
144
|
}
|
|
134
145
|
/**
|
|
@@ -136,20 +147,21 @@ var Model = class extends ActiveRecord {
|
|
|
136
147
|
* @param data
|
|
137
148
|
*/
|
|
138
149
|
static async create(data) {
|
|
139
|
-
const
|
|
150
|
+
const cls = this;
|
|
151
|
+
const rules = cls.validation_rules;
|
|
140
152
|
const new_data = {};
|
|
141
153
|
for (const field of Object.keys(data)) {
|
|
142
154
|
if (!Object.hasOwn(rules, field)) {
|
|
143
|
-
if (
|
|
155
|
+
if (cls.ignore_extra_fields) {
|
|
144
156
|
continue;
|
|
145
157
|
}
|
|
146
158
|
const ex = new OrangeDatabaseInputValidationError("Parameters error");
|
|
147
|
-
ex.info[field] = `Field "${field}" is not described for model ${
|
|
159
|
+
ex.info[field] = `Field "${field}" is not described for model ${cls.name}`;
|
|
148
160
|
throw ex;
|
|
149
161
|
}
|
|
150
|
-
if (
|
|
162
|
+
if (cls.restricted_for_create.includes(field)) {
|
|
151
163
|
const ex = new OrangeDatabaseInputValidationError("Parameters error");
|
|
152
|
-
ex.info[field] = `Field "${field}" is restricted for creating model ${
|
|
164
|
+
ex.info[field] = `Field "${field}" is restricted for creating model ${cls.name}`;
|
|
153
165
|
throw ex;
|
|
154
166
|
}
|
|
155
167
|
new_data[field] = data[field];
|
|
@@ -245,7 +257,21 @@ var Model = class extends ActiveRecord {
|
|
|
245
257
|
}
|
|
246
258
|
}
|
|
247
259
|
}
|
|
248
|
-
|
|
260
|
+
try {
|
|
261
|
+
this.data = parse(rules, this.data, { strictMode: false });
|
|
262
|
+
} catch (e) {
|
|
263
|
+
if (e instanceof ODValidatorException) {
|
|
264
|
+
const message = `Model ${cls.name} rules validation failed`;
|
|
265
|
+
console.error(message, e.info);
|
|
266
|
+
const ex = new OrangeDatabaseInputValidationError(message);
|
|
267
|
+
for (const [key, msgs] of Object.entries(e.info)) {
|
|
268
|
+
ex.info[key] = Array.isArray(msgs) ? msgs[0] : msgs;
|
|
269
|
+
}
|
|
270
|
+
throw ex;
|
|
271
|
+
} else {
|
|
272
|
+
throw e;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
249
275
|
const custom_validation_errors = await this.custom_validation();
|
|
250
276
|
if (custom_validation_errors && Object.keys(custom_validation_errors).length) {
|
|
251
277
|
const ex = new OrangeDatabaseInputValidationError("Validation failed");
|
|
@@ -263,12 +289,18 @@ var Model = class extends ActiveRecord {
|
|
|
263
289
|
}
|
|
264
290
|
}
|
|
265
291
|
}
|
|
266
|
-
if (relation_errors.length) {
|
|
267
|
-
const ex = new OrangeDatabaseInputValidationError(`Some relations of the ${cls.name} are not found`);
|
|
268
|
-
for (const param of relation_errors) ex.info[param] = "Parent object not found";
|
|
269
|
-
throw ex;
|
|
270
|
-
}
|
|
271
292
|
}
|
|
293
|
+
if (relation_errors.length) {
|
|
294
|
+
const ex = new OrangeDatabaseInputValidationError(`Some relations of the ${cls.name} are not found`);
|
|
295
|
+
for (const param of relation_errors) ex.info[param] = "Parent object not found";
|
|
296
|
+
throw ex;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
static model(class_name) {
|
|
300
|
+
return super.model(class_name);
|
|
301
|
+
}
|
|
302
|
+
static registerModel(model_class) {
|
|
303
|
+
return super.registerModel(model_class);
|
|
272
304
|
}
|
|
273
305
|
/**
|
|
274
306
|
* Returns object by ID if it exists and accessible by user
|
|
@@ -277,12 +309,13 @@ var Model = class extends ActiveRecord {
|
|
|
277
309
|
* @param mode
|
|
278
310
|
*/
|
|
279
311
|
static async findAndCheckAccessOrDie(id, user, mode = null) {
|
|
280
|
-
const
|
|
312
|
+
const cls = this;
|
|
313
|
+
const obj = await cls.find(id);
|
|
281
314
|
if (!obj) {
|
|
282
|
-
throw new OrangeDatabaseModelRuntimeError(`${
|
|
315
|
+
throw new OrangeDatabaseModelRuntimeError(`${cls.name} #${id} not found`);
|
|
283
316
|
}
|
|
284
317
|
if (!await obj.accessible(user, mode)) {
|
|
285
|
-
throw new OrangeDatabaseModelAccessError(`${
|
|
318
|
+
throw new OrangeDatabaseModelAccessError(`${cls.name} #${id} is not accessible${mode ? ` for ${mode}` : ""}`);
|
|
286
319
|
}
|
|
287
320
|
return obj;
|
|
288
321
|
}
|
|
@@ -291,7 +324,7 @@ var Model = class extends ActiveRecord {
|
|
|
291
324
|
* @param user
|
|
292
325
|
* @param mode
|
|
293
326
|
*/
|
|
294
|
-
async accessible(
|
|
327
|
+
async accessible(_user, mode = null) {
|
|
295
328
|
return mode === null;
|
|
296
329
|
}
|
|
297
330
|
/**
|
|
@@ -306,8 +339,7 @@ var Model = class extends ActiveRecord {
|
|
|
306
339
|
* Format output
|
|
307
340
|
* @param mode
|
|
308
341
|
*/
|
|
309
|
-
|
|
310
|
-
formatOutput(mode = null) {
|
|
342
|
+
formatOutput(_mode = null) {
|
|
311
343
|
return this.output;
|
|
312
344
|
}
|
|
313
345
|
/**
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/model.ts","../src/errors.ts"],"sourcesContent":["import { ActiveRecord, Relation, SelectQuery } from 'orange-dragonfly-orm'\nimport { parse, type ODValidatorRuleSchema, type ODValidatorRulesSchema } from 'orange-dragonfly-validator'\nimport {\n OrangeDatabaseInputValidationError,\n OrangeDatabaseModelAccessError,\n OrangeDatabaseModelError,\n OrangeDatabaseModelRuntimeError,\n} from './errors'\n\nexport default class Model extends ActiveRecord {\n private get _mCls(): typeof Model {\n return this.constructor as typeof Model\n }\n\n static get ignore_extra_fields(): boolean {\n return false\n }\n\n /**\n * Returns list of unique keys\n * @returns List of unique keys\n */\n static get unique_keys(): string[][] {\n return []\n }\n\n /**\n * Returns list of fulltext indexes\n * @returns List of fulltext indexes\n */\n static get fulltext_indexes(): string[][] {\n return []\n }\n\n /** @deprecated Use `ignore_extra_fields` instead */\n static get IGNORE_EXTRA_FIELDS(): boolean {\n return this.ignore_extra_fields\n }\n\n /** @deprecated Use `unique_keys` instead */\n static get UNIQUE_KEYS(): string[][] {\n return this.unique_keys\n }\n\n /** @deprecated Use `fulltext_indexes` instead */\n static get FULLTEXT_INDEXES(): string[][] {\n return this.fulltext_indexes\n }\n\n /**\n * Returns schema for the model (Orange Dragonfly Validator format)\n */\n static get validation_rules(): ODValidatorRulesSchema {\n return {\n id: {\n required: false,\n type: 'integer',\n min: 1,\n },\n }\n }\n\n /**\n * Overridden method of ActiveRecord. Returns special fields list based on validation rules\n */\n static override get special_fields(): string[] {\n const rules = this.validation_rules\n const fields: string[] = []\n for (const field of ['created_at', 'updated_at', 'deleted_at']) {\n if (Object.hasOwn(rules, field)) fields.push(field)\n }\n return fields\n }\n\n /**\n * Returns list of relations restricted for extended output\n */\n static get restricted_for_output(): string[] {\n return []\n }\n\n /**\n * Returns list of fields restricted for lookup\n */\n static get restricted_for_lookup(): string[] {\n return []\n }\n\n /**\n * Returns list of fields restricted for create method\n */\n static get restricted_for_create(): string[] {\n return ['id'].concat(this.special_fields)\n }\n\n /**\n * Returns list of fields restricted for update method\n */\n static get restricted_for_update(): string[] {\n return ['id'].concat(this.special_fields)\n }\n\n /**\n * Lookup method\n * @param data\n * @param basicQuery Query to be used for adding conditions. By default a new SelectQuery is created.\n */\n static lookupQuery(data: Record<string, unknown>, basicQuery: SelectQuery | null = null): SelectQuery {\n const rules = this.validation_rules\n const q = basicQuery ?? this.selectQuery()\n const filtered_rules: ODValidatorRulesSchema = {}\n for (const field of Object.keys(data)) {\n if (!Object.hasOwn(rules, field)) {\n if (this.ignore_extra_fields) {\n continue\n }\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\n ex.info[field] = `Field \"${field}\" is not described for model ${this.name}`\n throw ex\n }\n if (this.restricted_for_lookup.includes(field)) {\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\n ex.info[field] = `Field \"${field}\" is restricted for searching model ${this.name}`\n throw ex\n }\n q.where(field, data[field])\n const rule = rules[field] as ODValidatorRuleSchema\n filtered_rules[field] = Array.isArray(data[field]) ? { type: 'array', children: { '*': rule } } : rule\n }\n parse(filtered_rules, data, { strictMode: false })\n return q\n }\n\n /**\n * Creates object\n * @param data\n */\n static async create(data: Record<string, unknown>): Promise<Model> {\n const rules = this.validation_rules\n const new_data: Record<string, unknown> = {}\n for (const field of Object.keys(data)) {\n if (!Object.hasOwn(rules, field)) {\n if (this.ignore_extra_fields) {\n continue\n }\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\n ex.info[field] = `Field \"${field}\" is not described for model ${this.name}`\n throw ex\n }\n if (this.restricted_for_create.includes(field)) {\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\n ex.info[field] = `Field \"${field}\" is restricted for creating model ${this.name}`\n throw ex\n }\n new_data[field] = data[field]\n }\n const Cls = this as unknown as new (data?: Record<string, unknown>) => Model\n return (new Cls(new_data)).save()\n }\n\n /**\n * Updates object\n * @param data\n */\n async update(data: Record<string, unknown>): Promise<this> {\n if (!this.id) {\n throw new OrangeDatabaseModelRuntimeError('You can update saved object only')\n }\n const cls = this._mCls\n const rules = cls.validation_rules\n const new_data: Record<string, unknown> = {}\n for (const field of Object.keys(data)) {\n if (!Object.hasOwn(rules, field)) {\n if (cls.ignore_extra_fields) {\n continue\n }\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\n ex.info[field] = `Field \"${field}\" is not described for model ${cls.name}`\n throw ex\n }\n if (cls.restricted_for_update.includes(field)) {\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\n ex.info[field] = `Field \"${field}\" is restricted for updating model ${cls.name}`\n throw ex\n }\n new_data[field] = data[field]\n }\n return this.save(new_data)\n }\n\n /**\n * Checks uniqueness of the object based on unique_keys\n */\n async checkUniqueness(exception_mode = false, ignore_null = false): Promise<boolean> {\n const cls = this._mCls\n for (const fields of cls.unique_keys) {\n if (!await this.isUnique(fields, ignore_null)) {\n if (exception_mode) {\n const ex = new OrangeDatabaseInputValidationError('Object is not unique')\n for (const field of fields) {\n ex.info[field] = 'Part of the unique key'\n }\n throw ex\n }\n return false\n }\n }\n return true\n }\n\n protected override async _preSave(_is_new?: boolean): Promise<void> {\n const cls = this._mCls\n if (cls.ignore_extra_fields) {\n const rules = cls.validation_rules\n for (const key of Object.keys(this.data).filter(v => !Object.hasOwn(rules, v))) {\n delete this.data[key]\n }\n }\n await super._preSave(_is_new)\n await this.checkUniqueness(true, true)\n await this.validate()\n }\n\n /**\n * Performs custom validation - returns null in case of success or an object of validation issues\n */\n async custom_validation(): Promise<Record<string, string> | null> {\n return null\n }\n\n /**\n * Validate object's data\n */\n async validate(): Promise<void> {\n const cls = this._mCls\n const rules = cls.validation_rules\n if (!rules) {\n throw new OrangeDatabaseModelError(`Validation rules are not defined for model ${cls.name}`)\n }\n for (const rule_name of Object.keys(rules)) {\n if (Object.hasOwn(this.data, rule_name)) {\n const rule = rules[rule_name] as ODValidatorRuleSchema\n if (rule?.type) {\n const types = Array.isArray(rule.type) ? (rule.type as string[]) : [rule.type as string]\n if (types.includes('boolean')) {\n if ((this.data[rule_name] === 1) || (this.data[rule_name] === 0)) {\n this.data[rule_name] = this.data[rule_name] === 1\n }\n }\n }\n }\n }\n this.data = parse(rules, this.data, { strictMode: false })\n const custom_validation_errors = await this.custom_validation()\n if (custom_validation_errors && Object.keys(custom_validation_errors).length) {\n const ex = new OrangeDatabaseInputValidationError('Validation failed')\n for (const [param, message] of Object.entries(custom_validation_errors)) ex.info[param] = message\n throw ex\n }\n const relation_errors: string[] = []\n for (const rel_name of Object.keys(cls.available_relations)) {\n const rel: Relation = cls.available_relations[rel_name]\n if (rel.mode === 'parent') {\n const aKey = (rel as unknown as { _a_key_by_mode: string })._a_key_by_mode\n if (Object.hasOwn(this.data, aKey) && (this.data[aKey] !== null) && (this.data[aKey] !== 0)) {\n if ((await this.rel(rel_name, true) as Model | null) === null) {\n relation_errors.push(aKey)\n }\n }\n }\n if (relation_errors.length) {\n const ex = new OrangeDatabaseInputValidationError(`Some relations of the ${cls.name} are not found`)\n for (const param of relation_errors) ex.info[param] = 'Parent object not found'\n throw ex\n }\n }\n }\n\n /**\n * Returns object by ID if it exists and accessible by user\n * @param id\n * @param user\n * @param mode\n */\n static async findAndCheckAccessOrDie(id: unknown, user: unknown, mode: string | null = null): Promise<Model> {\n const obj = await this.find(id) as Model | null\n if (!obj) {\n throw new OrangeDatabaseModelRuntimeError(`${this.name} #${id} not found`)\n }\n if (!(await obj.accessible(user, mode))) {\n throw new OrangeDatabaseModelAccessError(`${this.name} #${id} is not accessible${mode ? ` for ${mode}` : ''}`)\n }\n return obj\n }\n\n /**\n * Returns whether the object is accessible by user\n * @param user\n * @param mode\n */\n \n async accessible(user: unknown, mode: string | null = null): Promise<boolean> {\n return mode === null\n }\n\n /**\n * Returns public data of the object\n */\n get output(): Record<string, unknown> {\n return {\n id: this.id,\n }\n }\n\n /**\n * Format output\n * @param mode\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n formatOutput(mode: string | null = null): Record<string, unknown> {\n return this.output\n }\n\n /**\n * Returns public data of the object with relations\n * @param required_relations\n * @param mode\n */\n async getExtendedOutput(required_relations: string[] = [], mode: string | null = null): Promise<Record<string, unknown>> {\n const output = this.formatOutput(mode)\n const cls = this._mCls\n for (const name of required_relations) {\n if (name.split(':').length > 1) continue\n if (cls.restricted_for_output.includes(name)) {\n throw new OrangeDatabaseModelError(`Relation \"${name}\" is not allowed for extended output of model ${cls.name}`)\n }\n const rel_data = await this.rel(name) as Model | Model[] | null\n const rel_mode = `relation:${cls.name}.${name}`\n const rel_relations = required_relations.filter(v => v.startsWith(`${name}:`)).map(v => v.slice(name.length + 1))\n output[`:${name}`] = Array.isArray(rel_data)\n ? await Promise.all(rel_data.map(v => v.getExtendedOutput(rel_relations, rel_mode)))\n : (rel_data ? await rel_data.getExtendedOutput(rel_relations, rel_mode) : null)\n }\n return output\n }\n}\n","import { OrangeDatabaseError, OrangeDatabaseInputError } from 'orange-dragonfly-orm'\n\nexport class OrangeDatabaseInputValidationError extends OrangeDatabaseInputError {\n private _info: Record<string, string> = {}\n\n get info(): Record<string, string> {\n return this._info\n }\n\n set info(v: Record<string, string>) {\n this._info = v\n }\n}\n\nexport class OrangeDatabaseModelError extends OrangeDatabaseError {}\n\nexport class OrangeDatabaseModelRuntimeError extends OrangeDatabaseModelError {}\n\nexport class OrangeDatabaseModelAccessError extends OrangeDatabaseModelRuntimeError {}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/model.ts","../src/errors.ts"],"sourcesContent":["import { ActiveRecord, type DeleteQuery, type InsertQuery, type IQueryBuilder, type Relation, SelectQuery, type UpdateQuery } from 'orange-dragonfly-orm'\r\nimport { ODValidatorException, parse, type ODValidatorRuleSchema, type ODValidatorRulesSchema } from 'orange-dragonfly-validator'\r\n\r\ntype ValidatorTSType<T extends string> =\r\n T extends 'string' ? string :\r\n T extends 'integer' | 'number' ? number :\r\n T extends 'boolean' ? boolean :\r\n T extends 'null' ? null :\r\n T extends 'object' ? Record<string, unknown> :\r\n T extends 'array' ? unknown[] :\r\n T extends 'function' ? (...args: unknown[]) => unknown :\r\n unknown\r\n\r\ntype FieldTS<R extends ODValidatorRuleSchema> =\r\n R['type'] extends string\r\n ? ValidatorTSType<R['type']>\r\n : R['type'] extends readonly (infer U extends string)[]\r\n ? ValidatorTSType<U>\r\n : unknown\r\n\r\n/**\r\n * Converts an `ODValidatorRulesSchema` to a TypeScript object type.\r\n * Requires the schema to be declared with `as const satisfies ODValidatorRulesSchema`\r\n * to preserve literal type information.\r\n *\r\n * @example\r\n * const schema = { name: { type: 'string', required: true } } as const satisfies ODValidatorRulesSchema\r\n * class MyModel extends Model {\r\n * static override get validation_rules() { return schema }\r\n * declare data: ModelData<typeof schema>\r\n * }\r\n */\r\nexport type ModelData<S extends ODValidatorRulesSchema> = {\r\n [K in Exclude<keyof S, '@' | '#' | '*'>]: S[K] extends ODValidatorRuleSchema ? FieldTS<S[K]> : never\r\n}\r\n\r\nimport {\r\n OrangeDatabaseInputValidationError,\r\n OrangeDatabaseModelAccessError,\r\n OrangeDatabaseModelError,\r\n OrangeDatabaseModelRuntimeError,\r\n} from './errors'\r\n\r\nexport interface IModelConstructor {\r\n new(data?: Record<string, unknown>): Model\r\n readonly id_key: string\r\n readonly table: string\r\n readonly name: string\r\n readonly special_fields: string[]\r\n readonly available_relations: Record<string, Relation>\r\n readonly ignore_extra_fields: boolean\r\n readonly unique_keys: string[][]\r\n readonly fulltext_indexes: string[][]\r\n /** @deprecated Use `ignore_extra_fields` instead */\r\n readonly IGNORE_EXTRA_FIELDS: boolean\r\n /** @deprecated Use `unique_keys` instead */\r\n readonly UNIQUE_KEYS: string[][]\r\n /** @deprecated Use `fulltext_indexes` instead */\r\n readonly FULLTEXT_INDEXES: string[][]\r\n readonly validation_rules: ODValidatorRulesSchema\r\n readonly restricted_for_output: string[]\r\n readonly restricted_for_lookup: string[]\r\n readonly restricted_for_create: string[]\r\n readonly restricted_for_update: string[]\r\n resetRegisteredModels(): void\r\n insertQuery(): InsertQuery\r\n selectQuery(include_deleted?: boolean): IQueryBuilder<Model>\r\n updateQuery(include_deleted?: boolean): UpdateQuery\r\n deleteQuery(include_deleted?: boolean): DeleteQuery\r\n loadRelations(objects: Model[], relations?: string[] | null): Promise<Model[]>\r\n find(id: unknown, include_deleted?: boolean): Promise<Model | null>\r\n all(include_deleted?: boolean): Promise<Model[]>\r\n lookupQuery(data: Record<string, unknown>, basicQuery?: SelectQuery | null): SelectQuery<Model>\r\n create(data: Record<string, unknown>): Promise<Model>\r\n findAndCheckAccessOrDie(id: unknown, user: unknown, mode?: string | null): Promise<Model>\r\n model(class_name: string): IModelConstructor\r\n registerModel(model_class: IModelConstructor): IModelConstructor\r\n}\r\n\r\nexport default class Model extends ActiveRecord {\r\n private get _mCls(): typeof Model {\r\n return this.constructor as typeof Model\r\n }\r\n\r\n static get ignore_extra_fields(): boolean {\r\n return false\r\n }\r\n\r\n /**\r\n * Returns list of unique keys\r\n * @returns List of unique keys\r\n */\r\n static get unique_keys(): string[][] {\r\n return []\r\n }\r\n\r\n /**\r\n * Returns list of fulltext indexes\r\n * @returns List of fulltext indexes\r\n */\r\n static get fulltext_indexes(): string[][] {\r\n return []\r\n }\r\n\r\n /** @deprecated Use `ignore_extra_fields` instead */\r\n static get IGNORE_EXTRA_FIELDS(): boolean {\r\n return this.ignore_extra_fields\r\n }\r\n\r\n /** @deprecated Use `unique_keys` instead */\r\n static get UNIQUE_KEYS(): string[][] {\r\n return this.unique_keys\r\n }\r\n\r\n /** @deprecated Use `fulltext_indexes` instead */\r\n static get FULLTEXT_INDEXES(): string[][] {\r\n return this.fulltext_indexes\r\n }\r\n\r\n /**\r\n * Returns schema for the model (Orange Dragonfly Validator format)\r\n */\r\n static get validation_rules(): ODValidatorRulesSchema {\r\n return {\r\n id: {\r\n required: false,\r\n type: 'integer',\r\n min: 1,\r\n },\r\n }\r\n }\r\n\r\n /**\r\n * Overridden method of ActiveRecord. Returns special fields list based on validation rules\r\n */\r\n static override get special_fields(): string[] {\r\n const rules = this.validation_rules\r\n const fields: string[] = []\r\n for (const field of ['created_at', 'updated_at', 'deleted_at']) {\r\n if (Object.hasOwn(rules, field)) fields.push(field)\r\n }\r\n return fields\r\n }\r\n\r\n /**\r\n * Returns list of relations restricted for extended output\r\n */\r\n static get restricted_for_output(): string[] {\r\n return []\r\n }\r\n\r\n /**\r\n * Returns list of fields restricted for lookup\r\n */\r\n static get restricted_for_lookup(): string[] {\r\n return []\r\n }\r\n\r\n /**\r\n * Returns list of fields restricted for create method\r\n */\r\n static get restricted_for_create(): string[] {\r\n return ['id'].concat(this.special_fields)\r\n }\r\n\r\n /**\r\n * Returns list of fields restricted for update method\r\n */\r\n static get restricted_for_update(): string[] {\r\n return ['id'].concat(this.special_fields)\r\n }\r\n\r\n /**\r\n * Lookup method\r\n * @param data\r\n * @param basicQuery Query to be used for adding conditions. By default a new SelectQuery is created.\r\n */\r\n static lookupQuery<T extends Model>(\r\n this: abstract new (data?: Record<string, unknown>) => T,\r\n data: Record<string, unknown>,\r\n basicQuery: SelectQuery | null = null,\r\n ): SelectQuery<T> {\r\n const cls = this as unknown as typeof Model\r\n const rules = cls.validation_rules\r\n const q = (basicQuery ?? cls.selectQuery()) as SelectQuery<T>\r\n const filtered_rules: ODValidatorRulesSchema = {}\r\n for (const field of Object.keys(data)) {\r\n if (!Object.hasOwn(rules, field)) {\r\n if (cls.ignore_extra_fields) {\r\n continue\r\n }\r\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\r\n ex.info[field] = `Field \"${field}\" is not described for model ${cls.name}`\r\n throw ex\r\n }\r\n if (cls.restricted_for_lookup.includes(field)) {\r\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\r\n ex.info[field] = `Field \"${field}\" is restricted for searching model ${cls.name}`\r\n throw ex\r\n }\r\n q.where(field, data[field])\r\n const rule = rules[field] as ODValidatorRuleSchema\r\n filtered_rules[field] = Array.isArray(data[field]) ? { type: 'array', children: { '*': rule } } : rule\r\n }\r\n try {\r\n parse(filtered_rules, data, { strictMode: false })\r\n } catch (e) {\r\n if (e instanceof ODValidatorException) {\r\n const message = `Model ${cls.name} lookup rules validation failed`\r\n console.error(message, e.info)\r\n throw new OrangeDatabaseInputValidationError(message)\r\n } else {\r\n throw e\r\n }\r\n }\r\n return q\r\n }\r\n\r\n /**\r\n * Creates object\r\n * @param data\r\n */\r\n static async create<T extends Model>(\r\n this: abstract new (data?: Record<string, unknown>) => T,\r\n data: Record<string, unknown>,\r\n ): Promise<T> {\r\n const cls = this as unknown as typeof Model\r\n const rules = cls.validation_rules\r\n const new_data: Record<string, unknown> = {}\r\n for (const field of Object.keys(data)) {\r\n if (!Object.hasOwn(rules, field)) {\r\n if (cls.ignore_extra_fields) {\r\n continue\r\n }\r\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\r\n ex.info[field] = `Field \"${field}\" is not described for model ${cls.name}`\r\n throw ex\r\n }\r\n if (cls.restricted_for_create.includes(field)) {\r\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\r\n ex.info[field] = `Field \"${field}\" is restricted for creating model ${cls.name}`\r\n throw ex\r\n }\r\n new_data[field] = data[field]\r\n }\r\n const Cls = this as unknown as new (data?: Record<string, unknown>) => T\r\n return (new Cls(new_data)).save()\r\n }\r\n\r\n /**\r\n * Updates object\r\n * @param data\r\n */\r\n async update(data: Record<string, unknown>): Promise<this> {\r\n if (!this.id) {\r\n throw new OrangeDatabaseModelRuntimeError('You can update saved object only')\r\n }\r\n const cls = this._mCls\r\n const rules = cls.validation_rules\r\n const new_data: Record<string, unknown> = {}\r\n for (const field of Object.keys(data)) {\r\n if (!Object.hasOwn(rules, field)) {\r\n if (cls.ignore_extra_fields) {\r\n continue\r\n }\r\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\r\n ex.info[field] = `Field \"${field}\" is not described for model ${cls.name}`\r\n throw ex\r\n }\r\n if (cls.restricted_for_update.includes(field)) {\r\n const ex = new OrangeDatabaseInputValidationError('Parameters error')\r\n ex.info[field] = `Field \"${field}\" is restricted for updating model ${cls.name}`\r\n throw ex\r\n }\r\n new_data[field] = data[field]\r\n }\r\n return this.save(new_data)\r\n }\r\n\r\n /**\r\n * Checks uniqueness of the object based on unique_keys\r\n */\r\n async checkUniqueness(exception_mode = false, ignore_null = false): Promise<boolean> {\r\n const cls = this._mCls\r\n for (const fields of cls.unique_keys) {\r\n if (!await this.isUnique(fields, ignore_null)) {\r\n if (exception_mode) {\r\n const ex = new OrangeDatabaseInputValidationError('Object is not unique')\r\n for (const field of fields) {\r\n ex.info[field] = 'Part of the unique key'\r\n }\r\n throw ex\r\n }\r\n return false\r\n }\r\n }\r\n return true\r\n }\r\n\r\n protected override async _preSave(_is_new?: boolean): Promise<void> {\r\n const cls = this._mCls\r\n if (cls.ignore_extra_fields) {\r\n const rules = cls.validation_rules\r\n for (const key of Object.keys(this.data).filter(v => !Object.hasOwn(rules, v))) {\r\n delete this.data[key]\r\n }\r\n }\r\n await super._preSave(_is_new)\r\n await this.checkUniqueness(true, true)\r\n await this.validate()\r\n }\r\n\r\n /**\r\n * Performs custom validation - returns null in case of success or an object of validation issues\r\n */\r\n async custom_validation(): Promise<Record<string, string> | null> {\r\n return null\r\n }\r\n\r\n /**\r\n * Validate object's data\r\n */\r\n async validate(): Promise<void> {\r\n const cls = this._mCls\r\n const rules = cls.validation_rules\r\n if (!rules) {\r\n throw new OrangeDatabaseModelError(`Validation rules are not defined for model ${cls.name}`)\r\n }\r\n for (const rule_name of Object.keys(rules)) {\r\n if (Object.hasOwn(this.data, rule_name)) {\r\n const rule = rules[rule_name] as ODValidatorRuleSchema\r\n if (rule?.type) {\r\n const types = Array.isArray(rule.type) ? (rule.type as string[]) : [rule.type as string]\r\n if (types.includes('boolean')) {\r\n if ((this.data[rule_name] === 1) || (this.data[rule_name] === 0)) {\r\n this.data[rule_name] = this.data[rule_name] === 1\r\n }\r\n }\r\n }\r\n }\r\n }\r\n try {\r\n this.data = parse(rules, this.data, { strictMode: false })\r\n } catch (e) {\r\n if (e instanceof ODValidatorException) {\r\n const message = `Model ${cls.name} rules validation failed`\r\n console.error(message, e.info)\r\n const ex = new OrangeDatabaseInputValidationError(message)\r\n for (const [key, msgs] of Object.entries(e.info as Record<string, string | string[]>)) {\r\n ex.info[key] = Array.isArray(msgs) ? msgs[0] : msgs\r\n }\r\n throw ex\r\n } else {\r\n throw e\r\n }\r\n }\r\n const custom_validation_errors = await this.custom_validation()\r\n if (custom_validation_errors && Object.keys(custom_validation_errors).length) {\r\n const ex = new OrangeDatabaseInputValidationError('Validation failed')\r\n for (const [param, message] of Object.entries(custom_validation_errors)) ex.info[param] = message\r\n throw ex\r\n }\r\n const relation_errors: string[] = []\r\n for (const rel_name of Object.keys(cls.available_relations)) {\r\n const rel: Relation = cls.available_relations[rel_name]\r\n if (rel.mode === 'parent') {\r\n const aKey = (rel as unknown as { _a_key_by_mode: string })._a_key_by_mode\r\n if (Object.hasOwn(this.data, aKey) && (this.data[aKey] !== null) && (this.data[aKey] !== 0)) {\r\n if ((await this.rel(rel_name, true) as Model | null) === null) {\r\n relation_errors.push(aKey)\r\n }\r\n }\r\n }\r\n }\r\n if (relation_errors.length) {\r\n const ex = new OrangeDatabaseInputValidationError(`Some relations of the ${cls.name} are not found`)\r\n for (const param of relation_errors) ex.info[param] = 'Parent object not found'\r\n throw ex\r\n }\r\n }\r\n\r\n static override model(class_name: string): IModelConstructor {\r\n return super.model(class_name) as unknown as IModelConstructor\r\n }\r\n\r\n static override registerModel(model_class: Parameters<typeof ActiveRecord.registerModel>[0]): typeof Model {\r\n return super.registerModel(model_class) as unknown as typeof Model\r\n }\r\n\r\n /**\r\n * Returns object by ID if it exists and accessible by user\r\n * @param id\r\n * @param user\r\n * @param mode\r\n */\r\n static async findAndCheckAccessOrDie<T extends Model>(\r\n this: abstract new (data?: Record<string, unknown>) => T,\r\n id: number,\r\n user: unknown,\r\n mode: string | null = null,\r\n ): Promise<T> {\r\n const cls = this as unknown as typeof Model\r\n const obj = await cls.find(id) as T | null\r\n if (!obj) {\r\n throw new OrangeDatabaseModelRuntimeError(`${cls.name} #${id} not found`)\r\n }\r\n if (!(await obj.accessible(user, mode))) {\r\n throw new OrangeDatabaseModelAccessError(`${cls.name} #${id} is not accessible${mode ? ` for ${mode}` : ''}`)\r\n }\r\n return obj\r\n }\r\n\r\n /**\r\n * Returns whether the object is accessible by user\r\n * @param user\r\n * @param mode\r\n */\r\n \r\n async accessible(_user: unknown, mode: string | null = null): Promise<boolean> {\r\n return mode === null\r\n }\r\n\r\n /**\r\n * Returns public data of the object\r\n */\r\n get output(): Record<string, unknown> {\r\n return {\r\n id: this.id,\r\n }\r\n }\r\n\r\n /**\r\n * Format output\r\n * @param mode\r\n */\r\n formatOutput(_mode: string | null = null): Record<string, unknown> {\r\n return this.output\r\n }\r\n\r\n /**\r\n * Returns public data of the object with relations\r\n * @param required_relations\r\n * @param mode\r\n */\r\n async getExtendedOutput(required_relations: string[] = [], mode: string | null = null): Promise<Record<string, unknown>> {\r\n const output = this.formatOutput(mode)\r\n const cls = this._mCls\r\n for (const name of required_relations) {\r\n if (name.split(':').length > 1) continue\r\n if (cls.restricted_for_output.includes(name)) {\r\n throw new OrangeDatabaseModelError(`Relation \"${name}\" is not allowed for extended output of model ${cls.name}`)\r\n }\r\n const rel_data = await this.rel(name) as Model | Model[] | null\r\n const rel_mode = `relation:${cls.name}.${name}`\r\n const rel_relations = required_relations.filter(v => v.startsWith(`${name}:`)).map(v => v.slice(name.length + 1))\r\n output[`:${name}`] = Array.isArray(rel_data)\r\n ? await Promise.all(rel_data.map(v => v.getExtendedOutput(rel_relations, rel_mode)))\r\n : (rel_data ? await rel_data.getExtendedOutput(rel_relations, rel_mode) : null)\r\n }\r\n return output\r\n }\r\n}\r\n","import { OrangeDatabaseError, OrangeDatabaseInputError } from 'orange-dragonfly-orm'\n\nexport class OrangeDatabaseInputValidationError extends OrangeDatabaseInputError {\n private _info: Record<string, string> = {}\n\n get info(): Record<string, string> {\n return this._info\n }\n\n set info(v: Record<string, string>) {\n this._info = v\n }\n}\n\nexport class OrangeDatabaseModelError extends OrangeDatabaseError {}\n\nexport class OrangeDatabaseModelRuntimeError extends OrangeDatabaseModelError {}\n\nexport class OrangeDatabaseModelAccessError extends OrangeDatabaseModelRuntimeError {}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orange-dragonfly-model",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Orange Dragonfly Model - advanced active record ORM model class for Orange Dragonfly framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
},
|
|
53
53
|
"homepage": "https://github.com/charger88/orange-dragonfly-model#readme",
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"orange-dragonfly-orm": "
|
|
55
|
+
"orange-dragonfly-orm": "file:./../orange-dragonfly-db",
|
|
56
56
|
"orange-dragonfly-validator": "^1.0.2"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|