mirror of
https://github.com/expo/entity.git
synced 2026-11-13 20:28:06 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa4e2484c9 |
No files matched your search
@@ -79,7 +79,7 @@ export default class EnforcingEntityLoader<
|
||||
uniqueFieldName,
|
||||
fieldValue
|
||||
);
|
||||
return entityResult ? entityResult.enforceValue() : null;
|
||||
return entityResult.enforceValue();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,7 +98,7 @@ export default class EnforcingEntityLoader<
|
||||
*/
|
||||
async loadByIDNullableAsync(id: TID): Promise<TEntity | null> {
|
||||
const entityResult = await this.entityLoader.loadByIDNullableAsync(id);
|
||||
return entityResult ? entityResult.enforceValue() : null;
|
||||
return entityResult.enforceValue();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,6 +23,7 @@ export default class EntityAssociationLoader<
|
||||
* @param queryContext - query context in which to perform the load
|
||||
*/
|
||||
async loadAssociatedEntityAsync<
|
||||
TIdentifyingField extends keyof Pick<TFields, TSelectedFields>,
|
||||
TAssociatedFields,
|
||||
TAssociatedID,
|
||||
TAssociatedEntity extends ReadonlyEntity<
|
||||
@@ -40,7 +41,7 @@ export default class EntityAssociationLoader<
|
||||
>,
|
||||
TAssociatedSelectedFields extends keyof TAssociatedFields = keyof TAssociatedFields
|
||||
>(
|
||||
fieldIdentifyingAssociatedEntity: keyof Pick<TFields, TSelectedFields>,
|
||||
fieldIdentifyingAssociatedEntity: TIdentifyingField,
|
||||
associatedEntityClass: IEntityClass<
|
||||
TAssociatedFields,
|
||||
TAssociatedID,
|
||||
@@ -54,14 +55,20 @@ export default class EntityAssociationLoader<
|
||||
.getViewerScopedEntityCompanionForClass(associatedEntityClass)
|
||||
.getQueryContextProvider()
|
||||
.getQueryContext()
|
||||
): Promise<Result<TAssociatedEntity>> {
|
||||
): Promise<
|
||||
Result<null extends TFields[TIdentifyingField] ? TAssociatedEntity | null : TAssociatedEntity>
|
||||
> {
|
||||
const associatedEntityID = this.entity.getField(fieldIdentifyingAssociatedEntity);
|
||||
if (!associatedEntityID) {
|
||||
return result(null) as any;
|
||||
}
|
||||
|
||||
const loader = this.entity
|
||||
.getViewerContext()
|
||||
.getViewerScopedEntityCompanionForClass(associatedEntityClass)
|
||||
.getLoaderFactory()
|
||||
.forLoad(queryContext);
|
||||
return await loader.loadByIDAsync((associatedEntityID as unknown) as TAssociatedID);
|
||||
return (await loader.loadByIDAsync((associatedEntityID as unknown) as TAssociatedID)) as any;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,6 +134,7 @@ export default class EntityAssociationLoader<
|
||||
* @param queryContext - query context in which to perform the load
|
||||
*/
|
||||
async loadAssociatedEntityByFieldEqualingAsync<
|
||||
TIdentifyingField extends keyof Pick<TFields, TSelectedFields>,
|
||||
TAssociatedFields,
|
||||
TAssociatedID,
|
||||
TAssociatedEntity extends ReadonlyEntity<
|
||||
@@ -144,7 +152,7 @@ export default class EntityAssociationLoader<
|
||||
>,
|
||||
TAssociatedSelectedFields extends keyof TAssociatedFields = keyof TAssociatedFields
|
||||
>(
|
||||
fieldIdentifyingAssociatedEntity: keyof Pick<TFields, TSelectedFields>,
|
||||
fieldIdentifyingAssociatedEntity: TIdentifyingField,
|
||||
associatedEntityClass: IEntityClass<
|
||||
TAssociatedFields,
|
||||
TAssociatedID,
|
||||
@@ -159,8 +167,12 @@ export default class EntityAssociationLoader<
|
||||
.getViewerScopedEntityCompanionForClass(associatedEntityClass)
|
||||
.getQueryContextProvider()
|
||||
.getQueryContext()
|
||||
): Promise<Result<TAssociatedEntity> | null> {
|
||||
): Promise<Result<TAssociatedEntity | null>> {
|
||||
const associatedFieldValue = this.entity.getField(fieldIdentifyingAssociatedEntity);
|
||||
if (!associatedFieldValue) {
|
||||
return result(null);
|
||||
}
|
||||
|
||||
const loader = this.entity
|
||||
.getViewerContext()
|
||||
.getViewerScopedEntityCompanionForClass(associatedEntityClass)
|
||||
@@ -215,6 +227,10 @@ export default class EntityAssociationLoader<
|
||||
.getQueryContext()
|
||||
): Promise<readonly Result<TAssociatedEntity>[]> {
|
||||
const associatedFieldValue = this.entity.getField(fieldIdentifyingAssociatedEntity);
|
||||
if (!associatedFieldValue) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const loader = this.entity
|
||||
.getViewerContext()
|
||||
.getViewerScopedEntityCompanionForClass(associatedEntityClass)
|
||||
@@ -258,7 +274,7 @@ export default class EntityAssociationLoader<
|
||||
>
|
||||
],
|
||||
queryContext?: EntityQueryContext
|
||||
): Promise<Result<TEntity2> | null>;
|
||||
): Promise<Result<TEntity2 | null>>;
|
||||
|
||||
/**
|
||||
* Load an associated entity by folding a sequence of {@link EntityLoadThroughDirective}. At each
|
||||
@@ -313,7 +329,7 @@ export default class EntityAssociationLoader<
|
||||
>
|
||||
],
|
||||
queryContext?: EntityQueryContext
|
||||
): Promise<Result<TEntity3> | null>;
|
||||
): Promise<Result<TEntity3 | null>>;
|
||||
|
||||
/**
|
||||
* Load an associated entity by folding a sequence of {@link EntityLoadThroughDirective}. At each
|
||||
@@ -389,7 +405,7 @@ export default class EntityAssociationLoader<
|
||||
>
|
||||
],
|
||||
queryContext?: EntityQueryContext
|
||||
): Promise<Result<TEntity4> | null>;
|
||||
): Promise<Result<TEntity4 | null>>;
|
||||
|
||||
/**
|
||||
* Load an associated entity by folding a sequence of {@link EntityLoadThroughDirective}. At each
|
||||
@@ -400,12 +416,12 @@ export default class EntityAssociationLoader<
|
||||
async loadAssociatedEntityThroughAsync(
|
||||
loadDirectives: EntityLoadThroughDirective<TViewerContext, any, any, any, any, any, any, any>[],
|
||||
queryContext?: EntityQueryContext
|
||||
): Promise<Result<ReadonlyEntity<any, any, any, any>> | null>;
|
||||
): Promise<Result<ReadonlyEntity<any, any, any, any> | null>>;
|
||||
|
||||
async loadAssociatedEntityThroughAsync(
|
||||
loadDirectives: EntityLoadThroughDirective<TViewerContext, any, any, any, any, any, any, any>[],
|
||||
queryContext?: EntityQueryContext
|
||||
): Promise<Result<ReadonlyEntity<any, any, any, any>> | null> {
|
||||
): Promise<Result<ReadonlyEntity<any, any, any, any> | null>> {
|
||||
let currentEntity: ReadonlyEntity<any, any, any, any> = this.entity;
|
||||
for (const loadDirective of loadDirectives) {
|
||||
const {
|
||||
@@ -413,7 +429,7 @@ export default class EntityAssociationLoader<
|
||||
fieldIdentifyingAssociatedEntity,
|
||||
associatedEntityLookupByField,
|
||||
} = loadDirective;
|
||||
let associatedEntityResult: Result<ReadonlyEntity<any, any, any, any>> | null;
|
||||
let associatedEntityResult: Result<ReadonlyEntity<any, any, any, any> | null>;
|
||||
if (associatedEntityLookupByField) {
|
||||
associatedEntityResult = await currentEntity
|
||||
.associationLoader()
|
||||
@@ -433,13 +449,14 @@ export default class EntityAssociationLoader<
|
||||
);
|
||||
}
|
||||
|
||||
if (!associatedEntityResult) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!associatedEntityResult.ok) {
|
||||
return result(associatedEntityResult.reason);
|
||||
}
|
||||
|
||||
if (!associatedEntityResult.value) {
|
||||
return result(null);
|
||||
}
|
||||
|
||||
currentEntity = associatedEntityResult.value;
|
||||
}
|
||||
return result(currentEntity);
|
||||
|
||||
@@ -132,13 +132,13 @@ export default class EntityLoader<
|
||||
async loadByFieldEqualingAsync<N extends keyof Pick<TFields, TSelectedFields>>(
|
||||
uniqueFieldName: N,
|
||||
fieldValue: NonNullable<TFields[N]>
|
||||
): Promise<Result<TEntity> | null> {
|
||||
): Promise<Result<TEntity | null>> {
|
||||
const entityResults = await this.loadManyByFieldEqualingAsync(uniqueFieldName, fieldValue);
|
||||
invariant(
|
||||
entityResults.length <= 1,
|
||||
`loadByFieldEqualing: Multiple entities of type ${this.entityClass.name} found for ${uniqueFieldName}=${fieldValue}`
|
||||
);
|
||||
return entityResults[0] ?? null;
|
||||
return entityResults[0] ?? result(null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,7 +160,7 @@ export default class EntityLoader<
|
||||
* @param id - ID of the entity
|
||||
* @returns entity result for matching ID, or null if no entity exists for ID.
|
||||
*/
|
||||
async loadByIDNullableAsync(id: TID): Promise<Result<TEntity> | null> {
|
||||
async loadByIDNullableAsync(id: TID): Promise<Result<TEntity | null>> {
|
||||
return await this.loadByFieldEqualingAsync(this.idField, id as any);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { createUnitTestEntityCompanionProvider } from '../utils/testing/createUn
|
||||
|
||||
describe(EntityAssociationLoader, () => {
|
||||
describe('loadAssociatedEntityAsync', () => {
|
||||
it('loads associated entities by ID', async () => {
|
||||
it('loads associated entities by ID and correctly handles a null value', async () => {
|
||||
const companionProvider = createUnitTestEntityCompanionProvider();
|
||||
const viewerContext = new TestViewerContext(companionProvider);
|
||||
const testOtherEntity = await enforceAsyncResult(
|
||||
@@ -24,6 +24,11 @@ describe(EntityAssociationLoader, () => {
|
||||
testEntity.associationLoader().loadAssociatedEntityAsync('stringField', TestEntity)
|
||||
);
|
||||
expect(loadedOther.getID()).toEqual(testOtherEntity.getID());
|
||||
|
||||
const loadedOther2 = await enforceAsyncResult(
|
||||
testEntity.associationLoader().loadAssociatedEntityAsync('nullableField', TestEntity)
|
||||
);
|
||||
expect(loadedOther2).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -62,7 +67,7 @@ describe(EntityAssociationLoader, () => {
|
||||
const loadedOtherResult = await testEntity
|
||||
.associationLoader()
|
||||
.loadAssociatedEntityByFieldEqualingAsync('stringField', TestEntity, 'customIdField');
|
||||
expect(loadedOtherResult?.enforceValue().getID()).toEqual(testOtherEntity.getID());
|
||||
expect(loadedOtherResult.enforceValue()?.getID()).toEqual(testOtherEntity.getID());
|
||||
});
|
||||
|
||||
it('returns null when loading associated entities by field equaling a non existent association', async () => {
|
||||
@@ -74,7 +79,19 @@ describe(EntityAssociationLoader, () => {
|
||||
const loadedOtherResult = await testEntity
|
||||
.associationLoader()
|
||||
.loadAssociatedEntityByFieldEqualingAsync('stringField', TestEntity, 'customIdField');
|
||||
expect(loadedOtherResult).toBeNull();
|
||||
expect(loadedOtherResult.enforceValue()).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null when load-by field is null', async () => {
|
||||
const companionProvider = createUnitTestEntityCompanionProvider();
|
||||
const viewerContext = new TestViewerContext(companionProvider);
|
||||
const testEntity = await enforceAsyncResult(
|
||||
TestEntity.creator(viewerContext).setField('stringField', 'blah').createAsync()
|
||||
);
|
||||
const loadedOtherResult = await testEntity
|
||||
.associationLoader()
|
||||
.loadAssociatedEntityByFieldEqualingAsync('nullableField', TestEntity, 'customIdField');
|
||||
expect(loadedOtherResult.enforceValue()).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -102,6 +119,22 @@ describe(EntityAssociationLoader, () => {
|
||||
expect(loaded.find((e) => e.getID() === testOtherEntity1.getID())).not.toBeUndefined();
|
||||
expect(loaded.find((e) => e.getID() === testOtherEntity2.getID())).not.toBeUndefined();
|
||||
});
|
||||
|
||||
it('returns empty results when field being queried by is null', async () => {
|
||||
const companionProvider = createUnitTestEntityCompanionProvider();
|
||||
const viewerContext = new TestViewerContext(companionProvider);
|
||||
const testEntity = await enforceAsyncResult(TestEntity.creator(viewerContext).createAsync());
|
||||
const loaded = await enforceResultsAsync(
|
||||
testEntity
|
||||
.associationLoader()
|
||||
.loadManyAssociatedEntitiesByFieldEqualingAsync(
|
||||
'nullableField',
|
||||
TestEntity,
|
||||
'stringField'
|
||||
)
|
||||
);
|
||||
expect(loaded).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadAssociatedEntityThroughAsync', () => {
|
||||
@@ -127,7 +160,7 @@ describe(EntityAssociationLoader, () => {
|
||||
fieldIdentifyingAssociatedEntity: 'foreignKey',
|
||||
},
|
||||
]);
|
||||
expect(loaded2Result?.enforceValue().getID()).toEqual(testEntity2.getID());
|
||||
expect(loaded2Result.enforceValue()?.getID()).toEqual(testEntity2.getID());
|
||||
|
||||
const loaded3Result = await testEntity.associationLoader().loadAssociatedEntityThroughAsync([
|
||||
{
|
||||
@@ -139,7 +172,7 @@ describe(EntityAssociationLoader, () => {
|
||||
fieldIdentifyingAssociatedEntity: 'testIndexedField',
|
||||
},
|
||||
]);
|
||||
expect(loaded3Result?.enforceValue().getID()).toEqual(testEntity3.getID());
|
||||
expect(loaded3Result.enforceValue()?.getID()).toEqual(testEntity3.getID());
|
||||
|
||||
const loaded4Result = await testEntity.associationLoader().loadAssociatedEntityThroughAsync([
|
||||
{
|
||||
@@ -155,7 +188,7 @@ describe(EntityAssociationLoader, () => {
|
||||
fieldIdentifyingAssociatedEntity: 'foreignKey',
|
||||
},
|
||||
]);
|
||||
expect(loaded4Result?.enforceValue().getID()).toEqual(testEntity4.getID());
|
||||
expect(loaded4Result.enforceValue()?.getID()).toEqual(testEntity4.getID());
|
||||
});
|
||||
|
||||
it('fails when chain loading associated entity fails', async () => {
|
||||
@@ -193,7 +226,7 @@ describe(EntityAssociationLoader, () => {
|
||||
associatedEntityLookupByField: 'stringField',
|
||||
},
|
||||
]);
|
||||
expect(loaded2Result?.enforceValue().getID()).toEqual(testEntity2.getID());
|
||||
expect(loaded2Result.enforceValue()?.getID()).toEqual(testEntity2.getID());
|
||||
});
|
||||
|
||||
it('returns null when chain loading by field equality returns null', async () => {
|
||||
@@ -211,7 +244,24 @@ describe(EntityAssociationLoader, () => {
|
||||
associatedEntityLookupByField: 'stringField',
|
||||
},
|
||||
]);
|
||||
expect(loaded2Result).toBeNull();
|
||||
expect(loaded2Result.enforceValue()).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null when chain loading by field is null', async () => {
|
||||
const companionProvider = createUnitTestEntityCompanionProvider();
|
||||
const viewerContext = new TestViewerContext(companionProvider);
|
||||
|
||||
const testEntity = await enforceAsyncResult(
|
||||
TestEntity.creator(viewerContext).setField('nullableField', null).createAsync()
|
||||
);
|
||||
|
||||
const loadedResult = await testEntity.associationLoader().loadAssociatedEntityThroughAsync([
|
||||
{
|
||||
associatedEntityClass: TestEntity,
|
||||
fieldIdentifyingAssociatedEntity: 'nullableField',
|
||||
},
|
||||
]);
|
||||
expect(loadedResult.enforceValue()).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -36,6 +36,7 @@ describe(EntityLoader, () => {
|
||||
numberField: 5,
|
||||
stringField: 'huh',
|
||||
dateField: dateToInsert,
|
||||
nullableField: null,
|
||||
},
|
||||
{
|
||||
customIdField: 'world',
|
||||
@@ -43,6 +44,7 @@ describe(EntityLoader, () => {
|
||||
numberField: 3,
|
||||
stringField: 'huh',
|
||||
dateField: dateToInsert,
|
||||
nullableField: null,
|
||||
},
|
||||
],
|
||||
],
|
||||
@@ -79,16 +81,16 @@ describe(EntityLoader, () => {
|
||||
|
||||
const entityResultNumber3 = await entityLoader.loadByFieldEqualingAsync('numberField', 3);
|
||||
expect(entityResultNumber3).not.toBeNull();
|
||||
expect(entityResultNumber3!.enforceValue().getID()).toEqual('world');
|
||||
expect(entityResultNumber3.enforceValue()?.getID()).toEqual('world');
|
||||
|
||||
const entityResultNumber4 = await entityLoader.loadByFieldEqualingAsync('numberField', 4);
|
||||
expect(entityResultNumber4).toBeNull();
|
||||
expect(entityResultNumber4.enforceValue()).toBeNull();
|
||||
|
||||
await expect(entityLoader.loadByFieldEqualingAsync('stringField', 'huh')).rejects.toThrowError(
|
||||
'loadByFieldEqualing: Multiple entities of type TestEntity found for stringField=huh'
|
||||
);
|
||||
|
||||
await expect(entityLoader.loadByIDNullableAsync('fake')).resolves.toBeNull();
|
||||
expect((await entityLoader.loadByIDNullableAsync('fake')).enforceValue()).toBeNull();
|
||||
await expect(entityLoader.loadByIDNullableAsync('hello')).resolves.not.toBeNull();
|
||||
});
|
||||
|
||||
@@ -112,6 +114,7 @@ describe(EntityLoader, () => {
|
||||
numberField: 4,
|
||||
testIndexedField: '4',
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
{
|
||||
customIdField: 'world',
|
||||
@@ -119,6 +122,7 @@ describe(EntityLoader, () => {
|
||||
numberField: 4,
|
||||
testIndexedField: '5',
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
{
|
||||
customIdField: 'blah',
|
||||
@@ -126,6 +130,7 @@ describe(EntityLoader, () => {
|
||||
numberField: 4,
|
||||
testIndexedField: '6',
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
],
|
||||
],
|
||||
@@ -189,6 +194,7 @@ describe(EntityLoader, () => {
|
||||
testIndexedField: '1',
|
||||
numberField: 3,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
],
|
||||
],
|
||||
|
||||
@@ -310,6 +310,7 @@ describe(EntityMutatorFactory, () => {
|
||||
testIndexedField: '4',
|
||||
numberField: 1,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
{
|
||||
customIdField: 'world',
|
||||
@@ -317,6 +318,7 @@ describe(EntityMutatorFactory, () => {
|
||||
testIndexedField: '5',
|
||||
numberField: 1,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
]);
|
||||
const newEntity = await entityMutatorFactory
|
||||
@@ -336,6 +338,7 @@ describe(EntityMutatorFactory, () => {
|
||||
testIndexedField: '4',
|
||||
numberField: 1,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
{
|
||||
customIdField: 'world',
|
||||
@@ -343,6 +346,7 @@ describe(EntityMutatorFactory, () => {
|
||||
testIndexedField: '5',
|
||||
numberField: 1,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -372,6 +376,7 @@ describe(EntityMutatorFactory, () => {
|
||||
testIndexedField: '4',
|
||||
numberField: 1,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
{
|
||||
customIdField: 'world',
|
||||
@@ -379,6 +384,7 @@ describe(EntityMutatorFactory, () => {
|
||||
testIndexedField: '5',
|
||||
numberField: 1,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -409,6 +415,7 @@ describe(EntityMutatorFactory, () => {
|
||||
testIndexedField: '4',
|
||||
numberField: 1,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
{
|
||||
customIdField: 'world',
|
||||
@@ -416,6 +423,7 @@ describe(EntityMutatorFactory, () => {
|
||||
testIndexedField: '5',
|
||||
numberField: 1,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -441,6 +449,7 @@ describe(EntityMutatorFactory, () => {
|
||||
testIndexedField: '3',
|
||||
numberField: 3,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
{
|
||||
customIdField: 'world',
|
||||
@@ -448,6 +457,7 @@ describe(EntityMutatorFactory, () => {
|
||||
testIndexedField: '4',
|
||||
numberField: 3,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -484,6 +494,7 @@ describe(EntityMutatorFactory, () => {
|
||||
testIndexedField: '3',
|
||||
numberField: 3,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
{
|
||||
customIdField: 'world',
|
||||
@@ -491,6 +502,7 @@ describe(EntityMutatorFactory, () => {
|
||||
testIndexedField: '4',
|
||||
numberField: 3,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -528,6 +540,7 @@ describe(EntityMutatorFactory, () => {
|
||||
testIndexedField: '3',
|
||||
numberField: 3,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
{
|
||||
customIdField: 'world',
|
||||
@@ -535,6 +548,7 @@ describe(EntityMutatorFactory, () => {
|
||||
testIndexedField: '4',
|
||||
numberField: 3,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -573,6 +587,7 @@ describe(EntityMutatorFactory, () => {
|
||||
testIndexedField: '3',
|
||||
numberField: 3,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
{
|
||||
customIdField: 'world',
|
||||
@@ -580,6 +595,7 @@ describe(EntityMutatorFactory, () => {
|
||||
testIndexedField: '4',
|
||||
numberField: 3,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -609,6 +625,7 @@ describe(EntityMutatorFactory, () => {
|
||||
testIndexedField: '3',
|
||||
numberField: 3,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -640,6 +657,7 @@ describe(EntityMutatorFactory, () => {
|
||||
testIndexedField: '3',
|
||||
numberField: 3,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -674,6 +692,7 @@ describe(EntityMutatorFactory, () => {
|
||||
testIndexedField: '3',
|
||||
numberField: 3,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -709,6 +728,7 @@ describe(EntityMutatorFactory, () => {
|
||||
testIndexedField: '3',
|
||||
numberField: 3,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -734,6 +754,7 @@ describe(EntityMutatorFactory, () => {
|
||||
testIndexedField: '3',
|
||||
numberField: 3,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
]);
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ describe(ReadonlyEntity, () => {
|
||||
stringField: '3',
|
||||
numberField: 4,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
});
|
||||
expect(testEntity.getUniqueIdentifier()).not.toEqual(testEntity2.getUniqueIdentifier());
|
||||
});
|
||||
|
||||
@@ -103,6 +103,15 @@ export default class EntityDataManager<TFields> {
|
||||
fieldName: N,
|
||||
fieldValues: readonly NonNullable<TFields[N]>[]
|
||||
): Promise<ReadonlyMap<NonNullable<TFields[N]>, readonly Readonly<TFields>[]>> {
|
||||
const nullOrUndefinedValueIndex = fieldValues.findIndex(
|
||||
(value) => value === null || value === undefined
|
||||
);
|
||||
if (nullOrUndefinedValueIndex >= 0) {
|
||||
throw new Error(
|
||||
`Invalid load: ${this.entityClassName} (${fieldName} = ${fieldValues[nullOrUndefinedValueIndex]})`
|
||||
);
|
||||
}
|
||||
|
||||
// don't cache when in transaction, as rollbacks complicate things significantly
|
||||
if (queryContext.isInTransaction()) {
|
||||
return await this.databaseAdapter.fetchManyWhereAsync(queryContext, fieldName, fieldValues);
|
||||
|
||||
@@ -36,6 +36,7 @@ const getObjects = (): Map<string, TestFields[]> =>
|
||||
stringField: 'hello',
|
||||
numberField: 1,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
{
|
||||
customIdField: '2',
|
||||
@@ -43,6 +44,7 @@ const getObjects = (): Map<string, TestFields[]> =>
|
||||
stringField: 'hello',
|
||||
numberField: 1,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
{
|
||||
customIdField: '3',
|
||||
@@ -50,6 +52,7 @@ const getObjects = (): Map<string, TestFields[]> =>
|
||||
stringField: 'world',
|
||||
numberField: 1,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
],
|
||||
],
|
||||
@@ -566,4 +569,34 @@ describe(EntityDataManager, () => {
|
||||
verify(metricsAdapterMock.incrementDataManagerCacheLoadCount(anyNumber())).never();
|
||||
verify(metricsAdapterMock.incrementDataManagerDatabaseLoadCount(anyNumber())).never();
|
||||
});
|
||||
|
||||
it('throws when a load by value is null or undefined', async () => {
|
||||
const objects = getObjects();
|
||||
const dataStore = StubDatabaseAdapter.convertFieldObjectsToDataStore(
|
||||
testEntityConfiguration,
|
||||
objects
|
||||
);
|
||||
const databaseAdapter = new StubDatabaseAdapter<TestFields>(testEntityConfiguration, dataStore);
|
||||
const cacheAdapterProvider = new NoCacheStubCacheAdapterProvider();
|
||||
const cacheAdapter = cacheAdapterProvider.getCacheAdapter(testEntityConfiguration);
|
||||
const entityCache = new ReadThroughEntityCache(testEntityConfiguration, cacheAdapter);
|
||||
const entityDataManager = new EntityDataManager(
|
||||
databaseAdapter,
|
||||
entityCache,
|
||||
StubQueryContextProvider,
|
||||
new NoOpEntityMetricsAdapter(),
|
||||
TestEntity.name
|
||||
);
|
||||
const queryContext = StubQueryContextProvider.getQueryContext();
|
||||
|
||||
await expect(
|
||||
entityDataManager.loadManyByFieldEqualingAsync(queryContext, 'nullableField', [null as any])
|
||||
).rejects.toThrowError('Invalid load: TestEntity (nullableField = null)');
|
||||
|
||||
await expect(
|
||||
entityDataManager.loadManyByFieldEqualingAsync(queryContext, 'nullableField', [
|
||||
undefined as any,
|
||||
])
|
||||
).rejects.toThrowError('Invalid load: TestEntity (nullableField = undefined)');
|
||||
});
|
||||
});
|
||||
@@ -18,6 +18,7 @@ export type TestFields = {
|
||||
stringField: string;
|
||||
numberField: number;
|
||||
dateField: Date;
|
||||
nullableField: string | null;
|
||||
};
|
||||
|
||||
export const testEntityConfiguration = new EntityConfiguration<TestFields>({
|
||||
@@ -40,6 +41,9 @@ export const testEntityConfiguration = new EntityConfiguration<TestFields>({
|
||||
dateField: new DateField({
|
||||
columnName: 'date_field',
|
||||
}),
|
||||
nullableField: new StringField({
|
||||
columnName: 'nullable_field',
|
||||
}),
|
||||
},
|
||||
databaseAdapterFlavor: DatabaseAdapterFlavor.POSTGRES,
|
||||
cacheAdapterFlavor: CacheAdapterFlavor.REDIS,
|
||||
@@ -88,6 +92,7 @@ export default class TestEntity extends Entity<TestFields, string, ViewerContext
|
||||
stringField: 'hello',
|
||||
numberField: 1,
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ describe(StubDatabaseAdapter, () => {
|
||||
numberField: 5,
|
||||
stringField: 'huh',
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
{
|
||||
customIdField: 'world',
|
||||
@@ -42,6 +43,7 @@ describe(StubDatabaseAdapter, () => {
|
||||
numberField: 3,
|
||||
stringField: 'wat',
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
],
|
||||
],
|
||||
@@ -73,6 +75,7 @@ describe(StubDatabaseAdapter, () => {
|
||||
numberField: 3,
|
||||
stringField: 'a',
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
{
|
||||
customIdField: 'world',
|
||||
@@ -80,6 +83,7 @@ describe(StubDatabaseAdapter, () => {
|
||||
numberField: 3,
|
||||
stringField: 'b',
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
{
|
||||
customIdField: 'world',
|
||||
@@ -87,6 +91,7 @@ describe(StubDatabaseAdapter, () => {
|
||||
numberField: 3,
|
||||
stringField: 'c',
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
],
|
||||
],
|
||||
@@ -138,6 +143,7 @@ describe(StubDatabaseAdapter, () => {
|
||||
numberField: 3,
|
||||
stringField: 'a',
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
{
|
||||
customIdField: 'world',
|
||||
@@ -145,6 +151,7 @@ describe(StubDatabaseAdapter, () => {
|
||||
numberField: 3,
|
||||
stringField: 'b',
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
{
|
||||
customIdField: 'world',
|
||||
@@ -152,6 +159,7 @@ describe(StubDatabaseAdapter, () => {
|
||||
numberField: 3,
|
||||
stringField: 'c',
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
],
|
||||
],
|
||||
@@ -236,6 +244,7 @@ describe(StubDatabaseAdapter, () => {
|
||||
numberField: 3,
|
||||
stringField: 'a',
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
],
|
||||
],
|
||||
@@ -269,6 +278,7 @@ describe(StubDatabaseAdapter, () => {
|
||||
numberField: 3,
|
||||
stringField: 'a',
|
||||
dateField: new Date(),
|
||||
nullableField: null,
|
||||
},
|
||||
],
|
||||
],
|
||||
|
||||
Reference in new issue
Block a user