diff --git a/packages/event-handler/src/appsync-graphql/RouteHandlerRegistry.ts b/packages/event-handler/src/appsync-graphql/RouteHandlerRegistry.ts index a33f7a62bf..ac6c2ff2aa 100644 --- a/packages/event-handler/src/appsync-graphql/RouteHandlerRegistry.ts +++ b/packages/event-handler/src/appsync-graphql/RouteHandlerRegistry.ts @@ -40,7 +40,13 @@ class RouteHandlerRegistry { public register( options: RouteHandlerOptions, boolean, boolean> ): void { - const { fieldName, handler, typeName, throwOnError, aggregate } = options; + const { + fieldName, + handler, + typeName, + throwOnError = false, + aggregate = true, + } = options; this.#logger.debug(`Adding resolver for field ${typeName}.${fieldName}`); const cacheKey = this.#makeKey(typeName, fieldName); if (this.resolvers.has(cacheKey)) { diff --git a/packages/event-handler/src/appsync-graphql/Router.ts b/packages/event-handler/src/appsync-graphql/Router.ts index ce70618726..57a243650b 100644 --- a/packages/event-handler/src/appsync-graphql/Router.ts +++ b/packages/event-handler/src/appsync-graphql/Router.ts @@ -531,8 +531,8 @@ class Router { fieldName, handler: handler as BatchResolverHandler, typeName, - aggregate: batchResolverOptions?.aggregate ?? true, - throwOnError: batchResolverOptions?.throwOnError ?? false, + aggregate: batchResolverOptions?.aggregate, + throwOnError: batchResolverOptions?.throwOnError, }); return; } @@ -544,8 +544,8 @@ class Router { fieldName, handler: descriptor?.value, typeName, - aggregate: batchResolverOptions?.aggregate ?? true, - throwOnError: batchResolverOptions?.throwOnError ?? false, + aggregate: batchResolverOptions?.aggregate, + throwOnError: batchResolverOptions?.throwOnError, }); return descriptor; }; @@ -730,8 +730,8 @@ class Router { fieldName, handler: handlerOrOptions as BatchResolverHandler, typeName: 'Query', - aggregate: options?.aggregate ?? true, - throwOnError: options?.throwOnError ?? false, + aggregate: options?.aggregate, + throwOnError: options?.throwOnError, }); return; @@ -742,8 +742,8 @@ class Router { fieldName, handler: descriptor?.value, typeName: 'Query', - aggregate: handlerOrOptions?.aggregate ?? true, - throwOnError: handlerOrOptions?.throwOnError ?? false, + aggregate: handlerOrOptions?.aggregate, + throwOnError: handlerOrOptions?.throwOnError, }); return descriptor; @@ -927,8 +927,8 @@ class Router { fieldName, handler: handlerOrOptions as BatchResolverHandler, typeName: 'Mutation', - aggregate: options?.aggregate ?? true, - throwOnError: options?.throwOnError ?? false, + aggregate: options?.aggregate, + throwOnError: options?.throwOnError, }); return; @@ -939,8 +939,8 @@ class Router { fieldName, handler: descriptor?.value, typeName: 'Mutation', - aggregate: handlerOrOptions?.aggregate ?? true, - throwOnError: handlerOrOptions?.throwOnError ?? false, + aggregate: handlerOrOptions?.aggregate, + throwOnError: handlerOrOptions?.throwOnError, }); return descriptor; diff --git a/packages/event-handler/tests/unit/appsync-graphql/AppSyncGraphQLResolver.test.ts b/packages/event-handler/tests/unit/appsync-graphql/AppSyncGraphQLResolver.test.ts index 27bc862e00..95cbcc25f8 100644 --- a/packages/event-handler/tests/unit/appsync-graphql/AppSyncGraphQLResolver.test.ts +++ b/packages/event-handler/tests/unit/appsync-graphql/AppSyncGraphQLResolver.test.ts @@ -506,15 +506,11 @@ describe('Class: AppSyncGraphQLResolver', () => { setupHandler(handler); if (aggregate) { - app.batchResolver(handler, { - fieldName: 'batchGet', - typeName: 'Query', + app.onBatchQuery('batchGet', handler, { aggregate: true, }); } else { - app.batchResolver(handler, { - fieldName: 'batchGet', - typeName: 'Query', + app.onBatchQuery('batchGet', handler, { aggregate: false, throwOnError: true, }); @@ -596,16 +592,14 @@ describe('Class: AppSyncGraphQLResolver', () => { .mockResolvedValueOnce({ id: '1', value: 'A' }) .mockRejectedValueOnce(new Error('fail')) .mockResolvedValueOnce({ id: '3', value: 'C' }); - app.batchResolver(handler, { - fieldName: 'batchGet', - typeName: 'Query', + app.onBatchMutation('batchPut', handler, { aggregate: false, throwOnError: true, }); const events = [ - onGraphqlEventFactory('batchGet', 'Query', { id: '1' }), - onGraphqlEventFactory('batchGet', 'Query', { id: '2' }), - onGraphqlEventFactory('batchGet', 'Query', { id: '3' }), + onGraphqlEventFactory('batchPut', 'Mutation', { id: '1' }), + onGraphqlEventFactory('batchPut', 'Mutation', { id: '2' }), + onGraphqlEventFactory('batchPut', 'Mutation', { id: '3' }), ]; // Act @@ -640,4 +634,76 @@ describe('Class: AppSyncGraphQLResolver', () => { ) ); }); + + it.each([ + { + throwOnError: true, + description: 'throwOnError=true', + }, + { + throwOnError: false, + description: 'throwOnError=false', + }, + ])( + 'preserves the scope when using `onBatchQuery` & `onBatchMutation` decorators when aggregate=false and $description', + async ({ throwOnError }) => { + // Prepare + const app = new AppSyncGraphQLResolver({ logger: console }); + + class Lambda { + public readonly scope = 'scoped'; + + @app.onBatchQuery('batchGet', { + throwOnError, + }) + public async handleBatchGet( + events: AppSyncResolverEvent<{ id: number }>[] + ) { + const ids = events.map((event) => event.arguments.id); + return ids.map((id) => ({ + id, + scope: this.scope, + })); + } + + @app.onBatchMutation('batchPut', { + throwOnError, + }) + public async handleBatchPut( + _events: AppSyncResolverEvent<{ id: number }>[] + ) { + return [this.scope, this.scope]; + } + + public async handler(event: unknown, context: Context) { + return app.resolve(event, context, { scope: this }); + } + } + const lambda = new Lambda(); + const handler = lambda.handler.bind(lambda); + + // Act + const resultQuery = await handler( + [ + onGraphqlEventFactory('batchGet', 'Query', { id: 1 }), + onGraphqlEventFactory('batchGet', 'Query', { id: 2 }), + ], + context + ); + const resultMutation = await handler( + [ + onGraphqlEventFactory('batchPut', 'Mutation', { id: 1 }), + onGraphqlEventFactory('batchPut', 'Mutation', { id: 2 }), + ], + context + ); + + // Assess + expect(resultQuery).toEqual([ + { id: 1, scope: 'scoped' }, + { id: 2, scope: 'scoped' }, + ]); + expect(resultMutation).toEqual(['scoped', 'scoped']); + } + ); }); diff --git a/packages/event-handler/tests/unit/appsync-graphql/RouteHandlerRegistry.test.ts b/packages/event-handler/tests/unit/appsync-graphql/RouteHandlerRegistry.test.ts index 4de557fd63..6b4d46be64 100644 --- a/packages/event-handler/tests/unit/appsync-graphql/RouteHandlerRegistry.test.ts +++ b/packages/event-handler/tests/unit/appsync-graphql/RouteHandlerRegistry.test.ts @@ -59,8 +59,10 @@ describe('Class: RouteHandlerRegistry', () => { // Assess expect(registry.resolvers.size).toBe(1); expect(registry.resolvers.get('Query.getPost')).toEqual({ + aggregate: true, fieldName: 'getPost', typeName: 'Query', + throwOnError: false, handler: otherHandler, }); expect(console.warn).toHaveBeenCalledWith( @@ -68,7 +70,7 @@ describe('Class: RouteHandlerRegistry', () => { ); }); - it('will not replace the resolver if the event type is different', () => { + it("doesn't replace the resolver if the event type is different", () => { // Prepare const registry = getRegistry(); const originalHandler = vi.fn(); @@ -89,13 +91,17 @@ describe('Class: RouteHandlerRegistry', () => { // Assess expect(registry.resolvers.size).toBe(2); expect(registry.resolvers.get('Query.getPost')).toEqual({ + aggregate: true, fieldName: 'getPost', typeName: 'Query', + throwOnError: false, handler: originalHandler, }); expect(registry.resolvers.get('Mutation.getPost')).toEqual({ + aggregate: true, fieldName: 'getPost', typeName: 'Mutation', + throwOnError: false, handler: otherHandler, }); }); diff --git a/vitest.config.ts b/vitest.config.ts index 78c6de7f3e..9045095d92 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -19,6 +19,11 @@ export default defineConfig({ 'packages/testing/**', ], }, + projects: [ + 'packages/*/vitest.config.ts', + 'examples/app/vitest.config.ts', + 'layers/vitest.config.ts', + ], setupFiles: ['./packages/testing/src/setupEnv.ts'], hookTimeout: 1_000 * 60 * 10, // 10 minutes testTimeout: 1_000 * 60 * 3, // 3 minutes diff --git a/vitest.workspace.ts b/vitest.workspace.ts deleted file mode 100644 index c13e16bdf4..0000000000 --- a/vitest.workspace.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default [ - 'packages/*/vitest.config.ts', - 'examples/app/vitest.config.ts', - 'layers/vitest.config.ts', -];