@wbce-d9/api 9.26.3 → 9.26.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,6 +12,6 @@ export declare class AuthorizationService {
12
12
  /**
13
13
  * Checks if the provided payload matches the configured permissions, and adds the presets to the payload.
14
14
  */
15
- validatePayload(action: PermissionsAction, collection: string, data: Partial<Item>): Partial<Item>;
15
+ validatePayload(action: PermissionsAction, collection: string, data: Partial<Item>, keys?: PrimaryKey[]): Promise<Partial<Item>>;
16
16
  checkAccess(action: PermissionsAction, collection: string, pk: PrimaryKey | PrimaryKey[]): Promise<void>;
17
17
  }
@@ -1,9 +1,9 @@
1
1
  import { FailedValidationException } from '@wbce-d9/exceptions';
2
2
  import { validatePayload } from '@wbce-d9/utils';
3
- import { cloneDeep, flatten, isArray, isNil, merge, reduce, uniq, uniqWith } from 'lodash-es';
3
+ import { cloneDeep, flatten, isArray, isNil, merge, reduce, uniq, uniqWith, clone } from 'lodash-es';
4
4
  import { GENERATE_SPECIAL } from '../constants.js';
5
5
  import getDatabase from '../database/index.js';
6
- import { ForbiddenException } from '../exceptions/index.js';
6
+ import { ForbiddenException, UnprocessableEntityException } from '../exceptions/index.js';
7
7
  import { getRelationInfo } from '../utils/get-relation-info.js';
8
8
  import { stripFunction } from '../utils/strip-function.js';
9
9
  import { ItemsService } from './items.js';
