diff --git a/handlers/handleCustomResource.js b/handlers/handleCustomResource.js index f32837f..58ab3ba 100644 --- a/handlers/handleCustomResource.js +++ b/handlers/handleCustomResource.js @@ -19,5 +19,23 @@ export default async function handleCustomResource(event, context) { logDebug('invocation', invocation); logDebug('handleCustomResource', { requestType: event.RequestType, requestId: context.awsRequestId }); console.log('Custom Resource request:', event.RequestType); - return {}; + + if (!event.ResponseURL) { + throw new Error('Missing ResponseURL'); + } + + const responseBody = JSON.stringify({ + Status: 'SUCCESS', + PhysicalResourceId: event.PhysicalResourceId || context.awsRequestId, + StackId: event.StackId, + RequestId: event.RequestId, + LogicalResourceId: event.LogicalResourceId + }); + + await fetch(event.ResponseURL, { + method: 'PUT', + body: responseBody + }); + + return; } diff --git a/tests/handleCustomResource.test.js b/tests/handleCustomResource.test.js new file mode 100644 index 0000000..1561e50 --- /dev/null +++ b/tests/handleCustomResource.test.js @@ -0,0 +1,33 @@ +import { jest } from '@jest/globals'; +import handleCustomResource from '../handlers/handleCustomResource.js'; + +describe('handleCustomResource', () => { + test('PUTs response to ResponseURL', async () => { + const fetchMock = jest.spyOn(global, 'fetch').mockResolvedValue({ ok: true }); + const event = { + RequestType: 'Create', + ResponseURL: 'https://example.com', + StackId: 's', + RequestId: 'r', + LogicalResourceId: 'l' + }; + const context = { awsRequestId: 'id1' }; + await handleCustomResource(event, context); + expect(fetchMock).toHaveBeenCalledWith('https://example.com', expect.objectContaining({ method: 'PUT' })); + const body = fetchMock.mock.calls[0][1].body; + expect(JSON.parse(body)).toMatchObject({ + Status: 'SUCCESS', + PhysicalResourceId: 'id1', + StackId: 's', + RequestId: 'r', + LogicalResourceId: 'l' + }); + fetchMock.mockRestore(); + }); + + test('throws when ResponseURL missing', async () => { + const event = { RequestType: 'Create' }; + const context = { awsRequestId: 'id1' }; + await expect(handleCustomResource(event, context)).rejects.toThrow('Missing ResponseURL'); + }); +}); diff --git a/tests/handlers.test.js b/tests/handlers.test.js index 665810b..ddfcdee 100644 --- a/tests/handlers.test.js +++ b/tests/handlers.test.js @@ -163,10 +163,13 @@ describe('handler dispatch', () => { }); test('handles Custom Resource event', async () => { - const event = { RequestType: 'Create', ResponseURL: 'https://example.com' }; + const fetchMock = jest.spyOn(global, 'fetch').mockResolvedValue({ ok: true }); + const event = { RequestType: 'Create', ResponseURL: 'https://example.com', StackId: 's', RequestId: 'r', LogicalResourceId: 'l' }; const context = { awsRequestId: '1' }; const result = await handler(event, context); - expect(result).toEqual({}); + expect(result).toBeUndefined(); + expect(fetchMock).toHaveBeenCalledWith('https://example.com', expect.objectContaining({ method: 'PUT' })); + fetchMock.mockRestore(); }); test('handles Cognito event', async () => {