Repository: localstack/localstack

Issue:
cloudformation: not creating request parameters in api gateway
<!-- Love localstack? Please consider supporting our collective:
👉  https://opencollective.com/localstack/donate -->

# Type of request: This is a ...

[x] bug report
[ ] feature request

# Detailed description
when you create a cloudformation stack with apigateway / s3 integration the cloudformation code is fine, but in the resulting method integration the request parameters are missing. this works in the aws cloud
...

## Expected behavior
the request parameters are not created.
...

## Actual behavior
the request parameters should be created.
...

# Steps to reproduce
```yaml
Resources:
  DevOfferApi161BA234:
    Type: AWS::ApiGateway::RestApi
    Properties:
      EndpointConfiguration:
        Types:
          - REGIONAL
      Name: DevOfferApi
    Metadata:
      aws:cdk:path: OfferApiStack/DevOfferApi/Resource
  DevOfferApiCloudWatchRole18727FB4:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: apigateway.amazonaws.com
        Version: "2012-10-17"
      ManagedPolicyArns:
        - Fn::Join:
            - ""
            - - "arn:"
              - Ref: AWS::Partition
              - :iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs
    Metadata:
      aws:cdk:path: OfferApiStack/DevOfferApi/CloudWatchRole/Resource
  DevOfferApiAccount61030BB1:
    Type: AWS::ApiGateway::Account
    Properties:
      CloudWatchRoleArn:
        Fn::GetAtt:
          - DevOfferApiCloudWatchRole18727FB4
          - Arn
    DependsOn:
      - DevOfferApi161BA234
    Metadata:
      aws:cdk:path: OfferApiStack/DevOfferApi/Account
  DevOfferApiDeployment67942EBFb7bddcff9bebe189def1e5b267a6cc3f:
    Type: AWS::ApiGateway::Deployment
    Properties:
      RestApiId:
        Ref: DevOfferApi161BA234
      Description: Automatically created by the RestApi construct
    DependsOn:
      - DevOfferApiidDELETE322BCA4A
      - DevOfferApiidGET0A920ED1
      - DevOfferApiidPUTF9263322
      - DevOfferApiidC396C3C1
      - DevOfferApiPOST14096B08
      - DevOfferApiDefaultValidator74076375
      - newoffer4C59669F
    Metadata:
      aws:cdk:path: OfferApiStack/DevOfferApi/Deployment/Resource
  DevOfferApiDeploymentStageprod61EB7D20:
    Type: AWS::ApiGateway::Stage
    Properties:
      RestApiId:
        Ref: DevOfferApi161BA234
      DeploymentId:
        Ref: DevOfferApiDeployment67942EBFb7bddcff9bebe189def1e5b267a6cc3f
      StageName: prod
    Metadata:
      aws:cdk:path: OfferApiStack/DevOfferApi/DeploymentStage.prod/Resource
  DevOfferApiidC396C3C1:
    Type: AWS::ApiGateway::Resource
    Properties:
      ParentId:
        Fn::GetAtt:
          - DevOfferApi161BA234
          - RootResourceId
      PathPart: "{id}"
      RestApiId:
        Ref: DevOfferApi161BA234
    Metadata:
      aws:cdk:path: OfferApiStack/DevOfferApi/Default/{id}/Resource
  DevOfferApiidGET0A920ED1:
    Type: AWS::ApiGateway::Method
    Properties:
      HttpMethod: GET
      ResourceId:
        Ref: DevOfferApiidC396C3C1
      RestApiId:
        Ref: DevOfferApi161BA234
      AuthorizationType: NONE
      Integration:
        Credentials:
          Fn::GetAtt:
            - OfferIntegrationsroleA20FBA4E
            - Arn
        IntegrationHttpMethod: GET
        IntegrationResponses:
          - StatusCode: "200"
        RequestParameters:
          integration.request.path.object: method.request.path.id
        Type: AWS
        Uri:
          Fn::Join:
            - ""
            - - "arn:"
              - Ref: AWS::Partition
              - ":apigateway:"
              - Ref: AWS::Region
              - :s3:path/
              - Ref: OfferIntegrationsofferbucketB94C26F7
              - /{object}.json
      MethodResponses:
        - StatusCode: "200"
      RequestParameters:
        method.request.path.id: false
    Metadata:
      aws:cdk:path: OfferApiStack/DevOfferApi/Default/{id}/GET/Resource
  DevOfferApiidDELETE322BCA4A:
    Type: AWS::ApiGateway::Method
    Properties:
      HttpMethod: DELETE
      ResourceId:
        Ref: DevOfferApiidC396C3C1
      RestApiId:
        Ref: DevOfferApi161BA234
      AuthorizationType: NONE
      Integration:
        Credentials:
          Fn::GetAtt:
            - OfferIntegrationsroleA20FBA4E
            - Arn
        IntegrationHttpMethod: DELETE
        IntegrationResponses:
          - StatusCode: "200"
        RequestParameters:
          integration.request.path.object: method.request.path.id
        Type: AWS
        Uri:
          Fn::Join:
            - ""
            - - "arn:"
              - Ref: AWS::Partition
              - ":apigateway:"
              - Ref: AWS::Region
              - :s3:path/
              - Ref: OfferIntegrationsofferbucketB94C26F7
              - /{object}.json
      MethodResponses:
        - StatusCode: "200"
      RequestParameters:
        method.request.path.id: false
    Metadata:
      aws:cdk:path: OfferApiStack/DevOfferApi/Default/{id}/DELETE/Resource
  DevOfferApiidPUTF9263322:
    Type: AWS::ApiGateway::Method
    Properties:
      HttpMethod: PUT
      ResourceId:
        Ref: DevOfferApiidC396C3C1
      RestApiId:
        Ref: DevOfferApi161BA234
      AuthorizationType: NONE
      Integration:
        Credentials:
          Fn::GetAtt:
            - OfferIntegrationsroleA20FBA4E
            - Arn
        IntegrationHttpMethod: PUT
        IntegrationResponses:
          - StatusCode: "200"
        RequestParameters:
          integration.request.path.object: method.request.path.id
          integration.request.header.Content-Type: method.request.header.Content-Type
        Type: AWS
        Uri:
          Fn::Join:
            - ""
            - - "arn:"
              - Ref: AWS::Partition
              - ":apigateway:"
              - Ref: AWS::Region
              - :s3:path/
              - Ref: OfferIntegrationsofferbucketB94C26F7
              - /{object}.json
      MethodResponses:
        - StatusCode: "200"
      RequestModels:
        application/json:
          Ref: newoffer4C59669F
      RequestParameters:
        method.request.path.id: false
        method.request.header.Content-Type: false
      RequestValidatorId:
        Ref: DevOfferApiDefaultValidator74076375
    Metadata:
      aws:cdk:path: OfferApiStack/DevOfferApi/Default/{id}/PUT/Resource
  DevOfferApiPOST14096B08:
    Type: AWS::ApiGateway::Method
    Properties:
      HttpMethod: POST
      ResourceId:
        Fn::GetAtt:
          - DevOfferApi161BA234
          - RootResourceId
      RestApiId:
        Ref: DevOfferApi161BA234
      AuthorizationType: NONE
      Integration:
        Credentials:
          Fn::GetAtt:
            - OfferIntegrationsroleA20FBA4E
            - Arn
        IntegrationHttpMethod: PUT
        IntegrationResponses:
          - ResponseTemplates:
              application/json: $context.requestId
            StatusCode: "200"
        RequestParameters:
          integration.request.path.object: context.requestId
          integration.request.header.Content-Type: method.request.header.Content-Type
        Type: AWS
        Uri:
          Fn::Join:
            - ""
            - - "arn:"
              - Ref: AWS::Partition
              - ":apigateway:"
              - Ref: AWS::Region
              - :s3:path/
              - Ref: OfferIntegrationsofferbucketB94C26F7
              - /{object}.json
      MethodResponses:
        - StatusCode: "200"
      RequestModels:
        application/json:
          Ref: newoffer4C59669F
      RequestParameters:
        method.request.header.Content-Type: false
      RequestValidatorId:
        Ref: DevOfferApiDefaultValidator74076375
    Metadata:
      aws:cdk:path: OfferApiStack/DevOfferApi/Default/POST/Resource
  DevOfferApiDefaultValidator74076375:
    Type: AWS::ApiGateway::RequestValidator
    Properties:
      RestApiId:
        Ref: DevOfferApi161BA234
      ValidateRequestBody: true
      ValidateRequestParameters: true
    Metadata:
      aws:cdk:path: OfferApiStack/DevOfferApi/DefaultValidator/Resource
  newoffer4C59669F:
    Type: AWS::ApiGateway::Model
    Properties:
      RestApiId:
        Ref: DevOfferApi161BA234
      ContentType: application/json
      Name: NewOffer
      Schema:
        type: object
        required:
          - productId
          - totalPremium
          - totalNetPremium
        properties:
          productId:
            type: string
            description: ID des Produkts
          totalPremium:
            type: number
            description: Gesamtbeitrag
          totalNetPremium:
            type: number
            description: Gesamtnettobeitrag
          components:
            type: array
            items:
              type: object
              required:
                - startOfInsurance
                - paymentMode
                - typeOfDefault
                - targetAmount
                - typeOfRuntime
                - runningTime
                - appropriationOfProfits
                - entryAge
                - durationOfInsurance
                - occupationalGroup
                - sumInsured
                - premium
                - netPremium
                - totalPayoutAmount
              properties:
                productName:
                  type: string
                  description: name of product
                smoker:
                  type: boolean
                homeowner:
                  type: boolean
                jobPosition:
                  type: string
                graduation:
                  type: string
                jobDetails:
                  properties:
                    personnelResponsibility:
                      properties:
                        hasResponsibility:
                          type: boolean
                        numberOfEmployees:
                          type: number
                    homeOfficePercentage:
                      type: number
                    officePercentage:
                      type: number
                    travelPercentage:
                      type: number
                    physicalWorkPercentage:
                      type: number
                smokingBehaviour:
                  type: number
                  description: Rauch-Verhalten
                productConditions:
                  type: string
                  description: Konditionen
                startOfInsurance:
                  type: string
                  format: date
                  description: Beginn
                paymentMode:
                  type: string
                  enum:
                    - monthly
                    - quarterly
                    - semiAnnually
                    - annually
                  description: Inkasso-Zahlweise
                typeOfDefault:
                  type: string
                  enum:
                    - premium
                    - sumInsured
                    - totalPayoutAmount
                  description: Art der Vorgabe
                targetAmount:
                  type: number
                  description: Vorgabesumme
                typeOfRuntime:
                  type: string
                  enum:
                    - runningTime
                    - endingAge
                  description: Art der Laufzeit
                runningTime:
                  type: number
                  description: Laufzeit
                appropriationOfProfits:
                  type: string
                  enum:
                    - instantDiscount
                    - deathBenefit
                  description: Gewinnverwendung
                entryAge:
                  type: number
                  description: Eintrittsalter
                durationOfInsurance:
                  type: number
                  description: Versicherungsdauer
                jobTitle:
                  type: string
                occupationalGroup:
                  type: string
                  enum:
                    - A
                    - B
                  description: Berufsgruppe
                sumInsured:
                  type: number
                  description: Versicherungssumme
                premium:
                  type: number
                  description: Beitrag
                netPremium:
                  type: number
                  description: Nettobeitrag
                totalPayoutAmount:
                  type: number
                  description: Gesamtauszahlungssumme
                riskCalculation:
                  properties:
                    riskResult:
                      type: string
                      enum:
                        - normal
                        - extra-charge
                        - riskClause
                        - expert-decision
                    riskExtraCharge:
                      type: number
        $schema: http://json-schema.org/draft-04/schema#
    Metadata:
      aws:cdk:path: OfferApiStack/new-offer/Resource
  OfferIntegrationsofferbucketB94C26F7:
    Type: AWS::S3::Bucket
    UpdateReplacePolicy: Retain
    DeletionPolicy: Retain
    Metadata:
      aws:cdk:path: OfferApiStack/OfferIntegrations/offer-bucket/Resource
  OfferIntegrationsroleA20FBA4E:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: apigateway.amazonaws.com
        Version: "2012-10-17"
      Path: /service-role/
    Metadata:
      aws:cdk:path: OfferApiStack/OfferIntegrations/role/Resource
  OfferIntegrationsroleDefaultPolicyB9943AE2:
    Type: AWS::IAM::Policy
    Properties:
      PolicyDocument:
        Statement:
          - Action:
              - s3:GetObject*
              - s3:GetBucket*
              - s3:List*
              - s3:DeleteObject*
              - s3:PutObject
              - s3:Abort*
            Effect: Allow
            Resource:
              - Fn::GetAtt:
                  - OfferIntegrationsofferbucketB94C26F7
                  - Arn
              - Fn::Join:
                  - ""
                  - - Fn::GetAtt:
                        - OfferIntegrationsofferbucketB94C26F7
                        - Arn
                    - /*
        Version: "2012-10-17"
      PolicyName: OfferIntegrationsroleDefaultPolicyB9943AE2
      Roles:
        - Ref: OfferIntegrationsroleA20FBA4E
    Metadata:
      aws:cdk:path: OfferApiStack/OfferIntegrations/role/DefaultPolicy/Resource
  CDKMetadata:
    Type: AWS::CDK::Metadata
    Properties:
      Analytics: v2:deflate64:H4sIAAAAAAAAE12QTW/DIAyGf8vulDbapF3XdteqUybtjsDr3BCcgVkVIf77ILTax8mPX+zXNp3sNo9yc/ekLmGlzbBOmjzI9MpKD2L/7o6Rp8hiTy6wj5qr1kOg6DVULg8GGcllUS2SmvCkGC5qlqnU8XbCa8sNt1pTdCyeYbI0j+AWz19ZmX1avBvcpu1U+En+rHEA/iBTpSv18BnLwDdl0Sgm36r/aQcyYJemClmgGsvKZJt3jS9kUc81bZRFuJdpF/UAy86Ncs7CFQt5Duuv7kF29T/PAXHly5k4guxb/Ab5F452bAEAAA==
    Metadata:
      aws:cdk:path: OfferApiStack/CDKMetadata/Default
    Condition: CDKMetadataAvailable
Outputs:
  DevOfferApiEndpoint5E4AE11B:
    Value:
      Fn::Join:
        - ""
        - - https://
          - Ref: DevOfferApi161BA234
          - .execute-api.
          - Ref: AWS::Region
          - "."
          - Ref: AWS::URLSuffix
          - /
          - Ref: DevOfferApiDeploymentStageprod61EB7D20
          - /
Conditions:
  CDKMetadataAvailable:
    Fn::Or:
      - Fn::Or:
          - Fn::Equals:
              - Ref: AWS::Region
              - af-south-1
          - Fn::Equals:
              - Ref: AWS::Region
              - ap-east-1
          - Fn::Equals:
              - Ref: AWS::Region
              - ap-northeast-1
          - Fn::Equals:
              - Ref: AWS::Region
              - ap-northeast-2
          - Fn::Equals:
              - Ref: AWS::Region
              - ap-south-1
          - Fn::Equals:
              - Ref: AWS::Region
              - ap-southeast-1
          - Fn::Equals:
              - Ref: AWS::Region
              - ap-southeast-2
          - Fn::Equals:
              - Ref: AWS::Region
              - ca-central-1
          - Fn::Equals:
              - Ref: AWS::Region
              - cn-north-1
          - Fn::Equals:
              - Ref: AWS::Region
              - cn-northwest-1
      - Fn::Or:
          - Fn::Equals:
              - Ref: AWS::Region
              - eu-central-1
          - Fn::Equals:
              - Ref: AWS::Region
              - eu-north-1
          - Fn::Equals:
              - Ref: AWS::Region
              - eu-south-1
          - Fn::Equals:
              - Ref: AWS::Region
              - eu-west-1
          - Fn::Equals:
              - Ref: AWS::Region
              - eu-west-2
          - Fn::Equals:
              - Ref: AWS::Region
              - eu-west-3
          - Fn::Equals:
              - Ref: AWS::Region
              - me-south-1
          - Fn::Equals:
              - Ref: AWS::Region
              - sa-east-1
          - Fn::Equals:
              - Ref: AWS::Region
              - us-east-1
          - Fn::Equals:
              - Ref: AWS::Region
              - us-east-2
      - Fn::Or:
          - Fn::Equals:
              - Ref: AWS::Region
              - us-west-1
          - Fn::Equals:
              - Ref: AWS::Region
              - us-west-2
```

