Skip to content

Commit

Permalink
fix: separate out EntityMutationValidator (#68)
Browse files Browse the repository at this point in the history
  • Loading branch information
wschurman authored Jul 23, 2020
1 parent fc4377d commit 547a1ef
Show file tree
Hide file tree
Showing 10 changed files with 113 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
CacheAdapterFlavor,
EntityCompanionDefinition,
Entity,
EntityMutationTrigger,
EntityMutationExecutable,
EntityQueryContext,
} from '@expo/entity';
import Knex from 'knex';
Expand Down Expand Up @@ -97,7 +97,7 @@ class PostgresTriggerTestEntityPrivacyPolicy extends EntityPrivacyPolicy<
];
}

class ThrowConditionallyTrigger extends EntityMutationTrigger<
class ThrowConditionallyTrigger extends EntityMutationExecutable<
PostgresTriggerTestEntityFields,
string,
ViewerContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
CacheAdapterFlavor,
EntityCompanionDefinition,
Entity,
EntityMutationTrigger,
EntityMutationExecutable,
EntityQueryContext,
} from '@expo/entity';
import Knex from 'knex';
Expand Down Expand Up @@ -97,7 +97,7 @@ class PostgresValidatorTestEntityPrivacyPolicy extends EntityPrivacyPolicy<
];
}

class ThrowConditionallyTrigger extends EntityMutationTrigger<
class ThrowConditionallyTrigger extends EntityMutationExecutable<
PostgresValidatorTestEntityFields,
string,
ViewerContext,
Expand Down
5 changes: 3 additions & 2 deletions packages/entity/src/EntityCompanion.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { IEntityClass } from './Entity';
import EntityLoaderFactory from './EntityLoaderFactory';
import { EntityMutationTriggerConfiguration, EntityMutationTrigger } from './EntityMutationTrigger';
import EntityMutationTriggerConfiguration from './EntityMutationTriggerConfiguration';
import EntityMutationValidator from './EntityMutationValidator';
import EntityMutatorFactory from './EntityMutatorFactory';
import EntityPrivacyPolicy from './EntityPrivacyPolicy';
import IEntityQueryContextProvider from './IEntityQueryContextProvider';
Expand Down Expand Up @@ -58,7 +59,7 @@ export default class EntityCompanion<
>,
private readonly tableDataCoordinator: EntityTableDataCoordinator<TFields>,
PrivacyPolicyClass: IPrivacyPolicyClass<TPrivacyPolicy>,
mutationValidators: EntityMutationTrigger<
mutationValidators: EntityMutationValidator<
TFields,
TID,
TViewerContext,
Expand Down
7 changes: 4 additions & 3 deletions packages/entity/src/EntityCompanionProvider.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { IEntityClass } from './Entity';
import EntityCompanion, { IPrivacyPolicyClass } from './EntityCompanion';
import EntityConfiguration from './EntityConfiguration';
import { EntityMutationTriggerConfiguration, EntityMutationTrigger } from './EntityMutationTrigger';
import EntityMutationTriggerConfiguration from './EntityMutationTriggerConfiguration';
import EntityMutationValidator from './EntityMutationValidator';
import EntityPrivacyPolicy from './EntityPrivacyPolicy';
import IEntityCacheAdapterProvider from './IEntityCacheAdapterProvider';
import IEntityDatabaseAdapterProvider from './IEntityDatabaseAdapterProvider';
Expand Down Expand Up @@ -81,7 +82,7 @@ export class EntityCompanionDefinition<
>;
readonly entityConfiguration: EntityConfiguration<TFields>;
readonly privacyPolicyClass: IPrivacyPolicyClass<TPrivacyPolicy>;
readonly mutationValidators: EntityMutationTrigger<
readonly mutationValidators: EntityMutationValidator<
TFields,
TID,
TViewerContext,
Expand Down Expand Up @@ -115,7 +116,7 @@ export class EntityCompanionDefinition<
>;
entityConfiguration: EntityConfiguration<TFields>;
privacyPolicyClass: IPrivacyPolicyClass<TPrivacyPolicy>;
mutationValidators?: EntityMutationTrigger<
mutationValidators?: EntityMutationValidator<
TFields,
TID,
TViewerContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import ViewerContext from './ViewerContext';
/**
* Interface to define trigger behavior for entities.
*/
export interface EntityMutationTriggerConfiguration<
export default interface EntityMutationTriggerConfiguration<
TFields,
TID,
TViewerContext extends ViewerContext,
Expand Down
21 changes: 21 additions & 0 deletions packages/entity/src/EntityMutationValidator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { EntityQueryContext } from './EntityQueryContext';
import ReadonlyEntity from './ReadonlyEntity';
import ViewerContext from './ViewerContext';

