Chalice
=======

.. class:: Chalice(app_name)

   This class represents a chalice application.  It provides:

   * The ability to register routes using the :meth:`route` method.
   * Within a view function, the ability to introspect the current
     request using the ``current_request`` attribute which is an instance
     of the :class:`Request` class.

   .. attribute:: current_request

      An object of type :class:`Request`.  This value is only set when
      a view function is being called.  This attribute can be used to
      introspect the current HTTP request.

   .. attribute:: api

      An object of type :class:`APIGateway`. This attribute can be used to control
      how apigateway interprets ``Content-Type`` headers in both requests and
      responses.

   .. attribute:: lambda_context

      A Lambda context object that is passed to the invoked view by AWS
      Lambda. You can find out more about this object by reading the
      `lambda context object documentation <https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html>`_.

   .. attribute:: debug

      A boolean value that enables debugging.  By default, this value is
      ``False``.  If debugging is true, then internal errors are returned back
      to the client.  Additionally, debug log messages generated by the
      framework will show up in the cloudwatch logs.  Example usage:

      .. code-block:: python

         from chalice import Chalice

         app = Chalice(app_name="appname")
         app.debug = True

   .. method:: route(path, \*\*options)

      Register a view function for a particular URI path.  This method
      is intended to be used as a decorator for a view function.  For example:

      .. code-block:: python

         from chalice import Chalice

         app = Chalice(app_name="appname")

         @app.route('/resource/{value}', methods=['PUT'])
         def viewfunction(value):
             pass


      :param str path: The path to associate with the view function.  The
        ``path`` should only contain ``[a-zA-Z0-9._-]`` chars and curly
        braces for parts of the URL you would like to capture.  The path
        should not end in a trailing slash, otherwise a validation error
        will be raised during deployment.

      :param list methods: Optional parameter that indicates which HTTP methods
        this view function should accept.  By default, only ``GET`` requests
        are supported.  If you only wanted to support ``POST`` requests, you
        would specify ``methods=['POST']``.  If you support multiple HTTP
        methods in a single view function (``methods=['GET', 'POST']``), you
        can check the :attr:`app.current_request.method <Request.method>`
        attribute to see which HTTP method was used when making the request.

      :param str name: Optional parameter to specify the name of the view
        function.  You generally do not need to set this value.  The name
        of the view function is used as the default value for the view name.

      :param Authorizer authorizer: Specify an authorizer to use for this
        view.  Can be an instance of :class:`CognitoUserPoolAuthorizer`,
        :class:`CustomAuthorizer` or :class:`IAMAuthorizer`.

      :param str content_types: A list of content types to accept for
        this view.  By default ``application/json`` is accepted.  If
        this value is specified, then chalice will reject any incoming request
        that does not match the provided list of content types with a
        415 Unsupported Media Type response.

      :param boolean api_key_required: Optional parameter to specify whether
        the method required a valid API key.

      :param cors: Specify if CORS is supported for this view.  This can either
        by a boolean value or an instance of :class:`CORSConfig`.  Setting this
        value is set to ``True`` gives similar behavior to enabling CORS in the
        AWS Console.  This includes injecting the
        ``Access-Control-Allow-Origin`` header to have a value of ``*`` as well
        as adding an ``OPTIONS`` method to support preflighting requests.  If
        you would like more control over how CORS is configured, you can
        provide an instance of :class:`CORSConfig`.

   .. method:: authorizer(name, \*\*options)

      Register a built-in authorizer.

      .. code-block:: python

         from chalice import Chalice, AuthResponse

         app = Chalice(app_name="appname")

         @app.authorizer(ttl_seconds=30)
         def my_auth(auth_request):
             # Validate auth_request.token, and then:
             return AuthResponse(routes=['/'], principal_id='username')

         @app.route('/', authorizer=my_auth)
         def viewfunction(value):
             pass

      :param ttl_seconds: The number of seconds to cache this response.
        Subsequent requests that require this authorizer will use a
        cached response if available.  The default is 300 seconds.

      :param execution_role: An optional IAM role to specify when invoking
        the Lambda function associated with the built-in authorizer.

   .. method:: schedule(expression, name=None)

      Register a scheduled event that's invoked on a regular schedule.
      This will create a lambda function associated with the decorated
      function.  It will also schedule the lambda function to be invoked
      with a scheduled CloudWatch Event.

      See :doc:`topics/events` for more information.

      .. code-block:: python

          @app.schedule('cron(15 10 ? * 6L 2002-2005)')
          def cron_handler(event):
              pass

          @app.schedule('rate(5 minutes)')
          def rate_handler(event):
              pass

          @app.schedule(Rate(5, unit=Rate.MINUTES))
          def rate_obj_handler(event):
              pass

          @app.schedule(Cron(15, 10, '?', '*', '6L', '2002-2005'))
          def cron_obj_handler(event):
              pass


      :param expression: The schedule expression to use for the CloudWatch
        event rule.  This value can either be a string value or an
        instance of type ``ScheduleExpression``, which is either a
        :class:`Cron` or :class:`Rate` object.  If a string value is
        provided, it will be provided directly as the ``ScheduleExpression``
        value in the `PutRule <https://docs.aws.amazon.com/AmazonCloudWatchEvents/latest/APIReference/API_PutRule.html#API_PutRule_RequestSyntax>`__ API
        call.

      :param name: The name of the function to use.  This name is combined
        with the chalice app name as well as the stage name to create the
        entire lambda function name.  This parameter is optional.  If it is
        not provided, the name of the python function will be used.

   .. method:: lambda_function(name=None)

      Create a pure lambda function that's not connected to anything.

      See :doc:`topics/purelambda` for more information.

      :param name: The name of the function to use.  This name is combined
        with the chalice app name as well as the stage name to create the
        entire lambda function name.  This parameter is optional.  If it is
        not provided, the name of the python function will be used.


