Search code examples
c#amazon-web-servicesamazon-sqsaws-cdkaws-api-gateway-v2

How to map request headers from ApiGatewayV2 to SQS event


I'm using dotnet framework 6.0, CDK v2 and ApiGatewayV2.Alpha I'm trying to send messages from the API Gateway directly to an SQS queue. An integration for this does not yet exist in this version, so i created one myself:

using System.Diagnostics.CodeAnalysis;
using Amazon.CDK.AWS.Apigatewayv2.Alpha;
using Amazon.CDK.AWS.IAM;
using Amazon.CDK.AWS.SQS;
using Constructs;

namespace Infrastructure;

public class HttpSqsSendIntegration : HttpRouteIntegration
{
    private readonly Queue _queue;
    private readonly HttpSqsSendIntegrationProps? _props;
    private readonly Role _role;

    public HttpSqsSendIntegration(Construct scope, string id, Queue queue, HttpSqsSendIntegrationProps? props = null) : base(id)
    {
        ArgumentNullException.ThrowIfNull(queue);
        ArgumentNullException.ThrowIfNull(scope);

        _queue = queue;
        _props = props;
        _role = new Role(scope, "HTTP-API-SQS-Role", new RoleProps
        {
            AssumedBy = new ServicePrincipal("apigateway.amazonaws.com"),
            InlinePolicies = new Dictionary<string, PolicyDocument>
            {
                {
                    "sqs-send", new PolicyDocument(new PolicyDocumentProps
                    {
                        Statements =
                        [
                            new PolicyStatement(new PolicyStatementProps
                            {
                                Actions =
                                [
                                    "sqs:SendMessage"
                                ],
                                Resources =
                                [
                                    queue.QueueArn
                                ],
                                Effect = Effect.ALLOW
                            })
                        ]
                    })
                }
            }
        });
    }

    public override IHttpRouteIntegrationConfig Bind(IHttpRouteIntegrationBindOptions options)
    {
        var paramMapping = new ParameterMapping()
            .Custom("QueueUrl", _queue.QueueUrl)
            .Custom("MessageBody", _props?.MessageBody ?? "$request.body");

        if (!string.IsNullOrWhiteSpace(_props?.MessageAttributes))
        {
            paramMapping.Custom("MessageAttributes", _props.MessageAttributes);
        }

        return new HttpRouteIntegrationConfig
        {
            Type = HttpIntegrationType.AWS_PROXY,
            Subtype = HttpIntegrationSubtype.SQS_SEND_MESSAGE,
            PayloadFormatVersion = PayloadFormatVersion.VERSION_1_0,
            ParameterMapping = paramMapping,
            Credentials = IntegrationCredentials.FromRole(_role)
        };
    }
}

public class HttpSqsSendIntegrationProps
{
    public string? MessageBody { get; init; }

    public string? MessageAttributes { get; init; }

    public string[] Headers { get; init; }
}

I need to map one of the headers from the request to a part of the SQS message. To accomplish this I try to map this header to a message attribute. This is implemented as follows:

var route = new HttpRoute(this, "HttpRoute",
            new HttpRouteProps
            {
                HttpApi = Framework.ApiGateway.Api,
                RouteKey = HttpRouteKey.With("/webhooks/{platform}/{webhook}", HttpMethod.POST),
                Integration = new HttpSqsSendIntegration(this, "SqsRouteIntegration", queue, new HttpSqsSendIntegrationProps
                {
                    MessageAttributes = "{\"Signature\":{\"DataType\":\"String\",\"StringValue\":\"$request.header.signature\"}}"
                })
            });

Whatever i do however results in a 400 with an accompanying exception: "Unable to resolve property MessageAttributes from source {\"Signature\":{\"DataType\":\"String\",\"StringValue\":\"$request.header.signature\"}}. Please make sure that the request to API Gateway contains all the necessary fields specified in request parameters"

Is it at all possible to map headers into an SQS message? If so, how would I do this?


Solution

  • I found the solution, thanks to a user on a Reddit thread! And it was brackets all along.

    I replaced:

    $request.header.signature
    

    with

    ${request.header.signature}