Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,21 @@ export class StubPostgresDatabaseAdapter<
return [...results];
}

protected async fetchOneWhereInternalAsync(
queryInterface: any,
tableName: string,
tableColumns: readonly string[],
tableTuple: readonly any[],
): Promise<object | null> {
const results = await this.fetchManyWhereInternalAsync(
queryInterface,
tableName,
tableColumns,
[tableTuple],
);
return results[0] ?? null;
}

private static compareByOrderBys(
orderBys: {
columnName: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,108 @@ describe(StubPostgresDatabaseAdapter, () => {
});
});

describe('fetchOneWhereAsync', () => {
it('fetches one where single', async () => {
const queryContext = instance(mock(EntityQueryContext));
const databaseAdapter = new StubPostgresDatabaseAdapter<TestFields, 'customIdField'>(
testEntityConfiguration,
StubPostgresDatabaseAdapter.convertFieldObjectsToDataStore(
testEntityConfiguration,
new Map([
[
testEntityConfiguration.tableName,
[
{
customIdField: 'hello',
testIndexedField: 'h1',
intField: 5,
stringField: 'huh',
dateField: new Date(),
nullableField: null,
},
{
customIdField: 'world',
testIndexedField: 'h2',
intField: 3,
stringField: 'huh',
dateField: new Date(),
nullableField: null,
},
],
],
]),
),
);

const result = await databaseAdapter.fetchOneWhereAsync(
queryContext,
new SingleFieldHolder('stringField'),
new SingleFieldValueHolder('huh'),
);
expect(result).toMatchObject({
stringField: 'huh',
});
});

it('returns null when no record found', async () => {
const queryContext = instance(mock(EntityQueryContext));
const databaseAdapter = new StubPostgresDatabaseAdapter<TestFields, 'customIdField'>(
testEntityConfiguration,
new Map(),
);

const result = await databaseAdapter.fetchOneWhereAsync(
queryContext,
new SingleFieldHolder('stringField'),
new SingleFieldValueHolder('huh'),
);
expect(result).toBeNull();
});

it('fetches one where composite', async () => {
const queryContext = instance(mock(EntityQueryContext));
const databaseAdapter = new StubPostgresDatabaseAdapter<TestFields, 'customIdField'>(
testEntityConfiguration,
StubPostgresDatabaseAdapter.convertFieldObjectsToDataStore(
testEntityConfiguration,
new Map([
[
testEntityConfiguration.tableName,
[
{
customIdField: 'hello',
testIndexedField: 'h1',
intField: 5,
stringField: 'huh',
dateField: new Date(),
nullableField: null,
},
{
customIdField: 'world',
testIndexedField: 'h2',
intField: 5,
stringField: 'huh',
dateField: new Date(),
nullableField: null,
},
],
],
]),
),
);

const result = await databaseAdapter.fetchOneWhereAsync(
queryContext,
new CompositeFieldHolder<TestFields, 'customIdField'>(['stringField', 'intField']),
new CompositeFieldValueHolder({ stringField: 'huh', intField: 5 }),
);
expect(result).toMatchObject({
stringField: 'huh',
intField: 5,
});
});
});