/*
* A validator is a way to specify entity mutation validation that runs within the
* same transaction as the mutation itself before creating or updating an entity.
*/
export default abstract class EntityMutationValidator<
TFields,
TID,
TViewerContext extends ViewerContext,
TEntity extends ReadonlyEntity<TFields, TID, TViewerContext, TSelectedFields>,
TSelectedFields extends keyof TFields = keyof TFields
> {
abstract async executeAsync(
viewerContext: TViewerContext,
queryContext: EntityQueryContext,
entity: TEntity
): Promise<void>;
}
92 changes: 67 additions & 25 deletions packages/entity/src/EntityMutator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import EntityConfiguration from './EntityConfiguration';
import EntityDatabaseAdapter from './EntityDatabaseAdapter';
import { EntityEdgeDeletionBehavior } from './EntityFields';
import EntityLoaderFactory from './EntityLoaderFactory';
import { EntityMutationTrigger, EntityMutationTriggerConfiguration } from './EntityMutationTrigger';
import EntityMutationTriggerConfiguration, {
EntityMutationTrigger,
} from './EntityMutationTriggerConfiguration';
import EntityMutationValidator from './EntityMutationValidator';
import EntityPrivacyPolicy from './EntityPrivacyPolicy';
import { EntityQueryContext, EntityTransactionalQueryContext } from './EntityQueryContext';
import ReadonlyEntity from './ReadonlyEntity';
Expand Down Expand Up @@ -42,7 +45,7 @@ abstract class BaseMutator<
TSelectedFields
>,
protected readonly privacyPolicy: TPrivacyPolicy,
protected readonly mutationValidators: EntityMutationTrigger<
protected readonly mutationValidators: EntityMutationValidator<
TFields,
TID,
TViewerContext,
Expand All @@ -68,18 +71,21 @@ abstract class BaseMutator<
protected readonly metricsAdapter: IEntityMetricsAdapter
) {}

