@expo/entity 0.22.0 → 0.25.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/ComposedEntityCacheAdapter.d.ts +19 -0
- package/build/ComposedEntityCacheAdapter.js +66 -0
- package/build/ComposedEntityCacheAdapter.js.map +1 -0
- package/build/ComposedSecondaryEntityCache.d.ts +15 -0
- package/build/ComposedSecondaryEntityCache.js +37 -0
- package/build/ComposedSecondaryEntityCache.js.map +1 -0
- package/build/Entity.js +6 -6
- package/build/Entity.js.map +1 -1
- package/build/EntityAssociationLoader.js +4 -4
- package/build/EntityAssociationLoader.js.map +1 -1
- package/build/EntityCacheAdapter.d.ts +2 -9
- package/build/EntityCacheAdapter.js.map +1 -1
- package/build/EntityFieldDefinition.d.ts +8 -0
- package/build/EntityFieldDefinition.js +5 -0
- package/build/EntityFieldDefinition.js.map +1 -1
- package/build/EntityFields.d.ts +38 -0
- package/build/EntityFields.js +38 -0
- package/build/EntityFields.js.map +1 -1
- package/build/EntityLoader.d.ts +3 -2
- package/build/EntityLoader.js +5 -4
- package/build/EntityLoader.js.map +1 -1
- package/build/EntityLoaderFactory.d.ts +2 -2
- package/build/EntityLoaderFactory.js +2 -2
- package/build/EntityLoaderFactory.js.map +1 -1
- package/build/EntityMutationInfo.d.ts +12 -3
- package/build/EntityMutator.d.ts +5 -4
- package/build/EntityMutator.js +31 -24
- 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 +15 -4
- package/build/EntityPrivacyPolicy.js +14 -14
- package/build/EntityPrivacyPolicy.js.map +1 -1
- package/build/GenericSecondaryEntityCache.d.ts +19 -0
- package/build/GenericSecondaryEntityCache.js +74 -0
- package/build/GenericSecondaryEntityCache.js.map +1 -0
- package/build/IEntityGenericCacher.d.ts +11 -0
- package/build/IEntityGenericCacher.js +3 -0
- package/build/IEntityGenericCacher.js.map +1 -0
- package/build/ReadonlyEntity.js +1 -1
- package/build/ReadonlyEntity.js.map +1 -1
- package/build/ViewerScopedEntityLoaderFactory.d.ts +2 -2
- package/build/ViewerScopedEntityLoaderFactory.js +2 -2
- package/build/ViewerScopedEntityLoaderFactory.js.map +1 -1
- package/build/ViewerScopedEntityMutatorFactory.d.ts +4 -4
- package/build/ViewerScopedEntityMutatorFactory.js +6 -6
- package/build/ViewerScopedEntityMutatorFactory.js.map +1 -1
- package/build/__tests__/ComposedCacheAdapter-test.d.ts +1 -0
- package/build/__tests__/ComposedCacheAdapter-test.js +198 -0
- package/build/__tests__/ComposedCacheAdapter-test.js.map +1 -0
- package/build/__tests__/ComposedSecondaryEntityCache-test.d.ts +1 -0
- package/build/__tests__/ComposedSecondaryEntityCache-test.js +65 -0
- package/build/__tests__/ComposedSecondaryEntityCache-test.js.map +1 -0
- package/build/__tests__/EntityCommonUseCases-test.js +1 -1
- package/build/__tests__/EntityCommonUseCases-test.js.map +1 -1
- package/build/__tests__/EntityEdges-test.js +260 -37
- package/build/__tests__/EntityEdges-test.js.map +1 -1
- package/build/__tests__/EntityLoader-constructor-test.js +2 -1
- package/build/__tests__/EntityLoader-constructor-test.js.map +1 -1
- package/build/__tests__/EntityLoader-test.js +19 -11
- package/build/__tests__/EntityLoader-test.js.map +1 -1
- package/build/__tests__/EntityMutator-test.js +96 -43
- package/build/__tests__/EntityMutator-test.js.map +1 -1
- package/build/__tests__/EntityPrivacyPolicy-test.js +23 -12
- package/build/__tests__/EntityPrivacyPolicy-test.js.map +1 -1
- package/build/__tests__/EntitySecondaryCacheLoader-test.js +1 -1
- package/build/__tests__/EntitySecondaryCacheLoader-test.js.map +1 -1
- package/build/__tests__/ViewerScopedEntityLoaderFactory-test.js +3 -2
- package/build/__tests__/ViewerScopedEntityLoaderFactory-test.js.map +1 -1
- package/build/__tests__/ViewerScopedEntityMutatorFactory-test.js +3 -2
- package/build/__tests__/ViewerScopedEntityMutatorFactory-test.js.map +1 -1
- package/build/index.d.ts +2 -0
- package/build/index.js +3 -1
- package/build/index.js.map +1 -1
- package/build/internal/ReadThroughEntityCache.d.ts +2 -3
- package/build/internal/ReadThroughEntityCache.js +2 -6
- package/build/internal/ReadThroughEntityCache.js.map +1 -1
- package/build/internal/__tests__/ReadThroughEntityCache-test.js +0 -32
- package/build/internal/__tests__/ReadThroughEntityCache-test.js.map +1 -1
- package/build/rules/AlwaysAllowPrivacyPolicyRule.d.ts +2 -1
- package/build/rules/AlwaysAllowPrivacyPolicyRule.js +1 -1
- package/build/rules/AlwaysAllowPrivacyPolicyRule.js.map +1 -1
- package/build/rules/AlwaysDenyPrivacyPolicyRule.d.ts +2 -1
- package/build/rules/AlwaysDenyPrivacyPolicyRule.js +1 -1
- package/build/rules/AlwaysDenyPrivacyPolicyRule.js.map +1 -1
- package/build/rules/AlwaysSkipPrivacyPolicyRule.d.ts +2 -1
- package/build/rules/AlwaysSkipPrivacyPolicyRule.js +1 -1
- package/build/rules/AlwaysSkipPrivacyPolicyRule.js.map +1 -1
- package/build/rules/PrivacyPolicyRule.d.ts +2 -1
- package/build/rules/PrivacyPolicyRule.js.map +1 -1
- package/build/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.js +1 -0
- package/build/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.js.map +1 -1
- package/build/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.js +1 -0
- package/build/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.js.map +1 -1
- package/build/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.js +1 -0
- package/build/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.js.map +1 -1
- package/build/utils/testing/PrivacyPolicyRuleTestUtils.d.ts +2 -0
- package/build/utils/testing/PrivacyPolicyRuleTestUtils.js +6 -6
- package/build/utils/testing/PrivacyPolicyRuleTestUtils.js.map +1 -1
- package/build/utils/testing/StubCacheAdapter.d.ts +6 -9
- package/build/utils/testing/StubCacheAdapter.js +0 -6
- package/build/utils/testing/StubCacheAdapter.js.map +1 -1
- package/package.json +1 -1
- package/src/ComposedEntityCacheAdapter.ts +86 -0
- package/src/ComposedSecondaryEntityCache.ts +63 -0
- package/src/Entity.ts +6 -4
- package/src/EntityAssociationLoader.ts +4 -4
- package/src/EntityCacheAdapter.ts +2 -10
- package/src/EntityFieldDefinition.ts +8 -0
- package/src/EntityFields.ts +45 -0
- package/src/EntityLoader.ts +5 -1
- package/src/EntityLoaderFactory.ts +4 -2
- package/src/EntityMutationInfo.ts +13 -3
- package/src/EntityMutator.ts +44 -21
- package/src/EntityMutatorFactory.ts +10 -4
- package/src/EntityPrivacyPolicy.ts +31 -1
- package/src/GenericSecondaryEntityCache.ts +98 -0
- package/src/IEntityGenericCacher.ts +15 -0
- package/src/ReadonlyEntity.ts +1 -1
- package/src/ViewerScopedEntityLoaderFactory.ts +8 -3
- package/src/ViewerScopedEntityMutatorFactory.ts +22 -7
- package/src/__tests__/ComposedCacheAdapter-test.ts +280 -0
- package/src/__tests__/ComposedSecondaryEntityCache-test.ts +101 -0
- package/src/__tests__/EntityCommonUseCases-test.ts +2 -1
- package/src/__tests__/EntityEdges-test.ts +286 -45
- package/src/__tests__/EntityLoader-constructor-test.ts +3 -1
- package/src/__tests__/EntityLoader-test.ts +26 -1
- package/src/__tests__/EntityMutator-test.ts +99 -37
- package/src/__tests__/EntityPrivacyPolicy-test.ts +66 -7
- package/src/__tests__/EntitySecondaryCacheLoader-test.ts +1 -0
- package/src/__tests__/ViewerScopedEntityLoaderFactory-test.ts +4 -2
- package/src/__tests__/ViewerScopedEntityMutatorFactory-test.ts +6 -2
- package/src/index.ts +2 -0
- package/src/internal/ReadThroughEntityCache.ts +6 -28
- package/src/internal/__tests__/ReadThroughEntityCache-test.ts +0 -44
- package/src/rules/AlwaysAllowPrivacyPolicyRule.ts +2 -0
- package/src/rules/AlwaysDenyPrivacyPolicyRule.ts +2 -0
- package/src/rules/AlwaysSkipPrivacyPolicyRule.ts +2 -0
- package/src/rules/PrivacyPolicyRule.ts +2 -0
- package/src/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.ts +2 -0
- package/src/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.ts +2 -0
- package/src/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.ts +2 -0
- package/src/utils/testing/PrivacyPolicyRuleTestUtils.ts +14 -6
- package/src/utils/testing/StubCacheAdapter.ts +11 -17
|
@@ -8,6 +8,7 @@ import EntityPrivacyPolicy, {
|
|
|
8
8
|
EntityPrivacyPolicyEvaluator,
|
|
9
9
|
EntityAuthorizationAction,
|
|
10
10
|
EntityPrivacyPolicyEvaluationMode,
|
|
11
|
+
EntityPrivacyPolicyEvaluationContext,
|
|
11
12
|
} from '../EntityPrivacyPolicy';
|
|
12
13
|
import { EntityQueryContext } from '../EntityQueryContext';
|
|
13
14
|
import ViewerContext from '../ViewerContext';
|
|
@@ -166,6 +167,7 @@ class AlwaysThrowPrivacyPolicyRule extends PrivacyPolicyRule<
|
|
|
166
167
|
evaluateAsync(
|
|
167
168
|
_viewerContext: ViewerContext,
|
|
168
169
|
_queryContext: EntityQueryContext,
|
|
170
|
+
_evaluationContext: EntityPrivacyPolicyEvaluationContext,
|
|
169
171
|
_entity: BlahEntity
|
|
170
172
|
): Promise<RuleEvaluationResult> {
|
|
171
173
|
throw new Error('WooHoo!');
|
|
@@ -245,12 +247,19 @@ describe(EntityPrivacyPolicy, () => {
|
|
|
245
247
|
it('throws EntityNotAuthorizedError when deny', async () => {
|
|
246
248
|
const viewerContext = instance(mock(ViewerContext));
|
|
247
249
|
const queryContext = instance(mock(EntityQueryContext));
|
|
250
|
+
const privacyPolicyEvaluationContext = instance(mock<EntityPrivacyPolicyEvaluationContext>());
|
|
248
251
|
const metricsAdapterMock = mock<IEntityMetricsAdapter>();
|
|
249
252
|
const metricsAdapter = instance(metricsAdapterMock);
|
|
250
253
|
const entity = new BlahEntity(viewerContext, { id: '1' });
|
|
251
254
|
const policy = new AlwaysDenyPolicy();
|
|
252
255
|
await expect(
|
|
253
|
-
policy.authorizeCreateAsync(
|
|
256
|
+
policy.authorizeCreateAsync(
|
|
257
|
+
viewerContext,
|
|
258
|
+
queryContext,
|
|
259
|
+
privacyPolicyEvaluationContext,
|
|
260
|
+
entity,
|
|
261
|
+
metricsAdapter
|
|
262
|
+
)
|
|
254
263
|
).rejects.toBeInstanceOf(EntityNotAuthorizedError);
|
|
255
264
|
verify(
|
|
256
265
|
metricsAdapterMock.logAuthorizationEvent(
|
|
@@ -267,6 +276,7 @@ describe(EntityPrivacyPolicy, () => {
|
|
|
267
276
|
it('returns entity when allowed', async () => {
|
|
268
277
|
const viewerContext = instance(mock(ViewerContext));
|
|
269
278
|
const queryContext = instance(mock(EntityQueryContext));
|
|
279
|
+
const privacyPolicyEvaluationContext = instance(mock<EntityPrivacyPolicyEvaluationContext>());
|
|
270
280
|
const metricsAdapterMock = mock<IEntityMetricsAdapter>();
|
|
271
281
|
const metricsAdapter = instance(metricsAdapterMock);
|
|
272
282
|
const entity = new BlahEntity(viewerContext, { id: '1' });
|
|
@@ -274,6 +284,7 @@ describe(EntityPrivacyPolicy, () => {
|
|
|
274
284
|
const approvedEntity = await policy.authorizeCreateAsync(
|
|
275
285
|
viewerContext,
|
|
276
286
|
queryContext,
|
|
287
|
+
privacyPolicyEvaluationContext,
|
|
277
288
|
entity,
|
|
278
289
|
metricsAdapter
|
|
279
290
|
);
|
|
@@ -293,12 +304,19 @@ describe(EntityPrivacyPolicy, () => {
|
|
|
293
304
|
it('throws EntityNotAuthorizedError when all skipped', async () => {
|
|
294
305
|
const viewerContext = instance(mock(ViewerContext));
|
|
295
306
|
const queryContext = instance(mock(EntityQueryContext));
|
|
307
|
+
const privacyPolicyEvaluationContext = instance(mock<EntityPrivacyPolicyEvaluationContext>());
|
|
296
308
|
const metricsAdapterMock = mock<IEntityMetricsAdapter>();
|
|
297
309
|
const metricsAdapter = instance(metricsAdapterMock);
|
|
298
310
|
const entity = new BlahEntity(viewerContext, { id: '1' });
|
|
299
311
|
const policy = new SkipAllPolicy();
|
|
300
312
|
await expect(
|
|
301
|
-
policy.authorizeCreateAsync(
|
|
313
|
+
policy.authorizeCreateAsync(
|
|
314
|
+
viewerContext,
|
|
315
|
+
queryContext,
|
|
316
|
+
privacyPolicyEvaluationContext,
|
|
317
|
+
entity,
|
|
318
|
+
metricsAdapter
|
|
319
|
+
)
|
|
302
320
|
).rejects.toBeInstanceOf(EntityNotAuthorizedError);
|
|
303
321
|
verify(
|
|
304
322
|
metricsAdapterMock.logAuthorizationEvent(
|
|
@@ -315,12 +333,19 @@ describe(EntityPrivacyPolicy, () => {
|
|
|
315
333
|
it('throws EntityNotAuthorizedError when empty policy', async () => {
|
|
316
334
|
const viewerContext = instance(mock(ViewerContext));
|
|
317
335
|
const queryContext = instance(mock(EntityQueryContext));
|
|
336
|
+
const privacyPolicyEvaluationContext = instance(mock<EntityPrivacyPolicyEvaluationContext>());
|
|
318
337
|
const metricsAdapterMock = mock<IEntityMetricsAdapter>();
|
|
319
338
|
const metricsAdapter = instance(metricsAdapterMock);
|
|
320
339
|
const entity = new BlahEntity(viewerContext, { id: '1' });
|
|
321
340
|
const policy = new EmptyPolicy();
|
|
322
341
|
await expect(
|
|
323
|
-
policy.authorizeCreateAsync(
|
|
342
|
+
policy.authorizeCreateAsync(
|
|
343
|
+
viewerContext,
|
|
344
|
+
queryContext,
|
|
345
|
+
privacyPolicyEvaluationContext,
|
|
346
|
+
entity,
|
|
347
|
+
metricsAdapter
|
|
348
|
+
)
|
|
324
349
|
).rejects.toBeInstanceOf(EntityNotAuthorizedError);
|
|
325
350
|
verify(
|
|
326
351
|
metricsAdapterMock.logAuthorizationEvent(
|
|
@@ -337,12 +362,19 @@ describe(EntityPrivacyPolicy, () => {
|
|
|
337
362
|
it('throws when rule throws', async () => {
|
|
338
363
|
const viewerContext = instance(mock(ViewerContext));
|
|
339
364
|
const queryContext = instance(mock(EntityQueryContext));
|
|
365
|
+
const privacyPolicyEvaluationContext = instance(mock<EntityPrivacyPolicyEvaluationContext>());
|
|
340
366
|
const metricsAdapterMock = mock<IEntityMetricsAdapter>();
|
|
341
367
|
const metricsAdapter = instance(metricsAdapterMock);
|
|
342
368
|
const entity = new BlahEntity(viewerContext, { id: '1' });
|
|
343
369
|
const policy = new ThrowAllPolicy();
|
|
344
370
|
await expect(
|
|
345
|
-
policy.authorizeCreateAsync(
|
|
371
|
+
policy.authorizeCreateAsync(
|
|
372
|
+
viewerContext,
|
|
373
|
+
queryContext,
|
|
374
|
+
privacyPolicyEvaluationContext,
|
|
375
|
+
entity,
|
|
376
|
+
metricsAdapter
|
|
377
|
+
)
|
|
346
378
|
).rejects.toThrowError('WooHoo!');
|
|
347
379
|
verify(metricsAdapterMock.logAuthorizationEvent(anything())).never();
|
|
348
380
|
});
|
|
@@ -352,6 +384,7 @@ describe(EntityPrivacyPolicy, () => {
|
|
|
352
384
|
it('returns entity when denied but calls denialHandler', async () => {
|
|
353
385
|
const viewerContext = instance(mock(ViewerContext));
|
|
354
386
|
const queryContext = instance(mock(EntityQueryContext));
|
|
387
|
+
const privacyPolicyEvaluationContext = instance(mock<EntityPrivacyPolicyEvaluationContext>());
|
|
355
388
|
const metricsAdapterMock = mock<IEntityMetricsAdapter>();
|
|
356
389
|
const metricsAdapter = instance(metricsAdapterMock);
|
|
357
390
|
const entity = new BlahEntity(viewerContext, { id: '1' });
|
|
@@ -362,6 +395,7 @@ describe(EntityPrivacyPolicy, () => {
|
|
|
362
395
|
const approvedEntity = await policy.authorizeCreateAsync(
|
|
363
396
|
viewerContext,
|
|
364
397
|
queryContext,
|
|
398
|
+
privacyPolicyEvaluationContext,
|
|
365
399
|
entity,
|
|
366
400
|
metricsAdapter
|
|
367
401
|
);
|
|
@@ -384,6 +418,7 @@ describe(EntityPrivacyPolicy, () => {
|
|
|
384
418
|
it('does not log when not denied', async () => {
|
|
385
419
|
const viewerContext = instance(mock(ViewerContext));
|
|
386
420
|
const queryContext = instance(mock(EntityQueryContext));
|
|
421
|
+
const privacyPolicyEvaluationContext = instance(mock<EntityPrivacyPolicyEvaluationContext>());
|
|
387
422
|
const metricsAdapterMock = mock<IEntityMetricsAdapter>();
|
|
388
423
|
const metricsAdapter = instance(metricsAdapterMock);
|
|
389
424
|
const entity = new BlahEntity(viewerContext, { id: '1' });
|
|
@@ -394,6 +429,7 @@ describe(EntityPrivacyPolicy, () => {
|
|
|
394
429
|
const approvedEntity = await policy.authorizeCreateAsync(
|
|
395
430
|
viewerContext,
|
|
396
431
|
queryContext,
|
|
432
|
+
privacyPolicyEvaluationContext,
|
|
397
433
|
entity,
|
|
398
434
|
metricsAdapter
|
|
399
435
|
);
|
|
@@ -416,6 +452,7 @@ describe(EntityPrivacyPolicy, () => {
|
|
|
416
452
|
it('passes through other errors', async () => {
|
|
417
453
|
const viewerContext = instance(mock(ViewerContext));
|
|
418
454
|
const queryContext = instance(mock(EntityQueryContext));
|
|
455
|
+
const privacyPolicyEvaluationContext = instance(mock<EntityPrivacyPolicyEvaluationContext>());
|
|
419
456
|
const metricsAdapterMock = mock<IEntityMetricsAdapter>();
|
|
420
457
|
const metricsAdapter = instance(metricsAdapterMock);
|
|
421
458
|
const entity = new BlahEntity(viewerContext, { id: '1' });
|
|
@@ -424,7 +461,13 @@ describe(EntityPrivacyPolicy, () => {
|
|
|
424
461
|
const policySpy = spy(policy);
|
|
425
462
|
|
|
426
463
|
await expect(
|
|
427
|
-
policy.authorizeCreateAsync(
|
|
464
|
+
policy.authorizeCreateAsync(
|
|
465
|
+
viewerContext,
|
|
466
|
+
queryContext,
|
|
467
|
+
privacyPolicyEvaluationContext,
|
|
468
|
+
entity,
|
|
469
|
+
metricsAdapter
|
|
470
|
+
)
|
|
428
471
|
).rejects.toThrowError('WooHoo!');
|
|
429
472
|
|
|
430
473
|
verify(policySpy.denyHandler(anyOfClass(EntityNotAuthorizedError))).never();
|
|
@@ -437,6 +480,7 @@ describe(EntityPrivacyPolicy, () => {
|
|
|
437
480
|
it('denies when denied but calls denialHandler', async () => {
|
|
438
481
|
const viewerContext = instance(mock(ViewerContext));
|
|
439
482
|
const queryContext = instance(mock(EntityQueryContext));
|
|
483
|
+
const privacyPolicyEvaluationContext = instance(mock<EntityPrivacyPolicyEvaluationContext>());
|
|
440
484
|
const metricsAdapterMock = mock<IEntityMetricsAdapter>();
|
|
441
485
|
const metricsAdapter = instance(metricsAdapterMock);
|
|
442
486
|
const entity = new BlahEntity(viewerContext, { id: '1' });
|
|
@@ -445,7 +489,13 @@ describe(EntityPrivacyPolicy, () => {
|
|
|
445
489
|
const policySpy = spy(policy);
|
|
446
490
|
|
|
447
491
|
await expect(
|
|
448
|
-
policy.authorizeCreateAsync(
|
|
492
|
+
policy.authorizeCreateAsync(
|
|
493
|
+
viewerContext,
|
|
494
|
+
queryContext,
|
|
495
|
+
privacyPolicyEvaluationContext,
|
|
496
|
+
entity,
|
|
497
|
+
metricsAdapter
|
|
498
|
+
)
|
|
449
499
|
).rejects.toBeInstanceOf(EntityNotAuthorizedError);
|
|
450
500
|
|
|
451
501
|
verify(policySpy.denyHandler(anyOfClass(EntityNotAuthorizedError))).once();
|
|
@@ -465,6 +515,7 @@ describe(EntityPrivacyPolicy, () => {
|
|
|
465
515
|
it('does not log when not denied', async () => {
|
|
466
516
|
const viewerContext = instance(mock(ViewerContext));
|
|
467
517
|
const queryContext = instance(mock(EntityQueryContext));
|
|
518
|
+
const privacyPolicyEvaluationContext = instance(mock<EntityPrivacyPolicyEvaluationContext>());
|
|
468
519
|
const metricsAdapterMock = mock<IEntityMetricsAdapter>();
|
|
469
520
|
const metricsAdapter = instance(metricsAdapterMock);
|
|
470
521
|
const entity = new BlahEntity(viewerContext, { id: '1' });
|
|
@@ -475,6 +526,7 @@ describe(EntityPrivacyPolicy, () => {
|
|
|
475
526
|
const approvedEntity = await policy.authorizeCreateAsync(
|
|
476
527
|
viewerContext,
|
|
477
528
|
queryContext,
|
|
529
|
+
privacyPolicyEvaluationContext,
|
|
478
530
|
entity,
|
|
479
531
|
metricsAdapter
|
|
480
532
|
);
|
|
@@ -497,6 +549,7 @@ describe(EntityPrivacyPolicy, () => {
|
|
|
497
549
|
it('passes through other errors', async () => {
|
|
498
550
|
const viewerContext = instance(mock(ViewerContext));
|
|
499
551
|
const queryContext = instance(mock(EntityQueryContext));
|
|
552
|
+
const privacyPolicyEvaluationContext = instance(mock<EntityPrivacyPolicyEvaluationContext>());
|
|
500
553
|
const metricsAdapterMock = mock<IEntityMetricsAdapter>();
|
|
501
554
|
const metricsAdapter = instance(metricsAdapterMock);
|
|
502
555
|
const entity = new BlahEntity(viewerContext, { id: '1' });
|
|
@@ -505,7 +558,13 @@ describe(EntityPrivacyPolicy, () => {
|
|
|
505
558
|
const policySpy = spy(policy);
|
|
506
559
|
|
|
507
560
|
await expect(
|
|
508
|
-
policy.authorizeCreateAsync(
|
|
561
|
+
policy.authorizeCreateAsync(
|
|
562
|
+
viewerContext,
|
|
563
|
+
queryContext,
|
|
564
|
+
privacyPolicyEvaluationContext,
|
|
565
|
+
entity,
|
|
566
|
+
metricsAdapter
|
|
567
|
+
)
|
|
509
568
|
).rejects.toThrowError('WooHoo!');
|
|
510
569
|
|
|
511
570
|
verify(policySpy.denyHandler(anyOfClass(EntityNotAuthorizedError))).never();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mock, verify, instance } from 'ts-mockito';
|
|
2
2
|
|
|
3
3
|
import EntityLoaderFactory from '../EntityLoaderFactory';
|
|
4
|
+
import { EntityPrivacyPolicyEvaluationContext } from '../EntityPrivacyPolicy';
|
|
4
5
|
import { EntityQueryContext } from '../EntityQueryContext';
|
|
5
6
|
import ViewerContext from '../ViewerContext';
|
|
6
7
|
import ViewerScopedEntityLoaderFactory from '../ViewerScopedEntityLoaderFactory';
|
|
@@ -8,6 +9,7 @@ import ViewerScopedEntityLoaderFactory from '../ViewerScopedEntityLoaderFactory'
|
|
|
8
9
|
describe(ViewerScopedEntityLoaderFactory, () => {
|
|
9
10
|
it('correctly scopes viewer to entity loads', async () => {
|
|
10
11
|
const viewerContext = instance(mock(ViewerContext));
|
|
12
|
+
const privacyPolicyEvaluationContext = instance(mock<EntityPrivacyPolicyEvaluationContext>());
|
|
11
13
|
const queryContext = instance(mock(EntityQueryContext));
|
|
12
14
|
const baseLoader = mock<EntityLoaderFactory<any, any, any, any, any, any>>(EntityLoaderFactory);
|
|
13
15
|
const baseLoaderInstance = instance(baseLoader);
|
|
@@ -21,8 +23,8 @@ describe(ViewerScopedEntityLoaderFactory, () => {
|
|
|
21
23
|
any
|
|
22
24
|
>(baseLoaderInstance, viewerContext);
|
|
23
25
|
|
|
24
|
-
viewerScopedEntityLoader.forLoad(queryContext);
|
|
26
|
+
viewerScopedEntityLoader.forLoad(queryContext, privacyPolicyEvaluationContext);
|
|
25
27
|
|
|
26
|
-
verify(baseLoader.forLoad(viewerContext, queryContext)).once();
|
|
28
|
+
verify(baseLoader.forLoad(viewerContext, queryContext, privacyPolicyEvaluationContext)).once();
|
|
27
29
|
});
|
|
28
30
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mock, instance, verify } from 'ts-mockito';
|
|
2
2
|
|
|
3
3
|
import EntityMutatorFactory from '../EntityMutatorFactory';
|
|
4
|
+
import { EntityPrivacyPolicyEvaluationContext } from '../EntityPrivacyPolicy';
|
|
4
5
|
import { EntityQueryContext } from '../EntityQueryContext';
|
|
5
6
|
import ViewerContext from '../ViewerContext';
|
|
6
7
|
import ViewerScopedEntityMutatorFactory from '../ViewerScopedEntityMutatorFactory';
|
|
@@ -9,6 +10,7 @@ import TestEntity, { TestFields, TestEntityPrivacyPolicy } from '../testfixtures
|
|
|
9
10
|
describe(ViewerScopedEntityMutatorFactory, () => {
|
|
10
11
|
it('correctly scopes viewer to entity mutations', async () => {
|
|
11
12
|
const viewerContext = instance(mock(ViewerContext));
|
|
13
|
+
const privacyPolicyEvaluationContext = instance(mock<EntityPrivacyPolicyEvaluationContext>());
|
|
12
14
|
const queryContext = instance(mock(EntityQueryContext));
|
|
13
15
|
const baseMutatorFactory =
|
|
14
16
|
mock<
|
|
@@ -25,8 +27,10 @@ describe(ViewerScopedEntityMutatorFactory, () => {
|
|
|
25
27
|
keyof TestFields
|
|
26
28
|
>(baseMutatorFactoryInstance, viewerContext);
|
|
27
29
|
|
|
28
|
-
viewerScopedEntityLoader.forCreate(queryContext);
|
|
30
|
+
viewerScopedEntityLoader.forCreate(queryContext, privacyPolicyEvaluationContext);
|
|
29
31
|
|
|
30
|
-
verify(
|
|
32
|
+
verify(
|
|
33
|
+
baseMutatorFactory.forCreate(viewerContext, queryContext, privacyPolicyEvaluationContext)
|
|
34
|
+
).once();
|
|
31
35
|
});
|
|
32
36
|
});
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* @module @expo/entity
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
export { default as GenericSecondaryEntityCache } from './GenericSecondaryEntityCache';
|
|
7
8
|
export { default as EnforcingEntityLoader } from './EnforcingEntityLoader';
|
|
8
9
|
export { default as Entity } from './Entity';
|
|
9
10
|
export * from './Entity';
|
|
@@ -43,6 +44,7 @@ export * from './EntityQueryContext';
|
|
|
43
44
|
export { default as IEntityCacheAdapterProvider } from './IEntityCacheAdapterProvider';
|
|
44
45
|
export { default as IEntityDatabaseAdapterProvider } from './IEntityDatabaseAdapterProvider';
|
|
45
46
|
export { default as EntityQueryContextProvider } from './EntityQueryContextProvider';
|
|
47
|
+
export { default as IEntityGenericCacher } from './IEntityGenericCacher';
|
|
46
48
|
export { default as ReadonlyEntity } from './ReadonlyEntity';
|
|
47
49
|
export { default as ViewerContext } from './ViewerContext';
|
|
48
50
|
export { default as ViewerScopedEntityCompanion } from './ViewerScopedEntityCompanion';
|
|
@@ -3,11 +3,6 @@ import invariant from 'invariant';
|
|
|
3
3
|
import EntityCacheAdapter from '../EntityCacheAdapter';
|
|
4
4
|
import EntityConfiguration from '../EntityConfiguration';
|
|
5
5
|
import { filterMap } from '../utils/collections/maps';
|
|
6
|
-
import {
|
|
7
|
-
FieldTransformerMap,
|
|
8
|
-
transformCacheObjectToFields,
|
|
9
|
-
transformFieldsToCacheObject,
|
|
10
|
-
} from './EntityFieldTransformationUtils';
|
|
11
6
|
|
|
12
7
|
export enum CacheStatus {
|
|
13
8
|
HIT,
|
|
@@ -15,10 +10,10 @@ export enum CacheStatus {
|
|
|
15
10
|
NEGATIVE,
|
|
16
11
|
}
|
|
17
12
|
|
|
18
|
-
export type CacheLoadResult =
|
|
13
|
+
export type CacheLoadResult<TFields> =
|
|
19
14
|
| {
|
|
20
15
|
status: CacheStatus.HIT;
|
|
21
|
-
item: Readonly<
|
|
16
|
+
item: Readonly<TFields>;
|
|
22
17
|
}
|
|
23
18
|
| {
|
|
24
19
|
status: CacheStatus.MISS;
|
|
@@ -32,14 +27,10 @@ export type CacheLoadResult =
|
|
|
32
27
|
* {@link EntityCacheAdapter} within the {@link EntityDataManager}.
|
|
33
28
|
*/
|
|
34
29
|
export default class ReadThroughEntityCache<TFields> {
|
|
35
|
-
private readonly fieldTransformerMap: FieldTransformerMap;
|
|
36
|
-
|
|
37
30
|
constructor(
|
|
38
31
|
private readonly entityConfiguration: EntityConfiguration<TFields>,
|
|
39
32
|
private readonly entityCacheAdapter: EntityCacheAdapter<TFields>
|
|
40
|
-
) {
|
|
41
|
-
this.fieldTransformerMap = entityCacheAdapter.getFieldTransformerMap();
|
|
42
|
-
}
|
|
33
|
+
) {}
|
|
43
34
|
|
|
44
35
|
private isFieldCacheable<N extends keyof TFields>(fieldName: N): boolean {
|
|
45
36
|
return this.entityConfiguration.cacheableKeys.has(fieldName);
|
|
@@ -91,13 +82,7 @@ export default class ReadThroughEntityCache<TFields> {
|
|
|
91
82
|
const results: Map<NonNullable<TFields[N]>, readonly Readonly<TFields>[]> = new Map();
|
|
92
83
|
cacheLoadResults.forEach((cacheLoadResult, fieldValue) => {
|
|
93
84
|
if (cacheLoadResult.status === CacheStatus.HIT) {
|
|
94
|
-
results.set(fieldValue, [
|
|
95
|
-
transformCacheObjectToFields(
|
|
96
|
-
this.entityConfiguration,
|
|
97
|
-
this.fieldTransformerMap,
|
|
98
|
-
cacheLoadResult.item
|
|
99
|
-
),
|
|
100
|
-
]);
|
|
85
|
+
results.set(fieldValue, [cacheLoadResult.item]);
|
|
101
86
|
}
|
|
102
87
|
});
|
|
103
88
|
|
|
@@ -110,7 +95,7 @@ export default class ReadThroughEntityCache<TFields> {
|
|
|
110
95
|
return !objectsFromFulfillerForFv || objectsFromFulfillerForFv.length === 0;
|
|
111
96
|
});
|
|
112
97
|
|
|
113
|
-
const objectsToCache: Map<NonNullable<TFields[N]>,
|
|
98
|
+
const objectsToCache: Map<NonNullable<TFields[N]>, Readonly<TFields>> = new Map();
|
|
114
99
|
for (const [fieldValue, objects] of dbFetchResults.entries()) {
|
|
115
100
|
if (objects.length > 1) {
|
|
116
101
|
// multiple objects received for what was supposed to be a unique query, don't add to return map nor cache
|
|
@@ -123,14 +108,7 @@ export default class ReadThroughEntityCache<TFields> {
|
|
|
123
108
|
}
|
|
124
109
|
const uniqueObject = objects[0];
|
|
125
110
|
if (uniqueObject) {
|
|
126
|
-
objectsToCache.set(
|
|
127
|
-
fieldValue,
|
|
128
|
-
transformFieldsToCacheObject(
|
|
129
|
-
this.entityConfiguration,
|
|
130
|
-
this.fieldTransformerMap,
|
|
131
|
-
uniqueObject
|
|
132
|
-
)
|
|
133
|
-
);
|
|
111
|
+
objectsToCache.set(fieldValue, uniqueObject);
|
|
134
112
|
results.set(fieldValue, [uniqueObject]);
|
|
135
113
|
}
|
|
136
114
|
}
|
|
@@ -40,7 +40,6 @@ describe(ReadThroughEntityCache, () => {
|
|
|
40
40
|
describe('readManyThroughAsync', () => {
|
|
41
41
|
it('fetches from DB upon cache miss and caches the result', async () => {
|
|
42
42
|
const cacheAdapterMock = mock<EntityCacheAdapter<BlahFields>>();
|
|
43
|
-
when(cacheAdapterMock.getFieldTransformerMap()).thenReturn(new Map());
|
|
44
43
|
const cacheAdapter = instance(cacheAdapterMock);
|
|
45
44
|
const entityCache = new ReadThroughEntityCache(makeEntityConfiguration(true), cacheAdapter);
|
|
46
45
|
const fetcher = createIdFetcher(['wat', 'who']);
|
|
@@ -77,7 +76,6 @@ describe(ReadThroughEntityCache, () => {
|
|
|
77
76
|
|
|
78
77
|
it('does not fetch from the DB or cache results when all cache fetches are hits', async () => {
|
|
79
78
|
const cacheAdapterMock = mock<EntityCacheAdapter<BlahFields>>();
|
|
80
|
-
when(cacheAdapterMock.getFieldTransformerMap()).thenReturn(new Map());
|
|
81
79
|
const cacheAdapter = instance(cacheAdapterMock);
|
|
82
80
|
const entityCache = new ReadThroughEntityCache(makeEntityConfiguration(true), cacheAdapter);
|
|
83
81
|
const fetcher = createIdFetcher(['wat', 'who']);
|
|
@@ -114,7 +112,6 @@ describe(ReadThroughEntityCache, () => {
|
|
|
114
112
|
|
|
115
113
|
it('negatively caches db misses', async () => {
|
|
116
114
|
const cacheAdapterMock = mock<EntityCacheAdapter<BlahFields>>();
|
|
117
|
-
when(cacheAdapterMock.getFieldTransformerMap()).thenReturn(new Map());
|
|
118
115
|
const cacheAdapter = instance(cacheAdapterMock);
|
|
119
116
|
const entityCache = new ReadThroughEntityCache(makeEntityConfiguration(true), cacheAdapter);
|
|
120
117
|
|
|
@@ -135,7 +132,6 @@ describe(ReadThroughEntityCache, () => {
|
|
|
135
132
|
|
|
136
133
|
it('does not return or fetch negatively cached results from DB', async () => {
|
|
137
134
|
const cacheAdapterMock = mock<EntityCacheAdapter<BlahFields>>();
|
|
138
|
-
when(cacheAdapterMock.getFieldTransformerMap()).thenReturn(new Map());
|
|
139
135
|
const cacheAdapter = instance(cacheAdapterMock);
|
|
140
136
|
const entityCache = new ReadThroughEntityCache(makeEntityConfiguration(true), cacheAdapter);
|
|
141
137
|
const fetcher = createIdFetcher([]);
|
|
@@ -153,7 +149,6 @@ describe(ReadThroughEntityCache, () => {
|
|
|
153
149
|
|
|
154
150
|
it('does a mix and match of hit, miss, and negative', async () => {
|
|
155
151
|
const cacheAdapterMock = mock<EntityCacheAdapter<BlahFields>>();
|
|
156
|
-
when(cacheAdapterMock.getFieldTransformerMap()).thenReturn(new Map());
|
|
157
152
|
const cacheAdapter = instance(cacheAdapterMock);
|
|
158
153
|
const entityCache = new ReadThroughEntityCache(makeEntityConfiguration(true), cacheAdapter);
|
|
159
154
|
const fetcher = createIdFetcher(['wat', 'who', 'why']);
|
|
@@ -189,7 +184,6 @@ describe(ReadThroughEntityCache, () => {
|
|
|
189
184
|
|
|
190
185
|
it('does not call into cache for field that is not cacheable', async () => {
|
|
191
186
|
const cacheAdapterMock = mock<EntityCacheAdapter<BlahFields>>();
|
|
192
|
-
when(cacheAdapterMock.getFieldTransformerMap()).thenReturn(new Map());
|
|
193
187
|
const cacheAdapter = instance(cacheAdapterMock);
|
|
194
188
|
const entityCache = new ReadThroughEntityCache(makeEntityConfiguration(false), cacheAdapter);
|
|
195
189
|
const fetcher = createIdFetcher(['wat']);
|
|
@@ -197,44 +191,6 @@ describe(ReadThroughEntityCache, () => {
|
|
|
197
191
|
verify(cacheAdapterMock.loadManyAsync('id', anything())).never();
|
|
198
192
|
expect(result).toEqual(new Map([['wat', [{ id: 'wat' }]]]));
|
|
199
193
|
});
|
|
200
|
-
|
|
201
|
-
it('transforms fields for cache storage', async () => {
|
|
202
|
-
const cacheAdapterMock = mock<EntityCacheAdapter<BlahFields>>();
|
|
203
|
-
when(cacheAdapterMock.getFieldTransformerMap()).thenReturn(
|
|
204
|
-
new Map([
|
|
205
|
-
[
|
|
206
|
-
UUIDField.name,
|
|
207
|
-
{
|
|
208
|
-
read: (val) => val.split('-')[0],
|
|
209
|
-
write: (val) => `${val}-in-cache`,
|
|
210
|
-
},
|
|
211
|
-
],
|
|
212
|
-
])
|
|
213
|
-
);
|
|
214
|
-
const cacheAdapter = instance(cacheAdapterMock);
|
|
215
|
-
const entityCache = new ReadThroughEntityCache(makeEntityConfiguration(true), cacheAdapter);
|
|
216
|
-
const fetcher = createIdFetcher(['wat', 'who']);
|
|
217
|
-
|
|
218
|
-
when(cacheAdapterMock.loadManyAsync('id', deepEqual(['wat', 'who']))).thenResolve(
|
|
219
|
-
new Map([
|
|
220
|
-
['wat', { status: CacheStatus.MISS }],
|
|
221
|
-
['who', { status: CacheStatus.HIT, item: { id: 'who-in-cache' } }],
|
|
222
|
-
])
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
const result = await entityCache.readManyThroughAsync('id', ['wat', 'who'], fetcher);
|
|
226
|
-
|
|
227
|
-
verify(cacheAdapterMock.loadManyAsync('id', deepEqual(['wat', 'who']))).once();
|
|
228
|
-
verify(
|
|
229
|
-
cacheAdapterMock.cacheManyAsync('id', deepEqual(new Map([['wat', { id: 'wat-in-cache' }]])))
|
|
230
|
-
).once();
|
|
231
|
-
expect(result).toEqual(
|
|
232
|
-
new Map([
|
|
233
|
-
['wat', [{ id: 'wat' }]],
|
|
234
|
-
['who', [{ id: 'who' }]],
|
|
235
|
-
])
|
|
236
|
-
);
|
|
237
|
-
});
|
|
238
194
|
});
|
|
239
195
|
|
|
240
196
|
describe('invalidateManyAsync', () => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EntityPrivacyPolicyEvaluationContext } from '../EntityPrivacyPolicy';
|
|
1
2
|
import { EntityQueryContext } from '../EntityQueryContext';
|
|
2
3
|
import ReadonlyEntity from '../ReadonlyEntity';
|
|
3
4
|
import ViewerContext from '../ViewerContext';
|
|
@@ -16,6 +17,7 @@ export default class AlwaysAllowPrivacyPolicyRule<
|
|
|
16
17
|
async evaluateAsync(
|
|
17
18
|
_viewerContext: TViewerContext,
|
|
18
19
|
_queryContext: EntityQueryContext,
|
|
20
|
+
_evaluationContext: EntityPrivacyPolicyEvaluationContext,
|
|
19
21
|
_entity: TEntity
|
|
20
22
|
): Promise<RuleEvaluationResult> {
|
|
21
23
|
return RuleEvaluationResult.ALLOW;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EntityPrivacyPolicyEvaluationContext } from '../EntityPrivacyPolicy';
|
|
1
2
|
import { EntityQueryContext } from '../EntityQueryContext';
|
|
2
3
|
import ReadonlyEntity from '../ReadonlyEntity';
|
|
3
4
|
import ViewerContext from '../ViewerContext';
|
|
@@ -16,6 +17,7 @@ export default class AlwaysDenyPrivacyPolicyRule<
|
|
|
16
17
|
async evaluateAsync(
|
|
17
18
|
_viewerContext: TViewerContext,
|
|
18
19
|
_queryContext: EntityQueryContext,
|
|
20
|
+
_evaluationContext: EntityPrivacyPolicyEvaluationContext,
|
|
19
21
|
_entity: TEntity
|
|
20
22
|
): Promise<RuleEvaluationResult> {
|
|
21
23
|
return RuleEvaluationResult.DENY;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EntityPrivacyPolicyEvaluationContext } from '../EntityPrivacyPolicy';
|
|
1
2
|
import { EntityQueryContext } from '../EntityQueryContext';
|
|
2
3
|
import ReadonlyEntity from '../ReadonlyEntity';
|
|
3
4
|
import ViewerContext from '../ViewerContext';
|
|
@@ -16,6 +17,7 @@ export default class AlwaysSkipPrivacyPolicyRule<
|
|
|
16
17
|
async evaluateAsync(
|
|
17
18
|
_viewerContext: TViewerContext,
|
|
18
19
|
_queryContext: EntityQueryContext,
|
|
20
|
+
_evaluationContext: EntityPrivacyPolicyEvaluationContext,
|
|
19
21
|
_entity: TEntity
|
|
20
22
|
): Promise<RuleEvaluationResult> {
|
|
21
23
|
return RuleEvaluationResult.SKIP;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EntityPrivacyPolicyEvaluationContext } from '../EntityPrivacyPolicy';
|
|
1
2
|
import { EntityQueryContext } from '../EntityQueryContext';
|
|
2
3
|
import ReadonlyEntity from '../ReadonlyEntity';
|
|
3
4
|
import ViewerContext from '../ViewerContext';
|
|
@@ -45,6 +46,7 @@ export default abstract class PrivacyPolicyRule<
|
|
|
45
46
|
abstract evaluateAsync(
|
|
46
47
|
viewerContext: TViewerContext,
|
|
47
48
|
queryContext: EntityQueryContext,
|
|
49
|
+
evaluationContext: EntityPrivacyPolicyEvaluationContext,
|
|
48
50
|
entity: TEntity
|
|
49
51
|
): Promise<RuleEvaluationResult>;
|
|
50
52
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mock, instance, anything } from 'ts-mockito';
|
|
2
2
|
|
|
3
|
+
import { EntityPrivacyPolicyEvaluationContext } from '../../EntityPrivacyPolicy';
|
|
3
4
|
import { EntityQueryContext } from '../../EntityQueryContext';
|
|
4
5
|
import ViewerContext from '../../ViewerContext';
|
|
5
6
|
import { describePrivacyPolicyRule } from '../../utils/testing/PrivacyPolicyRuleTestUtils';
|
|
@@ -10,6 +11,7 @@ describePrivacyPolicyRule(new AlwaysAllowPrivacyPolicyRule(), {
|
|
|
10
11
|
{
|
|
11
12
|
viewerContext: instance(mock(ViewerContext)),
|
|
12
13
|
queryContext: instance(mock(EntityQueryContext)),
|
|
14
|
+
evaluationContext: instance(mock<EntityPrivacyPolicyEvaluationContext>()),
|
|
13
15
|
entity: anything(),
|
|
14
16
|
},
|
|
15
17
|
],
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mock, instance, anything } from 'ts-mockito';
|
|
2
2
|
|
|
3
|
+
import { EntityPrivacyPolicyEvaluationContext } from '../../EntityPrivacyPolicy';
|
|
3
4
|
import { EntityQueryContext } from '../../EntityQueryContext';
|
|
4
5
|
import ViewerContext from '../../ViewerContext';
|
|
5
6
|
import { describePrivacyPolicyRule } from '../../utils/testing/PrivacyPolicyRuleTestUtils';
|
|
@@ -10,6 +11,7 @@ describePrivacyPolicyRule(new AlwaysDenyPrivacyPolicyRule(), {
|
|
|
10
11
|
{
|
|
11
12
|
viewerContext: instance(mock(ViewerContext)),
|
|
12
13
|
queryContext: instance(mock(EntityQueryContext)),
|
|
14
|
+
evaluationContext: instance(mock<EntityPrivacyPolicyEvaluationContext>()),
|
|
13
15
|
entity: anything(),
|
|
14
16
|
},
|
|
15
17
|
],
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mock, instance, anything } from 'ts-mockito';
|
|
2
2
|
|
|
3
|
+
import { EntityPrivacyPolicyEvaluationContext } from '../../EntityPrivacyPolicy';
|
|
3
4
|
import { EntityQueryContext } from '../../EntityQueryContext';
|
|
4
5
|
import ViewerContext from '../../ViewerContext';
|
|
5
6
|
import { describePrivacyPolicyRule } from '../../utils/testing/PrivacyPolicyRuleTestUtils';
|
|
@@ -10,6 +11,7 @@ describePrivacyPolicyRule(new AlwaysSkipPrivacyPolicyRule(), {
|
|
|
10
11
|
{
|
|
11
12
|
viewerContext: instance(mock(ViewerContext)),
|
|
12
13
|
queryContext: instance(mock(EntityQueryContext)),
|
|
14
|
+
evaluationContext: instance(mock<EntityPrivacyPolicyEvaluationContext>()),
|
|
13
15
|
entity: anything(),
|
|
14
16
|
},
|
|
15
17
|
],
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EntityPrivacyPolicyEvaluationContext } from '../../EntityPrivacyPolicy';
|
|
1
2
|
import { EntityQueryContext } from '../../EntityQueryContext';
|
|
2
3
|
import ReadonlyEntity from '../../ReadonlyEntity';
|
|
3
4
|
import ViewerContext from '../../ViewerContext';
|
|
@@ -12,6 +13,7 @@ export interface Case<
|
|
|
12
13
|
> {
|
|
13
14
|
viewerContext: TViewerContext;
|
|
14
15
|
queryContext: EntityQueryContext;
|
|
16
|
+
evaluationContext: EntityPrivacyPolicyEvaluationContext;
|
|
15
17
|
entity: TEntity;
|
|
16
18
|
}
|
|
17
19
|
|
|
@@ -48,9 +50,11 @@ export const describePrivacyPolicyRuleWithAsyncTestCase = <
|
|
|
48
50
|
if (allowCases && allowCases.size > 0) {
|
|
49
51
|
describe('allow cases', () => {
|
|
50
52
|
test.each(Array.from(allowCases.keys()))('%p', async (caseKey) => {
|
|
51
|
-
const { viewerContext, queryContext, entity } = await allowCases.get(
|
|
53
|
+
const { viewerContext, queryContext, evaluationContext, entity } = await allowCases.get(
|
|
54
|
+
caseKey
|
|
55
|
+
)!();
|
|
52
56
|
await expect(
|
|
53
|
-
privacyPolicyRule.evaluateAsync(viewerContext, queryContext, entity)
|
|
57
|
+
privacyPolicyRule.evaluateAsync(viewerContext, queryContext, evaluationContext, entity)
|
|
54
58
|
).resolves.toEqual(RuleEvaluationResult.ALLOW);
|
|
55
59
|
});
|
|
56
60
|
});
|
|
@@ -59,9 +63,11 @@ export const describePrivacyPolicyRuleWithAsyncTestCase = <
|
|
|
59
63
|
if (skipCases && skipCases.size > 0) {
|
|
60
64
|
describe('skip cases', () => {
|
|
61
65
|
test.each(Array.from(skipCases.keys()))('%p', async (caseKey) => {
|
|
62
|
-
const { viewerContext, queryContext, entity } = await skipCases.get(
|
|
66
|
+
const { viewerContext, queryContext, evaluationContext, entity } = await skipCases.get(
|
|
67
|
+
caseKey
|
|
68
|
+
)!();
|
|
63
69
|
await expect(
|
|
64
|
-
privacyPolicyRule.evaluateAsync(viewerContext, queryContext, entity)
|
|
70
|
+
privacyPolicyRule.evaluateAsync(viewerContext, queryContext, evaluationContext, entity)
|
|
65
71
|
).resolves.toEqual(RuleEvaluationResult.SKIP);
|
|
66
72
|
});
|
|
67
73
|
});
|
|
@@ -70,9 +76,11 @@ export const describePrivacyPolicyRuleWithAsyncTestCase = <
|
|
|
70
76
|
if (denyCases && denyCases.size > 0) {
|
|
71
77
|
describe('deny cases', () => {
|
|
72
78
|
test.each(Array.from(denyCases.keys()))('%p', async (caseKey) => {
|
|
73
|
-
const { viewerContext, queryContext, entity } = await denyCases.get(
|
|
79
|
+
const { viewerContext, queryContext, evaluationContext, entity } = await denyCases.get(
|
|
80
|
+
caseKey
|
|
81
|
+
)!();
|
|
74
82
|
await expect(
|
|
75
|
-
privacyPolicyRule.evaluateAsync(viewerContext, queryContext, entity)
|
|
83
|
+
privacyPolicyRule.evaluateAsync(viewerContext, queryContext, evaluationContext, entity)
|
|
76
84
|
).resolves.toEqual(RuleEvaluationResult.DENY);
|
|
77
85
|
});
|
|
78
86
|
});
|