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 @@ -109,7 +109,7 @@ describe(GenericRedisCacher, () => {
return pipeline;
},
);
when(mockPipeline.exec()).thenResolve({} as any);
when(mockPipeline.exec()).thenResolve([]);
const pipeline = instance(mockPipeline);

const mockRedisClient = mock<Redis>();
Expand Down Expand Up @@ -154,7 +154,7 @@ describe(GenericRedisCacher, () => {
return pipeline;
},
);
when(mockPipeline.exec()).thenResolve({} as any);
when(mockPipeline.exec()).thenResolve([]);
const pipeline = instance(mockPipeline);

const mockRedisClient = mock<Redis>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,7 @@ export class PostgresEntityDatabaseAdapter<
bindings: object | any[],
querySelectionModifiers: TableQuerySelectionModifiersWithOrderByRaw,
): Promise<object[]> {
let query = queryInterface
.select()
.from(tableName)
.whereRaw(rawWhereClause, bindings as any);
let query = queryInterface.select().from(tableName).whereRaw(rawWhereClause, bindings);
query = this.applyQueryModifiersToQueryOrderByRaw(query, querySelectionModifiers);
return await wrapNativePostgresCallAsync(() => query);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { BigIntField, JSONArrayField, MaybeJSONArrayField } from '../EntityField

describeFieldTestCase(
new JSONArrayField({ columnName: 'wat' }),
[[[1, 2]] as any, [['hello']] as any],
[[[1, 2]], [['hello']]],
[1, 'hello'],
);
describeFieldTestCase(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ describe('SQLOperator', () => {

it('handles non-SupportedSQLValue types gracefully', () => {
const fragment = new SQLFragment('SELECT * FROM test WHERE field = ? AND field2 = ?', [
{ type: 'value', value: new Error('wat') as any },
{ type: 'value', value: new Error('wat') },
{ type: 'value', value: Object.create(null) },
]);

Expand Down
12 changes: 8 additions & 4 deletions packages/entity-example/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ApolloServer } from '@apollo/server';
import { ApolloServer, type BaseContext } from '@apollo/server';
import { koaMiddleware } from '@as-integrations/koa';
import { EntityCompanionProvider } from '@expo/entity';
import { bodyParser } from '@koa/bodyparser';
Expand All @@ -23,6 +23,10 @@ export type ExampleState = {
entityCompanionProvider: EntityCompanionProvider; // entityCompanionMiddleware
};

export interface GraphQLContext extends BaseContext {
viewerContext: ExampleViewerContext;
}

export async function createAppAsync(): Promise<Koa<ExampleState, ExampleContext>> {
const app = new Koa<ExampleState, ExampleContext>();

Expand All @@ -44,15 +48,15 @@ export async function createAppAsync(): Promise<Koa<ExampleState, ExampleContext
router.use(notesRouter.routes());

// GraphQL routes
const server = new ApolloServer({
const server = new ApolloServer<GraphQLContext>({
typeDefs,
resolvers,
});
await server.start();
router.post(
'/graphql',
// https://github.com/apollographql/apollo-server/issues/7625
koaMiddleware(server as any, {
// @ts-expect-error - @as-integrations/koa peers on @apollo/server ^4 (CJS types) but we use v5 (ESM types)
koaMiddleware<GraphQLContext, ExampleState, ExampleContext>(server, {
context: async ({ ctx }: { ctx: ExampleContext }) => ({
viewerContext: ctx.state.viewerContext,
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class AuthorizationResultBasedEntityAssociationLoader<
TIDField extends keyof NonNullable<Pick<TFields, TSelectedFields>>,
TViewerContext extends ViewerContext,
TEntity extends ReadonlyEntity<TFields, TIDField, TViewerContext, TSelectedFields>,
TSelectedFields extends keyof TFields,
TSelectedFields extends keyof TFields = keyof TFields,
> {
constructor(
private readonly entity: TEntity,
Expand Down Expand Up @@ -123,10 +123,7 @@ export class AuthorizationResultBasedEntityAssociationLoader<
.getViewerScopedEntityCompanionForClass(associatedEntityClass)
.getLoaderFactory()
.forLoad(this.queryContext, { previousValue: null, cascadingDeleteCause: null });
return await loader.loadManyByFieldEqualingAsync(
associatedEntityFieldContainingThisID,
thisID as any,
);
return await loader.loadManyByFieldEqualingAsync(associatedEntityFieldContainingThisID, thisID);
}

/**
Expand Down Expand Up @@ -178,7 +175,7 @@ export class AuthorizationResultBasedEntityAssociationLoader<
.forLoad(this.queryContext, { previousValue: null, cascadingDeleteCause: null });
return await loader.loadByFieldEqualingAsync(
associatedEntityLookupByField,
associatedFieldValue as any,
associatedFieldValue,
);
}

Expand Down Expand Up @@ -232,7 +229,7 @@ export class AuthorizationResultBasedEntityAssociationLoader<
.forLoad(this.queryContext, { previousValue: null, cascadingDeleteCause: null });
return await loader.loadManyByFieldEqualingAsync(
associatedEntityLookupByField,
associatedFieldValue as any,
associatedFieldValue,
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -986,7 +986,7 @@ export class AuthorizationResultBasedDeleteMutator<
.loadManyByFieldEqualingAsync(
fieldName,
association.associatedEntityLookupByField
? entity.getField(association.associatedEntityLookupByField as any)
? entity.getField(association.associatedEntityLookupByField)
: entity.getID(),
),
);
Expand Down
2 changes: 1 addition & 1 deletion packages/entity/src/EnforcingEntityLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class EnforcingEntityLoader<
TEntity,
TSelectedFields
>,
TSelectedFields extends keyof TFields,
TSelectedFields extends keyof TFields = keyof TFields,
> {
constructor(
private readonly entityLoader: AuthorizationResultBasedEntityLoader<
Expand Down
2 changes: 1 addition & 1 deletion packages/entity/src/EntityFieldDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export interface EntityAssociationDefinition<
* Field by which to load the instance of associatedEntityClass. If not provided, the
* associatedEntityClass instance is fetched by its ID.
*/
associatedEntityLookupByField?: keyof TAssociatedFields;
associatedEntityLookupByField?: TAssociatedSelectedFields;

/**
* What action to perform on the entity at the other end of this edge when the entity on the source end of
Expand Down
6 changes: 3 additions & 3 deletions packages/entity/src/__tests__/ComposedCacheAdapter-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ describe(ComposedEntityCacheAdapter, () => {
const { cacheAdapter } = makeTestCacheAdapters();
const results = await cacheAdapter.loadManyAsync(
new SingleFieldHolder<BlahFields, 'id', 'id'>('id'),
[] as any,
[] as readonly SingleFieldValueHolder<BlahFields, 'id'>[],
);
expect(results.size).toBe(0);
});
Expand All @@ -298,7 +298,7 @@ describe(ComposedEntityCacheAdapter, () => {
const cacheAdapter = new ComposedEntityCacheAdapter<BlahFields, 'id'>([]);
const results = await cacheAdapter.loadManyAsync(
new SingleFieldHolder<BlahFields, 'id', 'id'>('id'),
[] as any,
[] as readonly SingleFieldValueHolder<BlahFields, 'id'>[],
);
expect(results.size).toBe(0);
});
Expand Down Expand Up @@ -430,7 +430,7 @@ describe(ComposedEntityCacheAdapter, () => {

await cacheAdapter.invalidateManyAsync(
new SingleFieldHolder<BlahFields, 'id', 'id'>('id'),
[] as any,
[] as readonly SingleFieldValueHolder<BlahFields, 'id'>[],
);
});
});
Expand Down
4 changes: 2 additions & 2 deletions packages/entity/src/__tests__/EntityConfiguration-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ describe(EntityConfiguration, () => {
])('disallows %p as field key', (keyName) => {
expect(
() =>
new EntityConfiguration<any, 'id'>({
new EntityConfiguration<{ id: string }, 'id'>({
idField: 'id',
tableName: 'blah_table',
schema: {
Expand All @@ -186,7 +186,7 @@ describe(EntityConfiguration, () => {
[keyName]: new StringField({
columnName: 'any',
}),
} as any,
},
databaseAdapterFlavor: 'postgres',
cacheAdapterFlavor: 'redis',
}),
Expand Down
20 changes: 10 additions & 10 deletions packages/entity/src/__tests__/EntityDatabaseAdapter-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,14 +228,14 @@ describe(EntityDatabaseAdapter, () => {
it('transforms object', async () => {
const queryContext = instance(mock(EntityQueryContext));
const adapter = new TestEntityDatabaseAdapter({ insertResults: [{ string_field: 'hello' }] });
const result = await adapter.insertAsync(queryContext, {} as any);
const result = await adapter.insertAsync(queryContext, {});
expect(result).toEqual({ stringField: 'hello' });
});

it('throws when insert result count zero', async () => {
const queryContext = instance(mock(EntityQueryContext));
const adapter = new TestEntityDatabaseAdapter({ insertResults: [] });
await expect(adapter.insertAsync(queryContext, {} as any)).rejects.toThrow(
await expect(adapter.insertAsync(queryContext, {})).rejects.toThrow(
EntityDatabaseAdapterEmptyInsertResultError,
);
});
Expand All @@ -245,7 +245,7 @@ describe(EntityDatabaseAdapter, () => {
const adapter = new TestEntityDatabaseAdapter({
insertResults: [{ string_field: 'hello' }, { string_field: 'hello2' }],
});
await expect(adapter.insertAsync(queryContext, {} as any)).rejects.toThrow(
await expect(adapter.insertAsync(queryContext, {})).rejects.toThrow(
EntityDatabaseAdapterExcessiveInsertResultError,
);
});
Expand All @@ -255,26 +255,26 @@ describe(EntityDatabaseAdapter, () => {
it('transforms object', async () => {
const queryContext = instance(mock(EntityQueryContext));
const adapter = new TestEntityDatabaseAdapter({ updateResults: [{ string_field: 'hello' }] });
const result = await adapter.updateAsync(queryContext, 'customIdField', 'wat', {} as any);
const result = await adapter.updateAsync(queryContext, 'customIdField', 'wat', {});
expect(result).toEqual({ stringField: 'hello' });
});

it('throws when update result count zero', async () => {
const queryContext = instance(mock(EntityQueryContext));
const adapter = new TestEntityDatabaseAdapter({ updateResults: [] });
await expect(
adapter.updateAsync(queryContext, 'customIdField', 'wat', {} as any),
).rejects.toThrow(EntityDatabaseAdapterEmptyUpdateResultError);
await expect(adapter.updateAsync(queryContext, 'customIdField', 'wat', {})).rejects.toThrow(
EntityDatabaseAdapterEmptyUpdateResultError,
);
});

it('throws when update result count greater than 1', async () => {
const queryContext = instance(mock(EntityQueryContext));
const adapter = new TestEntityDatabaseAdapter({
updateResults: [{ string_field: 'hello' }, { string_field: 'hello2' }],
});
await expect(
adapter.updateAsync(queryContext, 'customIdField', 'wat', {} as any),
).rejects.toThrow(EntityDatabaseAdapterExcessiveUpdateResultError);
await expect(adapter.updateAsync(queryContext, 'customIdField', 'wat', {})).rejects.toThrow(
EntityDatabaseAdapterExcessiveUpdateResultError,
);
});
});

Expand Down
18 changes: 13 additions & 5 deletions packages/entity/src/errors/__tests__/EntityError-test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { describe, expect, it } from '@jest/globals';
import { instance, mock } from 'ts-mockito';

import { ViewerContext } from '../../ViewerContext';
import { SimpleTestEntity } from '../../utils/__testfixtures__/SimpleTestEntity';
import { EntityCacheAdapterTransientError } from '../EntityCacheAdapterError';
import { EntityErrorCode, EntityErrorState } from '../EntityError';
import { EntityInvalidFieldValueError } from '../EntityInvalidFieldValueError';
Expand All @@ -14,16 +17,21 @@ describe('EntityError subclasses', () => {
});

it('EntityNotAuthorizedError has correct state and code', () => {
const mockEntity = { constructor: { name: 'TestEntity' }, toString: () => 'TestEntity' } as any;
const mockViewerContext = { toString: () => 'TestViewer' } as any;
const error = new EntityNotAuthorizedError(mockEntity, mockViewerContext, 0, 0);
const viewerContext = instance(mock(ViewerContext));
const data = { id: '1' };
const testEntity = new SimpleTestEntity({
viewerContext,
id: 'what',
databaseFields: data,
selectedFields: data,
});
const error = new EntityNotAuthorizedError(testEntity, viewerContext, 0, 0);
expect(error.state).toBe(EntityErrorState.PERMANENT);
expect(error.code).toBe(EntityErrorCode.ERR_ENTITY_NOT_AUTHORIZED);
});

it('EntityInvalidFieldValueError has correct state and code', () => {
const mockEntityClass = { name: 'TestEntity' } as any;
const error = new EntityInvalidFieldValueError(mockEntityClass, 'testField', 'badValue');
const error = new EntityInvalidFieldValueError(SimpleTestEntity, 'id', 'badValue');
expect(error.state).toBe(EntityErrorState.PERMANENT);
expect(error.code).toBe(EntityErrorCode.ERR_ENTITY_INVALID_FIELD_VALUE);
});
Expand Down
10 changes: 5 additions & 5 deletions packages/entity/src/internal/EntityFieldTransformationUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export const transformDatabaseObjectToFields = <
fieldTransformerMap: FieldTransformerMap,
databaseObject: { [key: string]: any },
): Readonly<TFields> => {
const fields: TFields = {} as any;
const fields: Partial<TFields> = {};
for (const k in databaseObject) {
const val = databaseObject[k];
const fieldsKey = entityConfiguration.dbToEntityFieldsKeyMapping.get(k);
Expand All @@ -64,7 +64,7 @@ export const transformDatabaseObjectToFields = <
);
}
}
return fields;
return fields as Readonly<TFields>;
};

/**
Expand All @@ -81,7 +81,7 @@ export const transformFieldsToDatabaseObject = <
const databaseObject: { [key: string]: any } = {};
for (const k in fields) {
const val = fields[k]!;
const databaseKey = entityConfiguration.entityToDBFieldsKeyMapping.get(k as any);
const databaseKey = entityConfiguration.entityToDBFieldsKeyMapping.get(k);
invariant(databaseKey, `must be database key for field: ${k}`);
databaseObject[databaseKey] = maybeTransformFieldValueToDatabaseValue(
entityConfiguration,
Expand All @@ -104,7 +104,7 @@ export const transformCacheObjectToFields = <
fieldTransformerMap: FieldTransformerMap,
cacheObject: { [key: string]: any },
): Readonly<TFields> => {
const fields: TFields = {} as any;
const fields: Partial<TFields> = {};
for (const fieldsKey in cacheObject) {
const val = cacheObject[fieldsKey]!;
fields[fieldsKey as keyof TFields] = maybeTransformCacheValueToFieldValue(
Expand All @@ -114,7 +114,7 @@ export const transformCacheObjectToFields = <
val,
);
}
return fields;
return fields as Readonly<TFields>;
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1143,15 +1143,15 @@ describe(EntityDataManager, () => {
entityDataManager.loadManyEqualingAsync(
queryContext,
new SingleFieldHolder<TestFields, 'customIdField', 'nullableField'>('nullableField'),
[new SingleFieldValueHolder(null as any)],
[new SingleFieldValueHolder(null)],
),
).rejects.toThrow('Invalid load: TestEntity (nullableField = null)');

await expect(
entityDataManager.loadManyEqualingAsync(
queryContext,
new SingleFieldHolder<TestFields, 'customIdField', 'nullableField'>('nullableField'),
[new SingleFieldValueHolder(undefined as any)],
[new SingleFieldValueHolder(undefined)],
),
).rejects.toThrow('Invalid load: TestEntity (nullableField = undefined)');
});
Expand Down Expand Up @@ -1187,7 +1187,7 @@ describe(EntityDataManager, () => {
]),
[
new CompositeFieldValueHolder({
nullableField: null as any,
nullableField: null,
testIndexedField: 'unique1',
}),
],
Expand All @@ -1205,7 +1205,7 @@ describe(EntityDataManager, () => {
]),
[
new CompositeFieldValueHolder({
nullableField: undefined as any,
nullableField: undefined,
testIndexedField: 'unique1',
}),
],
Expand Down
4 changes: 2 additions & 2 deletions packages/entity/src/utils/EntityPrivacyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ async function canViewerDeleteInternalAsync<
const singleEntityResultToTestForInboundEdge = await loader['loadOneByFieldEqualingAsync'](
fieldName,
association.associatedEntityLookupByField
? sourceEntity.getField(association.associatedEntityLookupByField as any)
? sourceEntity.getField(association.associatedEntityLookupByField)
: sourceEntity.getID(),
);
entityResultsToCheckForInboundEdge = singleEntityResultToTestForInboundEdge
Expand All @@ -400,7 +400,7 @@ async function canViewerDeleteInternalAsync<
const entityResultsForInboundEdge = await loader.loadManyByFieldEqualingAsync(
fieldName,
association.associatedEntityLookupByField
? sourceEntity.getField(association.associatedEntityLookupByField as any)
? sourceEntity.getField(association.associatedEntityLookupByField)
: sourceEntity.getID(),
);
entityResultsToCheckForInboundEdge = entityResultsForInboundEdge;
Expand Down