protected async executeTriggers(
triggers:
protected async executeMutationTriggersOrValidatorsAsync(
triggersOrValidators:
| EntityMutationTrigger<TFields, TID, TViewerContext, TEntity, TSelectedFields>[]
| EntityMutationValidator<TFields, TID, TViewerContext, TEntity, TSelectedFields>[]
| undefined,
queryContext: EntityQueryContext,
entity: TEntity
): Promise<void> {
if (!triggers) {
if (!triggersOrValidators) {
return;
}
await Promise.all(
triggers.map((trigger) => trigger.executeAsync(this.viewerContext, queryContext, entity))
triggersOrValidators.map((triggerOrValidator) =>
triggerOrValidator.executeAsync(this.viewerContext, queryContext, entity)
)
);
}
}
Expand Down Expand Up @@ -137,7 +143,7 @@ export class CreateMutator<
(innerQueryContext) => this.createInternalAsync(innerQueryContext)
);
if (internalResult.ok) {
await this.executeTriggers(
await this.executeMutationTriggersOrValidatorsAsync(
this.mutationTriggers.afterCommit,
this.queryContext,
internalResult.value
Expand Down Expand Up @@ -165,17 +171,17 @@ export class CreateMutator<
return authorizeCreateResult;
}

await this.executeTriggers(
await this.executeMutationTriggersOrValidatorsAsync(
this.mutationValidators,
queryContext,
temporaryEntityForPrivacyCheck
);
await this.executeTriggers(
await this.executeMutationTriggersOrValidatorsAsync(
this.mutationTriggers.beforeAll,
queryContext,
temporaryEntityForPrivacyCheck
);
await this.executeTriggers(
await this.executeMutationTriggersOrValidatorsAsync(
this.mutationTriggers.beforeCreate,
queryContext,
temporaryEntityForPrivacyCheck
Expand All @@ -191,8 +197,16 @@ export class CreateMutator<
.enforcing()
.loadByIDAsync(unauthorizedEntityAfterInsert.getID());

await this.executeTriggers(this.mutationTriggers.afterCreate, queryContext, newEntity);
await this.executeTriggers(this.mutationTriggers.afterAll, queryContext, newEntity);
await this.executeMutationTriggersOrValidatorsAsync(
this.mutationTriggers.afterCreate,
queryContext,
newEntity
);
await this.executeMutationTriggersOrValidatorsAsync(
this.mutationTriggers.afterAll,
queryContext,
newEntity
);

return result(newEntity);
}
Expand Down Expand Up @@ -232,7 +246,7 @@ export class UpdateMutator<
TSelectedFields
>,
privacyPolicy: TPrivacyPolicy,
mutationValidators: EntityMutationTrigger<
mutationValidators: EntityMutationValidator<
TFields,
TID,
TViewerContext,
Expand Down Expand Up @@ -310,7 +324,7 @@ export class UpdateMutator<
(innerQueryContext) => this.updateInternalAsync(innerQueryContext)
);
if (internalResult.ok) {
await this.executeTriggers(
await this.executeMutationTriggersOrValidatorsAsync(
this.mutationTriggers.afterCommit,
this.queryContext,
internalResult.value
Expand All @@ -334,13 +348,17 @@ export class UpdateMutator<
return authorizeUpdateResult;
}

await this.executeTriggers(this.mutationValidators, queryContext, entityAboutToBeUpdated);
await this.executeTriggers(
await this.executeMutationTriggersOrValidatorsAsync(
this.mutationValidators,
queryContext,
entityAboutToBeUpdated
);
await this.executeMutationTriggersOrValidatorsAsync(
this.mutationTriggers.beforeAll,
queryContext,
entityAboutToBeUpdated
);
await this.executeTriggers(
await this.executeMutationTriggersOrValidatorsAsync(
this.mutationTriggers.beforeUpdate,
queryContext,
entityAboutToBeUpdated
Expand All @@ -363,8 +381,16 @@ export class UpdateMutator<
.enforcing()
.loadByIDAsync(unauthorizedEntityAfterUpdate.getID());

await this.executeTriggers(this.mutationTriggers.afterUpdate, queryContext, updatedEntity);
await this.executeTriggers(this.mutationTriggers.afterAll, queryContext, updatedEntity);
await this.executeMutationTriggersOrValidatorsAsync(
this.mutationTriggers.afterUpdate,
queryContext,
updatedEntity
);
await this.executeMutationTriggersOrValidatorsAsync(
this.mutationTriggers.afterAll,
queryContext,
updatedEntity
);

return result(updatedEntity);
}
Expand Down Expand Up @@ -400,7 +426,7 @@ export class DeleteMutator<
TSelectedFields
>,
privacyPolicy: TPrivacyPolicy,
mutationValidators: EntityMutationTrigger<
mutationValidators: EntityMutationValidator<
TFields,
TID,
TViewerContext,
Expand Down Expand Up @@ -471,7 +497,7 @@ export class DeleteMutator<
)
);
if (internalResult.ok) {
await this.executeTriggers(
await this.executeMutationTriggersOrValidatorsAsync(
this.mutationTriggers.afterCommit,
this.queryContext,
internalResult.value
Expand All @@ -498,8 +524,16 @@ export class DeleteMutator<
processedEntityIdentifiersFromTransitiveDeletions
);

await this.executeTriggers(this.mutationTriggers.beforeAll, queryContext, this.entity);
await this.executeTriggers(this.mutationTriggers.beforeDelete, queryContext, this.entity);
await this.executeMutationTriggersOrValidatorsAsync(
this.mutationTriggers.beforeAll,
queryContext,
this.entity
);
await this.executeMutationTriggersOrValidatorsAsync(
this.mutationTriggers.beforeDelete,
queryContext,
this.entity
);

if (!skipDatabaseDeletion) {
await this.databaseAdapter.deleteAsync(
Expand All @@ -512,8 +546,16 @@ export class DeleteMutator<
const entityLoader = this.entityLoaderFactory.forLoad(this.viewerContext, queryContext);
await entityLoader.invalidateFieldsAsync(this.entity.getAllDatabaseFields());

await this.executeTriggers(this.mutationTriggers.afterDelete, queryContext, this.entity);
await this.executeTriggers(this.mutationTriggers.afterAll, queryContext, this.entity);
await this.executeMutationTriggersOrValidatorsAsync(
this.mutationTriggers.afterDelete,
queryContext,
this.entity
);
await this.executeMutationTriggersOrValidatorsAsync(
this.mutationTriggers.afterAll,
queryContext,
this.entity
);

return result(this.entity);
}
Expand Down
5 changes: 3 additions & 2 deletions packages/entity/src/EntityMutatorFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import Entity, { IEntityClass } from './Entity';
import EntityConfiguration from './EntityConfiguration';
import EntityDatabaseAdapter from './EntityDatabaseAdapter';
import EntityLoaderFactory from './EntityLoaderFactory';
import { EntityMutationTriggerConfiguration, EntityMutationTrigger } from './EntityMutationTrigger';
import EntityMutationTriggerConfiguration from './EntityMutationTriggerConfiguration';
import EntityMutationValidator from './EntityMutationValidator';
import { CreateMutator, UpdateMutator, DeleteMutator } from './EntityMutator';
import EntityPrivacyPolicy from './EntityPrivacyPolicy';
import { EntityQueryContext } from './EntityQueryContext';
Expand Down Expand Up @@ -37,7 +38,7 @@ export default class EntityMutatorFactory<
TSelectedFields
>,
private readonly privacyPolicy: TPrivacyPolicy,
private readonly mutationValidators: EntityMutationTrigger<
private readonly mutationValidators: EntityMutationValidator<
TFields,
TID,
TViewerContext,
Expand Down
16 changes: 8 additions & 8 deletions packages/entity/src/__tests__/EntityMutator-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import {

import EntityDatabaseAdapter from '../EntityDatabaseAdapter';
import EntityLoaderFactory from '../EntityLoaderFactory';
import {
EntityMutationTriggerConfiguration,
import EntityMutationTriggerConfiguration, {
EntityMutationTrigger,
} from '../EntityMutationTrigger';
} from '../EntityMutationTriggerConfiguration';
import EntityMutationValidator from '../EntityMutationValidator';
import EntityMutatorFactory from '../EntityMutatorFactory';
import {
EntityTransactionalQueryContext,
Expand Down Expand Up @@ -57,20 +57,20 @@ class TestMutationTrigger extends EntityMutationTrigger<
}

const setUpMutationValidatorSpies = (
mutationValidators: EntityMutationTrigger<
mutationValidators: EntityMutationValidator<
TestFields,
string,
ViewerContext,
TestEntity,
keyof TestFields
>[]
): EntityMutationTrigger<TestFields, string, ViewerContext, TestEntity, keyof TestFields>[] => {
): EntityMutationValidator<TestFields, string, ViewerContext, TestEntity, keyof TestFields>[] => {
return mutationValidators.map((validator) => spy(validator));
};

const verifyValidatorCounts = (
viewerContext: ViewerContext,
mutationValidatorSpies: EntityMutationTrigger<
mutationValidatorSpies: EntityMutationValidator<
TestFields,
string,
ViewerContext,
Expand Down Expand Up @@ -211,7 +211,7 @@ const createEntityMutatorFactory = (
TestEntityPrivacyPolicy
>;
metricsAdapter: IEntityMetricsAdapter;
mutationValidators: EntityMutationTrigger<
mutationValidators: EntityMutationValidator<
TestFields,
string,
ViewerContext,
Expand All @@ -226,7 +226,7 @@ const createEntityMutatorFactory = (
keyof TestFields
>;
} => {
const mutationValidators: EntityMutationTrigger<
const mutationValidators: EntityMutationValidator<
TestFields,
string,
ViewerContext,
Expand Down
Loading

0 comments on commit 547a1ef

Please sign in to comment.