Request
=======

.. class:: Request

  A class that represents the current request.  This is mapped to
  the ``app.current_request`` object.

  .. code-block:: python

      @app.route('/objects/{key}', methods=['GET', 'PUT'])
      def myobject(key):
          request = app.current_request
          if request.method == 'PUT':
              # handle PUT request
              pass
          elif request.method == 'GET':
              # handle GET request
              pass


  .. attribute:: query_params

     A dict of the query params for the request.

  .. attribute:: headers

     A dict of the request headers.

  .. attribute:: uri_params

     A dict of the captured URI params.

  .. attribute:: method

     The HTTP method as a string.

  .. attribute:: json_body

     The parsed JSON body (``json.loads(raw_body)``).  This value will only
     be non-None if the Content-Type header is ``application/json``, which
     is the default content type value in chalice.

  .. attribute:: raw_body

     The raw HTTP body as bytes.  This is useful if you need to
     calculate a checksum of the HTTP body.

  .. attribute:: context

     A dict of additional context information.

  .. attribute:: stage_vars

     A dict of configuration for the API Gateway stage.

  .. method:: to_dict()

     Convert the :class:`Request` object to a dictionary.  This is useful
     for debugging purposes.  This dictionary is guaranteed to be JSON
     serializable so you can return this value from a chalice view.


Response
========

.. class:: Response(body, headers=None, status_code=200)

  A class that represents the response for the view function.  You
  can optionally return an instance of this class from a view function if you
  want complete control over the returned HTTP response.

  .. code-block:: python

      from chalice import Chalice, Response

      app = Chalice(app_name='custom-response')


      @app.route('/')
      def index():
          return Response(body='hello world!',
                          status_code=200,
                          headers={'Content-Type': 'text/plain'})


  .. versionadded:: 0.6.0

  .. attribute:: body

     The HTTP response body to send back.  This value must be a string.

  .. attribute:: headers

     An optional dictionary of HTTP headers to send back.  This is a dictionary
     of header name to header value, e.g ``{'Content-Type': 'text/plain'}``

  .. attribute:: status_code

     The integer HTTP status code to send back in the HTTP response.


Authorization
=============

Each of these classes below can be provided using the ``authorizer`` argument
for an ``@app.route(authorizer=...)`` call:


.. code-block:: python

    authorizer = CognitoUserPoolAuthorizer(
        'MyPool', header='Authorization',
        provider_arns=['arn:aws:cognito:...:userpool/name'])

    @app.route('/user-pools', methods=['GET'], authorizer=authorizer)
    def authenticated():
        return {"secure": True}


.. class:: CognitoUserPoolAuthorizer(name, provider_arns, header='Authorization')

  .. versionadded:: 0.8.1

  .. attribute:: name

     The name of the authorizer.

  .. attribute:: provider_arns

     The Cognito User Pool arns to use.

  .. attribute:: header

     The header where the auth token will be specified.

.. class:: IAMAuthorizer()

  .. versionadded:: 0.8.3

.. class:: CustomAuthorizer(name, authorizer_uri, ttl_seconds, header='Authorization')

  .. versionadded:: 0.8.1

  .. attribute:: name

     The name of the authorizer.

  .. attribute:: authorizer_uri

     The URI of the lambda function to use for the custom authorizer.  This
     usually has the form
     ``arn:aws:apigateway:{region}:lambda:path/2015-03-01/functions/{lambda_arn}/invocations``.

  .. attribute:: ttl_seconds

     The number of seconds to cache the returned policy from a custom
     authorizer.

  .. attribute:: header

     The header where the auth token will be specified.


Built-in Authorizers
--------------------

These classes are used when defining built-in authorizers in Chalice.

.. class:: AuthRequest(auth_type, token, method_arn)

   An instance of this class is passed as the first argument
   to an authorizer defined via ``@app.authorizer()``.  You
   generally do not instantiate this class directly.

   .. attribute:: auth_type

      The type of authentication

   .. attribute:: token

      The authorization token.  This is usually the value of the
      ``Authorization`` header.

   .. attribute:: method_arn

      The ARN of the API gateway being authorized.

.. class:: AuthResponse(routes, principal_id, context=None)

   .. attribute:: routes

      A list of authorized routes.  Each element in the list
      can either by a string route such as `"/foo/bar"` or
      an instance of ``AuthRoute``.  If you specify the URL as
      a string, then all supported HTTP methods will be authorized.
      If you want to specify which HTTP methods are allowed, you
      can use ``AuthRoute``.  If you want to specify that all
      routes and HTTP methods are supported you can use the
      wildcard value of ``"*"``: ``AuthResponse(routes=['*'], ...)``

   .. attribute:: principal_id

      The principal id of the user.

   .. attribute:: context

      An optional dictionary of key value pairs.  This dictionary
      will be accessible in the ``app.current_request.context``
      in all subsequent authorized requests for this user.

.. class:: AuthRoute(path, methods)

   This class be used in the ``routes`` attribute of a
   :class:`AuthResponse` instance to get fine grained control
   over which HTTP methods are allowed for a given route.

   .. attribute:: path

      The allowed route specified as a string

   .. attribute:: methods

      A list of allowed HTTP methods.


APIGateway
==========

