@expo/entity 0.25.3 → 0.27.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/LICENSE +21 -0
- package/build/ComposedEntityCacheAdapter.d.ts +4 -2
- package/build/ComposedEntityCacheAdapter.js +39 -3
- package/build/ComposedEntityCacheAdapter.js.map +1 -1
- package/build/ComposedSecondaryEntityCache.d.ts +3 -2
- package/build/ComposedSecondaryEntityCache.js +3 -2
- package/build/ComposedSecondaryEntityCache.js.map +1 -1
- package/build/EnforcingEntityLoader.d.ts +10 -10
- package/build/EnforcingEntityLoader.js +8 -8
- package/build/EnforcingEntityLoader.js.map +1 -1
- package/build/Entity.d.ts +5 -5
- package/build/Entity.js +9 -9
- package/build/Entity.js.map +1 -1
- package/build/EntityAssociationLoader.d.ts +9 -4
- package/build/EntityAssociationLoader.js +5 -0
- package/build/EntityAssociationLoader.js.map +1 -1
- package/build/EntityCacheAdapter.d.ts +1 -1
- package/build/EntityCompanion.d.ts +1 -1
- package/build/EntityCompanion.js +1 -1
- package/build/EntityCompanionProvider.d.ts +9 -9
- package/build/EntityCompanionProvider.js +3 -3
- package/build/EntityConfiguration.d.ts +2 -2
- package/build/EntityDatabaseAdapter.d.ts +15 -6
- package/build/EntityDatabaseAdapter.js +8 -2
- package/build/EntityDatabaseAdapter.js.map +1 -1
- package/build/EntityFieldDefinition.d.ts +16 -8
- package/build/EntityFieldDefinition.js +12 -5
- package/build/EntityFieldDefinition.js.map +1 -1
- package/build/EntityFields.d.ts +11 -11
- package/build/EntityFields.js +11 -11
- package/build/EntityLoader.d.ts +7 -7
- package/build/EntityLoader.js +7 -7
- package/build/EntityLoader.js.map +1 -1
- package/build/EntityMutationInfo.d.ts +2 -0
- package/build/EntityMutationTriggerConfiguration.d.ts +1 -1
- package/build/EntityMutationTriggerConfiguration.js +1 -1
- package/build/EntityMutator.d.ts +7 -9
- package/build/EntityMutator.js +59 -47
- package/build/EntityMutator.js.map +1 -1
- package/build/EntityMutatorFactory.d.ts +4 -4
- package/build/EntityMutatorFactory.js +6 -6
- package/build/EntityMutatorFactory.js.map +1 -1
- package/build/EntityPrivacyPolicy.d.ts +18 -5
- package/build/EntityPrivacyPolicy.js +18 -5
- package/build/EntityPrivacyPolicy.js.map +1 -1
- package/build/EntityQueryContext.d.ts +12 -1
- package/build/EntityQueryContext.js +12 -1
- package/build/EntityQueryContext.js.map +1 -1
- package/build/EntityQueryContextProvider.d.ts +1 -1
- package/build/EntitySecondaryCacheLoader.d.ts +1 -1
- package/build/EntitySecondaryCacheLoader.js +1 -1
- package/build/GenericSecondaryEntityCache.d.ts +1 -1
- package/build/GenericSecondaryEntityCache.js +1 -1
- package/build/ReadonlyEntity.d.ts +1 -1
- package/build/ReadonlyEntity.js +1 -1
- package/build/ViewerContext.d.ts +2 -2
- package/build/ViewerContext.js +2 -2
- package/build/ViewerScopedEntityCompanion.d.ts +2 -2
- package/build/ViewerScopedEntityCompanion.js +2 -2
- package/build/ViewerScopedEntityLoaderFactory.d.ts +1 -1
- package/build/ViewerScopedEntityLoaderFactory.js +1 -1
- package/build/ViewerScopedEntityMutatorFactory.d.ts +5 -5
- package/build/ViewerScopedEntityMutatorFactory.js +7 -7
- package/build/ViewerScopedEntityMutatorFactory.js.map +1 -1
- package/build/__tests__/ComposedCacheAdapter-test.js +37 -4
- package/build/__tests__/ComposedCacheAdapter-test.js.map +1 -1
- package/build/__tests__/EntityCommonUseCases-test.js +5 -1
- package/build/__tests__/EntityCommonUseCases-test.js.map +1 -1
- package/build/__tests__/EntityCompanion-test.js +5 -1
- package/build/__tests__/EntityCompanion-test.js.map +1 -1
- package/build/__tests__/EntityCompanionProvider-test.js +5 -1
- package/build/__tests__/EntityCompanionProvider-test.js.map +1 -1
- package/build/__tests__/EntityEdges-test.js +199 -33
- package/build/__tests__/EntityEdges-test.js.map +1 -1
- package/build/__tests__/EntityLoader-test.js +5 -1
- package/build/__tests__/EntityLoader-test.js.map +1 -1
- package/build/__tests__/EntityMutator-test.js +38 -53
- package/build/__tests__/EntityMutator-test.js.map +1 -1
- package/build/__tests__/EntityPrivacyPolicy-test.js +5 -1
- package/build/__tests__/EntityPrivacyPolicy-test.js.map +1 -1
- package/build/__tests__/EntitySelfReferentialEdges-test.js +2 -2
- package/build/__tests__/EntitySelfReferentialEdges-test.js.map +1 -1
- package/build/__tests__/ViewerScopedEntityCompanionProvider-test.js +5 -1
- package/build/__tests__/ViewerScopedEntityCompanionProvider-test.js.map +1 -1
- package/build/__tests__/ViewerScopedEntityMutatorFactory-test.js +2 -3
- package/build/__tests__/ViewerScopedEntityMutatorFactory-test.js.map +1 -1
- package/build/errors/EntityCacheAdapterError.js +5 -1
- package/build/errors/EntityCacheAdapterError.js.map +1 -1
- package/build/errors/EntityDatabaseAdapterError.js +5 -1
- package/build/errors/EntityDatabaseAdapterError.js.map +1 -1
- package/build/errors/EntityInvalidFieldValueError.js +6 -2
- package/build/errors/EntityInvalidFieldValueError.js.map +1 -1
- package/build/errors/EntityNotAuthorizedError.js +5 -1
- package/build/errors/EntityNotAuthorizedError.js.map +1 -1
- package/build/errors/EntityNotFoundError.js +6 -2
- package/build/errors/EntityNotFoundError.js.map +1 -1
- package/build/index.js +5 -1
- package/build/index.js.map +1 -1
- package/build/internal/EntityDataManager.d.ts +5 -5
- package/build/internal/EntityDataManager.js +10 -7
- package/build/internal/EntityDataManager.js.map +1 -1
- package/build/internal/EntityFieldTransformationUtils.js +1 -1
- package/build/internal/EntityFieldTransformationUtils.js.map +1 -1
- package/build/internal/ReadThroughEntityCache.d.ts +2 -2
- package/build/internal/ReadThroughEntityCache.js +3 -3
- package/build/internal/ReadThroughEntityCache.js.map +1 -1
- package/build/internal/__tests__/EntityDataManager-test.js +12 -7
- package/build/internal/__tests__/EntityDataManager-test.js.map +1 -1
- package/build/internal/__tests__/ReadThroughEntityCache-test.js +5 -1
- package/build/internal/__tests__/ReadThroughEntityCache-test.js.map +1 -1
- package/build/metrics/IEntityMetricsAdapter.d.ts +63 -18
- package/build/metrics/IEntityMetricsAdapter.js +17 -1
- package/build/metrics/IEntityMetricsAdapter.js.map +1 -1
- package/build/metrics/NoOpEntityMetricsAdapter.d.ts +1 -3
- package/build/metrics/NoOpEntityMetricsAdapter.js +1 -3
- package/build/metrics/NoOpEntityMetricsAdapter.js.map +1 -1
- package/build/rules/AlwaysAllowPrivacyPolicyRule.js +5 -1
- package/build/rules/AlwaysAllowPrivacyPolicyRule.js.map +1 -1
- package/build/rules/AlwaysDenyPrivacyPolicyRule.js +5 -1
- package/build/rules/AlwaysDenyPrivacyPolicyRule.js.map +1 -1
- package/build/rules/AlwaysSkipPrivacyPolicyRule.js +5 -1
- package/build/rules/AlwaysSkipPrivacyPolicyRule.js.map +1 -1
- package/build/rules/PrivacyPolicyRule.d.ts +1 -1
- package/build/rules/PrivacyPolicyRule.js +1 -1
- package/build/utils/collections/maps.d.ts +1 -1
- package/build/utils/collections/maps.js +1 -1
- package/build/utils/testing/PrivacyPolicyRuleTestUtils.d.ts +1 -2
- package/build/utils/testing/StubDatabaseAdapter.js +6 -2
- package/build/utils/testing/StubDatabaseAdapter.js.map +1 -1
- package/build/utils/testing/StubQueryContextProvider.d.ts +1 -1
- package/build/utils/testing/StubQueryContextProvider.js +2 -0
- package/build/utils/testing/StubQueryContextProvider.js.map +1 -1
- package/package.json +3 -2
- package/src/ComposedEntityCacheAdapter.ts +44 -3
- package/src/ComposedSecondaryEntityCache.ts +3 -2
- package/src/EnforcingEntityLoader.ts +14 -10
- package/src/Entity.ts +9 -9
- package/src/EntityAssociationLoader.ts +9 -4
- package/src/EntityCacheAdapter.ts +1 -1
- package/src/EntityCompanion.ts +1 -1
- package/src/EntityCompanionProvider.ts +9 -9
- package/src/EntityConfiguration.ts +2 -2
- package/src/EntityDatabaseAdapter.ts +33 -6
- package/src/EntityFieldDefinition.ts +15 -6
- package/src/EntityFields.ts +11 -11
- package/src/EntityLoader.ts +11 -8
- package/src/EntityMutationInfo.ts +2 -0
- package/src/EntityMutationTriggerConfiguration.ts +1 -1
- package/src/EntityMutator.ts +99 -68
- package/src/EntityMutatorFactory.ts +4 -10
- package/src/EntityPrivacyPolicy.ts +20 -5
- package/src/EntityQueryContext.ts +12 -1
- package/src/EntityQueryContextProvider.ts +1 -1
- package/src/EntitySecondaryCacheLoader.ts +1 -1
- package/src/GenericSecondaryEntityCache.ts +1 -1
- package/src/ReadonlyEntity.ts +1 -1
- package/src/ViewerContext.ts +2 -2
- package/src/ViewerScopedEntityCompanion.ts +2 -2
- package/src/ViewerScopedEntityLoaderFactory.ts +1 -1
- package/src/ViewerScopedEntityMutatorFactory.ts +8 -23
- package/src/__tests__/ComposedCacheAdapter-test.ts +43 -4
- package/src/__tests__/EntityEdges-test.ts +287 -32
- package/src/__tests__/EntityMutator-test.ts +33 -54
- package/src/__tests__/EntitySelfReferentialEdges-test.ts +2 -2
- package/src/__tests__/ViewerScopedEntityMutatorFactory-test.ts +2 -6
- package/src/errors/EntityInvalidFieldValueError.ts +1 -1
- package/src/errors/EntityNotFoundError.ts +1 -1
- package/src/internal/EntityDataManager.ts +18 -9
- package/src/internal/EntityFieldTransformationUtils.ts +1 -1
- package/src/internal/ReadThroughEntityCache.ts +5 -3
- package/src/internal/__tests__/EntityDataManager-test.ts +11 -8
- package/src/metrics/IEntityMetricsAdapter.ts +73 -20
- package/src/metrics/NoOpEntityMetricsAdapter.ts +1 -5
- package/src/rules/PrivacyPolicyRule.ts +1 -1
- package/src/utils/collections/maps.ts +1 -1
- package/src/utils/testing/PrivacyPolicyRuleTestUtils.ts +1 -1
- package/src/utils/testing/StubDatabaseAdapter.ts +4 -1
- package/src/utils/testing/StubQueryContextProvider.ts +1 -1
package/src/EntityMutator.ts
CHANGED
|
@@ -18,7 +18,7 @@ import EntityMutationTriggerConfiguration, {
|
|
|
18
18
|
EntityNonTransactionalMutationTrigger,
|
|
19
19
|
} from './EntityMutationTriggerConfiguration';
|
|
20
20
|
import EntityMutationValidator from './EntityMutationValidator';
|
|
21
|
-
import EntityPrivacyPolicy
|
|
21
|
+
import EntityPrivacyPolicy from './EntityPrivacyPolicy';
|
|
22
22
|
import { EntityQueryContext, EntityTransactionalQueryContext } from './EntityQueryContext';
|
|
23
23
|
import ReadonlyEntity from './ReadonlyEntity';
|
|
24
24
|
import ViewerContext from './ViewerContext';
|
|
@@ -44,7 +44,6 @@ abstract class BaseMutator<
|
|
|
44
44
|
constructor(
|
|
45
45
|
protected readonly viewerContext: TViewerContext,
|
|
46
46
|
protected readonly queryContext: EntityQueryContext,
|
|
47
|
-
protected readonly privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext,
|
|
48
47
|
protected readonly entityConfiguration: EntityConfiguration<TFields>,
|
|
49
48
|
protected readonly entityClass: IEntityClass<
|
|
50
49
|
TFields,
|
|
@@ -183,7 +182,7 @@ export class CreateMutator<
|
|
|
183
182
|
|
|
184
183
|
/**
|
|
185
184
|
* Commit the new entity after authorizing against creation privacy rules. Invalidates all caches for
|
|
186
|
-
* queries that would return new entity
|
|
185
|
+
* queries that would return new entity.
|
|
187
186
|
* @returns authorized, cached, newly-created entity result, where result error can be UnauthorizedError
|
|
188
187
|
*/
|
|
189
188
|
async createAsync(): Promise<Result<TEntity>> {
|
|
@@ -221,7 +220,7 @@ export class CreateMutator<
|
|
|
221
220
|
this.privacyPolicy.authorizeCreateAsync(
|
|
222
221
|
this.viewerContext,
|
|
223
222
|
queryContext,
|
|
224
|
-
|
|
223
|
+
{ cascadingDeleteCause: null },
|
|
225
224
|
temporaryEntityForPrivacyCheck,
|
|
226
225
|
this.metricsAdapter
|
|
227
226
|
)
|
|
@@ -251,11 +250,9 @@ export class CreateMutator<
|
|
|
251
250
|
|
|
252
251
|
const insertResult = await this.databaseAdapter.insertAsync(queryContext, this.fieldsForEntity);
|
|
253
252
|
|
|
254
|
-
const entityLoader = this.entityLoaderFactory.forLoad(
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
this.privacyPolicyEvaluationContext
|
|
258
|
-
);
|
|
253
|
+
const entityLoader = this.entityLoaderFactory.forLoad(this.viewerContext, queryContext, {
|
|
254
|
+
cascadingDeleteCause: null,
|
|
255
|
+
});
|
|
259
256
|
queryContext.appendPostCommitInvalidationCallback(
|
|
260
257
|
entityLoader.invalidateFieldsAsync.bind(entityLoader, insertResult)
|
|
261
258
|
);
|
|
@@ -315,7 +312,6 @@ export class UpdateMutator<
|
|
|
315
312
|
constructor(
|
|
316
313
|
viewerContext: TViewerContext,
|
|
317
314
|
queryContext: EntityQueryContext,
|
|
318
|
-
privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext,
|
|
319
315
|
entityConfiguration: EntityConfiguration<TFields>,
|
|
320
316
|
entityClass: IEntityClass<
|
|
321
317
|
TFields,
|
|
@@ -355,7 +351,6 @@ export class UpdateMutator<
|
|
|
355
351
|
super(
|
|
356
352
|
viewerContext,
|
|
357
353
|
queryContext,
|
|
358
|
-
privacyPolicyEvaluationContext,
|
|
359
354
|
entityConfiguration,
|
|
360
355
|
entityClass,
|
|
361
356
|
privacyPolicy,
|
|
@@ -382,8 +377,7 @@ export class UpdateMutator<
|
|
|
382
377
|
|
|
383
378
|
/**
|
|
384
379
|
* Commit the changes to the entity after authorizing against update privacy rules.
|
|
385
|
-
* Invalidates all caches for pre-update entity
|
|
386
|
-
* transactional query context.
|
|
380
|
+
* Invalidates all caches for pre-update entity.
|
|
387
381
|
* @returns authorized updated entity result, where result error can be UnauthorizedError
|
|
388
382
|
*/
|
|
389
383
|
async updateAsync(): Promise<Result<TEntity>> {
|
|
@@ -391,7 +385,7 @@ export class UpdateMutator<
|
|
|
391
385
|
this.metricsAdapter,
|
|
392
386
|
EntityMetricsMutationType.UPDATE,
|
|
393
387
|
this.entityClass.name
|
|
394
|
-
)(this.updateInTransactionAsync());
|
|
388
|
+
)(this.updateInTransactionAsync(false, null));
|
|
395
389
|
}
|
|
396
390
|
|
|
397
391
|
/**
|
|
@@ -401,14 +395,27 @@ export class UpdateMutator<
|
|
|
401
395
|
return await enforceAsyncResult(this.updateAsync());
|
|
402
396
|
}
|
|
403
397
|
|
|
404
|
-
private async updateInTransactionAsync(
|
|
398
|
+
private async updateInTransactionAsync(
|
|
399
|
+
skipDatabaseUpdate: true,
|
|
400
|
+
cascadingDeleteCause: EntityCascadingDeletionInfo
|
|
401
|
+
): Promise<Result<TEntity>>;
|
|
402
|
+
private async updateInTransactionAsync(
|
|
403
|
+
skipDatabaseUpdate: false,
|
|
404
|
+
cascadingDeleteCause: EntityCascadingDeletionInfo | null
|
|
405
|
+
): Promise<Result<TEntity>>;
|
|
406
|
+
private async updateInTransactionAsync(
|
|
407
|
+
skipDatabaseUpdate: boolean,
|
|
408
|
+
cascadingDeleteCause: EntityCascadingDeletionInfo | null
|
|
409
|
+
): Promise<Result<TEntity>> {
|
|
405
410
|
return await this.queryContext.runInTransactionIfNotInTransactionAsync((innerQueryContext) =>
|
|
406
|
-
this.updateInternalAsync(innerQueryContext)
|
|
411
|
+
this.updateInternalAsync(innerQueryContext, skipDatabaseUpdate, cascadingDeleteCause)
|
|
407
412
|
);
|
|
408
413
|
}
|
|
409
414
|
|
|
410
415
|
private async updateInternalAsync(
|
|
411
|
-
queryContext: EntityTransactionalQueryContext
|
|
416
|
+
queryContext: EntityTransactionalQueryContext,
|
|
417
|
+
skipDatabaseUpdate: boolean,
|
|
418
|
+
cascadingDeleteCause: EntityCascadingDeletionInfo | null
|
|
412
419
|
): Promise<Result<TEntity>> {
|
|
413
420
|
this.validateFields(this.updatedFields);
|
|
414
421
|
|
|
@@ -417,7 +424,7 @@ export class UpdateMutator<
|
|
|
417
424
|
this.privacyPolicy.authorizeUpdateAsync(
|
|
418
425
|
this.viewerContext,
|
|
419
426
|
queryContext,
|
|
420
|
-
|
|
427
|
+
{ cascadingDeleteCause },
|
|
421
428
|
entityAboutToBeUpdated,
|
|
422
429
|
this.metricsAdapter
|
|
423
430
|
)
|
|
@@ -430,33 +437,34 @@ export class UpdateMutator<
|
|
|
430
437
|
this.mutationValidators,
|
|
431
438
|
queryContext,
|
|
432
439
|
entityAboutToBeUpdated,
|
|
433
|
-
{ type: EntityMutationType.UPDATE, previousValue: this.originalEntity }
|
|
440
|
+
{ type: EntityMutationType.UPDATE, previousValue: this.originalEntity, cascadingDeleteCause }
|
|
434
441
|
);
|
|
435
442
|
await this.executeMutationTriggersAsync(
|
|
436
443
|
this.mutationTriggers.beforeAll,
|
|
437
444
|
queryContext,
|
|
438
445
|
entityAboutToBeUpdated,
|
|
439
|
-
{ type: EntityMutationType.UPDATE, previousValue: this.originalEntity }
|
|
446
|
+
{ type: EntityMutationType.UPDATE, previousValue: this.originalEntity, cascadingDeleteCause }
|
|
440
447
|
);
|
|
441
448
|
await this.executeMutationTriggersAsync(
|
|
442
449
|
this.mutationTriggers.beforeUpdate,
|
|
443
450
|
queryContext,
|
|
444
451
|
entityAboutToBeUpdated,
|
|
445
|
-
{ type: EntityMutationType.UPDATE, previousValue: this.originalEntity }
|
|
452
|
+
{ type: EntityMutationType.UPDATE, previousValue: this.originalEntity, cascadingDeleteCause }
|
|
446
453
|
);
|
|
447
454
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
455
|
+
// skip the database update when specified
|
|
456
|
+
const updateResult = skipDatabaseUpdate
|
|
457
|
+
? null
|
|
458
|
+
: await this.databaseAdapter.updateAsync(
|
|
459
|
+
queryContext,
|
|
460
|
+
this.entityConfiguration.idField,
|
|
461
|
+
entityAboutToBeUpdated.getID(),
|
|
462
|
+
this.updatedFields
|
|
463
|
+
);
|
|
454
464
|
|
|
455
|
-
const entityLoader = this.entityLoaderFactory.forLoad(
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
this.privacyPolicyEvaluationContext
|
|
459
|
-
);
|
|
465
|
+
const entityLoader = this.entityLoaderFactory.forLoad(this.viewerContext, queryContext, {
|
|
466
|
+
cascadingDeleteCause,
|
|
467
|
+
});
|
|
460
468
|
|
|
461
469
|
queryContext.appendPostCommitInvalidationCallback(
|
|
462
470
|
entityLoader.invalidateFieldsAsync.bind(
|
|
@@ -468,22 +476,25 @@ export class UpdateMutator<
|
|
|
468
476
|
entityLoader.invalidateFieldsAsync.bind(entityLoader, this.fieldsForEntity)
|
|
469
477
|
);
|
|
470
478
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
479
|
+
// when the database update was skipped, assume it succeeded and use the optimistic
|
|
480
|
+
// entity to execute triggers
|
|
481
|
+
const updatedEntity = updateResult
|
|
482
|
+
? await entityLoader
|
|
483
|
+
.enforcing()
|
|
484
|
+
.loadByIDAsync(new this.entityClass(this.viewerContext, updateResult).getID())
|
|
485
|
+
: entityAboutToBeUpdated;
|
|
475
486
|
|
|
476
487
|
await this.executeMutationTriggersAsync(
|
|
477
488
|
this.mutationTriggers.afterUpdate,
|
|
478
489
|
queryContext,
|
|
479
490
|
updatedEntity,
|
|
480
|
-
{ type: EntityMutationType.UPDATE, previousValue: this.originalEntity }
|
|
491
|
+
{ type: EntityMutationType.UPDATE, previousValue: this.originalEntity, cascadingDeleteCause }
|
|
481
492
|
);
|
|
482
493
|
await this.executeMutationTriggersAsync(
|
|
483
494
|
this.mutationTriggers.afterAll,
|
|
484
495
|
queryContext,
|
|
485
496
|
updatedEntity,
|
|
486
|
-
{ type: EntityMutationType.UPDATE, previousValue: this.originalEntity }
|
|
497
|
+
{ type: EntityMutationType.UPDATE, previousValue: this.originalEntity, cascadingDeleteCause }
|
|
487
498
|
);
|
|
488
499
|
|
|
489
500
|
queryContext.appendPostCommitCallback(
|
|
@@ -491,7 +502,11 @@ export class UpdateMutator<
|
|
|
491
502
|
this,
|
|
492
503
|
this.mutationTriggers.afterCommit,
|
|
493
504
|
updatedEntity,
|
|
494
|
-
{
|
|
505
|
+
{
|
|
506
|
+
type: EntityMutationType.UPDATE,
|
|
507
|
+
previousValue: this.originalEntity,
|
|
508
|
+
cascadingDeleteCause,
|
|
509
|
+
}
|
|
495
510
|
)
|
|
496
511
|
);
|
|
497
512
|
|
|
@@ -519,7 +534,6 @@ export class DeleteMutator<
|
|
|
519
534
|
constructor(
|
|
520
535
|
viewerContext: TViewerContext,
|
|
521
536
|
queryContext: EntityQueryContext,
|
|
522
|
-
privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext,
|
|
523
537
|
entityConfiguration: EntityConfiguration<TFields>,
|
|
524
538
|
entityClass: IEntityClass<
|
|
525
539
|
TFields,
|
|
@@ -559,7 +573,6 @@ export class DeleteMutator<
|
|
|
559
573
|
super(
|
|
560
574
|
viewerContext,
|
|
561
575
|
queryContext,
|
|
562
|
-
privacyPolicyEvaluationContext,
|
|
563
576
|
entityConfiguration,
|
|
564
577
|
entityClass,
|
|
565
578
|
privacyPolicy,
|
|
@@ -687,7 +700,7 @@ export class DeleteMutator<
|
|
|
687
700
|
/**
|
|
688
701
|
* Finds all entities referencing the specified entity and either deletes them, nullifies
|
|
689
702
|
* their references to the specified entity, or invalidates the cache depending on the
|
|
690
|
-
*
|
|
703
|
+
* OnDeleteBehavior of the field referencing the specified entity.
|
|
691
704
|
*
|
|
692
705
|
* @remarks
|
|
693
706
|
* This works by doing reverse fan-out queries:
|
|
@@ -772,19 +785,34 @@ export class DeleteMutator<
|
|
|
772
785
|
}
|
|
773
786
|
|
|
774
787
|
switch (association.edgeDeletionBehavior) {
|
|
775
|
-
case
|
|
776
|
-
|
|
788
|
+
case EntityEdgeDeletionBehavior.CASCADE_DELETE_INVALIDATE_CACHE_ONLY: {
|
|
789
|
+
await Promise.all(
|
|
790
|
+
inboundReferenceEntities.map((inboundReferenceEntity) =>
|
|
791
|
+
enforceAsyncResult(
|
|
792
|
+
mutatorFactory
|
|
793
|
+
.forDelete(inboundReferenceEntity, queryContext)
|
|
794
|
+
.deleteInTransactionAsync(
|
|
795
|
+
processedEntityIdentifiers,
|
|
796
|
+
/* skipDatabaseDeletion */ true, // deletion is handled by DB
|
|
797
|
+
newCascadingDeleteCause
|
|
798
|
+
)
|
|
799
|
+
)
|
|
800
|
+
)
|
|
801
|
+
);
|
|
802
|
+
break;
|
|
803
|
+
}
|
|
804
|
+
case EntityEdgeDeletionBehavior.SET_NULL_INVALIDATE_CACHE_ONLY: {
|
|
777
805
|
await Promise.all(
|
|
778
806
|
inboundReferenceEntities.map((inboundReferenceEntity) =>
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
807
|
+
enforceAsyncResult(
|
|
808
|
+
mutatorFactory
|
|
809
|
+
.forUpdate(inboundReferenceEntity, queryContext)
|
|
810
|
+
.setField(fieldName, null)
|
|
811
|
+
['updateInTransactionAsync'](
|
|
812
|
+
/* skipDatabaseUpdate */ true,
|
|
813
|
+
newCascadingDeleteCause
|
|
814
|
+
)
|
|
815
|
+
)
|
|
788
816
|
)
|
|
789
817
|
);
|
|
790
818
|
break;
|
|
@@ -792,12 +820,15 @@ export class DeleteMutator<
|
|
|
792
820
|
case EntityEdgeDeletionBehavior.SET_NULL: {
|
|
793
821
|
await Promise.all(
|
|
794
822
|
inboundReferenceEntities.map((inboundReferenceEntity) =>
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
823
|
+
enforceAsyncResult(
|
|
824
|
+
mutatorFactory
|
|
825
|
+
.forUpdate(inboundReferenceEntity, queryContext)
|
|
826
|
+
.setField(fieldName, null)
|
|
827
|
+
['updateInTransactionAsync'](
|
|
828
|
+
/* skipDatabaseUpdate */ false,
|
|
829
|
+
newCascadingDeleteCause
|
|
830
|
+
)
|
|
831
|
+
)
|
|
801
832
|
)
|
|
802
833
|
);
|
|
803
834
|
break;
|
|
@@ -805,15 +836,15 @@ export class DeleteMutator<
|
|
|
805
836
|
case EntityEdgeDeletionBehavior.CASCADE_DELETE: {
|
|
806
837
|
await Promise.all(
|
|
807
838
|
inboundReferenceEntities.map((inboundReferenceEntity) =>
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
839
|
+
enforceAsyncResult(
|
|
840
|
+
mutatorFactory
|
|
841
|
+
.forDelete(inboundReferenceEntity, queryContext)
|
|
842
|
+
.deleteInTransactionAsync(
|
|
843
|
+
processedEntityIdentifiers,
|
|
844
|
+
/* skipDatabaseDeletion */ false,
|
|
845
|
+
newCascadingDeleteCause
|
|
846
|
+
)
|
|
847
|
+
)
|
|
817
848
|
)
|
|
818
849
|
);
|
|
819
850
|
}
|
|
@@ -5,7 +5,7 @@ import EntityLoaderFactory from './EntityLoaderFactory';
|
|
|
5
5
|
import EntityMutationTriggerConfiguration from './EntityMutationTriggerConfiguration';
|
|
6
6
|
import EntityMutationValidator from './EntityMutationValidator';
|
|
7
7
|
import { CreateMutator, UpdateMutator, DeleteMutator } from './EntityMutator';
|
|
8
|
-
import EntityPrivacyPolicy
|
|
8
|
+
import EntityPrivacyPolicy from './EntityPrivacyPolicy';
|
|
9
9
|
import { EntityQueryContext } from './EntityQueryContext';
|
|
10
10
|
import ViewerContext from './ViewerContext';
|
|
11
11
|
import IEntityMetricsAdapter from './metrics/IEntityMetricsAdapter';
|
|
@@ -72,13 +72,11 @@ export default class EntityMutatorFactory<
|
|
|
72
72
|
*/
|
|
73
73
|
forCreate(
|
|
74
74
|
viewerContext: TViewerContext,
|
|
75
|
-
queryContext: EntityQueryContext
|
|
76
|
-
privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext
|
|
75
|
+
queryContext: EntityQueryContext
|
|
77
76
|
): CreateMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
|
|
78
77
|
return new CreateMutator(
|
|
79
78
|
viewerContext,
|
|
80
79
|
queryContext,
|
|
81
|
-
privacyPolicyEvaluationContext,
|
|
82
80
|
this.entityConfiguration,
|
|
83
81
|
this.entityClass,
|
|
84
82
|
this.privacyPolicy,
|
|
@@ -98,13 +96,11 @@ export default class EntityMutatorFactory<
|
|
|
98
96
|
*/
|
|
99
97
|
forUpdate(
|
|
100
98
|
existingEntity: TEntity,
|
|
101
|
-
queryContext: EntityQueryContext
|
|
102
|
-
privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext
|
|
99
|
+
queryContext: EntityQueryContext
|
|
103
100
|
): UpdateMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
|
|
104
101
|
return new UpdateMutator(
|
|
105
102
|
existingEntity.getViewerContext(),
|
|
106
103
|
queryContext,
|
|
107
|
-
privacyPolicyEvaluationContext,
|
|
108
104
|
this.entityConfiguration,
|
|
109
105
|
this.entityClass,
|
|
110
106
|
this.privacyPolicy,
|
|
@@ -124,13 +120,11 @@ export default class EntityMutatorFactory<
|
|
|
124
120
|
*/
|
|
125
121
|
forDelete(
|
|
126
122
|
existingEntity: TEntity,
|
|
127
|
-
queryContext: EntityQueryContext
|
|
128
|
-
privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext
|
|
123
|
+
queryContext: EntityQueryContext
|
|
129
124
|
): DeleteMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
|
|
130
125
|
return new DeleteMutator(
|
|
131
126
|
existingEntity.getViewerContext(),
|
|
132
127
|
queryContext,
|
|
133
|
-
privacyPolicyEvaluationContext,
|
|
134
128
|
this.entityConfiguration,
|
|
135
129
|
this.entityClass,
|
|
136
130
|
this.privacyPolicy,
|
|
@@ -19,9 +19,24 @@ export type EntityPrivacyPolicyEvaluationContext = {
|
|
|
19
19
|
cascadingDeleteCause: EntityCascadingDeletionInfo | null;
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Evaluation mode for a EntityPrivacyPolicy. Useful when transitioning to
|
|
24
|
+
* using Entity for privacy.
|
|
25
|
+
*/
|
|
22
26
|
export enum EntityPrivacyPolicyEvaluationMode {
|
|
27
|
+
/**
|
|
28
|
+
* Enforce this privacy policy. Throw upon denial.
|
|
29
|
+
*/
|
|
23
30
|
ENFORCE,
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Do not enforce this privacy policy. Always allow but log when it would have denied.
|
|
34
|
+
*/
|
|
24
35
|
DRY_RUN,
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Enforce this privacy policy. Throw and log upon denial.
|
|
39
|
+
*/
|
|
25
40
|
ENFORCE_AND_LOG,
|
|
26
41
|
}
|
|
27
42
|
|
|
@@ -60,7 +75,7 @@ export enum EntityAuthorizationAction {
|
|
|
60
75
|
*
|
|
61
76
|
* @remarks
|
|
62
77
|
*
|
|
63
|
-
* A privacy policy declares lists of
|
|
78
|
+
* A privacy policy declares lists of PrivacyPolicyRule for create, read, update, and delete actions
|
|
64
79
|
* for an entity and provides logic for authorizing an entity against rules.
|
|
65
80
|
*
|
|
66
81
|
* Evaluation of a list of rules is performed according the following example. This allows constructing of
|
|
@@ -134,7 +149,7 @@ export default abstract class EntityPrivacyPolicy<
|
|
|
134
149
|
* @param queryContext - query context in which to perform the create authorization
|
|
135
150
|
* @param entity - entity to authorize
|
|
136
151
|
* @returns entity if authorized
|
|
137
|
-
* @throws
|
|
152
|
+
* @throws EntityNotAuthorizedError when not authorized
|
|
138
153
|
*/
|
|
139
154
|
async authorizeCreateAsync(
|
|
140
155
|
viewerContext: TViewerContext,
|
|
@@ -160,7 +175,7 @@ export default abstract class EntityPrivacyPolicy<
|
|
|
160
175
|
* @param queryContext - query context in which to perform the read authorization
|
|
161
176
|
* @param entity - entity to authorize
|
|
162
177
|
* @returns entity if authorized
|
|
163
|
-
* @throws
|
|
178
|
+
* @throws EntityNotAuthorizedError when not authorized
|
|
164
179
|
*/
|
|
165
180
|
async authorizeReadAsync(
|
|
166
181
|
viewerContext: TViewerContext,
|
|
@@ -186,7 +201,7 @@ export default abstract class EntityPrivacyPolicy<
|
|
|
186
201
|
* @param queryContext - query context in which to perform the update authorization
|
|
187
202
|
* @param entity - entity to authorize
|
|
188
203
|
* @returns entity if authorized
|
|
189
|
-
* @throws
|
|
204
|
+
* @throws EntityNotAuthorizedError when not authorized
|
|
190
205
|
*/
|
|
191
206
|
async authorizeUpdateAsync(
|
|
192
207
|
viewerContext: TViewerContext,
|
|
@@ -212,7 +227,7 @@ export default abstract class EntityPrivacyPolicy<
|
|
|
212
227
|
* @param queryContext - query context in which to perform the delete authorization
|
|
213
228
|
* @param entity - entity to authorize
|
|
214
229
|
* @returns entity if authorized
|
|
215
|
-
* @throws
|
|
230
|
+
* @throws EntityNotAuthorizedError when not authorized
|
|
216
231
|
*/
|
|
217
232
|
async authorizeDeleteAsync(
|
|
218
233
|
viewerContext: TViewerContext,
|
|
@@ -12,7 +12,7 @@ export type PreCommitCallback = (
|
|
|
12
12
|
* Entity framework representation of transactional and non-transactional database
|
|
13
13
|
* query execution units.
|
|
14
14
|
*
|
|
15
|
-
* The behavior of
|
|
15
|
+
* The behavior of EntityMutator and EntityLoader
|
|
16
16
|
* differs when in a transactional context.
|
|
17
17
|
*/
|
|
18
18
|
export abstract class EntityQueryContext {
|
|
@@ -29,6 +29,12 @@ export abstract class EntityQueryContext {
|
|
|
29
29
|
): Promise<T>;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Entity framework representation of a non-transactional query execution unit.
|
|
34
|
+
* When supplied to EntityMutator and EntityLoader methods, they will be
|
|
35
|
+
* run independently of any running transaction (though mutations start their own
|
|
36
|
+
* independent transactions internally when not being run in a transaction).
|
|
37
|
+
*/
|
|
32
38
|
export class EntityNonTransactionalQueryContext extends EntityQueryContext {
|
|
33
39
|
constructor(
|
|
34
40
|
queryInterface: any,
|
|
@@ -48,6 +54,11 @@ export class EntityNonTransactionalQueryContext extends EntityQueryContext {
|
|
|
48
54
|
}
|
|
49
55
|
}
|
|
50
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Entity framework representation of a transactional query execution unit. When supplied
|
|
59
|
+
* to EntityMutator and EntityLoader methods, those methods and their
|
|
60
|
+
* dependent triggers and validators will run within the transaction.
|
|
61
|
+
*/
|
|
51
62
|
export class EntityTransactionalQueryContext extends EntityQueryContext {
|
|
52
63
|
private readonly postCommitInvalidationCallbacks: PostCommitCallback[] = [];
|
|
53
64
|
private readonly postCommitCallbacks: PostCommitCallback[] = [];
|
|
@@ -20,7 +20,7 @@ export default abstract class EntityQueryContextProvider {
|
|
|
20
20
|
protected abstract getQueryInterface(): any;
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* Vend a transaction runner for use in
|
|
23
|
+
* Vend a transaction runner for use in runInTransactionAsync.
|
|
24
24
|
*/
|
|
25
25
|
protected abstract createTransactionRunner<T>(): (
|
|
26
26
|
transactionScope: (queryInterface: any) => Promise<T>
|
|
@@ -40,7 +40,7 @@ export interface ISecondaryEntityCache<TFields, TLoadParams> {
|
|
|
40
40
|
* when the underlying data of a cache key could be stale.
|
|
41
41
|
*
|
|
42
42
|
* This is most commonly used to further optimize hot paths that cannot make use of normal entity cache loading
|
|
43
|
-
* due to use of a non-unique-field-based
|
|
43
|
+
* due to use of a non-unique-field-based EntityLoader method like `loadManyByFieldEqualityConjunctionAsync` or
|
|
44
44
|
* `loadManyByRawWhereClauseAsync`.
|
|
45
45
|
*/
|
|
46
46
|
export default abstract class EntitySecondaryCacheLoader<
|
|
@@ -8,7 +8,7 @@ import { filterMap, zipToMap } from './utils/collections/maps';
|
|
|
8
8
|
/**
|
|
9
9
|
* A custom secondary read-through entity cache is a way to add a custom second layer of caching for a particular
|
|
10
10
|
* single entity load. One common way this may be used is to add a second layer of caching in a hot path that makes
|
|
11
|
-
* a call to
|
|
11
|
+
* a call to EntityLoader.loadManyByFieldEqualityConjunctionAsync is guaranteed to return at most one entity.
|
|
12
12
|
*/
|
|
13
13
|
export default abstract class GenericSecondaryEntityCache<TFields, TLoadParams>
|
|
14
14
|
implements ISecondaryEntityCache<TFields, TLoadParams>
|
package/src/ReadonlyEntity.ts
CHANGED
package/src/ViewerContext.ts
CHANGED
|
@@ -7,9 +7,9 @@ import ViewerScopedEntityCompanion from './ViewerScopedEntityCompanion';
|
|
|
7
7
|
import ViewerScopedEntityCompanionProvider from './ViewerScopedEntityCompanionProvider';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* A viewer context encapsulates all information necessary to evaluate an
|
|
10
|
+
* A viewer context encapsulates all information necessary to evaluate an EntityPrivacyPolicy.
|
|
11
11
|
*
|
|
12
|
-
* In combination with an
|
|
12
|
+
* In combination with an EntityCompanionProvider, a viewer context is the
|
|
13
13
|
* entry point into the Entity framework.
|
|
14
14
|
*/
|
|
15
15
|
export default class ViewerContext {
|
|
@@ -8,7 +8,7 @@ import ViewerScopedEntityMutatorFactory from './ViewerScopedEntityMutatorFactory
|
|
|
8
8
|
import IEntityMetricsAdapter from './metrics/IEntityMetricsAdapter';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* Provides a simpler API for loading and mutating entities by injecting the
|
|
11
|
+
* Provides a simpler API for loading and mutating entities by injecting the ViewerContext
|
|
12
12
|
* from the viewer-scoped entity companion provider.
|
|
13
13
|
*/
|
|
14
14
|
export default class ViewerScopedEntityCompanion<
|
|
@@ -79,7 +79,7 @@ export default class ViewerScopedEntityCompanion<
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
|
-
* Get the
|
|
82
|
+
* Get the IEntityMetricsAdapter for this companion.
|
|
83
83
|
*/
|
|
84
84
|
getMetricsAdapter(): IEntityMetricsAdapter {
|
|
85
85
|
return this.entityCompanion.getMetricsAdapter();
|
|
@@ -6,7 +6,7 @@ import ReadonlyEntity from './ReadonlyEntity';
|
|
|
6
6
|
import ViewerContext from './ViewerContext';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* Provides a cleaner API for loading entities by passing through the
|
|
9
|
+
* Provides a cleaner API for loading entities by passing through the ViewerContext.
|
|
10
10
|
*/
|
|
11
11
|
export default class ViewerScopedEntityLoaderFactory<
|
|
12
12
|
TFields,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { CreateMutator, UpdateMutator, DeleteMutator } from './EntityMutator';
|
|
2
2
|
import EntityMutatorFactory from './EntityMutatorFactory';
|
|
3
|
-
import EntityPrivacyPolicy
|
|
3
|
+
import EntityPrivacyPolicy from './EntityPrivacyPolicy';
|
|
4
4
|
import { EntityQueryContext } from './EntityQueryContext';
|
|
5
5
|
import ReadonlyEntity from './ReadonlyEntity';
|
|
6
6
|
import ViewerContext from './ViewerContext';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* Provides a cleaner API for mutating entities by passing through the
|
|
9
|
+
* Provides a cleaner API for mutating entities by passing through the ViewerContext.
|
|
10
10
|
*/
|
|
11
11
|
export default class ViewerScopedEntityMutatorFactory<
|
|
12
12
|
TFields,
|
|
@@ -35,37 +35,22 @@ export default class ViewerScopedEntityMutatorFactory<
|
|
|
35
35
|
) {}
|
|
36
36
|
|
|
37
37
|
forCreate(
|
|
38
|
-
queryContext: EntityQueryContext
|
|
39
|
-
privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext
|
|
38
|
+
queryContext: EntityQueryContext
|
|
40
39
|
): CreateMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
|
|
41
|
-
return this.entityMutatorFactory.forCreate(
|
|
42
|
-
this.viewerContext,
|
|
43
|
-
queryContext,
|
|
44
|
-
privacyPolicyEvaluationContext
|
|
45
|
-
);
|
|
40
|
+
return this.entityMutatorFactory.forCreate(this.viewerContext, queryContext);
|
|
46
41
|
}
|
|
47
42
|
|
|
48
43
|
forUpdate(
|
|
49
44
|
existingEntity: TEntity,
|
|
50
|
-
queryContext: EntityQueryContext
|
|
51
|
-
privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext
|
|
45
|
+
queryContext: EntityQueryContext
|
|
52
46
|
): UpdateMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
|
|
53
|
-
return this.entityMutatorFactory.forUpdate(
|
|
54
|
-
existingEntity,
|
|
55
|
-
queryContext,
|
|
56
|
-
privacyPolicyEvaluationContext
|
|
57
|
-
);
|
|
47
|
+
return this.entityMutatorFactory.forUpdate(existingEntity, queryContext);
|
|
58
48
|
}
|
|
59
49
|
|
|
60
50
|
forDelete(
|
|
61
51
|
existingEntity: TEntity,
|
|
62
|
-
queryContext: EntityQueryContext
|
|
63
|
-
privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext
|
|
52
|
+
queryContext: EntityQueryContext
|
|
64
53
|
): DeleteMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
|
|
65
|
-
return this.entityMutatorFactory.forDelete(
|
|
66
|
-
existingEntity,
|
|
67
|
-
queryContext,
|
|
68
|
-
privacyPolicyEvaluationContext
|
|
69
|
-
);
|
|
54
|
+
return this.entityMutatorFactory.forDelete(existingEntity, queryContext);
|
|
70
55
|
}
|
|
71
56
|
}
|