@@ -336,7 +336,7 @@ export class AuthorizationService {
336
336
  /**
337
337
  * Checks if the provided payload matches the configured permissions, and adds the presets to the payload.
338
338
  */
339
- validatePayload(action, collection, data) {
339
+ async validatePayload(action, collection, data, keys) {
340
340
  const payload = cloneDeep(data);
341
341
  let permission;
342
342
  if (this.accountability?.admin === true) {
@@ -368,14 +368,13 @@ export class AuthorizationService {
368
368
  }
369
369
  }
370
370
  const preset = permission.presets ?? {};
371
+ const fields = Object.values(this.schema.collections[collection].fields);
371
372
  const payloadWithPresets = merge({}, preset, payload);
372
- const fieldValidationRules = Object.values(this.schema.collections[collection].fields)
373
- .map((field) => field.validation)
374
- .filter((v) => v);
373
+ const fieldValidationRules = fields.map((field) => field.validation).filter((v) => v);
375
374
  const hasValidationRules = isNil(permission.validation) === false && Object.keys(permission.validation ?? {}).length > 0;
376
375
  const hasFieldValidationRules = fieldValidationRules && fieldValidationRules.length > 0;
377
376
  const requiredColumns = [];
378
- for (const field of Object.values(this.schema.collections[collection].fields)) {
377
+ for (const field of fields) {
379
378
  const specials = field?.special ?? [];
380
379
  const hasGenerateSpecial = GENERATE_SPECIAL.some((name) => specials.includes(name));
381
380
  const nullable = field.nullable || hasGenerateSpecial || field.generated;
@@ -412,7 +411,64 @@ export class AuthorizationService {
412
411
  }
413
412
  }
414
413
  const validationErrors = [];
415
- validationErrors.push(...flatten(validatePayload(permission.validation, payloadWithPresets).map((error) => error.details.map((details) => new FailedValidationException(details)))));
414
+ const payloadWithPresetsFlattened = clone(payloadWithPresets);
415
+ // In case the object has O2M relations, refactor it's arrays validation can 'count' on them
416
+ const relations = this.schema.relations.filter((relation) => {
417
+ return relation.related_collection === collection;
418
+ });
419
+ // Filter out 02M collections
420
+ const relationsToProcess = relations.filter((relation) => {
421
+ if (!relation.meta?.one_field)
422
+ return false;
423
+ return relation.meta.one_field in payloadWithPresets;
424
+ });
425
+ for (const relation of relationsToProcess) {
426
+ // Nested array of individual items
427
+ const fieldKey = relation.meta.one_field;
428
+ const field = payloadWithPresets[fieldKey];
429
+ if (!field || Array.isArray(field)) {
430
+ // if the field is already an array or is empty we do nothing
431
+ continue;
432
+ }
433
+ // Object w/ create/update/delete
434
+ // This is horribly intricated because this functions can apply to many items on updates
435
+ else {
436
+ // lets add object in create and update
437
+ const arrayToValidate = field.create.concat(field.update);
438
+ if (action == 'update') {
439
+ if (typeof keys === 'undefined') {
440
+ throw new UnprocessableEntityException('keys are required when updating an object');
441
+ }
442
+ else {
443
+ // get the number of items already linked to the object
444
+ const mainItemService = new ItemsService(collection, {
445
+ accountability: this.accountability,
446
+ knex: this.knex,
447
+ schema: this.schema,
448
+ });
449
+ const originalItems = await mainItemService.readMany(keys);
450
+ for (let i = 0; i < originalItems.length; i++) {
451
+ const originalItem = originalItems[i];
452
+ const emptyItemsToAdd = originalItem[fieldKey].length - field.update?.length - field.delete?.length;
453
+ const finalArrayToValidate = arrayToValidate.concat(Array(emptyItemsToAdd).fill({}));
454
+ // validate partial items until the last key
455
+ if (i == keys.length - 1) {
456
+ payloadWithPresetsFlattened[fieldKey] = finalArrayToValidate;
457
+ }
458
+ else {
459
+ const partialItemToValidate = { fieldKey: finalArrayToValidate };
460
+ validationErrors.push(...flatten(validatePayload(permission.validation, partialItemToValidate).map((error) => error.details.map((details) => new FailedValidationException(details)))));
461
+ }
462
+ }
463
+ }
464
+ }
465
+ else {
466
+ // set the payload to validate as a well formated array
467
+ payloadWithPresetsFlattened[fieldKey] = arrayToValidate;
468
+ }
469
+ }
470
+ }
471
+ validationErrors.push(...flatten(validatePayload(permission.validation, payloadWithPresetsFlattened).map((error) => error.details.map((details) => new FailedValidationException(details)))));
416
472
  if (validationErrors.length > 0)
417
473
  throw validationErrors;
418
474
  return payloadWithPresets;
@@ -105,7 +105,7 @@ export class ItemsService {
105
105
  })
106
106
  : payload;
107
107
  const payloadWithPresets = this.accountability
108
- ? authorizationService.validatePayload('create', this.collection, payloadAfterHooks)
108
+ ? await authorizationService.validatePayload('create', this.collection, payloadAfterHooks)
109
109
  : payloadAfterHooks;
110
110
  if (opts.preMutationException) {
111
111
  throw opts.preMutationException;
@@ -451,7 +451,7 @@ export class ItemsService {
451
451
  await authorizationService.checkAccess('update', this.collection, keys);
452
452
  }
453
453
  const payloadWithPresets = this.accountability
454
- ? authorizationService.validatePayload('update', this.collection, payloadAfterHooks)
454
+ ? await authorizationService.validatePayload('update', this.collection, payloadAfterHooks, keys)
455
455
  : payloadAfterHooks;
456
456
  if (opts.preMutationException) {
457
457
  throw opts.preMutationException;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wbce-d9/api",
3
- "version": "9.26.3",
3
+ "version": "9.26.4",
4
4
  "description": "Directus is a real-time API and App dashboard for managing SQL database content",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -143,20 +143,20 @@
143
143
  "uuid-validate": "0.0.3",
144
144
  "vm2": "^3.9.19",
145
145
  "wellknown": "0.5.0",
146
+ "@wbce-d9/app": "9.26.4",
147
+ "@wbce-d9/constants": "9.26.3",
146
148
  "@wbce-d9/exceptions": "9.26.3",
149
+ "@wbce-d9/extensions-sdk": "9.26.4",
147
150
  "@wbce-d9/schema": "9.26.3",
148
151
  "@wbce-d9/specs": "9.26.3",
149
- "@wbce-d9/app": "9.26.3",
150
- "@wbce-d9/constants": "9.26.3",
151
- "@wbce-d9/extensions-sdk": "9.26.3",
152
- "@wbce-d9/storage-driver-azure": "9.26.3",
153
152
  "@wbce-d9/storage": "9.26.3",
154
- "@wbce-d9/storage-driver-gcs": "9.26.3",
155
- "@wbce-d9/storage-driver-cloudinary": "9.26.3",
156
- "@wbce-d9/storage-driver-s3": "9.26.3",
157
- "@wbce-d9/storage-driver-local": "9.26.3",
153
+ "@wbce-d9/storage-driver-azure": "9.26.4",
154
+ "@wbce-d9/storage-driver-cloudinary": "9.26.4",
155
+ "@wbce-d9/storage-driver-gcs": "9.26.4",
156
+ "@wbce-d9/storage-driver-local": "9.26.4",
157
+ "@wbce-d9/storage-driver-s3": "9.26.4",
158
158
  "@wbce-d9/update-check": "9.26.3",
159
- "@wbce-d9/utils": "9.26.3"
159
+ "@wbce-d9/utils": "9.27.0"
160
160
  },
161
161
  "devDependencies": {
162
162
  "@ngneat/falso": "6.4.0",