.. class:: APIGateway()

   This class is used to control
   how API Gateway interprets ``Content-Type`` headers in both requests and
   responses.

   There is a single instance of this class attached to each
   :class:`Chalice` object under the ``api`` attribute.

   .. attribute:: default_binary_types

      The value of ``default_binary_types`` are the ``Content-Types`` that are
      considered binary by default. This value should not be changed, instead
      you should modify the ``binary_types`` list to change the behavior of a
      content type. Its value is: ``application/octet-stream``,
      ``application/x-tar``, ``application/zip``, ``audio/basic``,
      ``audio/ogg``, ``audio/mp4``, ``audio/mpeg``, ``audio/wav``,
      ``audio/webm``, ``image/png``, ``image/jpg``, ``image/jpeg``,
      ``image/gif``, ``video/ogg``, ``video/mpeg``, ``video/webm``.


   .. attribute:: binary_types

      The value of ``binary_types`` controls how API Gateway interprets
      requests and responses as detailed below.

      If an incoming request has a ``Content-Type`` header value that is
      present in the ``binary_types`` list it will be assumed that its body is
      a sequence of raw bytes. You can access these bytes by accessing the
      ``app.current_request.raw_body`` property.

      If an outgoing response from ``Chalice`` has a header ``Content-Type``
      that matches one of the ``binary_types`` its body must be a ``bytes``
      type object. It is important to note that originating request must have
      the ``Accept`` header for the same type as the ``Content-Type`` on the
      response. Otherwise a ``400`` error will be returned.

      This value can be modified to change what types API Gateway treats as
      binary. The easiest way to do this is to simply append new types to
      the list.

      .. code-block:: python

          app.api.binary_types.append('application/my-binary-data')


      Keep in mind that there can only be a total of 25 binary types at a time
      and Chalice by default has a list of 16 types. It is recommended if you
      are going to make extensive use of binary types to reset the list to
      the exact set of content types you will be using. This can easily be
      done by reassigning the whole list.

      .. code-block:: python

          app.api.binary_types = [
              'application/octet-stream',
              'application/my-binary-data',
          ]


      **Implementation Note**: API Gateway and Lambda communicate through a
      JSON event which is encoded using ``UTF-8``. The raw bytes are
      temporarily encoded using base64 when being passed between API Gateway
      and Lambda. In the worst case this encoding can cause the binary body
      to be inflated up to ``4/3`` its original size. Lambda only accepts an
      event up to ``6mb``, which means even if your binary data was not quite
      at that limit, with the base64 encoding it may exceed that limit. This
      will manifest as a ``502`` Bad Gateway error.


CORS
====

.. class:: CORSConfig(allow_origin='*', allow_headers=None, expose_headers=None, max_age=None, allow_credentials=None)

  CORS configuration to attach to a route.

  .. code-block:: python

      from chalice import CORSConfig
      cors_config = CORSConfig(
          allow_origin='https://foo.example.com',
          allow_headers=['X-Special-Header'],
          max_age=600,
          expose_headers=['X-Special-Header'],
          allow_credentials=True
      )

      @app.route('/custom_cors', methods=['GET'], cors=cors_config)
      def supports_custom_cors():
          return {'cors': True}

  .. versionadded:: 0.8.1

  .. attribute:: allow_origin

     The value of the ``Access-Control-Allow-Origin`` to send in the response.
     Keep in mind that even though the ``Access-Control-Allow-Origin`` header
     can be set to a string that is a space separated list of origins, this
     behavior does not work on all clients that implement CORS. You should only
     supply a single origin to the ``CORSConfig`` object. If you need to supply
     multiple origins you will need to define a custom handler for it that
     accepts ``OPTIONS`` requests and matches the ``Origin`` header against a
     whitelist of origins.  If the match is successful then return just their
     ``Origin`` back to them in the ``Access-Control-Allow-Origin`` header.

  .. attribute:: allow_headers

     The list of additional allowed headers.  This list is added to list of
     built in allowed headers: ``Content-Type``, ``X-Amz-Date``,
     ``Authorization``, ``X-Api-Key``, ``X-Amz-Security-Token``.

  .. attribute:: expose_headers

     A list of values to return for the ``Access-Control-Expose-Headers``:

  .. attribute:: max_age

     The value for the ``Access-Control-Max-Age``

  .. attribute:: allow_credentials

     A boolean value that sets the value of
     ``Access-Control-Allow-Credentials``.


Scheduled Events
================

.. versionadded:: 1.0.0b1

.. class:: Rate(value, unit)

  An instance of this class can be used as the ``expression`` value
  in the :meth:`Chalice.schedule` method:

  .. code-block:: python

     @app.schedule(Rate(5, unit=Rate.MINUTES))
     def handler(event):
         pass

  Examples:

  .. code-block:: python

      # Run every minute.
      Rate(1, unit=Rate.MINUTES)

      # Run every 2 hours.
      Rate(2, unit=Rate.HOURS)

  .. attribute:: value

     An integer value that presents the amount of time to wait
     between invocations of the scheduled event.

  .. attribute:: unit

     The unit of the provided ``value`` attribute.  This can be
     either ``Rate.MINUTES``, ``Rate.HOURS``, or ``Rate.DAYS``.

  .. attribute:: MINUTES, HOURS, DAYS

     These values should be used for the ``unit`` attribute.


