feat: add SQS + EventBridge
This commit is contained in:
@@ -2,6 +2,7 @@ import {
|
||||
CreateDeploymentCommand,
|
||||
CreateResourceCommand,
|
||||
CreateRestApiCommand,
|
||||
DeleteMethodCommand,
|
||||
DeleteRestApiCommand,
|
||||
GetRestApiCommand,
|
||||
GetRestApisCommand,
|
||||
@@ -13,12 +14,12 @@ import {
|
||||
type APIGatewayClient,
|
||||
type RestApi,
|
||||
} from '@aws-sdk/client-api-gateway'
|
||||
import {AddPermissionCommand, GetFunctionCommand, type LambdaClient} from '@aws-sdk/client-lambda'
|
||||
import {AddPermissionCommand, GetFunctionCommand, RemovePermissionCommand, type LambdaClient} from '@aws-sdk/client-lambda'
|
||||
import {apiGateway as defaultApiGateway, defaultAccountId, lambda as defaultLambda} from '../aws'
|
||||
import {awsApiGatewaySchema} from '../cloud-spi/apigatewaySchema'
|
||||
import {ValidationError} from '../cloud-spi/errors'
|
||||
import {awsEndpoint} from '../service/runtimeProbe'
|
||||
import type {ApiGatewayLambdaRouteInput, ApiGatewayLambdaRouteResult, ApiGatewayRouteOverview, ApiGatewayRoutesResult, ApiGatewayTestRouteInput, ApiGatewayTestRouteResult, CloudResource, CloudServiceAdapter, CreateResourceInput, ResourceQuery, ServiceSchema} from '../cloud-spi/types'
|
||||
import type {ApiGatewayDeleteRouteInput, ApiGatewayLambdaRouteInput, ApiGatewayLambdaRouteResult, ApiGatewayRouteOverview, ApiGatewayRoutesResult, ApiGatewayTestRouteInput, ApiGatewayTestRouteResult, CloudResource, CloudServiceAdapter, CreateResourceInput, ResourceQuery, ServiceSchema} from '../cloud-spi/types'
|
||||
|
||||
export class AwsApiGatewayAdapter implements CloudServiceAdapter {
|
||||
readonly cloud = 'aws' as const
|
||||
@@ -84,12 +85,7 @@ export class AwsApiGatewayAdapter implements CloudServiceAdapter {
|
||||
throw new ValidationError('method must be GET, POST, PUT, or DELETE')
|
||||
}
|
||||
|
||||
const root = await this.rootResource(apiId)
|
||||
const resource = await this.apiGateway.send(new CreateResourceCommand({
|
||||
restApiId: apiId,
|
||||
parentId: root.id,
|
||||
pathPart,
|
||||
}))
|
||||
const resource = await this.resourceForPath(apiId, `/${pathPart}`) ?? await this.createTopLevelResource(apiId, pathPart)
|
||||
const resourceId = resource.id
|
||||
if (!resourceId) throw new ValidationError('API Gateway did not return a resource id')
|
||||
|
||||
@@ -112,13 +108,23 @@ export class AwsApiGatewayAdapter implements CloudServiceAdapter {
|
||||
uri: `arn:aws:apigateway:${this.region}:lambda:path/2015-03-31/functions/${functionArn}/invocations`,
|
||||
}))
|
||||
|
||||
await this.lambda.send(new AddPermissionCommand({
|
||||
const permission = {
|
||||
FunctionName: functionName,
|
||||
StatementId: `apigateway-${apiId}-${stage}-${method}-${pathPart}`,
|
||||
StatementId: apiGatewayPermissionStatementId(apiId, stage, method, `/${pathPart}`),
|
||||
Action: 'lambda:InvokeFunction',
|
||||
Principal: 'apigateway.amazonaws.com',
|
||||
SourceArn: `arn:aws:execute-api:${this.region}:${this.accountId}:${apiId}/${stage}/${method}/${pathPart}`,
|
||||
}))
|
||||
}
|
||||
try {
|
||||
await this.lambda.send(new AddPermissionCommand(permission))
|
||||
} catch (error) {
|
||||
if (!hasHttpStatus(error, 409)) throw error
|
||||
await this.lambda.send(new RemovePermissionCommand({
|
||||
FunctionName: functionName,
|
||||
StatementId: permission.StatementId,
|
||||
}))
|
||||
await this.lambda.send(new AddPermissionCommand(permission))
|
||||
}
|
||||
|
||||
const deployment = await this.apiGateway.send(new CreateDeploymentCommand({
|
||||
restApiId: apiId,
|
||||
@@ -205,6 +211,40 @@ export class AwsApiGatewayAdapter implements CloudServiceAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
async deleteApiGatewayRoute(apiId: string, input: ApiGatewayDeleteRouteInput): Promise<void> {
|
||||
const method = input.method.toUpperCase()
|
||||
const stage = stageName(input.stage)
|
||||
if (!['GET', 'POST', 'PUT', 'DELETE'].includes(method)) throw new ValidationError('method must be GET, POST, PUT, or DELETE')
|
||||
if (!stringValue(input.resourceId)) throw new ValidationError('resourceId is required')
|
||||
|
||||
const route = await this.apiGateway.send(new GetMethodCommand({
|
||||
restApiId: apiId,
|
||||
resourceId: input.resourceId,
|
||||
httpMethod: method,
|
||||
}))
|
||||
await this.apiGateway.send(new DeleteMethodCommand({
|
||||
restApiId: apiId,
|
||||
resourceId: input.resourceId,
|
||||
httpMethod: method,
|
||||
}))
|
||||
const functionName = lambdaFunctionName(route.methodIntegration?.uri)
|
||||
if (functionName) {
|
||||
try {
|
||||
await this.lambda.send(new RemovePermissionCommand({
|
||||
FunctionName: functionName,
|
||||
StatementId: apiGatewayPermissionStatementId(apiId, stage, method, input.path),
|
||||
}))
|
||||
} catch (error) {
|
||||
if (!hasHttpStatus(error, 404)) throw error
|
||||
}
|
||||
}
|
||||
await this.apiGateway.send(new CreateDeploymentCommand({
|
||||
restApiId: apiId,
|
||||
stageName: stage,
|
||||
description: `Delete ${method} method`,
|
||||
}))
|
||||
}
|
||||
|
||||
private async rootResource(apiId: string) {
|
||||
let position: string | undefined
|
||||
do {
|
||||
@@ -215,6 +255,26 @@ export class AwsApiGatewayAdapter implements CloudServiceAdapter {
|
||||
} while (position)
|
||||
throw new ValidationError('API Gateway root resource was not found')
|
||||
}
|
||||
|
||||
private async resourceForPath(apiId: string, path: string) {
|
||||
let position: string | undefined
|
||||
do {
|
||||
const result = await this.apiGateway.send(new GetResourcesCommand({restApiId: apiId, position}))
|
||||
const resource = result.items?.find((item) => item.path === path)
|
||||
if (resource?.id) return resource
|
||||
position = result.position
|
||||
} while (position)
|
||||
return undefined
|
||||
}
|
||||
|
||||
private async createTopLevelResource(apiId: string, pathPart: string) {
|
||||
const root = await this.rootResource(apiId)
|
||||
return this.apiGateway.send(new CreateResourceCommand({
|
||||
restApiId: apiId,
|
||||
parentId: root.id,
|
||||
pathPart,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
function routePathPart(path: string): string {
|
||||
@@ -252,6 +312,10 @@ function lambdaFunctionName(uri?: string): string | null {
|
||||
return match?.[1] ?? null
|
||||
}
|
||||
|
||||
function apiGatewayPermissionStatementId(apiId: string, stage: string, method: string, path: string): string {
|
||||
return `apigateway-${apiId}-${stage}-${method}-${routePathPart(path)}`
|
||||
}
|
||||
|
||||
function toResource(api: RestApi): CloudResource {
|
||||
const id = api.id ?? ''
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user