the created api-gateway, see the request paramters are empty:
```json
{
    "items": [
        {
            "id": "7sw7q6lreq",
            "path": "/",
            "resourceMethods": {
                "POST": {
                    "httpMethod": "POST",
                    "authorizationType": "NONE",
                    "apiKeyRequired": false,
                    "requestParameters": {
                        "method.request.header.Content-Type": false
                    },
                    "methodIntegration": {
                        "type": "AWS",
                        "httpMethod": "PUT",
                        "uri": "arn:aws:apigateway:us-east-1:s3:path/offerapistack-offerintegrationsofferbucketb94c26f7-43d1b30b/{object}.json",
                        "requestParameters": {},
                        "passthroughBehavior": "WHEN_NO_MATCH",
                        "cacheNamespace": "4d2918ca",
                        "cacheKeyParameters": [],
                        "integrationResponses": {}
                    }
                }
            }
        },
        {
            "id": "6wov03mq4d",
            "parentId": "7sw7q6lreq",
            "pathPart": "{id}",
            "path": "/{id}",
            "resourceMethods": {
                "GET": {
                    "httpMethod": "GET",
                    "authorizationType": "NONE",
                    "apiKeyRequired": false,
                    "requestParameters": {
                        "method.request.path.id": false
                    },
                    "methodIntegration": {
                        "type": "AWS",
                        "httpMethod": "GET",
                        "uri": "arn:aws:apigateway:us-east-1:s3:path/offerapistack-offerintegrationsofferbucketb94c26f7-43d1b30b/{object}.json",
                        "requestParameters": {},
                        "passthroughBehavior": "WHEN_NO_MATCH",
                        "cacheNamespace": "0a506b54",
                        "cacheKeyParameters": [],
                        "integrationResponses": {}
                    }
                },
                "DELETE": {
                    "httpMethod": "DELETE",
                    "authorizationType": "NONE",
                    "apiKeyRequired": false,
                    "requestParameters": {
                        "method.request.path.id": false
                    },
                    "methodIntegration": {
                        "type": "AWS",
                        "httpMethod": "DELETE",
                        "uri": "arn:aws:apigateway:us-east-1:s3:path/offerapistack-offerintegrationsofferbucketb94c26f7-43d1b30b/{object}.json",
                        "requestParameters": {},
                        "passthroughBehavior": "WHEN_NO_MATCH",
                        "cacheNamespace": "f7f7182e",
                        "cacheKeyParameters": [],
                        "integrationResponses": {}
                    }
                },
                "PUT": {
                    "httpMethod": "PUT",
                    "authorizationType": "NONE",
                    "apiKeyRequired": false,
                    "requestParameters": {
                        "method.request.path.id": false,
                        "method.request.header.Content-Type": false
                    },
                    "methodIntegration": {
                        "type": "AWS",
                        "httpMethod": "PUT",
                        "uri": "arn:aws:apigateway:us-east-1:s3:path/offerapistack-offerintegrationsofferbucketb94c26f7-43d1b30b/{object}.json",
                        "requestParameters": {},
                        "passthroughBehavior": "WHEN_NO_MATCH",
                        "cacheNamespace": "27674016",
                        "cacheKeyParameters": [],
                        "integrationResponses": {}
                    }
                }
            }
        }
    ]
}
```