describe('fetchManyByFieldEqualityConjunctionAsync', () => {
it('supports conjuntions and query modifiers', async () => {
const queryContext = instance(mock(EntityQueryContext));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,25 @@ export class PostgresEntityDatabaseAdapter<
);
}

protected async fetchOneWhereInternalAsync(
queryInterface: Knex,
tableName: string,
tableColumns: readonly string[],
tableTuple: readonly any[],
): Promise<object | null> {
const results = await this.fetchManyByFieldEqualityConjunctionInternalAsync(
queryInterface,
tableName,
tableColumns.map((column, index) => ({
tableField: column,
tableValue: tableTuple[index],
})),
[],
{ limit: 1, orderBy: undefined, offset: undefined },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forget, do we require orderBy and offset to be undefined vs. omitted?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this internal interface, yep. For the public method types, they can be omitted.

);
return results[0] ?? null;
}

private applyQueryModifiersToQueryOrderByRaw(
query: Knex.QueryBuilder,
querySelectionModifiers: TableQuerySelectionModifiersWithOrderByRaw,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,42 @@ describe('postgres entity integration', () => {
});
});

describe('single field value loading (fetchOneWhereInternalAsync)', () => {
it('supports one loading', async () => {
const vc1 = new ViewerContext(createKnexIntegrationTestEntityCompanionProvider(knexInstance));

await enforceAsyncResult(
PostgresTestEntity.creatorWithAuthorizationResults(vc1)
.setField('name', 'hello')
.setField('hasACat', false)
.setField('hasADog', true)
.createAsync(),
);

await enforceAsyncResult(
PostgresTestEntity.creatorWithAuthorizationResults(vc1)
.setField('name', 'world')
.setField('hasACat', false)
.setField('hasADog', true)
.createAsync(),
);

await enforceAsyncResult(
PostgresTestEntity.creatorWithAuthorizationResults(vc1)
.setField('name', 'wat')
.setField('hasACat', false)
.setField('hasADog', false)
.createAsync(),
);

const result = await PostgresTestEntity.loaderWithAuthorizationResults(vc1)[
'loadOneByFieldEqualingAsync'
]('hasACat', false);
expect(result?.enforceValue()).not.toBeNull();
expect(result?.enforceValue().getField('hasACat')).toBe(false);
});
});

it('supports single field and composite field equality loading', async () => {
const vc1 = new ViewerContext(createKnexIntegrationTestEntityCompanionProvider(knexInstance));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class TestEntityDatabaseAdapter extends BasePostgresEntityDatabaseAdapter<
'customIdField'
> {
private readonly fetchResults: object[];
private readonly fetchOneResult: object | null;
private readonly insertResults: object[];
private readonly updateResults: object[];
private readonly fetchEqualityConditionResults: object[];
Expand All @@ -23,6 +24,7 @@ class TestEntityDatabaseAdapter extends BasePostgresEntityDatabaseAdapter<

constructor({
fetchResults = [],
fetchOneResult = null,
insertResults = [],
updateResults = [],
fetchEqualityConditionResults = [],
Expand All @@ -31,6 +33,7 @@ class TestEntityDatabaseAdapter extends BasePostgresEntityDatabaseAdapter<
deleteCount = 0,
}: {
fetchResults?: object[];
fetchOneResult?: object | null;
insertResults?: object[];
updateResults?: object[];
fetchEqualityConditionResults?: object[];
Expand All @@ -40,6 +43,7 @@ class TestEntityDatabaseAdapter extends BasePostgresEntityDatabaseAdapter<
}) {
super(testEntityConfiguration);
this.fetchResults = fetchResults;
this.fetchOneResult = fetchOneResult;
this.insertResults = insertResults;
this.updateResults = updateResults;
this.fetchEqualityConditionResults = fetchEqualityConditionResults;
Expand All @@ -61,6 +65,15 @@ class TestEntityDatabaseAdapter extends BasePostgresEntityDatabaseAdapter<
return this.fetchResults;
}

protected async fetchOneWhereInternalAsync(
_queryInterface: any,
_tableName: string,
_tableColumns: readonly string[],
_tableTuple: readonly any[],
): Promise<object | null> {
return this.fetchOneResult;
}

protected async fetchManyByRawWhereClauseInternalAsync(
_queryInterface: any,
_tableName: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,21 @@ export class StubPostgresDatabaseAdapter<
return [...results];
}

protected async fetchOneWhereInternalAsync(
queryInterface: any,
tableName: string,
tableColumns: readonly string[],
tableTuple: readonly any[],
): Promise<object | null> {
const results = await this.fetchManyWhereInternalAsync(
queryInterface,
tableName,
tableColumns,
[tableTuple],
);
return results[0] ?? null;
}

private static compareByOrderBys(
orderBys: {
columnName: string;
Expand Down
1 change: 0 additions & 1 deletion packages/entity-database-adapter-knex/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,3 @@ export * from './extensions/EntityTableDataCoordinatorExtensions';
export * from './extensions/ReadonlyEntityExtensions';
export * from './extensions/ViewerScopedEntityCompanionExtensions';
export * from './internal/EntityKnexDataManager';
export * from './utils/EntityPrivacyUtils';
15 changes: 15 additions & 0 deletions packages/entity-example/src/adapters/InMemoryDatabaseAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,21 @@ class InMemoryDatabaseAdapter<
return [...results];
}

protected override async fetchOneWhereInternalAsync(
queryInterface: any,
tableName: string,
tableColumns: readonly string[],
tableTuple: readonly any[],
): Promise<object | null> {
const results = await this.fetchManyWhereInternalAsync(
queryInterface,
tableName,
tableColumns,
[tableTuple],
);
return results[0] ?? null;
}

protected async insertInternalAsync(
_queryInterface: any,
_tableName: string,
Expand Down
15 changes: 15 additions & 0 deletions packages/entity-testing-utils/src/StubDatabaseAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,21 @@ export class StubDatabaseAdapter<
return [...results];
}

protected async fetchOneWhereInternalAsync(
queryInterface: any,
tableName: string,
tableColumns: readonly string[],
tableTuple: readonly any[],
): Promise<object | null> {
const results = await this.fetchManyWhereInternalAsync(
queryInterface,
tableName,
tableColumns,
[tableTuple],
);
return results[0] ?? null;
}

private generateRandomID(): any {
const idSchemaField = this.entityConfiguration2.schema.get(this.entityConfiguration2.idField);
invariant(
Expand Down
Loading