@expo/entity 0.17.0 → 0.21.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/build/EnforcingEntityLoader.js +2 -2
- package/build/EnforcingEntityLoader.js.map +1 -1
- package/build/Entity.js +8 -2
- package/build/Entity.js.map +1 -1
- package/build/EntityAssociationLoader.js +3 -3
- package/build/EntityAssociationLoader.js.map +1 -1
- package/build/EntityCompanion.d.ts +5 -0
- package/build/EntityCompanion.js +8 -1
- package/build/EntityCompanion.js.map +1 -1
- package/build/EntityCompanionProvider.d.ts +1 -1
- package/build/EntityCompanionProvider.js +5 -5
- package/build/EntityCompanionProvider.js.map +1 -1
- package/build/EntityConfiguration.d.ts +1 -1
- package/build/EntityConfiguration.js +3 -3
- package/build/EntityConfiguration.js.map +1 -1
- package/build/EntityDatabaseAdapter.d.ts +4 -4
- package/build/EntityDatabaseAdapter.js +13 -13
- package/build/EntityDatabaseAdapter.js.map +1 -1
- package/build/EntityFieldDefinition.d.ts +77 -0
- package/build/EntityFieldDefinition.js +53 -0
- package/build/EntityFieldDefinition.js.map +1 -0
- package/build/EntityFields.d.ts +1 -77
- package/build/EntityFields.js +13 -61
- package/build/EntityFields.js.map +1 -1
- package/build/EntityLoader.d.ts +3 -1
- package/build/EntityLoader.js +19 -15
- package/build/EntityLoader.js.map +1 -1
- package/build/EntityLoaderFactory.d.ts +3 -1
- package/build/EntityLoaderFactory.js +3 -2
- package/build/EntityLoaderFactory.js.map +1 -1
- package/build/EntityMutationInfo.d.ts +26 -0
- package/build/EntityMutationInfo.js +10 -0
- package/build/EntityMutationInfo.js.map +1 -0
- package/build/EntityMutationTriggerConfiguration.d.ts +4 -4
- package/build/EntityMutationValidator.d.ts +3 -3
- package/build/EntityMutationValidator.js.map +1 -1
- package/build/EntityMutator.d.ts +5 -16
- package/build/EntityMutator.js +62 -58
- package/build/EntityMutator.js.map +1 -1
- package/build/EntityPrivacyPolicy.d.ts +5 -4
- package/build/EntityPrivacyPolicy.js +60 -12
- package/build/EntityPrivacyPolicy.js.map +1 -1
- package/build/EntityQueryContext.d.ts +24 -0
- package/build/EntityQueryContext.js +43 -0
- package/build/EntityQueryContext.js.map +1 -1
- package/build/EntityQueryContextProvider.js +1 -0
- package/build/EntityQueryContextProvider.js.map +1 -1
- package/build/EntitySecondaryCacheLoader.js +2 -2
- package/build/EntitySecondaryCacheLoader.js.map +1 -1
- package/build/ReadonlyEntity.js +3 -4
- package/build/ReadonlyEntity.js.map +1 -1
- package/build/ViewerScopedEntityCompanion.d.ts +5 -0
- package/build/ViewerScopedEntityCompanion.js +6 -0
- package/build/ViewerScopedEntityCompanion.js.map +1 -1
- package/build/__tests__/EnforcingEntityLoader-test.js +82 -82
- package/build/__tests__/EnforcingEntityLoader-test.js.map +1 -1
- package/build/__tests__/Entity-test.js +6 -6
- package/build/__tests__/Entity-test.js.map +1 -1
- package/build/__tests__/EntityAssociationLoader-test.js +40 -40
- package/build/__tests__/EntityAssociationLoader-test.js.map +1 -1
- package/build/__tests__/EntityCommonUseCases-test.js +11 -11
- package/build/__tests__/EntityCommonUseCases-test.js.map +1 -1
- package/build/__tests__/EntityCompanion-test.js +3 -3
- package/build/__tests__/EntityCompanion-test.js.map +1 -1
- package/build/__tests__/EntityCompanionProvider-test.js +1 -1
- package/build/__tests__/EntityCompanionProvider-test.js.map +1 -1
- package/build/__tests__/EntityDatabaseAdapter-test.js +12 -12
- package/build/__tests__/EntityDatabaseAdapter-test.js.map +1 -1
- package/build/__tests__/EntityEdges-test.js +103 -6
- package/build/__tests__/EntityEdges-test.js.map +1 -1
- package/build/__tests__/EntityFields-test.js +18 -27
- package/build/__tests__/EntityFields-test.js.map +1 -1
- package/build/__tests__/EntityLoader-constructor-test.d.ts +22 -0
- package/build/__tests__/EntityLoader-constructor-test.js +111 -0
- package/build/__tests__/EntityLoader-constructor-test.js.map +1 -0
- package/build/__tests__/EntityLoader-test.js +72 -64
- package/build/__tests__/EntityLoader-test.js.map +1 -1
- package/build/__tests__/EntityMutator-MutationCacheConsistency-test.d.ts +1 -0
- package/build/__tests__/EntityMutator-MutationCacheConsistency-test.js +81 -0
- package/build/__tests__/EntityMutator-MutationCacheConsistency-test.js.map +1 -0
- package/build/__tests__/EntityMutator-test.js +116 -114
- package/build/__tests__/EntityMutator-test.js.map +1 -1
- package/build/__tests__/EntityPrivacyPolicy-test.js +143 -67
- package/build/__tests__/EntityPrivacyPolicy-test.js.map +1 -1
- package/build/__tests__/EntityQueryContext-test.d.ts +1 -0
- package/build/__tests__/EntityQueryContext-test.js +56 -0
- package/build/__tests__/EntityQueryContext-test.js.map +1 -0
- package/build/__tests__/EntitySecondaryCacheLoader-test.js +15 -15
- package/build/__tests__/EntitySecondaryCacheLoader-test.js.map +1 -1
- package/build/__tests__/EntitySelfReferentialEdges-test.js +13 -12
- package/build/__tests__/EntitySelfReferentialEdges-test.js.map +1 -1
- package/build/__tests__/ReadonlyEntity-test.js +11 -11
- package/build/__tests__/ReadonlyEntity-test.js.map +1 -1
- package/build/__tests__/ViewerContext-test.js +2 -2
- package/build/__tests__/ViewerContext-test.js.map +1 -1
- package/build/__tests__/ViewerScopedEntityCompanion-test.js +2 -2
- package/build/__tests__/ViewerScopedEntityCompanion-test.js.map +1 -1
- package/build/__tests__/ViewerScopedEntityCompanionProvider-test.js +2 -2
- package/build/__tests__/ViewerScopedEntityCompanionProvider-test.js.map +1 -1
- package/build/__tests__/ViewerScopedEntityLoaderFactory-test.js +5 -5
- package/build/__tests__/ViewerScopedEntityLoaderFactory-test.js.map +1 -1
- package/build/__tests__/ViewerScopedEntityMutatorFactory-test.js +5 -5
- package/build/__tests__/ViewerScopedEntityMutatorFactory-test.js.map +1 -1
- package/build/__tests__/cases/TwoEntitySameTableDisjointRows-test.js +5 -5
- package/build/__tests__/cases/TwoEntitySameTableDisjointRows-test.js.map +1 -1
- package/build/__tests__/cases/TwoEntitySameTableOverlappingRows-test.js +2 -2
- package/build/__tests__/cases/TwoEntitySameTableOverlappingRows-test.js.map +1 -1
- package/build/__tests__/entityUtils-test.js +21 -21
- package/build/__tests__/entityUtils-test.js.map +1 -1
- package/build/index.d.ts +3 -0
- package/build/index.js +5 -1
- package/build/index.js.map +1 -1
- package/build/internal/EntityDataManager.js +8 -7
- package/build/internal/EntityDataManager.js.map +1 -1
- package/build/internal/EntityFieldTransformationUtils.js +2 -2
- package/build/internal/EntityFieldTransformationUtils.js.map +1 -1
- package/build/internal/ReadThroughEntityCache.js +4 -4
- package/build/internal/ReadThroughEntityCache.js.map +1 -1
- package/build/internal/__tests__/EntityDataManager-test.js +17 -17
- package/build/internal/__tests__/EntityDataManager-test.js.map +1 -1
- package/build/internal/__tests__/EntityFieldTransformationUtils-test.js +8 -8
- package/build/internal/__tests__/EntityFieldTransformationUtils-test.js.map +1 -1
- package/build/internal/__tests__/ReadThroughEntityCache-test.js +48 -48
- package/build/internal/__tests__/ReadThroughEntityCache-test.js.map +1 -1
- package/build/metrics/EntityMetricsUtils.js +1 -1
- package/build/metrics/EntityMetricsUtils.js.map +1 -1
- package/build/metrics/IEntityMetricsAdapter.d.ts +16 -0
- package/build/metrics/IEntityMetricsAdapter.js +6 -1
- package/build/metrics/IEntityMetricsAdapter.js.map +1 -1
- package/build/metrics/NoOpEntityMetricsAdapter.d.ts +2 -1
- package/build/metrics/NoOpEntityMetricsAdapter.js +1 -0
- package/build/metrics/NoOpEntityMetricsAdapter.js.map +1 -1
- package/build/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.js +4 -4
- package/build/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.js.map +1 -1
- package/build/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.js +4 -4
- package/build/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.js.map +1 -1
- package/build/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.js +4 -4
- package/build/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.js.map +1 -1
- package/build/testfixtures/DateIDTestEntity.js.map +1 -1
- package/build/testfixtures/SimpleTestEntity.js.map +1 -1
- package/build/testfixtures/TestEntity.d.ts +5 -5
- package/build/testfixtures/TestEntity.js +2 -2
- package/build/testfixtures/TestEntity.js.map +1 -1
- package/build/testfixtures/TestEntity2.d.ts +5 -5
- package/build/testfixtures/TestEntity2.js.map +1 -1
- package/build/testfixtures/TestEntityNumberKey.js.map +1 -1
- package/build/utils/collections/__tests__/maps-test.js +13 -13
- package/build/utils/collections/__tests__/maps-test.js.map +1 -1
- package/build/utils/collections/maps.js +1 -1
- package/build/utils/collections/maps.js.map +1 -1
- package/build/utils/testing/PrivacyPolicyRuleTestUtils.d.ts +6 -6
- package/build/utils/testing/PrivacyPolicyRuleTestUtils.js +1 -1
- package/build/utils/testing/PrivacyPolicyRuleTestUtils.js.map +1 -1
- package/build/utils/testing/StubCacheAdapter.js +1 -1
- package/build/utils/testing/StubCacheAdapter.js.map +1 -1
- package/build/utils/testing/StubDatabaseAdapter.js +6 -6
- package/build/utils/testing/StubDatabaseAdapter.js.map +1 -1
- package/build/utils/testing/__tests__/StubDatabaseAdapter-test.js +9 -9
- package/build/utils/testing/__tests__/StubDatabaseAdapter-test.js.map +1 -1
- package/build/utils/testing/describeFieldTestCase.d.ts +2 -0
- package/build/utils/testing/describeFieldTestCase.js +18 -0
- package/build/utils/testing/describeFieldTestCase.js.map +1 -0
- package/package.json +2 -1
- package/src/Entity.ts +10 -2
- package/src/EntityAssociationLoader.ts +1 -1
- package/src/EntityCompanion.ts +10 -2
- package/src/EntityCompanionProvider.ts +5 -9
- package/src/EntityConfiguration.ts +1 -1
- package/src/EntityDatabaseAdapter.ts +10 -8
- package/src/EntityFieldDefinition.ts +124 -0
- package/src/EntityFields.ts +2 -125
- package/src/EntityLoader.ts +12 -4
- package/src/EntityLoaderFactory.ts +5 -2
- package/src/EntityMutationInfo.ts +47 -0
- package/src/EntityMutationTriggerConfiguration.ts +5 -5
- package/src/EntityMutationValidator.ts +10 -4
- package/src/EntityMutator.ts +98 -76
- package/src/EntityPrivacyPolicy.ts +77 -19
- package/src/EntityQueryContext.ts +54 -0
- package/src/EntityQueryContextProvider.ts +1 -0
- package/src/ReadonlyEntity.ts +3 -2
- package/src/ViewerScopedEntityCompanion.ts +8 -0
- package/src/__tests__/Entity-test.ts +8 -8
- package/src/__tests__/EntityCommonUseCases-test.ts +4 -4
- package/src/__tests__/EntityEdges-test.ts +169 -14
- package/src/__tests__/EntityFields-test.ts +2 -21
- package/src/__tests__/EntityLoader-constructor-test.ts +177 -0
- package/src/__tests__/EntityLoader-test.ts +39 -11
- package/src/__tests__/EntityMutator-MutationCacheConsistency-test.ts +105 -0
- package/src/__tests__/EntityMutator-test.ts +140 -133
- package/src/__tests__/EntityPrivacyPolicy-test.ts +215 -78
- package/src/__tests__/EntityQueryContext-test.ts +82 -0
- package/src/__tests__/EntitySecondaryCacheLoader-test.ts +7 -9
- package/src/__tests__/EntitySelfReferentialEdges-test.ts +6 -5
- package/src/__tests__/ViewerContext-test.ts +7 -6
- package/src/__tests__/ViewerScopedEntityCompanion-test.ts +11 -10
- package/src/__tests__/ViewerScopedEntityMutatorFactory-test.ts +4 -3
- package/src/__tests__/cases/TwoEntitySameTableDisjointRows-test.ts +6 -6
- package/src/__tests__/cases/TwoEntitySameTableOverlappingRows-test.ts +4 -4
- package/src/index.ts +3 -0
- package/src/internal/EntityDataManager.ts +2 -1
- package/src/internal/__tests__/EntityDataManager-test.ts +2 -2
- package/src/internal/__tests__/ReadThroughEntityCache-test.ts +15 -13
- package/src/metrics/EntityMetricsUtils.ts +56 -50
- package/src/metrics/IEntityMetricsAdapter.ts +23 -0
- package/src/metrics/NoOpEntityMetricsAdapter.ts +2 -0
- package/src/testfixtures/DateIDTestEntity.ts +4 -4
- package/src/testfixtures/SimpleTestEntity.ts +4 -4
- package/src/testfixtures/TestEntity.ts +4 -4
- package/src/testfixtures/TestEntity2.ts +4 -4
- package/src/testfixtures/TestEntityNumberKey.ts +4 -4
- package/src/utils/testing/StubDatabaseAdapter.ts +2 -2
- package/src/utils/testing/__tests__/StubDatabaseAdapter-test.ts +1 -1
- package/src/utils/testing/describeFieldTestCase.ts +21 -0
- package/CHANGELOG.md +0 -252
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { v1 as uuidv1, v3 as uuidv3, v4 as uuidv4, v5 as uuidv5 } from 'uuid';
|
|
2
2
|
|
|
3
|
+
import { EntityFieldDefinition } from '../EntityFieldDefinition';
|
|
3
4
|
import {
|
|
4
|
-
EntityFieldDefinition,
|
|
5
5
|
StringField,
|
|
6
6
|
UUIDField,
|
|
7
7
|
DateField,
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
JSONArrayField,
|
|
15
15
|
MaybeJSONArrayField,
|
|
16
16
|
} from '../EntityFields';
|
|
17
|
+
import describeFieldTestCase from '../utils/testing/describeFieldTestCase';
|
|
17
18
|
|
|
18
19
|
class TestFieldDefinition extends EntityFieldDefinition<string> {
|
|
19
20
|
protected validateInputValueInternal(value: string): boolean {
|
|
@@ -53,26 +54,6 @@ describe(EntityFieldDefinition, () => {
|
|
|
53
54
|
});
|
|
54
55
|
});
|
|
55
56
|
|
|
56
|
-
const describeFieldTestCase = <T>(
|
|
57
|
-
fieldDefinition: EntityFieldDefinition<T>,
|
|
58
|
-
validValues: T[],
|
|
59
|
-
invalidValues: any[]
|
|
60
|
-
): void => {
|
|
61
|
-
describe(fieldDefinition.constructor.name, () => {
|
|
62
|
-
if (validValues.length > 0) {
|
|
63
|
-
test.each(validValues)(`${fieldDefinition.constructor.name}.valid %p`, (value) => {
|
|
64
|
-
expect(fieldDefinition.validateInputValue(value)).toBe(true);
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (invalidValues.length > 0) {
|
|
69
|
-
test.each(invalidValues)(`${fieldDefinition.constructor.name}.invalid %p`, (value) => {
|
|
70
|
-
expect(fieldDefinition.validateInputValue(value)).toBe(false);
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
};
|
|
75
|
-
|
|
76
57
|
describeFieldTestCase(new StringField({ columnName: 'wat' }), ['hello', ''], [1, true, {}, [[]]]);
|
|
77
58
|
describeFieldTestCase(
|
|
78
59
|
new UUIDField({ columnName: 'wat' }),
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { instance, mock } from 'ts-mockito';
|
|
2
|
+
|
|
3
|
+
import Entity from '../Entity';
|
|
4
|
+
import { EntityCompanionDefinition } from '../EntityCompanionProvider';
|
|
5
|
+
import EntityConfiguration from '../EntityConfiguration';
|
|
6
|
+
import { StringField } from '../EntityFields';
|
|
7
|
+
import EntityLoader from '../EntityLoader';
|
|
8
|
+
import EntityPrivacyPolicy from '../EntityPrivacyPolicy';
|
|
9
|
+
import ViewerContext from '../ViewerContext';
|
|
10
|
+
import EntityDataManager from '../internal/EntityDataManager';
|
|
11
|
+
import ReadThroughEntityCache from '../internal/ReadThroughEntityCache';
|
|
12
|
+
import IEntityMetricsAdapter from '../metrics/IEntityMetricsAdapter';
|
|
13
|
+
import AlwaysAllowPrivacyPolicyRule from '../rules/AlwaysAllowPrivacyPolicyRule';
|
|
14
|
+
import { NoCacheStubCacheAdapterProvider } from '../utils/testing/StubCacheAdapter';
|
|
15
|
+
import StubDatabaseAdapter from '../utils/testing/StubDatabaseAdapter';
|
|
16
|
+
import StubQueryContextProvider from '../utils/testing/StubQueryContextProvider';
|
|
17
|
+
|
|
18
|
+
export type TestFields = {
|
|
19
|
+
id: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type TestFieldSelection = keyof TestFields;
|
|
23
|
+
|
|
24
|
+
export const testEntityConfiguration = new EntityConfiguration<TestFields>({
|
|
25
|
+
idField: 'id',
|
|
26
|
+
tableName: 'test_entity_should_not_write_to_db',
|
|
27
|
+
schema: {
|
|
28
|
+
id: new StringField({
|
|
29
|
+
columnName: 'id',
|
|
30
|
+
}),
|
|
31
|
+
},
|
|
32
|
+
databaseAdapterFlavor: 'postgres',
|
|
33
|
+
cacheAdapterFlavor: 'redis',
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export class TestEntityPrivacyPolicy extends EntityPrivacyPolicy<
|
|
37
|
+
TestFields,
|
|
38
|
+
string,
|
|
39
|
+
ViewerContext,
|
|
40
|
+
TestEntity,
|
|
41
|
+
TestFieldSelection
|
|
42
|
+
> {
|
|
43
|
+
protected override readonly readRules = [
|
|
44
|
+
new AlwaysAllowPrivacyPolicyRule<
|
|
45
|
+
TestFields,
|
|
46
|
+
string,
|
|
47
|
+
ViewerContext,
|
|
48
|
+
TestEntity,
|
|
49
|
+
TestFieldSelection
|
|
50
|
+
>(),
|
|
51
|
+
];
|
|
52
|
+
protected override readonly createRules = [
|
|
53
|
+
new AlwaysAllowPrivacyPolicyRule<
|
|
54
|
+
TestFields,
|
|
55
|
+
string,
|
|
56
|
+
ViewerContext,
|
|
57
|
+
TestEntity,
|
|
58
|
+
TestFieldSelection
|
|
59
|
+
>(),
|
|
60
|
+
];
|
|
61
|
+
protected override readonly updateRules = [
|
|
62
|
+
new AlwaysAllowPrivacyPolicyRule<
|
|
63
|
+
TestFields,
|
|
64
|
+
string,
|
|
65
|
+
ViewerContext,
|
|
66
|
+
TestEntity,
|
|
67
|
+
TestFieldSelection
|
|
68
|
+
>(),
|
|
69
|
+
];
|
|
70
|
+
protected override readonly deleteRules = [
|
|
71
|
+
new AlwaysAllowPrivacyPolicyRule<
|
|
72
|
+
TestFields,
|
|
73
|
+
string,
|
|
74
|
+
ViewerContext,
|
|
75
|
+
TestEntity,
|
|
76
|
+
TestFieldSelection
|
|
77
|
+
>(),
|
|
78
|
+
];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const ID_SENTINEL_THROW_LITERAL = 'throw_literal';
|
|
82
|
+
const ID_SENTINEL_THROW_ERROR = 'throw_error';
|
|
83
|
+
|
|
84
|
+
export default class TestEntity extends Entity<
|
|
85
|
+
TestFields,
|
|
86
|
+
string,
|
|
87
|
+
ViewerContext,
|
|
88
|
+
TestFieldSelection
|
|
89
|
+
> {
|
|
90
|
+
constructor(viewerContext: ViewerContext, rawFields: Readonly<TestFields>) {
|
|
91
|
+
if (rawFields.id === ID_SENTINEL_THROW_LITERAL) {
|
|
92
|
+
// eslint-disable-next-line no-throw-literal,@typescript-eslint/no-throw-literal
|
|
93
|
+
throw 'hello';
|
|
94
|
+
} else if (rawFields.id === ID_SENTINEL_THROW_ERROR) {
|
|
95
|
+
throw new Error('world');
|
|
96
|
+
}
|
|
97
|
+
super(viewerContext, rawFields);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static getCompanionDefinition(): EntityCompanionDefinition<
|
|
101
|
+
TestFields,
|
|
102
|
+
string,
|
|
103
|
+
ViewerContext,
|
|
104
|
+
TestEntity,
|
|
105
|
+
TestEntityPrivacyPolicy,
|
|
106
|
+
TestFieldSelection
|
|
107
|
+
> {
|
|
108
|
+
return testEntityCompanion;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export const testEntityCompanion = new EntityCompanionDefinition({
|
|
113
|
+
entityClass: TestEntity,
|
|
114
|
+
entityConfiguration: testEntityConfiguration,
|
|
115
|
+
privacyPolicyClass: TestEntityPrivacyPolicy,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe(EntityLoader, () => {
|
|
119
|
+
it('handles thrown errors and literals from constructor', async () => {
|
|
120
|
+
const viewerContext = instance(mock(ViewerContext));
|
|
121
|
+
const metricsAdapter = instance(mock<IEntityMetricsAdapter>());
|
|
122
|
+
const queryContext = StubQueryContextProvider.getQueryContext();
|
|
123
|
+
|
|
124
|
+
const databaseAdapter = new StubDatabaseAdapter<TestFields>(
|
|
125
|
+
testEntityConfiguration,
|
|
126
|
+
StubDatabaseAdapter.convertFieldObjectsToDataStore(
|
|
127
|
+
testEntityConfiguration,
|
|
128
|
+
new Map([
|
|
129
|
+
[
|
|
130
|
+
testEntityConfiguration.tableName,
|
|
131
|
+
[
|
|
132
|
+
{
|
|
133
|
+
id: ID_SENTINEL_THROW_LITERAL,
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
id: ID_SENTINEL_THROW_ERROR,
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
],
|
|
140
|
+
])
|
|
141
|
+
)
|
|
142
|
+
);
|
|
143
|
+
const privacyPolicy = new TestEntityPrivacyPolicy();
|
|
144
|
+
const cacheAdapterProvider = new NoCacheStubCacheAdapterProvider();
|
|
145
|
+
const cacheAdapter = cacheAdapterProvider.getCacheAdapter(testEntityConfiguration);
|
|
146
|
+
const entityCache = new ReadThroughEntityCache(testEntityConfiguration, cacheAdapter);
|
|
147
|
+
const dataManager = new EntityDataManager(
|
|
148
|
+
databaseAdapter,
|
|
149
|
+
entityCache,
|
|
150
|
+
StubQueryContextProvider,
|
|
151
|
+
metricsAdapter,
|
|
152
|
+
TestEntity.name
|
|
153
|
+
);
|
|
154
|
+
const entityLoader = new EntityLoader(
|
|
155
|
+
viewerContext,
|
|
156
|
+
queryContext,
|
|
157
|
+
testEntityConfiguration,
|
|
158
|
+
TestEntity,
|
|
159
|
+
privacyPolicy,
|
|
160
|
+
dataManager,
|
|
161
|
+
metricsAdapter
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
let capturedThrownThing1: any;
|
|
165
|
+
try {
|
|
166
|
+
await entityLoader.loadByIDAsync(ID_SENTINEL_THROW_LITERAL);
|
|
167
|
+
} catch (e) {
|
|
168
|
+
capturedThrownThing1 = e;
|
|
169
|
+
}
|
|
170
|
+
expect(capturedThrownThing1).not.toBeInstanceOf(Error);
|
|
171
|
+
expect(capturedThrownThing1).toEqual('hello');
|
|
172
|
+
|
|
173
|
+
const result = await entityLoader.loadByIDAsync(ID_SENTINEL_THROW_ERROR);
|
|
174
|
+
expect(result.ok).toBe(false);
|
|
175
|
+
expect(result.enforceError().message).toEqual('world');
|
|
176
|
+
});
|
|
177
|
+
});
|
|
@@ -21,6 +21,7 @@ describe(EntityLoader, () => {
|
|
|
21
21
|
it('loads entities', async () => {
|
|
22
22
|
const dateToInsert = new Date();
|
|
23
23
|
const viewerContext = instance(mock(ViewerContext));
|
|
24
|
+
const metricsAdapter = instance(mock<IEntityMetricsAdapter>());
|
|
24
25
|
const queryContext = StubQueryContextProvider.getQueryContext();
|
|
25
26
|
|
|
26
27
|
const id1 = uuidv4();
|
|
@@ -71,7 +72,8 @@ describe(EntityLoader, () => {
|
|
|
71
72
|
testEntityConfiguration,
|
|
72
73
|
TestEntity,
|
|
73
74
|
privacyPolicy,
|
|
74
|
-
dataManager
|
|
75
|
+
dataManager,
|
|
76
|
+
metricsAdapter
|
|
75
77
|
);
|
|
76
78
|
const entity = await enforceAsyncResult(entityLoader.loadByIDAsync(id1));
|
|
77
79
|
expect(entity.getID()).toEqual(id1);
|
|
@@ -111,6 +113,7 @@ describe(EntityLoader, () => {
|
|
|
111
113
|
const privacyPolicy = new TestEntityPrivacyPolicy();
|
|
112
114
|
const spiedPrivacyPolicy = spy(privacyPolicy);
|
|
113
115
|
const viewerContext = instance(mock(ViewerContext));
|
|
116
|
+
const metricsAdapter = instance(mock<IEntityMetricsAdapter>());
|
|
114
117
|
const queryContext = StubQueryContextProvider.getQueryContext();
|
|
115
118
|
|
|
116
119
|
const id1 = uuidv4();
|
|
@@ -169,7 +172,8 @@ describe(EntityLoader, () => {
|
|
|
169
172
|
testEntityConfiguration,
|
|
170
173
|
TestEntity,
|
|
171
174
|
privacyPolicy,
|
|
172
|
-
dataManager
|
|
175
|
+
dataManager,
|
|
176
|
+
metricsAdapter
|
|
173
177
|
);
|
|
174
178
|
const entities = await enforceResultsAsync(
|
|
175
179
|
entityLoader.loadManyByFieldEqualityConjunctionAsync([
|
|
@@ -185,7 +189,12 @@ describe(EntityLoader, () => {
|
|
|
185
189
|
);
|
|
186
190
|
expect(entities).toHaveLength(2);
|
|
187
191
|
verify(
|
|
188
|
-
spiedPrivacyPolicy.authorizeReadAsync(
|
|
192
|
+
spiedPrivacyPolicy.authorizeReadAsync(
|
|
193
|
+
viewerContext,
|
|
194
|
+
queryContext,
|
|
195
|
+
anyOfClass(TestEntity),
|
|
196
|
+
anything()
|
|
197
|
+
)
|
|
189
198
|
).twice();
|
|
190
199
|
|
|
191
200
|
await expect(
|
|
@@ -200,6 +209,7 @@ describe(EntityLoader, () => {
|
|
|
200
209
|
const spiedPrivacyPolicy = spy(privacyPolicy);
|
|
201
210
|
|
|
202
211
|
const viewerContext = instance(mock(ViewerContext));
|
|
212
|
+
const metricsAdapter = instance(mock<IEntityMetricsAdapter>());
|
|
203
213
|
const queryContext = StubQueryContextProvider.getQueryContext();
|
|
204
214
|
|
|
205
215
|
const id1 = uuidv4();
|
|
@@ -240,14 +250,18 @@ describe(EntityLoader, () => {
|
|
|
240
250
|
testEntityConfiguration,
|
|
241
251
|
TestEntity,
|
|
242
252
|
privacyPolicy,
|
|
243
|
-
dataManager
|
|
253
|
+
dataManager,
|
|
254
|
+
metricsAdapter
|
|
244
255
|
);
|
|
245
256
|
const entity = await enforceAsyncResult(entityLoader.loadByIDAsync(id1));
|
|
246
|
-
verify(
|
|
257
|
+
verify(
|
|
258
|
+
spiedPrivacyPolicy.authorizeReadAsync(viewerContext, queryContext, entity, anything())
|
|
259
|
+
).once();
|
|
247
260
|
});
|
|
248
261
|
|
|
249
262
|
it('invalidates upon invalidate one', async () => {
|
|
250
263
|
const viewerContext = instance(mock(ViewerContext));
|
|
264
|
+
const metricsAdapter = instance(mock<IEntityMetricsAdapter>());
|
|
251
265
|
const queryContext = StubQueryContextProvider.getQueryContext();
|
|
252
266
|
const privacyPolicy = instance(mock(TestEntityPrivacyPolicy));
|
|
253
267
|
const dataManagerMock = mock<EntityDataManager<TestFields>>();
|
|
@@ -260,7 +274,8 @@ describe(EntityLoader, () => {
|
|
|
260
274
|
testEntityConfiguration,
|
|
261
275
|
TestEntity,
|
|
262
276
|
privacyPolicy,
|
|
263
|
-
dataManagerInstance
|
|
277
|
+
dataManagerInstance,
|
|
278
|
+
metricsAdapter
|
|
264
279
|
);
|
|
265
280
|
await entityLoader.invalidateFieldsAsync({ customIdField: id1 } as any);
|
|
266
281
|
|
|
@@ -271,6 +286,7 @@ describe(EntityLoader, () => {
|
|
|
271
286
|
|
|
272
287
|
it('invalidates upon invalidate by field', async () => {
|
|
273
288
|
const viewerContext = instance(mock(ViewerContext));
|
|
289
|
+
const metricsAdapter = instance(mock<IEntityMetricsAdapter>());
|
|
274
290
|
const queryContext = StubQueryContextProvider.getQueryContext();
|
|
275
291
|
const privacyPolicy = instance(mock(TestEntityPrivacyPolicy));
|
|
276
292
|
const dataManagerMock = mock<EntityDataManager<TestFields>>();
|
|
@@ -283,7 +299,8 @@ describe(EntityLoader, () => {
|
|
|
283
299
|
testEntityConfiguration,
|
|
284
300
|
TestEntity,
|
|
285
301
|
privacyPolicy,
|
|
286
|
-
dataManagerInstance
|
|
302
|
+
dataManagerInstance,
|
|
303
|
+
metricsAdapter
|
|
287
304
|
);
|
|
288
305
|
await entityLoader.invalidateFieldsAsync({ customIdField: id1 } as any);
|
|
289
306
|
verify(
|
|
@@ -293,6 +310,7 @@ describe(EntityLoader, () => {
|
|
|
293
310
|
|
|
294
311
|
it('invalidates upon invalidate by entity', async () => {
|
|
295
312
|
const viewerContext = instance(mock(ViewerContext));
|
|
313
|
+
const metricsAdapter = instance(mock<IEntityMetricsAdapter>());
|
|
296
314
|
const queryContext = StubQueryContextProvider.getQueryContext();
|
|
297
315
|
const privacyPolicy = instance(mock(TestEntityPrivacyPolicy));
|
|
298
316
|
const dataManagerMock = mock<EntityDataManager<TestFields>>();
|
|
@@ -309,7 +327,8 @@ describe(EntityLoader, () => {
|
|
|
309
327
|
testEntityConfiguration,
|
|
310
328
|
TestEntity,
|
|
311
329
|
privacyPolicy,
|
|
312
|
-
dataManagerInstance
|
|
330
|
+
dataManagerInstance,
|
|
331
|
+
metricsAdapter
|
|
313
332
|
);
|
|
314
333
|
await entityLoader.invalidateEntityAsync(entityInstance);
|
|
315
334
|
verify(
|
|
@@ -319,6 +338,7 @@ describe(EntityLoader, () => {
|
|
|
319
338
|
|
|
320
339
|
it('returns error result when not allowed', async () => {
|
|
321
340
|
const viewerContext = instance(mock(ViewerContext));
|
|
341
|
+
const metricsAdapter = instance(mock<IEntityMetricsAdapter>());
|
|
322
342
|
const queryContext = StubQueryContextProvider.getQueryContext();
|
|
323
343
|
const privacyPolicyMock = mock(TestEntityPrivacyPolicy);
|
|
324
344
|
const dataManagerMock = mock<EntityDataManager<TestFields>>();
|
|
@@ -331,7 +351,12 @@ describe(EntityLoader, () => {
|
|
|
331
351
|
const rejectionError = new Error();
|
|
332
352
|
|
|
333
353
|
when(
|
|
334
|
-
privacyPolicyMock.authorizeReadAsync(
|
|
354
|
+
privacyPolicyMock.authorizeReadAsync(
|
|
355
|
+
viewerContext,
|
|
356
|
+
queryContext,
|
|
357
|
+
anyOfClass(TestEntity),
|
|
358
|
+
anything()
|
|
359
|
+
)
|
|
335
360
|
).thenReject(rejectionError);
|
|
336
361
|
|
|
337
362
|
const privacyPolicy = instance(privacyPolicyMock);
|
|
@@ -343,7 +368,8 @@ describe(EntityLoader, () => {
|
|
|
343
368
|
testEntityConfiguration,
|
|
344
369
|
TestEntity,
|
|
345
370
|
privacyPolicy,
|
|
346
|
-
dataManagerInstance
|
|
371
|
+
dataManagerInstance,
|
|
372
|
+
metricsAdapter
|
|
347
373
|
);
|
|
348
374
|
|
|
349
375
|
const entityResult = await entityLoader.loadByIDAsync(id1);
|
|
@@ -354,6 +380,7 @@ describe(EntityLoader, () => {
|
|
|
354
380
|
|
|
355
381
|
it('throws upon database adapter error', async () => {
|
|
356
382
|
const viewerContext = instance(mock(ViewerContext));
|
|
383
|
+
const metricsAdapter = instance(mock<IEntityMetricsAdapter>());
|
|
357
384
|
const queryContext = StubQueryContextProvider.getQueryContext();
|
|
358
385
|
const privacyPolicy = instance(mock(TestEntityPrivacyPolicy));
|
|
359
386
|
const dataManagerMock = mock<EntityDataManager<TestFields>>();
|
|
@@ -372,7 +399,8 @@ describe(EntityLoader, () => {
|
|
|
372
399
|
testEntityConfiguration,
|
|
373
400
|
TestEntity,
|
|
374
401
|
privacyPolicy,
|
|
375
|
-
dataManagerInstance
|
|
402
|
+
dataManagerInstance,
|
|
403
|
+
metricsAdapter
|
|
376
404
|
);
|
|
377
405
|
|
|
378
406
|
const loadByValue = uuidv4();
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import Entity from '../Entity';
|
|
2
|
+
import { EntityCompanionDefinition } from '../EntityCompanionProvider';
|
|
3
|
+
import EntityConfiguration from '../EntityConfiguration';
|
|
4
|
+
import { UUIDField } from '../EntityFields';
|
|
5
|
+
import { EntityMutationType, EntityTriggerMutationInfo } from '../EntityMutationInfo';
|
|
6
|
+
import { EntityNonTransactionalMutationTrigger } from '../EntityMutationTriggerConfiguration';
|
|
7
|
+
import EntityPrivacyPolicy from '../EntityPrivacyPolicy';
|
|
8
|
+
import ViewerContext from '../ViewerContext';
|
|
9
|
+
import AlwaysAllowPrivacyPolicyRule from '../rules/AlwaysAllowPrivacyPolicyRule';
|
|
10
|
+
import { createUnitTestEntityCompanionProvider } from '../utils/testing/createUnitTestEntityCompanionProvider';
|
|
11
|
+
|
|
12
|
+
type BlahFields = {
|
|
13
|
+
id: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
class BlahEntityPrivacyPolicy extends EntityPrivacyPolicy<
|
|
17
|
+
BlahFields,
|
|
18
|
+
string,
|
|
19
|
+
ViewerContext,
|
|
20
|
+
BlahEntity
|
|
21
|
+
> {
|
|
22
|
+
protected override readonly createRules = [
|
|
23
|
+
new AlwaysAllowPrivacyPolicyRule<BlahFields, string, ViewerContext, BlahEntity>(),
|
|
24
|
+
];
|
|
25
|
+
protected override readonly readRules = [
|
|
26
|
+
new AlwaysAllowPrivacyPolicyRule<BlahFields, string, ViewerContext, BlahEntity>(),
|
|
27
|
+
];
|
|
28
|
+
protected override readonly updateRules = [
|
|
29
|
+
new AlwaysAllowPrivacyPolicyRule<BlahFields, string, ViewerContext, BlahEntity>(),
|
|
30
|
+
];
|
|
31
|
+
protected override readonly deleteRules = [
|
|
32
|
+
new AlwaysAllowPrivacyPolicyRule<BlahFields, string, ViewerContext, BlahEntity>(),
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
class BlahEntity extends Entity<BlahFields, string, ViewerContext> {
|
|
37
|
+
static getCompanionDefinition(): EntityCompanionDefinition<
|
|
38
|
+
BlahFields,
|
|
39
|
+
string,
|
|
40
|
+
ViewerContext,
|
|
41
|
+
BlahEntity,
|
|
42
|
+
BlahEntityPrivacyPolicy
|
|
43
|
+
> {
|
|
44
|
+
return blahCompanion;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const blahCompanion = new EntityCompanionDefinition({
|
|
49
|
+
entityClass: BlahEntity,
|
|
50
|
+
entityConfiguration: new EntityConfiguration<BlahFields>({
|
|
51
|
+
idField: 'id',
|
|
52
|
+
tableName: 'blah_table',
|
|
53
|
+
schema: {
|
|
54
|
+
id: new UUIDField({
|
|
55
|
+
columnName: 'id',
|
|
56
|
+
cache: true,
|
|
57
|
+
}),
|
|
58
|
+
},
|
|
59
|
+
databaseAdapterFlavor: 'postgres',
|
|
60
|
+
cacheAdapterFlavor: 'redis',
|
|
61
|
+
}),
|
|
62
|
+
privacyPolicyClass: BlahEntityPrivacyPolicy,
|
|
63
|
+
mutationTriggers: () => ({
|
|
64
|
+
afterCommit: [new TestNonTransactionalMutationTrigger()],
|
|
65
|
+
}),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
class TestNonTransactionalMutationTrigger extends EntityNonTransactionalMutationTrigger<
|
|
69
|
+
BlahFields,
|
|
70
|
+
string,
|
|
71
|
+
ViewerContext,
|
|
72
|
+
BlahEntity
|
|
73
|
+
> {
|
|
74
|
+
async executeAsync(
|
|
75
|
+
viewerContext: ViewerContext,
|
|
76
|
+
entity: BlahEntity,
|
|
77
|
+
mutationInfo: EntityTriggerMutationInfo<BlahFields, string, ViewerContext, BlahEntity>
|
|
78
|
+
): Promise<void> {
|
|
79
|
+
if (mutationInfo.type === EntityMutationType.DELETE) {
|
|
80
|
+
const entityLoaded = await BlahEntity.loader(viewerContext)
|
|
81
|
+
.enforcing()
|
|
82
|
+
.loadByIDNullableAsync(entity.getID());
|
|
83
|
+
if (entityLoaded) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
'should not have been able to re-load the entity after delete. this means the cache has not been cleared'
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
describe('EntityMutator', () => {
|
|
93
|
+
test('cache consistency with post-commit callbacks', async () => {
|
|
94
|
+
const companionProvider = createUnitTestEntityCompanionProvider();
|
|
95
|
+
const viewerContext = new ViewerContext(companionProvider);
|
|
96
|
+
|
|
97
|
+
// put it in cache
|
|
98
|
+
const entity = await BlahEntity.creator(viewerContext).enforceCreateAsync();
|
|
99
|
+
const entityLoaded = await BlahEntity.loader(viewerContext)
|
|
100
|
+
.enforcing()
|
|
101
|
+
.loadByIDAsync(entity.getID());
|
|
102
|
+
|
|
103
|
+
await BlahEntity.enforceDeleteAsync(entityLoaded);
|
|
104
|
+
});
|
|
105
|
+
});
|