## Command used to start LocalStack
docker-compose:
```yaml
version: '3'
services:
  localstack:
    image: localstack/localstack
    container_name: localstack
    network_mode: bridge
    ports:
      - "4566:4566"
      - "4571:4571"
      - '8055:8080'
    environment:
      - SERVICES=s3,apigateway,cloudformation,iam
      - DATA_DIR=/tmp/localstack/data
    volumes:
      - './.localstack:/tmp/localstack'
      - '/var/run/docker.sock:/var/run/docker.sock'
```
...

## Client code (AWS SDK code snippet, or sequence of "awslocal" commands)
cdk-code:
```typescript
const defaultRequestParameters = {
            'integration.request.path.object': 'method.request.path.id'
        };
        this.postIntegration = new apigateway.AwsIntegration(this.createIntegrationProps('PUT',
            this.bucket.bucketName, {
                'integration.request.path.object': 'context.requestId',
                'integration.request.header.Content-Type': 'method.request.header.Content-Type'
            }, this.executeRole, {'application/json': '$context.requestId'}));
        this.getIntegration = new apigateway.AwsIntegration(this.createIntegrationProps('GET',
            this.bucket.bucketName, {...defaultRequestParameters}, this.executeRole));
        this.putIntegration = new apigateway.AwsIntegration(this.createIntegrationProps('PUT',
            this.bucket.bucketName, {
                ...defaultRequestParameters,
                'integration.request.header.Content-Type': 'method.request.header.Content-Type'
            }, this.executeRole));
        this.deleteIntegration = new apigateway.AwsIntegration(this.createIntegrationProps('DELETE',
            this.bucket.bucketName, {...defaultRequestParameters}, this.executeRole));
```
...



