Ensure the spcified api_name with the corresponding swaggerfile is deployed to the
    given stage_name in AWS ApiGateway.

    this state currently only supports ApiGateway integration with AWS Lambda, and CORS support is
    handled through a Mock integration.

    There may be multiple deployments for the API object, each deployment is tagged with a description
    (i.e. unique label) in pretty printed json format consisting of the following key/values.

    .. code-block:: text

        {
            "api_name": api_name,
            "swagger_file": basename_of_swagger_file
            "swagger_file_md5sum": md5sum_of_swagger_file,
            "swagger_info_object": info_object_content_in_swagger_file
        }

    Please note that the name of the lambda function to be integrated will be derived
    via the provided lambda_funcname_format parameters:

    - the default lambda_funcname_format is a string with the following
      substitutable keys: "{stage}_{api}_{resource}_{method}".  The user can
      choose to reorder the known keys.
    - the stage key corresponds to the stage_name passed in.
    - the api key corresponds to the api_name passed in.
    - the resource corresponds to the resource path defined in the passed swagger file.
    - the method corresponds to the method for a resource path defined in the passed swagger file.

    For the default lambda_funcname_format, given the following input:

    .. code-block:: python

        api_name = '  Test    Service'
        stage_name = 'alpha'
        basePath = '/api'
        path = '/a/{b}/c'
        method = 'POST'

    We will end up with the following Lambda Function Name that will be looked
    up: 'test_service_alpha_a_b_c_post'

    The canconicalization of these input parameters is done in the following order:

    1. lambda_funcname_format is formatted with the input parameters as passed,
    2. resulting string is stripped for leading/trailing spaces,
    3. path parameter's curly braces are removed from the resource path,
    4. consecutive spaces and forward slashes in the paths are replaced with '_'
    5. consecutive '_' are replaced with '_'

    Please note that for error response handling, the swagger file must have an error response model
    with the following schema.  The lambda functions should throw exceptions for any non successful responses.
    An optional pattern field can be specified in errorMessage field to aid the response mapping from Lambda
    to the proper error return status codes.

    .. code-block:: yaml

        Error:
          type: object
          properties:
            stackTrace:
              type: array
              items:
                type: array
                items:
                  type: string
              description: call stack
          errorType:
            type: string
            description: error type
          errorMessage:
            type: string
            description: |
              Error message, will be matched based on pattern.
              If no pattern is specified, the default pattern used for response mapping will be +*.

    name
        The name of the state definition

    api_name
        The name of the rest api that we want to ensure exists in AWS API Gateway

    swagger_file
        Name of the location of the swagger rest api definition file in YAML format.

    stage_name
        Name of the stage we want to be associated with the given api_name and swagger_file
        definition

    api_key_required
        True or False - whether the API Key is required to call API methods

    lambda_integration_role
        The name or ARN of the IAM role that the AWS ApiGateway assumes when it
        executes your lambda function to handle incoming requests

    lambda_region
        The region where we expect to find the lambda functions.  This is used to
        determine the region where we should look for the Lambda Function for
        integration purposes.  The region determination is based on the following
        priority:

        1. lambda_region as passed in (is not None)
        2. if lambda_region is None, use the region as if a boto_lambda
           function were executed without explicitly specifying lambda region.
        3. if region determined in (2) is different than the region used by
           boto_apigateway functions, a final lookup will be attempted using
           the boto_apigateway region.

    stage_variables
        A dict with variables and their values, or a pillar key (string) that
        contains a dict with variables and their values.
        key and values in the dict must be strings.  {'string': 'string'}

    region
        Region to connect to.

    key
        Secret key to be used.

    keyid
        Access key to be used.

    profile
        A dict with region, key and keyid, or a pillar key (string) that
        contains a dict with region, key and keyid.

    lambda_funcname_format
        Please review the earlier example for the usage.  The only substituable keys in the funcname
        format are {stage}, {api}, {resource}, {method}.
        Any other keys or positional subsitution parameters will be flagged as an invalid input.

    authorization_type
        This field can be either 'NONE', or 'AWS_IAM'.  This will be applied to all methods in the given
        swagger spec file.  Default is set to 'NONE'

    error_response_template
        String value that defines the response template mapping that should be applied in cases error occurs.
        Refer to AWS documentation for details: http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html

        If set to None, the following default value is used:

        .. code-block:: text

            '#set($inputRoot = $input.path(\'$\'))\\n'
            '{\\n'
            '  "errorMessage" : "$inputRoot.errorMessage",\\n'
            '  "errorType" : "$inputRoot.errorType",\\n'
            '  "stackTrace" : [\\n'
            '#foreach($stackTrace in $inputRoot.stackTrace)\\n'
            '    [\\n'
            '#foreach($elem in $stackTrace)\\n'
            '      "$elem"\\n'
            '#if($foreach.hasNext),#end\\n'
            '#end\\n'
            '    ]\\n'
            '#if($foreach.hasNext),#end\\n'
            '#end\\n'
            '  ]\\n'

        .. versionadded:: 2017.7.0

    response_template
        String value that defines the response template mapping applied in case
        of success (including OPTIONS method) If set to None, empty ({})
        template is assumed, which will transfer response from the lambda
        function as is.

        .. versionadded:: 2017.7.0

