Skip to content

Commit

Permalink
chore: internal refactor of loader to be structured like new mutation…
Browse files Browse the repository at this point in the history
… methods (#254)
  • Loading branch information
wschurman authored Feb 24, 2025
1 parent 4d56065 commit 92bac05
Show file tree
Hide file tree
Showing 18 changed files with 925 additions and 863 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,13 @@ class TestSecondaryLocalMemoryCacheLoader extends EntitySecondaryCacheLoader<
}
return nullthrows(
(
await this.entityLoader
.enforcing()
.loadManyByFieldEqualityConjunctionAsync([
{ fieldName: 'id', fieldValue: loadParams.id },
])
await this.entityLoader.loadManyByFieldEqualityConjunctionAsync([
{ fieldName: 'id', fieldValue: loadParams.id },
])
)[0],
).getAllFields();
)
.enforceValue()
.getAllFields();
});
}
}
Expand All @@ -182,7 +182,7 @@ describe(LocalMemorySecondaryEntityCache, () => {
localMemoryTestEntityConfiguration,
GenericLocalMemoryCacher.createLRUCache<LocalMemoryTestEntityFields>({}),
),
LocalMemoryTestEntity.loader(viewerContext),
LocalMemoryTestEntity.loader(viewerContext).withAuthorizationResults(),
);

const loadParams = { id: createdEntity.getID() };
Expand Down Expand Up @@ -218,7 +218,7 @@ describe(LocalMemorySecondaryEntityCache, () => {
localMemoryTestEntityConfiguration,
GenericLocalMemoryCacher.createLRUCache<LocalMemoryTestEntityFields>({}),
),
LocalMemoryTestEntity.loader(viewerContext),
LocalMemoryTestEntity.loader(viewerContext).withAuthorizationResults(),
);

const loadParams = { id: FAKE_ID };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ class TestSecondaryRedisCacheLoader extends EntitySecondaryCacheLoader<
}
return nullthrows(
(
await this.entityLoader
.enforcing()
.loadManyByFieldEqualityConjunctionAsync([
{ fieldName: 'id', fieldValue: loadParams.id },
])
await this.entityLoader.loadManyByFieldEqualityConjunctionAsync([
{ fieldName: 'id', fieldValue: loadParams.id },
])
)[0],
).getAllFields();
)
.enforceValue()
.getAllFields();
});
}
}
Expand Down Expand Up @@ -90,7 +90,7 @@ describe(RedisSecondaryEntityCache, () => {
genericRedisCacheContext,
(loadParams) => `test-key-${loadParams.id}`,
),
RedisTestEntity.loader(viewerContext),
RedisTestEntity.loader(viewerContext).withAuthorizationResults(),
);

const loadParams = { id: createdEntity.getID() };
Expand Down Expand Up @@ -129,7 +129,7 @@ describe(RedisSecondaryEntityCache, () => {
genericRedisCacheContext,
(loadParams) => `test-key-${loadParams.id}`,
),
RedisTestEntity.loader(viewerContext),
RedisTestEntity.loader(viewerContext).withAuthorizationResults(),
);

const loadParams = { id: FAKE_ID };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export default class AuthorizationResultBasedEntityLoader<
>,
private readonly dataManager: EntityDataManager<TFields>,
protected readonly metricsAdapter: IEntityMetricsAdapter,
private readonly utils: EntityLoaderUtils<
public readonly utils: EntityLoaderUtils<
TFields,
TID,
TViewerContext,
Expand Down
62 changes: 33 additions & 29 deletions packages/entity/src/AuthorizationResultBasedEntityMutator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import EntityMutationValidator from './EntityMutationValidator';
import EntityPrivacyPolicy from './EntityPrivacyPolicy';
import { EntityQueryContext, EntityTransactionalQueryContext } from './EntityQueryContext';
import ViewerContext from './ViewerContext';
import { enforceResultsAsync } from './entityUtils';
import EntityInvalidFieldValueError from './errors/EntityInvalidFieldValueError';
import { timeAndLogMutationEventAsync } from './metrics/EntityMetricsUtils';
import IEntityMetricsAdapter, { EntityMetricsMutationType } from './metrics/IEntityMetricsAdapter';
Expand Down Expand Up @@ -216,7 +217,7 @@ export class AuthorizationResultBasedCreateMutator<
cascadingDeleteCause: null,
});