.. class:: Cron(minutes, hours, day_of_month, month, day_of_week, year)

  An instance of this class can be used as the ``expression`` value
  in the :meth:`Chalice.schedule` method.

  .. code-block:: python

     @app.schedule(Cron(15, 10, '?', '*', '6L', '2002-2005'))
     def handler(event):
         pass

  It provides more capabilities than the :class:`Rate`
  class.  There are a few limits:

  * You can't specify ``day_of_month`` and ``day_of_week`` fields in
    the same Cron expression.  If you specify a value in one of the
    fields, you must use a ``?`` in the other.
  * Cron expressions that lead to rates faster than 1 minute are not
    supported.

  For more information, see the API
  `docs page <https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/ScheduledEvents.html#CronExpressions>`__.

  Examples:

  .. code-block:: python

      # Run at 10:00am (UTC) every day.
      Cron(0, 10, '*', '*', '?', '*')

      # Run at 12:15pm (UTC) every day.
      Cron(15, 12, '*', '*', '?', '*')

      # Run at 06:00pm (UTC) every Monday through Friday.
      Cron(0, 18, '?', '*', 'MON-FRI', '*')

      # Run at 08:00am (UTC) every 1st day of the month.
      Cron(0, 8, 1, '*', '?', '*')

      # Run every 15 minutes.
      Cron('0/15', '*', '*', '*', '?', '*')

      # Run every 10 minutes Monday through Friday.
      Cron('0/10', '*', '?', '*', 'MON-FRI', '*')

      # Run every 5 minutes Monday through Friday between
      # 08:00am and 5:55pm (UTC).
      Cron('0/5', '8-17', '?', '*', 'MON-FRI', '*')


.. class:: CloudWatchEvent()

   This is the input argument for a scheduled event.

   .. code-block:: python

      @app.schedule('rate(1 hour)')
      def every_hour(event: CloudWatchEvent):
          pass

   In the code example above, the ``event`` argument is of
   type ``CloudWatchEvent``, which will have the following
   attributes.

   .. attribute:: version

      By default, this is set to 0 (zero) in all events.

   .. attribute:: account

      The 12-digit number identifying an AWS account.

   .. attribute:: region

      Identifies the AWS region where the event originated.

   .. attribute:: detail

      For scheduled events, this will be an empty dictionary.

   .. attribute:: detail_type

      For scheduled events, this value will be ``"Scheduled Event"``.

   .. attribute:: source

      Identifies the service that sourced the event. All events sourced from
      within AWS will begin with "aws." Customer-generated events can have any
      value here as long as it doesn't begin with "aws." We recommend the use
      of java package-name style reverse domain-name strings.

      For scheduled events, this will be ``aws.events``.

   .. attribute:: time

      The event timestamp, which can be specified by the service originating
      the event. If the event spans a time interval, the service might choose
      to report the start time, so this value can be noticeably before the time
      the event is actually received.

   .. attribute:: event_id

      A unique value is generated for every event. This can be helpful in
      tracing events as they move through rules to targets, and are processed.

   .. attribute:: resources

      This JSON array contains ARNs that identify resources that are involved
      in the event. Inclusion of these ARNs is at the discretion of the
      service.

      For scheduled events, this will include the ARN of the CloudWatch
      rule that triggered this event.

   .. method:: to_dict()

      Return the original event dictionary provided
      from Lambda.  This is useful if you need direct
      access to the lambda event, for example if a
      new key is added to the lambda event that has not
      been mapped as an attribute to the ``CloudWatchEvent``
      object.  Example::

          {'account': '123457940291',
           'detail': {},
           'detail-type': 'Scheduled Event',
           'id': '12345678-b9f1-4667-9c5e-39f98e9a6113',
           'region': 'us-west-2',
           'resources': ['arn:aws:events:us-west-2:123457940291:rule/testevents-dev-every_minute'],
           'source': 'aws.events',
           'time': '2017-06-30T23:28:38Z',
           'version': '0'}