Discussion:

Resolving patch:
diff --git a/localstack/services/cloudformation/service_models.py b/localstack/services/cloudformation/service_models.py
--- a/localstack/services/cloudformation/service_models.py
+++ b/localstack/services/cloudformation/service_models.py
@@ -2,6 +2,7 @@
 import logging
 import os
 import re
+from urllib.parse import urlparse
 
 from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
 from moto.core.models import CloudFormationModel
@@ -1335,6 +1336,98 @@ def get_physical_resource_id(self, attribute=None, **kwargs):
         )
         return result
 
+    @classmethod
+    def get_deploy_templates(cls):
+        """
+        https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-method.html
+        https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-method-methodresponse.html
+        https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-method-integration.html
+        https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-method-integration-integrationresponse.html
+        """
+
+        def _subresources(resource_id, resources, resource_type, func, stack_name):
+            apigateway = aws_stack.connect_to_service("apigateway")
+            resource = cls(resources[resource_id])
+            props = resource.props
+
+            integration = props.get("Integration")
+            if integration:
+                api_id = resource.resolve_refs_recursively(
+                    stack_name, props["RestApiId"], resources
+                )
+                res_id = resource.resolve_refs_recursively(
+                    stack_name, props["ResourceId"], resources
+                )
+                kwargs = {}
+                if integration.get("Uri"):
+                    uri = resource.resolve_refs_recursively(
+                        stack_name, integration.get("Uri"), resources
+                    )
+
+                    # Moto has a validate method on Uri for integration_type "HTTP" | "HTTP_PROXY" that does not accept
+                    # Uri value without path, we need to add path ("/") if not exists
+                    if integration.get("Type") in ["HTTP", "HTTP_PROXY"]:
+                        rs = urlparse(uri)
+                        if not rs.path:
+                            uri = "{}/".format(uri)
+
+                    kwargs["uri"] = uri
+
+                if integration.get("IntegrationHttpMethod"):
+                    kwargs["integrationHttpMethod"] = integration["IntegrationHttpMethod"]
+
+                if integration.get("RequestTemplates"):
+                    kwargs["requestTemplates"] = integration["RequestTemplates"]
+
+                if integration.get("Credentials"):
+                    kwargs["credentials"] = integration["Credentials"]
+
+                if integration.get("RequestParameters"):
+                    kwargs["requestParameters"] = integration["RequestParameters"]
+
+                apigateway.put_integration(
+                    restApiId=api_id,
+                    resourceId=res_id,
+                    httpMethod=props["HttpMethod"],
+                    type=integration["Type"],
+                    **kwargs,
+                )
+
+            responses = props.get("MethodResponses") or []
+            for response in responses:
+                api_id = resource.resolve_refs_recursively(
+                    stack_name, props["RestApiId"], resources
+                )
+                res_id = resource.resolve_refs_recursively(
+                    stack_name, props["ResourceId"], resources
+                )
+                apigateway.put_method_response(
+                    restApiId=api_id,
+                    resourceId=res_id,
+                    httpMethod=props["HttpMethod"],
+                    statusCode=str(response["StatusCode"]),
+                    responseParameters=response.get("ResponseParameters", {}),
+                )
+
+        return {
+            "create": [
+                {
+                    "function": "put_method",
+                    "parameters": {
+                        "restApiId": "RestApiId",
+                        "resourceId": "ResourceId",
+                        "httpMethod": "HttpMethod",
+                        "authorizationType": "AuthorizationType",
+                        "authorizerId": "AuthorizerId",
+                        "requestParameters": "RequestParameters",
+                    },
+                },
+                {
+                    "function": _subresources  # dynamic mapping for additional sdk calls for this CFn resource
+                },
+            ]
+        }
+
 
 class GatewayStage(GenericBaseModel):
     @staticmethod