def present(name, api_name, swagger_file, stage_name, api_key_required,
            lambda_integration_role, lambda_region=None, stage_variables=None,
            region=None, key=None, keyid=None, profile=None,
            lambda_funcname_format='{stage}_{api}_{resource}_{method}',
            authorization_type='NONE', error_response_template=None, response_template=None):
    '''
    Ensure the spcified api_name with the corresponding swaggerfile is deployed to the
    given stage_name in AWS ApiGateway.

    this state currently only supports ApiGateway integration with AWS Lambda, and CORS support is
    handled through a Mock integration.

    There may be multiple deployments for the API object, each deployment is tagged with a description
    (i.e. unique label) in pretty printed json format consisting of the following key/values.

    .. code-block:: text

        {
            "api_name": api_name,
            "swagger_file": basename_of_swagger_file
            "swagger_file_md5sum": md5sum_of_swagger_file,
            "swagger_info_object": info_object_content_in_swagger_file
        }

    Please note that the name of the lambda function to be integrated will be derived
    via the provided lambda_funcname_format parameters:

    - the default lambda_funcname_format is a string with the following
      substitutable keys: "{stage}_{api}_{resource}_{method}".  The user can
      choose to reorder the known keys.
    - the stage key corresponds to the stage_name passed in.
    - the api key corresponds to the api_name passed in.
    - the resource corresponds to the resource path defined in the passed swagger file.
    - the method corresponds to the method for a resource path defined in the passed swagger file.

    For the default lambda_funcname_format, given the following input:

    .. code-block:: python

        api_name = '  Test    Service'
        stage_name = 'alpha'
        basePath = '/api'
        path = '/a/{b}/c'
        method = 'POST'

    We will end up with the following Lambda Function Name that will be looked
    up: 'test_service_alpha_a_b_c_post'

    The canconicalization of these input parameters is done in the following order:

    1. lambda_funcname_format is formatted with the input parameters as passed,
    2. resulting string is stripped for leading/trailing spaces,
    3. path parameter's curly braces are removed from the resource path,
    4. consecutive spaces and forward slashes in the paths are replaced with '_'
    5. consecutive '_' are replaced with '_'

    Please note that for error response handling, the swagger file must have an error response model
    with the following schema.  The lambda functions should throw exceptions for any non successful responses.
    An optional pattern field can be specified in errorMessage field to aid the response mapping from Lambda
    to the proper error return status codes.

    .. code-block:: yaml

        Error:
          type: object
          properties:
            stackTrace:
              type: array
              items:
                type: array
                items:
                  type: string
              description: call stack
          errorType:
            type: string
            description: error type
          errorMessage:
            type: string
            description: |
              Error message, will be matched based on pattern.
              If no pattern is specified, the default pattern used for response mapping will be +*.

    name
        The name of the state definition

    api_name
        The name of the rest api that we want to ensure exists in AWS API Gateway

    swagger_file
        Name of the location of the swagger rest api definition file in YAML format.

    stage_name
        Name of the stage we want to be associated with the given api_name and swagger_file
        definition

    api_key_required
        True or False - whether the API Key is required to call API methods

    lambda_integration_role
        The name or ARN of the IAM role that the AWS ApiGateway assumes when it
        executes your lambda function to handle incoming requests

    lambda_region
        The region where we expect to find the lambda functions.  This is used to
        determine the region where we should look for the Lambda Function for
        integration purposes.  The region determination is based on the following
        priority:

        1. lambda_region as passed in (is not None)
        2. if lambda_region is None, use the region as if a boto_lambda
           function were executed without explicitly specifying lambda region.
        3. if region determined in (2) is different than the region used by
           boto_apigateway functions, a final lookup will be attempted using
           the boto_apigateway region.

    stage_variables
        A dict with variables and their values, or a pillar key (string) that
        contains a dict with variables and their values.
        key and values in the dict must be strings.  {'string': 'string'}

    region
        Region to connect to.

    key
        Secret key to be used.

    keyid
        Access key to be used.

    profile
        A dict with region, key and keyid, or a pillar key (string) that
        contains a dict with region, key and keyid.

    lambda_funcname_format
        Please review the earlier example for the usage.  The only substituable keys in the funcname
        format are {stage}, {api}, {resource}, {method}.
        Any other keys or positional subsitution parameters will be flagged as an invalid input.

    authorization_type
        This field can be either 'NONE', or 'AWS_IAM'.  This will be applied to all methods in the given
        swagger spec file.  Default is set to 'NONE'

    error_response_template
        String value that defines the response template mapping that should be applied in cases error occurs.
        Refer to AWS documentation for details: http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html

        If set to None, the following default value is used:

        .. code-block:: text

            '#set($inputRoot = $input.path(\'$\'))\\n'
            '{\\n'
            '  "errorMessage" : "$inputRoot.errorMessage",\\n'
            '  "errorType" : "$inputRoot.errorType",\\n'
            '  "stackTrace" : [\\n'
            '#foreach($stackTrace in $inputRoot.stackTrace)\\n'
            '    [\\n'
            '#foreach($elem in $stackTrace)\\n'
            '      "$elem"\\n'
            '#if($foreach.hasNext),#end\\n'
            '#end\\n'
            '    ]\\n'
            '#if($foreach.hasNext),#end\\n'
            '#end\\n'
            '  ]\\n'

        .. versionadded:: 2017.7.0

    response_template
        String value that defines the response template mapping applied in case
        of success (including OPTIONS method) If set to None, empty ({})
        template is assumed, which will transfer response from the lambda
        function as is.

        .. versionadded:: 2017.7.0
    '''
    ret = {'name': name,
           'result': True,
           'comment': '',
           'changes': {}
           }

    try:
        common_args = dict([('region', region),
                            ('key', key),
                            ('keyid', keyid),
                            ('profile', profile)])

        # try to open the swagger file and basic validation
        swagger = _Swagger(api_name, stage_name,
                           lambda_funcname_format,
                           swagger_file,
                           error_response_template, response_template,
                           common_args)

        # retrieve stage variables
        stage_vars = _get_stage_variables(stage_variables)

        # verify if api and stage already exists
        ret = swagger.verify_api(ret)
        if ret.get('publish'):
            # there is a deployment label with signature matching the given api_name,
            # swagger file name, swagger file md5 sum, and swagger file info object
            # just reassociate the stage_name to the given deployment label.
            if __opts__['test']:
                ret['comment'] = ('[stage: {0}] will be reassociated to an already available '
                                  'deployment that matched the given [api_name: {1}] '
                                  'and [swagger_file: {2}].\n'
                                  'Stage variables will be set '
                                  'to {3}.'.format(stage_name, api_name, swagger_file, stage_vars))
                ret['result'] = None
                return ret
            return swagger.publish_api(ret, stage_vars)

        if ret.get('current'):
            # already at desired state for the stage, swagger_file, and api_name
            if __opts__['test']:
                ret['comment'] = ('[stage: {0}] is already at desired state with an associated '
                                  'deployment matching the given [api_name: {1}] '
                                  'and [swagger_file: {2}].\n'
                                  'Stage variables will be set '
                                  'to {3}.'.format(stage_name, api_name, swagger_file, stage_vars))
                ret['result'] = None
            return swagger.overwrite_stage_variables(ret, stage_vars)

        # there doesn't exist any previous deployments for the given swagger_file, we need
        # to redeploy the content of the swagger file to the api, models, and resources object
        # and finally create a new deployment and tie the stage_name to this new deployment
        if __opts__['test']:
            ret['comment'] = ('There is no deployment matching the given [api_name: {0}] '
                              'and [swagger_file: {1}].  A new deployment will be '
                              'created and the [stage_name: {2}] will then be associated '
                              'to the newly created deployment.\n'
                              'Stage variables will be set '
                              'to {3}.'.format(api_name, swagger_file, stage_name, stage_vars))
            ret['result'] = None
            return ret

        ret = swagger.deploy_api(ret)
        if ret.get('abort'):
            return ret

        ret = swagger.deploy_models(ret)
        if ret.get('abort'):
            return ret

        ret = swagger.deploy_resources(ret,
                                       api_key_required=api_key_required,
                                       lambda_integration_role=lambda_integration_role,
                                       lambda_region=lambda_region,
                                       authorization_type=authorization_type)
        if ret.get('abort'):
            return ret

        ret = swagger.publish_api(ret, stage_vars)

    except (ValueError, IOError) as e:
        ret['result'] = False
        ret['comment'] = '{0}'.format(e.args)

    return ret