feat: Cloudformation + RDS
This commit is contained in:
@@ -159,16 +159,16 @@ describe('AwsApiGatewayAdapter', () => {
|
||||
})
|
||||
|
||||
expect(apiCommands.map((command) => (command as {constructor: {name: string}}).constructor.name)).toEqual([
|
||||
'GetResourcesCommand', 'GetResourcesCommand', 'CreateResourceCommand', 'PutMethodCommand',
|
||||
'GetResourcesCommand', 'CreateResourceCommand', 'PutMethodCommand',
|
||||
'PutIntegrationCommand', 'CreateDeploymentCommand',
|
||||
])
|
||||
expect((apiCommands[2] as CreateResourceCommand).input).toMatchObject({
|
||||
expect((apiCommands[1] as CreateResourceCommand).input).toMatchObject({
|
||||
restApiId: 'a1b2c3', parentId: 'root123', pathPart: 'hello',
|
||||
})
|
||||
expect((apiCommands[3] as PutMethodCommand).input).toMatchObject({
|
||||
expect((apiCommands[2] as PutMethodCommand).input).toMatchObject({
|
||||
httpMethod: 'GET', authorizationType: 'NONE',
|
||||
})
|
||||
expect((apiCommands[4] as PutIntegrationCommand).input).toMatchObject({
|
||||
expect((apiCommands[3] as PutIntegrationCommand).input).toMatchObject({
|
||||
type: 'AWS_PROXY', integrationHttpMethod: 'POST',
|
||||
uri: 'arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:000000000000:function:hello/invocations',
|
||||
})
|
||||
@@ -182,13 +182,77 @@ describe('AwsApiGatewayAdapter', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test('creates missing nested resources and reuses existing path segments', async () => {
|
||||
const apiCommands: unknown[] = []
|
||||
const adapter = new AwsApiGatewayAdapter(
|
||||
fakeClient((command) => {
|
||||
apiCommands.push(command)
|
||||
if (command instanceof GetResourcesCommand) return {items: [
|
||||
{id: 'root123', path: '/'},
|
||||
{id: 'orders123', parentId: 'root123', pathPart: 'orders', path: '/orders'},
|
||||
]}
|
||||
if (command instanceof CreateResourceCommand) return {id: 'orderId123'}
|
||||
if (command instanceof CreateDeploymentCommand) return {id: 'deployment789'}
|
||||
return {}
|
||||
}),
|
||||
fakeLambda((command) => command instanceof GetFunctionCommand
|
||||
? {Configuration: {FunctionArn: 'arn:aws:lambda:us-east-1:000000000000:function:hello'}}
|
||||
: {}),
|
||||
)
|
||||
|
||||
const result = await adapter.createApiGatewayLambdaRoute('a1b2c3', {
|
||||
path: '/orders/{id}', method: 'PATCH', stage: 'dev', functionName: 'hello',
|
||||
})
|
||||
|
||||
const creates = apiCommands.filter((command) => command instanceof CreateResourceCommand) as CreateResourceCommand[]
|
||||
expect(creates).toHaveLength(1)
|
||||
expect(creates[0]?.input).toMatchObject({parentId: 'orders123', pathPart: '{id}'})
|
||||
expect((apiCommands.find((command) => command instanceof PutMethodCommand) as PutMethodCommand).input.httpMethod).toBe('PATCH')
|
||||
expect(result).toMatchObject({resourceId: 'orderId123', invokeUrl: 'http://localhost:4566/execute-api/a1b2c3/dev/orders/{id}'})
|
||||
})
|
||||
|
||||
test('creates a greedy child under the explicitly selected parent', async () => {
|
||||
const commands: unknown[] = []
|
||||
const lambdaCommands: unknown[] = []
|
||||
const adapter = new AwsApiGatewayAdapter(fakeClient((command) => {
|
||||
commands.push(command)
|
||||
if (command instanceof GetResourcesCommand) return {items: [
|
||||
{id: 'root123', path: '/'},
|
||||
{id: 'files123', parentId: 'root123', pathPart: 'files', path: '/files'},
|
||||
]}
|
||||
if (command instanceof CreateResourceCommand) return {id: 'proxy123'}
|
||||
if (command instanceof CreateDeploymentCommand) return {id: 'deployment789'}
|
||||
return {}
|
||||
}), fakeLambda((command) => {
|
||||
lambdaCommands.push(command)
|
||||
return command instanceof GetFunctionCommand
|
||||
? {Configuration: {FunctionArn: 'arn:aws:lambda:us-east-1:000000000000:function:files'}}
|
||||
: {}
|
||||
}))
|
||||
|
||||
await adapter.createApiGatewayLambdaRoute('a1b2c3', {
|
||||
path: '/files/{proxy+}', parentResourceId: 'files123', pathPart: '{proxy+}',
|
||||
method: 'ANY', stage: 'dev', functionName: 'files',
|
||||
})
|
||||
|
||||
expect((commands.find((command) => command instanceof CreateResourceCommand) as CreateResourceCommand).input).toMatchObject({
|
||||
parentId: 'files123', pathPart: '{proxy+}',
|
||||
})
|
||||
expect((lambdaCommands.find((command) => command instanceof AddPermissionCommand) as AddPermissionCommand).input).toMatchObject({
|
||||
SourceArn: 'arn:aws:execute-api:us-east-1:000000000000:a1b2c3/dev/*/files/*',
|
||||
})
|
||||
})
|
||||
|
||||
test('adds a method to an existing resource without creating a duplicate path', async () => {
|
||||
const apiCommands: unknown[] = []
|
||||
const adapter = new AwsApiGatewayAdapter(
|
||||
fakeClient((command) => {
|
||||
apiCommands.push(command)
|
||||
if (command instanceof GetResourcesCommand) {
|
||||
return {items: [{id: 'resource456', path: '/hello'}, {id: 'root123', path: '/'}]}
|
||||
return {items: [
|
||||
{id: 'resource456', parentId: 'root123', pathPart: 'hello', path: '/hello'},
|
||||
{id: 'root123', path: '/'},
|
||||
]}
|
||||
}
|
||||
if (command instanceof CreateDeploymentCommand) return {id: 'deployment789'}
|
||||
return {}
|
||||
@@ -264,6 +328,25 @@ describe('AwsApiGatewayAdapter', () => {
|
||||
}
|
||||
})
|
||||
|
||||
test('uses PATCH and HEAD for deployed requests and rejects ANY', async () => {
|
||||
const originalFetch = globalThis.fetch
|
||||
const requests: Request[] = []
|
||||
globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||
requests.push(new Request(input, init))
|
||||
return new Response(null)
|
||||
}) as typeof fetch
|
||||
try {
|
||||
const adapter = new AwsApiGatewayAdapter(fakeClient(() => ({})))
|
||||
await adapter.testApiGatewayRoute('a1b2c3', {path: '/orders/1', method: 'PATCH', stage: 'dev', body: '{}', headers: {}, query: ''})
|
||||
await adapter.testApiGatewayRoute('a1b2c3', {path: '/orders/1', method: 'HEAD', stage: 'dev', body: '{}', headers: {}, query: ''})
|
||||
await expect(adapter.testApiGatewayRoute('a1b2c3', {path: '/orders/1', method: 'ANY', stage: 'dev', body: '', headers: {}, query: ''})).rejects.toThrow('method must be')
|
||||
expect(requests.map((request) => request.method)).toEqual(['PATCH', 'HEAD'])
|
||||
expect(await requests[1]?.text()).toBe('')
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch
|
||||
}
|
||||
})
|
||||
|
||||
test('tests a configured method through the AWS SDK without a deployed stage', async () => {
|
||||
const commands: unknown[] = []
|
||||
const adapter = new AwsApiGatewayAdapter(fakeClient((command) => {
|
||||
@@ -283,7 +366,7 @@ describe('AwsApiGatewayAdapter', () => {
|
||||
|
||||
await expect(adapter.testApiGatewayMethod('a1b2c3', {
|
||||
resourceId: 'resource456',
|
||||
httpMethod: 'POST',
|
||||
httpMethod: 'ANY',
|
||||
pathWithQueryString: '/orders?dryRun=true',
|
||||
body: '{"name":"Ada"}',
|
||||
headers: {'content-type': 'application/json'},
|
||||
@@ -298,7 +381,7 @@ describe('AwsApiGatewayAdapter', () => {
|
||||
latency: 17,
|
||||
})
|
||||
expect((commands[0] as TestInvokeMethodCommand).input).toEqual({
|
||||
restApiId: 'a1b2c3', resourceId: 'resource456', httpMethod: 'POST',
|
||||
restApiId: 'a1b2c3', resourceId: 'resource456', httpMethod: 'ANY',
|
||||
pathWithQueryString: '/orders?dryRun=true', body: '{"name":"Ada"}',
|
||||
headers: {'content-type': 'application/json'}, multiValueHeaders: {'x-tag': ['one', 'two']},
|
||||
stageVariables: {environment: 'test'},
|
||||
@@ -310,7 +393,7 @@ describe('AwsApiGatewayAdapter', () => {
|
||||
if (command instanceof GetResourcesCommand) {
|
||||
return {items: [
|
||||
{id: 'root123', path: '/', resourceMethods: {}},
|
||||
{id: 'resource456', path: '/hello', resourceMethods: {GET: true, POST: true}},
|
||||
{id: 'resource456', parentId: 'root123', pathPart: 'hello', path: '/hello', resourceMethods: {GET: true, POST: true}},
|
||||
]}
|
||||
}
|
||||
if (command instanceof GetMethodCommand) {
|
||||
@@ -323,6 +406,13 @@ describe('AwsApiGatewayAdapter', () => {
|
||||
}))
|
||||
|
||||
await expect(adapter.listApiGatewayRoutes('a1b2c3')).resolves.toEqual({
|
||||
resources: [
|
||||
{resourceId: 'root123', parentId: null, path: '/', pathPart: null, methods: []},
|
||||
{resourceId: 'resource456', parentId: 'root123', path: '/hello', pathPart: 'hello', methods: [
|
||||
{resourceId: 'resource456', path: '/hello', method: 'GET', functionName: 'hello', integrationType: null},
|
||||
{resourceId: 'resource456', path: '/hello', method: 'POST', functionName: 'hello', integrationType: null},
|
||||
]},
|
||||
],
|
||||
routes: [
|
||||
{resourceId: 'resource456', path: '/hello', method: 'GET', functionName: 'hello', integrationType: null},
|
||||
{resourceId: 'resource456', path: '/hello', method: 'POST', functionName: 'hello', integrationType: null},
|
||||
@@ -331,6 +421,28 @@ describe('AwsApiGatewayAdapter', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test('paginates and returns resources that have no configured methods', async () => {
|
||||
const adapter = new AwsApiGatewayAdapter(fakeClient((command) => {
|
||||
if (command instanceof GetResourcesCommand && !command.input.position) {
|
||||
return {items: [{id: 'root123', path: '/', resourceMethods: {}}], position: 'next'}
|
||||
}
|
||||
if (command instanceof GetResourcesCommand) {
|
||||
return {items: [{id: 'orders123', parentId: 'root123', pathPart: 'orders', path: '/orders'}]}
|
||||
}
|
||||
if (command instanceof GetStagesCommand) return {item: []}
|
||||
throw new Error('Unexpected command')
|
||||
}))
|
||||
|
||||
await expect(adapter.listApiGatewayRoutes('a1b2c3')).resolves.toEqual({
|
||||
resources: [
|
||||
{resourceId: 'root123', parentId: null, path: '/', pathPart: null, methods: []},
|
||||
{resourceId: 'orders123', parentId: 'root123', path: '/orders', pathPart: 'orders', methods: []},
|
||||
],
|
||||
routes: [],
|
||||
stages: [],
|
||||
})
|
||||
})
|
||||
|
||||
test('deletes one method and redeploys its stage', async () => {
|
||||
const commands: unknown[] = []
|
||||
const lambdaCommands: unknown[] = []
|
||||
|
||||
Reference in New Issue
Block a user