diff --git a/localstack/utils/cloudformation/template_deployer.py b/localstack/utils/cloudformation/template_deployer.py
--- a/localstack/utils/cloudformation/template_deployer.py
+++ b/localstack/utils/cloudformation/template_deployer.py
@@ -5,7 +5,6 @@
 import re
 import traceback
 from typing import Optional
-from urllib.parse import urlparse
 
 import botocore
 from moto.cloudformation import parsing
@@ -258,20 +257,6 @@ def get_ddb_kinesis_stream_specification(params, **kwargs):
         },
         "delete": {"function": "delete_role", "parameters": {"RoleName": "RoleName"}},
     },
-    "ApiGateway::Method": {
-        "create": {
-            "function": "put_method",
-            "parameters": {
-                "restApiId": "RestApiId",
-                "resourceId": "ResourceId",
-                "httpMethod": "HttpMethod",
-                "authorizationType": "AuthorizationType",
-                "authorizerId": "AuthorizerId",
-                "requestParameters": "RequestParameters",
-            },
-        }
-    },
-    "ApiGateway::Method::Integration": {},
     "ApiGateway::Account": {},
     "ApiGateway::Model": {
         "create": {
@@ -1366,55 +1351,7 @@ def run_post_create_actions(action_name, resource_id, resources, resource_type,
     resource_props = resource["Properties"] = resource.get("Properties", {})
 
     # some resources have attached/nested resources which we need to create recursively now
-    if resource_type == "ApiGateway::Method":
-        integration = resource_props.get("Integration")
-        apigateway = aws_stack.connect_to_service("apigateway")
-        if integration:
-            api_id = resolve_refs_recursively(stack_name, resource_props["RestApiId"], resources)
-            res_id = resolve_refs_recursively(stack_name, resource_props["ResourceId"], resources)
-            kwargs = {}
-            if integration.get("Uri"):
-                uri = resolve_refs_recursively(stack_name, integration.get("Uri"), resources)
-
-                # Moto has a validate method on Uri for integration_type "HTTP" | "HTTP_PROXY" that does not accept
-                # Uri value without path, we need to add path ("/") if not exists
-                if integration.get("Type") in ["HTTP", "HTTP_PROXY"]:
-                    rs = urlparse(uri)
-                    if not rs.path:
-                        uri = "{}/".format(uri)
-
-                kwargs["uri"] = uri
-
-            if integration.get("IntegrationHttpMethod"):
-                kwargs["integrationHttpMethod"] = integration["IntegrationHttpMethod"]
-
-            if integration.get("RequestTemplates"):
-                kwargs["requestTemplates"] = integration["RequestTemplates"]
-
-            if integration.get("Credentials"):
-                kwargs["credentials"] = integration["Credentials"]
-
-            apigateway.put_integration(
-                restApiId=api_id,
-                resourceId=res_id,
-                httpMethod=resource_props["HttpMethod"],
-                type=integration["Type"],
-                **kwargs,
-            )
-
-        responses = resource_props.get("MethodResponses") or []
-        for response in responses:
-            api_id = resolve_refs_recursively(stack_name, resource_props["RestApiId"], resources)
-            res_id = resolve_refs_recursively(stack_name, resource_props["ResourceId"], resources)
-            apigateway.put_method_response(
-                restApiId=api_id,
-                resourceId=res_id,
-                httpMethod=resource_props["HttpMethod"],
-                statusCode=str(response["StatusCode"]),
-                responseParameters=response.get("ResponseParameters", {}),
-            )
-
-    elif resource_type == "ApiGateway::RestApi":
+    if resource_type == "ApiGateway::RestApi":
         body = resource_props.get("Body")
         if body:
             client = aws_stack.connect_to_service("apigateway")


Test patch:
diff --git a/tests/integration/cloudformation/test_cloudformation_apigateway.py b/tests/integration/cloudformation/test_cloudformation_apigateway.py
new file mode 100644
--- /dev/null
+++ b/tests/integration/cloudformation/test_cloudformation_apigateway.py
@@ -0,0 +1,57 @@
+import jinja2
+
+from localstack.utils.common import short_uid
+from localstack.utils.generic.wait_utils import wait_until
+from tests.integration.cloudformation.test_cloudformation_changesets import load_template_raw
+
+
+def test_cfn_apigateway_aws_integration(
+    cfn_client,
+    apigateway_client,
+    s3_client,
+    iam_client,
+    is_change_set_created_and_available,
+    is_stack_created,
+    cleanup_changesets,
+    cleanup_stacks,
+):
+    api_name = f"rest-api-{short_uid()}"
+    stack_name = f"stack-{short_uid()}"
+    change_set_name = f"change-set-{short_uid()}"
+    template_rendered = jinja2.Template(
+        load_template_raw("apigw-awsintegration-request-parameters.yaml")
+    ).render(api_name=api_name)
+    response = cfn_client.create_change_set(
+        StackName=stack_name,
+        ChangeSetName=change_set_name,
+        TemplateBody=template_rendered,
+        ChangeSetType="CREATE",
+    )
+    change_set_id = response["Id"]
+    stack_id = response["StackId"]
+
+    try:
+        wait_until(is_change_set_created_and_available(change_set_id))
+        cfn_client.execute_change_set(ChangeSetName=change_set_id)
+        wait_until(is_stack_created(stack_id))
+
+        apis = [
+            api for api in apigateway_client.get_rest_apis()["items"] if api["name"] == api_name
+        ]
+        assert len(apis) == 1
+        api_id = apis[0]["id"]
+
+        resources = apigateway_client.get_resources(restApiId=api_id)["items"]
+        assert (
+            resources[0]["resourceMethods"]["GET"]["requestParameters"]["method.request.path.id"]
+            is False
+        )
+        assert (
+            resources[0]["resourceMethods"]["GET"]["methodIntegration"]["requestParameters"][
+                "integration.request.path.object"
+            ]
+            == "method.request.path.id"
+        )
+    finally:
+        cleanup_changesets([change_set_id])
+        cleanup_stacks([stack_id])
diff --git a/tests/integration/fixtures.py b/tests/integration/fixtures.py
--- a/tests/integration/fixtures.py
+++ b/tests/integration/fixtures.py
@@ -11,8 +11,10 @@
 from localstack.utils.common import short_uid
 
 if TYPE_CHECKING:
+    from mypy_boto3_apigateway import APIGatewayClient
     from mypy_boto3_cloudformation import CloudFormationClient
     from mypy_boto3_dynamodb import DynamoDBClient
+    from mypy_boto3_iam import IAMClient
     from mypy_boto3_kinesis import KinesisClient
     from mypy_boto3_lambda import LambdaClient
     from mypy_boto3_logs import CloudWatchLogsClient
@@ -43,6 +45,16 @@ def dynamodb_client() -> "DynamoDBClient":
     return _client("dynamodb")
 
 
+@pytest.fixture(scope="class")
+def apigateway_client() -> "APIGatewayClient":
+    return _client("apigateway")
+
+
+@pytest.fixture(scope="class")
+def iam_client() -> "IAMClient":
+    return _client("iam")
+
+
 @pytest.fixture(scope="class")
 def s3_client() -> "S3Client":
     return _client("s3")
diff --git a/tests/integration/templates/apigw-awsintegration-request-parameters.yaml b/tests/integration/templates/apigw-awsintegration-request-parameters.yaml
new file mode 100644
--- /dev/null
+++ b/tests/integration/templates/apigw-awsintegration-request-parameters.yaml
@@ -0,0 +1,132 @@
+Resources:
+  Bucket83908E77:
+    Type: AWS::S3::Bucket
+    UpdateReplacePolicy: Delete
+    DeletionPolicy: Delete
+  MyRoleF48FFE04:
+    Type: AWS::IAM::Role
+    Properties:
+      AssumeRolePolicyDocument:
+        Statement:
+          - Action: sts:AssumeRole
+            Effect: Allow
+            Principal:
+              AWS: "*"
+        Version: "2012-10-17"
+      RoleName: MyRole
+  MyRoleDefaultPolicyA36BE1DD:
+    Type: AWS::IAM::Policy
+    Properties:
+      PolicyDocument:
+        Statement:
+          - Action:
+              - s3:GetObject*
+              - s3:GetBucket*
+              - s3:List*
+              - s3:DeleteObject*
+              - s3:PutObject
+              - s3:Abort*
+            Effect: Allow
+            Resource:
+              - Fn::GetAtt:
+                  - Bucket83908E77
+                  - Arn
+              - Fn::Join:
+                  - ""
+                  - - Fn::GetAtt:
+                        - Bucket83908E77
+                        - Arn
+                    - /*
+        Version: "2012-10-17"
+      PolicyName: MyRoleDefaultPolicyA36BE1DD
+      Roles:
+        - Ref: MyRoleF48FFE04
+  RestApi0C43BF4B:
+    Type: AWS::ApiGateway::RestApi
+    Properties:
+      Name: {{ api_name }}
+  RestApiCloudWatchRoleE3ED6605:
+    Type: AWS::IAM::Role
+    Properties:
+      AssumeRolePolicyDocument:
+        Statement:
+          - Action: sts:AssumeRole
+            Effect: Allow
+            Principal:
+              Service: apigateway.amazonaws.com
+        Version: "2012-10-17"
+      ManagedPolicyArns:
+        - Fn::Join:
+            - ""
+            - - "arn:"
+              - Ref: AWS::Partition
+              - :iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs
+  RestApiAccount7C83CF5A:
+    Type: AWS::ApiGateway::Account
+    Properties:
+      CloudWatchRoleArn:
+        Fn::GetAtt:
+          - RestApiCloudWatchRoleE3ED6605
+          - Arn
+    DependsOn:
+      - RestApi0C43BF4B
+  RestApiDeployment180EC50356429cbae887bcd485c89adef8803ba0:
+    Type: AWS::ApiGateway::Deployment
+    Properties:
+      RestApiId:
+        Ref: RestApi0C43BF4B
+      Description: Automatically created by the RestApi construct
+    DependsOn:
+      - RestApiGET0F59260B
+  RestApiDeploymentStageprod3855DE66:
+    Type: AWS::ApiGateway::Stage
+    Properties:
+      RestApiId:
+        Ref: RestApi0C43BF4B
+      DeploymentId:
+        Ref: RestApiDeployment180EC50356429cbae887bcd485c89adef8803ba0
+      StageName: prod
+  RestApiGET0F59260B:
+    Type: AWS::ApiGateway::Method
+    Properties:
+      HttpMethod: GET
+      ResourceId:
+        Fn::GetAtt:
+          - RestApi0C43BF4B
+          - RootResourceId
+      RestApiId:
+        Ref: RestApi0C43BF4B
+      AuthorizationType: NONE
+      Integration:
+        Credentials:
+          Fn::GetAtt:
+            - MyRoleF48FFE04
+            - Arn
+        IntegrationHttpMethod: GET
+        RequestParameters:
+          integration.request.path.object: method.request.path.id
+        Type: AWS
+        Uri:
+          Fn::Join:
+            - ""
+            - - "arn:"
+              - Ref: AWS::Partition
+              - :apigateway:eu-west-1:s3:path/
+              - Ref: Bucket83908E77
+              - /{object}.json
+      RequestParameters:
+        method.request.path.id: false
+Outputs:
+  RestApiEndpoint0551178A:
+    Value:
+      Fn::Join:
+        - ""
+        - - https://
+          - Ref: RestApi0C43BF4B
+          - .execute-api.
+          - Ref: AWS::Region
+          - "."
+          - Ref: AWS::URLSuffix
+          - /
+          - Ref: RestApiDeploymentStageprod3855DE66
+          - /