diff --git a/packages/entity-cache-adapter-redis/src/__tests__/GenericRedisCacher-test.ts b/packages/entity-cache-adapter-redis/src/__tests__/GenericRedisCacher-test.ts index 2238e1ee8..37d0eab29 100644 --- a/packages/entity-cache-adapter-redis/src/__tests__/GenericRedisCacher-test.ts +++ b/packages/entity-cache-adapter-redis/src/__tests__/GenericRedisCacher-test.ts @@ -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(); @@ -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(); diff --git a/packages/entity-database-adapter-knex/src/PostgresEntityDatabaseAdapter.ts b/packages/entity-database-adapter-knex/src/PostgresEntityDatabaseAdapter.ts index d73483390..3000decd1 100644 --- a/packages/entity-database-adapter-knex/src/PostgresEntityDatabaseAdapter.ts +++ b/packages/entity-database-adapter-knex/src/PostgresEntityDatabaseAdapter.ts @@ -215,10 +215,7 @@ export class PostgresEntityDatabaseAdapter< bindings: object | any[], querySelectionModifiers: TableQuerySelectionModifiersWithOrderByRaw, ): Promise { - 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); } diff --git a/packages/entity-database-adapter-knex/src/__tests__/EntityFields-test.ts b/packages/entity-database-adapter-knex/src/__tests__/EntityFields-test.ts index 854ff14aa..1ed258b4e 100644 --- a/packages/entity-database-adapter-knex/src/__tests__/EntityFields-test.ts +++ b/packages/entity-database-adapter-knex/src/__tests__/EntityFields-test.ts @@ -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( diff --git a/packages/entity-database-adapter-knex/src/__tests__/SQLOperator-test.ts b/packages/entity-database-adapter-knex/src/__tests__/SQLOperator-test.ts index 6bdd6a38e..a6e1d2015 100644 --- a/packages/entity-database-adapter-knex/src/__tests__/SQLOperator-test.ts +++ b/packages/entity-database-adapter-knex/src/__tests__/SQLOperator-test.ts @@ -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) }, ]); diff --git a/packages/entity-example/src/app.ts b/packages/entity-example/src/app.ts index 56caecbd2..4896b0150 100644 --- a/packages/entity-example/src/app.ts +++ b/packages/entity-example/src/app.ts @@ -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'; @@ -23,6 +23,10 @@ export type ExampleState = { entityCompanionProvider: EntityCompanionProvider; // entityCompanionMiddleware }; +export interface GraphQLContext extends BaseContext { + viewerContext: ExampleViewerContext; +} + export async function createAppAsync(): Promise> { const app = new Koa(); @@ -44,15 +48,15 @@ export async function createAppAsync(): Promise({ 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(server, { context: async ({ ctx }: { ctx: ExampleContext }) => ({ viewerContext: ctx.state.viewerContext, }), diff --git a/packages/entity/src/AuthorizationResultBasedEntityAssociationLoader.ts b/packages/entity/src/AuthorizationResultBasedEntityAssociationLoader.ts index 72ee0dee3..5d2b6831c 100644 --- a/packages/entity/src/AuthorizationResultBasedEntityAssociationLoader.ts +++ b/packages/entity/src/AuthorizationResultBasedEntityAssociationLoader.ts @@ -16,7 +16,7 @@ export class AuthorizationResultBasedEntityAssociationLoader< TIDField extends keyof NonNullable>, TViewerContext extends ViewerContext, TEntity extends ReadonlyEntity, - TSelectedFields extends keyof TFields, + TSelectedFields extends keyof TFields = keyof TFields, > { constructor( private readonly entity: TEntity, @@ -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); } /** @@ -178,7 +175,7 @@ export class AuthorizationResultBasedEntityAssociationLoader< .forLoad(this.queryContext, { previousValue: null, cascadingDeleteCause: null }); return await loader.loadByFieldEqualingAsync( associatedEntityLookupByField, - associatedFieldValue as any, + associatedFieldValue, ); } @@ -232,7 +229,7 @@ export class AuthorizationResultBasedEntityAssociationLoader< .forLoad(this.queryContext, { previousValue: null, cascadingDeleteCause: null }); return await loader.loadManyByFieldEqualingAsync( associatedEntityLookupByField, - associatedFieldValue as any, + associatedFieldValue, ); } diff --git a/packages/entity/src/AuthorizationResultBasedEntityMutator.ts b/packages/entity/src/AuthorizationResultBasedEntityMutator.ts index 0924dad60..8c6eafbf1 100644 --- a/packages/entity/src/AuthorizationResultBasedEntityMutator.ts +++ b/packages/entity/src/AuthorizationResultBasedEntityMutator.ts @@ -986,7 +986,7 @@ export class AuthorizationResultBasedDeleteMutator< .loadManyByFieldEqualingAsync( fieldName, association.associatedEntityLookupByField - ? entity.getField(association.associatedEntityLookupByField as any) + ? entity.getField(association.associatedEntityLookupByField) : entity.getID(), ), ); diff --git a/packages/entity/src/EnforcingEntityLoader.ts b/packages/entity/src/EnforcingEntityLoader.ts index 256b91b3c..329fb443c 100644 --- a/packages/entity/src/EnforcingEntityLoader.ts +++ b/packages/entity/src/EnforcingEntityLoader.ts @@ -24,7 +24,7 @@ export class EnforcingEntityLoader< TEntity, TSelectedFields >, - TSelectedFields extends keyof TFields, + TSelectedFields extends keyof TFields = keyof TFields, > { constructor( private readonly entityLoader: AuthorizationResultBasedEntityLoader< diff --git a/packages/entity/src/EntityFieldDefinition.ts b/packages/entity/src/EntityFieldDefinition.ts index 6da2ac033..01d5664c8 100644 --- a/packages/entity/src/EntityFieldDefinition.ts +++ b/packages/entity/src/EntityFieldDefinition.ts @@ -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 diff --git a/packages/entity/src/__tests__/ComposedCacheAdapter-test.ts b/packages/entity/src/__tests__/ComposedCacheAdapter-test.ts index f61dde542..286b20130 100644 --- a/packages/entity/src/__tests__/ComposedCacheAdapter-test.ts +++ b/packages/entity/src/__tests__/ComposedCacheAdapter-test.ts @@ -289,7 +289,7 @@ describe(ComposedEntityCacheAdapter, () => { const { cacheAdapter } = makeTestCacheAdapters(); const results = await cacheAdapter.loadManyAsync( new SingleFieldHolder('id'), - [] as any, + [] as readonly SingleFieldValueHolder[], ); expect(results.size).toBe(0); }); @@ -298,7 +298,7 @@ describe(ComposedEntityCacheAdapter, () => { const cacheAdapter = new ComposedEntityCacheAdapter([]); const results = await cacheAdapter.loadManyAsync( new SingleFieldHolder('id'), - [] as any, + [] as readonly SingleFieldValueHolder[], ); expect(results.size).toBe(0); }); @@ -430,7 +430,7 @@ describe(ComposedEntityCacheAdapter, () => { await cacheAdapter.invalidateManyAsync( new SingleFieldHolder('id'), - [] as any, + [] as readonly SingleFieldValueHolder[], ); }); }); diff --git a/packages/entity/src/__tests__/EntityConfiguration-test.ts b/packages/entity/src/__tests__/EntityConfiguration-test.ts index 48479bd05..155fe28ad 100644 --- a/packages/entity/src/__tests__/EntityConfiguration-test.ts +++ b/packages/entity/src/__tests__/EntityConfiguration-test.ts @@ -175,7 +175,7 @@ describe(EntityConfiguration, () => { ])('disallows %p as field key', (keyName) => { expect( () => - new EntityConfiguration({ + new EntityConfiguration<{ id: string }, 'id'>({ idField: 'id', tableName: 'blah_table', schema: { @@ -186,7 +186,7 @@ describe(EntityConfiguration, () => { [keyName]: new StringField({ columnName: 'any', }), - } as any, + }, databaseAdapterFlavor: 'postgres', cacheAdapterFlavor: 'redis', }), diff --git a/packages/entity/src/__tests__/EntityDatabaseAdapter-test.ts b/packages/entity/src/__tests__/EntityDatabaseAdapter-test.ts index 0df0815aa..42f9abe1c 100644 --- a/packages/entity/src/__tests__/EntityDatabaseAdapter-test.ts +++ b/packages/entity/src/__tests__/EntityDatabaseAdapter-test.ts @@ -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, ); }); @@ -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, ); }); @@ -255,16 +255,16 @@ 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 () => { @@ -272,9 +272,9 @@ describe(EntityDatabaseAdapter, () => { 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, + ); }); }); diff --git a/packages/entity/src/errors/__tests__/EntityError-test.ts b/packages/entity/src/errors/__tests__/EntityError-test.ts index 63e7bb2f0..bcafd1606 100644 --- a/packages/entity/src/errors/__tests__/EntityError-test.ts +++ b/packages/entity/src/errors/__tests__/EntityError-test.ts @@ -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'; @@ -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); }); diff --git a/packages/entity/src/internal/EntityFieldTransformationUtils.ts b/packages/entity/src/internal/EntityFieldTransformationUtils.ts index 25b136d53..2ac505ba7 100644 --- a/packages/entity/src/internal/EntityFieldTransformationUtils.ts +++ b/packages/entity/src/internal/EntityFieldTransformationUtils.ts @@ -51,7 +51,7 @@ export const transformDatabaseObjectToFields = < fieldTransformerMap: FieldTransformerMap, databaseObject: { [key: string]: any }, ): Readonly => { - const fields: TFields = {} as any; + const fields: Partial = {}; for (const k in databaseObject) { const val = databaseObject[k]; const fieldsKey = entityConfiguration.dbToEntityFieldsKeyMapping.get(k); @@ -64,7 +64,7 @@ export const transformDatabaseObjectToFields = < ); } } - return fields; + return fields as Readonly; }; /** @@ -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, @@ -104,7 +104,7 @@ export const transformCacheObjectToFields = < fieldTransformerMap: FieldTransformerMap, cacheObject: { [key: string]: any }, ): Readonly => { - const fields: TFields = {} as any; + const fields: Partial = {}; for (const fieldsKey in cacheObject) { const val = cacheObject[fieldsKey]!; fields[fieldsKey as keyof TFields] = maybeTransformCacheValueToFieldValue( @@ -114,7 +114,7 @@ export const transformCacheObjectToFields = < val, ); } - return fields; + return fields as Readonly; }; /** diff --git a/packages/entity/src/internal/__tests__/EntityDataManager-test.ts b/packages/entity/src/internal/__tests__/EntityDataManager-test.ts index 817f56aaf..a396f4413 100644 --- a/packages/entity/src/internal/__tests__/EntityDataManager-test.ts +++ b/packages/entity/src/internal/__tests__/EntityDataManager-test.ts @@ -1143,7 +1143,7 @@ describe(EntityDataManager, () => { entityDataManager.loadManyEqualingAsync( queryContext, new SingleFieldHolder('nullableField'), - [new SingleFieldValueHolder(null as any)], + [new SingleFieldValueHolder(null)], ), ).rejects.toThrow('Invalid load: TestEntity (nullableField = null)'); @@ -1151,7 +1151,7 @@ describe(EntityDataManager, () => { entityDataManager.loadManyEqualingAsync( queryContext, new SingleFieldHolder('nullableField'), - [new SingleFieldValueHolder(undefined as any)], + [new SingleFieldValueHolder(undefined)], ), ).rejects.toThrow('Invalid load: TestEntity (nullableField = undefined)'); }); @@ -1187,7 +1187,7 @@ describe(EntityDataManager, () => { ]), [ new CompositeFieldValueHolder({ - nullableField: null as any, + nullableField: null, testIndexedField: 'unique1', }), ], @@ -1205,7 +1205,7 @@ describe(EntityDataManager, () => { ]), [ new CompositeFieldValueHolder({ - nullableField: undefined as any, + nullableField: undefined, testIndexedField: 'unique1', }), ], diff --git a/packages/entity/src/utils/EntityPrivacyUtils.ts b/packages/entity/src/utils/EntityPrivacyUtils.ts index f3226543e..7ce8b553f 100644 --- a/packages/entity/src/utils/EntityPrivacyUtils.ts +++ b/packages/entity/src/utils/EntityPrivacyUtils.ts @@ -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 @@ -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;