Repository: aws/serverless-application-model

Issue:
Feature request: Support FailOnWarnings for AWS::Serverless::Api
<!-- Make sure we don't have an existing Issue for that feature request (open or closed). -->

### Describe your idea/feature/enhancement

Support [`FailOnWarnings`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html#cfn-apigateway-restapi-failonwarnings) for `AWS::Serverless::Api`


Discussion:
Thanks for the feature request! We have a [roadmap](https://github.com/aws/aws-sam-cli/wiki/SAM-CLI-Roadmap) here, but we also try to take in popular feature requests based on 👍   from the community. We will be adding this to our intake for prioritization.
Also, feel free to submit a PR for this and the team could get that reviewed!

Resolving patch:
diff --git a/samtranslator/model/api/api_generator.py b/samtranslator/model/api/api_generator.py
--- a/samtranslator/model/api/api_generator.py
+++ b/samtranslator/model/api/api_generator.py
@@ -181,6 +181,7 @@ def __init__(
         open_api_version=None,
         models=None,
         domain=None,
+        fail_on_warnings=None,
         description=None,
         mode=None,
         api_key_source_type=None,
@@ -232,6 +233,7 @@ def __init__(
         self.remove_extra_stage = open_api_version
         self.models = models
         self.domain = domain
+        self.fail_on_warnings = fail_on_warnings
         self.description = description
         self.shared_api_usage_plan = shared_api_usage_plan
         self.template_conditions = template_conditions
@@ -277,6 +279,9 @@ def _construct_rest_api(self):
         self._add_binary_media_types()
         self._add_models()
 
+        if self.fail_on_warnings:
+            rest_api.FailOnWarnings = self.fail_on_warnings
+
         if self.disable_execute_api_endpoint is not None:
             self._add_endpoint_extension()
 
diff --git a/samtranslator/model/api/http_api_generator.py b/samtranslator/model/api/http_api_generator.py
--- a/samtranslator/model/api/http_api_generator.py
+++ b/samtranslator/model/api/http_api_generator.py
@@ -48,7 +48,7 @@ def __init__(
         resource_attributes=None,
         passthrough_resource_attributes=None,
         domain=None,
-        fail_on_warnings=False,
+        fail_on_warnings=None,
         description=None,
         disable_execute_api_endpoint=None,
     ):
diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py
--- a/samtranslator/model/sam_resources.py
+++ b/samtranslator/model/sam_resources.py
@@ -1073,6 +1073,7 @@ class SamApi(SamResourceMacro):
         "OpenApiVersion": PropertyType(False, is_str()),
         "Models": PropertyType(False, is_type(dict)),
         "Domain": PropertyType(False, is_type(dict)),
+        "FailOnWarnings": PropertyType(False, is_type(bool)),
         "Description": PropertyType(False, is_str()),
         "Mode": PropertyType(False, is_str()),
         "DisableExecuteApiEndpoint": PropertyType(False, is_type(bool)),
@@ -1136,6 +1137,7 @@ def to_cloudformation(self, **kwargs):
             open_api_version=self.OpenApiVersion,
             models=self.Models,
             domain=self.Domain,
+            fail_on_warnings=self.FailOnWarnings,
             description=self.Description,
             mode=self.Mode,
             api_key_source_type=self.ApiKeySourceType,


Test patch:
diff --git a/integration/combination/test_api_with_fail_on_warnings.py b/integration/combination/test_api_with_fail_on_warnings.py
new file mode 100644
--- /dev/null
+++ b/integration/combination/test_api_with_fail_on_warnings.py
@@ -0,0 +1,29 @@
+from parameterized import parameterized
+
+from integration.helpers.base_test import BaseTest
+
+
+class TestApiWithFailOnWarnings(BaseTest):
+    @parameterized.expand(
+        [
+            ("combination/api_with_fail_on_warnings", True),
+            ("combination/api_with_fail_on_warnings", False),
+        ]
+    )
+    def test_end_point_configuration(self, file_name, disable_value):
+        parameters = [
+            {
+                "ParameterKey": "FailOnWarningsValue",
+                "ParameterValue": "true" if disable_value else "false",
+                "UsePreviousValue": False,
+                "ResolvedValue": "string",
+            }
+        ]
+
+        self.create_and_verify_stack(file_name, parameters)
+
+        rest_api_id = self.get_physical_id_by_type("AWS::ApiGateway::RestApi")
+        apigw_client = self.client_provider.api_client
+
+        api_result = apigw_client.get_rest_api(restApiId=rest_api_id)
+        self.assertEqual(api_result["ResponseMetadata"]["HTTPStatusCode"], 200)
diff --git a/tests/translator/input/api_with_fail_on_warnings.yaml b/tests/translator/input/api_with_fail_on_warnings.yaml
new file mode 100644
--- /dev/null
+++ b/tests/translator/input/api_with_fail_on_warnings.yaml
@@ -0,0 +1,22 @@
+Resources:
+  ApiGatewayApi:
+    Type: AWS::Serverless::Api
+    Properties:
+      StageName: prod
+      FailOnWarnings: true
+  ApiFunction: # Adds a GET api endpoint at "/" to the ApiGatewayApi via an Api event
+    Type: AWS::Serverless::Function
+    Properties:
+      Events:
+        ApiEvent:
+          Type: Api
+          Properties:
+            Path: /
+            Method: get
+            RestApiId:
+              Ref: ApiGatewayApi
+      Runtime: python3.7
+      Handler: index.handler
+      InlineCode: |
+        def handler(event, context):
+            return {'body': 'Hello World!', 'statusCode': 200}
diff --git a/tests/translator/input/error_api_invalid_fail_on_warnings.yaml b/tests/translator/input/error_api_invalid_fail_on_warnings.yaml
new file mode 100644
--- /dev/null
+++ b/tests/translator/input/error_api_invalid_fail_on_warnings.yaml
@@ -0,0 +1,22 @@
+Resources:
+  ApiGatewayApi:
+    Type: AWS::Serverless::Api
+    Properties:
+      StageName: prod
+      FailOnWarnings: ''
+  ApiFunction: # Adds a GET api endpoint at "/" to the ApiGatewayApi via an Api event
+    Type: AWS::Serverless::Function
+    Properties:
+      Events:
+        ApiEvent:
+          Type: Api
+          Properties:
+            Path: /
+            Method: get
+            RestApiId:
+              Ref: ApiGatewayApi
+      Runtime: python3.7
+      Handler: index.handler
+      InlineCode: |
+        def handler(event, context):
+            return {'body': 'Hello World!', 'statusCode': 200}
diff --git a/tests/translator/output/api_with_fail_on_warnings.json b/tests/translator/output/api_with_fail_on_warnings.json
new file mode 100644
--- /dev/null
+++ b/tests/translator/output/api_with_fail_on_warnings.json
@@ -0,0 +1,128 @@
+{
+  "Resources": {
+    "ApiGatewayApi": {
+      "Type": "AWS::ApiGateway::RestApi",
+      "Properties": {
+        "Body": {
+          "info": {
+            "version": "1.0",
+            "title": {
+              "Ref": "AWS::StackName"
+            }
+          },
+          "paths": {
+            "/": {
+              "get": {
+                "x-amazon-apigateway-integration": {
+                  "httpMethod": "POST",
+                  "type": "aws_proxy",
+                  "uri": {
+                    "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiFunction.Arn}/invocations"
+                  }
+                },
+                "responses": {}
+              }
+            }
+          },
+          "swagger": "2.0"
+        },
+        "FailOnWarnings": true
+      }
+    },
+    "ApiGatewayApiDeploymentf96bc9abda": {
+      "Type": "AWS::ApiGateway::Deployment",
+      "Properties": {
+        "RestApiId": {
+          "Ref": "ApiGatewayApi"
+        },
+        "Description": "RestApi deployment id: f96bc9abdad53c001153ce8ba04f1667c7b0a004",
+        "StageName": "Stage"
+      }
+    },
+    "ApiFunctionRole": {
+      "Type": "AWS::IAM::Role",
+      "Properties": {
+        "AssumeRolePolicyDocument": {
+          "Version": "2012-10-17",
+          "Statement": [
+            {
+              "Action": [
+                "sts:AssumeRole"
+              ],
+              "Effect": "Allow",
+              "Principal": {
+                "Service": [
+                  "lambda.amazonaws.com"
+                ]
+              }
+            }
+          ]
+        },
+        "ManagedPolicyArns": [
+          "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
+        ],
+        "Tags": [
+          {
+            "Value": "SAM",
+            "Key": "lambda:createdBy"
+          }
+        ]
+      }
+    },
+    "ApiFunction": {
+      "Type": "AWS::Lambda::Function",
+      "Properties": {
+        "Handler": "index.handler",
+        "Code": {
+          "ZipFile": "def handler(event, context):\n    return {'body': 'Hello World!', 'statusCode': 200}\n"
+        },
+        "Role": {
+          "Fn::GetAtt": [
+            "ApiFunctionRole",
+            "Arn"
+          ]
+        },
+        "Runtime": "python3.7",
+        "Tags": [
+          {
+            "Value": "SAM",
+            "Key": "lambda:createdBy"
+          }
+        ]
+      }
+    },
+    "ApiFunctionApiEventPermissionprod": {
+      "Type": "AWS::Lambda::Permission",
+      "Properties": {
+        "Action": "lambda:InvokeFunction",
+        "Principal": "apigateway.amazonaws.com",
+        "FunctionName": {
+          "Ref": "ApiFunction"
+        },
+        "SourceArn": {
+          "Fn::Sub": [
+            "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/",
+            {
+              "__Stage__": "*",
+              "__ApiId__": {
+                "Ref": "ApiGatewayApi"
+              }
+            }
+          ]
+        }
+      }
+    },
+    "ApiGatewayApiprodStage": {
+      "Type": "AWS::ApiGateway::Stage",
+      "Properties": {
+        "DeploymentId": {
+          "Ref": "ApiGatewayApiDeploymentf96bc9abda"
+        },
+        "RestApiId": {
+          "Ref": "ApiGatewayApi"
+        },
+        "StageName": "prod"
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/tests/translator/output/aws-cn/api_with_fail_on_warnings.json b/tests/translator/output/aws-cn/api_with_fail_on_warnings.json
new file mode 100644
--- /dev/null
+++ b/tests/translator/output/aws-cn/api_with_fail_on_warnings.json
@@ -0,0 +1,136 @@
+{
+  "Resources": {
+    "ApiGatewayApi": {
+      "Type": "AWS::ApiGateway::RestApi",
+      "Properties": {
+        "Body": {
+          "info": {
+            "version": "1.0",
+            "title": {
+              "Ref": "AWS::StackName"
+            }
+          },
+          "paths": {
+            "/": {
+              "get": {
+                "x-amazon-apigateway-integration": {
+                  "httpMethod": "POST",
+                  "type": "aws_proxy",
+                  "uri": {
+                    "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiFunction.Arn}/invocations"
+                  }
+                },
+                "responses": {}
+              }
+            }
+          },
+          "swagger": "2.0"
+        },
+        "FailOnWarnings": true,
+        "EndpointConfiguration": {
+          "Types": [
+            "REGIONAL"
+          ]
+        },
+        "Parameters": {
+          "endpointConfigurationTypes": "REGIONAL"
+        }
+      }
+    },
+    "ApiGatewayApiDeploymentb0ed1521b2": {
+      "Type": "AWS::ApiGateway::Deployment",
+      "Properties": {
+        "RestApiId": {
+          "Ref": "ApiGatewayApi"
+        },
+        "Description": "RestApi deployment id: b0ed1521b2c5e65da74d55d16b139143ae483503",
+        "StageName": "Stage"
+      }
+    },
+    "ApiFunctionRole": {
+      "Type": "AWS::IAM::Role",
+      "Properties": {
+        "AssumeRolePolicyDocument": {
+          "Version": "2012-10-17",
+          "Statement": [
+            {
+              "Action": [
+                "sts:AssumeRole"
+              ],
+              "Effect": "Allow",
+              "Principal": {
+                "Service": [
+                  "lambda.amazonaws.com"
+                ]
+              }
+            }
+          ]
+        },
+        "ManagedPolicyArns": [
+          "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
+        ],
+        "Tags": [
+          {
+            "Value": "SAM",
+            "Key": "lambda:createdBy"
+          }
+        ]
+      }
+    },
+    "ApiFunction": {
+      "Type": "AWS::Lambda::Function",
+      "Properties": {
+        "Handler": "index.handler",
+        "Code": {
+          "ZipFile": "def handler(event, context):\n    return {'body': 'Hello World!', 'statusCode': 200}\n"
+        },
+        "Role": {
+          "Fn::GetAtt": [
+            "ApiFunctionRole",
+            "Arn"
+          ]
+        },
+        "Runtime": "python3.7",
+        "Tags": [
+          {
+            "Value": "SAM",
+            "Key": "lambda:createdBy"
+          }
+        ]
+      }
+    },
+    "ApiFunctionApiEventPermissionprod": {
+      "Type": "AWS::Lambda::Permission",
+      "Properties": {
+        "Action": "lambda:InvokeFunction",
+        "Principal": "apigateway.amazonaws.com",
+        "FunctionName": {
+          "Ref": "ApiFunction"
+        },
+        "SourceArn": {
+          "Fn::Sub": [
+            "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/",
+            {
+              "__Stage__": "*",
+              "__ApiId__": {
+                "Ref": "ApiGatewayApi"
+              }
+            }
+          ]
+        }
+      }
+    },
+    "ApiGatewayApiprodStage": {
+      "Type": "AWS::ApiGateway::Stage",
+      "Properties": {
+        "DeploymentId": {
+          "Ref": "ApiGatewayApiDeploymentb0ed1521b2"
+        },
+        "RestApiId": {
+          "Ref": "ApiGatewayApi"
+        },
+        "StageName": "prod"
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/tests/translator/output/aws-us-gov/api_with_fail_on_warnings.json b/tests/translator/output/aws-us-gov/api_with_fail_on_warnings.json
new file mode 100644
--- /dev/null
+++ b/tests/translator/output/aws-us-gov/api_with_fail_on_warnings.json
@@ -0,0 +1,136 @@
+{
+  "Resources": {
+    "ApiGatewayApiDeployment4539d6d48c": {
+      "Type": "AWS::ApiGateway::Deployment",
+      "Properties": {
+        "RestApiId": {
+          "Ref": "ApiGatewayApi"
+        },
+        "Description": "RestApi deployment id: 4539d6d48c1b3fdc71e492190aa2eeff27526d7f",
+        "StageName": "Stage"
+      }
+    },
+    "ApiGatewayApi": {
+      "Type": "AWS::ApiGateway::RestApi",
+      "Properties": {
+        "Body": {
+          "info": {
+            "version": "1.0",
+            "title": {
+              "Ref": "AWS::StackName"
+            }
+          },
+          "paths": {
+            "/": {
+              "get": {
+                "x-amazon-apigateway-integration": {
+                  "httpMethod": "POST",
+                  "type": "aws_proxy",
+                  "uri": {
+                    "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiFunction.Arn}/invocations"
+                  }
+                },
+                "responses": {}
+              }
+            }
+          },
+          "swagger": "2.0"
+        },
+        "FailOnWarnings": true,
+        "EndpointConfiguration": {
+          "Types": [
+            "REGIONAL"
+          ]
+        },
+        "Parameters": {
+          "endpointConfigurationTypes": "REGIONAL"
+        }
+      }
+    },
+    "ApiFunctionRole": {
+      "Type": "AWS::IAM::Role",
+      "Properties": {
+        "AssumeRolePolicyDocument": {
+          "Version": "2012-10-17",
+          "Statement": [
+            {
+              "Action": [
+                "sts:AssumeRole"
+              ],
+              "Effect": "Allow",
+              "Principal": {
+                "Service": [
+                  "lambda.amazonaws.com"
+                ]
+              }
+            }
+          ]
+        },
+        "ManagedPolicyArns": [
+          "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
+        ],
+        "Tags": [
+          {
+            "Value": "SAM",
+            "Key": "lambda:createdBy"
+          }
+        ]
+      }
+    },
+    "ApiFunction": {
+      "Type": "AWS::Lambda::Function",
+      "Properties": {
+        "Handler": "index.handler",
+        "Code": {
+          "ZipFile": "def handler(event, context):\n    return {'body': 'Hello World!', 'statusCode': 200}\n"
+        },
+        "Role": {
+          "Fn::GetAtt": [
+            "ApiFunctionRole",
+            "Arn"
+          ]
+        },
+        "Runtime": "python3.7",
+        "Tags": [
+          {
+            "Value": "SAM",
+            "Key": "lambda:createdBy"
+          }
+        ]
+      }
+    },
+    "ApiFunctionApiEventPermissionprod": {
+      "Type": "AWS::Lambda::Permission",
+      "Properties": {
+        "Action": "lambda:InvokeFunction",
+        "Principal": "apigateway.amazonaws.com",
+        "FunctionName": {
+          "Ref": "ApiFunction"
+        },
+        "SourceArn": {
+          "Fn::Sub": [
+            "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/",
+            {
+              "__Stage__": "*",
+              "__ApiId__": {
+                "Ref": "ApiGatewayApi"
+              }
+            }
+          ]
+        }
+      }
+    },
+    "ApiGatewayApiprodStage": {
+      "Type": "AWS::ApiGateway::Stage",
+      "Properties": {
+        "DeploymentId": {
+          "Ref": "ApiGatewayApiDeployment4539d6d48c"
+        },
+        "RestApiId": {
+          "Ref": "ApiGatewayApi"
+        },
+        "StageName": "prod"
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/tests/translator/output/error_api_invalid_fail_on_warnings.json b/tests/translator/output/error_api_invalid_fail_on_warnings.json
new file mode 100644
--- /dev/null
+++ b/tests/translator/output/error_api_invalid_fail_on_warnings.json
@@ -0,0 +1,8 @@
+{
+    "errors": [
+      {
+        "errorMessage": "Resource with id [ApiGatewayApi] is invalid. FailOnWarnings cannot be empty."
+      }
+    ], 
+    "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [ApiGatewayApi] is invalid. Type of property 'FailOnWarnings' is invalid."
+  }
\ No newline at end of file