Gopal
Gopal

Reputation: 65

apigateway websocket sqs integration

I am trying to integrate an API Gateway WebSocket Route with SQS.

I have configured the SQS Integration with below properties

AWS Region: ap-southeast-1 AWS Service: SQS HTTP Method : POST Path override: 111111110111/my-queue

Configured Request Template as

"Action=SendMessage&MessageBody=$util.urlEncode($input.body)##

set($context.requestOverride.header.Content-Type="application/x-www-form-urlencoded")##"

When I try to send the data to SQS it is failing with below error

Error:

(VK1mEHZSyQ0FlZg=) Endpoint request body after transformations: Action=SendMessage&MessageBody=foobar (VK1mEHZSyQ0FlZg=) Sending request to https://sqs.ap-southeast-1.amazonaws.com/111111110111/my-queue (VK1mEHZSyQ0FlZg=) Received response. Integration latency: 16 ms (VK1mEHZSyQ0FlZg=) Endpoint response body before transformations: Unable to determine service/operation name to be authorized

Upvotes: 3

Views: 4840

Answers (2)

user3632094
user3632094

Reputation: 23

To elevate @Aksel's answer answer: here's a Terraform solution; in case anyone is searching for it.

The example given from Terraform's documentation doesn't work for me.


The following tf files are based on @Aksel's answer

main.tf

  • define AWS region and get caller identity
# main.tf

# AWS region
variable "aws_region" {
  description = "AWS region"
  type        = string
  default     = "ap-southeast-1"
  # default     = "ap-northeast-1"
  # default     = "us-east-1"
}

# Caller identity
data "aws_caller_identity" "current" {}

iam.tf

  • Set role for API Gateway, attach role policy which allows sending message to SQS
# iam.tf

resource "aws_iam_role" "apigw_sqs_role" {
  name               = "api_sqs_role"
  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "apigateway.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

resource "aws_iam_role_policy" "allow_apigw_sqs_policy" {
  name   = "allow_apigw_sqs_policy"
  role   = aws_iam_role.apigw_sqs_role.id
  policy = <<EOF
{
  "Version":"2012-10-17",
  "Statement":[{
    "Effect":"Allow",
    "Action":[
        "sqs:SendMessage",
    ],
    "Resource":"*"
  }]
}
EOF
}

sqs.tf

  • Define SQS
# sqs.tf

resource "aws_sqs_queue" "my_sqs_queue" {
  name = "my-queue"
}

apigateway.tf

  • Define API Gateway with websocket protocol
  • Set $default route and attach integration
  • apigateway integration:
    • integration_type = "AWS" and integration_method = "POST"
    • integration_uri = arn:aws:apigateway:<aws_region>:sqs:path/<aws_account_id>/<sqs_name>
      • e.g. arn:aws:apigateway:ap-southeast-1:sqs:path/111111110111/my-queue
    • credentials_arn is the role defined in iam.tf
# apigateway.tf

# API Gateway
resource "aws_apigatewayv2_api" "apigw_websocket" {
  name                       = "websocket-api"
  protocol_type              = "WEBSOCKET"
  route_selection_expression = "$request.body.action"
}

resource "aws_apigatewayv2_route" "apigw_websocket_default_route" {
  api_id                              = aws_apigatewayv2_api.apigw_websocket.id
  route_key                           = "$default"
  route_response_selection_expression = "$default"
  target                              = "integrations/${aws_apigatewayv2_integration.apigw_websocket_default_integration_sqs.id}"
}

resource "aws_apigatewayv2_integration" "apigw_websocket_default_integration_sqs" {
  api_id               = aws_apigatewayv2_api.apigw_websocket.id
  integration_type     = "AWS"
  integration_method   = "POST"
  integration_uri      = "arn:aws:apigateway:${var.aws_region}:sqs:path/${data.aws_caller_identity.current.account_id}/${aws_sqs_queue.my_sqs_queue.name}/"
  credentials_arn      = aws_iam_role.apigw_sqs_role.arn
  passthrough_behavior = "NEVER"
  request_parameters = {
    "integration.request.header.Content-Type" = "'application/x-www-form-urlencoded'"
  }
  request_templates = {
    "application/json" : "Action=SendMessage&MessageBody=$util.urlEncode($input.body)"
  }
}

Upvotes: 1

Aksel
Aksel

Reputation: 83

Almost a year late, but I figured out a way to do this. You can't do it from the console itself, but you can get it to work via the CLI.

Most of the steps are covered in this guide, but make it a WS api instead of REST api.

You'll notice that it isn't possible to specify HTTP header, nor is it possible to specify the mapping template. For some reason, this isn't available on the console. You'll have to do this via the CLI instead. Save the following as a JSON file.

{
  "PassthroughBehavior": "NEVER",
  "RequestParameters": {
    "integration.request.header.Content-Type": "'application/x-www-form-urlencoded'"
  },
  "RequestTemplates": {
    "application/json": "Action=SendMessage&MessageBody=$util.urlEncode($input.body)"
  }
}

And update the integration like so:

aws apigatewayv2 update-integration \ 
--api-id API_ID \
--integration-id INTEGRATION_ID \
--cli-input-json file://update.json

You'll see the API id on the console on the API overview, but the integration ID you'll have to find via the CLI, like so:

aws apigatewayv2 get-integrations --api-id API_ID

This results in the body itself being sent as plaintext to the SQS queue.

Note: You can forego all of this if you create the API with CloudFormation/SAM, as that enables you to set RequestParameters and RequestTemplates directly.

Upvotes: 8

Related Questions