const temporaryEntityForPrivacyCheck = entityLoader.utils().constructEntity({
const temporaryEntityForPrivacyCheck = entityLoader.utils.constructEntity({
[this.entityConfiguration.idField]: '00000000-0000-0000-0000-000000000000', // zero UUID
...this.fieldsForEntity,
} as unknown as TFields);
Expand Down Expand Up @@ -256,13 +257,13 @@ export class AuthorizationResultBasedCreateMutator<
const insertResult = await this.databaseAdapter.insertAsync(queryContext, this.fieldsForEntity);

queryContext.appendPostCommitInvalidationCallback(
entityLoader.utils().invalidateFieldsAsync.bind(entityLoader, insertResult),
entityLoader.utils.invalidateFieldsAsync.bind(entityLoader, insertResult),
);

const unauthorizedEntityAfterInsert = entityLoader.utils().constructEntity(insertResult);
const newEntity = await entityLoader
.enforcing()
.loadByIDAsync(unauthorizedEntityAfterInsert.getID());
const unauthorizedEntityAfterInsert = entityLoader.utils.constructEntity(insertResult);
const newEntity = await enforceAsyncResult(
entityLoader.loadByIDAsync(unauthorizedEntityAfterInsert.getID()),
);

await this.executeMutationTriggersAsync(
this.mutationTriggers.afterCreate,
Expand Down Expand Up @@ -429,7 +430,7 @@ export class AuthorizationResultBasedUpdateMutator<
cascadingDeleteCause,
});

const entityAboutToBeUpdated = entityLoader.utils().constructEntity(this.fieldsForEntity);
const entityAboutToBeUpdated = entityLoader.utils.constructEntity(this.fieldsForEntity);
const authorizeUpdateResult = await asyncResult(
this.privacyPolicy.authorizeUpdateAsync(
this.viewerContext,
Expand Down Expand Up @@ -473,17 +474,18 @@ export class AuthorizationResultBasedUpdateMutator<
}

queryContext.appendPostCommitInvalidationCallback(
entityLoader
.utils()
.invalidateFieldsAsync.bind(entityLoader, this.originalEntity.getAllDatabaseFields()),
entityLoader.utils.invalidateFieldsAsync.bind(
entityLoader,
this.originalEntity.getAllDatabaseFields(),
),
);
queryContext.appendPostCommitInvalidationCallback(
entityLoader.utils().invalidateFieldsAsync.bind(entityLoader, this.fieldsForEntity),
entityLoader.utils.invalidateFieldsAsync.bind(entityLoader, this.fieldsForEntity),
);

const updatedEntity = await entityLoader
.enforcing()
.loadByIDAsync(entityAboutToBeUpdated.getID()); // ID is guaranteed to be stable by ensureStableIDField
const updatedEntity = await enforceAsyncResult(
entityLoader.loadByIDAsync(entityAboutToBeUpdated.getID()),
); // ID is guaranteed to be stable by ensureStableIDField

await this.executeMutationTriggersAsync(
this.mutationTriggers.afterUpdate,
Expand Down Expand Up @@ -681,9 +683,10 @@ export class AuthorizationResultBasedDeleteMutator<
cascadingDeleteCause,
});
queryContext.appendPostCommitInvalidationCallback(
entityLoader
.utils()
.invalidateFieldsAsync.bind(entityLoader, this.entity.getAllDatabaseFields()),
entityLoader.utils.invalidateFieldsAsync.bind(
entityLoader,
this.entity.getAllDatabaseFields(),
),
);

await this.executeMutationTriggersAsync(
Expand Down Expand Up @@ -782,18 +785,19 @@ export class AuthorizationResultBasedDeleteMutator<
return;
}

const inboundReferenceEntities = await loaderFactory
.forLoad(queryContext, {
previousValue: null,
cascadingDeleteCause: newCascadingDeleteCause,
})
.enforcing()
.loadManyByFieldEqualingAsync(
fieldName,
association.associatedEntityLookupByField
? entity.getField(association.associatedEntityLookupByField as any)
: entity.getID(),
);
const inboundReferenceEntities = await enforceResultsAsync(
loaderFactory
.forLoad(queryContext, {
previousValue: null,
cascadingDeleteCause: newCascadingDeleteCause,
})
.loadManyByFieldEqualingAsync(
fieldName,
association.associatedEntityLookupByField
? entity.getField(association.associatedEntityLookupByField as any)
: entity.getID(),
),
);

switch (association.edgeDeletionBehavior) {
case EntityEdgeDeletionBehavior.CASCADE_DELETE_INVALIDATE_CACHE_ONLY: {
Expand Down
25 changes: 13 additions & 12 deletions packages/entity/src/EntityAssociationLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,7 @@ export default class EntityAssociationLoader<
.getLoaderFactory()
.forLoad(queryContext, { previousValue: null, cascadingDeleteCause: null });

return (await loader
.withAuthorizationResults()
.loadByIDAsync(associatedEntityID as unknown as TAssociatedID)) as Result<
return (await loader.loadByIDAsync(associatedEntityID as unknown as TAssociatedID)) as Result<
null extends TFields[TIdentifyingField] ? TAssociatedEntity | null : TAssociatedEntity
>;
}
Expand Down Expand Up @@ -131,9 +129,10 @@ export default class EntityAssociationLoader<
.getViewerScopedEntityCompanionForClass(associatedEntityClass)
.getLoaderFactory()
.forLoad(queryContext, { previousValue: null, cascadingDeleteCause: null });
return await loader
.withAuthorizationResults()
.loadManyByFieldEqualingAsync(associatedEntityFieldContainingThisID, thisID as any);
return await loader.loadManyByFieldEqualingAsync(
associatedEntityFieldContainingThisID,
thisID as any,
);
}

/**
Expand Down Expand Up @@ -187,9 +186,10 @@ export default class EntityAssociationLoader<
.getViewerScopedEntityCompanionForClass(associatedEntityClass)
.getLoaderFactory()
.forLoad(queryContext, { previousValue: null, cascadingDeleteCause: null });
return await loader
.withAuthorizationResults()
.loadByFieldEqualingAsync(associatedEntityLookupByField, associatedFieldValue as any);
return await loader.loadByFieldEqualingAsync(
associatedEntityLookupByField,
associatedFieldValue as any,
);
}

/**
Expand Down Expand Up @@ -244,9 +244,10 @@ export default class EntityAssociationLoader<
.getViewerScopedEntityCompanionForClass(associatedEntityClass)
.getLoaderFactory()
.forLoad(queryContext, { previousValue: null, cascadingDeleteCause: null });
return await loader
.withAuthorizationResults()
.loadManyByFieldEqualingAsync(associatedEntityLookupByField, associatedFieldValue as any);
return await loader.loadManyByFieldEqualingAsync(
associatedEntityLookupByField,
associatedFieldValue as any,
);
}

/**
Expand Down
57 changes: 9 additions & 48 deletions packages/entity/src/EntityLoader.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import AuthorizationResultBasedEntityLoader from './AuthorizationResultBasedEntityLoader';
import EnforcingEntityLoader from './EnforcingEntityLoader';
import { IEntityClass } from './Entity';
import EntityConfiguration from './EntityConfiguration';
import EntityLoaderUtils from './EntityLoaderUtils';
import EntityPrivacyPolicy, { EntityPrivacyPolicyEvaluationContext } from './EntityPrivacyPolicy';
import EntityPrivacyPolicy from './EntityPrivacyPolicy';
import { EntityQueryContext } from './EntityQueryContext';
import ReadonlyEntity from './ReadonlyEntity';
import ViewerContext from './ViewerContext';
import EntityDataManager from './internal/EntityDataManager';
import IEntityMetricsAdapter from './metrics/IEntityMetricsAdapter';

/**
* The primary interface for loading entities. All normal loads are batched,
Expand All @@ -18,6 +15,7 @@ export default class EntityLoader<
TFields extends object,
TID extends NonNullable<TFields[TSelectedFields]>,
TViewerContext extends ViewerContext,
TViewerContext2 extends TViewerContext,
TEntity extends ReadonlyEntity<TFields, TID, TViewerContext, TSelectedFields>,
TPrivacyPolicy extends EntityPrivacyPolicy<
TFields,
Expand All @@ -28,26 +26,9 @@ export default class EntityLoader<
>,
TSelectedFields extends keyof TFields,
> {
private readonly utilsPrivate: EntityLoaderUtils<
TFields,
TID,
TViewerContext,
TEntity,
TPrivacyPolicy,
TSelectedFields
>;

constructor(
private readonly viewerContext: TViewerContext,
private readonly viewerContext: TViewerContext2,
private readonly queryContext: EntityQueryContext,
private readonly privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext<
TFields,
TID,
TViewerContext,
TEntity,
TSelectedFields
>,
private readonly entityConfiguration: EntityConfiguration<TFields>,
private readonly entityClass: IEntityClass<
TFields,
TID,
Expand All @@ -56,23 +37,7 @@ export default class EntityLoader<
TPrivacyPolicy,
TSelectedFields
>,
private readonly entitySelectedFields: TSelectedFields[] | undefined,
private readonly privacyPolicy: TPrivacyPolicy,
private readonly dataManager: EntityDataManager<TFields>,
protected readonly metricsAdapter: IEntityMetricsAdapter,
) {
this.utilsPrivate = new EntityLoaderUtils(
this.viewerContext,
this.queryContext,
this.privacyPolicyEvaluationContext,
this.entityConfiguration,
this.entityClass,
this.entitySelectedFields,
this.privacyPolicy,
this.dataManager,
this.metricsAdapter,
);
}
) {}

/**
* Enforcing entity loader. All loads through this loader are
Expand Down Expand Up @@ -103,14 +68,10 @@ export default class EntityLoader<
TPrivacyPolicy,
TSelectedFields
> {
return new AuthorizationResultBasedEntityLoader(
this.queryContext,
this.entityConfiguration,
this.entityClass,
this.dataManager,
this.metricsAdapter,
this.utilsPrivate,
);
return this.viewerContext
.getViewerScopedEntityCompanionForClass(this.entityClass)
.getLoaderFactory()
.forLoad(this.queryContext, { previousValue: null, cascadingDeleteCause: null });
}

/**
Expand All @@ -125,6 +86,6 @@ export default class EntityLoader<
TPrivacyPolicy,
TSelectedFields
> {
return this.utilsPrivate;
return this.withAuthorizationResults().utils;
}
}
23 changes: 20 additions & 3 deletions packages/entity/src/EntityLoaderFactory.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import AuthorizationResultBasedEntityLoader from './AuthorizationResultBasedEntityLoader';
import EntityCompanion from './EntityCompanion';
import EntityLoader from './EntityLoader';
import EntityLoaderUtils from './EntityLoaderUtils';
import EntityPrivacyPolicy, { EntityPrivacyPolicyEvaluationContext } from './EntityPrivacyPolicy';
import { EntityQueryContext } from './EntityQueryContext';
import ReadonlyEntity from './ReadonlyEntity';
Expand Down Expand Up @@ -52,8 +53,15 @@ export default class EntityLoaderFactory<
TEntity,
TSelectedFields
>,
): EntityLoader<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
return new EntityLoader(
): AuthorizationResultBasedEntityLoader<
TFields,
TID,
TViewerContext,
TEntity,
TPrivacyPolicy,
TSelectedFields
> {
const utils = new EntityLoaderUtils(
viewerContext,
queryContext,
privacyPolicyEvaluationContext,
Expand All @@ -64,5 +72,14 @@ export default class EntityLoaderFactory<
this.dataManager,
this.metricsAdapter,
);

return new AuthorizationResultBasedEntityLoader(
queryContext,
this.entityCompanion.entityCompanionDefinition.entityConfiguration,
this.entityCompanion.entityCompanionDefinition.entityClass,
this.dataManager,
this.metricsAdapter,
utils,
);
}
}
Loading

0 comments on commit 92bac05

Please sign in to comment.