-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add global mutation trigger field to EntityCompanionProvider (#215
) * Add global mutation trigger field to EntityCompanionProvider * Add globalMutationTrigger to additional test cases * merge mutation triggers outside of EntityMutator * Exclude triggers with empty arrays
- Loading branch information
1 parent
778d9e3
commit 6569486
Showing
7 changed files
with
306 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
156 changes: 156 additions & 0 deletions
156
packages/entity/src/testfixtures/TestEntityWithMutationTriggers.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
import Entity from '../Entity'; | ||
import { EntityCompanionDefinition } from '../EntityCompanionProvider'; | ||
import EntityConfiguration from '../EntityConfiguration'; | ||
import { StringField, UUIDField } from '../EntityFields'; | ||
import { EntityTriggerMutationInfo } from '../EntityMutationInfo'; | ||
import { | ||
EntityMutationTrigger, | ||
EntityNonTransactionalMutationTrigger, | ||
} from '../EntityMutationTriggerConfiguration'; | ||
import EntityPrivacyPolicy from '../EntityPrivacyPolicy'; | ||
import { EntityQueryContext } from '../EntityQueryContext'; | ||
import ViewerContext from '../ViewerContext'; | ||
import AlwaysAllowPrivacyPolicyRule from '../rules/AlwaysAllowPrivacyPolicyRule'; | ||
|
||
export type TestMTFields = { | ||
id: string; | ||
stringField: string; | ||
}; | ||
|
||
export const testEntityMTConfiguration = new EntityConfiguration<TestMTFields>({ | ||
idField: 'id', | ||
tableName: 'test_entity_should_not_write_to_db_3', | ||
schema: { | ||
id: new UUIDField({ | ||
columnName: 'id', | ||
}), | ||
stringField: new StringField({ | ||
columnName: 'string_field', | ||
}), | ||
}, | ||
databaseAdapterFlavor: 'postgres', | ||
cacheAdapterFlavor: 'redis', | ||
}); | ||
|
||
export class TestEntityMTPrivacyPolicy extends EntityPrivacyPolicy< | ||
TestMTFields, | ||
string, | ||
ViewerContext, | ||
TestEntityWithMutationTriggers | ||
> { | ||
protected override readonly readRules = [ | ||
new AlwaysAllowPrivacyPolicyRule< | ||
TestMTFields, | ||
string, | ||
ViewerContext, | ||
TestEntityWithMutationTriggers | ||
>(), | ||
]; | ||
protected override readonly createRules = [ | ||
new AlwaysAllowPrivacyPolicyRule< | ||
TestMTFields, | ||
string, | ||
ViewerContext, | ||
TestEntityWithMutationTriggers | ||
>(), | ||
]; | ||
protected override readonly updateRules = [ | ||
new AlwaysAllowPrivacyPolicyRule< | ||
TestMTFields, | ||
string, | ||
ViewerContext, | ||
TestEntityWithMutationTriggers | ||
>(), | ||
]; | ||
protected override readonly deleteRules = [ | ||
new AlwaysAllowPrivacyPolicyRule< | ||
TestMTFields, | ||
string, | ||
ViewerContext, | ||
TestEntityWithMutationTriggers | ||
>(), | ||
]; | ||
} | ||
|
||
export class TestMutationTrigger extends EntityMutationTrigger< | ||
TestMTFields, | ||
string, | ||
ViewerContext, | ||
TestEntityWithMutationTriggers, | ||
keyof TestMTFields | ||
> { | ||
constructor( | ||
// @ts-expect-error key is never used but is helpful for debugging | ||
private readonly key: string, | ||
) { | ||
super(); | ||
} | ||
|
||
async executeAsync( | ||
_viewerContext: ViewerContext, | ||
_queryContext: EntityQueryContext, | ||
_entity: TestEntityWithMutationTriggers, | ||
_mutationInfo: EntityTriggerMutationInfo< | ||
TestMTFields, | ||
string, | ||
ViewerContext, | ||
TestEntityWithMutationTriggers, | ||
keyof TestMTFields | ||
>, | ||
): Promise<void> {} | ||
} | ||
|
||
export class NonTransactionalTestMutationTrigger extends EntityNonTransactionalMutationTrigger< | ||
TestMTFields, | ||
string, | ||
ViewerContext, | ||
TestEntityWithMutationTriggers, | ||
keyof TestMTFields | ||
> { | ||
constructor( | ||
// @ts-expect-error key is never used but is helpful for debugging | ||
private readonly key: string, | ||
) { | ||
super(); | ||
} | ||
|
||
async executeAsync( | ||
_viewerContext: ViewerContext, | ||
_entity: TestEntityWithMutationTriggers, | ||
_mutationInfo: EntityTriggerMutationInfo< | ||
TestMTFields, | ||
string, | ||
ViewerContext, | ||
TestEntityWithMutationTriggers, | ||
keyof TestMTFields | ||
>, | ||
): Promise<void> {} | ||
} | ||
|
||
/** | ||
* A test Entity that has one afterCreate and one afterAll trigger | ||
*/ | ||
export default class TestEntityWithMutationTriggers extends Entity< | ||
TestMTFields, | ||
string, | ||
ViewerContext | ||
> { | ||
static defineCompanionDefinition(): EntityCompanionDefinition< | ||
TestMTFields, | ||
string, | ||
ViewerContext, | ||
TestEntityWithMutationTriggers, | ||
TestEntityMTPrivacyPolicy | ||
> { | ||
return { | ||
entityClass: TestEntityWithMutationTriggers, | ||
entityConfiguration: testEntityMTConfiguration, | ||
privacyPolicyClass: TestEntityMTPrivacyPolicy, | ||
mutationTriggers: { | ||
afterCreate: [new TestMutationTrigger('localAfterCreate')], | ||
afterAll: [new TestMutationTrigger('localAfterAll')], | ||
afterCommit: [new NonTransactionalTestMutationTrigger('localAfterCommit')], | ||
}, | ||
}; | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
packages/entity/src/utils/__tests__/mergeEntityMutationTriggerConfigurations-test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { TestMutationTrigger } from '../../testfixtures/TestEntityWithMutationTriggers'; | ||
import { mergeEntityMutationTriggerConfigurations } from '../mergeEntityMutationTriggerConfigurations'; | ||
|
||
describe(mergeEntityMutationTriggerConfigurations, () => { | ||
it('successfully merges triggers', async () => { | ||
const firstAfter = new TestMutationTrigger('2'); | ||
const secondAfter = new TestMutationTrigger('3'); | ||
|
||
const merged = mergeEntityMutationTriggerConfigurations( | ||
{ | ||
beforeAll: [new TestMutationTrigger('1')], | ||
afterAll: [firstAfter], | ||
}, | ||
{ | ||
afterAll: [secondAfter], | ||
}, | ||
); | ||
|
||
expect(merged.beforeAll?.length).toBe(1); | ||
expect(merged.afterAll).toEqual([firstAfter, secondAfter]); | ||
expect(merged.beforeCreate?.length).toBeFalsy(); | ||
expect(merged.afterCreate?.length).toBeFalsy(); | ||
expect(merged.beforeUpdate?.length).toBeFalsy(); | ||
expect(merged.afterUpdate?.length).toBeFalsy(); | ||
expect(merged.beforeDelete?.length).toBeFalsy(); | ||
expect(merged.afterDelete?.length).toBeFalsy(); | ||
expect(merged.afterCommit?.length).toBeFalsy(); | ||
}); | ||
}); |
44 changes: 44 additions & 0 deletions
44
packages/entity/src/utils/mergeEntityMutationTriggerConfigurations.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import EntityMutationTriggerConfiguration from '../EntityMutationTriggerConfiguration'; | ||
import ReadonlyEntity from '../ReadonlyEntity'; | ||
import ViewerContext from '../ViewerContext'; | ||
|
||
function nonNullish<TValue>(value: TValue | null | undefined): value is NonNullable<TValue> { | ||
return value !== null && value !== undefined; | ||
} | ||
|
||
export function mergeEntityMutationTriggerConfigurations< | ||
TFields extends object, | ||
TID extends NonNullable<TFields[TSelectedFields]>, | ||
TViewerContext extends ViewerContext, | ||
TEntity extends ReadonlyEntity<TFields, TID, TViewerContext, TSelectedFields>, | ||
TSelectedFields extends keyof TFields, | ||
>( | ||
...mutationTriggerConfigurations: EntityMutationTriggerConfiguration< | ||
TFields, | ||
TID, | ||
TViewerContext, | ||
TEntity, | ||
TSelectedFields | ||
>[] | ||
): EntityMutationTriggerConfiguration<TFields, TID, TViewerContext, TEntity, TSelectedFields> { | ||
const merged = { | ||
beforeCreate: mutationTriggerConfigurations.flatMap((c) => c.beforeCreate).filter(nonNullish), | ||
afterCreate: mutationTriggerConfigurations.flatMap((c) => c.afterCreate).filter(nonNullish), | ||
beforeUpdate: mutationTriggerConfigurations.flatMap((c) => c.beforeUpdate).filter(nonNullish), | ||
afterUpdate: mutationTriggerConfigurations.flatMap((c) => c.afterUpdate).filter(nonNullish), | ||
beforeDelete: mutationTriggerConfigurations.flatMap((c) => c.beforeDelete).filter(nonNullish), | ||
afterDelete: mutationTriggerConfigurations.flatMap((c) => c.afterDelete).filter(nonNullish), | ||
beforeAll: mutationTriggerConfigurations.flatMap((c) => c.beforeAll).filter(nonNullish), | ||
afterAll: mutationTriggerConfigurations.flatMap((c) => c.afterAll).filter(nonNullish), | ||
afterCommit: mutationTriggerConfigurations.flatMap((c) => c.afterCommit).filter(nonNullish), | ||
}; | ||
|
||
/** Remove any trigger that is an empty array */ | ||
for (const key of Object.keys(merged) as (keyof typeof merged)[]) { | ||
if (merged[key].length === 0) { | ||
delete merged[key]; | ||
} | ||
} | ||
|
||
return merged; | ||
} |