[
  {
    "path": ".changes/0.1.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Require ``virtualenv`` as a package dependency. (#33)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"CLI\",\n      \"description\": \"Add ``--profile`` option when creating a new project (#28)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Add support for more error codes exceptions (#34)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Improve error validation when routes containing a\\ntrailing ``/`` char (#65)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Validate duplicate route entries (#79)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"policy\",\n      \"description\": \"Ignore lambda expressions in policy analyzer (#74)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Print original error traceback in debug mode (#50)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Add support for authenticate routes (#14)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"policy\",\n      \"description\": \"Add ability to disable IAM role management (#61)\",\n      \"type\": \"feature\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/0.10.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"deployment\",\n      \"description\": \"Fix issue where provided ``iam_role_arn`` was not respected on\\nredeployments of chalice applications and in the CloudFormation template\\ngenerated by ``chalice package`` (#339)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"config\",\n      \"description\": \"Fix ``autogen_policy`` in config being ignored (#367)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Add support for view functions that share the same view url but\\ndiffer by HTTP method (#81)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"deployment\",\n      \"description\": \"Improve deployment error messages for deployment packages that are\\ntoo large (#246, #330, #380)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Add support for built-in authorizers (#356)\",\n      \"type\": \"feature\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/0.10.1.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"deployment\",\n      \"description\": \"Fix deployment issue for projects deployed with versions\\nprior to 0.10.0 (#387)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"policy\",\n      \"description\": \"Fix crash in analyzer when encountering genexprs and listcomps (#263)\",\n      \"type\": \"bugfix\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/0.2.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Add support for input content types besides ``application/json`` (#96)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Allow ``ChaliceViewErrors`` to propagate, so that API Gateway\\ncan properly map HTTP status codes in non debug mode (#113)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"deployment\",\n      \"description\": \"Add windows compatibility (#31)\",\n      \"type\": \"enhancement\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/0.3.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Fix bug with case insensitive headers (#129)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"CORS\",\n      \"description\": \"Add initial support for CORS (#133)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"deployment\",\n      \"description\": \"Only add API gateway permissions if needed (#48)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"policy\",\n      \"description\": \"Fix error when dict comprehension is encountered during policy generation (#131)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"CLI\",\n      \"description\": \"Add ``--version`` and ``--debug`` options to the chalice CLI\",\n      \"type\": \"enhancement\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/0.4.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"deployment\",\n      \"description\": \"Fix issue where role name to arn lookup was failing due to lack of pagination (#139)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Raise errors when unknown kwargs are provided to ``app.route(...)`` (#144)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"config\",\n      \"description\": \"Raise validation error when configuring CORS and an OPTIONS method (#142)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Add support for multi-file applications (#21)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"local\",\n      \"description\": \"Add support for ``chalice local``, which runs a local HTTP server for testing (#22)\",\n      \"type\": \"feature\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/0.5.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"logging\",\n      \"description\": \"Add default application logger (#149)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"local\",\n      \"description\": \"Return 405 when method is not supported when running\\n``chalice local`` (#159)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"SDK\",\n      \"description\": \"Add path params as requestParameters so they can be used\\nin generated SDKs as well as cache keys (#163)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Map cognito user pool claims as part of request context (#165)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"CLI\",\n      \"description\": \"Add ``chalice url`` command to print the deployed URL (#169)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"deployment\",\n      \"description\": \"Bump up retry limit on initial function creation to 30 seconds (#172)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"local\",\n      \"description\": \"Add support for ``DELETE`` and ``PATCH`` in ``chalice local`` (#167)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"CLI\",\n      \"description\": \"Add ``chalice generate-sdk`` command (#178)\",\n      \"type\": \"feature\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/0.5.1.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"local\",\n      \"description\": \"Add support for serializing decimals in ``chalice local`` (#187)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"local\",\n      \"description\": \"Add stdout handler for root logger when using ``chalice local`` (#186)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"local\",\n      \"description\": \"Map query string parameters when using ``chalice local`` (#184)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Support Content-Type with a charset (#180)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"deployment\",\n      \"description\": \"Fix not all resources being retrieved due to pagination (#188)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"deployment\",\n      \"description\": \"Fix issue where root resource was not being correctly retrieved (#205)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"deployment\",\n      \"description\": \"Handle case where local policy does not exist\\n(`29 <https://github.com/awslabs/chalice/issues/29>`__)\",\n      \"type\": \"bugfix\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/0.6.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"summary\": \"Check out the `upgrade notes for 0.6.0\\n<http://chalice.readthedocs.io/en/latest/upgrading.html#v0-6-0>`__\\nfor more detailed information about changes in this release.\\n\\n\",\n  \"changes\": [\n    {\n      \"category\": \"local\",\n      \"description\": \"Add port parameter to local command (#220)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Add support for binary vendored packages (#182, #106, #42)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Add support for customizing the returned HTTP response (#240, #218, #110, #30, #226)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Always inject latest runtime to allow for chalice upgrades (#245)\",\n      \"type\": \"enhancement\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/0.7.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"CLI\",\n      \"description\": \"Add ``chalice package`` command.  This will\\ncreate a SAM template and Lambda deployment package that\\ncan be subsequently deployed by AWS CloudFormation. (#258)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"CLI\",\n      \"description\": \"Add a ``--stage-name`` argument for creating chalice stages.\\nA chalice stage is a completely separate set of AWS resources.\\nAs a result, most configuration values can also be specified\\nper chalice stage. (#264, #270)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"policy\",\n      \"description\": \"Add support for ``iam_role_file``, which allows you to\\nspecify the file location of an IAM policy to use for your app (#272)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"config\",\n      \"description\": \"Add support for setting environment variables in your app (#273)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"CI-CD\",\n      \"description\": \"Add a ``generate-pipeline`` command (#277)\",\n      \"type\": \"feature\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/0.8.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"python\",\n      \"description\": \"Add support for python3! (#296)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Fix swagger generation when using ``api_key_required=True`` (#279)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"CI-CD\",\n      \"description\": \"Fix ``generate-pipeline`` to install requirements file before packaging (#295)\",\n      \"type\": \"bugfix\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/0.8.1.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"deployment\",\n      \"description\": \"Alway overwrite existing API Gateway Rest API on updates (#305)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"CORS\",\n      \"description\": \"Added more granular support for CORS (#311)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"local\",\n      \"description\": \"Fix duplicate content type header in local model (#311)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Fix content type validation when charset is provided (#306)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Add back custom authorizer support (#322)\",\n      \"type\": \"enhancement\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/0.8.2.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"CLI\",\n      \"description\": \"Fix issue where ``--api-gateway-stage`` was being\\nignored  (#325)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"CLI\",\n      \"description\": \"Add ``chalice delete`` command (#40)\",\n      \"type\": \"feature\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/0.9.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Add support for ``IAM`` authorizer (#334)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"config\",\n      \"description\": \"Add support for configuring ``lambda_timeout``, ``lambda_memory_size``,\\nand ``tags`` in your AWS Lambda function (#347)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Fix vendor directory contents not being importable locally (#350)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Add support for binary payloads (#348)\",\n      \"type\": \"feature\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.0.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Change default API Gateway stage name to ``api`` (#431)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"local\",\n      \"description\": \"Add support for ``CORSConfig`` in ``chalice local`` (#436)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"logging\",\n      \"description\": \"Propagate ``DEBUG`` log level when setting ``app.debug`` (#386)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Add support for wildcard routes and HTTP methods in ``AuthResponse`` (#403)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"policy\",\n      \"description\": \"Fix bug when analyzing list comprehensions (#412)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"local\",\n      \"description\": \"Update ``chalice local`` to use HTTP 1.1 (#448)\",\n      \"type\": \"enhancement\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.0.0b1.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"summary\": \"Please read the `upgrade notes for 1.0.0b1\\n<http://chalice.readthedocs.io/en/latest/upgrading.html#v1-0-0b1>`__\\nfor more detailed information about upgrading to this release.\\n\\nNote: to install this beta version of chalice you must specify\\n``pip install 'chalice>=1.0.0b1,<2.0.0'`` or\\nuse the ``--pre`` flag for pip: ``pip install --pre chalice``.\\n\\n\",\n  \"changes\": [\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Fix unicode responses being quoted in python 2.7 (#262)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"event-source\",\n      \"description\": \"Add support for scheduled events (#390)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"event-source\",\n      \"description\": \"Add support for pure lambda functions (#390)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Add support for wheel packaging. (#249)\",\n      \"type\": \"feature\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.0.0b2.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"summary\": \"Please read the `upgrade notes for 1.0.0b2\\n<http://chalice.readthedocs.io/en/latest/upgrading.html#v1-0-0b2>`__\\nfor more detailed information about upgrading to this release.\\n\\nNote: to install this beta version of chalice you must specify\\n``pip install 'chalice>=1.0.0b2,<2.0.0'`` or\\nuse the ``--pre`` flag for pip: ``pip install --pre chalice``.\\n\",\n  \"changes\": [\n    {\n      \"category\": \"local\",\n      \"description\": \"Set env vars from config in ``chalice local`` (#396)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Fix edge case when building packages with optional c extensions (#421)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"policy\",\n      \"description\": \"Remove legacy ``policy.json`` file support. Policy files must\\nuse the stage name, e.g. ``policy-dev.json`` (#430)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"deployment\",\n      \"description\": \"Fix issue where IAM role policies were updated twice on redeploys (#428)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Validate route path is not an empty string (#432)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Change route code to invoke view function with kwargs instead of\\npositional args (#429)\",\n      \"type\": \"enhancement\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.0.1.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Only use alphanumeric characters for event names in SAM template (#450)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"config\",\n      \"description\": \"Print useful error message when config.json is invalid (#458)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Fix api gateway stage being set incorrectly in non-default chalice stage\\n(`#$70 <https://github.com/aws/chalice/issues/470>`__)\",\n      \"type\": \"bugfix\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.0.2.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Fix issue where requestParameters were not being mapped\\ncorrectly resulting in invalid generated javascript SDKs (#498)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Fix issue where ``api_gateway_stage`` was being\\nignored when set in the ``config.json`` file (#495)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Fix bug where ``raw_body`` would raise an exception if no HTTP\\nbody was provided (#503)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"CLI\",\n      \"description\": \"Fix bug where exit codes were not properly being propagated during packaging (#500)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"local\",\n      \"description\": \"Add support for Builtin Authorizers in local mode (#404)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Fix environment variables being passed to subprocess while packaging (#501)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Allow view to require API keys as well as authorization (#473)\",\n      \"type\": \"enhancement\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.0.3.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Fix issue with some packages with `-` or `.` in their distribution name (#555)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Fix issue where chalice local returned a 403 for successful OPTIONS requests (#554)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"local\",\n      \"description\": \"Fix issue with chalice local mode causing http clients to hang on responses\\nwith no body (#525)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"local\",\n      \"description\": \"Add ``--stage`` parameter to ``chalice local`` (#545)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"policy\",\n      \"description\": \"Fix issue with analyzer that followed recursive functions infinitely (#531)\",\n      \"type\": \"bugfix\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.0.4.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Fix issue deploying some packages in Windows with utf-8 characters (#560)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Add support for custom authorizers with ``chalice package`` (#580)\",\n      \"type\": \"feature\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.1.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Default to ``None`` in local mode when no query parameters\\nare provided (#593)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"local\",\n      \"description\": \"Add support for binding a custom address for local dev server (#596)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Fix local mode handling of routes with trailing slashes (#582)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"config\",\n      \"description\": \"Scale ``lambda_timeout`` parameter correctly in local mode (#579)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"CI-CD\",\n      \"description\": \"Add ``--codebuild-image`` to the ``generate-pipeline`` command (#609)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"CI-CD\",\n      \"description\": \"Add ``--source`` and ``--buildspec-file`` to the\\n``generate-pipeline`` command (#609)\",\n      \"type\": \"feature\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.1.1.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"CLI\",\n      \"description\": \"Add ``--connection-timeout`` to the ``deploy`` command (#344)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"policy\",\n      \"description\": \"Fix IAM role creation issue (#565)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"local\",\n      \"description\": \"Fix `chalice local` handling of browser requests (#565)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"policy\",\n      \"description\": \"Support async/await syntax in automatic policy generation (#565)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Support additional PyPi package formats (.tar.bz2) (#720)\",\n      \"type\": \"enhancement\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.10.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"websocket\",\n      \"description\": \"Add experimental support for websockets (#1017)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"API Gateway Endpoint Type Configuration (#1160)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"API Gateway Resource Policy Configuration (#1160)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Add --merge-template option to package command (#1195)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Add support for packaging via terraform (#1129)\",\n      \"type\": \"feature\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.11.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"config\",\n      \"description\": \"Add support for stage independent lambda configuration (#1162)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"event-source\",\n      \"description\": \"Add support for subscribing to CloudWatch Events (#1126)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"event-source\",\n      \"description\": \"Add a ``description`` argument to CloudWatch schedule events (#1155)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Fix deployment of API Gateway resource policies (#1220)\",\n      \"type\": \"bugfix\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.11.1.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"blueprint\",\n      \"description\": \"Fix mouting blueprints with root routes (#1230)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Add support for multi-value headers responses (#1205)\",\n      \"type\": \"feature\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.12.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"CLI\",\n      \"description\": \"Add ``generate-models`` command (#1245)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"websocket\",\n      \"description\": \"Add ``close`` and ``info`` commands to websocket api (#1259)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"dependencies\",\n      \"description\": \"Bump upper bound on PIP to ``<19.4`` (#1273, #1272)\",\n      \"type\": \"enhancement\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.13.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"logs\",\n      \"description\": \"Fix error for ``chalice logs`` when a Lambda function\\nhas not been invoked (#1252)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"CORS\",\n      \"description\": \"Add global CORS configuration (#70)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Fix packaging simplejson (#1304)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"python\",\n      \"description\": \"Add support for Python 3.8 (#1315)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"authorizer\",\n      \"description\": \"Add support for invocation role in custom authorizer (#1303)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Fix packaging on case-sensitive filesystems (#1356)\",\n      \"type\": \"bugfix\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.13.1.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"local\",\n      \"description\": \"Add support for multiValueHeaders in local mode (#1381).\"\n    },\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"local\",\n      \"description\": \"Make ``current_request`` thread safe in local mode (#759)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"local\",\n      \"description\": \"Add support for cognito in local mode (#1377).\"\n    },\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"packaging\",\n      \"description\": \"Fix terraform generation when injecting custom domains (#1237)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"packaging\",\n      \"description\": \"Ensure repeatable zip file generation (#1114).\"\n    },\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"CORS\",\n      \"description\": \"Fix CORS request when returning compressed binary types (#1336)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.14.0.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"packaging\",\n      \"description\": \"Fix pandas packaging regression (#1398)\"\n    },\n    {\n      \"type\": \"feature\",\n      \"category\": \"CLI\",\n      \"description\": \"Add ``dev plan/appgraph`` commands (#1396)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"SQS\",\n      \"description\": \"Validate queue name is used and not queue URL or ARN (#1388)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.14.1.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"pip\",\n      \"description\": \"Update pip version range to 20.1.\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.15.0.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"feature\",\n      \"category\": \"blueprints\",\n      \"description\": \"Mark blueprints as an accepted API (#1250)\"\n    },\n    {\n      \"type\": \"feature\",\n      \"category\": \"package\",\n      \"description\": \"Add ability to generate and merge yaml CloudFormation templates (#1425)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"terraform\",\n      \"description\": \"Allow generated terraform template to be used as a terraform module (#1300)\"\n    },\n    {\n      \"type\": \"feature\",\n      \"category\": \"logs\",\n      \"description\": \"Add support for tailing logs (#4).\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.15.1.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"packaging\",\n      \"description\": \"Fix setup.py dependencies where the wheel package was not being installed (#1435)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.16.0.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"local\",\n      \"description\": \"Avoid error from cognito client credentials in local authorizer (#1447)\"\n    },\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"package\",\n      \"description\": \"Traverse symlinks to directories when packaging the vendor directory (#583).\"\n    },\n    {\n      \"type\": \"feature\",\n      \"category\": \"DomainName\",\n      \"description\": \"Add support for custom domain names to REST/WebSocket APIs (#1194)\"\n    },\n    {\n      \"type\": \"feature\",\n      \"category\": \"auth\",\n      \"description\": \"Add support for oauth scopes on routes (#1444).\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.17.0.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"feature\",\n      \"category\": \"Testing\",\n      \"description\": \"Add Chalice test client (#1468)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"regions\",\n      \"description\": \"Add support for non `aws` partitions including aws-cn and aws-us-gov (#792).\"\n    },\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"dependencies\",\n      \"description\": \"Fix error when using old versions of click by requiring >=7\"\n    },\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"local\",\n      \"description\": \"Fix local mode builtin authorizer not stripping query string from URL (#1470)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.18.0.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"feature\",\n      \"category\": \"Packaging\",\n      \"description\": \"Add support for automatic layer creation (#1485, #1001)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.18.1.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Packaging\",\n      \"description\": \"Add fallback to retrieve name/version from sdist (#1486)\"\n    },\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Analyzer\",\n      \"description\": \"Handle symbols with multiple (shadowed) namespaces (#1494)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.19.0.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"feature\",\n      \"category\": \"Pipeline\",\n      \"description\": \"Add a new v2 template for the deployment pipeline CloudFormation template (#1506)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.2.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"summary\": \"This release features a rewrite of the core deployment\\ncode used in Chalice.  This is a backwards compatible change\\nfor users, but you may see changes to the autogenerated\\nfiles Chalice creates.\\nPlease read the `upgrade notes for 1.2.0\\n<http://chalice.readthedocs.io/en/latest/upgrading.html#v1-2-0>`__\\nfor more detailed information about upgrading to this release.\\n\\n\",\n  \"changes\": [\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Print out full stack trace when an error occurs (#711)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Add ``image/jpeg`` as a default binary content type (#707)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"event-source\",\n      \"description\": \"Add support for AWS Lambda only projects (#162, #640)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"policy\",\n      \"description\": \"Fix inconsistent IAM role generation with pure lambdas (#685)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"deployment\",\n      \"description\": \"Rewrite Chalice deployer to more easily support additional AWS resources (#604)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Update the ``chalice package`` command to support\\npure lambda functions and scheduled events. (#772)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Fix packager edge case normalizing sdist names (#778)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Fix SQLAlchemy packaging (#778)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Fix packaging abi3, wheels this fixes cryptography 2.2.x packaging (#764)\",\n      \"type\": \"bugfix\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.2.1.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Add CORS headers to error response (#715)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"local\",\n      \"description\": \"Fix parsing empty query strings in local mode (#767)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Fix regression in ``chalice package`` when using role arns (#793)\",\n      \"type\": \"bugfix\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.2.2.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Fix package command not correctly setting environment variables (#795)\",\n      \"type\": \"bugfix\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.2.3.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"dependency\",\n      \"description\": \"Add support for pip 10 (#808)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"policy\",\n      \"description\": \"Update ``policies.json`` file (#817)\",\n      \"type\": \"enhancement\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.20.0.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Blueprints\",\n      \"description\": \"Add `current_app` property to Blueprints (#1094)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"CLI\",\n      \"description\": \"Set `AWS_CHALICE_CLI_MODE` env var whenever a Chalice CLI command is run (#1200)\"\n    },\n    {\n      \"type\": \"feature\",\n      \"category\": \"Middleware\",\n      \"description\": \"Add support for middleware (#1509)\"\n    },\n    {\n      \"type\": \"feature\",\n      \"category\": \"X-Ray\",\n      \"description\": \"Add support for AWS X-Ray (#464)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.20.1.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Blueprints\",\n      \"description\": \"Preserve docstring in blueprints (#1525)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Binary\",\n      \"description\": \"Support returning native python types when using `*/*` for binary types (#1501)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.21.0.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Blueprints\",\n      \"description\": \"Fix regression when invoking Lambda functions from blueprints (#1535)\"\n    },\n    {\n      \"type\": \"feature\",\n      \"category\": \"Events\",\n      \"description\": \"Add support for Kinesis and DynamoDB event handlers (#987)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.21.1.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Websockets\",\n      \"description\": \"Fix custom domain name configuration for websockets (#1531)\"\n    },\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Local\",\n      \"description\": \"Add support for multiple actions in builtin auth in local mode (#1527)\"\n    },\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Websocket\",\n      \"description\": \"Fix websocket client configuration when using a custom domain (#1503)\"\n    },\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Local\",\n      \"description\": \"Fix CORs handling in local mode (#761)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.21.2.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Terraform\",\n      \"description\": \"Fix issue with wildcard partition names in s3 event handlers (#1508)\"\n    },\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Auth\",\n      \"description\": \"Fix special case processing for root URL auth (#1271)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Middleware\",\n      \"description\": \"Add support for HTTP middleware catching exceptions (#1541)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.21.3.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Test\",\n      \"description\": \"Add test client methods for generating sample kinesis events\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Config\",\n      \"description\": \"Validate env var values are strings (#1543)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.21.4.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Local\",\n      \"description\": \"Allow custom Chalice class in local mode (#1502)\"\n    },\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Layers\",\n      \"description\": \"Ensure single reference to managed layer (#1563)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.21.5.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Config\",\n      \"description\": \"Fix config validation for env vars on py27 (#1573)\"\n    },\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Pip\",\n      \"description\": \"Bump pip version contraint (#1590)\"\n    },\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"REST\",\n      \"description\": \"Add Allow header with list of allowed methods when returning 405 error (#1583)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.21.6.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Packaging\",\n      \"description\": \"Increase upper bound for AWS provider in Terraform to 3.x (#1596)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Packaging\",\n      \"description\": \"Add support for manylinux2014 wheels (#1551)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.21.7.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Terraform\",\n      \"description\": \"Map custom domain outputs in Terraform packaging (#1601)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.21.8.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Authorizers\",\n      \"description\": \"Add support for custom headers in built-in authorizers (#1613)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.21.9.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Dependencies\",\n      \"description\": \"Bump attr version constraint (#1620)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.22.0.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"feature\",\n      \"category\": \"CDK\",\n      \"description\": \"Add built-in support for the AWS CDK (#1622)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.22.1.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Pip\",\n      \"description\": \"Bump pip version range to latest version 21.x (#1630)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"IAM\",\n      \"description\": \"Improve client call collection when generation policies (#692)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.22.2.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Blueprint\",\n      \"description\": \"Add log property to blueprint\"\n    },\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Pipeline\",\n      \"description\": \"Fix build command in pipeline generation (#1653)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Dependencies\",\n      \"description\": \"Change enum-compat dependency to enum34 with version restrictions (#1667)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.22.3.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Terraform\",\n      \"description\": \"Bump Terraform version to include 0.14\"\n    },\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Typing\",\n      \"description\": \"Fix type definitions in app.pyi (#1676)\"\n    },\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Terraform\",\n      \"description\": \"Use references instead of function names in Terraform packaging (#1558)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.22.4.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Types\",\n      \"description\": \"Add missing types to app.pyi stub file (#1701)\"\n    },\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Custom Domain\",\n      \"description\": \"Fix custom domain generation when using the CDK (#1640)\"\n    },\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Packaging\",\n      \"description\": \"Special cases pyrsistent packaging (#1696)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.23.0.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Deploy\",\n      \"description\": \"Wait for function state to be active when deploying\"\n    },\n    {\n      \"type\": \"feature\",\n      \"category\": \"SQS\",\n      \"description\": \"Add queue_arn parameter to enable CDK integration with SQS event handler (#1681)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.24.0.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"feature\",\n      \"category\": \"Python2.7\",\n      \"description\": \"Remove support for Python 2.7 (#1766)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Terraform\",\n      \"description\": \"Update Terraform packaging to support version 1.0 (#1757)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Typing\",\n      \"description\": \"Add missing WebsocketEvent type information (#1746)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"S3 events\",\n      \"description\": \"Add source account to Lambda permissions when configuring S3 events (#1635)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Packaging\",\n      \"description\": \"Add support for Terraform v0.15 (#1725)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.24.1.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"GovCloud\",\n      \"description\": \"Fix partition error when updating API Gateway in GovCloud region (#1770)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.24.2.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Dependencies\",\n      \"description\": \"Bump attrs dependency to latest version (#1786)\"\n    },\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Auth\",\n      \"description\": \"Fix ARN parsing when generating a builtin AuthResponse (#1775)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"CLI\",\n      \"description\": \"Upgrade Click dependency to support v8.0.0 (#1729)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.25.0.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"feature\",\n      \"category\": \"Python\",\n      \"description\": \"Add support for Python 3.9 (#1787)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.26.0.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"feature\",\n      \"category\": \"Websockets\",\n      \"description\": \"Add support for setting the Websocket protocol from the connect handler (#1768)\"\n    },\n    {\n      \"type\": \"feature\",\n      \"category\": \"SQS\",\n      \"description\": \"Added MaximumBatchingWindowInSeconds to SQS event handler (#1778)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.26.1.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Dependencies\",\n      \"description\": \"Bump pip dependency to latest released version (#1817)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Tests\",\n      \"description\": \"Don't include tests package in .whl file (#1814)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.26.2.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Dependencies\",\n      \"description\": \"Update pyyaml to 6.x (#1830)\"\n    },\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Websocket\",\n      \"description\": \"Correctly configure websocket endpoint in the aws-cn partition (#1820)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.26.3.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Errors\",\n      \"description\": \"Remove redundant error code in error message string (#1339)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"VPC\",\n      \"description\": \"Associate VPC endpoint with Rest API (#1449)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.26.4.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Terraform\",\n      \"description\": \"Use updated keywords for providing provider version constraints (#1717)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.26.5.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Terraform\",\n      \"description\": \"Remove template provider in favor of locals (#1869)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Terraform\",\n      \"description\": \"Bump Terraform version to suppose 1.1.x (#1868)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.26.6.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"pip\",\n      \"description\": \"Fix RuntimeError with pip v22.x (#1887)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.27.0.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Local\",\n      \"description\": \"Set a default timeout when creating the local LambdaContext instance (#1896)\"\n    },\n    {\n      \"type\": \"feature\",\n      \"category\": \"CDK\",\n      \"description\": \"Add support for CDK v2 (#1742)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.27.1.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Pip\",\n      \"description\": \"Bump pip version range to latest version <22.2 (#1924)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Websockets\",\n      \"description\": \"Add support for WebSockets API Terraform packaging (#1670)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.27.2.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Terraform\",\n      \"description\": \"Update aws provider constraint to allow versions 4.x (#1951)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"event-source\",\n      \"description\": \"Add attribute for message attributes in SNSEvent and generated test events (#1934)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.27.3.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Versioning\",\n      \"description\": \"Fix version string updates used in the release process (#1971)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.28.0.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Terraform\",\n      \"description\": \"Update required terraform version to support 1.3 (#2014)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Pip\",\n      \"description\": \"Bump pip version range to latest version <22.3 (#2016)\"\n    },\n    {\n      \"type\": \"feature\",\n      \"category\": \"Config\",\n      \"description\": \"Add support for `log_retention_in_days` (#943)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.29.0.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"feature\",\n      \"category\": \"Python\",\n      \"description\": \"Add support for Python 3.10 (#2037)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Pip\",\n      \"description\": \"Bump pip version range to latest version <23.2 (#2034)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.3.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"config\",\n      \"description\": \"Add support for Lambdas in a VPC (#413, #837, #673)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Add support for packaging local directories (#653)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"local\",\n      \"description\": \"Add support for automatically reloading the local\\ndev server when files are modified (#316, #846, #706)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"logging\",\n      \"description\": \"Add support for viewing cloudwatch logs of all\\nlambda functions (#841, #849)\",\n      \"type\": \"enhancement\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.30.0.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"feature\",\n      \"category\": \"Python\",\n      \"description\": \"Add support for Python 3.11 (#2053)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Pip\",\n      \"description\": \"Update version dependency on pip (#2080)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.31.0.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"feature\",\n      \"category\": \"Python\",\n      \"description\": \"Add support for Python 3.12 (#2086)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Python\",\n      \"description\": \"Drop support for Python 3.7 (#2095)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.31.1.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"pip\",\n      \"description\": \"Update pip version to allow 24.0 (#2092)\"\n    },\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"tar\",\n      \"description\": \"Validate tar extraction does not escape destination dir (#1990)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.31.2.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"SQS\",\n      \"description\": \"Add configuration option for MaximumConcurrency for SQS event source (#2104)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.31.3.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Pip\",\n      \"description\": \"Update pip to the latest version (<24.4)\"\n    },\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"CLI\",\n      \"description\": \"Remove distutils warning when packaging/deploying apps (#2123)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.31.4.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"type\": \"enhancement\",\n      \"category\": \"Pip\",\n      \"description\": \"Update pip to the latest version (<25.1)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.32.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"type\": \"feature\",\n      \"category\": \"Python\",\n      \"description\": \"Add support for Python 3.13 (#2137)\"\n    },\n    {\n      \"type\": \"feature\",\n      \"category\": \"Python\",\n      \"description\": \"Drop support for Python 3.8 (#2138)\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.4.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"CI-CD\",\n      \"description\": \"Add support for generating python 3.6 pipelines (#858)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"event-source\",\n      \"description\": \"Add support for connecting lambda functions to S3 events (#855)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"event-source\",\n      \"description\": \"Add support for connecting lambda functions to SNS message (#488)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"local\",\n      \"description\": \"Make ``watchdog`` an optional dependency and add a built in\\n``stat()`` based file poller (#867)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"event-source\",\n      \"description\": \"Add support for connecting lambda functions to an SQS queue (#884)\",\n      \"type\": \"feature\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.5.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"policy\",\n      \"description\": \"Add support for S3 upload_file/download_file in\\npolicy generator (#889)\",\n      \"type\": \"feature\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.6.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"CLI\",\n      \"description\": \"Add ``chalice invoke`` command (#900)\",\n      \"type\": \"feature\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.6.1.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"local\",\n      \"description\": \"Fix local mode issue with unicode responses and Content-Length (#910)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"dev\",\n      \"description\": \"Fix issue with ``requirements-dev.txt`` not setting up a working\\ndev environment (#920)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"dependencies\",\n      \"description\": \"Add support for pip 18 (#910)\",\n      \"type\": \"enhancement\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.6.2.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"dependencies\",\n      \"description\": \"Add support for pip 18.2 (#991)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"logging\",\n      \"description\": \"Add more detailed debug logs to the packager. (#934)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"python\",\n      \"description\": \"Add support for python3.7 (#992)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Support bytes for the application/json binary type (#988)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Use more compact JSON representation by default for dicts (#958)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"logging\",\n      \"description\": \"Log internal exceptions as errors (#254)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Generate swagger documentation from docstrings (#574)\",\n      \"type\": \"feature\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.7.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Fix packaging multiple local directories as dependencies (#1047)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"event-source\",\n      \"description\": \"Add support for passing SNS ARNs to ``on_sns_message`` (#1048)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"blueprint\",\n      \"description\": \"Add support for Blueprints (#1023)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"config\",\n      \"description\": \"Add support for opting-in to experimental features (#1053)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"event-source\",\n      \"description\": \"Provide Lambda context in event object (#856)\",\n      \"type\": \"feature\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.8.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Fall back to pure python version of yaml parser\\nwhen unable to compile C bindings for PyYAML (#1074)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Add support for Lambda layers. (#1001)\",\n      \"type\": \"feature\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.9.0.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"dependencies\",\n      \"description\": \"Update PIP to support up to 19.1.x (#1104)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Fix handling of more complex Accept headers for binary\\ncontent types (#1078)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Raise TypeError when trying to serialize an unserializable\\ntype (#1100)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"policy\",\n      \"description\": \"Update ``policies.json`` file (#1110)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Support repeating values in the query string (#1131)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Add layer support to chalice package (#1130)\",\n      \"type\": \"feature\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Fix bug with route ``name`` kwarg raising a ``TypeError`` (#1112)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"logging\",\n      \"description\": \"Change exceptions to always be logged at the ERROR level (#969)\",\n      \"type\": \"enhancement\"\n    },\n    {\n      \"category\": \"CLI\",\n      \"description\": \"Fix bug handling exceptions during ``chalice invoke`` on\\nPython 3.7 (#1139)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Add support for API Gateway compression (#672)\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"packaging\",\n      \"description\": \"Add support for both relative and absolute paths for\\n``--package-dir`` (#940)\",\n      \"type\": \"enhancement\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/1.9.1.json",
    "content": "{\n  \"schema-version\": \"0.2\",\n  \"changes\": [\n    {\n      \"category\": \"rest-api\",\n      \"description\": \"Make MultiDict mutable (#1158)\",\n      \"type\": \"enhancement\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/templates/changelog",
    "content": "# CHANGELOG\n\n{% for release, changes in releases %}\n## {{ release }}\n{% if changes.summary %}\n{{ changes.summary -}}\n{% endif %}\n{% for change in changes.changes %}\n* {{ change.type }}:{{ change.category }}:{{ change.description -}}\n{% endfor %}\n{% endfor %}\n"
  },
  {
    "path": ".coveragerc",
    "content": "[run]\nbranch = True\n[report]\nexclude_lines =\n    raise NotImplementedError.*\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "*Issue #, if available:*\n\n*Description of changes:*\n\n\nBy submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n- package-ecosystem: pip\n  directory: \"/.\"\n  schedule:\n    interval: daily\n    time: \"13:00\"\n  open-pull-requests-limit: 10\n  allow:\n  - dependency-name: pip\n  - dependency-name: click\n"
  },
  {
    "path": ".github/workflows/run-tests.yml",
    "content": "name: Run PR Checks\non:\n  push:\n    branches:\n      - master\n      - \"feature/**\"\n  pull_request:\n    branches:\n      - master\n      - \"feature/**\"\njobs:\n  prcheck:\n    runs-on: ${{ matrix.os }}\n    env:\n      HYPOTHESIS_PROFILE: ci\n      CHALICE_TEST_EXTENDED_PACKAGING: true\n    strategy:\n      matrix:\n        os: [ubuntu-latest, macos-latest]\n        python-version: [3.9, '3.10', 3.11, 3.12, 3.13]\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions/setup-python@v2\n        name: Set up Python ${{ matrix.python-version }}\n        with:\n          python-version: ${{ matrix.python-version }}\n      - name: Install dependencies\n        run: |\n          make install-dev-deps\n      - name: Run PRCheck\n        run: make prcheck\n  cdktests:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        python-version: [3.9, '3.10', 3.11, 3.12, 3.13]\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions/setup-node@v2\n        with:\n          node-version: '14'\n      - uses: actions/setup-python@v2\n        name: Set up Python ${{ matrix.python-version }}\n        with:\n          python-version: ${{ matrix.python-version }}\n      - name: Install CDK\n        run: npm install -g aws-cdk\n      - name: Install dependencies\n        run: |\n          pip install -r requirements-test.txt --upgrade --upgrade-strategy eager -e .[cdkv2]\n      - name: Run CDK tests\n        run: python -m pytest tests/functional/cdk\n#  Chalice works on windows, but there's some differences between\n#  the GitHub actions windows environment and our windows dev\n#  laptops that are causing certain tests to fail.  Once these\n#  are fixed we can also test on windows but for now we have to\n#  disable these.\n#  tests-windows:\n#    runs-on: windows-latest\n#    strategy:\n#      matrix:\n#        # In windows where you have to explicitly install\n#        # python, it's unlikely users are going to install\n#        # python 2.7 which is no longer supported so we're\n#        # only testing python3 on windows.\n#        python-version: [3.6, 3.7, 3.8, 3.9]\n#    steps:\n#      - uses: actions/checkout@v2\n#      - uses: actions/setup-python@v2\n#        name: Set up Python ${{ matrix.python-version }}\n#        with:\n#          python-version: ${{ matrix.python-version }}\n#      - name: Install dependencies\n#        run: |\n#          pip install -r requirements-dev.txt\n#          pip install -e .\n#      - name: Run PRCheck\n#        run: python -m pytest tests/unit tests/functional tests/integration\n"
  },
  {
    "path": ".github/workflows/stale-issue.yml",
    "content": "name: \"Close stale issues\"\n\non:\n  schedule:\n    - cron: \"*/60 * * * *\"\n\njobs:\n  cleanup:\n    runs-on: ubuntu-latest\n    name: Stale issue job\n    steps:\n    - uses: aws-actions/stale-issue-cleanup@v4\n      with:\n        issue-types: issues\n        ancient-issue-message: \"\"\n        stale-issue-message: >\n          Greetings! It looks like this issue hasn’t been active in longer than\n          five days. We encourage you to check if this is still an issue in the latest\n          release. In the absence of more information, we will be closing this issue\n          soon. If you find that this is still a problem, please feel free to provide a\n          comment or upvote with a reaction on the initial post to prevent automatic\n          closure. If the issue is already closed, please feel free to open a new one.\n\n        # These labels are required\n        stale-issue-label: closing-soon\n        exempt-issue-labels: bug, feature-request\n        response-requested-label: response-requested\n\n        # Don't set closed-for-staleness label to skip closing very old issues\n        # regardless of label\n        closed-for-staleness-label: closed-for-staleness\n\n        # Issue timing\n        days-before-stale: 10\n        days-before-close: 4\n        days-before-ancient: 2190\n\n        # If you don't want to mark a issue as being ancient based on a\n        # threshold of \"upvotes\", you can set this here. An \"upvote\" is\n        # the total number of +1, heart, hooray, and rocket reactions\n        # on an issue.\n        minimum-upvotes-to-exempt: 2\n\n        repo-token: ${{ secrets.GITHUB_TOKEN }}\n        loglevel: DEBUG\n        # Set dry-run to true to not perform label or close actions.\n        dry-run: false\n"
  },
  {
    "path": ".gitignore",
    "content": "sample/\n.cache\nvenv\ndocs/build/\n.idea\n__pycache__/\n.coverage\nchalice.egg-info/\n.hypothesis/\n.pytest_cache/\n.mypy_cache/\n*.pyc\n.vscode\n"
  },
  {
    "path": ".pylintrc",
    "content": "[MASTER]\n\n# Specify a configuration file.\n#rcfile=\n\n# Python code to execute, usually for sys.path manipulation such as\n# pygtk.require().\ninit-hook=\"import sys, os; sys.path.append(os.path.join('tests', 'plugins'))\"\n\n# Add files or directories to the blacklist. They should be base names, not\n# paths.\nignore=compat.py,regions.py\n\n# Pickle collected data for later comparisons.\npersistent=yes\n\n# List of plugins (as comma separated values of python modules names) to load,\n# usually to register additional checkers.\nload-plugins=codelinter,testlinter\n\n# Use multiple processes to speed up Pylint.\njobs=1\n\n# Allow loading of arbitrary C extensions. Extensions are imported into the\n# active Python interpreter and may run arbitrary code.\nunsafe-load-any-extension=no\n\n# A comma-separated list of package or module names from where C extensions may\n# be loaded. Extensions are loading into the active Python interpreter and may\n# run arbitrary code\nextension-pkg-whitelist=\n\n\n[MESSAGES CONTROL]\n\n# Only show warnings with the listed confidence levels. Leave empty to show\n# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED\nconfidence=\n\n# Enable the message, report, category or checker with the given id(s). You can\n# either give multiple identifier separated by comma (,) or put this option\n# multiple time. See also the \"--disable\" option for examples.\n#enable=\n\n# Disable the message, report, category or checker with the given id(s). You\n# can either give multiple identifiers separated by comma (,) or put this\n# option multiple times (only on the command line, not in the configuration\n# file where it should appear only once).You can also use \"--disable=all\" to\n# disable everything first and then reenable specific checks. For example, if\n# you want to run only the similarities checker, you can use \"--disable=all\n# --enable=similarities\". If you want to run only the classes checker, but have\n# no Warning level messages displayed, use\"--disable=all --enable=classes\n# --disable=W\"\ndisable=W0613,I0021,I0020,C0111,R0902,R0903,W0231,W0611,R0913,W0703,I0011,R0904,R0205,R1705,R1710,C0415,R1725,W0707,R1732,W1512,C0209,W1514,C0412,C0411,R1735,R0917\n\n\n[REPORTS]\n\n# Set the output format. Available formats are text, parseable, colorized, msvs\n# (visual studio) and html. You can also give a reporter class, eg\n# mypackage.mymodule.MyReporterClass.\noutput-format=text\n\n# Tells whether to display a full report or only the messages\nreports=no\n\n# Python expression which should return a note less than 10 (10 is the highest\n# note). You have access to the variables errors warning, statement which\n# respectively contain the number of errors / warnings messages and the total\n# number of statements analyzed. This is used by the global evaluation report\n# (RP0004).\nevaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)\n\n# Template used to display messages. This is a python new-style format string\n# used to format the message information. See doc for all details\n#msg-template=\n\n\n[BASIC]\n\n# Good variable names which should always be accepted, separated by a comma\ngood-names=e,i,j,k,n,ex,Run,_\n\n# Bad variable names which should always be refused, separated by a comma\nbad-names=foo,bar,baz,toto,tutu,tata\n\n# Colon-delimited sets of names that determine each other's naming style when\n# the name regexes allow several styles.\nname-group=\n\n# Include a hint for the correct naming format with invalid-name\ninclude-naming-hint=no\n\n# Regular expression matching correct function names\nfunction-rgx=[a-z_][a-z0-9_]{2,50}$\n\n# Regular expression matching correct variable names\nvariable-rgx=[a-z_][a-z0-9_]{0,50}$\n\n# Regular expression matching correct constant names\nconst-rgx=(([a-zA-Z_][a-zA-Z0-9_]*)|(__.*__))$\n\n# Regular expression matching correct attribute names\nattr-rgx=[a-z_][a-z0-9_]{1,50}$\n\n# Regular expression matching correct argument names\nargument-rgx=[a-z_][a-z0-9_]{0,50}$\n\n# Regular expression matching correct class attribute names\nclass-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,40}|(__.*__))$\n\n# Regular expression matching correct inline iteration names\ninlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$\n\n# Regular expression matching correct class names\nclass-rgx=[A-Z_][a-zA-Z0-9]+$\n\n# Regular expression matching correct module names\nmodule-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$\n\n# Regular expression matching correct method names\n# Allow for the ast visitor method names of visit_CamelCase.\n# Allow for do_HTTPMETHOD used in chalice local.\nmethod-rgx=([a-z_][a-z0-9_]{2,50}|visit_[a-zA-Z]+|do_[A-Z]+)$\n\n# Regular expression which should only match function or class names that do\n# not require a docstring.\nno-docstring-rgx=.*\n\n# Minimum line length for functions/classes that require docstrings, shorter\n# ones are exempt.\ndocstring-min-length=-1\n\ntypevar-rgx=(([a-zA-Z_][a-zA-Z0-9_]*)|(__.*__))$\ntypealias-rgx=(([a-zA-Z_][a-zA-Z0-9_]*)|(__.*__))$\n\n[FORMAT]\n\n# Maximum number of characters on a single line.\nmax-line-length=80\n\n# Regexp for a line that is allowed to be longer than the limit.\nignore-long-lines=^\\s*(# )?<?https?://\\S+>?$\n\n# Allow the body of an if to be on the same line as the test if there is no\n# else.\nsingle-line-if-stmt=no\n\n# Maximum number of lines in a module\nmax-module-lines=1000\n\n# String used as indentation unit. This is usually \" \" (4 spaces) or \"\\t\" (1\n# tab).\nindent-string='    '\n\n# Number of spaces of indent required inside a hanging or continued line.\nindent-after-paren=4\n\n# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.\nexpected-line-ending-format=\n\n\n[LOGGING]\n\n# Logging modules to check that the string format arguments are in logging\n# function parameter format\nlogging-modules=logging\n\n\n[MISCELLANEOUS]\n\n# List of note tags to take in consideration, separated by a comma.\nnotes=FIXME,XXX\n\n\n[SIMILARITIES]\n\n# Minimum lines number of a similarity.\nmin-similarity-lines=5\n\n# Ignore comments when computing similarities.\nignore-comments=no\n\n# Ignore docstrings when computing similarities.\nignore-docstrings=no\n\n# Ignore imports when computing similarities.\nignore-imports=no\n\n\n[SPELLING]\n\n# Spelling dictionary name. Available dictionaries: none. To make it working\n# install python-enchant package.\nspelling-dict=\n\n# List of comma separated words that should not be checked.\nspelling-ignore-words=\n\n# A path to a file that contains private dictionary; one word per line.\nspelling-private-dict-file=\n\n# Tells whether to store unknown words to indicated private dictionary in\n# --spelling-private-dict-file option instead of raising a message.\nspelling-store-unknown-words=no\n\n\n[TYPECHECK]\n\n# Tells whether missing members accessed in mixin class should be ignored. A\n# mixin class is detected if its name ends with \"mixin\" (case insensitive).\nignore-mixin-members=yes\n\n# List of module names for which member attributes should not be checked\n# (useful for modules/projects where namespaces are manipulated during runtime\n# and thus existing member attributes cannot be deduced by static analysis\nignored-modules=six.moves,aws_cdk\n\n# List of classes names for which member attributes should not be checked\n# (useful for classes with attributes dynamically set).\nignored-classes=SQLObject\n\n# List of members which are set dynamically and missed by pylint inference\n# system, and so shouldn't trigger E0201 when accessed. Python regular\n# expressions are accepted.\ngenerated-members=REQUEST,acl_users,aq_parent,objects,DoesNotExist,md5,sha1,sha224,sha256,sha384,sha512\n\n\n[VARIABLES]\n\n# Tells whether we should check for unused import in __init__ files.\ninit-import=no\n\n# A regular expression matching the name of dummy variables (i.e. expectedly\n# not used).\ndummy-variables-rgx=_|dummy|ignore\n\n# List of additional names supposed to be defined in builtins. Remember that\n# you should avoid to define new builtins when possible.\nadditional-builtins=\n\n# List of strings which can identify a callback function by name. A callback\n# name must start or end with one of those strings.\ncallbacks=cb_,_cb\n\n\n[CLASSES]\n\n# List of method names used to declare (i.e. assign) instance attributes.\ndefining-attr-methods=__init__,__new__,setUp\n\n# List of valid names for the first argument in a class method.\nvalid-classmethod-first-arg=cls\n\n# List of valid names for the first argument in a metaclass class method.\nvalid-metaclass-classmethod-first-arg=mcs\n\n# List of member names, which should be excluded from the protected access\n# warning.\nexclude-protected=_asdict,_fields,_replace,_source,_make\n\n\n[DESIGN]\n\n# Maximum number of arguments for function / method\nmax-args=5\n\n# Argument names that match this expression will be ignored. Default to name\n# with leading underscore\nignored-argument-names=_.*\n\n# Maximum number of locals for function / method body\nmax-locals=15\n\n# Maximum number of return / yield for function / method body\nmax-returns=6\n\n# Maximum number of branch for function / method body\nmax-branches=12\n\n# Maximum number of statements in function / method body\nmax-statements=30\n\n# Maximum number of parents for a class (see R0901).\nmax-parents=6\n\n# Maximum number of attributes for a class (see R0902).\nmax-attributes=7\n\n# Minimum number of public methods for a class (see R0903).\nmin-public-methods=0\n\n# Maximum number of public methods for a class (see R0904).\nmax-public-methods=20\n\n\n[IMPORTS]\n\n# Deprecated modules which should not be used, separated by a comma\ndeprecated-modules=regsub,string,TERMIOS,Bastion,rexec,UserDict\n\n# Create a graph of every (i.e. internal and external) dependencies in the\n# given file (report RP0402 must not be disabled)\nimport-graph=\n\n# Create a graph of external dependencies in the given file (report RP0402 must\n# not be disabled)\next-import-graph=\n\n# Create a graph of internal dependencies in the given file (report RP0402 must\n# not be disabled)\nint-import-graph=\n\n# Typing is a third party package in python 2, but built-in to the standard\n# library for Python 3. This causes linting issues when typing get grouped\n# with the third party package imports. Since chalice was originally written\n# just for Python 2, the code standard is to treat as a third party library\n# and thus mark it as so when linting.\nknown-third-party=typing\n\n[EXCEPTIONS]\n\n# Exceptions that will emit a warning when being caught. Defaults to\n# \"Exception\"\novergeneral-exceptions=builtins.Exception\n"
  },
  {
    "path": ".python-version",
    "content": "3.7\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# CHANGELOG\n\n\n## 1.32.0\n\n\n* feature:Python:Add support for Python 3.13 (#2137)\n* feature:Python:Drop support for Python 3.8 (#2138)\n\n## 1.31.4\n\n\n* enhancement:Pip:Update pip to the latest version (<25.1)\n\n## 1.31.3\n\n\n* enhancement:Pip:Update pip to the latest version (<24.4)\n* enhancement:CLI:Remove distutils warning when packaging/deploying apps (#2123)\n\n## 1.31.2\n\n\n* enhancement:SQS:Add configuration option for MaximumConcurrency for SQS event source (#2104)\n\n## 1.31.1\n\n\n* enhancement:pip:Update pip version to allow 24.0 (#2092)\n* bugfix:tar:Validate tar extraction does not escape destination dir (#1990)\n\n## 1.31.0\n\n\n* feature:Python:Add support for Python 3.12 (#2086)\n* enhancement:Python:Drop support for Python 3.7 (#2095)\n\n## 1.30.0\n\n\n* feature:Python:Add support for Python 3.11 (#2053)\n* enhancement:Pip:Update version dependency on pip (#2080)\n\n## 1.29.0\n\n\n* feature:Python:Add support for Python 3.10 (#2037)\n* enhancement:Pip:Bump pip version range to latest version <23.2 (#2034)\n\n## 1.28.0\n\n\n* enhancement:Terraform:Update required terraform version to support 1.3 (#2014)\n* enhancement:Pip:Bump pip version range to latest version <22.3 (#2016)\n* feature:Config:Add support for `log_retention_in_days` (#943)\n\n## 1.27.3\n\n\n* bugfix:Versioning:Fix version string updates used in the release process (#1971)\n\n## 1.27.2\n\n\n* enhancement:Terraform:Update aws provider constraint to allow versions 4.x (#1951)\n* enhancement:event-source:Add attribute for message attributes in SNSEvent and generated test events (#1934)\n\n## 1.27.1\n\n\n* enhancement:Pip:Bump pip version range to latest version <22.2 (#1924)\n* enhancement:Websockets:Add support for WebSockets API Terraform packaging (#1670)\n\n## 1.27.0\n\n\n* bugfix:Local:Set a default timeout when creating the local LambdaContext instance (#1896)\n* feature:CDK:Add support for CDK v2 (#1742)\n\n## 1.26.6\n\n\n* bugfix:pip:Fix RuntimeError with pip v22.x (#1887)\n\n## 1.26.5\n\n\n* enhancement:Terraform:Remove template provider in favor of locals (#1869)\n* enhancement:Terraform:Bump Terraform version to suppose 1.1.x (#1868)\n\n## 1.26.4\n\n\n* bugfix:Terraform:Use updated keywords for providing provider version constraints (#1717)\n\n## 1.26.3\n\n\n* enhancement:Errors:Remove redundant error code in error message string (#1339)\n* enhancement:VPC:Associate VPC endpoint with Rest API (#1449)\n\n## 1.26.2\n\n\n* enhancement:Dependencies:Update pyyaml to 6.x (#1830)\n* bugfix:Websocket:Correctly configure websocket endpoint in the aws-cn partition (#1820)\n\n## 1.26.1\n\n\n* enhancement:Dependencies:Bump pip dependency to latest released version (#1817)\n* enhancement:Tests:Don't include tests package in .whl file (#1814)\n\n## 1.26.0\n\n\n* feature:Websockets:Add support for setting the Websocket protocol from the connect handler (#1768)\n* feature:SQS:Added MaximumBatchingWindowInSeconds to SQS event handler (#1778)\n\n## 1.25.0\n\n\n* feature:Python:Add support for Python 3.9 (#1787)\n\n## 1.24.2\n\n\n* enhancement:Dependencies:Bump attrs dependency to latest version (#1786)\n* bugfix:Auth:Fix ARN parsing when generating a builtin AuthResponse (#1775)\n* enhancement:CLI:Upgrade Click dependency to support v8.0.0 (#1729)\n\n## 1.24.1\n\n\n* bugfix:GovCloud:Fix partition error when updating API Gateway in GovCloud region (#1770)\n\n## 1.24.0\n\n\n* feature:Python2.7:Remove support for Python 2.7 (#1766)\n* enhancement:Terraform:Update Terraform packaging to support version 1.0 (#1757)\n* enhancement:Typing:Add missing WebsocketEvent type information (#1746)\n* enhancement:S3 events:Add source account to Lambda permissions when configuring S3 events (#1635)\n* enhancement:Packaging:Add support for Terraform v0.15 (#1725)\n\n## 1.23.0\n\n\n* enhancement:Deploy:Wait for function state to be active when deploying\n* feature:SQS:Add queue_arn parameter to enable CDK integration with SQS event handler (#1681)\n\n## 1.22.4\n\n\n* enhancement:Types:Add missing types to app.pyi stub file (#1701)\n* bugfix:Custom Domain:Fix custom domain generation when using the CDK (#1640)\n* bugfix:Packaging:Special cases pyrsistent packaging (#1696)\n\n## 1.22.3\n\n\n* enhancement:Terraform:Bump Terraform version to include 0.14\n* bugfix:Typing:Fix type definitions in app.pyi (#1676)\n* bugfix:Terraform:Use references instead of function names in Terraform packaging (#1558)\n\n## 1.22.2\n\n\n* enhancement:Blueprint:Add log property to blueprint\n* bugfix:Pipeline:Fix build command in pipeline generation (#1653)\n* enhancement:Dependencies:Change enum-compat dependency to enum34 with version restrictions (#1667)\n\n## 1.22.1\n\n\n* enhancement:Pip:Bump pip version range to latest version 21.x (#1630)\n* enhancement:IAM:Improve client call collection when generation policies (#692)\n\n## 1.22.0\n\n\n* feature:CDK:Add built-in support for the AWS CDK (#1622)\n\n## 1.21.9\n\n\n* enhancement:Dependencies:Bump attr version constraint (#1620)\n\n## 1.21.8\n\n\n* enhancement:Authorizers:Add support for custom headers in built-in authorizers (#1613)\n\n## 1.21.7\n\n\n* enhancement:Terraform:Map custom domain outputs in Terraform packaging (#1601)\n\n## 1.21.6\n\n\n* enhancement:Packaging:Increase upper bound for AWS provider in Terraform to 3.x (#1596)\n* enhancement:Packaging:Add support for manylinux2014 wheels (#1551)\n\n## 1.21.5\n\n\n* bugfix:Config:Fix config validation for env vars on py27 (#1573)\n* bugfix:Pip:Bump pip version contraint (#1590)\n* bugfix:REST:Add Allow header with list of allowed methods when returning 405 error (#1583)\n\n## 1.21.4\n\n\n* enhancement:Local:Allow custom Chalice class in local mode (#1502)\n* bugfix:Layers:Ensure single reference to managed layer (#1563)\n\n## 1.21.3\n\n\n* enhancement:Test:Add test client methods for generating sample kinesis events\n* enhancement:Config:Validate env var values are strings (#1543)\n\n## 1.21.2\n\n\n* bugfix:Terraform:Fix issue with wildcard partition names in s3 event handlers (#1508)\n* bugfix:Auth:Fix special case processing for root URL auth (#1271)\n* enhancement:Middleware:Add support for HTTP middleware catching exceptions (#1541)\n\n## 1.21.1\n\n\n* bugfix:Websockets:Fix custom domain name configuration for websockets (#1531)\n* bugfix:Local:Add support for multiple actions in builtin auth in local mode (#1527)\n* bugfix:Websocket:Fix websocket client configuration when using a custom domain (#1503)\n* bugfix:Local:Fix CORs handling in local mode (#761)\n\n## 1.21.0\n\n\n* bugfix:Blueprints:Fix regression when invoking Lambda functions from blueprints (#1535)\n* feature:Events:Add support for Kinesis and DynamoDB event handlers (#987)\n\n## 1.20.1\n\n\n* bugfix:Blueprints:Preserve docstring in blueprints (#1525)\n* enhancement:Binary:Support returning native python types when using `*/*` for binary types (#1501)\n\n## 1.20.0\n\n\n* enhancement:Blueprints:Add `current_app` property to Blueprints (#1094)\n* enhancement:CLI:Set `AWS_CHALICE_CLI_MODE` env var whenever a Chalice CLI command is run (#1200)\n* feature:Middleware:Add support for middleware (#1509)\n* feature:X-Ray:Add support for AWS X-Ray (#464)\n\n## 1.19.0\n\n\n* feature:Pipeline:Add a new v2 template for the deployment pipeline CloudFormation template (#1506)\n\n## 1.18.1\n\n\n* bugfix:Packaging:Add fallback to retrieve name/version from sdist (#1486)\n* bugfix:Analyzer:Handle symbols with multiple (shadowed) namespaces (#1494)\n\n## 1.18.0\n\n\n* feature:Packaging:Add support for automatic layer creation (#1485, #1001)\n\n## 1.17.0\n\n\n* feature:Testing:Add Chalice test client (#1468)\n* enhancement:regions:Add support for non `aws` partitions including aws-cn and aws-us-gov (#792).\n* bugfix:dependencies:Fix error when using old versions of click by requiring >=7\n* bugfix:local:Fix local mode builtin authorizer not stripping query string from URL (#1470)\n\n## 1.16.0\n\n\n* enhancement:local:Avoid error from cognito client credentials in local authorizer (#1447)\n* bugfix:package:Traverse symlinks to directories when packaging the vendor directory (#583).\n* feature:DomainName:Add support for custom domain names to REST/WebSocket APIs (#1194)\n* feature:auth:Add support for oauth scopes on routes (#1444).\n\n## 1.15.1\n\n\n* bugfix:packaging:Fix setup.py dependencies where the wheel package was not being installed (#1435)\n\n## 1.15.0\n\n\n* feature:blueprints:Mark blueprints as an accepted API (#1250)\n* feature:package:Add ability to generate and merge yaml CloudFormation templates (#1425)\n* enhancement:terraform:Allow generated terraform template to be used as a terraform module (#1300)\n* feature:logs:Add support for tailing logs (#4).\n\n## 1.14.1\n\n\n* enhancement:pip:Update pip version range to 20.1.\n\n## 1.14.0\n\n\n* bugfix:packaging:Fix pandas packaging regression (#1398)\n* feature:CLI:Add ``dev plan/appgraph`` commands (#1396)\n* enhancement:SQS:Validate queue name is used and not queue URL or ARN (#1388)\n\n## 1.13.1\n\n\n* enhancement:local:Add support for multiValueHeaders in local mode (#1381).\n* bugfix:local:Make ``current_request`` thread safe in local mode (#759)\n* enhancement:local:Add support for cognito in local mode (#1377).\n* bugfix:packaging:Fix terraform generation when injecting custom domains (#1237)\n* enhancement:packaging:Ensure repeatable zip file generation (#1114).\n* bugfix:CORS:Fix CORS request when returning compressed binary types (#1336)\n\n## 1.13.0\n\n\n* bugfix:logs:Fix error for ``chalice logs`` when a Lambda function\nhas not been invoked (#1252)\n* feature:CORS:Add global CORS configuration (#70)\n* bugfix:packaging:Fix packaging simplejson (#1304)\n* feature:python:Add support for Python 3.8 (#1315)\n* feature:authorizer:Add support for invocation role in custom authorizer (#1303)\n* bugfix:packaging:Fix packaging on case-sensitive filesystems (#1356)\n\n## 1.12.0\n\n\n* feature:CLI:Add ``generate-models`` command (#1245)\n* enhancement:websocket:Add ``close`` and ``info`` commands to websocket api (#1259)\n* enhancement:dependencies:Bump upper bound on PIP to ``<19.4`` (#1273, #1272)\n\n## 1.11.1\n\n\n* bugfix:blueprint:Fix mouting blueprints with root routes (#1230)\n* feature:rest-api:Add support for multi-value headers responses (#1205)\n\n## 1.11.0\n\n\n* feature:config:Add support for stage independent lambda configuration (#1162)\n* feature:event-source:Add support for subscribing to CloudWatch Events (#1126)\n* feature:event-source:Add a ``description`` argument to CloudWatch schedule events (#1155)\n* bugfix:rest-api:Fix deployment of API Gateway resource policies (#1220)\n\n## 1.10.0\n\n\n* feature:websocket:Add experimental support for websockets (#1017)\n* feature:rest-api:API Gateway Endpoint Type Configuration (#1160)\n* feature:rest-api:API Gateway Resource Policy Configuration (#1160)\n* feature:packaging:Add --merge-template option to package command (#1195)\n* feature:packaging:Add support for packaging via terraform (#1129)\n\n## 1.9.1\n\n\n* enhancement:rest-api:Make MultiDict mutable (#1158)\n\n## 1.9.0\n\n\n* enhancement:dependencies:Update PIP to support up to 19.1.x (#1104)\n* bugfix:rest-api:Fix handling of more complex Accept headers for binary\ncontent types (#1078)\n* enhancement:rest-api:Raise TypeError when trying to serialize an unserializable\ntype (#1100)\n* enhancement:policy:Update ``policies.json`` file (#1110)\n* feature:rest-api:Support repeating values in the query string (#1131)\n* feature:packaging:Add layer support to chalice package (#1130)\n* bugfix:rest-api:Fix bug with route ``name`` kwarg raising a ``TypeError`` (#1112)\n* enhancement:logging:Change exceptions to always be logged at the ERROR level (#969)\n* bugfix:CLI:Fix bug handling exceptions during ``chalice invoke`` on\nPython 3.7 (#1139)\n* bugfix:rest-api:Add support for API Gateway compression (#672)\n* enhancement:packaging:Add support for both relative and absolute paths for\n``--package-dir`` (#940)\n\n## 1.8.0\n\n\n* bugfix:packaging:Fall back to pure python version of yaml parser\nwhen unable to compile C bindings for PyYAML (#1074)\n* feature:packaging:Add support for Lambda layers. (#1001)\n\n## 1.7.0\n\n\n* bugfix:packaging:Fix packaging multiple local directories as dependencies (#1047)\n* feature:event-source:Add support for passing SNS ARNs to ``on_sns_message`` (#1048)\n* feature:blueprint:Add support for Blueprints (#1023)\n* feature:config:Add support for opting-in to experimental features (#1053)\n* feature:event-source:Provide Lambda context in event object (#856)\n\n## 1.6.2\n\n\n* enhancement:dependencies:Add support for pip 18.2 (#991)\n* enhancement:logging:Add more detailed debug logs to the packager. (#934)\n* feature:python:Add support for python3.7 (#992)\n* feature:rest-api:Support bytes for the application/json binary type (#988)\n* enhancement:rest-api:Use more compact JSON representation by default for dicts (#958)\n* enhancement:logging:Log internal exceptions as errors (#254)\n* feature:rest-api:Generate swagger documentation from docstrings (#574)\n\n## 1.6.1\n\n\n* bugfix:local:Fix local mode issue with unicode responses and Content-Length (#910)\n* enhancement:dev:Fix issue with ``requirements-dev.txt`` not setting up a working\ndev environment (#920)\n* enhancement:dependencies:Add support for pip 18 (#910)\n\n## 1.6.0\n\n\n* feature:CLI:Add ``chalice invoke`` command (#900)\n\n## 1.5.0\n\n\n* feature:policy:Add support for S3 upload_file/download_file in\npolicy generator (#889)\n\n## 1.4.0\n\n\n* enhancement:CI-CD:Add support for generating python 3.6 pipelines (#858)\n* feature:event-source:Add support for connecting lambda functions to S3 events (#855)\n* feature:event-source:Add support for connecting lambda functions to SNS message (#488)\n* enhancement:local:Make ``watchdog`` an optional dependency and add a built in\n``stat()`` based file poller (#867)\n* feature:event-source:Add support for connecting lambda functions to an SQS queue (#884)\n\n## 1.3.0\n\n\n* feature:config:Add support for Lambdas in a VPC (#413, #837, #673)\n* feature:packaging:Add support for packaging local directories (#653)\n* enhancement:local:Add support for automatically reloading the local\ndev server when files are modified (#316, #846, #706)\n* enhancement:logging:Add support for viewing cloudwatch logs of all\nlambda functions (#841, #849)\n\n## 1.2.3\n\n\n* enhancement:dependency:Add support for pip 10 (#808)\n* enhancement:policy:Update ``policies.json`` file (#817)\n\n## 1.2.2\n\n\n* bugfix:packaging:Fix package command not correctly setting environment variables (#795)\n\n## 1.2.1\n\n\n* enhancement:rest-api:Add CORS headers to error response (#715)\n* bugfix:local:Fix parsing empty query strings in local mode (#767)\n* bugfix:packaging:Fix regression in ``chalice package`` when using role arns (#793)\n\n## 1.2.0\n\nThis release features a rewrite of the core deployment\ncode used in Chalice.  This is a backwards compatible change\nfor users, but you may see changes to the autogenerated\nfiles Chalice creates.\nPlease read the `upgrade notes for 1.2.0\n<http://chalice.readthedocs.io/en/latest/upgrading.html#v1-2-0>`__\nfor more detailed information about upgrading to this release.\n\n\n\n* enhancement:rest-api:Print out full stack trace when an error occurs (#711)\n* enhancement:rest-api:Add ``image/jpeg`` as a default binary content type (#707)\n* feature:event-source:Add support for AWS Lambda only projects (#162, #640)\n* bugfix:policy:Fix inconsistent IAM role generation with pure lambdas (#685)\n* enhancement:deployment:Rewrite Chalice deployer to more easily support additional AWS resources (#604)\n* feature:packaging:Update the ``chalice package`` command to support\npure lambda functions and scheduled events. (#772)\n* bugfix:packaging:Fix packager edge case normalizing sdist names (#778)\n* bugfix:packaging:Fix SQLAlchemy packaging (#778)\n* bugfix:packaging:Fix packaging abi3, wheels this fixes cryptography 2.2.x packaging (#764)\n\n## 1.1.1\n\n\n* feature:CLI:Add ``--connection-timeout`` to the ``deploy`` command (#344)\n* bugfix:policy:Fix IAM role creation issue (#565)\n* bugfix:local:Fix `chalice local` handling of browser requests (#565)\n* enhancement:policy:Support async/await syntax in automatic policy generation (#565)\n* enhancement:packaging:Support additional PyPi package formats (.tar.bz2) (#720)\n\n## 1.1.0\n\n\n* enhancement:rest-api:Default to ``None`` in local mode when no query parameters\nare provided (#593)\n* enhancement:local:Add support for binding a custom address for local dev server (#596)\n* bugfix:rest-api:Fix local mode handling of routes with trailing slashes (#582)\n* bugfix:config:Scale ``lambda_timeout`` parameter correctly in local mode (#579)\n* feature:CI-CD:Add ``--codebuild-image`` to the ``generate-pipeline`` command (#609)\n* feature:CI-CD:Add ``--source`` and ``--buildspec-file`` to the\n``generate-pipeline`` command (#609)\n\n## 1.0.4\n\n\n* bugfix:packaging:Fix issue deploying some packages in Windows with utf-8 characters (#560)\n* feature:packaging:Add support for custom authorizers with ``chalice package`` (#580)\n\n## 1.0.3\n\n\n* bugfix:packaging:Fix issue with some packages with `-` or `.` in their distribution name (#555)\n* bugfix:rest-api:Fix issue where chalice local returned a 403 for successful OPTIONS requests (#554)\n* bugfix:local:Fix issue with chalice local mode causing http clients to hang on responses\nwith no body (#525)\n* enhancement:local:Add ``--stage`` parameter to ``chalice local`` (#545)\n* bugfix:policy:Fix issue with analyzer that followed recursive functions infinitely (#531)\n\n## 1.0.2\n\n\n* bugfix:rest-api:Fix issue where requestParameters were not being mapped\ncorrectly resulting in invalid generated javascript SDKs (#498)\n* bugfix:rest-api:Fix issue where ``api_gateway_stage`` was being\nignored when set in the ``config.json`` file (#495)\n* bugfix:rest-api:Fix bug where ``raw_body`` would raise an exception if no HTTP\nbody was provided (#503)\n* bugfix:CLI:Fix bug where exit codes were not properly being propagated during packaging (#500)\n* feature:local:Add support for Builtin Authorizers in local mode (#404)\n* bugfix:packaging:Fix environment variables being passed to subprocess while packaging (#501)\n* enhancement:rest-api:Allow view to require API keys as well as authorization (#473)\n\n## 1.0.1\n\n\n* bugfix:packaging:Only use alphanumeric characters for event names in SAM template (#450)\n* enhancement:config:Print useful error message when config.json is invalid (#458)\n* bugfix:rest-api:Fix api gateway stage being set incorrectly in non-default chalice stage\n(`#$70 <https://github.com/aws/chalice/issues/470>`__)\n\n## 1.0.0\n\n\n* enhancement:rest-api:Change default API Gateway stage name to ``api`` (#431)\n* enhancement:local:Add support for ``CORSConfig`` in ``chalice local`` (#436)\n* enhancement:logging:Propagate ``DEBUG`` log level when setting ``app.debug`` (#386)\n* feature:rest-api:Add support for wildcard routes and HTTP methods in ``AuthResponse`` (#403)\n* bugfix:policy:Fix bug when analyzing list comprehensions (#412)\n* enhancement:local:Update ``chalice local`` to use HTTP 1.1 (#448)\n\n## 1.0.0b2\n\nPlease read the `upgrade notes for 1.0.0b2\n<http://chalice.readthedocs.io/en/latest/upgrading.html#v1-0-0b2>`__\nfor more detailed information about upgrading to this release.\n\nNote: to install this beta version of chalice you must specify\n``pip install 'chalice>=1.0.0b2,<2.0.0'`` or\nuse the ``--pre`` flag for pip: ``pip install --pre chalice``.\n\n\n* enhancement:local:Set env vars from config in ``chalice local`` (#396)\n* bugfix:packaging:Fix edge case when building packages with optional c extensions (#421)\n* enhancement:policy:Remove legacy ``policy.json`` file support. Policy files must\nuse the stage name, e.g. ``policy-dev.json`` (#430)\n* bugfix:deployment:Fix issue where IAM role policies were updated twice on redeploys (#428)\n* enhancement:rest-api:Validate route path is not an empty string (#432)\n* enhancement:rest-api:Change route code to invoke view function with kwargs instead of\npositional args (#429)\n\n## 1.0.0b1\n\nPlease read the `upgrade notes for 1.0.0b1\n<http://chalice.readthedocs.io/en/latest/upgrading.html#v1-0-0b1>`__\nfor more detailed information about upgrading to this release.\n\nNote: to install this beta version of chalice you must specify\n``pip install 'chalice>=1.0.0b1,<2.0.0'`` or\nuse the ``--pre`` flag for pip: ``pip install --pre chalice``.\n\n\n\n* bugfix:rest-api:Fix unicode responses being quoted in python 2.7 (#262)\n* feature:event-source:Add support for scheduled events (#390)\n* feature:event-source:Add support for pure lambda functions (#390)\n* feature:packaging:Add support for wheel packaging. (#249)\n\n## 0.10.1\n\n\n* bugfix:deployment:Fix deployment issue for projects deployed with versions\nprior to 0.10.0 (#387)\n* bugfix:policy:Fix crash in analyzer when encountering genexprs and listcomps (#263)\n\n## 0.10.0\n\n\n* bugfix:deployment:Fix issue where provided ``iam_role_arn`` was not respected on\nredeployments of chalice applications and in the CloudFormation template\ngenerated by ``chalice package`` (#339)\n* bugfix:config:Fix ``autogen_policy`` in config being ignored (#367)\n* feature:rest-api:Add support for view functions that share the same view url but\ndiffer by HTTP method (#81)\n* enhancement:deployment:Improve deployment error messages for deployment packages that are\ntoo large (#246, #330, #380)\n* feature:rest-api:Add support for built-in authorizers (#356)\n\n## 0.9.0\n\n\n* feature:rest-api:Add support for ``IAM`` authorizer (#334)\n* feature:config:Add support for configuring ``lambda_timeout``, ``lambda_memory_size``,\nand ``tags`` in your AWS Lambda function (#347)\n* bugfix:packaging:Fix vendor directory contents not being importable locally (#350)\n* feature:rest-api:Add support for binary payloads (#348)\n\n## 0.8.2\n\n\n* bugfix:CLI:Fix issue where ``--api-gateway-stage`` was being\nignored  (#325)\n* feature:CLI:Add ``chalice delete`` command (#40)\n\n## 0.8.1\n\n\n* enhancement:deployment:Alway overwrite existing API Gateway Rest API on updates (#305)\n* enhancement:CORS:Added more granular support for CORS (#311)\n* bugfix:local:Fix duplicate content type header in local model (#311)\n* bugfix:rest-api:Fix content type validation when charset is provided (#306)\n* enhancement:rest-api:Add back custom authorizer support (#322)\n\n## 0.8.0\n\n\n* feature:python:Add support for python3! (#296)\n* bugfix:packaging:Fix swagger generation when using ``api_key_required=True`` (#279)\n* bugfix:CI-CD:Fix ``generate-pipeline`` to install requirements file before packaging (#295)\n\n## 0.7.0\n\n\n* feature:CLI:Add ``chalice package`` command.  This will\ncreate a SAM template and Lambda deployment package that\ncan be subsequently deployed by AWS CloudFormation. (#258)\n* feature:CLI:Add a ``--stage-name`` argument for creating chalice stages.\nA chalice stage is a completely separate set of AWS resources.\nAs a result, most configuration values can also be specified\nper chalice stage. (#264, #270)\n* feature:policy:Add support for ``iam_role_file``, which allows you to\nspecify the file location of an IAM policy to use for your app (#272)\n* feature:config:Add support for setting environment variables in your app (#273)\n* feature:CI-CD:Add a ``generate-pipeline`` command (#277)\n\n## 0.6.0\n\nCheck out the `upgrade notes for 0.6.0\n<http://chalice.readthedocs.io/en/latest/upgrading.html#v0-6-0>`__\nfor more detailed information about changes in this release.\n\n\n\n* feature:local:Add port parameter to local command (#220)\n* feature:packaging:Add support for binary vendored packages (#182, #106, #42)\n* feature:rest-api:Add support for customizing the returned HTTP response (#240, #218, #110, #30, #226)\n* enhancement:packaging:Always inject latest runtime to allow for chalice upgrades (#245)\n\n## 0.5.1\n\n\n* enhancement:local:Add support for serializing decimals in ``chalice local`` (#187)\n* enhancement:local:Add stdout handler for root logger when using ``chalice local`` (#186)\n* enhancement:local:Map query string parameters when using ``chalice local`` (#184)\n* enhancement:rest-api:Support Content-Type with a charset (#180)\n* bugfix:deployment:Fix not all resources being retrieved due to pagination (#188)\n* bugfix:deployment:Fix issue where root resource was not being correctly retrieved (#205)\n* bugfix:deployment:Handle case where local policy does not exist\n(`29 <https://github.com/awslabs/chalice/issues/29>`__)\n\n## 0.5.0\n\n\n* enhancement:logging:Add default application logger (#149)\n* enhancement:local:Return 405 when method is not supported when running\n``chalice local`` (#159)\n* enhancement:SDK:Add path params as requestParameters so they can be used\nin generated SDKs as well as cache keys (#163)\n* enhancement:rest-api:Map cognito user pool claims as part of request context (#165)\n* feature:CLI:Add ``chalice url`` command to print the deployed URL (#169)\n* enhancement:deployment:Bump up retry limit on initial function creation to 30 seconds (#172)\n* feature:local:Add support for ``DELETE`` and ``PATCH`` in ``chalice local`` (#167)\n* feature:CLI:Add ``chalice generate-sdk`` command (#178)\n\n## 0.4.0\n\n\n* bugfix:deployment:Fix issue where role name to arn lookup was failing due to lack of pagination (#139)\n* enhancement:rest-api:Raise errors when unknown kwargs are provided to ``app.route(...)`` (#144)\n* enhancement:config:Raise validation error when configuring CORS and an OPTIONS method (#142)\n* feature:rest-api:Add support for multi-file applications (#21)\n* feature:local:Add support for ``chalice local``, which runs a local HTTP server for testing (#22)\n\n## 0.3.0\n\n\n* bugfix:rest-api:Fix bug with case insensitive headers (#129)\n* feature:CORS:Add initial support for CORS (#133)\n* enhancement:deployment:Only add API gateway permissions if needed (#48)\n* bugfix:policy:Fix error when dict comprehension is encountered during policy generation (#131)\n* enhancement:CLI:Add ``--version`` and ``--debug`` options to the chalice CLI\n\n## 0.2.0\n\n\n* enhancement:rest-api:Add support for input content types besides ``application/json`` (#96)\n* enhancement:rest-api:Allow ``ChaliceViewErrors`` to propagate, so that API Gateway\ncan properly map HTTP status codes in non debug mode (#113)\n* enhancement:deployment:Add windows compatibility (#31)\n\n## 0.1.0\n\n\n* enhancement:packaging:Require ``virtualenv`` as a package dependency. (#33)\n* enhancement:CLI:Add ``--profile`` option when creating a new project (#28)\n* enhancement:rest-api:Add support for more error codes exceptions (#34)\n* enhancement:rest-api:Improve error validation when routes containing a\ntrailing ``/`` char (#65)\n* enhancement:rest-api:Validate duplicate route entries (#79)\n* enhancement:policy:Ignore lambda expressions in policy analyzer (#74)\n* enhancement:rest-api:Print original error traceback in debug mode (#50)\n* feature:rest-api:Add support for authenticate routes (#14)\n* feature:policy:Add ability to disable IAM role management (#61)\n"
  },
  {
    "path": "CODE_OF_CONDUCT.rst",
    "content": "===============\nCode of Conduct\n===============\n\n\nThis project has adopted the\n`Amazon Open Source Code of Conduct <https://aws.github.io/code-of-conduct>`__.\nFor more information see the\n`Code of Conduct FAQ <https://aws.github.io/code-of-conduct-faq>`__\nor contact opensource-codeofconduct@amazon.com with any additional\nquestions or comments.\n"
  },
  {
    "path": "CONTRIBUTING.rst",
    "content": "============\nContributing\n============\n\nWe work hard to provide a high-quality and useful framework, and we greatly value\nfeedback and contributions from our community. Whether it's a new feature,\ncorrection, or additional documentation, we welcome your pull requests. Please\nsubmit any `issues <https://github.com/aws/chalice/issues>`__\nor `pull requests <https://github.com/aws/chalice/pulls>`__ through GitHub.\n\nThis document contains guidelines for contributing code and filing issues.\n\nContributing Code\n=================\n\nThis list below are guidelines to use when submitting pull requests.\nThese are the same set of guidelines that the core contributors use\nwhen submitting changes, and we ask the same of all community\ncontributions as well:\n\n* Chalice is released under the\n  `Apache license <http://aws.amazon.com/apache2.0/>`__.\n  Any code you submit will be released under that license.\n* We maintain a high percentage of code coverage in our tests.  As\n  a general rule of thumb, code changes should not lower the overall\n  code coverage percentage for the project.  To help with this,\n  we use `codecov <https://codecov.io/gh/aws/chalice>`__, which will\n  comment on changes in code coverage for every pull request.\n  In practice, this means that every bug fix and feature addition should\n  include unit tests, and optionally functional and integration tests.\n* All PRs must run cleanly through ``make prcheck``.  This is described\n  in more detail in the sections below.\n* All new features must include documentation before they can be merged.\n\n\nFeature Development\n===================\n\nAny significant feature development for chalice should have a\ncorresponding github issue for discussion.  This gives several benefits:\n\n* Helps avoid wasted work by discussing the proposed API changes before\n  significant dev work is started.\n* Gives a single place to capture discussion about the rationale for\n  a feature.\n\nThis applies to:\n\n* Any feature that proposes modifying the public API for chalice\n* Additions to the chalice config file\n* Any new CLI commands\n\nIf you'd like to implement a significant feature for chalice,\nplease file an `issue <https://github.com/aws/chalice/issues>`__\nto start the design discussion.\n\nAll of the existing proposals are tagged with `proposals\n<https://github.com/aws/chalice/issues?q=is%3Aopen+is%3Aissue+label%3Aproposals>`__.\n\n\nDevelopment Environment Setup\n=============================\n\nFirst, create a virtual environment for chalice::\n\n    $ virtualenv venv\n    $ source venv/bin/activate\n\nKeep in mind that chalice is designed to work with AWS Lambda.\nMake sure to create your virtual environment using Python 3.9 to 3.12,\nas these are versions currently supported by both AWS Lambda and chalice.\n\nNext, you'll need to install chalice.  The easiest way to configure this\nis to  use::\n\n    $ pip install -e \".[event-file-poller]\"\n\n\nRun this command in the root directory of the chalice repo.\n\nNext, you have a few options.  There are various requirements files\ndepending on what you'd like to do.\n\nFor example, if you'd like to work on chalice, either fixing bugs or\nadding new features, install ``requirements-dev.txt``::\n\n\n    $ pip install -r requirements-dev.txt\n\n\nRunning Tests\n-------------\n\nChalice uses `pytest <https://docs.pytest.org/en/latest/>`__ to run tests.\nThe tests are categorized into 3 categories:\n\n* ``unit`` - Fast tests that don't make any IO calls (including file system\n  access).  Object dependencies are usually mocked out.\n* ``functional`` - These tests will test multiple components together,\n  typically through an interface that's close to what an end user would\n  be using.  For example, there are CLI functional tests that will invoke the\n  same functions that would correspond to a ``chalice deploy`` command.\n  In the functional tests, AWS calls are stubbed, but they'll go through the\n  `botocore stubber\n  <http://botocore.readthedocs.io/en/latest/reference/stubber.html>`__.\n* ``integration`` - These tests require an AWS account and will actually\n  create real AWS resources.  The integration tests in chalice usually\n  involve deploying a sample app and making assertions about the deployed\n  app by making HTTP/AWS requests to external endpoints.\n\nDuring development, you'll generally run the unit tests, and less\nfrequently you'll run the functional tests (the functional tests take\nan order of magnitude longer than the unit tests).  To run the unit tests,\nyou can run::\n\n    $ py.test tests/unit/\n\nTo run the functional tests you can run::\n\n    $ py.test tests/functional/\n\nThere's also a ``Makefile`` in the repo and you can run\n``make test`` to run both the unit and functional tests.\n\nCode Analysis\n-------------\n\nChalice uses several python linters to help ensure high\ncode quality.  This also helps to cut down on the noise\nfor pull request reviews because many issues are caught\nlocally during development.\n\nTo run all the linters, you can run ``make check``.\nThis will run:\n\n* `flake8 <http://flake8.pycqa.org/en/latest/>`__, a tool\n  for checking pep8 as well as common lint checks\n* `doc8 <https://pypi.python.org/pypi/doc8>`__, a style\n  checker for sphinx docs\n* `pydocstyle <https://github.com/PyCQA/pydocstyle>`__, a\n  docstring checker\n* `pylint <https://www.pylint.org/>`__, a much more\n  exhaustive linter that can catch additional issues\n  compared to ``flake8``.\n\nType Checking\n-------------\n\nChalice leverages the type hints introduced in python 3.5\nfrom `pep 484 <https://www.python.org/dev/peps/pep-0484/>`__\nand `pep 526 <https://www.python.org/dev/peps/pep-0526/>`__.\n`mypy <http://mypy-lang.org/>`__ is used to check types.\nAll chalice code must have type hints added or else the\nCI build will fail.  To check types you can run ``make typecheck``.\n\nChalice supports python2 as well as python3.  Because of\nthe requirement of supporting python2, function annotations\nare not allowed for specifying type hints, you must use\ntype comments as outlined in pep 484.\n\nKeep in mind that ``mypy`` only runs in python3, so you'll need\nto either use python3 when developing features or have mypy\nglobally installed.\n\nPRCheck\n-------\n\nBefore submitting a PR, ensure that ``make prcheck`` runs\nwithout any errors.  This command will run the linters,\nthe typecheckers and the unit and functional tests.\n``make prcheck`` is also run as part of the travis CI build.\nPull requests must pass ``make prcheck`` before they can be merged.\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include CONTRIBUTING.rst\ninclude CHANGELOG.md\ninclude LICENSE\ninclude NOTICE\ninclude README.rst\nrecursive-include chalice *.json\n\nrecursive-exclude * __pycache__\nrecursive-exclude * *.py[co]\n"
  },
  {
    "path": "Makefile",
    "content": "# Eventually I'll add:\n# py.test --cov chalice --cov-report term-missing --cov-fail-under 95 tests/\n# which will fail if tests are under 95%\nTESTS=tests/unit tests/functional tests/integration\n\ncheck:\n\t###### FLAKE8 #####\n\t# No unused imports, no undefined vars,\n\tflake8 --ignore=E731,W503,W504 --exclude chalice/__init__.py,chalice/compat.py,chalice/vendored/botocore/regions.py --max-complexity 10 chalice/\n\tflake8 --ignore=E731,W503,W504,F401 --max-complexity 10 chalice/compat.py\n\tflake8 tests/unit/ tests/functional/ tests/integration tests/aws\n\t#\n\t# Proper docstring conventions according to pep257\n\t#\n\t#\n\tpydocstyle --add-ignore=D100,D101,D102,D103,D104,D105,D107,D204,D301 --match='(?!(test_|regions)).*\\.py' chalice/\n\npylint:\n\t###### PYLINT ######\n\tpylint --rcfile .pylintrc chalice\n\t# Run our custom linter on test code.\n\tpylint --disable=I,E,W,R,C,F --enable C9999,C9998 tests/\n\ntest:\n\tpy.test -v $(TESTS)\n\ntypecheck:\n\tmypy --ignore-missing-imports --follow-imports=skip -p chalice --disallow-untyped-defs --strict-optional --warn-no-return\n\ncoverage:\n\tpy.test --cov chalice --cov-report term-missing $(TESTS)\n\ncoverage-unit:\n\tpy.test --cov chalice --cov-report term-missing tests/unit\n\nhtmlcov:\n\tpy.test --cov chalice --cov-report html $(TESTS)\n\trm -rf /tmp/htmlcov && mv htmlcov /tmp/\n\topen /tmp/htmlcov/index.html\n\ndoccheck:\n\t##### DOC8 ######\n\t# Correct rst formatting for documentation\n\t#\n\t# TODO: Remove doc8\n\t##\n\tdoc8 docs/source --ignore-path docs/source/topics/multifile.rst\n\t#\n\t#\n\t# Verify we have no broken external links\n\t# as well as no undefined internal references.\n\t$(MAKE) -C docs linkcheck\n\t# Verify we can build the docs.  The\n\t# treat warnings as errors flag is enabled\n\t# so any sphinx-build warnings will fail the build.\n\t$(MAKE) -C docs html\n\nprcheck: check pylint coverage doccheck typecheck\n\ninstall-dev-deps:\n\tpip install -r requirements-dev.txt --upgrade --upgrade-strategy eager -e .\n"
  },
  {
    "path": "NOTICE",
    "content": "chalice\nCopyright 2015 James Saryerwinnie. All Rights Reserved.\n"
  },
  {
    "path": "README.rst",
    "content": "===========\nAWS Chalice\n===========\n\n.. image:: https://badges.gitter.im/awslabs/chalice.svg\n   :target: https://gitter.im/awslabs/chalice?utm_source=badge&utm_medium=badge\n   :alt: Gitter\n.. image:: https://readthedocs.org/projects/chalice/badge/?version=latest\n   :target: http://aws.github.io/chalice/?badge=latest\n   :alt: Documentation Status\n\n\n.. image:: https://aws.github.io/chalice/_images/chalice-logo-whitespace.png\n   :target: https://aws.github.io/chalice/\n   :alt: Chalice Logo\n\n\nChalice is a framework for writing serverless apps in python. It allows\nyou to quickly create and deploy applications that use AWS Lambda.  It provides:\n\n* A command line tool for creating, deploying, and managing your app\n* A decorator based API for integrating with Amazon API Gateway, Amazon S3,\n  Amazon SNS, Amazon SQS, and other AWS services.\n* Automatic IAM policy generation\n\n\nYou can create Rest APIs:\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = Chalice(app_name=\"helloworld\")\n\n    @app.route(\"/\")\n    def index():\n        return {\"hello\": \"world\"}\n\nTasks that run on a periodic basis:\n\n.. code-block:: python\n\n    from chalice import Chalice, Rate\n\n    app = Chalice(app_name=\"helloworld\")\n\n    # Automatically runs every 5 minutes\n    @app.schedule(Rate(5, unit=Rate.MINUTES))\n    def periodic_task(event):\n        return {\"hello\": \"world\"}\n\n\nYou can connect a lambda function to an S3 event:\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = Chalice(app_name=\"helloworld\")\n\n    # Whenever an object is uploaded to 'mybucket'\n    # this lambda function will be invoked.\n\n    @app.on_s3_event(bucket='mybucket')\n    def handler(event):\n        print(\"Object uploaded for bucket: %s, key: %s\"\n              % (event.bucket, event.key))\n\nAs well as an SQS queue:\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = Chalice(app_name=\"helloworld\")\n\n    # Invoke this lambda function whenever a message\n    # is sent to the ``my-queue-name`` SQS queue.\n\n    @app.on_sqs_message(queue='my-queue-name')\n    def handler(event):\n        for record in event:\n            print(\"Message body: %s\" % record.body)\n\n\nAnd several other AWS resources.\n\nOnce you've written your code, you just run ``chalice deploy``\nand Chalice takes care of deploying your app.\n\n::\n\n    $ chalice deploy\n    ...\n    https://endpoint/dev\n\n    $ curl https://endpoint/api\n    {\"hello\": \"world\"}\n\nUp and running in less than 30 seconds.\nGive this project a try and share your feedback with us here on Github.\n\nThe documentation is available\n`here <http://aws.github.io/chalice/>`__.\n\nQuickstart\n==========\n\n.. quick-start-begin\n\nIn this tutorial, you'll use the ``chalice`` command line utility\nto create and deploy a basic REST API.  This quickstart uses Python 3.9,\nbut AWS Chalice supports all versions of python supported by AWS Lambda,\nwhich includes Python 3.9 through python 3.13.\n\nTo install Chalice, we'll first create and activate a virtual environment\nin python3.9::\n\n    $ python3 --version\n    Python 3.9.22\n    $ python3 -m venv venv39\n    $ . venv39/bin/activate\n\nNext we'll install Chalice using ``pip``::\n\n    $ python3 -m pip install chalice\n\nYou can verify you have chalice installed by running::\n\n    $ chalice --help\n    Usage: chalice [OPTIONS] COMMAND [ARGS]...\n    ...\n\n\nCredentials\n-----------\n\nBefore you can deploy an application, be sure you have\ncredentials configured.  If you have previously configured your\nmachine to run boto3 (the AWS SDK for Python) or the AWS CLI then\nyou can skip this section.\n\nIf this is your first time configuring credentials for AWS you\ncan follow these steps to quickly get started::\n\n    $ mkdir ~/.aws\n    $ cat >> ~/.aws/config\n    [default]\n    aws_access_key_id=YOUR_ACCESS_KEY_HERE\n    aws_secret_access_key=YOUR_SECRET_ACCESS_KEY\n    region=YOUR_REGION (such as us-west-2, us-west-1, etc)\n\nIf you want more information on all the supported methods for\nconfiguring credentials, see the\n`boto3 docs\n<http://boto3.readthedocs.io/en/latest/guide/configuration.html>`__.\n\n\nCreating Your Project\n---------------------\n\nThe next thing we'll do is use the ``chalice`` command to create a new\nproject::\n\n    $ chalice new-project helloworld\n\nThis will create a ``helloworld`` directory.  Cd into this\ndirectory.  You'll see several files have been created for you::\n\n    $ cd helloworld\n    $ ls -la\n    drwxr-xr-x   .chalice\n    -rw-r--r--   app.py\n    -rw-r--r--   requirements.txt\n\nYou can ignore the ``.chalice`` directory for now, the two main files\nwe'll focus on is ``app.py`` and ``requirements.txt``.\n\nLet's take a look at the ``app.py`` file:\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = Chalice(app_name='helloworld')\n\n\n    @app.route('/')\n    def index():\n        return {'hello': 'world'}\n\n\nThe ``new-project`` command created a sample app that defines a\nsingle view, ``/``, that when called will return the JSON body\n``{\"hello\": \"world\"}``.\n\n\nDeploying\n---------\n\nLet's deploy this app.  Make sure you're in the ``helloworld``\ndirectory and run ``chalice deploy``::\n\n    $ chalice deploy\n    Creating deployment package.\n    Creating IAM role: helloworld-dev\n    Creating lambda function: helloworld-dev\n    Creating Rest API\n    Resources deployed:\n      - Lambda ARN: arn:aws:lambda:us-west-2:12345:function:helloworld-dev\n      - Rest API URL: https://abcd.execute-api.us-west-2.amazonaws.com/api/\n\nYou now have an API up and running using API Gateway and Lambda::\n\n    $ curl https://qxea58oupc.execute-api.us-west-2.amazonaws.com/api/\n    {\"hello\": \"world\"}\n\nTry making a change to the returned dictionary from the ``index()``\nfunction.  You can then redeploy your changes by running ``chalice deploy``.\n\n.. quick-start-end\n\nNext Steps\n----------\n\nYou've now created your first app using ``chalice``.  You can make\nmodifications to your ``app.py`` file and rerun ``chalice deploy`` to\nredeploy your changes.\n\nAt this point, there are several next steps you can take.\n\n* `Tutorials <https://aws.github.io/chalice/tutorials/index.html>`__\n  - Choose from among several guided tutorials that will\n  give you step-by-step examples of various features of Chalice.\n* `Topics <https://aws.github.io/chalice/topics/index.html>`__ - Deep\n  dive into documentation on specific areas of Chalice.\n  This contains more detailed documentation than the tutorials.\n* `API Reference <https://aws.github.io/chalice/api.html>`__ - Low level\n  reference documentation on all the classes and methods that are part of the\n  public API of Chalice.\n\nIf you're done experimenting with Chalice and you'd like to cleanup, you can\nuse the ``chalice delete`` command, and Chalice will delete all the resources\nit created when running the ``chalice deploy`` command.\n\n::\n\n    $ chalice delete\n    Deleting Rest API: abcd4kwyl4\n    Deleting function aws:arn:lambda:region:123456789:helloworld-dev\n    Deleting IAM Role helloworld-dev\n\n\nFeedback\n========\n\nWe'd also love to hear from you.  Please create any Github issues for\nadditional features you'd like to see over at\nhttps://github.com/aws/chalice/issues.  You can also chat with us\non gitter: https://gitter.im/awslabs/chalice\n"
  },
  {
    "path": "chalice/__init__.py",
    "content": "from chalice.app import Chalice, Blueprint\nfrom chalice.app import (\n    ChaliceViewError, BadRequestError, UnauthorizedError, ForbiddenError,\n    NotFoundError, ConflictError, TooManyRequestsError, Response, CORSConfig,\n    CustomAuthorizer, CognitoUserPoolAuthorizer, IAMAuthorizer,\n    UnprocessableEntityError, WebsocketDisconnectedError,\n    AuthResponse, AuthRoute, Cron, Rate, __version__ as chalice_version,\n    ConvertToMiddleware, ChaliceUnhandledError\n)\n# We're reassigning version here to keep mypy happy.\n__version__ = chalice_version\n"
  },
  {
    "path": "chalice/analyzer.py",
    "content": "\"\"\"Source code analyzer for chalice app.\n\nThe main point of this module is to analyze your source code\nand track which AWS API calls you make.\n\nWe can then use this information to create IAM policies\nautomatically for you.\n\nHow it Works\n============\n\nThis is basically a simplified abstract interpreter.\nThe type inference is greatly simplified because\nwe're only interested in boto3 client types.\nIn a nutshell:\n\n* Create an AST and symbol table from the source code.\n* Interpret the AST and track boto3 types.  This is governed\n  by a few simple rules.\n* Propagate inferred boto3 types as much as possible.  Most of\n  the basic stuff is handled, for example:\n\n      * ``x = y`` if y is a boto3 type, so is x.\n      * ``a :: (x -> y), where y is a boto3 type, then given ``b = a()``,\n        b is of type y.\n      * Map inferred types across function params and return types.\n\nAt the end of the analysis, a final walk is performed to collect any\nnode of type ``Boto3ClientMethodCallType``.  This represents an\nAPI call being made.  This also lets you be selective about which\nAPI calls you care about.  For example, if you want only want to see\nwhich API calls happen in a particular function, only walk that\nparticular ``FunctionDef`` node.\n\n\"\"\"\nimport ast\nimport symtable\n\nfrom typing import Dict, Set, Any, Optional, List, Union, cast  # noqa\n\n\nAPICallT = Dict[str, Set[str]]\nOptASTSet = Optional[Set[ast.AST]]\nComprehensionNode = Union[ast.DictComp, ast.GeneratorExp, ast.ListComp]\n\n\ndef get_client_calls(source_code):\n    # type: (str) -> APICallT\n    \"\"\"Return all clients calls made in provided source code.\n\n    :returns: A dict of service_name -> set([client calls]).\n        Example: {\"s3\": set([\"list_objects\", \"create_bucket\"]),\n                  \"dynamodb\": set([\"describe_table\"])}\n    \"\"\"\n    parsed = parse_code(source_code)\n    t = SymbolTableTypeInfer(parsed)\n    binder = t.bind_types()\n    collector = APICallCollector(binder)\n    api_calls = collector.collect_api_calls(parsed.parsed_ast)\n    return api_calls\n\n\ndef get_client_calls_for_app(source_code):\n    # type: (str) -> APICallT\n    \"\"\"Return client calls for a chalice app.\n\n    This is similar to ``get_client_calls`` except it will\n    automatically traverse into chalice views with the assumption\n    that they will be called.\n\n    \"\"\"\n    parsed = parse_code(source_code)\n    t = AppViewTypeInfer(parsed)\n    binder = t.bind_types()\n    collector = APICallCollector(binder)\n    api_calls = collector.collect_api_calls(parsed.parsed_ast)\n    return api_calls\n\n\ndef parse_code(source_code, filename='app.py'):\n    # type: (str, str) -> ParsedCode\n    parsed = ast.parse(source_code, filename)\n    table = symtable.symtable(source_code, filename, 'exec')\n    return ParsedCode(parsed, ChainedSymbolTable(table, table))\n\n\nclass BaseType(object):\n    def __repr__(self):\n        # type: () -> str\n        return \"%s()\" % self.__class__.__name__\n\n    def __eq__(self, other):\n        # type: (Any) -> bool\n        return isinstance(other, self.__class__)\n\n\n# The next 5 classes are used to track the\n# components needed to create a boto3 client.\n# While we really only care about boto3 clients we need\n# to track all the types it takes to get there:\n#\n# import boto3          <--- bind \"boto3\" as the boto3 module type\n# c = boto.client       <--- bind \"c\" as the boto3 create client type\n# s3 = c('s3')          <--- bind 's3' as the boto3 client type, subtype 's3'.\n# m = s3.list_objects   <--- bind as API call 's3', 'list_objects'\n# r = m()               <--- bind as API call invoked (what we care about).\n#\n# That way we can handle (in addition to the case above) things like:\n# import boto3; boto3.client('s3').list_objects()\n# import boto3; s3 = boto3.client('s3'); s3.list_objects()\nclass Boto3ModuleType(BaseType):\n    pass\n\n\nclass Boto3CreateClientType(BaseType):\n    pass\n\n\nclass Boto3ClientType(BaseType):\n    def __init__(self, service_name):\n        # type: (str) -> None\n        #: The name of the AWS service, e.g. 's3'.\n        self.service_name = service_name\n\n    def __eq__(self, other):\n        # type: (Any) -> bool\n        # NOTE: We can't use self.__class__ because of a mypy bug:\n        # https://github.com/python/mypy/issues/3061\n        # We can change this back once that bug is fixed.\n        if not isinstance(other, Boto3ClientType):\n            return False\n        return self.service_name == other.service_name\n\n    def __repr__(self):\n        # type: () -> str\n        return \"%s(%s)\" % (self.__class__.__name__, self.service_name)\n\n\nclass Boto3ClientMethodType(BaseType):\n    def __init__(self, service_name, method_name):\n        # type: (str, str) -> None\n        self.service_name = service_name\n        self.method_name = method_name\n\n    def __eq__(self, other):\n        # type: (Any) -> bool\n        if self.__class__ != other.__class__:\n            return False\n        return (\n            self.service_name == other.service_name and\n            self.method_name == other.method_name)\n\n    def __repr__(self):\n        # type: () -> str\n        return \"%s(%s, %s)\" % (\n            self.__class__.__name__,\n            self.service_name,\n            self.method_name\n        )\n\n\nclass Boto3ClientMethodCallType(Boto3ClientMethodType):\n    pass\n\n\nclass TypedSymbol(symtable.Symbol):\n    inferred_type = None  # type: Any\n    ast_node = None  # type: ast.AST\n\n\nclass FunctionType(BaseType):\n    def __init__(self, return_type):\n        # type: (Any) -> None\n        self.return_type = return_type\n\n    def __eq__(self, other):\n        # type: (Any) -> bool\n        if self.__class__ != other.__class__:\n            return False\n        return self.return_type == other.return_type\n\n    def __repr__(self):\n        # type: () -> str\n        return \"%s(%s)\" % (\n            self.__class__.__name__,\n            self.return_type,\n        )\n\n\nclass StringLiteral(object):\n    def __init__(self, value):\n        # type: (str) -> None\n        self.value = value\n\n\nclass ParsedCode(object):\n    def __init__(self, parsed_ast, symbol_table):\n        # type: (ast.AST, ChainedSymbolTable) -> None\n        self.parsed_ast = parsed_ast\n        self.symbol_table = symbol_table\n\n\nclass APICallCollector(ast.NodeVisitor):\n    \"\"\"Traverse a given AST and look for any inferred API call types.\n\n    This visitor assumes you've ran type inference on the AST.\n    It will search through the AST and collect any API calls.\n    \"\"\"\n    def __init__(self, binder):\n        # type: (TypeBinder) -> None\n        self.api_calls = {}  # type: APICallT\n        self._binder = binder\n\n    def collect_api_calls(self, node):\n        # type: (ast.AST) -> APICallT\n        self.visit(node)\n        return self.api_calls\n\n    def visit(self, node):\n        # type: (ast.AST) -> None\n        inferred_type = self._binder.get_type_for_node(node)\n        if isinstance(inferred_type, Boto3ClientMethodCallType):\n            self.api_calls.setdefault(inferred_type.service_name, set()).add(\n                inferred_type.method_name)\n        ast.NodeVisitor.visit(self, node)\n\n\nclass ChainedSymbolTable(object):\n    def __init__(self, local_table, global_table):\n        # type: (symtable.SymbolTable, symtable.SymbolTable) -> None\n        # If you're in the module scope, then pass in\n        # the same symbol table for local and global.\n        self._local_table = local_table\n        self._global_table = global_table\n\n    def new_sub_table(self, local_table):\n        # type: (symtable.SymbolTable) -> ChainedSymbolTable\n        # Create a new symbol table using this instances\n        # local table as the new global table and the passed\n        # in local table as the new local table.\n        return self.__class__(local_table, self._local_table)\n\n    def get_inferred_type(self, name):\n        # type: (str) -> Any\n        # Given a symbol name, check whether a type\n        # has been inferred.\n        # The stdlib symtable will already fall back to\n        # global scope if necessary.\n        symbol = self._local_table.lookup(name)\n        if symbol.is_global():\n            try:\n                global_symbol = self._global_table.lookup(name)\n            except KeyError:\n                # It's not an error if a symbol.is_global()\n                # but is not in our \"_global_table\", because\n                # we're not considering the builtin scope.\n                # In this case we just say that there is no\n                # type we've inferred.\n                return None\n            return getattr(global_symbol, 'inferred_type', None)\n        return getattr(symbol, 'inferred_type', None)\n\n    def set_inferred_type(self, name, inferred_type):\n        # type: (str, Any) -> None\n        symbol = cast(TypedSymbol, self._local_table.lookup(name))\n        symbol.inferred_type = inferred_type\n\n    def lookup_sub_namespace(self, name, lineno=None):\n        # type: (str, Optional[int]) -> ChainedSymbolTable\n        for child in self._local_table.get_children():\n            if child.get_name() == name:\n                if lineno is not None:\n                    if child.get_lineno() == lineno:\n                        return self.__class__(child, self._local_table)\n                else:\n                    return self.__class__(child, self._local_table)\n        for child in self._global_table.get_children():\n            if child.get_name() == name:\n                return self.__class__(child, self._global_table)\n        raise ValueError(\"Unknown symbol name: %s\" % name)\n\n    def get_sub_namespaces(self):\n        # type: () -> List[symtable.SymbolTable]\n        return self._local_table.get_children()\n\n    def get_name(self):\n        # type: () -> str\n        return self._local_table.get_name()\n\n    def get_symbols(self):\n        # type: () -> List[symtable.Symbol]\n        return self._local_table.get_symbols()\n\n    def register_ast_node_for_symbol(self, name, node):\n        # type: (str, ast.AST) -> None\n        symbol = cast(TypedSymbol, self._local_table.lookup(name))\n        symbol.ast_node = node\n\n    def lookup_ast_node_for_symbol(self, name):\n        # type: (str) -> ast.AST\n        symbol = self._local_table.lookup(name)\n        if symbol.is_global():\n            symbol = self._global_table.lookup(name)\n        try:\n            return cast(TypedSymbol, symbol).ast_node\n        except AttributeError:\n            raise ValueError(\n                \"No AST node registered for symbol: %s\" % name)\n\n    def has_ast_node_for_symbol(self, name):\n        # type: (str) -> bool\n        try:\n            self.lookup_ast_node_for_symbol(name)\n            return True\n        except (ValueError, KeyError):\n            return False\n\n\nclass TypeBinder(object):\n\n    def __init__(self):\n        # type: () -> None\n        self._node_to_type = {}  # type: Dict[ast.AST, Any]\n\n    def get_type_for_node(self, node):\n        # type: (Any) -> Any\n        return self._node_to_type.get(node)\n\n    def set_type_for_node(self, node, inferred_type):\n        # type: (Any, Any) -> None\n        self._node_to_type[node] = inferred_type\n\n\nclass SymbolTableTypeInfer(ast.NodeVisitor):\n    _SDK_PACKAGE = 'boto3'\n    _CREATE_CLIENT = 'client'\n\n    def __init__(self, parsed_code, binder=None, visited=None):\n        # type: (ParsedCode, Optional[TypeBinder], OptASTSet) -> None\n        self._symbol_table = parsed_code.symbol_table\n        self._current_ast_namespace = parsed_code.parsed_ast\n        self._node_inference = {}  # type: Dict[ast.AST, Any]\n        if binder is None:\n            binder = TypeBinder()\n        if visited is None:\n            visited = set()\n        self._binder = binder\n        self._visited = visited\n\n    def bind_types(self):\n        # type: () -> TypeBinder\n        self.visit(self._current_ast_namespace)\n        return self._binder\n\n    def known_types(self, scope_name=None):\n        # type: (Optional[str]) -> Dict[str, Any]\n        table = None\n        if scope_name is None:\n            table = self._symbol_table\n        else:\n            table = self._symbol_table.lookup_sub_namespace(scope_name)\n        return {\n            s.get_name(): cast(TypedSymbol, s).inferred_type\n            for s in table.get_symbols()\n            if hasattr(s, 'inferred_type') and\n            cast(TypedSymbol, s).inferred_type is not None and\n            s.is_local()\n        }\n\n    def _set_inferred_type_for_name(self, name, inferred_type):\n        # type: (str, Any) -> None\n        self._symbol_table.set_inferred_type(name, inferred_type)\n\n    def _set_inferred_type_for_node(self, node, inferred_type):\n        # type: (Any, Any) -> None\n        self._binder.set_type_for_node(node, inferred_type)\n\n    def _get_inferred_type_for_node(self, node):\n        # type: (Any) -> Any\n        return self._binder.get_type_for_node(node)\n\n    def _new_inference_scope(self, parsed_code, binder, visited):\n        # type: (ParsedCode, TypeBinder, Set[ast.AST]) -> SymbolTableTypeInfer\n        instance = self.__class__(parsed_code, binder, visited)\n        return instance\n\n    def visit_Import(self, node):\n        # type: (ast.Import) -> None\n        for child in node.names:\n            if isinstance(child, ast.alias):\n                import_name = child.name\n                if import_name == self._SDK_PACKAGE:\n                    self._set_inferred_type_for_name(\n                        import_name, Boto3ModuleType())\n        self.generic_visit(node)\n\n    def visit_Name(self, node):\n        # type: (ast.Name) -> None\n        self._set_inferred_type_for_node(\n            node,\n            self._symbol_table.get_inferred_type(node.id)\n        )\n        self.generic_visit(node)\n\n    def visit_Assign(self, node):\n        # type: (ast.Assign) -> None\n        # The LHS gets the inferred type of the RHS.\n        # We do this post-traversal to let the type inference\n        # run on the children first.\n        self.generic_visit(node)\n        rhs_inferred_type = self._get_inferred_type_for_node(node.value)\n        if rhs_inferred_type is None:\n            # Special casing assignment to a string literal.\n            if isinstance(node.value, ast.Str):\n                rhs_inferred_type = StringLiteral(node.value.s)\n                self._set_inferred_type_for_node(node.value, rhs_inferred_type)\n        for t in node.targets:\n            if isinstance(t, ast.Name):\n                self._symbol_table.set_inferred_type(t.id, rhs_inferred_type)\n                self._set_inferred_type_for_node(node, rhs_inferred_type)\n\n    def visit_Attribute(self, node):\n        # type: (ast.Attribute) -> None\n        self.generic_visit(node)\n        lhs_inferred_type = self._get_inferred_type_for_node(node.value)\n        if lhs_inferred_type is None:\n            return\n        elif lhs_inferred_type == Boto3ModuleType():\n            # Check for attributes such as boto3.client.\n            if node.attr == self._CREATE_CLIENT:\n                # This is a \"boto3.client\" attribute.\n                self._set_inferred_type_for_node(node, Boto3CreateClientType())\n        elif isinstance(lhs_inferred_type, Boto3ClientType):\n            self._set_inferred_type_for_node(\n                node,\n                Boto3ClientMethodType(\n                    lhs_inferred_type.service_name,\n                    node.attr\n                )\n            )\n\n    def visit_Call(self, node):\n        # type: (ast.Call) -> None\n        self.generic_visit(node)\n        # func -> Node that's being called\n        # args -> Arguments being passed.\n        inferred_func_type = self._get_inferred_type_for_node(node.func)\n        if inferred_func_type == Boto3CreateClientType():\n            # e_0 : B3CCT -> B3CT[S]\n            # e_1 : S str which is a service name\n            # e_0(e_1) : B3CT[e_1]\n            if len(node.args) >= 1:\n                service_arg = node.args[0]\n                if isinstance(service_arg, ast.Str):\n                    self._set_inferred_type_for_node(\n                        node, Boto3ClientType(service_arg.s))\n                elif isinstance(self._get_inferred_type_for_node(service_arg),\n                                StringLiteral):\n                    sub_type = self._get_inferred_type_for_node(service_arg)\n                    inferred_type = Boto3ClientType(sub_type.value)\n                    self._set_inferred_type_for_node(node, inferred_type)\n        elif isinstance(inferred_func_type, Boto3ClientMethodType):\n            self._set_inferred_type_for_node(\n                node,\n                Boto3ClientMethodCallType(\n                    inferred_func_type.service_name,\n                    inferred_func_type.method_name\n                )\n            )\n        elif isinstance(inferred_func_type, FunctionType):\n            self._set_inferred_type_for_node(\n                node, inferred_func_type.return_type)\n        elif isinstance(node.func, ast.Name) and \\\n                self._symbol_table.has_ast_node_for_symbol(node.func.id):\n            if node not in self._visited:\n                self._visited.add(node)\n                self._infer_function_call(node)\n\n    def visit_Lambda(self, node):\n        # type: (ast.Lambda) -> None\n        # Lambda is going to be a bit tricky because\n        # there's a new child namespace (via .get_children()),\n        # but it's not something that will show up in the\n        # current symbol table via .lookup().\n        # For now, we're going to ignore lambda expressions.\n        pass\n\n    def _infer_function_call(self, node):\n        # type: (Any) -> None\n        # Here we're calling a function we haven't analyzed\n        # yet.  We're first going to analyze the function.\n        # This will set the inferred_type on the FunctionDef\n        # node.\n        # If we get a FunctionType as the inferred type of the\n        # function, then we know that the inferred type for\n        # calling the function is the .return_type type.\n        function_name = node.func.id\n        sub_table = self._symbol_table.lookup_sub_namespace(\n            function_name, node.lineno)\n        ast_node = self._symbol_table.lookup_ast_node_for_symbol(\n            function_name)\n\n        self._map_function_params(sub_table, node, ast_node)\n\n        child_infer = self._new_inference_scope(\n            ParsedCode(ast_node, sub_table), self._binder, self._visited)\n        child_infer.bind_types()\n        inferred_func_type = self._get_inferred_type_for_node(ast_node)\n        self._symbol_table.set_inferred_type(function_name, inferred_func_type)\n        # And finally the result of this Call() node will be\n        # the return type from the function we just analyzed.\n        if isinstance(inferred_func_type, FunctionType):\n            self._set_inferred_type_for_node(\n                node, inferred_func_type.return_type)\n\n    def _map_function_params(self, sub_table, node, def_node):\n        # type: (ChainedSymbolTable, Any, Any) -> None\n        # TODO: Handle the full calling syntax, kwargs, stargs, etc.\n        #       Right now we just handle positional args.\n        defined_args = def_node.args\n        for arg, defined in zip(node.args, defined_args.args):\n            inferred_type = self._get_inferred_type_for_node(arg)\n            if inferred_type is not None:\n                name = self._get_name(defined)\n                sub_table.set_inferred_type(name, inferred_type)\n\n    def _get_name(self, node):\n        # type: (Any) -> str\n        try:\n            return getattr(node, 'id')\n        except AttributeError:\n            return getattr(node, 'arg')\n\n    def visit_FunctionDef(self, node):\n        # type: (ast.FunctionDef) -> None\n        if node.name == self._symbol_table.get_name():\n            # Not using generic_visit() because we don't want to\n            # visit the decorator_list attr.\n            for child in node.body:\n                self.visit(child)\n        else:\n            self._symbol_table.register_ast_node_for_symbol(node.name, node)\n\n    def visit_AsyncFunctionDef(self, node):\n        # type: (ast.AsyncFunctionDef) -> None\n        # this type is actually wrong but we can't use the actual type as it's\n        # not available in python 2\n        converted = cast(ast.FunctionDef, node)\n        self.visit_FunctionDef(converted)\n\n    def visit_ClassDef(self, node):\n        # type: (ast.ClassDef) -> None\n        # Not implemented yet.  We want to ensure we don't\n        # traverse into the class body for now.\n        return\n\n    def visit_DictComp(self, node):\n        # type: (ast.DictComp) -> None\n        self._handle_comprehension(node, 'dictcomp')\n\n    def visit_Return(self, node):\n        # type: (Any) -> None\n        self.generic_visit(node)\n        inferred_type = self._get_inferred_type_for_node(node.value)\n        if inferred_type is not None:\n            self._set_inferred_type_for_node(node, inferred_type)\n            # We're making a pretty big assumption there's one return\n            # type per function.  Will likely need to come back to this.\n            inferred_func_type = FunctionType(inferred_type)\n            self._set_inferred_type_for_node(self._current_ast_namespace,\n                                             inferred_func_type)\n\n    def visit_ListComp(self, node):\n        # type: (ast.ListComp) -> None\n        # 'listcomp' is the string literal used by python\n        # to creating the SymbolTable for the corresponding\n        # list comp function.\n        self._handle_comprehension(node, 'listcomp')\n\n    def visit_GeneratorExp(self, node):\n        # type: (ast.GeneratorExp) -> None\n        # Generator expressions are an interesting case.\n        # They create a new sub scope, but they're not\n        # explicitly named.  Python just creates a table\n        # with the name \"genexpr\".\n        self._handle_comprehension(node, 'genexpr')\n\n    def _visit_first_comprehension_generator(self, node):\n        # type: (ComprehensionNode) -> None\n        if node.generators:\n            # first generator's iterator is visited in the current scope\n            first_generator = node.generators[0]\n            self.visit(first_generator.iter)\n\n    def _collect_comprehension_children(self, node):\n        # type: (ComprehensionNode) -> List[ast.expr]\n        if isinstance(node, ast.DictComp):\n            # dict comprehensions have two values to be checked\n            child_nodes = [node.key, node.value]\n        else:\n            child_nodes = [node.elt]\n\n        if node.generators:\n            first_generator = node.generators[0]\n            child_nodes.append(first_generator.target)\n            for if_expr in first_generator.ifs:\n                child_nodes.append(if_expr)\n\n        for generator in node.generators[1:]:\n            # rest need to be visited in the child scope\n            child_nodes.append(generator.iter)\n            child_nodes.append(generator.target)\n            for if_expr in generator.ifs:\n                child_nodes.append(if_expr)\n        return child_nodes\n\n    def _visit_comprehension_children(self, node, comprehension_type):\n        # type: (ComprehensionNode, str) -> None\n        child_nodes = self._collect_comprehension_children(node)\n        child_scope = self._get_matching_sub_namespace(comprehension_type,\n                                                       node.lineno)\n        if child_scope is None:\n            # In Python 2 there's no child scope for list comp\n            # Or we failed to locate the child scope, this happens in Python 2\n            # when there are multiple comprehensions of the same type in the\n            # same scope. The line number trick doesn't work as Python 2 always\n            # passes line number 0, make a best effort\n            for child_node in child_nodes:\n                try:\n                    self.visit(child_node)\n                except KeyError:\n                    pass\n            return\n        for child_node in child_nodes:\n            # visit sub expressions in the child scope\n            child_table = self._symbol_table.new_sub_table(child_scope)\n            child_infer = self._new_inference_scope(\n                ParsedCode(child_node, child_table),\n                self._binder, self._visited)\n            child_infer.bind_types()\n\n    def _handle_comprehension(self, node, comprehension_type):\n        # type: (ComprehensionNode, str) -> None\n        self._visit_first_comprehension_generator(node)\n        self._visit_comprehension_children(node, comprehension_type)\n\n    def _get_matching_sub_namespace(self, name, lineno):\n        # type: (str, int) -> Optional[symtable.SymbolTable]\n        namespaces = [t for t in self._symbol_table.get_sub_namespaces()\n                      if t.get_name() == name]\n        if len(namespaces) == 1:\n            # if there's only one match for the name, return it\n            return namespaces[0]\n        for namespace in namespaces:\n            # otherwise disambiguate by using the line number\n            if namespace.get_lineno() == lineno:\n                return namespace\n        return None\n\n    def visit(self, node):\n        # type: (Any) -> None\n        return ast.NodeVisitor.visit(self, node)\n\n\nclass AppViewTypeInfer(ast.NodeVisitor):\n    _CHALICE_DECORATORS = [\n        'route', 'authorizer', 'lambda_function',\n        'schedule', 'on_s3_event', 'on_sns_message',\n        'on_sqs_message', 'on_ws_connect', 'on_ws_message',\n        'on_ws_disconnect',\n    ]\n\n    def __init__(self, parsed_code):\n        # type: (ParsedCode) -> None\n        self._binder = TypeBinder()\n        self._visited = set()  # type: Set[ast.AST]\n        self._parsed_code = parsed_code\n        self._type_infer = SymbolTableTypeInfer(\n            self._parsed_code, self._binder, self._visited)\n\n    def bind_types(self):\n        # type: () -> TypeBinder\n        self._type_infer.bind_types()\n        self.visit(self._parsed_code.parsed_ast)\n        return self._binder\n\n    def visit_FunctionDef(self, node):\n        # type: (ast.FunctionDef) -> None\n        if self._is_chalice_view(node):\n            sub_table = self._parsed_code.symbol_table.lookup_sub_namespace(\n                node.name, node.lineno)\n            child_infer = SymbolTableTypeInfer(\n                ParsedCode(node, sub_table), self._binder, self._visited)\n            child_infer.bind_types()\n\n    def _is_chalice_view(self, node):\n        # type: (ast.FunctionDef) -> bool\n        # We can certainly improve on this, but this check is more\n        # of a heuristic for the time being.  The ideal way to do this\n        # is to infer the Chalice type and ensure the function is\n        # decorated with the Chalice type's route() method.\n        decorator_list = node.decorator_list\n        if not decorator_list:\n            return False\n        for decorator in decorator_list:\n            if isinstance(decorator, ast.Call) and \\\n                    isinstance(decorator.func, ast.Attribute):\n                if decorator.func.attr in self._CHALICE_DECORATORS:\n                    return True\n        return False\n"
  },
  {
    "path": "chalice/api/__init__.py",
    "content": "\"\"\"Control plane APIs for programatically building/deploying Chalice apps.\n\nThe eventual goal is to expose this as a public API that other tools can use\nin their own integrations with Chalice, but this will need time for the APIs\nto mature so for the time being this is an internal-only API.\n\"\"\"\nimport os\nfrom typing import Optional, Dict, Any\nfrom chalice.cli.factory import CLIFactory\n\n\ndef package_app(project_dir: str,\n                output_dir: str,\n                stage: str,\n                chalice_config: Optional[Dict[str, Any]] = None,\n                package_format: str = 'cloudformation',\n                template_format: str = 'json') -> None:\n    factory = CLIFactory(project_dir, environ=os.environ)\n    if chalice_config is None:\n        chalice_config = {}\n    config = factory.create_config_obj(\n        stage, user_provided_params=chalice_config)\n    options = factory.create_package_options()\n    packager = factory.create_app_packager(config, options,\n                                           package_format=package_format,\n                                           template_format=template_format)\n    packager.package_app(config, output_dir, stage)\n"
  },
  {
    "path": "chalice/app.py",
    "content": "\"\"\"Chalice app and routing code.\"\"\"\n# pylint: disable=too-many-lines,ungrouped-imports\nimport re\nimport sys\nimport os\nimport logging\nimport json\nimport traceback\nimport decimal\nimport base64\nimport copy\nimport functools\nimport datetime\nfrom collections import defaultdict\n\n# Implementation note:  This file is intended to be a standalone file\n# that gets copied into the lambda deployment package.  It has no dependencies\n# on other parts of chalice, so it can stay small and lightweight, with minimal\n# startup overhead.\nfrom urllib.parse import unquote_plus\nfrom collections.abc import Mapping\nfrom collections.abc import MutableMapping\n\n\n__version__: str = '1.32.0'\n\nfrom typing import List, Dict, Any, Optional, Sequence, Union, Callable, Set, \\\n    Iterator, TYPE_CHECKING, Tuple\n\nif TYPE_CHECKING:\n    from chalice.local import LambdaContext\n\n_PARAMS = re.compile(r'{\\w+}')\nMiddlewareFuncType = Callable[[Any, Callable[[Any], Any]], Any]\nUserHandlerFuncType = Callable[..., Any]\nHeadersType = Dict[str, Union[str, List[str]]]\n\n# In python 3 string and bytes are different so we explicitly check\n# for both.\n_ANY_STRING = (str, bytes)\n\n\ndef handle_extra_types(\n        obj: Union[decimal.Decimal, 'MultiDict']\n) -> Union[float, Dict]:\n    # Lambda will automatically serialize decimals so we need\n    # to support that as well.\n    if isinstance(obj, decimal.Decimal):\n        return float(obj)\n    # This is added for backwards compatibility.\n    # It will keep only the last value for every key as it used to.\n    if isinstance(obj, MultiDict):\n        return dict(obj)\n    raise TypeError('Object of type %s is not JSON serializable'\n                    % obj.__class__.__name__)\n\n\ndef error_response(\n    message: str, error_code: str, http_status_code: int,\n    headers: Optional[HeadersType] = None\n) -> 'Response':\n    body = {'Code': error_code, 'Message': message}\n    response = Response(body=body, status_code=http_status_code,\n                        headers=headers)\n    return response\n\n\ndef _matches_content_type(content_type: str,\n                          valid_content_types: List[str]) -> bool:\n    # If '*/*' is in the Accept header or the valid types,\n    # then all content_types match. Otherwise see of there are any common types\n    content_type = content_type.lower()\n    valid_content_types = [x.lower() for x in valid_content_types]\n    return '*/*' in content_type or \\\n        '*/*' in valid_content_types or \\\n        _content_type_header_contains(content_type, valid_content_types)\n\n\ndef _content_type_header_contains(\n        content_type_header: str,\n        valid_content_types: List[str]\n) -> bool:\n    content_type_header_parts = [\n        p.strip() for p in\n        re.split('[,;]', content_type_header)\n    ]\n    valid_parts = set(valid_content_types).intersection(\n        content_type_header_parts\n    )\n    return len(valid_parts) > 0\n\n\nclass ChaliceError(Exception):\n    pass\n\n\nclass WebsocketDisconnectedError(ChaliceError):\n    def __init__(self, connection_id: str):\n        self.connection_id: str = connection_id\n\n\nclass ChaliceViewError(ChaliceError):\n    STATUS_CODE: int = 500\n\n\nclass ChaliceUnhandledError(ChaliceError):\n    \"\"\"This error is not caught from a Chalice view function.\n\n    This exception is allowed to propagate from a view function so\n    that middleware handlers can process the exception.\n    \"\"\"\n\n\nclass BadRequestError(ChaliceViewError):\n    STATUS_CODE: int = 400\n\n\nclass UnauthorizedError(ChaliceViewError):\n    STATUS_CODE: int = 401\n\n\nclass ForbiddenError(ChaliceViewError):\n    STATUS_CODE: int = 403\n\n\nclass NotFoundError(ChaliceViewError):\n    STATUS_CODE: int = 404\n\n\nclass MethodNotAllowedError(ChaliceViewError):\n    STATUS_CODE: int = 405\n\n\nclass RequestTimeoutError(ChaliceViewError):\n    STATUS_CODE: int = 408\n\n\nclass ConflictError(ChaliceViewError):\n    STATUS_CODE: int = 409\n\n\nclass UnprocessableEntityError(ChaliceViewError):\n    STATUS_CODE: int = 422\n\n\nclass TooManyRequestsError(ChaliceViewError):\n    STATUS_CODE: int = 429\n\n\nALL_ERRORS = [\n    ChaliceViewError,\n    BadRequestError,\n    NotFoundError,\n    UnauthorizedError,\n    ForbiddenError,\n    MethodNotAllowedError,\n    RequestTimeoutError,\n    ConflictError,\n    UnprocessableEntityError,\n    TooManyRequestsError,\n]\n\n\nclass MultiDict(MutableMapping):  # pylint: disable=too-many-ancestors\n    \"\"\"A mapping of key to list of values.\n\n    Accessing it in the usual way will return the last value in the list.\n    Calling getlist will return a list of all the values associated with\n    the same key.\n    \"\"\"\n\n    def __init__(self, mapping: Optional[Dict]):\n        if mapping is None:\n            mapping = {}\n\n        self._dict = mapping\n\n    def __getitem__(self, k: Any) -> Any:\n        try:\n            return self._dict[k][-1]\n        except IndexError:\n            raise KeyError(k)\n\n    def __setitem__(self, k: Any, v: Any) -> None:\n        self._dict[k] = [v]\n\n    def __delitem__(self, k: Any) -> None:\n        del self._dict[k]\n\n    def getlist(self, k: Any) -> List:\n        return list(self._dict[k])\n\n    def __len__(self) -> int:\n        return len(self._dict)\n\n    def __iter__(self) -> Iterator:\n        return iter(self._dict)\n\n    def __repr__(self) -> str:\n        return 'MultiDict(%s)' % self._dict\n\n    def __str__(self) -> str:\n        return repr(self)\n\n\nclass CaseInsensitiveMapping(Mapping):\n    \"\"\"Case insensitive and read-only mapping.\"\"\"\n\n    def __init__(self, mapping: Union[Dict[str, Any], MultiDict]) -> None:\n        mapping = mapping or {}\n        self._dict = {k.lower(): v for k, v in mapping.items()}\n\n    def __getitem__(self, key: str) -> Any:\n        return self._dict[key.lower()]\n\n    def __iter__(self) -> Iterator:\n        return iter(self._dict)\n\n    def __len__(self) -> int:\n        return len(self._dict)\n\n    def __repr__(self) -> str:\n        return 'CaseInsensitiveMapping(%s)' % repr(self._dict)\n\n\nclass Authorizer(object):\n    name: str = ''\n    scopes: List[str] = []\n\n    def to_swagger(self) -> Dict[str, Any]:\n        raise NotImplementedError(\"to_swagger\")\n\n    def with_scopes(self, scopes: List[str]) -> 'Authorizer':\n        raise NotImplementedError(\"with_scopes\")\n\n\nclass IAMAuthorizer(Authorizer):\n    _AUTH_TYPE: str = 'aws_iam'\n\n    def __init__(self) -> None:\n        self.name: str = 'sigv4'\n        self.scopes: List[str] = []\n\n    def to_swagger(self) -> Dict[str, str]:\n        return {\n            'in': 'header',\n            'type': 'apiKey',\n            'name': 'Authorization',\n            'x-amazon-apigateway-authtype': 'awsSigv4',\n        }\n\n    def with_scopes(self, scopes: List[str]) -> 'Authorizer':\n        raise NotImplementedError(\"with_scopes\")\n\n\nclass CognitoUserPoolAuthorizer(Authorizer):\n\n    _AUTH_TYPE: str = 'cognito_user_pools'\n\n    def __init__(self, name: str, provider_arns: List[str],\n                 header: Optional[str] = 'Authorization',\n                 scopes: Optional[List] = None) -> None:\n        self.name = name\n        self._header = header\n        if not isinstance(provider_arns, list):\n            # This class is used directly by users so we're\n            # adding some validation to help them troubleshoot\n            # potential issues.\n            raise TypeError(\n                \"provider_arns should be a list of ARNs, received: %s\"\n                % provider_arns)\n        self._provider_arns = provider_arns\n        self.scopes = scopes or []\n\n    def to_swagger(self) -> Dict[str, Any]:\n        return {\n            'in': 'header',\n            'type': 'apiKey',\n            'name': self._header,\n            'x-amazon-apigateway-authtype': self._AUTH_TYPE,\n            'x-amazon-apigateway-authorizer': {\n                'type': self._AUTH_TYPE,\n                'providerARNs': self._provider_arns,\n            }\n        }\n\n    def with_scopes(self, scopes: List[str]) -> 'Authorizer':\n        authorizer_with_scopes = copy.deepcopy(self)\n        authorizer_with_scopes.scopes = scopes\n        return authorizer_with_scopes\n\n\nclass CustomAuthorizer(Authorizer):\n\n    _AUTH_TYPE = 'custom'\n\n    def __init__(self, name: str, authorizer_uri: str, ttl_seconds: int = 300,\n                 header: str = 'Authorization',\n                 invoke_role_arn: Optional[str] = None,\n                 scopes: Optional[List[str]] = None) -> None:\n        self.name = name\n        self._header = header\n        self._authorizer_uri = authorizer_uri\n        self._ttl_seconds = ttl_seconds\n        self._invoke_role_arn = invoke_role_arn\n        self.scopes = scopes or []\n\n    def to_swagger(self) -> Dict[str, Any]:\n        swagger: Dict[str, Any] = {\n            'in': 'header',\n            'type': 'apiKey',\n            'name': self._header,\n            'x-amazon-apigateway-authtype': self._AUTH_TYPE,\n            'x-amazon-apigateway-authorizer': {\n                'type': 'token',\n                'authorizerUri': self._authorizer_uri,\n                'authorizerResultTtlInSeconds': self._ttl_seconds,\n            }\n        }\n        if self._invoke_role_arn is not None:\n            swagger['x-amazon-apigateway-authorizer'][\n                'authorizerCredentials'] = self._invoke_role_arn\n        return swagger\n\n    def with_scopes(self, scopes: List[str]) -> 'Authorizer':\n        authorizer_with_scopes = copy.deepcopy(self)\n        authorizer_with_scopes.scopes = scopes\n        return authorizer_with_scopes\n\n\nclass CORSConfig(object):\n    \"\"\"A cors configuration to attach to a route.\"\"\"\n\n    _REQUIRED_HEADERS: List[str] = ['Content-Type', 'X-Amz-Date',\n                                    'Authorization', 'X-Api-Key',\n                                    'X-Amz-Security-Token']\n\n    def __init__(self, allow_origin: str = '*',\n                 allow_headers: Optional[Sequence[str]] = None,\n                 expose_headers: Optional[Sequence[str]] = None,\n                 max_age: Optional[int] = None,\n                 allow_credentials: Optional[bool] = None):\n        self.allow_origin = allow_origin\n\n        if allow_headers is None:\n            self._allow_headers = set(self._REQUIRED_HEADERS)\n        else:\n            self._allow_headers = set(\n                list(allow_headers) + self._REQUIRED_HEADERS\n            )\n\n        if expose_headers is None:\n            expose_headers = []\n        self._expose_headers = expose_headers\n\n        self._max_age = max_age\n        self._allow_credentials = allow_credentials\n\n    @property\n    def allow_headers(self) -> str:\n        return ','.join(sorted(self._allow_headers))\n\n    def get_access_control_headers(self) -> Dict[str, str]:\n        headers = {\n            'Access-Control-Allow-Origin': self.allow_origin,\n            'Access-Control-Allow-Headers': self.allow_headers\n        }\n        if self._expose_headers:\n            headers.update({\n                'Access-Control-Expose-Headers': ','.join(self._expose_headers)\n            })\n        if self._max_age is not None:\n            headers.update({\n                'Access-Control-Max-Age': str(self._max_age)\n            })\n        if self._allow_credentials is True:\n            headers.update({\n                'Access-Control-Allow-Credentials': 'true'\n            })\n\n        return headers\n\n    def __eq__(self, other: object) -> bool:\n        if isinstance(other, self.__class__):\n            return self.get_access_control_headers() == \\\n                other.get_access_control_headers()\n        return False\n\n\nclass Request(object):\n    \"\"\"The current request from API gateway.\"\"\"\n    _NON_SERIALIZED_ATTRS: List[str] = ['lambda_context']\n    body: Any\n    base64_body: str\n\n    def __init__(self, event_dict: Dict[str, Any],\n                 lambda_context: Optional[Any] = None) -> None:\n        query_params = event_dict['multiValueQueryStringParameters']\n        self.query_params: Optional[MultiDict] = None \\\n            if query_params is None else MultiDict(query_params)\n        self.headers: CaseInsensitiveMapping = \\\n            CaseInsensitiveMapping(event_dict['headers'])\n        self.uri_params: Optional[Dict[str, str]] \\\n            = event_dict['pathParameters']\n        self.method: str = event_dict['requestContext']['httpMethod']\n        self._is_base64_encoded = event_dict.get('isBase64Encoded', False)\n        self._body: Any = event_dict['body']\n        #: The parsed JSON from the body.  This value should\n        #: only be set if the Content-Type header is application/json,\n        #: which is the default content type value in chalice.\n        self._json_body: Optional[Any] = None\n        self._raw_body = b''\n        self.context: Dict[str, Any] = event_dict['requestContext']\n        self.stage_vars: Optional[Dict[str, str]] \\\n            = event_dict['stageVariables']\n        self.path: str = event_dict['requestContext']['resourcePath']\n        self.lambda_context = lambda_context\n        self._event_dict = event_dict\n\n    def _base64decode(self, encoded: Union[bytes, str]) -> bytes:\n        if not isinstance(encoded, bytes):\n            encoded = encoded.encode('ascii')\n        output = base64.b64decode(encoded)\n        return output\n\n    @property\n    def raw_body(self) -> Union[str, bytes]:\n        if not self._raw_body and self._body is not None:\n            if self._is_base64_encoded:\n                self._raw_body = self._base64decode(self._body)\n            elif not isinstance(self._body, bytes):\n                self._raw_body = self._body.encode('utf-8')\n            else:\n                self._raw_body = self._body\n        return self._raw_body\n\n    @property\n    def json_body(self) -> Any:\n        if self.headers.get('content-type', '').startswith('application/json'):\n            if self._json_body is None:\n                try:\n                    self._json_body = json.loads(self.raw_body)\n                except ValueError:\n                    raise BadRequestError('Error Parsing JSON')\n            return self._json_body\n\n    def to_dict(self) -> Dict[Any, Any]:\n        # Don't copy internal attributes.\n        copied = {\n            k: v for k, v in self.__dict__.items()\n            if not k.startswith('_') and\n            k not in self._NON_SERIALIZED_ATTRS\n        }\n        # We want the output of `to_dict()` to be\n        # JSON serializable, so we need to remove the CaseInsensitive dict.\n        copied['headers'] = dict(copied['headers'])\n        if copied['query_params'] is not None:\n            copied['query_params'] = dict(copied['query_params'])\n        return copied\n\n    def to_original_event(self) -> Dict[str, Any]:\n        # To bring consistency with the BaseLambdaEvents, every\n        # input event should have access to the original event\n        # dictionary as an escape hatch to the underlying data\n        # in case something gets added and we haven't mapped it yet.\n        # We unfortunately already have a `to_dict()` method which is\n        # what other events use so we have to use a different method name.\n        return self._event_dict\n\n\nclass Response(object):\n\n    def __init__(\n            self, body: Any,\n            headers: Optional[HeadersType] = None,\n            status_code: int = 200\n    ):\n        self.body: Any = body\n        if headers is None:\n            headers = {}\n        self.headers: HeadersType = headers\n        self.status_code = status_code\n\n    def to_dict(\n            self,\n            binary_types: Optional[List[str]] = None\n    ) -> Dict[str, Any]:\n        body = self.body\n        if not isinstance(body, _ANY_STRING):\n            body = json.dumps(body, separators=(',', ':'),\n                              default=handle_extra_types)\n        single_headers, multi_headers = self._sort_headers(self.headers)\n        response = {\n            'headers': single_headers,\n            'multiValueHeaders': multi_headers,\n            'statusCode': self.status_code,\n            'body': body\n        }\n        if binary_types is not None:\n            self._b64encode_body_if_needed(response, binary_types)\n        return response\n\n    def _sort_headers(\n            self, all_headers: HeadersType\n    ) -> Tuple[Dict[str, Any], Dict[str, List]]:\n        multi_headers: Dict[str, List] = {}\n        single_headers: Dict[str, Any] = {}\n        for name, value in all_headers.items():\n            if isinstance(value, list):\n                multi_headers[name] = value\n            else:\n                single_headers[name] = value\n        return single_headers, multi_headers\n\n    def _b64encode_body_if_needed(\n            self,\n            response_dict: Dict[str, Any],\n            binary_types: List[str]\n    ) -> None:\n        response_headers = CaseInsensitiveMapping(response_dict['headers'])\n        content_type = response_headers.get('content-type', '')\n        body = response_dict['body']\n\n        if _matches_content_type(content_type, binary_types):\n            if _matches_content_type(content_type, ['application/json']) or \\\n                    not content_type:\n                # There's a special case when a user configures\n                # ``application/json`` as a binary type.  The default\n                # json serialization results in a string type, but for binary\n                # content types we need a type bytes().  So we need to special\n                # case this scenario and encode the JSON body to bytes().\n                #\n                # If a user does not provide a content type header, which can\n                # happen if they return a python type instead of a ``Response``\n                # type, then we assume the content is application/json.\n                body = body if isinstance(body, bytes) \\\n                    else body.encode('utf-8')\n            body = self._base64encode(body)\n            response_dict['isBase64Encoded'] = True\n        response_dict['body'] = body\n\n    def _base64encode(self, data: bytes) -> str:\n        if not isinstance(data, bytes):\n            raise ValueError('Expected bytes type for body with binary '\n                             'Content-Type. Got %s type body instead.'\n                             % type(data))\n        data = base64.b64encode(data)\n        return data.decode('ascii')\n\n\nclass RouteEntry(object):\n\n    def __init__(self, view_function: Callable[..., Any], view_name: str,\n                 path: str, method: str,\n                 api_key_required: Optional[bool] = None,\n                 content_types: Optional[List[str]] = None,\n                 cors: Optional[Union[bool, CORSConfig]] = False,\n                 authorizer: Optional[Authorizer] = None):\n        self.view_function: Callable[..., Any] = view_function\n        self.view_name: str = view_name\n        self.uri_pattern: str = path\n        self.method: str = method\n        self.api_key_required: Optional[bool] = api_key_required\n        #: A list of names to extract from path:\n        #: e.g, '/foo/{bar}/{baz}/qux -> ['bar', 'baz']\n        self.view_args: List[str] = self._parse_view_args()\n        self.content_types: List[str] = content_types or []\n        # cors is passed as either a boolean or a CORSConfig object. If it is a\n        # boolean it needs to be replaced with a real CORSConfig object to\n        # pass the typechecker. None in this context will not inject any cors\n        # headers, otherwise the CORSConfig object will determine which\n        # headers are injected.\n        if cors is True:\n            cors = CORSConfig()\n        elif cors is False:\n            cors = None\n        self.cors: CORSConfig = cors  # type: ignore\n        self.authorizer: Optional[Authorizer] = authorizer\n\n    def _parse_view_args(self) -> List[str]:\n        if '{' not in self.uri_pattern:\n            return []\n        # The [1:-1] slice is to remove the braces\n        # e.g {foobar} -> foobar\n        results = [r[1:-1] for r in _PARAMS.findall(self.uri_pattern)]\n        return results\n\n    def __eq__(self, other: object) -> bool:\n        return self.__dict__ == other.__dict__\n\n\nclass APIGateway(object):\n\n    _DEFAULT_BINARY_TYPES = [\n        'application/octet-stream', 'application/x-tar', 'application/zip',\n        'audio/basic', 'audio/ogg', 'audio/mp4', 'audio/mpeg', 'audio/wav',\n        'audio/webm', 'image/png', 'image/jpg', 'image/jpeg', 'image/gif',\n        'video/ogg', 'video/mpeg', 'video/webm',\n    ]\n\n    def __init__(self) -> None:\n        self.binary_types: List[str] = self.default_binary_types\n        self.cors: Union[bool, CORSConfig] = False\n\n    @property\n    def default_binary_types(self) -> List[str]:\n        return list(self._DEFAULT_BINARY_TYPES)\n\n\nclass WebsocketAPI(object):\n    _WEBSOCKET_ENDPOINT_TEMPLATE = 'https://{domain_name}/{stage}'\n    _REGION_ENV_VARS = ['AWS_REGION', 'AWS_DEFAULT_REGION']\n\n    def __init__(self, env: Optional[MutableMapping] = None) -> None:\n        self.session: Optional[Any] = None\n        self._endpoint: Optional[str] = None\n        self._client = None\n        if env is None:\n            self._env: MutableMapping = os.environ\n        else:\n            self._env = env\n\n    def configure(self, domain_name: str, stage: str) -> None:\n        if self._endpoint is not None:\n            return\n        self._endpoint = self._WEBSOCKET_ENDPOINT_TEMPLATE.format(\n            domain_name=domain_name,\n            stage=stage,\n        )\n\n    def configure_from_api_id(self, api_id: str, stage: str) -> None:\n        if self._endpoint is not None:\n            return\n        region_name = self._get_region()\n\n        if region_name.startswith(\"cn-\"):\n            domain_name_template = (\n                '{api_id}.execute-api.{region}.amazonaws.com.cn'\n            )\n        else:\n            domain_name_template = (\n                '{api_id}.execute-api.{region}.amazonaws.com'\n            )\n\n        domain_name = domain_name_template.format(\n            api_id=api_id, region=region_name)\n        self.configure(domain_name, stage)\n\n    def _get_region(self) -> str:\n        # Attempt to get the region so we can configure the\n        # apigatewaymanagementapi client.  We'll first try\n        # retrieving this value from env vars because these should\n        # always be set in the Lambda runtime environment.\n        for varname in self._REGION_ENV_VARS:\n            if varname in self._env:\n                return self._env[varname]\n        # As a last attempt we'll try to retrieve the region\n        # from the currently configured region.  If the session\n        # isn't configured or we can't get the region, we have\n        # no choice but to error out.\n        if self.session is not None:\n            region_name = self.session.region_name\n            if region_name is not None:\n                return region_name\n        raise ValueError(\n            \"Unable to retrieve the region name when configuring the \"\n            \"websocket client.  Either set the 'AWS_REGION' environment \"\n            \"variable or assign 'app.websocket_api.session' to a boto3 \"\n            \"session.\"\n        )\n\n    def _get_client(self) -> Any:\n        if self.session is None:\n            raise ValueError(\n                'Assign app.websocket_api.session to a boto3 session before '\n                'using the WebsocketAPI'\n            )\n        if self._endpoint is None:\n            raise ValueError(\n                'WebsocketAPI.configure must be called before using the '\n                'WebsocketAPI'\n            )\n        if self._client is None:\n            self._client = self.session.client(\n                'apigatewaymanagementapi',\n                endpoint_url=self._endpoint,\n            )\n        return self._client\n\n    def send(self, connection_id: str, message: str) -> None:\n        client = self._get_client()\n        try:\n            client.post_to_connection(\n                ConnectionId=connection_id,\n                Data=message,\n            )\n        except client.exceptions.GoneException:\n            raise WebsocketDisconnectedError(connection_id)\n\n    def close(self, connection_id: str) -> None:\n        client = self._get_client()\n        try:\n            client.delete_connection(\n                ConnectionId=connection_id,\n            )\n        except client.exceptions.GoneException:\n            raise WebsocketDisconnectedError(connection_id)\n\n    def info(self, connection_id: str) -> Any:\n        client = self._get_client()\n        try:\n            return client.get_connection(\n                ConnectionId=connection_id,\n            )\n        except client.exceptions.GoneException:\n            raise WebsocketDisconnectedError(connection_id)\n\n\nclass DecoratorAPI(object):\n    websocket_api: Optional[WebsocketAPI] = None\n\n    def middleware(\n            self,\n            event_type: str = 'all'\n    ) -> Callable[[Callable[..., Any]], Any]:\n        def _middleware_wrapper(\n                func: Callable[..., Any]\n        ) -> Callable[..., Any]:\n            self.register_middleware(func, event_type)\n            return func\n        return _middleware_wrapper\n\n    def authorizer(self, ttl_seconds: Optional[int] = None,\n                   execution_role: Optional[str] = None,\n                   name: Optional[str] = None,\n                   header: Optional[str] = 'Authorization'\n                   ) -> Callable[..., Any]:\n        return self._create_registration_function(\n            handler_type='authorizer',\n            name=name,\n            registration_kwargs={\n                'ttl_seconds': ttl_seconds,\n                'execution_role': execution_role,\n                'header': header\n            }\n        )\n\n    def on_s3_event(self, bucket: str, events: Optional[List[str]] = None,\n                    prefix: Optional[str] = None, suffix: Optional[str] = None,\n                    name: Optional[str] = None) -> Callable[..., Any]:\n        return self._create_registration_function(\n            handler_type='on_s3_event',\n            name=name,\n            registration_kwargs={\n                'bucket': bucket, 'events': events,\n                'prefix': prefix, 'suffix': suffix,\n            }\n        )\n\n    def on_sns_message(self, topic: str,\n                       name: Optional[str] = None) -> Callable[..., Any]:\n        return self._create_registration_function(\n            handler_type='on_sns_message',\n            name=name,\n            registration_kwargs={'topic': topic}\n        )\n\n    def on_sqs_message(self, queue: Optional[str] = None, batch_size: int = 1,\n                       name: Optional[str] = None,\n                       queue_arn: Optional[str] = None,\n                       maximum_batching_window_in_seconds: int = 0,\n                       maximum_concurrency: Optional[int] = None,\n                       ) -> Callable[..., Any]:\n        return self._create_registration_function(\n            handler_type='on_sqs_message',\n            name=name,\n            registration_kwargs={\n                'queue': queue,\n                'queue_arn': queue_arn,\n                'batch_size': batch_size,\n                'maximum_batching_window_in_seconds':\n                    maximum_batching_window_in_seconds,\n                'maximum_concurrency': maximum_concurrency,\n            }\n        )\n\n    def on_cw_event(self, event_pattern: Dict[str, Any],\n                    name: Optional[str] = None) -> Callable[..., Any]:\n        return self._create_registration_function(\n            handler_type='on_cw_event',\n            name=name,\n            registration_kwargs={'event_pattern': event_pattern}\n        )\n\n    def schedule(self, expression: Union[str, 'ScheduleExpression'],\n                 name: Optional[str] = None,\n                 description: str = '') -> Callable[..., Any]:\n        return self._create_registration_function(\n            handler_type='schedule',\n            name=name,\n            registration_kwargs={'expression': expression,\n                                 'description': description},\n        )\n\n    def on_kinesis_record(self, stream: str, batch_size: int = 100,\n                          starting_position: str = 'LATEST',\n                          name: Optional[str] = None,\n                          maximum_batching_window_in_seconds: int = 0\n                          ) -> Callable[..., Any]:\n        return self._create_registration_function(\n            handler_type='on_kinesis_record',\n            name=name,\n            registration_kwargs={\n                'stream': stream,\n                'batch_size': batch_size,\n                'starting_position': starting_position,\n                'maximum_batching_window_in_seconds':\n                    maximum_batching_window_in_seconds},\n        )\n\n    def on_dynamodb_record(\n            self, stream_arn: str,\n            batch_size: int = 100,\n            starting_position: str = 'LATEST',\n            name: Optional[str] = None,\n            maximum_batching_window_in_seconds: int = 0\n    ) -> Callable[..., Any]:\n        return self._create_registration_function(\n            handler_type='on_dynamodb_record',\n            name=name,\n            registration_kwargs={\n                'stream_arn': stream_arn,\n                'batch_size': batch_size,\n                'starting_position': starting_position,\n                'maximum_batching_window_in_seconds':\n                    maximum_batching_window_in_seconds},\n        )\n\n    def route(self, path: str, **kwargs: Any) -> Callable[..., Any]:\n        return self._create_registration_function(\n            handler_type='route',\n            name=kwargs.pop('name', None),\n            # This looks a little weird taking kwargs as a key,\n            # but we want to preserve keep the **kwargs signature\n            # in the route decorator.\n            registration_kwargs={'path': path, 'kwargs': kwargs},\n        )\n\n    def lambda_function(self,\n                        name: Optional[str] = None) -> Callable[..., Any]:\n        return self._create_registration_function(\n            handler_type='lambda_function', name=name)\n\n    def on_ws_connect(self,\n                      name: Optional[str] = None) -> Callable[..., Any]:\n        return self._create_registration_function(\n            handler_type='on_ws_connect',\n            name=name,\n            registration_kwargs={'route_key': '$connect'},\n        )\n\n    def on_ws_disconnect(self,\n                         name: Optional[str] = None) -> Callable[..., Any]:\n        return self._create_registration_function(\n            handler_type='on_ws_disconnect',\n            name=name,\n            registration_kwargs={'route_key': '$disconnect'},\n        )\n\n    def on_ws_message(self, name: Optional[str] = None) -> Callable[..., Any]:\n        return self._create_registration_function(\n            handler_type='on_ws_message',\n            name=name,\n            registration_kwargs={'route_key': '$default'},\n        )\n\n    def _create_registration_function(self, handler_type: str,\n                                      name: Optional[str] = None,\n                                      registration_kwargs: Optional[Any] = None\n                                      ) -> Callable[..., Any]:\n        def _register_handler(\n                user_handler: UserHandlerFuncType\n        ) -> Callable[..., Any]:\n            handler_name = name\n            if handler_name is None:\n                handler_name = user_handler.__name__\n            if registration_kwargs is not None:\n                kwargs = registration_kwargs\n            else:\n                kwargs = {}\n            wrapped = self._wrap_handler(handler_type, handler_name,\n                                         user_handler)\n            self._register_handler(handler_type, handler_name,\n                                   user_handler, wrapped, kwargs)\n            return wrapped\n        return _register_handler\n\n    def _wrap_handler(self, handler_type: str,\n                      handler_name: str,\n                      user_handler: UserHandlerFuncType\n                      ) -> UserHandlerFuncType:\n        if handler_type in _EVENT_CLASSES:\n            if handler_type == 'lambda_function':\n                # We have to wrap existing @app.lambda_function()\n                # handlers for backwards compat reasons so we can\n                # preserve the `def handler(event, context): ...`\n                # interface.  However we need a consistent interface\n                # for middleware so we have to wrap the event\n                # here.\n                user_handler = PureLambdaWrapper(user_handler)\n            return EventSourceHandler(\n                user_handler, _EVENT_CLASSES[handler_type],\n                middleware_handlers=self._get_middleware_handlers(\n                    event_type=_MIDDLEWARE_MAPPING[handler_type],\n                )\n            )\n\n        websocket_event_classes = [\n            'on_ws_connect',\n            'on_ws_message',\n            'on_ws_disconnect',\n        ]\n        if self.websocket_api and handler_type in websocket_event_classes:\n            return WebsocketEventSourceHandler(\n                user_handler, WebsocketEvent,\n                self.websocket_api,\n                middleware_handlers=self._get_middleware_handlers(\n                    event_type='websocket')\n            )\n        if handler_type == 'authorizer':\n            # Authorizer is special cased and doesn't quite fit the\n            # EventSourceHandler pattern.\n            return ChaliceAuthorizer(handler_name, user_handler)\n        return user_handler\n\n    def _get_middleware_handlers(self, event_type: str) -> List:\n        raise NotImplementedError(\"_get_middleware_handlers\")\n\n    def _register_handler(self, handler_type: str, name: str,\n                          user_handler: UserHandlerFuncType,\n                          wrapped_handler: Callable[..., Any],\n                          kwargs: Dict[str, Any],\n                          options: Optional[Dict[Any, Any]] = None) -> None:\n        raise NotImplementedError(\"_register_handler\")\n\n    def register_middleware(self, func: MiddlewareFuncType,\n                            event_type: str = 'all') -> None:\n        raise NotImplementedError(\"register_middleware\")\n\n\nclass _HandlerRegistration(object):\n\n    def __init__(self) -> None:\n        self.routes: Dict[str, Dict[str, RouteEntry]] = defaultdict(dict)\n        self.websocket_handlers: Dict[str, Any] = {}\n        self.builtin_auth_handlers: List['BuiltinAuthConfig'] = []\n        self.event_sources: List['BaseEventSourceConfig'] = []\n        self.pure_lambda_functions: List['LambdaFunction'] = []\n        self.api: APIGateway = APIGateway()\n        self.handler_map: Dict[str, Callable[..., Any]] = {}\n        self.middleware_handlers: List[Tuple[MiddlewareFuncType, str]] = []\n\n    def register_middleware(self, func: MiddlewareFuncType,\n                            event_type: str = 'all') -> None:\n        self.middleware_handlers.append((func, event_type))\n\n    def _do_register_handler(self, handler_type: str, name: str,\n                             user_handler: UserHandlerFuncType,\n                             wrapped_handler: Callable[..., Any], kwargs: Any,\n                             options: Optional[Dict[Any, Any]] = None) -> None:\n        module_name = 'app'\n        if options is not None:\n            name_prefix = options.get('name_prefix')\n            if name_prefix is not None:\n                name = name_prefix + name\n            url_prefix = options.get('url_prefix')\n            if url_prefix is not None and handler_type == 'route':\n                # Move url_prefix into kwargs so only the\n                # route() handler gets a url_prefix kwarg.\n                kwargs['url_prefix'] = url_prefix\n            # module_name is always provided if options is not None.\n            module_name = options['module_name']\n        handler_string = '%s.%s' % (module_name, user_handler.__name__)\n        getattr(self, '_register_%s' % handler_type)(\n            name=name,\n            user_handler=user_handler,\n            handler_string=handler_string,\n            wrapped_handler=wrapped_handler,\n            kwargs=kwargs,\n        )\n        self.handler_map[name] = wrapped_handler\n\n    def _attach_websocket_handler(self, handler: Union[\n        'WebsocketConnectConfig',\n        'WebsocketMessageConfig',\n        'WebsocketDisconnectConfig'\n    ]) -> None:\n        route_key = handler.route_key_handled\n        decorator_name = {\n            '$default': 'on_ws_message',\n            '$connect': 'on_ws_connect',\n            '$disconnect': 'on_ws_disconnect',\n        }.get(route_key)\n        if route_key in self.websocket_handlers:\n            raise ValueError(\n                \"Duplicate websocket handler: '%s'. There can only be one \"\n                \"handler for each websocket decorator.\" % decorator_name\n            )\n        self.websocket_handlers[route_key] = handler\n\n    def _register_on_ws_connect(self, name: str,\n                                user_handler: UserHandlerFuncType,\n                                handler_string: str,\n                                kwargs: Any, **unused: Dict[str, Any]) -> None:\n        wrapper = WebsocketConnectConfig(\n            name=name,\n            handler_string=handler_string,\n            user_handler=user_handler,\n        )\n        self._attach_websocket_handler(wrapper)\n\n    def _register_on_ws_message(self, name: str,\n                                user_handler: UserHandlerFuncType,\n                                handler_string: str,\n                                kwargs: Any, **unused: Dict[str, Any]) -> None:\n        route_key = kwargs['route_key']\n        wrapper = WebsocketMessageConfig(\n            name=name,\n            route_key_handled=route_key,\n            handler_string=handler_string,\n            user_handler=user_handler,\n        )\n        self._attach_websocket_handler(wrapper)\n        self.websocket_handlers[route_key] = wrapper\n\n    def _register_on_ws_disconnect(self, name: str,\n                                   user_handler: UserHandlerFuncType,\n                                   handler_string: str, kwargs: Any,\n                                   **unused: Dict[str, Any]) -> None:\n        wrapper = WebsocketDisconnectConfig(\n            name=name,\n            handler_string=handler_string,\n            user_handler=user_handler,\n        )\n        self._attach_websocket_handler(wrapper)\n\n    def _register_lambda_function(self, name: str,\n                                  user_handler: UserHandlerFuncType,\n                                  handler_string: str,\n                                  **unused: Dict[str, Any]) -> None:\n        wrapper = LambdaFunction(\n            func=user_handler, name=name,\n            handler_string=handler_string,\n        )\n        self.pure_lambda_functions.append(wrapper)\n\n    def _register_on_s3_event(self, name: str,\n                              handler_string: str,\n                              kwargs: Any, **unused: Dict[str, Any]\n                              ) -> None:\n        events = kwargs['events']\n        if events is None:\n            events = ['s3:ObjectCreated:*']\n        s3_event = S3EventConfig(\n            name=name,\n            bucket=kwargs['bucket'],\n            events=events,\n            prefix=kwargs['prefix'],\n            suffix=kwargs['suffix'],\n            handler_string=handler_string,\n        )\n        self.event_sources.append(s3_event)\n\n    def _register_on_sns_message(self, name: str,\n                                 handler_string: str,\n                                 kwargs: Any,\n                                 **unused: Dict[str, Any]\n                                 ) -> None:\n        sns_config = SNSEventConfig(\n            name=name,\n            handler_string=handler_string,\n            topic=kwargs['topic'],\n        )\n        self.event_sources.append(sns_config)\n\n    def _register_on_sqs_message(self, name: str,\n                                 handler_string: str,\n                                 kwargs: Any,\n                                 **unused: Dict[str, Any]\n                                 ) -> None:\n        queue = kwargs.get('queue')\n        queue_arn = kwargs.get('queue_arn')\n        if not queue and not queue_arn:\n            raise ValueError(\n                \"Must provide either `queue` or `queue_arn` to the \"\n                \"`on_sqs_message` decorator.\"\n            )\n        sqs_config = SQSEventConfig(\n            name=name,\n            handler_string=handler_string,\n            queue=queue,\n            queue_arn=queue_arn,\n            batch_size=kwargs['batch_size'],\n            maximum_batching_window_in_seconds=kwargs[\n                'maximum_batching_window_in_seconds'],\n            maximum_concurrency=kwargs[\n                'maximum_concurrency'],\n        )\n        self.event_sources.append(sqs_config)\n\n    def _register_on_kinesis_record(self,\n                                    name: str,\n                                    handler_string: str,\n                                    kwargs: Any,\n                                    **unused: Dict[str, Any]\n                                    ) -> None:\n        kinesis_config = KinesisEventConfig(\n            name=name,\n            handler_string=handler_string,\n            stream=kwargs['stream'],\n            batch_size=kwargs['batch_size'],\n            starting_position=kwargs['starting_position'],\n            maximum_batching_window_in_seconds=kwargs[\n                'maximum_batching_window_in_seconds'],\n        )\n        self.event_sources.append(kinesis_config)\n\n    def _register_on_dynamodb_record(self, name: str,\n                                     handler_string: str,\n                                     kwargs: Any,\n                                     **unused: Dict[str, Any]) -> None:\n        ddb_config = DynamoDBEventConfig(\n            name=name,\n            handler_string=handler_string,\n            stream_arn=kwargs['stream_arn'],\n            batch_size=kwargs['batch_size'],\n            starting_position=kwargs['starting_position'],\n            maximum_batching_window_in_seconds=kwargs[\n                'maximum_batching_window_in_seconds'],\n        )\n        self.event_sources.append(ddb_config)\n\n    def _register_on_cw_event(self, name: str, handler_string: str,\n                              kwargs: Any, **unused: Dict[str, Any]) -> None:\n        event_source = CloudWatchEventConfig(\n            name=name,\n            event_pattern=kwargs['event_pattern'],\n            handler_string=handler_string\n        )\n        self.event_sources.append(event_source)\n\n    def _register_schedule(self, name: str, handler_string: str,\n                           kwargs: Any, **unused: Dict[str, Any]) -> None:\n        event_source = ScheduledEventConfig(\n            name=name,\n            schedule_expression=kwargs['expression'],\n            description=kwargs[\"description\"],\n            handler_string=handler_string,\n        )\n        self.event_sources.append(event_source)\n\n    def _register_authorizer(self, name: str, handler_string: str,\n                             wrapped_handler: 'ChaliceAuthorizer',\n                             kwargs: Any, **unused: Dict[str, Any]) -> None:\n        actual_kwargs = kwargs.copy()\n        ttl_seconds = actual_kwargs.pop('ttl_seconds', None)\n        execution_role = actual_kwargs.pop('execution_role', None)\n        header = actual_kwargs.pop('header', None)\n        if actual_kwargs:\n            raise TypeError(\n                'TypeError: authorizer() got unexpected keyword '\n                'arguments: %s' % ', '.join(list(actual_kwargs)))\n        auth_config = BuiltinAuthConfig(\n            name=name,\n            handler_string=handler_string,\n            ttl_seconds=ttl_seconds,\n            execution_role=execution_role,\n            header=header,\n        )\n        wrapped_handler.config = auth_config\n        self.builtin_auth_handlers.append(auth_config)\n\n    def _register_route(self, name: str, user_handler: UserHandlerFuncType,\n                        kwargs: Any, **unused: Dict[str, Any]) -> None:\n        actual_kwargs = kwargs['kwargs']\n        path = kwargs['path']\n        url_prefix = kwargs.pop('url_prefix', None)\n        if url_prefix is not None:\n            path = '/'.join([url_prefix.rstrip('/'),\n                             path.strip('/')]).rstrip('/')\n        methods = actual_kwargs.pop('methods', ['GET'])\n        route_kwargs = {\n            'authorizer': actual_kwargs.pop('authorizer', None),\n            'api_key_required': actual_kwargs.pop('api_key_required', None),\n            'content_types': actual_kwargs.pop('content_types',\n                                               ['application/json']),\n            'cors': actual_kwargs.pop('cors', self.api.cors),\n        }\n        if route_kwargs['cors'] is None:\n            route_kwargs['cors'] = self.api.cors\n        if not isinstance(route_kwargs['content_types'], list):\n            raise ValueError(\n                'In view function \"%s\", the content_types '\n                'value must be a list, not %s: %s' % (\n                    name, type(route_kwargs['content_types']),\n                    route_kwargs['content_types']))\n        if actual_kwargs:\n            raise TypeError('TypeError: route() got unexpected keyword '\n                            'arguments: %s' % ', '.join(list(actual_kwargs)))\n        for method in methods:\n            if method in self.routes[path]:\n                raise ValueError(\n                    \"Duplicate method: '%s' detected for route: '%s'\\n\"\n                    \"between view functions: \\\"%s\\\" and \\\"%s\\\". A specific \"\n                    \"method may only be specified once for \"\n                    \"a particular path.\" % (\n                        method, path, self.routes[path][method].view_name,\n                        name)\n                )\n            entry = RouteEntry(user_handler, name, path, method,\n                               **route_kwargs)\n            self.routes[path][method] = entry\n\n\nclass Chalice(_HandlerRegistration, DecoratorAPI):\n    FORMAT_STRING = '%(name)s - %(levelname)s - %(message)s'\n    authorizers: Dict[str, Dict[str, Any]]\n    lambda_context: 'LambdaContext'\n    current_request: Optional[Request]\n\n    def __init__(self, app_name: str, debug: bool = False,\n                 configure_logs: bool = True,\n                 env: Optional[MutableMapping] = None) -> None:\n        super(Chalice, self).__init__()\n        self.app_name: str = app_name\n        self.websocket_api: WebsocketAPI = WebsocketAPI()\n        self._debug: bool = debug\n        self.configure_logs: bool = configure_logs\n        self.log: logging.Logger = logging.getLogger(self.app_name)\n        if env is None:\n            env = os.environ\n        self._initialize(env)\n        self.experimental_feature_flags: Set[str] = set()\n        # This is marked as internal but is intended to be used by\n        # any code within Chalice.\n        self._features_used: Set[str] = set()\n\n    def _initialize(self, env: MutableMapping) -> None:\n        if self.configure_logs:\n            self._configure_logging()\n        env['AWS_EXECUTION_ENV'] = '%s aws-chalice/%s' % (\n            env.get('AWS_EXECUTION_ENV', 'AWS_Lambda'),\n            __version__,\n        )\n\n    @property\n    def debug(self) -> bool:\n        return self._debug\n\n    @debug.setter\n    def debug(self, value: bool) -> None:\n        self._debug = value\n        self._configure_log_level()\n\n    def _configure_logging(self) -> None:\n        if self._already_configured(self.log):\n            return\n        handler = logging.StreamHandler(sys.stdout)\n        # Timestamp is handled by lambda itself so the\n        # default FORMAT_STRING doesn't need to include it.\n        formatter = logging.Formatter(self.FORMAT_STRING)\n        handler.setFormatter(formatter)\n        self.log.propagate = False\n        self._configure_log_level()\n        self.log.addHandler(handler)\n\n    def _already_configured(self, log: logging.Logger) -> bool:\n        if not log.handlers:\n            return False\n        for handler in log.handlers:\n            if isinstance(handler, logging.StreamHandler):\n                if handler.stream == sys.stdout:\n                    return True\n        return False\n\n    def _configure_log_level(self) -> None:\n        if self._debug:\n            level = logging.DEBUG\n        else:\n            level = logging.ERROR\n        self.log.setLevel(level)\n\n    def register_blueprint(self, blueprint: 'Blueprint',\n                           name_prefix: Optional[str] = None,\n                           url_prefix: Optional[str] = None) -> None:\n        blueprint.register(self, options={'name_prefix': name_prefix,\n                                          'url_prefix': url_prefix})\n\n    def _register_handler(self, handler_type: str, name: str,\n                          user_handler: UserHandlerFuncType,\n                          wrapped_handler: Callable[..., Any],\n                          kwargs: Any, options: Optional[Dict[Any, Any]] = None\n                          ) -> None:\n        self._do_register_handler(handler_type, name, user_handler,\n                                  wrapped_handler, kwargs, options)\n\n    # These are defined here on the Chalice class because we want all the\n    # feature flag tracking to live in Chalice and not the DecoratorAPI.\n    def _register_on_ws_connect(self, name: str,\n                                user_handler: UserHandlerFuncType,\n                                handler_string: str,\n                                kwargs: Any, **unused: Dict[str, Any]) -> None:\n        self._features_used.add('WEBSOCKETS')\n        super(Chalice, self)._register_on_ws_connect(\n            name, user_handler, handler_string, kwargs, **unused)\n\n    def _register_on_ws_message(self, name: str,\n                                user_handler: UserHandlerFuncType,\n                                handler_string: str,\n                                kwargs: Any, **unused: Dict[str, Any]) -> None:\n        self._features_used.add('WEBSOCKETS')\n        super(Chalice, self)._register_on_ws_message(\n            name, user_handler, handler_string, kwargs, **unused)\n\n    def _register_on_ws_disconnect(self, name: str,\n                                   user_handler: UserHandlerFuncType,\n                                   handler_string: str, kwargs: Any,\n                                   **unused: Dict[str, Any]) -> None:\n        self._features_used.add('WEBSOCKETS')\n        super(Chalice, self)._register_on_ws_disconnect(\n            name, user_handler, handler_string, kwargs, **unused)\n\n    def _get_middleware_handlers(self, event_type: str) -> Any:\n        # We're returning a generator here because we want to defer the\n        # collection of all middleware until as last as possible (when\n        # then handler is actually invoked).  This lets us pick up any\n        # middleware that's registered after a handler has been defined,\n        # which is the behavior you'd expect.\n        return (func for func, filter_type in self.middleware_handlers if\n                filter_type in [event_type, 'all'])\n\n    def __call__(self, event: Any, context: Any) -> Dict[str, Any]:\n        # For legacy reasons, we can't move the Rest API handler entry\n        # point away from this Chalice.__call__ method . However, we can\n        # try to extract as much as logic as possible to a separate handler\n        # class we can call.  That way it's still structured somewhat similar\n        # to the other event handlers which makes it more manageable to\n        # implement shared functionality (e.g. middleware).\n        self.lambda_context: 'LambdaContext' = context\n        handler = RestAPIEventHandler(\n            self.routes, self.api, self.log, self.debug,\n            middleware_handlers=self._get_middleware_handlers('http'),\n        )\n        self.current_request: \\\n            Optional[Request] = handler.create_request_object(event, context)\n        return handler(event, context)\n\n\nclass BuiltinAuthConfig(object):\n    def __init__(self, name: str, handler_string: str,\n                 ttl_seconds: Optional[int] = None,\n                 execution_role: Optional[str] = None,\n                 header: str = 'Authorization'):\n        # We'd also support all the misc config options you can set.\n        self.name: str = name\n        self.handler_string: str = handler_string\n        self.ttl_seconds: Optional[int] = ttl_seconds\n        self.execution_role: Optional[str] = execution_role\n        self.header: str = header\n\n\n# ChaliceAuthorizer is unique in that the runtime component (the thing\n# that wraps the decorated function) also needs a reference to the config\n# object (the object the describes how to create the resource).  In\n# most event sources these are separate and don't need to know about\n# each other, but ChaliceAuthorizer does.  This is because the way\n# you associate a builtin authorizer with a view function is by passing\n# a direct reference:\n#\n# @app.authorizer(...)\n# def my_auth_function(...): pass\n#\n# @app.route('/', auth=my_auth_function)\n#\n# The 'route' part needs to know about the auth function for two reasons:\n#\n# 1. We use ``view.authorizer`` to figure out how to deploy the app\n# 2. We need a reference to the runtime handler for the auth in order\n#    to support local mode testing.\n# I *think* we can refactor things to handle both of those issues but\n# we would need more research to know for sure.  For now, this is a\n# special cased runtime class that knows about its config.\nclass ChaliceAuthorizer(object):\n    def __init__(self, name: str, func: Callable[..., Any],\n                 scopes: Optional[List[str]] = None) -> None:\n        self.name: str = name\n        self.func: Callable[\n            ['AuthRequest'], Union['AuthResponse', Dict[str, Any]]\n        ] = func\n        self.scopes: List[str] = scopes or []\n        # This is filled in during the @app.authorizer()\n        # processing.\n        self.config: BuiltinAuthConfig = None  # type: ignore\n\n    def __call__(\n            self,\n            event: Dict[str, Any],\n            context: Dict[str, Any]\n    ) -> Dict[str, Any]:\n        auth_request = self._transform_event(event)\n        result = self.func(auth_request)\n        if isinstance(result, AuthResponse):\n            return result.to_dict(auth_request)\n        return result\n\n    def _transform_event(self, event: Dict[str, Any]) -> 'AuthRequest':\n        return AuthRequest(event['type'],\n                           event['authorizationToken'],\n                           event['methodArn'])\n\n    def with_scopes(self, scopes: List[str]) -> 'ChaliceAuthorizer':\n        authorizer_with_scopes = copy.deepcopy(self)\n        authorizer_with_scopes.scopes = scopes\n        return authorizer_with_scopes\n\n\nclass AuthRequest(object):\n    def __init__(self, auth_type: str, token: str, method_arn: str) -> None:\n        self.auth_type: str = auth_type\n        self.token: str = token\n        self.method_arn: str = method_arn\n\n\nclass AuthResponse(object):\n    ALL_HTTP_METHODS: List[str] = ['DELETE', 'HEAD', 'OPTIONS',\n                                   'PATCH', 'POST', 'PUT', 'GET']\n\n    def __init__(self, routes: List[Union[str, 'AuthRoute']],\n                 principal_id: str, context: Optional[Dict[str, str]] = None):\n        self.routes: List[Union[str, 'AuthRoute']] = routes\n        self.principal_id: str = principal_id\n        # The request is used to generate full qualified ARNs\n        # that we need for the resource portion of the returned\n        # policy.\n        if context is None:\n            context = {}\n        self.context: Dict[str, str] = context\n\n    def to_dict(self, request: AuthRequest) -> Dict[str, Any]:\n        return {\n            'context': self.context,\n            'principalId': self.principal_id,\n            'policyDocument': self._generate_policy(request),\n        }\n\n    def _generate_policy(self, request: AuthRequest) -> Dict[str, Any]:\n        allowed_resources = self._generate_allowed_resources(request)\n        return {\n            'Version': '2012-10-17',\n            'Statement': [\n                {\n                    'Action': 'execute-api:Invoke',\n                    'Effect': 'Allow',\n                    'Resource': allowed_resources,\n                }\n            ]\n        }\n\n    def _generate_allowed_resources(self, request: AuthRequest) -> List[str]:\n        allowed_resources = []\n        for route in self.routes:\n            if isinstance(route, AuthRoute):\n                methods = route.methods\n                path = route.path\n            elif route == '*':\n                # A string route of '*' means that all paths and\n                # all HTTP methods are now allowed.\n                methods = ['*']\n                path = '*'\n            else:\n                # If 'route' is just a string, then they've\n                # opted not to use the AuthRoute(), so we'll\n                # generate a policy that allows all HTTP methods.\n                methods = ['*']\n                path = route\n            for method in methods:\n                allowed_resources.append(\n                    self._generate_arn(path, request, method))\n        return allowed_resources\n\n    def _generate_arn(\n            self,\n            route: str,\n            request: AuthRequest,\n            method: str = '*'\n    ) -> str:\n        incoming_arn = request.method_arn\n        # An incoming_arn would look like this:\n        # \"arn:aws:execute-api:us-west-2:123:rest-api-id/stage/GET/needs/auth\"\n        # Then we pull out the rest-api-id and stage, such that:\n        #   base = ['rest-api-id', 'stage']\n        #\n        # We rely on the fact that the first part of the ARN format is fixed\n        # as:    arn:<partition>:<service>:<region>:<account-id>:<resource>\n        arn_parts = incoming_arn.split(':', 5)\n        allowed_resource = arn_parts[-1].split('/')[:2]\n        # Now we add in the path components and rejoin everything\n        # back together to make a full arn.\n        # We're also assuming all HTTP methods (via '*') for now.\n        # To support per HTTP method routes the API will need to be updated.\n        # We also need to strip off the leading ``/`` so it can be\n        # '/'.join(...)'d properly.\n        allowed_resource.extend([method, route[1:]])\n        last_arn_segment = '/'.join(allowed_resource)\n        if route == '*':\n            # We also have to handle the '*' case which matches\n            # all routes.\n            last_arn_segment += route\n        arn_parts[-1] = last_arn_segment\n        final_arn = ':'.join(arn_parts)\n        return final_arn\n\n\nclass AuthRoute(object):\n    def __init__(self, path: str, methods: List[str]):\n        self.path: str = path\n        self.methods: List[str] = methods\n\n\nclass LambdaFunction(object):\n    def __init__(self, func: Callable[..., Any], name: str,\n                 handler_string: str):\n        self.func: Callable[..., Any] = func\n        self.name: str = name\n        self.handler_string: str = handler_string\n\n    def __call__(self, event: Dict[str, Any],\n                 context: Dict[str, Any]\n                 ) -> Callable[[Dict[str, Any], Dict[str, Any]], Any]:\n        return self.func(event, context)\n\n\nclass BaseEventSourceConfig(object):\n    def __init__(self, name: str, handler_string: str) -> None:\n        self.name: str = name\n        self.handler_string: str = handler_string\n\n\nclass ScheduledEventConfig(BaseEventSourceConfig):\n    def __init__(self, name: str, handler_string: str,\n                 schedule_expression: Union[str, 'ScheduleExpression'],\n                 description: str):\n        super(ScheduledEventConfig, self).__init__(name, handler_string)\n        self.schedule_expression: \\\n            Union[str, 'ScheduleExpression'] = schedule_expression\n        self.description: str = description\n\n\nclass CloudWatchEventConfig(BaseEventSourceConfig):\n\n    def __init__(self, name: str, handler_string: str,\n                 event_pattern: Dict[str, Any]):\n        super(CloudWatchEventConfig, self).__init__(name, handler_string)\n        self.event_pattern: Dict[str, Any] = event_pattern\n\n\nclass ScheduleExpression(object):\n    def to_string(self) -> str:\n        raise NotImplementedError(\"to_string\")\n\n\nclass Rate(ScheduleExpression):\n    MINUTES: str = 'MINUTES'\n    HOURS: str = 'HOURS'\n    DAYS: str = 'DAYS'\n\n    def __init__(self, value: int, unit: str) -> None:\n        self.value: int = value\n        self.unit: str = unit\n\n    def to_string(self) -> str:\n        unit = self.unit.lower()\n        if self.value == 1:\n            # Remove the 's' from the end if it's singular.\n            # This is required by the cloudwatch events API.\n            unit = unit[:-1]\n        return 'rate(%s %s)' % (self.value, unit)\n\n\nclass Cron(ScheduleExpression):\n    def __init__(self, minutes: Union[str, int], hours: Union[str, int],\n                 day_of_month: Union[str, int], month: Union[str, int],\n                 day_of_week: Union[str, int], year: Union[str, int]):\n        self.minutes: Union[str, int] = minutes\n        self.hours: Union[str, int] = hours\n        self.day_of_month: Union[str, int] = day_of_month\n        self.month: Union[str, int] = month\n        self.day_of_week: Union[str, int] = day_of_week\n        self.year: Union[str, int] = year\n\n    def to_string(self) -> str:\n        return 'cron(%s %s %s %s %s %s)' % (\n            self.minutes,\n            self.hours,\n            self.day_of_month,\n            self.month,\n            self.day_of_week,\n            self.year,\n        )\n\n\nclass S3EventConfig(BaseEventSourceConfig):\n    def __init__(self, name: str, bucket: str, events: List[str], prefix: str,\n                 suffix: str, handler_string: str):\n        super(S3EventConfig, self).__init__(name, handler_string)\n        self.bucket: str = bucket\n        self.events: List[str] = events\n        self.prefix: str = prefix\n        self.suffix: str = suffix\n\n\nclass SNSEventConfig(BaseEventSourceConfig):\n\n    def __init__(self, name: str, handler_string: str, topic: str):\n        super(SNSEventConfig, self).__init__(name, handler_string)\n        self.topic: str = topic\n\n\nclass SQSEventConfig(BaseEventSourceConfig):\n    def __init__(self, name: str, handler_string: str, queue: Optional[str],\n                 queue_arn: Optional[str], batch_size: int,\n                 maximum_batching_window_in_seconds: int,\n                 maximum_concurrency: Optional[int]):\n        super(SQSEventConfig, self).__init__(name, handler_string)\n        self.queue: Optional[str] = queue\n        self.queue_arn: Optional[str] = queue_arn\n        self.batch_size: int = batch_size\n        self.maximum_batching_window_in_seconds: int = \\\n            maximum_batching_window_in_seconds\n        self.maximum_concurrency: Optional[int] = maximum_concurrency\n\n\nclass KinesisEventConfig(BaseEventSourceConfig):\n    def __init__(self, name: str, handler_string: str, stream: str,\n                 batch_size: int, starting_position: str,\n                 maximum_batching_window_in_seconds: int) -> None:\n        super(KinesisEventConfig, self).__init__(name, handler_string)\n        self.stream: str = stream\n        self.batch_size: int = batch_size\n        self.starting_position: str = starting_position\n        self.maximum_batching_window_in_seconds: int = \\\n            maximum_batching_window_in_seconds\n\n\nclass DynamoDBEventConfig(BaseEventSourceConfig):\n    def __init__(self, name: str, handler_string: str, stream_arn: str,\n                 batch_size: int, starting_position: str,\n                 maximum_batching_window_in_seconds: int) -> None:\n        super(DynamoDBEventConfig, self).__init__(name, handler_string)\n        self.stream_arn: str = stream_arn\n        self.batch_size: int = batch_size\n        self.starting_position: str = starting_position\n        self.maximum_batching_window_in_seconds: int = \\\n            maximum_batching_window_in_seconds\n\n\nclass WebsocketConnectConfig(BaseEventSourceConfig):\n    CONNECT_ROUTE: str = '$connect'\n\n    def __init__(self, name: str, handler_string: str,\n                 user_handler: UserHandlerFuncType):\n        super(WebsocketConnectConfig, self).__init__(name, handler_string)\n        self.route_key_handled = self.CONNECT_ROUTE\n        self.handler_function = user_handler\n\n\nclass WebsocketMessageConfig(BaseEventSourceConfig):\n    def __init__(self, name: str, route_key_handled: str, handler_string: str,\n                 user_handler: UserHandlerFuncType) -> None:\n        super(WebsocketMessageConfig, self).__init__(name, handler_string)\n        self.route_key_handled: str = route_key_handled\n        self.handler_function: Callable[..., Any] = user_handler\n\n\nclass WebsocketDisconnectConfig(BaseEventSourceConfig):\n    DISCONNECT_ROUTE: str = '$disconnect'\n\n    def __init__(self, name: str, handler_string: str,\n                 user_handler: UserHandlerFuncType):\n        super(WebsocketDisconnectConfig, self).__init__(name, handler_string)\n        self.route_key_handled = self.DISCONNECT_ROUTE\n        self.handler_function = user_handler\n\n\nclass PureLambdaWrapper(object):\n    def __init__(self,\n                 original_func: Callable[\n                     [Dict[str, Any], Optional[Dict[str, Any]]], Any\n                 ]\n                 ):\n        self._original_func = original_func\n\n    def __call__(self, event: 'BaseLambdaEvent') -> Any:\n        # The @app.lambda_function() expects an event dict\n        # and a context argument so this class will is used to adapt\n        # from the Chalice single-arg style function (which is used\n        # in all the event handlers) to the low-level lambda api.\n        return self._original_func(event.to_dict(), event.context)\n\n\nclass MiddlewareHandler(object):\n    def __init__(self, handler: Callable[..., Any],\n                 next_handler: Callable[..., Any]) -> None:\n        self.handler: Callable[..., Any] = handler\n        self.next_handler: Callable[..., Any] = next_handler\n\n    def __call__(self, request: Any) -> Any:\n        return self.handler(request, self.next_handler)\n\n\nclass BaseLambdaHandler(object):\n    def __call__(self, event: Any, context: Any) -> Any:\n        pass\n\n    def _build_middleware_handlers(self, handlers: List[Callable[..., Any]],\n                                   original_handler: Callable[..., Any]\n                                   ) -> Callable[..., Any]:\n        current = original_handler\n        for handler in reversed(list(handlers)):\n            current = MiddlewareHandler(handler=handler, next_handler=current)\n        return current\n\n\nclass EventSourceHandler(BaseLambdaHandler):\n\n    def __init__(\n            self, func: Callable[..., Any], event_class: Any,\n            middleware_handlers: Optional[List[Callable[..., Any]]] = None\n    ) -> None:\n        self.func: Callable[..., Any] = func\n        self.event_class: Any = event_class\n        if middleware_handlers is None:\n            middleware_handlers = []\n        self._middleware_handlers: \\\n            List[Callable[..., Any]] = middleware_handlers\n        self.handler: Optional[Callable[..., Any]] = None\n\n    @property\n    def middleware_handlers(self) -> List[Callable[..., Any]]:\n        return self._middleware_handlers\n\n    @middleware_handlers.setter\n    def middleware_handlers(self, value: List[Callable[..., Any]]) -> None:\n        self._middleware_handlers = value\n\n    def __call__(self, event: Any, context: Any) -> Any:\n        event_obj = self.event_class(event, context)\n        if self.handler is None:\n            # Defer creating handlers so we have all middleware configured.\n            self.handler = self._build_middleware_handlers(\n                self._middleware_handlers, original_handler=self.func)\n        return self.handler(event_obj)\n\n\nclass WebsocketEventSourceHandler(EventSourceHandler):\n    WEBSOCKET_API_RESPONSE = {'statusCode': 200}\n\n    def __init__(self, func: Callable[..., Any],\n                 event_class: Any, websocket_api: WebsocketAPI,\n                 middleware_handlers: Optional[List[Callable[..., Any]]] = None\n                 ) -> None:\n        super(WebsocketEventSourceHandler, self).__init__(func, event_class,\n                                                          middleware_handlers)\n        self.websocket_api: WebsocketAPI = websocket_api\n\n    def __call__(self, event: Dict[str, Any],\n                 context: Dict[str, Any]) -> Dict[str, Any]:\n        self.websocket_api.configure_from_api_id(\n            event['requestContext']['apiId'],\n            event['requestContext']['stage'],\n        )\n        response = super(\n            WebsocketEventSourceHandler, self).__call__(event, context)\n        data = None\n        if isinstance(response, Response):\n            data = response.to_dict()\n        elif isinstance(response, dict):\n            data = response\n            if \"statusCode\" not in data:\n                data = {**self.WEBSOCKET_API_RESPONSE, **data}\n        return data or self.WEBSOCKET_API_RESPONSE\n\n\nclass RestAPIEventHandler(BaseLambdaHandler):\n    def __init__(self, route_table: Dict[str, Dict[str, RouteEntry]],\n                 api: APIGateway, log: logging.Logger, debug: bool,\n                 middleware_handlers: Optional[List[Callable[..., Any]]] = None\n                 ) -> None:\n        self.routes: Dict[str, Dict[str, RouteEntry]] = route_table\n        self.api: APIGateway = api\n        self.log: logging.Logger = log\n        self.debug: bool = debug\n        self.current_request: Optional[Request] = None\n        self.lambda_context: Optional['LambdaContext'] = None\n        if middleware_handlers is None:\n            middleware_handlers = []\n        self._middleware_handlers: \\\n            List[Callable[..., Any]] = middleware_handlers\n\n    def _global_error_handler(self, event: Any,\n                              get_response: Callable[..., Any]) -> Response:\n        try:\n            return get_response(event)\n        except Exception:\n            return self._unhandled_exception_to_response()\n\n    def create_request_object(self, event: Any,\n                              context: Any) -> Optional[Request]:\n        # For legacy reasons, there's some initial validation that takes\n        # place before we convert the input event to a python object.\n        # We don't do this in event handlers we added later, so we *should*\n        # be able to remove this code.  To be safe, we're keeping it in for\n        # now to minimize the potential for breaking changes.\n        resource_path = event.get('requestContext', {}).get('resourcePath')\n        if resource_path is not None:\n            self.current_request = Request(event, context)\n            return self.current_request\n        return None\n\n    def __call__(self, event: Any, context: Any) -> Any:\n        def wrapped_event(request: Request) -> Response:\n            return self._main_rest_api_handler(event, context)\n\n        final_handler = self._build_middleware_handlers(\n            [self._global_error_handler] + list(self._middleware_handlers),\n            original_handler=wrapped_event,\n        )\n        response = final_handler(self.current_request)\n        return response.to_dict(self.api.binary_types)\n\n    def _main_rest_api_handler(self, event: Any, context: Any) -> Response:\n        resource_path = event.get('requestContext', {}).get('resourcePath')\n        if resource_path is None:\n            return error_response(error_code='InternalServerError',\n                                  message='Unknown request.',\n                                  http_status_code=500)\n        http_method = event['requestContext']['httpMethod']\n        if http_method not in self.routes[resource_path]:\n            allowed_methods = ', '.join(self.routes[resource_path].keys())\n            return error_response(\n                error_code='MethodNotAllowedError',\n                message='Unsupported method: %s' % http_method,\n                http_status_code=405,\n                headers={'Allow': allowed_methods})\n        route_entry = self.routes[resource_path][http_method]\n        view_function = route_entry.view_function\n        function_args = {name: event['pathParameters'][name]\n                         for name in route_entry.view_args}\n        self.lambda_context = context\n        # We're getting the CORS headers before validation to be able to\n        # output desired headers with\n        cors_headers = None\n        if self._cors_enabled_for_route(route_entry):\n            cors_headers = self._get_cors_headers(route_entry.cors)\n        # We're doing the header validation after creating the request\n        # so can leverage the case insensitive dict that the Request class\n        # uses for headers.\n        if self.current_request and route_entry.content_types:\n            content_type = self.current_request.headers.get(\n                'content-type', 'application/json')\n            if not _matches_content_type(content_type,\n                                         route_entry.content_types):\n                return error_response(\n                    error_code='UnsupportedMediaType',\n                    message='Unsupported media type: %s' % content_type,\n                    http_status_code=415,\n                    headers=cors_headers\n                )\n        response = self._get_view_function_response(view_function,\n                                                    function_args)\n        if cors_headers is not None:\n            self._add_cors_headers(response, cors_headers)\n\n        response_headers = CaseInsensitiveMapping(response.headers)\n        if self.current_request and not self._validate_binary_response(\n                self.current_request.headers, response_headers):\n            content_type = response_headers.get('content-type', '')\n            return error_response(\n                error_code='BadRequest',\n                message=('Request did not specify an Accept header with %s, '\n                         'The response has a Content-Type of %s. If a '\n                         'response has a binary Content-Type then the request '\n                         'must specify an Accept header that matches.'\n                         % (content_type, content_type)),\n                http_status_code=400,\n                headers=cors_headers\n            )\n        return response\n\n    def _validate_binary_response(self,\n                                  request_headers: CaseInsensitiveMapping,\n                                  response_headers: CaseInsensitiveMapping\n                                  ) -> bool:\n        # Validates that a response is valid given the request. If the response\n        # content-type specifies a binary type, there must be an accept header\n        # that is a binary type as well.\n        request_accept_header = request_headers.get('accept')\n        response_content_type = response_headers.get(\n            'content-type', 'application/json')\n        response_is_binary = _matches_content_type(response_content_type,\n                                                   self.api.binary_types)\n        expects_binary_response = False\n        if request_accept_header is not None:\n            expects_binary_response = _matches_content_type(\n                request_accept_header, self.api.binary_types)\n        if response_is_binary and not expects_binary_response:\n            return False\n        return True\n\n    def _get_view_function_response(self, view_function: Callable[..., Any],\n                                    function_args: Dict[str, Any]) -> Response:\n        try:\n            response = view_function(**function_args)\n            if not isinstance(response, Response):\n                response = Response(body=response)\n            self._validate_response(response)\n        except ChaliceUnhandledError:\n            # Reraise this exception so that middleware has a chance\n            # to handle the exception.\n            raise\n        except ChaliceViewError as e:\n            # Any chalice view error should propagate.  These\n            # get mapped to various HTTP status codes in API Gateway.\n            response = Response(body={'Code': e.__class__.__name__,\n                                      'Message': str(e)},\n                                status_code=e.STATUS_CODE)\n        except Exception:\n            response = self._unhandled_exception_to_response()\n        return response\n\n    def _unhandled_exception_to_response(self) -> Response:\n        headers: HeadersType = {}\n        path = getattr(self.current_request, 'path', 'unknown')\n        self.log.error(\"Caught exception for path %s\", path, exc_info=True)\n        if self.debug:\n            # If the user has turned on debug mode,\n            # we'll let the original exception propagate so\n            # they get more information about what went wrong.\n            stack_trace = ''.join(traceback.format_exc())\n            body: Any = stack_trace\n            headers['Content-Type'] = 'text/plain'\n        else:\n            body = {'Code': 'InternalServerError',\n                    'Message': 'An internal server error occurred.'}\n        response = Response(body=body, headers=headers, status_code=500)\n        return response\n\n    def _validate_response(self, response: Response) -> None:\n        for header, value in response.headers.items():\n            if '\\n' in value:\n                raise ChaliceError(\"Bad value for header '%s': %r\" %\n                                   (header, value))\n\n    def _cors_enabled_for_route(self, route_entry: RouteEntry) -> bool:\n        return route_entry.cors is not None\n\n    def _get_cors_headers(self, cors: CORSConfig) -> Dict[str, Any]:\n        return cors.get_access_control_headers()\n\n    def _add_cors_headers(self, response: Response,\n                          cors_headers: Dict[str, str]) -> None:\n        for name, value in cors_headers.items():\n            if name not in response.headers:\n                response.headers[name] = value\n\n\n# These classes contain all the event types that are passed\n# in as arguments in the lambda event handlers.  These are\n# part of Chalice's public API and must be backwards compatible.\n\nclass BaseLambdaEvent(object):\n    def __init__(self, event_dict: Dict[str, Any],\n                 context: Optional[Dict[str, Any]]) -> None:\n        self._event_dict: Dict[str, Any] = event_dict\n        self.context: Optional[Dict[str, Any]] = context\n        self._extract_attributes(event_dict)\n\n    def _extract_attributes(self, event_dict: Dict[str, Any]) -> None:\n        raise NotImplementedError(\"_extract_attributes\")\n\n    def to_dict(self) -> Dict[str, Any]:\n        return self._event_dict\n\n\n# This class is only used for middleware handlers because\n# we can't change the existing interface for @app.lambda_function().\n# This could be a Chalice 2.0 thing where we make all the decorators\n# have a consistent interface that takes a single event arg.\nclass LambdaFunctionEvent(BaseLambdaEvent):\n    def __init__(self, event_dict: Dict[str, Any], context: Any) -> None:\n        self.event: Dict[str, Any] = event_dict\n        self.context: Optional[Dict[str, Any]] = context\n\n    def _extract_attributes(self, event_dict: Dict[str, Any]) -> None:\n        pass\n\n    def to_dict(self) -> Dict[str, Any]:\n        return self.event\n\n\nclass CloudWatchEvent(BaseLambdaEvent):\n    def _extract_attributes(self, event_dict: Dict[str, Any]) -> None:\n        self.version: str = event_dict['version']\n        self.account: str = event_dict['account']\n        self.region: str = event_dict['region']\n        self.detail: Dict[str, Any] = event_dict['detail']\n        self.detail_type: str = event_dict['detail-type']\n        self.source: str = event_dict['source']\n        self.time: str = event_dict['time']\n        self.event_id: str = event_dict['id']\n        self.resources: List[str] = event_dict['resources']\n\n\nclass WebsocketEvent(BaseLambdaEvent):\n    def __init__(self, event_dict: Dict[str, Any], context: Any):\n        super(WebsocketEvent, self).__init__(event_dict, context)\n        self._json_body: Optional[Dict[str, Any]] = None\n\n    def _extract_attributes(self, event_dict: Dict[str, Any]) -> None:\n        request_context = event_dict['requestContext']\n        self.domain_name: str = request_context['domainName']\n        self.stage: str = request_context['stage']\n        self.connection_id: str = request_context['connectionId']\n        self.body: str = str(event_dict.get('body'))\n\n    @property\n    def json_body(self) -> Dict[str, Any]:\n        if self._json_body is None:\n            try:\n                self._json_body = json.loads(self.body)\n            except ValueError:\n                raise BadRequestError('Error Parsing JSON')\n        return self._json_body\n\n\nclass SNSEvent(BaseLambdaEvent):\n\n    def _extract_attributes(self, event_dict: Dict[str, Any]) -> None:\n        first_record = event_dict['Records'][0]\n        self.message: str = first_record['Sns']['Message']\n        self.subject: str = first_record['Sns']['Subject']\n        self.message_attributes: Dict[str, Any] = \\\n            first_record['Sns']['MessageAttributes']\n\n\nclass S3Event(BaseLambdaEvent):\n    def _extract_attributes(self, event_dict: Dict[str, Any]) -> None:\n        s3 = event_dict['Records'][0]['s3']\n        self.bucket: str = s3['bucket']['name']\n        self.key: str = unquote_plus(s3['object']['key'])\n\n\nclass SQSEvent(BaseLambdaEvent):\n    def _extract_attributes(self, event_dict: Dict[str, Any]) -> None:\n        # We don't extract anything off the top level\n        # event.\n        pass\n\n    def __iter__(self) -> Iterator['SQSRecord']:\n        for record in self._event_dict['Records']:\n            yield SQSRecord(record, self.context)\n\n\nclass SQSRecord(BaseLambdaEvent):\n\n    def _extract_attributes(self, event_dict: Dict[str, Any]) -> None:\n        self.body: str = event_dict['body']\n        self.receipt_handle: str = event_dict['receiptHandle']\n\n\nclass KinesisEvent(BaseLambdaEvent):\n    def _extract_attributes(self, event_dict: Dict[str, Any]) -> None:\n        pass\n\n    def __iter__(self) -> Iterator['KinesisRecord']:\n        for record in self._event_dict['Records']:\n            yield KinesisRecord(record, self.context)\n\n\nclass KinesisRecord(BaseLambdaEvent):\n    def _extract_attributes(self, event_dict: Dict[str, Any]) -> None:\n        kinesis = event_dict['kinesis']\n        encoded_payload = kinesis['data']\n        self.data: bytes = base64.b64decode(encoded_payload)\n        self.sequence_number: str = kinesis['sequenceNumber']\n        self.partition_key: str = kinesis['partitionKey']\n        self.schema_version: str = kinesis['kinesisSchemaVersion']\n        self.timestamp: datetime.datetime = datetime.datetime.utcfromtimestamp(\n            kinesis['approximateArrivalTimestamp'])\n\n\nclass DynamoDBEvent(BaseLambdaEvent):\n    def _extract_attributes(self, event_dict: Dict[str, Any]) -> None:\n        pass\n\n    def __iter__(self) -> Iterator['DynamoDBRecord']:\n        for record in self._event_dict['Records']:\n            yield DynamoDBRecord(record, self.context)\n\n\nclass DynamoDBRecord(BaseLambdaEvent):\n\n    def _extract_attributes(self, event_dict: Dict[str, Any]) -> None:\n        dynamodb = event_dict['dynamodb']\n        self.timestamp: datetime.datetime = datetime.datetime.utcfromtimestamp(\n            dynamodb['ApproximateCreationDateTime'])\n        self.keys: Any = dynamodb.get('Keys')\n        self.new_image: Any = dynamodb.get('NewImage')\n        self.old_image: Any = dynamodb.get('OldImage')\n        self.sequence_number: str = dynamodb['SequenceNumber']\n        self.size_bytes: int = dynamodb['SizeBytes']\n        self.stream_view_type: str = dynamodb['StreamViewType']\n        # These are from the top level keys in a record.\n        self.aws_region: str = event_dict['awsRegion']\n        self.event_id: str = event_dict['eventID']\n        self.event_name: str = event_dict['eventName']\n        self.event_source_arn: str = event_dict['eventSourceARN']\n\n    @property\n    def table_name(self) -> str:\n        # Converts:\n        # \"arn:aws:dynamodb:us-west-2:12345:table/MyTable/\"\n        # \"stream/2020-09-28T16:49:14.209\"\n        #\n        # into:\n        # \"MyTable\"\n        parts = self.event_source_arn.split(':', 5)\n        if not len(parts) == 6:\n            return ''\n        full_name = parts[-1]\n        name_parts = full_name.split('/')\n        if len(name_parts) >= 2:\n            return name_parts[1]\n        return ''\n\n\nclass Blueprint(DecoratorAPI):\n    def __init__(self, import_name: str) -> None:\n        self._import_name = import_name\n        self._deferred_registrations: \\\n            List[Callable[[Chalice, Dict[str, Any]], None]] = []\n        self._current_app: Optional[Chalice] = None\n        self._lambda_context = None\n\n    @property\n    def log(self) -> logging.Logger:\n        if self._current_app is None:\n            raise RuntimeError(\n                \"Can only access Blueprint.log if it's registered to an app.\"\n            )\n        return self._current_app.log\n\n    @property\n    def current_request(self) -> Request:\n        if self._current_app is None or \\\n                self._current_app.current_request is None:\n            raise RuntimeError(\n                \"Can only access Blueprint.current_request if it's registered \"\n                \"to an app.\"\n            )\n        return self._current_app.current_request\n\n    @property\n    def current_app(self) -> Chalice:\n        if self._current_app is None:\n            raise RuntimeError(\n                \"Can only access Blueprint.current_app if it's registered \"\n                \"to an app.\"\n            )\n        return self._current_app\n\n    @property\n    def lambda_context(self) -> 'LambdaContext':\n        if self._current_app is None:\n            raise RuntimeError(\n                \"Can only access Blueprint.lambda_context if it's registered \"\n                \"to an app.\"\n            )\n        return self._current_app.lambda_context\n\n    def register(self, app: Chalice, options: Dict[str, Any]) -> None:\n        self._current_app = app\n        all_options = options.copy()\n        all_options['module_name'] = self._import_name\n        for function in self._deferred_registrations:\n            function(app, all_options)\n\n    # Note on blueprints implementation.  One option we have for implementing\n    # blueprints is to copy every decorator in our public API over to the\n    # Blueprints class.  Instead what we do is inherit from DecoratorAPI so we\n    # get new decorators for free.  The tradeoff is that need to add\n    # implementations of the internal methods used to manage handler\n    # registration that defer registration until we get an app object. While\n    # these methods are not public in the sense that we don't want users to\n    # call them, they're available for blueprints to use in order to avoid\n    # boilerplate code.\n\n    def register_middleware(self, func: Callable,\n                            event_type: str = 'all') -> None:\n        self._deferred_registrations.append(\n            lambda app, options: app.register_middleware(\n                func, event_type\n            )\n        )\n\n    def _register_handler(self, handler_type: str, name: str,\n                          user_handler: UserHandlerFuncType,\n                          wrapped_handler: Any, kwargs: Dict[str, Any],\n                          options: Optional[Dict[Any, Any]] = None\n                          ) -> None:\n        # If we go through the public API (app.route, app.schedule, etc) then\n        # we have to duplicate either the methods or the params in this\n        # class.  We're using _register_handler as a tradeoff for cutting\n        # down on the duplication.\n        def _register_blueprint_handler(app: Chalice,\n                                        options: Dict[Any, Any]\n                                        ) -> None:\n            if handler_type in _EVENT_CLASSES:\n                # pylint: disable=protected-access\n                wrapped_handler.middleware_handlers = \\\n                    app._get_middleware_handlers(\n                        _MIDDLEWARE_MAPPING[handler_type])\n            # pylint: disable=protected-access\n            app._register_handler(\n                handler_type, name, user_handler, wrapped_handler,\n                kwargs, options\n            )\n        self._deferred_registrations.append(_register_blueprint_handler)\n\n    def _get_middleware_handlers(self, event_type: str) -> List:\n        # This will get filled in later during the registration process.\n        return []\n\n\n# This class is used to convert any existing/3rd party decorators\n# that work directly on lambda functions with the original signature\n# of (event, context).  By using ConvertToMiddleware you can automatically\n# apply this decorator to every lambda function in a Chalice app.\n# Example:\n#\n# Before:\n#\n# @third_part.decorator\n# def some_lambda_function(event, context): pass\n#\n# Now:\n#\n# app.register_middleware(ConvertToMiddleware(third_party.decorator))\n#\n#\nclass ConvertToMiddleware(object):\n    def __init__(self, lambda_wrapper: Callable[..., Any]) -> None:\n        self._wrapper = lambda_wrapper\n\n    def __call__(self, event: Any, get_response: Callable[..., Any]) -> Any:\n        original_event, context = self._extract_original_param(event)\n\n        @functools.wraps(self._wrapper)\n        def wrapped(original_event: Any, context: Any) -> Any:\n            return get_response(event)\n        return self._wrapper(wrapped)(original_event, context)\n\n    def _extract_original_param(self, event: Any) -> Tuple[Any, Optional[Any]]:\n        if isinstance(event, Request):\n            return event.to_original_event(), event.lambda_context\n        return event.to_dict(), event.context\n\n\n_EVENT_CLASSES = {\n    'on_s3_event': S3Event,\n    'on_sns_message': SNSEvent,\n    'on_sqs_message': SQSEvent,\n    'on_cw_event': CloudWatchEvent,\n    'on_kinesis_record': KinesisEvent,\n    'on_dynamodb_record': DynamoDBEvent,\n    'schedule': CloudWatchEvent,\n    'lambda_function': LambdaFunctionEvent,\n}\n\n\n_MIDDLEWARE_MAPPING = {\n    'on_s3_event': 's3',\n    'on_sns_message': 'sns',\n    'on_sqs_message': 'sqs',\n    'on_cw_event': 'cloudwatch',\n    'on_kinesis_record': 'kinesis',\n    'on_dynamodb_record': 'dynamodb',\n    'schedule': 'scheduled',\n    'lambda_function': 'pure_lambda',\n}\n"
  },
  {
    "path": "chalice/awsclient.py",
    "content": "\"\"\"Simplified AWS client.\n\nThis module abstracts the botocore session and clients\nto provide a simpler interface.  This interface only\ncontains the API calls needed to work with AWS services\nused by chalice.\n\nThe interface provided can range from a direct 1-1 mapping\nof a method to a method on a botocore client all the way up\nto combining API calls across multiple AWS services.\n\nAs a side benefit, I can also add type annotations to\nthis class to get improved type checking across chalice.\n\n\"\"\"\nfrom __future__ import annotations\n\n# pylint: disable=too-many-lines\nimport os\nimport time\nimport tempfile\nfrom datetime import datetime\nimport zipfile\nimport shutil\nimport json\nimport re\nimport uuid\nfrom collections import OrderedDict\nfrom typing import (\n    Any,\n    Optional,\n    Dict,\n    Callable,\n    List,\n    Iterator,\n    Iterable,\n    Sequence,\n    IO,\n    Tuple,\n    Union,\n)  # noqa\n\nimport botocore.session  # noqa\nfrom botocore.loaders import create_loader\nfrom botocore.exceptions import ClientError\nfrom botocore.utils import datetime2timestamp\nfrom botocore.vendored.requests import (\n    ConnectionError as RequestsConnectionError,\n)\nfrom botocore.vendored.requests.exceptions import (\n    ReadTimeout as RequestsReadTimeout,\n)\nfrom typing import TypedDict\n\nfrom chalice.constants import DEFAULT_STAGE_NAME\nfrom chalice.constants import MAX_LAMBDA_DEPLOYMENT_SIZE\nfrom chalice.vendored.botocore.regions import EndpointResolver\n\nStrMap = Optional[Dict[str, str]]\nStrAnyMap = Dict[str, Any]\nOptStr = Optional[str]\nOptInt = Optional[int]\nOptStrList = Optional[List[str]]\nClientMethod = Callable[..., Dict[str, Any]]\nCWLogEvent = TypedDict(\n    'CWLogEvent',\n    {\n        'eventId': str,\n        'ingestionTime': datetime,\n        'logStreamName': str,\n        'message': str,\n        'timestamp': datetime,\n        'logShortId': str,\n    },\n)\nLogEventsResponse = TypedDict(\n    'LogEventsResponse',\n    {\n        'events': List[CWLogEvent],\n        'nextToken': str,\n    },\n    total=False,\n)\nDomainNameResponse = TypedDict(\n    'DomainNameResponse',\n    {\n        'domain_name': str,\n        'security_policy': str,\n        'hosted_zone_id': str,\n        'certificate_arn': str,\n        'alias_domain_name': str,\n    },\n)\n\n_REMOTE_CALL_ERRORS = (\n    botocore.exceptions.ClientError,\n    RequestsConnectionError,\n)\n\n\nclass AWSClientError(Exception):\n    pass\n\n\nclass ReadTimeout(AWSClientError):\n    def __init__(self, message: str) -> None:\n        self.message = message\n\n\nclass ResourceDoesNotExistError(AWSClientError):\n    pass\n\n\nclass LambdaClientError(AWSClientError):\n    def __init__(\n        self, original_error: Exception, context: LambdaErrorContext\n    ) -> None:\n        self.original_error = original_error\n        self.context = context\n        super(LambdaClientError, self).__init__(str(original_error))\n\n\nclass DeploymentPackageTooLargeError(LambdaClientError):\n    pass\n\n\nclass LambdaErrorContext(object):\n    def __init__(\n        self,\n        function_name: str,\n        client_method_name: str,\n        deployment_size: int,\n    ) -> None:\n        self.function_name = function_name\n        self.client_method_name = client_method_name\n        self.deployment_size = deployment_size\n\n\nclass TypedAWSClient(object):\n    # 30 * 5 == 150 seconds or 2.5 minutes for the initial lambda\n    # creation + role propagation.\n    LAMBDA_CREATE_ATTEMPTS = 30\n    DELAY_TIME = 5\n\n    def __init__(\n        self,\n        session: botocore.session.Session,\n        sleep: Callable[[int], None] = time.sleep,\n    ) -> None:\n        self._session = session\n        self._sleep = sleep\n        self._client_cache: Dict[str, Any] = {}\n        loader = create_loader('data_loader')\n        endpoints = loader.load_data('endpoints')\n        self._endpoint_resolver = EndpointResolver(endpoints)\n\n    def resolve_endpoint(\n        self, service: str, region: str\n    ) -> Optional[OrderedDict[str, Any]]:\n        \"\"\"Find details of an endpoint based on the service and region.\n\n        This utilizes the botocore EndpointResolver in order to find details on\n        the given service and region combination.  If the service and region\n        combination is not found the None will be returned.\n        \"\"\"\n        return self._endpoint_resolver.construct_endpoint(service, region)\n\n    def endpoint_from_arn(self, arn: str) -> Optional[OrderedDict[str, Any]]:\n        \"\"\"Find details for the endpoint associated with a resource ARN.\n\n        This allows the an endpoint to be discerned based on an ARN.  This\n        is a convenience method due to the need to parse multiple ARNs\n        throughout the project. If the service and region combination\n        is not found the None will be returned.\n        \"\"\"\n        arn_split = arn.split(':')\n        return self.resolve_endpoint(arn_split[2], arn_split[3])\n\n    def endpoint_dns_suffix(self, service: str, region: str) -> str:\n        \"\"\"Discover the dns suffix for a given service and region combination.\n\n        This allows the service DNS suffix to be discoverable throughout the\n        framework.  If the ARN's service and region combination is not found\n        then amazonaws.com is returned.\n\n        \"\"\"\n        endpoint = self.resolve_endpoint(service, region)\n        return endpoint['dnsSuffix'] if endpoint else 'amazonaws.com'\n\n    def endpoint_dns_suffix_from_arn(self, arn: str) -> str:\n        \"\"\"Discover the dns suffix for a given ARN.\n\n        This allows the service DNS suffix to be discoverable throughout the\n        framework based on the ARN.  If the ARN's service and region\n        combination is not found then amazonaws.com is returned.\n\n        \"\"\"\n        endpoint = self.endpoint_from_arn(arn)\n        return endpoint['dnsSuffix'] if endpoint else 'amazonaws.com'\n\n    def service_principal(\n        self,\n        service: str,\n        region: str = 'us-east-1',\n        url_suffix: str = 'amazonaws.com',\n    ) -> str:\n        # Disable too-many-return-statements due to ported code\n        # pylint: disable=too-many-return-statements\n        \"\"\"Compute a \"standard\" AWS Service principal for given arguments.\n\n        Attribution: This code was ported from https://github.com/aws/aws-cdk\n        and more specifically, aws-cdk/region-info/lib/default.ts\n\n        Computes a \"standard\" AWS Service principal for a given service, region\n        and suffix. This is useful for example when you need to compute a\n        service principal name, but you do not have a synthesize-time region\n        literal available (so all you have is `{ \"Ref\": \"AWS::Region\" }`). This\n        way you get the same defaulting behavior that is normally used for\n        built-in data.\n\n        :param service: the name of the service (s3, s3.amazonaws.com, ...)\n        :param region: the region in which the service principal is needed.\n        :param url_suffix: the URL suffix for the partition in which the region\n        is located.\n        :return: The service principal for the given combination of arguments\n        \"\"\"\n        matches = re.match(\n            (\n                r'^([^.]+)'\n                r'(?:(?:\\.amazonaws\\.com(?:\\.cn)?)|'\n                r'(?:\\.c2s\\.ic\\.gov)|'\n                r'(?:\\.sc2s\\.sgov\\.gov))?$'\n            ),\n            service,\n        )\n\n        if matches is None:\n            #  Return \"service\" if it does not look like any of the following:\n            #  - s3\n            #  - s3.amazonaws.com\n            #  - s3.amazonaws.com.cn\n            #  - s3.c2s.ic.gov\n            #  - s3.sc2s.sgov.gov\n            return service\n\n        # Simplify the service name down to something like \"s3\"\n        service_name = matches.group(1)\n\n        # Exceptions for Service Principals in us-iso-*\n        us_iso_exceptions = {'cloudhsm', 'config', 'states', 'workspaces'}\n\n        # Exceptions for Service Principals in us-isob-*\n        us_isob_exceptions = {'dms', 'states'}\n\n        # Account for idiosyncratic Service Principals in `us-iso-*` regions\n        if region.startswith('us-iso-') and service_name in us_iso_exceptions:\n            if service_name == 'states':\n                # Services with universal principal\n                return '{}.amazonaws.com'.format(service_name)\n            else:\n                # Services with a partitional principal\n                return '{}.{}'.format(service_name, url_suffix)\n\n        # Account for idiosyncratic Service Principals in `us-isob-*` regions\n        if (\n            region.startswith('us-isob-')\n            and service_name in us_isob_exceptions\n        ):\n            if service_name == 'states':\n                # Services with universal principal\n                return '{}.amazonaws.com'.format(service_name)\n            else:\n                # Services with a partitional principal\n                return '{}.{}'.format(service_name, url_suffix)\n\n        if service_name in ['codedeploy', 'logs']:\n            return '{}.{}.{}'.format(service_name, region, url_suffix)\n        elif service_name == 'states':\n            return '{}.{}.amazonaws.com'.format(service_name, region)\n        elif service_name == 'ec2':\n            return '{}.{}'.format(service_name, url_suffix)\n        else:\n            return '{}.amazonaws.com'.format(service_name)\n\n    def lambda_function_exists(self, name: str) -> bool:\n        client = self._client('lambda')\n        try:\n            client.get_function(FunctionName=name)\n            return True\n        except client.exceptions.ResourceNotFoundException:\n            return False\n\n    def api_mapping_exists(self, domain_name: str, api_map_key: str) -> bool:\n        client = self._client('apigatewayv2')\n        try:\n            result = client.get_api_mappings(DomainName=domain_name)\n            api_map = [\n                api_map\n                for api_map in result['Items']\n                if api_map['ApiMappingKey'] == api_map_key\n            ]\n            if api_map:\n                return True\n            return False\n        except client.exceptions.NotFoundException:\n            return False\n\n    def get_domain_name(self, domain_name: str) -> Dict[str, Any]:\n        client = self._client('apigateway')\n        try:\n            domain = client.get_domain_name(domainName=domain_name)\n        except client.exceptions.NotFoundException:\n            err_msg = \"No domain name found by %s name\" % domain_name\n            raise ResourceDoesNotExistError(err_msg)\n        return domain\n\n    def domain_name_exists(self, domain_name: str) -> bool:\n        try:\n            self.get_domain_name(domain_name)\n            return True\n        except ResourceDoesNotExistError:\n            return False\n\n    def domain_name_exists_v2(self, domain_name: str) -> bool:\n        client = self._client('apigatewayv2')\n        try:\n            client.get_domain_name(DomainName=domain_name)\n            return True\n        except client.exceptions.NotFoundException:\n            return False\n\n    def get_function_configuration(self, name: str) -> Dict[str, Any]:\n        response = self._client('lambda').get_function_configuration(\n            FunctionName=name\n        )\n        return response\n\n    def _create_vpc_config(\n        self, security_group_ids: OptStrList, subnet_ids: OptStrList\n    ) -> Dict[str, List[str]]:\n        # We always set the SubnetIds and SecurityGroupIds to an empty\n        # list to ensure that we properly remove Vpc configuration\n        # if you remove these values from your config.json.  Omitting\n        # the VpcConfig key or just setting to {} won't actually remove\n        # the VPC configuration.\n        vpc_config: Dict[str, List[str]] = {\n            'SubnetIds': [],\n            'SecurityGroupIds': [],\n        }\n        if security_group_ids is not None and subnet_ids is not None:\n            vpc_config['SubnetIds'] = subnet_ids\n            vpc_config['SecurityGroupIds'] = security_group_ids\n        return vpc_config\n\n    def publish_layer(\n        self, layer_name: str, zip_contents: bytes, runtime: str\n    ) -> str:\n        try:\n            return self._client('lambda').publish_layer_version(\n                LayerName=layer_name,\n                Content={'ZipFile': zip_contents},\n                CompatibleRuntimes=[runtime],\n            )['LayerVersionArn']\n        except _REMOTE_CALL_ERRORS as e:\n            context = LambdaErrorContext(\n                layer_name, 'publish_layer_version', len(zip_contents)\n            )\n            raise self._get_lambda_code_deployment_error(e, context)\n\n    def delete_layer_version(self, layer_version_arn: str) -> None:\n        client = self._client('lambda')\n        _, layer_name, version_number = layer_version_arn.rsplit(\":\", 2)\n        try:\n            return client.delete_layer_version(\n                LayerName=layer_name, VersionNumber=int(version_number)\n            )\n        except client.exceptions.ResourceNotFoundException:\n            pass\n\n    def get_layer_version(self, layer_version_arn: str) -> Dict[str, Any]:\n        client = self._client('lambda')\n        try:\n            return client.get_layer_version_by_arn(Arn=layer_version_arn)\n        except client.exceptions.ResourceNotFoundException:\n            pass\n        return {}\n\n    def create_function(\n        self,\n        function_name: str,\n        role_arn: str,\n        zip_contents: str,\n        runtime: str,\n        handler: str,\n        environment_variables: Optional[StrMap] = None,\n        tags: Optional[StrMap] = None,\n        xray: Optional[bool] = None,\n        timeout: OptInt = None,\n        memory_size: OptInt = None,\n        security_group_ids: OptStrList = None,\n        subnet_ids: OptStrList = None,\n        layers: OptStrList = None,\n    ) -> str:\n        # pylint: disable=too-many-locals\n        kwargs: Dict[str, Any] = {\n            'FunctionName': function_name,\n            'Runtime': runtime,\n            'Code': {'ZipFile': zip_contents},\n            'Handler': handler,\n            'Role': role_arn,\n        }\n        if environment_variables is not None:\n            kwargs['Environment'] = {\"Variables\": environment_variables}\n        if tags is not None:\n            kwargs['Tags'] = tags\n        if xray is True:\n            kwargs['TracingConfig'] = {'Mode': 'Active'}\n        if timeout is not None:\n            kwargs['Timeout'] = timeout\n        if memory_size is not None:\n            kwargs['MemorySize'] = memory_size\n        if security_group_ids is not None and subnet_ids is not None:\n            kwargs['VpcConfig'] = self._create_vpc_config(\n                security_group_ids=security_group_ids,\n                subnet_ids=subnet_ids,\n            )\n        if layers is not None:\n            kwargs['Layers'] = layers\n        arn, state = self._create_lambda_function(kwargs)\n        # Avoid the GetFunctionConfiguration call unless\n        # we're not immediately active.\n        if state != 'Active':\n            self._wait_for_active(function_name)\n        return arn\n\n    def _wait_for_active(self, function_name: str) -> None:\n        client = self._client('lambda')\n        waiter = client.get_waiter('function_active')\n        waiter.wait(FunctionName=function_name)\n\n    def create_api_mapping(\n        self, domain_name: str, path_key: str, api_id: str, stage: str\n    ) -> Dict[str, str]:\n        kwargs = {\n            'DomainName': domain_name,\n            'ApiMappingKey': path_key,\n            'ApiId': api_id,\n            'Stage': stage,\n        }\n        return self._create_api_mapping(kwargs)\n\n    def create_base_path_mapping(\n        self, domain_name: str, path_key: str, api_id: str, stage: str\n    ) -> Dict[str, str]:\n        kwargs = {\n            'domainName': domain_name,\n            'basePath': path_key,\n            'restApiId': api_id,\n            'stage': stage,\n        }\n        return self._create_base_path_mapping(kwargs)\n\n    def _create_base_path_mapping(\n        self, base_path_args: Dict[str, Any]\n    ) -> Dict[str, str]:\n        result = self._client('apigateway').create_base_path_mapping(\n            **base_path_args\n        )\n        if result['basePath'] == '(none)':\n            base_path = \"/\"\n        else:\n            base_path = \"/%s\" % result['basePath']\n        base_path_mapping = {'key': base_path}\n        return base_path_mapping\n\n    def _create_api_mapping(self, api_args: Dict[str, Any]) -> Dict[str, str]:\n        result = self._client('apigatewayv2').create_api_mapping(**api_args)\n        if result['ApiMappingKey'] == '(none)':\n            map_key = \"/\"\n        else:\n            map_key = \"/%s\" % result['ApiMappingKey']\n        api_mapping = {'key': map_key}\n        return api_mapping\n\n    def create_domain_name(\n        self,\n        protocol: str,\n        domain_name: str,\n        endpoint_type: str,\n        certificate_arn: str,\n        security_policy: Optional[str] = None,\n        tags: Optional[StrMap] = None,\n    ) -> DomainNameResponse:\n        if protocol == 'HTTP':\n            kwargs = {\n                'domainName': domain_name,\n                'endpointConfiguration': {\n                    'types': [endpoint_type],\n                },\n            }\n            if security_policy is not None:\n                kwargs['securityPolicy'] = security_policy\n            if endpoint_type == 'EDGE':\n                kwargs['certificateArn'] = certificate_arn\n            else:\n                kwargs['regionalCertificateArn'] = certificate_arn\n            if tags is not None:\n                kwargs['tags'] = tags\n            created_domain_name = self._create_domain_name(kwargs)\n        elif protocol == 'WEBSOCKET':\n            kwargs = self.get_custom_domain_params_v2(\n                domain_name=domain_name,\n                endpoint_type=endpoint_type,\n                security_policy=security_policy,\n                certificate_arn=certificate_arn,\n                tags=tags,\n            )\n            created_domain_name = self._create_domain_name_v2(kwargs)\n        else:\n            raise ValueError(\"Unsupported protocol value.\")\n        return created_domain_name\n\n    def _create_domain_name(\n        self, api_args: Dict[str, Any]\n    ) -> DomainNameResponse:\n        client = self._client('apigateway')\n        exceptions = (client.exceptions.TooManyRequestsException,)\n        result = self._call_client_method_with_retries(\n            client.create_domain_name,\n            api_args,\n            max_attempts=6,\n            should_retry=lambda x: True,\n            retryable_exceptions=exceptions,\n        )\n        if result.get('regionalHostedZoneId'):\n            hosted_zone_id = result['regionalHostedZoneId']\n        else:\n            hosted_zone_id = result['distributionHostedZoneId']\n\n        if result.get('regionalCertificateArn'):\n            certificate_arn = result['regionalCertificateArn']\n        else:\n            certificate_arn = result['certificateArn']\n\n        if result.get('regionalDomainName') is not None:\n            alias_domain_name = result['regionalDomainName']\n        else:\n            alias_domain_name = result['distributionDomainName']\n        domain_name: DomainNameResponse = {\n            'domain_name': result['domainName'],\n            'security_policy': result['securityPolicy'],\n            'hosted_zone_id': hosted_zone_id,\n            'certificate_arn': certificate_arn,\n            'alias_domain_name': alias_domain_name,\n        }\n        return domain_name\n\n    def _create_domain_name_v2(\n        self, api_args: Dict[str, Any]\n    ) -> DomainNameResponse:\n        client = self._client('apigatewayv2')\n        exceptions = (client.exceptions.TooManyRequestsException,)\n        result = self._call_client_method_with_retries(\n            client.create_domain_name,\n            api_args,\n            max_attempts=6,\n            should_retry=lambda x: True,\n            retryable_exceptions=exceptions,\n        )\n        result_data = result['DomainNameConfigurations'][0]\n        domain_name: DomainNameResponse = {\n            'domain_name': result['DomainName'],\n            'alias_domain_name': result_data['ApiGatewayDomainName'],\n            'security_policy': result_data['SecurityPolicy'],\n            'hosted_zone_id': result_data['HostedZoneId'],\n            'certificate_arn': result_data['CertificateArn'],\n        }\n        return domain_name\n\n    def _create_lambda_function(\n        self, api_args: Dict[str, Any]\n    ) -> Tuple[str, str]:\n        try:\n            result = self._call_client_method_with_retries(\n                self._client('lambda').create_function,\n                api_args,\n                max_attempts=self.LAMBDA_CREATE_ATTEMPTS,\n            )\n            return result['FunctionArn'], result['State']\n        except _REMOTE_CALL_ERRORS as e:\n            context = LambdaErrorContext(\n                api_args['FunctionName'],\n                'create_function',\n                len(api_args['Code']['ZipFile']),\n            )\n            raise self._get_lambda_code_deployment_error(e, context)\n\n    def _is_settling_error(\n        self, error: botocore.exceptions.ClientError\n    ) -> bool:\n        message = error.response['Error'].get('Message', '')\n        if re.search('event source mapping.*is in use', message):\n            return True\n        return False\n\n    def invoke_function(\n        self, name: str, payload: Optional[bytes] = None\n    ) -> Dict[str, Any]:\n        kwargs: Dict[str, Union[str, bytes]] = {\n            'FunctionName': name,\n            'InvocationType': 'RequestResponse',\n        }\n        if payload is not None:\n            kwargs['Payload'] = payload\n\n        try:\n            return self._client('lambda').invoke(**kwargs)\n        except RequestsReadTimeout as e:\n            raise ReadTimeout(str(e))\n\n    def _is_iam_role_related_error(\n        self, error: botocore.exceptions.ClientError\n    ) -> bool:\n        message = error.response['Error'].get('Message', '')\n        if re.search('role.*cannot be assumed', message):\n            return True\n        if re.search('role.*does not have permissions', message):\n            return True\n        # This message is also related to IAM roles, it happens when the grant\n        # used for the KMS key for encrypting env vars doesn't think the\n        # principal is valid yet.\n        if re.search('InvalidArnException.*valid principal', message):\n            return True\n        return False\n\n    def _get_lambda_code_deployment_error(\n        self, error: Any, context: LambdaErrorContext\n    ) -> LambdaClientError:\n        error_cls = LambdaClientError\n        if (\n            isinstance(error, RequestsConnectionError)\n            and context.deployment_size > MAX_LAMBDA_DEPLOYMENT_SIZE\n        ):\n            # When the zip deployment package is too large and Lambda\n            # aborts the connection as chalice is still sending it\n            # data\n            error_cls = DeploymentPackageTooLargeError\n        elif isinstance(error, ClientError):\n            code = error.response['Error'].get('Code', '')\n            message = error.response['Error'].get('Message', '')\n            if code == 'RequestEntityTooLargeException':\n                # Happens when the zipped deployment package sent to lambda\n                # is too large\n                error_cls = DeploymentPackageTooLargeError\n            elif (\n                code == 'InvalidParameterValueException'\n                and 'Unzipped size must be smaller' in message\n            ):\n                # Happens when the contents of the unzipped deployment\n                # package sent to lambda is too large\n                error_cls = DeploymentPackageTooLargeError\n        return error_cls(error, context)\n\n    def delete_function(self, function_name: str) -> None:\n        lambda_client = self._client('lambda')\n        try:\n            lambda_client.delete_function(FunctionName=function_name)\n        except lambda_client.exceptions.ResourceNotFoundException:\n            raise ResourceDoesNotExistError(function_name)\n\n    def get_custom_domain_params_v2(\n        self,\n        domain_name: str,\n        endpoint_type: str,\n        certificate_arn: str,\n        security_policy: Optional[str] = None,\n        tags: Optional[StrMap] = None,\n    ) -> Dict[str, Any]:\n        kwargs: Dict[str, Any] = {\n            'DomainName': domain_name,\n            'DomainNameConfigurations': [\n                {\n                    'ApiGatewayDomainName': domain_name,\n                    'CertificateArn': certificate_arn,\n                    'EndpointType': endpoint_type,\n                    'SecurityPolicy': security_policy,\n                    'DomainNameStatus': 'AVAILABLE',\n                }\n            ],\n        }\n        if tags:\n            kwargs['Tags'] = tags\n        return kwargs\n\n    def get_custom_domain_patch_operations(\n        self,\n        certificate_arn: str,\n        endpoint_type: Optional[str],\n        security_policy: Optional[str] = None,\n    ) -> List[Dict[str, str]]:\n        patch_operations = []\n        if security_policy is not None:\n            patch_operations.append(\n                {\n                    'op': 'replace',\n                    'path': '/securityPolicy',\n                    'value': security_policy,\n                }\n            )\n        if endpoint_type == 'EDGE':\n            patch_operations.append(\n                {\n                    'op': 'replace',\n                    'path': '/certificateArn',\n                    'value': certificate_arn,\n                }\n            )\n        else:\n            patch_operations.append(\n                {\n                    'op': 'replace',\n                    'path': '/regionalCertificateArn',\n                    'value': certificate_arn,\n                }\n            )\n        return patch_operations\n\n    def update_domain_name(\n        self,\n        protocol: str,\n        domain_name: str,\n        endpoint_type: str,\n        certificate_arn: str,\n        security_policy: Optional[str] = None,\n        tags: Optional[StrMap] = None,\n    ) -> DomainNameResponse:\n        if protocol == 'HTTP':\n            patch_operations = self.get_custom_domain_patch_operations(\n                certificate_arn,\n                endpoint_type,\n                security_policy,\n            )\n            updated_domain_name = self._update_domain_name(\n                domain_name, patch_operations\n            )\n        elif protocol == 'WEBSOCKET':\n            kwargs = self.get_custom_domain_params_v2(\n                domain_name=domain_name,\n                endpoint_type=endpoint_type,\n                security_policy=security_policy,\n                certificate_arn=certificate_arn,\n            )\n            updated_domain_name = self._update_domain_name_v2(kwargs)\n        else:\n            raise ValueError('Unsupported protocol value.')\n        resource_arn = (\n            'arn:{partition}:apigateway:{region_name}:'\n            ':/domainnames/{domain_name}'.format(\n                partition=self.partition_name,\n                region_name=self.region_name,\n                domain_name=domain_name,\n            )\n        )\n        self._update_resource_tags(resource_arn, tags)\n        return updated_domain_name\n\n    def _update_resource_tags(\n        self, resource_arn: str, requested_tags: Optional[Dict[str, str]]\n    ) -> None:\n        if not requested_tags:\n            requested_tags = {}\n\n        remote_tags = self._client('apigatewayv2').get_tags(\n            ResourceArn=resource_arn\n        )['Tags']\n        self._remove_unrequested_resource_tags(\n            resource_arn, requested_tags, remote_tags\n        )\n        self._add_missing_or_differing_value_resource_tags(\n            resource_arn, requested_tags, remote_tags\n        )\n\n    def _remove_unrequested_resource_tags(\n        self,\n        resource_arn: str,\n        requested_tags: Dict[Any, Any],\n        remote_tags: Dict[Any, Any],\n    ) -> None:\n        tag_keys_to_remove = list(set(remote_tags) - set(requested_tags))\n        if tag_keys_to_remove:\n            self._client('apigatewayv2').untag_resource(\n                ResourceArn=resource_arn, TagKeys=tag_keys_to_remove\n            )\n\n    def _add_missing_or_differing_value_resource_tags(\n        self,\n        resource_arn: str,\n        requested_tags: Dict[Any, Any],\n        remote_tags: Dict[Any, Any],\n    ) -> None:\n        tags_to_add = {\n            k: v\n            for k, v in requested_tags.items()\n            if k not in remote_tags or v != remote_tags[k]\n        }\n        if tags_to_add:\n            self._client('apigatewayv2').tag_resource(\n                ResourceArn=resource_arn, Tags=tags_to_add\n            )\n\n    def _update_domain_name(\n        self, custom_domain_name: str, patch_operations: List[Dict[str, str]]\n    ) -> DomainNameResponse:\n        client = self._client('apigateway')\n        exceptions = (client.exceptions.TooManyRequestsException,)\n        result = {}\n        for patch_operation in patch_operations:\n            api_args = {\n                'domainName': custom_domain_name,\n                'patchOperations': [patch_operation],\n            }\n            response = self._call_client_method_with_retries(\n                client.update_domain_name,\n                api_args,\n                max_attempts=6,\n                should_retry=lambda x: True,\n                retryable_exceptions=exceptions,\n            )\n            result.update(response)\n\n        if result.get('regionalCertificateArn'):\n            certificate_arn = result['regionalCertificateArn']\n        else:\n            certificate_arn = result['certificateArn']\n\n        if result.get('regionalHostedZoneId'):\n            hosted_zone_id = result['regionalHostedZoneId']\n        else:\n            hosted_zone_id = result['distributionHostedZoneId']\n\n        if result.get('regionalDomainName') is not None:\n            alias_domain_name = result['regionalDomainName']\n        else:\n            alias_domain_name = result['distributionDomainName']\n        domain_name: DomainNameResponse = {\n            'domain_name': result['domainName'],\n            'security_policy': result['securityPolicy'],\n            'certificate_arn': certificate_arn,\n            'hosted_zone_id': hosted_zone_id,\n            'alias_domain_name': alias_domain_name,\n        }\n        return domain_name\n\n    def _update_domain_name_v2(\n        self, api_args: Dict[str, Any]\n    ) -> DomainNameResponse:\n        client = self._client('apigatewayv2')\n        exceptions = (client.exceptions.TooManyRequestsException,)\n\n        result = self._call_client_method_with_retries(\n            client.update_domain_name,\n            api_args,\n            max_attempts=6,\n            should_retry=lambda x: True,\n            retryable_exceptions=exceptions,\n        )\n        result_data = result['DomainNameConfigurations'][0]\n        domain_name: DomainNameResponse = {\n            'domain_name': result['DomainName'],\n            'alias_domain_name': result_data['ApiGatewayDomainName'],\n            'security_policy': result_data['SecurityPolicy'],\n            'hosted_zone_id': result_data['HostedZoneId'],\n            'certificate_arn': result_data['CertificateArn'],\n        }\n        return domain_name\n\n    def delete_domain_name(self, domain_name: str) -> None:\n        client = self._client('apigatewayv2')\n        params = {'DomainName': domain_name}\n\n        exceptions = (client.exceptions.TooManyRequestsException,)\n        self._call_client_method_with_retries(\n            client.delete_domain_name,\n            params,\n            max_attempts=6,\n            should_retry=lambda x: True,\n            retryable_exceptions=exceptions,\n        )\n\n    def delete_api_mapping(self, domain_name: str, path_key: str) -> None:\n        client = self._client('apigateway')\n        params = {'domainName': domain_name, 'basePath': path_key}\n        client.delete_base_path_mapping(**params)\n\n    def update_function(\n        self,\n        function_name: str,\n        zip_contents: str,\n        environment_variables: Optional[StrMap] = None,\n        runtime: OptStr = None,\n        tags: Optional[StrMap] = None,\n        xray: Optional[bool] = None,\n        timeout: OptInt = None,\n        memory_size: OptInt = None,\n        role_arn: OptStr = None,\n        subnet_ids: OptStrList = None,\n        security_group_ids: OptStrList = None,\n        layers: OptStrList = None,\n    ) -> Dict[str, Any]:\n        \"\"\"Update a Lambda function's code and configuration.\n\n        This method only updates the values provided to it. If a parameter\n        is not provided, no changes will be made for that that parameter on\n        the targeted lambda function.\n        \"\"\"\n        return_value = self._update_function_code(\n            function_name=function_name, zip_contents=zip_contents\n        )\n        self._update_function_config(\n            environment_variables=environment_variables,\n            runtime=runtime,\n            timeout=timeout,\n            memory_size=memory_size,\n            role_arn=role_arn,\n            xray=xray,\n            subnet_ids=subnet_ids,\n            security_group_ids=security_group_ids,\n            function_name=function_name,\n            layers=layers,\n        )\n        if tags is not None:\n            self._update_function_tags(return_value['FunctionArn'], tags)\n        return return_value\n\n    def _update_function_code(\n        self, function_name: str, zip_contents: str\n    ) -> Dict[str, Any]:\n        lambda_client = self._client('lambda')\n        try:\n            result = lambda_client.update_function_code(\n                FunctionName=function_name, ZipFile=zip_contents\n            )\n        except _REMOTE_CALL_ERRORS as e:\n            context = LambdaErrorContext(\n                function_name, 'update_function_code', len(zip_contents)\n            )\n            raise self._get_lambda_code_deployment_error(e, context)\n        if result['LastUpdateStatus'] != 'Successful':\n            self._wait_for_function_update(function_name)\n        return result\n\n    def _wait_for_function_update(self, function_name: str) -> None:\n        client = self._client('lambda')\n        waiter = client.get_waiter('function_updated')\n        waiter.wait(FunctionName=function_name)\n\n    def put_function_concurrency(\n        self, function_name: str, reserved_concurrent_executions: int\n    ) -> None:\n        lambda_client = self._client('lambda')\n        lambda_client.put_function_concurrency(\n            FunctionName=function_name,\n            ReservedConcurrentExecutions=reserved_concurrent_executions,\n        )\n\n    def delete_function_concurrency(self, function_name: str) -> None:\n        lambda_client = self._client('lambda')\n        lambda_client.delete_function_concurrency(FunctionName=function_name)\n\n    def _update_function_config(\n        self,\n        environment_variables: StrMap,\n        runtime: OptStr,\n        timeout: OptInt,\n        memory_size: OptInt,\n        role_arn: OptStr,\n        subnet_ids: OptStrList,\n        security_group_ids: OptStrList,\n        function_name: str,\n        layers: OptStrList,\n        xray: Optional[bool],\n    ) -> None:\n        kwargs: Dict[str, Any] = {}\n        if environment_variables is not None:\n            kwargs['Environment'] = {'Variables': environment_variables}\n        if runtime is not None:\n            kwargs['Runtime'] = runtime\n        if timeout is not None:\n            kwargs['Timeout'] = timeout\n        if memory_size is not None:\n            kwargs['MemorySize'] = memory_size\n        if role_arn is not None:\n            kwargs['Role'] = role_arn\n        if xray:\n            kwargs['TracingConfig'] = {'Mode': 'Active'}\n        if security_group_ids is not None and subnet_ids is not None:\n            kwargs['VpcConfig'] = self._create_vpc_config(\n                subnet_ids=subnet_ids, security_group_ids=security_group_ids\n            )\n        if layers is not None:\n            kwargs['Layers'] = layers\n        if kwargs:\n            self._do_update_function_config(function_name, kwargs)\n\n    def _do_update_function_config(\n        self, function_name: str, kwargs: Dict[str, Any]\n    ) -> None:\n        kwargs['FunctionName'] = function_name\n        lambda_client = self._client('lambda')\n        result = self._call_client_method_with_retries(\n            lambda_client.update_function_configuration,\n            kwargs,\n            max_attempts=self.LAMBDA_CREATE_ATTEMPTS,\n        )\n        if result['LastUpdateStatus'] != 'Successful':\n            self._wait_for_function_update(function_name)\n\n    def _update_function_tags(\n        self, function_arn: str, requested_tags: Dict[str, str]\n    ) -> None:\n        remote_tags = self._client('lambda').list_tags(Resource=function_arn)[\n            'Tags'\n        ]\n        self._remove_unrequested_remote_tags(\n            function_arn, requested_tags, remote_tags\n        )\n        self._add_missing_or_differing_value_requested_tags(\n            function_arn, requested_tags, remote_tags\n        )\n\n    def _remove_unrequested_remote_tags(\n        self,\n        function_arn: str,\n        requested_tags: Dict[Any, Any],\n        remote_tags: Dict[Any, Any],\n    ) -> None:\n        tag_keys_to_remove = list(set(remote_tags) - set(requested_tags))\n        if tag_keys_to_remove:\n            self._client('lambda').untag_resource(\n                Resource=function_arn, TagKeys=tag_keys_to_remove\n            )\n\n    def _add_missing_or_differing_value_requested_tags(\n        self,\n        function_arn: str,\n        requested_tags: Dict[Any, Any],\n        remote_tags: Dict[Any, Any],\n    ) -> None:\n        tags_to_add = {\n            k: v\n            for k, v in requested_tags.items()\n            if k not in remote_tags or v != remote_tags[k]\n        }\n        if tags_to_add:\n            self._client('lambda').tag_resource(\n                Resource=function_arn, Tags=tags_to_add\n            )\n\n    def get_role_arn_for_name(self, name: str) -> str:\n        role = self.get_role(name)\n        return role['Arn']\n\n    def get_role(self, name: str) -> Dict[str, Any]:\n        client = self._client('iam')\n        try:\n            role = client.get_role(RoleName=name)\n        except client.exceptions.NoSuchEntityException:\n            raise ResourceDoesNotExistError(\"No role ARN found for: %s\" % name)\n        return role['Role']\n\n    def delete_role_policy(self, role_name: str, policy_name: str) -> None:\n        self._client('iam').delete_role_policy(\n            RoleName=role_name, PolicyName=policy_name\n        )\n\n    def put_role_policy(\n        self, role_name: str, policy_name: str, policy_document: Dict[str, Any]\n    ) -> None:\n        # Note: policy_document is not JSON encoded.\n        self._client('iam').put_role_policy(\n            RoleName=role_name,\n            PolicyName=policy_name,\n            PolicyDocument=json.dumps(policy_document, indent=2),\n        )\n\n    def create_role(\n        self, name: str, trust_policy: Dict[str, Any], policy: Dict[str, Any]\n    ) -> str:\n        client = self._client('iam')\n        response = client.create_role(\n            RoleName=name, AssumeRolePolicyDocument=json.dumps(trust_policy)\n        )\n        role_arn = response['Role']['Arn']\n        try:\n            self.put_role_policy(\n                role_name=name, policy_name=name, policy_document=policy\n            )\n        except client.exceptions.MalformedPolicyDocumentException as e:\n            self.delete_role(name=name)\n            raise e\n        return role_arn\n\n    def delete_role(self, name: str) -> None:\n        \"\"\"Delete a role by first deleting all inline policies.\"\"\"\n        client = self._client('iam')\n        inline_policies = client.list_role_policies(RoleName=name)[\n            'PolicyNames'\n        ]\n        for policy_name in inline_policies:\n            self.delete_role_policy(name, policy_name)\n        client.delete_role(RoleName=name)\n\n    def log_group_exists(self, name: str) -> bool:\n        \"\"\"Check if an CloudWatch LOG GROUP exists.\"\"\"\n        client = self._client('logs')\n        result = client.describe_log_groups(\n            logGroupNamePrefix=name\n        )\n        if len(result['logGroups']) == 0:\n            return False\n        return True\n\n    def create_log_group(self, log_group_name: str) -> None:\n        self._client('logs').create_log_group(\n            logGroupName=log_group_name,\n        )\n\n    def delete_retention_policy(self, log_group_name: str) -> None:\n        self._client('logs').delete_retention_policy(\n            logGroupName=log_group_name,\n        )\n\n    def delete_log_group(self, log_group_name: str) -> None:\n        self._client('logs').delete_log_group(\n            logGroupName=log_group_name,\n        )\n\n    def put_retention_policy(self, name: str, retention_in_days: int) -> None:\n        self._client('logs').put_retention_policy(\n            logGroupName=name,\n            retentionInDays=retention_in_days\n        )\n\n    def get_rest_api_id(self, name: str) -> Optional[str]:\n        \"\"\"Get rest api id associated with an API name.\n\n        :type name: str\n        :param name: The name of the rest api.\n\n        :rtype: str\n        :return: If the rest api exists, then the restApiId\n            is returned, otherwise None.\n\n        \"\"\"\n        rest_apis = self._client('apigateway').get_rest_apis()['items']\n        for api in rest_apis:\n            if api['name'] == name:\n                return api['id']\n        return None\n\n    def get_rest_api(self, rest_api_id: str) -> Dict[str, Any]:\n        \"\"\"Check if an API Gateway REST API exists.\"\"\"\n        client = self._client('apigateway')\n        try:\n            result = client.get_rest_api(restApiId=rest_api_id)\n            result.pop('ResponseMetadata', None)\n            return result\n        except client.exceptions.NotFoundException:\n            return {}\n\n    def import_rest_api(\n        self, swagger_document: Dict[str, Any], endpoint_type: str\n    ) -> str:\n        client = self._client('apigateway')\n        response = client.import_rest_api(\n            body=json.dumps(swagger_document, indent=2),\n            parameters={'endpointConfigurationTypes': endpoint_type},\n        )\n        rest_api_id = response['id']\n        return rest_api_id\n\n    def update_api_from_swagger(\n        self, rest_api_id: str, swagger_document: Dict[str, Any]\n    ) -> None:\n        client = self._client('apigateway')\n        client.put_rest_api(\n            restApiId=rest_api_id,\n            mode='overwrite',\n            body=json.dumps(swagger_document, indent=2),\n        )\n\n    def update_rest_api(\n        self, rest_api_id: str, patch_operations: List[Dict]\n    ) -> None:\n        client = self._client('apigateway')\n        client.update_rest_api(\n            restApiId=rest_api_id, patchOperations=patch_operations\n        )\n\n    def delete_rest_api(self, rest_api_id: str) -> None:\n        client = self._client('apigateway')\n        try:\n            client.delete_rest_api(restApiId=rest_api_id)\n        except client.exceptions.NotFoundException:\n            raise ResourceDoesNotExistError(rest_api_id)\n\n    def deploy_rest_api(\n        self, rest_api_id: str, api_gateway_stage: str, xray: bool\n    ) -> None:\n        client = self._client('apigateway')\n        client.create_deployment(\n            restApiId=rest_api_id,\n            stageName=api_gateway_stage,\n            tracingEnabled=bool(xray),\n        )\n\n    def add_permission_for_apigateway(\n        self,\n        function_name: str,\n        region_name: str,\n        account_id: str,\n        rest_api_id: str,\n        random_id: Optional[str] = None,\n    ) -> None:\n        \"\"\"Authorize API gateway to invoke a lambda function is needed.\n\n        This method will first check if API gateway has permission to call\n        the lambda function, and only if necessary will it invoke\n        ``self.add_permission_for_apigateway(...).\n\n        \"\"\"\n        source_arn = self._build_source_arn_str(\n            region_name, account_id, rest_api_id\n        )\n        self._add_lambda_permission_if_needed(\n            source_arn=source_arn,\n            function_arn=function_name,\n            service_name='apigateway',\n        )\n\n    def add_permission_for_apigateway_v2(\n        self,\n        function_name: str,\n        region_name: str,\n        account_id: str,\n        api_id: str,\n        random_id: Optional[str] = None,\n    ) -> None:\n        \"\"\"Authorize API gateway v2 to invoke a lambda function.\"\"\"\n        source_arn = self._build_source_arn_str(\n            region_name, account_id, api_id\n        )\n        self._add_lambda_permission_if_needed(\n            source_arn=source_arn,\n            function_arn=function_name,\n            service_name='apigateway',\n        )\n\n    def get_function_policy(self, function_name: str) -> Dict[str, Any]:\n        \"\"\"Return the function policy for a lambda function.\n\n        This function will extract the policy string as a json document\n        and return the json.loads(...) version of the policy.\n\n        \"\"\"\n        client = self._client('lambda')\n        try:\n            policy = client.get_policy(FunctionName=function_name)\n            return json.loads(policy['Policy'])\n        except client.exceptions.ResourceNotFoundException:\n            return {'Statement': []}\n\n    def download_sdk(\n        self,\n        rest_api_id: str,\n        output_dir: str,\n        api_gateway_stage: str = DEFAULT_STAGE_NAME,\n        sdk_type: str = 'javascript',\n    ) -> None:\n        \"\"\"Download an SDK to a directory.\n\n        This will generate an SDK and download it to the provided\n        ``output_dir``.  If you're using ``get_sdk_download_stream()``,\n        you have to handle downloading the stream and unzipping the\n        contents yourself.  This method handles that for you.\n\n        \"\"\"\n        zip_stream = self.get_sdk_download_stream(\n            rest_api_id, api_gateway_stage=api_gateway_stage, sdk_type=sdk_type\n        )\n        tmpdir = tempfile.mkdtemp()\n        with open(os.path.join(tmpdir, 'sdk.zip'), 'wb') as f:\n            f.write(zip_stream.read())\n        tmp_extract = os.path.join(tmpdir, 'extracted')\n        with zipfile.ZipFile(os.path.join(tmpdir, 'sdk.zip')) as z:\n            z.extractall(tmp_extract)\n        # The extract zip dir will have a single directory:\n        #  ['apiGateway-js-sdk']\n        dirnames = os.listdir(tmp_extract)\n        if len(dirnames) == 1:\n            full_dirname = os.path.join(tmp_extract, dirnames[0])\n            if os.path.isdir(full_dirname):\n                final_dirname = 'chalice-%s-sdk' % sdk_type\n                full_renamed_name = os.path.join(tmp_extract, final_dirname)\n                os.rename(full_dirname, full_renamed_name)\n                shutil.move(full_renamed_name, output_dir)\n                return\n        raise RuntimeError(\n            \"The downloaded SDK had an unexpected directory structure: %s\"\n            % (', '.join(dirnames))\n        )\n\n    def get_sdk_download_stream(\n        self,\n        rest_api_id: str,\n        api_gateway_stage: str = DEFAULT_STAGE_NAME,\n        sdk_type: str = 'javascript',\n    ) -> IO[bytes]:\n        \"\"\"Generate an SDK for a given SDK.\n\n        Returns a file like object that streams a zip contents for the\n        generated SDK.\n\n        \"\"\"\n        response = self._client('apigateway').get_sdk(\n            restApiId=rest_api_id,\n            stageName=api_gateway_stage,\n            sdkType=sdk_type,\n        )\n        return response['body']\n\n    def subscribe_function_to_topic(\n        self, topic_arn: str, function_arn: str\n    ) -> str:\n        sns_client = self._client('sns')\n        response = sns_client.subscribe(\n            TopicArn=topic_arn, Protocol='lambda', Endpoint=function_arn\n        )\n        return response['SubscriptionArn']\n\n    def unsubscribe_from_topic(self, subscription_arn: str) -> None:\n        sns_client = self._client('sns')\n        sns_client.unsubscribe(SubscriptionArn=subscription_arn)\n\n    def verify_sns_subscription_current(\n        self, subscription_arn: str, topic_name: str, function_arn: str\n    ) -> bool:\n        \"\"\"Verify a subscription arn matches the topic and function name.\n\n        Given a subscription arn, verify that the associated topic name\n        and function arn match up to the parameters passed in.\n\n        \"\"\"\n        sns_client = self._client('sns')\n        try:\n            attributes = sns_client.get_subscription_attributes(\n                SubscriptionArn=subscription_arn\n            )['Attributes']\n            return (\n                # Splitting on ':' is safe because topic names can't have\n                # a ':' char.\n                attributes['TopicArn'].rsplit(':', 1)[1] == topic_name\n                and attributes['Endpoint'] == function_arn\n            )\n        except sns_client.exceptions.NotFoundException:\n            return False\n\n    def add_permission_for_sns_topic(\n        self, topic_arn: str, function_arn: str\n    ) -> None:\n        self._add_lambda_permission_if_needed(\n            source_arn=topic_arn,\n            function_arn=function_arn,\n            service_name='sns',\n        )\n\n    def remove_permission_for_sns_topic(\n        self, topic_arn: str, function_arn: str\n    ) -> None:\n        self._remove_lambda_permission_if_needed(\n            source_arn=topic_arn,\n            function_arn=function_arn,\n            service_name='sns',\n        )\n\n    def _build_source_arn_str(\n        self, region_name: str, account_id: str, rest_api_id: str\n    ) -> str:\n        source_arn = (\n            'arn:{partition}:execute-api:'\n            '{region_name}:{account_id}:{rest_api_id}/*'\n        ).format(\n            partition=self.partition_name,\n            region_name=region_name,\n            # Assuming same account id for lambda function and API gateway.\n            account_id=account_id,\n            rest_api_id=rest_api_id,\n        )\n        return source_arn\n\n    @property\n    def partition_name(self) -> str:\n        return self._client('apigateway').meta.partition\n\n    @property\n    def region_name(self) -> str:\n        return self._client('apigateway').meta.region_name\n\n    def iter_log_events(\n        self,\n        log_group_name: str,\n        start_time: Optional[datetime] = None,\n        interleaved: bool = True,\n    ) -> Iterator[CWLogEvent]:\n        logs = self._client('logs')\n        paginator = logs.get_paginator('filter_log_events')\n        pages = paginator.paginate(\n            logGroupName=log_group_name, interleaved=True\n        )\n        try:\n            yield from self._iter_log_messages(pages)\n        except logs.exceptions.ResourceNotFoundException:\n            # If the lambda function exists but has not been invoked yet,\n            # it's possible that the log group does not exist and we'll get\n            # a ResourceNotFoundException.  If this happens we return instead\n            # of propagating an exception back to the user.\n            pass\n\n    def _iter_log_messages(\n        self, pages: Iterable[Dict[str, Any]]\n    ) -> Iterator[CWLogEvent]:\n        for page in pages:\n            events = page['events']\n            for event in events:\n                # timestamp is modeled as a 'long', so we'll\n                # convert to a datetime to make it easier to use\n                # in python.\n                event['ingestionTime'] = self._convert_to_datetime(\n                    event['ingestionTime']\n                )\n                event['timestamp'] = self._convert_to_datetime(\n                    event['timestamp']\n                )\n                yield event\n\n    def _convert_to_datetime(self, integer_timestamp: int) -> datetime:\n        return datetime.utcfromtimestamp(integer_timestamp / 1000.0)\n\n    def filter_log_events(\n        self,\n        log_group_name: str,\n        start_time: Optional[datetime] = None,\n        next_token: Optional[str] = None,\n    ) -> LogEventsResponse:\n        logs = self._client('logs')\n        kwargs = {\n            'logGroupName': log_group_name,\n            'interleaved': True,\n        }\n        if start_time is not None:\n            kwargs['startTime'] = int(datetime2timestamp(start_time) * 1000)\n        if next_token is not None:\n            kwargs['nextToken'] = next_token\n        try:\n            response = logs.filter_log_events(**kwargs)\n        except logs.exceptions.ResourceNotFoundException:\n            # If there's no log messages yet then we'll just return\n            # an empty response.\n            return {'events': []}\n        # We want to convert the individual events that have integer\n        # types over to datetime objects so it's easier for us to\n        # work with.\n        self._convert_types_on_response(response)\n        return response\n\n    def _convert_types_on_response(self, response: Dict[str, Any]) -> None:\n        response['events'] = list(self._iter_log_messages([response]))\n\n    def _client(self, service_name: str) -> Any:\n        if service_name not in self._client_cache:\n            self._client_cache[service_name] = self._session.create_client(\n                service_name\n            )\n        return self._client_cache[service_name]\n\n    def add_permission_for_authorizer(\n        self,\n        rest_api_id: str,\n        function_arn: str,\n        random_id: Optional[str] = None,\n    ) -> None:\n        client = self._client('apigateway')\n        # This is actually a paginated operation, but botocore does not\n        # support this style of pagination right now.  The max authorizers\n        # for an API is 10, so we're ok for now.  We will need to circle\n        # back on this eventually.\n        authorizers = client.get_authorizers(restApiId=rest_api_id)\n        for authorizer in authorizers['items']:\n            if function_arn in authorizer['authorizerUri']:\n                authorizer_id = authorizer['id']\n                break\n        else:\n            raise ResourceDoesNotExistError(\n                \"Unable to find authorizer associated \"\n                \"with function ARN: %s\" % function_arn\n            )\n        parts = function_arn.split(':')\n        partition = parts[1]\n        region_name = parts[3]\n        account_id = parts[4]\n        function_name = parts[-1]\n        source_arn = \"arn:%s:execute-api:%s:%s:%s/authorizers/%s\" % (\n            partition,\n            region_name,\n            account_id,\n            rest_api_id,\n            authorizer_id,\n        )\n        dns_suffix = self.endpoint_dns_suffix('apigateway', region_name)\n        if random_id is None:\n            random_id = self._random_id()\n        self._client('lambda').add_permission(\n            Action='lambda:InvokeFunction',\n            FunctionName=function_name,\n            StatementId=random_id,\n            Principal=self.service_principal(\n                'apigateway', self.region_name, dns_suffix\n            ),\n            SourceArn=source_arn,\n        )\n\n    def get_or_create_rule_arn(\n        self,\n        rule_name: str,\n        schedule_expression: Optional[str] = None,\n        event_pattern: Optional[str] = None,\n        rule_description: Optional[str] = None,\n    ) -> str:\n        events = self._client('events')\n        # put_rule is idempotent so we can safely call it even if it already\n        # exists.\n        params = {'Name': rule_name}\n        if schedule_expression:\n            params['ScheduleExpression'] = schedule_expression\n        elif event_pattern:\n            params['EventPattern'] = event_pattern\n        else:\n            raise ValueError(\"schedule_expression or event_pattern required\")\n        if rule_description is not None:\n            params['Description'] = rule_description\n        rule_arn = events.put_rule(**params)\n        return rule_arn['RuleArn']\n\n    def delete_rule(self, rule_name: str) -> None:\n        events = self._client('events')\n\n        # In put_targets call, we have used Id='1'\n        events.remove_targets(Rule=rule_name, Ids=['1'])\n        events.delete_rule(Name=rule_name)\n\n    def connect_rule_to_lambda(\n        self, rule_name: str, function_arn: str\n    ) -> None:\n        events = self._client('events')\n        events.put_targets(\n            Rule=rule_name, Targets=[{'Id': '1', 'Arn': function_arn}]\n        )\n\n    def add_permission_for_cloudwatch_event(\n        self, rule_arn: str, function_arn: str\n    ) -> None:\n        self._add_lambda_permission_if_needed(\n            source_arn=rule_arn,\n            function_arn=function_arn,\n            service_name='events',\n        )\n\n    def connect_s3_bucket_to_lambda(\n        self,\n        bucket: str,\n        function_arn: str,\n        events: List[str],\n        prefix: OptStr = None,\n        suffix: OptStr = None,\n    ) -> None:\n        \"\"\"Configure S3 bucket to invoke a lambda function.\n\n        The S3 bucket must already have permission to invoke the\n        lambda function before you call this function, otherwise\n        the service will return an error.  You can add permissions\n        by using the ``add_permission_for_s3_event`` below.  The\n        ``events`` param matches the event strings supported by the\n        service.\n\n        This method also only supports a single prefix/suffix for now,\n        which is what's offered in the Lambda console.\n\n        \"\"\"\n        s3 = self._client('s3')\n        existing_config = s3.get_bucket_notification_configuration(\n            Bucket=bucket\n        )\n        # Because we're going to PUT this config back to S3, we need\n        # to remove `ResponseMetadata` because that's added in botocore\n        # and isn't a param of the put_bucket_notification_configuration.\n        existing_config.pop('ResponseMetadata', None)\n        existing_lambda_config = existing_config.get(\n            'LambdaFunctionConfigurations', []\n        )\n        single_config: Dict[str, Any] = {\n            'LambdaFunctionArn': function_arn,\n            'Events': events,\n        }\n        filter_rules = []\n        if prefix is not None:\n            filter_rules.append({'Name': 'Prefix', 'Value': prefix})\n        if suffix is not None:\n            filter_rules.append({'Name': 'Suffix', 'Value': suffix})\n        if filter_rules:\n            single_config['Filter'] = {'Key': {'FilterRules': filter_rules}}\n        new_config = self._merge_s3_notification_config(\n            existing_lambda_config, single_config\n        )\n        existing_config['LambdaFunctionConfigurations'] = new_config\n        s3.put_bucket_notification_configuration(\n            Bucket=bucket,\n            NotificationConfiguration=existing_config,\n        )\n\n    def _merge_s3_notification_config(\n        self, existing_config: List[Dict[str, Any]], new_config: Dict[str, Any]\n    ) -> List[Dict[str, Any]]:\n        # Add the new_config to the existing_config.\n        # We have to handle two cases:\n        # 1. There's an existing config associated with the lambda arn.\n        #    In this case we replace the specific lambda config with the\n        #    new_config.\n        # 2. The new_config isn't part of the existing_config.  In\n        #    this case we just add it to the end of the existing config.\n        final_config = []\n        added_config = False\n        for config in existing_config:\n            if config['LambdaFunctionArn'] != new_config['LambdaFunctionArn']:\n                final_config.append(config)\n            else:\n                # Case 1, replace the existing config.\n                final_config.append(new_config)\n                added_config = True\n        if not added_config:\n            # Case 2, add it to the end of the existing list of configs.\n            final_config.append(new_config)\n        return final_config\n\n    def add_permission_for_s3_event(\n        self, bucket: str, function_arn: str, account_id: str\n    ) -> None:\n        bucket_arn = 'arn:{partition}:s3:::{bucket}'.format(\n            partition=self.partition_name, bucket=bucket\n        )\n        self._add_lambda_permission_if_needed(\n            source_arn=bucket_arn,\n            function_arn=function_arn,\n            service_name='s3',\n            source_account=account_id,\n        )\n\n    def remove_permission_for_s3_event(\n        self, bucket: str, function_arn: str, account_id: str\n    ) -> None:\n        bucket_arn = 'arn:{partition}:s3:::{bucket}'.format(\n            partition=self.partition_name, bucket=bucket\n        )\n        self._remove_lambda_permission_if_needed(\n            source_arn=bucket_arn,\n            function_arn=function_arn,\n            service_name='s3',\n            source_account=account_id,\n        )\n\n    def disconnect_s3_bucket_from_lambda(\n        self, bucket: str, function_arn: str\n    ) -> None:\n        s3 = self._client('s3')\n        existing_config = s3.get_bucket_notification_configuration(\n            Bucket=bucket\n        )\n        existing_config.pop('ResponseMetadata', None)\n        existing_lambda_config = existing_config.get(\n            'LambdaFunctionConfigurations', []\n        )\n        new_lambda_config = []\n        for config in existing_lambda_config:\n            if config['LambdaFunctionArn'] == function_arn:\n                continue\n            new_lambda_config.append(config)\n        existing_config['LambdaFunctionConfigurations'] = new_lambda_config\n        s3.put_bucket_notification_configuration(\n            Bucket=bucket,\n            NotificationConfiguration=existing_config,\n        )\n\n    def _add_lambda_permission_if_needed(\n        self,\n        source_arn: str,\n        function_arn: str,\n        service_name: str,\n        source_account: Optional[str] = None,\n    ) -> None:\n        policy = self.get_function_policy(function_arn)\n        if self._policy_gives_access(policy, source_arn, service_name):\n            return\n        random_id = self._random_id()\n        dns_suffix = self.endpoint_dns_suffix_from_arn(source_arn)\n        kwargs = {\n            'Action': 'lambda:InvokeFunction',\n            'FunctionName': function_arn,\n            'StatementId': random_id,\n            'Principal': self.service_principal(\n                service_name, self.region_name, dns_suffix\n            ),\n            'SourceArn': source_arn,\n        }\n        if source_account is not None:\n            kwargs['SourceAccount'] = source_account\n        self._client('lambda').add_permission(**kwargs)\n\n    def _policy_gives_access(\n        self, policy: Dict[str, Any], source_arn: str, service_name: str\n    ) -> bool:\n        # Here's what a sample policy looks like after add_permission()\n        # has been previously called:\n        # {\n        #  \"Id\": \"default\",\n        #  \"Statement\": [\n        #   {\n        #    \"Action\": \"lambda:InvokeFunction\",\n        #    \"Condition\": {\n        #     \"ArnLike\": {\n        #       \"AWS:SourceArn\": <source_arn>\n        #     }\n        #    },\n        #    \"Effect\": \"Allow\",\n        #    \"Principal\": {\n        #     \"Service\": \"apigateway.amazonaws.com\"\n        #    },\n        #    \"Resource\": \"arn:aws:lambda:us-west-2:aid:function:name\",\n        #    \"Sid\": \"e4755709-067e-4254-b6ec-e7f9639e6f7b\"\n        #   }\n        #  ],\n        #  \"Version\": \"2012-10-17\"\n        # }\n        # So we need to check if there's a policy that looks like this.\n        for statement in policy.get('Statement', []):\n            if self._statement_gives_arn_access(\n                statement, source_arn, service_name\n            ):\n                return True\n        return False\n\n    def _statement_gives_arn_access(\n        self,\n        statement: Dict[str, Any],\n        source_arn: str,\n        service_name: str,\n        source_account: Optional[str] = None,\n    ) -> bool:\n        dns_suffix = self.endpoint_dns_suffix_from_arn(source_arn)\n        principal = self.service_principal(\n            service_name, self.region_name, dns_suffix\n        )\n        if not statement['Action'] == 'lambda:InvokeFunction':\n            return False\n        if (\n            statement.get('Condition', {})\n            .get('ArnLike', {})\n            .get('AWS:SourceArn', '')\n            != source_arn\n        ):\n            return False\n        if statement.get('Principal', {}).get('Service', '') != principal:\n            return False\n        if source_account is not None:\n            if (\n                statement.get('Condition', {})\n                .get('StringEquals', {})\n                .get('AWS:SourceAccount', '')\n                != source_account\n            ):\n                return False\n        # We're not checking the \"Resource\" key because we're assuming\n        # that lambda.get_policy() is returning the policy for the particular\n        # resource in question.\n        return True\n\n    def _remove_lambda_permission_if_needed(\n        self,\n        source_arn: str,\n        function_arn: str,\n        service_name: str,\n        source_account: Optional[str] = None,\n    ) -> None:\n        client = self._client('lambda')\n        policy = self.get_function_policy(function_arn)\n        for statement in policy.get('Statement', []):\n            kwargs = {\n                'statement': statement,\n                'source_arn': source_arn,\n                'service_name': service_name,\n            }\n            if source_account is not None:\n                kwargs['source_account'] = source_account\n            if self._statement_gives_arn_access(**kwargs):\n                client.remove_permission(\n                    FunctionName=function_arn,\n                    StatementId=statement['Sid'],\n                )\n\n    def create_lambda_event_source(\n        self,\n        event_source_arn: str,\n        function_name: str,\n        batch_size: int,\n        starting_position: Optional[str] = None,\n        maximum_batching_window_in_seconds: Optional[int] = 0,\n        maximum_concurrency: Optional[int] = None,\n    ) -> None:\n        lambda_client = self._client('lambda')\n        batch_window = maximum_batching_window_in_seconds\n        kwargs = {\n            'EventSourceArn': event_source_arn,\n            'FunctionName': function_name,\n            'BatchSize': batch_size,\n            'MaximumBatchingWindowInSeconds': batch_window,\n        }\n        if maximum_concurrency:\n            kwargs['ScalingConfig'] = {\n                'MaximumConcurrency': maximum_concurrency\n            }\n        if starting_position is not None:\n            kwargs['StartingPosition'] = starting_position\n        return self._call_client_method_with_retries(\n            lambda_client.create_event_source_mapping,\n            kwargs,\n            max_attempts=self.LAMBDA_CREATE_ATTEMPTS,\n        )['UUID']\n\n    def update_lambda_event_source(\n        self,\n        event_uuid: str,\n        batch_size: int,\n        maximum_batching_window_in_seconds: Optional[int] = 0,\n        maximum_concurrency: Optional[int] = None,\n    ) -> None:\n        lambda_client = self._client('lambda')\n        batch_window = maximum_batching_window_in_seconds\n        kwargs = {\n            'UUID': event_uuid,\n            'BatchSize': batch_size,\n            'MaximumBatchingWindowInSeconds': batch_window,\n        }\n        if maximum_concurrency:\n            kwargs['ScalingConfig'] = {\n                'MaximumConcurrency': maximum_concurrency\n            }\n        self._call_client_method_with_retries(\n            lambda_client.update_event_source_mapping,\n            kwargs,\n            max_attempts=10,\n            should_retry=self._is_settling_error,\n        )\n\n    def remove_lambda_event_source(self, event_uuid: str) -> None:\n        lambda_client = self._client('lambda')\n        self._call_client_method_with_retries(\n            lambda_client.delete_event_source_mapping,\n            {'UUID': event_uuid},\n            max_attempts=10,\n            should_retry=self._is_settling_error,\n        )\n\n    def verify_event_source_current(\n        self,\n        event_uuid: str,\n        resource_name: str,\n        service_name: str,\n        function_arn: str,\n    ) -> bool:\n        \"\"\"Check if the uuid matches the resource and function arn provided.\n\n        Given a uuid representing an event source mapping for a lambda\n        function, verify that the associated source arn\n        and function arn match up to the parameters passed in.\n\n        Instead of providing the event source arn, the resource name\n        is provided along with the service name.  For example, if we're\n        checking an SQS queue event source, the resource name would be\n        the queue name (e.g. ``myqueue``) and the service would be ``sqs``.\n\n        \"\"\"\n        client = self._client('lambda')\n        try:\n            attributes = client.get_event_source_mapping(UUID=event_uuid)\n            actual_arn = attributes['EventSourceArn']\n            arn_start, actual_name = actual_arn.rsplit(':', 1)\n            return bool(\n                actual_name == resource_name\n                and re.match(\"^arn:aws[a-z\\\\-]*:%s\" % service_name, arn_start)\n                and attributes['FunctionArn'] == function_arn\n            )\n        except client.exceptions.ResourceNotFoundException:\n            return False\n\n    def verify_event_source_arn_current(\n        self, event_uuid: str, event_source_arn: str, function_arn: str\n    ) -> bool:\n        \"\"\"Check if the uuid matches the event and function ARN.\n\n        This is similar to verify_event_source_current, except that you provide\n        an explicit event_source_arn here.  This is useful for cases where you\n        know the event source ARN or where you can't construct the event source\n        arn solely based on the resource_name and the service_name.\n\n        \"\"\"\n        client = self._client('lambda')\n        try:\n            attributes = client.get_event_source_mapping(UUID=event_uuid)\n        except client.exceptions.ResourceNotFoundException:\n            return False\n        return bool(\n            event_source_arn == attributes['EventSourceArn']\n            and function_arn == attributes['FunctionArn']\n        )\n\n    def create_websocket_api(self, name: str) -> str:\n        client = self._client('apigatewayv2')\n        return self._call_client_method_with_retries(\n            client.create_api,\n            kwargs={\n                'Name': name,\n                'ProtocolType': 'WEBSOCKET',\n                'RouteSelectionExpression': '$request.body.action',\n            },\n            max_attempts=10,\n            should_retry=self._is_settling_error,\n        )['ApiId']\n\n    def get_websocket_api_id(self, name: str) -> Optional[str]:\n        apis = self._client('apigatewayv2').get_apis()['Items']\n        for api in apis:\n            if api['Name'] == name:\n                return api['ApiId']\n        return None\n\n    def websocket_api_exists(self, api_id: str) -> bool:\n        \"\"\"Check if an API Gateway WEBSOCKET API exists.\"\"\"\n        client = self._client('apigatewayv2')\n        try:\n            client.get_api(ApiId=api_id)\n            return True\n        except client.exceptions.NotFoundException:\n            return False\n\n    def delete_websocket_api(self, api_id: str) -> None:\n        client = self._client('apigatewayv2')\n        try:\n            client.delete_api(ApiId=api_id)\n        except client.exceptions.NotFoundException:\n            raise ResourceDoesNotExistError(api_id)\n\n    def create_websocket_integration(\n        self,\n        api_id: str,\n        lambda_function: str,\n        handler_type: str,\n    ) -> str:\n        client = self._client('apigatewayv2')\n        return client.create_integration(\n            ApiId=api_id,\n            ConnectionType='INTERNET',\n            ContentHandlingStrategy='CONVERT_TO_TEXT',\n            Description=handler_type,\n            IntegrationType='AWS_PROXY',\n            IntegrationUri=lambda_function,\n        )['IntegrationId']\n\n    def create_websocket_route(\n        self, api_id: str, route_key: str, integration_id: str\n    ) -> None:\n        client = self._client('apigatewayv2')\n        client.create_route(\n            ApiId=api_id,\n            RouteKey=route_key,\n            RouteResponseSelectionExpression='$default',\n            Target='integrations/%s' % integration_id,\n        )\n\n    def delete_websocket_routes(self, api_id: str, routes: List[str]) -> None:\n        client = self._client('apigatewayv2')\n        for route_id in routes:\n            client.delete_route(\n                ApiId=api_id,\n                RouteId=route_id,\n            )\n\n    def delete_websocket_integrations(\n        self, api_id: str, integrations: Dict[str, str]\n    ) -> None:\n        client = self._client('apigatewayv2')\n        for integration_id in integrations:\n            client.delete_integration(\n                ApiId=api_id,\n                IntegrationId=integration_id,\n            )\n\n    def deploy_websocket_api(self, api_id: str) -> str:\n        client = self._client('apigatewayv2')\n        return client.create_deployment(\n            ApiId=api_id,\n        )['DeploymentId']\n\n    def get_websocket_routes(self, api_id: str) -> List[str]:\n        client = self._client('apigatewayv2')\n        return [\n            i['RouteId']\n            for i in client.get_routes(\n                ApiId=api_id,\n            )['Items']\n        ]\n\n    def get_websocket_integrations(self, api_id: str) -> List[str]:\n        client = self._client('apigatewayv2')\n        return [\n            item['IntegrationId']\n            for item in client.get_integrations(ApiId=api_id)['Items']\n        ]\n\n    def create_stage(\n        self, api_id: str, stage_name: str, deployment_id: str\n    ) -> None:\n        client = self._client('apigatewayv2')\n        client.create_stage(\n            ApiId=api_id,\n            StageName=stage_name,\n            DeploymentId=deployment_id,\n        )\n\n    def _call_client_method_with_retries(\n        self,\n        method: ClientMethod,\n        kwargs: Dict[str, Any],\n        max_attempts: int,\n        should_retry: Optional[Callable[[Exception], bool]] = None,\n        delay_time: int = DELAY_TIME,\n        retryable_exceptions: Optional[Sequence[Exception]] = None,\n    ) -> Dict[str, Any]:\n        attempts = 0\n        if should_retry is None:\n            should_retry = self._is_iam_role_related_error\n\n        if not retryable_exceptions:\n            client = self._client('lambda')\n            retryable_exceptions = (\n                # We're assuming that if we receive an\n                # InvalidParameterValueException, it's because the role we just\n                # created can't be used by Lambda so retry until it can be.\n                client.exceptions.InvalidParameterValueException,\n                client.exceptions.ResourceInUseException,\n            )\n\n        while True:\n            try:\n                response = method(**kwargs)\n            except retryable_exceptions as e:  # type: ignore\n                self._sleep(delay_time)\n                attempts += 1\n                if attempts >= max_attempts or not should_retry(e):\n                    raise\n                continue\n            return response\n\n    def _random_id(self) -> str:\n        return str(uuid.uuid4())\n"
  },
  {
    "path": "chalice/cdk/__init__.py",
    "content": "import warnings\ntry:\n    from chalice.cdk.construct import Chalice\nexcept ImportError:\n    warnings.warn('Unable to import the Chalice CDK construct due to missing '\n                  'dependencies.\\nYou can install these by running '\n                  \"'pip install \\\"chalice[cdk]\\\"'\")\n\n\n__all__ = ['Chalice']\n"
  },
  {
    "path": "chalice/cdk/construct.py",
    "content": "import json\nimport os\nimport uuid\nfrom typing import List, Dict, Optional, Any  # noqa\n\nfrom aws_cdk import (\n    aws_s3_assets as assets,\n    cloudformation_include,\n    aws_iam as iam,\n    aws_lambda as lambda_,\n)\n\ntry:\n    from aws_cdk.core import Construct\n    from aws_cdk import core as cdk  # noqa\nexcept ImportError:\n    import aws_cdk as cdk  # noqa\n    from constructs import Construct\n\nfrom chalice import api\n\n\nclass Chalice(Construct):\n    \"\"\"Chalice construct for CDK.\n\n    Packages the application into AWS SAM format and imports the resulting\n    template into the construct tree under the provided ``scope``.\n\n    \"\"\"\n    # pylint: disable=redefined-builtin\n    # The 'id' parameter name is CDK convention.\n    def __init__(self,\n                 scope,                       # type: Construct\n                 id,                          # type: str\n                 source_dir,                  # type: str\n                 stage_config=None,           # type: Optional[Dict[str, Any]]\n                 preserve_logical_ids=True,   # type: bool\n                 **kwargs                     # type: Dict[str, Any]\n                 ):\n        # type: (...) -> None\n        \"\"\"Initialize Chalice construct.\n\n        :param str source_dir: Path to Chalice application source code.\n        :param dict stage_config: Chalice stage configuration.\n            The configuration object should have the same structure as Chalice\n            JSON stage configuration.\n        :param bool preserve_logical_ids: Whether the resources should have\n            the same logical IDs in the resulting CDK template as they did in\n            the original CloudFormation template file. If you're vending a\n            Construct using cdk-chalice, make sure to pass this as ``False``.\n            Note: regardless of whether this option is true or false, the\n            :attr:`sam_template`'s ``get_resource`` and related methods always\n            uses the original logical ID of the resource/element, as specified\n            in the template file.\n        :raises `ChaliceError`: Error packaging the Chalice application.\n        \"\"\"\n        super(Chalice, self).__init__(scope, id, **kwargs)\n\n        #: (:class:`str`) Path to Chalice application source code.\n        self.source_dir = os.path.abspath(source_dir)\n\n        #: (:class:`str`) Chalice stage name.\n        #: It is automatically assigned the encompassing CDK ``scope``'s name.\n        self.stage_name = scope.to_string()\n\n        #: (:class:`dict`) Chalice stage configuration.\n        #: The object has the same structure as Chalice JSON stage\n        #: configuration.\n        self.stage_config = stage_config\n\n        chalice_out_dir = os.path.join(os.getcwd(), 'chalice.out')\n        package_id = uuid.uuid4().hex\n        self._sam_package_dir = os.path.join(chalice_out_dir, package_id)\n\n        self._package_app()\n        sam_template_filename = self._generate_sam_template_with_assets(\n            chalice_out_dir, package_id)\n\n        #: (:class:`aws_cdk.cloudformation_include.CfnInclude`) AWS SAM\n        #: template updated with AWS CDK values where applicable. Can be\n        #: used to reference, access, and customize resources generated\n        #: by `chalice package` commandas CDK native objects.\n        self.sam_template = cloudformation_include.CfnInclude(\n            self, 'ChaliceApp', template_file=sam_template_filename,\n            preserve_logical_ids=preserve_logical_ids)\n        self._function_cache = {}  # type: Dict[str, lambda_.IFunction]\n        self._role_cache = {}  # type: Dict[str, iam.IRole]\n\n    def _package_app(self):\n        # type: () -> None\n        api.package_app(\n            project_dir=self.source_dir,\n            output_dir=self._sam_package_dir,\n            stage=self.stage_name,\n            chalice_config=self.stage_config,\n        )\n\n    def _generate_sam_template_with_assets(self, chalice_out_dir, package_id):\n        # type: (str, str) -> str\n        deployment_zip_path = os.path.join(\n            self._sam_package_dir, 'deployment.zip')\n        sam_deployment_asset = assets.Asset(\n            self, 'ChaliceAppCode', path=deployment_zip_path)\n        sam_template_path = os.path.join(self._sam_package_dir, 'sam.json')\n        sam_template_with_assets_path = os.path.join(\n            chalice_out_dir, '%s.sam_with_assets.json' % package_id)\n\n        with open(sam_template_path) as sam_template_file:\n            sam_template = json.load(sam_template_file)\n            for function in self._filter_resources(\n                    sam_template, 'AWS::Serverless::Function'):\n                function['Properties']['CodeUri'] = {\n                    'Bucket': sam_deployment_asset.s3_bucket_name,\n                    'Key': sam_deployment_asset.s3_object_key\n                }\n            managed_layers = self._filter_resources(\n                sam_template, 'AWS::Serverless::LayerVersion')\n            if len(managed_layers) == 1:\n                layer_filename = os.path.join(\n                    self._sam_package_dir, 'layer-deployment.zip')\n                layer_asset = assets.Asset(\n                    self, 'ChaliceManagedLayer', path=layer_filename)\n                managed_layers[0]['Properties']['ContentUri'] = {\n                    'Bucket': layer_asset.s3_bucket_name,\n                    'Key': layer_asset.s3_object_key\n                }\n        with open(sam_template_with_assets_path, 'w') as f:\n            f.write(json.dumps(sam_template, indent=2))\n        return sam_template_with_assets_path\n\n    def _filter_resources(self, template, resource_type):\n        # type: (Dict[str, Any], str) -> List[Dict[str, Any]]\n        return [resource for resource in template['Resources'].values()\n                if resource['Type'] == resource_type]\n\n    def get_resource(self, resource_name):\n        # type: (str) -> cdk.core.CfnResource\n        return self.sam_template.get_resource(resource_name)\n\n    def get_role(self, role_name):\n        # type: (str) -> iam.IRole\n        if role_name not in self._role_cache:\n            cfn_role = self.sam_template.get_resource(role_name)\n            # Pylint is incorrectly identifying this as a static method call\n            # but it's actually decorated as a @builtins.classmethod method.\n            # pylint: disable=no-value-for-parameter\n            role = iam.Role.from_role_arn(self, role_name, cfn_role.attr_arn)\n            self._role_cache[role_name] = role\n        return self._role_cache[role_name]\n\n    def get_function(self, function_name):\n        # type: (str) -> lambda_.IFunction\n        if function_name not in self._function_cache:\n            cfn_lambda = self.sam_template.get_resource(function_name)\n            arn_ref = cfn_lambda.get_att('Arn')\n            # Pylint is incorrectly identifying this as a static method call\n            # but it's actually decorated as a @builtins.classmethod method.\n            # pylint: disable=no-value-for-parameter\n            function = lambda_.Function.from_function_arn(\n                self, function_name, arn_ref.to_string())\n            self._function_cache[function_name] = function\n        return self._function_cache[function_name]\n\n    def add_environment_variable(self, key, value, function_name):\n        # type: (str, str, str) -> None\n        cfn_function = self.sam_template.get_resource(function_name)\n        cfn_function.add_override(\n            'Properties.Environment.Variables.%s' % key, value)\n"
  },
  {
    "path": "chalice/cli/__init__.py",
    "content": "\"\"\"Command line interface for chalice.\n\nContains commands for deploying chalice.\n\n\"\"\"\nfrom __future__ import annotations\nimport logging\nimport os\nimport platform\nimport sys\nimport tempfile\nimport shutil\nimport traceback\nimport functools\nimport json\n\nimport botocore.exceptions\nimport click\nfrom typing import Dict, Any, Optional, cast  # noqa\n\nfrom chalice import __version__ as chalice_version\nfrom chalice.app import Chalice  # noqa\nfrom chalice.awsclient import TypedAWSClient\nfrom chalice.awsclient import ReadTimeout\nfrom chalice.cli.factory import CLIFactory\nfrom chalice.cli.factory import NoSuchFunctionError\nfrom chalice.config import Config  # noqa\nfrom chalice.logs import display_logs, LogRetrieveOptions\nfrom chalice.utils import create_zip_file\nfrom chalice.deploy.validate import validate_routes, validate_python_version\nfrom chalice.deploy.validate import ExperimentalFeatureError\nfrom chalice.utils import UI, serialize_to_json\nfrom chalice.constants import DEFAULT_STAGE_NAME\nfrom chalice.local import LocalDevServer  # noqa\nfrom chalice.constants import DEFAULT_HANDLER_NAME\nfrom chalice.invoke import UnhandledLambdaError\nfrom chalice.deploy.swagger import TemplatedSwaggerGenerator\nfrom chalice.deploy.planner import PlanEncoder\nfrom chalice.deploy.appgraph import ApplicationGraphBuilder, GraphPrettyPrint\nfrom chalice.cli import newproj\n\n\ndef _configure_logging(level, format_string=None):\n    # type: (int, Optional[str]) -> None\n    if format_string is None:\n        format_string = \"%(asctime)s %(name)s [%(levelname)s] %(message)s\"\n    logger = logging.getLogger('')\n    logger.setLevel(level)\n    handler = logging.StreamHandler()\n    handler.setLevel(level)\n    formatter = logging.Formatter(format_string)\n    handler.setFormatter(formatter)\n    logger.addHandler(handler)\n\n\ndef get_system_info():\n    # type: () -> str\n    python_info = \"python {}.{}.{}\".format(sys.version_info[0],\n                                           sys.version_info[1],\n                                           sys.version_info[2])\n    platform_system = platform.system().lower()\n    platform_release = platform.release()\n    platform_info = \"{} {}\".format(platform_system, platform_release)\n    return \"{}, {}\".format(python_info, platform_info)\n\n\n@click.group()\n@click.version_option(version=chalice_version,\n                      message='%(prog)s %(version)s, {}'\n                      .format(get_system_info()))\n@click.option('--project-dir',\n              help='The project directory path (absolute or relative).'\n                   'Defaults to CWD')\n@click.option('--debug/--no-debug',\n              default=False,\n              help='Print debug logs to stderr.')\n@click.pass_context\ndef cli(ctx, project_dir, debug=False):\n    # type: (click.Context, str, bool) -> None\n    if project_dir is None:\n        project_dir = os.getcwd()\n    elif not os.path.isabs(project_dir):\n        project_dir = os.path.abspath(project_dir)\n    if debug is True:\n        _configure_logging(logging.DEBUG)\n    _configure_cli_env_vars()\n    ctx.obj['project_dir'] = project_dir\n    ctx.obj['debug'] = debug\n    ctx.obj['factory'] = CLIFactory(project_dir, debug, environ=os.environ)\n    os.chdir(project_dir)\n\n\ndef _configure_cli_env_vars():\n    # type: () -> None\n    # This will set chalice specific env vars so users can detect if\n    # we're running a Chalice CLI command.  This is useful if you want\n    # conditional behavior only when we're actually running in Lambda\n    # in your app.py file.\n    os.environ['AWS_CHALICE_CLI_MODE'] = 'true'\n\n\n@cli.command()\n@click.option('--host', default='127.0.0.1')\n@click.option('--port', default=8000, type=click.INT)\n@click.option('--stage', default=DEFAULT_STAGE_NAME,\n              help='Name of the Chalice stage for the local server to use.')\n@click.option('--autoreload/--no-autoreload',\n              default=True,\n              help='Automatically restart server when code changes.')\n@click.pass_context\ndef local(ctx, host='127.0.0.1', port=8000, stage=DEFAULT_STAGE_NAME,\n          autoreload=True):\n    # type: (click.Context, str, int, str, bool) -> None\n    factory = ctx.obj['factory']  # type: CLIFactory\n    from chalice.cli import reloader\n    # We don't create the server here because that will bind the\n    # socket and we only want to do this in the worker process.\n    server_factory = functools.partial(\n        create_local_server, factory, host, port, stage)\n    # When running `chalice local`, a stdout logger is configured\n    # so you'll see the same stdout logging as you would when\n    # running in lambda.  This is configuring the root logger.\n    # The app-specific logger (app.log) will still continue\n    # to work.\n    logging.basicConfig(\n        stream=sys.stdout, level=logging.INFO, format='%(message)s')\n    if autoreload:\n        project_dir = factory.create_config_obj(\n            chalice_stage_name=stage).project_dir\n        rc = reloader.run_with_reloader(\n            server_factory, os.environ, project_dir)\n        # Click doesn't sys.exit() with the RC this function.  The\n        # recommended way to do this is to use sys.exit() directly,\n        # see: https://github.com/pallets/click/issues/747\n        sys.exit(rc)\n    run_local_server(factory, host, port, stage)\n\n\ndef create_local_server(factory, host, port, stage):\n    # type: (CLIFactory, str, int, str) -> LocalDevServer\n    config = factory.create_config_obj(\n        chalice_stage_name=stage\n    )\n    app_obj = config.chalice_app\n    # Check that `chalice deploy` would let us deploy these routes, otherwise\n    # there is no point in testing locally.\n    routes = config.chalice_app.routes\n    validate_routes(routes)\n    server = factory.create_local_server(app_obj, config, host, port)\n    return server\n\n\ndef run_local_server(factory, host, port, stage):\n    # type: (CLIFactory, str, int, str) -> None\n    server = create_local_server(factory, host, port, stage)\n    server.serve_forever()\n\n\n@cli.command()\n@click.option('--autogen-policy/--no-autogen-policy',\n              default=None,\n              help='Automatically generate IAM policy for app code.')\n@click.option('--profile', help='Override profile at deploy time.')\n@click.option('--api-gateway-stage',\n              help='Name of the API gateway stage to deploy to.')\n@click.option('--stage', default=DEFAULT_STAGE_NAME,\n              help=('Name of the Chalice stage to deploy to. '\n                    'Specifying a new chalice stage will create '\n                    'an entirely new set of AWS resources.'))\n@click.option('--connection-timeout',\n              type=int,\n              help=('Overrides the default botocore connection '\n                    'timeout.'))\n@click.pass_context\ndef deploy(ctx, autogen_policy, profile, api_gateway_stage, stage,\n           connection_timeout):\n    # type: (click.Context, Optional[bool], str, str, str, int) -> None\n    factory = ctx.obj['factory']  # type: CLIFactory\n    factory.profile = profile\n    config = factory.create_config_obj(\n        chalice_stage_name=stage, autogen_policy=autogen_policy,\n        api_gateway_stage=api_gateway_stage,\n    )\n    session = factory.create_botocore_session(\n        connection_timeout=connection_timeout)\n    ui = UI()\n    d = factory.create_default_deployer(session=session,\n                                        config=config,\n                                        ui=ui)\n    deployed_values = d.deploy(config, chalice_stage_name=stage)\n    reporter = factory.create_deployment_reporter(ui=ui)\n    reporter.display_report(deployed_values)\n\n\n@cli.group()\ndef dev():\n    # type: () -> None\n    \"\"\"Development and debugging commands for chalice.\n\n    All the commands under the \"chalice dev\" namespace are provided\n    to help chalice developers introspect the internals of chalice.\n    They are also useful for users to better understand the chalice\n    deployment process.\n\n    These commands are provided for informational purposes only.\n    There is NO guarantee of backwards compatibility for any\n    \"chalice dev\" commands.  Do not rely on the output of these commands.\n    These commands allow introspection of chalice internals, and the\n    internals of chalice are subject to change as needed.\n\n    \"\"\"\n\n\n@dev.command()\n@click.option('--autogen-policy/--no-autogen-policy',\n              default=None,\n              help='Automatically generate IAM policy for app code.')\n@click.option('--profile', help='Override profile at deploy time.')\n@click.option('--api-gateway-stage',\n              help='Name of the API gateway stage to deploy to.')\n@click.option('--stage', default=DEFAULT_STAGE_NAME,\n              help=('Name of the Chalice stage to deploy to. '\n                    'Specifying a new chalice stage will create '\n                    'an entirely new set of AWS resources.'))\n@click.pass_context\ndef plan(ctx, autogen_policy, profile, api_gateway_stage, stage):\n    # type: (click.Context, Optional[bool], str, str, str) -> None\n    \"\"\"Generate and display deployment plan.\n\n    This command will calculate and pretty print the deployment plan\n    without actually executing the plan.  It's primarily used to better\n    understand the chalice deployment process.\n\n    \"\"\"\n    factory = ctx.obj['factory']  # type: CLIFactory\n    factory.profile = profile\n    config = factory.create_config_obj(\n        chalice_stage_name=stage, autogen_policy=autogen_policy,\n        api_gateway_stage=api_gateway_stage,\n    )\n    session = factory.create_botocore_session()\n    ui = UI()\n    d = factory.create_plan_only_deployer(\n        session=session, config=config, ui=ui)\n    d.deploy(config, chalice_stage_name=stage)\n\n\n@dev.command()\n@click.option('--autogen-policy/--no-autogen-policy',\n              default=None,\n              help='Automatically generate IAM policy for app code.')\n@click.option('--profile', help='Override profile at deploy time.')\n@click.option('--api-gateway-stage',\n              help='Name of the API gateway stage to deploy to.')\n@click.option('--stage', default=DEFAULT_STAGE_NAME,\n              help=('Name of the Chalice stage to deploy to. '\n                    'Specifying a new chalice stage will create '\n                    'an entirely new set of AWS resources.'))\n@click.pass_context\ndef appgraph(ctx, autogen_policy, profile, api_gateway_stage, stage):\n    # type: (click.Context, Optional[bool], str, str, str) -> None\n    \"\"\"Generate and display the application graph.\"\"\"\n    factory = ctx.obj['factory']  # type: CLIFactory\n    factory.profile = profile\n    config = factory.create_config_obj(\n        chalice_stage_name=stage, autogen_policy=autogen_policy,\n        api_gateway_stage=api_gateway_stage,\n    )\n    graph_build = ApplicationGraphBuilder()\n    graph = graph_build.build(config, stage)\n    ui = UI()\n    GraphPrettyPrint(ui).display_graph(graph)\n\n\n@cli.command('invoke')\n@click.option('-n', '--name', metavar='NAME', required=True,\n              help=('The name of the function to invoke. '\n                    'This is the logical name of the function. If the '\n                    'function is decorated by app.route use the name '\n                    'api_handler instead.'))\n@click.option('--profile', metavar='PROFILE',\n              help='Override profile at deploy time.')\n@click.option('--stage', metavar='STAGE', default=DEFAULT_STAGE_NAME,\n              help=('Name of the Chalice stage to deploy to. '\n                    'Specifying a new chalice stage will create '\n                    'an entirely new set of AWS resources.'))\n@click.pass_context\ndef invoke(ctx, name, profile, stage):\n    # type: (click.Context, str, str, str) -> None\n    \"\"\"Invoke the deployed lambda function NAME.\n\n    Reads payload from STDIN.\n    \"\"\"\n    factory = ctx.obj['factory']  # type: CLIFactory\n    factory.profile = profile\n\n    try:\n        invoke_handler = factory.create_lambda_invoke_handler(name, stage)\n        payload = factory.create_stdin_reader().read()\n        invoke_handler.invoke(payload)\n    except NoSuchFunctionError as e:\n        err = click.ClickException(\n            \"could not find a lambda function named %s.\" % e.name)\n        err.exit_code = 2\n        raise err\n    except botocore.exceptions.ClientError as e:\n        error = e.response['Error']\n        err = click.ClickException(\n            \"got '%s' exception back from Lambda\\n%s\"\n            % (error['Code'], error['Message']))\n        err.exit_code = 1\n        raise err\n    except UnhandledLambdaError:\n        err = click.ClickException(\n            \"Unhandled exception in Lambda function, details above.\")\n        err.exit_code = 1\n        raise err\n    except ReadTimeout as e:\n        err = click.ClickException(e.message)\n        err.exit_code = 1\n        raise err\n\n\n@cli.command('delete')\n@click.option('--profile', help='Override profile at deploy time.')\n@click.option('--stage', default=DEFAULT_STAGE_NAME,\n              help='Name of the Chalice stage to delete.')\n@click.pass_context\ndef delete(ctx, profile, stage):\n    # type: (click.Context, str, str) -> None\n    factory = ctx.obj['factory']  # type: CLIFactory\n    factory.profile = profile\n    config = factory.create_config_obj(chalice_stage_name=stage)\n    session = factory.create_botocore_session()\n    d = factory.create_deletion_deployer(session=session, ui=UI())\n    d.deploy(config, chalice_stage_name=stage)\n\n\n@cli.command()\n@click.option('--num-entries', default=None, type=int,\n              help='Max number of log entries to show.')\n@click.option('--include-lambda-messages/--no-include-lambda-messages',\n              default=False,\n              help='Controls whether or not lambda log messages are included.')\n@click.option('--stage', default=DEFAULT_STAGE_NAME,\n              help='Name of the Chalice stage to get logs for.')\n@click.option('-n', '--name',\n              help='The name of the lambda function to retrieve logs from.',\n              default=DEFAULT_HANDLER_NAME)\n@click.option('-s', '--since',\n              help=('Only display logs since the provided time.  If the '\n                    '-f/--follow option is specified, then this value will '\n                    'default to 10 minutes from the current time.  Otherwise '\n                    'by default all log messages are displayed.  This value '\n                    'can either be an ISO8601 formatted timestamp or a '\n                    'relative time.  For relative times provide a number '\n                    'and a single unit.  Units can be \"s\" for seconds, '\n                    '\"m\" for minutes, \"h\" for hours, \"d\" for days, and \"w\" '\n                    'for weeks.  For example \"5m\" would indicate to display '\n                    'logs starting five minutes in the past.'),\n              default=None)\n@click.option('-f', '--follow/--no-follow',\n              default=False,\n              help=('Continuously poll for new log messages.  Note that this '\n                    'is a best effort attempt, and in certain cases can '\n                    'miss log messages.  This option is intended for '\n                    'interactive usage only.'))\n@click.option('--profile', help='The profile to use for fetching logs.')\n@click.pass_context\ndef logs(ctx, num_entries, include_lambda_messages, stage,\n         name, since, follow, profile):\n    # type: (click.Context, int, bool, str, str, str, bool, str) -> None\n    factory = ctx.obj['factory']  # type: CLIFactory\n    factory.profile = profile\n    config = factory.create_config_obj(stage, False)\n    deployed = config.deployed_resources(stage)\n    if name in deployed.resource_names():\n        lambda_arn = deployed.resource_values(name)['lambda_arn']\n        session = factory.create_botocore_session()\n        retriever = factory.create_log_retriever(\n            session, lambda_arn, follow)\n        options = LogRetrieveOptions.create(\n            max_entries=num_entries,\n            since=since,\n            include_lambda_messages=include_lambda_messages,\n        )\n        display_logs(retriever, sys.stdout, options)\n\n\n@cli.command('gen-policy')\n@click.option('--filename',\n              help='The filename to analyze.  Otherwise app.py is assumed.')\n@click.pass_context\ndef gen_policy(ctx, filename):\n    # type: (click.Context, str) -> None\n    from chalice import policy\n    if filename is None:\n        filename = os.path.join(ctx.obj['project_dir'], 'app.py')\n    if not os.path.isfile(filename):\n        click.echo(\"App file does not exist: %s\" % filename, err=True)\n        raise click.Abort()\n    with open(filename) as f:\n        contents = f.read()\n        generated = policy.policy_from_source_code(contents)\n        click.echo(serialize_to_json(generated))\n\n\n@cli.command('new-project')\n@click.argument('project_name', required=False)\n@click.option('--profile', required=False)\n@click.option('-t', '--project-type', required=False, default='legacy')\n@click.pass_context\ndef new_project(ctx, project_name, profile, project_type):\n    # type: (click.Context, str, str, str) -> None\n    if project_name is None:\n        prompter = ctx.obj.get('prompter', newproj.getting_started_prompt)\n        answers = prompter()\n        project_name = answers['project_name']\n        project_type = answers['project_type']\n    if os.path.isdir(project_name):\n        click.echo(\"Directory already exists: %s\" % project_name, err=True)\n        raise click.Abort()\n    newproj.create_new_project_skeleton(\n        project_name, project_type=project_type)\n    validate_python_version(Config.create())\n    click.echo(\"Your project has been generated in ./%s\" % project_name)\n\n\n@cli.command('url')\n@click.option('--stage', default=DEFAULT_STAGE_NAME,\n              help='Name of the Chalice stage to get the deployed URL for.')\n@click.pass_context\ndef url(ctx, stage):\n    # type: (click.Context, str) -> None\n    factory = ctx.obj['factory']  # type: CLIFactory\n    config = factory.create_config_obj(stage)\n    deployed = config.deployed_resources(stage)\n    if deployed is not None and 'rest_api' in deployed.resource_names():\n        click.echo(deployed.resource_values('rest_api')['rest_api_url'])\n    else:\n        e = click.ClickException(\n            \"Could not find a record of a Rest API in chalice stage: '%s'\"\n            % stage)\n        e.exit_code = 2\n        raise e\n\n\n@cli.command('generate-sdk')\n@click.option('--sdk-type', default='javascript',\n              type=click.Choice(['javascript']))\n@click.option('--stage', default=DEFAULT_STAGE_NAME,\n              help='Name of the Chalice stage to generate an SDK for.')\n@click.argument('outdir')\n@click.pass_context\ndef generate_sdk(ctx, sdk_type, stage, outdir):\n    # type: (click.Context, str, str, str) -> None\n    factory = ctx.obj['factory']  # type: CLIFactory\n    config = factory.create_config_obj(stage)\n    session = factory.create_botocore_session()\n    client = TypedAWSClient(session)\n    deployed = config.deployed_resources(stage)\n    if deployed is not None and 'rest_api' in deployed.resource_names():\n        rest_api_id = deployed.resource_values('rest_api')['rest_api_id']\n        api_gateway_stage = config.api_gateway_stage\n        client.download_sdk(rest_api_id, outdir,\n                            api_gateway_stage=api_gateway_stage,\n                            sdk_type=sdk_type)\n    else:\n        click.echo(\"Could not find API ID, has this application \"\n                   \"been deployed?\", err=True)\n        raise click.Abort()\n\n\n@cli.command('generate-models')\n@click.option('--stage', default=DEFAULT_STAGE_NAME,\n              help=\"Chalice Stage for which to generate models.\")\n@click.pass_context\ndef generate_models(ctx, stage):\n    # type: (click.Context, str) -> None\n    \"\"\"Generate a model from Chalice routes.\n\n    Currently only supports generating Swagger 2.0 models.\n    \"\"\"\n    factory = ctx.obj['factory']  # type: CLIFactory\n    config = factory.create_config_obj(stage)\n    if not config.chalice_app.routes:\n        click.echo('No REST API found to generate model from.')\n        raise click.Abort()\n    swagger_generator = TemplatedSwaggerGenerator()\n    model = swagger_generator.generate_swagger(\n        config.chalice_app,\n    )\n    ui = UI()\n    ui.write(json.dumps(model, indent=4, cls=PlanEncoder))\n    ui.write('\\n')\n\n\n@cli.command('package')\n@click.option('--pkg-format', default='cloudformation',\n              help=('Specify the provisioning engine to use for '\n                    'template output. Chalice supports both '\n                    'CloudFormation and Terraform. Default '\n                    'is CloudFormation.'),\n              type=click.Choice(['cloudformation', 'terraform']))\n@click.option('--stage', default=DEFAULT_STAGE_NAME,\n              help=\"Chalice Stage to package.\")\n@click.option('--single-file', is_flag=True,\n              default=False,\n              help=(\"Create a single packaged file. \"\n                    \"By default, the 'out' argument \"\n                    \"specifies a directory in which the \"\n                    \"package assets will be placed.  If \"\n                    \"this argument is specified, a single \"\n                    \"zip file will be created instead. CloudFormation Only.\"))\n@click.option('--merge-template',\n              help=('Specify a JSON or YAML template to be merged '\n                    'into the generated template. This is useful '\n                    'for adding resources to a Chalice template or '\n                    'modify values in the template. CloudFormation Only.'))\n@click.option('--template-format', default='json',\n              type=click.Choice(['json', 'yaml'], case_sensitive=False),\n              help=('Specify if the generated template should be serialized '\n                    'as either JSON or YAML.  CloudFormation only.'))\n@click.option('--profile', help='Override profile at packaging time.')\n@click.argument('out')\n@click.pass_context\ndef package(ctx, single_file, stage, merge_template,\n            out, pkg_format, template_format, profile):\n    # type: (click.Context, bool, str, str, str, str, str, str) -> None\n    factory = ctx.obj['factory']  # type: CLIFactory\n    factory.profile = profile\n    config = factory.create_config_obj(stage)\n    options = factory.create_package_options()\n    packager = factory.create_app_packager(config, options,\n                                           pkg_format,\n                                           template_format,\n                                           merge_template)\n    if pkg_format == 'terraform' and (merge_template or\n                                      single_file or\n                                      template_format != 'json'):\n        # I don't see any reason we couldn't support --single-file for\n        # terraform if we wanted to.\n        click.echo((\n            \"Terraform format does not support \"\n            \"--merge-template, --single-file, or --template-format\"))\n        raise click.Abort()\n\n    if single_file:\n        dirname = tempfile.mkdtemp()\n        try:\n            packager.package_app(config, dirname, stage)\n            create_zip_file(source_dir=dirname, outfile=out)\n        finally:\n            shutil.rmtree(dirname)\n    else:\n        packager.package_app(config, out, stage)\n\n\n@cli.command('generate-pipeline')\n@click.option('--pipeline-version',\n              default='v1',\n              type=click.Choice(['v1', 'v2']),\n              help='Which version of the pipeline template to generate.')\n@click.option('-i', '--codebuild-image',\n              help=(\"Specify default codebuild image to use.  \"\n                    \"This option must be provided when using a python \"\n                    \"version besides 2.7.\"))\n@click.option('-s', '--source', default='codecommit',\n              type=click.Choice(['codecommit', 'github']),\n              help=(\"Specify the input source.  The default value of \"\n                    \"'codecommit' will create a CodeCommit repository \"\n                    \"for you.  The 'github' value allows you to \"\n                    \"reference an existing GitHub repository.\"))\n@click.option('-b', '--buildspec-file',\n              help=(\"Specify path for buildspec.yml file. \"\n                    \"By default, the build steps are included in the \"\n                    \"generated cloudformation template.  If this option \"\n                    \"is provided, a buildspec.yml will be generated \"\n                    \"as a separate file and not included in the cfn \"\n                    \"template.  This allows you to make changes to how \"\n                    \"the project is built without having to redeploy \"\n                    \"a CloudFormation template. This file should be \"\n                    \"named 'buildspec.yml' and placed in the root \"\n                    \"directory of your app.\"))\n@click.argument('filename')\n@click.pass_context\ndef generate_pipeline(ctx, pipeline_version, codebuild_image, source,\n                      buildspec_file, filename):\n    # type: (click.Context, str, str, str, str, str) -> None\n    \"\"\"Generate a cloudformation template for a starter CD pipeline.\n\n    This command will write a starter cloudformation template to\n    the filename you provide.  It contains a CodeCommit repo,\n    a CodeBuild stage for packaging your chalice app, and a\n    CodePipeline stage to deploy your application using cloudformation.\n\n    You can use any AWS SDK or the AWS CLI to deploy this stack.\n    Here's an example using the AWS CLI:\n\n        \\b\n        $ chalice generate-pipeline pipeline.json\n        $ aws cloudformation deploy --stack-name mystack \\b\n            --template-file pipeline.json --capabilities CAPABILITY_IAM\n    \"\"\"\n    from chalice import pipeline\n    factory = ctx.obj['factory']  # type: CLIFactory\n    config = factory.create_config_obj()\n    p = cast(pipeline.BasePipelineTemplate, None)\n    if pipeline_version == 'v1':\n        p = pipeline.CreatePipelineTemplateLegacy()\n    else:\n        p = pipeline.CreatePipelineTemplateV2()\n    params = pipeline.PipelineParameters(\n        app_name=config.app_name,\n        lambda_python_version=config.lambda_python_version,\n        codebuild_image=codebuild_image,\n        code_source=source,\n        pipeline_version=pipeline_version,\n    )\n    output = p.create_template(params)\n    if buildspec_file:\n        extractor = pipeline.BuildSpecExtractor()\n        buildspec_contents = extractor.extract_buildspec(output)\n        with open(buildspec_file, 'w') as f:\n            f.write(buildspec_contents)\n    with open(filename, 'w') as f:\n        f.write(serialize_to_json(output))\n\n\ndef main():\n    # type: () -> int\n    # click's dynamic attrs will allow us to pass through\n    # 'obj' via the context object, so we're ignoring\n    # these error messages from pylint because we know it's ok.\n    # pylint: disable=unexpected-keyword-arg,no-value-for-parameter\n    try:\n        return cli(obj={})\n    except botocore.exceptions.NoRegionError:\n        click.echo(\"No region configured. \"\n                   \"Either export the AWS_DEFAULT_REGION \"\n                   \"environment variable or set the \"\n                   \"region value in our ~/.aws/config file.\", err=True)\n        return 2\n    except ExperimentalFeatureError as e:\n        click.echo(str(e))\n        return 2\n    except Exception:\n        click.echo(traceback.format_exc(), err=True)\n        return 2\n"
  },
  {
    "path": "chalice/cli/factory.py",
    "content": "from __future__ import annotations\nimport sys\nimport os\nimport json\nimport importlib\nimport logging\nimport functools\n\nimport click\nfrom botocore.config import Config as BotocoreConfig\nfrom botocore.session import Session\nfrom typing import Any, Optional, Dict, MutableMapping, cast  # noqa\n\nfrom chalice import __version__ as chalice_version\nfrom chalice.awsclient import TypedAWSClient\nfrom chalice.app import Chalice  # noqa\nfrom chalice.config import Config\nfrom chalice.config import DeployedResources  # noqa\nfrom chalice.package import create_app_packager\nfrom chalice.package import AppPackager  # noqa\nfrom chalice.package import PackageOptions\nfrom chalice.constants import DEFAULT_STAGE_NAME\nfrom chalice.constants import DEFAULT_APIGATEWAY_STAGE_NAME\nfrom chalice.constants import DEFAULT_ENDPOINT_TYPE\nfrom chalice.logs import LogRetriever, LogEventGenerator\nfrom chalice.logs import FollowLogEventGenerator\nfrom chalice.logs import BaseLogEventGenerator\nfrom chalice import local\nfrom chalice.utils import UI  # noqa\nfrom chalice.utils import PipeReader  # noqa\nfrom chalice.deploy import deployer  # noqa\nfrom chalice.deploy import validate\nfrom chalice.invoke import LambdaInvokeHandler\nfrom chalice.invoke import LambdaInvoker\nfrom chalice.invoke import LambdaResponseFormatter\n\n\nOptStr = Optional[str]\nOptInt = Optional[int]\n\n\ndef create_botocore_session(\n    profile: OptStr = None,\n    debug: bool = False,\n    connection_timeout: OptInt = None,\n    read_timeout: OptInt = None,\n    max_retries: OptInt = None,\n) -> Session:\n    s = Session(profile=profile)\n    _add_chalice_user_agent(s)\n    if debug:\n        _inject_large_request_body_filter()\n    config_args: Dict[str, Any] = {}\n    if connection_timeout is not None:\n        config_args['connect_timeout'] = connection_timeout\n    if read_timeout is not None:\n        config_args['read_timeout'] = read_timeout\n    if max_retries is not None:\n        config_args['retries'] = {'max_attempts': max_retries}\n    if config_args:\n        config = BotocoreConfig(**config_args)\n        s.set_default_client_config(config)\n    return s\n\n\ndef _add_chalice_user_agent(session: Session) -> None:\n    suffix = '%s/%s' % (session.user_agent_name, session.user_agent_version)\n    session.user_agent_name = 'aws-chalice'\n    session.user_agent_version = chalice_version\n    session.user_agent_extra = suffix\n\n\ndef _inject_large_request_body_filter() -> None:\n    log = logging.getLogger('botocore.endpoint')\n    log.addFilter(LargeRequestBodyFilter())\n\n\nclass NoSuchFunctionError(Exception):\n    \"\"\"The specified function could not be found.\"\"\"\n\n    def __init__(self, name: str) -> None:\n        self.name = name\n        super(NoSuchFunctionError, self).__init__()\n\n\nclass UnknownConfigFileVersion(Exception):\n    def __init__(self, version: str) -> None:\n        super(UnknownConfigFileVersion, self).__init__(\n            \"Unknown version '%s' in config.json\" % version\n        )\n\n\nclass LargeRequestBodyFilter(logging.Filter):\n    def filter(self, record: Any) -> bool:\n        # Note: the proper type should be \"logging.LogRecord\", but\n        # the typechecker complains about 'Invalid index type \"int\" for \"dict\"'\n        # so we're using Any for now.\n        if record.msg.startswith('Making request'):\n            if record.args[0].name in ['UpdateFunctionCode', 'CreateFunction']:\n                # When using the ZipFile argument (which is used in chalice),\n                # the entire deployment package zip is sent as a base64 encoded\n                # string.  We don't want this to clutter the debug logs\n                # so we don't log the request body for lambda operations\n                # that have the ZipFile arg.\n                record.args = record.args[:-1] + (\n                    '(... omitted from logs due to size ...)',\n                )\n        return True\n\n\nclass CLIFactory(object):\n    def __init__(\n        self,\n        project_dir: str,\n        debug: bool = False,\n        profile: Optional[str] = None,\n        environ: Optional[MutableMapping] = None,\n    ) -> None:\n        self.project_dir = project_dir\n        self.debug = debug\n        self.profile = profile\n        if environ is None:\n            environ = dict(os.environ)\n        self._environ = environ\n\n    def create_botocore_session(\n        self,\n        connection_timeout: OptInt = None,\n        read_timeout: OptInt = None,\n        max_retries: OptInt = None,\n    ) -> Session:\n        return create_botocore_session(\n            profile=self.profile,\n            debug=self.debug,\n            connection_timeout=connection_timeout,\n            read_timeout=read_timeout,\n            max_retries=max_retries,\n        )\n\n    def create_default_deployer(\n        self, session: Session, config: Config, ui: UI\n    ) -> deployer.Deployer:\n        return deployer.create_default_deployer(session, config, ui)\n\n    def create_plan_only_deployer(\n        self, session: Session, config: Config, ui: UI\n    ) -> deployer.Deployer:\n        return deployer.create_plan_only_deployer(session, config, ui)\n\n    def create_deletion_deployer(\n        self, session: Session, ui: UI\n    ) -> deployer.Deployer:\n        return deployer.create_deletion_deployer(TypedAWSClient(session), ui)\n\n    def create_deployment_reporter(\n        self, ui: UI\n    ) -> deployer.DeploymentReporter:\n        return deployer.DeploymentReporter(ui=ui)\n\n    def create_config_obj(\n        self,\n        chalice_stage_name: str = DEFAULT_STAGE_NAME,\n        autogen_policy: Optional[bool] = None,\n        api_gateway_stage: Optional[str] = None,\n        user_provided_params: Optional[Dict[str, Any]] = None,\n    ) -> Config:\n        if user_provided_params is None:\n            user_provided_params = {}\n        default_params = {\n            'project_dir': self.project_dir,\n            'api_gateway_stage': DEFAULT_APIGATEWAY_STAGE_NAME,\n            'api_gateway_endpoint_type': DEFAULT_ENDPOINT_TYPE,\n            'autogen_policy': True,\n        }\n        try:\n            config_from_disk = self.load_project_config()\n        except (OSError, IOError):\n            raise RuntimeError(\n                \"Unable to load the project config file. \"\n                \"Are you sure this is a chalice project?\"\n            )\n        except ValueError as err:\n            raise RuntimeError(\n                \"Unable to load the project config file: %s\" % err\n            )\n\n        self._validate_config_from_disk(config_from_disk)\n        if autogen_policy is not None:\n            user_provided_params['autogen_policy'] = autogen_policy\n        if self.profile is not None:\n            user_provided_params['profile'] = self.profile\n        if api_gateway_stage is not None:\n            user_provided_params['api_gateway_stage'] = api_gateway_stage\n        config = Config(\n            chalice_stage=chalice_stage_name,\n            user_provided_params=user_provided_params,\n            config_from_disk=config_from_disk,\n            default_params=default_params,\n        )\n        user_provided_params['chalice_app'] = functools.partial(\n            self.load_chalice_app, config.environment_variables\n        )\n        return config\n\n    def _validate_config_from_disk(self, config: Dict[str, Any]) -> None:\n        string_version = config.get('version', '1.0')\n        try:\n            version = float(string_version)\n            if version > 2.0:\n                raise UnknownConfigFileVersion(string_version)\n        except ValueError:\n            raise UnknownConfigFileVersion(string_version)\n\n    def create_app_packager(\n        self,\n        config: Config,\n        options: PackageOptions,\n        package_format: str,\n        template_format: str,\n        merge_template: OptStr = None,\n    ) -> AppPackager:\n        return create_app_packager(\n            config,\n            options,\n            package_format,\n            template_format,\n            merge_template=merge_template,\n        )\n\n    def create_log_retriever(\n        self, session: Session, lambda_arn: str, follow_logs: bool\n    ) -> LogRetriever:\n        client = TypedAWSClient(session)\n        if follow_logs:\n            event_generator = cast(\n                BaseLogEventGenerator, FollowLogEventGenerator(client)\n            )\n        else:\n            event_generator = cast(\n                BaseLogEventGenerator, LogEventGenerator(client)\n            )\n        retriever = LogRetriever.create_from_lambda_arn(\n            event_generator, lambda_arn\n        )\n        return retriever\n\n    def create_stdin_reader(self) -> PipeReader:\n        stream = click.get_binary_stream('stdin')\n        reader = PipeReader(stream)\n        return reader\n\n    def create_lambda_invoke_handler(\n        self, name: str, stage: str\n    ) -> LambdaInvokeHandler:\n        config = self.create_config_obj(stage)\n        deployed = config.deployed_resources(stage)\n        try:\n            resource = deployed.resource_values(name)\n            arn = resource['lambda_arn']\n        except (KeyError, ValueError):\n            raise NoSuchFunctionError(name)\n\n        function_scoped_config = config.scope(stage, name)\n        # The session for max retries needs to be set to 0 for invoking a\n        # lambda function because in the case of a timeout or other retriable\n        # error the underlying client will call the function again.\n        session = self.create_botocore_session(\n            read_timeout=function_scoped_config.lambda_timeout,\n            max_retries=0,\n        )\n\n        client = TypedAWSClient(session)\n        invoker = LambdaInvoker(arn, client)\n\n        handler = LambdaInvokeHandler(\n            invoker,\n            LambdaResponseFormatter(),\n            UI(),\n        )\n\n        return handler\n\n    def load_chalice_app(\n        self,\n        environment_variables: Optional[MutableMapping] = None,\n        validate_feature_flags: Optional[bool] = True,\n    ) -> Chalice:\n        # validate_features indicates that we should validate that\n        # any expiremental features used have the appropriate feature flags.\n        if self.project_dir not in sys.path:\n            sys.path.insert(0, self.project_dir)\n        # The vendor directory has its contents copied up to the top level of\n        # the deployment package. This means that imports will work in the\n        # lambda function as if the vendor directory is on the python path.\n        # For loading the config locally we must add the vendor directory to\n        # the path so it will be treated the same as if it were running on\n        # lambda.\n        vendor_dir = os.path.join(self.project_dir, 'vendor')\n        if os.path.isdir(vendor_dir) and vendor_dir not in sys.path:\n            # This is a tradeoff we have to make for local use.\n            # The common use case of vendor/ is to include\n            # extension modules built for AWS Lambda.  If you're\n            # running on a non-linux dev machine, then attempting\n            # to import these files will raise exceptions.  As\n            # a workaround, the vendor is added to the end of\n            # sys.path so it's after `./lib/site-packages`.\n            # This gives you a change to install the correct\n            # version locally and still keep the lambda\n            # specific one in vendor/\n            sys.path.append(vendor_dir)\n        if environment_variables is not None:\n            self._environ.update(environment_variables)\n        try:\n            app = importlib.import_module('app')\n            chalice_app = getattr(app, 'app')\n        except SyntaxError as e:\n            message = (\n                'Unable to import your app.py file:\\n\\n'\n                'File \"%s\", line %s\\n'\n                '  %s\\n'\n                'SyntaxError: %s'\n            ) % (getattr(e, 'filename'), e.lineno, e.text, e.msg)\n            raise RuntimeError(message)\n        if validate_feature_flags:\n            validate.validate_feature_flags(chalice_app)\n        return chalice_app\n\n    def load_project_config(self) -> Dict[str, Any]:\n        \"\"\"Load the chalice config file from the project directory.\n\n        :raise: OSError/IOError if unable to load the config file.\n\n        \"\"\"\n        config_file = os.path.join(self.project_dir, '.chalice', 'config.json')\n        with open(config_file) as f:\n            return json.loads(f.read())\n\n    def create_local_server(\n        self, app_obj: Chalice, config: Config, host: str, port: int\n    ) -> local.LocalDevServer:\n        return local.create_local_server(app_obj, config, host, port)\n\n    def create_package_options(self) -> PackageOptions:\n        \"\"\"Create the package options that are required to target regions.\"\"\"\n        s = Session(profile=self.profile)\n        client = TypedAWSClient(session=s)\n        return PackageOptions(client)\n"
  },
  {
    "path": "chalice/cli/filewatch/__init__.py",
    "content": "import threading\n\nfrom typing import Callable, Optional, Type  # noqa\nfrom chalice.local import HTTPServerThread  # noqa\n\n\nRESTART_REQUEST_RC = 3\n\n\nclass FileWatcher(object):\n    \"\"\"Base class for watching files for changes.\"\"\"\n\n    def watch_for_file_changes(self, root_dir, callback):\n        # type: (str, Callable[[], None]) -> None\n        \"\"\"Recursively watch directory for changes.\n\n        When a changed file is detected, the provided callback\n        is immediately invoked and the current scan stops.\n\n        \"\"\"\n        raise NotImplementedError(\"watch_for_file_changes\")\n\n\nclass WorkerProcess(object):\n    \"\"\"Worker that runs the chalice dev server.\"\"\"\n    def __init__(self, http_thread):\n        # type: (HTTPServerThread) -> None\n        self._http_thread = http_thread\n        self._restart_event = threading.Event()\n\n    def main(self, project_dir, timeout=None):\n        # type: (str, Optional[int]) -> int\n        self._http_thread.start()\n        self._start_file_watcher(project_dir)\n        if self._restart_event.wait(timeout):\n            self._http_thread.shutdown()\n            return RESTART_REQUEST_RC\n        return 0\n\n    def _start_file_watcher(self, project_dir):\n        # type: (str) -> None\n        raise NotImplementedError(\"_start_file_watcher\")\n"
  },
  {
    "path": "chalice/cli/filewatch/eventbased.py",
    "content": "import threading  # noqa\n\nfrom typing import Callable, Optional  # noqa\nimport watchdog.observers  # pylint: disable=import-error\nfrom watchdog import events  # pylint: disable=import-error\n\nfrom chalice.cli.filewatch import FileWatcher, WorkerProcess\n\n\nclass WatchdogWorkerProcess(WorkerProcess):\n    \"\"\"Worker that runs the chalice dev server.\"\"\"\n\n    def _start_file_watcher(self, project_dir):\n        # type: (str) -> None\n        restart_callback = WatchdogRestarter(self._restart_event)\n        watcher = WatchdogFileWatcher()\n        watcher.watch_for_file_changes(\n            project_dir, restart_callback)\n\n\nclass WatchdogFileWatcher(FileWatcher):\n    def watch_for_file_changes(self, root_dir, callback):\n        # type: (str, Callable[[], None]) -> None\n        observer = watchdog.observers.Observer()\n        observer.schedule(callback, root_dir, recursive=True)\n        observer.start()\n\n\nclass WatchdogRestarter(events.FileSystemEventHandler):\n\n    def __init__(self, restart_event):\n        # type: (threading.Event) -> None\n        # The reason we're using threading\n        self.restart_event = restart_event\n\n    def on_any_event(self, event):\n        # type: (events.FileSystemEvent) -> None\n        # If we modify a file we'll get a FileModifiedEvent\n        # as well as a DirectoryModifiedEvent.\n        # We only care about reloading is a file is modified.\n        if event.is_directory:\n            return\n        self()\n\n    def __call__(self):\n        # type: () -> None\n        self.restart_event.set()\n"
  },
  {
    "path": "chalice/cli/filewatch/stat.py",
    "content": "import logging\nimport threading\nimport time\n\nfrom typing import Callable, Dict, Optional, Iterator  # noqa\n\nfrom chalice.cli.filewatch import FileWatcher, WorkerProcess\nfrom chalice.utils import OSUtils\n\n\nLOGGER = logging.getLogger(__name__)\n\n\nclass StatWorkerProcess(WorkerProcess):\n    def _start_file_watcher(self, project_dir):\n        # type: (str) -> None\n        watcher = StatFileWatcher()\n        watcher.watch_for_file_changes(project_dir, self._on_file_change)\n\n    def _on_file_change(self):\n        # type: () -> None\n        self._restart_event.set()\n\n\nclass StatFileWatcher(FileWatcher):\n    POLL_INTERVAL = 1\n\n    def __init__(self, osutils=None):\n        # type: (Optional[OSUtils]) -> None\n        self._mtime_cache = {}  # type: Dict[str, float]\n        self._shutdown_event = threading.Event()\n        self._thread = None  # type: Optional[threading.Thread]\n        if osutils is None:\n            osutils = OSUtils()\n        self._osutils = osutils\n\n    def watch_for_file_changes(self, root_dir, callback):\n        # type: (str, Callable[[], None]) -> None\n        t = threading.Thread(target=self.poll_for_changes_until_shutdown,\n                             args=(root_dir, callback))\n        t.daemon = True\n        t.start()\n        self._thread = t\n        LOGGER.debug(\"Stat file watching: %s, with callback: %s\",\n                     root_dir, callback)\n\n    def poll_for_changes_until_shutdown(self, root_dir, callback):\n        # type: (str, Callable[[], None]) -> None\n        self._seed_mtime_cache(root_dir)\n        while not self._shutdown_event.is_set():\n            self._single_pass_poll(root_dir, callback)\n            time.sleep(self.POLL_INTERVAL)\n\n    def _seed_mtime_cache(self, root_dir):\n        # type: (str) -> None\n        for rootdir, _, filenames in self._osutils.walk(root_dir):\n            for filename in filenames:\n                path = self._osutils.joinpath(rootdir, filename)\n                self._mtime_cache[path] = self._osutils.mtime(path)\n\n    def _single_pass_poll(self, root_dir, callback):\n        # type: (str, Callable[[], None]) -> None\n        new_mtimes = {}  # type: Dict[str, float]\n        for path in self._recursive_walk_files(root_dir):\n            if self._is_changed_file(path, new_mtimes):\n                callback()\n                return\n        if new_mtimes != self._mtime_cache:\n            # Files were removed.\n            LOGGER.debug(\"Files removed, triggering restart.\")\n            self._mtime_cache = new_mtimes\n            callback()\n            return\n\n    def _is_changed_file(self, path, new_mtimes):\n        # type: (str, Dict[str, float]) -> bool\n        last_mtime = self._mtime_cache.get(path)\n        if last_mtime is None:\n            LOGGER.debug(\"File added: %s, triggering restart.\", path)\n            return True\n        try:\n            new_mtime = self._osutils.mtime(path)\n            if new_mtime > last_mtime:\n                LOGGER.debug(\"File updated: %s, triggering restart.\", path)\n                return True\n            new_mtimes[path] = new_mtime\n            return False\n        except (OSError, IOError):\n            return False\n\n    def _recursive_walk_files(self, root_dir):\n        # type: (str) -> Iterator[str]\n        for rootdir, _, filenames in self._osutils.walk(root_dir):\n            for filename in filenames:\n                path = self._osutils.joinpath(rootdir, filename)\n                yield path\n"
  },
  {
    "path": "chalice/cli/newproj.py",
    "content": "\"\"\"New project generation.\n\nHow it Works\n============\n\nProject template are placed with the ./chalice/templates directory.  Each\ndirectory corresponds to a single template.  The name of the directory has\nthe structure ``0123-template-name``, where the first part consists of a four\ndigit number followed by a ``-``, then the name of the template.  The leading\nnumber is not exposed externally to the user and is used solely for sorting\npurposes so we can display the project templates in the order we prefer.\nThe template name is the name that's used in the ``--project-type`` value\nfor the ``new-project`` command.\n\nEach template can have a ``DESCRIPTION`` file that contains a short\ndescription of the project template.  This will be used as the display\nvalue instead of the project type key if this file is available.  The\n``DESCRIPTION`` file is not copied over when generating the new project\nfiles.\n\nThere's basic support for templating values.  This allows you to write\ntemplates with placeholder values that are filled in during project\ngeneration time.  These values are denoted via ``{{template_var}}``.\nThe following keys are supported:\n\n* ``app_name`` - The name of the project.\n* ``chalice_version`` - The current version of chalice generating the project.\n\n\"\"\"\nfrom __future__ import print_function\nimport os\nimport re\nimport json\nimport fnmatch\nfrom dataclasses import dataclass\nfrom typing import Optional, Dict, Any, Iterator, Tuple, Match, List  # noqa\n\nimport inquirer\n\nfrom chalice.constants import WELCOME_PROMPT\nfrom chalice.utils import OSUtils\nfrom chalice.app import __version__ as chalice_version\n\n\nTEMPLATES_DIR = os.path.join(\n    os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'templates'\n)\nVAR_REF_REGEX = r'{{(.*?)}}'\nIGNORE_FILES = ['metadata.json', '*.pyc']\n\n\nclass BadTemplateError(Exception):\n    pass\n\n\ndef create_new_project_skeleton(\n    project_name: str, project_type: Optional[str] = 'legacy'\n) -> None:\n    osutils = OSUtils()\n    all_projects = list_available_projects(TEMPLATES_DIR, osutils)\n    project = [p for p in all_projects if p.key == project_type][0]\n    template_kwargs = {\n        'app_name': project_name,\n        'chalice_version': chalice_version,\n    }\n    project_creator = ProjectCreator(osutils)\n    project_creator.create_new_project(\n        os.path.join(TEMPLATES_DIR, project.dirname),\n        project_name,\n        template_kwargs=template_kwargs,\n    )\n\n\n@dataclass\nclass ProjectTemplate:\n    dirname: str\n    metadata: Dict[str, Any]\n    key: str\n\n    @property\n    def description(self) -> str:\n        # Pylint doesn't understand the attrs types.\n        # pylint: disable=no-member\n        return self.metadata.get('description', self.key)\n\n\nclass ProjectCreator(object):\n    def __init__(self, osutils: Optional[OSUtils] = None) -> None:\n        if osutils is None:\n            osutils = OSUtils()\n        self._osutils = osutils\n\n    def create_new_project(\n        self,\n        source_dir: str,\n        destination_dir: str,\n        template_kwargs: Dict[str, Any],\n    ) -> None:\n        for full_src_path, full_dst_path in self._iter_files(\n            source_dir, destination_dir\n        ):\n            dest_dir = self._osutils.dirname(full_dst_path)\n            if not self._osutils.directory_exists(dest_dir):\n                self._osutils.makedirs(dest_dir)\n            contents = self._osutils.get_file_contents(\n                full_src_path, binary=False\n            )\n            templated_contents = get_templated_content(\n                contents, template_kwargs\n            )\n            self._osutils.set_file_contents(\n                full_dst_path, templated_contents, binary=False\n            )\n\n    def _iter_files(\n        self, source_dir: str, destination_dir: str\n    ) -> Iterator[Tuple[str, str]]:\n        for rootdir, _, filenames in self._osutils.walk(source_dir):\n            for filename in filenames:\n                if self._should_ignore(filename):\n                    continue\n                full_src_path = os.path.join(rootdir, filename)\n                # The starting index needs `+ 1` to account for the\n                # trailing `/` char (e.g. foo/bar -> foo/bar/).\n                full_dst_path = os.path.join(\n                    destination_dir, full_src_path[len(source_dir) + 1:]\n                )\n                yield full_src_path, full_dst_path\n\n    def _should_ignore(self, filename: str) -> bool:\n        for ignore in IGNORE_FILES:\n            if fnmatch.fnmatch(filename, ignore):\n                return True\n        return False\n\n\ndef get_templated_content(\n    contents: str, template_kwargs: Dict[str, Any]\n) -> str:\n    def lookup_var(match: Match) -> str:\n        var_name = match.group(1)\n        try:\n            return template_kwargs[var_name]\n        except KeyError:\n            raise BadTemplateError(\n                \"Bad template, referenced template var that does not \"\n                \"exist: '%s', for template contents:\\n%s\"\n                % (var_name, contents)\n            )\n\n    new_contents = re.sub(VAR_REF_REGEX, lookup_var, contents)\n    return new_contents\n\n\ndef list_available_projects(\n    templates_dir: str, osutils: OSUtils\n) -> List[ProjectTemplate]:\n    projects = []\n    for dirname in sorted(osutils.get_directory_contents(templates_dir)):\n        filename = osutils.joinpath(templates_dir, dirname, 'metadata.json')\n        metadata = json.loads(osutils.get_file_contents(filename, False))\n        key = dirname.split('-', 1)[1]\n        projects.append(ProjectTemplate(dirname, metadata, key=key))\n    return projects\n\n\ndef getting_started_prompt() -> Dict[str, Any]:\n    print(WELCOME_PROMPT)\n    projects = list_available_projects(TEMPLATES_DIR, OSUtils())\n    questions = [\n        inquirer.Text('project_name', message='Enter the project name'),\n        inquirer.List(\n            'project_type',\n            message='Select your project type',\n            choices=[(p.description, p.key) for p in projects],\n        ),\n    ]\n    answers = inquirer.prompt(questions)\n    return answers\n"
  },
  {
    "path": "chalice/cli/reloader.py",
    "content": "\"\"\"Automatically reload chalice app when files change.\n\nHow It Works\n============\n\nThis approach borrow from what django, flask, and other frameworks do.\nEssentially, with reloading enabled ``chalice local`` will start up\na worker process that runs the dev http server.  This means there will\nbe a total of two processes running (both will show as ``chalice local``\nin ps).  One process is the parent process.  It's job is to start up a child\nprocess and restart it if it exits (due to a restart request).  The child\nprocess is the process that actually starts up the web server for local mode.\nThe child process also sets up a watcher thread.  It's job is to monitor\ndirectories for changes.  If a change is encountered it sys.exit()s the process\nwith a known RC (the RESTART_REQUEST_RC constant in the module).\n\nThe parent process runs in an infinite loop.  If the child process exits with\nan RC of RESTART_REQUEST_RC the parent process starts up another child process.\n\nThe child worker is denoted by setting the ``CHALICE_WORKER`` env var.\nIf this env var is set, the process is intended to be a worker process (as\nopposed the parent process which just watches for restart requests from the\nworker process).\n\n\"\"\"\nimport subprocess\nimport logging\nimport copy\nimport sys\n\nfrom typing import MutableMapping, Type, Callable, Optional  # noqa\n\nfrom chalice.cli.filewatch import RESTART_REQUEST_RC, WorkerProcess\nfrom chalice.local import LocalDevServer, HTTPServerThread  # noqa\n\n\nLOGGER = logging.getLogger(__name__)\nWorkerProcType = Optional[Type[WorkerProcess]]\n\n\ndef get_best_worker_process():\n    # type: () -> Type[WorkerProcess]\n    try:\n        from chalice.cli.filewatch.eventbased import WatchdogWorkerProcess\n        LOGGER.debug(\"Using watchdog worker process.\")\n        return WatchdogWorkerProcess\n    except ImportError:\n        from chalice.cli.filewatch.stat import StatWorkerProcess\n        LOGGER.debug(\"Using stat() based worker process.\")\n        return StatWorkerProcess\n\n\ndef start_parent_process(env):\n    # type: (MutableMapping) -> None\n    process = ParentProcess(env, subprocess.Popen)\n    process.main()\n\n\ndef start_worker_process(server_factory, root_dir, worker_process_cls=None):\n    # type: (Callable[[], LocalDevServer], str, WorkerProcType) -> int\n    if worker_process_cls is None:\n        worker_process_cls = get_best_worker_process()\n    t = HTTPServerThread(server_factory)\n    worker = worker_process_cls(t)\n    LOGGER.debug(\"Starting worker...\")\n    rc = worker.main(root_dir)\n    LOGGER.info(\"Restarting local dev server.\")\n    return rc\n\n\nclass ParentProcess(object):\n    \"\"\"Spawns a child process and restarts it as needed.\"\"\"\n    def __init__(self, env, popen):\n        # type: (MutableMapping, Type[subprocess.Popen]) -> None\n        self._env = copy.copy(env)\n        self._popen = popen\n\n    def main(self):\n        # type: () -> None\n        # This method launches a child worker and restarts it if it\n        # exits with RESTART_REQUEST_RC.  This method doesn't return.\n        # A user can Ctrl-C to stop the parent process.\n        while True:\n            self._env['CHALICE_WORKER'] = 'true'\n            LOGGER.debug(\"Parent process starting child worker process...\")\n            process = self._popen(sys.argv, env=self._env)\n            try:\n                process.communicate()\n                if process.returncode != RESTART_REQUEST_RC:\n                    return\n            except KeyboardInterrupt:\n                process.terminate()\n                raise\n\n\ndef run_with_reloader(server_factory, env, root_dir, worker_process_cls=None):\n    # type: (Callable, MutableMapping, str, WorkerProcType) -> int\n    # This function is invoked in two possible modes, as the parent process\n    # or as a chalice worker.\n    try:\n        if env.get('CHALICE_WORKER') is not None:\n            # This is a chalice worker.  We need to start the main dev server\n            # in a daemon thread and install a file watcher.\n            return start_worker_process(server_factory, root_dir,\n                                        worker_process_cls)\n        else:\n            # This is the parent process.  It's just is to spawn an identical\n            # process but with the ``CHALICE_WORKER`` env var set.  It then\n            # will monitor this process and restart it if it exits with a\n            # RESTART_REQUEST exit code.\n            start_parent_process(env)\n    except KeyboardInterrupt:\n        pass\n    return 0\n"
  },
  {
    "path": "chalice/compat.py",
    "content": "import socket\nimport six\nimport os\n\nfrom typing import Dict, Any  # noqa\nfrom urllib.parse import urlparse, parse_qs\n\nfrom six import StringIO\n\n\nSTRING_TYPES = six.string_types\n\n\ndef pip_import_string():\n    # type: () -> str\n    import pip\n    pip_major_version = int(pip.__version__.split('.')[0])\n    pip_minor_version = int(pip.__version__.split('.')[1])\n    pip_major_minor = (pip_major_version, pip_minor_version)\n    # Pip moved its internals to an _internal module in version 10.\n    # In order to be compatible with version 9 which has it at at the\n    # top level we need to figure out the correct import path here.\n    if (9, 0) <= pip_major_minor < (10, 0):\n        return 'from pip import main'\n    elif (10, 0) <= pip_major_minor < (19, 3):\n        # Pip changed their import structure again in 19.3\n        # https://github.com/pypa/pip/commit/09fd200\n        return 'from pip._internal import main'\n    elif (19, 3) <= pip_major_minor < (20, 0):\n        return 'from pip._internal.main import main'\n    elif pip_major_minor >= (20, 0):\n        # More changes! https://github.com/pypa/pip/issues/7498\n        # We'll assume that anything >= v20.0 will use this import\n        # string.  We're already specifying our supported versions of\n        # pip as a dependency so assuming this stays the same, pip\n        # upgrades will just require bumping our dependency range in\n        # setup.py.\n        return 'from pip._internal.cli.main import main'\n    raise RuntimeError(\"Unknown import string for pip version: %s\"\n                       % str(pip_major_minor))\n\n\nif os.name == 'nt':\n    # windows\n    # This is the actual patch used on windows to prevent distutils from\n    # compiling C extensions. The msvc compiler base class has its compile\n    # method overridden to raise a CompileError. This can be caught by\n    # setup.py code which can then fallback to making a pure python\n    # package if possible.\n    # We need mypy to ignore these since they are never actually called from\n    # within our process they do not need to be a part of our typechecking\n    # pass.\n    def prevent_msvc_compiling_patch():  # type: ignore\n        import distutils\n        import distutils._msvccompiler\n        import distutils.msvc9compiler\n        import distutils.msvccompiler\n\n        from distutils.errors import CompileError\n\n        def raise_compile_error(*args, **kwargs):  # type: ignore\n            raise CompileError('Chalice blocked C extension compiling.')\n        distutils._msvccompiler.MSVCCompiler.compile = raise_compile_error\n        distutils.msvc9compiler.MSVCCompiler.compile = raise_compile_error\n        distutils.msvccompiler.MSVCCompiler.compile = raise_compile_error\n\n    # This is the setuptools shim used to execute setup.py by pip.\n    # Lines 2 and 3 have been added to call the above function\n    # `prevent_msvc_compiling_patch` and extra escapes have been added on line\n    # 5 because it is passed through another layer of string parsing before it\n    # is executed.\n    _SETUPTOOLS_SHIM = (\n        r\"import setuptools, tokenize;__file__=%r;\"\n        r\"from chalice.compat import prevent_msvc_compiling_patch;\"\n        r\"prevent_msvc_compiling_patch();\"\n        r\"f=getattr(tokenize, 'open', open)(__file__);\"\n        r\"code=f.read().replace('\\\\r\\\\n', '\\\\n');\"\n        r\"f.close();\"\n        r\"exec(compile(code, __file__, 'exec'))\"\n    )\n\n    # On windows the C compiling story is much more complex than on posix as\n    # there are many different C compilers that setuptools and disutils will\n    # try and find using a combination of known filepaths, registry entries,\n    # and environment variables. Since there is no simple environment variable\n    # we can replace when starting the subprocess that builds the package;\n    # we need to apply a patch at runtime to prevent pip/setuptools/distutils\n    # from being able to build C extensions.\n    # Patching out every possible technique for finding each compiler would\n    # be a losing game of whack-a-mole. In addition we need to apply a patch\n    # two layers down through subprocess calls, specifically:\n    #  * Chalice creates a subprocess of `pip wheel ...` to build sdists\n    #    into wheel files.\n    #  * Pip creates another python subprocess to call the setup.py file in\n    #    the sdist. Before doing so it applies the above shim to make the\n    #    setup file compatible with setuptools. This shim layer also reads\n    #    and executes the code in the setup.py.\n    #  * Setuptools (which will have been executed by the shim) will\n    #    eventually call distutils to do the heavy lifting for C compiling.\n    #\n    # Our patch needs to affect the bottom level here (distutils) and patch\n    # it out to prevent it from compiling C in a graceful way that results in\n    # falling back to building a purepython library if possible.\n    # The below line will be injected just before the `pip wheel ...` portion\n    # of the subprocess that Chalice starts. This replaces the\n    # SETUPTOOLS_SHIM that pip normally uses with the one defined above.\n    # When pip goes to run its subprocess for executing setup.py it will\n    # inject _SETUPTOOLS_SHIM rather than the usual SETUPTOOLS_SHIM in pip.\n    # This lets us apply our patches in the same process that will compile\n    # the c extensions before the setup.py file has been executed.\n    # The actual patches used are decribed in the comment above\n    # _SETUPTOOLS_SHIM.\n    pip_no_compile_c_shim = (\n        'import pip;'\n        'pip.wheel.SETUPTOOLS_SHIM = \"\"\"%s\"\"\";'\n    ) % _SETUPTOOLS_SHIM\n    pip_no_compile_c_env_vars = {}  # type: Dict[str, Any]\nelse:\n    # posix\n    # On posix systems setuptools/distutils uses the CC env variable to\n    # locate a C compiler for building C extensions. All we need to do is set\n    # it to /var/false, and the module building process will fail to build.\n    # C extensions, and any fallback processes in place to build a pure python\n    # package will be kicked off.\n    # No need to monkey patch the process.\n    pip_no_compile_c_shim = ''\n    pip_no_compile_c_env_vars = {\n        'CC': '/var/false'\n    }\n\n\ndef is_broken_pipe_error(error):\n    # type: (Exception) -> bool\n    return isinstance(error, BrokenPipeError)  # noqa\n"
  },
  {
    "path": "chalice/config.py",
    "content": "from __future__ import annotations\nimport os\nimport sys\nimport json\n\nfrom typing import Dict, Any, Optional, List, Union  # noqa\nfrom chalice import __version__ as current_chalice_version\nfrom chalice.app import Chalice  # noqa\nfrom chalice.constants import DEFAULT_STAGE_NAME\nfrom chalice.constants import DEFAULT_HANDLER_NAME\n\n\nStrMap = Dict[str, Any]\n\n\nclass Config(object):\n    \"\"\"Configuration information for a chalice app.\n\n    Configuration values for a chalice app can come from\n    a number of locations, files on disk, CLI params, default\n    values, etc.  This object is an abstraction that normalizes\n    these values.\n\n    In general, there's a precedence for looking up\n    config values:\n\n        * User specified params\n        * Config file values\n        * Default values\n\n    A user specified parameter would mean values explicitly\n    specified by a user.  Generally these come from command\n    line parameters (e.g ``--profile prod``), but for the purposes\n    of this object would also mean values passed explicitly to\n    this config object when instantiated.\n\n    Additionally, there are some configurations that can vary\n    per chalice stage (note that a chalice stage is different\n    from an api gateway stage).  For config values loaded from\n    disk, we allow values to be specified for all stages or\n    for a specific stage.  For example, take ``environment_variables``.\n    You can set this as a top level key to specify env vars\n    to set for all stages, or you can set this value per chalice\n    stage to set stage-specific environment variables.  Consider\n    this config file::\n\n        {\n          \"environment_variables\": {\n            \"TABLE\": \"foo\"\n          },\n          \"stages\": {\n            \"dev\": {\n              \"environment_variables\": {\n                \"S3BUCKET\": \"devbucket\"\n              }\n            },\n            \"prod\": {\n              \"environment_variables\": {\n                \"S3BUCKET\": \"prodbucket\",\n                \"TABLE\": \"prodtable\"\n              }\n            }\n          }\n        }\n\n    If the currently configured chalice stage is \"dev\", then\n    the config.environment_variables would be::\n\n        {\"TABLE\": \"foo\", \"S3BUCKET\": \"devbucket\"}\n\n    The \"prod\" stage would be::\n\n        {\"TABLE\": \"prodtable\", \"S3BUCKET\": \"prodbucket\"}\n\n    \"\"\"\n\n    def __init__(self,\n                 chalice_stage: str = DEFAULT_STAGE_NAME,\n                 function_name: str = DEFAULT_HANDLER_NAME,\n                 user_provided_params: Optional[StrMap] = None,\n                 config_from_disk: Optional[StrMap] = None,\n                 default_params: Optional[StrMap] = None,\n                 layers: Optional[List[str]] = None,\n                 ) -> None:\n        #: Params that a user provided explicitly,\n        #: typically via the command line.\n        self.chalice_stage = chalice_stage\n        self.function_name = function_name\n        if user_provided_params is None:\n            user_provided_params = {}\n        self._user_provided_params = user_provided_params\n        #: The json.loads() from .chalice/config.json\n        if config_from_disk is None:\n            config_from_disk = {}\n        self._config_from_disk = config_from_disk\n        if default_params is None:\n            default_params = {}\n        self._default_params = default_params\n        self._chalice_app = None\n        self._layers = layers\n\n    @classmethod\n    def create(cls, chalice_stage: str = DEFAULT_STAGE_NAME,\n               function_name: str = DEFAULT_HANDLER_NAME,\n               **kwargs: Any) -> Config:\n        return cls(chalice_stage=chalice_stage,\n                   user_provided_params=kwargs.copy())\n\n    @property\n    def profile(self) -> str:\n        return self._chain_lookup('profile')\n\n    @property\n    def app_name(self) -> str:\n        return self._chain_lookup('app_name')\n\n    @property\n    def project_dir(self) -> str:\n        return self._chain_lookup('project_dir')\n\n    @property\n    def chalice_app(self) -> Chalice:\n        v = self._chain_lookup('chalice_app')\n        # There's two value we support.  If the value\n        # is a chalice app, we return it as is.\n        # Otherwise, we assume it's a callable that creates\n        # a chalice app.  This is used to lazy load the chalice\n        # app.\n        if isinstance(v, Chalice):\n            return v\n        elif self._chalice_app is not None:\n            return self._chalice_app\n        elif not callable(v):\n            raise TypeError(\"Unable to load chalice app, lazy loader is \"\n                            \"not callable: %s\" % v)\n        app = v()\n        self._chalice_app = app\n        # Keep mypy happy.\n        return app\n\n    @property\n    def config_from_disk(self) -> StrMap:\n        return self._config_from_disk\n\n    @property\n    def lambda_python_version(self) -> str:\n        # We may open this up to configuration later, but for now,\n        # we attempt to match your python version to the closest version\n        # supported by lambda.\n        major, minor = sys.version_info[0], sys.version_info[1]\n        if (major, minor) < (3, 9):\n            return 'python3.9'\n        elif (major, minor) <= (3, 12):\n            # Otherwise we use your current version of python if Lambda\n            # supports it.\n            return f'python{major}.{minor}'\n        return 'python3.13'\n\n    @property\n    def log_retention_in_days(self) -> int:\n        return self._chain_lookup('log_retention_in_days',\n                                  varies_per_chalice_stage=True,\n                                  varies_per_function=True)\n\n    @property\n    def layers(self) -> List:\n        return self._chain_lookup('layers',\n                                  varies_per_chalice_stage=True,\n                                  varies_per_function=True)\n\n    @property\n    def api_gateway_custom_domain(self) -> StrMap:\n        return self._chain_lookup('api_gateway_custom_domain',\n                                  varies_per_chalice_stage=True)\n\n    @property\n    def websocket_api_custom_domain(self) -> StrMap:\n        return self._chain_lookup('websocket_api_custom_domain',\n                                  varies_per_chalice_stage=True)\n\n    def _chain_lookup(self, name: str, varies_per_chalice_stage: bool = False,\n                      varies_per_function: bool = False) -> Any:\n        search_dicts = [self._user_provided_params]\n        if varies_per_chalice_stage:\n            search_dicts.append(\n                self._config_from_disk.get('stages', {}).get(\n                    self.chalice_stage, {}))\n        if varies_per_function:\n            # search order:\n            # config['stages']['lambda_functions']\n            # config['stages']\n            # config['lambda_functions']\n            search_dicts.insert(\n                0, self._config_from_disk.get('stages', {}).get(\n                    self.chalice_stage, {}).get('lambda_functions', {}).get(\n                        self.function_name, {}))\n            search_dicts.append(\n                self._config_from_disk.get('lambda_functions', {}).get(\n                    self.function_name, {}))\n        search_dicts.extend([self._config_from_disk, self._default_params])\n        for cfg_dict in search_dicts:\n            if isinstance(cfg_dict, dict) and cfg_dict.get(name) is not None:\n                return cfg_dict[name]\n\n    def _chain_merge(self, name: str) -> Dict[str, Any]:\n        # Merge values for all search dicts instead of returning on first\n        # found.\n        search_dicts = [\n            # This is reverse order to _chain_lookup().\n            self._default_params,\n            self._config_from_disk,\n            self._config_from_disk.get('stages', {}).get(\n                self.chalice_stage, {}),\n            self._config_from_disk.get('stages', {}).get(\n                self.chalice_stage, {}).get('lambda_functions', {}).get(\n                    self.function_name, {}),\n            self._user_provided_params,\n        ]\n        final = {}\n        for cfg_dict in search_dicts:\n            value = cfg_dict.get(name, {})\n            if isinstance(value, dict):\n                final.update(value)\n        return final\n\n    @property\n    def config_file_version(self) -> str:\n        return self._config_from_disk.get('version', '1.0')\n\n    # These are all config values that can vary per\n    # chalice stage.\n\n    @property\n    def api_gateway_stage(self) -> str:\n        return self._chain_lookup('api_gateway_stage',\n                                  varies_per_chalice_stage=True)\n\n    @property\n    def api_gateway_endpoint_type(self) -> str:\n        return self._chain_lookup('api_gateway_endpoint_type',\n                                  varies_per_chalice_stage=True)\n\n    @property\n    def api_gateway_endpoint_vpce(self) -> Union[str, List[str]]:\n        return self._chain_lookup('api_gateway_endpoint_vpce',\n                                  varies_per_chalice_stage=True)\n\n    @property\n    def api_gateway_policy_file(self) -> str:\n        return self._chain_lookup('api_gateway_policy_file',\n                                  varies_per_chalice_stage=True)\n\n    @property\n    def minimum_compression_size(self) -> int:\n        return self._chain_lookup('minimum_compression_size',\n                                  varies_per_chalice_stage=True)\n\n    @property\n    def iam_policy_file(self) -> str:\n        return self._chain_lookup('iam_policy_file',\n                                  varies_per_chalice_stage=True,\n                                  varies_per_function=True)\n\n    @property\n    def lambda_memory_size(self) -> int:\n        return self._chain_lookup('lambda_memory_size',\n                                  varies_per_chalice_stage=True,\n                                  varies_per_function=True)\n\n    @property\n    def lambda_timeout(self) -> int:\n        return self._chain_lookup('lambda_timeout',\n                                  varies_per_chalice_stage=True,\n                                  varies_per_function=True)\n\n    @property\n    def automatic_layer(self) -> bool:\n        v = self._chain_lookup('automatic_layer',\n                               varies_per_chalice_stage=True,\n                               varies_per_function=False)\n        if v is None:\n            return False\n        return v\n\n    @property\n    def iam_role_arn(self) -> str:\n        return self._chain_lookup('iam_role_arn',\n                                  varies_per_chalice_stage=True,\n                                  varies_per_function=True)\n\n    @property\n    def manage_iam_role(self) -> bool:\n        result = self._chain_lookup('manage_iam_role',\n                                    varies_per_chalice_stage=True,\n                                    varies_per_function=True)\n        if result is None:\n            # To simplify downstream code, if manage_iam_role\n            # is None (indicating the user hasn't configured/specified this\n            # value anywhere), then we'll return a default value of True.\n            # Otherwise client code has to do an awkward\n            # \"if manage_iam_role is None and not manage_iam_role\".\n            return True\n        return result\n\n    @property\n    def autogen_policy(self) -> bool:\n        return self._chain_lookup('autogen_policy',\n                                  varies_per_chalice_stage=True,\n                                  varies_per_function=True)\n\n    @property\n    def xray_enabled(self) -> bool:\n        return self._chain_lookup('xray',\n                                  varies_per_chalice_stage=True,\n                                  varies_per_function=True)\n\n    @property\n    def environment_variables(self) -> Dict[str, str]:\n        return self._chain_merge('environment_variables')\n\n    @property\n    def tags(self) -> Dict[str, str]:\n        tags = self._chain_merge('tags')\n        tags['aws-chalice'] = 'version=%s:stage=%s:app=%s' % (\n            current_chalice_version, self.chalice_stage, self.app_name)\n        return tags\n\n    @property\n    def security_group_ids(self) -> List[str]:\n        return self._chain_lookup('security_group_ids',\n                                  varies_per_chalice_stage=True,\n                                  varies_per_function=True)\n\n    @property\n    def subnet_ids(self) -> List[str]:\n        return self._chain_lookup('subnet_ids',\n                                  varies_per_chalice_stage=True,\n                                  varies_per_function=True)\n\n    @property\n    def reserved_concurrency(self) -> int:\n        return self._chain_lookup('reserved_concurrency',\n                                  varies_per_chalice_stage=True,\n                                  varies_per_function=True)\n\n    def scope(self, chalice_stage: str, function_name: str) -> Config:\n        # Used to create a new config object that's scoped to a different\n        # stage and/or function.  This creates a completely separate copy.\n        # This is preferred over mutating the existing config obj.\n        # We technically don't need to do a copy here, but this avoids\n        # any possible issues if we ever mutate the config values.\n        clone = self.__class__(\n            chalice_stage=chalice_stage,\n            function_name=function_name,\n            user_provided_params=self._user_provided_params,\n            config_from_disk=self._config_from_disk,\n            default_params=self._default_params,\n        )\n        return clone\n\n    def deployed_resources(self, chalice_stage_name: str) -> DeployedResources:\n        \"\"\"Return resources associated with a given stage.\n\n        If a deployment to a given stage has never happened,\n        this method will return a value of None.\n\n        \"\"\"\n        # This is arguably the wrong level of abstraction.\n        # We might be able to move this elsewhere.\n        deployed_file = os.path.join(\n            self.project_dir, '.chalice', 'deployed',\n            '%s.json' % chalice_stage_name)\n        data = self._load_json_file(deployed_file)\n        if data is not None:\n            schema_version = data.get('schema_version', '1.0')\n            if schema_version != '2.0':\n                raise ValueError(\"Unsupported schema version (%s) in file: %s\"\n                                 % (schema_version, deployed_file))\n            return DeployedResources(data)\n        return self._try_old_deployer_values(chalice_stage_name)\n\n    def _try_old_deployer_values(self,\n                                 chalice_stage_name: str) -> DeployedResources:\n        # They are upgrading from v1.0 to v2.0 of the deployed.json\n        # schema.  Attempt to auto convert for them.\n        old_deployed_file = os.path.join(self.project_dir, '.chalice',\n                                         'deployed.json')\n        data = self._load_json_file(old_deployed_file)\n        if data is None or chalice_stage_name not in data:\n            return DeployedResources.empty()\n        return self._upgrade_deployed_values(chalice_stage_name, data)\n\n    def _load_json_file(self, deployed_file: str) -> Any:\n        if not os.path.isfile(deployed_file):\n            return None\n        with open(deployed_file, 'r') as f:\n            return json.load(f)\n\n    def _upgrade_deployed_values(self, chalice_stage_name: str,\n                                 data: Any) -> DeployedResources:\n        deployed = data[chalice_stage_name]\n        prefix = '%s-%s-' % (self.app_name, chalice_stage_name)\n        resources: List[Dict[str, Any]] = []\n        self._upgrade_lambda_functions(resources, deployed, prefix)\n        self._upgrade_rest_api(resources, deployed)\n        return DeployedResources(\n            {'resources': resources, 'schema_version': '2.0'})\n\n    def _upgrade_lambda_functions(self,\n                                  resources: List[Dict[str, Any]],\n                                  deployed: Dict[str, Any],\n                                  prefix: str) -> None:\n        lambda_functions = deployed.get('lambda_functions', {})\n        # In chalice 0.10.0, the lambda_functions had the format\n        # {\"function-name\": \"lambda_arn\"} as opposed to\n        # {\"function-name\": {\"arn\": \"lambda_arn\", \"type\": \"...'}} used\n        # in later versions of chalice.  We'll check for both cases\n        # so people can upgrade from 0.10.0 to the new deployer.\n        is_pre_10_format = not all(\n            isinstance(v, dict)\n            for v in lambda_functions.values()\n        )\n        if is_pre_10_format:\n            lambda_functions = {\n                # The only supported lambda functions in 0.10.0\n                # was built in authorizers.\n                k: {'type': 'authorizer', 'arn': v}\n                for k, v in lambda_functions.items()\n            }\n        for name, values in lambda_functions.items():\n            short_name = name[len(prefix):]\n            current = {\n                'resource_type': 'lambda_function',\n                'lambda_arn': values['arn'],\n                'name': short_name,\n            }\n            resources.append(current)\n\n    def _upgrade_rest_api(self,\n                          resources: List[Dict[str, Any]],\n                          deployed: Dict[str, Any]) -> None:\n        resources.extend([\n            {'name': 'api_handler',\n             'resource_type': 'lambda_function',\n             'lambda_arn': deployed['api_handler_arn']},\n            {'name': 'rest_api',\n             'resource_type': 'rest_api',\n             'rest_api_id': deployed['rest_api_id']},\n        ])\n\n\nclass DeployedResources(object):\n    def __init__(self, deployed_values: Dict[str, Any]) -> None:\n        self._deployed_values = deployed_values['resources']\n        self._deployed_values_by_name = {\n            resource['name']: resource\n            for resource in deployed_values['resources']\n        }\n\n    @classmethod\n    def empty(cls) -> DeployedResources:\n        return cls({'resources': [], 'schema_version': '2.0'})\n\n    def resource_values(self, name: str) -> Dict[str, Any]:\n        if 'api_mapping' in name:\n            name = name.split('.')[0]\n\n        try:\n            return self._deployed_values_by_name[name]\n        except KeyError:\n            raise ValueError(\"Resource does not exist: %s\" % name)\n\n    def resource_names(self) -> List[str]:\n        return [r['name'] for r in self._deployed_values]\n"
  },
  {
    "path": "chalice/constants.py",
    "content": "\n# This is the version that's written to the config file\n# on a `chalice new-project`.  It's also how chalice is able\n# to know when to warn you when changing behavior is introduced.\nCONFIG_VERSION = '2.0'\n\n\nTEMPLATE_APP = \"\"\"\\\nfrom chalice import Chalice\n\napp = Chalice(app_name='%s')\n\n\n@app.route('/')\ndef index():\n    return {'hello': 'world'}\n\n\n# The view function above will return {\"hello\": \"world\"}\n# whenever you make an HTTP GET request to '/'.\n#\n# Here are a few more examples:\n#\n# @app.route('/hello/{name}')\n# def hello_name(name):\n#    # '/hello/james' -> {\"hello\": \"james\"}\n#    return {'hello': name}\n#\n# @app.route('/users', methods=['POST'])\n# def create_user():\n#     # This is the JSON body the user sent in their POST request.\n#     user_as_json = app.current_request.json_body\n#     # We'll echo the json body back to the user in a 'user' key.\n#     return {'user': user_as_json}\n#\n# See the README documentation for more examples.\n#\n\"\"\"\n\n\nGITIGNORE = \"\"\"\\\n.chalice/deployments/\n.chalice/venv/\n\"\"\"\n\nDEFAULT_STAGE_NAME = 'dev'\nDEFAULT_APIGATEWAY_STAGE_NAME = 'api'\nDEFAULT_ENDPOINT_TYPE = 'EDGE'\nDEFAULT_TLS_VERSION = 'TLS_1_2'\n\nDEFAULT_LAMBDA_TIMEOUT = 60\nDEFAULT_LAMBDA_MEMORY_SIZE = 128\nMAX_LAMBDA_DEPLOYMENT_SIZE = 50 * (1024 ** 2)\n# This is the name of the main handler used to\n# handle API gateway requests.  This is used as a key\n# in the config module.\nDEFAULT_HANDLER_NAME = 'api_handler'\n\nMIN_COMPRESSION_SIZE = 0\nMAX_COMPRESSION_SIZE = 10485760\n\nLAMBDA_TRUST_POLICY = {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [{\n        \"Sid\": \"\",\n        \"Effect\": \"Allow\",\n        \"Principal\": {\n            \"Service\": \"lambda.amazonaws.com\"\n        },\n        \"Action\": \"sts:AssumeRole\"\n    }]\n}\n\n\nCLOUDWATCH_LOGS = {\n    \"Effect\": \"Allow\",\n    \"Action\": [\n        \"logs:CreateLogGroup\",\n        \"logs:CreateLogStream\",\n        \"logs:PutLogEvents\"\n    ],\n    \"Resource\": \"arn:*:logs:*:*:*\"\n}\n\n\nVPC_ATTACH_POLICY = {\n    \"Effect\": \"Allow\",\n    \"Action\": [\n        \"ec2:CreateNetworkInterface\",\n        \"ec2:DescribeNetworkInterfaces\",\n        \"ec2:DetachNetworkInterface\",\n        \"ec2:DeleteNetworkInterface\"\n    ],\n    \"Resource\": \"*\"\n}\n\nXRAY_POLICY = {\n    'Effect': 'Allow',\n    'Action': [\n        'xray:PutTraceSegments',\n        'xray:PutTelemetryRecords',\n    ],\n    'Resource': '*'\n}\n\nCODEBUILD_POLICY = {\n    \"Version\": \"2012-10-17\",\n    # This is the policy straight from the console.\n    \"Statement\": [\n        {\n            \"Action\": [\n                \"logs:CreateLogGroup\",\n                \"logs:CreateLogStream\",\n                \"logs:PutLogEvents\"\n            ],\n            \"Resource\": \"*\",\n            \"Effect\": \"Allow\"\n        },\n        {\n            \"Action\": [\n                \"s3:GetObject\",\n                \"s3:GetObjectVersion\",\n                \"s3:PutObject\"\n            ],\n            \"Resource\": \"arn:*:s3:::*\",\n            \"Effect\": \"Allow\"\n        }\n    ]\n}\n\nCODEPIPELINE_POLICY = {\n    \"Version\": \"2012-10-17\",\n    # Also straight from the console setup.\n    \"Statement\": [\n        {\n            \"Action\": [\n                \"s3:GetObject\",\n                \"s3:GetObjectVersion\",\n                \"s3:GetBucketVersioning\",\n                \"s3:CreateBucket\",\n                \"s3:PutObject\",\n                \"s3:PutBucketVersioning\"\n            ],\n            \"Resource\": \"*\",\n            \"Effect\": \"Allow\"\n        },\n        {\n            \"Action\": [\n                \"codecommit:CancelUploadArchive\",\n                \"codecommit:GetBranch\",\n                \"codecommit:GetCommit\",\n                \"codecommit:GetUploadArchiveStatus\",\n                \"codecommit:UploadArchive\"\n            ],\n            \"Resource\": \"*\",\n            \"Effect\": \"Allow\"\n        },\n        {\n            \"Action\": [\n                \"cloudwatch:*\",\n                \"iam:PassRole\"\n            ],\n            \"Resource\": \"*\",\n            \"Effect\": \"Allow\"\n        },\n        {\n            \"Action\": [\n                \"lambda:InvokeFunction\",\n                \"lambda:ListFunctions\"\n            ],\n            \"Resource\": \"*\",\n            \"Effect\": \"Allow\"\n        },\n        {\n            \"Action\": [\n                \"cloudformation:CreateStack\",\n                \"cloudformation:DeleteStack\",\n                \"cloudformation:DescribeStacks\",\n                \"cloudformation:UpdateStack\",\n                \"cloudformation:CreateChangeSet\",\n                \"cloudformation:DeleteChangeSet\",\n                \"cloudformation:DescribeChangeSet\",\n                \"cloudformation:ExecuteChangeSet\",\n                \"cloudformation:SetStackPolicy\",\n                \"cloudformation:ValidateTemplate\",\n                \"iam:PassRole\"\n            ],\n            \"Resource\": \"*\",\n            \"Effect\": \"Allow\"\n        },\n        {\n            \"Action\": [\n                \"codebuild:BatchGetBuilds\",\n                \"codebuild:StartBuild\"\n            ],\n            \"Resource\": \"*\",\n            \"Effect\": \"Allow\"\n        }\n    ]\n}\n\n\nWELCOME_PROMPT = r\"\"\"\n\n   ___  _  _    _    _     ___  ___  ___\n  / __|| || |  /_\\  | |   |_ _|/ __|| __|\n | (__ | __ | / _ \\ | |__  | || (__ | _|\n  \\___||_||_|/_/ \\_\\|____||___|\\___||___|\n\n\nThe python serverless microframework for AWS allows\nyou to quickly create and deploy applications using\nAmazon API Gateway and AWS Lambda.\n\nPlease enter the project name\"\"\"\n\n\nMISSING_DEPENDENCIES_TEMPLATE = r\"\"\"\nCould not install dependencies:\n%s\nYou will have to build these yourself and vendor them in\nthe chalice vendor folder.\n\nYour deployment will continue but may not work correctly\nif missing dependencies are not present. For more information:\nhttp://aws.github.io/chalice/topics/packaging.html\n\n\"\"\"\n\n\nEXPERIMENTAL_ERROR_MSG = \"\"\"\n\nYou are using experimental features without explicitly opting in.\nExperimental features do not guarantee backwards compatibility and may be\nremoved in the future.  If you'd still like to use these experimental features,\nyou can opt in by adding this to your app.py file:\\n\\n%s\n\nSee https://aws.github.io/chalice/topics/experimental.html for more\ndetails.\n\"\"\"\n\n\nSQS_EVENT_SOURCE_POLICY = {\n    \"Effect\": \"Allow\",\n    \"Action\": [\n        \"sqs:ReceiveMessage\",\n        \"sqs:DeleteMessage\",\n        \"sqs:GetQueueAttributes\",\n    ],\n    \"Resource\": \"*\",\n}\n\n\nKINESIS_EVENT_SOURCE_POLICY = {\n    \"Effect\": \"Allow\",\n    \"Action\": [\n        \"kinesis:GetRecords\",\n        \"kinesis:GetShardIterator\",\n        \"kinesis:DescribeStream\",\n        \"kinesis:ListStreams\",\n    ],\n    \"Resource\": \"*\",\n}\n\n\nDDB_EVENT_SOURCE_POLICY = {\n    \"Effect\": \"Allow\",\n    \"Action\": [\n        \"dynamodb:DescribeStream\",\n        \"dynamodb:GetRecords\",\n        \"dynamodb:GetShardIterator\",\n        \"dynamodb:ListStreams\"\n    ],\n    \"Resource\": \"*\"\n}\n\n\nPOST_TO_WEBSOCKET_CONNECTION_POLICY = {\n    \"Effect\": \"Allow\",\n    \"Action\": [\n        \"execute-api:ManageConnections\"\n    ],\n    \"Resource\": \"arn:*:execute-api:*:*:*/@connections/*\"\n}\n"
  },
  {
    "path": "chalice/deploy/__init__.py",
    "content": ""
  },
  {
    "path": "chalice/deploy/appgraph.py",
    "content": "import json\nimport os\nfrom dataclasses import asdict\n\nfrom typing import cast\nfrom typing import Dict, List, Tuple, Any, Set, Optional, Text, Union  # noqa\n\nfrom chalice.config import Config  # noqa\nfrom chalice import app\nfrom chalice.constants import LAMBDA_TRUST_POLICY\nfrom chalice.deploy import models\nfrom chalice.utils import UI  # noqa\n\nStrMapAny = Dict[str, Any]\n\n\nclass ChaliceBuildError(Exception):\n    pass\n\n\nclass ApplicationGraphBuilder(object):\n    def __init__(self) -> None:\n        self._known_roles: Dict[str, models.IAMRole] = {}\n        self._managed_layer: Optional[models.LambdaLayer] = None\n\n    def build(self, config: Config, stage_name: str) -> models.Application:\n        resources: List[models.Model] = []\n        deployment = models.DeploymentPackage(models.Placeholder.BUILD_STAGE)\n        for function in config.chalice_app.pure_lambda_functions:\n            resource = self._create_lambda_model(\n                config=config,\n                deployment=deployment,\n                name=function.name,\n                handler_name=function.handler_string,\n                stage_name=stage_name,\n            )\n            resources.append(resource)\n        event_resources = self._create_lambda_event_resources(\n            config, deployment, stage_name\n        )\n        resources.extend(event_resources)\n        if config.chalice_app.routes:\n            rest_api = self._create_rest_api_model(\n                config, deployment, stage_name\n            )\n            resources.append(rest_api)\n        if config.chalice_app.websocket_handlers:\n            websocket_api = self._create_websocket_api_model(\n                config, deployment, stage_name\n            )\n            resources.append(websocket_api)\n        return models.Application(stage_name, resources)\n\n    def _create_log_group(\n        self, config: Config, resource_name: str, log_group_name: str\n    ) -> models.LogGroup:\n        return models.LogGroup(\n            resource_name=resource_name,\n            log_group_name=log_group_name,\n            retention_in_days=config.log_retention_in_days,\n        )\n\n    def _create_custom_domain_name(\n        self,\n        api_type: models.APIType,\n        domain_name_data: StrMapAny,\n        endpoint_configuration: str,\n        api_gateway_stage: str,\n    ) -> models.DomainName:\n        url_prefix = domain_name_data.get(\"url_prefix\", '(none)')\n        api_mapping_model = self._create_api_mapping_model(\n            url_prefix, api_gateway_stage\n        )\n        domain_name = self._create_domain_name_model(\n            api_type,\n            domain_name_data,\n            endpoint_configuration,\n            api_mapping_model,\n        )\n        return domain_name\n\n    def _create_api_mapping_model(\n        self, key: str, stage: str\n    ) -> models.APIMapping:\n        if key == '/':\n            key = '(none)'\n        return models.APIMapping(\n            resource_name='api_mapping',\n            mount_path=key,\n            api_gateway_stage=stage,\n        )\n\n    def _create_lambda_event_resources(\n        self,\n        config: Config,\n        deployment: models.DeploymentPackage,\n        stage_name: str,\n    ) -> List[models.Model]:\n        resources: List[models.Model] = []\n        for event_source in config.chalice_app.event_sources:\n            if isinstance(event_source, app.S3EventConfig):\n                resources.append(\n                    self._create_bucket_notification(\n                        config, deployment, event_source, stage_name\n                    )\n                )\n            elif isinstance(event_source, app.SNSEventConfig):\n                resources.append(\n                    self._create_sns_subscription(\n                        config,\n                        deployment,\n                        event_source,\n                        stage_name,\n                    )\n                )\n            elif isinstance(event_source, app.CloudWatchEventConfig):\n                resources.append(\n                    self._create_cwe_subscription(\n                        config, deployment, event_source, stage_name\n                    )\n                )\n            elif isinstance(event_source, app.ScheduledEventConfig):\n                resources.append(\n                    self._create_scheduled_model(\n                        config, deployment, event_source, stage_name\n                    )\n                )\n            elif isinstance(event_source, app.SQSEventConfig):\n                resources.append(\n                    self._create_sqs_subscription(\n                        config,\n                        deployment,\n                        event_source,\n                        stage_name,\n                    )\n                )\n            elif isinstance(event_source, app.KinesisEventConfig):\n                resources.append(\n                    self._create_kinesis_subscription(\n                        config,\n                        deployment,\n                        event_source,\n                        stage_name,\n                    )\n                )\n            elif isinstance(event_source, app.DynamoDBEventConfig):\n                resources.append(\n                    self._create_ddb_subscription(\n                        config,\n                        deployment,\n                        event_source,\n                        stage_name,\n                    )\n                )\n        return resources\n\n    def _create_rest_api_model(\n        self,\n        config: Config,\n        deployment: models.DeploymentPackage,\n        stage_name: str,\n    ) -> models.RestAPI:\n        # Need to mess with the function name for back-compat.\n        lambda_function = self._create_lambda_model(\n            config=config,\n            deployment=deployment,\n            name='api_handler',\n            handler_name='app.app',\n            stage_name=stage_name,\n        )\n        # For backwards compatibility with the old deployer, the\n        # lambda function for the API handler doesn't have the\n        # resource_name appended to its complete function_name,\n        # it's just <app>-<stage>.\n        function_name = '%s-%s' % (config.app_name, config.chalice_stage)\n        lambda_function.function_name = function_name\n        if config.minimum_compression_size is None:\n            minimum_compression = ''\n        else:\n            minimum_compression = str(config.minimum_compression_size)\n        authorizers = []\n        for auth in config.chalice_app.builtin_auth_handlers:\n            auth_lambda = self._create_lambda_model(\n                config=config,\n                deployment=deployment,\n                name=auth.name,\n                handler_name=auth.handler_string,\n                stage_name=stage_name,\n            )\n            authorizers.append(auth_lambda)\n\n        policy = None\n        policy_path = config.api_gateway_policy_file\n        if config.api_gateway_endpoint_type == 'PRIVATE' and not policy_path:\n            policy = models.IAMPolicy(\n                document=self._get_default_private_api_policy(config)\n            )\n        elif policy_path:\n            policy = models.FileBasedIAMPolicy(\n                document=models.Placeholder.BUILD_STAGE,\n                filename=os.path.join(\n                    config.project_dir, '.chalice', policy_path\n                ),\n            )\n\n        vpce_ids = None\n        if config.api_gateway_endpoint_vpce:\n            vpce = config.api_gateway_endpoint_vpce\n            vpce_ids = [vpce] if isinstance(vpce, str) else vpce\n\n        custom_domain_name = None\n        if config.api_gateway_custom_domain:\n            custom_domain_name = self._create_custom_domain_name(\n                models.APIType.HTTP,\n                config.api_gateway_custom_domain,\n                config.api_gateway_endpoint_type,\n                config.api_gateway_stage,\n            )\n\n        return models.RestAPI(\n            resource_name='rest_api',\n            swagger_doc=models.Placeholder.BUILD_STAGE,\n            endpoint_type=config.api_gateway_endpoint_type,\n            minimum_compression=minimum_compression,\n            api_gateway_stage=config.api_gateway_stage,\n            lambda_function=lambda_function,\n            authorizers=authorizers,\n            policy=policy,\n            domain_name=custom_domain_name,\n            xray=config.xray_enabled,\n            vpce_ids=vpce_ids,\n        )\n\n    def _get_default_private_api_policy(self, config: Config) -> StrMapAny:\n        statements = [\n            {\n                \"Effect\": \"Allow\",\n                \"Principal\": \"*\",\n                \"Action\": \"execute-api:Invoke\",\n                \"Resource\": \"arn:*:execute-api:*:*:*\",\n                \"Condition\": {\n                    \"StringEquals\": {\n                        \"aws:SourceVpce\": config.api_gateway_endpoint_vpce\n                    }\n                },\n            }\n        ]\n        return {\"Version\": \"2012-10-17\", \"Statement\": statements}\n\n    def _create_websocket_api_model(\n        self,\n        config: Config,\n        deployment: models.DeploymentPackage,\n        stage_name: str,\n    ) -> models.WebsocketAPI:\n        connect_handler: Optional[models.LambdaFunction] = None\n        message_handler: Optional[models.LambdaFunction] = None\n        disconnect_handler: Optional[models.LambdaFunction] = None\n\n        routes = {\n            h.route_key_handled: h.handler_string\n            for h in config.chalice_app.websocket_handlers.values()\n        }\n        if '$connect' in routes:\n            connect_handler = self._create_lambda_model(\n                config=config,\n                deployment=deployment,\n                name='websocket_connect',\n                handler_name=routes['$connect'],\n                stage_name=stage_name,\n            )\n            routes.pop('$connect')\n        if '$disconnect' in routes:\n            disconnect_handler = self._create_lambda_model(\n                config=config,\n                deployment=deployment,\n                name='websocket_disconnect',\n                handler_name=routes['$disconnect'],\n                stage_name=stage_name,\n            )\n            routes.pop('$disconnect')\n        if routes:\n            # If there are left over routes they are message handlers.\n            handler_string = list(routes.values())[0]\n            message_handler = self._create_lambda_model(\n                config=config,\n                deployment=deployment,\n                name='websocket_message',\n                handler_name=handler_string,\n                stage_name=stage_name,\n            )\n\n        custom_domain_name = None\n        if config.websocket_api_custom_domain:\n            custom_domain_name = self._create_custom_domain_name(\n                models.APIType.WEBSOCKET,\n                config.websocket_api_custom_domain,\n                config.api_gateway_endpoint_type,\n                config.api_gateway_stage,\n            )\n\n        return models.WebsocketAPI(\n            name='%s-%s-websocket-api' % (config.app_name, stage_name),\n            resource_name='websocket_api',\n            connect_function=connect_handler,\n            message_function=message_handler,\n            disconnect_function=disconnect_handler,\n            routes=[\n                h.route_key_handled\n                for h in config.chalice_app.websocket_handlers.values()\n            ],\n            api_gateway_stage=config.api_gateway_stage,\n            domain_name=custom_domain_name,\n        )\n\n    def _create_cwe_subscription(\n        self,\n        config: Config,\n        deployment: models.DeploymentPackage,\n        event_source: app.CloudWatchEventConfig,\n        stage_name: str,\n    ) -> models.CloudWatchEvent:\n        lambda_function = self._create_lambda_model(\n            config=config,\n            deployment=deployment,\n            name=event_source.name,\n            handler_name=event_source.handler_string,\n            stage_name=stage_name,\n        )\n\n        resource_name = event_source.name + '-event'\n        rule_name = '%s-%s-%s' % (\n            config.app_name,\n            config.chalice_stage,\n            resource_name,\n        )\n        cwe = models.CloudWatchEvent(\n            resource_name=resource_name,\n            rule_name=rule_name,\n            event_pattern=json.dumps(event_source.event_pattern),\n            lambda_function=lambda_function,\n        )\n        return cwe\n\n    def _create_scheduled_model(\n        self,\n        config: Config,\n        deployment: models.DeploymentPackage,\n        event_source: app.ScheduledEventConfig,\n        stage_name: str,\n    ) -> models.ScheduledEvent:\n        lambda_function = self._create_lambda_model(\n            config=config,\n            deployment=deployment,\n            name=event_source.name,\n            handler_name=event_source.handler_string,\n            stage_name=stage_name,\n        )\n        # Resource names must be unique across a chalice app.\n        # However, in the original deployer code, the cloudwatch\n        # event + lambda function was considered a single resource.\n        # Now that they're treated as two separate resources we need\n        # a unique name for the event_source that's not the lambda\n        # function resource name.  We handle this by just appending\n        # '-event' to the name.  Ideally this is handled in app.py\n        # but we won't be able to do that until the old deployer\n        # is gone.\n        resource_name = event_source.name + '-event'\n        if isinstance(\n            event_source.schedule_expression, app.ScheduleExpression\n        ):\n            expression = event_source.schedule_expression.to_string()\n        else:\n            expression = event_source.schedule_expression\n        rule_name = '%s-%s-%s' % (\n            config.app_name,\n            config.chalice_stage,\n            resource_name,\n        )\n        scheduled_event = models.ScheduledEvent(\n            resource_name=resource_name,\n            rule_name=rule_name,\n            rule_description=event_source.description,\n            schedule_expression=expression,\n            lambda_function=lambda_function,\n        )\n        return scheduled_event\n\n    def _create_domain_name_model(\n        self,\n        protocol: models.APIType,\n        data: StrMapAny,\n        endpoint_type: str,\n        api_mapping: models.APIMapping,\n    ) -> models.DomainName:\n        default_name = 'api_gateway_custom_domain'\n        resource_name_map: Dict[str, str] = {\n            'HTTP': default_name,\n            'WEBSOCKET': 'websocket_api_custom_domain',\n        }\n\n        domain_name = models.DomainName(\n            protocol=protocol,\n            resource_name=resource_name_map.get(protocol.value, default_name),\n            domain_name=data['domain_name'],\n            tls_version=models.TLSVersion.create(data.get('tls_version', '')),\n            certificate_arn=data['certificate_arn'],\n            tags=data.get('tags'),\n            api_mapping=api_mapping,\n        )\n        return domain_name\n\n    def _create_lambda_model(\n        self,\n        config: Config,\n        deployment: models.DeploymentPackage,\n        name: str,\n        handler_name: str,\n        stage_name: str,\n    ) -> models.LambdaFunction:\n        new_config = config.scope(\n            chalice_stage=config.chalice_stage, function_name=name\n        )\n        role = self._get_role_reference(new_config, stage_name, name)\n        resource = self._build_lambda_function(\n            new_config, name, handler_name, deployment, role\n        )\n        if new_config.log_retention_in_days:\n            log_resource_name = '%s-log-group' % name\n            log_group_name = '/aws/lambda/%s-%s-%s' % (\n                new_config.app_name,\n                stage_name,\n                name,\n            )\n            resource.log_group = self._create_log_group(\n                new_config, log_resource_name, log_group_name\n            )\n        return resource\n\n    def _get_managed_lambda_layer(\n        self, config: Config\n    ) -> Optional[models.LambdaLayer]:\n        if not config.automatic_layer:\n            return None\n        if self._managed_layer is None:\n            self._managed_layer = models.LambdaLayer(\n                resource_name='managed-layer',\n                layer_name='%s-%s-%s'\n                % (config.app_name, config.chalice_stage, 'managed-layer'),\n                runtime=config.lambda_python_version,\n                deployment_package=models.DeploymentPackage(\n                    models.Placeholder.BUILD_STAGE\n                ),\n            )\n        return self._managed_layer\n\n    def _get_role_reference(\n        self, config: Config, stage_name: str, function_name: str\n    ) -> models.IAMRole:\n        role = self._create_role_reference(config, stage_name, function_name)\n        role_identifier = self._get_role_identifier(role)\n        if role_identifier in self._known_roles:\n            # If we've already create a models.IAMRole with the same\n            # identifier, we'll use the existing object instead of\n            # creating a new one.\n            return self._known_roles[role_identifier]\n        self._known_roles[role_identifier] = role\n        return role\n\n    def _get_role_identifier(self, role: models.IAMRole) -> str:\n        if isinstance(role, models.PreCreatedIAMRole):\n            return role.role_arn\n        # We know that if it's not a PreCreatedIAMRole, it's\n        # a managed role, so we're using cast() to make mypy happy.\n        role = cast(models.ManagedIAMRole, role)\n        return role.resource_name\n\n    def _create_role_reference(\n        self, config: Config, stage_name: str, function_name: str\n    ) -> models.IAMRole:\n        # First option, the user doesn't want us to manage\n        # the role at all.\n        if not config.manage_iam_role:\n            # We've already validated the iam_role_arn is provided\n            # if manage_iam_role is set to False.\n            return models.PreCreatedIAMRole(\n                role_arn=config.iam_role_arn,\n            )\n        policy = models.IAMPolicy(document=models.Placeholder.BUILD_STAGE)\n        if not config.autogen_policy:\n            resource_name = '%s_role' % function_name\n            role_name = '%s-%s-%s' % (\n                config.app_name,\n                stage_name,\n                function_name,\n            )\n            if config.iam_policy_file is not None:\n                filename = os.path.join(\n                    config.project_dir, '.chalice', config.iam_policy_file\n                )\n            else:\n                filename = os.path.join(\n                    config.project_dir,\n                    '.chalice',\n                    'policy-%s.json' % stage_name,\n                )\n            policy = models.FileBasedIAMPolicy(\n                filename=filename, document=models.Placeholder.BUILD_STAGE\n            )\n        else:\n            resource_name = 'default-role'\n            role_name = '%s-%s' % (config.app_name, stage_name)\n            policy = models.AutoGenIAMPolicy(\n                document=models.Placeholder.BUILD_STAGE,\n                traits=set([]),\n            )\n        return models.ManagedIAMRole(\n            resource_name=resource_name,\n            role_name=role_name,\n            trust_policy=LAMBDA_TRUST_POLICY,\n            policy=policy,\n        )\n\n    def _get_vpc_params(\n        self, function_name: str, config: Config\n    ) -> Tuple[List[str], List[str]]:\n        security_group_ids = config.security_group_ids\n        subnet_ids = config.subnet_ids\n        if security_group_ids and subnet_ids:\n            return security_group_ids, subnet_ids\n        elif not security_group_ids and not subnet_ids:\n            return [], []\n        else:\n            raise ChaliceBuildError(\n                \"Invalid VPC params for function '%s', in order to configure \"\n                \"VPC for a Lambda function, you must provide the subnet_ids \"\n                \"as well as the security_group_ids, got subnet_ids: %s, \"\n                \"security_group_ids: %s\"\n                % (function_name, subnet_ids, security_group_ids)\n            )\n\n    def _get_lambda_layers(self, config: Config) -> List[str]:\n        layers = config.layers\n        return layers if layers else []\n\n    def _build_lambda_function(\n        self,\n        config: Config,\n        name: str,\n        handler_name: str,\n        deployment: models.DeploymentPackage,\n        role: models.IAMRole,\n    ) -> models.LambdaFunction:\n        function_name = '%s-%s-%s' % (\n            config.app_name,\n            config.chalice_stage,\n            name,\n        )\n        security_group_ids, subnet_ids = self._get_vpc_params(name, config)\n        lambda_layers = self._get_lambda_layers(config)\n        function = models.LambdaFunction(\n            resource_name=name,\n            function_name=function_name,\n            environment_variables=config.environment_variables,\n            runtime=config.lambda_python_version,\n            handler=handler_name,\n            tags=config.tags,\n            timeout=config.lambda_timeout,\n            memory_size=config.lambda_memory_size,\n            deployment_package=deployment,\n            role=role,\n            security_group_ids=security_group_ids,\n            subnet_ids=subnet_ids,\n            reserved_concurrency=config.reserved_concurrency,\n            layers=lambda_layers,\n            managed_layer=self._get_managed_lambda_layer(config),\n            xray=config.xray_enabled,\n        )\n        self._inject_role_traits(function, role)\n        return function\n\n    def _inject_role_traits(\n        self, function: models.LambdaFunction, role: models.IAMRole\n    ) -> None:\n        if not isinstance(role, models.ManagedIAMRole):\n            return\n        policy = role.policy\n        if not isinstance(policy, models.AutoGenIAMPolicy):\n            return\n        if function.security_group_ids and function.subnet_ids:\n            policy.traits.add(models.RoleTraits.VPC_NEEDED)\n\n    def _create_bucket_notification(\n        self,\n        config: Config,\n        deployment: models.DeploymentPackage,\n        s3_event: app.S3EventConfig,\n        stage_name: str,\n    ) -> models.S3BucketNotification:\n        lambda_function = self._create_lambda_model(\n            config=config,\n            deployment=deployment,\n            name=s3_event.name,\n            handler_name=s3_event.handler_string,\n            stage_name=stage_name,\n        )\n        resource_name = s3_event.name + '-s3event'\n        s3_bucket = models.S3BucketNotification(\n            resource_name=resource_name,\n            bucket=s3_event.bucket,\n            prefix=s3_event.prefix,\n            suffix=s3_event.suffix,\n            events=s3_event.events,\n            lambda_function=lambda_function,\n        )\n        return s3_bucket\n\n    def _create_sns_subscription(\n        self,\n        config: Config,\n        deployment: models.DeploymentPackage,\n        sns_config: app.SNSEventConfig,\n        stage_name: str,\n    ) -> models.SNSLambdaSubscription:\n        lambda_function = self._create_lambda_model(\n            config=config,\n            deployment=deployment,\n            name=sns_config.name,\n            handler_name=sns_config.handler_string,\n            stage_name=stage_name,\n        )\n        resource_name = sns_config.name + '-sns-subscription'\n        sns_subscription = models.SNSLambdaSubscription(\n            resource_name=resource_name,\n            topic=sns_config.topic,\n            lambda_function=lambda_function,\n        )\n        return sns_subscription\n\n    def _create_sqs_subscription(\n        self,\n        config: Config,\n        deployment: models.DeploymentPackage,\n        sqs_config: app.SQSEventConfig,\n        stage_name: str,\n    ) -> models.SQSEventSource:\n        lambda_function = self._create_lambda_model(\n            config=config,\n            deployment=deployment,\n            name=sqs_config.name,\n            handler_name=sqs_config.handler_string,\n            stage_name=stage_name,\n        )\n        resource_name = sqs_config.name + '-sqs-event-source'\n        queue: Union[str, models.QueueARN] = ''\n        if sqs_config.queue_arn is not None:\n            queue = models.QueueARN(arn=sqs_config.queue_arn)\n        elif sqs_config.queue is not None:\n            queue = sqs_config.queue\n        batch_window = sqs_config.maximum_batching_window_in_seconds\n        sqs_event_source = models.SQSEventSource(\n            resource_name=resource_name,\n            queue=queue,\n            batch_size=sqs_config.batch_size,\n            lambda_function=lambda_function,\n            maximum_batching_window_in_seconds=batch_window,\n            maximum_concurrency=sqs_config.maximum_concurrency\n        )\n        return sqs_event_source\n\n    def _create_kinesis_subscription(\n        self,\n        config: Config,\n        deployment: models.DeploymentPackage,\n        kinesis_config: app.KinesisEventConfig,\n        stage_name: str,\n    ) -> models.KinesisEventSource:\n        lambda_function = self._create_lambda_model(\n            config=config,\n            deployment=deployment,\n            name=kinesis_config.name,\n            handler_name=kinesis_config.handler_string,\n            stage_name=stage_name,\n        )\n        resource_name = kinesis_config.name + '-kinesis-event-source'\n        batch_window = kinesis_config.maximum_batching_window_in_seconds\n        kinesis_event_source = models.KinesisEventSource(\n            resource_name=resource_name,\n            stream=kinesis_config.stream,\n            batch_size=kinesis_config.batch_size,\n            maximum_batching_window_in_seconds=batch_window,\n            starting_position=kinesis_config.starting_position,\n            lambda_function=lambda_function,\n        )\n        return kinesis_event_source\n\n    def _create_ddb_subscription(\n        self,\n        config: Config,\n        deployment: models.DeploymentPackage,\n        ddb_config: app.DynamoDBEventConfig,\n        stage_name: str,\n    ) -> models.DynamoDBEventSource:\n        lambda_function = self._create_lambda_model(\n            config=config,\n            deployment=deployment,\n            name=ddb_config.name,\n            handler_name=ddb_config.handler_string,\n            stage_name=stage_name,\n        )\n        resource_name = ddb_config.name + '-dynamodb-event-source'\n        batch_window = ddb_config.maximum_batching_window_in_seconds\n        ddb_event_source = models.DynamoDBEventSource(\n            resource_name=resource_name,\n            stream_arn=ddb_config.stream_arn,\n            batch_size=ddb_config.batch_size,\n            maximum_batching_window_in_seconds=batch_window,\n            starting_position=ddb_config.starting_position,\n            lambda_function=lambda_function,\n        )\n        return ddb_event_source\n\n\nclass DependencyBuilder(object):\n    def __init__(self) -> None:\n        pass\n\n    def build_dependencies(self, graph: models.Model) -> List[models.Model]:\n        seen: Set[int] = set()\n        ordered: List[models.Model] = []\n        for resource in graph.dependencies():\n            self._traverse(resource, ordered, seen)\n        return ordered\n\n    def _traverse(\n        self,\n        resource: models.Model,\n        ordered: List[models.Model],\n        seen: Set[int],\n    ) -> None:\n        for dep in resource.dependencies():\n            if id(dep) not in seen:\n                seen.add(id(dep))\n                self._traverse(dep, ordered, seen)\n        # If recreating this list is a perf issue later on,\n        # we can create yet-another set of ids that gets updated\n        # when we add a resource to the ordered list.\n        if id(resource) not in [id(r) for r in ordered]:\n            ordered.append(resource)\n\n\nclass GraphPrettyPrint(object):\n\n    _NEW_SECTION = '\\u251c\\u2500\\u2500'\n    _LINE_VERTICAL = '\\u2502'\n\n    def __init__(self, ui: UI) -> None:\n        self._ui = ui\n\n    def display_graph(self, graph: models.Model) -> None:\n        self._ui.write(\"Application\\n\")\n        for model in graph.dependencies():\n            self._traverse(model, level=0)\n\n    def _traverse(self, graph: models.Model, level: int) -> None:\n        prefix = ('%s   ' % self._LINE_VERTICAL) * level\n        spaces = prefix + self._NEW_SECTION + ' '\n        model_text = self._get_model_text(graph, spaces, level)\n        current_line = cast(str, '%s%s\\n' % (spaces, model_text))\n        self._ui.write(current_line)\n        for model in graph.dependencies():\n            self._traverse(model, level + 1)\n\n    def _get_model_text(\n        self, model: models.Model, spaces: Text, level: int\n    ) -> Text:\n        name = model.__class__.__name__\n        filtered = self._get_filtered_params(model)\n        if not filtered:\n            return '%s()' % name\n        total_len_prefix = len(spaces) + len(name) + 1\n        prefix = ('%s   ' % self._LINE_VERTICAL) * (level + 2)\n        full = '%s%s' % (prefix, ' ' * (total_len_prefix - len(prefix)))\n        param_items = list(filtered.items())\n        first = param_items[0]\n        remaining = param_items[1:]\n        lines = ['%s(%s=%s,' % (name, first[0], first[1])]\n        self._add_remaining_lines(lines, remaining, full)\n        return '\\n'.join(lines) + ')'\n\n    def _add_remaining_lines(\n        self, lines: List[str], remaining: List[Tuple[str, Any]], full: Text\n    ) -> None:\n        for key, value in remaining:\n            if isinstance(value, (list, dict)):\n                value = key.upper()\n            current = cast(str, '%s%s=%s,' % (full, key, value))\n            lines.append(current)\n\n    def _get_filtered_params(self, model: models.Model) -> StrMapAny:\n        dependencies = model.dependencies()\n        filtered = {\n            k: v for k, v in asdict(model).items() if v not in dependencies\n        }\n        return filtered\n"
  },
  {
    "path": "chalice/deploy/deployer.py",
    "content": "\"\"\"Chalice deployer module.\n\nThe deployment system in chalice is broken down into a pipeline of multiple\nstages.  Each stage takes the input and transforms it to some other form.  The\nreason for this is so that each stage can stay simple and focused on only a\nsingle part of the deployment process.  This makes the code easier to follow\nand easier to test.  The biggest downside is that adding support for a new\nresource type is split across several objects now, but I imagine as we add\nsupport for more resource types, we'll see common patterns emerge that we can\nextract out into higher levels of abstraction.\n\nThese are the stages of the deployment process.\n\nApplication Graph Builder\n=========================\n\nThe first stage is the resource graph builder.  This takes the objects in the\n``Chalice`` app and structures them into an ``Application`` object which\nconsists of various ``models.Model`` objects.  These models are just python\nobjects that describe the attributes of various AWS resources.  These\nmodels don't have any behavior on their own.\n\nDependency Builder\n==================\n\nThis process takes the graph of resources created from the previous step and\norders them such that all objects are listed before objects that depend on\nthem.  The AWS resources in the ``chalice.deploy.models`` module also model\ntheir required dependencies (see the ``dependencies()`` methods of the models).\nThis is the mechanism that's used to build the correct dependency ordering.\n\nLocal Build Stage\n=================\n\nThis takes the ordered list of resources and allows any local build processes\nto occur.  The rule of thumb here is no remote AWS calls.  This stage includes\nauto policy generation, pip packaging, injecting default values, etc.  To\nclarify which attributes are affected by the build stage, they'll usually have\na value of ``models.Placeholder.BUILD_STAGE``.  Processors in the build stage\nwill replaced those ``models.Placeholder.BUILD_STAGE`` values with whatever the\n\"built\" value is (e.g the filename of the zipped deployment package).\n\nFor example, we know when we create a lambda function that we need to create a\ndeployment package, but we don't know the name nor contents of the deployment\npackage until the ``LambdaDeploymentPackager`` runs.  Therefore, the Resource\nBuilder stage can record the fact that it knows that a\n``models.DeploymentPackage`` is needed, but use\n``models.Placeholder.BUILD_STAGE`` for the value of the filename.  The enum\nvalues aren't strictly necessary, they just add clarity about when this value\nis expected to be filled in.  These could also just be set to ``None`` and be\nof type ``Optional[T]``.\n\n\nExecution Plan Stage\n====================\n\nThis stage takes the ordered list of resources and figures out what AWS API\ncalls we have to make.  For example, if a resource doesn't exist at all, we'll\nneed to make a ``create_*`` call.  If the resource exists, we may need to make\na series of ``update_*`` calls.  If the resource exists and is already up to\ndate, we might not need to make any calls at all.  The output of this stage is\na list of ``APICall`` objects.  This stage doesn't actually make the mutating\nAPI calls, it only figures out what calls we should make.  This stage will\ntypically only make ``describe/list`` AWS calls.\n\n\nThe Executor\n============\n\nThis takes the list of ``APICall`` objects from the previous stage and finally\nexecutes them.  It also manages taking the output of API calls and storing them\nin variables so they can be referenced in subsequent ``APICall`` objects (see\nthe ``Variable`` class to see how this is used).  For example, if a lambda\nfunction needs the ``role_arn`` that's the result of a previous ``create_role``\nAPI call, a ``Variable`` object is used to forward this information.\n\nThe executor also records these variables with their associated resources so a\n``deployed.json`` file can be written to disk afterwards.  An ``APICall``\ntakes an optional resource object when it's created whose ``resource_name``\nis used as the key in the ``deployed.json`` dictionary.\n\n\n\"\"\"\n# pylint: disable=too-many-lines\nimport json\nimport textwrap\nimport socket\nimport logging\n\nimport botocore.exceptions\nfrom botocore.vendored.requests import ConnectionError as \\\n    RequestsConnectionError\nfrom botocore.session import Session  # noqa\nfrom typing import Optional, Dict, List, Any, Type, cast  # noqa\n\nfrom chalice.config import Config  # noqa\nfrom chalice.compat import is_broken_pipe_error\nfrom chalice.awsclient import DeploymentPackageTooLargeError\nfrom chalice.awsclient import LambdaClientError\nfrom chalice.awsclient import AWSClientError\nfrom chalice.awsclient import TypedAWSClient\nfrom chalice.constants import MAX_LAMBDA_DEPLOYMENT_SIZE\nfrom chalice.constants import VPC_ATTACH_POLICY\nfrom chalice.constants import DEFAULT_LAMBDA_TIMEOUT\nfrom chalice.constants import DEFAULT_LAMBDA_MEMORY_SIZE\nfrom chalice.constants import DEFAULT_TLS_VERSION\nfrom chalice.constants import SQS_EVENT_SOURCE_POLICY\nfrom chalice.constants import KINESIS_EVENT_SOURCE_POLICY\nfrom chalice.constants import DDB_EVENT_SOURCE_POLICY\nfrom chalice.constants import POST_TO_WEBSOCKET_CONNECTION_POLICY\nfrom chalice.deploy import models\nfrom chalice.deploy.appgraph import ApplicationGraphBuilder, DependencyBuilder\nfrom chalice.deploy.executor import BaseExecutor  # noqa\nfrom chalice.deploy.executor import Executor\nfrom chalice.deploy.executor import DisplayOnlyExecutor\nfrom chalice.deploy.packager import PipRunner\nfrom chalice.deploy.packager import SubprocessPip\nfrom chalice.deploy.packager import DependencyBuilder as PipDependencyBuilder\nfrom chalice.deploy.packager import LambdaDeploymentPackager\nfrom chalice.deploy.packager import AppOnlyDeploymentPackager\nfrom chalice.deploy.packager import LayerDeploymentPackager\nfrom chalice.deploy.packager import BaseLambdaDeploymentPackager  # noqa\nfrom chalice.deploy.packager import EmptyPackageError\nfrom chalice.deploy.planner import PlanStage\nfrom chalice.deploy.planner import RemoteState\nfrom chalice.deploy.planner import NoopPlanner\nfrom chalice.deploy.swagger import TemplatedSwaggerGenerator\nfrom chalice.deploy.swagger import SwaggerGenerator  # noqa\nfrom chalice.deploy.sweeper import ResourceSweeper\nfrom chalice.deploy.validate import validate_configuration\nfrom chalice.policy import AppPolicyGenerator\nfrom chalice.utils import OSUtils\nfrom chalice.utils import UI\nfrom chalice.utils import serialize_to_json\n\n\nOptStr = Optional[str]\nLOGGER = logging.getLogger(__name__)\n\n\n_AWSCLIENT_EXCEPTIONS = (\n    botocore.exceptions.ClientError, AWSClientError\n)\n\n\nclass ChaliceDeploymentError(Exception):\n    def __init__(self, error):\n        # type: (Exception) -> None\n        self.original_error = error\n        where = self._get_error_location(error)\n        msg = self._wrap_text(\n            'ERROR - %s, received the following error:' % where\n        )\n        msg += '\\n\\n'\n        msg += self._wrap_text(self._get_error_message(error), indent=' ')\n        msg += '\\n\\n'\n        suggestion = self._get_error_suggestion(error)\n        if suggestion is not None:\n            msg += self._wrap_text(suggestion)\n        super(ChaliceDeploymentError, self).__init__(msg)\n\n    def _get_error_location(self, error):\n        # type: (Exception) -> str\n        where = 'While deploying your chalice application'\n        if isinstance(error, LambdaClientError):\n            where = (\n                'While sending your chalice handler code to Lambda to %s '\n                'function \"%s\"' % (\n                    self._get_verb_from_client_method(\n                        error.context.client_method_name),\n                    error.context.function_name\n                )\n            )\n        return where\n\n    def _get_error_message(self, error):\n        # type: (Exception) -> str\n        msg = str(error)\n        if isinstance(error, LambdaClientError):\n            if isinstance(error.original_error, RequestsConnectionError):\n                msg = self._get_error_message_for_connection_error(\n                    error.original_error)\n        return msg\n\n    def _get_error_message_for_connection_error(self, connection_error):\n        # type: (RequestsConnectionError) -> str\n\n        # To get the underlying error that raised the\n        # requests.ConnectionError it is required to go down two levels of\n        # arguments to get the underlying exception. The instantiation of\n        # one of these exceptions looks like this:\n        #\n        # requests.ConnectionError(\n        #     urllib3.exceptions.ProtocolError(\n        #         'Connection aborted.', <SomeException>)\n        # )\n        message = connection_error.args[0].args[0]\n        underlying_error = connection_error.args[0].args[1]\n        if is_broken_pipe_error(underlying_error):\n            message += (\n                ' Lambda closed the connection before chalice finished '\n                'sending all of the data.'\n            )\n        elif isinstance(underlying_error, socket.timeout):\n            message += ' Timed out sending your app to Lambda.'\n        return message\n\n    def _get_error_suggestion(self, error):\n        # type: (Exception) -> OptStr\n        suggestion = None\n        if isinstance(error, DeploymentPackageTooLargeError):\n            suggestion = (\n                'To avoid this error, decrease the size of your chalice '\n                'application by removing code or removing '\n                'dependencies from your chalice application.'\n            )\n            deployment_size = error.context.deployment_size\n            if deployment_size > MAX_LAMBDA_DEPLOYMENT_SIZE:\n                size_warning = (\n                    'This is likely because the deployment package is %s. '\n                    'Lambda only allows deployment packages that are %s or '\n                    'less in size.' % (\n                        self._get_mb(deployment_size),\n                        self._get_mb(MAX_LAMBDA_DEPLOYMENT_SIZE)\n                    )\n                )\n                suggestion = size_warning + ' ' + suggestion\n        return suggestion\n\n    def _wrap_text(self, text, indent=''):\n        # type: (str, str) -> str\n        return '\\n'.join(\n            textwrap.wrap(\n                text, 79, replace_whitespace=False, drop_whitespace=False,\n                initial_indent=indent, subsequent_indent=indent\n            )\n        )\n\n    def _get_verb_from_client_method(self, client_method_name):\n        # type: (str) -> str\n        client_method_name_to_verb = {\n            'update_function_code': 'update',\n            'create_function': 'create'\n        }\n        return client_method_name_to_verb.get(\n            client_method_name, client_method_name)\n\n    def _get_mb(self, value):\n        # type: (int) -> str\n        return '%.1f MB' % (float(value) / (1024 ** 2))\n\n\ndef create_plan_only_deployer(session, config, ui):\n    # type: (Session, Config, UI) -> Deployer\n    return _create_deployer(session, config, ui, DisplayOnlyExecutor,\n                            NoopResultsRecorder)\n\n\ndef create_default_deployer(session, config, ui):\n    # type: (Session, Config, UI) -> Deployer\n    return _create_deployer(session, config, ui, Executor, ResultsRecorder)\n\n\ndef _create_deployer(session,       # type: Session\n                     config,        # type: Config\n                     ui,            # type: UI\n                     executor_cls,  # type: Type[BaseExecutor]\n                     recorder_cls,  # type: Type[ResultsRecorder]\n                     ):\n    # type: (...) -> Deployer\n    client = TypedAWSClient(session)\n    osutils = OSUtils()\n    return Deployer(\n        application_builder=ApplicationGraphBuilder(),\n        deps_builder=DependencyBuilder(),\n        build_stage=create_build_stage(\n            osutils, UI(), TemplatedSwaggerGenerator(), config\n        ),\n        plan_stage=PlanStage(\n            osutils=osutils, remote_state=RemoteState(\n                client, config.deployed_resources(config.chalice_stage)),\n        ),\n        sweeper=ResourceSweeper(),\n        executor=executor_cls(client, ui),\n        recorder=recorder_cls(osutils=osutils),\n    )\n\n\ndef create_build_stage(osutils, ui, swagger_gen, config):\n    # type: (OSUtils, UI, SwaggerGenerator, Config) -> BuildStage\n    pip_runner = PipRunner(pip=SubprocessPip(osutils=osutils),\n                           osutils=osutils)\n    dependency_builder = PipDependencyBuilder(\n        osutils=osutils,\n        pip_runner=pip_runner\n    )\n    deployment_packager = cast(BaseDeployStep, None)\n    if config.automatic_layer:\n        deployment_packager = ManagedLayerDeploymentPackager(\n            lambda_packager=AppOnlyDeploymentPackager(\n                osutils=osutils,\n                dependency_builder=dependency_builder,\n                ui=ui,\n            ),\n            layer_packager=LayerDeploymentPackager(\n                osutils=osutils,\n                dependency_builder=dependency_builder,\n                ui=ui,\n            )\n        )\n    else:\n        deployment_packager = DeploymentPackager(\n            packager=LambdaDeploymentPackager(\n                osutils=osutils,\n                dependency_builder=dependency_builder,\n                ui=ui,\n            )\n        )\n    build_stage = BuildStage(\n        steps=[\n            InjectDefaults(),\n            deployment_packager,\n            PolicyGenerator(\n                policy_gen=AppPolicyGenerator(\n                    osutils=osutils\n                ),\n                osutils=osutils,\n            ),\n            SwaggerBuilder(\n                swagger_generator=swagger_gen,\n            ),\n            LambdaEventSourcePolicyInjector(),\n            WebsocketPolicyInjector()\n        ],\n    )\n    return build_stage\n\n\ndef create_deletion_deployer(client, ui):\n    # type: (TypedAWSClient, UI) -> Deployer\n    return Deployer(\n        application_builder=ApplicationGraphBuilder(),\n        deps_builder=DependencyBuilder(),\n        build_stage=BuildStage(steps=[]),\n        plan_stage=NoopPlanner(),\n        sweeper=ResourceSweeper(),\n        executor=Executor(client, ui),\n        recorder=ResultsRecorder(osutils=OSUtils()),\n    )\n\n\nclass Deployer(object):\n    BACKEND_NAME = 'api'\n\n    def __init__(self,\n                 application_builder,  # type: ApplicationGraphBuilder\n                 deps_builder,         # type: DependencyBuilder\n                 build_stage,          # type: BuildStage\n                 plan_stage,           # type: PlanStage\n                 sweeper,              # type: ResourceSweeper\n                 executor,             # type: BaseExecutor\n                 recorder,             # type: ResultsRecorder\n                 ):\n        # type: (...) -> None\n        self._application_builder = application_builder\n        self._deps_builder = deps_builder\n        self._build_stage = build_stage\n        self._plan_stage = plan_stage\n        self._sweeper = sweeper\n        self._executor = executor\n        self._recorder = recorder\n\n    def deploy(self, config, chalice_stage_name):\n        # type: (Config, str) -> Dict[str, Any]\n        try:\n            return self._deploy(config, chalice_stage_name)\n        except _AWSCLIENT_EXCEPTIONS as e:\n            raise ChaliceDeploymentError(e)\n\n    def _deploy(self, config, chalice_stage_name):\n        # type: (Config, str) -> Dict[str, Any]\n        self._validate_config(config)\n        application = self._application_builder.build(\n            config, chalice_stage_name)\n        resources = self._deps_builder.build_dependencies(application)\n        self._build_stage.execute(config, resources)\n        # Rebuild dependencies in case the build stage modified\n        # the app graph.\n        resources = self._deps_builder.build_dependencies(application)\n        plan = self._plan_stage.execute(resources)\n        self._sweeper.execute(plan, config)\n        self._executor.execute(plan)\n        deployed_values = {\n            'resources': self._executor.resource_values,\n            'schema_version': '2.0',\n            'backend': self.BACKEND_NAME,\n        }\n        self._recorder.record_results(\n            deployed_values,\n            chalice_stage_name,\n            config.project_dir,\n        )\n        return deployed_values\n\n    def _validate_config(self, config):\n        # type: (Config) -> None\n        try:\n            validate_configuration(config)\n        except ValueError as e:\n            raise ChaliceDeploymentError(e)\n\n\nclass BaseDeployStep(object):\n    def handle(self, config, resource):\n        # type: (Config, models.Model) -> None\n        name = 'handle_%s' % resource.__class__.__name__.lower()\n        handler = getattr(self, name, None)\n        if handler is not None:\n            handler(config, resource)\n\n\nclass InjectDefaults(BaseDeployStep):\n    def __init__(self, lambda_timeout=DEFAULT_LAMBDA_TIMEOUT,\n                 lambda_memory_size=DEFAULT_LAMBDA_MEMORY_SIZE,\n                 tls_version=DEFAULT_TLS_VERSION):\n        # type: (int, int, str) -> None\n        self._lambda_timeout = lambda_timeout\n        self._lambda_memory_size = lambda_memory_size\n        self._tls_version = DEFAULT_TLS_VERSION\n\n    def handle_lambdafunction(self, config, resource):\n        # type: (Config, models.LambdaFunction) -> None\n        if resource.timeout is None:\n            resource.timeout = self._lambda_timeout\n        if resource.memory_size is None:\n            resource.memory_size = self._lambda_memory_size\n\n    def handle_domainname(self, config, resource):\n        # type: (Config, models.DomainName) -> None\n        if resource.tls_version is None:\n            resource.tls_version = models.TLSVersion.create(\n                DEFAULT_TLS_VERSION)\n\n\nclass DeploymentPackager(BaseDeployStep):\n    def __init__(self, packager):\n        # type: (LambdaDeploymentPackager) -> None\n        self._packager = packager\n\n    def handle_deploymentpackage(self, config, resource):\n        # type: (Config, models.DeploymentPackage) -> None\n        if isinstance(resource.filename, models.Placeholder):\n            zip_filename = self._packager.create_deployment_package(\n                config.project_dir, config.lambda_python_version)\n            resource.filename = zip_filename\n\n\nclass ManagedLayerDeploymentPackager(BaseDeployStep):\n    # If we're creating a layer for non-app code there's two different\n    # packagers we need.  One for the Lambda functions (app code) and\n    # one for the Lambda layer (requirements.txt + vendor).\n    def __init__(self,\n                 lambda_packager,  # type: BaseLambdaDeploymentPackager\n                 layer_packager,   # type: BaseLambdaDeploymentPackager\n                 ):\n        # type: (...) -> None\n        self._lambda_packager = lambda_packager\n        self._layer_packager = layer_packager\n\n    def handle_lambdafunction(self, config, resource):\n        # type: (Config, models.LambdaFunction) -> None\n        if isinstance(resource.deployment_package.filename,\n                      models.Placeholder):\n            zip_filename = self._lambda_packager.create_deployment_package(\n                config.project_dir, config.lambda_python_version\n            )\n            resource.deployment_package.filename = zip_filename\n        if resource.managed_layer is not None and \\\n                resource.managed_layer.is_empty:\n            # Lambda doesn't allow us to create an empty layer so if we've\n            # tried to create the deployment package and determined it's empty\n            # we should remove the managed layer from the model entirely so\n            # downstream consumers don't have to worry about it.\n            resource.managed_layer = None\n\n    def handle_lambdalayer(self, config, resource):\n        # type: (Config, models.LambdaLayer) -> None\n        if isinstance(resource.deployment_package.filename,\n                      models.Placeholder):\n            try:\n                zip_filename = self._layer_packager.create_deployment_package(\n                    config.project_dir, config.lambda_python_version\n                )\n                resource.deployment_package.filename = zip_filename\n            except EmptyPackageError:\n                # An empty package is not valid in Lambda.  There's\n                # no point in trying to represent it in the model\n                # because nothing downstream (planner, SAM/tf) can\n                # consume as a useful entity.\n                resource.is_empty = True\n\n\nclass SwaggerBuilder(BaseDeployStep):\n    def __init__(self, swagger_generator):\n        # type: (SwaggerGenerator) -> None\n        self._swagger_generator = swagger_generator\n\n    def handle_restapi(self, config, resource):\n        # type: (Config, models.RestAPI) -> None\n        swagger_doc = self._swagger_generator.generate_swagger(\n            config.chalice_app, resource)\n        resource.swagger_doc = swagger_doc\n\n\nclass LambdaEventSourcePolicyInjector(BaseDeployStep):\n    def __init__(self):\n        # type: () -> None\n        self._sqs_policy_injected = False\n        self._kinesis_policy_injected = False\n        self._ddb_policy_injected = False\n\n    def handle_sqseventsource(self, config, resource):\n        # type: (Config, models.SQSEventSource) -> None\n        # The sqs integration works by polling for\n        # available records so the lambda function needs\n        # permission to call sqs.\n        role = resource.lambda_function.role\n        if not self._sqs_policy_injected and \\\n                self._needs_policy_injected(role):\n            # mypy can't follow the type narrowing from\n            # _needs_policy_injected so we're working around\n            # that by explicitly casting the role.\n            role = cast(models.ManagedIAMRole, role)\n            document = cast(Dict[str, Any], role.policy.document)\n            self._inject_trigger_policy(document,\n                                        SQS_EVENT_SOURCE_POLICY.copy())\n            self._sqs_policy_injected = True\n\n    def handle_kinesiseventsource(self, config, resource):\n        # type: (Config, models.KinesisEventSource) -> None\n        role = resource.lambda_function.role\n        if not self._kinesis_policy_injected and \\\n                self._needs_policy_injected(role):\n            # See commen in handle_kinesiseventsource about this cast.\n            role = cast(models.ManagedIAMRole, role)\n            document = cast(Dict[str, Any], role.policy.document)\n            self._inject_trigger_policy(document,\n                                        KINESIS_EVENT_SOURCE_POLICY.copy())\n            self._kinesis_policy_injected = True\n\n    def handle_dynamodbeventsource(self, config, resource):\n        # type: (Config, models.KinesisEventSource) -> None\n        role = resource.lambda_function.role\n        if not self._ddb_policy_injected and \\\n                self._needs_policy_injected(role):\n            # See commen in handle_kinesiseventsource about this cast.\n            role = cast(models.ManagedIAMRole, role)\n            document = cast(Dict[str, Any], role.policy.document)\n            self._inject_trigger_policy(document,\n                                        DDB_EVENT_SOURCE_POLICY.copy())\n            self._ddb_policy_injected = True\n\n    def _needs_policy_injected(self, role):\n        # type: (models.IAMRole) -> bool\n        return (\n            isinstance(role, models.ManagedIAMRole) and\n            isinstance(role.policy, models.AutoGenIAMPolicy) and\n            not isinstance(role.policy.document, models.Placeholder)\n        )\n\n    def _inject_trigger_policy(self, document, policy):\n        # type: (Dict[str, Any], Dict[str, Any]) -> None\n        document['Statement'].append(policy)\n\n\nclass WebsocketPolicyInjector(BaseDeployStep):\n    def __init__(self):\n        # type: () -> None\n        self._policy_injected = False\n\n    def handle_websocketapi(self, config, resource):\n        # type: (Config, models.WebsocketAPI) -> None\n        self._inject_into_function(config, resource.connect_function)\n        self._inject_into_function(config, resource.message_function)\n        self._inject_into_function(config, resource.disconnect_function)\n\n    def _inject_into_function(self, config, lambda_function):\n        # type: (Config, Optional[models.LambdaFunction]) -> None\n        if lambda_function is None:\n            return\n        role = lambda_function.role\n        if role is None:\n            return\n        if (not self._policy_injected and\n            isinstance(role, models.ManagedIAMRole) and\n            isinstance(role.policy, models.AutoGenIAMPolicy) and\n            not isinstance(role.policy.document,\n                           models.Placeholder)):\n            self._inject_policy(\n                role.policy.document,\n                POST_TO_WEBSOCKET_CONNECTION_POLICY.copy())\n        self._policy_injected = True\n\n    def _inject_policy(self, document, policy):\n        # type: (Dict[str, Any], Dict[str, Any]) -> None\n        document['Statement'].append(policy)\n\n\nclass PolicyGenerator(BaseDeployStep):\n    def __init__(self, policy_gen, osutils):\n        # type: (AppPolicyGenerator, OSUtils) -> None\n        self._policy_gen = policy_gen\n        self._osutils = osutils\n\n    def _read_document_from_file(self, filename):\n        # type: (PolicyGenerator, str) -> Dict[str, Any]\n        try:\n            return json.loads(self._osutils.get_file_contents(filename))\n        except IOError as e:\n            raise RuntimeError(\"Unable to load IAM policy file %s: %s\"\n                               % (filename, e))\n\n    def handle_filebasediampolicy(self, config, resource):\n        # type: (Config, models.FileBasedIAMPolicy) -> None\n        resource.document = self._read_document_from_file(resource.filename)\n\n    def handle_restapi(self, config, resource):\n        # type: (Config, models.RestAPI) -> None\n        if resource.policy and isinstance(\n                resource.policy, models.FileBasedIAMPolicy):\n            resource.policy.document = self._read_document_from_file(\n                resource.policy.filename)\n\n    def handle_autogeniampolicy(self, config, resource):\n        # type: (Config, models.AutoGenIAMPolicy) -> None\n        if isinstance(resource.document, models.Placeholder):\n            policy = self._policy_gen.generate_policy(config)\n            if models.RoleTraits.VPC_NEEDED in resource.traits:\n                policy['Statement'].append(VPC_ATTACH_POLICY)\n            resource.document = policy\n\n\nclass BuildStage(object):\n    def __init__(self, steps):\n        # type: (List[BaseDeployStep]) -> None\n        self._steps = steps\n\n    def execute(self, config, resources):\n        # type: (Config, List[models.Model]) -> None\n        for resource in resources:\n            for step in self._steps:\n                step.handle(config, resource)\n\n\nclass ResultsRecorder(object):\n    def __init__(self, osutils):\n        # type: (OSUtils) -> None\n        self._osutils = osutils\n\n    def record_results(self, results, chalice_stage_name, project_dir):\n        # type: (Any, str, str) -> None\n        deployed_dir = self._osutils.joinpath(\n            project_dir, '.chalice', 'deployed')\n        deployed_filename = self._osutils.joinpath(\n            deployed_dir, '%s.json' % chalice_stage_name)\n        if not self._osutils.directory_exists(deployed_dir):\n            self._osutils.makedirs(deployed_dir)\n        serialized = serialize_to_json(results)\n        self._osutils.set_file_contents(\n            filename=deployed_filename,\n            contents=serialized,\n            binary=False\n        )\n\n\nclass NoopResultsRecorder(ResultsRecorder):\n    def record_results(self, results, chalice_stage_name, project_dir):\n        # type: (Any, str, str) -> None\n        return None\n\n\nclass DeploymentReporter(object):\n    # We want the API URLs to be displayed last.\n    _SORT_ORDER = {\n        'rest_api': 100,\n        'websocket_api': 100,\n        'domain_name': 100\n    }\n    # The default is chosen to sort before the rest_api\n    _DEFAULT_ORDERING = 50\n\n    def __init__(self, ui):\n        # type: (UI) -> None\n        self._ui = ui\n\n    def generate_report(self, deployed_values):\n        # type: (Dict[str, Any]) -> str\n        report = [\n            'Resources deployed:',\n        ]\n        ordered = sorted(\n            deployed_values['resources'],\n            key=lambda x: self._SORT_ORDER.get(x['resource_type'],\n                                               self._DEFAULT_ORDERING))\n        for resource in ordered:\n            getattr(self, '_report_%s' % resource['resource_type'],\n                    self._default_report)(resource, report)\n        report.append('')\n        return '\\n'.join(report)\n\n    def _report_rest_api(self, resource, report):\n        # type: (Dict[str, Any], List[str]) -> None\n        report.append('  - Rest API URL: %s' % resource['rest_api_url'])\n\n    def _report_websocket_api(self, resource, report):\n        # type: (Dict[str, Any], List[str]) -> None\n        report.append(\n            '  - Websocket API URL: %s' % resource['websocket_api_url'])\n\n    def _report_domain_name(self, resource, report):\n        # type: (Dict[str, Any], List[str]) -> None\n        report.append(\n            '  - Custom domain name:\\n'\n            '      HostedZoneId: %s\\n'\n            '      AliasDomainName: %s' % (\n                resource['hosted_zone_id'], resource['alias_domain_name']\n            )\n        )\n\n    def _report_lambda_function(self, resource, report):\n        # type: (Dict[str, Any], List[str]) -> None\n        report.append('  - Lambda ARN: %s' % resource['lambda_arn'])\n\n    def _report_lambda_layer(self, resource, report):\n        # type: (Dict[str, Any], List[str]) -> None\n        report.append('  - Lambda Layer ARN: %s' % (\n            resource['layer_version_arn']))\n\n    def _default_report(self, resource, report):\n        # type: (Dict[str, Any], List[str]) -> None\n        # The default behavior is to not report a resource.  This\n        # cuts down on the output verbosity.\n        pass\n\n    def display_report(self, deployed_values):\n        # type: (Dict[str, Any]) -> None\n        report = self.generate_report(deployed_values)\n        self._ui.write(report)\n"
  },
  {
    "path": "chalice/deploy/executor.py",
    "content": "import re\nimport pprint\nfrom dataclasses import asdict, is_dataclass\n\nimport jmespath\nfrom typing import Dict, List, Any  # noqa\n\nfrom chalice.deploy import models # noqa\nfrom chalice.awsclient import TypedAWSClient  # noqa\nfrom chalice.utils import UI  # noqa\n\n\nclass BaseExecutor(object):\n    def __init__(self, client, ui):\n        # type: (TypedAWSClient, UI) -> None\n        self._client = client\n        self._ui = ui\n        self.resource_values = []  # type: List[Dict[str, Any]]\n\n    def execute(self, plan):\n        # type: (models.Plan) -> None\n        pass\n\n\nclass Executor(BaseExecutor):\n    def __init__(self, client, ui):\n        # type: (TypedAWSClient, UI) -> None\n        super(Executor, self).__init__(client, ui)\n        # A mapping of variables that's populated as API calls\n        # are made.  These can be used in subsequent API calls.\n        self.variables = {}  # type: Dict[str, Any]\n        self._resource_value_index = {}  # type: Dict[str, Any]\n        self._variable_resolver = VariableResolver()\n\n    def execute(self, plan):\n        # type: (models.Plan) -> None\n        messages = plan.messages\n        for instruction in plan.instructions:\n            message = messages.get(id(instruction))\n            if message is not None:\n                self._ui.write(message)\n            getattr(self, '_do_%s' % instruction.__class__.__name__.lower(),\n                    self._default_handler)(instruction)\n\n    def _default_handler(self, instruction):\n        # type: (models.Instruction) -> None\n        raise RuntimeError(\"Deployment executor encountered an \"\n                           \"unknown instruction: %s\"\n                           % instruction.__class__.__name__)\n\n    def _do_apicall(self, instruction):\n        # type: (models.APICall) -> None\n        final_kwargs = self._resolve_variables(instruction)\n        method = getattr(self._client, instruction.method_name)\n        result = method(**final_kwargs)\n        if instruction.output_var is not None:\n            self.variables[instruction.output_var] = result\n\n    def _do_copyvariable(self, instruction):\n        # type: (models.CopyVariable) -> None\n        to_var = instruction.to_var\n        from_var = instruction.from_var\n        self.variables[to_var] = self.variables[from_var]\n\n    def _do_storevalue(self, instruction):\n        # type: (models.StoreValue) -> None\n        result = self._variable_resolver.resolve_variables(\n            instruction.value, self.variables)\n        self.variables[instruction.name] = result\n\n    def _do_storemultiplevalue(self, instruction):\n        # type: (models.StoreValue) -> None\n        result = self._variable_resolver.resolve_variables(\n            instruction.value, self.variables)\n        data = self.variables.get(instruction.name)\n        if data and isinstance(data, list):\n            self.variables[instruction.name].extend(result)\n        else:\n            self.variables[instruction.name] = result\n\n    def _do_recordresourcevariable(self, instruction):\n        # type: (models.RecordResourceVariable) -> None\n        payload = {\n            'name': instruction.resource_name,\n            'resource_type': instruction.resource_type,\n            instruction.name: self.variables[instruction.variable_name],\n        }\n        self._add_to_deployed_values(payload)\n\n    def _do_recordresourcevalue(self, instruction):\n        # type: (models.RecordResourceValue) -> None\n        payload = {\n            'name': instruction.resource_name,\n            'resource_type': instruction.resource_type,\n            instruction.name: instruction.value,\n        }\n        self._add_to_deployed_values(payload)\n\n    def _add_to_deployed_values(self, payload):\n        # type: (Dict[str, str]) -> None\n        key = payload['name']\n        if key not in self._resource_value_index:\n            self._resource_value_index[key] = payload\n            self.resource_values.append(payload)\n        else:\n            # If the key already exists, we merge the new payload\n            # with the existing payload.\n            self._resource_value_index[key].update(payload)\n\n    def _do_jpsearch(self, instruction):\n        # type: (models.JPSearch) -> None\n        v = self.variables[instruction.input_var]\n        result = jmespath.search(instruction.expression, v)\n        self.variables[instruction.output_var] = result\n\n    def _do_builtinfunction(self, instruction):\n        # type: (models.BuiltinFunction) -> None\n        # Split this out to a separate class of built in functions\n        # once we add more functions.\n        if instruction.function_name == 'parse_arn':\n            resolved_args = self._variable_resolver.resolve_variables(\n                instruction.args, self.variables)\n            value = resolved_args[0]\n            parts = value.split(':')\n            result = {\n                'partition': parts[1],\n                'service': parts[2],\n                'region': parts[3],\n                'account_id': parts[4],\n                'dns_suffix': self._client.endpoint_dns_suffix(parts[2],\n                                                               parts[3])\n            }\n            self.variables[instruction.output_var] = result\n        elif instruction.function_name == 'interrogate_profile':\n            region = self._client.region_name\n            result = {\n                'partition': self._client.partition_name,\n                'region': region,\n                'dns_suffix': self._client.endpoint_dns_suffix('apigateway',\n                                                               region)\n            }\n            self.variables[instruction.output_var] = result\n        elif instruction.function_name == 'service_principal':\n            resolved_args = self._variable_resolver.resolve_variables(\n                instruction.args, self.variables)\n            service_name = resolved_args[0]\n            region_name = self._client.region_name\n            dns_suffix = self._client.endpoint_dns_suffix(service_name,\n                                                          region_name)\n            result = {\n                'principal': self._client.service_principal(service_name,\n                                                            region_name,\n                                                            dns_suffix)\n            }\n            self.variables[instruction.output_var] = result\n        else:\n            raise ValueError(\"Unknown builtin function: %s\"\n                             % instruction.function_name)\n\n    def _resolve_variables(self, api_call):\n        # type: (models.APICall) -> Dict[str, Any]\n        try:\n            return self._variable_resolver.resolve_variables(\n                api_call.params, self.variables)\n        except UnresolvedValueError as e:\n            e.method_name = api_call.method_name\n            raise\n\n\nclass VariableResolver(object):\n    def resolve_variables(self, value, variables):\n        # type: (Any, Dict[str, str]) -> Any\n\n        value_type = type(value).__name__.lower()\n        handler_name = '_resolve_%s' % value_type\n        handler = getattr(self, handler_name, None)\n        if handler:\n            return handler(value, variables)\n        else:\n            return value\n\n    def _resolve_variable(self, value, variables):\n        # type: (Any, Dict[str, str]) -> Any\n        return variables[value.name]\n\n    def _resolve_stringformat(self, value, variables):\n        # type: (Any, Dict[str, str]) -> Any\n        v = {k: variables[k] for k in value.variables}\n        return value.template.format(**v)\n\n    def _resolve_keydatavariable(self, value, variables):\n        # type: (Any, Dict[str, str]) -> Any\n        return variables[value.name][value.key]\n\n    def _resolve_placeholder(self, value, variables):\n        # type: (Any, Dict[str, str]) -> Any\n        # The key and method_name values are added\n        # as the exception propagates up the stack.\n        raise UnresolvedValueError('', value, '')\n\n    def _resolve_dict(self, value, variables):\n        # type: (Any, Dict[str, str]) -> Any\n        final = {}\n        for k, v in value.items():\n            try:\n                final[k] = self.resolve_variables(v, variables)\n            except UnresolvedValueError as e:\n                e.key = k\n                raise\n        return final\n\n    def _resolve_list(self, value, variables):\n        # type: (Any, Dict[str, str]) -> Any\n        final_list = []\n        for v in value:\n            final_list.append(self.resolve_variables(v, variables))\n        return final_list\n\n\n# This class is used for the ``chalice dev plan`` command.\n# The dev commands don't have any backwards compatibility guarantees\n# so we can alter this output as needed.\nclass DisplayOnlyExecutor(BaseExecutor):\n    # Max length of bytes object before we truncate with '<bytes>'\n    _MAX_BYTE_LENGTH = 30\n    _LINE_VERTICAL = '\\u2502'\n\n    def execute(self, plan):\n        # type: (models.Plan) -> None\n        spillover_values = {}  # type: Dict[str, Any]\n        self._ui.write(\"Plan\\n\")\n        self._ui.write(\"====\\n\\n\")\n        for instruction in plan.instructions:\n            getattr(self, '_do_%s' % instruction.__class__.__name__.lower(),\n                    self._default_handler)(instruction, spillover_values)\n        self._write_spillover(spillover_values)\n\n    def _write_spillover(self, spillover_values):\n        # type: (Dict[str, Any]) -> None\n        if not spillover_values:\n            return\n        self._ui.write(\"Variable Pool\\n\")\n        self._ui.write(\"=============\\n\\n\")\n        for key, value in spillover_values.items():\n            self._ui.write('%s:\\n' % key)\n            self._ui.write(pprint.pformat(value) + '\\n\\n')\n\n    def _default_handler(self, instruction, spillover_values):\n        # type: (models.Instruction, Dict[str, Any]) -> None\n        instruction_name = self._upper_snake_case(\n            instruction.__class__.__name__)\n        # Need this to make typing happy . We're certain that we're always\n        # dealing with a dataclass, but the base type `Instruction` has\n        # no dataclass pieces.  There's probably a better way to represent\n        # this type hierarchy.\n        assert is_dataclass(instruction) and not isinstance(instruction, type)\n        for key, value in asdict(instruction).items():\n            if isinstance(value, dict):\n                value = self._format_dict(value, spillover_values)\n            line = ('%-30s %s%20s %-10s' % (\n                instruction_name, self._LINE_VERTICAL, '%s:' % key, value)\n            )\n            self._ui.write(line + '\\n')\n            instruction_name = ''\n        self._ui.write('\\n')\n\n    def _format_dict(self, dict_value, spillover_values):\n        # type: (Dict[str, Any], Dict[str, Any]) -> str\n        lines = ['']\n        for key, value in dict_value.items():\n            if not value:\n                continue\n            if isinstance(value, bytes) and len(value) > self._MAX_BYTE_LENGTH:\n                value = '<bytes>'\n            if isinstance(value, (dict, list)):\n                # We need a unique name to use so we just use a simple\n                # incrementing counter with the name prefixed.\n                spillover_name = '${%s_%s}' % (\n                    key.upper(), len(spillover_values))\n                spillover_values[spillover_name] = value\n                value = spillover_name\n            line = '%-31s%s%-15s%s%20s %-10s' % (\n                ' ', self._LINE_VERTICAL, ' ', self._LINE_VERTICAL,\n                '%s:' % key, value\n            )\n            lines.append(line)\n        return '\\n'.join(lines)\n\n    def _upper_snake_case(self, v):\n        # type: (str) -> str\n        first_cap_regex = re.compile('(.)([A-Z][a-z]+)')\n        end_cap_regex = re.compile('([a-z0-9])([A-Z])')\n        first = first_cap_regex.sub(r'\\1_\\2', v)\n        transformed = end_cap_regex.sub(r'\\1_\\2', first).upper()\n        return transformed\n\n\nclass UnresolvedValueError(Exception):\n    MSG = (\n        \"The API parameter '%s' has an unresolved value \"\n        \"of %s in the method call: %s\"\n    )\n\n    def __init__(self, key, value, method_name):\n        # type: (str, models.Placeholder, str) -> None\n        super(UnresolvedValueError, self).__init__()\n        self.key = key\n        self.value = value\n        self.method_name = method_name\n\n    def __str__(self):\n        # type: () -> str\n        return self.MSG % (self.key, self.value, self.method_name)\n"
  },
  {
    "path": "chalice/deploy/models.py",
    "content": "# pylint: disable=line-too-long\nfrom __future__ import annotations\nfrom dataclasses import dataclass, field\nimport enum\nfrom typing import List, Dict, Optional as Opt, Any, TypeVar, Union, Set  # noqa\nfrom typing import cast\n\n\nclass Placeholder(enum.Enum):\n    BUILD_STAGE = 'build_stage'\n\n\nclass Instruction(object):\n    pass\n\n\nclass RoleTraits(enum.Enum):\n    VPC_NEEDED = 'vpc_needed'\n\n\nclass APIType(enum.Enum):\n    WEBSOCKET = 'WEBSOCKET'\n    HTTP = 'HTTP'\n\n\nclass TLSVersion(enum.Enum):\n    TLS_1_0 = 'TLS_1_0'\n    TLS_1_1 = 'TLS_1_1'\n    TLS_1_2 = 'TLS_1_2'\n\n    @classmethod\n    def create(cls, str_version: str) -> Opt[TLSVersion]:\n        for version in cls:\n            if version.value == str_version:\n                return version\n        return None\n\n\nT = TypeVar('T')\nDV = Union[Placeholder, T]\nStrMap = Dict[str, str]\n\n\n@dataclass\nclass Plan:\n    instructions: List[Instruction] = field(default_factory=list)\n    messages: Dict[int, str] = field(default_factory=dict)\n\n\n@dataclass(frozen=True)\nclass APICall(Instruction):\n    method_name: str\n    params: Dict[str, Any]\n    output_var: Opt[str] = None\n\n\n@dataclass(frozen=True)\nclass StoreValue(Instruction):\n    name: str\n    value: Any\n\n\n@dataclass(frozen=True)\nclass StoreMultipleValue(Instruction):\n    name: str\n    value: List[Any] = field(default_factory=list)\n\n\n@dataclass(frozen=True)\nclass CopyVariable(Instruction):\n    from_var: str\n    to_var: str\n\n\n@dataclass(frozen=True)\nclass CopyVariableFromDict(Instruction):\n    from_var: str\n    key: str\n    to_var: str\n\n\n@dataclass(frozen=True)\nclass RecordResource(Instruction):\n    resource_type: str\n    resource_name: str\n    name: str\n\n\n@dataclass(frozen=True)\nclass RecordResourceVariable(RecordResource):\n    variable_name: str\n\n\n@dataclass(frozen=True)\nclass RecordResourceValue(RecordResource):\n    value: Any\n\n\n@dataclass(frozen=True)\nclass JPSearch(Instruction):\n    expression: Any\n    input_var: Any\n    output_var: Any\n\n\n@dataclass(frozen=True)\nclass BuiltinFunction(Instruction):\n    function_name: str\n    args: List[Any]\n    output_var: str\n\n\n@dataclass\nclass Model(object):\n    def dependencies(self) -> List[Model]:\n        return []\n\n\n@dataclass\nclass ManagedModel(Model):\n    resource_name: str\n    # Subclasses must fill in this attribute.\n    resource_type = ''\n\n\n@dataclass\nclass Application(Model):\n    stage: str\n    resources: List[Model]\n\n    def dependencies(self) -> List[Model]:\n        return self.resources\n\n\n@dataclass\nclass DeploymentPackage(Model):\n    filename: DV[str]\n\n\n@dataclass\nclass IAMPolicy(Model):\n    document: DV[Dict[str, Any]]\n\n\n@dataclass\nclass FileBasedIAMPolicy(IAMPolicy):\n    filename: str\n\n\n@dataclass\nclass AutoGenIAMPolicy(IAMPolicy):\n    traits: Set[RoleTraits] = field(default_factory=set)\n\n\n@dataclass\nclass IAMRole(Model):\n    pass\n\n\n@dataclass\nclass PreCreatedIAMRole(IAMRole):\n    role_arn: str\n\n\n@dataclass\nclass ManagedIAMRole(IAMRole, ManagedModel):\n    resource_type = 'iam_role'\n    role_name: str\n    trust_policy: Dict[str, Any]\n    policy: IAMPolicy\n\n    def dependencies(self) -> List[Model]:\n        return [self.policy]\n\n\n@dataclass\nclass LambdaLayer(ManagedModel):\n    resource_type = 'lambda_layer'\n    layer_name: str\n    runtime: str\n    deployment_package: DeploymentPackage\n    is_empty: bool = False\n\n    def dependencies(self) -> List[Model]:\n        return [self.deployment_package]\n\n\n@dataclass\nclass LambdaFunction(ManagedModel):\n    resource_type = 'lambda_function'\n    function_name: str\n    deployment_package: DeploymentPackage\n    environment_variables: StrMap\n    xray: bool\n    runtime: str\n    handler: str\n    tags: StrMap\n    timeout: int\n    memory_size: int\n    role: IAMRole\n    security_group_ids: List[str]\n    subnet_ids: List[str]\n    reserved_concurrency: int\n    # These are customer created layers.\n    layers: List[str]\n    managed_layer: Opt[LambdaLayer] = None\n    log_group: Opt[LogGroup] = None\n\n    def dependencies(self) -> List[Model]:\n        resources: List[Model] = []\n        if self.managed_layer is not None:\n            resources.append(self.managed_layer)\n        if self.log_group is not None:\n            resources.append(self.log_group)\n        resources.extend([self.role, self.deployment_package])\n        return resources\n\n\n@dataclass\nclass FunctionEventSubscriber(ManagedModel):\n    lambda_function: LambdaFunction\n\n    def dependencies(self) -> List[Model]:\n        return [self.lambda_function]\n\n\n@dataclass\nclass CloudWatchEventBase(FunctionEventSubscriber):\n    rule_name: str\n\n\n@dataclass\nclass CloudWatchEvent(CloudWatchEventBase):\n    resource_type = 'cloudwatch_event'\n    event_pattern: str\n\n\n@dataclass\nclass ScheduledEvent(CloudWatchEventBase):\n    resource_type = 'scheduled_event'\n    schedule_expression: str\n    rule_description: Opt[str] = None\n\n\n@dataclass\nclass LogGroup(ManagedModel):\n    resource_type = 'log_group'\n    log_group_name: str\n    retention_in_days: int\n\n\n@dataclass\nclass APIMapping(ManagedModel):\n    resource_type = 'api_mapping'\n    mount_path: str\n    api_gateway_stage: str\n\n\n@dataclass\nclass DomainName(ManagedModel):\n    resource_type = 'domain_name'\n    domain_name: str\n    protocol: APIType\n    api_mapping: APIMapping\n    certificate_arn: str\n    tags: Opt[Dict[str, Any]] = None\n    tls_version: Opt[TLSVersion] = None\n\n    def dependencies(self) -> List[Model]:\n        return [self.api_mapping]\n\n\n@dataclass\nclass RestAPI(ManagedModel):\n    resource_type = 'rest_api'\n    swagger_doc: DV[Dict[str, Any]]\n    minimum_compression: str\n    api_gateway_stage: str\n    endpoint_type: str\n    lambda_function: LambdaFunction\n    xray: bool = False\n    policy: Opt[IAMPolicy] = None\n    authorizers: List[LambdaFunction] = field(default_factory=list)\n    domain_name: Opt[DomainName] = None\n    vpce_ids: Opt[List[str]] = None\n\n    def dependencies(self) -> List[Model]:\n        resources: List[Model] = []\n        resources.extend([self.lambda_function] + self.authorizers)\n        if self.domain_name is not None:\n            resources.append(self.domain_name)\n        return cast(List[Model], resources)\n\n\n@dataclass\nclass WebsocketAPI(ManagedModel):\n    resource_type = 'websocket_api'\n    name: str\n    api_gateway_stage: str\n    routes: List[str]\n    connect_function: Opt[LambdaFunction]\n    message_function: Opt[LambdaFunction]\n    disconnect_function: Opt[LambdaFunction]\n    domain_name: Opt[DomainName] = None\n\n    def dependencies(self) -> List[Model]:\n        resources: List[Model] = []\n        if self.domain_name is not None:\n            resources.append(self.domain_name)\n        if self.connect_function is not None:\n            resources.append(self.connect_function)\n        if self.message_function is not None:\n            resources.append(self.message_function)\n        if self.disconnect_function is not None:\n            resources.append(self.disconnect_function)\n        return resources\n\n\n@dataclass\nclass S3BucketNotification(FunctionEventSubscriber):\n    resource_type = 's3_event'\n    bucket: str\n    events: List[str]\n    prefix: Opt[str]\n    suffix: Opt[str]\n\n\n@dataclass\nclass SNSLambdaSubscription(FunctionEventSubscriber):\n    resource_type = 'sns_event'\n    topic: str\n\n\n@dataclass\nclass QueueARN(object):\n    arn: str\n\n    @property\n    def queue_name(self) -> str:\n        # Pylint 2.x validates this correctly, but for py27, we have to\n        # use Pylint 1.x which doesn't support dataclass.\n        return self.arn.rpartition(':')[2]  # pylint: disable=no-member\n\n\n@dataclass\nclass SQSEventSource(FunctionEventSubscriber):\n    resource_type = 'sqs_event'\n    queue: Union[str, QueueARN]\n    batch_size: int\n    maximum_batching_window_in_seconds: int\n    maximum_concurrency: Opt[int] = None\n\n\n@dataclass\nclass KinesisEventSource(FunctionEventSubscriber):\n    resource_type = 'kinesis_event'\n    stream: str\n    batch_size: int\n    starting_position: str\n    maximum_batching_window_in_seconds: int\n\n\n@dataclass\nclass DynamoDBEventSource(FunctionEventSubscriber):\n    resource_type = 'dynamodb_event'\n    stream_arn: str\n    batch_size: int\n    starting_position: str\n    maximum_batching_window_in_seconds: int\n"
  },
  {
    "path": "chalice/deploy/packager.py",
    "content": "# pylint: disable=too-many-lines\nfrom __future__ import annotations\nimport sys\nimport hashlib\nimport inspect\nimport re\nimport subprocess\nimport logging\nimport functools\nfrom email.parser import FeedParser\nfrom email.message import Message  # noqa\nfrom zipfile import ZipFile  # noqa\n\nfrom typing import Any, Set, List, Optional, Tuple, Iterable, Callable  # noqa\nfrom typing import Iterator  # noqa\nfrom typing import Dict, MutableMapping, cast  # noqa\nfrom chalice.compat import pip_import_string\nfrom chalice.compat import pip_no_compile_c_env_vars\nfrom chalice.compat import pip_no_compile_c_shim\nfrom chalice.utils import OSUtils\nfrom chalice.utils import UI  # noqa\nfrom chalice.constants import MISSING_DEPENDENCIES_TEMPLATE\n\nimport chalice\nfrom chalice import app\n\n\nStrMap = Dict[str, Any]\nOptStrMap = Optional[StrMap]\nEnvVars = MutableMapping\nOptStr = Optional[str]\nOptBytes = Optional[bytes]\n\nlogger = logging.getLogger(__name__)\n\n\nclass InvalidSourceDistributionNameError(Exception):\n    pass\n\n\nclass MissingDependencyError(Exception):\n    \"\"\"Raised when some dependencies could not be packaged for any reason.\"\"\"\n\n    def __init__(self, missing: Set[Package]) -> None:\n        self.missing = missing\n\n\nclass NoSuchPackageError(Exception):\n    \"\"\"Raised when a package name or version could not be found.\"\"\"\n\n    def __init__(self, package_name: str) -> None:\n        super(NoSuchPackageError, self).__init__(\n            'Could not satisfy the requirement: %s' % package_name\n        )\n\n\nclass PackageDownloadError(Exception):\n    \"\"\"Generic networking error during a package download.\"\"\"\n\n\nclass EmptyPackageError(Exception):\n    \"\"\"A deployment package cannot be an empty zip file.\"\"\"\n\n\nclass UnsupportedPackageError(Exception):\n    \"\"\"Unable to parse package metadata.\"\"\"\n\n    def __init__(self, package_name: str) -> None:\n        super(UnsupportedPackageError, self).__init__(\n            'Unable to retrieve name/version for package: %s' % package_name\n        )\n\n\nclass BaseLambdaDeploymentPackager(object):\n    _CHALICE_LIB_DIR = 'chalicelib'\n    _VENDOR_DIR = 'vendor'\n\n    _RUNTIME_TO_ABI = {\n        'python3.9': 'cp39',\n        'python3.10': 'cp310',\n        'python3.11': 'cp311',\n        'python3.12': 'cp312',\n        'python3.13': 'cp313',\n    }\n\n    def __init__(\n        self, osutils: OSUtils, dependency_builder: DependencyBuilder, ui: UI\n    ) -> None:\n        self._osutils = osutils\n        self._dependency_builder = dependency_builder\n        self._ui = ui\n\n    def create_deployment_package(\n        self, project_dir: str, python_version: str\n    ) -> str:\n        raise NotImplementedError(\"create_deployment_package\")\n\n    def _get_requirements_filename(self, project_dir: str) -> str:\n        # Gets the path to a requirements.txt file out of a project dir path\n        return self._osutils.joinpath(project_dir, 'requirements.txt')\n\n    def _add_vendor_files(\n        self, zipped: ZipFile, dirname: str, prefix: str = ''\n    ) -> None:\n        if not self._osutils.directory_exists(dirname):\n            return\n        prefix_len = len(dirname) + 1\n        for root, _, filenames in self._osutils.walk(\n            dirname, followlinks=True\n        ):\n            for filename in filenames:\n                full_path = self._osutils.joinpath(root, filename)\n                zip_path = full_path[prefix_len:]\n                if prefix:\n                    zip_path = self._osutils.joinpath(prefix, zip_path)\n                zipped.write(full_path, zip_path)\n\n    def deployment_package_filename(\n        self, project_dir: str, python_version: str\n    ) -> str:\n        # Computes the name of the deployment package zipfile\n        # based on a hash of the requirements file.\n        # This is done so that we only \"pip install -r requirements.txt\"\n        # when we know there's new dependencies we need to install.\n        # The python version these depedencies were downloaded for is appended\n        # to the end of the filename since the the dependencies may not change\n        # but if the python version changes then the dependencies need to be\n        # re-downloaded since they will not be compatible.\n        return self._deployment_package_filename(project_dir, python_version)\n\n    def _deployment_package_filename(\n        self, project_dir: str, python_version: str, prefix: str = ''\n    ) -> str:\n        requirements_filename = self._get_requirements_filename(project_dir)\n        hash_contents = self._hash_project_dir(\n            requirements_filename,\n            self._osutils.joinpath(project_dir, self._VENDOR_DIR),\n            project_dir,\n        )\n        filename = '%s%s-%s.zip' % (prefix, hash_contents, python_version)\n        deployment_package_filename = self._osutils.joinpath(\n            project_dir, '.chalice', 'deployments', filename\n        )\n        return deployment_package_filename\n\n    def _add_py_deps(\n        self, zip_fileobj: ZipFile, deps_dir: str, prefix: str = ''\n    ) -> None:\n        prefix_len = len(deps_dir) + 1\n        for root, dirnames, filenames in self._osutils.walk(deps_dir):\n            if root == deps_dir and 'chalice' in dirnames:\n                # Don't include any chalice deps.  We cherry pick\n                # what we want to include in _add_app_files.\n                dirnames.remove('chalice')\n            for filename in filenames:\n                full_path = self._osutils.joinpath(root, filename)\n                zip_path = full_path[prefix_len:]\n                if prefix:\n                    zip_path = self._osutils.joinpath(prefix, zip_path)\n                zip_fileobj.write(full_path, zip_path)\n\n    def _add_app_files(self, zip_fileobj: ZipFile, project_dir: str) -> None:\n        for full_path, zip_path in self._iter_app_filenames(project_dir):\n            zip_fileobj.write(full_path, zip_path)\n\n    def _iter_app_filenames(\n        self, project_dir: str\n    ) -> Iterator[Tuple[str, str]]:\n        chalice_router = inspect.getfile(app)\n        if chalice_router.endswith('.pyc'):\n            chalice_router = chalice_router[:-1]\n        yield (chalice_router, 'chalice/app.py')\n\n        chalice_init = inspect.getfile(chalice)\n        if chalice_init.endswith('.pyc'):\n            chalice_init = chalice_init[:-1]\n        yield (chalice_init, 'chalice/__init__.py')\n        yield (self._osutils.joinpath(project_dir, 'app.py'), 'app.py')\n        yield from self._iter_chalice_lib_if_needed(project_dir)\n\n    def _hash_project_dir(\n        self, requirements_filename: str, vendor_dir: str, project_dir: str\n    ) -> str:\n        if not self._osutils.file_exists(requirements_filename):\n            contents = b''\n        else:\n            contents = cast(\n                bytes,\n                self._osutils.get_file_contents(\n                    requirements_filename, binary=True\n                ),\n            )\n        h = hashlib.md5(contents)\n        for filename, _ in self._iter_app_filenames(project_dir):\n            with self._osutils.open(filename, 'rb') as f:\n                reader = functools.partial(f.read, 1024 * 1024)\n                for chunk in iter(reader, b''):\n                    h.update(chunk)\n        if self._osutils.directory_exists(vendor_dir):\n            self._hash_vendor_dir(vendor_dir, h)\n        return h.hexdigest()\n\n    def _hash_vendor_dir(self, vendor_dir: str, md5: Any) -> None:\n        for rootdir, _, filenames in self._osutils.walk(\n            vendor_dir, followlinks=True\n        ):\n            for filename in filenames:\n                fullpath = self._osutils.joinpath(rootdir, filename)\n                with self._osutils.open(fullpath, 'rb') as f:\n                    # Not actually an issue, but pylint will complain\n                    # about the f var being used in the lambda function\n                    # is being used in a loop.  This is ok because\n                    # we're immediately using the lambda function.\n                    # Also binding it as a default argument fixes\n                    # pylint, but mypy will complain that it can't\n                    # infer the types.  So the compromise here is to\n                    # just write it the idiomatic way and have pylint\n                    # ignore this warning.\n                    reader = functools.partial(f.read, 1024 * 1024)\n                    for chunk in iter(reader, b''):\n                        md5.update(chunk)\n\n    def inject_latest_app(\n        self, deployment_package_filename: str, project_dir: str\n    ) -> None:\n        \"\"\"Inject latest version of chalice app into a zip package.\n\n        This method takes a pre-created deployment package and injects\n        in the latest chalice app code.  This is useful in the case where\n        you have no new package deps but have updated your chalice app code.\n\n        :type deployment_package_filename: str\n        :param deployment_package_filename: The zipfile of the\n            preexisting deployment package.\n\n        :type project_dir: str\n        :param project_dir: Path to chalice project dir.\n\n        \"\"\"\n        # Use the premade zip file and replace the app.py file\n        # with the latest version.  Python's zipfile does not have\n        # a way to do this efficiently so we need to create a new\n        # zip file that has all the same stuff except for the new\n        # app file.\n        self._ui.write(\"Regen deployment package.\\n\")\n        tmpzip = deployment_package_filename + '.tmp.zip'\n\n        with self._osutils.open_zip(deployment_package_filename, 'r') as inzip:\n            with self._osutils.open_zip(\n                tmpzip, 'w', self._osutils.ZIP_DEFLATED\n            ) as outzip:\n                for el in inzip.infolist():\n                    if self._needs_latest_version(el.filename):\n                        continue\n                    contents = inzip.read(el.filename)\n                    outzip.writestr(el, contents)\n                # Then at the end, add back the app.py, chalicelib,\n                # and runtime files.\n                self._add_app_files(outzip, project_dir)\n        self._osutils.move(tmpzip, deployment_package_filename)\n\n    def _needs_latest_version(self, filename: str) -> bool:\n        return filename == 'app.py' or filename.startswith(\n            ('chalicelib/', 'chalice/')\n        )\n\n    def _iter_chalice_lib_if_needed(\n        self, project_dir: str\n    ) -> Iterator[Tuple[str, str]]:\n        libdir = self._osutils.joinpath(project_dir, self._CHALICE_LIB_DIR)\n        if self._osutils.directory_exists(libdir):\n            for rootdir, _, filenames in self._osutils.walk(libdir):\n                for filename in filenames:\n                    fullpath = self._osutils.joinpath(rootdir, filename)\n                    zip_path = self._osutils.joinpath(\n                        self._CHALICE_LIB_DIR, fullpath[len(libdir) + 1:]\n                    )\n                    yield (fullpath, zip_path)\n\n    def _create_output_dir_if_needed(self, package_filename: str) -> None:\n        dirname = self._osutils.dirname(\n            self._osutils.abspath(package_filename)\n        )\n        if not self._osutils.directory_exists(dirname):\n            self._osutils.makedirs(dirname)\n\n    def _build_python_dependencies(\n        self,\n        python_version: str,\n        requirements_filepath: str,\n        site_packages_dir: str,\n    ) -> None:\n        try:\n            abi = self._RUNTIME_TO_ABI[python_version]\n            self._dependency_builder.build_site_packages(\n                abi, requirements_filepath, site_packages_dir\n            )\n        except MissingDependencyError as e:\n            missing_packages = '\\n'.join([p.identifier for p in e.missing])\n            self._ui.write(MISSING_DEPENDENCIES_TEMPLATE % missing_packages)\n\n\nclass LambdaDeploymentPackager(BaseLambdaDeploymentPackager):\n    def create_deployment_package(\n        self, project_dir: str, python_version: str\n    ) -> str:\n        msg = \"Creating deployment package.\"\n        self._ui.write(\"%s\\n\" % msg)\n        logger.debug(msg)\n        package_filename = self.deployment_package_filename(\n            project_dir, python_version\n        )\n        if self._osutils.file_exists(package_filename):\n            self._ui.write(\"Reusing existing deployment package.\\n\")\n            return package_filename\n        self._create_output_dir_if_needed(package_filename)\n        with self._osutils.tempdir() as tmpdir:\n            requirements_filepath = self._get_requirements_filename(\n                project_dir\n            )\n            self._build_python_dependencies(\n                python_version, requirements_filepath, site_packages_dir=tmpdir\n            )\n            with self._osutils.open_zip(\n                package_filename, 'w', self._osutils.ZIP_DEFLATED\n            ) as z:\n                self._add_py_deps(z, deps_dir=tmpdir)\n                self._add_app_files(z, project_dir)\n                self._add_vendor_files(\n                    z, self._osutils.joinpath(project_dir, self._VENDOR_DIR)\n                )\n        return package_filename\n\n\nclass AppOnlyDeploymentPackager(BaseLambdaDeploymentPackager):\n    def create_deployment_package(\n        self, project_dir: str, python_version: str\n    ) -> str:\n        msg = \"Creating app deployment package.\"\n        self._ui.write(\"%s\\n\" % msg)\n        logger.debug(msg)\n        package_filename = self.deployment_package_filename(\n            project_dir, python_version\n        )\n        if self._osutils.file_exists(package_filename):\n            self._ui.write(\"  Reusing existing app deployment package.\\n\")\n            return package_filename\n        self._create_output_dir_if_needed(package_filename)\n        with self._osutils.open_zip(\n            package_filename, 'w', self._osutils.ZIP_DEFLATED\n        ) as z:\n            self._add_app_files(z, project_dir)\n        return package_filename\n\n    def deployment_package_filename(\n        self, project_dir: str, python_version: str\n    ) -> str:\n        return self._deployment_package_filename(\n            project_dir, python_version, prefix='appcode-'\n        )\n\n    def _deployment_package_filename(\n        self, project_dir: str, python_version: str, prefix: str = ''\n    ) -> str:\n        h = hashlib.md5(b'')\n        for filename, _ in self._iter_app_filenames(project_dir):\n            with self._osutils.open(filename, 'rb') as f:\n                reader = functools.partial(f.read, 1024 * 1024)\n                for chunk in iter(reader, b''):\n                    h.update(chunk)\n        digest = h.hexdigest()\n        filename = '%s%s-%s.zip' % (prefix, digest, python_version)\n        deployment_package_filename = self._osutils.joinpath(\n            project_dir, '.chalice', 'deployments', filename\n        )\n        return deployment_package_filename\n\n\nclass LayerDeploymentPackager(BaseLambdaDeploymentPackager):\n\n    # A Lambda layer will unzip into the /opt directory instead of\n    # the current working directory of the function.  This means\n    # in order for our python dependencies to work we need.\n    _PREFIX = 'python/lib/%s/site-packages'\n\n    def create_deployment_package(\n        self, project_dir: str, python_version: str\n    ) -> str:\n        msg = \"Creating shared layer deployment package.\"\n        self._ui.write(\"%s\\n\" % msg)\n        logger.debug(msg)\n        package_filename = self.deployment_package_filename(\n            project_dir, python_version\n        )\n        self._create_output_dir_if_needed(package_filename)\n        if self._osutils.file_exists(package_filename):\n            self._ui.write(\n                \"  Reusing existing shared layer deployment package.\\n\"\n            )\n            return package_filename\n        with self._osutils.tempdir() as tmpdir:\n            requirements_filepath = self._get_requirements_filename(\n                project_dir\n            )\n            self._build_python_dependencies(\n                python_version, requirements_filepath, site_packages_dir=tmpdir\n            )\n            with self._osutils.open_zip(\n                package_filename, 'w', self._osutils.ZIP_DEFLATED\n            ) as z:\n                prefix = self._PREFIX % python_version\n                self._add_py_deps(z, deps_dir=tmpdir, prefix=prefix)\n                self._add_vendor_files(\n                    z,\n                    self._osutils.joinpath(project_dir, self._VENDOR_DIR),\n                    prefix=prefix,\n                )\n        self._check_valid_package(package_filename)\n        return package_filename\n\n    def _check_valid_package(self, package_filename: str) -> None:\n        # Lambda does not allow empty deployment packages, so if there are no\n        # requirements.txt deps or anything in vendor/, we need to let the\n        # call know that we couldn't generate a useful deployment package\n        # for them.  The caller will then need make the appropriate adjustments\n        # i.e remove that LambdaLayer model from the app.\n        with self._osutils.open_zip(\n            package_filename, 'r', self._osutils.ZIP_DEFLATED\n        ) as z:\n            total_size = sum(f.file_size for f in z.infolist())\n            # We have to check the total archive size, Lambda will still error\n            # out if you have a zip file with all empty files.  It's not enough\n            # to check if the zipfile is empty.\n            if not total_size > 0:\n                # We want to make sure we remove any deployment packages we\n                # know are invalid, we don't want them being used as cache\n                # hits in subsequent create_deployment_package() requests.\n                self._osutils.remove_file(package_filename)\n                raise EmptyPackageError(package_filename)\n\n    def deployment_package_filename(\n        self, project_dir: str, python_version: str\n    ) -> str:\n        return self._deployment_package_filename(\n            project_dir, python_version, prefix='managed-layer-'\n        )\n\n    def _deployment_package_filename(\n        self, project_dir: str, python_version: str, prefix: str = ''\n    ) -> str:\n        requirements_filename = self._get_requirements_filename(project_dir)\n        if not self._osutils.file_exists(requirements_filename):\n            contents = b''\n        else:\n            contents = cast(\n                bytes,\n                self._osutils.get_file_contents(\n                    requirements_filename, binary=True\n                ),\n            )\n        h = hashlib.md5(contents)\n        vendor_dir = self._osutils.joinpath(project_dir, self._VENDOR_DIR)\n        if self._osutils.directory_exists(vendor_dir):\n            self._hash_vendor_dir(vendor_dir, h)\n        hash_contents = h.hexdigest()\n        filename = '%s%s-%s.zip' % (prefix, hash_contents, python_version)\n        deployment_package_filename = self._osutils.joinpath(\n            project_dir, '.chalice', 'deployments', filename\n        )\n        return deployment_package_filename\n\n\nclass DependencyBuilder(object):\n    \"\"\"Build site-packages by manually downloading and unpacking wheels.\n\n    Pip is used to download all the dependency sdists. Then wheels that are\n    compatible with lambda are downloaded. Any source packages that do not\n    have a matching wheel file are built into a wheel and that file is checked\n    for compatibility with the lambda python runtime environment.\n\n    All compatible wheels that are downloaded/built this way are unpacked\n    into a site-packages directory, to be included in the bundle by the\n    packager.\n    \"\"\"\n\n    _ADDITIONAL_COMPATIBLE_PLATFORM = {'any', 'linux_x86_64'}\n    _MANYLINUX_LEGACY_MAP = {\n        'manylinux1_x86_64': 'manylinux_2_5_x86_64',\n        'manylinux2010_x86_64': 'manylinux_2_12_x86_64',\n        'manylinux2014_x86_64': 'manylinux_2_17_x86_64',\n    }\n\n    # Mapping of abi to glibc version in Lambda runtime.\n    _RUNTIME_GLIBC = {\n        'cp27mu': (2, 17),\n        'cp36m': (2, 17),\n        'cp37m': (2, 17),\n        'cp38': (2, 26),\n        'cp310': (2, 26),\n        'cp311': (2, 26),\n        'cp312': (2, 34),\n        'cp313': (2, 34),\n    }\n    # Fallback version if we're on an unknown python version\n    # not in _RUNTIME_GLIBC.\n    # Unlikely to hit this case.\n    _DEFAULT_GLIBC = (2, 17)\n\n    _COMPATIBLE_PACKAGE_WHITELIST = {\n        'sqlalchemy',\n        'pyyaml',\n        'pyrsistent',\n    }\n\n    def __init__(\n        self, osutils: OSUtils, pip_runner: Optional[PipRunner] = None\n    ) -> None:\n        self._osutils = osutils\n        if pip_runner is None:\n            pip_runner = PipRunner(SubprocessPip(osutils))\n        self._pip = pip_runner\n\n    def _is_compatible_wheel_filename(\n        self, expected_abi: str, filename: str\n    ) -> bool:\n        wheel = filename[:-4]\n        all_compatibility_tags = self._iter_all_compatibility_tags(wheel)\n        for implementation, abi, platform in all_compatibility_tags:\n            # Verify platform is compatible\n            if not self._is_compatible_platform_tag(expected_abi, platform):\n                continue\n            # Verify that the ABI is compatible with lambda. Either none or the\n            # correct type for the python version cp27mu for py27 and cp36m for\n            # py36.\n            if abi == 'none':\n                return True\n            prefix_version = implementation[:3]\n            expected_abis = [expected_abi]\n            if prefix_version == 'cp3':\n                # Deploying python 3 function which means we can accept the\n                # version specific abi, or we can accept the CPython 3 stable\n                # ABI of 'abi3'.\n                expected_abis.append('abi3')\n            if abi in expected_abis:\n                return True\n        return False\n\n    def _is_compatible_platform_tag(\n        self, expected_abi: str, platform: str\n    ) -> bool:\n        # From PEP 600, the new manylinux tag is\n        # manylinux_${GLIBCMAJOR}_${GLIBCMINOR}_${ARCH}\n        # e.g. manylinux_2_17_x86_64.\n        # To check if the wheel is compatible, we first need to map any of the\n        # legacy manylinux formats to the new perennial format.\n        # Then we verify that the glibc version is compatible with the version\n        # on the Lambda runtime (from _RUNTIME_GLIBC).\n        if platform in self._ADDITIONAL_COMPATIBLE_PLATFORM:\n            logger.debug(\"Found compatible platform tag: %s\", platform)\n            return True\n        elif platform.startswith('manylinux'):\n            # This is roughly based on the \"Package Installers\" section from\n            # PEP 600.\n            perennial_tag = self._MANYLINUX_LEGACY_MAP.get(platform, platform)\n            m = re.match(\"manylinux_([0-9]+)_([0-9]+)_(.*)\", perennial_tag)\n            if m is None:\n                return False\n            tag_major, tag_minor = [int(x) for x in m.groups()[:2]]\n            runtime_major, runtime_minor = self._RUNTIME_GLIBC.get(\n                expected_abi, self._DEFAULT_GLIBC\n            )\n            if (tag_major, tag_minor) <= (runtime_major, runtime_minor):\n                logger.debug(\n                    \"Tag glibc (%s, %s) is compatible with \"\n                    \"runtime glibc (%s, %s)\",\n                    tag_major,\n                    tag_minor,\n                    runtime_major,\n                    runtime_minor,\n                )\n                return True\n        return False\n\n    def _iter_all_compatibility_tags(\n        self, wheel: str\n    ) -> Iterator[Tuple[str, str, str]]:\n        # From PEP 425, section \"Compressed Tag Sets\"\n        #\n        # \"To allow for compact filenames of bdists that work with more than\n        # one compatibility tag triple, each tag in a filename can instead be a\n        # '.'-separated, sorted, set of tags.\"\n        #\n        # So this means that we have to iterate over all the possible\n        # compatibility tag tuples and check if the wheel is compatible.\n        # We just need any of the combinations to match for us to consider the\n        # wheel compatible.\n        implementation_tag, abi_tag, platform_tag = wheel.split('-')[-3:]\n        for implementation in implementation_tag.split('.'):\n            for abi in abi_tag.split('.'):\n                for platform in platform_tag.split('.'):\n                    yield (implementation, abi, platform)\n\n    def _has_at_least_one_package(self, filename: str) -> bool:\n        if not self._osutils.file_exists(filename):\n            return False\n        with open(filename, 'r') as f:\n            # This is meant to be a best effort attempt.\n            # This can return True and still have no packages\n            # actually being specified, but those aren't common\n            # cases.\n            for line in f:\n                line = line.strip()\n                if line and not line.startswith('#'):\n                    return True\n        return False\n\n    def _download_all_dependencies(\n        self, requirements_filename: str, directory: str\n    ) -> Set[Package]:\n        # Download dependencies prefering wheel files but falling back to\n        # raw source dependences to get the transitive closure over\n        # the dependency graph. Return the set of all package objects\n        # which will serve as the master list of dependencies needed to deploy\n        # successfully.\n        self._pip.download_all_dependencies(requirements_filename, directory)\n        deps = {\n            Package(directory, filename)\n            for filename in self._osutils.get_directory_contents(directory)\n        }\n        logger.debug(\"Full dependency closure: %s\", deps)\n        return deps\n\n    def _download_binary_wheels(\n        self, abi: str, packages: Set[Package], directory: str\n    ) -> None:\n        # Try to get binary wheels for each package that isn't compatible.\n        logger.debug(\"Downloading manylinux wheels: %s\", packages)\n        self._pip.download_manylinux_wheels(\n            abi, [pkg.identifier for pkg in packages], directory\n        )\n\n    def _download_sdists(self, packages: Set[Package], directory: str) -> None:\n        logger.debug(\"Downloading missing sdists: %s\", packages)\n        self._pip.download_sdists(\n            [pkg.identifier for pkg in packages], directory\n        )\n\n    def _find_sdists(self, directory: str) -> Set[Package]:\n        packages = [\n            Package(directory, filename)\n            for filename in self._osutils.get_directory_contents(directory)\n        ]\n        sdists = {\n            package for package in packages if package.dist_type == 'sdist'\n        }\n        return sdists\n\n    def _build_sdists(\n        self, sdists: Set[Package], directory: str, compile_c: bool = True\n    ) -> None:\n        logger.debug(\n            \"Build missing wheels from sdists (C compiling %s): %s\",\n            compile_c,\n            sdists,\n        )\n        for sdist in sdists:\n            path_to_sdist = self._osutils.joinpath(directory, sdist.filename)\n            self._pip.build_wheel(path_to_sdist, directory, compile_c)\n\n    def _categorize_wheel_files(\n        self, abi: str, directory: str\n    ) -> Tuple[Set[Package], Set[Package]]:\n        final_wheels = [\n            Package(directory, filename)\n            for filename in self._osutils.get_directory_contents(directory)\n            if filename.endswith('.whl')\n        ]\n\n        compatible_wheels, incompatible_wheels = set(), set()\n        for wheel in final_wheels:\n            if self._is_compatible_wheel_filename(abi, wheel.filename):\n                compatible_wheels.add(wheel)\n            else:\n                incompatible_wheels.add(wheel)\n        return compatible_wheels, incompatible_wheels\n\n    def _categorize_deps(self, abi: str, deps: Set[Package]) -> Any:\n        compatible_wheels = set()\n        incompatible_wheels = set()\n        sdists = set()\n        for package in deps:\n            if package.dist_type == 'sdist':\n                sdists.add(package)\n            else:\n                if self._is_compatible_wheel_filename(abi, package.filename):\n                    compatible_wheels.add(package)\n                else:\n                    incompatible_wheels.add(package)\n        return sdists, compatible_wheels, incompatible_wheels\n\n    def _download_dependencies(\n        self, abi: str, directory: str, requirements_filename: str\n    ) -> Tuple[Set[Package], Set[Package]]:\n        # Download all dependencies we can, letting pip choose what to\n        # download.\n        # deps should represent the best effort we can make to gather all the\n        # dependencies.\n        deps = self._download_all_dependencies(\n            requirements_filename, directory\n        )\n        # Sort the downloaded packages into three categories:\n        # - sdists (Pip could not get a wheel so it gave us an sdist)\n        # - lambda compatible wheel files\n        # - lambda incompatible wheel files\n        # Pip will give us a wheel when it can, but some distributions do not\n        # ship with wheels at all in which case we will have an sdist for it.\n        # In some cases a platform specific wheel file may be availble so pip\n        # will have downloaded that, if our platform does not match the\n        # platform lambda runs on (linux_x86_64/manylinux) then the downloaded\n        # wheel file may not be compatible with lambda. Pure python wheels\n        # still will be compatible because they have no platform dependencies.\n        sdists, compatible_wheels, incompatible_wheels = self._categorize_deps(\n            abi, deps\n        )\n        logger.debug(\"Compatible wheels for Lambda: %s\", compatible_wheels)\n        logger.debug(\n            \"Initial incompatible wheels for Lambda: %s\",\n            incompatible_wheels | sdists,\n        )\n\n        # Next we need to go through the downloaded packages and pick out any\n        # dependencies that do not have a compatible wheel file downloaded.\n        # For these packages we need to explicitly try to download a\n        # compatible wheel file.\n        missing_wheels = sdists.union(incompatible_wheels)\n        self._download_binary_wheels(abi, missing_wheels, directory)\n\n        # Re-count the wheel files after the second download pass. Anything\n        # that has an sdist but not a valid wheel file is still not going to\n        # work on lambda and we must now try and build the sdist into a wheel\n        # file ourselves.\n        # There also may be the case where no sdist was ever downloaded. For\n        # example if we are on MacOS, and the package in question has a mac\n        # compatible wheel file but no linux ones, we will only have an\n        # incompatible wheel file and no sdist. So we need to get any missing\n        # sdists before we can build them.\n        compatible_wheels, incompatible_wheels = self._categorize_wheel_files(\n            abi, directory\n        )\n        # The self._download_binary_wheels() can now introduce duplicate\n        # entries.  For example, if we download a macOS whl at first but\n        # then we're able to download a manylinux1 wheel, we'll now have\n        # two wheels for the package, so we have to remove any compatible\n        # wheels from our set of incompatible wheels.\n        incompatible_wheels -= compatible_wheels\n        missing_sdists = incompatible_wheels - sdists\n        self._download_sdists(missing_sdists, directory)\n        sdists = self._find_sdists(directory)\n        logger.debug(\n            \"compatible wheels after second download pass: %s\",\n            compatible_wheels,\n        )\n        missing_wheels = sdists - compatible_wheels\n        self._build_sdists(missing_wheels, directory, compile_c=True)\n\n        # There is still the case where the package had optional C dependencies\n        # for speedups. In this case the wheel file will have built above with\n        # the C dependencies if it managed to find a C compiler. If we are on\n        # an incompatible architecture this means the wheel file generated will\n        # not be compatible. If we categorize our files once more and find that\n        # there are missing dependencies we can try our last ditch effort of\n        # building the package and trying to sever its ability to find a C\n        # compiler.\n        compatible_wheels, incompatible_wheels = self._categorize_wheel_files(\n            abi, directory\n        )\n        logger.debug(\n            \"compatible after building wheels (C compiling): %s\",\n            compatible_wheels,\n        )\n        missing_wheels = sdists - compatible_wheels\n        self._build_sdists(missing_wheels, directory, compile_c=False)\n\n        # Final pass to find the compatible wheel files and see if there are\n        # any unmet dependencies left over. At this point there is nothing we\n        # can do about any missing wheel files. We tried downloading a\n        # compatible version directly and building from source.\n        compatible_wheels, incompatible_wheels = self._categorize_wheel_files(\n            abi, directory\n        )\n        logger.debug(\n            \"compatible after building wheels (no C compiling): %s\",\n            compatible_wheels,\n        )\n\n        # Now there is still the case left over where the setup.py has been\n        # made in such a way to be incompatible with python's setup tools,\n        # causing it to lie about its compatibility. To fix this we have a\n        # manually curated whitelist of packages that will work, despite\n        # claiming otherwise.\n        compatible_wheels, incompatible_wheels = self._apply_wheel_whitelist(\n            compatible_wheels, incompatible_wheels\n        )\n        missing_wheels = deps - compatible_wheels\n        logger.debug(\"Final compatible: %s\", compatible_wheels)\n        logger.debug(\"Final incompatible: %s\", incompatible_wheels)\n        logger.debug(\"Final missing wheels: %s\", missing_wheels)\n        return compatible_wheels, missing_wheels\n\n    def _apply_wheel_whitelist(\n        self,\n        compatible_wheels: Set[Package],\n        incompatible_wheels: Set[Package],\n    ) -> Tuple[Set[Package], Set[Package]]:\n        compatible_wheels = set(compatible_wheels)\n        actual_incompatible_wheels = set()\n        for missing_package in incompatible_wheels:\n            if missing_package.name in self._COMPATIBLE_PACKAGE_WHITELIST:\n                compatible_wheels.add(missing_package)\n            else:\n                actual_incompatible_wheels.add(missing_package)\n        return compatible_wheels, actual_incompatible_wheels\n\n    def _install_purelib_and_platlib(self, wheel: Package, root: str) -> None:\n        # Take a wheel package and the directory it was just unpacked into and\n        # unpackage the purelib/platlib directories if they are present into\n        # the parent directory. On some systems purelib and platlib need to\n        # be installed into separate locations, for lambda this is not the case\n        # and both should be installed in site-packages.\n        dirnames = self._osutils.get_directory_contents(root)\n        for dirname in dirnames:\n            if wheel.matches_data_dir(dirname):\n                data_dir = self._osutils.joinpath(root, dirname)\n                break\n        else:\n            return\n        unpack_dirs = {'purelib', 'platlib'}\n        data_contents = self._osutils.get_directory_contents(data_dir)\n        for content_name in data_contents:\n            if content_name in unpack_dirs:\n                source = self._osutils.joinpath(data_dir, content_name)\n                self._osutils.copytree(source, root)\n                # No reason to keep the purelib/platlib source directory around\n                # so we delete it to conserve space in the package.\n                self._osutils.rmtree(source)\n\n    def _install_wheels(\n        self, src_dir: str, dst_dir: str, wheels: Set[Package]\n    ) -> None:\n        if self._osutils.directory_exists(dst_dir):\n            self._osutils.rmtree(dst_dir)\n        self._osutils.makedirs(dst_dir)\n        for wheel in wheels:\n            zipfile_path = self._osutils.joinpath(src_dir, wheel.filename)\n            self._osutils.extract_zipfile(zipfile_path, dst_dir)\n            self._install_purelib_and_platlib(wheel, dst_dir)\n\n    def build_site_packages(\n        self, abi: str, requirements_filepath: str, target_directory: str\n    ) -> None:\n        if self._has_at_least_one_package(requirements_filepath):\n            with self._osutils.tempdir() as tempdir:\n                wheels, packages_without_wheels = self._download_dependencies(\n                    abi, tempdir, requirements_filepath\n                )\n                self._install_wheels(tempdir, target_directory, wheels)\n            if packages_without_wheels:\n                raise MissingDependencyError(packages_without_wheels)\n\n\nclass Package(object):\n    \"\"\"A class to represent a package downloaded but not yet installed.\"\"\"\n\n    def __init__(\n        self, directory: str, filename: str, osutils: Optional[OSUtils] = None\n    ) -> None:\n        self.dist_type = 'wheel' if filename.endswith('.whl') else 'sdist'\n        self._directory = directory\n        self.filename = filename\n        if osutils is None:\n            osutils = OSUtils()\n        self._osutils = osutils\n        self._name, self._version = self._calculate_name_and_version()\n\n    @property\n    def name(self) -> str:\n        return self._name\n\n    @property\n    def data_dir(self) -> str:\n        # The directory format is {distribution}-{version}.data\n        return '%s-%s.data' % (self._name, self._version)\n\n    def matches_data_dir(self, dirname: str) -> bool:\n        \"\"\"Check if a directory name matches the data_dir of a package.\n\n        This will normalize the directory name and perform a case-insensitive\n        match against the package name's data dir.\n\n        \"\"\"\n        if not self.dist_type == 'wheel' or '-' not in dirname:\n            return False\n        name, version = dirname.split('-')[:2]\n        comparison_data_dir = '%s-%s' % (self._normalize_name(name), version)\n        return self.data_dir == comparison_data_dir\n\n    @property\n    def identifier(self) -> str:\n        return '%s==%s' % (self._name, self._version)\n\n    def __str__(self) -> str:\n        return '%s(%s)' % (self.identifier, self.dist_type)\n\n    def __repr__(self) -> str:\n        return str(self)\n\n    def __eq__(self, other: Any) -> bool:\n        if not isinstance(other, Package):\n            return False\n        return self.identifier == other.identifier\n\n    def __hash__(self) -> int:\n        return hash(self.identifier)\n\n    def _calculate_name_and_version(self) -> Tuple[str, str]:\n        if self.dist_type == 'wheel':\n            # From the wheel spec (PEP 427)\n            # {distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-\n            # {platform tag}.whl\n            name, version = self.filename.split('-')[:2]\n        else:\n            info_fetcher = SDistMetadataFetcher(osutils=self._osutils)\n            sdist_path = self._osutils.joinpath(self._directory, self.filename)\n            name, version = info_fetcher.get_package_name_and_version(\n                sdist_path\n            )\n        normalized_name = self._normalize_name(name)\n        return normalized_name, version\n\n    def _normalize_name(self, name: str) -> str:\n        # Taken directly from PEP 503\n        return re.sub(r\"[-_.]+\", \"-\", name).lower()\n\n\nclass SDistMetadataFetcher(object):\n    \"\"\"This is the \"correct\" way to get name and version from an sdist.\"\"\"\n\n    # https://git.io/vQkwV\n    _SETUPTOOLS_SHIM = (\n        \"import setuptools, tokenize;__file__=%r;\"\n        \"f=getattr(tokenize, 'open', open)(__file__);\"\n        \"code=f.read().replace('\\\\r\\\\n', '\\\\n');\"\n        \"f.close();\"\n        \"exec(compile(code, __file__, 'exec'))\"\n    )\n\n    def __init__(self, osutils: Optional[OSUtils] = None) -> None:\n        if osutils is None:\n            osutils = OSUtils()\n        self._osutils = osutils\n\n    def _parse_pkg_info_file(self, filepath: str) -> Message:\n        # The PKG-INFO generated by the egg-info command is in an email feed\n        # format, so we use an email feedparser here to extract the metadata\n        # from the PKG-INFO file.\n        data = self._osutils.get_file_contents(filepath, binary=False)\n        parser = FeedParser()\n        parser.feed(data)\n        return parser.close()\n\n    def _get_pkg_info_filepath(self, package_dir: str) -> str:\n        setup_py = self._osutils.joinpath(package_dir, 'setup.py')\n        script = self._SETUPTOOLS_SHIM % setup_py\n\n        cmd = [\n            sys.executable,\n            '-c',\n            script,\n            '--no-user-cfg',\n            'egg_info',\n            '--egg-base',\n            'egg-info',\n        ]\n        egg_info_dir = self._osutils.joinpath(package_dir, 'egg-info')\n        self._osutils.makedirs(egg_info_dir)\n        p = subprocess.Popen(\n            cmd,\n            cwd=package_dir,\n            stdout=subprocess.PIPE,\n            stderr=subprocess.PIPE,\n        )\n        _, stderr = p.communicate()\n        if p.returncode != 0:\n            logger.debug(\n                \"Non zero rc (%s) from the setup.py egg_info command: %s\",\n                p.returncode,\n                stderr,\n            )\n        info_contents = self._osutils.get_directory_contents(egg_info_dir)\n        if info_contents:\n            pkg_info_path = self._osutils.joinpath(\n                egg_info_dir, info_contents[0], 'PKG-INFO'\n            )\n        else:\n            # This might be a pep 517 package in which case this PKG-INFO file\n            # should be available right in the top level directory of the sdist\n            # in the case where the egg_info command fails.\n            logger.debug(\n                \"Using fallback location for PKG-INFO file in \"\n                \"package directory: %s\",\n                package_dir,\n            )\n            pkg_info_path = self._osutils.joinpath(package_dir, 'PKG-INFO')\n        if not self._osutils.file_exists(pkg_info_path):\n            raise UnsupportedPackageError(self._osutils.basename(package_dir))\n        return pkg_info_path\n\n    def _unpack_sdist_into_dir(self, sdist_path: str, unpack_dir: str) -> str:\n        if sdist_path.endswith('.zip'):\n            self._osutils.extract_zipfile(sdist_path, unpack_dir)\n        elif sdist_path.endswith(('.tar.gz', '.tar.bz2')):\n            self._osutils.extract_tarfile(sdist_path, unpack_dir)\n        else:\n            raise InvalidSourceDistributionNameError(sdist_path)\n        # There should only be one directory unpacked.\n        contents = self._osutils.get_directory_contents(unpack_dir)\n        return self._osutils.joinpath(unpack_dir, contents[0])\n\n    def get_package_name_and_version(self, sdist_path: str) -> Tuple[str, str]:\n        with self._osutils.tempdir() as tempdir:\n            package_dir = self._unpack_sdist_into_dir(sdist_path, tempdir)\n            pkg_info_filepath = self._get_pkg_info_filepath(package_dir)\n            metadata = self._parse_pkg_info_file(pkg_info_filepath)\n            name = metadata['Name']\n            version = metadata['Version']\n        return name, version\n\n\nclass SubprocessPip(object):\n    \"\"\"Wrapper around calling pip through a subprocess.\"\"\"\n\n    def __init__(\n        self, osutils: Optional[OSUtils] = None, import_string: OptStr = None\n    ) -> None:\n        if osutils is None:\n            osutils = OSUtils()\n        self._osutils = osutils\n        if import_string is None:\n            import_string = pip_import_string()\n        self._import_string = import_string\n\n    def main(\n        self,\n        args: List[str],\n        env_vars: Optional[EnvVars] = None,\n        shim: OptStr = None,\n    ) -> Tuple[int, bytes, bytes]:\n        if env_vars is None:\n            env_vars = self._osutils.environ()\n        if shim is None:\n            shim = ''\n        python_exe = sys.executable\n        run_pip = ('import sys; %s; sys.exit(main(%s))') % (\n            self._import_string,\n            args,\n        )\n        exec_string = '%s%s' % (shim, run_pip)\n        invoke_pip = [python_exe, '-c', exec_string]\n        p = self._osutils.popen(\n            invoke_pip,\n            stdout=self._osutils.pipe,\n            stderr=self._osutils.pipe,\n            env=env_vars,\n        )\n        out, err = p.communicate()\n        rc = p.returncode\n        return rc, out, err\n\n\nclass PipRunner(object):\n    \"\"\"Wrapper around pip calls used by chalice.\"\"\"\n\n    _LINK_IS_DIR_PATTERN = (\n        \"Processing (.+?)\\n  Link is a directory, ignoring download_dir\"\n    )\n\n    def __init__(\n        self, pip: SubprocessPip, osutils: Optional[OSUtils] = None\n    ) -> None:\n        if osutils is None:\n            osutils = OSUtils()\n        self._wrapped_pip = pip\n        self._osutils = osutils\n\n    def _execute(\n        self,\n        command: str,\n        args: List[str],\n        env_vars: Optional[EnvVars] = None,\n        shim: OptStr = None,\n    ) -> Tuple[int, bytes, bytes]:\n        \"\"\"Execute a pip command with the given arguments.\"\"\"\n        main_args = [command] + args\n        logger.debug(\"calling pip %s\", ' '.join(main_args))\n        rc, out, err = self._wrapped_pip.main(\n            main_args, env_vars=env_vars, shim=shim\n        )\n        return rc, out, err\n\n    def build_wheel(\n        self, wheel: str, directory: str, compile_c: bool = True\n    ) -> None:\n        \"\"\"Build an sdist into a wheel file.\"\"\"\n        arguments = ['--no-deps', '--wheel-dir', directory, wheel]\n        env_vars = self._osutils.environ()\n        shim = ''\n        if not compile_c:\n            env_vars.update(pip_no_compile_c_env_vars)\n            shim = pip_no_compile_c_shim\n        # Ignore rc and stderr from this command since building the wheels\n        # may fail and we will find out when we categorize the files that were\n        # generated.\n        self._execute('wheel', arguments, env_vars=env_vars, shim=shim)\n\n    def download_all_dependencies(\n        self, requirements_filename: str, directory: str\n    ) -> None:\n        \"\"\"Download all dependencies as sdist or wheel.\"\"\"\n        arguments = ['-r', requirements_filename, '--dest', directory]\n        rc, out, err = self._execute('download', arguments)\n        # When downloading all dependencies we expect to get an rc of 0 back\n        # since we are casting a wide net here letting pip have options about\n        # what to download. If a package is not found it is likely because it\n        # does not exist and was mispelled. In this case we raise an error with\n        # the package name. Otherwise a nonzero rc results in a generic\n        # download error where we pass along the stderr.\n        if rc != 0:\n            if err is None:\n                err = b'Unknown error'\n            error = err.decode()\n            match = re.search(\n                r\"Could not find a version that satisfies the \"\n                r\"requirement ([^\\s]+)\",\n                error,\n            )\n            if match:\n                package_name = match.group(1)\n                raise NoSuchPackageError(str(package_name))\n            raise PackageDownloadError(error)\n        stdout = out.decode()\n        matches = re.finditer(self._LINK_IS_DIR_PATTERN, stdout)\n        for match in matches:\n            wheel_package_path = str(match.group(1))\n            # Looks odd we do not check on the error status of building the\n            # wheel here. We can assume this is a valid package path since\n            # we already passed the pip download stage. This stage would have\n            # thrown a PackageDownloadError if any of the listed packages were\n            # not valid.\n            # If it fails the actual build step, it will have the same behavior\n            # as any other package we fail to build a valid wheel for, and\n            # complain at deployment time.\n            self.build_wheel(wheel_package_path, directory)\n\n    def download_manylinux_wheels(\n        self, abi: str, packages: List[str], directory: str\n    ) -> None:\n        \"\"\"Download wheel files for manylinux for all the given packages.\"\"\"\n        # If any one of these dependencies fails pip will bail out. Since we\n        # are only interested in all the ones we can download, we need to feed\n        # each package to pip individually. The return code of pip doesn't\n        # matter here since we will inspect the working directory to see which\n        # wheels were downloaded. We are only interested in wheel files\n        # compatible with lambda, which means manylinux1_x86_64 platform and\n        # cpython implementation. The compatible abi depends on the python\n        # version and is checked later.\n        for package in packages:\n            arguments = [\n                '--only-binary=:all:',\n                '--no-deps',\n                '--platform',\n                'manylinux2014_x86_64',\n                '--implementation',\n                'cp',\n                '--abi',\n                abi,\n                '--dest',\n                directory,\n                package,\n            ]\n            self._execute('download', arguments)\n\n    def download_sdists(self, packages: List[str], directory: str) -> None:\n        for package in packages:\n            arguments = [\n                \"--no-binary=:all:\",\n                \"--no-deps\",\n                \"--dest\",\n                directory,\n                package,\n            ]\n            self._execute('download', arguments)\n"
  },
  {
    "path": "chalice/deploy/planner.py",
    "content": "# pylint: disable=too-many-lines\nimport re\nimport json\nfrom collections import OrderedDict\n\nfrom typing import List, Dict, Any, Optional, Union, Tuple, Set, cast  # noqa\nfrom typing import Sequence  # noqa\n\nfrom chalice.config import Config, DeployedResources  # noqa\nfrom chalice.utils import OSUtils  # noqa\nfrom chalice.deploy import models\nfrom chalice.awsclient import TypedAWSClient, ResourceDoesNotExistError  # noqa\n\n\nInstructionMsg = Union[models.Instruction, Tuple[models.Instruction, str]]\nMarkedResource = Dict[str, List[models.RecordResource]]\nCacheTuples = Union[Tuple[str, str, str], Tuple[str, str]]\nApiMap = Union[models.RestAPI, models.WebsocketAPI]\n\n\nclass RemoteState(object):\n    def __init__(self, client, deployed_resources):\n        # type: (TypedAWSClient, DeployedResources) -> None\n        self._client = client\n        self._cache = {}  # type: Dict[CacheTuples, bool]\n        self._deployed_resources = deployed_resources\n\n    def _cache_key(self, resource):\n        # type: (models.ManagedModel) -> CacheTuples\n        if isinstance(resource, models.APIMapping):\n            return (\n                resource.resource_type,\n                resource.resource_name,\n                resource.mount_path\n            )\n        return resource.resource_type, resource.resource_name\n\n    def resource_deployed_values(self, resource):\n        # type: (models.ManagedModel) -> Dict[str, Any]\n        try:\n            return self._deployed_resources.resource_values(\n                resource.resource_name)\n        except ValueError:\n            return self._dynamically_lookup_values(resource)\n\n    def _dynamically_lookup_values(self, resource):\n        # type: (models.ManagedModel) -> Dict[str, str]\n        if isinstance(resource, models.ManagedIAMRole):\n            arn = self._client.get_role_arn_for_name(resource.role_name)\n            return {\n                \"role_name\": resource.role_name,\n                \"role_arn\": arn,\n                \"name\": resource.resource_name,\n                \"resource_type\": \"iam_role\",\n            }\n        raise ValueError(\"Deployed values for resource does not exist: %s\"\n                         % resource.resource_name)\n\n    def resource_exists(self, resource, *args):\n        # type: (models.ManagedModel, Optional[Any]) -> bool\n        key = self._cache_key(resource)\n        if key in self._cache:\n            return self._cache[key]\n        try:\n            handler = getattr(self, '_resource_exists_%s'\n                              % resource.__class__.__name__.lower())\n        except AttributeError:\n            raise ValueError(\"RemoteState received an unsupported resource: %s\"\n                             % resource.resource_type)\n        result = handler(resource, *args)\n        self._cache[key] = result\n        return result\n\n    def _resource_exists_snslambdasubscription(self, resource):\n        # type: (models.SNSLambdaSubscription) -> bool\n        try:\n            deployed_values = self._deployed_resources.resource_values(\n                resource.resource_name)\n        except ValueError:\n            return False\n        return self._client.verify_sns_subscription_current(\n            deployed_values['subscription_arn'],\n            topic_name=resource.topic,\n            function_arn=deployed_values['lambda_arn'],\n        )\n\n    def _resource_exists_sqseventsource(self, resource):\n        # type: (models.SQSEventSource) -> bool\n        try:\n            deployed_values = self._deployed_resources.resource_values(\n                resource.resource_name)\n        except ValueError:\n            return False\n        if isinstance(resource.queue, models.QueueARN):\n            resource_name = resource.queue.queue_name\n        else:\n            resource_name = resource.queue\n        return self._client.verify_event_source_current(\n            event_uuid=deployed_values['event_uuid'],\n            resource_name=resource_name,\n            service_name='sqs',\n            function_arn=deployed_values['lambda_arn'],\n        )\n\n    def _resource_exists_kinesiseventsource(self, resource):\n        # type: (models.KinesisEventSource) -> bool\n        try:\n            deployed_values = self._deployed_resources.resource_values(\n                resource.resource_name)\n        except ValueError:\n            return False\n        return self._client.verify_event_source_current(\n            event_uuid=deployed_values['event_uuid'],\n            resource_name='stream/%s' % resource.stream,\n            service_name='kinesis',\n            function_arn=deployed_values['lambda_arn'],\n        )\n\n    def _resource_exists_dynamodbeventsource(self, resource):\n        # type: (models.DynamoDBEventSource) -> bool\n        try:\n            deployed_values = self._deployed_resources.resource_values(\n                resource.resource_name)\n        except ValueError:\n            return False\n        return self._client.verify_event_source_arn_current(\n            event_uuid=deployed_values['event_uuid'],\n            event_source_arn=deployed_values['stream_arn'],\n            function_arn=deployed_values['lambda_arn'],\n        )\n\n    def _resource_exists_lambdalayer(self, resource):\n        # type: (models.LambdaLayer) -> bool\n        try:\n            deployed_values = self._deployed_resources.resource_values(\n                resource.resource_name)\n        except ValueError:\n            return False\n        return bool(self._client.get_layer_version(\n            deployed_values['layer_version_arn']))\n\n    def _resource_exists_loggroup(self, resource):\n        # type: (models.LogGroup) -> bool\n        return self._client.log_group_exists(resource.log_group_name)\n\n    def _resource_exists_lambdafunction(self, resource):\n        # type: (models.LambdaFunction) -> bool\n        return self._client.lambda_function_exists(resource.function_name)\n\n    def _resource_exists_managediamrole(self, resource):\n        # type: (models.ManagedIAMRole) -> bool\n        try:\n            self._client.get_role_arn_for_name(resource.role_name)\n            return True\n        except ResourceDoesNotExistError:\n            return False\n\n    def _resource_exists_apimapping(self, resource, domain_name):\n        # type: (models.APIMapping, str) -> bool\n        map_key = resource.mount_path\n        if map_key == '(none)':\n            map_key = ''\n        elif map_key.startswith('/'):\n            map_key = map_key.lstrip('/')\n\n        return self._client.api_mapping_exists(domain_name, map_key)\n\n    def _resource_exists_domainname(self, resource):\n        # type: (models.DomainName) -> bool\n        if resource.protocol == models.APIType.WEBSOCKET:\n            return self._client.domain_name_exists_v2(\n                resource.domain_name)\n        return self._client.domain_name_exists(resource.domain_name)\n\n    def _resource_exists_restapi(self, resource):\n        # type: (models.RestAPI) -> bool\n        try:\n            deployed_values = self._deployed_resources.resource_values(\n                resource.resource_name)\n        except ValueError:\n            return False\n        rest_api_id = deployed_values['rest_api_id']\n        return bool(self._client.get_rest_api(rest_api_id))\n\n    def _resource_exists_websocketapi(self, resource):\n        # type: (models.WebsocketAPI) -> bool\n        try:\n            deployed_values = self._deployed_resources.resource_values(\n                resource.resource_name)\n        except ValueError:\n            return False\n        api_id = deployed_values['websocket_api_id']\n        return self._client.websocket_api_exists(api_id)\n\n\nclass PlanStage(object):\n    def __init__(self, remote_state, osutils):\n        # type: (RemoteState, OSUtils) -> None\n        self._remote_state = remote_state\n        self._osutils = osutils\n\n    def execute(self, resources):\n        # type: (List[models.Model]) -> models.Plan\n        plan = []  # type: List[models.Instruction]\n        messages = {}  # type: Dict[int, str]\n        for resource in resources:\n            name = '_plan_%s' % resource.__class__.__name__.lower()\n            handler = getattr(self, name, None)\n            if handler is not None:\n                result = handler(resource)\n                if result:\n                    self._add_result_to_plan(result, plan, messages)\n        return models.Plan(plan, messages)\n\n    def _add_result_to_plan(self,\n                            result,    # type: Sequence[InstructionMsg]\n                            plan,      # type: List[models.Instruction]\n                            messages,  # type: Dict[int, str]\n                            ):\n        # type: (...) -> None\n        for single in result:\n            if isinstance(single, tuple):\n                instruction, message = single\n                plan.append(instruction)\n                messages[id(instruction)] = message\n            else:\n                plan.append(single)\n\n    # TODO: This code will likely be refactored and pulled into\n    # per-resource classes so the PlanStage object doesn't need\n    # to know about every type of resource.\n\n    def _add_apimapping_plan(self,\n                             resource,    # type: models.APIMapping\n                             domain_name  # type: models.DomainName\n                             ):\n        # type: (...) -> Sequence[InstructionMsg]\n        api_calls = []  # type: List[InstructionMsg]\n        params = {\n            'domain_name': domain_name.domain_name,\n            'path_key': resource.mount_path,\n            'stage': resource.api_gateway_stage\n        }  # type: Dict[str, Any]\n        if domain_name.protocol == models.APIType.WEBSOCKET:\n            params['api_id'] = Variable('websocket_api_id')\n            variable_name = 'websocket_api_mapping'\n            api_call = models.APICall(\n                method_name='create_api_mapping',\n                params=params,\n                output_var='api_mapping'\n            )\n        else:\n            params['api_id'] = Variable('rest_api_id')\n            variable_name = 'rest_api_mapping'\n            api_call = models.APICall(\n                method_name='create_base_path_mapping',\n                params=params,\n                output_var='api_mapping'\n            )\n\n        if not self._remote_state.resource_exists(\n                resource, domain_name.domain_name\n        ):\n            path_to_print = '/'\n            if resource.mount_path != '(none)' and \\\n                    not resource.mount_path.startswith(\"/\"):\n                path_to_print = '/%s' % resource.mount_path\n            api_calls.extend([\n                (api_call, \"Creating api mapping: %s\\n\" % path_to_print),\n                models.StoreMultipleValue(\n                    name=variable_name,\n                    value=[Variable('api_mapping')]\n                ),\n                models.RecordResourceVariable(\n                    resource_type='domain_name',\n                    resource_name=domain_name.resource_name,\n                    name='api_mapping',\n                    variable_name=variable_name\n                ),\n            ])\n        else:\n            deployed = self._remote_state.resource_deployed_values(\n                domain_name\n            )\n            for api_mapping in deployed['api_mapping']:\n\n                mount_path = api_mapping['key'].lstrip('/')\n                if not mount_path:\n                    mount_path = '(none)'\n                if mount_path != resource.mount_path:\n                    continue\n\n                api_calls.extend([\n                    models.StoreMultipleValue(\n                        name=variable_name,\n                        value=[api_mapping]\n                    ),\n                    models.RecordResourceVariable(\n                        resource_type='domain_name',\n                        resource_name=domain_name.resource_name,\n                        name='api_mapping',\n                        variable_name=variable_name\n                    ),\n                ])\n        return api_calls\n\n    def _add_domainname_plan(self, resource, endpoint_type):\n        # type: (models.DomainName, str) -> Sequence[InstructionMsg]\n        api_calls = []  # type: List[InstructionMsg]\n\n        params = {\n            'protocol': resource.protocol.value,\n            'tags': resource.tags,\n            'endpoint_type': endpoint_type,\n            'domain_name': resource.domain_name,\n        }\n        params['certificate_arn'] = resource.certificate_arn\n        if resource.tls_version is not None:\n            params['security_policy'] = resource.tls_version.value\n\n        if not self._remote_state.resource_exists(resource):\n            domain_name_api_call = (\n                models.APICall(\n                    method_name='create_domain_name',\n                    params=params,\n                    output_var=resource.resource_name\n                ),\n                \"Creating custom domain name: %s\\n\" % resource.domain_name\n            )\n\n        else:\n            domain_name_api_call = (\n                models.APICall(\n                    method_name='update_domain_name',\n                    params=params,\n                    output_var=resource.resource_name\n                ),\n                \"Updating custom domain name: %s\\n\" % resource.domain_name\n            )\n\n        api_calls.extend([\n            domain_name_api_call,\n            models.StoreValue(\n                name='hosted_zone_id',\n                value=KeyDataVariable(resource.resource_name,\n                                      'hosted_zone_id')\n            ),\n            models.RecordResourceVariable(\n                resource_type='domain_name',\n                resource_name=resource.resource_name,\n                name='hosted_zone_id',\n                variable_name='hosted_zone_id'\n            ),\n            models.StoreValue(\n                name='alias_domain_name',\n                value=KeyDataVariable(resource.resource_name,\n                                      'alias_domain_name')\n            ),\n            models.RecordResourceVariable(\n                resource_type='domain_name',\n                resource_name=resource.resource_name,\n                name='alias_domain_name',\n                variable_name='alias_domain_name'\n            ),\n            models.StoreValue(\n                name='certificate_arn',\n                value=KeyDataVariable(resource.resource_name,\n                                      'certificate_arn')\n            ),\n            models.RecordResourceVariable(\n                resource_type='domain_name',\n                resource_name=resource.resource_name,\n                name='certificate_arn',\n                variable_name='certificate_arn'\n            ),\n            models.StoreValue(\n                name='security_policy',\n                value=KeyDataVariable(resource.resource_name,\n                                      'security_policy')\n            ),\n            models.RecordResourceVariable(\n                resource_type='domain_name',\n                resource_name=resource.resource_name,\n                name='security_policy',\n                variable_name='security_policy'\n            ),\n            models.RecordResourceValue(\n                resource_type='domain_name',\n                resource_name=resource.resource_name,\n                name='domain_name',\n                value=resource.domain_name\n            )\n        ])\n        return api_calls\n\n    def _plan_lambdalayer(self, resource):\n        # type: (models.LambdaLayer) -> Sequence[InstructionMsg]\n\n        api_calls = []  # type: List[InstructionMsg]\n        filename = cast(str, resource.deployment_package.filename)\n\n        # Automatically clean up old layer versions.\n        # See:\n        # https://docs.aws.amazon.com/lambda/latest/dg/API_DeleteLayerVersion.html\n        msg = 'Creating'\n        if self._remote_state.resource_exists(resource):\n            state = self._remote_state.resource_deployed_values(resource)\n            # Deleting a layer version won't break functions still using it.\n            # From the doc link above:\n            #\n            # \"To avoid breaking functions, a copy of the version remains in\n            # Lambda until no functions refer to it.\"\n            api_calls.append(\n                models.APICall(\n                    method_name='delete_layer_version',\n                    params={'layer_version_arn': state['layer_version_arn']}\n                )\n            )\n            msg = 'Updating'\n\n        api_calls.extend([(\n            models.APICall(\n                method_name='publish_layer',\n                params={'layer_name': resource.layer_name,\n                        'zip_contents': self._osutils.get_file_contents(\n                            filename, binary=True),\n                        'runtime': resource.runtime},\n                output_var='layer_version_arn'\n            ), \"%s lambda layer: %s\\n\" % (msg, resource.layer_name)),\n            models.RecordResourceVariable(\n                resource_type='lambda_layer',\n                resource_name=resource.resource_name,\n                name='layer_version_arn',\n                variable_name='layer_version_arn',\n        )])\n        return api_calls\n\n    def _plan_lambdafunction(self, resource):\n        # type: (models.LambdaFunction) -> Sequence[InstructionMsg]\n        role_arn = self._get_role_arn(resource.role)\n        # Make mypy happy, it complains if we don't \"declare\" this upfront.\n        params = {}  # type: Dict[str, Any]\n        varname = '%s_lambda_arn' % resource.resource_name\n        # Not sure the best way to express this via mypy, but we know\n        # that in the build stage we replace the deployment package\n        # name with the actual filename generated from the pip\n        # packager.  For now we resort to a cast.\n        filename = cast(str, resource.deployment_package.filename)\n\n        if resource.reserved_concurrency is None:\n            concurrency_api_call = models.APICall(\n                method_name='delete_function_concurrency',\n                params={\n                    'function_name': resource.function_name,\n                },\n                output_var='reserved_concurrency_result'\n            )\n        else:\n            concurrency = resource.reserved_concurrency\n            concurrency_api_call = (\n                models.APICall(\n                    method_name='put_function_concurrency',\n                    params={\n                        'function_name': resource.function_name,\n                        'reserved_concurrent_executions': concurrency,\n                    },\n                    output_var='reserved_concurrency_result'),\n                \"Updating lambda function concurrency limit: %s\\n\"\n                % resource.function_name\n            )\n\n        api_calls = []  # type: List[InstructionMsg]\n        layers = []  # type: List[Any]\n        if resource.managed_layer is not None:\n            layers.append(Variable('layer_version_arn'))\n        if resource.layers:\n            layers.extend(resource.layers)\n\n        if not self._remote_state.resource_exists(resource):\n            params = {\n                'function_name': resource.function_name,\n                'role_arn': role_arn,\n                'zip_contents': self._osutils.get_file_contents(\n                    filename, binary=True),\n                'runtime': resource.runtime,\n                'handler': resource.handler,\n                'environment_variables': resource.environment_variables,\n                'xray': resource.xray,\n                'tags': resource.tags,\n                'timeout': resource.timeout,\n                'memory_size': resource.memory_size,\n                'security_group_ids': resource.security_group_ids,\n                'subnet_ids': resource.subnet_ids,\n                'layers': layers\n            }\n\n            api_calls.extend([\n                (models.APICall(\n                    method_name='create_function',\n                    params=params,\n                    output_var=varname,\n                ), \"Creating lambda function: %s\\n\" % resource.function_name),\n                models.RecordResourceVariable(\n                    resource_type='lambda_function',\n                    resource_name=resource.resource_name,\n                    name='lambda_arn',\n                    variable_name=varname,\n                )\n            ])\n        else:\n            # TODO: Consider a smarter diff where we check if we even need\n            # to do an update() API call.\n            params = {\n                'function_name': resource.function_name,\n                'role_arn': role_arn,\n                'zip_contents': self._osutils.get_file_contents(\n                    filename, binary=True),\n                'runtime': resource.runtime,\n                'environment_variables': resource.environment_variables,\n                'xray': resource.xray,\n                'tags': resource.tags,\n                'timeout': resource.timeout,\n                'memory_size': resource.memory_size,\n                'security_group_ids': resource.security_group_ids,\n                'subnet_ids': resource.subnet_ids,\n                'layers': layers\n            }\n            api_calls.extend([\n                (models.APICall(\n                    method_name='update_function',\n                    params=params,\n                    output_var='update_function_result',\n                ), \"Updating lambda function: %s\\n\" % resource.function_name),\n                models.JPSearch(\n                    'FunctionArn',\n                    input_var='update_function_result',\n                    output_var=varname,\n                ),\n                models.RecordResourceVariable(\n                    resource_type='lambda_function',\n                    resource_name=resource.resource_name,\n                    name='lambda_arn',\n                    variable_name=varname,\n                )\n            ])\n        api_calls.append(concurrency_api_call)\n        return api_calls\n\n    def _plan_managediamrole(self, resource):\n        # type: (models.ManagedIAMRole) -> Sequence[InstructionMsg]\n        document = resource.policy.document\n        role_exists = self._remote_state.resource_exists(resource)\n        varname = '%s_role_arn' % resource.role_name\n        if not role_exists:\n            return [\n                models.BuiltinFunction(\n                    'service_principal',\n                    ['lambda'],\n                    output_var='lambda_service_principal',\n                ),\n                models.JPSearch('principal',\n                                input_var='lambda_service_principal',\n                                output_var='lambda_principal'),\n                models.StoreValue(\n                    name='lambda_principal',\n                    value=StringFormat('{lambda_principal}',\n                                       ['lambda_principal']),\n                ),\n                models.StoreValue(\n                    name='lambda_trust_policy',\n                    value={\n                        \"Version\": \"2012-10-17\",\n                        \"Statement\": [{\n                            \"Sid\": \"\",\n                            \"Effect\": \"Allow\",\n                            \"Principal\": {\n                                \"Service\": Variable('lambda_principal')\n                            },\n                            \"Action\": \"sts:AssumeRole\"\n                        }]\n                    },\n                ),\n                (models.APICall(\n                    method_name='create_role',\n                    params={'name': resource.role_name,\n                            'trust_policy': Variable('lambda_trust_policy'),\n                            'policy': document},\n                    output_var=varname,\n                ), \"Creating IAM role: %s\\n\" % resource.role_name),\n                models.RecordResourceVariable(\n                    resource_type='iam_role',\n                    resource_name=resource.resource_name,\n                    name='role_arn',\n                    variable_name=varname,\n                ),\n                models.RecordResourceValue(\n                    resource_type='iam_role',\n                    resource_name=resource.resource_name,\n                    name='role_name',\n                    value=resource.role_name,\n                )\n            ]\n        role_arn = self._remote_state.resource_deployed_values(\n            resource)['role_arn']\n        return [\n            models.StoreValue(name=varname, value=role_arn),\n            (models.APICall(\n                method_name='put_role_policy',\n                params={'role_name': resource.role_name,\n                        'policy_name': resource.role_name,\n                        'policy_document': document},\n            ), \"Updating policy for IAM role: %s\\n\" % resource.role_name),\n            models.RecordResourceVariable(\n                resource_type='iam_role',\n                resource_name=resource.resource_name,\n                name='role_arn',\n                variable_name=varname,\n            ),\n            models.RecordResourceValue(\n                resource_type='iam_role',\n                resource_name=resource.resource_name,\n                name='role_name',\n                value=resource.role_name,\n            )\n        ]\n\n    def _plan_snslambdasubscription(self, resource):\n        # type: (models.SNSLambdaSubscription) -> Sequence[InstructionMsg]\n        function_arn = Variable(\n            '%s_lambda_arn' % resource.lambda_function.resource_name\n        )\n        topic_arn_varname = '%s_topic_arn' % resource.resource_name\n        subscribe_varname = '%s_subscription_arn' % resource.resource_name\n\n        instruction_for_topic_arn = []  # type: List[InstructionMsg]\n        if re.match(r\"^arn:aws[a-z\\-]*:sns:\", resource.topic):\n            instruction_for_topic_arn += [\n                models.StoreValue(\n                    name=topic_arn_varname,\n                    value=resource.topic,\n                )\n            ]\n        else:\n            # To keep the user API simple, we only require the topic\n            # name and not the ARN.  However, the APIs require the topic\n            # ARN so we need to reconstruct it here in the planner.\n            instruction_for_topic_arn += self._arn_parse_instructions(\n                function_arn) + [\n                models.StoreValue(\n                    name=topic_arn_varname,\n                    value=StringFormat(\n                        'arn:{partition}:sns:{region_name}:{account_id}:%s' % (\n                            resource.topic\n                        ),\n                        ['partition', 'region_name', 'account_id'],\n                    ),\n                )\n            ]\n        if self._remote_state.resource_exists(resource):\n            # Given there's nothing about an SNS subscription you can\n            # configure for now, if the resource exists, we don't do\n            # anything.  The resource sweeper will verify that if the\n            # subscription doesn't actually apply that we should unsubscribe\n            # from the topic.\n            deployed = self._remote_state.resource_deployed_values(resource)\n            subscription_arn = deployed['subscription_arn']\n            return instruction_for_topic_arn + self._batch_record_resource(\n                'sns_event', resource.resource_name, {\n                    'topic': resource.topic,\n                    'lambda_arn': Variable(function_arn.name),\n                    'subscription_arn': subscription_arn,\n                    'topic_arn': Variable(topic_arn_varname),\n                }\n            )\n        return instruction_for_topic_arn + [\n            models.APICall(\n                method_name='add_permission_for_sns_topic',\n                params={'topic_arn': Variable(topic_arn_varname),\n                        'function_arn': function_arn},\n            ),\n            (models.APICall(\n                method_name='subscribe_function_to_topic',\n                params={'topic_arn': Variable(topic_arn_varname),\n                        'function_arn': function_arn},\n                output_var=subscribe_varname,\n            ), 'Subscribing %s to SNS topic %s\\n'\n                % (resource.lambda_function.function_name, resource.topic)\n            )\n        ] + self._batch_record_resource(\n            'sns_event', resource.resource_name, {\n                'topic': resource.topic,\n                'lambda_arn': Variable(function_arn.name),\n                'subscription_arn': Variable(subscribe_varname),\n                'topic_arn': Variable(topic_arn_varname),\n            }\n        )\n\n    def _plan_sqseventsource(self, resource):\n        # type: (models.SQSEventSource) -> Sequence[InstructionMsg]\n        queue_arn_varname = '%s_queue_arn' % resource.resource_name\n        uuid_varname = '%s_uuid' % resource.resource_name\n        function_arn = Variable(\n            '%s_lambda_arn' % resource.lambda_function.resource_name\n        )\n        if not isinstance(resource.queue, models.QueueARN):\n            instruction_for_queue_arn = self._arn_parse_instructions(\n                function_arn)\n            instruction_for_queue_arn.append(\n                models.StoreValue(\n                    name=queue_arn_varname,\n                    value=StringFormat(\n                        'arn:{partition}:sqs:{region_name}:{account_id}:%s' % (\n                            resource.queue\n                        ),\n                        ['partition', 'region_name', 'account_id'],\n                    ),\n                )\n            )\n            queue_name = resource.queue\n        else:\n            instruction_for_queue_arn = [\n                models.StoreValue(\n                    name=queue_arn_varname,\n                    value=resource.queue.arn,\n                )\n            ]\n            queue_name = resource.queue.queue_name\n        if self._remote_state.resource_exists(resource):\n            deployed = self._remote_state.resource_deployed_values(resource)\n            uuid = deployed['event_uuid']\n            return instruction_for_queue_arn + [\n                models.APICall(\n                    method_name='update_lambda_event_source',\n                    params={\n                        'event_uuid': uuid,\n                        'batch_size': resource.batch_size,\n                        'maximum_batching_window_in_seconds':\n                            resource.maximum_batching_window_in_seconds,\n                        'maximum_concurrency': resource.maximum_concurrency\n                    }\n                )\n            ] + self._batch_record_resource(\n                'sqs_event', resource.resource_name, {\n                    'queue_arn': deployed['queue_arn'],\n                    'event_uuid': uuid,\n                    'queue': queue_name,\n                    'lambda_arn': deployed['lambda_arn'],\n                }\n            )\n        return instruction_for_queue_arn + [\n            (models.APICall(\n                method_name='create_lambda_event_source',\n                params={'event_source_arn': Variable(queue_arn_varname),\n                        'batch_size': resource.batch_size,\n                        'maximum_batching_window_in_seconds':\n                            resource.maximum_batching_window_in_seconds,\n                        'maximum_concurrency': resource.maximum_concurrency,\n                        'function_name': function_arn},\n                output_var=uuid_varname,\n            ), 'Subscribing %s to SQS queue %s\\n'\n                % (resource.lambda_function.function_name, resource.queue)\n            ),\n        ] + self._batch_record_resource(\n            'sqs_event', resource.resource_name, {\n                'queue_arn': Variable(queue_arn_varname),\n                'event_uuid': Variable(uuid_varname),\n                'queue': queue_name,\n                'lambda_arn': Variable(function_arn.name)\n            }\n        )\n\n    def _plan_kinesiseventsource(self, resource):\n        # type: (models.KinesisEventSource) -> Sequence[InstructionMsg]\n        stream_arn_varname = '%s_stream_arn' % resource.resource_name\n        uuid_varname = '%s_uuid' % resource.resource_name\n        function_arn = Variable(\n            '%s_lambda_arn' % resource.lambda_function.resource_name\n        )\n        instruction_for_stream_arn = self._arn_parse_instructions(function_arn)\n        instruction_for_stream_arn.append(\n            models.StoreValue(\n                name=stream_arn_varname,\n                value=StringFormat(\n                    'arn:{partition}:kinesis:{region_name}:{account_id}:'\n                    'stream/%s' % resource.stream,\n                    ['partition', 'region_name', 'account_id'],\n                ),\n            )\n        )\n        if self._remote_state.resource_exists(resource):\n            deployed = self._remote_state.resource_deployed_values(resource)\n            uuid = deployed['event_uuid']\n            return instruction_for_stream_arn + [\n                models.APICall(\n                    method_name='update_lambda_event_source',\n                    params={'event_uuid': uuid,\n                            'batch_size': resource.batch_size,\n                            'maximum_batching_window_in_seconds':\n                                resource.maximum_batching_window_in_seconds}\n                )\n            ] + self._batch_record_resource(\n                'kinesis_event', resource.resource_name, {\n                    'kinesis_arn': deployed['kinesis_arn'],\n                    'event_uuid': uuid,\n                    'stream': resource.stream,\n                    'lambda_arn': deployed['lambda_arn'],\n                }\n            )\n        return instruction_for_stream_arn + [\n            (models.APICall(\n                method_name='create_lambda_event_source',\n                params={'event_source_arn': Variable(stream_arn_varname),\n                        'batch_size': resource.batch_size,\n                        'function_name': function_arn,\n                        'starting_position': resource.starting_position,\n                        'maximum_batching_window_in_seconds':\n                            resource.maximum_batching_window_in_seconds},\n                output_var=uuid_varname,\n            ), 'Subscribing %s to Kinesis stream %s\\n'\n                % (resource.lambda_function.function_name, resource.stream)\n            )\n        ] + self._batch_record_resource(\n            'kinesis_event', resource.resource_name, {\n                'kinesis_arn': Variable(stream_arn_varname),\n                'event_uuid': Variable(uuid_varname),\n                'stream': resource.stream,\n                'lambda_arn': Variable(function_arn.name),\n            }\n        )\n\n    def _plan_loggroup(self, resource):\n        # type: (models.LogGroup) -> Sequence[InstructionMsg]\n        instructions = []  # type: List[InstructionMsg]\n        if self._remote_state.resource_exists(resource):\n            return instructions + [\n                models.APICall(\n                    method_name='put_retention_policy',\n                    params={'name': resource.log_group_name,\n                            'retention_in_days': resource.retention_in_days}\n                ),\n                models.RecordResourceValue(\n                    resource_type='log_group',\n                    resource_name=resource.resource_name,\n                    name='log_group_name',\n                    value=resource.log_group_name,\n                )\n            ]\n        return instructions + [\n            models.APICall(\n                method_name='create_log_group',\n                params={'log_group_name': resource.log_group_name}\n            ),\n            models.APICall(\n                method_name='put_retention_policy',\n                params={'name': resource.log_group_name,\n                        'retention_in_days': resource.retention_in_days}\n            ),\n            models.RecordResourceValue(\n                resource_type='log_group',\n                resource_name=resource.resource_name,\n                name='log_group_name',\n                value=resource.log_group_name,\n            )\n        ]\n\n    def _plan_dynamodbeventsource(self, resource):\n        # type: (models.DynamoDBEventSource) -> Sequence[InstructionMsg]\n        uuid_varname = '%s_uuid' % resource.resource_name\n        function_arn = Variable(\n            '%s_lambda_arn' % resource.lambda_function.resource_name\n        )\n        instructions = []  # type: List[InstructionMsg]\n        if self._remote_state.resource_exists(resource):\n            deployed = self._remote_state.resource_deployed_values(resource)\n            uuid = deployed['event_uuid']\n            return instructions + [\n                models.APICall(\n                    method_name='update_lambda_event_source',\n                    params={'event_uuid': uuid,\n                            'batch_size': resource.batch_size,\n                            'maximum_batching_window_in_seconds':\n                                resource.maximum_batching_window_in_seconds}\n                )\n            ] + self._batch_record_resource(\n                'dynamodb_event', resource.resource_name, {\n                    'stream_arn': deployed['stream_arn'],\n                    'event_uuid': deployed['event_uuid'],\n                    'lambda_arn': deployed['lambda_arn'],\n                }\n            )\n        return instructions + [\n            (models.APICall(\n                method_name='create_lambda_event_source',\n                params={'event_source_arn': resource.stream_arn,\n                        'batch_size': resource.batch_size,\n                        'function_name': function_arn,\n                        'starting_position': resource.starting_position,\n                        'maximum_batching_window_in_seconds':\n                            resource.maximum_batching_window_in_seconds},\n                output_var=uuid_varname,\n            ), 'Subscribing %s to DynamoDB stream %s\\n'\n                % (resource.lambda_function.function_name,\n                   resource.stream_arn))\n        ] + self._batch_record_resource(\n            'dynamodb_event', resource.resource_name, {\n                'stream_arn': resource.stream_arn,\n                'event_uuid': Variable(uuid_varname),\n                'lambda_arn': function_arn,\n            }\n        )\n\n    def _arn_parse_instructions(self, function_arn):\n        # type: (Variable) -> List[InstructionMsg]\n        instruction_for_stream_arn = [\n            models.BuiltinFunction('parse_arn', [function_arn],\n                                   output_var='parsed_lambda_arn'),\n            models.JPSearch('account_id', input_var='parsed_lambda_arn',\n                            output_var='account_id'),\n            models.JPSearch('region', input_var='parsed_lambda_arn',\n                            output_var='region_name'),\n            models.JPSearch('partition', input_var='parsed_lambda_arn',\n                            output_var='partition'),\n        ]  # type: List[InstructionMsg]\n        return instruction_for_stream_arn\n\n    def _plan_s3bucketnotification(self, resource):\n        # type: (models.S3BucketNotification) -> Sequence[InstructionMsg]\n        function_arn = Variable(\n            '%s_lambda_arn' % resource.lambda_function.resource_name\n        )\n        return self._arn_parse_instructions(function_arn) + [\n            models.APICall(\n                method_name='add_permission_for_s3_event',\n                params={'bucket': resource.bucket,\n                        'function_arn': function_arn,\n                        'account_id': Variable('account_id')},\n            ),\n            (models.APICall(\n                method_name='connect_s3_bucket_to_lambda',\n                params={'bucket': resource.bucket,\n                        'function_arn': function_arn,\n                        'prefix': resource.prefix,\n                        'suffix': resource.suffix,\n                        'events': resource.events}\n            ), 'Configuring S3 events in bucket %s to function %s\\n'\n                % (resource.bucket, resource.lambda_function.function_name)\n            ),\n            models.RecordResourceValue(\n                resource_type='s3_event',\n                resource_name=resource.resource_name,\n                name='bucket',\n                value=resource.bucket,\n            ),\n            models.RecordResourceVariable(\n                resource_type='s3_event',\n                resource_name=resource.resource_name,\n                name='lambda_arn',\n                variable_name=function_arn.name,\n            ),\n        ]\n\n    def _create_cloudwatchevent(self, resource):\n        # type: (models.CloudWatchEventBase) -> Sequence[InstructionMsg]\n\n        function_arn = Variable(\n            '%s_lambda_arn' % resource.lambda_function.resource_name\n        )\n\n        params = {'rule_name': resource.rule_name}\n        if isinstance(resource, models.ScheduledEvent):\n            resource = cast(models.ScheduledEvent, resource)\n            params['schedule_expression'] = resource.schedule_expression\n            if resource.rule_description is not None:\n                params['rule_description'] = resource.rule_description\n        else:\n            resource = cast(models.CloudWatchEvent, resource)\n            params['event_pattern'] = resource.event_pattern\n\n        plan = [\n            models.APICall(\n                method_name='get_or_create_rule_arn',\n                params=params,\n                output_var='rule-arn',\n            ),\n            models.APICall(\n                method_name='connect_rule_to_lambda',\n                params={'rule_name': resource.rule_name,\n                        'function_arn': function_arn}\n            ),\n            models.APICall(\n                method_name='add_permission_for_cloudwatch_event',\n                params={'rule_arn': Variable('rule-arn'),\n                        'function_arn': function_arn},\n            ),\n            # You need to remove targets (which have IDs)\n            # before you can delete a rule.\n            models.RecordResourceValue(\n                resource_type='cloudwatch_event',\n                resource_name=resource.resource_name,\n                name='rule_name',\n                value=resource.rule_name,\n            )\n        ]\n        return plan\n\n    def _plan_cloudwatchevent(self, resource):\n        # type: (models.CloudWatchEvent) -> Sequence[InstructionMsg]\n        return self._create_cloudwatchevent(resource)\n\n    def _plan_scheduledevent(self, resource):\n        # type: (models.ScheduledEvent) -> Sequence[InstructionMsg]\n        return self._create_cloudwatchevent(resource)\n\n    def _create_websocket_function_configs(self, resource):\n        # type: (models.WebsocketAPI) -> Dict[str, Dict[str, Any]]\n        configs = OrderedDict()  # type: Dict[str, Dict[str, Any]]\n        if resource.connect_function is not None:\n            configs['connect'] = self._create_websocket_function_config(\n                resource.connect_function)\n        if resource.message_function is not None:\n            configs['message'] = self._create_websocket_function_config(\n                resource.message_function)\n        if resource.disconnect_function is not None:\n            configs['disconnect'] = self._create_websocket_function_config(\n                resource.disconnect_function)\n        return configs\n\n    def _create_websocket_function_config(self, function):\n        # type: (models.LambdaFunction) -> Dict[str, Any]\n        varname = '%s_lambda_arn' % function.resource_name\n        return {\n            'function': function,\n            'name': function.function_name,\n            'varname': varname,\n            'lambda_arn_var': Variable(varname),\n        }\n\n    def _inject_websocket_integrations(self, configs):\n        # type: (Dict[str, Any]) -> Sequence[InstructionMsg]\n        instructions = []  # type: List[InstructionMsg]\n        for key, config in configs.items():\n            instructions.append(\n                models.StoreValue(\n                    name='websocket-%s-integration-lambda-path' % key,\n                    value=StringFormat(\n                        'arn:{partition}:apigateway:{region_name}:lambda:path/'\n                        '2015-03-31/functions/arn:{partition}'\n                        ':lambda:{region_name}:{account_id}:function'\n                        ':%s/invocations' % config['name'],\n                        ['partition', 'region_name', 'account_id'],\n                    ),\n                ),\n            )\n            instructions.append(\n                models.APICall(\n                    method_name='create_websocket_integration',\n                    params={\n                        'api_id': Variable('websocket_api_id'),\n                        'lambda_function': Variable(\n                            'websocket-%s-integration-lambda-path' % key),\n                        'handler_type': key,\n                    },\n                    output_var='%s-integration-id' % key,\n                ),\n            )\n        return instructions\n\n    def _create_route_for_key(self, route_key):\n        # type: (str) -> models.APICall\n        integration_id = {\n            '$connect': 'connect-integration-id',\n            '$disconnect': 'disconnect-integration-id',\n        }.get(route_key, 'message-integration-id')\n        return models.APICall(\n            method_name='create_websocket_route',\n            params={\n                'api_id': Variable('websocket_api_id'),\n                'route_key': route_key,\n                'integration_id': Variable(integration_id),\n            },\n        )\n\n    def _plan_websocketapi(self, resource):\n        # type: (models.WebsocketAPI) -> Sequence[InstructionMsg]\n        configs = self._create_websocket_function_configs(resource)\n        routes = resource.routes\n\n        # Which lambda function we use here does not matter. We are only using\n        # it to find the account id and the region.\n        lambda_arn_var = list(configs.values())[0]['lambda_arn_var']\n        shared_plan_preamble = self._arn_parse_instructions(lambda_arn_var) + [\n            models.JPSearch('dns_suffix',\n                            input_var='parsed_lambda_arn',\n                            output_var='dns_suffix'),\n        ]  # type: List[InstructionMsg]\n\n        # There's also a set of instructions that are needed\n        # at the end of deploying a websocket API that apply to both\n        # the update and create case.\n        shared_plan_epilogue = [\n            models.StoreValue(\n                name='websocket_api_url',\n                value=StringFormat(\n                    'wss://{websocket_api_id}.execute-api.{region_name}'\n                    '.{dns_suffix}/%s/' % resource.api_gateway_stage,\n                    ['websocket_api_id', 'region_name', 'dns_suffix'],\n                ),\n            ),\n            models.RecordResourceVariable(\n                resource_type='websocket_api',\n                resource_name=resource.resource_name,\n                name='websocket_api_url',\n                variable_name='websocket_api_url',\n            ),\n            models.RecordResourceVariable(\n                resource_type='websocket_api',\n                resource_name=resource.resource_name,\n                name='websocket_api_id',\n                variable_name='websocket_api_id',\n            ),\n        ]  # type: List[InstructionMsg]\n\n        shared_plan_epilogue += [\n            models.APICall(\n                method_name='add_permission_for_apigateway_v2',\n                params={'function_name': function_config['name'],\n                        'region_name': Variable('region_name'),\n                        'account_id': Variable('account_id'),\n                        'api_id': Variable('websocket_api_id')},\n            ) for function_config in configs.values()\n        ]\n\n        main_plan = []  # type: List[InstructionMsg]\n        if not self._remote_state.resource_exists(resource):\n            # The resource does not exist, we create it in full here.\n            main_plan += [\n                (models.APICall(\n                    method_name='create_websocket_api',\n                    params={'name': resource.name},\n                    output_var='websocket_api_id',\n                ), \"Creating websocket api: %s\\n\" % resource.name),\n                models.StoreValue(\n                    name='routes',\n                    value=[],\n                ),\n            ]\n            main_plan += self._inject_websocket_integrations(configs)\n\n            for route_key in routes:\n                main_plan += [self._create_route_for_key(route_key)]\n            main_plan += [\n                models.APICall(\n                    method_name='deploy_websocket_api',\n                    params={\n                        'api_id': Variable('websocket_api_id'),\n                    },\n                    output_var='deployment-id',\n                ),\n                models.APICall(\n                    method_name='create_stage',\n                    params={\n                        'api_id': Variable('websocket_api_id'),\n                        'stage_name': resource.api_gateway_stage,\n                        'deployment_id': Variable('deployment-id'),\n                    }\n                ),\n            ]\n        else:\n            # Already exists. Need to sync up the routes, the easiest way to do\n            # this is to delete them and their integrations and re-create them.\n            # They will not work if the lambda function changes from under\n            # them, and the logic for detecting that and making just the needed\n            # changes is complex. There is an integration test to ensure there\n            # no dropped messages during a redeployment.\n            deployed = self._remote_state.resource_deployed_values(resource)\n            main_plan += [\n                models.StoreValue(\n                    name='websocket_api_id',\n                    value=deployed['websocket_api_id']\n                ),\n                models.APICall(\n                    method_name='get_websocket_routes',\n                    params={'api_id': Variable('websocket_api_id')},\n                    output_var='routes',\n                ),\n                models.APICall(\n                    method_name='delete_websocket_routes',\n                    params={\n                        'api_id': Variable('websocket_api_id'),\n                        'routes': Variable('routes'),\n                    },\n                ),\n                models.APICall(\n                    method_name='get_websocket_integrations',\n                    params={\n                        'api_id': Variable('websocket_api_id'),\n                    },\n                    output_var='integrations'\n                ),\n                models.APICall(\n                    method_name='delete_websocket_integrations',\n                    params={\n                        'api_id': Variable('websocket_api_id'),\n                        'integrations': Variable('integrations'),\n                    }\n                )\n            ]\n            main_plan += self._inject_websocket_integrations(configs)\n            for route_key in routes:\n                main_plan += [self._create_route_for_key(route_key)]\n\n        ws_plan = shared_plan_preamble + main_plan + shared_plan_epilogue\n\n        if resource.domain_name:\n            custom_domain_plan = self._add_custom_domain_plan(\n                resource.domain_name, 'REGIONAL',\n            )\n            ws_plan += custom_domain_plan\n\n        return ws_plan\n\n    def _plan_restapi(self, resource):\n        # type: (models.RestAPI) -> Sequence[InstructionMsg]\n        function = resource.lambda_function\n        function_name = function.function_name\n        varname = '%s_lambda_arn' % function.resource_name\n        lambda_arn_var = Variable(varname)\n        # There's a set of shared instructions that are needed\n        # in both the update as well as the initial create case.\n        # That's what this shared_plan_premable is for.\n        shared_plan_preamble = self._arn_parse_instructions(lambda_arn_var) + [\n            models.JPSearch('dns_suffix',\n                            input_var='parsed_lambda_arn',\n                            output_var='dns_suffix'),\n            # The swagger doc uses the 'api_handler_lambda_arn'\n            # var name so we need to make sure we populate this variable\n            # before importing the rest API.\n            models.CopyVariable(from_var=varname,\n                                to_var='api_handler_lambda_arn'),\n        ]  # type: List[InstructionMsg]\n        # There's also a set of instructions that are needed\n        # at the end of deploying a rest API that apply to both\n        # the update and create case.\n        shared_plan_patch_ops = [{\n            'op': 'replace',\n            'path': '/minimumCompressionSize',\n            'value': resource.minimum_compression}\n        ]  # type: List[Dict]\n\n        shared_plan_epilogue = [\n            models.APICall(\n                method_name='update_rest_api',\n                params={\n                    'rest_api_id': Variable('rest_api_id'),\n                    'patch_operations': shared_plan_patch_ops\n                }\n            ),\n            models.APICall(\n                method_name='add_permission_for_apigateway',\n                params={'function_name': function_name,\n                        'region_name': Variable('region_name'),\n                        'account_id': Variable('account_id'),\n                        'rest_api_id': Variable('rest_api_id')},\n            ),\n            models.APICall(\n                method_name='deploy_rest_api',\n                params={'rest_api_id': Variable('rest_api_id'),\n                        'xray': resource.xray,\n                        'api_gateway_stage': resource.api_gateway_stage},\n            ),\n            models.StoreValue(\n                name='rest_api_url',\n                value=StringFormat(\n                    'https://{rest_api_id}.execute-api.{region_name}'\n                    '.{dns_suffix}/%s/' % resource.api_gateway_stage,\n                    ['rest_api_id', 'region_name', 'dns_suffix'],\n                ),\n            ),\n            models.RecordResourceVariable(\n                resource_type='rest_api',\n                resource_name=resource.resource_name,\n                name='rest_api_url',\n                variable_name='rest_api_url',\n            ),\n        ]  # type: List[InstructionMsg]\n        for auth in resource.authorizers:\n            shared_plan_epilogue.append(\n                models.APICall(\n                    method_name='add_permission_for_apigateway',\n                    params={'function_name': auth.function_name,\n                            'region_name': Variable('region_name'),\n                            'account_id': Variable('account_id'),\n                            'rest_api_id': Variable('rest_api_id')},\n                )\n            )\n        if not self._remote_state.resource_exists(resource):\n            plan = shared_plan_preamble + [\n                (models.APICall(\n                    method_name='import_rest_api',\n                    params={'swagger_document': resource.swagger_doc,\n                            'endpoint_type': resource.endpoint_type},\n                    output_var='rest_api_id',\n                ), \"Creating Rest API\\n\"),\n                models.RecordResourceVariable(\n                    resource_type='rest_api',\n                    resource_name=resource.resource_name,\n                    name='rest_api_id',\n                    variable_name='rest_api_id',\n                ),\n            ]\n        else:\n            deployed = self._remote_state.resource_deployed_values(resource)\n            shared_plan_epilogue.insert(\n                0,\n                models.APICall(\n                    method_name='get_rest_api',\n                    params={'rest_api_id': Variable('rest_api_id')},\n                    output_var='rest_api')\n            )\n            shared_plan_patch_ops.append({\n                'op': 'replace',\n                'path': StringFormat(\n                    '/endpointConfiguration/types/%s' % (\n                        '{rest_api[endpointConfiguration][types][0]}'),\n                    ['rest_api']),\n                'value': resource.endpoint_type}\n            )\n            plan = shared_plan_preamble + [\n                models.StoreValue(\n                    name='rest_api_id',\n                    value=deployed['rest_api_id']),\n                models.RecordResourceVariable(\n                    resource_type='rest_api',\n                    resource_name=resource.resource_name,\n                    name='rest_api_id',\n                    variable_name='rest_api_id',\n                ),\n                (models.APICall(\n                    method_name='update_api_from_swagger',\n                    params={\n                        'rest_api_id': Variable('rest_api_id'),\n                        'swagger_document': resource.swagger_doc,\n                    },\n                ), \"Updating rest API\\n\"),\n            ]\n\n        plan.extend(shared_plan_epilogue)\n\n        if resource.domain_name:\n            custom_domain_plan = self._add_custom_domain_plan(\n                resource.domain_name, resource.endpoint_type\n            )\n            plan += custom_domain_plan\n        return plan\n\n    def _add_custom_domain_plan(self, resource, endpoint_type):\n        # type: (models.DomainName, str) -> Sequence[InstructionMsg]\n        result = []  # type: List[InstructionMsg]\n        custom_domain_plan = self._add_domainname_plan(\n            resource, endpoint_type\n        )\n        result += custom_domain_plan\n        api_mapping_plan = self._add_apimapping_plan(\n            resource.api_mapping, resource\n        )\n        result += api_mapping_plan\n        return result\n\n    def _get_role_arn(self, resource):\n        # type: (models.IAMRole) -> Union[str, Variable]\n        if isinstance(resource, models.PreCreatedIAMRole):\n            return resource.role_arn\n        elif isinstance(resource, models.ManagedIAMRole):\n            return Variable('%s_role_arn' % resource.role_name)\n        # Make mypy happy.\n        raise RuntimeError(\"Unknown resource type: %s\" % resource)\n\n    def _batch_record_resource(self, resource_type, resource_name,\n                               mapping):\n        # type: (str, str, Dict[str, Any]) -> List[InstructionMsg]\n        # This is a helper function for recording multiple values into\n        # the same resource dict.  The mapping is the set of variables\n        # you want to record.  If the value in a pair is a Variable type,\n        # then RecordResourceVariable is used, otherwise, RecordResourceValue\n        # is used.\n        instructions = []  # type: List[InstructionMsg]\n        for key, value in mapping.items():\n            instruction = cast(InstructionMsg, None)\n            if isinstance(value, Variable):\n                instruction = models.RecordResourceVariable(\n                    resource_type=resource_type,\n                    resource_name=resource_name,\n                    name=key,\n                    variable_name=value.name\n                )\n            else:\n                instruction = models.RecordResourceValue(\n                    resource_type=resource_type,\n                    resource_name=resource_name,\n                    name=key,\n                    value=value\n                )\n            instructions.append(instruction)\n        return instructions\n\n\nclass NoopPlanner(PlanStage):\n    def __init__(self):\n        # type: () -> None\n        pass\n\n    def execute(self, resources):\n        # type: (List[models.Model]) -> models.Plan\n        return models.Plan(instructions=[], messages={})\n\n\nclass Variable(object):\n    def __init__(self, name):\n        # type: (str) -> None\n        self.name = name\n\n    def __repr__(self):\n        # type: () -> str\n        return 'Variable(\"%s\")' % self.name\n\n    def __eq__(self, other):\n        # type: (Any) -> bool\n        return isinstance(other, Variable) and self.name == other.name\n\n\nclass StringFormat(object):\n    def __init__(self, template, variables):\n        # type: (str, List[str]) -> None\n        self.template = template\n        self.variables = variables\n\n    def __repr__(self):\n        # type: () -> str\n        return 'StringFormat(\"%s\")' % self.template\n\n    def __eq__(self, other):\n        # type: (Any) -> bool\n        return (\n            isinstance(other, StringFormat) and\n            self.template == other.template and\n            self.variables == other.variables\n        )\n\n\nclass PlanEncoder(json.JSONEncoder):\n    # pylint false positive overriden below\n    # https://github.com/PyCQA/pylint/issues/414\n    def default(self, o):  # pylint: disable=E0202\n        # type: (Any) -> Any\n        if isinstance(o, StringFormat):\n            return o.template\n        return o\n\n\nclass KeyDataVariable(object):\n    def __init__(self, name, key):\n        # type: (str, str) -> None\n        self.name = name\n        self.key = key\n\n    def __repr__(self):\n        # type: () -> str\n        return 'KeyDataVariable(\"%s\", \"%s\")' % (self.name, self.key)\n\n    def __eq__(self, other):\n        # type: (Any) -> bool\n        return (\n            isinstance(other, KeyDataVariable) and\n            self.name == other.name and\n            self.key == other.key\n        )\n"
  },
  {
    "path": "chalice/deploy/swagger.py",
    "content": "import copy\nimport inspect\n\nfrom typing import Any, List, Dict, Optional, Union  # noqa\n\nfrom chalice.app import Chalice, RouteEntry, Authorizer, CORSConfig  # noqa\nfrom chalice.app import ChaliceAuthorizer\nfrom chalice.deploy.planner import StringFormat\nfrom chalice.deploy.models import RestAPI  # noqa\nfrom chalice.utils import to_cfn_resource_name\n\n\nclass SwaggerGenerator(object):\n\n    _BASE_TEMPLATE = {\n        'swagger': '2.0',\n        'info': {\n            'version': '1.0',\n            'title': ''\n        },\n        'schemes': ['https'],\n        'paths': {},\n        'definitions': {\n            'Empty': {\n                'type': 'object',\n                'title': 'Empty Schema',\n            }\n        }\n    }  # type: Dict[str, Any]\n\n    def __init__(self, region, deployed_resources):\n        # type: (str, Dict[str, Any]) -> None\n        self._region = region\n        self._deployed_resources = deployed_resources\n\n    def generate_swagger(self, app, rest_api=None):\n        # type: (Chalice, Optional[RestAPI]) -> Dict[str, Any]\n        api = copy.deepcopy(self._BASE_TEMPLATE)\n        api['info']['title'] = app.app_name\n        self._add_binary_types(api, app)\n        self._add_route_paths(api, app)\n        self._add_resource_policy(api, rest_api)\n        self._add_vpc_endpoint(api, rest_api)\n        return api\n\n    def _add_resource_policy(self, api, rest_api):\n        # type: (Dict[str, Any], Optional[RestAPI]) -> None\n        if rest_api and rest_api.policy:\n            api['x-amazon-apigateway-policy'] = rest_api.policy.document\n\n    def _add_vpc_endpoint(self, api, rest_api):\n        # type: (Dict[str, Any], Optional[RestAPI]) -> None\n        if rest_api and rest_api.vpce_ids:\n            api['x-amazon-apigateway-endpoint-configuration'] = {\n                \"vpcEndpointIds\": rest_api.vpce_ids\n            }\n\n    def _add_binary_types(self, api, app):\n        # type: (Dict[str, Any], Chalice) -> None\n        api['x-amazon-apigateway-binary-media-types'] = app.api.binary_types\n\n    def _add_route_paths(self, api, app):\n        # type: (Dict[str, Any], Chalice) -> None\n        for path, methods in app.routes.items():\n            swagger_for_path = {}  # type: Dict[str, Any]\n            api['paths'][path] = swagger_for_path\n\n            cors_config = None\n            methods_with_cors = []\n            for http_method, view in methods.items():\n                current = self._generate_route_method(view)\n                if 'security' in current:\n                    self._add_to_security_definition(\n                        current['security'], api, view)\n                swagger_for_path[http_method.lower()] = current\n                if view.cors is not None:\n                    cors_config = view.cors\n                    methods_with_cors.append(http_method)\n\n            # Chalice ensures that routes with multiple views have the same\n            # CORS configuration. So if any entry has CORS enabled, use that\n            # entry's CORS configuration for the preflight setup.\n            if cors_config is not None:\n                self._add_preflight_request(\n                    cors_config, methods_with_cors, swagger_for_path)\n\n    def _generate_security_from_auth_obj(self, api_config, authorizer):\n        # type: (Dict[str, Any], Authorizer) -> None\n        if isinstance(authorizer, ChaliceAuthorizer):\n            auth_config = authorizer.config\n            config = {\n                'in': 'header',\n                'type': 'apiKey',\n                'name': auth_config.header,\n                'x-amazon-apigateway-authtype': 'custom'\n            }\n            api_gateway_authorizer = {\n                'type': 'token',\n                'authorizerUri': self._auth_uri(authorizer)\n            }\n            if auth_config.execution_role is not None:\n                api_gateway_authorizer['authorizerCredentials'] = \\\n                    auth_config.execution_role\n            if auth_config.ttl_seconds is not None:\n                api_gateway_authorizer['authorizerResultTtlInSeconds'] = \\\n                    auth_config.ttl_seconds\n            config['x-amazon-apigateway-authorizer'] = api_gateway_authorizer\n        else:\n            config = authorizer.to_swagger()\n        api_config.setdefault(\n            'securityDefinitions', {})[authorizer.name] = config\n\n    def _auth_uri(self, authorizer):\n        # type: (ChaliceAuthorizer) -> str\n        function_name = '%s-%s' % (\n            self._deployed_resources['api_handler_name'],\n            authorizer.config.name\n        )\n        return self._uri(\n            self._deployed_resources['lambda_functions'][function_name]['arn'])\n\n    def _add_to_security_definition(self, security,\n                                    api_config, view):\n        # type: (Any, Dict[str, Any], RouteEntry) -> None\n        if view.authorizer is not None:\n            self._generate_security_from_auth_obj(api_config, view.authorizer)\n        for auth in security:\n            name = list(auth.keys())[0]\n            if name == 'api_key':\n                # This is just the api_key_required=True config\n                swagger_snippet = {\n                    'type': 'apiKey',\n                    'name': 'x-api-key',\n                    'in': 'header',\n                }  # type: Dict[str, Any]\n                api_config.setdefault(\n                    'securityDefinitions', {})[name] = swagger_snippet\n\n    def _generate_route_method(self, view):\n        # type: (RouteEntry) -> Dict[str, Any]\n        current = {\n            'consumes': view.content_types,\n            'produces': ['application/json'],\n            'responses': self._generate_precanned_responses(),\n            'x-amazon-apigateway-integration': self._generate_apig_integ(\n                view),\n        }  # type: Dict[str, Any]\n        docstring = inspect.getdoc(view.view_function)\n        if docstring:\n            doc_lines = docstring.splitlines()\n            current['summary'] = doc_lines[0]\n            if len(doc_lines) > 1:\n                current['description'] = '\\n'.join(doc_lines[1:]).strip('\\n')\n        if view.api_key_required:\n            # When this happens we also have to add the relevant portions\n            # to the security definitions.  We have to someone indicate\n            # this because this neeeds to be added to the global config\n            # file.\n            current.setdefault('security', []).append({'api_key': []})\n        if view.authorizer:\n            current.setdefault('security', []).append(\n                {view.authorizer.name: view.authorizer.scopes})\n        if view.view_args:\n            self._add_view_args(current, view.view_args)\n        return current\n\n    def _generate_precanned_responses(self):\n        # type: () -> Dict[str, Any]\n        responses = {\n            '200': {\n                'description': '200 response',\n                'schema': {\n                    '$ref': '#/definitions/Empty',\n                }\n            }\n        }\n        return responses\n\n    def _uri(self, lambda_arn=None):\n        # type: (Optional[str]) -> Any\n        if lambda_arn is None:\n            lambda_arn = self._deployed_resources['api_handler_arn']\n        partition = lambda_arn.split(':')[1]\n        return ('arn:{partition}:apigateway:{region}:lambda:path/2015-03-31'\n                '/functions/{lambda_arn}/invocations').format(\n                    partition=partition, region=self._region,\n                    lambda_arn=lambda_arn)\n\n    def _generate_apig_integ(self, view):\n        # type: (RouteEntry) -> Dict[str, Any]\n        apig_integ = {\n            'responses': {\n                'default': {\n                    'statusCode': \"200\",\n                }\n            },\n            'uri': self._uri(),\n            'passthroughBehavior': 'when_no_match',\n            'httpMethod': 'POST',\n            'contentHandling': 'CONVERT_TO_TEXT',\n            'type': 'aws_proxy',\n        }\n        return apig_integ\n\n    def _add_view_args(self, single_method, view_args):\n        # type: (Dict[str, Any], List[str]) -> None\n        single_method['parameters'] = [\n            {'name': name, 'in': 'path', 'required': True, 'type': 'string'}\n            for name in view_args\n        ]\n\n    def _add_preflight_request(self, cors, methods, swagger_for_path):\n        # type: (CORSConfig, List[str], Dict[str, Any]) -> None\n        methods = methods + ['OPTIONS']\n        allowed_methods = ','.join(methods)\n\n        response_params = {\n            'Access-Control-Allow-Methods': '%s' % allowed_methods\n        }\n        response_params.update(cors.get_access_control_headers())\n\n        headers = {k: {'type': 'string'} for k, _ in response_params.items()}\n        response_params = {'method.response.header.%s' % k: \"'%s'\" % v for k, v\n                           in response_params.items()}\n\n        options_request = {\n            \"consumes\": [\"application/json\"],\n            \"produces\": [\"application/json\"],\n            \"responses\": {\n                \"200\": {\n                    \"description\": \"200 response\",\n                    \"schema\": {\"$ref\": \"#/definitions/Empty\"},\n                    \"headers\": headers\n                }\n            },\n            \"x-amazon-apigateway-integration\": {\n                \"responses\": {\n                    \"default\": {\n                        \"statusCode\": \"200\",\n                        \"responseParameters\": response_params,\n                    }\n                },\n                \"requestTemplates\": {\n                    \"application/json\": \"{\\\"statusCode\\\": 200}\"\n                },\n                \"passthroughBehavior\": \"when_no_match\",\n                \"type\": \"mock\",\n                \"contentHandling\": \"CONVERT_TO_TEXT\"\n            }\n        }\n        swagger_for_path['options'] = options_request\n\n\nclass CFNSwaggerGenerator(SwaggerGenerator):\n    def __init__(self):\n        # type: () -> None\n        pass\n\n    def _uri(self, lambda_arn=None):\n        # type: (Optional[str]) -> Any\n        return {\n            'Fn::Sub': (\n                'arn:${AWS::Partition}:apigateway:${AWS::Region}'\n                ':lambda:path/2015-03-31'\n                '/functions/${APIHandler.Arn}/invocations'\n            )\n        }\n\n    def _auth_uri(self, authorizer):\n        # type: (ChaliceAuthorizer) -> Any\n        return {\n            'Fn::Sub': (\n                'arn:${AWS::Partition}:apigateway:${AWS::Region}'\n                ':lambda:path/2015-03-31'\n                '/functions/${%s.Arn}/invocations' % to_cfn_resource_name(\n                    authorizer.name)\n            )\n        }\n\n\nclass TemplatedSwaggerGenerator(SwaggerGenerator):\n    def __init__(self):\n        # type: () -> None\n        pass\n\n    def _uri(self, lambda_arn=None):\n        # type: (Optional[str]) -> Any\n        return StringFormat(\n            'arn:{partition}:apigateway:{region_name}:lambda:path/2015-03-31'\n            '/functions/{api_handler_lambda_arn}/invocations',\n            ['partition', 'region_name', 'api_handler_lambda_arn'],\n        )\n\n    def _auth_uri(self, authorizer):\n        # type: (ChaliceAuthorizer) -> Any\n        varname = '%s_lambda_arn' % authorizer.name\n        return StringFormat(\n            'arn:{partition}:apigateway:{region_name}:lambda:path/2015-03-31'\n            '/functions/{%s}/invocations' % varname,\n            ['partition', 'region_name', varname],\n        )\n\n\nclass TerraformSwaggerGenerator(SwaggerGenerator):\n\n    def __init__(self):\n        # type: () -> None\n        pass\n\n    def _uri(self, lambda_arn=None):\n        # type: (Optional[str]) -> Any\n        return '${aws_lambda_function.api_handler.invoke_arn}'\n\n    def _auth_uri(self, authorizer):\n        # type: (ChaliceAuthorizer) -> Any\n        return '${aws_lambda_function.%s.invoke_arn}' % (authorizer.name)\n"
  },
  {
    "path": "chalice/deploy/sweeper.py",
    "content": "from typing import ( # noqa\n    List,\n    Dict,\n    Optional,\n    Tuple,\n    Any,\n    Union,\n    Sequence,\n    cast,\n    NoReturn,\n)\n\nfrom chalice.config import Config, DeployedResources  # noqa\nfrom chalice.deploy import models\nfrom chalice.deploy.planner import Variable\nfrom chalice.deploy.models import Instruction, StoreMultipleValue  # noqa\n\nMarkedResource = Dict[str, List[models.RecordResource]]\nResourceValueType = Dict[str, Union[Sequence[Instruction], str]]\nHandlerArgsType = List[Union[Dict[str, Any], str]]\n\n\nclass ResourceSweeper(object):\n\n    specific_resources = (\n        's3_event',\n        'sns_event',\n        'sqs_event',\n        'kinesis_event',\n        'dynamodb_event',\n        'domain_name'\n    )\n\n    def __init__(self):\n        # type: () -> None\n        self.plan = models.Plan()\n        self.marked = {}  # type: Dict\n\n    def execute(self, plan, config):\n        # type: (models.Plan, Config) -> None\n        self.plan = plan\n        self.marked = self._mark_resources()\n\n        deployed = config.deployed_resources(config.chalice_stage)\n        if deployed is not None:\n            remaining = self._determine_remaining(deployed)\n            self._plan_deletion(remaining, deployed)\n\n    def _determine_sns_event(self, name, resource_values):\n        # type: (str, Dict[str, str]) -> Optional[str]\n        existing_topic = resource_values['topic']\n        referenced_topic = [instruction for instruction in self.marked[name]\n                            if instruction.name == 'topic' and\n                            isinstance(instruction,\n                                       models.RecordResourceValue)][0]\n        if referenced_topic.value != existing_topic:\n            return name\n        return None\n\n    def _determine_s3_event(self, name, resource_values):\n        # type: (str, Dict[str, str]) -> Optional[str]\n        # Special case, we have to check the resource values\n        # to see if they've changed.  For s3 events, the resource\n        # name is not tied to the bucket, which means if you change\n        # the bucket, the resource name will stay the same.\n        # So we match up the bucket referenced in the instruction\n        # and the bucket recorded in the deployed values match up.\n        # If they don't then we need to clean up the bucket config\n        # referenced in the deployed values.\n        bucket = [instruction for instruction in self.marked[name]\n                  if instruction.name == 'bucket' and\n                  isinstance(instruction,\n                             models.RecordResourceValue)][0]\n        if bucket.value != resource_values['bucket']:\n            return name\n        return None\n\n    def _determine_sqs_event(self, name, resource_values):\n        # type: (str, Dict[str, str]) -> Optional[str]\n        existing_queue = resource_values['queue']\n        referenced_queue = [instruction for instruction in self.marked[name]\n                            if instruction.name == 'queue' and\n                            isinstance(instruction,\n                                       models.RecordResourceValue)][0]\n        if referenced_queue.value != existing_queue:\n            return name\n        return None\n\n    def _determine_kinesis_event(self, name, resource_values):\n        # type: (str, Dict[str, str]) -> Optional[str]\n        existing_stream = resource_values['stream']\n        referenced_stream = [instruction for instruction in self.marked[name]\n                             if instruction.name == 'stream' and\n                             isinstance(instruction,\n                                        models.RecordResourceValue)][0]\n        if referenced_stream.value != existing_stream:\n            return name\n        return None\n\n    def _determine_dynamodb_event(self, name, resource_values):\n        # type: (str, Dict[str, str]) -> Optional[str]\n        existing_stream_arn = resource_values['stream_arn']\n        referenced_stream = [instruction for instruction in self.marked[name]\n                             if instruction.name == 'stream_arn' and\n                             isinstance(instruction,\n                                        models.RecordResourceValue)][0]\n        if referenced_stream.value != existing_stream_arn:\n            return name\n        return None\n\n    def _determine_domain_name(self, name, resource_values):\n        # type: (str, Dict[str, Any]) -> Optional[List[str]]\n        api_mapping = resource_values.get('api_mapping')\n        if not api_mapping:\n            return None\n\n        deployed_api_mappings_ids = {\n            api_map['key']\n            for api_map in api_mapping\n        }\n        api_mapping_data = (\n            'rest_api_mapping',\n            'websocket_api_mapping'\n        )\n\n        instructions = self.plan.instructions\n\n        planned_api_mappings_ids = {\n            instr.value[0]['key']\n            for instr in instructions\n            if isinstance(instr, StoreMultipleValue) and\n            (instr.name in api_mapping_data and\n                isinstance(instr.value[0], dict))\n        }\n\n        api_mappings_to_remove = list(\n            deployed_api_mappings_ids - planned_api_mappings_ids\n        )\n\n        result_api_mappings = [\n            \"%s.api_mapping.%s\" % (name, api_map)\n            for api_map in api_mappings_to_remove\n        ]\n        return result_api_mappings\n\n    def _determine_remaining(self, deployed):\n        # type: (DeployedResources) -> List[str]\n        remaining = []\n        deployed_resource_names = reversed(deployed.resource_names())\n\n        for name in deployed_resource_names:\n            resource_values = deployed.resource_values(name)\n            if name not in self.marked:\n                remaining.append(name)\n            elif resource_values['resource_type'] in self.specific_resources:\n                method = '_determine_%s' % resource_values['resource_type']\n                handler = getattr(self, method)\n                resource_name = handler(name, resource_values)\n                if resource_name:\n                    if isinstance(resource_name, list):\n                        remaining.extend(resource_name)\n                    else:\n                        remaining.append(resource_name)\n        return remaining\n\n    def _mark_resources(self):\n        # type: () -> MarkedResource\n        marked = {}  # type: MarkedResource\n        for instruction in self.plan.instructions:\n            if isinstance(instruction, models.RecordResource):\n                marked.setdefault(instruction.resource_name, []).append(\n                    instruction)\n        return marked\n\n    def _delete_domain_name(self,\n                            resource_values  # type: Dict[str, Any]\n                            ):\n        # type: (...) -> ResourceValueType\n        params = {\n            'domain_name': resource_values['domain_name']\n        }\n        msg = 'Deleting custom domain name: %s\\n' % resource_values['name']\n        return {\n            'instructions': (\n                models.APICall(\n                    method_name='delete_domain_name',\n                    params=params,\n                ),\n            ),\n            'message': msg\n        }\n\n    def _delete_api_mapping(self,\n                            domain_name,   # type: str\n                            api_mapping    # type: Dict[str, Any]\n                            ):\n        # type: (...) -> ResourceValueType\n        if api_mapping['key'] == '/':\n            path_key = '(none)'\n        else:\n            path_key = api_mapping['key'].lstrip(\"/\")\n\n        params = {\n            'domain_name': domain_name,\n            'path_key': path_key\n        }\n        msg = 'Deleting base path mapping from %s custom domain name: %s\\n' % (\n            domain_name, api_mapping['key']\n        )\n        return {\n            'instructions': (\n                models.APICall(\n                    method_name='delete_api_mapping',\n                    params=params,\n                ),\n            ),\n            'message': msg\n        }\n\n    def _delete_lambda_function(self,\n                                resource_values  # type: Dict[str, Any]\n                                ):\n        # type: (...) -> ResourceValueType\n        msg = 'Deleting function: %s\\n' % resource_values['lambda_arn']\n        return {\n            'instructions': (\n                models.APICall(\n                    method_name='delete_function',\n                    params={'function_name': resource_values['lambda_arn']},\n                ),\n            ),\n            'message': msg\n        }\n\n    def _delete_log_group(self,\n                          resource_values  # type: Dict[str, Any]\n                          ):\n        # type: (...) -> ResourceValueType\n        log_group_name = resource_values['log_group_name']\n        msg = 'Deleting retention policy for log group: %s\\n' % log_group_name\n        return {\n            'instructions': (\n                models.APICall(\n                    method_name='delete_retention_policy',\n                    params={'log_group_name': log_group_name}\n                ),\n            ),\n            'message': msg\n        }\n\n    def _delete_lambda_layer(self, resource_values):\n        # type: (Dict[str, str]) -> ResourceValueType\n        apicall = models.APICall(\n            method_name='delete_layer_version',\n            params={'layer_version_arn': resource_values[\n                'layer_version_arn']})\n        return {\n            'instructions': (apicall,),\n            'message': (\n                \"Deleting layer version: %s\\n\"\n                % resource_values['layer_version_arn']\n            )\n        }\n\n    def _delete_iam_role(self, resource_values):\n        # type: (Dict[str, Any]) -> ResourceValueType\n        return {\n            'instructions': (\n                models.APICall(\n                    method_name='delete_role',\n                    params={'name': resource_values['role_name']},\n                ),\n            ),\n            'message': 'Deleting IAM role: %s\\n' % resource_values['role_name']\n        }\n\n    def _delete_cloudwatch_event(self, resource_values):\n        # type: (Dict[str, Any]) -> ResourceValueType\n        return {\n            'instructions': (\n                models.APICall(\n                    method_name='delete_rule',\n                    params={'rule_name': resource_values['rule_name']},\n                ),\n            )\n        }\n\n    def _delete_rest_api(self, resource_values):\n        # type: (Dict[str, Any]) -> ResourceValueType\n        msg = 'Deleting Rest API: %s\\n' % resource_values['rest_api_id']\n        return {\n            'instructions': (\n                models.APICall(\n                    method_name='delete_rest_api',\n                    params={'rest_api_id': resource_values['rest_api_id']}\n                ),\n            ),\n            'message': msg\n        }\n\n    def _delete_s3_event(self, resource_values):\n        # type: (Dict[str, Any]) -> ResourceValueType\n        bucket = resource_values['bucket']\n        function_arn = resource_values['lambda_arn']\n        return {\n            'instructions': (\n                models.BuiltinFunction('parse_arn', [function_arn],\n                                       output_var='parsed_lambda_arn'),\n                models.JPSearch('account_id', input_var='parsed_lambda_arn',\n                                output_var='account_id'),\n                models.APICall(\n                    method_name='disconnect_s3_bucket_from_lambda',\n                    params={'bucket': bucket, 'function_arn': function_arn}\n                ),\n                models.APICall(\n                    method_name='remove_permission_for_s3_event',\n                    params={'bucket': bucket, 'function_arn': function_arn,\n                            'account_id': Variable('account_id')}\n                ),\n            )\n        }\n\n    def _delete_sns_event(self, resource_values):\n        # type: (Dict[str, Any]) -> ResourceValueType\n        subscription_arn = resource_values['subscription_arn']\n        return {\n            'instructions': (\n                models.APICall(\n                    method_name='unsubscribe_from_topic',\n                    params={'subscription_arn': subscription_arn},\n                ),\n                models.APICall(\n                    method_name='remove_permission_for_sns_topic',\n                    params={\n                        'topic_arn': resource_values['topic_arn'],\n                        'function_arn': resource_values['lambda_arn'],\n                    },\n                ),\n            )\n        }\n\n    def _delete_sqs_event(self, resource_values):\n        # type: (Dict[str, Any]) -> ResourceValueType\n        return {\n            'instructions': (\n                models.APICall(\n                    method_name='remove_lambda_event_source',\n                    params={'event_uuid': resource_values['event_uuid']},\n                ),\n            )\n        }\n\n    def _delete_kinesis_event(self, resource_values):\n        # type: (Dict[str, Any]) -> ResourceValueType\n        return {\n            'instructions': (\n                models.APICall(\n                    method_name='remove_lambda_event_source',\n                    params={'event_uuid': resource_values['event_uuid']},\n                ),\n            )\n        }\n\n    def _delete_dynamodb_event(self, resource_values):\n        # type: (Dict[str, Any]) -> ResourceValueType\n        return {\n            'instructions': (\n                models.APICall(\n                    method_name='remove_lambda_event_source',\n                    params={'event_uuid': resource_values['event_uuid']},\n                ),\n            )\n        }\n\n    def _delete_websocket_api(self, resource_values):\n        # type: (Dict[str, Any]) -> ResourceValueType\n        msg = 'Deleting Websocket API: %s\\n' % (\n            resource_values['websocket_api_id']\n        )\n        return {\n            'instructions': (\n                models.APICall(\n                    method_name='delete_websocket_api',\n                    params={'api_id': resource_values['websocket_api_id']},\n                ),\n            ),\n            'message': msg\n        }\n\n    def _default_delete(self, *resource_values):\n        # type: (Any) -> NoReturn\n        err_msg = \"Sweeper encountered an unknown resource: %s\" % \\\n                  str(resource_values)\n        raise RuntimeError(err_msg)\n\n    def _update_plan(self, instructions, message=None, insert=False):\n        # type: (Tuple[Instruction], Optional[str], bool) -> None\n        if insert:\n            for instruction in instructions:\n                self.plan.instructions.insert(\n                    0, cast(Instruction, instruction)\n                )\n            if message:\n                instr_id = id(self.plan.instructions[0])\n                self.plan.messages[instr_id] = cast(\n                    str, message\n                )\n        else:\n            self.plan.instructions.extend(instructions)\n            if message:\n                self.plan.messages[id(self.plan.instructions[-1])] = message\n\n    def _delete_domain_api_mappings(self, resource_values, name):\n        # type: (Dict[str, Any], str) -> ResourceValueType\n        path_key = name.split('.')[-1]\n\n        api_mapping = {\n            k: v\n            for api_map in resource_values['api_mapping']\n            for k, v in api_map.items()\n            if api_map['key'] == path_key\n        }  # type: Dict[str, str]\n\n        resource_data = self._delete_api_mapping(\n            resource_values['domain_name'],\n            api_mapping\n        )\n        return resource_data\n\n    def _plan_deletion(self,\n                       remaining,  # type: List[str]\n                       deployed,   # type: DeployedResources\n                       ):\n        # type: (...) -> None\n        for name in remaining:\n            resource_values = deployed.resource_values(name)\n\n            resource_type = resource_values['resource_type']\n            handler_args = [resource_values]  # type: HandlerArgsType\n            insert = False\n            if 'api_mapping' in name:\n                resource_type = 'domain_api_mappings'\n                handler_args.append(name)\n                insert = True\n\n            method_name = '_delete_%s' % resource_type\n            handler = getattr(self, method_name, self._default_delete)\n            resource_data = handler(*handler_args)\n            instructions = cast(\n                Tuple[Instruction],\n                resource_data['instructions']\n            )\n            message = cast(Optional[str], resource_data.get('message'))\n            self._update_plan(\n                instructions,\n                message,\n                insert=insert\n            )\n"
  },
  {
    "path": "chalice/deploy/validate.py",
    "content": "import sys\nimport warnings\n\nfrom typing import Dict, List, Set, Iterator, Optional, Any  # noqa\n\nfrom chalice import app  # noqa\nfrom chalice.config import Config  # noqa\nfrom chalice.constants import EXPERIMENTAL_ERROR_MSG\nfrom chalice.constants import MIN_COMPRESSION_SIZE\nfrom chalice.constants import MAX_COMPRESSION_SIZE\nfrom chalice.compat import STRING_TYPES\n\n\nclass ExperimentalFeatureError(Exception):\n    def __init__(self, features_missing_opt_in):\n        # type: (Set[str]) -> None\n        self.features_missing_opt_in = features_missing_opt_in\n        msg = self._generate_msg(features_missing_opt_in)\n        super(ExperimentalFeatureError, self).__init__(msg)\n\n    def _generate_msg(self, missing_features):\n        # type: (Set[str]) -> str\n        opt_in_line = (\n            'app.experimental_feature_flags.update([\\n'\n            '%s\\n'\n            '])\\n' % ',\\n'.join([\"    '%s'\" % feature\n                                 for feature in missing_features]))\n        return EXPERIMENTAL_ERROR_MSG % opt_in_line\n\n\ndef validate_configuration(config):\n    # type: (Config) -> None\n    \"\"\"Validate app configuration.\n\n    The purpose of this method is to provide a fail fast mechanism\n    for anything we know is going to fail deployment.\n    We can detect common error cases and provide the user with helpful\n    error messages.\n\n    \"\"\"\n    routes = config.chalice_app.routes\n    validate_routes(routes)\n    validate_route_content_types(routes, config.chalice_app.api.binary_types)\n    validate_minimum_compression_size(config)\n    _validate_manage_iam_role(config)\n    validate_python_version(config)\n    validate_unique_function_names(config)\n    validate_feature_flags(config.chalice_app)\n    validate_endpoint_type(config)\n    validate_resource_policy(config)\n    validate_sqs_configuration(config.chalice_app)\n    validate_environment_variables_type(config)\n\n\ndef validate_resource_policy(config):\n    # type: (Config) -> None\n    if (config.api_gateway_endpoint_type != 'PRIVATE' and\n            config.api_gateway_endpoint_vpce):\n        raise ValueError(\n            \"config.api_gateway_endpoint_vpce should only be \"\n            \"specified for PRIVATE api_gateway_endpoint_type\")\n    if config.api_gateway_endpoint_type != 'PRIVATE':\n        return\n    if config.api_gateway_policy_file and config.api_gateway_endpoint_vpce:\n        raise ValueError(\n            \"Can only specify one of api_gateway_policy_file and \"\n            \"api_gateway_endpoint_vpce\")\n    if config.api_gateway_policy_file:\n        return\n    if not config.api_gateway_endpoint_vpce:\n        raise ValueError(\n            (\"Private Endpoints require api_gateway_policy_file or \"\n             \"api_gateway_endpoint_vpce specified\"))\n\n\ndef validate_endpoint_type(config):\n    # type: (Config) -> None\n    if not config.api_gateway_endpoint_type:\n        return\n    valid_types = ('EDGE', 'REGIONAL', 'PRIVATE')\n    if config.api_gateway_endpoint_type not in valid_types:\n        raise ValueError(\n            \"api gateway endpoint type must be one of %s\" % (\n                \", \".join(valid_types)))\n\n\ndef validate_feature_flags(chalice_app):\n    # type: (app.Chalice) -> None\n    missing_opt_in = set()\n    # pylint: disable=protected-access\n    for feature in chalice_app._features_used:\n        if feature not in chalice_app.experimental_feature_flags:\n            missing_opt_in.add(feature)\n    if missing_opt_in:\n        raise ExperimentalFeatureError(missing_opt_in)\n\n\ndef validate_routes(routes):\n    # type: (Dict[str, Dict[str, app.RouteEntry]]) -> None\n    # We're trying to validate any kind of route that will fail\n    # when we send the request to API gateway.\n    # We check for:\n    #\n    # * any routes that end with a trailing slash.\n    for route_name, methods in routes.items():\n        if not route_name:\n            raise ValueError(\"Route cannot be the empty string\")\n        if route_name != '/' and route_name.endswith('/'):\n            raise ValueError(\"Route cannot end with a trailing slash: %s\"\n                             % route_name)\n        _validate_cors_for_route(route_name, methods)\n\n\ndef validate_python_version(config, actual_py_version=None):\n    # type: (Config, Optional[str]) -> None\n    \"\"\"Validate configuration matches a specific python version.\n\n    If the ``actual_py_version`` is not provided, it will default\n    to the major/minor version of the currently running python\n    interpreter.\n\n    :param actual_py_version: The major/minor python version in\n        the form \"pythonX.Y\", e.g \"python2.7\", \"python3.6\".\n\n    \"\"\"\n    lambda_version = config.lambda_python_version\n    if actual_py_version is None:\n        actual_py_version = 'python%s.%s' % sys.version_info[:2]\n    if actual_py_version != lambda_version:\n        # We're not making this a hard error for now, but we may\n        # turn this into a hard fail.\n        warnings.warn(\"You are currently running %s, but the closest \"\n                      \"supported version on AWS Lambda is %s\\n\"\n                      \"Please use %s, otherwise you may run into \"\n                      \"deployment issues. \" %\n                      (actual_py_version, lambda_version, lambda_version),\n                      stacklevel=2)\n\n\ndef validate_route_content_types(routes, binary_types):\n    # type: (Dict[str, Dict[str, app.RouteEntry]], List[str]) -> None\n    for methods in routes.values():\n        for route_entry in methods.values():\n            _validate_entry_content_type(route_entry, binary_types)\n\n\ndef _validate_entry_content_type(route_entry, binary_types):\n    # type: (app.RouteEntry, List[str]) -> None\n    binary, non_binary = [], []\n    for content_type in route_entry.content_types:\n        if content_type in binary_types:\n            binary.append(content_type)\n        else:\n            non_binary.append(content_type)\n    if binary and non_binary:\n        # A routes content_types be homogeneous in their binary support.\n        raise ValueError(\n            'In view function \"%s\", the content_types %s support binary '\n            'and %s do not. All content_types must be consistent in their '\n            'binary support.' % (route_entry.view_name, binary, non_binary))\n\n\ndef _validate_cors_for_route(route_url, route_methods):\n    # type: (str, Dict[str, app.RouteEntry]) -> None\n    entries_with_cors = [\n        entry for entry in route_methods.values() if entry.cors\n    ]\n    if entries_with_cors:\n        # If the user has enabled CORS, they can't also have an OPTIONS\n        # method because we'll create one for them.  API gateway will\n        # raise an error about duplicate methods.\n        if 'OPTIONS' in route_methods:\n            raise ValueError(\n                \"Route entry cannot have both cors=True and \"\n                \"methods=['OPTIONS', ...] configured.  When \"\n                \"CORS is enabled, an OPTIONS method is automatically \"\n                \"added for you.  Please remove 'OPTIONS' from the list of \"\n                \"configured HTTP methods for: %s\" % route_url)\n\n        if not all(entries_with_cors[0].cors == entry.cors for entry in\n                   entries_with_cors):\n            raise ValueError(\n                \"Route may not have multiple differing CORS configurations. \"\n                \"Please ensure all views for \\\"%s\\\" that have CORS configured \"\n                \"have the same CORS configuration.\" % route_url\n            )\n\n\ndef validate_minimum_compression_size(config):\n    # type: (Config) -> None\n    if config.minimum_compression_size is None:\n        return\n    if not isinstance(config.minimum_compression_size, int):\n        raise ValueError(\"'minimum_compression_size' must be an int.\")\n    if config.minimum_compression_size < MIN_COMPRESSION_SIZE \\\n       or config.minimum_compression_size > MAX_COMPRESSION_SIZE:\n        raise ValueError(\"'minimum_compression_size' must be equal to or \"\n                         \"greater than %s and less than or equal to %s.\"\n                         % (MIN_COMPRESSION_SIZE, MAX_COMPRESSION_SIZE))\n\n\ndef _validate_manage_iam_role(config):\n    # type: (Config) -> None\n    # We need to check if manage_iam_role is None because that's the value\n    # it the user hasn't specified this value.\n    # However, if the manage_iam_role value is not None, the user set it\n    # to something, in which case we care if they set it to False.\n    if not config.manage_iam_role:\n        # If they don't want us to manage the role, they\n        # have to specify an iam_role_arn.\n        if not config.iam_role_arn:\n            raise ValueError(\n                \"When 'manage_iam_role' is set to false, you \"\n                \"must provide an 'iam_role_arn' in config.json.\"\n            )\n\n\ndef validate_unique_function_names(config):\n    # type: (Config) -> None\n    names = set()   # type: Set[str]\n    for name in _get_all_function_names(config.chalice_app):\n        if name in names:\n            raise ValueError(\"Duplicate function name detected: %s\\n\"\n                             \"Names must be unique across all lambda \"\n                             \"functions in your Chalice app.\" % name)\n        names.add(name)\n\n\ndef _get_all_function_names(chalice_app):\n    # type: (app.Chalice) -> Iterator[str]\n    for auth_handler in chalice_app.builtin_auth_handlers:\n        yield auth_handler.name\n    for event in chalice_app.event_sources:\n        yield event.name\n    for function in chalice_app.pure_lambda_functions:\n        yield function.name\n\n\ndef validate_sqs_configuration(chalice_app):\n    # type: (app.Chalice) -> None\n    for event in chalice_app.event_sources:\n        if not isinstance(event, app.SQSEventConfig):\n            continue\n        if not _is_valid_queue_name(event.queue, event.queue_arn):\n            raise ValueError(\"The 'queue' parameter for the \"\n                             \"'@app.on_sqs_message()' handler must be the \"\n                             \"name of the queue, not the queue URL or the \"\n                             \"queue ARN.  Invalid value: %s\" % event.queue)\n\n\ndef _is_valid_queue_name(queue_name, queue_arn):\n    # type: (Optional[str], Optional[str]) -> bool\n    # The mutually exclusiveness is verified in the on_sqs_message decorator.\n    if queue_name is not None and queue_name.startswith(('https:', 'arn:')):\n        return False\n    if queue_arn is not None and not queue_arn.startswith('arn:'):\n        return False\n    # We're not validating that the queue has only valid chars because SQS\n    # won't let you create a queue with that name in the first place.  We just\n    # want to detect the case where a user puts the queue URL/ARN instead of\n    # the name for the queue_name.\n    return True\n\n\ndef validate_environment_variables_type(config):\n    # type: (Config) -> None\n    _validate_environment_variables(config.environment_variables)\n    for name in _get_all_function_names(config.chalice_app):\n        _validate_environment_variables(\n            config.scope(config.chalice_stage, name).environment_variables)\n\n\ndef _validate_environment_variables(environment_variables):\n    # type: (Dict[str, Any]) -> None\n    for key, value in environment_variables.items():\n        if not isinstance(value, STRING_TYPES):\n            raise ValueError(\"Environment variable values must be strings, \"\n                             \"got 'type' %s for key '%s'\" % (\n                                 type(value).__name__, key))\n"
  },
  {
    "path": "chalice/invoke.py",
    "content": "\"\"\"Abstraction for invoking a lambda function.\"\"\"\nimport json\n\nfrom typing import Any, Optional, Dict, List, Union, Tuple  # noqa\n\nfrom chalice.config import DeployedResources  # noqa\nfrom chalice.awsclient import TypedAWSClient  # noqa\nfrom chalice.utils import UI  # noqa\nfrom chalice.compat import StringIO\n\n\nOptBytes = Optional[bytes]\n_ERROR_KEY = 'FunctionError'\n_ERROR_VALUE = 'Unhandled'\n\n\ndef _response_is_error(response):\n    # type: (Dict[str, Any]) -> bool\n    return response.get(_ERROR_KEY) == _ERROR_VALUE\n\n\nclass UnhandledLambdaError(Exception):\n    pass\n\n\nclass LambdaInvokeHandler(object):\n    \"\"\"Handler class to coordinate making an invoke call to lambda.\n\n    This class takes a LambdaInvoker, a LambdaResponseFormatter, and a UI\n    object in order to make an invoke call against lambda, format the response\n    and render it to the UI.\n    \"\"\"\n    def __init__(self, invoker, formatter, ui):\n        # type: (LambdaInvoker, LambdaResponseFormatter, UI) -> None\n        self._invoker = invoker\n        self._formatter = formatter\n        self._ui = ui\n\n    def invoke(self, payload=None):\n        # type: (OptBytes) -> None\n        response = self._invoker.invoke(payload)\n        formatted_response = self._formatter.format_response(response)\n        if _response_is_error(response):\n            self._ui.error(formatted_response)\n            raise UnhandledLambdaError()\n        self._ui.write(formatted_response)\n\n\nclass LambdaInvoker(object):\n    def __init__(self, lambda_arn, client):\n        # type: (str, TypedAWSClient) -> None\n        self._lambda_arn = lambda_arn\n        self._client = client\n\n    def invoke(self, payload=None):\n        # type: (OptBytes) -> Dict[str, Any]\n        return self._client.invoke_function(\n            self._lambda_arn,\n            payload=payload\n        )\n\n\nclass LambdaResponseFormatter(object):\n    _PAYLOAD_KEY = 'Payload'\n\n    _TRACEBACK_HEADING = 'Traceback (most recent call last):\\n'\n\n    def format_response(self, response):\n        # type: (Dict[str, Any]) -> str\n        formatted = StringIO()\n        payload = response[self._PAYLOAD_KEY].read()\n        if _response_is_error(response):\n            self._format_error(formatted, payload)\n        else:\n            self._format_success(formatted, payload)\n        return str(formatted.getvalue())\n\n    def _format_error(self, formatted, payload):\n        # type: (StringIO, bytes) -> None\n        loaded_error = json.loads(payload)\n        error_message = loaded_error['errorMessage']\n        error_type = loaded_error.get('errorType')\n        stack_trace = loaded_error.get('stackTrace')\n\n        if stack_trace is not None:\n            self._format_stacktrace(formatted, stack_trace)\n\n        if error_type is not None:\n            formatted.write('{}: {}\\n'.format(error_type, error_message))\n        else:\n            formatted.write('{}\\n'.format(error_message))\n\n    def _format_stacktrace(self, formatted, stack_trace):\n        # type: (StringIO, List[List[Union[str, int]]]) -> None\n        formatted.write(self._TRACEBACK_HEADING)\n        for frame in stack_trace:\n            self._format_frame(formatted, frame)\n\n    def _format_frame(self, formatted, frame):\n        # type: (StringIO, Union[str, List[Union[str, int]]]) -> None\n        if isinstance(frame, list):\n            # If the output is a list, it came from a 4-tuple as a result of\n            # an extract_tb call. This is the behavior up to and including\n            # python 3.6.\n            path, lineno, function, code = frame\n            formatted.write(\n                '  File \"{}\", line {}, in {}\\n'.format(path, lineno, function))\n            formatted.write(\n                '    {}\\n'.format(code))\n        else:\n            # If it is not a list, its a string. This is because the 4-tuple\n            # was replaced with a FrameSummary object which is serialized as\n            # a string by Lambda. In this case we can just print it directly.\n            formatted.write(frame)\n\n    def _format_success(self, formatted, payload):\n        # type: (StringIO, bytes) -> None\n        formatted.write('{}\\n'.format(str(payload.decode('utf-8'))))\n"
  },
  {
    "path": "chalice/local.py",
    "content": "\"\"\"Dev server used for running a chalice app locally.\n\nThis is intended only for local development purposes.\n\n\"\"\"\nfrom __future__ import print_function\nfrom __future__ import annotations\nimport re\nimport threading\nimport time\nimport uuid\nimport base64\nimport functools\nimport warnings\nfrom collections import namedtuple\nimport json\n\nfrom six.moves.BaseHTTPServer import HTTPServer\nfrom six.moves.BaseHTTPServer import BaseHTTPRequestHandler\nfrom six.moves.socketserver import ThreadingMixIn\nfrom typing import (\n    List,\n    Any,\n    Dict,\n    Tuple,\n    Callable,\n    Optional,\n    Union,\n)  # noqa\n\nfrom chalice.app import Chalice  # noqa\nfrom chalice.app import CORSConfig  # noqa\nfrom chalice.app import ChaliceAuthorizer  # noqa\nfrom chalice.app import CognitoUserPoolAuthorizer  # noqa\nfrom chalice.app import RouteEntry  # noqa\nfrom chalice.app import Request  # noqa\nfrom chalice.app import AuthResponse  # noqa\nfrom chalice.app import BuiltinAuthConfig  # noqa\nfrom chalice.config import Config  # noqa\n\nfrom chalice.compat import urlparse, parse_qs\n\n\nMatchResult = namedtuple('MatchResult', ['route', 'captured', 'query_params'])\nEventType = Dict[str, Any]\nContextType = Dict[str, Any]\nHeaderType = Dict[str, Any]\nResponseType = Dict[str, Any]\nHandlerCls = Callable[..., 'ChaliceRequestHandler']\nServerCls = Callable[..., 'HTTPServer']\n\n\nclass Clock(object):\n    def time(self) -> float:\n        return time.time()\n\n\ndef create_local_server(app_obj: Chalice,\n                        config: Config,\n                        host: str, port: int) -> LocalDevServer:\n    CustomLocalChalice.__bases__ = (LocalChalice, app_obj.__class__)\n    app_obj.__class__ = CustomLocalChalice\n    return LocalDevServer(app_obj, config, host, port)\n\n\nclass LocalARNBuilder(object):\n    ARN_FORMAT = ('arn:aws:execute-api:{region}:{account_id}'\n                  ':{api_id}/{stage}/{method}/{resource_path}')\n    LOCAL_REGION = 'mars-west-1'\n    LOCAL_ACCOUNT_ID = '123456789012'\n    LOCAL_API_ID = 'ymy8tbxw7b'\n    LOCAL_STAGE = 'api'\n\n    def build_arn(self, method: str, path: str) -> str:\n        # In API Gateway the method and URI are separated by a / so typically\n        # the uri portion omits the leading /. In the case where the entire\n        # url is just '/' API Gateway adds a / to the end so that the arn end\n        # with a '//'.\n        if path != '/':\n            path = path[1:]\n        path = path.split('?')[0]\n        return self.ARN_FORMAT.format(\n            region=self.LOCAL_REGION,\n            account_id=self.LOCAL_ACCOUNT_ID,\n            api_id=self.LOCAL_API_ID,\n            stage=self.LOCAL_STAGE,\n            method=method,\n            resource_path=path\n        )\n\n\nclass ARNMatcher(object):\n    def __init__(self, target_arn: str) -> None:\n        self._arn = target_arn\n\n    def _resource_match(self, resource: str) -> bool:\n        # Arn matching supports two special case characetrs that are not\n        # escapable. * represents a glob which translates to a non-greedy\n        # match of any number of characters. ? which is any single character.\n        # These are easy to translate to a regex using .*? and . respectivly.\n        escaped_resource = re.escape(resource)\n        resource_regex = escaped_resource.replace(r'\\?', '.').replace(\n            r'\\*', '.*?')\n        resource_regex = '^%s$' % resource_regex\n        return re.match(resource_regex, self._arn) is not None\n\n    def does_any_resource_match(self, resources: List[str]) -> bool:\n        for resource in resources:\n            if self._resource_match(resource):\n                return True\n        return False\n\n\nclass RouteMatcher(object):\n    def __init__(self, route_urls: List[str]) -> None:\n        # Sorting the route_urls ensures we always check\n        # the concrete routes for a prefix before the\n        # variable/capture parts of the route, e.g\n        # '/foo/bar' before '/foo/{capture}'\n        self.route_urls = sorted(route_urls)\n\n    def match_route(self, url: str) -> MatchResult:\n        \"\"\"Match the url against known routes.\n\n        This method takes a concrete route \"/foo/bar\", and\n        matches it against a set of routes.  These routes can\n        use param substitution corresponding to API gateway patterns.\n        For example::\n\n            match_route('/foo/bar') -> '/foo/{name}'\n\n        \"\"\"\n        # Otherwise we need to check for param substitution\n        parsed_url = urlparse(url)\n        query_params = parse_qs(parsed_url.query, keep_blank_values=True)\n        path = parsed_url.path\n        # API Gateway removes the trailing slash if the route is not the root\n        # path. We do the same here so our route matching works the same way.\n        if path != '/' and path.endswith('/'):\n            path = path[:-1]\n        parts = path.split('/')\n        captured = {}\n        for route_url in self.route_urls:\n            url_parts = route_url.split('/')\n            if len(parts) == len(url_parts):\n                for i, j in zip(parts, url_parts):\n                    if j.startswith('{') and j.endswith('}'):\n                        captured[j[1:-1]] = i\n                        continue\n                    if i != j:\n                        break\n                else:\n                    return MatchResult(route_url, captured, query_params)\n        raise ValueError(\"No matching route found for: %s\" % url)\n\n\nclass LambdaEventConverter(object):\n\n    LOCAL_SOURCE_IP = '127.0.0.1'\n\n    \"\"\"Convert an HTTP request to an event dict used by lambda.\"\"\"\n    def __init__(self, route_matcher: RouteMatcher,\n                 binary_types: Optional[List[str]] = None) -> None:\n        self._route_matcher = route_matcher\n        if binary_types is None:\n            binary_types = []\n        self._binary_types = binary_types\n\n    def _is_binary(self, headers: Dict[str, Any]) -> bool:\n        return headers.get('content-type', '') in self._binary_types\n\n    def create_lambda_event(self,\n                            method: str,\n                            path: str,\n                            headers: Dict[str, str],\n                            body: Optional[bytes] = None) -> EventType:\n        view_route = self._route_matcher.match_route(path)\n        event = {\n            'requestContext': {\n                'httpMethod': method,\n                'resourcePath': view_route.route,\n                'identity': {\n                    'sourceIp': self.LOCAL_SOURCE_IP\n                },\n                'path': path.split('?')[0],\n            },\n            'headers': {k.lower(): v for k, v in headers.items()},\n            'pathParameters': view_route.captured,\n            'stageVariables': {},\n        }\n        if view_route.query_params:\n            event['multiValueQueryStringParameters'] = view_route.query_params\n        else:\n            # If no query parameters are provided, API gateway maps\n            # this to None so we're doing this for parity.\n            event['multiValueQueryStringParameters'] = None\n        if self._is_binary(headers) and body is not None:\n            event['body'] = base64.b64encode(body).decode('ascii')\n            event['isBase64Encoded'] = True\n        else:\n            event['body'] = body\n        return event\n\n\nclass LocalGatewayException(Exception):\n    CODE = 0\n\n    def __init__(self,\n                 headers: HeaderType,\n                 body: Optional[bytes] = None) -> None:\n        self.headers = headers\n        self.body = body\n\n\nclass InvalidAuthorizerError(LocalGatewayException):\n    CODE = 500\n\n\nclass ForbiddenError(LocalGatewayException):\n    CODE = 403\n\n\nclass NotAuthorizedError(LocalGatewayException):\n    CODE = 401\n\n\nclass LambdaContext(object):\n    def __init__(self, function_name: str, memory_size: int,\n                 max_runtime_ms: int = 3000,\n                 time_source: Optional[Clock] = None) -> None:\n        if time_source is None:\n            time_source = Clock()\n        self._time_source = time_source\n        self._start_time = self._current_time_millis()\n        self._max_runtime = max_runtime_ms\n\n        # Below are properties that are found on the real LambdaContext passed\n        # by lambda and their associated documentation.\n\n        # Name of the Lambda function that is executing.\n        self.function_name = function_name\n\n        # The Lambda function version that is executing. If an alias is used\n        # to invoke the function, then function_version will be the version\n        # the alias points to.\n        # Chalice local obviously does not support versioning so it will always\n        # be set to $LATEST.\n        self.function_version = '$LATEST'\n\n        # The ARN used to invoke this function. It can be function ARN or\n        # alias ARN. An unqualified ARN executes the $LATEST version and\n        # aliases execute the function version it is pointing to.\n        self.invoked_function_arn = ''\n\n        # Memory limit, in MB, you configured for the Lambda function. You set\n        # the memory limit at the time you create a Lambda function and you\n        # can change it later.\n        self.memory_limit_in_mb = memory_size\n\n        # AWS request ID associated with the request. This is the ID returned\n        # to the client that called the invoke method.\n        self.aws_request_id = str(uuid.uuid4())\n\n        # The name of the CloudWatch log group where you can find logs written\n        # by your Lambda function.\n        self.log_group_name = ''\n\n        # The name of the CloudWatch log stream where you can find logs\n        # written by your Lambda function. The log stream may or may not\n        # change for each invocation of the Lambda function.\n        #\n        # The value is null if your Lambda function is unable to create a log\n        # stream, which can happen if the execution role that grants necessary\n        # permissions to the Lambda function does not include permissions for\n        # the CloudWatch Logs actions.\n        self.log_stream_name = ''\n\n        # The last two attributes have the following comment in the\n        # documentation:\n        # Information about the client application and device when invoked\n        # through the AWS Mobile SDK, it can be null.\n        # Chalice local doens't need to set these since they are specifically\n        # for the mobile SDK.\n        self.identity = None\n        self.client_context = None\n\n    def _current_time_millis(self) -> float:\n        return self._time_source.time() * 1000\n\n    def get_remaining_time_in_millis(self) -> float:\n        runtime = self._current_time_millis() - self._start_time\n        return self._max_runtime - runtime\n\n\nLocalAuthPair = Tuple[EventType, LambdaContext]\n\n\nclass LocalGatewayAuthorizer(object):\n    \"\"\"A class for running user defined authorizers in local mode.\"\"\"\n    def __init__(self, app_object: Chalice) -> None:\n        self._app_object = app_object\n        self._arn_builder = LocalARNBuilder()\n\n    def authorize(self,\n                  raw_path: str,\n                  lambda_event: EventType,\n                  lambda_context: LambdaContext) -> LocalAuthPair:\n        method = lambda_event['requestContext']['httpMethod']\n        route_entry = self._route_for_event(lambda_event)\n        if not route_entry:\n            return lambda_event, lambda_context\n        authorizer = route_entry.authorizer\n        if not authorizer:\n            return lambda_event, lambda_context\n        # If authorizer is Cognito then try to parse the JWT and simulate an\n        # APIGateway validated request\n        if isinstance(authorizer, CognitoUserPoolAuthorizer):\n            if \"headers\" in lambda_event\\\n                    and \"authorization\" in lambda_event[\"headers\"]:\n                token = lambda_event[\"headers\"][\"authorization\"]\n                claims = self._decode_jwt_payload(token)\n\n                try:\n                    cognito_username = claims[\"cognito:username\"]\n                except KeyError:\n                    # If a key error is raised when trying to get the cognito\n                    # username then it is a machine-to-machine communication.\n                    # This kind of cognito authorization flow is not\n                    # supported in local mode. We can ignore it here to allow\n                    # users to test their code local with a different cognito\n                    # authorization flow.\n                    warnings.warn(\n                        '%s for machine-to-machine communicaiton is not '\n                        'supported in local mode. All requests made against '\n                        'a route will be authorized to allow local testing.'\n                        % authorizer.__class__.__name__\n                    )\n                    return lambda_event, lambda_context\n\n                auth_result = {\"context\": {\"claims\": claims},\n                               \"principalId\": cognito_username}\n                lambda_event = self._update_lambda_event(lambda_event,\n                                                         auth_result)\n        if not isinstance(authorizer, ChaliceAuthorizer):\n            # Currently the only supported local authorizer is the\n            # BuiltinAuthConfig type. Anything else we will err on the side of\n            # allowing local testing by simply admiting the request. Otherwise\n            # there is no way for users to test their code in local mode.\n            warnings.warn(\n                '%s is not a supported in local mode. All requests made '\n                'against a route will be authorized to allow local testing.'\n                % authorizer.__class__.__name__\n            )\n            return lambda_event, lambda_context\n        arn = self._arn_builder.build_arn(method, raw_path)\n        auth_event = self._prepare_authorizer_event(arn, lambda_event,\n                                                    lambda_context)\n        auth_result = authorizer(auth_event, lambda_context)\n        if auth_result is None:\n            raise InvalidAuthorizerError(\n                {'x-amzn-RequestId': lambda_context.aws_request_id,\n                 'x-amzn-ErrorType': 'AuthorizerConfigurationException'},\n                b'{\"message\":null}'\n            )\n        authed = self._check_can_invoke_view_function(arn, auth_result)\n        if authed:\n            lambda_event = self._update_lambda_event(lambda_event, auth_result)\n        else:\n            raise ForbiddenError(\n                {'x-amzn-RequestId': lambda_context.aws_request_id,\n                 'x-amzn-ErrorType': 'AccessDeniedException'},\n                (b'{\"Message\": '\n                 b'\"User is not authorized to access this resource\"}'))\n        return lambda_event, lambda_context\n\n    def _check_can_invoke_view_function(self,\n                                        arn: str,\n                                        auth_result: ResponseType) -> bool:\n        policy = auth_result.get('policyDocument', {})\n        statements = policy.get('Statement', [])\n        allow_resource_statements = []\n        for statement in statements:\n            if statement.get('Effect') == 'Allow' and \\\n                    (statement.get('Action') == 'execute-api:Invoke' or\n                     'execute-api:Invoke' in statement.get('Action')):\n                for resource in statement.get('Resource'):\n                    allow_resource_statements.append(resource)\n\n        arn_matcher = ARNMatcher(arn)\n        return arn_matcher.does_any_resource_match(allow_resource_statements)\n\n    def _route_for_event(self,\n                         lambda_event: EventType) -> Optional[RouteEntry]:\n        # Authorizer had to be made into an Any type since mypy couldn't\n        # detect that app.ChaliceAuthorizer was callable.\n        resource_path = lambda_event.get(\n            'requestContext', {}).get('resourcePath')\n        http_method = lambda_event['requestContext']['httpMethod']\n        try:\n            route_entry = self._app_object.routes[resource_path][http_method]\n        except KeyError:\n            # If a key error is raised when trying to get the route entry\n            # then this route does not support this method. A method error\n            # will be raised by the chalice handler method. We can ignore it\n            # here by returning no authorizer to avoid duplicating the logic.\n            return None\n        return route_entry\n\n    def _update_lambda_event(self, lambda_event: EventType,\n                             auth_result: ResponseType) -> EventType:\n        auth_context = auth_result['context']\n        auth_context.update({\n            'principalId': auth_result['principalId']\n        })\n        lambda_event['requestContext']['authorizer'] = auth_context\n        return lambda_event\n\n    def _prepare_authorizer_event(self, arn: str,\n                                  lambda_event: EventType,\n                                  lambda_context: LambdaContext) -> EventType:\n        \"\"\"Translate event for an authorizer input.\"\"\"\n        authorizer_event = lambda_event.copy()\n        authorizer_event['type'] = 'TOKEN'\n        try:\n            authorizer_event['authorizationToken'] = authorizer_event.get(\n                'headers', {})['authorization']\n        except KeyError:\n            raise NotAuthorizedError(\n                {'x-amzn-RequestId': lambda_context.aws_request_id,\n                 'x-amzn-ErrorType': 'UnauthorizedException'},\n                b'{\"message\":\"Unauthorized\"}')\n        authorizer_event['methodArn'] = arn\n        return authorizer_event\n\n    def _decode_jwt_payload(self, jwt: str) -> Dict:\n        payload_segment = jwt.split(\".\", 2)[1]\n        payload = base64.urlsafe_b64decode(self._base64_pad(payload_segment))\n        return json.loads(payload)\n\n    def _base64_pad(self, value: str) -> str:\n        rem = len(value) % 4\n        if rem > 0:\n            value += \"=\" * (4 - rem)\n        return value\n\n\nclass LocalGateway(object):\n    \"\"\"A class for faking the behavior of API Gateway.\"\"\"\n\n    MAX_LAMBDA_EXECUTION_TIME = 900\n\n    def __init__(self, app_object: Chalice, config: Config) -> None:\n        self._app_object = app_object\n        self._config = config\n        self.event_converter = LambdaEventConverter(\n            RouteMatcher(list(app_object.routes)),\n            self._app_object.api.binary_types\n        )\n        self._authorizer = LocalGatewayAuthorizer(app_object)\n\n    def _generate_lambda_context(self) -> LambdaContext:\n        if self._config.lambda_timeout is None:\n            timeout = self.MAX_LAMBDA_EXECUTION_TIME * 1000\n        else:\n            timeout = self._config.lambda_timeout * 1000\n        return LambdaContext(\n            function_name=self._config.function_name,\n            memory_size=self._config.lambda_memory_size,\n            max_runtime_ms=timeout\n        )\n\n    def _generate_lambda_event(self,\n                               method: str,\n                               path: str,\n                               headers: HeaderType,\n                               body: Optional[bytes]) -> EventType:\n        lambda_event = self.event_converter.create_lambda_event(\n            method=method, path=path, headers=headers,\n            body=body,\n        )\n        return lambda_event\n\n    def _has_user_defined_options_method(self,\n                                         lambda_event: EventType) -> bool:\n        route_key = lambda_event['requestContext']['resourcePath']\n        return 'OPTIONS' in self._app_object.routes[route_key]\n\n    def handle_request(self,\n                       method: str,\n                       path: str,\n                       headers: HeaderType,\n                       body: Optional[bytes]) -> ResponseType:\n        lambda_context = self._generate_lambda_context()\n        try:\n            lambda_event = self._generate_lambda_event(\n                method, path, headers, body)\n        except ValueError:\n            # API Gateway will return a different error on route not found\n            # depending on whether or not we have an authorization token in our\n            # request. Since we do not do that check until we actually find\n            # the authorizer that we will call we do not have that information\n            # available at this point. Instead we just check to see if that\n            # header is present and change our response if it is. This will\n            # need to be refactored later if we decide to more closely mirror\n            # how API Gateway does their auth and routing.\n            error_headers = {'x-amzn-RequestId': lambda_context.aws_request_id,\n                             'x-amzn-ErrorType': 'UnauthorizedException'}\n            auth_header = headers.get('authorization')\n            if auth_header is None:\n                auth_header = headers.get('Authorization')\n            if auth_header is not None:\n                raise ForbiddenError(\n                    error_headers,\n                    (b'{\"message\": \"Authorization header requires '\n                     b'\\'Credential\\''\n                     b' parameter. Authorization header requires \\'Signature\\''\n                     b' parameter. Authorization header requires '\n                     b'\\'SignedHeaders\\' parameter. Authorization header '\n                     b'requires existence of either a \\'X-Amz-Date\\' or a'\n                     b' \\'Date\\' header. Authorization=%s\"}'\n                     % auth_header.encode('ascii')))\n            raise ForbiddenError(\n                error_headers,\n                b'{\"message\": \"Missing Authentication Token\"}')\n\n        # This can either be because the user's provided an OPTIONS method\n        # *or* this is a preflight request, which chalice automatically\n        # responds to without invoking a user defined route.\n        if method == 'OPTIONS' and \\\n           not self._has_user_defined_options_method(lambda_event):\n            # No options route was defined for this path. API Gateway should\n            # automatically generate our CORS headers.\n            options_headers = self._autogen_options_headers(lambda_event)\n            return {\n                'statusCode': 200,\n                'headers': options_headers,\n                'multiValueHeaders': {},\n                'body': None\n            }\n        # The authorizer call will be a noop if there is no authorizer method\n        # defined for route. Otherwise it will raise a ForbiddenError\n        # which will be caught by the handler that called this and a 403 or\n        # 401 will be sent back over the wire.\n        lambda_event, lambda_context = self._authorizer.authorize(\n            path, lambda_event, lambda_context)\n        response = self._app_object(lambda_event, lambda_context)\n        return response\n\n    def _autogen_options_headers(self, lambda_event: EventType) -> HeaderType:\n        route_key = lambda_event['requestContext']['resourcePath']\n        route_dict = self._app_object.routes[route_key]\n        route_methods = [method for method in route_dict.keys()\n                         if route_dict[method].cors is not None]\n\n        # If there are no views with CORS enabled\n        # then OPTIONS is the only allowed method.\n        if not route_methods:\n            return {'Access-Control-Allow-Methods': 'OPTIONS'}\n\n        # Chalice ensures that routes with multiple views have the same\n        # CORS configuration, so if any view has a CORS Config we can use\n        # that config since they will all be the same.\n        cors_config = route_dict[route_methods[0]].cors\n        cors_headers = cors_config.get_access_control_headers()\n\n        # We need to add OPTIONS since it is not a part of the CORSConfig\n        # object. APIGateway handles this entirely based on the API definition.\n        # So our local version needs to add this manually to our set of allowed\n        # headers.\n        route_methods.append('OPTIONS')\n\n        # The Access-Control-Allow-Methods header is not added by the\n        # CORSConfig object it is added to the API Gateway route during\n        # deployment, so we need to manually add those headers here.\n        cors_headers.update({\n            'Access-Control-Allow-Methods': '%s' % ','.join(route_methods)\n        })\n        return cors_headers\n\n\nclass ChaliceRequestHandler(BaseHTTPRequestHandler):\n    \"\"\"A class for mapping raw HTTP events to and from LocalGateway.\"\"\"\n    protocol_version = 'HTTP/1.1'\n\n    def __init__(self,\n                 request: bytes,\n                 client_address: Tuple[str, int],\n                 server: HTTPServer,\n                 app_object: Chalice,\n                 config: Config) -> None:\n        self.local_gateway = LocalGateway(app_object, config)\n        BaseHTTPRequestHandler.__init__(\n            self, request, client_address, server)  # type: ignore\n\n    def _parse_payload(self) -> Tuple[HeaderType, Optional[bytes]]:\n        body = None\n        content_length = int(self.headers.get('content-length', '0'))\n        if content_length > 0:\n            body = self.rfile.read(content_length)\n        converted_headers = dict(self.headers)\n        return converted_headers, body\n\n    def _generic_handle(self) -> None:\n        headers, body = self._parse_payload()\n        try:\n            response = self.local_gateway.handle_request(\n                method=self.command,\n                path=self.path,\n                headers=headers,\n                body=body\n            )\n            status_code = response['statusCode']\n            headers = response['headers'].copy()\n            headers.update(response['multiValueHeaders'])\n            response = self._handle_binary(response)\n            body = response['body']\n            self._send_http_response(status_code, headers, body)\n        except LocalGatewayException as e:\n            self._send_error_response(e)\n\n    def _handle_binary(self, response: Dict[str, Any]) -> Dict[str, Any]:\n        if response.get('isBase64Encoded'):\n            body = base64.b64decode(response['body'])\n            response['body'] = body\n        return response\n\n    def _send_error_response(self, error: LocalGatewayException) -> None:\n        code = error.CODE\n        headers = error.headers\n        body = error.body\n        self._send_http_response(code, headers, body)\n\n    def _send_http_response(self,\n                            code: int,\n                            headers: HeaderType,\n                            body: Optional[Union[str, bytes]]) -> None:\n        if body is None:\n            self._send_http_response_no_body(code, headers)\n        else:\n            self._send_http_response_with_body(code, headers, body)\n\n    def _send_http_response_with_body(self,\n                                      code: int,\n                                      headers: HeaderType,\n                                      body: Union[str, bytes]) -> None:\n        self.send_response(code)\n        if not isinstance(body, bytes):\n            body = body.encode('utf-8')\n        self.send_header('Content-Length', str(len(body)))\n        content_type = headers.pop(\n            'Content-Type', 'application/json')\n        self.send_header('Content-Type', content_type)\n        self._send_headers(headers)\n        self.wfile.write(body)\n\n    do_GET = do_PUT = do_POST = do_HEAD = do_DELETE = \\\n        do_PATCH = do_OPTIONS = _generic_handle\n\n    def _send_http_response_no_body(self,\n                                    code: int,\n                                    headers: HeaderType) -> None:\n        headers['Content-Length'] = '0'\n        self.send_response(code)\n        self._send_headers(headers)\n\n    def _send_headers(self, headers: HeaderType) -> None:\n        for header_name, header_value in headers.items():\n            if isinstance(header_value, list):\n                for value in header_value:\n                    self.send_header(header_name, value)\n            else:\n                self.send_header(header_name, header_value)\n        self.end_headers()\n\n\nclass ThreadedHTTPServer(ThreadingMixIn, HTTPServer):\n    \"\"\"Threading mixin to better support browsers.\n\n    When a browser sends a GET request to Chalice it keeps the connection open\n    for reuse. In the single threaded model this causes Chalice local to become\n    unresponsive to all clients other than that browser socket. Even sending a\n    header requesting that the client close the connection is not good enough,\n    the browswer will simply open another one and sit on it.\n    \"\"\"\n\n    daemon_threads = True\n\n\nclass LocalDevServer(object):\n    def __init__(self,\n                 app_object: Chalice,\n                 config: Config, host: str, port: int,\n                 handler_cls: HandlerCls = ChaliceRequestHandler,\n                 server_cls: ServerCls = ThreadedHTTPServer) -> None:\n        self.app_object = app_object\n        self.host = host\n        self.port = port\n        self._wrapped_handler = functools.partial(\n            handler_cls, app_object=app_object, config=config)\n        self.server = server_cls((host, port), self._wrapped_handler)\n\n    def handle_single_request(self) -> None:\n        self.server.handle_request()\n\n    def serve_forever(self) -> None:\n        print(\"Serving on http://%s:%s\" % (self.host, self.port))\n        self.server.serve_forever()\n\n    def shutdown(self) -> None:\n        # This must be called from another thread of else it\n        # will deadlock.\n        self.server.shutdown()\n\n\nclass HTTPServerThread(threading.Thread):\n    \"\"\"Thread that manages starting/stopping local HTTP server.\n\n    This is a small wrapper around a normal threading.Thread except\n    that it adds shutdown capability of the HTTP server, which is\n    not part of the normal threading.Thread interface.\n\n    \"\"\"\n    def __init__(self, server_factory: Callable[[], LocalDevServer]) -> None:\n        threading.Thread.__init__(self)\n        self._server_factory = server_factory\n        self._server: Optional[LocalDevServer] = None\n        self.daemon = True\n\n    def run(self) -> None:\n        self._server = self._server_factory()\n        self._server.serve_forever()\n\n    def shutdown(self) -> None:\n        if self._server is not None:\n            self._server.shutdown()\n\n\nclass LocalChalice(Chalice):\n\n    _THREAD_LOCAL = threading.local()\n\n    # This is a known mypy bug where you can't override instance\n    # variables with properties.  So this should be type safe, which\n    # is why we're adding the type: ignore comments here.\n    # See: https://github.com/python/mypy/issues/4125\n\n    @property  # type: ignore\n    def current_request(self) -> Request:  # type: ignore\n        return self._THREAD_LOCAL.current_request\n\n    @current_request.setter\n    def current_request(self, value: Request) -> None:  # type: ignore\n        self._THREAD_LOCAL.current_request = value\n\n\nclass CustomLocalChalice(LocalChalice):\n    pass\n"
  },
  {
    "path": "chalice/logs.py",
    "content": "\"\"\"Module for inspecting chalice logs.\n\nThis module provides APIs for searching, interacting\nwith the logs generated by AWS Lambda.\n\n\"\"\"\nfrom __future__ import annotations\nimport time\nfrom datetime import datetime, timedelta\nfrom dataclasses import dataclass\nfrom collections import defaultdict\n\nfrom typing import Any, Optional, Iterator, Dict, IO, Callable, Set  # noqa\nfrom botocore.session import Session  # noqa\n\nfrom chalice.awsclient import TypedAWSClient, CWLogEvent  # noqa\nfrom chalice.utils import TimestampConverter\n\n\n@dataclass\nclass LogRetrieveOptions(object):\n    max_entries: Optional[int] = None\n    start_time: Optional[datetime] = None\n    include_lambda_messages: bool = False\n\n    @classmethod\n    def create(\n        cls, follow: bool = False, since: Optional[str] = None, **kwargs: Any\n    ) -> LogRetrieveOptions:\n        start_time = None\n        if since is not None:\n            start_time = TimestampConverter().timestamp_to_datetime(since)\n        elif follow:\n            # If they've specified --follow with no start time, we'll default\n            # to 10 minutes prior to now.  This is a backwards compat\n            # quirk where we can't change the default for `chalice logs`, but\n            # for the --follow mode, we can default to only showing 10 minutes\n            # worth of logs before we start polling for new logs.  In a\n            # future major version we can just change the default for all log\n            # retrieval to 10 minutes.\n            start_time = datetime.utcnow() - timedelta(minutes=10)\n        kwargs['start_time'] = start_time\n        return cls(**kwargs)\n\n\ndef display_logs(\n    retriever: LogRetriever,\n    stream: IO[str],\n    retrieve_options: LogRetrieveOptions,\n) -> None:\n    events = retriever.retrieve_logs(retrieve_options)\n    for event in events:\n        stream.write(\n            '%s %s %s\\n'\n            % (\n                event['timestamp'],\n                event['logShortId'],\n                event['message'].strip(),\n            )\n        )\n\n\nclass LogRetriever(object):\n    def __init__(\n        self, log_event_generator: BaseLogEventGenerator, log_group_name: str\n    ) -> None:\n        self._log_event_generator = log_event_generator\n        self._log_group_name = log_group_name\n\n    @classmethod\n    def create_from_lambda_arn(\n        cls, log_event_generator: BaseLogEventGenerator, lambda_arn: str\n    ) -> LogRetriever:\n        \"\"\"Create a LogRetriever from a client and lambda arn.\n\n        :type client: botocore.client.Logs\n        :param client: A ``logs`` client.\n\n        :type lambda_arn: str\n        :param lambda_arn: The ARN of the lambda function.\n\n        :return: An instance of ``LogRetriever``.\n\n        \"\"\"\n        lambda_name = lambda_arn.split(':')[6]\n        log_group_name = '/aws/lambda/%s' % lambda_name\n        return cls(log_event_generator, log_group_name)\n\n    def _is_lambda_message(self, event: CWLogEvent) -> bool:\n        # Lambda will also inject log messages into your log streams.\n        # They look like:\n        # START RequestId: guid Version: $LATEST\n        # END RequestId: guid\n        # REPORT RequestId: guid    Duration: 0.35 ms   Billed Duration: ...\n\n        # By default, these message are included in retrieve_logs().\n        # But you can also request that retrieve_logs() filter out\n        # these message so that we only include log messages generated\n        # by your chalice app.\n        msg = event['message'].strip()\n        return msg.startswith(\n            ('START RequestId', 'END RequestId', 'REPORT RequestId')\n        )\n\n    def retrieve_logs(\n        self, retrieve_options: LogRetrieveOptions\n    ) -> Iterator[CWLogEvent]:\n        \"\"\"Retrieve logs from a log group.\n\n        :type include_lambda_messages: boolean\n        :param include_lambda_messages: Include logs generated by the AWS\n            Lambda service.  If this value is False, only chalice logs will be\n            included.\n\n        :type max_entries: int\n        :param max_entries: Maximum number of log messages to include.\n\n        :rtype: iterator\n        :return: An iterator that yields event dicts.  Each event\n            dict has these keys:\n\n            * logStreamName -> (string) The name of the log stream.\n            * timestamp -> (datetime.datetime) - The timestamp for the msg.\n            * message -> (string) The data contained in the log event.\n            * ingestionTime -> (datetime.datetime) Ingestion time of event.\n            * eventId -> (string) A unique identifier for this event.\n            * logShortId -> (string) Short identifier for logStreamName.\n\n        \"\"\"\n        max_entries = retrieve_options.max_entries\n        shown = 0\n        events = self._log_event_generator.iter_log_events(\n            self._log_group_name, retrieve_options\n        )\n        for event in events:\n            if (\n                not retrieve_options.include_lambda_messages\n                and self._is_lambda_message(event)\n            ):\n                continue\n            # logStreamName is: '2016/07/05/[id]hash'\n            # We want to extract the hash portion and\n            # provide a short identifier.\n            identifier = event['logStreamName']\n            if ']' in identifier:\n                index = identifier.find(']')\n                identifier = identifier[index + 1:index + 7]\n            event['logShortId'] = identifier\n            yield event\n            shown += 1\n            if max_entries is not None and shown >= max_entries:\n                return\n\n\nclass BaseLogEventGenerator(object):\n    def __init__(self, client: TypedAWSClient) -> None:\n        self._client = client\n\n    def iter_log_events(\n        self, log_group_name: str, options: LogRetrieveOptions\n    ) -> Iterator[CWLogEvent]:\n        raise NotImplementedError(\"iter_log_events\")\n\n\nclass LogEventGenerator(BaseLogEventGenerator):\n    def iter_log_events(\n        self, log_group_name: str, options: LogRetrieveOptions\n    ) -> Iterator[CWLogEvent]:\n        logs = self._client.iter_log_events(\n            log_group_name=log_group_name, start_time=options.start_time\n        )\n        yield from logs\n\n\nclass FollowLogEventGenerator(BaseLogEventGenerator):\n\n    _POLL_TIME = 5\n\n    def __init__(\n        self,\n        client: TypedAWSClient,\n        sleep: Callable[[int], None] = time.sleep,\n        poll_time: int = _POLL_TIME,\n    ) -> None:\n        self._client = client\n        self._sleep = sleep\n        self._event_id_cache: Dict[datetime, Set[str]] = defaultdict(set)\n        self._poll_time = poll_time\n\n    def iter_log_events(\n        self, log_group_name: str, options: LogRetrieveOptions\n    ) -> Iterator[CWLogEvent]:\n        start_time = options.start_time\n        try:\n            yield from self._loop_on_filter_log_events(\n                log_group_name, start_time\n            )\n        except KeyboardInterrupt:\n            pass\n\n    def _loop_on_filter_log_events(\n        self, log_group_name: str, start_time: Optional[datetime]\n    ) -> Iterator[CWLogEvent]:\n        self._event_id_cache.clear()\n        kwargs: Dict[str, Any] = {\n            'log_group_name': log_group_name,\n            'start_time': start_time,\n        }\n        while True:\n            response = self._client.filter_log_events(**kwargs)\n            for event in response['events']:\n                if not self._in_cache(event):\n                    self._add_to_cache(event)\n                    yield event\n            if 'nextToken' in response:\n                # If there's more pages we go through the normal pagination\n                # logic of following nextTokens.\n                kwargs['next_token'] = response['nextToken']\n            else:\n                kwargs.pop('next_token', None)\n                if self._event_id_cache:\n                    # However, if there's no nextToken it means we've iterated\n                    # through all the existing log events.  We now need to\n                    # start polling for new events.  To do this, we need\n                    # to use start time and not nextToken, because once\n                    # we've exhausted the iteration we won't see new events\n                    # from any log streams we've already iterated through.\n                    # Querying only based on start time ensures we see new\n                    # messages from all new streams.\n                    # This isn't going to be perfect.  It's possible that\n                    # we miss a gap between when we finished searching through\n                    # a log stream and the new start time we're going to use\n                    # to start polling, especially at high rates of log\n                    # generation.\n                    most_recent_start_time = max(self._event_id_cache)\n                    kwargs['start_time'] = most_recent_start_time\n                    self._prune_old_cache_entries(most_recent_start_time)\n                    self._sleep(self._poll_time)\n\n    def _in_cache(self, event: CWLogEvent) -> bool:\n        return event['eventId'] in self._event_id_cache[event['timestamp']]\n\n    def _add_to_cache(self, event: CWLogEvent) -> None:\n        self._event_id_cache[event['timestamp']].add(event['eventId'])\n\n    def _prune_old_cache_entries(self, timestamp: datetime) -> None:\n        surviving_entries = self._event_id_cache[timestamp]\n        self._event_id_cache.clear()\n        self._event_id_cache[timestamp] = surviving_entries\n"
  },
  {
    "path": "chalice/package.py",
    "content": "# pylint: disable=too-many-lines\n\nimport copy\nimport json\nimport os\nimport re\n\nimport six\nfrom typing import Any, Optional, Dict, List, Set, Union  # noqa\nfrom typing import cast\n\nimport yaml\nfrom yaml.scanner import ScannerError\nfrom yaml.nodes import Node  # noqa\nfrom yaml.nodes import ScalarNode, SequenceNode, MappingNode\n\nfrom chalice.deploy.swagger import (\n    CFNSwaggerGenerator, TerraformSwaggerGenerator)\nfrom chalice.utils import (\n    OSUtils, UI, serialize_to_json, to_cfn_resource_name\n)\nfrom chalice.awsclient import TypedAWSClient  # noqa\nfrom chalice.config import Config  # noqa\nfrom chalice.deploy import models\nfrom chalice.deploy.appgraph import ApplicationGraphBuilder, DependencyBuilder\nfrom chalice.deploy.deployer import BuildStage  # noqa\nfrom chalice.deploy.deployer import create_build_stage\n\n\ndef create_app_packager(\n        config, options, package_format='cloudformation',\n        template_format='json', merge_template=None):\n    # type: (Config, PackageOptions, str, str, Optional[str]) -> AppPackager\n    osutils = OSUtils()\n    ui = UI()\n    application_builder = ApplicationGraphBuilder()\n    deps_builder = DependencyBuilder()\n    post_processors = []  # type: List[TemplatePostProcessor]\n    generator = None  # type: Union[None, TemplateGenerator]\n\n    template_serializer = cast(TemplateSerializer, JSONTemplateSerializer())\n    if package_format == 'cloudformation':\n        build_stage = create_build_stage(\n            osutils, ui, CFNSwaggerGenerator(), config)\n        use_yaml_serializer = template_format == 'yaml'\n        if merge_template is not None and \\\n                YAMLTemplateSerializer.is_yaml_template(merge_template):\n            # Automatically switch the serializer to yaml if they specify\n            # a yaml template to merge, regardless of what template format\n            # they specify.\n            use_yaml_serializer = True\n        if use_yaml_serializer:\n            template_serializer = YAMLTemplateSerializer()\n        post_processors.extend([\n            SAMCodeLocationPostProcessor(osutils=osutils),\n            TemplateMergePostProcessor(\n                osutils=osutils,\n                merger=TemplateDeepMerger(),\n                template_serializer=template_serializer,\n                merge_template=merge_template)])\n        generator = SAMTemplateGenerator(config, options)\n    else:\n        build_stage = create_build_stage(\n            osutils, ui, TerraformSwaggerGenerator(), config)\n        generator = TerraformGenerator(config, options)\n        post_processors.append(\n            TerraformCodeLocationPostProcessor(osutils=osutils))\n\n    resource_builder = ResourceBuilder(\n        application_builder, deps_builder, build_stage)\n\n    return AppPackager(\n        generator,\n        resource_builder,\n        CompositePostProcessor(post_processors),\n        template_serializer,\n        osutils)\n\n\nclass UnsupportedFeatureError(Exception):\n    pass\n\n\nclass DuplicateResourceNameError(Exception):\n    pass\n\n\nclass PackageOptions(object):\n    def __init__(self, client):\n        # type: (TypedAWSClient) -> None\n        self._client = client  # type: TypedAWSClient\n\n    def service_principal(self, service):\n        # type: (str) -> str\n        dns_suffix = self._client.endpoint_dns_suffix(service,\n                                                      self._client.region_name)\n        return self._client.service_principal(service,\n                                              self._client.region_name,\n                                              dns_suffix)\n\n\nclass ResourceBuilder(object):\n    def __init__(self,\n                 application_builder,  # type: ApplicationGraphBuilder\n                 deps_builder,         # type: DependencyBuilder\n                 build_stage,          # type: BuildStage\n                 ):\n        # type: (...) -> None\n        self._application_builder = application_builder\n        self._deps_builder = deps_builder\n        self._build_stage = build_stage\n\n    def construct_resources(self, config, chalice_stage_name):\n        # type: (Config, str) -> List[models.Model]\n        application = self._application_builder.build(\n            config, chalice_stage_name)\n        resources = self._deps_builder.build_dependencies(application)\n        self._build_stage.execute(config, resources)\n        # Rebuild dependencies in case the build stage modified\n        # the application graph.\n        resources = self._deps_builder.build_dependencies(application)\n        return resources\n\n\nclass TemplateGenerator(object):\n    template_file = None  # type: str\n\n    def __init__(self, config, options):\n        # type: (Config, PackageOptions) -> None\n        self._config = config\n        self._options = options\n\n    def dispatch(self, resource, template):\n        # type: (models.Model, Dict[str, Any]) -> None\n        name = '_generate_%s' % resource.__class__.__name__.lower()\n        handler = getattr(self, name, self._default)\n        handler(resource, template)\n\n    def generate(self, resources):\n        # type: (List[models.Model]) -> Dict[str, Any]\n        raise NotImplementedError()\n\n    def _generate_filebasediampolicy(self, resource, template):\n        # type: (models.FileBasedIAMPolicy, Dict[str, Any]) -> None\n        pass\n\n    def _generate_autogeniampolicy(self, resource, template):\n        # type: (models.AutoGenIAMPolicy, Dict[str, Any]) -> None\n        pass\n\n    def _generate_deploymentpackage(self, resource, template):\n        # type: (models.DeploymentPackage, Dict[str, Any]) -> None\n        pass\n\n    def _generate_precreatediamrole(self, resource, template):\n        # type: (models.PreCreatedIAMRole, Dict[str, Any]) -> None\n        pass\n\n    def _default(self, resource, template):\n        # type: (models.Model, Dict[str, Any]) -> None\n        raise UnsupportedFeatureError(resource)\n\n\nclass SAMTemplateGenerator(TemplateGenerator):\n    _BASE_TEMPLATE = {\n        'AWSTemplateFormatVersion': '2010-09-09',\n        'Transform': 'AWS::Serverless-2016-10-31',\n        'Outputs': {},\n        'Resources': {},\n    }\n\n    template_file = \"sam\"\n\n    def __init__(self, config, options):\n        # type: (Config, PackageOptions) -> None\n        super(SAMTemplateGenerator, self).__init__(config, options)\n        self._seen_names = set([])  # type: Set[str]\n        self._chalice_layer = \"\"\n\n    def generate(self, resources):\n        # type: (List[models.Model]) -> Dict[str, Any]\n        template = copy.deepcopy(self._BASE_TEMPLATE)\n        self._seen_names.clear()\n        for resource in resources:\n            self.dispatch(resource, template)\n        return template\n\n    def _generate_lambdalayer(self, resource, template):\n        # type: (models.LambdaLayer, Dict[str, Any]) -> None\n        layer = to_cfn_resource_name(\n            resource.resource_name)\n        template['Resources'][layer] = {\n            \"Type\": \"AWS::Serverless::LayerVersion\",\n            \"Properties\": {\n                \"CompatibleRuntimes\": [resource.runtime],\n                \"ContentUri\": resource.deployment_package.filename,\n                \"LayerName\": resource.layer_name\n            }\n        }\n        self._chalice_layer = layer\n\n    def _generate_scheduledevent(self, resource, template):\n        # type: (models.ScheduledEvent, Dict[str, Any]) -> None\n        function_cfn_name = to_cfn_resource_name(\n            resource.lambda_function.resource_name)\n        function_cfn = template['Resources'][function_cfn_name]\n        event_cfn_name = self._register_cfn_resource_name(\n            resource.resource_name)\n        function_cfn['Properties']['Events'] = {\n            event_cfn_name: {\n                'Type': 'Schedule',\n                'Properties': {\n                    'Schedule': resource.schedule_expression,\n                }\n            }\n        }\n\n    def _generate_cloudwatchevent(self, resource, template):\n        # type: (models.CloudWatchEvent, Dict[str, Any]) -> None\n        function_cfn_name = to_cfn_resource_name(\n            resource.lambda_function.resource_name)\n        function_cfn = template['Resources'][function_cfn_name]\n        event_cfn_name = self._register_cfn_resource_name(\n            resource.resource_name)\n        function_cfn['Properties']['Events'] = {\n            event_cfn_name: {\n                'Type': 'CloudWatchEvent',\n                'Properties': {\n                    # For api calls we need serialized string form, for\n                    # SAM Templates we need datastructures.\n                    'Pattern': json.loads(resource.event_pattern)\n                }\n            }\n        }\n\n    def _generate_lambdafunction(self, resource, template):\n        # type: (models.LambdaFunction, Dict[str, Any]) -> None\n        resources = template['Resources']\n        cfn_name = self._register_cfn_resource_name(resource.resource_name)\n        lambdafunction_definition = {\n            'Type': 'AWS::Serverless::Function',\n            'Properties': {\n                'Runtime': resource.runtime,\n                'Handler': resource.handler,\n                'CodeUri': resource.deployment_package.filename,\n                'Tags': resource.tags,\n                'Tracing': resource.xray and 'Active' or 'PassThrough',\n                'Timeout': resource.timeout,\n                'MemorySize': resource.memory_size,\n            },\n        }  # type: Dict[str, Any]\n\n        if resource.environment_variables:\n            environment_config = {\n                'Environment': {\n                    'Variables': resource.environment_variables\n                }\n            }  # type: Dict[str, Dict[str, Dict[str, str]]]\n            lambdafunction_definition['Properties'].update(environment_config)\n        if resource.security_group_ids and resource.subnet_ids:\n            vpc_config = {\n                'VpcConfig': {\n                    'SecurityGroupIds': resource.security_group_ids,\n                    'SubnetIds': resource.subnet_ids,\n                }\n            }  # type: Dict[str, Dict[str, List[str]]]\n            lambdafunction_definition['Properties'].update(vpc_config)\n        if resource.reserved_concurrency is not None:\n            reserved_concurrency_config = {\n                'ReservedConcurrentExecutions': resource.reserved_concurrency\n            }\n            lambdafunction_definition['Properties'].update(\n                reserved_concurrency_config)\n\n        layers = list(resource.layers) or []  # type: List[Any]\n        if self._chalice_layer:\n            layers.insert(0, {'Ref': self._chalice_layer})\n\n        if layers:\n            layers_config = {\n                'Layers': layers\n            }  # type: Dict[str, Any]\n            lambdafunction_definition['Properties'].update(layers_config)\n\n        if resource.log_group is not None:\n            num_days = resource.log_group.retention_in_days\n            log_name = self._register_cfn_resource_name(\n                resource.log_group.resource_name)\n            log_def = {\n                'Type': 'AWS::Logs::LogGroup',\n                'Properties': {\n                    'LogGroupName': {\n                        'Fn::Sub': '/aws/lambda/${%s}' % cfn_name\n                    },\n                    'RetentionInDays': num_days\n                }\n            }\n            resources[log_name] = log_def\n\n        resources[cfn_name] = lambdafunction_definition\n        self._add_iam_role(resource, resources[cfn_name])\n\n    def _add_iam_role(self, resource, cfn_resource):\n        # type: (models.LambdaFunction, Dict[str, Any]) -> None\n        role = resource.role\n        if isinstance(role, models.ManagedIAMRole):\n            cfn_resource['Properties']['Role'] = {\n                'Fn::GetAtt': [\n                    to_cfn_resource_name(role.resource_name), 'Arn'\n                ],\n            }\n        else:\n            # resource is a PreCreatedIAMRole.  This is the only other\n            # subclass of IAMRole.\n            role = cast(models.PreCreatedIAMRole, role)\n            cfn_resource['Properties']['Role'] = role.role_arn\n\n    def _generate_loggroup(self, resource, template):\n        # type: (models.LogGroup, Dict[str, Any]) -> None\n        # Handled in LambdaFunction generation\n        pass\n\n    def _generate_restapi(self, resource, template):\n        # type: (models.RestAPI, Dict[str, Any]) -> None\n        resources = template['Resources']\n        resources['RestAPI'] = {\n            'Type': 'AWS::Serverless::Api',\n            'Properties': {\n                'EndpointConfiguration': resource.endpoint_type,\n                'StageName': resource.api_gateway_stage,\n                'DefinitionBody': resource.swagger_doc,\n            }\n        }\n        if resource.minimum_compression:\n            properties = resources['RestAPI']['Properties']\n            properties['MinimumCompressionSize'] = \\\n                int(resource.minimum_compression)\n\n        handler_cfn_name = to_cfn_resource_name(\n            resource.lambda_function.resource_name)\n        api_handler = template['Resources'].pop(handler_cfn_name)\n        template['Resources']['APIHandler'] = api_handler\n        resources['APIHandlerInvokePermission'] = {\n            'Type': 'AWS::Lambda::Permission',\n            'Properties': {\n                'FunctionName': {'Ref': 'APIHandler'},\n                'Action': 'lambda:InvokeFunction',\n                'Principal': self._options.service_principal('apigateway'),\n                'SourceArn': {\n                    'Fn::Sub': [\n                        ('arn:${AWS::Partition}:execute-api:${AWS::Region}'\n                         ':${AWS::AccountId}:${RestAPIId}/*'),\n                        {'RestAPIId': {'Ref': 'RestAPI'}},\n                    ]\n                },\n            }\n        }\n        for auth in resource.authorizers:\n            auth_cfn_name = to_cfn_resource_name(auth.resource_name)\n            resources[auth_cfn_name + 'InvokePermission'] = {\n                'Type': 'AWS::Lambda::Permission',\n                'Properties': {\n                    'FunctionName': {'Fn::GetAtt': [auth_cfn_name, 'Arn']},\n                    'Action': 'lambda:InvokeFunction',\n                    'Principal': self._options.service_principal('apigateway'),\n                    'SourceArn': {\n                        'Fn::Sub': [\n                            ('arn:${AWS::Partition}:execute-api'\n                             ':${AWS::Region}:${AWS::AccountId}'\n                             ':${RestAPIId}/*'),\n                            {'RestAPIId': {'Ref': 'RestAPI'}},\n                        ]\n                    },\n                }\n            }\n        self._add_domain_name(resource, template)\n        self._inject_restapi_outputs(template)\n\n    def _inject_restapi_outputs(self, template):\n        # type: (Dict[str, Any]) -> None\n        # The 'Outputs' of the SAM template are considered\n        # part of the public API of chalice and therefore\n        # need to maintain backwards compatibility.  This\n        # method uses the same output key names as the old\n        # deployer.\n        # For now, we aren't adding any of the new resources\n        # to the Outputs section until we can figure out\n        # a consist naming scheme.  Ideally we don't use\n        # the autogen'd names that contain the md5 suffixes.\n        stage_name = template['Resources']['RestAPI'][\n            'Properties']['StageName']\n        outputs = template['Outputs']\n        outputs['RestAPIId'] = {\n            'Value': {'Ref': 'RestAPI'}\n        }\n        outputs['APIHandlerName'] = {\n            'Value': {'Ref': 'APIHandler'}\n        }\n        outputs['APIHandlerArn'] = {\n            'Value': {'Fn::GetAtt': ['APIHandler', 'Arn']}\n        }\n        outputs['EndpointURL'] = {\n            'Value': {\n                'Fn::Sub': (\n                    'https://${RestAPI}.execute-api.${AWS::Region}'\n                    # The api_gateway_stage is filled in when\n                    # the template is built.\n                    '.${AWS::URLSuffix}/%s/'\n                ) % stage_name\n            }\n        }\n\n    def _add_websocket_lambda_integration(\n            self, api_ref, websocket_handler, resources):\n        # type: (Dict[str, Any], str, Dict[str, Any]) -> None\n        resources['%sAPIIntegration' % websocket_handler] = {\n            'Type': 'AWS::ApiGatewayV2::Integration',\n            'Properties': {\n                'ApiId': api_ref,\n                'ConnectionType': 'INTERNET',\n                'ContentHandlingStrategy': 'CONVERT_TO_TEXT',\n                'IntegrationType': 'AWS_PROXY',\n                'IntegrationUri': {\n                    'Fn::Sub': [\n                        (\n                            'arn:${AWS::Partition}:apigateway:${AWS::Region}'\n                            ':lambda:path/2015-03-31/functions/arn'\n                            ':${AWS::Partition}:lambda:${AWS::Region}'\n                            ':${AWS::AccountId}:function'\n                            ':${WebsocketHandler}/invocations'\n                        ),\n                        {'WebsocketHandler': {'Ref': websocket_handler}}\n                    ],\n                }\n            }\n        }\n\n    def _add_websocket_lambda_invoke_permission(\n            self, api_ref, websocket_handler, resources):\n        # type: (Dict[str, str], str, Dict[str, Any]) -> None\n        resources['%sInvokePermission' % websocket_handler] = {\n            'Type': 'AWS::Lambda::Permission',\n            'Properties': {\n                'FunctionName': {'Ref': websocket_handler},\n                'Action': 'lambda:InvokeFunction',\n                'Principal': self._options.service_principal('apigateway'),\n                'SourceArn': {\n                    'Fn::Sub': [\n                        ('arn:${AWS::Partition}:execute-api'\n                         ':${AWS::Region}:${AWS::AccountId}'\n                         ':${WebsocketAPIId}/*'),\n                        {'WebsocketAPIId': api_ref},\n                    ],\n                },\n            }\n        }\n\n    def _add_websocket_lambda_integrations(self, api_ref, resources):\n        # type: (Dict[str, str], Dict[str, Any]) -> None\n        websocket_handlers = [\n            'WebsocketConnect',\n            'WebsocketMessage',\n            'WebsocketDisconnect',\n        ]\n        for handler in websocket_handlers:\n            if handler in resources:\n                self._add_websocket_lambda_integration(\n                    api_ref, handler, resources)\n                self._add_websocket_lambda_invoke_permission(\n                    api_ref, handler, resources)\n\n    def _create_route_for_key(self, route_key, api_ref):\n        # type: (str, Dict[str, str]) -> Dict[str, Any]\n        integration_ref = {\n            '$connect': 'WebsocketConnectAPIIntegration',\n            '$disconnect': 'WebsocketDisconnectAPIIntegration',\n        }.get(route_key, 'WebsocketMessageAPIIntegration')\n\n        return {\n            'Type': 'AWS::ApiGatewayV2::Route',\n            'Properties': {\n                'ApiId': api_ref,\n                'RouteKey': route_key,\n                'Target': {\n                    'Fn::Join': [\n                        '/',\n                        [\n                            'integrations',\n                            {'Ref': integration_ref},\n                        ]\n                    ]\n                },\n            },\n        }\n\n    def _generate_websocketapi(self, resource, template):\n        # type: (models.WebsocketAPI, Dict[str, Any]) -> None\n        resources = template['Resources']\n        api_ref = {'Ref': 'WebsocketAPI'}\n        resources['WebsocketAPI'] = {\n            'Type': 'AWS::ApiGatewayV2::Api',\n            'Properties': {\n                'Name': resource.name,\n                'RouteSelectionExpression': '$request.body.action',\n                'ProtocolType': 'WEBSOCKET',\n            }\n        }\n\n        self._add_websocket_lambda_integrations(api_ref, resources)\n\n        route_key_names = []\n        for route in resource.routes:\n            key_name = 'Websocket%sRoute' % route.replace(\n                '$', '').replace('default', 'message').capitalize()\n            route_key_names.append(key_name)\n            resources[key_name] = self._create_route_for_key(route, api_ref)\n\n        resources['WebsocketAPIDeployment'] = {\n            'Type': 'AWS::ApiGatewayV2::Deployment',\n            'DependsOn': route_key_names,\n            'Properties': {\n                'ApiId': api_ref,\n            }\n        }\n\n        resources['WebsocketAPIStage'] = {\n            'Type': 'AWS::ApiGatewayV2::Stage',\n            'Properties': {\n                'ApiId': api_ref,\n                'DeploymentId': {'Ref': 'WebsocketAPIDeployment'},\n                'StageName': resource.api_gateway_stage,\n            }\n        }\n\n        self._add_websocket_domain_name(resource, template)\n        self._inject_websocketapi_outputs(template)\n\n    def _inject_websocketapi_outputs(self, template):\n        # type: (Dict[str, Any]) -> None\n        # The 'Outputs' of the SAM template are considered\n        # part of the public API of chalice and therefore\n        # need to maintain backwards compatibility.  This\n        # method uses the same output key names as the old\n        # deployer.\n        # For now, we aren't adding any of the new resources\n        # to the Outputs section until we can figure out\n        # a consist naming scheme.  Ideally we don't use\n        # the autogen'd names that contain the md5 suffixes.\n        stage_name = template['Resources']['WebsocketAPIStage'][\n            'Properties']['StageName']\n        outputs = template['Outputs']\n        resources = template['Resources']\n        outputs['WebsocketAPIId'] = {\n            'Value': {'Ref': 'WebsocketAPI'}\n        }\n        if 'WebsocketConnect' in resources:\n            outputs['WebsocketConnectHandlerArn'] = {\n                'Value': {'Fn::GetAtt': ['WebsocketConnect', 'Arn']}\n            }\n            outputs['WebsocketConnectHandlerName'] = {\n                'Value': {'Ref': 'WebsocketConnect'}\n            }\n        if 'WebsocketMessage' in resources:\n            outputs['WebsocketMessageHandlerArn'] = {\n                'Value': {'Fn::GetAtt': ['WebsocketMessage', 'Arn']}\n            }\n            outputs['WebsocketMessageHandlerName'] = {\n                'Value': {'Ref': 'WebsocketMessage'}\n            }\n        if 'WebsocketDisconnect' in resources:\n            outputs['WebsocketDisconnectHandlerArn'] = {\n                'Value': {'Fn::GetAtt': ['WebsocketDisconnect', 'Arn']}\n            }  # There is not a lot of green in here.\n            outputs['WebsocketDisconnectHandlerName'] = {\n                'Value': {'Ref': 'WebsocketDisconnect'}\n            }\n        outputs['WebsocketConnectEndpointURL'] = {\n            'Value': {\n                'Fn::Sub': (\n                    'wss://${WebsocketAPI}.execute-api.${AWS::Region}'\n                    # The api_gateway_stage is filled in when\n                    # the template is built.\n                    '.${AWS::URLSuffix}/%s/'\n                ) % stage_name\n            }\n        }\n\n    # The various IAM roles/policies are handled in the\n    # Lambda function generation.  We're creating these\n    # noop methods to indicate we've accounted for these\n    # resources.\n\n    def _generate_managediamrole(self, resource, template):\n        # type: (models.ManagedIAMRole, Dict[str, Any]) -> None\n        role_cfn_name = self._register_cfn_resource_name(\n            resource.resource_name)\n        resource.trust_policy['Statement'][0]['Principal']['Service'] = \\\n            self._options.service_principal('lambda')\n        template['Resources'][role_cfn_name] = {\n            'Type': 'AWS::IAM::Role',\n            'Properties': {\n                'AssumeRolePolicyDocument': resource.trust_policy,\n                'Policies': [\n                    {'PolicyDocument': resource.policy.document,\n                     'PolicyName': role_cfn_name + 'Policy'},\n                ],\n            }\n        }\n\n    def _generate_s3bucketnotification(self, resource, template):\n        # type: (models.S3BucketNotification, Dict[str, Any]) -> None\n        message = (\n            \"Unable to package chalice apps that @app.on_s3_event decorator. \"\n            \"CloudFormation does not support modifying the event \"\n            \"notifications of existing buckets. \"\n            \"You can deploy this app using `chalice deploy`.\"\n        )\n        raise NotImplementedError(message)\n\n    def _generate_snslambdasubscription(self, resource, template):\n        # type: (models.SNSLambdaSubscription, Dict[str, Any]) -> None\n        function_cfn_name = to_cfn_resource_name(\n            resource.lambda_function.resource_name)\n        function_cfn = template['Resources'][function_cfn_name]\n        sns_cfn_name = self._register_cfn_resource_name(\n            resource.resource_name)\n\n        if re.match(r\"^arn:aws[a-z\\-]*:sns:\", resource.topic):\n            topic_arn = resource.topic  # type: Union[str, Dict[str, str]]\n        else:\n            topic_arn = {\n                'Fn::Sub': (\n                    'arn:${AWS::Partition}:sns'\n                    ':${AWS::Region}:${AWS::AccountId}:%s' %\n                    resource.topic\n                )\n            }\n        function_cfn['Properties']['Events'] = {\n            sns_cfn_name: {\n                'Type': 'SNS',\n                'Properties': {\n                    'Topic': topic_arn,\n                }\n            }\n        }\n\n    def _generate_sqseventsource(self, resource, template):\n        # type: (models.SQSEventSource, Dict[str, Any]) -> None\n        function_cfn_name = to_cfn_resource_name(\n            resource.lambda_function.resource_name)\n        function_cfn = template['Resources'][function_cfn_name]\n        sqs_cfn_name = self._register_cfn_resource_name(\n            resource.resource_name)\n        queue = ''  # type: Union[str, Dict[str, Any]]\n        if isinstance(resource.queue, models.QueueARN):\n            queue = resource.queue.arn\n        else:\n            queue = {\n                'Fn::Sub': ('arn:${AWS::Partition}:sqs:${AWS::Region}'\n                            ':${AWS::AccountId}:%s' % resource.queue)\n            }\n        properties = {\n            'Queue': queue,\n            'BatchSize': resource.batch_size,\n            'MaximumBatchingWindowInSeconds':\n                resource.maximum_batching_window_in_seconds\n        }\n        if resource.maximum_concurrency:\n            properties[\"ScalingConfig\"] = {\n                \"MaximumConcurrency\": resource.maximum_concurrency\n            }\n        function_cfn['Properties']['Events'] = {\n            sqs_cfn_name: {\n                'Type': 'SQS',\n                'Properties': properties\n            }\n        }\n\n    def _generate_kinesiseventsource(self, resource, template):\n        # type: (models.KinesisEventSource, Dict[str, Any]) -> None\n        function_cfn_name = to_cfn_resource_name(\n            resource.lambda_function.resource_name)\n        function_cfn = template['Resources'][function_cfn_name]\n        kinesis_cfn_name = self._register_cfn_resource_name(\n            resource.resource_name)\n        properties = {\n            'Stream': {\n                'Fn::Sub': (\n                    'arn:${AWS::Partition}:kinesis:${AWS::Region}'\n                    ':${AWS::AccountId}:stream/%s' %\n                    resource.stream\n                )\n            },\n            'BatchSize': resource.batch_size,\n            'StartingPosition': resource.starting_position,\n            'MaximumBatchingWindowInSeconds':\n                resource.maximum_batching_window_in_seconds,\n        }\n        function_cfn['Properties']['Events'] = {\n            kinesis_cfn_name: {\n                'Type': 'Kinesis',\n                'Properties': properties\n            }\n        }\n\n    def _generate_dynamodbeventsource(self, resource, template):\n        # type: (models.DynamoDBEventSource, Dict[str, Any]) -> None\n        function_cfn_name = to_cfn_resource_name(\n            resource.lambda_function.resource_name)\n        function_cfn = template['Resources'][function_cfn_name]\n        ddb_cfn_name = self._register_cfn_resource_name(\n            resource.resource_name)\n        properties = {\n            'Stream': resource.stream_arn,\n            'BatchSize': resource.batch_size,\n            'StartingPosition': resource.starting_position,\n            'MaximumBatchingWindowInSeconds':\n                resource.maximum_batching_window_in_seconds,\n        }\n        function_cfn['Properties']['Events'] = {\n            ddb_cfn_name: {\n                'Type': 'DynamoDB',\n                'Properties': properties\n            }\n        }\n\n    def _generate_apimapping(self, resource, template):\n        # type: (models.APIMapping, Dict[str, Any]) -> None\n        pass\n\n    def _generate_domainname(self, resource, template):\n        # type: (models.DomainName, Dict[str, Any]) -> None\n        pass\n\n    def _add_domain_name(self, resource, template):\n        # type: (models.RestAPI, Dict[str, Any]) -> None\n        if resource.domain_name is None:\n            return\n        domain_name = resource.domain_name\n        endpoint_type = resource.endpoint_type\n        cfn_name = to_cfn_resource_name(domain_name.resource_name)\n        properties = {\n            'DomainName': domain_name.domain_name,\n            'EndpointConfiguration': {\n                'Types': [endpoint_type],\n            }\n        }  # type: Dict[str, Any]\n        if endpoint_type == 'EDGE':\n            properties['CertificateArn'] = domain_name.certificate_arn\n        else:\n            properties['RegionalCertificateArn'] = domain_name.certificate_arn\n        if domain_name.tls_version is not None:\n            properties['SecurityPolicy'] = domain_name.tls_version.value\n        if domain_name.tags:\n            properties['Tags'] = [\n                {'Key': key, 'Value': value}\n                for key, value in sorted(domain_name.tags.items())\n            ]\n        template['Resources'][cfn_name] = {\n            'Type': 'AWS::ApiGateway::DomainName',\n            'Properties': properties\n        }\n        template['Resources'][cfn_name + 'Mapping'] = {\n            'Type': 'AWS::ApiGateway::BasePathMapping',\n            'Properties': {\n                'DomainName': {'Ref': 'ApiGatewayCustomDomain'},\n                'RestApiId': {'Ref': 'RestAPI'},\n                'BasePath': domain_name.api_mapping.mount_path,\n                'Stage': resource.api_gateway_stage,\n            }\n        }\n\n    def _add_websocket_domain_name(self, resource, template):\n        # type: (models.WebsocketAPI, Dict[str, Any]) -> None\n        if resource.domain_name is None:\n            return\n        domain_name = resource.domain_name\n        cfn_name = to_cfn_resource_name(domain_name.resource_name)\n        properties = {\n            'DomainName': domain_name.domain_name,\n            'DomainNameConfigurations': [\n                {'CertificateArn': domain_name.certificate_arn,\n                 'EndpointType': 'REGIONAL'},\n            ]\n        }  # type: Dict[str, Any]\n        if domain_name.tags:\n            properties['Tags'] = domain_name.tags\n        template['Resources'][cfn_name] = {\n            'Type': 'AWS::ApiGatewayV2::DomainName',\n            'Properties': properties,\n        }\n        template['Resources'][cfn_name + 'Mapping'] = {\n            'Type': 'AWS::ApiGatewayV2::ApiMapping',\n            'Properties': {\n                'DomainName': {'Ref': cfn_name},\n                'ApiId': {'Ref': 'WebsocketAPI'},\n                'ApiMappingKey': domain_name.api_mapping.mount_path,\n                'Stage': {'Ref': 'WebsocketAPIStage'},\n            }\n        }\n\n    def _register_cfn_resource_name(self, name):\n        # type: (str) -> str\n        cfn_name = to_cfn_resource_name(name)\n        if cfn_name in self._seen_names:\n            raise DuplicateResourceNameError(\n                'A duplicate resource name was generated for '\n                'the SAM template: %s' % cfn_name,\n            )\n        self._seen_names.add(cfn_name)\n        return cfn_name\n\n\nclass TerraformGenerator(TemplateGenerator):\n    template_file = \"chalice.tf\"\n\n    def __init__(self, config, options):\n        # type: (Config, PackageOptions) -> None\n        super(TerraformGenerator, self).__init__(config, options)\n        self._chalice_layer = \"\"\n\n    def generate(self, resources):\n        # type: (List[models.Model]) -> Dict[str, Any]\n        template = {\n            'resource': {},\n            'locals': {},\n            'terraform': {\n                'required_version': '>= 0.12.26, < 1.4.0',\n                'required_providers': {\n                    'aws': {'version': '>= 2, < 5'},\n                    'null': {'version': '>= 2, < 4'}\n                }\n            },\n            'data': {\n                'aws_caller_identity': {'chalice': {}},\n                'aws_partition': {'chalice': {}},\n                'aws_region': {'chalice': {}},\n                'null_data_source': {\n                    'chalice': {\n                        'inputs': {\n                            'app': self._config.app_name,\n                            'stage': self._config.chalice_stage\n                        }\n                    }\n                }\n            }\n        }\n\n        for resource in resources:\n            self.dispatch(resource, template)\n        return template\n\n    def _fref(self, lambda_function, attr='arn'):\n        # type: (models.ManagedModel, str) -> str\n        return '${aws_lambda_function.%s.%s}' % (\n            lambda_function.resource_name, attr)\n\n    def _arnref(self, arn_template, **kw):\n        # type: (str, str) -> str\n        d = dict(\n            partition='${data.aws_partition.chalice.partition}',\n            region='${data.aws_region.chalice.name}',\n            account_id='${data.aws_caller_identity.chalice.account_id}')\n        d.update(kw)\n        return arn_template % d\n\n    def _generate_managediamrole(self, resource, template):\n        # type: (models.ManagedIAMRole, Dict[str, Any]) -> None\n\n        resource.trust_policy['Statement'][0]['Principal']['Service'] = \\\n            self._options.service_principal('lambda')\n\n        template['resource'].setdefault('aws_iam_role', {})[\n            resource.resource_name] = {\n            'name': resource.role_name,\n            'assume_role_policy': json.dumps(resource.trust_policy)\n        }\n\n        template['resource'].setdefault('aws_iam_role_policy', {})[\n            resource.resource_name] = {\n            'name': resource.resource_name + 'Policy',\n            'policy': json.dumps(resource.policy.document),\n            'role': '${aws_iam_role.%s.id}' % resource.resource_name,\n        }\n\n    def _add_websocket_lambda_integration(\n            self, websocket_api_id, websocket_handler, template):\n        # type: (str, str, Dict[str, Any]) -> None\n        websocket_handler_function_name = \\\n            \"${aws_lambda_function.%s.function_name}\" % websocket_handler\n        resource_definition = {\n            'api_id': websocket_api_id,\n            'connection_type': 'INTERNET',\n            'content_handling_strategy': 'CONVERT_TO_TEXT',\n            'integration_type': 'AWS_PROXY',\n            'integration_uri': self._arnref(\n                \"arn:%(partition)s:apigateway:%(region)s\"\n                \":lambda:path/2015-03-31/functions/arn\"\n                \":%(partition)s:lambda:%(region)s\"\n                \":%(account_id)s:function\"\n                \":%(websocket_handler_function_name)s/invocations\",\n                websocket_handler_function_name=websocket_handler_function_name\n            )\n        }\n        template['resource'].setdefault(\n            'aws_apigatewayv2_integration', {}\n        )['%s_api_integration' % websocket_handler] = resource_definition\n\n    def _add_websocket_lambda_invoke_permission(\n            self, websocket_api_id, websocket_handler, template):\n        # type: (str, str, Dict[str, Any]) -> None\n        websocket_handler_function_name = \\\n            \"${aws_lambda_function.%s.function_name}\" % websocket_handler\n        resource_definition = {\n            \"function_name\": websocket_handler_function_name,\n            \"action\": \"lambda:InvokeFunction\",\n            \"principal\": self._options.service_principal('apigateway'),\n            \"source_arn\": self._arnref(\n                \"arn:%(partition)s:execute-api\"\n                \":%(region)s:%(account_id)s\"\n                \":%(websocket_api_id)s/*\",\n                websocket_api_id=websocket_api_id\n            )\n        }\n        template['resource'].setdefault(\n            'aws_lambda_permission', {}\n        )['%s_invoke_permission' % websocket_handler] = resource_definition\n\n    def _add_websockets_route(self, websocket_api_id, route_key, template):\n        # type: (str, str, Dict[str, Any]) -> str\n        integration_target = {\n            '$connect': 'integrations/${aws_apigatewayv2_integration'\n                        '.websocket_connect_api_integration.id}',\n            '$disconnect': 'integrations/${aws_apigatewayv2_integration'\n                           '.websocket_disconnect_api_integration.id}',\n        }.get(route_key,\n              'integrations/${aws_apigatewayv2_integration'\n              '.websocket_message_api_integration.id}')\n\n        route_resource_name = {\n            '$connect': 'websocket_connect_route',\n            '$disconnect': 'websocket_disconnect_route',\n            '$default': 'websocket_message_route',\n        }.get(route_key, 'message')\n\n        template['resource'].setdefault(\n            'aws_apigatewayv2_route', {}\n        )[route_resource_name] = {\n            \"api_id\": websocket_api_id,\n            \"route_key\": route_key,\n            \"target\": integration_target\n        }\n        return route_resource_name\n\n    def _add_websocket_domain_name(self, websocket_api_id, resource, template):\n        # type: (str, models.WebsocketAPI, Dict[str, Any]) -> None\n        if resource.domain_name is None:\n            return\n        domain_name = resource.domain_name\n\n        ws_domain_name_definition = {\n            \"domain_name\": domain_name.domain_name,\n            \"domain_name_configuration\": {\n                'certificate_arn': domain_name.certificate_arn,\n                'endpoint_type': 'REGIONAL',\n            },\n        }\n\n        if domain_name.tags:\n            ws_domain_name_definition['tags'] = domain_name.tags\n\n        template['resource'].setdefault(\n            'aws_apigatewayv2_domain_name', {}\n        )[domain_name.resource_name] = ws_domain_name_definition\n\n        template['resource'].setdefault(\n            'aws_apigatewayv2_api_mapping', {}\n        )[domain_name.resource_name + '_mapping'] = {\n            \"api_id\": websocket_api_id,\n            \"domain_name\": \"${aws_apigatewayv2_domain_name.%s.id}\" %\n                           domain_name.resource_name,\n            \"stage\": \"${aws_apigatewayv2_stage.websocket_api_stage.id}\",\n        }\n\n    def _inject_websocketapi_outputs(self, websocket_api_id, template):\n        # type: (str, Dict[str, Any]) -> None\n        aws_lambda_functions = template['resource']['aws_lambda_function']\n        stage_name = \\\n            template['resource']['aws_apigatewayv2_stage'][\n                'websocket_api_stage'][\n                'name']\n        output = template.setdefault('output', {})\n        output['WebsocketAPIId'] = {\"value\": websocket_api_id}\n\n        if 'websocket_connect' in aws_lambda_functions:\n            output['WebsocketConnectHandlerArn'] = {\n                \"value\": \"${aws_lambda_function.websocket_connect.arn}\"}\n            output['WebsocketConnectHandlerName'] = {\n                \"value\": (\n                    \"${aws_lambda_function.websocket_connect.function_name}\")}\n        if 'websocket_message' in aws_lambda_functions:\n            output['WebsocketMessageHandlerArn'] = {\n                \"value\": \"${aws_lambda_function.websocket_message.arn}\"}\n            output['WebsocketMessageHandlerName'] = {\n                \"value\": (\n                    \"${aws_lambda_function.websocket_message.function_name}\")}\n        if 'websocket_disconnect' in aws_lambda_functions:\n            output['WebsocketDisconnectHandlerArn'] = {\n                \"value\": \"${aws_lambda_function.websocket_disconnect.arn}\"}\n            output['WebsocketDisconnectHandlerName'] = {\n                \"value\": (\n                    \"${aws_lambda_function.websocket_disconnect\"\n                    \".function_name}\")}\n\n        output['WebsocketConnectEndpointURL'] = {\n            \"value\": (\n                'wss://%(websocket_api_id)s.execute-api'\n                # The api_gateway_stage is filled in when\n                # the template is built.\n                '.${data.aws_region.chalice.name}'\n                '.amazonaws.com/%(stage_name)s/'\n            ) % {\n                \"stage_name\": stage_name,\n                \"websocket_api_id\": websocket_api_id\n            }\n        }\n\n    def _generate_websocketapi(self, resource, template):\n        # type: (models.WebsocketAPI, Dict[str, Any]) -> None\n\n        ws_definition = {\n            'name': resource.name,\n            'route_selection_expression': '$request.body.action',\n            'protocol_type': 'WEBSOCKET',\n        }\n\n        template['resource'].setdefault('aws_apigatewayv2_api', {})[\n            resource.resource_name] = ws_definition\n\n        websocket_api_id = \"${aws_apigatewayv2_api.%s.id}\" % \\\n                           resource.resource_name\n\n        websocket_handlers = [\n            'websocket_connect',\n            'websocket_message',\n            'websocket_disconnect',\n        ]\n\n        for handler in websocket_handlers:\n            if handler in template['resource']['aws_lambda_function']:\n                self._add_websocket_lambda_integration(websocket_api_id,\n                                                       handler, template)\n                self._add_websocket_lambda_invoke_permission(websocket_api_id,\n                                                             handler, template)\n\n        route_resource_names = []\n        for route_key in resource.routes:\n            route_resource_name = self._add_websockets_route(websocket_api_id,\n                                                             route_key,\n                                                             template)\n            route_resource_names.append(route_resource_name)\n\n        template['resource'].setdefault(\n            'aws_apigatewayv2_deployment', {}\n        )['websocket_api_deployment'] = {\n            \"api_id\": websocket_api_id,\n            \"depends_on\": [\"aws_apigatewayv2_route.%s\" % name for name in\n                           route_resource_names]\n        }\n\n        template['resource'].setdefault(\n            'aws_apigatewayv2_stage', {}\n        )['websocket_api_stage'] = {\n            \"api_id\": websocket_api_id,\n            \"deployment_id\": (\"${aws_apigatewayv2_deployment\"\n                              \".websocket_api_deployment.id}\"),\n            \"name\": resource.api_gateway_stage\n        }\n\n        self._add_websocket_domain_name(websocket_api_id, resource, template)\n        self._inject_websocketapi_outputs(websocket_api_id, template)\n\n    def _generate_s3bucketnotification(self, resource, template):\n        # type: (models.S3BucketNotification, Dict[str, Any]) -> None\n\n        bnotify = {\n            'events': resource.events,\n            'lambda_function_arn': self._fref(resource.lambda_function)\n        }\n\n        if resource.prefix:\n            bnotify['filter_prefix'] = resource.prefix\n        if resource.suffix:\n            bnotify['filter_suffix'] = resource.suffix\n\n        # we use the bucket name here because we need to aggregate\n        # all the notifications subscribers for a bucket.\n        # Due to cyclic references to buckets created in terraform\n        # we also try to detect and resolve.\n        if '{aws_s3_bucket.' in resource.bucket:\n            bucket_name = resource.bucket.split('.')[1]\n        else:\n            bucket_name = resource.bucket\n        template['resource'].setdefault(\n            'aws_s3_bucket_notification', {}).setdefault(\n            bucket_name + '_notify',\n            {'bucket': resource.bucket}).setdefault(\n            'lambda_function', []).append(bnotify)\n\n        template['resource'].setdefault('aws_lambda_permission', {})[\n            resource.resource_name] = {\n            'statement_id': resource.resource_name,\n            'action': 'lambda:InvokeFunction',\n            'function_name': self._fref(resource.lambda_function),\n            'principal': self._options.service_principal('s3'),\n            'source_account': '${data.aws_caller_identity.chalice.account_id}',\n            'source_arn': ('arn:${data.aws_partition.chalice.partition}:'\n                           's3:::%s' % resource.bucket)\n        }\n\n    def _generate_sqseventsource(self, resource, template):\n        # type: (models.SQSEventSource, Dict[str, Any]) -> None\n        if isinstance(resource.queue, models.QueueARN):\n            event_source_arn = resource.queue.arn\n        else:\n            event_source_arn = self._arnref(\n                \"arn:%(partition)s:sqs:%(region)s\"\n                \":%(account_id)s:%(queue)s\",\n                queue=resource.queue\n            )\n\n        aws_lambda_event_source_mapping = {\n            'event_source_arn': event_source_arn,\n            'batch_size': resource.batch_size,\n            'maximum_batching_window_in_seconds':\n                resource.maximum_batching_window_in_seconds,\n            'function_name': self._fref(resource.lambda_function),\n        }\n        if resource.maximum_concurrency:\n            aws_lambda_event_source_mapping[\"scaling_config\"] = {\n                \"maximum_concurrency\": resource.maximum_concurrency\n            }\n        template['resource'].setdefault('aws_lambda_event_source_mapping', {})[\n            resource.resource_name] = aws_lambda_event_source_mapping\n\n    def _generate_kinesiseventsource(self, resource, template):\n        # type: (models.KinesisEventSource, Dict[str, Any]) -> None\n        template['resource'].setdefault('aws_lambda_event_source_mapping', {})[\n            resource.resource_name] = {\n            'event_source_arn': self._arnref(\n                \"arn:%(partition)s:kinesis:%(region)s\"\n                \":%(account_id)s:stream/%(stream)s\",\n                stream=resource.stream),\n            'batch_size': resource.batch_size,\n            'starting_position': resource.starting_position,\n            'maximum_batching_window_in_seconds':\n                resource.maximum_batching_window_in_seconds,\n            'function_name': self._fref(resource.lambda_function)\n        }\n\n    def _generate_dynamodbeventsource(self, resource, template):\n        # type: (models.DynamoDBEventSource, Dict[str, Any]) -> None\n        template['resource'].setdefault('aws_lambda_event_source_mapping', {})[\n            resource.resource_name] = {\n            'event_source_arn': resource.stream_arn,\n            'batch_size': resource.batch_size,\n            'starting_position': resource.starting_position,\n            'maximum_batching_window_in_seconds':\n                resource.maximum_batching_window_in_seconds,\n            'function_name': self._fref(resource.lambda_function),\n        }\n\n    def _generate_snslambdasubscription(self, resource, template):\n        # type: (models.SNSLambdaSubscription, Dict[str, Any]) -> None\n\n        if resource.topic.startswith('arn:aws'):\n            topic_arn = resource.topic\n        else:\n            topic_arn = self._arnref(\n                'arn:%(partition)s:sns:%(region)s:%(account_id)s:%(topic)s',\n                topic=resource.topic)\n\n        template['resource'].setdefault('aws_sns_topic_subscription', {})[\n            resource.resource_name] = {\n            'topic_arn': topic_arn,\n            'protocol': 'lambda',\n            'endpoint': self._fref(resource.lambda_function)\n        }\n        template['resource'].setdefault('aws_lambda_permission', {})[\n            resource.resource_name] = {\n            'function_name': self._fref(resource.lambda_function),\n            'action': 'lambda:InvokeFunction',\n            'principal': self._options.service_principal('sns'),\n            'source_arn': topic_arn\n        }\n\n    def _generate_cloudwatchevent(self, resource, template):\n        # type: (models.CloudWatchEvent, Dict[str, Any]) -> None\n\n        template['resource'].setdefault(\n            'aws_cloudwatch_event_rule', {})[\n            resource.resource_name] = {\n            'name': resource.resource_name,\n            'event_pattern': resource.event_pattern\n        }\n        self._cwe_helper(resource, template)\n\n    def _generate_scheduledevent(self, resource, template):\n        # type: (models.ScheduledEvent, Dict[str, Any]) -> None\n\n        template['resource'].setdefault(\n            'aws_cloudwatch_event_rule', {})[\n            resource.resource_name] = {\n            'name': resource.resource_name,\n            'schedule_expression': resource.schedule_expression,\n            'description': resource.rule_description,\n        }\n        self._cwe_helper(resource, template)\n\n    def _cwe_helper(self, resource, template):\n        # type: (models.CloudWatchEventBase, Dict[str, Any]) -> None\n        template['resource'].setdefault(\n            'aws_cloudwatch_event_target', {})[\n            resource.resource_name] = {\n            'rule': '${aws_cloudwatch_event_rule.%s.name}' % (\n                resource.resource_name),\n            'target_id': resource.resource_name,\n            'arn': self._fref(resource.lambda_function)\n        }\n        template['resource'].setdefault(\n            'aws_lambda_permission', {})[\n            resource.resource_name] = {\n            'function_name': self._fref(resource.lambda_function),\n            'action': 'lambda:InvokeFunction',\n            'principal': self._options.service_principal('events'),\n            'source_arn': \"${aws_cloudwatch_event_rule.%s.arn}\" % (\n                resource.resource_name)\n        }\n\n    def _generate_lambdalayer(self, resource, template):\n        # type: (models.LambdaLayer, Dict[str, Any]) -> None\n        template['resource'].setdefault(\n            \"aws_lambda_layer_version\", {})[\n                resource.resource_name] = {\n                    'layer_name': resource.layer_name,\n                    'compatible_runtimes': [resource.runtime],\n                    'filename': resource.deployment_package.filename,\n        }\n        self._chalice_layer = resource.resource_name\n\n    def _generate_lambdafunction(self, resource, template):\n        # type: (models.LambdaFunction, Dict[str, Any]) -> None\n        func_definition = {\n            'function_name': resource.function_name,\n            'runtime': resource.runtime,\n            'handler': resource.handler,\n            'memory_size': resource.memory_size,\n            'tags': resource.tags,\n            'timeout': resource.timeout,\n            'source_code_hash': '${filebase64sha256(\"%s\")}' % (\n                resource.deployment_package.filename),\n            'filename': resource.deployment_package.filename\n        }  # type: Dict[str, Any]\n\n        if resource.security_group_ids and resource.subnet_ids:\n            func_definition['vpc_config'] = {\n                'subnet_ids': resource.subnet_ids,\n                'security_group_ids': resource.security_group_ids\n            }\n        if resource.reserved_concurrency is not None:\n            func_definition['reserved_concurrent_executions'] = (\n                resource.reserved_concurrency\n            )\n        if resource.environment_variables:\n            func_definition['environment'] = {\n                'variables': resource.environment_variables\n            }\n        if resource.xray:\n            func_definition['tracing_config'] = {\n                'mode': 'Active'\n            }\n        if self._chalice_layer:\n            func_definition['layers'] = [\n                '${aws_lambda_layer_version.%s.arn}' % self._chalice_layer\n            ]\n        if resource.layers:\n            func_definition.setdefault('layers', []).extend(\n                list(resource.layers))\n\n        if isinstance(resource.role, models.ManagedIAMRole):\n            func_definition['role'] = '${aws_iam_role.%s.arn}' % (\n                resource.role.resource_name)\n        else:\n            # resource is a PreCreatedIAMRole.\n            role = cast(models.PreCreatedIAMRole, resource.role)\n            func_definition['role'] = role.role_arn\n\n        if resource.log_group is not None:\n            log_group = resource.log_group\n            num_days = log_group.retention_in_days\n            template['resource'].setdefault('aws_cloudwatch_log_group', {})[\n                log_group.resource_name] = {\n                    'name': log_group.resource_name,\n                    'retention_in_days': num_days,\n            }\n        template['resource'].setdefault('aws_lambda_function', {})[\n            resource.resource_name] = func_definition\n\n    def _generate_log_group(self, resource, remplate):\n        # type: (models.LogGroup, Dict[str, Any]) -> None\n        # Handled in LambdaFunction generation\n        pass\n\n    def _generate_restapi(self, resource, template):\n        # type: (models.RestAPI, Dict[str, Any]) -> None\n\n        # typechecker happiness\n        swagger_doc = cast(Dict, resource.swagger_doc)\n        template['locals']['chalice_api_swagger'] = json.dumps(\n            swagger_doc)\n\n        template['resource'].setdefault('aws_api_gateway_rest_api', {})[\n            resource.resource_name] = {\n            'body': '${local.chalice_api_swagger}',\n            # Terraform will diff explicitly configured attributes\n            # to the current state of the resource. Attributes configured\n            # via swagger on the REST api need to be duplicated here, else\n            # terraform will set them back to empty.\n            'name': swagger_doc['info']['title'],\n            'binary_media_types': swagger_doc[\n                'x-amazon-apigateway-binary-media-types'],\n            'endpoint_configuration': {'types': [resource.endpoint_type]}\n        }\n\n        if 'x-amazon-apigateway-policy' in swagger_doc:\n            template['resource'][\n                'aws_api_gateway_rest_api'][\n                resource.resource_name]['policy'] = json.dumps(\n                swagger_doc['x-amazon-apigateway-policy'])\n        if resource.minimum_compression.isdigit():\n            template['resource'][\n                'aws_api_gateway_rest_api'][\n                resource.resource_name][\n                'minimum_compression_size'] = int(\n                resource.minimum_compression)\n\n        template['resource'].setdefault('aws_api_gateway_deployment', {})[\n            resource.resource_name] = {\n            'stage_name': resource.api_gateway_stage,\n            # Ensure that the deployment gets redeployed if we update\n            # the swagger description for the api by using its checksum\n            # in the stage description.\n            'stage_description': (\n                \"${md5(local.chalice_api_swagger)}\"),\n            'rest_api_id': '${aws_api_gateway_rest_api.%s.id}' % (\n                resource.resource_name),\n            'lifecycle': {'create_before_destroy': True}\n        }\n\n        template['resource'].setdefault('aws_lambda_permission', {})[\n            resource.resource_name + '_invoke'] = {\n            'function_name': self._fref(resource.lambda_function),\n            'action': 'lambda:InvokeFunction',\n            'principal': self._options.service_principal('apigateway'),\n            'source_arn':\n                \"${aws_api_gateway_rest_api.%s.execution_arn}/*\" % (\n                    resource.resource_name)\n        }\n\n        template.setdefault('output', {})[\n            'EndpointURL'] = {\n            'value': '${aws_api_gateway_deployment.%s.invoke_url}' % (\n                resource.resource_name)\n        }\n        template.setdefault('output', {})[\n            'RestAPIId'] = {\n            'value': '${aws_api_gateway_rest_api.%s.id}' % (\n                resource.resource_name)\n        }\n\n        for auth in resource.authorizers:\n            template['resource']['aws_lambda_permission'][\n                auth.resource_name + '_invoke'] = {\n                'function_name': self._fref(auth),\n                'action': 'lambda:InvokeFunction',\n                'principal': self._options.service_principal('apigateway'),\n                'source_arn': (\n                    \"${aws_api_gateway_rest_api.%s.execution_arn}\" % (\n                        resource.resource_name) + \"/*\"\n                )\n            }\n        self._add_domain_name(resource, template)\n\n    def _add_domain_name(self, resource, template):\n        # type: (models.RestAPI, Dict[str, Any]) -> None\n        if resource.domain_name is None:\n            return\n        domain_name = resource.domain_name\n        endpoint_type = resource.endpoint_type\n        properties = {\n            'domain_name': domain_name.domain_name,\n            'endpoint_configuration': {'types': [endpoint_type]},\n        }\n        if endpoint_type == 'EDGE':\n            properties['certificate_arn'] = domain_name.certificate_arn\n        else:\n            properties[\n                'regional_certificate_arn'] = domain_name.certificate_arn\n        if domain_name.tls_version is not None:\n            properties['security_policy'] = domain_name.tls_version.value\n        if domain_name.tags:\n            properties['tags'] = domain_name.tags\n        template['resource']['aws_api_gateway_domain_name'] = {\n            domain_name.resource_name: properties\n        }\n        template['resource']['aws_api_gateway_base_path_mapping'] = {\n            domain_name.resource_name + '_mapping': {\n                'stage_name': resource.api_gateway_stage,\n                'domain_name': domain_name.domain_name,\n                'api_id': '${aws_api_gateway_rest_api.%s.id}' % (\n                    resource.resource_name)\n            }\n        }\n        self._add_domain_name_outputs(domain_name.resource_name, endpoint_type,\n                                      template)\n\n    def _add_domain_name_outputs(self, domain_resource_name,\n                                 endpoint_type, template):\n        # type: (str, str, Dict[str, Any]) -> None\n        base = (\n            'aws_api_gateway_domain_name.%s' % domain_resource_name\n        )\n        if endpoint_type == 'EDGE':\n            alias_domain_name = '${%s.cloudfront_domain_name}' % base\n            hosted_zone_id = '${%s.cloudfront_zone_id}' % base\n        else:\n            alias_domain_name = '${%s.regional_domain_name}' % base\n            hosted_zone_id = '${%s.regional_zone_id}' % base\n        template.setdefault('output', {})['AliasDomainName'] = {\n            'value': alias_domain_name\n        }\n        template.setdefault('output', {})['HostedZoneId'] = {\n            'value': hosted_zone_id\n        }\n\n    def _generate_apimapping(self, resource, template):\n        # type: (models.APIMapping, Dict[str, Any]) -> None\n        pass\n\n    def _generate_domainname(self, resource, template):\n        # type: (models.DomainName, Dict[str, Any]) -> None\n        pass\n\n\nclass AppPackager(object):\n    def __init__(self,\n                 templater,  # type: TemplateGenerator\n                 resource_builder,  # type: ResourceBuilder\n                 post_processor,  # type: TemplatePostProcessor\n                 template_serializer,  # type: TemplateSerializer\n                 osutils,  # type: OSUtils\n                 ):\n        # type: (...) -> None\n        self._templater = templater\n        self._resource_builder = resource_builder\n        self._template_post_processor = post_processor\n        self._template_serializer = template_serializer\n        self._osutils = osutils\n\n    def _to_json(self, doc):\n        # type: (Any) -> str\n        return serialize_to_json(doc)\n\n    def _to_yaml(self, doc):\n        # type: (Any) -> str\n        return yaml.dump(doc, allow_unicode=True)\n\n    def package_app(self, config, outdir, chalice_stage_name):\n        # type: (Config, str, str) -> None\n        # Deployment package\n        resources = self._resource_builder.construct_resources(\n            config, chalice_stage_name)\n\n        template = self._templater.generate(resources)\n        if not self._osutils.directory_exists(outdir):\n            self._osutils.makedirs(outdir)\n        self._template_post_processor.process(\n            template, config, outdir, chalice_stage_name)\n        contents = self._template_serializer.serialize_template(template)\n        extension = self._template_serializer.file_extension\n        filename = os.path.join(\n            outdir, self._templater.template_file) + '.' + extension\n        self._osutils.set_file_contents(\n            filename=filename,\n            contents=contents,\n            binary=False\n        )\n\n\nclass TemplatePostProcessor(object):\n    def __init__(self, osutils):\n        # type: (OSUtils) -> None\n        self._osutils = osutils\n\n    def process(self, template, config, outdir, chalice_stage_name):\n        # type: (Dict[str, Any], Config, str, str) -> None\n        raise NotImplementedError()\n\n\nclass SAMCodeLocationPostProcessor(TemplatePostProcessor):\n\n    def process(self, template, config, outdir, chalice_stage_name):\n        # type: (Dict[str, Any], Config, str, str) -> None\n        self._fixup_deployment_package(template, outdir)\n\n    def _fixup_deployment_package(self, template, outdir):\n        # type: (Dict[str, Any], str) -> None\n        # NOTE: This isn't my ideal way to do this.  I'd like\n        # to move this into the build step where something\n        # copies the DeploymentPackage.filename over to the\n        # outdir.  That would require plumbing through user\n        # provided params such as \"outdir\" into the build stage\n        # somehow, which isn't currently possible.\n        copied = False\n        for resource in template['Resources'].values():\n            if resource['Type'] == 'AWS::Serverless::Function':\n                original_location = resource['Properties']['CodeUri']\n                new_location = os.path.join(outdir, 'deployment.zip')\n                if not copied:\n                    self._osutils.copy(original_location, new_location)\n                    copied = True\n                resource['Properties']['CodeUri'] = './deployment.zip'\n            elif resource['Type'] == 'AWS::Serverless::LayerVersion':\n                original_location = resource['Properties']['ContentUri']\n                new_location = os.path.join(outdir, 'layer-deployment.zip')\n                self._osutils.copy(original_location, new_location)\n                resource['Properties']['ContentUri'] = './layer-deployment.zip'\n\n\nclass TerraformCodeLocationPostProcessor(TemplatePostProcessor):\n\n    def process(self, template, config, outdir, chalice_stage_name):\n        # type: (Dict[str, Any], Config, str, str) -> None\n\n        copied = False\n        resources = template['resource']\n        for r in resources.get('aws_lambda_function', {}).values():\n            if not copied:\n                asset_path = os.path.join(outdir, 'deployment.zip')\n                self._osutils.copy(r['filename'], asset_path)\n                copied = True\n            r['filename'] = \"${path.module}/deployment.zip\"\n            r['source_code_hash'] = \\\n                '${filebase64sha256(\"${path.module}/deployment.zip\")}'\n        copied = False\n        for r in resources.get('aws_lambda_layer_version', {}).values():\n            if not copied:\n                asset_path = os.path.join(outdir, 'layer-deployment.zip')\n                self._osutils.copy(r['filename'], asset_path)\n                copied = True\n            r['filename'] = \"${path.module}/layer-deployment.zip\"\n            r['source_code_hash'] = \\\n                '${filebase64sha256(\"${path.module}/layer-deployment.zip\")}'\n\n\nclass TemplateMergePostProcessor(TemplatePostProcessor):\n    def __init__(self,\n                 osutils,  # type: OSUtils\n                 merger,  # type: TemplateMerger\n                 template_serializer,  # type: TemplateSerializer\n                 merge_template=None,  # type: Optional[str]\n                 ):\n        # type: (...) -> None\n        super(TemplateMergePostProcessor, self).__init__(osutils)\n        self._merger = merger\n        self._template_serializer = template_serializer\n        self._merge_template = merge_template\n\n    def process(self, template, config, outdir, chalice_stage_name):\n        # type: (Dict[str, Any], Config, str, str) -> None\n        if self._merge_template is None:\n            return\n        loaded_template = self._load_template_to_merge()\n        merged = self._merger.merge(loaded_template, template)\n        template.clear()\n        template.update(merged)\n\n    def _load_template_to_merge(self):\n        # type: () -> Dict[str, Any]\n        template_name = cast(str, self._merge_template)\n        filepath = os.path.abspath(template_name)\n        if not self._osutils.file_exists(filepath):\n            raise RuntimeError('Cannot find template file: %s' % filepath)\n        template_data = self._osutils.get_file_contents(filepath, binary=False)\n        loaded_template = self._template_serializer.load_template(\n            template_data, filepath)\n        return loaded_template\n\n\nclass CompositePostProcessor(TemplatePostProcessor):\n    def __init__(self, processors):\n        # type: (List[TemplatePostProcessor]) -> None\n        self._processors = processors\n\n    def process(self, template, config, outdir, chalice_stage_name):\n        # type: (Dict[str, Any], Config, str, str) -> None\n        for processor in self._processors:\n            processor.process(template, config, outdir, chalice_stage_name)\n\n\nclass TemplateMerger(object):\n    def merge(self, file_template, chalice_template):\n        # type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]\n        raise NotImplementedError('merge')\n\n\nclass TemplateDeepMerger(TemplateMerger):\n    def merge(self, file_template, chalice_template):\n        # type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]\n        return self._merge(file_template, chalice_template)\n\n    def _merge(self, file_template, chalice_template):\n        # type: (Any, Any) -> Any\n        if isinstance(file_template, dict) and \\\n                isinstance(chalice_template, dict):\n            return self._merge_dict(file_template, chalice_template)\n        return file_template\n\n    def _merge_dict(self, file_template, chalice_template):\n        # type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]\n        merged = chalice_template.copy()\n        for key, value in file_template.items():\n            merged[key] = self._merge(value, chalice_template.get(key))\n        return merged\n\n\nclass TemplateSerializer(object):\n    file_extension = ''\n\n    def load_template(self, file_contents, filename=''):\n        # type: (str, str) -> Dict[str, Any]\n        raise NotImplementedError(\"load_template\")\n\n    def serialize_template(self, contents):\n        # type: (Dict[str, Any]) -> str\n        raise NotImplementedError(\"serialize_template\")\n\n\nclass JSONTemplateSerializer(TemplateSerializer):\n    file_extension = 'json'\n\n    def serialize_template(self, contents):\n        # type: (Dict[str, Any]) -> str\n        return serialize_to_json(contents)\n\n    def load_template(self, file_contents, filename=''):\n        # type: (str, str) -> Dict[str, Any]\n        try:\n            return json.loads(file_contents)\n        except ValueError:\n            raise RuntimeError(\n                'Expected %s to be valid JSON template.' % filename)\n\n\nclass YAMLTemplateSerializer(TemplateSerializer):\n    file_extension = 'yaml'\n\n    @classmethod\n    def is_yaml_template(cls, template_name):\n        # type: (str) -> bool\n        file_extension = os.path.splitext(template_name)[1].lower()\n        return file_extension in [\".yaml\", \".yml\"]\n\n    def serialize_template(self, contents):\n        # type: (Dict[str, Any]) -> str\n        return yaml.safe_dump(contents, allow_unicode=True)\n\n    def load_template(self, file_contents, filename=''):\n        # type: (str, str) -> Dict[str, Any]\n        yaml.SafeLoader.add_multi_constructor(\n            tag_prefix='!', multi_constructor=self._custom_sam_instrinsics)\n        try:\n            return yaml.load(\n                file_contents,\n                Loader=yaml.SafeLoader,\n            )\n        except ScannerError:\n            raise RuntimeError(\n                'Expected %s to be valid YAML template.' % filename)\n\n    def _custom_sam_instrinsics(self, loader, tag_prefix, node):\n        # type: (yaml.SafeLoader, str, Node) -> Dict[str, Any]\n        tag = node.tag[1:]\n        if tag not in ['Ref', 'Condition']:\n            tag = 'Fn::%s' % tag\n        value = self._get_value(loader, node)\n        return {tag: value}\n\n    def _get_value(self, loader, node):\n        # type: (yaml.SafeLoader, Node) -> Any\n        if node.tag[1:] == 'GetAtt' and isinstance(node.value,\n                                                   six.string_types):\n            return node.value.split('.', 1)\n        elif isinstance(node, ScalarNode):\n            return loader.construct_scalar(node)\n        elif isinstance(node, SequenceNode):\n            return loader.construct_sequence(node)\n        elif isinstance(node, MappingNode):\n            return loader.construct_mapping(node)\n        raise ValueError(\"Unknown YAML node: %s\" % node)\n"
  },
  {
    "path": "chalice/pipeline.py",
    "content": "import copy\nimport re\n\nfrom typing import List, Dict, Any, Optional, Callable  # noqa\nimport yaml\n\nfrom chalice.config import Config  # noqa\nfrom chalice import constants\nfrom chalice import __version__ as chalice_version\n\n\ndef create_buildspec_v2(pipeline_params):\n    # type: (PipelineParameters) -> Dict[str, Any]\n    install_commands = [\n        \"pip install 'chalice%s'\" % pipeline_params.chalice_version_range,\n        \"pip install -r requirements.txt\",\n    ]\n    build_commands = [\n        \"chalice package /tmp/packaged\",\n        (\"aws cloudformation package --template-file /tmp/packaged/sam.json \"\n         \"--s3-bucket ${APP_S3_BUCKET} \"\n         \"--output-template-file transformed.yaml\")\n    ]\n    buildspec = {\n        \"version\": \"0.2\",\n        \"phases\": {\n            \"install\": {\n                \"commands\": install_commands,\n                \"runtime-versions\": {\n                    \"python\": pipeline_params.py_major_minor,\n                }\n            },\n            \"build\": {\n                \"commands\": build_commands,\n            }\n        },\n        \"artifacts\": {\n            \"type\": \"zip\",\n            \"files\": [\n                \"transformed.yaml\"\n            ]\n        }\n\n    }\n    return buildspec\n\n\ndef create_buildspec_legacy(pipeline_params):\n    # type: (PipelineParameters) -> Dict[str, Any]\n    install_commands = [\n        'sudo pip install --upgrade awscli',\n        'aws --version',\n        \"sudo pip install 'chalice%s'\" % pipeline_params.chalice_version_range,\n        'sudo pip install -r requirements.txt',\n        'chalice package /tmp/packaged',\n        ('aws cloudformation package '\n            '--template-file /tmp/packaged/sam.json'\n            ' --s3-bucket ${APP_S3_BUCKET} '\n            '--output-template-file transformed.yaml'),\n    ]\n    buildspec = {\n        'version': '0.1',\n        'phases': {\n            'install': {\n                'commands': install_commands,\n            }\n        },\n        'artifacts': {\n            'type': 'zip',\n            'files': ['transformed.yaml']\n        }\n    }\n    return buildspec\n\n\nclass InvalidCodeBuildPythonVersion(Exception):\n    def __init__(self, version, msg=None):\n        # type: (str, Optional[str]) -> None\n        if msg is None:\n            msg = 'CodeBuild does not yet support python version %s.' % version\n        super(InvalidCodeBuildPythonVersion, self).__init__(msg)\n\n\nclass PipelineParameters(object):\n\n    _PYTHON_VERSION = re.compile('python(.+)')\n\n    def __init__(self, app_name, lambda_python_version,\n                 codebuild_image=None, code_source='codecommit',\n                 chalice_version_range=None, pipeline_version='v1'):\n        # type: (str, str, Optional[str], str, Optional[str], str) -> None\n        self.app_name = app_name\n        # lambda_python_version is what matches lambda, e.g. 'python3.9'.\n        self.lambda_python_version = lambda_python_version\n        # py_major_minor is just the version string, e.g. '3.9'\n        self.py_major_minor = self._extract_version(lambda_python_version)\n        self.codebuild_image = codebuild_image\n        self.code_source = code_source\n        if chalice_version_range is None:\n            chalice_version_range = self._lock_to_minor_version()\n        self.chalice_version_range = chalice_version_range\n        self.pipeline_version = pipeline_version\n\n    def _extract_version(self, lambda_python_version):\n        # type: (str) -> str\n        matched = self._PYTHON_VERSION.match(lambda_python_version)\n        if matched is None:\n            raise InvalidCodeBuildPythonVersion(lambda_python_version)\n        return matched.group(1)\n\n    def _lock_to_minor_version(self):\n        # type: () -> str\n        parts = [int(p) for p in chalice_version.split('.')]\n        min_version = '%s.%s.%s' % (parts[0], parts[1], 0)\n        max_version = '%s.%s.%s' % (parts[0], parts[1] + 1, 0)\n        return '>=%s,<%s' % (min_version, max_version)\n\n\nclass BasePipelineTemplate(object):\n    def create_template(self, pipeline_params):\n        # type: (PipelineParameters) -> Dict[str, Any]\n        raise NotImplementedError(\"create_template\")\n\n\nclass CreatePipelineTemplateV2(BasePipelineTemplate):\n    _BASE_TEMPLATE = {\n        \"AWSTemplateFormatVersion\": \"2010-09-09\",\n        \"Parameters\": {\n            \"ApplicationName\": {\n                \"Default\": \"ChaliceApp\",\n                \"Type\": \"String\",\n                \"Description\": \"Enter the name of your application\"\n            },\n            \"CodeBuildImage\": {\n                \"Default\": \"aws/codebuild/amazonlinux2-x86_64-standard:3.0\",\n                \"Type\": \"String\",\n                \"Description\": \"Name of codebuild image to use.\"\n            }\n        },\n        \"Resources\": {},\n        \"Outputs\": {},\n    }\n\n    def create_template(self, pipeline_params):\n        # type: (PipelineParameters) -> Dict[str, Any]\n        self._validate_python_version(pipeline_params.py_major_minor)\n        t = copy.deepcopy(self._BASE_TEMPLATE)  # type: Dict[str, Any]\n        params = t['Parameters']\n        params['ApplicationName']['Default'] = pipeline_params.app_name\n        resources = []  # type: List[BaseResource]\n        if pipeline_params.code_source == 'github':\n            resources.append(GithubSource())\n        else:\n            resources.append(CodeCommitSourceRepository())\n        resources.extend([CodeBuild(create_buildspec_v2), CodePipeline()])\n        for resource in resources:\n            resource.add_to_template(t, pipeline_params)\n        return t\n\n    def _validate_python_version(self, python_version):\n        # type: (str) -> None\n        major, minor = [\n            int(v) for v in python_version.split('.')\n        ]\n        if (major, minor) < (3, 9):\n            raise InvalidCodeBuildPythonVersion(\n                python_version,\n                'This CodeBuild image does not support python version: %s' % (\n                    python_version\n                )\n            )\n\n\nclass CreatePipelineTemplateLegacy(BasePipelineTemplate):\n\n    _CODEBUILD_IMAGE = {\n        'python2.7': 'python:2.7.12',\n        'python3.6': 'python:3.6.5',\n        'python3.7': 'python:3.7.1',\n    }\n\n    _BASE_TEMPLATE = {\n        \"AWSTemplateFormatVersion\": \"2010-09-09\",\n        \"Parameters\": {\n            \"ApplicationName\": {\n                \"Default\": \"ChaliceApp\",\n                \"Type\": \"String\",\n                \"Description\": \"Enter the name of your application\"\n            },\n            \"CodeBuildImage\": {\n                \"Default\": \"aws/codebuild/python:2.7.12\",\n                \"Type\": \"String\",\n                \"Description\": \"Name of codebuild image to use.\"\n            }\n        },\n        \"Resources\": {},\n        \"Outputs\": {},\n    }\n\n    def create_template(self, pipeline_params):\n        # type: (PipelineParameters) -> Dict[str, Any]\n        t = copy.deepcopy(self._BASE_TEMPLATE)  # type: Dict[str, Any]\n        params = t['Parameters']\n        params['ApplicationName']['Default'] = pipeline_params.app_name\n        params['CodeBuildImage']['Default'] = self._get_codebuild_image(\n            pipeline_params)\n\n        resources = []  # type: List[BaseResource]\n        if pipeline_params.code_source == 'github':\n            resources.append(GithubSource())\n        else:\n            resources.append(CodeCommitSourceRepository())\n        resources.extend([CodeBuild(create_buildspec_legacy), CodePipeline()])\n        for resource in resources:\n            resource.add_to_template(t, pipeline_params)\n        return t\n\n    def _get_codebuild_image(self, params):\n        # type: (PipelineParameters) -> str\n        if params.codebuild_image is not None:\n            return params.codebuild_image\n        try:\n            image_suffix = self._CODEBUILD_IMAGE[params.lambda_python_version]\n            return 'aws/codebuild/%s' % image_suffix\n        except KeyError as e:\n            raise InvalidCodeBuildPythonVersion(str(e))\n\n\nclass BaseResource(object):\n    def add_to_template(self, template, pipeline_params):\n        # type: (Dict[str, Any], PipelineParameters) -> None\n        raise NotImplementedError(\"add_to_template\")\n\n\nclass CodeCommitSourceRepository(BaseResource):\n    def add_to_template(self, template, pipeline_params):\n        # type: (Dict[str, Any], PipelineParameters) -> None\n        resources = template.setdefault('Resources', {})\n        resources['SourceRepository'] = {\n            \"Type\": \"AWS::CodeCommit::Repository\",\n            \"Properties\": {\n                \"RepositoryName\": {\n                    \"Ref\": \"ApplicationName\"\n                },\n                \"RepositoryDescription\": {\n                    \"Fn::Sub\": \"Source code for ${ApplicationName}\"\n                }\n            }\n        }\n        template.setdefault('Outputs', {})['SourceRepoURL'] = {\n            \"Value\": {\n                \"Fn::GetAtt\": \"SourceRepository.CloneUrlHttp\"\n            }\n        }\n\n\nclass GithubSource(BaseResource):\n    def add_to_template(self, template, pipeline_params):\n        # type: (Dict[str, Any], PipelineParameters) -> None\n        # For the github source, we don't create a github repo,\n        # we just wire it up in the code pipeline.  The\n        # only thing we add to the template are parameters\n        # we reference in other resources later.\n        p = template.setdefault('Parameters', {})\n        p['GithubOwner'] = {\n            'Type': 'String',\n            'Description': 'The github owner or org name of the repository.',\n        }\n        p['GithubRepoName'] = {\n            'Type': 'String',\n            'Description': 'The name of the github repository.',\n        }\n        if pipeline_params.pipeline_version == 'v1':\n            p['GithubPersonalToken'] = {\n                'Type': 'String',\n                'Description': 'Personal access token for the github repo.',\n                'NoEcho': True,\n            }\n        else:\n            p['GithubRepoSecretId'] = {\n                'Type': 'String',\n                'Default': 'GithubRepoAccess',\n                'Description': (\n                    'The name/ID of the SecretsManager secret that '\n                    'contains the personal access token for the github repo.'\n                )\n            }\n            p['GithubRepoSecretJSONKey'] = {\n                'Type': 'String',\n                'Default': 'OAuthToken',\n                'Description': (\n                    'The name of the JSON key in the SecretsManager secret '\n                    'that contains the personal access token for the '\n                    'github repo.'\n                )\n            }\n\n\nclass CodeBuild(BaseResource):\n    def __init__(self, buildspec_generator=create_buildspec_legacy):\n        # type: (Callable[[PipelineParameters], Dict[str, Any]]) -> None\n        self._buildspec_generator = buildspec_generator\n\n    def add_to_template(self, template, pipeline_params):\n        # type: (Dict[str, Any], PipelineParameters) -> None\n        resources = template.setdefault('Resources', {})\n        outputs = template.setdefault('Outputs', {})\n        # Used to store the application source when the SAM\n        # template is packaged.\n        self._add_s3_bucket(resources, outputs)\n        self._add_codebuild_role(resources, outputs)\n        self._add_codebuild_policy(resources)\n        self._add_package_build(resources, pipeline_params)\n\n    def _add_package_build(self, resources, pipeline_params):\n        # type: (Dict[str, Any], PipelineParameters) -> None\n        resources['AppPackageBuild'] = {\n            \"Type\": \"AWS::CodeBuild::Project\",\n            \"Properties\": {\n                \"Artifacts\": {\n                    \"Type\": \"CODEPIPELINE\"\n                },\n                \"Environment\": {\n                    \"ComputeType\": \"BUILD_GENERAL1_SMALL\",\n                    \"Image\": {\n                        \"Ref\": \"CodeBuildImage\"\n                    },\n                    \"Type\": \"LINUX_CONTAINER\",\n                    \"EnvironmentVariables\": [\n                        {\n                            \"Name\": \"APP_S3_BUCKET\",\n                            \"Value\": {\n                                \"Ref\": \"ApplicationBucket\"\n                            }\n                        }\n                    ]\n                },\n                \"Name\": {\n                    \"Fn::Sub\": \"${ApplicationName}Build\"\n                },\n                \"ServiceRole\": {\n                    \"Fn::GetAtt\": \"CodeBuildRole.Arn\"\n                },\n                \"Source\": {\n                    \"Type\": \"CODEPIPELINE\",\n                    \"BuildSpec\": yaml.dump(\n                        self._buildspec_generator(pipeline_params),\n                    ),\n                }\n            }\n        }\n\n    def _add_s3_bucket(self, resources, outputs):\n        # type: (Dict[str, Any], Dict[str, Any]) -> None\n        resources['ApplicationBucket'] = {'Type': 'AWS::S3::Bucket'}\n        outputs['S3ApplicationBucket'] = {\n            'Value': {'Ref': 'ApplicationBucket'}\n        }\n\n    def _add_codebuild_role(self, resources, outputs):\n        # type: (Dict[str, Any], Dict[str, Any]) -> None\n        resources['CodeBuildRole'] = {\n            \"Type\": \"AWS::IAM::Role\",\n            \"Properties\": {\n                \"AssumeRolePolicyDocument\": {\n                    \"Version\": \"2012-10-17\",\n                    \"Statement\": [\n                        {\n                            \"Action\": [\n                                \"sts:AssumeRole\"\n                            ],\n                            \"Effect\": \"Allow\",\n                            \"Principal\": {\n                                \"Service\": [\n                                    {'Fn::Sub': 'codebuild.${AWS::URLSuffix}'}\n                                ]\n                            }\n                        }\n                    ]\n                }\n            }\n        }\n        outputs['CodeBuildRoleArn'] = {\n            \"Value\": {\n                \"Fn::GetAtt\": \"CodeBuildRole.Arn\"\n            }\n        }\n\n    def _add_codebuild_policy(self, resources):\n        # type: (Dict[str, Any]) -> None\n        resources['CodeBuildPolicy'] = {\n            \"Type\": \"AWS::IAM::Policy\",\n            \"Properties\": {\n                \"PolicyName\": \"CodeBuildPolicy\",\n                \"PolicyDocument\": constants.CODEBUILD_POLICY,\n                \"Roles\": [\n                    {\n                        \"Ref\": \"CodeBuildRole\"\n                    }\n                ]\n            }\n        }\n\n\nclass CodePipeline(BaseResource):\n    def add_to_template(self, template, pipeline_params):\n        # type: (Dict[str, Any], PipelineParameters) -> None\n        resources = template.setdefault('Resources', {})\n        outputs = template.setdefault('Outputs', {})\n        self._add_pipeline(resources, pipeline_params)\n        self._add_bucket_store(resources, outputs)\n        self._add_codepipeline_role(resources, outputs)\n        self._add_cfn_deploy_role(resources, outputs)\n\n    def _add_cfn_deploy_role(self, resources, outputs):\n        # type: (Dict[str, Any], Dict[str, Any]) -> None\n        outputs['CFNDeployRoleArn'] = {\n            'Value': {'Fn::GetAtt': 'CFNDeployRole.Arn'}\n        }\n        resources['CFNDeployRole'] = {\n            'Type': 'AWS::IAM::Role',\n            'Properties': {\n                \"Policies\": [\n                    {\n                        \"PolicyName\": \"DeployAccess\",\n                        \"PolicyDocument\": {\n                            \"Version\": \"2012-10-17\",\n                            \"Statement\": [\n                                {\n                                    \"Action\": \"*\",\n                                    \"Resource\": \"*\",\n                                    \"Effect\": \"Allow\"\n                                }\n                            ]\n                        }\n                    }\n                ],\n                \"AssumeRolePolicyDocument\": {\n                    \"Version\": \"2012-10-17\",\n                    \"Statement\": [\n                        {\n                            \"Action\": [\n                                \"sts:AssumeRole\"\n                            ],\n                            \"Effect\": \"Allow\",\n                            \"Principal\": {\n                                \"Service\": [\n                                    {'Fn::Sub':\n                                     'cloudformation.${AWS::URLSuffix}'}\n                                ]\n                            }\n                        }\n                    ]\n                }\n            }\n        }\n\n    def _add_pipeline(self, resources, pipeline_params):\n        # type: (Dict[str, Any], PipelineParameters) -> None\n        properties = {\n            'Name': {\n                'Fn::Sub': '${ApplicationName}Pipeline'\n            },\n            'ArtifactStore': {\n                'Type': 'S3',\n                'Location': {'Ref': 'ArtifactBucketStore'},\n            },\n            'RoleArn': {\n                'Fn::GetAtt': 'CodePipelineRole.Arn',\n            },\n            'Stages': self._create_pipeline_stages(pipeline_params),\n        }\n        resources['AppPipeline'] = {\n            'Type': 'AWS::CodePipeline::Pipeline',\n            'Properties': properties\n        }\n\n    def _create_pipeline_stages(self, pipeline_params):\n        # type: (PipelineParameters) -> List[Dict[str, Any]]\n        # The goal is to eventually allow a user to configure\n        # the various stages they want created. For now, there's\n        # a fixed list.\n        stages = []\n        source = self._create_source_stage(pipeline_params)\n        if source:\n            stages.append(source)\n        stages.extend([self._create_build_stage(), self._create_beta_stage()])\n        return stages\n\n    def _code_commit_source(self):\n        # type: () -> Dict[str, Any]\n        return {\n            \"Name\": \"Source\",\n            \"Actions\": [\n                {\n                    \"ActionTypeId\": {\n                        \"Category\": \"Source\",\n                        \"Owner\": \"AWS\",\n                        \"Version\": 1,\n                        \"Provider\": \"CodeCommit\"\n                    },\n                    \"Configuration\": {\n                        \"BranchName\": \"master\",\n                        \"RepositoryName\": {\n                            \"Fn::GetAtt\": \"SourceRepository.Name\"\n                        }\n                    },\n                    \"OutputArtifacts\": [\n                        {\n                            \"Name\": \"SourceRepo\"\n                        }\n                    ],\n                    \"RunOrder\": 1,\n                    \"Name\": \"Source\"\n                }\n            ]\n        }\n\n    def _create_source_stage(self, pipeline_params):\n        # type: (PipelineParameters) -> Dict[str, Any]\n        if pipeline_params.code_source == 'codecommit':\n            return self._code_commit_source()\n        return self._github_source(pipeline_params.pipeline_version)\n\n    def _github_source(self, pipeline_version):\n        # type: (str) -> Dict[str, Any]\n        oauth_token = {'Ref': 'GithubPersonalToken'}  # type: Dict[str, Any]\n        if pipeline_version == 'v2':\n            oauth_token = {\n                \"Fn::Join\": [\n                    \"\", [\"{{resolve:secretsmanager:\",\n                         {\"Ref\": \"GithubRepoSecretId\"},\n                         \":SecretString:\",\n                         {\"Ref\": \"GithubRepoSecretJSONKey\"},\n                         \"}}\"]\n                ]\n            }\n        return {\n            'Name': 'Source',\n            'Actions': [{\n                \"Name\": \"Source\",\n                \"ActionTypeId\": {\n                    \"Category\": \"Source\",\n                    \"Owner\": \"ThirdParty\",\n                    \"Version\": \"1\",\n                    \"Provider\": \"GitHub\"\n                },\n                'RunOrder': 1,\n                'OutputArtifacts': [{\n                    'Name': 'SourceRepo',\n                }],\n                'Configuration': {\n                    'Owner': {'Ref': 'GithubOwner'},\n                    'Repo': {'Ref': 'GithubRepoName'},\n                    'OAuthToken': oauth_token,\n                    'Branch': 'master',\n                    'PollForSourceChanges': True,\n                }\n            }],\n        }\n\n    def _create_build_stage(self):\n        # type: () -> Dict[str, Any]\n        return {\n            \"Name\": \"Build\",\n            \"Actions\": [\n                {\n                    \"InputArtifacts\": [\n                        {\n                            \"Name\": \"SourceRepo\"\n                        }\n                    ],\n                    \"Name\": \"CodeBuild\",\n                    \"ActionTypeId\": {\n                        \"Category\": \"Build\",\n                        \"Owner\": \"AWS\",\n                        \"Version\": \"1\",\n                        \"Provider\": \"CodeBuild\"\n                    },\n                    \"OutputArtifacts\": [\n                        {\n                            \"Name\": \"CompiledCFNTemplate\"\n                        }\n                    ],\n                    \"Configuration\": {\n                        \"ProjectName\": {\n                            \"Ref\": \"AppPackageBuild\"\n                        }\n                    },\n                    \"RunOrder\": 1\n                }\n            ]\n        }\n\n    def _create_beta_stage(self):\n        # type: () -> Dict[str, Any]\n        return {\n            \"Name\": \"Beta\",\n            \"Actions\": [\n                {\n                    \"ActionTypeId\": {\n                        \"Category\": \"Deploy\",\n                        \"Owner\": \"AWS\",\n                        \"Version\": \"1\",\n                        \"Provider\": \"CloudFormation\"\n                    },\n                    \"InputArtifacts\": [\n                        {\n                            \"Name\": \"CompiledCFNTemplate\"\n                        }\n                    ],\n                    \"Name\": \"CreateBetaChangeSet\",\n                    \"Configuration\": {\n                        \"ActionMode\": \"CHANGE_SET_REPLACE\",\n                        \"ChangeSetName\": {\n                            \"Fn::Sub\": \"${ApplicationName}ChangeSet\"\n                        },\n                        \"RoleArn\": {\n                            \"Fn::GetAtt\": \"CFNDeployRole.Arn\"\n                        },\n                        \"Capabilities\": \"CAPABILITY_IAM\",\n                        \"StackName\": {\n                            \"Fn::Sub\": \"${ApplicationName}BetaStack\"\n                        },\n                        \"TemplatePath\": \"CompiledCFNTemplate::transformed.yaml\"\n                    },\n                    \"RunOrder\": 1\n                },\n                {\n                    \"RunOrder\": 2,\n                    \"ActionTypeId\": {\n                        \"Category\": \"Deploy\",\n                        \"Owner\": \"AWS\",\n                        \"Version\": \"1\",\n                        \"Provider\": \"CloudFormation\"\n                    },\n                    \"Configuration\": {\n                        \"StackName\": {\n                            \"Fn::Sub\": \"${ApplicationName}BetaStack\"\n                        },\n                        \"ActionMode\": \"CHANGE_SET_EXECUTE\",\n                        \"ChangeSetName\": {\n                            \"Fn::Sub\": \"${ApplicationName}ChangeSet\"\n                        },\n                        \"OutputFileName\": \"StackOutputs.json\"\n                    },\n                    \"Name\": \"ExecuteChangeSet\",\n                    \"OutputArtifacts\": [\n                        {\n                            \"Name\": \"AppDeploymentValues\"\n                        }\n                    ]\n                }\n            ]\n        }\n\n    def _add_bucket_store(self, resources, outputs):\n        # type: (Dict[str, Any], Dict[str, Any]) -> None\n        resources['ArtifactBucketStore'] = {\n            'Type': 'AWS::S3::Bucket',\n            'Properties': {\n                'VersioningConfiguration': {\n                    'Status': 'Enabled'\n                }\n            }\n        }\n        outputs['S3PipelineBucket'] = {\n            'Value': {'Ref': 'ArtifactBucketStore'}\n        }\n\n    def _add_codepipeline_role(self, resources, outputs):\n        # type: (Dict[str, Any], Dict[str, Any]) -> None\n        outputs['CodePipelineRoleArn'] = {\n            'Value': {'Fn::GetAtt': 'CodePipelineRole.Arn'}\n        }\n        resources['CodePipelineRole'] = {\n            \"Type\": \"AWS::IAM::Role\",\n            \"Properties\": {\n                \"Policies\": [\n                    {\n                        \"PolicyName\": \"DefaultPolicy\",\n                        \"PolicyDocument\": constants.CODEPIPELINE_POLICY,\n                    }\n                ],\n                \"AssumeRolePolicyDocument\": {\n                    \"Version\": \"2012-10-17\",\n                    \"Statement\": [\n                        {\n                            \"Action\": [\n                                \"sts:AssumeRole\"\n                            ],\n                            \"Effect\": \"Allow\",\n                            \"Principal\": {\n                                \"Service\": [\n                                    {'Fn::Sub': 'codepipeline'\n                                                '.${AWS::URLSuffix}'}\n                                ]\n                            }\n                        }\n                    ]\n                }\n            }\n        }\n\n\nclass BuildSpecExtractor(object):\n    def extract_buildspec(self, template):\n        # type: (Dict[str, Any]) -> str\n        source = template['Resources']['AppPackageBuild'][\n            'Properties']['Source']\n        buildspec = source.pop('BuildSpec')\n        return buildspec\n"
  },
  {
    "path": "chalice/policies-extra.json",
    "content": "{\n  \"s3\": {\n    \"upload_file\": [\"s3:PutObject\", \"s3:AbortMultipartUpload\"],\n    \"upload_fileobj\": [\"s3:PutObject\", \"s3: AbortMultipartUpload\"],\n    \"download_file\": [\"s3:GetObject\"],\n    \"download_fileobj\": [\"s3:GetObject\"],\n    \"copy\": [\"s3:PutObject\", \"s3:AbortMultipartUpload\"]\n  }\n}\n"
  },
  {
    "path": "chalice/policies.json",
    "content": "{\n  \"account\": {\n    \"DeleteAlternateContact\": \"account:DeleteAlternateContact\",\n    \"GetAlternateContact\": \"account:GetAlternateContact\",\n    \"PutAlternateContact\": \"account:PutAlternateContact\"\n  },\n  \"acm\": {\n    \"AddTagsToCertificate\": \"acm:AddTagsToCertificate\",\n    \"DeleteCertificate\": \"acm:DeleteCertificate\",\n    \"DescribeCertificate\": \"acm:DescribeCertificate\",\n    \"ExportCertificate\": \"acm:ExportCertificate\",\n    \"GetAccountConfiguration\": \"acm:GetAccountConfiguration\",\n    \"GetCertificate\": \"acm:GetCertificate\",\n    \"ImportCertificate\": \"acm:ImportCertificate\",\n    \"ListCertificates\": \"acm:ListCertificates\",\n    \"ListTagsForCertificate\": \"acm:ListTagsForCertificate\",\n    \"PutAccountConfiguration\": \"acm:PutAccountConfiguration\",\n    \"RemoveTagsFromCertificate\": \"acm:RemoveTagsFromCertificate\",\n    \"RenewCertificate\": \"acm:RenewCertificate\",\n    \"RequestCertificate\": \"acm:RequestCertificate\",\n    \"ResendValidationEmail\": \"acm:ResendValidationEmail\",\n    \"UpdateCertificateOptions\": \"acm:UpdateCertificateOptions\"\n  },\n  \"acm-pca\": {\n    \"CreateCertificateAuthority\": \"acm-pca:CreateCertificateAuthority\",\n    \"CreateCertificateAuthorityAuditReport\": \"acm-pca:CreateCertificateAuthorityAuditReport\",\n    \"CreatePermission\": \"acm-pca:CreatePermission\",\n    \"DeleteCertificateAuthority\": \"acm-pca:DeleteCertificateAuthority\",\n    \"DeletePermission\": \"acm-pca:DeletePermission\",\n    \"DeletePolicy\": \"acm-pca:DeletePolicy\",\n    \"DescribeCertificateAuthority\": \"acm-pca:DescribeCertificateAuthority\",\n    \"DescribeCertificateAuthorityAuditReport\": \"acm-pca:DescribeCertificateAuthorityAuditReport\",\n    \"GetCertificate\": \"acm-pca:GetCertificate\",\n    \"GetCertificateAuthorityCertificate\": \"acm-pca:GetCertificateAuthorityCertificate\",\n    \"GetCertificateAuthorityCsr\": \"acm-pca:GetCertificateAuthorityCsr\",\n    \"GetPolicy\": \"acm-pca:GetPolicy\",\n    \"ImportCertificateAuthorityCertificate\": \"acm-pca:ImportCertificateAuthorityCertificate\",\n    \"IssueCertificate\": \"acm-pca:IssueCertificate\",\n    \"ListCertificateAuthorities\": \"acm-pca:ListCertificateAuthorities\",\n    \"ListPermissions\": \"acm-pca:ListPermissions\",\n    \"ListTags\": \"acm-pca:ListTags\",\n    \"PutPolicy\": \"acm-pca:PutPolicy\",\n    \"RestoreCertificateAuthority\": \"acm-pca:RestoreCertificateAuthority\",\n    \"RevokeCertificate\": \"acm-pca:RevokeCertificate\",\n    \"TagCertificateAuthority\": \"acm-pca:TagCertificateAuthority\",\n    \"UntagCertificateAuthority\": \"acm-pca:UntagCertificateAuthority\",\n    \"UpdateCertificateAuthority\": \"acm-pca:UpdateCertificateAuthority\"\n  },\n  \"alexaforbusiness\": {\n    \"ApproveSkill\": \"a4b:ApproveSkill\",\n    \"AssociateContactWithAddressBook\": \"a4b:AssociateContactWithAddressBook\",\n    \"AssociateDeviceWithNetworkProfile\": \"a4b:AssociateDeviceWithNetworkProfile\",\n    \"AssociateDeviceWithRoom\": \"a4b:AssociateDeviceWithRoom\",\n    \"AssociateSkillGroupWithRoom\": \"a4b:AssociateSkillGroupWithRoom\",\n    \"AssociateSkillWithSkillGroup\": \"a4b:AssociateSkillWithSkillGroup\",\n    \"AssociateSkillWithUsers\": \"a4b:AssociateSkillWithUsers\",\n    \"CreateAddressBook\": \"a4b:CreateAddressBook\",\n    \"CreateBusinessReportSchedule\": \"a4b:CreateBusinessReportSchedule\",\n    \"CreateConferenceProvider\": \"a4b:CreateConferenceProvider\",\n    \"CreateContact\": \"a4b:CreateContact\",\n    \"CreateGatewayGroup\": \"a4b:CreateGatewayGroup\",\n    \"CreateNetworkProfile\": \"a4b:CreateNetworkProfile\",\n    \"CreateProfile\": \"a4b:CreateProfile\",\n    \"CreateRoom\": \"a4b:CreateRoom\",\n    \"CreateSkillGroup\": \"a4b:CreateSkillGroup\",\n    \"CreateUser\": \"a4b:CreateUser\",\n    \"DeleteAddressBook\": \"a4b:DeleteAddressBook\",\n    \"DeleteBusinessReportSchedule\": \"a4b:DeleteBusinessReportSchedule\",\n    \"DeleteConferenceProvider\": \"a4b:DeleteConferenceProvider\",\n    \"DeleteContact\": \"a4b:DeleteContact\",\n    \"DeleteDevice\": \"a4b:DeleteDevice\",\n    \"DeleteDeviceUsageData\": \"a4b:DeleteDeviceUsageData\",\n    \"DeleteGatewayGroup\": \"a4b:DeleteGatewayGroup\",\n    \"DeleteNetworkProfile\": \"a4b:DeleteNetworkProfile\",\n    \"DeleteProfile\": \"a4b:DeleteProfile\",\n    \"DeleteRoom\": \"a4b:DeleteRoom\",\n    \"DeleteRoomSkillParameter\": \"a4b:DeleteRoomSkillParameter\",\n    \"DeleteSkillAuthorization\": \"a4b:DeleteSkillAuthorization\",\n    \"DeleteSkillGroup\": \"a4b:DeleteSkillGroup\",\n    \"DeleteUser\": \"a4b:DeleteUser\",\n    \"DisassociateContactFromAddressBook\": \"a4b:DisassociateContactFromAddressBook\",\n    \"DisassociateDeviceFromRoom\": \"a4b:DisassociateDeviceFromRoom\",\n    \"DisassociateSkillFromSkillGroup\": \"a4b:DisassociateSkillFromSkillGroup\",\n    \"DisassociateSkillFromUsers\": \"a4b:DisassociateSkillFromUsers\",\n    \"DisassociateSkillGroupFromRoom\": \"a4b:DisassociateSkillGroupFromRoom\",\n    \"ForgetSmartHomeAppliances\": \"a4b:ForgetSmartHomeAppliances\",\n    \"GetAddressBook\": \"a4b:GetAddressBook\",\n    \"GetConferencePreference\": \"a4b:GetConferencePreference\",\n    \"GetConferenceProvider\": \"a4b:GetConferenceProvider\",\n    \"GetContact\": \"a4b:GetContact\",\n    \"GetDevice\": \"a4b:GetDevice\",\n    \"GetGateway\": \"a4b:GetGateway\",\n    \"GetGatewayGroup\": \"a4b:GetGatewayGroup\",\n    \"GetInvitationConfiguration\": \"a4b:GetInvitationConfiguration\",\n    \"GetNetworkProfile\": \"a4b:GetNetworkProfile\",\n    \"GetProfile\": \"a4b:GetProfile\",\n    \"GetRoom\": \"a4b:GetRoom\",\n    \"GetRoomSkillParameter\": \"a4b:GetRoomSkillParameter\",\n    \"GetSkillGroup\": \"a4b:GetSkillGroup\",\n    \"ListBusinessReportSchedules\": \"a4b:ListBusinessReportSchedules\",\n    \"ListConferenceProviders\": \"a4b:ListConferenceProviders\",\n    \"ListDeviceEvents\": \"a4b:ListDeviceEvents\",\n    \"ListGatewayGroups\": \"a4b:ListGatewayGroups\",\n    \"ListGateways\": \"a4b:ListGateways\",\n    \"ListSkills\": \"a4b:ListSkills\",\n    \"ListSkillsStoreCategories\": \"a4b:ListSkillsStoreCategories\",\n    \"ListSkillsStoreSkillsByCategory\": \"a4b:ListSkillsStoreSkillsByCategory\",\n    \"ListSmartHomeAppliances\": \"a4b:ListSmartHomeAppliances\",\n    \"ListTags\": \"a4b:ListTags\",\n    \"PutConferencePreference\": \"a4b:PutConferencePreference\",\n    \"PutInvitationConfiguration\": \"a4b:PutInvitationConfiguration\",\n    \"PutRoomSkillParameter\": \"a4b:PutRoomSkillParameter\",\n    \"PutSkillAuthorization\": \"a4b:PutSkillAuthorization\",\n    \"RegisterAVSDevice\": \"a4b:RegisterAVSDevice\",\n    \"RejectSkill\": \"a4b:RejectSkill\",\n    \"ResolveRoom\": \"a4b:ResolveRoom\",\n    \"RevokeInvitation\": \"a4b:RevokeInvitation\",\n    \"SearchAddressBooks\": \"a4b:SearchAddressBooks\",\n    \"SearchContacts\": \"a4b:SearchContacts\",\n    \"SearchDevices\": \"a4b:SearchDevices\",\n    \"SearchNetworkProfiles\": \"a4b:SearchNetworkProfiles\",\n    \"SearchProfiles\": \"a4b:SearchProfiles\",\n    \"SearchRooms\": \"a4b:SearchRooms\",\n    \"SearchSkillGroups\": \"a4b:SearchSkillGroups\",\n    \"SearchUsers\": \"a4b:SearchUsers\",\n    \"SendAnnouncement\": \"a4b:SendAnnouncement\",\n    \"SendInvitation\": \"a4b:SendInvitation\",\n    \"StartDeviceSync\": \"a4b:StartDeviceSync\",\n    \"StartSmartHomeApplianceDiscovery\": \"a4b:StartSmartHomeApplianceDiscovery\",\n    \"TagResource\": \"a4b:TagResource\",\n    \"UntagResource\": \"a4b:UntagResource\",\n    \"UpdateAddressBook\": \"a4b:UpdateAddressBook\",\n    \"UpdateBusinessReportSchedule\": \"a4b:UpdateBusinessReportSchedule\",\n    \"UpdateConferenceProvider\": \"a4b:UpdateConferenceProvider\",\n    \"UpdateContact\": \"a4b:UpdateContact\",\n    \"UpdateDevice\": \"a4b:UpdateDevice\",\n    \"UpdateGateway\": \"a4b:UpdateGateway\",\n    \"UpdateGatewayGroup\": \"a4b:UpdateGatewayGroup\",\n    \"UpdateNetworkProfile\": \"a4b:UpdateNetworkProfile\",\n    \"UpdateProfile\": \"a4b:UpdateProfile\",\n    \"UpdateRoom\": \"a4b:UpdateRoom\",\n    \"UpdateSkillGroup\": \"a4b:UpdateSkillGroup\"\n  },\n  \"amplify\": {\n    \"CreateApp\": \"amplify:CreateApp\",\n    \"CreateBackendEnvironment\": \"amplify:CreateBackendEnvironment\",\n    \"CreateBranch\": \"amplify:CreateBranch\",\n    \"CreateDeployment\": \"amplify:CreateDeployment\",\n    \"CreateDomainAssociation\": \"amplify:CreateDomainAssociation\",\n    \"DeleteApp\": \"amplify:DeleteApp\",\n    \"DeleteBackendEnvironment\": \"amplify:DeleteBackendEnvironment\",\n    \"DeleteBranch\": \"amplify:DeleteBranch\",\n    \"DeleteDomainAssociation\": \"amplify:DeleteDomainAssociation\",\n    \"DeleteJob\": \"amplify:DeleteJob\",\n    \"GenerateAccessLogs\": \"amplify:GenerateAccessLogs\",\n    \"GetApp\": \"amplify:GetApp\",\n    \"GetArtifactUrl\": \"amplify:GetArtifactUrl\",\n    \"GetBackendEnvironment\": \"amplify:GetBackendEnvironment\",\n    \"GetBranch\": \"amplify:GetBranch\",\n    \"GetDomainAssociation\": \"amplify:GetDomainAssociation\",\n    \"GetJob\": \"amplify:GetJob\",\n    \"ListApps\": \"amplify:ListApps\",\n    \"ListArtifacts\": \"amplify:ListArtifacts\",\n    \"ListBackendEnvironments\": \"amplify:ListBackendEnvironments\",\n    \"ListBranches\": \"amplify:ListBranches\",\n    \"ListDomainAssociations\": \"amplify:ListDomainAssociations\",\n    \"ListJobs\": \"amplify:ListJobs\",\n    \"ListTagsForResource\": \"amplify:ListTagsForResource\",\n    \"StartDeployment\": \"amplify:StartDeployment\",\n    \"StartJob\": \"amplify:StartJob\",\n    \"StopJob\": \"amplify:StopJob\",\n    \"TagResource\": \"amplify:TagResource\",\n    \"UntagResource\": \"amplify:UntagResource\",\n    \"UpdateApp\": \"amplify:UpdateApp\",\n    \"UpdateBranch\": \"amplify:UpdateBranch\",\n    \"UpdateDomainAssociation\": \"amplify:UpdateDomainAssociation\"\n  },\n  \"amplifybackend\": {\n    \"CloneBackend\": \"amplifybackend:CloneBackend\",\n    \"CreateBackend\": \"amplifybackend:CreateBackend\",\n    \"CreateBackendAPI\": \"amplifybackend:CreateBackendAPI\",\n    \"CreateBackendAuth\": \"amplifybackend:CreateBackendAuth\",\n    \"CreateBackendConfig\": \"amplifybackend:CreateBackendConfig\",\n    \"CreateToken\": \"amplifybackend:CreateToken\",\n    \"DeleteBackend\": \"amplifybackend:DeleteBackend\",\n    \"DeleteBackendAPI\": \"amplifybackend:DeleteBackendAPI\",\n    \"DeleteBackendAuth\": \"amplifybackend:DeleteBackendAuth\",\n    \"DeleteToken\": \"amplifybackend:DeleteToken\",\n    \"GenerateBackendAPIModels\": \"amplifybackend:GenerateBackendAPIModels\",\n    \"GetBackend\": \"amplifybackend:GetBackend\",\n    \"GetBackendAPI\": \"amplifybackend:GetBackendAPI\",\n    \"GetBackendAPIModels\": \"amplifybackend:GetBackendAPIModels\",\n    \"GetBackendAuth\": \"amplifybackend:GetBackendAuth\",\n    \"GetBackendJob\": \"amplifybackend:GetBackendJob\",\n    \"GetToken\": \"amplifybackend:GetToken\",\n    \"ImportBackendAuth\": \"amplifybackend:ImportBackendAuth\",\n    \"ListBackendJobs\": \"amplifybackend:ListBackendJobs\",\n    \"RemoveAllBackends\": \"amplifybackend:RemoveAllBackends\",\n    \"RemoveBackendConfig\": \"amplifybackend:RemoveBackendConfig\",\n    \"UpdateBackendAPI\": \"amplifybackend:UpdateBackendAPI\",\n    \"UpdateBackendAuth\": \"amplifybackend:UpdateBackendAuth\",\n    \"UpdateBackendConfig\": \"amplifybackend:UpdateBackendConfig\",\n    \"UpdateBackendJob\": \"amplifybackend:UpdateBackendJob\"\n  },\n  \"amplifyuibuilder\": {\n    \"CreateComponent\": \"amplifyuibuilder:CreateComponent\",\n    \"CreateTheme\": \"amplifyuibuilder:CreateTheme\",\n    \"DeleteComponent\": \"amplifyuibuilder:DeleteComponent\",\n    \"DeleteTheme\": \"amplifyuibuilder:DeleteTheme\",\n    \"ExchangeCodeForToken\": \"amplifyuibuilder:ExchangeCodeForToken\",\n    \"ExportComponents\": \"amplifyuibuilder:ExportComponents\",\n    \"ExportThemes\": \"amplifyuibuilder:ExportThemes\",\n    \"GetComponent\": \"amplifyuibuilder:GetComponent\",\n    \"GetTheme\": \"amplifyuibuilder:GetTheme\",\n    \"ListComponents\": \"amplifyuibuilder:ListComponents\",\n    \"ListThemes\": \"amplifyuibuilder:ListThemes\",\n    \"RefreshToken\": \"amplifyuibuilder:RefreshToken\",\n    \"UpdateComponent\": \"amplifyuibuilder:UpdateComponent\",\n    \"UpdateTheme\": \"amplifyuibuilder:UpdateTheme\"\n  },\n  \"appconfig\": {\n    \"CreateApplication\": \"appconfig:CreateApplication\",\n    \"CreateConfigurationProfile\": \"appconfig:CreateConfigurationProfile\",\n    \"CreateDeploymentStrategy\": \"appconfig:CreateDeploymentStrategy\",\n    \"CreateEnvironment\": \"appconfig:CreateEnvironment\",\n    \"CreateHostedConfigurationVersion\": \"appconfig:CreateHostedConfigurationVersion\",\n    \"DeleteApplication\": \"appconfig:DeleteApplication\",\n    \"DeleteConfigurationProfile\": \"appconfig:DeleteConfigurationProfile\",\n    \"DeleteDeploymentStrategy\": \"appconfig:DeleteDeploymentStrategy\",\n    \"DeleteEnvironment\": \"appconfig:DeleteEnvironment\",\n    \"DeleteHostedConfigurationVersion\": \"appconfig:DeleteHostedConfigurationVersion\",\n    \"GetApplication\": \"appconfig:GetApplication\",\n    \"GetConfiguration\": \"appconfig:GetConfiguration\",\n    \"GetConfigurationProfile\": \"appconfig:GetConfigurationProfile\",\n    \"GetDeployment\": \"appconfig:GetDeployment\",\n    \"GetDeploymentStrategy\": \"appconfig:GetDeploymentStrategy\",\n    \"GetEnvironment\": \"appconfig:GetEnvironment\",\n    \"GetHostedConfigurationVersion\": \"appconfig:GetHostedConfigurationVersion\",\n    \"ListApplications\": \"appconfig:ListApplications\",\n    \"ListConfigurationProfiles\": \"appconfig:ListConfigurationProfiles\",\n    \"ListDeploymentStrategies\": \"appconfig:ListDeploymentStrategies\",\n    \"ListDeployments\": \"appconfig:ListDeployments\",\n    \"ListEnvironments\": \"appconfig:ListEnvironments\",\n    \"ListHostedConfigurationVersions\": \"appconfig:ListHostedConfigurationVersions\",\n    \"ListTagsForResource\": \"appconfig:ListTagsForResource\",\n    \"StartDeployment\": \"appconfig:StartDeployment\",\n    \"StopDeployment\": \"appconfig:StopDeployment\",\n    \"TagResource\": \"appconfig:TagResource\",\n    \"UntagResource\": \"appconfig:UntagResource\",\n    \"UpdateApplication\": \"appconfig:UpdateApplication\",\n    \"UpdateConfigurationProfile\": \"appconfig:UpdateConfigurationProfile\",\n    \"UpdateDeploymentStrategy\": \"appconfig:UpdateDeploymentStrategy\",\n    \"UpdateEnvironment\": \"appconfig:UpdateEnvironment\",\n    \"ValidateConfiguration\": \"appconfig:ValidateConfiguration\"\n  },\n  \"appflow\": {\n    \"CreateConnectorProfile\": \"appflow:CreateConnectorProfile\",\n    \"CreateFlow\": \"appflow:CreateFlow\",\n    \"DeleteConnectorProfile\": \"appflow:DeleteConnectorProfile\",\n    \"DeleteFlow\": \"appflow:DeleteFlow\",\n    \"DescribeConnectorEntity\": \"appflow:DescribeConnectorEntity\",\n    \"DescribeConnectorProfiles\": \"appflow:DescribeConnectorProfiles\",\n    \"DescribeConnectors\": \"appflow:DescribeConnectors\",\n    \"DescribeFlow\": \"appflow:DescribeFlow\",\n    \"DescribeFlowExecutionRecords\": \"appflow:DescribeFlowExecutionRecords\",\n    \"ListConnectorEntities\": \"appflow:ListConnectorEntities\",\n    \"ListFlows\": \"appflow:ListFlows\",\n    \"ListTagsForResource\": \"appflow:ListTagsForResource\",\n    \"StartFlow\": \"appflow:StartFlow\",\n    \"StopFlow\": \"appflow:StopFlow\",\n    \"TagResource\": \"appflow:TagResource\",\n    \"UntagResource\": \"appflow:UntagResource\",\n    \"UpdateConnectorProfile\": \"appflow:UpdateConnectorProfile\",\n    \"UpdateFlow\": \"appflow:UpdateFlow\"\n  },\n  \"application-autoscaling\": {\n    \"DeleteScalingPolicy\": \"application-autoscaling:DeleteScalingPolicy\",\n    \"DeleteScheduledAction\": \"application-autoscaling:DeleteScheduledAction\",\n    \"DeregisterScalableTarget\": \"application-autoscaling:DeregisterScalableTarget\",\n    \"DescribeScalableTargets\": \"application-autoscaling:DescribeScalableTargets\",\n    \"DescribeScalingActivities\": \"application-autoscaling:DescribeScalingActivities\",\n    \"DescribeScalingPolicies\": \"application-autoscaling:DescribeScalingPolicies\",\n    \"DescribeScheduledActions\": \"application-autoscaling:DescribeScheduledActions\",\n    \"PutScalingPolicy\": \"application-autoscaling:PutScalingPolicy\",\n    \"PutScheduledAction\": \"application-autoscaling:PutScheduledAction\",\n    \"RegisterScalableTarget\": \"application-autoscaling:RegisterScalableTarget\"\n  },\n  \"appmesh\": {\n    \"CreateGatewayRoute\": \"appmesh:CreateGatewayRoute\",\n    \"CreateMesh\": \"appmesh:CreateMesh\",\n    \"CreateRoute\": \"appmesh:CreateRoute\",\n    \"CreateVirtualGateway\": \"appmesh:CreateVirtualGateway\",\n    \"CreateVirtualNode\": \"appmesh:CreateVirtualNode\",\n    \"CreateVirtualRouter\": \"appmesh:CreateVirtualRouter\",\n    \"CreateVirtualService\": \"appmesh:CreateVirtualService\",\n    \"DeleteGatewayRoute\": \"appmesh:DeleteGatewayRoute\",\n    \"DeleteMesh\": \"appmesh:DeleteMesh\",\n    \"DeleteRoute\": \"appmesh:DeleteRoute\",\n    \"DeleteVirtualGateway\": \"appmesh:DeleteVirtualGateway\",\n    \"DeleteVirtualNode\": \"appmesh:DeleteVirtualNode\",\n    \"DeleteVirtualRouter\": \"appmesh:DeleteVirtualRouter\",\n    \"DeleteVirtualService\": \"appmesh:DeleteVirtualService\",\n    \"DescribeGatewayRoute\": \"appmesh:DescribeGatewayRoute\",\n    \"DescribeMesh\": \"appmesh:DescribeMesh\",\n    \"DescribeRoute\": \"appmesh:DescribeRoute\",\n    \"DescribeVirtualGateway\": \"appmesh:DescribeVirtualGateway\",\n    \"DescribeVirtualNode\": \"appmesh:DescribeVirtualNode\",\n    \"DescribeVirtualRouter\": \"appmesh:DescribeVirtualRouter\",\n    \"DescribeVirtualService\": \"appmesh:DescribeVirtualService\",\n    \"ListGatewayRoutes\": \"appmesh:ListGatewayRoutes\",\n    \"ListMeshes\": \"appmesh:ListMeshes\",\n    \"ListRoutes\": \"appmesh:ListRoutes\",\n    \"ListTagsForResource\": \"appmesh:ListTagsForResource\",\n    \"ListVirtualGateways\": \"appmesh:ListVirtualGateways\",\n    \"ListVirtualNodes\": \"appmesh:ListVirtualNodes\",\n    \"ListVirtualRouters\": \"appmesh:ListVirtualRouters\",\n    \"ListVirtualServices\": \"appmesh:ListVirtualServices\",\n    \"TagResource\": \"appmesh:TagResource\",\n    \"UntagResource\": \"appmesh:UntagResource\",\n    \"UpdateGatewayRoute\": \"appmesh:UpdateGatewayRoute\",\n    \"UpdateMesh\": \"appmesh:UpdateMesh\",\n    \"UpdateRoute\": \"appmesh:UpdateRoute\",\n    \"UpdateVirtualGateway\": \"appmesh:UpdateVirtualGateway\",\n    \"UpdateVirtualNode\": \"appmesh:UpdateVirtualNode\",\n    \"UpdateVirtualRouter\": \"appmesh:UpdateVirtualRouter\",\n    \"UpdateVirtualService\": \"appmesh:UpdateVirtualService\"\n  },\n  \"apprunner\": {\n    \"AssociateCustomDomain\": \"apprunner:AssociateCustomDomain\",\n    \"CreateAutoScalingConfiguration\": \"apprunner:CreateAutoScalingConfiguration\",\n    \"CreateConnection\": \"apprunner:CreateConnection\",\n    \"CreateService\": \"apprunner:CreateService\",\n    \"DeleteAutoScalingConfiguration\": \"apprunner:DeleteAutoScalingConfiguration\",\n    \"DeleteConnection\": \"apprunner:DeleteConnection\",\n    \"DeleteService\": \"apprunner:DeleteService\",\n    \"DescribeAutoScalingConfiguration\": \"apprunner:DescribeAutoScalingConfiguration\",\n    \"DescribeCustomDomains\": \"apprunner:DescribeCustomDomains\",\n    \"DescribeService\": \"apprunner:DescribeService\",\n    \"DisassociateCustomDomain\": \"apprunner:DisassociateCustomDomain\",\n    \"ListAutoScalingConfigurations\": \"apprunner:ListAutoScalingConfigurations\",\n    \"ListConnections\": \"apprunner:ListConnections\",\n    \"ListOperations\": \"apprunner:ListOperations\",\n    \"ListServices\": \"apprunner:ListServices\",\n    \"ListTagsForResource\": \"apprunner:ListTagsForResource\",\n    \"PauseService\": \"apprunner:PauseService\",\n    \"ResumeService\": \"apprunner:ResumeService\",\n    \"StartDeployment\": \"apprunner:StartDeployment\",\n    \"TagResource\": \"apprunner:TagResource\",\n    \"UntagResource\": \"apprunner:UntagResource\",\n    \"UpdateService\": \"apprunner:UpdateService\"\n  },\n  \"appstream\": {\n    \"AssociateApplicationFleet\": \"appstream:AssociateApplicationFleet\",\n    \"AssociateFleet\": \"appstream:AssociateFleet\",\n    \"BatchAssociateUserStack\": \"appstream:BatchAssociateUserStack\",\n    \"BatchDisassociateUserStack\": \"appstream:BatchDisassociateUserStack\",\n    \"CopyImage\": \"appstream:CopyImage\",\n    \"CreateAppBlock\": \"appstream:CreateAppBlock\",\n    \"CreateApplication\": \"appstream:CreateApplication\",\n    \"CreateDirectoryConfig\": \"appstream:CreateDirectoryConfig\",\n    \"CreateEntitlement\": \"appstream:CreateEntitlement\",\n    \"CreateFleet\": \"appstream:CreateFleet\",\n    \"CreateImageBuilder\": \"appstream:CreateImageBuilder\",\n    \"CreateImageBuilderStreamingURL\": \"appstream:CreateImageBuilderStreamingURL\",\n    \"CreateStack\": \"appstream:CreateStack\",\n    \"CreateStreamingURL\": \"appstream:CreateStreamingURL\",\n    \"CreateUpdatedImage\": \"appstream:CreateUpdatedImage\",\n    \"CreateUsageReportSubscription\": \"appstream:CreateUsageReportSubscription\",\n    \"CreateUser\": \"appstream:CreateUser\",\n    \"DeleteAppBlock\": \"appstream:DeleteAppBlock\",\n    \"DeleteApplication\": \"appstream:DeleteApplication\",\n    \"DeleteDirectoryConfig\": \"appstream:DeleteDirectoryConfig\",\n    \"DeleteEntitlement\": \"appstream:DeleteEntitlement\",\n    \"DeleteFleet\": \"appstream:DeleteFleet\",\n    \"DeleteImage\": \"appstream:DeleteImage\",\n    \"DeleteImageBuilder\": \"appstream:DeleteImageBuilder\",\n    \"DeleteImagePermissions\": \"appstream:DeleteImagePermissions\",\n    \"DeleteStack\": \"appstream:DeleteStack\",\n    \"DeleteUsageReportSubscription\": \"appstream:DeleteUsageReportSubscription\",\n    \"DeleteUser\": \"appstream:DeleteUser\",\n    \"DescribeAppBlocks\": \"appstream:DescribeAppBlocks\",\n    \"DescribeApplicationFleetAssociations\": \"appstream:DescribeApplicationFleetAssociations\",\n    \"DescribeApplications\": \"appstream:DescribeApplications\",\n    \"DescribeDirectoryConfigs\": \"appstream:DescribeDirectoryConfigs\",\n    \"DescribeEntitlements\": \"appstream:DescribeEntitlements\",\n    \"DescribeFleets\": \"appstream:DescribeFleets\",\n    \"DescribeImageBuilders\": \"appstream:DescribeImageBuilders\",\n    \"DescribeImagePermissions\": \"appstream:DescribeImagePermissions\",\n    \"DescribeImages\": \"appstream:DescribeImages\",\n    \"DescribeSessions\": \"appstream:DescribeSessions\",\n    \"DescribeStacks\": \"appstream:DescribeStacks\",\n    \"DescribeUsageReportSubscriptions\": \"appstream:DescribeUsageReportSubscriptions\",\n    \"DescribeUserStackAssociations\": \"appstream:DescribeUserStackAssociations\",\n    \"DescribeUsers\": \"appstream:DescribeUsers\",\n    \"DisableUser\": \"appstream:DisableUser\",\n    \"DisassociateApplicationFleet\": \"appstream:DisassociateApplicationFleet\",\n    \"DisassociateFleet\": \"appstream:DisassociateFleet\",\n    \"EnableUser\": \"appstream:EnableUser\",\n    \"ExpireSession\": \"appstream:ExpireSession\",\n    \"ListAssociatedFleets\": \"appstream:ListAssociatedFleets\",\n    \"ListAssociatedStacks\": \"appstream:ListAssociatedStacks\",\n    \"ListEntitledApplications\": \"appstream:ListEntitledApplications\",\n    \"ListTagsForResource\": \"appstream:ListTagsForResource\",\n    \"StartFleet\": \"appstream:StartFleet\",\n    \"StartImageBuilder\": \"appstream:StartImageBuilder\",\n    \"StopFleet\": \"appstream:StopFleet\",\n    \"StopImageBuilder\": \"appstream:StopImageBuilder\",\n    \"TagResource\": \"appstream:TagResource\",\n    \"UntagResource\": \"appstream:UntagResource\",\n    \"UpdateApplication\": \"appstream:UpdateApplication\",\n    \"UpdateDirectoryConfig\": \"appstream:UpdateDirectoryConfig\",\n    \"UpdateEntitlement\": \"appstream:UpdateEntitlement\",\n    \"UpdateFleet\": \"appstream:UpdateFleet\",\n    \"UpdateImagePermissions\": \"appstream:UpdateImagePermissions\",\n    \"UpdateStack\": \"appstream:UpdateStack\"\n  },\n  \"athena\": {\n    \"BatchGetNamedQuery\": \"athena:BatchGetNamedQuery\",\n    \"BatchGetQueryExecution\": \"athena:BatchGetQueryExecution\",\n    \"CreateDataCatalog\": \"athena:CreateDataCatalog\",\n    \"CreateNamedQuery\": \"athena:CreateNamedQuery\",\n    \"CreatePreparedStatement\": \"athena:CreatePreparedStatement\",\n    \"CreateWorkGroup\": \"athena:CreateWorkGroup\",\n    \"DeleteDataCatalog\": \"athena:DeleteDataCatalog\",\n    \"DeleteNamedQuery\": \"athena:DeleteNamedQuery\",\n    \"DeletePreparedStatement\": \"athena:DeletePreparedStatement\",\n    \"DeleteWorkGroup\": \"athena:DeleteWorkGroup\",\n    \"GetDataCatalog\": \"athena:GetDataCatalog\",\n    \"GetDatabase\": \"athena:GetDatabase\",\n    \"GetNamedQuery\": \"athena:GetNamedQuery\",\n    \"GetPreparedStatement\": \"athena:GetPreparedStatement\",\n    \"GetQueryExecution\": \"athena:GetQueryExecution\",\n    \"GetQueryResults\": \"athena:GetQueryResults\",\n    \"GetTableMetadata\": \"athena:GetTableMetadata\",\n    \"GetWorkGroup\": \"athena:GetWorkGroup\",\n    \"ListDataCatalogs\": \"athena:ListDataCatalogs\",\n    \"ListDatabases\": \"athena:ListDatabases\",\n    \"ListEngineVersions\": \"athena:ListEngineVersions\",\n    \"ListNamedQueries\": \"athena:ListNamedQueries\",\n    \"ListPreparedStatements\": \"athena:ListPreparedStatements\",\n    \"ListQueryExecutions\": \"athena:ListQueryExecutions\",\n    \"ListTableMetadata\": \"athena:ListTableMetadata\",\n    \"ListTagsForResource\": \"athena:ListTagsForResource\",\n    \"ListWorkGroups\": \"athena:ListWorkGroups\",\n    \"StartQueryExecution\": \"athena:StartQueryExecution\",\n    \"StopQueryExecution\": \"athena:StopQueryExecution\",\n    \"TagResource\": \"athena:TagResource\",\n    \"UntagResource\": \"athena:UntagResource\",\n    \"UpdateDataCatalog\": \"athena:UpdateDataCatalog\",\n    \"UpdatePreparedStatement\": \"athena:UpdatePreparedStatement\",\n    \"UpdateWorkGroup\": \"athena:UpdateWorkGroup\"\n  },\n  \"auditmanager\": {\n    \"AssociateAssessmentReportEvidenceFolder\": \"auditmanager:AssociateAssessmentReportEvidenceFolder\",\n    \"BatchAssociateAssessmentReportEvidence\": \"auditmanager:BatchAssociateAssessmentReportEvidence\",\n    \"BatchCreateDelegationByAssessment\": \"auditmanager:BatchCreateDelegationByAssessment\",\n    \"BatchDeleteDelegationByAssessment\": \"auditmanager:BatchDeleteDelegationByAssessment\",\n    \"BatchDisassociateAssessmentReportEvidence\": \"auditmanager:BatchDisassociateAssessmentReportEvidence\",\n    \"BatchImportEvidenceToAssessmentControl\": \"auditmanager:BatchImportEvidenceToAssessmentControl\",\n    \"CreateAssessment\": \"auditmanager:CreateAssessment\",\n    \"CreateAssessmentFramework\": \"auditmanager:CreateAssessmentFramework\",\n    \"CreateAssessmentReport\": \"auditmanager:CreateAssessmentReport\",\n    \"CreateControl\": \"auditmanager:CreateControl\",\n    \"DeleteAssessment\": \"auditmanager:DeleteAssessment\",\n    \"DeleteAssessmentFramework\": \"auditmanager:DeleteAssessmentFramework\",\n    \"DeleteAssessmentFrameworkShare\": \"auditmanager:DeleteAssessmentFrameworkShare\",\n    \"DeleteAssessmentReport\": \"auditmanager:DeleteAssessmentReport\",\n    \"DeleteControl\": \"auditmanager:DeleteControl\",\n    \"DeregisterAccount\": \"auditmanager:DeregisterAccount\",\n    \"DeregisterOrganizationAdminAccount\": \"auditmanager:DeregisterOrganizationAdminAccount\",\n    \"DisassociateAssessmentReportEvidenceFolder\": \"auditmanager:DisassociateAssessmentReportEvidenceFolder\",\n    \"GetAccountStatus\": \"auditmanager:GetAccountStatus\",\n    \"GetAssessment\": \"auditmanager:GetAssessment\",\n    \"GetAssessmentFramework\": \"auditmanager:GetAssessmentFramework\",\n    \"GetAssessmentReportUrl\": \"auditmanager:GetAssessmentReportUrl\",\n    \"GetChangeLogs\": \"auditmanager:GetChangeLogs\",\n    \"GetControl\": \"auditmanager:GetControl\",\n    \"GetDelegations\": \"auditmanager:GetDelegations\",\n    \"GetEvidence\": \"auditmanager:GetEvidence\",\n    \"GetEvidenceByEvidenceFolder\": \"auditmanager:GetEvidenceByEvidenceFolder\",\n    \"GetEvidenceFolder\": \"auditmanager:GetEvidenceFolder\",\n    \"GetEvidenceFoldersByAssessment\": \"auditmanager:GetEvidenceFoldersByAssessment\",\n    \"GetEvidenceFoldersByAssessmentControl\": \"auditmanager:GetEvidenceFoldersByAssessmentControl\",\n    \"GetInsights\": \"auditmanager:GetInsights\",\n    \"GetInsightsByAssessment\": \"auditmanager:GetInsightsByAssessment\",\n    \"GetOrganizationAdminAccount\": \"auditmanager:GetOrganizationAdminAccount\",\n    \"GetServicesInScope\": \"auditmanager:GetServicesInScope\",\n    \"GetSettings\": \"auditmanager:GetSettings\",\n    \"ListAssessmentControlInsightsByControlDomain\": \"auditmanager:ListAssessmentControlInsightsByControlDomain\",\n    \"ListAssessmentFrameworkShareRequests\": \"auditmanager:ListAssessmentFrameworkShareRequests\",\n    \"ListAssessmentFrameworks\": \"auditmanager:ListAssessmentFrameworks\",\n    \"ListAssessmentReports\": \"auditmanager:ListAssessmentReports\",\n    \"ListAssessments\": \"auditmanager:ListAssessments\",\n    \"ListControlDomainInsights\": \"auditmanager:ListControlDomainInsights\",\n    \"ListControlDomainInsightsByAssessment\": \"auditmanager:ListControlDomainInsightsByAssessment\",\n    \"ListControlInsightsByControlDomain\": \"auditmanager:ListControlInsightsByControlDomain\",\n    \"ListControls\": \"auditmanager:ListControls\",\n    \"ListKeywordsForDataSource\": \"auditmanager:ListKeywordsForDataSource\",\n    \"ListNotifications\": \"auditmanager:ListNotifications\",\n    \"ListTagsForResource\": \"auditmanager:ListTagsForResource\",\n    \"RegisterAccount\": \"auditmanager:RegisterAccount\",\n    \"RegisterOrganizationAdminAccount\": \"auditmanager:RegisterOrganizationAdminAccount\",\n    \"StartAssessmentFrameworkShare\": \"auditmanager:StartAssessmentFrameworkShare\",\n    \"TagResource\": \"auditmanager:TagResource\",\n    \"UntagResource\": \"auditmanager:UntagResource\",\n    \"UpdateAssessment\": \"auditmanager:UpdateAssessment\",\n    \"UpdateAssessmentControl\": \"auditmanager:UpdateAssessmentControl\",\n    \"UpdateAssessmentControlSetStatus\": \"auditmanager:UpdateAssessmentControlSetStatus\",\n    \"UpdateAssessmentFramework\": \"auditmanager:UpdateAssessmentFramework\",\n    \"UpdateAssessmentFrameworkShare\": \"auditmanager:UpdateAssessmentFrameworkShare\",\n    \"UpdateAssessmentStatus\": \"auditmanager:UpdateAssessmentStatus\",\n    \"UpdateControl\": \"auditmanager:UpdateControl\",\n    \"UpdateSettings\": \"auditmanager:UpdateSettings\",\n    \"ValidateAssessmentReportIntegrity\": \"auditmanager:ValidateAssessmentReportIntegrity\"\n  },\n  \"autoscaling\": {\n    \"AttachInstances\": \"autoscaling:AttachInstances\",\n    \"AttachLoadBalancerTargetGroups\": \"autoscaling:AttachLoadBalancerTargetGroups\",\n    \"AttachLoadBalancers\": \"autoscaling:AttachLoadBalancers\",\n    \"BatchDeleteScheduledAction\": \"autoscaling:BatchDeleteScheduledAction\",\n    \"BatchPutScheduledUpdateGroupAction\": \"autoscaling:BatchPutScheduledUpdateGroupAction\",\n    \"CancelInstanceRefresh\": \"autoscaling:CancelInstanceRefresh\",\n    \"CompleteLifecycleAction\": \"autoscaling:CompleteLifecycleAction\",\n    \"CreateAutoScalingGroup\": \"autoscaling:CreateAutoScalingGroup\",\n    \"CreateLaunchConfiguration\": \"autoscaling:CreateLaunchConfiguration\",\n    \"CreateOrUpdateTags\": \"autoscaling:CreateOrUpdateTags\",\n    \"DeleteAutoScalingGroup\": \"autoscaling:DeleteAutoScalingGroup\",\n    \"DeleteLaunchConfiguration\": \"autoscaling:DeleteLaunchConfiguration\",\n    \"DeleteLifecycleHook\": \"autoscaling:DeleteLifecycleHook\",\n    \"DeleteNotificationConfiguration\": \"autoscaling:DeleteNotificationConfiguration\",\n    \"DeletePolicy\": \"autoscaling:DeletePolicy\",\n    \"DeleteScheduledAction\": \"autoscaling:DeleteScheduledAction\",\n    \"DeleteTags\": \"autoscaling:DeleteTags\",\n    \"DeleteWarmPool\": \"autoscaling:DeleteWarmPool\",\n    \"DescribeAccountLimits\": \"autoscaling:DescribeAccountLimits\",\n    \"DescribeAdjustmentTypes\": \"autoscaling:DescribeAdjustmentTypes\",\n    \"DescribeAutoScalingGroups\": \"autoscaling:DescribeAutoScalingGroups\",\n    \"DescribeAutoScalingInstances\": \"autoscaling:DescribeAutoScalingInstances\",\n    \"DescribeAutoScalingNotificationTypes\": \"autoscaling:DescribeAutoScalingNotificationTypes\",\n    \"DescribeInstanceRefreshes\": \"autoscaling:DescribeInstanceRefreshes\",\n    \"DescribeLaunchConfigurations\": \"autoscaling:DescribeLaunchConfigurations\",\n    \"DescribeLifecycleHookTypes\": \"autoscaling:DescribeLifecycleHookTypes\",\n    \"DescribeLifecycleHooks\": \"autoscaling:DescribeLifecycleHooks\",\n    \"DescribeLoadBalancerTargetGroups\": \"autoscaling:DescribeLoadBalancerTargetGroups\",\n    \"DescribeLoadBalancers\": \"autoscaling:DescribeLoadBalancers\",\n    \"DescribeMetricCollectionTypes\": \"autoscaling:DescribeMetricCollectionTypes\",\n    \"DescribeNotificationConfigurations\": \"autoscaling:DescribeNotificationConfigurations\",\n    \"DescribePolicies\": \"autoscaling:DescribePolicies\",\n    \"DescribeScalingActivities\": \"autoscaling:DescribeScalingActivities\",\n    \"DescribeScalingProcessTypes\": \"autoscaling:DescribeScalingProcessTypes\",\n    \"DescribeScheduledActions\": \"autoscaling:DescribeScheduledActions\",\n    \"DescribeTags\": \"autoscaling:DescribeTags\",\n    \"DescribeTerminationPolicyTypes\": \"autoscaling:DescribeTerminationPolicyTypes\",\n    \"DescribeWarmPool\": \"autoscaling:DescribeWarmPool\",\n    \"DetachInstances\": \"autoscaling:DetachInstances\",\n    \"DetachLoadBalancerTargetGroups\": \"autoscaling:DetachLoadBalancerTargetGroups\",\n    \"DetachLoadBalancers\": \"autoscaling:DetachLoadBalancers\",\n    \"DisableMetricsCollection\": \"autoscaling:DisableMetricsCollection\",\n    \"EnableMetricsCollection\": \"autoscaling:EnableMetricsCollection\",\n    \"EnterStandby\": \"autoscaling:EnterStandby\",\n    \"ExecutePolicy\": \"autoscaling:ExecutePolicy\",\n    \"ExitStandby\": \"autoscaling:ExitStandby\",\n    \"GetPredictiveScalingForecast\": \"autoscaling:GetPredictiveScalingForecast\",\n    \"PutLifecycleHook\": \"autoscaling:PutLifecycleHook\",\n    \"PutNotificationConfiguration\": \"autoscaling:PutNotificationConfiguration\",\n    \"PutScalingPolicy\": \"autoscaling:PutScalingPolicy\",\n    \"PutScheduledUpdateGroupAction\": \"autoscaling:PutScheduledUpdateGroupAction\",\n    \"PutWarmPool\": \"autoscaling:PutWarmPool\",\n    \"RecordLifecycleActionHeartbeat\": \"autoscaling:RecordLifecycleActionHeartbeat\",\n    \"ResumeProcesses\": \"autoscaling:ResumeProcesses\",\n    \"SetDesiredCapacity\": \"autoscaling:SetDesiredCapacity\",\n    \"SetInstanceHealth\": \"autoscaling:SetInstanceHealth\",\n    \"SetInstanceProtection\": \"autoscaling:SetInstanceProtection\",\n    \"StartInstanceRefresh\": \"autoscaling:StartInstanceRefresh\",\n    \"SuspendProcesses\": \"autoscaling:SuspendProcesses\",\n    \"TerminateInstanceInAutoScalingGroup\": \"autoscaling:TerminateInstanceInAutoScalingGroup\",\n    \"UpdateAutoScalingGroup\": \"autoscaling:UpdateAutoScalingGroup\"\n  },\n  \"autoscaling-plans\": {\n    \"CreateScalingPlan\": \"autoscaling-plans:CreateScalingPlan\",\n    \"DeleteScalingPlan\": \"autoscaling-plans:DeleteScalingPlan\",\n    \"DescribeScalingPlanResources\": \"autoscaling-plans:DescribeScalingPlanResources\",\n    \"DescribeScalingPlans\": \"autoscaling-plans:DescribeScalingPlans\",\n    \"GetScalingPlanResourceForecastData\": \"autoscaling-plans:GetScalingPlanResourceForecastData\",\n    \"UpdateScalingPlan\": \"autoscaling-plans:UpdateScalingPlan\"\n  },\n  \"backup\": {\n    \"CreateBackupPlan\": \"backup:CreateBackupPlan\",\n    \"CreateBackupSelection\": \"backup:CreateBackupSelection\",\n    \"CreateBackupVault\": \"backup:CreateBackupVault\",\n    \"CreateFramework\": \"backup:CreateFramework\",\n    \"CreateReportPlan\": \"backup:CreateReportPlan\",\n    \"DeleteBackupPlan\": \"backup:DeleteBackupPlan\",\n    \"DeleteBackupSelection\": \"backup:DeleteBackupSelection\",\n    \"DeleteBackupVault\": \"backup:DeleteBackupVault\",\n    \"DeleteBackupVaultAccessPolicy\": \"backup:DeleteBackupVaultAccessPolicy\",\n    \"DeleteBackupVaultLockConfiguration\": \"backup:DeleteBackupVaultLockConfiguration\",\n    \"DeleteBackupVaultNotifications\": \"backup:DeleteBackupVaultNotifications\",\n    \"DeleteFramework\": \"backup:DeleteFramework\",\n    \"DeleteRecoveryPoint\": \"backup:DeleteRecoveryPoint\",\n    \"DeleteReportPlan\": \"backup:DeleteReportPlan\",\n    \"DescribeBackupJob\": \"backup:DescribeBackupJob\",\n    \"DescribeBackupVault\": \"backup:DescribeBackupVault\",\n    \"DescribeCopyJob\": \"backup:DescribeCopyJob\",\n    \"DescribeFramework\": \"backup:DescribeFramework\",\n    \"DescribeGlobalSettings\": \"backup:DescribeGlobalSettings\",\n    \"DescribeProtectedResource\": \"backup:DescribeProtectedResource\",\n    \"DescribeRecoveryPoint\": \"backup:DescribeRecoveryPoint\",\n    \"DescribeRegionSettings\": \"backup:DescribeRegionSettings\",\n    \"DescribeReportJob\": \"backup:DescribeReportJob\",\n    \"DescribeReportPlan\": \"backup:DescribeReportPlan\",\n    \"DescribeRestoreJob\": \"backup:DescribeRestoreJob\",\n    \"DisassociateRecoveryPoint\": \"backup:DisassociateRecoveryPoint\",\n    \"ExportBackupPlanTemplate\": \"backup:ExportBackupPlanTemplate\",\n    \"GetBackupPlan\": \"backup:GetBackupPlan\",\n    \"GetBackupPlanFromJSON\": \"backup:GetBackupPlanFromJSON\",\n    \"GetBackupPlanFromTemplate\": \"backup:GetBackupPlanFromTemplate\",\n    \"GetBackupSelection\": \"backup:GetBackupSelection\",\n    \"GetBackupVaultAccessPolicy\": \"backup:GetBackupVaultAccessPolicy\",\n    \"GetBackupVaultNotifications\": \"backup:GetBackupVaultNotifications\",\n    \"GetRecoveryPointRestoreMetadata\": \"backup:GetRecoveryPointRestoreMetadata\",\n    \"GetSupportedResourceTypes\": \"backup:GetSupportedResourceTypes\",\n    \"ListBackupJobs\": \"backup:ListBackupJobs\",\n    \"ListBackupPlanTemplates\": \"backup:ListBackupPlanTemplates\",\n    \"ListBackupPlanVersions\": \"backup:ListBackupPlanVersions\",\n    \"ListBackupPlans\": \"backup:ListBackupPlans\",\n    \"ListBackupSelections\": \"backup:ListBackupSelections\",\n    \"ListBackupVaults\": \"backup:ListBackupVaults\",\n    \"ListCopyJobs\": \"backup:ListCopyJobs\",\n    \"ListFrameworks\": \"backup:ListFrameworks\",\n    \"ListProtectedResources\": \"backup:ListProtectedResources\",\n    \"ListRecoveryPointsByBackupVault\": \"backup:ListRecoveryPointsByBackupVault\",\n    \"ListRecoveryPointsByResource\": \"backup:ListRecoveryPointsByResource\",\n    \"ListReportJobs\": \"backup:ListReportJobs\",\n    \"ListReportPlans\": \"backup:ListReportPlans\",\n    \"ListRestoreJobs\": \"backup:ListRestoreJobs\",\n    \"ListTags\": \"backup:ListTags\",\n    \"PutBackupVaultAccessPolicy\": \"backup:PutBackupVaultAccessPolicy\",\n    \"PutBackupVaultLockConfiguration\": \"backup:PutBackupVaultLockConfiguration\",\n    \"PutBackupVaultNotifications\": \"backup:PutBackupVaultNotifications\",\n    \"StartBackupJob\": \"backup:StartBackupJob\",\n    \"StartCopyJob\": \"backup:StartCopyJob\",\n    \"StartReportJob\": \"backup:StartReportJob\",\n    \"StartRestoreJob\": \"backup:StartRestoreJob\",\n    \"StopBackupJob\": \"backup:StopBackupJob\",\n    \"TagResource\": \"backup:TagResource\",\n    \"UntagResource\": \"backup:UntagResource\",\n    \"UpdateBackupPlan\": \"backup:UpdateBackupPlan\",\n    \"UpdateFramework\": \"backup:UpdateFramework\",\n    \"UpdateGlobalSettings\": \"backup:UpdateGlobalSettings\",\n    \"UpdateRecoveryPointLifecycle\": \"backup:UpdateRecoveryPointLifecycle\",\n    \"UpdateRegionSettings\": \"backup:UpdateRegionSettings\",\n    \"UpdateReportPlan\": \"backup:UpdateReportPlan\"\n  },\n  \"backup-gateway\": {\n    \"AssociateGatewayToServer\": \"backup-gateway:AssociateGatewayToServer\",\n    \"CreateGateway\": \"backup-gateway:CreateGateway\",\n    \"DeleteGateway\": \"backup-gateway:DeleteGateway\",\n    \"DeleteHypervisor\": \"backup-gateway:DeleteHypervisor\",\n    \"DisassociateGatewayFromServer\": \"backup-gateway:DisassociateGatewayFromServer\",\n    \"ImportHypervisorConfiguration\": \"backup-gateway:ImportHypervisorConfiguration\",\n    \"ListGateways\": \"backup-gateway:ListGateways\",\n    \"ListHypervisors\": \"backup-gateway:ListHypervisors\",\n    \"ListTagsForResource\": \"backup-gateway:ListTagsForResource\",\n    \"ListVirtualMachines\": \"backup-gateway:ListVirtualMachines\",\n    \"PutMaintenanceStartTime\": \"backup-gateway:PutMaintenanceStartTime\",\n    \"TagResource\": \"backup-gateway:TagResource\",\n    \"TestHypervisorConfiguration\": \"backup-gateway:TestHypervisorConfiguration\",\n    \"UntagResource\": \"backup-gateway:UntagResource\",\n    \"UpdateGatewayInformation\": \"backup-gateway:UpdateGatewayInformation\",\n    \"UpdateHypervisor\": \"backup-gateway:UpdateHypervisor\"\n  },\n  \"batch\": {\n    \"CancelJob\": \"batch:CancelJob\",\n    \"CreateComputeEnvironment\": \"batch:CreateComputeEnvironment\",\n    \"CreateJobQueue\": \"batch:CreateJobQueue\",\n    \"CreateSchedulingPolicy\": \"batch:CreateSchedulingPolicy\",\n    \"DeleteComputeEnvironment\": \"batch:DeleteComputeEnvironment\",\n    \"DeleteJobQueue\": \"batch:DeleteJobQueue\",\n    \"DeleteSchedulingPolicy\": \"batch:DeleteSchedulingPolicy\",\n    \"DeregisterJobDefinition\": \"batch:DeregisterJobDefinition\",\n    \"DescribeComputeEnvironments\": \"batch:DescribeComputeEnvironments\",\n    \"DescribeJobDefinitions\": \"batch:DescribeJobDefinitions\",\n    \"DescribeJobQueues\": \"batch:DescribeJobQueues\",\n    \"DescribeJobs\": \"batch:DescribeJobs\",\n    \"DescribeSchedulingPolicies\": \"batch:DescribeSchedulingPolicies\",\n    \"ListJobs\": \"batch:ListJobs\",\n    \"ListSchedulingPolicies\": \"batch:ListSchedulingPolicies\",\n    \"ListTagsForResource\": \"batch:ListTagsForResource\",\n    \"RegisterJobDefinition\": \"batch:RegisterJobDefinition\",\n    \"SubmitJob\": \"batch:SubmitJob\",\n    \"TagResource\": \"batch:TagResource\",\n    \"TerminateJob\": \"batch:TerminateJob\",\n    \"UntagResource\": \"batch:UntagResource\",\n    \"UpdateComputeEnvironment\": \"batch:UpdateComputeEnvironment\",\n    \"UpdateJobQueue\": \"batch:UpdateJobQueue\",\n    \"UpdateSchedulingPolicy\": \"batch:UpdateSchedulingPolicy\"\n  },\n  \"braket\": {\n    \"CancelJob\": \"braket:CancelJob\",\n    \"CancelQuantumTask\": \"braket:CancelQuantumTask\",\n    \"CreateJob\": \"braket:CreateJob\",\n    \"CreateQuantumTask\": \"braket:CreateQuantumTask\",\n    \"GetDevice\": \"braket:GetDevice\",\n    \"GetJob\": \"braket:GetJob\",\n    \"GetQuantumTask\": \"braket:GetQuantumTask\",\n    \"ListTagsForResource\": \"braket:ListTagsForResource\",\n    \"SearchDevices\": \"braket:SearchDevices\",\n    \"SearchJobs\": \"braket:SearchJobs\",\n    \"SearchQuantumTasks\": \"braket:SearchQuantumTasks\",\n    \"TagResource\": \"braket:TagResource\",\n    \"UntagResource\": \"braket:UntagResource\"\n  },\n  \"budgets\": {\n    \"CreateBudgetAction\": \"budgets:CreateBudgetAction\",\n    \"DeleteBudgetAction\": \"budgets:DeleteBudgetAction\",\n    \"DescribeBudgetAction\": \"budgets:DescribeBudgetAction\",\n    \"DescribeBudgetActionHistories\": \"budgets:DescribeBudgetActionHistories\",\n    \"DescribeBudgetActionsForAccount\": \"budgets:DescribeBudgetActionsForAccount\",\n    \"DescribeBudgetActionsForBudget\": \"budgets:DescribeBudgetActionsForBudget\",\n    \"ExecuteBudgetAction\": \"budgets:ExecuteBudgetAction\",\n    \"UpdateBudgetAction\": \"budgets:UpdateBudgetAction\"\n  },\n  \"chime\": {\n    \"AssociatePhoneNumberWithUser\": \"chime:AssociatePhoneNumberWithUser\",\n    \"AssociatePhoneNumbersWithVoiceConnector\": \"chime:AssociatePhoneNumbersWithVoiceConnector\",\n    \"AssociatePhoneNumbersWithVoiceConnectorGroup\": \"chime:AssociatePhoneNumbersWithVoiceConnectorGroup\",\n    \"AssociateSigninDelegateGroupsWithAccount\": \"chime:AssociateSigninDelegateGroupsWithAccount\",\n    \"BatchCreateAttendee\": \"chime:BatchCreateAttendee\",\n    \"BatchCreateChannelMembership\": \"chime:BatchCreateChannelMembership\",\n    \"BatchCreateRoomMembership\": \"chime:BatchCreateRoomMembership\",\n    \"BatchDeletePhoneNumber\": \"chime:BatchDeletePhoneNumber\",\n    \"BatchSuspendUser\": \"chime:BatchSuspendUser\",\n    \"BatchUnsuspendUser\": \"chime:BatchUnsuspendUser\",\n    \"BatchUpdatePhoneNumber\": \"chime:BatchUpdatePhoneNumber\",\n    \"BatchUpdateUser\": \"chime:BatchUpdateUser\",\n    \"CreateAccount\": \"chime:CreateAccount\",\n    \"CreateAppInstance\": \"chime:CreateAppInstance\",\n    \"CreateAppInstanceAdmin\": \"chime:CreateAppInstanceAdmin\",\n    \"CreateAppInstanceUser\": \"chime:CreateAppInstanceUser\",\n    \"CreateAttendee\": \"chime:CreateAttendee\",\n    \"CreateBot\": \"chime:CreateBot\",\n    \"CreateChannel\": \"chime:CreateChannel\",\n    \"CreateChannelBan\": \"chime:CreateChannelBan\",\n    \"CreateChannelMembership\": \"chime:CreateChannelMembership\",\n    \"CreateChannelModerator\": \"chime:CreateChannelModerator\",\n    \"CreateMediaCapturePipeline\": \"chime:CreateMediaCapturePipeline\",\n    \"CreateMeeting\": \"chime:CreateMeeting\",\n    \"CreateMeetingDialOut\": \"chime:CreateMeetingDialOut\",\n    \"CreateMeetingWithAttendees\": \"chime:CreateMeetingWithAttendees\",\n    \"CreatePhoneNumberOrder\": \"chime:CreatePhoneNumberOrder\",\n    \"CreateProxySession\": \"chime:CreateProxySession\",\n    \"CreateRoom\": \"chime:CreateRoom\",\n    \"CreateRoomMembership\": \"chime:CreateRoomMembership\",\n    \"CreateSipMediaApplication\": \"chime:CreateSipMediaApplication\",\n    \"CreateSipMediaApplicationCall\": \"chime:CreateSipMediaApplicationCall\",\n    \"CreateSipRule\": \"chime:CreateSipRule\",\n    \"CreateUser\": \"chime:CreateUser\",\n    \"CreateVoiceConnector\": \"chime:CreateVoiceConnector\",\n    \"CreateVoiceConnectorGroup\": \"chime:CreateVoiceConnectorGroup\",\n    \"DeleteAccount\": \"chime:DeleteAccount\",\n    \"DeleteAppInstance\": \"chime:DeleteAppInstance\",\n    \"DeleteAppInstanceAdmin\": \"chime:DeleteAppInstanceAdmin\",\n    \"DeleteAppInstanceStreamingConfigurations\": \"chime:DeleteAppInstanceStreamingConfigurations\",\n    \"DeleteAppInstanceUser\": \"chime:DeleteAppInstanceUser\",\n    \"DeleteAttendee\": \"chime:DeleteAttendee\",\n    \"DeleteChannel\": \"chime:DeleteChannel\",\n    \"DeleteChannelBan\": \"chime:DeleteChannelBan\",\n    \"DeleteChannelMembership\": \"chime:DeleteChannelMembership\",\n    \"DeleteChannelMessage\": \"chime:DeleteChannelMessage\",\n    \"DeleteChannelModerator\": \"chime:DeleteChannelModerator\",\n    \"DeleteEventsConfiguration\": \"chime:DeleteEventsConfiguration\",\n    \"DeleteMediaCapturePipeline\": \"chime:DeleteMediaCapturePipeline\",\n    \"DeleteMeeting\": \"chime:DeleteMeeting\",\n    \"DeletePhoneNumber\": \"chime:DeletePhoneNumber\",\n    \"DeleteProxySession\": \"chime:DeleteProxySession\",\n    \"DeleteRoom\": \"chime:DeleteRoom\",\n    \"DeleteRoomMembership\": \"chime:DeleteRoomMembership\",\n    \"DeleteSipMediaApplication\": \"chime:DeleteSipMediaApplication\",\n    \"DeleteSipRule\": \"chime:DeleteSipRule\",\n    \"DeleteVoiceConnector\": \"chime:DeleteVoiceConnector\",\n    \"DeleteVoiceConnectorEmergencyCallingConfiguration\": \"chime:DeleteVoiceConnectorEmergencyCallingConfiguration\",\n    \"DeleteVoiceConnectorGroup\": \"chime:DeleteVoiceConnectorGroup\",\n    \"DeleteVoiceConnectorOrigination\": \"chime:DeleteVoiceConnectorOrigination\",\n    \"DeleteVoiceConnectorProxy\": \"chime:DeleteVoiceConnectorProxy\",\n    \"DeleteVoiceConnectorStreamingConfiguration\": \"chime:DeleteVoiceConnectorStreamingConfiguration\",\n    \"DeleteVoiceConnectorTermination\": \"chime:DeleteVoiceConnectorTermination\",\n    \"DeleteVoiceConnectorTerminationCredentials\": \"chime:DeleteVoiceConnectorTerminationCredentials\",\n    \"DescribeAppInstance\": \"chime:DescribeAppInstance\",\n    \"DescribeAppInstanceAdmin\": \"chime:DescribeAppInstanceAdmin\",\n    \"DescribeAppInstanceUser\": \"chime:DescribeAppInstanceUser\",\n    \"DescribeChannel\": \"chime:DescribeChannel\",\n    \"DescribeChannelBan\": \"chime:DescribeChannelBan\",\n    \"DescribeChannelMembership\": \"chime:DescribeChannelMembership\",\n    \"DescribeChannelMembershipForAppInstanceUser\": \"chime:DescribeChannelMembershipForAppInstanceUser\",\n    \"DescribeChannelModeratedByAppInstanceUser\": \"chime:DescribeChannelModeratedByAppInstanceUser\",\n    \"DescribeChannelModerator\": \"chime:DescribeChannelModerator\",\n    \"DisassociatePhoneNumberFromUser\": \"chime:DisassociatePhoneNumberFromUser\",\n    \"DisassociatePhoneNumbersFromVoiceConnector\": \"chime:DisassociatePhoneNumbersFromVoiceConnector\",\n    \"DisassociatePhoneNumbersFromVoiceConnectorGroup\": \"chime:DisassociatePhoneNumbersFromVoiceConnectorGroup\",\n    \"DisassociateSigninDelegateGroupsFromAccount\": \"chime:DisassociateSigninDelegateGroupsFromAccount\",\n    \"GetAccount\": \"chime:GetAccount\",\n    \"GetAccountSettings\": \"chime:GetAccountSettings\",\n    \"GetAppInstanceRetentionSettings\": \"chime:GetAppInstanceRetentionSettings\",\n    \"GetAppInstanceStreamingConfigurations\": \"chime:GetAppInstanceStreamingConfigurations\",\n    \"GetAttendee\": \"chime:GetAttendee\",\n    \"GetBot\": \"chime:GetBot\",\n    \"GetChannelMessage\": \"chime:GetChannelMessage\",\n    \"GetEventsConfiguration\": \"chime:GetEventsConfiguration\",\n    \"GetGlobalSettings\": \"chime:GetGlobalSettings\",\n    \"GetMediaCapturePipeline\": \"chime:GetMediaCapturePipeline\",\n    \"GetMeeting\": \"chime:GetMeeting\",\n    \"GetMessagingSessionEndpoint\": \"chime:GetMessagingSessionEndpoint\",\n    \"GetPhoneNumber\": \"chime:GetPhoneNumber\",\n    \"GetPhoneNumberOrder\": \"chime:GetPhoneNumberOrder\",\n    \"GetPhoneNumberSettings\": \"chime:GetPhoneNumberSettings\",\n    \"GetProxySession\": \"chime:GetProxySession\",\n    \"GetRetentionSettings\": \"chime:GetRetentionSettings\",\n    \"GetRoom\": \"chime:GetRoom\",\n    \"GetSipMediaApplication\": \"chime:GetSipMediaApplication\",\n    \"GetSipMediaApplicationLoggingConfiguration\": \"chime:GetSipMediaApplicationLoggingConfiguration\",\n    \"GetSipRule\": \"chime:GetSipRule\",\n    \"GetUser\": \"chime:GetUser\",\n    \"GetUserSettings\": \"chime:GetUserSettings\",\n    \"GetVoiceConnector\": \"chime:GetVoiceConnector\",\n    \"GetVoiceConnectorEmergencyCallingConfiguration\": \"chime:GetVoiceConnectorEmergencyCallingConfiguration\",\n    \"GetVoiceConnectorGroup\": \"chime:GetVoiceConnectorGroup\",\n    \"GetVoiceConnectorLoggingConfiguration\": \"chime:GetVoiceConnectorLoggingConfiguration\",\n    \"GetVoiceConnectorOrigination\": \"chime:GetVoiceConnectorOrigination\",\n    \"GetVoiceConnectorProxy\": \"chime:GetVoiceConnectorProxy\",\n    \"GetVoiceConnectorStreamingConfiguration\": \"chime:GetVoiceConnectorStreamingConfiguration\",\n    \"GetVoiceConnectorTermination\": \"chime:GetVoiceConnectorTermination\",\n    \"GetVoiceConnectorTerminationHealth\": \"chime:GetVoiceConnectorTerminationHealth\",\n    \"InviteUsers\": \"chime:InviteUsers\",\n    \"ListAccounts\": \"chime:ListAccounts\",\n    \"ListAppInstanceAdmins\": \"chime:ListAppInstanceAdmins\",\n    \"ListAppInstanceUsers\": \"chime:ListAppInstanceUsers\",\n    \"ListAppInstances\": \"chime:ListAppInstances\",\n    \"ListAttendeeTags\": \"chime:ListAttendeeTags\",\n    \"ListAttendees\": \"chime:ListAttendees\",\n    \"ListBots\": \"chime:ListBots\",\n    \"ListChannelBans\": \"chime:ListChannelBans\",\n    \"ListChannelMemberships\": \"chime:ListChannelMemberships\",\n    \"ListChannelMembershipsForAppInstanceUser\": \"chime:ListChannelMembershipsForAppInstanceUser\",\n    \"ListChannelMessages\": \"chime:ListChannelMessages\",\n    \"ListChannelModerators\": \"chime:ListChannelModerators\",\n    \"ListChannels\": \"chime:ListChannels\",\n    \"ListChannelsModeratedByAppInstanceUser\": \"chime:ListChannelsModeratedByAppInstanceUser\",\n    \"ListMediaCapturePipelines\": \"chime:ListMediaCapturePipelines\",\n    \"ListMeetingTags\": \"chime:ListMeetingTags\",\n    \"ListMeetings\": \"chime:ListMeetings\",\n    \"ListPhoneNumberOrders\": \"chime:ListPhoneNumberOrders\",\n    \"ListPhoneNumbers\": \"chime:ListPhoneNumbers\",\n    \"ListProxySessions\": \"chime:ListProxySessions\",\n    \"ListRoomMemberships\": \"chime:ListRoomMemberships\",\n    \"ListRooms\": \"chime:ListRooms\",\n    \"ListSipMediaApplications\": \"chime:ListSipMediaApplications\",\n    \"ListSipRules\": \"chime:ListSipRules\",\n    \"ListSupportedPhoneNumberCountries\": \"chime:ListSupportedPhoneNumberCountries\",\n    \"ListTagsForResource\": \"chime:ListTagsForResource\",\n    \"ListUsers\": \"chime:ListUsers\",\n    \"ListVoiceConnectorGroups\": \"chime:ListVoiceConnectorGroups\",\n    \"ListVoiceConnectorTerminationCredentials\": \"chime:ListVoiceConnectorTerminationCredentials\",\n    \"ListVoiceConnectors\": \"chime:ListVoiceConnectors\",\n    \"LogoutUser\": \"chime:LogoutUser\",\n    \"PutAppInstanceRetentionSettings\": \"chime:PutAppInstanceRetentionSettings\",\n    \"PutAppInstanceStreamingConfigurations\": \"chime:PutAppInstanceStreamingConfigurations\",\n    \"PutEventsConfiguration\": \"chime:PutEventsConfiguration\",\n    \"PutRetentionSettings\": \"chime:PutRetentionSettings\",\n    \"PutSipMediaApplicationLoggingConfiguration\": \"chime:PutSipMediaApplicationLoggingConfiguration\",\n    \"PutVoiceConnectorEmergencyCallingConfiguration\": \"chime:PutVoiceConnectorEmergencyCallingConfiguration\",\n    \"PutVoiceConnectorLoggingConfiguration\": \"chime:PutVoiceConnectorLoggingConfiguration\",\n    \"PutVoiceConnectorOrigination\": \"chime:PutVoiceConnectorOrigination\",\n    \"PutVoiceConnectorProxy\": \"chime:PutVoiceConnectorProxy\",\n    \"PutVoiceConnectorStreamingConfiguration\": \"chime:PutVoiceConnectorStreamingConfiguration\",\n    \"PutVoiceConnectorTermination\": \"chime:PutVoiceConnectorTermination\",\n    \"PutVoiceConnectorTerminationCredentials\": \"chime:PutVoiceConnectorTerminationCredentials\",\n    \"RedactChannelMessage\": \"chime:RedactChannelMessage\",\n    \"RedactConversationMessage\": \"chime:RedactConversationMessage\",\n    \"RedactRoomMessage\": \"chime:RedactRoomMessage\",\n    \"RegenerateSecurityToken\": \"chime:RegenerateSecurityToken\",\n    \"ResetPersonalPIN\": \"chime:ResetPersonalPIN\",\n    \"RestorePhoneNumber\": \"chime:RestorePhoneNumber\",\n    \"SearchAvailablePhoneNumbers\": \"chime:SearchAvailablePhoneNumbers\",\n    \"SendChannelMessage\": \"chime:SendChannelMessage\",\n    \"StartMeetingTranscription\": \"chime:StartMeetingTranscription\",\n    \"StopMeetingTranscription\": \"chime:StopMeetingTranscription\",\n    \"TagAttendee\": \"chime:TagAttendee\",\n    \"TagMeeting\": \"chime:TagMeeting\",\n    \"TagResource\": \"chime:TagResource\",\n    \"UntagAttendee\": \"chime:UntagAttendee\",\n    \"UntagMeeting\": \"chime:UntagMeeting\",\n    \"UntagResource\": \"chime:UntagResource\",\n    \"UpdateAccount\": \"chime:UpdateAccount\",\n    \"UpdateAccountSettings\": \"chime:UpdateAccountSettings\",\n    \"UpdateAppInstance\": \"chime:UpdateAppInstance\",\n    \"UpdateAppInstanceUser\": \"chime:UpdateAppInstanceUser\",\n    \"UpdateBot\": \"chime:UpdateBot\",\n    \"UpdateChannel\": \"chime:UpdateChannel\",\n    \"UpdateChannelMessage\": \"chime:UpdateChannelMessage\",\n    \"UpdateChannelReadMarker\": \"chime:UpdateChannelReadMarker\",\n    \"UpdateGlobalSettings\": \"chime:UpdateGlobalSettings\",\n    \"UpdatePhoneNumber\": \"chime:UpdatePhoneNumber\",\n    \"UpdatePhoneNumberSettings\": \"chime:UpdatePhoneNumberSettings\",\n    \"UpdateProxySession\": \"chime:UpdateProxySession\",\n    \"UpdateRoom\": \"chime:UpdateRoom\",\n    \"UpdateRoomMembership\": \"chime:UpdateRoomMembership\",\n    \"UpdateSipMediaApplication\": \"chime:UpdateSipMediaApplication\",\n    \"UpdateSipMediaApplicationCall\": \"chime:UpdateSipMediaApplicationCall\",\n    \"UpdateSipRule\": \"chime:UpdateSipRule\",\n    \"UpdateUser\": \"chime:UpdateUser\",\n    \"UpdateUserSettings\": \"chime:UpdateUserSettings\",\n    \"UpdateVoiceConnector\": \"chime:UpdateVoiceConnector\",\n    \"UpdateVoiceConnectorGroup\": \"chime:UpdateVoiceConnectorGroup\"\n  },\n  \"clouddirectory\": {\n    \"AddFacetToObject\": \"clouddirectory:AddFacetToObject\",\n    \"ApplySchema\": \"clouddirectory:ApplySchema\",\n    \"AttachObject\": \"clouddirectory:AttachObject\",\n    \"AttachPolicy\": \"clouddirectory:AttachPolicy\",\n    \"AttachToIndex\": \"clouddirectory:AttachToIndex\",\n    \"AttachTypedLink\": \"clouddirectory:AttachTypedLink\",\n    \"BatchRead\": \"clouddirectory:BatchRead\",\n    \"BatchWrite\": \"clouddirectory:BatchWrite\",\n    \"CreateDirectory\": \"clouddirectory:CreateDirectory\",\n    \"CreateFacet\": \"clouddirectory:CreateFacet\",\n    \"CreateIndex\": \"clouddirectory:CreateIndex\",\n    \"CreateObject\": \"clouddirectory:CreateObject\",\n    \"CreateSchema\": \"clouddirectory:CreateSchema\",\n    \"CreateTypedLinkFacet\": \"clouddirectory:CreateTypedLinkFacet\",\n    \"DeleteDirectory\": \"clouddirectory:DeleteDirectory\",\n    \"DeleteFacet\": \"clouddirectory:DeleteFacet\",\n    \"DeleteObject\": \"clouddirectory:DeleteObject\",\n    \"DeleteSchema\": \"clouddirectory:DeleteSchema\",\n    \"DeleteTypedLinkFacet\": \"clouddirectory:DeleteTypedLinkFacet\",\n    \"DetachFromIndex\": \"clouddirectory:DetachFromIndex\",\n    \"DetachObject\": \"clouddirectory:DetachObject\",\n    \"DetachPolicy\": \"clouddirectory:DetachPolicy\",\n    \"DetachTypedLink\": \"clouddirectory:DetachTypedLink\",\n    \"DisableDirectory\": \"clouddirectory:DisableDirectory\",\n    \"EnableDirectory\": \"clouddirectory:EnableDirectory\",\n    \"GetDirectory\": \"clouddirectory:GetDirectory\",\n    \"GetFacet\": \"clouddirectory:GetFacet\",\n    \"GetLinkAttributes\": \"clouddirectory:GetLinkAttributes\",\n    \"GetObjectAttributes\": \"clouddirectory:GetObjectAttributes\",\n    \"GetObjectInformation\": \"clouddirectory:GetObjectInformation\",\n    \"GetSchemaAsJson\": \"clouddirectory:GetSchemaAsJson\",\n    \"GetTypedLinkFacetInformation\": \"clouddirectory:GetTypedLinkFacetInformation\",\n    \"ListAppliedSchemaArns\": \"clouddirectory:ListAppliedSchemaArns\",\n    \"ListAttachedIndices\": \"clouddirectory:ListAttachedIndices\",\n    \"ListDevelopmentSchemaArns\": \"clouddirectory:ListDevelopmentSchemaArns\",\n    \"ListDirectories\": \"clouddirectory:ListDirectories\",\n    \"ListFacetAttributes\": \"clouddirectory:ListFacetAttributes\",\n    \"ListFacetNames\": \"clouddirectory:ListFacetNames\",\n    \"ListIncomingTypedLinks\": \"clouddirectory:ListIncomingTypedLinks\",\n    \"ListIndex\": \"clouddirectory:ListIndex\",\n    \"ListManagedSchemaArns\": \"clouddirectory:ListManagedSchemaArns\",\n    \"ListObjectAttributes\": \"clouddirectory:ListObjectAttributes\",\n    \"ListObjectChildren\": \"clouddirectory:ListObjectChildren\",\n    \"ListObjectParentPaths\": \"clouddirectory:ListObjectParentPaths\",\n    \"ListObjectParents\": \"clouddirectory:ListObjectParents\",\n    \"ListObjectPolicies\": \"clouddirectory:ListObjectPolicies\",\n    \"ListOutgoingTypedLinks\": \"clouddirectory:ListOutgoingTypedLinks\",\n    \"ListPolicyAttachments\": \"clouddirectory:ListPolicyAttachments\",\n    \"ListPublishedSchemaArns\": \"clouddirectory:ListPublishedSchemaArns\",\n    \"ListTagsForResource\": \"clouddirectory:ListTagsForResource\",\n    \"ListTypedLinkFacetAttributes\": \"clouddirectory:ListTypedLinkFacetAttributes\",\n    \"ListTypedLinkFacetNames\": \"clouddirectory:ListTypedLinkFacetNames\",\n    \"LookupPolicy\": \"clouddirectory:LookupPolicy\",\n    \"PublishSchema\": \"clouddirectory:PublishSchema\",\n    \"PutSchemaFromJson\": \"clouddirectory:PutSchemaFromJson\",\n    \"RemoveFacetFromObject\": \"clouddirectory:RemoveFacetFromObject\",\n    \"TagResource\": \"clouddirectory:TagResource\",\n    \"UntagResource\": \"clouddirectory:UntagResource\",\n    \"UpdateFacet\": \"clouddirectory:UpdateFacet\",\n    \"UpdateLinkAttributes\": \"clouddirectory:UpdateLinkAttributes\",\n    \"UpdateObjectAttributes\": \"clouddirectory:UpdateObjectAttributes\",\n    \"UpdateSchema\": \"clouddirectory:UpdateSchema\",\n    \"UpdateTypedLinkFacet\": \"clouddirectory:UpdateTypedLinkFacet\"\n  },\n  \"cloudformation\": {\n    \"ActivateType\": \"cloudformation:ActivateType\",\n    \"BatchDescribeTypeConfigurations\": \"cloudformation:BatchDescribeTypeConfigurations\",\n    \"CancelUpdateStack\": \"cloudformation:CancelUpdateStack\",\n    \"ContinueUpdateRollback\": \"cloudformation:ContinueUpdateRollback\",\n    \"CreateChangeSet\": \"cloudformation:CreateChangeSet\",\n    \"CreateStack\": \"cloudformation:CreateStack\",\n    \"CreateStackInstances\": \"cloudformation:CreateStackInstances\",\n    \"CreateStackSet\": \"cloudformation:CreateStackSet\",\n    \"DeactivateType\": \"cloudformation:DeactivateType\",\n    \"DeleteChangeSet\": \"cloudformation:DeleteChangeSet\",\n    \"DeleteStack\": \"cloudformation:DeleteStack\",\n    \"DeleteStackInstances\": \"cloudformation:DeleteStackInstances\",\n    \"DeleteStackSet\": \"cloudformation:DeleteStackSet\",\n    \"DeregisterType\": \"cloudformation:DeregisterType\",\n    \"DescribeAccountLimits\": \"cloudformation:DescribeAccountLimits\",\n    \"DescribeChangeSet\": \"cloudformation:DescribeChangeSet\",\n    \"DescribePublisher\": \"cloudformation:DescribePublisher\",\n    \"DescribeStackDriftDetectionStatus\": \"cloudformation:DescribeStackDriftDetectionStatus\",\n    \"DescribeStackEvents\": \"cloudformation:DescribeStackEvents\",\n    \"DescribeStackInstance\": \"cloudformation:DescribeStackInstance\",\n    \"DescribeStackResource\": \"cloudformation:DescribeStackResource\",\n    \"DescribeStackResourceDrifts\": \"cloudformation:DescribeStackResourceDrifts\",\n    \"DescribeStackResources\": \"cloudformation:DescribeStackResources\",\n    \"DescribeStackSet\": \"cloudformation:DescribeStackSet\",\n    \"DescribeStackSetOperation\": \"cloudformation:DescribeStackSetOperation\",\n    \"DescribeStacks\": \"cloudformation:DescribeStacks\",\n    \"DescribeType\": \"cloudformation:DescribeType\",\n    \"DescribeTypeRegistration\": \"cloudformation:DescribeTypeRegistration\",\n    \"DetectStackDrift\": \"cloudformation:DetectStackDrift\",\n    \"DetectStackResourceDrift\": \"cloudformation:DetectStackResourceDrift\",\n    \"DetectStackSetDrift\": \"cloudformation:DetectStackSetDrift\",\n    \"EstimateTemplateCost\": \"cloudformation:EstimateTemplateCost\",\n    \"ExecuteChangeSet\": \"cloudformation:ExecuteChangeSet\",\n    \"GetStackPolicy\": \"cloudformation:GetStackPolicy\",\n    \"GetTemplate\": \"cloudformation:GetTemplate\",\n    \"GetTemplateSummary\": \"cloudformation:GetTemplateSummary\",\n    \"ImportStacksToStackSet\": \"cloudformation:ImportStacksToStackSet\",\n    \"ListChangeSets\": \"cloudformation:ListChangeSets\",\n    \"ListExports\": \"cloudformation:ListExports\",\n    \"ListImports\": \"cloudformation:ListImports\",\n    \"ListStackInstances\": \"cloudformation:ListStackInstances\",\n    \"ListStackResources\": \"cloudformation:ListStackResources\",\n    \"ListStackSetOperationResults\": \"cloudformation:ListStackSetOperationResults\",\n    \"ListStackSetOperations\": \"cloudformation:ListStackSetOperations\",\n    \"ListStackSets\": \"cloudformation:ListStackSets\",\n    \"ListStacks\": \"cloudformation:ListStacks\",\n    \"ListTypeRegistrations\": \"cloudformation:ListTypeRegistrations\",\n    \"ListTypeVersions\": \"cloudformation:ListTypeVersions\",\n    \"ListTypes\": \"cloudformation:ListTypes\",\n    \"PublishType\": \"cloudformation:PublishType\",\n    \"RecordHandlerProgress\": \"cloudformation:RecordHandlerProgress\",\n    \"RegisterPublisher\": \"cloudformation:RegisterPublisher\",\n    \"RegisterType\": \"cloudformation:RegisterType\",\n    \"SetStackPolicy\": \"cloudformation:SetStackPolicy\",\n    \"SetTypeConfiguration\": \"cloudformation:SetTypeConfiguration\",\n    \"SetTypeDefaultVersion\": \"cloudformation:SetTypeDefaultVersion\",\n    \"SignalResource\": \"cloudformation:SignalResource\",\n    \"StopStackSetOperation\": \"cloudformation:StopStackSetOperation\",\n    \"TestType\": \"cloudformation:TestType\",\n    \"UpdateStack\": \"cloudformation:UpdateStack\",\n    \"UpdateStackInstances\": \"cloudformation:UpdateStackInstances\",\n    \"UpdateStackSet\": \"cloudformation:UpdateStackSet\",\n    \"UpdateTerminationProtection\": \"cloudformation:UpdateTerminationProtection\",\n    \"ValidateTemplate\": \"cloudformation:ValidateTemplate\"\n  },\n  \"cloudfront\": {\n    \"AssociateAlias\": \"cloudfront:AssociateAlias\",\n    \"CreateCachePolicy\": \"cloudfront:CreateCachePolicy\",\n    \"CreateCloudFrontOriginAccessIdentity\": \"cloudfront:CreateCloudFrontOriginAccessIdentity\",\n    \"CreateDistribution\": \"cloudfront:CreateDistribution\",\n    \"CreateDistributionWithTags\": \"cloudfront:CreateDistributionWithTags\",\n    \"CreateFieldLevelEncryptionConfig\": \"cloudfront:CreateFieldLevelEncryptionConfig\",\n    \"CreateFieldLevelEncryptionProfile\": \"cloudfront:CreateFieldLevelEncryptionProfile\",\n    \"CreateFunction\": \"cloudfront:CreateFunction\",\n    \"CreateInvalidation\": \"cloudfront:CreateInvalidation\",\n    \"CreateKeyGroup\": \"cloudfront:CreateKeyGroup\",\n    \"CreateMonitoringSubscription\": \"cloudfront:CreateMonitoringSubscription\",\n    \"CreateOriginRequestPolicy\": \"cloudfront:CreateOriginRequestPolicy\",\n    \"CreatePublicKey\": \"cloudfront:CreatePublicKey\",\n    \"CreateRealtimeLogConfig\": \"cloudfront:CreateRealtimeLogConfig\",\n    \"CreateResponseHeadersPolicy\": \"cloudfront:CreateResponseHeadersPolicy\",\n    \"CreateStreamingDistribution\": \"cloudfront:CreateStreamingDistribution\",\n    \"CreateStreamingDistributionWithTags\": \"cloudfront:CreateStreamingDistributionWithTags\",\n    \"DeleteCachePolicy\": \"cloudfront:DeleteCachePolicy\",\n    \"DeleteCloudFrontOriginAccessIdentity\": \"cloudfront:DeleteCloudFrontOriginAccessIdentity\",\n    \"DeleteDistribution\": \"cloudfront:DeleteDistribution\",\n    \"DeleteFieldLevelEncryptionConfig\": \"cloudfront:DeleteFieldLevelEncryptionConfig\",\n    \"DeleteFieldLevelEncryptionProfile\": \"cloudfront:DeleteFieldLevelEncryptionProfile\",\n    \"DeleteFunction\": \"cloudfront:DeleteFunction\",\n    \"DeleteKeyGroup\": \"cloudfront:DeleteKeyGroup\",\n    \"DeleteMonitoringSubscription\": \"cloudfront:DeleteMonitoringSubscription\",\n    \"DeleteOriginRequestPolicy\": \"cloudfront:DeleteOriginRequestPolicy\",\n    \"DeletePublicKey\": \"cloudfront:DeletePublicKey\",\n    \"DeleteRealtimeLogConfig\": \"cloudfront:DeleteRealtimeLogConfig\",\n    \"DeleteResponseHeadersPolicy\": \"cloudfront:DeleteResponseHeadersPolicy\",\n    \"DeleteStreamingDistribution\": \"cloudfront:DeleteStreamingDistribution\",\n    \"DescribeFunction\": \"cloudfront:DescribeFunction\",\n    \"GetCachePolicy\": \"cloudfront:GetCachePolicy\",\n    \"GetCachePolicyConfig\": \"cloudfront:GetCachePolicyConfig\",\n    \"GetCloudFrontOriginAccessIdentity\": \"cloudfront:GetCloudFrontOriginAccessIdentity\",\n    \"GetCloudFrontOriginAccessIdentityConfig\": \"cloudfront:GetCloudFrontOriginAccessIdentityConfig\",\n    \"GetDistribution\": \"cloudfront:GetDistribution\",\n    \"GetDistributionConfig\": \"cloudfront:GetDistributionConfig\",\n    \"GetFieldLevelEncryption\": \"cloudfront:GetFieldLevelEncryption\",\n    \"GetFieldLevelEncryptionConfig\": \"cloudfront:GetFieldLevelEncryptionConfig\",\n    \"GetFieldLevelEncryptionProfile\": \"cloudfront:GetFieldLevelEncryptionProfile\",\n    \"GetFieldLevelEncryptionProfileConfig\": \"cloudfront:GetFieldLevelEncryptionProfileConfig\",\n    \"GetFunction\": \"cloudfront:GetFunction\",\n    \"GetInvalidation\": \"cloudfront:GetInvalidation\",\n    \"GetKeyGroup\": \"cloudfront:GetKeyGroup\",\n    \"GetKeyGroupConfig\": \"cloudfront:GetKeyGroupConfig\",\n    \"GetMonitoringSubscription\": \"cloudfront:GetMonitoringSubscription\",\n    \"GetOriginRequestPolicy\": \"cloudfront:GetOriginRequestPolicy\",\n    \"GetOriginRequestPolicyConfig\": \"cloudfront:GetOriginRequestPolicyConfig\",\n    \"GetPublicKey\": \"cloudfront:GetPublicKey\",\n    \"GetPublicKeyConfig\": \"cloudfront:GetPublicKeyConfig\",\n    \"GetRealtimeLogConfig\": \"cloudfront:GetRealtimeLogConfig\",\n    \"GetResponseHeadersPolicy\": \"cloudfront:GetResponseHeadersPolicy\",\n    \"GetResponseHeadersPolicyConfig\": \"cloudfront:GetResponseHeadersPolicyConfig\",\n    \"GetStreamingDistribution\": \"cloudfront:GetStreamingDistribution\",\n    \"GetStreamingDistributionConfig\": \"cloudfront:GetStreamingDistributionConfig\",\n    \"ListCachePolicies\": \"cloudfront:ListCachePolicies\",\n    \"ListCloudFrontOriginAccessIdentities\": \"cloudfront:ListCloudFrontOriginAccessIdentities\",\n    \"ListConflictingAliases\": \"cloudfront:ListConflictingAliases\",\n    \"ListDistributions\": \"cloudfront:ListDistributions\",\n    \"ListDistributionsByCachePolicyId\": \"cloudfront:ListDistributionsByCachePolicyId\",\n    \"ListDistributionsByKeyGroup\": \"cloudfront:ListDistributionsByKeyGroup\",\n    \"ListDistributionsByOriginRequestPolicyId\": \"cloudfront:ListDistributionsByOriginRequestPolicyId\",\n    \"ListDistributionsByRealtimeLogConfig\": \"cloudfront:ListDistributionsByRealtimeLogConfig\",\n    \"ListDistributionsByResponseHeadersPolicyId\": \"cloudfront:ListDistributionsByResponseHeadersPolicyId\",\n    \"ListDistributionsByWebACLId\": \"cloudfront:ListDistributionsByWebACLId\",\n    \"ListFieldLevelEncryptionConfigs\": \"cloudfront:ListFieldLevelEncryptionConfigs\",\n    \"ListFieldLevelEncryptionProfiles\": \"cloudfront:ListFieldLevelEncryptionProfiles\",\n    \"ListFunctions\": \"cloudfront:ListFunctions\",\n    \"ListInvalidations\": \"cloudfront:ListInvalidations\",\n    \"ListKeyGroups\": \"cloudfront:ListKeyGroups\",\n    \"ListOriginRequestPolicies\": \"cloudfront:ListOriginRequestPolicies\",\n    \"ListPublicKeys\": \"cloudfront:ListPublicKeys\",\n    \"ListRealtimeLogConfigs\": \"cloudfront:ListRealtimeLogConfigs\",\n    \"ListResponseHeadersPolicies\": \"cloudfront:ListResponseHeadersPolicies\",\n    \"ListStreamingDistributions\": \"cloudfront:ListStreamingDistributions\",\n    \"ListTagsForResource\": \"cloudfront:ListTagsForResource\",\n    \"PublishFunction\": \"cloudfront:PublishFunction\",\n    \"TagResource\": \"cloudfront:TagResource\",\n    \"TestFunction\": \"cloudfront:TestFunction\",\n    \"UntagResource\": \"cloudfront:UntagResource\",\n    \"UpdateCachePolicy\": \"cloudfront:UpdateCachePolicy\",\n    \"UpdateCloudFrontOriginAccessIdentity\": \"cloudfront:UpdateCloudFrontOriginAccessIdentity\",\n    \"UpdateDistribution\": \"cloudfront:UpdateDistribution\",\n    \"UpdateFieldLevelEncryptionConfig\": \"cloudfront:UpdateFieldLevelEncryptionConfig\",\n    \"UpdateFieldLevelEncryptionProfile\": \"cloudfront:UpdateFieldLevelEncryptionProfile\",\n    \"UpdateFunction\": \"cloudfront:UpdateFunction\",\n    \"UpdateKeyGroup\": \"cloudfront:UpdateKeyGroup\",\n    \"UpdateOriginRequestPolicy\": \"cloudfront:UpdateOriginRequestPolicy\",\n    \"UpdatePublicKey\": \"cloudfront:UpdatePublicKey\",\n    \"UpdateRealtimeLogConfig\": \"cloudfront:UpdateRealtimeLogConfig\",\n    \"UpdateResponseHeadersPolicy\": \"cloudfront:UpdateResponseHeadersPolicy\",\n    \"UpdateStreamingDistribution\": \"cloudfront:UpdateStreamingDistribution\"\n  },\n  \"cloudhsm\": {\n    \"AddTagsToResource\": \"cloudhsm:AddTagsToResource\",\n    \"CreateHapg\": \"cloudhsm:CreateHapg\",\n    \"CreateHsm\": \"cloudhsm:CreateHsm\",\n    \"CreateLunaClient\": \"cloudhsm:CreateLunaClient\",\n    \"DeleteHapg\": \"cloudhsm:DeleteHapg\",\n    \"DeleteHsm\": \"cloudhsm:DeleteHsm\",\n    \"DeleteLunaClient\": \"cloudhsm:DeleteLunaClient\",\n    \"DescribeHapg\": \"cloudhsm:DescribeHapg\",\n    \"DescribeHsm\": \"cloudhsm:DescribeHsm\",\n    \"DescribeLunaClient\": \"cloudhsm:DescribeLunaClient\",\n    \"GetConfig\": \"cloudhsm:GetConfig\",\n    \"ListAvailableZones\": \"cloudhsm:ListAvailableZones\",\n    \"ListHapgs\": \"cloudhsm:ListHapgs\",\n    \"ListHsms\": \"cloudhsm:ListHsms\",\n    \"ListLunaClients\": \"cloudhsm:ListLunaClients\",\n    \"ListTagsForResource\": \"cloudhsm:ListTagsForResource\",\n    \"ModifyHapg\": \"cloudhsm:ModifyHapg\",\n    \"ModifyHsm\": \"cloudhsm:ModifyHsm\",\n    \"ModifyLunaClient\": \"cloudhsm:ModifyLunaClient\",\n    \"RemoveTagsFromResource\": \"cloudhsm:RemoveTagsFromResource\"\n  },\n  \"cloudhsmv2\": {\n    \"CopyBackupToRegion\": \"cloudhsm:CopyBackupToRegion\",\n    \"CreateCluster\": \"cloudhsm:CreateCluster\",\n    \"CreateHsm\": \"cloudhsm:CreateHsm\",\n    \"DeleteBackup\": \"cloudhsm:DeleteBackup\",\n    \"DeleteCluster\": \"cloudhsm:DeleteCluster\",\n    \"DeleteHsm\": \"cloudhsm:DeleteHsm\",\n    \"DescribeBackups\": \"cloudhsm:DescribeBackups\",\n    \"DescribeClusters\": \"cloudhsm:DescribeClusters\",\n    \"InitializeCluster\": \"cloudhsm:InitializeCluster\",\n    \"ListTags\": \"cloudhsm:ListTags\",\n    \"ModifyBackupAttributes\": \"cloudhsm:ModifyBackupAttributes\",\n    \"ModifyCluster\": \"cloudhsm:ModifyCluster\",\n    \"RestoreBackup\": \"cloudhsm:RestoreBackup\",\n    \"TagResource\": \"cloudhsm:TagResource\",\n    \"UntagResource\": \"cloudhsm:UntagResource\"\n  },\n  \"cloudsearch\": {\n    \"BuildSuggesters\": \"cloudsearch:BuildSuggesters\",\n    \"CreateDomain\": \"cloudsearch:CreateDomain\",\n    \"DefineAnalysisScheme\": \"cloudsearch:DefineAnalysisScheme\",\n    \"DefineExpression\": \"cloudsearch:DefineExpression\",\n    \"DefineIndexField\": \"cloudsearch:DefineIndexField\",\n    \"DefineSuggester\": \"cloudsearch:DefineSuggester\",\n    \"DeleteAnalysisScheme\": \"cloudsearch:DeleteAnalysisScheme\",\n    \"DeleteDomain\": \"cloudsearch:DeleteDomain\",\n    \"DeleteExpression\": \"cloudsearch:DeleteExpression\",\n    \"DeleteIndexField\": \"cloudsearch:DeleteIndexField\",\n    \"DeleteSuggester\": \"cloudsearch:DeleteSuggester\",\n    \"DescribeAnalysisSchemes\": \"cloudsearch:DescribeAnalysisSchemes\",\n    \"DescribeAvailabilityOptions\": \"cloudsearch:DescribeAvailabilityOptions\",\n    \"DescribeDomainEndpointOptions\": \"cloudsearch:DescribeDomainEndpointOptions\",\n    \"DescribeDomains\": \"cloudsearch:DescribeDomains\",\n    \"DescribeExpressions\": \"cloudsearch:DescribeExpressions\",\n    \"DescribeIndexFields\": \"cloudsearch:DescribeIndexFields\",\n    \"DescribeScalingParameters\": \"cloudsearch:DescribeScalingParameters\",\n    \"DescribeServiceAccessPolicies\": \"cloudsearch:DescribeServiceAccessPolicies\",\n    \"DescribeSuggesters\": \"cloudsearch:DescribeSuggesters\",\n    \"IndexDocuments\": \"cloudsearch:IndexDocuments\",\n    \"ListDomainNames\": \"cloudsearch:ListDomainNames\",\n    \"UpdateAvailabilityOptions\": \"cloudsearch:UpdateAvailabilityOptions\",\n    \"UpdateDomainEndpointOptions\": \"cloudsearch:UpdateDomainEndpointOptions\",\n    \"UpdateScalingParameters\": \"cloudsearch:UpdateScalingParameters\",\n    \"UpdateServiceAccessPolicies\": \"cloudsearch:UpdateServiceAccessPolicies\"\n  },\n  \"cloudtrail\": {\n    \"AddTags\": \"cloudtrail:AddTags\",\n    \"CreateTrail\": \"cloudtrail:CreateTrail\",\n    \"DeleteTrail\": \"cloudtrail:DeleteTrail\",\n    \"DescribeTrails\": \"cloudtrail:DescribeTrails\",\n    \"GetEventSelectors\": \"cloudtrail:GetEventSelectors\",\n    \"GetInsightSelectors\": \"cloudtrail:GetInsightSelectors\",\n    \"GetTrail\": \"cloudtrail:GetTrail\",\n    \"GetTrailStatus\": \"cloudtrail:GetTrailStatus\",\n    \"ListPublicKeys\": \"cloudtrail:ListPublicKeys\",\n    \"ListTags\": \"cloudtrail:ListTags\",\n    \"ListTrails\": \"cloudtrail:ListTrails\",\n    \"LookupEvents\": \"cloudtrail:LookupEvents\",\n    \"PutEventSelectors\": \"cloudtrail:PutEventSelectors\",\n    \"PutInsightSelectors\": \"cloudtrail:PutInsightSelectors\",\n    \"RemoveTags\": \"cloudtrail:RemoveTags\",\n    \"StartLogging\": \"cloudtrail:StartLogging\",\n    \"StopLogging\": \"cloudtrail:StopLogging\",\n    \"UpdateTrail\": \"cloudtrail:UpdateTrail\"\n  },\n  \"cloudwatch\": {\n    \"DeleteAlarms\": \"cloudwatch:DeleteAlarms\",\n    \"DeleteAnomalyDetector\": \"cloudwatch:DeleteAnomalyDetector\",\n    \"DeleteDashboards\": \"cloudwatch:DeleteDashboards\",\n    \"DeleteInsightRules\": \"cloudwatch:DeleteInsightRules\",\n    \"DeleteMetricStream\": \"cloudwatch:DeleteMetricStream\",\n    \"DescribeAlarmHistory\": \"cloudwatch:DescribeAlarmHistory\",\n    \"DescribeAlarms\": \"cloudwatch:DescribeAlarms\",\n    \"DescribeAlarmsForMetric\": \"cloudwatch:DescribeAlarmsForMetric\",\n    \"DescribeAnomalyDetectors\": \"cloudwatch:DescribeAnomalyDetectors\",\n    \"DescribeInsightRules\": \"cloudwatch:DescribeInsightRules\",\n    \"DisableAlarmActions\": \"cloudwatch:DisableAlarmActions\",\n    \"DisableInsightRules\": \"cloudwatch:DisableInsightRules\",\n    \"EnableAlarmActions\": \"cloudwatch:EnableAlarmActions\",\n    \"EnableInsightRules\": \"cloudwatch:EnableInsightRules\",\n    \"GetDashboard\": \"cloudwatch:GetDashboard\",\n    \"GetInsightRuleReport\": \"cloudwatch:GetInsightRuleReport\",\n    \"GetMetricData\": \"cloudwatch:GetMetricData\",\n    \"GetMetricStatistics\": \"cloudwatch:GetMetricStatistics\",\n    \"GetMetricStream\": \"cloudwatch:GetMetricStream\",\n    \"GetMetricWidgetImage\": \"cloudwatch:GetMetricWidgetImage\",\n    \"ListDashboards\": \"cloudwatch:ListDashboards\",\n    \"ListMetricStreams\": \"cloudwatch:ListMetricStreams\",\n    \"ListMetrics\": \"cloudwatch:ListMetrics\",\n    \"ListTagsForResource\": \"cloudwatch:ListTagsForResource\",\n    \"PutAnomalyDetector\": \"cloudwatch:PutAnomalyDetector\",\n    \"PutCompositeAlarm\": \"cloudwatch:PutCompositeAlarm\",\n    \"PutDashboard\": \"cloudwatch:PutDashboard\",\n    \"PutInsightRule\": \"cloudwatch:PutInsightRule\",\n    \"PutMetricAlarm\": \"cloudwatch:PutMetricAlarm\",\n    \"PutMetricData\": \"cloudwatch:PutMetricData\",\n    \"PutMetricStream\": \"cloudwatch:PutMetricStream\",\n    \"SetAlarmState\": \"cloudwatch:SetAlarmState\",\n    \"StartMetricStreams\": \"cloudwatch:StartMetricStreams\",\n    \"StopMetricStreams\": \"cloudwatch:StopMetricStreams\",\n    \"TagResource\": \"cloudwatch:TagResource\",\n    \"UntagResource\": \"cloudwatch:UntagResource\"\n  },\n  \"codeartifact\": {\n    \"AssociateExternalConnection\": \"codeartifact:AssociateExternalConnection\",\n    \"CopyPackageVersions\": \"codeartifact:CopyPackageVersions\",\n    \"CreateDomain\": \"codeartifact:CreateDomain\",\n    \"CreateRepository\": \"codeartifact:CreateRepository\",\n    \"DeleteDomain\": \"codeartifact:DeleteDomain\",\n    \"DeleteDomainPermissionsPolicy\": \"codeartifact:DeleteDomainPermissionsPolicy\",\n    \"DeletePackageVersions\": \"codeartifact:DeletePackageVersions\",\n    \"DeleteRepository\": \"codeartifact:DeleteRepository\",\n    \"DeleteRepositoryPermissionsPolicy\": \"codeartifact:DeleteRepositoryPermissionsPolicy\",\n    \"DescribeDomain\": \"codeartifact:DescribeDomain\",\n    \"DescribePackageVersion\": \"codeartifact:DescribePackageVersion\",\n    \"DescribeRepository\": \"codeartifact:DescribeRepository\",\n    \"DisassociateExternalConnection\": \"codeartifact:DisassociateExternalConnection\",\n    \"DisposePackageVersions\": \"codeartifact:DisposePackageVersions\",\n    \"GetAuthorizationToken\": \"codeartifact:GetAuthorizationToken\",\n    \"GetDomainPermissionsPolicy\": \"codeartifact:GetDomainPermissionsPolicy\",\n    \"GetPackageVersionAsset\": \"codeartifact:GetPackageVersionAsset\",\n    \"GetPackageVersionReadme\": \"codeartifact:GetPackageVersionReadme\",\n    \"GetRepositoryEndpoint\": \"codeartifact:GetRepositoryEndpoint\",\n    \"GetRepositoryPermissionsPolicy\": \"codeartifact:GetRepositoryPermissionsPolicy\",\n    \"ListDomains\": \"codeartifact:ListDomains\",\n    \"ListPackageVersionAssets\": \"codeartifact:ListPackageVersionAssets\",\n    \"ListPackageVersionDependencies\": \"codeartifact:ListPackageVersionDependencies\",\n    \"ListPackageVersions\": \"codeartifact:ListPackageVersions\",\n    \"ListPackages\": \"codeartifact:ListPackages\",\n    \"ListRepositories\": \"codeartifact:ListRepositories\",\n    \"ListRepositoriesInDomain\": \"codeartifact:ListRepositoriesInDomain\",\n    \"ListTagsForResource\": \"codeartifact:ListTagsForResource\",\n    \"PutDomainPermissionsPolicy\": \"codeartifact:PutDomainPermissionsPolicy\",\n    \"PutRepositoryPermissionsPolicy\": \"codeartifact:PutRepositoryPermissionsPolicy\",\n    \"TagResource\": \"codeartifact:TagResource\",\n    \"UntagResource\": \"codeartifact:UntagResource\",\n    \"UpdatePackageVersionsStatus\": \"codeartifact:UpdatePackageVersionsStatus\",\n    \"UpdateRepository\": \"codeartifact:UpdateRepository\"\n  },\n  \"codebuild\": {\n    \"BatchDeleteBuilds\": \"codebuild:BatchDeleteBuilds\",\n    \"BatchGetBuildBatches\": \"codebuild:BatchGetBuildBatches\",\n    \"BatchGetBuilds\": \"codebuild:BatchGetBuilds\",\n    \"BatchGetProjects\": \"codebuild:BatchGetProjects\",\n    \"BatchGetReportGroups\": \"codebuild:BatchGetReportGroups\",\n    \"BatchGetReports\": \"codebuild:BatchGetReports\",\n    \"CreateProject\": \"codebuild:CreateProject\",\n    \"CreateReportGroup\": \"codebuild:CreateReportGroup\",\n    \"CreateWebhook\": \"codebuild:CreateWebhook\",\n    \"DeleteBuildBatch\": \"codebuild:DeleteBuildBatch\",\n    \"DeleteProject\": \"codebuild:DeleteProject\",\n    \"DeleteReport\": \"codebuild:DeleteReport\",\n    \"DeleteReportGroup\": \"codebuild:DeleteReportGroup\",\n    \"DeleteResourcePolicy\": \"codebuild:DeleteResourcePolicy\",\n    \"DeleteSourceCredentials\": \"codebuild:DeleteSourceCredentials\",\n    \"DeleteWebhook\": \"codebuild:DeleteWebhook\",\n    \"DescribeCodeCoverages\": \"codebuild:DescribeCodeCoverages\",\n    \"DescribeTestCases\": \"codebuild:DescribeTestCases\",\n    \"GetReportGroupTrend\": \"codebuild:GetReportGroupTrend\",\n    \"GetResourcePolicy\": \"codebuild:GetResourcePolicy\",\n    \"ImportSourceCredentials\": \"codebuild:ImportSourceCredentials\",\n    \"InvalidateProjectCache\": \"codebuild:InvalidateProjectCache\",\n    \"ListBuildBatches\": \"codebuild:ListBuildBatches\",\n    \"ListBuildBatchesForProject\": \"codebuild:ListBuildBatchesForProject\",\n    \"ListBuilds\": \"codebuild:ListBuilds\",\n    \"ListBuildsForProject\": \"codebuild:ListBuildsForProject\",\n    \"ListCuratedEnvironmentImages\": \"codebuild:ListCuratedEnvironmentImages\",\n    \"ListProjects\": \"codebuild:ListProjects\",\n    \"ListReportGroups\": \"codebuild:ListReportGroups\",\n    \"ListReports\": \"codebuild:ListReports\",\n    \"ListReportsForReportGroup\": \"codebuild:ListReportsForReportGroup\",\n    \"ListSharedProjects\": \"codebuild:ListSharedProjects\",\n    \"ListSharedReportGroups\": \"codebuild:ListSharedReportGroups\",\n    \"ListSourceCredentials\": \"codebuild:ListSourceCredentials\",\n    \"PutResourcePolicy\": \"codebuild:PutResourcePolicy\",\n    \"RetryBuild\": \"codebuild:RetryBuild\",\n    \"RetryBuildBatch\": \"codebuild:RetryBuildBatch\",\n    \"StartBuild\": \"codebuild:StartBuild\",\n    \"StartBuildBatch\": \"codebuild:StartBuildBatch\",\n    \"StopBuild\": \"codebuild:StopBuild\",\n    \"StopBuildBatch\": \"codebuild:StopBuildBatch\",\n    \"UpdateProject\": \"codebuild:UpdateProject\",\n    \"UpdateProjectVisibility\": \"codebuild:UpdateProjectVisibility\",\n    \"UpdateReportGroup\": \"codebuild:UpdateReportGroup\",\n    \"UpdateWebhook\": \"codebuild:UpdateWebhook\"\n  },\n  \"codecommit\": {\n    \"AssociateApprovalRuleTemplateWithRepository\": \"codecommit:AssociateApprovalRuleTemplateWithRepository\",\n    \"BatchAssociateApprovalRuleTemplateWithRepositories\": \"codecommit:BatchAssociateApprovalRuleTemplateWithRepositories\",\n    \"BatchDescribeMergeConflicts\": \"codecommit:BatchDescribeMergeConflicts\",\n    \"BatchDisassociateApprovalRuleTemplateFromRepositories\": \"codecommit:BatchDisassociateApprovalRuleTemplateFromRepositories\",\n    \"BatchGetCommits\": \"codecommit:BatchGetCommits\",\n    \"BatchGetRepositories\": \"codecommit:BatchGetRepositories\",\n    \"CreateApprovalRuleTemplate\": \"codecommit:CreateApprovalRuleTemplate\",\n    \"CreateBranch\": \"codecommit:CreateBranch\",\n    \"CreateCommit\": \"codecommit:CreateCommit\",\n    \"CreatePullRequest\": \"codecommit:CreatePullRequest\",\n    \"CreatePullRequestApprovalRule\": \"codecommit:CreatePullRequestApprovalRule\",\n    \"CreateRepository\": \"codecommit:CreateRepository\",\n    \"CreateUnreferencedMergeCommit\": \"codecommit:CreateUnreferencedMergeCommit\",\n    \"DeleteApprovalRuleTemplate\": \"codecommit:DeleteApprovalRuleTemplate\",\n    \"DeleteBranch\": \"codecommit:DeleteBranch\",\n    \"DeleteCommentContent\": \"codecommit:DeleteCommentContent\",\n    \"DeleteFile\": \"codecommit:DeleteFile\",\n    \"DeletePullRequestApprovalRule\": \"codecommit:DeletePullRequestApprovalRule\",\n    \"DeleteRepository\": \"codecommit:DeleteRepository\",\n    \"DescribeMergeConflicts\": \"codecommit:DescribeMergeConflicts\",\n    \"DescribePullRequestEvents\": \"codecommit:DescribePullRequestEvents\",\n    \"DisassociateApprovalRuleTemplateFromRepository\": \"codecommit:DisassociateApprovalRuleTemplateFromRepository\",\n    \"EvaluatePullRequestApprovalRules\": \"codecommit:EvaluatePullRequestApprovalRules\",\n    \"GetApprovalRuleTemplate\": \"codecommit:GetApprovalRuleTemplate\",\n    \"GetBlob\": \"codecommit:GetBlob\",\n    \"GetBranch\": \"codecommit:GetBranch\",\n    \"GetComment\": \"codecommit:GetComment\",\n    \"GetCommentReactions\": \"codecommit:GetCommentReactions\",\n    \"GetCommentsForComparedCommit\": \"codecommit:GetCommentsForComparedCommit\",\n    \"GetCommentsForPullRequest\": \"codecommit:GetCommentsForPullRequest\",\n    \"GetCommit\": \"codecommit:GetCommit\",\n    \"GetDifferences\": \"codecommit:GetDifferences\",\n    \"GetFile\": \"codecommit:GetFile\",\n    \"GetFolder\": \"codecommit:GetFolder\",\n    \"GetMergeCommit\": \"codecommit:GetMergeCommit\",\n    \"GetMergeConflicts\": \"codecommit:GetMergeConflicts\",\n    \"GetMergeOptions\": \"codecommit:GetMergeOptions\",\n    \"GetPullRequest\": \"codecommit:GetPullRequest\",\n    \"GetPullRequestApprovalStates\": \"codecommit:GetPullRequestApprovalStates\",\n    \"GetPullRequestOverrideState\": \"codecommit:GetPullRequestOverrideState\",\n    \"GetRepository\": \"codecommit:GetRepository\",\n    \"GetRepositoryTriggers\": \"codecommit:GetRepositoryTriggers\",\n    \"ListApprovalRuleTemplates\": \"codecommit:ListApprovalRuleTemplates\",\n    \"ListAssociatedApprovalRuleTemplatesForRepository\": \"codecommit:ListAssociatedApprovalRuleTemplatesForRepository\",\n    \"ListBranches\": \"codecommit:ListBranches\",\n    \"ListPullRequests\": \"codecommit:ListPullRequests\",\n    \"ListRepositories\": \"codecommit:ListRepositories\",\n    \"ListRepositoriesForApprovalRuleTemplate\": \"codecommit:ListRepositoriesForApprovalRuleTemplate\",\n    \"ListTagsForResource\": \"codecommit:ListTagsForResource\",\n    \"MergeBranchesByFastForward\": \"codecommit:MergeBranchesByFastForward\",\n    \"MergeBranchesBySquash\": \"codecommit:MergeBranchesBySquash\",\n    \"MergeBranchesByThreeWay\": \"codecommit:MergeBranchesByThreeWay\",\n    \"MergePullRequestByFastForward\": \"codecommit:MergePullRequestByFastForward\",\n    \"MergePullRequestBySquash\": \"codecommit:MergePullRequestBySquash\",\n    \"MergePullRequestByThreeWay\": \"codecommit:MergePullRequestByThreeWay\",\n    \"OverridePullRequestApprovalRules\": \"codecommit:OverridePullRequestApprovalRules\",\n    \"PostCommentForComparedCommit\": \"codecommit:PostCommentForComparedCommit\",\n    \"PostCommentForPullRequest\": \"codecommit:PostCommentForPullRequest\",\n    \"PostCommentReply\": \"codecommit:PostCommentReply\",\n    \"PutCommentReaction\": \"codecommit:PutCommentReaction\",\n    \"PutFile\": \"codecommit:PutFile\",\n    \"PutRepositoryTriggers\": \"codecommit:PutRepositoryTriggers\",\n    \"TagResource\": \"codecommit:TagResource\",\n    \"TestRepositoryTriggers\": \"codecommit:TestRepositoryTriggers\",\n    \"UntagResource\": \"codecommit:UntagResource\",\n    \"UpdateApprovalRuleTemplateContent\": \"codecommit:UpdateApprovalRuleTemplateContent\",\n    \"UpdateApprovalRuleTemplateDescription\": \"codecommit:UpdateApprovalRuleTemplateDescription\",\n    \"UpdateApprovalRuleTemplateName\": \"codecommit:UpdateApprovalRuleTemplateName\",\n    \"UpdateComment\": \"codecommit:UpdateComment\",\n    \"UpdateDefaultBranch\": \"codecommit:UpdateDefaultBranch\",\n    \"UpdatePullRequestApprovalRuleContent\": \"codecommit:UpdatePullRequestApprovalRuleContent\",\n    \"UpdatePullRequestApprovalState\": \"codecommit:UpdatePullRequestApprovalState\",\n    \"UpdatePullRequestDescription\": \"codecommit:UpdatePullRequestDescription\",\n    \"UpdatePullRequestStatus\": \"codecommit:UpdatePullRequestStatus\",\n    \"UpdatePullRequestTitle\": \"codecommit:UpdatePullRequestTitle\",\n    \"UpdateRepositoryDescription\": \"codecommit:UpdateRepositoryDescription\",\n    \"UpdateRepositoryName\": \"codecommit:UpdateRepositoryName\"\n  },\n  \"codedeploy\": {\n    \"AddTagsToOnPremisesInstances\": \"codedeploy:AddTagsToOnPremisesInstances\",\n    \"BatchGetApplicationRevisions\": \"codedeploy:BatchGetApplicationRevisions\",\n    \"BatchGetApplications\": \"codedeploy:BatchGetApplications\",\n    \"BatchGetDeploymentGroups\": \"codedeploy:BatchGetDeploymentGroups\",\n    \"BatchGetDeploymentInstances\": \"codedeploy:BatchGetDeploymentInstances\",\n    \"BatchGetDeploymentTargets\": \"codedeploy:BatchGetDeploymentTargets\",\n    \"BatchGetDeployments\": \"codedeploy:BatchGetDeployments\",\n    \"BatchGetOnPremisesInstances\": \"codedeploy:BatchGetOnPremisesInstances\",\n    \"ContinueDeployment\": \"codedeploy:ContinueDeployment\",\n    \"CreateApplication\": \"codedeploy:CreateApplication\",\n    \"CreateDeployment\": \"codedeploy:CreateDeployment\",\n    \"CreateDeploymentConfig\": \"codedeploy:CreateDeploymentConfig\",\n    \"CreateDeploymentGroup\": \"codedeploy:CreateDeploymentGroup\",\n    \"DeleteApplication\": \"codedeploy:DeleteApplication\",\n    \"DeleteDeploymentConfig\": \"codedeploy:DeleteDeploymentConfig\",\n    \"DeleteDeploymentGroup\": \"codedeploy:DeleteDeploymentGroup\",\n    \"DeleteGitHubAccountToken\": \"codedeploy:DeleteGitHubAccountToken\",\n    \"DeleteResourcesByExternalId\": \"codedeploy:DeleteResourcesByExternalId\",\n    \"DeregisterOnPremisesInstance\": \"codedeploy:DeregisterOnPremisesInstance\",\n    \"GetApplication\": \"codedeploy:GetApplication\",\n    \"GetApplicationRevision\": \"codedeploy:GetApplicationRevision\",\n    \"GetDeployment\": \"codedeploy:GetDeployment\",\n    \"GetDeploymentConfig\": \"codedeploy:GetDeploymentConfig\",\n    \"GetDeploymentGroup\": \"codedeploy:GetDeploymentGroup\",\n    \"GetDeploymentInstance\": \"codedeploy:GetDeploymentInstance\",\n    \"GetDeploymentTarget\": \"codedeploy:GetDeploymentTarget\",\n    \"GetOnPremisesInstance\": \"codedeploy:GetOnPremisesInstance\",\n    \"ListApplicationRevisions\": \"codedeploy:ListApplicationRevisions\",\n    \"ListApplications\": \"codedeploy:ListApplications\",\n    \"ListDeploymentConfigs\": \"codedeploy:ListDeploymentConfigs\",\n    \"ListDeploymentGroups\": \"codedeploy:ListDeploymentGroups\",\n    \"ListDeploymentInstances\": \"codedeploy:ListDeploymentInstances\",\n    \"ListDeploymentTargets\": \"codedeploy:ListDeploymentTargets\",\n    \"ListDeployments\": \"codedeploy:ListDeployments\",\n    \"ListGitHubAccountTokenNames\": \"codedeploy:ListGitHubAccountTokenNames\",\n    \"ListOnPremisesInstances\": \"codedeploy:ListOnPremisesInstances\",\n    \"ListTagsForResource\": \"codedeploy:ListTagsForResource\",\n    \"PutLifecycleEventHookExecutionStatus\": \"codedeploy:PutLifecycleEventHookExecutionStatus\",\n    \"RegisterApplicationRevision\": \"codedeploy:RegisterApplicationRevision\",\n    \"RegisterOnPremisesInstance\": \"codedeploy:RegisterOnPremisesInstance\",\n    \"RemoveTagsFromOnPremisesInstances\": \"codedeploy:RemoveTagsFromOnPremisesInstances\",\n    \"SkipWaitTimeForInstanceTermination\": \"codedeploy:SkipWaitTimeForInstanceTermination\",\n    \"StopDeployment\": \"codedeploy:StopDeployment\",\n    \"TagResource\": \"codedeploy:TagResource\",\n    \"UntagResource\": \"codedeploy:UntagResource\",\n    \"UpdateApplication\": \"codedeploy:UpdateApplication\",\n    \"UpdateDeploymentGroup\": \"codedeploy:UpdateDeploymentGroup\"\n  },\n  \"codeguru-reviewer\": {\n    \"AssociateRepository\": \"codeguru-reviewer:AssociateRepository\",\n    \"CreateCodeReview\": \"codeguru-reviewer:CreateCodeReview\",\n    \"DescribeCodeReview\": \"codeguru-reviewer:DescribeCodeReview\",\n    \"DescribeRecommendationFeedback\": \"codeguru-reviewer:DescribeRecommendationFeedback\",\n    \"DescribeRepositoryAssociation\": \"codeguru-reviewer:DescribeRepositoryAssociation\",\n    \"DisassociateRepository\": \"codeguru-reviewer:DisassociateRepository\",\n    \"ListCodeReviews\": \"codeguru-reviewer:ListCodeReviews\",\n    \"ListRecommendationFeedback\": \"codeguru-reviewer:ListRecommendationFeedback\",\n    \"ListRecommendations\": \"codeguru-reviewer:ListRecommendations\",\n    \"ListRepositoryAssociations\": \"codeguru-reviewer:ListRepositoryAssociations\",\n    \"ListTagsForResource\": \"codeguru-reviewer:ListTagsForResource\",\n    \"PutRecommendationFeedback\": \"codeguru-reviewer:PutRecommendationFeedback\",\n    \"TagResource\": \"codeguru-reviewer:TagResource\"\n  },\n  \"codepipeline\": {\n    \"AcknowledgeJob\": \"codepipeline:AcknowledgeJob\",\n    \"AcknowledgeThirdPartyJob\": \"codepipeline:AcknowledgeThirdPartyJob\",\n    \"CreateCustomActionType\": \"codepipeline:CreateCustomActionType\",\n    \"CreatePipeline\": \"codepipeline:CreatePipeline\",\n    \"DeleteCustomActionType\": \"codepipeline:DeleteCustomActionType\",\n    \"DeletePipeline\": \"codepipeline:DeletePipeline\",\n    \"DeleteWebhook\": \"codepipeline:DeleteWebhook\",\n    \"DeregisterWebhookWithThirdParty\": \"codepipeline:DeregisterWebhookWithThirdParty\",\n    \"DisableStageTransition\": \"codepipeline:DisableStageTransition\",\n    \"EnableStageTransition\": \"codepipeline:EnableStageTransition\",\n    \"GetActionType\": \"codepipeline:GetActionType\",\n    \"GetJobDetails\": \"codepipeline:GetJobDetails\",\n    \"GetPipeline\": \"codepipeline:GetPipeline\",\n    \"GetPipelineExecution\": \"codepipeline:GetPipelineExecution\",\n    \"GetPipelineState\": \"codepipeline:GetPipelineState\",\n    \"GetThirdPartyJobDetails\": \"codepipeline:GetThirdPartyJobDetails\",\n    \"ListActionExecutions\": \"codepipeline:ListActionExecutions\",\n    \"ListActionTypes\": \"codepipeline:ListActionTypes\",\n    \"ListPipelineExecutions\": \"codepipeline:ListPipelineExecutions\",\n    \"ListPipelines\": \"codepipeline:ListPipelines\",\n    \"ListTagsForResource\": \"codepipeline:ListTagsForResource\",\n    \"ListWebhooks\": \"codepipeline:ListWebhooks\",\n    \"PollForJobs\": \"codepipeline:PollForJobs\",\n    \"PollForThirdPartyJobs\": \"codepipeline:PollForThirdPartyJobs\",\n    \"PutActionRevision\": \"codepipeline:PutActionRevision\",\n    \"PutApprovalResult\": \"codepipeline:PutApprovalResult\",\n    \"PutJobFailureResult\": \"codepipeline:PutJobFailureResult\",\n    \"PutJobSuccessResult\": \"codepipeline:PutJobSuccessResult\",\n    \"PutThirdPartyJobFailureResult\": \"codepipeline:PutThirdPartyJobFailureResult\",\n    \"PutThirdPartyJobSuccessResult\": \"codepipeline:PutThirdPartyJobSuccessResult\",\n    \"PutWebhook\": \"codepipeline:PutWebhook\",\n    \"RegisterWebhookWithThirdParty\": \"codepipeline:RegisterWebhookWithThirdParty\",\n    \"RetryStageExecution\": \"codepipeline:RetryStageExecution\",\n    \"StartPipelineExecution\": \"codepipeline:StartPipelineExecution\",\n    \"StopPipelineExecution\": \"codepipeline:StopPipelineExecution\",\n    \"TagResource\": \"codepipeline:TagResource\",\n    \"UntagResource\": \"codepipeline:UntagResource\",\n    \"UpdateActionType\": \"codepipeline:UpdateActionType\",\n    \"UpdatePipeline\": \"codepipeline:UpdatePipeline\"\n  },\n  \"codestar\": {\n    \"AssociateTeamMember\": \"codestar:AssociateTeamMember\",\n    \"CreateProject\": \"codestar:CreateProject\",\n    \"CreateUserProfile\": \"codestar:CreateUserProfile\",\n    \"DeleteProject\": \"codestar:DeleteProject\",\n    \"DeleteUserProfile\": \"codestar:DeleteUserProfile\",\n    \"DescribeProject\": \"codestar:DescribeProject\",\n    \"DescribeUserProfile\": \"codestar:DescribeUserProfile\",\n    \"DisassociateTeamMember\": \"codestar:DisassociateTeamMember\",\n    \"ListProjects\": \"codestar:ListProjects\",\n    \"ListResources\": \"codestar:ListResources\",\n    \"ListTagsForProject\": \"codestar:ListTagsForProject\",\n    \"ListTeamMembers\": \"codestar:ListTeamMembers\",\n    \"ListUserProfiles\": \"codestar:ListUserProfiles\",\n    \"TagProject\": \"codestar:TagProject\",\n    \"UntagProject\": \"codestar:UntagProject\",\n    \"UpdateProject\": \"codestar:UpdateProject\",\n    \"UpdateTeamMember\": \"codestar:UpdateTeamMember\",\n    \"UpdateUserProfile\": \"codestar:UpdateUserProfile\"\n  },\n  \"codestar-connections\": {\n    \"CreateConnection\": \"codestar-connections:CreateConnection\",\n    \"CreateHost\": \"codestar-connections:CreateHost\",\n    \"DeleteConnection\": \"codestar-connections:DeleteConnection\",\n    \"DeleteHost\": \"codestar-connections:DeleteHost\",\n    \"GetConnection\": \"codestar-connections:GetConnection\",\n    \"GetHost\": \"codestar-connections:GetHost\",\n    \"ListConnections\": \"codestar-connections:ListConnections\",\n    \"ListHosts\": \"codestar-connections:ListHosts\",\n    \"ListTagsForResource\": \"codestar-connections:ListTagsForResource\",\n    \"TagResource\": \"codestar-connections:TagResource\",\n    \"UntagResource\": \"codestar-connections:UntagResource\",\n    \"UpdateHost\": \"codestar-connections:UpdateHost\"\n  },\n  \"codestar-notifications\": {\n    \"CreateNotificationRule\": \"codestar-notifications:CreateNotificationRule\",\n    \"DeleteNotificationRule\": \"codestar-notifications:DeleteNotificationRule\",\n    \"DeleteTarget\": \"codestar-notifications:DeleteTarget\",\n    \"DescribeNotificationRule\": \"codestar-notifications:DescribeNotificationRule\",\n    \"ListEventTypes\": \"codestar-notifications:ListEventTypes\",\n    \"ListNotificationRules\": \"codestar-notifications:ListNotificationRules\",\n    \"ListTagsForResource\": \"codestar-notifications:ListTagsForResource\",\n    \"ListTargets\": \"codestar-notifications:ListTargets\",\n    \"Subscribe\": \"codestar-notifications:Subscribe\",\n    \"TagResource\": \"codestar-notifications:TagResource\",\n    \"Unsubscribe\": \"codestar-notifications:Unsubscribe\",\n    \"UntagResource\": \"codestar-notifications:UntagResource\",\n    \"UpdateNotificationRule\": \"codestar-notifications:UpdateNotificationRule\"\n  },\n  \"cognito-identity\": {\n    \"CreateIdentityPool\": \"cognito-identity:CreateIdentityPool\",\n    \"DeleteIdentities\": \"cognito-identity:DeleteIdentities\",\n    \"DeleteIdentityPool\": \"cognito-identity:DeleteIdentityPool\",\n    \"DescribeIdentity\": \"cognito-identity:DescribeIdentity\",\n    \"DescribeIdentityPool\": \"cognito-identity:DescribeIdentityPool\",\n    \"GetCredentialsForIdentity\": \"cognito-identity:GetCredentialsForIdentity\",\n    \"GetId\": \"cognito-identity:GetId\",\n    \"GetIdentityPoolRoles\": \"cognito-identity:GetIdentityPoolRoles\",\n    \"GetOpenIdToken\": \"cognito-identity:GetOpenIdToken\",\n    \"GetOpenIdTokenForDeveloperIdentity\": \"cognito-identity:GetOpenIdTokenForDeveloperIdentity\",\n    \"GetPrincipalTagAttributeMap\": \"cognito-identity:GetPrincipalTagAttributeMap\",\n    \"ListIdentities\": \"cognito-identity:ListIdentities\",\n    \"ListIdentityPools\": \"cognito-identity:ListIdentityPools\",\n    \"ListTagsForResource\": \"cognito-identity:ListTagsForResource\",\n    \"LookupDeveloperIdentity\": \"cognito-identity:LookupDeveloperIdentity\",\n    \"MergeDeveloperIdentities\": \"cognito-identity:MergeDeveloperIdentities\",\n    \"SetIdentityPoolRoles\": \"cognito-identity:SetIdentityPoolRoles\",\n    \"SetPrincipalTagAttributeMap\": \"cognito-identity:SetPrincipalTagAttributeMap\",\n    \"TagResource\": \"cognito-identity:TagResource\",\n    \"UnlinkDeveloperIdentity\": \"cognito-identity:UnlinkDeveloperIdentity\",\n    \"UnlinkIdentity\": \"cognito-identity:UnlinkIdentity\",\n    \"UntagResource\": \"cognito-identity:UntagResource\",\n    \"UpdateIdentityPool\": \"cognito-identity:UpdateIdentityPool\"\n  },\n  \"cognito-idp\": {\n    \"AddCustomAttributes\": \"cognito-idp:AddCustomAttributes\",\n    \"AdminAddUserToGroup\": \"cognito-idp:AdminAddUserToGroup\",\n    \"AdminConfirmSignUp\": \"cognito-idp:AdminConfirmSignUp\",\n    \"AdminCreateUser\": \"cognito-idp:AdminCreateUser\",\n    \"AdminDeleteUser\": \"cognito-idp:AdminDeleteUser\",\n    \"AdminDeleteUserAttributes\": \"cognito-idp:AdminDeleteUserAttributes\",\n    \"AdminDisableProviderForUser\": \"cognito-idp:AdminDisableProviderForUser\",\n    \"AdminDisableUser\": \"cognito-idp:AdminDisableUser\",\n    \"AdminEnableUser\": \"cognito-idp:AdminEnableUser\",\n    \"AdminForgetDevice\": \"cognito-idp:AdminForgetDevice\",\n    \"AdminGetDevice\": \"cognito-idp:AdminGetDevice\",\n    \"AdminGetUser\": \"cognito-idp:AdminGetUser\",\n    \"AdminInitiateAuth\": \"cognito-idp:AdminInitiateAuth\",\n    \"AdminLinkProviderForUser\": \"cognito-idp:AdminLinkProviderForUser\",\n    \"AdminListDevices\": \"cognito-idp:AdminListDevices\",\n    \"AdminListGroupsForUser\": \"cognito-idp:AdminListGroupsForUser\",\n    \"AdminListUserAuthEvents\": \"cognito-idp:AdminListUserAuthEvents\",\n    \"AdminRemoveUserFromGroup\": \"cognito-idp:AdminRemoveUserFromGroup\",\n    \"AdminResetUserPassword\": \"cognito-idp:AdminResetUserPassword\",\n    \"AdminRespondToAuthChallenge\": \"cognito-idp:AdminRespondToAuthChallenge\",\n    \"AdminSetUserMFAPreference\": \"cognito-idp:AdminSetUserMFAPreference\",\n    \"AdminSetUserPassword\": \"cognito-idp:AdminSetUserPassword\",\n    \"AdminSetUserSettings\": \"cognito-idp:AdminSetUserSettings\",\n    \"AdminUpdateAuthEventFeedback\": \"cognito-idp:AdminUpdateAuthEventFeedback\",\n    \"AdminUpdateDeviceStatus\": \"cognito-idp:AdminUpdateDeviceStatus\",\n    \"AdminUpdateUserAttributes\": \"cognito-idp:AdminUpdateUserAttributes\",\n    \"AdminUserGlobalSignOut\": \"cognito-idp:AdminUserGlobalSignOut\",\n    \"AssociateSoftwareToken\": \"cognito-idp:AssociateSoftwareToken\",\n    \"ChangePassword\": \"cognito-idp:ChangePassword\",\n    \"ConfirmDevice\": \"cognito-idp:ConfirmDevice\",\n    \"ConfirmForgotPassword\": \"cognito-idp:ConfirmForgotPassword\",\n    \"ConfirmSignUp\": \"cognito-idp:ConfirmSignUp\",\n    \"CreateGroup\": \"cognito-idp:CreateGroup\",\n    \"CreateIdentityProvider\": \"cognito-idp:CreateIdentityProvider\",\n    \"CreateResourceServer\": \"cognito-idp:CreateResourceServer\",\n    \"CreateUserImportJob\": \"cognito-idp:CreateUserImportJob\",\n    \"CreateUserPool\": \"cognito-idp:CreateUserPool\",\n    \"CreateUserPoolClient\": \"cognito-idp:CreateUserPoolClient\",\n    \"CreateUserPoolDomain\": \"cognito-idp:CreateUserPoolDomain\",\n    \"DeleteGroup\": \"cognito-idp:DeleteGroup\",\n    \"DeleteIdentityProvider\": \"cognito-idp:DeleteIdentityProvider\",\n    \"DeleteResourceServer\": \"cognito-idp:DeleteResourceServer\",\n    \"DeleteUser\": \"cognito-idp:DeleteUser\",\n    \"DeleteUserAttributes\": \"cognito-idp:DeleteUserAttributes\",\n    \"DeleteUserPool\": \"cognito-idp:DeleteUserPool\",\n    \"DeleteUserPoolClient\": \"cognito-idp:DeleteUserPoolClient\",\n    \"DeleteUserPoolDomain\": \"cognito-idp:DeleteUserPoolDomain\",\n    \"DescribeIdentityProvider\": \"cognito-idp:DescribeIdentityProvider\",\n    \"DescribeResourceServer\": \"cognito-idp:DescribeResourceServer\",\n    \"DescribeRiskConfiguration\": \"cognito-idp:DescribeRiskConfiguration\",\n    \"DescribeUserImportJob\": \"cognito-idp:DescribeUserImportJob\",\n    \"DescribeUserPool\": \"cognito-idp:DescribeUserPool\",\n    \"DescribeUserPoolClient\": \"cognito-idp:DescribeUserPoolClient\",\n    \"DescribeUserPoolDomain\": \"cognito-idp:DescribeUserPoolDomain\",\n    \"ForgetDevice\": \"cognito-idp:ForgetDevice\",\n    \"ForgotPassword\": \"cognito-idp:ForgotPassword\",\n    \"GetCSVHeader\": \"cognito-idp:GetCSVHeader\",\n    \"GetDevice\": \"cognito-idp:GetDevice\",\n    \"GetGroup\": \"cognito-idp:GetGroup\",\n    \"GetIdentityProviderByIdentifier\": \"cognito-idp:GetIdentityProviderByIdentifier\",\n    \"GetSigningCertificate\": \"cognito-idp:GetSigningCertificate\",\n    \"GetUICustomization\": \"cognito-idp:GetUICustomization\",\n    \"GetUser\": \"cognito-idp:GetUser\",\n    \"GetUserAttributeVerificationCode\": \"cognito-idp:GetUserAttributeVerificationCode\",\n    \"GetUserPoolMfaConfig\": \"cognito-idp:GetUserPoolMfaConfig\",\n    \"GlobalSignOut\": \"cognito-idp:GlobalSignOut\",\n    \"InitiateAuth\": \"cognito-idp:InitiateAuth\",\n    \"ListDevices\": \"cognito-idp:ListDevices\",\n    \"ListGroups\": \"cognito-idp:ListGroups\",\n    \"ListIdentityProviders\": \"cognito-idp:ListIdentityProviders\",\n    \"ListResourceServers\": \"cognito-idp:ListResourceServers\",\n    \"ListTagsForResource\": \"cognito-idp:ListTagsForResource\",\n    \"ListUserImportJobs\": \"cognito-idp:ListUserImportJobs\",\n    \"ListUserPoolClients\": \"cognito-idp:ListUserPoolClients\",\n    \"ListUserPools\": \"cognito-idp:ListUserPools\",\n    \"ListUsers\": \"cognito-idp:ListUsers\",\n    \"ListUsersInGroup\": \"cognito-idp:ListUsersInGroup\",\n    \"ResendConfirmationCode\": \"cognito-idp:ResendConfirmationCode\",\n    \"RespondToAuthChallenge\": \"cognito-idp:RespondToAuthChallenge\",\n    \"SetRiskConfiguration\": \"cognito-idp:SetRiskConfiguration\",\n    \"SetUICustomization\": \"cognito-idp:SetUICustomization\",\n    \"SetUserMFAPreference\": \"cognito-idp:SetUserMFAPreference\",\n    \"SetUserPoolMfaConfig\": \"cognito-idp:SetUserPoolMfaConfig\",\n    \"SetUserSettings\": \"cognito-idp:SetUserSettings\",\n    \"SignUp\": \"cognito-idp:SignUp\",\n    \"StartUserImportJob\": \"cognito-idp:StartUserImportJob\",\n    \"StopUserImportJob\": \"cognito-idp:StopUserImportJob\",\n    \"TagResource\": \"cognito-idp:TagResource\",\n    \"UntagResource\": \"cognito-idp:UntagResource\",\n    \"UpdateAuthEventFeedback\": \"cognito-idp:UpdateAuthEventFeedback\",\n    \"UpdateDeviceStatus\": \"cognito-idp:UpdateDeviceStatus\",\n    \"UpdateGroup\": \"cognito-idp:UpdateGroup\",\n    \"UpdateIdentityProvider\": \"cognito-idp:UpdateIdentityProvider\",\n    \"UpdateResourceServer\": \"cognito-idp:UpdateResourceServer\",\n    \"UpdateUserAttributes\": \"cognito-idp:UpdateUserAttributes\",\n    \"UpdateUserPool\": \"cognito-idp:UpdateUserPool\",\n    \"UpdateUserPoolClient\": \"cognito-idp:UpdateUserPoolClient\",\n    \"UpdateUserPoolDomain\": \"cognito-idp:UpdateUserPoolDomain\",\n    \"VerifySoftwareToken\": \"cognito-idp:VerifySoftwareToken\",\n    \"VerifyUserAttribute\": \"cognito-idp:VerifyUserAttribute\"\n  },\n  \"cognito-sync\": {\n    \"BulkPublish\": \"cognito-sync:BulkPublish\",\n    \"DeleteDataset\": \"cognito-sync:DeleteDataset\",\n    \"DescribeDataset\": \"cognito-sync:DescribeDataset\",\n    \"DescribeIdentityPoolUsage\": \"cognito-sync:DescribeIdentityPoolUsage\",\n    \"DescribeIdentityUsage\": \"cognito-sync:DescribeIdentityUsage\",\n    \"GetBulkPublishDetails\": \"cognito-sync:GetBulkPublishDetails\",\n    \"GetCognitoEvents\": \"cognito-sync:GetCognitoEvents\",\n    \"GetIdentityPoolConfiguration\": \"cognito-sync:GetIdentityPoolConfiguration\",\n    \"ListDatasets\": \"cognito-sync:ListDatasets\",\n    \"ListIdentityPoolUsage\": \"cognito-sync:ListIdentityPoolUsage\",\n    \"ListRecords\": \"cognito-sync:ListRecords\",\n    \"RegisterDevice\": \"cognito-sync:RegisterDevice\",\n    \"SetCognitoEvents\": \"cognito-sync:SetCognitoEvents\",\n    \"SetIdentityPoolConfiguration\": \"cognito-sync:SetIdentityPoolConfiguration\",\n    \"SubscribeToDataset\": \"cognito-sync:SubscribeToDataset\",\n    \"UnsubscribeFromDataset\": \"cognito-sync:UnsubscribeFromDataset\",\n    \"UpdateRecords\": \"cognito-sync:UpdateRecords\"\n  },\n  \"comprehend\": {\n    \"BatchDetectDominantLanguage\": \"comprehend:BatchDetectDominantLanguage\",\n    \"BatchDetectEntities\": \"comprehend:BatchDetectEntities\",\n    \"BatchDetectKeyPhrases\": \"comprehend:BatchDetectKeyPhrases\",\n    \"BatchDetectSentiment\": \"comprehend:BatchDetectSentiment\",\n    \"BatchDetectSyntax\": \"comprehend:BatchDetectSyntax\",\n    \"ClassifyDocument\": \"comprehend:ClassifyDocument\",\n    \"ContainsPiiEntities\": \"comprehend:ContainsPiiEntities\",\n    \"CreateDocumentClassifier\": \"comprehend:CreateDocumentClassifier\",\n    \"CreateEndpoint\": \"comprehend:CreateEndpoint\",\n    \"CreateEntityRecognizer\": \"comprehend:CreateEntityRecognizer\",\n    \"DeleteDocumentClassifier\": \"comprehend:DeleteDocumentClassifier\",\n    \"DeleteEndpoint\": \"comprehend:DeleteEndpoint\",\n    \"DeleteEntityRecognizer\": \"comprehend:DeleteEntityRecognizer\",\n    \"DescribeDocumentClassificationJob\": \"comprehend:DescribeDocumentClassificationJob\",\n    \"DescribeDocumentClassifier\": \"comprehend:DescribeDocumentClassifier\",\n    \"DescribeDominantLanguageDetectionJob\": \"comprehend:DescribeDominantLanguageDetectionJob\",\n    \"DescribeEndpoint\": \"comprehend:DescribeEndpoint\",\n    \"DescribeEntitiesDetectionJob\": \"comprehend:DescribeEntitiesDetectionJob\",\n    \"DescribeEntityRecognizer\": \"comprehend:DescribeEntityRecognizer\",\n    \"DescribeEventsDetectionJob\": \"comprehend:DescribeEventsDetectionJob\",\n    \"DescribeKeyPhrasesDetectionJob\": \"comprehend:DescribeKeyPhrasesDetectionJob\",\n    \"DescribePiiEntitiesDetectionJob\": \"comprehend:DescribePiiEntitiesDetectionJob\",\n    \"DescribeSentimentDetectionJob\": \"comprehend:DescribeSentimentDetectionJob\",\n    \"DescribeTopicsDetectionJob\": \"comprehend:DescribeTopicsDetectionJob\",\n    \"DetectDominantLanguage\": \"comprehend:DetectDominantLanguage\",\n    \"DetectEntities\": \"comprehend:DetectEntities\",\n    \"DetectKeyPhrases\": \"comprehend:DetectKeyPhrases\",\n    \"DetectPiiEntities\": \"comprehend:DetectPiiEntities\",\n    \"DetectSentiment\": \"comprehend:DetectSentiment\",\n    \"DetectSyntax\": \"comprehend:DetectSyntax\",\n    \"ListDocumentClassificationJobs\": \"comprehend:ListDocumentClassificationJobs\",\n    \"ListDocumentClassifierSummaries\": \"comprehend:ListDocumentClassifierSummaries\",\n    \"ListDocumentClassifiers\": \"comprehend:ListDocumentClassifiers\",\n    \"ListDominantLanguageDetectionJobs\": \"comprehend:ListDominantLanguageDetectionJobs\",\n    \"ListEndpoints\": \"comprehend:ListEndpoints\",\n    \"ListEntitiesDetectionJobs\": \"comprehend:ListEntitiesDetectionJobs\",\n    \"ListEntityRecognizerSummaries\": \"comprehend:ListEntityRecognizerSummaries\",\n    \"ListEntityRecognizers\": \"comprehend:ListEntityRecognizers\",\n    \"ListEventsDetectionJobs\": \"comprehend:ListEventsDetectionJobs\",\n    \"ListKeyPhrasesDetectionJobs\": \"comprehend:ListKeyPhrasesDetectionJobs\",\n    \"ListPiiEntitiesDetectionJobs\": \"comprehend:ListPiiEntitiesDetectionJobs\",\n    \"ListSentimentDetectionJobs\": \"comprehend:ListSentimentDetectionJobs\",\n    \"ListTagsForResource\": \"comprehend:ListTagsForResource\",\n    \"ListTopicsDetectionJobs\": \"comprehend:ListTopicsDetectionJobs\",\n    \"StartDocumentClassificationJob\": \"comprehend:StartDocumentClassificationJob\",\n    \"StartDominantLanguageDetectionJob\": \"comprehend:StartDominantLanguageDetectionJob\",\n    \"StartEntitiesDetectionJob\": \"comprehend:StartEntitiesDetectionJob\",\n    \"StartEventsDetectionJob\": \"comprehend:StartEventsDetectionJob\",\n    \"StartKeyPhrasesDetectionJob\": \"comprehend:StartKeyPhrasesDetectionJob\",\n    \"StartPiiEntitiesDetectionJob\": \"comprehend:StartPiiEntitiesDetectionJob\",\n    \"StartSentimentDetectionJob\": \"comprehend:StartSentimentDetectionJob\",\n    \"StartTopicsDetectionJob\": \"comprehend:StartTopicsDetectionJob\",\n    \"StopDominantLanguageDetectionJob\": \"comprehend:StopDominantLanguageDetectionJob\",\n    \"StopEntitiesDetectionJob\": \"comprehend:StopEntitiesDetectionJob\",\n    \"StopEventsDetectionJob\": \"comprehend:StopEventsDetectionJob\",\n    \"StopKeyPhrasesDetectionJob\": \"comprehend:StopKeyPhrasesDetectionJob\",\n    \"StopPiiEntitiesDetectionJob\": \"comprehend:StopPiiEntitiesDetectionJob\",\n    \"StopSentimentDetectionJob\": \"comprehend:StopSentimentDetectionJob\",\n    \"StopTrainingDocumentClassifier\": \"comprehend:StopTrainingDocumentClassifier\",\n    \"StopTrainingEntityRecognizer\": \"comprehend:StopTrainingEntityRecognizer\",\n    \"TagResource\": \"comprehend:TagResource\",\n    \"UntagResource\": \"comprehend:UntagResource\",\n    \"UpdateEndpoint\": \"comprehend:UpdateEndpoint\"\n  },\n  \"comprehendmedical\": {\n    \"DescribeEntitiesDetectionV2Job\": \"comprehendmedical:DescribeEntitiesDetectionV2Job\",\n    \"DescribeICD10CMInferenceJob\": \"comprehendmedical:DescribeICD10CMInferenceJob\",\n    \"DescribePHIDetectionJob\": \"comprehendmedical:DescribePHIDetectionJob\",\n    \"DescribeRxNormInferenceJob\": \"comprehendmedical:DescribeRxNormInferenceJob\",\n    \"DetectEntitiesV2\": \"comprehendmedical:DetectEntitiesV2\",\n    \"DetectPHI\": \"comprehendmedical:DetectPHI\",\n    \"InferICD10CM\": \"comprehendmedical:InferICD10CM\",\n    \"InferRxNorm\": \"comprehendmedical:InferRxNorm\",\n    \"ListEntitiesDetectionV2Jobs\": \"comprehendmedical:ListEntitiesDetectionV2Jobs\",\n    \"ListICD10CMInferenceJobs\": \"comprehendmedical:ListICD10CMInferenceJobs\",\n    \"ListPHIDetectionJobs\": \"comprehendmedical:ListPHIDetectionJobs\",\n    \"ListRxNormInferenceJobs\": \"comprehendmedical:ListRxNormInferenceJobs\",\n    \"StartEntitiesDetectionV2Job\": \"comprehendmedical:StartEntitiesDetectionV2Job\",\n    \"StartICD10CMInferenceJob\": \"comprehendmedical:StartICD10CMInferenceJob\",\n    \"StartPHIDetectionJob\": \"comprehendmedical:StartPHIDetectionJob\",\n    \"StartRxNormInferenceJob\": \"comprehendmedical:StartRxNormInferenceJob\",\n    \"StopEntitiesDetectionV2Job\": \"comprehendmedical:StopEntitiesDetectionV2Job\",\n    \"StopICD10CMInferenceJob\": \"comprehendmedical:StopICD10CMInferenceJob\",\n    \"StopPHIDetectionJob\": \"comprehendmedical:StopPHIDetectionJob\",\n    \"StopRxNormInferenceJob\": \"comprehendmedical:StopRxNormInferenceJob\"\n  },\n  \"compute-optimizer\": {\n    \"DeleteRecommendationPreferences\": \"compute-optimizer:DeleteRecommendationPreferences\",\n    \"DescribeRecommendationExportJobs\": \"compute-optimizer:DescribeRecommendationExportJobs\",\n    \"ExportAutoScalingGroupRecommendations\": \"compute-optimizer:ExportAutoScalingGroupRecommendations\",\n    \"ExportEBSVolumeRecommendations\": \"compute-optimizer:ExportEBSVolumeRecommendations\",\n    \"ExportEC2InstanceRecommendations\": \"compute-optimizer:ExportEC2InstanceRecommendations\",\n    \"ExportLambdaFunctionRecommendations\": \"compute-optimizer:ExportLambdaFunctionRecommendations\",\n    \"GetAutoScalingGroupRecommendations\": \"compute-optimizer:GetAutoScalingGroupRecommendations\",\n    \"GetEBSVolumeRecommendations\": \"compute-optimizer:GetEBSVolumeRecommendations\",\n    \"GetEC2InstanceRecommendations\": \"compute-optimizer:GetEC2InstanceRecommendations\",\n    \"GetEC2RecommendationProjectedMetrics\": \"compute-optimizer:GetEC2RecommendationProjectedMetrics\",\n    \"GetEffectiveRecommendationPreferences\": \"compute-optimizer:GetEffectiveRecommendationPreferences\",\n    \"GetEnrollmentStatus\": \"compute-optimizer:GetEnrollmentStatus\",\n    \"GetEnrollmentStatusesForOrganization\": \"compute-optimizer:GetEnrollmentStatusesForOrganization\",\n    \"GetLambdaFunctionRecommendations\": \"compute-optimizer:GetLambdaFunctionRecommendations\",\n    \"GetRecommendationPreferences\": \"compute-optimizer:GetRecommendationPreferences\",\n    \"GetRecommendationSummaries\": \"compute-optimizer:GetRecommendationSummaries\",\n    \"PutRecommendationPreferences\": \"compute-optimizer:PutRecommendationPreferences\",\n    \"UpdateEnrollmentStatus\": \"compute-optimizer:UpdateEnrollmentStatus\"\n  },\n  \"config\": {\n    \"BatchGetAggregateResourceConfig\": \"config:BatchGetAggregateResourceConfig\",\n    \"BatchGetResourceConfig\": \"config:BatchGetResourceConfig\",\n    \"DeleteAggregationAuthorization\": \"config:DeleteAggregationAuthorization\",\n    \"DeleteConfigRule\": \"config:DeleteConfigRule\",\n    \"DeleteConfigurationAggregator\": \"config:DeleteConfigurationAggregator\",\n    \"DeleteConfigurationRecorder\": \"config:DeleteConfigurationRecorder\",\n    \"DeleteConformancePack\": \"config:DeleteConformancePack\",\n    \"DeleteDeliveryChannel\": \"config:DeleteDeliveryChannel\",\n    \"DeleteEvaluationResults\": \"config:DeleteEvaluationResults\",\n    \"DeleteOrganizationConfigRule\": \"config:DeleteOrganizationConfigRule\",\n    \"DeleteOrganizationConformancePack\": \"config:DeleteOrganizationConformancePack\",\n    \"DeletePendingAggregationRequest\": \"config:DeletePendingAggregationRequest\",\n    \"DeleteRemediationConfiguration\": \"config:DeleteRemediationConfiguration\",\n    \"DeleteRemediationExceptions\": \"config:DeleteRemediationExceptions\",\n    \"DeleteResourceConfig\": \"config:DeleteResourceConfig\",\n    \"DeleteRetentionConfiguration\": \"config:DeleteRetentionConfiguration\",\n    \"DeleteStoredQuery\": \"config:DeleteStoredQuery\",\n    \"DeliverConfigSnapshot\": \"config:DeliverConfigSnapshot\",\n    \"DescribeAggregateComplianceByConfigRules\": \"config:DescribeAggregateComplianceByConfigRules\",\n    \"DescribeAggregateComplianceByConformancePacks\": \"config:DescribeAggregateComplianceByConformancePacks\",\n    \"DescribeAggregationAuthorizations\": \"config:DescribeAggregationAuthorizations\",\n    \"DescribeComplianceByConfigRule\": \"config:DescribeComplianceByConfigRule\",\n    \"DescribeComplianceByResource\": \"config:DescribeComplianceByResource\",\n    \"DescribeConfigRuleEvaluationStatus\": \"config:DescribeConfigRuleEvaluationStatus\",\n    \"DescribeConfigRules\": \"config:DescribeConfigRules\",\n    \"DescribeConfigurationAggregatorSourcesStatus\": \"config:DescribeConfigurationAggregatorSourcesStatus\",\n    \"DescribeConfigurationAggregators\": \"config:DescribeConfigurationAggregators\",\n    \"DescribeConfigurationRecorderStatus\": \"config:DescribeConfigurationRecorderStatus\",\n    \"DescribeConfigurationRecorders\": \"config:DescribeConfigurationRecorders\",\n    \"DescribeConformancePackCompliance\": \"config:DescribeConformancePackCompliance\",\n    \"DescribeConformancePackStatus\": \"config:DescribeConformancePackStatus\",\n    \"DescribeConformancePacks\": \"config:DescribeConformancePacks\",\n    \"DescribeDeliveryChannelStatus\": \"config:DescribeDeliveryChannelStatus\",\n    \"DescribeDeliveryChannels\": \"config:DescribeDeliveryChannels\",\n    \"DescribeOrganizationConfigRuleStatuses\": \"config:DescribeOrganizationConfigRuleStatuses\",\n    \"DescribeOrganizationConfigRules\": \"config:DescribeOrganizationConfigRules\",\n    \"DescribeOrganizationConformancePackStatuses\": \"config:DescribeOrganizationConformancePackStatuses\",\n    \"DescribeOrganizationConformancePacks\": \"config:DescribeOrganizationConformancePacks\",\n    \"DescribePendingAggregationRequests\": \"config:DescribePendingAggregationRequests\",\n    \"DescribeRemediationConfigurations\": \"config:DescribeRemediationConfigurations\",\n    \"DescribeRemediationExceptions\": \"config:DescribeRemediationExceptions\",\n    \"DescribeRemediationExecutionStatus\": \"config:DescribeRemediationExecutionStatus\",\n    \"DescribeRetentionConfigurations\": \"config:DescribeRetentionConfigurations\",\n    \"GetAggregateComplianceDetailsByConfigRule\": \"config:GetAggregateComplianceDetailsByConfigRule\",\n    \"GetAggregateConfigRuleComplianceSummary\": \"config:GetAggregateConfigRuleComplianceSummary\",\n    \"GetAggregateConformancePackComplianceSummary\": \"config:GetAggregateConformancePackComplianceSummary\",\n    \"GetAggregateDiscoveredResourceCounts\": \"config:GetAggregateDiscoveredResourceCounts\",\n    \"GetAggregateResourceConfig\": \"config:GetAggregateResourceConfig\",\n    \"GetComplianceDetailsByConfigRule\": \"config:GetComplianceDetailsByConfigRule\",\n    \"GetComplianceDetailsByResource\": \"config:GetComplianceDetailsByResource\",\n    \"GetComplianceSummaryByConfigRule\": \"config:GetComplianceSummaryByConfigRule\",\n    \"GetComplianceSummaryByResourceType\": \"config:GetComplianceSummaryByResourceType\",\n    \"GetConformancePackComplianceDetails\": \"config:GetConformancePackComplianceDetails\",\n    \"GetConformancePackComplianceSummary\": \"config:GetConformancePackComplianceSummary\",\n    \"GetDiscoveredResourceCounts\": \"config:GetDiscoveredResourceCounts\",\n    \"GetOrganizationConfigRuleDetailedStatus\": \"config:GetOrganizationConfigRuleDetailedStatus\",\n    \"GetOrganizationConformancePackDetailedStatus\": \"config:GetOrganizationConformancePackDetailedStatus\",\n    \"GetResourceConfigHistory\": \"config:GetResourceConfigHistory\",\n    \"GetStoredQuery\": \"config:GetStoredQuery\",\n    \"ListAggregateDiscoveredResources\": \"config:ListAggregateDiscoveredResources\",\n    \"ListDiscoveredResources\": \"config:ListDiscoveredResources\",\n    \"ListStoredQueries\": \"config:ListStoredQueries\",\n    \"ListTagsForResource\": \"config:ListTagsForResource\",\n    \"PutAggregationAuthorization\": \"config:PutAggregationAuthorization\",\n    \"PutConfigRule\": \"config:PutConfigRule\",\n    \"PutConfigurationAggregator\": \"config:PutConfigurationAggregator\",\n    \"PutConfigurationRecorder\": \"config:PutConfigurationRecorder\",\n    \"PutConformancePack\": \"config:PutConformancePack\",\n    \"PutDeliveryChannel\": \"config:PutDeliveryChannel\",\n    \"PutEvaluations\": \"config:PutEvaluations\",\n    \"PutExternalEvaluation\": \"config:PutExternalEvaluation\",\n    \"PutOrganizationConfigRule\": \"config:PutOrganizationConfigRule\",\n    \"PutOrganizationConformancePack\": \"config:PutOrganizationConformancePack\",\n    \"PutRemediationConfigurations\": \"config:PutRemediationConfigurations\",\n    \"PutRemediationExceptions\": \"config:PutRemediationExceptions\",\n    \"PutResourceConfig\": \"config:PutResourceConfig\",\n    \"PutRetentionConfiguration\": \"config:PutRetentionConfiguration\",\n    \"PutStoredQuery\": \"config:PutStoredQuery\",\n    \"SelectAggregateResourceConfig\": \"config:SelectAggregateResourceConfig\",\n    \"SelectResourceConfig\": \"config:SelectResourceConfig\",\n    \"StartConfigRulesEvaluation\": \"config:StartConfigRulesEvaluation\",\n    \"StartConfigurationRecorder\": \"config:StartConfigurationRecorder\",\n    \"StartRemediationExecution\": \"config:StartRemediationExecution\",\n    \"StopConfigurationRecorder\": \"config:StopConfigurationRecorder\",\n    \"TagResource\": \"config:TagResource\",\n    \"UntagResource\": \"config:UntagResource\"\n  },\n  \"connect\": {\n    \"AssociateApprovedOrigin\": \"connect:AssociateApprovedOrigin\",\n    \"AssociateBot\": \"connect:AssociateBot\",\n    \"AssociateInstanceStorageConfig\": \"connect:AssociateInstanceStorageConfig\",\n    \"AssociateLambdaFunction\": \"connect:AssociateLambdaFunction\",\n    \"AssociateLexBot\": \"connect:AssociateLexBot\",\n    \"AssociateQueueQuickConnects\": \"connect:AssociateQueueQuickConnects\",\n    \"AssociateRoutingProfileQueues\": \"connect:AssociateRoutingProfileQueues\",\n    \"AssociateSecurityKey\": \"connect:AssociateSecurityKey\",\n    \"CreateAgentStatus\": \"connect:CreateAgentStatus\",\n    \"CreateContactFlow\": \"connect:CreateContactFlow\",\n    \"CreateContactFlowModule\": \"connect:CreateContactFlowModule\",\n    \"CreateHoursOfOperation\": \"connect:CreateHoursOfOperation\",\n    \"CreateInstance\": \"connect:CreateInstance\",\n    \"CreateIntegrationAssociation\": \"connect:CreateIntegrationAssociation\",\n    \"CreateQueue\": \"connect:CreateQueue\",\n    \"CreateQuickConnect\": \"connect:CreateQuickConnect\",\n    \"CreateRoutingProfile\": \"connect:CreateRoutingProfile\",\n    \"CreateSecurityProfile\": \"connect:CreateSecurityProfile\",\n    \"CreateUseCase\": \"connect:CreateUseCase\",\n    \"CreateUser\": \"connect:CreateUser\",\n    \"CreateUserHierarchyGroup\": \"connect:CreateUserHierarchyGroup\",\n    \"DeleteContactFlow\": \"connect:DeleteContactFlow\",\n    \"DeleteContactFlowModule\": \"connect:DeleteContactFlowModule\",\n    \"DeleteHoursOfOperation\": \"connect:DeleteHoursOfOperation\",\n    \"DeleteInstance\": \"connect:DeleteInstance\",\n    \"DeleteIntegrationAssociation\": \"connect:DeleteIntegrationAssociation\",\n    \"DeleteQuickConnect\": \"connect:DeleteQuickConnect\",\n    \"DeleteSecurityProfile\": \"connect:DeleteSecurityProfile\",\n    \"DeleteUseCase\": \"connect:DeleteUseCase\",\n    \"DeleteUser\": \"connect:DeleteUser\",\n    \"DeleteUserHierarchyGroup\": \"connect:DeleteUserHierarchyGroup\",\n    \"DescribeAgentStatus\": \"connect:DescribeAgentStatus\",\n    \"DescribeContact\": \"connect:DescribeContact\",\n    \"DescribeContactFlow\": \"connect:DescribeContactFlow\",\n    \"DescribeContactFlowModule\": \"connect:DescribeContactFlowModule\",\n    \"DescribeHoursOfOperation\": \"connect:DescribeHoursOfOperation\",\n    \"DescribeInstance\": \"connect:DescribeInstance\",\n    \"DescribeInstanceAttribute\": \"connect:DescribeInstanceAttribute\",\n    \"DescribeInstanceStorageConfig\": \"connect:DescribeInstanceStorageConfig\",\n    \"DescribeQueue\": \"connect:DescribeQueue\",\n    \"DescribeQuickConnect\": \"connect:DescribeQuickConnect\",\n    \"DescribeRoutingProfile\": \"connect:DescribeRoutingProfile\",\n    \"DescribeSecurityProfile\": \"connect:DescribeSecurityProfile\",\n    \"DescribeUser\": \"connect:DescribeUser\",\n    \"DescribeUserHierarchyGroup\": \"connect:DescribeUserHierarchyGroup\",\n    \"DescribeUserHierarchyStructure\": \"connect:DescribeUserHierarchyStructure\",\n    \"DisassociateApprovedOrigin\": \"connect:DisassociateApprovedOrigin\",\n    \"DisassociateBot\": \"connect:DisassociateBot\",\n    \"DisassociateInstanceStorageConfig\": \"connect:DisassociateInstanceStorageConfig\",\n    \"DisassociateLambdaFunction\": \"connect:DisassociateLambdaFunction\",\n    \"DisassociateLexBot\": \"connect:DisassociateLexBot\",\n    \"DisassociateQueueQuickConnects\": \"connect:DisassociateQueueQuickConnects\",\n    \"DisassociateRoutingProfileQueues\": \"connect:DisassociateRoutingProfileQueues\",\n    \"DisassociateSecurityKey\": \"connect:DisassociateSecurityKey\",\n    \"GetContactAttributes\": \"connect:GetContactAttributes\",\n    \"GetCurrentMetricData\": \"connect:GetCurrentMetricData\",\n    \"GetFederationToken\": \"connect:GetFederationToken\",\n    \"GetMetricData\": \"connect:GetMetricData\",\n    \"ListAgentStatuses\": \"connect:ListAgentStatuses\",\n    \"ListApprovedOrigins\": \"connect:ListApprovedOrigins\",\n    \"ListBots\": \"connect:ListBots\",\n    \"ListContactFlowModules\": \"connect:ListContactFlowModules\",\n    \"ListContactFlows\": \"connect:ListContactFlows\",\n    \"ListContactReferences\": \"connect:ListContactReferences\",\n    \"ListHoursOfOperations\": \"connect:ListHoursOfOperations\",\n    \"ListInstanceAttributes\": \"connect:ListInstanceAttributes\",\n    \"ListInstanceStorageConfigs\": \"connect:ListInstanceStorageConfigs\",\n    \"ListInstances\": \"connect:ListInstances\",\n    \"ListIntegrationAssociations\": \"connect:ListIntegrationAssociations\",\n    \"ListLambdaFunctions\": \"connect:ListLambdaFunctions\",\n    \"ListLexBots\": \"connect:ListLexBots\",\n    \"ListPhoneNumbers\": \"connect:ListPhoneNumbers\",\n    \"ListPrompts\": \"connect:ListPrompts\",\n    \"ListQueueQuickConnects\": \"connect:ListQueueQuickConnects\",\n    \"ListQueues\": \"connect:ListQueues\",\n    \"ListQuickConnects\": \"connect:ListQuickConnects\",\n    \"ListRoutingProfileQueues\": \"connect:ListRoutingProfileQueues\",\n    \"ListRoutingProfiles\": \"connect:ListRoutingProfiles\",\n    \"ListSecurityKeys\": \"connect:ListSecurityKeys\",\n    \"ListSecurityProfilePermissions\": \"connect:ListSecurityProfilePermissions\",\n    \"ListSecurityProfiles\": \"connect:ListSecurityProfiles\",\n    \"ListTagsForResource\": \"connect:ListTagsForResource\",\n    \"ListUseCases\": \"connect:ListUseCases\",\n    \"ListUserHierarchyGroups\": \"connect:ListUserHierarchyGroups\",\n    \"ListUsers\": \"connect:ListUsers\",\n    \"ResumeContactRecording\": \"connect:ResumeContactRecording\",\n    \"StartChatContact\": \"connect:StartChatContact\",\n    \"StartContactRecording\": \"connect:StartContactRecording\",\n    \"StartOutboundVoiceContact\": \"connect:StartOutboundVoiceContact\",\n    \"StartTaskContact\": \"connect:StartTaskContact\",\n    \"StopContact\": \"connect:StopContact\",\n    \"StopContactRecording\": \"connect:StopContactRecording\",\n    \"SuspendContactRecording\": \"connect:SuspendContactRecording\",\n    \"TagResource\": \"connect:TagResource\",\n    \"UntagResource\": \"connect:UntagResource\",\n    \"UpdateAgentStatus\": \"connect:UpdateAgentStatus\",\n    \"UpdateContact\": \"connect:UpdateContact\",\n    \"UpdateContactAttributes\": \"connect:UpdateContactAttributes\",\n    \"UpdateContactFlowContent\": \"connect:UpdateContactFlowContent\",\n    \"UpdateContactFlowMetadata\": \"connect:UpdateContactFlowMetadata\",\n    \"UpdateContactFlowModuleMetadata\": \"connect:UpdateContactFlowModuleMetadata\",\n    \"UpdateContactFlowName\": \"connect:UpdateContactFlowName\",\n    \"UpdateContactSchedule\": \"connect:UpdateContactSchedule\",\n    \"UpdateHoursOfOperation\": \"connect:UpdateHoursOfOperation\",\n    \"UpdateInstanceAttribute\": \"connect:UpdateInstanceAttribute\",\n    \"UpdateInstanceStorageConfig\": \"connect:UpdateInstanceStorageConfig\",\n    \"UpdateQueueHoursOfOperation\": \"connect:UpdateQueueHoursOfOperation\",\n    \"UpdateQueueMaxContacts\": \"connect:UpdateQueueMaxContacts\",\n    \"UpdateQueueName\": \"connect:UpdateQueueName\",\n    \"UpdateQueueOutboundCallerConfig\": \"connect:UpdateQueueOutboundCallerConfig\",\n    \"UpdateQueueStatus\": \"connect:UpdateQueueStatus\",\n    \"UpdateQuickConnectConfig\": \"connect:UpdateQuickConnectConfig\",\n    \"UpdateQuickConnectName\": \"connect:UpdateQuickConnectName\",\n    \"UpdateRoutingProfileConcurrency\": \"connect:UpdateRoutingProfileConcurrency\",\n    \"UpdateRoutingProfileDefaultOutboundQueue\": \"connect:UpdateRoutingProfileDefaultOutboundQueue\",\n    \"UpdateRoutingProfileName\": \"connect:UpdateRoutingProfileName\",\n    \"UpdateRoutingProfileQueues\": \"connect:UpdateRoutingProfileQueues\",\n    \"UpdateSecurityProfile\": \"connect:UpdateSecurityProfile\",\n    \"UpdateUserHierarchy\": \"connect:UpdateUserHierarchy\",\n    \"UpdateUserHierarchyGroupName\": \"connect:UpdateUserHierarchyGroupName\",\n    \"UpdateUserHierarchyStructure\": \"connect:UpdateUserHierarchyStructure\",\n    \"UpdateUserIdentityInfo\": \"connect:UpdateUserIdentityInfo\",\n    \"UpdateUserPhoneConfig\": \"connect:UpdateUserPhoneConfig\",\n    \"UpdateUserRoutingProfile\": \"connect:UpdateUserRoutingProfile\",\n    \"UpdateUserSecurityProfiles\": \"connect:UpdateUserSecurityProfiles\"\n  },\n  \"cur\": {\n    \"DeleteReportDefinition\": \"cur:DeleteReportDefinition\",\n    \"DescribeReportDefinitions\": \"cur:DescribeReportDefinitions\",\n    \"ModifyReportDefinition\": \"cur:ModifyReportDefinition\",\n    \"PutReportDefinition\": \"cur:PutReportDefinition\"\n  },\n  \"databrew\": {\n    \"BatchDeleteRecipeVersion\": \"databrew:BatchDeleteRecipeVersion\",\n    \"CreateDataset\": \"databrew:CreateDataset\",\n    \"CreateProfileJob\": \"databrew:CreateProfileJob\",\n    \"CreateProject\": \"databrew:CreateProject\",\n    \"CreateRecipe\": \"databrew:CreateRecipe\",\n    \"CreateRecipeJob\": \"databrew:CreateRecipeJob\",\n    \"CreateRuleset\": \"databrew:CreateRuleset\",\n    \"CreateSchedule\": \"databrew:CreateSchedule\",\n    \"DeleteDataset\": \"databrew:DeleteDataset\",\n    \"DeleteJob\": \"databrew:DeleteJob\",\n    \"DeleteProject\": \"databrew:DeleteProject\",\n    \"DeleteRecipeVersion\": \"databrew:DeleteRecipeVersion\",\n    \"DeleteRuleset\": \"databrew:DeleteRuleset\",\n    \"DeleteSchedule\": \"databrew:DeleteSchedule\",\n    \"DescribeDataset\": \"databrew:DescribeDataset\",\n    \"DescribeJob\": \"databrew:DescribeJob\",\n    \"DescribeJobRun\": \"databrew:DescribeJobRun\",\n    \"DescribeProject\": \"databrew:DescribeProject\",\n    \"DescribeRecipe\": \"databrew:DescribeRecipe\",\n    \"DescribeRuleset\": \"databrew:DescribeRuleset\",\n    \"DescribeSchedule\": \"databrew:DescribeSchedule\",\n    \"ListDatasets\": \"databrew:ListDatasets\",\n    \"ListJobRuns\": \"databrew:ListJobRuns\",\n    \"ListJobs\": \"databrew:ListJobs\",\n    \"ListProjects\": \"databrew:ListProjects\",\n    \"ListRecipeVersions\": \"databrew:ListRecipeVersions\",\n    \"ListRecipes\": \"databrew:ListRecipes\",\n    \"ListRulesets\": \"databrew:ListRulesets\",\n    \"ListSchedules\": \"databrew:ListSchedules\",\n    \"ListTagsForResource\": \"databrew:ListTagsForResource\",\n    \"PublishRecipe\": \"databrew:PublishRecipe\",\n    \"SendProjectSessionAction\": \"databrew:SendProjectSessionAction\",\n    \"StartJobRun\": \"databrew:StartJobRun\",\n    \"StartProjectSession\": \"databrew:StartProjectSession\",\n    \"StopJobRun\": \"databrew:StopJobRun\",\n    \"TagResource\": \"databrew:TagResource\",\n    \"UntagResource\": \"databrew:UntagResource\",\n    \"UpdateDataset\": \"databrew:UpdateDataset\",\n    \"UpdateProfileJob\": \"databrew:UpdateProfileJob\",\n    \"UpdateProject\": \"databrew:UpdateProject\",\n    \"UpdateRecipe\": \"databrew:UpdateRecipe\",\n    \"UpdateRecipeJob\": \"databrew:UpdateRecipeJob\",\n    \"UpdateRuleset\": \"databrew:UpdateRuleset\",\n    \"UpdateSchedule\": \"databrew:UpdateSchedule\"\n  },\n  \"dataexchange\": {\n    \"CancelJob\": \"dataexchange:CancelJob\",\n    \"CreateDataSet\": \"dataexchange:CreateDataSet\",\n    \"CreateEventAction\": \"dataexchange:CreateEventAction\",\n    \"CreateJob\": \"dataexchange:CreateJob\",\n    \"CreateRevision\": \"dataexchange:CreateRevision\",\n    \"DeleteAsset\": \"dataexchange:DeleteAsset\",\n    \"DeleteDataSet\": \"dataexchange:DeleteDataSet\",\n    \"DeleteEventAction\": \"dataexchange:DeleteEventAction\",\n    \"DeleteRevision\": \"dataexchange:DeleteRevision\",\n    \"GetAsset\": \"dataexchange:GetAsset\",\n    \"GetDataSet\": \"dataexchange:GetDataSet\",\n    \"GetEventAction\": \"dataexchange:GetEventAction\",\n    \"GetJob\": \"dataexchange:GetJob\",\n    \"GetRevision\": \"dataexchange:GetRevision\",\n    \"ListDataSetRevisions\": \"dataexchange:ListDataSetRevisions\",\n    \"ListDataSets\": \"dataexchange:ListDataSets\",\n    \"ListEventActions\": \"dataexchange:ListEventActions\",\n    \"ListJobs\": \"dataexchange:ListJobs\",\n    \"ListRevisionAssets\": \"dataexchange:ListRevisionAssets\",\n    \"ListTagsForResource\": \"dataexchange:ListTagsForResource\",\n    \"SendApiAsset\": \"dataexchange:SendApiAsset\",\n    \"StartJob\": \"dataexchange:StartJob\",\n    \"TagResource\": \"dataexchange:TagResource\",\n    \"UntagResource\": \"dataexchange:UntagResource\",\n    \"UpdateAsset\": \"dataexchange:UpdateAsset\",\n    \"UpdateDataSet\": \"dataexchange:UpdateDataSet\",\n    \"UpdateEventAction\": \"dataexchange:UpdateEventAction\",\n    \"UpdateRevision\": \"dataexchange:UpdateRevision\"\n  },\n  \"datapipeline\": {\n    \"ActivatePipeline\": \"datapipeline:ActivatePipeline\",\n    \"AddTags\": \"datapipeline:AddTags\",\n    \"CreatePipeline\": \"datapipeline:CreatePipeline\",\n    \"DeactivatePipeline\": \"datapipeline:DeactivatePipeline\",\n    \"DeletePipeline\": \"datapipeline:DeletePipeline\",\n    \"DescribeObjects\": \"datapipeline:DescribeObjects\",\n    \"DescribePipelines\": \"datapipeline:DescribePipelines\",\n    \"EvaluateExpression\": \"datapipeline:EvaluateExpression\",\n    \"GetPipelineDefinition\": \"datapipeline:GetPipelineDefinition\",\n    \"ListPipelines\": \"datapipeline:ListPipelines\",\n    \"PollForTask\": \"datapipeline:PollForTask\",\n    \"PutPipelineDefinition\": \"datapipeline:PutPipelineDefinition\",\n    \"QueryObjects\": \"datapipeline:QueryObjects\",\n    \"RemoveTags\": \"datapipeline:RemoveTags\",\n    \"ReportTaskProgress\": \"datapipeline:ReportTaskProgress\",\n    \"ReportTaskRunnerHeartbeat\": \"datapipeline:ReportTaskRunnerHeartbeat\",\n    \"SetStatus\": \"datapipeline:SetStatus\",\n    \"SetTaskStatus\": \"datapipeline:SetTaskStatus\",\n    \"ValidatePipelineDefinition\": \"datapipeline:ValidatePipelineDefinition\"\n  },\n  \"datasync\": {\n    \"CancelTaskExecution\": \"datasync:CancelTaskExecution\",\n    \"CreateAgent\": \"datasync:CreateAgent\",\n    \"CreateLocationEfs\": \"datasync:CreateLocationEfs\",\n    \"CreateLocationFsxWindows\": \"datasync:CreateLocationFsxWindows\",\n    \"CreateLocationNfs\": \"datasync:CreateLocationNfs\",\n    \"CreateLocationObjectStorage\": \"datasync:CreateLocationObjectStorage\",\n    \"CreateLocationS3\": \"datasync:CreateLocationS3\",\n    \"CreateLocationSmb\": \"datasync:CreateLocationSmb\",\n    \"CreateTask\": \"datasync:CreateTask\",\n    \"DeleteAgent\": \"datasync:DeleteAgent\",\n    \"DeleteLocation\": \"datasync:DeleteLocation\",\n    \"DeleteTask\": \"datasync:DeleteTask\",\n    \"DescribeAgent\": \"datasync:DescribeAgent\",\n    \"DescribeLocationEfs\": \"datasync:DescribeLocationEfs\",\n    \"DescribeLocationFsxWindows\": \"datasync:DescribeLocationFsxWindows\",\n    \"DescribeLocationNfs\": \"datasync:DescribeLocationNfs\",\n    \"DescribeLocationObjectStorage\": \"datasync:DescribeLocationObjectStorage\",\n    \"DescribeLocationS3\": \"datasync:DescribeLocationS3\",\n    \"DescribeLocationSmb\": \"datasync:DescribeLocationSmb\",\n    \"DescribeTask\": \"datasync:DescribeTask\",\n    \"DescribeTaskExecution\": \"datasync:DescribeTaskExecution\",\n    \"ListAgents\": \"datasync:ListAgents\",\n    \"ListLocations\": \"datasync:ListLocations\",\n    \"ListTagsForResource\": \"datasync:ListTagsForResource\",\n    \"ListTaskExecutions\": \"datasync:ListTaskExecutions\",\n    \"ListTasks\": \"datasync:ListTasks\",\n    \"StartTaskExecution\": \"datasync:StartTaskExecution\",\n    \"TagResource\": \"datasync:TagResource\",\n    \"UntagResource\": \"datasync:UntagResource\",\n    \"UpdateAgent\": \"datasync:UpdateAgent\",\n    \"UpdateLocationNfs\": \"datasync:UpdateLocationNfs\",\n    \"UpdateLocationObjectStorage\": \"datasync:UpdateLocationObjectStorage\",\n    \"UpdateLocationSmb\": \"datasync:UpdateLocationSmb\",\n    \"UpdateTask\": \"datasync:UpdateTask\",\n    \"UpdateTaskExecution\": \"datasync:UpdateTaskExecution\"\n  },\n  \"dax\": {\n    \"CreateCluster\": \"dax:CreateCluster\",\n    \"CreateParameterGroup\": \"dax:CreateParameterGroup\",\n    \"CreateSubnetGroup\": \"dax:CreateSubnetGroup\",\n    \"DecreaseReplicationFactor\": \"dax:DecreaseReplicationFactor\",\n    \"DeleteCluster\": \"dax:DeleteCluster\",\n    \"DeleteParameterGroup\": \"dax:DeleteParameterGroup\",\n    \"DeleteSubnetGroup\": \"dax:DeleteSubnetGroup\",\n    \"DescribeClusters\": \"dax:DescribeClusters\",\n    \"DescribeDefaultParameters\": \"dax:DescribeDefaultParameters\",\n    \"DescribeEvents\": \"dax:DescribeEvents\",\n    \"DescribeParameterGroups\": \"dax:DescribeParameterGroups\",\n    \"DescribeParameters\": \"dax:DescribeParameters\",\n    \"DescribeSubnetGroups\": \"dax:DescribeSubnetGroups\",\n    \"IncreaseReplicationFactor\": \"dax:IncreaseReplicationFactor\",\n    \"ListTags\": \"dax:ListTags\",\n    \"RebootNode\": \"dax:RebootNode\",\n    \"TagResource\": \"dax:TagResource\",\n    \"UntagResource\": \"dax:UntagResource\",\n    \"UpdateCluster\": \"dax:UpdateCluster\",\n    \"UpdateParameterGroup\": \"dax:UpdateParameterGroup\",\n    \"UpdateSubnetGroup\": \"dax:UpdateSubnetGroup\"\n  },\n  \"detective\": {\n    \"AcceptInvitation\": \"detective:AcceptInvitation\",\n    \"CreateGraph\": \"detective:CreateGraph\",\n    \"CreateMembers\": \"detective:CreateMembers\",\n    \"DeleteGraph\": \"detective:DeleteGraph\",\n    \"DeleteMembers\": \"detective:DeleteMembers\",\n    \"DescribeOrganizationConfiguration\": \"detective:DescribeOrganizationConfiguration\",\n    \"DisableOrganizationAdminAccount\": \"detective:DisableOrganizationAdminAccount\",\n    \"DisassociateMembership\": \"detective:DisassociateMembership\",\n    \"EnableOrganizationAdminAccount\": \"detective:EnableOrganizationAdminAccount\",\n    \"GetMembers\": \"detective:GetMembers\",\n    \"ListGraphs\": \"detective:ListGraphs\",\n    \"ListInvitations\": \"detective:ListInvitations\",\n    \"ListMembers\": \"detective:ListMembers\",\n    \"ListOrganizationAdminAccounts\": \"detective:ListOrganizationAdminAccounts\",\n    \"ListTagsForResource\": \"detective:ListTagsForResource\",\n    \"RejectInvitation\": \"detective:RejectInvitation\",\n    \"StartMonitoringMember\": \"detective:StartMonitoringMember\",\n    \"TagResource\": \"detective:TagResource\",\n    \"UntagResource\": \"detective:UntagResource\",\n    \"UpdateOrganizationConfiguration\": \"detective:UpdateOrganizationConfiguration\"\n  },\n  \"devicefarm\": {\n    \"CreateDevicePool\": \"devicefarm:CreateDevicePool\",\n    \"CreateInstanceProfile\": \"devicefarm:CreateInstanceProfile\",\n    \"CreateNetworkProfile\": \"devicefarm:CreateNetworkProfile\",\n    \"CreateProject\": \"devicefarm:CreateProject\",\n    \"CreateRemoteAccessSession\": \"devicefarm:CreateRemoteAccessSession\",\n    \"CreateTestGridProject\": \"devicefarm:CreateTestGridProject\",\n    \"CreateTestGridUrl\": \"devicefarm:CreateTestGridUrl\",\n    \"CreateUpload\": \"devicefarm:CreateUpload\",\n    \"CreateVPCEConfiguration\": \"devicefarm:CreateVPCEConfiguration\",\n    \"DeleteDevicePool\": \"devicefarm:DeleteDevicePool\",\n    \"DeleteInstanceProfile\": \"devicefarm:DeleteInstanceProfile\",\n    \"DeleteNetworkProfile\": \"devicefarm:DeleteNetworkProfile\",\n    \"DeleteProject\": \"devicefarm:DeleteProject\",\n    \"DeleteRemoteAccessSession\": \"devicefarm:DeleteRemoteAccessSession\",\n    \"DeleteRun\": \"devicefarm:DeleteRun\",\n    \"DeleteTestGridProject\": \"devicefarm:DeleteTestGridProject\",\n    \"DeleteUpload\": \"devicefarm:DeleteUpload\",\n    \"DeleteVPCEConfiguration\": \"devicefarm:DeleteVPCEConfiguration\",\n    \"GetAccountSettings\": \"devicefarm:GetAccountSettings\",\n    \"GetDevice\": \"devicefarm:GetDevice\",\n    \"GetDeviceInstance\": \"devicefarm:GetDeviceInstance\",\n    \"GetDevicePool\": \"devicefarm:GetDevicePool\",\n    \"GetDevicePoolCompatibility\": \"devicefarm:GetDevicePoolCompatibility\",\n    \"GetInstanceProfile\": \"devicefarm:GetInstanceProfile\",\n    \"GetJob\": \"devicefarm:GetJob\",\n    \"GetNetworkProfile\": \"devicefarm:GetNetworkProfile\",\n    \"GetOfferingStatus\": \"devicefarm:GetOfferingStatus\",\n    \"GetProject\": \"devicefarm:GetProject\",\n    \"GetRemoteAccessSession\": \"devicefarm:GetRemoteAccessSession\",\n    \"GetRun\": \"devicefarm:GetRun\",\n    \"GetSuite\": \"devicefarm:GetSuite\",\n    \"GetTest\": \"devicefarm:GetTest\",\n    \"GetTestGridProject\": \"devicefarm:GetTestGridProject\",\n    \"GetTestGridSession\": \"devicefarm:GetTestGridSession\",\n    \"GetUpload\": \"devicefarm:GetUpload\",\n    \"GetVPCEConfiguration\": \"devicefarm:GetVPCEConfiguration\",\n    \"InstallToRemoteAccessSession\": \"devicefarm:InstallToRemoteAccessSession\",\n    \"ListArtifacts\": \"devicefarm:ListArtifacts\",\n    \"ListDeviceInstances\": \"devicefarm:ListDeviceInstances\",\n    \"ListDevicePools\": \"devicefarm:ListDevicePools\",\n    \"ListDevices\": \"devicefarm:ListDevices\",\n    \"ListInstanceProfiles\": \"devicefarm:ListInstanceProfiles\",\n    \"ListJobs\": \"devicefarm:ListJobs\",\n    \"ListNetworkProfiles\": \"devicefarm:ListNetworkProfiles\",\n    \"ListOfferingPromotions\": \"devicefarm:ListOfferingPromotions\",\n    \"ListOfferingTransactions\": \"devicefarm:ListOfferingTransactions\",\n    \"ListOfferings\": \"devicefarm:ListOfferings\",\n    \"ListProjects\": \"devicefarm:ListProjects\",\n    \"ListRemoteAccessSessions\": \"devicefarm:ListRemoteAccessSessions\",\n    \"ListRuns\": \"devicefarm:ListRuns\",\n    \"ListSamples\": \"devicefarm:ListSamples\",\n    \"ListSuites\": \"devicefarm:ListSuites\",\n    \"ListTagsForResource\": \"devicefarm:ListTagsForResource\",\n    \"ListTestGridProjects\": \"devicefarm:ListTestGridProjects\",\n    \"ListTestGridSessionActions\": \"devicefarm:ListTestGridSessionActions\",\n    \"ListTestGridSessionArtifacts\": \"devicefarm:ListTestGridSessionArtifacts\",\n    \"ListTestGridSessions\": \"devicefarm:ListTestGridSessions\",\n    \"ListTests\": \"devicefarm:ListTests\",\n    \"ListUniqueProblems\": \"devicefarm:ListUniqueProblems\",\n    \"ListUploads\": \"devicefarm:ListUploads\",\n    \"ListVPCEConfigurations\": \"devicefarm:ListVPCEConfigurations\",\n    \"PurchaseOffering\": \"devicefarm:PurchaseOffering\",\n    \"RenewOffering\": \"devicefarm:RenewOffering\",\n    \"ScheduleRun\": \"devicefarm:ScheduleRun\",\n    \"StopJob\": \"devicefarm:StopJob\",\n    \"StopRemoteAccessSession\": \"devicefarm:StopRemoteAccessSession\",\n    \"StopRun\": \"devicefarm:StopRun\",\n    \"TagResource\": \"devicefarm:TagResource\",\n    \"UntagResource\": \"devicefarm:UntagResource\",\n    \"UpdateDeviceInstance\": \"devicefarm:UpdateDeviceInstance\",\n    \"UpdateDevicePool\": \"devicefarm:UpdateDevicePool\",\n    \"UpdateInstanceProfile\": \"devicefarm:UpdateInstanceProfile\",\n    \"UpdateNetworkProfile\": \"devicefarm:UpdateNetworkProfile\",\n    \"UpdateProject\": \"devicefarm:UpdateProject\",\n    \"UpdateTestGridProject\": \"devicefarm:UpdateTestGridProject\",\n    \"UpdateUpload\": \"devicefarm:UpdateUpload\",\n    \"UpdateVPCEConfiguration\": \"devicefarm:UpdateVPCEConfiguration\"\n  },\n  \"devops-guru\": {\n    \"AddNotificationChannel\": \"devops-guru:AddNotificationChannel\",\n    \"DescribeAccountHealth\": \"devops-guru:DescribeAccountHealth\",\n    \"DescribeAccountOverview\": \"devops-guru:DescribeAccountOverview\",\n    \"DescribeAnomaly\": \"devops-guru:DescribeAnomaly\",\n    \"DescribeFeedback\": \"devops-guru:DescribeFeedback\",\n    \"DescribeInsight\": \"devops-guru:DescribeInsight\",\n    \"DescribeOrganizationHealth\": \"devops-guru:DescribeOrganizationHealth\",\n    \"DescribeOrganizationOverview\": \"devops-guru:DescribeOrganizationOverview\",\n    \"DescribeOrganizationResourceCollectionHealth\": \"devops-guru:DescribeOrganizationResourceCollectionHealth\",\n    \"DescribeResourceCollectionHealth\": \"devops-guru:DescribeResourceCollectionHealth\",\n    \"DescribeServiceIntegration\": \"devops-guru:DescribeServiceIntegration\",\n    \"GetCostEstimation\": \"devops-guru:GetCostEstimation\",\n    \"GetResourceCollection\": \"devops-guru:GetResourceCollection\",\n    \"ListAnomaliesForInsight\": \"devops-guru:ListAnomaliesForInsight\",\n    \"ListEvents\": \"devops-guru:ListEvents\",\n    \"ListInsights\": \"devops-guru:ListInsights\",\n    \"ListNotificationChannels\": \"devops-guru:ListNotificationChannels\",\n    \"ListOrganizationInsights\": \"devops-guru:ListOrganizationInsights\",\n    \"ListRecommendations\": \"devops-guru:ListRecommendations\",\n    \"PutFeedback\": \"devops-guru:PutFeedback\",\n    \"RemoveNotificationChannel\": \"devops-guru:RemoveNotificationChannel\",\n    \"SearchInsights\": \"devops-guru:SearchInsights\",\n    \"SearchOrganizationInsights\": \"devops-guru:SearchOrganizationInsights\",\n    \"StartCostEstimation\": \"devops-guru:StartCostEstimation\",\n    \"UpdateResourceCollection\": \"devops-guru:UpdateResourceCollection\",\n    \"UpdateServiceIntegration\": \"devops-guru:UpdateServiceIntegration\"\n  },\n  \"directconnect\": {\n    \"AcceptDirectConnectGatewayAssociationProposal\": \"directconnect:AcceptDirectConnectGatewayAssociationProposal\",\n    \"AllocateConnectionOnInterconnect\": \"directconnect:AllocateConnectionOnInterconnect\",\n    \"AllocateHostedConnection\": \"directconnect:AllocateHostedConnection\",\n    \"AllocatePrivateVirtualInterface\": \"directconnect:AllocatePrivateVirtualInterface\",\n    \"AllocatePublicVirtualInterface\": \"directconnect:AllocatePublicVirtualInterface\",\n    \"AllocateTransitVirtualInterface\": \"directconnect:AllocateTransitVirtualInterface\",\n    \"AssociateConnectionWithLag\": \"directconnect:AssociateConnectionWithLag\",\n    \"AssociateHostedConnection\": \"directconnect:AssociateHostedConnection\",\n    \"AssociateMacSecKey\": \"directconnect:AssociateMacSecKey\",\n    \"AssociateVirtualInterface\": \"directconnect:AssociateVirtualInterface\",\n    \"ConfirmConnection\": \"directconnect:ConfirmConnection\",\n    \"ConfirmCustomerAgreement\": \"directconnect:ConfirmCustomerAgreement\",\n    \"ConfirmPrivateVirtualInterface\": \"directconnect:ConfirmPrivateVirtualInterface\",\n    \"ConfirmPublicVirtualInterface\": \"directconnect:ConfirmPublicVirtualInterface\",\n    \"ConfirmTransitVirtualInterface\": \"directconnect:ConfirmTransitVirtualInterface\",\n    \"CreateBGPPeer\": \"directconnect:CreateBGPPeer\",\n    \"CreateConnection\": \"directconnect:CreateConnection\",\n    \"CreateDirectConnectGateway\": \"directconnect:CreateDirectConnectGateway\",\n    \"CreateDirectConnectGatewayAssociation\": \"directconnect:CreateDirectConnectGatewayAssociation\",\n    \"CreateDirectConnectGatewayAssociationProposal\": \"directconnect:CreateDirectConnectGatewayAssociationProposal\",\n    \"CreateInterconnect\": \"directconnect:CreateInterconnect\",\n    \"CreateLag\": \"directconnect:CreateLag\",\n    \"CreatePrivateVirtualInterface\": \"directconnect:CreatePrivateVirtualInterface\",\n    \"CreatePublicVirtualInterface\": \"directconnect:CreatePublicVirtualInterface\",\n    \"CreateTransitVirtualInterface\": \"directconnect:CreateTransitVirtualInterface\",\n    \"DeleteBGPPeer\": \"directconnect:DeleteBGPPeer\",\n    \"DeleteConnection\": \"directconnect:DeleteConnection\",\n    \"DeleteDirectConnectGateway\": \"directconnect:DeleteDirectConnectGateway\",\n    \"DeleteDirectConnectGatewayAssociation\": \"directconnect:DeleteDirectConnectGatewayAssociation\",\n    \"DeleteDirectConnectGatewayAssociationProposal\": \"directconnect:DeleteDirectConnectGatewayAssociationProposal\",\n    \"DeleteInterconnect\": \"directconnect:DeleteInterconnect\",\n    \"DeleteLag\": \"directconnect:DeleteLag\",\n    \"DeleteVirtualInterface\": \"directconnect:DeleteVirtualInterface\",\n    \"DescribeConnectionLoa\": \"directconnect:DescribeConnectionLoa\",\n    \"DescribeConnections\": \"directconnect:DescribeConnections\",\n    \"DescribeConnectionsOnInterconnect\": \"directconnect:DescribeConnectionsOnInterconnect\",\n    \"DescribeCustomerMetadata\": \"directconnect:DescribeCustomerMetadata\",\n    \"DescribeDirectConnectGatewayAssociationProposals\": \"directconnect:DescribeDirectConnectGatewayAssociationProposals\",\n    \"DescribeDirectConnectGatewayAssociations\": \"directconnect:DescribeDirectConnectGatewayAssociations\",\n    \"DescribeDirectConnectGatewayAttachments\": \"directconnect:DescribeDirectConnectGatewayAttachments\",\n    \"DescribeDirectConnectGateways\": \"directconnect:DescribeDirectConnectGateways\",\n    \"DescribeHostedConnections\": \"directconnect:DescribeHostedConnections\",\n    \"DescribeInterconnectLoa\": \"directconnect:DescribeInterconnectLoa\",\n    \"DescribeInterconnects\": \"directconnect:DescribeInterconnects\",\n    \"DescribeLags\": \"directconnect:DescribeLags\",\n    \"DescribeLoa\": \"directconnect:DescribeLoa\",\n    \"DescribeLocations\": \"directconnect:DescribeLocations\",\n    \"DescribeRouterConfiguration\": \"directconnect:DescribeRouterConfiguration\",\n    \"DescribeTags\": \"directconnect:DescribeTags\",\n    \"DescribeVirtualGateways\": \"directconnect:DescribeVirtualGateways\",\n    \"DescribeVirtualInterfaces\": \"directconnect:DescribeVirtualInterfaces\",\n    \"DisassociateConnectionFromLag\": \"directconnect:DisassociateConnectionFromLag\",\n    \"DisassociateMacSecKey\": \"directconnect:DisassociateMacSecKey\",\n    \"ListVirtualInterfaceTestHistory\": \"directconnect:ListVirtualInterfaceTestHistory\",\n    \"StartBgpFailoverTest\": \"directconnect:StartBgpFailoverTest\",\n    \"StopBgpFailoverTest\": \"directconnect:StopBgpFailoverTest\",\n    \"TagResource\": \"directconnect:TagResource\",\n    \"UntagResource\": \"directconnect:UntagResource\",\n    \"UpdateConnection\": \"directconnect:UpdateConnection\",\n    \"UpdateDirectConnectGateway\": \"directconnect:UpdateDirectConnectGateway\",\n    \"UpdateDirectConnectGatewayAssociation\": \"directconnect:UpdateDirectConnectGatewayAssociation\",\n    \"UpdateLag\": \"directconnect:UpdateLag\",\n    \"UpdateVirtualInterfaceAttributes\": \"directconnect:UpdateVirtualInterfaceAttributes\"\n  },\n  \"discovery\": {\n    \"AssociateConfigurationItemsToApplication\": \"discovery:AssociateConfigurationItemsToApplication\",\n    \"BatchDeleteImportData\": \"discovery:BatchDeleteImportData\",\n    \"CreateApplication\": \"discovery:CreateApplication\",\n    \"CreateTags\": \"discovery:CreateTags\",\n    \"DeleteApplications\": \"discovery:DeleteApplications\",\n    \"DeleteTags\": \"discovery:DeleteTags\",\n    \"DescribeAgents\": \"discovery:DescribeAgents\",\n    \"DescribeConfigurations\": \"discovery:DescribeConfigurations\",\n    \"DescribeContinuousExports\": \"discovery:DescribeContinuousExports\",\n    \"DescribeExportConfigurations\": \"discovery:DescribeExportConfigurations\",\n    \"DescribeExportTasks\": \"discovery:DescribeExportTasks\",\n    \"DescribeImportTasks\": \"discovery:DescribeImportTasks\",\n    \"DescribeTags\": \"discovery:DescribeTags\",\n    \"DisassociateConfigurationItemsFromApplication\": \"discovery:DisassociateConfigurationItemsFromApplication\",\n    \"ExportConfigurations\": \"discovery:ExportConfigurations\",\n    \"GetDiscoverySummary\": \"discovery:GetDiscoverySummary\",\n    \"ListConfigurations\": \"discovery:ListConfigurations\",\n    \"ListServerNeighbors\": \"discovery:ListServerNeighbors\",\n    \"StartContinuousExport\": \"discovery:StartContinuousExport\",\n    \"StartDataCollectionByAgentIds\": \"discovery:StartDataCollectionByAgentIds\",\n    \"StartExportTask\": \"discovery:StartExportTask\",\n    \"StartImportTask\": \"discovery:StartImportTask\",\n    \"StopContinuousExport\": \"discovery:StopContinuousExport\",\n    \"StopDataCollectionByAgentIds\": \"discovery:StopDataCollectionByAgentIds\",\n    \"UpdateApplication\": \"discovery:UpdateApplication\"\n  },\n  \"dlm\": {\n    \"CreateLifecyclePolicy\": \"dlm:CreateLifecyclePolicy\",\n    \"DeleteLifecyclePolicy\": \"dlm:DeleteLifecyclePolicy\",\n    \"GetLifecyclePolicies\": \"dlm:GetLifecyclePolicies\",\n    \"GetLifecyclePolicy\": \"dlm:GetLifecyclePolicy\",\n    \"ListTagsForResource\": \"dlm:ListTagsForResource\",\n    \"TagResource\": \"dlm:TagResource\",\n    \"UntagResource\": \"dlm:UntagResource\",\n    \"UpdateLifecyclePolicy\": \"dlm:UpdateLifecyclePolicy\"\n  },\n  \"dms\": {\n    \"AddTagsToResource\": \"dms:AddTagsToResource\",\n    \"ApplyPendingMaintenanceAction\": \"dms:ApplyPendingMaintenanceAction\",\n    \"CancelReplicationTaskAssessmentRun\": \"dms:CancelReplicationTaskAssessmentRun\",\n    \"CreateEndpoint\": \"dms:CreateEndpoint\",\n    \"CreateEventSubscription\": \"dms:CreateEventSubscription\",\n    \"CreateReplicationInstance\": \"dms:CreateReplicationInstance\",\n    \"CreateReplicationSubnetGroup\": \"dms:CreateReplicationSubnetGroup\",\n    \"CreateReplicationTask\": \"dms:CreateReplicationTask\",\n    \"DeleteCertificate\": \"dms:DeleteCertificate\",\n    \"DeleteConnection\": \"dms:DeleteConnection\",\n    \"DeleteEndpoint\": \"dms:DeleteEndpoint\",\n    \"DeleteEventSubscription\": \"dms:DeleteEventSubscription\",\n    \"DeleteReplicationInstance\": \"dms:DeleteReplicationInstance\",\n    \"DeleteReplicationSubnetGroup\": \"dms:DeleteReplicationSubnetGroup\",\n    \"DeleteReplicationTask\": \"dms:DeleteReplicationTask\",\n    \"DeleteReplicationTaskAssessmentRun\": \"dms:DeleteReplicationTaskAssessmentRun\",\n    \"DescribeAccountAttributes\": \"dms:DescribeAccountAttributes\",\n    \"DescribeApplicableIndividualAssessments\": \"dms:DescribeApplicableIndividualAssessments\",\n    \"DescribeCertificates\": \"dms:DescribeCertificates\",\n    \"DescribeConnections\": \"dms:DescribeConnections\",\n    \"DescribeEndpointSettings\": \"dms:DescribeEndpointSettings\",\n    \"DescribeEndpointTypes\": \"dms:DescribeEndpointTypes\",\n    \"DescribeEndpoints\": \"dms:DescribeEndpoints\",\n    \"DescribeEventCategories\": \"dms:DescribeEventCategories\",\n    \"DescribeEventSubscriptions\": \"dms:DescribeEventSubscriptions\",\n    \"DescribeEvents\": \"dms:DescribeEvents\",\n    \"DescribeOrderableReplicationInstances\": \"dms:DescribeOrderableReplicationInstances\",\n    \"DescribeRefreshSchemasStatus\": \"dms:DescribeRefreshSchemasStatus\",\n    \"DescribeReplicationInstanceTaskLogs\": \"dms:DescribeReplicationInstanceTaskLogs\",\n    \"DescribeReplicationInstances\": \"dms:DescribeReplicationInstances\",\n    \"DescribeReplicationSubnetGroups\": \"dms:DescribeReplicationSubnetGroups\",\n    \"DescribeReplicationTaskAssessmentResults\": \"dms:DescribeReplicationTaskAssessmentResults\",\n    \"DescribeReplicationTaskAssessmentRuns\": \"dms:DescribeReplicationTaskAssessmentRuns\",\n    \"DescribeReplicationTaskIndividualAssessments\": \"dms:DescribeReplicationTaskIndividualAssessments\",\n    \"DescribeReplicationTasks\": \"dms:DescribeReplicationTasks\",\n    \"DescribeSchemas\": \"dms:DescribeSchemas\",\n    \"DescribeTableStatistics\": \"dms:DescribeTableStatistics\",\n    \"ImportCertificate\": \"dms:ImportCertificate\",\n    \"ListTagsForResource\": \"dms:ListTagsForResource\",\n    \"ModifyEndpoint\": \"dms:ModifyEndpoint\",\n    \"ModifyEventSubscription\": \"dms:ModifyEventSubscription\",\n    \"ModifyReplicationInstance\": \"dms:ModifyReplicationInstance\",\n    \"ModifyReplicationSubnetGroup\": \"dms:ModifyReplicationSubnetGroup\",\n    \"ModifyReplicationTask\": \"dms:ModifyReplicationTask\",\n    \"MoveReplicationTask\": \"dms:MoveReplicationTask\",\n    \"RebootReplicationInstance\": \"dms:RebootReplicationInstance\",\n    \"RefreshSchemas\": \"dms:RefreshSchemas\",\n    \"ReloadTables\": \"dms:ReloadTables\",\n    \"RemoveTagsFromResource\": \"dms:RemoveTagsFromResource\",\n    \"StartReplicationTask\": \"dms:StartReplicationTask\",\n    \"StartReplicationTaskAssessment\": \"dms:StartReplicationTaskAssessment\",\n    \"StartReplicationTaskAssessmentRun\": \"dms:StartReplicationTaskAssessmentRun\",\n    \"StopReplicationTask\": \"dms:StopReplicationTask\",\n    \"TestConnection\": \"dms:TestConnection\"\n  },\n  \"drs\": {\n    \"CreateReplicationConfigurationTemplate\": \"drs:CreateReplicationConfigurationTemplate\",\n    \"DeleteJob\": \"drs:DeleteJob\",\n    \"DeleteRecoveryInstance\": \"drs:DeleteRecoveryInstance\",\n    \"DeleteReplicationConfigurationTemplate\": \"drs:DeleteReplicationConfigurationTemplate\",\n    \"DeleteSourceServer\": \"drs:DeleteSourceServer\",\n    \"DescribeJobLogItems\": \"drs:DescribeJobLogItems\",\n    \"DescribeJobs\": \"drs:DescribeJobs\",\n    \"DescribeRecoveryInstances\": \"drs:DescribeRecoveryInstances\",\n    \"DescribeRecoverySnapshots\": \"drs:DescribeRecoverySnapshots\",\n    \"DescribeReplicationConfigurationTemplates\": \"drs:DescribeReplicationConfigurationTemplates\",\n    \"DescribeSourceServers\": \"drs:DescribeSourceServers\",\n    \"DisconnectRecoveryInstance\": \"drs:DisconnectRecoveryInstance\",\n    \"DisconnectSourceServer\": \"drs:DisconnectSourceServer\",\n    \"GetFailbackReplicationConfiguration\": \"drs:GetFailbackReplicationConfiguration\",\n    \"GetLaunchConfiguration\": \"drs:GetLaunchConfiguration\",\n    \"GetReplicationConfiguration\": \"drs:GetReplicationConfiguration\",\n    \"InitializeService\": \"drs:InitializeService\",\n    \"ListTagsForResource\": \"drs:ListTagsForResource\",\n    \"RetryDataReplication\": \"drs:RetryDataReplication\",\n    \"StartFailbackLaunch\": \"drs:StartFailbackLaunch\",\n    \"StartRecovery\": \"drs:StartRecovery\",\n    \"StopFailback\": \"drs:StopFailback\",\n    \"TagResource\": \"drs:TagResource\",\n    \"TerminateRecoveryInstances\": \"drs:TerminateRecoveryInstances\",\n    \"UntagResource\": \"drs:UntagResource\",\n    \"UpdateFailbackReplicationConfiguration\": \"drs:UpdateFailbackReplicationConfiguration\",\n    \"UpdateLaunchConfiguration\": \"drs:UpdateLaunchConfiguration\",\n    \"UpdateReplicationConfiguration\": \"drs:UpdateReplicationConfiguration\",\n    \"UpdateReplicationConfigurationTemplate\": \"drs:UpdateReplicationConfigurationTemplate\"\n  },\n  \"ds\": {\n    \"AcceptSharedDirectory\": \"ds:AcceptSharedDirectory\",\n    \"AddIpRoutes\": \"ds:AddIpRoutes\",\n    \"AddRegion\": \"ds:AddRegion\",\n    \"AddTagsToResource\": \"ds:AddTagsToResource\",\n    \"CancelSchemaExtension\": \"ds:CancelSchemaExtension\",\n    \"ConnectDirectory\": \"ds:ConnectDirectory\",\n    \"CreateAlias\": \"ds:CreateAlias\",\n    \"CreateComputer\": \"ds:CreateComputer\",\n    \"CreateConditionalForwarder\": \"ds:CreateConditionalForwarder\",\n    \"CreateDirectory\": \"ds:CreateDirectory\",\n    \"CreateLogSubscription\": \"ds:CreateLogSubscription\",\n    \"CreateMicrosoftAD\": \"ds:CreateMicrosoftAD\",\n    \"CreateSnapshot\": \"ds:CreateSnapshot\",\n    \"CreateTrust\": \"ds:CreateTrust\",\n    \"DeleteConditionalForwarder\": \"ds:DeleteConditionalForwarder\",\n    \"DeleteDirectory\": \"ds:DeleteDirectory\",\n    \"DeleteLogSubscription\": \"ds:DeleteLogSubscription\",\n    \"DeleteSnapshot\": \"ds:DeleteSnapshot\",\n    \"DeleteTrust\": \"ds:DeleteTrust\",\n    \"DeregisterCertificate\": \"ds:DeregisterCertificate\",\n    \"DeregisterEventTopic\": \"ds:DeregisterEventTopic\",\n    \"DescribeCertificate\": \"ds:DescribeCertificate\",\n    \"DescribeConditionalForwarders\": \"ds:DescribeConditionalForwarders\",\n    \"DescribeDirectories\": \"ds:DescribeDirectories\",\n    \"DescribeDomainControllers\": \"ds:DescribeDomainControllers\",\n    \"DescribeEventTopics\": \"ds:DescribeEventTopics\",\n    \"DescribeLDAPSSettings\": \"ds:DescribeLDAPSSettings\",\n    \"DescribeRegions\": \"ds:DescribeRegions\",\n    \"DescribeSharedDirectories\": \"ds:DescribeSharedDirectories\",\n    \"DescribeSnapshots\": \"ds:DescribeSnapshots\",\n    \"DescribeTrusts\": \"ds:DescribeTrusts\",\n    \"DisableClientAuthentication\": \"ds:DisableClientAuthentication\",\n    \"DisableLDAPS\": \"ds:DisableLDAPS\",\n    \"DisableRadius\": \"ds:DisableRadius\",\n    \"DisableSso\": \"ds:DisableSso\",\n    \"EnableClientAuthentication\": \"ds:EnableClientAuthentication\",\n    \"EnableLDAPS\": \"ds:EnableLDAPS\",\n    \"EnableRadius\": \"ds:EnableRadius\",\n    \"EnableSso\": \"ds:EnableSso\",\n    \"GetDirectoryLimits\": \"ds:GetDirectoryLimits\",\n    \"GetSnapshotLimits\": \"ds:GetSnapshotLimits\",\n    \"ListCertificates\": \"ds:ListCertificates\",\n    \"ListIpRoutes\": \"ds:ListIpRoutes\",\n    \"ListLogSubscriptions\": \"ds:ListLogSubscriptions\",\n    \"ListSchemaExtensions\": \"ds:ListSchemaExtensions\",\n    \"ListTagsForResource\": \"ds:ListTagsForResource\",\n    \"RegisterCertificate\": \"ds:RegisterCertificate\",\n    \"RegisterEventTopic\": \"ds:RegisterEventTopic\",\n    \"RejectSharedDirectory\": \"ds:RejectSharedDirectory\",\n    \"RemoveIpRoutes\": \"ds:RemoveIpRoutes\",\n    \"RemoveRegion\": \"ds:RemoveRegion\",\n    \"RemoveTagsFromResource\": \"ds:RemoveTagsFromResource\",\n    \"ResetUserPassword\": \"ds:ResetUserPassword\",\n    \"RestoreFromSnapshot\": \"ds:RestoreFromSnapshot\",\n    \"ShareDirectory\": \"ds:ShareDirectory\",\n    \"StartSchemaExtension\": \"ds:StartSchemaExtension\",\n    \"UnshareDirectory\": \"ds:UnshareDirectory\",\n    \"UpdateConditionalForwarder\": \"ds:UpdateConditionalForwarder\",\n    \"UpdateNumberOfDomainControllers\": \"ds:UpdateNumberOfDomainControllers\",\n    \"UpdateRadius\": \"ds:UpdateRadius\",\n    \"UpdateTrust\": \"ds:UpdateTrust\",\n    \"VerifyTrust\": \"ds:VerifyTrust\"\n  },\n  \"dynamodb\": {\n    \"BatchGetItem\": \"dynamodb:BatchGetItem\",\n    \"BatchWriteItem\": \"dynamodb:BatchWriteItem\",\n    \"CreateBackup\": \"dynamodb:CreateBackup\",\n    \"CreateGlobalTable\": \"dynamodb:CreateGlobalTable\",\n    \"CreateTable\": \"dynamodb:CreateTable\",\n    \"DeleteBackup\": \"dynamodb:DeleteBackup\",\n    \"DeleteItem\": \"dynamodb:DeleteItem\",\n    \"DeleteTable\": \"dynamodb:DeleteTable\",\n    \"DescribeBackup\": \"dynamodb:DescribeBackup\",\n    \"DescribeContinuousBackups\": \"dynamodb:DescribeContinuousBackups\",\n    \"DescribeContributorInsights\": \"dynamodb:DescribeContributorInsights\",\n    \"DescribeExport\": \"dynamodb:DescribeExport\",\n    \"DescribeGlobalTable\": \"dynamodb:DescribeGlobalTable\",\n    \"DescribeGlobalTableSettings\": \"dynamodb:DescribeGlobalTableSettings\",\n    \"DescribeKinesisStreamingDestination\": \"dynamodb:DescribeKinesisStreamingDestination\",\n    \"DescribeLimits\": \"dynamodb:DescribeLimits\",\n    \"DescribeTable\": \"dynamodb:DescribeTable\",\n    \"DescribeTableReplicaAutoScaling\": \"dynamodb:DescribeTableReplicaAutoScaling\",\n    \"DescribeTimeToLive\": \"dynamodb:DescribeTimeToLive\",\n    \"DisableKinesisStreamingDestination\": \"dynamodb:DisableKinesisStreamingDestination\",\n    \"EnableKinesisStreamingDestination\": \"dynamodb:EnableKinesisStreamingDestination\",\n    \"ExportTableToPointInTime\": \"dynamodb:ExportTableToPointInTime\",\n    \"GetItem\": \"dynamodb:GetItem\",\n    \"ListBackups\": \"dynamodb:ListBackups\",\n    \"ListContributorInsights\": \"dynamodb:ListContributorInsights\",\n    \"ListExports\": \"dynamodb:ListExports\",\n    \"ListGlobalTables\": \"dynamodb:ListGlobalTables\",\n    \"ListTables\": \"dynamodb:ListTables\",\n    \"ListTagsOfResource\": \"dynamodb:ListTagsOfResource\",\n    \"PutItem\": \"dynamodb:PutItem\",\n    \"Query\": \"dynamodb:Query\",\n    \"RestoreTableFromBackup\": \"dynamodb:RestoreTableFromBackup\",\n    \"RestoreTableToPointInTime\": \"dynamodb:RestoreTableToPointInTime\",\n    \"Scan\": \"dynamodb:Scan\",\n    \"TagResource\": \"dynamodb:TagResource\",\n    \"UntagResource\": \"dynamodb:UntagResource\",\n    \"UpdateContinuousBackups\": \"dynamodb:UpdateContinuousBackups\",\n    \"UpdateContributorInsights\": \"dynamodb:UpdateContributorInsights\",\n    \"UpdateGlobalTable\": \"dynamodb:UpdateGlobalTable\",\n    \"UpdateGlobalTableSettings\": \"dynamodb:UpdateGlobalTableSettings\",\n    \"UpdateItem\": \"dynamodb:UpdateItem\",\n    \"UpdateTable\": \"dynamodb:UpdateTable\",\n    \"UpdateTableReplicaAutoScaling\": \"dynamodb:UpdateTableReplicaAutoScaling\",\n    \"UpdateTimeToLive\": \"dynamodb:UpdateTimeToLive\"\n  },\n  \"dynamodbstreams\": {\n    \"DescribeStream\": \"dynamodb:DescribeStream\",\n    \"GetRecords\": \"dynamodb:GetRecords\",\n    \"GetShardIterator\": \"dynamodb:GetShardIterator\",\n    \"ListStreams\": \"dynamodb:ListStreams\"\n  },\n  \"ebs\": {\n    \"CompleteSnapshot\": \"ebs:CompleteSnapshot\",\n    \"GetSnapshotBlock\": \"ebs:GetSnapshotBlock\",\n    \"ListChangedBlocks\": \"ebs:ListChangedBlocks\",\n    \"ListSnapshotBlocks\": \"ebs:ListSnapshotBlocks\",\n    \"PutSnapshotBlock\": \"ebs:PutSnapshotBlock\",\n    \"StartSnapshot\": \"ebs:StartSnapshot\"\n  },\n  \"ec2\": {\n    \"AcceptReservedInstancesExchangeQuote\": \"ec2:AcceptReservedInstancesExchangeQuote\",\n    \"AcceptTransitGatewayMulticastDomainAssociations\": \"ec2:AcceptTransitGatewayMulticastDomainAssociations\",\n    \"AcceptTransitGatewayPeeringAttachment\": \"ec2:AcceptTransitGatewayPeeringAttachment\",\n    \"AcceptTransitGatewayVpcAttachment\": \"ec2:AcceptTransitGatewayVpcAttachment\",\n    \"AcceptVpcEndpointConnections\": \"ec2:AcceptVpcEndpointConnections\",\n    \"AcceptVpcPeeringConnection\": \"ec2:AcceptVpcPeeringConnection\",\n    \"AdvertiseByoipCidr\": \"ec2:AdvertiseByoipCidr\",\n    \"AllocateAddress\": \"ec2:AllocateAddress\",\n    \"AllocateHosts\": \"ec2:AllocateHosts\",\n    \"AllocateIpamPoolCidr\": \"ec2:AllocateIpamPoolCidr\",\n    \"ApplySecurityGroupsToClientVpnTargetNetwork\": \"ec2:ApplySecurityGroupsToClientVpnTargetNetwork\",\n    \"AssignIpv6Addresses\": \"ec2:AssignIpv6Addresses\",\n    \"AssignPrivateIpAddresses\": \"ec2:AssignPrivateIpAddresses\",\n    \"AssociateAddress\": \"ec2:AssociateAddress\",\n    \"AssociateClientVpnTargetNetwork\": \"ec2:AssociateClientVpnTargetNetwork\",\n    \"AssociateDhcpOptions\": \"ec2:AssociateDhcpOptions\",\n    \"AssociateEnclaveCertificateIamRole\": \"ec2:AssociateEnclaveCertificateIamRole\",\n    \"AssociateIamInstanceProfile\": \"ec2:AssociateIamInstanceProfile\",\n    \"AssociateInstanceEventWindow\": \"ec2:AssociateInstanceEventWindow\",\n    \"AssociateRouteTable\": \"ec2:AssociateRouteTable\",\n    \"AssociateSubnetCidrBlock\": \"ec2:AssociateSubnetCidrBlock\",\n    \"AssociateTransitGatewayMulticastDomain\": \"ec2:AssociateTransitGatewayMulticastDomain\",\n    \"AssociateTransitGatewayRouteTable\": \"ec2:AssociateTransitGatewayRouteTable\",\n    \"AssociateTrunkInterface\": \"ec2:AssociateTrunkInterface\",\n    \"AssociateVpcCidrBlock\": \"ec2:AssociateVpcCidrBlock\",\n    \"AttachClassicLinkVpc\": \"ec2:AttachClassicLinkVpc\",\n    \"AttachInternetGateway\": \"ec2:AttachInternetGateway\",\n    \"AttachNetworkInterface\": \"ec2:AttachNetworkInterface\",\n    \"AttachVolume\": \"ec2:AttachVolume\",\n    \"AttachVpnGateway\": \"ec2:AttachVpnGateway\",\n    \"AuthorizeClientVpnIngress\": \"ec2:AuthorizeClientVpnIngress\",\n    \"AuthorizeSecurityGroupEgress\": \"ec2:AuthorizeSecurityGroupEgress\",\n    \"AuthorizeSecurityGroupIngress\": \"ec2:AuthorizeSecurityGroupIngress\",\n    \"BundleInstance\": \"ec2:BundleInstance\",\n    \"CancelBundleTask\": \"ec2:CancelBundleTask\",\n    \"CancelCapacityReservation\": \"ec2:CancelCapacityReservation\",\n    \"CancelCapacityReservationFleets\": \"ec2:CancelCapacityReservationFleets\",\n    \"CancelConversionTask\": \"ec2:CancelConversionTask\",\n    \"CancelExportTask\": \"ec2:CancelExportTask\",\n    \"CancelImportTask\": \"ec2:CancelImportTask\",\n    \"CancelReservedInstancesListing\": \"ec2:CancelReservedInstancesListing\",\n    \"CancelSpotFleetRequests\": \"ec2:CancelSpotFleetRequests\",\n    \"CancelSpotInstanceRequests\": \"ec2:CancelSpotInstanceRequests\",\n    \"ConfirmProductInstance\": \"ec2:ConfirmProductInstance\",\n    \"CopyFpgaImage\": \"ec2:CopyFpgaImage\",\n    \"CopyImage\": \"ec2:CopyImage\",\n    \"CopySnapshot\": \"ec2:CopySnapshot\",\n    \"CreateCapacityReservation\": \"ec2:CreateCapacityReservation\",\n    \"CreateCapacityReservationFleet\": \"ec2:CreateCapacityReservationFleet\",\n    \"CreateCarrierGateway\": \"ec2:CreateCarrierGateway\",\n    \"CreateClientVpnEndpoint\": \"ec2:CreateClientVpnEndpoint\",\n    \"CreateClientVpnRoute\": \"ec2:CreateClientVpnRoute\",\n    \"CreateCustomerGateway\": \"ec2:CreateCustomerGateway\",\n    \"CreateDefaultSubnet\": \"ec2:CreateDefaultSubnet\",\n    \"CreateDefaultVpc\": \"ec2:CreateDefaultVpc\",\n    \"CreateDhcpOptions\": \"ec2:CreateDhcpOptions\",\n    \"CreateEgressOnlyInternetGateway\": \"ec2:CreateEgressOnlyInternetGateway\",\n    \"CreateFleet\": \"ec2:CreateFleet\",\n    \"CreateFlowLogs\": \"ec2:CreateFlowLogs\",\n    \"CreateFpgaImage\": \"ec2:CreateFpgaImage\",\n    \"CreateImage\": \"ec2:CreateImage\",\n    \"CreateInstanceEventWindow\": \"ec2:CreateInstanceEventWindow\",\n    \"CreateInstanceExportTask\": \"ec2:CreateInstanceExportTask\",\n    \"CreateInternetGateway\": \"ec2:CreateInternetGateway\",\n    \"CreateIpam\": \"ec2:CreateIpam\",\n    \"CreateIpamPool\": \"ec2:CreateIpamPool\",\n    \"CreateIpamScope\": \"ec2:CreateIpamScope\",\n    \"CreateKeyPair\": \"ec2:CreateKeyPair\",\n    \"CreateLaunchTemplate\": \"ec2:CreateLaunchTemplate\",\n    \"CreateLaunchTemplateVersion\": \"ec2:CreateLaunchTemplateVersion\",\n    \"CreateLocalGatewayRoute\": \"ec2:CreateLocalGatewayRoute\",\n    \"CreateLocalGatewayRouteTableVpcAssociation\": \"ec2:CreateLocalGatewayRouteTableVpcAssociation\",\n    \"CreateManagedPrefixList\": \"ec2:CreateManagedPrefixList\",\n    \"CreateNatGateway\": \"ec2:CreateNatGateway\",\n    \"CreateNetworkAcl\": \"ec2:CreateNetworkAcl\",\n    \"CreateNetworkAclEntry\": \"ec2:CreateNetworkAclEntry\",\n    \"CreateNetworkInsightsAccessScope\": \"ec2:CreateNetworkInsightsAccessScope\",\n    \"CreateNetworkInsightsPath\": \"ec2:CreateNetworkInsightsPath\",\n    \"CreateNetworkInterface\": \"ec2:CreateNetworkInterface\",\n    \"CreateNetworkInterfacePermission\": \"ec2:CreateNetworkInterfacePermission\",\n    \"CreatePlacementGroup\": \"ec2:CreatePlacementGroup\",\n    \"CreatePublicIpv4Pool\": \"ec2:CreatePublicIpv4Pool\",\n    \"CreateReplaceRootVolumeTask\": \"ec2:CreateReplaceRootVolumeTask\",\n    \"CreateReservedInstancesListing\": \"ec2:CreateReservedInstancesListing\",\n    \"CreateRestoreImageTask\": \"ec2:CreateRestoreImageTask\",\n    \"CreateRoute\": \"ec2:CreateRoute\",\n    \"CreateRouteTable\": \"ec2:CreateRouteTable\",\n    \"CreateSecurityGroup\": \"ec2:CreateSecurityGroup\",\n    \"CreateSnapshot\": \"ec2:CreateSnapshot\",\n    \"CreateSnapshots\": \"ec2:CreateSnapshots\",\n    \"CreateSpotDatafeedSubscription\": \"ec2:CreateSpotDatafeedSubscription\",\n    \"CreateStoreImageTask\": \"ec2:CreateStoreImageTask\",\n    \"CreateSubnet\": \"ec2:CreateSubnet\",\n    \"CreateSubnetCidrReservation\": \"ec2:CreateSubnetCidrReservation\",\n    \"CreateTags\": \"ec2:CreateTags\",\n    \"CreateTrafficMirrorFilter\": \"ec2:CreateTrafficMirrorFilter\",\n    \"CreateTrafficMirrorFilterRule\": \"ec2:CreateTrafficMirrorFilterRule\",\n    \"CreateTrafficMirrorSession\": \"ec2:CreateTrafficMirrorSession\",\n    \"CreateTrafficMirrorTarget\": \"ec2:CreateTrafficMirrorTarget\",\n    \"CreateTransitGateway\": \"ec2:CreateTransitGateway\",\n    \"CreateTransitGatewayConnect\": \"ec2:CreateTransitGatewayConnect\",\n    \"CreateTransitGatewayConnectPeer\": \"ec2:CreateTransitGatewayConnectPeer\",\n    \"CreateTransitGatewayMulticastDomain\": \"ec2:CreateTransitGatewayMulticastDomain\",\n    \"CreateTransitGatewayPeeringAttachment\": \"ec2:CreateTransitGatewayPeeringAttachment\",\n    \"CreateTransitGatewayPrefixListReference\": \"ec2:CreateTransitGatewayPrefixListReference\",\n    \"CreateTransitGatewayRoute\": \"ec2:CreateTransitGatewayRoute\",\n    \"CreateTransitGatewayRouteTable\": \"ec2:CreateTransitGatewayRouteTable\",\n    \"CreateTransitGatewayVpcAttachment\": \"ec2:CreateTransitGatewayVpcAttachment\",\n    \"CreateVolume\": \"ec2:CreateVolume\",\n    \"CreateVpc\": \"ec2:CreateVpc\",\n    \"CreateVpcEndpoint\": \"ec2:CreateVpcEndpoint\",\n    \"CreateVpcEndpointConnectionNotification\": \"ec2:CreateVpcEndpointConnectionNotification\",\n    \"CreateVpcEndpointServiceConfiguration\": \"ec2:CreateVpcEndpointServiceConfiguration\",\n    \"CreateVpcPeeringConnection\": \"ec2:CreateVpcPeeringConnection\",\n    \"CreateVpnConnection\": \"ec2:CreateVpnConnection\",\n    \"CreateVpnConnectionRoute\": \"ec2:CreateVpnConnectionRoute\",\n    \"CreateVpnGateway\": \"ec2:CreateVpnGateway\",\n    \"DeleteCarrierGateway\": \"ec2:DeleteCarrierGateway\",\n    \"DeleteClientVpnEndpoint\": \"ec2:DeleteClientVpnEndpoint\",\n    \"DeleteClientVpnRoute\": \"ec2:DeleteClientVpnRoute\",\n    \"DeleteCustomerGateway\": \"ec2:DeleteCustomerGateway\",\n    \"DeleteDhcpOptions\": \"ec2:DeleteDhcpOptions\",\n    \"DeleteEgressOnlyInternetGateway\": \"ec2:DeleteEgressOnlyInternetGateway\",\n    \"DeleteFleets\": \"ec2:DeleteFleets\",\n    \"DeleteFlowLogs\": \"ec2:DeleteFlowLogs\",\n    \"DeleteFpgaImage\": \"ec2:DeleteFpgaImage\",\n    \"DeleteInstanceEventWindow\": \"ec2:DeleteInstanceEventWindow\",\n    \"DeleteInternetGateway\": \"ec2:DeleteInternetGateway\",\n    \"DeleteIpam\": \"ec2:DeleteIpam\",\n    \"DeleteIpamPool\": \"ec2:DeleteIpamPool\",\n    \"DeleteIpamScope\": \"ec2:DeleteIpamScope\",\n    \"DeleteKeyPair\": \"ec2:DeleteKeyPair\",\n    \"DeleteLaunchTemplate\": \"ec2:DeleteLaunchTemplate\",\n    \"DeleteLaunchTemplateVersions\": \"ec2:DeleteLaunchTemplateVersions\",\n    \"DeleteLocalGatewayRoute\": \"ec2:DeleteLocalGatewayRoute\",\n    \"DeleteLocalGatewayRouteTableVpcAssociation\": \"ec2:DeleteLocalGatewayRouteTableVpcAssociation\",\n    \"DeleteManagedPrefixList\": \"ec2:DeleteManagedPrefixList\",\n    \"DeleteNatGateway\": \"ec2:DeleteNatGateway\",\n    \"DeleteNetworkAcl\": \"ec2:DeleteNetworkAcl\",\n    \"DeleteNetworkAclEntry\": \"ec2:DeleteNetworkAclEntry\",\n    \"DeleteNetworkInsightsAccessScope\": \"ec2:DeleteNetworkInsightsAccessScope\",\n    \"DeleteNetworkInsightsAccessScopeAnalysis\": \"ec2:DeleteNetworkInsightsAccessScopeAnalysis\",\n    \"DeleteNetworkInsightsAnalysis\": \"ec2:DeleteNetworkInsightsAnalysis\",\n    \"DeleteNetworkInsightsPath\": \"ec2:DeleteNetworkInsightsPath\",\n    \"DeleteNetworkInterface\": \"ec2:DeleteNetworkInterface\",\n    \"DeleteNetworkInterfacePermission\": \"ec2:DeleteNetworkInterfacePermission\",\n    \"DeletePlacementGroup\": \"ec2:DeletePlacementGroup\",\n    \"DeletePublicIpv4Pool\": \"ec2:DeletePublicIpv4Pool\",\n    \"DeleteQueuedReservedInstances\": \"ec2:DeleteQueuedReservedInstances\",\n    \"DeleteRoute\": \"ec2:DeleteRoute\",\n    \"DeleteRouteTable\": \"ec2:DeleteRouteTable\",\n    \"DeleteSecurityGroup\": \"ec2:DeleteSecurityGroup\",\n    \"DeleteSnapshot\": \"ec2:DeleteSnapshot\",\n    \"DeleteSpotDatafeedSubscription\": \"ec2:DeleteSpotDatafeedSubscription\",\n    \"DeleteSubnet\": \"ec2:DeleteSubnet\",\n    \"DeleteSubnetCidrReservation\": \"ec2:DeleteSubnetCidrReservation\",\n    \"DeleteTags\": \"ec2:DeleteTags\",\n    \"DeleteTrafficMirrorFilter\": \"ec2:DeleteTrafficMirrorFilter\",\n    \"DeleteTrafficMirrorFilterRule\": \"ec2:DeleteTrafficMirrorFilterRule\",\n    \"DeleteTrafficMirrorSession\": \"ec2:DeleteTrafficMirrorSession\",\n    \"DeleteTrafficMirrorTarget\": \"ec2:DeleteTrafficMirrorTarget\",\n    \"DeleteTransitGateway\": \"ec2:DeleteTransitGateway\",\n    \"DeleteTransitGatewayConnect\": \"ec2:DeleteTransitGatewayConnect\",\n    \"DeleteTransitGatewayConnectPeer\": \"ec2:DeleteTransitGatewayConnectPeer\",\n    \"DeleteTransitGatewayMulticastDomain\": \"ec2:DeleteTransitGatewayMulticastDomain\",\n    \"DeleteTransitGatewayPeeringAttachment\": \"ec2:DeleteTransitGatewayPeeringAttachment\",\n    \"DeleteTransitGatewayPrefixListReference\": \"ec2:DeleteTransitGatewayPrefixListReference\",\n    \"DeleteTransitGatewayRoute\": \"ec2:DeleteTransitGatewayRoute\",\n    \"DeleteTransitGatewayRouteTable\": \"ec2:DeleteTransitGatewayRouteTable\",\n    \"DeleteTransitGatewayVpcAttachment\": \"ec2:DeleteTransitGatewayVpcAttachment\",\n    \"DeleteVolume\": \"ec2:DeleteVolume\",\n    \"DeleteVpc\": \"ec2:DeleteVpc\",\n    \"DeleteVpcEndpointConnectionNotifications\": \"ec2:DeleteVpcEndpointConnectionNotifications\",\n    \"DeleteVpcEndpointServiceConfigurations\": \"ec2:DeleteVpcEndpointServiceConfigurations\",\n    \"DeleteVpcEndpoints\": \"ec2:DeleteVpcEndpoints\",\n    \"DeleteVpcPeeringConnection\": \"ec2:DeleteVpcPeeringConnection\",\n    \"DeleteVpnConnection\": \"ec2:DeleteVpnConnection\",\n    \"DeleteVpnConnectionRoute\": \"ec2:DeleteVpnConnectionRoute\",\n    \"DeleteVpnGateway\": \"ec2:DeleteVpnGateway\",\n    \"DeprovisionByoipCidr\": \"ec2:DeprovisionByoipCidr\",\n    \"DeprovisionIpamPoolCidr\": \"ec2:DeprovisionIpamPoolCidr\",\n    \"DeprovisionPublicIpv4PoolCidr\": \"ec2:DeprovisionPublicIpv4PoolCidr\",\n    \"DeregisterImage\": \"ec2:DeregisterImage\",\n    \"DeregisterInstanceEventNotificationAttributes\": \"ec2:DeregisterInstanceEventNotificationAttributes\",\n    \"DeregisterTransitGatewayMulticastGroupMembers\": \"ec2:DeregisterTransitGatewayMulticastGroupMembers\",\n    \"DeregisterTransitGatewayMulticastGroupSources\": \"ec2:DeregisterTransitGatewayMulticastGroupSources\",\n    \"DescribeAccountAttributes\": \"ec2:DescribeAccountAttributes\",\n    \"DescribeAddresses\": \"ec2:DescribeAddresses\",\n    \"DescribeAddressesAttribute\": \"ec2:DescribeAddressesAttribute\",\n    \"DescribeAggregateIdFormat\": \"ec2:DescribeAggregateIdFormat\",\n    \"DescribeAvailabilityZones\": \"ec2:DescribeAvailabilityZones\",\n    \"DescribeBundleTasks\": \"ec2:DescribeBundleTasks\",\n    \"DescribeByoipCidrs\": \"ec2:DescribeByoipCidrs\",\n    \"DescribeCapacityReservationFleets\": \"ec2:DescribeCapacityReservationFleets\",\n    \"DescribeCapacityReservations\": \"ec2:DescribeCapacityReservations\",\n    \"DescribeCarrierGateways\": \"ec2:DescribeCarrierGateways\",\n    \"DescribeClassicLinkInstances\": \"ec2:DescribeClassicLinkInstances\",\n    \"DescribeClientVpnAuthorizationRules\": \"ec2:DescribeClientVpnAuthorizationRules\",\n    \"DescribeClientVpnConnections\": \"ec2:DescribeClientVpnConnections\",\n    \"DescribeClientVpnEndpoints\": \"ec2:DescribeClientVpnEndpoints\",\n    \"DescribeClientVpnRoutes\": \"ec2:DescribeClientVpnRoutes\",\n    \"DescribeClientVpnTargetNetworks\": \"ec2:DescribeClientVpnTargetNetworks\",\n    \"DescribeCoipPools\": \"ec2:DescribeCoipPools\",\n    \"DescribeConversionTasks\": \"ec2:DescribeConversionTasks\",\n    \"DescribeCustomerGateways\": \"ec2:DescribeCustomerGateways\",\n    \"DescribeDhcpOptions\": \"ec2:DescribeDhcpOptions\",\n    \"DescribeEgressOnlyInternetGateways\": \"ec2:DescribeEgressOnlyInternetGateways\",\n    \"DescribeElasticGpus\": \"ec2:DescribeElasticGpus\",\n    \"DescribeExportImageTasks\": \"ec2:DescribeExportImageTasks\",\n    \"DescribeExportTasks\": \"ec2:DescribeExportTasks\",\n    \"DescribeFastSnapshotRestores\": \"ec2:DescribeFastSnapshotRestores\",\n    \"DescribeFleetHistory\": \"ec2:DescribeFleetHistory\",\n    \"DescribeFleetInstances\": \"ec2:DescribeFleetInstances\",\n    \"DescribeFleets\": \"ec2:DescribeFleets\",\n    \"DescribeFlowLogs\": \"ec2:DescribeFlowLogs\",\n    \"DescribeFpgaImageAttribute\": \"ec2:DescribeFpgaImageAttribute\",\n    \"DescribeFpgaImages\": \"ec2:DescribeFpgaImages\",\n    \"DescribeHostReservationOfferings\": \"ec2:DescribeHostReservationOfferings\",\n    \"DescribeHostReservations\": \"ec2:DescribeHostReservations\",\n    \"DescribeHosts\": \"ec2:DescribeHosts\",\n    \"DescribeIamInstanceProfileAssociations\": \"ec2:DescribeIamInstanceProfileAssociations\",\n    \"DescribeIdFormat\": \"ec2:DescribeIdFormat\",\n    \"DescribeIdentityIdFormat\": \"ec2:DescribeIdentityIdFormat\",\n    \"DescribeImageAttribute\": \"ec2:DescribeImageAttribute\",\n    \"DescribeImages\": \"ec2:DescribeImages\",\n    \"DescribeImportImageTasks\": \"ec2:DescribeImportImageTasks\",\n    \"DescribeImportSnapshotTasks\": \"ec2:DescribeImportSnapshotTasks\",\n    \"DescribeInstanceAttribute\": \"ec2:DescribeInstanceAttribute\",\n    \"DescribeInstanceCreditSpecifications\": \"ec2:DescribeInstanceCreditSpecifications\",\n    \"DescribeInstanceEventNotificationAttributes\": \"ec2:DescribeInstanceEventNotificationAttributes\",\n    \"DescribeInstanceEventWindows\": \"ec2:DescribeInstanceEventWindows\",\n    \"DescribeInstanceStatus\": \"ec2:DescribeInstanceStatus\",\n    \"DescribeInstanceTypeOfferings\": \"ec2:DescribeInstanceTypeOfferings\",\n    \"DescribeInstanceTypes\": \"ec2:DescribeInstanceTypes\",\n    \"DescribeInstances\": \"ec2:DescribeInstances\",\n    \"DescribeInternetGateways\": \"ec2:DescribeInternetGateways\",\n    \"DescribeIpamPools\": \"ec2:DescribeIpamPools\",\n    \"DescribeIpamScopes\": \"ec2:DescribeIpamScopes\",\n    \"DescribeIpams\": \"ec2:DescribeIpams\",\n    \"DescribeIpv6Pools\": \"ec2:DescribeIpv6Pools\",\n    \"DescribeKeyPairs\": \"ec2:DescribeKeyPairs\",\n    \"DescribeLaunchTemplateVersions\": \"ec2:DescribeLaunchTemplateVersions\",\n    \"DescribeLaunchTemplates\": \"ec2:DescribeLaunchTemplates\",\n    \"DescribeLocalGatewayRouteTableVirtualInterfaceGroupAssociations\": \"ec2:DescribeLocalGatewayRouteTableVirtualInterfaceGroupAssociations\",\n    \"DescribeLocalGatewayRouteTableVpcAssociations\": \"ec2:DescribeLocalGatewayRouteTableVpcAssociations\",\n    \"DescribeLocalGatewayRouteTables\": \"ec2:DescribeLocalGatewayRouteTables\",\n    \"DescribeLocalGatewayVirtualInterfaceGroups\": \"ec2:DescribeLocalGatewayVirtualInterfaceGroups\",\n    \"DescribeLocalGatewayVirtualInterfaces\": \"ec2:DescribeLocalGatewayVirtualInterfaces\",\n    \"DescribeLocalGateways\": \"ec2:DescribeLocalGateways\",\n    \"DescribeManagedPrefixLists\": \"ec2:DescribeManagedPrefixLists\",\n    \"DescribeMovingAddresses\": \"ec2:DescribeMovingAddresses\",\n    \"DescribeNatGateways\": \"ec2:DescribeNatGateways\",\n    \"DescribeNetworkAcls\": \"ec2:DescribeNetworkAcls\",\n    \"DescribeNetworkInsightsAccessScopeAnalyses\": \"ec2:DescribeNetworkInsightsAccessScopeAnalyses\",\n    \"DescribeNetworkInsightsAccessScopes\": \"ec2:DescribeNetworkInsightsAccessScopes\",\n    \"DescribeNetworkInsightsAnalyses\": \"ec2:DescribeNetworkInsightsAnalyses\",\n    \"DescribeNetworkInsightsPaths\": \"ec2:DescribeNetworkInsightsPaths\",\n    \"DescribeNetworkInterfaceAttribute\": \"ec2:DescribeNetworkInterfaceAttribute\",\n    \"DescribeNetworkInterfacePermissions\": \"ec2:DescribeNetworkInterfacePermissions\",\n    \"DescribeNetworkInterfaces\": \"ec2:DescribeNetworkInterfaces\",\n    \"DescribePlacementGroups\": \"ec2:DescribePlacementGroups\",\n    \"DescribePrefixLists\": \"ec2:DescribePrefixLists\",\n    \"DescribePrincipalIdFormat\": \"ec2:DescribePrincipalIdFormat\",\n    \"DescribePublicIpv4Pools\": \"ec2:DescribePublicIpv4Pools\",\n    \"DescribeRegions\": \"ec2:DescribeRegions\",\n    \"DescribeReplaceRootVolumeTasks\": \"ec2:DescribeReplaceRootVolumeTasks\",\n    \"DescribeReservedInstances\": \"ec2:DescribeReservedInstances\",\n    \"DescribeReservedInstancesListings\": \"ec2:DescribeReservedInstancesListings\",\n    \"DescribeReservedInstancesModifications\": \"ec2:DescribeReservedInstancesModifications\",\n    \"DescribeReservedInstancesOfferings\": \"ec2:DescribeReservedInstancesOfferings\",\n    \"DescribeRouteTables\": \"ec2:DescribeRouteTables\",\n    \"DescribeScheduledInstanceAvailability\": \"ec2:DescribeScheduledInstanceAvailability\",\n    \"DescribeScheduledInstances\": \"ec2:DescribeScheduledInstances\",\n    \"DescribeSecurityGroupReferences\": \"ec2:DescribeSecurityGroupReferences\",\n    \"DescribeSecurityGroupRules\": \"ec2:DescribeSecurityGroupRules\",\n    \"DescribeSecurityGroups\": \"ec2:DescribeSecurityGroups\",\n    \"DescribeSnapshotAttribute\": \"ec2:DescribeSnapshotAttribute\",\n    \"DescribeSnapshotTierStatus\": \"ec2:DescribeSnapshotTierStatus\",\n    \"DescribeSnapshots\": \"ec2:DescribeSnapshots\",\n    \"DescribeSpotDatafeedSubscription\": \"ec2:DescribeSpotDatafeedSubscription\",\n    \"DescribeSpotFleetInstances\": \"ec2:DescribeSpotFleetInstances\",\n    \"DescribeSpotFleetRequestHistory\": \"ec2:DescribeSpotFleetRequestHistory\",\n    \"DescribeSpotFleetRequests\": \"ec2:DescribeSpotFleetRequests\",\n    \"DescribeSpotInstanceRequests\": \"ec2:DescribeSpotInstanceRequests\",\n    \"DescribeSpotPriceHistory\": \"ec2:DescribeSpotPriceHistory\",\n    \"DescribeStaleSecurityGroups\": \"ec2:DescribeStaleSecurityGroups\",\n    \"DescribeStoreImageTasks\": \"ec2:DescribeStoreImageTasks\",\n    \"DescribeSubnets\": \"ec2:DescribeSubnets\",\n    \"DescribeTags\": \"ec2:DescribeTags\",\n    \"DescribeTrafficMirrorFilters\": \"ec2:DescribeTrafficMirrorFilters\",\n    \"DescribeTrafficMirrorSessions\": \"ec2:DescribeTrafficMirrorSessions\",\n    \"DescribeTrafficMirrorTargets\": \"ec2:DescribeTrafficMirrorTargets\",\n    \"DescribeTransitGatewayAttachments\": \"ec2:DescribeTransitGatewayAttachments\",\n    \"DescribeTransitGatewayConnectPeers\": \"ec2:DescribeTransitGatewayConnectPeers\",\n    \"DescribeTransitGatewayConnects\": \"ec2:DescribeTransitGatewayConnects\",\n    \"DescribeTransitGatewayMulticastDomains\": \"ec2:DescribeTransitGatewayMulticastDomains\",\n    \"DescribeTransitGatewayPeeringAttachments\": \"ec2:DescribeTransitGatewayPeeringAttachments\",\n    \"DescribeTransitGatewayRouteTables\": \"ec2:DescribeTransitGatewayRouteTables\",\n    \"DescribeTransitGatewayVpcAttachments\": \"ec2:DescribeTransitGatewayVpcAttachments\",\n    \"DescribeTransitGateways\": \"ec2:DescribeTransitGateways\",\n    \"DescribeTrunkInterfaceAssociations\": \"ec2:DescribeTrunkInterfaceAssociations\",\n    \"DescribeVolumeAttribute\": \"ec2:DescribeVolumeAttribute\",\n    \"DescribeVolumeStatus\": \"ec2:DescribeVolumeStatus\",\n    \"DescribeVolumes\": \"ec2:DescribeVolumes\",\n    \"DescribeVolumesModifications\": \"ec2:DescribeVolumesModifications\",\n    \"DescribeVpcAttribute\": \"ec2:DescribeVpcAttribute\",\n    \"DescribeVpcClassicLink\": \"ec2:DescribeVpcClassicLink\",\n    \"DescribeVpcClassicLinkDnsSupport\": \"ec2:DescribeVpcClassicLinkDnsSupport\",\n    \"DescribeVpcEndpointConnectionNotifications\": \"ec2:DescribeVpcEndpointConnectionNotifications\",\n    \"DescribeVpcEndpointConnections\": \"ec2:DescribeVpcEndpointConnections\",\n    \"DescribeVpcEndpointServiceConfigurations\": \"ec2:DescribeVpcEndpointServiceConfigurations\",\n    \"DescribeVpcEndpointServicePermissions\": \"ec2:DescribeVpcEndpointServicePermissions\",\n    \"DescribeVpcEndpointServices\": \"ec2:DescribeVpcEndpointServices\",\n    \"DescribeVpcEndpoints\": \"ec2:DescribeVpcEndpoints\",\n    \"DescribeVpcPeeringConnections\": \"ec2:DescribeVpcPeeringConnections\",\n    \"DescribeVpcs\": \"ec2:DescribeVpcs\",\n    \"DescribeVpnConnections\": \"ec2:DescribeVpnConnections\",\n    \"DescribeVpnGateways\": \"ec2:DescribeVpnGateways\",\n    \"DetachClassicLinkVpc\": \"ec2:DetachClassicLinkVpc\",\n    \"DetachInternetGateway\": \"ec2:DetachInternetGateway\",\n    \"DetachNetworkInterface\": \"ec2:DetachNetworkInterface\",\n    \"DetachVolume\": \"ec2:DetachVolume\",\n    \"DetachVpnGateway\": \"ec2:DetachVpnGateway\",\n    \"DisableEbsEncryptionByDefault\": \"ec2:DisableEbsEncryptionByDefault\",\n    \"DisableFastSnapshotRestores\": \"ec2:DisableFastSnapshotRestores\",\n    \"DisableImageDeprecation\": \"ec2:DisableImageDeprecation\",\n    \"DisableIpamOrganizationAdminAccount\": \"ec2:DisableIpamOrganizationAdminAccount\",\n    \"DisableSerialConsoleAccess\": \"ec2:DisableSerialConsoleAccess\",\n    \"DisableTransitGatewayRouteTablePropagation\": \"ec2:DisableTransitGatewayRouteTablePropagation\",\n    \"DisableVgwRoutePropagation\": \"ec2:DisableVgwRoutePropagation\",\n    \"DisableVpcClassicLink\": \"ec2:DisableVpcClassicLink\",\n    \"DisableVpcClassicLinkDnsSupport\": \"ec2:DisableVpcClassicLinkDnsSupport\",\n    \"DisassociateAddress\": \"ec2:DisassociateAddress\",\n    \"DisassociateClientVpnTargetNetwork\": \"ec2:DisassociateClientVpnTargetNetwork\",\n    \"DisassociateEnclaveCertificateIamRole\": \"ec2:DisassociateEnclaveCertificateIamRole\",\n    \"DisassociateIamInstanceProfile\": \"ec2:DisassociateIamInstanceProfile\",\n    \"DisassociateInstanceEventWindow\": \"ec2:DisassociateInstanceEventWindow\",\n    \"DisassociateRouteTable\": \"ec2:DisassociateRouteTable\",\n    \"DisassociateSubnetCidrBlock\": \"ec2:DisassociateSubnetCidrBlock\",\n    \"DisassociateTransitGatewayMulticastDomain\": \"ec2:DisassociateTransitGatewayMulticastDomain\",\n    \"DisassociateTransitGatewayRouteTable\": \"ec2:DisassociateTransitGatewayRouteTable\",\n    \"DisassociateTrunkInterface\": \"ec2:DisassociateTrunkInterface\",\n    \"DisassociateVpcCidrBlock\": \"ec2:DisassociateVpcCidrBlock\",\n    \"EnableEbsEncryptionByDefault\": \"ec2:EnableEbsEncryptionByDefault\",\n    \"EnableFastSnapshotRestores\": \"ec2:EnableFastSnapshotRestores\",\n    \"EnableImageDeprecation\": \"ec2:EnableImageDeprecation\",\n    \"EnableIpamOrganizationAdminAccount\": \"ec2:EnableIpamOrganizationAdminAccount\",\n    \"EnableSerialConsoleAccess\": \"ec2:EnableSerialConsoleAccess\",\n    \"EnableTransitGatewayRouteTablePropagation\": \"ec2:EnableTransitGatewayRouteTablePropagation\",\n    \"EnableVgwRoutePropagation\": \"ec2:EnableVgwRoutePropagation\",\n    \"EnableVolumeIO\": \"ec2:EnableVolumeIO\",\n    \"EnableVpcClassicLink\": \"ec2:EnableVpcClassicLink\",\n    \"EnableVpcClassicLinkDnsSupport\": \"ec2:EnableVpcClassicLinkDnsSupport\",\n    \"ExportClientVpnClientCertificateRevocationList\": \"ec2:ExportClientVpnClientCertificateRevocationList\",\n    \"ExportClientVpnClientConfiguration\": \"ec2:ExportClientVpnClientConfiguration\",\n    \"ExportImage\": \"ec2:ExportImage\",\n    \"ExportTransitGatewayRoutes\": \"ec2:ExportTransitGatewayRoutes\",\n    \"GetAssociatedEnclaveCertificateIamRoles\": \"ec2:GetAssociatedEnclaveCertificateIamRoles\",\n    \"GetAssociatedIpv6PoolCidrs\": \"ec2:GetAssociatedIpv6PoolCidrs\",\n    \"GetCapacityReservationUsage\": \"ec2:GetCapacityReservationUsage\",\n    \"GetCoipPoolUsage\": \"ec2:GetCoipPoolUsage\",\n    \"GetConsoleOutput\": \"ec2:GetConsoleOutput\",\n    \"GetConsoleScreenshot\": \"ec2:GetConsoleScreenshot\",\n    \"GetDefaultCreditSpecification\": \"ec2:GetDefaultCreditSpecification\",\n    \"GetEbsDefaultKmsKeyId\": \"ec2:GetEbsDefaultKmsKeyId\",\n    \"GetEbsEncryptionByDefault\": \"ec2:GetEbsEncryptionByDefault\",\n    \"GetFlowLogsIntegrationTemplate\": \"ec2:GetFlowLogsIntegrationTemplate\",\n    \"GetGroupsForCapacityReservation\": \"ec2:GetGroupsForCapacityReservation\",\n    \"GetHostReservationPurchasePreview\": \"ec2:GetHostReservationPurchasePreview\",\n    \"GetInstanceTypesFromInstanceRequirements\": \"ec2:GetInstanceTypesFromInstanceRequirements\",\n    \"GetIpamAddressHistory\": \"ec2:GetIpamAddressHistory\",\n    \"GetIpamPoolAllocations\": \"ec2:GetIpamPoolAllocations\",\n    \"GetIpamPoolCidrs\": \"ec2:GetIpamPoolCidrs\",\n    \"GetIpamResourceCidrs\": \"ec2:GetIpamResourceCidrs\",\n    \"GetLaunchTemplateData\": \"ec2:GetLaunchTemplateData\",\n    \"GetManagedPrefixListAssociations\": \"ec2:GetManagedPrefixListAssociations\",\n    \"GetManagedPrefixListEntries\": \"ec2:GetManagedPrefixListEntries\",\n    \"GetNetworkInsightsAccessScopeAnalysisFindings\": \"ec2:GetNetworkInsightsAccessScopeAnalysisFindings\",\n    \"GetNetworkInsightsAccessScopeContent\": \"ec2:GetNetworkInsightsAccessScopeContent\",\n    \"GetPasswordData\": \"ec2:GetPasswordData\",\n    \"GetReservedInstancesExchangeQuote\": \"ec2:GetReservedInstancesExchangeQuote\",\n    \"GetSerialConsoleAccessStatus\": \"ec2:GetSerialConsoleAccessStatus\",\n    \"GetSpotPlacementScores\": \"ec2:GetSpotPlacementScores\",\n    \"GetSubnetCidrReservations\": \"ec2:GetSubnetCidrReservations\",\n    \"GetTransitGatewayAttachmentPropagations\": \"ec2:GetTransitGatewayAttachmentPropagations\",\n    \"GetTransitGatewayMulticastDomainAssociations\": \"ec2:GetTransitGatewayMulticastDomainAssociations\",\n    \"GetTransitGatewayPrefixListReferences\": \"ec2:GetTransitGatewayPrefixListReferences\",\n    \"GetTransitGatewayRouteTableAssociations\": \"ec2:GetTransitGatewayRouteTableAssociations\",\n    \"GetTransitGatewayRouteTablePropagations\": \"ec2:GetTransitGatewayRouteTablePropagations\",\n    \"GetVpnConnectionDeviceSampleConfiguration\": \"ec2:GetVpnConnectionDeviceSampleConfiguration\",\n    \"GetVpnConnectionDeviceTypes\": \"ec2:GetVpnConnectionDeviceTypes\",\n    \"ImportClientVpnClientCertificateRevocationList\": \"ec2:ImportClientVpnClientCertificateRevocationList\",\n    \"ImportImage\": \"ec2:ImportImage\",\n    \"ImportInstance\": \"ec2:ImportInstance\",\n    \"ImportKeyPair\": \"ec2:ImportKeyPair\",\n    \"ImportSnapshot\": \"ec2:ImportSnapshot\",\n    \"ImportVolume\": \"ec2:ImportVolume\",\n    \"ListSnapshotsInRecycleBin\": \"ec2:ListSnapshotsInRecycleBin\",\n    \"ModifyAddressAttribute\": \"ec2:ModifyAddressAttribute\",\n    \"ModifyAvailabilityZoneGroup\": \"ec2:ModifyAvailabilityZoneGroup\",\n    \"ModifyCapacityReservation\": \"ec2:ModifyCapacityReservation\",\n    \"ModifyCapacityReservationFleet\": \"ec2:ModifyCapacityReservationFleet\",\n    \"ModifyClientVpnEndpoint\": \"ec2:ModifyClientVpnEndpoint\",\n    \"ModifyDefaultCreditSpecification\": \"ec2:ModifyDefaultCreditSpecification\",\n    \"ModifyEbsDefaultKmsKeyId\": \"ec2:ModifyEbsDefaultKmsKeyId\",\n    \"ModifyFleet\": \"ec2:ModifyFleet\",\n    \"ModifyFpgaImageAttribute\": \"ec2:ModifyFpgaImageAttribute\",\n    \"ModifyHosts\": \"ec2:ModifyHosts\",\n    \"ModifyIdFormat\": \"ec2:ModifyIdFormat\",\n    \"ModifyIdentityIdFormat\": \"ec2:ModifyIdentityIdFormat\",\n    \"ModifyImageAttribute\": \"ec2:ModifyImageAttribute\",\n    \"ModifyInstanceAttribute\": \"ec2:ModifyInstanceAttribute\",\n    \"ModifyInstanceCapacityReservationAttributes\": \"ec2:ModifyInstanceCapacityReservationAttributes\",\n    \"ModifyInstanceCreditSpecification\": \"ec2:ModifyInstanceCreditSpecification\",\n    \"ModifyInstanceEventStartTime\": \"ec2:ModifyInstanceEventStartTime\",\n    \"ModifyInstanceEventWindow\": \"ec2:ModifyInstanceEventWindow\",\n    \"ModifyInstanceMetadataOptions\": \"ec2:ModifyInstanceMetadataOptions\",\n    \"ModifyInstancePlacement\": \"ec2:ModifyInstancePlacement\",\n    \"ModifyIpam\": \"ec2:ModifyIpam\",\n    \"ModifyIpamPool\": \"ec2:ModifyIpamPool\",\n    \"ModifyIpamResourceCidr\": \"ec2:ModifyIpamResourceCidr\",\n    \"ModifyIpamScope\": \"ec2:ModifyIpamScope\",\n    \"ModifyLaunchTemplate\": \"ec2:ModifyLaunchTemplate\",\n    \"ModifyManagedPrefixList\": \"ec2:ModifyManagedPrefixList\",\n    \"ModifyNetworkInterfaceAttribute\": \"ec2:ModifyNetworkInterfaceAttribute\",\n    \"ModifyReservedInstances\": \"ec2:ModifyReservedInstances\",\n    \"ModifySecurityGroupRules\": \"ec2:ModifySecurityGroupRules\",\n    \"ModifySnapshotAttribute\": \"ec2:ModifySnapshotAttribute\",\n    \"ModifySnapshotTier\": \"ec2:ModifySnapshotTier\",\n    \"ModifySpotFleetRequest\": \"ec2:ModifySpotFleetRequest\",\n    \"ModifySubnetAttribute\": \"ec2:ModifySubnetAttribute\",\n    \"ModifyTrafficMirrorFilterNetworkServices\": \"ec2:ModifyTrafficMirrorFilterNetworkServices\",\n    \"ModifyTrafficMirrorFilterRule\": \"ec2:ModifyTrafficMirrorFilterRule\",\n    \"ModifyTrafficMirrorSession\": \"ec2:ModifyTrafficMirrorSession\",\n    \"ModifyTransitGateway\": \"ec2:ModifyTransitGateway\",\n    \"ModifyTransitGatewayPrefixListReference\": \"ec2:ModifyTransitGatewayPrefixListReference\",\n    \"ModifyTransitGatewayVpcAttachment\": \"ec2:ModifyTransitGatewayVpcAttachment\",\n    \"ModifyVolume\": \"ec2:ModifyVolume\",\n    \"ModifyVolumeAttribute\": \"ec2:ModifyVolumeAttribute\",\n    \"ModifyVpcAttribute\": \"ec2:ModifyVpcAttribute\",\n    \"ModifyVpcEndpoint\": \"ec2:ModifyVpcEndpoint\",\n    \"ModifyVpcEndpointConnectionNotification\": \"ec2:ModifyVpcEndpointConnectionNotification\",\n    \"ModifyVpcEndpointServiceConfiguration\": \"ec2:ModifyVpcEndpointServiceConfiguration\",\n    \"ModifyVpcEndpointServicePermissions\": \"ec2:ModifyVpcEndpointServicePermissions\",\n    \"ModifyVpcPeeringConnectionOptions\": \"ec2:ModifyVpcPeeringConnectionOptions\",\n    \"ModifyVpcTenancy\": \"ec2:ModifyVpcTenancy\",\n    \"ModifyVpnConnection\": \"ec2:ModifyVpnConnection\",\n    \"ModifyVpnConnectionOptions\": \"ec2:ModifyVpnConnectionOptions\",\n    \"ModifyVpnTunnelCertificate\": \"ec2:ModifyVpnTunnelCertificate\",\n    \"ModifyVpnTunnelOptions\": \"ec2:ModifyVpnTunnelOptions\",\n    \"MonitorInstances\": \"ec2:MonitorInstances\",\n    \"MoveAddressToVpc\": \"ec2:MoveAddressToVpc\",\n    \"MoveByoipCidrToIpam\": \"ec2:MoveByoipCidrToIpam\",\n    \"ProvisionByoipCidr\": \"ec2:ProvisionByoipCidr\",\n    \"ProvisionIpamPoolCidr\": \"ec2:ProvisionIpamPoolCidr\",\n    \"ProvisionPublicIpv4PoolCidr\": \"ec2:ProvisionPublicIpv4PoolCidr\",\n    \"PurchaseHostReservation\": \"ec2:PurchaseHostReservation\",\n    \"PurchaseReservedInstancesOffering\": \"ec2:PurchaseReservedInstancesOffering\",\n    \"PurchaseScheduledInstances\": \"ec2:PurchaseScheduledInstances\",\n    \"RebootInstances\": \"ec2:RebootInstances\",\n    \"RegisterImage\": \"ec2:RegisterImage\",\n    \"RegisterInstanceEventNotificationAttributes\": \"ec2:RegisterInstanceEventNotificationAttributes\",\n    \"RegisterTransitGatewayMulticastGroupMembers\": \"ec2:RegisterTransitGatewayMulticastGroupMembers\",\n    \"RegisterTransitGatewayMulticastGroupSources\": \"ec2:RegisterTransitGatewayMulticastGroupSources\",\n    \"RejectTransitGatewayMulticastDomainAssociations\": \"ec2:RejectTransitGatewayMulticastDomainAssociations\",\n    \"RejectTransitGatewayPeeringAttachment\": \"ec2:RejectTransitGatewayPeeringAttachment\",\n    \"RejectTransitGatewayVpcAttachment\": \"ec2:RejectTransitGatewayVpcAttachment\",\n    \"RejectVpcEndpointConnections\": \"ec2:RejectVpcEndpointConnections\",\n    \"RejectVpcPeeringConnection\": \"ec2:RejectVpcPeeringConnection\",\n    \"ReleaseAddress\": \"ec2:ReleaseAddress\",\n    \"ReleaseHosts\": \"ec2:ReleaseHosts\",\n    \"ReleaseIpamPoolAllocation\": \"ec2:ReleaseIpamPoolAllocation\",\n    \"ReplaceIamInstanceProfileAssociation\": \"ec2:ReplaceIamInstanceProfileAssociation\",\n    \"ReplaceNetworkAclAssociation\": \"ec2:ReplaceNetworkAclAssociation\",\n    \"ReplaceNetworkAclEntry\": \"ec2:ReplaceNetworkAclEntry\",\n    \"ReplaceRoute\": \"ec2:ReplaceRoute\",\n    \"ReplaceRouteTableAssociation\": \"ec2:ReplaceRouteTableAssociation\",\n    \"ReplaceTransitGatewayRoute\": \"ec2:ReplaceTransitGatewayRoute\",\n    \"ReportInstanceStatus\": \"ec2:ReportInstanceStatus\",\n    \"RequestSpotFleet\": \"ec2:RequestSpotFleet\",\n    \"RequestSpotInstances\": \"ec2:RequestSpotInstances\",\n    \"ResetAddressAttribute\": \"ec2:ResetAddressAttribute\",\n    \"ResetEbsDefaultKmsKeyId\": \"ec2:ResetEbsDefaultKmsKeyId\",\n    \"ResetFpgaImageAttribute\": \"ec2:ResetFpgaImageAttribute\",\n    \"ResetImageAttribute\": \"ec2:ResetImageAttribute\",\n    \"ResetInstanceAttribute\": \"ec2:ResetInstanceAttribute\",\n    \"ResetNetworkInterfaceAttribute\": \"ec2:ResetNetworkInterfaceAttribute\",\n    \"ResetSnapshotAttribute\": \"ec2:ResetSnapshotAttribute\",\n    \"RestoreAddressToClassic\": \"ec2:RestoreAddressToClassic\",\n    \"RestoreManagedPrefixListVersion\": \"ec2:RestoreManagedPrefixListVersion\",\n    \"RestoreSnapshotFromRecycleBin\": \"ec2:RestoreSnapshotFromRecycleBin\",\n    \"RestoreSnapshotTier\": \"ec2:RestoreSnapshotTier\",\n    \"RevokeClientVpnIngress\": \"ec2:RevokeClientVpnIngress\",\n    \"RevokeSecurityGroupEgress\": \"ec2:RevokeSecurityGroupEgress\",\n    \"RevokeSecurityGroupIngress\": \"ec2:RevokeSecurityGroupIngress\",\n    \"RunInstances\": \"ec2:RunInstances\",\n    \"RunScheduledInstances\": \"ec2:RunScheduledInstances\",\n    \"SearchLocalGatewayRoutes\": \"ec2:SearchLocalGatewayRoutes\",\n    \"SearchTransitGatewayMulticastGroups\": \"ec2:SearchTransitGatewayMulticastGroups\",\n    \"SearchTransitGatewayRoutes\": \"ec2:SearchTransitGatewayRoutes\",\n    \"SendDiagnosticInterrupt\": \"ec2:SendDiagnosticInterrupt\",\n    \"StartInstances\": \"ec2:StartInstances\",\n    \"StartNetworkInsightsAccessScopeAnalysis\": \"ec2:StartNetworkInsightsAccessScopeAnalysis\",\n    \"StartNetworkInsightsAnalysis\": \"ec2:StartNetworkInsightsAnalysis\",\n    \"StartVpcEndpointServicePrivateDnsVerification\": \"ec2:StartVpcEndpointServicePrivateDnsVerification\",\n    \"StopInstances\": \"ec2:StopInstances\",\n    \"TerminateClientVpnConnections\": \"ec2:TerminateClientVpnConnections\",\n    \"TerminateInstances\": \"ec2:TerminateInstances\",\n    \"UnassignIpv6Addresses\": \"ec2:UnassignIpv6Addresses\",\n    \"UnassignPrivateIpAddresses\": \"ec2:UnassignPrivateIpAddresses\",\n    \"UnmonitorInstances\": \"ec2:UnmonitorInstances\",\n    \"UpdateSecurityGroupRuleDescriptionsEgress\": \"ec2:UpdateSecurityGroupRuleDescriptionsEgress\",\n    \"UpdateSecurityGroupRuleDescriptionsIngress\": \"ec2:UpdateSecurityGroupRuleDescriptionsIngress\",\n    \"WithdrawByoipCidr\": \"ec2:WithdrawByoipCidr\"\n  },\n  \"ec2-instance-connect\": {\n    \"SendSSHPublicKey\": \"ec2-instance-connect:SendSSHPublicKey\",\n    \"SendSerialConsoleSSHPublicKey\": \"ec2-instance-connect:SendSerialConsoleSSHPublicKey\"\n  },\n  \"ecr\": {\n    \"BatchCheckLayerAvailability\": \"ecr:BatchCheckLayerAvailability\",\n    \"BatchDeleteImage\": \"ecr:BatchDeleteImage\",\n    \"BatchGetImage\": \"ecr:BatchGetImage\",\n    \"BatchGetRepositoryScanningConfiguration\": \"ecr:BatchGetRepositoryScanningConfiguration\",\n    \"CompleteLayerUpload\": \"ecr:CompleteLayerUpload\",\n    \"CreatePullThroughCacheRule\": \"ecr:CreatePullThroughCacheRule\",\n    \"CreateRepository\": \"ecr:CreateRepository\",\n    \"DeleteLifecyclePolicy\": \"ecr:DeleteLifecyclePolicy\",\n    \"DeletePullThroughCacheRule\": \"ecr:DeletePullThroughCacheRule\",\n    \"DeleteRegistryPolicy\": \"ecr:DeleteRegistryPolicy\",\n    \"DeleteRepository\": \"ecr:DeleteRepository\",\n    \"DeleteRepositoryPolicy\": \"ecr:DeleteRepositoryPolicy\",\n    \"DescribeImageReplicationStatus\": \"ecr:DescribeImageReplicationStatus\",\n    \"DescribeImageScanFindings\": \"ecr:DescribeImageScanFindings\",\n    \"DescribeImages\": \"ecr:DescribeImages\",\n    \"DescribePullThroughCacheRules\": \"ecr:DescribePullThroughCacheRules\",\n    \"DescribeRegistry\": \"ecr:DescribeRegistry\",\n    \"DescribeRepositories\": \"ecr:DescribeRepositories\",\n    \"GetAuthorizationToken\": \"ecr:GetAuthorizationToken\",\n    \"GetDownloadUrlForLayer\": \"ecr:GetDownloadUrlForLayer\",\n    \"GetLifecyclePolicy\": \"ecr:GetLifecyclePolicy\",\n    \"GetLifecyclePolicyPreview\": \"ecr:GetLifecyclePolicyPreview\",\n    \"GetRegistryPolicy\": \"ecr:GetRegistryPolicy\",\n    \"GetRegistryScanningConfiguration\": \"ecr:GetRegistryScanningConfiguration\",\n    \"GetRepositoryPolicy\": \"ecr:GetRepositoryPolicy\",\n    \"InitiateLayerUpload\": \"ecr:InitiateLayerUpload\",\n    \"ListImages\": \"ecr:ListImages\",\n    \"ListTagsForResource\": \"ecr:ListTagsForResource\",\n    \"PutImage\": \"ecr:PutImage\",\n    \"PutImageScanningConfiguration\": \"ecr:PutImageScanningConfiguration\",\n    \"PutImageTagMutability\": \"ecr:PutImageTagMutability\",\n    \"PutLifecyclePolicy\": \"ecr:PutLifecyclePolicy\",\n    \"PutRegistryPolicy\": \"ecr:PutRegistryPolicy\",\n    \"PutRegistryScanningConfiguration\": \"ecr:PutRegistryScanningConfiguration\",\n    \"PutReplicationConfiguration\": \"ecr:PutReplicationConfiguration\",\n    \"SetRepositoryPolicy\": \"ecr:SetRepositoryPolicy\",\n    \"StartImageScan\": \"ecr:StartImageScan\",\n    \"StartLifecyclePolicyPreview\": \"ecr:StartLifecyclePolicyPreview\",\n    \"TagResource\": \"ecr:TagResource\",\n    \"UntagResource\": \"ecr:UntagResource\",\n    \"UploadLayerPart\": \"ecr:UploadLayerPart\"\n  },\n  \"ecr-public\": {\n    \"BatchCheckLayerAvailability\": \"ecr-public:BatchCheckLayerAvailability\",\n    \"BatchDeleteImage\": \"ecr-public:BatchDeleteImage\",\n    \"CompleteLayerUpload\": \"ecr-public:CompleteLayerUpload\",\n    \"CreateRepository\": \"ecr-public:CreateRepository\",\n    \"DeleteRepository\": \"ecr-public:DeleteRepository\",\n    \"DeleteRepositoryPolicy\": \"ecr-public:DeleteRepositoryPolicy\",\n    \"DescribeImageTags\": \"ecr-public:DescribeImageTags\",\n    \"DescribeImages\": \"ecr-public:DescribeImages\",\n    \"DescribeRegistries\": \"ecr-public:DescribeRegistries\",\n    \"DescribeRepositories\": \"ecr-public:DescribeRepositories\",\n    \"GetAuthorizationToken\": \"ecr-public:GetAuthorizationToken\",\n    \"GetRegistryCatalogData\": \"ecr-public:GetRegistryCatalogData\",\n    \"GetRepositoryCatalogData\": \"ecr-public:GetRepositoryCatalogData\",\n    \"GetRepositoryPolicy\": \"ecr-public:GetRepositoryPolicy\",\n    \"InitiateLayerUpload\": \"ecr-public:InitiateLayerUpload\",\n    \"ListTagsForResource\": \"ecr-public:ListTagsForResource\",\n    \"PutImage\": \"ecr-public:PutImage\",\n    \"PutRegistryCatalogData\": \"ecr-public:PutRegistryCatalogData\",\n    \"PutRepositoryCatalogData\": \"ecr-public:PutRepositoryCatalogData\",\n    \"SetRepositoryPolicy\": \"ecr-public:SetRepositoryPolicy\",\n    \"TagResource\": \"ecr-public:TagResource\",\n    \"UntagResource\": \"ecr-public:UntagResource\",\n    \"UploadLayerPart\": \"ecr-public:UploadLayerPart\"\n  },\n  \"ecs\": {\n    \"CreateCapacityProvider\": \"ecs:CreateCapacityProvider\",\n    \"CreateCluster\": \"ecs:CreateCluster\",\n    \"CreateService\": \"ecs:CreateService\",\n    \"CreateTaskSet\": \"ecs:CreateTaskSet\",\n    \"DeleteAccountSetting\": \"ecs:DeleteAccountSetting\",\n    \"DeleteAttributes\": \"ecs:DeleteAttributes\",\n    \"DeleteCapacityProvider\": \"ecs:DeleteCapacityProvider\",\n    \"DeleteCluster\": \"ecs:DeleteCluster\",\n    \"DeleteService\": \"ecs:DeleteService\",\n    \"DeleteTaskSet\": \"ecs:DeleteTaskSet\",\n    \"DeregisterContainerInstance\": \"ecs:DeregisterContainerInstance\",\n    \"DeregisterTaskDefinition\": \"ecs:DeregisterTaskDefinition\",\n    \"DescribeCapacityProviders\": \"ecs:DescribeCapacityProviders\",\n    \"DescribeClusters\": \"ecs:DescribeClusters\",\n    \"DescribeContainerInstances\": \"ecs:DescribeContainerInstances\",\n    \"DescribeServices\": \"ecs:DescribeServices\",\n    \"DescribeTaskDefinition\": \"ecs:DescribeTaskDefinition\",\n    \"DescribeTaskSets\": \"ecs:DescribeTaskSets\",\n    \"DescribeTasks\": \"ecs:DescribeTasks\",\n    \"DiscoverPollEndpoint\": \"ecs:DiscoverPollEndpoint\",\n    \"ExecuteCommand\": \"ecs:ExecuteCommand\",\n    \"ListAccountSettings\": \"ecs:ListAccountSettings\",\n    \"ListAttributes\": \"ecs:ListAttributes\",\n    \"ListClusters\": \"ecs:ListClusters\",\n    \"ListContainerInstances\": \"ecs:ListContainerInstances\",\n    \"ListServices\": \"ecs:ListServices\",\n    \"ListTagsForResource\": \"ecs:ListTagsForResource\",\n    \"ListTaskDefinitionFamilies\": \"ecs:ListTaskDefinitionFamilies\",\n    \"ListTaskDefinitions\": \"ecs:ListTaskDefinitions\",\n    \"ListTasks\": \"ecs:ListTasks\",\n    \"PutAccountSetting\": \"ecs:PutAccountSetting\",\n    \"PutAccountSettingDefault\": \"ecs:PutAccountSettingDefault\",\n    \"PutAttributes\": \"ecs:PutAttributes\",\n    \"PutClusterCapacityProviders\": \"ecs:PutClusterCapacityProviders\",\n    \"RegisterContainerInstance\": \"ecs:RegisterContainerInstance\",\n    \"RegisterTaskDefinition\": \"ecs:RegisterTaskDefinition\",\n    \"RunTask\": \"ecs:RunTask\",\n    \"StartTask\": \"ecs:StartTask\",\n    \"StopTask\": \"ecs:StopTask\",\n    \"SubmitAttachmentStateChanges\": \"ecs:SubmitAttachmentStateChanges\",\n    \"SubmitContainerStateChange\": \"ecs:SubmitContainerStateChange\",\n    \"SubmitTaskStateChange\": \"ecs:SubmitTaskStateChange\",\n    \"TagResource\": \"ecs:TagResource\",\n    \"UntagResource\": \"ecs:UntagResource\",\n    \"UpdateCapacityProvider\": \"ecs:UpdateCapacityProvider\",\n    \"UpdateCluster\": \"ecs:UpdateCluster\",\n    \"UpdateClusterSettings\": \"ecs:UpdateClusterSettings\",\n    \"UpdateContainerAgent\": \"ecs:UpdateContainerAgent\",\n    \"UpdateContainerInstancesState\": \"ecs:UpdateContainerInstancesState\",\n    \"UpdateService\": \"ecs:UpdateService\",\n    \"UpdateServicePrimaryTaskSet\": \"ecs:UpdateServicePrimaryTaskSet\",\n    \"UpdateTaskSet\": \"ecs:UpdateTaskSet\"\n  },\n  \"efs\": {\n    \"CreateAccessPoint\": \"elasticfilesystem:CreateAccessPoint\",\n    \"CreateFileSystem\": \"elasticfilesystem:CreateFileSystem\",\n    \"CreateMountTarget\": \"elasticfilesystem:CreateMountTarget\",\n    \"CreateTags\": \"elasticfilesystem:CreateTags\",\n    \"DeleteAccessPoint\": \"elasticfilesystem:DeleteAccessPoint\",\n    \"DeleteFileSystem\": \"elasticfilesystem:DeleteFileSystem\",\n    \"DeleteFileSystemPolicy\": \"elasticfilesystem:DeleteFileSystemPolicy\",\n    \"DeleteMountTarget\": \"elasticfilesystem:DeleteMountTarget\",\n    \"DeleteTags\": \"elasticfilesystem:DeleteTags\",\n    \"DescribeAccessPoints\": \"elasticfilesystem:DescribeAccessPoints\",\n    \"DescribeAccountPreferences\": \"elasticfilesystem:DescribeAccountPreferences\",\n    \"DescribeBackupPolicy\": \"elasticfilesystem:DescribeBackupPolicy\",\n    \"DescribeFileSystemPolicy\": \"elasticfilesystem:DescribeFileSystemPolicy\",\n    \"DescribeFileSystems\": \"elasticfilesystem:DescribeFileSystems\",\n    \"DescribeLifecycleConfiguration\": \"elasticfilesystem:DescribeLifecycleConfiguration\",\n    \"DescribeMountTargetSecurityGroups\": \"elasticfilesystem:DescribeMountTargetSecurityGroups\",\n    \"DescribeMountTargets\": \"elasticfilesystem:DescribeMountTargets\",\n    \"DescribeTags\": \"elasticfilesystem:DescribeTags\",\n    \"ListTagsForResource\": \"elasticfilesystem:ListTagsForResource\",\n    \"ModifyMountTargetSecurityGroups\": \"elasticfilesystem:ModifyMountTargetSecurityGroups\",\n    \"PutAccountPreferences\": \"elasticfilesystem:PutAccountPreferences\",\n    \"PutBackupPolicy\": \"elasticfilesystem:PutBackupPolicy\",\n    \"PutFileSystemPolicy\": \"elasticfilesystem:PutFileSystemPolicy\",\n    \"PutLifecycleConfiguration\": \"elasticfilesystem:PutLifecycleConfiguration\",\n    \"TagResource\": \"elasticfilesystem:TagResource\",\n    \"UntagResource\": \"elasticfilesystem:UntagResource\",\n    \"UpdateFileSystem\": \"elasticfilesystem:UpdateFileSystem\"\n  },\n  \"eks\": {\n    \"AssociateEncryptionConfig\": \"eks:AssociateEncryptionConfig\",\n    \"AssociateIdentityProviderConfig\": \"eks:AssociateIdentityProviderConfig\",\n    \"CreateAddon\": \"eks:CreateAddon\",\n    \"CreateCluster\": \"eks:CreateCluster\",\n    \"CreateFargateProfile\": \"eks:CreateFargateProfile\",\n    \"CreateNodegroup\": \"eks:CreateNodegroup\",\n    \"DeleteAddon\": \"eks:DeleteAddon\",\n    \"DeleteCluster\": \"eks:DeleteCluster\",\n    \"DeleteFargateProfile\": \"eks:DeleteFargateProfile\",\n    \"DeleteNodegroup\": \"eks:DeleteNodegroup\",\n    \"DescribeAddon\": \"eks:DescribeAddon\",\n    \"DescribeAddonVersions\": \"eks:DescribeAddonVersions\",\n    \"DescribeCluster\": \"eks:DescribeCluster\",\n    \"DescribeFargateProfile\": \"eks:DescribeFargateProfile\",\n    \"DescribeIdentityProviderConfig\": \"eks:DescribeIdentityProviderConfig\",\n    \"DescribeNodegroup\": \"eks:DescribeNodegroup\",\n    \"DescribeUpdate\": \"eks:DescribeUpdate\",\n    \"DisassociateIdentityProviderConfig\": \"eks:DisassociateIdentityProviderConfig\",\n    \"ListAddons\": \"eks:ListAddons\",\n    \"ListClusters\": \"eks:ListClusters\",\n    \"ListFargateProfiles\": \"eks:ListFargateProfiles\",\n    \"ListIdentityProviderConfigs\": \"eks:ListIdentityProviderConfigs\",\n    \"ListNodegroups\": \"eks:ListNodegroups\",\n    \"ListTagsForResource\": \"eks:ListTagsForResource\",\n    \"ListUpdates\": \"eks:ListUpdates\",\n    \"TagResource\": \"eks:TagResource\",\n    \"UntagResource\": \"eks:UntagResource\",\n    \"UpdateAddon\": \"eks:UpdateAddon\",\n    \"UpdateClusterConfig\": \"eks:UpdateClusterConfig\",\n    \"UpdateClusterVersion\": \"eks:UpdateClusterVersion\",\n    \"UpdateNodegroupConfig\": \"eks:UpdateNodegroupConfig\",\n    \"UpdateNodegroupVersion\": \"eks:UpdateNodegroupVersion\"\n  },\n  \"elastic-inference\": {\n    \"DescribeAcceleratorOfferings\": \"elastic-inference:DescribeAcceleratorOfferings\",\n    \"DescribeAcceleratorTypes\": \"elastic-inference:DescribeAcceleratorTypes\",\n    \"DescribeAccelerators\": \"elastic-inference:DescribeAccelerators\",\n    \"ListTagsForResource\": \"elastic-inference:ListTagsForResource\",\n    \"TagResource\": \"elastic-inference:TagResource\",\n    \"UntagResource\": \"elastic-inference:UntagResource\"\n  },\n  \"elasticache\": {\n    \"AddTagsToResource\": \"elasticache:AddTagsToResource\",\n    \"AuthorizeCacheSecurityGroupIngress\": \"elasticache:AuthorizeCacheSecurityGroupIngress\",\n    \"BatchApplyUpdateAction\": \"elasticache:BatchApplyUpdateAction\",\n    \"BatchStopUpdateAction\": \"elasticache:BatchStopUpdateAction\",\n    \"CompleteMigration\": \"elasticache:CompleteMigration\",\n    \"CopySnapshot\": \"elasticache:CopySnapshot\",\n    \"CreateCacheCluster\": \"elasticache:CreateCacheCluster\",\n    \"CreateCacheParameterGroup\": \"elasticache:CreateCacheParameterGroup\",\n    \"CreateCacheSecurityGroup\": \"elasticache:CreateCacheSecurityGroup\",\n    \"CreateCacheSubnetGroup\": \"elasticache:CreateCacheSubnetGroup\",\n    \"CreateGlobalReplicationGroup\": \"elasticache:CreateGlobalReplicationGroup\",\n    \"CreateReplicationGroup\": \"elasticache:CreateReplicationGroup\",\n    \"CreateSnapshot\": \"elasticache:CreateSnapshot\",\n    \"CreateUser\": \"elasticache:CreateUser\",\n    \"CreateUserGroup\": \"elasticache:CreateUserGroup\",\n    \"DecreaseNodeGroupsInGlobalReplicationGroup\": \"elasticache:DecreaseNodeGroupsInGlobalReplicationGroup\",\n    \"DecreaseReplicaCount\": \"elasticache:DecreaseReplicaCount\",\n    \"DeleteCacheCluster\": \"elasticache:DeleteCacheCluster\",\n    \"DeleteCacheParameterGroup\": \"elasticache:DeleteCacheParameterGroup\",\n    \"DeleteCacheSecurityGroup\": \"elasticache:DeleteCacheSecurityGroup\",\n    \"DeleteCacheSubnetGroup\": \"elasticache:DeleteCacheSubnetGroup\",\n    \"DeleteGlobalReplicationGroup\": \"elasticache:DeleteGlobalReplicationGroup\",\n    \"DeleteReplicationGroup\": \"elasticache:DeleteReplicationGroup\",\n    \"DeleteSnapshot\": \"elasticache:DeleteSnapshot\",\n    \"DeleteUser\": \"elasticache:DeleteUser\",\n    \"DeleteUserGroup\": \"elasticache:DeleteUserGroup\",\n    \"DescribeCacheClusters\": \"elasticache:DescribeCacheClusters\",\n    \"DescribeCacheEngineVersions\": \"elasticache:DescribeCacheEngineVersions\",\n    \"DescribeCacheParameterGroups\": \"elasticache:DescribeCacheParameterGroups\",\n    \"DescribeCacheParameters\": \"elasticache:DescribeCacheParameters\",\n    \"DescribeCacheSecurityGroups\": \"elasticache:DescribeCacheSecurityGroups\",\n    \"DescribeCacheSubnetGroups\": \"elasticache:DescribeCacheSubnetGroups\",\n    \"DescribeEngineDefaultParameters\": \"elasticache:DescribeEngineDefaultParameters\",\n    \"DescribeEvents\": \"elasticache:DescribeEvents\",\n    \"DescribeGlobalReplicationGroups\": \"elasticache:DescribeGlobalReplicationGroups\",\n    \"DescribeReplicationGroups\": \"elasticache:DescribeReplicationGroups\",\n    \"DescribeReservedCacheNodes\": \"elasticache:DescribeReservedCacheNodes\",\n    \"DescribeReservedCacheNodesOfferings\": \"elasticache:DescribeReservedCacheNodesOfferings\",\n    \"DescribeServiceUpdates\": \"elasticache:DescribeServiceUpdates\",\n    \"DescribeSnapshots\": \"elasticache:DescribeSnapshots\",\n    \"DescribeUpdateActions\": \"elasticache:DescribeUpdateActions\",\n    \"DescribeUserGroups\": \"elasticache:DescribeUserGroups\",\n    \"DescribeUsers\": \"elasticache:DescribeUsers\",\n    \"DisassociateGlobalReplicationGroup\": \"elasticache:DisassociateGlobalReplicationGroup\",\n    \"FailoverGlobalReplicationGroup\": \"elasticache:FailoverGlobalReplicationGroup\",\n    \"IncreaseNodeGroupsInGlobalReplicationGroup\": \"elasticache:IncreaseNodeGroupsInGlobalReplicationGroup\",\n    \"IncreaseReplicaCount\": \"elasticache:IncreaseReplicaCount\",\n    \"ListAllowedNodeTypeModifications\": \"elasticache:ListAllowedNodeTypeModifications\",\n    \"ListTagsForResource\": \"elasticache:ListTagsForResource\",\n    \"ModifyCacheCluster\": \"elasticache:ModifyCacheCluster\",\n    \"ModifyCacheParameterGroup\": \"elasticache:ModifyCacheParameterGroup\",\n    \"ModifyCacheSubnetGroup\": \"elasticache:ModifyCacheSubnetGroup\",\n    \"ModifyGlobalReplicationGroup\": \"elasticache:ModifyGlobalReplicationGroup\",\n    \"ModifyReplicationGroup\": \"elasticache:ModifyReplicationGroup\",\n    \"ModifyReplicationGroupShardConfiguration\": \"elasticache:ModifyReplicationGroupShardConfiguration\",\n    \"ModifyUser\": \"elasticache:ModifyUser\",\n    \"ModifyUserGroup\": \"elasticache:ModifyUserGroup\",\n    \"PurchaseReservedCacheNodesOffering\": \"elasticache:PurchaseReservedCacheNodesOffering\",\n    \"RebalanceSlotsInGlobalReplicationGroup\": \"elasticache:RebalanceSlotsInGlobalReplicationGroup\",\n    \"RebootCacheCluster\": \"elasticache:RebootCacheCluster\",\n    \"RemoveTagsFromResource\": \"elasticache:RemoveTagsFromResource\",\n    \"ResetCacheParameterGroup\": \"elasticache:ResetCacheParameterGroup\",\n    \"RevokeCacheSecurityGroupIngress\": \"elasticache:RevokeCacheSecurityGroupIngress\",\n    \"StartMigration\": \"elasticache:StartMigration\",\n    \"TestFailover\": \"elasticache:TestFailover\"\n  },\n  \"elasticbeanstalk\": {\n    \"AbortEnvironmentUpdate\": \"elasticbeanstalk:AbortEnvironmentUpdate\",\n    \"ApplyEnvironmentManagedAction\": \"elasticbeanstalk:ApplyEnvironmentManagedAction\",\n    \"AssociateEnvironmentOperationsRole\": \"elasticbeanstalk:AssociateEnvironmentOperationsRole\",\n    \"CheckDNSAvailability\": \"elasticbeanstalk:CheckDNSAvailability\",\n    \"ComposeEnvironments\": \"elasticbeanstalk:ComposeEnvironments\",\n    \"CreateApplication\": \"elasticbeanstalk:CreateApplication\",\n    \"CreateApplicationVersion\": \"elasticbeanstalk:CreateApplicationVersion\",\n    \"CreateConfigurationTemplate\": \"elasticbeanstalk:CreateConfigurationTemplate\",\n    \"CreateEnvironment\": \"elasticbeanstalk:CreateEnvironment\",\n    \"CreatePlatformVersion\": \"elasticbeanstalk:CreatePlatformVersion\",\n    \"CreateStorageLocation\": \"elasticbeanstalk:CreateStorageLocation\",\n    \"DeleteApplication\": \"elasticbeanstalk:DeleteApplication\",\n    \"DeleteApplicationVersion\": \"elasticbeanstalk:DeleteApplicationVersion\",\n    \"DeleteConfigurationTemplate\": \"elasticbeanstalk:DeleteConfigurationTemplate\",\n    \"DeleteEnvironmentConfiguration\": \"elasticbeanstalk:DeleteEnvironmentConfiguration\",\n    \"DeletePlatformVersion\": \"elasticbeanstalk:DeletePlatformVersion\",\n    \"DescribeAccountAttributes\": \"elasticbeanstalk:DescribeAccountAttributes\",\n    \"DescribeApplicationVersions\": \"elasticbeanstalk:DescribeApplicationVersions\",\n    \"DescribeApplications\": \"elasticbeanstalk:DescribeApplications\",\n    \"DescribeConfigurationOptions\": \"elasticbeanstalk:DescribeConfigurationOptions\",\n    \"DescribeConfigurationSettings\": \"elasticbeanstalk:DescribeConfigurationSettings\",\n    \"DescribeEnvironmentHealth\": \"elasticbeanstalk:DescribeEnvironmentHealth\",\n    \"DescribeEnvironmentManagedActionHistory\": \"elasticbeanstalk:DescribeEnvironmentManagedActionHistory\",\n    \"DescribeEnvironmentManagedActions\": \"elasticbeanstalk:DescribeEnvironmentManagedActions\",\n    \"DescribeEnvironmentResources\": \"elasticbeanstalk:DescribeEnvironmentResources\",\n    \"DescribeEnvironments\": \"elasticbeanstalk:DescribeEnvironments\",\n    \"DescribeEvents\": \"elasticbeanstalk:DescribeEvents\",\n    \"DescribeInstancesHealth\": \"elasticbeanstalk:DescribeInstancesHealth\",\n    \"DescribePlatformVersion\": \"elasticbeanstalk:DescribePlatformVersion\",\n    \"DisassociateEnvironmentOperationsRole\": \"elasticbeanstalk:DisassociateEnvironmentOperationsRole\",\n    \"ListAvailableSolutionStacks\": \"elasticbeanstalk:ListAvailableSolutionStacks\",\n    \"ListPlatformBranches\": \"elasticbeanstalk:ListPlatformBranches\",\n    \"ListPlatformVersions\": \"elasticbeanstalk:ListPlatformVersions\",\n    \"ListTagsForResource\": \"elasticbeanstalk:ListTagsForResource\",\n    \"RebuildEnvironment\": \"elasticbeanstalk:RebuildEnvironment\",\n    \"RequestEnvironmentInfo\": \"elasticbeanstalk:RequestEnvironmentInfo\",\n    \"RestartAppServer\": \"elasticbeanstalk:RestartAppServer\",\n    \"RetrieveEnvironmentInfo\": \"elasticbeanstalk:RetrieveEnvironmentInfo\",\n    \"SwapEnvironmentCNAMEs\": \"elasticbeanstalk:SwapEnvironmentCNAMEs\",\n    \"TerminateEnvironment\": \"elasticbeanstalk:TerminateEnvironment\",\n    \"UpdateApplication\": \"elasticbeanstalk:UpdateApplication\",\n    \"UpdateApplicationResourceLifecycle\": \"elasticbeanstalk:UpdateApplicationResourceLifecycle\",\n    \"UpdateApplicationVersion\": \"elasticbeanstalk:UpdateApplicationVersion\",\n    \"UpdateConfigurationTemplate\": \"elasticbeanstalk:UpdateConfigurationTemplate\",\n    \"UpdateEnvironment\": \"elasticbeanstalk:UpdateEnvironment\",\n    \"UpdateTagsForResource\": \"elasticbeanstalk:UpdateTagsForResource\",\n    \"ValidateConfigurationSettings\": \"elasticbeanstalk:ValidateConfigurationSettings\"\n  },\n  \"elastictranscoder\": {\n    \"CancelJob\": \"elastictranscoder:CancelJob\",\n    \"CreateJob\": \"elastictranscoder:CreateJob\",\n    \"CreatePipeline\": \"elastictranscoder:CreatePipeline\",\n    \"CreatePreset\": \"elastictranscoder:CreatePreset\",\n    \"DeletePipeline\": \"elastictranscoder:DeletePipeline\",\n    \"DeletePreset\": \"elastictranscoder:DeletePreset\",\n    \"ListJobsByPipeline\": \"elastictranscoder:ListJobsByPipeline\",\n    \"ListJobsByStatus\": \"elastictranscoder:ListJobsByStatus\",\n    \"ListPipelines\": \"elastictranscoder:ListPipelines\",\n    \"ListPresets\": \"elastictranscoder:ListPresets\",\n    \"ReadJob\": \"elastictranscoder:ReadJob\",\n    \"ReadPipeline\": \"elastictranscoder:ReadPipeline\",\n    \"ReadPreset\": \"elastictranscoder:ReadPreset\",\n    \"TestRole\": \"elastictranscoder:TestRole\",\n    \"UpdatePipeline\": \"elastictranscoder:UpdatePipeline\",\n    \"UpdatePipelineNotifications\": \"elastictranscoder:UpdatePipelineNotifications\",\n    \"UpdatePipelineStatus\": \"elastictranscoder:UpdatePipelineStatus\"\n  },\n  \"elb\": {\n    \"AddTags\": \"elasticloadbalancing:AddTags\",\n    \"ApplySecurityGroupsToLoadBalancer\": \"elasticloadbalancing:ApplySecurityGroupsToLoadBalancer\",\n    \"AttachLoadBalancerToSubnets\": \"elasticloadbalancing:AttachLoadBalancerToSubnets\",\n    \"ConfigureHealthCheck\": \"elasticloadbalancing:ConfigureHealthCheck\",\n    \"CreateAppCookieStickinessPolicy\": \"elasticloadbalancing:CreateAppCookieStickinessPolicy\",\n    \"CreateLBCookieStickinessPolicy\": \"elasticloadbalancing:CreateLBCookieStickinessPolicy\",\n    \"CreateLoadBalancer\": \"elasticloadbalancing:CreateLoadBalancer\",\n    \"CreateLoadBalancerListeners\": \"elasticloadbalancing:CreateLoadBalancerListeners\",\n    \"CreateLoadBalancerPolicy\": \"elasticloadbalancing:CreateLoadBalancerPolicy\",\n    \"DeleteLoadBalancer\": \"elasticloadbalancing:DeleteLoadBalancer\",\n    \"DeleteLoadBalancerListeners\": \"elasticloadbalancing:DeleteLoadBalancerListeners\",\n    \"DeleteLoadBalancerPolicy\": \"elasticloadbalancing:DeleteLoadBalancerPolicy\",\n    \"DeregisterInstancesFromLoadBalancer\": \"elasticloadbalancing:DeregisterInstancesFromLoadBalancer\",\n    \"DescribeAccountLimits\": \"elasticloadbalancing:DescribeAccountLimits\",\n    \"DescribeInstanceHealth\": \"elasticloadbalancing:DescribeInstanceHealth\",\n    \"DescribeLoadBalancerAttributes\": \"elasticloadbalancing:DescribeLoadBalancerAttributes\",\n    \"DescribeLoadBalancerPolicies\": \"elasticloadbalancing:DescribeLoadBalancerPolicies\",\n    \"DescribeLoadBalancerPolicyTypes\": \"elasticloadbalancing:DescribeLoadBalancerPolicyTypes\",\n    \"DescribeLoadBalancers\": \"elasticloadbalancing:DescribeLoadBalancers\",\n    \"DescribeTags\": \"elasticloadbalancing:DescribeTags\",\n    \"DetachLoadBalancerFromSubnets\": \"elasticloadbalancing:DetachLoadBalancerFromSubnets\",\n    \"DisableAvailabilityZonesForLoadBalancer\": \"elasticloadbalancing:DisableAvailabilityZonesForLoadBalancer\",\n    \"EnableAvailabilityZonesForLoadBalancer\": \"elasticloadbalancing:EnableAvailabilityZonesForLoadBalancer\",\n    \"ModifyLoadBalancerAttributes\": \"elasticloadbalancing:ModifyLoadBalancerAttributes\",\n    \"RegisterInstancesWithLoadBalancer\": \"elasticloadbalancing:RegisterInstancesWithLoadBalancer\",\n    \"RemoveTags\": \"elasticloadbalancing:RemoveTags\",\n    \"SetLoadBalancerListenerSSLCertificate\": \"elasticloadbalancing:SetLoadBalancerListenerSSLCertificate\",\n    \"SetLoadBalancerPoliciesForBackendServer\": \"elasticloadbalancing:SetLoadBalancerPoliciesForBackendServer\",\n    \"SetLoadBalancerPoliciesOfListener\": \"elasticloadbalancing:SetLoadBalancerPoliciesOfListener\"\n  },\n  \"elbv2\": {\n    \"AddListenerCertificates\": \"elasticloadbalancing:AddListenerCertificates\",\n    \"AddTags\": \"elasticloadbalancing:AddTags\",\n    \"CreateListener\": \"elasticloadbalancing:CreateListener\",\n    \"CreateLoadBalancer\": \"elasticloadbalancing:CreateLoadBalancer\",\n    \"CreateRule\": \"elasticloadbalancing:CreateRule\",\n    \"CreateTargetGroup\": \"elasticloadbalancing:CreateTargetGroup\",\n    \"DeleteListener\": \"elasticloadbalancing:DeleteListener\",\n    \"DeleteLoadBalancer\": \"elasticloadbalancing:DeleteLoadBalancer\",\n    \"DeleteRule\": \"elasticloadbalancing:DeleteRule\",\n    \"DeleteTargetGroup\": \"elasticloadbalancing:DeleteTargetGroup\",\n    \"DeregisterTargets\": \"elasticloadbalancing:DeregisterTargets\",\n    \"DescribeAccountLimits\": \"elasticloadbalancing:DescribeAccountLimits\",\n    \"DescribeListenerCertificates\": \"elasticloadbalancing:DescribeListenerCertificates\",\n    \"DescribeListeners\": \"elasticloadbalancing:DescribeListeners\",\n    \"DescribeLoadBalancerAttributes\": \"elasticloadbalancing:DescribeLoadBalancerAttributes\",\n    \"DescribeLoadBalancers\": \"elasticloadbalancing:DescribeLoadBalancers\",\n    \"DescribeRules\": \"elasticloadbalancing:DescribeRules\",\n    \"DescribeSSLPolicies\": \"elasticloadbalancing:DescribeSSLPolicies\",\n    \"DescribeTags\": \"elasticloadbalancing:DescribeTags\",\n    \"DescribeTargetGroupAttributes\": \"elasticloadbalancing:DescribeTargetGroupAttributes\",\n    \"DescribeTargetGroups\": \"elasticloadbalancing:DescribeTargetGroups\",\n    \"DescribeTargetHealth\": \"elasticloadbalancing:DescribeTargetHealth\",\n    \"ModifyListener\": \"elasticloadbalancing:ModifyListener\",\n    \"ModifyLoadBalancerAttributes\": \"elasticloadbalancing:ModifyLoadBalancerAttributes\",\n    \"ModifyRule\": \"elasticloadbalancing:ModifyRule\",\n    \"ModifyTargetGroup\": \"elasticloadbalancing:ModifyTargetGroup\",\n    \"ModifyTargetGroupAttributes\": \"elasticloadbalancing:ModifyTargetGroupAttributes\",\n    \"RegisterTargets\": \"elasticloadbalancing:RegisterTargets\",\n    \"RemoveListenerCertificates\": \"elasticloadbalancing:RemoveListenerCertificates\",\n    \"RemoveTags\": \"elasticloadbalancing:RemoveTags\",\n    \"SetIpAddressType\": \"elasticloadbalancing:SetIpAddressType\",\n    \"SetRulePriorities\": \"elasticloadbalancing:SetRulePriorities\",\n    \"SetSecurityGroups\": \"elasticloadbalancing:SetSecurityGroups\",\n    \"SetSubnets\": \"elasticloadbalancing:SetSubnets\"\n  },\n  \"emr\": {\n    \"AddInstanceFleet\": \"elasticmapreduce:AddInstanceFleet\",\n    \"AddInstanceGroups\": \"elasticmapreduce:AddInstanceGroups\",\n    \"AddJobFlowSteps\": \"elasticmapreduce:AddJobFlowSteps\",\n    \"AddTags\": \"elasticmapreduce:AddTags\",\n    \"CancelSteps\": \"elasticmapreduce:CancelSteps\",\n    \"CreateSecurityConfiguration\": \"elasticmapreduce:CreateSecurityConfiguration\",\n    \"CreateStudio\": \"elasticmapreduce:CreateStudio\",\n    \"CreateStudioSessionMapping\": \"elasticmapreduce:CreateStudioSessionMapping\",\n    \"DeleteSecurityConfiguration\": \"elasticmapreduce:DeleteSecurityConfiguration\",\n    \"DeleteStudio\": \"elasticmapreduce:DeleteStudio\",\n    \"DeleteStudioSessionMapping\": \"elasticmapreduce:DeleteStudioSessionMapping\",\n    \"DescribeCluster\": \"elasticmapreduce:DescribeCluster\",\n    \"DescribeJobFlows\": \"elasticmapreduce:DescribeJobFlows\",\n    \"DescribeNotebookExecution\": \"elasticmapreduce:DescribeNotebookExecution\",\n    \"DescribeReleaseLabel\": \"elasticmapreduce:DescribeReleaseLabel\",\n    \"DescribeSecurityConfiguration\": \"elasticmapreduce:DescribeSecurityConfiguration\",\n    \"DescribeStep\": \"elasticmapreduce:DescribeStep\",\n    \"DescribeStudio\": \"elasticmapreduce:DescribeStudio\",\n    \"GetAutoTerminationPolicy\": \"elasticmapreduce:GetAutoTerminationPolicy\",\n    \"GetBlockPublicAccessConfiguration\": \"elasticmapreduce:GetBlockPublicAccessConfiguration\",\n    \"GetManagedScalingPolicy\": \"elasticmapreduce:GetManagedScalingPolicy\",\n    \"GetStudioSessionMapping\": \"elasticmapreduce:GetStudioSessionMapping\",\n    \"ListBootstrapActions\": \"elasticmapreduce:ListBootstrapActions\",\n    \"ListClusters\": \"elasticmapreduce:ListClusters\",\n    \"ListInstanceFleets\": \"elasticmapreduce:ListInstanceFleets\",\n    \"ListInstanceGroups\": \"elasticmapreduce:ListInstanceGroups\",\n    \"ListInstances\": \"elasticmapreduce:ListInstances\",\n    \"ListNotebookExecutions\": \"elasticmapreduce:ListNotebookExecutions\",\n    \"ListReleaseLabels\": \"elasticmapreduce:ListReleaseLabels\",\n    \"ListSecurityConfigurations\": \"elasticmapreduce:ListSecurityConfigurations\",\n    \"ListSteps\": \"elasticmapreduce:ListSteps\",\n    \"ListStudioSessionMappings\": \"elasticmapreduce:ListStudioSessionMappings\",\n    \"ListStudios\": \"elasticmapreduce:ListStudios\",\n    \"ModifyCluster\": \"elasticmapreduce:ModifyCluster\",\n    \"ModifyInstanceFleet\": \"elasticmapreduce:ModifyInstanceFleet\",\n    \"ModifyInstanceGroups\": \"elasticmapreduce:ModifyInstanceGroups\",\n    \"PutAutoScalingPolicy\": \"elasticmapreduce:PutAutoScalingPolicy\",\n    \"PutAutoTerminationPolicy\": \"elasticmapreduce:PutAutoTerminationPolicy\",\n    \"PutBlockPublicAccessConfiguration\": \"elasticmapreduce:PutBlockPublicAccessConfiguration\",\n    \"PutManagedScalingPolicy\": \"elasticmapreduce:PutManagedScalingPolicy\",\n    \"RemoveAutoScalingPolicy\": \"elasticmapreduce:RemoveAutoScalingPolicy\",\n    \"RemoveAutoTerminationPolicy\": \"elasticmapreduce:RemoveAutoTerminationPolicy\",\n    \"RemoveManagedScalingPolicy\": \"elasticmapreduce:RemoveManagedScalingPolicy\",\n    \"RemoveTags\": \"elasticmapreduce:RemoveTags\",\n    \"RunJobFlow\": \"elasticmapreduce:RunJobFlow\",\n    \"SetTerminationProtection\": \"elasticmapreduce:SetTerminationProtection\",\n    \"StartNotebookExecution\": \"elasticmapreduce:StartNotebookExecution\",\n    \"StopNotebookExecution\": \"elasticmapreduce:StopNotebookExecution\",\n    \"TerminateJobFlows\": \"elasticmapreduce:TerminateJobFlows\",\n    \"UpdateStudio\": \"elasticmapreduce:UpdateStudio\",\n    \"UpdateStudioSessionMapping\": \"elasticmapreduce:UpdateStudioSessionMapping\"\n  },\n  \"emr-containers\": {\n    \"CancelJobRun\": \"emr-containers:CancelJobRun\",\n    \"CreateManagedEndpoint\": \"emr-containers:CreateManagedEndpoint\",\n    \"CreateVirtualCluster\": \"emr-containers:CreateVirtualCluster\",\n    \"DeleteManagedEndpoint\": \"emr-containers:DeleteManagedEndpoint\",\n    \"DeleteVirtualCluster\": \"emr-containers:DeleteVirtualCluster\",\n    \"DescribeJobRun\": \"emr-containers:DescribeJobRun\",\n    \"DescribeManagedEndpoint\": \"emr-containers:DescribeManagedEndpoint\",\n    \"DescribeVirtualCluster\": \"emr-containers:DescribeVirtualCluster\",\n    \"ListJobRuns\": \"emr-containers:ListJobRuns\",\n    \"ListManagedEndpoints\": \"emr-containers:ListManagedEndpoints\",\n    \"ListTagsForResource\": \"emr-containers:ListTagsForResource\",\n    \"ListVirtualClusters\": \"emr-containers:ListVirtualClusters\",\n    \"StartJobRun\": \"emr-containers:StartJobRun\",\n    \"TagResource\": \"emr-containers:TagResource\",\n    \"UntagResource\": \"emr-containers:UntagResource\"\n  },\n  \"es\": {\n    \"AcceptInboundCrossClusterSearchConnection\": \"es:AcceptInboundCrossClusterSearchConnection\",\n    \"AddTags\": \"es:AddTags\",\n    \"AssociatePackage\": \"es:AssociatePackage\",\n    \"CancelElasticsearchServiceSoftwareUpdate\": \"es:CancelElasticsearchServiceSoftwareUpdate\",\n    \"CreateElasticsearchDomain\": \"es:CreateElasticsearchDomain\",\n    \"CreateOutboundCrossClusterSearchConnection\": \"es:CreateOutboundCrossClusterSearchConnection\",\n    \"CreatePackage\": \"es:CreatePackage\",\n    \"DeleteElasticsearchDomain\": \"es:DeleteElasticsearchDomain\",\n    \"DeleteElasticsearchServiceRole\": \"es:DeleteElasticsearchServiceRole\",\n    \"DeleteInboundCrossClusterSearchConnection\": \"es:DeleteInboundCrossClusterSearchConnection\",\n    \"DeleteOutboundCrossClusterSearchConnection\": \"es:DeleteOutboundCrossClusterSearchConnection\",\n    \"DeletePackage\": \"es:DeletePackage\",\n    \"DescribeDomainAutoTunes\": \"es:DescribeDomainAutoTunes\",\n    \"DescribeElasticsearchDomain\": \"es:DescribeElasticsearchDomain\",\n    \"DescribeElasticsearchDomainConfig\": \"es:DescribeElasticsearchDomainConfig\",\n    \"DescribeElasticsearchDomains\": \"es:DescribeElasticsearchDomains\",\n    \"DescribeElasticsearchInstanceTypeLimits\": \"es:DescribeElasticsearchInstanceTypeLimits\",\n    \"DescribeInboundCrossClusterSearchConnections\": \"es:DescribeInboundCrossClusterSearchConnections\",\n    \"DescribeOutboundCrossClusterSearchConnections\": \"es:DescribeOutboundCrossClusterSearchConnections\",\n    \"DescribePackages\": \"es:DescribePackages\",\n    \"DescribeReservedElasticsearchInstanceOfferings\": \"es:DescribeReservedElasticsearchInstanceOfferings\",\n    \"DescribeReservedElasticsearchInstances\": \"es:DescribeReservedElasticsearchInstances\",\n    \"DissociatePackage\": \"es:DissociatePackage\",\n    \"GetCompatibleElasticsearchVersions\": \"es:GetCompatibleElasticsearchVersions\",\n    \"GetPackageVersionHistory\": \"es:GetPackageVersionHistory\",\n    \"GetUpgradeHistory\": \"es:GetUpgradeHistory\",\n    \"GetUpgradeStatus\": \"es:GetUpgradeStatus\",\n    \"ListDomainNames\": \"es:ListDomainNames\",\n    \"ListDomainsForPackage\": \"es:ListDomainsForPackage\",\n    \"ListElasticsearchInstanceTypes\": \"es:ListElasticsearchInstanceTypes\",\n    \"ListElasticsearchVersions\": \"es:ListElasticsearchVersions\",\n    \"ListPackagesForDomain\": \"es:ListPackagesForDomain\",\n    \"ListTags\": \"es:ListTags\",\n    \"PurchaseReservedElasticsearchInstanceOffering\": \"es:PurchaseReservedElasticsearchInstanceOffering\",\n    \"RejectInboundCrossClusterSearchConnection\": \"es:RejectInboundCrossClusterSearchConnection\",\n    \"RemoveTags\": \"es:RemoveTags\",\n    \"StartElasticsearchServiceSoftwareUpdate\": \"es:StartElasticsearchServiceSoftwareUpdate\",\n    \"UpdateElasticsearchDomainConfig\": \"es:UpdateElasticsearchDomainConfig\",\n    \"UpdatePackage\": \"es:UpdatePackage\",\n    \"UpgradeElasticsearchDomain\": \"es:UpgradeElasticsearchDomain\"\n  },\n  \"events\": {\n    \"ActivateEventSource\": \"events:ActivateEventSource\",\n    \"CancelReplay\": \"events:CancelReplay\",\n    \"CreateApiDestination\": \"events:CreateApiDestination\",\n    \"CreateArchive\": \"events:CreateArchive\",\n    \"CreateConnection\": \"events:CreateConnection\",\n    \"CreateEventBus\": \"events:CreateEventBus\",\n    \"CreatePartnerEventSource\": \"events:CreatePartnerEventSource\",\n    \"DeactivateEventSource\": \"events:DeactivateEventSource\",\n    \"DeauthorizeConnection\": \"events:DeauthorizeConnection\",\n    \"DeleteApiDestination\": \"events:DeleteApiDestination\",\n    \"DeleteArchive\": \"events:DeleteArchive\",\n    \"DeleteConnection\": \"events:DeleteConnection\",\n    \"DeleteEventBus\": \"events:DeleteEventBus\",\n    \"DeletePartnerEventSource\": \"events:DeletePartnerEventSource\",\n    \"DeleteRule\": \"events:DeleteRule\",\n    \"DescribeApiDestination\": \"events:DescribeApiDestination\",\n    \"DescribeArchive\": \"events:DescribeArchive\",\n    \"DescribeConnection\": \"events:DescribeConnection\",\n    \"DescribeEventBus\": \"events:DescribeEventBus\",\n    \"DescribeEventSource\": \"events:DescribeEventSource\",\n    \"DescribePartnerEventSource\": \"events:DescribePartnerEventSource\",\n    \"DescribeReplay\": \"events:DescribeReplay\",\n    \"DescribeRule\": \"events:DescribeRule\",\n    \"DisableRule\": \"events:DisableRule\",\n    \"EnableRule\": \"events:EnableRule\",\n    \"ListApiDestinations\": \"events:ListApiDestinations\",\n    \"ListArchives\": \"events:ListArchives\",\n    \"ListConnections\": \"events:ListConnections\",\n    \"ListEventBuses\": \"events:ListEventBuses\",\n    \"ListEventSources\": \"events:ListEventSources\",\n    \"ListPartnerEventSourceAccounts\": \"events:ListPartnerEventSourceAccounts\",\n    \"ListPartnerEventSources\": \"events:ListPartnerEventSources\",\n    \"ListReplays\": \"events:ListReplays\",\n    \"ListRuleNamesByTarget\": \"events:ListRuleNamesByTarget\",\n    \"ListRules\": \"events:ListRules\",\n    \"ListTagsForResource\": \"events:ListTagsForResource\",\n    \"ListTargetsByRule\": \"events:ListTargetsByRule\",\n    \"PutEvents\": \"events:PutEvents\",\n    \"PutPartnerEvents\": \"events:PutPartnerEvents\",\n    \"PutPermission\": \"events:PutPermission\",\n    \"PutRule\": \"events:PutRule\",\n    \"PutTargets\": \"events:PutTargets\",\n    \"RemovePermission\": \"events:RemovePermission\",\n    \"RemoveTargets\": \"events:RemoveTargets\",\n    \"StartReplay\": \"events:StartReplay\",\n    \"TagResource\": \"events:TagResource\",\n    \"TestEventPattern\": \"events:TestEventPattern\",\n    \"UntagResource\": \"events:UntagResource\",\n    \"UpdateApiDestination\": \"events:UpdateApiDestination\",\n    \"UpdateArchive\": \"events:UpdateArchive\",\n    \"UpdateConnection\": \"events:UpdateConnection\"\n  },\n  \"evidently\": {\n    \"CreateExperiment\": \"evidently:CreateExperiment\",\n    \"CreateFeature\": \"evidently:CreateFeature\",\n    \"CreateLaunch\": \"evidently:CreateLaunch\",\n    \"CreateProject\": \"evidently:CreateProject\",\n    \"DeleteExperiment\": \"evidently:DeleteExperiment\",\n    \"DeleteFeature\": \"evidently:DeleteFeature\",\n    \"DeleteLaunch\": \"evidently:DeleteLaunch\",\n    \"DeleteProject\": \"evidently:DeleteProject\",\n    \"GetExperiment\": \"evidently:GetExperiment\",\n    \"GetExperimentResults\": \"evidently:GetExperimentResults\",\n    \"GetFeature\": \"evidently:GetFeature\",\n    \"GetLaunch\": \"evidently:GetLaunch\",\n    \"GetProject\": \"evidently:GetProject\",\n    \"ListExperiments\": \"evidently:ListExperiments\",\n    \"ListFeatures\": \"evidently:ListFeatures\",\n    \"ListLaunches\": \"evidently:ListLaunches\",\n    \"ListProjects\": \"evidently:ListProjects\",\n    \"StartExperiment\": \"evidently:StartExperiment\",\n    \"StartLaunch\": \"evidently:StartLaunch\",\n    \"StopExperiment\": \"evidently:StopExperiment\",\n    \"StopLaunch\": \"evidently:StopLaunch\",\n    \"UpdateExperiment\": \"evidently:UpdateExperiment\",\n    \"UpdateFeature\": \"evidently:UpdateFeature\",\n    \"UpdateLaunch\": \"evidently:UpdateLaunch\",\n    \"UpdateProject\": \"evidently:UpdateProject\",\n    \"UpdateProjectDataDelivery\": \"evidently:UpdateProjectDataDelivery\"\n  },\n  \"finspace\": {\n    \"CreateEnvironment\": \"finspace:CreateEnvironment\",\n    \"DeleteEnvironment\": \"finspace:DeleteEnvironment\",\n    \"GetEnvironment\": \"finspace:GetEnvironment\",\n    \"ListEnvironments\": \"finspace:ListEnvironments\",\n    \"ListTagsForResource\": \"finspace:ListTagsForResource\",\n    \"TagResource\": \"finspace:TagResource\",\n    \"UntagResource\": \"finspace:UntagResource\",\n    \"UpdateEnvironment\": \"finspace:UpdateEnvironment\"\n  },\n  \"firehose\": {\n    \"CreateDeliveryStream\": \"firehose:CreateDeliveryStream\",\n    \"DeleteDeliveryStream\": \"firehose:DeleteDeliveryStream\",\n    \"DescribeDeliveryStream\": \"firehose:DescribeDeliveryStream\",\n    \"ListDeliveryStreams\": \"firehose:ListDeliveryStreams\",\n    \"ListTagsForDeliveryStream\": \"firehose:ListTagsForDeliveryStream\",\n    \"PutRecord\": \"firehose:PutRecord\",\n    \"PutRecordBatch\": \"firehose:PutRecordBatch\",\n    \"StartDeliveryStreamEncryption\": \"firehose:StartDeliveryStreamEncryption\",\n    \"StopDeliveryStreamEncryption\": \"firehose:StopDeliveryStreamEncryption\",\n    \"TagDeliveryStream\": \"firehose:TagDeliveryStream\",\n    \"UntagDeliveryStream\": \"firehose:UntagDeliveryStream\",\n    \"UpdateDestination\": \"firehose:UpdateDestination\"\n  },\n  \"fis\": {\n    \"CreateExperimentTemplate\": \"fis:CreateExperimentTemplate\",\n    \"DeleteExperimentTemplate\": \"fis:DeleteExperimentTemplate\",\n    \"GetAction\": \"fis:GetAction\",\n    \"GetExperiment\": \"fis:GetExperiment\",\n    \"GetExperimentTemplate\": \"fis:GetExperimentTemplate\",\n    \"ListActions\": \"fis:ListActions\",\n    \"ListExperimentTemplates\": \"fis:ListExperimentTemplates\",\n    \"ListExperiments\": \"fis:ListExperiments\",\n    \"ListTagsForResource\": \"fis:ListTagsForResource\",\n    \"StartExperiment\": \"fis:StartExperiment\",\n    \"StopExperiment\": \"fis:StopExperiment\",\n    \"TagResource\": \"fis:TagResource\",\n    \"UntagResource\": \"fis:UntagResource\",\n    \"UpdateExperimentTemplate\": \"fis:UpdateExperimentTemplate\"\n  },\n  \"fms\": {\n    \"AssociateAdminAccount\": \"fms:AssociateAdminAccount\",\n    \"DeleteAppsList\": \"fms:DeleteAppsList\",\n    \"DeleteNotificationChannel\": \"fms:DeleteNotificationChannel\",\n    \"DeletePolicy\": \"fms:DeletePolicy\",\n    \"DeleteProtocolsList\": \"fms:DeleteProtocolsList\",\n    \"DisassociateAdminAccount\": \"fms:DisassociateAdminAccount\",\n    \"GetAdminAccount\": \"fms:GetAdminAccount\",\n    \"GetAppsList\": \"fms:GetAppsList\",\n    \"GetComplianceDetail\": \"fms:GetComplianceDetail\",\n    \"GetNotificationChannel\": \"fms:GetNotificationChannel\",\n    \"GetPolicy\": \"fms:GetPolicy\",\n    \"GetProtectionStatus\": \"fms:GetProtectionStatus\",\n    \"GetProtocolsList\": \"fms:GetProtocolsList\",\n    \"GetViolationDetails\": \"fms:GetViolationDetails\",\n    \"ListAppsLists\": \"fms:ListAppsLists\",\n    \"ListComplianceStatus\": \"fms:ListComplianceStatus\",\n    \"ListMemberAccounts\": \"fms:ListMemberAccounts\",\n    \"ListPolicies\": \"fms:ListPolicies\",\n    \"ListProtocolsLists\": \"fms:ListProtocolsLists\",\n    \"ListTagsForResource\": \"fms:ListTagsForResource\",\n    \"PutAppsList\": \"fms:PutAppsList\",\n    \"PutNotificationChannel\": \"fms:PutNotificationChannel\",\n    \"PutPolicy\": \"fms:PutPolicy\",\n    \"PutProtocolsList\": \"fms:PutProtocolsList\",\n    \"TagResource\": \"fms:TagResource\",\n    \"UntagResource\": \"fms:UntagResource\"\n  },\n  \"forecast\": {\n    \"CreateAutoPredictor\": \"forecast:CreateAutoPredictor\",\n    \"CreateDataset\": \"forecast:CreateDataset\",\n    \"CreateDatasetGroup\": \"forecast:CreateDatasetGroup\",\n    \"CreateDatasetImportJob\": \"forecast:CreateDatasetImportJob\",\n    \"CreateExplainability\": \"forecast:CreateExplainability\",\n    \"CreateExplainabilityExport\": \"forecast:CreateExplainabilityExport\",\n    \"CreateForecast\": \"forecast:CreateForecast\",\n    \"CreateForecastExportJob\": \"forecast:CreateForecastExportJob\",\n    \"CreatePredictor\": \"forecast:CreatePredictor\",\n    \"CreatePredictorBacktestExportJob\": \"forecast:CreatePredictorBacktestExportJob\",\n    \"DeleteDataset\": \"forecast:DeleteDataset\",\n    \"DeleteDatasetGroup\": \"forecast:DeleteDatasetGroup\",\n    \"DeleteDatasetImportJob\": \"forecast:DeleteDatasetImportJob\",\n    \"DeleteExplainability\": \"forecast:DeleteExplainability\",\n    \"DeleteExplainabilityExport\": \"forecast:DeleteExplainabilityExport\",\n    \"DeleteForecast\": \"forecast:DeleteForecast\",\n    \"DeleteForecastExportJob\": \"forecast:DeleteForecastExportJob\",\n    \"DeletePredictor\": \"forecast:DeletePredictor\",\n    \"DeletePredictorBacktestExportJob\": \"forecast:DeletePredictorBacktestExportJob\",\n    \"DeleteResourceTree\": \"forecast:DeleteResourceTree\",\n    \"DescribeAutoPredictor\": \"forecast:DescribeAutoPredictor\",\n    \"DescribeDataset\": \"forecast:DescribeDataset\",\n    \"DescribeDatasetGroup\": \"forecast:DescribeDatasetGroup\",\n    \"DescribeDatasetImportJob\": \"forecast:DescribeDatasetImportJob\",\n    \"DescribeExplainabilityExport\": \"forecast:DescribeExplainabilityExport\",\n    \"DescribeForecast\": \"forecast:DescribeForecast\",\n    \"DescribeForecastExportJob\": \"forecast:DescribeForecastExportJob\",\n    \"DescribePredictor\": \"forecast:DescribePredictor\",\n    \"DescribePredictorBacktestExportJob\": \"forecast:DescribePredictorBacktestExportJob\",\n    \"GetAccuracyMetrics\": \"forecast:GetAccuracyMetrics\",\n    \"ListDatasetGroups\": \"forecast:ListDatasetGroups\",\n    \"ListDatasetImportJobs\": \"forecast:ListDatasetImportJobs\",\n    \"ListDatasets\": \"forecast:ListDatasets\",\n    \"ListExplainabilities\": \"forecast:ListExplainabilities\",\n    \"ListExplainabilityExports\": \"forecast:ListExplainabilityExports\",\n    \"ListForecastExportJobs\": \"forecast:ListForecastExportJobs\",\n    \"ListForecasts\": \"forecast:ListForecasts\",\n    \"ListPredictorBacktestExportJobs\": \"forecast:ListPredictorBacktestExportJobs\",\n    \"ListPredictors\": \"forecast:ListPredictors\",\n    \"ListTagsForResource\": \"forecast:ListTagsForResource\",\n    \"StopResource\": \"forecast:StopResource\",\n    \"TagResource\": \"forecast:TagResource\",\n    \"UntagResource\": \"forecast:UntagResource\",\n    \"UpdateDatasetGroup\": \"forecast:UpdateDatasetGroup\"\n  },\n  \"frauddetector\": {\n    \"BatchCreateVariable\": \"frauddetector:BatchCreateVariable\",\n    \"BatchGetVariable\": \"frauddetector:BatchGetVariable\",\n    \"CancelBatchImportJob\": \"frauddetector:CancelBatchImportJob\",\n    \"CancelBatchPredictionJob\": \"frauddetector:CancelBatchPredictionJob\",\n    \"CreateBatchImportJob\": \"frauddetector:CreateBatchImportJob\",\n    \"CreateBatchPredictionJob\": \"frauddetector:CreateBatchPredictionJob\",\n    \"CreateDetectorVersion\": \"frauddetector:CreateDetectorVersion\",\n    \"CreateModel\": \"frauddetector:CreateModel\",\n    \"CreateModelVersion\": \"frauddetector:CreateModelVersion\",\n    \"CreateRule\": \"frauddetector:CreateRule\",\n    \"CreateVariable\": \"frauddetector:CreateVariable\",\n    \"DeleteBatchImportJob\": \"frauddetector:DeleteBatchImportJob\",\n    \"DeleteBatchPredictionJob\": \"frauddetector:DeleteBatchPredictionJob\",\n    \"DeleteDetector\": \"frauddetector:DeleteDetector\",\n    \"DeleteDetectorVersion\": \"frauddetector:DeleteDetectorVersion\",\n    \"DeleteEntityType\": \"frauddetector:DeleteEntityType\",\n    \"DeleteEvent\": \"frauddetector:DeleteEvent\",\n    \"DeleteEventType\": \"frauddetector:DeleteEventType\",\n    \"DeleteEventsByEventType\": \"frauddetector:DeleteEventsByEventType\",\n    \"DeleteExternalModel\": \"frauddetector:DeleteExternalModel\",\n    \"DeleteLabel\": \"frauddetector:DeleteLabel\",\n    \"DeleteModel\": \"frauddetector:DeleteModel\",\n    \"DeleteModelVersion\": \"frauddetector:DeleteModelVersion\",\n    \"DeleteOutcome\": \"frauddetector:DeleteOutcome\",\n    \"DeleteRule\": \"frauddetector:DeleteRule\",\n    \"DeleteVariable\": \"frauddetector:DeleteVariable\",\n    \"DescribeDetector\": \"frauddetector:DescribeDetector\",\n    \"DescribeModelVersions\": \"frauddetector:DescribeModelVersions\",\n    \"GetBatchImportJobs\": \"frauddetector:GetBatchImportJobs\",\n    \"GetBatchPredictionJobs\": \"frauddetector:GetBatchPredictionJobs\",\n    \"GetDeleteEventsByEventTypeStatus\": \"frauddetector:GetDeleteEventsByEventTypeStatus\",\n    \"GetDetectorVersion\": \"frauddetector:GetDetectorVersion\",\n    \"GetDetectors\": \"frauddetector:GetDetectors\",\n    \"GetEntityTypes\": \"frauddetector:GetEntityTypes\",\n    \"GetEvent\": \"frauddetector:GetEvent\",\n    \"GetEventPrediction\": \"frauddetector:GetEventPrediction\",\n    \"GetEventTypes\": \"frauddetector:GetEventTypes\",\n    \"GetExternalModels\": \"frauddetector:GetExternalModels\",\n    \"GetKMSEncryptionKey\": \"frauddetector:GetKMSEncryptionKey\",\n    \"GetLabels\": \"frauddetector:GetLabels\",\n    \"GetModelVersion\": \"frauddetector:GetModelVersion\",\n    \"GetModels\": \"frauddetector:GetModels\",\n    \"GetOutcomes\": \"frauddetector:GetOutcomes\",\n    \"GetRules\": \"frauddetector:GetRules\",\n    \"GetVariables\": \"frauddetector:GetVariables\",\n    \"ListTagsForResource\": \"frauddetector:ListTagsForResource\",\n    \"PutDetector\": \"frauddetector:PutDetector\",\n    \"PutEntityType\": \"frauddetector:PutEntityType\",\n    \"PutEventType\": \"frauddetector:PutEventType\",\n    \"PutExternalModel\": \"frauddetector:PutExternalModel\",\n    \"PutKMSEncryptionKey\": \"frauddetector:PutKMSEncryptionKey\",\n    \"PutLabel\": \"frauddetector:PutLabel\",\n    \"PutOutcome\": \"frauddetector:PutOutcome\",\n    \"SendEvent\": \"frauddetector:SendEvent\",\n    \"TagResource\": \"frauddetector:TagResource\",\n    \"UntagResource\": \"frauddetector:UntagResource\",\n    \"UpdateDetectorVersion\": \"frauddetector:UpdateDetectorVersion\",\n    \"UpdateDetectorVersionMetadata\": \"frauddetector:UpdateDetectorVersionMetadata\",\n    \"UpdateDetectorVersionStatus\": \"frauddetector:UpdateDetectorVersionStatus\",\n    \"UpdateEventLabel\": \"frauddetector:UpdateEventLabel\",\n    \"UpdateModel\": \"frauddetector:UpdateModel\",\n    \"UpdateModelVersion\": \"frauddetector:UpdateModelVersion\",\n    \"UpdateModelVersionStatus\": \"frauddetector:UpdateModelVersionStatus\",\n    \"UpdateRuleMetadata\": \"frauddetector:UpdateRuleMetadata\",\n    \"UpdateRuleVersion\": \"frauddetector:UpdateRuleVersion\",\n    \"UpdateVariable\": \"frauddetector:UpdateVariable\"\n  },\n  \"fsx\": {\n    \"AssociateFileSystemAliases\": \"fsx:AssociateFileSystemAliases\",\n    \"CancelDataRepositoryTask\": \"fsx:CancelDataRepositoryTask\",\n    \"CopyBackup\": \"fsx:CopyBackup\",\n    \"CreateBackup\": \"fsx:CreateBackup\",\n    \"CreateDataRepositoryAssociation\": \"fsx:CreateDataRepositoryAssociation\",\n    \"CreateDataRepositoryTask\": \"fsx:CreateDataRepositoryTask\",\n    \"CreateFileSystem\": \"fsx:CreateFileSystem\",\n    \"CreateFileSystemFromBackup\": \"fsx:CreateFileSystemFromBackup\",\n    \"CreateSnapshot\": \"fsx:CreateSnapshot\",\n    \"CreateStorageVirtualMachine\": \"fsx:CreateStorageVirtualMachine\",\n    \"CreateVolume\": \"fsx:CreateVolume\",\n    \"CreateVolumeFromBackup\": \"fsx:CreateVolumeFromBackup\",\n    \"DeleteBackup\": \"fsx:DeleteBackup\",\n    \"DeleteDataRepositoryAssociation\": \"fsx:DeleteDataRepositoryAssociation\",\n    \"DeleteFileSystem\": \"fsx:DeleteFileSystem\",\n    \"DeleteSnapshot\": \"fsx:DeleteSnapshot\",\n    \"DeleteStorageVirtualMachine\": \"fsx:DeleteStorageVirtualMachine\",\n    \"DeleteVolume\": \"fsx:DeleteVolume\",\n    \"DescribeBackups\": \"fsx:DescribeBackups\",\n    \"DescribeDataRepositoryAssociations\": \"fsx:DescribeDataRepositoryAssociations\",\n    \"DescribeDataRepositoryTasks\": \"fsx:DescribeDataRepositoryTasks\",\n    \"DescribeFileSystemAliases\": \"fsx:DescribeFileSystemAliases\",\n    \"DescribeFileSystems\": \"fsx:DescribeFileSystems\",\n    \"DescribeSnapshots\": \"fsx:DescribeSnapshots\",\n    \"DescribeStorageVirtualMachines\": \"fsx:DescribeStorageVirtualMachines\",\n    \"DescribeVolumes\": \"fsx:DescribeVolumes\",\n    \"DisassociateFileSystemAliases\": \"fsx:DisassociateFileSystemAliases\",\n    \"ListTagsForResource\": \"fsx:ListTagsForResource\",\n    \"RestoreVolumeFromSnapshot\": \"fsx:RestoreVolumeFromSnapshot\",\n    \"TagResource\": \"fsx:TagResource\",\n    \"UntagResource\": \"fsx:UntagResource\",\n    \"UpdateDataRepositoryAssociation\": \"fsx:UpdateDataRepositoryAssociation\",\n    \"UpdateFileSystem\": \"fsx:UpdateFileSystem\",\n    \"UpdateSnapshot\": \"fsx:UpdateSnapshot\",\n    \"UpdateStorageVirtualMachine\": \"fsx:UpdateStorageVirtualMachine\",\n    \"UpdateVolume\": \"fsx:UpdateVolume\"\n  },\n  \"gamelift\": {\n    \"AcceptMatch\": \"gamelift:AcceptMatch\",\n    \"ClaimGameServer\": \"gamelift:ClaimGameServer\",\n    \"CreateAlias\": \"gamelift:CreateAlias\",\n    \"CreateBuild\": \"gamelift:CreateBuild\",\n    \"CreateFleet\": \"gamelift:CreateFleet\",\n    \"CreateFleetLocations\": \"gamelift:CreateFleetLocations\",\n    \"CreateGameServerGroup\": \"gamelift:CreateGameServerGroup\",\n    \"CreateGameSession\": \"gamelift:CreateGameSession\",\n    \"CreateGameSessionQueue\": \"gamelift:CreateGameSessionQueue\",\n    \"CreateMatchmakingConfiguration\": \"gamelift:CreateMatchmakingConfiguration\",\n    \"CreateMatchmakingRuleSet\": \"gamelift:CreateMatchmakingRuleSet\",\n    \"CreatePlayerSession\": \"gamelift:CreatePlayerSession\",\n    \"CreatePlayerSessions\": \"gamelift:CreatePlayerSessions\",\n    \"CreateScript\": \"gamelift:CreateScript\",\n    \"CreateVpcPeeringAuthorization\": \"gamelift:CreateVpcPeeringAuthorization\",\n    \"CreateVpcPeeringConnection\": \"gamelift:CreateVpcPeeringConnection\",\n    \"DeleteAlias\": \"gamelift:DeleteAlias\",\n    \"DeleteBuild\": \"gamelift:DeleteBuild\",\n    \"DeleteFleet\": \"gamelift:DeleteFleet\",\n    \"DeleteFleetLocations\": \"gamelift:DeleteFleetLocations\",\n    \"DeleteGameServerGroup\": \"gamelift:DeleteGameServerGroup\",\n    \"DeleteGameSessionQueue\": \"gamelift:DeleteGameSessionQueue\",\n    \"DeleteMatchmakingConfiguration\": \"gamelift:DeleteMatchmakingConfiguration\",\n    \"DeleteMatchmakingRuleSet\": \"gamelift:DeleteMatchmakingRuleSet\",\n    \"DeleteScalingPolicy\": \"gamelift:DeleteScalingPolicy\",\n    \"DeleteScript\": \"gamelift:DeleteScript\",\n    \"DeleteVpcPeeringAuthorization\": \"gamelift:DeleteVpcPeeringAuthorization\",\n    \"DeleteVpcPeeringConnection\": \"gamelift:DeleteVpcPeeringConnection\",\n    \"DeregisterGameServer\": \"gamelift:DeregisterGameServer\",\n    \"DescribeAlias\": \"gamelift:DescribeAlias\",\n    \"DescribeBuild\": \"gamelift:DescribeBuild\",\n    \"DescribeEC2InstanceLimits\": \"gamelift:DescribeEC2InstanceLimits\",\n    \"DescribeFleetAttributes\": \"gamelift:DescribeFleetAttributes\",\n    \"DescribeFleetCapacity\": \"gamelift:DescribeFleetCapacity\",\n    \"DescribeFleetEvents\": \"gamelift:DescribeFleetEvents\",\n    \"DescribeFleetLocationAttributes\": \"gamelift:DescribeFleetLocationAttributes\",\n    \"DescribeFleetLocationCapacity\": \"gamelift:DescribeFleetLocationCapacity\",\n    \"DescribeFleetLocationUtilization\": \"gamelift:DescribeFleetLocationUtilization\",\n    \"DescribeFleetPortSettings\": \"gamelift:DescribeFleetPortSettings\",\n    \"DescribeFleetUtilization\": \"gamelift:DescribeFleetUtilization\",\n    \"DescribeGameServer\": \"gamelift:DescribeGameServer\",\n    \"DescribeGameServerGroup\": \"gamelift:DescribeGameServerGroup\",\n    \"DescribeGameServerInstances\": \"gamelift:DescribeGameServerInstances\",\n    \"DescribeGameSessionDetails\": \"gamelift:DescribeGameSessionDetails\",\n    \"DescribeGameSessionPlacement\": \"gamelift:DescribeGameSessionPlacement\",\n    \"DescribeGameSessionQueues\": \"gamelift:DescribeGameSessionQueues\",\n    \"DescribeGameSessions\": \"gamelift:DescribeGameSessions\",\n    \"DescribeInstances\": \"gamelift:DescribeInstances\",\n    \"DescribeMatchmaking\": \"gamelift:DescribeMatchmaking\",\n    \"DescribeMatchmakingConfigurations\": \"gamelift:DescribeMatchmakingConfigurations\",\n    \"DescribeMatchmakingRuleSets\": \"gamelift:DescribeMatchmakingRuleSets\",\n    \"DescribePlayerSessions\": \"gamelift:DescribePlayerSessions\",\n    \"DescribeRuntimeConfiguration\": \"gamelift:DescribeRuntimeConfiguration\",\n    \"DescribeScalingPolicies\": \"gamelift:DescribeScalingPolicies\",\n    \"DescribeScript\": \"gamelift:DescribeScript\",\n    \"DescribeVpcPeeringAuthorizations\": \"gamelift:DescribeVpcPeeringAuthorizations\",\n    \"DescribeVpcPeeringConnections\": \"gamelift:DescribeVpcPeeringConnections\",\n    \"GetGameSessionLogUrl\": \"gamelift:GetGameSessionLogUrl\",\n    \"GetInstanceAccess\": \"gamelift:GetInstanceAccess\",\n    \"ListAliases\": \"gamelift:ListAliases\",\n    \"ListBuilds\": \"gamelift:ListBuilds\",\n    \"ListFleets\": \"gamelift:ListFleets\",\n    \"ListGameServerGroups\": \"gamelift:ListGameServerGroups\",\n    \"ListGameServers\": \"gamelift:ListGameServers\",\n    \"ListScripts\": \"gamelift:ListScripts\",\n    \"ListTagsForResource\": \"gamelift:ListTagsForResource\",\n    \"PutScalingPolicy\": \"gamelift:PutScalingPolicy\",\n    \"RegisterGameServer\": \"gamelift:RegisterGameServer\",\n    \"RequestUploadCredentials\": \"gamelift:RequestUploadCredentials\",\n    \"ResolveAlias\": \"gamelift:ResolveAlias\",\n    \"ResumeGameServerGroup\": \"gamelift:ResumeGameServerGroup\",\n    \"SearchGameSessions\": \"gamelift:SearchGameSessions\",\n    \"StartFleetActions\": \"gamelift:StartFleetActions\",\n    \"StartGameSessionPlacement\": \"gamelift:StartGameSessionPlacement\",\n    \"StartMatchBackfill\": \"gamelift:StartMatchBackfill\",\n    \"StartMatchmaking\": \"gamelift:StartMatchmaking\",\n    \"StopFleetActions\": \"gamelift:StopFleetActions\",\n    \"StopGameSessionPlacement\": \"gamelift:StopGameSessionPlacement\",\n    \"StopMatchmaking\": \"gamelift:StopMatchmaking\",\n    \"SuspendGameServerGroup\": \"gamelift:SuspendGameServerGroup\",\n    \"TagResource\": \"gamelift:TagResource\",\n    \"UntagResource\": \"gamelift:UntagResource\",\n    \"UpdateAlias\": \"gamelift:UpdateAlias\",\n    \"UpdateBuild\": \"gamelift:UpdateBuild\",\n    \"UpdateFleetAttributes\": \"gamelift:UpdateFleetAttributes\",\n    \"UpdateFleetCapacity\": \"gamelift:UpdateFleetCapacity\",\n    \"UpdateFleetPortSettings\": \"gamelift:UpdateFleetPortSettings\",\n    \"UpdateGameServer\": \"gamelift:UpdateGameServer\",\n    \"UpdateGameServerGroup\": \"gamelift:UpdateGameServerGroup\",\n    \"UpdateGameSession\": \"gamelift:UpdateGameSession\",\n    \"UpdateGameSessionQueue\": \"gamelift:UpdateGameSessionQueue\",\n    \"UpdateMatchmakingConfiguration\": \"gamelift:UpdateMatchmakingConfiguration\",\n    \"UpdateRuntimeConfiguration\": \"gamelift:UpdateRuntimeConfiguration\",\n    \"UpdateScript\": \"gamelift:UpdateScript\",\n    \"ValidateMatchmakingRuleSet\": \"gamelift:ValidateMatchmakingRuleSet\"\n  },\n  \"glacier\": {\n    \"AbortMultipartUpload\": \"glacier:AbortMultipartUpload\",\n    \"AbortVaultLock\": \"glacier:AbortVaultLock\",\n    \"AddTagsToVault\": \"glacier:AddTagsToVault\",\n    \"CompleteMultipartUpload\": \"glacier:CompleteMultipartUpload\",\n    \"CompleteVaultLock\": \"glacier:CompleteVaultLock\",\n    \"CreateVault\": \"glacier:CreateVault\",\n    \"DeleteArchive\": \"glacier:DeleteArchive\",\n    \"DeleteVault\": \"glacier:DeleteVault\",\n    \"DeleteVaultAccessPolicy\": \"glacier:DeleteVaultAccessPolicy\",\n    \"DeleteVaultNotifications\": \"glacier:DeleteVaultNotifications\",\n    \"DescribeJob\": \"glacier:DescribeJob\",\n    \"DescribeVault\": \"glacier:DescribeVault\",\n    \"GetDataRetrievalPolicy\": \"glacier:GetDataRetrievalPolicy\",\n    \"GetJobOutput\": \"glacier:GetJobOutput\",\n    \"GetVaultAccessPolicy\": \"glacier:GetVaultAccessPolicy\",\n    \"GetVaultLock\": \"glacier:GetVaultLock\",\n    \"GetVaultNotifications\": \"glacier:GetVaultNotifications\",\n    \"InitiateJob\": \"glacier:InitiateJob\",\n    \"InitiateMultipartUpload\": \"glacier:InitiateMultipartUpload\",\n    \"InitiateVaultLock\": \"glacier:InitiateVaultLock\",\n    \"ListJobs\": \"glacier:ListJobs\",\n    \"ListMultipartUploads\": \"glacier:ListMultipartUploads\",\n    \"ListParts\": \"glacier:ListParts\",\n    \"ListProvisionedCapacity\": \"glacier:ListProvisionedCapacity\",\n    \"ListTagsForVault\": \"glacier:ListTagsForVault\",\n    \"ListVaults\": \"glacier:ListVaults\",\n    \"PurchaseProvisionedCapacity\": \"glacier:PurchaseProvisionedCapacity\",\n    \"RemoveTagsFromVault\": \"glacier:RemoveTagsFromVault\",\n    \"SetDataRetrievalPolicy\": \"glacier:SetDataRetrievalPolicy\",\n    \"SetVaultAccessPolicy\": \"glacier:SetVaultAccessPolicy\",\n    \"SetVaultNotifications\": \"glacier:SetVaultNotifications\",\n    \"UploadArchive\": \"glacier:UploadArchive\",\n    \"UploadMultipartPart\": \"glacier:UploadMultipartPart\"\n  },\n  \"globalaccelerator\": {\n    \"AddCustomRoutingEndpoints\": \"globalaccelerator:AddCustomRoutingEndpoints\",\n    \"AdvertiseByoipCidr\": \"globalaccelerator:AdvertiseByoipCidr\",\n    \"AllowCustomRoutingTraffic\": \"globalaccelerator:AllowCustomRoutingTraffic\",\n    \"CreateAccelerator\": \"globalaccelerator:CreateAccelerator\",\n    \"CreateCustomRoutingAccelerator\": \"globalaccelerator:CreateCustomRoutingAccelerator\",\n    \"CreateCustomRoutingEndpointGroup\": \"globalaccelerator:CreateCustomRoutingEndpointGroup\",\n    \"CreateCustomRoutingListener\": \"globalaccelerator:CreateCustomRoutingListener\",\n    \"CreateEndpointGroup\": \"globalaccelerator:CreateEndpointGroup\",\n    \"CreateListener\": \"globalaccelerator:CreateListener\",\n    \"DeleteAccelerator\": \"globalaccelerator:DeleteAccelerator\",\n    \"DeleteCustomRoutingAccelerator\": \"globalaccelerator:DeleteCustomRoutingAccelerator\",\n    \"DeleteCustomRoutingEndpointGroup\": \"globalaccelerator:DeleteCustomRoutingEndpointGroup\",\n    \"DeleteCustomRoutingListener\": \"globalaccelerator:DeleteCustomRoutingListener\",\n    \"DeleteEndpointGroup\": \"globalaccelerator:DeleteEndpointGroup\",\n    \"DeleteListener\": \"globalaccelerator:DeleteListener\",\n    \"DenyCustomRoutingTraffic\": \"globalaccelerator:DenyCustomRoutingTraffic\",\n    \"DeprovisionByoipCidr\": \"globalaccelerator:DeprovisionByoipCidr\",\n    \"DescribeAccelerator\": \"globalaccelerator:DescribeAccelerator\",\n    \"DescribeAcceleratorAttributes\": \"globalaccelerator:DescribeAcceleratorAttributes\",\n    \"DescribeCustomRoutingAccelerator\": \"globalaccelerator:DescribeCustomRoutingAccelerator\",\n    \"DescribeCustomRoutingAcceleratorAttributes\": \"globalaccelerator:DescribeCustomRoutingAcceleratorAttributes\",\n    \"DescribeCustomRoutingEndpointGroup\": \"globalaccelerator:DescribeCustomRoutingEndpointGroup\",\n    \"DescribeCustomRoutingListener\": \"globalaccelerator:DescribeCustomRoutingListener\",\n    \"DescribeEndpointGroup\": \"globalaccelerator:DescribeEndpointGroup\",\n    \"DescribeListener\": \"globalaccelerator:DescribeListener\",\n    \"ListAccelerators\": \"globalaccelerator:ListAccelerators\",\n    \"ListByoipCidrs\": \"globalaccelerator:ListByoipCidrs\",\n    \"ListCustomRoutingAccelerators\": \"globalaccelerator:ListCustomRoutingAccelerators\",\n    \"ListCustomRoutingEndpointGroups\": \"globalaccelerator:ListCustomRoutingEndpointGroups\",\n    \"ListCustomRoutingListeners\": \"globalaccelerator:ListCustomRoutingListeners\",\n    \"ListCustomRoutingPortMappings\": \"globalaccelerator:ListCustomRoutingPortMappings\",\n    \"ListCustomRoutingPortMappingsByDestination\": \"globalaccelerator:ListCustomRoutingPortMappingsByDestination\",\n    \"ListEndpointGroups\": \"globalaccelerator:ListEndpointGroups\",\n    \"ListListeners\": \"globalaccelerator:ListListeners\",\n    \"ListTagsForResource\": \"globalaccelerator:ListTagsForResource\",\n    \"ProvisionByoipCidr\": \"globalaccelerator:ProvisionByoipCidr\",\n    \"RemoveCustomRoutingEndpoints\": \"globalaccelerator:RemoveCustomRoutingEndpoints\",\n    \"TagResource\": \"globalaccelerator:TagResource\",\n    \"UntagResource\": \"globalaccelerator:UntagResource\",\n    \"UpdateAccelerator\": \"globalaccelerator:UpdateAccelerator\",\n    \"UpdateAcceleratorAttributes\": \"globalaccelerator:UpdateAcceleratorAttributes\",\n    \"UpdateCustomRoutingAccelerator\": \"globalaccelerator:UpdateCustomRoutingAccelerator\",\n    \"UpdateCustomRoutingAcceleratorAttributes\": \"globalaccelerator:UpdateCustomRoutingAcceleratorAttributes\",\n    \"UpdateCustomRoutingListener\": \"globalaccelerator:UpdateCustomRoutingListener\",\n    \"UpdateEndpointGroup\": \"globalaccelerator:UpdateEndpointGroup\",\n    \"UpdateListener\": \"globalaccelerator:UpdateListener\",\n    \"WithdrawByoipCidr\": \"globalaccelerator:WithdrawByoipCidr\"\n  },\n  \"grafana\": {\n    \"AssociateLicense\": \"grafana:AssociateLicense\",\n    \"CreateWorkspace\": \"grafana:CreateWorkspace\",\n    \"DeleteWorkspace\": \"grafana:DeleteWorkspace\",\n    \"DescribeWorkspace\": \"grafana:DescribeWorkspace\",\n    \"DescribeWorkspaceAuthentication\": \"grafana:DescribeWorkspaceAuthentication\",\n    \"DisassociateLicense\": \"grafana:DisassociateLicense\",\n    \"ListPermissions\": \"grafana:ListPermissions\",\n    \"ListWorkspaces\": \"grafana:ListWorkspaces\",\n    \"UpdatePermissions\": \"grafana:UpdatePermissions\",\n    \"UpdateWorkspace\": \"grafana:UpdateWorkspace\",\n    \"UpdateWorkspaceAuthentication\": \"grafana:UpdateWorkspaceAuthentication\"\n  },\n  \"groundstation\": {\n    \"CancelContact\": \"groundstation:CancelContact\",\n    \"CreateConfig\": \"groundstation:CreateConfig\",\n    \"CreateDataflowEndpointGroup\": \"groundstation:CreateDataflowEndpointGroup\",\n    \"CreateMissionProfile\": \"groundstation:CreateMissionProfile\",\n    \"DeleteConfig\": \"groundstation:DeleteConfig\",\n    \"DeleteDataflowEndpointGroup\": \"groundstation:DeleteDataflowEndpointGroup\",\n    \"DeleteMissionProfile\": \"groundstation:DeleteMissionProfile\",\n    \"DescribeContact\": \"groundstation:DescribeContact\",\n    \"GetConfig\": \"groundstation:GetConfig\",\n    \"GetDataflowEndpointGroup\": \"groundstation:GetDataflowEndpointGroup\",\n    \"GetMinuteUsage\": \"groundstation:GetMinuteUsage\",\n    \"GetMissionProfile\": \"groundstation:GetMissionProfile\",\n    \"GetSatellite\": \"groundstation:GetSatellite\",\n    \"ListConfigs\": \"groundstation:ListConfigs\",\n    \"ListContacts\": \"groundstation:ListContacts\",\n    \"ListDataflowEndpointGroups\": \"groundstation:ListDataflowEndpointGroups\",\n    \"ListGroundStations\": \"groundstation:ListGroundStations\",\n    \"ListMissionProfiles\": \"groundstation:ListMissionProfiles\",\n    \"ListSatellites\": \"groundstation:ListSatellites\",\n    \"ListTagsForResource\": \"groundstation:ListTagsForResource\",\n    \"ReserveContact\": \"groundstation:ReserveContact\",\n    \"TagResource\": \"groundstation:TagResource\",\n    \"UntagResource\": \"groundstation:UntagResource\",\n    \"UpdateConfig\": \"groundstation:UpdateConfig\",\n    \"UpdateMissionProfile\": \"groundstation:UpdateMissionProfile\"\n  },\n  \"guardduty\": {\n    \"AcceptInvitation\": \"guardduty:AcceptInvitation\",\n    \"ArchiveFindings\": \"guardduty:ArchiveFindings\",\n    \"CreateDetector\": \"guardduty:CreateDetector\",\n    \"CreateFilter\": \"guardduty:CreateFilter\",\n    \"CreateIPSet\": \"guardduty:CreateIPSet\",\n    \"CreateMembers\": \"guardduty:CreateMembers\",\n    \"CreatePublishingDestination\": \"guardduty:CreatePublishingDestination\",\n    \"CreateSampleFindings\": \"guardduty:CreateSampleFindings\",\n    \"CreateThreatIntelSet\": \"guardduty:CreateThreatIntelSet\",\n    \"DeclineInvitations\": \"guardduty:DeclineInvitations\",\n    \"DeleteDetector\": \"guardduty:DeleteDetector\",\n    \"DeleteFilter\": \"guardduty:DeleteFilter\",\n    \"DeleteIPSet\": \"guardduty:DeleteIPSet\",\n    \"DeleteInvitations\": \"guardduty:DeleteInvitations\",\n    \"DeleteMembers\": \"guardduty:DeleteMembers\",\n    \"DeletePublishingDestination\": \"guardduty:DeletePublishingDestination\",\n    \"DeleteThreatIntelSet\": \"guardduty:DeleteThreatIntelSet\",\n    \"DescribeOrganizationConfiguration\": \"guardduty:DescribeOrganizationConfiguration\",\n    \"DescribePublishingDestination\": \"guardduty:DescribePublishingDestination\",\n    \"DisableOrganizationAdminAccount\": \"guardduty:DisableOrganizationAdminAccount\",\n    \"DisassociateFromMasterAccount\": \"guardduty:DisassociateFromMasterAccount\",\n    \"DisassociateMembers\": \"guardduty:DisassociateMembers\",\n    \"EnableOrganizationAdminAccount\": \"guardduty:EnableOrganizationAdminAccount\",\n    \"GetDetector\": \"guardduty:GetDetector\",\n    \"GetFilter\": \"guardduty:GetFilter\",\n    \"GetFindings\": \"guardduty:GetFindings\",\n    \"GetFindingsStatistics\": \"guardduty:GetFindingsStatistics\",\n    \"GetIPSet\": \"guardduty:GetIPSet\",\n    \"GetInvitationsCount\": \"guardduty:GetInvitationsCount\",\n    \"GetMasterAccount\": \"guardduty:GetMasterAccount\",\n    \"GetMemberDetectors\": \"guardduty:GetMemberDetectors\",\n    \"GetMembers\": \"guardduty:GetMembers\",\n    \"GetThreatIntelSet\": \"guardduty:GetThreatIntelSet\",\n    \"GetUsageStatistics\": \"guardduty:GetUsageStatistics\",\n    \"InviteMembers\": \"guardduty:InviteMembers\",\n    \"ListDetectors\": \"guardduty:ListDetectors\",\n    \"ListFilters\": \"guardduty:ListFilters\",\n    \"ListFindings\": \"guardduty:ListFindings\",\n    \"ListIPSets\": \"guardduty:ListIPSets\",\n    \"ListInvitations\": \"guardduty:ListInvitations\",\n    \"ListMembers\": \"guardduty:ListMembers\",\n    \"ListOrganizationAdminAccounts\": \"guardduty:ListOrganizationAdminAccounts\",\n    \"ListPublishingDestinations\": \"guardduty:ListPublishingDestinations\",\n    \"ListTagsForResource\": \"guardduty:ListTagsForResource\",\n    \"ListThreatIntelSets\": \"guardduty:ListThreatIntelSets\",\n    \"StartMonitoringMembers\": \"guardduty:StartMonitoringMembers\",\n    \"StopMonitoringMembers\": \"guardduty:StopMonitoringMembers\",\n    \"TagResource\": \"guardduty:TagResource\",\n    \"UnarchiveFindings\": \"guardduty:UnarchiveFindings\",\n    \"UntagResource\": \"guardduty:UntagResource\",\n    \"UpdateDetector\": \"guardduty:UpdateDetector\",\n    \"UpdateFilter\": \"guardduty:UpdateFilter\",\n    \"UpdateFindingsFeedback\": \"guardduty:UpdateFindingsFeedback\",\n    \"UpdateIPSet\": \"guardduty:UpdateIPSet\",\n    \"UpdateMemberDetectors\": \"guardduty:UpdateMemberDetectors\",\n    \"UpdateOrganizationConfiguration\": \"guardduty:UpdateOrganizationConfiguration\",\n    \"UpdatePublishingDestination\": \"guardduty:UpdatePublishingDestination\",\n    \"UpdateThreatIntelSet\": \"guardduty:UpdateThreatIntelSet\"\n  },\n  \"health\": {\n    \"DescribeAffectedAccountsForOrganization\": \"health:DescribeAffectedAccountsForOrganization\",\n    \"DescribeAffectedEntities\": \"health:DescribeAffectedEntities\",\n    \"DescribeAffectedEntitiesForOrganization\": \"health:DescribeAffectedEntitiesForOrganization\",\n    \"DescribeEntityAggregates\": \"health:DescribeEntityAggregates\",\n    \"DescribeEventAggregates\": \"health:DescribeEventAggregates\",\n    \"DescribeEventDetails\": \"health:DescribeEventDetails\",\n    \"DescribeEventDetailsForOrganization\": \"health:DescribeEventDetailsForOrganization\",\n    \"DescribeEventTypes\": \"health:DescribeEventTypes\",\n    \"DescribeEvents\": \"health:DescribeEvents\",\n    \"DescribeEventsForOrganization\": \"health:DescribeEventsForOrganization\",\n    \"DescribeHealthServiceStatusForOrganization\": \"health:DescribeHealthServiceStatusForOrganization\",\n    \"DisableHealthServiceAccessForOrganization\": \"health:DisableHealthServiceAccessForOrganization\",\n    \"EnableHealthServiceAccessForOrganization\": \"health:EnableHealthServiceAccessForOrganization\"\n  },\n  \"healthlake\": {\n    \"CreateFHIRDatastore\": \"healthlake:CreateFHIRDatastore\",\n    \"DeleteFHIRDatastore\": \"healthlake:DeleteFHIRDatastore\",\n    \"DescribeFHIRDatastore\": \"healthlake:DescribeFHIRDatastore\",\n    \"DescribeFHIRExportJob\": \"healthlake:DescribeFHIRExportJob\",\n    \"DescribeFHIRImportJob\": \"healthlake:DescribeFHIRImportJob\",\n    \"ListFHIRDatastores\": \"healthlake:ListFHIRDatastores\",\n    \"ListFHIRExportJobs\": \"healthlake:ListFHIRExportJobs\",\n    \"ListFHIRImportJobs\": \"healthlake:ListFHIRImportJobs\",\n    \"ListTagsForResource\": \"healthlake:ListTagsForResource\",\n    \"StartFHIRExportJob\": \"healthlake:StartFHIRExportJob\",\n    \"StartFHIRImportJob\": \"healthlake:StartFHIRImportJob\",\n    \"TagResource\": \"healthlake:TagResource\",\n    \"UntagResource\": \"healthlake:UntagResource\"\n  },\n  \"honeycode\": {\n    \"BatchCreateTableRows\": \"honeycode:BatchCreateTableRows\",\n    \"BatchDeleteTableRows\": \"honeycode:BatchDeleteTableRows\",\n    \"BatchUpdateTableRows\": \"honeycode:BatchUpdateTableRows\",\n    \"BatchUpsertTableRows\": \"honeycode:BatchUpsertTableRows\",\n    \"DescribeTableDataImportJob\": \"honeycode:DescribeTableDataImportJob\",\n    \"GetScreenData\": \"honeycode:GetScreenData\",\n    \"InvokeScreenAutomation\": \"honeycode:InvokeScreenAutomation\",\n    \"ListTableColumns\": \"honeycode:ListTableColumns\",\n    \"ListTableRows\": \"honeycode:ListTableRows\",\n    \"ListTables\": \"honeycode:ListTables\",\n    \"QueryTableRows\": \"honeycode:QueryTableRows\",\n    \"StartTableDataImportJob\": \"honeycode:StartTableDataImportJob\"\n  },\n  \"iam\": {\n    \"AddClientIDToOpenIDConnectProvider\": \"iam:AddClientIDToOpenIDConnectProvider\",\n    \"AddRoleToInstanceProfile\": \"iam:AddRoleToInstanceProfile\",\n    \"AddUserToGroup\": \"iam:AddUserToGroup\",\n    \"AttachGroupPolicy\": \"iam:AttachGroupPolicy\",\n    \"AttachRolePolicy\": \"iam:AttachRolePolicy\",\n    \"AttachUserPolicy\": \"iam:AttachUserPolicy\",\n    \"ChangePassword\": \"iam:ChangePassword\",\n    \"CreateAccessKey\": \"iam:CreateAccessKey\",\n    \"CreateAccountAlias\": \"iam:CreateAccountAlias\",\n    \"CreateGroup\": \"iam:CreateGroup\",\n    \"CreateInstanceProfile\": \"iam:CreateInstanceProfile\",\n    \"CreateLoginProfile\": \"iam:CreateLoginProfile\",\n    \"CreateOpenIDConnectProvider\": \"iam:CreateOpenIDConnectProvider\",\n    \"CreatePolicy\": \"iam:CreatePolicy\",\n    \"CreatePolicyVersion\": \"iam:CreatePolicyVersion\",\n    \"CreateRole\": \"iam:CreateRole\",\n    \"CreateSAMLProvider\": \"iam:CreateSAMLProvider\",\n    \"CreateServiceLinkedRole\": \"iam:CreateServiceLinkedRole\",\n    \"CreateServiceSpecificCredential\": \"iam:CreateServiceSpecificCredential\",\n    \"CreateUser\": \"iam:CreateUser\",\n    \"CreateVirtualMFADevice\": \"iam:CreateVirtualMFADevice\",\n    \"DeactivateMFADevice\": \"iam:DeactivateMFADevice\",\n    \"DeleteAccessKey\": \"iam:DeleteAccessKey\",\n    \"DeleteAccountAlias\": \"iam:DeleteAccountAlias\",\n    \"DeleteAccountPasswordPolicy\": \"iam:DeleteAccountPasswordPolicy\",\n    \"DeleteGroup\": \"iam:DeleteGroup\",\n    \"DeleteGroupPolicy\": \"iam:DeleteGroupPolicy\",\n    \"DeleteInstanceProfile\": \"iam:DeleteInstanceProfile\",\n    \"DeleteLoginProfile\": \"iam:DeleteLoginProfile\",\n    \"DeleteOpenIDConnectProvider\": \"iam:DeleteOpenIDConnectProvider\",\n    \"DeletePolicy\": \"iam:DeletePolicy\",\n    \"DeletePolicyVersion\": \"iam:DeletePolicyVersion\",\n    \"DeleteRole\": \"iam:DeleteRole\",\n    \"DeleteRolePermissionsBoundary\": \"iam:DeleteRolePermissionsBoundary\",\n    \"DeleteRolePolicy\": \"iam:DeleteRolePolicy\",\n    \"DeleteSAMLProvider\": \"iam:DeleteSAMLProvider\",\n    \"DeleteSSHPublicKey\": \"iam:DeleteSSHPublicKey\",\n    \"DeleteServerCertificate\": \"iam:DeleteServerCertificate\",\n    \"DeleteServiceLinkedRole\": \"iam:DeleteServiceLinkedRole\",\n    \"DeleteServiceSpecificCredential\": \"iam:DeleteServiceSpecificCredential\",\n    \"DeleteSigningCertificate\": \"iam:DeleteSigningCertificate\",\n    \"DeleteUser\": \"iam:DeleteUser\",\n    \"DeleteUserPermissionsBoundary\": \"iam:DeleteUserPermissionsBoundary\",\n    \"DeleteUserPolicy\": \"iam:DeleteUserPolicy\",\n    \"DeleteVirtualMFADevice\": \"iam:DeleteVirtualMFADevice\",\n    \"DetachGroupPolicy\": \"iam:DetachGroupPolicy\",\n    \"DetachRolePolicy\": \"iam:DetachRolePolicy\",\n    \"DetachUserPolicy\": \"iam:DetachUserPolicy\",\n    \"EnableMFADevice\": \"iam:EnableMFADevice\",\n    \"GenerateCredentialReport\": \"iam:GenerateCredentialReport\",\n    \"GenerateOrganizationsAccessReport\": \"iam:GenerateOrganizationsAccessReport\",\n    \"GenerateServiceLastAccessedDetails\": \"iam:GenerateServiceLastAccessedDetails\",\n    \"GetAccessKeyLastUsed\": \"iam:GetAccessKeyLastUsed\",\n    \"GetAccountAuthorizationDetails\": \"iam:GetAccountAuthorizationDetails\",\n    \"GetAccountPasswordPolicy\": \"iam:GetAccountPasswordPolicy\",\n    \"GetAccountSummary\": \"iam:GetAccountSummary\",\n    \"GetContextKeysForCustomPolicy\": \"iam:GetContextKeysForCustomPolicy\",\n    \"GetContextKeysForPrincipalPolicy\": \"iam:GetContextKeysForPrincipalPolicy\",\n    \"GetCredentialReport\": \"iam:GetCredentialReport\",\n    \"GetGroup\": \"iam:GetGroup\",\n    \"GetGroupPolicy\": \"iam:GetGroupPolicy\",\n    \"GetInstanceProfile\": \"iam:GetInstanceProfile\",\n    \"GetLoginProfile\": \"iam:GetLoginProfile\",\n    \"GetOpenIDConnectProvider\": \"iam:GetOpenIDConnectProvider\",\n    \"GetOrganizationsAccessReport\": \"iam:GetOrganizationsAccessReport\",\n    \"GetPolicy\": \"iam:GetPolicy\",\n    \"GetPolicyVersion\": \"iam:GetPolicyVersion\",\n    \"GetRole\": \"iam:GetRole\",\n    \"GetRolePolicy\": \"iam:GetRolePolicy\",\n    \"GetSAMLProvider\": \"iam:GetSAMLProvider\",\n    \"GetSSHPublicKey\": \"iam:GetSSHPublicKey\",\n    \"GetServerCertificate\": \"iam:GetServerCertificate\",\n    \"GetServiceLastAccessedDetails\": \"iam:GetServiceLastAccessedDetails\",\n    \"GetServiceLastAccessedDetailsWithEntities\": \"iam:GetServiceLastAccessedDetailsWithEntities\",\n    \"GetServiceLinkedRoleDeletionStatus\": \"iam:GetServiceLinkedRoleDeletionStatus\",\n    \"GetUser\": \"iam:GetUser\",\n    \"GetUserPolicy\": \"iam:GetUserPolicy\",\n    \"ListAccessKeys\": \"iam:ListAccessKeys\",\n    \"ListAccountAliases\": \"iam:ListAccountAliases\",\n    \"ListAttachedGroupPolicies\": \"iam:ListAttachedGroupPolicies\",\n    \"ListAttachedRolePolicies\": \"iam:ListAttachedRolePolicies\",\n    \"ListAttachedUserPolicies\": \"iam:ListAttachedUserPolicies\",\n    \"ListEntitiesForPolicy\": \"iam:ListEntitiesForPolicy\",\n    \"ListGroupPolicies\": \"iam:ListGroupPolicies\",\n    \"ListGroups\": \"iam:ListGroups\",\n    \"ListGroupsForUser\": \"iam:ListGroupsForUser\",\n    \"ListInstanceProfileTags\": \"iam:ListInstanceProfileTags\",\n    \"ListInstanceProfiles\": \"iam:ListInstanceProfiles\",\n    \"ListInstanceProfilesForRole\": \"iam:ListInstanceProfilesForRole\",\n    \"ListMFADeviceTags\": \"iam:ListMFADeviceTags\",\n    \"ListMFADevices\": \"iam:ListMFADevices\",\n    \"ListOpenIDConnectProviderTags\": \"iam:ListOpenIDConnectProviderTags\",\n    \"ListOpenIDConnectProviders\": \"iam:ListOpenIDConnectProviders\",\n    \"ListPolicies\": \"iam:ListPolicies\",\n    \"ListPoliciesGrantingServiceAccess\": \"iam:ListPoliciesGrantingServiceAccess\",\n    \"ListPolicyTags\": \"iam:ListPolicyTags\",\n    \"ListPolicyVersions\": \"iam:ListPolicyVersions\",\n    \"ListRolePolicies\": \"iam:ListRolePolicies\",\n    \"ListRoleTags\": \"iam:ListRoleTags\",\n    \"ListRoles\": \"iam:ListRoles\",\n    \"ListSAMLProviderTags\": \"iam:ListSAMLProviderTags\",\n    \"ListSAMLProviders\": \"iam:ListSAMLProviders\",\n    \"ListSSHPublicKeys\": \"iam:ListSSHPublicKeys\",\n    \"ListServerCertificateTags\": \"iam:ListServerCertificateTags\",\n    \"ListServerCertificates\": \"iam:ListServerCertificates\",\n    \"ListServiceSpecificCredentials\": \"iam:ListServiceSpecificCredentials\",\n    \"ListSigningCertificates\": \"iam:ListSigningCertificates\",\n    \"ListUserPolicies\": \"iam:ListUserPolicies\",\n    \"ListUserTags\": \"iam:ListUserTags\",\n    \"ListUsers\": \"iam:ListUsers\",\n    \"ListVirtualMFADevices\": \"iam:ListVirtualMFADevices\",\n    \"PutGroupPolicy\": \"iam:PutGroupPolicy\",\n    \"PutRolePermissionsBoundary\": \"iam:PutRolePermissionsBoundary\",\n    \"PutRolePolicy\": \"iam:PutRolePolicy\",\n    \"PutUserPermissionsBoundary\": \"iam:PutUserPermissionsBoundary\",\n    \"PutUserPolicy\": \"iam:PutUserPolicy\",\n    \"RemoveClientIDFromOpenIDConnectProvider\": \"iam:RemoveClientIDFromOpenIDConnectProvider\",\n    \"RemoveRoleFromInstanceProfile\": \"iam:RemoveRoleFromInstanceProfile\",\n    \"RemoveUserFromGroup\": \"iam:RemoveUserFromGroup\",\n    \"ResetServiceSpecificCredential\": \"iam:ResetServiceSpecificCredential\",\n    \"ResyncMFADevice\": \"iam:ResyncMFADevice\",\n    \"SetDefaultPolicyVersion\": \"iam:SetDefaultPolicyVersion\",\n    \"SetSecurityTokenServicePreferences\": \"iam:SetSecurityTokenServicePreferences\",\n    \"SimulateCustomPolicy\": \"iam:SimulateCustomPolicy\",\n    \"SimulatePrincipalPolicy\": \"iam:SimulatePrincipalPolicy\",\n    \"TagInstanceProfile\": \"iam:TagInstanceProfile\",\n    \"TagMFADevice\": \"iam:TagMFADevice\",\n    \"TagOpenIDConnectProvider\": \"iam:TagOpenIDConnectProvider\",\n    \"TagPolicy\": \"iam:TagPolicy\",\n    \"TagRole\": \"iam:TagRole\",\n    \"TagSAMLProvider\": \"iam:TagSAMLProvider\",\n    \"TagServerCertificate\": \"iam:TagServerCertificate\",\n    \"TagUser\": \"iam:TagUser\",\n    \"UntagInstanceProfile\": \"iam:UntagInstanceProfile\",\n    \"UntagMFADevice\": \"iam:UntagMFADevice\",\n    \"UntagOpenIDConnectProvider\": \"iam:UntagOpenIDConnectProvider\",\n    \"UntagPolicy\": \"iam:UntagPolicy\",\n    \"UntagRole\": \"iam:UntagRole\",\n    \"UntagSAMLProvider\": \"iam:UntagSAMLProvider\",\n    \"UntagServerCertificate\": \"iam:UntagServerCertificate\",\n    \"UntagUser\": \"iam:UntagUser\",\n    \"UpdateAccessKey\": \"iam:UpdateAccessKey\",\n    \"UpdateAccountPasswordPolicy\": \"iam:UpdateAccountPasswordPolicy\",\n    \"UpdateAssumeRolePolicy\": \"iam:UpdateAssumeRolePolicy\",\n    \"UpdateGroup\": \"iam:UpdateGroup\",\n    \"UpdateLoginProfile\": \"iam:UpdateLoginProfile\",\n    \"UpdateOpenIDConnectProviderThumbprint\": \"iam:UpdateOpenIDConnectProviderThumbprint\",\n    \"UpdateRole\": \"iam:UpdateRole\",\n    \"UpdateRoleDescription\": \"iam:UpdateRoleDescription\",\n    \"UpdateSAMLProvider\": \"iam:UpdateSAMLProvider\",\n    \"UpdateSSHPublicKey\": \"iam:UpdateSSHPublicKey\",\n    \"UpdateServerCertificate\": \"iam:UpdateServerCertificate\",\n    \"UpdateServiceSpecificCredential\": \"iam:UpdateServiceSpecificCredential\",\n    \"UpdateSigningCertificate\": \"iam:UpdateSigningCertificate\",\n    \"UpdateUser\": \"iam:UpdateUser\",\n    \"UploadSSHPublicKey\": \"iam:UploadSSHPublicKey\",\n    \"UploadServerCertificate\": \"iam:UploadServerCertificate\",\n    \"UploadSigningCertificate\": \"iam:UploadSigningCertificate\"\n  },\n  \"identitystore\": {\n    \"DescribeGroup\": \"identitystore:DescribeGroup\",\n    \"DescribeUser\": \"identitystore:DescribeUser\",\n    \"ListGroups\": \"identitystore:ListGroups\",\n    \"ListUsers\": \"identitystore:ListUsers\"\n  },\n  \"imagebuilder\": {\n    \"CancelImageCreation\": \"imagebuilder:CancelImageCreation\",\n    \"CreateComponent\": \"imagebuilder:CreateComponent\",\n    \"CreateContainerRecipe\": \"imagebuilder:CreateContainerRecipe\",\n    \"CreateDistributionConfiguration\": \"imagebuilder:CreateDistributionConfiguration\",\n    \"CreateImage\": \"imagebuilder:CreateImage\",\n    \"CreateImagePipeline\": \"imagebuilder:CreateImagePipeline\",\n    \"CreateImageRecipe\": \"imagebuilder:CreateImageRecipe\",\n    \"CreateInfrastructureConfiguration\": \"imagebuilder:CreateInfrastructureConfiguration\",\n    \"DeleteComponent\": \"imagebuilder:DeleteComponent\",\n    \"DeleteContainerRecipe\": \"imagebuilder:DeleteContainerRecipe\",\n    \"DeleteDistributionConfiguration\": \"imagebuilder:DeleteDistributionConfiguration\",\n    \"DeleteImage\": \"imagebuilder:DeleteImage\",\n    \"DeleteImagePipeline\": \"imagebuilder:DeleteImagePipeline\",\n    \"DeleteImageRecipe\": \"imagebuilder:DeleteImageRecipe\",\n    \"DeleteInfrastructureConfiguration\": \"imagebuilder:DeleteInfrastructureConfiguration\",\n    \"GetComponent\": \"imagebuilder:GetComponent\",\n    \"GetComponentPolicy\": \"imagebuilder:GetComponentPolicy\",\n    \"GetContainerRecipe\": \"imagebuilder:GetContainerRecipe\",\n    \"GetContainerRecipePolicy\": \"imagebuilder:GetContainerRecipePolicy\",\n    \"GetDistributionConfiguration\": \"imagebuilder:GetDistributionConfiguration\",\n    \"GetImage\": \"imagebuilder:GetImage\",\n    \"GetImagePipeline\": \"imagebuilder:GetImagePipeline\",\n    \"GetImagePolicy\": \"imagebuilder:GetImagePolicy\",\n    \"GetImageRecipe\": \"imagebuilder:GetImageRecipe\",\n    \"GetImageRecipePolicy\": \"imagebuilder:GetImageRecipePolicy\",\n    \"GetInfrastructureConfiguration\": \"imagebuilder:GetInfrastructureConfiguration\",\n    \"ImportComponent\": \"imagebuilder:ImportComponent\",\n    \"ListComponentBuildVersions\": \"imagebuilder:ListComponentBuildVersions\",\n    \"ListComponents\": \"imagebuilder:ListComponents\",\n    \"ListContainerRecipes\": \"imagebuilder:ListContainerRecipes\",\n    \"ListDistributionConfigurations\": \"imagebuilder:ListDistributionConfigurations\",\n    \"ListImageBuildVersions\": \"imagebuilder:ListImageBuildVersions\",\n    \"ListImagePackages\": \"imagebuilder:ListImagePackages\",\n    \"ListImagePipelineImages\": \"imagebuilder:ListImagePipelineImages\",\n    \"ListImagePipelines\": \"imagebuilder:ListImagePipelines\",\n    \"ListImageRecipes\": \"imagebuilder:ListImageRecipes\",\n    \"ListImages\": \"imagebuilder:ListImages\",\n    \"ListInfrastructureConfigurations\": \"imagebuilder:ListInfrastructureConfigurations\",\n    \"ListTagsForResource\": \"imagebuilder:ListTagsForResource\",\n    \"PutComponentPolicy\": \"imagebuilder:PutComponentPolicy\",\n    \"PutContainerRecipePolicy\": \"imagebuilder:PutContainerRecipePolicy\",\n    \"PutImagePolicy\": \"imagebuilder:PutImagePolicy\",\n    \"PutImageRecipePolicy\": \"imagebuilder:PutImageRecipePolicy\",\n    \"StartImagePipelineExecution\": \"imagebuilder:StartImagePipelineExecution\",\n    \"TagResource\": \"imagebuilder:TagResource\",\n    \"UntagResource\": \"imagebuilder:UntagResource\",\n    \"UpdateDistributionConfiguration\": \"imagebuilder:UpdateDistributionConfiguration\",\n    \"UpdateImagePipeline\": \"imagebuilder:UpdateImagePipeline\",\n    \"UpdateInfrastructureConfiguration\": \"imagebuilder:UpdateInfrastructureConfiguration\"\n  },\n  \"importexport\": {\n    \"CancelJob\": \"importexport:CancelJob\",\n    \"CreateJob\": \"importexport:CreateJob\",\n    \"GetShippingLabel\": \"importexport:GetShippingLabel\",\n    \"GetStatus\": \"importexport:GetStatus\",\n    \"ListJobs\": \"importexport:ListJobs\",\n    \"UpdateJob\": \"importexport:UpdateJob\"\n  },\n  \"inspector\": {\n    \"AddAttributesToFindings\": \"inspector:AddAttributesToFindings\",\n    \"CreateAssessmentTarget\": \"inspector:CreateAssessmentTarget\",\n    \"CreateAssessmentTemplate\": \"inspector:CreateAssessmentTemplate\",\n    \"CreateExclusionsPreview\": \"inspector:CreateExclusionsPreview\",\n    \"CreateResourceGroup\": \"inspector:CreateResourceGroup\",\n    \"DeleteAssessmentRun\": \"inspector:DeleteAssessmentRun\",\n    \"DeleteAssessmentTarget\": \"inspector:DeleteAssessmentTarget\",\n    \"DeleteAssessmentTemplate\": \"inspector:DeleteAssessmentTemplate\",\n    \"DescribeAssessmentRuns\": \"inspector:DescribeAssessmentRuns\",\n    \"DescribeAssessmentTargets\": \"inspector:DescribeAssessmentTargets\",\n    \"DescribeAssessmentTemplates\": \"inspector:DescribeAssessmentTemplates\",\n    \"DescribeCrossAccountAccessRole\": \"inspector:DescribeCrossAccountAccessRole\",\n    \"DescribeExclusions\": \"inspector:DescribeExclusions\",\n    \"DescribeFindings\": \"inspector:DescribeFindings\",\n    \"DescribeResourceGroups\": \"inspector:DescribeResourceGroups\",\n    \"DescribeRulesPackages\": \"inspector:DescribeRulesPackages\",\n    \"GetAssessmentReport\": \"inspector:GetAssessmentReport\",\n    \"GetExclusionsPreview\": \"inspector:GetExclusionsPreview\",\n    \"GetTelemetryMetadata\": \"inspector:GetTelemetryMetadata\",\n    \"ListAssessmentRunAgents\": \"inspector:ListAssessmentRunAgents\",\n    \"ListAssessmentRuns\": \"inspector:ListAssessmentRuns\",\n    \"ListAssessmentTargets\": \"inspector:ListAssessmentTargets\",\n    \"ListAssessmentTemplates\": \"inspector:ListAssessmentTemplates\",\n    \"ListEventSubscriptions\": \"inspector:ListEventSubscriptions\",\n    \"ListExclusions\": \"inspector:ListExclusions\",\n    \"ListFindings\": \"inspector:ListFindings\",\n    \"ListRulesPackages\": \"inspector:ListRulesPackages\",\n    \"ListTagsForResource\": \"inspector:ListTagsForResource\",\n    \"PreviewAgents\": \"inspector:PreviewAgents\",\n    \"RegisterCrossAccountAccessRole\": \"inspector:RegisterCrossAccountAccessRole\",\n    \"RemoveAttributesFromFindings\": \"inspector:RemoveAttributesFromFindings\",\n    \"SetTagsForResource\": \"inspector:SetTagsForResource\",\n    \"StartAssessmentRun\": \"inspector:StartAssessmentRun\",\n    \"StopAssessmentRun\": \"inspector:StopAssessmentRun\",\n    \"SubscribeToEvent\": \"inspector:SubscribeToEvent\",\n    \"UnsubscribeFromEvent\": \"inspector:UnsubscribeFromEvent\",\n    \"UpdateAssessmentTarget\": \"inspector:UpdateAssessmentTarget\"\n  },\n  \"inspector2\": {\n    \"AssociateMember\": \"inspector2:AssociateMember\",\n    \"BatchGetAccountStatus\": \"inspector2:BatchGetAccountStatus\",\n    \"BatchGetFreeTrialInfo\": \"inspector2:BatchGetFreeTrialInfo\",\n    \"CancelFindingsReport\": \"inspector2:CancelFindingsReport\",\n    \"CreateFilter\": \"inspector2:CreateFilter\",\n    \"CreateFindingsReport\": \"inspector2:CreateFindingsReport\",\n    \"DeleteFilter\": \"inspector2:DeleteFilter\",\n    \"DescribeOrganizationConfiguration\": \"inspector2:DescribeOrganizationConfiguration\",\n    \"Disable\": \"inspector2:Disable\",\n    \"DisableDelegatedAdminAccount\": \"inspector2:DisableDelegatedAdminAccount\",\n    \"DisassociateMember\": \"inspector2:DisassociateMember\",\n    \"Enable\": \"inspector2:Enable\",\n    \"EnableDelegatedAdminAccount\": \"inspector2:EnableDelegatedAdminAccount\",\n    \"GetDelegatedAdminAccount\": \"inspector2:GetDelegatedAdminAccount\",\n    \"GetFindingsReportStatus\": \"inspector2:GetFindingsReportStatus\",\n    \"GetMember\": \"inspector2:GetMember\",\n    \"ListAccountPermissions\": \"inspector2:ListAccountPermissions\",\n    \"ListCoverage\": \"inspector2:ListCoverage\",\n    \"ListCoverageStatistics\": \"inspector2:ListCoverageStatistics\",\n    \"ListDelegatedAdminAccounts\": \"inspector2:ListDelegatedAdminAccounts\",\n    \"ListFilters\": \"inspector2:ListFilters\",\n    \"ListFindingAggregations\": \"inspector2:ListFindingAggregations\",\n    \"ListFindings\": \"inspector2:ListFindings\",\n    \"ListMembers\": \"inspector2:ListMembers\",\n    \"ListTagsForResource\": \"inspector2:ListTagsForResource\",\n    \"ListUsageTotals\": \"inspector2:ListUsageTotals\",\n    \"TagResource\": \"inspector2:TagResource\",\n    \"UntagResource\": \"inspector2:UntagResource\",\n    \"UpdateFilter\": \"inspector2:UpdateFilter\",\n    \"UpdateOrganizationConfiguration\": \"inspector2:UpdateOrganizationConfiguration\"\n  },\n  \"iot\": {\n    \"AcceptCertificateTransfer\": \"iot:AcceptCertificateTransfer\",\n    \"AddThingToBillingGroup\": \"iot:AddThingToBillingGroup\",\n    \"AddThingToThingGroup\": \"iot:AddThingToThingGroup\",\n    \"AssociateTargetsWithJob\": \"iot:AssociateTargetsWithJob\",\n    \"AttachPolicy\": \"iot:AttachPolicy\",\n    \"AttachPrincipalPolicy\": \"iot:AttachPrincipalPolicy\",\n    \"AttachSecurityProfile\": \"iot:AttachSecurityProfile\",\n    \"AttachThingPrincipal\": \"iot:AttachThingPrincipal\",\n    \"CancelAuditMitigationActionsTask\": \"iot:CancelAuditMitigationActionsTask\",\n    \"CancelAuditTask\": \"iot:CancelAuditTask\",\n    \"CancelCertificateTransfer\": \"iot:CancelCertificateTransfer\",\n    \"CancelDetectMitigationActionsTask\": \"iot:CancelDetectMitigationActionsTask\",\n    \"CancelJob\": \"iot:CancelJob\",\n    \"CancelJobExecution\": \"iot:CancelJobExecution\",\n    \"ClearDefaultAuthorizer\": \"iot:ClearDefaultAuthorizer\",\n    \"ConfirmTopicRuleDestination\": \"iot:ConfirmTopicRuleDestination\",\n    \"CreateAuditSuppression\": \"iot:CreateAuditSuppression\",\n    \"CreateAuthorizer\": \"iot:CreateAuthorizer\",\n    \"CreateBillingGroup\": \"iot:CreateBillingGroup\",\n    \"CreateCertificateFromCsr\": \"iot:CreateCertificateFromCsr\",\n    \"CreateCustomMetric\": \"iot:CreateCustomMetric\",\n    \"CreateDimension\": \"iot:CreateDimension\",\n    \"CreateDomainConfiguration\": \"iot:CreateDomainConfiguration\",\n    \"CreateDynamicThingGroup\": \"iot:CreateDynamicThingGroup\",\n    \"CreateFleetMetric\": \"iot:CreateFleetMetric\",\n    \"CreateJob\": \"iot:CreateJob\",\n    \"CreateJobTemplate\": \"iot:CreateJobTemplate\",\n    \"CreateKeysAndCertificate\": \"iot:CreateKeysAndCertificate\",\n    \"CreateMitigationAction\": \"iot:CreateMitigationAction\",\n    \"CreateOTAUpdate\": \"iot:CreateOTAUpdate\",\n    \"CreatePolicy\": \"iot:CreatePolicy\",\n    \"CreatePolicyVersion\": \"iot:CreatePolicyVersion\",\n    \"CreateProvisioningClaim\": \"iot:CreateProvisioningClaim\",\n    \"CreateProvisioningTemplate\": \"iot:CreateProvisioningTemplate\",\n    \"CreateProvisioningTemplateVersion\": \"iot:CreateProvisioningTemplateVersion\",\n    \"CreateRoleAlias\": \"iot:CreateRoleAlias\",\n    \"CreateScheduledAudit\": \"iot:CreateScheduledAudit\",\n    \"CreateSecurityProfile\": \"iot:CreateSecurityProfile\",\n    \"CreateStream\": \"iot:CreateStream\",\n    \"CreateThing\": \"iot:CreateThing\",\n    \"CreateThingGroup\": \"iot:CreateThingGroup\",\n    \"CreateThingType\": \"iot:CreateThingType\",\n    \"CreateTopicRule\": \"iot:CreateTopicRule\",\n    \"CreateTopicRuleDestination\": \"iot:CreateTopicRuleDestination\",\n    \"DeleteAccountAuditConfiguration\": \"iot:DeleteAccountAuditConfiguration\",\n    \"DeleteAuditSuppression\": \"iot:DeleteAuditSuppression\",\n    \"DeleteAuthorizer\": \"iot:DeleteAuthorizer\",\n    \"DeleteBillingGroup\": \"iot:DeleteBillingGroup\",\n    \"DeleteCACertificate\": \"iot:DeleteCACertificate\",\n    \"DeleteCertificate\": \"iot:DeleteCertificate\",\n    \"DeleteCustomMetric\": \"iot:DeleteCustomMetric\",\n    \"DeleteDimension\": \"iot:DeleteDimension\",\n    \"DeleteDomainConfiguration\": \"iot:DeleteDomainConfiguration\",\n    \"DeleteDynamicThingGroup\": \"iot:DeleteDynamicThingGroup\",\n    \"DeleteFleetMetric\": \"iot:DeleteFleetMetric\",\n    \"DeleteJob\": \"iot:DeleteJob\",\n    \"DeleteJobExecution\": \"iot:DeleteJobExecution\",\n    \"DeleteJobTemplate\": \"iot:DeleteJobTemplate\",\n    \"DeleteMitigationAction\": \"iot:DeleteMitigationAction\",\n    \"DeleteOTAUpdate\": \"iot:DeleteOTAUpdate\",\n    \"DeletePolicy\": \"iot:DeletePolicy\",\n    \"DeletePolicyVersion\": \"iot:DeletePolicyVersion\",\n    \"DeleteProvisioningTemplate\": \"iot:DeleteProvisioningTemplate\",\n    \"DeleteProvisioningTemplateVersion\": \"iot:DeleteProvisioningTemplateVersion\",\n    \"DeleteRegistrationCode\": \"iot:DeleteRegistrationCode\",\n    \"DeleteRoleAlias\": \"iot:DeleteRoleAlias\",\n    \"DeleteScheduledAudit\": \"iot:DeleteScheduledAudit\",\n    \"DeleteSecurityProfile\": \"iot:DeleteSecurityProfile\",\n    \"DeleteStream\": \"iot:DeleteStream\",\n    \"DeleteThing\": \"iot:DeleteThing\",\n    \"DeleteThingGroup\": \"iot:DeleteThingGroup\",\n    \"DeleteThingType\": \"iot:DeleteThingType\",\n    \"DeleteTopicRule\": \"iot:DeleteTopicRule\",\n    \"DeleteTopicRuleDestination\": \"iot:DeleteTopicRuleDestination\",\n    \"DeleteV2LoggingLevel\": \"iot:DeleteV2LoggingLevel\",\n    \"DeprecateThingType\": \"iot:DeprecateThingType\",\n    \"DescribeAccountAuditConfiguration\": \"iot:DescribeAccountAuditConfiguration\",\n    \"DescribeAuditFinding\": \"iot:DescribeAuditFinding\",\n    \"DescribeAuditMitigationActionsTask\": \"iot:DescribeAuditMitigationActionsTask\",\n    \"DescribeAuditSuppression\": \"iot:DescribeAuditSuppression\",\n    \"DescribeAuditTask\": \"iot:DescribeAuditTask\",\n    \"DescribeAuthorizer\": \"iot:DescribeAuthorizer\",\n    \"DescribeBillingGroup\": \"iot:DescribeBillingGroup\",\n    \"DescribeCACertificate\": \"iot:DescribeCACertificate\",\n    \"DescribeCertificate\": \"iot:DescribeCertificate\",\n    \"DescribeCustomMetric\": \"iot:DescribeCustomMetric\",\n    \"DescribeDefaultAuthorizer\": \"iot:DescribeDefaultAuthorizer\",\n    \"DescribeDetectMitigationActionsTask\": \"iot:DescribeDetectMitigationActionsTask\",\n    \"DescribeDimension\": \"iot:DescribeDimension\",\n    \"DescribeDomainConfiguration\": \"iot:DescribeDomainConfiguration\",\n    \"DescribeEndpoint\": \"iot:DescribeEndpoint\",\n    \"DescribeEventConfigurations\": \"iot:DescribeEventConfigurations\",\n    \"DescribeFleetMetric\": \"iot:DescribeFleetMetric\",\n    \"DescribeIndex\": \"iot:DescribeIndex\",\n    \"DescribeJob\": \"iot:DescribeJob\",\n    \"DescribeJobExecution\": \"iot:DescribeJobExecution\",\n    \"DescribeJobTemplate\": \"iot:DescribeJobTemplate\",\n    \"DescribeManagedJobTemplate\": \"iot:DescribeManagedJobTemplate\",\n    \"DescribeMitigationAction\": \"iot:DescribeMitigationAction\",\n    \"DescribeProvisioningTemplate\": \"iot:DescribeProvisioningTemplate\",\n    \"DescribeProvisioningTemplateVersion\": \"iot:DescribeProvisioningTemplateVersion\",\n    \"DescribeRoleAlias\": \"iot:DescribeRoleAlias\",\n    \"DescribeScheduledAudit\": \"iot:DescribeScheduledAudit\",\n    \"DescribeSecurityProfile\": \"iot:DescribeSecurityProfile\",\n    \"DescribeStream\": \"iot:DescribeStream\",\n    \"DescribeThing\": \"iot:DescribeThing\",\n    \"DescribeThingGroup\": \"iot:DescribeThingGroup\",\n    \"DescribeThingRegistrationTask\": \"iot:DescribeThingRegistrationTask\",\n    \"DescribeThingType\": \"iot:DescribeThingType\",\n    \"DetachPolicy\": \"iot:DetachPolicy\",\n    \"DetachPrincipalPolicy\": \"iot:DetachPrincipalPolicy\",\n    \"DetachSecurityProfile\": \"iot:DetachSecurityProfile\",\n    \"DetachThingPrincipal\": \"iot:DetachThingPrincipal\",\n    \"DisableTopicRule\": \"iot:DisableTopicRule\",\n    \"EnableTopicRule\": \"iot:EnableTopicRule\",\n    \"GetBehaviorModelTrainingSummaries\": \"iot:GetBehaviorModelTrainingSummaries\",\n    \"GetBucketsAggregation\": \"iot:GetBucketsAggregation\",\n    \"GetCardinality\": \"iot:GetCardinality\",\n    \"GetEffectivePolicies\": \"iot:GetEffectivePolicies\",\n    \"GetIndexingConfiguration\": \"iot:GetIndexingConfiguration\",\n    \"GetJobDocument\": \"iot:GetJobDocument\",\n    \"GetLoggingOptions\": \"iot:GetLoggingOptions\",\n    \"GetOTAUpdate\": \"iot:GetOTAUpdate\",\n    \"GetPercentiles\": \"iot:GetPercentiles\",\n    \"GetPolicy\": \"iot:GetPolicy\",\n    \"GetPolicyVersion\": \"iot:GetPolicyVersion\",\n    \"GetRegistrationCode\": \"iot:GetRegistrationCode\",\n    \"GetStatistics\": \"iot:GetStatistics\",\n    \"GetTopicRule\": \"iot:GetTopicRule\",\n    \"GetTopicRuleDestination\": \"iot:GetTopicRuleDestination\",\n    \"GetV2LoggingOptions\": \"iot:GetV2LoggingOptions\",\n    \"ListActiveViolations\": \"iot:ListActiveViolations\",\n    \"ListAttachedPolicies\": \"iot:ListAttachedPolicies\",\n    \"ListAuditFindings\": \"iot:ListAuditFindings\",\n    \"ListAuditMitigationActionsExecutions\": \"iot:ListAuditMitigationActionsExecutions\",\n    \"ListAuditMitigationActionsTasks\": \"iot:ListAuditMitigationActionsTasks\",\n    \"ListAuditSuppressions\": \"iot:ListAuditSuppressions\",\n    \"ListAuditTasks\": \"iot:ListAuditTasks\",\n    \"ListAuthorizers\": \"iot:ListAuthorizers\",\n    \"ListBillingGroups\": \"iot:ListBillingGroups\",\n    \"ListCACertificates\": \"iot:ListCACertificates\",\n    \"ListCertificates\": \"iot:ListCertificates\",\n    \"ListCertificatesByCA\": \"iot:ListCertificatesByCA\",\n    \"ListCustomMetrics\": \"iot:ListCustomMetrics\",\n    \"ListDetectMitigationActionsExecutions\": \"iot:ListDetectMitigationActionsExecutions\",\n    \"ListDetectMitigationActionsTasks\": \"iot:ListDetectMitigationActionsTasks\",\n    \"ListDimensions\": \"iot:ListDimensions\",\n    \"ListDomainConfigurations\": \"iot:ListDomainConfigurations\",\n    \"ListFleetMetrics\": \"iot:ListFleetMetrics\",\n    \"ListIndices\": \"iot:ListIndices\",\n    \"ListJobExecutionsForJob\": \"iot:ListJobExecutionsForJob\",\n    \"ListJobExecutionsForThing\": \"iot:ListJobExecutionsForThing\",\n    \"ListJobTemplates\": \"iot:ListJobTemplates\",\n    \"ListJobs\": \"iot:ListJobs\",\n    \"ListManagedJobTemplates\": \"iot:ListManagedJobTemplates\",\n    \"ListMitigationActions\": \"iot:ListMitigationActions\",\n    \"ListOTAUpdates\": \"iot:ListOTAUpdates\",\n    \"ListOutgoingCertificates\": \"iot:ListOutgoingCertificates\",\n    \"ListPolicies\": \"iot:ListPolicies\",\n    \"ListPolicyPrincipals\": \"iot:ListPolicyPrincipals\",\n    \"ListPolicyVersions\": \"iot:ListPolicyVersions\",\n    \"ListPrincipalPolicies\": \"iot:ListPrincipalPolicies\",\n    \"ListPrincipalThings\": \"iot:ListPrincipalThings\",\n    \"ListProvisioningTemplateVersions\": \"iot:ListProvisioningTemplateVersions\",\n    \"ListProvisioningTemplates\": \"iot:ListProvisioningTemplates\",\n    \"ListRoleAliases\": \"iot:ListRoleAliases\",\n    \"ListScheduledAudits\": \"iot:ListScheduledAudits\",\n    \"ListSecurityProfiles\": \"iot:ListSecurityProfiles\",\n    \"ListSecurityProfilesForTarget\": \"iot:ListSecurityProfilesForTarget\",\n    \"ListStreams\": \"iot:ListStreams\",\n    \"ListTagsForResource\": \"iot:ListTagsForResource\",\n    \"ListTargetsForPolicy\": \"iot:ListTargetsForPolicy\",\n    \"ListTargetsForSecurityProfile\": \"iot:ListTargetsForSecurityProfile\",\n    \"ListThingGroups\": \"iot:ListThingGroups\",\n    \"ListThingGroupsForThing\": \"iot:ListThingGroupsForThing\",\n    \"ListThingPrincipals\": \"iot:ListThingPrincipals\",\n    \"ListThingRegistrationTaskReports\": \"iot:ListThingRegistrationTaskReports\",\n    \"ListThingRegistrationTasks\": \"iot:ListThingRegistrationTasks\",\n    \"ListThingTypes\": \"iot:ListThingTypes\",\n    \"ListThings\": \"iot:ListThings\",\n    \"ListThingsInBillingGroup\": \"iot:ListThingsInBillingGroup\",\n    \"ListThingsInThingGroup\": \"iot:ListThingsInThingGroup\",\n    \"ListTopicRuleDestinations\": \"iot:ListTopicRuleDestinations\",\n    \"ListTopicRules\": \"iot:ListTopicRules\",\n    \"ListV2LoggingLevels\": \"iot:ListV2LoggingLevels\",\n    \"ListViolationEvents\": \"iot:ListViolationEvents\",\n    \"RegisterCACertificate\": \"iot:RegisterCACertificate\",\n    \"RegisterCertificate\": \"iot:RegisterCertificate\",\n    \"RegisterCertificateWithoutCA\": \"iot:RegisterCertificateWithoutCA\",\n    \"RegisterThing\": \"iot:RegisterThing\",\n    \"RejectCertificateTransfer\": \"iot:RejectCertificateTransfer\",\n    \"RemoveThingFromBillingGroup\": \"iot:RemoveThingFromBillingGroup\",\n    \"RemoveThingFromThingGroup\": \"iot:RemoveThingFromThingGroup\",\n    \"ReplaceTopicRule\": \"iot:ReplaceTopicRule\",\n    \"SearchIndex\": \"iot:SearchIndex\",\n    \"SetDefaultAuthorizer\": \"iot:SetDefaultAuthorizer\",\n    \"SetDefaultPolicyVersion\": \"iot:SetDefaultPolicyVersion\",\n    \"SetLoggingOptions\": \"iot:SetLoggingOptions\",\n    \"SetV2LoggingLevel\": \"iot:SetV2LoggingLevel\",\n    \"SetV2LoggingOptions\": \"iot:SetV2LoggingOptions\",\n    \"StartAuditMitigationActionsTask\": \"iot:StartAuditMitigationActionsTask\",\n    \"StartDetectMitigationActionsTask\": \"iot:StartDetectMitigationActionsTask\",\n    \"StartOnDemandAuditTask\": \"iot:StartOnDemandAuditTask\",\n    \"StartThingRegistrationTask\": \"iot:StartThingRegistrationTask\",\n    \"StopThingRegistrationTask\": \"iot:StopThingRegistrationTask\",\n    \"TagResource\": \"iot:TagResource\",\n    \"TestAuthorization\": \"iot:TestAuthorization\",\n    \"TestInvokeAuthorizer\": \"iot:TestInvokeAuthorizer\",\n    \"TransferCertificate\": \"iot:TransferCertificate\",\n    \"UntagResource\": \"iot:UntagResource\",\n    \"UpdateAccountAuditConfiguration\": \"iot:UpdateAccountAuditConfiguration\",\n    \"UpdateAuditSuppression\": \"iot:UpdateAuditSuppression\",\n    \"UpdateAuthorizer\": \"iot:UpdateAuthorizer\",\n    \"UpdateBillingGroup\": \"iot:UpdateBillingGroup\",\n    \"UpdateCACertificate\": \"iot:UpdateCACertificate\",\n    \"UpdateCertificate\": \"iot:UpdateCertificate\",\n    \"UpdateCustomMetric\": \"iot:UpdateCustomMetric\",\n    \"UpdateDimension\": \"iot:UpdateDimension\",\n    \"UpdateDomainConfiguration\": \"iot:UpdateDomainConfiguration\",\n    \"UpdateDynamicThingGroup\": \"iot:UpdateDynamicThingGroup\",\n    \"UpdateEventConfigurations\": \"iot:UpdateEventConfigurations\",\n    \"UpdateFleetMetric\": \"iot:UpdateFleetMetric\",\n    \"UpdateIndexingConfiguration\": \"iot:UpdateIndexingConfiguration\",\n    \"UpdateJob\": \"iot:UpdateJob\",\n    \"UpdateMitigationAction\": \"iot:UpdateMitigationAction\",\n    \"UpdateProvisioningTemplate\": \"iot:UpdateProvisioningTemplate\",\n    \"UpdateRoleAlias\": \"iot:UpdateRoleAlias\",\n    \"UpdateScheduledAudit\": \"iot:UpdateScheduledAudit\",\n    \"UpdateSecurityProfile\": \"iot:UpdateSecurityProfile\",\n    \"UpdateStream\": \"iot:UpdateStream\",\n    \"UpdateThing\": \"iot:UpdateThing\",\n    \"UpdateThingGroup\": \"iot:UpdateThingGroup\",\n    \"UpdateThingGroupsForThing\": \"iot:UpdateThingGroupsForThing\",\n    \"UpdateTopicRuleDestination\": \"iot:UpdateTopicRuleDestination\",\n    \"ValidateSecurityProfileBehaviors\": \"iot:ValidateSecurityProfileBehaviors\"\n  },\n  \"iot-data\": {\n    \"DeleteThingShadow\": \"iot:DeleteThingShadow\",\n    \"GetRetainedMessage\": \"iot:GetRetainedMessage\",\n    \"GetThingShadow\": \"iot:GetThingShadow\",\n    \"ListNamedShadowsForThing\": \"iot:ListNamedShadowsForThing\",\n    \"ListRetainedMessages\": \"iot:ListRetainedMessages\",\n    \"Publish\": \"iot:Publish\",\n    \"UpdateThingShadow\": \"iot:UpdateThingShadow\"\n  },\n  \"iot-jobs-data\": {\n    \"DescribeJobExecution\": \"iot:DescribeJobExecution\",\n    \"GetPendingJobExecutions\": \"iot:GetPendingJobExecutions\",\n    \"StartNextPendingJobExecution\": \"iot:StartNextPendingJobExecution\",\n    \"UpdateJobExecution\": \"iot:UpdateJobExecution\"\n  },\n  \"iotanalytics\": {\n    \"BatchPutMessage\": \"iotanalytics:BatchPutMessage\",\n    \"CancelPipelineReprocessing\": \"iotanalytics:CancelPipelineReprocessing\",\n    \"CreateChannel\": \"iotanalytics:CreateChannel\",\n    \"CreateDataset\": \"iotanalytics:CreateDataset\",\n    \"CreateDatasetContent\": \"iotanalytics:CreateDatasetContent\",\n    \"CreateDatastore\": \"iotanalytics:CreateDatastore\",\n    \"CreatePipeline\": \"iotanalytics:CreatePipeline\",\n    \"DeleteChannel\": \"iotanalytics:DeleteChannel\",\n    \"DeleteDataset\": \"iotanalytics:DeleteDataset\",\n    \"DeleteDatasetContent\": \"iotanalytics:DeleteDatasetContent\",\n    \"DeleteDatastore\": \"iotanalytics:DeleteDatastore\",\n    \"DeletePipeline\": \"iotanalytics:DeletePipeline\",\n    \"DescribeChannel\": \"iotanalytics:DescribeChannel\",\n    \"DescribeDataset\": \"iotanalytics:DescribeDataset\",\n    \"DescribeDatastore\": \"iotanalytics:DescribeDatastore\",\n    \"DescribeLoggingOptions\": \"iotanalytics:DescribeLoggingOptions\",\n    \"DescribePipeline\": \"iotanalytics:DescribePipeline\",\n    \"GetDatasetContent\": \"iotanalytics:GetDatasetContent\",\n    \"ListChannels\": \"iotanalytics:ListChannels\",\n    \"ListDatasetContents\": \"iotanalytics:ListDatasetContents\",\n    \"ListDatasets\": \"iotanalytics:ListDatasets\",\n    \"ListDatastores\": \"iotanalytics:ListDatastores\",\n    \"ListPipelines\": \"iotanalytics:ListPipelines\",\n    \"ListTagsForResource\": \"iotanalytics:ListTagsForResource\",\n    \"PutLoggingOptions\": \"iotanalytics:PutLoggingOptions\",\n    \"RunPipelineActivity\": \"iotanalytics:RunPipelineActivity\",\n    \"SampleChannelData\": \"iotanalytics:SampleChannelData\",\n    \"StartPipelineReprocessing\": \"iotanalytics:StartPipelineReprocessing\",\n    \"TagResource\": \"iotanalytics:TagResource\",\n    \"UntagResource\": \"iotanalytics:UntagResource\",\n    \"UpdateChannel\": \"iotanalytics:UpdateChannel\",\n    \"UpdateDataset\": \"iotanalytics:UpdateDataset\",\n    \"UpdateDatastore\": \"iotanalytics:UpdateDatastore\",\n    \"UpdatePipeline\": \"iotanalytics:UpdatePipeline\"\n  },\n  \"iotdeviceadvisor\": {\n    \"CreateSuiteDefinition\": \"iotdeviceadvisor:CreateSuiteDefinition\",\n    \"DeleteSuiteDefinition\": \"iotdeviceadvisor:DeleteSuiteDefinition\",\n    \"GetSuiteDefinition\": \"iotdeviceadvisor:GetSuiteDefinition\",\n    \"GetSuiteRun\": \"iotdeviceadvisor:GetSuiteRun\",\n    \"GetSuiteRunReport\": \"iotdeviceadvisor:GetSuiteRunReport\",\n    \"ListSuiteDefinitions\": \"iotdeviceadvisor:ListSuiteDefinitions\",\n    \"ListSuiteRuns\": \"iotdeviceadvisor:ListSuiteRuns\",\n    \"ListTagsForResource\": \"iotdeviceadvisor:ListTagsForResource\",\n    \"StartSuiteRun\": \"iotdeviceadvisor:StartSuiteRun\",\n    \"StopSuiteRun\": \"iotdeviceadvisor:StopSuiteRun\",\n    \"TagResource\": \"iotdeviceadvisor:TagResource\",\n    \"UntagResource\": \"iotdeviceadvisor:UntagResource\",\n    \"UpdateSuiteDefinition\": \"iotdeviceadvisor:UpdateSuiteDefinition\"\n  },\n  \"iotevents\": {\n    \"CreateAlarmModel\": \"iotevents:CreateAlarmModel\",\n    \"CreateDetectorModel\": \"iotevents:CreateDetectorModel\",\n    \"CreateInput\": \"iotevents:CreateInput\",\n    \"DeleteAlarmModel\": \"iotevents:DeleteAlarmModel\",\n    \"DeleteDetectorModel\": \"iotevents:DeleteDetectorModel\",\n    \"DeleteInput\": \"iotevents:DeleteInput\",\n    \"DescribeAlarmModel\": \"iotevents:DescribeAlarmModel\",\n    \"DescribeDetectorModel\": \"iotevents:DescribeDetectorModel\",\n    \"DescribeDetectorModelAnalysis\": \"iotevents:DescribeDetectorModelAnalysis\",\n    \"DescribeInput\": \"iotevents:DescribeInput\",\n    \"DescribeLoggingOptions\": \"iotevents:DescribeLoggingOptions\",\n    \"GetDetectorModelAnalysisResults\": \"iotevents:GetDetectorModelAnalysisResults\",\n    \"ListAlarmModelVersions\": \"iotevents:ListAlarmModelVersions\",\n    \"ListAlarmModels\": \"iotevents:ListAlarmModels\",\n    \"ListDetectorModelVersions\": \"iotevents:ListDetectorModelVersions\",\n    \"ListDetectorModels\": \"iotevents:ListDetectorModels\",\n    \"ListInputRoutings\": \"iotevents:ListInputRoutings\",\n    \"ListInputs\": \"iotevents:ListInputs\",\n    \"ListTagsForResource\": \"iotevents:ListTagsForResource\",\n    \"PutLoggingOptions\": \"iotevents:PutLoggingOptions\",\n    \"StartDetectorModelAnalysis\": \"iotevents:StartDetectorModelAnalysis\",\n    \"TagResource\": \"iotevents:TagResource\",\n    \"UntagResource\": \"iotevents:UntagResource\",\n    \"UpdateAlarmModel\": \"iotevents:UpdateAlarmModel\",\n    \"UpdateDetectorModel\": \"iotevents:UpdateDetectorModel\",\n    \"UpdateInput\": \"iotevents:UpdateInput\"\n  },\n  \"iotfleethub\": {\n    \"CreateApplication\": \"iotfleethub:CreateApplication\",\n    \"DeleteApplication\": \"iotfleethub:DeleteApplication\",\n    \"DescribeApplication\": \"iotfleethub:DescribeApplication\",\n    \"ListApplications\": \"iotfleethub:ListApplications\",\n    \"ListTagsForResource\": \"iotfleethub:ListTagsForResource\",\n    \"TagResource\": \"iotfleethub:TagResource\",\n    \"UntagResource\": \"iotfleethub:UntagResource\",\n    \"UpdateApplication\": \"iotfleethub:UpdateApplication\"\n  },\n  \"iotsitewise\": {\n    \"AssociateAssets\": \"iotsitewise:AssociateAssets\",\n    \"AssociateTimeSeriesToAssetProperty\": \"iotsitewise:AssociateTimeSeriesToAssetProperty\",\n    \"BatchAssociateProjectAssets\": \"iotsitewise:BatchAssociateProjectAssets\",\n    \"BatchDisassociateProjectAssets\": \"iotsitewise:BatchDisassociateProjectAssets\",\n    \"BatchPutAssetPropertyValue\": \"iotsitewise:BatchPutAssetPropertyValue\",\n    \"CreateAccessPolicy\": \"iotsitewise:CreateAccessPolicy\",\n    \"CreateAsset\": \"iotsitewise:CreateAsset\",\n    \"CreateAssetModel\": \"iotsitewise:CreateAssetModel\",\n    \"CreateDashboard\": \"iotsitewise:CreateDashboard\",\n    \"CreateGateway\": \"iotsitewise:CreateGateway\",\n    \"CreatePortal\": \"iotsitewise:CreatePortal\",\n    \"CreateProject\": \"iotsitewise:CreateProject\",\n    \"DeleteAccessPolicy\": \"iotsitewise:DeleteAccessPolicy\",\n    \"DeleteAsset\": \"iotsitewise:DeleteAsset\",\n    \"DeleteAssetModel\": \"iotsitewise:DeleteAssetModel\",\n    \"DeleteDashboard\": \"iotsitewise:DeleteDashboard\",\n    \"DeleteGateway\": \"iotsitewise:DeleteGateway\",\n    \"DeletePortal\": \"iotsitewise:DeletePortal\",\n    \"DeleteProject\": \"iotsitewise:DeleteProject\",\n    \"DeleteTimeSeries\": \"iotsitewise:DeleteTimeSeries\",\n    \"DescribeAccessPolicy\": \"iotsitewise:DescribeAccessPolicy\",\n    \"DescribeAsset\": \"iotsitewise:DescribeAsset\",\n    \"DescribeAssetModel\": \"iotsitewise:DescribeAssetModel\",\n    \"DescribeAssetProperty\": \"iotsitewise:DescribeAssetProperty\",\n    \"DescribeDashboard\": \"iotsitewise:DescribeDashboard\",\n    \"DescribeDefaultEncryptionConfiguration\": \"iotsitewise:DescribeDefaultEncryptionConfiguration\",\n    \"DescribeGateway\": \"iotsitewise:DescribeGateway\",\n    \"DescribeGatewayCapabilityConfiguration\": \"iotsitewise:DescribeGatewayCapabilityConfiguration\",\n    \"DescribeLoggingOptions\": \"iotsitewise:DescribeLoggingOptions\",\n    \"DescribePortal\": \"iotsitewise:DescribePortal\",\n    \"DescribeProject\": \"iotsitewise:DescribeProject\",\n    \"DescribeStorageConfiguration\": \"iotsitewise:DescribeStorageConfiguration\",\n    \"DescribeTimeSeries\": \"iotsitewise:DescribeTimeSeries\",\n    \"DisassociateAssets\": \"iotsitewise:DisassociateAssets\",\n    \"DisassociateTimeSeriesFromAssetProperty\": \"iotsitewise:DisassociateTimeSeriesFromAssetProperty\",\n    \"GetAssetPropertyAggregates\": \"iotsitewise:GetAssetPropertyAggregates\",\n    \"GetAssetPropertyValue\": \"iotsitewise:GetAssetPropertyValue\",\n    \"GetAssetPropertyValueHistory\": \"iotsitewise:GetAssetPropertyValueHistory\",\n    \"GetInterpolatedAssetPropertyValues\": \"iotsitewise:GetInterpolatedAssetPropertyValues\",\n    \"ListAccessPolicies\": \"iotsitewise:ListAccessPolicies\",\n    \"ListAssetModels\": \"iotsitewise:ListAssetModels\",\n    \"ListAssetRelationships\": \"iotsitewise:ListAssetRelationships\",\n    \"ListAssets\": \"iotsitewise:ListAssets\",\n    \"ListAssociatedAssets\": \"iotsitewise:ListAssociatedAssets\",\n    \"ListDashboards\": \"iotsitewise:ListDashboards\",\n    \"ListGateways\": \"iotsitewise:ListGateways\",\n    \"ListPortals\": \"iotsitewise:ListPortals\",\n    \"ListProjectAssets\": \"iotsitewise:ListProjectAssets\",\n    \"ListProjects\": \"iotsitewise:ListProjects\",\n    \"ListTagsForResource\": \"iotsitewise:ListTagsForResource\",\n    \"ListTimeSeries\": \"iotsitewise:ListTimeSeries\",\n    \"PutDefaultEncryptionConfiguration\": \"iotsitewise:PutDefaultEncryptionConfiguration\",\n    \"PutLoggingOptions\": \"iotsitewise:PutLoggingOptions\",\n    \"PutStorageConfiguration\": \"iotsitewise:PutStorageConfiguration\",\n    \"TagResource\": \"iotsitewise:TagResource\",\n    \"UntagResource\": \"iotsitewise:UntagResource\",\n    \"UpdateAccessPolicy\": \"iotsitewise:UpdateAccessPolicy\",\n    \"UpdateAsset\": \"iotsitewise:UpdateAsset\",\n    \"UpdateAssetModel\": \"iotsitewise:UpdateAssetModel\",\n    \"UpdateAssetProperty\": \"iotsitewise:UpdateAssetProperty\",\n    \"UpdateDashboard\": \"iotsitewise:UpdateDashboard\",\n    \"UpdateGateway\": \"iotsitewise:UpdateGateway\",\n    \"UpdateGatewayCapabilityConfiguration\": \"iotsitewise:UpdateGatewayCapabilityConfiguration\",\n    \"UpdatePortal\": \"iotsitewise:UpdatePortal\",\n    \"UpdateProject\": \"iotsitewise:UpdateProject\"\n  },\n  \"iotthingsgraph\": {\n    \"AssociateEntityToThing\": \"iotthingsgraph:AssociateEntityToThing\",\n    \"CreateFlowTemplate\": \"iotthingsgraph:CreateFlowTemplate\",\n    \"CreateSystemInstance\": \"iotthingsgraph:CreateSystemInstance\",\n    \"CreateSystemTemplate\": \"iotthingsgraph:CreateSystemTemplate\",\n    \"DeleteFlowTemplate\": \"iotthingsgraph:DeleteFlowTemplate\",\n    \"DeleteNamespace\": \"iotthingsgraph:DeleteNamespace\",\n    \"DeleteSystemInstance\": \"iotthingsgraph:DeleteSystemInstance\",\n    \"DeleteSystemTemplate\": \"iotthingsgraph:DeleteSystemTemplate\",\n    \"DeploySystemInstance\": \"iotthingsgraph:DeploySystemInstance\",\n    \"DeprecateFlowTemplate\": \"iotthingsgraph:DeprecateFlowTemplate\",\n    \"DeprecateSystemTemplate\": \"iotthingsgraph:DeprecateSystemTemplate\",\n    \"DescribeNamespace\": \"iotthingsgraph:DescribeNamespace\",\n    \"DissociateEntityFromThing\": \"iotthingsgraph:DissociateEntityFromThing\",\n    \"GetEntities\": \"iotthingsgraph:GetEntities\",\n    \"GetFlowTemplate\": \"iotthingsgraph:GetFlowTemplate\",\n    \"GetFlowTemplateRevisions\": \"iotthingsgraph:GetFlowTemplateRevisions\",\n    \"GetNamespaceDeletionStatus\": \"iotthingsgraph:GetNamespaceDeletionStatus\",\n    \"GetSystemInstance\": \"iotthingsgraph:GetSystemInstance\",\n    \"GetSystemTemplate\": \"iotthingsgraph:GetSystemTemplate\",\n    \"GetSystemTemplateRevisions\": \"iotthingsgraph:GetSystemTemplateRevisions\",\n    \"GetUploadStatus\": \"iotthingsgraph:GetUploadStatus\",\n    \"ListFlowExecutionMessages\": \"iotthingsgraph:ListFlowExecutionMessages\",\n    \"ListTagsForResource\": \"iotthingsgraph:ListTagsForResource\",\n    \"SearchEntities\": \"iotthingsgraph:SearchEntities\",\n    \"SearchFlowExecutions\": \"iotthingsgraph:SearchFlowExecutions\",\n    \"SearchFlowTemplates\": \"iotthingsgraph:SearchFlowTemplates\",\n    \"SearchSystemInstances\": \"iotthingsgraph:SearchSystemInstances\",\n    \"SearchSystemTemplates\": \"iotthingsgraph:SearchSystemTemplates\",\n    \"SearchThings\": \"iotthingsgraph:SearchThings\",\n    \"TagResource\": \"iotthingsgraph:TagResource\",\n    \"UndeploySystemInstance\": \"iotthingsgraph:UndeploySystemInstance\",\n    \"UntagResource\": \"iotthingsgraph:UntagResource\",\n    \"UpdateFlowTemplate\": \"iotthingsgraph:UpdateFlowTemplate\",\n    \"UpdateSystemTemplate\": \"iotthingsgraph:UpdateSystemTemplate\",\n    \"UploadEntityDefinitions\": \"iotthingsgraph:UploadEntityDefinitions\"\n  },\n  \"iottwinmaker\": {\n    \"BatchPutPropertyValues\": \"iottwinmaker:BatchPutPropertyValues\",\n    \"CreateComponentType\": \"iottwinmaker:CreateComponentType\",\n    \"CreateEntity\": \"iottwinmaker:CreateEntity\",\n    \"CreateScene\": \"iottwinmaker:CreateScene\",\n    \"CreateWorkspace\": \"iottwinmaker:CreateWorkspace\",\n    \"DeleteComponentType\": \"iottwinmaker:DeleteComponentType\",\n    \"DeleteEntity\": \"iottwinmaker:DeleteEntity\",\n    \"DeleteScene\": \"iottwinmaker:DeleteScene\",\n    \"DeleteWorkspace\": \"iottwinmaker:DeleteWorkspace\",\n    \"GetComponentType\": \"iottwinmaker:GetComponentType\",\n    \"GetEntity\": \"iottwinmaker:GetEntity\",\n    \"GetPropertyValue\": \"iottwinmaker:GetPropertyValue\",\n    \"GetPropertyValueHistory\": \"iottwinmaker:GetPropertyValueHistory\",\n    \"GetScene\": \"iottwinmaker:GetScene\",\n    \"GetWorkspace\": \"iottwinmaker:GetWorkspace\",\n    \"ListComponentTypes\": \"iottwinmaker:ListComponentTypes\",\n    \"ListEntities\": \"iottwinmaker:ListEntities\",\n    \"ListScenes\": \"iottwinmaker:ListScenes\",\n    \"ListTagsForResource\": \"iottwinmaker:ListTagsForResource\",\n    \"ListWorkspaces\": \"iottwinmaker:ListWorkspaces\",\n    \"TagResource\": \"iottwinmaker:TagResource\",\n    \"UntagResource\": \"iottwinmaker:UntagResource\",\n    \"UpdateComponentType\": \"iottwinmaker:UpdateComponentType\",\n    \"UpdateEntity\": \"iottwinmaker:UpdateEntity\",\n    \"UpdateScene\": \"iottwinmaker:UpdateScene\",\n    \"UpdateWorkspace\": \"iottwinmaker:UpdateWorkspace\"\n  },\n  \"iotwireless\": {\n    \"AssociateAwsAccountWithPartnerAccount\": \"iotwireless:AssociateAwsAccountWithPartnerAccount\",\n    \"AssociateMulticastGroupWithFuotaTask\": \"iotwireless:AssociateMulticastGroupWithFuotaTask\",\n    \"AssociateWirelessDeviceWithFuotaTask\": \"iotwireless:AssociateWirelessDeviceWithFuotaTask\",\n    \"AssociateWirelessDeviceWithMulticastGroup\": \"iotwireless:AssociateWirelessDeviceWithMulticastGroup\",\n    \"AssociateWirelessDeviceWithThing\": \"iotwireless:AssociateWirelessDeviceWithThing\",\n    \"AssociateWirelessGatewayWithCertificate\": \"iotwireless:AssociateWirelessGatewayWithCertificate\",\n    \"AssociateWirelessGatewayWithThing\": \"iotwireless:AssociateWirelessGatewayWithThing\",\n    \"CancelMulticastGroupSession\": \"iotwireless:CancelMulticastGroupSession\",\n    \"CreateDestination\": \"iotwireless:CreateDestination\",\n    \"CreateDeviceProfile\": \"iotwireless:CreateDeviceProfile\",\n    \"CreateFuotaTask\": \"iotwireless:CreateFuotaTask\",\n    \"CreateMulticastGroup\": \"iotwireless:CreateMulticastGroup\",\n    \"CreateServiceProfile\": \"iotwireless:CreateServiceProfile\",\n    \"CreateWirelessDevice\": \"iotwireless:CreateWirelessDevice\",\n    \"CreateWirelessGateway\": \"iotwireless:CreateWirelessGateway\",\n    \"CreateWirelessGatewayTask\": \"iotwireless:CreateWirelessGatewayTask\",\n    \"CreateWirelessGatewayTaskDefinition\": \"iotwireless:CreateWirelessGatewayTaskDefinition\",\n    \"DeleteDestination\": \"iotwireless:DeleteDestination\",\n    \"DeleteDeviceProfile\": \"iotwireless:DeleteDeviceProfile\",\n    \"DeleteFuotaTask\": \"iotwireless:DeleteFuotaTask\",\n    \"DeleteMulticastGroup\": \"iotwireless:DeleteMulticastGroup\",\n    \"DeleteServiceProfile\": \"iotwireless:DeleteServiceProfile\",\n    \"DeleteWirelessDevice\": \"iotwireless:DeleteWirelessDevice\",\n    \"DeleteWirelessGateway\": \"iotwireless:DeleteWirelessGateway\",\n    \"DeleteWirelessGatewayTask\": \"iotwireless:DeleteWirelessGatewayTask\",\n    \"DeleteWirelessGatewayTaskDefinition\": \"iotwireless:DeleteWirelessGatewayTaskDefinition\",\n    \"DisassociateAwsAccountFromPartnerAccount\": \"iotwireless:DisassociateAwsAccountFromPartnerAccount\",\n    \"DisassociateMulticastGroupFromFuotaTask\": \"iotwireless:DisassociateMulticastGroupFromFuotaTask\",\n    \"DisassociateWirelessDeviceFromFuotaTask\": \"iotwireless:DisassociateWirelessDeviceFromFuotaTask\",\n    \"DisassociateWirelessDeviceFromMulticastGroup\": \"iotwireless:DisassociateWirelessDeviceFromMulticastGroup\",\n    \"DisassociateWirelessDeviceFromThing\": \"iotwireless:DisassociateWirelessDeviceFromThing\",\n    \"DisassociateWirelessGatewayFromCertificate\": \"iotwireless:DisassociateWirelessGatewayFromCertificate\",\n    \"DisassociateWirelessGatewayFromThing\": \"iotwireless:DisassociateWirelessGatewayFromThing\",\n    \"GetDestination\": \"iotwireless:GetDestination\",\n    \"GetDeviceProfile\": \"iotwireless:GetDeviceProfile\",\n    \"GetFuotaTask\": \"iotwireless:GetFuotaTask\",\n    \"GetLogLevelsByResourceTypes\": \"iotwireless:GetLogLevelsByResourceTypes\",\n    \"GetMulticastGroup\": \"iotwireless:GetMulticastGroup\",\n    \"GetMulticastGroupSession\": \"iotwireless:GetMulticastGroupSession\",\n    \"GetNetworkAnalyzerConfiguration\": \"iotwireless:GetNetworkAnalyzerConfiguration\",\n    \"GetPartnerAccount\": \"iotwireless:GetPartnerAccount\",\n    \"GetResourceEventConfiguration\": \"iotwireless:GetResourceEventConfiguration\",\n    \"GetResourceLogLevel\": \"iotwireless:GetResourceLogLevel\",\n    \"GetServiceEndpoint\": \"iotwireless:GetServiceEndpoint\",\n    \"GetServiceProfile\": \"iotwireless:GetServiceProfile\",\n    \"GetWirelessDevice\": \"iotwireless:GetWirelessDevice\",\n    \"GetWirelessDeviceStatistics\": \"iotwireless:GetWirelessDeviceStatistics\",\n    \"GetWirelessGateway\": \"iotwireless:GetWirelessGateway\",\n    \"GetWirelessGatewayCertificate\": \"iotwireless:GetWirelessGatewayCertificate\",\n    \"GetWirelessGatewayFirmwareInformation\": \"iotwireless:GetWirelessGatewayFirmwareInformation\",\n    \"GetWirelessGatewayStatistics\": \"iotwireless:GetWirelessGatewayStatistics\",\n    \"GetWirelessGatewayTask\": \"iotwireless:GetWirelessGatewayTask\",\n    \"GetWirelessGatewayTaskDefinition\": \"iotwireless:GetWirelessGatewayTaskDefinition\",\n    \"ListDestinations\": \"iotwireless:ListDestinations\",\n    \"ListDeviceProfiles\": \"iotwireless:ListDeviceProfiles\",\n    \"ListFuotaTasks\": \"iotwireless:ListFuotaTasks\",\n    \"ListMulticastGroups\": \"iotwireless:ListMulticastGroups\",\n    \"ListMulticastGroupsByFuotaTask\": \"iotwireless:ListMulticastGroupsByFuotaTask\",\n    \"ListPartnerAccounts\": \"iotwireless:ListPartnerAccounts\",\n    \"ListServiceProfiles\": \"iotwireless:ListServiceProfiles\",\n    \"ListTagsForResource\": \"iotwireless:ListTagsForResource\",\n    \"ListWirelessDevices\": \"iotwireless:ListWirelessDevices\",\n    \"ListWirelessGatewayTaskDefinitions\": \"iotwireless:ListWirelessGatewayTaskDefinitions\",\n    \"ListWirelessGateways\": \"iotwireless:ListWirelessGateways\",\n    \"PutResourceLogLevel\": \"iotwireless:PutResourceLogLevel\",\n    \"ResetAllResourceLogLevels\": \"iotwireless:ResetAllResourceLogLevels\",\n    \"ResetResourceLogLevel\": \"iotwireless:ResetResourceLogLevel\",\n    \"SendDataToMulticastGroup\": \"iotwireless:SendDataToMulticastGroup\",\n    \"SendDataToWirelessDevice\": \"iotwireless:SendDataToWirelessDevice\",\n    \"StartBulkAssociateWirelessDeviceWithMulticastGroup\": \"iotwireless:StartBulkAssociateWirelessDeviceWithMulticastGroup\",\n    \"StartBulkDisassociateWirelessDeviceFromMulticastGroup\": \"iotwireless:StartBulkDisassociateWirelessDeviceFromMulticastGroup\",\n    \"StartFuotaTask\": \"iotwireless:StartFuotaTask\",\n    \"StartMulticastGroupSession\": \"iotwireless:StartMulticastGroupSession\",\n    \"TagResource\": \"iotwireless:TagResource\",\n    \"TestWirelessDevice\": \"iotwireless:TestWirelessDevice\",\n    \"UntagResource\": \"iotwireless:UntagResource\",\n    \"UpdateDestination\": \"iotwireless:UpdateDestination\",\n    \"UpdateFuotaTask\": \"iotwireless:UpdateFuotaTask\",\n    \"UpdateLogLevelsByResourceTypes\": \"iotwireless:UpdateLogLevelsByResourceTypes\",\n    \"UpdateMulticastGroup\": \"iotwireless:UpdateMulticastGroup\",\n    \"UpdateNetworkAnalyzerConfiguration\": \"iotwireless:UpdateNetworkAnalyzerConfiguration\",\n    \"UpdatePartnerAccount\": \"iotwireless:UpdatePartnerAccount\",\n    \"UpdateResourceEventConfiguration\": \"iotwireless:UpdateResourceEventConfiguration\",\n    \"UpdateWirelessDevice\": \"iotwireless:UpdateWirelessDevice\",\n    \"UpdateWirelessGateway\": \"iotwireless:UpdateWirelessGateway\"\n  },\n  \"ivs\": {\n    \"BatchGetChannel\": \"ivs:BatchGetChannel\",\n    \"BatchGetStreamKey\": \"ivs:BatchGetStreamKey\",\n    \"CreateChannel\": \"ivs:CreateChannel\",\n    \"CreateRecordingConfiguration\": \"ivs:CreateRecordingConfiguration\",\n    \"CreateStreamKey\": \"ivs:CreateStreamKey\",\n    \"DeleteChannel\": \"ivs:DeleteChannel\",\n    \"DeletePlaybackKeyPair\": \"ivs:DeletePlaybackKeyPair\",\n    \"DeleteRecordingConfiguration\": \"ivs:DeleteRecordingConfiguration\",\n    \"DeleteStreamKey\": \"ivs:DeleteStreamKey\",\n    \"GetChannel\": \"ivs:GetChannel\",\n    \"GetPlaybackKeyPair\": \"ivs:GetPlaybackKeyPair\",\n    \"GetRecordingConfiguration\": \"ivs:GetRecordingConfiguration\",\n    \"GetStream\": \"ivs:GetStream\",\n    \"GetStreamKey\": \"ivs:GetStreamKey\",\n    \"GetStreamSession\": \"ivs:GetStreamSession\",\n    \"ImportPlaybackKeyPair\": \"ivs:ImportPlaybackKeyPair\",\n    \"ListChannels\": \"ivs:ListChannels\",\n    \"ListPlaybackKeyPairs\": \"ivs:ListPlaybackKeyPairs\",\n    \"ListRecordingConfigurations\": \"ivs:ListRecordingConfigurations\",\n    \"ListStreamKeys\": \"ivs:ListStreamKeys\",\n    \"ListStreamSessions\": \"ivs:ListStreamSessions\",\n    \"ListStreams\": \"ivs:ListStreams\",\n    \"ListTagsForResource\": \"ivs:ListTagsForResource\",\n    \"PutMetadata\": \"ivs:PutMetadata\",\n    \"StopStream\": \"ivs:StopStream\",\n    \"TagResource\": \"ivs:TagResource\",\n    \"UntagResource\": \"ivs:UntagResource\",\n    \"UpdateChannel\": \"ivs:UpdateChannel\"\n  },\n  \"kafka\": {\n    \"BatchAssociateScramSecret\": \"kafka:BatchAssociateScramSecret\",\n    \"BatchDisassociateScramSecret\": \"kafka:BatchDisassociateScramSecret\",\n    \"CreateCluster\": \"kafka:CreateCluster\",\n    \"CreateConfiguration\": \"kafka:CreateConfiguration\",\n    \"DeleteCluster\": \"kafka:DeleteCluster\",\n    \"DeleteConfiguration\": \"kafka:DeleteConfiguration\",\n    \"DescribeCluster\": \"kafka:DescribeCluster\",\n    \"DescribeClusterOperation\": \"kafka:DescribeClusterOperation\",\n    \"DescribeConfiguration\": \"kafka:DescribeConfiguration\",\n    \"DescribeConfigurationRevision\": \"kafka:DescribeConfigurationRevision\",\n    \"GetBootstrapBrokers\": \"kafka:GetBootstrapBrokers\",\n    \"GetCompatibleKafkaVersions\": \"kafka:GetCompatibleKafkaVersions\",\n    \"ListClusterOperations\": \"kafka:ListClusterOperations\",\n    \"ListClusters\": \"kafka:ListClusters\",\n    \"ListConfigurationRevisions\": \"kafka:ListConfigurationRevisions\",\n    \"ListConfigurations\": \"kafka:ListConfigurations\",\n    \"ListKafkaVersions\": \"kafka:ListKafkaVersions\",\n    \"ListNodes\": \"kafka:ListNodes\",\n    \"ListScramSecrets\": \"kafka:ListScramSecrets\",\n    \"ListTagsForResource\": \"kafka:ListTagsForResource\",\n    \"RebootBroker\": \"kafka:RebootBroker\",\n    \"TagResource\": \"kafka:TagResource\",\n    \"UntagResource\": \"kafka:UntagResource\",\n    \"UpdateBrokerCount\": \"kafka:UpdateBrokerCount\",\n    \"UpdateBrokerStorage\": \"kafka:UpdateBrokerStorage\",\n    \"UpdateBrokerType\": \"kafka:UpdateBrokerType\",\n    \"UpdateClusterConfiguration\": \"kafka:UpdateClusterConfiguration\",\n    \"UpdateClusterKafkaVersion\": \"kafka:UpdateClusterKafkaVersion\",\n    \"UpdateConfiguration\": \"kafka:UpdateConfiguration\",\n    \"UpdateConnectivity\": \"kafka:UpdateConnectivity\",\n    \"UpdateMonitoring\": \"kafka:UpdateMonitoring\",\n    \"UpdateSecurity\": \"kafka:UpdateSecurity\"\n  },\n  \"kafkaconnect\": {\n    \"CreateConnector\": \"kafkaconnect:CreateConnector\",\n    \"CreateCustomPlugin\": \"kafkaconnect:CreateCustomPlugin\",\n    \"CreateWorkerConfiguration\": \"kafkaconnect:CreateWorkerConfiguration\",\n    \"DeleteConnector\": \"kafkaconnect:DeleteConnector\",\n    \"DescribeConnector\": \"kafkaconnect:DescribeConnector\",\n    \"DescribeCustomPlugin\": \"kafkaconnect:DescribeCustomPlugin\",\n    \"DescribeWorkerConfiguration\": \"kafkaconnect:DescribeWorkerConfiguration\",\n    \"ListConnectors\": \"kafkaconnect:ListConnectors\",\n    \"ListCustomPlugins\": \"kafkaconnect:ListCustomPlugins\",\n    \"ListWorkerConfigurations\": \"kafkaconnect:ListWorkerConfigurations\",\n    \"UpdateConnector\": \"kafkaconnect:UpdateConnector\"\n  },\n  \"kendra\": {\n    \"BatchDeleteDocument\": \"kendra:BatchDeleteDocument\",\n    \"BatchGetDocumentStatus\": \"kendra:BatchGetDocumentStatus\",\n    \"BatchPutDocument\": \"kendra:BatchPutDocument\",\n    \"ClearQuerySuggestions\": \"kendra:ClearQuerySuggestions\",\n    \"CreateDataSource\": \"kendra:CreateDataSource\",\n    \"CreateFaq\": \"kendra:CreateFaq\",\n    \"CreateIndex\": \"kendra:CreateIndex\",\n    \"CreateQuerySuggestionsBlockList\": \"kendra:CreateQuerySuggestionsBlockList\",\n    \"CreateThesaurus\": \"kendra:CreateThesaurus\",\n    \"DeleteDataSource\": \"kendra:DeleteDataSource\",\n    \"DeleteFaq\": \"kendra:DeleteFaq\",\n    \"DeleteIndex\": \"kendra:DeleteIndex\",\n    \"DeletePrincipalMapping\": \"kendra:DeletePrincipalMapping\",\n    \"DeleteQuerySuggestionsBlockList\": \"kendra:DeleteQuerySuggestionsBlockList\",\n    \"DeleteThesaurus\": \"kendra:DeleteThesaurus\",\n    \"DescribeDataSource\": \"kendra:DescribeDataSource\",\n    \"DescribeFaq\": \"kendra:DescribeFaq\",\n    \"DescribeIndex\": \"kendra:DescribeIndex\",\n    \"DescribePrincipalMapping\": \"kendra:DescribePrincipalMapping\",\n    \"DescribeQuerySuggestionsBlockList\": \"kendra:DescribeQuerySuggestionsBlockList\",\n    \"DescribeQuerySuggestionsConfig\": \"kendra:DescribeQuerySuggestionsConfig\",\n    \"DescribeThesaurus\": \"kendra:DescribeThesaurus\",\n    \"GetQuerySuggestions\": \"kendra:GetQuerySuggestions\",\n    \"ListDataSourceSyncJobs\": \"kendra:ListDataSourceSyncJobs\",\n    \"ListDataSources\": \"kendra:ListDataSources\",\n    \"ListFaqs\": \"kendra:ListFaqs\",\n    \"ListGroupsOlderThanOrderingId\": \"kendra:ListGroupsOlderThanOrderingId\",\n    \"ListIndices\": \"kendra:ListIndices\",\n    \"ListQuerySuggestionsBlockLists\": \"kendra:ListQuerySuggestionsBlockLists\",\n    \"ListTagsForResource\": \"kendra:ListTagsForResource\",\n    \"ListThesauri\": \"kendra:ListThesauri\",\n    \"PutPrincipalMapping\": \"kendra:PutPrincipalMapping\",\n    \"Query\": \"kendra:Query\",\n    \"StartDataSourceSyncJob\": \"kendra:StartDataSourceSyncJob\",\n    \"StopDataSourceSyncJob\": \"kendra:StopDataSourceSyncJob\",\n    \"SubmitFeedback\": \"kendra:SubmitFeedback\",\n    \"TagResource\": \"kendra:TagResource\",\n    \"UntagResource\": \"kendra:UntagResource\",\n    \"UpdateDataSource\": \"kendra:UpdateDataSource\",\n    \"UpdateIndex\": \"kendra:UpdateIndex\",\n    \"UpdateQuerySuggestionsBlockList\": \"kendra:UpdateQuerySuggestionsBlockList\",\n    \"UpdateQuerySuggestionsConfig\": \"kendra:UpdateQuerySuggestionsConfig\",\n    \"UpdateThesaurus\": \"kendra:UpdateThesaurus\"\n  },\n  \"kinesis\": {\n    \"AddTagsToStream\": \"kinesis:AddTagsToStream\",\n    \"CreateStream\": \"kinesis:CreateStream\",\n    \"DecreaseStreamRetentionPeriod\": \"kinesis:DecreaseStreamRetentionPeriod\",\n    \"DeleteStream\": \"kinesis:DeleteStream\",\n    \"DeregisterStreamConsumer\": \"kinesis:DeregisterStreamConsumer\",\n    \"DescribeLimits\": \"kinesis:DescribeLimits\",\n    \"DescribeStream\": \"kinesis:DescribeStream\",\n    \"DescribeStreamConsumer\": \"kinesis:DescribeStreamConsumer\",\n    \"DescribeStreamSummary\": \"kinesis:DescribeStreamSummary\",\n    \"DisableEnhancedMonitoring\": \"kinesis:DisableEnhancedMonitoring\",\n    \"EnableEnhancedMonitoring\": \"kinesis:EnableEnhancedMonitoring\",\n    \"GetRecords\": \"kinesis:GetRecords\",\n    \"GetShardIterator\": \"kinesis:GetShardIterator\",\n    \"IncreaseStreamRetentionPeriod\": \"kinesis:IncreaseStreamRetentionPeriod\",\n    \"ListShards\": \"kinesis:ListShards\",\n    \"ListStreamConsumers\": \"kinesis:ListStreamConsumers\",\n    \"ListStreams\": \"kinesis:ListStreams\",\n    \"ListTagsForStream\": \"kinesis:ListTagsForStream\",\n    \"MergeShards\": \"kinesis:MergeShards\",\n    \"PutRecord\": \"kinesis:PutRecord\",\n    \"PutRecords\": \"kinesis:PutRecords\",\n    \"RegisterStreamConsumer\": \"kinesis:RegisterStreamConsumer\",\n    \"RemoveTagsFromStream\": \"kinesis:RemoveTagsFromStream\",\n    \"SplitShard\": \"kinesis:SplitShard\",\n    \"StartStreamEncryption\": \"kinesis:StartStreamEncryption\",\n    \"StopStreamEncryption\": \"kinesis:StopStreamEncryption\",\n    \"SubscribeToShard\": \"kinesis:SubscribeToShard\",\n    \"UpdateShardCount\": \"kinesis:UpdateShardCount\"\n  },\n  \"kinesis-video-archived-media\": {\n    \"GetClip\": \"kinesisvideo:GetClip\",\n    \"GetDASHStreamingSessionURL\": \"kinesisvideo:GetDASHStreamingSessionURL\",\n    \"GetHLSStreamingSessionURL\": \"kinesisvideo:GetHLSStreamingSessionURL\",\n    \"GetMediaForFragmentList\": \"kinesisvideo:GetMediaForFragmentList\",\n    \"ListFragments\": \"kinesisvideo:ListFragments\"\n  },\n  \"kinesis-video-media\": {\n    \"GetMedia\": \"kinesisvideo:GetMedia\"\n  },\n  \"kinesisanalytics\": {\n    \"AddApplicationCloudWatchLoggingOption\": \"kinesisanalytics:AddApplicationCloudWatchLoggingOption\",\n    \"AddApplicationInput\": \"kinesisanalytics:AddApplicationInput\",\n    \"AddApplicationInputProcessingConfiguration\": \"kinesisanalytics:AddApplicationInputProcessingConfiguration\",\n    \"AddApplicationOutput\": \"kinesisanalytics:AddApplicationOutput\",\n    \"AddApplicationReferenceDataSource\": \"kinesisanalytics:AddApplicationReferenceDataSource\",\n    \"CreateApplication\": \"kinesisanalytics:CreateApplication\",\n    \"DeleteApplication\": \"kinesisanalytics:DeleteApplication\",\n    \"DeleteApplicationCloudWatchLoggingOption\": \"kinesisanalytics:DeleteApplicationCloudWatchLoggingOption\",\n    \"DeleteApplicationInputProcessingConfiguration\": \"kinesisanalytics:DeleteApplicationInputProcessingConfiguration\",\n    \"DeleteApplicationOutput\": \"kinesisanalytics:DeleteApplicationOutput\",\n    \"DeleteApplicationReferenceDataSource\": \"kinesisanalytics:DeleteApplicationReferenceDataSource\",\n    \"DescribeApplication\": \"kinesisanalytics:DescribeApplication\",\n    \"DiscoverInputSchema\": \"kinesisanalytics:DiscoverInputSchema\",\n    \"ListApplications\": \"kinesisanalytics:ListApplications\",\n    \"ListTagsForResource\": \"kinesisanalytics:ListTagsForResource\",\n    \"StartApplication\": \"kinesisanalytics:StartApplication\",\n    \"StopApplication\": \"kinesisanalytics:StopApplication\",\n    \"TagResource\": \"kinesisanalytics:TagResource\",\n    \"UntagResource\": \"kinesisanalytics:UntagResource\",\n    \"UpdateApplication\": \"kinesisanalytics:UpdateApplication\"\n  },\n  \"kinesisvideo\": {\n    \"CreateSignalingChannel\": \"kinesisvideo:CreateSignalingChannel\",\n    \"CreateStream\": \"kinesisvideo:CreateStream\",\n    \"DeleteSignalingChannel\": \"kinesisvideo:DeleteSignalingChannel\",\n    \"DeleteStream\": \"kinesisvideo:DeleteStream\",\n    \"DescribeSignalingChannel\": \"kinesisvideo:DescribeSignalingChannel\",\n    \"DescribeStream\": \"kinesisvideo:DescribeStream\",\n    \"GetDataEndpoint\": \"kinesisvideo:GetDataEndpoint\",\n    \"GetSignalingChannelEndpoint\": \"kinesisvideo:GetSignalingChannelEndpoint\",\n    \"ListSignalingChannels\": \"kinesisvideo:ListSignalingChannels\",\n    \"ListStreams\": \"kinesisvideo:ListStreams\",\n    \"ListTagsForResource\": \"kinesisvideo:ListTagsForResource\",\n    \"ListTagsForStream\": \"kinesisvideo:ListTagsForStream\",\n    \"TagResource\": \"kinesisvideo:TagResource\",\n    \"TagStream\": \"kinesisvideo:TagStream\",\n    \"UntagResource\": \"kinesisvideo:UntagResource\",\n    \"UntagStream\": \"kinesisvideo:UntagStream\",\n    \"UpdateDataRetention\": \"kinesisvideo:UpdateDataRetention\",\n    \"UpdateSignalingChannel\": \"kinesisvideo:UpdateSignalingChannel\",\n    \"UpdateStream\": \"kinesisvideo:UpdateStream\"\n  },\n  \"kms\": {\n    \"CancelKeyDeletion\": \"kms:CancelKeyDeletion\",\n    \"ConnectCustomKeyStore\": \"kms:ConnectCustomKeyStore\",\n    \"CreateAlias\": \"kms:CreateAlias\",\n    \"CreateCustomKeyStore\": \"kms:CreateCustomKeyStore\",\n    \"CreateGrant\": \"kms:CreateGrant\",\n    \"CreateKey\": \"kms:CreateKey\",\n    \"Decrypt\": \"kms:Decrypt\",\n    \"DeleteAlias\": \"kms:DeleteAlias\",\n    \"DeleteCustomKeyStore\": \"kms:DeleteCustomKeyStore\",\n    \"DeleteImportedKeyMaterial\": \"kms:DeleteImportedKeyMaterial\",\n    \"DescribeCustomKeyStores\": \"kms:DescribeCustomKeyStores\",\n    \"DescribeKey\": \"kms:DescribeKey\",\n    \"DisableKey\": \"kms:DisableKey\",\n    \"DisableKeyRotation\": \"kms:DisableKeyRotation\",\n    \"DisconnectCustomKeyStore\": \"kms:DisconnectCustomKeyStore\",\n    \"EnableKey\": \"kms:EnableKey\",\n    \"EnableKeyRotation\": \"kms:EnableKeyRotation\",\n    \"Encrypt\": \"kms:Encrypt\",\n    \"GenerateDataKey\": \"kms:GenerateDataKey\",\n    \"GenerateDataKeyPair\": \"kms:GenerateDataKeyPair\",\n    \"GenerateDataKeyPairWithoutPlaintext\": \"kms:GenerateDataKeyPairWithoutPlaintext\",\n    \"GenerateDataKeyWithoutPlaintext\": \"kms:GenerateDataKeyWithoutPlaintext\",\n    \"GenerateRandom\": \"kms:GenerateRandom\",\n    \"GetKeyPolicy\": \"kms:GetKeyPolicy\",\n    \"GetKeyRotationStatus\": \"kms:GetKeyRotationStatus\",\n    \"GetParametersForImport\": \"kms:GetParametersForImport\",\n    \"GetPublicKey\": \"kms:GetPublicKey\",\n    \"ImportKeyMaterial\": \"kms:ImportKeyMaterial\",\n    \"ListAliases\": \"kms:ListAliases\",\n    \"ListGrants\": \"kms:ListGrants\",\n    \"ListKeyPolicies\": \"kms:ListKeyPolicies\",\n    \"ListKeys\": \"kms:ListKeys\",\n    \"ListResourceTags\": \"kms:ListResourceTags\",\n    \"ListRetirableGrants\": \"kms:ListRetirableGrants\",\n    \"PutKeyPolicy\": \"kms:PutKeyPolicy\",\n    \"ReplicateKey\": \"kms:ReplicateKey\",\n    \"RetireGrant\": \"kms:RetireGrant\",\n    \"RevokeGrant\": \"kms:RevokeGrant\",\n    \"ScheduleKeyDeletion\": \"kms:ScheduleKeyDeletion\",\n    \"Sign\": \"kms:Sign\",\n    \"TagResource\": \"kms:TagResource\",\n    \"UntagResource\": \"kms:UntagResource\",\n    \"UpdateAlias\": \"kms:UpdateAlias\",\n    \"UpdateCustomKeyStore\": \"kms:UpdateCustomKeyStore\",\n    \"UpdateKeyDescription\": \"kms:UpdateKeyDescription\",\n    \"UpdatePrimaryRegion\": \"kms:UpdatePrimaryRegion\",\n    \"Verify\": \"kms:Verify\"\n  },\n  \"lakeformation\": {\n    \"AddLFTagsToResource\": \"lakeformation:AddLFTagsToResource\",\n    \"BatchGrantPermissions\": \"lakeformation:BatchGrantPermissions\",\n    \"BatchRevokePermissions\": \"lakeformation:BatchRevokePermissions\",\n    \"CancelTransaction\": \"lakeformation:CancelTransaction\",\n    \"CommitTransaction\": \"lakeformation:CommitTransaction\",\n    \"CreateDataCellsFilter\": \"lakeformation:CreateDataCellsFilter\",\n    \"CreateLFTag\": \"lakeformation:CreateLFTag\",\n    \"DeleteDataCellsFilter\": \"lakeformation:DeleteDataCellsFilter\",\n    \"DeleteLFTag\": \"lakeformation:DeleteLFTag\",\n    \"DeleteObjectsOnCancel\": \"lakeformation:DeleteObjectsOnCancel\",\n    \"DeregisterResource\": \"lakeformation:DeregisterResource\",\n    \"DescribeResource\": \"lakeformation:DescribeResource\",\n    \"DescribeTransaction\": \"lakeformation:DescribeTransaction\",\n    \"ExtendTransaction\": \"lakeformation:ExtendTransaction\",\n    \"GetDataLakeSettings\": \"lakeformation:GetDataLakeSettings\",\n    \"GetEffectivePermissionsForPath\": \"lakeformation:GetEffectivePermissionsForPath\",\n    \"GetLFTag\": \"lakeformation:GetLFTag\",\n    \"GetQueryState\": \"lakeformation:GetQueryState\",\n    \"GetQueryStatistics\": \"lakeformation:GetQueryStatistics\",\n    \"GetResourceLFTags\": \"lakeformation:GetResourceLFTags\",\n    \"GetTableObjects\": \"lakeformation:GetTableObjects\",\n    \"GetWorkUnitResults\": \"lakeformation:GetWorkUnitResults\",\n    \"GetWorkUnits\": \"lakeformation:GetWorkUnits\",\n    \"GrantPermissions\": \"lakeformation:GrantPermissions\",\n    \"ListDataCellsFilter\": \"lakeformation:ListDataCellsFilter\",\n    \"ListLFTags\": \"lakeformation:ListLFTags\",\n    \"ListPermissions\": \"lakeformation:ListPermissions\",\n    \"ListResources\": \"lakeformation:ListResources\",\n    \"ListTableStorageOptimizers\": \"lakeformation:ListTableStorageOptimizers\",\n    \"ListTransactions\": \"lakeformation:ListTransactions\",\n    \"PutDataLakeSettings\": \"lakeformation:PutDataLakeSettings\",\n    \"RegisterResource\": \"lakeformation:RegisterResource\",\n    \"RemoveLFTagsFromResource\": \"lakeformation:RemoveLFTagsFromResource\",\n    \"RevokePermissions\": \"lakeformation:RevokePermissions\",\n    \"SearchDatabasesByLFTags\": \"lakeformation:SearchDatabasesByLFTags\",\n    \"SearchTablesByLFTags\": \"lakeformation:SearchTablesByLFTags\",\n    \"StartQueryPlanning\": \"lakeformation:StartQueryPlanning\",\n    \"StartTransaction\": \"lakeformation:StartTransaction\",\n    \"UpdateLFTag\": \"lakeformation:UpdateLFTag\",\n    \"UpdateResource\": \"lakeformation:UpdateResource\",\n    \"UpdateTableObjects\": \"lakeformation:UpdateTableObjects\",\n    \"UpdateTableStorageOptimizer\": \"lakeformation:UpdateTableStorageOptimizer\"\n  },\n  \"lambda\": {\n    \"AddLayerVersionPermission\": \"lambda:AddLayerVersionPermission\",\n    \"AddPermission\": \"lambda:AddPermission\",\n    \"CreateAlias\": \"lambda:CreateAlias\",\n    \"CreateCodeSigningConfig\": \"lambda:CreateCodeSigningConfig\",\n    \"CreateEventSourceMapping\": \"lambda:CreateEventSourceMapping\",\n    \"CreateFunction\": \"lambda:CreateFunction\",\n    \"DeleteAlias\": \"lambda:DeleteAlias\",\n    \"DeleteCodeSigningConfig\": \"lambda:DeleteCodeSigningConfig\",\n    \"DeleteEventSourceMapping\": \"lambda:DeleteEventSourceMapping\",\n    \"DeleteFunction\": \"lambda:DeleteFunction\",\n    \"DeleteFunctionCodeSigningConfig\": \"lambda:DeleteFunctionCodeSigningConfig\",\n    \"DeleteFunctionConcurrency\": \"lambda:DeleteFunctionConcurrency\",\n    \"DeleteFunctionEventInvokeConfig\": \"lambda:DeleteFunctionEventInvokeConfig\",\n    \"DeleteLayerVersion\": \"lambda:DeleteLayerVersion\",\n    \"DeleteProvisionedConcurrencyConfig\": \"lambda:DeleteProvisionedConcurrencyConfig\",\n    \"GetAccountSettings\": \"lambda:GetAccountSettings\",\n    \"GetAlias\": \"lambda:GetAlias\",\n    \"GetCodeSigningConfig\": \"lambda:GetCodeSigningConfig\",\n    \"GetEventSourceMapping\": \"lambda:GetEventSourceMapping\",\n    \"GetFunction\": \"lambda:GetFunction\",\n    \"GetFunctionCodeSigningConfig\": \"lambda:GetFunctionCodeSigningConfig\",\n    \"GetFunctionConcurrency\": \"lambda:GetFunctionConcurrency\",\n    \"GetFunctionConfiguration\": \"lambda:GetFunctionConfiguration\",\n    \"GetFunctionEventInvokeConfig\": \"lambda:GetFunctionEventInvokeConfig\",\n    \"GetLayerVersion\": \"lambda:GetLayerVersion\",\n    \"GetLayerVersionPolicy\": \"lambda:GetLayerVersionPolicy\",\n    \"GetPolicy\": \"lambda:GetPolicy\",\n    \"GetProvisionedConcurrencyConfig\": \"lambda:GetProvisionedConcurrencyConfig\",\n    \"InvokeAsync\": \"lambda:InvokeAsync\",\n    \"ListAliases\": \"lambda:ListAliases\",\n    \"ListCodeSigningConfigs\": \"lambda:ListCodeSigningConfigs\",\n    \"ListEventSourceMappings\": \"lambda:ListEventSourceMappings\",\n    \"ListFunctionEventInvokeConfigs\": \"lambda:ListFunctionEventInvokeConfigs\",\n    \"ListFunctions\": \"lambda:ListFunctions\",\n    \"ListFunctionsByCodeSigningConfig\": \"lambda:ListFunctionsByCodeSigningConfig\",\n    \"ListLayerVersions\": \"lambda:ListLayerVersions\",\n    \"ListLayers\": \"lambda:ListLayers\",\n    \"ListProvisionedConcurrencyConfigs\": \"lambda:ListProvisionedConcurrencyConfigs\",\n    \"ListTags\": \"lambda:ListTags\",\n    \"ListVersionsByFunction\": \"lambda:ListVersionsByFunction\",\n    \"PublishLayerVersion\": \"lambda:PublishLayerVersion\",\n    \"PublishVersion\": \"lambda:PublishVersion\",\n    \"PutFunctionCodeSigningConfig\": \"lambda:PutFunctionCodeSigningConfig\",\n    \"PutFunctionConcurrency\": \"lambda:PutFunctionConcurrency\",\n    \"PutFunctionEventInvokeConfig\": \"lambda:PutFunctionEventInvokeConfig\",\n    \"PutProvisionedConcurrencyConfig\": \"lambda:PutProvisionedConcurrencyConfig\",\n    \"RemoveLayerVersionPermission\": \"lambda:RemoveLayerVersionPermission\",\n    \"RemovePermission\": \"lambda:RemovePermission\",\n    \"TagResource\": \"lambda:TagResource\",\n    \"UntagResource\": \"lambda:UntagResource\",\n    \"UpdateAlias\": \"lambda:UpdateAlias\",\n    \"UpdateCodeSigningConfig\": \"lambda:UpdateCodeSigningConfig\",\n    \"UpdateEventSourceMapping\": \"lambda:UpdateEventSourceMapping\",\n    \"UpdateFunctionCode\": \"lambda:UpdateFunctionCode\",\n    \"UpdateFunctionConfiguration\": \"lambda:UpdateFunctionConfiguration\",\n    \"UpdateFunctionEventInvokeConfig\": \"lambda:UpdateFunctionEventInvokeConfig\"\n  },\n  \"lex-models\": {\n    \"CreateBotVersion\": \"lex:CreateBotVersion\",\n    \"CreateIntentVersion\": \"lex:CreateIntentVersion\",\n    \"CreateSlotTypeVersion\": \"lex:CreateSlotTypeVersion\",\n    \"DeleteBot\": \"lex:DeleteBot\",\n    \"DeleteBotAlias\": \"lex:DeleteBotAlias\",\n    \"DeleteBotChannelAssociation\": \"lex:DeleteBotChannelAssociation\",\n    \"DeleteBotVersion\": \"lex:DeleteBotVersion\",\n    \"DeleteIntent\": \"lex:DeleteIntent\",\n    \"DeleteIntentVersion\": \"lex:DeleteIntentVersion\",\n    \"DeleteSlotType\": \"lex:DeleteSlotType\",\n    \"DeleteSlotTypeVersion\": \"lex:DeleteSlotTypeVersion\",\n    \"DeleteUtterances\": \"lex:DeleteUtterances\",\n    \"GetBot\": \"lex:GetBot\",\n    \"GetBotAlias\": \"lex:GetBotAlias\",\n    \"GetBotAliases\": \"lex:GetBotAliases\",\n    \"GetBotChannelAssociation\": \"lex:GetBotChannelAssociation\",\n    \"GetBotChannelAssociations\": \"lex:GetBotChannelAssociations\",\n    \"GetBotVersions\": \"lex:GetBotVersions\",\n    \"GetBots\": \"lex:GetBots\",\n    \"GetBuiltinIntent\": \"lex:GetBuiltinIntent\",\n    \"GetBuiltinIntents\": \"lex:GetBuiltinIntents\",\n    \"GetBuiltinSlotTypes\": \"lex:GetBuiltinSlotTypes\",\n    \"GetExport\": \"lex:GetExport\",\n    \"GetImport\": \"lex:GetImport\",\n    \"GetIntent\": \"lex:GetIntent\",\n    \"GetIntentVersions\": \"lex:GetIntentVersions\",\n    \"GetIntents\": \"lex:GetIntents\",\n    \"GetMigration\": \"lex:GetMigration\",\n    \"GetMigrations\": \"lex:GetMigrations\",\n    \"GetSlotType\": \"lex:GetSlotType\",\n    \"GetSlotTypeVersions\": \"lex:GetSlotTypeVersions\",\n    \"GetSlotTypes\": \"lex:GetSlotTypes\",\n    \"GetUtterancesView\": \"lex:GetUtterancesView\",\n    \"ListTagsForResource\": \"lex:ListTagsForResource\",\n    \"PutBot\": \"lex:PutBot\",\n    \"PutBotAlias\": \"lex:PutBotAlias\",\n    \"PutIntent\": \"lex:PutIntent\",\n    \"PutSlotType\": \"lex:PutSlotType\",\n    \"StartImport\": \"lex:StartImport\",\n    \"StartMigration\": \"lex:StartMigration\",\n    \"TagResource\": \"lex:TagResource\",\n    \"UntagResource\": \"lex:UntagResource\"\n  },\n  \"lex-runtime\": {\n    \"DeleteSession\": \"lex:DeleteSession\",\n    \"GetSession\": \"lex:GetSession\",\n    \"PostContent\": \"lex:PostContent\",\n    \"PostText\": \"lex:PostText\",\n    \"PutSession\": \"lex:PutSession\"\n  },\n  \"license-manager\": {\n    \"AcceptGrant\": \"license-manager:AcceptGrant\",\n    \"CheckInLicense\": \"license-manager:CheckInLicense\",\n    \"CheckoutBorrowLicense\": \"license-manager:CheckoutBorrowLicense\",\n    \"CheckoutLicense\": \"license-manager:CheckoutLicense\",\n    \"CreateGrant\": \"license-manager:CreateGrant\",\n    \"CreateGrantVersion\": \"license-manager:CreateGrantVersion\",\n    \"CreateLicense\": \"license-manager:CreateLicense\",\n    \"CreateLicenseConfiguration\": \"license-manager:CreateLicenseConfiguration\",\n    \"CreateLicenseConversionTaskForResource\": \"license-manager:CreateLicenseConversionTaskForResource\",\n    \"CreateLicenseManagerReportGenerator\": \"license-manager:CreateLicenseManagerReportGenerator\",\n    \"CreateLicenseVersion\": \"license-manager:CreateLicenseVersion\",\n    \"CreateToken\": \"license-manager:CreateToken\",\n    \"DeleteGrant\": \"license-manager:DeleteGrant\",\n    \"DeleteLicense\": \"license-manager:DeleteLicense\",\n    \"DeleteLicenseConfiguration\": \"license-manager:DeleteLicenseConfiguration\",\n    \"DeleteLicenseManagerReportGenerator\": \"license-manager:DeleteLicenseManagerReportGenerator\",\n    \"DeleteToken\": \"license-manager:DeleteToken\",\n    \"ExtendLicenseConsumption\": \"license-manager:ExtendLicenseConsumption\",\n    \"GetAccessToken\": \"license-manager:GetAccessToken\",\n    \"GetGrant\": \"license-manager:GetGrant\",\n    \"GetLicense\": \"license-manager:GetLicense\",\n    \"GetLicenseConfiguration\": \"license-manager:GetLicenseConfiguration\",\n    \"GetLicenseConversionTask\": \"license-manager:GetLicenseConversionTask\",\n    \"GetLicenseManagerReportGenerator\": \"license-manager:GetLicenseManagerReportGenerator\",\n    \"GetLicenseUsage\": \"license-manager:GetLicenseUsage\",\n    \"GetServiceSettings\": \"license-manager:GetServiceSettings\",\n    \"ListAssociationsForLicenseConfiguration\": \"license-manager:ListAssociationsForLicenseConfiguration\",\n    \"ListDistributedGrants\": \"license-manager:ListDistributedGrants\",\n    \"ListFailuresForLicenseConfigurationOperations\": \"license-manager:ListFailuresForLicenseConfigurationOperations\",\n    \"ListLicenseConfigurations\": \"license-manager:ListLicenseConfigurations\",\n    \"ListLicenseConversionTasks\": \"license-manager:ListLicenseConversionTasks\",\n    \"ListLicenseManagerReportGenerators\": \"license-manager:ListLicenseManagerReportGenerators\",\n    \"ListLicenseSpecificationsForResource\": \"license-manager:ListLicenseSpecificationsForResource\",\n    \"ListLicenseVersions\": \"license-manager:ListLicenseVersions\",\n    \"ListLicenses\": \"license-manager:ListLicenses\",\n    \"ListReceivedGrants\": \"license-manager:ListReceivedGrants\",\n    \"ListReceivedLicenses\": \"license-manager:ListReceivedLicenses\",\n    \"ListResourceInventory\": \"license-manager:ListResourceInventory\",\n    \"ListTagsForResource\": \"license-manager:ListTagsForResource\",\n    \"ListTokens\": \"license-manager:ListTokens\",\n    \"ListUsageForLicenseConfiguration\": \"license-manager:ListUsageForLicenseConfiguration\",\n    \"RejectGrant\": \"license-manager:RejectGrant\",\n    \"TagResource\": \"license-manager:TagResource\",\n    \"UntagResource\": \"license-manager:UntagResource\",\n    \"UpdateLicenseConfiguration\": \"license-manager:UpdateLicenseConfiguration\",\n    \"UpdateLicenseManagerReportGenerator\": \"license-manager:UpdateLicenseManagerReportGenerator\",\n    \"UpdateLicenseSpecificationsForResource\": \"license-manager:UpdateLicenseSpecificationsForResource\",\n    \"UpdateServiceSettings\": \"license-manager:UpdateServiceSettings\"\n  },\n  \"lightsail\": {\n    \"AllocateStaticIp\": \"lightsail:AllocateStaticIp\",\n    \"AttachCertificateToDistribution\": \"lightsail:AttachCertificateToDistribution\",\n    \"AttachDisk\": \"lightsail:AttachDisk\",\n    \"AttachInstancesToLoadBalancer\": \"lightsail:AttachInstancesToLoadBalancer\",\n    \"AttachLoadBalancerTlsCertificate\": \"lightsail:AttachLoadBalancerTlsCertificate\",\n    \"AttachStaticIp\": \"lightsail:AttachStaticIp\",\n    \"CloseInstancePublicPorts\": \"lightsail:CloseInstancePublicPorts\",\n    \"CopySnapshot\": \"lightsail:CopySnapshot\",\n    \"CreateBucket\": \"lightsail:CreateBucket\",\n    \"CreateBucketAccessKey\": \"lightsail:CreateBucketAccessKey\",\n    \"CreateCertificate\": \"lightsail:CreateCertificate\",\n    \"CreateCloudFormationStack\": \"lightsail:CreateCloudFormationStack\",\n    \"CreateContactMethod\": \"lightsail:CreateContactMethod\",\n    \"CreateContainerService\": \"lightsail:CreateContainerService\",\n    \"CreateContainerServiceDeployment\": \"lightsail:CreateContainerServiceDeployment\",\n    \"CreateContainerServiceRegistryLogin\": \"lightsail:CreateContainerServiceRegistryLogin\",\n    \"CreateDisk\": \"lightsail:CreateDisk\",\n    \"CreateDiskFromSnapshot\": \"lightsail:CreateDiskFromSnapshot\",\n    \"CreateDiskSnapshot\": \"lightsail:CreateDiskSnapshot\",\n    \"CreateDistribution\": \"lightsail:CreateDistribution\",\n    \"CreateDomain\": \"lightsail:CreateDomain\",\n    \"CreateDomainEntry\": \"lightsail:CreateDomainEntry\",\n    \"CreateInstanceSnapshot\": \"lightsail:CreateInstanceSnapshot\",\n    \"CreateInstances\": \"lightsail:CreateInstances\",\n    \"CreateInstancesFromSnapshot\": \"lightsail:CreateInstancesFromSnapshot\",\n    \"CreateKeyPair\": \"lightsail:CreateKeyPair\",\n    \"CreateLoadBalancer\": \"lightsail:CreateLoadBalancer\",\n    \"CreateLoadBalancerTlsCertificate\": \"lightsail:CreateLoadBalancerTlsCertificate\",\n    \"CreateRelationalDatabase\": \"lightsail:CreateRelationalDatabase\",\n    \"CreateRelationalDatabaseFromSnapshot\": \"lightsail:CreateRelationalDatabaseFromSnapshot\",\n    \"CreateRelationalDatabaseSnapshot\": \"lightsail:CreateRelationalDatabaseSnapshot\",\n    \"DeleteAlarm\": \"lightsail:DeleteAlarm\",\n    \"DeleteAutoSnapshot\": \"lightsail:DeleteAutoSnapshot\",\n    \"DeleteBucket\": \"lightsail:DeleteBucket\",\n    \"DeleteBucketAccessKey\": \"lightsail:DeleteBucketAccessKey\",\n    \"DeleteCertificate\": \"lightsail:DeleteCertificate\",\n    \"DeleteContactMethod\": \"lightsail:DeleteContactMethod\",\n    \"DeleteContainerImage\": \"lightsail:DeleteContainerImage\",\n    \"DeleteContainerService\": \"lightsail:DeleteContainerService\",\n    \"DeleteDisk\": \"lightsail:DeleteDisk\",\n    \"DeleteDiskSnapshot\": \"lightsail:DeleteDiskSnapshot\",\n    \"DeleteDistribution\": \"lightsail:DeleteDistribution\",\n    \"DeleteDomain\": \"lightsail:DeleteDomain\",\n    \"DeleteDomainEntry\": \"lightsail:DeleteDomainEntry\",\n    \"DeleteInstance\": \"lightsail:DeleteInstance\",\n    \"DeleteInstanceSnapshot\": \"lightsail:DeleteInstanceSnapshot\",\n    \"DeleteKeyPair\": \"lightsail:DeleteKeyPair\",\n    \"DeleteKnownHostKeys\": \"lightsail:DeleteKnownHostKeys\",\n    \"DeleteLoadBalancer\": \"lightsail:DeleteLoadBalancer\",\n    \"DeleteLoadBalancerTlsCertificate\": \"lightsail:DeleteLoadBalancerTlsCertificate\",\n    \"DeleteRelationalDatabase\": \"lightsail:DeleteRelationalDatabase\",\n    \"DeleteRelationalDatabaseSnapshot\": \"lightsail:DeleteRelationalDatabaseSnapshot\",\n    \"DetachCertificateFromDistribution\": \"lightsail:DetachCertificateFromDistribution\",\n    \"DetachDisk\": \"lightsail:DetachDisk\",\n    \"DetachInstancesFromLoadBalancer\": \"lightsail:DetachInstancesFromLoadBalancer\",\n    \"DetachStaticIp\": \"lightsail:DetachStaticIp\",\n    \"DisableAddOn\": \"lightsail:DisableAddOn\",\n    \"DownloadDefaultKeyPair\": \"lightsail:DownloadDefaultKeyPair\",\n    \"EnableAddOn\": \"lightsail:EnableAddOn\",\n    \"ExportSnapshot\": \"lightsail:ExportSnapshot\",\n    \"GetActiveNames\": \"lightsail:GetActiveNames\",\n    \"GetAlarms\": \"lightsail:GetAlarms\",\n    \"GetAutoSnapshots\": \"lightsail:GetAutoSnapshots\",\n    \"GetBlueprints\": \"lightsail:GetBlueprints\",\n    \"GetBucketAccessKeys\": \"lightsail:GetBucketAccessKeys\",\n    \"GetBucketBundles\": \"lightsail:GetBucketBundles\",\n    \"GetBucketMetricData\": \"lightsail:GetBucketMetricData\",\n    \"GetBuckets\": \"lightsail:GetBuckets\",\n    \"GetBundles\": \"lightsail:GetBundles\",\n    \"GetCertificates\": \"lightsail:GetCertificates\",\n    \"GetCloudFormationStackRecords\": \"lightsail:GetCloudFormationStackRecords\",\n    \"GetContactMethods\": \"lightsail:GetContactMethods\",\n    \"GetContainerAPIMetadata\": \"lightsail:GetContainerAPIMetadata\",\n    \"GetContainerImages\": \"lightsail:GetContainerImages\",\n    \"GetContainerLog\": \"lightsail:GetContainerLog\",\n    \"GetContainerServiceDeployments\": \"lightsail:GetContainerServiceDeployments\",\n    \"GetContainerServiceMetricData\": \"lightsail:GetContainerServiceMetricData\",\n    \"GetContainerServicePowers\": \"lightsail:GetContainerServicePowers\",\n    \"GetContainerServices\": \"lightsail:GetContainerServices\",\n    \"GetDisk\": \"lightsail:GetDisk\",\n    \"GetDiskSnapshot\": \"lightsail:GetDiskSnapshot\",\n    \"GetDiskSnapshots\": \"lightsail:GetDiskSnapshots\",\n    \"GetDisks\": \"lightsail:GetDisks\",\n    \"GetDistributionBundles\": \"lightsail:GetDistributionBundles\",\n    \"GetDistributionLatestCacheReset\": \"lightsail:GetDistributionLatestCacheReset\",\n    \"GetDistributionMetricData\": \"lightsail:GetDistributionMetricData\",\n    \"GetDistributions\": \"lightsail:GetDistributions\",\n    \"GetDomain\": \"lightsail:GetDomain\",\n    \"GetDomains\": \"lightsail:GetDomains\",\n    \"GetExportSnapshotRecords\": \"lightsail:GetExportSnapshotRecords\",\n    \"GetInstance\": \"lightsail:GetInstance\",\n    \"GetInstanceAccessDetails\": \"lightsail:GetInstanceAccessDetails\",\n    \"GetInstanceMetricData\": \"lightsail:GetInstanceMetricData\",\n    \"GetInstancePortStates\": \"lightsail:GetInstancePortStates\",\n    \"GetInstanceSnapshot\": \"lightsail:GetInstanceSnapshot\",\n    \"GetInstanceSnapshots\": \"lightsail:GetInstanceSnapshots\",\n    \"GetInstanceState\": \"lightsail:GetInstanceState\",\n    \"GetInstances\": \"lightsail:GetInstances\",\n    \"GetKeyPair\": \"lightsail:GetKeyPair\",\n    \"GetKeyPairs\": \"lightsail:GetKeyPairs\",\n    \"GetLoadBalancer\": \"lightsail:GetLoadBalancer\",\n    \"GetLoadBalancerMetricData\": \"lightsail:GetLoadBalancerMetricData\",\n    \"GetLoadBalancerTlsCertificates\": \"lightsail:GetLoadBalancerTlsCertificates\",\n    \"GetLoadBalancers\": \"lightsail:GetLoadBalancers\",\n    \"GetOperation\": \"lightsail:GetOperation\",\n    \"GetOperations\": \"lightsail:GetOperations\",\n    \"GetOperationsForResource\": \"lightsail:GetOperationsForResource\",\n    \"GetRegions\": \"lightsail:GetRegions\",\n    \"GetRelationalDatabase\": \"lightsail:GetRelationalDatabase\",\n    \"GetRelationalDatabaseBlueprints\": \"lightsail:GetRelationalDatabaseBlueprints\",\n    \"GetRelationalDatabaseBundles\": \"lightsail:GetRelationalDatabaseBundles\",\n    \"GetRelationalDatabaseEvents\": \"lightsail:GetRelationalDatabaseEvents\",\n    \"GetRelationalDatabaseLogEvents\": \"lightsail:GetRelationalDatabaseLogEvents\",\n    \"GetRelationalDatabaseLogStreams\": \"lightsail:GetRelationalDatabaseLogStreams\",\n    \"GetRelationalDatabaseMasterUserPassword\": \"lightsail:GetRelationalDatabaseMasterUserPassword\",\n    \"GetRelationalDatabaseMetricData\": \"lightsail:GetRelationalDatabaseMetricData\",\n    \"GetRelationalDatabaseParameters\": \"lightsail:GetRelationalDatabaseParameters\",\n    \"GetRelationalDatabaseSnapshot\": \"lightsail:GetRelationalDatabaseSnapshot\",\n    \"GetRelationalDatabaseSnapshots\": \"lightsail:GetRelationalDatabaseSnapshots\",\n    \"GetRelationalDatabases\": \"lightsail:GetRelationalDatabases\",\n    \"GetStaticIp\": \"lightsail:GetStaticIp\",\n    \"GetStaticIps\": \"lightsail:GetStaticIps\",\n    \"ImportKeyPair\": \"lightsail:ImportKeyPair\",\n    \"IsVpcPeered\": \"lightsail:IsVpcPeered\",\n    \"OpenInstancePublicPorts\": \"lightsail:OpenInstancePublicPorts\",\n    \"PeerVpc\": \"lightsail:PeerVpc\",\n    \"PutAlarm\": \"lightsail:PutAlarm\",\n    \"PutInstancePublicPorts\": \"lightsail:PutInstancePublicPorts\",\n    \"RebootInstance\": \"lightsail:RebootInstance\",\n    \"RebootRelationalDatabase\": \"lightsail:RebootRelationalDatabase\",\n    \"RegisterContainerImage\": \"lightsail:RegisterContainerImage\",\n    \"ReleaseStaticIp\": \"lightsail:ReleaseStaticIp\",\n    \"ResetDistributionCache\": \"lightsail:ResetDistributionCache\",\n    \"SendContactMethodVerification\": \"lightsail:SendContactMethodVerification\",\n    \"SetIpAddressType\": \"lightsail:SetIpAddressType\",\n    \"SetResourceAccessForBucket\": \"lightsail:SetResourceAccessForBucket\",\n    \"StartInstance\": \"lightsail:StartInstance\",\n    \"StartRelationalDatabase\": \"lightsail:StartRelationalDatabase\",\n    \"StopInstance\": \"lightsail:StopInstance\",\n    \"StopRelationalDatabase\": \"lightsail:StopRelationalDatabase\",\n    \"TagResource\": \"lightsail:TagResource\",\n    \"TestAlarm\": \"lightsail:TestAlarm\",\n    \"UnpeerVpc\": \"lightsail:UnpeerVpc\",\n    \"UntagResource\": \"lightsail:UntagResource\",\n    \"UpdateBucket\": \"lightsail:UpdateBucket\",\n    \"UpdateBucketBundle\": \"lightsail:UpdateBucketBundle\",\n    \"UpdateContainerService\": \"lightsail:UpdateContainerService\",\n    \"UpdateDistribution\": \"lightsail:UpdateDistribution\",\n    \"UpdateDistributionBundle\": \"lightsail:UpdateDistributionBundle\",\n    \"UpdateDomainEntry\": \"lightsail:UpdateDomainEntry\",\n    \"UpdateLoadBalancerAttribute\": \"lightsail:UpdateLoadBalancerAttribute\",\n    \"UpdateRelationalDatabase\": \"lightsail:UpdateRelationalDatabase\",\n    \"UpdateRelationalDatabaseParameters\": \"lightsail:UpdateRelationalDatabaseParameters\"\n  },\n  \"logs\": {\n    \"AssociateKmsKey\": \"logs:AssociateKmsKey\",\n    \"CancelExportTask\": \"logs:CancelExportTask\",\n    \"CreateExportTask\": \"logs:CreateExportTask\",\n    \"CreateLogGroup\": \"logs:CreateLogGroup\",\n    \"CreateLogStream\": \"logs:CreateLogStream\",\n    \"DeleteDestination\": \"logs:DeleteDestination\",\n    \"DeleteLogGroup\": \"logs:DeleteLogGroup\",\n    \"DeleteLogStream\": \"logs:DeleteLogStream\",\n    \"DeleteMetricFilter\": \"logs:DeleteMetricFilter\",\n    \"DeleteQueryDefinition\": \"logs:DeleteQueryDefinition\",\n    \"DeleteResourcePolicy\": \"logs:DeleteResourcePolicy\",\n    \"DeleteRetentionPolicy\": \"logs:DeleteRetentionPolicy\",\n    \"DeleteSubscriptionFilter\": \"logs:DeleteSubscriptionFilter\",\n    \"DescribeDestinations\": \"logs:DescribeDestinations\",\n    \"DescribeExportTasks\": \"logs:DescribeExportTasks\",\n    \"DescribeLogGroups\": \"logs:DescribeLogGroups\",\n    \"DescribeLogStreams\": \"logs:DescribeLogStreams\",\n    \"DescribeMetricFilters\": \"logs:DescribeMetricFilters\",\n    \"DescribeQueries\": \"logs:DescribeQueries\",\n    \"DescribeQueryDefinitions\": \"logs:DescribeQueryDefinitions\",\n    \"DescribeResourcePolicies\": \"logs:DescribeResourcePolicies\",\n    \"DescribeSubscriptionFilters\": \"logs:DescribeSubscriptionFilters\",\n    \"DisassociateKmsKey\": \"logs:DisassociateKmsKey\",\n    \"FilterLogEvents\": \"logs:FilterLogEvents\",\n    \"GetLogEvents\": \"logs:GetLogEvents\",\n    \"GetLogGroupFields\": \"logs:GetLogGroupFields\",\n    \"GetLogRecord\": \"logs:GetLogRecord\",\n    \"GetQueryResults\": \"logs:GetQueryResults\",\n    \"ListTagsLogGroup\": \"logs:ListTagsLogGroup\",\n    \"PutDestination\": \"logs:PutDestination\",\n    \"PutDestinationPolicy\": \"logs:PutDestinationPolicy\",\n    \"PutLogEvents\": \"logs:PutLogEvents\",\n    \"PutMetricFilter\": \"logs:PutMetricFilter\",\n    \"PutQueryDefinition\": \"logs:PutQueryDefinition\",\n    \"PutResourcePolicy\": \"logs:PutResourcePolicy\",\n    \"PutRetentionPolicy\": \"logs:PutRetentionPolicy\",\n    \"PutSubscriptionFilter\": \"logs:PutSubscriptionFilter\",\n    \"StartQuery\": \"logs:StartQuery\",\n    \"StopQuery\": \"logs:StopQuery\",\n    \"TagLogGroup\": \"logs:TagLogGroup\",\n    \"TestMetricFilter\": \"logs:TestMetricFilter\",\n    \"UntagLogGroup\": \"logs:UntagLogGroup\"\n  },\n  \"lookoutequipment\": {\n    \"CreateDataset\": \"lookoutequipment:CreateDataset\",\n    \"CreateInferenceScheduler\": \"lookoutequipment:CreateInferenceScheduler\",\n    \"CreateModel\": \"lookoutequipment:CreateModel\",\n    \"DeleteDataset\": \"lookoutequipment:DeleteDataset\",\n    \"DeleteInferenceScheduler\": \"lookoutequipment:DeleteInferenceScheduler\",\n    \"DeleteModel\": \"lookoutequipment:DeleteModel\",\n    \"DescribeDataIngestionJob\": \"lookoutequipment:DescribeDataIngestionJob\",\n    \"DescribeDataset\": \"lookoutequipment:DescribeDataset\",\n    \"DescribeInferenceScheduler\": \"lookoutequipment:DescribeInferenceScheduler\",\n    \"DescribeModel\": \"lookoutequipment:DescribeModel\",\n    \"ListDataIngestionJobs\": \"lookoutequipment:ListDataIngestionJobs\",\n    \"ListDatasets\": \"lookoutequipment:ListDatasets\",\n    \"ListInferenceExecutions\": \"lookoutequipment:ListInferenceExecutions\",\n    \"ListInferenceSchedulers\": \"lookoutequipment:ListInferenceSchedulers\",\n    \"ListModels\": \"lookoutequipment:ListModels\",\n    \"ListTagsForResource\": \"lookoutequipment:ListTagsForResource\",\n    \"StartDataIngestionJob\": \"lookoutequipment:StartDataIngestionJob\",\n    \"StartInferenceScheduler\": \"lookoutequipment:StartInferenceScheduler\",\n    \"StopInferenceScheduler\": \"lookoutequipment:StopInferenceScheduler\",\n    \"TagResource\": \"lookoutequipment:TagResource\",\n    \"UntagResource\": \"lookoutequipment:UntagResource\",\n    \"UpdateInferenceScheduler\": \"lookoutequipment:UpdateInferenceScheduler\"\n  },\n  \"lookoutmetrics\": {\n    \"ActivateAnomalyDetector\": \"lookoutmetrics:ActivateAnomalyDetector\",\n    \"BackTestAnomalyDetector\": \"lookoutmetrics:BackTestAnomalyDetector\",\n    \"CreateAlert\": \"lookoutmetrics:CreateAlert\",\n    \"CreateAnomalyDetector\": \"lookoutmetrics:CreateAnomalyDetector\",\n    \"CreateMetricSet\": \"lookoutmetrics:CreateMetricSet\",\n    \"DeleteAlert\": \"lookoutmetrics:DeleteAlert\",\n    \"DeleteAnomalyDetector\": \"lookoutmetrics:DeleteAnomalyDetector\",\n    \"DescribeAlert\": \"lookoutmetrics:DescribeAlert\",\n    \"DescribeAnomalyDetectionExecutions\": \"lookoutmetrics:DescribeAnomalyDetectionExecutions\",\n    \"DescribeAnomalyDetector\": \"lookoutmetrics:DescribeAnomalyDetector\",\n    \"DescribeMetricSet\": \"lookoutmetrics:DescribeMetricSet\",\n    \"GetAnomalyGroup\": \"lookoutmetrics:GetAnomalyGroup\",\n    \"GetFeedback\": \"lookoutmetrics:GetFeedback\",\n    \"GetSampleData\": \"lookoutmetrics:GetSampleData\",\n    \"ListAlerts\": \"lookoutmetrics:ListAlerts\",\n    \"ListAnomalyDetectors\": \"lookoutmetrics:ListAnomalyDetectors\",\n    \"ListAnomalyGroupRelatedMetrics\": \"lookoutmetrics:ListAnomalyGroupRelatedMetrics\",\n    \"ListAnomalyGroupSummaries\": \"lookoutmetrics:ListAnomalyGroupSummaries\",\n    \"ListAnomalyGroupTimeSeries\": \"lookoutmetrics:ListAnomalyGroupTimeSeries\",\n    \"ListMetricSets\": \"lookoutmetrics:ListMetricSets\",\n    \"ListTagsForResource\": \"lookoutmetrics:ListTagsForResource\",\n    \"PutFeedback\": \"lookoutmetrics:PutFeedback\",\n    \"TagResource\": \"lookoutmetrics:TagResource\",\n    \"UntagResource\": \"lookoutmetrics:UntagResource\",\n    \"UpdateAnomalyDetector\": \"lookoutmetrics:UpdateAnomalyDetector\",\n    \"UpdateMetricSet\": \"lookoutmetrics:UpdateMetricSet\"\n  },\n  \"lookoutvision\": {\n    \"CreateDataset\": \"lookoutvision:CreateDataset\",\n    \"CreateModel\": \"lookoutvision:CreateModel\",\n    \"CreateProject\": \"lookoutvision:CreateProject\",\n    \"DeleteDataset\": \"lookoutvision:DeleteDataset\",\n    \"DeleteModel\": \"lookoutvision:DeleteModel\",\n    \"DeleteProject\": \"lookoutvision:DeleteProject\",\n    \"DescribeDataset\": \"lookoutvision:DescribeDataset\",\n    \"DescribeModel\": \"lookoutvision:DescribeModel\",\n    \"DescribeModelPackagingJob\": \"lookoutvision:DescribeModelPackagingJob\",\n    \"DescribeProject\": \"lookoutvision:DescribeProject\",\n    \"DetectAnomalies\": \"lookoutvision:DetectAnomalies\",\n    \"ListDatasetEntries\": \"lookoutvision:ListDatasetEntries\",\n    \"ListModelPackagingJobs\": \"lookoutvision:ListModelPackagingJobs\",\n    \"ListModels\": \"lookoutvision:ListModels\",\n    \"ListProjects\": \"lookoutvision:ListProjects\",\n    \"ListTagsForResource\": \"lookoutvision:ListTagsForResource\",\n    \"StartModel\": \"lookoutvision:StartModel\",\n    \"StartModelPackagingJob\": \"lookoutvision:StartModelPackagingJob\",\n    \"StopModel\": \"lookoutvision:StopModel\",\n    \"TagResource\": \"lookoutvision:TagResource\",\n    \"UntagResource\": \"lookoutvision:UntagResource\",\n    \"UpdateDatasetEntries\": \"lookoutvision:UpdateDatasetEntries\"\n  },\n  \"machinelearning\": {\n    \"AddTags\": \"machinelearning:AddTags\",\n    \"CreateBatchPrediction\": \"machinelearning:CreateBatchPrediction\",\n    \"CreateDataSourceFromRDS\": \"machinelearning:CreateDataSourceFromRDS\",\n    \"CreateDataSourceFromRedshift\": \"machinelearning:CreateDataSourceFromRedshift\",\n    \"CreateDataSourceFromS3\": \"machinelearning:CreateDataSourceFromS3\",\n    \"CreateEvaluation\": \"machinelearning:CreateEvaluation\",\n    \"CreateMLModel\": \"machinelearning:CreateMLModel\",\n    \"CreateRealtimeEndpoint\": \"machinelearning:CreateRealtimeEndpoint\",\n    \"DeleteBatchPrediction\": \"machinelearning:DeleteBatchPrediction\",\n    \"DeleteDataSource\": \"machinelearning:DeleteDataSource\",\n    \"DeleteEvaluation\": \"machinelearning:DeleteEvaluation\",\n    \"DeleteMLModel\": \"machinelearning:DeleteMLModel\",\n    \"DeleteRealtimeEndpoint\": \"machinelearning:DeleteRealtimeEndpoint\",\n    \"DeleteTags\": \"machinelearning:DeleteTags\",\n    \"DescribeBatchPredictions\": \"machinelearning:DescribeBatchPredictions\",\n    \"DescribeDataSources\": \"machinelearning:DescribeDataSources\",\n    \"DescribeEvaluations\": \"machinelearning:DescribeEvaluations\",\n    \"DescribeMLModels\": \"machinelearning:DescribeMLModels\",\n    \"DescribeTags\": \"machinelearning:DescribeTags\",\n    \"GetBatchPrediction\": \"machinelearning:GetBatchPrediction\",\n    \"GetDataSource\": \"machinelearning:GetDataSource\",\n    \"GetEvaluation\": \"machinelearning:GetEvaluation\",\n    \"GetMLModel\": \"machinelearning:GetMLModel\",\n    \"Predict\": \"machinelearning:Predict\",\n    \"UpdateBatchPrediction\": \"machinelearning:UpdateBatchPrediction\",\n    \"UpdateDataSource\": \"machinelearning:UpdateDataSource\",\n    \"UpdateEvaluation\": \"machinelearning:UpdateEvaluation\",\n    \"UpdateMLModel\": \"machinelearning:UpdateMLModel\"\n  },\n  \"macie\": {\n    \"AssociateMemberAccount\": \"macie:AssociateMemberAccount\",\n    \"AssociateS3Resources\": \"macie:AssociateS3Resources\",\n    \"DisassociateMemberAccount\": \"macie:DisassociateMemberAccount\",\n    \"DisassociateS3Resources\": \"macie:DisassociateS3Resources\",\n    \"ListMemberAccounts\": \"macie:ListMemberAccounts\",\n    \"ListS3Resources\": \"macie:ListS3Resources\",\n    \"UpdateS3Resources\": \"macie:UpdateS3Resources\"\n  },\n  \"macie2\": {\n    \"AcceptInvitation\": \"macie2:AcceptInvitation\",\n    \"BatchGetCustomDataIdentifiers\": \"macie2:BatchGetCustomDataIdentifiers\",\n    \"CreateClassificationJob\": \"macie2:CreateClassificationJob\",\n    \"CreateCustomDataIdentifier\": \"macie2:CreateCustomDataIdentifier\",\n    \"CreateFindingsFilter\": \"macie2:CreateFindingsFilter\",\n    \"CreateInvitations\": \"macie2:CreateInvitations\",\n    \"CreateMember\": \"macie2:CreateMember\",\n    \"CreateSampleFindings\": \"macie2:CreateSampleFindings\",\n    \"DeclineInvitations\": \"macie2:DeclineInvitations\",\n    \"DeleteCustomDataIdentifier\": \"macie2:DeleteCustomDataIdentifier\",\n    \"DeleteFindingsFilter\": \"macie2:DeleteFindingsFilter\",\n    \"DeleteInvitations\": \"macie2:DeleteInvitations\",\n    \"DeleteMember\": \"macie2:DeleteMember\",\n    \"DescribeBuckets\": \"macie2:DescribeBuckets\",\n    \"DescribeClassificationJob\": \"macie2:DescribeClassificationJob\",\n    \"DescribeOrganizationConfiguration\": \"macie2:DescribeOrganizationConfiguration\",\n    \"DisableMacie\": \"macie2:DisableMacie\",\n    \"DisableOrganizationAdminAccount\": \"macie2:DisableOrganizationAdminAccount\",\n    \"DisassociateFromAdministratorAccount\": \"macie2:DisassociateFromAdministratorAccount\",\n    \"DisassociateFromMasterAccount\": \"macie2:DisassociateFromMasterAccount\",\n    \"DisassociateMember\": \"macie2:DisassociateMember\",\n    \"EnableMacie\": \"macie2:EnableMacie\",\n    \"EnableOrganizationAdminAccount\": \"macie2:EnableOrganizationAdminAccount\",\n    \"GetAdministratorAccount\": \"macie2:GetAdministratorAccount\",\n    \"GetBucketStatistics\": \"macie2:GetBucketStatistics\",\n    \"GetClassificationExportConfiguration\": \"macie2:GetClassificationExportConfiguration\",\n    \"GetCustomDataIdentifier\": \"macie2:GetCustomDataIdentifier\",\n    \"GetFindingStatistics\": \"macie2:GetFindingStatistics\",\n    \"GetFindings\": \"macie2:GetFindings\",\n    \"GetFindingsFilter\": \"macie2:GetFindingsFilter\",\n    \"GetFindingsPublicationConfiguration\": \"macie2:GetFindingsPublicationConfiguration\",\n    \"GetInvitationsCount\": \"macie2:GetInvitationsCount\",\n    \"GetMacieSession\": \"macie2:GetMacieSession\",\n    \"GetMasterAccount\": \"macie2:GetMasterAccount\",\n    \"GetMember\": \"macie2:GetMember\",\n    \"GetUsageStatistics\": \"macie2:GetUsageStatistics\",\n    \"GetUsageTotals\": \"macie2:GetUsageTotals\",\n    \"ListClassificationJobs\": \"macie2:ListClassificationJobs\",\n    \"ListCustomDataIdentifiers\": \"macie2:ListCustomDataIdentifiers\",\n    \"ListFindings\": \"macie2:ListFindings\",\n    \"ListFindingsFilters\": \"macie2:ListFindingsFilters\",\n    \"ListInvitations\": \"macie2:ListInvitations\",\n    \"ListManagedDataIdentifiers\": \"macie2:ListManagedDataIdentifiers\",\n    \"ListMembers\": \"macie2:ListMembers\",\n    \"ListOrganizationAdminAccounts\": \"macie2:ListOrganizationAdminAccounts\",\n    \"ListTagsForResource\": \"macie2:ListTagsForResource\",\n    \"PutClassificationExportConfiguration\": \"macie2:PutClassificationExportConfiguration\",\n    \"PutFindingsPublicationConfiguration\": \"macie2:PutFindingsPublicationConfiguration\",\n    \"SearchResources\": \"macie2:SearchResources\",\n    \"TagResource\": \"macie2:TagResource\",\n    \"TestCustomDataIdentifier\": \"macie2:TestCustomDataIdentifier\",\n    \"UntagResource\": \"macie2:UntagResource\",\n    \"UpdateClassificationJob\": \"macie2:UpdateClassificationJob\",\n    \"UpdateFindingsFilter\": \"macie2:UpdateFindingsFilter\",\n    \"UpdateMacieSession\": \"macie2:UpdateMacieSession\",\n    \"UpdateMemberSession\": \"macie2:UpdateMemberSession\",\n    \"UpdateOrganizationConfiguration\": \"macie2:UpdateOrganizationConfiguration\"\n  },\n  \"managedblockchain\": {\n    \"CreateMember\": \"managedblockchain:CreateMember\",\n    \"CreateNetwork\": \"managedblockchain:CreateNetwork\",\n    \"CreateNode\": \"managedblockchain:CreateNode\",\n    \"CreateProposal\": \"managedblockchain:CreateProposal\",\n    \"DeleteMember\": \"managedblockchain:DeleteMember\",\n    \"DeleteNode\": \"managedblockchain:DeleteNode\",\n    \"GetMember\": \"managedblockchain:GetMember\",\n    \"GetNetwork\": \"managedblockchain:GetNetwork\",\n    \"GetNode\": \"managedblockchain:GetNode\",\n    \"GetProposal\": \"managedblockchain:GetProposal\",\n    \"ListInvitations\": \"managedblockchain:ListInvitations\",\n    \"ListMembers\": \"managedblockchain:ListMembers\",\n    \"ListNetworks\": \"managedblockchain:ListNetworks\",\n    \"ListNodes\": \"managedblockchain:ListNodes\",\n    \"ListProposalVotes\": \"managedblockchain:ListProposalVotes\",\n    \"ListProposals\": \"managedblockchain:ListProposals\",\n    \"ListTagsForResource\": \"managedblockchain:ListTagsForResource\",\n    \"RejectInvitation\": \"managedblockchain:RejectInvitation\",\n    \"TagResource\": \"managedblockchain:TagResource\",\n    \"UntagResource\": \"managedblockchain:UntagResource\",\n    \"UpdateMember\": \"managedblockchain:UpdateMember\",\n    \"UpdateNode\": \"managedblockchain:UpdateNode\",\n    \"VoteOnProposal\": \"managedblockchain:VoteOnProposal\"\n  },\n  \"mediaconnect\": {\n    \"AddFlowMediaStreams\": \"mediaconnect:AddFlowMediaStreams\",\n    \"AddFlowOutputs\": \"mediaconnect:AddFlowOutputs\",\n    \"AddFlowSources\": \"mediaconnect:AddFlowSources\",\n    \"AddFlowVpcInterfaces\": \"mediaconnect:AddFlowVpcInterfaces\",\n    \"CreateFlow\": \"mediaconnect:CreateFlow\",\n    \"DeleteFlow\": \"mediaconnect:DeleteFlow\",\n    \"DescribeFlow\": \"mediaconnect:DescribeFlow\",\n    \"DescribeOffering\": \"mediaconnect:DescribeOffering\",\n    \"DescribeReservation\": \"mediaconnect:DescribeReservation\",\n    \"GrantFlowEntitlements\": \"mediaconnect:GrantFlowEntitlements\",\n    \"ListEntitlements\": \"mediaconnect:ListEntitlements\",\n    \"ListFlows\": \"mediaconnect:ListFlows\",\n    \"ListOfferings\": \"mediaconnect:ListOfferings\",\n    \"ListReservations\": \"mediaconnect:ListReservations\",\n    \"ListTagsForResource\": \"mediaconnect:ListTagsForResource\",\n    \"PurchaseOffering\": \"mediaconnect:PurchaseOffering\",\n    \"RemoveFlowMediaStream\": \"mediaconnect:RemoveFlowMediaStream\",\n    \"RemoveFlowOutput\": \"mediaconnect:RemoveFlowOutput\",\n    \"RemoveFlowSource\": \"mediaconnect:RemoveFlowSource\",\n    \"RemoveFlowVpcInterface\": \"mediaconnect:RemoveFlowVpcInterface\",\n    \"RevokeFlowEntitlement\": \"mediaconnect:RevokeFlowEntitlement\",\n    \"StartFlow\": \"mediaconnect:StartFlow\",\n    \"StopFlow\": \"mediaconnect:StopFlow\",\n    \"TagResource\": \"mediaconnect:TagResource\",\n    \"UntagResource\": \"mediaconnect:UntagResource\",\n    \"UpdateFlow\": \"mediaconnect:UpdateFlow\",\n    \"UpdateFlowEntitlement\": \"mediaconnect:UpdateFlowEntitlement\",\n    \"UpdateFlowMediaStream\": \"mediaconnect:UpdateFlowMediaStream\",\n    \"UpdateFlowOutput\": \"mediaconnect:UpdateFlowOutput\",\n    \"UpdateFlowSource\": \"mediaconnect:UpdateFlowSource\"\n  },\n  \"mediaconvert\": {\n    \"AssociateCertificate\": \"mediaconvert:AssociateCertificate\",\n    \"CancelJob\": \"mediaconvert:CancelJob\",\n    \"CreateJob\": \"mediaconvert:CreateJob\",\n    \"CreateJobTemplate\": \"mediaconvert:CreateJobTemplate\",\n    \"CreatePreset\": \"mediaconvert:CreatePreset\",\n    \"CreateQueue\": \"mediaconvert:CreateQueue\",\n    \"DeleteJobTemplate\": \"mediaconvert:DeleteJobTemplate\",\n    \"DeletePolicy\": \"mediaconvert:DeletePolicy\",\n    \"DeletePreset\": \"mediaconvert:DeletePreset\",\n    \"DeleteQueue\": \"mediaconvert:DeleteQueue\",\n    \"DescribeEndpoints\": \"mediaconvert:DescribeEndpoints\",\n    \"DisassociateCertificate\": \"mediaconvert:DisassociateCertificate\",\n    \"GetJob\": \"mediaconvert:GetJob\",\n    \"GetJobTemplate\": \"mediaconvert:GetJobTemplate\",\n    \"GetPolicy\": \"mediaconvert:GetPolicy\",\n    \"GetPreset\": \"mediaconvert:GetPreset\",\n    \"GetQueue\": \"mediaconvert:GetQueue\",\n    \"ListJobTemplates\": \"mediaconvert:ListJobTemplates\",\n    \"ListJobs\": \"mediaconvert:ListJobs\",\n    \"ListPresets\": \"mediaconvert:ListPresets\",\n    \"ListQueues\": \"mediaconvert:ListQueues\",\n    \"ListTagsForResource\": \"mediaconvert:ListTagsForResource\",\n    \"PutPolicy\": \"mediaconvert:PutPolicy\",\n    \"TagResource\": \"mediaconvert:TagResource\",\n    \"UntagResource\": \"mediaconvert:UntagResource\",\n    \"UpdateJobTemplate\": \"mediaconvert:UpdateJobTemplate\",\n    \"UpdatePreset\": \"mediaconvert:UpdatePreset\",\n    \"UpdateQueue\": \"mediaconvert:UpdateQueue\"\n  },\n  \"mediapackage-vod\": {\n    \"ConfigureLogs\": \"mediapackage-vod:ConfigureLogs\",\n    \"CreateAsset\": \"mediapackage-vod:CreateAsset\",\n    \"CreatePackagingConfiguration\": \"mediapackage-vod:CreatePackagingConfiguration\",\n    \"CreatePackagingGroup\": \"mediapackage-vod:CreatePackagingGroup\",\n    \"DeleteAsset\": \"mediapackage-vod:DeleteAsset\",\n    \"DeletePackagingConfiguration\": \"mediapackage-vod:DeletePackagingConfiguration\",\n    \"DeletePackagingGroup\": \"mediapackage-vod:DeletePackagingGroup\",\n    \"DescribeAsset\": \"mediapackage-vod:DescribeAsset\",\n    \"DescribePackagingConfiguration\": \"mediapackage-vod:DescribePackagingConfiguration\",\n    \"DescribePackagingGroup\": \"mediapackage-vod:DescribePackagingGroup\",\n    \"ListAssets\": \"mediapackage-vod:ListAssets\",\n    \"ListPackagingConfigurations\": \"mediapackage-vod:ListPackagingConfigurations\",\n    \"ListPackagingGroups\": \"mediapackage-vod:ListPackagingGroups\",\n    \"ListTagsForResource\": \"mediapackage-vod:ListTagsForResource\",\n    \"TagResource\": \"mediapackage-vod:TagResource\",\n    \"UntagResource\": \"mediapackage-vod:UntagResource\",\n    \"UpdatePackagingGroup\": \"mediapackage-vod:UpdatePackagingGroup\"\n  },\n  \"mediatailor\": {\n    \"CreateChannel\": \"mediatailor:CreateChannel\",\n    \"CreateProgram\": \"mediatailor:CreateProgram\",\n    \"CreateSourceLocation\": \"mediatailor:CreateSourceLocation\",\n    \"CreateVodSource\": \"mediatailor:CreateVodSource\",\n    \"DeleteChannel\": \"mediatailor:DeleteChannel\",\n    \"DeleteChannelPolicy\": \"mediatailor:DeleteChannelPolicy\",\n    \"DeletePlaybackConfiguration\": \"mediatailor:DeletePlaybackConfiguration\",\n    \"DeleteProgram\": \"mediatailor:DeleteProgram\",\n    \"DeleteSourceLocation\": \"mediatailor:DeleteSourceLocation\",\n    \"DeleteVodSource\": \"mediatailor:DeleteVodSource\",\n    \"DescribeChannel\": \"mediatailor:DescribeChannel\",\n    \"DescribeProgram\": \"mediatailor:DescribeProgram\",\n    \"DescribeSourceLocation\": \"mediatailor:DescribeSourceLocation\",\n    \"DescribeVodSource\": \"mediatailor:DescribeVodSource\",\n    \"GetChannelPolicy\": \"mediatailor:GetChannelPolicy\",\n    \"GetChannelSchedule\": \"mediatailor:GetChannelSchedule\",\n    \"GetPlaybackConfiguration\": \"mediatailor:GetPlaybackConfiguration\",\n    \"ListAlerts\": \"mediatailor:ListAlerts\",\n    \"ListChannels\": \"mediatailor:ListChannels\",\n    \"ListPlaybackConfigurations\": \"mediatailor:ListPlaybackConfigurations\",\n    \"ListSourceLocations\": \"mediatailor:ListSourceLocations\",\n    \"ListTagsForResource\": \"mediatailor:ListTagsForResource\",\n    \"ListVodSources\": \"mediatailor:ListVodSources\",\n    \"PutChannelPolicy\": \"mediatailor:PutChannelPolicy\",\n    \"PutPlaybackConfiguration\": \"mediatailor:PutPlaybackConfiguration\",\n    \"StartChannel\": \"mediatailor:StartChannel\",\n    \"StopChannel\": \"mediatailor:StopChannel\",\n    \"TagResource\": \"mediatailor:TagResource\",\n    \"UntagResource\": \"mediatailor:UntagResource\",\n    \"UpdateChannel\": \"mediatailor:UpdateChannel\",\n    \"UpdateSourceLocation\": \"mediatailor:UpdateSourceLocation\",\n    \"UpdateVodSource\": \"mediatailor:UpdateVodSource\"\n  },\n  \"memorydb\": {\n    \"CopySnapshot\": \"memorydb:CopySnapshot\",\n    \"CreateCluster\": \"memorydb:CreateCluster\",\n    \"CreateParameterGroup\": \"memorydb:CreateParameterGroup\",\n    \"CreateSnapshot\": \"memorydb:CreateSnapshot\",\n    \"CreateSubnetGroup\": \"memorydb:CreateSubnetGroup\",\n    \"CreateUser\": \"memorydb:CreateUser\",\n    \"DeleteCluster\": \"memorydb:DeleteCluster\",\n    \"DeleteParameterGroup\": \"memorydb:DeleteParameterGroup\",\n    \"DeleteSnapshot\": \"memorydb:DeleteSnapshot\",\n    \"DeleteSubnetGroup\": \"memorydb:DeleteSubnetGroup\",\n    \"DeleteUser\": \"memorydb:DeleteUser\",\n    \"DescribeClusters\": \"memorydb:DescribeClusters\",\n    \"DescribeEngineVersions\": \"memorydb:DescribeEngineVersions\",\n    \"DescribeEvents\": \"memorydb:DescribeEvents\",\n    \"DescribeParameterGroups\": \"memorydb:DescribeParameterGroups\",\n    \"DescribeParameters\": \"memorydb:DescribeParameters\",\n    \"DescribeServiceUpdates\": \"memorydb:DescribeServiceUpdates\",\n    \"DescribeSnapshots\": \"memorydb:DescribeSnapshots\",\n    \"DescribeSubnetGroups\": \"memorydb:DescribeSubnetGroups\",\n    \"DescribeUsers\": \"memorydb:DescribeUsers\",\n    \"FailoverShard\": \"memorydb:FailoverShard\",\n    \"ListTags\": \"memorydb:ListTags\",\n    \"ResetParameterGroup\": \"memorydb:ResetParameterGroup\",\n    \"TagResource\": \"memorydb:TagResource\",\n    \"UntagResource\": \"memorydb:UntagResource\",\n    \"UpdateCluster\": \"memorydb:UpdateCluster\",\n    \"UpdateParameterGroup\": \"memorydb:UpdateParameterGroup\",\n    \"UpdateSubnetGroup\": \"memorydb:UpdateSubnetGroup\",\n    \"UpdateUser\": \"memorydb:UpdateUser\"\n  },\n  \"mgn\": {\n    \"ChangeServerLifeCycleState\": \"mgn:ChangeServerLifeCycleState\",\n    \"CreateReplicationConfigurationTemplate\": \"mgn:CreateReplicationConfigurationTemplate\",\n    \"DeleteJob\": \"mgn:DeleteJob\",\n    \"DeleteReplicationConfigurationTemplate\": \"mgn:DeleteReplicationConfigurationTemplate\",\n    \"DeleteSourceServer\": \"mgn:DeleteSourceServer\",\n    \"DeleteVcenterClient\": \"mgn:DeleteVcenterClient\",\n    \"DescribeJobLogItems\": \"mgn:DescribeJobLogItems\",\n    \"DescribeJobs\": \"mgn:DescribeJobs\",\n    \"DescribeReplicationConfigurationTemplates\": \"mgn:DescribeReplicationConfigurationTemplates\",\n    \"DescribeSourceServers\": \"mgn:DescribeSourceServers\",\n    \"DescribeVcenterClients\": \"mgn:DescribeVcenterClients\",\n    \"DisconnectFromService\": \"mgn:DisconnectFromService\",\n    \"FinalizeCutover\": \"mgn:FinalizeCutover\",\n    \"GetLaunchConfiguration\": \"mgn:GetLaunchConfiguration\",\n    \"GetReplicationConfiguration\": \"mgn:GetReplicationConfiguration\",\n    \"InitializeService\": \"mgn:InitializeService\",\n    \"ListTagsForResource\": \"mgn:ListTagsForResource\",\n    \"MarkAsArchived\": \"mgn:MarkAsArchived\",\n    \"RetryDataReplication\": \"mgn:RetryDataReplication\",\n    \"StartCutover\": \"mgn:StartCutover\",\n    \"StartReplication\": \"mgn:StartReplication\",\n    \"StartTest\": \"mgn:StartTest\",\n    \"TagResource\": \"mgn:TagResource\",\n    \"TerminateTargetInstances\": \"mgn:TerminateTargetInstances\",\n    \"UntagResource\": \"mgn:UntagResource\",\n    \"UpdateLaunchConfiguration\": \"mgn:UpdateLaunchConfiguration\",\n    \"UpdateReplicationConfiguration\": \"mgn:UpdateReplicationConfiguration\",\n    \"UpdateReplicationConfigurationTemplate\": \"mgn:UpdateReplicationConfigurationTemplate\",\n    \"UpdateSourceServerReplicationType\": \"mgn:UpdateSourceServerReplicationType\"\n  },\n  \"mobile\": {\n    \"CreateProject\": \"mobilehub:CreateProject\",\n    \"DeleteProject\": \"mobilehub:DeleteProject\",\n    \"DescribeBundle\": \"mobilehub:DescribeBundle\",\n    \"ExportBundle\": \"mobilehub:ExportBundle\",\n    \"ExportProject\": \"mobilehub:ExportProject\",\n    \"ListBundles\": \"mobilehub:ListBundles\",\n    \"ListProjects\": \"mobilehub:ListProjects\",\n    \"UpdateProject\": \"mobilehub:UpdateProject\"\n  },\n  \"mq\": {\n    \"CreateBroker\": \"mq:CreateBroker\",\n    \"CreateConfiguration\": \"mq:CreateConfiguration\",\n    \"CreateTags\": \"mq:CreateTags\",\n    \"CreateUser\": \"mq:CreateUser\",\n    \"DeleteBroker\": \"mq:DeleteBroker\",\n    \"DeleteTags\": \"mq:DeleteTags\",\n    \"DeleteUser\": \"mq:DeleteUser\",\n    \"DescribeBroker\": \"mq:DescribeBroker\",\n    \"DescribeBrokerEngineTypes\": \"mq:DescribeBrokerEngineTypes\",\n    \"DescribeBrokerInstanceOptions\": \"mq:DescribeBrokerInstanceOptions\",\n    \"DescribeConfiguration\": \"mq:DescribeConfiguration\",\n    \"DescribeConfigurationRevision\": \"mq:DescribeConfigurationRevision\",\n    \"DescribeUser\": \"mq:DescribeUser\",\n    \"ListBrokers\": \"mq:ListBrokers\",\n    \"ListConfigurationRevisions\": \"mq:ListConfigurationRevisions\",\n    \"ListConfigurations\": \"mq:ListConfigurations\",\n    \"ListTags\": \"mq:ListTags\",\n    \"ListUsers\": \"mq:ListUsers\",\n    \"RebootBroker\": \"mq:RebootBroker\",\n    \"UpdateBroker\": \"mq:UpdateBroker\",\n    \"UpdateConfiguration\": \"mq:UpdateConfiguration\",\n    \"UpdateUser\": \"mq:UpdateUser\"\n  },\n  \"mturk\": {\n    \"AcceptQualificationRequest\": \"mechanicalturk:AcceptQualificationRequest\",\n    \"ApproveAssignment\": \"mechanicalturk:ApproveAssignment\",\n    \"AssociateQualificationWithWorker\": \"mechanicalturk:AssociateQualificationWithWorker\",\n    \"CreateAdditionalAssignmentsForHIT\": \"mechanicalturk:CreateAdditionalAssignmentsForHIT\",\n    \"CreateHIT\": \"mechanicalturk:CreateHIT\",\n    \"CreateHITType\": \"mechanicalturk:CreateHITType\",\n    \"CreateHITWithHITType\": \"mechanicalturk:CreateHITWithHITType\",\n    \"CreateQualificationType\": \"mechanicalturk:CreateQualificationType\",\n    \"CreateWorkerBlock\": \"mechanicalturk:CreateWorkerBlock\",\n    \"DeleteHIT\": \"mechanicalturk:DeleteHIT\",\n    \"DeleteQualificationType\": \"mechanicalturk:DeleteQualificationType\",\n    \"DeleteWorkerBlock\": \"mechanicalturk:DeleteWorkerBlock\",\n    \"DisassociateQualificationFromWorker\": \"mechanicalturk:DisassociateQualificationFromWorker\",\n    \"GetAccountBalance\": \"mechanicalturk:GetAccountBalance\",\n    \"GetAssignment\": \"mechanicalturk:GetAssignment\",\n    \"GetFileUploadURL\": \"mechanicalturk:GetFileUploadURL\",\n    \"GetHIT\": \"mechanicalturk:GetHIT\",\n    \"GetQualificationScore\": \"mechanicalturk:GetQualificationScore\",\n    \"GetQualificationType\": \"mechanicalturk:GetQualificationType\",\n    \"ListAssignmentsForHIT\": \"mechanicalturk:ListAssignmentsForHIT\",\n    \"ListBonusPayments\": \"mechanicalturk:ListBonusPayments\",\n    \"ListHITs\": \"mechanicalturk:ListHITs\",\n    \"ListHITsForQualificationType\": \"mechanicalturk:ListHITsForQualificationType\",\n    \"ListQualificationRequests\": \"mechanicalturk:ListQualificationRequests\",\n    \"ListQualificationTypes\": \"mechanicalturk:ListQualificationTypes\",\n    \"ListReviewPolicyResultsForHIT\": \"mechanicalturk:ListReviewPolicyResultsForHIT\",\n    \"ListReviewableHITs\": \"mechanicalturk:ListReviewableHITs\",\n    \"ListWorkerBlocks\": \"mechanicalturk:ListWorkerBlocks\",\n    \"ListWorkersWithQualificationType\": \"mechanicalturk:ListWorkersWithQualificationType\",\n    \"NotifyWorkers\": \"mechanicalturk:NotifyWorkers\",\n    \"RejectAssignment\": \"mechanicalturk:RejectAssignment\",\n    \"RejectQualificationRequest\": \"mechanicalturk:RejectQualificationRequest\",\n    \"SendBonus\": \"mechanicalturk:SendBonus\",\n    \"SendTestEventNotification\": \"mechanicalturk:SendTestEventNotification\",\n    \"UpdateExpirationForHIT\": \"mechanicalturk:UpdateExpirationForHIT\",\n    \"UpdateHITReviewStatus\": \"mechanicalturk:UpdateHITReviewStatus\",\n    \"UpdateHITTypeOfHIT\": \"mechanicalturk:UpdateHITTypeOfHIT\",\n    \"UpdateNotificationSettings\": \"mechanicalturk:UpdateNotificationSettings\",\n    \"UpdateQualificationType\": \"mechanicalturk:UpdateQualificationType\"\n  },\n  \"network-firewall\": {\n    \"AssociateFirewallPolicy\": \"network-firewall:AssociateFirewallPolicy\",\n    \"AssociateSubnets\": \"network-firewall:AssociateSubnets\",\n    \"CreateFirewall\": \"network-firewall:CreateFirewall\",\n    \"CreateFirewallPolicy\": \"network-firewall:CreateFirewallPolicy\",\n    \"CreateRuleGroup\": \"network-firewall:CreateRuleGroup\",\n    \"DeleteFirewall\": \"network-firewall:DeleteFirewall\",\n    \"DeleteFirewallPolicy\": \"network-firewall:DeleteFirewallPolicy\",\n    \"DeleteResourcePolicy\": \"network-firewall:DeleteResourcePolicy\",\n    \"DeleteRuleGroup\": \"network-firewall:DeleteRuleGroup\",\n    \"DescribeFirewall\": \"network-firewall:DescribeFirewall\",\n    \"DescribeFirewallPolicy\": \"network-firewall:DescribeFirewallPolicy\",\n    \"DescribeLoggingConfiguration\": \"network-firewall:DescribeLoggingConfiguration\",\n    \"DescribeResourcePolicy\": \"network-firewall:DescribeResourcePolicy\",\n    \"DescribeRuleGroup\": \"network-firewall:DescribeRuleGroup\",\n    \"DisassociateSubnets\": \"network-firewall:DisassociateSubnets\",\n    \"ListFirewallPolicies\": \"network-firewall:ListFirewallPolicies\",\n    \"ListFirewalls\": \"network-firewall:ListFirewalls\",\n    \"ListRuleGroups\": \"network-firewall:ListRuleGroups\",\n    \"ListTagsForResource\": \"network-firewall:ListTagsForResource\",\n    \"PutResourcePolicy\": \"network-firewall:PutResourcePolicy\",\n    \"TagResource\": \"network-firewall:TagResource\",\n    \"UntagResource\": \"network-firewall:UntagResource\",\n    \"UpdateFirewallDeleteProtection\": \"network-firewall:UpdateFirewallDeleteProtection\",\n    \"UpdateFirewallDescription\": \"network-firewall:UpdateFirewallDescription\",\n    \"UpdateFirewallPolicy\": \"network-firewall:UpdateFirewallPolicy\",\n    \"UpdateFirewallPolicyChangeProtection\": \"network-firewall:UpdateFirewallPolicyChangeProtection\",\n    \"UpdateLoggingConfiguration\": \"network-firewall:UpdateLoggingConfiguration\",\n    \"UpdateRuleGroup\": \"network-firewall:UpdateRuleGroup\",\n    \"UpdateSubnetChangeProtection\": \"network-firewall:UpdateSubnetChangeProtection\"\n  },\n  \"networkmanager\": {\n    \"AcceptAttachment\": \"networkmanager:AcceptAttachment\",\n    \"AssociateConnectPeer\": \"networkmanager:AssociateConnectPeer\",\n    \"AssociateCustomerGateway\": \"networkmanager:AssociateCustomerGateway\",\n    \"AssociateLink\": \"networkmanager:AssociateLink\",\n    \"AssociateTransitGatewayConnectPeer\": \"networkmanager:AssociateTransitGatewayConnectPeer\",\n    \"CreateConnectAttachment\": \"networkmanager:CreateConnectAttachment\",\n    \"CreateConnectPeer\": \"networkmanager:CreateConnectPeer\",\n    \"CreateConnection\": \"networkmanager:CreateConnection\",\n    \"CreateCoreNetwork\": \"networkmanager:CreateCoreNetwork\",\n    \"CreateDevice\": \"networkmanager:CreateDevice\",\n    \"CreateGlobalNetwork\": \"networkmanager:CreateGlobalNetwork\",\n    \"CreateLink\": \"networkmanager:CreateLink\",\n    \"CreateSite\": \"networkmanager:CreateSite\",\n    \"CreateSiteToSiteVpnAttachment\": \"networkmanager:CreateSiteToSiteVpnAttachment\",\n    \"CreateVpcAttachment\": \"networkmanager:CreateVpcAttachment\",\n    \"DeleteAttachment\": \"networkmanager:DeleteAttachment\",\n    \"DeleteConnectPeer\": \"networkmanager:DeleteConnectPeer\",\n    \"DeleteConnection\": \"networkmanager:DeleteConnection\",\n    \"DeleteCoreNetwork\": \"networkmanager:DeleteCoreNetwork\",\n    \"DeleteCoreNetworkPolicyVersion\": \"networkmanager:DeleteCoreNetworkPolicyVersion\",\n    \"DeleteDevice\": \"networkmanager:DeleteDevice\",\n    \"DeleteGlobalNetwork\": \"networkmanager:DeleteGlobalNetwork\",\n    \"DeleteLink\": \"networkmanager:DeleteLink\",\n    \"DeleteResourcePolicy\": \"networkmanager:DeleteResourcePolicy\",\n    \"DeleteSite\": \"networkmanager:DeleteSite\",\n    \"DeregisterTransitGateway\": \"networkmanager:DeregisterTransitGateway\",\n    \"DescribeGlobalNetworks\": \"networkmanager:DescribeGlobalNetworks\",\n    \"DisassociateConnectPeer\": \"networkmanager:DisassociateConnectPeer\",\n    \"DisassociateCustomerGateway\": \"networkmanager:DisassociateCustomerGateway\",\n    \"DisassociateLink\": \"networkmanager:DisassociateLink\",\n    \"DisassociateTransitGatewayConnectPeer\": \"networkmanager:DisassociateTransitGatewayConnectPeer\",\n    \"ExecuteCoreNetworkChangeSet\": \"networkmanager:ExecuteCoreNetworkChangeSet\",\n    \"GetConnectAttachment\": \"networkmanager:GetConnectAttachment\",\n    \"GetConnectPeer\": \"networkmanager:GetConnectPeer\",\n    \"GetConnectPeerAssociations\": \"networkmanager:GetConnectPeerAssociations\",\n    \"GetConnections\": \"networkmanager:GetConnections\",\n    \"GetCoreNetwork\": \"networkmanager:GetCoreNetwork\",\n    \"GetCoreNetworkChangeSet\": \"networkmanager:GetCoreNetworkChangeSet\",\n    \"GetCoreNetworkPolicy\": \"networkmanager:GetCoreNetworkPolicy\",\n    \"GetCustomerGatewayAssociations\": \"networkmanager:GetCustomerGatewayAssociations\",\n    \"GetDevices\": \"networkmanager:GetDevices\",\n    \"GetLinkAssociations\": \"networkmanager:GetLinkAssociations\",\n    \"GetLinks\": \"networkmanager:GetLinks\",\n    \"GetNetworkResourceCounts\": \"networkmanager:GetNetworkResourceCounts\",\n    \"GetNetworkResourceRelationships\": \"networkmanager:GetNetworkResourceRelationships\",\n    \"GetNetworkResources\": \"networkmanager:GetNetworkResources\",\n    \"GetNetworkRoutes\": \"networkmanager:GetNetworkRoutes\",\n    \"GetNetworkTelemetry\": \"networkmanager:GetNetworkTelemetry\",\n    \"GetResourcePolicy\": \"networkmanager:GetResourcePolicy\",\n    \"GetRouteAnalysis\": \"networkmanager:GetRouteAnalysis\",\n    \"GetSiteToSiteVpnAttachment\": \"networkmanager:GetSiteToSiteVpnAttachment\",\n    \"GetSites\": \"networkmanager:GetSites\",\n    \"GetTransitGatewayConnectPeerAssociations\": \"networkmanager:GetTransitGatewayConnectPeerAssociations\",\n    \"GetTransitGatewayRegistrations\": \"networkmanager:GetTransitGatewayRegistrations\",\n    \"GetVpcAttachment\": \"networkmanager:GetVpcAttachment\",\n    \"ListAttachments\": \"networkmanager:ListAttachments\",\n    \"ListConnectPeers\": \"networkmanager:ListConnectPeers\",\n    \"ListCoreNetworkPolicyVersions\": \"networkmanager:ListCoreNetworkPolicyVersions\",\n    \"ListCoreNetworks\": \"networkmanager:ListCoreNetworks\",\n    \"ListTagsForResource\": \"networkmanager:ListTagsForResource\",\n    \"PutCoreNetworkPolicy\": \"networkmanager:PutCoreNetworkPolicy\",\n    \"PutResourcePolicy\": \"networkmanager:PutResourcePolicy\",\n    \"RegisterTransitGateway\": \"networkmanager:RegisterTransitGateway\",\n    \"RejectAttachment\": \"networkmanager:RejectAttachment\",\n    \"RestoreCoreNetworkPolicyVersion\": \"networkmanager:RestoreCoreNetworkPolicyVersion\",\n    \"StartRouteAnalysis\": \"networkmanager:StartRouteAnalysis\",\n    \"TagResource\": \"networkmanager:TagResource\",\n    \"UntagResource\": \"networkmanager:UntagResource\",\n    \"UpdateConnection\": \"networkmanager:UpdateConnection\",\n    \"UpdateCoreNetwork\": \"networkmanager:UpdateCoreNetwork\",\n    \"UpdateDevice\": \"networkmanager:UpdateDevice\",\n    \"UpdateGlobalNetwork\": \"networkmanager:UpdateGlobalNetwork\",\n    \"UpdateLink\": \"networkmanager:UpdateLink\",\n    \"UpdateNetworkResourceMetadata\": \"networkmanager:UpdateNetworkResourceMetadata\",\n    \"UpdateSite\": \"networkmanager:UpdateSite\",\n    \"UpdateVpcAttachment\": \"networkmanager:UpdateVpcAttachment\"\n  },\n  \"nimble\": {\n    \"AcceptEulas\": \"nimble:AcceptEulas\",\n    \"CreateLaunchProfile\": \"nimble:CreateLaunchProfile\",\n    \"CreateStreamingImage\": \"nimble:CreateStreamingImage\",\n    \"CreateStreamingSession\": \"nimble:CreateStreamingSession\",\n    \"CreateStreamingSessionStream\": \"nimble:CreateStreamingSessionStream\",\n    \"CreateStudio\": \"nimble:CreateStudio\",\n    \"CreateStudioComponent\": \"nimble:CreateStudioComponent\",\n    \"DeleteLaunchProfile\": \"nimble:DeleteLaunchProfile\",\n    \"DeleteLaunchProfileMember\": \"nimble:DeleteLaunchProfileMember\",\n    \"DeleteStreamingImage\": \"nimble:DeleteStreamingImage\",\n    \"DeleteStreamingSession\": \"nimble:DeleteStreamingSession\",\n    \"DeleteStudio\": \"nimble:DeleteStudio\",\n    \"DeleteStudioComponent\": \"nimble:DeleteStudioComponent\",\n    \"DeleteStudioMember\": \"nimble:DeleteStudioMember\",\n    \"GetEula\": \"nimble:GetEula\",\n    \"GetLaunchProfile\": \"nimble:GetLaunchProfile\",\n    \"GetLaunchProfileDetails\": \"nimble:GetLaunchProfileDetails\",\n    \"GetLaunchProfileInitialization\": \"nimble:GetLaunchProfileInitialization\",\n    \"GetLaunchProfileMember\": \"nimble:GetLaunchProfileMember\",\n    \"GetStreamingImage\": \"nimble:GetStreamingImage\",\n    \"GetStreamingSession\": \"nimble:GetStreamingSession\",\n    \"GetStreamingSessionStream\": \"nimble:GetStreamingSessionStream\",\n    \"GetStudio\": \"nimble:GetStudio\",\n    \"GetStudioComponent\": \"nimble:GetStudioComponent\",\n    \"GetStudioMember\": \"nimble:GetStudioMember\",\n    \"ListEulaAcceptances\": \"nimble:ListEulaAcceptances\",\n    \"ListEulas\": \"nimble:ListEulas\",\n    \"ListLaunchProfileMembers\": \"nimble:ListLaunchProfileMembers\",\n    \"ListLaunchProfiles\": \"nimble:ListLaunchProfiles\",\n    \"ListStreamingImages\": \"nimble:ListStreamingImages\",\n    \"ListStreamingSessions\": \"nimble:ListStreamingSessions\",\n    \"ListStudioComponents\": \"nimble:ListStudioComponents\",\n    \"ListStudioMembers\": \"nimble:ListStudioMembers\",\n    \"ListStudios\": \"nimble:ListStudios\",\n    \"ListTagsForResource\": \"nimble:ListTagsForResource\",\n    \"PutLaunchProfileMembers\": \"nimble:PutLaunchProfileMembers\",\n    \"PutStudioMembers\": \"nimble:PutStudioMembers\",\n    \"StartStreamingSession\": \"nimble:StartStreamingSession\",\n    \"StartStudioSSOConfigurationRepair\": \"nimble:StartStudioSSOConfigurationRepair\",\n    \"StopStreamingSession\": \"nimble:StopStreamingSession\",\n    \"TagResource\": \"nimble:TagResource\",\n    \"UntagResource\": \"nimble:UntagResource\",\n    \"UpdateLaunchProfile\": \"nimble:UpdateLaunchProfile\",\n    \"UpdateLaunchProfileMember\": \"nimble:UpdateLaunchProfileMember\",\n    \"UpdateStreamingImage\": \"nimble:UpdateStreamingImage\",\n    \"UpdateStudio\": \"nimble:UpdateStudio\",\n    \"UpdateStudioComponent\": \"nimble:UpdateStudioComponent\"\n  },\n  \"opsworks\": {\n    \"AssignInstance\": \"opsworks:AssignInstance\",\n    \"AssignVolume\": \"opsworks:AssignVolume\",\n    \"AssociateElasticIp\": \"opsworks:AssociateElasticIp\",\n    \"AttachElasticLoadBalancer\": \"opsworks:AttachElasticLoadBalancer\",\n    \"CloneStack\": \"opsworks:CloneStack\",\n    \"CreateApp\": \"opsworks:CreateApp\",\n    \"CreateDeployment\": \"opsworks:CreateDeployment\",\n    \"CreateInstance\": \"opsworks:CreateInstance\",\n    \"CreateLayer\": \"opsworks:CreateLayer\",\n    \"CreateStack\": \"opsworks:CreateStack\",\n    \"CreateUserProfile\": \"opsworks:CreateUserProfile\",\n    \"DeleteApp\": \"opsworks:DeleteApp\",\n    \"DeleteInstance\": \"opsworks:DeleteInstance\",\n    \"DeleteLayer\": \"opsworks:DeleteLayer\",\n    \"DeleteStack\": \"opsworks:DeleteStack\",\n    \"DeleteUserProfile\": \"opsworks:DeleteUserProfile\",\n    \"DeregisterEcsCluster\": \"opsworks:DeregisterEcsCluster\",\n    \"DeregisterElasticIp\": \"opsworks:DeregisterElasticIp\",\n    \"DeregisterInstance\": \"opsworks:DeregisterInstance\",\n    \"DeregisterRdsDbInstance\": \"opsworks:DeregisterRdsDbInstance\",\n    \"DeregisterVolume\": \"opsworks:DeregisterVolume\",\n    \"DescribeAgentVersions\": \"opsworks:DescribeAgentVersions\",\n    \"DescribeApps\": \"opsworks:DescribeApps\",\n    \"DescribeCommands\": \"opsworks:DescribeCommands\",\n    \"DescribeDeployments\": \"opsworks:DescribeDeployments\",\n    \"DescribeEcsClusters\": \"opsworks:DescribeEcsClusters\",\n    \"DescribeElasticIps\": \"opsworks:DescribeElasticIps\",\n    \"DescribeElasticLoadBalancers\": \"opsworks:DescribeElasticLoadBalancers\",\n    \"DescribeInstances\": \"opsworks:DescribeInstances\",\n    \"DescribeLayers\": \"opsworks:DescribeLayers\",\n    \"DescribeLoadBasedAutoScaling\": \"opsworks:DescribeLoadBasedAutoScaling\",\n    \"DescribeMyUserProfile\": \"opsworks:DescribeMyUserProfile\",\n    \"DescribeOperatingSystems\": \"opsworks:DescribeOperatingSystems\",\n    \"DescribePermissions\": \"opsworks:DescribePermissions\",\n    \"DescribeRaidArrays\": \"opsworks:DescribeRaidArrays\",\n    \"DescribeRdsDbInstances\": \"opsworks:DescribeRdsDbInstances\",\n    \"DescribeServiceErrors\": \"opsworks:DescribeServiceErrors\",\n    \"DescribeStackProvisioningParameters\": \"opsworks:DescribeStackProvisioningParameters\",\n    \"DescribeStackSummary\": \"opsworks:DescribeStackSummary\",\n    \"DescribeStacks\": \"opsworks:DescribeStacks\",\n    \"DescribeTimeBasedAutoScaling\": \"opsworks:DescribeTimeBasedAutoScaling\",\n    \"DescribeUserProfiles\": \"opsworks:DescribeUserProfiles\",\n    \"DescribeVolumes\": \"opsworks:DescribeVolumes\",\n    \"DetachElasticLoadBalancer\": \"opsworks:DetachElasticLoadBalancer\",\n    \"DisassociateElasticIp\": \"opsworks:DisassociateElasticIp\",\n    \"GetHostnameSuggestion\": \"opsworks:GetHostnameSuggestion\",\n    \"GrantAccess\": \"opsworks:GrantAccess\",\n    \"ListTags\": \"opsworks:ListTags\",\n    \"RebootInstance\": \"opsworks:RebootInstance\",\n    \"RegisterEcsCluster\": \"opsworks:RegisterEcsCluster\",\n    \"RegisterElasticIp\": \"opsworks:RegisterElasticIp\",\n    \"RegisterInstance\": \"opsworks:RegisterInstance\",\n    \"RegisterRdsDbInstance\": \"opsworks:RegisterRdsDbInstance\",\n    \"RegisterVolume\": \"opsworks:RegisterVolume\",\n    \"SetLoadBasedAutoScaling\": \"opsworks:SetLoadBasedAutoScaling\",\n    \"SetPermission\": \"opsworks:SetPermission\",\n    \"SetTimeBasedAutoScaling\": \"opsworks:SetTimeBasedAutoScaling\",\n    \"StartInstance\": \"opsworks:StartInstance\",\n    \"StartStack\": \"opsworks:StartStack\",\n    \"StopInstance\": \"opsworks:StopInstance\",\n    \"StopStack\": \"opsworks:StopStack\",\n    \"TagResource\": \"opsworks:TagResource\",\n    \"UnassignInstance\": \"opsworks:UnassignInstance\",\n    \"UnassignVolume\": \"opsworks:UnassignVolume\",\n    \"UntagResource\": \"opsworks:UntagResource\",\n    \"UpdateApp\": \"opsworks:UpdateApp\",\n    \"UpdateElasticIp\": \"opsworks:UpdateElasticIp\",\n    \"UpdateInstance\": \"opsworks:UpdateInstance\",\n    \"UpdateLayer\": \"opsworks:UpdateLayer\",\n    \"UpdateMyUserProfile\": \"opsworks:UpdateMyUserProfile\",\n    \"UpdateRdsDbInstance\": \"opsworks:UpdateRdsDbInstance\",\n    \"UpdateStack\": \"opsworks:UpdateStack\",\n    \"UpdateUserProfile\": \"opsworks:UpdateUserProfile\",\n    \"UpdateVolume\": \"opsworks:UpdateVolume\"\n  },\n  \"opsworkscm\": {\n    \"AssociateNode\": \"opsworks-cm:AssociateNode\",\n    \"CreateBackup\": \"opsworks-cm:CreateBackup\",\n    \"CreateServer\": \"opsworks-cm:CreateServer\",\n    \"DeleteBackup\": \"opsworks-cm:DeleteBackup\",\n    \"DeleteServer\": \"opsworks-cm:DeleteServer\",\n    \"DescribeAccountAttributes\": \"opsworks-cm:DescribeAccountAttributes\",\n    \"DescribeBackups\": \"opsworks-cm:DescribeBackups\",\n    \"DescribeEvents\": \"opsworks-cm:DescribeEvents\",\n    \"DescribeNodeAssociationStatus\": \"opsworks-cm:DescribeNodeAssociationStatus\",\n    \"DescribeServers\": \"opsworks-cm:DescribeServers\",\n    \"DisassociateNode\": \"opsworks-cm:DisassociateNode\",\n    \"ExportServerEngineAttribute\": \"opsworks-cm:ExportServerEngineAttribute\",\n    \"ListTagsForResource\": \"opsworks-cm:ListTagsForResource\",\n    \"RestoreServer\": \"opsworks-cm:RestoreServer\",\n    \"StartMaintenance\": \"opsworks-cm:StartMaintenance\",\n    \"TagResource\": \"opsworks-cm:TagResource\",\n    \"UntagResource\": \"opsworks-cm:UntagResource\",\n    \"UpdateServer\": \"opsworks-cm:UpdateServer\",\n    \"UpdateServerEngineAttributes\": \"opsworks-cm:UpdateServerEngineAttributes\"\n  },\n  \"organizations\": {\n    \"AcceptHandshake\": \"organizations:AcceptHandshake\",\n    \"AttachPolicy\": \"organizations:AttachPolicy\",\n    \"CancelHandshake\": \"organizations:CancelHandshake\",\n    \"CreateAccount\": \"organizations:CreateAccount\",\n    \"CreateGovCloudAccount\": \"organizations:CreateGovCloudAccount\",\n    \"CreateOrganization\": \"organizations:CreateOrganization\",\n    \"CreateOrganizationalUnit\": \"organizations:CreateOrganizationalUnit\",\n    \"CreatePolicy\": \"organizations:CreatePolicy\",\n    \"DeclineHandshake\": \"organizations:DeclineHandshake\",\n    \"DeleteOrganization\": \"organizations:DeleteOrganization\",\n    \"DeleteOrganizationalUnit\": \"organizations:DeleteOrganizationalUnit\",\n    \"DeletePolicy\": \"organizations:DeletePolicy\",\n    \"DeregisterDelegatedAdministrator\": \"organizations:DeregisterDelegatedAdministrator\",\n    \"DescribeAccount\": \"organizations:DescribeAccount\",\n    \"DescribeCreateAccountStatus\": \"organizations:DescribeCreateAccountStatus\",\n    \"DescribeEffectivePolicy\": \"organizations:DescribeEffectivePolicy\",\n    \"DescribeHandshake\": \"organizations:DescribeHandshake\",\n    \"DescribeOrganization\": \"organizations:DescribeOrganization\",\n    \"DescribeOrganizationalUnit\": \"organizations:DescribeOrganizationalUnit\",\n    \"DescribePolicy\": \"organizations:DescribePolicy\",\n    \"DetachPolicy\": \"organizations:DetachPolicy\",\n    \"DisableAWSServiceAccess\": \"organizations:DisableAWSServiceAccess\",\n    \"DisablePolicyType\": \"organizations:DisablePolicyType\",\n    \"EnableAWSServiceAccess\": \"organizations:EnableAWSServiceAccess\",\n    \"EnableAllFeatures\": \"organizations:EnableAllFeatures\",\n    \"EnablePolicyType\": \"organizations:EnablePolicyType\",\n    \"InviteAccountToOrganization\": \"organizations:InviteAccountToOrganization\",\n    \"LeaveOrganization\": \"organizations:LeaveOrganization\",\n    \"ListAWSServiceAccessForOrganization\": \"organizations:ListAWSServiceAccessForOrganization\",\n    \"ListAccounts\": \"organizations:ListAccounts\",\n    \"ListAccountsForParent\": \"organizations:ListAccountsForParent\",\n    \"ListChildren\": \"organizations:ListChildren\",\n    \"ListCreateAccountStatus\": \"organizations:ListCreateAccountStatus\",\n    \"ListDelegatedAdministrators\": \"organizations:ListDelegatedAdministrators\",\n    \"ListDelegatedServicesForAccount\": \"organizations:ListDelegatedServicesForAccount\",\n    \"ListHandshakesForAccount\": \"organizations:ListHandshakesForAccount\",\n    \"ListHandshakesForOrganization\": \"organizations:ListHandshakesForOrganization\",\n    \"ListOrganizationalUnitsForParent\": \"organizations:ListOrganizationalUnitsForParent\",\n    \"ListParents\": \"organizations:ListParents\",\n    \"ListPolicies\": \"organizations:ListPolicies\",\n    \"ListPoliciesForTarget\": \"organizations:ListPoliciesForTarget\",\n    \"ListRoots\": \"organizations:ListRoots\",\n    \"ListTagsForResource\": \"organizations:ListTagsForResource\",\n    \"ListTargetsForPolicy\": \"organizations:ListTargetsForPolicy\",\n    \"MoveAccount\": \"organizations:MoveAccount\",\n    \"RegisterDelegatedAdministrator\": \"organizations:RegisterDelegatedAdministrator\",\n    \"RemoveAccountFromOrganization\": \"organizations:RemoveAccountFromOrganization\",\n    \"TagResource\": \"organizations:TagResource\",\n    \"UntagResource\": \"organizations:UntagResource\",\n    \"UpdateOrganizationalUnit\": \"organizations:UpdateOrganizationalUnit\",\n    \"UpdatePolicy\": \"organizations:UpdatePolicy\"\n  },\n  \"outposts\": {\n    \"CancelOrder\": \"outposts:CancelOrder\",\n    \"CreateOrder\": \"outposts:CreateOrder\",\n    \"CreateOutpost\": \"outposts:CreateOutpost\",\n    \"CreateSite\": \"outposts:CreateSite\",\n    \"DeleteOutpost\": \"outposts:DeleteOutpost\",\n    \"DeleteSite\": \"outposts:DeleteSite\",\n    \"GetCatalogItem\": \"outposts:GetCatalogItem\",\n    \"GetOutpost\": \"outposts:GetOutpost\",\n    \"GetOutpostInstanceTypes\": \"outposts:GetOutpostInstanceTypes\",\n    \"GetSite\": \"outposts:GetSite\",\n    \"GetSiteAddress\": \"outposts:GetSiteAddress\",\n    \"ListCatalogItems\": \"outposts:ListCatalogItems\",\n    \"ListOrders\": \"outposts:ListOrders\",\n    \"ListOutposts\": \"outposts:ListOutposts\",\n    \"ListSites\": \"outposts:ListSites\",\n    \"ListTagsForResource\": \"outposts:ListTagsForResource\",\n    \"TagResource\": \"outposts:TagResource\",\n    \"UntagResource\": \"outposts:UntagResource\",\n    \"UpdateOutpost\": \"outposts:UpdateOutpost\",\n    \"UpdateSite\": \"outposts:UpdateSite\",\n    \"UpdateSiteAddress\": \"outposts:UpdateSiteAddress\",\n    \"UpdateSiteRackPhysicalProperties\": \"outposts:UpdateSiteRackPhysicalProperties\"\n  },\n  \"panorama\": {\n    \"CreateApplicationInstance\": \"panorama:CreateApplicationInstance\",\n    \"CreateJobForDevices\": \"panorama:CreateJobForDevices\",\n    \"CreateNodeFromTemplateJob\": \"panorama:CreateNodeFromTemplateJob\",\n    \"CreatePackage\": \"panorama:CreatePackage\",\n    \"CreatePackageImportJob\": \"panorama:CreatePackageImportJob\",\n    \"DeleteDevice\": \"panorama:DeleteDevice\",\n    \"DeletePackage\": \"panorama:DeletePackage\",\n    \"DeregisterPackageVersion\": \"panorama:DeregisterPackageVersion\",\n    \"DescribeApplicationInstance\": \"panorama:DescribeApplicationInstance\",\n    \"DescribeApplicationInstanceDetails\": \"panorama:DescribeApplicationInstanceDetails\",\n    \"DescribeDevice\": \"panorama:DescribeDevice\",\n    \"DescribeDeviceJob\": \"panorama:DescribeDeviceJob\",\n    \"DescribeNode\": \"panorama:DescribeNode\",\n    \"DescribeNodeFromTemplateJob\": \"panorama:DescribeNodeFromTemplateJob\",\n    \"DescribePackage\": \"panorama:DescribePackage\",\n    \"DescribePackageImportJob\": \"panorama:DescribePackageImportJob\",\n    \"DescribePackageVersion\": \"panorama:DescribePackageVersion\",\n    \"ListApplicationInstanceDependencies\": \"panorama:ListApplicationInstanceDependencies\",\n    \"ListApplicationInstanceNodeInstances\": \"panorama:ListApplicationInstanceNodeInstances\",\n    \"ListApplicationInstances\": \"panorama:ListApplicationInstances\",\n    \"ListDevices\": \"panorama:ListDevices\",\n    \"ListDevicesJobs\": \"panorama:ListDevicesJobs\",\n    \"ListNodeFromTemplateJobs\": \"panorama:ListNodeFromTemplateJobs\",\n    \"ListNodes\": \"panorama:ListNodes\",\n    \"ListPackageImportJobs\": \"panorama:ListPackageImportJobs\",\n    \"ListPackages\": \"panorama:ListPackages\",\n    \"ListTagsForResource\": \"panorama:ListTagsForResource\",\n    \"ProvisionDevice\": \"panorama:ProvisionDevice\",\n    \"RegisterPackageVersion\": \"panorama:RegisterPackageVersion\",\n    \"RemoveApplicationInstance\": \"panorama:RemoveApplicationInstance\",\n    \"TagResource\": \"panorama:TagResource\",\n    \"UntagResource\": \"panorama:UntagResource\",\n    \"UpdateDeviceMetadata\": \"panorama:UpdateDeviceMetadata\"\n  },\n  \"personalize\": {\n    \"CreateBatchInferenceJob\": \"personalize:CreateBatchInferenceJob\",\n    \"CreateBatchSegmentJob\": \"personalize:CreateBatchSegmentJob\",\n    \"CreateCampaign\": \"personalize:CreateCampaign\",\n    \"CreateDataset\": \"personalize:CreateDataset\",\n    \"CreateDatasetExportJob\": \"personalize:CreateDatasetExportJob\",\n    \"CreateDatasetGroup\": \"personalize:CreateDatasetGroup\",\n    \"CreateDatasetImportJob\": \"personalize:CreateDatasetImportJob\",\n    \"CreateEventTracker\": \"personalize:CreateEventTracker\",\n    \"CreateFilter\": \"personalize:CreateFilter\",\n    \"CreateRecommender\": \"personalize:CreateRecommender\",\n    \"CreateSchema\": \"personalize:CreateSchema\",\n    \"CreateSolution\": \"personalize:CreateSolution\",\n    \"CreateSolutionVersion\": \"personalize:CreateSolutionVersion\",\n    \"DeleteCampaign\": \"personalize:DeleteCampaign\",\n    \"DeleteDataset\": \"personalize:DeleteDataset\",\n    \"DeleteDatasetGroup\": \"personalize:DeleteDatasetGroup\",\n    \"DeleteEventTracker\": \"personalize:DeleteEventTracker\",\n    \"DeleteFilter\": \"personalize:DeleteFilter\",\n    \"DeleteRecommender\": \"personalize:DeleteRecommender\",\n    \"DeleteSchema\": \"personalize:DeleteSchema\",\n    \"DeleteSolution\": \"personalize:DeleteSolution\",\n    \"DescribeAlgorithm\": \"personalize:DescribeAlgorithm\",\n    \"DescribeBatchInferenceJob\": \"personalize:DescribeBatchInferenceJob\",\n    \"DescribeBatchSegmentJob\": \"personalize:DescribeBatchSegmentJob\",\n    \"DescribeCampaign\": \"personalize:DescribeCampaign\",\n    \"DescribeDataset\": \"personalize:DescribeDataset\",\n    \"DescribeDatasetExportJob\": \"personalize:DescribeDatasetExportJob\",\n    \"DescribeDatasetGroup\": \"personalize:DescribeDatasetGroup\",\n    \"DescribeDatasetImportJob\": \"personalize:DescribeDatasetImportJob\",\n    \"DescribeEventTracker\": \"personalize:DescribeEventTracker\",\n    \"DescribeFeatureTransformation\": \"personalize:DescribeFeatureTransformation\",\n    \"DescribeFilter\": \"personalize:DescribeFilter\",\n    \"DescribeRecipe\": \"personalize:DescribeRecipe\",\n    \"DescribeRecommender\": \"personalize:DescribeRecommender\",\n    \"DescribeSchema\": \"personalize:DescribeSchema\",\n    \"DescribeSolution\": \"personalize:DescribeSolution\",\n    \"DescribeSolutionVersion\": \"personalize:DescribeSolutionVersion\",\n    \"GetSolutionMetrics\": \"personalize:GetSolutionMetrics\",\n    \"ListBatchInferenceJobs\": \"personalize:ListBatchInferenceJobs\",\n    \"ListBatchSegmentJobs\": \"personalize:ListBatchSegmentJobs\",\n    \"ListCampaigns\": \"personalize:ListCampaigns\",\n    \"ListDatasetExportJobs\": \"personalize:ListDatasetExportJobs\",\n    \"ListDatasetGroups\": \"personalize:ListDatasetGroups\",\n    \"ListDatasetImportJobs\": \"personalize:ListDatasetImportJobs\",\n    \"ListDatasets\": \"personalize:ListDatasets\",\n    \"ListEventTrackers\": \"personalize:ListEventTrackers\",\n    \"ListFilters\": \"personalize:ListFilters\",\n    \"ListRecipes\": \"personalize:ListRecipes\",\n    \"ListRecommenders\": \"personalize:ListRecommenders\",\n    \"ListSchemas\": \"personalize:ListSchemas\",\n    \"ListSolutionVersions\": \"personalize:ListSolutionVersions\",\n    \"ListSolutions\": \"personalize:ListSolutions\",\n    \"StopSolutionVersionCreation\": \"personalize:StopSolutionVersionCreation\",\n    \"UpdateCampaign\": \"personalize:UpdateCampaign\",\n    \"UpdateRecommender\": \"personalize:UpdateRecommender\"\n  },\n  \"pi\": {\n    \"DescribeDimensionKeys\": \"pi:DescribeDimensionKeys\",\n    \"GetDimensionKeyDetails\": \"pi:GetDimensionKeyDetails\",\n    \"GetResourceMetrics\": \"pi:GetResourceMetrics\"\n  },\n  \"pinpoint\": {\n    \"CreateApp\": \"mobiletargeting:CreateApp\",\n    \"CreateCampaign\": \"mobiletargeting:CreateCampaign\",\n    \"CreateEmailTemplate\": \"mobiletargeting:CreateEmailTemplate\",\n    \"CreateExportJob\": \"mobiletargeting:CreateExportJob\",\n    \"CreateImportJob\": \"mobiletargeting:CreateImportJob\",\n    \"CreateJourney\": \"mobiletargeting:CreateJourney\",\n    \"CreatePushTemplate\": \"mobiletargeting:CreatePushTemplate\",\n    \"CreateRecommenderConfiguration\": \"mobiletargeting:CreateRecommenderConfiguration\",\n    \"CreateSegment\": \"mobiletargeting:CreateSegment\",\n    \"CreateSmsTemplate\": \"mobiletargeting:CreateSmsTemplate\",\n    \"CreateVoiceTemplate\": \"mobiletargeting:CreateVoiceTemplate\",\n    \"DeleteAdmChannel\": \"mobiletargeting:DeleteAdmChannel\",\n    \"DeleteApnsChannel\": \"mobiletargeting:DeleteApnsChannel\",\n    \"DeleteApnsSandboxChannel\": \"mobiletargeting:DeleteApnsSandboxChannel\",\n    \"DeleteApnsVoipChannel\": \"mobiletargeting:DeleteApnsVoipChannel\",\n    \"DeleteApnsVoipSandboxChannel\": \"mobiletargeting:DeleteApnsVoipSandboxChannel\",\n    \"DeleteApp\": \"mobiletargeting:DeleteApp\",\n    \"DeleteBaiduChannel\": \"mobiletargeting:DeleteBaiduChannel\",\n    \"DeleteCampaign\": \"mobiletargeting:DeleteCampaign\",\n    \"DeleteEmailChannel\": \"mobiletargeting:DeleteEmailChannel\",\n    \"DeleteEmailTemplate\": \"mobiletargeting:DeleteEmailTemplate\",\n    \"DeleteEndpoint\": \"mobiletargeting:DeleteEndpoint\",\n    \"DeleteEventStream\": \"mobiletargeting:DeleteEventStream\",\n    \"DeleteGcmChannel\": \"mobiletargeting:DeleteGcmChannel\",\n    \"DeleteJourney\": \"mobiletargeting:DeleteJourney\",\n    \"DeletePushTemplate\": \"mobiletargeting:DeletePushTemplate\",\n    \"DeleteRecommenderConfiguration\": \"mobiletargeting:DeleteRecommenderConfiguration\",\n    \"DeleteSegment\": \"mobiletargeting:DeleteSegment\",\n    \"DeleteSmsChannel\": \"mobiletargeting:DeleteSmsChannel\",\n    \"DeleteSmsTemplate\": \"mobiletargeting:DeleteSmsTemplate\",\n    \"DeleteUserEndpoints\": \"mobiletargeting:DeleteUserEndpoints\",\n    \"DeleteVoiceChannel\": \"mobiletargeting:DeleteVoiceChannel\",\n    \"DeleteVoiceTemplate\": \"mobiletargeting:DeleteVoiceTemplate\",\n    \"GetAdmChannel\": \"mobiletargeting:GetAdmChannel\",\n    \"GetApnsChannel\": \"mobiletargeting:GetApnsChannel\",\n    \"GetApnsSandboxChannel\": \"mobiletargeting:GetApnsSandboxChannel\",\n    \"GetApnsVoipChannel\": \"mobiletargeting:GetApnsVoipChannel\",\n    \"GetApnsVoipSandboxChannel\": \"mobiletargeting:GetApnsVoipSandboxChannel\",\n    \"GetApp\": \"mobiletargeting:GetApp\",\n    \"GetApplicationDateRangeKpi\": \"mobiletargeting:GetApplicationDateRangeKpi\",\n    \"GetApplicationSettings\": \"mobiletargeting:GetApplicationSettings\",\n    \"GetApps\": \"mobiletargeting:GetApps\",\n    \"GetBaiduChannel\": \"mobiletargeting:GetBaiduChannel\",\n    \"GetCampaign\": \"mobiletargeting:GetCampaign\",\n    \"GetCampaignActivities\": \"mobiletargeting:GetCampaignActivities\",\n    \"GetCampaignDateRangeKpi\": \"mobiletargeting:GetCampaignDateRangeKpi\",\n    \"GetCampaignVersion\": \"mobiletargeting:GetCampaignVersion\",\n    \"GetCampaignVersions\": \"mobiletargeting:GetCampaignVersions\",\n    \"GetCampaigns\": \"mobiletargeting:GetCampaigns\",\n    \"GetChannels\": \"mobiletargeting:GetChannels\",\n    \"GetEmailChannel\": \"mobiletargeting:GetEmailChannel\",\n    \"GetEmailTemplate\": \"mobiletargeting:GetEmailTemplate\",\n    \"GetEndpoint\": \"mobiletargeting:GetEndpoint\",\n    \"GetEventStream\": \"mobiletargeting:GetEventStream\",\n    \"GetExportJob\": \"mobiletargeting:GetExportJob\",\n    \"GetExportJobs\": \"mobiletargeting:GetExportJobs\",\n    \"GetGcmChannel\": \"mobiletargeting:GetGcmChannel\",\n    \"GetImportJob\": \"mobiletargeting:GetImportJob\",\n    \"GetImportJobs\": \"mobiletargeting:GetImportJobs\",\n    \"GetInAppMessages\": \"mobiletargeting:GetInAppMessages\",\n    \"GetJourney\": \"mobiletargeting:GetJourney\",\n    \"GetJourneyDateRangeKpi\": \"mobiletargeting:GetJourneyDateRangeKpi\",\n    \"GetJourneyExecutionActivityMetrics\": \"mobiletargeting:GetJourneyExecutionActivityMetrics\",\n    \"GetJourneyExecutionMetrics\": \"mobiletargeting:GetJourneyExecutionMetrics\",\n    \"GetPushTemplate\": \"mobiletargeting:GetPushTemplate\",\n    \"GetRecommenderConfiguration\": \"mobiletargeting:GetRecommenderConfiguration\",\n    \"GetRecommenderConfigurations\": \"mobiletargeting:GetRecommenderConfigurations\",\n    \"GetSegment\": \"mobiletargeting:GetSegment\",\n    \"GetSegmentExportJobs\": \"mobiletargeting:GetSegmentExportJobs\",\n    \"GetSegmentImportJobs\": \"mobiletargeting:GetSegmentImportJobs\",\n    \"GetSegmentVersion\": \"mobiletargeting:GetSegmentVersion\",\n    \"GetSegmentVersions\": \"mobiletargeting:GetSegmentVersions\",\n    \"GetSegments\": \"mobiletargeting:GetSegments\",\n    \"GetSmsChannel\": \"mobiletargeting:GetSmsChannel\",\n    \"GetSmsTemplate\": \"mobiletargeting:GetSmsTemplate\",\n    \"GetUserEndpoints\": \"mobiletargeting:GetUserEndpoints\",\n    \"GetVoiceChannel\": \"mobiletargeting:GetVoiceChannel\",\n    \"GetVoiceTemplate\": \"mobiletargeting:GetVoiceTemplate\",\n    \"ListJourneys\": \"mobiletargeting:ListJourneys\",\n    \"ListTagsForResource\": \"mobiletargeting:ListTagsForResource\",\n    \"ListTemplateVersions\": \"mobiletargeting:ListTemplateVersions\",\n    \"ListTemplates\": \"mobiletargeting:ListTemplates\",\n    \"PhoneNumberValidate\": \"mobiletargeting:PhoneNumberValidate\",\n    \"PutEventStream\": \"mobiletargeting:PutEventStream\",\n    \"PutEvents\": \"mobiletargeting:PutEvents\",\n    \"RemoveAttributes\": \"mobiletargeting:RemoveAttributes\",\n    \"SendMessages\": \"mobiletargeting:SendMessages\",\n    \"SendUsersMessages\": \"mobiletargeting:SendUsersMessages\",\n    \"TagResource\": \"mobiletargeting:TagResource\",\n    \"UntagResource\": \"mobiletargeting:UntagResource\",\n    \"UpdateAdmChannel\": \"mobiletargeting:UpdateAdmChannel\",\n    \"UpdateApnsChannel\": \"mobiletargeting:UpdateApnsChannel\",\n    \"UpdateApnsSandboxChannel\": \"mobiletargeting:UpdateApnsSandboxChannel\",\n    \"UpdateApnsVoipChannel\": \"mobiletargeting:UpdateApnsVoipChannel\",\n    \"UpdateApnsVoipSandboxChannel\": \"mobiletargeting:UpdateApnsVoipSandboxChannel\",\n    \"UpdateApplicationSettings\": \"mobiletargeting:UpdateApplicationSettings\",\n    \"UpdateBaiduChannel\": \"mobiletargeting:UpdateBaiduChannel\",\n    \"UpdateCampaign\": \"mobiletargeting:UpdateCampaign\",\n    \"UpdateEmailChannel\": \"mobiletargeting:UpdateEmailChannel\",\n    \"UpdateEmailTemplate\": \"mobiletargeting:UpdateEmailTemplate\",\n    \"UpdateEndpoint\": \"mobiletargeting:UpdateEndpoint\",\n    \"UpdateEndpointsBatch\": \"mobiletargeting:UpdateEndpointsBatch\",\n    \"UpdateGcmChannel\": \"mobiletargeting:UpdateGcmChannel\",\n    \"UpdateJourney\": \"mobiletargeting:UpdateJourney\",\n    \"UpdateJourneyState\": \"mobiletargeting:UpdateJourneyState\",\n    \"UpdatePushTemplate\": \"mobiletargeting:UpdatePushTemplate\",\n    \"UpdateRecommenderConfiguration\": \"mobiletargeting:UpdateRecommenderConfiguration\",\n    \"UpdateSegment\": \"mobiletargeting:UpdateSegment\",\n    \"UpdateSmsChannel\": \"mobiletargeting:UpdateSmsChannel\",\n    \"UpdateSmsTemplate\": \"mobiletargeting:UpdateSmsTemplate\",\n    \"UpdateTemplateActiveVersion\": \"mobiletargeting:UpdateTemplateActiveVersion\",\n    \"UpdateVoiceChannel\": \"mobiletargeting:UpdateVoiceChannel\",\n    \"UpdateVoiceTemplate\": \"mobiletargeting:UpdateVoiceTemplate\"\n  },\n  \"polly\": {\n    \"DeleteLexicon\": \"polly:DeleteLexicon\",\n    \"DescribeVoices\": \"polly:DescribeVoices\",\n    \"GetLexicon\": \"polly:GetLexicon\",\n    \"GetSpeechSynthesisTask\": \"polly:GetSpeechSynthesisTask\",\n    \"ListLexicons\": \"polly:ListLexicons\",\n    \"ListSpeechSynthesisTasks\": \"polly:ListSpeechSynthesisTasks\",\n    \"PutLexicon\": \"polly:PutLexicon\",\n    \"StartSpeechSynthesisTask\": \"polly:StartSpeechSynthesisTask\",\n    \"SynthesizeSpeech\": \"polly:SynthesizeSpeech\"\n  },\n  \"pricing\": {\n    \"DescribeServices\": \"pricing:DescribeServices\",\n    \"GetAttributeValues\": \"pricing:GetAttributeValues\",\n    \"GetProducts\": \"pricing:GetProducts\"\n  },\n  \"proton\": {\n    \"AcceptEnvironmentAccountConnection\": \"proton:AcceptEnvironmentAccountConnection\",\n    \"CancelEnvironmentDeployment\": \"proton:CancelEnvironmentDeployment\",\n    \"CancelServiceInstanceDeployment\": \"proton:CancelServiceInstanceDeployment\",\n    \"CancelServicePipelineDeployment\": \"proton:CancelServicePipelineDeployment\",\n    \"CreateEnvironment\": \"proton:CreateEnvironment\",\n    \"CreateEnvironmentAccountConnection\": \"proton:CreateEnvironmentAccountConnection\",\n    \"CreateEnvironmentTemplate\": \"proton:CreateEnvironmentTemplate\",\n    \"CreateEnvironmentTemplateVersion\": \"proton:CreateEnvironmentTemplateVersion\",\n    \"CreateRepository\": \"proton:CreateRepository\",\n    \"CreateService\": \"proton:CreateService\",\n    \"CreateServiceTemplate\": \"proton:CreateServiceTemplate\",\n    \"CreateServiceTemplateVersion\": \"proton:CreateServiceTemplateVersion\",\n    \"CreateTemplateSyncConfig\": \"proton:CreateTemplateSyncConfig\",\n    \"DeleteEnvironment\": \"proton:DeleteEnvironment\",\n    \"DeleteEnvironmentAccountConnection\": \"proton:DeleteEnvironmentAccountConnection\",\n    \"DeleteEnvironmentTemplate\": \"proton:DeleteEnvironmentTemplate\",\n    \"DeleteEnvironmentTemplateVersion\": \"proton:DeleteEnvironmentTemplateVersion\",\n    \"DeleteRepository\": \"proton:DeleteRepository\",\n    \"DeleteService\": \"proton:DeleteService\",\n    \"DeleteServiceTemplate\": \"proton:DeleteServiceTemplate\",\n    \"DeleteServiceTemplateVersion\": \"proton:DeleteServiceTemplateVersion\",\n    \"DeleteTemplateSyncConfig\": \"proton:DeleteTemplateSyncConfig\",\n    \"GetAccountSettings\": \"proton:GetAccountSettings\",\n    \"GetEnvironment\": \"proton:GetEnvironment\",\n    \"GetEnvironmentAccountConnection\": \"proton:GetEnvironmentAccountConnection\",\n    \"GetEnvironmentTemplate\": \"proton:GetEnvironmentTemplate\",\n    \"GetEnvironmentTemplateVersion\": \"proton:GetEnvironmentTemplateVersion\",\n    \"GetRepository\": \"proton:GetRepository\",\n    \"GetRepositorySyncStatus\": \"proton:GetRepositorySyncStatus\",\n    \"GetService\": \"proton:GetService\",\n    \"GetServiceInstance\": \"proton:GetServiceInstance\",\n    \"GetServiceTemplate\": \"proton:GetServiceTemplate\",\n    \"GetServiceTemplateVersion\": \"proton:GetServiceTemplateVersion\",\n    \"GetTemplateSyncConfig\": \"proton:GetTemplateSyncConfig\",\n    \"GetTemplateSyncStatus\": \"proton:GetTemplateSyncStatus\",\n    \"ListEnvironmentAccountConnections\": \"proton:ListEnvironmentAccountConnections\",\n    \"ListEnvironmentTemplateVersions\": \"proton:ListEnvironmentTemplateVersions\",\n    \"ListEnvironmentTemplates\": \"proton:ListEnvironmentTemplates\",\n    \"ListEnvironments\": \"proton:ListEnvironments\",\n    \"ListRepositories\": \"proton:ListRepositories\",\n    \"ListRepositorySyncDefinitions\": \"proton:ListRepositorySyncDefinitions\",\n    \"ListServiceInstances\": \"proton:ListServiceInstances\",\n    \"ListServiceTemplateVersions\": \"proton:ListServiceTemplateVersions\",\n    \"ListServiceTemplates\": \"proton:ListServiceTemplates\",\n    \"ListServices\": \"proton:ListServices\",\n    \"ListTagsForResource\": \"proton:ListTagsForResource\",\n    \"RejectEnvironmentAccountConnection\": \"proton:RejectEnvironmentAccountConnection\",\n    \"TagResource\": \"proton:TagResource\",\n    \"UntagResource\": \"proton:UntagResource\",\n    \"UpdateAccountSettings\": \"proton:UpdateAccountSettings\",\n    \"UpdateEnvironment\": \"proton:UpdateEnvironment\",\n    \"UpdateEnvironmentAccountConnection\": \"proton:UpdateEnvironmentAccountConnection\",\n    \"UpdateEnvironmentTemplate\": \"proton:UpdateEnvironmentTemplate\",\n    \"UpdateEnvironmentTemplateVersion\": \"proton:UpdateEnvironmentTemplateVersion\",\n    \"UpdateService\": \"proton:UpdateService\",\n    \"UpdateServiceInstance\": \"proton:UpdateServiceInstance\",\n    \"UpdateServicePipeline\": \"proton:UpdateServicePipeline\",\n    \"UpdateServiceTemplate\": \"proton:UpdateServiceTemplate\",\n    \"UpdateServiceTemplateVersion\": \"proton:UpdateServiceTemplateVersion\",\n    \"UpdateTemplateSyncConfig\": \"proton:UpdateTemplateSyncConfig\"\n  },\n  \"qldb\": {\n    \"CancelJournalKinesisStream\": \"qldb:CancelJournalKinesisStream\",\n    \"CreateLedger\": \"qldb:CreateLedger\",\n    \"DeleteLedger\": \"qldb:DeleteLedger\",\n    \"DescribeJournalKinesisStream\": \"qldb:DescribeJournalKinesisStream\",\n    \"DescribeJournalS3Export\": \"qldb:DescribeJournalS3Export\",\n    \"DescribeLedger\": \"qldb:DescribeLedger\",\n    \"ExportJournalToS3\": \"qldb:ExportJournalToS3\",\n    \"GetBlock\": \"qldb:GetBlock\",\n    \"GetDigest\": \"qldb:GetDigest\",\n    \"GetRevision\": \"qldb:GetRevision\",\n    \"ListJournalKinesisStreamsForLedger\": \"qldb:ListJournalKinesisStreamsForLedger\",\n    \"ListJournalS3Exports\": \"qldb:ListJournalS3Exports\",\n    \"ListJournalS3ExportsForLedger\": \"qldb:ListJournalS3ExportsForLedger\",\n    \"ListLedgers\": \"qldb:ListLedgers\",\n    \"ListTagsForResource\": \"qldb:ListTagsForResource\",\n    \"StreamJournalToKinesis\": \"qldb:StreamJournalToKinesis\",\n    \"TagResource\": \"qldb:TagResource\",\n    \"UntagResource\": \"qldb:UntagResource\",\n    \"UpdateLedger\": \"qldb:UpdateLedger\",\n    \"UpdateLedgerPermissionsMode\": \"qldb:UpdateLedgerPermissionsMode\"\n  },\n  \"quicksight\": {\n    \"CancelIngestion\": \"quicksight:CancelIngestion\",\n    \"CreateAccountCustomization\": \"quicksight:CreateAccountCustomization\",\n    \"CreateAnalysis\": \"quicksight:CreateAnalysis\",\n    \"CreateDashboard\": \"quicksight:CreateDashboard\",\n    \"CreateDataSet\": \"quicksight:CreateDataSet\",\n    \"CreateDataSource\": \"quicksight:CreateDataSource\",\n    \"CreateFolder\": \"quicksight:CreateFolder\",\n    \"CreateFolderMembership\": \"quicksight:CreateFolderMembership\",\n    \"CreateGroup\": \"quicksight:CreateGroup\",\n    \"CreateGroupMembership\": \"quicksight:CreateGroupMembership\",\n    \"CreateIAMPolicyAssignment\": \"quicksight:CreateIAMPolicyAssignment\",\n    \"CreateIngestion\": \"quicksight:CreateIngestion\",\n    \"CreateNamespace\": \"quicksight:CreateNamespace\",\n    \"CreateTemplate\": \"quicksight:CreateTemplate\",\n    \"CreateTemplateAlias\": \"quicksight:CreateTemplateAlias\",\n    \"CreateTheme\": \"quicksight:CreateTheme\",\n    \"CreateThemeAlias\": \"quicksight:CreateThemeAlias\",\n    \"DeleteAccountCustomization\": \"quicksight:DeleteAccountCustomization\",\n    \"DeleteAnalysis\": \"quicksight:DeleteAnalysis\",\n    \"DeleteDashboard\": \"quicksight:DeleteDashboard\",\n    \"DeleteDataSet\": \"quicksight:DeleteDataSet\",\n    \"DeleteDataSource\": \"quicksight:DeleteDataSource\",\n    \"DeleteFolder\": \"quicksight:DeleteFolder\",\n    \"DeleteFolderMembership\": \"quicksight:DeleteFolderMembership\",\n    \"DeleteGroup\": \"quicksight:DeleteGroup\",\n    \"DeleteGroupMembership\": \"quicksight:DeleteGroupMembership\",\n    \"DeleteIAMPolicyAssignment\": \"quicksight:DeleteIAMPolicyAssignment\",\n    \"DeleteNamespace\": \"quicksight:DeleteNamespace\",\n    \"DeleteTemplate\": \"quicksight:DeleteTemplate\",\n    \"DeleteTemplateAlias\": \"quicksight:DeleteTemplateAlias\",\n    \"DeleteTheme\": \"quicksight:DeleteTheme\",\n    \"DeleteThemeAlias\": \"quicksight:DeleteThemeAlias\",\n    \"DeleteUser\": \"quicksight:DeleteUser\",\n    \"DeleteUserByPrincipalId\": \"quicksight:DeleteUserByPrincipalId\",\n    \"DescribeAccountCustomization\": \"quicksight:DescribeAccountCustomization\",\n    \"DescribeAccountSettings\": \"quicksight:DescribeAccountSettings\",\n    \"DescribeAnalysis\": \"quicksight:DescribeAnalysis\",\n    \"DescribeAnalysisPermissions\": \"quicksight:DescribeAnalysisPermissions\",\n    \"DescribeDashboard\": \"quicksight:DescribeDashboard\",\n    \"DescribeDashboardPermissions\": \"quicksight:DescribeDashboardPermissions\",\n    \"DescribeDataSet\": \"quicksight:DescribeDataSet\",\n    \"DescribeDataSetPermissions\": \"quicksight:DescribeDataSetPermissions\",\n    \"DescribeDataSource\": \"quicksight:DescribeDataSource\",\n    \"DescribeDataSourcePermissions\": \"quicksight:DescribeDataSourcePermissions\",\n    \"DescribeFolder\": \"quicksight:DescribeFolder\",\n    \"DescribeFolderPermissions\": \"quicksight:DescribeFolderPermissions\",\n    \"DescribeFolderResolvedPermissions\": \"quicksight:DescribeFolderResolvedPermissions\",\n    \"DescribeGroup\": \"quicksight:DescribeGroup\",\n    \"DescribeIAMPolicyAssignment\": \"quicksight:DescribeIAMPolicyAssignment\",\n    \"DescribeIngestion\": \"quicksight:DescribeIngestion\",\n    \"DescribeIpRestriction\": \"quicksight:DescribeIpRestriction\",\n    \"DescribeNamespace\": \"quicksight:DescribeNamespace\",\n    \"DescribeTemplate\": \"quicksight:DescribeTemplate\",\n    \"DescribeTemplateAlias\": \"quicksight:DescribeTemplateAlias\",\n    \"DescribeTemplatePermissions\": \"quicksight:DescribeTemplatePermissions\",\n    \"DescribeTheme\": \"quicksight:DescribeTheme\",\n    \"DescribeThemeAlias\": \"quicksight:DescribeThemeAlias\",\n    \"DescribeThemePermissions\": \"quicksight:DescribeThemePermissions\",\n    \"DescribeUser\": \"quicksight:DescribeUser\",\n    \"GenerateEmbedUrlForAnonymousUser\": \"quicksight:GenerateEmbedUrlForAnonymousUser\",\n    \"GenerateEmbedUrlForRegisteredUser\": \"quicksight:GenerateEmbedUrlForRegisteredUser\",\n    \"GetDashboardEmbedUrl\": \"quicksight:GetDashboardEmbedUrl\",\n    \"GetSessionEmbedUrl\": \"quicksight:GetSessionEmbedUrl\",\n    \"ListAnalyses\": \"quicksight:ListAnalyses\",\n    \"ListDashboardVersions\": \"quicksight:ListDashboardVersions\",\n    \"ListDashboards\": \"quicksight:ListDashboards\",\n    \"ListDataSets\": \"quicksight:ListDataSets\",\n    \"ListDataSources\": \"quicksight:ListDataSources\",\n    \"ListFolderMembers\": \"quicksight:ListFolderMembers\",\n    \"ListFolders\": \"quicksight:ListFolders\",\n    \"ListGroupMemberships\": \"quicksight:ListGroupMemberships\",\n    \"ListGroups\": \"quicksight:ListGroups\",\n    \"ListIAMPolicyAssignments\": \"quicksight:ListIAMPolicyAssignments\",\n    \"ListIAMPolicyAssignmentsForUser\": \"quicksight:ListIAMPolicyAssignmentsForUser\",\n    \"ListIngestions\": \"quicksight:ListIngestions\",\n    \"ListNamespaces\": \"quicksight:ListNamespaces\",\n    \"ListTagsForResource\": \"quicksight:ListTagsForResource\",\n    \"ListTemplateAliases\": \"quicksight:ListTemplateAliases\",\n    \"ListTemplateVersions\": \"quicksight:ListTemplateVersions\",\n    \"ListTemplates\": \"quicksight:ListTemplates\",\n    \"ListThemeAliases\": \"quicksight:ListThemeAliases\",\n    \"ListThemeVersions\": \"quicksight:ListThemeVersions\",\n    \"ListThemes\": \"quicksight:ListThemes\",\n    \"ListUserGroups\": \"quicksight:ListUserGroups\",\n    \"ListUsers\": \"quicksight:ListUsers\",\n    \"RegisterUser\": \"quicksight:RegisterUser\",\n    \"RestoreAnalysis\": \"quicksight:RestoreAnalysis\",\n    \"SearchAnalyses\": \"quicksight:SearchAnalyses\",\n    \"SearchDashboards\": \"quicksight:SearchDashboards\",\n    \"SearchFolders\": \"quicksight:SearchFolders\",\n    \"TagResource\": \"quicksight:TagResource\",\n    \"UntagResource\": \"quicksight:UntagResource\",\n    \"UpdateAccountCustomization\": \"quicksight:UpdateAccountCustomization\",\n    \"UpdateAccountSettings\": \"quicksight:UpdateAccountSettings\",\n    \"UpdateAnalysis\": \"quicksight:UpdateAnalysis\",\n    \"UpdateAnalysisPermissions\": \"quicksight:UpdateAnalysisPermissions\",\n    \"UpdateDashboard\": \"quicksight:UpdateDashboard\",\n    \"UpdateDashboardPermissions\": \"quicksight:UpdateDashboardPermissions\",\n    \"UpdateDashboardPublishedVersion\": \"quicksight:UpdateDashboardPublishedVersion\",\n    \"UpdateDataSet\": \"quicksight:UpdateDataSet\",\n    \"UpdateDataSetPermissions\": \"quicksight:UpdateDataSetPermissions\",\n    \"UpdateDataSource\": \"quicksight:UpdateDataSource\",\n    \"UpdateDataSourcePermissions\": \"quicksight:UpdateDataSourcePermissions\",\n    \"UpdateFolder\": \"quicksight:UpdateFolder\",\n    \"UpdateFolderPermissions\": \"quicksight:UpdateFolderPermissions\",\n    \"UpdateGroup\": \"quicksight:UpdateGroup\",\n    \"UpdateIAMPolicyAssignment\": \"quicksight:UpdateIAMPolicyAssignment\",\n    \"UpdateIpRestriction\": \"quicksight:UpdateIpRestriction\",\n    \"UpdateTemplate\": \"quicksight:UpdateTemplate\",\n    \"UpdateTemplateAlias\": \"quicksight:UpdateTemplateAlias\",\n    \"UpdateTemplatePermissions\": \"quicksight:UpdateTemplatePermissions\",\n    \"UpdateTheme\": \"quicksight:UpdateTheme\",\n    \"UpdateThemeAlias\": \"quicksight:UpdateThemeAlias\",\n    \"UpdateThemePermissions\": \"quicksight:UpdateThemePermissions\",\n    \"UpdateUser\": \"quicksight:UpdateUser\"\n  },\n  \"ram\": {\n    \"AcceptResourceShareInvitation\": \"ram:AcceptResourceShareInvitation\",\n    \"AssociateResourceShare\": \"ram:AssociateResourceShare\",\n    \"AssociateResourceSharePermission\": \"ram:AssociateResourceSharePermission\",\n    \"CreateResourceShare\": \"ram:CreateResourceShare\",\n    \"DeleteResourceShare\": \"ram:DeleteResourceShare\",\n    \"DisassociateResourceShare\": \"ram:DisassociateResourceShare\",\n    \"DisassociateResourceSharePermission\": \"ram:DisassociateResourceSharePermission\",\n    \"EnableSharingWithAwsOrganization\": \"ram:EnableSharingWithAwsOrganization\",\n    \"GetPermission\": \"ram:GetPermission\",\n    \"GetResourcePolicies\": \"ram:GetResourcePolicies\",\n    \"GetResourceShareAssociations\": \"ram:GetResourceShareAssociations\",\n    \"GetResourceShareInvitations\": \"ram:GetResourceShareInvitations\",\n    \"GetResourceShares\": \"ram:GetResourceShares\",\n    \"ListPendingInvitationResources\": \"ram:ListPendingInvitationResources\",\n    \"ListPermissions\": \"ram:ListPermissions\",\n    \"ListPrincipals\": \"ram:ListPrincipals\",\n    \"ListResourceSharePermissions\": \"ram:ListResourceSharePermissions\",\n    \"ListResourceTypes\": \"ram:ListResourceTypes\",\n    \"ListResources\": \"ram:ListResources\",\n    \"PromoteResourceShareCreatedFromPolicy\": \"ram:PromoteResourceShareCreatedFromPolicy\",\n    \"RejectResourceShareInvitation\": \"ram:RejectResourceShareInvitation\",\n    \"TagResource\": \"ram:TagResource\",\n    \"UntagResource\": \"ram:UntagResource\",\n    \"UpdateResourceShare\": \"ram:UpdateResourceShare\"\n  },\n  \"rbin\": {\n    \"CreateRule\": \"rbin:CreateRule\",\n    \"DeleteRule\": \"rbin:DeleteRule\",\n    \"GetRule\": \"rbin:GetRule\",\n    \"ListRules\": \"rbin:ListRules\",\n    \"ListTagsForResource\": \"rbin:ListTagsForResource\",\n    \"TagResource\": \"rbin:TagResource\",\n    \"UntagResource\": \"rbin:UntagResource\",\n    \"UpdateRule\": \"rbin:UpdateRule\"\n  },\n  \"rds\": {\n    \"AddRoleToDBCluster\": \"rds:AddRoleToDBCluster\",\n    \"AddRoleToDBInstance\": \"rds:AddRoleToDBInstance\",\n    \"AddSourceIdentifierToSubscription\": \"rds:AddSourceIdentifierToSubscription\",\n    \"AddTagsToResource\": \"rds:AddTagsToResource\",\n    \"ApplyPendingMaintenanceAction\": \"rds:ApplyPendingMaintenanceAction\",\n    \"AuthorizeDBSecurityGroupIngress\": \"rds:AuthorizeDBSecurityGroupIngress\",\n    \"BacktrackDBCluster\": \"rds:BacktrackDBCluster\",\n    \"CancelExportTask\": \"rds:CancelExportTask\",\n    \"CopyDBClusterParameterGroup\": \"rds:CopyDBClusterParameterGroup\",\n    \"CopyDBClusterSnapshot\": \"rds:CopyDBClusterSnapshot\",\n    \"CopyDBParameterGroup\": \"rds:CopyDBParameterGroup\",\n    \"CopyDBSnapshot\": \"rds:CopyDBSnapshot\",\n    \"CopyOptionGroup\": \"rds:CopyOptionGroup\",\n    \"CreateCustomAvailabilityZone\": \"rds:CreateCustomAvailabilityZone\",\n    \"CreateCustomDBEngineVersion\": \"rds:CreateCustomDBEngineVersion\",\n    \"CreateDBCluster\": \"rds:CreateDBCluster\",\n    \"CreateDBClusterEndpoint\": \"rds:CreateDBClusterEndpoint\",\n    \"CreateDBClusterParameterGroup\": \"rds:CreateDBClusterParameterGroup\",\n    \"CreateDBClusterSnapshot\": \"rds:CreateDBClusterSnapshot\",\n    \"CreateDBInstance\": \"rds:CreateDBInstance\",\n    \"CreateDBInstanceReadReplica\": \"rds:CreateDBInstanceReadReplica\",\n    \"CreateDBParameterGroup\": \"rds:CreateDBParameterGroup\",\n    \"CreateDBProxy\": \"rds:CreateDBProxy\",\n    \"CreateDBProxyEndpoint\": \"rds:CreateDBProxyEndpoint\",\n    \"CreateDBSecurityGroup\": \"rds:CreateDBSecurityGroup\",\n    \"CreateDBSnapshot\": \"rds:CreateDBSnapshot\",\n    \"CreateDBSubnetGroup\": \"rds:CreateDBSubnetGroup\",\n    \"CreateEventSubscription\": \"rds:CreateEventSubscription\",\n    \"CreateGlobalCluster\": \"rds:CreateGlobalCluster\",\n    \"CreateOptionGroup\": \"rds:CreateOptionGroup\",\n    \"DeleteCustomAvailabilityZone\": \"rds:DeleteCustomAvailabilityZone\",\n    \"DeleteCustomDBEngineVersion\": \"rds:DeleteCustomDBEngineVersion\",\n    \"DeleteDBCluster\": \"rds:DeleteDBCluster\",\n    \"DeleteDBClusterEndpoint\": \"rds:DeleteDBClusterEndpoint\",\n    \"DeleteDBClusterParameterGroup\": \"rds:DeleteDBClusterParameterGroup\",\n    \"DeleteDBClusterSnapshot\": \"rds:DeleteDBClusterSnapshot\",\n    \"DeleteDBInstance\": \"rds:DeleteDBInstance\",\n    \"DeleteDBInstanceAutomatedBackup\": \"rds:DeleteDBInstanceAutomatedBackup\",\n    \"DeleteDBParameterGroup\": \"rds:DeleteDBParameterGroup\",\n    \"DeleteDBProxy\": \"rds:DeleteDBProxy\",\n    \"DeleteDBProxyEndpoint\": \"rds:DeleteDBProxyEndpoint\",\n    \"DeleteDBSecurityGroup\": \"rds:DeleteDBSecurityGroup\",\n    \"DeleteDBSnapshot\": \"rds:DeleteDBSnapshot\",\n    \"DeleteDBSubnetGroup\": \"rds:DeleteDBSubnetGroup\",\n    \"DeleteEventSubscription\": \"rds:DeleteEventSubscription\",\n    \"DeleteGlobalCluster\": \"rds:DeleteGlobalCluster\",\n    \"DeleteInstallationMedia\": \"rds:DeleteInstallationMedia\",\n    \"DeleteOptionGroup\": \"rds:DeleteOptionGroup\",\n    \"DeregisterDBProxyTargets\": \"rds:DeregisterDBProxyTargets\",\n    \"DescribeAccountAttributes\": \"rds:DescribeAccountAttributes\",\n    \"DescribeCertificates\": \"rds:DescribeCertificates\",\n    \"DescribeCustomAvailabilityZones\": \"rds:DescribeCustomAvailabilityZones\",\n    \"DescribeDBClusterBacktracks\": \"rds:DescribeDBClusterBacktracks\",\n    \"DescribeDBClusterEndpoints\": \"rds:DescribeDBClusterEndpoints\",\n    \"DescribeDBClusterParameterGroups\": \"rds:DescribeDBClusterParameterGroups\",\n    \"DescribeDBClusterParameters\": \"rds:DescribeDBClusterParameters\",\n    \"DescribeDBClusterSnapshotAttributes\": \"rds:DescribeDBClusterSnapshotAttributes\",\n    \"DescribeDBClusterSnapshots\": \"rds:DescribeDBClusterSnapshots\",\n    \"DescribeDBClusters\": \"rds:DescribeDBClusters\",\n    \"DescribeDBEngineVersions\": \"rds:DescribeDBEngineVersions\",\n    \"DescribeDBInstanceAutomatedBackups\": \"rds:DescribeDBInstanceAutomatedBackups\",\n    \"DescribeDBInstances\": \"rds:DescribeDBInstances\",\n    \"DescribeDBLogFiles\": \"rds:DescribeDBLogFiles\",\n    \"DescribeDBParameterGroups\": \"rds:DescribeDBParameterGroups\",\n    \"DescribeDBParameters\": \"rds:DescribeDBParameters\",\n    \"DescribeDBProxies\": \"rds:DescribeDBProxies\",\n    \"DescribeDBProxyEndpoints\": \"rds:DescribeDBProxyEndpoints\",\n    \"DescribeDBProxyTargetGroups\": \"rds:DescribeDBProxyTargetGroups\",\n    \"DescribeDBProxyTargets\": \"rds:DescribeDBProxyTargets\",\n    \"DescribeDBSecurityGroups\": \"rds:DescribeDBSecurityGroups\",\n    \"DescribeDBSnapshotAttributes\": \"rds:DescribeDBSnapshotAttributes\",\n    \"DescribeDBSnapshots\": \"rds:DescribeDBSnapshots\",\n    \"DescribeDBSubnetGroups\": \"rds:DescribeDBSubnetGroups\",\n    \"DescribeEngineDefaultClusterParameters\": \"rds:DescribeEngineDefaultClusterParameters\",\n    \"DescribeEngineDefaultParameters\": \"rds:DescribeEngineDefaultParameters\",\n    \"DescribeEventCategories\": \"rds:DescribeEventCategories\",\n    \"DescribeEventSubscriptions\": \"rds:DescribeEventSubscriptions\",\n    \"DescribeEvents\": \"rds:DescribeEvents\",\n    \"DescribeExportTasks\": \"rds:DescribeExportTasks\",\n    \"DescribeGlobalClusters\": \"rds:DescribeGlobalClusters\",\n    \"DescribeInstallationMedia\": \"rds:DescribeInstallationMedia\",\n    \"DescribeOptionGroupOptions\": \"rds:DescribeOptionGroupOptions\",\n    \"DescribeOptionGroups\": \"rds:DescribeOptionGroups\",\n    \"DescribeOrderableDBInstanceOptions\": \"rds:DescribeOrderableDBInstanceOptions\",\n    \"DescribePendingMaintenanceActions\": \"rds:DescribePendingMaintenanceActions\",\n    \"DescribeReservedDBInstances\": \"rds:DescribeReservedDBInstances\",\n    \"DescribeReservedDBInstancesOfferings\": \"rds:DescribeReservedDBInstancesOfferings\",\n    \"DescribeSourceRegions\": \"rds:DescribeSourceRegions\",\n    \"DescribeValidDBInstanceModifications\": \"rds:DescribeValidDBInstanceModifications\",\n    \"DownloadDBLogFilePortion\": \"rds:DownloadDBLogFilePortion\",\n    \"FailoverDBCluster\": \"rds:FailoverDBCluster\",\n    \"FailoverGlobalCluster\": \"rds:FailoverGlobalCluster\",\n    \"ImportInstallationMedia\": \"rds:ImportInstallationMedia\",\n    \"ListTagsForResource\": \"rds:ListTagsForResource\",\n    \"ModifyCertificates\": \"rds:ModifyCertificates\",\n    \"ModifyCurrentDBClusterCapacity\": \"rds:ModifyCurrentDBClusterCapacity\",\n    \"ModifyCustomDBEngineVersion\": \"rds:ModifyCustomDBEngineVersion\",\n    \"ModifyDBCluster\": \"rds:ModifyDBCluster\",\n    \"ModifyDBClusterEndpoint\": \"rds:ModifyDBClusterEndpoint\",\n    \"ModifyDBClusterParameterGroup\": \"rds:ModifyDBClusterParameterGroup\",\n    \"ModifyDBClusterSnapshotAttribute\": \"rds:ModifyDBClusterSnapshotAttribute\",\n    \"ModifyDBInstance\": \"rds:ModifyDBInstance\",\n    \"ModifyDBParameterGroup\": \"rds:ModifyDBParameterGroup\",\n    \"ModifyDBProxy\": \"rds:ModifyDBProxy\",\n    \"ModifyDBProxyEndpoint\": \"rds:ModifyDBProxyEndpoint\",\n    \"ModifyDBProxyTargetGroup\": \"rds:ModifyDBProxyTargetGroup\",\n    \"ModifyDBSnapshot\": \"rds:ModifyDBSnapshot\",\n    \"ModifyDBSnapshotAttribute\": \"rds:ModifyDBSnapshotAttribute\",\n    \"ModifyDBSubnetGroup\": \"rds:ModifyDBSubnetGroup\",\n    \"ModifyEventSubscription\": \"rds:ModifyEventSubscription\",\n    \"ModifyGlobalCluster\": \"rds:ModifyGlobalCluster\",\n    \"ModifyOptionGroup\": \"rds:ModifyOptionGroup\",\n    \"PromoteReadReplica\": \"rds:PromoteReadReplica\",\n    \"PromoteReadReplicaDBCluster\": \"rds:PromoteReadReplicaDBCluster\",\n    \"PurchaseReservedDBInstancesOffering\": \"rds:PurchaseReservedDBInstancesOffering\",\n    \"RebootDBCluster\": \"rds:RebootDBCluster\",\n    \"RebootDBInstance\": \"rds:RebootDBInstance\",\n    \"RegisterDBProxyTargets\": \"rds:RegisterDBProxyTargets\",\n    \"RemoveFromGlobalCluster\": \"rds:RemoveFromGlobalCluster\",\n    \"RemoveRoleFromDBCluster\": \"rds:RemoveRoleFromDBCluster\",\n    \"RemoveRoleFromDBInstance\": \"rds:RemoveRoleFromDBInstance\",\n    \"RemoveSourceIdentifierFromSubscription\": \"rds:RemoveSourceIdentifierFromSubscription\",\n    \"RemoveTagsFromResource\": \"rds:RemoveTagsFromResource\",\n    \"ResetDBClusterParameterGroup\": \"rds:ResetDBClusterParameterGroup\",\n    \"ResetDBParameterGroup\": \"rds:ResetDBParameterGroup\",\n    \"RestoreDBClusterFromS3\": \"rds:RestoreDBClusterFromS3\",\n    \"RestoreDBClusterFromSnapshot\": \"rds:RestoreDBClusterFromSnapshot\",\n    \"RestoreDBClusterToPointInTime\": \"rds:RestoreDBClusterToPointInTime\",\n    \"RestoreDBInstanceFromDBSnapshot\": \"rds:RestoreDBInstanceFromDBSnapshot\",\n    \"RestoreDBInstanceFromS3\": \"rds:RestoreDBInstanceFromS3\",\n    \"RestoreDBInstanceToPointInTime\": \"rds:RestoreDBInstanceToPointInTime\",\n    \"RevokeDBSecurityGroupIngress\": \"rds:RevokeDBSecurityGroupIngress\",\n    \"StartActivityStream\": \"rds:StartActivityStream\",\n    \"StartDBCluster\": \"rds:StartDBCluster\",\n    \"StartDBInstance\": \"rds:StartDBInstance\",\n    \"StartDBInstanceAutomatedBackupsReplication\": \"rds:StartDBInstanceAutomatedBackupsReplication\",\n    \"StartExportTask\": \"rds:StartExportTask\",\n    \"StopActivityStream\": \"rds:StopActivityStream\",\n    \"StopDBCluster\": \"rds:StopDBCluster\",\n    \"StopDBInstance\": \"rds:StopDBInstance\",\n    \"StopDBInstanceAutomatedBackupsReplication\": \"rds:StopDBInstanceAutomatedBackupsReplication\"\n  },\n  \"rds-data\": {\n    \"BatchExecuteStatement\": \"rds-data:BatchExecuteStatement\",\n    \"BeginTransaction\": \"rds-data:BeginTransaction\",\n    \"CommitTransaction\": \"rds-data:CommitTransaction\",\n    \"ExecuteSql\": \"rds-data:ExecuteSql\",\n    \"ExecuteStatement\": \"rds-data:ExecuteStatement\",\n    \"RollbackTransaction\": \"rds-data:RollbackTransaction\"\n  },\n  \"redshift\": {\n    \"AcceptReservedNodeExchange\": \"redshift:AcceptReservedNodeExchange\",\n    \"AssociateDataShareConsumer\": \"redshift:AssociateDataShareConsumer\",\n    \"AuthorizeClusterSecurityGroupIngress\": \"redshift:AuthorizeClusterSecurityGroupIngress\",\n    \"AuthorizeDataShare\": \"redshift:AuthorizeDataShare\",\n    \"AuthorizeSnapshotAccess\": \"redshift:AuthorizeSnapshotAccess\",\n    \"BatchDeleteClusterSnapshots\": \"redshift:BatchDeleteClusterSnapshots\",\n    \"BatchModifyClusterSnapshots\": \"redshift:BatchModifyClusterSnapshots\",\n    \"CancelResize\": \"redshift:CancelResize\",\n    \"CopyClusterSnapshot\": \"redshift:CopyClusterSnapshot\",\n    \"CreateAuthenticationProfile\": \"redshift:CreateAuthenticationProfile\",\n    \"CreateCluster\": \"redshift:CreateCluster\",\n    \"CreateClusterParameterGroup\": \"redshift:CreateClusterParameterGroup\",\n    \"CreateClusterSecurityGroup\": \"redshift:CreateClusterSecurityGroup\",\n    \"CreateClusterSnapshot\": \"redshift:CreateClusterSnapshot\",\n    \"CreateClusterSubnetGroup\": \"redshift:CreateClusterSubnetGroup\",\n    \"CreateEventSubscription\": \"redshift:CreateEventSubscription\",\n    \"CreateHsmClientCertificate\": \"redshift:CreateHsmClientCertificate\",\n    \"CreateHsmConfiguration\": \"redshift:CreateHsmConfiguration\",\n    \"CreateScheduledAction\": \"redshift:CreateScheduledAction\",\n    \"CreateSnapshotCopyGrant\": \"redshift:CreateSnapshotCopyGrant\",\n    \"CreateSnapshotSchedule\": \"redshift:CreateSnapshotSchedule\",\n    \"CreateTags\": \"redshift:CreateTags\",\n    \"CreateUsageLimit\": \"redshift:CreateUsageLimit\",\n    \"DeauthorizeDataShare\": \"redshift:DeauthorizeDataShare\",\n    \"DeleteAuthenticationProfile\": \"redshift:DeleteAuthenticationProfile\",\n    \"DeleteCluster\": \"redshift:DeleteCluster\",\n    \"DeleteClusterParameterGroup\": \"redshift:DeleteClusterParameterGroup\",\n    \"DeleteClusterSecurityGroup\": \"redshift:DeleteClusterSecurityGroup\",\n    \"DeleteClusterSnapshot\": \"redshift:DeleteClusterSnapshot\",\n    \"DeleteClusterSubnetGroup\": \"redshift:DeleteClusterSubnetGroup\",\n    \"DeleteEventSubscription\": \"redshift:DeleteEventSubscription\",\n    \"DeleteHsmClientCertificate\": \"redshift:DeleteHsmClientCertificate\",\n    \"DeleteHsmConfiguration\": \"redshift:DeleteHsmConfiguration\",\n    \"DeleteScheduledAction\": \"redshift:DeleteScheduledAction\",\n    \"DeleteSnapshotCopyGrant\": \"redshift:DeleteSnapshotCopyGrant\",\n    \"DeleteSnapshotSchedule\": \"redshift:DeleteSnapshotSchedule\",\n    \"DeleteTags\": \"redshift:DeleteTags\",\n    \"DeleteUsageLimit\": \"redshift:DeleteUsageLimit\",\n    \"DescribeAccountAttributes\": \"redshift:DescribeAccountAttributes\",\n    \"DescribeAuthenticationProfiles\": \"redshift:DescribeAuthenticationProfiles\",\n    \"DescribeClusterDbRevisions\": \"redshift:DescribeClusterDbRevisions\",\n    \"DescribeClusterParameterGroups\": \"redshift:DescribeClusterParameterGroups\",\n    \"DescribeClusterParameters\": \"redshift:DescribeClusterParameters\",\n    \"DescribeClusterSecurityGroups\": \"redshift:DescribeClusterSecurityGroups\",\n    \"DescribeClusterSnapshots\": \"redshift:DescribeClusterSnapshots\",\n    \"DescribeClusterSubnetGroups\": \"redshift:DescribeClusterSubnetGroups\",\n    \"DescribeClusterTracks\": \"redshift:DescribeClusterTracks\",\n    \"DescribeClusterVersions\": \"redshift:DescribeClusterVersions\",\n    \"DescribeClusters\": \"redshift:DescribeClusters\",\n    \"DescribeDataShares\": \"redshift:DescribeDataShares\",\n    \"DescribeDataSharesForConsumer\": \"redshift:DescribeDataSharesForConsumer\",\n    \"DescribeDataSharesForProducer\": \"redshift:DescribeDataSharesForProducer\",\n    \"DescribeDefaultClusterParameters\": \"redshift:DescribeDefaultClusterParameters\",\n    \"DescribeEventCategories\": \"redshift:DescribeEventCategories\",\n    \"DescribeEventSubscriptions\": \"redshift:DescribeEventSubscriptions\",\n    \"DescribeEvents\": \"redshift:DescribeEvents\",\n    \"DescribeHsmClientCertificates\": \"redshift:DescribeHsmClientCertificates\",\n    \"DescribeHsmConfigurations\": \"redshift:DescribeHsmConfigurations\",\n    \"DescribeLoggingStatus\": \"redshift:DescribeLoggingStatus\",\n    \"DescribeNodeConfigurationOptions\": \"redshift:DescribeNodeConfigurationOptions\",\n    \"DescribeOrderableClusterOptions\": \"redshift:DescribeOrderableClusterOptions\",\n    \"DescribeReservedNodeOfferings\": \"redshift:DescribeReservedNodeOfferings\",\n    \"DescribeReservedNodes\": \"redshift:DescribeReservedNodes\",\n    \"DescribeResize\": \"redshift:DescribeResize\",\n    \"DescribeScheduledActions\": \"redshift:DescribeScheduledActions\",\n    \"DescribeSnapshotCopyGrants\": \"redshift:DescribeSnapshotCopyGrants\",\n    \"DescribeSnapshotSchedules\": \"redshift:DescribeSnapshotSchedules\",\n    \"DescribeStorage\": \"redshift:DescribeStorage\",\n    \"DescribeTableRestoreStatus\": \"redshift:DescribeTableRestoreStatus\",\n    \"DescribeTags\": \"redshift:DescribeTags\",\n    \"DescribeUsageLimits\": \"redshift:DescribeUsageLimits\",\n    \"DisableLogging\": \"redshift:DisableLogging\",\n    \"DisableSnapshotCopy\": \"redshift:DisableSnapshotCopy\",\n    \"DisassociateDataShareConsumer\": \"redshift:DisassociateDataShareConsumer\",\n    \"EnableLogging\": \"redshift:EnableLogging\",\n    \"EnableSnapshotCopy\": \"redshift:EnableSnapshotCopy\",\n    \"GetClusterCredentials\": \"redshift:GetClusterCredentials\",\n    \"GetReservedNodeExchangeOfferings\": \"redshift:GetReservedNodeExchangeOfferings\",\n    \"ModifyAquaConfiguration\": \"redshift:ModifyAquaConfiguration\",\n    \"ModifyAuthenticationProfile\": \"redshift:ModifyAuthenticationProfile\",\n    \"ModifyCluster\": \"redshift:ModifyCluster\",\n    \"ModifyClusterDbRevision\": \"redshift:ModifyClusterDbRevision\",\n    \"ModifyClusterIamRoles\": \"redshift:ModifyClusterIamRoles\",\n    \"ModifyClusterMaintenance\": \"redshift:ModifyClusterMaintenance\",\n    \"ModifyClusterParameterGroup\": \"redshift:ModifyClusterParameterGroup\",\n    \"ModifyClusterSnapshot\": \"redshift:ModifyClusterSnapshot\",\n    \"ModifyClusterSnapshotSchedule\": \"redshift:ModifyClusterSnapshotSchedule\",\n    \"ModifyClusterSubnetGroup\": \"redshift:ModifyClusterSubnetGroup\",\n    \"ModifyEventSubscription\": \"redshift:ModifyEventSubscription\",\n    \"ModifyScheduledAction\": \"redshift:ModifyScheduledAction\",\n    \"ModifySnapshotCopyRetentionPeriod\": \"redshift:ModifySnapshotCopyRetentionPeriod\",\n    \"ModifySnapshotSchedule\": \"redshift:ModifySnapshotSchedule\",\n    \"ModifyUsageLimit\": \"redshift:ModifyUsageLimit\",\n    \"PauseCluster\": \"redshift:PauseCluster\",\n    \"PurchaseReservedNodeOffering\": \"redshift:PurchaseReservedNodeOffering\",\n    \"RebootCluster\": \"redshift:RebootCluster\",\n    \"RejectDataShare\": \"redshift:RejectDataShare\",\n    \"ResetClusterParameterGroup\": \"redshift:ResetClusterParameterGroup\",\n    \"ResizeCluster\": \"redshift:ResizeCluster\",\n    \"RestoreFromClusterSnapshot\": \"redshift:RestoreFromClusterSnapshot\",\n    \"RestoreTableFromClusterSnapshot\": \"redshift:RestoreTableFromClusterSnapshot\",\n    \"ResumeCluster\": \"redshift:ResumeCluster\",\n    \"RevokeClusterSecurityGroupIngress\": \"redshift:RevokeClusterSecurityGroupIngress\",\n    \"RevokeSnapshotAccess\": \"redshift:RevokeSnapshotAccess\",\n    \"RotateEncryptionKey\": \"redshift:RotateEncryptionKey\"\n  },\n  \"redshift-data\": {\n    \"BatchExecuteStatement\": \"redshift-data:BatchExecuteStatement\",\n    \"CancelStatement\": \"redshift-data:CancelStatement\",\n    \"DescribeStatement\": \"redshift-data:DescribeStatement\",\n    \"DescribeTable\": \"redshift-data:DescribeTable\",\n    \"ExecuteStatement\": \"redshift-data:ExecuteStatement\",\n    \"GetStatementResult\": \"redshift-data:GetStatementResult\",\n    \"ListDatabases\": \"redshift-data:ListDatabases\",\n    \"ListSchemas\": \"redshift-data:ListSchemas\",\n    \"ListStatements\": \"redshift-data:ListStatements\",\n    \"ListTables\": \"redshift-data:ListTables\"\n  },\n  \"rekognition\": {\n    \"CompareFaces\": \"rekognition:CompareFaces\",\n    \"CreateCollection\": \"rekognition:CreateCollection\",\n    \"CreateDataset\": \"rekognition:CreateDataset\",\n    \"CreateProject\": \"rekognition:CreateProject\",\n    \"CreateProjectVersion\": \"rekognition:CreateProjectVersion\",\n    \"CreateStreamProcessor\": \"rekognition:CreateStreamProcessor\",\n    \"DeleteCollection\": \"rekognition:DeleteCollection\",\n    \"DeleteDataset\": \"rekognition:DeleteDataset\",\n    \"DeleteFaces\": \"rekognition:DeleteFaces\",\n    \"DeleteProject\": \"rekognition:DeleteProject\",\n    \"DeleteProjectVersion\": \"rekognition:DeleteProjectVersion\",\n    \"DeleteStreamProcessor\": \"rekognition:DeleteStreamProcessor\",\n    \"DescribeCollection\": \"rekognition:DescribeCollection\",\n    \"DescribeDataset\": \"rekognition:DescribeDataset\",\n    \"DescribeProjectVersions\": \"rekognition:DescribeProjectVersions\",\n    \"DescribeProjects\": \"rekognition:DescribeProjects\",\n    \"DescribeStreamProcessor\": \"rekognition:DescribeStreamProcessor\",\n    \"DetectCustomLabels\": \"rekognition:DetectCustomLabels\",\n    \"DetectFaces\": \"rekognition:DetectFaces\",\n    \"DetectLabels\": \"rekognition:DetectLabels\",\n    \"DetectModerationLabels\": \"rekognition:DetectModerationLabels\",\n    \"DetectProtectiveEquipment\": \"rekognition:DetectProtectiveEquipment\",\n    \"DetectText\": \"rekognition:DetectText\",\n    \"DistributeDatasetEntries\": \"rekognition:DistributeDatasetEntries\",\n    \"GetCelebrityInfo\": \"rekognition:GetCelebrityInfo\",\n    \"GetCelebrityRecognition\": \"rekognition:GetCelebrityRecognition\",\n    \"GetContentModeration\": \"rekognition:GetContentModeration\",\n    \"GetFaceDetection\": \"rekognition:GetFaceDetection\",\n    \"GetFaceSearch\": \"rekognition:GetFaceSearch\",\n    \"GetLabelDetection\": \"rekognition:GetLabelDetection\",\n    \"GetPersonTracking\": \"rekognition:GetPersonTracking\",\n    \"GetSegmentDetection\": \"rekognition:GetSegmentDetection\",\n    \"GetTextDetection\": \"rekognition:GetTextDetection\",\n    \"IndexFaces\": \"rekognition:IndexFaces\",\n    \"ListCollections\": \"rekognition:ListCollections\",\n    \"ListDatasetEntries\": \"rekognition:ListDatasetEntries\",\n    \"ListDatasetLabels\": \"rekognition:ListDatasetLabels\",\n    \"ListFaces\": \"rekognition:ListFaces\",\n    \"ListStreamProcessors\": \"rekognition:ListStreamProcessors\",\n    \"ListTagsForResource\": \"rekognition:ListTagsForResource\",\n    \"RecognizeCelebrities\": \"rekognition:RecognizeCelebrities\",\n    \"SearchFaces\": \"rekognition:SearchFaces\",\n    \"SearchFacesByImage\": \"rekognition:SearchFacesByImage\",\n    \"StartCelebrityRecognition\": \"rekognition:StartCelebrityRecognition\",\n    \"StartContentModeration\": \"rekognition:StartContentModeration\",\n    \"StartFaceDetection\": \"rekognition:StartFaceDetection\",\n    \"StartFaceSearch\": \"rekognition:StartFaceSearch\",\n    \"StartLabelDetection\": \"rekognition:StartLabelDetection\",\n    \"StartPersonTracking\": \"rekognition:StartPersonTracking\",\n    \"StartProjectVersion\": \"rekognition:StartProjectVersion\",\n    \"StartSegmentDetection\": \"rekognition:StartSegmentDetection\",\n    \"StartStreamProcessor\": \"rekognition:StartStreamProcessor\",\n    \"StartTextDetection\": \"rekognition:StartTextDetection\",\n    \"StopProjectVersion\": \"rekognition:StopProjectVersion\",\n    \"StopStreamProcessor\": \"rekognition:StopStreamProcessor\",\n    \"TagResource\": \"rekognition:TagResource\",\n    \"UntagResource\": \"rekognition:UntagResource\",\n    \"UpdateDatasetEntries\": \"rekognition:UpdateDatasetEntries\"\n  },\n  \"resiliencehub\": {\n    \"AddDraftAppVersionResourceMappings\": \"resiliencehub:AddDraftAppVersionResourceMappings\",\n    \"CreateApp\": \"resiliencehub:CreateApp\",\n    \"CreateRecommendationTemplate\": \"resiliencehub:CreateRecommendationTemplate\",\n    \"CreateResiliencyPolicy\": \"resiliencehub:CreateResiliencyPolicy\",\n    \"DeleteApp\": \"resiliencehub:DeleteApp\",\n    \"DeleteAppAssessment\": \"resiliencehub:DeleteAppAssessment\",\n    \"DeleteRecommendationTemplate\": \"resiliencehub:DeleteRecommendationTemplate\",\n    \"DeleteResiliencyPolicy\": \"resiliencehub:DeleteResiliencyPolicy\",\n    \"DescribeApp\": \"resiliencehub:DescribeApp\",\n    \"DescribeAppAssessment\": \"resiliencehub:DescribeAppAssessment\",\n    \"DescribeAppVersionResourcesResolutionStatus\": \"resiliencehub:DescribeAppVersionResourcesResolutionStatus\",\n    \"DescribeAppVersionTemplate\": \"resiliencehub:DescribeAppVersionTemplate\",\n    \"DescribeDraftAppVersionResourcesImportStatus\": \"resiliencehub:DescribeDraftAppVersionResourcesImportStatus\",\n    \"DescribeResiliencyPolicy\": \"resiliencehub:DescribeResiliencyPolicy\",\n    \"ImportResourcesToDraftAppVersion\": \"resiliencehub:ImportResourcesToDraftAppVersion\",\n    \"ListAlarmRecommendations\": \"resiliencehub:ListAlarmRecommendations\",\n    \"ListAppAssessments\": \"resiliencehub:ListAppAssessments\",\n    \"ListAppComponentCompliances\": \"resiliencehub:ListAppComponentCompliances\",\n    \"ListAppComponentRecommendations\": \"resiliencehub:ListAppComponentRecommendations\",\n    \"ListAppVersionResourceMappings\": \"resiliencehub:ListAppVersionResourceMappings\",\n    \"ListAppVersionResources\": \"resiliencehub:ListAppVersionResources\",\n    \"ListAppVersions\": \"resiliencehub:ListAppVersions\",\n    \"ListApps\": \"resiliencehub:ListApps\",\n    \"ListRecommendationTemplates\": \"resiliencehub:ListRecommendationTemplates\",\n    \"ListResiliencyPolicies\": \"resiliencehub:ListResiliencyPolicies\",\n    \"ListSopRecommendations\": \"resiliencehub:ListSopRecommendations\",\n    \"ListSuggestedResiliencyPolicies\": \"resiliencehub:ListSuggestedResiliencyPolicies\",\n    \"ListTagsForResource\": \"resiliencehub:ListTagsForResource\",\n    \"ListTestRecommendations\": \"resiliencehub:ListTestRecommendations\",\n    \"ListUnsupportedAppVersionResources\": \"resiliencehub:ListUnsupportedAppVersionResources\",\n    \"PublishAppVersion\": \"resiliencehub:PublishAppVersion\",\n    \"PutDraftAppVersionTemplate\": \"resiliencehub:PutDraftAppVersionTemplate\",\n    \"RemoveDraftAppVersionResourceMappings\": \"resiliencehub:RemoveDraftAppVersionResourceMappings\",\n    \"ResolveAppVersionResources\": \"resiliencehub:ResolveAppVersionResources\",\n    \"StartAppAssessment\": \"resiliencehub:StartAppAssessment\",\n    \"TagResource\": \"resiliencehub:TagResource\",\n    \"UntagResource\": \"resiliencehub:UntagResource\",\n    \"UpdateApp\": \"resiliencehub:UpdateApp\",\n    \"UpdateResiliencyPolicy\": \"resiliencehub:UpdateResiliencyPolicy\"\n  },\n  \"resource-groups\": {\n    \"CreateGroup\": \"resource-groups:CreateGroup\",\n    \"DeleteGroup\": \"resource-groups:DeleteGroup\",\n    \"GetGroup\": \"resource-groups:GetGroup\",\n    \"GetGroupConfiguration\": \"resource-groups:GetGroupConfiguration\",\n    \"GetGroupQuery\": \"resource-groups:GetGroupQuery\",\n    \"GetTags\": \"resource-groups:GetTags\",\n    \"GroupResources\": \"resource-groups:GroupResources\",\n    \"ListGroupResources\": \"resource-groups:ListGroupResources\",\n    \"ListGroups\": \"resource-groups:ListGroups\",\n    \"PutGroupConfiguration\": \"resource-groups:PutGroupConfiguration\",\n    \"SearchResources\": \"resource-groups:SearchResources\",\n    \"Tag\": \"resource-groups:Tag\",\n    \"UngroupResources\": \"resource-groups:UngroupResources\",\n    \"Untag\": \"resource-groups:Untag\",\n    \"UpdateGroup\": \"resource-groups:UpdateGroup\",\n    \"UpdateGroupQuery\": \"resource-groups:UpdateGroupQuery\"\n  },\n  \"resourcegroupstaggingapi\": {\n    \"DescribeReportCreation\": \"tag:DescribeReportCreation\",\n    \"GetComplianceSummary\": \"tag:GetComplianceSummary\",\n    \"GetResources\": \"tag:GetResources\",\n    \"GetTagKeys\": \"tag:GetTagKeys\",\n    \"GetTagValues\": \"tag:GetTagValues\",\n    \"StartReportCreation\": \"tag:StartReportCreation\",\n    \"TagResources\": \"tag:TagResources\",\n    \"UntagResources\": \"tag:UntagResources\"\n  },\n  \"robomaker\": {\n    \"BatchDeleteWorlds\": \"robomaker:BatchDeleteWorlds\",\n    \"BatchDescribeSimulationJob\": \"robomaker:BatchDescribeSimulationJob\",\n    \"CancelDeploymentJob\": \"robomaker:CancelDeploymentJob\",\n    \"CancelSimulationJob\": \"robomaker:CancelSimulationJob\",\n    \"CancelSimulationJobBatch\": \"robomaker:CancelSimulationJobBatch\",\n    \"CancelWorldExportJob\": \"robomaker:CancelWorldExportJob\",\n    \"CancelWorldGenerationJob\": \"robomaker:CancelWorldGenerationJob\",\n    \"CreateDeploymentJob\": \"robomaker:CreateDeploymentJob\",\n    \"CreateFleet\": \"robomaker:CreateFleet\",\n    \"CreateRobot\": \"robomaker:CreateRobot\",\n    \"CreateRobotApplication\": \"robomaker:CreateRobotApplication\",\n    \"CreateRobotApplicationVersion\": \"robomaker:CreateRobotApplicationVersion\",\n    \"CreateSimulationApplication\": \"robomaker:CreateSimulationApplication\",\n    \"CreateSimulationApplicationVersion\": \"robomaker:CreateSimulationApplicationVersion\",\n    \"CreateSimulationJob\": \"robomaker:CreateSimulationJob\",\n    \"CreateWorldExportJob\": \"robomaker:CreateWorldExportJob\",\n    \"CreateWorldGenerationJob\": \"robomaker:CreateWorldGenerationJob\",\n    \"CreateWorldTemplate\": \"robomaker:CreateWorldTemplate\",\n    \"DeleteFleet\": \"robomaker:DeleteFleet\",\n    \"DeleteRobot\": \"robomaker:DeleteRobot\",\n    \"DeleteRobotApplication\": \"robomaker:DeleteRobotApplication\",\n    \"DeleteSimulationApplication\": \"robomaker:DeleteSimulationApplication\",\n    \"DeleteWorldTemplate\": \"robomaker:DeleteWorldTemplate\",\n    \"DeregisterRobot\": \"robomaker:DeregisterRobot\",\n    \"DescribeDeploymentJob\": \"robomaker:DescribeDeploymentJob\",\n    \"DescribeFleet\": \"robomaker:DescribeFleet\",\n    \"DescribeRobot\": \"robomaker:DescribeRobot\",\n    \"DescribeRobotApplication\": \"robomaker:DescribeRobotApplication\",\n    \"DescribeSimulationApplication\": \"robomaker:DescribeSimulationApplication\",\n    \"DescribeSimulationJob\": \"robomaker:DescribeSimulationJob\",\n    \"DescribeSimulationJobBatch\": \"robomaker:DescribeSimulationJobBatch\",\n    \"DescribeWorld\": \"robomaker:DescribeWorld\",\n    \"DescribeWorldExportJob\": \"robomaker:DescribeWorldExportJob\",\n    \"DescribeWorldGenerationJob\": \"robomaker:DescribeWorldGenerationJob\",\n    \"DescribeWorldTemplate\": \"robomaker:DescribeWorldTemplate\",\n    \"GetWorldTemplateBody\": \"robomaker:GetWorldTemplateBody\",\n    \"ListDeploymentJobs\": \"robomaker:ListDeploymentJobs\",\n    \"ListFleets\": \"robomaker:ListFleets\",\n    \"ListRobotApplications\": \"robomaker:ListRobotApplications\",\n    \"ListRobots\": \"robomaker:ListRobots\",\n    \"ListSimulationApplications\": \"robomaker:ListSimulationApplications\",\n    \"ListSimulationJobBatches\": \"robomaker:ListSimulationJobBatches\",\n    \"ListSimulationJobs\": \"robomaker:ListSimulationJobs\",\n    \"ListTagsForResource\": \"robomaker:ListTagsForResource\",\n    \"ListWorldExportJobs\": \"robomaker:ListWorldExportJobs\",\n    \"ListWorldGenerationJobs\": \"robomaker:ListWorldGenerationJobs\",\n    \"ListWorldTemplates\": \"robomaker:ListWorldTemplates\",\n    \"ListWorlds\": \"robomaker:ListWorlds\",\n    \"RegisterRobot\": \"robomaker:RegisterRobot\",\n    \"RestartSimulationJob\": \"robomaker:RestartSimulationJob\",\n    \"StartSimulationJobBatch\": \"robomaker:StartSimulationJobBatch\",\n    \"SyncDeploymentJob\": \"robomaker:SyncDeploymentJob\",\n    \"TagResource\": \"robomaker:TagResource\",\n    \"UntagResource\": \"robomaker:UntagResource\",\n    \"UpdateRobotApplication\": \"robomaker:UpdateRobotApplication\",\n    \"UpdateSimulationApplication\": \"robomaker:UpdateSimulationApplication\",\n    \"UpdateWorldTemplate\": \"robomaker:UpdateWorldTemplate\"\n  },\n  \"route53\": {\n    \"ActivateKeySigningKey\": \"route53:ActivateKeySigningKey\",\n    \"AssociateVPCWithHostedZone\": \"route53:AssociateVPCWithHostedZone\",\n    \"ChangeResourceRecordSets\": \"route53:ChangeResourceRecordSets\",\n    \"ChangeTagsForResource\": \"route53:ChangeTagsForResource\",\n    \"CreateHealthCheck\": \"route53:CreateHealthCheck\",\n    \"CreateHostedZone\": \"route53:CreateHostedZone\",\n    \"CreateKeySigningKey\": \"route53:CreateKeySigningKey\",\n    \"CreateQueryLoggingConfig\": \"route53:CreateQueryLoggingConfig\",\n    \"CreateReusableDelegationSet\": \"route53:CreateReusableDelegationSet\",\n    \"CreateTrafficPolicy\": \"route53:CreateTrafficPolicy\",\n    \"CreateTrafficPolicyInstance\": \"route53:CreateTrafficPolicyInstance\",\n    \"CreateTrafficPolicyVersion\": \"route53:CreateTrafficPolicyVersion\",\n    \"CreateVPCAssociationAuthorization\": \"route53:CreateVPCAssociationAuthorization\",\n    \"DeactivateKeySigningKey\": \"route53:DeactivateKeySigningKey\",\n    \"DeleteHealthCheck\": \"route53:DeleteHealthCheck\",\n    \"DeleteHostedZone\": \"route53:DeleteHostedZone\",\n    \"DeleteKeySigningKey\": \"route53:DeleteKeySigningKey\",\n    \"DeleteQueryLoggingConfig\": \"route53:DeleteQueryLoggingConfig\",\n    \"DeleteReusableDelegationSet\": \"route53:DeleteReusableDelegationSet\",\n    \"DeleteTrafficPolicy\": \"route53:DeleteTrafficPolicy\",\n    \"DeleteTrafficPolicyInstance\": \"route53:DeleteTrafficPolicyInstance\",\n    \"DeleteVPCAssociationAuthorization\": \"route53:DeleteVPCAssociationAuthorization\",\n    \"DisableHostedZoneDNSSEC\": \"route53:DisableHostedZoneDNSSEC\",\n    \"DisassociateVPCFromHostedZone\": \"route53:DisassociateVPCFromHostedZone\",\n    \"EnableHostedZoneDNSSEC\": \"route53:EnableHostedZoneDNSSEC\",\n    \"GetAccountLimit\": \"route53:GetAccountLimit\",\n    \"GetChange\": \"route53:GetChange\",\n    \"GetCheckerIpRanges\": \"route53:GetCheckerIpRanges\",\n    \"GetDNSSEC\": \"route53:GetDNSSEC\",\n    \"GetGeoLocation\": \"route53:GetGeoLocation\",\n    \"GetHealthCheck\": \"route53:GetHealthCheck\",\n    \"GetHealthCheckCount\": \"route53:GetHealthCheckCount\",\n    \"GetHealthCheckLastFailureReason\": \"route53:GetHealthCheckLastFailureReason\",\n    \"GetHealthCheckStatus\": \"route53:GetHealthCheckStatus\",\n    \"GetHostedZone\": \"route53:GetHostedZone\",\n    \"GetHostedZoneCount\": \"route53:GetHostedZoneCount\",\n    \"GetHostedZoneLimit\": \"route53:GetHostedZoneLimit\",\n    \"GetQueryLoggingConfig\": \"route53:GetQueryLoggingConfig\",\n    \"GetReusableDelegationSet\": \"route53:GetReusableDelegationSet\",\n    \"GetReusableDelegationSetLimit\": \"route53:GetReusableDelegationSetLimit\",\n    \"GetTrafficPolicy\": \"route53:GetTrafficPolicy\",\n    \"GetTrafficPolicyInstance\": \"route53:GetTrafficPolicyInstance\",\n    \"GetTrafficPolicyInstanceCount\": \"route53:GetTrafficPolicyInstanceCount\",\n    \"ListGeoLocations\": \"route53:ListGeoLocations\",\n    \"ListHealthChecks\": \"route53:ListHealthChecks\",\n    \"ListHostedZones\": \"route53:ListHostedZones\",\n    \"ListHostedZonesByName\": \"route53:ListHostedZonesByName\",\n    \"ListHostedZonesByVPC\": \"route53:ListHostedZonesByVPC\",\n    \"ListQueryLoggingConfigs\": \"route53:ListQueryLoggingConfigs\",\n    \"ListResourceRecordSets\": \"route53:ListResourceRecordSets\",\n    \"ListReusableDelegationSets\": \"route53:ListReusableDelegationSets\",\n    \"ListTagsForResource\": \"route53:ListTagsForResource\",\n    \"ListTagsForResources\": \"route53:ListTagsForResources\",\n    \"ListTrafficPolicies\": \"route53:ListTrafficPolicies\",\n    \"ListTrafficPolicyInstances\": \"route53:ListTrafficPolicyInstances\",\n    \"ListTrafficPolicyInstancesByHostedZone\": \"route53:ListTrafficPolicyInstancesByHostedZone\",\n    \"ListTrafficPolicyInstancesByPolicy\": \"route53:ListTrafficPolicyInstancesByPolicy\",\n    \"ListTrafficPolicyVersions\": \"route53:ListTrafficPolicyVersions\",\n    \"ListVPCAssociationAuthorizations\": \"route53:ListVPCAssociationAuthorizations\",\n    \"TestDNSAnswer\": \"route53:TestDNSAnswer\",\n    \"UpdateHealthCheck\": \"route53:UpdateHealthCheck\",\n    \"UpdateHostedZoneComment\": \"route53:UpdateHostedZoneComment\",\n    \"UpdateTrafficPolicyComment\": \"route53:UpdateTrafficPolicyComment\",\n    \"UpdateTrafficPolicyInstance\": \"route53:UpdateTrafficPolicyInstance\"\n  },\n  \"route53-recovery-cluster\": {\n    \"GetRoutingControlState\": \"route53-recovery-cluster:GetRoutingControlState\",\n    \"UpdateRoutingControlState\": \"route53-recovery-cluster:UpdateRoutingControlState\",\n    \"UpdateRoutingControlStates\": \"route53-recovery-cluster:UpdateRoutingControlStates\"\n  },\n  \"route53-recovery-control-config\": {\n    \"CreateCluster\": \"route53-recovery-control-config:CreateCluster\",\n    \"CreateControlPanel\": \"route53-recovery-control-config:CreateControlPanel\",\n    \"CreateRoutingControl\": \"route53-recovery-control-config:CreateRoutingControl\",\n    \"CreateSafetyRule\": \"route53-recovery-control-config:CreateSafetyRule\",\n    \"DeleteCluster\": \"route53-recovery-control-config:DeleteCluster\",\n    \"DeleteControlPanel\": \"route53-recovery-control-config:DeleteControlPanel\",\n    \"DeleteRoutingControl\": \"route53-recovery-control-config:DeleteRoutingControl\",\n    \"DeleteSafetyRule\": \"route53-recovery-control-config:DeleteSafetyRule\",\n    \"DescribeCluster\": \"route53-recovery-control-config:DescribeCluster\",\n    \"DescribeControlPanel\": \"route53-recovery-control-config:DescribeControlPanel\",\n    \"DescribeRoutingControl\": \"route53-recovery-control-config:DescribeRoutingControl\",\n    \"DescribeSafetyRule\": \"route53-recovery-control-config:DescribeSafetyRule\",\n    \"ListAssociatedRoute53HealthChecks\": \"route53-recovery-control-config:ListAssociatedRoute53HealthChecks\",\n    \"ListClusters\": \"route53-recovery-control-config:ListClusters\",\n    \"ListControlPanels\": \"route53-recovery-control-config:ListControlPanels\",\n    \"ListRoutingControls\": \"route53-recovery-control-config:ListRoutingControls\",\n    \"ListSafetyRules\": \"route53-recovery-control-config:ListSafetyRules\",\n    \"ListTagsForResource\": \"route53-recovery-control-config:ListTagsForResource\",\n    \"TagResource\": \"route53-recovery-control-config:TagResource\",\n    \"UntagResource\": \"route53-recovery-control-config:UntagResource\",\n    \"UpdateControlPanel\": \"route53-recovery-control-config:UpdateControlPanel\",\n    \"UpdateRoutingControl\": \"route53-recovery-control-config:UpdateRoutingControl\",\n    \"UpdateSafetyRule\": \"route53-recovery-control-config:UpdateSafetyRule\"\n  },\n  \"route53-recovery-readiness\": {\n    \"CreateCell\": \"route53-recovery-readiness:CreateCell\",\n    \"CreateCrossAccountAuthorization\": \"route53-recovery-readiness:CreateCrossAccountAuthorization\",\n    \"CreateReadinessCheck\": \"route53-recovery-readiness:CreateReadinessCheck\",\n    \"CreateRecoveryGroup\": \"route53-recovery-readiness:CreateRecoveryGroup\",\n    \"CreateResourceSet\": \"route53-recovery-readiness:CreateResourceSet\",\n    \"DeleteCell\": \"route53-recovery-readiness:DeleteCell\",\n    \"DeleteCrossAccountAuthorization\": \"route53-recovery-readiness:DeleteCrossAccountAuthorization\",\n    \"DeleteReadinessCheck\": \"route53-recovery-readiness:DeleteReadinessCheck\",\n    \"DeleteRecoveryGroup\": \"route53-recovery-readiness:DeleteRecoveryGroup\",\n    \"DeleteResourceSet\": \"route53-recovery-readiness:DeleteResourceSet\",\n    \"GetArchitectureRecommendations\": \"route53-recovery-readiness:GetArchitectureRecommendations\",\n    \"GetCell\": \"route53-recovery-readiness:GetCell\",\n    \"GetCellReadinessSummary\": \"route53-recovery-readiness:GetCellReadinessSummary\",\n    \"GetReadinessCheck\": \"route53-recovery-readiness:GetReadinessCheck\",\n    \"GetReadinessCheckResourceStatus\": \"route53-recovery-readiness:GetReadinessCheckResourceStatus\",\n    \"GetReadinessCheckStatus\": \"route53-recovery-readiness:GetReadinessCheckStatus\",\n    \"GetRecoveryGroup\": \"route53-recovery-readiness:GetRecoveryGroup\",\n    \"GetRecoveryGroupReadinessSummary\": \"route53-recovery-readiness:GetRecoveryGroupReadinessSummary\",\n    \"GetResourceSet\": \"route53-recovery-readiness:GetResourceSet\",\n    \"ListCells\": \"route53-recovery-readiness:ListCells\",\n    \"ListCrossAccountAuthorizations\": \"route53-recovery-readiness:ListCrossAccountAuthorizations\",\n    \"ListReadinessChecks\": \"route53-recovery-readiness:ListReadinessChecks\",\n    \"ListRecoveryGroups\": \"route53-recovery-readiness:ListRecoveryGroups\",\n    \"ListResourceSets\": \"route53-recovery-readiness:ListResourceSets\",\n    \"ListRules\": \"route53-recovery-readiness:ListRules\",\n    \"ListTagsForResources\": \"route53-recovery-readiness:ListTagsForResources\",\n    \"TagResource\": \"route53-recovery-readiness:TagResource\",\n    \"UntagResource\": \"route53-recovery-readiness:UntagResource\",\n    \"UpdateCell\": \"route53-recovery-readiness:UpdateCell\",\n    \"UpdateReadinessCheck\": \"route53-recovery-readiness:UpdateReadinessCheck\",\n    \"UpdateRecoveryGroup\": \"route53-recovery-readiness:UpdateRecoveryGroup\",\n    \"UpdateResourceSet\": \"route53-recovery-readiness:UpdateResourceSet\"\n  },\n  \"route53domains\": {\n    \"AcceptDomainTransferFromAnotherAwsAccount\": \"route53domains:AcceptDomainTransferFromAnotherAwsAccount\",\n    \"CancelDomainTransferToAnotherAwsAccount\": \"route53domains:CancelDomainTransferToAnotherAwsAccount\",\n    \"CheckDomainAvailability\": \"route53domains:CheckDomainAvailability\",\n    \"CheckDomainTransferability\": \"route53domains:CheckDomainTransferability\",\n    \"DeleteDomain\": \"route53domains:DeleteDomain\",\n    \"DeleteTagsForDomain\": \"route53domains:DeleteTagsForDomain\",\n    \"DisableDomainAutoRenew\": \"route53domains:DisableDomainAutoRenew\",\n    \"DisableDomainTransferLock\": \"route53domains:DisableDomainTransferLock\",\n    \"EnableDomainAutoRenew\": \"route53domains:EnableDomainAutoRenew\",\n    \"EnableDomainTransferLock\": \"route53domains:EnableDomainTransferLock\",\n    \"GetContactReachabilityStatus\": \"route53domains:GetContactReachabilityStatus\",\n    \"GetDomainDetail\": \"route53domains:GetDomainDetail\",\n    \"GetDomainSuggestions\": \"route53domains:GetDomainSuggestions\",\n    \"GetOperationDetail\": \"route53domains:GetOperationDetail\",\n    \"ListDomains\": \"route53domains:ListDomains\",\n    \"ListOperations\": \"route53domains:ListOperations\",\n    \"ListPrices\": \"route53domains:ListPrices\",\n    \"ListTagsForDomain\": \"route53domains:ListTagsForDomain\",\n    \"RegisterDomain\": \"route53domains:RegisterDomain\",\n    \"RejectDomainTransferFromAnotherAwsAccount\": \"route53domains:RejectDomainTransferFromAnotherAwsAccount\",\n    \"RenewDomain\": \"route53domains:RenewDomain\",\n    \"ResendContactReachabilityEmail\": \"route53domains:ResendContactReachabilityEmail\",\n    \"RetrieveDomainAuthCode\": \"route53domains:RetrieveDomainAuthCode\",\n    \"TransferDomain\": \"route53domains:TransferDomain\",\n    \"TransferDomainToAnotherAwsAccount\": \"route53domains:TransferDomainToAnotherAwsAccount\",\n    \"UpdateDomainContact\": \"route53domains:UpdateDomainContact\",\n    \"UpdateDomainContactPrivacy\": \"route53domains:UpdateDomainContactPrivacy\",\n    \"UpdateDomainNameservers\": \"route53domains:UpdateDomainNameservers\",\n    \"UpdateTagsForDomain\": \"route53domains:UpdateTagsForDomain\",\n    \"ViewBilling\": \"route53domains:ViewBilling\"\n  },\n  \"route53resolver\": {\n    \"AssociateFirewallRuleGroup\": \"route53resolver:AssociateFirewallRuleGroup\",\n    \"AssociateResolverEndpointIpAddress\": \"route53resolver:AssociateResolverEndpointIpAddress\",\n    \"AssociateResolverQueryLogConfig\": \"route53resolver:AssociateResolverQueryLogConfig\",\n    \"AssociateResolverRule\": \"route53resolver:AssociateResolverRule\",\n    \"CreateFirewallDomainList\": \"route53resolver:CreateFirewallDomainList\",\n    \"CreateFirewallRule\": \"route53resolver:CreateFirewallRule\",\n    \"CreateFirewallRuleGroup\": \"route53resolver:CreateFirewallRuleGroup\",\n    \"CreateResolverEndpoint\": \"route53resolver:CreateResolverEndpoint\",\n    \"CreateResolverQueryLogConfig\": \"route53resolver:CreateResolverQueryLogConfig\",\n    \"CreateResolverRule\": \"route53resolver:CreateResolverRule\",\n    \"DeleteFirewallDomainList\": \"route53resolver:DeleteFirewallDomainList\",\n    \"DeleteFirewallRule\": \"route53resolver:DeleteFirewallRule\",\n    \"DeleteFirewallRuleGroup\": \"route53resolver:DeleteFirewallRuleGroup\",\n    \"DeleteResolverEndpoint\": \"route53resolver:DeleteResolverEndpoint\",\n    \"DeleteResolverQueryLogConfig\": \"route53resolver:DeleteResolverQueryLogConfig\",\n    \"DeleteResolverRule\": \"route53resolver:DeleteResolverRule\",\n    \"DisassociateFirewallRuleGroup\": \"route53resolver:DisassociateFirewallRuleGroup\",\n    \"DisassociateResolverEndpointIpAddress\": \"route53resolver:DisassociateResolverEndpointIpAddress\",\n    \"DisassociateResolverQueryLogConfig\": \"route53resolver:DisassociateResolverQueryLogConfig\",\n    \"DisassociateResolverRule\": \"route53resolver:DisassociateResolverRule\",\n    \"GetFirewallConfig\": \"route53resolver:GetFirewallConfig\",\n    \"GetFirewallDomainList\": \"route53resolver:GetFirewallDomainList\",\n    \"GetFirewallRuleGroup\": \"route53resolver:GetFirewallRuleGroup\",\n    \"GetFirewallRuleGroupAssociation\": \"route53resolver:GetFirewallRuleGroupAssociation\",\n    \"GetFirewallRuleGroupPolicy\": \"route53resolver:GetFirewallRuleGroupPolicy\",\n    \"GetResolverConfig\": \"route53resolver:GetResolverConfig\",\n    \"GetResolverDnssecConfig\": \"route53resolver:GetResolverDnssecConfig\",\n    \"GetResolverEndpoint\": \"route53resolver:GetResolverEndpoint\",\n    \"GetResolverQueryLogConfig\": \"route53resolver:GetResolverQueryLogConfig\",\n    \"GetResolverQueryLogConfigAssociation\": \"route53resolver:GetResolverQueryLogConfigAssociation\",\n    \"GetResolverQueryLogConfigPolicy\": \"route53resolver:GetResolverQueryLogConfigPolicy\",\n    \"GetResolverRule\": \"route53resolver:GetResolverRule\",\n    \"GetResolverRuleAssociation\": \"route53resolver:GetResolverRuleAssociation\",\n    \"GetResolverRulePolicy\": \"route53resolver:GetResolverRulePolicy\",\n    \"ImportFirewallDomains\": \"route53resolver:ImportFirewallDomains\",\n    \"ListFirewallConfigs\": \"route53resolver:ListFirewallConfigs\",\n    \"ListFirewallDomainLists\": \"route53resolver:ListFirewallDomainLists\",\n    \"ListFirewallDomains\": \"route53resolver:ListFirewallDomains\",\n    \"ListFirewallRuleGroupAssociations\": \"route53resolver:ListFirewallRuleGroupAssociations\",\n    \"ListFirewallRuleGroups\": \"route53resolver:ListFirewallRuleGroups\",\n    \"ListFirewallRules\": \"route53resolver:ListFirewallRules\",\n    \"ListResolverConfigs\": \"route53resolver:ListResolverConfigs\",\n    \"ListResolverDnssecConfigs\": \"route53resolver:ListResolverDnssecConfigs\",\n    \"ListResolverEndpointIpAddresses\": \"route53resolver:ListResolverEndpointIpAddresses\",\n    \"ListResolverEndpoints\": \"route53resolver:ListResolverEndpoints\",\n    \"ListResolverQueryLogConfigAssociations\": \"route53resolver:ListResolverQueryLogConfigAssociations\",\n    \"ListResolverQueryLogConfigs\": \"route53resolver:ListResolverQueryLogConfigs\",\n    \"ListResolverRuleAssociations\": \"route53resolver:ListResolverRuleAssociations\",\n    \"ListResolverRules\": \"route53resolver:ListResolverRules\",\n    \"ListTagsForResource\": \"route53resolver:ListTagsForResource\",\n    \"PutFirewallRuleGroupPolicy\": \"route53resolver:PutFirewallRuleGroupPolicy\",\n    \"PutResolverQueryLogConfigPolicy\": \"route53resolver:PutResolverQueryLogConfigPolicy\",\n    \"PutResolverRulePolicy\": \"route53resolver:PutResolverRulePolicy\",\n    \"TagResource\": \"route53resolver:TagResource\",\n    \"UntagResource\": \"route53resolver:UntagResource\",\n    \"UpdateFirewallConfig\": \"route53resolver:UpdateFirewallConfig\",\n    \"UpdateFirewallDomains\": \"route53resolver:UpdateFirewallDomains\",\n    \"UpdateFirewallRule\": \"route53resolver:UpdateFirewallRule\",\n    \"UpdateFirewallRuleGroupAssociation\": \"route53resolver:UpdateFirewallRuleGroupAssociation\",\n    \"UpdateResolverConfig\": \"route53resolver:UpdateResolverConfig\",\n    \"UpdateResolverDnssecConfig\": \"route53resolver:UpdateResolverDnssecConfig\",\n    \"UpdateResolverEndpoint\": \"route53resolver:UpdateResolverEndpoint\",\n    \"UpdateResolverRule\": \"route53resolver:UpdateResolverRule\"\n  },\n  \"rum\": {\n    \"CreateAppMonitor\": \"rum:CreateAppMonitor\",\n    \"DeleteAppMonitor\": \"rum:DeleteAppMonitor\",\n    \"GetAppMonitor\": \"rum:GetAppMonitor\",\n    \"GetAppMonitorData\": \"rum:GetAppMonitorData\",\n    \"ListAppMonitors\": \"rum:ListAppMonitors\",\n    \"ListTagsForResource\": \"rum:ListTagsForResource\",\n    \"PutRumEvents\": \"rum:PutRumEvents\",\n    \"TagResource\": \"rum:TagResource\",\n    \"UntagResource\": \"rum:UntagResource\",\n    \"UpdateAppMonitor\": \"rum:UpdateAppMonitor\"\n  },\n  \"s3\": {\n    \"AbortMultipartUpload\": \"s3:AbortMultipartUpload\",\n    \"CompleteMultipartUpload\": \"s3:PutObject\",\n    \"CreateBucket\": \"s3:CreateBucket\",\n    \"CreateMultipartUpload\": \"s3:PutObject\",\n    \"DeleteBucket\": \"s3:DeleteBucket\",\n    \"DeleteBucketPolicy\": \"s3:DeleteBucketPolicy\",\n    \"DeleteBucketWebsite\": \"s3:DeleteBucketWebsite\",\n    \"DeleteObject\": \"s3:DeleteObject\",\n    \"DeleteObjectTagging\": \"s3:DeleteObjectTagging\",\n    \"GetBucketAcl\": \"s3:GetBucketAcl\",\n    \"GetBucketLocation\": \"s3:GetBucketLocation\",\n    \"GetBucketLogging\": \"s3:GetBucketLogging\",\n    \"GetBucketNotification\": \"s3:GetBucketNotification\",\n    \"GetBucketOwnershipControls\": \"s3:GetBucketOwnershipControls\",\n    \"GetBucketPolicy\": \"s3:GetBucketPolicy\",\n    \"GetBucketPolicyStatus\": \"s3:GetBucketPolicyStatus\",\n    \"GetBucketRequestPayment\": \"s3:GetBucketRequestPayment\",\n    \"GetBucketTagging\": \"s3:GetBucketTagging\",\n    \"GetBucketVersioning\": \"s3:GetBucketVersioning\",\n    \"GetBucketWebsite\": \"s3:GetBucketWebsite\",\n    \"GetObject\": \"s3:GetObject\",\n    \"GetObjectAcl\": \"s3:GetObjectAcl\",\n    \"GetObjectLegalHold\": \"s3:GetObjectLegalHold\",\n    \"GetObjectRetention\": \"s3:GetObjectRetention\",\n    \"GetObjectTagging\": \"s3:GetObjectTagging\",\n    \"GetObjectTorrent\": \"s3:GetObjectTorrent\",\n    \"HeadObject\": \"s3:GetObject\",\n    \"ListBuckets\": \"s3:ListAllMyBuckets\",\n    \"ListObjects\": \"s3:ListBucket\",\n    \"ListObjectsV2\": \"s3:ListBucket\",\n    \"PutBucketAcl\": \"s3:PutBucketAcl\",\n    \"PutBucketLogging\": \"s3:PutBucketLogging\",\n    \"PutBucketNotification\": \"s3:PutBucketNotification\",\n    \"PutBucketOwnershipControls\": \"s3:PutBucketOwnershipControls\",\n    \"PutBucketPolicy\": \"s3:PutBucketPolicy\",\n    \"PutBucketRequestPayment\": \"s3:PutBucketRequestPayment\",\n    \"PutBucketTagging\": \"s3:PutBucketTagging\",\n    \"PutBucketVersioning\": \"s3:PutBucketVersioning\",\n    \"PutBucketWebsite\": \"s3:PutBucketWebsite\",\n    \"PutObject\": \"s3:PutObject\",\n    \"PutObjectAcl\": \"s3:PutObjectAcl\",\n    \"PutObjectLegalHold\": \"s3:PutObjectLegalHold\",\n    \"PutObjectRetention\": \"s3:PutObjectRetention\",\n    \"PutObjectTagging\": \"s3:PutObjectTagging\",\n    \"RestoreObject\": \"s3:RestoreObject\",\n    \"UploadPart\": \"s3:PutObject\"\n  },\n  \"sagemaker\": {\n    \"AddAssociation\": \"sagemaker:AddAssociation\",\n    \"AddTags\": \"sagemaker:AddTags\",\n    \"AssociateTrialComponent\": \"sagemaker:AssociateTrialComponent\",\n    \"BatchDescribeModelPackage\": \"sagemaker:BatchDescribeModelPackage\",\n    \"CreateAction\": \"sagemaker:CreateAction\",\n    \"CreateAlgorithm\": \"sagemaker:CreateAlgorithm\",\n    \"CreateApp\": \"sagemaker:CreateApp\",\n    \"CreateAppImageConfig\": \"sagemaker:CreateAppImageConfig\",\n    \"CreateArtifact\": \"sagemaker:CreateArtifact\",\n    \"CreateAutoMLJob\": \"sagemaker:CreateAutoMLJob\",\n    \"CreateCodeRepository\": \"sagemaker:CreateCodeRepository\",\n    \"CreateCompilationJob\": \"sagemaker:CreateCompilationJob\",\n    \"CreateContext\": \"sagemaker:CreateContext\",\n    \"CreateDataQualityJobDefinition\": \"sagemaker:CreateDataQualityJobDefinition\",\n    \"CreateDeviceFleet\": \"sagemaker:CreateDeviceFleet\",\n    \"CreateDomain\": \"sagemaker:CreateDomain\",\n    \"CreateEdgePackagingJob\": \"sagemaker:CreateEdgePackagingJob\",\n    \"CreateEndpoint\": \"sagemaker:CreateEndpoint\",\n    \"CreateEndpointConfig\": \"sagemaker:CreateEndpointConfig\",\n    \"CreateExperiment\": \"sagemaker:CreateExperiment\",\n    \"CreateFeatureGroup\": \"sagemaker:CreateFeatureGroup\",\n    \"CreateFlowDefinition\": \"sagemaker:CreateFlowDefinition\",\n    \"CreateHumanTaskUi\": \"sagemaker:CreateHumanTaskUi\",\n    \"CreateHyperParameterTuningJob\": \"sagemaker:CreateHyperParameterTuningJob\",\n    \"CreateImage\": \"sagemaker:CreateImage\",\n    \"CreateImageVersion\": \"sagemaker:CreateImageVersion\",\n    \"CreateInferenceRecommendationsJob\": \"sagemaker:CreateInferenceRecommendationsJob\",\n    \"CreateLabelingJob\": \"sagemaker:CreateLabelingJob\",\n    \"CreateModel\": \"sagemaker:CreateModel\",\n    \"CreateModelBiasJobDefinition\": \"sagemaker:CreateModelBiasJobDefinition\",\n    \"CreateModelExplainabilityJobDefinition\": \"sagemaker:CreateModelExplainabilityJobDefinition\",\n    \"CreateModelPackage\": \"sagemaker:CreateModelPackage\",\n    \"CreateModelPackageGroup\": \"sagemaker:CreateModelPackageGroup\",\n    \"CreateModelQualityJobDefinition\": \"sagemaker:CreateModelQualityJobDefinition\",\n    \"CreateMonitoringSchedule\": \"sagemaker:CreateMonitoringSchedule\",\n    \"CreateNotebookInstance\": \"sagemaker:CreateNotebookInstance\",\n    \"CreateNotebookInstanceLifecycleConfig\": \"sagemaker:CreateNotebookInstanceLifecycleConfig\",\n    \"CreatePipeline\": \"sagemaker:CreatePipeline\",\n    \"CreatePresignedDomainUrl\": \"sagemaker:CreatePresignedDomainUrl\",\n    \"CreatePresignedNotebookInstanceUrl\": \"sagemaker:CreatePresignedNotebookInstanceUrl\",\n    \"CreateProcessingJob\": \"sagemaker:CreateProcessingJob\",\n    \"CreateProject\": \"sagemaker:CreateProject\",\n    \"CreateTrainingJob\": \"sagemaker:CreateTrainingJob\",\n    \"CreateTransformJob\": \"sagemaker:CreateTransformJob\",\n    \"CreateTrial\": \"sagemaker:CreateTrial\",\n    \"CreateTrialComponent\": \"sagemaker:CreateTrialComponent\",\n    \"CreateUserProfile\": \"sagemaker:CreateUserProfile\",\n    \"CreateWorkforce\": \"sagemaker:CreateWorkforce\",\n    \"CreateWorkteam\": \"sagemaker:CreateWorkteam\",\n    \"DeleteAction\": \"sagemaker:DeleteAction\",\n    \"DeleteAlgorithm\": \"sagemaker:DeleteAlgorithm\",\n    \"DeleteApp\": \"sagemaker:DeleteApp\",\n    \"DeleteAppImageConfig\": \"sagemaker:DeleteAppImageConfig\",\n    \"DeleteArtifact\": \"sagemaker:DeleteArtifact\",\n    \"DeleteAssociation\": \"sagemaker:DeleteAssociation\",\n    \"DeleteCodeRepository\": \"sagemaker:DeleteCodeRepository\",\n    \"DeleteContext\": \"sagemaker:DeleteContext\",\n    \"DeleteDataQualityJobDefinition\": \"sagemaker:DeleteDataQualityJobDefinition\",\n    \"DeleteDeviceFleet\": \"sagemaker:DeleteDeviceFleet\",\n    \"DeleteDomain\": \"sagemaker:DeleteDomain\",\n    \"DeleteEndpoint\": \"sagemaker:DeleteEndpoint\",\n    \"DeleteEndpointConfig\": \"sagemaker:DeleteEndpointConfig\",\n    \"DeleteExperiment\": \"sagemaker:DeleteExperiment\",\n    \"DeleteFeatureGroup\": \"sagemaker:DeleteFeatureGroup\",\n    \"DeleteFlowDefinition\": \"sagemaker:DeleteFlowDefinition\",\n    \"DeleteHumanTaskUi\": \"sagemaker:DeleteHumanTaskUi\",\n    \"DeleteImage\": \"sagemaker:DeleteImage\",\n    \"DeleteImageVersion\": \"sagemaker:DeleteImageVersion\",\n    \"DeleteModel\": \"sagemaker:DeleteModel\",\n    \"DeleteModelBiasJobDefinition\": \"sagemaker:DeleteModelBiasJobDefinition\",\n    \"DeleteModelExplainabilityJobDefinition\": \"sagemaker:DeleteModelExplainabilityJobDefinition\",\n    \"DeleteModelPackage\": \"sagemaker:DeleteModelPackage\",\n    \"DeleteModelPackageGroup\": \"sagemaker:DeleteModelPackageGroup\",\n    \"DeleteModelPackageGroupPolicy\": \"sagemaker:DeleteModelPackageGroupPolicy\",\n    \"DeleteModelQualityJobDefinition\": \"sagemaker:DeleteModelQualityJobDefinition\",\n    \"DeleteMonitoringSchedule\": \"sagemaker:DeleteMonitoringSchedule\",\n    \"DeleteNotebookInstance\": \"sagemaker:DeleteNotebookInstance\",\n    \"DeleteNotebookInstanceLifecycleConfig\": \"sagemaker:DeleteNotebookInstanceLifecycleConfig\",\n    \"DeletePipeline\": \"sagemaker:DeletePipeline\",\n    \"DeleteProject\": \"sagemaker:DeleteProject\",\n    \"DeleteTags\": \"sagemaker:DeleteTags\",\n    \"DeleteTrial\": \"sagemaker:DeleteTrial\",\n    \"DeleteTrialComponent\": \"sagemaker:DeleteTrialComponent\",\n    \"DeleteUserProfile\": \"sagemaker:DeleteUserProfile\",\n    \"DeleteWorkforce\": \"sagemaker:DeleteWorkforce\",\n    \"DeleteWorkteam\": \"sagemaker:DeleteWorkteam\",\n    \"DeregisterDevices\": \"sagemaker:DeregisterDevices\",\n    \"DescribeAction\": \"sagemaker:DescribeAction\",\n    \"DescribeAlgorithm\": \"sagemaker:DescribeAlgorithm\",\n    \"DescribeApp\": \"sagemaker:DescribeApp\",\n    \"DescribeAppImageConfig\": \"sagemaker:DescribeAppImageConfig\",\n    \"DescribeArtifact\": \"sagemaker:DescribeArtifact\",\n    \"DescribeAutoMLJob\": \"sagemaker:DescribeAutoMLJob\",\n    \"DescribeCodeRepository\": \"sagemaker:DescribeCodeRepository\",\n    \"DescribeCompilationJob\": \"sagemaker:DescribeCompilationJob\",\n    \"DescribeContext\": \"sagemaker:DescribeContext\",\n    \"DescribeDataQualityJobDefinition\": \"sagemaker:DescribeDataQualityJobDefinition\",\n    \"DescribeDevice\": \"sagemaker:DescribeDevice\",\n    \"DescribeDeviceFleet\": \"sagemaker:DescribeDeviceFleet\",\n    \"DescribeDomain\": \"sagemaker:DescribeDomain\",\n    \"DescribeEdgePackagingJob\": \"sagemaker:DescribeEdgePackagingJob\",\n    \"DescribeEndpoint\": \"sagemaker:DescribeEndpoint\",\n    \"DescribeEndpointConfig\": \"sagemaker:DescribeEndpointConfig\",\n    \"DescribeExperiment\": \"sagemaker:DescribeExperiment\",\n    \"DescribeFeatureGroup\": \"sagemaker:DescribeFeatureGroup\",\n    \"DescribeFlowDefinition\": \"sagemaker:DescribeFlowDefinition\",\n    \"DescribeHumanTaskUi\": \"sagemaker:DescribeHumanTaskUi\",\n    \"DescribeHyperParameterTuningJob\": \"sagemaker:DescribeHyperParameterTuningJob\",\n    \"DescribeImage\": \"sagemaker:DescribeImage\",\n    \"DescribeImageVersion\": \"sagemaker:DescribeImageVersion\",\n    \"DescribeInferenceRecommendationsJob\": \"sagemaker:DescribeInferenceRecommendationsJob\",\n    \"DescribeLabelingJob\": \"sagemaker:DescribeLabelingJob\",\n    \"DescribeLineageGroup\": \"sagemaker:DescribeLineageGroup\",\n    \"DescribeModel\": \"sagemaker:DescribeModel\",\n    \"DescribeModelBiasJobDefinition\": \"sagemaker:DescribeModelBiasJobDefinition\",\n    \"DescribeModelExplainabilityJobDefinition\": \"sagemaker:DescribeModelExplainabilityJobDefinition\",\n    \"DescribeModelPackage\": \"sagemaker:DescribeModelPackage\",\n    \"DescribeModelPackageGroup\": \"sagemaker:DescribeModelPackageGroup\",\n    \"DescribeModelQualityJobDefinition\": \"sagemaker:DescribeModelQualityJobDefinition\",\n    \"DescribeMonitoringSchedule\": \"sagemaker:DescribeMonitoringSchedule\",\n    \"DescribeNotebookInstance\": \"sagemaker:DescribeNotebookInstance\",\n    \"DescribeNotebookInstanceLifecycleConfig\": \"sagemaker:DescribeNotebookInstanceLifecycleConfig\",\n    \"DescribePipeline\": \"sagemaker:DescribePipeline\",\n    \"DescribePipelineDefinitionForExecution\": \"sagemaker:DescribePipelineDefinitionForExecution\",\n    \"DescribePipelineExecution\": \"sagemaker:DescribePipelineExecution\",\n    \"DescribeProcessingJob\": \"sagemaker:DescribeProcessingJob\",\n    \"DescribeProject\": \"sagemaker:DescribeProject\",\n    \"DescribeSubscribedWorkteam\": \"sagemaker:DescribeSubscribedWorkteam\",\n    \"DescribeTrainingJob\": \"sagemaker:DescribeTrainingJob\",\n    \"DescribeTransformJob\": \"sagemaker:DescribeTransformJob\",\n    \"DescribeTrial\": \"sagemaker:DescribeTrial\",\n    \"DescribeTrialComponent\": \"sagemaker:DescribeTrialComponent\",\n    \"DescribeUserProfile\": \"sagemaker:DescribeUserProfile\",\n    \"DescribeWorkforce\": \"sagemaker:DescribeWorkforce\",\n    \"DescribeWorkteam\": \"sagemaker:DescribeWorkteam\",\n    \"DisableSagemakerServicecatalogPortfolio\": \"sagemaker:DisableSagemakerServicecatalogPortfolio\",\n    \"DisassociateTrialComponent\": \"sagemaker:DisassociateTrialComponent\",\n    \"EnableSagemakerServicecatalogPortfolio\": \"sagemaker:EnableSagemakerServicecatalogPortfolio\",\n    \"GetDeviceFleetReport\": \"sagemaker:GetDeviceFleetReport\",\n    \"GetLineageGroupPolicy\": \"sagemaker:GetLineageGroupPolicy\",\n    \"GetModelPackageGroupPolicy\": \"sagemaker:GetModelPackageGroupPolicy\",\n    \"GetSagemakerServicecatalogPortfolioStatus\": \"sagemaker:GetSagemakerServicecatalogPortfolioStatus\",\n    \"GetSearchSuggestions\": \"sagemaker:GetSearchSuggestions\",\n    \"ListActions\": \"sagemaker:ListActions\",\n    \"ListAlgorithms\": \"sagemaker:ListAlgorithms\",\n    \"ListAppImageConfigs\": \"sagemaker:ListAppImageConfigs\",\n    \"ListApps\": \"sagemaker:ListApps\",\n    \"ListArtifacts\": \"sagemaker:ListArtifacts\",\n    \"ListAssociations\": \"sagemaker:ListAssociations\",\n    \"ListAutoMLJobs\": \"sagemaker:ListAutoMLJobs\",\n    \"ListCandidatesForAutoMLJob\": \"sagemaker:ListCandidatesForAutoMLJob\",\n    \"ListCodeRepositories\": \"sagemaker:ListCodeRepositories\",\n    \"ListCompilationJobs\": \"sagemaker:ListCompilationJobs\",\n    \"ListContexts\": \"sagemaker:ListContexts\",\n    \"ListDataQualityJobDefinitions\": \"sagemaker:ListDataQualityJobDefinitions\",\n    \"ListDeviceFleets\": \"sagemaker:ListDeviceFleets\",\n    \"ListDevices\": \"sagemaker:ListDevices\",\n    \"ListDomains\": \"sagemaker:ListDomains\",\n    \"ListEdgePackagingJobs\": \"sagemaker:ListEdgePackagingJobs\",\n    \"ListEndpointConfigs\": \"sagemaker:ListEndpointConfigs\",\n    \"ListEndpoints\": \"sagemaker:ListEndpoints\",\n    \"ListExperiments\": \"sagemaker:ListExperiments\",\n    \"ListFeatureGroups\": \"sagemaker:ListFeatureGroups\",\n    \"ListFlowDefinitions\": \"sagemaker:ListFlowDefinitions\",\n    \"ListHumanTaskUis\": \"sagemaker:ListHumanTaskUis\",\n    \"ListHyperParameterTuningJobs\": \"sagemaker:ListHyperParameterTuningJobs\",\n    \"ListImageVersions\": \"sagemaker:ListImageVersions\",\n    \"ListImages\": \"sagemaker:ListImages\",\n    \"ListInferenceRecommendationsJobs\": \"sagemaker:ListInferenceRecommendationsJobs\",\n    \"ListLabelingJobs\": \"sagemaker:ListLabelingJobs\",\n    \"ListLabelingJobsForWorkteam\": \"sagemaker:ListLabelingJobsForWorkteam\",\n    \"ListLineageGroups\": \"sagemaker:ListLineageGroups\",\n    \"ListModelBiasJobDefinitions\": \"sagemaker:ListModelBiasJobDefinitions\",\n    \"ListModelExplainabilityJobDefinitions\": \"sagemaker:ListModelExplainabilityJobDefinitions\",\n    \"ListModelMetadata\": \"sagemaker:ListModelMetadata\",\n    \"ListModelPackageGroups\": \"sagemaker:ListModelPackageGroups\",\n    \"ListModelPackages\": \"sagemaker:ListModelPackages\",\n    \"ListModelQualityJobDefinitions\": \"sagemaker:ListModelQualityJobDefinitions\",\n    \"ListModels\": \"sagemaker:ListModels\",\n    \"ListMonitoringExecutions\": \"sagemaker:ListMonitoringExecutions\",\n    \"ListMonitoringSchedules\": \"sagemaker:ListMonitoringSchedules\",\n    \"ListNotebookInstanceLifecycleConfigs\": \"sagemaker:ListNotebookInstanceLifecycleConfigs\",\n    \"ListNotebookInstances\": \"sagemaker:ListNotebookInstances\",\n    \"ListPipelineExecutionSteps\": \"sagemaker:ListPipelineExecutionSteps\",\n    \"ListPipelineExecutions\": \"sagemaker:ListPipelineExecutions\",\n    \"ListPipelineParametersForExecution\": \"sagemaker:ListPipelineParametersForExecution\",\n    \"ListPipelines\": \"sagemaker:ListPipelines\",\n    \"ListProcessingJobs\": \"sagemaker:ListProcessingJobs\",\n    \"ListProjects\": \"sagemaker:ListProjects\",\n    \"ListSubscribedWorkteams\": \"sagemaker:ListSubscribedWorkteams\",\n    \"ListTags\": \"sagemaker:ListTags\",\n    \"ListTrainingJobs\": \"sagemaker:ListTrainingJobs\",\n    \"ListTrainingJobsForHyperParameterTuningJob\": \"sagemaker:ListTrainingJobsForHyperParameterTuningJob\",\n    \"ListTransformJobs\": \"sagemaker:ListTransformJobs\",\n    \"ListTrialComponents\": \"sagemaker:ListTrialComponents\",\n    \"ListTrials\": \"sagemaker:ListTrials\",\n    \"ListUserProfiles\": \"sagemaker:ListUserProfiles\",\n    \"ListWorkforces\": \"sagemaker:ListWorkforces\",\n    \"ListWorkteams\": \"sagemaker:ListWorkteams\",\n    \"PutModelPackageGroupPolicy\": \"sagemaker:PutModelPackageGroupPolicy\",\n    \"QueryLineage\": \"sagemaker:QueryLineage\",\n    \"RegisterDevices\": \"sagemaker:RegisterDevices\",\n    \"RenderUiTemplate\": \"sagemaker:RenderUiTemplate\",\n    \"Search\": \"sagemaker:Search\",\n    \"SendPipelineExecutionStepFailure\": \"sagemaker:SendPipelineExecutionStepFailure\",\n    \"SendPipelineExecutionStepSuccess\": \"sagemaker:SendPipelineExecutionStepSuccess\",\n    \"StartMonitoringSchedule\": \"sagemaker:StartMonitoringSchedule\",\n    \"StartNotebookInstance\": \"sagemaker:StartNotebookInstance\",\n    \"StartPipelineExecution\": \"sagemaker:StartPipelineExecution\",\n    \"StopAutoMLJob\": \"sagemaker:StopAutoMLJob\",\n    \"StopCompilationJob\": \"sagemaker:StopCompilationJob\",\n    \"StopEdgePackagingJob\": \"sagemaker:StopEdgePackagingJob\",\n    \"StopHyperParameterTuningJob\": \"sagemaker:StopHyperParameterTuningJob\",\n    \"StopInferenceRecommendationsJob\": \"sagemaker:StopInferenceRecommendationsJob\",\n    \"StopLabelingJob\": \"sagemaker:StopLabelingJob\",\n    \"StopMonitoringSchedule\": \"sagemaker:StopMonitoringSchedule\",\n    \"StopNotebookInstance\": \"sagemaker:StopNotebookInstance\",\n    \"StopPipelineExecution\": \"sagemaker:StopPipelineExecution\",\n    \"StopProcessingJob\": \"sagemaker:StopProcessingJob\",\n    \"StopTrainingJob\": \"sagemaker:StopTrainingJob\",\n    \"StopTransformJob\": \"sagemaker:StopTransformJob\",\n    \"UpdateAction\": \"sagemaker:UpdateAction\",\n    \"UpdateAppImageConfig\": \"sagemaker:UpdateAppImageConfig\",\n    \"UpdateArtifact\": \"sagemaker:UpdateArtifact\",\n    \"UpdateCodeRepository\": \"sagemaker:UpdateCodeRepository\",\n    \"UpdateContext\": \"sagemaker:UpdateContext\",\n    \"UpdateDeviceFleet\": \"sagemaker:UpdateDeviceFleet\",\n    \"UpdateDevices\": \"sagemaker:UpdateDevices\",\n    \"UpdateDomain\": \"sagemaker:UpdateDomain\",\n    \"UpdateEndpoint\": \"sagemaker:UpdateEndpoint\",\n    \"UpdateEndpointWeightsAndCapacities\": \"sagemaker:UpdateEndpointWeightsAndCapacities\",\n    \"UpdateExperiment\": \"sagemaker:UpdateExperiment\",\n    \"UpdateImage\": \"sagemaker:UpdateImage\",\n    \"UpdateModelPackage\": \"sagemaker:UpdateModelPackage\",\n    \"UpdateMonitoringSchedule\": \"sagemaker:UpdateMonitoringSchedule\",\n    \"UpdateNotebookInstance\": \"sagemaker:UpdateNotebookInstance\",\n    \"UpdateNotebookInstanceLifecycleConfig\": \"sagemaker:UpdateNotebookInstanceLifecycleConfig\",\n    \"UpdatePipeline\": \"sagemaker:UpdatePipeline\",\n    \"UpdatePipelineExecution\": \"sagemaker:UpdatePipelineExecution\",\n    \"UpdateProject\": \"sagemaker:UpdateProject\",\n    \"UpdateTrainingJob\": \"sagemaker:UpdateTrainingJob\",\n    \"UpdateTrial\": \"sagemaker:UpdateTrial\",\n    \"UpdateTrialComponent\": \"sagemaker:UpdateTrialComponent\",\n    \"UpdateUserProfile\": \"sagemaker:UpdateUserProfile\",\n    \"UpdateWorkforce\": \"sagemaker:UpdateWorkforce\",\n    \"UpdateWorkteam\": \"sagemaker:UpdateWorkteam\"\n  },\n  \"sagemaker-runtime\": {\n    \"InvokeEndpoint\": \"sagemaker:InvokeEndpoint\",\n    \"InvokeEndpointAsync\": \"sagemaker:InvokeEndpointAsync\"\n  },\n  \"savingsplans\": {\n    \"CreateSavingsPlan\": \"savingsplans:CreateSavingsPlan\",\n    \"DeleteQueuedSavingsPlan\": \"savingsplans:DeleteQueuedSavingsPlan\",\n    \"DescribeSavingsPlanRates\": \"savingsplans:DescribeSavingsPlanRates\",\n    \"DescribeSavingsPlans\": \"savingsplans:DescribeSavingsPlans\",\n    \"DescribeSavingsPlansOfferingRates\": \"savingsplans:DescribeSavingsPlansOfferingRates\",\n    \"DescribeSavingsPlansOfferings\": \"savingsplans:DescribeSavingsPlansOfferings\",\n    \"ListTagsForResource\": \"savingsplans:ListTagsForResource\",\n    \"TagResource\": \"savingsplans:TagResource\",\n    \"UntagResource\": \"savingsplans:UntagResource\"\n  },\n  \"schemas\": {\n    \"CreateDiscoverer\": \"schemas:CreateDiscoverer\",\n    \"CreateRegistry\": \"schemas:CreateRegistry\",\n    \"CreateSchema\": \"schemas:CreateSchema\",\n    \"DeleteDiscoverer\": \"schemas:DeleteDiscoverer\",\n    \"DeleteRegistry\": \"schemas:DeleteRegistry\",\n    \"DeleteResourcePolicy\": \"schemas:DeleteResourcePolicy\",\n    \"DeleteSchema\": \"schemas:DeleteSchema\",\n    \"DeleteSchemaVersion\": \"schemas:DeleteSchemaVersion\",\n    \"DescribeCodeBinding\": \"schemas:DescribeCodeBinding\",\n    \"DescribeDiscoverer\": \"schemas:DescribeDiscoverer\",\n    \"DescribeRegistry\": \"schemas:DescribeRegistry\",\n    \"DescribeSchema\": \"schemas:DescribeSchema\",\n    \"ExportSchema\": \"schemas:ExportSchema\",\n    \"GetCodeBindingSource\": \"schemas:GetCodeBindingSource\",\n    \"GetDiscoveredSchema\": \"schemas:GetDiscoveredSchema\",\n    \"GetResourcePolicy\": \"schemas:GetResourcePolicy\",\n    \"ListDiscoverers\": \"schemas:ListDiscoverers\",\n    \"ListRegistries\": \"schemas:ListRegistries\",\n    \"ListSchemaVersions\": \"schemas:ListSchemaVersions\",\n    \"ListSchemas\": \"schemas:ListSchemas\",\n    \"ListTagsForResource\": \"schemas:ListTagsForResource\",\n    \"PutCodeBinding\": \"schemas:PutCodeBinding\",\n    \"PutResourcePolicy\": \"schemas:PutResourcePolicy\",\n    \"SearchSchemas\": \"schemas:SearchSchemas\",\n    \"StartDiscoverer\": \"schemas:StartDiscoverer\",\n    \"StopDiscoverer\": \"schemas:StopDiscoverer\",\n    \"TagResource\": \"schemas:TagResource\",\n    \"UntagResource\": \"schemas:UntagResource\",\n    \"UpdateDiscoverer\": \"schemas:UpdateDiscoverer\",\n    \"UpdateRegistry\": \"schemas:UpdateRegistry\",\n    \"UpdateSchema\": \"schemas:UpdateSchema\"\n  },\n  \"sdb\": {\n    \"BatchDeleteAttributes\": \"sdb:BatchDeleteAttributes\",\n    \"BatchPutAttributes\": \"sdb:BatchPutAttributes\",\n    \"CreateDomain\": \"sdb:CreateDomain\",\n    \"DeleteAttributes\": \"sdb:DeleteAttributes\",\n    \"DeleteDomain\": \"sdb:DeleteDomain\",\n    \"DomainMetadata\": \"sdb:DomainMetadata\",\n    \"GetAttributes\": \"sdb:GetAttributes\",\n    \"ListDomains\": \"sdb:ListDomains\",\n    \"PutAttributes\": \"sdb:PutAttributes\",\n    \"Select\": \"sdb:Select\"\n  },\n  \"secretsmanager\": {\n    \"CancelRotateSecret\": \"secretsmanager:CancelRotateSecret\",\n    \"CreateSecret\": \"secretsmanager:CreateSecret\",\n    \"DeleteResourcePolicy\": \"secretsmanager:DeleteResourcePolicy\",\n    \"DeleteSecret\": \"secretsmanager:DeleteSecret\",\n    \"DescribeSecret\": \"secretsmanager:DescribeSecret\",\n    \"GetRandomPassword\": \"secretsmanager:GetRandomPassword\",\n    \"GetResourcePolicy\": \"secretsmanager:GetResourcePolicy\",\n    \"GetSecretValue\": \"secretsmanager:GetSecretValue\",\n    \"ListSecretVersionIds\": \"secretsmanager:ListSecretVersionIds\",\n    \"ListSecrets\": \"secretsmanager:ListSecrets\",\n    \"PutResourcePolicy\": \"secretsmanager:PutResourcePolicy\",\n    \"PutSecretValue\": \"secretsmanager:PutSecretValue\",\n    \"RemoveRegionsFromReplication\": \"secretsmanager:RemoveRegionsFromReplication\",\n    \"ReplicateSecretToRegions\": \"secretsmanager:ReplicateSecretToRegions\",\n    \"RestoreSecret\": \"secretsmanager:RestoreSecret\",\n    \"RotateSecret\": \"secretsmanager:RotateSecret\",\n    \"StopReplicationToReplica\": \"secretsmanager:StopReplicationToReplica\",\n    \"TagResource\": \"secretsmanager:TagResource\",\n    \"UntagResource\": \"secretsmanager:UntagResource\",\n    \"UpdateSecret\": \"secretsmanager:UpdateSecret\",\n    \"UpdateSecretVersionStage\": \"secretsmanager:UpdateSecretVersionStage\",\n    \"ValidateResourcePolicy\": \"secretsmanager:ValidateResourcePolicy\"\n  },\n  \"securityhub\": {\n    \"AcceptAdministratorInvitation\": \"securityhub:AcceptAdministratorInvitation\",\n    \"AcceptInvitation\": \"securityhub:AcceptInvitation\",\n    \"BatchDisableStandards\": \"securityhub:BatchDisableStandards\",\n    \"BatchEnableStandards\": \"securityhub:BatchEnableStandards\",\n    \"BatchImportFindings\": \"securityhub:BatchImportFindings\",\n    \"BatchUpdateFindings\": \"securityhub:BatchUpdateFindings\",\n    \"CreateActionTarget\": \"securityhub:CreateActionTarget\",\n    \"CreateFindingAggregator\": \"securityhub:CreateFindingAggregator\",\n    \"CreateInsight\": \"securityhub:CreateInsight\",\n    \"CreateMembers\": \"securityhub:CreateMembers\",\n    \"DeclineInvitations\": \"securityhub:DeclineInvitations\",\n    \"DeleteActionTarget\": \"securityhub:DeleteActionTarget\",\n    \"DeleteFindingAggregator\": \"securityhub:DeleteFindingAggregator\",\n    \"DeleteInsight\": \"securityhub:DeleteInsight\",\n    \"DeleteInvitations\": \"securityhub:DeleteInvitations\",\n    \"DeleteMembers\": \"securityhub:DeleteMembers\",\n    \"DescribeActionTargets\": \"securityhub:DescribeActionTargets\",\n    \"DescribeHub\": \"securityhub:DescribeHub\",\n    \"DescribeOrganizationConfiguration\": \"securityhub:DescribeOrganizationConfiguration\",\n    \"DescribeProducts\": \"securityhub:DescribeProducts\",\n    \"DescribeStandards\": \"securityhub:DescribeStandards\",\n    \"DescribeStandardsControls\": \"securityhub:DescribeStandardsControls\",\n    \"DisableImportFindingsForProduct\": \"securityhub:DisableImportFindingsForProduct\",\n    \"DisableOrganizationAdminAccount\": \"securityhub:DisableOrganizationAdminAccount\",\n    \"DisableSecurityHub\": \"securityhub:DisableSecurityHub\",\n    \"DisassociateFromAdministratorAccount\": \"securityhub:DisassociateFromAdministratorAccount\",\n    \"DisassociateFromMasterAccount\": \"securityhub:DisassociateFromMasterAccount\",\n    \"DisassociateMembers\": \"securityhub:DisassociateMembers\",\n    \"EnableImportFindingsForProduct\": \"securityhub:EnableImportFindingsForProduct\",\n    \"EnableOrganizationAdminAccount\": \"securityhub:EnableOrganizationAdminAccount\",\n    \"EnableSecurityHub\": \"securityhub:EnableSecurityHub\",\n    \"GetAdministratorAccount\": \"securityhub:GetAdministratorAccount\",\n    \"GetEnabledStandards\": \"securityhub:GetEnabledStandards\",\n    \"GetFindingAggregator\": \"securityhub:GetFindingAggregator\",\n    \"GetFindings\": \"securityhub:GetFindings\",\n    \"GetInsightResults\": \"securityhub:GetInsightResults\",\n    \"GetInsights\": \"securityhub:GetInsights\",\n    \"GetInvitationsCount\": \"securityhub:GetInvitationsCount\",\n    \"GetMasterAccount\": \"securityhub:GetMasterAccount\",\n    \"GetMembers\": \"securityhub:GetMembers\",\n    \"InviteMembers\": \"securityhub:InviteMembers\",\n    \"ListEnabledProductsForImport\": \"securityhub:ListEnabledProductsForImport\",\n    \"ListFindingAggregators\": \"securityhub:ListFindingAggregators\",\n    \"ListInvitations\": \"securityhub:ListInvitations\",\n    \"ListMembers\": \"securityhub:ListMembers\",\n    \"ListOrganizationAdminAccounts\": \"securityhub:ListOrganizationAdminAccounts\",\n    \"ListTagsForResource\": \"securityhub:ListTagsForResource\",\n    \"TagResource\": \"securityhub:TagResource\",\n    \"UntagResource\": \"securityhub:UntagResource\",\n    \"UpdateActionTarget\": \"securityhub:UpdateActionTarget\",\n    \"UpdateFindingAggregator\": \"securityhub:UpdateFindingAggregator\",\n    \"UpdateFindings\": \"securityhub:UpdateFindings\",\n    \"UpdateInsight\": \"securityhub:UpdateInsight\",\n    \"UpdateOrganizationConfiguration\": \"securityhub:UpdateOrganizationConfiguration\",\n    \"UpdateSecurityHubConfiguration\": \"securityhub:UpdateSecurityHubConfiguration\",\n    \"UpdateStandardsControl\": \"securityhub:UpdateStandardsControl\"\n  },\n  \"servicecatalog\": {\n    \"AcceptPortfolioShare\": \"servicecatalog:AcceptPortfolioShare\",\n    \"AssociateBudgetWithResource\": \"servicecatalog:AssociateBudgetWithResource\",\n    \"AssociatePrincipalWithPortfolio\": \"servicecatalog:AssociatePrincipalWithPortfolio\",\n    \"AssociateProductWithPortfolio\": \"servicecatalog:AssociateProductWithPortfolio\",\n    \"AssociateServiceActionWithProvisioningArtifact\": \"servicecatalog:AssociateServiceActionWithProvisioningArtifact\",\n    \"AssociateTagOptionWithResource\": \"servicecatalog:AssociateTagOptionWithResource\",\n    \"BatchAssociateServiceActionWithProvisioningArtifact\": \"servicecatalog:BatchAssociateServiceActionWithProvisioningArtifact\",\n    \"BatchDisassociateServiceActionFromProvisioningArtifact\": \"servicecatalog:BatchDisassociateServiceActionFromProvisioningArtifact\",\n    \"CopyProduct\": \"servicecatalog:CopyProduct\",\n    \"CreateConstraint\": \"servicecatalog:CreateConstraint\",\n    \"CreatePortfolio\": \"servicecatalog:CreatePortfolio\",\n    \"CreatePortfolioShare\": \"servicecatalog:CreatePortfolioShare\",\n    \"CreateProduct\": \"servicecatalog:CreateProduct\",\n    \"CreateProvisionedProductPlan\": \"servicecatalog:CreateProvisionedProductPlan\",\n    \"CreateProvisioningArtifact\": \"servicecatalog:CreateProvisioningArtifact\",\n    \"CreateServiceAction\": \"servicecatalog:CreateServiceAction\",\n    \"CreateTagOption\": \"servicecatalog:CreateTagOption\",\n    \"DeleteConstraint\": \"servicecatalog:DeleteConstraint\",\n    \"DeletePortfolio\": \"servicecatalog:DeletePortfolio\",\n    \"DeletePortfolioShare\": \"servicecatalog:DeletePortfolioShare\",\n    \"DeleteProduct\": \"servicecatalog:DeleteProduct\",\n    \"DeleteProvisionedProductPlan\": \"servicecatalog:DeleteProvisionedProductPlan\",\n    \"DeleteProvisioningArtifact\": \"servicecatalog:DeleteProvisioningArtifact\",\n    \"DeleteServiceAction\": \"servicecatalog:DeleteServiceAction\",\n    \"DeleteTagOption\": \"servicecatalog:DeleteTagOption\",\n    \"DescribeConstraint\": \"servicecatalog:DescribeConstraint\",\n    \"DescribeCopyProductStatus\": \"servicecatalog:DescribeCopyProductStatus\",\n    \"DescribePortfolio\": \"servicecatalog:DescribePortfolio\",\n    \"DescribePortfolioShareStatus\": \"servicecatalog:DescribePortfolioShareStatus\",\n    \"DescribePortfolioShares\": \"servicecatalog:DescribePortfolioShares\",\n    \"DescribeProduct\": \"servicecatalog:DescribeProduct\",\n    \"DescribeProductAsAdmin\": \"servicecatalog:DescribeProductAsAdmin\",\n    \"DescribeProductView\": \"servicecatalog:DescribeProductView\",\n    \"DescribeProvisionedProduct\": \"servicecatalog:DescribeProvisionedProduct\",\n    \"DescribeProvisionedProductPlan\": \"servicecatalog:DescribeProvisionedProductPlan\",\n    \"DescribeProvisioningArtifact\": \"servicecatalog:DescribeProvisioningArtifact\",\n    \"DescribeProvisioningParameters\": \"servicecatalog:DescribeProvisioningParameters\",\n    \"DescribeRecord\": \"servicecatalog:DescribeRecord\",\n    \"DescribeServiceAction\": \"servicecatalog:DescribeServiceAction\",\n    \"DescribeServiceActionExecutionParameters\": \"servicecatalog:DescribeServiceActionExecutionParameters\",\n    \"DescribeTagOption\": \"servicecatalog:DescribeTagOption\",\n    \"DisableAWSOrganizationsAccess\": \"servicecatalog:DisableAWSOrganizationsAccess\",\n    \"DisassociateBudgetFromResource\": \"servicecatalog:DisassociateBudgetFromResource\",\n    \"DisassociatePrincipalFromPortfolio\": \"servicecatalog:DisassociatePrincipalFromPortfolio\",\n    \"DisassociateProductFromPortfolio\": \"servicecatalog:DisassociateProductFromPortfolio\",\n    \"DisassociateServiceActionFromProvisioningArtifact\": \"servicecatalog:DisassociateServiceActionFromProvisioningArtifact\",\n    \"DisassociateTagOptionFromResource\": \"servicecatalog:DisassociateTagOptionFromResource\",\n    \"EnableAWSOrganizationsAccess\": \"servicecatalog:EnableAWSOrganizationsAccess\",\n    \"ExecuteProvisionedProductPlan\": \"servicecatalog:ExecuteProvisionedProductPlan\",\n    \"ExecuteProvisionedProductServiceAction\": \"servicecatalog:ExecuteProvisionedProductServiceAction\",\n    \"GetAWSOrganizationsAccessStatus\": \"servicecatalog:GetAWSOrganizationsAccessStatus\",\n    \"GetProvisionedProductOutputs\": \"servicecatalog:GetProvisionedProductOutputs\",\n    \"ImportAsProvisionedProduct\": \"servicecatalog:ImportAsProvisionedProduct\",\n    \"ListAcceptedPortfolioShares\": \"servicecatalog:ListAcceptedPortfolioShares\",\n    \"ListBudgetsForResource\": \"servicecatalog:ListBudgetsForResource\",\n    \"ListConstraintsForPortfolio\": \"servicecatalog:ListConstraintsForPortfolio\",\n    \"ListLaunchPaths\": \"servicecatalog:ListLaunchPaths\",\n    \"ListOrganizationPortfolioAccess\": \"servicecatalog:ListOrganizationPortfolioAccess\",\n    \"ListPortfolioAccess\": \"servicecatalog:ListPortfolioAccess\",\n    \"ListPortfolios\": \"servicecatalog:ListPortfolios\",\n    \"ListPortfoliosForProduct\": \"servicecatalog:ListPortfoliosForProduct\",\n    \"ListPrincipalsForPortfolio\": \"servicecatalog:ListPrincipalsForPortfolio\",\n    \"ListProvisionedProductPlans\": \"servicecatalog:ListProvisionedProductPlans\",\n    \"ListProvisioningArtifacts\": \"servicecatalog:ListProvisioningArtifacts\",\n    \"ListProvisioningArtifactsForServiceAction\": \"servicecatalog:ListProvisioningArtifactsForServiceAction\",\n    \"ListRecordHistory\": \"servicecatalog:ListRecordHistory\",\n    \"ListResourcesForTagOption\": \"servicecatalog:ListResourcesForTagOption\",\n    \"ListServiceActions\": \"servicecatalog:ListServiceActions\",\n    \"ListServiceActionsForProvisioningArtifact\": \"servicecatalog:ListServiceActionsForProvisioningArtifact\",\n    \"ListStackInstancesForProvisionedProduct\": \"servicecatalog:ListStackInstancesForProvisionedProduct\",\n    \"ListTagOptions\": \"servicecatalog:ListTagOptions\",\n    \"ProvisionProduct\": \"servicecatalog:ProvisionProduct\",\n    \"RejectPortfolioShare\": \"servicecatalog:RejectPortfolioShare\",\n    \"ScanProvisionedProducts\": \"servicecatalog:ScanProvisionedProducts\",\n    \"SearchProducts\": \"servicecatalog:SearchProducts\",\n    \"SearchProductsAsAdmin\": \"servicecatalog:SearchProductsAsAdmin\",\n    \"SearchProvisionedProducts\": \"servicecatalog:SearchProvisionedProducts\",\n    \"TerminateProvisionedProduct\": \"servicecatalog:TerminateProvisionedProduct\",\n    \"UpdateConstraint\": \"servicecatalog:UpdateConstraint\",\n    \"UpdatePortfolio\": \"servicecatalog:UpdatePortfolio\",\n    \"UpdatePortfolioShare\": \"servicecatalog:UpdatePortfolioShare\",\n    \"UpdateProduct\": \"servicecatalog:UpdateProduct\",\n    \"UpdateProvisionedProduct\": \"servicecatalog:UpdateProvisionedProduct\",\n    \"UpdateProvisionedProductProperties\": \"servicecatalog:UpdateProvisionedProductProperties\",\n    \"UpdateProvisioningArtifact\": \"servicecatalog:UpdateProvisioningArtifact\",\n    \"UpdateServiceAction\": \"servicecatalog:UpdateServiceAction\",\n    \"UpdateTagOption\": \"servicecatalog:UpdateTagOption\"\n  },\n  \"ses\": {\n    \"CloneReceiptRuleSet\": \"ses:CloneReceiptRuleSet\",\n    \"CreateConfigurationSet\": \"ses:CreateConfigurationSet\",\n    \"CreateConfigurationSetEventDestination\": \"ses:CreateConfigurationSetEventDestination\",\n    \"CreateConfigurationSetTrackingOptions\": \"ses:CreateConfigurationSetTrackingOptions\",\n    \"CreateCustomVerificationEmailTemplate\": \"ses:CreateCustomVerificationEmailTemplate\",\n    \"CreateReceiptFilter\": \"ses:CreateReceiptFilter\",\n    \"CreateReceiptRule\": \"ses:CreateReceiptRule\",\n    \"CreateReceiptRuleSet\": \"ses:CreateReceiptRuleSet\",\n    \"CreateTemplate\": \"ses:CreateTemplate\",\n    \"DeleteConfigurationSet\": \"ses:DeleteConfigurationSet\",\n    \"DeleteConfigurationSetEventDestination\": \"ses:DeleteConfigurationSetEventDestination\",\n    \"DeleteConfigurationSetTrackingOptions\": \"ses:DeleteConfigurationSetTrackingOptions\",\n    \"DeleteCustomVerificationEmailTemplate\": \"ses:DeleteCustomVerificationEmailTemplate\",\n    \"DeleteIdentity\": \"ses:DeleteIdentity\",\n    \"DeleteIdentityPolicy\": \"ses:DeleteIdentityPolicy\",\n    \"DeleteReceiptFilter\": \"ses:DeleteReceiptFilter\",\n    \"DeleteReceiptRule\": \"ses:DeleteReceiptRule\",\n    \"DeleteReceiptRuleSet\": \"ses:DeleteReceiptRuleSet\",\n    \"DeleteTemplate\": \"ses:DeleteTemplate\",\n    \"DeleteVerifiedEmailAddress\": \"ses:DeleteVerifiedEmailAddress\",\n    \"DescribeActiveReceiptRuleSet\": \"ses:DescribeActiveReceiptRuleSet\",\n    \"DescribeConfigurationSet\": \"ses:DescribeConfigurationSet\",\n    \"DescribeReceiptRule\": \"ses:DescribeReceiptRule\",\n    \"DescribeReceiptRuleSet\": \"ses:DescribeReceiptRuleSet\",\n    \"GetAccountSendingEnabled\": \"ses:GetAccountSendingEnabled\",\n    \"GetCustomVerificationEmailTemplate\": \"ses:GetCustomVerificationEmailTemplate\",\n    \"GetIdentityDkimAttributes\": \"ses:GetIdentityDkimAttributes\",\n    \"GetIdentityMailFromDomainAttributes\": \"ses:GetIdentityMailFromDomainAttributes\",\n    \"GetIdentityNotificationAttributes\": \"ses:GetIdentityNotificationAttributes\",\n    \"GetIdentityPolicies\": \"ses:GetIdentityPolicies\",\n    \"GetIdentityVerificationAttributes\": \"ses:GetIdentityVerificationAttributes\",\n    \"GetSendQuota\": \"ses:GetSendQuota\",\n    \"GetSendStatistics\": \"ses:GetSendStatistics\",\n    \"GetTemplate\": \"ses:GetTemplate\",\n    \"ListConfigurationSets\": \"ses:ListConfigurationSets\",\n    \"ListCustomVerificationEmailTemplates\": \"ses:ListCustomVerificationEmailTemplates\",\n    \"ListIdentities\": \"ses:ListIdentities\",\n    \"ListIdentityPolicies\": \"ses:ListIdentityPolicies\",\n    \"ListReceiptFilters\": \"ses:ListReceiptFilters\",\n    \"ListReceiptRuleSets\": \"ses:ListReceiptRuleSets\",\n    \"ListTemplates\": \"ses:ListTemplates\",\n    \"ListVerifiedEmailAddresses\": \"ses:ListVerifiedEmailAddresses\",\n    \"PutConfigurationSetDeliveryOptions\": \"ses:PutConfigurationSetDeliveryOptions\",\n    \"PutIdentityPolicy\": \"ses:PutIdentityPolicy\",\n    \"ReorderReceiptRuleSet\": \"ses:ReorderReceiptRuleSet\",\n    \"SendBounce\": \"ses:SendBounce\",\n    \"SendBulkTemplatedEmail\": \"ses:SendBulkTemplatedEmail\",\n    \"SendCustomVerificationEmail\": \"ses:SendCustomVerificationEmail\",\n    \"SendEmail\": \"ses:SendEmail\",\n    \"SendRawEmail\": \"ses:SendRawEmail\",\n    \"SendTemplatedEmail\": \"ses:SendTemplatedEmail\",\n    \"SetActiveReceiptRuleSet\": \"ses:SetActiveReceiptRuleSet\",\n    \"SetIdentityDkimEnabled\": \"ses:SetIdentityDkimEnabled\",\n    \"SetIdentityFeedbackForwardingEnabled\": \"ses:SetIdentityFeedbackForwardingEnabled\",\n    \"SetIdentityHeadersInNotificationsEnabled\": \"ses:SetIdentityHeadersInNotificationsEnabled\",\n    \"SetIdentityMailFromDomain\": \"ses:SetIdentityMailFromDomain\",\n    \"SetIdentityNotificationTopic\": \"ses:SetIdentityNotificationTopic\",\n    \"SetReceiptRulePosition\": \"ses:SetReceiptRulePosition\",\n    \"TestRenderTemplate\": \"ses:TestRenderTemplate\",\n    \"UpdateAccountSendingEnabled\": \"ses:UpdateAccountSendingEnabled\",\n    \"UpdateConfigurationSetEventDestination\": \"ses:UpdateConfigurationSetEventDestination\",\n    \"UpdateConfigurationSetReputationMetricsEnabled\": \"ses:UpdateConfigurationSetReputationMetricsEnabled\",\n    \"UpdateConfigurationSetSendingEnabled\": \"ses:UpdateConfigurationSetSendingEnabled\",\n    \"UpdateConfigurationSetTrackingOptions\": \"ses:UpdateConfigurationSetTrackingOptions\",\n    \"UpdateCustomVerificationEmailTemplate\": \"ses:UpdateCustomVerificationEmailTemplate\",\n    \"UpdateReceiptRule\": \"ses:UpdateReceiptRule\",\n    \"UpdateTemplate\": \"ses:UpdateTemplate\",\n    \"VerifyDomainDkim\": \"ses:VerifyDomainDkim\",\n    \"VerifyDomainIdentity\": \"ses:VerifyDomainIdentity\",\n    \"VerifyEmailAddress\": \"ses:VerifyEmailAddress\",\n    \"VerifyEmailIdentity\": \"ses:VerifyEmailIdentity\"\n  },\n  \"shield\": {\n    \"AssociateDRTLogBucket\": \"shield:AssociateDRTLogBucket\",\n    \"AssociateDRTRole\": \"shield:AssociateDRTRole\",\n    \"AssociateHealthCheck\": \"shield:AssociateHealthCheck\",\n    \"AssociateProactiveEngagementDetails\": \"shield:AssociateProactiveEngagementDetails\",\n    \"CreateProtection\": \"shield:CreateProtection\",\n    \"CreateProtectionGroup\": \"shield:CreateProtectionGroup\",\n    \"CreateSubscription\": \"shield:CreateSubscription\",\n    \"DeleteProtection\": \"shield:DeleteProtection\",\n    \"DeleteProtectionGroup\": \"shield:DeleteProtectionGroup\",\n    \"DeleteSubscription\": \"shield:DeleteSubscription\",\n    \"DescribeAttack\": \"shield:DescribeAttack\",\n    \"DescribeAttackStatistics\": \"shield:DescribeAttackStatistics\",\n    \"DescribeDRTAccess\": \"shield:DescribeDRTAccess\",\n    \"DescribeEmergencyContactSettings\": \"shield:DescribeEmergencyContactSettings\",\n    \"DescribeProtection\": \"shield:DescribeProtection\",\n    \"DescribeProtectionGroup\": \"shield:DescribeProtectionGroup\",\n    \"DescribeSubscription\": \"shield:DescribeSubscription\",\n    \"DisableApplicationLayerAutomaticResponse\": \"shield:DisableApplicationLayerAutomaticResponse\",\n    \"DisableProactiveEngagement\": \"shield:DisableProactiveEngagement\",\n    \"DisassociateDRTLogBucket\": \"shield:DisassociateDRTLogBucket\",\n    \"DisassociateDRTRole\": \"shield:DisassociateDRTRole\",\n    \"DisassociateHealthCheck\": \"shield:DisassociateHealthCheck\",\n    \"EnableApplicationLayerAutomaticResponse\": \"shield:EnableApplicationLayerAutomaticResponse\",\n    \"EnableProactiveEngagement\": \"shield:EnableProactiveEngagement\",\n    \"GetSubscriptionState\": \"shield:GetSubscriptionState\",\n    \"ListAttacks\": \"shield:ListAttacks\",\n    \"ListProtectionGroups\": \"shield:ListProtectionGroups\",\n    \"ListProtections\": \"shield:ListProtections\",\n    \"ListResourcesInProtectionGroup\": \"shield:ListResourcesInProtectionGroup\",\n    \"ListTagsForResource\": \"shield:ListTagsForResource\",\n    \"TagResource\": \"shield:TagResource\",\n    \"UntagResource\": \"shield:UntagResource\",\n    \"UpdateApplicationLayerAutomaticResponse\": \"shield:UpdateApplicationLayerAutomaticResponse\",\n    \"UpdateEmergencyContactSettings\": \"shield:UpdateEmergencyContactSettings\",\n    \"UpdateProtectionGroup\": \"shield:UpdateProtectionGroup\",\n    \"UpdateSubscription\": \"shield:UpdateSubscription\"\n  },\n  \"signer\": {\n    \"AddProfilePermission\": \"signer:AddProfilePermission\",\n    \"CancelSigningProfile\": \"signer:CancelSigningProfile\",\n    \"DescribeSigningJob\": \"signer:DescribeSigningJob\",\n    \"GetSigningPlatform\": \"signer:GetSigningPlatform\",\n    \"GetSigningProfile\": \"signer:GetSigningProfile\",\n    \"ListProfilePermissions\": \"signer:ListProfilePermissions\",\n    \"ListSigningJobs\": \"signer:ListSigningJobs\",\n    \"ListSigningPlatforms\": \"signer:ListSigningPlatforms\",\n    \"ListSigningProfiles\": \"signer:ListSigningProfiles\",\n    \"ListTagsForResource\": \"signer:ListTagsForResource\",\n    \"PutSigningProfile\": \"signer:PutSigningProfile\",\n    \"RemoveProfilePermission\": \"signer:RemoveProfilePermission\",\n    \"RevokeSignature\": \"signer:RevokeSignature\",\n    \"RevokeSigningProfile\": \"signer:RevokeSigningProfile\",\n    \"StartSigningJob\": \"signer:StartSigningJob\",\n    \"TagResource\": \"signer:TagResource\",\n    \"UntagResource\": \"signer:UntagResource\"\n  },\n  \"sms-voice\": {\n    \"CreateConfigurationSet\": \"sms-voice:CreateConfigurationSet\",\n    \"CreateConfigurationSetEventDestination\": \"sms-voice:CreateConfigurationSetEventDestination\",\n    \"DeleteConfigurationSet\": \"sms-voice:DeleteConfigurationSet\",\n    \"DeleteConfigurationSetEventDestination\": \"sms-voice:DeleteConfigurationSetEventDestination\",\n    \"GetConfigurationSetEventDestinations\": \"sms-voice:GetConfigurationSetEventDestinations\",\n    \"ListConfigurationSets\": \"sms-voice:ListConfigurationSets\",\n    \"SendVoiceMessage\": \"sms-voice:SendVoiceMessage\",\n    \"UpdateConfigurationSetEventDestination\": \"sms-voice:UpdateConfigurationSetEventDestination\"\n  },\n  \"snow-device-management\": {\n    \"CancelTask\": \"snow-device-management:CancelTask\",\n    \"CreateTask\": \"snow-device-management:CreateTask\",\n    \"DescribeDevice\": \"snow-device-management:DescribeDevice\",\n    \"DescribeDeviceEc2Instances\": \"snow-device-management:DescribeDeviceEc2Instances\",\n    \"DescribeExecution\": \"snow-device-management:DescribeExecution\",\n    \"DescribeTask\": \"snow-device-management:DescribeTask\",\n    \"ListDeviceResources\": \"snow-device-management:ListDeviceResources\",\n    \"ListDevices\": \"snow-device-management:ListDevices\",\n    \"ListExecutions\": \"snow-device-management:ListExecutions\",\n    \"ListTagsForResource\": \"snow-device-management:ListTagsForResource\",\n    \"ListTasks\": \"snow-device-management:ListTasks\",\n    \"TagResource\": \"snow-device-management:TagResource\",\n    \"UntagResource\": \"snow-device-management:UntagResource\"\n  },\n  \"snowball\": {\n    \"CancelCluster\": \"snowball:CancelCluster\",\n    \"CancelJob\": \"snowball:CancelJob\",\n    \"CreateAddress\": \"snowball:CreateAddress\",\n    \"CreateCluster\": \"snowball:CreateCluster\",\n    \"CreateJob\": \"snowball:CreateJob\",\n    \"CreateLongTermPricing\": \"snowball:CreateLongTermPricing\",\n    \"CreateReturnShippingLabel\": \"snowball:CreateReturnShippingLabel\",\n    \"DescribeAddress\": \"snowball:DescribeAddress\",\n    \"DescribeAddresses\": \"snowball:DescribeAddresses\",\n    \"DescribeCluster\": \"snowball:DescribeCluster\",\n    \"DescribeJob\": \"snowball:DescribeJob\",\n    \"DescribeReturnShippingLabel\": \"snowball:DescribeReturnShippingLabel\",\n    \"GetJobManifest\": \"snowball:GetJobManifest\",\n    \"GetJobUnlockCode\": \"snowball:GetJobUnlockCode\",\n    \"GetSnowballUsage\": \"snowball:GetSnowballUsage\",\n    \"GetSoftwareUpdates\": \"snowball:GetSoftwareUpdates\",\n    \"ListClusterJobs\": \"snowball:ListClusterJobs\",\n    \"ListClusters\": \"snowball:ListClusters\",\n    \"ListCompatibleImages\": \"snowball:ListCompatibleImages\",\n    \"ListJobs\": \"snowball:ListJobs\",\n    \"ListLongTermPricing\": \"snowball:ListLongTermPricing\",\n    \"UpdateCluster\": \"snowball:UpdateCluster\",\n    \"UpdateJob\": \"snowball:UpdateJob\",\n    \"UpdateJobShipmentState\": \"snowball:UpdateJobShipmentState\",\n    \"UpdateLongTermPricing\": \"snowball:UpdateLongTermPricing\"\n  },\n  \"sns\": {\n    \"AddPermission\": \"sns:AddPermission\",\n    \"CheckIfPhoneNumberIsOptedOut\": \"sns:CheckIfPhoneNumberIsOptedOut\",\n    \"ConfirmSubscription\": \"sns:ConfirmSubscription\",\n    \"CreatePlatformApplication\": \"sns:CreatePlatformApplication\",\n    \"CreatePlatformEndpoint\": \"sns:CreatePlatformEndpoint\",\n    \"CreateSMSSandboxPhoneNumber\": \"sns:CreateSMSSandboxPhoneNumber\",\n    \"CreateTopic\": \"sns:CreateTopic\",\n    \"DeleteEndpoint\": \"sns:DeleteEndpoint\",\n    \"DeletePlatformApplication\": \"sns:DeletePlatformApplication\",\n    \"DeleteSMSSandboxPhoneNumber\": \"sns:DeleteSMSSandboxPhoneNumber\",\n    \"DeleteTopic\": \"sns:DeleteTopic\",\n    \"GetEndpointAttributes\": \"sns:GetEndpointAttributes\",\n    \"GetPlatformApplicationAttributes\": \"sns:GetPlatformApplicationAttributes\",\n    \"GetSMSAttributes\": \"sns:GetSMSAttributes\",\n    \"GetSMSSandboxAccountStatus\": \"sns:GetSMSSandboxAccountStatus\",\n    \"GetSubscriptionAttributes\": \"sns:GetSubscriptionAttributes\",\n    \"GetTopicAttributes\": \"sns:GetTopicAttributes\",\n    \"ListEndpointsByPlatformApplication\": \"sns:ListEndpointsByPlatformApplication\",\n    \"ListOriginationNumbers\": \"sns:ListOriginationNumbers\",\n    \"ListPhoneNumbersOptedOut\": \"sns:ListPhoneNumbersOptedOut\",\n    \"ListPlatformApplications\": \"sns:ListPlatformApplications\",\n    \"ListSMSSandboxPhoneNumbers\": \"sns:ListSMSSandboxPhoneNumbers\",\n    \"ListSubscriptions\": \"sns:ListSubscriptions\",\n    \"ListSubscriptionsByTopic\": \"sns:ListSubscriptionsByTopic\",\n    \"ListTagsForResource\": \"sns:ListTagsForResource\",\n    \"ListTopics\": \"sns:ListTopics\",\n    \"OptInPhoneNumber\": \"sns:OptInPhoneNumber\",\n    \"Publish\": \"sns:Publish\",\n    \"RemovePermission\": \"sns:RemovePermission\",\n    \"SetEndpointAttributes\": \"sns:SetEndpointAttributes\",\n    \"SetPlatformApplicationAttributes\": \"sns:SetPlatformApplicationAttributes\",\n    \"SetSMSAttributes\": \"sns:SetSMSAttributes\",\n    \"SetSubscriptionAttributes\": \"sns:SetSubscriptionAttributes\",\n    \"SetTopicAttributes\": \"sns:SetTopicAttributes\",\n    \"Subscribe\": \"sns:Subscribe\",\n    \"TagResource\": \"sns:TagResource\",\n    \"Unsubscribe\": \"sns:Unsubscribe\",\n    \"UntagResource\": \"sns:UntagResource\",\n    \"VerifySMSSandboxPhoneNumber\": \"sns:VerifySMSSandboxPhoneNumber\"\n  },\n  \"sqs\": {\n    \"AddPermission\": \"sqs:AddPermission\",\n    \"ChangeMessageVisibility\": \"sqs:ChangeMessageVisibility\",\n    \"ChangeMessageVisibilityBatch\": \"sqs:ChangeMessageVisibility\",\n    \"CreateQueue\": \"sqs:CreateQueue\",\n    \"DeleteMessage\": \"sqs:DeleteMessage\",\n    \"DeleteMessageBatch\": \"sqs:DeleteMessage\",\n    \"DeleteQueue\": \"sqs:DeleteQueue\",\n    \"GetQueueAttributes\": \"sqs:GetQueueAttributes\",\n    \"GetQueueUrl\": \"sqs:GetQueueUrl\",\n    \"ListDeadLetterSourceQueues\": \"sqs:ListDeadLetterSourceQueues\",\n    \"ListQueueTags\": \"sqs:ListQueueTags\",\n    \"ListQueues\": \"sqs:ListQueues\",\n    \"PurgeQueue\": \"sqs:PurgeQueue\",\n    \"ReceiveMessage\": \"sqs:ReceiveMessage\",\n    \"RemovePermission\": \"sqs:RemovePermission\",\n    \"SendMessage\": \"sqs:SendMessage\",\n    \"SendMessageBatch\": \"sqs:SendMessage\",\n    \"SetQueueAttributes\": \"sqs:SetQueueAttributes\",\n    \"TagQueue\": \"sqs:TagQueue\",\n    \"UntagQueue\": \"sqs:UntagQueue\"\n  },\n  \"ssm\": {\n    \"AddTagsToResource\": \"ssm:AddTagsToResource\",\n    \"AssociateOpsItemRelatedItem\": \"ssm:AssociateOpsItemRelatedItem\",\n    \"CancelCommand\": \"ssm:CancelCommand\",\n    \"CancelMaintenanceWindowExecution\": \"ssm:CancelMaintenanceWindowExecution\",\n    \"CreateActivation\": \"ssm:CreateActivation\",\n    \"CreateAssociation\": \"ssm:CreateAssociation\",\n    \"CreateAssociationBatch\": \"ssm:CreateAssociationBatch\",\n    \"CreateDocument\": \"ssm:CreateDocument\",\n    \"CreateMaintenanceWindow\": \"ssm:CreateMaintenanceWindow\",\n    \"CreateOpsItem\": \"ssm:CreateOpsItem\",\n    \"CreateOpsMetadata\": \"ssm:CreateOpsMetadata\",\n    \"CreatePatchBaseline\": \"ssm:CreatePatchBaseline\",\n    \"CreateResourceDataSync\": \"ssm:CreateResourceDataSync\",\n    \"DeleteActivation\": \"ssm:DeleteActivation\",\n    \"DeleteAssociation\": \"ssm:DeleteAssociation\",\n    \"DeleteDocument\": \"ssm:DeleteDocument\",\n    \"DeleteInventory\": \"ssm:DeleteInventory\",\n    \"DeleteMaintenanceWindow\": \"ssm:DeleteMaintenanceWindow\",\n    \"DeleteOpsMetadata\": \"ssm:DeleteOpsMetadata\",\n    \"DeleteParameter\": \"ssm:DeleteParameter\",\n    \"DeleteParameters\": \"ssm:DeleteParameters\",\n    \"DeletePatchBaseline\": \"ssm:DeletePatchBaseline\",\n    \"DeleteResourceDataSync\": \"ssm:DeleteResourceDataSync\",\n    \"DeregisterManagedInstance\": \"ssm:DeregisterManagedInstance\",\n    \"DeregisterPatchBaselineForPatchGroup\": \"ssm:DeregisterPatchBaselineForPatchGroup\",\n    \"DeregisterTargetFromMaintenanceWindow\": \"ssm:DeregisterTargetFromMaintenanceWindow\",\n    \"DeregisterTaskFromMaintenanceWindow\": \"ssm:DeregisterTaskFromMaintenanceWindow\",\n    \"DescribeActivations\": \"ssm:DescribeActivations\",\n    \"DescribeAssociation\": \"ssm:DescribeAssociation\",\n    \"DescribeAssociationExecutionTargets\": \"ssm:DescribeAssociationExecutionTargets\",\n    \"DescribeAssociationExecutions\": \"ssm:DescribeAssociationExecutions\",\n    \"DescribeAutomationExecutions\": \"ssm:DescribeAutomationExecutions\",\n    \"DescribeAutomationStepExecutions\": \"ssm:DescribeAutomationStepExecutions\",\n    \"DescribeAvailablePatches\": \"ssm:DescribeAvailablePatches\",\n    \"DescribeDocument\": \"ssm:DescribeDocument\",\n    \"DescribeDocumentPermission\": \"ssm:DescribeDocumentPermission\",\n    \"DescribeEffectiveInstanceAssociations\": \"ssm:DescribeEffectiveInstanceAssociations\",\n    \"DescribeEffectivePatchesForPatchBaseline\": \"ssm:DescribeEffectivePatchesForPatchBaseline\",\n    \"DescribeInstanceAssociationsStatus\": \"ssm:DescribeInstanceAssociationsStatus\",\n    \"DescribeInstanceInformation\": \"ssm:DescribeInstanceInformation\",\n    \"DescribeInstancePatchStates\": \"ssm:DescribeInstancePatchStates\",\n    \"DescribeInstancePatchStatesForPatchGroup\": \"ssm:DescribeInstancePatchStatesForPatchGroup\",\n    \"DescribeInstancePatches\": \"ssm:DescribeInstancePatches\",\n    \"DescribeInventoryDeletions\": \"ssm:DescribeInventoryDeletions\",\n    \"DescribeMaintenanceWindowExecutionTaskInvocations\": \"ssm:DescribeMaintenanceWindowExecutionTaskInvocations\",\n    \"DescribeMaintenanceWindowExecutionTasks\": \"ssm:DescribeMaintenanceWindowExecutionTasks\",\n    \"DescribeMaintenanceWindowExecutions\": \"ssm:DescribeMaintenanceWindowExecutions\",\n    \"DescribeMaintenanceWindowSchedule\": \"ssm:DescribeMaintenanceWindowSchedule\",\n    \"DescribeMaintenanceWindowTargets\": \"ssm:DescribeMaintenanceWindowTargets\",\n    \"DescribeMaintenanceWindowTasks\": \"ssm:DescribeMaintenanceWindowTasks\",\n    \"DescribeMaintenanceWindows\": \"ssm:DescribeMaintenanceWindows\",\n    \"DescribeMaintenanceWindowsForTarget\": \"ssm:DescribeMaintenanceWindowsForTarget\",\n    \"DescribeOpsItems\": \"ssm:DescribeOpsItems\",\n    \"DescribeParameters\": \"ssm:DescribeParameters\",\n    \"DescribePatchBaselines\": \"ssm:DescribePatchBaselines\",\n    \"DescribePatchGroupState\": \"ssm:DescribePatchGroupState\",\n    \"DescribePatchGroups\": \"ssm:DescribePatchGroups\",\n    \"DescribePatchProperties\": \"ssm:DescribePatchProperties\",\n    \"DescribeSessions\": \"ssm:DescribeSessions\",\n    \"DisassociateOpsItemRelatedItem\": \"ssm:DisassociateOpsItemRelatedItem\",\n    \"GetAutomationExecution\": \"ssm:GetAutomationExecution\",\n    \"GetCalendarState\": \"ssm:GetCalendarState\",\n    \"GetCommandInvocation\": \"ssm:GetCommandInvocation\",\n    \"GetConnectionStatus\": \"ssm:GetConnectionStatus\",\n    \"GetDefaultPatchBaseline\": \"ssm:GetDefaultPatchBaseline\",\n    \"GetDeployablePatchSnapshotForInstance\": \"ssm:GetDeployablePatchSnapshotForInstance\",\n    \"GetDocument\": \"ssm:GetDocument\",\n    \"GetInventory\": \"ssm:GetInventory\",\n    \"GetInventorySchema\": \"ssm:GetInventorySchema\",\n    \"GetMaintenanceWindow\": \"ssm:GetMaintenanceWindow\",\n    \"GetMaintenanceWindowExecution\": \"ssm:GetMaintenanceWindowExecution\",\n    \"GetMaintenanceWindowExecutionTask\": \"ssm:GetMaintenanceWindowExecutionTask\",\n    \"GetMaintenanceWindowExecutionTaskInvocation\": \"ssm:GetMaintenanceWindowExecutionTaskInvocation\",\n    \"GetMaintenanceWindowTask\": \"ssm:GetMaintenanceWindowTask\",\n    \"GetOpsItem\": \"ssm:GetOpsItem\",\n    \"GetOpsMetadata\": \"ssm:GetOpsMetadata\",\n    \"GetOpsSummary\": \"ssm:GetOpsSummary\",\n    \"GetParameter\": \"ssm:GetParameter\",\n    \"GetParameterHistory\": \"ssm:GetParameterHistory\",\n    \"GetParameters\": \"ssm:GetParameters\",\n    \"GetParametersByPath\": \"ssm:GetParametersByPath\",\n    \"GetPatchBaseline\": \"ssm:GetPatchBaseline\",\n    \"GetPatchBaselineForPatchGroup\": \"ssm:GetPatchBaselineForPatchGroup\",\n    \"GetServiceSetting\": \"ssm:GetServiceSetting\",\n    \"LabelParameterVersion\": \"ssm:LabelParameterVersion\",\n    \"ListAssociationVersions\": \"ssm:ListAssociationVersions\",\n    \"ListAssociations\": \"ssm:ListAssociations\",\n    \"ListCommandInvocations\": \"ssm:ListCommandInvocations\",\n    \"ListCommands\": \"ssm:ListCommands\",\n    \"ListComplianceItems\": \"ssm:ListComplianceItems\",\n    \"ListComplianceSummaries\": \"ssm:ListComplianceSummaries\",\n    \"ListDocumentMetadataHistory\": \"ssm:ListDocumentMetadataHistory\",\n    \"ListDocumentVersions\": \"ssm:ListDocumentVersions\",\n    \"ListDocuments\": \"ssm:ListDocuments\",\n    \"ListInventoryEntries\": \"ssm:ListInventoryEntries\",\n    \"ListOpsItemEvents\": \"ssm:ListOpsItemEvents\",\n    \"ListOpsItemRelatedItems\": \"ssm:ListOpsItemRelatedItems\",\n    \"ListOpsMetadata\": \"ssm:ListOpsMetadata\",\n    \"ListResourceComplianceSummaries\": \"ssm:ListResourceComplianceSummaries\",\n    \"ListResourceDataSync\": \"ssm:ListResourceDataSync\",\n    \"ListTagsForResource\": \"ssm:ListTagsForResource\",\n    \"ModifyDocumentPermission\": \"ssm:ModifyDocumentPermission\",\n    \"PutComplianceItems\": \"ssm:PutComplianceItems\",\n    \"PutInventory\": \"ssm:PutInventory\",\n    \"PutParameter\": \"ssm:PutParameter\",\n    \"RegisterDefaultPatchBaseline\": \"ssm:RegisterDefaultPatchBaseline\",\n    \"RegisterPatchBaselineForPatchGroup\": \"ssm:RegisterPatchBaselineForPatchGroup\",\n    \"RegisterTargetWithMaintenanceWindow\": \"ssm:RegisterTargetWithMaintenanceWindow\",\n    \"RegisterTaskWithMaintenanceWindow\": \"ssm:RegisterTaskWithMaintenanceWindow\",\n    \"RemoveTagsFromResource\": \"ssm:RemoveTagsFromResource\",\n    \"ResetServiceSetting\": \"ssm:ResetServiceSetting\",\n    \"ResumeSession\": \"ssm:ResumeSession\",\n    \"SendAutomationSignal\": \"ssm:SendAutomationSignal\",\n    \"SendCommand\": \"ssm:SendCommand\",\n    \"StartAssociationsOnce\": \"ssm:StartAssociationsOnce\",\n    \"StartAutomationExecution\": \"ssm:StartAutomationExecution\",\n    \"StartChangeRequestExecution\": \"ssm:StartChangeRequestExecution\",\n    \"StartSession\": \"ssm:StartSession\",\n    \"StopAutomationExecution\": \"ssm:StopAutomationExecution\",\n    \"TerminateSession\": \"ssm:TerminateSession\",\n    \"UpdateAssociation\": \"ssm:UpdateAssociation\",\n    \"UpdateAssociationStatus\": \"ssm:UpdateAssociationStatus\",\n    \"UpdateDocument\": \"ssm:UpdateDocument\",\n    \"UpdateDocumentDefaultVersion\": \"ssm:UpdateDocumentDefaultVersion\",\n    \"UpdateDocumentMetadata\": \"ssm:UpdateDocumentMetadata\",\n    \"UpdateMaintenanceWindow\": \"ssm:UpdateMaintenanceWindow\",\n    \"UpdateMaintenanceWindowTarget\": \"ssm:UpdateMaintenanceWindowTarget\",\n    \"UpdateMaintenanceWindowTask\": \"ssm:UpdateMaintenanceWindowTask\",\n    \"UpdateManagedInstanceRole\": \"ssm:UpdateManagedInstanceRole\",\n    \"UpdateOpsItem\": \"ssm:UpdateOpsItem\",\n    \"UpdateOpsMetadata\": \"ssm:UpdateOpsMetadata\",\n    \"UpdatePatchBaseline\": \"ssm:UpdatePatchBaseline\",\n    \"UpdateResourceDataSync\": \"ssm:UpdateResourceDataSync\",\n    \"UpdateServiceSetting\": \"ssm:UpdateServiceSetting\"\n  },\n  \"ssm-contacts\": {\n    \"AcceptPage\": \"ssm-contacts:AcceptPage\",\n    \"ActivateContactChannel\": \"ssm-contacts:ActivateContactChannel\",\n    \"CreateContact\": \"ssm-contacts:CreateContact\",\n    \"CreateContactChannel\": \"ssm-contacts:CreateContactChannel\",\n    \"DeactivateContactChannel\": \"ssm-contacts:DeactivateContactChannel\",\n    \"DeleteContact\": \"ssm-contacts:DeleteContact\",\n    \"DeleteContactChannel\": \"ssm-contacts:DeleteContactChannel\",\n    \"DescribeEngagement\": \"ssm-contacts:DescribeEngagement\",\n    \"DescribePage\": \"ssm-contacts:DescribePage\",\n    \"GetContact\": \"ssm-contacts:GetContact\",\n    \"GetContactChannel\": \"ssm-contacts:GetContactChannel\",\n    \"GetContactPolicy\": \"ssm-contacts:GetContactPolicy\",\n    \"ListContactChannels\": \"ssm-contacts:ListContactChannels\",\n    \"ListContacts\": \"ssm-contacts:ListContacts\",\n    \"ListEngagements\": \"ssm-contacts:ListEngagements\",\n    \"ListPageReceipts\": \"ssm-contacts:ListPageReceipts\",\n    \"ListPagesByContact\": \"ssm-contacts:ListPagesByContact\",\n    \"ListPagesByEngagement\": \"ssm-contacts:ListPagesByEngagement\",\n    \"ListTagsForResource\": \"ssm-contacts:ListTagsForResource\",\n    \"PutContactPolicy\": \"ssm-contacts:PutContactPolicy\",\n    \"SendActivationCode\": \"ssm-contacts:SendActivationCode\",\n    \"StartEngagement\": \"ssm-contacts:StartEngagement\",\n    \"StopEngagement\": \"ssm-contacts:StopEngagement\",\n    \"TagResource\": \"ssm-contacts:TagResource\",\n    \"UntagResource\": \"ssm-contacts:UntagResource\",\n    \"UpdateContact\": \"ssm-contacts:UpdateContact\",\n    \"UpdateContactChannel\": \"ssm-contacts:UpdateContactChannel\"\n  },\n  \"ssm-incidents\": {\n    \"CreateReplicationSet\": \"ssm-incidents:CreateReplicationSet\",\n    \"CreateResponsePlan\": \"ssm-incidents:CreateResponsePlan\",\n    \"CreateTimelineEvent\": \"ssm-incidents:CreateTimelineEvent\",\n    \"DeleteIncidentRecord\": \"ssm-incidents:DeleteIncidentRecord\",\n    \"DeleteReplicationSet\": \"ssm-incidents:DeleteReplicationSet\",\n    \"DeleteResourcePolicy\": \"ssm-incidents:DeleteResourcePolicy\",\n    \"DeleteResponsePlan\": \"ssm-incidents:DeleteResponsePlan\",\n    \"DeleteTimelineEvent\": \"ssm-incidents:DeleteTimelineEvent\",\n    \"GetIncidentRecord\": \"ssm-incidents:GetIncidentRecord\",\n    \"GetReplicationSet\": \"ssm-incidents:GetReplicationSet\",\n    \"GetResourcePolicies\": \"ssm-incidents:GetResourcePolicies\",\n    \"GetResponsePlan\": \"ssm-incidents:GetResponsePlan\",\n    \"GetTimelineEvent\": \"ssm-incidents:GetTimelineEvent\",\n    \"ListIncidentRecords\": \"ssm-incidents:ListIncidentRecords\",\n    \"ListRelatedItems\": \"ssm-incidents:ListRelatedItems\",\n    \"ListReplicationSets\": \"ssm-incidents:ListReplicationSets\",\n    \"ListResponsePlans\": \"ssm-incidents:ListResponsePlans\",\n    \"ListTagsForResource\": \"ssm-incidents:ListTagsForResource\",\n    \"ListTimelineEvents\": \"ssm-incidents:ListTimelineEvents\",\n    \"PutResourcePolicy\": \"ssm-incidents:PutResourcePolicy\",\n    \"StartIncident\": \"ssm-incidents:StartIncident\",\n    \"TagResource\": \"ssm-incidents:TagResource\",\n    \"UntagResource\": \"ssm-incidents:UntagResource\",\n    \"UpdateDeletionProtection\": \"ssm-incidents:UpdateDeletionProtection\",\n    \"UpdateIncidentRecord\": \"ssm-incidents:UpdateIncidentRecord\",\n    \"UpdateRelatedItems\": \"ssm-incidents:UpdateRelatedItems\",\n    \"UpdateReplicationSet\": \"ssm-incidents:UpdateReplicationSet\",\n    \"UpdateResponsePlan\": \"ssm-incidents:UpdateResponsePlan\",\n    \"UpdateTimelineEvent\": \"ssm-incidents:UpdateTimelineEvent\"\n  },\n  \"sso\": {},\n  \"stepfunctions\": {\n    \"CreateActivity\": \"states:CreateActivity\",\n    \"CreateStateMachine\": \"states:CreateStateMachine\",\n    \"DeleteActivity\": \"states:DeleteActivity\",\n    \"DeleteStateMachine\": \"states:DeleteStateMachine\",\n    \"DescribeActivity\": \"states:DescribeActivity\",\n    \"DescribeExecution\": \"states:DescribeExecution\",\n    \"DescribeStateMachine\": \"states:DescribeStateMachine\",\n    \"DescribeStateMachineForExecution\": \"states:DescribeStateMachineForExecution\",\n    \"GetActivityTask\": \"states:GetActivityTask\",\n    \"GetExecutionHistory\": \"states:GetExecutionHistory\",\n    \"ListActivities\": \"states:ListActivities\",\n    \"ListExecutions\": \"states:ListExecutions\",\n    \"ListStateMachines\": \"states:ListStateMachines\",\n    \"ListTagsForResource\": \"states:ListTagsForResource\",\n    \"SendTaskFailure\": \"states:SendTaskFailure\",\n    \"SendTaskHeartbeat\": \"states:SendTaskHeartbeat\",\n    \"SendTaskSuccess\": \"states:SendTaskSuccess\",\n    \"StartExecution\": \"states:StartExecution\",\n    \"StartSyncExecution\": \"states:StartSyncExecution\",\n    \"StopExecution\": \"states:StopExecution\",\n    \"TagResource\": \"states:TagResource\",\n    \"UntagResource\": \"states:UntagResource\",\n    \"UpdateStateMachine\": \"states:UpdateStateMachine\"\n  },\n  \"storagegateway\": {\n    \"ActivateGateway\": \"storagegateway:ActivateGateway\",\n    \"AddCache\": \"storagegateway:AddCache\",\n    \"AddTagsToResource\": \"storagegateway:AddTagsToResource\",\n    \"AddUploadBuffer\": \"storagegateway:AddUploadBuffer\",\n    \"AddWorkingStorage\": \"storagegateway:AddWorkingStorage\",\n    \"AssignTapePool\": \"storagegateway:AssignTapePool\",\n    \"AssociateFileSystem\": \"storagegateway:AssociateFileSystem\",\n    \"AttachVolume\": \"storagegateway:AttachVolume\",\n    \"CancelArchival\": \"storagegateway:CancelArchival\",\n    \"CancelRetrieval\": \"storagegateway:CancelRetrieval\",\n    \"CreateCachediSCSIVolume\": \"storagegateway:CreateCachediSCSIVolume\",\n    \"CreateNFSFileShare\": \"storagegateway:CreateNFSFileShare\",\n    \"CreateSMBFileShare\": \"storagegateway:CreateSMBFileShare\",\n    \"CreateSnapshot\": \"storagegateway:CreateSnapshot\",\n    \"CreateSnapshotFromVolumeRecoveryPoint\": \"storagegateway:CreateSnapshotFromVolumeRecoveryPoint\",\n    \"CreateStorediSCSIVolume\": \"storagegateway:CreateStorediSCSIVolume\",\n    \"CreateTapePool\": \"storagegateway:CreateTapePool\",\n    \"CreateTapeWithBarcode\": \"storagegateway:CreateTapeWithBarcode\",\n    \"CreateTapes\": \"storagegateway:CreateTapes\",\n    \"DeleteAutomaticTapeCreationPolicy\": \"storagegateway:DeleteAutomaticTapeCreationPolicy\",\n    \"DeleteBandwidthRateLimit\": \"storagegateway:DeleteBandwidthRateLimit\",\n    \"DeleteChapCredentials\": \"storagegateway:DeleteChapCredentials\",\n    \"DeleteFileShare\": \"storagegateway:DeleteFileShare\",\n    \"DeleteGateway\": \"storagegateway:DeleteGateway\",\n    \"DeleteSnapshotSchedule\": \"storagegateway:DeleteSnapshotSchedule\",\n    \"DeleteTape\": \"storagegateway:DeleteTape\",\n    \"DeleteTapeArchive\": \"storagegateway:DeleteTapeArchive\",\n    \"DeleteTapePool\": \"storagegateway:DeleteTapePool\",\n    \"DeleteVolume\": \"storagegateway:DeleteVolume\",\n    \"DescribeAvailabilityMonitorTest\": \"storagegateway:DescribeAvailabilityMonitorTest\",\n    \"DescribeBandwidthRateLimit\": \"storagegateway:DescribeBandwidthRateLimit\",\n    \"DescribeBandwidthRateLimitSchedule\": \"storagegateway:DescribeBandwidthRateLimitSchedule\",\n    \"DescribeCache\": \"storagegateway:DescribeCache\",\n    \"DescribeCachediSCSIVolumes\": \"storagegateway:DescribeCachediSCSIVolumes\",\n    \"DescribeChapCredentials\": \"storagegateway:DescribeChapCredentials\",\n    \"DescribeFileSystemAssociations\": \"storagegateway:DescribeFileSystemAssociations\",\n    \"DescribeGatewayInformation\": \"storagegateway:DescribeGatewayInformation\",\n    \"DescribeMaintenanceStartTime\": \"storagegateway:DescribeMaintenanceStartTime\",\n    \"DescribeNFSFileShares\": \"storagegateway:DescribeNFSFileShares\",\n    \"DescribeSMBFileShares\": \"storagegateway:DescribeSMBFileShares\",\n    \"DescribeSMBSettings\": \"storagegateway:DescribeSMBSettings\",\n    \"DescribeSnapshotSchedule\": \"storagegateway:DescribeSnapshotSchedule\",\n    \"DescribeStorediSCSIVolumes\": \"storagegateway:DescribeStorediSCSIVolumes\",\n    \"DescribeTapeArchives\": \"storagegateway:DescribeTapeArchives\",\n    \"DescribeTapeRecoveryPoints\": \"storagegateway:DescribeTapeRecoveryPoints\",\n    \"DescribeTapes\": \"storagegateway:DescribeTapes\",\n    \"DescribeUploadBuffer\": \"storagegateway:DescribeUploadBuffer\",\n    \"DescribeVTLDevices\": \"storagegateway:DescribeVTLDevices\",\n    \"DescribeWorkingStorage\": \"storagegateway:DescribeWorkingStorage\",\n    \"DetachVolume\": \"storagegateway:DetachVolume\",\n    \"DisableGateway\": \"storagegateway:DisableGateway\",\n    \"DisassociateFileSystem\": \"storagegateway:DisassociateFileSystem\",\n    \"JoinDomain\": \"storagegateway:JoinDomain\",\n    \"ListAutomaticTapeCreationPolicies\": \"storagegateway:ListAutomaticTapeCreationPolicies\",\n    \"ListFileShares\": \"storagegateway:ListFileShares\",\n    \"ListFileSystemAssociations\": \"storagegateway:ListFileSystemAssociations\",\n    \"ListGateways\": \"storagegateway:ListGateways\",\n    \"ListLocalDisks\": \"storagegateway:ListLocalDisks\",\n    \"ListTagsForResource\": \"storagegateway:ListTagsForResource\",\n    \"ListTapePools\": \"storagegateway:ListTapePools\",\n    \"ListTapes\": \"storagegateway:ListTapes\",\n    \"ListVolumeInitiators\": \"storagegateway:ListVolumeInitiators\",\n    \"ListVolumeRecoveryPoints\": \"storagegateway:ListVolumeRecoveryPoints\",\n    \"ListVolumes\": \"storagegateway:ListVolumes\",\n    \"NotifyWhenUploaded\": \"storagegateway:NotifyWhenUploaded\",\n    \"RefreshCache\": \"storagegateway:RefreshCache\",\n    \"RemoveTagsFromResource\": \"storagegateway:RemoveTagsFromResource\",\n    \"ResetCache\": \"storagegateway:ResetCache\",\n    \"RetrieveTapeArchive\": \"storagegateway:RetrieveTapeArchive\",\n    \"RetrieveTapeRecoveryPoint\": \"storagegateway:RetrieveTapeRecoveryPoint\",\n    \"SetLocalConsolePassword\": \"storagegateway:SetLocalConsolePassword\",\n    \"SetSMBGuestPassword\": \"storagegateway:SetSMBGuestPassword\",\n    \"ShutdownGateway\": \"storagegateway:ShutdownGateway\",\n    \"StartAvailabilityMonitorTest\": \"storagegateway:StartAvailabilityMonitorTest\",\n    \"StartGateway\": \"storagegateway:StartGateway\",\n    \"UpdateAutomaticTapeCreationPolicy\": \"storagegateway:UpdateAutomaticTapeCreationPolicy\",\n    \"UpdateBandwidthRateLimit\": \"storagegateway:UpdateBandwidthRateLimit\",\n    \"UpdateBandwidthRateLimitSchedule\": \"storagegateway:UpdateBandwidthRateLimitSchedule\",\n    \"UpdateChapCredentials\": \"storagegateway:UpdateChapCredentials\",\n    \"UpdateFileSystemAssociation\": \"storagegateway:UpdateFileSystemAssociation\",\n    \"UpdateGatewayInformation\": \"storagegateway:UpdateGatewayInformation\",\n    \"UpdateGatewaySoftwareNow\": \"storagegateway:UpdateGatewaySoftwareNow\",\n    \"UpdateMaintenanceStartTime\": \"storagegateway:UpdateMaintenanceStartTime\",\n    \"UpdateNFSFileShare\": \"storagegateway:UpdateNFSFileShare\",\n    \"UpdateSMBFileShare\": \"storagegateway:UpdateSMBFileShare\",\n    \"UpdateSMBFileShareVisibility\": \"storagegateway:UpdateSMBFileShareVisibility\",\n    \"UpdateSMBSecurityStrategy\": \"storagegateway:UpdateSMBSecurityStrategy\",\n    \"UpdateSnapshotSchedule\": \"storagegateway:UpdateSnapshotSchedule\",\n    \"UpdateVTLDeviceType\": \"storagegateway:UpdateVTLDeviceType\"\n  },\n  \"sts\": {\n    \"AssumeRole\": \"sts:AssumeRole\",\n    \"AssumeRoleWithSAML\": \"sts:AssumeRoleWithSAML\",\n    \"AssumeRoleWithWebIdentity\": \"sts:AssumeRoleWithWebIdentity\",\n    \"DecodeAuthorizationMessage\": \"sts:DecodeAuthorizationMessage\",\n    \"GetAccessKeyInfo\": \"sts:GetAccessKeyInfo\",\n    \"GetCallerIdentity\": \"sts:GetCallerIdentity\",\n    \"GetFederationToken\": \"sts:GetFederationToken\",\n    \"GetSessionToken\": \"sts:GetSessionToken\"\n  },\n  \"swf\": {\n    \"CountClosedWorkflowExecutions\": \"swf:CountClosedWorkflowExecutions\",\n    \"CountOpenWorkflowExecutions\": \"swf:CountOpenWorkflowExecutions\",\n    \"CountPendingActivityTasks\": \"swf:CountPendingActivityTasks\",\n    \"CountPendingDecisionTasks\": \"swf:CountPendingDecisionTasks\",\n    \"DeprecateActivityType\": \"swf:DeprecateActivityType\",\n    \"DeprecateDomain\": \"swf:DeprecateDomain\",\n    \"DeprecateWorkflowType\": \"swf:DeprecateWorkflowType\",\n    \"DescribeActivityType\": \"swf:DescribeActivityType\",\n    \"DescribeDomain\": \"swf:DescribeDomain\",\n    \"DescribeWorkflowExecution\": \"swf:DescribeWorkflowExecution\",\n    \"DescribeWorkflowType\": \"swf:DescribeWorkflowType\",\n    \"GetWorkflowExecutionHistory\": \"swf:GetWorkflowExecutionHistory\",\n    \"ListActivityTypes\": \"swf:ListActivityTypes\",\n    \"ListClosedWorkflowExecutions\": \"swf:ListClosedWorkflowExecutions\",\n    \"ListDomains\": \"swf:ListDomains\",\n    \"ListOpenWorkflowExecutions\": \"swf:ListOpenWorkflowExecutions\",\n    \"ListTagsForResource\": \"swf:ListTagsForResource\",\n    \"ListWorkflowTypes\": \"swf:ListWorkflowTypes\",\n    \"PollForActivityTask\": \"swf:PollForActivityTask\",\n    \"PollForDecisionTask\": \"swf:PollForDecisionTask\",\n    \"RecordActivityTaskHeartbeat\": \"swf:RecordActivityTaskHeartbeat\",\n    \"RegisterActivityType\": \"swf:RegisterActivityType\",\n    \"RegisterDomain\": \"swf:RegisterDomain\",\n    \"RegisterWorkflowType\": \"swf:RegisterWorkflowType\",\n    \"RequestCancelWorkflowExecution\": \"swf:RequestCancelWorkflowExecution\",\n    \"RespondActivityTaskCanceled\": \"swf:RespondActivityTaskCanceled\",\n    \"RespondActivityTaskCompleted\": \"swf:RespondActivityTaskCompleted\",\n    \"RespondActivityTaskFailed\": \"swf:RespondActivityTaskFailed\",\n    \"RespondDecisionTaskCompleted\": \"swf:RespondDecisionTaskCompleted\",\n    \"SignalWorkflowExecution\": \"swf:SignalWorkflowExecution\",\n    \"StartWorkflowExecution\": \"swf:StartWorkflowExecution\",\n    \"TagResource\": \"swf:TagResource\",\n    \"TerminateWorkflowExecution\": \"swf:TerminateWorkflowExecution\",\n    \"UndeprecateActivityType\": \"swf:UndeprecateActivityType\",\n    \"UndeprecateDomain\": \"swf:UndeprecateDomain\",\n    \"UndeprecateWorkflowType\": \"swf:UndeprecateWorkflowType\",\n    \"UntagResource\": \"swf:UntagResource\"\n  },\n  \"synthetics\": {\n    \"CreateCanary\": \"synthetics:CreateCanary\",\n    \"DeleteCanary\": \"synthetics:DeleteCanary\",\n    \"DescribeCanaries\": \"synthetics:DescribeCanaries\",\n    \"DescribeCanariesLastRun\": \"synthetics:DescribeCanariesLastRun\",\n    \"DescribeRuntimeVersions\": \"synthetics:DescribeRuntimeVersions\",\n    \"GetCanary\": \"synthetics:GetCanary\",\n    \"GetCanaryRuns\": \"synthetics:GetCanaryRuns\",\n    \"ListTagsForResource\": \"synthetics:ListTagsForResource\",\n    \"StartCanary\": \"synthetics:StartCanary\",\n    \"StopCanary\": \"synthetics:StopCanary\",\n    \"TagResource\": \"synthetics:TagResource\",\n    \"UntagResource\": \"synthetics:UntagResource\",\n    \"UpdateCanary\": \"synthetics:UpdateCanary\"\n  },\n  \"textract\": {\n    \"AnalyzeDocument\": \"textract:AnalyzeDocument\",\n    \"AnalyzeExpense\": \"textract:AnalyzeExpense\",\n    \"AnalyzeID\": \"textract:AnalyzeID\",\n    \"DetectDocumentText\": \"textract:DetectDocumentText\",\n    \"GetDocumentAnalysis\": \"textract:GetDocumentAnalysis\",\n    \"GetDocumentTextDetection\": \"textract:GetDocumentTextDetection\",\n    \"GetExpenseAnalysis\": \"textract:GetExpenseAnalysis\",\n    \"StartDocumentAnalysis\": \"textract:StartDocumentAnalysis\",\n    \"StartDocumentTextDetection\": \"textract:StartDocumentTextDetection\",\n    \"StartExpenseAnalysis\": \"textract:StartExpenseAnalysis\"\n  },\n  \"transcribe\": {\n    \"CreateCallAnalyticsCategory\": \"transcribe:CreateCallAnalyticsCategory\",\n    \"CreateLanguageModel\": \"transcribe:CreateLanguageModel\",\n    \"CreateMedicalVocabulary\": \"transcribe:CreateMedicalVocabulary\",\n    \"CreateVocabulary\": \"transcribe:CreateVocabulary\",\n    \"CreateVocabularyFilter\": \"transcribe:CreateVocabularyFilter\",\n    \"DeleteCallAnalyticsCategory\": \"transcribe:DeleteCallAnalyticsCategory\",\n    \"DeleteCallAnalyticsJob\": \"transcribe:DeleteCallAnalyticsJob\",\n    \"DeleteLanguageModel\": \"transcribe:DeleteLanguageModel\",\n    \"DeleteMedicalTranscriptionJob\": \"transcribe:DeleteMedicalTranscriptionJob\",\n    \"DeleteMedicalVocabulary\": \"transcribe:DeleteMedicalVocabulary\",\n    \"DeleteTranscriptionJob\": \"transcribe:DeleteTranscriptionJob\",\n    \"DeleteVocabulary\": \"transcribe:DeleteVocabulary\",\n    \"DeleteVocabularyFilter\": \"transcribe:DeleteVocabularyFilter\",\n    \"DescribeLanguageModel\": \"transcribe:DescribeLanguageModel\",\n    \"GetCallAnalyticsCategory\": \"transcribe:GetCallAnalyticsCategory\",\n    \"GetCallAnalyticsJob\": \"transcribe:GetCallAnalyticsJob\",\n    \"GetMedicalTranscriptionJob\": \"transcribe:GetMedicalTranscriptionJob\",\n    \"GetMedicalVocabulary\": \"transcribe:GetMedicalVocabulary\",\n    \"GetTranscriptionJob\": \"transcribe:GetTranscriptionJob\",\n    \"GetVocabulary\": \"transcribe:GetVocabulary\",\n    \"GetVocabularyFilter\": \"transcribe:GetVocabularyFilter\",\n    \"ListCallAnalyticsCategories\": \"transcribe:ListCallAnalyticsCategories\",\n    \"ListCallAnalyticsJobs\": \"transcribe:ListCallAnalyticsJobs\",\n    \"ListLanguageModels\": \"transcribe:ListLanguageModels\",\n    \"ListMedicalTranscriptionJobs\": \"transcribe:ListMedicalTranscriptionJobs\",\n    \"ListMedicalVocabularies\": \"transcribe:ListMedicalVocabularies\",\n    \"ListTranscriptionJobs\": \"transcribe:ListTranscriptionJobs\",\n    \"ListVocabularies\": \"transcribe:ListVocabularies\",\n    \"ListVocabularyFilters\": \"transcribe:ListVocabularyFilters\",\n    \"StartCallAnalyticsJob\": \"transcribe:StartCallAnalyticsJob\",\n    \"StartMedicalTranscriptionJob\": \"transcribe:StartMedicalTranscriptionJob\",\n    \"StartTranscriptionJob\": \"transcribe:StartTranscriptionJob\",\n    \"UpdateCallAnalyticsCategory\": \"transcribe:UpdateCallAnalyticsCategory\",\n    \"UpdateMedicalVocabulary\": \"transcribe:UpdateMedicalVocabulary\",\n    \"UpdateVocabulary\": \"transcribe:UpdateVocabulary\",\n    \"UpdateVocabularyFilter\": \"transcribe:UpdateVocabularyFilter\"\n  },\n  \"transfer\": {\n    \"CreateAccess\": \"transfer:CreateAccess\",\n    \"CreateServer\": \"transfer:CreateServer\",\n    \"CreateUser\": \"transfer:CreateUser\",\n    \"CreateWorkflow\": \"transfer:CreateWorkflow\",\n    \"DeleteAccess\": \"transfer:DeleteAccess\",\n    \"DeleteServer\": \"transfer:DeleteServer\",\n    \"DeleteSshPublicKey\": \"transfer:DeleteSshPublicKey\",\n    \"DeleteUser\": \"transfer:DeleteUser\",\n    \"DeleteWorkflow\": \"transfer:DeleteWorkflow\",\n    \"DescribeAccess\": \"transfer:DescribeAccess\",\n    \"DescribeExecution\": \"transfer:DescribeExecution\",\n    \"DescribeSecurityPolicy\": \"transfer:DescribeSecurityPolicy\",\n    \"DescribeServer\": \"transfer:DescribeServer\",\n    \"DescribeUser\": \"transfer:DescribeUser\",\n    \"DescribeWorkflow\": \"transfer:DescribeWorkflow\",\n    \"ImportSshPublicKey\": \"transfer:ImportSshPublicKey\",\n    \"ListAccesses\": \"transfer:ListAccesses\",\n    \"ListExecutions\": \"transfer:ListExecutions\",\n    \"ListSecurityPolicies\": \"transfer:ListSecurityPolicies\",\n    \"ListServers\": \"transfer:ListServers\",\n    \"ListTagsForResource\": \"transfer:ListTagsForResource\",\n    \"ListUsers\": \"transfer:ListUsers\",\n    \"ListWorkflows\": \"transfer:ListWorkflows\",\n    \"SendWorkflowStepState\": \"transfer:SendWorkflowStepState\",\n    \"StartServer\": \"transfer:StartServer\",\n    \"StopServer\": \"transfer:StopServer\",\n    \"TagResource\": \"transfer:TagResource\",\n    \"TestIdentityProvider\": \"transfer:TestIdentityProvider\",\n    \"UntagResource\": \"transfer:UntagResource\",\n    \"UpdateAccess\": \"transfer:UpdateAccess\",\n    \"UpdateServer\": \"transfer:UpdateServer\",\n    \"UpdateUser\": \"transfer:UpdateUser\"\n  },\n  \"translate\": {\n    \"CreateParallelData\": \"translate:CreateParallelData\",\n    \"DeleteParallelData\": \"translate:DeleteParallelData\",\n    \"DeleteTerminology\": \"translate:DeleteTerminology\",\n    \"DescribeTextTranslationJob\": \"translate:DescribeTextTranslationJob\",\n    \"GetParallelData\": \"translate:GetParallelData\",\n    \"GetTerminology\": \"translate:GetTerminology\",\n    \"ImportTerminology\": \"translate:ImportTerminology\",\n    \"ListParallelData\": \"translate:ListParallelData\",\n    \"ListTerminologies\": \"translate:ListTerminologies\",\n    \"ListTextTranslationJobs\": \"translate:ListTextTranslationJobs\",\n    \"StartTextTranslationJob\": \"translate:StartTextTranslationJob\",\n    \"StopTextTranslationJob\": \"translate:StopTextTranslationJob\",\n    \"TranslateText\": \"translate:TranslateText\",\n    \"UpdateParallelData\": \"translate:UpdateParallelData\"\n  },\n  \"waf\": {\n    \"CreateByteMatchSet\": \"waf:CreateByteMatchSet\",\n    \"CreateGeoMatchSet\": \"waf:CreateGeoMatchSet\",\n    \"CreateIPSet\": \"waf:CreateIPSet\",\n    \"CreateRateBasedRule\": \"waf:CreateRateBasedRule\",\n    \"CreateRegexMatchSet\": \"waf:CreateRegexMatchSet\",\n    \"CreateRegexPatternSet\": \"waf:CreateRegexPatternSet\",\n    \"CreateRule\": \"waf:CreateRule\",\n    \"CreateRuleGroup\": \"waf:CreateRuleGroup\",\n    \"CreateSizeConstraintSet\": \"waf:CreateSizeConstraintSet\",\n    \"CreateSqlInjectionMatchSet\": \"waf:CreateSqlInjectionMatchSet\",\n    \"CreateWebACL\": \"waf:CreateWebACL\",\n    \"CreateWebACLMigrationStack\": \"waf:CreateWebACLMigrationStack\",\n    \"CreateXssMatchSet\": \"waf:CreateXssMatchSet\",\n    \"DeleteByteMatchSet\": \"waf:DeleteByteMatchSet\",\n    \"DeleteGeoMatchSet\": \"waf:DeleteGeoMatchSet\",\n    \"DeleteIPSet\": \"waf:DeleteIPSet\",\n    \"DeleteLoggingConfiguration\": \"waf:DeleteLoggingConfiguration\",\n    \"DeletePermissionPolicy\": \"waf:DeletePermissionPolicy\",\n    \"DeleteRateBasedRule\": \"waf:DeleteRateBasedRule\",\n    \"DeleteRegexMatchSet\": \"waf:DeleteRegexMatchSet\",\n    \"DeleteRegexPatternSet\": \"waf:DeleteRegexPatternSet\",\n    \"DeleteRule\": \"waf:DeleteRule\",\n    \"DeleteRuleGroup\": \"waf:DeleteRuleGroup\",\n    \"DeleteSizeConstraintSet\": \"waf:DeleteSizeConstraintSet\",\n    \"DeleteSqlInjectionMatchSet\": \"waf:DeleteSqlInjectionMatchSet\",\n    \"DeleteWebACL\": \"waf:DeleteWebACL\",\n    \"DeleteXssMatchSet\": \"waf:DeleteXssMatchSet\",\n    \"GetByteMatchSet\": \"waf:GetByteMatchSet\",\n    \"GetChangeToken\": \"waf:GetChangeToken\",\n    \"GetChangeTokenStatus\": \"waf:GetChangeTokenStatus\",\n    \"GetGeoMatchSet\": \"waf:GetGeoMatchSet\",\n    \"GetIPSet\": \"waf:GetIPSet\",\n    \"GetLoggingConfiguration\": \"waf:GetLoggingConfiguration\",\n    \"GetPermissionPolicy\": \"waf:GetPermissionPolicy\",\n    \"GetRateBasedRule\": \"waf:GetRateBasedRule\",\n    \"GetRateBasedRuleManagedKeys\": \"waf:GetRateBasedRuleManagedKeys\",\n    \"GetRegexMatchSet\": \"waf:GetRegexMatchSet\",\n    \"GetRegexPatternSet\": \"waf:GetRegexPatternSet\",\n    \"GetRule\": \"waf:GetRule\",\n    \"GetRuleGroup\": \"waf:GetRuleGroup\",\n    \"GetSampledRequests\": \"waf:GetSampledRequests\",\n    \"GetSizeConstraintSet\": \"waf:GetSizeConstraintSet\",\n    \"GetSqlInjectionMatchSet\": \"waf:GetSqlInjectionMatchSet\",\n    \"GetWebACL\": \"waf:GetWebACL\",\n    \"GetXssMatchSet\": \"waf:GetXssMatchSet\",\n    \"ListActivatedRulesInRuleGroup\": \"waf:ListActivatedRulesInRuleGroup\",\n    \"ListByteMatchSets\": \"waf:ListByteMatchSets\",\n    \"ListGeoMatchSets\": \"waf:ListGeoMatchSets\",\n    \"ListIPSets\": \"waf:ListIPSets\",\n    \"ListLoggingConfigurations\": \"waf:ListLoggingConfigurations\",\n    \"ListRateBasedRules\": \"waf:ListRateBasedRules\",\n    \"ListRegexMatchSets\": \"waf:ListRegexMatchSets\",\n    \"ListRegexPatternSets\": \"waf:ListRegexPatternSets\",\n    \"ListRuleGroups\": \"waf:ListRuleGroups\",\n    \"ListRules\": \"waf:ListRules\",\n    \"ListSizeConstraintSets\": \"waf:ListSizeConstraintSets\",\n    \"ListSqlInjectionMatchSets\": \"waf:ListSqlInjectionMatchSets\",\n    \"ListSubscribedRuleGroups\": \"waf:ListSubscribedRuleGroups\",\n    \"ListTagsForResource\": \"waf:ListTagsForResource\",\n    \"ListWebACLs\": \"waf:ListWebACLs\",\n    \"ListXssMatchSets\": \"waf:ListXssMatchSets\",\n    \"PutLoggingConfiguration\": \"waf:PutLoggingConfiguration\",\n    \"PutPermissionPolicy\": \"waf:PutPermissionPolicy\",\n    \"TagResource\": \"waf:TagResource\",\n    \"UntagResource\": \"waf:UntagResource\",\n    \"UpdateByteMatchSet\": \"waf:UpdateByteMatchSet\",\n    \"UpdateGeoMatchSet\": \"waf:UpdateGeoMatchSet\",\n    \"UpdateIPSet\": \"waf:UpdateIPSet\",\n    \"UpdateRateBasedRule\": \"waf:UpdateRateBasedRule\",\n    \"UpdateRegexMatchSet\": \"waf:UpdateRegexMatchSet\",\n    \"UpdateRegexPatternSet\": \"waf:UpdateRegexPatternSet\",\n    \"UpdateRule\": \"waf:UpdateRule\",\n    \"UpdateRuleGroup\": \"waf:UpdateRuleGroup\",\n    \"UpdateSizeConstraintSet\": \"waf:UpdateSizeConstraintSet\",\n    \"UpdateSqlInjectionMatchSet\": \"waf:UpdateSqlInjectionMatchSet\",\n    \"UpdateWebACL\": \"waf:UpdateWebACL\",\n    \"UpdateXssMatchSet\": \"waf:UpdateXssMatchSet\"\n  },\n  \"waf-regional\": {\n    \"AssociateWebACL\": \"waf-regional:AssociateWebACL\",\n    \"CreateByteMatchSet\": \"waf-regional:CreateByteMatchSet\",\n    \"CreateGeoMatchSet\": \"waf-regional:CreateGeoMatchSet\",\n    \"CreateIPSet\": \"waf-regional:CreateIPSet\",\n    \"CreateRateBasedRule\": \"waf-regional:CreateRateBasedRule\",\n    \"CreateRegexMatchSet\": \"waf-regional:CreateRegexMatchSet\",\n    \"CreateRegexPatternSet\": \"waf-regional:CreateRegexPatternSet\",\n    \"CreateRule\": \"waf-regional:CreateRule\",\n    \"CreateRuleGroup\": \"waf-regional:CreateRuleGroup\",\n    \"CreateSizeConstraintSet\": \"waf-regional:CreateSizeConstraintSet\",\n    \"CreateSqlInjectionMatchSet\": \"waf-regional:CreateSqlInjectionMatchSet\",\n    \"CreateWebACL\": \"waf-regional:CreateWebACL\",\n    \"CreateWebACLMigrationStack\": \"waf-regional:CreateWebACLMigrationStack\",\n    \"CreateXssMatchSet\": \"waf-regional:CreateXssMatchSet\",\n    \"DeleteByteMatchSet\": \"waf-regional:DeleteByteMatchSet\",\n    \"DeleteGeoMatchSet\": \"waf-regional:DeleteGeoMatchSet\",\n    \"DeleteIPSet\": \"waf-regional:DeleteIPSet\",\n    \"DeleteLoggingConfiguration\": \"waf-regional:DeleteLoggingConfiguration\",\n    \"DeletePermissionPolicy\": \"waf-regional:DeletePermissionPolicy\",\n    \"DeleteRateBasedRule\": \"waf-regional:DeleteRateBasedRule\",\n    \"DeleteRegexMatchSet\": \"waf-regional:DeleteRegexMatchSet\",\n    \"DeleteRegexPatternSet\": \"waf-regional:DeleteRegexPatternSet\",\n    \"DeleteRule\": \"waf-regional:DeleteRule\",\n    \"DeleteRuleGroup\": \"waf-regional:DeleteRuleGroup\",\n    \"DeleteSizeConstraintSet\": \"waf-regional:DeleteSizeConstraintSet\",\n    \"DeleteSqlInjectionMatchSet\": \"waf-regional:DeleteSqlInjectionMatchSet\",\n    \"DeleteWebACL\": \"waf-regional:DeleteWebACL\",\n    \"DeleteXssMatchSet\": \"waf-regional:DeleteXssMatchSet\",\n    \"DisassociateWebACL\": \"waf-regional:DisassociateWebACL\",\n    \"GetByteMatchSet\": \"waf-regional:GetByteMatchSet\",\n    \"GetChangeToken\": \"waf-regional:GetChangeToken\",\n    \"GetChangeTokenStatus\": \"waf-regional:GetChangeTokenStatus\",\n    \"GetGeoMatchSet\": \"waf-regional:GetGeoMatchSet\",\n    \"GetIPSet\": \"waf-regional:GetIPSet\",\n    \"GetLoggingConfiguration\": \"waf-regional:GetLoggingConfiguration\",\n    \"GetPermissionPolicy\": \"waf-regional:GetPermissionPolicy\",\n    \"GetRateBasedRule\": \"waf-regional:GetRateBasedRule\",\n    \"GetRateBasedRuleManagedKeys\": \"waf-regional:GetRateBasedRuleManagedKeys\",\n    \"GetRegexMatchSet\": \"waf-regional:GetRegexMatchSet\",\n    \"GetRegexPatternSet\": \"waf-regional:GetRegexPatternSet\",\n    \"GetRule\": \"waf-regional:GetRule\",\n    \"GetRuleGroup\": \"waf-regional:GetRuleGroup\",\n    \"GetSampledRequests\": \"waf-regional:GetSampledRequests\",\n    \"GetSizeConstraintSet\": \"waf-regional:GetSizeConstraintSet\",\n    \"GetSqlInjectionMatchSet\": \"waf-regional:GetSqlInjectionMatchSet\",\n    \"GetWebACL\": \"waf-regional:GetWebACL\",\n    \"GetWebACLForResource\": \"waf-regional:GetWebACLForResource\",\n    \"GetXssMatchSet\": \"waf-regional:GetXssMatchSet\",\n    \"ListActivatedRulesInRuleGroup\": \"waf-regional:ListActivatedRulesInRuleGroup\",\n    \"ListByteMatchSets\": \"waf-regional:ListByteMatchSets\",\n    \"ListGeoMatchSets\": \"waf-regional:ListGeoMatchSets\",\n    \"ListIPSets\": \"waf-regional:ListIPSets\",\n    \"ListLoggingConfigurations\": \"waf-regional:ListLoggingConfigurations\",\n    \"ListRateBasedRules\": \"waf-regional:ListRateBasedRules\",\n    \"ListRegexMatchSets\": \"waf-regional:ListRegexMatchSets\",\n    \"ListRegexPatternSets\": \"waf-regional:ListRegexPatternSets\",\n    \"ListResourcesForWebACL\": \"waf-regional:ListResourcesForWebACL\",\n    \"ListRuleGroups\": \"waf-regional:ListRuleGroups\",\n    \"ListRules\": \"waf-regional:ListRules\",\n    \"ListSizeConstraintSets\": \"waf-regional:ListSizeConstraintSets\",\n    \"ListSqlInjectionMatchSets\": \"waf-regional:ListSqlInjectionMatchSets\",\n    \"ListSubscribedRuleGroups\": \"waf-regional:ListSubscribedRuleGroups\",\n    \"ListTagsForResource\": \"waf-regional:ListTagsForResource\",\n    \"ListWebACLs\": \"waf-regional:ListWebACLs\",\n    \"ListXssMatchSets\": \"waf-regional:ListXssMatchSets\",\n    \"PutLoggingConfiguration\": \"waf-regional:PutLoggingConfiguration\",\n    \"PutPermissionPolicy\": \"waf-regional:PutPermissionPolicy\",\n    \"TagResource\": \"waf-regional:TagResource\",\n    \"UntagResource\": \"waf-regional:UntagResource\",\n    \"UpdateByteMatchSet\": \"waf-regional:UpdateByteMatchSet\",\n    \"UpdateGeoMatchSet\": \"waf-regional:UpdateGeoMatchSet\",\n    \"UpdateIPSet\": \"waf-regional:UpdateIPSet\",\n    \"UpdateRateBasedRule\": \"waf-regional:UpdateRateBasedRule\",\n    \"UpdateRegexMatchSet\": \"waf-regional:UpdateRegexMatchSet\",\n    \"UpdateRegexPatternSet\": \"waf-regional:UpdateRegexPatternSet\",\n    \"UpdateRule\": \"waf-regional:UpdateRule\",\n    \"UpdateRuleGroup\": \"waf-regional:UpdateRuleGroup\",\n    \"UpdateSizeConstraintSet\": \"waf-regional:UpdateSizeConstraintSet\",\n    \"UpdateSqlInjectionMatchSet\": \"waf-regional:UpdateSqlInjectionMatchSet\",\n    \"UpdateWebACL\": \"waf-regional:UpdateWebACL\",\n    \"UpdateXssMatchSet\": \"waf-regional:UpdateXssMatchSet\"\n  },\n  \"wafv2\": {\n    \"AssociateWebACL\": \"wafv2:AssociateWebACL\",\n    \"CheckCapacity\": \"wafv2:CheckCapacity\",\n    \"CreateIPSet\": \"wafv2:CreateIPSet\",\n    \"CreateRegexPatternSet\": \"wafv2:CreateRegexPatternSet\",\n    \"CreateRuleGroup\": \"wafv2:CreateRuleGroup\",\n    \"CreateWebACL\": \"wafv2:CreateWebACL\",\n    \"DeleteFirewallManagerRuleGroups\": \"wafv2:DeleteFirewallManagerRuleGroups\",\n    \"DeleteIPSet\": \"wafv2:DeleteIPSet\",\n    \"DeleteLoggingConfiguration\": \"wafv2:DeleteLoggingConfiguration\",\n    \"DeletePermissionPolicy\": \"wafv2:DeletePermissionPolicy\",\n    \"DeleteRegexPatternSet\": \"wafv2:DeleteRegexPatternSet\",\n    \"DeleteRuleGroup\": \"wafv2:DeleteRuleGroup\",\n    \"DeleteWebACL\": \"wafv2:DeleteWebACL\",\n    \"DescribeManagedRuleGroup\": \"wafv2:DescribeManagedRuleGroup\",\n    \"DisassociateWebACL\": \"wafv2:DisassociateWebACL\",\n    \"GetIPSet\": \"wafv2:GetIPSet\",\n    \"GetLoggingConfiguration\": \"wafv2:GetLoggingConfiguration\",\n    \"GetManagedRuleSet\": \"wafv2:GetManagedRuleSet\",\n    \"GetPermissionPolicy\": \"wafv2:GetPermissionPolicy\",\n    \"GetRateBasedStatementManagedKeys\": \"wafv2:GetRateBasedStatementManagedKeys\",\n    \"GetRegexPatternSet\": \"wafv2:GetRegexPatternSet\",\n    \"GetRuleGroup\": \"wafv2:GetRuleGroup\",\n    \"GetSampledRequests\": \"wafv2:GetSampledRequests\",\n    \"GetWebACL\": \"wafv2:GetWebACL\",\n    \"GetWebACLForResource\": \"wafv2:GetWebACLForResource\",\n    \"ListAvailableManagedRuleGroups\": \"wafv2:ListAvailableManagedRuleGroups\",\n    \"ListIPSets\": \"wafv2:ListIPSets\",\n    \"ListLoggingConfigurations\": \"wafv2:ListLoggingConfigurations\",\n    \"ListManagedRuleSets\": \"wafv2:ListManagedRuleSets\",\n    \"ListRegexPatternSets\": \"wafv2:ListRegexPatternSets\",\n    \"ListResourcesForWebACL\": \"wafv2:ListResourcesForWebACL\",\n    \"ListRuleGroups\": \"wafv2:ListRuleGroups\",\n    \"ListTagsForResource\": \"wafv2:ListTagsForResource\",\n    \"ListWebACLs\": \"wafv2:ListWebACLs\",\n    \"PutLoggingConfiguration\": \"wafv2:PutLoggingConfiguration\",\n    \"PutManagedRuleSetVersions\": \"wafv2:PutManagedRuleSetVersions\",\n    \"PutPermissionPolicy\": \"wafv2:PutPermissionPolicy\",\n    \"TagResource\": \"wafv2:TagResource\",\n    \"UntagResource\": \"wafv2:UntagResource\",\n    \"UpdateIPSet\": \"wafv2:UpdateIPSet\",\n    \"UpdateManagedRuleSetVersionExpiryDate\": \"wafv2:UpdateManagedRuleSetVersionExpiryDate\",\n    \"UpdateRegexPatternSet\": \"wafv2:UpdateRegexPatternSet\",\n    \"UpdateRuleGroup\": \"wafv2:UpdateRuleGroup\",\n    \"UpdateWebACL\": \"wafv2:UpdateWebACL\"\n  },\n  \"wellarchitected\": {\n    \"AssociateLenses\": \"wellarchitected:AssociateLenses\",\n    \"CreateLensShare\": \"wellarchitected:CreateLensShare\",\n    \"CreateLensVersion\": \"wellarchitected:CreateLensVersion\",\n    \"CreateMilestone\": \"wellarchitected:CreateMilestone\",\n    \"CreateWorkload\": \"wellarchitected:CreateWorkload\",\n    \"CreateWorkloadShare\": \"wellarchitected:CreateWorkloadShare\",\n    \"DeleteLens\": \"wellarchitected:DeleteLens\",\n    \"DeleteLensShare\": \"wellarchitected:DeleteLensShare\",\n    \"DeleteWorkload\": \"wellarchitected:DeleteWorkload\",\n    \"DeleteWorkloadShare\": \"wellarchitected:DeleteWorkloadShare\",\n    \"DisassociateLenses\": \"wellarchitected:DisassociateLenses\",\n    \"ExportLens\": \"wellarchitected:ExportLens\",\n    \"GetAnswer\": \"wellarchitected:GetAnswer\",\n    \"GetLens\": \"wellarchitected:GetLens\",\n    \"GetLensReview\": \"wellarchitected:GetLensReview\",\n    \"GetLensReviewReport\": \"wellarchitected:GetLensReviewReport\",\n    \"GetLensVersionDifference\": \"wellarchitected:GetLensVersionDifference\",\n    \"GetMilestone\": \"wellarchitected:GetMilestone\",\n    \"GetWorkload\": \"wellarchitected:GetWorkload\",\n    \"ImportLens\": \"wellarchitected:ImportLens\",\n    \"ListAnswers\": \"wellarchitected:ListAnswers\",\n    \"ListLensReviewImprovements\": \"wellarchitected:ListLensReviewImprovements\",\n    \"ListLensReviews\": \"wellarchitected:ListLensReviews\",\n    \"ListLensShares\": \"wellarchitected:ListLensShares\",\n    \"ListLenses\": \"wellarchitected:ListLenses\",\n    \"ListMilestones\": \"wellarchitected:ListMilestones\",\n    \"ListNotifications\": \"wellarchitected:ListNotifications\",\n    \"ListShareInvitations\": \"wellarchitected:ListShareInvitations\",\n    \"ListTagsForResource\": \"wellarchitected:ListTagsForResource\",\n    \"ListWorkloadShares\": \"wellarchitected:ListWorkloadShares\",\n    \"ListWorkloads\": \"wellarchitected:ListWorkloads\",\n    \"TagResource\": \"wellarchitected:TagResource\",\n    \"UntagResource\": \"wellarchitected:UntagResource\",\n    \"UpdateAnswer\": \"wellarchitected:UpdateAnswer\",\n    \"UpdateLensReview\": \"wellarchitected:UpdateLensReview\",\n    \"UpdateShareInvitation\": \"wellarchitected:UpdateShareInvitation\",\n    \"UpdateWorkload\": \"wellarchitected:UpdateWorkload\",\n    \"UpdateWorkloadShare\": \"wellarchitected:UpdateWorkloadShare\",\n    \"UpgradeLensReview\": \"wellarchitected:UpgradeLensReview\"\n  },\n  \"wisdom\": {\n    \"CreateAssistant\": \"wisdom:CreateAssistant\",\n    \"CreateAssistantAssociation\": \"wisdom:CreateAssistantAssociation\",\n    \"CreateContent\": \"wisdom:CreateContent\",\n    \"CreateKnowledgeBase\": \"wisdom:CreateKnowledgeBase\",\n    \"CreateSession\": \"wisdom:CreateSession\",\n    \"DeleteAssistant\": \"wisdom:DeleteAssistant\",\n    \"DeleteAssistantAssociation\": \"wisdom:DeleteAssistantAssociation\",\n    \"DeleteContent\": \"wisdom:DeleteContent\",\n    \"DeleteKnowledgeBase\": \"wisdom:DeleteKnowledgeBase\",\n    \"GetAssistant\": \"wisdom:GetAssistant\",\n    \"GetAssistantAssociation\": \"wisdom:GetAssistantAssociation\",\n    \"GetContent\": \"wisdom:GetContent\",\n    \"GetContentSummary\": \"wisdom:GetContentSummary\",\n    \"GetKnowledgeBase\": \"wisdom:GetKnowledgeBase\",\n    \"GetRecommendations\": \"wisdom:GetRecommendations\",\n    \"GetSession\": \"wisdom:GetSession\",\n    \"ListAssistantAssociations\": \"wisdom:ListAssistantAssociations\",\n    \"ListAssistants\": \"wisdom:ListAssistants\",\n    \"ListContents\": \"wisdom:ListContents\",\n    \"ListKnowledgeBases\": \"wisdom:ListKnowledgeBases\",\n    \"ListTagsForResource\": \"wisdom:ListTagsForResource\",\n    \"NotifyRecommendationsReceived\": \"wisdom:NotifyRecommendationsReceived\",\n    \"QueryAssistant\": \"wisdom:QueryAssistant\",\n    \"RemoveKnowledgeBaseTemplateUri\": \"wisdom:RemoveKnowledgeBaseTemplateUri\",\n    \"SearchContent\": \"wisdom:SearchContent\",\n    \"SearchSessions\": \"wisdom:SearchSessions\",\n    \"StartContentUpload\": \"wisdom:StartContentUpload\",\n    \"TagResource\": \"wisdom:TagResource\",\n    \"UntagResource\": \"wisdom:UntagResource\",\n    \"UpdateContent\": \"wisdom:UpdateContent\",\n    \"UpdateKnowledgeBaseTemplateUri\": \"wisdom:UpdateKnowledgeBaseTemplateUri\"\n  },\n  \"workdocs\": {\n    \"AbortDocumentVersionUpload\": \"workdocs:AbortDocumentVersionUpload\",\n    \"ActivateUser\": \"workdocs:ActivateUser\",\n    \"AddResourcePermissions\": \"workdocs:AddResourcePermissions\",\n    \"CreateComment\": \"workdocs:CreateComment\",\n    \"CreateCustomMetadata\": \"workdocs:CreateCustomMetadata\",\n    \"CreateFolder\": \"workdocs:CreateFolder\",\n    \"CreateLabels\": \"workdocs:CreateLabels\",\n    \"CreateNotificationSubscription\": \"workdocs:CreateNotificationSubscription\",\n    \"CreateUser\": \"workdocs:CreateUser\",\n    \"DeactivateUser\": \"workdocs:DeactivateUser\",\n    \"DeleteComment\": \"workdocs:DeleteComment\",\n    \"DeleteCustomMetadata\": \"workdocs:DeleteCustomMetadata\",\n    \"DeleteDocument\": \"workdocs:DeleteDocument\",\n    \"DeleteFolder\": \"workdocs:DeleteFolder\",\n    \"DeleteFolderContents\": \"workdocs:DeleteFolderContents\",\n    \"DeleteLabels\": \"workdocs:DeleteLabels\",\n    \"DeleteNotificationSubscription\": \"workdocs:DeleteNotificationSubscription\",\n    \"DeleteUser\": \"workdocs:DeleteUser\",\n    \"DescribeActivities\": \"workdocs:DescribeActivities\",\n    \"DescribeComments\": \"workdocs:DescribeComments\",\n    \"DescribeDocumentVersions\": \"workdocs:DescribeDocumentVersions\",\n    \"DescribeFolderContents\": \"workdocs:DescribeFolderContents\",\n    \"DescribeGroups\": \"workdocs:DescribeGroups\",\n    \"DescribeNotificationSubscriptions\": \"workdocs:DescribeNotificationSubscriptions\",\n    \"DescribeResourcePermissions\": \"workdocs:DescribeResourcePermissions\",\n    \"DescribeRootFolders\": \"workdocs:DescribeRootFolders\",\n    \"DescribeUsers\": \"workdocs:DescribeUsers\",\n    \"GetCurrentUser\": \"workdocs:GetCurrentUser\",\n    \"GetDocument\": \"workdocs:GetDocument\",\n    \"GetDocumentPath\": \"workdocs:GetDocumentPath\",\n    \"GetDocumentVersion\": \"workdocs:GetDocumentVersion\",\n    \"GetFolder\": \"workdocs:GetFolder\",\n    \"GetFolderPath\": \"workdocs:GetFolderPath\",\n    \"GetResources\": \"workdocs:GetResources\",\n    \"InitiateDocumentVersionUpload\": \"workdocs:InitiateDocumentVersionUpload\",\n    \"RemoveAllResourcePermissions\": \"workdocs:RemoveAllResourcePermissions\",\n    \"RemoveResourcePermission\": \"workdocs:RemoveResourcePermission\",\n    \"UpdateDocument\": \"workdocs:UpdateDocument\",\n    \"UpdateDocumentVersion\": \"workdocs:UpdateDocumentVersion\",\n    \"UpdateFolder\": \"workdocs:UpdateFolder\",\n    \"UpdateUser\": \"workdocs:UpdateUser\"\n  },\n  \"worklink\": {\n    \"AssociateDomain\": \"worklink:AssociateDomain\",\n    \"AssociateWebsiteAuthorizationProvider\": \"worklink:AssociateWebsiteAuthorizationProvider\",\n    \"AssociateWebsiteCertificateAuthority\": \"worklink:AssociateWebsiteCertificateAuthority\",\n    \"CreateFleet\": \"worklink:CreateFleet\",\n    \"DeleteFleet\": \"worklink:DeleteFleet\",\n    \"DescribeAuditStreamConfiguration\": \"worklink:DescribeAuditStreamConfiguration\",\n    \"DescribeCompanyNetworkConfiguration\": \"worklink:DescribeCompanyNetworkConfiguration\",\n    \"DescribeDevice\": \"worklink:DescribeDevice\",\n    \"DescribeDevicePolicyConfiguration\": \"worklink:DescribeDevicePolicyConfiguration\",\n    \"DescribeDomain\": \"worklink:DescribeDomain\",\n    \"DescribeFleetMetadata\": \"worklink:DescribeFleetMetadata\",\n    \"DescribeIdentityProviderConfiguration\": \"worklink:DescribeIdentityProviderConfiguration\",\n    \"DescribeWebsiteCertificateAuthority\": \"worklink:DescribeWebsiteCertificateAuthority\",\n    \"DisassociateDomain\": \"worklink:DisassociateDomain\",\n    \"DisassociateWebsiteAuthorizationProvider\": \"worklink:DisassociateWebsiteAuthorizationProvider\",\n    \"DisassociateWebsiteCertificateAuthority\": \"worklink:DisassociateWebsiteCertificateAuthority\",\n    \"ListDevices\": \"worklink:ListDevices\",\n    \"ListDomains\": \"worklink:ListDomains\",\n    \"ListFleets\": \"worklink:ListFleets\",\n    \"ListTagsForResource\": \"worklink:ListTagsForResource\",\n    \"ListWebsiteAuthorizationProviders\": \"worklink:ListWebsiteAuthorizationProviders\",\n    \"ListWebsiteCertificateAuthorities\": \"worklink:ListWebsiteCertificateAuthorities\",\n    \"RestoreDomainAccess\": \"worklink:RestoreDomainAccess\",\n    \"RevokeDomainAccess\": \"worklink:RevokeDomainAccess\",\n    \"SignOutUser\": \"worklink:SignOutUser\",\n    \"TagResource\": \"worklink:TagResource\",\n    \"UntagResource\": \"worklink:UntagResource\",\n    \"UpdateAuditStreamConfiguration\": \"worklink:UpdateAuditStreamConfiguration\",\n    \"UpdateCompanyNetworkConfiguration\": \"worklink:UpdateCompanyNetworkConfiguration\",\n    \"UpdateDevicePolicyConfiguration\": \"worklink:UpdateDevicePolicyConfiguration\",\n    \"UpdateDomainMetadata\": \"worklink:UpdateDomainMetadata\",\n    \"UpdateFleetMetadata\": \"worklink:UpdateFleetMetadata\",\n    \"UpdateIdentityProviderConfiguration\": \"worklink:UpdateIdentityProviderConfiguration\"\n  },\n  \"workmail\": {\n    \"AssociateDelegateToResource\": \"workmail:AssociateDelegateToResource\",\n    \"AssociateMemberToGroup\": \"workmail:AssociateMemberToGroup\",\n    \"CancelMailboxExportJob\": \"workmail:CancelMailboxExportJob\",\n    \"CreateAlias\": \"workmail:CreateAlias\",\n    \"CreateGroup\": \"workmail:CreateGroup\",\n    \"CreateMobileDeviceAccessRule\": \"workmail:CreateMobileDeviceAccessRule\",\n    \"CreateOrganization\": \"workmail:CreateOrganization\",\n    \"CreateResource\": \"workmail:CreateResource\",\n    \"CreateUser\": \"workmail:CreateUser\",\n    \"DeleteAccessControlRule\": \"workmail:DeleteAccessControlRule\",\n    \"DeleteAlias\": \"workmail:DeleteAlias\",\n    \"DeleteGroup\": \"workmail:DeleteGroup\",\n    \"DeleteMailboxPermissions\": \"workmail:DeleteMailboxPermissions\",\n    \"DeleteMobileDeviceAccessOverride\": \"workmail:DeleteMobileDeviceAccessOverride\",\n    \"DeleteMobileDeviceAccessRule\": \"workmail:DeleteMobileDeviceAccessRule\",\n    \"DeleteOrganization\": \"workmail:DeleteOrganization\",\n    \"DeleteResource\": \"workmail:DeleteResource\",\n    \"DeleteRetentionPolicy\": \"workmail:DeleteRetentionPolicy\",\n    \"DeleteUser\": \"workmail:DeleteUser\",\n    \"DeregisterFromWorkMail\": \"workmail:DeregisterFromWorkMail\",\n    \"DeregisterMailDomain\": \"workmail:DeregisterMailDomain\",\n    \"DescribeGroup\": \"workmail:DescribeGroup\",\n    \"DescribeInboundDmarcSettings\": \"workmail:DescribeInboundDmarcSettings\",\n    \"DescribeMailboxExportJob\": \"workmail:DescribeMailboxExportJob\",\n    \"DescribeOrganization\": \"workmail:DescribeOrganization\",\n    \"DescribeResource\": \"workmail:DescribeResource\",\n    \"DescribeUser\": \"workmail:DescribeUser\",\n    \"DisassociateDelegateFromResource\": \"workmail:DisassociateDelegateFromResource\",\n    \"DisassociateMemberFromGroup\": \"workmail:DisassociateMemberFromGroup\",\n    \"GetAccessControlEffect\": \"workmail:GetAccessControlEffect\",\n    \"GetDefaultRetentionPolicy\": \"workmail:GetDefaultRetentionPolicy\",\n    \"GetMailDomain\": \"workmail:GetMailDomain\",\n    \"GetMailboxDetails\": \"workmail:GetMailboxDetails\",\n    \"GetMobileDeviceAccessEffect\": \"workmail:GetMobileDeviceAccessEffect\",\n    \"GetMobileDeviceAccessOverride\": \"workmail:GetMobileDeviceAccessOverride\",\n    \"ListAccessControlRules\": \"workmail:ListAccessControlRules\",\n    \"ListAliases\": \"workmail:ListAliases\",\n    \"ListGroupMembers\": \"workmail:ListGroupMembers\",\n    \"ListGroups\": \"workmail:ListGroups\",\n    \"ListMailDomains\": \"workmail:ListMailDomains\",\n    \"ListMailboxExportJobs\": \"workmail:ListMailboxExportJobs\",\n    \"ListMailboxPermissions\": \"workmail:ListMailboxPermissions\",\n    \"ListMobileDeviceAccessOverrides\": \"workmail:ListMobileDeviceAccessOverrides\",\n    \"ListMobileDeviceAccessRules\": \"workmail:ListMobileDeviceAccessRules\",\n    \"ListOrganizations\": \"workmail:ListOrganizations\",\n    \"ListResourceDelegates\": \"workmail:ListResourceDelegates\",\n    \"ListResources\": \"workmail:ListResources\",\n    \"ListTagsForResource\": \"workmail:ListTagsForResource\",\n    \"ListUsers\": \"workmail:ListUsers\",\n    \"PutAccessControlRule\": \"workmail:PutAccessControlRule\",\n    \"PutInboundDmarcSettings\": \"workmail:PutInboundDmarcSettings\",\n    \"PutMailboxPermissions\": \"workmail:PutMailboxPermissions\",\n    \"PutMobileDeviceAccessOverride\": \"workmail:PutMobileDeviceAccessOverride\",\n    \"PutRetentionPolicy\": \"workmail:PutRetentionPolicy\",\n    \"RegisterMailDomain\": \"workmail:RegisterMailDomain\",\n    \"RegisterToWorkMail\": \"workmail:RegisterToWorkMail\",\n    \"ResetPassword\": \"workmail:ResetPassword\",\n    \"StartMailboxExportJob\": \"workmail:StartMailboxExportJob\",\n    \"TagResource\": \"workmail:TagResource\",\n    \"UntagResource\": \"workmail:UntagResource\",\n    \"UpdateDefaultMailDomain\": \"workmail:UpdateDefaultMailDomain\",\n    \"UpdateMailboxQuota\": \"workmail:UpdateMailboxQuota\",\n    \"UpdateMobileDeviceAccessRule\": \"workmail:UpdateMobileDeviceAccessRule\",\n    \"UpdatePrimaryEmailAddress\": \"workmail:UpdatePrimaryEmailAddress\",\n    \"UpdateResource\": \"workmail:UpdateResource\"\n  },\n  \"workmailmessageflow\": {\n    \"GetRawMessageContent\": \"workmailmessageflow:GetRawMessageContent\",\n    \"PutRawMessageContent\": \"workmailmessageflow:PutRawMessageContent\"\n  },\n  \"workspaces\": {\n    \"AssociateConnectionAlias\": \"workspaces:AssociateConnectionAlias\",\n    \"AssociateIpGroups\": \"workspaces:AssociateIpGroups\",\n    \"AuthorizeIpRules\": \"workspaces:AuthorizeIpRules\",\n    \"CopyWorkspaceImage\": \"workspaces:CopyWorkspaceImage\",\n    \"CreateConnectionAlias\": \"workspaces:CreateConnectionAlias\",\n    \"CreateIpGroup\": \"workspaces:CreateIpGroup\",\n    \"CreateTags\": \"workspaces:CreateTags\",\n    \"CreateUpdatedWorkspaceImage\": \"workspaces:CreateUpdatedWorkspaceImage\",\n    \"CreateWorkspaceBundle\": \"workspaces:CreateWorkspaceBundle\",\n    \"CreateWorkspaces\": \"workspaces:CreateWorkspaces\",\n    \"DeleteConnectionAlias\": \"workspaces:DeleteConnectionAlias\",\n    \"DeleteIpGroup\": \"workspaces:DeleteIpGroup\",\n    \"DeleteTags\": \"workspaces:DeleteTags\",\n    \"DeleteWorkspaceBundle\": \"workspaces:DeleteWorkspaceBundle\",\n    \"DeleteWorkspaceImage\": \"workspaces:DeleteWorkspaceImage\",\n    \"DeregisterWorkspaceDirectory\": \"workspaces:DeregisterWorkspaceDirectory\",\n    \"DescribeAccount\": \"workspaces:DescribeAccount\",\n    \"DescribeAccountModifications\": \"workspaces:DescribeAccountModifications\",\n    \"DescribeClientProperties\": \"workspaces:DescribeClientProperties\",\n    \"DescribeConnectionAliasPermissions\": \"workspaces:DescribeConnectionAliasPermissions\",\n    \"DescribeConnectionAliases\": \"workspaces:DescribeConnectionAliases\",\n    \"DescribeIpGroups\": \"workspaces:DescribeIpGroups\",\n    \"DescribeTags\": \"workspaces:DescribeTags\",\n    \"DescribeWorkspaceBundles\": \"workspaces:DescribeWorkspaceBundles\",\n    \"DescribeWorkspaceDirectories\": \"workspaces:DescribeWorkspaceDirectories\",\n    \"DescribeWorkspaceImagePermissions\": \"workspaces:DescribeWorkspaceImagePermissions\",\n    \"DescribeWorkspaceImages\": \"workspaces:DescribeWorkspaceImages\",\n    \"DescribeWorkspaceSnapshots\": \"workspaces:DescribeWorkspaceSnapshots\",\n    \"DescribeWorkspaces\": \"workspaces:DescribeWorkspaces\",\n    \"DescribeWorkspacesConnectionStatus\": \"workspaces:DescribeWorkspacesConnectionStatus\",\n    \"DisassociateConnectionAlias\": \"workspaces:DisassociateConnectionAlias\",\n    \"DisassociateIpGroups\": \"workspaces:DisassociateIpGroups\",\n    \"ImportWorkspaceImage\": \"workspaces:ImportWorkspaceImage\",\n    \"ListAvailableManagementCidrRanges\": \"workspaces:ListAvailableManagementCidrRanges\",\n    \"MigrateWorkspace\": \"workspaces:MigrateWorkspace\",\n    \"ModifyAccount\": \"workspaces:ModifyAccount\",\n    \"ModifyClientProperties\": \"workspaces:ModifyClientProperties\",\n    \"ModifySelfservicePermissions\": \"workspaces:ModifySelfservicePermissions\",\n    \"ModifyWorkspaceAccessProperties\": \"workspaces:ModifyWorkspaceAccessProperties\",\n    \"ModifyWorkspaceCreationProperties\": \"workspaces:ModifyWorkspaceCreationProperties\",\n    \"ModifyWorkspaceProperties\": \"workspaces:ModifyWorkspaceProperties\",\n    \"ModifyWorkspaceState\": \"workspaces:ModifyWorkspaceState\",\n    \"RebootWorkspaces\": \"workspaces:RebootWorkspaces\",\n    \"RebuildWorkspaces\": \"workspaces:RebuildWorkspaces\",\n    \"RegisterWorkspaceDirectory\": \"workspaces:RegisterWorkspaceDirectory\",\n    \"RestoreWorkspace\": \"workspaces:RestoreWorkspace\",\n    \"RevokeIpRules\": \"workspaces:RevokeIpRules\",\n    \"StartWorkspaces\": \"workspaces:StartWorkspaces\",\n    \"StopWorkspaces\": \"workspaces:StopWorkspaces\",\n    \"TerminateWorkspaces\": \"workspaces:TerminateWorkspaces\",\n    \"UpdateConnectionAliasPermission\": \"workspaces:UpdateConnectionAliasPermission\",\n    \"UpdateRulesOfIpGroup\": \"workspaces:UpdateRulesOfIpGroup\",\n    \"UpdateWorkspaceBundle\": \"workspaces:UpdateWorkspaceBundle\",\n    \"UpdateWorkspaceImagePermission\": \"workspaces:UpdateWorkspaceImagePermission\"\n  },\n  \"workspaces-web\": {\n    \"AssociateBrowserSettings\": \"workspaces-web:AssociateBrowserSettings\",\n    \"AssociateNetworkSettings\": \"workspaces-web:AssociateNetworkSettings\",\n    \"AssociateTrustStore\": \"workspaces-web:AssociateTrustStore\",\n    \"AssociateUserSettings\": \"workspaces-web:AssociateUserSettings\",\n    \"CreateBrowserSettings\": \"workspaces-web:CreateBrowserSettings\",\n    \"CreateIdentityProvider\": \"workspaces-web:CreateIdentityProvider\",\n    \"CreateNetworkSettings\": \"workspaces-web:CreateNetworkSettings\",\n    \"CreatePortal\": \"workspaces-web:CreatePortal\",\n    \"CreateTrustStore\": \"workspaces-web:CreateTrustStore\",\n    \"CreateUserSettings\": \"workspaces-web:CreateUserSettings\",\n    \"DeleteBrowserSettings\": \"workspaces-web:DeleteBrowserSettings\",\n    \"DeleteIdentityProvider\": \"workspaces-web:DeleteIdentityProvider\",\n    \"DeleteNetworkSettings\": \"workspaces-web:DeleteNetworkSettings\",\n    \"DeletePortal\": \"workspaces-web:DeletePortal\",\n    \"DeleteTrustStore\": \"workspaces-web:DeleteTrustStore\",\n    \"DeleteUserSettings\": \"workspaces-web:DeleteUserSettings\",\n    \"DisassociateBrowserSettings\": \"workspaces-web:DisassociateBrowserSettings\",\n    \"DisassociateNetworkSettings\": \"workspaces-web:DisassociateNetworkSettings\",\n    \"DisassociateTrustStore\": \"workspaces-web:DisassociateTrustStore\",\n    \"DisassociateUserSettings\": \"workspaces-web:DisassociateUserSettings\",\n    \"GetBrowserSettings\": \"workspaces-web:GetBrowserSettings\",\n    \"GetIdentityProvider\": \"workspaces-web:GetIdentityProvider\",\n    \"GetNetworkSettings\": \"workspaces-web:GetNetworkSettings\",\n    \"GetPortal\": \"workspaces-web:GetPortal\",\n    \"GetPortalServiceProviderMetadata\": \"workspaces-web:GetPortalServiceProviderMetadata\",\n    \"GetTrustStore\": \"workspaces-web:GetTrustStore\",\n    \"GetTrustStoreCertificate\": \"workspaces-web:GetTrustStoreCertificate\",\n    \"GetUserSettings\": \"workspaces-web:GetUserSettings\",\n    \"ListBrowserSettings\": \"workspaces-web:ListBrowserSettings\",\n    \"ListIdentityProviders\": \"workspaces-web:ListIdentityProviders\",\n    \"ListNetworkSettings\": \"workspaces-web:ListNetworkSettings\",\n    \"ListPortals\": \"workspaces-web:ListPortals\",\n    \"ListTagsForResource\": \"workspaces-web:ListTagsForResource\",\n    \"ListTrustStoreCertificates\": \"workspaces-web:ListTrustStoreCertificates\",\n    \"ListTrustStores\": \"workspaces-web:ListTrustStores\",\n    \"ListUserSettings\": \"workspaces-web:ListUserSettings\",\n    \"TagResource\": \"workspaces-web:TagResource\",\n    \"UntagResource\": \"workspaces-web:UntagResource\",\n    \"UpdateBrowserSettings\": \"workspaces-web:UpdateBrowserSettings\",\n    \"UpdateIdentityProvider\": \"workspaces-web:UpdateIdentityProvider\",\n    \"UpdateNetworkSettings\": \"workspaces-web:UpdateNetworkSettings\",\n    \"UpdatePortal\": \"workspaces-web:UpdatePortal\",\n    \"UpdateTrustStore\": \"workspaces-web:UpdateTrustStore\",\n    \"UpdateUserSettings\": \"workspaces-web:UpdateUserSettings\"\n  },\n  \"xray\": {\n    \"BatchGetTraces\": \"xray:BatchGetTraces\",\n    \"CreateGroup\": \"xray:CreateGroup\",\n    \"CreateSamplingRule\": \"xray:CreateSamplingRule\",\n    \"DeleteGroup\": \"xray:DeleteGroup\",\n    \"DeleteSamplingRule\": \"xray:DeleteSamplingRule\",\n    \"GetEncryptionConfig\": \"xray:GetEncryptionConfig\",\n    \"GetGroup\": \"xray:GetGroup\",\n    \"GetGroups\": \"xray:GetGroups\",\n    \"GetInsight\": \"xray:GetInsight\",\n    \"GetInsightEvents\": \"xray:GetInsightEvents\",\n    \"GetInsightImpactGraph\": \"xray:GetInsightImpactGraph\",\n    \"GetInsightSummaries\": \"xray:GetInsightSummaries\",\n    \"GetSamplingRules\": \"xray:GetSamplingRules\",\n    \"GetSamplingStatisticSummaries\": \"xray:GetSamplingStatisticSummaries\",\n    \"GetSamplingTargets\": \"xray:GetSamplingTargets\",\n    \"GetServiceGraph\": \"xray:GetServiceGraph\",\n    \"GetTimeSeriesServiceStatistics\": \"xray:GetTimeSeriesServiceStatistics\",\n    \"GetTraceGraph\": \"xray:GetTraceGraph\",\n    \"GetTraceSummaries\": \"xray:GetTraceSummaries\",\n    \"ListTagsForResource\": \"xray:ListTagsForResource\",\n    \"PutEncryptionConfig\": \"xray:PutEncryptionConfig\",\n    \"PutTelemetryRecords\": \"xray:PutTelemetryRecords\",\n    \"PutTraceSegments\": \"xray:PutTraceSegments\",\n    \"TagResource\": \"xray:TagResource\",\n    \"UntagResource\": \"xray:UntagResource\",\n    \"UpdateGroup\": \"xray:UpdateGroup\",\n    \"UpdateSamplingRule\": \"xray:UpdateSamplingRule\"\n  }\n}\n"
  },
  {
    "path": "chalice/policy.py",
    "content": "\"\"\"Policy generator based on allowed API calls.\n\nThis module will take a set of API calls for services\nand make a best effort attempt to generate an IAM policy\nfor you.\n\n\"\"\"\nfrom __future__ import print_function\nimport os\nimport json\nimport uuid\n\nfrom typing import Optional, Any, List, Dict, Set  # noqa\nimport botocore.session\n\nfrom chalice.constants import (\n    CLOUDWATCH_LOGS, VPC_ATTACH_POLICY, XRAY_POLICY)\nfrom chalice.utils import OSUtils  # noqa\nfrom chalice.config import Config  # noqa\n\nAPIPolicyT = Dict[str, Dict[str, str]]\nCustomPolicyT = Dict[str, Dict[str, List[str]]]\n\n\ndef policy_from_source_code(source_code: str) -> Dict[str, Any]:\n    from chalice.analyzer import get_client_calls_for_app\n    client_calls = get_client_calls_for_app(source_code)\n    builder = PolicyBuilder()\n    policy = builder.build_policy_from_api_calls(client_calls)\n    return policy\n\n\ndef load_api_policy_actions() -> APIPolicyT:\n    return _load_json_file('policies.json')\n\n\ndef load_custom_policy_actions() -> CustomPolicyT:\n    return _load_json_file('policies-extra.json')\n\n\ndef _load_json_file(relative_filename: str) -> Dict[str, Any]:\n    policy_json = os.path.join(\n        os.path.dirname(os.path.abspath(__file__)),\n        relative_filename)\n    with open(policy_json) as f:\n        return json.loads(f.read())\n\n\ndef diff_policies(old: Dict[str, Any],\n                  new: Dict[str, Any]) -> Dict[str, Set[str]]:\n    diff = {}\n    old_actions = _create_simple_format(old)\n    new_actions = _create_simple_format(new)\n    removed = old_actions - new_actions\n    added = new_actions - old_actions\n    if removed:\n        diff['removed'] = removed\n    if added:\n        diff['added'] = added\n    return diff\n\n\ndef _create_simple_format(policy: Dict[str, Any]) -> Set[str]:\n    # This won't be sufficient is the analyzer is ever able\n    # to work out which resources you're accessing.\n    actions: Set[str] = set()\n    for statement in policy['Statement']:\n        actions.update(statement['Action'])\n    return actions\n\n\nclass AppPolicyGenerator(object):\n    def __init__(self, osutils: OSUtils) -> None:\n        self._osutils = osutils\n\n    def generate_policy(self, config: Config) -> Dict[str, Any]:\n        \"\"\"Auto generate policy for an application.\"\"\"\n        # Admittedly, this is pretty bare bones logic for the time\n        # being.  All it really does it work out, given a Config instance,\n        # which files need to analyzed and then delegates to the\n        # appropriately analyzer functions to do the real work.\n        # This may change in the future.\n        app_py = os.path.join(config.project_dir, 'app.py')\n        assert self._osutils.file_exists(app_py)\n        app_source = self._osutils.get_file_contents(app_py, binary=False)\n        app_policy = policy_from_source_code(app_source)\n        app_policy['Statement'].append(CLOUDWATCH_LOGS)\n        if config.subnet_ids and config.security_group_ids:\n            app_policy['Statement'].append(VPC_ATTACH_POLICY)\n        if config.xray_enabled:\n            app_policy['Statement'].append(XRAY_POLICY)\n        return app_policy\n\n\nclass PolicyBuilder(object):\n    VERSION = '2012-10-17'\n\n    def __init__(self,\n                 session: Optional[Any] = None,\n                 api_policy_actions: Optional[APIPolicyT] = None,\n                 custom_policy_actions: Optional[CustomPolicyT] = None\n                 ) -> None:\n        if session is None:\n            session = botocore.session.get_session()\n        # The difference between api_policy_actions and custom_policy_actions\n        # is that api_policy_actions correspond to the 1-1 method to API calls\n        # exposed in boto3/botocore clients whereas custom_policy_actions\n        # correspond to method names that represent high level abstractions\n        # that are typically hand written (e.g s3.download_file()).  These\n        # are kept as separate files because we manage these files\n        # separately.\n        if api_policy_actions is None:\n            api_policy_actions = load_api_policy_actions()\n        if custom_policy_actions is None:\n            custom_policy_actions = load_custom_policy_actions()\n        self._session = session\n        self._api_policy_actions = api_policy_actions\n        self._custom_policy_actions = custom_policy_actions\n\n    def build_policy_from_api_calls(self,\n                                    client_calls: Dict[str, Set[str]]\n                                    ) -> Dict[str, Any]:\n        statements = self._build_statements_from_client_calls(client_calls)\n        policy = {\n            'Version': self.VERSION,\n            'Statement': statements\n        }\n        return policy\n\n    def _build_statements_from_client_calls(self,\n                                            client_calls: Dict[str, Set[str]]\n                                            ) -> List[Dict[str, Any]]:\n        statements = []\n        # client_calls = service_name -> set([method_calls])\n        for service in sorted(client_calls):\n            api_actions = self._get_actions_from_api_calls(\n                service, client_calls)\n            custom_actions = self._get_actions_from_high_level_calls(\n                service, client_calls)\n            actions = api_actions + custom_actions\n            if actions:\n                statements.append({\n                    'Effect': 'Allow',\n                    'Action': actions,\n                    # Probably impossible, but it would be nice\n                    # to even keep track of what resources are used\n                    # so we can create ARNs and further restrict the policies.\n                    'Resource': ['*'],\n                    'Sid': str(uuid.uuid4()).replace('-', ''),\n                })\n        return statements\n\n    def _get_actions_from_api_calls(self,\n                                    service: str,\n                                    client_calls: Dict[str, Set[str]]\n                                    ) -> List[str]:\n        if service not in self._api_policy_actions:\n            print(\"Unsupported service for auto policy generation: %s\"\n                  % service)\n            return []\n        service_actions = self._api_policy_actions[service]\n        method_calls = client_calls[service]\n        # Next thing we need to do is convert the method_name to\n        # MethodName.  To do this reliably we're going to use\n        # botocore clients.\n        client = self._session.create_client(service,\n                                             region_name='us-east-1')\n        mapping = client.meta.method_to_api_mapping\n        actions = [service_actions[mapping[method_name]]\n                   for method_name in method_calls\n                   if mapping.get(method_name) in service_actions]\n        actions.sort()\n        return actions\n\n    def _get_actions_from_high_level_calls(self,\n                                           service: str,\n                                           client_calls: Dict[str, Set[str]]\n                                           ) -> List[str]:\n        # This gets any actions associated with high level abstractions\n        # e.g s3.download_file(), s3.upload_file(), etc.\n        if service not in self._custom_policy_actions:\n            # We don't warn the user in this case but it's unlikely there\n            # are high level abstractions for a service, this only applies\n            # to s3, dynamodb, and a few other services.\n            return []\n        service_actions = self._custom_policy_actions[service]\n        method_calls = client_calls[service]\n        actions: Set[str] = set()\n        for method_name in method_calls:\n            if method_name in service_actions:\n                actions.update(service_actions[method_name])\n        return list(sorted(actions))\n"
  },
  {
    "path": "chalice/py.typed",
    "content": "# Marker file for PEP 561. This package provides inline type annotations.\n"
  },
  {
    "path": "chalice/templates/0000-rest-api/.chalice/config.json",
    "content": "{\n  \"version\": \"2.0\",\n  \"app_name\": \"{{app_name}}\",\n  \"stages\": {\n    \"dev\": {\n      \"api_gateway_stage\": \"api\"\n    }\n  }\n}\n"
  },
  {
    "path": "chalice/templates/0000-rest-api/.gitignore",
    "content": ".chalice/deployments/\n.chalice/venv/\n"
  },
  {
    "path": "chalice/templates/0000-rest-api/app.py",
    "content": "from chalice import Chalice\n\napp = Chalice(app_name='{{app_name}}')\n\n\n@app.route('/')\ndef index():\n    return {'hello': 'world'}\n\n\n# The view function above will return {\"hello\": \"world\"}\n# whenever you make an HTTP GET request to '/'.\n#\n# Here are a few more examples:\n#\n# @app.route('/hello/{name}')\n# def hello_name(name):\n#    # '/hello/james' -> {\"hello\": \"james\"}\n#    return {'hello': name}\n#\n# @app.route('/users', methods=['POST'])\n# def create_user():\n#     # This is the JSON body the user sent in their POST request.\n#     user_as_json = app.current_request.json_body\n#     # We'll echo the json body back to the user in a 'user' key.\n#     return {'user': user_as_json}\n#\n# See the README documentation for more examples.\n#\n"
  },
  {
    "path": "chalice/templates/0000-rest-api/chalicelib/__init__.py",
    "content": ""
  },
  {
    "path": "chalice/templates/0000-rest-api/metadata.json",
    "content": "{\"description\": \"REST API\"}\n"
  },
  {
    "path": "chalice/templates/0000-rest-api/requirements-dev.txt",
    "content": "chalice=={{chalice_version}}\npytest\n"
  },
  {
    "path": "chalice/templates/0000-rest-api/requirements.txt",
    "content": ""
  },
  {
    "path": "chalice/templates/0000-rest-api/tests/__init__.py",
    "content": ""
  },
  {
    "path": "chalice/templates/0000-rest-api/tests/test_app.py",
    "content": "from chalice.test import Client\nfrom app import app\n\n\ndef test_index():\n    with Client(app) as client:\n        response = client.http.get('/')\n        assert response.json_body == {'hello': 'world'}\n"
  },
  {
    "path": "chalice/templates/0002-s3-event-handler/.chalice/config.json",
    "content": "{\n  \"version\": \"2.0\",\n  \"app_name\": \"{{app_name}}\",\n  \"stages\": {\n    \"dev\": {\n      \"api_gateway_stage\": \"api\"\n    }\n  }\n}\n"
  },
  {
    "path": "chalice/templates/0002-s3-event-handler/.gitignore",
    "content": ".chalice/deployments/\n.chalice/venv/\n"
  },
  {
    "path": "chalice/templates/0002-s3-event-handler/app.py",
    "content": "import os\n\nfrom chalice import Chalice\n\napp = Chalice(app_name='{{app_name}}')\napp.debug = True\n\n\n# Set the value of APP_BUCKET_NAME in the .chalice/config.json file.\nS3_BUCKET = os.environ.get('APP_BUCKET_NAME', '')\n\n\n@app.on_s3_event(bucket=S3_BUCKET, events=['s3:ObjectCreated:*'])\ndef s3_handler(event):\n    app.log.debug(\"Received event for bucket: %s, key: %s\",\n                  event.bucket, event.key)\n"
  },
  {
    "path": "chalice/templates/0002-s3-event-handler/chalicelib/__init__.py",
    "content": ""
  },
  {
    "path": "chalice/templates/0002-s3-event-handler/metadata.json",
    "content": "{\"description\": \"S3 Event Handler\"}\n"
  },
  {
    "path": "chalice/templates/0002-s3-event-handler/requirements-dev.txt",
    "content": "chalice\npytest\n"
  },
  {
    "path": "chalice/templates/0002-s3-event-handler/requirements.txt",
    "content": ""
  },
  {
    "path": "chalice/templates/0002-s3-event-handler/tests/__init__.py",
    "content": ""
  },
  {
    "path": "chalice/templates/0002-s3-event-handler/tests/test_app.py",
    "content": "from chalice.test import Client\nfrom app import app\n\n\ndef test_s3_handler():\n    with Client(app) as client:\n        event = client.events.generate_s3_event(\n            bucket='mybucket', key='mykey')\n        client.lambda_.invoke('s3_handler', event)\n"
  },
  {
    "path": "chalice/templates/0007-lambda-only/.chalice/config.json",
    "content": "{\n  \"version\": \"2.0\",\n  \"app_name\": \"{{app_name}}\",\n  \"stages\": {\n    \"dev\": {\n      \"api_gateway_stage\": \"api\"\n    }\n  }\n}\n"
  },
  {
    "path": "chalice/templates/0007-lambda-only/.gitignore",
    "content": ".chalice/deployments/\n.chalice/venv/\n"
  },
  {
    "path": "chalice/templates/0007-lambda-only/app.py",
    "content": "from chalice import Chalice\n\napp = Chalice(app_name='{{app_name}}')\n\n\n@app.lambda_function()\ndef first_function(event, context):\n    return {'hello': 'world'}\n\n\n@app.lambda_function()\ndef second_function(event, context):\n    return {'hello': 'world2'}\n"
  },
  {
    "path": "chalice/templates/0007-lambda-only/chalicelib/__init__.py",
    "content": ""
  },
  {
    "path": "chalice/templates/0007-lambda-only/metadata.json",
    "content": "{\"description\": \"Lambda Functions only\"}\n"
  },
  {
    "path": "chalice/templates/0007-lambda-only/requirements-dev.txt",
    "content": "chalice\npytest\n"
  },
  {
    "path": "chalice/templates/0007-lambda-only/requirements.txt",
    "content": ""
  },
  {
    "path": "chalice/templates/0007-lambda-only/tests/__init__.py",
    "content": ""
  },
  {
    "path": "chalice/templates/0007-lambda-only/tests/test_app.py",
    "content": "from chalice.test import Client\nfrom app import app\n\n\ndef test_index():\n    with Client(app) as client:\n        response = client.lambda_.invoke('first_function', {})\n        assert response.payload == {'hello': 'world'}\n"
  },
  {
    "path": "chalice/templates/0009-legacy/.chalice/config.json",
    "content": "{\n  \"version\": \"2.0\",\n  \"app_name\": \"{{app_name}}\",\n  \"stages\": {\n    \"dev\": {\n      \"api_gateway_stage\": \"api\"\n    }\n  }\n}\n"
  },
  {
    "path": "chalice/templates/0009-legacy/.gitignore",
    "content": ".chalice/deployments/\n.chalice/venv/\n"
  },
  {
    "path": "chalice/templates/0009-legacy/app.py",
    "content": "from chalice import Chalice\n\napp = Chalice(app_name='{{app_name}}')\n\n\n@app.route('/')\ndef index():\n    return {'hello': 'world'}\n\n\n# The view function above will return {\"hello\": \"world\"}\n# whenever you make an HTTP GET request to '/'.\n#\n# Here are a few more examples:\n#\n# @app.route('/hello/{name}')\n# def hello_name(name):\n#    # '/hello/james' -> {\"hello\": \"james\"}\n#    return {'hello': name}\n#\n# @app.route('/users', methods=['POST'])\n# def create_user():\n#     # This is the JSON body the user sent in their POST request.\n#     user_as_json = app.current_request.json_body\n#     # We'll echo the json body back to the user in a 'user' key.\n#     return {'user': user_as_json}\n#\n# See the README documentation for more examples.\n#\n"
  },
  {
    "path": "chalice/templates/0009-legacy/metadata.json",
    "content": "{\"description\": \"Legacy REST API Template\"}\n"
  },
  {
    "path": "chalice/templates/0009-legacy/requirements.txt",
    "content": ""
  },
  {
    "path": "chalice/templates/6001-cdk-ddb/README.rst",
    "content": "REST API backed by Amazon DynamoDB\n==================================\n\nThis template provides a REST API that's backed by an Amazon DynamoDB table.\nThis application is deployed using the AWS CDK.\n\nFor more information, see the `Deploying with the AWS CDK\n<https://aws.github.io/chalice/tutorials/cdk.html>`__ tutorial.\n\n\nQuickstart\n----------\n\nFirst, you'll need to install the AWS CDK if you haven't already.\nThe CDK requires Node.js and npm to run.\nSee the `Getting started with the AWS CDK\n<https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html>`__ for\nmore details.\n\n::\n\n  $ npm install -g aws-cdk\n\nNext you'll need to install the requirements for the project.\n\n::\n\n  $ pip install -r requirements.txt\n\nThere's also separate requirements files in the ``infrastructure``\nand ``runtime`` directories if you'd prefer to have separate virtual\nenvironments for your CDK and Chalice app.\n\nTo deploy the application, ``cd`` to the ``infrastructure`` directory.\nIf this is you're first time using the CDK you'll need to bootstrap\nyour environment.\n\n::\n\n  $ cdk bootstrap\n\nThen you can deploy your application using the CDK.\n\n::\n\n  $ cdk deploy\n\n\nProject layout\n--------------\n\nThis project template combines a CDK application and a Chalice application.\nThese correspond to the ``infrastructure`` and ``runtime`` directory\nrespectively.  To run any CDK CLI commands, ensure you're in the\n``infrastructure`` directory, and to run any Chalice CLI commands ensure\nyou're in the ``runtime`` directory.\n"
  },
  {
    "path": "chalice/templates/6001-cdk-ddb/infrastructure/app.py",
    "content": "#!/usr/bin/env python3\ntry:\n    from aws_cdk import core as cdk\nexcept ImportError:\n    import aws_cdk as cdk\nfrom stacks.chaliceapp import ChaliceApp\n\napp = cdk.App()\nChaliceApp(app, '{{app_name}}')\n\napp.synth()\n"
  },
  {
    "path": "chalice/templates/6001-cdk-ddb/infrastructure/cdk.json",
    "content": "{\n  \"app\": \"python3 app.py\",\n  \"context\": {\n    \"aws-cdk:enableDiffNoFail\": \"true\",\n    \"@aws-cdk/core:stackRelativeExports\": \"true\"\n  }\n}\n"
  },
  {
    "path": "chalice/templates/6001-cdk-ddb/infrastructure/requirements.txt",
    "content": "aws-cdk-lib>2.0,<3.0\n"
  },
  {
    "path": "chalice/templates/6001-cdk-ddb/infrastructure/stacks/__init__.py",
    "content": ""
  },
  {
    "path": "chalice/templates/6001-cdk-ddb/infrastructure/stacks/chaliceapp.py",
    "content": "import os\n\nfrom aws_cdk import aws_dynamodb as dynamodb\n\ntry:\n    from aws_cdk import core as cdk\nexcept ImportError:\n    import aws_cdk as cdk\n\nfrom chalice.cdk import Chalice\n\n\nRUNTIME_SOURCE_DIR = os.path.join(\n    os.path.dirname(os.path.dirname(__file__)), os.pardir, 'runtime')\n\n\nclass ChaliceApp(cdk.Stack):\n\n    def __init__(self, scope, id, **kwargs):\n        super().__init__(scope, id, **kwargs)\n        self.dynamodb_table = self._create_ddb_table()\n        self.chalice = Chalice(\n            self, 'ChaliceApp', source_dir=RUNTIME_SOURCE_DIR,\n            stage_config={\n                'environment_variables': {\n                    'APP_TABLE_NAME': self.dynamodb_table.table_name\n                }\n            }\n        )\n        self.dynamodb_table.grant_read_write_data(\n            self.chalice.get_role('DefaultRole')\n        )\n\n    def _create_ddb_table(self):\n        dynamodb_table = dynamodb.Table(\n            self, 'AppTable',\n            partition_key=dynamodb.Attribute(\n                name='PK', type=dynamodb.AttributeType.STRING),\n            sort_key=dynamodb.Attribute(\n                name='SK', type=dynamodb.AttributeType.STRING\n            ),\n            removal_policy=cdk.RemovalPolicy.DESTROY)\n        cdk.CfnOutput(self, 'AppTableName',\n                      value=dynamodb_table.table_name)\n        return dynamodb_table\n"
  },
  {
    "path": "chalice/templates/6001-cdk-ddb/metadata.json",
    "content": "{\"description\": \"[CDK] Rest API with a DynamoDB table\"}\n"
  },
  {
    "path": "chalice/templates/6001-cdk-ddb/requirements.txt",
    "content": "-r infrastructure/requirements.txt\n-r runtime/requirements.txt\n"
  },
  {
    "path": "chalice/templates/6001-cdk-ddb/runtime/.chalice/config.json",
    "content": "{\n  \"version\": \"2.0\",\n  \"app_name\": \"{{app_name}}\",\n  \"stages\": {\n    \"dev\": {\n      \"api_gateway_stage\": \"api\",\n      \"lambda_functions\": {\n        \"api_handler\": {\n          \"environment_variables\": {\n            \"APP_TABLE_NAME\": \"\"\n\t  }\n\t}\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "chalice/templates/6001-cdk-ddb/runtime/.gitignore",
    "content": ".chalice/deployments/\n.chalice/venv/\n"
  },
  {
    "path": "chalice/templates/6001-cdk-ddb/runtime/app.py",
    "content": "import os\nimport boto3\nfrom chalice import Chalice\n\n\napp = Chalice(app_name='{{app_name}}')\ndynamodb = boto3.resource('dynamodb')\ndynamodb_table = dynamodb.Table(os.environ.get('APP_TABLE_NAME', ''))\n\n\n@app.route('/users', methods=['POST'])\ndef create_user():\n    request = app.current_request.json_body\n    item = {\n        'PK': 'User#%s' % request['username'],\n        'SK': 'Profile#%s' % request['username'],\n    }\n    item.update(request)\n    dynamodb_table.put_item(Item=item)\n    return {}\n\n\n@app.route('/users/{username}', methods=['GET'])\ndef get_user(username):\n    key = {\n        'PK': 'User#%s' % username,\n        'SK': 'Profile#%s' % username,\n    }\n    item = dynamodb_table.get_item(Key=key)['Item']\n    del item['PK']\n    del item['SK']\n    return item\n"
  },
  {
    "path": "chalice/templates/6001-cdk-ddb/runtime/requirements.txt",
    "content": "boto3<2.0.0\n"
  },
  {
    "path": "chalice/test.py",
    "content": "from __future__ import annotations\nimport os\nimport json\nimport base64\nimport contextlib\nfrom types import TracebackType\n\nfrom typing import Optional, Type, Generator, Dict, Any, List  # noqa\n\nfrom chalice import Chalice  # noqa\nfrom chalice.config import Config\nfrom chalice.local import LocalGateway, LambdaContext, LocalGatewayException\nfrom chalice.cli.factory import CLIFactory\n\n\nclass FunctionNotFoundError(Exception):\n    pass\n\n\nclass Client(object):\n    def __init__(self,\n                 app: Chalice,\n                 stage_name: str = 'dev',\n                 project_dir: str = '.') -> None:\n        self._app = app\n        self._project_dir = project_dir\n        self._stage_name = stage_name\n        self._http_client: Optional[TestHTTPClient] = None\n        self._events_client: Optional[TestEventsClient] = None\n        self._lambda_client: Optional[TestLambdaClient] = None\n        self._chalice_config_obj: Optional[Config] = None\n        # We have to be careful about not passing in the CLIFactory\n        # because this is a public interface we're exposing and we don't\n        # want the CLIFactory to be part of that.\n        self._cli_factory = CLIFactory(project_dir)\n\n    @property\n    def _chalice_config(self) -> Config:\n        if self._chalice_config_obj is None:\n            try:\n                self._chalice_config_obj = self._cli_factory.create_config_obj(\n                    chalice_stage_name=self._stage_name)\n            except RuntimeError:\n                # Being able to load a valid config is not required to use\n                # the test client.  If you don't have one that you just won't\n                # get any config related data added to your environment.\n                self._chalice_config_obj = Config.create()\n        return self._chalice_config_obj\n\n    @property\n    def http(self) -> TestHTTPClient:\n        if self._http_client is None:\n            self._http_client = TestHTTPClient(self._app, self._chalice_config)\n        return self._http_client\n\n    @property\n    def lambda_(self) -> TestLambdaClient:\n        if self._lambda_client is None:\n            self._lambda_client = TestLambdaClient(\n                self._app, self._chalice_config)\n        return self._lambda_client\n\n    @property\n    def events(self) -> TestEventsClient:\n        if self._events_client is None:\n            self._events_client = TestEventsClient(self._app)\n        return self._events_client\n\n    def __enter__(self) -> Client:\n        return self\n\n    def __exit__(self,\n                 exception_type: Optional[Type[BaseException]],\n                 exception_value: Optional[BaseException],\n                 traceback: Optional[TracebackType],\n                 ) -> None:\n        pass\n\n\nclass BaseClient(object):\n\n    @contextlib.contextmanager\n    def _patched_env_vars(self, environment_variables):  # type: ignore\n        # re: the \"type: ignore\".  Mypy is incredibly pedantic that\n        # os.environ is of type \"_Environ[str]\" and it can't be assigned\n        # an expression of Dict[str, str].  While that's technically \"correct\",\n        # that's way too much effort to get the typing right on this, so\n        # we're just ignoring the type checking for this method.\n        original = os.environ\n        patched = os.environ.copy()\n        patched.update(environment_variables)\n        os.environ = patched\n        try:\n            yield\n        finally:\n            os.environ = original\n\n\nclass TestHTTPClient(BaseClient):\n    def __init__(self, app: Chalice, config: Config) -> None:\n        self._app = app\n        self._config = config\n        self._local_gateway = LocalGateway(app, self._config)\n\n    def request(self,\n                method: str,\n                path: str,\n                headers: Optional[Dict[str, str]] = None,\n                body: bytes = b'') -> HTTPResponse:\n        if headers is None:\n            headers = {}\n        scoped = self._config.scope(self._config.chalice_stage, 'api_handler')\n        with self._patched_env_vars(scoped.environment_variables):\n            try:\n                response = self._local_gateway.handle_request(\n                    method=method.upper(), path=path,\n                    headers=headers, body=body\n                )\n            except LocalGatewayException as e:\n                return self._error_response(e)\n        return HTTPResponse.create_from_dict(response)\n\n    def _error_response(self, e: LocalGatewayException) -> HTTPResponse:\n        return HTTPResponse(\n            headers=e.headers,\n            body=e.body if e.body else b'',\n            status_code=e.CODE\n        )\n\n    def get(self, path: str, **kwargs: Any) -> HTTPResponse:\n        return self.request('GET', path, **kwargs)\n\n    def post(self, path: str, **kwargs: Any) -> HTTPResponse:\n        return self.request('POST', path, **kwargs)\n\n    def put(self, path: str, **kwargs: Any) -> HTTPResponse:\n        return self.request('PUT', path, **kwargs)\n\n    def patch(self, path: str, **kwargs: Any) -> HTTPResponse:\n        return self.request('PATCH', path, **kwargs)\n\n    def options(self, path: str, **kwargs: Any) -> HTTPResponse:\n        return self.request('OPTIONS', path, **kwargs)\n\n    def delete(self, path: str, **kwargs: Any) -> HTTPResponse:\n        return self.request('DELETE', path, **kwargs)\n\n    def head(self, path: str, **kwargs: Any) -> HTTPResponse:\n        return self.request('HEAD', path, **kwargs)\n\n\nclass HTTPResponse(object):\n    def __init__(self,\n                 body: bytes,\n                 headers: Dict[str, str],\n                 status_code: int) -> None:\n        self.body = body\n        self.headers = headers\n        self.status_code = status_code\n\n    @property\n    def json_body(self) -> Any:\n        try:\n            return json.loads(self.body)\n        except ValueError:\n            return None\n\n    @classmethod\n    def create_from_dict(cls, response_dict: Dict[str, Any]) -> HTTPResponse:\n        # Takes the response dict we have to send back to lambda\n        # and exposes it as a python object.\n        if response_dict.get('isBase64Encoded', False):\n            body = base64.b64decode(response_dict['body'])\n        else:\n            body = response_dict['body'].encode('utf-8')\n        combined_headers = response_dict['headers']\n        combined_headers.update(response_dict['multiValueHeaders'])\n        return cls(\n            body=body,\n            status_code=response_dict['statusCode'],\n            headers=combined_headers,\n        )\n\n\nclass TestEventsClient(BaseClient):\n    def __init__(self, app: Chalice) -> None:\n        self._app = app\n\n    def generate_sns_event(self,\n                           message: str,\n                           subject: str = '',\n                           message_attributes: Optional[Dict[str, Any]] = None\n                           ) -> Dict[str, Any]:\n        if message_attributes is None:\n            message_attributes = {\n                'AttributeKey': {\n                    'Type': 'String',\n                    'Value': 'AttributeValue'\n                }\n            }\n        sns_event = {'Records': [{\n            'EventSource': 'aws:sns',\n            'EventSubscriptionArn': 'arn:subscription-arn',\n            'EventVersion': '1.0',\n            'Sns': {\n                'Message': message,\n                'MessageAttributes': message_attributes,\n                'MessageId': 'abcdefgh-51e4-5ae2-9964-b296c8d65d1a',\n                'Signature': 'signature',\n                'SignatureVersion': '1',\n                'SigningCertUrl': (\n                    'https://sns.us-west-2.amazonaws.com/cert.pem'),\n                'Subject': subject,\n                'Timestamp': '2018-06-26T19:41:38.695Z',\n                'TopicArn': 'arn:aws:sns:us-west-2:12345:TopicName',\n                'Type': 'Notification',\n                'UnsubscribeUrl': 'https://unsubscribe-url/'\n            }\n        }]}\n        return sns_event\n\n    def generate_s3_event(self,\n                          bucket: str,\n                          key: str,\n                          event_name: str = 'ObjectCreated:Put'\n                          ) -> Dict[str, Any]:\n        s3_event = {\n            'Records': [\n                {'awsRegion': 'us-west-2',\n                 'eventName': event_name,\n                 'eventSource': 'aws:s3',\n                 'eventTime': '2018-05-22T04:41:23.823Z',\n                 'eventVersion': '2.0',\n                 'requestParameters': {'sourceIPAddress': '1.1.1.1'},\n                 'responseElements': {\n                     'x-amz-id-2': 'request-id-2',\n                     'x-amz-request-id': 'request-id-1'\n                 },\n                 's3': {\n                     'bucket': {\n                         'arn': 'arn:aws:s3:::%s' % bucket,\n                         'name': bucket,\n                         'ownerIdentity': {\n                             'principalId': 'ABCD'\n                         }\n                     },\n                     'configurationId': 'config-id',\n                     'object': {\n                         'eTag': 'd41d8cd98f00b204e9800998ecf8427e',\n                         'key': key,\n                         'sequencer': '005B039F73C627CE8B',\n                         'size': 0\n                     },\n                     's3SchemaVersion': '1.0'\n                 },\n                 'userIdentity': {'principalId': 'AWS:XYZ'}\n                 }\n            ]\n        }\n        return s3_event\n\n    def generate_sqs_event(self,\n                           message_bodies: List[str],\n                           queue_name: str = 'queue-name') -> Dict[str, Any]:\n        records = [{\n            'attributes': {\n                'ApproximateFirstReceiveTimestamp': '1530576251596',\n                'ApproximateReceiveCount': '1',\n                'SenderId': 'sender-id',\n                'SentTimestamp': '1530576251595'\n            },\n            'awsRegion': 'us-west-2',\n            'body': body,\n            'eventSource': 'aws:sqs',\n            'eventSourceARN': 'arn:aws:sqs:us-west-2:12345:%s' % queue_name,\n            'md5OfBody': '754ac2f7a12df38320e0c5eafd060145',\n            'messageAttributes': {},\n            'messageId': 'message-id',\n            'receiptHandle': 'receipt-handle'\n        } for body in message_bodies]\n        sqs_event = {'Records': records}\n        return sqs_event\n\n    def generate_cw_event(self,\n                          source: str,\n                          detail_type: str,\n                          detail: Dict[str, Any],\n                          resources: List[str], region: str = 'us-west-2'\n                          ) -> Dict[str, Any]:\n        event = {\n            \"version\": 0,\n            \"id\": \"7bf73129-1428-4cd3-a780-95db273d1602\",\n            \"detail-type\": detail_type,\n            \"source\": source,\n            \"account\": \"123456789012\",\n            \"time\": \"2015-11-11T21:29:54Z\",\n            \"region\": region,\n            \"resources\": resources,\n            \"detail\": detail,\n        }\n        return event\n\n    def generate_kinesis_event(self, message_bodies: List[bytes],\n                               stream_name: str = 'stream-name'\n                               ) -> Dict[str, Any]:\n        records = [{\n            \"kinesis\": {\n                \"kinesisSchemaVersion\": \"1.0\",\n                \"partitionKey\": \"1\",\n                \"sequenceNumber\": \"12345\",\n                \"data\": base64.b64encode(body).decode('ascii'),\n                \"approximateArrivalTimestamp\": 1545084650.987\n            },\n            \"eventSource\": \"aws:kinesis\",\n            \"eventVersion\": \"1.0\",\n            \"eventID\": \"shardId-000000000006:12345\",\n            \"eventName\": \"aws:kinesis:record\",\n            \"invokeIdentityArn\": \"arn:aws:iam::123:role/lambda-role\",\n            \"awsRegion\": \"us-west-2\",\n            \"eventSourceARN\": (\n                \"arn:aws:kinesis:us-east-2:123:stream/%s\" % stream_name\n            )\n        } for body in message_bodies]\n        return {'Records': records}\n\n\nclass TestLambdaClient(BaseClient):\n    def __init__(self, app: Chalice, config: Config) -> None:\n        self._app = app\n        self._config = config\n\n    def invoke(self,\n               function_name: str,\n               payload: Optional[Any] = None) -> InvokeResponse:\n        if payload is None:\n            payload = {}\n        scoped = self._config.scope(self._config.chalice_stage, function_name)\n        lambda_context = LambdaContext(\n            function_name, memory_size=scoped.lambda_memory_size)\n        if function_name not in self._app.handler_map:\n            raise FunctionNotFoundError(function_name)\n        with self._patched_env_vars(scoped.environment_variables):\n            response = self._app.handler_map[function_name](\n                payload, lambda_context)\n        return InvokeResponse(payload=response)\n\n\nclass InvokeResponse(object):\n    def __init__(self, payload: Any) -> None:\n        self.payload = payload\n"
  },
  {
    "path": "chalice/utils.py",
    "content": "import io\nimport os\nimport zipfile\nimport json\nimport contextlib\nimport tempfile\nimport re\nimport shutil\nimport sys\nimport tarfile\nfrom datetime import datetime, timedelta\nimport subprocess\nfrom os import PathLike  # noqa\n\nfrom collections import OrderedDict  # noqa\nimport click\nfrom typing import IO, Dict, List, Any, Tuple, Iterator, BinaryIO, Text  # noqa\nfrom typing import Optional, Union  # noqa\nfrom typing import MutableMapping, Callable  # noqa\nfrom typing import cast  # noqa\nimport dateutil.parser\nfrom dateutil.tz import tzutc\n\nfrom chalice.constants import WELCOME_PROMPT\n\nOptInt = Optional[int]\nOptBytes = Optional[bytes]\nEnvVars = MutableMapping\nStrPath = Union[str, 'PathLike[str]']\n\n\nclass AbortedError(Exception):\n    pass\n\n\ndef to_cfn_resource_name(name: str) -> str:\n    \"\"\"Transform a name to a valid cfn name.\n\n    This will convert the provided name to a CamelCase name.\n    It's possible that the conversion to a CFN resource name\n    can result in name collisions.  It's up to the caller\n    to handle name collisions appropriately.\n\n    \"\"\"\n    if not name:\n        raise ValueError(\"Invalid name: %r\" % name)\n    word_separators = ['-', '_']\n    for word_separator in word_separators:\n        word_parts = [p for p in name.split(word_separator) if p]\n        name = ''.join([w[0].upper() + w[1:] for w in word_parts])\n    return re.sub(r'[^A-Za-z0-9]+', '', name)\n\n\ndef remove_stage_from_deployed_values(key: str, filename: str) -> None:\n    \"\"\"Delete a top level key from the deployed JSON file.\"\"\"\n    final_values: Dict[str, Any] = {}\n    try:\n        with open(filename, 'r') as f:\n            final_values = json.load(f)\n    except IOError:\n        # If there is no file to delete from, then this funciton is a noop.\n        return\n\n    try:\n        del final_values[key]\n        with open(filename, 'wb') as outfile:\n            data = serialize_to_json(final_values)\n            outfile.write(data.encode('utf-8'))\n    except KeyError:\n        # If they key didn't exist then there is nothing to remove.\n        pass\n\n\ndef record_deployed_values(\n    deployed_values: Dict[str, Any], filename: str\n) -> None:\n    \"\"\"Record deployed values to a JSON file.\n\n    This allows subsequent deploys to lookup previously deployed values.\n\n    \"\"\"\n    final_values: Dict[str, Any] = {}\n    if os.path.isfile(filename):\n        with open(filename, 'r') as f:\n            final_values = json.load(f)\n    final_values.update(deployed_values)\n    with open(filename, 'wb') as outfile:\n        data = serialize_to_json(final_values)\n        outfile.write(data.encode('utf-8'))\n\n\ndef serialize_to_json(data: Any) -> str:\n    \"\"\"Serialize to pretty printed JSON.\n\n    This includes using 2 space indentation, no trailing whitespace, and\n    including a newline at the end of the JSON document.  Useful when you want\n    to serialize JSON to disk.\n\n    \"\"\"\n    return json.dumps(data, indent=2, separators=(',', ': ')) + '\\n'\n\n\nclass ChaliceZipFile(zipfile.ZipFile):\n    \"\"\"Support deterministic zipfile generation.\n\n    Normalizes datetime and permissions.\n\n    \"\"\"\n\n    compression = 0  # Try to make mypy happy.\n    _default_time_time = (1980, 1, 1, 0, 0, 0)\n\n    def __init__(self, *args: Any, **kwargs: Any) -> None:\n        self._osutils = cast(OSUtils, kwargs.pop('osutils', OSUtils()))\n        super(ChaliceZipFile, self).__init__(*args, **kwargs)\n\n    # pylint: disable=W0221\n    def write(\n        self,\n        filename: StrPath,\n        arcname: Optional[StrPath] = None,\n        compress_type: OptInt = None,\n        compresslevel: OptInt = None,\n    ) -> None:\n        # Only supports files, py2.7 and 3 have different signatures.\n        # We know that in our packager code we never call write() on\n        # directories.\n        zinfo = self._create_zipinfo(filename, arcname, compress_type)\n        with open(filename, 'rb') as f:\n            self.writestr(zinfo, f.read())\n\n    def _create_zipinfo(\n        self,\n        filename: StrPath,\n        arcname: Optional[StrPath],\n        compress_type: Optional[int],\n    ) -> zipfile.ZipInfo:\n        # The main thing that prevents deterministic zip file generation\n        # is that the mtime of the file is included in the zip metadata.\n        # We don't actually care what the mtime is when we run on lambda,\n        # so we always set it to the default value (which comes from\n        # zipfile.py).  This ensures that as long as the file contents don't\n        # change (or the permissions) then we'll always generate the exact\n        # same zip file bytes.\n        # We also can't use ZipInfo.from_file(), it's only in python3.\n        st = self._osutils.stat(str(filename))\n        if arcname is None:\n            arcname = filename\n        arcname = self._osutils.normalized_filename(str(arcname))\n        arcname = arcname.lstrip(os.sep)\n        zinfo = zipfile.ZipInfo(arcname, self._default_time_time)\n        # The external_attr needs the upper 16 bits to be the file mode\n        # so we have to shift it up to the right place.\n        zinfo.external_attr = (st.st_mode & 0xFFFF) << 16\n        zinfo.file_size = st.st_size\n        zinfo.compress_type = compress_type or self.compression\n        return zinfo\n\n\ndef create_zip_file(source_dir: str, outfile: str) -> None:\n    \"\"\"Create a zip file from a source input directory.\n\n    This function is intended to be an equivalent to\n    `zip -r`.  You give it a source directory, `source_dir`,\n    and it will recursively zip up the files into a zipfile\n    specified by the `outfile` argument.\n\n    \"\"\"\n    with ChaliceZipFile(\n        outfile, 'w', compression=zipfile.ZIP_DEFLATED, osutils=OSUtils()\n    ) as z:\n        for root, _, filenames in os.walk(source_dir):\n            for filename in filenames:\n                full_name = os.path.join(root, filename)\n                archive_name = os.path.relpath(full_name, source_dir)\n                z.write(full_name, archive_name)\n\n\nclass OSUtils(object):\n    ZIP_DEFLATED = zipfile.ZIP_DEFLATED\n\n    def environ(self) -> MutableMapping:\n        return os.environ\n\n    def open(self, filename: str, mode: str) -> IO:\n        return open(filename, mode)\n\n    def open_zip(\n        self, filename: str, mode: str, compression: int = ZIP_DEFLATED\n    ) -> zipfile.ZipFile:\n        return ChaliceZipFile(\n            filename, mode, compression=compression, osutils=self\n        )\n\n    def remove_file(self, filename: str) -> None:\n        \"\"\"Remove a file, noop if file does not exist.\"\"\"\n        # Unlike os.remove, if the file does not exist,\n        # then this method does nothing.\n        try:\n            os.remove(filename)\n        except OSError:\n            pass\n\n    def file_exists(self, filename: str) -> bool:\n        return os.path.isfile(filename)\n\n    def get_file_contents(\n        self, filename: str, binary: bool = True, encoding: Any = 'utf-8'\n    ) -> str:\n        # It looks like the type definition for io.open is wrong.\n        # the encoding arg is unicode, but the actual type is\n        # Optional[Text].  For now we have to use Any to keep mypy happy.\n        if binary:\n            mode = 'rb'\n            # In binary mode the encoding is not used and most be None.\n            encoding = None\n        else:\n            mode = 'r'\n        with io.open(filename, mode, encoding=encoding) as f:\n            return f.read()\n\n    def set_file_contents(\n        self, filename: str, contents: str, binary: bool = True\n    ) -> None:\n        if binary:\n            mode = 'wb'\n        else:\n            mode = 'w'\n        with open(filename, mode) as f:\n            f.write(contents)\n\n    def extract_zipfile(self, zipfile_path: str, unpack_dir: str) -> None:\n        with zipfile.ZipFile(zipfile_path, 'r') as z:\n            z.extractall(unpack_dir)\n\n    def extract_tarfile(self, tarfile_path: str, unpack_dir: str) -> None:\n        with tarfile.open(tarfile_path, 'r:*') as tar:\n            # In Python 3.12+, there's a `filter` arg where passing a\n            # 'data' value will handle this behavior for us.  To support older\n            # versions of Python we handle this ourselves.  We can't hook\n            # into `extractall` directly so the idea is that we do a separate\n            # validation pass first to ensure there's no files that try\n            # to extract outside of the provided `unpack_dir`.  This is roughly\n            # based off of what's done in the `data_filter()` in Python 3.12.\n            self._validate_safe_extract(tar, unpack_dir)\n            tar.extractall(unpack_dir)\n\n    def _validate_safe_extract(\n        self,\n        tar: tarfile.TarFile,\n        unpack_dir: str\n    ) -> None:\n        for member in tar:\n            self._validate_single_tar_member(member, unpack_dir)\n\n    def _validate_single_tar_member(\n        self,\n        member: tarfile.TarInfo,\n        unpack_dir: str\n    ) -> None:\n        name = member.name\n        dest_path = os.path.realpath(unpack_dir)\n        if name.startswith(('/', os.sep)):\n            name = member.path.lstrip('/' + os.sep)\n        if os.path.isabs(name):\n            raise RuntimeError(f\"Absolute path in tarfile not allowed: {name}\")\n        target_path = os.path.realpath(os.path.join(dest_path, name))\n        # Check we don't escape the destination dir, e.g `../../foo`\n        if os.path.commonpath([target_path, dest_path]) != dest_path:\n            raise RuntimeError(\n                f\"Tar member outside destination dir: {target_path}\")\n        # If we're dealing with a member that's some type of link, ensure\n        # it doesn't point to anything outside of the destination dir.\n        if member.islnk() or member.issym():\n            if os.path.abspath(member.linkname):\n                raise RuntimeError(f\"Symlink to abspath: {member.linkname}\")\n            if member.issym():\n                target_path = os.path.join(\n                    dest_path,\n                    os.path.dirname(name),\n                    member.linkname,\n                )\n            else:\n                target_path = os.path.join(\n                    dest_path,\n                    member.linkname)\n            target_path = os.path.realpath(target_path)\n            if os.path.commonpath([target_path, dest_path]) != dest_path:\n                raise RuntimeError(\n                    f\"Symlink outside of dest dir: {target_path}\")\n\n    def directory_exists(self, path: str) -> bool:\n        return os.path.isdir(path)\n\n    def get_directory_contents(self, path: str) -> List[str]:\n        return os.listdir(path)\n\n    def makedirs(self, path: str) -> None:\n        os.makedirs(path)\n\n    def dirname(self, path: str) -> str:\n        return os.path.dirname(path)\n\n    def abspath(self, path: str) -> str:\n        return os.path.abspath(path)\n\n    def joinpath(self, *args: str) -> str:\n        return os.path.join(*args)\n\n    def walk(\n        self, path: str, followlinks: bool = False\n    ) -> Iterator[Tuple[str, List[str], List[str]]]:\n        return os.walk(path, followlinks=followlinks)\n\n    def copytree(self, source: str, destination: str) -> None:\n        if not os.path.exists(destination):\n            self.makedirs(destination)\n        names = self.get_directory_contents(source)\n        for name in names:\n            new_source = os.path.join(source, name)\n            new_destination = os.path.join(destination, name)\n            if os.path.isdir(new_source):\n                self.copytree(new_source, new_destination)\n            else:\n                shutil.copy2(new_source, new_destination)\n\n    def rmtree(self, directory: str) -> None:\n        shutil.rmtree(directory)\n\n    def copy(self, source: str, destination: str) -> None:\n        shutil.copy(source, destination)\n\n    def move(self, source: str, destination: str) -> None:\n        shutil.move(source, destination)\n\n    @contextlib.contextmanager\n    def tempdir(self) -> Any:\n        tempdir = tempfile.mkdtemp()\n        try:\n            yield tempdir\n        finally:\n            shutil.rmtree(tempdir)\n\n    def popen(\n        self,\n        command: List[str],\n        stdout: OptInt = None,\n        stderr: OptInt = None,\n        env: Optional[EnvVars] = None,\n    ) -> subprocess.Popen:\n        p = subprocess.Popen(command, stdout=stdout, stderr=stderr, env=env)\n        return p\n\n    def mtime(self, path: str) -> float:\n        return os.stat(path).st_mtime\n\n    def stat(self, path: str) -> os.stat_result:\n        return os.stat(path)\n\n    def normalized_filename(self, path: str) -> str:\n        \"\"\"Normalize a path into a filename.\n\n        This will normalize a file and remove any 'drive' component\n        from the path on OSes that support drive specifications.\n\n        \"\"\"\n        return os.path.normpath(os.path.splitdrive(path)[1])\n\n    @property\n    def pipe(self) -> int:\n        return subprocess.PIPE\n\n    def basename(self, path: str) -> str:\n        return os.path.basename(path)\n\n\ndef getting_started_prompt(prompter: Any) -> bool:\n    return prompter.prompt(WELCOME_PROMPT)\n\n\nclass UI(object):\n    def __init__(\n        self,\n        out: Optional[IO] = None,\n        err: Optional[IO] = None,\n        confirm: Optional[Any] = None,\n    ) -> None:\n        # I tried using a more exact type for the 'confirm'\n        # param, but mypy seems to miss the 'if confirm is None'\n        # check and types _confirm as Union[..., None].\n        # So for now, we're using Any for this type.\n        if out is None:\n            out = sys.stdout\n        if err is None:\n            err = sys.stderr\n        if confirm is None:\n            confirm = click.confirm\n        self._out = out\n        self._err = err\n        self._confirm = confirm\n\n    def write(self, msg: str) -> None:\n        self._out.write(msg)\n\n    def error(self, msg: str) -> None:\n        self._err.write(msg)\n\n    def confirm(\n        self, msg: str, default: bool = False, abort: bool = False\n    ) -> Any:\n        try:\n            return self._confirm(msg, default, abort)\n        except click.Abort:\n            raise AbortedError()\n\n\nclass PipeReader(object):\n    def __init__(self, stream: IO[bytes]) -> None:\n        self._stream = stream\n\n    def read(self) -> OptBytes:\n        if not self._stream.isatty():\n            return self._stream.read()\n        return None\n\n\nclass TimestampConverter(object):\n    # This is roughly based off of what's used in the AWS CLI.\n    _RELATIVE_TIMESTAMP_REGEX = re.compile(\n        r\"(?P<amount>\\d+)(?P<unit>s|m|h|d|w)$\"\n    )\n    _TO_SECONDS = {\n        's': 1,\n        'm': 60,\n        'h': 3600,\n        'd': 24 * 3600,\n        'w': 7 * 24 * 3600,\n    }\n\n    def __init__(self, now: Optional[Callable[[], datetime]] = None) -> None:\n        if now is None:\n            now = datetime.utcnow\n        self._now = now\n\n    def timestamp_to_datetime(self, timestamp: str) -> datetime:\n        \"\"\"Convert a timestamp to a datetime object.\n\n        This method detects what type of timestamp is provided and\n        parse is appropriately to a timestamp object.\n\n        \"\"\"\n        re_match = self._RELATIVE_TIMESTAMP_REGEX.match(timestamp)\n        if re_match:\n            datetime_value = self._relative_timestamp_to_datetime(\n                int(re_match.group('amount')), re_match.group('unit')\n            )\n        else:\n            datetime_value = self.parse_iso8601_timestamp(timestamp)\n        return datetime_value\n\n    def _relative_timestamp_to_datetime(\n        self, amount: int, unit: str\n    ) -> datetime:\n        multiplier = self._TO_SECONDS[unit]\n        return self._now() + timedelta(seconds=amount * multiplier * -1)\n\n    def parse_iso8601_timestamp(self, timestamp: str) -> datetime:\n        return dateutil.parser.parse(timestamp, tzinfos={'GMT': tzutc()})\n"
  },
  {
    "path": "chalice/vendored/__init__.py",
    "content": ""
  },
  {
    "path": "chalice/vendored/botocore/__init__.py",
    "content": ""
  },
  {
    "path": "chalice/vendored/botocore/regions.py",
    "content": "# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"). You\n# may not use this file except in compliance with the License. A copy of\n# the License is located at\n#\n# http://aws.amazon.com/apache2.0/\n#\n# or in the \"license\" file accompanying this file. This file is\n# distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n# ANY KIND, either express or implied. See the License for the specific\n# language governing permissions and limitations under the License.\n\"\"\"Resolves regions and endpoints.\n\nThis module implements endpoint resolution, including resolving endpoints for a\ngiven service and region and resolving the available endpoints for a service\nin a specific AWS partition.\n\"\"\"\nimport logging\nimport re\n\nfrom botocore.exceptions import NoRegionError\n\nLOG = logging.getLogger(__name__)\nDEFAULT_URI_TEMPLATE = '{service}.{region}.{dnsSuffix}'\nDEFAULT_SERVICE_DATA = {'endpoints': {}}\n\n\nclass BaseEndpointResolver(object):\n    \"\"\"Resolves regions and endpoints. Must be subclassed.\"\"\"\n    def construct_endpoint(self, service_name, region_name=None):\n        \"\"\"Resolves an endpoint for a service and region combination.\n\n        :type service_name: string\n        :param service_name: Name of the service to resolve an endpoint for\n            (e.g., s3)\n\n        :type region_name: string\n        :param region_name: Region/endpoint name to resolve (e.g., us-east-1)\n            if no region is provided, the first found partition-wide endpoint\n            will be used if available.\n\n        :rtype: dict\n        :return: Returns a dict containing the following keys:\n            - partition: (string, required) Resolved partition name\n            - endpointName: (string, required) Resolved endpoint name\n            - hostname: (string, required) Hostname to use for this endpoint\n            - sslCommonName: (string) sslCommonName to use for this endpoint.\n            - credentialScope: (dict) Signature version 4 credential scope\n              - region: (string) region name override when signing.\n              - service: (string) service name override when signing.\n            - signatureVersions: (list<string>) A list of possible signature\n              versions, including s3, v4, v2, and s3v4\n            - protocols: (list<string>) A list of supported protocols\n              (e.g., http, https)\n            - ...: Other keys may be included as well based on the metadata\n        \"\"\"\n        raise NotImplementedError\n\n    def get_available_partitions(self):\n        \"\"\"Lists the partitions available to the endpoint resolver.\n\n        :return: Returns a list of partition names (e.g., [\"aws\", \"aws-cn\"]).\n        \"\"\"\n        raise NotImplementedError\n\n    def get_available_endpoints(self, service_name, partition_name='aws',\n                                allow_non_regional=False):\n        \"\"\"Lists the endpoint names of a particular partition.\n\n        :type service_name: string\n        :param service_name: Name of a service to list endpoint for (e.g., s3)\n\n        :type partition_name: string\n        :param partition_name: Name of the partition to limit endpoints to.\n            (e.g., aws for the public AWS endpoints, aws-cn for AWS China\n            endpoints, aws-us-gov for AWS GovCloud (US) Endpoints, etc.\n\n        :type allow_non_regional: bool\n        :param allow_non_regional: Set to True to include endpoints that are\n             not regional endpoints (e.g., s3-external-1,\n             fips-us-gov-west-1, etc).\n        :return: Returns a list of endpoint names (e.g., [\"us-east-1\"]).\n        \"\"\"\n        raise NotImplementedError\n\n\nclass EndpointResolver(BaseEndpointResolver):\n    \"\"\"Resolves endpoints based on partition endpoint metadata\"\"\"\n    def __init__(self, endpoint_data):\n        \"\"\"\n        :param endpoint_data: A dict of partition data.\n        \"\"\"\n        if 'partitions' not in endpoint_data:\n            raise ValueError('Missing \"partitions\" in endpoint data')\n        self._endpoint_data = endpoint_data\n\n    def get_available_partitions(self):\n        result = []\n        for partition in self._endpoint_data['partitions']:\n            result.append(partition['partition'])\n        return result\n\n    def get_available_endpoints(self, service_name, partition_name='aws',\n                                allow_non_regional=False):\n        result = []\n        for partition in self._endpoint_data['partitions']:\n            if partition['partition'] != partition_name:\n                continue\n            services = partition['services']\n            if service_name not in services:\n                continue\n            for endpoint_name in services[service_name]['endpoints']:\n                if allow_non_regional or endpoint_name in partition['regions']:\n                    result.append(endpoint_name)\n        return result\n\n    def construct_endpoint(self, service_name, region_name=None, partition_name=None):\n        if partition_name is not None:\n            valid_partition = None\n            for partition in self._endpoint_data['partitions']:\n                if partition['partition'] == partition_name:\n                    valid_partition = partition\n\n            if valid_partition is not None:\n                result = self._endpoint_for_partition(valid_partition, service_name,\n                             region_name, True)\n                return result\n            return None\n\n        # Iterate over each partition until a match is found.\n        for partition in self._endpoint_data['partitions']:\n            result = self._endpoint_for_partition(\n                partition, service_name, region_name)\n            if result:\n                return result\n\n    def _endpoint_for_partition(self, partition, service_name, region_name,\n            force_partition=False):\n        # Get the service from the partition, or an empty template.\n        service_data = partition['services'].get(\n            service_name, DEFAULT_SERVICE_DATA)\n        # Use the partition endpoint if no region is supplied.\n        if region_name is None:\n            if 'partitionEndpoint' in service_data:\n                region_name = service_data['partitionEndpoint']\n            else:\n                raise NoRegionError()\n        # Attempt to resolve the exact region for this partition.\n        if region_name in service_data['endpoints']:\n            return self._resolve(\n                partition, service_name, service_data, region_name)\n        # Check to see if the endpoint provided is valid for the partition.\n        if self._region_match(partition, region_name) or force_partition:\n            # Use the partition endpoint if set and not regionalized.\n            partition_endpoint = service_data.get('partitionEndpoint')\n            is_regionalized = service_data.get('isRegionalized', True)\n            if partition_endpoint and not is_regionalized:\n                LOG.debug('Using partition endpoint for %s, %s: %s',\n                          service_name, region_name, partition_endpoint)\n                return self._resolve(\n                    partition, service_name, service_data, partition_endpoint)\n            LOG.debug('Creating a regex based endpoint for %s, %s',\n                      service_name, region_name)\n            return self._resolve(\n                partition, service_name, service_data, region_name)\n\n    def _region_match(self, partition, region_name):\n        if region_name in partition['regions']:\n            return True\n        if 'regionRegex' in partition:\n            return re.compile(partition['regionRegex']).match(region_name)\n        return False\n\n    def _resolve(self, partition, service_name, service_data, endpoint_name):\n        result = service_data['endpoints'].get(endpoint_name, {})\n        result['partition'] = partition['partition']\n        result['endpointName'] = endpoint_name\n        # Merge in the service defaults then the partition defaults.\n        self._merge_keys(service_data.get('defaults', {}), result)\n        self._merge_keys(partition.get('defaults', {}), result)\n        hostname = result.get('hostname', DEFAULT_URI_TEMPLATE)\n        result['hostname'] = self._expand_template(\n            partition, result['hostname'], service_name, endpoint_name)\n        if 'sslCommonName' in result:\n            result['sslCommonName'] = self._expand_template(\n                partition, result['sslCommonName'], service_name,\n                endpoint_name)\n        result['dnsSuffix'] = partition['dnsSuffix']\n        return result\n\n    def _merge_keys(self, from_data, result):\n        for key in from_data:\n            if key not in result:\n                result[key] = from_data[key]\n\n    def _expand_template(self, partition, template, service_name,\n                         endpoint_name):\n        return template.format(\n            service=service_name, region=endpoint_name,\n            dnsSuffix=partition['dnsSuffix'])"
  },
  {
    "path": "docs/Makefile",
    "content": "# Makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    = -W\nSPHINXBUILD   = sphinx-build\nPAPER         =\nBUILDDIR      = build\n\n# User-friendly check for sphinx-build\nifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)\n    $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don\\'t have Sphinx installed, grab it from http://sphinx-doc.org/)\nendif\n\n# Internal variables.\nPAPEROPT_a4     = -D latex_paper_size=a4\nPAPEROPT_letter = -D latex_paper_size=letter\nALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source\n# the i18n builder cannot share the environment and doctrees with the others\nI18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source\n\n.PHONY: help\nhelp:\n\t@echo \"Please use \\`make <target>' where <target> is one of\"\n\t@echo \"  html       to make standalone HTML files\"\n\t@echo \"  dirhtml    to make HTML files named index.html in directories\"\n\t@echo \"  singlehtml to make a single large HTML file\"\n\t@echo \"  pickle     to make pickle files\"\n\t@echo \"  json       to make JSON files\"\n\t@echo \"  htmlhelp   to make HTML files and a HTML help project\"\n\t@echo \"  qthelp     to make HTML files and a qthelp project\"\n\t@echo \"  applehelp  to make an Apple Help Book\"\n\t@echo \"  devhelp    to make HTML files and a Devhelp project\"\n\t@echo \"  epub       to make an epub\"\n\t@echo \"  epub3      to make an epub3\"\n\t@echo \"  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter\"\n\t@echo \"  latexpdf   to make LaTeX files and run them through pdflatex\"\n\t@echo \"  latexpdfja to make LaTeX files and run them through platex/dvipdfmx\"\n\t@echo \"  text       to make text files\"\n\t@echo \"  man        to make manual pages\"\n\t@echo \"  texinfo    to make Texinfo files\"\n\t@echo \"  info       to make Texinfo files and run them through makeinfo\"\n\t@echo \"  gettext    to make PO message catalogs\"\n\t@echo \"  changes    to make an overview of all changed/added/deprecated items\"\n\t@echo \"  xml        to make Docutils-native XML files\"\n\t@echo \"  pseudoxml  to make pseudoxml-XML files for display purposes\"\n\t@echo \"  linkcheck  to check all external links for integrity\"\n\t@echo \"  doctest    to run all doctests embedded in the documentation (if enabled)\"\n\t@echo \"  coverage   to run coverage check of the documentation (if enabled)\"\n\t@echo \"  dummy      to check syntax errors of document sources\"\n\n.PHONY: clean\nclean:\n\trm -rf $(BUILDDIR)/*\n\n.PHONY: html\nhtml:\n\t$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/html.\"\n\n.PHONY: dirhtml\ndirhtml:\n\t$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/dirhtml.\"\n\n.PHONY: singlehtml\nsinglehtml:\n\t$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml\n\t@echo\n\t@echo \"Build finished. The HTML page is in $(BUILDDIR)/singlehtml.\"\n\n.PHONY: pickle\npickle:\n\t$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle\n\t@echo\n\t@echo \"Build finished; now you can process the pickle files.\"\n\n.PHONY: json\njson:\n\t$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json\n\t@echo\n\t@echo \"Build finished; now you can process the JSON files.\"\n\n.PHONY: htmlhelp\nhtmlhelp:\n\t$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp\n\t@echo\n\t@echo \"Build finished; now you can run HTML Help Workshop with the\" \\\n\t      \".hhp project file in $(BUILDDIR)/htmlhelp.\"\n\n.PHONY: qthelp\nqthelp:\n\t$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp\n\t@echo\n\t@echo \"Build finished; now you can run \"qcollectiongenerator\" with the\" \\\n\t      \".qhcp project file in $(BUILDDIR)/qthelp, like this:\"\n\t@echo \"# qcollectiongenerator $(BUILDDIR)/qthelp/Chalice.qhcp\"\n\t@echo \"To view the help file:\"\n\t@echo \"# assistant -collectionFile $(BUILDDIR)/qthelp/Chalice.qhc\"\n\n.PHONY: applehelp\napplehelp:\n\t$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp\n\t@echo\n\t@echo \"Build finished. The help book is in $(BUILDDIR)/applehelp.\"\n\t@echo \"N.B. You won't be able to view it unless you put it in\" \\\n\t      \"~/Library/Documentation/Help or install it in your application\" \\\n\t      \"bundle.\"\n\n.PHONY: devhelp\ndevhelp:\n\t$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp\n\t@echo\n\t@echo \"Build finished.\"\n\t@echo \"To view the help file:\"\n\t@echo \"# mkdir -p $$HOME/.local/share/devhelp/Chalice\"\n\t@echo \"# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Chalice\"\n\t@echo \"# devhelp\"\n\n.PHONY: epub\nepub:\n\t$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub\n\t@echo\n\t@echo \"Build finished. The epub file is in $(BUILDDIR)/epub.\"\n\n.PHONY: epub3\nepub3:\n\t$(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3\n\t@echo\n\t@echo \"Build finished. The epub3 file is in $(BUILDDIR)/epub3.\"\n\n.PHONY: latex\nlatex:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo\n\t@echo \"Build finished; the LaTeX files are in $(BUILDDIR)/latex.\"\n\t@echo \"Run \\`make' in that directory to run these through (pdf)latex\" \\\n\t      \"(use \\`make latexpdf' here to do that automatically).\"\n\n.PHONY: latexpdf\nlatexpdf:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo \"Running LaTeX files through pdflatex...\"\n\t$(MAKE) -C $(BUILDDIR)/latex all-pdf\n\t@echo \"pdflatex finished; the PDF files are in $(BUILDDIR)/latex.\"\n\n.PHONY: latexpdfja\nlatexpdfja:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo \"Running LaTeX files through platex and dvipdfmx...\"\n\t$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja\n\t@echo \"pdflatex finished; the PDF files are in $(BUILDDIR)/latex.\"\n\n.PHONY: text\ntext:\n\t$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text\n\t@echo\n\t@echo \"Build finished. The text files are in $(BUILDDIR)/text.\"\n\n.PHONY: man\nman:\n\t$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man\n\t@echo\n\t@echo \"Build finished. The manual pages are in $(BUILDDIR)/man.\"\n\n.PHONY: texinfo\ntexinfo:\n\t$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo\n\t@echo\n\t@echo \"Build finished. The Texinfo files are in $(BUILDDIR)/texinfo.\"\n\t@echo \"Run \\`make' in that directory to run these through makeinfo\" \\\n\t      \"(use \\`make info' here to do that automatically).\"\n\n.PHONY: info\ninfo:\n\t$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo\n\t@echo \"Running Texinfo files through makeinfo...\"\n\tmake -C $(BUILDDIR)/texinfo info\n\t@echo \"makeinfo finished; the Info files are in $(BUILDDIR)/texinfo.\"\n\n.PHONY: gettext\ngettext:\n\t$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale\n\t@echo\n\t@echo \"Build finished. The message catalogs are in $(BUILDDIR)/locale.\"\n\n.PHONY: changes\nchanges:\n\t$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes\n\t@echo\n\t@echo \"The overview file is in $(BUILDDIR)/changes.\"\n\n.PHONY: linkcheck\nlinkcheck:\n\t$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck\n\t@echo\n\t@echo \"Link check complete; look for any errors in the above output \" \\\n\t      \"or in $(BUILDDIR)/linkcheck/output.txt.\"\n\n.PHONY: doctest\ndoctest:\n\t$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest\n\t@echo \"Testing of doctests in the sources finished, look at the \" \\\n\t      \"results in $(BUILDDIR)/doctest/output.txt.\"\n\n.PHONY: coverage\ncoverage:\n\t$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage\n\t@echo \"Testing of coverage in the sources finished, look at the \" \\\n\t      \"results in $(BUILDDIR)/coverage/python.txt.\"\n\n.PHONY: xml\nxml:\n\t$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml\n\t@echo\n\t@echo \"Build finished. The XML files are in $(BUILDDIR)/xml.\"\n\n.PHONY: pseudoxml\npseudoxml:\n\t$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml\n\t@echo\n\t@echo \"Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml.\"\n\n.PHONY: dummy\ndummy:\n\t$(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy\n\t@echo\n\t@echo \"Build finished. Dummy builder generates no files.\"\n"
  },
  {
    "path": "docs/source/_static/custom.css",
    "content": "div.body h1 { margin-top: 0; padding-top: 0; font-size: 200%; }\n\ndiv.highlight-python {\n  border-width: 0 0 0 2px;\n  border-style: solid;\n  border-color: #84ad98;\n}\n\ndiv.highlight-default {\n  border-width: 0 0 0 2px;\n  border-style: solid;\n  border-color: #a9a9a9;\n}\n\na.reference {\n  border-bottom: 0.5px dotted #faa;\n}\n"
  },
  {
    "path": "docs/source/_static/fonts/open-sans/stylesheet.css",
    "content": "/***** Font Definition for Open Sans. This stylesheet comes from qrohlf.com/posts/better-opensans *****/\n\n/* Regular */\n@font-face {\n    font-family: 'Open Sans';\n    \n    src: url('fonts/OpenSans-Regular-webfont.eot');\n    src: url('fonts/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'),\n         url('fonts/OpenSans-Regular-webfont.woff') format('woff'),\n         url('fonts/OpenSans-Regular-webfont.ttf') format('truetype'),\n         url('fonts/OpenSans-Regular-webfont.svg#OpenSansRegular') format('svg');\n    font-weight: normal;\n    font-weight: 400;\n    font-style: normal;\n\n}\n\n/* Italic */\n@font-face {\n    font-family: 'Open Sans';\n    src: url('fonts/OpenSans-Italic-webfont.eot');\n    src: url('fonts/OpenSans-Italic-webfont.eot?#iefix') format('embedded-opentype'),\n         url('fonts/OpenSans-Italic-webfont.woff') format('woff'),\n         url('fonts/OpenSans-Italic-webfont.ttf') format('truetype'),\n         url('fonts/OpenSans-Italic-webfont.svg#OpenSansItalic') format('svg');\n    font-weight: normal;\n    font-weight: 400;\n    font-style: italic;\n\n}\n\n/* Light */\n@font-face {\n    font-family: 'Open Sans';\n    src: url('fonts/OpenSans-Light-webfont.eot');\n    src: url('fonts/OpenSans-Light-webfont.eot?#iefix') format('embedded-opentype'),\n         url('fonts/OpenSans-Light-webfont.woff') format('woff'),\n         url('fonts/OpenSans-Light-webfont.ttf') format('truetype'),\n         url('fonts/OpenSans-Light-webfont.svg#OpenSansLight') format('svg');\n    font-weight: 200;\n    font-style: normal;\n\n}\n\n/* Light Italic */\n@font-face {\n    font-family: 'Open Sans';\n    src: url('fonts/OpenSans-LightItalic-webfont.eot');\n    src: url('fonts/OpenSans-LightItalic-webfont.eot?#iefix') format('embedded-opentype'),\n         url('fonts/OpenSans-LightItalic-webfont.woff') format('woff'),\n         url('fonts/OpenSans-LightItalic-webfont.ttf') format('truetype'),\n         url('fonts/OpenSans-LightItalic-webfont.svg#OpenSansLightItalic') format('svg');\n    font-weight: 200;\n    font-style: italic;\n\n}\n\n/* Semibold */\n@font-face {\n    font-family: 'Open Sans';\n    src: url('fonts/OpenSans-Semibold-webfont.eot');\n    src: url('fonts/OpenSans-Semibold-webfont.eot?#iefix') format('embedded-opentype'),\n         url('fonts/OpenSans-Semibold-webfont.woff') format('woff'),\n         url('fonts/OpenSans-Semibold-webfont.ttf') format('truetype'),\n         url('fonts/OpenSans-Semibold-webfont.svg#OpenSansSemibold') format('svg');\n    font-weight: 500;\n    font-style: normal;\n\n}\n\n/* Semibold Italic */\n@font-face {\n    font-family: 'Open Sans';\n    src: url('fonts/OpenSans-SemiboldItalic-webfont.eot');\n    src: url('fonts/OpenSans-SemiboldItalic-webfont.eot?#iefix') format('embedded-opentype'),\n         url('fonts/OpenSans-SemiboldItalic-webfont.woff') format('woff'),\n         url('fonts/OpenSans-SemiboldItalic-webfont.ttf') format('truetype'),\n         url('fonts/OpenSans-SemiboldItalic-webfont.svg#OpenSansSemiboldItalic') format('svg');\n    font-weight: 500;\n    font-style: italic;\n\n}\n\n/* Bold */\n@font-face {\n    font-family: 'Open Sans';\n    src: url('fonts/OpenSans-Bold-webfont.eot');\n    src: url('fonts/OpenSans-Bold-webfont.eot?#iefix') format('embedded-opentype'),\n         url('fonts/OpenSans-Bold-webfont.woff') format('woff'),\n         url('fonts/OpenSans-Bold-webfont.ttf') format('truetype'),\n         url('fonts/OpenSans-Bold-webfont.svg#OpenSansBold') format('svg');\n    font-weight: bold;\n    font-weight: 700;\n    font-style: normal;\n\n}\n\n/* Bold Italic */\n@font-face {\n    font-family: 'Open Sans';\n    src: url('fonts/OpenSans-BoldItalic-webfont.eot');\n    src: url('fonts/OpenSans-BoldItalic-webfont.eot?#iefix') format('embedded-opentype'),\n         url('fonts/OpenSans-BoldItalic-webfont.woff') format('woff'),\n         url('fonts/OpenSans-BoldItalic-webfont.ttf') format('truetype'),\n         url('fonts/OpenSans-BoldItalic-webfont.svg#OpenSansBoldItalic') format('svg');\n    font-weight: bold;\n    font-weight: 700;\n    font-style: italic;\n\n}\n\n/* Extra Bold */\n@font-face {\n    font-family: 'Open Sans';\n    src: url('fonts/OpenSans-ExtraBold-webfont.eot');\n    src: url('fonts/OpenSans-ExtraBold-webfont.eot?#iefix') format('embedded-opentype'),\n         url('fonts/OpenSans-ExtraBold-webfont.woff') format('woff'),\n         url('fonts/OpenSans-ExtraBold-webfont.ttf') format('truetype'),\n         url('fonts/OpenSans-ExtraBold-webfont.svg#OpenSansExtrabold') format('svg');\n    font-weight: 900;\n    font-style: normal;\n\n}\n\n/* Extra Bold Italic */\n@font-face {\n    font-family: 'Open Sans';\n    src: url('fonts/OpenSans-ExtraBoldItalic-webfont.eot');\n    src: url('fonts/OpenSans-ExtraBoldItalic-webfont.eot?#iefix') format('embedded-opentype'),\n         url('fonts/OpenSans-ExtraBoldItalic-webfont.woff') format('woff'),\n         url('fonts/OpenSans-ExtraBoldItalic-webfont.ttf') format('truetype'),\n         url('fonts/OpenSans-ExtraBoldItalic-webfont.svg#OpenSansExtraboldItalic') format('svg');\n    font-weight: 900;\n    font-style: italic;\n\n}\n"
  },
  {
    "path": "docs/source/_templates/layout.html",
    "content": "{%- extends \"!layout.html\" %}\n\n{%- block extrahead %}\n  <!-- Licensed under the Apache 2.0 License -->\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"{{ pathto('_static/fonts/open-sans/stylesheet.css', 1) }}\" />\n  {{ super() }}\n{% endblock %}\n\n"
  },
  {
    "path": "docs/source/api.rst",
    "content": "Chalice\n=======\n\n.. class:: Chalice(app_name)\n\n   This class represents a chalice application.  It provides:\n\n   * The ability to register routes using the :meth:`route` method.\n   * Within a view function, the ability to introspect the current\n     request using the ``current_request`` attribute which is an instance\n     of the :class:`Request` class.\n\n   .. attribute:: app_name\n\n      The name of the Chalice app.  This corresponds to the value provided\n      when instantiating a ``Chalice`` object.\n\n   .. attribute:: current_request\n\n      An object of type :class:`Request`.  This value is only set when\n      a view function is being called.  This attribute can be used to\n      introspect the current HTTP request.\n\n   .. attribute:: api\n\n      An object of type :class:`APIGateway`. This attribute can be used to control\n      how apigateway interprets ``Content-Type`` headers in both requests and\n      responses.\n\n   .. attribute:: lambda_context\n\n      A Lambda context object that is passed to the invoked view by AWS\n      Lambda. You can find out more about this object by reading the\n      `lambda context object documentation <https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html>`_.\n\n      .. note::\n\n       This is only set on ``@app.route`` handlers. For other handlers it\n       will be ``None``. Instead the event parameter will have a ``context``\n       property. For example :attr:`S3Event.context`.\n\n   .. attribute:: debug\n\n      A boolean value that enables debugging.  By default, this value is\n      ``False``.  If debugging is true, then internal errors are returned back\n      to the client.  Additionally, debug log messages generated by the\n      framework will show up in the cloudwatch logs.  Example usage:\n\n      .. code-block:: python\n\n         from chalice import Chalice\n\n         app = Chalice(app_name=\"appname\")\n         app.debug = True\n\n   .. attribute:: websocket_api\n\n      An object of type :class:`WebsocketAPI`. This attribute can be used to\n      send messages to websocket clients connected through API Gateway.\n\n   .. method:: route(path, \\*\\*options)\n\n      Register a view function for a particular URI path.  This method\n      is intended to be used as a decorator for a view function.  For example:\n\n      .. code-block:: python\n\n         from chalice import Chalice\n\n         app = Chalice(app_name=\"appname\")\n\n         @app.route('/resource/{value}', methods=['PUT'])\n         def viewfunction(value):\n             pass\n\n\n      :param str path: The path to associate with the view function.  The\n        ``path`` should only contain ``[a-zA-Z0-9._-]`` chars and curly\n        braces for parts of the URL you would like to capture.  The path\n        should not end in a trailing slash, otherwise a validation error\n        will be raised during deployment.\n\n      :param list methods: Optional parameter that indicates which HTTP methods\n        this view function should accept.  By default, only ``GET`` requests\n        are supported.  If you only wanted to support ``POST`` requests, you\n        would specify ``methods=['POST']``.  If you support multiple HTTP\n        methods in a single view function (``methods=['GET', 'POST']``), you\n        can check the :attr:`app.current_request.method <Request.method>`\n        attribute to see which HTTP method was used when making the request.\n        You can provide any HTTP method supported by API Gateway, which\n        includes: ``GET``, ``POST``, ``PUT``, ``PATCH``, ``HEAD``,\n        ``OPTIONS``, and ``DELETE``.\n\n      :param str name: Optional parameter to specify the name of the view\n        function.  You generally do not need to set this value.  The name\n        of the view function is used as the default value for the view name.\n\n      :param Authorizer authorizer: Specify an authorizer to use for this\n        view.  Can be an instance of :class:`CognitoUserPoolAuthorizer`,\n        :class:`CustomAuthorizer` or :class:`IAMAuthorizer`.\n\n      :param str content_types: A list of content types to accept for\n        this view.  By default ``application/json`` is accepted.  If\n        this value is specified, then chalice will reject any incoming request\n        that does not match the provided list of content types with a\n        415 Unsupported Media Type response.\n\n      :param boolean api_key_required: Optional parameter to specify whether\n        the method required a valid API key.\n\n      :param cors: Specify if CORS is supported for this view.  This can either\n        by a boolean value, ``None``, or an instance of :class:`CORSConfig`.\n        Setting this value is set to ``True`` gives similar behavior to enabling\n        CORS in the AWS Console.  This includes injecting the\n        ``Access-Control-Allow-Origin`` header to have a value of ``*`` as well\n        as adding an ``OPTIONS`` method to support preflighting requests.  If\n        you would like more control over how CORS is configured, you can provide\n        an instance of :class:`CORSConfig`.\n\n   .. method:: authorizer(name, \\*\\*options)\n\n      Register a built-in authorizer.\n\n      .. code-block:: python\n\n         from chalice import Chalice, AuthResponse\n\n         app = Chalice(app_name=\"appname\")\n\n         @app.authorizer(ttl_seconds=30)\n         def my_auth(auth_request):\n             # Validate auth_request.token, and then:\n             return AuthResponse(routes=['/'], principal_id='username')\n\n         @app.route('/', authorizer=my_auth)\n         def viewfunction(value):\n             pass\n\n      :param ttl_seconds: The number of seconds to cache this response.\n        Subsequent requests that require this authorizer will use a\n        cached response if available.  The default is 300 seconds.\n\n      :param execution_role: An optional IAM role to specify when invoking\n        the Lambda function associated with the built-in authorizer.\n\n      :param header: The header where the auth token will be specified.\n        The default is ``Authorization``\n\n   .. method:: schedule(expression, name=None)\n\n      Register a scheduled event that's invoked on a regular schedule.\n      This will create a lambda function associated with the decorated\n      function.  It will also schedule the lambda function to be invoked\n      with a scheduled CloudWatch Event.\n\n      See :ref:`scheduled-events` for more information.\n\n      .. code-block:: python\n\n          @app.schedule('cron(15 10 ? * 6L 2002-2005)')\n          def cron_handler(event):\n              pass\n\n          @app.schedule('rate(5 minutes)')\n          def rate_handler(event):\n              pass\n\n          @app.schedule(Rate(5, unit=Rate.MINUTES))\n          def rate_obj_handler(event):\n              pass\n\n          @app.schedule(Cron(15, 10, '?', '*', '6L', '2002-2005'))\n          def cron_obj_handler(event):\n              pass\n\n\n      :param expression: The schedule expression to use for the CloudWatch\n        event rule.  This value can either be a string value or an\n        instance of type ``ScheduleExpression``, which is either a\n        :class:`Cron` or :class:`Rate` object.  If a string value is\n        provided, it will be provided directly as the ``ScheduleExpression``\n        value in the `PutRule <https://docs.aws.amazon.com/AmazonCloudWatchEvents/latest/APIReference/API_PutRule.html#API_PutRule_RequestSyntax>`__ API\n        call.\n\n      :param name: The name of the function to use.  This name is combined\n        with the chalice app name as well as the stage name to create the\n        entire lambda function name.  This parameter is optional.  If it is\n        not provided, the name of the python function will be used.\n\n   .. method:: on_cw_event(pattern, name=None)\n\n      Create a lambda function and configure it to be invoked whenever\n      an event that matches the given pattern flows through CloudWatch Events\n      or Event Bridge.\n\n      :param pattern: The event pattern to use to filter subscribed events.\n        See the CloudWatch Events docs for examples https://amzn.to/2OlqZso\n\n      :param name: The name of the function to create.  This name is combined\n        with the chalice app name as well as the stage name to create the\n        entire lambda function name.  This parameter is optional.  If it is\n        not provided, the name of the python function will be used.\n\n   .. method:: on_s3_event(bucket, events=None, prefix=None, suffix=None, name=None)\n\n      Create a lambda function and configure it to be automatically invoked\n      whenever an event happens on an S3 bucket.\n\n      .. warning::\n\n          You can't use the ``chalice package`` command when using the\n          ``on_s3_event`` decorator.  This is because CFN does not support\n          configuring an existing S3 bucket.\n\n      See :ref:`s3-events` for more information.\n\n      This example shows how you could implement an image resizer that's\n      triggered whenever an object is uploaded to the ``images/`` prefix\n      of an S3 bucket (e.g ``s3://mybucket/images/house.jpg``).\n\n      .. code-block:: python\n\n          @app.on_s3_event('mybucket', events=['s3:ObjectCreated:Put'],\n                           prefix='images/', suffix='.jpg')\n          def resize_image(event):\n              with tempfile.NamedTemporaryFile('w') as f:\n                  s3.download_file(event.bucket, event.key, f.name)\n                  resize_image(f.name)\n                  s3.upload_file(event.bucket, 'resized/%s' % event.key, f.name)\n\n\n      :param bucket: The name of the S3 bucket.  This bucket must already exist.\n\n      :param events: A list of strings indicating the events that should trigger\n        the lambda function.  See `Supported Event Types <https://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html#supported-notification-event-types>`__\n        for the full list of strings you can provide.  If this option is not\n        provided, a default of ``['s3:ObjectCreated:*']`` is used, which will\n        configure the lambda function to be invoked whenever a new object\n        is created in the S3 bucket.\n\n      :param prefix: An optional key prefix.  This specifies that\n        the lambda function should only be invoked if the key starts with\n        this prefix (e.g. ``prefix='images/'``).  Note that this value\n        is not a glob (e.g. ``images/*``), it is a literal string match\n        for the start of the key.\n\n      :param suffix: An optional key suffix.  This specifies that the\n        lambda function should only be invoked if the key name ends with\n        this suffix (e.g. ``suffix='.jpg'``).  Note that this value is\n        not a glob (e.g. ``*.txt``), it is a literal string match for\n        the end of the key.\n\n      :param name: The name of the function to use.  This name is combined\n        with the chalice app name as well as the stage name to create the\n        entire lambda function name.  This parameter is optional.  If it is\n        not provided, the name of the python function will be used.\n\n   .. method:: on_sns_message(topic, name=None)\n\n      Create a lambda function and configure it to be automatically invoked\n      whenever an SNS message is published to the specified topic.\n\n      See :ref:`sns-events` for more information.\n\n      This example prints the subject and the contents of the message whenever\n      something publishes to the sns topic of ``mytopic``.  In this example,\n      the input parameter is of type :class:`SNSEvent`.\n\n      .. code-block:: python\n\n          app.debug = True\n\n          @app.on_sns_message(topic='mytopic')\n          def handler(event):\n              app.log.info(\"SNS subject: %s\", event.subject)\n              app.log.info(\"SNS message: %s\", event.message)\n\n      :param topic: The name or ARN of the SNS topic you want to subscribe to.\n\n      :param name: The name of the function to use.  This name is combined\n        with the chalice app name as well as the stage name to create the\n        entire lambda function name.  This parameter is optional.  If it is\n        not provided, the name of the python function will be used.\n\n   .. method:: on_sqs_message(queue, batch_size=1, name=None, queue_arn=None, maximum_batching_window_in_seconds=0, maximum_concurrency=None)\n\n      Create a lambda function and configure it to be automatically invoked\n      whenever a message is published to the specified SQS queue.\n\n      The lambda function must accept a single parameter which\n      is of type :class:`SQSEvent`.\n\n      If the decorated function returns without raising any exceptions\n      then Lambda will automatically delete the SQS messages associated\n      with the :class:`SQSEvent`.  You don't need to manually delete\n      messages.  If any exception is raised, Lambda won't delete any messages,\n      and the messages will become available once the visibility timeout\n      has been reached.  Note that for batch sizes of more than one, either\n      the entire batch succeeds and all the messages in the batch are\n      deleted by Lambda, or the entire batch fails.  The default batch size\n      is 1.  See the\n      `Using AWS Lambda with Amazon SQS <https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html>`__\n      for more information on how Lambda integrates with SQS.\n\n      See the :ref:`sqs-events` topic guide for more information on using SQS\n      in Chalice.\n\n      .. code-block:: python\n\n          app.debug = True\n\n          @app.on_sqs_message(queue='myqueue')\n          def handler(event):\n              app.log.info(\"Event: %s\", event.to_dict())\n              for record in event:\n                  app.log.info(\"Message body: %s\", record.body)\n\n      :param queue: The name of the SQS queue you want to subscribe to.\n        This is the name of the queue, not the ARN or Queue URL.\n\n      :param batch_size: The maximum number of messages to retrieve\n        when polling for SQS messages.  The event parameter can have\n        multiple SQS messages associated with it.  This is why the\n        event parameter passed to the lambda function is iterable.  The\n        batch size controls how many messages can be in a single event.\n\n      :param name: The name of the function to use.  This name is combined\n        with the chalice app name as well as the stage name to create the\n        entire lambda function name.  This parameter is optional.  If it is\n        not provided, the name of the python function will be used.\n\n      :param queue_arn: The ARN of the SQS queue you want to subscribe to.\n        This argument is mutually exclusive with the ``queue`` parameter.\n        This is useful if you already know the exact ARN or when integrating\n        with the AWS CDK to create your SQS queue.\n\n      :param maximum_batching_window_in_seconds: The maximum amount of time,\n        in seconds, to gather records before invoking the function.\n\n      :param maximum_concurrency: The maximum number of concurrent functions\n        that the event source can invoke.\n\n   .. method:: on_kinesis_record(stream, batch_size=100, starting_position='LATEST', name=None, maximum_batching_window_in_seconds=0)\n\n      Create a lambda function and configure it to be automatically invoked\n      whenever data is published to the specified Kinesis stream.\n\n      The lambda function must accept a single parameter which\n      is of type :class:`KinesisEvent`.\n\n      If the decorated function raises an exception, Lambda retries the\n      batch until processing succeeds or the data expires.\n\n      See\n      `Using AWS Lambda with Amazon Kinesis <https://docs.aws.amazon.com/lambda/latest/dg/with-kinesis.html>`__\n      for more information on how Lambda integrates with Kinesis.\n\n      .. code-block:: python\n\n          app.debug = True\n\n          @app.on_kinesis_record(stream='mystream')\n          def handler(event):\n              app.log.info(\"Event: %s\", event.to_dict())\n              for record in event:\n                  app.log.info(\"Message body: %s\", record.data)\n\n      :param stream: The name of the Kinesis stream you want to subscribe to.\n        This is the name of the data stream, not the ARN.\n\n      :param batch_size: The maximum number of messages to retrieve\n        when polling for Kinesis messages.  The event parameter can have\n        multiple Kinesis records associated with it.  This is why the\n        event parameter passed to the lambda function is iterable.  The\n        batch size controls how many messages can be in a single event.\n\n      :param starting_position: Specifies where to start processing records.\n        This can have the following values:\n\n        * ``LATEST`` - Process new records that are added to the stream.\n        * ``TRIM_HORIZON`` - Process all records in the stream.\n\n      :param name: The name of the function to use.  This name is combined\n        with the chalice app name as well as the stage name to create the\n        entire lambda function name.  This parameter is optional.  If it is\n        not provided, the name of the python function will be used.\n\n      :param maximum_batching_window_in_seconds: The maximum amount of time,\n        in seconds, to gather records before invoking the function.\n\n   .. method:: on_dynamodb_record(stream_arn, batch_size=100, starting_position='LATEST', name=None, maximum_batching_window_in_seconds=0)\n\n      Create a lambda function and configure it to be automatically invoked\n      whenever data is written to a DynamoDB stream.\n\n      The lambda function must accept a single parameter which\n      is of type :class:`DynamoDBEvent`.\n\n      If the decorated function raises an exception, Lambda retries the\n      batch until processing succeeds or the data expires.\n\n      See\n      `Using AWS Lambda with Amazon DynamoDB <https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html>`__\n      for more information on how Lambda integrates with DynamoDB Streams.\n\n      .. code-block:: python\n\n          app.debug = True\n\n          @app.on_dynamodb_record(stream_arn='arn:aws:dynamodb:...:stream')\n          def handler(event):\n              app.log.info(\"Event: %s\", event.to_dict())\n              for record in event:\n                  app.log.info(\"New: %s\", record.new_image)\n\n      :param stream_arn: The name of the DynamoDB stream ARN you want to\n        subscribe to.  Note that, unlike other event handlers that accept\n        the resource name, you must provide the stream ARN when subscribing\n        to the DynamoDB stream ARN.\n\n      :param batch_size: The maximum number of messages to retrieve\n        when polling for DynamoDB messages.  The event parameter can have\n        multiple DynamoDB records associated with it.  This is why the\n        event parameter passed to the lambda function is iterable.  The\n        batch size controls how many messages can be in a single event.\n\n      :param starting_position: Specifies where to start processing records.\n        This can have the following values:\n\n        * ``LATEST`` - Process new records that are added to the stream.\n        * ``TRIM_HORIZON`` - Process all records in the stream.\n\n      :param name: The name of the function to use.  This name is combined\n        with the chalice app name as well as the stage name to create the\n        entire lambda function name.  This parameter is optional.  If it is\n        not provided, the name of the python function will be used.\n\n      :param maximum_batching_window_in_seconds: The maximum amount of time,\n        in seconds, to gather records before invoking the function.\n\n   .. method:: lambda_function(name=None)\n\n      Create a pure lambda function that's not connected to anything.\n\n      See :doc:`topics/purelambda` for more information.\n\n      :param name: The name of the function to use.  This name is combined\n        with the chalice app name as well as the stage name to create the\n        entire lambda function name.  This parameter is optional.  If it is\n        not provided, the name of the python function will be used.\n\n   .. method:: register_blueprint(blueprint, name_prefix=None, url_prefix=None)\n\n      Register a :class:`Blueprint` to a Chalice app.\n      See :doc:`topics/blueprints` for more information.\n\n      :param blueprint: The :class:`Blueprint` to register to the app.\n\n      :param name_prefix: An optional name prefix that's added to all the\n        resources specified in the blueprint.\n\n      :param url_prefix: An optional url prefix that's added to all the\n        routes defined the Blueprint.  This allows you to set the root mount\n        point for all URLs in a Blueprint.\n\n   .. method:: on_ws_connect(event)\n\n      Create a Websocket API connect event handler.\n\n      :param event: The :class:`WebsocketEvent` received to indicate a new\n         connection has been registered with API Gateway. The identifier of this\n         connection is under the :attr:`WebsocketEvent.connection_id` attribute.\n\n      see :doc:`topics/websockets` for more information.\n\n   .. method:: on_ws_message(event)\n\n      Create a Websocket API message event handler.\n\n      :param event: The :class:`WebsocketEvent` received to indicate API Gateway\n         received a message from a connected client. The identifier of the\n         client that sent the message is under the\n         :attr:`WebsocketEvent.connection_id` attribute. The content of the\n         message is available in the :attr:`WebsocketEvent.body` attribute.\n\n      see :doc:`topics/websockets` for more information.\n\n   .. method:: on_ws_disconnect(event)\n\n      Create a Websocket API disconnect event handler.\n\n      :param event: The :class:`WebsocketEvent` received to indicate an existing\n         connection has been disconnected from API Gateway. The identifier of this\n         connection is under the :attr:`WebsocketEvent.connection_id` attribute.\n\n      see :doc:`topics/websockets` for more information.\n\n   .. method:: middleware(event_type='all')\n\n      Register a middleware with a Chalice application.\n      This decorator will register a function as Chalice middleware, which\n      will be automatically invoked as part of the request/response cycle for\n      a Lambda invocation.  You can provide the ``event_type`` argument to\n      indicate what type of lambda events you want to register with.  The\n      default value, ``all``, indicates that the middleware will be called\n      for all Lambda functions defined in your Chalice app.  Supported\n      values are:\n\n      * ``all`` - ``Any``\n      * ``s3`` - :class:`S3Event`\n      * ``sns`` - :class:`SNSEvent`\n      * ``sqs`` - :class:`SQSEvent`\n      * ``cloudwatch`` - :class:`CloudWatchEvent`\n      * ``scheduled`` - :class:`CloudWatchEvent`\n      * ``websocket`` - :class:`WebsocketEvent`\n      * ``http`` - :class:`Request`\n      * ``pure_lambda`` - :class:`LambdaFunctionEvent`\n\n      The decorated function must accept two arguments, ``event`` and\n      ``get_response``.  The ``event`` is the input event associated with\n      the Lambda invocation, and ``get_response`` is a callable that takes\n      an input event and will invoke the next middleware in the chain,\n      and eventually the original Lambda handler.  Below is a noop middleware\n      that shows the minimum needed to write middleware:\n\n      .. code-block:: python\n\n          @app.middleware('all')\n          def mymiddleware(event, get_response):\n              return get_response(event)\n\n      See :doc:`topics/middleware` for more information on writing middleware.\n\n   .. method:: register_middleware(func, event_type='all')\n\n      Register a middleware with a Chalice application.  This is the same\n      behavior as the :meth:`Chalice.middleware` decorator and is useful\n      if you want to register middleware for pre-existing functions:\n\n      .. code-block:: python\n\n          import thirdparty\n\n          app.register_middleware(thirdparty.func, 'all')\n\n.. class:: ConvertToMiddleware(lambda_wrapper)\n\n   This class is used to convert a function that wraps/proxies a Lambda\n   function into middleware.  This allows this wrapper to automatically\n   be applied to every function in your app.  For example, if you had the\n   following logging decorator:\n\n   .. code-block:: python\n\n       def log_invocation(func):\n           def wrapper(event, context):\n               logger.debug(\"Before lambda function.\")\n               response = func(event, context)\n               logger.debug(\"After lambda function.\")\n           return wrapper\n\n       @app.lambda_function()\n       @log_invocation\n       def myfunction(event, context):\n           logger.debug(\"In myfunction().\")\n\n\n   Rather than decorate every Lambda function with the ``@log_invocation``\n   decorator, you can instead use ``ConvertToMiddleware`` to automatically\n   apply this wrapper to every Lambda function in your app.\n\n   .. code-block:: python\n\n       app.register_middleware(ConvertToMiddleware(log_invoation))\n\n\nRequest\n=======\n\n.. class:: Request\n\n  A class that represents the current request.  This is mapped to\n  the ``app.current_request`` object.\n\n  .. code-block:: python\n\n      @app.route('/objects/{key}', methods=['GET', 'PUT'])\n      def myobject(key):\n          request = app.current_request  # type: Request\n          if request.method == 'PUT':\n              # handle PUT request\n              pass\n          elif request.method == 'GET':\n              # handle GET request\n              pass\n\n\n  .. attribute:: path\n\n     The path of the HTTP request.\n\n  .. attribute:: query_params\n\n     A MultiDict of the query params for the request.  This value is ``None``\n     if no query params were provided in the request. The MultiDict acts like a\n     normal dictionary except that you can call the method ``getlist()`` to get\n     multiple keys from the same query string parameter\n\n     .. code-block:: python\n\n          request = app.current_request\n          # Raises an exception if key doesn't exist, usual Python behavior.\n          single_param = request.query_params['single']\n\n          # None if key doesn't exist, usual Python behavior\n          another_param = request.query_params.get('another_param')\n\n          # A List of all parameters named multi_param, Throws an exception if\n          # key doesn't exist\n          multi_param_list = request.query_params.getlist('multi_param')\n\n  .. attribute:: headers\n\n     A dict of the request headers.\n\n  .. attribute:: uri_params\n\n     A dict of the captured URI params.  This value is ``None`` if no\n     URI params were provided in the request.\n\n  .. attribute:: method\n\n     The HTTP method as a string.\n\n  .. attribute:: json_body\n\n     The parsed JSON body (``json.loads(raw_body)``).  This value will only\n     be non-None if the Content-Type header is ``application/json``, which\n     is the default content type value in chalice.\n\n  .. attribute:: raw_body\n\n     The raw HTTP body as bytes.  This is useful if you need to\n     calculate a checksum of the HTTP body.\n\n  .. attribute:: context\n\n     A dict of additional context information.\n\n  .. attribute:: stage_vars\n\n     A dict of configuration for the API Gateway stage.\n\n   .. attribute:: lambda_context\n\n     A Lambda context object that is passed to the invoked view by AWS\n     Lambda. You can find out more about this object by reading the\n     `lambda context object documentation <https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html>`_.\n\n  .. method:: to_dict()\n\n     Convert the :class:`Request` object to a dictionary.  This is useful\n     for debugging purposes.  This dictionary is guaranteed to be JSON\n     serializable so you can return this value from a chalice view.\n\n\nResponse\n========\n\n.. class:: Response(body, headers=None, status_code=200)\n\n  A class that represents the response for the view function.  You\n  can optionally return an instance of this class from a view function if you\n  want complete control over the returned HTTP response.\n\n  .. code-block:: python\n\n      from chalice import Chalice, Response\n\n      app = Chalice(app_name='custom-response')\n\n\n      @app.route('/')\n      def index():\n          return Response(body='hello world!',\n                          status_code=200,\n                          headers={'Content-Type': 'text/plain'})\n\n\n  .. versionadded:: 0.6.0\n\n  .. attribute:: body\n\n     The HTTP response body to send back.  This value must be a string.\n\n  .. attribute:: headers\n\n     An optional dictionary of HTTP headers to send back.  This is a dictionary\n     of header name to header value, e.g ``{'Content-Type': 'text/plain'}``\n\n  .. attribute:: status_code\n\n     The integer HTTP status code to send back in the HTTP response.\n\n\nAuthorization\n=============\n\nEach of these classes below can be provided using the ``authorizer`` argument\nfor an ``@app.route(authorizer=...)`` call:\n\n\n.. code-block:: python\n\n    authorizer = CognitoUserPoolAuthorizer(\n        'MyPool', header='Authorization',\n        provider_arns=['arn:aws:cognito:...:userpool/name'])\n\n    @app.route('/user-pools', methods=['GET'], authorizer=authorizer)\n    def authenticated():\n        return {\"secure\": True}\n\n\n.. class:: CognitoUserPoolAuthorizer(name, provider_arns, header='Authorization')\n\n  .. versionadded:: 0.8.1\n\n  .. attribute:: name\n\n     The name of the authorizer.\n\n  .. attribute:: provider_arns\n\n     The Cognito User Pool arns to use.\n\n  .. attribute:: header\n\n     The header where the auth token will be specified.\n\n.. class:: IAMAuthorizer()\n\n  .. versionadded:: 0.8.3\n\n.. class:: CustomAuthorizer(name, authorizer_uri, ttl_seconds, header='Authorization')\n\n  .. versionadded:: 0.8.1\n\n  .. attribute:: name\n\n     The name of the authorizer.\n\n  .. attribute:: authorizer_uri\n\n     The URI of the lambda function to use for the custom authorizer.  This\n     usually has the form\n     ``arn:aws:apigateway:{region}:lambda:path/2015-03-31/functions/{lambda_arn}/invocations``.\n\n  .. attribute:: ttl_seconds\n\n     The number of seconds to cache the returned policy from a custom\n     authorizer.\n\n  .. attribute:: header\n\n     The header where the auth token will be specified.\n\n\nBuilt-in Authorizers\n--------------------\n\nThese classes are used when defining built-in authorizers in Chalice.\n\n.. class:: AuthRequest(auth_type, token, method_arn)\n\n   An instance of this class is passed as the first argument\n   to an authorizer defined via ``@app.authorizer()``.  You\n   generally do not instantiate this class directly.\n\n   .. attribute:: auth_type\n\n      The type of authentication\n\n   .. attribute:: token\n\n      The authorization token.  This is usually the value of the\n      ``Authorization`` header.\n\n   .. attribute:: method_arn\n\n      The ARN of the API gateway being authorized.\n\n.. class:: AuthResponse(routes, principal_id, context=None)\n\n   .. attribute:: routes\n\n      A list of authorized routes.  Each element in the list\n      can either by a string route such as `\"/foo/bar\"` or\n      an instance of ``AuthRoute``.  If you specify the URL as\n      a string, then all supported HTTP methods will be authorized.\n      If you want to specify which HTTP methods are allowed, you\n      can use ``AuthRoute``.  If you want to specify that all\n      routes and HTTP methods are supported you can use the\n      wildcard value of ``\"*\"``: ``AuthResponse(routes=['*'], ...)``\n\n   .. attribute:: principal_id\n\n      The principal id of the user.\n\n   .. attribute:: context\n\n      An optional dictionary of key value pairs.  This dictionary\n      will be accessible in the ``app.current_request.context``\n      in all subsequent authorized requests for this user.\n\n.. class:: AuthRoute(path, methods)\n\n   This class be used in the ``routes`` attribute of a\n   :class:`AuthResponse` instance to get fine grained control\n   over which HTTP methods are allowed for a given route.\n\n   .. attribute:: path\n\n      The allowed route specified as a string\n\n   .. attribute:: methods\n\n      A list of allowed HTTP methods.\n\n\nAPIGateway\n==========\n\n.. class:: APIGateway()\n\n   This class is used to control\n   how API Gateway interprets ``Content-Type`` headers in both requests and\n   responses.\n\n   There is a single instance of this class attached to each\n   :class:`Chalice` object under the ``api`` attribute.\n\n   .. attribute:: cors\n\n      Global cors configuration. If a route-level cors configuration is not\n      provided, or is ``None`` then this configuration will be used. By\n      default it is set to ``False``. This can either be ``True``, ``False``,\n      or an instance of the ``CORSConfig`` class. This makes it easy to enable\n      CORS for your entire application by setting ``app.api.cors = True``.\n\n      .. versionadded:: 1.12.1\n\n\n   .. attribute:: default_binary_types\n\n      The value of ``default_binary_types`` are the ``Content-Types`` that are\n      considered binary by default. This value should not be changed, instead\n      you should modify the ``binary_types`` list to change the behavior of a\n      content type. Its value is: ``application/octet-stream``,\n      ``application/x-tar``, ``application/zip``, ``audio/basic``,\n      ``audio/ogg``, ``audio/mp4``, ``audio/mpeg``, ``audio/wav``,\n      ``audio/webm``, ``image/png``, ``image/jpg``, ``image/jpeg``,\n      ``image/gif``, ``video/ogg``, ``video/mpeg``, ``video/webm``.\n\n\n   .. attribute:: binary_types\n\n      The value of ``binary_types`` controls how API Gateway interprets\n      requests and responses as detailed below.\n\n      If an incoming request has a ``Content-Type`` header value that is\n      present in the ``binary_types`` list it will be assumed that its body is\n      a sequence of raw bytes. You can access these bytes by accessing the\n      ``app.current_request.raw_body`` property.\n\n      If an outgoing response from ``Chalice`` has a header ``Content-Type``\n      that matches one of the ``binary_types`` its body must be a ``bytes``\n      type object. It is important to note that originating request must have\n      the ``Accept`` header for the same type as the ``Content-Type`` on the\n      response. Otherwise a ``400`` error will be returned.\n\n      This value can be modified to change what types API Gateway treats as\n      binary. The easiest way to do this is to simply append new types to\n      the list.\n\n      .. code-block:: python\n\n          app.api.binary_types.append('application/my-binary-data')\n\n\n      Keep in mind that there can only be a total of 25 binary types at a time\n      and Chalice by default has a list of 16 types. It is recommended if you\n      are going to make extensive use of binary types to reset the list to\n      the exact set of content types you will be using. This can easily be\n      done by reassigning the whole list.\n\n      .. code-block:: python\n\n          app.api.binary_types = [\n              'application/octet-stream',\n              'application/my-binary-data',\n          ]\n\n\n      **Implementation Note**: API Gateway and Lambda communicate through a\n      JSON event which is encoded using ``UTF-8``. The raw bytes are\n      temporarily encoded using base64 when being passed between API Gateway\n      and Lambda. In the worst case this encoding can cause the binary body\n      to be inflated up to ``4/3`` its original size. Lambda only accepts an\n      event up to ``6mb``, which means even if your binary data was not quite\n      at that limit, with the base64 encoding it may exceed that limit. This\n      will manifest as a ``502`` Bad Gateway error.\n\n\nWebsocketAPI\n============\n\n.. class:: WebsocketAPI\n\n   This class is used to send messages to websocket clients connected to an API\n   Gateway Websocket API.\n\n   .. attribute:: session\n\n      A boto3 Session that will be used to send websocket messages to\n      clients. Any custom configuration can be set through a botocore\n      ``session``. This **must** be manually set before websocket features can\n      be used.\n\n      .. code-block:: python\n\n         import botocore\n         from boto3.session import Session\n         from chalice import Chalice\n\n         app = Chalice('example')\n         session = botocore.session.Session()\n         session.set_config_variable('retries', {'max_attempts': 0})\n         app.websocket_api.session = Session(botocore_session=session)\n\n   .. method:: configure(domain_name, stage)\n\n      Configure prepares the :class:`WebsocketAPI` to call the :meth:`send`\n      method. Without first calling this method calls to :meth:`send` will fail\n      with the message ``WebsocketAPI needs to be configured before sending\n      messages.``. This is because a boto3 ``apigatewaymanagementapi`` client\n      must be created from the :attr:`session` with a custom endpoint in order\n      to properly communicate with our API Gateway WebsocketAPI. This method is\n      called on your behalf before each of the websocket handlers:\n      ``on_ws_connect``, ``on_ws_message``, ``on_ws_disconnect``. This ensures\n      that the :meth:`send` method is available in each of those handlers.\n\n.. _websocket-send:\n\n   .. method:: send(connection_id, message)\n\n      *requires* ``boto3>=1.9.91``\n\n      Method to send a message to a client. The ``connection_id`` is the unique\n      identifier of the socket to send the ``message`` to. The ``message`` must\n      be a utf-8 string.\n\n      If the socket is disconnected it raises a :class:`WebsocketDisconnectedError`\n      error.\n\n   .. method:: close(connection_id)\n\n      *requires* ``boto3>=1.9.221``\n\n      Method to close a WebSocket connection. The ``connection_id`` is the\n      unique identifier of the socket to close.\n\n      If the socket is already disconnected it raises a\n      :class:`WebsocketDisconnectedError` error.\n\n   .. method:: info(connection_id)\n\n      *requires* ``boto3>=1.9.221``\n\n      Method to get info about a WebSocket. The ``connection_id`` is the unique\n      identifier of the socket to get info about.\n\n      The following is an example of the format this method returns::\n\n       {\n           'ConnectedAt': datetime(2015, 1, 1),\n           'Identity': {\n               'SourceIp': 'string',\n               'UserAgent': 'string'\n           },\n           'LastActiveAt': datetime(2015, 1, 1)\n       }\n\n      If the socket is disconnected it raises a :class:`WebsocketDisconnectedError`\n      error.\n\n.. class:: WebsocketDisconnectedError\n\n   An exception raised when a message is sent to a websocket that has disconnected.\n\n   .. attribute:: connection_id\n\n      The unique identifier of the websocket that was disconnected.\n\n\nCORS\n====\n\n.. class:: CORSConfig(allow_origin='*', allow_headers=None, expose_headers=None, max_age=None, allow_credentials=None)\n\n  CORS configuration to attach to a route, or globally on ``app.api.cors``.\n\n  .. code-block:: python\n\n      from chalice import CORSConfig\n      cors_config = CORSConfig(\n          allow_origin='https://foo.example.com',\n          allow_headers=['X-Special-Header'],\n          max_age=600,\n          expose_headers=['X-Special-Header'],\n          allow_credentials=True\n      )\n\n      @app.route('/custom_cors', methods=['GET'], cors=cors_config)\n      def supports_custom_cors():\n          return {'cors': True}\n\n  .. versionadded:: 0.8.1\n\n  .. attribute:: allow_origin\n\n     The value of the ``Access-Control-Allow-Origin`` to send in the response.\n     Keep in mind that even though the ``Access-Control-Allow-Origin`` header\n     can be set to a string that is a space separated list of origins, this\n     behavior does not work on all clients that implement CORS. You should only\n     supply a single origin to the ``CORSConfig`` object. If you need to supply\n     multiple origins you will need to define a custom handler for it that\n     accepts ``OPTIONS`` requests and matches the ``Origin`` header against a\n     whitelist of origins.  If the match is successful then return just their\n     ``Origin`` back to them in the ``Access-Control-Allow-Origin`` header.\n\n  .. attribute:: allow_headers\n\n     The list of additional allowed headers.  This list is added to list of\n     built in allowed headers: ``Content-Type``, ``X-Amz-Date``,\n     ``Authorization``, ``X-Api-Key``, ``X-Amz-Security-Token``.\n\n  .. attribute:: expose_headers\n\n     A list of values to return for the ``Access-Control-Expose-Headers``:\n\n  .. attribute:: max_age\n\n     The value for the ``Access-Control-Max-Age``\n\n  .. attribute:: allow_credentials\n\n     A boolean value that sets the value of\n     ``Access-Control-Allow-Credentials``.\n\n\nEvent Sources\n=============\n\n.. versionadded:: 1.0.0b1\n\n.. class:: Rate(value, unit)\n\n  An instance of this class can be used as the ``expression`` value\n  in the :meth:`Chalice.schedule` method:\n\n  .. code-block:: python\n\n     @app.schedule(Rate(5, unit=Rate.MINUTES))\n     def handler(event):\n         pass\n\n  Examples:\n\n  .. code-block:: python\n\n      # Run every minute.\n      Rate(1, unit=Rate.MINUTES)\n\n      # Run every 2 hours.\n      Rate(2, unit=Rate.HOURS)\n\n  .. attribute:: value\n\n     An integer value that presents the amount of time to wait\n     between invocations of the scheduled event.\n\n  .. attribute:: unit\n\n     The unit of the provided ``value`` attribute.  This can be\n     either ``Rate.MINUTES``, ``Rate.HOURS``, or ``Rate.DAYS``.\n\n  .. attribute:: MINUTES, HOURS, DAYS\n\n     These values should be used for the ``unit`` attribute.\n\n\n.. class:: Cron(minutes, hours, day_of_month, month, day_of_week, year)\n\n  An instance of this class can be used as the ``expression`` value\n  in the :meth:`Chalice.schedule` method.\n\n  .. code-block:: python\n\n     @app.schedule(Cron(15, 10, '?', '*', '6L', '2002-2005'))\n     def handler(event):\n         pass\n\n  It provides more capabilities than the :class:`Rate`\n  class.  There are a few limits:\n\n  * You can't specify ``day_of_month`` and ``day_of_week`` fields in\n    the same Cron expression.  If you specify a value in one of the\n    fields, you must use a ``?`` in the other.\n  * Cron expressions that lead to rates faster than 1 minute are not\n    supported.\n\n  For more information, see the API\n  `docs page <https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/ScheduledEvents.html#CronExpressions>`__.\n\n  Examples:\n\n  .. code-block:: python\n\n      # Run at 10:00am (UTC) every day.\n      Cron(0, 10, '*', '*', '?', '*')\n\n      # Run at 12:15pm (UTC) every day.\n      Cron(15, 12, '*', '*', '?', '*')\n\n      # Run at 06:00pm (UTC) every Monday through Friday.\n      Cron(0, 18, '?', '*', 'MON-FRI', '*')\n\n      # Run at 08:00am (UTC) every 1st day of the month.\n      Cron(0, 8, 1, '*', '?', '*')\n\n      # Run every 15 minutes.\n      Cron('0/15', '*', '*', '*', '?', '*')\n\n      # Run every 10 minutes Monday through Friday.\n      Cron('0/10', '*', '?', '*', 'MON-FRI', '*')\n\n      # Run every 5 minutes Monday through Friday between\n      # 08:00am and 5:55pm (UTC).\n      Cron('0/5', '8-17', '?', '*', 'MON-FRI', '*')\n\n\n.. class:: CloudWatchEvent()\n\n   This is the input argument for a scheduled or CloudWatch events.\n\n   .. code-block:: python\n\n      @app.schedule('rate(1 hour)')\n      def every_hour(event: CloudWatchEvent):\n          pass\n\n   In the code example above, the ``event`` argument is of\n   type ``CloudWatchEvent``, which will have the following\n   attributes.\n\n   .. attribute:: version\n\n      By default, this is set to 0 (zero) in all events.\n\n   .. attribute:: account\n\n      The 12-digit number identifying an AWS account.\n\n   .. attribute:: region\n\n      Identifies the AWS region where the event originated.\n\n   .. attribute:: detail\n\n      For CloudWatch events this will be the event payload.\n      For scheduled events, this will be an empty dictionary.\n\n   .. attribute:: detail_type\n\n      For scheduled events, this value will be ``\"Scheduled Event\"``.\n\n   .. attribute:: source\n\n      Identifies the service that sourced the event. All events sourced from\n      within AWS will begin with \"aws.\" Customer-generated events can have any\n      value here as long as it doesn't begin with \"aws.\" We recommend the use\n      of java package-name style reverse domain-name strings.\n\n      For scheduled events, this will be ``aws.events``.\n\n   .. attribute:: time\n\n      The event timestamp, which can be specified by the service originating\n      the event. If the event spans a time interval, the service might choose\n      to report the start time, so this value can be noticeably before the time\n      the event is actually received.\n\n   .. attribute:: event_id\n\n      A unique value is generated for every event. This can be helpful in\n      tracing events as they move through rules to targets, and are processed.\n\n   .. attribute:: resources\n\n      This JSON array contains ARNs that identify resources that are involved\n      in the event. Inclusion of these ARNs is at the discretion of the\n      service.\n\n      For scheduled events, this will include the ARN of the CloudWatch\n      rule that triggered this event.\n\n   .. attribute:: context\n\n      A `Lambda context object <https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html>`_\n      that is passed to the handler by AWS Lambda. This is useful if you need\n      the AWS request ID for tracing, or any other data in the context object.\n\n   .. method:: to_dict()\n\n      Return the original event dictionary provided\n      from Lambda.  This is useful if you need direct\n      access to the lambda event, for example if a\n      new key is added to the lambda event that has not\n      been mapped as an attribute to the ``CloudWatchEvent``\n      object.  Example::\n\n          {'account': '123457940291',\n           'detail': {},\n           'detail-type': 'Scheduled Event',\n           'id': '12345678-b9f1-4667-9c5e-39f98e9a6113',\n           'region': 'us-west-2',\n           'resources': ['arn:aws:events:us-west-2:123457940291:rule/testevents-dev-every_minute'],\n           'source': 'aws.events',\n           'time': '2017-06-30T23:28:38Z',\n           'version': '0'}\n\n\n.. class:: S3Event()\n\n   This is the input argument for an S3 event.\n\n   .. code-block:: python\n\n      @app.on_s3_event(bucket='mybucket')\n      def event_handler(event: S3Event):\n          app.log.info(\"Event received for bucket: %s, key %s\",\n                       event.bucket, event.key)\n\n   In the code example above, the ``event`` argument is of\n   type ``S3Event``, which will have the following\n   attributes.\n\n   .. attribute:: bucket\n\n      The S3 bucket associated with the event.\n\n   .. attribute:: key\n\n      The S3 key name associated with the event.\n      The original key name in the S3 event payload\n      is URL encoded.  However, this ``key`` attribute automatically\n      URL decodes the key name for you.  If you need\n      access to the original URL encoded key name, you can\n      access it through the ``to_dict()`` method.\n\n   .. attribute:: context\n\n      A `Lambda context object <https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html>`_\n      that is passed to the handler by AWS Lambda. This is useful if you need\n      the AWS request ID for tracing, or any other data in the context object.\n\n   .. method:: to_dict()\n\n      Return the original event dictionary provided\n      from Lambda.  This is useful if you need direct\n      access to the lambda event, for example if a\n      new key is added to the lambda event that has not\n      been mapped as an attribute to the ``S3Event``\n      object.  Note that this event is not modified in any way.\n      This means that the key name of the S3 object is URL\n      encoded, which is the way that S3 sends this value\n      to Lambda.\n\n\n.. class:: SNSEvent()\n\n   This is the input argument for an SNS event handler.\n\n   .. code-block:: python\n\n      @app.on_sns_message(topic='mytopic')\n      def event_handler(event: SNSEvent):\n          app.log.info(\"Message received with subject: %s, message: %s\",\n                       event.subject, event.message)\n\n   In the code example above, the ``event`` argument is of\n   type ``SNSEvent``, which will have the following\n   attributes.\n\n   .. attribute:: subject\n\n      The subject of the SNS message that was published.\n\n   .. attribute:: message\n\n      The string value of the SNS message that was published.\n\n   .. attribute:: context\n\n      A `Lambda context object <https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html>`_\n      that is passed to the handler by AWS Lambda. This is useful if you need\n      the AWS request ID for tracing, or any other data in the context object.\n\n   .. method:: to_dict()\n\n      Return the original event dictionary provided\n      from Lambda.  This is useful if you need direct\n      access to the lambda event, for example if a\n      new key is added to the lambda event that has not\n      been mapped as an attribute to the ``SNSEvent``\n      object.\n\n\n.. class:: SQSEvent()\n\n   This is the input argument for an SQS event handler.\n\n   .. code-block:: python\n\n      @app.on_sqs_message(queue='myqueue')\n      def event_handler(event: SQSEvent):\n          app.log.info(\"Event: %s\", event.to_dict())\n\n   In the code example above, the ``event`` argument is of\n   type ``SQSEvent``.  An ``SQSEvent`` can have multiple\n   sqs messages associated with it.  To access the multiple\n   messages, you can iterate over the ``SQSEvent``.\n\n   .. method:: __iter__()\n\n      Iterate over individual SQS messages associated with\n      the event.  Each element in the iterable is of type\n      :class:`SQSRecord`.\n\n   .. attribute:: context\n\n      A `Lambda context object <https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html>`_\n      that is passed to the handler by AWS Lambda. This is useful if you need\n      the AWS request ID for tracing, or any other data in the context object.\n\n   .. method:: to_dict()\n\n      Return the original event dictionary provided\n      from Lambda.  This is useful if you need direct\n      access to the lambda event, for example if a\n      new key is added to the lambda event that has not\n      been mapped as an attribute to the ``SQSEvent``\n      object.\n\n.. class:: SQSRecord()\n\n   Represents a single SQS record within an :class:`SQSEvent`.\n\n   .. attribute:: body\n\n      The body of the SQS message.\n\n   .. attribute:: receipt_handle\n\n      The receipt handle associated with the message.  This is useful\n      if you need to manually delete an SQS message to account for\n      partial failures.\n\n   .. attribute:: context\n\n      A `Lambda context object <https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html>`_\n      that is passed to the handler by AWS Lambda.\n\n   .. method:: to_dict()\n\n      Return the original dictionary associated with the given\n      message. This is useful if you need direct\n      access to the lambda event.\n\n\n.. class:: KinesisEvent()\n\n   This is the input argument for a Kinesis data stream event handler.\n\n   .. code-block:: python\n\n      @app.on_kinesis_record(stream='mystream')\n      def event_handler(event: KinesisEvent):\n          app.log.info(\"Event: %s\", event.to_dict())\n\n   In the code example above, the ``event`` argument is of\n   type ``KinesisEvent``.  A ``KinesisEvent`` can have multiple\n   messages associated with it.  To access the multiple\n   messages, you can iterate over the ``KinesisEvent``.\n\n   .. method:: __iter__()\n\n      Iterate over individual Kinesis records associated with\n      the event.  Each element in the iterable is of type\n      :class:`KinesisRecord`.\n\n   .. attribute:: context\n\n      A `Lambda context object <https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html>`_\n      that is passed to the handler by AWS Lambda. This is useful if you need\n      the AWS request ID for tracing, or any other data in the context object.\n\n   .. method:: to_dict()\n\n      Return the original event dictionary provided\n      from Lambda.  This is useful if you need direct\n      access to the lambda event, for example if a\n      new key is added to the lambda event that has not\n      been mapped as an attribute to the ``SQSEvent``\n      object.\n\n.. class:: KinesisRecord()\n\n   Represents a single Kinesis record within a :class:`KinesisEvent`.\n\n   .. attribute:: data\n\n      The payload data for the Kinesis record.  This data is automatically\n      base64 decoded for you and will be a ``bytes`` type.\n\n   .. attribute:: sequence_number\n\n      The unique identifier of the record within its shard.\n\n   .. attribute:: partition_key\n\n      Identifies which shard in the stream the data record is assigned to.\n\n   .. attribute:: schema_version\n\n      Schema version for the record.\n\n   .. attribute:: timestamp\n\n      The approximate time that the record was inserted into the stream.  This\n      is automatically converted to a ``datetime.datetime`` object.\n\n   .. attribute:: context\n\n      A `Lambda context object <https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html>`_\n      that is passed to the handler by AWS Lambda.\n\n   .. method:: to_dict()\n\n      Return the original dictionary associated with the given\n      message. This is useful if you need direct\n      access to the lambda event.\n\n\n.. class:: DynamoDBEvent()\n\n   This is the input argument for a DynamoDB stream event handler.\n\n   .. code-block:: python\n\n      @app.on_dynamodb_record(stream_arn='arn:aws:us-west-2:.../stream')\n      def event_handler(event: DynamoDBEvent):\n          app.log.info(\"Event: %s\", event.to_dict())\n          for record in event:\n              app.log.info(record.to_dict())\n\n   In the code example above, the ``event`` argument is of\n   type ``DynamoDBEvent``.  A ``DynamoDBEvent`` can have multiple\n   messages associated with it.  To access the multiple\n   messages, you can iterate over the ``DynamoDBEvent``.\n\n   .. method:: __iter__()\n\n      Iterate over individual DynamoDB records associated with\n      the event.  Each element in the iterable is of type\n      :class:`DynamoDBRecord`.\n\n   .. attribute:: context\n\n      A `Lambda context object <https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html>`_\n      that is passed to the handler by AWS Lambda. This is useful if you need\n      the AWS request ID for tracing, or any other data in the context object.\n\n   .. method:: to_dict()\n\n      Return the original event dictionary provided\n      from Lambda.  This is useful if you need direct\n      access to the lambda event, for example if a\n      new key is added to the lambda event that has not\n      been mapped as an attribute to the ``SQSEvent``\n      object.\n\n.. class:: DynamoDBRecord()\n\n   Represents a single DynamoDB record within a :class:`DynamoDBEvent`.\n\n   .. attribute:: timestamp\n\n      The approximate time that the record was the stream record was created.\n      This is automatically converted to a ``datetime.datetime`` object.\n\n   .. attribute:: keys\n\n      The primary key attribute(s) for the DynamoDB item that was modified.\n\n   .. attribute:: new_image\n\n      The item in the DynamoDB table as it appeared after it was modified.\n\n   .. attribute:: old_image\n\n      The item in the DynamoDB table as it appeared before it was modified.\n\n   .. attribute:: sequence_number\n\n      The sequence number of the stream record.\n\n   .. attribute:: size_bytes\n\n      The size of the stream record, in bytes.\n\n   .. attribute:: stream_view_type\n\n      The type of data from the modified DynamoDB item.\n\n   .. attribute:: aws_region\n\n      The region associated with the event.\n\n   .. attribute:: event_id\n\n      A unique identifier for the event.\n\n   .. attribute:: event_name\n\n      The type of data modification that was performed on the DynamoDB table.\n      This can be: ``INSERT``, ``MODIFY``, or ``DELETE``.\n\n   .. attribute:: event_source_arn\n\n      The ARN of the DynamoDB stream.\n\n   .. attribute:: table_name\n\n      The name of the DynamoDB table associated with the stream.  This value is\n      computed from the ``event_source_arn`` parameter and will be an empty string\n      if Chalice is unable to parse the table name from the event source ARN.\n\n   .. attribute:: context\n\n      A `Lambda context object <https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html>`_\n      that is passed to the handler by AWS Lambda.\n\n   .. method:: to_dict()\n\n      Return the original dictionary associated with the given\n      message. This is useful if you need direct\n      access to the lambda event.\n\n\n.. class:: LambdaFunctionEvent()\n\n   This is the input argument of middleware registered to a\n   pure Lambda function (``@app.lambda_function()``).\n\n   .. attribute:: event\n\n      The original input event dictionary.\n\n   .. attribute:: context\n\n      A `Lambda context object <https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html>`_\n      that is passed to the handler by AWS Lambda.\n\nBlueprints\n==========\n\n.. class:: Blueprint(import_name)\n\n  An object used for grouping related handlers together.\n  This is primarily used as a mechanism for organizing your lambda\n  handlers.  Any decorator methods defined in the :class:`Chalice`\n  object are also defined on a ``Blueprint`` object.  You can register\n  a blueprint to a Chalice app using the :meth:`Chalice.register_blueprint`\n  method.\n\n  The ``import_name`` is the module in which the Blueprint is defined.\n  It is used to construct the appropriate handler string when creating\n  the Lambda functions associated with a Blueprint.  This is typically\n  the `__name__` attribute:``mybp = Blueprint(__name__)``.\n\n  See :doc:`topics/blueprints` for more information.\n\n  .. code-block:: python\n\n      # In ./app.py\n\n      from chalice import Chalice\n      from chalicelib import myblueprint\n\n      app = Chalice(app_name='blueprints')\n      app.register_blueprint(myblueprint)\n\n      # In chalicelib/myblueprint.py\n\n      from chalice import Blueprint\n\n      myblueprint = Blueprint(__name__)\n\n      @myblueprint.route('/')\n      def index():\n          return {'hello': 'world'}\n\n\nWebsockets\n==========\n.. _websocket-api:\n\n.. class:: WebsocketEvent()\n\n  Event object event that is passed as the sole arugment to any handler\n  function decorated with one of the three websocket related handlers:\n  ``on_ws_connect``, ``on_ws_disconnect``, ``on_ws_message``.\n\n  .. attribute:: domain_name\n\n     The domain name of the endpoint for the API Gateway Websocket API.\n\n  .. attribute:: stage\n\n     The API Gateway stage of the Websocket API.\n\n  .. attribute:: connection_id\n\n     A handle that uniquely identifies a connection with API Gateway.\n\n  .. attribute:: body\n\n     The message body received. This is only populated on the ``on_ws_message``\n     otherwise it will be set to ``None``.\n\n  .. attribute:: json_body\n\n     The parsed JSON body (``json.loads(body)``) of the message. If the body is\n     not JSON parsable then using this attribute will raise a ``ValueError``.\n\n  See :doc:`topics/websockets` for more information.\n\n\n.. _testing-api:\n\nTesting\n=======\n\n.. class:: Client(app, stage_name='dev', project_dir='.')\n\n  A test client used to write tests for Chalice apps.  It allows you to\n  test Lambda function invocation as well as REST APIs.  Depending\n  on what you want to test, you'll access the various attributes\n  of this class.  You can use this class as a context manager.\n  When entering the context manager, any environment variables\n  specified for your function will be set.  The original environment\n  variables are put back when the block is exited:\n\n  .. code-block:: python\n\n     from chalice.test import Client\n\n     with Client(app) as client:\n         result = client.http.post(\"/my-data\")\n\n  See the :doc:`topics/testing` documentation for more details on testing\n  your Chalice app.\n\n  .. attribute:: lambda_\n\n     Returns the Lambda test client :class:`TestLambdaClient`.\n\n  .. attribute:: http\n\n     Returns the test client for REST APIs :class:`TestHTTPClient`.\n\n  .. attribute:: events\n\n     Returns the test client for generating Lambda events\n     :class:`TestEventsClient`.\n\n.. class:: TestLambdaClient(import_name)\n\n   Test client for invoking Lambda functions.  This class should not be\n   instantiated directly, and instead should be accessed via the\n   ``Client.lambda_`` attribute:\n\n   .. code-block:: python\n\n      @app.lambda_function()\n      def myfunction(event, context):\n          return {\"hello\": \"world\"}\n\n      with Client(app) as client:\n          result = client.lambda_.invoke(\"myfunction\")\n          assert result.payload == {\"hello\": \"world\"}\n\n   .. method:: invoke(function_name, payload=None)\n\n      Invoke a Lambda function by name.  The name should match\n      the resource name of the function.  This is typically the\n      name of the python function unless an explicit ``name=``\n      kwarg is provided when registering the function.\n\n      Returns an :class:`InvokeResponse` instance.\n\n\n.. class:: TestHTTPClient(import_name)\n\n   Test client for REST APIs.  This class should not be\n   instantiated directly, and instead should be accessed via the\n   ``Client.http`` attribute:\n\n   .. code-block:: python\n\n      with Client(app) as client:\n          response = client.http.get(\"/my-route\")\n\n   .. method:: request(method, path, headers=None, body=b'')\n\n      Makes a test HTTP request to your REST API.  Returns\n      an :class:`HTTPResponse`.  You can also use the methods below\n      to make a request with a specific HTTP method instead of using\n      this method directly, e.g. ``client.http.get(\"/foo\")`` instead\n      of ``client.http.request(\"GET\", \"/foo\")``.\n\n   .. method:: get(path, \\*\\*kwargs)\n\n      Makes an HTTP GET request.\n\n   .. method:: post(path, \\*\\*kwargs)\n\n      Makes an HTTP POST request.\n\n   .. method:: put(path, \\*\\*kwargs)\n\n      Makes an HTTP PUT request.\n\n   .. method:: patch(path, \\*\\*kwargs)\n\n      Makes an HTTP PATCH request.\n\n   .. method:: options(path, \\*\\*kwargs)\n\n      Makes an HTTP OPTIONS request.\n\n   .. method:: delete(path, \\*\\*kwargs)\n\n      Makes an HTTP DELETE request.\n\n   .. method:: head(path, \\*\\*kwargs)\n\n      Makes an HTTP HEAD request.\n\n\n.. class:: TestEventsClient(import_name)\n\n   Test client for generating Lambda events.  This class should not be\n   instantiated directly, and instead should be accessed via the\n   ``Client.events`` attribute:\n\n   .. code-block:: python\n\n      with Client(app) as client:\n          result = client.lambda_.invoke(\n              \"my_sns_handler\",\n              client.events.generate_sns_event(\"Hello world\")\n          )\n\n   .. method:: generate_sns_event(message, subject='')\n\n      Generates a sample SNS event.\n\n   .. method:: generate_s3_event(bucket, key, event_name='ObjectCreated:Put')\n\n      Generates a sample S3 event.\n\n   .. method:: generate_sqs_event(message_bodies, queue_name='queue-name')\n\n      Generates a sample SQS event.\n\n   .. method:: generate_cw_event(source, detail_type, detail, resources, region='us-west-2')\n\n      Generates a sample CloudWatch event.\n\n   .. method:: generate_kinesis_event(message_bodies, stream_name='stream-name')\n\n      Generates a Kinesis event.\n\n\n.. class:: HTTPResponse()\n\n  .. attribute:: body\n\n     The body of the HTTP response, in ``bytes``.\n\n  .. attribute:: headers\n\n     A dictionary of HTTP headers in the resopnse.\n\n  .. attribute:: status_code\n\n     The status code of the HTTP response.\n\n.. class:: InvokeResponse(payload)\n\n  .. attribute:: payload\n\n     The response payload of Lambda invocation.\n\n\n.. _cdk-api:\n\nAWS CDK\n=======\n\nThe Chalice CDK construct is available in the ``chalice.cdk`` namespace.\nFor more details on using the AWS CDK with Chalice, see :doc:`tutorials/cdk`.\n\n.. code-block:: python\n\n   from chalice import cdk\n\n.. class:: cdk.Chalice(scope, id, source_dir, stage_config=None, preserve_logical_ids=True, \\*\\*kwargs)\n\n   A test client used to write tests for Chalice apps.  It allows you to\n\n   :param scope: The CDK scope that the construct is created within.\n   :param str id: The identifier for the construct.  Must be unique within the\n       scope in which it's created.\n   :param str source_dir: Path to Chalice application source code.\n   :param dict stage_config: Chalice stage configuration.\n       The configuration object should have the same structure as Chalice\n       JSON stage configuration.\n   :param bool preserve_logical_ids: Whether the resources should have\n       the same logical IDs in the resulting CDK template as they did in\n       the original CloudFormation template file. If you're vending a\n       Construct using cdk-chalice, make sure to pass this as ``False``.\n       Note: regardless of whether this option is true or false, the\n       :attr:`sam_template`'s ``get_resource`` and related methods always\n       uses the original logical ID of the resource/element, as specified\n       in the template file.\n   :raises `ChaliceError`: Error packaging the Chalice application.\n\n   .. code-block:: python\n\n      chalice = Chalice(\n          self, 'ChaliceApp', source_dir='../runtime',\n          stage_config={\n              'environment_variables': {\n                  'MY_ENV_VAR': 'FOO'\n              }\n          }\n      )\n\n\n   .. method:: get_resource(resource_name)\n\n      Returns a low-level CfnResource from the resources in a Chalice app with\n      the given resource name.  The resource name corresponds to the logical ID\n      of the underlying resource in the SAM template.  Any modifications\n      performed on that resource will be reflected in the resulting CDK\n      template.\n\n      :param str resource_name: The logical ID of the resource in the SAM template.\n      :rtype: aws_cdk.cdk.CfnResource\n\n   .. method:: get_role(resource_name)\n\n      Returns an ``IRole`` for an underlying SAM template resource.  This is useful\n      if you want to grant additional permissions to an IAM role constructed by Chalice.\n\n      .. code-block:: python\n\n        dynamodb_table = dynamodb.Table(...)\n        chalice = Chalice(scope, 'ChaliceApp', ....)\n        dynamodb_table.grant_read_write_data(chalice.get_role('DefaultRole'))\n\n      :param str resource_name: The logical ID of the resource in the SAM template.\n      :rtype: aws_cdk.aws_iam.IRole\n\n   .. method:: get_function(resource_name)\n\n      Returns an ``IFunction`` for an underlying SAM template resource.\n\n      :param str resource_name: The logical ID of the resource in the SAM template.\n      :rtype: aws_cdk.aws_lambda.IFunction\n\n   .. method:: add_environment_variable(key, value, function_name)\n\n      Convenience function to add environment variables to a single Lambda\n      function constructed by Chalice.  You can also add environment variables\n      to Lambda functions using the ``stage_config`` parameter when creating the\n      ``Chalice()`` construct.\n\n      :param str key: The environment variable key name.\n      :param str value: The value of the environment variable.\n      :param str function_name: The logical ID of the Lambda function resource.\n"
  },
  {
    "path": "docs/source/chalicedocs.py",
    "content": "# This code is based on the sphinxcontrib.youtube\n# code, but with support for python3 and a few other\n# changes.\n\nimport re\nfrom docutils import nodes\nfrom docutils.parsers.rst import directives, Directive\n\n\nCONTROL_HEIGHT = 30\n\n\ndef get_size(d, key):\n    if key not in d:\n        return None\n    m = re.match(\"(\\d+)(|%|px)$\", d[key])\n    if not m:\n        raise ValueError(\"invalid size %r\" % d[key])\n    return int(m.group(1)), m.group(2) or \"px\"\n\n\ndef css(d):\n    return \"; \".join(sorted(\"%s: %s\" % kv for kv in d.items()))\n\n\nclass youtube(nodes.General, nodes.Element):\n    pass\n\n\ndef visit_youtube_node(self, node):\n    aspect = node[\"aspect\"]\n    width = node[\"width\"]\n    height = node[\"height\"]\n\n    if aspect is None:\n        aspect = 16, 9\n\n    div_style = {}\n    if (height is None) and (width is not None) and (width[1] == \"%\"):\n        div_style = {\n            \"padding-top\": \"%dpx\" % CONTROL_HEIGHT,\n            \"padding-bottom\": \"%f%%\" % (width[0] * aspect[1] / aspect[0]),\n            \"width\": \"%d%s\" % width,\n            \"position\": \"relative\",\n            \"margin\": \"0 auto 30px auto\",\n        }\n        style = {\n            \"position\": \"absolute\",\n            \"top\": \"0\",\n            \"left\": \"0\",\n            \"width\": \"100%\",\n            \"height\": \"100%\",\n            \"border\": \"0\",\n        }\n        attrs = {\n            \"src\": \"https://www.youtube.com/embed/%s\" % node[\"id\"],\n            \"style\": css(style),\n        }\n    else:\n        if width is None:\n            if height is None:\n                width = 560, \"px\"\n            else:\n                width = height[0] * aspect[0] / aspect[1], \"px\"\n        if height is None:\n            height = width[0] * aspect[1] / aspect[0], \"px\"\n        style = {\n            \"width\": \"%d%s\" % width,\n            \"height\": \"%d%s\" % (height[0] + CONTROL_HEIGHT, height[1]),\n            \"border\": \"0\",\n        }\n        attrs = {\n            \"src\": \"https://www.youtube.com/embed/%s\" % node[\"id\"],\n            \"style\": css(style),\n        }\n    attrs[\"allowfullscreen\"] = \"true\"\n    div_attrs = {\n        \"CLASS\": \"youtube-wrapper\",\n        \"style\": css(div_style),\n    }\n    self.body.append(self.starttag(node, \"div\", **div_attrs))\n    self.body.append(self.starttag(node, \"iframe\", **attrs))\n    self.body.append(\"</iframe></div>\")\n\n\ndef depart_youtube_node(self, node):\n    pass\n\n\nclass YouTube(Directive):\n    has_content = True\n    required_arguments = 1\n    optional_arguments = 0\n    final_argument_whitespace = False\n    option_spec = {\n        \"width\": directives.unchanged,\n        \"height\": directives.unchanged,\n        \"aspect\": directives.unchanged,\n    }\n\n    def run(self):\n        if \"aspect\" in self.options:\n            aspect = self.options.get(\"aspect\")\n            m = re.match(\"(\\d+):(\\d+)\", aspect)\n            if m is None:\n                raise ValueError(\"invalid aspect ratio %r\" % aspect)\n            aspect = tuple(int(x) for x in m.groups())\n        else:\n            aspect = None\n        width = get_size(self.options, \"width\")\n        height = get_size(self.options, \"height\")\n        return [\n            youtube(id=self.arguments[0],\n                    aspect=aspect,\n                    width=width,\n                    height=height)\n        ]\n\n\ndef unsupported_visit_youtube(self, node):\n    self.builder.warn('youtube: unsupported output format (node skipped)')\n    raise nodes.SkipNode\n\n\n_NODE_VISITORS = {\n    'html': (visit_youtube_node, depart_youtube_node),\n    'latex': (unsupported_visit_youtube, None),\n    'man': (unsupported_visit_youtube, None),\n    'texinfo': (unsupported_visit_youtube, None),\n    'text': (unsupported_visit_youtube, None)\n}\n\n\ndef setup(app):\n    app.add_node(youtube, **_NODE_VISITORS)\n    app.add_directive(\"youtube\", YouTube)\n"
  },
  {
    "path": "docs/source/conf.py",
    "content": "# -*- coding: utf-8 -*-\n#\n# Chalice documentation build configuration file, created by\n# sphinx-quickstart on Tue May 17 14:09:17 2016.\n#\n# This file is execfile()d with the current directory set to its\n# containing dir.\n#\n# Note that not all possible configuration values are present in this\n# autogenerated file.\n#\n# All configuration values have a default; values that are commented out\n# serve to show the default.\n\nimport sys\nimport os\n\n_ROOT_SOURCE = os.path.dirname(os.path.abspath(__file__))\nsys.path.insert(0, _ROOT_SOURCE)\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#sys.path.insert(0, os.path.abspath('.'))\n\n# -- General configuration ------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\n#needs_sphinx = '1.0'\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = [\n    'sphinx.ext.autodoc',\n    'sphinx.ext.viewcode',\n    'chalicedocs',\n]\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix(es) of source filenames.\n# You can specify multiple suffix as a list of string:\n# source_suffix = ['.rst', '.md']\nsource_suffix = '.rst'\n\n# The encoding of source files.\n#source_encoding = 'utf-8-sig'\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# General information about the project.\nproject = u'AWS Chalice'\ncopyright = u'2016, James Saryerwinnie'\nauthor = u'James Saryerwinnie'\n\n# The version info for the project you're documenting, acts as replacement for\n# |version| and |release|, also used in various other places throughout the\n# built documents.\n#\n# The short X.Y version.\nversion = u'1.32'\n# The full version, including alpha/beta/rc tags.\nrelease = u'1.32.0'\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#\n# This is also used if you do content translation via gettext catalogs.\n# Usually you set \"language\" from the command line for these cases.\nlanguage = None\n\n# There are two options for replacing |today|: either, you set today to some\n# non-false value, then it is used:\n#today = ''\n# Else, today_fmt is used as the format for a strftime call.\n#today_fmt = '%B %d, %Y'\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\n# This patterns also effect to html_static_path and html_extra_path\nexclude_patterns = []\n\n# The reST default role (used for this markup: `text`) to use for all\n# documents.\n#default_role = None\n\n# If true, '()' will be appended to :func: etc. cross-reference text.\n#add_function_parentheses = True\n\n# If true, the current module name will be prepended to all description\n# unit titles (such as .. function::).\n#add_module_names = True\n\n# If true, sectionauthor and moduleauthor directives will be shown in the\n# output. They are ignored by default.\n#show_authors = False\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = 'sphinx'\n\n# A list of ignored prefixes for module index sorting.\n#modindex_common_prefix = []\n\n# If true, keep warnings as \"system message\" paragraphs in the built documents.\n#keep_warnings = False\n\n# If true, `todo` and `todoList` produce output, else they produce nothing.\ntodo_include_todos = False\n\n\n# -- Options for HTML output ----------------------------------------------\n\n# Our theme is based off the smithy theme but keep in mind that we heavily\n# modify parts of the theme.\nhtml_theme = 'smithy'\nhtml_theme_path = ['./theme']\nhtml_theme_options = {'ga_id': os.environ.get('_CHALICE_GA_ID', '')}\n\n\n# The name for this set of Sphinx documents.\n# \"<project> v<release> documentation\" by default.\n\nhtml_title = 'AWS Chalice'\n# A shorter title for the navigation bar.  Default is the same as html_title.\nhtml_short_title = 'Chalice'\n\n# The name of an image file (relative to this directory) to place at the top\n# of the sidebar.\n#html_logo = None\n\n# The name of an image file (relative to this directory) to use as a favicon of\n# the docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32\n# pixels large.\n#html_favicon = None\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = ['_static']\n\n# Add any extra paths that contain custom files (such as robots.txt or\n# .htaccess) here, relative to this directory. These files are copied\n# directly to the root of the documentation.\n#html_extra_path = []\n\n# If not None, a 'Last updated on:' timestamp is inserted at every page\n# bottom, using the given strftime format.\n# The empty string is equivalent to '%b %d, %Y'.\n#html_last_updated_fmt = None\n\n# If true, SmartyPants will be used to convert quotes and dashes to\n# typographically correct entities.\n#html_use_smartypants = True\n\n# Custom sidebar templates, maps document names to template names.\n#html_sidebars = {}\n\n# Additional templates that should be rendered to pages, maps page names to\n# template names.\n#html_additional_pages = {}\n\n# If false, no module index is generated.\n#html_domain_indices = True\n\n# If false, no index is generated.\n#html_use_index = True\n\n# If true, the index is split into individual pages for each letter.\n#html_split_index = False\n\n# If true, links to the reST sources are added to the pages.\n#html_show_sourcelink = True\n\n# If true, \"Created using Sphinx\" is shown in the HTML footer. Default is True.\n#html_show_sphinx = True\n\n# If true, \"(C) Copyright ...\" is shown in the HTML footer. Default is True.\n#html_show_copyright = True\n\n# If true, an OpenSearch description file will be output, and all pages will\n# contain a <link> tag referring to it.  The value of this option must be the\n# base URL from which the finished HTML is served.\n#html_use_opensearch = ''\n\n# This is the file name suffix for HTML files (e.g. \".xhtml\").\n#html_file_suffix = None\n\n# Language to be used for generating the HTML full-text search index.\n# Sphinx supports the following languages:\n#   'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'\n#   'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh'\n#html_search_language = 'en'\n\n# A dictionary with options for the search language support, empty by default.\n# 'ja' uses this config value.\n# 'zh' user can custom change `jieba` dictionary path.\n#html_search_options = {'type': 'default'}\n\n# The name of a javascript file (relative to the configuration directory) that\n# implements a search results scorer. If empty, the default will be used.\n#html_search_scorer = 'scorer.js'\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = 'Chalicedoc'\n\n\nprimary_domain = 'py'\n\nlinkcheck_retries = 5\n# The AWS Doc site now uses javascript to dynamically render the content,\n# so the anchors aren't going to exist in the static html content.  This\n# will fail the anchor checker so we have to disable this.\nlinkcheck_anchors = False\n"
  },
  {
    "path": "docs/source/faq.rst",
    "content": "Frequently Asked Questions\n==========================\n\n**Q: What is AWS Chalice?**\n\nAWS Chalice is a framework for writing serverless apps in python.\nIt consists of a CLI, a declarative python API for connecting\nevents to AWS Lambda functions, and a runtime component that\nprovides APIs accessible to your Lambda functions.\n\n**Q: Why should I use AWS Chalice?**\n\nChalice is designed for a seamless getting started experience\nthat can get you up and running quickly.  It handles all the\nboilerplate and low level details of creating a serverless\napplication, allowing you to focus on the business logic\nof your application.  It also provides deep integration with\nvarious AWS services allowing you to take advantage of the\nfeatures available in each service.\n\n**Q: How does Chalice compare to AWS SAM and the AWS CDK?**\n\nChalice is designed to work together with AWS SAM.\nSAM focus on provisioning the resources needed\nfor your application, and not necessarily on the application code\nitself.  Chalice provides a set of APIs to help you write your\napplication code, including a routing layer for REST and Websocket\nAPIs, and decorators to connect various AWS event sources to\nLambda functions.  It then can integrate with AWS SAM by offloading\nthe deployment to AWS CloudFormation.\n\n**Q: How does Chalice compare to other similiar frameworks?**\n\nThe biggest difference between Chalice and other frameworks is that Chalice\nis focused on using a familiar, decorator-based API to write serverless\npython applications that run on AWS Lambda.  Its goal is to make writing and\ndeploying these types of applications as simple as possible specifically for\nPython developers.  It was designed from the ground up to run in a\nserverless environment.\n\nTo achieve this goal, it has to make certain tradeoffs.  Chalice makes\nassumptions about how applications will be deployed, and it has restrictions on\nhow an application can be structured.  The feature set is purposefully small.\n"
  },
  {
    "path": "docs/source/index.rst",
    "content": "Documentation\n=============\n\n\nGetting Started\n---------------\n\n.. toctree::\n   :maxdepth: 2\n\n   quickstart\n\n\nTutorials\n---------\n\n.. toctree::\n   :maxdepth: 2\n\n   tutorials/index\n\n\nTopics\n------\n\n.. toctree::\n   :maxdepth: 2\n\n   topics/index\n\n\nAPI Reference\n-------------\n\n.. toctree::\n   :maxdepth: 2\n\n   api\n\n\nSample Apps\n-----------\n\n.. toctree::\n   :maxdepth: 2\n\n   samples/index\n\n\nUpgrade Notes\n-------------\n\n.. toctree::\n   :maxdepth: 2\n\n   upgrading\n\n\nFAQ\n---\n\n.. toctree::\n   :maxdepth: 1\n\n   faq\n\n\nIndices and tables\n==================\n\n* :ref:`genindex`\n* :ref:`search`\n"
  },
  {
    "path": "docs/source/main.rst",
    "content": ":orphan:\n\n.. include:: ./index.rst\n"
  },
  {
    "path": "docs/source/quickstart.rst",
    "content": "Quickstart\n==========\n\n.. include:: ../../README.rst\n  :start-after: quick-start-begin\n  :end-before: quick-start-end\n\nCleaning Up\n-----------\n\nIf you're done experimenting with Chalice and you'd like to cleanup, you can\nuse the ``chalice delete`` command, and Chalice will delete all the resources\nit created when running the ``chalice deploy`` command.\n\n::\n\n    $ chalice delete\n    Deleting Rest API: abcd4kwyl4\n    Deleting function aws:arn:lambda:region:123456789:helloworld-dev\n    Deleting IAM Role helloworld-dev\n\n\nNext Steps\n----------\n\nAt this point, there are several tutorials you can follow based on what\nyou're interested in:\n\n* :doc:`Creating REST APIs <tutorials/basicrestapi>` - Dive into more detail on\n  how to create a REST API using Chalice.  We'll explore URL parameters, error\n  messages, content types, CORs, and more.\n* :doc:`Event Sources <tutorials/events>` - In this tutorial, we'll focus on\n  difference event sources you can connect with a Lambda function other than a\n  REST API with Amazon API Gateway.\n* :doc:`Websockets <tutorials/wschat>` - In this tutorial, we'll show you\n  how to create a websocket API and create a sample chat application.\n\nYou can also jump into specific :doc:`topic guides <topics/index>`.  These are\nmore detailed than the tutorials, and provide more reference style\ndocumentation on specific features of Chalice.\n\nAnd finally, you can look at the :doc:`API Reference <api>` for detailed API\ndocumentation for Chalice.  This is useful if you know exactly what feature\nyou're using but need to lookup a specific parameter name or return value.\n"
  },
  {
    "path": "docs/source/samples/index.rst",
    "content": "Sample Applications\n===================\n\nBelow are a collection of Chalice sample applications.  They\nshow you how you can write more real-world serverless applications.\nThe code for all of these sample applications is available in\nthe `Chalice repository on GitHub\n<https://github.com/aws/chalice/tree/master/docs/source/samples>`__.\n\nFor each of these sample apps, we'll cover what is does,\nthe architecture of the app, how to deploy and test the app,\nand we'll walk through the key parts of the application code.\n\n:doc:`todo-app/index`\n  This app is a REST API that manages Todo\n  items.  These items are stored in an Amazon DynamoDB database.\n  The REST API is protected with JWT auth.  We show you how to\n  implement auth and login with Chalice's :ref:`builtin-authorizers`.\n\n:doc:`media-query/index`\n  This app shows how to create an image\n  processing pipeline that can analyze images and videos to detect\n  real world objects.  The results of this analysis are then\n  stored in a database and exposed through a queryable REST API.\n\n\n.. toctree::\n   :hidden:\n   :glob:\n\n   ./*/index\n"
  },
  {
    "path": "docs/source/samples/media-query/code/.chalice/config.json",
    "content": "{\n  \"version\": \"2.0\",\n  \"app_name\": \"media-query\",\n  \"stages\": {\n    \"dev\": {\n      \"api_gateway_stage\": \"api\",\n      \"autogen_policy\": false\n    }\n  }\n}\n"
  },
  {
    "path": "docs/source/samples/media-query/code/.chalice/policy-dev.json",
    "content": "{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": [\n        \"logs:CreateLogGroup\",\n        \"logs:CreateLogStream\",\n        \"logs:PutLogEvents\"\n      ],\n      \"Resource\": \"arn:aws:logs:*:*:*\",\n      \"Effect\": \"Allow\"\n    },\n    {\n      \"Action\": [\n        \"dynamodb:PutItem\",\n        \"dynamodb:DeleteItem\",\n        \"dynamodb:UpdateItem\",\n        \"dynamodb:GetItem\",\n        \"dynamodb:Scan\",\n        \"dynamodb:Query\"\n      ],\n      \"Resource\": [\n        \"arn:aws:dynamodb:*:*:table/media-query-*\"\n      ],\n      \"Effect\": \"Allow\"\n    },\n    {\n      \"Action\": [\n        \"rekognition:DetectLabels\",\n        \"rekognition:StartLabelDetection\",\n        \"rekognition:GetLabelDetection\"\n      ],\n      \"Resource\": \"*\",\n      \"Effect\": \"Allow\"\n    },\n    {\n      \"Action\": [\n         \"s3:GetObject\"\n      ],\n      \"Resource\": \"arn:aws:s3:::media-query*\",\n      \"Effect\": \"Allow\"\n    },\n    {\n      \"Action\": [\n         \"iam:PassRole\"\n      ],\n      \"Resource\": \"arn:aws:iam::*:role/media-query-*\",\n      \"Effect\": \"Allow\"\n    }\n  ]\n}\n"
  },
  {
    "path": "docs/source/samples/media-query/code/app.py",
    "content": "import json\nimport os\n\nimport boto3\nfrom chalice import Chalice\nfrom chalice import NotFoundError\nfrom chalicelib import db\nfrom chalicelib import rekognition\n\napp = Chalice(app_name='media-query')\n\n_MEDIA_DB = None\n_REKOGNITION_CLIENT = None\n_SUPPORTED_IMAGE_EXTENSIONS = (\n    '.jpg',\n    '.png',\n)\n_SUPPORTED_VIDEO_EXTENSIONS = (\n    '.mp4',\n    '.flv',\n    '.mov',\n)\n\n\ndef get_media_db():\n    global _MEDIA_DB\n    if _MEDIA_DB is None:\n        _MEDIA_DB = db.DynamoMediaDB(\n            boto3.resource('dynamodb').Table(\n                os.environ['MEDIA_TABLE_NAME']))\n    return _MEDIA_DB\n\n\ndef get_rekognition_client():\n    global _REKOGNITION_CLIENT\n    if _REKOGNITION_CLIENT is None:\n        _REKOGNITION_CLIENT = rekognition.RekognitonClient(\n            boto3.client('rekognition'))\n    return _REKOGNITION_CLIENT\n\n\n# Start of Event Handlers.\n@app.on_s3_event(bucket=os.environ['MEDIA_BUCKET_NAME'],\n                 events=['s3:ObjectCreated:*'])\ndef handle_object_created(event):\n    if _is_image(event.key):\n        _handle_created_image(bucket=event.bucket, key=event.key)\n    elif _is_video(event.key):\n        _handle_created_video(bucket=event.bucket, key=event.key)\n\n\n@app.on_s3_event(bucket=os.environ['MEDIA_BUCKET_NAME'],\n                 events=['s3:ObjectRemoved:*'])\ndef handle_object_removed(event):\n    if _is_image(event.key) or _is_video(event.key):\n        get_media_db().delete_media_file(event.key)\n\n\n@app.on_sns_message(topic=os.environ['VIDEO_TOPIC_NAME'])\ndef add_video_file(event):\n    message = json.loads(event.message)\n    labels = get_rekognition_client().get_video_job_labels(message['JobId'])\n    get_media_db().add_media_file(\n        name=message['Video']['S3ObjectName'],\n        media_type=db.VIDEO_TYPE,\n        labels=labels)\n\n\n@app.route('/')\ndef list_media_files():\n    params = {}\n    if app.current_request.query_params:\n        params = _extract_db_list_params(app.current_request.query_params)\n    return get_media_db().list_media_files(**params)\n\n\n@app.route('/{name}')\ndef get_media_file(name):\n    item = get_media_db().get_media_file(name)\n    if item is None:\n        raise NotFoundError('Media file (%s) not found' % name)\n    return item\n# End of Event Handlers.\n\n\ndef _extract_db_list_params(query_params):\n    valid_query_params = [\n        'startswith',\n        'media-type',\n        'label'\n    ]\n    return {\n        k.replace('-', '_'): v\n        for k, v in query_params.items() if k in valid_query_params\n    }\n\n\ndef _is_image(key):\n    return key.endswith(_SUPPORTED_IMAGE_EXTENSIONS)\n\n\ndef _handle_created_image(bucket, key):\n    labels = get_rekognition_client().get_image_labels(bucket=bucket, key=key)\n    get_media_db().add_media_file(key, media_type=db.IMAGE_TYPE, labels=labels)\n\n\ndef _is_video(key):\n    return key.endswith(_SUPPORTED_VIDEO_EXTENSIONS)\n\n\ndef _handle_created_video(bucket, key):\n    get_rekognition_client().start_video_label_job(\n        bucket=bucket, key=key, topic_arn=os.environ['VIDEO_TOPIC_ARN'],\n        role_arn=os.environ['VIDEO_ROLE_ARN']\n    )\n"
  },
  {
    "path": "docs/source/samples/media-query/code/chalicelib/__init__.py",
    "content": ""
  },
  {
    "path": "docs/source/samples/media-query/code/chalicelib/db.py",
    "content": "from boto3.dynamodb.conditions import Attr\n\n\nIMAGE_TYPE = 'image'\nVIDEO_TYPE = 'video'\n\n\nclass MediaDB(object):\n    def list_media_files(self, label=None):\n        pass\n\n    def add_media_file(self, name, media_type, labels=None):\n        pass\n\n    def get_media_file(self, name):\n        pass\n\n    def delete_media_file(self, name):\n        pass\n\n\nclass DynamoMediaDB(MediaDB):\n    def __init__(self, table_resource):\n        self._table = table_resource\n\n    def list_media_files(self, startswith=None, media_type=None, label=None):\n        scan_params = {}\n        filter_expression = None\n        if startswith is not None:\n            filter_expression = self._add_to_filter_expression(\n                filter_expression, Attr('name').begins_with(startswith)\n            )\n        if media_type is not None:\n            filter_expression = self._add_to_filter_expression(\n                filter_expression, Attr('type').eq(media_type)\n            )\n        if label is not None:\n            filter_expression = self._add_to_filter_expression(\n                filter_expression, Attr('labels').contains(label)\n            )\n        if filter_expression:\n            scan_params['FilterExpression'] = filter_expression\n        response = self._table.scan(**scan_params)\n        return response['Items']\n\n    def add_media_file(self, name, media_type, labels=None):\n        if labels is None:\n            labels = []\n        self._table.put_item(\n            Item={\n                'name': name,\n                'type': media_type,\n                'labels': labels,\n            }\n        )\n\n    def get_media_file(self, name):\n        response = self._table.get_item(\n            Key={\n                'name': name,\n            },\n        )\n        return response.get('Item')\n\n    def delete_media_file(self, name):\n        self._table.delete_item(\n            Key={\n                'name': name,\n            }\n        )\n\n    def _add_to_filter_expression(self, expression, condition):\n        if expression is None:\n            return condition\n        return expression & condition\n"
  },
  {
    "path": "docs/source/samples/media-query/code/chalicelib/rekognition.py",
    "content": "import uuid\n\n\nclass RekognitonClient(object):\n    def __init__(self, boto3_client):\n        self._boto3_client = boto3_client\n\n    def get_image_labels(self, bucket, key):\n        response = self._boto3_client.detect_labels(\n            Image={\n                'S3Object': {\n                    'Bucket': bucket,\n                    'Name': key\n                },\n            },\n            MinConfidence=50.0\n        )\n        return [label['Name'] for label in response['Labels']]\n\n    def start_video_label_job(self, bucket, key, topic_arn, role_arn):\n        response = self._boto3_client.start_label_detection(\n            Video={\n                'S3Object': {\n                    'Bucket': bucket,\n                    'Name': key\n                }\n            },\n            ClientRequestToken=str(uuid.uuid4()),\n            NotificationChannel={\n                'SNSTopicArn': topic_arn,\n                'RoleArn': role_arn\n            },\n            MinConfidence=50.0\n        )\n        return response['JobId']\n\n    def get_video_job_labels(self, job_id):\n        labels = set()\n        client_kwargs = {\n            'JobId': job_id,\n        }\n        response = self._boto3_client.get_label_detection(**client_kwargs)\n        self._collect_video_labels(labels, response)\n        while 'NextToken' in response:\n            client_kwargs['NextToken'] = response['NextToken']\n            response = self._boto3_client.get_label_detection(**client_kwargs)\n            self._collect_video_labels(labels, response)\n        return list(labels)\n\n    def _collect_video_labels(self, labels, response):\n        for label in response['Labels']:\n            label_name = label['Label']['Name']\n            labels.add(label_name)\n"
  },
  {
    "path": "docs/source/samples/media-query/code/recordresources.py",
    "content": "import argparse\nimport json\nimport os\n\nimport boto3\nfrom botocore import xform_name\n\n\ndef record_as_env_var(stack_name, stage):\n    cloudformation = boto3.client('cloudformation')\n    response = cloudformation.describe_stacks(\n        StackName=stack_name\n    )\n    outputs = response['Stacks'][0]['Outputs']\n    with open(os.path.join('.chalice', 'config.json')) as f:\n        data = json.load(f)\n        data['stages'].setdefault(stage, {}).setdefault(\n            'environment_variables', {}\n        )\n        for output in outputs:\n            data['stages'][stage]['environment_variables'][\n                _to_env_var_name(output['OutputKey'])] = output['OutputValue']\n    with open(os.path.join('.chalice', 'config.json'), 'w') as f:\n        serialized = json.dumps(data, indent=2, separators=(',', ': '))\n        f.write(serialized + '\\n')\n\n\ndef _to_env_var_name(name):\n    return xform_name(name).upper()\n\n\ndef main():\n    parser = argparse.ArgumentParser()\n    parser.add_argument('-s', '--stage', default='dev')\n    parser.add_argument('--stack-name', required=True)\n    args = parser.parse_args()\n    record_as_env_var(stack_name=args.stack_name, stage=args.stage)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "docs/source/samples/media-query/code/requirements.txt",
    "content": "boto3<1.15.0\n"
  },
  {
    "path": "docs/source/samples/media-query/code/resources.json",
    "content": "{\n  \"Outputs\": {\n    \"MediaBucketName\": {\n      \"Value\": {\n        \"Ref\": \"MediaBucket\"\n      }\n    },\n    \"MediaTableName\": {\n      \"Value\": {\n        \"Ref\": \"MediaTable\"\n      }\n    },\n    \"VideoTopicArn\": {\n      \"Value\": {\n        \"Ref\": \"VideoTopic\"\n      }\n    },\n    \"VideoTopicName\": {\n      \"Value\": {\n        \"Fn::GetAtt\": [\n          \"VideoTopic\",\n          \"TopicName\"\n        ]\n      }\n    },\n    \"VideoRoleArn\": {\n      \"Value\": {\n        \"Fn::GetAtt\": [\n          \"VideoRole\",\n          \"Arn\"\n        ]\n      }\n    }\n  },\n  \"Resources\": {\n    \"MediaBucket\": {\n      \"Type\": \"AWS::S3::Bucket\"\n    },\n    \"MediaTable\": {\n      \"Properties\": {\n        \"AttributeDefinitions\": [\n          {\n            \"AttributeName\": \"name\",\n            \"AttributeType\": \"S\"\n          }\n        ],\n        \"KeySchema\": [\n          {\n            \"AttributeName\": \"name\",\n            \"KeyType\": \"HASH\"\n          }\n        ],\n        \"ProvisionedThroughput\": {\n          \"ReadCapacityUnits\": 5,\n          \"WriteCapacityUnits\": 5\n        }\n      },\n      \"Type\": \"AWS::DynamoDB::Table\"\n    },\n    \"VideoTopic\": {\n      \"Type\": \"AWS::SNS::Topic\"\n    },\n    \"VideoRole\": {\n      \"Properties\": {\n        \"AssumeRolePolicyDocument\": {\n          \"Statement\": [\n            {\n              \"Effect\": \"Allow\",\n              \"Action\": [\n                \"sts:AssumeRole\"\n              ],\n              \"Principal\": {\n                \"Service\": [\n                  \"rekognition.amazonaws.com\"\n                ]\n              }\n            }\n          ]\n        },\n        \"Policies\": [\n          {\n            \"PolicyName\": \"RekognitionPublish\",\n            \"PolicyDocument\": {\n              \"Statement\": [\n                {\n                  \"Effect\": \"Allow\",\n                  \"Action\": [\n                    \"sns:Publish\"\n                  ],\n                  \"Resource\": [\n                    {\n                      \"Ref\": \"VideoTopic\"\n                    }\n                  ]\n                }\n              ]\n            }\n          }\n        ]\n      },\n      \"Type\": \"AWS::IAM::Role\"\n    }\n  }\n}\n"
  },
  {
    "path": "docs/source/samples/media-query/index.rst",
    "content": "=======================\nMedia Query Application\n=======================\n\n\n.. youtube:: UCZXJpI1dKw\n   :width: 75%\n\n\nThis sample application shows how to combine multiple event handlers in Chalice\nto create an image processing pipeline.  It takes as input any image or video\nand it will identify objects, people, text, scenes, and activities.  This\nresults of this analysis can then be queried with a REST API.\n\n.. image:: docs/assets/appexample.jpg\n  :width: 100%\n  :alt: Application Example\n\nThere are several components of this application.  The first part is an image\nprocessing pipeline.  The application is registered to automatically process\nany media that's uploaded to an Amazon S3 bucket.  The application will then\nuse Amazon Rekognition to automatically detect labels in either the image\nor the video.  The returned labels are then stored in an Amazon DynamoDB\ntable.\n\nFor videos, an asynchronous job is started.  This is because the analysis\nfor videos takes longer than analyzing images so we don't want our Lambda\nfunction to block until the job is complete.  To handle this asynchronous\njob, we subscribe to an Amazon SNS topic.  When the asynchronous job\nis finished analyzing our uploaded video, an event handler is called that\nwill retrieve the results and store the labels in Amazon DynamoDB.\n\nThe final component is the REST API.  This allows users to query for\nlabels associated with the media that has been uploaded.\n\nYou can find the full source code for this application in our\n`samples directory on GitHub <https://github.com/aws/chalice/tree/master/docs/source/samples/media-query/code/>`__.\n\n::\n\n    $ git clone git://github.com/aws/chalice\n    $ cd chalice/docs/source/samples/media-query/code\n\n\nWe'll now walk through the architecture of the application, how to\ndeploy and use the application, and go over the application code.\n\n\n.. note::\n    This sample application is also available as a `workshop\n    <https://chalice-workshop.readthedocs.io/en/latest/media-query/index.html>`__.\n    The main difference between the sample apps here and the Chalice workshops\n    is that the workshop is a detailed step by step process for how to create\n    this application from scratch.  You build the app by gradually adding each\n    feature piece by piece.  It takes several hours to work through all the\n    workshop material.  In this document we review the architecture,\n    the deployment process, then walk through the main sections of the code.\n\n\nArchitecture\n============\n\nBelow is the architecture for the application.\n\n.. image:: docs/assets/architecture.png\n  :width: 100%\n  :alt: Architecture diagram\n\nThe main components of the application are as follows:\n\n* ``handle_object_created``: A Lambda function that is triggered when an\n  object is uploaded to a S3 bucket. If the object is an image, it will\n  call Amazon Rekognition's ``DetectLabels`` API to detect objects in the\n  image. With the detected objects, the Lambda function will then add the\n  object to an Amazon DynamoDB table. If the object is a video, it will call\n  Rekognition's ``StartLabelDetection`` API to initiate an asynchronous\n  job to detect labels in the video. When the job is completed, a completion\n  notification is pushed to an SNS topic.\n\n* ``handle_object_deleted``: A Lambda function that removes the object from\n  the DynamoDB table if the object is deleted from the S3 bucket.\n\n* ``add_video_labels``: A Lambda function that is triggered on video label\n  detection SNS messages. On invocation, it will call Rekognition's\n  ``GetLabelDetection`` API to retrieve all detected objects from the video.\n  It then adds the video with its labels to the DynamoDB Table\n\n* ``api_handler``: A Lambda function that is invoked by HTTP requests to\n  Amazon API Gateway. On invocation, it will query the database based on the\n  received HTTP request and return the results to the user through API Gateway.\n\n\nDeployment\n==========\n\nFirst, we'll setup our development environment by cloning the Chalice\nGitHub repository and copying the sample code in a new directory::\n\n    $ git clone git://github.com/aws/chalice\n    $ mkdir /tmp/demo\n    $ cp -r chalice/docs/source/samples/media-query/code/ /tmp/demo/media-query\n    $ cd /tmp/demo/media-query/\n\nNext configure a virtual environment that uses Python 3.  In this example\nwe're using Python 3.7.\n\n::\n  $ python3 -m venv /tmp/venv37\n  $ . /tmp/venv37/bin/activate\n\nTo deploy the application, first install the necessary requirements and\ninstall Chalice::\n\n  $ pip install -r requirements.txt\n  $ pip install chalice\n\n\nWe'll also be using the AWS CLI to help deploy our application, you can\nfollow the `installation instructions <https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html>`__\nif you don't have the AWS CLI installed.\n\nNext, we'll use the AWS CLI to deploy a CloudFormation stack containing the S3\nbucket, DynamoDB table, and SNS topic needed to run this application::\n\n  $ aws cloudformation deploy --template-file resources.json \\\n      --stack-name media-query --capabilities CAPABILITY_IAM\n\n\nRecord the deployed resources as environment variables in the Chalice\napplication by running the `recordresources.py` script::\n\n  $ python recordresources.py --stack-name media-query\n\nYou can see these values by looking at the ``.chalice/config.json`` file.\nOnce those resources are created and recorded, deploy the Chalice application::\n\n  $ chalice deploy\n\n\nUsing the Application\n=====================\n\nOnce the application is deployed, use the AWS CLI to fetch the name of the\nbucket that is storing the media files::\n\n   $ aws cloudformation describe-stacks --stack-name media-query \\\n       --query \"Stacks[0].Outputs[?OutputKey=='MediaBucketName'].OutputValue\" \\\n       --output text\n   media-query-mediabucket-xtrhd3c4b59\n\n\nUpload some sample media files to your Amazon S3 bucket so the system populates\ninformation about the media files in your DynamoDB table.  If you need sample\nmedia files, you can use the included samples from the corresponding\n`Chalice workshop <https://chalice-workshop.readthedocs.io/en/latest/media-query/index.html>`__ assets\n`here <https://github.com/aws-samples/chalice-workshop/tree/master/code/media-query/final/assets>`__.\n\n::\n\n   $ aws s3 cp assets/sample.jpg s3://media-query-mediabucket-xtrhd3c4b59/sample.jpg\n   $ aws s3 cp assets/sample.mp4 s3://media-query-mediabucket-xtrhd3c4b59/sample.mp4\n\n\nWait about a minute for the media files to be populated in the database and\nthen install HTTPie::\n\n    $ pip install httpie\n\n\nThen, list out all if the media files using the application's API with HTTPie::\n\n    $ chalice url\n    https://qi5hf4djdg.execute-api.us-west-2.amazonaws.com/api/\n\n    $ http https://qi5hf4djdg.execute-api.us-west-2.amazonaws.com/api/\n    HTTP/1.1 200 OK\n    Connection: keep-alive\n    Content-Length: 279\n    Content-Type: application/json\n    Date: Tue, 10 Jul 2018 17:58:40 GMT\n    Via: 1.1 fa751ee53e2bf18781ae98b293ff9375.cloudfront.net (CloudFront)\n    X-Amz-Cf-Id: sNnrzvbdvgj1ZraySJvfSUbHthC_fok8l5GJ7glV4QcED_M1c8tlvg==\n    X-Amzn-Trace-Id: Root=1-5b44f3d0-4546157e8f5e35a008d06d88;Sampled=0\n    X-Cache: Miss from cloudfront\n    x-amz-apigw-id: J0sIlHs3vHcFj9g=\n    x-amzn-RequestId: e0aaf4e1-846a-11e8-b756-99d52d342d60\n\n    [\n        {\n            \"labels\": [\n                \"Animal\",\n                \"Canine\",\n                \"Dog\",\n                \"German Shepherd\",\n                \"Mammal\",\n                \"Pet\",\n                \"Collie\"\n            ],\n            \"name\": \"sample.jpg\",\n            \"type\": \"image\"\n        },\n        {\n            \"labels\": [\n                \"Human\",\n                \"Clothing\",\n                \"Dog\",\n                \"Nest\",\n                \"Person\",\n                \"Footwear\",\n                \"Bird Nest\",\n                \"People\",\n                \"Animal\",\n                \"Husky\"\n            ],\n            \"name\": \"sample.mp4\",\n            \"type\": \"video\"\n        }\n    ]\n\nYou can include query string parameters as well to query all objects based\non what the file name starts with, the type of the media file, and the detected\nobjects in the media file::\n\n    $ http https://qi5hf4djdg.execute-api.us-west-2.amazonaws.com/api/ startswith==sample.m\n    HTTP/1.1 200 OK\n    Connection: keep-alive\n    Content-Length: 153\n    Content-Type: application/json\n    Date: Tue, 10 Jul 2018 19:20:02 GMT\n    Via: 1.1 aa42484f82c16d99015c599631def20c.cloudfront.net (CloudFront)\n    X-Amz-Cf-Id: euqlOlWN5k5V_zKCJy4SL988Vcje6W5jDR88GrWr5uYGH-_ZvN4arg==\n    X-Amzn-Trace-Id: Root=1-5b4506e0-db041a3492ee56e8f3d9457c;Sampled=0\n    X-Cache: Miss from cloudfront\n    x-amz-apigw-id: J04DHE92PHcF--Q=\n    x-amzn-RequestId: 3d82319d-8476-11e8-86d9-a1e4585e5c26\n\n    [\n        {\n            \"labels\": [\n                \"Human\",\n                \"Clothing\",\n                \"Dog\",\n                \"Nest\",\n                \"Person\",\n                \"Footwear\",\n                \"Bird Nest\",\n                \"People\",\n                \"Animal\",\n                \"Husky\"\n            ],\n            \"name\": \"sample.mp4\",\n            \"type\": \"video\"\n        }\n    ]\n\n    $ http https://qi5hf4djdg.execute-api.us-west-2.amazonaws.com/api/ media-type==image\n    HTTP/1.1 200 OK\n    Connection: keep-alive\n    Content-Length: 126\n    Content-Type: application/json\n    Date: Tue, 10 Jul 2018 19:20:53 GMT\n    Via: 1.1 88eb066576c1b47cd896ab0019b9f25f.cloudfront.net (CloudFront)\n    X-Amz-Cf-Id: rwuOwzLKDM4KgcSBXFihWeNNsYSpZDYVpc8IXdT0xOu8qz8aA2Pj3w==\n    X-Amzn-Trace-Id: Root=1-5b450715-de71cf04ca2900b839ff1194;Sampled=0\n    X-Cache: Miss from cloudfront\n    x-amz-apigw-id: J04LaE6YPHcF3VA=\n    x-amzn-RequestId: 5d29d59a-8476-11e8-a347-ebb5d5f47789\n\n    [\n        {\n            \"labels\": [\n                \"Animal\",\n                \"Canine\",\n                \"Dog\",\n                \"German Shepherd\",\n                \"Mammal\",\n                \"Pet\",\n                \"Collie\"\n            ],\n            \"name\": \"sample.jpg\",\n            \"type\": \"image\"\n        }\n    ]\n\n    $ http https://qi5hf4djdg.execute-api.us-west-2.amazonaws.com/api/ label==Person\n    HTTP/1.1 200 OK\n    Connection: keep-alive\n    Content-Length: 153\n    Content-Type: application/json\n    Date: Tue, 10 Jul 2018 19:20:02 GMT\n    Via: 1.1 aa42484f82c16d99015c599631def20c.cloudfront.net (CloudFront)\n    X-Amz-Cf-Id: euqlOlWN5k5V_zKCJy4SL988Vcje6W5jDR88GrWr5uYGH-_ZvN4arg==\n    X-Amzn-Trace-Id: Root=1-5b4506e0-db041a3492ee56e8f3d9457c;Sampled=0\n    X-Cache: Miss from cloudfront\n    x-amz-apigw-id: J04DHE92PHcF--Q=\n    x-amzn-RequestId: 3d82319d-8476-11e8-86d9-a1e4585e5c26\n\n    [\n        {\n            \"labels\": [\n                \"Human\",\n                \"Clothing\",\n                \"Dog\",\n                \"Nest\",\n                \"Person\",\n                \"Footwear\",\n                \"Bird Nest\",\n                \"People\",\n                \"Animal\",\n                \"Husky\"\n            ],\n            \"name\": \"sample.mp4\",\n            \"type\": \"video\"\n        }\n    ]\n\n\nYou can also query for a specific object::\n\n    $ http https://qi5hf4djdg.execute-api.us-west-2.amazonaws.com/api/sample.jpg\n    HTTP/1.1 200 OK\n    Connection: keep-alive\n    Content-Length: 126\n    Content-Type: application/json\n    Date: Tue, 10 Jul 2018 19:20:53 GMT\n    Via: 1.1 88eb066576c1b47cd896ab0019b9f25f.cloudfront.net (CloudFront)\n    X-Amz-Cf-Id: rwuOwzLKDM4KgcSBXFihWeNNsYSpZDYVpc8IXdT0xOu8qz8aA2Pj3w==\n    X-Amzn-Trace-Id: Root=1-5b450715-de71cf04ca2900b839ff1194;Sampled=0\n    X-Cache: Miss from cloudfront\n    x-amz-apigw-id: J04LaE6YPHcF3VA=\n    x-amzn-RequestId: 5d29d59a-8476-11e8-a347-ebb5d5f47789\n\n    [\n        {\n            \"labels\": [\n                \"Animal\",\n                \"Canine\",\n                \"Dog\",\n                \"German Shepherd\",\n                \"Mammal\",\n                \"Pet\",\n                \"Collie\"\n            ],\n            \"name\": \"sample.jpg\",\n            \"type\": \"image\"\n        }\n    ]\n\n\nCode Walkthrough\n================\n\nWe'll take a top-down approach with this application and start with the main\nentry point, the ``app.py`` file.  The source code for this application is\nsplit between the ``app.py`` as well as supporting code in ``chalicelib/``.\n\nEvent Handlers\n--------------\n\nIn the ``app.py`` we see four different decorator types, each corresponding\nto Lambda functions that are triggered by different events.  Note that\nthe line numbers correspond to the line numbers in the ``app.py`` file.\n\n\n.. literalinclude:: code/app.py\n   :lineno-match:\n   :start-after: # Start of Event Handlers\n   :end-before: # End of Event Handlers\n\n\nThe first two decorators use ``@app.on_s3_event`` and are specifying\nthat these two Lambda functions should be invoked when an object is\ncreated or deleted from S3, respectively.  The name of the S3 bucket\nis not hardcoded in the ``app.py`` file but instead pulled from the\nenvironment variable ``MEDIA_BUCKET_NAME``.  The ``recordresources.py``\nscript that was run as part of the deployment process described above\nautomatically created these resources and updated the Chalice config file\n(``.chalice/config.json``) with these values.  If you look at the\ncontents of your ``.chalice/config.json`` file, it should look something\nlike this:\n\n\n.. code-block:: json\n\n    {\n      \"version\": \"2.0\",\n      \"app_name\": \"media-query\",\n      \"stages\": {\n        \"dev\": {\n          \"api_gateway_stage\": \"api\",\n          \"autogen_policy\": false,\n          \"environment_variables\": {\n            \"MEDIA_TABLE_NAME\": \"media-query-MediaTable-10QEPR0O8DOT4\",\n            \"MEDIA_BUCKET_NAME\": \"media-query-mediabucket-fb8oddjbslv1\",\n            \"VIDEO_TOPIC_NAME\": \"media-query-VideoTopic-KU38EEHIIUV1\",\n            \"VIDEO_ROLE_ARN\": \"arn:aws:iam::123456789123:role/media-query-VideoRole-1GKK0CA30VCAD\",\n            \"VIDEO_TOPIC_ARN\": \"arn:aws:sns:us-west-2:123456789123:media-query-VideoTopic-KU38EEHIIUV1\"\n          }\n        }\n      }\n    }\n\nNext, the ``@app.on_sns_message`` is used to connect an SNS topic to our\nLambda function.  This is only used for video processing with Rekognition.\nBecause of the longer processing times of video compared to images,\nvideo analysis is performed by first starting a \"video label job\".  When\nyou start this asynchronous job, we can specify an SNS topic that\nRekognition will publish to when the job is complete, as shown in the\n``_handle_created_video`` function below.\n\n\n.. literalinclude:: code/app.py\n  :linenos:\n  :lineno-match:\n  :pyobject: _handle_created_video\n\nThe ``add_video_file()`` function will then query for the results\nof the job (the ``JobId`` is provided as part of the SNS message\nthat's published) and store the results in the DynamoDB table.\n\nThe final two decorators of this app creates a REST API with\nAmazon API Gateway and defines two routes: ``/`` and ``/{name}``.\n\nRequesting the root URL of ``/`` is equivalent to a \"List\" API call that will\nreturn all the media files that have been analyzed so far.  Request\n``/{name}``, where ``{name}`` is the name of the media file that was uploaded\nto S3 will return the detected labels for that single resource.  This is\nequivalent to a \"Get\" API call.\n\n.. note::\n  This sample application returns all analyzed media files in its\n  List API call.  In practice, you should paginate your List API calls\n  to ensure you don't return unbounded results.\n\n\nSupporting Files\n----------------\n\nThe event handlers described in the previous section interact with\nRekognition and DynamoDB through clients that are accessed through\n``get_rekognition_client()`` and ``get_rekognition_client()``\nrespectively.  These clients are high level wrappers to the corresponding\nboto3 clients/resources for these services.  The code for these\nhigh level clients are in ``chalicelib/rekognition.py`` and\n``chalicelib/db.py``.  If we look at the ``DynamoMediaDB.add_media_file()``\nmethod in the ``chalicelib/db.py`` file, we see that it's a small wrapper\naround the ``put_item()`` operation of the underlying DynamoDB API:\n\n.. literalinclude:: code/chalicelib/db.py\n  :linenos:\n  :lineno-match:\n  :pyobject: DynamoMediaDB.add_media_file\n\n\nWe see a similar pattern in ``chalicelib/rekognition.py``.  Here's the\n``start_video_label_job`` job that starts the asynchronous processing\ndiscussed in the previous section.\n\n.. literalinclude:: code/chalicelib/rekognition.py\n  :linenos:\n  :lineno-match:\n  :pyobject: RekognitonClient.start_video_label_job\n\nAs you can see, it's a small wrapper around the ``start_label_detection``\noperation of the underlying Rekognition API.\n\nWe encourage you to look through the rest of the ``chalicelib/`` directory\nto see how these high level clients are implemented.\n\nCleaning Up\n===========\n\nIf you're done experimenting with this sample app, you can run these commands\nto delete this app.\n\n\n1. Delete the chalice application::\n\n    $ chalice delete\n    Deleting Rest API: kyfn3gqcf0\n    Deleting function: arn:aws:lambda:us-west-2:123456789123:function:media-query-dev\n    Deleting IAM role: media-query-dev-api_handler\n    Deleting function: arn:aws:lambda:us-west-2:123456789123:function:media-query-dev-add_video_file\n    Deleting IAM role: media-query-dev-add_video_file\n    Deleting function: arn:aws:lambda:us-west-2:123456789123:function:media-query-dev-handle_object_removed\n    Deleting IAM role: media-query-dev-handle_object_removed\n    Deleting function: arn:aws:lambda:us-west-2:123456789123:function:media-query-dev-handle_object_created\n    Deleting IAM role: media-query-dev-handle_object_created\n\n2. Delete all objects in your S3 bucket::\n\n    $ aws s3 rm s3://$MEDIA_BUCKET_NAME --recursive\n    delete: s3://media-query-mediabucket-4b1h8anboxpa/sample.jpg\n    delete: s3://media-query-mediabucket-4b1h8anboxpa/sample.mp4\n\n3. Delete the CloudFormation stack containing the additional AWS resources::\n\n    $ aws cloudformation delete-stack --stack-name media-query\n"
  },
  {
    "path": "docs/source/samples/todo-app/code/.chalice/config.json",
    "content": "{\n  \"stages\": {\n    \"dev\": {\n      \"autogen_policy\": false,\n      \"api_gateway_stage\": \"api\"\n    }\n  },\n  \"version\": \"2.0\",\n  \"app_name\": \"mytodo\"\n}\n"
  },
  {
    "path": "docs/source/samples/todo-app/code/.chalice/policy-dev.json",
    "content": "{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": [\n        \"logs:CreateLogGroup\",\n        \"logs:CreateLogStream\",\n        \"logs:PutLogEvents\"\n      ],\n      \"Resource\": \"arn:aws:logs:*:*:*\",\n      \"Effect\": \"Allow\"\n    },\n    {\n      \"Action\": [\n        \"dynamodb:PutItem\",\n        \"dynamodb:DeleteItem\",\n        \"dynamodb:UpdateItem\",\n        \"dynamodb:GetItem\",\n        \"dynamodb:Scan\",\n        \"dynamodb:Query\"\n      ],\n      \"Resource\": [\n        \"arn:aws:dynamodb:*:*:table/todo-app-*\",\n        \"arn:aws:dynamodb:*:*:table/users-app-*\"\n      ],\n      \"Effect\": \"Allow\"\n    },\n    {\n      \"Action\": [\n        \"ssm:GetParameter\"\n      ],\n      \"Resource\": [\n\t\"arn:aws:ssm:*:*:parameter/todo-sample-app/auth-key\"\n      ],\n      \"Effect\": \"Allow\"\n    }\n  ]\n}\n"
  },
  {
    "path": "docs/source/samples/todo-app/code/.gitignore",
    "content": ".chalice/deployments/\n.chalice/venv/\n"
  },
  {
    "path": "docs/source/samples/todo-app/code/app.py",
    "content": "import os\nimport base64\n\nimport boto3\nfrom chalice import Chalice, AuthResponse\nfrom chalicelib import auth, db\n\n\napp = Chalice(app_name='mytodo')\napp.debug = True\n_DB = None\n_USER_DB = None\n_AUTH_KEY = None\n_SSM_AUTH_KEY_NAME = '/todo-sample-app/auth-key'\n\n\n@app.route('/login', methods=['POST'])\ndef login():\n    body = app.current_request.json_body\n    record = get_users_db().get_item(\n        Key={'username': body['username']})['Item']\n    jwt_token = auth.get_jwt_token(\n        body['username'], body['password'], record, get_auth_key())\n    return {'token': jwt_token}\n\n\n@app.authorizer()\ndef jwt_auth(auth_request):\n    token = auth_request.token\n    decoded = auth.decode_jwt_token(token, get_auth_key())\n    return AuthResponse(routes=['*'], principal_id=decoded['sub'])\n\n\ndef get_auth_key():\n    global _AUTH_KEY\n    if _AUTH_KEY is None:\n        base64_key = boto3.client('ssm').get_parameter(\n            Name=_SSM_AUTH_KEY_NAME,\n            WithDecryption=True\n        )['Parameter']['Value']\n        _AUTH_KEY = base64.b64decode(base64_key)\n    return _AUTH_KEY\n\n\ndef get_users_db():\n    global _USER_DB\n    if _USER_DB is None:\n        _USER_DB = boto3.resource('dynamodb').Table(\n            os.environ['USERS_TABLE_NAME'])\n    return _USER_DB\n\n\ndef get_app_db():\n    global _DB\n    if _DB is None:\n        _DB = db.DynamoDBTodo(\n            boto3.resource('dynamodb').Table(\n                os.environ['APP_TABLE_NAME'])\n        )\n    return _DB\n\n\ndef get_authorized_username(current_request):\n    return current_request.context['authorizer']['principalId']\n\n\n@app.route('/todos', methods=['GET'], authorizer=jwt_auth)\ndef list_todos():\n    username = get_authorized_username(app.current_request)\n    return get_app_db().list_items(username=username)\n\n\n@app.route('/todos', methods=['POST'], authorizer=jwt_auth)\ndef create_todo():\n    body = app.current_request.json_body\n    username = get_authorized_username(app.current_request)\n    return get_app_db().add_item(\n        username=username,\n        description=body['description'],\n        metadata=body.get('metadata'),\n    )\n\n\n@app.route('/todos/{uid}', methods=['GET'], authorizer=jwt_auth)\ndef get_todo(uid):\n    username = get_authorized_username(app.current_request)\n    return get_app_db().get_item(uid, username=username)\n\n\n@app.route('/todos/{uid}', methods=['PUT'], authorizer=jwt_auth)\ndef update_todo(uid):\n    body = app.current_request.json_body\n    username = get_authorized_username(app.current_request)\n    get_app_db().update_item(\n        uid,\n        description=body.get('description'),\n        state=body.get('state'),\n        metadata=body.get('metadata'),\n        username=username)\n\n\n@app.route('/todos/{uid}', methods=['DELETE'], authorizer=jwt_auth)\ndef delete_todo(uid):\n    username = get_authorized_username(app.current_request)\n    return get_app_db().delete_item(uid, username=username)\n"
  },
  {
    "path": "docs/source/samples/todo-app/code/chalicelib/__init__.py",
    "content": ""
  },
  {
    "path": "docs/source/samples/todo-app/code/chalicelib/auth.py",
    "content": "import hashlib\nimport hmac\nimport datetime\nfrom uuid import uuid4\n\nimport jwt\nfrom chalice import UnauthorizedError\n\n\ndef get_jwt_token(username, password, record, secret):\n    actual = hashlib.pbkdf2_hmac(\n        record['hash'],\n        password.encode('utf-8'),\n        record['salt'].value,\n        int(record['rounds'])\n    )\n    expected = record['hashed'].value\n    if hmac.compare_digest(actual, expected):\n        now = datetime.datetime.utcnow()\n        unique_id = str(uuid4())\n        payload = {\n            'sub': username,\n            'iat': now,\n            'nbf': now,\n            'jti': unique_id,\n            # NOTE: We can also add 'exp' if we want tokens to expire.\n        }\n        return jwt.encode(payload, secret, algorithm='HS256')\n    raise UnauthorizedError('Invalid password')\n\n\ndef decode_jwt_token(token, secret):\n    return jwt.decode(token, secret, algorithms=['HS256'])\n"
  },
  {
    "path": "docs/source/samples/todo-app/code/chalicelib/db.py",
    "content": "from uuid import uuid4\n\nfrom boto3.dynamodb.conditions import Key\n\n\nDEFAULT_USERNAME = 'default'\n\n\nclass TodoDB(object):\n    def list_items(self):\n        pass\n\n    def add_item(self, description, metadata=None):\n        pass\n\n    def get_item(self, uid):\n        pass\n\n    def delete_item(self, uid):\n        pass\n\n    def update_item(self, uid, description=None, state=None,\n                    metadata=None):\n        pass\n\n\nclass InMemoryTodoDB(TodoDB):\n    def __init__(self, state=None):\n        if state is None:\n            state = {}\n        self._state = state\n\n    def list_all_items(self):\n        all_items = []\n        for username in self._state:\n            all_items.extend(self.list_items(username))\n        return all_items\n\n    def list_items(self, username=DEFAULT_USERNAME):\n        return list(self._state.get(username, {}).values())\n\n    def add_item(self, description, metadata=None, username=DEFAULT_USERNAME):\n        if username not in self._state:\n            self._state[username] = {}\n        uid = str(uuid4())\n        self._state[username][uid] = {\n            'uid': uid,\n            'description': description,\n            'state': 'unstarted',\n            'metadata': metadata if metadata is not None else {},\n            'username': username\n        }\n        return uid\n\n    def get_item(self, uid, username=DEFAULT_USERNAME):\n        return self._state[username][uid]\n\n    def delete_item(self, uid, username=DEFAULT_USERNAME):\n        del self._state[username][uid]\n\n    def update_item(self, uid, description=None, state=None,\n                    metadata=None, username=DEFAULT_USERNAME):\n        item = self._state[username][uid]\n        if description is not None:\n            item['description'] = description\n        if state is not None:\n            item['state'] = state\n        if metadata is not None:\n            item['metadata'] = metadata\n\n\nclass DynamoDBTodo(TodoDB):\n    def __init__(self, table_resource):\n        self._table = table_resource\n\n    def list_all_items(self):\n        response = self._table.scan()\n        return response['Items']\n\n    def list_items(self, username=DEFAULT_USERNAME):\n        response = self._table.query(\n            KeyConditionExpression=Key('username').eq(username)\n        )\n        return response['Items']\n\n    def add_item(self, description, metadata=None, username=DEFAULT_USERNAME):\n        uid = str(uuid4())\n        self._table.put_item(\n            Item={\n                'username': username,\n                'uid': uid,\n                'description': description,\n                'state': 'unstarted',\n                'metadata': metadata if metadata is not None else {},\n            }\n        )\n        return uid\n\n    def get_item(self, uid, username=DEFAULT_USERNAME):\n        response = self._table.get_item(\n            Key={\n                'username': username,\n                'uid': uid,\n            },\n        )\n        return response['Item']\n\n    def delete_item(self, uid, username=DEFAULT_USERNAME):\n        self._table.delete_item(\n            Key={\n                'username': username,\n                'uid': uid,\n            }\n        )\n\n    def update_item(self, uid, description=None, state=None,\n                    metadata=None, username=DEFAULT_USERNAME):\n        # We could also use update_item() with an UpdateExpression.\n        item = self.get_item(uid, username)\n        if description is not None:\n            item['description'] = description\n        if state is not None:\n            item['state'] = state\n        if metadata is not None:\n            item['metadata'] = metadata\n        self._table.put_item(Item=item)\n"
  },
  {
    "path": "docs/source/samples/todo-app/code/create-resources.py",
    "content": "import os\nimport uuid\nimport json\nimport argparse\nimport base64\n\nimport boto3\n\n\nAUTH_KEY_PARAM_NAME = '/todo-sample-app/auth-key'\nTABLES = {\n    'app': {\n        'prefix': 'todo-app',\n        'env_var': 'APP_TABLE_NAME',\n        'hash_key': 'username',\n        'range_key': 'uid'\n    },\n    'users': {\n        'prefix': 'users-app',\n        'env_var': 'USERS_TABLE_NAME',\n        'hash_key': 'username',\n    }\n}\n\n\ndef create_table(table_name_prefix, hash_key, range_key=None):\n    table_name = '%s-%s' % (table_name_prefix, str(uuid.uuid4()))\n    client = boto3.client('dynamodb')\n    key_schema = [\n        {\n            'AttributeName': hash_key,\n            'KeyType': 'HASH',\n        }\n    ]\n    attribute_definitions = [\n        {\n            'AttributeName': hash_key,\n            'AttributeType': 'S',\n        }\n    ]\n    if range_key is not None:\n        key_schema.append({'AttributeName': range_key, 'KeyType': 'RANGE'})\n        attribute_definitions.append(\n            {'AttributeName': range_key, 'AttributeType': 'S'})\n    client.create_table(\n        TableName=table_name,\n        KeySchema=key_schema,\n        AttributeDefinitions=attribute_definitions,\n        ProvisionedThroughput={\n            'ReadCapacityUnits': 5,\n            'WriteCapacityUnits': 5,\n        }\n    )\n    waiter = client.get_waiter('table_exists')\n    waiter.wait(TableName=table_name, WaiterConfig={'Delay': 1})\n    return table_name\n\n\ndef record_as_env_var(key, value, stage):\n    with open(os.path.join('.chalice', 'config.json')) as f:\n        data = json.load(f)\n        data['stages'].setdefault(stage, {}).setdefault(\n            'environment_variables', {}\n        )[key] = value\n    with open(os.path.join('.chalice', 'config.json'), 'w') as f:\n        serialized = json.dumps(data, indent=2, separators=(',', ': '))\n        f.write(serialized + '\\n')\n\n\ndef _already_in_config(env_var, stage):\n    with open(os.path.join('.chalice', 'config.json')) as f:\n        return env_var in json.load(f)['stages'].get(\n            stage, {}).get('environment_variables', {})\n\n\ndef create_auth_key_if_needed(stage):\n    ssm = boto3.client('ssm')\n    try:\n        ssm.get_parameter(Name=AUTH_KEY_PARAM_NAME)\n    except ssm.exceptions.ParameterNotFound:\n        print(f\"Generating auth key.\")\n        kms = boto3.client('kms')\n        random_bytes = kms.generate_random(NumberOfBytes=32)['Plaintext']\n        encoded_random_bytes = base64.b64encode(random_bytes).decode()\n        ssm.put_parameter(Name=AUTH_KEY_PARAM_NAME, Value=encoded_random_bytes,\n                          Type='SecureString')\n\n\ndef create_resources(args):\n    for table_config in TABLES.values():\n        # We assume if it a value is recorded in the Chalice config\n        # file, the table already exists.\n        if _already_in_config(table_config['env_var'], args.stage):\n            continue\n        print(f\"Creating table: {table_config['prefix']}\")\n        table_name = create_table(\n            table_config['prefix'], table_config['hash_key'],\n            table_config.get('range_key')\n        )\n        record_as_env_var(table_config['env_var'], table_name, args.stage)\n    create_auth_key_if_needed(args.stage)\n\n\ndef cleanup_resources(args):\n    ddb = boto3.client('dynamodb')\n    ssm = boto3.client('ssm')\n    with open(os.path.join('.chalice', 'config.json')) as f:\n        config = json.load(f)\n        env_vars = config['stages'].get(args.stage, {}).get(\n            'environment_variables', {})\n        for key in list(env_vars):\n            value = env_vars.pop(key)\n            if key.endswith('_TABLE_NAME'):\n                print(f\"Deleting table: {value}\")\n                ddb.delete_table(TableName=value)\n        if not env_vars:\n            del config['stages'][args.stage]['environment_variables']\n    try:\n        print(f\"Deleting SSM param: {AUTH_KEY_PARAM_NAME}\")\n        ssm.delete_parameter(Name=AUTH_KEY_PARAM_NAME)\n    except Exception:\n        pass\n\n    with open(os.path.join('.chalice', 'config.json'), 'w') as f:\n        serialized = json.dumps(config, indent=2, separators=(',', ': '))\n        f.write(serialized + '\\n')\n\n    print(\"Resources deleted.  If you haven't already, be \"\n          \"sure to run 'chalice delete' to delete your Chalice application.\")\n\n\ndef main():\n    parser = argparse.ArgumentParser()\n    parser.add_argument('-s', '--stage', default='dev')\n    parser.add_argument('-c', '--cleanup', action='store_true')\n    # app - stores the todo items\n    # users - stores the user data.\n    args = parser.parse_args()\n    if args.cleanup:\n        cleanup_resources(args)\n    else:\n        create_resources(args)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "docs/source/samples/todo-app/code/requirements-dev.txt",
    "content": "chalice==1.29.0\npytest==7.4.0\n"
  },
  {
    "path": "docs/source/samples/todo-app/code/requirements.txt",
    "content": "boto3==1.27.0\nbotocore==1.30.0\nPyJWT==2.7.0\n"
  },
  {
    "path": "docs/source/samples/todo-app/code/tests/__init__.py",
    "content": ""
  },
  {
    "path": "docs/source/samples/todo-app/code/tests/test_db.py",
    "content": "import os\nimport unittest\nimport boto3\nfrom uuid import uuid4\n\nfrom chalicelib.db import InMemoryTodoDB\nfrom chalicelib.db import DynamoDBTodo\n\n\nclass TestTodoDB(unittest.TestCase):\n    def setUp(self):\n        self.db_dict = {}\n        self.db = InMemoryTodoDB(self.db_dict)\n\n    def tearDown(self):\n        response = self.db.list_all_items()\n        for item in response:\n            self.db.delete_item(item['uid'], username=item['username'])\n\n    def test_can_add_and_retrieve_data(self):\n        todo_id = self.db.add_item('First item')\n        must_contain = {'description': 'First item',\n                        'state': 'unstarted',\n                        'metadata': {}}\n        full_record = self.db.get_item(todo_id)\n        assert dict(full_record, **must_contain) == full_record\n\n    def test_can_add_and_list_data(self):\n        todo_id = self.db.add_item('First item')\n        todos = self.db.list_items()\n        self.assertEqual(len(todos), 1)\n        self.assertEqual(todos[0]['uid'], todo_id)\n\n    def test_can_add_and_delete_data(self):\n        todo_id = self.db.add_item('First item')\n        self.assertEqual(len(self.db.list_items()), 1)\n        self.db.delete_item(todo_id)\n        self.assertEqual(len(self.db.list_items()), 0)\n\n    def test_can_add_and_update_data(self):\n        todo_id = self.db.add_item('First item')\n        self.db.update_item(todo_id, state='started')\n        self.assertEqual(self.db.get_item(todo_id)['state'], 'started')\n\n    def test_can_add_and_retrieve_data_with_specified_username(self):\n        username = 'myusername'\n        todo_id = self.db.add_item('First item', username=username)\n        must_contain = {\n            'description': 'First item',\n            'state': 'unstarted',\n            'metadata': {},\n            'username': username\n        }\n        full_record = self.db.get_item(todo_id, username=username)\n        assert dict(full_record, **must_contain) == full_record\n\n    def test_can_add_and_list_data_with_specified_username(self):\n        username = 'myusername'\n        todo_id = self.db.add_item('First item', username=username)\n        todos = self.db.list_items(username=username)\n        self.assertEqual(len(todos), 1)\n        self.assertEqual(todos[0]['uid'], todo_id)\n        self.assertEqual(todos[0]['username'], username)\n\n    def test_can_add_and_delete_data_with_specified_username(self):\n        username = 'myusername'\n        todo_id = self.db.add_item('First item', username=username)\n        self.assertEqual(len(self.db.list_items(username=username)), 1)\n        self.db.delete_item(todo_id, username=username)\n        self.assertEqual(len(self.db.list_items(username=username)), 0)\n\n    def test_can_add_and_update_data_with_specified_username(self):\n        username = 'myusername'\n        todo_id = self.db.add_item('First item', username=username)\n        self.db.update_item(todo_id, state='started', username=username)\n        self.assertEqual(self.db.get_item(\n            todo_id, username=username)['state'], 'started')\n\n    def test_list_all_items(self):\n        todo_id = self.db.add_item('First item', username='user')\n        other_todo_id = self.db.add_item('First item', username='otheruser')\n        all_todos = self.db.list_all_items()\n        self.assertEqual(len(all_todos), 2)\n        users = [todo['username'] for todo in all_todos]\n        todo_ids = [todo['uid'] for todo in all_todos]\n        self.assertCountEqual(['user', 'otheruser'], users)\n        self.assertCountEqual([todo_id, other_todo_id], todo_ids)\n\n\n@unittest.skipUnless(os.environ.get('RUN_INTEG_TESTS', False),\n                     \"Skipping integ tests (RUN_INTEG_TESTS) not test.\")\nclass TestDynamoDB(TestTodoDB):\n    @classmethod\n    def setUpClass(cls):\n        cls.TABLE_NAME = 'todo-integ-%s' % str(uuid4())\n        client = boto3.client('dynamodb')\n        client.create_table(\n            TableName=cls.TABLE_NAME,\n            KeySchema=[\n                {\n                    'AttributeName': 'username',\n                    'KeyType': 'HASH'\n                },\n                {\n                    'AttributeName': 'uid',\n                    'KeyType': 'RANGE',\n                }\n            ],\n            AttributeDefinitions=[\n                {\n                    'AttributeName': 'username',\n                    'AttributeType': 'S',\n                },\n                {\n                    'AttributeName': 'uid',\n                    'AttributeType': 'S',\n                }\n            ],\n            ProvisionedThroughput={\n                'ReadCapacityUnits': 5,\n                'WriteCapacityUnits': 5,\n            }\n        )\n        waiter = client.get_waiter('table_exists')\n        waiter.wait(TableName=cls.TABLE_NAME, WaiterConfig={'Delay': 1})\n\n    @classmethod\n    def tearDownClass(cls):\n        client = boto3.client('dynamodb')\n        client.delete_table(TableName=cls.TABLE_NAME)\n        waiter = client.get_waiter('table_not_exists')\n        waiter.wait(TableName=cls.TABLE_NAME, WaiterConfig={'Delay': 1})\n\n    def setUp(self):\n        resource = boto3.resource('dynamodb')\n        self.table = resource.Table(self.TABLE_NAME)\n        self.db = DynamoDBTodo(self.table)\n"
  },
  {
    "path": "docs/source/samples/todo-app/code/users.py",
    "content": "import os\nimport json\nimport getpass\nimport argparse\nimport hashlib\nimport hmac\nimport base64\n\nimport boto3\nfrom boto3.dynamodb.types import Binary\n\n\ndef get_table_name(stage):\n    # We might want to user the chalice modules to\n    # load the config.  For now we'll just load it directly.\n    with open(os.path.join('.chalice', 'config.json')) as f:\n        data = json.load(f)\n    return data['stages'][stage]['environment_variables']['USERS_TABLE_NAME']\n\n\ndef create_user(stage):\n    table_name = get_table_name(stage)\n    table = boto3.resource('dynamodb').Table(table_name)\n    username = input('Username: ').strip()\n    password = getpass.getpass('Password: ').strip()\n    password_fields = encode_password(password)\n    item = {\n        'username': username,\n        'hash': password_fields['hash'],\n        'salt': Binary(password_fields['salt']),\n        'rounds': password_fields['rounds'],\n        'hashed': Binary(password_fields['hashed']),\n    }\n    table.put_item(Item=item)\n\n\ndef encode_password(password, salt=None):\n    if salt is None:\n        salt = os.urandom(32)\n    rounds = 100000\n    hashed = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'),\n                                 salt, rounds)\n    return {\n        'hash': 'sha256',\n        'salt': salt,\n        'rounds': rounds,\n        'hashed': hashed,\n    }\n\n\ndef list_users(stage):\n    table_name = get_table_name(stage)\n    table = boto3.resource('dynamodb').Table(table_name)\n    for item in table.scan()['Items']:\n        print(item['username'])\n\n\ndef get_user(username, stage):\n    table_name = get_table_name(stage)\n    table = boto3.resource('dynamodb').Table(table_name)\n    user_record = table.get_item(Key={'username': username}).get('Item')\n    if user_record is not None:\n        print(f\"Entry for user: {username}\")\n        for key, value in user_record.items():\n            if isinstance(value, Binary):\n                value = base64.b64encode(value.value).decode()\n            print(f\"  {key:10}: {value}\")\n\n\ndef test_password(stage):\n    username = input('Username: ').strip()\n    password = getpass.getpass('Password: ').strip()\n    table_name = get_table_name(stage)\n    table = boto3.resource('dynamodb').Table(table_name)\n    item = table.get_item(Key={'username': username})['Item']\n    encoded = encode_password(password, salt=item['salt'].value)\n    if hmac.compare_digest(encoded['hashed'], item['hashed'].value):\n        print(\"Password verified.\")\n    else:\n        print(\"Password verification failed.\")\n\n\ndef main():\n    parser = argparse.ArgumentParser()\n    parser.add_argument('-c', '--create-user', action='store_true')\n    parser.add_argument('-t', '--test-password', action='store_true')\n    parser.add_argument('-g', '--get-user')\n    parser.add_argument('-s', '--stage', default='dev')\n    parser.add_argument('-l', '--list-users', action='store_true')\n    args = parser.parse_args()\n    if args.create_user:\n        create_user(args.stage)\n    elif args.list_users:\n        list_users(args.stage)\n    elif args.test_password:\n        test_password(args.stage)\n    elif args.get_user is not None:\n        get_user(args.get_user, args.stage)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "docs/source/samples/todo-app/index.rst",
    "content": "================\nTodo Application\n================\n\nThis is a sample application that allows you to manage Todo items.  This\ntutorial will walk through creating a serverless web API to create, update,\nget, and delete Todos, managing Todos in a database, and adding authorization\nwith JWT.  AWS services covered include AWS Lambda, Amazon API Gateway, Amazon\nDynamoDB, AWS CodeBuild, and AWS Systems Manager.\n\nYou can find the full source code for this application in our\n`samples directory on GitHub <https://github.com/aws/chalice/tree/master/docs/source/samples/todo-app/code/>`__.\n\n::\n\n    $ git clone git://github.com/aws/chalice\n    $ cd chalice/docs/source/samples/todo-app/code\n\nWe'll now walk through the architecture of this application,\nhow to deploy and use the application, and finally we'll go over\nthe main components of the application code.\n\n.. note::\n    This sample application is also available as a `workshop\n    <https://chalice-workshop.readthedocs.io/en/latest/todo-app/index.html>`__.\n    The main difference between the sample apps here and the Chalice workshops\n    is that the workshop is a detailed step by step process for how to create\n    this application from scratch.  You build the app by gradually adding each\n    feature piece by piece.  In the workshop, we first create a REST API with\n    no authentication or data store.  Then we introduce DynamoDB, then JWT\n    auth, etc.  The workshop also shows you how to set up a CI/CD pipeline\n    to automatically deploy your application whenever you push to your git\n    repository.  It takes several hours to work through all the workshop\n    material.  In this document we review the architecture, the deployment process,\n    then walk through the main sections of the final version of this\n    application.\n\nArchitecture\n============\n\nThe main component of this application is a REST API backed by Amazon API\nGateway and AWS Lambda.  The rest API lets you manage a Todo list.  It lets you\ncreate a new Todo list as well as check off existing Todo items.\n\nIn order to see a list of your Todo items, you must first log in.  Information\nabout our users is stored in an Amazon DynamoDB table.  The authentication is\ndone using a builtin authorizer.  This lets you define a Lambda function to\nperform your custom auth process.  For this sample app, we're using JSON Web\nTokens (JWT).\n\nThe Todo items are stored in a separate DynamoDB table.  Below is\nan architecture diagram of our sample app.  It shows the API Gateway\nREST API, along with a Lambda function for our authorizer, a Lambda function\nfor our REST API, and two DynamoDB tables.\n\n.. image:: docs/assets/architecture.jpg\n  :width: 100%\n  :alt: Architecture diagram\n\n.. _todo-sample-rest-api:\n\nREST API\n--------\n\nThe REST API supports the following resources:\n\n* GET    - ``/todos/`` - Gets a list of all todo items\n* POST   - ``/todos/`` - Creates a new Todo item\n* GET    - ``/todos/{id}`` - Gets a specific todo item\n* DELETE - ``/todos/{id}`` - Deletes a specific todo item\n* PUT    - ``/todos/{id}`` - Updates the state of a todo item\n\nA todo item has this schema::\n\n  {\n    \"description\": {\"type\": \"str\"},\n    \"uid\": {\"type: \"str\"},\n    \"state\": {\"type: \"str\", \"enum\": [\"unstarted\", \"started\", \"completed\"]},\n    \"metadata\": {\n      \"type\": \"object\"\n    }\n  }\n\n\nDeployment\n==========\n\nTo run and deploy this application, first create a virtual environment\nand install the dependencies.  Python 3.7 is used for this sample app.\n\n::\n\n    $ python3 -m /tmp/venv37\n    $ . /tmp/venv37/bin/activate\n    $ pip install ./requirements-dev.txt\n    $ pip install ./requirements.txt\n\nAs part of this application, there are additional resources that\nare created that are used by this application, including two DynamoDB\ntables as well as an SSM parameter used to store our secret key used\nin our JWT auth.  To create these resources, you can run::\n\n    $ python create-resources.py\n\nThis will also update your ``.chalice/config.json`` file with environment\nvariables containing the name of the DynamoDB tables that were created.\n\nAt this point, you can either test the application by running ``chalice\nlocal``.  This will start a local HTTP server on port 8000 that emulates API\nGateway so that you can test without having to deploy your application to AWS.\nYou can also run ``chalice deploy`` to deploy your application to\nAWS, which allows you to test on an actual API Gateway REST API::\n\n     $ chalice deploy\n     Creating deployment package.\n     Creating IAM role: mytodo-dev-api_handler\n     Creating lambda function: mytodo-dev\n     Creating IAM role: mytodo-dev-jwt_auth\n     Creating lambda function: mytodo-dev-jwt_auth\n     Creating Rest API\n     Resources deployed:\n       - Lambda ARN: arn:aws:lambda:us-east-1:12345:function:mytodo-dev\n       - Lambda ARN: arn:aws:lambda:us-east-1:12345:function:mytodo-dev-jwt_auth\n       - Rest API URL: https://abcd.execute-api.us-west-2.amazonaws.com/api/\n\n\nUsing the Application\n=====================\n\nIf you've deployed your application using ``chalice deploy``, you can test\nthe REST API by making requests to the ``Rest API URL``, shown in the output\nof ``chalice deploy``, in our example that would be\n``https://abcd.execute-api.us-west-2.amazonaws.com/api/``.  If you're using\n``chalice local``, you'll make requests to ``http://localhost:8000/``.\n\nBefore we can make requests we need to authenticate with the API.  In order\nto authenticate with the API we need to create user accounts.\n\nA helper script, ``users.py`` is included in the repository to help you\nmanage users.  The first thing we'll need to do is create a user::\n\n    $ python users.py --create-user\n    Username: myusername\n    Password:\n\nThis will create a new entry in our users DynamoDB table.\nYou can then test that the password verification works by running::\n\n    $ python users.py --test-password\n    Username: myusername\n    Password:\n    Password verified.\n\nOnce we've created a test user, we can now login by sending a POST\nrequest to the ``/login`` URL::\n\n    $ echo '{\"username\": \"myusername\", \"password\": \"mypassword\"}' | \\\n        http POST https://abcd.execute-api.us-west-2.amazonaws.com/api/login/\n    {\n        \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJteXVzZXJuYW1lIiwiaWF0IjoxNTk1NDU3Njg5LCJuYmYiOjE1OTU0NTc2ODksImp0aSI6IjMxNjc4YzFkLTdkZjEtNGEzOC04YmZiLTllZjZiMGM1YzAyNyJ9.w46RdtzZdk_P0LAh_St3wjsqgh-k-Hp1ykTpbDqad2k\",\n    }\n\n.. note::\n  We're using the HTTPie command line tool instead of cURL.  You can\n  install this tool by running ``pip install httpie``.\n\nNow whenever we make any requests to our REST API, we need to include\nthe token value in the output above as the value of our ``Authorization``\nheader.  For example, we can list all of our Todos, which is initially\nempty::\n\n    $ http https://abcd.execute-api.us-west-2.amazonaws.com/api/todos/ 'Authorization: my.jwt.token'\n    HTTP/1.1 200 OK\n    Content-Length: 2\n    Content-Type: application/json\n\n    []\n\n\nIf you omit the ``Authorization`` header, you'll see this error response::\n\n    $ http https://abcd.execute-api.us-west-2.amazonaws.com/api/todos/\n    HTTP/1.1 401 Unauthorized\n    Content-Length: 26\n    Content-Type: application/json\n    x-amzn-ErrorType: UnauthorizedException\n\n    {\n        \"message\": \"Unauthorized\"\n    }\n\n\nWe can create a new Todo::\n\n    $ echo '{\"description\": \"My first Todo\", \"metadata\": {}}' \\\n        |  http POST https://abcd.execute-api.us-west-2.amazonaws.com/api/todos/ \\\n           'Authorization: my.jwt.token'\n    HTTP/1.1 200 OK\n    Content-Length: 36\n    Content-Type: application/json\n\n    e25643f7-0b18-47d2-b124-4e6713ab527c\n\nNow when we list our Todos, we'll see our new entry we created::\n\n    $ http https://abcd.execute-api.us-west-2.amazonaws.com/api/todos/ 'Authorization: my.jwt.token'\n    HTTP/1.1 200 OK\n    Content-Length: 136\n    Content-Type: application/json\n\n    [\n        {\n            \"description\": \"My first Todo\",\n            \"metadata\": {},\n            \"state\": \"unstarted\",\n            \"uid\": \"e25643f7-0b18-47d2-b124-4e6713ab527c\",\n            \"username\": \"myusername\"\n        }\n    ]\n\nWe can update our Todo and mark it completed::\n\n\n    $ echo '{\"state\": \"completed\"}' |  \\\n        http PUT https://abcd.execute-api.us-west-2.amazonaws.com/api/todos/e25643f7-0b18-47d2-b124-4e6713ab527c \\\n        'Authorization: my.jwt.token'\n    HTTP/1.1 200 OK\n    Content-Length: 4\n    Content-Type: application/json\n\n    null\n\nAnd we can now verify that the Todo item shows up as completed::\n\n    $ http https://abcd.execute-api.us-west-2.amazonaws.com/api/todos/e25643f7-0b18-47d2-b124-4e6713ab527c \\\n        'Authorization: my.jwt.token'\n    HTTP/1.1 200 OK\n    Content-Length: 134\n    Content-Type: application/json\n\n    {\n        \"description\": \"My first Todo\",\n        \"metadata\": {},\n        \"state\": \"completed\",\n        \"uid\": \"e25643f7-0b18-47d2-b124-4e6713ab527c\",\n        \"username\": \"myusername\"\n    }\n\n\nCode Walkthrough\n================\n\n\n.. _todo-app-rest-api:\n\nRest API\n--------\n\nBelow is the code for the five routes defined in the\n:ref:`todo-sample-rest-api` section defined in the ``app.py`` file:\n\n.. literalinclude:: code/app.py\n   :caption: app.py\n   :linenos:\n   :lineno-match:\n   :lines: 67-105\n\nThe first thing all of these routes do is extract the current username from the\nrequest.  This is done by examining the context associated with the current\nrequest.  This will include the ``principalId``, or the current username, which\nis discussed in more detail in the :ref:`todo-app-jwt-auth` section below.\n\nEach of these routes then makes a call into the data storage layer,\nand either retrieves or updates data in the ``Todo`` DynamoDB table.\nThis is discussed in the next section on data storage.\n\nThe application DB is tracked as a module level variable that is\nretrieved through the ``get_app_db()`` function.  The name of the\nDynamoDB table is provided through the ``APP_TABLE_NAME`` environment\nvariable, which is specified in your ``.chalice/config.json`` file.\nThis was automatically filled in for you when you ran the\n``create-resources.py`` script.\n\nUser input is extracted from both the URL (the ``uid`` associated with\na Todo item is provided as part of the URL) as well as the JSON\nrequest body.  A key takeaway from these routes is that there's\nminimal logic in the route definitions themselves.  They're primarily\nabout extracting user input and then delegating the heavy lifting to\nother objects that are independent of any routing information.\n\n\nData Storage\n------------\n\nEach route in this sample application app makes a call to the\ndata storage layer, which is backed by a DynamoDB table.  This interface\nis defined by the ``TodoDB`` interface, which is defined in the\n``chalicelib/db.py`` file:\n\n.. literalinclude:: code/chalicelib/db.py\n  :caption: chalicelib/db.py\n  :linenos:\n  :lineno-match:\n  :pyobject: TodoDB\n\nThere are two different implementations of this interface.  The first one,\n``InMemoryTodoDB``, is an in-memory implementation of this interface where\nall data is stored within the process.  The purpose of this implementation\nis for testing purposes when you don't want to work with the real DynamoDB\nservice.  This allows you to develop your application locally and test\nusing ``chalice local``.  The other implementation of ``TodoDB`` interface is\n``DynamoDBTodo``, which communicates with the actual DynamoDB service\nto store and retrieve Todo items.  It uses the Table resource of ``boto3``,\ncreated via ``boto3.resource('dynamodb').Table(TABLE_NAME)``.  This allows\nus to use the `high level querying interface of boto3 <https://boto3.amazonaws.com/v1/documentation/api/latest/reference/customizations/dynamodb.html#dynamodb-conditions>`__.\nThe implementation is shown below.\n\n.. literalinclude:: code/chalicelib/db.py\n  :caption: chalicelib/db.py\n  :linenos:\n  :lineno-match:\n  :pyobject: DynamoDBTodo\n\n.. _todo-app-jwt-auth:\n\nJWT Authentication\n------------------\n\n.. note::\n  This example is for illustration purposes and does not necessarily\n  represent best practices.  Its intent is to show how custom\n  authentication can be implemented in a Chalice app.\n\nOur REST API for our Todo items requires that you send an appropriate\n``Authorization`` header when making HTTP requests.  You can retrieve\na auth token by making a request to the ``/login`` route with your\nuser name and password.  The underlying mechanism used to handle\nour auth functionality is through issuing a\n`JWT <https://datatracker.ietf.org/doc/html/rfc7519>`__ when you login.\n\nUsers Table\n~~~~~~~~~~~\n\nIn order to login, we need a way to store and retrieve user information.  This\nis done through our ``Users`` DynamoDB table.  This was created when you ran\nthe ``create-resoureces.py`` file.  Each user record stores their username and\ninformation about their password.  We're using PBKDF2 as our key derivation\nfunction for password hashing, which is available in Python's standard library\nthrough the `hashlib.pbkdf2_hmac\n<https://docs.python.org/3/library/hashlib.html#hashlib.pbkdf2_hmac>`__\nfunction.  The parameters needed by ``pbkdf2_hmac`` are stored in each user's\nrecord, including the password hash, salt, number of rounds, and the hash used\nfor PBKDF2 (sha256 in our example).  These user entries were created and stored\nin the ``Users`` DynamoDB table when you ran the ``python users.py\n--create-user`` command.\nYou can see the fields for a specific user by using the ``--get-user`` option\nto the ``users.py`` script::\n\n\n    $ python users.py --get-user myusername\n    Entry for user: myusername\n      hash      : sha256\n      username  : myusername\n      hashed    : Hym8Ss6WIArus+aZ6BucZ3sz6Wu5w8Tc3lPUivTuUi4=\n      salt      : rXMPBx8ZriKU3SQTh58BlxQQtpcLHfmITTB2tpRs/sM=\n      rounds    : 100000\n\n\nLogin Flow\n~~~~~~~~~~\n\nBelow is the code for the ``/login`` route:\n\n.. literalinclude:: code/app.py\n  :caption: app.py\n  :linenos:\n  :lineno-match:\n  :pyobject: login\n\nIn this login view, we first lookup the user record fom our users DB,\nand then try to generate a JWT token for this entry.  The\n``auth.get_jwt_token`` will first verify that the password hash\nmatches what's stored in our users DB, and then generate a JWT token\nfor this user as shown in the code below:\n\n.. literalinclude:: code/chalicelib/auth.py\n  :caption: chalicelib/auth.py\n  :linenos:\n  :lineno-match:\n  :pyobject: get_jwt_token\n\nThe call to ``jwt.encode()`` requires a payload and a secret.\nThis secret is a value that is only known to our application and is\nused in our built-in authorizer to verify the JWT is valid.\nThis secret value is stored as an SSM parameter.  A random secret\nwas automatically generated and stored in SSM for you when running the\n``create-resources.py`` script.  When we call ``auth.get_jwt_token`` we first\nretrieve this value from SSM as shown in the ``get_auth_key()`` function\ndefined in our ``app.py`` file:\n\n.. literalinclude:: code/app.py\n  :caption: app.py\n  :linenos:\n  :lineno-match:\n  :pyobject: get_auth_key\n\nOnce we've generated a JWT token, we return the token back to the caller.\nThey must then provide that same token in the ``Authorization`` header\nwhenever they make API calls to the REST API.\n\nCustom Authorizer\n~~~~~~~~~~~~~~~~~\n\nIn order to require that a specific route requires proper authorization,\nwe must first create an authorizer, and then associate it with any routes\nthat require auth.  Chalice supports different types of\n:doc:`../../topics/authorizers`, and in this example we're using the\n:ref:`builtin-authorizers` type provided by Chalice.  This lets us write\nour custom authorization logic as part of our Chalice app.  To do this,\nwe decorate our auth function with the ``@app.authorizer`` decorator.\nOur custom authorizer logic takes the JWT token (accessible through the\n``auth_request.token`` attribute, and verifies the token is valid\nusing our secret key retrieved via ``get_auth_key()``.  The custom\nauthorizer is shown below:\n\n.. literalinclude:: code/app.py\n  :caption: app.py\n  :linenos:\n  :lineno-match:\n  :pyobject: jwt_auth\n\nOnce we verify that JWT token is valid, we return an ``AuthResponse`` that\nspecifies what routes the user is allowed to access.  In our example, we're\ngiving them access to all routes, denoted by a ``*``.\n\nNow that we have our authorizer, we can associate with a route by providing\nthe function as the value of the ``authorizer=`` parameter.  We saw this in the\n:ref:`todo-app-rest-api` section above.  For example, note that the\n``@app.route()`` decorator is being provided an ``authorizer`` function:\n\n.. literalinclude:: code/app.py\n  :caption: app.py\n  :linenos:\n  :lineno-match:\n  :pyobject: list_todos\n\n\nCleaning Up\n===========\n\nOnce you're finished experimenting with this sample app, you can cleanup your\nresources by deleting the Chalice app and deleting any additional resources\nassociated with this app.  To do this, first delete your Chalice app::\n\n    $ chalice delete\n    Deleting Rest API: q7dc49grhk\n    Deleting function: arn:aws:lambda:us-west-w:12345:function:mytodo-dev-jwt_auth\n    Deleting IAM role: mytodo-dev-jwt_auth\n    Deleting function: arn:aws:lambda:us-west-w:12345:function:mytodo-dev\n    Deleting IAM role: mytodo-dev-api_handler\n\nThen to cleanup the remaining resources, rerun the\n``create-resources.py`` script with the ``--cleanup`` flag.  This will delete\nthe DynamoDB tables and the SSM parameter, along with any additional resources\ncreated as part of your Chalice app::\n\n    $ python create-resources.py --cleanup\n    Deleting table: todo-app-632a558c-8355-4c2d-a46e-24350f371389\n    Deleting table: users-app-05b34fa2-1ae6-4d81-95d1-7ced59878a2b\n    Deleting SSM param: /todo-sample-app/auth-key\n    Resources deleted.  If you haven't already, be sure to run 'chalice delete' to delete your Chalice application.\n"
  },
  {
    "path": "docs/source/theme/smithy/globaltoc.html",
    "content": "{#\n    basic/globaltoc.html\n    ~~~~~~~~~~~~~~~~~~~~\n#}\n<div id=\"sidebar-navigation\" class=\"hidden-xs\">\n  {{ toctree(maxdepth=3, titles_only=True) }}\n</div>\n"
  },
  {
    "path": "docs/source/theme/smithy/landing.html",
    "content": "<section id=\"landing-container\">\n\n    <div id=\"splash\">\n        <div class=\"width-wrapper\">\n            <div class=\"splash-row\">\n                <div class=\"splash-column\">\n\t\t    <img id=\"splash-logo\" src=\"{{ pathto('_static/img/chalice-logo-whitespace.png', 1) }}\" alt=\"AWS Chalice\" />\n                    <div class=\"headline\">\n                        A framework for writing serverless applications\n                    </div>\n                    <a class=\"splash-link\" href=\"quickstart.html\">Get started</a>\n                </div>\n\n                <div class=\"splash-column\">\n                    <div class=\"highlight-chalice notranslate\">\n                        <div class=\"highlight\">\n<pre><a class=\"see-full-example\" href=\"{{ pathto('quickstart') }}#complete-example\">[full example]</a>\n<span style=\"color: #f92672\">from</span> <span style=\"color: #f8f8f2\">chalice</span> <span style=\"color: #66d9ef\">import</span> <span style=\"color: #f8f8f2\">Chalice</span>\n\n<span style=\"color: #f8f8f2\">app</span> <span style=\"color: #f92672\">=</span> <span style=\"color: #f8f8f2\">Chalice(app_name</span><span style=\"color: #f92672\">=</span><span style=\"color: #e6db74\">\"helloworld\"</span><span style=\"color: #f8f8f2\">)</span>\n\n<span style=\"color: #a6e22e\">@app</span><span style=\"color: #f92672\">.</span><span style=\"color: #f8f8f2\">route(</span><span style=\"color: #e6db74\">\"/\"</span><span style=\"color: #f8f8f2\">)</span>\n<span style=\"color: #66d9ef\">def</span> <span style=\"color: #a6e22e\">index</span><span style=\"color: #f8f8f2\">():</span>\n    <span style=\"color: #66d9ef\">return</span> <span style=\"color: #f8f8f2\">{</span><span style=\"color: #e6db74\">\"hello\"</span><span style=\"color: #f8f8f2\">:</span> <span style=\"color: #e6db74\">\"world\"</span><span style=\"color: #f8f8f2\">}</span>\n\n<span style=\"color: #a6e22e\">@app</span><span style=\"color: #f92672\">.</span><span style=\"color: #f8f8f2\">schedule(Rate(</span><span style=\"color: #ae81ff\">5</span><span style=\"color: #f8f8f2\">,</span> <span style=\"color: #f8f8f2\">unit</span><span style=\"color: #f92672\">=</span><span style=\"color: #f8f8f2\">Rate</span><span style=\"color: #f92672\">.</span><span style=\"color: #f8f8f2\">MINUTES))</span>\n<span style=\"color: #66d9ef\">def</span> <span style=\"color: #a6e22e\">periodic_task</span><span style=\"color: #f8f8f2\">(event):</span>\n    <span style=\"color: #66d9ef\">return</span> <span style=\"color: #f8f8f2\">{</span><span style=\"color: #e6db74\">\"hello\"</span><span style=\"color: #f8f8f2\">:</span> <span style=\"color: #e6db74\">\"world\"</span><span style=\"color: #f8f8f2\">}</span>\n\n<span style=\"color: #a6e22e\">@app</span><span style=\"color: #f92672\">.</span><span style=\"color: #f8f8f2\">on_s3_event(bucket</span><span style=\"color: #f92672\">=</span><span style=\"color: #e6db74\">'mybucket'</span><span style=\"color: #f8f8f2\">)</span>\n<span style=\"color: #66d9ef\">def</span> <span style=\"color: #a6e22e\">s3_handler</span><span style=\"color: #f8f8f2\">(event):</span>\n    <span style=\"color: #f8f8f2\">print(event</span><span style=\"color: #f92672\">.</span><span style=\"color: #f8f8f2\">bucket,</span> <span style=\"color: #f8f8f2\">event</span><span style=\"color: #f92672\">.</span><span style=\"color: #f8f8f2\">key)</span>\n\n</pre>\n                        </div>\n                    </div>\n                </div>\n            </div>\n        </div>\n    </div>\n\n    <div class=\"width-wrapper\">\n        <div class=\"splash-row\">\n\t    <img class=\"lp-image\" src=\"{{ pathto('_static/img/coding.png', 1) }}\" alt=\"Coding\" />\n            <div class=\"splash-column\">\n                <h2>Focus on writing your application code</h2>\n                <p>Focus on writing your application code\n\t\t   instead of the resources or services needed to deploy\n\t\t   your application.  Chalice automatically determines how to\n\t\t   provision the necessary resources for your application.</p>\n            </div>\n\t    <img class=\"lp-image\" src=\"{{ pathto('_static/img/programming.png', 1) }}\" alt=\"Coding\" />\n            <div class=\"splash-column\">\n                <h2>A familiar decorator based API</h2>\n                <p>Chalice's API for writing serverless application uses a familiar\n\t\t   decorator-based syntax used in frameworks such as Flask,\n\t\t   bottle, and FastAPI.  Skip the learning curve and get up and\n\t\t   running quickly.\n\t\t   </p>\n            </div>\n\t    <img class=\"lp-image\" src=\"{{ pathto('_static/img/maintenance.png', 1) }}\" alt=\"Coding\" />\n            <div class=\"splash-column\">\n                <h2>Supports multiple deployment systems</h2>\n                <p>Chalice supports multiple tools to deploy your application\n\t\t   including AWS CloudFormation, Terraform, and its own built-in\n\t\t   deployer based on the AWS SDK for Python.  Use the deployment\n\t\t   tools and services you're already familiar with.</p>\n            </div>\n        </div>\n    </div>\n\n    <div class=\"quickstart-vid\">\n        <div class=\"width-wrapper\">\n            <div class=\"splash-row\">\n                <div class=\"splash-column-one-third\">\n                    <span>\n                    <img id=\"quickstart-img\" src=\"{{ pathto('_static/img/speed.svg', 1) }}\" alt=\"Quickstart\" />\n                    <h2 id=\"quickstart-title\">Up and running in minutes</h2>\n                    </span>\n                    <p>Chalice lets you quickly create and deploy python applications that use\n            AWS Lambda.  Using the Chalice CLI, you can have a REST API deployed to\n            Amazon API Gateway and AWS Lambda in minutes.\n            </p>\n                </div>\n                <div class=\"splash-column-two-third\">\n                <asciinema-player preload=1 speed=1.2 idle-time-limit=1.2 src=\"{{ pathto('_static/casts/chalice-quickstart.cast', 1) }}\"></asciinema-player>\n            <script src=\"{{ pathto('_static/asciinema-player.js', 1) }}\"></script>\n                </div>\n            </div>\n            </div>\n        </div>\n    </div>\n\n    <div class=\"width-wrapper\">\n\t<h2>Features</h2>\n        <div class=\"splash-row\">\n            <div class=\"splash-column\">\n                <h2>Native python packaging</h2>\n                <p class=\"feature-desc\">Chalice has built-in support for python packaging tools.\n                    It will automatically package your application and install\n                    3rd party dependencies specified in your requirements.txt file.\n                </p>\n\t        </div>\n            <div class=\"splash-column\">\n                <h2>AWS SAM and Terraform integration</h2>\n                <p class=\"feature-desc\">\n                    You can use Chalice's included deployer that's built\n                    using the AWS SDK for Python (boto3) or you can have\n                    Chalice generate packages that can be deployed with\n                    AWS CloudFormation or Terraform.\n                </p>\n\t        </div>\n            <div class=\"splash-column\">\n                <h2>CI/CD pipeline generation</h2>\n                <p class=\"feature-desc\">Automatically generate a deployment pipeline that's\n                   built with AWS CodePipeline and AWS CodeBuild.\n                   Deploy your application whenever you push changes\n                   to your Git repository.\n                </p>\n\t        </div>\n        </div>\n        <div class=\"splash-row\">\n            <div class=\"splash-column\">\n                <h2>Local testing support</h2>\n                <p class=\"feature-desc\">Test your REST API using the local test server.  This gives\n                    you a quicker feedback loop and let's you test your code\n                    before deploying to AWS.\n                </p>\n\t        </div>\n            <div class=\"splash-column\">\n                <h2>Websocket APIs</h2>\n                <p class=\"feature-desc\">Create Websocket APIs with API Gateway.\n                    Includes runtime APIs to send messages back to connected clients.\n                </p>\n\t        </div>\n            <div class=\"splash-column\">\n                <h2>Automatic policy generation</h2>\n                <p class=\"feature-desc\">\n                    Automatically generate policies for your application based on\n                    scanning your source code.\n                </p>\n\t        </div>\n        </div>\n    </div>\n    <div class=\"faq\">\n        <div class=\"width-wrapper\">\n            <h2 class=\"faq-heading\">Learning Resources</h2>\n            <div class=\"splash-row\">\n                <div class=\"splash-column splash-box\">\n                    <h2>Tutorials</h2>\n\t\t    <p>See step-by-step tutorials that show you how to use various\n\t\t    features of Chalice.  These are designed to quickly\n\t\t    get you up and running if you're new to Chalice.  These\n\t\t    are perfect for new users of Chalice.\n\t\t    </p>\n\t            <a href=\"{{ pathto('tutorials/index') }}\" class=\"splash-clickable\">\n                    See more.\n                    </a>\n\t\t</div>\n                <div class=\"splash-column splash-box\">\n                    <h2>Sample Applications</h2>\n\t\t    <p>Our sample applications are complete examples\n\t\t      that are larger in scope than our tutorials.  Learn\n\t\t      how you can combine multiple features of Chalice\n\t\t      to create more real-world serverless applications.  We walk through\n\t\t      the architecture, deployment, and code for all\n\t\t      of our sample applications.\n\t\t    </p>\n\t\t    <a href=\"{{ pathto('samples/index') }}\" class=\"splash-clickable\">\n                    See more.\n                    </a>\n\t\t</div>\n\t    </div>\n        </div>\n    </div>\n    <div class=\"width-wrapper\">\n        <section class=\"relations\">\n        <a class=\"next-page clearfix\" href=\"{{ pathto('quickstart')}}\">Quick start -&gt;</a>\n        </section>\n\t<p></p>\n    </div>\n</section>\n"
  },
  {
    "path": "docs/source/theme/smithy/layout.html",
    "content": "{%- extends \"basic/layout.html\" %}\n\n{%- block extrahead %}\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n{% endblock %}\n\n{%- block scripts %}\n    {{ super() }}\n    <script type=\"text/javascript\">\n        function _scroll(subjectId) {\n            var subjectElement = $(subjectId);\n            var actualSubjectHeight = subjectElement.height();\n            var startingPosition = subjectElement[0].getBoundingClientRect().top;\n            return function() {\n                var availableHeight = $(window).height() - startingPosition;\n                // Subtract the scroll position to account for sticky movement.\n                availableHeight += Math.min($(window).scrollTop(), 40);\n                var cappedHeight = Math.min(actualSubjectHeight, availableHeight);\n                if (subjectElement.css(\"height\") !== cappedHeight) {\n                    subjectElement.css(\"height\", cappedHeight);\n                }\n            };\n        }\n\n        // Scroll and resize the the columns when scrolled.\n        $(function() {\n            var rightScroll = _scroll(\"#right-column > .column-body\");\n            var scrollFn = function() {\n                rightScroll.call(this, arguments);\n            };\n            scrollFn();\n            $(window).scroll(scrollFn);\n            $(window).resize(scrollFn);\n        });\n\n        // Scroll spy to change highlighted navigation element.\n        $(function() {\n            var section = document.querySelectorAll(\".section\");\n            var sections = {};\n            var i = 0;\n            Array.prototype.forEach.call(section, function(e) {\n                sections[e.id] = e.offsetTop;\n            });\n            var scrollSpy = function() {\n                var scrollPosition = document.documentElement.scrollTop || document.body.scrollTop;\n                for (i in sections) {\n                    if (sections[i] <= scrollPosition) {\n                        $('#right-column .current').removeClass('current');\n                        $(\"#right-column a[href='#\" + i + \"']\").addClass('current');\n                    }\n                }\n            };\n            $(window).scroll(scrollSpy);\n            scrollSpy();\n        });\n    </script>\n    {% if theme_ga_id %}\n    <script async src=\"https://www.googletagmanager.com/gtag/js?id={{ theme_ga_id }}\"></script>\n    <script>\n      window.dataLayer = window.dataLayer || [];\n      function gtag(){dataLayer.push(arguments);}\n      gtag('js', new Date());\n\n      gtag('config', '{{ theme_ga_id }}');\n    </script>\n    {% endif %}\n{%- endblock %}\n\n{%- block css -%}\n    <link rel=\"stylesheet\" href=\"{{ pathto('_static/bootstrap-reboot.css', 1) }}\" type=\"text/css\" />\n    {{ super() }}\n    <link rel=\"stylesheet\" href=\"{{ pathto('_static/custom-tabs.css', 1) }}\" type=\"text/css\" />\n    {% if pagename == \"index\" %}\n    <link rel=\"stylesheet\" href=\"{{ pathto('_static/asciinema-player.css', 1) }}\" type=\"text/css\" />\n    {% endif %}\n{% endblock -%}\n\n<div id=\"site-container\">\n\n    {%- block header %}\n    <header>\n        <div class=\"header-flex width-wrapper\">\n            <div class=\"site-logo\">\n                <a href=\"{{ pathto(master_doc) }}\">\n\t\t  <span class=\"logo-icon\"><img src=\"{{ pathto('_static/img/chalice-logo-icon-small.png', 1) }}\" /></span>\n                </a>\n            </div>\n\n            <ul id=\"page-navigation\">\n                <li class=\"site-page first\"><a href=\"{{ pathto('quickstart') }}\">Quick Start</a></li>\n                <li class=\"site-page\"><a href=\"{{ pathto('tutorials/index') }}\">Tutorials</a></li>\n                <li class=\"site-page\"><a href=\"{{ pathto('samples/index') }}\">Samples</a></li>\n                <li class=\"site-page\"><a href=\"{{ pathto('main') }}\">Documentation</a></li>\n                <li class=\"site-page\"><a href=\"https://github.com/aws/chalice\">Code</a></li>\n                <li class=\"site-search hidden-sm\">\n                    <form action=\"{{ pathto('search') }}\" method=\"get\">\n                        <input type=\"hidden\" name=\"check_keywords\" value=\"yes\" />\n                        <input type=\"hidden\" name=\"area\" value=\"default\" />\n                        <input class=\"search-input\" autocomplete=\"off\" type=\"search\" name=\"q\" placeholder=\"Search\" />\n                    </form>\n                </li>\n            </ul>\n        </div>\n    </header>\n    {% endblock -%}\n\n    {%- block relbar1 %}{% endblock %}\n\n    {% block content %}\n        {% if pagename == \"index\" %}\n        {%- include 'landing.html' with context %}\n        {% endif %}\n        {% if pagename != \"index\" or builder == \"singlehtml\" %}\n        <section id=\"page-container\">\n            <div class=\"width-wrapper flex\">\n                <article id=\"document-body\">\n                    {% if parents %}\n                    <ul class=\"rel-parents\">\n                    {%- for parent in parents %}\n                    <li><a href=\"{{ parent.link|e }}\" {% if loop.last %}{{ accesskey(\"U\") }}{% endif %}>{{ parent.title }}</a></li>\n                    {%- endfor %}\n                    </ul>\n                    {% endif %}\n                    {% block body %} {% endblock %}\n                    {% if prev or next %}\n                    <section class=\"relations\">\n                        {% if prev %}\n                        <a href=\"{{ prev.link|e }}\" title=\"{{ _('previous chapter')}}\" class=\"previous-page clearfix hidden-xs\">← {{ prev.title }}</a>\n                        {% endif %}\n                        {%- if next and next.title != '&lt;no title&gt;' %}\n                        <a href=\"{{ next.link|e }}\" title=\"{{ _('next chapter')}}\" class=\"next-page clearfix\">{{ next.title }} →</a>\n                        {%- endif %}\n                    </section>\n                    {% endif %}\n                    {%- block content_footer %}{%- endblock %}\n                </article>\n\n                {%- if pagename not in ('search', 'contents', 'index', '404') -%}\n                <aside id=\"right-column\" class=\"side-column hidden-sm\">\n                    <div class=\"column-body\">\n                        <section class=\"sidebar\">\n                            {% if prev or next %}\n                            <section class=\"next-previous\">\n                                {% if prev %}\n                                <a href=\"{{ prev.link|e }}\" title=\"{{ _('previous chapter')}}\" class=\"previous-page clearfix hidden-xs\">← Prev</a>\n                                {% endif %}\n                                {%- if next and next.title != '&lt;no title&gt;' %}\n                                <a href=\"{{ next.link|e }}\" title=\"{{ _('next chapter')}}\" class=\"next-page clearfix\">Next →</a>\n                                {%- endif %}\n                            </section>\n                            {% endif %}\n                            {{ toc }}\n                        </section>\n                    </div>\n                </aside>\n                {%- endif -%}\n            </div>\n        </section>\n        {% endif %}\n    {% endblock %}\n\n</div><!-- site-container -->\n\n{%- block relbar2 %}{% endblock %}\n{%- block footer %}\n<footer id=\"footer\">\n    <div class=\"width-wrapper\">\n        <div class=\"copyright\">\n            <p>©2020, Amazon Web Services, Inc or its affiliates. All rights reserved.</p>\n        </div>\n    </div>\n</footer>\n{%- endblock -%}\n"
  },
  {
    "path": "docs/source/theme/smithy/static/asciinema-player.css",
    "content": ".asciinema-player-wrapper {\n  position: relative;\n  text-align: center;\n  outline: none;\n}\n.asciinema-player-wrapper .title-bar {\n  display: none;\n  top: -78px;\n  transition: top 0.15s linear;\n  position: absolute;\n  left: 0;\n  right: 0;\n  box-sizing: content-box;\n  font-size: 20px;\n  line-height: 1em;\n  padding: 15px;\n  font-family: sans-serif;\n  color: white;\n  background-color: rgba(0, 0, 0, 0.8);\n}\n.asciinema-player-wrapper .title-bar img {\n  vertical-align: middle;\n  height: 48px;\n  margin-right: 16px;\n}\n.asciinema-player-wrapper .title-bar a {\n  color: white;\n  text-decoration: underline;\n}\n.asciinema-player-wrapper .title-bar a:hover {\n  text-decoration: none;\n}\n.asciinema-player-wrapper:fullscreen {\n  background-color: #000;\n  width: 100%;\n  height: 100%;\n  display: -webkit-flex;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-justify-content: center;\n  justify-content: center;\n  -webkit-align-items: center;\n  align-items: center;\n}\n.asciinema-player-wrapper:fullscreen .asciinema-player {\n  position: static;\n}\n.asciinema-player-wrapper:fullscreen .title-bar {\n  display: initial;\n}\n.asciinema-player-wrapper:fullscreen.hud .title-bar {\n  top: 0;\n}\n.asciinema-player-wrapper:-webkit-full-screen {\n  background-color: #000;\n  width: 100%;\n  height: 100%;\n  display: -webkit-flex;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-justify-content: center;\n  justify-content: center;\n  -webkit-align-items: center;\n  align-items: center;\n}\n.asciinema-player-wrapper:-webkit-full-screen .asciinema-player {\n  position: static;\n}\n.asciinema-player-wrapper:-webkit-full-screen .title-bar {\n  display: initial;\n}\n.asciinema-player-wrapper:-webkit-full-screen.hud .title-bar {\n  top: 0;\n}\n.asciinema-player-wrapper:-moz-full-screen {\n  background-color: #000;\n  width: 100%;\n  height: 100%;\n  display: -webkit-flex;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-justify-content: center;\n  justify-content: center;\n  -webkit-align-items: center;\n  align-items: center;\n}\n.asciinema-player-wrapper:-moz-full-screen .asciinema-player {\n  position: static;\n}\n.asciinema-player-wrapper:-moz-full-screen .title-bar {\n  display: initial;\n}\n.asciinema-player-wrapper:-moz-full-screen.hud .title-bar {\n  top: 0;\n}\n.asciinema-player-wrapper:-ms-fullscreen {\n  background-color: #000;\n  width: 100%;\n  height: 100%;\n  display: -webkit-flex;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-justify-content: center;\n  justify-content: center;\n  -webkit-align-items: center;\n  align-items: center;\n}\n.asciinema-player-wrapper:-ms-fullscreen .asciinema-player {\n  position: static;\n}\n.asciinema-player-wrapper:-ms-fullscreen .title-bar {\n  display: initial;\n}\n.asciinema-player-wrapper:-ms-fullscreen.hud .title-bar {\n  top: 0;\n}\n.asciinema-player-wrapper .asciinema-player {\n  text-align: left;\n  display: inline-block;\n  padding: 0px;\n  position: relative;\n  box-sizing: content-box;\n  -moz-box-sizing: content-box;\n  -webkit-box-sizing: content-box;\n  overflow: hidden;\n  max-width: 100%;\n}\n.asciinema-terminal {\n  box-sizing: content-box;\n  -moz-box-sizing: content-box;\n  -webkit-box-sizing: content-box;\n  overflow: hidden;\n  padding: 0;\n  margin: 0px;\n  display: block;\n  white-space: pre;\n  border: 0;\n  word-wrap: normal;\n  word-break: normal;\n  border-radius: 0;\n  border-style: solid;\n  cursor: text;\n  border-width: 0.5em;\n  font-family: Consolas, Menlo, 'Bitstream Vera Sans Mono', monospace, 'Powerline Symbols';\n  line-height: 1.3333333333em;\n}\n.asciinema-terminal .line {\n  letter-spacing: normal;\n  overflow: hidden;\n  height: 1.3333333333em;\n}\n.asciinema-terminal .line span {\n  padding: 0;\n  display: inline-block;\n  height: 1.3333333333em;\n}\n.asciinema-terminal .line {\n  display: block;\n  width: 200%;\n}\n.asciinema-terminal .bright {\n  font-weight: bold;\n}\n.asciinema-terminal .underline {\n  text-decoration: underline;\n}\n.asciinema-terminal .italic {\n  font-style: italic;\n}\n.asciinema-terminal.font-small {\n  font-size: 12px;\n}\n.asciinema-terminal.font-medium {\n  font-size: 18px;\n}\n.asciinema-terminal.font-big {\n  font-size: 24px;\n}\n.asciinema-player .control-bar {\n  width: 100%;\n  height: 32px;\n  background: rgba(0, 0, 0, 0.8);\n  /* no gradient fallback */\n  background: -moz-linear-gradient(top, rgba(0, 0, 0, 0.5) 0%, #000000 25%, #000000 100%);\n  /* FF3.6-15 */\n  background: -webkit-linear-gradient(top, rgba(0, 0, 0, 0.5) 0%, #000000 25%, #000000 100%);\n  /* Chrome10-25,Safari5.1-6 */\n  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.5) 0%, #000000 25%, #000000 100%);\n  /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */\n  color: #bbbbbb;\n  box-sizing: content-box;\n  line-height: 1;\n  position: absolute;\n  bottom: -35px;\n  left: 0;\n  transition: bottom 0.15s linear;\n}\n.asciinema-player .control-bar * {\n  box-sizing: inherit;\n  font-size: 0;\n}\n.asciinema-player .control-bar svg.icon path {\n  fill: #bbbbbb;\n}\n.asciinema-player .control-bar .playback-button {\n  display: block;\n  float: left;\n  cursor: pointer;\n  height: 12px;\n  width: 12px;\n  padding: 10px;\n}\n.asciinema-player .control-bar .playback-button svg {\n  height: 12px;\n  width: 12px;\n}\n.asciinema-player .control-bar .timer {\n  display: block;\n  float: left;\n  width: 50px;\n  height: 100%;\n  text-align: center;\n  font-family: Helvetica, Arial, sans-serif;\n  font-size: 11px;\n  font-weight: bold;\n  line-height: 32px;\n  cursor: default;\n}\n.asciinema-player .control-bar .timer span {\n  display: inline-block;\n  font-size: inherit;\n}\n.asciinema-player .control-bar .timer .time-remaining {\n  display: none;\n}\n.asciinema-player .control-bar .timer:hover .time-elapsed {\n  display: none;\n}\n.asciinema-player .control-bar .timer:hover .time-remaining {\n  display: inline;\n}\n.asciinema-player .control-bar .progressbar {\n  display: block;\n  overflow: hidden;\n  height: 100%;\n  padding: 0 10px;\n}\n.asciinema-player .control-bar .progressbar .bar {\n  display: block;\n  cursor: pointer;\n  height: 100%;\n  padding-top: 15px;\n  font-size: 0;\n}\n.asciinema-player .control-bar .progressbar .bar .gutter {\n  display: block;\n  height: 3px;\n  background-color: #333;\n}\n.asciinema-player .control-bar .progressbar .bar .gutter span {\n  display: inline-block;\n  height: 100%;\n  background-color: #bbbbbb;\n  border-radius: 3px;\n}\n.asciinema-player .control-bar.live .progressbar .bar {\n  cursor: default;\n}\n.asciinema-player .control-bar .fullscreen-button {\n  display: block;\n  float: right;\n  width: 14px;\n  height: 14px;\n  padding: 9px;\n  cursor: pointer;\n}\n.asciinema-player .control-bar .fullscreen-button svg {\n  width: 14px;\n  height: 14px;\n}\n.asciinema-player .control-bar .fullscreen-button svg:first-child {\n  display: inline;\n}\n.asciinema-player .control-bar .fullscreen-button svg:last-child {\n  display: none;\n}\n.asciinema-player-wrapper.hud .control-bar {\n  bottom: 0px;\n}\n.asciinema-player-wrapper:fullscreen .fullscreen-button svg:first-child {\n  display: none;\n}\n.asciinema-player-wrapper:fullscreen .fullscreen-button svg:last-child {\n  display: inline;\n}\n.asciinema-player-wrapper:-webkit-full-screen .fullscreen-button svg:first-child {\n  display: none;\n}\n.asciinema-player-wrapper:-webkit-full-screen .fullscreen-button svg:last-child {\n  display: inline;\n}\n.asciinema-player-wrapper:-moz-full-screen .fullscreen-button svg:first-child {\n  display: none;\n}\n.asciinema-player-wrapper:-moz-full-screen .fullscreen-button svg:last-child {\n  display: inline;\n}\n.asciinema-player-wrapper:-ms-fullscreen .fullscreen-button svg:first-child {\n  display: none;\n}\n.asciinema-player-wrapper:-ms-fullscreen .fullscreen-button svg:last-child {\n  display: inline;\n}\n.asciinema-player .loading {\n  z-index: 10;\n  background-repeat: no-repeat;\n  background-position: center;\n  position: absolute;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 32px;\n  background-color: rgba(0, 0, 0, 0.5);\n}\n.asciinema-player .start-prompt {\n  z-index: 10;\n  background-repeat: no-repeat;\n  background-position: center;\n  position: absolute;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 32px;\n  z-index: 20;\n  cursor: pointer;\n}\n.asciinema-player .start-prompt .play-button {\n  font-size: 0px;\n}\n.asciinema-player .start-prompt .play-button {\n  position: absolute;\n  left: 0;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  text-align: center;\n  color: white;\n  display: table;\n  width: 100%;\n  height: 100%;\n}\n.asciinema-player .start-prompt .play-button div {\n  vertical-align: middle;\n  display: table-cell;\n}\n.asciinema-player .start-prompt .play-button div span {\n  width: 96px;\n  height: 96px;\n  display: inline-block;\n}\n@-webkit-keyframes expand {\n  0% {\n    -webkit-transform: scale(0);\n  }\n  50% {\n    -webkit-transform: scale(1);\n  }\n  100% {\n    z-index: 1;\n  }\n}\n@-moz-keyframes expand {\n  0% {\n    -moz-transform: scale(0);\n  }\n  50% {\n    -moz-transform: scale(1);\n  }\n  100% {\n    z-index: 1;\n  }\n}\n@-o-keyframes expand {\n  0% {\n    -o-transform: scale(0);\n  }\n  50% {\n    -o-transform: scale(1);\n  }\n  100% {\n    z-index: 1;\n  }\n}\n@keyframes expand {\n  0% {\n    transform: scale(0);\n  }\n  50% {\n    transform: scale(1);\n  }\n  100% {\n    z-index: 1;\n  }\n}\n.loader {\n  position: absolute;\n  left: 50%;\n  top: 50%;\n  margin: -20px 0 0 -20px;\n  background-color: white;\n  border-radius: 50%;\n  box-shadow: 0 0 0 6.66667px #141414;\n  width: 40px;\n  height: 40px;\n}\n.loader:before,\n.loader:after {\n  content: \"\";\n  position: absolute;\n  left: 50%;\n  top: 50%;\n  display: block;\n  margin: -21px 0 0 -21px;\n  border-radius: 50%;\n  z-index: 2;\n  width: 42px;\n  height: 42px;\n}\n.loader:before {\n  background-color: #141414;\n  -webkit-animation: expand 1.6s linear infinite both;\n  -moz-animation: expand 1.6s linear infinite both;\n  animation: expand 1.6s linear infinite both;\n}\n.loader:after {\n  background-color: white;\n  -webkit-animation: expand 1.6s linear 0.8s infinite both;\n  -moz-animation: expand 1.6s linear 0.8s infinite both;\n  animation: expand 1.6s linear 0.8s infinite both;\n}\n.asciinema-terminal .fg-16 {\n  color: #000000;\n}\n.asciinema-terminal .bg-16 {\n  background-color: #000000;\n}\n.asciinema-terminal .fg-17 {\n  color: #00005f;\n}\n.asciinema-terminal .bg-17 {\n  background-color: #00005f;\n}\n.asciinema-terminal .fg-18 {\n  color: #000087;\n}\n.asciinema-terminal .bg-18 {\n  background-color: #000087;\n}\n.asciinema-terminal .fg-19 {\n  color: #0000af;\n}\n.asciinema-terminal .bg-19 {\n  background-color: #0000af;\n}\n.asciinema-terminal .fg-20 {\n  color: #0000d7;\n}\n.asciinema-terminal .bg-20 {\n  background-color: #0000d7;\n}\n.asciinema-terminal .fg-21 {\n  color: #0000ff;\n}\n.asciinema-terminal .bg-21 {\n  background-color: #0000ff;\n}\n.asciinema-terminal .fg-22 {\n  color: #005f00;\n}\n.asciinema-terminal .bg-22 {\n  background-color: #005f00;\n}\n.asciinema-terminal .fg-23 {\n  color: #005f5f;\n}\n.asciinema-terminal .bg-23 {\n  background-color: #005f5f;\n}\n.asciinema-terminal .fg-24 {\n  color: #005f87;\n}\n.asciinema-terminal .bg-24 {\n  background-color: #005f87;\n}\n.asciinema-terminal .fg-25 {\n  color: #005faf;\n}\n.asciinema-terminal .bg-25 {\n  background-color: #005faf;\n}\n.asciinema-terminal .fg-26 {\n  color: #005fd7;\n}\n.asciinema-terminal .bg-26 {\n  background-color: #005fd7;\n}\n.asciinema-terminal .fg-27 {\n  color: #005fff;\n}\n.asciinema-terminal .bg-27 {\n  background-color: #005fff;\n}\n.asciinema-terminal .fg-28 {\n  color: #008700;\n}\n.asciinema-terminal .bg-28 {\n  background-color: #008700;\n}\n.asciinema-terminal .fg-29 {\n  color: #00875f;\n}\n.asciinema-terminal .bg-29 {\n  background-color: #00875f;\n}\n.asciinema-terminal .fg-30 {\n  color: #008787;\n}\n.asciinema-terminal .bg-30 {\n  background-color: #008787;\n}\n.asciinema-terminal .fg-31 {\n  color: #0087af;\n}\n.asciinema-terminal .bg-31 {\n  background-color: #0087af;\n}\n.asciinema-terminal .fg-32 {\n  color: #0087d7;\n}\n.asciinema-terminal .bg-32 {\n  background-color: #0087d7;\n}\n.asciinema-terminal .fg-33 {\n  color: #0087ff;\n}\n.asciinema-terminal .bg-33 {\n  background-color: #0087ff;\n}\n.asciinema-terminal .fg-34 {\n  color: #00af00;\n}\n.asciinema-terminal .bg-34 {\n  background-color: #00af00;\n}\n.asciinema-terminal .fg-35 {\n  color: #00af5f;\n}\n.asciinema-terminal .bg-35 {\n  background-color: #00af5f;\n}\n.asciinema-terminal .fg-36 {\n  color: #00af87;\n}\n.asciinema-terminal .bg-36 {\n  background-color: #00af87;\n}\n.asciinema-terminal .fg-37 {\n  color: #00afaf;\n}\n.asciinema-terminal .bg-37 {\n  background-color: #00afaf;\n}\n.asciinema-terminal .fg-38 {\n  color: #00afd7;\n}\n.asciinema-terminal .bg-38 {\n  background-color: #00afd7;\n}\n.asciinema-terminal .fg-39 {\n  color: #00afff;\n}\n.asciinema-terminal .bg-39 {\n  background-color: #00afff;\n}\n.asciinema-terminal .fg-40 {\n  color: #00d700;\n}\n.asciinema-terminal .bg-40 {\n  background-color: #00d700;\n}\n.asciinema-terminal .fg-41 {\n  color: #00d75f;\n}\n.asciinema-terminal .bg-41 {\n  background-color: #00d75f;\n}\n.asciinema-terminal .fg-42 {\n  color: #00d787;\n}\n.asciinema-terminal .bg-42 {\n  background-color: #00d787;\n}\n.asciinema-terminal .fg-43 {\n  color: #00d7af;\n}\n.asciinema-terminal .bg-43 {\n  background-color: #00d7af;\n}\n.asciinema-terminal .fg-44 {\n  color: #00d7d7;\n}\n.asciinema-terminal .bg-44 {\n  background-color: #00d7d7;\n}\n.asciinema-terminal .fg-45 {\n  color: #00d7ff;\n}\n.asciinema-terminal .bg-45 {\n  background-color: #00d7ff;\n}\n.asciinema-terminal .fg-46 {\n  color: #00ff00;\n}\n.asciinema-terminal .bg-46 {\n  background-color: #00ff00;\n}\n.asciinema-terminal .fg-47 {\n  color: #00ff5f;\n}\n.asciinema-terminal .bg-47 {\n  background-color: #00ff5f;\n}\n.asciinema-terminal .fg-48 {\n  color: #00ff87;\n}\n.asciinema-terminal .bg-48 {\n  background-color: #00ff87;\n}\n.asciinema-terminal .fg-49 {\n  color: #00ffaf;\n}\n.asciinema-terminal .bg-49 {\n  background-color: #00ffaf;\n}\n.asciinema-terminal .fg-50 {\n  color: #00ffd7;\n}\n.asciinema-terminal .bg-50 {\n  background-color: #00ffd7;\n}\n.asciinema-terminal .fg-51 {\n  color: #00ffff;\n}\n.asciinema-terminal .bg-51 {\n  background-color: #00ffff;\n}\n.asciinema-terminal .fg-52 {\n  color: #5f0000;\n}\n.asciinema-terminal .bg-52 {\n  background-color: #5f0000;\n}\n.asciinema-terminal .fg-53 {\n  color: #5f005f;\n}\n.asciinema-terminal .bg-53 {\n  background-color: #5f005f;\n}\n.asciinema-terminal .fg-54 {\n  color: #5f0087;\n}\n.asciinema-terminal .bg-54 {\n  background-color: #5f0087;\n}\n.asciinema-terminal .fg-55 {\n  color: #5f00af;\n}\n.asciinema-terminal .bg-55 {\n  background-color: #5f00af;\n}\n.asciinema-terminal .fg-56 {\n  color: #5f00d7;\n}\n.asciinema-terminal .bg-56 {\n  background-color: #5f00d7;\n}\n.asciinema-terminal .fg-57 {\n  color: #5f00ff;\n}\n.asciinema-terminal .bg-57 {\n  background-color: #5f00ff;\n}\n.asciinema-terminal .fg-58 {\n  color: #5f5f00;\n}\n.asciinema-terminal .bg-58 {\n  background-color: #5f5f00;\n}\n.asciinema-terminal .fg-59 {\n  color: #5f5f5f;\n}\n.asciinema-terminal .bg-59 {\n  background-color: #5f5f5f;\n}\n.asciinema-terminal .fg-60 {\n  color: #5f5f87;\n}\n.asciinema-terminal .bg-60 {\n  background-color: #5f5f87;\n}\n.asciinema-terminal .fg-61 {\n  color: #5f5faf;\n}\n.asciinema-terminal .bg-61 {\n  background-color: #5f5faf;\n}\n.asciinema-terminal .fg-62 {\n  color: #5f5fd7;\n}\n.asciinema-terminal .bg-62 {\n  background-color: #5f5fd7;\n}\n.asciinema-terminal .fg-63 {\n  color: #5f5fff;\n}\n.asciinema-terminal .bg-63 {\n  background-color: #5f5fff;\n}\n.asciinema-terminal .fg-64 {\n  color: #5f8700;\n}\n.asciinema-terminal .bg-64 {\n  background-color: #5f8700;\n}\n.asciinema-terminal .fg-65 {\n  color: #5f875f;\n}\n.asciinema-terminal .bg-65 {\n  background-color: #5f875f;\n}\n.asciinema-terminal .fg-66 {\n  color: #5f8787;\n}\n.asciinema-terminal .bg-66 {\n  background-color: #5f8787;\n}\n.asciinema-terminal .fg-67 {\n  color: #5f87af;\n}\n.asciinema-terminal .bg-67 {\n  background-color: #5f87af;\n}\n.asciinema-terminal .fg-68 {\n  color: #5f87d7;\n}\n.asciinema-terminal .bg-68 {\n  background-color: #5f87d7;\n}\n.asciinema-terminal .fg-69 {\n  color: #5f87ff;\n}\n.asciinema-terminal .bg-69 {\n  background-color: #5f87ff;\n}\n.asciinema-terminal .fg-70 {\n  color: #5faf00;\n}\n.asciinema-terminal .bg-70 {\n  background-color: #5faf00;\n}\n.asciinema-terminal .fg-71 {\n  color: #5faf5f;\n}\n.asciinema-terminal .bg-71 {\n  background-color: #5faf5f;\n}\n.asciinema-terminal .fg-72 {\n  color: #5faf87;\n}\n.asciinema-terminal .bg-72 {\n  background-color: #5faf87;\n}\n.asciinema-terminal .fg-73 {\n  color: #5fafaf;\n}\n.asciinema-terminal .bg-73 {\n  background-color: #5fafaf;\n}\n.asciinema-terminal .fg-74 {\n  color: #5fafd7;\n}\n.asciinema-terminal .bg-74 {\n  background-color: #5fafd7;\n}\n.asciinema-terminal .fg-75 {\n  color: #5fafff;\n}\n.asciinema-terminal .bg-75 {\n  background-color: #5fafff;\n}\n.asciinema-terminal .fg-76 {\n  color: #5fd700;\n}\n.asciinema-terminal .bg-76 {\n  background-color: #5fd700;\n}\n.asciinema-terminal .fg-77 {\n  color: #5fd75f;\n}\n.asciinema-terminal .bg-77 {\n  background-color: #5fd75f;\n}\n.asciinema-terminal .fg-78 {\n  color: #5fd787;\n}\n.asciinema-terminal .bg-78 {\n  background-color: #5fd787;\n}\n.asciinema-terminal .fg-79 {\n  color: #5fd7af;\n}\n.asciinema-terminal .bg-79 {\n  background-color: #5fd7af;\n}\n.asciinema-terminal .fg-80 {\n  color: #5fd7d7;\n}\n.asciinema-terminal .bg-80 {\n  background-color: #5fd7d7;\n}\n.asciinema-terminal .fg-81 {\n  color: #5fd7ff;\n}\n.asciinema-terminal .bg-81 {\n  background-color: #5fd7ff;\n}\n.asciinema-terminal .fg-82 {\n  color: #5fff00;\n}\n.asciinema-terminal .bg-82 {\n  background-color: #5fff00;\n}\n.asciinema-terminal .fg-83 {\n  color: #5fff5f;\n}\n.asciinema-terminal .bg-83 {\n  background-color: #5fff5f;\n}\n.asciinema-terminal .fg-84 {\n  color: #5fff87;\n}\n.asciinema-terminal .bg-84 {\n  background-color: #5fff87;\n}\n.asciinema-terminal .fg-85 {\n  color: #5fffaf;\n}\n.asciinema-terminal .bg-85 {\n  background-color: #5fffaf;\n}\n.asciinema-terminal .fg-86 {\n  color: #5fffd7;\n}\n.asciinema-terminal .bg-86 {\n  background-color: #5fffd7;\n}\n.asciinema-terminal .fg-87 {\n  color: #5fffff;\n}\n.asciinema-terminal .bg-87 {\n  background-color: #5fffff;\n}\n.asciinema-terminal .fg-88 {\n  color: #870000;\n}\n.asciinema-terminal .bg-88 {\n  background-color: #870000;\n}\n.asciinema-terminal .fg-89 {\n  color: #87005f;\n}\n.asciinema-terminal .bg-89 {\n  background-color: #87005f;\n}\n.asciinema-terminal .fg-90 {\n  color: #870087;\n}\n.asciinema-terminal .bg-90 {\n  background-color: #870087;\n}\n.asciinema-terminal .fg-91 {\n  color: #8700af;\n}\n.asciinema-terminal .bg-91 {\n  background-color: #8700af;\n}\n.asciinema-terminal .fg-92 {\n  color: #8700d7;\n}\n.asciinema-terminal .bg-92 {\n  background-color: #8700d7;\n}\n.asciinema-terminal .fg-93 {\n  color: #8700ff;\n}\n.asciinema-terminal .bg-93 {\n  background-color: #8700ff;\n}\n.asciinema-terminal .fg-94 {\n  color: #875f00;\n}\n.asciinema-terminal .bg-94 {\n  background-color: #875f00;\n}\n.asciinema-terminal .fg-95 {\n  color: #875f5f;\n}\n.asciinema-terminal .bg-95 {\n  background-color: #875f5f;\n}\n.asciinema-terminal .fg-96 {\n  color: #875f87;\n}\n.asciinema-terminal .bg-96 {\n  background-color: #875f87;\n}\n.asciinema-terminal .fg-97 {\n  color: #875faf;\n}\n.asciinema-terminal .bg-97 {\n  background-color: #875faf;\n}\n.asciinema-terminal .fg-98 {\n  color: #875fd7;\n}\n.asciinema-terminal .bg-98 {\n  background-color: #875fd7;\n}\n.asciinema-terminal .fg-99 {\n  color: #875fff;\n}\n.asciinema-terminal .bg-99 {\n  background-color: #875fff;\n}\n.asciinema-terminal .fg-100 {\n  color: #878700;\n}\n.asciinema-terminal .bg-100 {\n  background-color: #878700;\n}\n.asciinema-terminal .fg-101 {\n  color: #87875f;\n}\n.asciinema-terminal .bg-101 {\n  background-color: #87875f;\n}\n.asciinema-terminal .fg-102 {\n  color: #878787;\n}\n.asciinema-terminal .bg-102 {\n  background-color: #878787;\n}\n.asciinema-terminal .fg-103 {\n  color: #8787af;\n}\n.asciinema-terminal .bg-103 {\n  background-color: #8787af;\n}\n.asciinema-terminal .fg-104 {\n  color: #8787d7;\n}\n.asciinema-terminal .bg-104 {\n  background-color: #8787d7;\n}\n.asciinema-terminal .fg-105 {\n  color: #8787ff;\n}\n.asciinema-terminal .bg-105 {\n  background-color: #8787ff;\n}\n.asciinema-terminal .fg-106 {\n  color: #87af00;\n}\n.asciinema-terminal .bg-106 {\n  background-color: #87af00;\n}\n.asciinema-terminal .fg-107 {\n  color: #87af5f;\n}\n.asciinema-terminal .bg-107 {\n  background-color: #87af5f;\n}\n.asciinema-terminal .fg-108 {\n  color: #87af87;\n}\n.asciinema-terminal .bg-108 {\n  background-color: #87af87;\n}\n.asciinema-terminal .fg-109 {\n  color: #87afaf;\n}\n.asciinema-terminal .bg-109 {\n  background-color: #87afaf;\n}\n.asciinema-terminal .fg-110 {\n  color: #87afd7;\n}\n.asciinema-terminal .bg-110 {\n  background-color: #87afd7;\n}\n.asciinema-terminal .fg-111 {\n  color: #87afff;\n}\n.asciinema-terminal .bg-111 {\n  background-color: #87afff;\n}\n.asciinema-terminal .fg-112 {\n  color: #87d700;\n}\n.asciinema-terminal .bg-112 {\n  background-color: #87d700;\n}\n.asciinema-terminal .fg-113 {\n  color: #87d75f;\n}\n.asciinema-terminal .bg-113 {\n  background-color: #87d75f;\n}\n.asciinema-terminal .fg-114 {\n  color: #87d787;\n}\n.asciinema-terminal .bg-114 {\n  background-color: #87d787;\n}\n.asciinema-terminal .fg-115 {\n  color: #87d7af;\n}\n.asciinema-terminal .bg-115 {\n  background-color: #87d7af;\n}\n.asciinema-terminal .fg-116 {\n  color: #87d7d7;\n}\n.asciinema-terminal .bg-116 {\n  background-color: #87d7d7;\n}\n.asciinema-terminal .fg-117 {\n  color: #87d7ff;\n}\n.asciinema-terminal .bg-117 {\n  background-color: #87d7ff;\n}\n.asciinema-terminal .fg-118 {\n  color: #87ff00;\n}\n.asciinema-terminal .bg-118 {\n  background-color: #87ff00;\n}\n.asciinema-terminal .fg-119 {\n  color: #87ff5f;\n}\n.asciinema-terminal .bg-119 {\n  background-color: #87ff5f;\n}\n.asciinema-terminal .fg-120 {\n  color: #87ff87;\n}\n.asciinema-terminal .bg-120 {\n  background-color: #87ff87;\n}\n.asciinema-terminal .fg-121 {\n  color: #87ffaf;\n}\n.asciinema-terminal .bg-121 {\n  background-color: #87ffaf;\n}\n.asciinema-terminal .fg-122 {\n  color: #87ffd7;\n}\n.asciinema-terminal .bg-122 {\n  background-color: #87ffd7;\n}\n.asciinema-terminal .fg-123 {\n  color: #87ffff;\n}\n.asciinema-terminal .bg-123 {\n  background-color: #87ffff;\n}\n.asciinema-terminal .fg-124 {\n  color: #af0000;\n}\n.asciinema-terminal .bg-124 {\n  background-color: #af0000;\n}\n.asciinema-terminal .fg-125 {\n  color: #af005f;\n}\n.asciinema-terminal .bg-125 {\n  background-color: #af005f;\n}\n.asciinema-terminal .fg-126 {\n  color: #af0087;\n}\n.asciinema-terminal .bg-126 {\n  background-color: #af0087;\n}\n.asciinema-terminal .fg-127 {\n  color: #af00af;\n}\n.asciinema-terminal .bg-127 {\n  background-color: #af00af;\n}\n.asciinema-terminal .fg-128 {\n  color: #af00d7;\n}\n.asciinema-terminal .bg-128 {\n  background-color: #af00d7;\n}\n.asciinema-terminal .fg-129 {\n  color: #af00ff;\n}\n.asciinema-terminal .bg-129 {\n  background-color: #af00ff;\n}\n.asciinema-terminal .fg-130 {\n  color: #af5f00;\n}\n.asciinema-terminal .bg-130 {\n  background-color: #af5f00;\n}\n.asciinema-terminal .fg-131 {\n  color: #af5f5f;\n}\n.asciinema-terminal .bg-131 {\n  background-color: #af5f5f;\n}\n.asciinema-terminal .fg-132 {\n  color: #af5f87;\n}\n.asciinema-terminal .bg-132 {\n  background-color: #af5f87;\n}\n.asciinema-terminal .fg-133 {\n  color: #af5faf;\n}\n.asciinema-terminal .bg-133 {\n  background-color: #af5faf;\n}\n.asciinema-terminal .fg-134 {\n  color: #af5fd7;\n}\n.asciinema-terminal .bg-134 {\n  background-color: #af5fd7;\n}\n.asciinema-terminal .fg-135 {\n  color: #af5fff;\n}\n.asciinema-terminal .bg-135 {\n  background-color: #af5fff;\n}\n.asciinema-terminal .fg-136 {\n  color: #af8700;\n}\n.asciinema-terminal .bg-136 {\n  background-color: #af8700;\n}\n.asciinema-terminal .fg-137 {\n  color: #af875f;\n}\n.asciinema-terminal .bg-137 {\n  background-color: #af875f;\n}\n.asciinema-terminal .fg-138 {\n  color: #af8787;\n}\n.asciinema-terminal .bg-138 {\n  background-color: #af8787;\n}\n.asciinema-terminal .fg-139 {\n  color: #af87af;\n}\n.asciinema-terminal .bg-139 {\n  background-color: #af87af;\n}\n.asciinema-terminal .fg-140 {\n  color: #af87d7;\n}\n.asciinema-terminal .bg-140 {\n  background-color: #af87d7;\n}\n.asciinema-terminal .fg-141 {\n  color: #af87ff;\n}\n.asciinema-terminal .bg-141 {\n  background-color: #af87ff;\n}\n.asciinema-terminal .fg-142 {\n  color: #afaf00;\n}\n.asciinema-terminal .bg-142 {\n  background-color: #afaf00;\n}\n.asciinema-terminal .fg-143 {\n  color: #afaf5f;\n}\n.asciinema-terminal .bg-143 {\n  background-color: #afaf5f;\n}\n.asciinema-terminal .fg-144 {\n  color: #afaf87;\n}\n.asciinema-terminal .bg-144 {\n  background-color: #afaf87;\n}\n.asciinema-terminal .fg-145 {\n  color: #afafaf;\n}\n.asciinema-terminal .bg-145 {\n  background-color: #afafaf;\n}\n.asciinema-terminal .fg-146 {\n  color: #afafd7;\n}\n.asciinema-terminal .bg-146 {\n  background-color: #afafd7;\n}\n.asciinema-terminal .fg-147 {\n  color: #afafff;\n}\n.asciinema-terminal .bg-147 {\n  background-color: #afafff;\n}\n.asciinema-terminal .fg-148 {\n  color: #afd700;\n}\n.asciinema-terminal .bg-148 {\n  background-color: #afd700;\n}\n.asciinema-terminal .fg-149 {\n  color: #afd75f;\n}\n.asciinema-terminal .bg-149 {\n  background-color: #afd75f;\n}\n.asciinema-terminal .fg-150 {\n  color: #afd787;\n}\n.asciinema-terminal .bg-150 {\n  background-color: #afd787;\n}\n.asciinema-terminal .fg-151 {\n  color: #afd7af;\n}\n.asciinema-terminal .bg-151 {\n  background-color: #afd7af;\n}\n.asciinema-terminal .fg-152 {\n  color: #afd7d7;\n}\n.asciinema-terminal .bg-152 {\n  background-color: #afd7d7;\n}\n.asciinema-terminal .fg-153 {\n  color: #afd7ff;\n}\n.asciinema-terminal .bg-153 {\n  background-color: #afd7ff;\n}\n.asciinema-terminal .fg-154 {\n  color: #afff00;\n}\n.asciinema-terminal .bg-154 {\n  background-color: #afff00;\n}\n.asciinema-terminal .fg-155 {\n  color: #afff5f;\n}\n.asciinema-terminal .bg-155 {\n  background-color: #afff5f;\n}\n.asciinema-terminal .fg-156 {\n  color: #afff87;\n}\n.asciinema-terminal .bg-156 {\n  background-color: #afff87;\n}\n.asciinema-terminal .fg-157 {\n  color: #afffaf;\n}\n.asciinema-terminal .bg-157 {\n  background-color: #afffaf;\n}\n.asciinema-terminal .fg-158 {\n  color: #afffd7;\n}\n.asciinema-terminal .bg-158 {\n  background-color: #afffd7;\n}\n.asciinema-terminal .fg-159 {\n  color: #afffff;\n}\n.asciinema-terminal .bg-159 {\n  background-color: #afffff;\n}\n.asciinema-terminal .fg-160 {\n  color: #d70000;\n}\n.asciinema-terminal .bg-160 {\n  background-color: #d70000;\n}\n.asciinema-terminal .fg-161 {\n  color: #d7005f;\n}\n.asciinema-terminal .bg-161 {\n  background-color: #d7005f;\n}\n.asciinema-terminal .fg-162 {\n  color: #d70087;\n}\n.asciinema-terminal .bg-162 {\n  background-color: #d70087;\n}\n.asciinema-terminal .fg-163 {\n  color: #d700af;\n}\n.asciinema-terminal .bg-163 {\n  background-color: #d700af;\n}\n.asciinema-terminal .fg-164 {\n  color: #d700d7;\n}\n.asciinema-terminal .bg-164 {\n  background-color: #d700d7;\n}\n.asciinema-terminal .fg-165 {\n  color: #d700ff;\n}\n.asciinema-terminal .bg-165 {\n  background-color: #d700ff;\n}\n.asciinema-terminal .fg-166 {\n  color: #d75f00;\n}\n.asciinema-terminal .bg-166 {\n  background-color: #d75f00;\n}\n.asciinema-terminal .fg-167 {\n  color: #d75f5f;\n}\n.asciinema-terminal .bg-167 {\n  background-color: #d75f5f;\n}\n.asciinema-terminal .fg-168 {\n  color: #d75f87;\n}\n.asciinema-terminal .bg-168 {\n  background-color: #d75f87;\n}\n.asciinema-terminal .fg-169 {\n  color: #d75faf;\n}\n.asciinema-terminal .bg-169 {\n  background-color: #d75faf;\n}\n.asciinema-terminal .fg-170 {\n  color: #d75fd7;\n}\n.asciinema-terminal .bg-170 {\n  background-color: #d75fd7;\n}\n.asciinema-terminal .fg-171 {\n  color: #d75fff;\n}\n.asciinema-terminal .bg-171 {\n  background-color: #d75fff;\n}\n.asciinema-terminal .fg-172 {\n  color: #d78700;\n}\n.asciinema-terminal .bg-172 {\n  background-color: #d78700;\n}\n.asciinema-terminal .fg-173 {\n  color: #d7875f;\n}\n.asciinema-terminal .bg-173 {\n  background-color: #d7875f;\n}\n.asciinema-terminal .fg-174 {\n  color: #d78787;\n}\n.asciinema-terminal .bg-174 {\n  background-color: #d78787;\n}\n.asciinema-terminal .fg-175 {\n  color: #d787af;\n}\n.asciinema-terminal .bg-175 {\n  background-color: #d787af;\n}\n.asciinema-terminal .fg-176 {\n  color: #d787d7;\n}\n.asciinema-terminal .bg-176 {\n  background-color: #d787d7;\n}\n.asciinema-terminal .fg-177 {\n  color: #d787ff;\n}\n.asciinema-terminal .bg-177 {\n  background-color: #d787ff;\n}\n.asciinema-terminal .fg-178 {\n  color: #d7af00;\n}\n.asciinema-terminal .bg-178 {\n  background-color: #d7af00;\n}\n.asciinema-terminal .fg-179 {\n  color: #d7af5f;\n}\n.asciinema-terminal .bg-179 {\n  background-color: #d7af5f;\n}\n.asciinema-terminal .fg-180 {\n  color: #d7af87;\n}\n.asciinema-terminal .bg-180 {\n  background-color: #d7af87;\n}\n.asciinema-terminal .fg-181 {\n  color: #d7afaf;\n}\n.asciinema-terminal .bg-181 {\n  background-color: #d7afaf;\n}\n.asciinema-terminal .fg-182 {\n  color: #d7afd7;\n}\n.asciinema-terminal .bg-182 {\n  background-color: #d7afd7;\n}\n.asciinema-terminal .fg-183 {\n  color: #d7afff;\n}\n.asciinema-terminal .bg-183 {\n  background-color: #d7afff;\n}\n.asciinema-terminal .fg-184 {\n  color: #d7d700;\n}\n.asciinema-terminal .bg-184 {\n  background-color: #d7d700;\n}\n.asciinema-terminal .fg-185 {\n  color: #d7d75f;\n}\n.asciinema-terminal .bg-185 {\n  background-color: #d7d75f;\n}\n.asciinema-terminal .fg-186 {\n  color: #d7d787;\n}\n.asciinema-terminal .bg-186 {\n  background-color: #d7d787;\n}\n.asciinema-terminal .fg-187 {\n  color: #d7d7af;\n}\n.asciinema-terminal .bg-187 {\n  background-color: #d7d7af;\n}\n.asciinema-terminal .fg-188 {\n  color: #d7d7d7;\n}\n.asciinema-terminal .bg-188 {\n  background-color: #d7d7d7;\n}\n.asciinema-terminal .fg-189 {\n  color: #d7d7ff;\n}\n.asciinema-terminal .bg-189 {\n  background-color: #d7d7ff;\n}\n.asciinema-terminal .fg-190 {\n  color: #d7ff00;\n}\n.asciinema-terminal .bg-190 {\n  background-color: #d7ff00;\n}\n.asciinema-terminal .fg-191 {\n  color: #d7ff5f;\n}\n.asciinema-terminal .bg-191 {\n  background-color: #d7ff5f;\n}\n.asciinema-terminal .fg-192 {\n  color: #d7ff87;\n}\n.asciinema-terminal .bg-192 {\n  background-color: #d7ff87;\n}\n.asciinema-terminal .fg-193 {\n  color: #d7ffaf;\n}\n.asciinema-terminal .bg-193 {\n  background-color: #d7ffaf;\n}\n.asciinema-terminal .fg-194 {\n  color: #d7ffd7;\n}\n.asciinema-terminal .bg-194 {\n  background-color: #d7ffd7;\n}\n.asciinema-terminal .fg-195 {\n  color: #d7ffff;\n}\n.asciinema-terminal .bg-195 {\n  background-color: #d7ffff;\n}\n.asciinema-terminal .fg-196 {\n  color: #ff0000;\n}\n.asciinema-terminal .bg-196 {\n  background-color: #ff0000;\n}\n.asciinema-terminal .fg-197 {\n  color: #ff005f;\n}\n.asciinema-terminal .bg-197 {\n  background-color: #ff005f;\n}\n.asciinema-terminal .fg-198 {\n  color: #ff0087;\n}\n.asciinema-terminal .bg-198 {\n  background-color: #ff0087;\n}\n.asciinema-terminal .fg-199 {\n  color: #ff00af;\n}\n.asciinema-terminal .bg-199 {\n  background-color: #ff00af;\n}\n.asciinema-terminal .fg-200 {\n  color: #ff00d7;\n}\n.asciinema-terminal .bg-200 {\n  background-color: #ff00d7;\n}\n.asciinema-terminal .fg-201 {\n  color: #ff00ff;\n}\n.asciinema-terminal .bg-201 {\n  background-color: #ff00ff;\n}\n.asciinema-terminal .fg-202 {\n  color: #ff5f00;\n}\n.asciinema-terminal .bg-202 {\n  background-color: #ff5f00;\n}\n.asciinema-terminal .fg-203 {\n  color: #ff5f5f;\n}\n.asciinema-terminal .bg-203 {\n  background-color: #ff5f5f;\n}\n.asciinema-terminal .fg-204 {\n  color: #ff5f87;\n}\n.asciinema-terminal .bg-204 {\n  background-color: #ff5f87;\n}\n.asciinema-terminal .fg-205 {\n  color: #ff5faf;\n}\n.asciinema-terminal .bg-205 {\n  background-color: #ff5faf;\n}\n.asciinema-terminal .fg-206 {\n  color: #ff5fd7;\n}\n.asciinema-terminal .bg-206 {\n  background-color: #ff5fd7;\n}\n.asciinema-terminal .fg-207 {\n  color: #ff5fff;\n}\n.asciinema-terminal .bg-207 {\n  background-color: #ff5fff;\n}\n.asciinema-terminal .fg-208 {\n  color: #ff8700;\n}\n.asciinema-terminal .bg-208 {\n  background-color: #ff8700;\n}\n.asciinema-terminal .fg-209 {\n  color: #ff875f;\n}\n.asciinema-terminal .bg-209 {\n  background-color: #ff875f;\n}\n.asciinema-terminal .fg-210 {\n  color: #ff8787;\n}\n.asciinema-terminal .bg-210 {\n  background-color: #ff8787;\n}\n.asciinema-terminal .fg-211 {\n  color: #ff87af;\n}\n.asciinema-terminal .bg-211 {\n  background-color: #ff87af;\n}\n.asciinema-terminal .fg-212 {\n  color: #ff87d7;\n}\n.asciinema-terminal .bg-212 {\n  background-color: #ff87d7;\n}\n.asciinema-terminal .fg-213 {\n  color: #ff87ff;\n}\n.asciinema-terminal .bg-213 {\n  background-color: #ff87ff;\n}\n.asciinema-terminal .fg-214 {\n  color: #ffaf00;\n}\n.asciinema-terminal .bg-214 {\n  background-color: #ffaf00;\n}\n.asciinema-terminal .fg-215 {\n  color: #ffaf5f;\n}\n.asciinema-terminal .bg-215 {\n  background-color: #ffaf5f;\n}\n.asciinema-terminal .fg-216 {\n  color: #ffaf87;\n}\n.asciinema-terminal .bg-216 {\n  background-color: #ffaf87;\n}\n.asciinema-terminal .fg-217 {\n  color: #ffafaf;\n}\n.asciinema-terminal .bg-217 {\n  background-color: #ffafaf;\n}\n.asciinema-terminal .fg-218 {\n  color: #ffafd7;\n}\n.asciinema-terminal .bg-218 {\n  background-color: #ffafd7;\n}\n.asciinema-terminal .fg-219 {\n  color: #ffafff;\n}\n.asciinema-terminal .bg-219 {\n  background-color: #ffafff;\n}\n.asciinema-terminal .fg-220 {\n  color: #ffd700;\n}\n.asciinema-terminal .bg-220 {\n  background-color: #ffd700;\n}\n.asciinema-terminal .fg-221 {\n  color: #ffd75f;\n}\n.asciinema-terminal .bg-221 {\n  background-color: #ffd75f;\n}\n.asciinema-terminal .fg-222 {\n  color: #ffd787;\n}\n.asciinema-terminal .bg-222 {\n  background-color: #ffd787;\n}\n.asciinema-terminal .fg-223 {\n  color: #ffd7af;\n}\n.asciinema-terminal .bg-223 {\n  background-color: #ffd7af;\n}\n.asciinema-terminal .fg-224 {\n  color: #ffd7d7;\n}\n.asciinema-terminal .bg-224 {\n  background-color: #ffd7d7;\n}\n.asciinema-terminal .fg-225 {\n  color: #ffd7ff;\n}\n.asciinema-terminal .bg-225 {\n  background-color: #ffd7ff;\n}\n.asciinema-terminal .fg-226 {\n  color: #ffff00;\n}\n.asciinema-terminal .bg-226 {\n  background-color: #ffff00;\n}\n.asciinema-terminal .fg-227 {\n  color: #ffff5f;\n}\n.asciinema-terminal .bg-227 {\n  background-color: #ffff5f;\n}\n.asciinema-terminal .fg-228 {\n  color: #ffff87;\n}\n.asciinema-terminal .bg-228 {\n  background-color: #ffff87;\n}\n.asciinema-terminal .fg-229 {\n  color: #ffffaf;\n}\n.asciinema-terminal .bg-229 {\n  background-color: #ffffaf;\n}\n.asciinema-terminal .fg-230 {\n  color: #ffffd7;\n}\n.asciinema-terminal .bg-230 {\n  background-color: #ffffd7;\n}\n.asciinema-terminal .fg-231 {\n  color: #ffffff;\n}\n.asciinema-terminal .bg-231 {\n  background-color: #ffffff;\n}\n.asciinema-terminal .fg-232 {\n  color: #080808;\n}\n.asciinema-terminal .bg-232 {\n  background-color: #080808;\n}\n.asciinema-terminal .fg-233 {\n  color: #121212;\n}\n.asciinema-terminal .bg-233 {\n  background-color: #121212;\n}\n.asciinema-terminal .fg-234 {\n  color: #1c1c1c;\n}\n.asciinema-terminal .bg-234 {\n  background-color: #1c1c1c;\n}\n.asciinema-terminal .fg-235 {\n  color: #262626;\n}\n.asciinema-terminal .bg-235 {\n  background-color: #262626;\n}\n.asciinema-terminal .fg-236 {\n  color: #303030;\n}\n.asciinema-terminal .bg-236 {\n  background-color: #303030;\n}\n.asciinema-terminal .fg-237 {\n  color: #3a3a3a;\n}\n.asciinema-terminal .bg-237 {\n  background-color: #3a3a3a;\n}\n.asciinema-terminal .fg-238 {\n  color: #444444;\n}\n.asciinema-terminal .bg-238 {\n  background-color: #444444;\n}\n.asciinema-terminal .fg-239 {\n  color: #4e4e4e;\n}\n.asciinema-terminal .bg-239 {\n  background-color: #4e4e4e;\n}\n.asciinema-terminal .fg-240 {\n  color: #585858;\n}\n.asciinema-terminal .bg-240 {\n  background-color: #585858;\n}\n.asciinema-terminal .fg-241 {\n  color: #626262;\n}\n.asciinema-terminal .bg-241 {\n  background-color: #626262;\n}\n.asciinema-terminal .fg-242 {\n  color: #6c6c6c;\n}\n.asciinema-terminal .bg-242 {\n  background-color: #6c6c6c;\n}\n.asciinema-terminal .fg-243 {\n  color: #767676;\n}\n.asciinema-terminal .bg-243 {\n  background-color: #767676;\n}\n.asciinema-terminal .fg-244 {\n  color: #808080;\n}\n.asciinema-terminal .bg-244 {\n  background-color: #808080;\n}\n.asciinema-terminal .fg-245 {\n  color: #8a8a8a;\n}\n.asciinema-terminal .bg-245 {\n  background-color: #8a8a8a;\n}\n.asciinema-terminal .fg-246 {\n  color: #949494;\n}\n.asciinema-terminal .bg-246 {\n  background-color: #949494;\n}\n.asciinema-terminal .fg-247 {\n  color: #9e9e9e;\n}\n.asciinema-terminal .bg-247 {\n  background-color: #9e9e9e;\n}\n.asciinema-terminal .fg-248 {\n  color: #a8a8a8;\n}\n.asciinema-terminal .bg-248 {\n  background-color: #a8a8a8;\n}\n.asciinema-terminal .fg-249 {\n  color: #b2b2b2;\n}\n.asciinema-terminal .bg-249 {\n  background-color: #b2b2b2;\n}\n.asciinema-terminal .fg-250 {\n  color: #bcbcbc;\n}\n.asciinema-terminal .bg-250 {\n  background-color: #bcbcbc;\n}\n.asciinema-terminal .fg-251 {\n  color: #c6c6c6;\n}\n.asciinema-terminal .bg-251 {\n  background-color: #c6c6c6;\n}\n.asciinema-terminal .fg-252 {\n  color: #d0d0d0;\n}\n.asciinema-terminal .bg-252 {\n  background-color: #d0d0d0;\n}\n.asciinema-terminal .fg-253 {\n  color: #dadada;\n}\n.asciinema-terminal .bg-253 {\n  background-color: #dadada;\n}\n.asciinema-terminal .fg-254 {\n  color: #e4e4e4;\n}\n.asciinema-terminal .bg-254 {\n  background-color: #e4e4e4;\n}\n.asciinema-terminal .fg-255 {\n  color: #eeeeee;\n}\n.asciinema-terminal .bg-255 {\n  background-color: #eeeeee;\n}\n.asciinema-theme-asciinema .asciinema-terminal {\n  color: #cccccc;\n  background-color: #121314;\n  border-color: #121314;\n}\n.asciinema-theme-asciinema .fg-bg {\n  color: #121314;\n}\n.asciinema-theme-asciinema .bg-fg {\n  background-color: #cccccc;\n}\n.asciinema-theme-asciinema .fg-0 {\n  color: #000000;\n}\n.asciinema-theme-asciinema .bg-0 {\n  background-color: #000000;\n}\n.asciinema-theme-asciinema .fg-1 {\n  color: #dd3c69;\n}\n.asciinema-theme-asciinema .bg-1 {\n  background-color: #dd3c69;\n}\n.asciinema-theme-asciinema .fg-2 {\n  color: #4ebf22;\n}\n.asciinema-theme-asciinema .bg-2 {\n  background-color: #4ebf22;\n}\n.asciinema-theme-asciinema .fg-3 {\n  color: #ddaf3c;\n}\n.asciinema-theme-asciinema .bg-3 {\n  background-color: #ddaf3c;\n}\n.asciinema-theme-asciinema .fg-4 {\n  color: #26b0d7;\n}\n.asciinema-theme-asciinema .bg-4 {\n  background-color: #26b0d7;\n}\n.asciinema-theme-asciinema .fg-5 {\n  color: #b954e1;\n}\n.asciinema-theme-asciinema .bg-5 {\n  background-color: #b954e1;\n}\n.asciinema-theme-asciinema .fg-6 {\n  color: #54e1b9;\n}\n.asciinema-theme-asciinema .bg-6 {\n  background-color: #54e1b9;\n}\n.asciinema-theme-asciinema .fg-7 {\n  color: #d9d9d9;\n}\n.asciinema-theme-asciinema .bg-7 {\n  background-color: #d9d9d9;\n}\n.asciinema-theme-asciinema .fg-8 {\n  color: #4d4d4d;\n}\n.asciinema-theme-asciinema .bg-8 {\n  background-color: #4d4d4d;\n}\n.asciinema-theme-asciinema .fg-9 {\n  color: #dd3c69;\n}\n.asciinema-theme-asciinema .bg-9 {\n  background-color: #dd3c69;\n}\n.asciinema-theme-asciinema .fg-10 {\n  color: #4ebf22;\n}\n.asciinema-theme-asciinema .bg-10 {\n  background-color: #4ebf22;\n}\n.asciinema-theme-asciinema .fg-11 {\n  color: #ddaf3c;\n}\n.asciinema-theme-asciinema .bg-11 {\n  background-color: #ddaf3c;\n}\n.asciinema-theme-asciinema .fg-12 {\n  color: #26b0d7;\n}\n.asciinema-theme-asciinema .bg-12 {\n  background-color: #26b0d7;\n}\n.asciinema-theme-asciinema .fg-13 {\n  color: #b954e1;\n}\n.asciinema-theme-asciinema .bg-13 {\n  background-color: #b954e1;\n}\n.asciinema-theme-asciinema .fg-14 {\n  color: #54e1b9;\n}\n.asciinema-theme-asciinema .bg-14 {\n  background-color: #54e1b9;\n}\n.asciinema-theme-asciinema .fg-15 {\n  color: #ffffff;\n}\n.asciinema-theme-asciinema .bg-15 {\n  background-color: #ffffff;\n}\n.asciinema-theme-asciinema .fg-8,\n.asciinema-theme-asciinema .fg-9,\n.asciinema-theme-asciinema .fg-10,\n.asciinema-theme-asciinema .fg-11,\n.asciinema-theme-asciinema .fg-12,\n.asciinema-theme-asciinema .fg-13,\n.asciinema-theme-asciinema .fg-14,\n.asciinema-theme-asciinema .fg-15 {\n  font-weight: bold;\n}\n.asciinema-theme-tango .asciinema-terminal {\n  color: #cccccc;\n  background-color: #121314;\n  border-color: #121314;\n}\n.asciinema-theme-tango .fg-bg {\n  color: #121314;\n}\n.asciinema-theme-tango .bg-fg {\n  background-color: #cccccc;\n}\n.asciinema-theme-tango .fg-0 {\n  color: #000000;\n}\n.asciinema-theme-tango .bg-0 {\n  background-color: #000000;\n}\n.asciinema-theme-tango .fg-1 {\n  color: #cc0000;\n}\n.asciinema-theme-tango .bg-1 {\n  background-color: #cc0000;\n}\n.asciinema-theme-tango .fg-2 {\n  color: #4e9a06;\n}\n.asciinema-theme-tango .bg-2 {\n  background-color: #4e9a06;\n}\n.asciinema-theme-tango .fg-3 {\n  color: #c4a000;\n}\n.asciinema-theme-tango .bg-3 {\n  background-color: #c4a000;\n}\n.asciinema-theme-tango .fg-4 {\n  color: #3465a4;\n}\n.asciinema-theme-tango .bg-4 {\n  background-color: #3465a4;\n}\n.asciinema-theme-tango .fg-5 {\n  color: #75507b;\n}\n.asciinema-theme-tango .bg-5 {\n  background-color: #75507b;\n}\n.asciinema-theme-tango .fg-6 {\n  color: #06989a;\n}\n.asciinema-theme-tango .bg-6 {\n  background-color: #06989a;\n}\n.asciinema-theme-tango .fg-7 {\n  color: #d3d7cf;\n}\n.asciinema-theme-tango .bg-7 {\n  background-color: #d3d7cf;\n}\n.asciinema-theme-tango .fg-8 {\n  color: #555753;\n}\n.asciinema-theme-tango .bg-8 {\n  background-color: #555753;\n}\n.asciinema-theme-tango .fg-9 {\n  color: #ef2929;\n}\n.asciinema-theme-tango .bg-9 {\n  background-color: #ef2929;\n}\n.asciinema-theme-tango .fg-10 {\n  color: #8ae234;\n}\n.asciinema-theme-tango .bg-10 {\n  background-color: #8ae234;\n}\n.asciinema-theme-tango .fg-11 {\n  color: #fce94f;\n}\n.asciinema-theme-tango .bg-11 {\n  background-color: #fce94f;\n}\n.asciinema-theme-tango .fg-12 {\n  color: #729fcf;\n}\n.asciinema-theme-tango .bg-12 {\n  background-color: #729fcf;\n}\n.asciinema-theme-tango .fg-13 {\n  color: #ad7fa8;\n}\n.asciinema-theme-tango .bg-13 {\n  background-color: #ad7fa8;\n}\n.asciinema-theme-tango .fg-14 {\n  color: #34e2e2;\n}\n.asciinema-theme-tango .bg-14 {\n  background-color: #34e2e2;\n}\n.asciinema-theme-tango .fg-15 {\n  color: #eeeeec;\n}\n.asciinema-theme-tango .bg-15 {\n  background-color: #eeeeec;\n}\n.asciinema-theme-tango .fg-8,\n.asciinema-theme-tango .fg-9,\n.asciinema-theme-tango .fg-10,\n.asciinema-theme-tango .fg-11,\n.asciinema-theme-tango .fg-12,\n.asciinema-theme-tango .fg-13,\n.asciinema-theme-tango .fg-14,\n.asciinema-theme-tango .fg-15 {\n  font-weight: bold;\n}\n.asciinema-theme-solarized-dark .asciinema-terminal {\n  color: #839496;\n  background-color: #002b36;\n  border-color: #002b36;\n}\n.asciinema-theme-solarized-dark .fg-bg {\n  color: #002b36;\n}\n.asciinema-theme-solarized-dark .bg-fg {\n  background-color: #839496;\n}\n.asciinema-theme-solarized-dark .fg-0 {\n  color: #073642;\n}\n.asciinema-theme-solarized-dark .bg-0 {\n  background-color: #073642;\n}\n.asciinema-theme-solarized-dark .fg-1 {\n  color: #dc322f;\n}\n.asciinema-theme-solarized-dark .bg-1 {\n  background-color: #dc322f;\n}\n.asciinema-theme-solarized-dark .fg-2 {\n  color: #859900;\n}\n.asciinema-theme-solarized-dark .bg-2 {\n  background-color: #859900;\n}\n.asciinema-theme-solarized-dark .fg-3 {\n  color: #b58900;\n}\n.asciinema-theme-solarized-dark .bg-3 {\n  background-color: #b58900;\n}\n.asciinema-theme-solarized-dark .fg-4 {\n  color: #268bd2;\n}\n.asciinema-theme-solarized-dark .bg-4 {\n  background-color: #268bd2;\n}\n.asciinema-theme-solarized-dark .fg-5 {\n  color: #d33682;\n}\n.asciinema-theme-solarized-dark .bg-5 {\n  background-color: #d33682;\n}\n.asciinema-theme-solarized-dark .fg-6 {\n  color: #2aa198;\n}\n.asciinema-theme-solarized-dark .bg-6 {\n  background-color: #2aa198;\n}\n.asciinema-theme-solarized-dark .fg-7 {\n  color: #eee8d5;\n}\n.asciinema-theme-solarized-dark .bg-7 {\n  background-color: #eee8d5;\n}\n.asciinema-theme-solarized-dark .fg-8 {\n  color: #002b36;\n}\n.asciinema-theme-solarized-dark .bg-8 {\n  background-color: #002b36;\n}\n.asciinema-theme-solarized-dark .fg-9 {\n  color: #cb4b16;\n}\n.asciinema-theme-solarized-dark .bg-9 {\n  background-color: #cb4b16;\n}\n.asciinema-theme-solarized-dark .fg-10 {\n  color: #586e75;\n}\n.asciinema-theme-solarized-dark .bg-10 {\n  background-color: #586e75;\n}\n.asciinema-theme-solarized-dark .fg-11 {\n  color: #657b83;\n}\n.asciinema-theme-solarized-dark .bg-11 {\n  background-color: #657b83;\n}\n.asciinema-theme-solarized-dark .fg-12 {\n  color: #839496;\n}\n.asciinema-theme-solarized-dark .bg-12 {\n  background-color: #839496;\n}\n.asciinema-theme-solarized-dark .fg-13 {\n  color: #6c71c4;\n}\n.asciinema-theme-solarized-dark .bg-13 {\n  background-color: #6c71c4;\n}\n.asciinema-theme-solarized-dark .fg-14 {\n  color: #93a1a1;\n}\n.asciinema-theme-solarized-dark .bg-14 {\n  background-color: #93a1a1;\n}\n.asciinema-theme-solarized-dark .fg-15 {\n  color: #fdf6e3;\n}\n.asciinema-theme-solarized-dark .bg-15 {\n  background-color: #fdf6e3;\n}\n.asciinema-theme-solarized-light .asciinema-terminal {\n  color: #657b83;\n  background-color: #fdf6e3;\n  border-color: #fdf6e3;\n}\n.asciinema-theme-solarized-light .fg-bg {\n  color: #fdf6e3;\n}\n.asciinema-theme-solarized-light .bg-fg {\n  background-color: #657b83;\n}\n.asciinema-theme-solarized-light .fg-0 {\n  color: #073642;\n}\n.asciinema-theme-solarized-light .bg-0 {\n  background-color: #073642;\n}\n.asciinema-theme-solarized-light .fg-1 {\n  color: #dc322f;\n}\n.asciinema-theme-solarized-light .bg-1 {\n  background-color: #dc322f;\n}\n.asciinema-theme-solarized-light .fg-2 {\n  color: #859900;\n}\n.asciinema-theme-solarized-light .bg-2 {\n  background-color: #859900;\n}\n.asciinema-theme-solarized-light .fg-3 {\n  color: #b58900;\n}\n.asciinema-theme-solarized-light .bg-3 {\n  background-color: #b58900;\n}\n.asciinema-theme-solarized-light .fg-4 {\n  color: #268bd2;\n}\n.asciinema-theme-solarized-light .bg-4 {\n  background-color: #268bd2;\n}\n.asciinema-theme-solarized-light .fg-5 {\n  color: #d33682;\n}\n.asciinema-theme-solarized-light .bg-5 {\n  background-color: #d33682;\n}\n.asciinema-theme-solarized-light .fg-6 {\n  color: #2aa198;\n}\n.asciinema-theme-solarized-light .bg-6 {\n  background-color: #2aa198;\n}\n.asciinema-theme-solarized-light .fg-7 {\n  color: #eee8d5;\n}\n.asciinema-theme-solarized-light .bg-7 {\n  background-color: #eee8d5;\n}\n.asciinema-theme-solarized-light .fg-8 {\n  color: #002b36;\n}\n.asciinema-theme-solarized-light .bg-8 {\n  background-color: #002b36;\n}\n.asciinema-theme-solarized-light .fg-9 {\n  color: #cb4b16;\n}\n.asciinema-theme-solarized-light .bg-9 {\n  background-color: #cb4b16;\n}\n.asciinema-theme-solarized-light .fg-10 {\n  color: #586e75;\n}\n.asciinema-theme-solarized-light .bg-10 {\n  background-color: #586e75;\n}\n.asciinema-theme-solarized-light .fg-11 {\n  color: #657c83;\n}\n.asciinema-theme-solarized-light .bg-11 {\n  background-color: #657c83;\n}\n.asciinema-theme-solarized-light .fg-12 {\n  color: #839496;\n}\n.asciinema-theme-solarized-light .bg-12 {\n  background-color: #839496;\n}\n.asciinema-theme-solarized-light .fg-13 {\n  color: #6c71c4;\n}\n.asciinema-theme-solarized-light .bg-13 {\n  background-color: #6c71c4;\n}\n.asciinema-theme-solarized-light .fg-14 {\n  color: #93a1a1;\n}\n.asciinema-theme-solarized-light .bg-14 {\n  background-color: #93a1a1;\n}\n.asciinema-theme-solarized-light .fg-15 {\n  color: #fdf6e3;\n}\n.asciinema-theme-solarized-light .bg-15 {\n  background-color: #fdf6e3;\n}\n.asciinema-theme-seti .asciinema-terminal {\n  color: #cacecd;\n  background-color: #111213;\n  border-color: #111213;\n}\n.asciinema-theme-seti .fg-bg {\n  color: #111213;\n}\n.asciinema-theme-seti .bg-fg {\n  background-color: #cacecd;\n}\n.asciinema-theme-seti .fg-0 {\n  color: #323232;\n}\n.asciinema-theme-seti .bg-0 {\n  background-color: #323232;\n}\n.asciinema-theme-seti .fg-1 {\n  color: #c22832;\n}\n.asciinema-theme-seti .bg-1 {\n  background-color: #c22832;\n}\n.asciinema-theme-seti .fg-2 {\n  color: #8ec43d;\n}\n.asciinema-theme-seti .bg-2 {\n  background-color: #8ec43d;\n}\n.asciinema-theme-seti .fg-3 {\n  color: #e0c64f;\n}\n.asciinema-theme-seti .bg-3 {\n  background-color: #e0c64f;\n}\n.asciinema-theme-seti .fg-4 {\n  color: #43a5d5;\n}\n.asciinema-theme-seti .bg-4 {\n  background-color: #43a5d5;\n}\n.asciinema-theme-seti .fg-5 {\n  color: #8b57b5;\n}\n.asciinema-theme-seti .bg-5 {\n  background-color: #8b57b5;\n}\n.asciinema-theme-seti .fg-6 {\n  color: #8ec43d;\n}\n.asciinema-theme-seti .bg-6 {\n  background-color: #8ec43d;\n}\n.asciinema-theme-seti .fg-7 {\n  color: #eeeeee;\n}\n.asciinema-theme-seti .bg-7 {\n  background-color: #eeeeee;\n}\n.asciinema-theme-seti .fg-8 {\n  color: #323232;\n}\n.asciinema-theme-seti .bg-8 {\n  background-color: #323232;\n}\n.asciinema-theme-seti .fg-9 {\n  color: #c22832;\n}\n.asciinema-theme-seti .bg-9 {\n  background-color: #c22832;\n}\n.asciinema-theme-seti .fg-10 {\n  color: #8ec43d;\n}\n.asciinema-theme-seti .bg-10 {\n  background-color: #8ec43d;\n}\n.asciinema-theme-seti .fg-11 {\n  color: #e0c64f;\n}\n.asciinema-theme-seti .bg-11 {\n  background-color: #e0c64f;\n}\n.asciinema-theme-seti .fg-12 {\n  color: #43a5d5;\n}\n.asciinema-theme-seti .bg-12 {\n  background-color: #43a5d5;\n}\n.asciinema-theme-seti .fg-13 {\n  color: #8b57b5;\n}\n.asciinema-theme-seti .bg-13 {\n  background-color: #8b57b5;\n}\n.asciinema-theme-seti .fg-14 {\n  color: #8ec43d;\n}\n.asciinema-theme-seti .bg-14 {\n  background-color: #8ec43d;\n}\n.asciinema-theme-seti .fg-15 {\n  color: #ffffff;\n}\n.asciinema-theme-seti .bg-15 {\n  background-color: #ffffff;\n}\n.asciinema-theme-seti .fg-8,\n.asciinema-theme-seti .fg-9,\n.asciinema-theme-seti .fg-10,\n.asciinema-theme-seti .fg-11,\n.asciinema-theme-seti .fg-12,\n.asciinema-theme-seti .fg-13,\n.asciinema-theme-seti .fg-14,\n.asciinema-theme-seti .fg-15 {\n  font-weight: bold;\n}\n/* Based on Monokai from base16 collection - https://github.com/chriskempson/base16 */\n.asciinema-theme-monokai .asciinema-terminal {\n  color: #f8f8f2;\n  background-color: #272822;\n  border-color: #272822;\n}\n.asciinema-theme-monokai .fg-bg {\n  color: #272822;\n}\n.asciinema-theme-monokai .bg-fg {\n  background-color: #f8f8f2;\n}\n.asciinema-theme-monokai .fg-0 {\n  color: #272822;\n}\n.asciinema-theme-monokai .bg-0 {\n  background-color: #272822;\n}\n.asciinema-theme-monokai .fg-1 {\n  color: #f92672;\n}\n.asciinema-theme-monokai .bg-1 {\n  background-color: #f92672;\n}\n.asciinema-theme-monokai .fg-2 {\n  color: #a6e22e;\n}\n.asciinema-theme-monokai .bg-2 {\n  background-color: #a6e22e;\n}\n.asciinema-theme-monokai .fg-3 {\n  color: #f4bf75;\n}\n.asciinema-theme-monokai .bg-3 {\n  background-color: #f4bf75;\n}\n.asciinema-theme-monokai .fg-4 {\n  color: #66d9ef;\n}\n.asciinema-theme-monokai .bg-4 {\n  background-color: #66d9ef;\n}\n.asciinema-theme-monokai .fg-5 {\n  color: #ae81ff;\n}\n.asciinema-theme-monokai .bg-5 {\n  background-color: #ae81ff;\n}\n.asciinema-theme-monokai .fg-6 {\n  color: #a1efe4;\n}\n.asciinema-theme-monokai .bg-6 {\n  background-color: #a1efe4;\n}\n.asciinema-theme-monokai .fg-7 {\n  color: #f8f8f2;\n}\n.asciinema-theme-monokai .bg-7 {\n  background-color: #f8f8f2;\n}\n.asciinema-theme-monokai .fg-8 {\n  color: #75715e;\n}\n.asciinema-theme-monokai .bg-8 {\n  background-color: #75715e;\n}\n.asciinema-theme-monokai .fg-9 {\n  color: #f92672;\n}\n.asciinema-theme-monokai .bg-9 {\n  background-color: #f92672;\n}\n.asciinema-theme-monokai .fg-10 {\n  color: #a6e22e;\n}\n.asciinema-theme-monokai .bg-10 {\n  background-color: #a6e22e;\n}\n.asciinema-theme-monokai .fg-11 {\n  color: #f4bf75;\n}\n.asciinema-theme-monokai .bg-11 {\n  background-color: #f4bf75;\n}\n.asciinema-theme-monokai .fg-12 {\n  color: #66d9ef;\n}\n.asciinema-theme-monokai .bg-12 {\n  background-color: #66d9ef;\n}\n.asciinema-theme-monokai .fg-13 {\n  color: #ae81ff;\n}\n.asciinema-theme-monokai .bg-13 {\n  background-color: #ae81ff;\n}\n.asciinema-theme-monokai .fg-14 {\n  color: #a1efe4;\n}\n.asciinema-theme-monokai .bg-14 {\n  background-color: #a1efe4;\n}\n.asciinema-theme-monokai .fg-15 {\n  color: #f9f8f5;\n}\n.asciinema-theme-monokai .bg-15 {\n  background-color: #f9f8f5;\n}\n.asciinema-theme-monokai .fg-8,\n.asciinema-theme-monokai .fg-9,\n.asciinema-theme-monokai .fg-10,\n.asciinema-theme-monokai .fg-11,\n.asciinema-theme-monokai .fg-12,\n.asciinema-theme-monokai .fg-13,\n.asciinema-theme-monokai .fg-14,\n.asciinema-theme-monokai .fg-15 {\n  font-weight: bold;\n}\n"
  },
  {
    "path": "docs/source/theme/smithy/static/asciinema-player.js",
    "content": "/**\n * asciinema-player v2.6.1\n *\n * Copyright 2011-2018, Marcin Kulik\n *\n */\n\n// CustomEvent polyfill from MDN (https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent)\n\n(function () {\n  if (typeof window.CustomEvent === \"function\") return false;\n\n  function CustomEvent ( event, params ) {\n    params = params || { bubbles: false, cancelable: false, detail: undefined };\n    var evt = document.createEvent( 'CustomEvent');\n    evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);\n    return evt;\n  }\n\n  CustomEvent.prototype = window.Event.prototype;\n\n  window.CustomEvent = CustomEvent;\n})();\n\n/**\n * @license\n * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n */\n// @version 0.7.22\n\"undefined\"==typeof WeakMap&&!function(){var e=Object.defineProperty,t=Date.now()%1e9,n=function(){this.name=\"__st\"+(1e9*Math.random()>>>0)+(t++ +\"__\")};n.prototype={set:function(t,n){var o=t[this.name];return o&&o[0]===t?o[1]=n:e(t,this.name,{value:[t,n],writable:!0}),this},get:function(e){var t;return(t=e[this.name])&&t[0]===e?t[1]:void 0},\"delete\":function(e){var t=e[this.name];return t&&t[0]===e?(t[0]=t[1]=void 0,!0):!1},has:function(e){var t=e[this.name];return t?t[0]===e:!1}},window.WeakMap=n}(),function(e){function t(e){E.push(e),b||(b=!0,w(o))}function n(e){return window.ShadowDOMPolyfill&&window.ShadowDOMPolyfill.wrapIfNeeded(e)||e}function o(){b=!1;var e=E;E=[],e.sort(function(e,t){return e.uid_-t.uid_});var t=!1;e.forEach(function(e){var n=e.takeRecords();r(e),n.length&&(e.callback_(n,e),t=!0)}),t&&o()}function r(e){e.nodes_.forEach(function(t){var n=v.get(t);n&&n.forEach(function(t){t.observer===e&&t.removeTransientObservers()})})}function i(e,t){for(var n=e;n;n=n.parentNode){var o=v.get(n);if(o)for(var r=0;r<o.length;r++){var i=o[r],a=i.options;if(n===e||a.subtree){var d=t(a);d&&i.enqueue(d)}}}}function a(e){this.callback_=e,this.nodes_=[],this.records_=[],this.uid_=++_}function d(e,t){this.type=e,this.target=t,this.addedNodes=[],this.removedNodes=[],this.previousSibling=null,this.nextSibling=null,this.attributeName=null,this.attributeNamespace=null,this.oldValue=null}function s(e){var t=new d(e.type,e.target);return t.addedNodes=e.addedNodes.slice(),t.removedNodes=e.removedNodes.slice(),t.previousSibling=e.previousSibling,t.nextSibling=e.nextSibling,t.attributeName=e.attributeName,t.attributeNamespace=e.attributeNamespace,t.oldValue=e.oldValue,t}function u(e,t){return y=new d(e,t)}function c(e){return N?N:(N=s(y),N.oldValue=e,N)}function l(){y=N=void 0}function f(e){return e===N||e===y}function p(e,t){return e===t?e:N&&f(e)?N:null}function m(e,t,n){this.observer=e,this.target=t,this.options=n,this.transientObservedNodes=[]}if(!e.JsMutationObserver){var w,v=new WeakMap;if(/Trident|Edge/.test(navigator.userAgent))w=setTimeout;else if(window.setImmediate)w=window.setImmediate;else{var h=[],g=String(Math.random());window.addEventListener(\"message\",function(e){if(e.data===g){var t=h;h=[],t.forEach(function(e){e()})}}),w=function(e){h.push(e),window.postMessage(g,\"*\")}}var b=!1,E=[],_=0;a.prototype={observe:function(e,t){if(e=n(e),!t.childList&&!t.attributes&&!t.characterData||t.attributeOldValue&&!t.attributes||t.attributeFilter&&t.attributeFilter.length&&!t.attributes||t.characterDataOldValue&&!t.characterData)throw new SyntaxError;var o=v.get(e);o||v.set(e,o=[]);for(var r,i=0;i<o.length;i++)if(o[i].observer===this){r=o[i],r.removeListeners(),r.options=t;break}r||(r=new m(this,e,t),o.push(r),this.nodes_.push(e)),r.addListeners()},disconnect:function(){this.nodes_.forEach(function(e){for(var t=v.get(e),n=0;n<t.length;n++){var o=t[n];if(o.observer===this){o.removeListeners(),t.splice(n,1);break}}},this),this.records_=[]},takeRecords:function(){var e=this.records_;return this.records_=[],e}};var y,N;m.prototype={enqueue:function(e){var n=this.observer.records_,o=n.length;if(n.length>0){var r=n[o-1],i=p(r,e);if(i)return void(n[o-1]=i)}else t(this.observer);n[o]=e},addListeners:function(){this.addListeners_(this.target)},addListeners_:function(e){var t=this.options;t.attributes&&e.addEventListener(\"DOMAttrModified\",this,!0),t.characterData&&e.addEventListener(\"DOMCharacterDataModified\",this,!0),t.childList&&e.addEventListener(\"DOMNodeInserted\",this,!0),(t.childList||t.subtree)&&e.addEventListener(\"DOMNodeRemoved\",this,!0)},removeListeners:function(){this.removeListeners_(this.target)},removeListeners_:function(e){var t=this.options;t.attributes&&e.removeEventListener(\"DOMAttrModified\",this,!0),t.characterData&&e.removeEventListener(\"DOMCharacterDataModified\",this,!0),t.childList&&e.removeEventListener(\"DOMNodeInserted\",this,!0),(t.childList||t.subtree)&&e.removeEventListener(\"DOMNodeRemoved\",this,!0)},addTransientObserver:function(e){if(e!==this.target){this.addListeners_(e),this.transientObservedNodes.push(e);var t=v.get(e);t||v.set(e,t=[]),t.push(this)}},removeTransientObservers:function(){var e=this.transientObservedNodes;this.transientObservedNodes=[],e.forEach(function(e){this.removeListeners_(e);for(var t=v.get(e),n=0;n<t.length;n++)if(t[n]===this){t.splice(n,1);break}},this)},handleEvent:function(e){switch(e.stopImmediatePropagation(),e.type){case\"DOMAttrModified\":var t=e.attrName,n=e.relatedNode.namespaceURI,o=e.target,r=new u(\"attributes\",o);r.attributeName=t,r.attributeNamespace=n;var a=e.attrChange===MutationEvent.ADDITION?null:e.prevValue;i(o,function(e){return!e.attributes||e.attributeFilter&&e.attributeFilter.length&&-1===e.attributeFilter.indexOf(t)&&-1===e.attributeFilter.indexOf(n)?void 0:e.attributeOldValue?c(a):r});break;case\"DOMCharacterDataModified\":var o=e.target,r=u(\"characterData\",o),a=e.prevValue;i(o,function(e){return e.characterData?e.characterDataOldValue?c(a):r:void 0});break;case\"DOMNodeRemoved\":this.addTransientObserver(e.target);case\"DOMNodeInserted\":var d,s,f=e.target;\"DOMNodeInserted\"===e.type?(d=[f],s=[]):(d=[],s=[f]);var p=f.previousSibling,m=f.nextSibling,r=u(\"childList\",e.target.parentNode);r.addedNodes=d,r.removedNodes=s,r.previousSibling=p,r.nextSibling=m,i(e.relatedNode,function(e){return e.childList?r:void 0})}l()}},e.JsMutationObserver=a,e.MutationObserver||(e.MutationObserver=a,a._isPolyfilled=!0)}}(self),function(e){\"use strict\";if(!window.performance){var t=Date.now();window.performance={now:function(){return Date.now()-t}}}window.requestAnimationFrame||(window.requestAnimationFrame=function(){var e=window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame;return e?function(t){return e(function(){t(performance.now())})}:function(e){return window.setTimeout(e,1e3/60)}}()),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(){return window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||function(e){clearTimeout(e)}}());var n=function(){var e=document.createEvent(\"Event\");return e.initEvent(\"foo\",!0,!0),e.preventDefault(),e.defaultPrevented}();if(!n){var o=Event.prototype.preventDefault;Event.prototype.preventDefault=function(){this.cancelable&&(o.call(this),Object.defineProperty(this,\"defaultPrevented\",{get:function(){return!0},configurable:!0}))}}var r=/Trident/.test(navigator.userAgent);if((!window.CustomEvent||r&&\"function\"!=typeof window.CustomEvent)&&(window.CustomEvent=function(e,t){t=t||{};var n=document.createEvent(\"CustomEvent\");return n.initCustomEvent(e,Boolean(t.bubbles),Boolean(t.cancelable),t.detail),n},window.CustomEvent.prototype=window.Event.prototype),!window.Event||r&&\"function\"!=typeof window.Event){var i=window.Event;window.Event=function(e,t){t=t||{};var n=document.createEvent(\"Event\");return n.initEvent(e,Boolean(t.bubbles),Boolean(t.cancelable)),n},window.Event.prototype=i.prototype}}(window.WebComponents),window.CustomElements=window.CustomElements||{flags:{}},function(e){var t=e.flags,n=[],o=function(e){n.push(e)},r=function(){n.forEach(function(t){t(e)})};e.addModule=o,e.initializeModules=r,e.hasNative=Boolean(document.registerElement),e.isIE=/Trident/.test(navigator.userAgent),e.useNative=!t.register&&e.hasNative&&!window.ShadowDOMPolyfill&&(!window.HTMLImports||window.HTMLImports.useNative)}(window.CustomElements),window.CustomElements.addModule(function(e){function t(e,t){n(e,function(e){return t(e)?!0:void o(e,t)}),o(e,t)}function n(e,t,o){var r=e.firstElementChild;if(!r)for(r=e.firstChild;r&&r.nodeType!==Node.ELEMENT_NODE;)r=r.nextSibling;for(;r;)t(r,o)!==!0&&n(r,t,o),r=r.nextElementSibling;return null}function o(e,n){for(var o=e.shadowRoot;o;)t(o,n),o=o.olderShadowRoot}function r(e,t){i(e,t,[])}function i(e,t,n){if(e=window.wrap(e),!(n.indexOf(e)>=0)){n.push(e);for(var o,r=e.querySelectorAll(\"link[rel=\"+a+\"]\"),d=0,s=r.length;s>d&&(o=r[d]);d++)o[\"import\"]&&i(o[\"import\"],t,n);t(e)}}var a=window.HTMLImports?window.HTMLImports.IMPORT_LINK_TYPE:\"none\";e.forDocumentTree=r,e.forSubtree=t}),window.CustomElements.addModule(function(e){function t(e,t){return n(e,t)||o(e,t)}function n(t,n){return e.upgrade(t,n)?!0:void(n&&a(t))}function o(e,t){b(e,function(e){return n(e,t)?!0:void 0})}function r(e){N.push(e),y||(y=!0,setTimeout(i))}function i(){y=!1;for(var e,t=N,n=0,o=t.length;o>n&&(e=t[n]);n++)e();N=[]}function a(e){_?r(function(){d(e)}):d(e)}function d(e){e.__upgraded__&&!e.__attached&&(e.__attached=!0,e.attachedCallback&&e.attachedCallback())}function s(e){u(e),b(e,function(e){u(e)})}function u(e){_?r(function(){c(e)}):c(e)}function c(e){e.__upgraded__&&e.__attached&&(e.__attached=!1,e.detachedCallback&&e.detachedCallback())}function l(e){for(var t=e,n=window.wrap(document);t;){if(t==n)return!0;t=t.parentNode||t.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&t.host}}function f(e){if(e.shadowRoot&&!e.shadowRoot.__watched){g.dom&&console.log(\"watching shadow-root for: \",e.localName);for(var t=e.shadowRoot;t;)w(t),t=t.olderShadowRoot}}function p(e,n){if(g.dom){var o=n[0];if(o&&\"childList\"===o.type&&o.addedNodes&&o.addedNodes){for(var r=o.addedNodes[0];r&&r!==document&&!r.host;)r=r.parentNode;var i=r&&(r.URL||r._URL||r.host&&r.host.localName)||\"\";i=i.split(\"/?\").shift().split(\"/\").pop()}console.group(\"mutations (%d) [%s]\",n.length,i||\"\")}var a=l(e);n.forEach(function(e){\"childList\"===e.type&&(M(e.addedNodes,function(e){e.localName&&t(e,a)}),M(e.removedNodes,function(e){e.localName&&s(e)}))}),g.dom&&console.groupEnd()}function m(e){for(e=window.wrap(e),e||(e=window.wrap(document));e.parentNode;)e=e.parentNode;var t=e.__observer;t&&(p(e,t.takeRecords()),i())}function w(e){if(!e.__observer){var t=new MutationObserver(p.bind(this,e));t.observe(e,{childList:!0,subtree:!0}),e.__observer=t}}function v(e){e=window.wrap(e),g.dom&&console.group(\"upgradeDocument: \",e.baseURI.split(\"/\").pop());var n=e===window.wrap(document);t(e,n),w(e),g.dom&&console.groupEnd()}function h(e){E(e,v)}var g=e.flags,b=e.forSubtree,E=e.forDocumentTree,_=window.MutationObserver._isPolyfilled&&g[\"throttle-attached\"];e.hasPolyfillMutations=_,e.hasThrottledAttached=_;var y=!1,N=[],M=Array.prototype.forEach.call.bind(Array.prototype.forEach),O=Element.prototype.createShadowRoot;O&&(Element.prototype.createShadowRoot=function(){var e=O.call(this);return window.CustomElements.watchShadow(this),e}),e.watchShadow=f,e.upgradeDocumentTree=h,e.upgradeDocument=v,e.upgradeSubtree=o,e.upgradeAll=t,e.attached=a,e.takeRecords=m}),window.CustomElements.addModule(function(e){function t(t,o){if(\"template\"===t.localName&&window.HTMLTemplateElement&&HTMLTemplateElement.decorate&&HTMLTemplateElement.decorate(t),!t.__upgraded__&&t.nodeType===Node.ELEMENT_NODE){var r=t.getAttribute(\"is\"),i=e.getRegisteredDefinition(t.localName)||e.getRegisteredDefinition(r);if(i&&(r&&i.tag==t.localName||!r&&!i[\"extends\"]))return n(t,i,o)}}function n(t,n,r){return a.upgrade&&console.group(\"upgrade:\",t.localName),n.is&&t.setAttribute(\"is\",n.is),o(t,n),t.__upgraded__=!0,i(t),r&&e.attached(t),e.upgradeSubtree(t,r),a.upgrade&&console.groupEnd(),t}function o(e,t){Object.__proto__?e.__proto__=t.prototype:(r(e,t.prototype,t[\"native\"]),e.__proto__=t.prototype)}function r(e,t,n){for(var o={},r=t;r!==n&&r!==HTMLElement.prototype;){for(var i,a=Object.getOwnPropertyNames(r),d=0;i=a[d];d++)o[i]||(Object.defineProperty(e,i,Object.getOwnPropertyDescriptor(r,i)),o[i]=1);r=Object.getPrototypeOf(r)}}function i(e){e.createdCallback&&e.createdCallback()}var a=e.flags;e.upgrade=t,e.upgradeWithDefinition=n,e.implementPrototype=o}),window.CustomElements.addModule(function(e){function t(t,o){var s=o||{};if(!t)throw new Error(\"document.registerElement: first argument `name` must not be empty\");if(t.indexOf(\"-\")<0)throw new Error(\"document.registerElement: first argument ('name') must contain a dash ('-'). Argument provided was '\"+String(t)+\"'.\");if(r(t))throw new Error(\"Failed to execute 'registerElement' on 'Document': Registration failed for type '\"+String(t)+\"'. The type name is invalid.\");if(u(t))throw new Error(\"DuplicateDefinitionError: a type with name '\"+String(t)+\"' is already registered\");return s.prototype||(s.prototype=Object.create(HTMLElement.prototype)),s.__name=t.toLowerCase(),s[\"extends\"]&&(s[\"extends\"]=s[\"extends\"].toLowerCase()),s.lifecycle=s.lifecycle||{},s.ancestry=i(s[\"extends\"]),a(s),d(s),n(s.prototype),c(s.__name,s),s.ctor=l(s),s.ctor.prototype=s.prototype,s.prototype.constructor=s.ctor,e.ready&&v(document),s.ctor}function n(e){if(!e.setAttribute._polyfilled){var t=e.setAttribute;e.setAttribute=function(e,n){o.call(this,e,n,t)};var n=e.removeAttribute;e.removeAttribute=function(e){o.call(this,e,null,n)},e.setAttribute._polyfilled=!0}}function o(e,t,n){e=e.toLowerCase();var o=this.getAttribute(e);n.apply(this,arguments);var r=this.getAttribute(e);this.attributeChangedCallback&&r!==o&&this.attributeChangedCallback(e,o,r)}function r(e){for(var t=0;t<_.length;t++)if(e===_[t])return!0}function i(e){var t=u(e);return t?i(t[\"extends\"]).concat([t]):[]}function a(e){for(var t,n=e[\"extends\"],o=0;t=e.ancestry[o];o++)n=t.is&&t.tag;e.tag=n||e.__name,n&&(e.is=e.__name)}function d(e){if(!Object.__proto__){var t=HTMLElement.prototype;if(e.is){var n=document.createElement(e.tag);t=Object.getPrototypeOf(n)}for(var o,r=e.prototype,i=!1;r;)r==t&&(i=!0),o=Object.getPrototypeOf(r),o&&(r.__proto__=o),r=o;i||console.warn(e.tag+\" prototype not found in prototype chain for \"+e.is),e[\"native\"]=t}}function s(e){return g(M(e.tag),e)}function u(e){return e?y[e.toLowerCase()]:void 0}function c(e,t){y[e]=t}function l(e){return function(){return s(e)}}function f(e,t,n){return e===N?p(t,n):O(e,t)}function p(e,t){e&&(e=e.toLowerCase()),t&&(t=t.toLowerCase());var n=u(t||e);if(n){if(e==n.tag&&t==n.is)return new n.ctor;if(!t&&!n.is)return new n.ctor}var o;return t?(o=p(e),o.setAttribute(\"is\",t),o):(o=M(e),e.indexOf(\"-\")>=0&&b(o,HTMLElement),o)}function m(e,t){var n=e[t];e[t]=function(){var e=n.apply(this,arguments);return h(e),e}}var w,v=(e.isIE,e.upgradeDocumentTree),h=e.upgradeAll,g=e.upgradeWithDefinition,b=e.implementPrototype,E=e.useNative,_=[\"annotation-xml\",\"color-profile\",\"font-face\",\"font-face-src\",\"font-face-uri\",\"font-face-format\",\"font-face-name\",\"missing-glyph\"],y={},N=\"http://www.w3.org/1999/xhtml\",M=document.createElement.bind(document),O=document.createElementNS.bind(document);w=Object.__proto__||E?function(e,t){return e instanceof t}:function(e,t){if(e instanceof t)return!0;for(var n=e;n;){if(n===t.prototype)return!0;n=n.__proto__}return!1},m(Node.prototype,\"cloneNode\"),m(document,\"importNode\"),document.registerElement=t,document.createElement=p,document.createElementNS=f,e.registry=y,e[\"instanceof\"]=w,e.reservedTagList=_,e.getRegisteredDefinition=u,document.register=document.registerElement}),function(e){function t(){i(window.wrap(document)),window.CustomElements.ready=!0;var e=window.requestAnimationFrame||function(e){setTimeout(e,16)};e(function(){setTimeout(function(){window.CustomElements.readyTime=Date.now(),window.HTMLImports&&(window.CustomElements.elapsed=window.CustomElements.readyTime-window.HTMLImports.readyTime),document.dispatchEvent(new CustomEvent(\"WebComponentsReady\",{bubbles:!0}))})})}var n=e.useNative,o=e.initializeModules;e.isIE;if(n){var r=function(){};e.watchShadow=r,e.upgrade=r,e.upgradeAll=r,e.upgradeDocumentTree=r,e.upgradeSubtree=r,e.takeRecords=r,e[\"instanceof\"]=function(e,t){return e instanceof t}}else o();var i=e.upgradeDocumentTree,a=e.upgradeDocument;if(window.wrap||(window.ShadowDOMPolyfill?(window.wrap=window.ShadowDOMPolyfill.wrapIfNeeded,window.unwrap=window.ShadowDOMPolyfill.unwrapIfNeeded):window.wrap=window.unwrap=function(e){return e}),window.HTMLImports&&(window.HTMLImports.__importsParsingHook=function(e){e[\"import\"]&&a(wrap(e[\"import\"]))}),\"complete\"===document.readyState||e.flags.eager)t();else if(\"interactive\"!==document.readyState||window.attachEvent||window.HTMLImports&&!window.HTMLImports.ready){var d=window.HTMLImports&&!window.HTMLImports.ready?\"HTMLImportsLoaded\":\"DOMContentLoaded\";window.addEventListener(d,t)}else t()}(window.CustomElements);\nif(typeof Math.imul == \"undefined\" || (Math.imul(0xffffffff,5) == 0)) {\n    Math.imul = function (a, b) {\n        var ah  = (a >>> 16) & 0xffff;\n        var al = a & 0xffff;\n        var bh  = (b >>> 16) & 0xffff;\n        var bl = b & 0xffff;\n        // the shift by 0 fixes the sign on the high part\n        // the final |0 converts the unsigned value into a signed value\n        return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0)|0);\n    }\n}\n\n/**\n * React v15.5.4\n *\n * Copyright 2013-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n *\n */\n!function(t){if(\"object\"==typeof exports&&\"undefined\"!=typeof module)module.exports=t();else if(\"function\"==typeof define&&define.amd)define([],t);else{var e;e=\"undefined\"!=typeof window?window:\"undefined\"!=typeof global?global:\"undefined\"!=typeof self?self:this,e.React=t()}}(function(){return function t(e,n,r){function o(u,a){if(!n[u]){if(!e[u]){var s=\"function\"==typeof require&&require;if(!a&&s)return s(u,!0);if(i)return i(u,!0);var c=new Error(\"Cannot find module '\"+u+\"'\");throw c.code=\"MODULE_NOT_FOUND\",c}var l=n[u]={exports:{}};e[u][0].call(l.exports,function(t){var n=e[u][1][t];return o(n||t)},l,l.exports,t,e,n,r)}return n[u].exports}for(var i=\"function\"==typeof require&&require,u=0;u<r.length;u++)o(r[u]);return o}({1:[function(t,e,n){\"use strict\";function r(t){var e={\"=\":\"=0\",\":\":\"=2\"};return\"$\"+(\"\"+t).replace(/[=:]/g,function(t){return e[t]})}function o(t){var e={\"=0\":\"=\",\"=2\":\":\"};return(\"\"+(\".\"===t[0]&&\"$\"===t[1]?t.substring(2):t.substring(1))).replace(/(=0|=2)/g,function(t){return e[t]})}var i={escape:r,unescape:o};e.exports=i},{}],2:[function(t,e,n){\"use strict\";var r=t(20),o=(t(24),function(t){var e=this;if(e.instancePool.length){var n=e.instancePool.pop();return e.call(n,t),n}return new e(t)}),i=function(t,e){var n=this;if(n.instancePool.length){var r=n.instancePool.pop();return n.call(r,t,e),r}return new n(t,e)},u=function(t,e,n){var r=this;if(r.instancePool.length){var o=r.instancePool.pop();return r.call(o,t,e,n),o}return new r(t,e,n)},a=function(t,e,n,r){var o=this;if(o.instancePool.length){var i=o.instancePool.pop();return o.call(i,t,e,n,r),i}return new o(t,e,n,r)},s=function(t){var e=this;t instanceof e||r(\"25\"),t.destructor(),e.instancePool.length<e.poolSize&&e.instancePool.push(t)},c=o,l=function(t,e){var n=t;return n.instancePool=[],n.getPooled=e||c,n.poolSize||(n.poolSize=10),n.release=s,n},f={addPoolingTo:l,oneArgumentPooler:o,twoArgumentPooler:i,threeArgumentPooler:u,fourArgumentPooler:a};e.exports=f},{20:20,24:24}],3:[function(t,e,n){\"use strict\";var r=t(26),o=t(4),i=t(6),u=t(14),a=t(5),s=t(8),c=t(9),l=t(13),f=t(16),p=t(19),d=(t(25),c.createElement),y=c.createFactory,h=c.cloneElement,v=r,m={Children:{map:o.map,forEach:o.forEach,count:o.count,toArray:o.toArray,only:p},Component:i,PureComponent:u,createElement:d,cloneElement:h,isValidElement:c.isValidElement,PropTypes:l,createClass:a.createClass,createFactory:y,createMixin:function(t){return t},DOM:s,version:f,__spread:v};e.exports=m},{13:13,14:14,16:16,19:19,25:25,26:26,4:4,5:5,6:6,8:8,9:9}],4:[function(t,e,n){\"use strict\";function r(t){return(\"\"+t).replace(E,\"$&/\")}function o(t,e){this.func=t,this.context=e,this.count=0}function i(t,e,n){var r=t.func,o=t.context;r.call(o,e,t.count++)}function u(t,e,n){if(null==t)return t;var r=o.getPooled(e,n);m(t,i,r),o.release(r)}function a(t,e,n,r){this.result=t,this.keyPrefix=e,this.func=n,this.context=r,this.count=0}function s(t,e,n){var o=t.result,i=t.keyPrefix,u=t.func,a=t.context,s=u.call(a,e,t.count++);Array.isArray(s)?c(s,o,n,v.thatReturnsArgument):null!=s&&(h.isValidElement(s)&&(s=h.cloneAndReplaceKey(s,i+(!s.key||e&&e.key===s.key?\"\":r(s.key)+\"/\")+n)),o.push(s))}function c(t,e,n,o,i){var u=\"\";null!=n&&(u=r(n)+\"/\");var c=a.getPooled(e,u,o,i);m(t,s,c),a.release(c)}function l(t,e,n){if(null==t)return t;var r=[];return c(t,r,null,e,n),r}function f(t,e,n){return null}function p(t,e){return m(t,f,null)}function d(t){var e=[];return c(t,e,null,v.thatReturnsArgument),e}var y=t(2),h=t(9),v=t(22),m=t(21),b=y.twoArgumentPooler,g=y.fourArgumentPooler,E=/\\/+/g;o.prototype.destructor=function(){this.func=null,this.context=null,this.count=0},y.addPoolingTo(o,b),a.prototype.destructor=function(){this.result=null,this.keyPrefix=null,this.func=null,this.context=null,this.count=0},y.addPoolingTo(a,g);var x={forEach:u,map:l,mapIntoWithKeyPrefixInternal:c,count:p,toArray:d};e.exports=x},{2:2,21:21,22:22,9:9}],5:[function(t,e,n){\"use strict\";function r(t){return t}function o(t,e){var n=E.hasOwnProperty(e)?E[e]:null;_.hasOwnProperty(e)&&\"OVERRIDE_BASE\"!==n&&p(\"73\",e),t&&\"DEFINE_MANY\"!==n&&\"DEFINE_MANY_MERGED\"!==n&&p(\"74\",e)}function i(t,e){if(e){\"function\"==typeof e&&p(\"75\"),h.isValidElement(e)&&p(\"76\");var n=t.prototype,r=n.__reactAutoBindPairs;e.hasOwnProperty(b)&&x.mixins(t,e.mixins);for(var i in e)if(e.hasOwnProperty(i)&&i!==b){var u=e[i],a=n.hasOwnProperty(i);if(o(a,i),x.hasOwnProperty(i))x[i](t,u);else{var l=E.hasOwnProperty(i),f=\"function\"==typeof u,d=f&&!l&&!a&&!1!==e.autobind;if(d)r.push(i,u),n[i]=u;else if(a){var y=E[i];(!l||\"DEFINE_MANY_MERGED\"!==y&&\"DEFINE_MANY\"!==y)&&p(\"77\",y,i),\"DEFINE_MANY_MERGED\"===y?n[i]=s(n[i],u):\"DEFINE_MANY\"===y&&(n[i]=c(n[i],u))}else n[i]=u}}}}function u(t,e){if(e)for(var n in e){var r=e[n];if(e.hasOwnProperty(n)){var o=n in x;o&&p(\"78\",n);var i=n in t;i&&p(\"79\",n),t[n]=r}}}function a(t,e){t&&e&&\"object\"==typeof t&&\"object\"==typeof e||p(\"80\");for(var n in e)e.hasOwnProperty(n)&&(void 0!==t[n]&&p(\"81\",n),t[n]=e[n]);return t}function s(t,e){return function(){var n=t.apply(this,arguments),r=e.apply(this,arguments);if(null==n)return r;if(null==r)return n;var o={};return a(o,n),a(o,r),o}}function c(t,e){return function(){t.apply(this,arguments),e.apply(this,arguments)}}function l(t,e){return e.bind(t)}function f(t){for(var e=t.__reactAutoBindPairs,n=0;n<e.length;n+=2){var r=e[n],o=e[n+1];t[r]=l(t,o)}}var p=t(20),d=t(26),y=t(6),h=t(9),v=(t(12),t(11)),m=t(23),b=(t(24),t(25),\"mixins\"),g=[],E={mixins:\"DEFINE_MANY\",statics:\"DEFINE_MANY\",propTypes:\"DEFINE_MANY\",contextTypes:\"DEFINE_MANY\",childContextTypes:\"DEFINE_MANY\",getDefaultProps:\"DEFINE_MANY_MERGED\",getInitialState:\"DEFINE_MANY_MERGED\",getChildContext:\"DEFINE_MANY_MERGED\",render:\"DEFINE_ONCE\",componentWillMount:\"DEFINE_MANY\",componentDidMount:\"DEFINE_MANY\",componentWillReceiveProps:\"DEFINE_MANY\",shouldComponentUpdate:\"DEFINE_ONCE\",componentWillUpdate:\"DEFINE_MANY\",componentDidUpdate:\"DEFINE_MANY\",componentWillUnmount:\"DEFINE_MANY\",updateComponent:\"OVERRIDE_BASE\"},x={displayName:function(t,e){t.displayName=e},mixins:function(t,e){if(e)for(var n=0;n<e.length;n++)i(t,e[n])},childContextTypes:function(t,e){t.childContextTypes=d({},t.childContextTypes,e)},contextTypes:function(t,e){t.contextTypes=d({},t.contextTypes,e)},getDefaultProps:function(t,e){t.getDefaultProps?t.getDefaultProps=s(t.getDefaultProps,e):t.getDefaultProps=e},propTypes:function(t,e){t.propTypes=d({},t.propTypes,e)},statics:function(t,e){u(t,e)},autobind:function(){}},_={replaceState:function(t,e){this.updater.enqueueReplaceState(this,t),e&&this.updater.enqueueCallback(this,e,\"replaceState\")},isMounted:function(){return this.updater.isMounted(this)}},P=function(){};d(P.prototype,y.prototype,_);var w={createClass:function(t){var e=r(function(t,n,r){this.__reactAutoBindPairs.length&&f(this),this.props=t,this.context=n,this.refs=m,this.updater=r||v,this.state=null;var o=this.getInitialState?this.getInitialState():null;(\"object\"!=typeof o||Array.isArray(o))&&p(\"82\",e.displayName||\"ReactCompositeComponent\"),this.state=o});e.prototype=new P,e.prototype.constructor=e,e.prototype.__reactAutoBindPairs=[],g.forEach(i.bind(null,e)),i(e,t),e.getDefaultProps&&(e.defaultProps=e.getDefaultProps()),e.prototype.render||p(\"83\");for(var n in E)e.prototype[n]||(e.prototype[n]=null);return e},injection:{injectMixin:function(t){g.push(t)}}};e.exports=w},{11:11,12:12,20:20,23:23,24:24,25:25,26:26,6:6,9:9}],6:[function(t,e,n){\"use strict\";function r(t,e,n){this.props=t,this.context=e,this.refs=u,this.updater=n||i}var o=t(20),i=t(11),u=(t(17),t(23));t(24),t(25);r.prototype.isReactComponent={},r.prototype.setState=function(t,e){\"object\"!=typeof t&&\"function\"!=typeof t&&null!=t&&o(\"85\"),this.updater.enqueueSetState(this,t),e&&this.updater.enqueueCallback(this,e,\"setState\")},r.prototype.forceUpdate=function(t){this.updater.enqueueForceUpdate(this),t&&this.updater.enqueueCallback(this,t,\"forceUpdate\")};e.exports=r},{11:11,17:17,20:20,23:23,24:24,25:25}],7:[function(t,e,n){\"use strict\";var r={current:null};e.exports=r},{}],8:[function(t,e,n){\"use strict\";var r=t(9),o=r.createFactory,i={a:o(\"a\"),abbr:o(\"abbr\"),address:o(\"address\"),area:o(\"area\"),article:o(\"article\"),aside:o(\"aside\"),audio:o(\"audio\"),b:o(\"b\"),base:o(\"base\"),bdi:o(\"bdi\"),bdo:o(\"bdo\"),big:o(\"big\"),blockquote:o(\"blockquote\"),body:o(\"body\"),br:o(\"br\"),button:o(\"button\"),canvas:o(\"canvas\"),caption:o(\"caption\"),cite:o(\"cite\"),code:o(\"code\"),col:o(\"col\"),colgroup:o(\"colgroup\"),data:o(\"data\"),datalist:o(\"datalist\"),dd:o(\"dd\"),del:o(\"del\"),details:o(\"details\"),dfn:o(\"dfn\"),dialog:o(\"dialog\"),div:o(\"div\"),dl:o(\"dl\"),dt:o(\"dt\"),em:o(\"em\"),embed:o(\"embed\"),fieldset:o(\"fieldset\"),figcaption:o(\"figcaption\"),figure:o(\"figure\"),footer:o(\"footer\"),form:o(\"form\"),h1:o(\"h1\"),h2:o(\"h2\"),h3:o(\"h3\"),h4:o(\"h4\"),h5:o(\"h5\"),h6:o(\"h6\"),head:o(\"head\"),header:o(\"header\"),hgroup:o(\"hgroup\"),hr:o(\"hr\"),html:o(\"html\"),i:o(\"i\"),iframe:o(\"iframe\"),img:o(\"img\"),input:o(\"input\"),ins:o(\"ins\"),kbd:o(\"kbd\"),keygen:o(\"keygen\"),label:o(\"label\"),legend:o(\"legend\"),li:o(\"li\"),link:o(\"link\"),main:o(\"main\"),map:o(\"map\"),mark:o(\"mark\"),menu:o(\"menu\"),menuitem:o(\"menuitem\"),meta:o(\"meta\"),meter:o(\"meter\"),nav:o(\"nav\"),noscript:o(\"noscript\"),object:o(\"object\"),ol:o(\"ol\"),optgroup:o(\"optgroup\"),option:o(\"option\"),output:o(\"output\"),p:o(\"p\"),param:o(\"param\"),picture:o(\"picture\"),pre:o(\"pre\"),progress:o(\"progress\"),q:o(\"q\"),rp:o(\"rp\"),rt:o(\"rt\"),ruby:o(\"ruby\"),s:o(\"s\"),samp:o(\"samp\"),script:o(\"script\"),section:o(\"section\"),select:o(\"select\"),small:o(\"small\"),source:o(\"source\"),span:o(\"span\"),strong:o(\"strong\"),style:o(\"style\"),sub:o(\"sub\"),summary:o(\"summary\"),sup:o(\"sup\"),table:o(\"table\"),tbody:o(\"tbody\"),td:o(\"td\"),textarea:o(\"textarea\"),tfoot:o(\"tfoot\"),th:o(\"th\"),thead:o(\"thead\"),time:o(\"time\"),title:o(\"title\"),tr:o(\"tr\"),track:o(\"track\"),u:o(\"u\"),ul:o(\"ul\"),var:o(\"var\"),video:o(\"video\"),wbr:o(\"wbr\"),circle:o(\"circle\"),clipPath:o(\"clipPath\"),defs:o(\"defs\"),ellipse:o(\"ellipse\"),g:o(\"g\"),image:o(\"image\"),line:o(\"line\"),linearGradient:o(\"linearGradient\"),mask:o(\"mask\"),path:o(\"path\"),pattern:o(\"pattern\"),polygon:o(\"polygon\"),polyline:o(\"polyline\"),radialGradient:o(\"radialGradient\"),rect:o(\"rect\"),stop:o(\"stop\"),svg:o(\"svg\"),text:o(\"text\"),tspan:o(\"tspan\")};e.exports=i},{9:9}],9:[function(t,e,n){\"use strict\";function r(t){return void 0!==t.ref}function o(t){return void 0!==t.key}var i=t(26),u=t(7),a=(t(25),t(17),Object.prototype.hasOwnProperty),s=t(10),c={key:!0,ref:!0,__self:!0,__source:!0},l=function(t,e,n,r,o,i,u){return{$$typeof:s,type:t,key:e,ref:n,props:u,_owner:i}};l.createElement=function(t,e,n){var i,s={},f=null,p=null;if(null!=e){r(e)&&(p=e.ref),o(e)&&(f=\"\"+e.key),void 0===e.__self?null:e.__self,void 0===e.__source?null:e.__source;for(i in e)a.call(e,i)&&!c.hasOwnProperty(i)&&(s[i]=e[i])}var d=arguments.length-2;if(1===d)s.children=n;else if(d>1){for(var y=Array(d),h=0;h<d;h++)y[h]=arguments[h+2];s.children=y}if(t&&t.defaultProps){var v=t.defaultProps;for(i in v)void 0===s[i]&&(s[i]=v[i])}return l(t,f,p,0,0,u.current,s)},l.createFactory=function(t){var e=l.createElement.bind(null,t);return e.type=t,e},l.cloneAndReplaceKey=function(t,e){return l(t.type,e,t.ref,t._self,t._source,t._owner,t.props)},l.cloneElement=function(t,e,n){var s,f=i({},t.props),p=t.key,d=t.ref,y=(t._self,t._source,t._owner);if(null!=e){r(e)&&(d=e.ref,y=u.current),o(e)&&(p=\"\"+e.key);var h;t.type&&t.type.defaultProps&&(h=t.type.defaultProps);for(s in e)a.call(e,s)&&!c.hasOwnProperty(s)&&(void 0===e[s]&&void 0!==h?f[s]=h[s]:f[s]=e[s])}var v=arguments.length-2;if(1===v)f.children=n;else if(v>1){for(var m=Array(v),b=0;b<v;b++)m[b]=arguments[b+2];f.children=m}return l(t.type,p,d,0,0,y,f)},l.isValidElement=function(t){return\"object\"==typeof t&&null!==t&&t.$$typeof===s},e.exports=l},{10:10,17:17,25:25,26:26,7:7}],10:[function(t,e,n){\"use strict\";var r=\"function\"==typeof Symbol&&Symbol.for&&Symbol.for(\"react.element\")||60103;e.exports=r},{}],11:[function(t,e,n){\"use strict\";var r=(t(25),{isMounted:function(t){return!1},enqueueCallback:function(t,e){},enqueueForceUpdate:function(t){},enqueueReplaceState:function(t,e){},enqueueSetState:function(t,e){}});e.exports=r},{25:25}],12:[function(t,e,n){\"use strict\";var r={};e.exports=r},{}],13:[function(t,e,n){\"use strict\";var r=t(9),o=r.isValidElement,i=t(28);e.exports=i(o)},{28:28,9:9}],14:[function(t,e,n){\"use strict\";function r(t,e,n){this.props=t,this.context=e,this.refs=s,this.updater=n||a}function o(){}var i=t(26),u=t(6),a=t(11),s=t(23);o.prototype=u.prototype,r.prototype=new o,r.prototype.constructor=r,i(r.prototype,u.prototype),r.prototype.isPureReactComponent=!0,e.exports=r},{11:11,23:23,26:26,6:6}],15:[function(t,e,n){\"use strict\";var r=t(26),o=t(3),i=r(o,{__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:{ReactCurrentOwner:t(7)}});e.exports=i},{26:26,3:3,7:7}],16:[function(t,e,n){\"use strict\";e.exports=\"15.5.4\"},{}],17:[function(t,e,n){\"use strict\";e.exports=!1},{}],18:[function(t,e,n){\"use strict\";function r(t){var e=t&&(o&&t[o]||t[i]);if(\"function\"==typeof e)return e}var o=\"function\"==typeof Symbol&&Symbol.iterator,i=\"@@iterator\";e.exports=r},{}],19:[function(t,e,n){\"use strict\";function r(t){return i.isValidElement(t)||o(\"143\"),t}var o=t(20),i=t(9);t(24);e.exports=r},{20:20,24:24,9:9}],20:[function(t,e,n){\"use strict\";function r(t){for(var e=arguments.length-1,n=\"Minified React error #\"+t+\"; visit http://facebook.github.io/react/docs/error-decoder.html?invariant=\"+t,r=0;r<e;r++)n+=\"&args[]=\"+encodeURIComponent(arguments[r+1]);n+=\" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.\";var o=new Error(n);throw o.name=\"Invariant Violation\",o.framesToPop=1,o}e.exports=r},{}],21:[function(t,e,n){\"use strict\";function r(t,e){return t&&\"object\"==typeof t&&null!=t.key?c.escape(t.key):e.toString(36)}function o(t,e,n,i){var p=typeof t;if(\"undefined\"!==p&&\"boolean\"!==p||(t=null),null===t||\"string\"===p||\"number\"===p||\"object\"===p&&t.$$typeof===a)return n(i,t,\"\"===e?l+r(t,0):e),1;var d,y,h=0,v=\"\"===e?l:e+f;if(Array.isArray(t))for(var m=0;m<t.length;m++)d=t[m],y=v+r(d,m),h+=o(d,y,n,i);else{var b=s(t);if(b){var g,E=b.call(t);if(b!==t.entries)for(var x=0;!(g=E.next()).done;)d=g.value,y=v+r(d,x++),h+=o(d,y,n,i);else for(;!(g=E.next()).done;){var _=g.value;_&&(d=_[1],y=v+c.escape(_[0])+f+r(d,0),h+=o(d,y,n,i))}}else if(\"object\"===p){var P=String(t);u(\"31\",\"[object Object]\"===P?\"object with keys {\"+Object.keys(t).join(\", \")+\"}\":P,\"\")}}return h}function i(t,e,n){return null==t?0:o(t,\"\",e,n)}var u=t(20),a=(t(7),t(10)),s=t(18),c=(t(24),t(1)),l=(t(25),\".\"),f=\":\";e.exports=i},{1:1,10:10,18:18,20:20,24:24,25:25,7:7}],22:[function(t,e,n){\"use strict\";function r(t){return function(){return t}}var o=function(){};o.thatReturns=r,o.thatReturnsFalse=r(!1),o.thatReturnsTrue=r(!0),o.thatReturnsNull=r(null),o.thatReturnsThis=function(){return this},o.thatReturnsArgument=function(t){return t},e.exports=o},{}],23:[function(t,e,n){\"use strict\";var r={};e.exports=r},{}],24:[function(t,e,n){\"use strict\";function r(t,e,n,r,i,u,a,s){if(o(e),!t){var c;if(void 0===e)c=new Error(\"Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.\");else{var l=[n,r,i,u,a,s],f=0;c=new Error(e.replace(/%s/g,function(){return l[f++]})),c.name=\"Invariant Violation\"}throw c.framesToPop=1,c}}var o=function(t){};e.exports=r},{}],25:[function(t,e,n){\"use strict\";var r=t(22),o=r;e.exports=o},{22:22}],26:[function(t,e,n){\"use strict\";function r(t){if(null===t||void 0===t)throw new TypeError(\"Object.assign cannot be called with null or undefined\");return Object(t)}var o=Object.getOwnPropertySymbols,i=Object.prototype.hasOwnProperty,u=Object.prototype.propertyIsEnumerable;e.exports=function(){try{if(!Object.assign)return!1;var t=new String(\"abc\");if(t[5]=\"de\",\"5\"===Object.getOwnPropertyNames(t)[0])return!1;for(var e={},n=0;n<10;n++)e[\"_\"+String.fromCharCode(n)]=n;if(\"0123456789\"!==Object.getOwnPropertyNames(e).map(function(t){return e[t]}).join(\"\"))return!1;var r={};return\"abcdefghijklmnopqrst\".split(\"\").forEach(function(t){r[t]=t}),\"abcdefghijklmnopqrst\"===Object.keys(Object.assign({},r)).join(\"\")}catch(t){return!1}}()?Object.assign:function(t,e){for(var n,a,s=r(t),c=1;c<arguments.length;c++){n=Object(arguments[c]);for(var l in n)i.call(n,l)&&(s[l]=n[l]);if(o){a=o(n);for(var f=0;f<a.length;f++)u.call(n,a[f])&&(s[a[f]]=n[a[f]])}}return s}},{}],27:[function(t,e,n){\"use strict\";function r(t,e,n,r,o){}e.exports=r},{24:24,25:25,30:30}],28:[function(t,e,n){\"use strict\";var r=t(29);e.exports=function(t){return r(t,!1)}},{29:29}],29:[function(t,e,n){\"use strict\";var r=t(22),o=t(24),i=(t(25),t(30)),u=t(27);e.exports=function(t,e){function n(t){var e=t&&(_&&t[_]||t[P]);if(\"function\"==typeof e)return e}function a(t,e){return t===e?0!==t||1/t==1/e:t!==t&&e!==e}function s(t){this.message=t,this.stack=\"\"}function c(t){function n(n,r,u,a,c,l,f){if(a=a||w,l=l||u,f!==i)if(e)o(!1,\"Calling PropTypes validators directly is not supported by the `prop-types` package. Use `PropTypes.checkPropTypes()` to call them. Read more at http://fb.me/use-check-prop-types\");else;return null==r[u]?n?new s(null===r[u]?\"The \"+c+\" `\"+l+\"` is marked as required in `\"+a+\"`, but its value is `null`.\":\"The \"+c+\" `\"+l+\"` is marked as required in `\"+a+\"`, but its value is `undefined`.\"):null:t(r,u,a,c,l)}var r=n.bind(null,!1);return r.isRequired=n.bind(null,!0),r}function l(t){function e(e,n,r,o,i,u){var a=e[n];if(g(a)!==t)return new s(\"Invalid \"+o+\" `\"+i+\"` of type `\"+E(a)+\"` supplied to `\"+r+\"`, expected `\"+t+\"`.\");return null}return c(e)}function f(t){function e(e,n,r,o,u){if(\"function\"!=typeof t)return new s(\"Property `\"+u+\"` of component `\"+r+\"` has invalid PropType notation inside arrayOf.\");var a=e[n];if(!Array.isArray(a)){return new s(\"Invalid \"+o+\" `\"+u+\"` of type `\"+g(a)+\"` supplied to `\"+r+\"`, expected an array.\")}for(var c=0;c<a.length;c++){var l=t(a,c,r,o,u+\"[\"+c+\"]\",i);if(l instanceof Error)return l}return null}return c(e)}function p(t){function e(e,n,r,o,i){if(!(e[n]instanceof t)){var u=t.name||w;return new s(\"Invalid \"+o+\" `\"+i+\"` of type `\"+x(e[n])+\"` supplied to `\"+r+\"`, expected instance of `\"+u+\"`.\")}return null}return c(e)}function d(t){function e(e,n,r,o,i){for(var u=e[n],c=0;c<t.length;c++)if(a(u,t[c]))return null;return new s(\"Invalid \"+o+\" `\"+i+\"` of value `\"+u+\"` supplied to `\"+r+\"`, expected one of \"+JSON.stringify(t)+\".\")}return Array.isArray(t)?c(e):r.thatReturnsNull}function y(t){function e(e,n,r,o,u){if(\"function\"!=typeof t)return new s(\"Property `\"+u+\"` of component `\"+r+\"` has invalid PropType notation inside objectOf.\");var a=e[n],c=g(a);if(\"object\"!==c)return new s(\"Invalid \"+o+\" `\"+u+\"` of type `\"+c+\"` supplied to `\"+r+\"`, expected an object.\");for(var l in a)if(a.hasOwnProperty(l)){var f=t(a,l,r,o,u+\".\"+l,i);if(f instanceof Error)return f}return null}return c(e)}function h(t){function e(e,n,r,o,u){for(var a=0;a<t.length;a++){if(null==(0,t[a])(e,n,r,o,u,i))return null}return new s(\"Invalid \"+o+\" `\"+u+\"` supplied to `\"+r+\"`.\")}return Array.isArray(t)?c(e):r.thatReturnsNull}function v(t){function e(e,n,r,o,u){var a=e[n],c=g(a);if(\"object\"!==c)return new s(\"Invalid \"+o+\" `\"+u+\"` of type `\"+c+\"` supplied to `\"+r+\"`, expected `object`.\");for(var l in t){var f=t[l];if(f){var p=f(a,l,r,o,u+\".\"+l,i);if(p)return p}}return null}return c(e)}function m(e){switch(typeof e){case\"number\":case\"string\":case\"undefined\":return!0;case\"boolean\":return!e;case\"object\":if(Array.isArray(e))return e.every(m);if(null===e||t(e))return!0;var r=n(e);if(!r)return!1;var o,i=r.call(e);if(r!==e.entries){for(;!(o=i.next()).done;)if(!m(o.value))return!1}else for(;!(o=i.next()).done;){var u=o.value;if(u&&!m(u[1]))return!1}return!0;default:return!1}}function b(t,e){return\"symbol\"===t||(\"Symbol\"===e[\"@@toStringTag\"]||\"function\"==typeof Symbol&&e instanceof Symbol)}function g(t){var e=typeof t;return Array.isArray(t)?\"array\":t instanceof RegExp?\"object\":b(e,t)?\"symbol\":e}function E(t){var e=g(t);if(\"object\"===e){if(t instanceof Date)return\"date\";if(t instanceof RegExp)return\"regexp\"}return e}function x(t){return t.constructor&&t.constructor.name?t.constructor.name:w}var _=\"function\"==typeof Symbol&&Symbol.iterator,P=\"@@iterator\",w=\"<<anonymous>>\",N={array:l(\"array\"),bool:l(\"boolean\"),func:l(\"function\"),number:l(\"number\"),object:l(\"object\"),string:l(\"string\"),symbol:l(\"symbol\"),any:function(){return c(r.thatReturnsNull)}(),arrayOf:f,element:function(){function e(e,n,r,o,i){var u=e[n];if(!t(u)){return new s(\"Invalid \"+o+\" `\"+i+\"` of type `\"+g(u)+\"` supplied to `\"+r+\"`, expected a single ReactElement.\")}return null}return c(e)}(),instanceOf:p,node:function(){function t(t,e,n,r,o){return m(t[e])?null:new s(\"Invalid \"+r+\" `\"+o+\"` supplied to `\"+n+\"`, expected a ReactNode.\")}return c(t)}(),objectOf:y,oneOf:d,oneOfType:h,shape:v};return s.prototype=Error.prototype,N.checkPropTypes=u,N.PropTypes=N,N}},{22:22,24:24,25:25,27:27,30:30}],30:[function(t,e,n){\"use strict\";e.exports=\"SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED\"},{}]},{},[15])(15)});\n!function(f){if(\"object\"==typeof exports&&\"undefined\"!=typeof module)module.exports=f();else if(\"function\"==typeof define&&define.amd)define([],f);else{var g;if(g=\"undefined\"!=typeof window?window:\"undefined\"!=typeof global?global:\"undefined\"!=typeof self?self:this,void 0===g.React)throw Error(\"React module should be required before createClass\");g.createReactClass=f()}}(function(){return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=\"function\"==typeof require&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error(\"Cannot find module '\"+o+\"'\");throw f.code=\"MODULE_NOT_FOUND\",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n||e)},l,l.exports,e,t,n,r)}return n[o].exports}for(var i=\"function\"==typeof require&&require,o=0;o<r.length;o++)s(r[o]);return s}({1:[function(require,module,exports){\"use strict\";function identity(fn){return fn}function factory(ReactComponent,isValidElement,ReactNoopUpdateQueue){function validateMethodOverride(isAlreadyDefined,name){var specPolicy=ReactClassInterface.hasOwnProperty(name)?ReactClassInterface[name]:null;ReactClassMixin.hasOwnProperty(name)&&_invariant(\"OVERRIDE_BASE\"===specPolicy,\"ReactClassInterface: You are attempting to override `%s` from your class specification. Ensure that your method names do not overlap with React methods.\",name),isAlreadyDefined&&_invariant(\"DEFINE_MANY\"===specPolicy||\"DEFINE_MANY_MERGED\"===specPolicy,\"ReactClassInterface: You are attempting to define `%s` on your component more than once. This conflict may be due to a mixin.\",name)}function mixSpecIntoComponent(Constructor,spec){if(spec){_invariant(\"function\"!=typeof spec,\"ReactClass: You're attempting to use a component class or function as a mixin. Instead, just use a regular object.\"),_invariant(!isValidElement(spec),\"ReactClass: You're attempting to use a component as a mixin. Instead, just use a regular object.\");var proto=Constructor.prototype,autoBindPairs=proto.__reactAutoBindPairs;spec.hasOwnProperty(MIXINS_KEY)&&RESERVED_SPEC_KEYS.mixins(Constructor,spec.mixins);for(var name in spec)if(spec.hasOwnProperty(name)&&name!==MIXINS_KEY){var property=spec[name],isAlreadyDefined=proto.hasOwnProperty(name);if(validateMethodOverride(isAlreadyDefined,name),RESERVED_SPEC_KEYS.hasOwnProperty(name))RESERVED_SPEC_KEYS[name](Constructor,property);else{var isReactClassMethod=ReactClassInterface.hasOwnProperty(name),isFunction=\"function\"==typeof property,shouldAutoBind=isFunction&&!isReactClassMethod&&!isAlreadyDefined&&!1!==spec.autobind;if(shouldAutoBind)autoBindPairs.push(name,property),proto[name]=property;else if(isAlreadyDefined){var specPolicy=ReactClassInterface[name];_invariant(isReactClassMethod&&(\"DEFINE_MANY_MERGED\"===specPolicy||\"DEFINE_MANY\"===specPolicy),\"ReactClass: Unexpected spec policy %s for key %s when mixing in component specs.\",specPolicy,name),\"DEFINE_MANY_MERGED\"===specPolicy?proto[name]=createMergedResultFunction(proto[name],property):\"DEFINE_MANY\"===specPolicy&&(proto[name]=createChainedFunction(proto[name],property))}else proto[name]=property}}}else;}function mixStaticSpecIntoComponent(Constructor,statics){if(statics)for(var name in statics){var property=statics[name];if(statics.hasOwnProperty(name)){var isReserved=name in RESERVED_SPEC_KEYS;_invariant(!isReserved,'ReactClass: You are attempting to define a reserved property, `%s`, that shouldn\\'t be on the \"statics\" key. Define it as an instance property instead; it will still be accessible on the constructor.',name);var isInherited=name in Constructor;_invariant(!isInherited,\"ReactClass: You are attempting to define `%s` on your component more than once. This conflict may be due to a mixin.\",name),Constructor[name]=property}}}function mergeIntoWithNoDuplicateKeys(one,two){_invariant(one&&two&&\"object\"==typeof one&&\"object\"==typeof two,\"mergeIntoWithNoDuplicateKeys(): Cannot merge non-objects.\");for(var key in two)two.hasOwnProperty(key)&&(_invariant(void 0===one[key],\"mergeIntoWithNoDuplicateKeys(): Tried to merge two objects with the same key: `%s`. This conflict may be due to a mixin; in particular, this may be caused by two getInitialState() or getDefaultProps() methods returning objects with clashing keys.\",key),one[key]=two[key]);return one}function createMergedResultFunction(one,two){return function(){var a=one.apply(this,arguments),b=two.apply(this,arguments);if(null==a)return b;if(null==b)return a;var c={};return mergeIntoWithNoDuplicateKeys(c,a),mergeIntoWithNoDuplicateKeys(c,b),c}}function createChainedFunction(one,two){return function(){one.apply(this,arguments),two.apply(this,arguments)}}function bindAutoBindMethod(component,method){var boundMethod=method.bind(component);return boundMethod}function bindAutoBindMethods(component){for(var pairs=component.__reactAutoBindPairs,i=0;i<pairs.length;i+=2){var autoBindKey=pairs[i],method=pairs[i+1];component[autoBindKey]=bindAutoBindMethod(component,method)}}function createClass(spec){var Constructor=identity(function(props,context,updater){this.__reactAutoBindPairs.length&&bindAutoBindMethods(this),this.props=props,this.context=context,this.refs=emptyObject,this.updater=updater||ReactNoopUpdateQueue,this.state=null;var initialState=this.getInitialState?this.getInitialState():null;_invariant(\"object\"==typeof initialState&&!Array.isArray(initialState),\"%s.getInitialState(): must return an object or null\",Constructor.displayName||\"ReactCompositeComponent\"),this.state=initialState});Constructor.prototype=new ReactClassComponent,Constructor.prototype.constructor=Constructor,Constructor.prototype.__reactAutoBindPairs=[],injectedMixins.forEach(mixSpecIntoComponent.bind(null,Constructor)),mixSpecIntoComponent(Constructor,IsMountedMixin),mixSpecIntoComponent(Constructor,spec),Constructor.getDefaultProps&&(Constructor.defaultProps=Constructor.getDefaultProps()),_invariant(Constructor.prototype.render,\"createClass(...): Class specification must implement a `render` method.\");for(var methodName in ReactClassInterface)Constructor.prototype[methodName]||(Constructor.prototype[methodName]=null);return Constructor}var injectedMixins=[],ReactClassInterface={mixins:\"DEFINE_MANY\",statics:\"DEFINE_MANY\",propTypes:\"DEFINE_MANY\",contextTypes:\"DEFINE_MANY\",childContextTypes:\"DEFINE_MANY\",getDefaultProps:\"DEFINE_MANY_MERGED\",getInitialState:\"DEFINE_MANY_MERGED\",getChildContext:\"DEFINE_MANY_MERGED\",render:\"DEFINE_ONCE\",componentWillMount:\"DEFINE_MANY\",componentDidMount:\"DEFINE_MANY\",componentWillReceiveProps:\"DEFINE_MANY\",shouldComponentUpdate:\"DEFINE_ONCE\",componentWillUpdate:\"DEFINE_MANY\",componentDidUpdate:\"DEFINE_MANY\",componentWillUnmount:\"DEFINE_MANY\",updateComponent:\"OVERRIDE_BASE\"},RESERVED_SPEC_KEYS={displayName:function(Constructor,displayName){Constructor.displayName=displayName},mixins:function(Constructor,mixins){if(mixins)for(var i=0;i<mixins.length;i++)mixSpecIntoComponent(Constructor,mixins[i])},childContextTypes:function(Constructor,childContextTypes){Constructor.childContextTypes=_assign({},Constructor.childContextTypes,childContextTypes)},contextTypes:function(Constructor,contextTypes){Constructor.contextTypes=_assign({},Constructor.contextTypes,contextTypes)},getDefaultProps:function(Constructor,getDefaultProps){Constructor.getDefaultProps?Constructor.getDefaultProps=createMergedResultFunction(Constructor.getDefaultProps,getDefaultProps):Constructor.getDefaultProps=getDefaultProps},propTypes:function(Constructor,propTypes){Constructor.propTypes=_assign({},Constructor.propTypes,propTypes)},statics:function(Constructor,statics){mixStaticSpecIntoComponent(Constructor,statics)},autobind:function(){}},IsMountedMixin={componentDidMount:function(){this.__isMounted=!0},componentWillUnmount:function(){this.__isMounted=!1}},ReactClassMixin={replaceState:function(newState,callback){this.updater.enqueueReplaceState(this,newState,callback)},isMounted:function(){return!!this.__isMounted}},ReactClassComponent=function(){};return _assign(ReactClassComponent.prototype,ReactComponent.prototype,ReactClassMixin),createClass}var _assign=require(7),emptyObject=require(4),_invariant=require(5),MIXINS_KEY=\"mixins\";module.exports=factory},{4:4,5:5,6:6,7:7}],2:[function(require,module,exports){\"use strict\";var factory=require(1),ReactNoopUpdateQueue=(new React.Component).updater;module.exports=factory(React.Component,React.isValidElement,ReactNoopUpdateQueue)},{1:1}],3:[function(require,module,exports){\"use strict\";function makeEmptyFunction(arg){return function(){return arg}}var emptyFunction=function(){};emptyFunction.thatReturns=makeEmptyFunction,emptyFunction.thatReturnsFalse=makeEmptyFunction(!1),emptyFunction.thatReturnsTrue=makeEmptyFunction(!0),emptyFunction.thatReturnsNull=makeEmptyFunction(null),emptyFunction.thatReturnsThis=function(){return this},emptyFunction.thatReturnsArgument=function(arg){return arg},module.exports=emptyFunction},{}],4:[function(require,module,exports){\"use strict\";var emptyObject={};module.exports=emptyObject},{}],5:[function(require,module,exports){\"use strict\";function invariant(condition,format,a,b,c,d,e,f){if(validateFormat(format),!condition){var error;if(void 0===format)error=new Error(\"Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.\");else{var args=[a,b,c,d,e,f],argIndex=0;error=new Error(format.replace(/%s/g,function(){return args[argIndex++]})),error.name=\"Invariant Violation\"}throw error.framesToPop=1,error}}var validateFormat=function(format){};module.exports=invariant},{}],6:[function(require,module,exports){\"use strict\";var emptyFunction=require(3),warning=emptyFunction;module.exports=warning},{3:3}],7:[function(require,module,exports){\"use strict\";function toObject(val){if(null===val||void 0===val)throw new TypeError(\"Object.assign cannot be called with null or undefined\");return Object(val)}var getOwnPropertySymbols=Object.getOwnPropertySymbols,hasOwnProperty=Object.prototype.hasOwnProperty,propIsEnumerable=Object.prototype.propertyIsEnumerable;module.exports=function(){try{if(!Object.assign)return!1;var test1=new String(\"abc\");if(test1[5]=\"de\",\"5\"===Object.getOwnPropertyNames(test1)[0])return!1;for(var test2={},i=0;i<10;i++)test2[\"_\"+String.fromCharCode(i)]=i;if(\"0123456789\"!==Object.getOwnPropertyNames(test2).map(function(n){return test2[n]}).join(\"\"))return!1;var test3={};return\"abcdefghijklmnopqrst\".split(\"\").forEach(function(letter){test3[letter]=letter}),\"abcdefghijklmnopqrst\"===Object.keys(Object.assign({},test3)).join(\"\")}catch(err){return!1}}()?Object.assign:function(target,source){for(var from,symbols,to=toObject(target),s=1;s<arguments.length;s++){from=Object(arguments[s]);for(var key in from)hasOwnProperty.call(from,key)&&(to[key]=from[key]);if(getOwnPropertySymbols){symbols=getOwnPropertySymbols(from);for(var i=0;i<symbols.length;i++)propIsEnumerable.call(from,symbols[i])&&(to[symbols[i]]=from[symbols[i]])}}return to}},{}]},{},[2])(2)});\n\n/**\n * ReactDOM v15.5.4\n *\n * Copyright 2013-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n *\n */\n!function(e){if(\"object\"==typeof exports&&\"undefined\"!=typeof module)module.exports=e(require(\"react\"));else if(\"function\"==typeof define&&define.amd)define([\"react\"],e);else{var t;t=\"undefined\"!=typeof window?window:\"undefined\"!=typeof global?global:\"undefined\"!=typeof self?self:this,t.ReactDOM=e(t.React)}}(function(e){return function(t){return function(){return function e(t,n,r){function o(a,s){if(!n[a]){if(!t[a]){var u=\"function\"==typeof require&&require;if(!s&&u)return u(a,!0);if(i)return i(a,!0);var l=new Error(\"Cannot find module '\"+a+\"'\");throw l.code=\"MODULE_NOT_FOUND\",l}var c=n[a]={exports:{}};t[a][0].call(c.exports,function(e){var n=t[a][1][e];return o(n||e)},c,c.exports,e,t,n,r)}return n[a].exports}for(var i=\"function\"==typeof require&&require,a=0;a<r.length;a++)o(r[a]);return o}({1:[function(e,t,n){\"use strict\";var r={Properties:{\"aria-current\":0,\"aria-details\":0,\"aria-disabled\":0,\"aria-hidden\":0,\"aria-invalid\":0,\"aria-keyshortcuts\":0,\"aria-label\":0,\"aria-roledescription\":0,\"aria-autocomplete\":0,\"aria-checked\":0,\"aria-expanded\":0,\"aria-haspopup\":0,\"aria-level\":0,\"aria-modal\":0,\"aria-multiline\":0,\"aria-multiselectable\":0,\"aria-orientation\":0,\"aria-placeholder\":0,\"aria-pressed\":0,\"aria-readonly\":0,\"aria-required\":0,\"aria-selected\":0,\"aria-sort\":0,\"aria-valuemax\":0,\"aria-valuemin\":0,\"aria-valuenow\":0,\"aria-valuetext\":0,\"aria-atomic\":0,\"aria-busy\":0,\"aria-live\":0,\"aria-relevant\":0,\"aria-dropeffect\":0,\"aria-grabbed\":0,\"aria-activedescendant\":0,\"aria-colcount\":0,\"aria-colindex\":0,\"aria-colspan\":0,\"aria-controls\":0,\"aria-describedby\":0,\"aria-errormessage\":0,\"aria-flowto\":0,\"aria-labelledby\":0,\"aria-owns\":0,\"aria-posinset\":0,\"aria-rowcount\":0,\"aria-rowindex\":0,\"aria-rowspan\":0,\"aria-setsize\":0},DOMAttributeNames:{},DOMPropertyNames:{}};t.exports=r},{}],2:[function(e,t,n){\"use strict\";var r=e(33),o=e(131),i={focusDOMComponent:function(){o(r.getNodeFromInstance(this))}};t.exports=i},{131:131,33:33}],3:[function(e,t,n){\"use strict\";function r(e){return(e.ctrlKey||e.altKey||e.metaKey)&&!(e.ctrlKey&&e.altKey)}function o(e){switch(e){case\"topCompositionStart\":return T.compositionStart;case\"topCompositionEnd\":return T.compositionEnd;case\"topCompositionUpdate\":return T.compositionUpdate}}function i(e,t){return\"topKeyDown\"===e&&t.keyCode===y}function a(e,t){switch(e){case\"topKeyUp\":return-1!==g.indexOf(t.keyCode);case\"topKeyDown\":return t.keyCode!==y;case\"topKeyPress\":case\"topMouseDown\":case\"topBlur\":return!0;default:return!1}}function s(e){var t=e.detail;return\"object\"==typeof t&&\"data\"in t?t.data:null}function u(e,t,n,r){var u,l;if(_?u=o(e):P?a(e,n)&&(u=T.compositionEnd):i(e,n)&&(u=T.compositionStart),!u)return null;E&&(P||u!==T.compositionStart?u===T.compositionEnd&&P&&(l=P.getData()):P=h.getPooled(r));var c=m.getPooled(u,t,n,r);if(l)c.data=l;else{var p=s(n);null!==p&&(c.data=p)}return d.accumulateTwoPhaseDispatches(c),c}function l(e,t){switch(e){case\"topCompositionEnd\":return s(t);case\"topKeyPress\":return t.which!==x?null:(k=!0,w);case\"topTextInput\":var n=t.data;return n===w&&k?null:n;default:return null}}function c(e,t){if(P){if(\"topCompositionEnd\"===e||!_&&a(e,t)){var n=P.getData();return h.release(P),P=null,n}return null}switch(e){case\"topPaste\":return null;case\"topKeyPress\":return t.which&&!r(t)?String.fromCharCode(t.which):null;case\"topCompositionEnd\":return E?null:t.data;default:return null}}function p(e,t,n,r){var o;if(!(o=b?l(e,n):c(e,n)))return null;var i=v.getPooled(T.beforeInput,t,n,r);return i.data=o,d.accumulateTwoPhaseDispatches(i),i}var d=e(19),f=e(123),h=e(20),m=e(78),v=e(82),g=[9,13,27,32],y=229,_=f.canUseDOM&&\"CompositionEvent\"in window,C=null;f.canUseDOM&&\"documentMode\"in document&&(C=document.documentMode);var b=f.canUseDOM&&\"TextEvent\"in window&&!C&&!function(){var e=window.opera;return\"object\"==typeof e&&\"function\"==typeof e.version&&parseInt(e.version(),10)<=12}(),E=f.canUseDOM&&(!_||C&&C>8&&C<=11),x=32,w=String.fromCharCode(x),T={beforeInput:{phasedRegistrationNames:{bubbled:\"onBeforeInput\",captured:\"onBeforeInputCapture\"},dependencies:[\"topCompositionEnd\",\"topKeyPress\",\"topTextInput\",\"topPaste\"]},compositionEnd:{phasedRegistrationNames:{bubbled:\"onCompositionEnd\",captured:\"onCompositionEndCapture\"},dependencies:[\"topBlur\",\"topCompositionEnd\",\"topKeyDown\",\"topKeyPress\",\"topKeyUp\",\"topMouseDown\"]},compositionStart:{phasedRegistrationNames:{bubbled:\"onCompositionStart\",captured:\"onCompositionStartCapture\"},dependencies:[\"topBlur\",\"topCompositionStart\",\"topKeyDown\",\"topKeyPress\",\"topKeyUp\",\"topMouseDown\"]},compositionUpdate:{phasedRegistrationNames:{bubbled:\"onCompositionUpdate\",captured:\"onCompositionUpdateCapture\"},dependencies:[\"topBlur\",\"topCompositionUpdate\",\"topKeyDown\",\"topKeyPress\",\"topKeyUp\",\"topMouseDown\"]}},k=!1,P=null,S={eventTypes:T,extractEvents:function(e,t,n,r){return[u(e,t,n,r),p(e,t,n,r)]}};t.exports=S},{123:123,19:19,20:20,78:78,82:82}],4:[function(e,t,n){\"use strict\";function r(e,t){return e+t.charAt(0).toUpperCase()+t.substring(1)}var o={animationIterationCount:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridRow:!0,gridColumn:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},i=[\"Webkit\",\"ms\",\"Moz\",\"O\"];Object.keys(o).forEach(function(e){i.forEach(function(t){o[r(t,e)]=o[e]})});var a={background:{backgroundAttachment:!0,backgroundColor:!0,backgroundImage:!0,backgroundPositionX:!0,backgroundPositionY:!0,backgroundRepeat:!0},backgroundPosition:{backgroundPositionX:!0,backgroundPositionY:!0},border:{borderWidth:!0,borderStyle:!0,borderColor:!0},borderBottom:{borderBottomWidth:!0,borderBottomStyle:!0,borderBottomColor:!0},borderLeft:{borderLeftWidth:!0,borderLeftStyle:!0,borderLeftColor:!0},borderRight:{borderRightWidth:!0,borderRightStyle:!0,borderRightColor:!0},borderTop:{borderTopWidth:!0,borderTopStyle:!0,borderTopColor:!0},font:{fontStyle:!0,fontVariant:!0,fontWeight:!0,fontSize:!0,lineHeight:!0,fontFamily:!0},outline:{outlineWidth:!0,outlineStyle:!0,outlineColor:!0}},s={isUnitlessNumber:o,shorthandPropertyExpansions:a};t.exports=s},{}],5:[function(e,t,n){\"use strict\";var r=e(4),o=e(123),i=(e(58),e(125),e(94)),a=e(136),s=e(140),u=(e(142),s(function(e){return a(e)})),l=!1,c=\"cssFloat\";if(o.canUseDOM){var p=document.createElement(\"div\").style;try{p.font=\"\"}catch(e){l=!0}void 0===document.documentElement.style.cssFloat&&(c=\"styleFloat\")}var d={createMarkupForStyles:function(e,t){var n=\"\";for(var r in e)if(e.hasOwnProperty(r)){var o=e[r];null!=o&&(n+=u(r)+\":\",n+=i(r,o,t)+\";\")}return n||null},setValueForStyles:function(e,t,n){var o=e.style;for(var a in t)if(t.hasOwnProperty(a)){var s=i(a,t[a],n);if(\"float\"!==a&&\"cssFloat\"!==a||(a=c),s)o[a]=s;else{var u=l&&r.shorthandPropertyExpansions[a];if(u)for(var p in u)o[p]=\"\";else o[a]=\"\"}}}};t.exports=d},{123:123,125:125,136:136,140:140,142:142,4:4,58:58,94:94}],6:[function(e,t,n){\"use strict\";function r(e,t){if(!(e instanceof t))throw new TypeError(\"Cannot call a class as a function\")}var o=e(112),i=e(24),a=(e(137),function(){function e(t){r(this,e),this._callbacks=null,this._contexts=null,this._arg=t}return e.prototype.enqueue=function(e,t){this._callbacks=this._callbacks||[],this._callbacks.push(e),this._contexts=this._contexts||[],this._contexts.push(t)},e.prototype.notifyAll=function(){var e=this._callbacks,t=this._contexts,n=this._arg;if(e&&t){e.length!==t.length&&o(\"24\"),this._callbacks=null,this._contexts=null;for(var r=0;r<e.length;r++)e[r].call(t[r],n);e.length=0,t.length=0}},e.prototype.checkpoint=function(){return this._callbacks?this._callbacks.length:0},e.prototype.rollback=function(e){this._callbacks&&this._contexts&&(this._callbacks.length=e,this._contexts.length=e)},e.prototype.reset=function(){this._callbacks=null,this._contexts=null},e.prototype.destructor=function(){this.reset()},e}());t.exports=i.addPoolingTo(a)},{112:112,137:137,24:24}],7:[function(e,t,n){\"use strict\";function r(e){var t=e.nodeName&&e.nodeName.toLowerCase();return\"select\"===t||\"input\"===t&&\"file\"===e.type}function o(e){var t=w.getPooled(S.change,M,e,T(e));C.accumulateTwoPhaseDispatches(t),x.batchedUpdates(i,t)}function i(e){_.enqueueEvents(e),_.processEventQueue(!1)}function a(e,t){N=e,M=t,N.attachEvent(\"onchange\",o)}function s(){N&&(N.detachEvent(\"onchange\",o),N=null,M=null)}function u(e,t){if(\"topChange\"===e)return t}function l(e,t,n){\"topFocus\"===e?(s(),a(t,n)):\"topBlur\"===e&&s()}function c(e,t){N=e,M=t,I=e.value,O=Object.getOwnPropertyDescriptor(e.constructor.prototype,\"value\"),Object.defineProperty(N,\"value\",D),N.attachEvent?N.attachEvent(\"onpropertychange\",d):N.addEventListener(\"propertychange\",d,!1)}function p(){N&&(delete N.value,N.detachEvent?N.detachEvent(\"onpropertychange\",d):N.removeEventListener(\"propertychange\",d,!1),N=null,M=null,I=null,O=null)}function d(e){if(\"value\"===e.propertyName){var t=e.srcElement.value;t!==I&&(I=t,o(e))}}function f(e,t){if(\"topInput\"===e)return t}function h(e,t,n){\"topFocus\"===e?(p(),c(t,n)):\"topBlur\"===e&&p()}function m(e,t){if((\"topSelectionChange\"===e||\"topKeyUp\"===e||\"topKeyDown\"===e)&&N&&N.value!==I)return I=N.value,M}function v(e){return e.nodeName&&\"input\"===e.nodeName.toLowerCase()&&(\"checkbox\"===e.type||\"radio\"===e.type)}function g(e,t){if(\"topClick\"===e)return t}function y(e,t){if(null!=e){var n=e._wrapperState||t._wrapperState;if(n&&n.controlled&&\"number\"===t.type){var r=\"\"+t.value;t.getAttribute(\"value\")!==r&&t.setAttribute(\"value\",r)}}}var _=e(16),C=e(19),b=e(123),E=e(33),x=e(71),w=e(80),T=e(102),k=e(109),P=e(110),S={change:{phasedRegistrationNames:{bubbled:\"onChange\",captured:\"onChangeCapture\"},dependencies:[\"topBlur\",\"topChange\",\"topClick\",\"topFocus\",\"topInput\",\"topKeyDown\",\"topKeyUp\",\"topSelectionChange\"]}},N=null,M=null,I=null,O=null,R=!1;b.canUseDOM&&(R=k(\"change\")&&(!document.documentMode||document.documentMode>8));var A=!1;b.canUseDOM&&(A=k(\"input\")&&(!document.documentMode||document.documentMode>11));var D={get:function(){return O.get.call(this)},set:function(e){I=\"\"+e,O.set.call(this,e)}},L={eventTypes:S,extractEvents:function(e,t,n,o){var i,a,s=t?E.getNodeFromInstance(t):window;if(r(s)?R?i=u:a=l:P(s)?A?i=f:(i=m,a=h):v(s)&&(i=g),i){var c=i(e,t);if(c){var p=w.getPooled(S.change,c,n,o);return p.type=\"change\",C.accumulateTwoPhaseDispatches(p),p}}a&&a(e,s,t),\"topBlur\"===e&&y(t,s)}};t.exports=L},{102:102,109:109,110:110,123:123,16:16,19:19,33:33,71:71,80:80}],8:[function(e,t,n){\"use strict\";function r(e,t){return Array.isArray(t)&&(t=t[1]),t?t.nextSibling:e.firstChild}function o(e,t,n){c.insertTreeBefore(e,t,n)}function i(e,t,n){Array.isArray(t)?s(e,t[0],t[1],n):m(e,t,n)}function a(e,t){if(Array.isArray(t)){var n=t[1];t=t[0],u(e,t,n),e.removeChild(n)}e.removeChild(t)}function s(e,t,n,r){for(var o=t;;){var i=o.nextSibling;if(m(e,o,r),o===n)break;o=i}}function u(e,t,n){for(;;){var r=t.nextSibling;if(r===n)break;e.removeChild(r)}}function l(e,t,n){var r=e.parentNode,o=e.nextSibling;o===t?n&&m(r,document.createTextNode(n),o):n?(h(o,n),u(r,o,t)):u(r,e,t)}var c=e(9),p=e(13),d=(e(33),e(58),e(93)),f=e(114),h=e(115),m=d(function(e,t,n){e.insertBefore(t,n)}),v=p.dangerouslyReplaceNodeWithMarkup,g={dangerouslyReplaceNodeWithMarkup:v,replaceDelimitedText:l,processUpdates:function(e,t){for(var n=0;n<t.length;n++){var s=t[n];switch(s.type){case\"INSERT_MARKUP\":o(e,s.content,r(e,s.afterNode));break;case\"MOVE_EXISTING\":i(e,s.fromNode,r(e,s.afterNode));break;case\"SET_MARKUP\":f(e,s.content);break;case\"TEXT_CONTENT\":h(e,s.content);break;case\"REMOVE_NODE\":a(e,s.fromNode)}}}};t.exports=g},{114:114,115:115,13:13,33:33,58:58,9:9,93:93}],9:[function(e,t,n){\"use strict\";function r(e){if(h){var t=e.node,n=e.children;if(n.length)for(var r=0;r<n.length;r++)m(t,n[r],null);else null!=e.html?p(t,e.html):null!=e.text&&f(t,e.text)}}function o(e,t){e.parentNode.replaceChild(t.node,e),r(t)}function i(e,t){h?e.children.push(t):e.node.appendChild(t.node)}function a(e,t){h?e.html=t:p(e.node,t)}function s(e,t){h?e.text=t:f(e.node,t)}function u(){return this.node.nodeName}function l(e){return{node:e,children:[],html:null,text:null,toString:u}}var c=e(10),p=e(114),d=e(93),f=e(115),h=\"undefined\"!=typeof document&&\"number\"==typeof document.documentMode||\"undefined\"!=typeof navigator&&\"string\"==typeof navigator.userAgent&&/\\bEdge\\/\\d/.test(navigator.userAgent),m=d(function(e,t,n){11===t.node.nodeType||1===t.node.nodeType&&\"object\"===t.node.nodeName.toLowerCase()&&(null==t.node.namespaceURI||t.node.namespaceURI===c.html)?(r(t),e.insertBefore(t.node,n)):(e.insertBefore(t.node,n),r(t))});l.insertTreeBefore=m,l.replaceChildWithTree=o,l.queueChild=i,l.queueHTML=a,l.queueText=s,t.exports=l},{10:10,114:114,115:115,93:93}],10:[function(e,t,n){\"use strict\";var r={html:\"http://www.w3.org/1999/xhtml\",mathml:\"http://www.w3.org/1998/Math/MathML\",svg:\"http://www.w3.org/2000/svg\"};t.exports=r},{}],11:[function(e,t,n){\"use strict\";function r(e,t){return(e&t)===t}var o=e(112),i=(e(137),{MUST_USE_PROPERTY:1,HAS_BOOLEAN_VALUE:4,HAS_NUMERIC_VALUE:8,HAS_POSITIVE_NUMERIC_VALUE:24,HAS_OVERLOADED_BOOLEAN_VALUE:32,injectDOMPropertyConfig:function(e){var t=i,n=e.Properties||{},a=e.DOMAttributeNamespaces||{},u=e.DOMAttributeNames||{},l=e.DOMPropertyNames||{},c=e.DOMMutationMethods||{};e.isCustomAttribute&&s._isCustomAttributeFunctions.push(e.isCustomAttribute);for(var p in n){s.properties.hasOwnProperty(p)&&o(\"48\",p);var d=p.toLowerCase(),f=n[p],h={attributeName:d,attributeNamespace:null,propertyName:p,mutationMethod:null,mustUseProperty:r(f,t.MUST_USE_PROPERTY),hasBooleanValue:r(f,t.HAS_BOOLEAN_VALUE),hasNumericValue:r(f,t.HAS_NUMERIC_VALUE),hasPositiveNumericValue:r(f,t.HAS_POSITIVE_NUMERIC_VALUE),hasOverloadedBooleanValue:r(f,t.HAS_OVERLOADED_BOOLEAN_VALUE)};if(h.hasBooleanValue+h.hasNumericValue+h.hasOverloadedBooleanValue<=1||o(\"50\",p),u.hasOwnProperty(p)){var m=u[p];h.attributeName=m}a.hasOwnProperty(p)&&(h.attributeNamespace=a[p]),l.hasOwnProperty(p)&&(h.propertyName=l[p]),c.hasOwnProperty(p)&&(h.mutationMethod=c[p]),s.properties[p]=h}}}),a=\":A-Z_a-z\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u02FF\\\\u0370-\\\\u037D\\\\u037F-\\\\u1FFF\\\\u200C-\\\\u200D\\\\u2070-\\\\u218F\\\\u2C00-\\\\u2FEF\\\\u3001-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFFD\",s={ID_ATTRIBUTE_NAME:\"data-reactid\",ROOT_ATTRIBUTE_NAME:\"data-reactroot\",ATTRIBUTE_NAME_START_CHAR:a,ATTRIBUTE_NAME_CHAR:a+\"\\\\-.0-9\\\\u00B7\\\\u0300-\\\\u036F\\\\u203F-\\\\u2040\",properties:{},getPossibleStandardName:null,_isCustomAttributeFunctions:[],isCustomAttribute:function(e){for(var t=0;t<s._isCustomAttributeFunctions.length;t++)if((0,s._isCustomAttributeFunctions[t])(e))return!0;return!1},injection:i};t.exports=s},{112:112,137:137}],12:[function(e,t,n){\"use strict\";function r(e){return!!l.hasOwnProperty(e)||!u.hasOwnProperty(e)&&(s.test(e)?(l[e]=!0,!0):(u[e]=!0,!1))}function o(e,t){return null==t||e.hasBooleanValue&&!t||e.hasNumericValue&&isNaN(t)||e.hasPositiveNumericValue&&t<1||e.hasOverloadedBooleanValue&&!1===t}var i=e(11),a=(e(33),e(58),e(111)),s=(e(142),new RegExp(\"^[\"+i.ATTRIBUTE_NAME_START_CHAR+\"][\"+i.ATTRIBUTE_NAME_CHAR+\"]*$\")),u={},l={},c={createMarkupForID:function(e){return i.ID_ATTRIBUTE_NAME+\"=\"+a(e)},setAttributeForID:function(e,t){e.setAttribute(i.ID_ATTRIBUTE_NAME,t)},createMarkupForRoot:function(){return i.ROOT_ATTRIBUTE_NAME+'=\"\"'},setAttributeForRoot:function(e){e.setAttribute(i.ROOT_ATTRIBUTE_NAME,\"\")},createMarkupForProperty:function(e,t){var n=i.properties.hasOwnProperty(e)?i.properties[e]:null;if(n){if(o(n,t))return\"\";var r=n.attributeName;return n.hasBooleanValue||n.hasOverloadedBooleanValue&&!0===t?r+'=\"\"':r+\"=\"+a(t)}return i.isCustomAttribute(e)?null==t?\"\":e+\"=\"+a(t):null},createMarkupForCustomAttribute:function(e,t){return r(e)&&null!=t?e+\"=\"+a(t):\"\"},setValueForProperty:function(e,t,n){var r=i.properties.hasOwnProperty(t)?i.properties[t]:null;if(r){var a=r.mutationMethod;if(a)a(e,n);else{if(o(r,n))return void this.deleteValueForProperty(e,t);if(r.mustUseProperty)e[r.propertyName]=n;else{var s=r.attributeName,u=r.attributeNamespace;u?e.setAttributeNS(u,s,\"\"+n):r.hasBooleanValue||r.hasOverloadedBooleanValue&&!0===n?e.setAttribute(s,\"\"):e.setAttribute(s,\"\"+n)}}}else if(i.isCustomAttribute(t))return void c.setValueForAttribute(e,t,n)},setValueForAttribute:function(e,t,n){r(t)&&(null==n?e.removeAttribute(t):e.setAttribute(t,\"\"+n))},deleteValueForAttribute:function(e,t){e.removeAttribute(t)},deleteValueForProperty:function(e,t){var n=i.properties.hasOwnProperty(t)?i.properties[t]:null;if(n){var r=n.mutationMethod;if(r)r(e,void 0);else if(n.mustUseProperty){var o=n.propertyName;n.hasBooleanValue?e[o]=!1:e[o]=\"\"}else e.removeAttribute(n.attributeName)}else i.isCustomAttribute(t)&&e.removeAttribute(t)}};t.exports=c},{11:11,111:111,142:142,33:33,58:58}],13:[function(e,t,n){\"use strict\";var r=e(112),o=e(9),i=e(123),a=e(128),s=e(129),u=(e(137),{dangerouslyReplaceNodeWithMarkup:function(e,t){if(i.canUseDOM||r(\"56\"),t||r(\"57\"),\"HTML\"===e.nodeName&&r(\"58\"),\"string\"==typeof t){var n=a(t,s)[0];e.parentNode.replaceChild(n,e)}else o.replaceChildWithTree(e,t)}});t.exports=u},{112:112,123:123,128:128,129:129,137:137,9:9}],14:[function(e,t,n){\"use strict\";var r=[\"ResponderEventPlugin\",\"SimpleEventPlugin\",\"TapEventPlugin\",\"EnterLeaveEventPlugin\",\"ChangeEventPlugin\",\"SelectEventPlugin\",\"BeforeInputEventPlugin\"];t.exports=r},{}],15:[function(e,t,n){\"use strict\";var r=e(19),o=e(33),i=e(84),a={mouseEnter:{registrationName:\"onMouseEnter\",dependencies:[\"topMouseOut\",\"topMouseOver\"]},mouseLeave:{registrationName:\"onMouseLeave\",dependencies:[\"topMouseOut\",\"topMouseOver\"]}},s={eventTypes:a,extractEvents:function(e,t,n,s){if(\"topMouseOver\"===e&&(n.relatedTarget||n.fromElement))return null;if(\"topMouseOut\"!==e&&\"topMouseOver\"!==e)return null;var u;if(s.window===s)u=s;else{var l=s.ownerDocument;u=l?l.defaultView||l.parentWindow:window}var c,p;if(\"topMouseOut\"===e){c=t;var d=n.relatedTarget||n.toElement;p=d?o.getClosestInstanceFromNode(d):null}else c=null,p=t;if(c===p)return null;var f=null==c?u:o.getNodeFromInstance(c),h=null==p?u:o.getNodeFromInstance(p),m=i.getPooled(a.mouseLeave,c,n,s);m.type=\"mouseleave\",m.target=f,m.relatedTarget=h;var v=i.getPooled(a.mouseEnter,p,n,s);return v.type=\"mouseenter\",v.target=h,v.relatedTarget=f,r.accumulateEnterLeaveDispatches(m,v,c,p),[m,v]}};t.exports=s},{19:19,33:33,84:84}],16:[function(e,t,n){\"use strict\";function r(e){return\"button\"===e||\"input\"===e||\"select\"===e||\"textarea\"===e}function o(e,t,n){switch(e){case\"onClick\":case\"onClickCapture\":case\"onDoubleClick\":case\"onDoubleClickCapture\":case\"onMouseDown\":case\"onMouseDownCapture\":case\"onMouseMove\":case\"onMouseMoveCapture\":case\"onMouseUp\":case\"onMouseUpCapture\":return!(!n.disabled||!r(t));default:return!1}}var i=e(112),a=e(17),s=e(18),u=e(50),l=e(91),c=e(98),p=(e(137),{}),d=null,f=function(e,t){e&&(s.executeDispatchesInOrder(e,t),e.isPersistent()||e.constructor.release(e))},h=function(e){return f(e,!0)},m=function(e){return f(e,!1)},v=function(e){return\".\"+e._rootNodeID},g={injection:{injectEventPluginOrder:a.injectEventPluginOrder,injectEventPluginsByName:a.injectEventPluginsByName},putListener:function(e,t,n){\"function\"!=typeof n&&i(\"94\",t,typeof n);var r=v(e);(p[t]||(p[t]={}))[r]=n;var o=a.registrationNameModules[t];o&&o.didPutListener&&o.didPutListener(e,t,n)},getListener:function(e,t){var n=p[t];if(o(t,e._currentElement.type,e._currentElement.props))return null;var r=v(e);return n&&n[r]},deleteListener:function(e,t){var n=a.registrationNameModules[t];n&&n.willDeleteListener&&n.willDeleteListener(e,t);var r=p[t];r&&delete r[v(e)]},deleteAllListeners:function(e){var t=v(e);for(var n in p)if(p.hasOwnProperty(n)&&p[n][t]){var r=a.registrationNameModules[n];r&&r.willDeleteListener&&r.willDeleteListener(e,n),delete p[n][t]}},extractEvents:function(e,t,n,r){for(var o,i=a.plugins,s=0;s<i.length;s++){var u=i[s];if(u){var c=u.extractEvents(e,t,n,r);c&&(o=l(o,c))}}return o},enqueueEvents:function(e){e&&(d=l(d,e))},processEventQueue:function(e){var t=d;d=null,e?c(t,h):c(t,m),d&&i(\"95\"),u.rethrowCaughtError()},__purge:function(){p={}},__getListenerBank:function(){return p}};t.exports=g},{112:112,137:137,17:17,18:18,50:50,91:91,98:98}],17:[function(e,t,n){\"use strict\";function r(){if(s)for(var e in u){var t=u[e],n=s.indexOf(e);if(n>-1||a(\"96\",e),!l.plugins[n]){t.extractEvents||a(\"97\",e),l.plugins[n]=t;var r=t.eventTypes;for(var i in r)o(r[i],t,i)||a(\"98\",i,e)}}}function o(e,t,n){l.eventNameDispatchConfigs.hasOwnProperty(n)&&a(\"99\",n),l.eventNameDispatchConfigs[n]=e;var r=e.phasedRegistrationNames;if(r){for(var o in r)if(r.hasOwnProperty(o)){var s=r[o];i(s,t,n)}return!0}return!!e.registrationName&&(i(e.registrationName,t,n),!0)}function i(e,t,n){l.registrationNameModules[e]&&a(\"100\",e),l.registrationNameModules[e]=t,l.registrationNameDependencies[e]=t.eventTypes[n].dependencies}var a=e(112),s=(e(137),null),u={},l={plugins:[],eventNameDispatchConfigs:{},registrationNameModules:{},registrationNameDependencies:{},possibleRegistrationNames:null,injectEventPluginOrder:function(e){s&&a(\"101\"),s=Array.prototype.slice.call(e),r()},injectEventPluginsByName:function(e){var t=!1;for(var n in e)if(e.hasOwnProperty(n)){var o=e[n];u.hasOwnProperty(n)&&u[n]===o||(u[n]&&a(\"102\",n),u[n]=o,t=!0)}t&&r()},getPluginModuleForEvent:function(e){var t=e.dispatchConfig;if(t.registrationName)return l.registrationNameModules[t.registrationName]||null;if(void 0!==t.phasedRegistrationNames){var n=t.phasedRegistrationNames;for(var r in n)if(n.hasOwnProperty(r)){var o=l.registrationNameModules[n[r]];if(o)return o}}return null},_resetEventPlugins:function(){s=null;for(var e in u)u.hasOwnProperty(e)&&delete u[e];l.plugins.length=0;var t=l.eventNameDispatchConfigs;for(var n in t)t.hasOwnProperty(n)&&delete t[n];var r=l.registrationNameModules;for(var o in r)r.hasOwnProperty(o)&&delete r[o]}};t.exports=l},{112:112,137:137}],18:[function(e,t,n){\"use strict\";function r(e){return\"topMouseUp\"===e||\"topTouchEnd\"===e||\"topTouchCancel\"===e}function o(e){return\"topMouseMove\"===e||\"topTouchMove\"===e}function i(e){return\"topMouseDown\"===e||\"topTouchStart\"===e}function a(e,t,n,r){var o=e.type||\"unknown-event\";e.currentTarget=g.getNodeFromInstance(r),t?m.invokeGuardedCallbackWithCatch(o,n,e):m.invokeGuardedCallback(o,n,e),e.currentTarget=null}function s(e,t){var n=e._dispatchListeners,r=e._dispatchInstances;if(Array.isArray(n))for(var o=0;o<n.length&&!e.isPropagationStopped();o++)a(e,t,n[o],r[o]);else n&&a(e,t,n,r);e._dispatchListeners=null,e._dispatchInstances=null}function u(e){var t=e._dispatchListeners,n=e._dispatchInstances;if(Array.isArray(t)){for(var r=0;r<t.length&&!e.isPropagationStopped();r++)if(t[r](e,n[r]))return n[r]}else if(t&&t(e,n))return n;return null}function l(e){var t=u(e);return e._dispatchInstances=null,e._dispatchListeners=null,t}function c(e){var t=e._dispatchListeners,n=e._dispatchInstances;Array.isArray(t)&&h(\"103\"),e.currentTarget=t?g.getNodeFromInstance(n):null;var r=t?t(e):null;return e.currentTarget=null,e._dispatchListeners=null,e._dispatchInstances=null,r}function p(e){return!!e._dispatchListeners}var d,f,h=e(112),m=e(50),v=(e(137),e(142),{injectComponentTree:function(e){d=e},injectTreeTraversal:function(e){f=e}}),g={isEndish:r,isMoveish:o,isStartish:i,executeDirectDispatch:c,executeDispatchesInOrder:s,executeDispatchesInOrderStopAtTrue:l,hasDispatches:p,getInstanceFromNode:function(e){return d.getInstanceFromNode(e)},getNodeFromInstance:function(e){return d.getNodeFromInstance(e)},isAncestor:function(e,t){return f.isAncestor(e,t)},getLowestCommonAncestor:function(e,t){return f.getLowestCommonAncestor(e,t)},getParentInstance:function(e){return f.getParentInstance(e)},traverseTwoPhase:function(e,t,n){return f.traverseTwoPhase(e,t,n)},traverseEnterLeave:function(e,t,n,r,o){return f.traverseEnterLeave(e,t,n,r,o)},injection:v};t.exports=g},{112:112,137:137,142:142,50:50}],19:[function(e,t,n){\"use strict\";function r(e,t,n){var r=t.dispatchConfig.phasedRegistrationNames[n];return g(e,r)}function o(e,t,n){var o=r(e,n,t);o&&(n._dispatchListeners=m(n._dispatchListeners,o),n._dispatchInstances=m(n._dispatchInstances,e))}function i(e){e&&e.dispatchConfig.phasedRegistrationNames&&h.traverseTwoPhase(e._targetInst,o,e)}function a(e){if(e&&e.dispatchConfig.phasedRegistrationNames){var t=e._targetInst,n=t?h.getParentInstance(t):null;h.traverseTwoPhase(n,o,e)}}function s(e,t,n){if(n&&n.dispatchConfig.registrationName){var r=n.dispatchConfig.registrationName,o=g(e,r);o&&(n._dispatchListeners=m(n._dispatchListeners,o),n._dispatchInstances=m(n._dispatchInstances,e))}}function u(e){e&&e.dispatchConfig.registrationName&&s(e._targetInst,null,e)}function l(e){v(e,i)}function c(e){v(e,a)}function p(e,t,n,r){h.traverseEnterLeave(n,r,s,e,t)}function d(e){v(e,u)}var f=e(16),h=e(18),m=e(91),v=e(98),g=(e(142),f.getListener),y={accumulateTwoPhaseDispatches:l,accumulateTwoPhaseDispatchesSkipTarget:c,accumulateDirectDispatches:d,accumulateEnterLeaveDispatches:p};t.exports=y},{142:142,16:16,18:18,91:91,98:98}],20:[function(e,t,n){\"use strict\";function r(e){this._root=e,this._startText=this.getText(),this._fallbackText=null}var o=e(143),i=e(24),a=e(106);o(r.prototype,{destructor:function(){this._root=null,this._startText=null,this._fallbackText=null},getText:function(){return\"value\"in this._root?this._root.value:this._root[a()]},getData:function(){if(this._fallbackText)return this._fallbackText;var e,t,n=this._startText,r=n.length,o=this.getText(),i=o.length;for(e=0;e<r&&n[e]===o[e];e++);var a=r-e;for(t=1;t<=a&&n[r-t]===o[i-t];t++);var s=t>1?1-t:void 0;return this._fallbackText=o.slice(e,s),this._fallbackText}}),i.addPoolingTo(r),t.exports=r},{106:106,143:143,24:24}],21:[function(e,t,n){\"use strict\";var r=e(11),o=r.injection.MUST_USE_PROPERTY,i=r.injection.HAS_BOOLEAN_VALUE,a=r.injection.HAS_NUMERIC_VALUE,s=r.injection.HAS_POSITIVE_NUMERIC_VALUE,u=r.injection.HAS_OVERLOADED_BOOLEAN_VALUE,l={isCustomAttribute:RegExp.prototype.test.bind(new RegExp(\"^(data|aria)-[\"+r.ATTRIBUTE_NAME_CHAR+\"]*$\")),Properties:{accept:0,acceptCharset:0,accessKey:0,action:0,allowFullScreen:i,allowTransparency:0,alt:0,as:0,async:i,autoComplete:0,autoPlay:i,capture:i,cellPadding:0,cellSpacing:0,charSet:0,challenge:0,checked:o|i,cite:0,classID:0,className:0,cols:s,colSpan:0,content:0,contentEditable:0,contextMenu:0,controls:i,coords:0,crossOrigin:0,data:0,dateTime:0,default:i,defer:i,dir:0,disabled:i,download:u,draggable:0,encType:0,form:0,formAction:0,formEncType:0,formMethod:0,formNoValidate:i,formTarget:0,frameBorder:0,headers:0,height:0,hidden:i,high:0,href:0,hrefLang:0,htmlFor:0,httpEquiv:0,icon:0,id:0,inputMode:0,integrity:0,is:0,keyParams:0,keyType:0,kind:0,label:0,lang:0,list:0,loop:i,low:0,manifest:0,marginHeight:0,marginWidth:0,max:0,maxLength:0,media:0,mediaGroup:0,method:0,min:0,minLength:0,multiple:o|i,muted:o|i,name:0,nonce:0,noValidate:i,open:i,optimum:0,pattern:0,placeholder:0,playsInline:i,poster:0,preload:0,profile:0,radioGroup:0,readOnly:i,referrerPolicy:0,rel:0,required:i,reversed:i,role:0,rows:s,rowSpan:a,sandbox:0,scope:0,scoped:i,scrolling:0,seamless:i,selected:o|i,shape:0,size:s,sizes:0,span:s,spellCheck:0,src:0,srcDoc:0,srcLang:0,srcSet:0,start:a,step:0,style:0,summary:0,tabIndex:0,target:0,title:0,type:0,useMap:0,value:0,width:0,wmode:0,wrap:0,about:0,datatype:0,inlist:0,prefix:0,property:0,resource:0,typeof:0,vocab:0,autoCapitalize:0,autoCorrect:0,autoSave:0,color:0,itemProp:0,itemScope:i,itemType:0,itemID:0,itemRef:0,results:0,security:0,unselectable:0},DOMAttributeNames:{acceptCharset:\"accept-charset\",className:\"class\",htmlFor:\"for\",httpEquiv:\"http-equiv\"},DOMPropertyNames:{},DOMMutationMethods:{value:function(e,t){if(null==t)return e.removeAttribute(\"value\");\"number\"!==e.type||!1===e.hasAttribute(\"value\")?e.setAttribute(\"value\",\"\"+t):e.validity&&!e.validity.badInput&&e.ownerDocument.activeElement!==e&&e.setAttribute(\"value\",\"\"+t)}}};t.exports=l},{11:11}],22:[function(e,t,n){\"use strict\";function r(e){var t={\"=\":\"=0\",\":\":\"=2\"};return\"$\"+(\"\"+e).replace(/[=:]/g,function(e){return t[e]})}function o(e){var t={\"=0\":\"=\",\"=2\":\":\"};return(\"\"+(\".\"===e[0]&&\"$\"===e[1]?e.substring(2):e.substring(1))).replace(/(=0|=2)/g,function(e){return t[e]})}var i={escape:r,unescape:o};t.exports=i},{}],23:[function(e,t,n){\"use strict\";function r(e){null!=e.checkedLink&&null!=e.valueLink&&s(\"87\")}function o(e){r(e),(null!=e.value||null!=e.onChange)&&s(\"88\")}function i(e){r(e),(null!=e.checked||null!=e.onChange)&&s(\"89\")}function a(e){if(e){var t=e.getName();if(t)return\" Check the render method of `\"+t+\"`.\"}return\"\"}var s=e(112),u=e(64),l=e(145),c=e(120),p=l(c.isValidElement),d=(e(137),e(142),{button:!0,checkbox:!0,image:!0,hidden:!0,radio:!0,reset:!0,submit:!0}),f={value:function(e,t,n){return!e[t]||d[e.type]||e.onChange||e.readOnly||e.disabled?null:new Error(\"You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.\")},checked:function(e,t,n){return!e[t]||e.onChange||e.readOnly||e.disabled?null:new Error(\"You provided a `checked` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultChecked`. Otherwise, set either `onChange` or `readOnly`.\")},onChange:p.func},h={},m={checkPropTypes:function(e,t,n){for(var r in f){if(f.hasOwnProperty(r))var o=f[r](t,r,e,\"prop\",null,u);o instanceof Error&&!(o.message in h)&&(h[o.message]=!0,a(n))}},getValue:function(e){return e.valueLink?(o(e),e.valueLink.value):e.value},getChecked:function(e){return e.checkedLink?(i(e),e.checkedLink.value):e.checked},executeOnChange:function(e,t){return e.valueLink?(o(e),e.valueLink.requestChange(t.target.value)):e.checkedLink?(i(e),e.checkedLink.requestChange(t.target.checked)):e.onChange?e.onChange.call(void 0,t):void 0}};t.exports=m},{112:112,120:120,137:137,142:142,145:145,64:64}],24:[function(e,t,n){\"use strict\";var r=e(112),o=(e(137),function(e){var t=this;if(t.instancePool.length){var n=t.instancePool.pop();return t.call(n,e),n}return new t(e)}),i=function(e,t){var n=this;if(n.instancePool.length){var r=n.instancePool.pop();return n.call(r,e,t),r}return new n(e,t)},a=function(e,t,n){var r=this;if(r.instancePool.length){var o=r.instancePool.pop();return r.call(o,e,t,n),o}return new r(e,t,n)},s=function(e,t,n,r){var o=this;if(o.instancePool.length){var i=o.instancePool.pop();return o.call(i,e,t,n,r),i}return new o(e,t,n,r)},u=function(e){var t=this;e instanceof t||r(\"25\"),e.destructor(),t.instancePool.length<t.poolSize&&t.instancePool.push(e)},l=o,c=function(e,t){var n=e;return n.instancePool=[],n.getPooled=t||l,n.poolSize||(n.poolSize=10),n.release=u,n},p={addPoolingTo:c,oneArgumentPooler:o,twoArgumentPooler:i,threeArgumentPooler:a,fourArgumentPooler:s};t.exports=p},{112:112,137:137}],25:[function(e,t,n){\"use strict\";function r(e){return Object.prototype.hasOwnProperty.call(e,m)||(e[m]=f++,p[e[m]]={}),p[e[m]]}var o,i=e(143),a=e(17),s=e(51),u=e(90),l=e(107),c=e(109),p={},d=!1,f=0,h={topAbort:\"abort\",topAnimationEnd:l(\"animationend\")||\"animationend\",topAnimationIteration:l(\"animationiteration\")||\"animationiteration\",topAnimationStart:l(\"animationstart\")||\"animationstart\",topBlur:\"blur\",topCanPlay:\"canplay\",topCanPlayThrough:\"canplaythrough\",topChange:\"change\",topClick:\"click\",topCompositionEnd:\"compositionend\",topCompositionStart:\"compositionstart\",topCompositionUpdate:\"compositionupdate\",topContextMenu:\"contextmenu\",topCopy:\"copy\",topCut:\"cut\",topDoubleClick:\"dblclick\",topDrag:\"drag\",topDragEnd:\"dragend\",topDragEnter:\"dragenter\",topDragExit:\"dragexit\",topDragLeave:\"dragleave\",topDragOver:\"dragover\",topDragStart:\"dragstart\",topDrop:\"drop\",topDurationChange:\"durationchange\",topEmptied:\"emptied\",topEncrypted:\"encrypted\",topEnded:\"ended\",topError:\"error\",\ntopFocus:\"focus\",topInput:\"input\",topKeyDown:\"keydown\",topKeyPress:\"keypress\",topKeyUp:\"keyup\",topLoadedData:\"loadeddata\",topLoadedMetadata:\"loadedmetadata\",topLoadStart:\"loadstart\",topMouseDown:\"mousedown\",topMouseMove:\"mousemove\",topMouseOut:\"mouseout\",topMouseOver:\"mouseover\",topMouseUp:\"mouseup\",topPaste:\"paste\",topPause:\"pause\",topPlay:\"play\",topPlaying:\"playing\",topProgress:\"progress\",topRateChange:\"ratechange\",topScroll:\"scroll\",topSeeked:\"seeked\",topSeeking:\"seeking\",topSelectionChange:\"selectionchange\",topStalled:\"stalled\",topSuspend:\"suspend\",topTextInput:\"textInput\",topTimeUpdate:\"timeupdate\",topTouchCancel:\"touchcancel\",topTouchEnd:\"touchend\",topTouchMove:\"touchmove\",topTouchStart:\"touchstart\",topTransitionEnd:l(\"transitionend\")||\"transitionend\",topVolumeChange:\"volumechange\",topWaiting:\"waiting\",topWheel:\"wheel\"},m=\"_reactListenersID\"+String(Math.random()).slice(2),v=i({},s,{ReactEventListener:null,injection:{injectReactEventListener:function(e){e.setHandleTopLevel(v.handleTopLevel),v.ReactEventListener=e}},setEnabled:function(e){v.ReactEventListener&&v.ReactEventListener.setEnabled(e)},isEnabled:function(){return!(!v.ReactEventListener||!v.ReactEventListener.isEnabled())},listenTo:function(e,t){for(var n=t,o=r(n),i=a.registrationNameDependencies[e],s=0;s<i.length;s++){var u=i[s];o.hasOwnProperty(u)&&o[u]||(\"topWheel\"===u?c(\"wheel\")?v.ReactEventListener.trapBubbledEvent(\"topWheel\",\"wheel\",n):c(\"mousewheel\")?v.ReactEventListener.trapBubbledEvent(\"topWheel\",\"mousewheel\",n):v.ReactEventListener.trapBubbledEvent(\"topWheel\",\"DOMMouseScroll\",n):\"topScroll\"===u?c(\"scroll\",!0)?v.ReactEventListener.trapCapturedEvent(\"topScroll\",\"scroll\",n):v.ReactEventListener.trapBubbledEvent(\"topScroll\",\"scroll\",v.ReactEventListener.WINDOW_HANDLE):\"topFocus\"===u||\"topBlur\"===u?(c(\"focus\",!0)?(v.ReactEventListener.trapCapturedEvent(\"topFocus\",\"focus\",n),v.ReactEventListener.trapCapturedEvent(\"topBlur\",\"blur\",n)):c(\"focusin\")&&(v.ReactEventListener.trapBubbledEvent(\"topFocus\",\"focusin\",n),v.ReactEventListener.trapBubbledEvent(\"topBlur\",\"focusout\",n)),o.topBlur=!0,o.topFocus=!0):h.hasOwnProperty(u)&&v.ReactEventListener.trapBubbledEvent(u,h[u],n),o[u]=!0)}},trapBubbledEvent:function(e,t,n){return v.ReactEventListener.trapBubbledEvent(e,t,n)},trapCapturedEvent:function(e,t,n){return v.ReactEventListener.trapCapturedEvent(e,t,n)},supportsEventPageXY:function(){if(!document.createEvent)return!1;var e=document.createEvent(\"MouseEvent\");return null!=e&&\"pageX\"in e},ensureScrollValueMonitoring:function(){if(void 0===o&&(o=v.supportsEventPageXY()),!o&&!d){var e=u.refreshScrollValues;v.ReactEventListener.monitorScrollValue(e),d=!0}}});t.exports=v},{107:107,109:109,143:143,17:17,51:51,90:90}],26:[function(e,t,n){(function(n){\"use strict\";function r(e,t,n,r){var o=void 0===e[n];null!=t&&o&&(e[n]=i(t,!0))}var o=e(66),i=e(108),a=(e(22),e(116)),s=e(117);e(142);void 0!==n&&n.env;var u={instantiateChildren:function(e,t,n,o){if(null==e)return null;var i={};return s(e,r,i),i},updateChildren:function(e,t,n,r,s,u,l,c,p){if(t||e){var d,f;for(d in t)if(t.hasOwnProperty(d)){f=e&&e[d];var h=f&&f._currentElement,m=t[d];if(null!=f&&a(h,m))o.receiveComponent(f,m,s,c),t[d]=f;else{f&&(r[d]=o.getHostNode(f),o.unmountComponent(f,!1));var v=i(m,!0);t[d]=v;var g=o.mountComponent(v,s,u,l,c,p);n.push(g)}}for(d in e)!e.hasOwnProperty(d)||t&&t.hasOwnProperty(d)||(f=e[d],r[d]=o.getHostNode(f),o.unmountComponent(f,!1))}},unmountChildren:function(e,t){for(var n in e)if(e.hasOwnProperty(n)){var r=e[n];o.unmountComponent(r,t)}}};t.exports=u}).call(this,void 0)},{108:108,116:116,117:117,142:142,22:22,66:66}],27:[function(e,t,n){\"use strict\";var r=e(8),o=e(37),i={processChildrenUpdates:o.dangerouslyProcessChildrenUpdates,replaceNodeWithMarkup:r.dangerouslyReplaceNodeWithMarkup};t.exports=i},{37:37,8:8}],28:[function(e,t,n){\"use strict\";var r=e(112),o=(e(137),!1),i={replaceNodeWithMarkup:null,processChildrenUpdates:null,injection:{injectEnvironment:function(e){o&&r(\"104\"),i.replaceNodeWithMarkup=e.replaceNodeWithMarkup,i.processChildrenUpdates=e.processChildrenUpdates,o=!0}}};t.exports=i},{112:112,137:137}],29:[function(e,t,n){\"use strict\";function r(e){}function o(e){return!(!e.prototype||!e.prototype.isReactComponent)}function i(e){return!(!e.prototype||!e.prototype.isPureReactComponent)}var a=e(112),s=e(143),u=e(120),l=e(28),c=e(119),p=e(50),d=e(57),f=(e(58),e(62)),h=e(66),m=e(130),v=(e(137),e(141)),g=e(116),y=(e(142),{ImpureClass:0,PureClass:1,StatelessFunctional:2});r.prototype.render=function(){var e=d.get(this)._currentElement.type,t=e(this.props,this.context,this.updater);return t};var _=1,C={construct:function(e){this._currentElement=e,this._rootNodeID=0,this._compositeType=null,this._instance=null,this._hostParent=null,this._hostContainerInfo=null,this._updateBatchNumber=null,this._pendingElement=null,this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1,this._renderedNodeType=null,this._renderedComponent=null,this._context=null,this._mountOrder=0,this._topLevelWrapper=null,this._pendingCallbacks=null,this._calledComponentWillUnmount=!1},mountComponent:function(e,t,n,s){this._context=s,this._mountOrder=_++,this._hostParent=t,this._hostContainerInfo=n;var l,c=this._currentElement.props,p=this._processContext(s),f=this._currentElement.type,h=e.getUpdateQueue(),v=o(f),g=this._constructComponent(v,c,p,h);v||null!=g&&null!=g.render?i(f)?this._compositeType=y.PureClass:this._compositeType=y.ImpureClass:(l=g,null===g||!1===g||u.isValidElement(g)||a(\"105\",f.displayName||f.name||\"Component\"),g=new r(f),this._compositeType=y.StatelessFunctional),g.props=c,g.context=p,g.refs=m,g.updater=h,this._instance=g,d.set(g,this);var C=g.state;void 0===C&&(g.state=C=null),(\"object\"!=typeof C||Array.isArray(C))&&a(\"106\",this.getName()||\"ReactCompositeComponent\"),this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1;var b;return b=g.unstable_handleError?this.performInitialMountWithErrorHandling(l,t,n,e,s):this.performInitialMount(l,t,n,e,s),g.componentDidMount&&e.getReactMountReady().enqueue(g.componentDidMount,g),b},_constructComponent:function(e,t,n,r){return this._constructComponentWithoutOwner(e,t,n,r)},_constructComponentWithoutOwner:function(e,t,n,r){var o=this._currentElement.type;return e?new o(t,n,r):o(t,n,r)},performInitialMountWithErrorHandling:function(e,t,n,r,o){var i,a=r.checkpoint();try{i=this.performInitialMount(e,t,n,r,o)}catch(s){r.rollback(a),this._instance.unstable_handleError(s),this._pendingStateQueue&&(this._instance.state=this._processPendingState(this._instance.props,this._instance.context)),a=r.checkpoint(),this._renderedComponent.unmountComponent(!0),r.rollback(a),i=this.performInitialMount(e,t,n,r,o)}return i},performInitialMount:function(e,t,n,r,o){var i=this._instance;i.componentWillMount&&(i.componentWillMount(),this._pendingStateQueue&&(i.state=this._processPendingState(i.props,i.context))),void 0===e&&(e=this._renderValidatedComponent());var a=f.getType(e);this._renderedNodeType=a;var s=this._instantiateReactComponent(e,a!==f.EMPTY);return this._renderedComponent=s,h.mountComponent(s,r,t,n,this._processChildContext(o),0)},getHostNode:function(){return h.getHostNode(this._renderedComponent)},unmountComponent:function(e){if(this._renderedComponent){var t=this._instance;if(t.componentWillUnmount&&!t._calledComponentWillUnmount)if(t._calledComponentWillUnmount=!0,e){var n=this.getName()+\".componentWillUnmount()\";p.invokeGuardedCallback(n,t.componentWillUnmount.bind(t))}else t.componentWillUnmount();this._renderedComponent&&(h.unmountComponent(this._renderedComponent,e),this._renderedNodeType=null,this._renderedComponent=null,this._instance=null),this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1,this._pendingCallbacks=null,this._pendingElement=null,this._context=null,this._rootNodeID=0,this._topLevelWrapper=null,d.remove(t)}},_maskContext:function(e){var t=this._currentElement.type,n=t.contextTypes;if(!n)return m;var r={};for(var o in n)r[o]=e[o];return r},_processContext:function(e){return this._maskContext(e)},_processChildContext:function(e){var t,n=this._currentElement.type,r=this._instance;if(r.getChildContext&&(t=r.getChildContext()),t){\"object\"!=typeof n.childContextTypes&&a(\"107\",this.getName()||\"ReactCompositeComponent\");for(var o in t)o in n.childContextTypes||a(\"108\",this.getName()||\"ReactCompositeComponent\",o);return s({},e,t)}return e},_checkContextTypes:function(e,t,n){},receiveComponent:function(e,t,n){var r=this._currentElement,o=this._context;this._pendingElement=null,this.updateComponent(t,r,e,o,n)},performUpdateIfNecessary:function(e){null!=this._pendingElement?h.receiveComponent(this,this._pendingElement,e,this._context):null!==this._pendingStateQueue||this._pendingForceUpdate?this.updateComponent(e,this._currentElement,this._currentElement,this._context,this._context):this._updateBatchNumber=null},updateComponent:function(e,t,n,r,o){var i=this._instance;null==i&&a(\"136\",this.getName()||\"ReactCompositeComponent\");var s,u=!1;this._context===o?s=i.context:(s=this._processContext(o),u=!0);var l=t.props,c=n.props;t!==n&&(u=!0),u&&i.componentWillReceiveProps&&i.componentWillReceiveProps(c,s);var p=this._processPendingState(c,s),d=!0;this._pendingForceUpdate||(i.shouldComponentUpdate?d=i.shouldComponentUpdate(c,p,s):this._compositeType===y.PureClass&&(d=!v(l,c)||!v(i.state,p))),this._updateBatchNumber=null,d?(this._pendingForceUpdate=!1,this._performComponentUpdate(n,c,p,s,e,o)):(this._currentElement=n,this._context=o,i.props=c,i.state=p,i.context=s)},_processPendingState:function(e,t){var n=this._instance,r=this._pendingStateQueue,o=this._pendingReplaceState;if(this._pendingReplaceState=!1,this._pendingStateQueue=null,!r)return n.state;if(o&&1===r.length)return r[0];for(var i=s({},o?r[0]:n.state),a=o?1:0;a<r.length;a++){var u=r[a];s(i,\"function\"==typeof u?u.call(n,i,e,t):u)}return i},_performComponentUpdate:function(e,t,n,r,o,i){var a,s,u,l=this._instance,c=Boolean(l.componentDidUpdate);c&&(a=l.props,s=l.state,u=l.context),l.componentWillUpdate&&l.componentWillUpdate(t,n,r),this._currentElement=e,this._context=i,l.props=t,l.state=n,l.context=r,this._updateRenderedComponent(o,i),c&&o.getReactMountReady().enqueue(l.componentDidUpdate.bind(l,a,s,u),l)},_updateRenderedComponent:function(e,t){var n=this._renderedComponent,r=n._currentElement,o=this._renderValidatedComponent();if(g(r,o))h.receiveComponent(n,o,e,this._processChildContext(t));else{var i=h.getHostNode(n);h.unmountComponent(n,!1);var a=f.getType(o);this._renderedNodeType=a;var s=this._instantiateReactComponent(o,a!==f.EMPTY);this._renderedComponent=s;var u=h.mountComponent(s,e,this._hostParent,this._hostContainerInfo,this._processChildContext(t),0);this._replaceNodeWithMarkup(i,u,n)}},_replaceNodeWithMarkup:function(e,t,n){l.replaceNodeWithMarkup(e,t,n)},_renderValidatedComponentWithoutOwnerOrContext:function(){return this._instance.render()},_renderValidatedComponent:function(){var e;if(this._compositeType!==y.StatelessFunctional){c.current=this;try{e=this._renderValidatedComponentWithoutOwnerOrContext()}finally{c.current=null}}else e=this._renderValidatedComponentWithoutOwnerOrContext();return null===e||!1===e||u.isValidElement(e)||a(\"109\",this.getName()||\"ReactCompositeComponent\"),e},attachRef:function(e,t){var n=this.getPublicInstance();null==n&&a(\"110\");var r=t.getPublicInstance();(n.refs===m?n.refs={}:n.refs)[e]=r},detachRef:function(e){delete this.getPublicInstance().refs[e]},getName:function(){var e=this._currentElement.type,t=this._instance&&this._instance.constructor;return e.displayName||t&&t.displayName||e.name||t&&t.name||null},getPublicInstance:function(){var e=this._instance;return this._compositeType===y.StatelessFunctional?null:e},_instantiateReactComponent:null};t.exports=C},{112:112,116:116,119:119,120:120,130:130,137:137,141:141,142:142,143:143,28:28,50:50,57:57,58:58,62:62,66:66}],30:[function(e,t,n){\"use strict\";var r=e(33),o=e(47),i=e(60),a=e(66),s=e(71),u=e(72),l=e(96),c=e(103),p=e(113);e(142);o.inject();var d={findDOMNode:l,render:i.render,unmountComponentAtNode:i.unmountComponentAtNode,version:u,unstable_batchedUpdates:s.batchedUpdates,unstable_renderSubtreeIntoContainer:p};\"undefined\"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&\"function\"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject&&__REACT_DEVTOOLS_GLOBAL_HOOK__.inject({ComponentTree:{getClosestInstanceFromNode:r.getClosestInstanceFromNode,getNodeFromInstance:function(e){return e._renderedComponent&&(e=c(e)),e?r.getNodeFromInstance(e):null}},Mount:i,Reconciler:a});t.exports=d},{103:103,113:113,142:142,33:33,47:47,60:60,66:66,71:71,72:72,96:96}],31:[function(e,t,n){\"use strict\";function r(e){if(e){var t=e._currentElement._owner||null;if(t){var n=t.getName();if(n)return\" This DOM node was rendered by `\"+n+\"`.\"}}return\"\"}function o(e,t){t&&(Y[e._tag]&&(null!=t.children||null!=t.dangerouslySetInnerHTML)&&m(\"137\",e._tag,e._currentElement._owner?\" Check the render method of \"+e._currentElement._owner.getName()+\".\":\"\"),null!=t.dangerouslySetInnerHTML&&(null!=t.children&&m(\"60\"),\"object\"==typeof t.dangerouslySetInnerHTML&&B in t.dangerouslySetInnerHTML||m(\"61\")),null!=t.style&&\"object\"!=typeof t.style&&m(\"62\",r(e)))}function i(e,t,n,r){if(!(r instanceof R)){var o=e._hostContainerInfo,i=o._node&&o._node.nodeType===H,s=i?o._node:o._ownerDocument;F(t,s),r.getReactMountReady().enqueue(a,{inst:e,registrationName:t,listener:n})}}function a(){var e=this;x.putListener(e.inst,e.registrationName,e.listener)}function s(){var e=this;S.postMountWrapper(e)}function u(){var e=this;I.postMountWrapper(e)}function l(){var e=this;N.postMountWrapper(e)}function c(){var e=this;e._rootNodeID||m(\"63\");var t=U(e);switch(t||m(\"64\"),e._tag){case\"iframe\":case\"object\":e._wrapperState.listeners=[T.trapBubbledEvent(\"topLoad\",\"load\",t)];break;case\"video\":case\"audio\":e._wrapperState.listeners=[];for(var n in q)q.hasOwnProperty(n)&&e._wrapperState.listeners.push(T.trapBubbledEvent(n,q[n],t));break;case\"source\":e._wrapperState.listeners=[T.trapBubbledEvent(\"topError\",\"error\",t)];break;case\"img\":e._wrapperState.listeners=[T.trapBubbledEvent(\"topError\",\"error\",t),T.trapBubbledEvent(\"topLoad\",\"load\",t)];break;case\"form\":e._wrapperState.listeners=[T.trapBubbledEvent(\"topReset\",\"reset\",t),T.trapBubbledEvent(\"topSubmit\",\"submit\",t)];break;case\"input\":case\"select\":case\"textarea\":e._wrapperState.listeners=[T.trapBubbledEvent(\"topInvalid\",\"invalid\",t)]}}function p(){M.postUpdateWrapper(this)}function d(e){G.call(Q,e)||(X.test(e)||m(\"65\",e),Q[e]=!0)}function f(e,t){return e.indexOf(\"-\")>=0||null!=t.is}function h(e){var t=e.type;d(t),this._currentElement=e,this._tag=t.toLowerCase(),this._namespaceURI=null,this._renderedChildren=null,this._previousStyle=null,this._previousStyleCopy=null,this._hostNode=null,this._hostParent=null,this._rootNodeID=0,this._domID=0,this._hostContainerInfo=null,this._wrapperState=null,this._topLevelWrapper=null,this._flags=0}var m=e(112),v=e(143),g=e(2),y=e(5),_=e(9),C=e(10),b=e(11),E=e(12),x=e(16),w=e(17),T=e(25),k=e(32),P=e(33),S=e(38),N=e(39),M=e(40),I=e(43),O=(e(58),e(61)),R=e(68),A=(e(129),e(95)),D=(e(137),e(109),e(141),e(118),e(142),k),L=x.deleteListener,U=P.getNodeFromInstance,F=T.listenTo,j=w.registrationNameModules,V={string:!0,number:!0},B=\"__html\",W={children:null,dangerouslySetInnerHTML:null,suppressContentEditableWarning:null},H=11,q={topAbort:\"abort\",topCanPlay:\"canplay\",topCanPlayThrough:\"canplaythrough\",topDurationChange:\"durationchange\",topEmptied:\"emptied\",topEncrypted:\"encrypted\",topEnded:\"ended\",topError:\"error\",topLoadedData:\"loadeddata\",topLoadedMetadata:\"loadedmetadata\",topLoadStart:\"loadstart\",topPause:\"pause\",topPlay:\"play\",topPlaying:\"playing\",topProgress:\"progress\",topRateChange:\"ratechange\",topSeeked:\"seeked\",topSeeking:\"seeking\",topStalled:\"stalled\",topSuspend:\"suspend\",topTimeUpdate:\"timeupdate\",topVolumeChange:\"volumechange\",topWaiting:\"waiting\"},K={area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0},z={listing:!0,pre:!0,textarea:!0},Y=v({menuitem:!0},K),X=/^[a-zA-Z][a-zA-Z:_\\.\\-\\d]*$/,Q={},G={}.hasOwnProperty,$=1;h.displayName=\"ReactDOMComponent\",h.Mixin={mountComponent:function(e,t,n,r){this._rootNodeID=$++,this._domID=n._idCounter++,this._hostParent=t,this._hostContainerInfo=n;var i=this._currentElement.props;switch(this._tag){case\"audio\":case\"form\":case\"iframe\":case\"img\":case\"link\":case\"object\":case\"source\":case\"video\":this._wrapperState={listeners:null},e.getReactMountReady().enqueue(c,this);break;case\"input\":S.mountWrapper(this,i,t),i=S.getHostProps(this,i),e.getReactMountReady().enqueue(c,this);break;case\"option\":N.mountWrapper(this,i,t),i=N.getHostProps(this,i);break;case\"select\":M.mountWrapper(this,i,t),i=M.getHostProps(this,i),e.getReactMountReady().enqueue(c,this);break;case\"textarea\":I.mountWrapper(this,i,t),i=I.getHostProps(this,i),e.getReactMountReady().enqueue(c,this)}o(this,i);var a,p;null!=t?(a=t._namespaceURI,p=t._tag):n._tag&&(a=n._namespaceURI,p=n._tag),(null==a||a===C.svg&&\"foreignobject\"===p)&&(a=C.html),a===C.html&&(\"svg\"===this._tag?a=C.svg:\"math\"===this._tag&&(a=C.mathml)),this._namespaceURI=a;var d;if(e.useCreateElement){var f,h=n._ownerDocument;if(a===C.html)if(\"script\"===this._tag){var m=h.createElement(\"div\"),v=this._currentElement.type;m.innerHTML=\"<\"+v+\"></\"+v+\">\",f=m.removeChild(m.firstChild)}else f=i.is?h.createElement(this._currentElement.type,i.is):h.createElement(this._currentElement.type);else f=h.createElementNS(a,this._currentElement.type);P.precacheNode(this,f),this._flags|=D.hasCachedChildNodes,this._hostParent||E.setAttributeForRoot(f),this._updateDOMProperties(null,i,e);var y=_(f);this._createInitialChildren(e,i,r,y),d=y}else{var b=this._createOpenTagMarkupAndPutListeners(e,i),x=this._createContentMarkup(e,i,r);d=!x&&K[this._tag]?b+\"/>\":b+\">\"+x+\"</\"+this._currentElement.type+\">\"}switch(this._tag){case\"input\":e.getReactMountReady().enqueue(s,this),i.autoFocus&&e.getReactMountReady().enqueue(g.focusDOMComponent,this);break;case\"textarea\":e.getReactMountReady().enqueue(u,this),i.autoFocus&&e.getReactMountReady().enqueue(g.focusDOMComponent,this);break;case\"select\":case\"button\":i.autoFocus&&e.getReactMountReady().enqueue(g.focusDOMComponent,this);break;case\"option\":e.getReactMountReady().enqueue(l,this)}return d},_createOpenTagMarkupAndPutListeners:function(e,t){var n=\"<\"+this._currentElement.type;for(var r in t)if(t.hasOwnProperty(r)){var o=t[r];if(null!=o)if(j.hasOwnProperty(r))o&&i(this,r,o,e);else{\"style\"===r&&(o&&(o=this._previousStyleCopy=v({},t.style)),o=y.createMarkupForStyles(o,this));var a=null;null!=this._tag&&f(this._tag,t)?W.hasOwnProperty(r)||(a=E.createMarkupForCustomAttribute(r,o)):a=E.createMarkupForProperty(r,o),a&&(n+=\" \"+a)}}return e.renderToStaticMarkup?n:(this._hostParent||(n+=\" \"+E.createMarkupForRoot()),n+=\" \"+E.createMarkupForID(this._domID))},_createContentMarkup:function(e,t,n){var r=\"\",o=t.dangerouslySetInnerHTML;if(null!=o)null!=o.__html&&(r=o.__html);else{var i=V[typeof t.children]?t.children:null,a=null!=i?null:t.children;if(null!=i)r=A(i);else if(null!=a){var s=this.mountChildren(a,e,n);r=s.join(\"\")}}return z[this._tag]&&\"\\n\"===r.charAt(0)?\"\\n\"+r:r},_createInitialChildren:function(e,t,n,r){var o=t.dangerouslySetInnerHTML;if(null!=o)null!=o.__html&&_.queueHTML(r,o.__html);else{var i=V[typeof t.children]?t.children:null,a=null!=i?null:t.children;if(null!=i)\"\"!==i&&_.queueText(r,i);else if(null!=a)for(var s=this.mountChildren(a,e,n),u=0;u<s.length;u++)_.queueChild(r,s[u])}},receiveComponent:function(e,t,n){var r=this._currentElement;this._currentElement=e,this.updateComponent(t,r,e,n)},updateComponent:function(e,t,n,r){var i=t.props,a=this._currentElement.props;switch(this._tag){case\"input\":i=S.getHostProps(this,i),a=S.getHostProps(this,a);break;case\"option\":i=N.getHostProps(this,i),a=N.getHostProps(this,a);break;case\"select\":i=M.getHostProps(this,i),a=M.getHostProps(this,a);break;case\"textarea\":i=I.getHostProps(this,i),a=I.getHostProps(this,a)}switch(o(this,a),this._updateDOMProperties(i,a,e),this._updateDOMChildren(i,a,e,r),this._tag){case\"input\":S.updateWrapper(this);break;case\"textarea\":I.updateWrapper(this);break;case\"select\":e.getReactMountReady().enqueue(p,this)}},_updateDOMProperties:function(e,t,n){var r,o,a;for(r in e)if(!t.hasOwnProperty(r)&&e.hasOwnProperty(r)&&null!=e[r])if(\"style\"===r){var s=this._previousStyleCopy;for(o in s)s.hasOwnProperty(o)&&(a=a||{},a[o]=\"\");this._previousStyleCopy=null}else j.hasOwnProperty(r)?e[r]&&L(this,r):f(this._tag,e)?W.hasOwnProperty(r)||E.deleteValueForAttribute(U(this),r):(b.properties[r]||b.isCustomAttribute(r))&&E.deleteValueForProperty(U(this),r);for(r in t){var u=t[r],l=\"style\"===r?this._previousStyleCopy:null!=e?e[r]:void 0;if(t.hasOwnProperty(r)&&u!==l&&(null!=u||null!=l))if(\"style\"===r)if(u?u=this._previousStyleCopy=v({},u):this._previousStyleCopy=null,l){for(o in l)!l.hasOwnProperty(o)||u&&u.hasOwnProperty(o)||(a=a||{},a[o]=\"\");for(o in u)u.hasOwnProperty(o)&&l[o]!==u[o]&&(a=a||{},a[o]=u[o])}else a=u;else if(j.hasOwnProperty(r))u?i(this,r,u,n):l&&L(this,r);else if(f(this._tag,t))W.hasOwnProperty(r)||E.setValueForAttribute(U(this),r,u);else if(b.properties[r]||b.isCustomAttribute(r)){var c=U(this);null!=u?E.setValueForProperty(c,r,u):E.deleteValueForProperty(c,r)}}a&&y.setValueForStyles(U(this),a,this)},_updateDOMChildren:function(e,t,n,r){var o=V[typeof e.children]?e.children:null,i=V[typeof t.children]?t.children:null,a=e.dangerouslySetInnerHTML&&e.dangerouslySetInnerHTML.__html,s=t.dangerouslySetInnerHTML&&t.dangerouslySetInnerHTML.__html,u=null!=o?null:e.children,l=null!=i?null:t.children,c=null!=o||null!=a,p=null!=i||null!=s;null!=u&&null==l?this.updateChildren(null,n,r):c&&!p&&this.updateTextContent(\"\"),null!=i?o!==i&&this.updateTextContent(\"\"+i):null!=s?a!==s&&this.updateMarkup(\"\"+s):null!=l&&this.updateChildren(l,n,r)},getHostNode:function(){return U(this)},unmountComponent:function(e){switch(this._tag){case\"audio\":case\"form\":case\"iframe\":case\"img\":case\"link\":case\"object\":case\"source\":case\"video\":var t=this._wrapperState.listeners;if(t)for(var n=0;n<t.length;n++)t[n].remove();break;case\"html\":case\"head\":case\"body\":m(\"66\",this._tag)}this.unmountChildren(e),P.uncacheNode(this),x.deleteAllListeners(this),this._rootNodeID=0,this._domID=0,this._wrapperState=null},getPublicInstance:function(){return U(this)}},v(h.prototype,h.Mixin,O.Mixin),t.exports=h},{10:10,109:109,11:11,112:112,118:118,12:12,129:129,137:137,141:141,142:142,143:143,16:16,17:17,2:2,25:25,32:32,33:33,38:38,39:39,40:40,43:43,5:5,58:58,61:61,68:68,9:9,95:95}],32:[function(e,t,n){\"use strict\";var r={hasCachedChildNodes:1};t.exports=r},{}],33:[function(e,t,n){\"use strict\";function r(e,t){return 1===e.nodeType&&e.getAttribute(h)===String(t)||8===e.nodeType&&e.nodeValue===\" react-text: \"+t+\" \"||8===e.nodeType&&e.nodeValue===\" react-empty: \"+t+\" \"}function o(e){for(var t;t=e._renderedComponent;)e=t;return e}function i(e,t){var n=o(e);n._hostNode=t,t[v]=n}function a(e){var t=e._hostNode;t&&(delete t[v],e._hostNode=null)}function s(e,t){if(!(e._flags&m.hasCachedChildNodes)){var n=e._renderedChildren,a=t.firstChild;e:for(var s in n)if(n.hasOwnProperty(s)){var u=n[s],l=o(u)._domID;if(0!==l){for(;null!==a;a=a.nextSibling)if(r(a,l)){i(u,a);continue e}p(\"32\",l)}}e._flags|=m.hasCachedChildNodes}}function u(e){if(e[v])return e[v];for(var t=[];!e[v];){if(t.push(e),!e.parentNode)return null;e=e.parentNode}for(var n,r;e&&(r=e[v]);e=t.pop())n=r,t.length&&s(r,e);return n}function l(e){var t=u(e);return null!=t&&t._hostNode===e?t:null}function c(e){if(void 0===e._hostNode&&p(\"33\"),e._hostNode)return e._hostNode;for(var t=[];!e._hostNode;)t.push(e),e._hostParent||p(\"34\"),e=e._hostParent;for(;t.length;e=t.pop())s(e,e._hostNode);return e._hostNode}var p=e(112),d=e(11),f=e(32),h=(e(137),d.ID_ATTRIBUTE_NAME),m=f,v=\"__reactInternalInstance$\"+Math.random().toString(36).slice(2),g={getClosestInstanceFromNode:u,getInstanceFromNode:l,getNodeFromInstance:c,precacheChildNodes:s,precacheNode:i,uncacheNode:a};t.exports=g},{11:11,112:112,137:137,32:32}],34:[function(e,t,n){\"use strict\";function r(e,t){return{_topLevelWrapper:e,_idCounter:1,_ownerDocument:t?t.nodeType===o?t:t.ownerDocument:null,_node:t,_tag:t?t.nodeName.toLowerCase():null,_namespaceURI:t?t.namespaceURI:null}}var o=(e(118),9);t.exports=r},{118:118}],35:[function(e,t,n){\"use strict\";var r=e(143),o=e(9),i=e(33),a=function(e){this._currentElement=null,this._hostNode=null,this._hostParent=null,this._hostContainerInfo=null,this._domID=0};r(a.prototype,{mountComponent:function(e,t,n,r){var a=n._idCounter++;this._domID=a,this._hostParent=t,this._hostContainerInfo=n;var s=\" react-empty: \"+this._domID+\" \";if(e.useCreateElement){var u=n._ownerDocument,l=u.createComment(s);return i.precacheNode(this,l),o(l)}return e.renderToStaticMarkup?\"\":\"<!--\"+s+\"-->\"},receiveComponent:function(){},getHostNode:function(){return i.getNodeFromInstance(this)},unmountComponent:function(){i.uncacheNode(this)}}),t.exports=a},{143:143,33:33,9:9}],36:[function(e,t,n){\"use strict\";var r={useCreateElement:!0,useFiber:!1};t.exports=r},{}],37:[function(e,t,n){\"use strict\";var r=e(8),o=e(33),i={dangerouslyProcessChildrenUpdates:function(e,t){var n=o.getNodeFromInstance(e);r.processUpdates(n,t)}};t.exports=i},{33:33,8:8}],38:[function(e,t,n){\"use strict\";function r(){this._rootNodeID&&d.updateWrapper(this)}function o(e){return\"checkbox\"===e.type||\"radio\"===e.type?null!=e.checked:null!=e.value}function i(e){var t=this._currentElement.props,n=l.executeOnChange(t,e);p.asap(r,this);var o=t.name;if(\"radio\"===t.type&&null!=o){for(var i=c.getNodeFromInstance(this),s=i;s.parentNode;)s=s.parentNode;for(var u=s.querySelectorAll(\"input[name=\"+JSON.stringify(\"\"+o)+'][type=\"radio\"]'),d=0;d<u.length;d++){var f=u[d];if(f!==i&&f.form===i.form){var h=c.getInstanceFromNode(f);h||a(\"90\"),p.asap(r,h)}}}return n}var a=e(112),s=e(143),u=e(12),l=e(23),c=e(33),p=e(71),d=(e(137),e(142),{getHostProps:function(e,t){var n=l.getValue(t),r=l.getChecked(t);return s({type:void 0,step:void 0,min:void 0,max:void 0},t,{defaultChecked:void 0,defaultValue:void 0,value:null!=n?n:e._wrapperState.initialValue,checked:null!=r?r:e._wrapperState.initialChecked,onChange:e._wrapperState.onChange})},mountWrapper:function(e,t){var n=t.defaultValue;e._wrapperState={initialChecked:null!=t.checked?t.checked:t.defaultChecked,initialValue:null!=t.value?t.value:n,listeners:null,onChange:i.bind(e),controlled:o(t)}},updateWrapper:function(e){var t=e._currentElement.props,n=t.checked;null!=n&&u.setValueForProperty(c.getNodeFromInstance(e),\"checked\",n||!1);var r=c.getNodeFromInstance(e),o=l.getValue(t);if(null!=o)if(0===o&&\"\"===r.value)r.value=\"0\";else if(\"number\"===t.type){var i=parseFloat(r.value,10)||0;o!=i&&(r.value=\"\"+o)}else o!=r.value&&(r.value=\"\"+o);else null==t.value&&null!=t.defaultValue&&r.defaultValue!==\"\"+t.defaultValue&&(r.defaultValue=\"\"+t.defaultValue),null==t.checked&&null!=t.defaultChecked&&(r.defaultChecked=!!t.defaultChecked)},postMountWrapper:function(e){var t=e._currentElement.props,n=c.getNodeFromInstance(e);switch(t.type){case\"submit\":case\"reset\":break;case\"color\":case\"date\":case\"datetime\":case\"datetime-local\":case\"month\":case\"time\":case\"week\":n.value=\"\",n.value=n.defaultValue;break;default:n.value=n.value}var r=n.name;\"\"!==r&&(n.name=\"\"),n.defaultChecked=!n.defaultChecked,n.defaultChecked=!n.defaultChecked,\"\"!==r&&(n.name=r)}});t.exports=d},{112:112,12:12,137:137,142:142,143:143,23:23,33:33,71:71}],39:[function(e,t,n){\"use strict\";function r(e){var t=\"\";return i.Children.forEach(e,function(e){null!=e&&(\"string\"==typeof e||\"number\"==typeof e?t+=e:u||(u=!0))}),t}var o=e(143),i=e(120),a=e(33),s=e(40),u=(e(142),!1),l={mountWrapper:function(e,t,n){var o=null;if(null!=n){var i=n;\"optgroup\"===i._tag&&(i=i._hostParent),null!=i&&\"select\"===i._tag&&(o=s.getSelectValueContext(i))}var a=null;if(null!=o){var u;if(u=null!=t.value?t.value+\"\":r(t.children),a=!1,Array.isArray(o)){for(var l=0;l<o.length;l++)if(\"\"+o[l]===u){a=!0;break}}else a=\"\"+o===u}e._wrapperState={selected:a}},postMountWrapper:function(e){var t=e._currentElement.props;null!=t.value&&a.getNodeFromInstance(e).setAttribute(\"value\",t.value)},getHostProps:function(e,t){var n=o({selected:void 0,children:void 0},t);null!=e._wrapperState.selected&&(n.selected=e._wrapperState.selected);var i=r(t.children);return i&&(n.children=i),n}};t.exports=l},{120:120,142:142,143:143,33:33,40:40}],40:[function(e,t,n){\"use strict\";function r(){if(this._rootNodeID&&this._wrapperState.pendingUpdate){this._wrapperState.pendingUpdate=!1;var e=this._currentElement.props,t=s.getValue(e);null!=t&&o(this,Boolean(e.multiple),t)}}function o(e,t,n){var r,o,i=u.getNodeFromInstance(e).options;if(t){for(r={},o=0;o<n.length;o++)r[\"\"+n[o]]=!0;for(o=0;o<i.length;o++){var a=r.hasOwnProperty(i[o].value);i[o].selected!==a&&(i[o].selected=a)}}else{for(r=\"\"+n,o=0;o<i.length;o++)if(i[o].value===r)return void(i[o].selected=!0);i.length&&(i[0].selected=!0)}}function i(e){var t=this._currentElement.props,n=s.executeOnChange(t,e);return this._rootNodeID&&(this._wrapperState.pendingUpdate=!0),l.asap(r,this),n}var a=e(143),s=e(23),u=e(33),l=e(71),c=(e(142),!1),p={getHostProps:function(e,t){return a({},t,{onChange:e._wrapperState.onChange,value:void 0})},mountWrapper:function(e,t){var n=s.getValue(t);e._wrapperState={pendingUpdate:!1,initialValue:null!=n?n:t.defaultValue,listeners:null,onChange:i.bind(e),wasMultiple:Boolean(t.multiple)},void 0===t.value||void 0===t.defaultValue||c||(c=!0)},getSelectValueContext:function(e){return e._wrapperState.initialValue},postUpdateWrapper:function(e){var t=e._currentElement.props;e._wrapperState.initialValue=void 0;var n=e._wrapperState.wasMultiple;e._wrapperState.wasMultiple=Boolean(t.multiple);var r=s.getValue(t);null!=r?(e._wrapperState.pendingUpdate=!1,o(e,Boolean(t.multiple),r)):n!==Boolean(t.multiple)&&(null!=t.defaultValue?o(e,Boolean(t.multiple),t.defaultValue):o(e,Boolean(t.multiple),t.multiple?[]:\"\"))}};t.exports=p},{142:142,143:143,23:23,33:33,71:71}],41:[function(e,t,n){\"use strict\";function r(e,t,n,r){return e===n&&t===r}function o(e){var t=document.selection,n=t.createRange(),r=n.text.length,o=n.duplicate();o.moveToElementText(e),o.setEndPoint(\"EndToStart\",n);var i=o.text.length;return{start:i,end:i+r}}function i(e){var t=window.getSelection&&window.getSelection();if(!t||0===t.rangeCount)return null;var n=t.anchorNode,o=t.anchorOffset,i=t.focusNode,a=t.focusOffset,s=t.getRangeAt(0);try{s.startContainer.nodeType,s.endContainer.nodeType}catch(e){return null}var u=r(t.anchorNode,t.anchorOffset,t.focusNode,t.focusOffset),l=u?0:s.toString().length,c=s.cloneRange();c.selectNodeContents(e),c.setEnd(s.startContainer,s.startOffset);var p=r(c.startContainer,c.startOffset,c.endContainer,c.endOffset),d=p?0:c.toString().length,f=d+l,h=document.createRange();h.setStart(n,o),h.setEnd(i,a);var m=h.collapsed;return{start:m?f:d,end:m?d:f}}function a(e,t){var n,r,o=document.selection.createRange().duplicate();void 0===t.end?(n=t.start,r=n):t.start>t.end?(n=t.end,r=t.start):(n=t.start,r=t.end),o.moveToElementText(e),o.moveStart(\"character\",n),o.setEndPoint(\"EndToStart\",o),o.moveEnd(\"character\",r-n),o.select()}function s(e,t){if(window.getSelection){var n=window.getSelection(),r=e[c()].length,o=Math.min(t.start,r),i=void 0===t.end?o:Math.min(t.end,r);if(!n.extend&&o>i){var a=i;i=o,o=a}var s=l(e,o),u=l(e,i);if(s&&u){var p=document.createRange();p.setStart(s.node,s.offset),n.removeAllRanges(),o>i?(n.addRange(p),n.extend(u.node,u.offset)):(p.setEnd(u.node,u.offset),n.addRange(p))}}}var u=e(123),l=e(105),c=e(106),p=u.canUseDOM&&\"selection\"in document&&!(\"getSelection\"in window),d={getOffsets:p?o:i,setOffsets:p?a:s};t.exports=d},{105:105,106:106,123:123}],42:[function(e,t,n){\"use strict\";var r=e(112),o=e(143),i=e(8),a=e(9),s=e(33),u=e(95),l=(e(137),e(118),function(e){this._currentElement=e,this._stringText=\"\"+e,\nthis._hostNode=null,this._hostParent=null,this._domID=0,this._mountIndex=0,this._closingComment=null,this._commentNodes=null});o(l.prototype,{mountComponent:function(e,t,n,r){var o=n._idCounter++,i=\" react-text: \"+o+\" \";if(this._domID=o,this._hostParent=t,e.useCreateElement){var l=n._ownerDocument,c=l.createComment(i),p=l.createComment(\" /react-text \"),d=a(l.createDocumentFragment());return a.queueChild(d,a(c)),this._stringText&&a.queueChild(d,a(l.createTextNode(this._stringText))),a.queueChild(d,a(p)),s.precacheNode(this,c),this._closingComment=p,d}var f=u(this._stringText);return e.renderToStaticMarkup?f:\"<!--\"+i+\"-->\"+f+\"<!-- /react-text -->\"},receiveComponent:function(e,t){if(e!==this._currentElement){this._currentElement=e;var n=\"\"+e;if(n!==this._stringText){this._stringText=n;var r=this.getHostNode();i.replaceDelimitedText(r[0],r[1],n)}}},getHostNode:function(){var e=this._commentNodes;if(e)return e;if(!this._closingComment)for(var t=s.getNodeFromInstance(this),n=t.nextSibling;;){if(null==n&&r(\"67\",this._domID),8===n.nodeType&&\" /react-text \"===n.nodeValue){this._closingComment=n;break}n=n.nextSibling}return e=[this._hostNode,this._closingComment],this._commentNodes=e,e},unmountComponent:function(){this._closingComment=null,this._commentNodes=null,s.uncacheNode(this)}}),t.exports=l},{112:112,118:118,137:137,143:143,33:33,8:8,9:9,95:95}],43:[function(e,t,n){\"use strict\";function r(){this._rootNodeID&&c.updateWrapper(this)}function o(e){var t=this._currentElement.props,n=s.executeOnChange(t,e);return l.asap(r,this),n}var i=e(112),a=e(143),s=e(23),u=e(33),l=e(71),c=(e(137),e(142),{getHostProps:function(e,t){return null!=t.dangerouslySetInnerHTML&&i(\"91\"),a({},t,{value:void 0,defaultValue:void 0,children:\"\"+e._wrapperState.initialValue,onChange:e._wrapperState.onChange})},mountWrapper:function(e,t){var n=s.getValue(t),r=n;if(null==n){var a=t.defaultValue,u=t.children;null!=u&&(null!=a&&i(\"92\"),Array.isArray(u)&&(u.length<=1||i(\"93\"),u=u[0]),a=\"\"+u),null==a&&(a=\"\"),r=a}e._wrapperState={initialValue:\"\"+r,listeners:null,onChange:o.bind(e)}},updateWrapper:function(e){var t=e._currentElement.props,n=u.getNodeFromInstance(e),r=s.getValue(t);if(null!=r){var o=\"\"+r;o!==n.value&&(n.value=o),null==t.defaultValue&&(n.defaultValue=o)}null!=t.defaultValue&&(n.defaultValue=t.defaultValue)},postMountWrapper:function(e){var t=u.getNodeFromInstance(e),n=t.textContent;n===e._wrapperState.initialValue&&(t.value=n)}});t.exports=c},{112:112,137:137,142:142,143:143,23:23,33:33,71:71}],44:[function(e,t,n){\"use strict\";function r(e,t){\"_hostNode\"in e||u(\"33\"),\"_hostNode\"in t||u(\"33\");for(var n=0,r=e;r;r=r._hostParent)n++;for(var o=0,i=t;i;i=i._hostParent)o++;for(;n-o>0;)e=e._hostParent,n--;for(;o-n>0;)t=t._hostParent,o--;for(var a=n;a--;){if(e===t)return e;e=e._hostParent,t=t._hostParent}return null}function o(e,t){\"_hostNode\"in e||u(\"35\"),\"_hostNode\"in t||u(\"35\");for(;t;){if(t===e)return!0;t=t._hostParent}return!1}function i(e){return\"_hostNode\"in e||u(\"36\"),e._hostParent}function a(e,t,n){for(var r=[];e;)r.push(e),e=e._hostParent;var o;for(o=r.length;o-- >0;)t(r[o],\"captured\",n);for(o=0;o<r.length;o++)t(r[o],\"bubbled\",n)}function s(e,t,n,o,i){for(var a=e&&t?r(e,t):null,s=[];e&&e!==a;)s.push(e),e=e._hostParent;for(var u=[];t&&t!==a;)u.push(t),t=t._hostParent;var l;for(l=0;l<s.length;l++)n(s[l],\"bubbled\",o);for(l=u.length;l-- >0;)n(u[l],\"captured\",i)}var u=e(112);e(137);t.exports={isAncestor:o,getLowestCommonAncestor:r,getParentInstance:i,traverseTwoPhase:a,traverseEnterLeave:s}},{112:112,137:137}],45:[function(e,t,n){\"use strict\";var r=e(120),o=e(30),i=o;r.addons&&(r.__SECRET_INJECTED_REACT_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=i),t.exports=i},{120:120,30:30}],46:[function(e,t,n){\"use strict\";function r(){this.reinitializeTransaction()}var o=e(143),i=e(71),a=e(89),s=e(129),u={initialize:s,close:function(){d.isBatchingUpdates=!1}},l={initialize:s,close:i.flushBatchedUpdates.bind(i)},c=[l,u];o(r.prototype,a,{getTransactionWrappers:function(){return c}});var p=new r,d={isBatchingUpdates:!1,batchedUpdates:function(e,t,n,r,o,i){var a=d.isBatchingUpdates;return d.isBatchingUpdates=!0,a?e(t,n,r,o,i):p.perform(e,null,t,n,r,o,i)}};t.exports=d},{129:129,143:143,71:71,89:89}],47:[function(e,t,n){\"use strict\";function r(){x||(x=!0,y.EventEmitter.injectReactEventListener(g),y.EventPluginHub.injectEventPluginOrder(s),y.EventPluginUtils.injectComponentTree(d),y.EventPluginUtils.injectTreeTraversal(h),y.EventPluginHub.injectEventPluginsByName({SimpleEventPlugin:E,EnterLeaveEventPlugin:u,ChangeEventPlugin:a,SelectEventPlugin:b,BeforeInputEventPlugin:i}),y.HostComponent.injectGenericComponentClass(p),y.HostComponent.injectTextComponentClass(m),y.DOMProperty.injectDOMPropertyConfig(o),y.DOMProperty.injectDOMPropertyConfig(l),y.DOMProperty.injectDOMPropertyConfig(C),y.EmptyComponent.injectEmptyComponentFactory(function(e){return new f(e)}),y.Updates.injectReconcileTransaction(_),y.Updates.injectBatchingStrategy(v),y.Component.injectEnvironment(c))}var o=e(1),i=e(3),a=e(7),s=e(14),u=e(15),l=e(21),c=e(27),p=e(31),d=e(33),f=e(35),h=e(44),m=e(42),v=e(46),g=e(52),y=e(55),_=e(65),C=e(73),b=e(74),E=e(75),x=!1;t.exports={inject:r}},{1:1,14:14,15:15,21:21,27:27,3:3,31:31,33:33,35:35,42:42,44:44,46:46,52:52,55:55,65:65,7:7,73:73,74:74,75:75}],48:[function(e,t,n){\"use strict\";var r=\"function\"==typeof Symbol&&Symbol.for&&Symbol.for(\"react.element\")||60103;t.exports=r},{}],49:[function(e,t,n){\"use strict\";var r,o={injectEmptyComponentFactory:function(e){r=e}},i={create:function(e){return r(e)}};i.injection=o,t.exports=i},{}],50:[function(e,t,n){\"use strict\";function r(e,t,n){try{t(n)}catch(e){null===o&&(o=e)}}var o=null,i={invokeGuardedCallback:r,invokeGuardedCallbackWithCatch:r,rethrowCaughtError:function(){if(o){var e=o;throw o=null,e}}};t.exports=i},{}],51:[function(e,t,n){\"use strict\";function r(e){o.enqueueEvents(e),o.processEventQueue(!1)}var o=e(16),i={handleTopLevel:function(e,t,n,i){r(o.extractEvents(e,t,n,i))}};t.exports=i},{16:16}],52:[function(e,t,n){\"use strict\";function r(e){for(;e._hostParent;)e=e._hostParent;var t=p.getNodeFromInstance(e),n=t.parentNode;return p.getClosestInstanceFromNode(n)}function o(e,t){this.topLevelType=e,this.nativeEvent=t,this.ancestors=[]}function i(e){var t=f(e.nativeEvent),n=p.getClosestInstanceFromNode(t),o=n;do{e.ancestors.push(o),o=o&&r(o)}while(o);for(var i=0;i<e.ancestors.length;i++)n=e.ancestors[i],m._handleTopLevel(e.topLevelType,n,e.nativeEvent,f(e.nativeEvent))}function a(e){e(h(window))}var s=e(143),u=e(122),l=e(123),c=e(24),p=e(33),d=e(71),f=e(102),h=e(134);s(o.prototype,{destructor:function(){this.topLevelType=null,this.nativeEvent=null,this.ancestors.length=0}}),c.addPoolingTo(o,c.twoArgumentPooler);var m={_enabled:!0,_handleTopLevel:null,WINDOW_HANDLE:l.canUseDOM?window:null,setHandleTopLevel:function(e){m._handleTopLevel=e},setEnabled:function(e){m._enabled=!!e},isEnabled:function(){return m._enabled},trapBubbledEvent:function(e,t,n){return n?u.listen(n,t,m.dispatchEvent.bind(null,e)):null},trapCapturedEvent:function(e,t,n){return n?u.capture(n,t,m.dispatchEvent.bind(null,e)):null},monitorScrollValue:function(e){var t=a.bind(null,e);u.listen(window,\"scroll\",t)},dispatchEvent:function(e,t){if(m._enabled){var n=o.getPooled(e,t);try{d.batchedUpdates(i,n)}finally{o.release(n)}}}};t.exports=m},{102:102,122:122,123:123,134:134,143:143,24:24,33:33,71:71}],53:[function(e,t,n){\"use strict\";var r={logTopLevelRenders:!1};t.exports=r},{}],54:[function(e,t,n){\"use strict\";function r(e){return s||a(\"111\",e.type),new s(e)}function o(e){return new u(e)}function i(e){return e instanceof u}var a=e(112),s=(e(137),null),u=null,l={injectGenericComponentClass:function(e){s=e},injectTextComponentClass:function(e){u=e}},c={createInternalComponent:r,createInstanceForText:o,isTextComponent:i,injection:l};t.exports=c},{112:112,137:137}],55:[function(e,t,n){\"use strict\";var r=e(11),o=e(16),i=e(18),a=e(28),s=e(49),u=e(25),l=e(54),c=e(71),p={Component:a.injection,DOMProperty:r.injection,EmptyComponent:s.injection,EventPluginHub:o.injection,EventPluginUtils:i.injection,EventEmitter:u.injection,HostComponent:l.injection,Updates:c.injection};t.exports=p},{11:11,16:16,18:18,25:25,28:28,49:49,54:54,71:71}],56:[function(e,t,n){\"use strict\";function r(e){return i(document.documentElement,e)}var o=e(41),i=e(126),a=e(131),s=e(132),u={hasSelectionCapabilities:function(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(\"input\"===t&&\"text\"===e.type||\"textarea\"===t||\"true\"===e.contentEditable)},getSelectionInformation:function(){var e=s();return{focusedElem:e,selectionRange:u.hasSelectionCapabilities(e)?u.getSelection(e):null}},restoreSelection:function(e){var t=s(),n=e.focusedElem,o=e.selectionRange;t!==n&&r(n)&&(u.hasSelectionCapabilities(n)&&u.setSelection(n,o),a(n))},getSelection:function(e){var t;if(\"selectionStart\"in e)t={start:e.selectionStart,end:e.selectionEnd};else if(document.selection&&e.nodeName&&\"input\"===e.nodeName.toLowerCase()){var n=document.selection.createRange();n.parentElement()===e&&(t={start:-n.moveStart(\"character\",-e.value.length),end:-n.moveEnd(\"character\",-e.value.length)})}else t=o.getOffsets(e);return t||{start:0,end:0}},setSelection:function(e,t){var n=t.start,r=t.end;if(void 0===r&&(r=n),\"selectionStart\"in e)e.selectionStart=n,e.selectionEnd=Math.min(r,e.value.length);else if(document.selection&&e.nodeName&&\"input\"===e.nodeName.toLowerCase()){var i=e.createTextRange();i.collapse(!0),i.moveStart(\"character\",n),i.moveEnd(\"character\",r-n),i.select()}else o.setOffsets(e,t)}};t.exports=u},{126:126,131:131,132:132,41:41}],57:[function(e,t,n){\"use strict\";var r={remove:function(e){e._reactInternalInstance=void 0},get:function(e){return e._reactInternalInstance},has:function(e){return void 0!==e._reactInternalInstance},set:function(e,t){e._reactInternalInstance=t}};t.exports=r},{}],58:[function(e,t,n){\"use strict\";t.exports={debugTool:null}},{}],59:[function(e,t,n){\"use strict\";var r=e(92),o=/^<\\!\\-\\-/,i={CHECKSUM_ATTR_NAME:\"data-react-checksum\",addChecksumToMarkup:function(e){var t=r(e);return o.test(e)?e:e.replace(/\\/?>/,\" \"+i.CHECKSUM_ATTR_NAME+'=\"'+t+'\"$&')},canReuseMarkup:function(e,t){var n=t.getAttribute(i.CHECKSUM_ATTR_NAME);return n=n&&parseInt(n,10),r(e)===n}};t.exports=i},{92:92}],60:[function(e,t,n){\"use strict\";function r(e,t){for(var n=Math.min(e.length,t.length),r=0;r<n;r++)if(e.charAt(r)!==t.charAt(r))return r;return e.length===t.length?-1:n}function o(e){return e?e.nodeType===A?e.documentElement:e.firstChild:null}function i(e){return e.getAttribute&&e.getAttribute(I)||\"\"}function a(e,t,n,r,o){var i;if(b.logTopLevelRenders){var a=e._currentElement.props.child,s=a.type;i=\"React mount: \"+(\"string\"==typeof s?s:s.displayName||s.name),console.time(i)}var u=w.mountComponent(e,n,null,_(e,t),o,0);i&&console.timeEnd(i),e._renderedComponent._topLevelWrapper=e,j._mountImageIntoNode(u,t,e,r,n)}function s(e,t,n,r){var o=k.ReactReconcileTransaction.getPooled(!n&&C.useCreateElement);o.perform(a,null,e,t,o,n,r),k.ReactReconcileTransaction.release(o)}function u(e,t,n){for(w.unmountComponent(e,n),t.nodeType===A&&(t=t.documentElement);t.lastChild;)t.removeChild(t.lastChild)}function l(e){var t=o(e);if(t){var n=y.getInstanceFromNode(t);return!(!n||!n._hostParent)}}function c(e){return!(!e||e.nodeType!==R&&e.nodeType!==A&&e.nodeType!==D)}function p(e){var t=o(e),n=t&&y.getInstanceFromNode(t);return n&&!n._hostParent?n:null}function d(e){var t=p(e);return t?t._hostContainerInfo._topLevelWrapper:null}var f=e(112),h=e(9),m=e(11),v=e(120),g=e(25),y=(e(119),e(33)),_=e(34),C=e(36),b=e(53),E=e(57),x=(e(58),e(59)),w=e(66),T=e(70),k=e(71),P=e(130),S=e(108),N=(e(137),e(114)),M=e(116),I=(e(142),m.ID_ATTRIBUTE_NAME),O=m.ROOT_ATTRIBUTE_NAME,R=1,A=9,D=11,L={},U=1,F=function(){this.rootID=U++};F.prototype.isReactComponent={},F.prototype.render=function(){return this.props.child},F.isReactTopLevelWrapper=!0;var j={TopLevelWrapper:F,_instancesByReactRootID:L,scrollMonitor:function(e,t){t()},_updateRootComponent:function(e,t,n,r,o){return j.scrollMonitor(r,function(){T.enqueueElementInternal(e,t,n),o&&T.enqueueCallbackInternal(e,o)}),e},_renderNewRootComponent:function(e,t,n,r){c(t)||f(\"37\"),g.ensureScrollValueMonitoring();var o=S(e,!1);k.batchedUpdates(s,o,t,n,r);var i=o._instance.rootID;return L[i]=o,o},renderSubtreeIntoContainer:function(e,t,n,r){return null!=e&&E.has(e)||f(\"38\"),j._renderSubtreeIntoContainer(e,t,n,r)},_renderSubtreeIntoContainer:function(e,t,n,r){T.validateCallback(r,\"ReactDOM.render\"),v.isValidElement(t)||f(\"39\",\"string\"==typeof t?\" Instead of passing a string like 'div', pass React.createElement('div') or <div />.\":\"function\"==typeof t?\" Instead of passing a class like Foo, pass React.createElement(Foo) or <Foo />.\":null!=t&&void 0!==t.props?\" This may be caused by unintentionally loading two independent copies of React.\":\"\");var a,s=v.createElement(F,{child:t});if(e){var u=E.get(e);a=u._processChildContext(u._context)}else a=P;var c=d(n);if(c){var p=c._currentElement,h=p.props.child;if(M(h,t)){var m=c._renderedComponent.getPublicInstance(),g=r&&function(){r.call(m)};return j._updateRootComponent(c,s,a,n,g),m}j.unmountComponentAtNode(n)}var y=o(n),_=y&&!!i(y),C=l(n),b=_&&!c&&!C,x=j._renderNewRootComponent(s,n,b,a)._renderedComponent.getPublicInstance();return r&&r.call(x),x},render:function(e,t,n){return j._renderSubtreeIntoContainer(null,e,t,n)},unmountComponentAtNode:function(e){c(e)||f(\"40\");var t=d(e);return t?(delete L[t._instance.rootID],k.batchedUpdates(u,t,e,!1),!0):(l(e),1===e.nodeType&&e.hasAttribute(O),!1)},_mountImageIntoNode:function(e,t,n,i,a){if(c(t)||f(\"41\"),i){var s=o(t);if(x.canReuseMarkup(e,s))return void y.precacheNode(n,s);var u=s.getAttribute(x.CHECKSUM_ATTR_NAME);s.removeAttribute(x.CHECKSUM_ATTR_NAME);var l=s.outerHTML;s.setAttribute(x.CHECKSUM_ATTR_NAME,u);var p=e,d=r(p,l),m=\" (client) \"+p.substring(d-20,d+20)+\"\\n (server) \"+l.substring(d-20,d+20);t.nodeType===A&&f(\"42\",m)}if(t.nodeType===A&&f(\"43\"),a.useCreateElement){for(;t.lastChild;)t.removeChild(t.lastChild);h.insertTreeBefore(t,e,null)}else N(t,e),y.precacheNode(n,t.firstChild)}};t.exports=j},{108:108,11:11,112:112,114:114,116:116,119:119,120:120,130:130,137:137,142:142,25:25,33:33,34:34,36:36,53:53,57:57,58:58,59:59,66:66,70:70,71:71,9:9}],61:[function(e,t,n){\"use strict\";function r(e,t,n){return{type:\"INSERT_MARKUP\",content:e,fromIndex:null,fromNode:null,toIndex:n,afterNode:t}}function o(e,t,n){return{type:\"MOVE_EXISTING\",content:null,fromIndex:e._mountIndex,fromNode:d.getHostNode(e),toIndex:n,afterNode:t}}function i(e,t){return{type:\"REMOVE_NODE\",content:null,fromIndex:e._mountIndex,fromNode:t,toIndex:null,afterNode:null}}function a(e){return{type:\"SET_MARKUP\",content:e,fromIndex:null,fromNode:null,toIndex:null,afterNode:null}}function s(e){return{type:\"TEXT_CONTENT\",content:e,fromIndex:null,fromNode:null,toIndex:null,afterNode:null}}function u(e,t){return t&&(e=e||[],e.push(t)),e}function l(e,t){p.processChildrenUpdates(e,t)}var c=e(112),p=e(28),d=(e(57),e(58),e(119),e(66)),f=e(26),h=(e(129),e(97)),m=(e(137),{Mixin:{_reconcilerInstantiateChildren:function(e,t,n){return f.instantiateChildren(e,t,n)},_reconcilerUpdateChildren:function(e,t,n,r,o,i){var a;return a=h(t,0),f.updateChildren(e,a,n,r,o,this,this._hostContainerInfo,i,0),a},mountChildren:function(e,t,n){var r=this._reconcilerInstantiateChildren(e,t,n);this._renderedChildren=r;var o=[],i=0;for(var a in r)if(r.hasOwnProperty(a)){var s=r[a],u=d.mountComponent(s,t,this,this._hostContainerInfo,n,0);s._mountIndex=i++,o.push(u)}return o},updateTextContent:function(e){var t=this._renderedChildren;f.unmountChildren(t,!1);for(var n in t)t.hasOwnProperty(n)&&c(\"118\");l(this,[s(e)])},updateMarkup:function(e){var t=this._renderedChildren;f.unmountChildren(t,!1);for(var n in t)t.hasOwnProperty(n)&&c(\"118\");l(this,[a(e)])},updateChildren:function(e,t,n){this._updateChildren(e,t,n)},_updateChildren:function(e,t,n){var r=this._renderedChildren,o={},i=[],a=this._reconcilerUpdateChildren(r,e,i,o,t,n);if(a||r){var s,c=null,p=0,f=0,h=0,m=null;for(s in a)if(a.hasOwnProperty(s)){var v=r&&r[s],g=a[s];v===g?(c=u(c,this.moveChild(v,m,p,f)),f=Math.max(v._mountIndex,f),v._mountIndex=p):(v&&(f=Math.max(v._mountIndex,f)),c=u(c,this._mountChildAtIndex(g,i[h],m,p,t,n)),h++),p++,m=d.getHostNode(g)}for(s in o)o.hasOwnProperty(s)&&(c=u(c,this._unmountChild(r[s],o[s])));c&&l(this,c),this._renderedChildren=a}},unmountChildren:function(e){var t=this._renderedChildren;f.unmountChildren(t,e),this._renderedChildren=null},moveChild:function(e,t,n,r){if(e._mountIndex<r)return o(e,t,n)},createChild:function(e,t,n){return r(n,t,e._mountIndex)},removeChild:function(e,t){return i(e,t)},_mountChildAtIndex:function(e,t,n,r,o,i){return e._mountIndex=r,this.createChild(e,n,t)},_unmountChild:function(e,t){var n=this.removeChild(e,t);return e._mountIndex=null,n}}});t.exports=m},{112:112,119:119,129:129,137:137,26:26,28:28,57:57,58:58,66:66,97:97}],62:[function(e,t,n){\"use strict\";var r=e(112),o=e(120),i=(e(137),{HOST:0,COMPOSITE:1,EMPTY:2,getType:function(e){return null===e||!1===e?i.EMPTY:o.isValidElement(e)?\"function\"==typeof e.type?i.COMPOSITE:i.HOST:void r(\"26\",e)}});t.exports=i},{112:112,120:120,137:137}],63:[function(e,t,n){\"use strict\";function r(e){return!(!e||\"function\"!=typeof e.attachRef||\"function\"!=typeof e.detachRef)}var o=e(112),i=(e(137),{addComponentAsRefTo:function(e,t,n){r(n)||o(\"119\"),n.attachRef(t,e)},removeComponentAsRefFrom:function(e,t,n){r(n)||o(\"120\");var i=n.getPublicInstance();i&&i.refs[t]===e.getPublicInstance()&&n.detachRef(t)}});t.exports=i},{112:112,137:137}],64:[function(e,t,n){\"use strict\";t.exports=\"SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED\"},{}],65:[function(e,t,n){\"use strict\";function r(e){this.reinitializeTransaction(),this.renderToStaticMarkup=!1,this.reactMountReady=i.getPooled(null),this.useCreateElement=e}var o=e(143),i=e(6),a=e(24),s=e(25),u=e(56),l=(e(58),e(89)),c=e(70),p={initialize:u.getSelectionInformation,close:u.restoreSelection},d={initialize:function(){var e=s.isEnabled();return s.setEnabled(!1),e},close:function(e){s.setEnabled(e)}},f={initialize:function(){this.reactMountReady.reset()},close:function(){this.reactMountReady.notifyAll()}},h=[p,d,f],m={getTransactionWrappers:function(){return h},getReactMountReady:function(){return this.reactMountReady},getUpdateQueue:function(){return c},checkpoint:function(){return this.reactMountReady.checkpoint()},rollback:function(e){this.reactMountReady.rollback(e)},destructor:function(){i.release(this.reactMountReady),this.reactMountReady=null}};o(r.prototype,l,m),a.addPoolingTo(r),t.exports=r},{143:143,24:24,25:25,56:56,58:58,6:6,70:70,89:89}],66:[function(e,t,n){\"use strict\";function r(){o.attachRefs(this,this._currentElement)}var o=e(67),i=(e(58),e(142),{mountComponent:function(e,t,n,o,i,a){var s=e.mountComponent(t,n,o,i,a);return e._currentElement&&null!=e._currentElement.ref&&t.getReactMountReady().enqueue(r,e),s},getHostNode:function(e){return e.getHostNode()},unmountComponent:function(e,t){o.detachRefs(e,e._currentElement),e.unmountComponent(t)},receiveComponent:function(e,t,n,i){var a=e._currentElement;if(t!==a||i!==e._context){var s=o.shouldUpdateRefs(a,t);s&&o.detachRefs(e,a),e.receiveComponent(t,n,i),s&&e._currentElement&&null!=e._currentElement.ref&&n.getReactMountReady().enqueue(r,e)}},performUpdateIfNecessary:function(e,t,n){e._updateBatchNumber===n&&e.performUpdateIfNecessary(t)}});t.exports=i},{142:142,58:58,67:67}],67:[function(e,t,n){\"use strict\";function r(e,t,n){\"function\"==typeof e?e(t.getPublicInstance()):i.addComponentAsRefTo(t,e,n)}function o(e,t,n){\"function\"==typeof e?e(null):i.removeComponentAsRefFrom(t,e,n)}var i=e(63),a={};a.attachRefs=function(e,t){if(null!==t&&\"object\"==typeof t){var n=t.ref;null!=n&&r(n,e,t._owner)}},a.shouldUpdateRefs=function(e,t){var n=null,r=null;null!==e&&\"object\"==typeof e&&(n=e.ref,r=e._owner);var o=null,i=null;return null!==t&&\"object\"==typeof t&&(o=t.ref,i=t._owner),n!==o||\"string\"==typeof o&&i!==r},a.detachRefs=function(e,t){if(null!==t&&\"object\"==typeof t){var n=t.ref;null!=n&&o(n,e,t._owner)}},t.exports=a},{63:63}],68:[function(e,t,n){\"use strict\";function r(e){this.reinitializeTransaction(),this.renderToStaticMarkup=e,this.useCreateElement=!1,this.updateQueue=new s(this)}var o=e(143),i=e(24),a=e(89),s=(e(58),e(69)),u=[],l={enqueue:function(){}},c={getTransactionWrappers:function(){return u},getReactMountReady:function(){return l},getUpdateQueue:function(){return this.updateQueue},destructor:function(){},checkpoint:function(){},rollback:function(){}};o(r.prototype,a,c),i.addPoolingTo(r),t.exports=r},{143:143,24:24,58:58,69:69,89:89}],69:[function(e,t,n){\"use strict\";function r(e,t){if(!(e instanceof t))throw new TypeError(\"Cannot call a class as a function\")}var o=e(70),i=(e(142),function(){function e(t){r(this,e),this.transaction=t}return e.prototype.isMounted=function(e){return!1},e.prototype.enqueueCallback=function(e,t,n){this.transaction.isInTransaction()&&o.enqueueCallback(e,t,n)},e.prototype.enqueueForceUpdate=function(e){this.transaction.isInTransaction()&&o.enqueueForceUpdate(e)},e.prototype.enqueueReplaceState=function(e,t){this.transaction.isInTransaction()&&o.enqueueReplaceState(e,t)},e.prototype.enqueueSetState=function(e,t){this.transaction.isInTransaction()&&o.enqueueSetState(e,t)},e}());t.exports=i},{142:142,70:70}],70:[function(e,t,n){\"use strict\";function r(e){u.enqueueUpdate(e)}function o(e){var t=typeof e;if(\"object\"!==t)return t;var n=e.constructor&&e.constructor.name||t,r=Object.keys(e);return r.length>0&&r.length<20?n+\" (keys: \"+r.join(\", \")+\")\":n}function i(e,t){var n=s.get(e);return n||null}var a=e(112),s=(e(119),e(57)),u=(e(58),e(71)),l=(e(137),e(142),{isMounted:function(e){var t=s.get(e);return!!t&&!!t._renderedComponent},enqueueCallback:function(e,t,n){l.validateCallback(t,n);var o=i(e);if(!o)return null;o._pendingCallbacks?o._pendingCallbacks.push(t):o._pendingCallbacks=[t],r(o)},enqueueCallbackInternal:function(e,t){e._pendingCallbacks?e._pendingCallbacks.push(t):e._pendingCallbacks=[t],r(e)},enqueueForceUpdate:function(e){var t=i(e,\"forceUpdate\");t&&(t._pendingForceUpdate=!0,r(t))},enqueueReplaceState:function(e,t,n){var o=i(e,\"replaceState\");o&&(o._pendingStateQueue=[t],o._pendingReplaceState=!0,void 0!==n&&null!==n&&(l.validateCallback(n,\"replaceState\"),o._pendingCallbacks?o._pendingCallbacks.push(n):o._pendingCallbacks=[n]),r(o))},enqueueSetState:function(e,t){var n=i(e,\"setState\");n&&((n._pendingStateQueue||(n._pendingStateQueue=[])).push(t),r(n))},enqueueElementInternal:function(e,t,n){e._pendingElement=t,e._context=n,r(e)},validateCallback:function(e,t){e&&\"function\"!=typeof e&&a(\"122\",t,o(e))}});t.exports=l},{112:112,119:119,137:137,142:142,57:57,58:58,71:71}],71:[function(e,t,n){\"use strict\";function r(){P.ReactReconcileTransaction&&b||c(\"123\")}function o(){this.reinitializeTransaction(),this.dirtyComponentsLength=null,this.callbackQueue=d.getPooled(),this.reconcileTransaction=P.ReactReconcileTransaction.getPooled(!0)}function i(e,t,n,o,i,a){return r(),b.batchedUpdates(e,t,n,o,i,a)}function a(e,t){return e._mountOrder-t._mountOrder}function s(e){var t=e.dirtyComponentsLength;t!==g.length&&c(\"124\",t,g.length),g.sort(a),y++;for(var n=0;n<t;n++){var r=g[n],o=r._pendingCallbacks;r._pendingCallbacks=null;var i;if(h.logTopLevelRenders){var s=r;r._currentElement.type.isReactTopLevelWrapper&&(s=r._renderedComponent),i=\"React update: \"+s.getName(),console.time(i)}if(m.performUpdateIfNecessary(r,e.reconcileTransaction,y),i&&console.timeEnd(i),o)for(var u=0;u<o.length;u++)e.callbackQueue.enqueue(o[u],r.getPublicInstance())}}function u(e){if(r(),!b.isBatchingUpdates)return void b.batchedUpdates(u,e);g.push(e),null==e._updateBatchNumber&&(e._updateBatchNumber=y+1)}function l(e,t){b.isBatchingUpdates||c(\"125\"),_.enqueue(e,t),C=!0}var c=e(112),p=e(143),d=e(6),f=e(24),h=e(53),m=e(66),v=e(89),g=(e(137),[]),y=0,_=d.getPooled(),C=!1,b=null,E={initialize:function(){this.dirtyComponentsLength=g.length},close:function(){this.dirtyComponentsLength!==g.length?(g.splice(0,this.dirtyComponentsLength),T()):g.length=0}},x={initialize:function(){this.callbackQueue.reset()},close:function(){this.callbackQueue.notifyAll()}},w=[E,x];p(o.prototype,v,{getTransactionWrappers:function(){return w},destructor:function(){this.dirtyComponentsLength=null,d.release(this.callbackQueue),this.callbackQueue=null,P.ReactReconcileTransaction.release(this.reconcileTransaction),this.reconcileTransaction=null},perform:function(e,t,n){return v.perform.call(this,this.reconcileTransaction.perform,this.reconcileTransaction,e,t,n)}}),f.addPoolingTo(o);var T=function(){for(;g.length||C;){if(g.length){var e=o.getPooled();e.perform(s,null,e),o.release(e)}if(C){C=!1;var t=_;_=d.getPooled(),t.notifyAll(),d.release(t)}}},k={injectReconcileTransaction:function(e){e||c(\"126\"),P.ReactReconcileTransaction=e},injectBatchingStrategy:function(e){e||c(\"127\"),\"function\"!=typeof e.batchedUpdates&&c(\"128\"),\"boolean\"!=typeof e.isBatchingUpdates&&c(\"129\"),b=e}},P={ReactReconcileTransaction:null,batchedUpdates:i,enqueueUpdate:u,flushBatchedUpdates:T,injection:k,asap:l};t.exports=P},{112:112,137:137,143:143,24:24,53:53,6:6,66:66,89:89}],72:[function(e,t,n){\"use strict\";t.exports=\"15.5.4\"},{}],73:[function(e,t,n){\"use strict\";var r={xlink:\"http://www.w3.org/1999/xlink\",xml:\"http://www.w3.org/XML/1998/namespace\"},o={accentHeight:\"accent-height\",accumulate:0,additive:0,alignmentBaseline:\"alignment-baseline\",allowReorder:\"allowReorder\",alphabetic:0,amplitude:0,arabicForm:\"arabic-form\",ascent:0,attributeName:\"attributeName\",attributeType:\"attributeType\",autoReverse:\"autoReverse\",azimuth:0,baseFrequency:\"baseFrequency\",baseProfile:\"baseProfile\",baselineShift:\"baseline-shift\",bbox:0,begin:0,bias:0,by:0,calcMode:\"calcMode\",capHeight:\"cap-height\",clip:0,clipPath:\"clip-path\",clipRule:\"clip-rule\",clipPathUnits:\"clipPathUnits\",colorInterpolation:\"color-interpolation\",colorInterpolationFilters:\"color-interpolation-filters\",colorProfile:\"color-profile\",colorRendering:\"color-rendering\",contentScriptType:\"contentScriptType\",contentStyleType:\"contentStyleType\",cursor:0,cx:0,cy:0,d:0,decelerate:0,descent:0,diffuseConstant:\"diffuseConstant\",direction:0,display:0,divisor:0,dominantBaseline:\"dominant-baseline\",dur:0,dx:0,dy:0,edgeMode:\"edgeMode\",elevation:0,enableBackground:\"enable-background\",end:0,exponent:0,externalResourcesRequired:\"externalResourcesRequired\",fill:0,fillOpacity:\"fill-opacity\",fillRule:\"fill-rule\",filter:0,filterRes:\"filterRes\",filterUnits:\"filterUnits\",floodColor:\"flood-color\",floodOpacity:\"flood-opacity\",focusable:0,fontFamily:\"font-family\",fontSize:\"font-size\",fontSizeAdjust:\"font-size-adjust\",fontStretch:\"font-stretch\",fontStyle:\"font-style\",fontVariant:\"font-variant\",fontWeight:\"font-weight\",format:0,from:0,fx:0,fy:0,g1:0,g2:0,glyphName:\"glyph-name\",glyphOrientationHorizontal:\"glyph-orientation-horizontal\",glyphOrientationVertical:\"glyph-orientation-vertical\",glyphRef:\"glyphRef\",gradientTransform:\"gradientTransform\",gradientUnits:\"gradientUnits\",hanging:0,horizAdvX:\"horiz-adv-x\",horizOriginX:\"horiz-origin-x\",ideographic:0,imageRendering:\"image-rendering\",in:0,in2:0,intercept:0,k:0,k1:0,k2:0,k3:0,k4:0,kernelMatrix:\"kernelMatrix\",kernelUnitLength:\"kernelUnitLength\",kerning:0,keyPoints:\"keyPoints\",keySplines:\"keySplines\",keyTimes:\"keyTimes\",lengthAdjust:\"lengthAdjust\",letterSpacing:\"letter-spacing\",lightingColor:\"lighting-color\",limitingConeAngle:\"limitingConeAngle\",local:0,markerEnd:\"marker-end\",markerMid:\"marker-mid\",markerStart:\"marker-start\",markerHeight:\"markerHeight\",markerUnits:\"markerUnits\",markerWidth:\"markerWidth\",mask:0,maskContentUnits:\"maskContentUnits\",maskUnits:\"maskUnits\",mathematical:0,mode:0,numOctaves:\"numOctaves\",offset:0,opacity:0,operator:0,order:0,orient:0,orientation:0,origin:0,overflow:0,overlinePosition:\"overline-position\",overlineThickness:\"overline-thickness\",paintOrder:\"paint-order\",panose1:\"panose-1\",pathLength:\"pathLength\",patternContentUnits:\"patternContentUnits\",patternTransform:\"patternTransform\",patternUnits:\"patternUnits\",pointerEvents:\"pointer-events\",points:0,pointsAtX:\"pointsAtX\",pointsAtY:\"pointsAtY\",pointsAtZ:\"pointsAtZ\",preserveAlpha:\"preserveAlpha\",preserveAspectRatio:\"preserveAspectRatio\",primitiveUnits:\"primitiveUnits\",r:0,radius:0,refX:\"refX\",refY:\"refY\",renderingIntent:\"rendering-intent\",repeatCount:\"repeatCount\",repeatDur:\"repeatDur\",requiredExtensions:\"requiredExtensions\",requiredFeatures:\"requiredFeatures\",restart:0,result:0,rotate:0,rx:0,ry:0,scale:0,seed:0,shapeRendering:\"shape-rendering\",slope:0,spacing:0,specularConstant:\"specularConstant\",specularExponent:\"specularExponent\",speed:0,spreadMethod:\"spreadMethod\",startOffset:\"startOffset\",stdDeviation:\"stdDeviation\",stemh:0,stemv:0,stitchTiles:\"stitchTiles\",stopColor:\"stop-color\",stopOpacity:\"stop-opacity\",strikethroughPosition:\"strikethrough-position\",strikethroughThickness:\"strikethrough-thickness\",string:0,stroke:0,strokeDasharray:\"stroke-dasharray\",strokeDashoffset:\"stroke-dashoffset\",strokeLinecap:\"stroke-linecap\",strokeLinejoin:\"stroke-linejoin\",strokeMiterlimit:\"stroke-miterlimit\",strokeOpacity:\"stroke-opacity\",strokeWidth:\"stroke-width\",surfaceScale:\"surfaceScale\",systemLanguage:\"systemLanguage\",tableValues:\"tableValues\",targetX:\"targetX\",targetY:\"targetY\",textAnchor:\"text-anchor\",textDecoration:\"text-decoration\",textRendering:\"text-rendering\",textLength:\"textLength\",to:0,transform:0,u1:0,u2:0,underlinePosition:\"underline-position\",underlineThickness:\"underline-thickness\",unicode:0,unicodeBidi:\"unicode-bidi\",unicodeRange:\"unicode-range\",unitsPerEm:\"units-per-em\",vAlphabetic:\"v-alphabetic\",vHanging:\"v-hanging\",vIdeographic:\"v-ideographic\",vMathematical:\"v-mathematical\",values:0,vectorEffect:\"vector-effect\",version:0,vertAdvY:\"vert-adv-y\",vertOriginX:\"vert-origin-x\",vertOriginY:\"vert-origin-y\",viewBox:\"viewBox\",viewTarget:\"viewTarget\",visibility:0,widths:0,wordSpacing:\"word-spacing\",writingMode:\"writing-mode\",x:0,xHeight:\"x-height\",x1:0,x2:0,xChannelSelector:\"xChannelSelector\",xlinkActuate:\"xlink:actuate\",xlinkArcrole:\"xlink:arcrole\",xlinkHref:\"xlink:href\",xlinkRole:\"xlink:role\",xlinkShow:\"xlink:show\",xlinkTitle:\"xlink:title\",xlinkType:\"xlink:type\",xmlBase:\"xml:base\",xmlns:0,xmlnsXlink:\"xmlns:xlink\",xmlLang:\"xml:lang\",xmlSpace:\"xml:space\",y:0,y1:0,y2:0,yChannelSelector:\"yChannelSelector\",z:0,zoomAndPan:\"zoomAndPan\"},i={Properties:{},DOMAttributeNamespaces:{xlinkActuate:r.xlink,xlinkArcrole:r.xlink,xlinkHref:r.xlink,xlinkRole:r.xlink,xlinkShow:r.xlink,xlinkTitle:r.xlink,xlinkType:r.xlink,xmlBase:r.xml,xmlLang:r.xml,xmlSpace:r.xml},DOMAttributeNames:{}};Object.keys(o).forEach(function(e){i.Properties[e]=0,o[e]&&(i.DOMAttributeNames[e]=o[e])}),t.exports=i},{}],74:[function(e,t,n){\"use strict\";function r(e){if(\"selectionStart\"in e&&u.hasSelectionCapabilities(e))return{start:e.selectionStart,end:e.selectionEnd};if(window.getSelection){var t=window.getSelection();return{anchorNode:t.anchorNode,anchorOffset:t.anchorOffset,focusNode:t.focusNode,focusOffset:t.focusOffset}}if(document.selection){var n=document.selection.createRange();return{parentElement:n.parentElement(),text:n.text,top:n.boundingTop,left:n.boundingLeft}}}function o(e,t){if(y||null==m||m!==c())return null;var n=r(m);if(!g||!d(g,n)){g=n;var o=l.getPooled(h.select,v,e,t);return o.type=\"select\",o.target=m,i.accumulateTwoPhaseDispatches(o),o}return null}var i=e(19),a=e(123),s=e(33),u=e(56),l=e(80),c=e(132),p=e(110),d=e(141),f=a.canUseDOM&&\"documentMode\"in document&&document.documentMode<=11,h={select:{phasedRegistrationNames:{bubbled:\"onSelect\",captured:\"onSelectCapture\"},dependencies:[\"topBlur\",\"topContextMenu\",\"topFocus\",\"topKeyDown\",\"topKeyUp\",\"topMouseDown\",\"topMouseUp\",\"topSelectionChange\"]}},m=null,v=null,g=null,y=!1,_=!1,C={eventTypes:h,extractEvents:function(e,t,n,r){if(!_)return null;var i=t?s.getNodeFromInstance(t):window;switch(e){case\"topFocus\":(p(i)||\"true\"===i.contentEditable)&&(m=i,v=t,g=null);break\n;case\"topBlur\":m=null,v=null,g=null;break;case\"topMouseDown\":y=!0;break;case\"topContextMenu\":case\"topMouseUp\":return y=!1,o(n,r);case\"topSelectionChange\":if(f)break;case\"topKeyDown\":case\"topKeyUp\":return o(n,r)}return null},didPutListener:function(e,t,n){\"onSelect\"===t&&(_=!0)}};t.exports=C},{110:110,123:123,132:132,141:141,19:19,33:33,56:56,80:80}],75:[function(e,t,n){\"use strict\";function r(e){return\".\"+e._rootNodeID}function o(e){return\"button\"===e||\"input\"===e||\"select\"===e||\"textarea\"===e}var i=e(112),a=e(122),s=e(19),u=e(33),l=e(76),c=e(77),p=e(80),d=e(81),f=e(83),h=e(84),m=e(79),v=e(85),g=e(86),y=e(87),_=e(88),C=e(129),b=e(99),E=(e(137),{}),x={};[\"abort\",\"animationEnd\",\"animationIteration\",\"animationStart\",\"blur\",\"canPlay\",\"canPlayThrough\",\"click\",\"contextMenu\",\"copy\",\"cut\",\"doubleClick\",\"drag\",\"dragEnd\",\"dragEnter\",\"dragExit\",\"dragLeave\",\"dragOver\",\"dragStart\",\"drop\",\"durationChange\",\"emptied\",\"encrypted\",\"ended\",\"error\",\"focus\",\"input\",\"invalid\",\"keyDown\",\"keyPress\",\"keyUp\",\"load\",\"loadedData\",\"loadedMetadata\",\"loadStart\",\"mouseDown\",\"mouseMove\",\"mouseOut\",\"mouseOver\",\"mouseUp\",\"paste\",\"pause\",\"play\",\"playing\",\"progress\",\"rateChange\",\"reset\",\"scroll\",\"seeked\",\"seeking\",\"stalled\",\"submit\",\"suspend\",\"timeUpdate\",\"touchCancel\",\"touchEnd\",\"touchMove\",\"touchStart\",\"transitionEnd\",\"volumeChange\",\"waiting\",\"wheel\"].forEach(function(e){var t=e[0].toUpperCase()+e.slice(1),n=\"on\"+t,r=\"top\"+t,o={phasedRegistrationNames:{bubbled:n,captured:n+\"Capture\"},dependencies:[r]};E[e]=o,x[r]=o});var w={},T={eventTypes:E,extractEvents:function(e,t,n,r){var o=x[e];if(!o)return null;var a;switch(e){case\"topAbort\":case\"topCanPlay\":case\"topCanPlayThrough\":case\"topDurationChange\":case\"topEmptied\":case\"topEncrypted\":case\"topEnded\":case\"topError\":case\"topInput\":case\"topInvalid\":case\"topLoad\":case\"topLoadedData\":case\"topLoadedMetadata\":case\"topLoadStart\":case\"topPause\":case\"topPlay\":case\"topPlaying\":case\"topProgress\":case\"topRateChange\":case\"topReset\":case\"topSeeked\":case\"topSeeking\":case\"topStalled\":case\"topSubmit\":case\"topSuspend\":case\"topTimeUpdate\":case\"topVolumeChange\":case\"topWaiting\":a=p;break;case\"topKeyPress\":if(0===b(n))return null;case\"topKeyDown\":case\"topKeyUp\":a=f;break;case\"topBlur\":case\"topFocus\":a=d;break;case\"topClick\":if(2===n.button)return null;case\"topDoubleClick\":case\"topMouseDown\":case\"topMouseMove\":case\"topMouseUp\":case\"topMouseOut\":case\"topMouseOver\":case\"topContextMenu\":a=h;break;case\"topDrag\":case\"topDragEnd\":case\"topDragEnter\":case\"topDragExit\":case\"topDragLeave\":case\"topDragOver\":case\"topDragStart\":case\"topDrop\":a=m;break;case\"topTouchCancel\":case\"topTouchEnd\":case\"topTouchMove\":case\"topTouchStart\":a=v;break;case\"topAnimationEnd\":case\"topAnimationIteration\":case\"topAnimationStart\":a=l;break;case\"topTransitionEnd\":a=g;break;case\"topScroll\":a=y;break;case\"topWheel\":a=_;break;case\"topCopy\":case\"topCut\":case\"topPaste\":a=c}a||i(\"86\",e);var u=a.getPooled(o,t,n,r);return s.accumulateTwoPhaseDispatches(u),u},didPutListener:function(e,t,n){if(\"onClick\"===t&&!o(e._tag)){var i=r(e),s=u.getNodeFromInstance(e);w[i]||(w[i]=a.listen(s,\"click\",C))}},willDeleteListener:function(e,t){if(\"onClick\"===t&&!o(e._tag)){var n=r(e);w[n].remove(),delete w[n]}}};t.exports=T},{112:112,122:122,129:129,137:137,19:19,33:33,76:76,77:77,79:79,80:80,81:81,83:83,84:84,85:85,86:86,87:87,88:88,99:99}],76:[function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(80),i={animationName:null,elapsedTime:null,pseudoElement:null};o.augmentClass(r,i),t.exports=r},{80:80}],77:[function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(80),i={clipboardData:function(e){return\"clipboardData\"in e?e.clipboardData:window.clipboardData}};o.augmentClass(r,i),t.exports=r},{80:80}],78:[function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(80),i={data:null};o.augmentClass(r,i),t.exports=r},{80:80}],79:[function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(84),i={dataTransfer:null};o.augmentClass(r,i),t.exports=r},{84:84}],80:[function(e,t,n){\"use strict\";function r(e,t,n,r){this.dispatchConfig=e,this._targetInst=t,this.nativeEvent=n;var o=this.constructor.Interface;for(var i in o)if(o.hasOwnProperty(i)){var s=o[i];s?this[i]=s(n):\"target\"===i?this.target=r:this[i]=n[i]}var u=null!=n.defaultPrevented?n.defaultPrevented:!1===n.returnValue;return this.isDefaultPrevented=u?a.thatReturnsTrue:a.thatReturnsFalse,this.isPropagationStopped=a.thatReturnsFalse,this}var o=e(143),i=e(24),a=e(129),s=(e(142),[\"dispatchConfig\",\"_targetInst\",\"nativeEvent\",\"isDefaultPrevented\",\"isPropagationStopped\",\"_dispatchListeners\",\"_dispatchInstances\"]),u={type:null,target:null,currentTarget:a.thatReturnsNull,eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:null,isTrusted:null};o(r.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():\"unknown\"!=typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=a.thatReturnsTrue)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():\"unknown\"!=typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=a.thatReturnsTrue)},persist:function(){this.isPersistent=a.thatReturnsTrue},isPersistent:a.thatReturnsFalse,destructor:function(){var e=this.constructor.Interface;for(var t in e)this[t]=null;for(var n=0;n<s.length;n++)this[s[n]]=null}}),r.Interface=u,r.augmentClass=function(e,t){var n=this,r=function(){};r.prototype=n.prototype;var a=new r;o(a,e.prototype),e.prototype=a,e.prototype.constructor=e,e.Interface=o({},n.Interface,t),e.augmentClass=n.augmentClass,i.addPoolingTo(e,i.fourArgumentPooler)},i.addPoolingTo(r,i.fourArgumentPooler),t.exports=r},{129:129,142:142,143:143,24:24}],81:[function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(87),i={relatedTarget:null};o.augmentClass(r,i),t.exports=r},{87:87}],82:[function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(80),i={data:null};o.augmentClass(r,i),t.exports=r},{80:80}],83:[function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(87),i=e(99),a=e(100),s=e(101),u={key:a,location:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,repeat:null,locale:null,getModifierState:s,charCode:function(e){return\"keypress\"===e.type?i(e):0},keyCode:function(e){return\"keydown\"===e.type||\"keyup\"===e.type?e.keyCode:0},which:function(e){return\"keypress\"===e.type?i(e):\"keydown\"===e.type||\"keyup\"===e.type?e.keyCode:0}};o.augmentClass(r,u),t.exports=r},{100:100,101:101,87:87,99:99}],84:[function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(87),i=e(90),a=e(101),s={screenX:null,screenY:null,clientX:null,clientY:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,getModifierState:a,button:function(e){var t=e.button;return\"which\"in e?t:2===t?2:4===t?1:0},buttons:null,relatedTarget:function(e){return e.relatedTarget||(e.fromElement===e.srcElement?e.toElement:e.fromElement)},pageX:function(e){return\"pageX\"in e?e.pageX:e.clientX+i.currentScrollLeft},pageY:function(e){return\"pageY\"in e?e.pageY:e.clientY+i.currentScrollTop}};o.augmentClass(r,s),t.exports=r},{101:101,87:87,90:90}],85:[function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(87),i=e(101),a={touches:null,targetTouches:null,changedTouches:null,altKey:null,metaKey:null,ctrlKey:null,shiftKey:null,getModifierState:i};o.augmentClass(r,a),t.exports=r},{101:101,87:87}],86:[function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(80),i={propertyName:null,elapsedTime:null,pseudoElement:null};o.augmentClass(r,i),t.exports=r},{80:80}],87:[function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(80),i=e(102),a={view:function(e){if(e.view)return e.view;var t=i(e);if(t.window===t)return t;var n=t.ownerDocument;return n?n.defaultView||n.parentWindow:window},detail:function(e){return e.detail||0}};o.augmentClass(r,a),t.exports=r},{102:102,80:80}],88:[function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(84),i={deltaX:function(e){return\"deltaX\"in e?e.deltaX:\"wheelDeltaX\"in e?-e.wheelDeltaX:0},deltaY:function(e){return\"deltaY\"in e?e.deltaY:\"wheelDeltaY\"in e?-e.wheelDeltaY:\"wheelDelta\"in e?-e.wheelDelta:0},deltaZ:null,deltaMode:null};o.augmentClass(r,i),t.exports=r},{84:84}],89:[function(e,t,n){\"use strict\";var r=e(112),o=(e(137),{}),i={reinitializeTransaction:function(){this.transactionWrappers=this.getTransactionWrappers(),this.wrapperInitData?this.wrapperInitData.length=0:this.wrapperInitData=[],this._isInTransaction=!1},_isInTransaction:!1,getTransactionWrappers:null,isInTransaction:function(){return!!this._isInTransaction},perform:function(e,t,n,o,i,a,s,u){this.isInTransaction()&&r(\"27\");var l,c;try{this._isInTransaction=!0,l=!0,this.initializeAll(0),c=e.call(t,n,o,i,a,s,u),l=!1}finally{try{if(l)try{this.closeAll(0)}catch(e){}else this.closeAll(0)}finally{this._isInTransaction=!1}}return c},initializeAll:function(e){for(var t=this.transactionWrappers,n=e;n<t.length;n++){var r=t[n];try{this.wrapperInitData[n]=o,this.wrapperInitData[n]=r.initialize?r.initialize.call(this):null}finally{if(this.wrapperInitData[n]===o)try{this.initializeAll(n+1)}catch(e){}}}},closeAll:function(e){this.isInTransaction()||r(\"28\");for(var t=this.transactionWrappers,n=e;n<t.length;n++){var i,a=t[n],s=this.wrapperInitData[n];try{i=!0,s!==o&&a.close&&a.close.call(this,s),i=!1}finally{if(i)try{this.closeAll(n+1)}catch(e){}}}this.wrapperInitData.length=0}};t.exports=i},{112:112,137:137}],90:[function(e,t,n){\"use strict\";var r={currentScrollLeft:0,currentScrollTop:0,refreshScrollValues:function(e){r.currentScrollLeft=e.x,r.currentScrollTop=e.y}};t.exports=r},{}],91:[function(e,t,n){\"use strict\";function r(e,t){return null==t&&o(\"30\"),null==e?t:Array.isArray(e)?Array.isArray(t)?(e.push.apply(e,t),e):(e.push(t),e):Array.isArray(t)?[e].concat(t):[e,t]}var o=e(112);e(137);t.exports=r},{112:112,137:137}],92:[function(e,t,n){\"use strict\";function r(e){for(var t=1,n=0,r=0,i=e.length,a=-4&i;r<a;){for(var s=Math.min(r+4096,a);r<s;r+=4)n+=(t+=e.charCodeAt(r))+(t+=e.charCodeAt(r+1))+(t+=e.charCodeAt(r+2))+(t+=e.charCodeAt(r+3));t%=o,n%=o}for(;r<i;r++)n+=t+=e.charCodeAt(r);return t%=o,n%=o,t|n<<16}var o=65521;t.exports=r},{}],93:[function(e,t,n){\"use strict\";var r=function(e){return\"undefined\"!=typeof MSApp&&MSApp.execUnsafeLocalFunction?function(t,n,r,o){MSApp.execUnsafeLocalFunction(function(){return e(t,n,r,o)})}:e};t.exports=r},{}],94:[function(e,t,n){\"use strict\";function r(e,t,n){return null==t||\"boolean\"==typeof t||\"\"===t?\"\":isNaN(t)||0===t||i.hasOwnProperty(e)&&i[e]?\"\"+t:(\"string\"==typeof t&&(t=t.trim()),t+\"px\")}var o=e(4),i=(e(142),o.isUnitlessNumber);t.exports=r},{142:142,4:4}],95:[function(e,t,n){\"use strict\";function r(e){var t=\"\"+e,n=i.exec(t);if(!n)return t;var r,o=\"\",a=0,s=0;for(a=n.index;a<t.length;a++){switch(t.charCodeAt(a)){case 34:r=\"&quot;\";break;case 38:r=\"&amp;\";break;case 39:r=\"&#x27;\";break;case 60:r=\"&lt;\";break;case 62:r=\"&gt;\";break;default:continue}s!==a&&(o+=t.substring(s,a)),s=a+1,o+=r}return s!==a?o+t.substring(s,a):o}function o(e){return\"boolean\"==typeof e||\"number\"==typeof e?\"\"+e:r(e)}var i=/[\"'&<>]/;t.exports=o},{}],96:[function(e,t,n){\"use strict\";function r(e){if(null==e)return null;if(1===e.nodeType)return e;var t=a.get(e);if(t)return t=s(t),t?i.getNodeFromInstance(t):null;\"function\"==typeof e.render?o(\"44\"):o(\"45\",Object.keys(e))}var o=e(112),i=(e(119),e(33)),a=e(57),s=e(103);e(137),e(142);t.exports=r},{103:103,112:112,119:119,137:137,142:142,33:33,57:57}],97:[function(e,t,n){(function(n){\"use strict\";function r(e,t,n,r){if(e&&\"object\"==typeof e){var o=e;void 0===o[n]&&null!=t&&(o[n]=t)}}function o(e,t){if(null==e)return e;var n={};return i(e,r,n),n}var i=(e(22),e(117));e(142);void 0!==n&&n.env,t.exports=o}).call(this,void 0)},{117:117,142:142,22:22}],98:[function(e,t,n){\"use strict\";function r(e,t,n){Array.isArray(e)?e.forEach(t,n):e&&t.call(n,e)}t.exports=r},{}],99:[function(e,t,n){\"use strict\";function r(e){var t,n=e.keyCode;return\"charCode\"in e?0===(t=e.charCode)&&13===n&&(t=13):t=n,t>=32||13===t?t:0}t.exports=r},{}],100:[function(e,t,n){\"use strict\";function r(e){if(e.key){var t=i[e.key]||e.key;if(\"Unidentified\"!==t)return t}if(\"keypress\"===e.type){var n=o(e);return 13===n?\"Enter\":String.fromCharCode(n)}return\"keydown\"===e.type||\"keyup\"===e.type?a[e.keyCode]||\"Unidentified\":\"\"}var o=e(99),i={Esc:\"Escape\",Spacebar:\" \",Left:\"ArrowLeft\",Up:\"ArrowUp\",Right:\"ArrowRight\",Down:\"ArrowDown\",Del:\"Delete\",Win:\"OS\",Menu:\"ContextMenu\",Apps:\"ContextMenu\",Scroll:\"ScrollLock\",MozPrintableKey:\"Unidentified\"},a={8:\"Backspace\",9:\"Tab\",12:\"Clear\",13:\"Enter\",16:\"Shift\",17:\"Control\",18:\"Alt\",19:\"Pause\",20:\"CapsLock\",27:\"Escape\",32:\" \",33:\"PageUp\",34:\"PageDown\",35:\"End\",36:\"Home\",37:\"ArrowLeft\",38:\"ArrowUp\",39:\"ArrowRight\",40:\"ArrowDown\",45:\"Insert\",46:\"Delete\",112:\"F1\",113:\"F2\",114:\"F3\",115:\"F4\",116:\"F5\",117:\"F6\",118:\"F7\",119:\"F8\",120:\"F9\",121:\"F10\",122:\"F11\",123:\"F12\",144:\"NumLock\",145:\"ScrollLock\",224:\"Meta\"};t.exports=r},{99:99}],101:[function(e,t,n){\"use strict\";function r(e){var t=this,n=t.nativeEvent;if(n.getModifierState)return n.getModifierState(e);var r=i[e];return!!r&&!!n[r]}function o(e){return r}var i={Alt:\"altKey\",Control:\"ctrlKey\",Meta:\"metaKey\",Shift:\"shiftKey\"};t.exports=o},{}],102:[function(e,t,n){\"use strict\";function r(e){var t=e.target||e.srcElement||window;return t.correspondingUseElement&&(t=t.correspondingUseElement),3===t.nodeType?t.parentNode:t}t.exports=r},{}],103:[function(e,t,n){\"use strict\";function r(e){for(var t;(t=e._renderedNodeType)===o.COMPOSITE;)e=e._renderedComponent;return t===o.HOST?e._renderedComponent:t===o.EMPTY?null:void 0}var o=e(62);t.exports=r},{62:62}],104:[function(e,t,n){\"use strict\";function r(e){var t=e&&(o&&e[o]||e[i]);if(\"function\"==typeof t)return t}var o=\"function\"==typeof Symbol&&Symbol.iterator,i=\"@@iterator\";t.exports=r},{}],105:[function(e,t,n){\"use strict\";function r(e){for(;e&&e.firstChild;)e=e.firstChild;return e}function o(e){for(;e;){if(e.nextSibling)return e.nextSibling;e=e.parentNode}}function i(e,t){for(var n=r(e),i=0,a=0;n;){if(3===n.nodeType){if(a=i+n.textContent.length,i<=t&&a>=t)return{node:n,offset:t-i};i=a}n=r(o(n))}}t.exports=i},{}],106:[function(e,t,n){\"use strict\";function r(){return!i&&o.canUseDOM&&(i=\"textContent\"in document.documentElement?\"textContent\":\"innerText\"),i}var o=e(123),i=null;t.exports=r},{123:123}],107:[function(e,t,n){\"use strict\";function r(e,t){var n={};return n[e.toLowerCase()]=t.toLowerCase(),n[\"Webkit\"+e]=\"webkit\"+t,n[\"Moz\"+e]=\"moz\"+t,n[\"ms\"+e]=\"MS\"+t,n[\"O\"+e]=\"o\"+t.toLowerCase(),n}function o(e){if(s[e])return s[e];if(!a[e])return e;var t=a[e];for(var n in t)if(t.hasOwnProperty(n)&&n in u)return s[e]=t[n];return\"\"}var i=e(123),a={animationend:r(\"Animation\",\"AnimationEnd\"),animationiteration:r(\"Animation\",\"AnimationIteration\"),animationstart:r(\"Animation\",\"AnimationStart\"),transitionend:r(\"Transition\",\"TransitionEnd\")},s={},u={};i.canUseDOM&&(u=document.createElement(\"div\").style,\"AnimationEvent\"in window||(delete a.animationend.animation,delete a.animationiteration.animation,delete a.animationstart.animation),\"TransitionEvent\"in window||delete a.transitionend.transition),t.exports=o},{123:123}],108:[function(e,t,n){\"use strict\";function r(e){if(e){var t=e.getName();if(t)return\" Check the render method of `\"+t+\"`.\"}return\"\"}function o(e){return\"function\"==typeof e&&void 0!==e.prototype&&\"function\"==typeof e.prototype.mountComponent&&\"function\"==typeof e.prototype.receiveComponent}function i(e,t){var n;if(null===e||!1===e)n=l.create(i);else if(\"object\"==typeof e){var s=e,u=s.type;if(\"function\"!=typeof u&&\"string\"!=typeof u){var d=\"\";d+=r(s._owner),a(\"130\",null==u?u:typeof u,d)}\"string\"==typeof s.type?n=c.createInternalComponent(s):o(s.type)?(n=new s.type(s),n.getHostNode||(n.getHostNode=n.getNativeNode)):n=new p(s)}else\"string\"==typeof e||\"number\"==typeof e?n=c.createInstanceForText(e):a(\"131\",typeof e);return n._mountIndex=0,n._mountImage=null,n}var a=e(112),s=e(143),u=e(29),l=e(49),c=e(54),p=(e(121),e(137),e(142),function(e){this.construct(e)});s(p.prototype,u,{_instantiateReactComponent:i}),t.exports=i},{112:112,121:121,137:137,142:142,143:143,29:29,49:49,54:54}],109:[function(e,t,n){\"use strict\";function r(e,t){if(!i.canUseDOM||t&&!(\"addEventListener\"in document))return!1;var n=\"on\"+e,r=n in document;if(!r){var a=document.createElement(\"div\");a.setAttribute(n,\"return;\"),r=\"function\"==typeof a[n]}return!r&&o&&\"wheel\"===e&&(r=document.implementation.hasFeature(\"Events.wheel\",\"3.0\")),r}var o,i=e(123);i.canUseDOM&&(o=document.implementation&&document.implementation.hasFeature&&!0!==document.implementation.hasFeature(\"\",\"\")),t.exports=r},{123:123}],110:[function(e,t,n){\"use strict\";function r(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return\"input\"===t?!!o[e.type]:\"textarea\"===t}var o={color:!0,date:!0,datetime:!0,\"datetime-local\":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};t.exports=r},{}],111:[function(e,t,n){\"use strict\";function r(e){return'\"'+o(e)+'\"'}var o=e(95);t.exports=r},{95:95}],112:[function(e,t,n){\"use strict\";function r(e){for(var t=arguments.length-1,n=\"Minified React error #\"+e+\"; visit http://facebook.github.io/react/docs/error-decoder.html?invariant=\"+e,r=0;r<t;r++)n+=\"&args[]=\"+encodeURIComponent(arguments[r+1]);n+=\" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.\";var o=new Error(n);throw o.name=\"Invariant Violation\",o.framesToPop=1,o}t.exports=r},{}],113:[function(e,t,n){\"use strict\";var r=e(60);t.exports=r.renderSubtreeIntoContainer},{60:60}],114:[function(e,t,n){\"use strict\";var r,o=e(123),i=e(10),a=/^[ \\r\\n\\t\\f]/,s=/<(!--|link|noscript|meta|script|style)[ \\r\\n\\t\\f\\/>]/,u=e(93),l=u(function(e,t){if(e.namespaceURI!==i.svg||\"innerHTML\"in e)e.innerHTML=t;else{r=r||document.createElement(\"div\"),r.innerHTML=\"<svg>\"+t+\"</svg>\";for(var n=r.firstChild;n.firstChild;)e.appendChild(n.firstChild)}});if(o.canUseDOM){var c=document.createElement(\"div\");c.innerHTML=\" \",\"\"===c.innerHTML&&(l=function(e,t){if(e.parentNode&&e.parentNode.replaceChild(e,e),a.test(t)||\"<\"===t[0]&&s.test(t)){e.innerHTML=String.fromCharCode(65279)+t;var n=e.firstChild;1===n.data.length?e.removeChild(n):n.deleteData(0,1)}else e.innerHTML=t}),c=null}t.exports=l},{10:10,123:123,93:93}],115:[function(e,t,n){\"use strict\";var r=e(123),o=e(95),i=e(114),a=function(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&3===n.nodeType)return void(n.nodeValue=t)}e.textContent=t};r.canUseDOM&&(\"textContent\"in document.documentElement||(a=function(e,t){if(3===e.nodeType)return void(e.nodeValue=t);i(e,o(t))})),t.exports=a},{114:114,123:123,95:95}],116:[function(e,t,n){\"use strict\";function r(e,t){var n=null===e||!1===e,r=null===t||!1===t;if(n||r)return n===r;var o=typeof e,i=typeof t;return\"string\"===o||\"number\"===o?\"string\"===i||\"number\"===i:\"object\"===i&&e.type===t.type&&e.key===t.key}t.exports=r},{}],117:[function(e,t,n){\"use strict\";function r(e,t){return e&&\"object\"==typeof e&&null!=e.key?l.escape(e.key):t.toString(36)}function o(e,t,n,i){var d=typeof e;if(\"undefined\"!==d&&\"boolean\"!==d||(e=null),null===e||\"string\"===d||\"number\"===d||\"object\"===d&&e.$$typeof===s)return n(i,e,\"\"===t?c+r(e,0):t),1;var f,h,m=0,v=\"\"===t?c:t+p;if(Array.isArray(e))for(var g=0;g<e.length;g++)f=e[g],h=v+r(f,g),m+=o(f,h,n,i);else{var y=u(e);if(y){var _,C=y.call(e);if(y!==e.entries)for(var b=0;!(_=C.next()).done;)f=_.value,h=v+r(f,b++),m+=o(f,h,n,i);else for(;!(_=C.next()).done;){var E=_.value;E&&(f=E[1],h=v+l.escape(E[0])+p+r(f,0),m+=o(f,h,n,i))}}else if(\"object\"===d){var x=String(e);a(\"31\",\"[object Object]\"===x?\"object with keys {\"+Object.keys(e).join(\", \")+\"}\":x,\"\")}}return m}function i(e,t,n){return null==e?0:o(e,\"\",t,n)}var a=e(112),s=(e(119),e(48)),u=e(104),l=(e(137),e(22)),c=(e(142),\".\"),p=\":\";t.exports=i},{104:104,112:112,119:119,137:137,142:142,22:22,48:48}],118:[function(e,t,n){\"use strict\";var r=(e(143),e(129)),o=(e(142),r);t.exports=o},{129:129,142:142,143:143}],119:[function(t,n,r){\"use strict\";var o=e.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;n.exports=o.ReactCurrentOwner},{}],120:[function(t,n,r){\"use strict\";n.exports=e},{}],121:[function(t,n,r){\"use strict\";var o=e.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;n.exports=o.getNextDebugID},{}],122:[function(e,t,n){\"use strict\";var r=e(129),o={listen:function(e,t,n){return e.addEventListener?(e.addEventListener(t,n,!1),{remove:function(){e.removeEventListener(t,n,!1)}}):e.attachEvent?(e.attachEvent(\"on\"+t,n),{remove:function(){e.detachEvent(\"on\"+t,n)}}):void 0},capture:function(e,t,n){return e.addEventListener?(e.addEventListener(t,n,!0),{remove:function(){e.removeEventListener(t,n,!0)}}):{remove:r}},registerDefault:function(){}};t.exports=o},{129:129}],123:[function(e,t,n){\"use strict\";var r=!(\"undefined\"==typeof window||!window.document||!window.document.createElement),o={canUseDOM:r,canUseWorkers:\"undefined\"!=typeof Worker,canUseEventListeners:r&&!(!window.addEventListener&&!window.attachEvent),canUseViewport:r&&!!window.screen,isInWorker:!r};t.exports=o},{}],124:[function(e,t,n){\"use strict\";function r(e){return e.replace(o,function(e,t){return t.toUpperCase()})}var o=/-(.)/g;t.exports=r},{}],125:[function(e,t,n){\"use strict\";function r(e){return o(e.replace(i,\"ms-\"))}var o=e(124),i=/^-ms-/;t.exports=r},{124:124}],126:[function(e,t,n){\"use strict\";function r(e,t){return!(!e||!t)&&(e===t||!o(e)&&(o(t)?r(e,t.parentNode):\"contains\"in e?e.contains(t):!!e.compareDocumentPosition&&!!(16&e.compareDocumentPosition(t))))}var o=e(139);t.exports=r},{139:139}],127:[function(e,t,n){\"use strict\";function r(e){var t=e.length;if((Array.isArray(e)||\"object\"!=typeof e&&\"function\"!=typeof e)&&a(!1),\"number\"!=typeof t&&a(!1),0===t||t-1 in e||a(!1),\"function\"==typeof e.callee&&a(!1),e.hasOwnProperty)try{return Array.prototype.slice.call(e)}catch(e){}for(var n=Array(t),r=0;r<t;r++)n[r]=e[r];return n}function o(e){return!!e&&(\"object\"==typeof e||\"function\"==typeof e)&&\"length\"in e&&!(\"setInterval\"in e)&&\"number\"!=typeof e.nodeType&&(Array.isArray(e)||\"callee\"in e||\"item\"in e)}function i(e){return o(e)?Array.isArray(e)?e.slice():r(e):[e]}var a=e(137);t.exports=i},{137:137}],128:[function(e,t,n){\"use strict\";function r(e){var t=e.match(c);return t&&t[1].toLowerCase()}function o(e,t){var n=l;l||u(!1);var o=r(e),i=o&&s(o);if(i){n.innerHTML=i[1]+e+i[2];for(var c=i[0];c--;)n=n.lastChild}else n.innerHTML=e;var p=n.getElementsByTagName(\"script\");p.length&&(t||u(!1),a(p).forEach(t));for(var d=Array.from(n.childNodes);n.lastChild;)n.removeChild(n.lastChild);return d}var i=e(123),a=e(127),s=e(133),u=e(137),l=i.canUseDOM?document.createElement(\"div\"):null,c=/^\\s*<(\\w+)/;t.exports=o},{123:123,127:127,133:133,137:137}],129:[function(e,t,n){\"use strict\";function r(e){return function(){return e}}var o=function(){};o.thatReturns=r,o.thatReturnsFalse=r(!1),o.thatReturnsTrue=r(!0),o.thatReturnsNull=r(null),o.thatReturnsThis=function(){return this},o.thatReturnsArgument=function(e){return e},t.exports=o},{}],130:[function(e,t,n){\"use strict\";var r={};t.exports=r},{}],131:[function(e,t,n){\"use strict\";function r(e){try{e.focus()}catch(e){}}t.exports=r},{}],132:[function(e,t,n){\"use strict\";function r(e){if(void 0===(e=e||(\"undefined\"!=typeof document?document:void 0)))return null;try{return e.activeElement||e.body}catch(t){return e.body}}t.exports=r},{}],133:[function(e,t,n){\"use strict\";function r(e){return a||i(!1),d.hasOwnProperty(e)||(e=\"*\"),s.hasOwnProperty(e)||(a.innerHTML=\"*\"===e?\"<link />\":\"<\"+e+\"></\"+e+\">\",s[e]=!a.firstChild),s[e]?d[e]:null}var o=e(123),i=e(137),a=o.canUseDOM?document.createElement(\"div\"):null,s={},u=[1,'<select multiple=\"true\">',\"</select>\"],l=[1,\"<table>\",\"</table>\"],c=[3,\"<table><tbody><tr>\",\"</tr></tbody></table>\"],p=[1,'<svg xmlns=\"http://www.w3.org/2000/svg\">',\"</svg>\"],d={\"*\":[1,\"?<div>\",\"</div>\"],area:[1,\"<map>\",\"</map>\"],col:[2,\"<table><tbody></tbody><colgroup>\",\"</colgroup></table>\"],legend:[1,\"<fieldset>\",\"</fieldset>\"],param:[1,\"<object>\",\"</object>\"],tr:[2,\"<table><tbody>\",\"</tbody></table>\"],optgroup:u,option:u,caption:l,colgroup:l,tbody:l,tfoot:l,thead:l,td:c,th:c};[\"circle\",\"clipPath\",\"defs\",\"ellipse\",\"g\",\"image\",\"line\",\"linearGradient\",\"mask\",\"path\",\"pattern\",\"polygon\",\"polyline\",\"radialGradient\",\"rect\",\"stop\",\"text\",\"tspan\"].forEach(function(e){d[e]=p,s[e]=!0}),t.exports=r},{123:123,137:137}],134:[function(e,t,n){\"use strict\";function r(e){return e.Window&&e instanceof e.Window?{x:e.pageXOffset||e.document.documentElement.scrollLeft,y:e.pageYOffset||e.document.documentElement.scrollTop}:{x:e.scrollLeft,y:e.scrollTop}}t.exports=r},{}],135:[function(e,t,n){\"use strict\";function r(e){return e.replace(o,\"-$1\").toLowerCase()}var o=/([A-Z])/g;t.exports=r},{}],136:[function(e,t,n){\"use strict\";function r(e){return o(e).replace(i,\"-ms-\")}var o=e(135),i=/^ms-/;t.exports=r},{135:135}],137:[function(e,t,n){\"use strict\";function r(e,t,n,r,i,a,s,u){if(o(t),!e){var l;if(void 0===t)l=new Error(\"Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.\");else{var c=[n,r,i,a,s,u],p=0;l=new Error(t.replace(/%s/g,function(){return c[p++]})),l.name=\"Invariant Violation\"}throw l.framesToPop=1,l}}var o=function(e){};t.exports=r},{}],138:[function(e,t,n){\"use strict\";function r(e){var t=e?e.ownerDocument||e:document,n=t.defaultView||window;return!(!e||!(\"function\"==typeof n.Node?e instanceof n.Node:\"object\"==typeof e&&\"number\"==typeof e.nodeType&&\"string\"==typeof e.nodeName))}t.exports=r},{}],139:[function(e,t,n){\"use strict\";function r(e){return o(e)&&3==e.nodeType}var o=e(138);t.exports=r},{138:138}],140:[function(e,t,n){\"use strict\";function r(e){var t={};return function(n){return t.hasOwnProperty(n)||(t[n]=e.call(this,n)),t[n]}}t.exports=r},{}],141:[function(e,t,n){\"use strict\";function r(e,t){return e===t?0!==e||0!==t||1/e==1/t:e!==e&&t!==t}function o(e,t){if(r(e,t))return!0;if(\"object\"!=typeof e||null===e||\"object\"!=typeof t||null===t)return!1;var n=Object.keys(e),o=Object.keys(t);if(n.length!==o.length)return!1;for(var a=0;a<n.length;a++)if(!i.call(t,n[a])||!r(e[n[a]],t[n[a]]))return!1;return!0}var i=Object.prototype.hasOwnProperty;t.exports=o},{}],142:[function(e,t,n){\"use strict\";var r=e(129),o=r;t.exports=o},{129:129}],143:[function(e,t,n){\"use strict\";function r(e){if(null===e||void 0===e)throw new TypeError(\"Object.assign cannot be called with null or undefined\");return Object(e)}var o=Object.getOwnPropertySymbols,i=Object.prototype.hasOwnProperty,a=Object.prototype.propertyIsEnumerable;t.exports=function(){try{if(!Object.assign)return!1;var e=new String(\"abc\");if(e[5]=\"de\",\"5\"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t[\"_\"+String.fromCharCode(n)]=n;if(\"0123456789\"!==Object.getOwnPropertyNames(t).map(function(e){return t[e]}).join(\"\"))return!1;var r={};return\"abcdefghijklmnopqrst\".split(\"\").forEach(function(e){r[e]=e}),\"abcdefghijklmnopqrst\"===Object.keys(Object.assign({},r)).join(\"\")}catch(e){return!1}}()?Object.assign:function(e,t){for(var n,s,u=r(e),l=1;l<arguments.length;l++){n=Object(arguments[l]);for(var c in n)i.call(n,c)&&(u[c]=n[c]);if(o){s=o(n);for(var p=0;p<s.length;p++)a.call(n,s[p])&&(u[s[p]]=n[s[p]])}}return u}},{}],144:[function(e,t,n){\"use strict\";function r(e,t,n,r,o){}t.exports=r},{137:137,142:142,147:147}],145:[function(e,t,n){\"use strict\";var r=e(146);t.exports=function(e){return r(e,!1)}},{146:146}],146:[function(e,t,n){\"use strict\";var r=e(129),o=e(137),i=(e(142),e(147)),a=e(144);t.exports=function(e,t){function n(e){var t=e&&(E&&e[E]||e[x]);if(\"function\"==typeof t)return t}function s(e,t){return e===t?0!==e||1/e==1/t:e!==e&&t!==t}function u(e){this.message=e,this.stack=\"\"}function l(e){function n(n,r,a,s,l,c,p){if(s=s||w,c=c||a,p!==i)if(t)o(!1,\"Calling PropTypes validators directly is not supported by the `prop-types` package. Use `PropTypes.checkPropTypes()` to call them. Read more at http://fb.me/use-check-prop-types\");else;return null==r[a]?n?new u(null===r[a]?\"The \"+l+\" `\"+c+\"` is marked as required in `\"+s+\"`, but its value is `null`.\":\"The \"+l+\" `\"+c+\"` is marked as required in `\"+s+\"`, but its value is `undefined`.\"):null:e(r,a,s,l,c)}var r=n.bind(null,!1);return r.isRequired=n.bind(null,!0),r}function c(e){function t(t,n,r,o,i,a){var s=t[n];if(_(s)!==e)return new u(\"Invalid \"+o+\" `\"+i+\"` of type `\"+C(s)+\"` supplied to `\"+r+\"`, expected `\"+e+\"`.\");return null}return l(t)}function p(e){function t(t,n,r,o,a){if(\"function\"!=typeof e)return new u(\"Property `\"+a+\"` of component `\"+r+\"` has invalid PropType notation inside arrayOf.\");var s=t[n];if(!Array.isArray(s)){return new u(\"Invalid \"+o+\" `\"+a+\"` of type `\"+_(s)+\"` supplied to `\"+r+\"`, expected an array.\")}for(var l=0;l<s.length;l++){var c=e(s,l,r,o,a+\"[\"+l+\"]\",i);if(c instanceof Error)return c}return null}return l(t)}function d(e){function t(t,n,r,o,i){if(!(t[n]instanceof e)){var a=e.name||w;return new u(\"Invalid \"+o+\" `\"+i+\"` of type `\"+b(t[n])+\"` supplied to `\"+r+\"`, expected instance of `\"+a+\"`.\")}return null}return l(t)}function f(e){function t(t,n,r,o,i){for(var a=t[n],l=0;l<e.length;l++)if(s(a,e[l]))return null;return new u(\"Invalid \"+o+\" `\"+i+\"` of value `\"+a+\"` supplied to `\"+r+\"`, expected one of \"+JSON.stringify(e)+\".\")}return Array.isArray(e)?l(t):r.thatReturnsNull}function h(e){function t(t,n,r,o,a){if(\"function\"!=typeof e)return new u(\"Property `\"+a+\"` of component `\"+r+\"` has invalid PropType notation inside objectOf.\");var s=t[n],l=_(s);if(\"object\"!==l)return new u(\"Invalid \"+o+\" `\"+a+\"` of type `\"+l+\"` supplied to `\"+r+\"`, expected an object.\");for(var c in s)if(s.hasOwnProperty(c)){var p=e(s,c,r,o,a+\".\"+c,i);if(p instanceof Error)return p}return null}return l(t)}function m(e){function t(t,n,r,o,a){for(var s=0;s<e.length;s++){if(null==(0,e[s])(t,n,r,o,a,i))return null}return new u(\"Invalid \"+o+\" `\"+a+\"` supplied to `\"+r+\"`.\")}return Array.isArray(e)?l(t):r.thatReturnsNull}function v(e){function t(t,n,r,o,a){var s=t[n],l=_(s);if(\"object\"!==l)return new u(\"Invalid \"+o+\" `\"+a+\"` of type `\"+l+\"` supplied to `\"+r+\"`, expected `object`.\");for(var c in e){var p=e[c];if(p){var d=p(s,c,r,o,a+\".\"+c,i);if(d)return d}}return null}return l(t)}function g(t){switch(typeof t){case\"number\":case\"string\":case\"undefined\":return!0;case\"boolean\":return!t;case\"object\":if(Array.isArray(t))return t.every(g);if(null===t||e(t))return!0;var r=n(t);if(!r)return!1;var o,i=r.call(t);if(r!==t.entries){for(;!(o=i.next()).done;)if(!g(o.value))return!1}else for(;!(o=i.next()).done;){var a=o.value;if(a&&!g(a[1]))return!1}return!0;default:return!1}}function y(e,t){return\"symbol\"===e||(\"Symbol\"===t[\"@@toStringTag\"]||\"function\"==typeof Symbol&&t instanceof Symbol)}function _(e){var t=typeof e;return Array.isArray(e)?\"array\":e instanceof RegExp?\"object\":y(t,e)?\"symbol\":t}function C(e){var t=_(e);if(\"object\"===t){if(e instanceof Date)return\"date\";if(e instanceof RegExp)return\"regexp\"}return t}function b(e){return e.constructor&&e.constructor.name?e.constructor.name:w}var E=\"function\"==typeof Symbol&&Symbol.iterator,x=\"@@iterator\",w=\"<<anonymous>>\",T={array:c(\"array\"),bool:c(\"boolean\"),func:c(\"function\"),number:c(\"number\"),object:c(\"object\"),string:c(\"string\"),symbol:c(\"symbol\"),any:function(){return l(r.thatReturnsNull)}(),arrayOf:p,element:function(){function t(t,n,r,o,i){var a=t[n];if(!e(a)){return new u(\"Invalid \"+o+\" `\"+i+\"` of type `\"+_(a)+\"` supplied to `\"+r+\"`, expected a single ReactElement.\")}return null}return l(t)}(),instanceOf:d,node:function(){function e(e,t,n,r,o){return g(e[t])?null:new u(\"Invalid \"+r+\" `\"+o+\"` supplied to `\"+n+\"`, expected a ReactNode.\")}return l(e)}(),objectOf:h,oneOf:f,oneOfType:m,shape:v}\n;return u.prototype=Error.prototype,T.checkPropTypes=a,T.PropTypes=T,T}},{129:129,137:137,142:142,144:144,147:147}],147:[function(e,t,n){\"use strict\";t.exports=\"SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED\"},{}]},{},[45])(45)}()}()});\n/*! http://mths.be/fromcodepoint v0.1.0 by @mathias */\nif (!String.fromCodePoint) {\n  (function() {\n    var defineProperty = (function() {\n      // IE 8 only supports `Object.defineProperty` on DOM elements\n      try {\n        var object = {};\n        var $defineProperty = Object.defineProperty;\n        var result = $defineProperty(object, object, object) && $defineProperty;\n      } catch(error) {}\n      return result;\n    }());\n    var stringFromCharCode = String.fromCharCode;\n    var floor = Math.floor;\n    var fromCodePoint = function() {\n      var MAX_SIZE = 0x4000;\n      var codeUnits = [];\n      var highSurrogate;\n      var lowSurrogate;\n      var index = -1;\n      var length = arguments.length;\n      if (!length) {\n        return '';\n      }\n      var result = '';\n      while (++index < length) {\n        var codePoint = Number(arguments[index]);\n        if (\n          !isFinite(codePoint) ||       // `NaN`, `+Infinity`, or `-Infinity`\n          codePoint < 0 ||              // not a valid Unicode code point\n          codePoint > 0x10FFFF ||       // not a valid Unicode code point\n          floor(codePoint) != codePoint // not an integer\n        ) {\n          throw RangeError('Invalid code point: ' + codePoint);\n        }\n        if (codePoint <= 0xFFFF) { // BMP code point\n          codeUnits.push(codePoint);\n        } else { // Astral code point; split in surrogate halves\n          // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae\n          codePoint -= 0x10000;\n          highSurrogate = (codePoint >> 10) + 0xD800;\n          lowSurrogate = (codePoint % 0x400) + 0xDC00;\n          codeUnits.push(highSurrogate, lowSurrogate);\n        }\n        if (index + 1 == length || codeUnits.length > MAX_SIZE) {\n          result += stringFromCharCode.apply(null, codeUnits);\n          codeUnits.length = 0;\n        }\n      }\n      return result;\n    };\n    if (defineProperty) {\n      defineProperty(String, 'fromCodePoint', {\n        'value': fromCodePoint,\n        'configurable': true,\n        'writable': true\n      });\n    } else {\n      String.fromCodePoint = fromCodePoint;\n    }\n  }());\n}\n\n/*! http://mths.be/codepointat v0.1.0 by @mathias */\nif (!String.prototype.codePointAt) {\n  (function() {\n    'use strict'; // needed to support `apply`/`call` with `undefined`/`null`\n    var codePointAt = function(position) {\n      if (this == null) {\n        throw TypeError();\n      }\n      var string = String(this);\n      var size = string.length;\n      // `ToInteger`\n      var index = position ? Number(position) : 0;\n      if (index != index) { // better `isNaN`\n        index = 0;\n      }\n      // Account for out-of-bounds indices:\n      if (index < 0 || index >= size) {\n        return undefined;\n      }\n      // Get the first code unit\n      var first = string.charCodeAt(index);\n      var second;\n      if ( // check if it’s the start of a surrogate pair\n        first >= 0xD800 && first <= 0xDBFF && // high surrogate\n        size > index + 1 // there is a next code unit\n      ) {\n        second = string.charCodeAt(index + 1);\n        if (second >= 0xDC00 && second <= 0xDFFF) { // low surrogate\n          // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae\n          return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;\n        }\n      }\n      return first;\n    };\n    if (Object.defineProperty) {\n      Object.defineProperty(String.prototype, 'codePointAt', {\n        'value': codePointAt,\n        'configurable': true,\n        'writable': true\n      });\n    } else {\n      String.prototype.codePointAt = codePointAt;\n    }\n  }());\n}\n\nfunction registerAsciinemaPlayerElement() {\n  var AsciinemaPlayerProto = Object.create(HTMLElement.prototype);\n\n  function merge() {\n    var merged = {};\n    for (var i=0; i<arguments.length; i++) {\n      var obj = arguments[i];\n      for (var attrname in obj) {\n        merged[attrname] = obj[attrname];\n      }\n    }\n    return merged;\n  }\n\n  function attribute(element, attrName, optName, defaultValue, coerceFn) {\n    var obj = {};\n    var value = element.getAttribute(attrName);\n    if (value !== null) {\n      if (value === '' && defaultValue !== undefined) {\n        value = defaultValue;\n      } else if (coerceFn) {\n        value = coerceFn(value);\n      }\n      obj[optName] = value;\n    }\n    return obj;\n  };\n\n  function fixEscapeCodes(text) {\n    if (text) {\n      var f = function(match, p1, offset, string) {\n        return String.fromCodePoint(parseInt(p1, 16));\n      };\n\n      return text.\n        replace(/\\\\u([a-z0-9]{4})/gi, f).\n        replace(/\\\\x([a-z0-9]{2})/gi, f).\n        replace(/\\\\e/g, \"\\x1b\");\n    } else {\n      return text;\n    }\n  }\n\n  AsciinemaPlayerProto.createdCallback = function() {\n    var self = this;\n\n    var opts = merge(\n      attribute(this, 'cols', 'width', 0, parseInt),\n      attribute(this, 'rows', 'height', 0, parseInt),\n      attribute(this, 'autoplay', 'autoPlay', true, Boolean),\n      attribute(this, 'preload', 'preload', true, Boolean),\n      attribute(this, 'loop', 'loop', true, Boolean),\n      attribute(this, 'start-at', 'startAt', 0, parseInt),\n      attribute(this, 'speed', 'speed', 1, parseFloat),\n      attribute(this, 'idle-time-limit', 'idleTimeLimit', null, parseFloat),\n      attribute(this, 'poster', 'poster', null, fixEscapeCodes),\n      attribute(this, 'font-size', 'fontSize'),\n      attribute(this, 'theme', 'theme'),\n      attribute(this, 'title', 'title'),\n      attribute(this, 'author', 'author'),\n      attribute(this, 'author-url', 'authorURL'),\n      attribute(this, 'author-img-url', 'authorImgURL'),\n      {\n        onCanPlay: function() {\n          self.dispatchEvent(new CustomEvent(\"loadedmetadata\"));\n          self.dispatchEvent(new CustomEvent(\"loadeddata\"));\n          self.dispatchEvent(new CustomEvent(\"canplay\"));\n          self.dispatchEvent(new CustomEvent(\"canplaythrough\"));\n        },\n\n        onPlay: function() {\n          self.dispatchEvent(new CustomEvent(\"play\"));\n        },\n\n        onPause: function() {\n          self.dispatchEvent(new CustomEvent(\"pause\"));\n        }\n      }\n    );\n\n    this.player = asciinema.player.js.CreatePlayer(this, this.getAttribute('src'), opts);\n  };\n\n  AsciinemaPlayerProto.attachedCallback = function() {\n    var self = this;\n    setTimeout(function() {\n      self.dispatchEvent(new CustomEvent(\"attached\"));\n    }, 0);\n  };\n\n  AsciinemaPlayerProto.detachedCallback = function() {\n    asciinema.player.js.UnmountPlayer(this);\n    this.player = undefined;\n  };\n\n  AsciinemaPlayerProto.play = function() {\n    this.player.play();\n  };\n\n  AsciinemaPlayerProto.pause = function() {\n    this.player.pause();\n  };\n\n  Object.defineProperty(AsciinemaPlayerProto, \"duration\", {\n    get: function() {\n      return this.player.getDuration() || 0;\n    },\n\n    set: function(value) {}\n  });\n\n  Object.defineProperty(AsciinemaPlayerProto, \"currentTime\", {\n    get: function() {\n      return this.player.getCurrentTime();\n    },\n\n    set: function(value) {\n      this.player.setCurrentTime(value);\n    }\n  });\n\n  document.registerElement('asciinema-player', { prototype: AsciinemaPlayerProto });\n};\n\n;(function(){\nvar g,aa=aa||{},ba=this;function ca(a){return\"string\"==typeof a}function da(a,b){var c=a.split(\".\"),d=ba;c[0]in d||!d.execScript||d.execScript(\"var \"+c[0]);for(var e;c.length&&(e=c.shift());)c.length||void 0===b?d=d[e]&&d[e]!==Object.prototype[e]?d[e]:d[e]={}:d[e]=b}function ea(){}\nfunction n(a){var b=typeof a;if(\"object\"==b)if(a){if(a instanceof Array)return\"array\";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(\"[object Window]\"==c)return\"object\";if(\"[object Array]\"==c||\"number\"==typeof a.length&&\"undefined\"!=typeof a.splice&&\"undefined\"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable(\"splice\"))return\"array\";if(\"[object Function]\"==c||\"undefined\"!=typeof a.call&&\"undefined\"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable(\"call\"))return\"function\"}else return\"null\";\nelse if(\"function\"==b&&\"undefined\"==typeof a.call)return\"object\";return b}function fa(a){var b=n(a);return\"array\"==b||\"object\"==b&&\"number\"==typeof a.length}function ha(a){return\"function\"==n(a)}function ia(a){var b=typeof a;return\"object\"==b&&null!=a||\"function\"==b}function ja(a){return a[la]||(a[la]=++ma)}var la=\"closure_uid_\"+(1E9*Math.random()>>>0),ma=0;function na(a,b,c){return a.call.apply(a.bind,arguments)}\nfunction oa(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var c=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(c,d);return a.apply(b,c)}}return function(){return a.apply(b,arguments)}}function pa(a,b,c){pa=Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf(\"native code\")?na:oa;return pa.apply(null,arguments)}\nfunction qa(a,b){function c(){}c.prototype=b.prototype;a.Zd=b.prototype;a.prototype=new c;a.prototype.constructor=a;a.base=function(a,c,f){for(var d=Array(arguments.length-2),e=2;e<arguments.length;e++)d[e-2]=arguments[e];return b.prototype[c].apply(a,d)}};var ra=String.prototype.trim?function(a){return a.trim()}:function(a){return a.replace(/^[\\s\\xa0]+|[\\s\\xa0]+$/g,\"\")},sa=String.prototype.repeat?function(a,b){return a.repeat(b)}:function(a,b){return Array(b+1).join(a)};function ta(a,b){return a<b?-1:a>b?1:0};var ua=Array.prototype.indexOf?function(a,b,c){return Array.prototype.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(ca(a))return ca(b)&&1==b.length?a.indexOf(b,c):-1;for(;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},va=Array.prototype.forEach?function(a,b,c){Array.prototype.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=ca(a)?a.split(\"\"):a,f=0;f<d;f++)f in e&&b.call(c,e[f],f,a)};\nfunction wa(a){a:{var b=xa;for(var c=a.length,d=ca(a)?a.split(\"\"):a,e=0;e<c;e++)if(e in d&&b.call(void 0,d[e],e,a)){b=e;break a}b=-1}return 0>b?null:ca(a)?a.charAt(b):a[b]}function ya(a,b){var c=ua(a,b),d;(d=0<=c)&&Array.prototype.splice.call(a,c,1);return d}function za(a,b){a.sort(b||Aa)}function Ca(a,b){for(var c=Array(a.length),d=0;d<a.length;d++)c[d]={index:d,value:a[d]};var e=b||Aa;za(c,function(a,b){return e(a.value,b.value)||a.index-b.index});for(d=0;d<a.length;d++)a[d]=c[d].value}\nfunction Aa(a,b){return a>b?1:a<b?-1:0};function Da(a){var b=[],c=0,d;for(d in a)b[c++]=a[d];return b}function Ea(a){var b=[],c=0,d;for(d in a)b[c++]=d;return b}var Fa=\"constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf\".split(\" \");function Ia(a,b){for(var c,d,e=1;e<arguments.length;e++){d=arguments[e];for(c in d)a[c]=d[c];for(var f=0;f<Fa.length;f++)c=Fa[f],Object.prototype.hasOwnProperty.call(d,c)&&(a[c]=d[c])}};function Ka(a){if(a.Yc&&\"function\"==typeof a.Yc)return a.Yc();if(ca(a))return a.split(\"\");if(fa(a)){for(var b=[],c=a.length,d=0;d<c;d++)b.push(a[d]);return b}return Da(a)}\nfunction La(a,b){if(a.forEach&&\"function\"==typeof a.forEach)a.forEach(b,void 0);else if(fa(a)||ca(a))va(a,b,void 0);else{if(a.Xc&&\"function\"==typeof a.Xc)var c=a.Xc();else if(a.Yc&&\"function\"==typeof a.Yc)c=void 0;else if(fa(a)||ca(a)){c=[];for(var d=a.length,e=0;e<d;e++)c.push(e)}else c=Ea(a);d=Ka(a);e=d.length;for(var f=0;f<e;f++)b.call(void 0,d[f],c&&c[f],a)}};function Ma(a,b){this.ic={};this.ib=[];this.Fc=0;var c=arguments.length;if(1<c){if(c%2)throw Error(\"Uneven number of arguments\");for(var d=0;d<c;d+=2)this.set(arguments[d],arguments[d+1])}else a&&this.addAll(a)}g=Ma.prototype;g.Yc=function(){Na(this);for(var a=[],b=0;b<this.ib.length;b++)a.push(this.ic[this.ib[b]]);return a};g.Xc=function(){Na(this);return this.ib.concat()};g.Td=function(){return 0==this.Fc};g.clear=function(){this.ic={};this.Fc=this.ib.length=0};\ng.remove=function(a){return Object.prototype.hasOwnProperty.call(this.ic,a)?(delete this.ic[a],this.Fc--,this.ib.length>2*this.Fc&&Na(this),!0):!1};function Na(a){if(a.Fc!=a.ib.length){for(var b=0,c=0;b<a.ib.length;){var d=a.ib[b];Object.prototype.hasOwnProperty.call(a.ic,d)&&(a.ib[c++]=d);b++}a.ib.length=c}if(a.Fc!=a.ib.length){var e={};for(c=b=0;b<a.ib.length;)d=a.ib[b],Object.prototype.hasOwnProperty.call(e,d)||(a.ib[c++]=d,e[d]=1),b++;a.ib.length=c}}\ng.get=function(a,b){return Object.prototype.hasOwnProperty.call(this.ic,a)?this.ic[a]:b};g.set=function(a,b){Object.prototype.hasOwnProperty.call(this.ic,a)||(this.Fc++,this.ib.push(a));this.ic[a]=b};g.addAll=function(a){if(a instanceof Ma){var b=a.Xc();a=a.Yc()}else b=Ea(a),a=Da(a);for(var c=0;c<b.length;c++)this.set(b[c],a[c])};g.forEach=function(a,b){for(var c=this.Xc(),d=0;d<c.length;d++){var e=c[d],f=this.get(e);a.call(b,f,e,this)}};g.clone=function(){return new Ma(this)};var Pa=/^(?:([^:/?#.]+):)?(?:\\/\\/(?:([^/?#]*)@)?([^/#?]*?)(?::([0-9]+))?(?=[/#?]|$))?([^?#]+)?(?:\\?([^#]*))?(?:#([\\s\\S]*))?$/;function Qa(a,b){this.Ma=[];this.Lc=b;for(var c=!0,d=a.length-1;0<=d;d--){var e=a[d]|0;c&&e==b||(this.Ma[d]=e,c=!1)}}var Ra={};function Sa(a){if(-128<=a&&128>a){var b=Ra[a];if(b)return b}b=new Qa([a|0],0>a?-1:0);-128<=a&&128>a&&(Ra[a]=b);return b}function Ta(a){if(isNaN(a)||!isFinite(a))return Ua;if(0>a)return Ta(-a).kb();for(var b=[],c=1,d=0;a>=c;d++)b[d]=a/c|0,c*=Va;return new Qa(b,0)}var Va=4294967296,Ua=Sa(0),Wa=Sa(1),Xa=Sa(16777216);g=Qa.prototype;\ng.Of=function(){return 0<this.Ma.length?this.Ma[0]:this.Lc};g.vd=function(){if(this.Eb())return-this.kb().vd();for(var a=0,b=1,c=0;c<this.Ma.length;c++){var d=Ya(this,c);a+=(0<=d?d:Va+d)*b;b*=Va}return a};\ng.toString=function(a){a=a||10;if(2>a||36<a)throw Error(\"radix out of range: \"+a);if(this.hc())return\"0\";if(this.Eb())return\"-\"+this.kb().toString(a);for(var b=Ta(Math.pow(a,6)),c=this,d=\"\";;){var e=Za(c,b),f=(c.ze(e.multiply(b)).Of()>>>0).toString(a);c=e;if(c.hc())return f+d;for(;6>f.length;)f=\"0\"+f;d=\"\"+f+d}};function Ya(a,b){return 0>b?0:b<a.Ma.length?a.Ma[b]:a.Lc}g.hc=function(){if(0!=this.Lc)return!1;for(var a=0;a<this.Ma.length;a++)if(0!=this.Ma[a])return!1;return!0};\ng.Eb=function(){return-1==this.Lc};g.xf=function(a){return 0<this.compare(a)};g.yf=function(a){return 0<=this.compare(a)};g.Ue=function(){return 0>this.compare(Xa)};g.Ve=function(a){return 0>=this.compare(a)};g.compare=function(a){a=this.ze(a);return a.Eb()?-1:a.hc()?0:1};g.kb=function(){return this.Hf().add(Wa)};\ng.add=function(a){for(var b=Math.max(this.Ma.length,a.Ma.length),c=[],d=0,e=0;e<=b;e++){var f=d+(Ya(this,e)&65535)+(Ya(a,e)&65535),h=(f>>>16)+(Ya(this,e)>>>16)+(Ya(a,e)>>>16);d=h>>>16;f&=65535;h&=65535;c[e]=h<<16|f}return new Qa(c,c[c.length-1]&-2147483648?-1:0)};g.ze=function(a){return this.add(a.kb())};\ng.multiply=function(a){if(this.hc()||a.hc())return Ua;if(this.Eb())return a.Eb()?this.kb().multiply(a.kb()):this.kb().multiply(a).kb();if(a.Eb())return this.multiply(a.kb()).kb();if(this.Ue()&&a.Ue())return Ta(this.vd()*a.vd());for(var b=this.Ma.length+a.Ma.length,c=[],d=0;d<2*b;d++)c[d]=0;for(d=0;d<this.Ma.length;d++)for(var e=0;e<a.Ma.length;e++){var f=Ya(this,d)>>>16,h=Ya(this,d)&65535,k=Ya(a,e)>>>16,l=Ya(a,e)&65535;c[2*d+2*e]+=h*l;ab(c,2*d+2*e);c[2*d+2*e+1]+=f*l;ab(c,2*d+2*e+1);c[2*d+2*e+1]+=\nh*k;ab(c,2*d+2*e+1);c[2*d+2*e+2]+=f*k;ab(c,2*d+2*e+2)}for(d=0;d<b;d++)c[d]=c[2*d+1]<<16|c[2*d];for(d=b;d<2*b;d++)c[d]=0;return new Qa(c,0)};function ab(a,b){for(;(a[b]&65535)!=a[b];)a[b+1]+=a[b]>>>16,a[b]&=65535,b++}\nfunction Za(a,b){if(b.hc())throw Error(\"division by zero\");if(a.hc())return Ua;if(a.Eb())return b.Eb()?Za(a.kb(),b.kb()):Za(a.kb(),b).kb();if(b.Eb())return Za(a,b.kb()).kb();if(30<a.Ma.length){if(a.Eb()||b.Eb())throw Error(\"slowDivide_ only works with positive integers.\");for(var c=Wa,d=b;d.Ve(a);)c=c.shiftLeft(1),d=d.shiftLeft(1);var e=c.ad(1),f=d.ad(1);d=d.ad(2);for(c=c.ad(2);!d.hc();){var h=f.add(d);h.Ve(a)&&(e=e.add(c),f=h);d=d.ad(1);c=c.ad(1)}return e}c=Ua;for(d=a;d.yf(b);){e=Math.max(1,Math.floor(d.vd()/\nb.vd()));f=Math.ceil(Math.log(e)/Math.LN2);f=48>=f?1:Math.pow(2,f-48);h=Ta(e);for(var k=h.multiply(b);k.Eb()||k.xf(d);)e-=f,h=Ta(e),k=h.multiply(b);h.hc()&&(h=Wa);c=c.add(h);d=d.ze(k)}return c}g.Hf=function(){for(var a=this.Ma.length,b=[],c=0;c<a;c++)b[c]=~this.Ma[c];return new Qa(b,~this.Lc)};g.shiftLeft=function(a){var b=a>>5;a%=32;for(var c=this.Ma.length+b+(0<a?1:0),d=[],e=0;e<c;e++)d[e]=0<a?Ya(this,e-b)<<a|Ya(this,e-b-1)>>>32-a:Ya(this,e-b);return new Qa(d,this.Lc)};\ng.ad=function(a){var b=a>>5;a%=32;for(var c=this.Ma.length-b,d=[],e=0;e<c;e++)d[e]=0<a?Ya(this,e+b)>>>a|Ya(this,e+b+1)<<32-a:Ya(this,e+b);return new Qa(d,this.Lc)};function cb(a,b){null!=a&&this.append.apply(this,arguments)}g=cb.prototype;g.xc=\"\";g.set=function(a){this.xc=\"\"+a};g.append=function(a,b,c){this.xc+=String(a);if(null!=b)for(var d=1;d<arguments.length;d++)this.xc+=arguments[d];return this};g.clear=function(){this.xc=\"\"};g.toString=function(){return this.xc};function eb(a){eb[\" \"](a);return a}eb[\" \"]=ea;function fb(a,b){var c=gb;return Object.prototype.hasOwnProperty.call(c,a)?c[a]:c[a]=b(a)};var hb;if(\"undefined\"===typeof q)var q={};if(\"undefined\"===typeof ib)var ib=null;if(\"undefined\"===typeof kb)var kb=null;var lb=null;if(\"undefined\"===typeof mb)var mb=null;function ob(){return new r(null,5,[pb,!0,qb,!0,rb,!1,sb,!1,tb,null],null)}function t(a){return null!=a&&!1!==a}function ub(a){return null==a}function vb(a){return a instanceof Array}function wb(a){return null==a?!0:!1===a?!0:!1}function yb(a){return ca(a)}function Ab(a,b){return a[n(null==b?null:b)]?!0:a._?!0:!1}\nfunction Bb(a){return null==a?null:a.constructor}function Cb(a,b){var c=Bb(b);c=t(t(c)?c.qc:c)?c.Tb:n(b);return Error([\"No protocol method \",a,\" defined for type \",c,\": \",b].join(\"\"))}function Db(a){var b=a.Tb;return t(b)?b:\"\"+v.h(a)}var Fb=\"undefined\"!==typeof Symbol&&\"function\"===n(Symbol)?Symbol.iterator:\"@@iterator\";function Gb(a){for(var b=a.length,c=Array(b),d=0;;)if(d<b)c[d]=a[d],d+=1;else break;return c}\nvar Hb=function Hb(a){switch(arguments.length){case 2:return Hb.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return Hb.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};Hb.c=function(a,b){return a[b]};Hb.A=function(a,b,c){return Kb(Hb,a[b],c)};Hb.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return Hb.A(b,a,c)};Hb.L=2;function Lb(a){return Mb(function(a,c){a.push(c);return a},[],a)}function Nb(){}function Ob(){}\nfunction Pb(){}var Qb=function Qb(a){if(null!=a&&null!=a.W)return a.W(a);var c=Qb[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Qb._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"ICounted.-count\",a);},Rb=function Rb(a){if(null!=a&&null!=a.oa)return a.oa(a);var c=Rb[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Rb._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"IEmptyableCollection.-empty\",a);};function Sb(){}\nvar Tb=function Tb(a,b){if(null!=a&&null!=a.X)return a.X(a,b);var d=Tb[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=Tb._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb(\"ICollection.-conj\",a);};function Ub(){}var A=function A(a){switch(arguments.length){case 2:return A.c(arguments[0],arguments[1]);case 3:return A.l(arguments[0],arguments[1],arguments[2]);default:throw Error([\"Invalid arity: \",v.h(arguments.length)].join(\"\"));}};\nA.c=function(a,b){if(null!=a&&null!=a.$)return a.$(a,b);var c=A[n(null==a?null:a)];if(null!=c)return c.c?c.c(a,b):c.call(null,a,b);c=A._;if(null!=c)return c.c?c.c(a,b):c.call(null,a,b);throw Cb(\"IIndexed.-nth\",a);};A.l=function(a,b,c){if(null!=a&&null!=a.ka)return a.ka(a,b,c);var d=A[n(null==a?null:a)];if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);d=A._;if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);throw Cb(\"IIndexed.-nth\",a);};A.L=3;function Vb(){}\nvar Wb=function Wb(a){if(null!=a&&null!=a.Ia)return a.Ia(a);var c=Wb[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Wb._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"ISeq.-first\",a);},Yb=function Yb(a){if(null!=a&&null!=a.bb)return a.bb(a);var c=Yb[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Yb._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"ISeq.-rest\",a);};function Zb(){}function $b(){}\nvar cc=function cc(a){switch(arguments.length){case 2:return cc.c(arguments[0],arguments[1]);case 3:return cc.l(arguments[0],arguments[1],arguments[2]);default:throw Error([\"Invalid arity: \",v.h(arguments.length)].join(\"\"));}};cc.c=function(a,b){if(null!=a&&null!=a.V)return a.V(a,b);var c=cc[n(null==a?null:a)];if(null!=c)return c.c?c.c(a,b):c.call(null,a,b);c=cc._;if(null!=c)return c.c?c.c(a,b):c.call(null,a,b);throw Cb(\"ILookup.-lookup\",a);};\ncc.l=function(a,b,c){if(null!=a&&null!=a.I)return a.I(a,b,c);var d=cc[n(null==a?null:a)];if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);d=cc._;if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);throw Cb(\"ILookup.-lookup\",a);};cc.L=3;\nvar dc=function dc(a,b){if(null!=a&&null!=a.yc)return a.yc(a,b);var d=dc[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=dc._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb(\"IAssociative.-contains-key?\",a);},ec=function ec(a,b,c){if(null!=a&&null!=a.O)return a.O(a,b,c);var e=ec[n(null==a?null:a)];if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);e=ec._;if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);throw Cb(\"IAssociative.-assoc\",a);};function fc(){}\nvar gc=function gc(a,b){if(null!=a&&null!=a.ga)return a.ga(a,b);var d=gc[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=gc._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb(\"IMap.-dissoc\",a);};function hc(){}\nvar jc=function jc(a){if(null!=a&&null!=a.fd)return a.fd(a);var c=jc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=jc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"IMapEntry.-key\",a);},kc=function kc(a){if(null!=a&&null!=a.gd)return a.gd(a);var c=kc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=kc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"IMapEntry.-val\",a);};function lc(){}\nvar mc=function mc(a,b){if(null!=a&&null!=a.ie)return a.ie(a,b);var d=mc[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=mc._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb(\"ISet.-disjoin\",a);},nc=function nc(a){if(null!=a&&null!=a.Ac)return a.Ac(a);var c=nc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=nc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"IStack.-peek\",a);},oc=function oc(a){if(null!=a&&null!=a.Bc)return a.Bc(a);var c=oc[n(null==\na?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=oc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"IStack.-pop\",a);};function pc(){}\nvar qc=function qc(a,b,c){if(null!=a&&null!=a.dc)return a.dc(a,b,c);var e=qc[n(null==a?null:a)];if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);e=qc._;if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);throw Cb(\"IVector.-assoc-n\",a);},B=function B(a){if(null!=a&&null!=a.pc)return a.pc(a);var c=B[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=B._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"IDeref.-deref\",a);};function rc(){}\nvar sc=function sc(a){if(null!=a&&null!=a.P)return a.P(a);var c=sc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=sc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"IMeta.-meta\",a);},tc=function tc(a,b){if(null!=a&&null!=a.T)return a.T(a,b);var d=tc[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=tc._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb(\"IWithMeta.-with-meta\",a);};function uc(){}\nvar vc=function vc(a){switch(arguments.length){case 2:return vc.c(arguments[0],arguments[1]);case 3:return vc.l(arguments[0],arguments[1],arguments[2]);default:throw Error([\"Invalid arity: \",v.h(arguments.length)].join(\"\"));}};vc.c=function(a,b){if(null!=a&&null!=a.Fa)return a.Fa(a,b);var c=vc[n(null==a?null:a)];if(null!=c)return c.c?c.c(a,b):c.call(null,a,b);c=vc._;if(null!=c)return c.c?c.c(a,b):c.call(null,a,b);throw Cb(\"IReduce.-reduce\",a);};\nvc.l=function(a,b,c){if(null!=a&&null!=a.Ga)return a.Ga(a,b,c);var d=vc[n(null==a?null:a)];if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);d=vc._;if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);throw Cb(\"IReduce.-reduce\",a);};vc.L=3;function wc(){}\nvar yc=function yc(a,b,c){if(null!=a&&null!=a.Qc)return a.Qc(a,b,c);var e=yc[n(null==a?null:a)];if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);e=yc._;if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);throw Cb(\"IKVReduce.-kv-reduce\",a);},zc=function zc(a,b){if(null!=a&&null!=a.K)return a.K(a,b);var d=zc[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=zc._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb(\"IEquiv.-equiv\",a);},Ac=function Ac(a){if(null!=a&&null!=a.U)return a.U(a);\nvar c=Ac[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Ac._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"IHash.-hash\",a);};function Bc(){}var Cc=function Cc(a){if(null!=a&&null!=a.S)return a.S(a);var c=Cc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Cc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"ISeqable.-seq\",a);};function Ec(){}function Fc(){}function Gc(){}function Hc(){}\nvar Ic=function Ic(a){if(null!=a&&null!=a.Rc)return a.Rc(a);var c=Ic[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Ic._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"IReversible.-rseq\",a);},Jc=function Jc(a,b){if(null!=a&&null!=a.Re)return a.Re(0,b);var d=Jc[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=Jc._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb(\"IWriter.-write\",a);};function Kc(){}\nvar Lc=function Lc(a,b,c){if(null!=a&&null!=a.Kd)return a.Kd(a,b,c);var e=Lc[n(null==a?null:a)];if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);e=Lc._;if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);throw Cb(\"IWatchable.-notify-watches\",a);},Mc=function Mc(a,b,c){if(null!=a&&null!=a.Jd)return a.Jd(a,b,c);var e=Mc[n(null==a?null:a)];if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);e=Mc._;if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);throw Cb(\"IWatchable.-add-watch\",a);},Nc=function Nc(a,\nb){if(null!=a&&null!=a.Ld)return a.Ld(a,b);var d=Nc[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=Nc._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb(\"IWatchable.-remove-watch\",a);},Oc=function Oc(a){if(null!=a&&null!=a.Pc)return a.Pc(a);var c=Oc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Oc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"IEditableCollection.-as-transient\",a);},Pc=function Pc(a,b){if(null!=a&&null!=a.Dc)return a.Dc(a,\nb);var d=Pc[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=Pc._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb(\"ITransientCollection.-conj!\",a);},Qc=function Qc(a){if(null!=a&&null!=a.kd)return a.kd(a);var c=Qc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Qc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"ITransientCollection.-persistent!\",a);},Rc=function Rc(a,b,c){if(null!=a&&null!=a.Cc)return a.Cc(a,b,c);var e=Rc[n(null==a?null:a)];if(null!=\ne)return e.l?e.l(a,b,c):e.call(null,a,b,c);e=Rc._;if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);throw Cb(\"ITransientAssociative.-assoc!\",a);};function Tc(){}\nvar Uc=function Uc(a,b){if(null!=a&&null!=a.cc)return a.cc(a,b);var d=Uc[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=Uc._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb(\"IComparable.-compare\",a);},Vc=function Vc(a){if(null!=a&&null!=a.Le)return a.Le();var c=Vc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Vc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"IChunk.-drop-first\",a);},Wc=function Wc(a){if(null!=a&&null!=a.ge)return a.ge(a);\nvar c=Wc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Wc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"IChunkedSeq.-chunked-first\",a);},Xc=function Xc(a){if(null!=a&&null!=a.Hd)return a.Hd(a);var c=Xc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Xc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"IChunkedSeq.-chunked-rest\",a);},Yc=function Yc(a){if(null!=a&&null!=a.hd)return a.hd(a);var c=Yc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,\na);c=Yc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"INamed.-name\",a);},Zc=function Zc(a){if(null!=a&&null!=a.jd)return a.jd(a);var c=Zc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Zc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"INamed.-namespace\",a);},$c=function $c(a,b){if(null!=a&&null!=a.Gb)return a.Gb(a,b);var d=$c[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=$c._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb(\"IReset.-reset!\",\na);},ad=function ad(a){switch(arguments.length){case 2:return ad.c(arguments[0],arguments[1]);case 3:return ad.l(arguments[0],arguments[1],arguments[2]);case 4:return ad.M(arguments[0],arguments[1],arguments[2],arguments[3]);case 5:return ad.Z(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4]);default:throw Error([\"Invalid arity: \",v.h(arguments.length)].join(\"\"));}};\nad.c=function(a,b){if(null!=a&&null!=a.je)return a.je(a,b);var c=ad[n(null==a?null:a)];if(null!=c)return c.c?c.c(a,b):c.call(null,a,b);c=ad._;if(null!=c)return c.c?c.c(a,b):c.call(null,a,b);throw Cb(\"ISwap.-swap!\",a);};ad.l=function(a,b,c){if(null!=a&&null!=a.ke)return a.ke(a,b,c);var d=ad[n(null==a?null:a)];if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);d=ad._;if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);throw Cb(\"ISwap.-swap!\",a);};\nad.M=function(a,b,c,d){if(null!=a&&null!=a.le)return a.le(a,b,c,d);var e=ad[n(null==a?null:a)];if(null!=e)return e.M?e.M(a,b,c,d):e.call(null,a,b,c,d);e=ad._;if(null!=e)return e.M?e.M(a,b,c,d):e.call(null,a,b,c,d);throw Cb(\"ISwap.-swap!\",a);};ad.Z=function(a,b,c,d,e){if(null!=a&&null!=a.me)return a.me(a,b,c,d,e);var f=ad[n(null==a?null:a)];if(null!=f)return f.Z?f.Z(a,b,c,d,e):f.call(null,a,b,c,d,e);f=ad._;if(null!=f)return f.Z?f.Z(a,b,c,d,e):f.call(null,a,b,c,d,e);throw Cb(\"ISwap.-swap!\",a);};\nad.L=5;var bd=function bd(a,b){if(null!=a&&null!=a.Qe)return a.Qe(0,b);var d=bd[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=bd._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb(\"IVolatile.-vreset!\",a);};function cd(){}var dd=function dd(a){if(null!=a&&null!=a.ba)return a.ba(a);var c=dd[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=dd._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"IIterable.-iterator\",a);};\nfunction ed(a){this.Nf=a;this.m=1073741824;this.J=0}ed.prototype.Re=function(a,b){return this.Nf.append(b)};function fd(a){var b=new cb;a.R(null,new ed(b),ob());return\"\"+v.h(b)}var gd=\"undefined\"!==typeof Math.imul&&0!==Math.imul(4294967295,5)?function(a,b){return Math.imul(a,b)}:function(a,b){var c=a&65535,d=b&65535;return c*d+((a>>>16&65535)*d+c*(b>>>16&65535)<<16>>>0)|0};function hd(a){a=gd(a|0,-862048943);return gd(a<<15|a>>>-15,461845907)}\nfunction id(a,b){var c=(a|0)^(b|0);return gd(c<<13|c>>>-13,5)+-430675100|0}function jd(a,b){var c=(a|0)^b;c=gd(c^c>>>16,-2048144789);c=gd(c^c>>>13,-1028477387);return c^c>>>16}function kd(a){a:{var b=1;for(var c=0;;)if(b<a.length){var d=b+2;c=id(c,hd(a.charCodeAt(b-1)|a.charCodeAt(b)<<16));b=d}else{b=c;break a}}b=1===(a.length&1)?b^hd(a.charCodeAt(a.length-1)):b;return jd(b,gd(2,a.length))}var ld={},md=0;\nfunction nd(a){255<md&&(ld={},md=0);if(null==a)return 0;var b=ld[a];if(\"number\"!==typeof b){a:if(null!=a)if(b=a.length,0<b)for(var c=0,d=0;;)if(c<b){var e=c+1;d=gd(31,d)+a.charCodeAt(c);c=e}else{b=d;break a}else b=0;else b=0;ld[a]=b;md+=1}return a=b}\nfunction od(a){if(null!=a&&(a.m&4194304||q===a.Sf))return a.U(null)^0;if(\"number\"===typeof a){if(t(isFinite(a)))return Math.floor(a)%2147483647;switch(a){case Infinity:return 2146435072;case -Infinity:return-1048576;default:return 2146959360}}else return!0===a?a=1231:!1===a?a=1237:\"string\"===typeof a?(a=nd(a),0!==a&&(a=hd(a),a=id(0,a),a=jd(a,4))):a=a instanceof Date?a.valueOf()^0:null==a?0:Ac(a)^0,a}function pd(a,b){return a^b+2654435769+(a<<6)+(a>>2)}function qd(a){return a instanceof rd}\nfunction sd(a,b){if(a.Zb===b.Zb)return 0;var c=wb(a.fb);if(t(c?b.fb:c))return-1;if(t(a.fb)){if(wb(b.fb))return 1;c=Aa(a.fb,b.fb);return 0===c?Aa(a.name,b.name):c}return Aa(a.name,b.name)}function rd(a,b,c,d,e){this.fb=a;this.name=b;this.Zb=c;this.Oc=d;this.hb=e;this.m=2154168321;this.J=4096}g=rd.prototype;g.toString=function(){return this.Zb};g.equiv=function(a){return this.K(null,a)};g.K=function(a,b){return b instanceof rd?this.Zb===b.Zb:!1};\ng.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return D.c(c,this);case 3:return D.l(c,this,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.c=function(a,c){return D.c(c,this)};a.l=function(a,c,d){return D.l(c,this,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return D.c(a,this)};g.c=function(a,b){return D.l(a,this,b)};g.P=function(){return this.hb};\ng.T=function(a,b){return new rd(this.fb,this.name,this.Zb,this.Oc,b)};g.U=function(){var a=this.Oc;return null!=a?a:this.Oc=a=pd(kd(this.name),nd(this.fb))};g.hd=function(){return this.name};g.jd=function(){return this.fb};g.R=function(a,b){return Jc(b,this.Zb)};var td=function td(a){switch(arguments.length){case 1:return td.h(arguments[0]);case 2:return td.c(arguments[0],arguments[1]);default:throw Error([\"Invalid arity: \",v.h(arguments.length)].join(\"\"));}};\ntd.h=function(a){if(a instanceof rd)return a;var b=a.indexOf(\"/\");return 1>b?td.c(null,a):td.c(a.substring(0,b),a.substring(b+1,a.length))};td.c=function(a,b){var c=null!=a?[v.h(a),\"/\",v.h(b)].join(\"\"):b;return new rd(a,b,c,null,null)};td.L=2;function ud(a){return null!=a?a.J&131072||q===a.Tf?!0:a.J?!1:Ab(cd,a):Ab(cd,a)}\nfunction E(a){if(null==a)return null;if(null!=a&&(a.m&8388608||q===a.Pe))return a.S(null);if(vb(a)||\"string\"===typeof a)return 0===a.length?null:new Jb(a,0,null);if(Ab(Bc,a))return Cc(a);throw Error([v.h(a),\" is not ISeqable\"].join(\"\"));}function y(a){if(null==a)return null;if(null!=a&&(a.m&64||q===a.G))return a.Ia(null);a=E(a);return null==a?null:Wb(a)}function vd(a){return null!=a?null!=a&&(a.m&64||q===a.G)?a.bb(null):(a=E(a))?Yb(a):wd:wd}\nfunction z(a){return null==a?null:null!=a&&(a.m&128||q===a.Id)?a.Ka(null):E(vd(a))}var G=function G(a){switch(arguments.length){case 1:return G.h(arguments[0]);case 2:return G.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return G.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};G.h=function(){return!0};G.c=function(a,b){return null==a?null==b:a===b||zc(a,b)};\nG.A=function(a,b,c){for(;;)if(G.c(a,b))if(z(c))a=b,b=y(c),c=z(c);else return G.c(b,y(c));else return!1};G.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return G.A(b,a,c)};G.L=2;function xd(a){this.s=a}xd.prototype.next=function(){if(null!=this.s){var a=y(this.s);this.s=z(this.s);return{value:a,done:!1}}return{value:null,done:!0}};function yd(a){return new xd(E(a))}function zd(a,b){var c=hd(a);c=id(0,c);return jd(c,b)}\nfunction Ad(a){var b=0,c=1;for(a=E(a);;)if(null!=a)b+=1,c=gd(31,c)+od(y(a))|0,a=z(a);else return zd(c,b)}var Cd=zd(1,0);function Dd(a){var b=0,c=0;for(a=E(a);;)if(null!=a)b+=1,c=c+od(y(a))|0,a=z(a);else return zd(c,b)}var Ed=zd(0,0);Pb[\"null\"]=!0;Qb[\"null\"]=function(){return 0};Date.prototype.K=function(a,b){return b instanceof Date&&this.valueOf()===b.valueOf()};Date.prototype.zc=q;\nDate.prototype.cc=function(a,b){if(b instanceof Date)return Aa(this.valueOf(),b.valueOf());throw Error([\"Cannot compare \",v.h(this),\" to \",v.h(b)].join(\"\"));};zc.number=function(a,b){return a===b};Nb[\"function\"]=!0;rc[\"function\"]=!0;sc[\"function\"]=function(){return null};Ac._=function(a){return ja(a)};function Fd(a){return a+1}function Gd(a){this.H=a;this.m=32768;this.J=0}Gd.prototype.pc=function(){return this.H};function Hd(a){return a instanceof Gd}function Id(a){return Hd(a)?a:new Gd(a)}\nfunction Jd(a){return Hd(a)?B(a):a}function Kd(a,b){var c=Qb(a);if(0===c)return b.B?b.B():b.call(null);for(var d=A.c(a,0),e=1;;)if(e<c){var f=A.c(a,e);d=b.c?b.c(d,f):b.call(null,d,f);if(Hd(d))return B(d);e+=1}else return d}function Ld(a,b,c){var d=Qb(a),e=c;for(c=0;;)if(c<d){var f=A.c(a,c);e=b.c?b.c(e,f):b.call(null,e,f);if(Hd(e))return B(e);c+=1}else return e}\nfunction Md(a,b){var c=a.length;if(0===a.length)return b.B?b.B():b.call(null);for(var d=a[0],e=1;;)if(e<c){var f=a[e];d=b.c?b.c(d,f):b.call(null,d,f);if(Hd(d))return B(d);e+=1}else return d}function Nd(a,b,c){var d=a.length,e=c;for(c=0;;)if(c<d){var f=a[c];e=b.c?b.c(e,f):b.call(null,e,f);if(Hd(e))return B(e);c+=1}else return e}function Od(a,b,c,d){for(var e=a.length;;)if(d<e){var f=a[d];c=b.c?b.c(c,f):b.call(null,c,f);if(Hd(c))return B(c);d+=1}else return c}\nfunction Pd(a){return null!=a?a.m&2||q===a.jf?!0:a.m?!1:Ab(Pb,a):Ab(Pb,a)}function Qd(a){return null!=a?a.m&16||q===a.Ne?!0:a.m?!1:Ab(Ub,a):Ab(Ub,a)}function Ud(a,b,c){var d=H(a);if(c>=d)return-1;!(0<c)&&0>c&&(c+=d,c=0>c?0:c);for(;;)if(c<d){if(G.c(Vd(a,c),b))return c;c+=1}else return-1}function Xd(a,b,c){var d=H(a);if(0===d)return-1;0<c?(--d,c=d<c?d:c):c=0>c?d+c:c;for(;;)if(0<=c){if(G.c(Vd(a,c),b))return c;--c}else return-1}function Yd(a,b){this.o=a;this.i=b}\nYd.prototype.ja=function(){return this.i<this.o.length};Yd.prototype.next=function(){var a=this.o[this.i];this.i+=1;return a};function Jb(a,b,c){this.o=a;this.i=b;this.meta=c;this.m=166592766;this.J=139264}g=Jb.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};\ng.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();\ng.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.$=function(a,b){var c=b+this.i;if(0<=c&&c<this.o.length)return this.o[c];throw Error(\"Index out of bounds\");};g.ka=function(a,b,c){a=b+this.i;return 0<=a&&a<this.o.length?this.o[a]:c};\ng.ba=function(){return new Yd(this.o,this.i)};g.P=function(){return this.meta};g.Ka=function(){return this.i+1<this.o.length?new Jb(this.o,this.i+1,null):null};g.W=function(){var a=this.o.length-this.i;return 0>a?0:a};g.Rc=function(){var a=this.W(null);return 0<a?new Zd(this,a-1,null):null};g.U=function(){return Ad(this)};g.K=function(a,b){return $d(this,b)};g.oa=function(){return wd};g.Fa=function(a,b){return Od(this.o,b,this.o[this.i],this.i+1)};g.Ga=function(a,b,c){return Od(this.o,b,c,this.i)};\ng.Ia=function(){return this.o[this.i]};g.bb=function(){return this.i+1<this.o.length?new Jb(this.o,this.i+1,null):wd};g.S=function(){return this.i<this.o.length?this:null};g.T=function(a,b){return new Jb(this.o,this.i,b)};g.X=function(a,b){return ae(b,this)};Jb.prototype[Fb]=function(){return yd(this)};function be(a){return 0<a.length?new Jb(a,0,null):null}function Zd(a,b,c){this.Gd=a;this.i=b;this.meta=c;this.m=32374990;this.J=8192}g=Zd.prototype;g.toString=function(){return fd(this)};\ng.equiv=function(a){return this.K(null,a)};g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();\ng.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.meta};g.Ka=function(){return 0<this.i?new Zd(this.Gd,this.i-1,null):null};g.W=function(){return this.i+1};g.U=function(){return Ad(this)};g.K=function(a,b){return $d(this,b)};\ng.oa=function(){return tc(wd,this.meta)};g.Fa=function(a,b){return ce(b,this)};g.Ga=function(a,b,c){return de(b,c,this)};g.Ia=function(){return A.c(this.Gd,this.i)};g.bb=function(){return 0<this.i?new Zd(this.Gd,this.i-1,null):wd};g.S=function(){return this};g.T=function(a,b){return new Zd(this.Gd,this.i,b)};g.X=function(a,b){return ae(b,this)};Zd.prototype[Fb]=function(){return yd(this)};function ee(a){return y(z(a))}function fe(a){for(;;){var b=z(a);if(null!=b)a=b;else return y(a)}}\nzc._=function(a,b){return a===b};var ge=function ge(a){switch(arguments.length){case 0:return ge.B();case 1:return ge.h(arguments[0]);case 2:return ge.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return ge.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};ge.B=function(){return he};ge.h=function(a){return a};ge.c=function(a,b){return null!=a?Tb(a,b):Tb(wd,b)};\nge.A=function(a,b,c){for(;;)if(t(c))a=ge.c(a,b),b=y(c),c=z(c);else return ge.c(a,b)};ge.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return ge.A(b,a,c)};ge.L=2;function ie(a){return null==a?null:Rb(a)}function H(a){if(null!=a)if(null!=a&&(a.m&2||q===a.jf))a=a.W(null);else if(vb(a))a=a.length;else if(\"string\"===typeof a)a=a.length;else if(null!=a&&(a.m&8388608||q===a.Pe))a:{a=E(a);for(var b=0;;){if(Pd(a)){a=b+Qb(a);break a}a=z(a);b+=1}}else a=Qb(a);else a=0;return a}\nfunction je(a,b,c){for(;;){if(null==a)return c;if(0===b)return E(a)?y(a):c;if(Qd(a))return A.l(a,b,c);if(E(a))a=z(a),--b;else return c}}\nfunction Vd(a,b){if(\"number\"!==typeof b)throw Error(\"Index argument to nth must be a number\");if(null==a)return a;if(null!=a&&(a.m&16||q===a.Ne))return a.$(null,b);if(vb(a)){if(0<=b&&b<a.length)return a[b];throw Error(\"Index out of bounds\");}if(\"string\"===typeof a){if(0<=b&&b<a.length)return a.charAt(b);throw Error(\"Index out of bounds\");}if(null!=a&&(a.m&64||q===a.G)){a:{var c=a;for(var d=b;;){if(null==c)throw Error(\"Index out of bounds\");if(0===d){if(E(c)){c=y(c);break a}throw Error(\"Index out of bounds\");\n}if(Qd(c)){c=A.c(c,d);break a}if(E(c))c=z(c),--d;else throw Error(\"Index out of bounds\");}}return c}if(Ab(Ub,a))return A.c(a,b);throw Error([\"nth not supported on this type \",v.h(Db(Bb(a)))].join(\"\"));}\nfunction J(a,b,c){if(\"number\"!==typeof b)throw Error(\"Index argument to nth must be a number.\");if(null==a)return c;if(null!=a&&(a.m&16||q===a.Ne))return a.ka(null,b,c);if(vb(a))return 0<=b&&b<a.length?a[b]:c;if(\"string\"===typeof a)return 0<=b&&b<a.length?a.charAt(b):c;if(null!=a&&(a.m&64||q===a.G))return je(a,b,c);if(Ab(Ub,a))return A.l(a,b,c);throw Error([\"nth not supported on this type \",v.h(Db(Bb(a)))].join(\"\"));}\nvar D=function D(a){switch(arguments.length){case 2:return D.c(arguments[0],arguments[1]);case 3:return D.l(arguments[0],arguments[1],arguments[2]);default:throw Error([\"Invalid arity: \",v.h(arguments.length)].join(\"\"));}};D.c=function(a,b){return null==a?null:null!=a&&(a.m&256||q===a.rf)?a.V(null,b):vb(a)?null!=b&&b<a.length?a[b|0]:null:\"string\"===typeof a?null!=b&&b<a.length?a.charAt(b|0):null:Ab($b,a)?cc.c(a,b):null};\nD.l=function(a,b,c){return null!=a?null!=a&&(a.m&256||q===a.rf)?a.I(null,b,c):vb(a)?null!=b&&0<=b&&b<a.length?a[b|0]:c:\"string\"===typeof a?null!=b&&0<=b&&b<a.length?a.charAt(b|0):c:Ab($b,a)?cc.l(a,b,c):c:c};D.L=3;var K=function K(a){switch(arguments.length){case 3:return K.l(arguments[0],arguments[1],arguments[2]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return K.A(arguments[0],arguments[1],arguments[2],new Jb(c.slice(3),0,null))}};\nK.l=function(a,b,c){return null!=a?ec(a,b,c):ke([b,c])};K.A=function(a,b,c,d){for(;;)if(a=K.l(a,b,c),t(d))b=y(d),c=ee(d),d=z(z(d));else return a};K.N=function(a){var b=y(a),c=z(a);a=y(c);var d=z(c);c=y(d);d=z(d);return K.A(b,a,c,d)};K.L=3;\nvar le=function le(a){switch(arguments.length){case 1:return le.h(arguments[0]);case 2:return le.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return le.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};le.h=function(a){return a};le.c=function(a,b){return null==a?null:gc(a,b)};le.A=function(a,b,c){for(;;){if(null==a)return null;a=le.c(a,b);if(t(c))b=y(c),c=z(c);else return a}};\nle.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return le.A(b,a,c)};le.L=2;function me(a){var b=ha(a);return b?b:null!=a?q===a.hf?!0:a.Tc?!1:Ab(Nb,a):Ab(Nb,a)}function ne(a,b){this.C=a;this.meta=b;this.m=393217;this.J=0}g=ne.prototype;g.P=function(){return this.meta};g.T=function(a,b){return new ne(this.C,b)};g.hf=q;\ng.call=function(){function a(a,b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M,S,X,Q,Ga){return oe(this.C,b,c,d,e,be([f,h,k,m,l,p,u,w,x,C,F,I,M,S,X,Q,Ga]))}function b(a,b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M,S,X,Q){a=this;return a.C.Xa?a.C.Xa(b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M,S,X,Q):a.C.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M,S,X,Q)}function c(a,b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M,S,X){a=this;return a.C.Wa?a.C.Wa(b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M,S,X):a.C.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M,S,X)}function d(a,\nb,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M,S){a=this;return a.C.Va?a.C.Va(b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M,S):a.C.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M,S)}function e(a,b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M){a=this;return a.C.Ua?a.C.Ua(b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M):a.C.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M)}function f(a,b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I){a=this;return a.C.Ta?a.C.Ta(b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I):a.C.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I)}function h(a,b,c,d,e,f,h,k,m,l,p,u,\nw,x,C,F){a=this;return a.C.Sa?a.C.Sa(b,c,d,e,f,h,k,m,l,p,u,w,x,C,F):a.C.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,C,F)}function k(a,b,c,d,e,f,h,k,m,l,p,u,w,x,C){a=this;return a.C.Ra?a.C.Ra(b,c,d,e,f,h,k,m,l,p,u,w,x,C):a.C.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,C)}function l(a,b,c,d,e,f,h,k,m,l,p,u,w,x){a=this;return a.C.Qa?a.C.Qa(b,c,d,e,f,h,k,m,l,p,u,w,x):a.C.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x)}function p(a,b,c,d,e,f,h,k,m,l,p,u,w){a=this;return a.C.Pa?a.C.Pa(b,c,d,e,f,h,k,m,l,p,u,w):a.C.call(null,b,c,d,\ne,f,h,k,m,l,p,u,w)}function m(a,b,c,d,e,f,h,k,m,l,p,u){a=this;return a.C.Oa?a.C.Oa(b,c,d,e,f,h,k,m,l,p,u):a.C.call(null,b,c,d,e,f,h,k,m,l,p,u)}function u(a,b,c,d,e,f,h,k,m,l,p){a=this;return a.C.Na?a.C.Na(b,c,d,e,f,h,k,m,l,p):a.C.call(null,b,c,d,e,f,h,k,m,l,p)}function w(a,b,c,d,e,f,h,k,m,l){a=this;return a.C.Za?a.C.Za(b,c,d,e,f,h,k,m,l):a.C.call(null,b,c,d,e,f,h,k,m,l)}function x(a,b,c,d,e,f,h,k,m){a=this;return a.C.Ha?a.C.Ha(b,c,d,e,f,h,k,m):a.C.call(null,b,c,d,e,f,h,k,m)}function C(a,b,c,d,e,f,\nh,k){a=this;return a.C.Ya?a.C.Ya(b,c,d,e,f,h,k):a.C.call(null,b,c,d,e,f,h,k)}function F(a,b,c,d,e,f,h){a=this;return a.C.Ca?a.C.Ca(b,c,d,e,f,h):a.C.call(null,b,c,d,e,f,h)}function I(a,b,c,d,e,f){a=this;return a.C.Z?a.C.Z(b,c,d,e,f):a.C.call(null,b,c,d,e,f)}function M(a,b,c,d,e){a=this;return a.C.M?a.C.M(b,c,d,e):a.C.call(null,b,c,d,e)}function S(a,b,c,d){a=this;return a.C.l?a.C.l(b,c,d):a.C.call(null,b,c,d)}function X(a,b,c){a=this;return a.C.c?a.C.c(b,c):a.C.call(null,b,c)}function Ga(a,b){a=this;\nreturn a.C.h?a.C.h(b):a.C.call(null,b)}function db(a){a=this;return a.C.B?a.C.B():a.C.call(null)}var Q=null;Q=function(xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Q,Xb,ic,xc,Sc,Bd,se,Lf,Ih,kl){switch(arguments.length){case 1:return db.call(this,xb);case 2:return Ga.call(this,xb,Ha);case 3:return X.call(this,xb,Ha,Ja);case 4:return S.call(this,xb,Ha,Ja,Oa);case 5:return M.call(this,xb,Ha,Ja,Oa,Ba);case 6:return I.call(this,xb,Ha,Ja,Oa,Ba,W);case 7:return F.call(this,xb,Ha,Ja,Oa,Ba,W,$a);case 8:return C.call(this,\nxb,Ha,Ja,Oa,Ba,W,$a,ka);case 9:return x.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb);case 10:return w.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb);case 11:return u.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb);case 12:return m.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib);case 13:return p.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Q);case 14:return l.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Q,Xb);case 15:return k.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Q,Xb,ic);case 16:return h.call(this,xb,Ha,Ja,Oa,Ba,\nW,$a,ka,jb,nb,zb,Ib,Q,Xb,ic,xc);case 17:return f.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Q,Xb,ic,xc,Sc);case 18:return e.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Q,Xb,ic,xc,Sc,Bd);case 19:return d.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Q,Xb,ic,xc,Sc,Bd,se);case 20:return c.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Q,Xb,ic,xc,Sc,Bd,se,Lf);case 21:return b.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Q,Xb,ic,xc,Sc,Bd,se,Lf,Ih);case 22:return a.call(this,0,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,\nzb,Ib,Q,Xb,ic,xc,Sc,Bd,se,Lf,Ih,kl)}throw Error(\"Invalid arity: \"+(arguments.length-1));};Q.h=db;Q.c=Ga;Q.l=X;Q.M=S;Q.Z=M;Q.Ca=I;Q.Ya=F;Q.Ha=C;Q.Za=x;Q.Na=w;Q.Oa=u;Q.Pa=m;Q.Qa=p;Q.Ra=l;Q.Sa=k;Q.Ta=h;Q.Ua=f;Q.Va=e;Q.Wa=d;Q.Xa=c;Q.he=b;Q.qf=a;return Q}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.B=function(){return this.C.B?this.C.B():this.C.call(null)};g.h=function(a){return this.C.h?this.C.h(a):this.C.call(null,a)};\ng.c=function(a,b){return this.C.c?this.C.c(a,b):this.C.call(null,a,b)};g.l=function(a,b,c){return this.C.l?this.C.l(a,b,c):this.C.call(null,a,b,c)};g.M=function(a,b,c,d){return this.C.M?this.C.M(a,b,c,d):this.C.call(null,a,b,c,d)};g.Z=function(a,b,c,d,e){return this.C.Z?this.C.Z(a,b,c,d,e):this.C.call(null,a,b,c,d,e)};g.Ca=function(a,b,c,d,e,f){return this.C.Ca?this.C.Ca(a,b,c,d,e,f):this.C.call(null,a,b,c,d,e,f)};\ng.Ya=function(a,b,c,d,e,f,h){return this.C.Ya?this.C.Ya(a,b,c,d,e,f,h):this.C.call(null,a,b,c,d,e,f,h)};g.Ha=function(a,b,c,d,e,f,h,k){return this.C.Ha?this.C.Ha(a,b,c,d,e,f,h,k):this.C.call(null,a,b,c,d,e,f,h,k)};g.Za=function(a,b,c,d,e,f,h,k,l){return this.C.Za?this.C.Za(a,b,c,d,e,f,h,k,l):this.C.call(null,a,b,c,d,e,f,h,k,l)};g.Na=function(a,b,c,d,e,f,h,k,l,p){return this.C.Na?this.C.Na(a,b,c,d,e,f,h,k,l,p):this.C.call(null,a,b,c,d,e,f,h,k,l,p)};\ng.Oa=function(a,b,c,d,e,f,h,k,l,p,m){return this.C.Oa?this.C.Oa(a,b,c,d,e,f,h,k,l,p,m):this.C.call(null,a,b,c,d,e,f,h,k,l,p,m)};g.Pa=function(a,b,c,d,e,f,h,k,l,p,m,u){return this.C.Pa?this.C.Pa(a,b,c,d,e,f,h,k,l,p,m,u):this.C.call(null,a,b,c,d,e,f,h,k,l,p,m,u)};g.Qa=function(a,b,c,d,e,f,h,k,l,p,m,u,w){return this.C.Qa?this.C.Qa(a,b,c,d,e,f,h,k,l,p,m,u,w):this.C.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w)};\ng.Ra=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x){return this.C.Ra?this.C.Ra(a,b,c,d,e,f,h,k,l,p,m,u,w,x):this.C.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x)};g.Sa=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C){return this.C.Sa?this.C.Sa(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C):this.C.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C)};g.Ta=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F){return this.C.Ta?this.C.Ta(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F):this.C.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F)};\ng.Ua=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I){return this.C.Ua?this.C.Ua(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I):this.C.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I)};g.Va=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M){return this.C.Va?this.C.Va(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M):this.C.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M)};\ng.Wa=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S){return this.C.Wa?this.C.Wa(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S):this.C.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S)};g.Xa=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X){return this.C.Xa?this.C.Xa(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X):this.C.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X)};g.he=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga){return oe(this.C,a,b,c,d,be([e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga]))};\nfunction pe(a,b){return ha(a)?new ne(a,b):null==a?null:tc(a,b)}function qe(a){var b=null!=a;return(b?null!=a?a.m&131072||q===a.tf||(a.m?0:Ab(rc,a)):Ab(rc,a):b)?sc(a):null}var re=function re(a){switch(arguments.length){case 1:return re.h(arguments[0]);case 2:return re.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return re.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};re.h=function(a){return a};\nre.c=function(a,b){return null==a?null:mc(a,b)};re.A=function(a,b,c){for(;;){if(null==a)return null;a=re.c(a,b);if(t(c))b=y(c),c=z(c);else return a}};re.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return re.A(b,a,c)};re.L=2;function te(a){return null==a||wb(E(a))}function ue(a){return null==a?!1:null!=a?a.m&8||q===a.Qf?!0:a.m?!1:Ab(Sb,a):Ab(Sb,a)}function ve(a){return null==a?!1:null!=a?a.m&4096||q===a.$f?!0:a.m?!1:Ab(lc,a):Ab(lc,a)}\nfunction we(a){return null!=a?a.m&16777216||q===a.Zf?!0:a.m?!1:Ab(Ec,a):Ab(Ec,a)}function xe(a){return null==a?!1:null!=a?a.m&1024||q===a.Wf?!0:a.m?!1:Ab(fc,a):Ab(fc,a)}function ye(a){return null!=a?a.m&67108864||q===a.Xf?!0:a.m?!1:Ab(Gc,a):Ab(Gc,a)}function ze(a){return null!=a?a.m&16384||q===a.ag?!0:a.m?!1:Ab(pc,a):Ab(pc,a)}function Ae(a){return null!=a?a.J&512||q===a.Pf?!0:!1:!1}function Be(a,b,c,d,e){for(;0!==e;)c[d]=a[b],d+=1,--e,b+=1}var Ce={};\nfunction De(a){return null==a?!1:null!=a?a.m&64||q===a.G?!0:a.m?!1:Ab(Vb,a):Ab(Vb,a)}function Ee(a){return null==a?!1:!1===a?!1:!0}function Fe(a){var b=me(a);return b?b:null!=a?a.m&1||q===a.Rf?!0:a.m?!1:Ab(Ob,a):Ab(Ob,a)}function Ge(a){return\"number\"===typeof a&&!isNaN(a)&&Infinity!==a&&parseFloat(a)===parseInt(a,10)}function He(a,b){return D.l(a,b,Ce)===Ce?!1:!0}\nvar Ie=function Ie(a){switch(arguments.length){case 1:return Ie.h(arguments[0]);case 2:return Ie.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return Ie.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};Ie.h=function(){return!0};Ie.c=function(a,b){return!G.c(a,b)};Ie.A=function(a,b,c){if(G.c(a,b))return!1;a=Je([a,b]);for(b=c;;){var d=y(b);c=z(b);if(t(b)){if(He(a,d))return!1;a=ge.c(a,d);b=c}else return!0}};\nIe.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return Ie.A(b,a,c)};Ie.L=2;function Ke(a,b){if(a===b)return 0;if(null==a)return-1;if(null==b)return 1;if(\"number\"===typeof a){if(\"number\"===typeof b)return Aa(a,b);throw Error([\"Cannot compare \",v.h(a),\" to \",v.h(b)].join(\"\"));}if(null!=a?a.J&2048||q===a.zc||(a.J?0:Ab(Tc,a)):Ab(Tc,a))return Uc(a,b);if(\"string\"!==typeof a&&!vb(a)&&!0!==a&&!1!==a||Bb(a)!==Bb(b))throw Error([\"Cannot compare \",v.h(a),\" to \",v.h(b)].join(\"\"));return Aa(a,b)}\nfunction Le(a,b){var c=H(a),d=H(b);if(c<d)c=-1;else if(c>d)c=1;else if(0===c)c=0;else a:for(d=0;;){var e=Ke(Vd(a,d),Vd(b,d));if(0===e&&d+1<c)d+=1;else{c=e;break a}}return c}function Me(a){return G.c(a,Ke)?Ke:function(b,c){var d=a.c?a.c(b,c):a.call(null,b,c);return\"number\"===typeof d?d:t(d)?-1:t(a.c?a.c(c,b):a.call(null,c,b))?1:0}}function Ne(a,b){if(E(b)){a:{var c=[];for(var d=E(b);;)if(null!=d)c.push(y(d)),d=z(d);else break a}d=Me(a);Ca(c,d);return E(c)}return wd}\nfunction Oe(a){var b=Pe(\"@!\\\"#%\\x26'*+-/:[{\\x3c\\\\|\\x3d]}\\x3e^~?\".split(\"\"),\"_CIRCA_ _BANG_ _DOUBLEQUOTE_ _SHARP_ _PERCENT_ _AMPERSAND_ _SINGLEQUOTE_ _STAR_ _PLUS_ _ _SLASH_ _COLON_ _LBRACK_ _LBRACE_ _LT_ _BSLASH_ _BAR_ _EQ_ _RBRACK_ _RBRACE_ _GT_ _CARET_ _TILDE_ _QMARK_\".split(\" \"));return Qe(a,b)}function Qe(a,b){return Ne(function(b,d){var c=a.h?a.h(b):a.call(null,b),f=a.h?a.h(d):a.call(null,d),h=Me(Ke);return h.c?h.c(c,f):h.call(null,c,f)},b)}\nfunction ce(a,b){var c=E(b);return c?Mb(a,y(c),z(c)):a.B?a.B():a.call(null)}function de(a,b,c){for(c=E(c);;)if(c){var d=y(c);b=a.c?a.c(b,d):a.call(null,b,d);if(Hd(b))return B(b);c=z(c)}else return b}function Re(a,b){var c=dd(a);if(t(c.ja()))for(var d=c.next();;)if(c.ja()){var e=c.next();d=b.c?b.c(d,e):b.call(null,d,e);if(Hd(d))return B(d)}else return d;else return b.B?b.B():b.call(null)}\nfunction Se(a,b,c){for(a=dd(a);;)if(a.ja()){var d=a.next();c=b.c?b.c(c,d):b.call(null,c,d);if(Hd(c))return B(c)}else return c}function Te(a,b){return null!=b&&(b.m&524288||q===b.uf)?b.Fa(null,a):vb(b)?Md(b,a):\"string\"===typeof b?Md(b,a):Ab(uc,b)?vc.c(b,a):ud(b)?Re(b,a):ce(a,b)}function Mb(a,b,c){return null!=c&&(c.m&524288||q===c.uf)?c.Ga(null,a,b):vb(c)?Nd(c,a,b):\"string\"===typeof c?Nd(c,a,b):Ab(uc,c)?vc.l(c,a,b):ud(c)?Se(c,a,b):de(a,b,c)}function Ue(a,b,c){return null!=c?yc(c,a,b):b}\nfunction Ve(a){return a}function We(a,b,c,d){a=a.h?a.h(b):a.call(null,b);c=Mb(a,c,d);return a.h?a.h(c):a.call(null,c)}var Xe=function Xe(a){switch(arguments.length){case 0:return Xe.B();case 1:return Xe.h(arguments[0]);case 2:return Xe.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return Xe.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};Xe.B=function(){return 0};Xe.h=function(a){return a};\nXe.c=function(a,b){return a+b};Xe.A=function(a,b,c){return Mb(Xe,a+b,c)};Xe.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return Xe.A(b,a,c)};Xe.L=2;var Ye=function Ye(a){switch(arguments.length){case 0:return Ye.B();case 1:return Ye.h(arguments[0]);case 2:return Ye.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return Ye.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};Ye.B=function(){return 1};Ye.h=function(a){return a};\nYe.c=function(a,b){return a*b};Ye.A=function(a,b,c){return Mb(Ye,a*b,c)};Ye.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return Ye.A(b,a,c)};Ye.L=2;function Ze(a){a=(a-a%2)/2;return 0<=a?Math.floor(a):Math.ceil(a)}function $e(a){a-=a>>1&1431655765;a=(a&858993459)+(a>>2&858993459);return 16843009*(a+(a>>4)&252645135)>>24}\nvar v=function v(a){switch(arguments.length){case 0:return v.B();case 1:return v.h(arguments[0]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return v.A(arguments[0],new Jb(c.slice(1),0,null))}};v.B=function(){return\"\"};v.h=function(a){return null==a?\"\":\"\"+a};v.A=function(a,b){for(var c=new cb(\"\"+v.h(a)),d=b;;)if(t(d))c=c.append(\"\"+v.h(y(d))),d=z(d);else return c.toString()};v.N=function(a){var b=y(a);a=z(a);return v.A(b,a)};v.L=1;\nfunction $d(a,b){if(we(b))if(Pd(a)&&Pd(b)&&H(a)!==H(b))var c=!1;else a:{c=E(a);for(var d=E(b);;){if(null==c){c=null==d;break a}if(null!=d&&G.c(y(c),y(d)))c=z(c),d=z(d);else{c=!1;break a}}}else c=null;return Ee(c)}function af(a,b,c,d,e){this.meta=a;this.first=b;this.kc=c;this.count=d;this.w=e;this.m=65937646;this.J=8192}g=af.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};\ng.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();\ng.lastIndexOf=function(){function a(a){return Xd(this,a,this.count)}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.meta};g.Ka=function(){return 1===this.count?null:this.kc};g.W=function(){return this.count};g.Ac=function(){return this.first};g.Bc=function(){return this.bb(null)};\ng.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};g.K=function(a,b){return $d(this,b)};g.oa=function(){return tc(wd,this.meta)};g.Fa=function(a,b){return ce(b,this)};g.Ga=function(a,b,c){return de(b,c,this)};g.Ia=function(){return this.first};g.bb=function(){return 1===this.count?wd:this.kc};g.S=function(){return this};g.T=function(a,b){return new af(b,this.first,this.kc,this.count,this.w)};g.X=function(a,b){return new af(this.meta,b,this,this.count+1,null)};af.prototype[Fb]=function(){return yd(this)};\nfunction bf(a){this.meta=a;this.m=65937614;this.J=8192}g=bf.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();\ng.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.meta};g.Ka=function(){return null};g.W=function(){return 0};g.Ac=function(){return null};g.Bc=function(){throw Error(\"Can't pop empty list\");};g.U=function(){return Cd};\ng.K=function(a,b){return(null!=b?b.m&33554432||q===b.Vf||(b.m?0:Ab(Fc,b)):Ab(Fc,b))||we(b)?null==E(b):!1};g.oa=function(){return this};g.Fa=function(a,b){return ce(b,this)};g.Ga=function(a,b,c){return de(b,c,this)};g.Ia=function(){return null};g.bb=function(){return wd};g.S=function(){return null};g.T=function(a,b){return new bf(b)};g.X=function(a,b){return new af(this.meta,b,null,1,null)};var wd=new bf(null);bf.prototype[Fb]=function(){return yd(this)};\nfunction cf(a){return(null!=a?a.m&134217728||q===a.Yf||(a.m?0:Ab(Hc,a)):Ab(Hc,a))?Ic(a):Mb(ge,wd,a)}var df=function df(a){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return df.A(0<c.length?new Jb(c.slice(0),0,null):null)};df.A=function(a){if(a instanceof Jb&&0===a.i)var b=a.o;else a:for(b=[];;)if(null!=a)b.push(a.Ia(null)),a=a.Ka(null);else break a;a=b.length;for(var c=wd;;)if(0<a){var d=a-1;c=c.X(null,b[a-1]);a=d}else return c};df.L=0;df.N=function(a){return df.A(E(a))};\nfunction ef(a,b,c,d){this.meta=a;this.first=b;this.kc=c;this.w=d;this.m=65929452;this.J=8192}g=ef.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();\ng.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.meta};g.Ka=function(){return null==this.kc?null:E(this.kc)};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};g.K=function(a,b){return $d(this,b)};\ng.oa=function(){return tc(wd,this.meta)};g.Fa=function(a,b){return ce(b,this)};g.Ga=function(a,b,c){return de(b,c,this)};g.Ia=function(){return this.first};g.bb=function(){return null==this.kc?wd:this.kc};g.S=function(){return this};g.T=function(a,b){return new ef(b,this.first,this.kc,this.w)};g.X=function(a,b){return new ef(null,b,this,null)};ef.prototype[Fb]=function(){return yd(this)};function ae(a,b){return null==b||null!=b&&(b.m&64||q===b.G)?new ef(null,a,b,null):new ef(null,a,E(b),null)}\nfunction ff(a,b){if(a.ea===b.ea)return 0;var c=wb(a.fb);if(t(c?b.fb:c))return-1;if(t(a.fb)){if(wb(b.fb))return 1;c=Aa(a.fb,b.fb);return 0===c?Aa(a.name,b.name):c}return Aa(a.name,b.name)}function L(a,b,c,d){this.fb=a;this.name=b;this.ea=c;this.Oc=d;this.m=2153775105;this.J=4096}g=L.prototype;g.toString=function(){return[\":\",v.h(this.ea)].join(\"\")};g.equiv=function(a){return this.K(null,a)};g.K=function(a,b){return b instanceof L?this.ea===b.ea:!1};\ng.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return D.c(c,this);case 3:return D.l(c,this,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.c=function(a,c){return D.c(c,this)};a.l=function(a,c,d){return D.l(c,this,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return D.c(a,this)};g.c=function(a,b){return D.l(a,this,b)};\ng.U=function(){var a=this.Oc;return null!=a?a:this.Oc=a=pd(kd(this.name),nd(this.fb))+2654435769|0};g.hd=function(){return this.name};g.jd=function(){return this.fb};g.R=function(a,b){return Jc(b,[\":\",v.h(this.ea)].join(\"\"))};function gf(a){return a instanceof L}function N(a,b){return a===b?!0:a instanceof L&&b instanceof L?a.ea===b.ea:!1}\nvar hf=function hf(a){switch(arguments.length){case 1:return hf.h(arguments[0]);case 2:return hf.c(arguments[0],arguments[1]);default:throw Error([\"Invalid arity: \",v.h(arguments.length)].join(\"\"));}};\nhf.h=function(a){if(a instanceof L)return a;if(a instanceof rd){if(null!=a&&(a.J&4096||q===a.Oe))var b=a.jd(null);else throw Error([\"Doesn't support namespace: \",v.h(a)].join(\"\"));return new L(b,jf(a),a.Zb,null)}return\"string\"===typeof a?(b=a.split(\"/\"),2===b.length?new L(b[0],b[1],a,null):new L(null,b[0],a,null)):null};\nhf.c=function(a,b){var c=a instanceof L?jf(a):a instanceof rd?jf(a):a,d=b instanceof L?jf(b):b instanceof rd?jf(b):b;return new L(c,d,[v.h(t(c)?[v.h(c),\"/\"].join(\"\"):null),v.h(d)].join(\"\"),null)};hf.L=2;function kf(a,b,c,d){this.meta=a;this.Vc=b;this.s=c;this.w=d;this.m=32374988;this.J=1}g=kf.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};function lf(a){null!=a.Vc&&(a.s=a.Vc.B?a.Vc.B():a.Vc.call(null),a.Vc=null);return a.s}\ng.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();\ng.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.meta};g.Ka=function(){this.S(null);return null==this.s?null:z(this.s)};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};g.K=function(a,b){return $d(this,b)};\ng.oa=function(){return tc(wd,this.meta)};g.Fa=function(a,b){return ce(b,this)};g.Ga=function(a,b,c){return de(b,c,this)};g.Ia=function(){this.S(null);return null==this.s?null:y(this.s)};g.bb=function(){this.S(null);return null!=this.s?vd(this.s):wd};g.S=function(){lf(this);if(null==this.s)return null;for(var a=this.s;;)if(a instanceof kf)a=lf(a);else return this.s=a,E(this.s)};g.T=function(a,b){return new kf(b,this.Vc,this.s,this.w)};g.X=function(a,b){return ae(b,this)};kf.prototype[Fb]=function(){return yd(this)};\nfunction mf(a,b){this.aa=a;this.end=b;this.m=2;this.J=0}mf.prototype.add=function(a){this.aa[this.end]=a;return this.end+=1};mf.prototype.Da=function(){var a=new nf(this.aa,0,this.end);this.aa=null;return a};mf.prototype.W=function(){return this.end};function of(a){return new mf(Array(a),0)}function nf(a,b,c){this.o=a;this.ab=b;this.end=c;this.m=524306;this.J=0}g=nf.prototype;g.W=function(){return this.end-this.ab};g.$=function(a,b){return this.o[this.ab+b]};\ng.ka=function(a,b,c){return 0<=b&&b<this.end-this.ab?this.o[this.ab+b]:c};g.Le=function(){if(this.ab===this.end)throw Error(\"-drop-first of empty chunk\");return new nf(this.o,this.ab+1,this.end)};g.Fa=function(a,b){return Od(this.o,b,this.o[this.ab],this.ab+1)};g.Ga=function(a,b,c){return Od(this.o,b,c,this.ab)};function pf(a,b,c,d){this.Da=a;this.Wb=b;this.meta=c;this.w=d;this.m=31850732;this.J=1536}g=pf.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};\ng.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();\ng.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.meta};g.Ka=function(){if(1<Qb(this.Da))return new pf(Vc(this.Da),this.Wb,this.meta,null);var a=Cc(this.Wb);return null==a?null:a};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};\ng.K=function(a,b){return $d(this,b)};g.oa=function(){return tc(wd,this.meta)};g.Ia=function(){return A.c(this.Da,0)};g.bb=function(){return 1<Qb(this.Da)?new pf(Vc(this.Da),this.Wb,this.meta,null):null==this.Wb?wd:this.Wb};g.S=function(){return this};g.ge=function(){return this.Da};g.Hd=function(){return null==this.Wb?wd:this.Wb};g.T=function(a,b){return new pf(this.Da,this.Wb,b,this.w)};g.X=function(a,b){return ae(b,this)};g.Me=function(){return null==this.Wb?null:this.Wb};pf.prototype[Fb]=function(){return yd(this)};\nfunction qf(a,b){return 0===Qb(a)?b:new pf(a,b,null,null)}function rf(a,b){a.add(b)}function sf(a,b){if(Pd(b))return H(b);for(var c=0,d=E(b);;)if(null!=d&&c<a)c+=1,d=z(d);else return c}\nvar tf=function tf(a){if(null==a)return null;var c=z(a);return null==c?E(y(a)):ae(y(a),tf.h?tf.h(c):tf.call(null,c))},O=function O(a){switch(arguments.length){case 0:return O.B();case 1:return O.h(arguments[0]);case 2:return O.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return O.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};O.B=function(){return new kf(null,function(){return null},null,null)};\nO.h=function(a){return new kf(null,function(){return a},null,null)};O.c=function(a,b){return new kf(null,function(){var c=E(a);return c?Ae(c)?qf(Wc(c),O.c(Xc(c),b)):ae(y(c),O.c(vd(c),b)):b},null,null)};O.A=function(a,b,c){return function h(a,b){return new kf(null,function(){var c=E(a);return c?Ae(c)?qf(Wc(c),h(Xc(c),b)):ae(y(c),h(vd(c),b)):t(b)?h(y(b),z(b)):null},null,null)}(O.c(a,b),c)};O.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return O.A(b,a,c)};O.L=2;\nvar uf=function uf(a){switch(arguments.length){case 0:return uf.B();case 1:return uf.h(arguments[0]);case 2:return uf.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return uf.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};uf.B=function(){return Oc(he)};uf.h=function(a){return a};uf.c=function(a,b){return Pc(a,b)};uf.A=function(a,b,c){for(;;)if(a=Pc(a,b),t(c))b=y(c),c=z(c);else return a};\nuf.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return uf.A(b,a,c)};uf.L=2;\nfunction vf(a,b,c){var d=E(c);if(0===b)return a.B?a.B():a.call(null);c=Wb(d);var e=Yb(d);if(1===b)return a.h?a.h(c):a.call(null,c);d=Wb(e);var f=Yb(e);if(2===b)return a.c?a.c(c,d):a.call(null,c,d);e=Wb(f);var h=Yb(f);if(3===b)return a.l?a.l(c,d,e):a.call(null,c,d,e);f=Wb(h);var k=Yb(h);if(4===b)return a.M?a.M(c,d,e,f):a.call(null,c,d,e,f);h=Wb(k);var l=Yb(k);if(5===b)return a.Z?a.Z(c,d,e,f,h):a.call(null,c,d,e,f,h);k=Wb(l);var p=Yb(l);if(6===b)return a.Ca?a.Ca(c,d,e,f,h,k):a.call(null,c,d,e,f,h,k);\nl=Wb(p);var m=Yb(p);if(7===b)return a.Ya?a.Ya(c,d,e,f,h,k,l):a.call(null,c,d,e,f,h,k,l);p=Wb(m);var u=Yb(m);if(8===b)return a.Ha?a.Ha(c,d,e,f,h,k,l,p):a.call(null,c,d,e,f,h,k,l,p);m=Wb(u);var w=Yb(u);if(9===b)return a.Za?a.Za(c,d,e,f,h,k,l,p,m):a.call(null,c,d,e,f,h,k,l,p,m);u=Wb(w);var x=Yb(w);if(10===b)return a.Na?a.Na(c,d,e,f,h,k,l,p,m,u):a.call(null,c,d,e,f,h,k,l,p,m,u);w=Wb(x);var C=Yb(x);if(11===b)return a.Oa?a.Oa(c,d,e,f,h,k,l,p,m,u,w):a.call(null,c,d,e,f,h,k,l,p,m,u,w);x=Wb(C);var F=Yb(C);\nif(12===b)return a.Pa?a.Pa(c,d,e,f,h,k,l,p,m,u,w,x):a.call(null,c,d,e,f,h,k,l,p,m,u,w,x);C=Wb(F);var I=Yb(F);if(13===b)return a.Qa?a.Qa(c,d,e,f,h,k,l,p,m,u,w,x,C):a.call(null,c,d,e,f,h,k,l,p,m,u,w,x,C);F=Wb(I);var M=Yb(I);if(14===b)return a.Ra?a.Ra(c,d,e,f,h,k,l,p,m,u,w,x,C,F):a.call(null,c,d,e,f,h,k,l,p,m,u,w,x,C,F);I=Wb(M);var S=Yb(M);if(15===b)return a.Sa?a.Sa(c,d,e,f,h,k,l,p,m,u,w,x,C,F,I):a.call(null,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I);M=Wb(S);var X=Yb(S);if(16===b)return a.Ta?a.Ta(c,d,e,f,h,k,l,\np,m,u,w,x,C,F,I,M):a.call(null,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M);S=Wb(X);var Ga=Yb(X);if(17===b)return a.Ua?a.Ua(c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S):a.call(null,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S);X=Wb(Ga);var db=Yb(Ga);if(18===b)return a.Va?a.Va(c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X):a.call(null,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X);Ga=Wb(db);db=Yb(db);if(19===b)return a.Wa?a.Wa(c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga):a.call(null,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga);var Q=Wb(db);Yb(db);if(20===b)return a.Xa?\na.Xa(c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga,Q):a.call(null,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga,Q);throw Error(\"Only up to 20 arguments supported on functions\");}function wf(a,b,c){return null==c?a.h?a.h(b):a.call(a,b):xf(a,b,Wb(c),z(c))}function xf(a,b,c,d){return null==d?a.c?a.c(b,c):a.call(a,b,c):yf(a,b,c,Wb(d),z(d))}function yf(a,b,c,d,e){return null==e?a.l?a.l(b,c,d):a.call(a,b,c,d):zf(a,b,c,d,Wb(e),z(e))}\nfunction zf(a,b,c,d,e,f){if(null==f)return a.M?a.M(b,c,d,e):a.call(a,b,c,d,e);var h=Wb(f),k=z(f);if(null==k)return a.Z?a.Z(b,c,d,e,h):a.call(a,b,c,d,e,h);f=Wb(k);var l=z(k);if(null==l)return a.Ca?a.Ca(b,c,d,e,h,f):a.call(a,b,c,d,e,h,f);k=Wb(l);var p=z(l);if(null==p)return a.Ya?a.Ya(b,c,d,e,h,f,k):a.call(a,b,c,d,e,h,f,k);l=Wb(p);var m=z(p);if(null==m)return a.Ha?a.Ha(b,c,d,e,h,f,k,l):a.call(a,b,c,d,e,h,f,k,l);p=Wb(m);var u=z(m);if(null==u)return a.Za?a.Za(b,c,d,e,h,f,k,l,p):a.call(a,b,c,d,e,h,f,k,\nl,p);m=Wb(u);var w=z(u);if(null==w)return a.Na?a.Na(b,c,d,e,h,f,k,l,p,m):a.call(a,b,c,d,e,h,f,k,l,p,m);u=Wb(w);var x=z(w);if(null==x)return a.Oa?a.Oa(b,c,d,e,h,f,k,l,p,m,u):a.call(a,b,c,d,e,h,f,k,l,p,m,u);w=Wb(x);var C=z(x);if(null==C)return a.Pa?a.Pa(b,c,d,e,h,f,k,l,p,m,u,w):a.call(a,b,c,d,e,h,f,k,l,p,m,u,w);x=Wb(C);var F=z(C);if(null==F)return a.Qa?a.Qa(b,c,d,e,h,f,k,l,p,m,u,w,x):a.call(a,b,c,d,e,h,f,k,l,p,m,u,w,x);C=Wb(F);var I=z(F);if(null==I)return a.Ra?a.Ra(b,c,d,e,h,f,k,l,p,m,u,w,x,C):a.call(a,\nb,c,d,e,h,f,k,l,p,m,u,w,x,C);F=Wb(I);var M=z(I);if(null==M)return a.Sa?a.Sa(b,c,d,e,h,f,k,l,p,m,u,w,x,C,F):a.call(a,b,c,d,e,h,f,k,l,p,m,u,w,x,C,F);I=Wb(M);var S=z(M);if(null==S)return a.Ta?a.Ta(b,c,d,e,h,f,k,l,p,m,u,w,x,C,F,I):a.call(a,b,c,d,e,h,f,k,l,p,m,u,w,x,C,F,I);M=Wb(S);var X=z(S);if(null==X)return a.Ua?a.Ua(b,c,d,e,h,f,k,l,p,m,u,w,x,C,F,I,M):a.call(a,b,c,d,e,h,f,k,l,p,m,u,w,x,C,F,I,M);S=Wb(X);var Ga=z(X);if(null==Ga)return a.Va?a.Va(b,c,d,e,h,f,k,l,p,m,u,w,x,C,F,I,M,S):a.call(a,b,c,d,e,h,f,\nk,l,p,m,u,w,x,C,F,I,M,S);X=Wb(Ga);var db=z(Ga);if(null==db)return a.Wa?a.Wa(b,c,d,e,h,f,k,l,p,m,u,w,x,C,F,I,M,S,X):a.call(a,b,c,d,e,h,f,k,l,p,m,u,w,x,C,F,I,M,S,X);Ga=Wb(db);db=z(db);if(null==db)return a.Xa?a.Xa(b,c,d,e,h,f,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga):a.call(a,b,c,d,e,h,f,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga);b=[b,c,d,e,h,f,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga];for(c=db;;)if(c)b.push(Wb(c)),c=z(c);else break;return a.apply(a,b)}\nfunction P(a,b){if(a.N){var c=a.L,d=sf(c+1,b);return d<=c?vf(a,d,b):a.N(b)}c=E(b);return null==c?a.B?a.B():a.call(a):wf(a,Wb(c),z(c))}function Kb(a,b,c){if(a.N){b=ae(b,c);var d=a.L;c=sf(d,c)+1;return c<=d?vf(a,c,b):a.N(b)}return wf(a,b,E(c))}function Af(a,b,c,d,e){return a.N?(b=ae(b,ae(c,ae(d,e))),c=a.L,e=3+sf(c-2,e),e<=c?vf(a,e,b):a.N(b)):yf(a,b,c,d,E(e))}function oe(a,b,c,d,e,f){return a.N?(f=tf(f),b=ae(b,ae(c,ae(d,ae(e,f)))),c=a.L,f=4+sf(c-3,f),f<=c?vf(a,f,b):a.N(b)):zf(a,b,c,d,e,tf(f))}\nfunction Bf(a){return E(a)?a:null}\nfunction Cf(){\"undefined\"===typeof hb&&(hb=function(a){this.zf=a;this.m=393216;this.J=0},hb.prototype.T=function(a,b){return new hb(b)},hb.prototype.P=function(){return this.zf},hb.prototype.ja=function(){return!1},hb.prototype.next=function(){return Error(\"No such element\")},hb.prototype.remove=function(){return Error(\"Unsupported operation\")},hb.Wc=function(){return new R(null,1,5,T,[Df],null)},hb.qc=!0,hb.Tb=\"cljs.core/t_cljs$core34616\",hb.Ec=function(a,b){return Jc(b,\"cljs.core/t_cljs$core34616\")});\nreturn new hb(Ef)}function Ff(a,b){this.s=a;this.i=b}Ff.prototype.ja=function(){return this.i<this.s.length};Ff.prototype.next=function(){var a=this.s.charAt(this.i);this.i+=1;return a};Ff.prototype.remove=function(){return Error(\"Unsupported operation\")};function Gf(a,b){this.o=a;this.i=b}Gf.prototype.ja=function(){return this.i<this.o.length};Gf.prototype.next=function(){var a=this.o[this.i];this.i+=1;return a};Gf.prototype.remove=function(){return Error(\"Unsupported operation\")};var Hf={},If={};\nfunction Jf(a,b){this.cd=a;this.ub=b}Jf.prototype.ja=function(){this.cd===Hf?(this.cd=If,this.ub=E(this.ub)):this.cd===this.ub&&(this.ub=z(this.cd));return null!=this.ub};Jf.prototype.next=function(){if(this.ja())return this.cd=this.ub,y(this.ub);throw Error(\"No such element\");};Jf.prototype.remove=function(){return Error(\"Unsupported operation\")};\nfunction Kf(a){if(ud(a))return dd(a);if(null==a)return Cf();if(\"string\"===typeof a)return new Ff(a,0);if(vb(a))return new Gf(a,0);if((null!=a?a.m&8388608||q===a.Pe||(a.m?0:Ab(Bc,a)):Ab(Bc,a))||vb(a)||\"string\"===typeof a)return new Jf(Hf,a);throw Error([\"Cannot create iterator from \",v.h(a)].join(\"\"));}function Mf(a){this.ae=a}Mf.prototype.add=function(a){this.ae.push(a);return this};Mf.prototype.remove=function(){return this.ae.shift()};Mf.prototype.Td=function(){return 0===this.ae.length};\nMf.prototype.toString=function(){return[\"Many: \",v.h(this.ae)].join(\"\")};var Nf={};function Of(a){this.H=a}Of.prototype.add=function(a){return this.H===Nf?(this.H=a,this):new Mf([this.H,a])};Of.prototype.remove=function(){if(this.H===Nf)throw Error(\"Removing object from empty buffer\");var a=this.H;this.H=Nf;return a};Of.prototype.Td=function(){return this.H===Nf};Of.prototype.toString=function(){return[\"Single: \",v.h(this.H)].join(\"\")};function Pf(){}Pf.prototype.add=function(a){return new Of(a)};\nPf.prototype.remove=function(){throw Error(\"Removing object from empty buffer\");};Pf.prototype.Td=function(){return!0};Pf.prototype.toString=function(){return\"Empty\"};var Qf=new Pf,Rf=function Rf(a){return new kf(null,function(){if(a.ja())for(var c=[],d=0;;){var e=a.ja();if(t(t(e)?32>d:e))c[d]=a.next(),d+=1;else return qf(new nf(c,0,d),Rf.h?Rf.h(a):Rf.call(null,a))}else return null},null,null)};function Sf(a,b,c,d,e,f){this.buffer=a;this.ub=b;this.pe=c;this.Rb=d;this.ye=e;this.Gf=f}\nSf.prototype.step=function(){if(this.ub!==Nf)return!0;for(;;)if(this.ub===Nf)if(this.buffer.Td()){if(this.pe)return!1;if(this.ye.ja()){if(this.Gf)var a=P(this.Rb,ae(null,this.ye.next()));else a=this.ye.next(),a=this.Rb.c?this.Rb.c(null,a):this.Rb.call(null,null,a);Hd(a)&&(this.Rb.h?this.Rb.h(null):this.Rb.call(null,null),this.pe=!0)}else this.Rb.h?this.Rb.h(null):this.Rb.call(null,null),this.pe=!0}else this.ub=this.buffer.remove();else return!0};Sf.prototype.ja=function(){return this.step()};\nSf.prototype.next=function(){if(this.ja()){var a=this.ub;this.ub=Nf;return a}throw Error(\"No such element\");};Sf.prototype.remove=function(){return Error(\"Unsupported operation\")};Sf.prototype[Fb]=function(){return yd(this)};\nfunction Tf(a,b){var c=new Sf(Qf,Nf,!1,null,b,!1);c.Rb=function(){var b=function(a){return function(){function b(b,c){a.buffer=a.buffer.add(c);return b}var c=null;c=function(a,c){switch(arguments.length){case 0:return null;case 1:return a;case 2:return b.call(this,a,c)}throw Error(\"Invalid arity: \"+(arguments.length-1));};c.B=function(){return null};c.h=function(a){return a};c.c=b;return c}()}(c);return a.h?a.h(b):a.call(null,b)}();return c}\nfunction Uf(a,b){var c=Kf(b);c=Tf(a,c);c=Rf(c);return t(c)?c:wd}function Vf(a,b){for(;;){if(null==E(b))return!0;var c=y(b);c=a.h?a.h(c):a.call(null,c);if(t(c)){c=a;var d=z(b);a=c;b=d}else return!1}}function Wf(a,b){for(;;)if(E(b)){var c=y(b);c=a.h?a.h(c):a.call(null,c);if(t(c))return c;c=a;var d=z(b);a=c;b=d}else return null}function Xf(a){if(Ge(a))return 0===(a&1);throw Error([\"Argument must be an integer: \",v.h(a)].join(\"\"));}\nfunction Yf(a){return function(){function b(b,c){return wb(a.c?a.c(b,c):a.call(null,b,c))}function c(b){return wb(a.h?a.h(b):a.call(null,b))}function d(){return wb(a.B?a.B():a.call(null))}var e=null,f=function(){function b(a,b,d){var e=null;if(2<arguments.length){e=0;for(var f=Array(arguments.length-2);e<f.length;)f[e]=arguments[e+2],++e;e=new Jb(f,0,null)}return c.call(this,a,b,e)}function c(b,c,d){a.N?(b=ae(b,ae(c,d)),c=a.L,d=2+sf(c-1,d),d=d<=c?vf(a,d,b):a.N(b)):d=xf(a,b,c,E(d));return wb(d)}b.L=\n2;b.N=function(a){var b=y(a);a=z(a);var d=y(a);a=vd(a);return c(b,d,a)};b.A=c;return b}();e=function(a,e,l){switch(arguments.length){case 0:return d.call(this);case 1:return c.call(this,a);case 2:return b.call(this,a,e);default:var h=null;if(2<arguments.length){h=0;for(var k=Array(arguments.length-2);h<k.length;)k[h]=arguments[h+2],++h;h=new Jb(k,0,null)}return f.A(a,e,h)}throw Error(\"Invalid arity: \"+(arguments.length-1));};e.L=2;e.N=f.N;e.B=d;e.h=c;e.c=b;e.A=f.A;return e}()}\nfunction Zf(a){return function(){function b(b){if(0<arguments.length)for(var c=0,e=Array(arguments.length-0);c<e.length;)e[c]=arguments[c+0],++c;return a}b.L=0;b.N=function(b){E(b);return a};b.A=function(){return a};return b}()}\nvar $f=function $f(a){switch(arguments.length){case 0:return $f.B();case 1:return $f.h(arguments[0]);case 2:return $f.c(arguments[0],arguments[1]);case 3:return $f.l(arguments[0],arguments[1],arguments[2]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return $f.A(arguments[0],arguments[1],arguments[2],new Jb(c.slice(3),0,null))}};$f.B=function(){return Ve};$f.h=function(a){return a};\n$f.c=function(a,b){return function(){function c(c,d,e){c=b.l?b.l(c,d,e):b.call(null,c,d,e);return a.h?a.h(c):a.call(null,c)}function d(c,d){var e=b.c?b.c(c,d):b.call(null,c,d);return a.h?a.h(e):a.call(null,e)}function e(c){c=b.h?b.h(c):b.call(null,c);return a.h?a.h(c):a.call(null,c)}function f(){var c=b.B?b.B():b.call(null);return a.h?a.h(c):a.call(null,c)}var h=null,k=function(){function c(a,b,c,e){var f=null;if(3<arguments.length){f=0;for(var h=Array(arguments.length-3);f<h.length;)h[f]=arguments[f+\n3],++f;f=new Jb(h,0,null)}return d.call(this,a,b,c,f)}function d(c,d,e,f){c=Af(b,c,d,e,f);return a.h?a.h(c):a.call(null,c)}c.L=3;c.N=function(a){var b=y(a);a=z(a);var c=y(a);a=z(a);var e=y(a);a=vd(a);return d(b,c,e,a)};c.A=d;return c}();h=function(a,b,h,u){switch(arguments.length){case 0:return f.call(this);case 1:return e.call(this,a);case 2:return d.call(this,a,b);case 3:return c.call(this,a,b,h);default:var m=null;if(3<arguments.length){m=0;for(var p=Array(arguments.length-3);m<p.length;)p[m]=\narguments[m+3],++m;m=new Jb(p,0,null)}return k.A(a,b,h,m)}throw Error(\"Invalid arity: \"+(arguments.length-1));};h.L=3;h.N=k.N;h.B=f;h.h=e;h.c=d;h.l=c;h.A=k.A;return h}()};\n$f.l=function(a,b,c){return function(){function d(d,e,f){d=c.l?c.l(d,e,f):c.call(null,d,e,f);d=b.h?b.h(d):b.call(null,d);return a.h?a.h(d):a.call(null,d)}function e(d,e){var f=c.c?c.c(d,e):c.call(null,d,e);f=b.h?b.h(f):b.call(null,f);return a.h?a.h(f):a.call(null,f)}function f(d){d=c.h?c.h(d):c.call(null,d);d=b.h?b.h(d):b.call(null,d);return a.h?a.h(d):a.call(null,d)}function h(){var d=c.B?c.B():c.call(null);d=b.h?b.h(d):b.call(null,d);return a.h?a.h(d):a.call(null,d)}var k=null,l=function(){function d(a,\nb,c,d){var f=null;if(3<arguments.length){f=0;for(var h=Array(arguments.length-3);f<h.length;)h[f]=arguments[f+3],++f;f=new Jb(h,0,null)}return e.call(this,a,b,c,f)}function e(d,e,f,h){d=Af(c,d,e,f,h);d=b.h?b.h(d):b.call(null,d);return a.h?a.h(d):a.call(null,d)}d.L=3;d.N=function(a){var b=y(a);a=z(a);var c=y(a);a=z(a);var d=y(a);a=vd(a);return e(b,c,d,a)};d.A=e;return d}();k=function(a,b,c,k){switch(arguments.length){case 0:return h.call(this);case 1:return f.call(this,a);case 2:return e.call(this,\na,b);case 3:return d.call(this,a,b,c);default:var m=null;if(3<arguments.length){m=0;for(var p=Array(arguments.length-3);m<p.length;)p[m]=arguments[m+3],++m;m=new Jb(p,0,null)}return l.A(a,b,c,m)}throw Error(\"Invalid arity: \"+(arguments.length-1));};k.L=3;k.N=l.N;k.B=h;k.h=f;k.c=e;k.l=d;k.A=l.A;return k}()};\n$f.A=function(a,b,c,d){return function(a){return function(){function b(a){var b=null;if(0<arguments.length){b=0;for(var d=Array(arguments.length-0);b<d.length;)d[b]=arguments[b+0],++b;b=new Jb(d,0,null)}return c.call(this,b)}function c(b){b=P(y(a),b);for(var c=z(a);;)if(c){var d=y(c);b=d.h?d.h(b):d.call(null,b);c=z(c)}else return b}b.L=0;b.N=function(a){a=E(a);return c(a)};b.A=c;return b}()}(cf(ae(a,ae(b,ae(c,d)))))};\n$f.N=function(a){var b=y(a),c=z(a);a=y(c);var d=z(c);c=y(d);d=z(d);return $f.A(b,a,c,d)};$f.L=3;\nvar ag=function ag(a){switch(arguments.length){case 1:return ag.h(arguments[0]);case 2:return ag.c(arguments[0],arguments[1]);case 3:return ag.l(arguments[0],arguments[1],arguments[2]);case 4:return ag.M(arguments[0],arguments[1],arguments[2],arguments[3]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return ag.A(arguments[0],arguments[1],arguments[2],arguments[3],new Jb(c.slice(4),0,null))}};ag.h=function(a){return a};\nag.c=function(a,b){return function(){function c(c,d,e){return a.M?a.M(b,c,d,e):a.call(null,b,c,d,e)}function d(c,d){return a.l?a.l(b,c,d):a.call(null,b,c,d)}function e(c){return a.c?a.c(b,c):a.call(null,b,c)}function f(){return a.h?a.h(b):a.call(null,b)}var h=null,k=function(){function c(a,b,c,e){var f=null;if(3<arguments.length){f=0;for(var h=Array(arguments.length-3);f<h.length;)h[f]=arguments[f+3],++f;f=new Jb(h,0,null)}return d.call(this,a,b,c,f)}function d(c,d,e,f){return oe(a,b,c,d,e,be([f]))}\nc.L=3;c.N=function(a){var b=y(a);a=z(a);var c=y(a);a=z(a);var e=y(a);a=vd(a);return d(b,c,e,a)};c.A=d;return c}();h=function(a,b,h,u){switch(arguments.length){case 0:return f.call(this);case 1:return e.call(this,a);case 2:return d.call(this,a,b);case 3:return c.call(this,a,b,h);default:var m=null;if(3<arguments.length){m=0;for(var l=Array(arguments.length-3);m<l.length;)l[m]=arguments[m+3],++m;m=new Jb(l,0,null)}return k.A(a,b,h,m)}throw Error(\"Invalid arity: \"+(arguments.length-1));};h.L=3;h.N=k.N;\nh.B=f;h.h=e;h.c=d;h.l=c;h.A=k.A;return h}()};\nag.l=function(a,b,c){return function(){function d(d,e,f){return a.Z?a.Z(b,c,d,e,f):a.call(null,b,c,d,e,f)}function e(d,e){return a.M?a.M(b,c,d,e):a.call(null,b,c,d,e)}function f(d){return a.l?a.l(b,c,d):a.call(null,b,c,d)}function h(){return a.c?a.c(b,c):a.call(null,b,c)}var k=null,l=function(){function d(a,b,c,d){var f=null;if(3<arguments.length){f=0;for(var h=Array(arguments.length-3);f<h.length;)h[f]=arguments[f+3],++f;f=new Jb(h,0,null)}return e.call(this,a,b,c,f)}function e(d,e,f,h){return oe(a,\nb,c,d,e,be([f,h]))}d.L=3;d.N=function(a){var b=y(a);a=z(a);var c=y(a);a=z(a);var d=y(a);a=vd(a);return e(b,c,d,a)};d.A=e;return d}();k=function(a,b,c,k){switch(arguments.length){case 0:return h.call(this);case 1:return f.call(this,a);case 2:return e.call(this,a,b);case 3:return d.call(this,a,b,c);default:var m=null;if(3<arguments.length){m=0;for(var p=Array(arguments.length-3);m<p.length;)p[m]=arguments[m+3],++m;m=new Jb(p,0,null)}return l.A(a,b,c,m)}throw Error(\"Invalid arity: \"+(arguments.length-\n1));};k.L=3;k.N=l.N;k.B=h;k.h=f;k.c=e;k.l=d;k.A=l.A;return k}()};\nag.M=function(a,b,c,d){return function(){function e(e,f,h){return a.Ca?a.Ca(b,c,d,e,f,h):a.call(null,b,c,d,e,f,h)}function f(e,f){return a.Z?a.Z(b,c,d,e,f):a.call(null,b,c,d,e,f)}function h(e){return a.M?a.M(b,c,d,e):a.call(null,b,c,d,e)}function k(){return a.l?a.l(b,c,d):a.call(null,b,c,d)}var l=null,p=function(){function e(a,b,c,d){var e=null;if(3<arguments.length){e=0;for(var h=Array(arguments.length-3);e<h.length;)h[e]=arguments[e+3],++e;e=new Jb(h,0,null)}return f.call(this,a,b,c,e)}function f(e,\nf,h,k){return oe(a,b,c,d,e,be([f,h,k]))}e.L=3;e.N=function(a){var b=y(a);a=z(a);var c=y(a);a=z(a);var d=y(a);a=vd(a);return f(b,c,d,a)};e.A=f;return e}();l=function(a,b,c,d){switch(arguments.length){case 0:return k.call(this);case 1:return h.call(this,a);case 2:return f.call(this,a,b);case 3:return e.call(this,a,b,c);default:var m=null;if(3<arguments.length){m=0;for(var l=Array(arguments.length-3);m<l.length;)l[m]=arguments[m+3],++m;m=new Jb(l,0,null)}return p.A(a,b,c,m)}throw Error(\"Invalid arity: \"+\n(arguments.length-1));};l.L=3;l.N=p.N;l.B=k;l.h=h;l.c=f;l.l=e;l.A=p.A;return l}()};ag.A=function(a,b,c,d,e){return function(){function f(a){var b=null;if(0<arguments.length){b=0;for(var c=Array(arguments.length-0);b<c.length;)c[b]=arguments[b+0],++b;b=new Jb(c,0,null)}return h.call(this,b)}function h(f){return Af(a,b,c,d,O.c(e,f))}f.L=0;f.N=function(a){a=E(a);return h(a)};f.A=h;return f}()};ag.N=function(a){var b=y(a),c=z(a);a=y(c);var d=z(c);c=y(d);var e=z(d);d=y(e);e=z(e);return ag.A(b,a,c,d,e)};\nag.L=4;function bg(a,b){return function f(b,e){return new kf(null,function(){var d=E(e);if(d){if(Ae(d)){for(var k=Wc(d),l=H(k),p=of(l),m=0;;)if(m<l)rf(p,function(){var d=b+m,e=A.c(k,m);return a.c?a.c(d,e):a.call(null,d,e)}()),m+=1;else break;return qf(p.Da(),f(b+l,Xc(d)))}return ae(function(){var e=y(d);return a.c?a.c(b,e):a.call(null,b,e)}(),f(b+1,vd(d)))}return null},null,null)}(0,b)}function cg(a,b,c,d){this.state=a;this.meta=b;this.df=c;this.gb=d;this.J=16386;this.m=6455296}g=cg.prototype;\ng.equiv=function(a){return this.K(null,a)};g.K=function(a,b){return this===b};g.pc=function(){return this.state};g.P=function(){return this.meta};g.Kd=function(a,b,c){a=E(this.gb);for(var d=null,e=0,f=0;;)if(f<e){var h=d.$(null,f),k=J(h,0,null);h=J(h,1,null);h.M?h.M(k,this,b,c):h.call(null,k,this,b,c);f+=1}else if(a=E(a))Ae(a)?(d=Wc(a),a=Xc(a),k=d,e=H(d),d=k):(d=y(a),k=J(d,0,null),h=J(d,1,null),h.M?h.M(k,this,b,c):h.call(null,k,this,b,c),a=z(a),d=null,e=0),f=0;else return null};\ng.Jd=function(a,b,c){this.gb=K.l(this.gb,b,c);return this};g.Ld=function(a,b){return this.gb=le.c(this.gb,b)};g.U=function(){return ja(this)};var dg=function dg(a){switch(arguments.length){case 1:return dg.h(arguments[0]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return dg.A(arguments[0],new Jb(c.slice(1),0,null))}};dg.h=function(a){return new cg(a,null,null,null)};\ndg.A=function(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,rb);c=D.c(c,eg);return new cg(a,d,c,null)};dg.N=function(a){var b=y(a);a=z(a);return dg.A(b,a)};dg.L=1;function fg(a,b){if(a instanceof cg){var c=a.df;if(null!=c&&!t(c.h?c.h(b):c.call(null,b)))throw Error(\"Validator rejected reference state\");c=a.state;a.state=b;null!=a.gb&&Lc(a,c,b);return b}return $c(a,b)}\nvar gg=function gg(a){switch(arguments.length){case 2:return gg.c(arguments[0],arguments[1]);case 3:return gg.l(arguments[0],arguments[1],arguments[2]);case 4:return gg.M(arguments[0],arguments[1],arguments[2],arguments[3]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return gg.A(arguments[0],arguments[1],arguments[2],arguments[3],new Jb(c.slice(4),0,null))}};\ngg.c=function(a,b){if(a instanceof cg){var c=a.state;c=b.h?b.h(c):b.call(null,c);c=fg(a,c)}else c=ad.c(a,b);return c};gg.l=function(a,b,c){if(a instanceof cg){var d=a.state;b=b.c?b.c(d,c):b.call(null,d,c);a=fg(a,b)}else a=ad.l(a,b,c);return a};gg.M=function(a,b,c,d){if(a instanceof cg){var e=a.state;b=b.l?b.l(e,c,d):b.call(null,e,c,d);a=fg(a,b)}else a=ad.M(a,b,c,d);return a};gg.A=function(a,b,c,d,e){return a instanceof cg?fg(a,Af(b,a.state,c,d,e)):ad.Z(a,b,c,d,e)};\ngg.N=function(a){var b=y(a),c=z(a);a=y(c);var d=z(c);c=y(d);var e=z(d);d=y(e);e=z(e);return gg.A(b,a,c,d,e)};gg.L=4;function hg(a){this.state=a;this.m=32768;this.J=0}hg.prototype.Qe=function(a,b){return this.state=b};hg.prototype.pc=function(){return this.state};\nvar ig=function ig(a){switch(arguments.length){case 1:return ig.h(arguments[0]);case 2:return ig.c(arguments[0],arguments[1]);case 3:return ig.l(arguments[0],arguments[1],arguments[2]);case 4:return ig.M(arguments[0],arguments[1],arguments[2],arguments[3]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return ig.A(arguments[0],arguments[1],arguments[2],arguments[3],new Jb(c.slice(4),0,null))}};\nig.h=function(a){return function(b){return function(){function c(c,d){var e=a.h?a.h(d):a.call(null,d);return b.c?b.c(c,e):b.call(null,c,e)}function d(a){return b.h?b.h(a):b.call(null,a)}function e(){return b.B?b.B():b.call(null)}var f=null,h=function(){function c(a,b,c){var e=null;if(2<arguments.length){e=0;for(var f=Array(arguments.length-2);e<f.length;)f[e]=arguments[e+2],++e;e=new Jb(f,0,null)}return d.call(this,a,b,e)}function d(c,d,e){d=Kb(a,d,e);return b.c?b.c(c,d):b.call(null,c,d)}c.L=2;c.N=\nfunction(a){var b=y(a);a=z(a);var c=y(a);a=vd(a);return d(b,c,a)};c.A=d;return c}();f=function(a,b,f){switch(arguments.length){case 0:return e.call(this);case 1:return d.call(this,a);case 2:return c.call(this,a,b);default:var k=null;if(2<arguments.length){k=0;for(var l=Array(arguments.length-2);k<l.length;)l[k]=arguments[k+2],++k;k=new Jb(l,0,null)}return h.A(a,b,k)}throw Error(\"Invalid arity: \"+(arguments.length-1));};f.L=2;f.N=h.N;f.B=e;f.h=d;f.c=c;f.A=h.A;return f}()}};\nig.c=function(a,b){return new kf(null,function(){var c=E(b);if(c){if(Ae(c)){for(var d=Wc(c),e=H(d),f=of(e),h=0;;)if(h<e)rf(f,function(){var b=A.c(d,h);return a.h?a.h(b):a.call(null,b)}()),h+=1;else break;return qf(f.Da(),ig.c(a,Xc(c)))}return ae(function(){var b=y(c);return a.h?a.h(b):a.call(null,b)}(),ig.c(a,vd(c)))}return null},null,null)};\nig.l=function(a,b,c){return new kf(null,function(){var d=E(b),e=E(c);if(d&&e){var f=ae;var h=y(d);var k=y(e);h=a.c?a.c(h,k):a.call(null,h,k);d=f(h,ig.l(a,vd(d),vd(e)))}else d=null;return d},null,null)};ig.M=function(a,b,c,d){return new kf(null,function(){var e=E(b),f=E(c),h=E(d);if(e&&f&&h){var k=ae;var l=y(e);var p=y(f),m=y(h);l=a.l?a.l(l,p,m):a.call(null,l,p,m);e=k(l,ig.M(a,vd(e),vd(f),vd(h)))}else e=null;return e},null,null)};\nig.A=function(a,b,c,d,e){var f=function l(a){return new kf(null,function(){var b=ig.c(E,a);return Vf(Ve,b)?ae(ig.c(y,b),l(ig.c(vd,b))):null},null,null)};return ig.c(function(){return function(b){return P(a,b)}}(f),f(ge.A(e,d,be([c,b]))))};ig.N=function(a){var b=y(a),c=z(a);a=y(c);var d=z(c);c=y(d);var e=z(d);d=y(e);e=z(e);return ig.A(b,a,c,d,e)};ig.L=4;function jg(a,b){return new kf(null,function(){if(0<a){var c=E(b);return c?ae(y(c),jg(a-1,vd(c))):null}return null},null,null)}\nfunction kg(a,b){return new kf(null,function(c){return function(){return c(a,b)}}(function(a,b){for(;;){var c=E(b);if(0<a&&c){var d=a-1;c=vd(c);a=d;b=c}else return c}}),null,null)}function lg(a){return ig.l(function(a){return a},a,kg(2,a))}\nfunction mg(a){return function(b){return function(c){return function(){function d(d,e){var f=B(c);if(t(t(f)?a.h?a.h(e):a.call(null,e):f))return d;bd(c,null);return b.c?b.c(d,e):b.call(null,d,e)}function e(a){return b.h?b.h(a):b.call(null,a)}function f(){return b.B?b.B():b.call(null)}var h=null;h=function(a,b){switch(arguments.length){case 0:return f.call(this);case 1:return e.call(this,a);case 2:return d.call(this,a,b)}throw Error(\"Invalid arity: \"+(arguments.length-1));};h.B=f;h.h=e;h.c=d;return h}()}(new hg(!0))}}\nfunction ng(a,b){return new kf(null,function(c){return function(){return c(a,b)}}(function(a,b){for(;;){var c=E(b),d;if(d=c)d=y(c),d=a.h?a.h(d):a.call(null,d);if(t(d))d=a,c=vd(c),a=d,b=c;else return c}}),null,null)}var og=function og(a){return new kf(null,function(){var c=E(a);return c?O.c(c,og.h?og.h(c):og.call(null,c)):null},null,null)};function pg(a){return new kf(null,function(){return ae(a,pg(a))},null,null)}function qg(a,b){return jg(a,pg(b))}\nvar rg=function rg(a,b){return ae(b,new kf(null,function(){var d=a.h?a.h(b):a.call(null,b);return rg.c?rg.c(a,d):rg.call(null,a,d)},null,null))};function sg(a,b){return P(O,Kb(ig,a,b))}\nfunction tg(a){return function(b){return function(){function c(c,d){return t(a.h?a.h(d):a.call(null,d))?b.c?b.c(c,d):b.call(null,c,d):c}function d(a){return b.h?b.h(a):b.call(null,a)}function e(){return b.B?b.B():b.call(null)}var f=null;f=function(a,b){switch(arguments.length){case 0:return e.call(this);case 1:return d.call(this,a);case 2:return c.call(this,a,b)}throw Error(\"Invalid arity: \"+(arguments.length-1));};f.B=e;f.h=d;f.c=c;return f}()}}\nfunction ug(a,b){return new kf(null,function(){var c=E(b);if(c){if(Ae(c)){for(var d=Wc(c),e=H(d),f=of(e),h=0;;)if(h<e){var k=A.c(d,h);k=a.h?a.h(k):a.call(null,k);t(k)&&(k=A.c(d,h),f.add(k));h+=1}else break;return qf(f.Da(),ug(a,Xc(c)))}d=y(c);c=vd(c);return t(a.h?a.h(d):a.call(null,d))?ae(d,ug(a,c)):ug(a,c)}return null},null,null)}function vg(a,b){return ug(Yf(a),b)}\nvar wg=function wg(a){switch(arguments.length){case 0:return wg.B();case 1:return wg.h(arguments[0]);case 2:return wg.c(arguments[0],arguments[1]);case 3:return wg.l(arguments[0],arguments[1],arguments[2]);default:throw Error([\"Invalid arity: \",v.h(arguments.length)].join(\"\"));}};wg.B=function(){return he};wg.h=function(a){return a};wg.c=function(a,b){return null!=a?null!=a&&(a.J&4||q===a.kf)?tc(Qc(Mb(Pc,Oc(a),b)),qe(a)):Mb(Tb,a,b):Mb(ge,wd,b)};\nwg.l=function(a,b,c){return null!=a&&(a.J&4||q===a.kf)?tc(Qc(We(b,uf,Oc(a),c)),qe(a)):We(b,ge,a,c)};wg.L=3;function xg(a,b){return Qc(Mb(function(b,d){return uf.c(b,a.h?a.h(d):a.call(null,d))},Oc(he),b))}function yg(a,b,c){return new kf(null,function(){var d=E(c);if(d){var e=jg(a,d);return a===H(e)?ae(e,yg(a,b,kg(b,d))):null}return null},null,null)}\nvar zg=function zg(a,b,c){b=E(b);var e=y(b),f=z(b);return f?K.l(a,e,function(){var b=D.c(a,e);return zg.l?zg.l(b,f,c):zg.call(null,b,f,c)}()):K.l(a,e,c)},Ag=function Ag(a){switch(arguments.length){case 3:return Ag.l(arguments[0],arguments[1],arguments[2]);case 4:return Ag.M(arguments[0],arguments[1],arguments[2],arguments[3]);case 5:return Ag.Z(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4]);case 6:return Ag.Ca(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5]);\ndefault:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return Ag.A(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5],new Jb(c.slice(6),0,null))}};Ag.l=function(a,b,c){b=E(b);var d=y(b);return(b=z(b))?K.l(a,d,Ag.l(D.c(a,d),b,c)):K.l(a,d,function(){var b=D.c(a,d);return c.h?c.h(b):c.call(null,b)}())};\nAg.M=function(a,b,c,d){b=E(b);var e=y(b);return(b=z(b))?K.l(a,e,Ag.M(D.c(a,e),b,c,d)):K.l(a,e,function(){var b=D.c(a,e);return c.c?c.c(b,d):c.call(null,b,d)}())};Ag.Z=function(a,b,c,d,e){b=E(b);var f=y(b);return(b=z(b))?K.l(a,f,Ag.Z(D.c(a,f),b,c,d,e)):K.l(a,f,function(){var b=D.c(a,f);return c.l?c.l(b,d,e):c.call(null,b,d,e)}())};\nAg.Ca=function(a,b,c,d,e,f){b=E(b);var h=y(b);return(b=z(b))?K.l(a,h,Ag.Ca(D.c(a,h),b,c,d,e,f)):K.l(a,h,function(){var b=D.c(a,h);return c.M?c.M(b,d,e,f):c.call(null,b,d,e,f)}())};Ag.A=function(a,b,c,d,e,f,h){var k=E(b);b=y(k);return(k=z(k))?K.l(a,b,oe(Ag,D.c(a,b),k,c,d,be([e,f,h]))):K.l(a,b,oe(c,D.c(a,b),d,e,f,be([h])))};Ag.N=function(a){var b=y(a),c=z(a);a=y(c);var d=z(c);c=y(d);var e=z(d);d=y(e);var f=z(e);e=y(f);var h=z(f);f=y(h);h=z(h);return Ag.A(b,a,c,d,e,f,h)};Ag.L=6;\nfunction Bg(a,b,c){return K.l(a,b,function(){var d=D.c(a,b);return c.h?c.h(d):c.call(null,d)}())}function Cg(a,b,c,d){return K.l(a,b,function(){var e=D.c(a,b);return c.c?c.c(e,d):c.call(null,e,d)}())}function Dg(a,b,c){var d=V,e=Eg;return K.l(a,d,function(){var f=D.c(a,d);return e.l?e.l(f,b,c):e.call(null,f,b,c)}())}function Fg(a,b){this.la=a;this.o=b}\nfunction Gg(a){return new Fg(a,[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null])}function Hg(a){return new Fg(a.la,Gb(a.o))}function Ig(a){a=a.F;return 32>a?0:a-1>>>5<<5}function Jg(a,b,c){for(;;){if(0===b)return c;var d=Gg(a);d.o[0]=c;c=d;b-=5}}\nvar Kg=function Kg(a,b,c,d){var f=Hg(c),h=a.F-1>>>b&31;5===b?f.o[h]=d:(c=c.o[h],null!=c?(b-=5,a=Kg.M?Kg.M(a,b,c,d):Kg.call(null,a,b,c,d)):a=Jg(null,b-5,d),f.o[h]=a);return f};function Lg(a,b){throw Error([\"No item \",v.h(a),\" in vector of length \",v.h(b)].join(\"\"));}function Mg(a,b){if(b>=Ig(a))return a.fa;for(var c=a.root,d=a.shift;;)if(0<d){var e=d-5;c=c.o[b>>>d&31];d=e}else return c.o}\nvar Ng=function Ng(a,b,c,d,e){var h=Hg(c);if(0===b)h.o[d&31]=e;else{var k=d>>>b&31;b-=5;c=c.o[k];a=Ng.Z?Ng.Z(a,b,c,d,e):Ng.call(null,a,b,c,d,e);h.o[k]=a}return h},Og=function Og(a,b,c){var e=a.F-2>>>b&31;if(5<b){b-=5;var f=c.o[e];a=Og.l?Og.l(a,b,f):Og.call(null,a,b,f);if(null==a&&0===e)return null;c=Hg(c);c.o[e]=a;return c}if(0===e)return null;c=Hg(c);c.o[e]=null;return c};function Pg(a,b,c,d,e,f){this.i=a;this.base=b;this.o=c;this.Ja=d;this.start=e;this.end=f}\nPg.prototype.ja=function(){return this.i<this.end};Pg.prototype.next=function(){32===this.i-this.base&&(this.o=Mg(this.Ja,this.i),this.base+=32);var a=this.o[this.i&31];this.i+=1;return a};function Qg(a,b,c){return new Pg(b,b-b%32,b<H(a)?Mg(a,b):null,a,b,c)}function Rg(a,b,c,d){return c<d?Sg(a,b,Vd(a,c),c+1,d):b.B?b.B():b.call(null)}\nfunction Sg(a,b,c,d,e){var f=c;c=d;for(d=Mg(a,d);;)if(c<e){var h=c&31;d=0===h?Mg(a,c):d;h=d[h];f=b.c?b.c(f,h):b.call(null,f,h);if(Hd(f))return B(f);c+=1}else return f}function R(a,b,c,d,e,f){this.meta=a;this.F=b;this.shift=c;this.root=d;this.fa=e;this.w=f;this.m=167668511;this.J=139268}g=R.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};\ng.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();\ng.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){return\"number\"===typeof b?this.ka(null,b,c):c};\ng.Qc=function(a,b,c){a=0;for(var d=c;;)if(a<this.F){var e=Mg(this,a);c=e.length;a:for(var f=0;;)if(f<c){var h=f+a,k=e[f];d=b.l?b.l(d,h,k):b.call(null,d,h,k);if(Hd(d)){e=d;break a}f+=1}else{e=d;break a}if(Hd(e))return B(e);a+=c;d=e}else return d};g.fe=q;g.$=function(a,b){return(0<=b&&b<this.F?Mg(this,b):Lg(b,this.F))[b&31]};g.ka=function(a,b,c){return 0<=b&&b<this.F?Mg(this,b)[b&31]:c};\ng.dc=function(a,b,c){if(0<=b&&b<this.F)return Ig(this)<=b?(a=Gb(this.fa),a[b&31]=c,new R(this.meta,this.F,this.shift,this.root,a,null)):new R(this.meta,this.F,this.shift,Ng(this,this.shift,this.root,b,c),this.fa,null);if(b===this.F)return this.X(null,c);throw Error([\"Index \",v.h(b),\" out of bounds  [0,\",v.h(this.F),\"]\"].join(\"\"));};g.ba=function(){return Qg(this,0,this.F)};g.P=function(){return this.meta};g.W=function(){return this.F};g.fd=function(){return this.$(null,0)};\ng.gd=function(){return this.$(null,1)};g.Ac=function(){return 0<this.F?this.$(null,this.F-1):null};\ng.Bc=function(){if(0===this.F)throw Error(\"Can't pop empty vector\");if(1===this.F)return tc(he,this.meta);if(1<this.F-Ig(this))return new R(this.meta,this.F-1,this.shift,this.root,this.fa.slice(0,-1),null);var a=Mg(this,this.F-2),b=Og(this,this.shift,this.root);b=null==b?T:b;var c=this.F-1;return 5<this.shift&&null==b.o[1]?new R(this.meta,c,this.shift-5,b.o[0],a,null):new R(this.meta,c,this.shift,b,a,null)};g.Rc=function(){return 0<this.F?new Zd(this,this.F-1,null):null};\ng.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};g.K=function(a,b){if(b instanceof R)if(this.F===H(b))for(var c=this.ba(null),d=dd(b);;)if(c.ja()){var e=c.next(),f=d.next();if(!G.c(e,f))return!1}else return!0;else return!1;else return $d(this,b)};\ng.Pc=function(){var a=this.F,b=this.shift,c=new Fg({},Gb(this.root.o)),d=this.fa,e=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];Be(d,0,e,0,d.length);return new Tg(a,b,c,e)};g.oa=function(){return tc(he,this.meta)};g.Fa=function(a,b){return Rg(this,b,0,this.F)};\ng.Ga=function(a,b,c){a=0;for(var d=c;;)if(a<this.F){var e=Mg(this,a);c=e.length;a:for(var f=0;;)if(f<c){var h=e[f];d=b.c?b.c(d,h):b.call(null,d,h);if(Hd(d)){e=d;break a}f+=1}else{e=d;break a}if(Hd(e))return B(e);a+=c;d=e}else return d};g.O=function(a,b,c){if(\"number\"===typeof b)return this.dc(null,b,c);throw Error(\"Vector's key for assoc must be a number.\");};g.yc=function(a,b){return Ge(b)?0<=b&&b<this.F:!1};\ng.S=function(){if(0===this.F)var a=null;else if(32>=this.F)a=new Jb(this.fa,0,null);else{a:{a=this.root;for(var b=this.shift;;)if(0<b)b-=5,a=a.o[0];else{a=a.o;break a}}a=new Ug(this,a,0,0,null,null)}return a};g.T=function(a,b){return new R(b,this.F,this.shift,this.root,this.fa,this.w)};\ng.X=function(a,b){if(32>this.F-Ig(this)){for(var c=this.fa.length,d=Array(c+1),e=0;;)if(e<c)d[e]=this.fa[e],e+=1;else break;d[c]=b;return new R(this.meta,this.F+1,this.shift,this.root,d,null)}c=(d=this.F>>>5>1<<this.shift)?this.shift+5:this.shift;d?(d=Gg(null),d.o[0]=this.root,e=Jg(null,this.shift,new Fg(null,this.fa)),d.o[1]=e):d=Kg(this,this.shift,this.root,new Fg(null,this.fa));return new R(this.meta,this.F+1,c,d,[b],null)};\ng.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return this.$(null,c);case 3:return this.ka(null,c,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.c=function(a,c){return this.$(null,c)};a.l=function(a,c,d){return this.ka(null,c,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return this.$(null,a)};g.c=function(a,b){return this.ka(null,a,b)};\nvar T=new Fg(null,[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]),he=new R(null,0,5,T,[],Cd);function Vg(a){var b=a.length;if(32>b)return new R(null,b,5,T,a,null);for(var c=32,d=(new R(null,32,5,T,a.slice(0,32),null)).Pc(null);;)if(c<b){var e=c+1;d=uf.c(d,a[c]);c=e}else return Qc(d)}R.prototype[Fb]=function(){return yd(this)};function Wg(a){return vb(a)?Vg(a):Qc(Mb(Pc,Oc(he),a))}\nvar Xg=function Xg(a){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return Xg.A(0<c.length?new Jb(c.slice(0),0,null):null)};Xg.A=function(a){return a instanceof Jb&&0===a.i?Vg(a.o):Wg(a)};Xg.L=0;Xg.N=function(a){return Xg.A(E(a))};function Ug(a,b,c,d,e,f){this.zb=a;this.node=b;this.i=c;this.ab=d;this.meta=e;this.w=f;this.m=32375020;this.J=1536}g=Ug.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};\ng.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();\ng.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.meta};g.Ka=function(){if(this.ab+1<this.node.length){var a=new Ug(this.zb,this.node,this.i,this.ab+1,null,null);return null==a?null:a}return this.Me(null)};\ng.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};g.K=function(a,b){return $d(this,b)};g.oa=function(){return tc(he,this.meta)};g.Fa=function(a,b){return Rg(this.zb,b,this.i+this.ab,H(this.zb))};g.Ga=function(a,b,c){return Sg(this.zb,b,c,this.i+this.ab,H(this.zb))};g.Ia=function(){return this.node[this.ab]};g.bb=function(){if(this.ab+1<this.node.length){var a=new Ug(this.zb,this.node,this.i,this.ab+1,null,null);return null==a?wd:a}return this.Hd(null)};g.S=function(){return this};\ng.ge=function(){var a=this.node;return new nf(a,this.ab,a.length)};g.Hd=function(){var a=this.i+this.node.length;return a<Qb(this.zb)?new Ug(this.zb,Mg(this.zb,a),a,0,null,null):wd};g.T=function(a,b){return new Ug(this.zb,this.node,this.i,this.ab,b,null)};g.X=function(a,b){return ae(b,this)};g.Me=function(){var a=this.i+this.node.length;return a<Qb(this.zb)?new Ug(this.zb,Mg(this.zb,a),a,0,null,null):null};Ug.prototype[Fb]=function(){return yd(this)};\nfunction Yg(a,b,c,d,e){this.meta=a;this.Ja=b;this.start=c;this.end=d;this.w=e;this.m=167666463;this.J=139264}g=Yg.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();\ng.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){return\"number\"===typeof b?this.ka(null,b,c):c};\ng.Qc=function(a,b,c){a=this.start;for(var d=0;;)if(a<this.end){var e=d,f=A.c(this.Ja,a);c=b.l?b.l(c,e,f):b.call(null,c,e,f);if(Hd(c))return B(c);d+=1;a+=1}else return c};g.$=function(a,b){return 0>b||this.end<=this.start+b?Lg(b,this.end-this.start):A.c(this.Ja,this.start+b)};g.ka=function(a,b,c){return 0>b||this.end<=this.start+b?c:A.l(this.Ja,this.start+b,c)};\ng.dc=function(a,b,c){a=this.start+b;if(0>b||this.end+1<=a)throw Error([\"Index \",v.h(b),\" out of bounds [0,\",v.h(this.W(null)),\"]\"].join(\"\"));b=this.meta;c=K.l(this.Ja,a,c);var d=this.end;a+=1;return Zg(b,c,this.start,d>a?d:a,null)};g.ba=function(){return null!=this.Ja&&q===this.Ja.fe?Qg(this.Ja,this.start,this.end):new Jf(Hf,this)};g.P=function(){return this.meta};g.W=function(){return this.end-this.start};g.Ac=function(){return A.c(this.Ja,this.end-1)};\ng.Bc=function(){if(this.start===this.end)throw Error(\"Can't pop empty vector\");return Zg(this.meta,this.Ja,this.start,this.end-1,null)};g.Rc=function(){return this.start!==this.end?new Zd(this,this.end-this.start-1,null):null};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};g.K=function(a,b){return $d(this,b)};g.oa=function(){return tc(he,this.meta)};g.Fa=function(a,b){return null!=this.Ja&&q===this.Ja.fe?Rg(this.Ja,b,this.start,this.end):Kd(this,b)};\ng.Ga=function(a,b,c){return null!=this.Ja&&q===this.Ja.fe?Sg(this.Ja,b,c,this.start,this.end):Ld(this,b,c)};g.O=function(a,b,c){if(\"number\"===typeof b)return this.dc(null,b,c);throw Error(\"Subvec's key for assoc must be a number.\");};g.S=function(){var a=this;return function(b){return function e(d){return d===a.end?null:ae(A.c(a.Ja,d),new kf(null,function(){return function(){return e(d+1)}}(b),null,null))}}(this)(a.start)};g.T=function(a,b){return Zg(b,this.Ja,this.start,this.end,this.w)};\ng.X=function(a,b){return Zg(this.meta,qc(this.Ja,this.end,b),this.start,this.end+1,null)};g.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return this.$(null,c);case 3:return this.ka(null,c,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.c=function(a,c){return this.$(null,c)};a.l=function(a,c,d){return this.ka(null,c,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return this.$(null,a)};\ng.c=function(a,b){return this.ka(null,a,b)};Yg.prototype[Fb]=function(){return yd(this)};function Zg(a,b,c,d,e){for(;;)if(b instanceof Yg)c=b.start+c,d=b.start+d,b=b.Ja;else{if(!ze(b))throw Error(\"v must satisfy IVector\");var f=H(b);if(0>c||0>d||c>f||d>f)throw Error(\"Index out of bounds\");return new Yg(a,b,c,d,e)}}function $g(a,b){return a===b.la?b:new Fg(a,Gb(b.o))}\nvar ah=function ah(a,b,c,d){c=$g(a.root.la,c);var f=a.F-1>>>b&31;if(5===b)a=d;else{var h=c.o[f];null!=h?(b-=5,a=ah.M?ah.M(a,b,h,d):ah.call(null,a,b,h,d)):a=Jg(a.root.la,b-5,d)}c.o[f]=a;return c};function Tg(a,b,c,d){this.F=a;this.shift=b;this.root=c;this.fa=d;this.J=88;this.m=275}g=Tg.prototype;\ng.Dc=function(a,b){if(this.root.la){if(32>this.F-Ig(this))this.fa[this.F&31]=b;else{var c=new Fg(this.root.la,this.fa),d=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];d[0]=b;this.fa=d;if(this.F>>>5>1<<this.shift){d=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];var e=this.shift+\n5;d[0]=this.root;d[1]=Jg(this.root.la,this.shift,c);this.root=new Fg(this.root.la,d);this.shift=e}else this.root=ah(this,this.shift,this.root,c)}this.F+=1;return this}throw Error(\"conj! after persistent!\");};g.kd=function(){if(this.root.la){this.root.la=null;var a=this.F-Ig(this),b=Array(a);Be(this.fa,0,b,0,a);return new R(null,this.F,this.shift,this.root,b,null)}throw Error(\"persistent! called twice\");};\ng.Cc=function(a,b,c){if(\"number\"===typeof b)return bh(this,b,c);throw Error(\"TransientVector's key for assoc! must be a number.\");};\nfunction bh(a,b,c){if(a.root.la){if(0<=b&&b<a.F){if(Ig(a)<=b)a.fa[b&31]=c;else{var d=function(){return function(){return function k(d,h){var f=$g(a.root.la,h);if(0===d)f.o[b&31]=c;else{var p=b>>>d&31,m=k(d-5,f.o[p]);f.o[p]=m}return f}}(a)(a.shift,a.root)}();a.root=d}return a}if(b===a.F)return a.Dc(null,c);throw Error([\"Index \",v.h(b),\" out of bounds for TransientVector of length\",v.h(a.F)].join(\"\"));}throw Error(\"assoc! after persistent!\");}\ng.W=function(){if(this.root.la)return this.F;throw Error(\"count after persistent!\");};g.$=function(a,b){if(this.root.la)return(0<=b&&b<this.F?Mg(this,b):Lg(b,this.F))[b&31];throw Error(\"nth after persistent!\");};g.ka=function(a,b,c){return 0<=b&&b<this.F?this.$(null,b):c};g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){return\"number\"===typeof b?this.ka(null,b,c):c};\ng.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return this.V(null,c);case 3:return this.I(null,c,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.c=function(a,c){return this.V(null,c)};a.l=function(a,c,d){return this.I(null,c,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return this.V(null,a)};g.c=function(a,b){return this.I(null,a,b)};function ch(){this.m=2097152;this.J=0}\nch.prototype.equiv=function(a){return this.K(null,a)};ch.prototype.K=function(){return!1};var dh=new ch;function eh(a,b){return Ee(xe(b)&&!ye(b)?H(a)===H(b)?(null!=a?a.m&1048576||q===a.Uf||(a.m?0:Ab(wc,a)):Ab(wc,a))?Ue(function(a,d,e){return G.c(D.l(b,d,dh),e)?!0:new Gd(!1)},!0,a):Vf(function(a){return G.c(D.l(b,y(a),dh),ee(a))},a):null:null)}function fh(a,b,c,d,e){this.i=a;this.Mf=b;this.Ie=c;this.wf=d;this.Se=e}fh.prototype.ja=function(){var a=this.i<this.Ie;return a?a:this.Se.ja()};\nfh.prototype.next=function(){if(this.i<this.Ie){var a=Vd(this.wf,this.i);this.i+=1;return new R(null,2,5,T,[a,cc.c(this.Mf,a)],null)}return this.Se.next()};fh.prototype.remove=function(){return Error(\"Unsupported operation\")};function gh(a){this.s=a}gh.prototype.next=function(){if(null!=this.s){var a=y(this.s),b=J(a,0,null);a=J(a,1,null);this.s=z(this.s);return{value:[b,a],done:!1}}return{value:null,done:!0}};function hh(a){this.s=a}\nhh.prototype.next=function(){if(null!=this.s){var a=y(this.s);this.s=z(this.s);return{value:[a,a],done:!1}}return{value:null,done:!0}};\nfunction ih(a,b){if(b instanceof L)a:{var c=a.length;for(var d=b.ea,e=0;;){if(c<=e){c=-1;break a}if(a[e]instanceof L&&d===a[e].ea){c=e;break a}e+=2}}else if(ca(b)||\"number\"===typeof b)a:for(c=a.length,d=0;;){if(c<=d){c=-1;break a}if(b===a[d]){c=d;break a}d+=2}else if(b instanceof rd)a:for(c=a.length,d=b.Zb,e=0;;){if(c<=e){c=-1;break a}if(a[e]instanceof rd&&d===a[e].Zb){c=e;break a}e+=2}else if(null==b)a:for(c=a.length,d=0;;){if(c<=d){c=-1;break a}if(null==a[d]){c=d;break a}d+=2}else a:for(c=a.length,\nd=0;;){if(c<=d){c=-1;break a}if(G.c(b,a[d])){c=d;break a}d+=2}return c}function jh(a,b,c){this.o=a;this.i=b;this.hb=c;this.m=32374990;this.J=0}g=jh.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();\ng.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.hb};g.Ka=function(){return this.i<this.o.length-2?new jh(this.o,this.i+2,this.hb):null};g.W=function(){return(this.o.length-this.i)/2};g.U=function(){return Ad(this)};\ng.K=function(a,b){return $d(this,b)};g.oa=function(){return tc(wd,this.hb)};g.Fa=function(a,b){return ce(b,this)};g.Ga=function(a,b,c){return de(b,c,this)};g.Ia=function(){return new R(null,2,5,T,[this.o[this.i],this.o[this.i+1]],null)};g.bb=function(){return this.i<this.o.length-2?new jh(this.o,this.i+2,this.hb):wd};g.S=function(){return this};g.T=function(a,b){return new jh(this.o,this.i,b)};g.X=function(a,b){return ae(b,this)};jh.prototype[Fb]=function(){return yd(this)};\nfunction kh(a,b,c){this.o=a;this.i=b;this.F=c}kh.prototype.ja=function(){return this.i<this.F};kh.prototype.next=function(){var a=new R(null,2,5,T,[this.o[this.i],this.o[this.i+1]],null);this.i+=2;return a};function r(a,b,c,d){this.meta=a;this.F=b;this.o=c;this.w=d;this.m=16647951;this.J=139268}g=r.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};g.keys=function(){return yd(lh(this))};g.entries=function(){return new gh(E(E(this)))};g.values=function(){return yd(mh(this))};\ng.has=function(a){return He(this,a)};g.get=function(a,b){return this.I(null,a,b)};g.forEach=function(a){for(var b=E(this),c=null,d=0,e=0;;)if(e<d){var f=c.$(null,e),h=J(f,0,null);f=J(f,1,null);a.c?a.c(f,h):a.call(null,f,h);e+=1}else if(b=E(b))Ae(b)?(c=Wc(b),b=Xc(b),h=c,d=H(c),c=h):(c=y(b),h=J(c,0,null),f=J(c,1,null),a.c?a.c(f,h):a.call(null,f,h),b=z(b),c=null,d=0),e=0;else return null};g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){a=ih(this.o,b);return-1===a?c:this.o[a+1]};\ng.Qc=function(a,b,c){a=this.o.length;for(var d=0;;)if(d<a){var e=this.o[d],f=this.o[d+1];c=b.l?b.l(c,e,f):b.call(null,c,e,f);if(Hd(c))return B(c);d+=2}else return c};g.ba=function(){return new kh(this.o,0,2*this.F)};g.P=function(){return this.meta};g.W=function(){return this.F};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Dd(this)};\ng.K=function(a,b){if(xe(b)&&!ye(b)){var c=this.o.length;if(this.F===b.W(null))for(var d=0;;)if(d<c){var e=b.I(null,this.o[d],Ce);if(e!==Ce)if(G.c(this.o[d+1],e))d+=2;else return!1;else return!1}else return!0;else return!1}else return!1};g.Pc=function(){return new nh({},this.o.length,Gb(this.o))};g.oa=function(){return tc(Ef,this.meta)};g.Fa=function(a,b){return Re(this,b)};g.Ga=function(a,b,c){return Se(this,b,c)};\ng.ga=function(a,b){if(0<=ih(this.o,b)){var c=this.o.length,d=c-2;if(0===d)return this.oa(null);d=Array(d);for(var e=0,f=0;;){if(e>=c)return new r(this.meta,this.F-1,d,null);G.c(b,this.o[e])||(d[f]=this.o[e],d[f+1]=this.o[e+1],f+=2);e+=2}}else return this};\ng.O=function(a,b,c){a=ih(this.o,b);if(-1===a){if(this.F<oh){a=this.o;for(var d=a.length,e=Array(d+2),f=0;;)if(f<d)e[f]=a[f],f+=1;else break;e[d]=b;e[d+1]=c;return new r(this.meta,this.F+1,e,null)}return tc(ec(wg.c(ph,this),b,c),this.meta)}if(c===this.o[a+1])return this;b=Gb(this.o);b[a+1]=c;return new r(this.meta,this.F,b,null)};g.yc=function(a,b){return-1!==ih(this.o,b)};g.S=function(){var a=this.o;return 0<=a.length-2?new jh(a,0,null):null};g.T=function(a,b){return new r(b,this.F,this.o,this.w)};\ng.X=function(a,b){if(ze(b))return this.O(null,A.c(b,0),A.c(b,1));for(var c=this,d=E(b);;){if(null==d)return c;var e=y(d);if(ze(e))c=c.O(null,A.c(e,0),A.c(e,1)),d=z(d);else throw Error(\"conj on a map takes map entries or seqables of map entries\");}};\ng.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return this.V(null,c);case 3:return this.I(null,c,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.c=function(a,c){return this.V(null,c)};a.l=function(a,c,d){return this.I(null,c,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return this.V(null,a)};g.c=function(a,b){return this.I(null,a,b)};var Ef=new r(null,0,[],Ed),oh=8;\nfunction ke(a){for(var b=[],c=0;;)if(c<a.length){var d=a[c],e=a[c+1],f=ih(b,d);-1===f?(f=b,f.push(d),f.push(e)):b[f+1]=e;c+=2}else break;return new r(null,b.length/2,b,null)}r.prototype[Fb]=function(){return yd(this)};function nh(a,b,c){this.Uc=a;this.Zc=b;this.o=c;this.m=258;this.J=56}g=nh.prototype;g.W=function(){if(t(this.Uc))return Ze(this.Zc);throw Error(\"count after persistent!\");};g.V=function(a,b){return this.I(null,b,null)};\ng.I=function(a,b,c){if(t(this.Uc))return a=ih(this.o,b),-1===a?c:this.o[a+1];throw Error(\"lookup after persistent!\");};g.Dc=function(a,b){if(t(this.Uc)){if(null!=b?b.m&2048||q===b.sf||(b.m?0:Ab(hc,b)):Ab(hc,b))return this.Cc(null,jc(b),kc(b));for(var c=E(b),d=this;;){var e=y(c);if(t(e))c=z(c),d=d.Cc(null,jc(e),kc(e));else return d}}else throw Error(\"conj! after persistent!\");};\ng.kd=function(){if(t(this.Uc))return this.Uc=!1,new r(null,Ze(this.Zc),this.o,null);throw Error(\"persistent! called twice\");};g.Cc=function(a,b,c){if(t(this.Uc)){a=ih(this.o,b);if(-1===a){if(this.Zc+2<=2*oh)return this.Zc+=2,this.o.push(b),this.o.push(c),this;a:{a=this.Zc;var d=this.o;var e=Oc(ph);for(var f=0;;)if(f<a)e=Rc(e,d[f],d[f+1]),f+=2;else break a}return Rc(e,b,c)}c!==this.o[a+1]&&(this.o[a+1]=c);return this}throw Error(\"assoc! after persistent!\");};function qh(){this.H=!1}\nfunction rh(a,b){return a===b?!0:N(a,b)?!0:G.c(a,b)}function sh(a,b,c){a=Gb(a);a[b]=c;return a}function th(a,b){var c=Array(a.length-2);Be(a,0,c,0,2*b);Be(a,2*(b+1),c,2*b,c.length-2*b);return c}function uh(a,b,c,d){a=a.Gc(b);a.o[c]=d;return a}function vh(a,b,c){for(var d=a.length,e=0,f=c;;)if(e<d){c=a[e];if(null!=c){var h=a[e+1];c=b.l?b.l(f,c,h):b.call(null,f,c,h)}else c=a[e+1],c=null!=c?c.Jc(b,f):f;if(Hd(c))return c;e+=2;f=c}else return f}\nfunction wh(a,b,c,d){this.o=a;this.i=b;this.sd=c;this.Lb=d}wh.prototype.advance=function(){for(var a=this.o.length;;)if(this.i<a){var b=this.o[this.i],c=this.o[this.i+1];null!=b?b=this.sd=new R(null,2,5,T,[b,c],null):null!=c?(b=dd(c),b=b.ja()?this.Lb=b:!1):b=!1;this.i+=2;if(b)return!0}else return!1};wh.prototype.ja=function(){var a=null!=this.sd;return a?a:(a=null!=this.Lb)?a:this.advance()};\nwh.prototype.next=function(){if(null!=this.sd){var a=this.sd;this.sd=null;return a}if(null!=this.Lb)return a=this.Lb.next(),this.Lb.ja()||(this.Lb=null),a;if(this.advance())return this.next();throw Error(\"No such element\");};wh.prototype.remove=function(){return Error(\"Unsupported operation\")};function xh(a,b,c){this.la=a;this.na=b;this.o=c;this.J=131072;this.m=0}g=xh.prototype;\ng.Gc=function(a){if(a===this.la)return this;var b=$e(this.na),c=Array(0>b?4:2*(b+1));Be(this.o,0,c,0,2*b);return new xh(a,this.na,c)};g.qd=function(){return yh(this.o,0,null)};g.Jc=function(a,b){return vh(this.o,a,b)};g.sc=function(a,b,c,d){var e=1<<(b>>>a&31);if(0===(this.na&e))return d;var f=$e(this.na&e-1);e=this.o[2*f];f=this.o[2*f+1];return null==e?f.sc(a+5,b,c,d):rh(c,e)?f:d};\ng.Kb=function(a,b,c,d,e,f){var h=1<<(c>>>b&31),k=$e(this.na&h-1);if(0===(this.na&h)){var l=$e(this.na);if(2*l<this.o.length){a=this.Gc(a);b=a.o;f.H=!0;a:for(c=2*(l-k),f=2*k+(c-1),l=2*(k+1)+(c-1);;){if(0===c)break a;b[l]=b[f];--l;--c;--f}b[2*k]=d;b[2*k+1]=e;a.na|=h;return a}if(16<=l){k=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];k[c>>>b&31]=zh.Kb(a,b+5,c,d,e,f);for(e=d=0;;)if(32>d)0!==\n(this.na>>>d&1)&&(k[d]=null!=this.o[e]?zh.Kb(a,b+5,od(this.o[e]),this.o[e],this.o[e+1],f):this.o[e+1],e+=2),d+=1;else break;return new Ah(a,l+1,k)}b=Array(2*(l+4));Be(this.o,0,b,0,2*k);b[2*k]=d;b[2*k+1]=e;Be(this.o,2*k,b,2*(k+1),2*(l-k));f.H=!0;a=this.Gc(a);a.o=b;a.na|=h;return a}l=this.o[2*k];h=this.o[2*k+1];if(null==l)return l=h.Kb(a,b+5,c,d,e,f),l===h?this:uh(this,a,2*k+1,l);if(rh(d,l))return e===h?this:uh(this,a,2*k+1,e);f.H=!0;f=b+5;b=od(l);if(b===c)e=new Bh(null,b,2,[l,h,d,e]);else{var p=new qh;\ne=zh.Kb(a,f,b,l,h,p).Kb(a,f,c,d,e,p)}d=2*k;k=2*k+1;a=this.Gc(a);a.o[d]=null;a.o[k]=e;return a};\ng.Jb=function(a,b,c,d,e){var f=1<<(b>>>a&31),h=$e(this.na&f-1);if(0===(this.na&f)){var k=$e(this.na);if(16<=k){h=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];h[b>>>a&31]=zh.Jb(a+5,b,c,d,e);for(d=c=0;;)if(32>c)0!==(this.na>>>c&1)&&(h[c]=null!=this.o[d]?zh.Jb(a+5,od(this.o[d]),this.o[d],this.o[d+1],e):this.o[d+1],d+=2),c+=1;else break;return new Ah(null,k+1,h)}a=Array(2*(k+1));Be(this.o,\n0,a,0,2*h);a[2*h]=c;a[2*h+1]=d;Be(this.o,2*h,a,2*(h+1),2*(k-h));e.H=!0;return new xh(null,this.na|f,a)}var l=this.o[2*h];f=this.o[2*h+1];if(null==l)return k=f.Jb(a+5,b,c,d,e),k===f?this:new xh(null,this.na,sh(this.o,2*h+1,k));if(rh(c,l))return d===f?this:new xh(null,this.na,sh(this.o,2*h+1,d));e.H=!0;e=this.na;k=this.o;a+=5;var p=od(l);if(p===b)c=new Bh(null,p,2,[l,f,c,d]);else{var m=new qh;c=zh.Jb(a,p,l,f,m).Jb(a,b,c,d,m)}a=2*h;h=2*h+1;d=Gb(k);d[a]=null;d[h]=c;return new xh(null,e,d)};\ng.rd=function(a,b,c){var d=1<<(b>>>a&31);if(0===(this.na&d))return this;var e=$e(this.na&d-1),f=this.o[2*e],h=this.o[2*e+1];return null==f?(a=h.rd(a+5,b,c),a===h?this:null!=a?new xh(null,this.na,sh(this.o,2*e+1,a)):this.na===d?null:new xh(null,this.na^d,th(this.o,e))):rh(c,f)?new xh(null,this.na^d,th(this.o,e)):this};g.ba=function(){return new wh(this.o,0,null,null)};var zh=new xh(null,0,[]);function Ch(a,b,c){this.o=a;this.i=b;this.Lb=c}\nCh.prototype.ja=function(){for(var a=this.o.length;;){if(null!=this.Lb&&this.Lb.ja())return!0;if(this.i<a){var b=this.o[this.i];this.i+=1;null!=b&&(this.Lb=dd(b))}else return!1}};Ch.prototype.next=function(){if(this.ja())return this.Lb.next();throw Error(\"No such element\");};Ch.prototype.remove=function(){return Error(\"Unsupported operation\")};function Ah(a,b,c){this.la=a;this.F=b;this.o=c;this.J=131072;this.m=0}g=Ah.prototype;g.Gc=function(a){return a===this.la?this:new Ah(a,this.F,Gb(this.o))};\ng.qd=function(){return Dh(this.o,0,null)};g.Jc=function(a,b){for(var c=this.o.length,d=0,e=b;;)if(d<c){var f=this.o[d];if(null!=f&&(e=f.Jc(a,e),Hd(e)))return e;d+=1}else return e};g.sc=function(a,b,c,d){var e=this.o[b>>>a&31];return null!=e?e.sc(a+5,b,c,d):d};g.Kb=function(a,b,c,d,e,f){var h=c>>>b&31,k=this.o[h];if(null==k)return a=uh(this,a,h,zh.Kb(a,b+5,c,d,e,f)),a.F+=1,a;b=k.Kb(a,b+5,c,d,e,f);return b===k?this:uh(this,a,h,b)};\ng.Jb=function(a,b,c,d,e){var f=b>>>a&31,h=this.o[f];if(null==h)return new Ah(null,this.F+1,sh(this.o,f,zh.Jb(a+5,b,c,d,e)));a=h.Jb(a+5,b,c,d,e);return a===h?this:new Ah(null,this.F,sh(this.o,f,a))};\ng.rd=function(a,b,c){var d=b>>>a&31,e=this.o[d];if(null!=e){a=e.rd(a+5,b,c);if(a===e)d=this;else if(null==a)if(8>=this.F)a:{e=this.o;a=e.length;b=Array(2*(this.F-1));c=0;for(var f=1,h=0;;)if(c<a)c!==d&&null!=e[c]&&(b[f]=e[c],f+=2,h|=1<<c),c+=1;else{d=new xh(null,h,b);break a}}else d=new Ah(null,this.F-1,sh(this.o,d,a));else d=new Ah(null,this.F,sh(this.o,d,a));return d}return this};g.ba=function(){return new Ch(this.o,0,null)};\nfunction Eh(a,b,c){b*=2;for(var d=0;;)if(d<b){if(rh(c,a[d]))return d;d+=2}else return-1}function Bh(a,b,c,d){this.la=a;this.ec=b;this.F=c;this.o=d;this.J=131072;this.m=0}g=Bh.prototype;g.Gc=function(a){if(a===this.la)return this;var b=Array(2*(this.F+1));Be(this.o,0,b,0,2*this.F);return new Bh(a,this.ec,this.F,b)};g.qd=function(){return yh(this.o,0,null)};g.Jc=function(a,b){return vh(this.o,a,b)};g.sc=function(a,b,c,d){a=Eh(this.o,this.F,c);return 0>a?d:rh(c,this.o[a])?this.o[a+1]:d};\ng.Kb=function(a,b,c,d,e,f){if(c===this.ec){b=Eh(this.o,this.F,d);if(-1===b){if(this.o.length>2*this.F)return b=2*this.F,c=2*this.F+1,a=this.Gc(a),a.o[b]=d,a.o[c]=e,f.H=!0,a.F+=1,a;c=this.o.length;b=Array(c+2);Be(this.o,0,b,0,c);b[c]=d;b[c+1]=e;f.H=!0;d=this.F+1;a===this.la?(this.o=b,this.F=d,a=this):a=new Bh(this.la,this.ec,d,b);return a}return this.o[b+1]===e?this:uh(this,a,b+1,e)}return(new xh(a,1<<(this.ec>>>b&31),[null,this,null,null])).Kb(a,b,c,d,e,f)};\ng.Jb=function(a,b,c,d,e){return b===this.ec?(a=Eh(this.o,this.F,c),-1===a?(a=2*this.F,b=Array(a+2),Be(this.o,0,b,0,a),b[a]=c,b[a+1]=d,e.H=!0,new Bh(null,this.ec,this.F+1,b)):G.c(this.o[a+1],d)?this:new Bh(null,this.ec,this.F,sh(this.o,a+1,d))):(new xh(null,1<<(this.ec>>>a&31),[null,this])).Jb(a,b,c,d,e)};g.rd=function(a,b,c){a=Eh(this.o,this.F,c);return-1===a?this:1===this.F?null:new Bh(null,this.ec,this.F-1,th(this.o,Ze(a)))};g.ba=function(){return new wh(this.o,0,null,null)};\nfunction Fh(a,b,c,d,e){this.meta=a;this.Mb=b;this.i=c;this.s=d;this.w=e;this.m=32374988;this.J=0}g=Fh.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();\ng.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.meta};g.Ka=function(){return null==this.s?yh(this.Mb,this.i+2,null):yh(this.Mb,this.i,z(this.s))};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};\ng.K=function(a,b){return $d(this,b)};g.oa=function(){return tc(wd,this.meta)};g.Fa=function(a,b){return ce(b,this)};g.Ga=function(a,b,c){return de(b,c,this)};g.Ia=function(){return null==this.s?new R(null,2,5,T,[this.Mb[this.i],this.Mb[this.i+1]],null):y(this.s)};g.bb=function(){var a=null==this.s?yh(this.Mb,this.i+2,null):yh(this.Mb,this.i,z(this.s));return null!=a?a:wd};g.S=function(){return this};g.T=function(a,b){return new Fh(b,this.Mb,this.i,this.s,this.w)};g.X=function(a,b){return ae(b,this)};\nFh.prototype[Fb]=function(){return yd(this)};function yh(a,b,c){if(null==c)for(c=a.length;;)if(b<c){if(null!=a[b])return new Fh(null,a,b,null,null);var d=a[b+1];if(t(d)&&(d=d.qd(),t(d)))return new Fh(null,a,b+2,d,null);b+=2}else return null;else return new Fh(null,a,b,c,null)}function Gh(a,b,c,d,e){this.meta=a;this.Mb=b;this.i=c;this.s=d;this.w=e;this.m=32374988;this.J=0}g=Gh.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};\ng.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();\ng.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.meta};g.Ka=function(){return Dh(this.Mb,this.i,z(this.s))};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};g.K=function(a,b){return $d(this,b)};g.oa=function(){return tc(wd,this.meta)};\ng.Fa=function(a,b){return ce(b,this)};g.Ga=function(a,b,c){return de(b,c,this)};g.Ia=function(){return y(this.s)};g.bb=function(){var a=Dh(this.Mb,this.i,z(this.s));return null!=a?a:wd};g.S=function(){return this};g.T=function(a,b){return new Gh(b,this.Mb,this.i,this.s,this.w)};g.X=function(a,b){return ae(b,this)};Gh.prototype[Fb]=function(){return yd(this)};\nfunction Dh(a,b,c){if(null==c)for(c=a.length;;)if(b<c){var d=a[b];if(t(d)&&(d=d.qd(),t(d)))return new Gh(null,a,b+1,d,null);b+=1}else return null;else return new Gh(null,a,b,c,null)}function Hh(a,b,c){this.eb=a;this.bf=b;this.xe=c}Hh.prototype.ja=function(){return!this.xe||this.bf.ja()};Hh.prototype.next=function(){if(this.xe)return this.bf.next();this.xe=!0;return new R(null,2,5,T,[null,this.eb],null)};Hh.prototype.remove=function(){return Error(\"Unsupported operation\")};\nfunction Jh(a,b,c,d,e,f){this.meta=a;this.F=b;this.root=c;this.cb=d;this.eb=e;this.w=f;this.m=16123663;this.J=139268}g=Jh.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};g.keys=function(){return yd(lh(this))};g.entries=function(){return new gh(E(E(this)))};g.values=function(){return yd(mh(this))};g.has=function(a){return He(this,a)};g.get=function(a,b){return this.I(null,a,b)};\ng.forEach=function(a){for(var b=E(this),c=null,d=0,e=0;;)if(e<d){var f=c.$(null,e),h=J(f,0,null);f=J(f,1,null);a.c?a.c(f,h):a.call(null,f,h);e+=1}else if(b=E(b))Ae(b)?(c=Wc(b),b=Xc(b),h=c,d=H(c),c=h):(c=y(b),h=J(c,0,null),f=J(c,1,null),a.c?a.c(f,h):a.call(null,f,h),b=z(b),c=null,d=0),e=0;else return null};g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){return null==b?this.cb?this.eb:c:null==this.root?c:this.root.sc(0,od(b),b,c)};\ng.Qc=function(a,b,c){a=this.cb?b.l?b.l(c,null,this.eb):b.call(null,c,null,this.eb):c;return Hd(a)?B(a):null!=this.root?Jd(this.root.Jc(b,a)):a};g.ba=function(){var a=this.root?dd(this.root):Cf();return this.cb?new Hh(this.eb,a,!1):a};g.P=function(){return this.meta};g.W=function(){return this.F};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Dd(this)};g.K=function(a,b){return eh(this,b)};g.Pc=function(){return new Kh({},this.root,this.F,this.cb,this.eb)};g.oa=function(){return tc(ph,this.meta)};\ng.ga=function(a,b){if(null==b)return this.cb?new Jh(this.meta,this.F-1,this.root,!1,null,null):this;if(null==this.root)return this;var c=this.root.rd(0,od(b),b);return c===this.root?this:new Jh(this.meta,this.F-1,c,this.cb,this.eb,null)};\ng.O=function(a,b,c){if(null==b)return this.cb&&c===this.eb?this:new Jh(this.meta,this.cb?this.F:this.F+1,this.root,!0,c,null);a=new qh;b=(null==this.root?zh:this.root).Jb(0,od(b),b,c,a);return b===this.root?this:new Jh(this.meta,a.H?this.F+1:this.F,b,this.cb,this.eb,null)};g.yc=function(a,b){return null==b?this.cb:null==this.root?!1:this.root.sc(0,od(b),b,Ce)!==Ce};g.S=function(){if(0<this.F){var a=null!=this.root?this.root.qd():null;return this.cb?ae(new R(null,2,5,T,[null,this.eb],null),a):a}return null};\ng.T=function(a,b){return new Jh(b,this.F,this.root,this.cb,this.eb,this.w)};g.X=function(a,b){if(ze(b))return this.O(null,A.c(b,0),A.c(b,1));for(var c=this,d=E(b);;){if(null==d)return c;var e=y(d);if(ze(e))c=c.O(null,A.c(e,0),A.c(e,1)),d=z(d);else throw Error(\"conj on a map takes map entries or seqables of map entries\");}};\ng.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return this.V(null,c);case 3:return this.I(null,c,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.c=function(a,c){return this.V(null,c)};a.l=function(a,c,d){return this.I(null,c,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return this.V(null,a)};g.c=function(a,b){return this.I(null,a,b)};var ph=new Jh(null,0,null,!1,null,Ed);\nfunction Pe(a,b){for(var c=a.length,d=0,e=Oc(ph);;)if(d<c){var f=d+1;e=e.Cc(null,a[d],b[d]);d=f}else return Qc(e)}Jh.prototype[Fb]=function(){return yd(this)};function Kh(a,b,c,d,e){this.la=a;this.root=b;this.count=c;this.cb=d;this.eb=e;this.m=258;this.J=56}\nfunction Lh(a,b,c){if(a.la){if(null==b)a.eb!==c&&(a.eb=c),a.cb||(a.count+=1,a.cb=!0);else{var d=new qh;b=(null==a.root?zh:a.root).Kb(a.la,0,od(b),b,c,d);b!==a.root&&(a.root=b);d.H&&(a.count+=1)}return a}throw Error(\"assoc! after persistent!\");}g=Kh.prototype;g.W=function(){if(this.la)return this.count;throw Error(\"count after persistent!\");};g.V=function(a,b){return null==b?this.cb?this.eb:null:null==this.root?null:this.root.sc(0,od(b),b)};\ng.I=function(a,b,c){return null==b?this.cb?this.eb:c:null==this.root?c:this.root.sc(0,od(b),b,c)};g.Dc=function(a,b){a:if(this.la)if(null!=b?b.m&2048||q===b.sf||(b.m?0:Ab(hc,b)):Ab(hc,b))var c=Lh(this,jc(b),kc(b));else{c=E(b);for(var d=this;;){var e=y(c);if(t(e))c=z(c),d=Lh(d,jc(e),kc(e));else{c=d;break a}}}else throw Error(\"conj! after persistent\");return c};\ng.kd=function(){if(this.la){this.la=null;var a=new Jh(null,this.count,this.root,this.cb,this.eb,null)}else throw Error(\"persistent! called twice\");return a};g.Cc=function(a,b,c){return Lh(this,b,c)};function Mh(a,b,c){for(var d=b;;)if(null!=a)b=c?a.left:a.right,d=ge.c(d,a),a=b;else return d}function Nh(a,b,c,d,e){this.meta=a;this.stack=b;this.vc=c;this.F=d;this.w=e;this.m=32374990;this.J=0}g=Nh.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};\ng.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();\ng.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.meta};g.Ka=function(){var a=y(this.stack);a=Mh(this.vc?a.right:a.left,z(this.stack),this.vc);return null==a?null:new Nh(null,a,this.vc,this.F-1,null)};\ng.W=function(){return 0>this.F?H(z(this))+1:this.F};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};g.K=function(a,b){return $d(this,b)};g.oa=function(){return tc(wd,this.meta)};g.Fa=function(a,b){return ce(b,this)};g.Ga=function(a,b,c){return de(b,c,this)};g.Ia=function(){var a=this.stack;return null==a?null:nc(a)};g.bb=function(){var a=y(this.stack);a=Mh(this.vc?a.right:a.left,z(this.stack),this.vc);return null!=a?new Nh(null,a,this.vc,this.F-1,null):wd};g.S=function(){return this};\ng.T=function(a,b){return new Nh(b,this.stack,this.vc,this.F,this.w)};g.X=function(a,b){return ae(b,this)};Nh.prototype[Fb]=function(){return yd(this)};function Oh(a,b,c){return new Nh(null,Mh(a,null,b),b,c,null)}\nfunction Ph(a,b,c,d){return c instanceof Qh?c.left instanceof Qh?new Qh(c.key,c.H,c.left.bc(),new Rh(a,b,c.right,d,null),null):c.right instanceof Qh?new Qh(c.right.key,c.right.H,new Rh(c.key,c.H,c.left,c.right.left,null),new Rh(a,b,c.right.right,d,null),null):new Rh(a,b,c,d,null):new Rh(a,b,c,d,null)}\nfunction Sh(a,b,c,d){return d instanceof Qh?d.right instanceof Qh?new Qh(d.key,d.H,new Rh(a,b,c,d.left,null),d.right.bc(),null):d.left instanceof Qh?new Qh(d.left.key,d.left.H,new Rh(a,b,c,d.left.left,null),new Rh(d.key,d.H,d.left.right,d.right,null),null):new Rh(a,b,c,d,null):new Rh(a,b,c,d,null)}\nfunction Th(a,b,c,d){if(c instanceof Qh)return new Qh(a,b,c.bc(),d,null);if(d instanceof Rh)return Sh(a,b,c,d.ud());if(d instanceof Qh&&d.left instanceof Rh)return new Qh(d.left.key,d.left.H,new Rh(a,b,c,d.left.left,null),Sh(d.key,d.H,d.left.right,d.right.ud()),null);throw Error(\"red-black tree invariant violation\");}\nfunction Uh(a,b,c,d){if(d instanceof Qh)return new Qh(a,b,c,d.bc(),null);if(c instanceof Rh)return Ph(a,b,c.ud(),d);if(c instanceof Qh&&c.right instanceof Rh)return new Qh(c.right.key,c.right.H,Ph(c.key,c.H,c.left.ud(),c.right.left),new Rh(a,b,c.right.right,d,null),null);throw Error(\"red-black tree invariant violation\");}\nvar Vh=function Vh(a,b,c){var e=null!=a.left?function(){var e=a.left;return Vh.l?Vh.l(e,b,c):Vh.call(null,e,b,c)}():c;if(Hd(e))return e;var f=function(){var c=a.key,f=a.H;return b.l?b.l(e,c,f):b.call(null,e,c,f)}();if(Hd(f))return f;if(null!=a.right){var h=a.right;return Vh.l?Vh.l(h,b,f):Vh.call(null,h,b,f)}return f};function Rh(a,b,c,d,e){this.key=a;this.H=b;this.left=c;this.right=d;this.w=e;this.m=32402207;this.J=0}g=Rh.prototype;\ng.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();\ng.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();g.Ee=function(a){return a.He(this)};g.ud=function(){return new Qh(this.key,this.H,this.left,this.right,null)};g.bc=function(){return this};g.De=function(a){return a.Ge(this)};g.replace=function(a,b,c,d){return new Rh(a,b,c,d,null)};\ng.Ge=function(a){return new Rh(a.key,a.H,this,a.right,null)};g.He=function(a){return new Rh(a.key,a.H,a.left,this,null)};g.Jc=function(a,b){return Vh(this,a,b)};g.V=function(a,b){return this.ka(null,b,null)};g.I=function(a,b,c){return this.ka(null,b,c)};g.$=function(a,b){if(0===b)return this.key;if(1===b)return this.H;throw Error(\"Index out of bounds\");};g.ka=function(a,b,c){return 0===b?this.key:1===b?this.H:c};g.dc=function(a,b,c){return(new R(null,2,5,T,[this.key,this.H],null)).dc(null,b,c)};\ng.P=function(){return null};g.W=function(){return 2};g.fd=function(){return this.key};g.gd=function(){return this.H};g.Ac=function(){return this.H};g.Bc=function(){return new R(null,1,5,T,[this.key],null)};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};g.K=function(a,b){return $d(this,b)};g.oa=function(){return he};g.Fa=function(a,b){return Kd(this,b)};g.Ga=function(a,b,c){return Ld(this,b,c)};g.O=function(a,b,c){return K.l(new R(null,2,5,T,[this.key,this.H],null),b,c)};\ng.yc=function(a,b){return 0===b||1===b};g.S=function(){var a=this.key;return Tb(Tb(wd,this.H),a)};g.T=function(a,b){return tc(new R(null,2,5,T,[this.key,this.H],null),b)};g.X=function(a,b){return new R(null,3,5,T,[this.key,this.H,b],null)};\ng.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return this.$(null,c);case 3:return this.ka(null,c,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.c=function(a,c){return this.$(null,c)};a.l=function(a,c,d){return this.ka(null,c,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return this.$(null,a)};g.c=function(a,b){return this.ka(null,a,b)};Rh.prototype[Fb]=function(){return yd(this)};\nfunction Qh(a,b,c,d,e){this.key=a;this.H=b;this.left=c;this.right=d;this.w=e;this.m=32402207;this.J=0}g=Qh.prototype;g.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();\ng.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();g.Ee=function(a){return new Qh(this.key,this.H,this.left,a,null)};g.ud=function(){throw Error(\"red-black tree invariant violation\");};g.bc=function(){return new Rh(this.key,this.H,this.left,this.right,null)};\ng.De=function(a){return new Qh(this.key,this.H,a,this.right,null)};g.replace=function(a,b,c,d){return new Qh(a,b,c,d,null)};g.Ge=function(a){return this.left instanceof Qh?new Qh(this.key,this.H,this.left.bc(),new Rh(a.key,a.H,this.right,a.right,null),null):this.right instanceof Qh?new Qh(this.right.key,this.right.H,new Rh(this.key,this.H,this.left,this.right.left,null),new Rh(a.key,a.H,this.right.right,a.right,null),null):new Rh(a.key,a.H,this,a.right,null)};\ng.He=function(a){return this.right instanceof Qh?new Qh(this.key,this.H,new Rh(a.key,a.H,a.left,this.left,null),this.right.bc(),null):this.left instanceof Qh?new Qh(this.left.key,this.left.H,new Rh(a.key,a.H,a.left,this.left.left,null),new Rh(this.key,this.H,this.left.right,this.right,null),null):new Rh(a.key,a.H,a.left,this,null)};g.Jc=function(a,b){return Vh(this,a,b)};g.V=function(a,b){return this.ka(null,b,null)};g.I=function(a,b,c){return this.ka(null,b,c)};\ng.$=function(a,b){if(0===b)return this.key;if(1===b)return this.H;throw Error(\"Index out of bounds\");};g.ka=function(a,b,c){return 0===b?this.key:1===b?this.H:c};g.dc=function(a,b,c){return(new R(null,2,5,T,[this.key,this.H],null)).dc(null,b,c)};g.P=function(){return null};g.W=function(){return 2};g.fd=function(){return this.key};g.gd=function(){return this.H};g.Ac=function(){return this.H};g.Bc=function(){return new R(null,1,5,T,[this.key],null)};\ng.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};g.K=function(a,b){return $d(this,b)};g.oa=function(){return he};g.Fa=function(a,b){return Kd(this,b)};g.Ga=function(a,b,c){return Ld(this,b,c)};g.O=function(a,b,c){return K.l(new R(null,2,5,T,[this.key,this.H],null),b,c)};g.yc=function(a,b){return 0===b||1===b};g.S=function(){var a=this.key;return Tb(Tb(wd,this.H),a)};g.T=function(a,b){return tc(new R(null,2,5,T,[this.key,this.H],null),b)};\ng.X=function(a,b){return new R(null,3,5,T,[this.key,this.H,b],null)};g.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return this.$(null,c);case 3:return this.ka(null,c,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.c=function(a,c){return this.$(null,c)};a.l=function(a,c,d){return this.ka(null,c,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return this.$(null,a)};\ng.c=function(a,b){return this.ka(null,a,b)};Qh.prototype[Fb]=function(){return yd(this)};\nvar Wh=function Wh(a,b,c,d,e){if(null==b)return new Qh(c,d,null,null,null);var h=function(){var d=b.key;return a.c?a.c(c,d):a.call(null,c,d)}();if(0===h)return e[0]=b,null;if(0>h)return h=function(){var h=b.left;return Wh.Z?Wh.Z(a,h,c,d,e):Wh.call(null,a,h,c,d,e)}(),null!=h?b.De(h):null;h=function(){var h=b.right;return Wh.Z?Wh.Z(a,h,c,d,e):Wh.call(null,a,h,c,d,e)}();return null!=h?b.Ee(h):null},Xh=function Xh(a,b){if(null==a)return b;if(null==b)return a;if(a instanceof Qh){if(b instanceof Qh){var d=\nfunction(){var d=a.right,f=b.left;return Xh.c?Xh.c(d,f):Xh.call(null,d,f)}();return d instanceof Qh?new Qh(d.key,d.H,new Qh(a.key,a.H,a.left,d.left,null),new Qh(b.key,b.H,d.right,b.right,null),null):new Qh(a.key,a.H,a.left,new Qh(b.key,b.H,d,b.right,null),null)}return new Qh(a.key,a.H,a.left,function(){var d=a.right;return Xh.c?Xh.c(d,b):Xh.call(null,d,b)}(),null)}if(b instanceof Qh)return new Qh(b.key,b.H,function(){var d=b.left;return Xh.c?Xh.c(a,d):Xh.call(null,a,d)}(),b.right,null);d=function(){var d=\na.right,f=b.left;return Xh.c?Xh.c(d,f):Xh.call(null,d,f)}();return d instanceof Qh?new Qh(d.key,d.H,new Rh(a.key,a.H,a.left,d.left,null),new Rh(b.key,b.H,d.right,b.right,null),null):Th(a.key,a.H,a.left,new Rh(b.key,b.H,d,b.right,null))},Yh=function Yh(a,b,c,d){if(null!=b){var f=function(){var d=b.key;return a.c?a.c(c,d):a.call(null,c,d)}();if(0===f)return d[0]=b,Xh(b.left,b.right);if(0>f)return f=function(){var f=b.left;return Yh.M?Yh.M(a,f,c,d):Yh.call(null,a,f,c,d)}(),null!=f||null!=d[0]?b.left instanceof\nRh?Th(b.key,b.H,f,b.right):new Qh(b.key,b.H,f,b.right,null):null;f=function(){var f=b.right;return Yh.M?Yh.M(a,f,c,d):Yh.call(null,a,f,c,d)}();return null!=f||null!=d[0]?b.right instanceof Rh?Uh(b.key,b.H,b.left,f):new Qh(b.key,b.H,b.left,f,null):null}return null},Zh=function Zh(a,b,c,d){var f=b.key,h=a.c?a.c(c,f):a.call(null,c,f);return 0===h?b.replace(f,d,b.left,b.right):0>h?b.replace(f,b.H,function(){var f=b.left;return Zh.M?Zh.M(a,f,c,d):Zh.call(null,a,f,c,d)}(),b.right):b.replace(f,b.H,b.left,\nfunction(){var f=b.right;return Zh.M?Zh.M(a,f,c,d):Zh.call(null,a,f,c,d)}())};function $h(a,b,c,d,e){this.Bb=a;this.mc=b;this.F=c;this.meta=d;this.w=e;this.m=418776847;this.J=8192}g=$h.prototype;g.forEach=function(a){for(var b=E(this),c=null,d=0,e=0;;)if(e<d){var f=c.$(null,e),h=J(f,0,null);f=J(f,1,null);a.c?a.c(f,h):a.call(null,f,h);e+=1}else if(b=E(b))Ae(b)?(c=Wc(b),b=Xc(b),h=c,d=H(c),c=h):(c=y(b),h=J(c,0,null),f=J(c,1,null),a.c?a.c(f,h):a.call(null,f,h),b=z(b),c=null,d=0),e=0;else return null};\ng.get=function(a,b){return this.I(null,a,b)};g.entries=function(){return new gh(E(E(this)))};g.toString=function(){return fd(this)};g.keys=function(){return yd(lh(this))};g.values=function(){return yd(mh(this))};g.equiv=function(a){return this.K(null,a)};function ai(a,b){for(var c=a.mc;;)if(null!=c){var d=c.key;d=a.Bb.c?a.Bb.c(b,d):a.Bb.call(null,b,d);if(0===d)return c;c=0>d?c.left:c.right}else return null}g.has=function(a){return He(this,a)};g.V=function(a,b){return this.I(null,b,null)};\ng.I=function(a,b,c){a=ai(this,b);return null!=a?a.H:c};g.Qc=function(a,b,c){return null!=this.mc?Jd(Vh(this.mc,b,c)):c};g.P=function(){return this.meta};g.W=function(){return this.F};g.Rc=function(){return 0<this.F?Oh(this.mc,!1,this.F):null};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Dd(this)};g.K=function(a,b){return eh(this,b)};g.oa=function(){return new $h(this.Bb,null,0,this.meta,0)};\ng.ga=function(a,b){var c=[null],d=Yh(this.Bb,this.mc,b,c);return null==d?null==Vd(c,0)?this:new $h(this.Bb,null,0,this.meta,null):new $h(this.Bb,d.bc(),this.F-1,this.meta,null)};g.O=function(a,b,c){a=[null];var d=Wh(this.Bb,this.mc,b,c,a);return null==d?(a=Vd(a,0),G.c(c,a.H)?this:new $h(this.Bb,Zh(this.Bb,this.mc,b,c),this.F,this.meta,null)):new $h(this.Bb,d.bc(),this.F+1,this.meta,null)};g.yc=function(a,b){return null!=ai(this,b)};g.S=function(){return 0<this.F?Oh(this.mc,!0,this.F):null};\ng.T=function(a,b){return new $h(this.Bb,this.mc,this.F,b,this.w)};g.X=function(a,b){if(ze(b))return this.O(null,A.c(b,0),A.c(b,1));for(var c=this,d=E(b);;){if(null==d)return c;var e=y(d);if(ze(e))c=c.O(null,A.c(e,0),A.c(e,1)),d=z(d);else throw Error(\"conj on a map takes map entries or seqables of map entries\");}};\ng.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return this.V(null,c);case 3:return this.I(null,c,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.c=function(a,c){return this.V(null,c)};a.l=function(a,c,d){return this.I(null,c,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return this.V(null,a)};g.c=function(a,b){return this.I(null,a,b)};var bi=new $h(Ke,null,0,null,Ed);$h.prototype[Fb]=function(){return yd(this)};\nvar U=function U(a){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return U.A(0<c.length?new Jb(c.slice(0),0,null):null)};U.A=function(a){for(var b=E(a),c=Oc(ph);;)if(b){a=z(z(b));var d=y(b);b=ee(b);c=Rc(c,d,b);b=a}else return Qc(c)};U.L=0;U.N=function(a){return U.A(E(a))};var ci=function ci(a){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return ci.A(0<c.length?new Jb(c.slice(0),0,null):null)};\nci.A=function(a){a=a instanceof Jb&&0===a.i?a.o:Lb(a);return ke(a)};ci.L=0;ci.N=function(a){return ci.A(E(a))};function di(a){for(var b=[],c=arguments.length,d=0;;)if(d<c)b.push(arguments[d]),d+=1;else break;a:for(b=E(0<b.length?new Jb(b.slice(0),0,null):null),d=bi;;)if(b)c=z(z(b)),d=K.l(d,y(b),ee(b)),b=c;else break a;return d}function ei(a,b){this.da=a;this.hb=b;this.m=32374988;this.J=0}g=ei.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};\ng.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();\ng.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.hb};g.Ka=function(){var a=(null!=this.da?this.da.m&128||q===this.da.Id||(this.da.m?0:Ab(Zb,this.da)):Ab(Zb,this.da))?this.da.Ka(null):z(this.da);return null==a?null:new ei(a,this.hb)};g.U=function(){return Ad(this)};\ng.K=function(a,b){return $d(this,b)};g.oa=function(){return tc(wd,this.hb)};g.Fa=function(a,b){return ce(b,this)};g.Ga=function(a,b,c){return de(b,c,this)};g.Ia=function(){return this.da.Ia(null).fd(null)};g.bb=function(){var a=(null!=this.da?this.da.m&128||q===this.da.Id||(this.da.m?0:Ab(Zb,this.da)):Ab(Zb,this.da))?this.da.Ka(null):z(this.da);return null!=a?new ei(a,this.hb):wd};g.S=function(){return this};g.T=function(a,b){return new ei(this.da,b)};g.X=function(a,b){return ae(b,this)};\nei.prototype[Fb]=function(){return yd(this)};function lh(a){return(a=E(a))?new ei(a,null):null}function fi(a){return jc(a)}function gi(a,b){this.da=a;this.hb=b;this.m=32374988;this.J=0}g=gi.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};\ng.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();\ng.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.hb};g.Ka=function(){var a=(null!=this.da?this.da.m&128||q===this.da.Id||(this.da.m?0:Ab(Zb,this.da)):Ab(Zb,this.da))?this.da.Ka(null):z(this.da);return null==a?null:new gi(a,this.hb)};g.U=function(){return Ad(this)};\ng.K=function(a,b){return $d(this,b)};g.oa=function(){return tc(wd,this.hb)};g.Fa=function(a,b){return ce(b,this)};g.Ga=function(a,b,c){return de(b,c,this)};g.Ia=function(){return this.da.Ia(null).gd(null)};g.bb=function(){var a=(null!=this.da?this.da.m&128||q===this.da.Id||(this.da.m?0:Ab(Zb,this.da)):Ab(Zb,this.da))?this.da.Ka(null):z(this.da);return null!=a?new gi(a,this.hb):wd};g.S=function(){return this};g.T=function(a,b){return new gi(this.da,b)};g.X=function(a,b){return ae(b,this)};\ngi.prototype[Fb]=function(){return yd(this)};function mh(a){return(a=E(a))?new gi(a,null):null}var hi=function hi(a){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return hi.A(0<c.length?new Jb(c.slice(0),0,null):null)};hi.A=function(a){return t(Wf(Ve,a))?Te(function(a,c){return ge.c(t(a)?a:Ef,c)},a):null};hi.L=0;hi.N=function(a){return hi.A(E(a))};\nvar ii=function ii(a){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return ii.A(arguments[0],1<c.length?new Jb(c.slice(1),0,null):null)};ii.A=function(a,b){return t(Wf(Ve,b))?Te(function(a){return function(b,c){return Mb(a,t(b)?b:Ef,E(c))}}(function(b,d){var c=y(d),f=ee(d);return He(b,c)?K.l(b,c,function(){var d=D.c(b,c);return a.c?a.c(d,f):a.call(null,d,f)}()):K.l(b,c,f)}),b):null};ii.L=1;ii.N=function(a){var b=y(a);a=z(a);return ii.A(b,a)};\nfunction ji(a){for(var b=Ef,c=E(new R(null,7,5,T,[ki,li,mi,ni,oi,pi,qi],null));;)if(c){var d=y(c),e=D.l(a,d,ri);b=G.c(e,ri)?b:K.l(b,d,e);c=z(c)}else return tc(b,qe(a))}function si(a){this.te=a}si.prototype.ja=function(){return this.te.ja()};si.prototype.next=function(){if(this.te.ja())return this.te.next().fa[0];throw Error(\"No such element\");};si.prototype.remove=function(){return Error(\"Unsupported operation\")};function ti(a,b,c){this.meta=a;this.gc=b;this.w=c;this.m=15077647;this.J=139268}g=ti.prototype;\ng.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};g.keys=function(){return yd(E(this))};g.entries=function(){return new hh(E(E(this)))};g.values=function(){return yd(E(this))};g.has=function(a){return He(this,a)};\ng.forEach=function(a){for(var b=E(this),c=null,d=0,e=0;;)if(e<d){var f=c.$(null,e),h=J(f,0,null);f=J(f,1,null);a.c?a.c(f,h):a.call(null,f,h);e+=1}else if(b=E(b))Ae(b)?(c=Wc(b),b=Xc(b),h=c,d=H(c),c=h):(c=y(b),h=J(c,0,null),f=J(c,1,null),a.c?a.c(f,h):a.call(null,f,h),b=z(b),c=null,d=0),e=0;else return null};g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){return dc(this.gc,b)?b:c};g.ba=function(){return new si(dd(this.gc))};g.P=function(){return this.meta};g.W=function(){return Qb(this.gc)};\ng.U=function(){var a=this.w;return null!=a?a:this.w=a=Dd(this)};g.K=function(a,b){return ve(b)&&H(this)===H(b)&&Ue(function(){return function(a,d){var c=He(b,d);return c?c:new Gd(!1)}}(this),!0,this.gc)};g.Pc=function(){return new ui(Oc(this.gc))};g.oa=function(){return tc(vi,this.meta)};g.ie=function(a,b){return new ti(this.meta,gc(this.gc,b),null)};g.S=function(){return lh(this.gc)};g.T=function(a,b){return new ti(b,this.gc,this.w)};\ng.X=function(a,b){return new ti(this.meta,K.l(this.gc,b,null),null)};g.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return this.V(null,c);case 3:return this.I(null,c,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.c=function(a,c){return this.V(null,c)};a.l=function(a,c,d){return this.I(null,c,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return this.V(null,a)};\ng.c=function(a,b){return this.I(null,a,b)};var vi=new ti(null,Ef,Ed);function Je(a){for(var b=a.length,c=Oc(vi),d=0;;)if(d<b)Pc(c,a[d]),d+=1;else break;return Qc(c)}ti.prototype[Fb]=function(){return yd(this)};function ui(a){this.lc=a;this.J=136;this.m=259}g=ui.prototype;g.Dc=function(a,b){this.lc=Rc(this.lc,b,null);return this};g.kd=function(){return new ti(null,Qc(this.lc),null)};g.W=function(){return H(this.lc)};g.V=function(a,b){return this.I(null,b,null)};\ng.I=function(a,b,c){return cc.l(this.lc,b,Ce)===Ce?c:b};g.call=function(){function a(a,b,c){return cc.l(this.lc,b,Ce)===Ce?c:b}function b(a,b){return cc.l(this.lc,b,Ce)===Ce?null:b}var c=null;c=function(c,e,f){switch(arguments.length){case 2:return b.call(this,0,e);case 3:return a.call(this,0,e,f)}throw Error(\"Invalid arity: \"+(arguments.length-1));};c.c=b;c.l=a;return c}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};\ng.h=function(a){return cc.l(this.lc,a,Ce)===Ce?null:a};g.c=function(a,b){return cc.l(this.lc,a,Ce)===Ce?b:a};function wi(a,b,c){this.meta=a;this.$b=b;this.w=c;this.m=417730831;this.J=8192}g=wi.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};g.keys=function(){return yd(E(this))};g.entries=function(){return new hh(E(E(this)))};g.values=function(){return yd(E(this))};g.has=function(a){return He(this,a)};\ng.forEach=function(a){for(var b=E(this),c=null,d=0,e=0;;)if(e<d){var f=c.$(null,e),h=J(f,0,null);f=J(f,1,null);a.c?a.c(f,h):a.call(null,f,h);e+=1}else if(b=E(b))Ae(b)?(c=Wc(b),b=Xc(b),h=c,d=H(c),c=h):(c=y(b),h=J(c,0,null),f=J(c,1,null),a.c?a.c(f,h):a.call(null,f,h),b=z(b),c=null,d=0),e=0;else return null};g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){a=ai(this.$b,b);return null!=a?a.key:c};g.P=function(){return this.meta};g.W=function(){return H(this.$b)};\ng.Rc=function(){return 0<H(this.$b)?ig.c(fi,Ic(this.$b)):null};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Dd(this)};g.K=function(a,b){return ve(b)&&H(this)===H(b)&&Ue(function(){return function(a,d){var c=He(b,d);return c?c:new Gd(!1)}}(this),!0,this.$b)};g.oa=function(){return new wi(this.meta,Rb(this.$b),0)};g.ie=function(a,b){return new wi(this.meta,le.c(this.$b,b),null)};g.S=function(){return lh(this.$b)};g.T=function(a,b){return new wi(b,this.$b,this.w)};\ng.X=function(a,b){return new wi(this.meta,K.l(this.$b,b,null),null)};g.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return this.V(null,c);case 3:return this.I(null,c,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.c=function(a,c){return this.V(null,c)};a.l=function(a,c,d){return this.I(null,c,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return this.V(null,a)};\ng.c=function(a,b){return this.I(null,a,b)};var xi=new wi(null,bi,Ed);wi.prototype[Fb]=function(){return yd(this)};function yi(a){a=E(a);if(null==a)return vi;if(a instanceof Jb&&0===a.i)return Je(a.o);for(var b=Oc(vi);;)if(null!=a){var c=z(a);b=b.Dc(null,a.Ia(null));a=c}else return Qc(b)}var zi=function zi(a){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return zi.A(0<c.length?new Jb(c.slice(0),0,null):null)};zi.A=function(a){return Mb(Tb,xi,a)};zi.L=0;zi.N=function(a){return zi.A(E(a))};\nfunction jf(a){if(null!=a&&(a.J&4096||q===a.Oe))return a.hd(null);if(\"string\"===typeof a)return a;throw Error([\"Doesn't support name: \",v.h(a)].join(\"\"));}var Ai=function Ai(a){switch(arguments.length){case 2:return Ai.c(arguments[0],arguments[1]);case 3:return Ai.l(arguments[0],arguments[1],arguments[2]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return Ai.A(arguments[0],arguments[1],arguments[2],new Jb(c.slice(3),0,null))}};Ai.c=function(a,b){return b};\nAi.l=function(a,b,c){return(a.h?a.h(b):a.call(null,b))>(a.h?a.h(c):a.call(null,c))?b:c};Ai.A=function(a,b,c,d){return Mb(function(b,c){return Ai.l(a,b,c)},Ai.l(a,b,c),d)};Ai.N=function(a){var b=y(a),c=z(a);a=y(c);var d=z(c);c=y(d);d=z(d);return Ai.A(b,a,c,d)};Ai.L=3;function Bi(a,b){return new kf(null,function(){var c=E(b);if(c){var d=y(c);d=a.h?a.h(d):a.call(null,d);c=t(d)?ae(y(c),Bi(a,vd(c))):null}else c=null;return c},null,null)}function Di(a,b,c){this.i=a;this.end=b;this.step=c}\nDi.prototype.ja=function(){return 0<this.step?this.i<this.end:this.i>this.end};Di.prototype.next=function(){var a=this.i;this.i+=this.step;return a};function Ei(a,b,c,d,e){this.meta=a;this.start=b;this.end=c;this.step=d;this.w=e;this.m=32375006;this.J=139264}g=Ei.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};\ng.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error(\"Invalid arity: \"+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();\ng.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.$=function(a,b){if(0<=b&&b<this.W(null))return this.start+b*this.step;if(0<=b&&this.start>this.end&&0===this.step)return this.start;throw Error(\"Index out of bounds\");};\ng.ka=function(a,b,c){return 0<=b&&b<this.W(null)?this.start+b*this.step:0<=b&&this.start>this.end&&0===this.step?this.start:c};g.ba=function(){return new Di(this.start,this.end,this.step)};g.P=function(){return this.meta};g.Ka=function(){return 0<this.step?this.start+this.step<this.end?new Ei(this.meta,this.start+this.step,this.end,this.step,null):null:this.start+this.step>this.end?new Ei(this.meta,this.start+this.step,this.end,this.step,null):null};\ng.W=function(){return wb(this.S(null))?0:Math.ceil((this.end-this.start)/this.step)};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};g.K=function(a,b){return $d(this,b)};g.oa=function(){return tc(wd,this.meta)};g.Fa=function(a,b){return Kd(this,b)};g.Ga=function(a,b,c){for(a=this.start;;)if(0<this.step?a<this.end:a>this.end){c=b.c?b.c(c,a):b.call(null,c,a);if(Hd(c))return B(c);a+=this.step}else return c};g.Ia=function(){return null==this.S(null)?null:this.start};\ng.bb=function(){return null!=this.S(null)?new Ei(this.meta,this.start+this.step,this.end,this.step,null):wd};g.S=function(){return 0<this.step?this.start<this.end?this:null:0>this.step?this.start>this.end?this:null:this.start===this.end?null:this};g.T=function(a,b){return new Ei(b,this.start,this.end,this.step,this.w)};g.X=function(a,b){return ae(b,this)};Ei.prototype[Fb]=function(){return yd(this)};function Fi(a,b,c){return new Ei(null,a,b,c,null)}\nfunction Gi(a,b){return new R(null,2,5,T,[Bi(a,b),ng(a,b)],null)}\nfunction Hi(a){var b=y;return function(){function c(c,d,e){return new R(null,2,5,T,[b.l?b.l(c,d,e):b.call(null,c,d,e),a.l?a.l(c,d,e):a.call(null,c,d,e)],null)}function d(c,d){return new R(null,2,5,T,[b.c?b.c(c,d):b.call(null,c,d),a.c?a.c(c,d):a.call(null,c,d)],null)}function e(c){return new R(null,2,5,T,[b.h?b.h(c):b.call(null,c),a.h?a.h(c):a.call(null,c)],null)}function f(){return new R(null,2,5,T,[b.B?b.B():b.call(null),a.B?a.B():a.call(null)],null)}var h=null,k=function(){function c(a,b,c,e){var f=\nnull;if(3<arguments.length){f=0;for(var h=Array(arguments.length-3);f<h.length;)h[f]=arguments[f+3],++f;f=new Jb(h,0,null)}return d.call(this,a,b,c,f)}function d(c,d,e,f){return new R(null,2,5,T,[Af(b,c,d,e,f),Af(a,c,d,e,f)],null)}c.L=3;c.N=function(a){var b=y(a);a=z(a);var c=y(a);a=z(a);var e=y(a);a=vd(a);return d(b,c,e,a)};c.A=d;return c}();h=function(a,b,h,u){switch(arguments.length){case 0:return f.call(this);case 1:return e.call(this,a);case 2:return d.call(this,a,b);case 3:return c.call(this,\na,b,h);default:var m=null;if(3<arguments.length){m=0;for(var l=Array(arguments.length-3);m<l.length;)l[m]=arguments[m+3],++m;m=new Jb(l,0,null)}return k.A(a,b,h,m)}throw Error(\"Invalid arity: \"+(arguments.length-1));};h.L=3;h.N=k.N;h.B=f;h.h=e;h.c=d;h.l=c;h.A=k.A;return h}()}function Ii(a){a:for(var b=a;;)if(E(b))b=z(b);else break a;return a}\nfunction Ji(a,b){if(\"string\"===typeof b){var c=a.exec(b);return G.c(y(c),b)?1===H(c)?y(c):Wg(c):null}throw new TypeError(\"re-matches must match against a string.\");}\nfunction Y(a,b,c,d,e,f,h){var k=lb;lb=null==lb?null:lb-1;try{if(null!=lb&&0>lb)return Jc(a,\"#\");Jc(a,c);if(0===tb.h(f))E(h)&&Jc(a,function(){var a=Ki.h(f);return t(a)?a:\"...\"}());else{if(E(h)){var l=y(h);b.l?b.l(l,a,f):b.call(null,l,a,f)}for(var p=z(h),m=tb.h(f)-1;;)if(!p||null!=m&&0===m){E(p)&&0===m&&(Jc(a,d),Jc(a,function(){var a=Ki.h(f);return t(a)?a:\"...\"}()));break}else{Jc(a,d);var u=y(p);c=a;h=f;b.l?b.l(u,c,h):b.call(null,u,c,h);var w=z(p);c=m-1;p=w;m=c}}return Jc(a,e)}finally{lb=k}}\nfunction Li(a,b){for(var c=E(b),d=null,e=0,f=0;;)if(f<e){var h=d.$(null,f);Jc(a,h);f+=1}else if(c=E(c))d=c,Ae(d)?(c=Wc(d),e=Xc(d),d=c,h=H(c),c=e,e=h):(h=y(d),Jc(a,h),c=z(d),d=null,e=0),f=0;else return null}var Mi={'\"':'\\\\\"',\"\\\\\":\"\\\\\\\\\",\"\\b\":\"\\\\b\",\"\\f\":\"\\\\f\",\"\\n\":\"\\\\n\",\"\\r\":\"\\\\r\",\"\\t\":\"\\\\t\"};function Ni(a){return[v.h('\"'),v.h(a.replace(RegExp('[\\\\\\\\\"\\b\\f\\n\\r\\t]',\"g\"),function(a){return Mi[a]})),v.h('\"')].join(\"\")}\nfunction Oi(a,b){var c=Ee(D.c(a,rb));return c?(c=null!=b?b.m&131072||q===b.tf?!0:!1:!1)?null!=qe(b):c:c}\nfunction Pi(a,b,c){if(null==a)return Jc(b,\"nil\");Oi(c,a)&&(Jc(b,\"^\"),Qi(qe(a),b,c),Jc(b,\" \"));if(a.qc)return a.Ec(a,b,c);if(null!=a&&(a.m&2147483648||q===a.ma))return a.R(null,b,c);if(!0===a||!1===a)return Jc(b,\"\"+v.h(a));if(\"number\"===typeof a)return Jc(b,isNaN(a)?\"##NaN\":a===Number.POSITIVE_INFINITY?\"##Inf\":a===Number.NEGATIVE_INFINITY?\"##-Inf\":\"\"+v.h(a));if(null!=a&&a.constructor===Object)return Jc(b,\"#js \"),Ri(ig.c(function(b){return new R(null,2,5,T,[null!=Ji(/[A-Za-z_\\*\\+\\?!\\-'][\\w\\*\\+\\?!\\-']*/,\nb)?hf.h(b):b,a[b]],null)},Ea(a)),b,c);if(vb(a))return Y(b,Qi,\"#js [\",\" \",\"]\",c,a);if(ca(a))return t(qb.h(c))?Jc(b,Ni(a)):Jc(b,a);if(ha(a)){var d=a.name;c=t(function(){var a=null==d;return a?a:/^[\\s\\xa0]*$/.test(d)}())?\"Function\":d;return Li(b,be([\"#object[\",c,\"\",\"]\"]))}if(a instanceof Date)return c=function(a,b){for(var c=\"\"+v.h(a);;)if(H(c)<b)c=[\"0\",v.h(c)].join(\"\");else return c},Li(b,be(['#inst \"',\"\"+v.h(a.getUTCFullYear()),\"-\",c(a.getUTCMonth()+1,2),\"-\",c(a.getUTCDate(),2),\"T\",c(a.getUTCHours(),\n2),\":\",c(a.getUTCMinutes(),2),\":\",c(a.getUTCSeconds(),2),\".\",c(a.getUTCMilliseconds(),3),\"-\",'00:00\"']));if(a instanceof RegExp)return Li(b,be(['#\"',a.source,'\"']));if(t(function(){var b=null==a?null:a.constructor;return null==b?null:b.Tb}()))return Li(b,be([\"#object[\",a.constructor.Tb.replace(RegExp(\"/\",\"g\"),\".\"),\"]\"]));d=function(){var b=null==a?null:a.constructor;return null==b?null:b.name}();c=t(function(){var a=null==d;return a?a:/^[\\s\\xa0]*$/.test(d)}())?\"Object\":d;return null==a.constructor?\nLi(b,be([\"#object[\",c,\"]\"])):Li(b,be([\"#object[\",c,\" \",\"\"+v.h(a),\"]\"]))}function Qi(a,b,c){var d=Si.h(c);return t(d)?(c=K.l(c,Ti,Pi),d.l?d.l(a,b,c):d.call(null,a,b,c)):Pi(a,b,c)}function Ui(a,b){var c=new cb;a:{var d=new ed(c);Qi(y(a),d,b);for(var e=E(z(a)),f=null,h=0,k=0;;)if(k<h){var l=f.$(null,k);Jc(d,\" \");Qi(l,d,b);k+=1}else if(e=E(e))f=e,Ae(f)?(e=Wc(f),h=Xc(f),f=e,l=H(e),e=h,h=l):(l=y(f),Jc(d,\" \"),Qi(l,d,b),e=z(f),f=null,h=0),k=0;else break a}return c}\nfunction Vi(a){var b=ob();return te(a)?\"\":\"\"+v.h(Ui(a,b))}function Wi(a,b,c,d,e){return Y(d,function(a,b,d){var e=jc(a);c.l?c.l(e,b,d):c.call(null,e,b,d);Jc(b,\" \");a=kc(a);return c.l?c.l(a,b,d):c.call(null,a,b,d)},[v.h(a),\"{\"].join(\"\"),\", \",\"}\",e,E(b))}function Ri(a,b,c){var d=Qi,e=(xe(a),null),f=J(e,0,null);e=J(e,1,null);return t(f)?Wi([\"#:\",v.h(f)].join(\"\"),e,d,b,c):Wi(null,a,d,b,c)}hg.prototype.ma=q;\nhg.prototype.R=function(a,b,c){Jc(b,\"#object [cljs.core.Volatile \");Qi(new r(null,1,[Xi,this.state],null),b,c);return Jc(b,\"]\")};Jb.prototype.ma=q;Jb.prototype.R=function(a,b,c){return Y(b,Qi,\"(\",\" \",\")\",c,this)};kf.prototype.ma=q;kf.prototype.R=function(a,b,c){return Y(b,Qi,\"(\",\" \",\")\",c,this)};Nh.prototype.ma=q;Nh.prototype.R=function(a,b,c){return Y(b,Qi,\"(\",\" \",\")\",c,this)};Fh.prototype.ma=q;Fh.prototype.R=function(a,b,c){return Y(b,Qi,\"(\",\" \",\")\",c,this)};Rh.prototype.ma=q;\nRh.prototype.R=function(a,b,c){return Y(b,Qi,\"[\",\" \",\"]\",c,this)};jh.prototype.ma=q;jh.prototype.R=function(a,b,c){return Y(b,Qi,\"(\",\" \",\")\",c,this)};wi.prototype.ma=q;wi.prototype.R=function(a,b,c){return Y(b,Qi,\"#{\",\" \",\"}\",c,this)};Ug.prototype.ma=q;Ug.prototype.R=function(a,b,c){return Y(b,Qi,\"(\",\" \",\")\",c,this)};ef.prototype.ma=q;ef.prototype.R=function(a,b,c){return Y(b,Qi,\"(\",\" \",\")\",c,this)};Zd.prototype.ma=q;Zd.prototype.R=function(a,b,c){return Y(b,Qi,\"(\",\" \",\")\",c,this)};\nJh.prototype.ma=q;Jh.prototype.R=function(a,b,c){return Ri(this,b,c)};Gh.prototype.ma=q;Gh.prototype.R=function(a,b,c){return Y(b,Qi,\"(\",\" \",\")\",c,this)};Yg.prototype.ma=q;Yg.prototype.R=function(a,b,c){return Y(b,Qi,\"[\",\" \",\"]\",c,this)};$h.prototype.ma=q;$h.prototype.R=function(a,b,c){return Ri(this,b,c)};ti.prototype.ma=q;ti.prototype.R=function(a,b,c){return Y(b,Qi,\"#{\",\" \",\"}\",c,this)};pf.prototype.ma=q;pf.prototype.R=function(a,b,c){return Y(b,Qi,\"(\",\" \",\")\",c,this)};cg.prototype.ma=q;\ncg.prototype.R=function(a,b,c){Jc(b,\"#object [cljs.core.Atom \");Qi(new r(null,1,[Xi,this.state],null),b,c);return Jc(b,\"]\")};gi.prototype.ma=q;gi.prototype.R=function(a,b,c){return Y(b,Qi,\"(\",\" \",\")\",c,this)};Qh.prototype.ma=q;Qh.prototype.R=function(a,b,c){return Y(b,Qi,\"[\",\" \",\"]\",c,this)};R.prototype.ma=q;R.prototype.R=function(a,b,c){return Y(b,Qi,\"[\",\" \",\"]\",c,this)};bf.prototype.ma=q;bf.prototype.R=function(a,b){return Jc(b,\"()\")};r.prototype.ma=q;\nr.prototype.R=function(a,b,c){return Ri(this,b,c)};Ei.prototype.ma=q;Ei.prototype.R=function(a,b,c){return Y(b,Qi,\"(\",\" \",\")\",c,this)};Sf.prototype.ma=q;Sf.prototype.R=function(a,b,c){return Y(b,Qi,\"(\",\" \",\")\",c,this)};ei.prototype.ma=q;ei.prototype.R=function(a,b,c){return Y(b,Qi,\"(\",\" \",\")\",c,this)};af.prototype.ma=q;af.prototype.R=function(a,b,c){return Y(b,Qi,\"(\",\" \",\")\",c,this)};rd.prototype.zc=q;\nrd.prototype.cc=function(a,b){if(b instanceof rd)return sd(this,b);throw Error([\"Cannot compare \",v.h(this),\" to \",v.h(b)].join(\"\"));};L.prototype.zc=q;L.prototype.cc=function(a,b){if(b instanceof L)return ff(this,b);throw Error([\"Cannot compare \",v.h(this),\" to \",v.h(b)].join(\"\"));};Yg.prototype.zc=q;Yg.prototype.cc=function(a,b){if(ze(b))return Le(this,b);throw Error([\"Cannot compare \",v.h(this),\" to \",v.h(b)].join(\"\"));};R.prototype.zc=q;\nR.prototype.cc=function(a,b){if(ze(b))return Le(this,b);throw Error([\"Cannot compare \",v.h(this),\" to \",v.h(b)].join(\"\"));};Rh.prototype.zc=q;Rh.prototype.cc=function(a,b){if(ze(b))return Le(this,b);throw Error([\"Cannot compare \",v.h(this),\" to \",v.h(b)].join(\"\"));};Qh.prototype.zc=q;Qh.prototype.cc=function(a,b){if(ze(b))return Le(this,b);throw Error([\"Cannot compare \",v.h(this),\" to \",v.h(b)].join(\"\"));};var Yi=null;\nfunction Zi(){null==Yi&&(Yi=dg.h(0));return td.h([v.h(\"reagent\"),v.h(gg.c(Yi,Fd))].join(\"\"))}function $i(){}var aj=function aj(a){if(null!=a&&null!=a.pf)return a.pf(a);var c=aj[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=aj._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"IEncodeJS.-clj-\\x3ejs\",a);};function bj(a){return(null!=a?q===a.nf||(a.Tc?0:Ab($i,a)):Ab($i,a))?aj(a):\"string\"===typeof a||\"number\"===typeof a||a instanceof L||a instanceof rd?cj(a):Vi(be([a]))}\nvar cj=function cj(a){if(null==a)return null;if(null!=a?q===a.nf||(a.Tc?0:Ab($i,a)):Ab($i,a))return aj(a);if(a instanceof L)return jf(a);if(a instanceof rd)return\"\"+v.h(a);if(xe(a)){var c={};a=E(a);for(var d=null,e=0,f=0;;)if(f<e){var h=d.$(null,f),k=J(h,0,null),l=J(h,1,null);h=c;k=bj(k);l=cj.h?cj.h(l):cj.call(null,l);h[k]=l;f+=1}else if(a=E(a))Ae(a)?(e=Wc(a),a=Xc(a),d=e,e=H(e)):(d=y(a),e=J(d,0,null),f=J(d,1,null),d=c,e=bj(e),f=cj.h?cj.h(f):cj.call(null,f),d[e]=f,a=z(a),d=null,e=0),f=0;else break;\nreturn c}if(ue(a)){c=[];a=E(ig.c(cj,a));d=null;for(f=e=0;;)if(f<e)h=d.$(null,f),c.push(h),f+=1;else if(a=E(a))d=a,Ae(d)?(a=Wc(d),f=Xc(d),d=a,e=H(a),a=f):(a=y(d),c.push(a),a=z(d),d=null,e=0),f=0;else break;return c}return a};function dj(){}var ej=function ej(a,b){if(null!=a&&null!=a.mf)return a.mf(a,b);var d=ej[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=ej._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb(\"IEncodeClojure.-js-\\x3eclj\",a);};\nfunction fj(a){var b=be([gj,!0]),c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,gj);return function(a,c,d,k){return function m(e){return(null!=e?q===e.lf||(e.Tc?0:Ab(dj,e)):Ab(dj,e))?ej(e,P(ci,b)):De(e)?Ii(ig.c(m,e)):ue(e)?wg.c(ie(e),ig.c(m,e)):vb(e)?Wg(ig.c(m,e)):Bb(e)===Object?wg.c(Ef,function(){return function(a,b,c,d){return function M(f){return new kf(null,function(a,b,c,d){return function(){for(;;){var a=E(f);if(a){if(Ae(a)){var b=Wc(a),c=H(b),h=of(c);a:for(var k=0;;)if(k<c){var p=A.c(b,k);p=\nnew R(null,2,5,T,[d.h?d.h(p):d.call(null,p),m(e[p])],null);h.add(p);k+=1}else{b=!0;break a}return b?qf(h.Da(),M(Xc(a))):qf(h.Da(),null)}h=y(a);return ae(new R(null,2,5,T,[d.h?d.h(h):d.call(null,h),m(e[h])],null),M(vd(a)))}return null}}}(a,b,c,d),null,null)}}(a,c,d,k)(Ea(e))}()):e}}(b,c,d,t(d)?hf:v)(a)}\nfunction hj(a){return function(b){return function(){function c(a){var b=null;if(0<arguments.length){b=0;for(var c=Array(arguments.length-0);b<c.length;)c[b]=arguments[b+0],++b;b=new Jb(c,0,null)}return d.call(this,b)}function d(c){var d=D.l(B(b),c,Ce);d===Ce&&(d=P(a,c),gg.M(b,K,c,d));return d}c.L=0;c.N=function(a){a=E(a);return d(a)};c.A=d;return c}()}(dg.h(Ef))}var ij=null;function jj(){null==ij&&(ij=dg.h(new r(null,3,[kj,Ef,lj,Ef,mj,Ef],null)));return ij}\nfunction nj(a,b,c){var d=G.c(b,c);if(d)return d;d=mj.h(a);d=d.h?d.h(b):d.call(null,b);if(!(d=He(d,c))&&(d=ze(c)))if(d=ze(b))if(d=H(c)===H(b)){d=!0;for(var e=0;;)if(d&&e!==H(c))d=nj(a,b.h?b.h(e):b.call(null,e),c.h?c.h(e):c.call(null,e)),e+=1;else return d}else return d;else return d;else return d}function oj(a){var b=B(jj());return Bf(D.c(kj.h(b),a))}function pj(a,b,c,d){gg.c(a,function(){return B(b)});gg.c(c,function(){return B(d)})}\nvar qj=function qj(a,b,c){var e=function(){var b=B(c);return b.h?b.h(a):b.call(null,a)}();e=t(t(e)?e.h?e.h(b):e.call(null,b):e)?!0:null;if(t(e))return e;e=function(){for(var e=oj(b);;)if(0<H(e)){var h=y(e);qj.l?qj.l(a,h,c):qj.call(null,a,h,c);e=vd(e)}else return null}();if(t(e))return e;e=function(){for(var e=oj(a);;)if(0<H(e)){var h=y(e);qj.l?qj.l(h,b,c):qj.call(null,h,b,c);e=vd(e)}else return null}();return t(e)?e:!1};function rj(a,b,c,d){c=qj(a,b,c);return t(c)?c:nj(d,a,b)}\nvar sj=function sj(a,b,c,d,e,f,h,k){var p=Mb(function(d,f){var h=J(f,0,null);J(f,1,null);if(nj(B(c),b,h)){var k=(k=null==d)?k:rj(h,y(d),e,B(c));k=t(k)?f:d;if(!t(rj(y(k),h,e,B(c))))throw Error([\"Multiple methods in multimethod '\",v.h(a),\"' match dispatch value: \",v.h(b),\" -\\x3e \",v.h(h),\" and \",v.h(y(k)),\", and neither is preferred\"].join(\"\"));return k}return d},null,B(d)),m=function(){var a;if(a=null==p)a=B(d),a=a.h?a.h(k):a.call(null,k);return t(a)?new R(null,2,5,T,[k,a],null):p}();if(t(m)){if(G.c(B(h),\nB(c)))return gg.M(f,K,b,ee(m)),ee(m);pj(f,d,h,c);return sj.Ha?sj.Ha(a,b,c,d,e,f,h,k):sj.call(null,a,b,c,d,e,f,h,k)}return null};function tj(a,b){throw Error([\"No method in multimethod '\",v.h(a),\"' for dispatch value: \",v.h(b)].join(\"\"));}function uj(a,b,c,d,e,f,h,k){this.name=a;this.D=b;this.vf=c;this.Rd=d;this.Vd=e;this.Kf=f;this.Ud=h;this.Ed=k;this.m=4194305;this.J=4352}g=uj.prototype;\ng.call=function(){function a(a,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S,X,Q,Ga){a=this;var W=oe(a.D,b,c,d,e,be([f,h,k,m,l,p,u,w,x,F,C,I,M,S,X,Q,Ga])),ka=vj(this,W);t(ka)||tj(a.name,W);return oe(ka,b,c,d,e,be([f,h,k,m,l,p,u,w,x,F,C,I,M,S,X,Q,Ga]))}function b(a,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S,X,Q){a=this;var W=a.D.Xa?a.D.Xa(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S,X,Q):a.D.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S,X,Q),ka=vj(this,W);t(ka)||tj(a.name,W);return ka.Xa?ka.Xa(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,\nI,M,S,X,Q):ka.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S,X,Q)}function c(a,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S,X){a=this;var W=a.D.Wa?a.D.Wa(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S,X):a.D.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S,X),ka=vj(this,W);t(ka)||tj(a.name,W);return ka.Wa?ka.Wa(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S,X):ka.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S,X)}function d(a,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S){a=this;var W=a.D.Va?a.D.Va(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S):a.D.call(null,\nb,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S),ka=vj(this,W);t(ka)||tj(a.name,W);return ka.Va?ka.Va(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S):ka.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S)}function e(a,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M){a=this;var W=a.D.Ua?a.D.Ua(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M):a.D.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M),ka=vj(this,W);t(ka)||tj(a.name,W);return ka.Ua?ka.Ua(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M):ka.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M)}function f(a,b,c,d,e,f,h,k,m,\nl,p,u,w,x,F,C,I){a=this;var W=a.D.Ta?a.D.Ta(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I):a.D.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I),ka=vj(this,W);t(ka)||tj(a.name,W);return ka.Ta?ka.Ta(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I):ka.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I)}function h(a,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C){a=this;var W=a.D.Sa?a.D.Sa(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C):a.D.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C),ka=vj(this,W);t(ka)||tj(a.name,W);return ka.Sa?ka.Sa(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C):ka.call(null,b,\nc,d,e,f,h,k,m,l,p,u,w,x,F,C)}function k(a,b,c,d,e,f,h,k,m,l,p,u,w,x,F){a=this;var W=a.D.Ra?a.D.Ra(b,c,d,e,f,h,k,m,l,p,u,w,x,F):a.D.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F),C=vj(this,W);t(C)||tj(a.name,W);return C.Ra?C.Ra(b,c,d,e,f,h,k,m,l,p,u,w,x,F):C.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F)}function l(a,b,c,d,e,f,h,k,m,l,p,u,w,x){a=this;var W=a.D.Qa?a.D.Qa(b,c,d,e,f,h,k,m,l,p,u,w,x):a.D.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x),F=vj(this,W);t(F)||tj(a.name,W);return F.Qa?F.Qa(b,c,d,e,f,h,k,m,l,p,u,w,x):F.call(null,\nb,c,d,e,f,h,k,m,l,p,u,w,x)}function p(a,b,c,d,e,f,h,k,m,l,p,u,w){a=this;var x=a.D.Pa?a.D.Pa(b,c,d,e,f,h,k,m,l,p,u,w):a.D.call(null,b,c,d,e,f,h,k,m,l,p,u,w),W=vj(this,x);t(W)||tj(a.name,x);return W.Pa?W.Pa(b,c,d,e,f,h,k,m,l,p,u,w):W.call(null,b,c,d,e,f,h,k,m,l,p,u,w)}function m(a,b,c,d,e,f,h,k,m,l,p,u){a=this;var w=a.D.Oa?a.D.Oa(b,c,d,e,f,h,k,m,l,p,u):a.D.call(null,b,c,d,e,f,h,k,m,l,p,u),x=vj(this,w);t(x)||tj(a.name,w);return x.Oa?x.Oa(b,c,d,e,f,h,k,m,l,p,u):x.call(null,b,c,d,e,f,h,k,m,l,p,u)}function u(a,\nb,c,d,e,f,h,k,m,l,p){a=this;var u=a.D.Na?a.D.Na(b,c,d,e,f,h,k,m,l,p):a.D.call(null,b,c,d,e,f,h,k,m,l,p),w=vj(this,u);t(w)||tj(a.name,u);return w.Na?w.Na(b,c,d,e,f,h,k,m,l,p):w.call(null,b,c,d,e,f,h,k,m,l,p)}function w(a,b,c,d,e,f,h,k,m,l){a=this;var p=a.D.Za?a.D.Za(b,c,d,e,f,h,k,m,l):a.D.call(null,b,c,d,e,f,h,k,m,l),u=vj(this,p);t(u)||tj(a.name,p);return u.Za?u.Za(b,c,d,e,f,h,k,m,l):u.call(null,b,c,d,e,f,h,k,m,l)}function x(a,b,c,d,e,f,h,k,m){a=this;var l=a.D.Ha?a.D.Ha(b,c,d,e,f,h,k,m):a.D.call(null,\nb,c,d,e,f,h,k,m),p=vj(this,l);t(p)||tj(a.name,l);return p.Ha?p.Ha(b,c,d,e,f,h,k,m):p.call(null,b,c,d,e,f,h,k,m)}function C(a,b,c,d,e,f,h,k){a=this;var m=a.D.Ya?a.D.Ya(b,c,d,e,f,h,k):a.D.call(null,b,c,d,e,f,h,k),l=vj(this,m);t(l)||tj(a.name,m);return l.Ya?l.Ya(b,c,d,e,f,h,k):l.call(null,b,c,d,e,f,h,k)}function F(a,b,c,d,e,f,h){a=this;var k=a.D.Ca?a.D.Ca(b,c,d,e,f,h):a.D.call(null,b,c,d,e,f,h),m=vj(this,k);t(m)||tj(a.name,k);return m.Ca?m.Ca(b,c,d,e,f,h):m.call(null,b,c,d,e,f,h)}function I(a,b,c,d,\ne,f){a=this;var h=a.D.Z?a.D.Z(b,c,d,e,f):a.D.call(null,b,c,d,e,f),k=vj(this,h);t(k)||tj(a.name,h);return k.Z?k.Z(b,c,d,e,f):k.call(null,b,c,d,e,f)}function M(a,b,c,d,e){a=this;var f=a.D.M?a.D.M(b,c,d,e):a.D.call(null,b,c,d,e),h=vj(this,f);t(h)||tj(a.name,f);return h.M?h.M(b,c,d,e):h.call(null,b,c,d,e)}function S(a,b,c,d){a=this;var e=a.D.l?a.D.l(b,c,d):a.D.call(null,b,c,d),f=vj(this,e);t(f)||tj(a.name,e);return f.l?f.l(b,c,d):f.call(null,b,c,d)}function X(a,b,c){a=this;var d=a.D.c?a.D.c(b,c):a.D.call(null,\nb,c),e=vj(this,d);t(e)||tj(a.name,d);return e.c?e.c(b,c):e.call(null,b,c)}function Ga(a,b){a=this;var c=a.D.h?a.D.h(b):a.D.call(null,b),d=vj(this,c);t(d)||tj(a.name,c);return d.h?d.h(b):d.call(null,b)}function db(a){a=this;var b=a.D.B?a.D.B():a.D.call(null),c=vj(this,b);t(c)||tj(a.name,b);return c.B?c.B():c.call(null)}var Q=null;Q=function(Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Wd,Xb,ic,xc,Sc,Bd,se,Lf,Ih,kl){switch(arguments.length){case 1:return db.call(this,Q);case 2:return Ga.call(this,Q,Ha);case 3:return X.call(this,\nQ,Ha,Ja);case 4:return S.call(this,Q,Ha,Ja,Oa);case 5:return M.call(this,Q,Ha,Ja,Oa,Ba);case 6:return I.call(this,Q,Ha,Ja,Oa,Ba,W);case 7:return F.call(this,Q,Ha,Ja,Oa,Ba,W,$a);case 8:return C.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka);case 9:return x.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb);case 10:return w.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb);case 11:return u.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb);case 12:return m.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib);case 13:return p.call(this,Q,Ha,Ja,Oa,Ba,W,$a,\nka,jb,nb,zb,Ib,Wd);case 14:return l.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Wd,Xb);case 15:return k.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Wd,Xb,ic);case 16:return h.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Wd,Xb,ic,xc);case 17:return f.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Wd,Xb,ic,xc,Sc);case 18:return e.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Wd,Xb,ic,xc,Sc,Bd);case 19:return d.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Wd,Xb,ic,xc,Sc,Bd,se);case 20:return c.call(this,Q,\nHa,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Wd,Xb,ic,xc,Sc,Bd,se,Lf);case 21:return b.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Wd,Xb,ic,xc,Sc,Bd,se,Lf,Ih);case 22:return a.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Wd,Xb,ic,xc,Sc,Bd,se,Lf,Ih,kl)}throw Error(\"Invalid arity: \"+(arguments.length-1));};Q.h=db;Q.c=Ga;Q.l=X;Q.M=S;Q.Z=M;Q.Ca=I;Q.Ya=F;Q.Ha=C;Q.Za=x;Q.Na=w;Q.Oa=u;Q.Pa=m;Q.Qa=p;Q.Ra=l;Q.Sa=k;Q.Ta=h;Q.Ua=f;Q.Va=e;Q.Wa=d;Q.Xa=c;Q.he=b;Q.qf=a;return Q}();\ng.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.B=function(){var a=this.D.B?this.D.B():this.D.call(null),b=vj(this,a);t(b)||tj(this.name,a);return b.B?b.B():b.call(null)};g.h=function(a){var b=this.D.h?this.D.h(a):this.D.call(null,a),c=vj(this,b);t(c)||tj(this.name,b);return c.h?c.h(a):c.call(null,a)};g.c=function(a,b){var c=this.D.c?this.D.c(a,b):this.D.call(null,a,b),d=vj(this,c);t(d)||tj(this.name,c);return d.c?d.c(a,b):d.call(null,a,b)};\ng.l=function(a,b,c){var d=this.D.l?this.D.l(a,b,c):this.D.call(null,a,b,c),e=vj(this,d);t(e)||tj(this.name,d);return e.l?e.l(a,b,c):e.call(null,a,b,c)};g.M=function(a,b,c,d){var e=this.D.M?this.D.M(a,b,c,d):this.D.call(null,a,b,c,d),f=vj(this,e);t(f)||tj(this.name,e);return f.M?f.M(a,b,c,d):f.call(null,a,b,c,d)};g.Z=function(a,b,c,d,e){var f=this.D.Z?this.D.Z(a,b,c,d,e):this.D.call(null,a,b,c,d,e),h=vj(this,f);t(h)||tj(this.name,f);return h.Z?h.Z(a,b,c,d,e):h.call(null,a,b,c,d,e)};\ng.Ca=function(a,b,c,d,e,f){var h=this.D.Ca?this.D.Ca(a,b,c,d,e,f):this.D.call(null,a,b,c,d,e,f),k=vj(this,h);t(k)||tj(this.name,h);return k.Ca?k.Ca(a,b,c,d,e,f):k.call(null,a,b,c,d,e,f)};g.Ya=function(a,b,c,d,e,f,h){var k=this.D.Ya?this.D.Ya(a,b,c,d,e,f,h):this.D.call(null,a,b,c,d,e,f,h),l=vj(this,k);t(l)||tj(this.name,k);return l.Ya?l.Ya(a,b,c,d,e,f,h):l.call(null,a,b,c,d,e,f,h)};\ng.Ha=function(a,b,c,d,e,f,h,k){var l=this.D.Ha?this.D.Ha(a,b,c,d,e,f,h,k):this.D.call(null,a,b,c,d,e,f,h,k),p=vj(this,l);t(p)||tj(this.name,l);return p.Ha?p.Ha(a,b,c,d,e,f,h,k):p.call(null,a,b,c,d,e,f,h,k)};g.Za=function(a,b,c,d,e,f,h,k,l){var p=this.D.Za?this.D.Za(a,b,c,d,e,f,h,k,l):this.D.call(null,a,b,c,d,e,f,h,k,l),m=vj(this,p);t(m)||tj(this.name,p);return m.Za?m.Za(a,b,c,d,e,f,h,k,l):m.call(null,a,b,c,d,e,f,h,k,l)};\ng.Na=function(a,b,c,d,e,f,h,k,l,p){var m=this.D.Na?this.D.Na(a,b,c,d,e,f,h,k,l,p):this.D.call(null,a,b,c,d,e,f,h,k,l,p),u=vj(this,m);t(u)||tj(this.name,m);return u.Na?u.Na(a,b,c,d,e,f,h,k,l,p):u.call(null,a,b,c,d,e,f,h,k,l,p)};g.Oa=function(a,b,c,d,e,f,h,k,l,p,m){var u=this.D.Oa?this.D.Oa(a,b,c,d,e,f,h,k,l,p,m):this.D.call(null,a,b,c,d,e,f,h,k,l,p,m),w=vj(this,u);t(w)||tj(this.name,u);return w.Oa?w.Oa(a,b,c,d,e,f,h,k,l,p,m):w.call(null,a,b,c,d,e,f,h,k,l,p,m)};\ng.Pa=function(a,b,c,d,e,f,h,k,l,p,m,u){var w=this.D.Pa?this.D.Pa(a,b,c,d,e,f,h,k,l,p,m,u):this.D.call(null,a,b,c,d,e,f,h,k,l,p,m,u),x=vj(this,w);t(x)||tj(this.name,w);return x.Pa?x.Pa(a,b,c,d,e,f,h,k,l,p,m,u):x.call(null,a,b,c,d,e,f,h,k,l,p,m,u)};g.Qa=function(a,b,c,d,e,f,h,k,l,p,m,u,w){var x=this.D.Qa?this.D.Qa(a,b,c,d,e,f,h,k,l,p,m,u,w):this.D.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w),C=vj(this,x);t(C)||tj(this.name,x);return C.Qa?C.Qa(a,b,c,d,e,f,h,k,l,p,m,u,w):C.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w)};\ng.Ra=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x){var C=this.D.Ra?this.D.Ra(a,b,c,d,e,f,h,k,l,p,m,u,w,x):this.D.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x),F=vj(this,C);t(F)||tj(this.name,C);return F.Ra?F.Ra(a,b,c,d,e,f,h,k,l,p,m,u,w,x):F.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x)};\ng.Sa=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C){var F=this.D.Sa?this.D.Sa(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C):this.D.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C),I=vj(this,F);t(I)||tj(this.name,F);return I.Sa?I.Sa(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C):I.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C)};\ng.Ta=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F){var I=this.D.Ta?this.D.Ta(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F):this.D.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F),M=vj(this,I);t(M)||tj(this.name,I);return M.Ta?M.Ta(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F):M.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F)};\ng.Ua=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I){var M=this.D.Ua?this.D.Ua(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I):this.D.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I),S=vj(this,M);t(S)||tj(this.name,M);return S.Ua?S.Ua(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I):S.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I)};\ng.Va=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M){var S=this.D.Va?this.D.Va(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M):this.D.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M),X=vj(this,S);t(X)||tj(this.name,S);return X.Va?X.Va(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M):X.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M)};\ng.Wa=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S){var X=this.D.Wa?this.D.Wa(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S):this.D.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S),Ga=vj(this,X);t(Ga)||tj(this.name,X);return Ga.Wa?Ga.Wa(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S):Ga.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S)};\ng.Xa=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X){var Ga=this.D.Xa?this.D.Xa(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X):this.D.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X),db=vj(this,Ga);t(db)||tj(this.name,Ga);return db.Xa?db.Xa(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X):db.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X)};\ng.he=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga){var db=oe(this.D,a,b,c,d,be([e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga])),Q=vj(this,db);t(Q)||tj(this.name,db);return oe(Q,a,b,c,d,be([e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga]))};function wj(a,b){var c=xj;gg.M(c.Vd,K,a,b);pj(c.Ud,c.Vd,c.Ed,c.Rd)}function vj(a,b){G.c(B(a.Ed),B(a.Rd))||pj(a.Ud,a.Vd,a.Ed,a.Rd);var c=B(a.Ud);c=c.h?c.h(b):c.call(null,b);return t(c)?c:sj(a.name,b,a.Rd,a.Vd,a.Kf,a.Ud,a.Ed,a.vf)}g.hd=function(){return Yc(this.name)};g.jd=function(){return Zc(this.name)};\ng.U=function(){return ja(this)};function yj(a,b){this.Mc=a;this.w=b;this.m=2153775104;this.J=2048}g=yj.prototype;g.toString=function(){return this.Mc};g.equiv=function(a){return this.K(null,a)};g.K=function(a,b){return b instanceof yj&&this.Mc===b.Mc};g.R=function(a,b){return Jc(b,['#uuid \"',v.h(this.Mc),'\"'].join(\"\"))};g.U=function(){null==this.w&&(this.w=od(this.Mc));return this.w};g.cc=function(a,b){return Aa(this.Mc,b.Mc)};var zj=new L(null,\"hook\",\"hook\",750265408),Aj=new L(null,\"y\",\"y\",-1757859776),Bj=new L(null,\"setCurrentTime\",\"setCurrentTime\",-623552),Cj=new L(null,\"span.gutter\",\"span.gutter\",-700214016),Dj=new rd(null,\"\\x26\",\"\\x26\",-2144855648,null),Ej=new L(null,\"dcs-param\",\"dcs-param\",-971011648),Fj=new L(null,\"path\",\"path\",-188191168),Gj=new L(null,\"escape\",\"escape\",-991601952),Df=new rd(null,\"meta34617\",\"meta34617\",-1789836320,null),Hj=new L(null,\"force-load-ch\",\"force-load-ch\",-1689229247),Ij=new rd(\"schema.core\",\n\"Any\",\"schema.core/Any\",-1891898271,null),Jj=new L(null,\"tab-index\",\"tab-index\",895755393),Kj=new L(null,\"bold\",\"bold\",-116809535),Lj=new L(null,\"authorImgURL\",\"authorImgURL\",-1171541759),Mj=new L(null,\"schema\",\"schema\",-1582001791),Nj=new rd(null,\"optional-key\",\"optional-key\",988406145,null),Oj=new L(null,\"char-attrs\",\"char-attrs\",-1444091455),Pj=new L(null,\"esc-dispatch\",\"esc-dispatch\",17832481),Qj=new L(null,\"idle_time_limit\",\"idle_time_limit\",-1837919647),Rj=new L(null,\"auto-wrap-mode\",\"auto-wrap-mode\",\n-2049555583),Sj=new L(null,\"preload?\",\"preload?\",445442977),Tj=new L(null,\"on-set\",\"on-set\",-140953470),Uj=new L(null,\"current-time\",\"current-time\",-1609407134),Vj=new L(null,\"span.progressbar\",\"span.progressbar\",766750210),Wj=new L(null,\"osc-end\",\"osc-end\",1762953954),Xj=new L(\"internal\",\"rewind\",\"internal/rewind\",-31749342),Yj=new L(null,\"bottom-margin\",\"bottom-margin\",-701300733),Zj=new L(null,\"on-key-press\",\"on-key-press\",-399563677),ak=new L(null,\"osc-put\",\"osc-put\",-1827844733),bk=new L(null,\n\"cljsLegacyRender\",\"cljsLegacyRender\",-1527295613),ck=new L(null,\"klass\",\"klass\",-1386752349),dk=new L(null,\"blink\",\"blink\",-271985917),ek=new rd(null,\"meta43127\",\"meta43127\",166183907,null),fk=new L(null,\"primary\",\"primary\",817773892),gk=new rd(null,\"meta43105\",\"meta43105\",-531987068,null),rb=new L(null,\"meta\",\"meta\",1499536964),V=new L(null,\"screen\",\"screen\",1990059748),hk=new rd(null,\"Symbol\",\"Symbol\",716452869,null),ik=new L(null,\"color\",\"color\",1011675173),jk=new rd(null,\"blockable\",\"blockable\",\n-28395259,null),sb=new L(null,\"dup\",\"dup\",556298533),kk=new L(null,\"parser-params\",\"parser-params\",36457893),lk=new rd(null,\"height\",\"height\",-1629257147,null),mk=new L(null,\"key\",\"key\",-1516042587),nk=new rd(null,\"CellLine\",\"CellLine\",-317574363,null),ok=new L(null,\"asciicast\",\"asciicast\",509526949),pk=new rd(null,\"conditional\",\"conditional\",-1212542970,null),qk=new L(null,\"exit\",\"exit\",351849638),rk=new L(null,\"parser-intermediates\",\"parser-intermediates\",-169100058),sk=new L(null,\"else\",\"else\",\n-1508377146),tk=new L(null,\"tabs\",\"tabs\",-779855354),uk=new L(null,\"ground\",\"ground\",1193572934),vk=new L(null,\"next-print-wraps\",\"next-print-wraps\",-1664999738),wk=new L(null,\"font-size\",\"font-size\",-1847940346),xk=new rd(null,\"Bool\",\"Bool\",195910502,null),yk=new L(null,\"transition\",\"transition\",765692007),zk=new rd(null,\"one\",\"one\",-1719427865,null),Ak=new L(null,\"speed\",\"speed\",1257663751),Bk=new L(null,\"displayName\",\"displayName\",-809144601),Ck=new L(null,\"_\",\"_\",1453416199),eg=new L(null,\"validator\",\n\"validator\",-1966190681),Dk=new rd(null,\"char-attrs\",\"char-attrs\",196440072,null),Ek=new L(null,\"div.loading\",\"div.loading\",-155515768),Fk=new L(null,\"dcs-passthrough\",\"dcs-passthrough\",-671044440),Gk=new L(null,\"show-hud\",\"show-hud\",1983299752),Hk=new L(null,\"start-at\",\"start-at\",-103334680),Ik=new L(null,\"default\",\"default\",-1987822328),Jk=new L(null,\"csi-param\",\"csi-param\",-1120111192),Kk=new L(null,\"div.control-bar\",\"div.control-bar\",-1316808248),Lk=new L(null,\"finally-block\",\"finally-block\",\n832982472),Mk=new rd(null,\"cb\",\"cb\",-2064487928,null),Nk=new L(null,\"inverse\",\"inverse\",-1623859672),Ok=new L(null,\"fg\",\"fg\",-101797208),Pk=new L(null,\"warn\",\"warn\",-436710552),Qk=new L(null,\"dcs-intermediate\",\"dcs-intermediate\",480808872),Rk=new L(null,\"osc-string\",\"osc-string\",-486531128),Sk=new L(null,\"on-enter\",\"on-enter\",-928988216),Tk=new L(null,\"name\",\"name\",1843675177),Uk=new L(null,\"frames\",\"frames\",1765687497),Vk=new L(null,\"extra-validator-fn\",\"extra-validator-fn\",1562905865),Wk=new L(null,\n\"output-schema\",\"output-schema\",272504137),Xk=new L(null,\"div.play-button\",\"div.play-button\",1020321513),Yk=new L(null,\"span.time-elapsed\",\"span.time-elapsed\",-1782475638),Zk=new L(null,\"time\",\"time\",1385887882),$k=new L(null,\"component-did-mount\",\"component-did-mount\",-1126910518),al=new L(null,\"background-color\",\"background-color\",570434026),bl=new L(null,\"recording-ch-fn\",\"recording-ch-fn\",-902533462),cl=new L(null,\"span.playback-button\",\"span.playback-button\",-1136389398),dl=new L(null,\"span.title-bar\",\n\"span.title-bar\",-1165872085),el=new L(null,\"loaded\",\"loaded\",-1246482293),fl=new L(null,\"width\",\"width\",-384071477),gl=new L(null,\"start\",\"start\",-355208981),hl=new rd(null,\"meta43130\",\"meta43130\",1056327947,null),il=new L(null,\"lines\",\"lines\",-700165781),jl=new L(null,\"input-schemas\",\"input-schemas\",-982154805),ll=new L(null,\"sos-pm-apc-string\",\"sos-pm-apc-string\",398998091),ml=new L(null,\"cursor-on\",\"cursor-on\",302555051),nl=new L(null,\"component-did-update\",\"component-did-update\",-1468549173),\nol=new L(null,\"div.start-prompt\",\"div.start-prompt\",-41424788),Xi=new L(null,\"val\",\"val\",128701612),pl=new L(null,\"cursor\",\"cursor\",1011937484),ql=new L(null,\"dcs-entry\",\"dcs-entry\",216833388),Z=new L(null,\"recur\",\"recur\",-437573268),rl=new L(null,\"type\",\"type\",1174270348),sl=new rd(null,\"Num\",\"Num\",-2044934708,null),tl=new L(null,\"alternate\",\"alternate\",-931038644),ul=new L(null,\"catch-block\",\"catch-block\",1175212748),vl=new L(null,\"onPlay\",\"onPlay\",150417132),wl=new L(null,\"duration\",\"duration\",\n1444101068),xl=new L(null,\"execute\",\"execute\",-129499188),yl=new rd(null,\"pred\",\"pred\",-727012372,null),zl=new L(null,\"src\",\"src\",-1651076051),Al=new rd(null,\"Any\",\"Any\",1277492269,null),Bl=new L(null,\"span.bar\",\"span.bar\",-1986926323),Cl=new rd(null,\"Regex\",\"Regex\",205914413,null),Dl=new L(null,\"msg-ch\",\"msg-ch\",-1840176755),El=new L(null,\"on-exit\",\"on-exit\",1821961613),Ti=new L(null,\"fallback-impl\",\"fallback-impl\",-1501286995),Fl=new L(null,\"view-box\",\"view-box\",-1792199155),Gl=new L(null,\"source\",\n\"source\",-433931539),Hl=new L(null,\"csi-entry\",\"csi-entry\",-1787942099),pb=new L(null,\"flush-on-newline\",\"flush-on-newline\",-151457939),Il=new L(null,\"preds-and-schemas\",\"preds-and-schemas\",-1306766355),Jl=new L(null,\"command-ch\",\"command-ch\",508874766),Kl=new L(null,\"componentWillUnmount\",\"componentWillUnmount\",1573788814),Ll=new rd(null,\"Inst\",\"Inst\",292408622,null),Ml=new L(null,\"span.timer\",\"span.timer\",2111534382),Nl=new L(null,\"toggle\",\"toggle\",1291842030),Ol=new L(null,\"cursor-blink-ch\",\"cursor-blink-ch\",\n1063651214),Pl=new L(null,\"print\",\"print\",1299562414),Ql=new L(null,\"on-mouse-down\",\"on-mouse-down\",1147755470),Rl=new L(null,\"csi-dispatch\",\"csi-dispatch\",-126857169),Sl=new L(null,\"on-click\",\"on-click\",1632826543),Tl=new L(null,\"parser-state\",\"parser-state\",594493647),Ul=new L(null,\"ignore\",\"ignore\",-1631542033),lj=new L(null,\"descendants\",\"descendants\",1824886031),Vl=new L(null,\"underline\",\"underline\",2018066703),Wl=new rd(null,\"Str\",\"Str\",907970895,null),Xl=new L(null,\"param\",\"param\",2013631823),\nYl=new L(null,\"k\",\"k\",-2146297393),ki=new L(null,\"title\",\"title\",636505583),Zl=new L(null,\"stop-ch\",\"stop-ch\",-219113969),$l=new L(null,\"insert-mode\",\"insert-mode\",894811791),am=new rd(null,\"maybe\",\"maybe\",1326133967,null),bm=new L(null,\"toggle-fullscreen\",\"toggle-fullscreen\",-1647254833),cm=new L(null,\"loop\",\"loop\",-395552849),ni=new L(null,\"author-img-url\",\"author-img-url\",2016975920),dm=new L(null,\"shouldComponentUpdate\",\"shouldComponentUpdate\",1795750960),mj=new L(null,\"ancestors\",\"ancestors\",\n-776045424),em=new rd(null,\"flag\",\"flag\",-1565787888,null),fm=new L(null,\"style\",\"style\",-496642736),gm=new L(null,\"theme\",\"theme\",-1247880880),hm=new L(null,\"stream\",\"stream\",1534941648),im=new L(null,\"charset-fn\",\"charset-fn\",1374523920),li=new L(null,\"author\",\"author\",2111686192),jm=new L(null,\"escape-intermediate\",\"escape-intermediate\",1036490448),km=new L(null,\"div\",\"div\",1057191632),qb=new L(null,\"readably\",\"readably\",1129599760),lm=new L(null,\"change-speed\",\"change-speed\",2125740976),Ki=new L(null,\n\"more-marker\",\"more-marker\",-14717935),mm=new L(null,\"new-line-mode\",\"new-line-mode\",1467504785),nm=new L(null,\"optional?\",\"optional?\",1184638129),om=new L(null,\"csi-intermediate\",\"csi-intermediate\",-410048175),pm=new L(null,\"reagentRender\",\"reagentRender\",-358306383),qm=new L(null,\"idle-time-limit\",\"idle-time-limit\",-928369231),rm=new L(null,\"started?\",\"started?\",-1301062863),sm=new L(null,\"other-buffer-saved\",\"other-buffer-saved\",-2048065486),tm=new L(null,\"snapshot\",\"snapshot\",-1274785710),um=\nnew L(null,\"osc-start\",\"osc-start\",-1717437326),vm=new L(null,\"preload\",\"preload\",1646824722),wm=new L(null,\"stop\",\"stop\",-2140911342),xm=new L(null,\"no-cache\",\"no-cache\",1588056370),ym=new rd(null,\"Uuid\",\"Uuid\",-1866694318,null),zm=new L(null,\"render\",\"render\",-1408033454),Am=new rd(null,\"width\",\"width\",1256460050,null),Bm=new L(null,\"poster\",\"poster\",-1616913550),Cm=new L(null,\"csi-ignore\",\"csi-ignore\",-764437550),Dm=new L(null,\"reagent-render\",\"reagent-render\",-985383853),Em=new L(null,\"auto-play\",\n\"auto-play\",-645319501),Fm=new L(null,\"collect\",\"collect\",-284321549),Gm=new L(null,\"pre.asciinema-terminal\",\"pre.asciinema-terminal\",832737619),Hm=new L(null,\"loading\",\"loading\",-737050189),Im=new L(null,\"priority\",\"priority\",1431093715),Jm=new L(null,\"auto-play?\",\"auto-play?\",385278451),Km=new rd(null,\"val\",\"val\",1769233139,null),Lm=new L(null,\"span.line\",\"span.line\",-1541583788),tb=new L(null,\"print-length\",\"print-length\",1931866356),Mm=new L(null,\"poster-time\",\"poster-time\",1478579796),Nm=new L(null,\n\"saved\",\"saved\",288760660),Om=new L(null,\"error-symbol\",\"error-symbol\",-823480428),oi=new L(null,\"on-can-play\",\"on-can-play\",1481578549),Pm=new L(null,\"catch-exception\",\"catch-exception\",-1997306795),Qm=new L(null,\"constructor\",\"constructor\",-1953928811),Rm=new L(null,\"auto-run\",\"auto-run\",1958400437),Sm=new L(null,\"div.asciinema-player\",\"div.asciinema-player\",-1293079051),kj=new L(null,\"parents\",\"parents\",-2027538891),mi=new L(null,\"author-url\",\"author-url\",1091920533),Tm=new L(null,\"pred-name\",\n\"pred-name\",-3677451),Um=new rd(null,\"meta42957\",\"meta42957\",-1080714315,null),Vm=new L(null,\"on-mouse-move\",\"on-mouse-move\",-1386320874),Wm=new L(null,\"component-will-unmount\",\"component-will-unmount\",-2058314698),Xm=new L(null,\"prev\",\"prev\",-1597069226),Ym=new L(null,\"svg\",\"svg\",856789142),Zm=new L(null,\"getDuration\",\"getDuration\",-995932010),$m=new L(null,\"url\",\"url\",276297046),an=new L(null,\"authorURL\",\"authorURL\",549221782),bn=new rd(null,\"meta38850\",\"meta38850\",1963771318,null),cn=new L(null,\n\"continue-block\",\"continue-block\",-1852047850),dn=new L(null,\"loop?\",\"loop?\",457687798),en=new rd(null,\"ch\",\"ch\",1085813622,null),fn=new rd(null,\"CodePoint\",\"CodePoint\",-132710345,null),gn=new L(null,\"autoPlay\",\"autoPlay\",-561263241),hn=new rd(null,\"\\x3d\\x3e\",\"\\x3d\\x3e\",-813269641,null),jn=new L(null,\"playing\",\"playing\",70013335),kn=new rd(null,\"Keyword\",\"Keyword\",-850065993,null),ln=new L(null,\"display-name\",\"display-name\",694513143),mn=new L(null,\"random\",\"random\",-557811113),nn=new L(null,\"position\",\n\"position\",-2011731912),on=new L(null,\"on-dispose\",\"on-dispose\",2105306360),pn=new L(null,\"d\",\"d\",1972142424),qn=new L(null,\"action\",\"action\",-811238024),rn=new L(null,\"stdout-ch\",\"stdout-ch\",825692568),sn=new L(null,\"pause\",\"pause\",-2095325672),tn=new L(null,\"error\",\"error\",-978969032),un=new L(null,\"span.fullscreen-button\",\"span.fullscreen-button\",-1476136392),vn=new L(null,\"class-name\",\"class-name\",945142584),wn=new L(null,\"componentFunction\",\"componentFunction\",825866104),xn=new L(null,\"div.loader\",\n\"div.loader\",-1644603528),yn=new L(null,\"origin-mode\",\"origin-mode\",-1430095912),zn=new L(null,\"x\",\"x\",2099068185),An=new L(null,\"__html\",\"__html\",674048345),Bn=new L(null,\"fontSize\",\"fontSize\",919623033),Cn=new L(null,\"div.asciinema-player-wrapper\",\"div.asciinema-player-wrapper\",2009764409),Dn=new L(null,\"startAt\",\"startAt\",849336089),En=new L(null,\"getCurrentTime\",\"getCurrentTime\",697283642),Fn=new L(null,\"put\",\"put\",1299772570),Gn=new rd(null,\"CharAttrs\",\"CharAttrs\",1533586778,null),Hn=new L(null,\n\"top-margin\",\"top-margin\",655579514),In=new L(null,\"unhook\",\"unhook\",1440586234),Jn=new L(null,\"play\",\"play\",-580418022),Kn=new L(null,\"seek\",\"seek\",758996602),Ln=new rd(null,\"chars\",\"chars\",545901210,null),Mn=new L(null,\"version\",\"version\",425292698),Nn=new rd(null,\"line\",\"line\",1852876762,null),qi=new L(null,\"on-pause\",\"on-pause\",1839279163),On=new L(null,\"visible\",\"visible\",-1024216805),Pn=new L(null,\"autobind\",\"autobind\",-570650245),Qn=new L(null,\"hierarchy\",\"hierarchy\",-1053470341),Rn=new L(null,\n\"on-key-down\",\"on-key-down\",-1374733765),pi=new L(null,\"on-play\",\"on-play\",-188934501),Sn=new rd(null,\"\\x3d\\x3e*\",\"\\x3d\\x3e*\",1909690043,null),Si=new L(null,\"alt-impl\",\"alt-impl\",670969595),Tn=new L(null,\"bg\",\"bg\",-206688421),Un=new L(null,\"p?\",\"p?\",-1172161701),Vn=new L(null,\"onCanPlay\",\"onCanPlay\",197552027),Wn=new L(null,\"other-buffer-lines\",\"other-buffer-lines\",-1562366021),Xn=new rd(null,\"record\",\"record\",861424668,null),Yn=new L(null,\"italic\",\"italic\",32599196),Zn=new rd(null,\"required-key\",\n\"required-key\",1624616412,null),$n=new L(null,\"dcs-ignore\",\"dcs-ignore\",198619612),ao=new rd(null,\"optional\",\"optional\",-600484260,null),gj=new L(null,\"keywordize-keys\",\"keywordize-keys\",1310784252),bo=new rd(null,\"Int\",\"Int\",-2116888740,null),co=new L(null,\"span.time-remaining\",\"span.time-remaining\",706865437),eo=new L(null,\"componentWillMount\",\"componentWillMount\",-285327619),fo=new L(null,\"idleTimeLimit\",\"idleTimeLimit\",-867712227),go=new L(\"internal\",\"seek\",\"internal/seek\",-1958914115),ho=new L(null,\n\"href\",\"href\",-793805698),io=new L(null,\"buffer\",\"buffer\",617295198),jo=new L(null,\"img\",\"img\",1442687358),ko=new L(null,\"stdout\",\"stdout\",-531490018),lo=new L(null,\"a\",\"a\",-2123407586),mo=new L(null,\"dangerouslySetInnerHTML\",\"dangerouslySetInnerHTML\",-554971138),no=new L(null,\"height\",\"height\",1025178622),oo=new rd(\"s\",\"Num\",\"s/Num\",-2044935073,null),po=new L(null,\"clear\",\"clear\",1877104959),ri=new L(\"cljs.core\",\"not-found\",\"cljs.core/not-found\",-1572889185),qo=new rd(null,\"meta36583\",\"meta36583\",\n-346463841,null),ro=new L(null,\"span\",\"span\",1394872991),so=new L(null,\"show\",\"show\",-576705889),to=new rd(null,\"f\",\"f\",43394975,null),uo=new L(null,\"onPause\",\"onPause\",-470027297);function vo(a,b){var c=Kb(Ai,a,b);return ae(c,vg(function(a){return function(b){return a===b}}(c),b))}var wo=function wo(a){switch(arguments.length){case 0:return wo.B();case 1:return wo.h(arguments[0]);case 2:return wo.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return wo.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};wo.B=function(){return vi};wo.h=function(a){return a};\nwo.c=function(a,b){return H(a)<H(b)?Mb(ge,b,a):Mb(ge,a,b)};wo.A=function(a,b,c){a=vo(H,ge.A(c,b,be([a])));return Mb(wg,y(a),vd(a))};wo.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return wo.A(b,a,c)};wo.L=2;var xo=function xo(a){switch(arguments.length){case 1:return xo.h(arguments[0]);case 2:return xo.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return xo.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};xo.h=function(a){return a};\nxo.c=function(a,b){return H(a)<H(b)?Mb(function(a,d){return He(b,d)?re.c(a,d):a},a,a):Mb(re,a,b)};xo.A=function(a,b,c){return Mb(xo,a,ge.c(c,b))};xo.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return xo.A(b,a,c)};xo.L=2;function yo(a){var b=Pe([Lj,vl,tm,an,gn,Bn,Dn,Vn,fo,uo],[ni,pi,Bm,mi,Em,wk,Hk,oi,qm,qi]);return Mb(function(b,d){var c=J(d,0,null),f=J(d,1,null);return He(a,c)?K.l(b,f,D.c(a,c)):b},Kb(le,a,lh(b)),b)};if(\"undefined\"===typeof zo)var zo=dg.h(null);\nif(\"undefined\"===typeof Ao)var Ao=function(){var a={};a.warn=function(){return function(){function a(a){var b=null;if(0<arguments.length){b=0;for(var d=Array(arguments.length-0);b<d.length;)d[b]=arguments[b+0],++b;b=new Jb(d,0,null)}return c.call(this,b)}function c(a){return gg.A(zo,Ag,new R(null,1,5,T,[Pk],null),ge,be([P(v,a)]))}a.L=0;a.N=function(a){a=E(a);return c(a)};a.A=c;return a}()}(a);a.error=function(){return function(){function a(a){var b=null;if(0<arguments.length){b=0;for(var d=Array(arguments.length-\n0);b<d.length;)d[b]=arguments[b+0],++b;b=new Jb(d,0,null)}return c.call(this,b)}function c(a){return gg.A(zo,Ag,new R(null,1,5,T,[tn],null),ge,be([P(v,a)]))}a.L=0;a.N=function(a){a=E(a);return c(a)};a.A=c;return a}()}(a);return a}();function Bo(a,b,c){var d=RegExp,e=b.source,f=t(b.ignoreCase)?[v.h(\"g\"),\"i\"].join(\"\"):\"g\";f=t(b.multiline)?[v.h(f),\"m\"].join(\"\"):f;b=t(b.cg)?[v.h(f),\"u\"].join(\"\"):f;d=new d(e,b);return a.replace(d,c)}\nfunction Co(a){return function(){function b(a){var b=null;if(0<arguments.length){b=0;for(var d=Array(arguments.length-0);b<d.length;)d[b]=arguments[b+0],++b;b=new Jb(d,0,null)}return c.call(this,b)}function c(b){b=lg(b);if(G.c(H(b),1))return b=y(b),a.h?a.h(b):a.call(null,b);b=Wg(b);return a.h?a.h(b):a.call(null,b)}b.L=0;b.N=function(a){a=E(a);return c(a)};b.A=c;return b}()}\nfunction Do(a,b,c){if(\"string\"===typeof b)return a.replace(new RegExp(String(b).replace(/([-()\\[\\]{}+?*.$\\^|,:#<!\\\\])/g,\"\\\\$1\").replace(/\\x08/g,\"\\\\x08\"),\"g\"),c);if(b instanceof RegExp)return\"string\"===typeof c?Bo(a,b,c):Bo(a,b,Co(c));throw[\"Invalid match arg: \",v.h(b)].join(\"\");}function Eo(a){var b=new cb;for(a=E(a);;)if(null!=a)b=b.append(\"\"+v.h(y(a))),a=z(a);else return b.toString()}\nfunction Fo(a,b){var c=\"/(?:)/\"===\"\"+v.h(b)?ge.c(Wg(ae(\"\",ig.c(v,E(a)))),\"\"):Wg((\"\"+v.h(a)).split(b));if(1<H(c))a:for(;;)if(\"\"===(null==c?null:nc(c)))c=null==c?null:oc(c);else break a;return c};if(\"undefined\"===typeof Go){var Ho;if(\"undefined\"!==typeof React)Ho=React;else{var Io;if(\"undefined\"!==typeof require){var Jo=require(\"react\");if(t(Jo))Io=Jo;else throw Error(\"require('react') failed\");}else throw Error(\"js/React is missing\");Ho=Io}var Go=Ho}\nif(\"undefined\"===typeof Ko){var Lo;if(\"undefined\"!==typeof createReactClass)Lo=createReactClass;else{var Mo;if(\"undefined\"!==typeof require){var No=require(\"create-react-class\");if(t(No))Mo=No;else throw Error(\"require('create-react-class') failed\");}else throw Error(\"js/createReactClass is missing\");Lo=Mo}var Ko=Lo}var Oo=new ti(null,new r(null,2,[\"aria\",null,\"data\",null],null),null);function Po(a){return 2>H(a)?a.toUpperCase():[v.h(a.substring(0,1).toUpperCase()),v.h(a.substring(1))].join(\"\")}\nfunction Qo(a){if(\"string\"===typeof a)return a;a=jf(a);var b=Fo(a,/-/),c=E(b);b=y(c);c=z(c);return t(Oo.h?Oo.h(b):Oo.call(null,b))?a:Kb(v,b,ig.c(Po,c))}function Ro(a){var b=function(){var b=function(){var b=me(a);return b?(b=a.displayName,t(b)?b:a.name):b}();if(t(b))return b;b=function(){var b=null!=a?a.J&4096||q===a.Oe?!0:!1:!1;return b?jf(a):b}();if(t(b))return b;b=qe(a);return xe(b)?Tk.h(b):null}();return Do(\"\"+v.h(b),\"$\",\".\")}var So=!1;if(\"undefined\"===typeof To)var To=0;function Uo(a){return setTimeout(a,16)}var Vo=\"undefined\"===typeof window||null==window.document?Uo:function(){var a=window,b=a.requestAnimationFrame;if(t(b))return b;b=a.webkitRequestAnimationFrame;if(t(b))return b;b=a.mozRequestAnimationFrame;if(t(b))return b;a=a.msRequestAnimationFrame;return t(a)?a:Uo}();function Wo(a,b){return a.cljsMountOrder-b.cljsMountOrder}if(\"undefined\"===typeof Xo)var Xo=function(){return null};function Yo(a){this.Yd=a}\nfunction Zo(a,b){var c=a[b];if(null==c)return null;a[b]=null;for(var d=c.length,e=0;;)if(e<d){var f=c[e];f.B?f.B():f.call(null);e+=1}else return null}function $o(a){if(a.Yd)return null;a.Yd=!0;a=function(a){return function(){a.Yd=!1;return ap(a)}}(a);return Vo.h?Vo.h(a):Vo.call(null,a)}\nfunction ap(a){Zo(a,\"beforeFlush\");Xo();var b=a.componentQueue;if(null!=b)a:{a.componentQueue=null,b.sort(Wo);for(var c=b.length,d=0;;)if(d<c){var e=b[d];!0===e.cljsIsDirty&&e.forceUpdate();d+=1}else break a}return Zo(a,\"afterRender\")}Yo.prototype.enqueue=function(a,b){null==this[a]&&(this[a]=[]);this[a].push(b);return $o(this)};if(\"undefined\"===typeof bp)var bp=new Yo(!1);function cp(a){if(t(a.cljsIsDirty))return null;a.cljsIsDirty=!0;return bp.enqueue(\"componentQueue\",a)};var dp;if(\"undefined\"===typeof ep)var ep=!1;if(\"undefined\"===typeof fp)var fp=0;if(\"undefined\"===typeof gp)var gp=dg.h(0);\nfunction hp(a,b){b.captured=null;a:{var c=dp;dp=b;try{var d=a.B?a.B():a.call(null);break a}finally{dp=c}d=void 0}var e=b.captured;b.rc=!1;a:{c=b.Nc;var f=null==e?0:e.length,h=f===(null==c?0:c.length);if(h)for(h=0;;){var k=h===f;if(k){c=k;break a}if(e[h]===c[h])h+=1;else{c=!1;break a}}else c=h}if(!c)a:{c=yi(e);f=yi(b.Nc);b.Nc=e;e=E(xo.c(c,f));h=null;for(var l=k=0;;)if(l<k){var p=h.$(null,l);Mc(p,b,ip);l+=1}else if(e=E(e))h=e,Ae(h)?(e=Wc(h),l=Xc(h),h=e,k=H(e),e=l):(e=y(h),Mc(e,b,ip),e=z(h),h=null,k=\n0),l=0;else break;c=E(xo.c(f,c));f=null;for(k=h=0;;)if(k<h)e=f.$(null,k),Nc(e,b),k+=1;else if(c=E(c))f=c,Ae(f)?(c=Wc(f),h=Xc(f),f=c,e=H(c),c=h,h=e):(e=y(f),Nc(e,b),c=z(f),f=null,h=0),k=0;else break a}return d}function jp(a){var b=dp;if(null!=b){var c=b.captured;null==c?b.captured=[a]:c.push(a)}}function kp(a,b){ep&&gg.l(gp,Xe,H(b)-H(a));return b}function lp(a,b,c){var d=a.gb;a.gb=kp(d,K.l(d,b,c));return a.Ce=null}function mp(a,b){var c=a.gb;a.gb=kp(c,le.c(c,b));return a.Ce=null}\nfunction np(a,b,c){var d=a.Ce;d=null==d?a.Ce=Ue(function(){return function(a,b,c){a.push(b);a.push(c);return a}}(d),[],a.gb):d;for(var e=d.length,f=0;;)if(f<e){var h=d[f],k=d[f+1];k.M?k.M(h,a,b,c):k.call(null,h,a,b,c);f=2+f}else return null}function op(a,b,c,d){Jc(b,[\"#\\x3c\",v.h(d),\" \"].join(\"\"));a:{d=dp;dp=null;try{var e=B(a);break a}finally{dp=d}e=void 0}Qi(e,b,c);return Jc(b,\"\\x3e\")}if(\"undefined\"===typeof pp)var pp=null;\nfunction qp(){for(;;){var a=pp;if(null==a)return null;pp=null;for(var b=a.length,c=0;;)if(c<b){var d=a[c];d.rc&&null!=d.Nc&&rp(d,!0);c+=1}else break}}Xo=qp;function sp(a,b,c,d){this.state=a;this.meta=b;this.df=c;this.gb=d;this.m=2153938944;this.J=114690}g=sp.prototype;g.R=function(a,b,c){return op(this,b,c,\"Atom:\")};g.P=function(){return this.meta};g.U=function(){return ja(this)};g.K=function(a,b){return this===b};g.Gb=function(a,b){var c=this.state;this.state=b;null!=this.gb&&np(this,c,b);return b};\ng.je=function(a,b){return this.Gb(null,b.h?b.h(this.state):b.call(null,this.state))};g.ke=function(a,b,c){return this.Gb(null,b.c?b.c(this.state,c):b.call(null,this.state,c))};g.le=function(a,b,c,d){return this.Gb(null,b.l?b.l(this.state,c,d):b.call(null,this.state,c,d))};g.me=function(a,b,c,d,e){return this.Gb(null,Af(b,this.state,c,d,e))};g.Kd=function(a,b,c){return np(this,b,c)};g.Jd=function(a,b,c){return lp(this,b,c)};g.Ld=function(a,b){return mp(this,b)};g.pc=function(){jp(this);return this.state};\nvar tp=function tp(a){switch(arguments.length){case 1:return tp.h(arguments[0]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return tp.A(arguments[0],new Jb(c.slice(1),0,null))}};tp.h=function(a){return new sp(a,null,null,null)};tp.A=function(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,rb);c=D.c(c,eg);return new sp(a,d,c,null)};tp.N=function(a){var b=y(a);a=z(a);return tp.A(b,a)};tp.L=1;\nvar up=function up(a){if(null!=a&&null!=a.we)return a.we();var c=up[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=up._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"IDisposable.dispose!\",a);};function ip(a,b,c,d){c===d||a.rc?a=null:null==a.Sb?(a.rc=!0,null==pp&&(pp=[],!1===bp.Yd&&$o(bp)),a=pp.push(a)):a=!0===a.Sb?rp(a,!1):a.Sb.h?a.Sb.h(a):a.Sb.call(null,a);return a}\nfunction vp(a,b,c,d,e,f,h,k){this.Cb=a;this.state=b;this.rc=c;this.We=d;this.Nc=e;this.gb=f;this.Sb=h;this.ee=k;this.m=2153807872;this.J=114690}function wp(a){var b=dp;dp=null;try{return a.pc(null)}finally{dp=b}}function rp(a,b){var c=a.state;if(t(b)){var d=a.Cb;try{a.ee=null;var e=hp(d,a)}catch(f){e=f,a.state=e,a.ee=e,e=a.rc=!1}}else e=hp(a.Cb,a);a.We||(a.state=e,null==a.gb||G.c(c,e)||np(a,c,e));return e}\nfunction xp(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,Rm),e=D.c(c,Tj),f=D.c(c,on);c=D.c(c,xm);null!=d&&(a.Sb=d);null!=e&&(a.Jf=e);null!=f&&(a.Ze=f);null!=c&&(a.We=c)}g=vp.prototype;g.R=function(a,b,c){return op(this,b,c,[\"Reaction \",v.h(od(this)),\":\"].join(\"\"))};g.U=function(){return ja(this)};g.K=function(a,b){return this===b};\ng.we=function(){var a=this.state,b=this.Nc;this.Sb=this.state=this.Nc=null;this.rc=!0;b=E(yi(b));for(var c=null,d=0,e=0;;)if(e<d){var f=c.$(null,e);Nc(f,this);e+=1}else if(b=E(b))c=b,Ae(c)?(b=Wc(c),e=Xc(c),c=b,d=H(b),b=e):(b=y(c),Nc(b,this),b=z(c),c=null,d=0),e=0;else break;null!=this.Ze&&this.Ze(a);a=this.bg;if(null==a)return null;b=a.length;for(c=0;;)if(c<b)d=a[c],d.h?d.h(this):d.call(null,this),c+=1;else return null};g.Gb=function(a,b){var c=this.state;this.state=b;this.Jf(c,b);np(this,c,b);return b};\ng.je=function(a,b){var c=this;return c.Gb(null,function(){var a=wp(c);return b.h?b.h(a):b.call(null,a)}())};g.ke=function(a,b,c){var d=this;return d.Gb(null,function(){var a=wp(d);return b.c?b.c(a,c):b.call(null,a,c)}())};g.le=function(a,b,c,d){var e=this;return e.Gb(null,function(){var a=wp(e);return b.l?b.l(a,c,d):b.call(null,a,c,d)}())};g.me=function(a,b,c,d,e){return this.Gb(null,Af(b,wp(this),c,d,e))};g.Kd=function(a,b,c){return np(this,b,c)};g.Jd=function(a,b,c){return lp(this,b,c)};\ng.Ld=function(a,b){var c=te(this.gb);mp(this,b);return!c&&te(this.gb)&&null==this.Sb?this.we():null};g.pc=function(){var a=this.ee;if(null!=a)throw a;(a=null==dp)&&qp();a&&null==this.Sb?this.rc&&(a=this.state,this.state=this.Cb.B?this.Cb.B():this.Cb.call(null),null==this.gb||G.c(a,this.state)||np(this,a,this.state)):(jp(this),this.rc&&rp(this,!1));return this.state};\nfunction yp(a){for(var b=[],c=arguments.length,d=0;;)if(d<c)b.push(arguments[d]),d+=1;else break;c=arguments[0];b=1<b.length?new Jb(b.slice(1),0,null):null;var e=null!=b&&(b.m&64||q===b.G)?P(U,b):b;b=D.c(e,Rm);d=D.c(e,Tj);e=D.c(e,on);c=new vp(c,null,!0,!1,null,null,null,null);xp(c,new r(null,3,[Rm,b,Tj,d,on,e],null));return c}var zp=yp(null);\nfunction Ap(a,b){var c=Bp,d=zp,e=hp(a,d);null!=d.Nc&&(zp=yp(null),xp(d,c),d.Cb=a,d.Sb=function(){return function(){return cp.h?cp.h(b):cp.call(null,b)}}(d,e),b.cljsRatom=d);return e};var Cp;function Dp(a,b){var c=b.argv;if(null==c){c=T;var d=a.constructor;a:for(var e=Ea(b),f=e.length,h=Ef,k=0;;)if(k<f){var l=e[k];h=K.l(h,hf.h(l),b[l]);k+=1}else break a;c=new R(null,2,5,c,[d,h],null)}return c}function Ep(a){var b;if(b=me(a))a=null==a?null:a.prototype,b=null!=(null==a?null:a.reagentRender);return b}if(\"undefined\"===typeof Fp)var Fp=null;\nfunction Gp(a){for(;;){var b=a.reagentRender,c=!0===a.cljsLegacyRender?b.call(a,a):function(){var c=Dp(a,a.props);switch(H(c)){case 1:return b.call(a);case 2:return b.call(a,Vd(c,1));case 3:return b.call(a,Vd(c,1),Vd(c,2));case 4:return b.call(a,Vd(c,1),Vd(c,2),Vd(c,3));case 5:return b.call(a,Vd(c,1),Vd(c,2),Vd(c,3),Vd(c,4));default:return b.apply(a,Lb(c).slice(1))}}();if(ze(c))return Fp.h?Fp.h(c):Fp.call(null,c);if(Fe(c))c=Ep(c)?function(a,b,c,h){return function(){function a(a){var c=null;if(0<arguments.length){c=\n0;for(var d=Array(arguments.length-0);c<d.length;)d[c]=arguments[c+0],++c;c=new Jb(d,0,null)}return b.call(this,c)}function b(a){a=Kb(Xg,h,a);return Fp.h?Fp.h(a):Fp.call(null,a)}a.L=0;a.N=function(a){a=E(a);return b(a)};a.A=b;return a}()}(a,b,null,c):c,a.reagentRender=c;else return c}}\nvar Bp=new r(null,1,[xm,!0],null),Hp=new r(null,1,[zm,function(){var a=this.cljsRatom;this.cljsIsDirty=!1;return null==a?Ap(function(a,c){return function(){a:{var a=Cp;Cp=c;try{var b=Gp(c);break a}finally{Cp=a}b=void 0}return b}}(a,this),this):rp(a,!1)}],null);\nfunction Ip(a,b){var c=a instanceof L?a.ea:null;switch(c){case \"getDefaultProps\":throw Error(\"getDefaultProps not supported\");case \"getInitialState\":return function(){return function(){var a=this.cljsState;a=null!=a?a:this.cljsState=tp.h(null);return fg(a,b.call(this,this))}}(a,c);case \"componentWillReceiveProps\":return function(){return function(a){return b.call(this,this,Dp(this,a))}}(a,c);case \"shouldComponentUpdate\":return function(){return function(a){var c=So;if(c)return c;c=this.props.argv;\nvar d=a.argv,h=null==c||null==d;return null==b?h||!G.c(c,d):h?b.call(this,this,Dp(this,this.props),Dp(this,a)):b.call(this,this,c,d)}}(a,c);case \"componentWillUpdate\":return function(){return function(a){return b.call(this,this,Dp(this,a))}}(a,c);case \"componentDidUpdate\":return function(){return function(a){return b.call(this,this,Dp(this,a))}}(a,c);case \"componentWillMount\":return function(){return function(){this.cljsMountOrder=To+=1;return null==b?null:b.call(this,this)}}(a,c);case \"componentDidMount\":return function(){return function(){return b.call(this,\nthis)}}(a,c);case \"componentWillUnmount\":return function(){return function(){var a=this.cljsRatom;null!=a&&up(a);this.cljsIsDirty=!1;return null==b?null:b.call(this,this)}}(a,c);default:return null}}function Jp(a,b){var c=Ip(a,b);return t(c)?c:b}var Kp=new r(null,3,[dm,null,eo,null,Kl,null],null),Lp=function(a){return function(b){return function(c){var d=D.c(B(b),c);if(null!=d)return d;d=a.h?a.h(c):a.call(null,c);gg.M(b,K,c,d);return d}}(dg.h(Ef))}(Qo);\nfunction Mp(a){return Ue(function(a,c,d){return K.l(a,hf.h(Lp.h?Lp.h(c):Lp.call(null,c)),d)},Ef,a)}function Np(a){var b=function(){var b=pm.h(a);return t(b)?b:wn.h(a)}(),c=null==b,d=t(b)?b:zm.h(a),e=\"\"+v.h(function(){var b=Bk.h(a);return t(b)?b:Ro(d)}());a:switch(e){case \"\":var f=\"\"+v.h(Zi());break a;default:f=e}b=Ue(function(){return function(a,b,c){return K.l(a,b,Jp(b,c))}}(b,c,d,e,f),Ef,a);return K.A(b,Bk,f,be([Pn,!1,bk,c,pm,d,zm,zm.h(Hp)]))}\nfunction Op(a){return Ue(function(a,c,d){a[jf(c)]=d;return a},{},a)}function Pp(a){a=Op(Np(hi.A(be([Kp,Mp(a)]))));return Ko.h?Ko.h(a):Ko.call(null,a)};var Qp=/([^\\s\\.#]+)(?:#([^\\s\\.#]+))?(?:\\.([^\\s#]+))?/;function Rp(a){return a instanceof L||a instanceof rd}var Sp={\"class\":\"className\",\"for\":\"htmlFor\",charset:\"charSet\"};function Tp(a,b,c){if(Rp(b)){var d=jf(b);d=Sp.hasOwnProperty(d)?Sp[d]:null;b=null==d?Sp[jf(b)]=Qo(b):d}a[b]=Up.h?Up.h(c):Up.call(null,c);return a}\nfunction Up(a){return\"object\"!==n(a)?a:Rp(a)?jf(a):xe(a)?Ue(Tp,{},a):ue(a)?cj(a):Fe(a)?function(){function b(a){var b=null;if(0<arguments.length){b=0;for(var d=Array(arguments.length-0);b<d.length;)d[b]=arguments[b+0],++b;b=new Jb(d,0,null)}return c.call(this,b)}function c(b){return P(a,b)}b.L=0;b.N=function(a){a=E(a);return c(a)};b.A=c;return b}():cj(a)}function Vp(a,b,c){a=null==a?{}:a;a[b]=c;return a}if(\"undefined\"===typeof Wp)var Wp=null;\nvar Xp=new ti(null,new r(null,6,[\"url\",null,\"tel\",null,\"text\",null,\"textarea\",null,\"password\",null,\"search\",null],null),null),Yp=function Yp(a){if(t(a.cljsInputLive)){a.cljsInputDirty=!1;var c=a.cljsRenderedValue,d=a.cljsDOMValue,e=Wp.h?Wp.h(a):Wp.call(null,a);if(!G.c(c,d)){if(e===document.activeElement&&He(Xp,e.type)&&\"string\"===typeof c&&\"string\"===typeof d){var f=e.value;if(!G.c(f,d))return bp.enqueue(\"afterRender\",function(){return function(){return Yp.h?Yp.h(a):Yp.call(null,a)}}(f,c,d,e));d=\nH(f)-e.selectionStart;d=H(c)-d;a.cljsDOMValue=c;e.value=c;e.selectionStart=d;return e.selectionEnd=d}a.cljsDOMValue=c;return e.value=c}}return null};function Zp(a,b,c){a.cljsDOMValue=c.target.value;t(a.cljsInputDirty)||(a.cljsInputDirty=!0,bp.enqueue(\"afterRender\",function(){return Yp(a)}));return b.h?b.h(c):b.call(null,c)}\nfunction $p(a){var b=Cp;if(t(function(){var b=null!=a;return b?(b=a.hasOwnProperty(\"onChange\"),t(b)?a.hasOwnProperty(\"value\"):b):b}())){var c=a.value,d=null==c?\"\":c,e=a.onChange;t(b.cljsInputLive)||(b.cljsInputLive=!0,b.cljsDOMValue=d);b.cljsRenderedValue=d;delete a.value;a.defaultValue=d;a.onChange=function(a,c,d,e){return function(a){return Zp(b,e,a)}}(a,c,d,e)}}\nvar aq=null,cq=new r(null,4,[ln,\"ReagentInput\",nl,Yp,Wm,function(a){return a.cljsInputLive=null},Dm,function(a,b,c,d){$p(c);return bq.M?bq.M(a,b,c,d):bq.call(null,a,b,c,d)}],null);function dq(a){if(xe(a))try{var b=D.c(a,mk)}catch(c){b=null}else b=null;return b}var eq={};\nfunction fq(a,b,c){var d=a.name,e=J(b,c,null),f=null==e||xe(e);e=Up(f?e:null);var h=a.id;e=null!=h&&null==(null==e?null:e.id)?Vp(e,\"id\",h):e;a=a.className;null==a?a=e:(h=null==e?null:e.className,a=Vp(e,\"className\",null==h?a:[v.h(a),\" \",v.h(h)].join(\"\")));c+=f?1:0;a:switch(d){case \"input\":case \"textarea\":f=!0;break a;default:f=!1}if(f)return f=T,null==aq&&(aq=Pp(cq)),b=pe(new R(null,5,5,f,[aq,b,d,a,c],null),qe(b)),gq.h?gq.h(b):gq.call(null,b);f=dq(qe(b));f=null==f?a:Vp(a,\"key\",f);return bq.M?bq.M(b,\nd,f,c):bq.call(null,b,d,f,c)}\nfunction hq(a){for(;;){var b=J(a,0,null);if(Rp(b)||\"string\"===typeof b){b=jf(b);var c=b.indexOf(\"\\x3e\");switch(c){case -1:c=b;b=eq;var d=c;b=b.hasOwnProperty(d)?b[d]:null;if(null==b){b=c;var e=z(Ji(Qp,jf(c)));c=J(e,0,null);d=J(e,1,null);e=J(e,2,null);e=null==e?null:Do(e,/\\./,\" \");b=eq[b]={name:c,id:d,className:e}}return fq(b,a,1);case 0:return b=J(a,1,null),fq({name:b},a,2);default:a=new R(null,2,5,T,[b.substring(0,c),K.l(a,0,b.substring(c+1))],null)}}else return c=b.cljsReactClass,null==c?Ep(b)?\nb=b.cljsReactClass=b:(c=qe(b),c=K.l(c,Dm,b),c=Pp(c),b=b.cljsReactClass=c):b=c,c={argv:a},d=dq(qe(a)),a=null==d?dq(J(a,1,null)):d,null!=a&&(c.key=a),Go.createElement(b,c)}}function gq(a){return\"object\"!==n(a)?a:ze(a)?hq(a):De(a)?iq.h?iq.h(a):iq.call(null,a):Rp(a)?jf(a):(null!=a?a.m&2147483648||q===a.ma||(a.m?0:Ab(Kc,a)):Ab(Kc,a))?Vi(be([a])):a}Fp=gq;function iq(a){a=Lb(a);for(var b=a.length,c=0;;)if(c<b)a[c]=gq(a[c]),c+=1;else break;return a}\nfunction bq(a,b,c,d){var e=H(a)-d;switch(e){case 0:return Go.createElement(b,c);case 1:return Go.createElement(b,c,gq(J(a,d,null)));default:return Go.createElement.apply(null,Ue(function(){return function(a,b,c){b>=d&&a.push(gq(c));return a}}(e),[b,c],a))}};if(\"undefined\"===typeof jq)var jq=null;function kq(){if(null!=jq)return jq;if(\"undefined\"!==typeof ReactDOM)return jq=ReactDOM;if(\"undefined\"!==typeof require){var a=jq=require(\"react-dom\");if(t(a))return a;throw Error(\"require('react-dom') failed\");}throw Error(\"js/ReactDOM is missing\");}if(\"undefined\"===typeof lq)var lq=dg.h(Ef);\nfunction mq(a,b,c){var d=So;So=!0;try{return kq().render(a.B?a.B():a.call(null),b,function(){return function(){var d=So;So=!1;try{return gg.M(lq,K,b,new R(null,2,5,T,[a,b],null)),Zo(bp,\"afterRender\"),null!=c?c.B?c.B():c.call(null):null}finally{So=d}}}(d))}finally{So=d}}function nq(a,b){return mq(a,b,null)}function oq(a,b,c){qp();return mq(function(){return gq(me(a)?a.B?a.B():a.call(null):a)},b,c)}Wp=function(a){return kq().findDOMNode(a)};function pq(a){switch(arguments.length){case 2:return oq(arguments[0],arguments[1],null);case 3:return oq(arguments[0],arguments[1],arguments[2]);default:throw Error([\"Invalid arity: \",v.h(arguments.length)].join(\"\"));}}function qq(a,b){return oq(a,b,null)}\nda(\"reagent.core.force_update_all\",function(){qp();qp();for(var a=E(mh(B(lq))),b=null,c=0,d=0;;)if(d<c){var e=b.$(null,d);P(nq,e);d+=1}else if(a=E(a))b=a,Ae(b)?(a=Wc(b),d=Xc(b),b=a,c=H(a),a=d):(a=y(b),P(nq,a),a=z(b),b=null,c=0),d=0;else break;return Zo(bp,\"afterRender\")});var rq=yi(df(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,25,28,29,30,31)),sq=ke([yi(df(24,26,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,145,146,147,148,149,150,151,153,154)),new r(null,2,[qn,xl,yk,uk],null),yi(df(156)),new r(null,1,[yk,uk],null),yi(df(27)),new r(null,1,[yk,Gj],null),yi(df(152,158,159)),new r(null,1,[yk,ll],null),yi(df(144)),new r(null,1,[yk,ql],null),yi(df(157)),new r(null,1,[yk,Rk],null),yi(df(155)),new r(null,1,[yk,Hl],null)]),tq=Pe([Ej,Gj,\nuk,Fk,Jk,Qk,Rk,ll,ql,Hl,jm,om,Cm,$n],[ke([rq,new r(null,1,[qn,Ul],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47)),new r(null,2,[qn,Fm,yk,Qk],null),yi(df(48,49,50,51,52,53,54,55,56,57,59)),new r(null,1,[qn,Xl],null),yi(df(58,60,61,62,63)),new r(null,1,[yk,$n],null),yi(df(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),\nnew r(null,1,[yk,Fk],null),yi(df(127)),new r(null,1,[qn,Ul],null)]),Pe([Sk,yi(df(88,94,95)),rq,yi(df(91)),yi(df(80)),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47)),yi(df(127)),yi(df(48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,81,82,83,84,85,86,87,89,90,92,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),yi(df(93))],[po,new r(null,1,[yk,ll],null),new r(null,1,[qn,xl],\nnull),new r(null,1,[yk,Hl],null),new r(null,1,[yk,ql],null),new r(null,2,[qn,Fm,yk,jm],null),new r(null,1,[qn,Ul],null),new r(null,2,[qn,Pj,yk,uk],null),new r(null,1,[yk,Rk],null)]),ke([rq,new r(null,1,[qn,xl],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,\n119,120,121,122,123,124,125,126,127,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255)),new r(null,1,[qn,Pl],null)]),ke([Sk,zj,rq,new r(null,1,[qn,Fn],null),yi(df(32,33,\n34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,1,[qn,Fn],null),yi(df(127)),new r(null,1,[qn,Ul],null),El,In]),ke([rq,new r(null,1,[qn,xl],null),yi(df(48,49,50,51,52,53,54,55,56,57,59)),new r(null,1,[qn,Xl],null),yi(df(58,60,61,62,\n63)),new r(null,1,[yk,Cm],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47)),new r(null,2,[qn,Fm,yk,om],null),yi(df(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,2,[qn,Rl,yk,uk],null),yi(df(127)),new r(null,1,[qn,Ul],null)]),ke([rq,new r(null,1,[qn,Ul],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,\n46,47)),new r(null,1,[qn,Fm],null),yi(df(48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63)),new r(null,1,[yk,$n],null),yi(df(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,1,[yk,Fk],null),yi(df(127)),new r(null,1,[qn,Ul],null)]),ke([Sk,um,re.c(rq,7),new r(null,1,[qn,Ul],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,\n45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127)),new r(null,1,[qn,ak],null),yi(df(7)),new r(null,1,[yk,uk],null),El,Wj]),ke([rq,new r(null,1,[qn,Ul],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,\n69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127)),new r(null,1,[qn,Ul],null)]),ke([Sk,po,rq,new r(null,1,[qn,Ul],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47)),new r(null,2,[qn,Fm,yk,Qk],null),yi(df(58)),new r(null,1,[yk,$n],null),yi(df(48,49,50,51,52,53,54,55,56,57,59)),new r(null,2,[qn,Xl,yk,Ej],null),yi(df(60,61,62,63)),new r(null,\n2,[qn,Fm,yk,Ej],null),yi(df(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,1,[yk,Fk],null),yi(df(127)),new r(null,1,[qn,Ul],null)]),ke([Sk,po,rq,new r(null,1,[qn,xl],null),yi(df(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,\n109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,2,[qn,Rl,yk,uk],null),yi(df(48,49,50,51,52,53,54,55,56,57,59)),new r(null,2,[qn,Xl,yk,Jk],null),yi(df(60,61,62,63)),new r(null,2,[qn,Fm,yk,Jk],null),yi(df(58)),new r(null,1,[yk,Cm],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47)),new r(null,2,[qn,Fm,yk,om],null),yi(df(127)),new r(null,1,[qn,Ul],null)]),ke([rq,new r(null,1,[qn,xl],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47)),new r(null,\n1,[qn,Fm],null),yi(df(48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,2,[qn,Pj,yk,uk],null),yi(df(127)),new r(null,1,[qn,Ul],null)]),ke([rq,new r(null,1,[qn,xl],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47)),new r(null,1,[qn,Fm],null),yi(df(64,65,66,67,68,69,\n70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,2,[qn,Rl,yk,uk],null),yi(df(48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63)),new r(null,1,[yk,Cm],null),yi(df(127)),new r(null,1,[qn,Ul],null)]),ke([rq,new r(null,1,[qn,xl],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63)),new r(null,\n1,[qn,Ul],null),yi(df(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,1,[yk,uk],null),yi(df(127)),new r(null,1,[qn,Ul],null)]),ke([rq,new r(null,1,[qn,Ul],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,\n83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127)),new r(null,1,[qn,Ul],null)])]);function uq(a,b){return Wf(function(a){var c=J(a,0,null);a=J(a,1,null);return t(c.h?c.h(b):c.call(null,b))?a:null},a)}\nfunction vq(a,b){var c=D.c(tq,a),d=uq(sq,b);var e=t(d)?d:uq(c,160<=b?65:b);d=qn.h(e);e=yk.h(e);if(t(e)){var f=D.c(tq,e);c=El.h(c);f=Sk.h(f);d=Wg(vg(ub,new R(null,3,5,T,[c,d,f],null)));return new R(null,2,5,T,[e,d],null)}return new R(null,2,5,T,[a,t(d)?new R(null,1,5,T,[d],null):he],null)}\nvar xq=P(hi,function wq(a){return new kf(null,function(){for(;;){var c=E(a);if(c){if(Ae(c)){var d=Wc(c),e=H(d),f=of(e);a:for(var h=0;;)if(h<e){var k=A.c(d,h);k=ke([k,xg(ag.c(vq,k),Fi(0,160,1))]);f.add(k);h+=1}else{d=!0;break a}return d?qf(f.Da(),wq(Xc(c))):qf(f.Da(),null)}f=y(c);return ae(ke([f,xg(ag.c(vq,f),Fi(0,160,1))]),wq(vd(c)))}return null}},null,null)}(lh(tq)));function yq(a,b){var c=Array.prototype.slice.call(arguments),d=c.shift();if(\"undefined\"==typeof d)throw Error(\"[goog.string.format] Template required\");return d.replace(/%([0\\-\\ \\+]*)(\\d+)?(\\.(\\d+))?([%sfdiu])/g,function(a,b,d,k,l,p,m,u){if(\"%\"==p)return\"%\";var e=c.shift();if(\"undefined\"==typeof e)throw Error(\"[goog.string.format] Not enough arguments\");arguments[0]=e;return yq.fc[p].apply(null,arguments)})}yq.fc={};\nyq.fc.s=function(a,b,c){return isNaN(c)||\"\"==c||a.length>=Number(c)?a:a=-1<b.indexOf(\"-\",0)?a+sa(\" \",Number(c)-a.length):sa(\" \",Number(c)-a.length)+a};\nyq.fc.f=function(a,b,c,d,e){d=a.toString();isNaN(e)||\"\"==e||(d=parseFloat(a).toFixed(e));var f=0>Number(a)?\"-\":0<=b.indexOf(\"+\")?\"+\":0<=b.indexOf(\" \")?\" \":\"\";0<=Number(a)&&(d=f+d);if(isNaN(c)||d.length>=Number(c))return d;d=isNaN(e)?Math.abs(Number(a)).toString():Math.abs(Number(a)).toFixed(e);a=Number(c)-d.length-f.length;0<=b.indexOf(\"-\",0)?d=f+d+sa(\" \",a):(b=0<=b.indexOf(\"0\",0)?\"0\":\" \",d=f+sa(b,a)+d);return d};yq.fc.d=function(a,b,c,d,e,f,h,k){return yq.fc.f(parseInt(a,10),b,c,d,0,f,h,k)};\nyq.fc.i=yq.fc.d;yq.fc.u=yq.fc.d;function zq(a){var b=be([Vk,null]);return wg.c(t(a)?a:Ef,function(){return function e(a){return new kf(null,function(){for(var b=a;;)if(b=E(b)){if(Ae(b)){var d=Wc(b),k=H(d),l=of(k);a:for(var p=0;;)if(p<k){var m=A.c(d,p),u=J(m,0,null);m=J(m,1,null);t(m)&&l.add(new R(null,2,5,T,[u,m],null));p+=1}else{d=!0;break a}return d?qf(l.Da(),e(Xc(b))):qf(l.Da(),null)}d=y(b);l=J(d,0,null);d=J(d,1,null);if(t(d))return ae(new R(null,2,5,T,[l,d],null),e(vd(b)));b=vd(b)}else return null},null,null)}(yg(2,2,b))}())}\nfunction Aq(a){for(var b=[],c=arguments.length,d=0;;)if(d<c)b.push(arguments[d]),d+=1;else break;return Bq(arguments[0],1<b.length?new Jb(b.slice(1),0,null):null)}function Bq(a,b){return Kb(yq,a,b)}dg.h(19);function Cq(a){return Mb(function(a,c){var b=J(c,0,null),e=J(c,1,null);return Do(a,e,\"\"+v.h(b))},a,Oe(function(a){return-H(ee(a))}))}\nfunction Dq(a){a=\"\"+v.h(a);var b=/function ([^\\(]*)\\(/;if(\"string\"===typeof a)a=b.exec(a),a=null==a?null:1===H(a)?y(a):Wg(a);else throw new TypeError(\"re-find must match against a string.\");a=Bf(ee(a));return Cq(t(a)?a:\"function\")}function Eq(a,b){a.schema$utils$schema=b}dg.h(!1);var Fq,Gq=function Gq(a){if(null!=a&&null!=a.xb)return a.xb(a);var c=Gq[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Gq._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"Schema.explain\",a);};\nGq[\"function\"]=function(a){var b=a.schema$utils$schema;return t(b)?Gq(b):t(G.c?G.c(null,a):G.call(null,null,a))?Wl:t(G.c?G.c(Boolean,a):G.call(null,Boolean,a))?xk:t(G.c?G.c(Number,a):G.call(null,Number,a))?sl:t(G.c?G.c(null,a):G.call(null,null,a))?Cl:t(G.c?G.c(Date,a):G.call(null,Date,a))?Ll:t(G.c?G.c(yj,a):G.call(null,yj,a))?ym:a};function Hq(a,b,c,d){this.nc=a;this.v=b;this.j=c;this.w=d;this.m=2229667594;this.J=139264}g=Hq.prototype;g.V=function(a,b){return this.I(null,b,null)};\ng.I=function(a,b,c){switch(b instanceof L?b.ea:null){case \"_\":return this.nc;default:return D.l(this.j,b,c)}};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#schema.core.AnythingSchema{\",\", \",\"}\",c,O.c(new R(null,1,5,T,[new R(null,2,5,T,[Ck,this.nc],null)],null),this.j))};g.ba=function(){return new fh(0,this,1,new R(null,1,5,T,[Ck],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 1+H(this.j)};\ng.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-1432036169^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.nc,b.nc)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,1,[Ck,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new Hq(this.nc,this.v,Bf(le.c(this.j,b)),null)};\ng.O=function(a,b,c){return t(N.c?N.c(Ck,b):N.call(null,Ck,b))?new Hq(c,this.v,this.j,null):new Hq(this.nc,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,1,5,T,[new R(null,2,5,T,[Ck,this.nc],null)],null),this.j))};g.T=function(a,b){return new Hq(this.nc,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};g.xb=function(){return Al};var Iq=new Hq(null,null,null,null);\nfunction Jq(a,b,c,d,e){this.wb=a;this.Xb=b;this.v=c;this.j=d;this.w=e;this.m=2229667594;this.J=139264}g=Jq.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case \"p?\":return this.wb;case \"pred-name\":return this.Xb;default:return D.l(this.j,b,c)}};\ng.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#schema.core.Predicate{\",\", \",\"}\",c,O.c(new R(null,2,5,T,[new R(null,2,5,T,[Un,this.wb],null),new R(null,2,5,T,[Tm,this.Xb],null)],null),this.j))};g.ba=function(){return new fh(0,this,2,new R(null,2,5,T,[Un,Tm],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 2+H(this.j)};\ng.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 2041221968^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.wb,b.wb)&&G.c(this.Xb,b.Xb)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,2,[Tm,null,Un,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new Jq(this.wb,this.Xb,this.v,Bf(le.c(this.j,b)),null)};\ng.O=function(a,b,c){return t(N.c?N.c(Un,b):N.call(null,Un,b))?new Jq(c,this.Xb,this.v,this.j,null):t(N.c?N.c(Tm,b):N.call(null,Tm,b))?new Jq(this.wb,c,this.v,this.j,null):new Jq(this.wb,this.Xb,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,2,5,T,[new R(null,2,5,T,[Un,this.wb],null),new R(null,2,5,T,[Tm,this.Xb],null)],null),this.j))};g.T=function(a,b){return new Jq(this.wb,this.Xb,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};\ng.xb=function(){return G.c(this.wb,Ge)?bo:G.c(this.wb,gf)?kn:G.c(this.wb,qd)?hk:G.c(this.wb,yb)?Wl:Tb(Tb(wd,this.Xb),yl)};function Kq(a){var b=td.h(Dq(a));if(!Fe(a))throw Error(Bq(\"Not a function: %s\",be([a])));return new Jq(a,b,null,null,null)}RegExp.prototype.xb=function(){return td.h(['#\"',v.h((\"\"+v.h(this)).slice(1,-1)),'\"'].join(\"\"))};var Lq=Kq(yb),Mq=Boolean,Nq=Number,Oq=Kq(Ge),Pq=Kq(gf);Kq(qd);\n\"undefined\"===typeof Fq&&(Fq=function(a){this.Bf=a;this.m=393216;this.J=0},Fq.prototype.T=function(a,b){return new Fq(b)},Fq.prototype.P=function(){return this.Bf},Fq.prototype.xb=function(){return Cl},Fq.Wc=function(){return new R(null,1,5,T,[bn],null)},Fq.qc=!0,Fq.Tb=\"schema.core/t_schema$core38849\",Fq.Ec=function(a,b){return Jc(b,\"schema.core/t_schema$core38849\")});function Qq(a,b,c,d){this.ia=a;this.v=b;this.j=c;this.w=d;this.m=2229667594;this.J=139264}g=Qq.prototype;\ng.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case \"schema\":return this.ia;default:return D.l(this.j,b,c)}};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#schema.core.Maybe{\",\", \",\"}\",c,O.c(new R(null,1,5,T,[new R(null,2,5,T,[Mj,this.ia],null)],null),this.j))};g.ba=function(){return new fh(0,this,1,new R(null,1,5,T,[Mj],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};\ng.W=function(){return 1+H(this.j)};g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-805411239^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.ia,b.ia)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,1,[Mj,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new Qq(this.ia,this.v,Bf(le.c(this.j,b)),null)};\ng.O=function(a,b,c){return t(N.c?N.c(Mj,b):N.call(null,Mj,b))?new Qq(c,this.v,this.j,null):new Qq(this.ia,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,1,5,T,[new R(null,2,5,T,[Mj,this.ia],null)],null),this.j))};g.T=function(a,b){return new Qq(this.ia,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};g.xb=function(){var a=Gq(this.ia);a=Tb(wd,a);return Tb(a,am)};\nfunction Rq(a,b,c,d,e){this.Yb=a;this.Hb=b;this.v=c;this.j=d;this.w=e;this.m=2229667594;this.J=139264}g=Rq.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case \"preds-and-schemas\":return this.Yb;case \"error-symbol\":return this.Hb;default:return D.l(this.j,b,c)}};\ng.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#schema.core.ConditionalSchema{\",\", \",\"}\",c,O.c(new R(null,2,5,T,[new R(null,2,5,T,[Il,this.Yb],null),new R(null,2,5,T,[Om,this.Hb],null)],null),this.j))};g.ba=function(){return new fh(0,this,2,new R(null,2,5,T,[Il,Om],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 2+H(this.j)};\ng.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 1418435858^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.Yb,b.Yb)&&G.c(this.Hb,b.Hb)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,2,[Il,null,Om,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new Rq(this.Yb,this.Hb,this.v,Bf(le.c(this.j,b)),null)};\ng.O=function(a,b,c){return t(N.c?N.c(Il,b):N.call(null,Il,b))?new Rq(c,this.Hb,this.v,this.j,null):t(N.c?N.c(Om,b):N.call(null,Om,b))?new Rq(this.Yb,c,this.v,this.j,null):new Rq(this.Yb,this.Hb,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,2,5,T,[new R(null,2,5,T,[Il,this.Yb],null),new R(null,2,5,T,[Om,this.Hb],null)],null),this.j))};g.T=function(a,b){return new Rq(this.Yb,this.Hb,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};\ng.xb=function(){return ae(pk,O.c(sg(function(){return function(a){var b=J(a,0,null);a=J(a,1,null);return new R(null,2,5,T,[td.h(Dq(b)),Gq(a)],null)}}(this),be([this.Yb])),t(this.Hb)?new R(null,1,5,T,[this.Hb],null):null))};function Sq(a){return a instanceof L||!1}function Tq(a,b,c,d){this.k=a;this.v=b;this.j=c;this.w=d;this.m=2229667594;this.J=139264}g=Tq.prototype;g.V=function(a,b){return this.I(null,b,null)};\ng.I=function(a,b,c){switch(b instanceof L?b.ea:null){case \"k\":return this.k;default:return D.l(this.j,b,c)}};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#schema.core.OptionalKey{\",\", \",\"}\",c,O.c(new R(null,1,5,T,[new R(null,2,5,T,[Yl,this.k],null)],null),this.j))};g.ba=function(){return new fh(0,this,1,new R(null,1,5,T,[Yl],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 1+H(this.j)};\ng.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-1508333161^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.k,b.k)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,1,[Yl,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new Tq(this.k,this.v,Bf(le.c(this.j,b)),null)};\ng.O=function(a,b,c){return t(N.c?N.c(Yl,b):N.call(null,Yl,b))?new Tq(c,this.v,this.j,null):new Tq(this.k,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,1,5,T,[new R(null,2,5,T,[Yl,this.k],null)],null),this.j))};g.T=function(a,b){return new Tq(this.k,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function Uq(a){return new Tq(a,null,null,null)}\nfunction Vq(a){var b=Sq(a);if(t(t(b)?b:a instanceof Tq)){if(a instanceof L)return a;b=t(Sq(a))?Zn:t(a instanceof Tq)?Nj:null;if(!(a instanceof L))if(t(a instanceof Tq))a=a.k;else throw Error(Bq(\"Bad explicit key: %s\",be([a])));a=Tb(wd,a);return Tb(a,b)}return Gq(a)}\nfunction Wq(a){return wg.c(Ef,function(){return function d(a){return new kf(null,function(){for(;;){var c=E(a);if(c){if(Ae(c)){var f=Wc(c),h=H(f),k=of(h);a:for(var l=0;;)if(l<h){var p=A.c(f,l),m=J(p,0,null);p=J(p,1,null);m=new R(null,2,5,T,[Vq(m),Gq(p)],null);k.add(m);l+=1}else{f=!0;break a}return f?qf(k.Da(),d(Xc(c))):qf(k.Da(),null)}f=y(c);k=J(f,0,null);f=J(f,1,null);return ae(new R(null,2,5,T,[Vq(k),Gq(f)],null),d(vd(c)))}return null}},null,null)}(a)}())}r.prototype.xb=function(){return Wq(this)};\nJh.prototype.xb=function(){return Wq(this)};ti.prototype.xb=function(){return yi(new R(null,1,5,T,[Gq(y(this))],null))};function Xq(a,b,c,d,e,f){this.ia=a;this.Fb=b;this.name=c;this.v=d;this.j=e;this.w=f;this.m=2229667594;this.J=139264}g=Xq.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case \"schema\":return this.ia;case \"optional?\":return this.Fb;case \"name\":return this.name;default:return D.l(this.j,b,c)}};\ng.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#schema.core.One{\",\", \",\"}\",c,O.c(new R(null,3,5,T,[new R(null,2,5,T,[Mj,this.ia],null),new R(null,2,5,T,[nm,this.Fb],null),new R(null,2,5,T,[Tk,this.name],null)],null),this.j))};g.ba=function(){return new fh(0,this,3,new R(null,3,5,T,[Mj,nm,Tk],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 3+H(this.j)};\ng.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-197981045^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.ia,b.ia)&&G.c(this.Fb,b.Fb)&&G.c(this.name,b.name)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,3,[Mj,null,Tk,null,nm,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new Xq(this.ia,this.Fb,this.name,this.v,Bf(le.c(this.j,b)),null)};\ng.O=function(a,b,c){return t(N.c?N.c(Mj,b):N.call(null,Mj,b))?new Xq(c,this.Fb,this.name,this.v,this.j,null):t(N.c?N.c(nm,b):N.call(null,nm,b))?new Xq(this.ia,c,this.name,this.v,this.j,null):t(N.c?N.c(Tk,b):N.call(null,Tk,b))?new Xq(this.ia,this.Fb,c,this.v,this.j,null):new Xq(this.ia,this.Fb,this.name,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,3,5,T,[new R(null,2,5,T,[Mj,this.ia],null),new R(null,2,5,T,[nm,this.Fb],null),new R(null,2,5,T,[Tk,this.name],null)],null),this.j))};\ng.T=function(a,b){return new Xq(this.ia,this.Fb,this.name,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function Yq(a,b){return new Xq(a,!1,b,null,null,null)}\nfunction Zq(a){var b=Gi(function(a){return a instanceof Xq&&wb(nm.h(a))},a),c=J(b,0,null),d=J(b,1,null),e=Gi(function(){return function(a){var b=a instanceof Xq;return b?nm.h(a):b}}(b,c,d),d),f=J(e,0,null),h=J(e,1,null);if(!(1>=H(h)&&Vf(function(){return function(a){return!(a instanceof Xq)}}(b,c,d,e,f,h),h)))throw Error(Bq(\"%s is not a valid sequence schema; %s%s%s\",be([a,\"a valid sequence schema consists of zero or more `one` elements, \",\"followed by zero or more `optional` elements, followed by an optional \",\n\"schema that will match the remaining elements.\"])));return new R(null,2,5,T,[O.c(c,f),y(h)],null)}\nR.prototype.xb=function(){var a=this,b=Zq(a),c=J(b,0,null),d=J(b,1,null);return Wg(O.c(function(){return function(a,b,c,d){return function m(e){return new kf(null,function(){return function(){for(;;){var a=E(e);if(a){if(Ae(a)){var b=Wc(a),c=H(b),d=of(c);return function(){for(var a=0;;)if(a<c){var e=A.c(b,a),f=d;var h=t(e.Fb)?ao:zk;var k=Gq(Mj.h(e));e=Tk.h(e);e=Tb(wd,e);k=Tb(e,k);h=Tb(k,h);f.add(h);a+=1}else return!0}()?qf(d.Da(),m(Xc(a))):qf(d.Da(),null)}var f=y(a);return ae(function(){var a=t(f.Fb)?\nao:zk;var b=Gq(Mj.h(f));var c=Tk.h(f);c=Tb(wd,c);b=Tb(c,b);return Tb(b,a)}(),m(vd(a)))}return null}}}(a,b,c,d),null,null)}}(b,c,d,a)(c)}(),t(d)?new R(null,1,5,T,[Gq(d)],null):null))};function $q(a,b,c,d,e){this.Vb=a;this.ia=b;this.v=c;this.j=d;this.w=e;this.m=2229667594;this.J=139264}g=$q.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case \"klass\":return this.Vb;case \"schema\":return this.ia;default:return D.l(this.j,b,c)}};\ng.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#schema.core.Record{\",\", \",\"}\",c,O.c(new R(null,2,5,T,[new R(null,2,5,T,[ck,this.Vb],null),new R(null,2,5,T,[Mj,this.ia],null)],null),this.j))};g.ba=function(){return new fh(0,this,2,new R(null,2,5,T,[ck,Mj],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 2+H(this.j)};\ng.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-1486476872^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.Vb,b.Vb)&&G.c(this.ia,b.ia)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,2,[Mj,null,ck,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new $q(this.Vb,this.ia,this.v,Bf(le.c(this.j,b)),null)};\ng.O=function(a,b,c){return t(N.c?N.c(ck,b):N.call(null,ck,b))?new $q(c,this.ia,this.v,this.j,null):t(N.c?N.c(Mj,b):N.call(null,Mj,b))?new $q(this.Vb,c,this.v,this.j,null):new $q(this.Vb,this.ia,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,2,5,T,[new R(null,2,5,T,[ck,this.Vb],null),new R(null,2,5,T,[Mj,this.ia],null)],null),this.j))};g.T=function(a,b){return new $q(this.Vb,this.ia,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};\ng.xb=function(){var a=td.h(Vi(be([this.Vb])));var b=Gq(this.ia);b=Tb(wd,b);a=Tb(b,a);return Tb(a,Xn)};function ar(a,b,c){if(!xe(b))throw Error(Bq(\"Expected map, got %s\",be([typeof b])));return pe(new $q(a,b,null,null,null),new r(null,1,[Qm,c],null))}function br(a){a=Gi(function(a){return a instanceof Xq},a);var b=J(a,0,null),c=J(a,1,null);return O.c(ig.c(function(){return function(a){return Gq(a.ia)}}(a,b,c),b),E(c)?new R(null,2,5,T,[Dj,xg(Gq,c)],null):null)}\nfunction cr(a,b,c,d,e){this.Nb=a;this.Db=b;this.v=c;this.j=d;this.w=e;this.m=2229667594;this.J=139264}g=cr.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case \"output-schema\":return this.Nb;case \"input-schemas\":return this.Db;default:return D.l(this.j,b,c)}};\ng.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#schema.core.FnSchema{\",\", \",\"}\",c,O.c(new R(null,2,5,T,[new R(null,2,5,T,[Wk,this.Nb],null),new R(null,2,5,T,[jl,this.Db],null)],null),this.j))};g.ba=function(){return new fh(0,this,2,new R(null,2,5,T,[Wk,jl],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 2+H(this.j)};\ng.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-2054647546^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.Nb,b.Nb)&&G.c(this.Db,b.Db)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,2,[Wk,null,jl,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new cr(this.Nb,this.Db,this.v,Bf(le.c(this.j,b)),null)};\ng.O=function(a,b,c){return t(N.c?N.c(Wk,b):N.call(null,Wk,b))?new cr(c,this.Db,this.v,this.j,null):t(N.c?N.c(jl,b):N.call(null,jl,b))?new cr(this.Nb,c,this.v,this.j,null):new cr(this.Nb,this.Db,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,2,5,T,[new R(null,2,5,T,[Wk,this.Nb],null),new R(null,2,5,T,[jl,this.Db],null)],null),this.j))};g.T=function(a,b){return new cr(this.Nb,this.Db,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};\ng.xb=function(){if(1<H(this.Db)){var a=Gq(this.Nb);var b=ig.c(br,this.Db);a=ae(Sn,ae(a,b))}else a=Gq(this.Nb),b=br(y(this.Db)),a=ae(hn,ae(a,b));return a};function dr(a,b){return new cr(a,b,null,null,null)}function er(a){return E(a)?fe(a)instanceof Xq?H(a):Number.MAX_VALUE:0}\nfunction fr(a,b){if(!E(b))throw Error(Aq(\"Function must have at least one input schema\"));if(!Vf(ze,b))throw Error(Aq(\"Each arity must be a vector.\"));if(!t(P(Ie,ig.c(er,b))))throw Error(Aq(\"Arities must be distinct\"));return new cr(a,Qe(er,b),null,null,null)};var gr,hr,ir=Kq(Fe),jr=new r(null,3,[zn,Nq,Aj,Nq,On,Mq],null),kr;\nkr=function(a){if(!E(a)||!(Xf(H(a))||fe(a)instanceof rd))throw Error(Bq(\"Expected even, nonzero number of args (with optional trailing symbol); got %s\",be([H(a)])));return new Rq(Wg(function(){return function d(a){return new kf(null,function(){for(;;){var c=E(a);if(c){if(Ae(c)){var f=Wc(c),h=H(f),k=of(h);a:for(var l=0;;)if(l<h){var p=A.c(f,l),m=J(p,0,null),u=J(p,1,null);p=k;if(!Fe(m))throw Error(Aq([\"Conditional predicate \",v.h(m),\" must be a function\"].join(\"\")));m=new R(null,2,5,T,[G.c(m,sk)?Zf(!0):\nm,u],null);p.add(m);l+=1}else{f=!0;break a}return f?qf(k.Da(),d(Xc(c))):qf(k.Da(),null)}f=y(c);k=J(f,0,null);h=J(f,1,null);f=ae;if(!Fe(k))throw Error(Aq([\"Conditional predicate \",v.h(k),\" must be a function\"].join(\"\")));k=new R(null,2,5,T,[G.c(k,sk)?Zf(!0):k,h],null);return f(k,d(vd(c)))}return null}},null,null)}(yg(2,2,a))}()),Xf(H(a))?null:fe(a),null,null,null)}(be([ze,new R(null,3,5,T,[Yq(Nq,\"r\"),Yq(Nq,\"g\"),Yq(Nq,\"b\")],null),Zf(!0),Nq]));\nvar lr=ke([Uq(Ok),kr,Uq(Tn),kr,Uq(Kj),Mq,Uq(Yn),Mq,Uq(Vl),Mq,Uq(dk),Mq,Uq(Nk),Mq]),mr=new r(null,4,[pl,new r(null,2,[zn,Nq,Aj,Nq],null),Oj,lr,yn,Mq,Rj,Mq],null),nr=new R(null,2,5,T,[Yq(Nq,\"unicode codepoint\"),Yq(lr,\"text attributes\")],null),or=new R(null,1,5,T,[nr],null),pr=E(ug(function(a){return Sq(a)},lh(null)));if(!wb(pr))throw Error(Bq(\"extra-key-schema? can not contain required keys: %s\",be([Wg(pr)])));\nfunction qr(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga){this.width=a;this.height=b;this.Ba=c;this.qa=d;this.Aa=e;this.cursor=f;this.ra=h;this.sa=k;this.ta=l;this.pa=p;this.ua=m;this.va=u;this.wa=w;this.buffer=x;this.lines=C;this.za=F;this.xa=I;this.ya=M;this.v=S;this.j=X;this.w=Ga;this.m=2229667594;this.J=139264}g=qr.prototype;g.V=function(a,b){return this.I(null,b,null)};\ng.I=function(a,b,c){switch(b instanceof L?b.ea:null){case \"width\":return this.width;case \"height\":return this.height;case \"top-margin\":return this.Ba;case \"bottom-margin\":return this.qa;case \"tabs\":return this.Aa;case \"cursor\":return this.cursor;case \"char-attrs\":return this.ra;case \"charset-fn\":return this.sa;case \"insert-mode\":return this.ta;case \"auto-wrap-mode\":return this.pa;case \"new-line-mode\":return this.ua;case \"next-print-wraps\":return this.va;case \"origin-mode\":return this.wa;case \"buffer\":return this.buffer;\ncase \"lines\":return this.lines;case \"saved\":return this.za;case \"other-buffer-lines\":return this.xa;case \"other-buffer-saved\":return this.ya;default:return D.l(this.j,b,c)}};\ng.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#asciinema.vt.screen.Screen{\",\", \",\"}\",c,O.c(new R(null,18,5,T,[new R(null,2,5,T,[fl,this.width],null),new R(null,2,5,T,[no,this.height],null),new R(null,2,5,T,[Hn,this.Ba],null),new R(null,2,5,T,[Yj,this.qa],null),new R(null,2,5,T,[tk,this.Aa],null),new R(null,2,5,T,[pl,this.cursor],null),new R(null,2,5,T,[Oj,this.ra],null),new R(null,2,5,T,[im,this.sa],null),new R(null,2,5,T,[$l,this.ta],null),new R(null,\n2,5,T,[Rj,this.pa],null),new R(null,2,5,T,[mm,this.ua],null),new R(null,2,5,T,[vk,this.va],null),new R(null,2,5,T,[yn,this.wa],null),new R(null,2,5,T,[io,this.buffer],null),new R(null,2,5,T,[il,this.lines],null),new R(null,2,5,T,[Nm,this.za],null),new R(null,2,5,T,[Wn,this.xa],null),new R(null,2,5,T,[sm,this.ya],null)],null),this.j))};g.ba=function(){return new fh(0,this,18,new R(null,18,5,T,[fl,no,Hn,Yj,tk,pl,Oj,im,$l,Rj,mm,vk,yn,io,il,Nm,Wn,sm],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};\ng.W=function(){return 18+H(this.j)};g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-1452363486^Dd(a)}}(b,a)(a)}();return this.w=c};\ng.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.width,b.width)&&G.c(this.height,b.height)&&G.c(this.Ba,b.Ba)&&G.c(this.qa,b.qa)&&G.c(this.Aa,b.Aa)&&G.c(this.cursor,b.cursor)&&G.c(this.ra,b.ra)&&G.c(this.sa,b.sa)&&G.c(this.ta,b.ta)&&G.c(this.pa,b.pa)&&G.c(this.ua,b.ua)&&G.c(this.va,b.va)&&G.c(this.wa,b.wa)&&G.c(this.buffer,b.buffer)&&G.c(this.lines,b.lines)&&G.c(this.za,b.za)&&G.c(this.xa,b.xa)&&G.c(this.ya,b.ya)&&G.c(this.j,b.j)};\ng.ga=function(a,b){return He(new ti(null,new r(null,18,[Oj,null,Rj,null,Yj,null,tk,null,vk,null,fl,null,il,null,pl,null,$l,null,im,null,mm,null,sm,null,Nm,null,yn,null,Hn,null,Wn,null,io,null,no,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,Bf(le.c(this.j,b)),null)};\ng.O=function(a,b,c){return t(N.c?N.c(fl,b):N.call(null,fl,b))?new qr(c,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(no,b):N.call(null,no,b))?new qr(this.width,c,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(Hn,b):N.call(null,Hn,b))?new qr(this.width,\nthis.height,c,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(Yj,b):N.call(null,Yj,b))?new qr(this.width,this.height,this.Ba,c,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(tk,b):N.call(null,tk,b))?new qr(this.width,this.height,this.Ba,this.qa,c,this.cursor,this.ra,this.sa,this.ta,\nthis.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(pl,b):N.call(null,pl,b))?new qr(this.width,this.height,this.Ba,this.qa,this.Aa,c,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(Oj,b):N.call(null,Oj,b))?new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,c,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,\nthis.ya,this.v,this.j,null):t(N.c?N.c(im,b):N.call(null,im,b))?new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,c,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c($l,b):N.call(null,$l,b))?new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,c,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(Rj,b):N.call(null,Rj,b))?new qr(this.width,\nthis.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,c,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(mm,b):N.call(null,mm,b))?new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,c,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(vk,b):N.call(null,vk,b))?new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,\nthis.pa,this.ua,c,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(yn,b):N.call(null,yn,b))?new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,c,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(io,b):N.call(null,io,b))?new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,c,this.lines,this.za,this.xa,this.ya,\nthis.v,this.j,null):t(N.c?N.c(il,b):N.call(null,il,b))?new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,c,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(Nm,b):N.call(null,Nm,b))?new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,c,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(Wn,b):N.call(null,Wn,b))?new qr(this.width,\nthis.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,c,this.ya,this.v,this.j,null):t(N.c?N.c(sm,b):N.call(null,sm,b))?new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,c,this.v,this.j,null):new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,\nthis.buffer,this.lines,this.za,this.xa,this.ya,this.v,K.l(this.j,b,c),null)};\ng.S=function(){return E(O.c(new R(null,18,5,T,[new R(null,2,5,T,[fl,this.width],null),new R(null,2,5,T,[no,this.height],null),new R(null,2,5,T,[Hn,this.Ba],null),new R(null,2,5,T,[Yj,this.qa],null),new R(null,2,5,T,[tk,this.Aa],null),new R(null,2,5,T,[pl,this.cursor],null),new R(null,2,5,T,[Oj,this.ra],null),new R(null,2,5,T,[im,this.sa],null),new R(null,2,5,T,[$l,this.ta],null),new R(null,2,5,T,[Rj,this.pa],null),new R(null,2,5,T,[mm,this.ua],null),new R(null,2,5,T,[vk,this.va],null),new R(null,\n2,5,T,[yn,this.wa],null),new R(null,2,5,T,[io,this.buffer],null),new R(null,2,5,T,[il,this.lines],null),new R(null,2,5,T,[Nm,this.za],null),new R(null,2,5,T,[Wn,this.xa],null),new R(null,2,5,T,[sm,this.ya],null)],null),this.j))};g.T=function(a,b){return new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,b,this.j,this.w)};\ng.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function rr(a){return new qr(fl.h(a),no.h(a),Hn.h(a),Yj.h(a),tk.h(a),pl.h(a),Oj.h(a),im.h(a),$l.h(a),Rj.h(a),mm.h(a),vk.h(a),yn.h(a),io.h(a),il.h(a),Nm.h(a),Wn.h(a),sm.h(a),null,Bf(le.A(a,fl,be([no,Hn,Yj,tk,pl,Oj,im,$l,Rj,mm,vk,yn,io,il,Nm,Wn,sm]))),null)}\nEq(qr,zq(ar(qr,hi.A(be([Pe([Oj,Rj,Yj,tk,vk,fl,il,pl,$l,im,mm,sm,Nm,yn,Hn,Wn,io,no],[lr,Mq,Nq,wi,Mq,Nq,new R(null,1,5,T,[or],null),jr,Mq,ir,Mq,mr,mr,Mq,Nq,new Qq(new R(null,1,5,T,[or],null),null,null,null),Pq,Nq]),null])),function(a){return rr(wg.c(Ef,a))})));var sr=new R(null,2,5,T,[Yq(Nq,pe(en,new r(null,1,[Mj,fn],null))),Yq(lr,pe(Dk,new r(null,1,[Mj,Gn],null)))],null),tr;tr=function(a,b){return new R(null,2,5,T,[a,b],null)};Eq(tr,dr(nr,new R(null,1,5,T,[sr],null)));\nvar ur=new R(null,1,5,T,[Yq(Iq,pe(Dk,new r(null,1,[Mj,Ij],null)))],null),vr;vr=function(a){return tr(32,a)};Eq(vr,dr(nr,new R(null,1,5,T,[ur],null)));var wr=new R(null,1,5,T,[Yq(Iq,pe(Am,new r(null,1,[Mj,Ij],null)))],null),xr=new R(null,2,5,T,[Yq(Iq,pe(Am,new r(null,1,[Mj,Ij],null))),Yq(Iq,pe(Dk,new r(null,1,[Mj,Ij],null)))],null);\ngr=function yr(a){switch(arguments.length){case 1:return yr.h(arguments[0]);case 2:return yr.c(arguments[0],arguments[1]);default:throw Error([\"Invalid arity: \",v.h(arguments.length)].join(\"\"));}};gr.h=function(a){return gr.c(a,Ef)};gr.c=function(a,b){return Wg(qg(a,vr(b)))};gr.L=2;Eq(gr,fr(or,new R(null,2,5,T,[wr,xr],null)));\nvar zr=new R(null,1,5,T,[or],null),Ar=new R(null,2,5,T,[Yq(Iq,pe(Am,new r(null,1,[Mj,Ij],null))),Yq(Iq,pe(lk,new r(null,1,[Mj,Ij],null)))],null),Br=new R(null,3,5,T,[Yq(Iq,pe(Am,new r(null,1,[Mj,Ij],null))),Yq(Iq,pe(lk,new r(null,1,[Mj,Ij],null))),Yq(Iq,pe(Dk,new r(null,1,[Mj,Ij],null)))],null);\nhr=function Cr(a){switch(arguments.length){case 2:return Cr.c(arguments[0],arguments[1]);case 3:return Cr.l(arguments[0],arguments[1],arguments[2]);default:throw Error([\"Invalid arity: \",v.h(arguments.length)].join(\"\"));}};hr.c=function(a,b){return hr.l(a,b,Ef)};hr.l=function(a,b,c){a=gr.c(a,c);return Wg(qg(b,a))};hr.L=3;Eq(hr,fr(zr,new R(null,2,5,T,[Ar,Br],null)));var Dr=new R(null,1,5,T,[Yq(Iq,pe(Am,new r(null,1,[Mj,Ij],null)))],null),Er;Er=function(a){return P(zi,Fi(8,a,8))};\nEq(Er,dr(wi,new R(null,1,5,T,[Dr],null)));\nvar Fr=new r(null,3,[zn,0,Aj,0,On,!0],null),Gr=new r(null,4,[pl,new r(null,2,[zn,0,Aj,0],null),Oj,Ef,yn,!1,Rj,!0],null),Hr=Pe([121,110,101,102,106,119,104,116,99,113,117,108,109,118,100,122,111,103,125,107,97,115,112,123,120,126,98,124,96,105,114],[8804,9532,9226,176,9496,9516,9252,9500,9228,9472,9508,9484,9492,9524,9229,8805,9146,177,163,9488,9618,9149,9147,960,9474,8901,9225,8800,9830,9227,9148]),Ir=new R(null,2,5,T,[Yq(Nq,pe(Am,new r(null,1,[Mj,oo],null))),Yq(Nq,pe(lk,new r(null,1,[Mj,oo],null)))],\nnull),Jr;Jr=function(a,b){return rr(Pe([Oj,Rj,Yj,tk,vk,fl,il,pl,$l,im,mm,sm,Nm,yn,Hn,Wn,io,no],[Ef,!0,b-1,Er(a),!1,a,hr.c(a,b),Fr,!1,Ve,!1,Gr,Gr,!1,0,null,fk,b]))};Eq(Jr,dr(qr,new R(null,1,5,T,[Ir],null)));function Kr(a){return K.l(a,$l,!0)}function Lr(a){return K.l(a,$l,!1)}function Mr(a){return K.l(a,mm,!0)}function Nr(a){return K.l(a,mm,!1)}function Or(a){return K.l(a,Rj,!0)}function Pr(a){return K.l(a,Rj,!1)}function Qr(a,b,c){return zg(a,new R(null,2,5,T,[Oj,b],null),c)}\nfunction Rr(a,b){return Cg(a,Oj,le,b)}function Sr(a,b,c){var d=H(a);b=b<d?b:d;return O.c(kg(b,a),qg(b,c))}var Tr=function Tr(a){switch(arguments.length){case 1:return Tr.h(arguments[0]);case 2:return Tr.c(arguments[0],arguments[1]);default:throw Error([\"Invalid arity: \",v.h(arguments.length)].join(\"\"));}};Tr.h=function(a){return Tr.c(a,1)};\nTr.c=function(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,fl),e=D.c(c,Hn),f=D.c(c,Yj),h=D.c(c,Oj),k=gr.c(d,h);return Bg(c,il,function(a,c,d,e,f,h,k){return function(c){return Wg(O.A(jg(h,c),Sr(Zg(null,c,h,k+1,null),b,a),be([kg(k+1,c)])))}}(k,a,c,c,d,e,f,h))};Tr.L=2;function Ur(a,b,c){var d=H(a);b=b<d?b:d;return O.c(qg(b,c),jg(d-b,a))}\nvar Vr=function Vr(a){switch(arguments.length){case 1:return Vr.h(arguments[0]);case 2:return Vr.c(arguments[0],arguments[1]);default:throw Error([\"Invalid arity: \",v.h(arguments.length)].join(\"\"));}};Vr.h=function(a){return Vr.c(a,1)};\nVr.c=function(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,fl),e=D.c(c,Hn),f=D.c(c,Yj),h=D.c(c,Oj),k=gr.c(d,h);return Bg(c,il,function(a,c,d,e,f,h,k){return function(c){return Wg(O.A(jg(h,c),Ur(Zg(null,c,h,k+1,null),b,a),be([kg(k+1,c)])))}}(k,a,c,c,d,e,f,h))};Vr.L=2;function Wr(a){return zg(a,new R(null,2,5,T,[pl,On],null),!0)}function Xr(a){return zg(a,new R(null,2,5,T,[pl,On],null),!1)}function Yr(a,b){return K.l(zg(a,new R(null,2,5,T,[pl,zn],null),b),vk,!1)}\nfunction Zr(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,fl),e=0<b?b:0;--d;return Yr(c,e<d?e:d)}function $r(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl);d=null!=d&&(d.m&64||q===d.G)?P(U,d):d;d=D.c(d,zn);var e=D.c(c,fl)-1;return K.l(zg(zg(c,new R(null,2,5,T,[pl,zn],null),d<e?d:e),new R(null,2,5,T,[pl,Aj],null),b),vk,!1)}function as(a){var b=null!=a&&(a.m&64||q===a.G)?P(U,a):a;a=D.c(b,yn);b=D.c(b,Hn);return t(a)?b:0}\nfunction bs(a,b){var c=as(a),d=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var e=D.c(d,yn);var f=D.c(d,Yj);d=D.c(d,no);e=t(e)?f:d-1;f=c+b;c=f>c?f:c;return $r(a,e<c?e:c)}function cs(a){return $r(Yr(a,0),as(a))}function Eg(a,b,c){return bs(Zr(a,b),c)}function ds(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,pl);b=null!=b&&(b.m&64||q===b.G)?P(U,b):b;b=D.c(b,Aj);var c=D.c(a,Yj),d=D.c(a,no)-1;return G.c(b,c)?Tr.h(a):b<d?$r(a,b+1):a}\nfunction es(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,pl);b=null!=b&&(b.m&64||q===b.G)?P(U,b):b;b=D.c(b,zn);return Zr(a,b-1)}function fs(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl);d=null!=d&&(d.m&64||q===d.G)?P(U,d):d;var e=D.c(d,Aj),f=D.c(c,Hn);return $r(c,e<f?function(){var a=e-b;return 0>a?0:a}():function(){var a=e-b;return f>a?f:a}())}\nfunction gs(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl);d=null!=d&&(d.m&64||q===d.G)?P(U,d):d;var e=D.c(d,Aj),f=D.c(c,Yj),h=D.c(c,no);return $r(c,e>f?function(){var a=h-1,c=e+b;return a<c?a:c}():function(){var a=e+b;return f<a?f:a}())}function hs(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl);d=null!=d&&(d.m&64||q===d.G)?P(U,d):d;d=D.c(d,zn);return Zr(c,d+b)}\nfunction is(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl);d=null!=d&&(d.m&64||q===d.G)?P(U,d):d;d=D.c(d,zn);return Zr(c,d-b)}function js(a){var b=null!=a&&(a.m&64||q===a.G)?P(U,a):a;a=D.c(b,mm);b=ds(b);return t(a)?Yr(b,0):b}function ks(a){return Yr(ds(a),0)}function ls(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,pl);b=null!=b&&(b.m&64||q===b.G)?P(U,b):b;b=D.c(b,Aj);var c=D.c(a,Hn);return G.c(b,c)?Vr.h(a):0<b?$r(a,b-1):a}\nfunction ms(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,pl),c=null!=b&&(b.m&64||q===b.G)?P(U,b):b;b=D.c(c,zn);c=D.c(c,Aj);var d=D.c(a,Oj),e=D.c(a,yn),f=D.c(a,Rj);return K.l(a,Nm,new r(null,4,[pl,new r(null,2,[zn,b,Aj,c],null),Oj,d,yn,e,Rj,f],null))}function ns(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,Nm),c=null!=b&&(b.m&64||q===b.G)?P(U,b):b;b=D.c(c,pl);var d=D.c(c,Oj),e=D.c(c,yn);c=D.c(c,Rj);return Cg(K.A(a,Oj,d,be([vk,!1,yn,e,Rj,c])),pl,hi,b)}\nfunction os(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,io),c=D.c(a,fl),d=D.c(a,no),e=D.c(a,Oj);return G.c(b,fk)?K.A(a,io,tl,be([Wn,il.h(a),sm,Nm.h(a),il,hr.l(c,d,e),Nm,sm.h(a)])):a}function dt(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,io);return G.c(b,tl)?K.A(a,io,fk,be([Wn,null,sm,Nm.h(a),il,Wn.h(a),Nm,sm.h(a)])):a}\nfunction et(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,pl);b=null!=b&&(b.m&64||q===b.G)?P(U,b):b;b=D.c(b,zn);var c=D.c(a,fl);return 0<b&&b<c?Cg(a,tk,ge,b):a}function ft(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,pl);b=null!=b&&(b.m&64||q===b.G)?P(U,b):b;b=D.c(b,zn);return Cg(a,tk,re,b)}function gt(a){return Bg(a,tk,ie)}\nfunction ht(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl),e=null!=d&&(d.m&64||q===d.G)?P(U,d):d,f=D.c(e,zn),h=D.c(c,tk),k=D.c(c,fl),l=b-1,p=k-1;d=J(ng(function(a,b,c,d,e,f,h,k){return function(a){return k>=a}}(l,p,a,c,c,d,e,f,h,k),h),l,p);return Zr(c,d)}\nfunction it(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl),e=null!=d&&(d.m&64||q===d.G)?P(U,d):d,f=D.c(e,zn),h=D.c(c,tk),k=D.c(c,fl),l=b-1;d=J(cf(Bi(function(a,b,c,d,e,f,h){return function(a){return h>a}}(l,a,c,c,d,e,f,h,k),h)),l,0);return Zr(c,d)}function jt(a){return K.l(a,im,Ve)}function kt(a){return K.l(a,im,Hr)}function lt(a,b,c){return K.l(a,b,c)}function mt(a,b,c){return Wg(O.A(jg(b,a),new R(null,1,5,T,[c],null),be([jg(H(a)-b-1,kg(b,a))])))}\nfunction nt(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl),e=null!=d&&(d.m&64||q===d.G)?P(U,d):d;d=D.c(e,zn);e=D.c(e,Aj);var f=D.c(c,fl);D.c(c,no);var h=D.c(c,Oj),k=D.c(c,Rj),l=D.c(c,$l),p=D.c(c,im);p=95<b&&127>b?p.h?p.h(b):p.call(null,b):b;h=tr(p,h);return G.c(f,d+1)?t(k)?K.l(Yr(zg(c,new R(null,3,5,T,[il,e,d],null),h),d+1),vk,!0):zg(c,new R(null,3,5,T,[il,e,d],null),h):Yr(Ag.Z(c,new R(null,2,5,T,[il,e],null),t(l)?mt:lt,d,h),d+1)}\nfunction ot(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,Rj),e=D.c(c,vk);t(t(d)?e:d)&&(c=null!=c&&(c.m&64||q===c.G)?P(U,c):c,d=D.c(c,pl),d=null!=d&&(d.m&64||q===d.G)?P(U,d):d,d=D.c(d,Aj),e=D.c(c,no),c=Yr(c,0),c=G.c(e,d+1)?Tr.h(c):$r(c,d+1));return c=nt(c,b)}function pt(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,fl),c=D.c(a,no);return K.l(a,il,Wg(qg(c,Wg(qg(b,new R(null,2,5,T,[69,Ef],null))))))}\nfunction qt(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,pl);b=null!=b&&(b.m&64||q===b.G)?P(U,b):b;b=D.c(b,Aj);var c=D.c(a,fl),d=D.c(a,Oj);return zg(a,new R(null,2,5,T,[il,b],null),gr.c(c,d))}function rt(a,b,c){return Wg(O.c(jg(b,a),qg(H(a)-b,vr(c))))}function st(a,b,c){return Wg(O.c(qg(b+1,vr(c)),kg(b+1,a)))}\nfunction tt(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,pl),c=null!=b&&(b.m&64||q===b.G)?P(U,b):b;b=D.c(c,zn);c=D.c(c,Aj);var d=D.c(a,fl),e=D.c(a,Oj);--d;return Ag.Z(a,new R(null,2,5,T,[il,c],null),rt,b<d?b:d,e)}function ut(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,pl),c=null!=b&&(b.m&64||q===b.G)?P(U,b):b;b=D.c(c,zn);c=D.c(c,Aj);var d=D.c(a,fl),e=D.c(a,Oj);--d;return Ag.Z(a,new R(null,2,5,T,[il,c],null),st,b<d?b:d,e)}\nfunction vt(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,fl),c=D.c(a,no),d=D.c(a,Oj);return K.l(a,il,hr.l(b,c,d))}function wt(a){var b=null!=a&&(a.m&64||q===a.G)?P(U,a):a,c=D.c(b,pl),d=null!=c&&(c.m&64||q===c.G)?P(U,c):c,e=D.c(d,zn),f=D.c(d,Aj),h=D.c(b,fl),k=D.c(b,no),l=D.c(b,Oj);return Bg(b,il,function(a,b,c,d,e,f,h,k,l,S){return function(a){var b=jg(h,a);a=rt(Vd(a,h),f,S);var c=qg(l-h-1,gr.c(k,S));return Wg(O.A(b,new R(null,1,5,T,[a],null),be([c])))}}(a,b,b,c,d,e,f,h,k,l))}\nfunction xt(a){var b=null!=a&&(a.m&64||q===a.G)?P(U,a):a,c=D.c(b,pl),d=null!=c&&(c.m&64||q===c.G)?P(U,c):c,e=D.c(d,zn),f=D.c(d,Aj),h=D.c(b,fl),k=D.c(b,no),l=D.c(b,Oj);return Bg(b,il,function(a,b,c,d,e,f,h,k,l,S,X){return function(b){var c=qg(k,gr.c(l,X)),d=st(Vd(b,k),a,X);return Wg(O.A(c,new R(null,1,5,T,[d],null),be([kg(k+1,b)])))}}(function(){var a=h-1;return e<a?e:a}(),a,b,b,c,d,e,f,h,k,l))}\nfunction yt(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl),e=null!=d&&(d.m&64||q===d.G)?P(U,d):d,f=D.c(e,zn),h=D.c(e,Aj),k=D.c(c,fl),l=D.c(c,Oj);return Ag.l(c,new R(null,2,5,T,[il,h],null),function(a,b,c,d,e,f,h,k,l,S){return function(b){return Wg(O.A(jg(h,b),qg(a,vr(S)),be([kg(h+a,b)])))}}(function(){var a=k-f;return b<a?b:a}(),a,c,c,d,e,f,h,k,l))}\nfunction zt(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl),e=null!=d&&(d.m&64||q===d.G)?P(U,d):d,f=D.c(e,zn),h=D.c(e,Aj),k=D.c(c,fl),l=D.c(c,Oj);return Ag.l(c,new R(null,2,5,T,[il,h],null),function(a,c,d,e,f,h,k,l,M){return function(a){return Wg(jg(l,O.A(jg(h,a),qg(b,new R(null,2,5,T,[32,M],null)),be([kg(h,a)]))))}}(a,c,c,d,e,f,h,k,l))}\nfunction At(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl),e=null!=d&&(d.m&64||q===d.G)?P(U,d):d,f=D.c(e,Aj),h=D.c(c,Yj),k=D.c(c,fl),l=D.c(c,no),p=D.c(c,Oj),m=gr.c(k,p);return Bg(c,il,function(a,c,d,e,f,h,k,m){return function(c){return Wg(k<=m?O.A(jg(k,c),Ur(Zg(null,c,k,m+1,null),b,a),be([kg(m+1,c)])):O.c(jg(k,c),Ur(kg(k,c),b,a)))}}(m,a,c,c,d,e,f,h,k,l,p))}\nfunction Bt(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl),e=null!=d&&(d.m&64||q===d.G)?P(U,d):d,f=D.c(e,Aj),h=D.c(c,Yj),k=D.c(c,fl),l=D.c(c,no),p=D.c(c,Oj),m=gr.c(k,p);return Bg(c,il,function(a,c,d,e,f,h,k,m){return function(c){return Wg(k<=m?O.A(jg(k,c),Sr(Zg(null,c,k,m+1,null),b,a),be([kg(m+1,c)])):O.c(jg(k,c),Sr(kg(k,c),b,a)))}}(m,a,c,c,d,e,f,h,k,l,p))}\nfunction Ct(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl),e=null!=d&&(d.m&64||q===d.G)?P(U,d):d,f=D.c(e,zn),h=D.c(e,Aj),k=D.c(c,fl),l=D.c(c,Oj),p=f>=k?Zr(c,k-1):c,m=Mb(D,p,new R(null,2,5,T,[pl,zn],null));return Ag.l(p,new R(null,2,5,T,[il,h],null),function(a,b,c,d,e,f,h,k,m,l,p,Q){return function(a){return Wg(O.A(jg(b,a),kg(b+c,a),be([qg(c,vr(Q))])))}}(p,m,function(){var a=k-m;return b<a?b:a}(),a,c,c,d,e,f,h,k,l))}\nvar Dt=new R(null,1,5,T,[Yq(new R(null,1,5,T,[Nq],null),pe(Ln,new r(null,1,[Mj,new R(null,1,5,T,[fn],null)],null)))],null),Et;Et=function(a){return P(String.fromCodePoint,a)};Eq(Et,dr(Lq,new R(null,1,5,T,[Dt],null)));var Ft=new R(null,1,5,T,[new R(null,2,5,T,[Yq(Lq,\"text\"),Yq(lr,\"text attributes\")],null)],null),Gt=new R(null,1,5,T,[Yq(or,pe(Nn,new r(null,1,[Mj,nk],null)))],null),Ht;\nHt=function(a){a=E(a);var b=y(a),c=z(a);a=he;var d=new R(null,1,5,T,[y(b)],null),e=fe(b);for(b=c;;)if(c=y(b),t(c)){var f=c;c=J(f,0,null);f=J(f,1,null);G.c(f,e)?d=ge.c(d,c):(a=ge.c(a,new R(null,2,5,T,[Et(d),e],null)),d=new R(null,1,5,T,[c],null),e=f);b=vd(b)}else return ge.c(a,new R(null,2,5,T,[Et(d),e],null))};Eq(Ht,dr(Ft,new R(null,1,5,T,[Gt],null)));\nfunction It(a){a=Wr(a);a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,no);a=K.A(a,Hn,0,be([Yj,b-1]));return K.l(K.l(K.l(Lr(a),yn,!1),Oj,Ef),Nm,Gr)};var Jt=Error();var Kt=E(ug(function(a){return Sq(a)},lh(null)));if(!wb(Kt))throw Error(Bq(\"extra-key-schema? can not contain required keys: %s\",be([Wg(Kt)])));function Lt(a,b,c,d,e,f,h){this.Qb=a;this.Pb=b;this.Ob=c;this.screen=d;this.v=e;this.j=f;this.w=h;this.m=2229667594;this.J=139264}g=Lt.prototype;g.V=function(a,b){return this.I(null,b,null)};\ng.I=function(a,b,c){switch(b instanceof L?b.ea:null){case \"parser-state\":return this.Qb;case \"parser-params\":return this.Pb;case \"parser-intermediates\":return this.Ob;case \"screen\":return this.screen;default:return D.l(this.j,b,c)}};\ng.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#asciinema.vt.VT{\",\", \",\"}\",c,O.c(new R(null,4,5,T,[new R(null,2,5,T,[Tl,this.Qb],null),new R(null,2,5,T,[kk,this.Pb],null),new R(null,2,5,T,[rk,this.Ob],null),new R(null,2,5,T,[V,this.screen],null)],null),this.j))};g.ba=function(){return new fh(0,this,4,new R(null,4,5,T,[Tl,kk,rk,V],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 4+H(this.j)};\ng.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-156373259^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.Qb,b.Qb)&&G.c(this.Pb,b.Pb)&&G.c(this.Ob,b.Ob)&&G.c(this.screen,b.screen)&&G.c(this.j,b.j)};\ng.ga=function(a,b){return He(new ti(null,new r(null,4,[V,null,kk,null,rk,null,Tl,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new Lt(this.Qb,this.Pb,this.Ob,this.screen,this.v,Bf(le.c(this.j,b)),null)};\ng.O=function(a,b,c){return t(N.c?N.c(Tl,b):N.call(null,Tl,b))?new Lt(c,this.Pb,this.Ob,this.screen,this.v,this.j,null):t(N.c?N.c(kk,b):N.call(null,kk,b))?new Lt(this.Qb,c,this.Ob,this.screen,this.v,this.j,null):t(N.c?N.c(rk,b):N.call(null,rk,b))?new Lt(this.Qb,this.Pb,c,this.screen,this.v,this.j,null):t(N.c?N.c(V,b):N.call(null,V,b))?new Lt(this.Qb,this.Pb,this.Ob,c,this.v,this.j,null):new Lt(this.Qb,this.Pb,this.Ob,this.screen,this.v,K.l(this.j,b,c),null)};\ng.S=function(){return E(O.c(new R(null,4,5,T,[new R(null,2,5,T,[Tl,this.Qb],null),new R(null,2,5,T,[kk,this.Pb],null),new R(null,2,5,T,[rk,this.Ob],null),new R(null,2,5,T,[V,this.screen],null)],null),this.j))};g.T=function(a,b){return new Lt(this.Qb,this.Pb,this.Ob,this.screen,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function Mt(a){return new Lt(Tl.h(a),kk.h(a),rk.h(a),V.h(a),null,Bf(le.A(a,Tl,be([kk,rk,V]))),null)}\nEq(Lt,zq(ar(Lt,hi.A(be([new r(null,4,[Tl,Pq,kk,new R(null,1,5,T,[Oq],null),rk,new R(null,1,5,T,[Oq],null),V,qr],null),null])),function(a){return Mt(wg.c(Ef,a))})));var Nt=new R(null,2,5,T,[Yq(Nq,pe(Am,new r(null,1,[Mj,oo],null))),Yq(Nq,pe(lk,new r(null,1,[Mj,oo],null)))],null),Ot;Ot=function(a,b){return Mt(new r(null,4,[Tl,uk,kk,he,rk,he,V,Jr(a,b)],null))};Eq(Ot,dr(Lt,new R(null,1,5,T,[Nt],null)));\nfunction Pt(a,b,c){try{if(null===b)try{if(4===c)return Bg(a,V,Kr);throw Jt;}catch(p){if(p instanceof Error){var d=p;if(d===Jt)try{if(20===c)return Bg(a,V,Mr);throw Jt;}catch(m){if(m instanceof Error){var e=m;if(e===Jt)throw Jt;throw e;}throw m;}else throw d;}else throw p;}else throw Jt;}catch(p){if(p instanceof Error)if(d=p,d===Jt)try{if(63===b)try{if(6===c)return Bg(a,V,function(){return function(a){return cs(K.l(a,yn,!0))}}(d));throw Jt;}catch(m){if(m instanceof Error)if(e=m,e===Jt)try{if(7===c)return Bg(a,\nV,Or);throw Jt;}catch(u){if(u instanceof Error)if(b=u,b===Jt)try{if(25===c)return Bg(a,V,Wr);throw Jt;}catch(w){if(w instanceof Error){var f=w;if(f===Jt)try{if(47===c)return Bg(a,V,os);throw Jt;}catch(x){if(x instanceof Error){var h=x;if(h===Jt)try{if(1047===c)return Bg(a,V,os);throw Jt;}catch(C){if(C instanceof Error){var k=C;if(k===Jt)try{if(1048===c)return Bg(a,V,ms);throw Jt;}catch(F){if(F instanceof Error){var l=F;if(l===Jt)try{if(1049===c)return Bg(a,V,function(){return function(a){return os(ms(a))}}(l,\nk,h,f,b,e,d));throw Jt;}catch(I){if(I instanceof Error){c=I;if(c===Jt)throw Jt;throw c;}throw I;}else throw l;}else throw F;}else throw k;}else throw C;}else throw h;}else throw x;}else throw f;}else throw w;}else throw b;else throw u;}else throw e;else throw m;}else throw Jt;}catch(m){if(m instanceof Error){e=m;if(e===Jt)return a;throw e;}throw m;}else throw d;else throw p;}}\nfunction Qt(a,b,c){try{if(null===b)try{if(4===c)return Bg(a,V,Lr);throw Jt;}catch(p){if(p instanceof Error){var d=p;if(d===Jt)try{if(20===c)return Bg(a,V,Nr);throw Jt;}catch(m){if(m instanceof Error){var e=m;if(e===Jt)throw Jt;throw e;}throw m;}else throw d;}else throw p;}else throw Jt;}catch(p){if(p instanceof Error)if(d=p,d===Jt)try{if(63===b)try{if(6===c)return Bg(a,V,function(){return function(a){return cs(K.l(a,yn,!1))}}(d));throw Jt;}catch(m){if(m instanceof Error)if(e=m,e===Jt)try{if(7===c)return Bg(a,\nV,Pr);throw Jt;}catch(u){if(u instanceof Error)if(b=u,b===Jt)try{if(25===c)return Bg(a,V,Xr);throw Jt;}catch(w){if(w instanceof Error){var f=w;if(f===Jt)try{if(47===c)return Bg(a,V,dt);throw Jt;}catch(x){if(x instanceof Error){var h=x;if(h===Jt)try{if(1047===c)return Bg(a,V,dt);throw Jt;}catch(C){if(C instanceof Error){var k=C;if(k===Jt)try{if(1048===c)return Bg(a,V,ns);throw Jt;}catch(F){if(F instanceof Error){var l=F;if(l===Jt)try{if(1049===c)return Bg(a,V,function(){return function(a){return ns(dt(a))}}(l,\nk,h,f,b,e,d));throw Jt;}catch(I){if(I instanceof Error){c=I;if(c===Jt)throw Jt;throw c;}throw I;}else throw l;}else throw F;}else throw k;}else throw C;}else throw h;}else throw x;}else throw f;}else throw w;}else throw b;else throw u;}else throw e;else throw m;}else throw Jt;}catch(m){if(m instanceof Error){e=m;if(e===Jt)return a;throw e;}throw m;}else throw d;else throw p;}}\nfunction Rt(a){a=ig.c(function(a){return a-48},a);a=ig.l(Ye,cf(a),rg(function(){return function(a){return 10*a}}(a),1));return Mb(Xe,0,a)}var St=hj(function(a){a:for(var b=he,c=he;;){var d=y(a);if(t(d))G.c(d,59)?(a=vd(a),b=ge.c(b,c),c=he):(a=vd(a),c=ge.c(c,d));else{a=E(c)?ge.c(b,c):b;break a}}return ig.c(Rt,a)});function Tt(a){a=kk.h(a);return St.h?St.h(a):St.call(null,a)}function Ut(a,b,c){a=J(Tt(a),b,0);return 0===a?c:a}function Vt(a){return Bg(a,V,es)}function Wt(a){return Cg(a,V,ht,1)}\nfunction Xt(a){return Cg(a,V,Yr,0)}function Yt(a){return Bg(a,V,js)}function Zt(a){return Bg(a,V,kt)}function $t(a){return Bg(a,V,jt)}function au(a){return Bg(a,V,ks)}function bu(a){return Bg(a,V,et)}function cu(a){return Bg(a,V,ls)}function du(a){return Ot(fl.h(V.h(a)),no.h(V.h(a)))}function eu(a){var b=Ut(a,0,1);return Cg(a,V,zt,b)}function fu(a){var b=Ut(a,0,1);return Cg(a,V,fs,b)}function gu(a){var b=Ut(a,0,1);return Cg(a,V,gs,b)}function hu(a){var b=Ut(a,0,1);return Cg(a,V,hs,b)}\nfunction iu(a){var b=Ut(a,0,1);return Cg(a,V,is,b)}function ju(a){var b=Ut(a,0,1);return Bg(a,V,function(a){return function(b){return Yr(gs(b,a),0)}}(b))}function ku(a){var b=Ut(a,0,1);return Bg(a,V,function(a){return function(b){return Yr(fs(b,a),0)}}(b))}function lu(a){var b=Ut(a,0,1)-1;return Cg(a,V,Zr,b)}function mu(a){var b=Ut(a,0,1)-1,c=Ut(a,1,1)-1;return Dg(a,c,b)}function nu(a){var b=Ut(a,0,1);return Cg(a,V,ht,b)}\nfunction ou(a){var b=Ut(a,0,0);return Bg(a,V,function(){switch(b){case 0:return wt;case 1:return xt;case 2:return vt;default:return Ve}}())}function pu(a){var b=Ut(a,0,0);return Bg(a,V,function(){switch(b){case 0:return tt;case 1:return ut;case 2:return qt;default:return Ve}}())}function qu(a){var b=Ut(a,0,1);return Cg(a,V,Tr,b)}function ru(a){var b=Ut(a,0,1);return Cg(a,V,Vr,b)}function su(a){var b=Ut(a,0,1);return Cg(a,V,At,b)}function tu(a){var b=Ut(a,0,1);return Cg(a,V,Bt,b)}\nfunction uu(a){var b=Ut(a,0,1);return Cg(a,V,Ct,b)}function vu(a){switch(Ut(a,0,0)){case 0:return Bg(a,V,et);case 2:return Bg(a,V,ft);case 5:return Bg(a,V,gt);default:return a}}function wu(a){var b=Ut(a,0,1);return Cg(a,V,yt,b)}function xu(a){var b=Ut(a,0,1);return Cg(a,V,it,b)}function yu(a){switch(Ut(a,0,0)){case 0:return Bg(a,V,ft);case 3:return Bg(a,V,gt);default:return a}}function zu(a){var b=D.c(rk.h(a),0);return Mb(function(a){return function(b,c){return Pt(b,a,c)}}(b),a,Tt(a))}\nfunction Au(a){var b=D.c(rk.h(a),0);return Mb(function(a){return function(b,c){return Qt(b,a,c)}}(b),a,Tt(a))}\nfunction Bu(a,b){for(var c=a,d=b;;)if(E(d)){var e=y(d);switch(e){case 0:c=K.l(c,Oj,Ef);d=vd(d);continue;case 1:c=Qr(c,Kj,!0);d=vd(d);continue;case 3:c=Qr(c,Yn,!0);d=vd(d);continue;case 4:c=Qr(c,Vl,!0);d=vd(d);continue;case 5:c=Qr(c,dk,!0);d=vd(d);continue;case 7:c=Qr(c,Nk,!0);d=vd(d);continue;case 21:c=Rr(c,Kj);d=vd(d);continue;case 22:c=Rr(c,Kj);d=vd(d);continue;case 23:c=Rr(c,Yn);d=vd(d);continue;case 24:c=Rr(c,Vl);d=vd(d);continue;case 25:c=Rr(c,dk);d=vd(d);continue;case 27:c=Rr(c,Nk);d=vd(d);\ncontinue;case 30:case 31:case 32:case 33:case 34:case 35:case 36:case 37:c=Qr(c,Ok,e-30);d=vd(d);continue;case 38:switch(ee(d)){case 2:var f=jg(3,kg(2,d));e=J(f,0,null);var h=J(f,1,null);f=J(f,2,null);t(f)?(c=Qr(c,Ok,new R(null,3,5,T,[e,h,f],null)),d=kg(5,d)):d=kg(2,d);continue;case 5:e=y(kg(2,d));t(e)?(c=Qr(c,Ok,e),d=kg(3,d)):d=kg(2,d);continue;default:d=vd(d);continue}case 39:c=Rr(c,Ok);d=vd(d);continue;case 40:case 41:case 42:case 43:case 44:case 45:case 46:case 47:c=Qr(c,Tn,e-40);d=vd(d);continue;\ncase 48:switch(ee(d)){case 2:f=jg(3,kg(2,d));e=J(f,0,null);h=J(f,1,null);f=J(f,2,null);t(f)?(c=Qr(c,Tn,new R(null,3,5,T,[e,h,f],null)),d=kg(5,d)):d=kg(2,d);continue;case 5:e=y(kg(2,d));t(e)?(c=Qr(c,Tn,e),d=kg(3,d)):d=kg(2,d);continue;default:d=vd(d);continue}case 49:c=Rr(c,Tn);d=vd(d);continue;case 90:case 91:case 92:case 93:case 94:case 95:case 96:case 97:c=Qr(c,Ok,e-82);d=vd(d);continue;case 100:case 101:case 102:case 103:case 104:case 105:case 106:case 107:c=Qr(c,Tn,e-92);d=vd(d);continue;default:d=\nvd(d)}}else return c}function Cu(a){var b=E(Tt(a));return Cg(a,V,Bu,b?b:new R(null,1,5,T,[0],null))}function Du(a){var b=Ut(a,0,1)-1;return Cg(a,V,bs,b)}function Eu(a){return G.c(D.c(rk.h(a),0),33)?Bg(a,V,It):a}function Fu(a){var b=Ut(a,0,1)-1,c=function(){var b=null==a?null:Ut(a,1,null);return null==b?null:b-1}();return Bg(a,V,function(a,b){return function(c){c=null!=c&&(c.m&64||q===c.G)?P(U,c):c;var d=D.c(c,no),e=t(b)?b:d-1;c=-1<a&&a<e&&e<d?K.A(c,Hn,a,be([Yj,e])):c;return cs(c)}}(b,c))}\nfunction Gu(a,b){var c=function(){switch(b){case 8:return Vt;case 9:return Wt;case 10:return Yt;case 11:return Yt;case 12:return Yt;case 13:return Xt;case 14:return Zt;case 15:return $t;case 132:return Yt;case 133:return au;case 136:return bu;case 141:return cu;default:return null}}();return t(c)?c.h?c.h(a):c.call(null,a):a}\nvar Hu=Pe([zj,Pj,Wj,ak,xl,Pl,Rl,Ul,Xl,um,Fm,Fn,In,po],[function(a){return a},function(a,b){var c=D.c(rk.h(a),0);try{if(null===c)try{if(t(function(){return function(){return function(a){return 64<=a&&95>=a}}(c,b)(b)}()))return Gu(a,b+64);throw Jt;}catch(h){if(h instanceof Error){var d=h;if(d===Jt)try{if(55===b)return Bg(a,V,ms);throw Jt;}catch(k){if(k instanceof Error){var e=k;if(e===Jt)try{if(56===b)return Bg(a,V,ns);throw Jt;}catch(l){if(l instanceof Error){var f=l;if(f===Jt)try{if(99===b)return du(a);\nthrow Jt;}catch(p){if(p instanceof Error){d=p;if(d===Jt)throw Jt;throw d;}throw p;}else throw f;}else throw l;}else throw e;}else throw k;}else throw d;}else throw h;}else throw Jt;}catch(h){if(h instanceof Error)if(d=h,d===Jt)try{if(35===c)try{if(56===b)return Bg(a,V,pt);throw Jt;}catch(k){if(k instanceof Error){e=k;if(e===Jt)throw Jt;throw e;}throw k;}else throw Jt;}catch(k){if(k instanceof Error)if(e=k,e===Jt)try{if(40===c)try{if(48===b)return Zt(a);throw Jt;}catch(l){if(l instanceof Error){f=\nl;if(f===Jt)return $t(a);throw f;}throw l;}else throw Jt;}catch(l){if(l instanceof Error){f=l;if(f===Jt)return a;throw f;}throw l;}else throw e;else throw k;}else throw d;else throw h;}},function(a){return a},function(a){return a},Gu,function(a,b){return Cg(a,V,ot,b)},function(a,b){var c=function(){switch(b){case 64:return eu;case 65:return fu;case 66:return gu;case 67:return hu;case 68:return iu;case 69:return ju;case 70:return ku;case 71:return lu;case 72:return mu;case 73:return nu;case 74:return ou;\ncase 75:return pu;case 76:return su;case 77:return tu;case 80:return uu;case 83:return qu;case 84:return ru;case 87:return vu;case 88:return wu;case 90:return xu;case 96:return lu;case 97:return hu;case 100:return Du;case 101:return fu;case 102:return mu;case 103:return yu;case 104:return zu;case 108:return Au;case 109:return Cu;case 112:return Eu;case 114:return Fu;default:return null}}();return t(c)?c.h?c.h(a):c.call(null,a):a},function(a){return a},function(a,b){return K.l(a,kk,ge.c(kk.h(a),b))},\nfunction(a){return a},function(a,b){return K.l(a,rk,ge.c(rk.h(a),b))},function(a){return a},function(a){return a},function(a){return K.A(a,rk,he,be([kk,he]))}]);function Iu(a,b){for(var c=a,d=Tl.h(c),e=b;;){var f=y(e);if(t(f)){var h=160<=f?65:f;h=D.c(d.h?d.h(xq):d.call(null,xq),h);d=J(h,0,null);h=J(h,1,null);a:for(;;)if(E(h)){var k=y(h);k=Hu.h?Hu.h(k):Hu.call(null,k);c=k.c?k.c(c,f):k.call(null,c,f);h=z(h)}else break a;e=vd(e)}else return K.l(c,Tl,d)}}\nfunction Ju(a,b){var c=xg(function(a){return a.codePointAt(0)},b);return Iu(a,c)}\nfunction Ku(a,b){try{if(ze(b)&&3===H(b)){var c=Vd(b,0),d=Vd(b,1),e=Vd(b,2);return[v.h(a+8),\";2;\",v.h(c),\";\",v.h(d),\";\",v.h(e)].join(\"\")}throw Jt;}catch(k){if(k instanceof Error){var f=k;if(f===Jt)try{if(t(function(){return function(){return function(a){return 8>a}}(f)(b)}()))return\"\"+v.h(a+b);throw Jt;}catch(l){if(l instanceof Error){var h=l;if(h===Jt)try{if(t(function(){return function(){return function(a){return 16>a}}(h,f)(b)}()))return\"\"+v.h(a+52+b);throw Jt;}catch(p){if(p instanceof Error){c=\np;if(c===Jt)return[v.h(a+8),\";5;\",v.h(b)].join(\"\");throw c;}throw p;}else throw h;}else throw l;}else throw f;}else throw k;}}ag.c(Ku,30);ag.c(Ku,40);var Lu=function Lu(a){if(null!=a&&null!=a.yd)return a.yd(a);var c=Lu[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Lu._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"Screen.lines\",a);},Mu=function Mu(a){if(null!=a&&null!=a.xd)return a.xd(a);var c=Mu[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Mu._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"Screen.cursor\",a);};function Nu(a,b){var c=0<a?a:0;return b<c?b:c}function Ou(a){return function(b){return function(){return((new Date).getTime()-b.getTime())/1E3*a}}(new Date)}function Pu(a){return document[a]}\nfunction Qu(a){return function(b){var c=new hg(null);bd(c,c);return function(c){return function(){function d(d,e){if(B(c)===c){var f=bd(c,e);return b.c?b.c(d,f):b.call(null,d,f)}var h=bd(c,function(){var b=B(c);return a.c?a.c(b,e):a.call(null,b,e)}());return Hd(h)?Id(function(){var a=B(h);return b.c?b.c(d,a):b.call(null,d,a)}()):b.c?b.c(d,h):b.call(null,d,h)}function f(a){return B(c)===c?a:b.h?b.h(a):b.call(null,a)}function h(){return b.B?b.B():b.call(null)}var k=null;k=function(a,b){switch(arguments.length){case 0:return h.call(this);\ncase 1:return f.call(this,a);case 2:return d.call(this,a,b)}throw Error(\"Invalid arity: \"+(arguments.length-1));};k.B=h;k.h=f;k.c=d;return k}()}(c)}}\nfunction Ru(a,b){return function(c){var d=new hg(null);bd(d,d);return function(d){return function(){function e(e,f){for(;;)if(B(d)===d){var h=function(){var a=e,f=bd(d,b);return c.c?c.c(a,f):c.call(null,a,f)}();if(Hd(h))return h;var k=f;e=h;f=k}else{var m=bd(d,function(){var b=B(d),c=f;return a.c?a.c(b,c):a.call(null,b,c)}());return Hd(m)?Id(function(){var a=e,b=B(m);return c.c?c.c(a,b):c.call(null,a,b)}()):c.c?c.c(e,m):c.call(null,e,m)}}function h(a){B(d)===d&&(a=Jd(c.c?c.c(a,b):c.call(null,a,b)));\nreturn c.h?c.h(a):c.call(null,a)}function k(){return c.B?c.B():c.call(null)}var l=null;l=function(a,b){switch(arguments.length){case 0:return k.call(this);case 1:return h.call(this,a);case 2:return e.call(this,a,b)}throw Error(\"Invalid arity: \"+(arguments.length-1));};l.B=k;l.h=h;l.c=e;return l}()}(d)}};function Su(a,b){return ig.c(function(b){var c=J(b,0,null);b=J(b,1,null);return new R(null,2,5,T,[c,a.h?a.h(b):a.call(null,b)],null)},b)}var Tu=function Tu(a,b){return new kf(null,function(){if(E(a)){if(E(b)){var d=y(a),e=J(d,0,null);J(d,1,null);var f=y(b),h=J(f,0,null);J(f,1,null);return e<h?ae(d,function(){var d=vd(a);return Tu.c?Tu.c(d,b):Tu.call(null,d,b)}()):ae(f,function(){var d=vd(b);return Tu.c?Tu.c(a,d):Tu.call(null,a,d)}())}return a}return null},null,null)};\nfunction Uu(a,b){var c=J(b,0,null),d=J(b,1,null);return new R(null,2,5,T,[c+a,d],null)}function Vu(a,b){var c=J(b,0,null),d=J(b,1,null);return new R(null,2,5,T,[c/a,d],null)}function Wu(a){return ig.h(function(b){var c=J(b,0,null),d=J(b,1,null);return t(a)?new R(null,2,5,T,[c<a?c:a,d],null):b})}function Xu(a,b){return y(b)<a}function Yu(a,b,c){return Uf($f.l(mg(ag.c(Xu,a)),ig.h(ag.c(Uu,-a)),ig.h(ag.c(Vu,b))),c)}function Zu(a,b){return y(b)<=a}function $u(a,b){return fe(Bi(ag.c(Zu,a),b))}\nfunction av(a,b){return Ru(function(b,d){J(b,0,null);var c=J(b,1,null),f=J(d,0,null),h=J(d,1,null);return new R(null,2,5,T,[f,a.c?a.c(c,h):a.call(null,c,h)],null)},new R(null,2,5,T,[0,b],null))}function bv(){return Qu(function(a,b){var c=J(a,0,null);J(a,1,null);var d=J(b,0,null),e=J(b,1,null);return new R(null,2,5,T,[c+d,e],null)})}\nfunction cv(){return function(a){return function(b){return function(){function c(c,d){var e=J(d,0,null),f=J(d,1,null),h=e-B(b);bd(b,e);e=new R(null,2,5,T,[h,f],null);return a.c?a.c(c,e):a.call(null,c,e)}function d(b){return a.h?a.h(b):a.call(null,b)}function e(){return a.B?a.B():a.call(null)}var f=null;f=function(a,b){switch(arguments.length){case 0:return e.call(this);case 1:return d.call(this,a);case 2:return c.call(this,a,b)}throw Error(\"Invalid arity: \"+(arguments.length-1));};f.B=e;f.h=d;f.c=\nc;return f}()}(new hg(0))}};function dv(a,b,c,d){return Uf($f.A(tg(function(a){return G.c(ee(a),\"o\")}),ig.h(Hi(function(a){return Vd(a,2)})),cv(),be([Wu(d),bv(),av(Ju,Ot(b,c))])),a)};function ev(a){var b=be([gj,!0]);if(null!=a?q===a.lf||(a.Tc?0:Ab(dj,a)):Ab(dj,a))return ej(a,P(ci,b));if(E(b)){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,gj);return function(a,b,c,d){return function m(e){return De(e)?Ii(ig.c(m,e)):ue(e)?wg.l(ie(e),ig.h(m),e):vb(e)?Qc(Mb(function(){return function(a,b){return uf.c(a,m(b))}}(a,b,c,d),Oc(he),e)):Bb(e)===Object?Qc(Mb(function(a,b,c,d){return function(a,b){var c=d.h?d.h(b):d.call(null,b),f=m(e[b]);return Rc(a,c,f)}}(a,b,c,d),Oc(Ef),Ea(e))):e}}(b,\nc,d,t(d)?hf:v)(a)}return null};function fv(a,b,c,d,e){this.cursor=a;this.lines=b;this.v=c;this.j=d;this.w=e;this.m=2229667594;this.J=139264}g=fv.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case \"cursor\":return this.cursor;case \"lines\":return this.lines;default:return D.l(this.j,b,c)}};\ng.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#asciinema.player.asciicast.v0.LegacyScreen{\",\", \",\"}\",c,O.c(new R(null,2,5,T,[new R(null,2,5,T,[pl,this.cursor],null),new R(null,2,5,T,[il,this.lines],null)],null),this.j))};g.ba=function(){return new fh(0,this,2,new R(null,2,5,T,[pl,il],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 2+H(this.j)};\ng.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 1528554851^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.cursor,b.cursor)&&G.c(this.lines,b.lines)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,2,[il,null,pl,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new fv(this.cursor,this.lines,this.v,Bf(le.c(this.j,b)),null)};\ng.O=function(a,b,c){return t(N.c?N.c(pl,b):N.call(null,pl,b))?new fv(c,this.lines,this.v,this.j,null):t(N.c?N.c(il,b):N.call(null,il,b))?new fv(this.cursor,c,this.v,this.j,null):new fv(this.cursor,this.lines,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,2,5,T,[new R(null,2,5,T,[pl,this.cursor],null),new R(null,2,5,T,[il,this.lines],null)],null),this.j))};g.T=function(a,b){return new fv(this.cursor,this.lines,b,this.j,this.w)};\ng.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function gv(a,b){return y(fe(Uf($f.c(Wu(b),bv()),a)))}function hv(a){return wg.c(Ef,ig.c(function(a){var b=J(a,0,null);a=J(a,1,null);var d=T;b=jf(b);return new R(null,2,5,d,[parseInt(b,10),a],null)},a))}function iv(a,b){var c=Bg(b,il,hv);return ii.A(hi,be([a,c]))}\nfunction jv(a,b){var c=new r(null,2,[il,di(),pl,new r(null,3,[zn,0,Aj,0,On,!0],null)],null);c=new fv(pl.h(c),il.h(c),null,Bf(le.A(c,pl,be([il]))),null);return Uf($f.l(Wu(b),bv(),av(iv,c)),a)}function kv(a,b){var c=il.h(fe(y(a))),d=Te(Xe,ig.c(function(){return function(a){return H(y(a))}}(c),y(mh(c))));c=H(c);return new r(null,5,[Mn,0,fl,d,no,c,wl,gv(a,b),Uk,jv(a,b)],null)}g.yd=function(){return Wg(mh(il.h(this)))};g.xd=function(){return pl.h(this)};function lv(a){return ev(JSON.parse(a))}function mv(a,b,c,d){if(G.c(Mn.h(a),1)){b=t(b)?b:fl.h(a);c=t(c)?c:no.h(a);var e=ko.h(a);a=y(fe(Uf($f.c(Wu(d),bv()),e)));d=Uf($f.l(Wu(d),bv(),av(Ju,Ot(b,c))),e);d=new r(null,5,[Mn,1,fl,b,no,c,wl,a,Uk,d],null)}else d=null;return d}\nfunction nv(a,b,c,d){var e=y(a);G.c(Mn.h(e),2)?(e=y(a),a=vd(a),b=t(b)?b:fl.h(e),c=t(c)?c:no.h(e),d=t(d)?d:Qj.h(e),e=y(fe(Uf($f.l(cv(),Wu(d),bv()),a))),d=new r(null,5,[Mn,2,fl,b,no,c,wl,e,Uk,dv(a,b,c,d)],null)):d=t(il.h(ee(e)))?kv(a,d):null;return d}function ov(a,b,c,d){try{var e=lv(a);return we(e)?nv(e,b,c,d):xe(e)?mv(e,b,c,d):null}catch(k){try{var f=Fo(ra(a),\"\\n\");var h=ig.c(lv,f);return nv(h,b,c,d)}catch(l){return null}}}\nfunction pv(a,b,c,d){var e=\"string\"===typeof a?ov:we(a)?nv:xe(a)?mv:null;a=t(e)?e.M?e.M(a,b,c,d):e.call(null,a,b,c,d):null;if(t(a))return a;throw\"only asciicast v1 and v2 formats can be opened\";}Lt.prototype.yd=function(){return xg(Ht,il.h(V.h(this)))};Lt.prototype.xd=function(){return pl.h(V.h(this))};var qv;a:{var rv=ba.navigator;if(rv){var sv=rv.userAgent;if(sv){qv=sv;break a}}qv=\"\"}function tv(a){return-1!=qv.indexOf(a)};var uv;\nfunction vv(){var a=ba.MessageChannel;\"undefined\"===typeof a&&\"undefined\"!==typeof window&&window.postMessage&&window.addEventListener&&!tv(\"Presto\")&&(a=function(){var a=document.createElement(\"IFRAME\");a.style.display=\"none\";a.src=\"\";document.documentElement.appendChild(a);var b=a.contentWindow;a=b.document;a.open();a.write(\"\");a.close();var c=\"callImmediate\"+Math.random(),d=\"file:\"==b.location.protocol?\"*\":b.location.protocol+\"//\"+b.location.host;a=pa(function(a){if((\"*\"==d||a.origin==d)&&a.data==\nc)this.port1.onmessage()},this);b.addEventListener(\"message\",a,!1);this.port1={};this.port2={postMessage:function(){b.postMessage(c,d)}}});if(\"undefined\"!==typeof a&&!tv(\"Trident\")&&!tv(\"MSIE\")){var b=new a,c={},d=c;b.port1.onmessage=function(){if(void 0!==c.next){c=c.next;var a=c.ed;c.ed=null;a()}};return function(a){d.next={ed:a};d=d.next;b.port2.postMessage(0)}}return\"undefined\"!==typeof document&&\"onreadystatechange\"in document.createElement(\"SCRIPT\")?function(a){var b=document.createElement(\"SCRIPT\");\nb.onreadystatechange=function(){b.onreadystatechange=null;b.parentNode.removeChild(b);b=null;a();a=null};document.documentElement.appendChild(b)}:function(a){ba.setTimeout(a,0)}};function wv(){0!=xv&&(yv[ja(this)]=this);this.od=this.od;this.Wd=this.Wd}var xv=0,yv={};wv.prototype.od=!1;wv.prototype.nd=function(){if(this.Wd)for(;this.Wd.length;)this.Wd.shift()()};function zv(){return tv(\"iPhone\")&&!tv(\"iPod\")&&!tv(\"iPad\")};var Av=tv(\"Opera\"),Bv=tv(\"Trident\")||tv(\"MSIE\"),Cv=tv(\"Edge\"),Dv=tv(\"Gecko\")&&!(-1!=qv.toLowerCase().indexOf(\"webkit\")&&!tv(\"Edge\"))&&!(tv(\"Trident\")||tv(\"MSIE\"))&&!tv(\"Edge\"),Ev=-1!=qv.toLowerCase().indexOf(\"webkit\")&&!tv(\"Edge\");Ev&&tv(\"Mobile\");tv(\"Macintosh\");tv(\"Windows\");tv(\"Linux\")||tv(\"CrOS\");var Fv=ba.navigator||null;Fv&&(Fv.appVersion||\"\").indexOf(\"X11\");tv(\"Android\");zv();tv(\"iPad\");tv(\"iPod\");zv()||tv(\"iPad\")||tv(\"iPod\");function Gv(){var a=ba.document;return a?a.documentMode:void 0}var Hv;\na:{var Iv=\"\",Jv=function(){var a=qv;if(Dv)return/rv\\:([^\\);]+)(\\)|;)/.exec(a);if(Cv)return/Edge\\/([\\d\\.]+)/.exec(a);if(Bv)return/\\b(?:MSIE|rv)[: ]([^\\);]+)(\\)|;)/.exec(a);if(Ev)return/WebKit\\/(\\S+)/.exec(a);if(Av)return/(?:Version)[ \\/]?(\\S+)/.exec(a)}();Jv&&(Iv=Jv?Jv[1]:\"\");if(Bv){var Kv=Gv();if(null!=Kv&&Kv>parseFloat(Iv)){Hv=String(Kv);break a}}Hv=Iv}var gb={};\nfunction Lv(a){return fb(a,function(){for(var b=0,c=ra(String(Hv)).split(\".\"),d=ra(String(a)).split(\".\"),e=Math.max(c.length,d.length),f=0;0==b&&f<e;f++){var h=c[f]||\"\",k=d[f]||\"\";do{h=/(\\d*)(\\D*)(.*)/.exec(h)||[\"\",\"\",\"\",\"\"];k=/(\\d*)(\\D*)(.*)/.exec(k)||[\"\",\"\",\"\",\"\"];if(0==h[0].length&&0==k[0].length)break;b=ta(0==h[1].length?0:parseInt(h[1],10),0==k[1].length?0:parseInt(k[1],10))||ta(0==h[2].length,0==k[2].length)||ta(h[2],k[2]);h=h[3];k=k[3]}while(0==b)}return 0<=b})}var Mv;var Nv=ba.document;\nMv=Nv&&Bv?Gv()||(\"CSS1Compat\"==Nv.compatMode?parseInt(Hv,10):5):void 0;var Ov;(Ov=!Bv)||(Ov=9<=Number(Mv));var Pv=Ov,Qv=Bv&&!Lv(\"9\");!Ev||Lv(\"528\");Dv&&Lv(\"1.9b\")||Bv&&Lv(\"8\")||Av&&Lv(\"9.5\")||Ev&&Lv(\"528\");Dv&&!Lv(\"8\")||Bv&&Lv(\"9\");var Rv=function(){if(!ba.addEventListener||!Object.defineProperty)return!1;var a=!1,b=Object.defineProperty({},\"passive\",{get:function(){a=!0}});ba.addEventListener(\"test\",ea,b);ba.removeEventListener(\"test\",ea,b);return a}();function Sv(a,b){this.type=a;this.currentTarget=this.target=b;this.defaultPrevented=this.Kc=!1;this.af=!0}Sv.prototype.stopPropagation=function(){this.Kc=!0};Sv.prototype.preventDefault=function(){this.defaultPrevented=!0;this.af=!1};function Tv(a,b){Sv.call(this,a?a.type:\"\");this.relatedTarget=this.currentTarget=this.target=null;this.button=this.screenY=this.screenX=this.clientY=this.clientX=this.offsetY=this.offsetX=0;this.key=\"\";this.charCode=this.keyCode=0;this.metaKey=this.shiftKey=this.altKey=this.ctrlKey=!1;this.pd=this.state=null;if(a){var c=this.type=a.type,d=a.changedTouches?a.changedTouches[0]:null;this.target=a.target||a.srcElement;this.currentTarget=b;var e=a.relatedTarget;if(e){if(Dv){a:{try{eb(e.nodeName);var f=\n!0;break a}catch(h){}f=!1}f||(e=null)}}else\"mouseover\"==c?e=a.fromElement:\"mouseout\"==c&&(e=a.toElement);this.relatedTarget=e;null===d?(this.offsetX=Ev||void 0!==a.offsetX?a.offsetX:a.layerX,this.offsetY=Ev||void 0!==a.offsetY?a.offsetY:a.layerY,this.clientX=void 0!==a.clientX?a.clientX:a.pageX,this.clientY=void 0!==a.clientY?a.clientY:a.pageY,this.screenX=a.screenX||0,this.screenY=a.screenY||0):(this.clientX=void 0!==d.clientX?d.clientX:d.pageX,this.clientY=void 0!==d.clientY?d.clientY:d.pageY,this.screenX=\nd.screenX||0,this.screenY=d.screenY||0);this.button=a.button;this.keyCode=a.keyCode||0;this.key=a.key||\"\";this.charCode=a.charCode||(\"keypress\"==c?a.keyCode:0);this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;this.metaKey=a.metaKey;this.state=a.state;this.pd=a;a.defaultPrevented&&this.preventDefault()}}qa(Tv,Sv);Tv.prototype.stopPropagation=function(){Tv.Zd.stopPropagation.call(this);this.pd.stopPropagation?this.pd.stopPropagation():this.pd.cancelBubble=!0};\nTv.prototype.preventDefault=function(){Tv.Zd.preventDefault.call(this);var a=this.pd;if(a.preventDefault)a.preventDefault();else if(a.returnValue=!1,Qv)try{if(a.ctrlKey||112<=a.keyCode&&123>=a.keyCode)a.keyCode=-1}catch(b){}};var Uv=\"closure_listenable_\"+(1E6*Math.random()|0),Vv=0;function Wv(a,b,c,d,e){this.listener=a;this.Xd=null;this.src=b;this.type=c;this.capture=!!d;this.Ub=e;this.key=++Vv;this.$c=this.Fd=!1}function Xv(a){a.$c=!0;a.listener=null;a.Xd=null;a.src=null;a.Ub=null};function Yv(a){this.src=a;this.rb={};this.wd=0}Yv.prototype.add=function(a,b,c,d,e){var f=a.toString();a=this.rb[f];a||(a=this.rb[f]=[],this.wd++);var h=Zv(a,b,d,e);-1<h?(b=a[h],c||(b.Fd=!1)):(b=new Wv(b,this.src,f,!!d,e),b.Fd=c,a.push(b));return b};Yv.prototype.remove=function(a,b,c,d){a=a.toString();if(!(a in this.rb))return!1;var e=this.rb[a];b=Zv(e,b,c,d);return-1<b?(Xv(e[b]),Array.prototype.splice.call(e,b,1),0==e.length&&(delete this.rb[a],this.wd--),!0):!1};\nfunction $v(a,b){var c=b.type;c in a.rb&&ya(a.rb[c],b)&&(Xv(b),0==a.rb[c].length&&(delete a.rb[c],a.wd--))}Yv.prototype.re=function(a,b,c,d){a=this.rb[a.toString()];var e=-1;a&&(e=Zv(a,b,c,d));return-1<e?a[e]:null};function Zv(a,b,c,d){for(var e=0;e<a.length;++e){var f=a[e];if(!f.$c&&f.listener==b&&f.capture==!!c&&f.Ub==d)return e}return-1};var aw=\"closure_lm_\"+(1E6*Math.random()|0),bw={},cw=0;function dw(a,b,c,d,e){if(d&&d.once)ew(a,b,c,d,e);else if(\"array\"==n(b))for(var f=0;f<b.length;f++)dw(a,b[f],c,d,e);else c=fw(c),a&&a[Uv]?a.Ib.add(String(b),c,!1,ia(d)?!!d.capture:!!d,e):gw(a,b,c,!1,d,e)}\nfunction gw(a,b,c,d,e,f){if(!b)throw Error(\"Invalid event type\");var h=ia(e)?!!e.capture:!!e,k=hw(a);k||(a[aw]=k=new Yv(a));c=k.add(b,c,d,h,f);if(!c.Xd){d=iw();c.Xd=d;d.src=a;d.listener=c;if(a.addEventListener)Rv||(e=h),void 0===e&&(e=!1),a.addEventListener(b.toString(),d,e);else if(a.attachEvent)a.attachEvent(jw(b.toString()),d);else throw Error(\"addEventListener and attachEvent are unavailable.\");cw++}}\nfunction iw(){var a=kw,b=Pv?function(c){return a.call(b.src,b.listener,c)}:function(c){c=a.call(b.src,b.listener,c);if(!c)return c};return b}function ew(a,b,c,d,e){if(\"array\"==n(b))for(var f=0;f<b.length;f++)ew(a,b[f],c,d,e);else c=fw(c),a&&a[Uv]?a.Ib.add(String(b),c,!0,ia(d)?!!d.capture:!!d,e):gw(a,b,c,!0,d,e)}\nfunction lw(a,b,c,d,e){if(\"array\"==n(b))for(var f=0;f<b.length;f++)lw(a,b[f],c,d,e);else d=ia(d)?!!d.capture:!!d,c=fw(c),a&&a[Uv]?a.Ib.remove(String(b),c,d,e):a&&(a=hw(a))&&(b=a.re(b,c,d,e))&&mw(b)}function mw(a){if(\"number\"!=typeof a&&a&&!a.$c){var b=a.src;if(b&&b[Uv])$v(b.Ib,a);else{var c=a.type,d=a.Xd;b.removeEventListener?b.removeEventListener(c,d,a.capture):b.detachEvent&&b.detachEvent(jw(c),d);cw--;(c=hw(b))?($v(c,a),0==c.wd&&(c.src=null,b[aw]=null)):Xv(a)}}}\nfunction jw(a){return a in bw?bw[a]:bw[a]=\"on\"+a}function nw(a,b,c,d){var e=!0;if(a=hw(a))if(b=a.rb[b.toString()])for(b=b.concat(),a=0;a<b.length;a++){var f=b[a];f&&f.capture==c&&!f.$c&&(f=ow(f,d),e=e&&!1!==f)}return e}function ow(a,b){var c=a.listener,d=a.Ub||a.src;a.Fd&&mw(a);return c.call(d,b)}\nfunction kw(a,b){if(a.$c)return!0;if(!Pv){var c;if(!(c=b))a:{c=[\"window\",\"event\"];for(var d=ba,e;e=c.shift();)if(null!=d[e])d=d[e];else{c=null;break a}c=d}e=c;c=new Tv(e,this);d=!0;if(!(0>e.keyCode||void 0!=e.returnValue)){a:{var f=!1;if(0==e.keyCode)try{e.keyCode=-1;break a}catch(l){f=!0}if(f||void 0==e.returnValue)e.returnValue=!0}e=[];for(f=c.currentTarget;f;f=f.parentNode)e.push(f);f=a.type;for(var h=e.length-1;!c.Kc&&0<=h;h--){c.currentTarget=e[h];var k=nw(e[h],f,!0,c);d=d&&k}for(h=0;!c.Kc&&\nh<e.length;h++)c.currentTarget=e[h],k=nw(e[h],f,!1,c),d=d&&k}return d}return ow(a,new Tv(b,this))}function hw(a){a=a[aw];return a instanceof Yv?a:null}var pw=\"__closure_events_fn_\"+(1E9*Math.random()>>>0);function fw(a){if(ha(a))return a;a[pw]||(a[pw]=function(b){return a.handleEvent(b)});return a[pw]};function qw(){wv.call(this);this.Ib=new Yv(this);this.ff=this;this.ve=null}qa(qw,wv);qw.prototype[Uv]=!0;g=qw.prototype;g.addEventListener=function(a,b,c,d){dw(this,a,b,c,d)};g.removeEventListener=function(a,b,c,d){lw(this,a,b,c,d)};\ng.dispatchEvent=function(a){var b,c=this.ve;if(c)for(b=[];c;c=c.ve)b.push(c);c=this.ff;var d=a.type||a;if(ca(a))a=new Sv(a,c);else if(a instanceof Sv)a.target=a.target||c;else{var e=a;a=new Sv(d,c);Ia(a,e)}e=!0;if(b)for(var f=b.length-1;!a.Kc&&0<=f;f--){var h=a.currentTarget=b[f];e=rw(h,d,!0,a)&&e}a.Kc||(h=a.currentTarget=c,e=rw(h,d,!0,a)&&e,a.Kc||(e=rw(h,d,!1,a)&&e));if(b)for(f=0;!a.Kc&&f<b.length;f++)h=a.currentTarget=b[f],e=rw(h,d,!1,a)&&e;return e};\ng.nd=function(){qw.Zd.nd.call(this);if(this.Ib){var a=this.Ib,b=0,c;for(c in a.rb){for(var d=a.rb[c],e=0;e<d.length;e++)++b,Xv(d[e]);delete a.rb[c];a.wd--}}this.ve=null};function rw(a,b,c,d){b=a.Ib.rb[String(b)];if(!b)return!0;b=b.concat();for(var e=!0,f=0;f<b.length;++f){var h=b[f];if(h&&!h.$c&&h.capture==c){var k=h.listener,l=h.Ub||h.src;h.Fd&&$v(a.Ib,h);e=!1!==k.call(l,d)&&e}}return e&&0!=d.af}g.re=function(a,b,c,d){return this.Ib.re(String(a),b,c,d)};function sw(a,b,c){if(ha(a))c&&(a=pa(a,c));else if(a&&\"function\"==typeof a.handleEvent)a=pa(a.handleEvent,a);else throw Error(\"Invalid listener argument\");return 2147483647<Number(b)?-1:ba.setTimeout(a,b||0)};function tw(){}tw.prototype.Ke=null;function uw(a){var b;(b=a.Ke)||(b={},vw(a)&&(b[0]=!0,b[1]=!0),b=a.Ke=b);return b};var ww;function xw(){}qa(xw,tw);function yw(a){return(a=vw(a))?new ActiveXObject(a):new XMLHttpRequest}function vw(a){if(!a.Te&&\"undefined\"==typeof XMLHttpRequest&&\"undefined\"!=typeof ActiveXObject){for(var b=[\"MSXML2.XMLHTTP.6.0\",\"MSXML2.XMLHTTP.3.0\",\"MSXML2.XMLHTTP\",\"Microsoft.XMLHTTP\"],c=0;c<b.length;c++){var d=b[c];try{return new ActiveXObject(d),a.Te=d}catch(e){}}throw Error(\"Could not create ActiveXObject. ActiveX might be disabled, or MSXML might not be installed\");}return a.Te}ww=new xw;function zw(a){qw.call(this);this.headers=new Ma;this.ce=a||null;this.oc=!1;this.be=this.ca=null;this.ue=\"\";this.Ic=this.se=this.Sd=this.qe=!1;this.Ae=0;this.$d=null;this.$e=Aw;this.Be=this.Lf=this.ef=!1}qa(zw,qw);var Aw=\"\",Bw=/^https?$/i,Cw=[\"POST\",\"PUT\"],Dw=[];function Ew(a,b){var c=new zw;Dw.push(c);b&&c.Ib.add(\"complete\",b,!1,void 0,void 0);c.Ib.add(\"ready\",c.gf,!0,void 0,void 0);c.send(a,void 0,void 0,void 0);return c}g=zw.prototype;\ng.gf=function(){if(!this.od&&(this.od=!0,this.nd(),0!=xv)){var a=ja(this);delete yv[a]}ya(Dw,this)};\ng.send=function(a,b,c,d){if(this.ca)throw Error(\"[goog.net.XhrIo] Object is active with another request\\x3d\"+this.ue+\"; newUri\\x3d\"+a);b=b?b.toUpperCase():\"GET\";this.ue=a;this.qe=!1;this.oc=!0;this.ca=this.ce?yw(this.ce):yw(ww);this.be=this.ce?uw(this.ce):uw(ww);this.ca.onreadystatechange=pa(this.Ye,this);this.Lf&&\"onprogress\"in this.ca&&(this.ca.onprogress=pa(function(a){this.Xe(a,!0)},this),this.ca.upload&&(this.ca.upload.onprogress=pa(this.Xe,this)));try{this.se=!0,this.ca.open(b,String(a),!0),\nthis.se=!1}catch(f){Fw(this);return}a=c||\"\";var e=this.headers.clone();d&&La(d,function(a,b){e.set(b,a)});d=wa(e.Xc());c=ba.FormData&&a instanceof ba.FormData;!(0<=ua(Cw,b))||d||c||e.set(\"Content-Type\",\"application/x-www-form-urlencoded;charset\\x3dutf-8\");e.forEach(function(a,b){this.ca.setRequestHeader(b,a)},this);this.$e&&(this.ca.responseType=this.$e);\"withCredentials\"in this.ca&&this.ca.withCredentials!==this.ef&&(this.ca.withCredentials=this.ef);try{Gw(this),0<this.Ae&&((this.Be=Hw(this.ca))?\n(this.ca.timeout=this.Ae,this.ca.ontimeout=pa(this.cf,this)):this.$d=sw(this.cf,this.Ae,this)),this.Sd=!0,this.ca.send(a),this.Sd=!1}catch(f){Fw(this)}};function Hw(a){return Bv&&Lv(9)&&\"number\"==typeof a.timeout&&void 0!==a.ontimeout}function xa(a){return\"content-type\"==a.toLowerCase()}g.cf=function(){\"undefined\"!=typeof aa&&this.ca&&(this.dispatchEvent(\"timeout\"),this.abort(8))};function Fw(a){a.oc=!1;a.ca&&(a.Ic=!0,a.ca.abort(),a.Ic=!1);Iw(a);Jw(a)}\nfunction Iw(a){a.qe||(a.qe=!0,a.dispatchEvent(\"complete\"),a.dispatchEvent(\"error\"))}g.abort=function(){this.ca&&this.oc&&(this.oc=!1,this.Ic=!0,this.ca.abort(),this.Ic=!1,this.dispatchEvent(\"complete\"),this.dispatchEvent(\"abort\"),Jw(this))};g.nd=function(){this.ca&&(this.oc&&(this.oc=!1,this.Ic=!0,this.ca.abort(),this.Ic=!1),Jw(this,!0));zw.Zd.nd.call(this)};g.Ye=function(){this.od||(this.se||this.Sd||this.Ic?Kw(this):this.If())};g.If=function(){Kw(this)};\nfunction Kw(a){if(a.oc&&\"undefined\"!=typeof aa&&(!a.be[1]||4!=Lw(a)||2!=Mw(a)))if(a.Sd&&4==Lw(a))sw(a.Ye,0,a);else if(a.dispatchEvent(\"readystatechange\"),4==Lw(a)){a.oc=!1;try{var b=Mw(a);a:switch(b){case 200:case 201:case 202:case 204:case 206:case 304:case 1223:var c=!0;break a;default:c=!1}var d;if(!(d=c)){var e;if(e=0===b){var f=String(a.ue).match(Pa)[1]||null;if(!f&&ba.self&&ba.self.location){var h=ba.self.location.protocol;f=h.substr(0,h.length-1)}e=!Bw.test(f?f.toLowerCase():\"\")}d=e}d?(a.dispatchEvent(\"complete\"),\na.dispatchEvent(\"success\")):Iw(a)}finally{Jw(a)}}}g.Xe=function(a,b){this.dispatchEvent(Nw(a,\"progress\"));this.dispatchEvent(Nw(a,b?\"downloadprogress\":\"uploadprogress\"))};function Nw(a,b){return{type:b,lengthComputable:a.lengthComputable,loaded:a.loaded,total:a.total}}function Jw(a,b){if(a.ca){Gw(a);var c=a.ca,d=a.be[0]?ea:null;a.ca=null;a.be=null;b||a.dispatchEvent(\"ready\");try{c.onreadystatechange=d}catch(e){}}}\nfunction Gw(a){a.ca&&a.Be&&(a.ca.ontimeout=null);\"number\"==typeof a.$d&&(ba.clearTimeout(a.$d),a.$d=null)}function Lw(a){return a.ca?a.ca.readyState:0}function Mw(a){try{return 2<Lw(a)?a.ca.status:-1}catch(b){return-1}}g.getResponseHeader=function(a){if(this.ca&&4==Lw(this))return a=this.ca.getResponseHeader(a),null===a?void 0:a};g.getAllResponseHeaders=function(){return this.ca&&4==Lw(this)?this.ca.getAllResponseHeaders():\"\"};var Ow,Pw,Qw,Rw=function Rw(a,b){if(null!=a&&null!=a.oe)return a.oe(0,b);var d=Rw[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=Rw._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb(\"ReadPort.take!\",a);},Sw=function Sw(a,b,c){if(null!=a&&null!=a.Od)return a.Od(0,b,c);var e=Sw[n(null==a?null:a)];if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);e=Sw._;if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);throw Cb(\"WritePort.put!\",a);},Tw=function Tw(a){if(null!=a&&null!=\na.ld)return a.ld();var c=Tw[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Tw._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"Channel.close!\",a);},Uw=function Uw(a){if(null!=a&&null!=a.vb)return a.vb(a);var c=Uw[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Uw._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"Handler.active?\",a);},Vw=function Vw(a){if(null!=a&&null!=a.tb)return a.tb(a);var c=Vw[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,\na);c=Vw._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"Handler.commit\",a);},Ww=function Ww(a,b){if(null!=a&&null!=a.Md)return a.Md(a,b);var d=Ww[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=Ww._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb(\"Buffer.add!*\",a);},Xw=function Xw(a){switch(arguments.length){case 1:return Xw.h(arguments[0]);case 2:return Xw.c(arguments[0],arguments[1]);default:throw Error([\"Invalid arity: \",v.h(arguments.length)].join(\"\"));}};\nXw.h=function(a){return a};Xw.c=function(a,b){return Ww(a,b)};Xw.L=2;function Yw(a,b,c,d,e){for(var f=0;;)if(f<e)c[d+f]=a[b+f],f+=1;else break}function Zw(a,b,c,d){this.head=a;this.fa=b;this.length=c;this.o=d}Zw.prototype.pop=function(){if(0===this.length)return null;var a=this.o[this.fa];this.o[this.fa]=null;this.fa=(this.fa+1)%this.o.length;--this.length;return a};Zw.prototype.unshift=function(a){this.o[this.head]=a;this.head=(this.head+1)%this.o.length;this.length+=1;return null};function $w(a,b){a.length+1===a.o.length&&a.resize();a.unshift(b)}\nZw.prototype.resize=function(){var a=Array(2*this.o.length);return this.fa<this.head?(Yw(this.o,this.fa,a,0,this.length),this.fa=0,this.head=this.length,this.o=a):this.fa>this.head?(Yw(this.o,this.fa,a,0,this.o.length-this.fa),Yw(this.o,0,a,this.o.length-this.fa,this.head),this.fa=0,this.head=this.length,this.o=a):this.fa===this.head?(this.head=this.fa=0,this.o=a):null};function ax(a,b){for(var c=a.length,d=0;;)if(d<c){var e=a.pop();(b.h?b.h(e):b.call(null,e))&&a.unshift(e);d+=1}else break}\nfunction bx(a){return new Zw(0,0,0,Array(a))}function cx(a,b){this.aa=a;this.n=b;this.m=2;this.J=0}g=cx.prototype;g.Nd=function(){return this.aa.length===this.n};g.Sc=function(){return this.aa.pop()};g.Md=function(a,b){$w(this.aa,b);return this};g.ne=function(){return null};g.W=function(){return this.aa.length};function dx(a,b){this.aa=a;this.n=b;this.m=2;this.J=0}g=dx.prototype;g.Nd=function(){return!1};g.Sc=function(){return this.aa.pop()};\ng.Md=function(a,b){this.aa.length!==this.n&&this.aa.unshift(b);return this};g.ne=function(){return null};g.W=function(){return this.aa.length};if(\"undefined\"===typeof ex)var ex={};function fx(a){this.H=a;this.m=2;this.J=0}g=fx.prototype;g.Nd=function(){return!1};g.Sc=function(){return this.H};g.Md=function(a,b){t(ex===this.H)&&(this.H=b);return this};g.ne=function(){return t(ex===this.H)?this.H=null:null};g.W=function(){return t(ex===this.H)?0:1};var gx=bx(32),hx=!1,ix=!1;function jx(){hx=!0;ix=!1;for(var a=0;;){var b=gx.pop();if(null!=b&&(b.B?b.B():b.call(null),1024>a)){a+=1;continue}break}hx=!1;return 0<gx.length?kx.B?kx.B():kx.call(null):null}function kx(){if(ix&&hx)return null;ix=!0;!ha(ba.setImmediate)||ba.Window&&ba.Window.prototype&&!tv(\"Edge\")&&ba.Window.prototype.setImmediate==ba.setImmediate?(uv||(uv=vv()),uv(jx)):ba.setImmediate(jx)}function lx(a){$w(gx,a);kx()}function mx(a,b){setTimeout(a,b)};var nx;\nfunction ox(a){\"undefined\"===typeof nx&&(nx=function(a,c){this.H=a;this.Af=c;this.m=425984;this.J=0},nx.prototype.T=function(a,c){return new nx(this.H,c)},nx.prototype.P=function(){return this.Af},nx.prototype.pc=function(){return this.H},nx.Wc=function(){return new R(null,2,5,T,[Km,qo],null)},nx.qc=!0,nx.Tb=\"cljs.core.async.impl.channels/t_cljs$core$async$impl$channels36582\",nx.Ec=function(a,c){return Jc(c,\"cljs.core.async.impl.channels/t_cljs$core$async$impl$channels36582\")});return new nx(a,Ef)}\nfunction px(a,b){this.Ub=a;this.H=b}function qx(a){return Uw(a.Ub)}function rx(a,b,c,d,e,f,h){this.bd=a;this.Qd=b;this.jc=c;this.Pd=d;this.aa=e;this.closed=f;this.Ab=h}function sx(a){for(;;){var b=a.jc.pop();if(null!=b){var c=b.Ub,d=b.H;if(c.vb(null)){var e=c.tb(null);lx(function(a){return function(){return a.h?a.h(!0):a.call(null,!0)}}(e,c,d,b,a))}else continue}break}ax(a.jc,Zf(!1));a.ld()}\nrx.prototype.Od=function(a,b,c){var d=this,e=this,f=d.closed;if(f||!c.vb(null))return ox(!f);if(t(function(){var a=d.aa;return t(a)?wb(d.aa.Nd(null)):a}())){c.tb(null);var h=Hd(d.Ab.c?d.Ab.c(d.aa,b):d.Ab.call(null,d.aa,b));c=function(){for(var a=he;;)if(0<d.bd.length&&0<H(d.aa)){var b=d.bd.pop();if(b.vb(null)){var c=b.tb(null),k=d.aa.Sc(null);a=ge.c(a,function(a,b,c){return function(){return b.h?b.h(c):b.call(null,c)}}(a,c,k,b,h,f,e))}}else return a}();h&&sx(e);if(E(c)){c=E(c);a=null;for(var k=0,\nl=0;;)if(l<k){var p=a.$(null,l);lx(p);l+=1}else if(c=E(c))a=c,Ae(a)?(c=Wc(a),l=Xc(a),a=c,k=H(c),c=l):(c=y(a),lx(c),c=z(a),a=null,k=0),l=0;else break}return ox(!0)}a=function(){for(;;){var a=d.bd.pop();if(t(a)){if(t(a.vb(null)))return a}else return null}}();if(t(a))return k=Vw(a),c.tb(null),lx(function(a){return function(){return a.h?a.h(b):a.call(null,b)}}(k,a,f,e)),ox(!0);64<d.Pd?(d.Pd=0,ax(d.jc,qx)):d.Pd+=1;t(c.md(null))&&$w(d.jc,new px(c,b));return null};\nrx.prototype.oe=function(a,b){var c=this;if(b.vb(null)){if(null!=c.aa&&0<H(c.aa)){var d=b.tb(null);if(t(d)){var e=c.aa.Sc(null),f=0<c.jc.length?function(){for(var a=he;;){var b=c.jc.pop(),d=b.Ub;b=b.H;var e=d.vb(null);d=e?d.tb(null):e;a=t(d)?ge.c(a,d):a;b=t(d)?Hd(c.Ab.c?c.Ab.c(c.aa,b):c.Ab.call(null,c.aa,b)):null;if(!(wb(b)&&wb(c.aa.Nd(null))&&0<c.jc.length))return new R(null,2,5,T,[b,a],null)}}():null,h=J(f,0,null),k=J(f,1,null);t(h)&&sx(this);for(var l=E(k),p=null,m=0,u=0;;)if(u<m){var w=p.$(null,\nu);lx(function(a,b,c,d,e){return function(){return e.h?e.h(!0):e.call(null,!0)}}(l,p,m,u,w,e,f,h,k,d,d,this));u+=1}else{var x=E(l);if(x){w=x;if(Ae(w))l=Wc(w),u=Xc(w),p=l,m=H(l),l=u;else{var C=y(w);lx(function(a,b,c,d,e){return function(){return e.h?e.h(!0):e.call(null,!0)}}(l,p,m,u,C,w,x,e,f,h,k,d,d,this));l=z(w);p=null;m=0}u=0}else break}return ox(e)}return null}d=function(){for(;;){var a=c.jc.pop();if(t(a)){if(Uw(a.Ub))return a}else return null}}();if(t(d))return e=Vw(d.Ub),b.tb(null),lx(function(a){return function(){return a.h?\na.h(!0):a.call(null,!0)}}(e,d,this)),ox(d.H);if(t(c.closed))return t(c.aa)&&(c.Ab.h?c.Ab.h(c.aa):c.Ab.call(null,c.aa)),t(function(){var a=b.vb(null);return t(a)?b.tb(null):a}())?(d=function(){var a=c.aa;return t(a)?0<H(c.aa):a}(),e=t(d)?c.aa.Sc(null):null,ox(e)):null;64<c.Qd?(c.Qd=0,ax(c.bd,Uw)):c.Qd+=1;t(b.md(null))&&$w(c.bd,b)}return null};\nrx.prototype.ld=function(){var a=this;if(!a.closed){a.closed=!0;for(t(function(){var b=a.aa;return t(b)?0===a.jc.length:b}())&&(a.Ab.h?a.Ab.h(a.aa):a.Ab.call(null,a.aa));;){var b=a.bd.pop();if(null!=b){if(b.vb(null)){var c=b.tb(null),d=t(function(){var b=a.aa;return t(b)?0<H(a.aa):b}())?a.aa.Sc(null):null;lx(function(a,b){return function(){return a.h?a.h(b):a.call(null,b)}}(c,d,b,this))}}else break}t(a.aa)&&a.aa.ne(null)}return null};function tx(a){console.log(a);return null}\nfunction ux(a,b){var c=t(null)?null:tx;c=c.h?c.h(b):c.call(null,b);return null==c?a:Xw.c(a,c)}\nfunction vx(a,b){return new rx(bx(32),0,bx(32),0,a,!1,function(){return function(a){return function(){function b(b,c){try{return a.c?a.c(b,c):a.call(null,b,c)}catch(l){return ux(b,l)}}function c(b){try{return a.h?a.h(b):a.call(null,b)}catch(k){return ux(b,k)}}var f=null;f=function(a,d){switch(arguments.length){case 1:return c.call(this,a);case 2:return b.call(this,a,d)}throw Error(\"Invalid arity: \"+(arguments.length-1));};f.h=c;f.c=b;return f}()}(t(b)?b.h?b.h(Xw):b.call(null,Xw):Xw)}())};var wx;\nfunction xx(a){\"undefined\"===typeof wx&&(wx=function(a,c){this.Cb=a;this.Cf=c;this.m=393216;this.J=0},wx.prototype.T=function(a,c){return new wx(this.Cb,c)},wx.prototype.P=function(){return this.Cf},wx.prototype.vb=function(){return!0},wx.prototype.md=function(){return!0},wx.prototype.tb=function(){return this.Cb},wx.Wc=function(){return new R(null,2,5,T,[to,Um],null)},wx.qc=!0,wx.Tb=\"cljs.core.async.impl.ioc-helpers/t_cljs$core$async$impl$ioc_helpers42956\",wx.Ec=function(a,c){return Jc(c,\"cljs.core.async.impl.ioc-helpers/t_cljs$core$async$impl$ioc_helpers42956\")});\nreturn new wx(a,Ef)}function yx(a){try{var b=a[0];return b.h?b.h(a):b.call(null,a)}catch(c){if(c instanceof Object)throw b=c,a[6].ld(),b;throw c;}}function zx(a,b,c){c=c.oe(0,xx(function(c){a[2]=c;a[1]=b;return yx(a)}));return t(c)?(a[2]=B(c),a[1]=b,Z):null}function Ax(a,b,c,d){c=c.Od(0,d,xx(function(c){a[2]=c;a[1]=b;return yx(a)}));return t(c)?(a[2]=B(c),a[1]=b,Z):null}function Bx(a,b){var c=a[6];null!=b&&c.Od(0,b,xx(function(){return function(){return null}}(c)));c.ld();return c}\nfunction Cx(a){for(;;){var b=a[4],c=ul.h(b),d=Pm.h(b),e=a[5];if(t(function(){var a=e;return t(a)?wb(b):a}()))throw e;if(t(function(){var a=e;return t(a)?(a=c,t(a)?G.c(Ik,d)||e instanceof d:a):a}())){a[1]=c;a[2]=e;a[5]=null;a[4]=K.A(b,ul,null,be([Pm,null]));break}if(t(function(){var a=e;return t(a)?wb(c)&&wb(Lk.h(b)):a}()))a[4]=Xm.h(b);else{if(t(function(){var a=e;return t(a)?(a=wb(c))?Lk.h(b):a:a}())){a[1]=Lk.h(b);a[4]=K.l(b,Lk,null);break}if(t(function(){var a=wb(e);return a?Lk.h(b):a}())){a[1]=\nLk.h(b);a[4]=K.l(b,Lk,null);break}if(wb(e)&&wb(Lk.h(b))){a[1]=cn.h(b);a[4]=Xm.h(b);break}throw Error(\"No matching clause\");}}};function Dx(a,b,c){this.key=a;this.H=b;this.forward=c;this.m=2155872256;this.J=0}Dx.prototype.S=function(){var a=this.key;return Tb(Tb(wd,this.H),a)};Dx.prototype.R=function(a,b,c){return Y(b,Qi,\"[\",\" \",\"]\",c,this)};function Ex(a,b,c){c=Array(c+1);for(var d=0;;)if(d<c.length)c[d]=null,d+=1;else break;return new Dx(a,b,c)}function Fx(a,b,c,d){for(;;){if(0>c)return a;a:for(;;){var e=c<a.forward.length?a.forward[c]:null;if(t(e))if(e.key<b)a=e;else break a;else break a}null!=d&&(d[c]=a);--c}}\nfunction Gx(a,b){this.header=a;this.level=b;this.m=2155872256;this.J=0}Gx.prototype.put=function(a,b){var c=Array(15),d=Fx(this.header,a,this.level,c).forward[0];if(null!=d&&d.key===a)return d.H=b;a:for(d=0;;)if(.5>Math.random()&&15>d)d+=1;else break a;if(d>this.level){for(var e=this.level+1;;)if(e<=d+1)c[e]=this.header,e+=1;else break;this.level=d}for(d=Ex(a,b,Array(d));;)return 0<=this.level?(c=c[0].forward,d.forward[0]=c[0],c[0]=d):null};\nGx.prototype.remove=function(a){var b=Array(15),c=Fx(this.header,a,this.level,b);c=0===c.forward.length?null:c.forward[0];if(null!=c&&c.key===a){for(a=0;;)if(a<=this.level){var d=b[a].forward;c===(a<d.length?d[a]:null)&&(d[a]=c.forward[a]);a+=1}else break;for(;;)if(0<this.level&&this.level<this.header.forward.length&&null==this.header.forward[this.level])--this.level;else return null}else return null};\nfunction Hx(a){for(var b=Ix,c=b.header,d=b.level;;){if(0>d)return c===b.header?null:c;var e;a:for(e=c;;){e=d<e.forward.length?e.forward[d]:null;if(null==e){e=null;break a}if(e.key>=a)break a}null!=e?(--d,c=e):--d}}Gx.prototype.S=function(){return function(a){return function d(c){return new kf(null,function(){return function(){return null==c?null:ae(new R(null,2,5,T,[c.key,c.H],null),d(c.forward[0]))}}(a),null,null)}}(this)(this.header.forward[0])};\nGx.prototype.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"{\",\", \",\"}\",c,this)};var Ix=new Gx(Ex(null,null,0),0);function Jx(a){var b=(new Date).valueOf()+a,c=Hx(b),d=t(t(c)?c.key<b+10:c)?c.H:null;if(t(d))return d;var e=vx(null,null);Ix.put(b,e);mx(function(a,b,c){return function(){Ix.remove(c);return Tw(a)}}(e,d,b,c),a);return e};function Kx(a){return Lx(a,null)}function Mx(a,b){return Lx(a,b)}function Lx(a,b){var c=G.c(a,0)?null:a;return vx(\"number\"===typeof c?new cx(bx(c),c):c,b)}\nvar Nx=function(a){\"undefined\"===typeof Ow&&(Ow=function(a,c,d){this.Cb=a;this.Je=c;this.Df=d;this.m=393216;this.J=0},Ow.prototype.T=function(a,c){return new Ow(this.Cb,this.Je,c)},Ow.prototype.P=function(){return this.Df},Ow.prototype.vb=function(){return!0},Ow.prototype.md=function(){return this.Je},Ow.prototype.tb=function(){return this.Cb},Ow.Wc=function(){return new R(null,3,5,T,[to,jk,gk],null)},Ow.qc=!0,Ow.Tb=\"cljs.core.async/t_cljs$core$async43104\",Ow.Ec=function(a,c){return Jc(c,\"cljs.core.async/t_cljs$core$async43104\")});\nreturn new Ow(a,!0,Ef)}(function(){return null});function Ox(a,b){var c=Sw(a,b,Nx);return t(c)?B(c):!0}function Px(a){for(var b=Array(a),c=0;;)if(c<a)b[c]=0,c+=1;else break;for(c=1;;){if(G.c(c,a))return b;var d=Math.floor(Math.random()*c);b[c]=b[d];b[d]=c;c+=1}}\nfunction Qx(){var a=dg.h(!0);\"undefined\"===typeof Pw&&(Pw=function(a,c){this.Hc=a;this.Ef=c;this.m=393216;this.J=0},Pw.prototype.T=function(){return function(a,c){return new Pw(this.Hc,c)}}(a),Pw.prototype.P=function(){return function(){return this.Ef}}(a),Pw.prototype.vb=function(){return function(){return B(this.Hc)}}(a),Pw.prototype.md=function(){return function(){return!0}}(a),Pw.prototype.tb=function(){return function(){fg(this.Hc,null);return!0}}(a),Pw.Wc=function(){return function(){return new R(null,\n2,5,T,[em,ek],null)}}(a),Pw.qc=!0,Pw.Tb=\"cljs.core.async/t_cljs$core$async43126\",Pw.Ec=function(){return function(a,c){return Jc(c,\"cljs.core.async/t_cljs$core$async43126\")}}(a));return new Pw(a,Ef)}\nfunction Rx(a,b){\"undefined\"===typeof Qw&&(Qw=function(a,b,e){this.Hc=a;this.ed=b;this.Ff=e;this.m=393216;this.J=0},Qw.prototype.T=function(a,b){return new Qw(this.Hc,this.ed,b)},Qw.prototype.P=function(){return this.Ff},Qw.prototype.vb=function(){return Uw(this.Hc)},Qw.prototype.md=function(){return!0},Qw.prototype.tb=function(){Vw(this.Hc);return this.ed},Qw.Wc=function(){return new R(null,3,5,T,[em,Mk,hl],null)},Qw.qc=!0,Qw.Tb=\"cljs.core.async/t_cljs$core$async43129\",Qw.Ec=function(a,b){return Jc(b,\n\"cljs.core.async/t_cljs$core$async43129\")});return new Qw(a,b,Ef)}\nfunction Sx(a,b,c){var d=Qx(),e=H(b),f=Px(e),h=Im.h(c),k=function(){for(var c=0;;)if(c<e){var k=t(h)?c:f[c],m=Vd(b,k),u=ze(m)?m.h?m.h(0):m.call(null,0):null,w=t(u)?function(){var b=m.h?m.h(1):m.call(null,1);return Sw(u,b,Rx(d,function(b,c,d,e,f){return function(b){b=new R(null,2,5,T,[b,f],null);return a.h?a.h(b):a.call(null,b)}}(c,b,k,m,u,d,e,f,h)))}():Rw(m,Rx(d,function(b,c,d){return function(b){b=new R(null,2,5,T,[b,d],null);return a.h?a.h(b):a.call(null,b)}}(c,k,m,u,d,e,f,h)));if(t(w))return ox(new R(null,\n2,5,T,[B(w),function(){var a=u;return t(a)?a:m}()],null));c+=1}else return null}();return t(k)?k:He(c,Ik)&&(k=function(){var a=Uw(d);return t(a)?Vw(d):a}(),t(k))?ox(new R(null,2,5,T,[Ik.h(c),Ik],null)):null}\nfunction Tx(a,b){var c=Kx(1);lx(function(c){return function(){var d=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(x){if(x instanceof Object)b[5]=x,Cx(b),d=Z;else throw x;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error(\"Invalid arity: \"+(arguments.length-\n1));};d.B=c;d.h=b;return d}()}(function(){return function(c){var d=c[1];return 7===d?(c[2]=c[2],c[1]=3,Z):1===d?(c[2]=null,c[1]=2,Z):4===d?(d=c[2],c[7]=d,c[1]=t(null==d)?5:6,Z):13===d?(c[2]=null,c[1]=14,Z):6===d?(d=c[7],Ax(c,11,b,d)):3===d?Bx(c,c[2]):12===d?(c[2]=null,c[1]=2,Z):2===d?zx(c,4,a):11===d?(c[1]=t(c[2])?12:13,Z):9===d?(c[2]=null,c[1]=10,Z):5===d?(c[1]=t(!0)?8:9,Z):14===d||10===d?(c[2]=c[2],c[1]=7,Z):8===d?(d=Tw(b),c[2]=d,c[1]=10,Z):null}}(c),c)}(),f=function(){var a=d.B?d.B():d.call(null);\na[6]=c;return a}();return yx(f)}}(c))}function Ux(a){for(var b=[],c=arguments.length,d=0;;)if(d<c)b.push(arguments[d]),d+=1;else break;return Vx(arguments[0],arguments[1],arguments[2],3<b.length?new Jb(b.slice(3),0,null):null)}function Vx(a,b,c,d){var e=null!=d&&(d.m&64||q===d.G)?P(U,d):d;a[1]=b;b=Sx(function(){return function(b){a[2]=b;return yx(a)}}(d,e,e),c,e);return t(b)?(a[2]=B(b),Z):null};function Wx(){}var Xx=function Xx(a,b){if(null!=a&&null!=a.qb)return a.qb(a,b);var d=Xx[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=Xx._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb(\"Update.update-player\",a);};function Yx(){}var Zx=function Zx(a,b){if(null!=a&&null!=a.de)return a.de(a,b);var d=Zx[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=Zx._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb(\"ChannelSource.get-channels\",a);};\nfunction $x(a,b,c){this.v=a;this.j=b;this.w=c;this.m=2229667594;this.J=139264}g=$x.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){return D.l(this.j,b,c)};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#asciinema.player.messages.FastForward{\",\", \",\"}\",c,O.c(he,this.j))};g.ba=function(){return new fh(0,this,0,he,t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 0+H(this.j)};\ng.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 1082393681^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.j,b.j)};g.ga=function(a,b){return He(vi,b)?le.c(tc(wg.c(Ef,this),this.v),b):new $x(this.v,Bf(le.c(this.j,b)),null)};g.O=function(a,b,c){return new $x(this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(he,this.j))};\ng.T=function(a,b){return new $x(b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function ay(a,b,c){this.v=a;this.j=b;this.w=c;this.m=2229667594;this.J=139264}g=ay.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){return D.l(this.j,b,c)};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#asciinema.player.messages.Rewind{\",\", \",\"}\",c,O.c(he,this.j))};\ng.ba=function(){return new fh(0,this,0,he,t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 0+H(this.j)};g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-1020675721^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.j,b.j)};g.ga=function(a,b){return He(vi,b)?le.c(tc(wg.c(Ef,this),this.v),b):new ay(this.v,Bf(le.c(this.j,b)),null)};\ng.O=function(a,b,c){return new ay(this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(he,this.j))};g.T=function(a,b){return new ay(b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function by(a,b,c,d){this.position=a;this.v=b;this.j=c;this.w=d;this.m=2229667594;this.J=139264}g=by.prototype;g.V=function(a,b){return this.I(null,b,null)};\ng.I=function(a,b,c){switch(b instanceof L?b.ea:null){case \"position\":return this.position;default:return D.l(this.j,b,c)}};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#asciinema.player.messages.Seek{\",\", \",\"}\",c,O.c(new R(null,1,5,T,[new R(null,2,5,T,[nn,this.position],null)],null),this.j))};g.ba=function(){return new fh(0,this,1,new R(null,1,5,T,[nn],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 1+H(this.j)};\ng.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-2136325183^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.position,b.position)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,1,[nn,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new by(this.position,this.v,Bf(le.c(this.j,b)),null)};\ng.O=function(a,b,c){return t(N.c?N.c(nn,b):N.call(null,nn,b))?new by(c,this.v,this.j,null):new by(this.position,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,1,5,T,[new R(null,2,5,T,[nn,this.position],null)],null),this.j))};g.T=function(a,b){return new by(this.position,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function cy(a){return new by(a,null,null,null)}\nfunction dy(a,b,c){this.v=a;this.j=b;this.w=c;this.m=2229667594;this.J=139264}g=dy.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){return D.l(this.j,b,c)};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#asciinema.player.messages.SpeedDown{\",\", \",\"}\",c,O.c(he,this.j))};g.ba=function(){return new fh(0,this,0,he,t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 0+H(this.j)};\ng.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 1945704126^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.j,b.j)};g.ga=function(a,b){return He(vi,b)?le.c(tc(wg.c(Ef,this),this.v),b):new dy(this.v,Bf(le.c(this.j,b)),null)};g.O=function(a,b,c){return new dy(this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(he,this.j))};\ng.T=function(a,b){return new dy(b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function ey(a,b,c){this.v=a;this.j=b;this.w=c;this.m=2229667594;this.J=139264}g=ey.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){return D.l(this.j,b,c)};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#asciinema.player.messages.SpeedUp{\",\", \",\"}\",c,O.c(he,this.j))};\ng.ba=function(){return new fh(0,this,0,he,t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 0+H(this.j)};g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 2001377313^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.j,b.j)};g.ga=function(a,b){return He(vi,b)?le.c(tc(wg.c(Ef,this),this.v),b):new ey(this.v,Bf(le.c(this.j,b)),null)};\ng.O=function(a,b,c){return new ey(this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(he,this.j))};g.T=function(a,b){return new ey(b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function fy(a,b,c){this.v=a;this.j=b;this.w=c;this.m=2229667594;this.J=139264}g=fy.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){return D.l(this.j,b,c)};\ng.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#asciinema.player.messages.TogglePlay{\",\", \",\"}\",c,O.c(he,this.j))};g.ba=function(){return new fh(0,this,0,he,t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 0+H(this.j)};g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 1662385780^Dd(a)}}(b,a)(a)}();return this.w=c};\ng.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.j,b.j)};g.ga=function(a,b){return He(vi,b)?le.c(tc(wg.c(Ef,this),this.v),b):new fy(this.v,Bf(le.c(this.j,b)),null)};g.O=function(a,b,c){return new fy(this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(he,this.j))};g.T=function(a,b){return new fy(b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};\nfunction gy(a,b,c,d){this.show=a;this.v=b;this.j=c;this.w=d;this.m=2229667594;this.J=139264}g=gy.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case \"show\":return this.show;default:return D.l(this.j,b,c)}};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#asciinema.player.messages.ShowCursor{\",\", \",\"}\",c,O.c(new R(null,1,5,T,[new R(null,2,5,T,[so,this.show],null)],null),this.j))};\ng.ba=function(){return new fh(0,this,1,new R(null,1,5,T,[so],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 1+H(this.j)};g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 1380979759^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.show,b.show)&&G.c(this.j,b.j)};\ng.ga=function(a,b){return He(new ti(null,new r(null,1,[so,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new gy(this.show,this.v,Bf(le.c(this.j,b)),null)};g.O=function(a,b,c){return t(N.c?N.c(so,b):N.call(null,so,b))?new gy(c,this.v,this.j,null):new gy(this.show,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,1,5,T,[new R(null,2,5,T,[so,this.show],null)],null),this.j))};g.T=function(a,b){return new gy(this.show,b,this.j,this.w)};\ng.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function hy(a,b,c,d){this.show=a;this.v=b;this.j=c;this.w=d;this.m=2229667594;this.J=139264}g=hy.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case \"show\":return this.show;default:return D.l(this.j,b,c)}};\ng.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#asciinema.player.messages.ShowHud{\",\", \",\"}\",c,O.c(new R(null,1,5,T,[new R(null,2,5,T,[so,this.show],null)],null),this.j))};g.ba=function(){return new fh(0,this,1,new R(null,1,5,T,[so],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 1+H(this.j)};\ng.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-1875838466^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.show,b.show)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,1,[so,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new hy(this.show,this.v,Bf(le.c(this.j,b)),null)};\ng.O=function(a,b,c){return t(N.c?N.c(so,b):N.call(null,so,b))?new hy(c,this.v,this.j,null):new hy(this.show,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,1,5,T,[new R(null,2,5,T,[so,this.show],null)],null),this.j))};g.T=function(a,b){return new hy(this.show,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function iy(a){return new hy(a,null,null,null)}\nfunction jy(a,b,c,d,e,f){this.width=a;this.height=b;this.duration=c;this.v=d;this.j=e;this.w=f;this.m=2229667594;this.J=139264}g=jy.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case \"width\":return this.width;case \"height\":return this.height;case \"duration\":return this.duration;default:return D.l(this.j,b,c)}};\ng.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#asciinema.player.messages.SetMetadata{\",\", \",\"}\",c,O.c(new R(null,3,5,T,[new R(null,2,5,T,[fl,this.width],null),new R(null,2,5,T,[no,this.height],null),new R(null,2,5,T,[wl,this.duration],null)],null),this.j))};g.ba=function(){return new fh(0,this,3,new R(null,3,5,T,[fl,no,wl],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 3+H(this.j)};\ng.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 2110730596^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.width,b.width)&&G.c(this.height,b.height)&&G.c(this.duration,b.duration)&&G.c(this.j,b.j)};\ng.ga=function(a,b){return He(new ti(null,new r(null,3,[fl,null,wl,null,no,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new jy(this.width,this.height,this.duration,this.v,Bf(le.c(this.j,b)),null)};\ng.O=function(a,b,c){return t(N.c?N.c(fl,b):N.call(null,fl,b))?new jy(c,this.height,this.duration,this.v,this.j,null):t(N.c?N.c(no,b):N.call(null,no,b))?new jy(this.width,c,this.duration,this.v,this.j,null):t(N.c?N.c(wl,b):N.call(null,wl,b))?new jy(this.width,this.height,c,this.v,this.j,null):new jy(this.width,this.height,this.duration,this.v,K.l(this.j,b,c),null)};\ng.S=function(){return E(O.c(new R(null,3,5,T,[new R(null,2,5,T,[fl,this.width],null),new R(null,2,5,T,[no,this.height],null),new R(null,2,5,T,[wl,this.duration],null)],null),this.j))};g.T=function(a,b){return new jy(this.width,this.height,this.duration,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function ky(a,b,c,d){this.tc=a;this.v=b;this.j=c;this.w=d;this.m=2229667594;this.J=139264}g=ky.prototype;g.V=function(a,b){return this.I(null,b,null)};\ng.I=function(a,b,c){switch(b instanceof L?b.ea:null){case \"loading\":return this.tc;default:return D.l(this.j,b,c)}};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#asciinema.player.messages.SetLoading{\",\", \",\"}\",c,O.c(new R(null,1,5,T,[new R(null,2,5,T,[Hm,this.tc],null)],null),this.j))};g.ba=function(){return new fh(0,this,1,new R(null,1,5,T,[Hm],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 1+H(this.j)};\ng.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 1609009220^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.tc,b.tc)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,1,[Hm,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new ky(this.tc,this.v,Bf(le.c(this.j,b)),null)};\ng.O=function(a,b,c){return t(N.c?N.c(Hm,b):N.call(null,Hm,b))?new ky(c,this.v,this.j,null):new ky(this.tc,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,1,5,T,[new R(null,2,5,T,[Hm,this.tc],null)],null),this.j))};g.T=function(a,b){return new ky(this.tc,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function ly(a){return new ky(a,null,null,null)}\nfunction my(a,b,c,d){this.uc=a;this.v=b;this.j=c;this.w=d;this.m=2229667594;this.J=139264}g=my.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case \"playing\":return this.uc;default:return D.l(this.j,b,c)}};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#asciinema.player.messages.SetPlaying{\",\", \",\"}\",c,O.c(new R(null,1,5,T,[new R(null,2,5,T,[jn,this.uc],null)],null),this.j))};\ng.ba=function(){return new fh(0,this,1,new R(null,1,5,T,[jn],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 1+H(this.j)};g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-2119286176^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.uc,b.uc)&&G.c(this.j,b.j)};\ng.ga=function(a,b){return He(new ti(null,new r(null,1,[jn,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new my(this.uc,this.v,Bf(le.c(this.j,b)),null)};g.O=function(a,b,c){return t(N.c?N.c(jn,b):N.call(null,jn,b))?new my(c,this.v,this.j,null):new my(this.uc,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,1,5,T,[new R(null,2,5,T,[jn,this.uc],null)],null),this.j))};g.T=function(a,b){return new my(this.uc,b,this.j,this.w)};\ng.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function ny(a){return new my(a,null,null,null)}function oy(a,b,c){this.v=a;this.j=b;this.w=c;this.m=2229667594;this.J=139264}g=oy.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){return D.l(this.j,b,c)};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#asciinema.player.messages.TriggerCanPlay{\",\", \",\"}\",c,O.c(he,this.j))};\ng.ba=function(){return new fh(0,this,0,he,t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 0+H(this.j)};g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-1080034109^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.j,b.j)};g.ga=function(a,b){return He(vi,b)?le.c(tc(wg.c(Ef,this),this.v),b):new oy(this.v,Bf(le.c(this.j,b)),null)};\ng.O=function(a,b,c){return new oy(this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(he,this.j))};g.T=function(a,b){return new oy(b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function py(a,b,c,d){this.screen=a;this.v=b;this.j=c;this.w=d;this.m=2229667594;this.J=139264}g=py.prototype;g.V=function(a,b){return this.I(null,b,null)};\ng.I=function(a,b,c){switch(b instanceof L?b.ea:null){case \"screen\":return this.screen;default:return D.l(this.j,b,c)}};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#asciinema.player.messages.UpdateScreen{\",\", \",\"}\",c,O.c(new R(null,1,5,T,[new R(null,2,5,T,[V,this.screen],null)],null),this.j))};g.ba=function(){return new fh(0,this,1,new R(null,1,5,T,[V],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 1+H(this.j)};\ng.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-1861248332^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.screen,b.screen)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,1,[V,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new py(this.screen,this.v,Bf(le.c(this.j,b)),null)};\ng.O=function(a,b,c){return t(N.c?N.c(V,b):N.call(null,V,b))?new py(c,this.v,this.j,null):new py(this.screen,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,1,5,T,[new R(null,2,5,T,[V,this.screen],null)],null),this.j))};g.T=function(a,b){return new py(this.screen,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function qy(a){return new py(a,null,null,null)}\nfunction ry(a,b,c,d){this.time=a;this.v=b;this.j=c;this.w=d;this.m=2229667594;this.J=139264}g=ry.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case \"time\":return this.time;default:return D.l(this.j,b,c)}};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#asciinema.player.messages.UpdateTime{\",\", \",\"}\",c,O.c(new R(null,1,5,T,[new R(null,2,5,T,[Zk,this.time],null)],null),this.j))};\ng.ba=function(){return new fh(0,this,1,new R(null,1,5,T,[Zk],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 1+H(this.j)};g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 463038319^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.time,b.time)&&G.c(this.j,b.j)};\ng.ga=function(a,b){return He(new ti(null,new r(null,1,[Zk,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new ry(this.time,this.v,Bf(le.c(this.j,b)),null)};g.O=function(a,b,c){return t(N.c?N.c(Zk,b):N.call(null,Zk,b))?new ry(c,this.v,this.j,null):new ry(this.time,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,1,5,T,[new R(null,2,5,T,[Zk,this.time],null)],null),this.j))};g.T=function(a,b){return new ry(this.time,b,this.j,this.w)};\ng.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function sy(a){return new ry(a,null,null,null)};var ty=function ty(a){if(null!=a&&null!=a.Bd)return a.Bd(a);var c=ty[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=ty._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"Source.init\",a);},uy=function uy(a){if(null!=a&&null!=a.Ad)return a.Ad(a);var c=uy[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=uy._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"Source.close\",a);},vy=function vy(a){if(null!=a&&null!=a.ac)return a.ac(a);var c=vy[n(null==a?null:a)];\nif(null!=c)return c.h?c.h(a):c.call(null,a);c=vy._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"Source.start\",a);},wy=function wy(a){if(null!=a&&null!=a.wc)return a.wc(a);var c=wy[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=wy._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb(\"Source.stop\",a);},xy=function xy(a){if(null!=a&&null!=a.Dd)return a.Dd(a);var c=xy[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=xy._;if(null!=c)return c.h?c.h(a):c.call(null,\na);throw Cb(\"Source.toggle\",a);},yy=function yy(a,b){if(null!=a&&null!=a.Cd)return a.Cd(a,b);var d=yy[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=yy._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb(\"Source.seek\",a);},zy=function zy(a,b){if(null!=a&&null!=a.zd)return a.zd(a,b);var d=zy[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=zy._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb(\"Source.change-speed\",a);};\nif(\"undefined\"===typeof xj)var xj=function(){var a=dg.h(Ef),b=dg.h(Ef),c=dg.h(Ef),d=dg.h(Ef),e=D.l(Ef,Qn,jj());return new uj(td.c(\"asciinema.player.source\",\"make-source\"),function(){return function(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b;c=D.c(c,rl);return t(c)?c:ok}}(a,b,c,d,e),Ik,e,a,b,c,d)}();function Ay(){return ig.c(function(a){return function(b){b*=a;return new R(null,2,5,T,[b,b],null)}}(1/3),Fi(0,Number.MAX_VALUE,1))}\nfunction By(a){var b=Kx(null),c=Lx(new fx(ex),null),d=Kx(1);lx(function(b,c,d){return function(){var e=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(I){if(I instanceof Object)b[5]=I,Cx(b),d=Z;else throw I;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error(\"Invalid arity: \"+\n(arguments.length-1));};d.B=c;d.h=b;return d}()}(function(b,c,d){return function(e){var f=e[1];if(1===f)return zx(e,2,c);if(2===f){var h=e[2],k=function(){return function(a,b,c,d,e){return function(a){return Ox(e,a)}}(h,f,b,c,d)}();k=a.h?a.h(k):a.call(null,k);e[7]=h;return Bx(e,k)}return null}}(b,c,d),b,c,d)}(),f=function(){var a=e.B?e.B():e.call(null);a[6]=b;return a}();return yx(f)}}(d,b,c));return function(a,b){return function(c){t(c)&&Tw(a);return b}}(b,c)}\nfunction Cy(a,b,c,d){return By(function(e){if(\"string\"===typeof a)return Ew(a,function(){return function(a){a=a.target;try{var f=a.ca?a.ca.responseText:\"\"}catch(l){f=\"\"}f=pv(f,b,c,d);return e.h?e.h(f):e.call(null,f)}}(a));var f=pv(a,b,c,d);return e.h?e.h(f):e.call(null,f)})}\nfunction Dy(a){var b=Kx(null),c=Kx(1);lx(function(b,c){return function(){var d=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(C){if(C instanceof Object)b[5]=C,Cx(b),d=Z;else throw C;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,\na)}throw Error(\"Invalid arity: \"+(arguments.length-1));};d.B=c;d.h=b;return d}()}(function(b,c){return function(b){var d=b[1];if(7===d)return d=Jx(1E3*b[7]),zx(b,10,d);if(1===d){d=Ou(1);var e=d.B?d.B():d.call(null),f=a;b[8]=d;b[9]=e;b[10]=f;b[2]=null;b[1]=2;return Z}return 4===d?(e=b[11],d=b[9],f=J(e,0,null),e=J(e,1,null),d=f-d,b[12]=e,b[7]=d,b[1]=t(0<d)?7:8,Z):15===d?(b[1]=t(b[2])?16:17,Z):13===d?(b[2]=null,b[1]=14,Z):6===d?(b[2]=b[2],b[1]=3,Z):17===d?(b[2]=null,b[1]=18,Z):3===d?Bx(b,b[2]):12===\nd?(d=b[8],f=b[10],f=vd(f),d=d.B?d.B():d.call(null),b[9]=d,b[10]=f,b[2]=null,b[1]=2,Z):2===d?(f=b[10],d=y(f),b[11]=d,b[1]=t(d)?4:5,Z):11===d?(b[1]=t(b[2])?12:13,Z):9===d?(b[2]=b[2],b[1]=6,Z):5===d?(d=Tw(c),b[2]=d,b[1]=6,Z):14===d?(b[2]=b[2],b[1]=9,Z):16===d?(d=b[9],f=b[10],f=vd(f),b[9]=d,b[10]=f,b[2]=null,b[1]=2,Z):10===d?(e=b[12],b[13]=b[2],Ax(b,11,c,e)):18===d?(b[2]=b[2],b[1]=9,Z):8===d?(e=b[12],Ax(b,15,c,e)):null}}(b,c),b,c)}(),e=function(){var a=d.B?d.B():d.call(null);a[6]=b;return a}();return yx(e)}}(c,\nb));return b}\nfunction Ey(a,b,c,d,e,f){var h=Kx(1);lx(function(h){return function(){var k=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(M){if(M instanceof Object)b[5]=M,Cx(b),d=Z;else throw M;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error(\"Invalid arity: \"+\n(arguments.length-1));};d.B=c;d.h=b;return d}()}(function(){return function(h){var k=h[1];if(7===k)return h[2]=h[2],h[1]=3,Z;if(1===k){k=Yu(c,d,b);var l=Dy(k);k=c;var m=Ou(d);h[7]=l;h[8]=m;h[9]=k;h[2]=null;h[1]=2;return Z}if(4===k)return l=h[7],m=h[2],k=J(m,0,null),m=J(m,1,null),l=G.c(l,m),h[11]=k,h[10]=m,h[1]=l?5:6,Z;if(15===k)return l=h[7],m=h[8],k=h[9],l=Tw(l),m=m.B?m.B():m.call(null),h[12]=l,h[2]=k+m,h[1]=17,Z;if(13===k)return h[2]=null,h[1]=14,Z;if(6===k)return k=h[10],k=G.c(f,k),h[1]=k?15:16,\nZ;if(17===k)return h[2]=h[2],h[1]=7,Z;if(3===k)return Bx(h,h[2]);if(12===k)return k=Yu(0,d,b),k=Dy(k),l=Ou(d),h[7]=k,h[8]=l,h[9]=0,h[2]=null,h[1]=2,Z;if(2===k)return l=h[7],Ux(h,4,new R(null,2,5,T,[l,f],null));if(11===k)return l=h[7],m=h[8],k=h[9],h[13]=h[2],h[7]=l,h[8]=m,h[9]=k,h[2]=null,h[1]=2,Z;if(9===k)return h[1]=t(e)?12:13,Z;if(5===k)return k=h[11],h[1]=t(k)?8:9,Z;if(14===k)return h[2]=h[2],h[1]=10,Z;if(16===k)throw k=h[10],h=[\"No matching clause: \",v.h(k)].join(\"\"),Error(h);return 10===k?(h[2]=\nh[2],h[1]=7,Z):8===k?(k=h[11],Ax(h,11,a,k)):null}}(h),h)}(),p=function(){var a=k.B?k.B():k.call(null);a[6]=h;return a}();return yx(p)}}(h));return h}\nfunction Fy(a,b,c,d,e,f,h){var k=Kx(1);lx(function(k){return function(){var l=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(S){if(S instanceof Object)b[5]=S,Cx(b),d=Z;else throw S;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error(\"Invalid arity: \"+\n(arguments.length-1));};d.B=c;d.h=b;return d}()}(function(){return function(k){var l=k[1];if(7===l)return l=k[7],k[2]=l,k[1]=9,Z;if(1===l)return Ax(k,2,a,ny(!0));if(4===l){l=k[2];var m=Su(qy,b),p=Ay();p=Su(sy,p);m=Ey(a,Tu(m,p),d,e,f,h);k[8]=l;return zx(k,5,m)}return 6===l?(l=ny(!1),k[9]=k[2],Ax(k,10,a,l)):3===l?(l=k[2],m=fe($u(d,b)),m=qy(m),k[10]=l,Ax(k,4,a,m)):2===l?(l=sy(d),k[11]=k[2],Ax(k,3,a,l)):9===l?Ax(k,6,a,sy(k[2])):5===l?(l=k[2],k[7]=l,k[1]=t(l)?7:8,Z):10===l?(l=k[7],k[12]=k[2],Bx(k,l)):\n8===l?(k[2]=c,k[1]=9,Z):null}}(k),k)}(),m=function(){var a=l.B?l.B():l.call(null);a[6]=k;return a}();return yx(m)}}(k));return k}\nfunction Gy(a,b,c){var d=null!=a&&(a.m&64||q===a.G)?P(U,a):a,e=D.c(d,Hk),f=D.c(d,Ak),h=D.c(d,dn),k=D.c(d,Jl),l=Kx(10),p=Kx(1);lx(function(a,d,e,f,h,k,l,p){return function(){var m=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(Ba){if(Ba instanceof Object)b[5]=Ba,Cx(b),d=Z;else throw Ba;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,\nnull,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error(\"Invalid arity: \"+(arguments.length-1));};d.B=c;d.h=b;return d}()}(function(a,d,e,f,h,k,l,m){return function(a){var e=a[1];if(65===e){var f=a,p=f;p[2]=a[2];p[1]=62;return Z}if(70===e){var u=f=a;u[2]=!1;u[1]=71;return Z}if(62===e){var w=a[2],x=f=a;x[2]=w;x[1]=59;return Z}if(74===e){var C=a[7],F=a[8],I=a[2],M=D.c(I,Uk),S=D.c(I,\nwl),Q=Nu(F,S),X=sy(Q);a[7]=Q;a[9]=M;f=a;return Ax(f,75,b,X)}if(7===e){var W=a[10],Ha=a[2],Ba=J(Ha,0,null);F=J(Ha,1,null);var Ga=G.c(gl,Ba);a[8]=F;a[10]=Ba;f=a;f[1]=Ga?8:9;return Z}if(59===e){var Oa=a[2],Ja=f=a;Ja[2]=Oa;Ja[1]=51;return Z}if(20===e){var db=P(U,c),xb=f=a;xb[2]=db;xb[1]=22;return Z}if(72===e){var az=P(U,c),ps=f=a;ps[2]=az;ps[1]=74;return Z}if(58===e){W=a[10];var bz=G.c(Xj,W);f=a;f[1]=bz?60:61;return Z}if(60===e){var ac=a[11],bc=0,Dc=ac,Eb=null,bb=null;a[11]=Dc;a[12]=bb;a[13]=bc;a[14]=\nEb;var qs=f=a;qs[2]=null;qs[1]=2;return Z}if(27===e){ac=a[11];bb=a[12];bc=a[13];Eb=a[14];var cz=bb,dz=Eb,Rd=bc;Dc=ac;var Sd=dz,Td=cz;a[11]=Dc;a[12]=Td;a[13]=Rd;a[14]=Sd;var rs=f=a;rs[2]=null;rs[1]=2;return Z}if(1===e){bc=h;ac=k;bb=Eb=null;a[11]=ac;a[12]=bb;a[13]=bc;a[14]=Eb;var ss=f=a;ss[2]=null;ss[1]=2;return Z}if(69===e){var ts=f=a;ts[2]=!0;ts[1]=71;return Z}if(24===e){W=a[10];var ez=G.c(Nl,W);f=a;f[1]=ez?30:31;return Z}if(55===e){var fz=new R(null,1,5,T,[gl],null);a[15]=a[2];f=a;return Ax(f,56,\nd,fz)}if(39===e){var gz=a[2],us=f=a;us[2]=gz;us[1]=32;return Z}if(46===e){var vs=f=a;vs[2]=null;vs[1]=47;return Z}if(4===e){Eb=a[14];var ws=a[2],Ci=J(ws,0,null),hz=J(ws,1,null),iz=G.c(hz,Eb);a[16]=Ci;f=a;f[1]=iz?5:6;return Z}if(54===e){F=a[8];bb=a[12];bc=a[13];Eb=a[14];var jz=a[2],kz=bb,lz=Eb;Rd=bc;ac=F;Sd=lz;Td=kz;a[11]=ac;a[17]=jz;a[12]=Td;a[13]=Rd;a[14]=Sd;var xs=f=a;xs[2]=null;xs[1]=2;return Z}if(15===e){var ys=f=a;ys[2]=!1;ys[1]=16;return Z}if(48===e){var mz=a[2],zs=f=a;zs[2]=mz;zs[1]=47;return Z}if(50===\ne){W=a[10];var nz=G.c(qk,W);f=a;f[1]=nz?57:58;return Z}if(75===e){C=a[7];M=a[9];var oz=a[2],pz=fe($u(C,M)),qz=qy(pz);a[18]=oz;f=a;return Ax(f,76,b,qz)}if(21===e){var As=f=a;As[2]=c;As[1]=22;return Z}if(31===e){W=a[10];var rz=G.c(Kn,W);f=a;f[1]=rz?37:38;return Z}if(32===e){var sz=a[2],Bs=f=a;Bs[2]=sz;Bs[1]=25;return Z}if(40===e){var tz=new R(null,1,5,T,[wm],null);f=a;return Ax(f,43,d,tz)}if(56===e){var uz=a[2],Cs=f=a;Cs[2]=uz;Cs[1]=54;return Z}if(33===e){var Ds=f=a;Ds[2]=wm;Ds[1]=35;return Z}if(13===\ne){var vz=a[2],Es=f=a;Es[2]=vz;Es[1]=10;return Z}if(22===e){ac=a[11];bc=a[13];var Fs=a[2],wz=D.c(Fs,Uk),xz=D.c(Fs,wl),Gs=Kx(null),yz=Fy(b,wz,xz,bc,ac,l,Gs),zz=ac;Rd=null;Dc=zz;Eb=yz;bb=Gs;a[11]=Dc;a[12]=bb;a[13]=Rd;a[14]=Eb;var Hs=f=a;Hs[2]=null;Hs[1]=2;return Z}if(36===e){ac=a[11];bb=a[12];bc=a[13];Eb=a[14];var Az=a[2],Bz=ac,Cz=bb,Dz=Eb;Rd=bc;Dc=Bz;Sd=Dz;Td=Cz;a[11]=Dc;a[19]=Az;a[12]=Td;a[13]=Rd;a[14]=Sd;var Is=f=a;Is[2]=null;Is[1]=2;return Z}if(41===e){var Js=f=a;Js[2]=null;Js[1]=42;return Z}if(43===\ne){var Ez=a[2],Ks=f=a;Ks[2]=Ez;Ks[1]=42;return Z}if(61===e){W=a[10];var Fz=G.c(go,W);f=a;f[1]=Fz?63:64;return Z}if(29===e){var Gz=ac=a[11];bc=a[2];Dc=Gz;bb=Eb=null;a[11]=Dc;a[12]=bb;a[13]=bc;a[14]=Eb;var Ls=f=a;Ls[2]=null;Ls[1]=2;return Z}if(44===e)return bb=a[12],a[20]=a[2],f=a,f[1]=t(bb)?45:46,Z;if(6===e){Ci=a[16];var Ms=f=a;Ms[2]=Ci;Ms[1]=7;return Z}if(28===e){var Hz=a[2],Ns=f=a;Ns[2]=Hz;Ns[1]=25;return Z}if(64===e){W=a[10];var Iz=[\"No matching clause: \",v.h(W)].join(\"\");throw Error(Iz);}if(51===\ne){var Jz=a[2],Os=f=a;Os[2]=Jz;Os[1]=39;return Z}if(25===e){var Kz=a[2],Ps=f=a;Ps[2]=Kz;Ps[1]=10;return Z}if(34===e){var Qs=f=a;Qs[2]=gl;Qs[1]=35;return Z}if(17===e){var Rs=f=a;Rs[2]=!0;Rs[1]=19;return Z}if(3===e){var Lz=a[2];f=a;return Bx(f,Lz)}if(12===e){var Mz=wb(null==c);f=a;f[1]=Mz?14:15;return Z}if(2===e){Eb=a[14];var Nz=vg(ub,new R(null,3,5,T,[d,m,Eb],null));f=a;return Vx(f,4,Nz,be([Im,!0]))}if(66===e){var Oz=q===c.G,Pz=c.m&64||Oz;f=a;f[1]=t(Pz)?69:70;return Z}if(23===e)return bb=a[12],f=a,\nf[1]=t(bb)?26:27,Z;if(47===e){ac=a[11];bb=a[12];bc=a[13];Eb=a[14];var Qz=a[2],Rz=ac,Sz=bb,Tz=Eb;Rd=bc;Dc=Rz;Sd=Tz;Td=Sz;a[11]=Dc;a[21]=Qz;a[12]=Td;a[13]=Rd;a[14]=Sd;var Ss=f=a;Ss[2]=null;Ss[1]=2;return Z}if(35===e){var Uz=new R(null,1,5,T,[a[2]],null);f=a;return Ax(f,36,d,Uz)}if(76===e){ac=a[11];C=a[7];bb=a[12];Eb=a[14];var Vz=a[2],Wz=ac,Xz=bb,Yz=Eb;bc=C;Dc=Wz;Sd=Yz;Td=Xz;a[11]=Dc;a[22]=Vz;a[12]=Td;a[13]=bc;a[14]=Sd;var Ts=f=a;Ts[2]=null;Ts[1]=2;return Z}if(19===e){var Zz=a[2],Us=f=a;Us[2]=Zz;Us[1]=\n16;return Z}if(57===e){var Vs=f=a;Vs[2]=null;Vs[1]=59;return Z}if(68===e){var $z=a[2];f=a;f[1]=t($z)?72:73;return Z}if(11===e){ac=a[11];bb=a[12];bc=a[13];Eb=a[14];var aA=ac,bA=bb,cA=Eb;Rd=bc;Dc=aA;Sd=cA;Td=bA;a[11]=Dc;a[12]=Td;a[13]=Rd;a[14]=Sd;var Ws=f=a;Ws[2]=null;Ws[1]=2;return Z}if(9===e){W=a[10];var dA=G.c(wm,W);f=a;f[1]=dA?23:24;return Z}if(5===e){Ci=a[16];var eA=new R(null,2,5,T,[Xj,Ci],null),Xs=f=a;Xs[2]=eA;Xs[1]=7;return Z}if(14===e){var fA=q===c.G,gA=c.m&64||fA;f=a;f[1]=t(gA)?17:18;return Z}if(45===\ne){var hA=new R(null,1,5,T,[gl],null);f=a;return Ax(f,48,d,hA)}if(53===e){var Ys=f=a;Ys[2]=null;Ys[1]=54;return Z}if(26===e){bb=a[12];Eb=a[14];var iA=Tw(bb);a[23]=iA;f=a;return zx(f,29,Eb)}if(16===e){var jA=a[2];f=a;f[1]=t(jA)?20:21;return Z}if(38===e){W=a[10];var kA=G.c(lm,W);f=a;f[1]=kA?49:50;return Z}if(30===e)return bb=a[12],f=a,f[1]=t(bb)?33:34,Z;if(73===e){var Zs=f=a;Zs[2]=c;Zs[1]=74;return Z}if(10===e){var lA=a[2],$s=f=a;$s[2]=lA;$s[1]=3;return Z}if(18===e){var at=f=a;at[2]=!1;at[1]=19;return Z}if(52===\ne){var mA=new R(null,1,5,T,[wm],null);f=a;return Ax(f,55,d,mA)}if(67===e){var bt=f=a;bt[2]=!1;bt[1]=68;return Z}if(71===e){var nA=a[2],ct=f=a;ct[2]=nA;ct[1]=68;return Z}if(42===e){F=a[8];var oA=new R(null,2,5,T,[go,F],null);a[24]=a[2];f=a;return Ax(f,44,d,oA)}if(37===e)return bb=a[12],f=a,f[1]=t(bb)?40:41,Z;if(63===e){var pA=wb(null==c);f=a;f[1]=pA?66:67;return Z}return 8===e?(bb=a[12],f=a,f[1]=t(bb)?11:12,Z):49===e?(bb=a[12],f=a,f[1]=t(bb)?52:53,Z):null}}(a,d,e,f,h,k,l,p),a,d,e,f,h,k,l,p)}(),u=function(){var b=\nm.B?m.B():m.call(null);b[6]=a;return b}();return yx(u)}}(p,l,a,d,e,f,h,k));return p}function Hy(a){var b=window.requestIdleCallback;return t(b)?(a=function(a,b){return function h(c){return function(a){return function(){if(E(c)){var b=h(vd(c));return a.h?a.h(b):a.call(null,b)}return null}}(a,b)}}(b,b)(a),b.h?b.h(a):b.call(null,a)):null}\nfunction Iy(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,bl),e=D.c(c,Jl),f=D.c(c,Hj),h=D.c(c,Sj),k=D.c(c,Mm),l=Kx(1);lx(function(a,c,d,e,f,h,k,l,M){return function(){var m=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(Ba){if(Ba instanceof Object)b[5]=Ba,Cx(b),d=Z;else throw Ba;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];\na[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error(\"Invalid arity: \"+(arguments.length-1));};d.B=c;d.h=b;return d}()}(function(a,c,d,e,f,h,k,l,m){return function(a){var c=a[1];if(7===c)return c=a[7],c=G.c(k,c),a[1]=c?9:10,Z;if(1===c)return a[1]=t(l)?2:3,Z;if(4===c){c=a[2];c=f.h?f.h(c):f.call(null,c);var d=new R(null,2,5,T,[c,k],null);a[8]=c;return Ux(a,5,d)}if(15===c)return c=d=a[9],c=null!=c&&(c.m&64||q===c.G)?\nP(U,c):c,c=D.c(c,Uk),c=Ox(b,qy(fe($u(m,c)))),a[2]=c,a[1]=17,Z;if(13===c)return c=a[2],d=ly(!1),a[10]=c,Ax(a,14,b,d);if(6===c)return c=a[11],a[2]=c,a[1]=8,Z;if(17===c){d=a[9];c=a[2];var h=d;var p=null!=h&&(h.m&64||q===h.G)?P(U,h):h;h=D.c(p,fl);var u=D.c(p,no);p=D.c(p,wl);Ox(b,new jy(h,u,p,null,null,null));h=Ox(b,new oy(null,null,null));u=Gy(e,b,d);d=Uk.h(d);d=Hy(d);a[12]=c;a[13]=u;a[14]=h;return Bx(a,d)}if(3===c)return a[2]=m,a[1]=4,Z;if(12===c)return c=a[2],d=f.h?f.h(!0):f.call(null,!0),a[15]=c,zx(a,\n13,d);if(2===c)return a[2]=l,a[1]=4,Z;if(11===c)return a[2]=a[2],a[1]=8,Z;if(9===c)return Ax(a,12,b,ly(!0));if(5===c)return d=a[8],h=a[2],c=J(h,0,null),h=J(h,1,null),d=G.c(d,h),a[7]=h,a[11]=c,a[1]=d?6:7,Z;if(14===c)return c=a[10],a[16]=a[2],a[2]=c,a[1]=11,Z;if(16===c)return a[2]=null,a[1]=17,Z;if(10===c)throw c=a[7],a=[\"No matching clause: \",v.h(c)].join(\"\"),Error(a);return 8===c?(d=a[2],a[9]=d,a[1]=t(m)?15:16,Z):null}}(a,c,d,e,f,h,k,l,M),a,c,d,e,f,h,k,l,M)}(),p=function(){var b=m.B?m.B():m.call(null);\nb[6]=a;return b}();return yx(p)}}(l,a,c,c,d,e,f,h,k))}function Jy(a,b,c,d,e,f,h,k,l,p,m,u){this.nb=a;this.Ea=b;this.$a=c;this.ob=d;this.speed=e;this.Y=f;this.jb=h;this.mb=k;this.lb=l;this.v=p;this.j=m;this.w=u;this.m=2229667594;this.J=139264}g=Jy.prototype;g.Bd=function(){var a=Kx(null);Iy(this,a);t(this.Y)&&this.ac(null);return a};g.Ad=function(){Ox(this.Ea,new R(null,1,5,T,[wm],null));return Ox(this.Ea,new R(null,1,5,T,[qk],null))};\ng.ac=function(){Tw(this.$a);return Ox(this.Ea,new R(null,1,5,T,[gl],null))};g.wc=function(){return Ox(this.Ea,new R(null,1,5,T,[wm],null))};g.Dd=function(){Tw(this.$a);return Ox(this.Ea,new R(null,1,5,T,[Nl],null))};g.Cd=function(a,b){Tw(this.$a);return Ox(this.Ea,new R(null,2,5,T,[Kn,b],null))};g.zd=function(a,b){return Ox(this.Ea,new R(null,2,5,T,[lm,b],null))};g.V=function(a,b){return this.I(null,b,null)};\ng.I=function(a,b,c){switch(b instanceof L?b.ea:null){case \"recording-ch-fn\":return this.nb;case \"command-ch\":return this.Ea;case \"force-load-ch\":return this.$a;case \"start-at\":return this.ob;case \"speed\":return this.speed;case \"auto-play?\":return this.Y;case \"loop?\":return this.jb;case \"preload?\":return this.mb;case \"poster-time\":return this.lb;default:return D.l(this.j,b,c)}};\ng.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#asciinema.player.source.Recording{\",\", \",\"}\",c,O.c(new R(null,9,5,T,[new R(null,2,5,T,[bl,this.nb],null),new R(null,2,5,T,[Jl,this.Ea],null),new R(null,2,5,T,[Hj,this.$a],null),new R(null,2,5,T,[Hk,this.ob],null),new R(null,2,5,T,[Ak,this.speed],null),new R(null,2,5,T,[Jm,this.Y],null),new R(null,2,5,T,[dn,this.jb],null),new R(null,2,5,T,[Sj,this.mb],null),new R(null,2,5,T,[Mm,this.lb],null)],null),\nthis.j))};g.ba=function(){return new fh(0,this,9,new R(null,9,5,T,[bl,Jl,Hj,Hk,Ak,Jm,dn,Sj,Mm],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 9+H(this.j)};g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 1201370539^Dd(a)}}(b,a)(a)}();return this.w=c};\ng.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.nb,b.nb)&&G.c(this.Ea,b.Ea)&&G.c(this.$a,b.$a)&&G.c(this.ob,b.ob)&&G.c(this.speed,b.speed)&&G.c(this.Y,b.Y)&&G.c(this.jb,b.jb)&&G.c(this.mb,b.mb)&&G.c(this.lb,b.lb)&&G.c(this.j,b.j)};\ng.ga=function(a,b){return He(new ti(null,new r(null,9,[Hj,null,Sj,null,Ak,null,Hk,null,bl,null,Jl,null,Jm,null,Mm,null,dn,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new Jy(this.nb,this.Ea,this.$a,this.ob,this.speed,this.Y,this.jb,this.mb,this.lb,this.v,Bf(le.c(this.j,b)),null)};\ng.O=function(a,b,c){return t(N.c?N.c(bl,b):N.call(null,bl,b))?new Jy(c,this.Ea,this.$a,this.ob,this.speed,this.Y,this.jb,this.mb,this.lb,this.v,this.j,null):t(N.c?N.c(Jl,b):N.call(null,Jl,b))?new Jy(this.nb,c,this.$a,this.ob,this.speed,this.Y,this.jb,this.mb,this.lb,this.v,this.j,null):t(N.c?N.c(Hj,b):N.call(null,Hj,b))?new Jy(this.nb,this.Ea,c,this.ob,this.speed,this.Y,this.jb,this.mb,this.lb,this.v,this.j,null):t(N.c?N.c(Hk,b):N.call(null,Hk,b))?new Jy(this.nb,this.Ea,this.$a,c,this.speed,this.Y,\nthis.jb,this.mb,this.lb,this.v,this.j,null):t(N.c?N.c(Ak,b):N.call(null,Ak,b))?new Jy(this.nb,this.Ea,this.$a,this.ob,c,this.Y,this.jb,this.mb,this.lb,this.v,this.j,null):t(N.c?N.c(Jm,b):N.call(null,Jm,b))?new Jy(this.nb,this.Ea,this.$a,this.ob,this.speed,c,this.jb,this.mb,this.lb,this.v,this.j,null):t(N.c?N.c(dn,b):N.call(null,dn,b))?new Jy(this.nb,this.Ea,this.$a,this.ob,this.speed,this.Y,c,this.mb,this.lb,this.v,this.j,null):t(N.c?N.c(Sj,b):N.call(null,Sj,b))?new Jy(this.nb,this.Ea,this.$a,this.ob,\nthis.speed,this.Y,this.jb,c,this.lb,this.v,this.j,null):t(N.c?N.c(Mm,b):N.call(null,Mm,b))?new Jy(this.nb,this.Ea,this.$a,this.ob,this.speed,this.Y,this.jb,this.mb,c,this.v,this.j,null):new Jy(this.nb,this.Ea,this.$a,this.ob,this.speed,this.Y,this.jb,this.mb,this.lb,this.v,K.l(this.j,b,c),null)};\ng.S=function(){return E(O.c(new R(null,9,5,T,[new R(null,2,5,T,[bl,this.nb],null),new R(null,2,5,T,[Jl,this.Ea],null),new R(null,2,5,T,[Hj,this.$a],null),new R(null,2,5,T,[Hk,this.ob],null),new R(null,2,5,T,[Ak,this.speed],null),new R(null,2,5,T,[Jm,this.Y],null),new R(null,2,5,T,[dn,this.jb],null),new R(null,2,5,T,[Sj,this.mb],null),new R(null,2,5,T,[Mm,this.lb],null)],null),this.j))};\ng.T=function(a,b){return new Jy(this.nb,this.Ea,this.$a,this.ob,this.speed,this.Y,this.jb,this.mb,this.lb,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};wj(ok,function(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,fl),e=D.c(c,no),f=D.c(c,Hk),h=D.c(c,Ak),k=D.c(c,qm),l=D.c(c,Em),p=D.c(c,cm),m=D.c(c,vm);c=D.c(c,Mm);d=Cy(a,d,e,k);e=Kx(10);k=Kx(null);return new Jy(d,e,k,f,h,l,p,m,c,null,null,null)});\nfunction Ky(a,b,c){var d=Kx(null),e=Kx(1);lx(function(d,e){return function(){var f=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(I){if(I instanceof Object)b[5]=I,Cx(b),d=Z;else throw I;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error(\"Invalid arity: \"+\n(arguments.length-1));};d.B=c;d.h=b;return d}()}(function(d,e){return function(d){var f=d[1];if(1===f)return f=Ot(a,b),d[7]=f,d[2]=null,d[1]=2,Z;if(2===f)return zx(d,4,e);if(3===f)return Bx(d,d[2]);if(4===f)return f=d[2],d[8]=f,d[1]=t(f)?5:6,Z;if(5===f){var h=d[8];f=d[7];f=Ju(f,h);h=qy(f);d[9]=f;return Ax(d,8,c,h)}return 6===f?(d[2]=null,d[1]=7,Z):7===f?(d[2]=d[2],d[1]=3,Z):8===f?(f=d[9],h=d[2],d[10]=h,d[7]=f,d[2]=null,d[1]=2,Z):null}}(d,e),d,e)}(),h=function(){var a=f.B?f.B():f.call(null);a[6]=d;\nreturn a}();return yx(h)}}(e,d));return d}\nfunction Ly(a,b,c,d){var e=Kx(1);lx(function(e){return function(){var f=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(F){if(F instanceof Object)b[5]=F,Cx(b),d=Z;else throw F;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error(\"Invalid arity: \"+\n(arguments.length-1));};d.B=c;d.h=b;return d}()}(function(){return function(e){var f=e[1];if(7===f)return Ax(e,9,b,String.fromCharCode(Math.floor(160*Math.random())));if(1===f)return Ax(e,2,a,ny(!0));if(4===f)return f=ny(!1),e[7]=e[2],Ax(e,10,a,f);if(6===f)return e[2]=null,e[1]=8,Z;if(3===f)return f=Jx(100*Math.random()/c),Ux(e,5,new R(null,2,5,T,[d,f],null));if(2===f)return e[8]=e[2],e[2]=null,e[1]=3,Z;if(9===f)return e[9]=e[2],e[2]=null,e[1]=3,Z;if(5===f){var h=e[2];f=J(h,0,null);h=J(h,1,null);\nh=G.c(h,d);e[10]=f;e[1]=h?6:7;return Z}return 10===f?Bx(e,e[2]):8===f?(e[2]=e[2],e[1]=4,Z):null}}(e),e)}(),k=function(){var a=f.B?f.B():f.call(null);a[6]=e;return a}();return yx(k)}}(e));return e}function My(a,b,c,d,e,f,h,k,l,p){this.speed=a;this.Y=b;this.width=c;this.height=d;this.ha=e;this.pb=f;this.La=h;this.v=k;this.j=l;this.w=p;this.m=2229667594;this.J=139264}g=My.prototype;g.Bd=function(){fg(this.ha,Kx(null));fg(this.pb,Ky(this.width,this.height,B(this.ha)));t(this.Y)&&this.ac(null);return B(this.ha)};\ng.Ad=function(){return this.wc(null)};g.ac=function(){if(t(B(this.La)))return null;var a=Kx(null);fg(this.La,a);return Ly(B(this.ha),B(this.pb),this.speed,a)};g.wc=function(){return t(B(this.La))?(Tw(B(this.La)),fg(this.La,null)):null};g.Dd=function(){return t(B(this.La))?this.wc(null):this.ac(null)};g.Cd=function(){return null};g.zd=function(){return null};g.V=function(a,b){return this.I(null,b,null)};\ng.I=function(a,b,c){switch(b instanceof L?b.ea:null){case \"speed\":return this.speed;case \"auto-play?\":return this.Y;case \"width\":return this.width;case \"height\":return this.height;case \"msg-ch\":return this.ha;case \"stdout-ch\":return this.pb;case \"stop-ch\":return this.La;default:return D.l(this.j,b,c)}};\ng.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#asciinema.player.source.JunkPrinter{\",\", \",\"}\",c,O.c(new R(null,7,5,T,[new R(null,2,5,T,[Ak,this.speed],null),new R(null,2,5,T,[Jm,this.Y],null),new R(null,2,5,T,[fl,this.width],null),new R(null,2,5,T,[no,this.height],null),new R(null,2,5,T,[Dl,this.ha],null),new R(null,2,5,T,[rn,this.pb],null),new R(null,2,5,T,[Zl,this.La],null)],null),this.j))};\ng.ba=function(){return new fh(0,this,7,new R(null,7,5,T,[Ak,Jm,fl,no,Dl,rn,Zl],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 7+H(this.j)};g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 1937333797^Dd(a)}}(b,a)(a)}();return this.w=c};\ng.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.speed,b.speed)&&G.c(this.Y,b.Y)&&G.c(this.width,b.width)&&G.c(this.height,b.height)&&G.c(this.ha,b.ha)&&G.c(this.pb,b.pb)&&G.c(this.La,b.La)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,7,[Ak,null,fl,null,Dl,null,Zl,null,Jm,null,rn,null,no,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new My(this.speed,this.Y,this.width,this.height,this.ha,this.pb,this.La,this.v,Bf(le.c(this.j,b)),null)};\ng.O=function(a,b,c){return t(N.c?N.c(Ak,b):N.call(null,Ak,b))?new My(c,this.Y,this.width,this.height,this.ha,this.pb,this.La,this.v,this.j,null):t(N.c?N.c(Jm,b):N.call(null,Jm,b))?new My(this.speed,c,this.width,this.height,this.ha,this.pb,this.La,this.v,this.j,null):t(N.c?N.c(fl,b):N.call(null,fl,b))?new My(this.speed,this.Y,c,this.height,this.ha,this.pb,this.La,this.v,this.j,null):t(N.c?N.c(no,b):N.call(null,no,b))?new My(this.speed,this.Y,this.width,c,this.ha,this.pb,this.La,this.v,this.j,null):\nt(N.c?N.c(Dl,b):N.call(null,Dl,b))?new My(this.speed,this.Y,this.width,this.height,c,this.pb,this.La,this.v,this.j,null):t(N.c?N.c(rn,b):N.call(null,rn,b))?new My(this.speed,this.Y,this.width,this.height,this.ha,c,this.La,this.v,this.j,null):t(N.c?N.c(Zl,b):N.call(null,Zl,b))?new My(this.speed,this.Y,this.width,this.height,this.ha,this.pb,c,this.v,this.j,null):new My(this.speed,this.Y,this.width,this.height,this.ha,this.pb,this.La,this.v,K.l(this.j,b,c),null)};\ng.S=function(){return E(O.c(new R(null,7,5,T,[new R(null,2,5,T,[Ak,this.speed],null),new R(null,2,5,T,[Jm,this.Y],null),new R(null,2,5,T,[fl,this.width],null),new R(null,2,5,T,[no,this.height],null),new R(null,2,5,T,[Dl,this.ha],null),new R(null,2,5,T,[rn,this.pb],null),new R(null,2,5,T,[Zl,this.La],null)],null),this.j))};g.T=function(a,b){return new My(this.speed,this.Y,this.width,this.height,this.ha,this.pb,this.La,b,this.j,this.w)};\ng.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};wj(mn,function(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b;D.c(c,$m);var d=D.c(c,fl),e=D.c(c,no),f=D.c(c,Ak);c=D.c(c,Em);var h=dg.h(null),k=dg.h(null),l=dg.h(null);return new My(f,c,d,e,h,k,l,null,null,null)});function Ny(a){return ev(JSON.parse(a))}\nfunction Oy(a,b){var c=Kx(1);lx(function(c){return function(){var d=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(x){if(x instanceof Object)b[5]=x,Cx(b),d=Z;else throw x;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error(\"Invalid arity: \"+\n(arguments.length-1));};d.B=c;d.h=b;return d}()}(function(){return function(c){var d=c[1];if(7===d)return c[2]=!1,c[1]=8,Z;if(20===d)return c[2]=!1,c[1]=21,Z;if(27===d){d=c[7];var e=D.c(c[2],ko);return Ax(c,28,d,e)}if(1===d)return zx(c,2,a);if(24===d)return c[2]=c[2],c[1]=21,Z;if(4===d)return c[2]=!1,c[1]=5,Z;if(15===d)return d=c[2],c[8]=d,c[1]=t(d)?16:17,Z;if(21===d)return c[1]=t(c[2])?25:26,Z;if(13===d)return zx(c,15,a);if(22===d)return c[2]=!0,c[1]=24,Z;if(6===d)return c[2]=!0,c[1]=8,Z;if(28===\nd)return c[9]=c[2],c[2]=null,c[1]=13,Z;if(25===d)return d=c[8],d=P(U,d),c[2]=d,c[1]=27,Z;if(17===d)return c[2]=null,c[1]=18,Z;if(3===d)return d=c[10],e=q===d.G,c[1]=t(d.m&64||e)?6:7,Z;if(12===d)return c[11]=c[2],c[2]=null,c[1]=13,Z;if(2===d)return d=c[2],e=wb(null==d),c[10]=d,c[1]=e?3:4,Z;if(23===d)return c[2]=!1,c[1]=24,Z;if(19===d)return d=c[8],e=q===d.G,c[1]=t(d.m&64||e)?22:23,Z;if(11===d){var f=c[2];d=D.c(f,Zk);e=D.c(f,fl);var h=D.c(f,no);f=D.c(f,ko);e=Ky(e,h,b);c[7]=e;c[12]=d;return Ax(c,12,\ne,f)}return 9===d?(d=c[10],d=P(U,d),c[2]=d,c[1]=11,Z):5===d?(c[1]=t(c[2])?9:10,Z):14===d?Bx(c,c[2]):26===d?(d=c[8],c[2]=d,c[1]=27,Z):16===d?(d=c[8],c[1]=wb(null==d)?19:20,Z):10===d?(d=c[10],c[2]=d,c[1]=11,Z):18===d?(c[2]=c[2],c[1]=14,Z):8===d?(c[2]=c[2],c[1]=5,Z):null}}(c),c)}(),f=function(){var a=d.B?d.B():d.call(null);a[6]=c;return a}();return yx(f)}}(c))}\nfunction Py(a,b){var c=new EventSource(a),d=dg.h(null);Ox(b,ly(!0));c.onopen=function(a,c){return function(){var a=Mx(1E4,ig.h(Ny));fg(c,a);Oy(a,b);Ox(b,ny(!0));return Ox(b,ly(!1))}}(c,d);c.onerror=function(a,c){return function(){Tw(B(c));fg(c,null);return Ox(b,ly(!0))}}(c,d);return c.onmessage=function(a,b){return function(a){var c=B(b);return t(c)?Ox(c,a.data):null}}(c,d)}\nfunction Qy(a,b,c,d,e,f,h){this.ha=a;this.url=b;this.Y=c;this.yb=d;this.v=e;this.j=f;this.w=h;this.m=2229667594;this.J=139264}g=Qy.prototype;g.Bd=function(){fg(this.ha,Kx(null));return t(this.Y)?this.ac(null):null};g.Ad=function(){return this.wc(null)};g.ac=function(){if(t(B(this.yb)))return null;fg(this.yb,!0);return Py(this.url,B(this.ha))};g.wc=function(){return null};g.Dd=function(){return this.ac(null)};g.Cd=function(){return null};g.zd=function(){return null};\ng.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case \"msg-ch\":return this.ha;case \"url\":return this.url;case \"auto-play?\":return this.Y;case \"started?\":return this.yb;default:return D.l(this.j,b,c)}};\ng.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,\"\",\" \",\"\",c,a)}}(this),\"#asciinema.player.source.Stream{\",\", \",\"}\",c,O.c(new R(null,4,5,T,[new R(null,2,5,T,[Dl,this.ha],null),new R(null,2,5,T,[$m,this.url],null),new R(null,2,5,T,[Jm,this.Y],null),new R(null,2,5,T,[rm,this.yb],null)],null),this.j))};g.ba=function(){return new fh(0,this,4,new R(null,4,5,T,[Dl,$m,Jm,rm],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 4+H(this.j)};\ng.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 187678783^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.ha,b.ha)&&G.c(this.url,b.url)&&G.c(this.Y,b.Y)&&G.c(this.yb,b.yb)&&G.c(this.j,b.j)};\ng.ga=function(a,b){return He(new ti(null,new r(null,4,[Dl,null,rm,null,Jm,null,$m,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new Qy(this.ha,this.url,this.Y,this.yb,this.v,Bf(le.c(this.j,b)),null)};\ng.O=function(a,b,c){return t(N.c?N.c(Dl,b):N.call(null,Dl,b))?new Qy(c,this.url,this.Y,this.yb,this.v,this.j,null):t(N.c?N.c($m,b):N.call(null,$m,b))?new Qy(this.ha,c,this.Y,this.yb,this.v,this.j,null):t(N.c?N.c(Jm,b):N.call(null,Jm,b))?new Qy(this.ha,this.url,c,this.yb,this.v,this.j,null):t(N.c?N.c(rm,b):N.call(null,rm,b))?new Qy(this.ha,this.url,this.Y,c,this.v,this.j,null):new Qy(this.ha,this.url,this.Y,this.yb,this.v,K.l(this.j,b,c),null)};\ng.S=function(){return E(O.c(new R(null,4,5,T,[new R(null,2,5,T,[Dl,this.ha],null),new R(null,2,5,T,[$m,this.url],null),new R(null,2,5,T,[Jm,this.Y],null),new R(null,2,5,T,[rm,this.yb],null)],null),this.j))};g.T=function(a,b){return new Qy(this.ha,this.url,this.Y,this.yb,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};wj(hm,function(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b;c=D.c(c,Em);var d=dg.h(null),e=dg.h(!1);return new Qy(d,a,c,e,null,null,null)});function Ry(a){var b=new R(null,5,5,T,[\"fullscreenElement\",\"mozFullScreenElement\",\"webkitFullscreenElement\",\"webkitCurrentFullScreenElement\",\"msFullscreenElement\"],null);b=Wf($f.c(Ee,Pu),b);t(b)?(a=Wf(Pu,new R(null,5,5,T,[\"exitFullscreen\",\"webkitExitFullscreen\",\"webkitCancelFullScreen\",\"mozCancelFullScreen\",\"msExitFullscreen\"],null)),a=t(a)?a.call(document):null):(b=new R(null,5,5,T,[\"requestFullscreen\",\"webkitRequestFullscreen\",\"webkitRequestFullScreen\",\"mozRequestFullScreen\",\"msRequestFullscreen\"],\nnull),b=Wf(ag.c(Hb,a),b),a=t(b)?b.call(a):null);return a};r.prototype.yd=function(){return il.h(this)};r.prototype.xd=function(){return pl.h(this)};function Sy(a,b){return function(c){var d=b.h?b.h(c):b.call(null,c);return t(d)?(Ox(a,d),c.stopPropagation()):null}}function Ty(a,b){return Sy(a,function(){return b})}function Uy(a,b,c){var d=\"number\"===typeof a||G.c(a,\"fg\")||G.c(a,\"bg\");return t(d)?(a=t(t(b)?8>a:b)?a+8:a,[v.h(c),v.h(a)].join(\"\")):null}\nfunction Vy(a){var b=J(a,0,null),c=J(a,1,null);a=J(a,2,null);return[\"rgb(\",v.h(b),\",\",v.h(c),\",\",v.h(a),\")\"].join(\"\")}\nvar Wy=hj(function(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,Nk),c=D.c(a,pl);a=K.l(a,Nk,t(c)?wb(b):b);var d=null!=a&&(a.m&64||q===a.G)?P(U,a):a,e=D.c(d,Ok),f=D.c(d,Tn);b=D.c(d,Kj);var h=D.c(d,dk);c=D.c(d,Vl);var k=D.c(d,Nk),l=D.c(d,Yn);d=D.c(d,pl);var p=t(k)?t(e)?e:\"fg\":f;e=Uy(t(k)?t(f)?f:\"bg\":e,b,\"fg-\");h=Uy(p,h,\"bg-\");c=vg(ub,new R(null,6,5,T,[e,h,t(b)?\"bright\":null,t(l)?\"italic\":null,t(c)?\"underline\":null,t(d)?\"cursor\":null],null));if(E(c))a:for(b=new cb,c=E(c);;)if(null!=c)b.append(\"\"+\nv.h(y(c))),c=z(c),null!=c&&b.append(\" \");else{b=b.toString();break a}else b=null;l=null!=a&&(a.m&64||q===a.G)?P(U,a):a;a=D.c(l,Ok);c=D.c(l,Tn);h=D.c(l,Nk);l=t(h)?c:a;a=t(h)?a:c;a=hi.A(be([t(ze.h?ze.h(l):ze.call(null,l))?new r(null,1,[ik,Vy(l)],null):null,t(ze.h?ze.h(a):ze.call(null,a))?new r(null,1,[al,Vy(a)],null):null]));return hi.A(be([t(b)?new r(null,1,[vn,b],null):null,t(a)?new r(null,1,[fm,a],null):null]))});\nfunction Xy(a,b){var c=J(a,0,null),d=J(a,1,null);d=Bg(d,pl,function(){return function(a){return t(a)?B(b):a}}(a,c,d));return new R(null,3,5,T,[ro,Wy.h?Wy.h(d):Wy.call(null,d),c],null)}function Yy(a,b){var c=J(a,0,null),d=J(a,1,null),e=jg(b,c);e=E(e)?new R(null,2,5,T,[Eo(e),d],null):null;var f=K.l(d,pl,!0);f=new R(null,2,5,T,[Vd(c,b),f],null);c=kg(b+1,c);d=E(c)?new R(null,2,5,T,[Eo(c),d],null):null;return vg(ub,new R(null,3,5,T,[e,f,d],null))}\nfunction Zy(a,b){for(var c=he,d=a,e=b;;)if(E(d)){var f=y(d),h=J(f,0,null);J(f,1,null);h=H(h);if(h<=e)c=ge.c(c,f),d=vd(d),e-=h;else return O.A(c,Yy(f,e),be([vd(d)]))}else return c}function $y(a,b,c){a=t(B(b))?Zy(B(a),B(b)):B(a);return new R(null,2,5,T,[Lm,Ii(bg(function(){return function(a,b){return pe(new R(null,3,5,T,[Xy,b,c],null),new r(null,1,[mk,a],null))}}(a),a))],null)}var qA=new ti(null,new r(null,3,[\"small\",null,\"medium\",null,\"big\",null],null),null);\nfunction rA(a,b,c,d,e){var f=yp(function(){var a=B(c);return t(qA.h?qA.h(a):qA.call(null,a))?[\"font-\",v.h(a)].join(\"\"):null}),h=yp(function(){return function(){var d=B(a),e=B(b),f=B(c);f=t(qA.h?qA.h(f):qA.call(null,f))?null:new r(null,1,[wk,f],null);return hi.A(be([new r(null,2,[fl,[v.h(d),\"ch\"].join(\"\"),no,[v.h(1.3333333333*e),\"em\"].join(\"\")],null),f]))}}(f)),k=yp(function(){return function(){return Lu(B(d))}}(f,h)),l=yp(function(a,c,d){return function(){return xg(function(a,b,c){return function(d){return yp(function(a,\nb,c){return function(){return D.c(B(c),d)}}(a,b,c))}}(a,c,d),Fi(0,B(b),1))}}(f,h,k)),p=yp(function(){return function(){return Mu(B(d))}}(f,h,k,l)),m=yp(function(a,b,c,d,e){return function(){return zn.h(B(e))}}(f,h,k,l,p)),u=yp(function(a,b,c,d,e){return function(){return Aj.h(B(e))}}(f,h,k,l,p,m)),w=yp(function(a,b,c,d,e){return function(){return On.h(B(e))}}(f,h,k,l,p,m,u));return function(a,b,c,d,f,h,k,l){return function(){return new R(null,3,5,T,[Gm,new r(null,2,[vn,B(a),fm,B(b)],null),bg(function(a,\nb,c,d,f,h,k,l){return function(m,p){var u=yp(function(a,b,c,d,e,f,h,k){return function(){var a=B(k);return t(a)?(a=G.c(m,B(h)))?B(f):a:a}}(a,b,c,d,f,h,k,l));return pe(new R(null,4,5,T,[$y,p,u,e],null),new r(null,1,[mk,m],null))}}(a,b,c,d,f,h,k,l),B(d))],null)}}(f,h,k,l,p,m,u,w)}\nfunction sA(){return new R(null,2,5,T,[Ym,new r(null,4,[Mn,\"1.1\",Fl,\"0 0 866.0254037844387 866.0254037844387\",vn,\"icon\",mo,new r(null,1,[An,'\\x3cdefs\\x3e \\x3cmask id\\x3d\"small-triangle-mask\"\\x3e \\x3crect width\\x3d\"100%\" height\\x3d\"100%\" fill\\x3d\"white\"/\\x3e \\x3cpolygon points\\x3d\"508.01270189221935 433.01270189221935, 208.0127018922194 259.8076211353316, 208.01270189221927 606.217782649107\" fill\\x3d\"black\"\\x3e\\x3c/polygon\\x3e \\x3c/mask\\x3e \\x3c/defs\\x3e \\x3cpolygon points\\x3d\"808.0127018922194 433.01270189221935, 58.01270189221947 -1.1368683772161603e-13, 58.01270189221913 866.0254037844386\" mask\\x3d\"url(#small-triangle-mask)\" fill\\x3d\"white\"\\x3e\\x3c/polygon\\x3e \\x3cpolyline points\\x3d\"481.2177826491071 333.0127018922194, 134.80762113533166 533.0127018922194\" stroke\\x3d\"white\" stroke-width\\x3d\"90\"\\x3e\\x3c/polyline\\x3e'],null)],\nnull)],null)}function tA(){return new R(null,3,5,T,[Ym,new r(null,3,[Mn,\"1.1\",Fl,\"0 0 12 12\",vn,\"icon\"],null),new R(null,2,5,T,[Fj,new r(null,1,[pn,\"M1,0 L11,6 L1,12 Z\"],null)],null)],null)}function uA(){return new R(null,4,5,T,[Ym,new r(null,3,[Mn,\"1.1\",Fl,\"0 0 12 12\",vn,\"icon\"],null),new R(null,2,5,T,[Fj,new r(null,1,[pn,\"M1,0 L4,0 L4,12 L1,12 Z\"],null)],null),new R(null,2,5,T,[Fj,new r(null,1,[pn,\"M8,0 L11,0 L11,12 L8,12 Z\"],null)],null)],null)}\nfunction vA(){return new R(null,4,5,T,[Ym,new r(null,3,[Mn,\"1.1\",Fl,\"0 0 12 12\",vn,\"icon\"],null),new R(null,2,5,T,[Fj,new r(null,1,[pn,\"M12,0 L7,0 L9,2 L7,4 L8,5 L10,3 L12,5 Z\"],null)],null),new R(null,2,5,T,[Fj,new r(null,1,[pn,\"M0,12 L0,7 L2,9 L4,7 L5,8 L3,10 L5,12 Z\"],null)],null)],null)}\nfunction wA(){return new R(null,4,5,T,[Ym,new r(null,3,[Mn,\"1.1\",Fl,\"0 0 12 12\",vn,\"icon\"],null),new R(null,2,5,T,[Fj,new r(null,1,[pn,\"M7,5 L7,0 L9,2 L11,0 L12,1 L10,3 L12,5 Z\"],null)],null),new R(null,2,5,T,[Fj,new r(null,1,[pn,\"M5,7 L0,7 L2,9 L0,11 L1,12 L3,10 L5,12 Z\"],null)],null)],null)}function xA(a,b){return function(b){return function(){return new R(null,3,5,T,[cl,new r(null,1,[Sl,b],null),new R(null,1,5,T,[t(B(a))?uA:tA],null)],null)}}(Ty(b,new fy(null,null,null)))}\nfunction yA(a){return 10>a?[\"0\",v.h(a)].join(\"\"):a}function zA(a){var b=Math.floor((a%60+60)%60);return[v.h(yA(Math.floor(a/60))),\":\",v.h(yA(b))].join(\"\")}function AA(a,b){var c=T,d=new R(null,2,5,T,[Yk,zA(B(a))],null),e=T;var f=B(a);var h=B(b);f=[\"-\",v.h(zA(h-f))].join(\"\");return new R(null,3,5,c,[Ml,d,new R(null,2,5,e,[co,f],null)],null)}\nfunction BA(){function a(a){a.preventDefault();return Ry(a.currentTarget.parentNode.parentNode.parentNode)}return function(){return new R(null,4,5,T,[un,new r(null,1,[Sl,a],null),new R(null,1,5,T,[vA],null),new R(null,1,5,T,[wA],null)],null)}}\nfunction CA(a,b){var c=Sy(b,function(a){var b=a.currentTarget.offsetWidth,c=a.currentTarget.getBoundingClientRect();return cy(Nu(a.clientX-c.left,b)/b)}),d=yp(function(){return function(){return[v.h(100*B(a)),\"%\"].join(\"\")}}(c));return function(a,b){return function(){return new R(null,2,5,T,[Vj,new R(null,3,5,T,[Bl,new r(null,1,[Ql,a],null),new R(null,2,5,T,[Cj,new R(null,2,5,T,[ro,new r(null,1,[fm,new r(null,1,[fl,B(b)],null)],null)],null)],null)],null)],null)}}(c,d)}\nfunction DA(a,b,c,d){return function(e){return function(){return new R(null,5,5,T,[Kk,new R(null,3,5,T,[xA,a,d],null),new R(null,3,5,T,[AA,b,c],null),new R(null,1,5,T,[BA],null),new R(null,3,5,T,[CA,e,d],null)],null)}}(yp(function(){return B(b)/B(c)}))}\nfunction EA(a){return function(a){return function(){return new R(null,3,5,T,[ol,new r(null,1,[Sl,a],null),new R(null,2,5,T,[Xk,new R(null,2,5,T,[km,new R(null,2,5,T,[ro,new R(null,1,5,T,[sA],null)],null)],null)],null)],null)}}(Ty(a,new fy(null,null,null)))}function FA(){return new R(null,2,5,T,[Ek,new R(null,1,5,T,[xn],null)],null)}function GA(a){return Wf(function(b){return a[b]},new R(null,4,5,T,[\"altKey\",\"shiftKey\",\"metaKey\",\"ctrlKey\"],null))}\nfunction HA(a){var b=t(GA(a))?null:function(){switch(a.key){case \" \":return new fy(null,null,null);case \"f\":return bm;case \"0\":return cy(0);case \"1\":return cy(.1);case \"2\":return cy(.2);case \"3\":return cy(.3);case \"4\":return cy(.4);case \"5\":return cy(.5);case \"6\":return cy(.6);case \"7\":return cy(.7);case \"8\":return cy(.8);case \"9\":return cy(.9);default:return null}}();if(t(b))return b;switch(a.key){case \"\\x3e\":return new ey(null,null,null);case \"\\x3c\":return new dy(null,null,null);default:return null}}\nfunction IA(a){if(t(GA(a)))return null;switch(a.which){case 37:return new ay(null,null,null);case 39:return new $x(null,null,null);default:return null}}function JA(a){var b=HA(a);return t(b)?(a.preventDefault(),G.c(b,bm)?(Ry(a.currentTarget),null):b):null}function KA(a){var b=IA(a);return t(b)?(a.preventDefault(),b):null}\nfunction LA(a,b,c,d){a=t(a)?['\"',v.h(a),'\"'].join(\"\"):\"untitled\";return new R(null,4,5,T,[dl,t(d)?new R(null,2,5,T,[jo,new r(null,1,[zl,d],null)],null):null,a,t(b)?new R(null,3,5,T,[ro,\" by \",t(c)?new R(null,3,5,T,[lo,new r(null,1,[ho,c],null),b],null):b],null):null],null)}\nfunction MA(a){var b=Mx(1,ig.h(iy)),c=Kx(1);lx(function(c){return function(){var d=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(x){if(x instanceof Object)b[5]=x,Cx(b),d=Z;else throw x;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error(\"Invalid arity: \"+\n(arguments.length-1));};d.B=c;d.h=b;return d}()}(function(){return function(c){var d=c[1];if(7===d)return c[7]=c[2],Ax(c,12,b,!1);if(1===d)return c[2]=null,c[1]=2,Z;if(4===d)return c[8]=c[2],Ax(c,5,b,!0);if(6===d)return d=Jx(3E3),Ux(c,8,new R(null,2,5,T,[a,d],null));if(3===d)return Bx(c,c[2]);if(12===d)return c[9]=c[2],c[2]=null,c[1]=2,Z;if(2===d)return zx(c,4,a);if(11===d)return c[2]=c[2],c[1]=7,Z;if(9===d)return c[2]=null,c[1]=6,Z;if(5===d)return c[10]=c[2],c[2]=null,c[1]=6,Z;if(10===d)return c[2]=\nnull,c[1]=11,Z;if(8===d){var e=c[2];d=J(e,0,null);e=J(e,1,null);e=G.c(e,a);c[11]=d;c[1]=e?9:10;return Z}return null}}(c),c)}(),f=function(){var a=d.B?d.B():d.call(null);a[6]=c;return a}();return yx(f)}}(c));return b}\nfunction NA(a,b){var c=dg.h(b),d=Kx(1);lx(function(b,c){return function(){var d=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(F){if(F instanceof Object)b[5]=F,Cx(b),d=Z;else throw F;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,\na)}throw Error(\"Invalid arity: \"+(arguments.length-1));};d.B=c;d.h=b;return d}()}(function(b,c){return function(d){var e=d[1];if(7===e){var f=d[7],h=wb(null==f);d[8]=d[2];d[1]=h?8:9;return Z}if(20===e)return f=d[7],d[1]=t(q===f.Fe)?23:24,Z;if(27===e)return d[2]=!1,d[1]=28,Z;if(1===e)return d[2]=null,d[1]=2,Z;if(24===e)return f=d[7],d[1]=t(!f.Tc)?26:27,Z;if(4===e){f=d[7];var k=d[9];h=d[2];var l=J(h,0,null),m=J(h,1,null);d[10]=m;d[7]=l;d[9]=h;d[1]=t(null==l)?5:6;return Z}return 15===e?(d[2]=!1,d[1]=\n16,Z):21===e?(f=d[7],h=Ab(Yx,f),d[2]=h,d[1]=22,Z):31===e?(d[11]=d[2],d[2]=null,d[1]=2,Z):13===e?(d[2]=d[2],d[1]=10,Z):22===e?(d[1]=t(d[2])?29:30,Z):29===e?(f=d[7],h=B(a),h=Zx(f,h),h=gg.l(c,wo,h),d[2]=h,d[1]=31,Z):6===e?(d[2]=null,d[1]=7,Z):28===e?(d[2]=d[2],d[1]=25,Z):25===e?(d[2]=d[2],d[1]=22,Z):17===e?(m=d[10],f=d[7],k=d[9],h=gg.c(a,function(){return function(a,b){return function(a){return Xx(b,a)}}(k,f,m,m,f,k,e,b,c)}()),d[2]=h,d[1]=19,Z):3===e?Bx(d,d[2]):12===e?(f=d[7],d[1]=t(!f.Tc)?14:15,Z):\n2===e?(h=B(c),h=E(h),Ux(d,4,h)):23===e?(d[2]=!0,d[1]=25,Z):19===e?(f=d[7],h=wb(null==f),d[12]=d[2],d[1]=h?20:21,Z):11===e?(d[2]=!0,d[1]=13,Z):9===e?(f=d[7],h=Ab(Wx,f),d[2]=h,d[1]=10,Z):5===e?(m=d[10],h=gg.l(c,re,m),d[2]=h,d[1]=7,Z):14===e?(f=d[7],h=Ab(Wx,f),d[2]=h,d[1]=16,Z):26===e?(f=d[7],h=Ab(Yx,f),d[2]=h,d[1]=28,Z):16===e?(d[2]=d[2],d[1]=13,Z):30===e?(d[2]=null,d[1]=31,Z):10===e?(d[1]=t(d[2])?17:18,Z):18===e?(d[2]=null,d[1]=19,Z):8===e?(f=d[7],d[1]=t(q===f.sb)?11:12,Z):null}}(b,c),b,c)}(),e=function(){var a=\nd.B?d.B():d.call(null);a[6]=b;return a}();return yx(e)}}(d,c));return d}\nfunction OA(a,b,c){c=Ty(c,!0);var d=Sy(b,JA),e=Sy(b,KA),f=yp(function(){return function(){return Hm.h(B(a))}}(c,d,e)),h=yp(function(){return function(){return el.h(B(a))}}(c,d,e,f)),k=yp(function(a,b,c,d,e){return function(){var a=B(d);return t(a)?a:B(e)}}(c,d,e,f,h)),l=yp(function(b,c,d,e,f,h){return function(){var b=Gk.h(B(a));b=t(b)?b:wb(B(h));return t(b)?\"hud\":null}}(c,d,e,f,h,k)),p=yp(function(){return function(){return[\"asciinema-theme-\",v.h(gm.h(B(a)))].join(\"\")}}(c,d,e,f,h,k,l)),m=yp(function(){return function(){var b=\nfl.h(B(a));return t(b)?b:80}}(c,d,e,f,h,k,l,p)),u=yp(function(){return function(){var b=no.h(B(a));return t(b)?b:24}}(c,d,e,f,h,k,l,p,m)),w=yp(function(){return function(){return wk.h(B(a))}}(c,d,e,f,h,k,l,p,m,u)),x=yp(function(){return function(){return V.h(B(a))}}(c,d,e,f,h,k,l,p,m,u,w)),C=yp(function(){return function(){return ml.h(B(a))}}(c,d,e,f,h,k,l,p,m,u,w,x)),F=yp(function(){return function(){return jn.h(B(a))}}(c,d,e,f,h,k,l,p,m,u,w,x,C)),I=yp(function(){return function(){return Uj.h(B(a))}}(c,\nd,e,f,h,k,l,p,m,u,w,x,C,F)),M=yp(function(){return function(){return wl.h(B(a))}}(c,d,e,f,h,k,l,p,m,u,w,x,C,F,I)),S=B(a),X=null!=S&&(S.m&64||q===S.G)?P(U,S):S,Ga=D.c(X,ki),db=D.c(X,li),Q=D.c(X,mi),xb=D.c(X,ni);return function(a,c,d,e,f,h,k,l,m,p,u,w,x,C,F,I,M,S,Q,X,Ga,db){return function(){return new R(null,3,5,T,[Cn,new r(null,5,[Jj,-1,Zj,c,Rn,d,Vm,a,vn,B(k)],null),new R(null,7,5,T,[Sm,new r(null,1,[vn,B(l)],null),new R(null,6,5,T,[rA,m,p,u,w,x],null),new R(null,5,5,T,[DA,C,F,I,b],null),t(t(Q)?Q:\nX)?new R(null,5,5,T,[LA,Q,X,Ga,db],null):null,t(B(h))?null:new R(null,2,5,T,[EA,b],null),t(B(e))?new R(null,1,5,T,[FA],null):null],null)],null)}}(c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga,db,Q,xb)}\nfunction PA(a){var b=Kx(null),c=Kx(new dx(bx(1),1));return function(b,c){return function(){return Pp(new r(null,4,[ln,\"asciinema-player\",Dm,function(b,c){return function(){return OA(a,b,c)}}(b,c),$k,function(b,c){return function(){var d=ty(Gl.h(B(a))),e=MA(c);Tx(e,b);return NA(a,Je([b,d]))}}(b,c),Wm,function(){return function(){return uy(Gl.h(B(a)))}}(b,c)],null))}}(b,c)};function QA(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,Ak),e=D.c(c,Gl);d=a.h?a.h(d):a.call(null,d);zy(e,d);return K.l(c,Ak,d)}$x.prototype.sb=q;$x.prototype.qb=function(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,Uj),e=D.c(c,wl),f=D.c(c,Gl);t(e)&&yy(f,Nu(d+5,e));return c};ay.prototype.sb=q;ay.prototype.qb=function(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,Uj),e=D.c(c,wl),f=D.c(c,Gl);t(e)&&yy(f,Nu(d+-5,e));return c};by.prototype.sb=q;\nby.prototype.qb=function(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,wl),e=D.c(c,Gl);t(d)&&(d*=nn.h(this),yy(e,d));return c};dy.prototype.sb=q;dy.prototype.qb=function(a,b){return QA(function(){return function(a){return a/2}}(this),b)};ey.prototype.sb=q;ey.prototype.qb=function(a,b){return QA(function(){return function(a){return 2*a}}(this),b)};fy.prototype.sb=q;fy.prototype.qb=function(a,b){xy(Gl.h(b));return b};gy.prototype.sb=q;gy.prototype.qb=function(a,b){return K.l(b,ml,so.h(this))};\nhy.prototype.sb=q;hy.prototype.qb=function(a,b){return K.l(b,Gk,so.h(this))};jy.prototype.sb=q;jy.prototype.qb=function(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a;D.c(c,fl);D.c(c,no);D.c(c,wl);c=null!=b&&(b.m&64||q===b.G)?P(U,b):b;var d=D.c(c,fl),e=D.c(c,no),f=null!=this&&(this.m&64||q===this.G)?P(U,this):this,h=D.c(f,fl),k=D.c(f,no);f=D.c(f,wl);return K.A(c,fl,t(d)?d:h,be([no,t(e)?e:k,wl,f]))};ky.prototype.sb=q;ky.prototype.qb=function(a,b){return K.l(b,Hm,Hm.h(this))};oy.prototype.sb=q;\noy.prototype.qb=function(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,oi);t(d)&&(ap(bp),d.B?d.B():d.call(null));return c};ry.prototype.sb=q;ry.prototype.qb=function(a,b){return K.l(b,Uj,Zk.h(this))};function RA(){return ig.l(function(a,b){return new R(null,2,5,T,[a,new gy(b,null,null,null)],null)},rg(function(a){return a+.5},.5),og(new R(null,2,5,T,[!1,!0],null)))}function SA(a){var b=Dy(RA());return K.l(K.l(a,ml,!0),Ol,b)}\nfunction TA(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,Ol);Tw(b);return K.l(K.l(a,ml,!0),Ol,null)}function UA(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;a=D.c(a,Ol);return t(a)?Je([a]):vi}my.prototype.sb=q;\nmy.prototype.qb=function(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a;D.c(c,jn);var d=null!=b&&(b.m&64||q===b.G)?P(U,b):b,e=D.c(d,jn);c=D.c(d,pi);var f=D.c(d,qi),h=null!=this&&(this.m&64||q===this.G)?P(U,this):this;h=D.c(h,jn);if(G.c(e,h))return d;d=K.A(d,jn,h,be([el,!0]));if(t(h))return t(c)&&(c.B?c.B():c.call(null)),SA(d);t(f)&&(f.B?f.B():f.call(null));return TA(d)};my.prototype.Fe=q;my.prototype.de=function(a,b){return UA(b)};py.prototype.sb=q;\npy.prototype.qb=function(a,b){var c=K.l(b,V,V.h(this));c=null!=c&&(c.m&64||q===c.G)?P(U,c):c;var d=D.c(c,Ol);return t(d)?SA(TA(c)):c};py.prototype.Fe=q;py.prototype.de=function(a,b){return UA(b)};function VA(a){return t(a)?(a=ig.c(parseFloat,Fo(\"\"+v.h(a),/:/)),a=ig.l(Ye,cf(a),rg(function(){return function(a){return 60*a}}(a),1)),P(Xe,a)):null}\nfunction WA(a,b,c){t(a)?\"string\"===typeof a?t(0===a.indexOf(\"data:application/json;base64,\"))?(b=a.substring(29).replace(RegExp(\"\\\\s\",\"g\"),\"\"),b=JSON.parse(atob(b)),b=fj(b),b=new r(null,1,[V,new r(null,1,[il,b],null)],null)):t(0===a.indexOf(\"data:text/plain,\"))?(a=a.substring(16),b=Ju(Ot(t(b)?b:80,t(c)?c:24),a),b=new r(null,1,[V,b],null)):b=t(0===a.indexOf(\"npt:\"))?new r(null,1,[Zk,VA(a.substring(4))],null):null:b=new r(null,1,[V,new r(null,1,[il,a],null)],null):b=null;return b}\nvar XA=new r(null,2,[pl,new r(null,1,[On,!1],null),il,he],null);\nfunction YA(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,no),e=D.l(c,wk,\"small\"),f=D.l(c,Ak,1),h=D.c(c,Hk),k=D.c(c,fl),l=D.c(c,rl),p=D.l(c,cm,!1),m=D.l(c,gm,\"asciinema\"),u=D.c(c,qm),w=D.c(c,Bm),x=D.l(c,vm,!1),C=D.l(c,Em,!1),F=function(){var a=VA(h);return t(a)?a:0}();w=WA(w,k,d);var I=null!=w&&(w.m&64||q===w.G)?P(U,w):w;w=D.c(I,V);I=D.c(I,Zk);var M=t(I)?I:wb(w)&&0<F?F:null;I=function(){var b=Pe([Ak,Hk,fl,rl,cm,qm,vm,Em,Mm,no],[f,F,k,l,p,u,x,C,M,d]);return xj.c?xj.c(a,b):xj.call(null,a,b)}();\nreturn hi.A(be([Pe([Uj,V,wk,Ak,Gk,el,fl,wl,Gl,Ol,gm,Hm,jn,no],[F,t(w)?w:XA,e,f,!1,!1,k,null,I,null,m,!1,!1,d]),ji(c)]))}function ZA(a,b,c){a=\"string\"===typeof a?document.getElementById(a):a;b=tp.h(P(YA,be([b,c])));c=new R(null,2,5,T,[PA,b],null);qq?oq(c,a,null):pq.call(null,c,a);return b}\nib=function(){function a(a){var c=null;if(0<arguments.length){c=0;for(var e=Array(arguments.length-0);c<e.length;)e[c]=arguments[c+0],++c;c=new Jb(e,0,null)}return b.call(this,c)}function b(a){return console.log.apply(console,Lb(a))}a.L=0;a.N=function(a){a=E(a);return b(a)};a.A=b;return a}();\nkb=function(){function a(a){var c=null;if(0<arguments.length){c=0;for(var e=Array(arguments.length-0);c<e.length;)e[c]=arguments[c+0],++c;c=new Jb(e,0,null)}return b.call(this,c)}function b(a){return console.error.apply(console,Lb(a))}a.L=0;a.N=function(a){a=E(a);return b(a)};a.A=b;return a}();var $A=function $A(a){switch(arguments.length){case 2:return $A.c(arguments[0],arguments[1]);case 3:return $A.l(arguments[0],arguments[1],arguments[2]);default:throw Error([\"Invalid arity: \",v.h(arguments.length)].join(\"\"));}};da(\"asciinema.player.js.CreatePlayer\",$A);$A.c=function(a,b){return $A.l(a,b,Ef)};\n$A.l=function(a,b,c){b=fj(b);c=yo(fj(c));a=ZA(a,b,c);return cj(new r(null,5,[En,function(a,b,c){return function(){return Uj.h(B(c))}}(b,c,a),Bj,function(a,b,c){return function(a){var b=B(c);b=null!=b&&(b.m&64||q===b.G)?P(U,b):b;D.c(b,wl);b=D.c(b,Gl);return yy(b,a)}}(b,c,a),Zm,function(a,b,c){return function(){return wl.h(B(c))}}(b,c,a),Jn,function(a,b,c){return function(){var a=B(c);a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;a=D.c(a,Gl);return vy(a)}}(b,c,a),sn,function(a,b,c){return function(){var a=\nB(c);a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;a=D.c(a,Gl);return wy(a)}}(b,c,a)],null))};$A.L=3;da(\"asciinema.player.js.UnmountPlayer\",function(a){a=\"string\"===typeof a?document.getElementById(a):a;gg.l(lq,le,a);return kq().unmountComponentAtNode(a)});registerAsciinemaPlayerElement();\n})();\n\n//# sourceMappingURL=asciinema-player.js.map\n\n"
  },
  {
    "path": "docs/source/theme/smithy/static/bootstrap-reboot.css",
    "content": "/*!\n * Bootstrap v4.0.0-alpha.6 (https://getbootstrap.com)\n * Copyright 2011-2017 The Bootstrap Authors\n * Copyright 2011-2017 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\nhtml {\n  box-sizing: border-box;\n  font-family: sans-serif;\n  line-height: 1.15;\n  -webkit-text-size-adjust: 100%;\n  -ms-text-size-adjust: 100%;\n  -ms-overflow-style: scrollbar;\n  -webkit-tap-highlight-color: transparent;\n}\n\n*,\n*::before,\n*::after {\n  box-sizing: inherit;\n}\n\n@-ms-viewport {\n  width: device-width;\n}\n\narticle, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section {\n  display: block;\n}\n\nbody {\n  margin: 0;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n  font-size: 1rem;\n  font-weight: normal;\n  line-height: 1.5;\n  color: #212529;\n  background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus {\n  outline: none !important;\n}\n\nhr {\n  box-sizing: content-box;\n  height: 0;\n  overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n  margin-top: 0;\n  margin-bottom: .5rem;\n}\n\np {\n  margin-top: 0;\n  margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n  text-decoration: underline;\n  text-decoration: underline dotted;\n  cursor: help;\n  border-bottom: 0;\n}\n\naddress {\n  margin-bottom: 1rem;\n  font-style: normal;\n  line-height: inherit;\n}\n\nol,\nul,\ndl {\n  margin-top: 0;\n  margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n  margin-bottom: 0;\n}\n\ndt {\n  font-weight: bold;\n}\n\ndd {\n  margin-bottom: .5rem;\n  margin-left: 0;\n}\n\nblockquote {\n  margin: 0 0 1rem;\n}\n\ndfn {\n  font-style: italic;\n}\n\nb,\nstrong {\n  font-weight: bolder;\n}\n\nsmall {\n  font-size: 80%;\n}\n\nsub,\nsup {\n  position: relative;\n  font-size: 75%;\n  line-height: 0;\n  vertical-align: baseline;\n}\n\nsub {\n  bottom: -.25em;\n}\n\nsup {\n  top: -.5em;\n}\n\na {\n  color: #007bff;\n  text-decoration: none;\n  background-color: transparent;\n  -webkit-text-decoration-skip: objects;\n}\n\na:hover {\n  color: #0056b3;\n  text-decoration: underline;\n}\n\na:not([href]):not([tabindex]) {\n  color: inherit;\n  text-decoration: none;\n}\n\na:not([href]):not([tabindex]):focus, a:not([href]):not([tabindex]):hover {\n  color: inherit;\n  text-decoration: none;\n}\n\na:not([href]):not([tabindex]):focus {\n  outline: 0;\n}\n\npre,\ncode,\nkbd,\nsamp {\n  font-family: monospace, monospace;\n  font-size: 1em;\n}\n\npre {\n  margin-top: 0;\n  margin-bottom: 1rem;\n  overflow: auto;\n}\n\nfigure {\n  margin: 0 0 1rem;\n}\n\nimg {\n  vertical-align: middle;\n  border-style: none;\n}\n\nsvg:not(:root) {\n  overflow: hidden;\n}\n\na,\narea,\nbutton,\n[role=\"button\"],\ninput,\nlabel,\nselect,\nsummary,\ntextarea {\n  -ms-touch-action: manipulation;\n      touch-action: manipulation;\n}\n\ntable {\n  border-collapse: collapse;\n}\n\ncaption {\n  padding-top: 0.75rem;\n  padding-bottom: 0.75rem;\n  color: #868e96;\n  text-align: left;\n  caption-side: bottom;\n}\n\nth {\n  text-align: left;\n}\n\nlabel {\n  display: inline-block;\n  margin-bottom: .5rem;\n}\n\nbutton:focus {\n  outline: 1px dotted;\n  outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n  margin: 0;\n  font-family: inherit;\n  font-size: inherit;\n  line-height: inherit;\n}\n\nbutton,\ninput {\n  overflow: visible;\n}\n\nbutton,\nselect {\n  text-transform: none;\n}\n\nbutton,\nhtml [type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n  -webkit-appearance: button;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n  padding: 0;\n  border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n  box-sizing: border-box;\n  padding: 0;\n}\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n  -webkit-appearance: listbox;\n}\n\ntextarea {\n  overflow: auto;\n  resize: vertical;\n}\n\nfieldset {\n  min-width: 0;\n  padding: 0;\n  margin: 0;\n  border: 0;\n}\n\nlegend {\n  display: block;\n  width: 100%;\n  max-width: 100%;\n  padding: 0;\n  margin-bottom: .5rem;\n  font-size: 1.5rem;\n  line-height: inherit;\n  color: inherit;\n  white-space: normal;\n}\n\nprogress {\n  vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n  height: auto;\n}\n\n[type=\"search\"] {\n  outline-offset: -2px;\n  -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-cancel-button,\n[type=\"search\"]::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n  font: inherit;\n  -webkit-appearance: button;\n}\n\noutput {\n  display: inline-block;\n}\n\nsummary {\n  display: list-item;\n}\n\ntemplate {\n  display: none;\n}\n\n[hidden] {\n  display: none !important;\n}\n"
  },
  {
    "path": "docs/source/theme/smithy/static/casts/chalice-quickstart-old.cast",
    "content": "{\"version\": 2, \"width\": 158, \"height\": 40, \"timestamp\": 1593453589, \"env\": {\"SHELL\": \"/bin/bash\", \"TERM\": \"xterm-256color\"}}\n[1.053838, \"o\", \"/tmp $ \"]\n[2.460624, \"o\", \"p\"]\n[2.596024, \"o\", \"y\"]\n[2.740091, \"o\", \"t\"]\n[2.824086, \"o\", \"h\"]\n[2.896309, \"o\", \"o\"]\n[3.015152, \"o\", \"n\"]\n[3.092211, \"o\", \"3\"]\n[3.238352, \"o\", \" \"]\n[3.319302, \"o\", \"-\"]\n[3.436422, \"o\", \"m\"]\n[3.498993, \"o\", \" \"]\n[3.627191, \"o\", \"v\"]\n[3.775945, \"o\", \"e\"]\n[3.856262, \"o\", \"n\"]\n[3.948462, \"o\", \"v\"]\n[4.259055, \"o\", \" \"]\n[4.441266, \"o\", \"v\"]\n[4.838451, \"o\", \"e\"]\n[4.95398, \"o\", \"n\"]\n[5.042034, \"o\", \"v\"]\n[5.337278, \"o\", \"3\"]\n[5.929087, \"o\", \"7\"]\n[6.150019, \"o\", \"\\r\\n\"]\n[7.458515, \"o\", \"/tmp $ \"]\n[7.979084, \"o\", \".\"]\n[8.062787, \"o\", \" \"]\n[8.105758, \"o\", \"v\"]\n[8.292986, \"o\", \"e\"]\n[8.368045, \"o\", \"n\"]\n[8.464069, \"o\", \"v37/\"]\n[8.82418, \"o\", \"b\"]\n[8.900077, \"o\", \"i\"]\n[8.952082, \"o\", \"n\"]\n[9.011206, \"o\", \"/\"]\n[9.199576, \"o\", \"a\"]\n[9.259176, \"o\", \"c\"]\n[9.427754, \"o\", \"tivate\"]\n[9.552457, \"o\", \"\\r\\n\"]\n[9.566004, \"o\", \"(venv37) /tmp $ \"]\n[10.21502, \"o\", \"p\"]\n[10.35833, \"o\", \"i\"]\n[10.422742, \"o\", \"p\"]\n[10.494248, \"o\", \" \"]\n[10.530118, \"o\", \"i\"]\n[10.611312, \"o\", \"n\"]\n[10.662435, \"o\", \"s\"]\n[10.692453, \"o\", \"t\"]\n[10.753861, \"o\", \"a\"]\n[10.830775, \"o\", \"l\"]\n[10.939921, \"o\", \"l\"]\n[11.01153, \"o\", \" \"]\n[11.09975, \"o\", \"c\"]\n[11.156039, \"o\", \"h\"]\n[11.259665, \"o\", \"a\"]\n[11.319952, \"o\", \"l\"]\n[11.37577, \"o\", \"i\"]\n[11.44404, \"o\", \"c\"]\n[11.476159, \"o\", \"e\"]\n[11.715507, \"o\", \"\\r\\n\"]\n[12.082684, \"o\", \"Requirement already satisfied: chalice in ./venv37/lib/python3.7/site-packages (1.15.1)\\r\\n\"]\n[12.099075, \"o\", \"Requirement already satisfied: attrs<20.0.0,>=19.3.0 in ./venv37/lib/python3.7/site-packages (from chalice) (19.3.0)\\r\\n\"]\n[12.125123, \"o\", \"Requirement already satisfied: botocore<2.0.0,>=1.12.86 in ./venv37/lib/python3.7/site-packages (from chalice) (1.17.12)\\r\\n\"]\n[12.132778, \"o\", \"Requirement already satisfied: mypy-extensions==0.4.3 in ./venv37/lib/python3.7/site-packages (from chalice) (0.4.3)\\r\\n\"]\n[12.136117, \"o\", \"Requirement already satisfied: pyyaml<6.0.0,>=5.3.1 in ./venv37/lib/python3.7/site-packages (from chalice) (5.3.1)\\r\\n\"]\n[12.138016, \"o\", \"Requirement already satisfied: wheel in ./venv37/lib/python3.7/site-packages (from chalice) (0.34.2)\\r\\n\"]\n[12.144637, \"o\", \"Requirement already satisfied: click<8.0,>=6.6 in ./venv37/lib/python3.7/site-packages (from chalice) (7.1.2)\\r\\n\"]\n[12.147122, \"o\", \"Requirement already satisfied: pip<20.2,>=9 in ./venv37/lib/python3.7/site-packages (from chalice) (19.0.3)\\r\\n\"]\n[12.149632, \"o\", \"Requirement already satisfied: jmespath<1.0.0,>=0.9.3 in ./venv37/lib/python3.7/site-packages (from chalice) (0.10.0)\\r\\n\"]\n[12.152261, \"o\", \"Requirement already satisfied: six<2.0.0,>=1.10.0 in ./venv37/lib/python3.7/site-packages (from chalice) (1.15.0)\\r\\n\"]\n[12.154232, \"o\", \"Requirement already satisfied: setuptools in ./venv37/lib/python3.7/site-packages (from chalice) (40.8.0)\\r\\n\"]\n[12.159711, \"o\", \"Requirement already satisfied: enum-compat>=0.0.2 in ./venv37/lib/python3.7/site-packages (from chalice) (0.0.3)\\r\\n\"]\n[12.162856, \"o\", \"Requirement already satisfied: docutils<0.16,>=0.10 in ./venv37/lib/python3.7/site-packages (from botocore<2.0.0,>=1.12.86->chalice) (0.15.2)\\r\\n\"]\n[12.166178, \"o\", \"Requirement already satisfied: urllib3<1.26,>=1.20; python_version != \\\"3.4\\\" in ./venv37/lib/python3.7/site-packages (from botocore<2.0.0,>=1.12.86->chalice) (1.25.9)\\r\\n\"]\n[12.17935, \"o\", \"Requirement already satisfied: python-dateutil<3.0.0,>=2.1 in ./venv37/lib/python3.7/site-packages (from botocore<2.0.0,>=1.12.86->chalice) (2.8.1)\\r\\n\"]\n[12.202073, \"o\", \"\\u001b[33mYou are using pip version 19.0.3, however version 20.2b1 is available.\\r\\nYou should consider upgrading via the 'pip install --upgrade pip' command.\\u001b[0m\\r\\n\"]\n[12.265649, \"o\", \"(venv37) /tmp $ \"]\n[12.98204, \"o\", \"c\"]\n[13.066351, \"o\", \"h\"]\n[13.155867, \"o\", \"a\"]\n[13.203798, \"o\", \"l\"]\n[13.264152, \"o\", \"i\"]\n[13.335991, \"o\", \"c\"]\n[13.375697, \"o\", \"e\"]\n[13.554865, \"o\", \" \"]\n[13.67067, \"o\", \"n\"]\n[13.747029, \"o\", \"e\"]\n[13.802796, \"o\", \"w\"]\n[13.898973, \"o\", \"-\"]\n[14.591986, \"o\", \"p\"]\n[15.039938, \"o\", \"r\"]\n[15.136758, \"o\", \"o\"]\n[15.183997, \"o\", \"j\"]\n[15.307781, \"o\", \"e\"]\n[15.395801, \"o\", \"c\"]\n[15.651701, \"o\", \"t\"]\n[15.750829, \"o\", \" \"]\n[15.909655, \"o\", \"q\"]\n[15.957861, \"o\", \"u\"]\n[16.021247, \"o\", \"i\"]\n[16.197586, \"o\", \"k\"]\n[16.285391, \"o\", \"s\"]\n[16.377484, \"o\", \"t\"]\n[16.465843, \"o\", \"a\"]\n[16.565281, \"o\", \"r\"]\n[16.941683, \"o\", \"\\b\\b\\b\\b\\b\\b\\b\\b\\u001b[K\"]\n[17.116486, \"o\", \"q\"]\n[17.192849, \"o\", \"u\"]\n[17.23197, \"o\", \"i\"]\n[17.287961, \"o\", \"c\"]\n[17.376716, \"o\", \"k\"]\n[17.431874, \"o\", \"s\"]\n[17.534897, \"o\", \"t\"]\n[17.574701, \"o\", \"a\"]\n[17.67868, \"o\", \"r\"]\n[17.815443, \"o\", \"t\"]\n[17.971564, \"o\", \"\\r\\n\"]\n[18.312339, \"o\", \"(venv37) /tmp $ \"]\n[19.017756, \"o\", \"c\"]\n[19.094431, \"o\", \"d\"]\n[19.146139, \"o\", \" \"]\n[19.301759, \"o\", \"q\"]\n[19.38554, \"o\", \"u\"]\n[19.410578, \"o\", \"i\"]\n[19.513519, \"o\", \"ckstart/\"]\n[20.029639, \"o\", \"\\r\\n\"]\n[20.066324, \"o\", \"(venv37) /tmp/quickstart $ \"]\n[21.855514, \"o\", \"t\"]\n[21.939564, \"o\", \"r\"]\n[21.954566, \"o\", \"e\"]\n[22.05956, \"o\", \"e\"]\n[22.487414, \"o\", \"\\r\\n\"]\n[22.494275, \"o\", \".\\r\\n├── app.py\\r\\n└── requirements.txt\\r\\n\\r\\n0 directories, 2 files\\r\\n\"]\n[22.504646, \"o\", \"(venv37) /tmp/quickstart $ \"]\n[23.627249, \"o\", \"c\"]\n[23.713762, \"o\", \"h\"]\n[23.79159, \"o\", \"a\"]\n[23.851259, \"o\", \"l\"]\n[23.907601, \"o\", \"i\"]\n[23.967212, \"o\", \"c\"]\n[24.019798, \"o\", \"e\"]\n[24.07564, \"o\", \" \"]\n[24.183206, \"o\", \"d\"]\n[24.313388, \"o\", \"e\"]\n[24.352526, \"o\", \"p\"]\n[24.409609, \"o\", \"l\"]\n[24.566365, \"o\", \"o\"]\n[24.674363, \"o\", \"y\"]\n[24.877033, \"o\", \"\\r\\n\"]\n[25.197886, \"o\", \"Creating deployment package.\\r\\n\"]\n[26.046612, \"o\", \"Creating IAM role: quickstart-dev\\r\\n\"]\n[27.100193, \"o\", \"Creating lambda function: quickstart-dev\\r\\n\"]\n[38.261393, \"o\", \"Creating Rest API\\r\\n\"]\n[40.005804, \"o\", \"Resources deployed:\\r\\n  - Lambda ARN: arn:aws:lambda:us-west-2:675687397822:function:quickstart-dev\\r\\n  - Rest API URL: https://b5fnh9kzs6.execute-api.us-west-2.amazonaws.com/api/\\r\\n\"]\n[40.080238, \"o\", \"(venv37) /tmp/quickstart $ \"]\n[43.567108, \"o\", \"h\"]\n[43.67546, \"o\", \"t\"]\n[43.82861, \"o\", \"t\"]\n[43.884828, \"o\", \"p\"]\n[43.972725, \"o\", \" \"]\n[44.350426, \"o\", \"htt\"]\n[44.350598, \"o\", \"ps://b5fnh9k\"]\n[44.350745, \"o\", \"zs6\"]\n[44.351105, \"o\", \".execute-api.us-west-2\"]\n[44.351233, \"o\", \".amaz\"]\n[44.351265, \"o\", \"ona\"]\n[44.351301, \"o\", \"w\"]\n[44.351453, \"o\", \"s.com/api\"]\n[44.351566, \"o\", \"/\"]\n[45.053598, \"o\", \"\\r\\n\"]\n[46.149458, \"o\", \"\\u001b[34mHTTP\\u001b[39;49;00m/\\u001b[34m1.1\\u001b[39;49;00m \\u001b[34m200\\u001b[39;49;00m \\u001b[36mOK\\u001b[39;49;00m\\r\\n\\u001b[36mConnection\\u001b[39;49;00m: keep-alive\\r\\n\\u001b[36mContent-Length\\u001b[39;49;00m: 17\\r\\n\\u001b[36mContent-Type\\u001b[39;49;00m: application/json\\r\\n\\u001b[36mDate\\u001b[39;49;00m: Mon, 29 Jun 2020 18:00:35 GMT\\r\\n\\u001b[36mVia\\u001b[39;49;00m: 1.1 c6b0d1d85b2590c57ac754bf9e61944f.cloudfront.net (CloudFront)\\r\\n\\u001b[36mX-Amz-Cf-Id\\u001b[39;49;00m: kJfXoAUfXWQ-87o10cQyqlvjCCbD5SKBMvwdViXWEKS7buwkwO27yg==\\r\\n\\u001b[36mX-Amz-Cf-Pop\\u001b[39;49;00m: IAD89-C1\\r\\n\\u001b[36mX-Amzn-Trace-Id\\u001b[39;49;00m: Root=1-5efa2c43-31c191ba91e097c5059cb023;Sampled=0\\r\\n\\u001b[36mX-Cache\\u001b[39;49;00m: Miss from cloudfront\\r\\n\\u001b[36mx-amz-apigw-id\\u001b[39;49;00m: O5vagEJGPHcFZ7w=\\r\\n\\u001b[36mx-amzn-RequestId\\u001b[39;49;00m: 33ee07ef-d19e-4987-b9e5-dfcecd3f1173\\r\\r\\n\\r\\r\\n\"]\n[46.152089, \"o\", \"{\\r\\n    \\u001b[34;01m\\\"hello\\\"\\u001b[39;49;00m: \\u001b[33m\\\"world\\\"\\u001b[39;49;00m\\r\\n}\\r\\n\\r\\n\"]\n[46.219896, \"o\", \"(venv37) /tmp/quickstart $ \"]\n[49.057166, \"o\", \"v\"]\n[49.137315, \"o\", \"i\"]\n[49.141611, \"o\", \"m\"]\n[49.198615, \"o\", \" \"]\n[49.390691, \"o\", \"a\"]\n[49.506651, \"o\", \"p\"]\n[49.658458, \"o\", \"p.py \"]\n[50.447641, \"o\", \"\\r\\n\"]\n[50.506808, \"o\", \"\\u001b[?1000h\\u001b[?2004h\\u001b[?1049h\\u001b[?1h\\u001b=\\u001b[?2004h\"]\n[50.512619, \"o\", \"\\u001b[1;40r\\u001b[?12h\\u001b[?12l\\u001b[27m\\u001b[29m\\u001b[m\\u001b[H\\u001b[2J\\u001b[?25l\\u001b[40;1H\\\"app.py\\\"\"]\n[50.512899, \"o\", \" 29L, 735C\"]\n[50.558635, \"o\", \"\\u001b[1;1H\\u001b[93m  1 \\u001b[mfrom chalice import Chalice\\r\\n\\u001b[93m  2 \\r\\n  3 \\u001b[mapp = Chalice(app_name='quickstart')\\r\\n\\u001b[93m  4 \\r\\n  5 \\r\\n  6 \\u001b[m@app.route('/')\\r\\n\\u001b[93m  7 \\u001b[mdef index():\\r\\n\\u001b[93m  8 \\u001b[m    return {'hello': 'world'}\\r\\n\\u001b[93m  9 \\r\\n 10 \\r\\n 11 \\u001b[m# The view function above will return {\\\"hello\\\": \\\"world\\\"}\\r\\n\\u001b[93m 12 \\u001b[m# whenever you make an HTTP GET request to '/'.\\r\\n\\u001b[93m 13 \\u001b[m#\\r\\n\\u001b[93m 14 \\u001b[m# Here are a few more examples:\\r\\n\\u001b[93m 15 \\u001b[m#\\r\\n\\u001b[93m 16 \\u001b[m# @app.route('/hello/{name}')\\r\\n\\u001b[93m 17 \\u001b[m# def hello_name(name):\\r\\n\\u001b[93m 18 \\u001b[m#    # '/hello/james' -> {\\\"hello\\\": \\\"james\\\"}\\r\\n\\u001b[93m 19 \\u001b[m#    return {'hello': name}\\r\\n\\u001b[93m 20 \\u001b[m#\\r\\n\\u001b[93m 21 \\u001b[m# @app.route('/users', methods=['POST'])\\r\\n\\u001b[93m 22 \\u001b[m# def create_user():\\r\\n\\u001b[93m 23 \\u001b[m#     # This is the JSON body the user sent in their POST request.\\r\\n\\u001b[93m 24 \\u001b[m#     user_as_json = app.current_request.json_body\\r\\n\\u001b[93m 25 \\u001b[m#     # We'll echo the json body back to the user in a 'user' key.\\r\\n\\u001b[93m 26 \\u001b[m#     return {'user': user_as_json}\\r\\n\\u001b[93m 27 \\u001b[m#\\r\\n\\u001b[93m 28 \\u001b[m# See the RE\"]\n[50.559194, \"o\", \"ADME documentation for more examples.\\r\\n\\u001b[93m 29 \\u001b[m#\\r\\n\\u001b[94m~                                                                                                                                                             \\u001b[31;1H~                                                                                                                                                             \\u001b[32;1H~                                                                                                                                                             \\u001b[33;1H~                                                                                                                                                             \\u001b[34;1H~                                                                                                                                                             \\u001b[35;1H~                                                                                                                                           \"]\n[50.559597, \"o\", \"                  \\u001b[36;1H~                                                                                                                                                             \\u001b[37;1H~                                                                                                                                                             \\u001b[38;1H~                                                                                                                                                             \\u001b[39;1H~                                                                                                                                                             \\u001b[m\\u001b[40;1H\\u001b[38;5;224m[Pymode] Activate virtualenv: /private/tmp/venv37\"]\n[50.587979, \"o\", \"\\u001b[2;1H▽\\u001b[6n\\u001b[2;1H  \\u001b[1;1H\"]\n[50.588141, \"o\", \"\\u001b[>c\\u001b]10;?\\u0007\\u001b]11;?\\u0007\"]\n[50.605848, \"o\", \"\\u001b[m\\u001b[1;5H\\u001b[38;5;81mfrom\\u001b[9Cimport\\u001b[m\\u001b[3;9H\\u001b[93m=\\u001b[17C=\\u001b[m\\u001b[95m'quickstart'\\u001b[m\\u001b[6;5H\\u001b[38;5;81m@\\u001b[m\\u001b[1m\\u001b[96mapp\\u001b[m.\\u001b[1m\\u001b[96mroute\\u001b[m(\\u001b[95m'/'\\u001b[m\\u001b[7;5H\\u001b[93mdef\\u001b[m \\u001b[1m\\u001b[96mindex\\u001b[m\\u001b[8;9H\\u001b[93mreturn\\u001b[m {\\u001b[95m'hello'\\u001b[m: \\u001b[95m'world'\\u001b[m\\u001b[11;5H\\u001b[96m# The view function above will return {\\\"hello\\\": \\\"world\\\"}\\u001b[12;5H# whenever you make an HTTP GET request to '/'.\\u001b[13;5H#\\u001b[14;5H# Here are a few more examples:\\u001b[15;5H#\\u001b[16;5H# @app.route('/hello/{name}')\\u001b[17;5H# def hello_name(name):\\u001b[18;5H#    # '/hello/james' -> {\\\"hello\\\": \\\"james\\\"}\\u001b[19;5H#    return {'hello': name}\\u001b[20;5H#\\u001b[21;5H# @app.route('/users', methods=['POST'])\\u001b[22;5H# def create_user():\\u001b[23;5H#     # This is the JSON body the user sent in their POST request.\\u001b[24;5H#     user_as_json = app.current_request.json_body\\u001b[25;5H#     # We'll echo the json body back to the user in a 'user' key.\\u001b[26;5H#     return {'user': user_as_json}\\u001b[27;5H#\\u001b[28;5H# See the README documentation for more examples.\\u001b[29;5H#\\u001b[m\\u001b[40;141H1,1\\u001b[11CAll\\u001b[1;5H\\u001b[?25h\"]\n[50.606087, \"o\", \"\\u001b[?1000l\\u001b[?1006h\\u001b[?1002h\\u001b[?1006l\\u001b[?1002l\\u001b[?1006h\\u001b[?1002h\\u001b[?12$p\"]\n[51.146831, \"o\", \"\\u001b[?25l\\u001b[40;141H2,0-1\\u001b[2;5H\\u001b[?25h\"]\n[51.306094, \"o\", \"\\u001b[?25l\\u001b[40;141H3,1  \\u001b[3;5H\\u001b[?25h\"]\n[51.469845, \"o\", \"\\u001b[?25l\\u001b[40;141H4,0-1\\u001b[4;5H\\u001b[?25h\"]\n[51.631969, \"o\", \"\\u001b[?25l\\u001b[40;141H5\\u001b[5;5H\\u001b[?25h\"]\n[52.234099, \"o\", \"\\u001b[?25l\\u001b[40;141H6,1  \\u001b[6;5H\\u001b[?25h\"]\n[52.419097, \"o\", \"\\u001b[?25l\\u001b[1C\\u001b[1m\\u001b[96m\\u001b[48;5;242mapp\\u001b[m\\u001b[48;5;242m.\\u001b[m\\u001b[1m\\u001b[96m\\u001b[48;5;242mroute\\u001b[m\\u001b[48;5;242m(\\u001b[m\\u001b[95m\\u001b[48;5;242m'/'\\u001b[m\\u001b[48;5;242m) \"]\n[52.419253, \"o\", \"\\u001b[m\\u001b[40;1H\\u001b[1m-- VISUAL LINE --\\u001b[m\\u001b[40;18H\\u001b[K\\u001b[40;141H6,1\\u001b[11CAll\\u001b[6;5H\\u001b[?25h\"]\n[52.524012, \"o\", \"\\u001b[?25l\\u001b[38;5;81m\\u001b[48;5;242m@\\u001b[m\\u001b[7;6H\\u001b[93m\\u001b[48;5;242mef\\u001b[m\\u001b[48;5;242m \\u001b[m\\u001b[1m\\u001b[96m\\u001b[48;5;242mindex\\u001b[m\\u001b[48;5;242m(): \\u001b[m\\u001b[40;141H7\\u001b[7;5H\\u001b[?25h\"]\n[52.658516, \"o\", \"\\u001b[?25l\\u001b[93m\\u001b[48;5;242md\\u001b[m\\u001b[8;6H\\u001b[48;5;242m   \\u001b[m\\u001b[93m\\u001b[48;5;242mreturn\\u001b[m\\u001b[48;5;242m {\\u001b[m\\u001b[95m\\u001b[48;5;242m'hello'\\u001b[m\\u001b[48;5;242m: \\u001b[m\\u001b[95m\\u001b[48;5;242m'world'\\u001b[m\\u001b[48;5;242m} \\u001b[m\\u001b[40;141H8\\u001b[8;5H\\u001b[?25h\"]\n[52.770352, \"o\", \"\\u001b[?25l\\u001b[48;5;242m \\u001b[m\\u001b[40;141H9,0-1\\u001b[9;5H\\u001b[?25h\"]\n[52.904444, \"o\", \"\\u001b[?25l\\u001b[6;5H\\u001b[38;5;81m@\\u001b[m\\u001b[1m\\u001b[96mapp\\u001b[m.\\u001b[1m\\u001b[96mroute\\u001b[m(\\u001b[95m'/'\\u001b[m)\\u001b[6;20H\\u001b[K\\u001b[7;5H\\u001b[93mdef\\u001b[m \\u001b[1m\\u001b[96mindex\\u001b[m():\\u001b[7;17H\\u001b[K\\u001b[8;5H    \\u001b[93mreturn\\u001b[m {\\u001b[95m'hello'\\u001b[m: \\u001b[95m'world'\\u001b[m}\\u001b[8;34H\\u001b[K\\u001b[40;1H\\u001b[K\\u001b[40;141H6,1\\u001b[11CAll\\r4 lines yanked\\u001b[40;141H\\u001b[K\"]\n[52.904636, \"o\", \"\\u001b[40;141H6,1\\u001b[11CAll\\u001b[6;5H\\u001b[?25h\"]\n[53.068878, \"o\", \"\\u001b[?25l\\u001b[40;141H7\\u001b[7;5H\\u001b[?25h\"]\n[53.188894, \"o\", \"\\u001b[?25l\\u001b[40;141H8\\u001b[8;5H\\u001b[?25h\"]\n[53.31296, \"o\", \"\\u001b[?25l\\u001b[40;141H9,0-1\\u001b[9;5H\\u001b[?25h\"]\n[53.404885, \"o\", \"\\u001b[?25l\\u001b[40;3Hmore lines\\u001b[40;13H\\u001b[K\"]\n[53.42306, \"o\", \"\\u001b[10;5H\\u001b[38;5;81m@\\u001b[m\\u001b[1m\\u001b[96mapp\\u001b[m.\\u001b[1m\\u001b[96mroute\\u001b[m(\\u001b[95m'/'\\u001b[m)\\u001b[11;5H\\u001b[93mdef\\u001b[m \\u001b[1m\\u001b[96mindex\\u001b[m():\\u001b[11;17H\\u001b[K\\u001b[12;5H    \\u001b[93mreturn\\u001b[m {\\u001b[95m'hello'\\u001b[m: \\u001b[95m'world'\\u001b[m}\\u001b[12;34H\\u001b[K\\u001b[13;5H\\u001b[K\\u001b[14;5H\\u001b[K\\u001b[15;6H\\u001b[96m The view function above will return {\\\"hello\\\": \\\"world\\\"}\\u001b[16;7Hwhenever you make an HTTP GET request to '/'.\\u001b[m\\u001b[17;6H\\u001b[K\\u001b[18;7H\\u001b[96mHere are a few more examples:\\u001b[m\\u001b[18;36H\\u001b[K\\u001b[19;6H\\u001b[K\\u001b[20;6H\\u001b[96m @app.route('/hello/{name}')\\u001b[21;7Hdef hello_name(name):\\u001b[m\\u001b[21;28H\\u001b[K\\u001b[22;7H\\u001b[96m   # '/hello/james' -> {\\\"hello\\\": \\\"james\\\"}\\u001b[23;10Hreturn {'hello': name}\\u001b[m\\u001b[23;32H\\u001b[K\\u001b[24;6H\\u001b[K\\u001b[25;7H\\u001b[96m@app.route('/users', methods=['POST'])\\u001b[m\\u001b[25;45H\\u001b[K\\u001b[26;7H\\u001b[96mdef create_user():\\u001b[m\\u001b[26;25H\\u001b[K\\u001b[27;6H\\u001b[96m     # This is the JSON body the user sent in their POST request.\\u001b[28;7H    user_as_json = app.current_request.json_body\\u001b[29;6H     # We'll echo the json body back to the user in a 'user' key.\\u001b[m\\r\\n\\u001b[93m 30 \\u001b[m\\u001b[96m#     return {'user': user_as_json}\\u001b[m\\u001b[30;40H\\u001b[K\\u001b[31;1H\\u001b[93m 31 \\u001b[m\\u001b[96m#\\u001b[m\\u001b[31;6H\\u001b[K\\u001b[32;1H\\u001b[\"]\n[53.423236, \"o\", \"93m 32 \\u001b[m\\u001b[96m# See the README documentation for more examples.\\u001b[m\\u001b[32;54H\\u001b[K\\u001b[33;1H\\u001b[93m 33 \\u001b[m\\u001b[96m#\\u001b[m\\u001b[33;6H\\u001b[K\\u001b[40;141H10,1\\u001b[10CAll\\u001b[40;141H\\u001b[K\\u001b[40;141H10,1\\u001b[10CAll\\u001b[10;5H\\u001b[?25h\"]\n[54.356725, \"o\", \"\\u001b[?25l\\u001b[10C\\u001b[46m(\\u001b[3C)\\u001b[m\\u001b[40;145H5\\u001b[10;19H\\u001b[?25h\"]\n[54.514045, \"o\", \"\\u001b[?25l\\b\\b\\b\\b(\\u001b[3C)\\u001b[40;145H4\\u001b[10;18H\\u001b[?25h\"]\n[54.664626, \"o\", \"\\u001b[?25l\\u001b[40;1H\\u001b[1m-- INSERT --\\u001b[m\\u001b[40;141H\\u001b[K\\u001b[40;141H10,14\\u001b[9CAll\"]\n[54.664783, \"o\", \"\\u001b[10;18H\\u001b[?25h\"]\n[54.853485, \"o\", \"\\u001b[?25l\\u001b[95mh'\\u001b[m)\\u001b[40;145H5\\u001b[10;19H\\u001b[?25h\"]\n[54.980214, \"o\", \"\\u001b[?25l\\u001b[95me'\\u001b[m)\\u001b[40;145H6\\u001b[10;20H\\u001b[?25h\"]\n[55.07667, \"o\", \"\\u001b[?25l\\u001b[95ml'\\u001b[m)\\u001b[40;145H7\\u001b[10;21H\\u001b[?25h\"]\n[55.1742, \"o\", \"\\u001b[?25l\\u001b[95ml'\\u001b[m)\\u001b[40;145H8\\u001b[10;22H\\u001b[?25h\"]\n[55.352723, \"o\", \"\\u001b[?25l\\u001b[95mo'\\u001b[m)\\u001b[40;145H9\\u001b[10;23H\\u001b[?25h\"]\n[55.688281, \"o\", \"\\u001b[?25l\\u001b[95m/'\\u001b[m)\\u001b[40;144H20\\u001b[10;24H\\u001b[?25h\"]\n[56.129681, \"o\", \"\\u001b[?25l\\u001b[95m{'\\u001b[m)\\u001b[40;145H1\\u001b[10;25H\\u001b[?25h\"]\n[56.32739, \"o\", \"\\u001b[?25l\\u001b[95mn'\\u001b[m)\\u001b[40;145H2\\u001b[10;26H\\u001b[?25h\"]\n[56.424794, \"o\", \"\\u001b[?25l\\u001b[95ma'\\u001b[m)\\u001b[40;145H3\\u001b[10;27H\\u001b[?25h\"]\n[56.482477, \"o\", \"\\u001b[?25l\\u001b[95mm'\\u001b[m)\\u001b[40;145H4\\u001b[10;28H\\u001b[?25h\"]\n[56.582245, \"o\", \"\\u001b[?25l\\u001b[95me'\\u001b[m)\\u001b[40;145H5\\u001b[10;29H\\u001b[?25h\"]\n[56.753992, \"o\", \"\\u001b[?25l\\b\\b\\b\\b\\b\\u001b[38;5;224m{name}\\u001b[m\\u001b[95m'\\u001b[m)\\u001b[40;145H6\\u001b[10;30H\\u001b[?25h\"]\n[56.956073, \"o\", \"\\u001b[40;1H\\u001b[K\\u001b[10;29H\"]\n[57.005342, \"o\", \"\\u001b[?25l\"]\n[57.006671, \"o\", \"\\u001b[40;141H10,25\\u001b[9CAll\\u001b[10;29H\\u001b[?25h\\u001b[?25l\\u001b[40;142H1,12\\u001b[11;16H\\u001b[?25h\"]\n[57.196056, \"o\", \"\\u001b[?25l\\b\\b\\u001b[46m()\\u001b[m\\u001b[40;145H1\\u001b[11;15H\\u001b[?25h\"]\n[57.432569, \"o\", \"\\u001b[?25l\\u001b[40;1H\\u001b[1m-- INSERT --\\u001b[m\\u001b[40;141H\\u001b[K\\u001b[40;141H11,11\\u001b[9CAll\"]\n[57.432697, \"o\", \"\\u001b[11;15H\\u001b[?25h\"]\n[57.602247, \"o\", \"\\u001b[?25l\\u001b[46mn\\u001b[m):\\b\\b\\bn\\u001b[46m)\\u001b[m\\u001b[40;145H2\\u001b[11;16H\\u001b[?25h\"]\n[57.683304, \"o\", \"\\u001b[?25l\\u001b[46ma\\u001b[m):\\b\\b\\ba\\u001b[46m)\\u001b[m\\u001b[40;145H3\\u001b[11;17H\\u001b[?25h\"]\n[57.774657, \"o\", \"\\u001b[?25l\\u001b[46mm\\u001b[m):\\b\\b\\bm\\u001b[46m)\\u001b[m\\u001b[40;145H4\\u001b[11;18H\\u001b[?25h\"]\n[57.8501, \"o\", \"\\u001b[?25l\\u001b[46me\\u001b[m):\\b\\b\\be\\u001b[46m)\\u001b[m\\u001b[40;145H5\\u001b[11;19H\\u001b[?25h\"]\n[57.956926, \"o\", \"\\u001b[40;1H\\u001b[K\\u001b[11;18H\"]\n[58.111393, \"o\", \"\\u001b[?25l\"]\n[58.113038, \"o\", \"\\b\\b\\b\\b(name)\\u001b[40;141H11,14\\u001b[9CAll\\u001b[11;18H\\u001b[?25h\\u001b[?25l\\u001b[40;145H \\u001b[11;5H\\u001b[?25h\"]\n[58.279802, \"o\", \"\\u001b[?25l\\u001b[40;144H5\\u001b[11;9H\\u001b[?25h\"]\n[58.791478, \"o\", \"\\u001b[?25l\\u001b[40;1H\\u001b[1m-- INSERT --\\u001b[m\\u001b[40;141H\\u001b[K\\u001b[40;141H11,5\\u001b[10CAll\"]\n[58.796463, \"o\", \"\\u001b[11;9H(name):\\u001b[11;16H\\u001b[K\\u001b[11;9H\\u001b[46m(\\u001b[mname\\u001b[46m)\\b\\b\\b\\b\\b\\b\\u001b[?25h\"]\n[58.911903, \"o\", \"\\u001b[?25l\\u001b[m\\u001b[1m\\u001b[96m\\u001b[46mh\\u001b[m(nam\\u001b[46me\\u001b[m):\\u001b[11;9H\\u001b[1m\\u001b[96mh\\u001b[m\\u001b[46m(\\u001b[mname\\u001b[46m)\\u001b[m\\u001b[40;144H6\\u001b[11;10H\\u001b[?25h\"]\n[59.002263, \"o\", \"\\u001b[?25l\\u001b[1m\\u001b[96m\\u001b[46me\\u001b[m(nam\\u001b[46me\\u001b[m):\\u001b[11;10H\\u001b[1m\\u001b[96me\\u001b[m\\u001b[46m(\\u001b[mname\\u001b[46m)\\u001b[m\\u001b[40;144H7\\u001b[11;11H\\u001b[?25h\"]\n[59.087106, \"o\", \"\\u001b[?25l\\u001b[1m\\u001b[96m\\u001b[46ml\\u001b[m(nam\\u001b[46me\\u001b[m):\\u001b[11;11H\\u001b[1m\\u001b[96ml\\u001b[m\\u001b[46m(\\u001b[mname\\u001b[46m)\\u001b[m\\u001b[40;144H8\\u001b[11;12H\\u001b[?25h\"]\n[59.197175, \"o\", \"\\u001b[?25l\\u001b[1m\\u001b[96m\\u001b[46ml\\u001b[m(nam\\u001b[46me\\u001b[m):\\u001b[11;12H\\u001b[1m\\u001b[96ml\\u001b[m\\u001b[46m(\\u001b[mname\\u001b[46m)\\u001b[m\\u001b[40;144H9\\u001b[11;13H\\u001b[?25h\"]\n[59.340986, \"o\", \"\\u001b[?25l\\u001b[1m\\u001b[96m\\u001b[46mo\\u001b[m(nam\\u001b[46me\\u001b[m):\\u001b[11;13H\\u001b[1m\\u001b[96mo\\u001b[m\\u001b[46m(\\u001b[mname\\u001b[46m)\\u001b[m\\u001b[40;144H10\\u001b[11;14H\\u001b[?25h\"]\n[59.478202, \"o\", \"\\u001b[40;1H\\u001b[K\\u001b[11;13H\"]\n[59.59759, \"o\", \"\\u001b[?25l\"]\n[59.599543, \"o\", \"\\u001b[1C(name)\\u001b[40;141H11,9\\u001b[10CAll\\u001b[11;13H\\u001b[?25h\\u001b[?25l\\u001b[40;142H2\\u001b[12;13H\\u001b[?25h\"]\n[60.309843, \"o\", \"\\u001b[?25l\\u001b[3C\\u001b[46m{\\u001b[16C}\\u001b[m\\u001b[40;144H12\\u001b[12;16H\\u001b[?25h\"]\n[60.430347, \"o\", \"\\u001b[?25l{\\u001b[16C}\\u001b[40;145H4\\u001b[12;18H\\u001b[?25h\"]\n[60.56452, \"o\", \"\\u001b[?25l\\u001b[40;145H9\\u001b[12;23H\\u001b[?25h\"]\n[60.705548, \"o\", \"\\u001b[?25l\\u001b[40;144H22\\u001b[12;26H\\u001b[?25h\"]\n[61.376546, \"o\", \"\\u001b[?25l\\u001b[40;1H\\u001b[1m-- INSERT --\\u001b[m\\u001b[40;141H\\u001b[K\\u001b[40;141H12,22\\u001b[9CAll\"]\n[61.381539, \"o\", \"\\u001b[12;26H}\\u001b[12;27H\\u001b[K\\u001b[12;16H\\u001b[46m{\\u001b[9C}\\b\\u001b[?25h\"]\n[61.933986, \"o\", \"\\u001b[?25ln\\u001b[m}\\b\\bn\\u001b[46m}\\u001b[m\\u001b[40;145H3\\u001b[12;27H\\u001b[?25h\"]\n[62.020328, \"o\", \"\\u001b[?25l\\u001b[46ma\\u001b[m}\\b\\ba\\u001b[46m}\\u001b[m\\u001b[40;145H4\\u001b[12;28H\\u001b[?25h\"]\n[62.088359, \"o\", \"\\u001b[?25l\\u001b[46mm\\u001b[m}\\b\\bm\\u001b[46m}\\u001b[m\\u001b[40;145H5\\u001b[12;29H\\u001b[?25h\"]\n[62.173839, \"o\", \"\\u001b[?25l\\u001b[46me\\u001b[m}\\b\\be\\u001b[46m}\\u001b[m\\u001b[40;145H6\\u001b[12;30H\\u001b[?25h\"]\n[62.436729, \"o\", \"\\u001b[40;1H\\u001b[K\\u001b[12;29H\"]\n[62.575634, \"o\", \"\\u001b[?25l\"]\n[62.576755, \"o\", \"\\u001b[12;16H{\\u001b[13C}\\u001b[40;141H12,25\\u001b[9CAll\\u001b[12;29H\\u001b[?25h\\u001b[?25l\\u001b[40;142H3,0-1\\u001b[13;5H\\u001b[?25h\"]\n[62.867449, \"o\", \"\\u001b[?25l\\u001b[40;1H21 fewer lines\\u001b[40;141H\\u001b[K\"]\n[62.868158, \"o\", \"\\u001b[13;1H\\u001b[94m~                                                                                                                                                             \\u001b[14;1H~                                                                                                                                                             \\u001b[15;1H~                                                                                                                                                             \\u001b[16;1H~                                                                                                                                                             \\u001b[17;1H~                                                                                                                                                             \\u001b[18;1H~                                                                                                                                                             \\u001b[19;1H~                     \"]\n[62.868378, \"o\", \"                                                                                                                                        \\u001b[20;1H~                                                                                                                                                             \\u001b[21;1H~                                                                                                                                                             \\u001b[22;1H~                                                                                                                                                             \\u001b[23;1H~                                                                                                                                                             \\u001b[24;1H~                                                                                                                                                             \\u001b[25;1H~                                                       \"]\n[62.86863, \"o\", \"                                                                                                      \\u001b[26;1H~                                                                                                                                                             \\u001b[27;1H~                                                                                                                                                             \\u001b[28;1H~                                                                                                                                                             \\u001b[29;1H~                                                                                                                                                             \\u001b[30;1H~                                                                                                                                                             \\u001b[31;1H~                                                                                         \"]\n[62.868824, \"o\", \"                                                                    \\u001b[32;1H~                                                                                                                                                             \\u001b[33;1H~                                                                                                                                                             \\u001b[m\\u001b[40;141H12,5\\u001b[10CAll\\u001b[40;141H\\u001b[K\\u001b[40;141H12,5\\u001b[10CAll\\u001b[12;9H\\u001b[?25h\"]\n[63.205557, \"o\", \"\\u0007\\u001b[?25l\\u001b[?25h\\u001b[?25l\\u001b[40;1H\\u001b[K\\u001b[40;1H:\\u001b[?2004h\"]\n[63.205812, \"o\", \"\\u001b[?25h\"]\n[63.324591, \"o\", \"w\\u001b[?25l\\u001b[?25h\"]\n[63.348525, \"o\", \"q\"]\n[63.348727, \"o\", \"\\u001b[?25l\\u001b[?25h\"]\n[63.909529, \"o\", \"\\u001b[?25l\\u001b[40;1H\\u001b[K\\u001b[40;141H12,5\\u001b[10CAll\\u001b[12;9H\\u001b[?25h\"]\n[64.012766, \"o\", \"\\u0007\\u001b[?25l\\u001b[40;142H1\\u001b[11;9H\\u001b[?25h\"]\n[64.131252, \"o\", \"\\u001b[?25l\\u001b[40;142H0\\u001b[10;9H\\u001b[?25h\"]\n[64.267846, \"o\", \"\\u001b[?25l\\u001b[40;141H9,0-1\\u001b[9;5H\\u001b[?25h\"]\n[64.654867, \"o\", \"\\u001b[?25l\\u001b[40;1H\\u001b[1m-- INSERT --\\u001b[m\\u001b[40;141H\\u001b[K\\u001b[40;141H10,1\\u001b[10CTop\"]\n[64.656708, \"o\", \"\\u001b[10;5H\\u001b[K\\u001b[11;5H\\u001b[38;5;81m@\\u001b[m\\u001b[1m\\u001b[96mapp\\u001b[m.\\u001b[1m\\u001b[96mroute\\u001b[m(\\u001b[95m'/hello/\\u001b[m\\u001b[38;5;224m{name}\\u001b[m\\u001b[95m'\\u001b[m)\\u001b[12;5H\\u001b[93mdef\\u001b[m \\u001b[1m\\u001b[96mhello\\u001b[m(name):\\u001b[12;21H\\u001b[K\\u001b[13;1H\\u001b[93m 13 \\u001b[m    \\u001b[93mreturn\\u001b[m {\\u001b[95m'hello'\\u001b[m: name}\\u001b[13;31H\\u001b[K\\u001b[10;5H\\u001b[?25h\"]\n[64.911661, \"o\", \"\\u001b[40;1H\\u001b[K\\u001b[10;5H\"]\n[65.200368, \"o\", \"\\u001b[?25l\"]\n[65.200588, \"o\", \"\\u001b[40;141H10,0-1\\u001b[8CAll\\u001b[10;5H\\u001b[?25h\\u001b[?25l\\u001b[40;141H\\u001b[K\\u001b[40;1H:\\u001b[?2004h\\u001b[?25h\"]\n[65.332362, \"o\", \"w\\u001b[?25l\\u001b[?25h\"]\n[65.367727, \"o\", \"q\\u001b[?25l\\u001b[?25h\"]\n[66.912049, \"o\", \"\\r\"]\n[66.912257, \"o\", \"\\u001b[?25l\\u001b[?1006l\\u001b[?1002l\\u001b[?2004l\"]\n[66.91259, \"o\", \"\\\"app.py\\\"\"]\n[66.914931, \"o\", \" 13L, 201C written\"]\n[67.026347, \"o\", \"\\r\\r\\r\\n\\u001b[?2004l\\u001b[?1l\\u001b>\\u001b[?25h\\u001b[?1049l\"]\n[67.042676, \"o\", \"(venv37) /tmp/quickstart $ \"]\n[67.389317, \"o\", \"c\"]\n[67.465545, \"o\", \"h\"]\n[67.564323, \"o\", \"a\"]\n[67.607364, \"o\", \"l\"]\n[67.667366, \"o\", \"i\"]\n[67.743259, \"o\", \"c\"]\n[67.79956, \"o\", \"e\"]\n[67.883361, \"o\", \" \"]\n[67.99061, \"o\", \"d\"]\n[68.123009, \"o\", \"e\"]\n[68.164435, \"o\", \"p\"]\n[68.212231, \"o\", \"l\"]\n[68.355133, \"o\", \"o\"]\n[68.481266, \"o\", \"y\"]\n[68.756228, \"o\", \"\\r\\n\"]\n[69.130517, \"o\", \"Creating deployment package.\\r\\n\"]\n[70.214404, \"o\", \"Updating policy for IAM role: quickstart-dev\\r\\n\"]\n[70.279891, \"o\", \"Updating lambda function: quickstart-dev\\r\\n\"]\n[71.065479, \"o\", \"Updating rest API\\r\\n\"]\n[72.568024, \"o\", \"Resources deployed:\\r\\n  - Lambda ARN: arn:aws:lambda:us-west-2:675687397822:function:quickstart-dev\\r\\n  - Rest API URL: https://b5fnh9kzs6.execute-api.us-west-2.amazonaws.com/api/\\r\\n\"]\n[72.646763, \"o\", \"(venv37) /tmp/quickstart $ \"]\n[74.602372, \"o\", \"chalice deploy\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\"]\n[74.833353, \"o\", \"\\u001b[3Pvim app.py \\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\"]\n[75.247373, \"o\", \"http https://b5fnh9kzs6.execute-api.us-west-2.amazonaws.com/api/\\r\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\"]\n[76.011695, \"o\", \"\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\\u001b[C\"]\n[76.460939, \"o\", \"h\"]\n[76.593905, \"o\", \"e\"]\n[76.682525, \"o\", \"l\"]\n[76.8132, \"o\", \"l\"]\n[76.96527, \"o\", \"o\"]\n[77.257256, \"o\", \"/\"]\n[77.485964, \"o\", \"j\"]\n[77.628394, \"o\", \"a\"]\n[77.67508, \"o\", \"m\"]\n[77.810034, \"o\", \"e\"]\n[77.846264, \"o\", \"s\"]\n[79.500303, \"o\", \"\\r\\n\"]\n[80.556672, \"o\", \"\\u001b[34mHTTP\\u001b[39;49;00m/\\u001b[34m1.1\\u001b[39;49;00m \\u001b[34m200\\u001b[39;49;00m \\u001b[36mOK\\u001b[39;49;00m\\r\\n\\u001b[36mConnection\\u001b[39;49;00m: keep-alive\\r\\n\\u001b[36mContent-Length\\u001b[39;49;00m: 17\\r\\n\\u001b[36mContent-Type\\u001b[39;49;00m: application/json\\r\\n\\u001b[36mDate\\u001b[39;49;00m: Mon, 29 Jun 2020 18:01:09 GMT\\r\\n\\u001b[36mVia\\u001b[39;49;00m: 1.1 fba666ceffdeb316c8edf476d8994bd5.cloudfront.net (CloudFront)\\r\\n\\u001b[36mX-Amz-Cf-Id\\u001b[39;49;00m: Q1-NF5uST8iVuyrnEnclDVa-Ji-SxHUqbaqJxJWhEyL9TMnincbIkQ==\\r\\n\\u001b[36mX-Amz-Cf-Pop\\u001b[39;49;00m: IAD89-C1\\r\\n\\u001b[36mX-Amzn-Trace-Id\\u001b[39;49;00m: Root=1-5efa2c65-84110420cb5f38eaa1c19888;Sampled=0\\r\\n\\u001b[36mX-Cache\\u001b[39;49;00m: Miss from cloudfront\\r\\n\\u001b[36mx-amz-apigw-id\\u001b[39;49;00m: O5vf3FeCvHcFX3Q=\\r\\n\\u001b[36mx-amzn-RequestId\\u001b[39;49;00m: 6a6ce2d8-74cb-4dcd-a5a0-9b55b911f7e2\\r\\r\\n\\r\\r\\n\"]\n[80.558673, \"o\", \"{\\r\\n    \\u001b[34;01m\\\"hello\\\"\\u001b[39;49;00m: \\u001b[33m\\\"james\\\"\\u001b[39;49;00m\\r\\n}\\r\\n\\r\\n\"]\n[80.603004, \"o\", \"(venv37) /tmp/quickstart $ \"]\n[85.496612, \"o\", \"c\"]\n[85.543118, \"o\", \"h\"]\n[85.62348, \"o\", \"a\"]\n[85.703524, \"o\", \"l\"]\n[85.75966, \"o\", \"i\"]\n[85.81519, \"o\", \"c\"]\n[85.89096, \"o\", \"e\"]\n[85.97549, \"o\", \" \"]\n[86.174666, \"o\", \"d\"]\n[86.303506, \"o\", \"e\"]\n[86.419472, \"o\", \"l\"]\n[86.474898, \"o\", \"e\"]\n[86.570301, \"o\", \"t\"]\n[86.647516, \"o\", \"e\"]\n[87.030095, \"o\", \"\\r\\n\"]\n[87.340116, \"o\", \"Deleting Rest API: b5fnh9kzs6\\r\\n\"]\n[88.075667, \"o\", \"Deleting function: arn:aws:lambda:us-west-2:675687397822:function:quickstart-dev\\r\\n\"]\n[88.601449, \"o\", \"Deleting IAM role: quickstart-dev\\r\\n\"]\n[88.906189, \"o\", \"(venv37) /tmp/quickstart $ \"]\n[90.826624, \"o\", \"exit\\r\\n\"]\n"
  },
  {
    "path": "docs/source/theme/smithy/static/casts/chalice-quickstart.cast",
    "content": "{\"version\": 2, \"width\": 87, \"height\": 23, \"timestamp\": 1593531270, \"env\": {\"SHELL\": \"/bin/bash\", \"TERM\": \"xterm-256color\"}}\n[1.67467, \"o\", \"/tmp $ \"]\n[2.82912, \"o\", \"p\"]\n[2.908222, \"o\", \"y\"]\n[3.054837, \"o\", \"t\"]\n[3.159015, \"o\", \"h\"]\n[3.236088, \"o\", \"o\"]\n[3.358366, \"o\", \"n\"]\n[3.506435, \"o\", \"3\"]\n[3.646629, \"o\", \" \"]\n[3.800527, \"o\", \"-\"]\n[3.874477, \"o\", \"m\"]\n[3.939706, \"o\", \" \"]\n[4.113357, \"o\", \"v\"]\n[4.206063, \"o\", \"e\"]\n[4.298333, \"o\", \"n\"]\n[4.394909, \"o\", \"v\"]\n[4.470036, \"o\", \" \"]\n[4.534614, \"o\", \"/\"]\n[4.664571, \"o\", \"t\"]\n[4.728648, \"o\", \"m\"]\n[4.792779, \"o\", \"p\"]\n[4.874453, \"o\", \"/\"]\n[5.10581, \"o\", \"v\"]\n[5.258582, \"o\", \"e\"]\n[5.35144, \"o\", \"n\"]\n[5.451347, \"o\", \"v\"]\n[5.742178, \"o\", \"3\"]\n[5.850467, \"o\", \"7\"]\n[6.310548, \"o\", \"\\r\\n\"]\n[7.626815, \"o\", \"/tmp $ \"]\n[8.061287, \"o\", \".\"]\n[8.154682, \"o\", \" \"]\n[8.23327, \"o\", \"/\"]\n[8.392318, \"o\", \"t\"]\n[8.472022, \"o\", \"m\"]\n[8.524285, \"o\", \"p\"]\n[8.604116, \"o\", \"/\"]\n[8.809077, \"o\", \"v\"]\n[8.963934, \"o\", \"e\"]\n[9.191374, \"o\", \"nv37/\"]\n[9.90222, \"o\", \"b\"]\n[9.954488, \"o\", \"i\"]\n[10.010006, \"o\", \"n\"]\n[10.08251, \"o\", \"/\"]\n[10.33724, \"o\", \"a\"]\n[10.425233, \"o\", \"c\"]\n[10.597212, \"o\", \"tivate\"]\n[10.986728, \"o\", \"\\r\\n\"]\n[10.99959, \"o\", \"(venv37) /tmp $ \"]\n[11.522134, \"o\", \"p\"]\n[11.653115, \"o\", \"i\"]\n[11.716907, \"o\", \"p\"]\n[11.809288, \"o\", \" \"]\n[11.853119, \"o\", \"i\"]\n[11.929131, \"o\", \"n\"]\n[11.996453, \"o\", \"s\"]\n[12.029432, \"o\", \"t\"]\n[12.129105, \"o\", \"a\"]\n[12.197512, \"o\", \"l\"]\n[12.297103, \"o\", \"l\"]\n[12.360499, \"o\", \" \"]\n[12.441458, \"o\", \"c\"]\n[12.51321, \"o\", \"h\"]\n[12.588779, \"o\", \"a\"]\n[12.681105, \"o\", \"l\"]\n[12.729368, \"o\", \"i\"]\n[12.785045, \"o\", \"c\"]\n[12.825263, \"o\", \"e\"]\n[13.08795, \"o\", \"\\r\\n\"]\n[13.559207, \"o\", \"Requirement already satisfied: chalice in ./venv37/lib/python3.7/site-packages (1.15.1)\\r\\n\"]\n[13.576265, \"o\", \"Requirement already satisfied: botocore<2.0.0,>=1.12.86 in ./venv37/lib/python3.7/site-packages (from chalice) (1.17.12)\\r\\n\"]\n[13.585476, \"o\", \"Requirement already satisfied: pyyaml<6.0.0,>=5.3.1 in ./venv37/lib/python3.7/site-packages (from chalice) (5.3.1)\\r\\n\"]\n[13.590093, \"o\", \"Requirement already satisfied: wheel in ./venv37/lib/python3.7/site-packages (from chalice) (0.34.2)\\r\\n\"]\n[13.594832, \"o\", \"Requirement already satisfied: click<8.0,>=6.6 in ./venv37/lib/python3.7/site-packages (from chalice) (7.1.2)\\r\\n\"]\n[13.597284, \"o\", \"Requirement already satisfied: setuptools in ./venv37/lib/python3.7/site-packages (from chalice) (40.8.0)\\r\\n\"]\n[13.602554, \"o\", \"Requirement already satisfied: mypy-extensions==0.4.3 in ./venv37/lib/python3.7/site-packages (from chalice) (0.4.3)\\r\\n\"]\n[13.608629, \"o\", \"Requirement already satisfied: attrs<20.0.0,>=19.3.0 in ./venv37/lib/python3.7/site-packages (from chalice) (19.3.0)\\r\\n\"]\n[13.642509, \"o\", \"Requirement already satisfied: pip<20.2,>=9 in ./venv37/lib/python3.7/site-packages (from chalice) (19.0.3)\\r\\n\"]\n[13.645222, \"o\", \"Requirement already satisfied: enum-compat>=0.0.2 in ./venv37/lib/python3.7/site-packages (from chalice) (0.0.3)\\r\\n\"]\n[13.648654, \"o\", \"Requirement already satisfied: six<2.0.0,>=1.10.0 in ./venv37/lib/python3.7/site-packages (from chalice) (1.15.0)\\r\\n\"]\n[13.651191, \"o\", \"Requirement already satisfied: jmespath<1.0.0,>=0.9.3 in ./venv37/lib/python3.7/site-packages (from chalice) (0.10.0)\\r\\n\"]\n[13.655427, \"o\", \"Requirement already satisfied: docutils<0.16,>=0.10 in ./venv37/lib/python3.7/site-packages (from botocore<2.0.0,>=1.12.86->chalice) (0.15.2)\\r\\n\"]\n[13.661959, \"o\", \"Requirement already satisfied: python-dateutil<3.0.0,>=2.1 in ./venv37/lib/python3.7/site-packages (from botocore<2.0.0,>=1.12.86->chalice) (2.8.1)\\r\\n\"]\n[13.666173, \"o\", \"Requirement already satisfied: urllib3<1.26,>=1.20; python_version != \\\"3.4\\\" in ./venv37/lib/python3.7/site-packages (from botocore<2.0.0,>=1.12.86->chalice) (1.25.9)\\r\\n\"]\n[13.704113, \"o\", \"\\u001b[33mYou are using pip version 19.0.3, however version 20.2b1 is available.\\r\\nYou should consider upgrading via the 'pip install --upgrade pip' command.\\u001b[0m\\r\\n\"]\n[13.765639, \"o\", \"(venv37) /tmp $ \"]\n[15.423004, \"o\", \"c\"]\n[15.494675, \"o\", \"h\"]\n[15.625856, \"o\", \"a\"]\n[15.924079, \"o\", \"l\"]\n[15.99449, \"o\", \"i\"]\n[16.055846, \"o\", \"c\"]\n[16.134495, \"o\", \"e\"]\n[16.258072, \"o\", \" \"]\n[16.333947, \"o\", \"n\"]\n[16.393892, \"o\", \"e\"]\n[16.453783, \"o\", \"w\"]\n[16.572702, \"o\", \"-\"]\n[16.713632, \"o\", \"p\"]\n[16.808701, \"o\", \"r\"]\n[16.869814, \"o\", \"o\"]\n[16.929625, \"o\", \"j\"]\n[17.013662, \"o\", \"e\"]\n[17.089494, \"o\", \"c\"]\n[17.310739, \"o\", \"t\"]\n[17.391292, \"o\", \" \"]\n[17.537597, \"o\", \"q\"]\n[17.605757, \"o\", \"u\"]\n[17.66049, \"o\", \"i\"]\n[17.774063, \"o\", \"c\"]\n[17.890681, \"o\", \"k\"]\n[18.226816, \"o\", \"s\"]\n[18.314648, \"o\", \"t\"]\n[18.44831, \"o\", \"a\"]\n[18.507571, \"o\", \"r\"]\n[18.671791, \"o\", \"t\"]\n[18.774308, \"o\", \"\\r\\n\"]\n[19.11508, \"o\", \"(venv37) /tmp $ \"]\n[19.594546, \"o\", \"c\"]\n[19.658678, \"o\", \"d\"]\n[19.73047, \"o\", \" \"]\n[19.90467, \"o\", \"q\"]\n[19.995339, \"o\", \"u\"]\n[20.043158, \"o\", \"i\"]\n[20.147159, \"o\", \"ckstart/\"]\n[20.563526, \"o\", \"\\r\\n\"]\n[20.598524, \"o\", \"(venv37) /tmp/quickstart $ \"]\n[21.372239, \"o\", \"t\"]\n[21.448471, \"o\", \"r\"]\n[21.484382, \"o\", \"e\"]\n[21.725807, \"o\", \"e\"]\n[21.943864, \"o\", \"\\r\\n\"]\n[21.954549, \"o\", \".\\r\\n├── app.py\\r\\n└── requirements.txt\\r\\n\\r\\n0 directories, 2 files\\r\\n\"]\n[21.966425, \"o\", \"(venv37) /tmp/quickstart $ \"]\n[23.820448, \"o\", \"c\"]\n[23.901148, \"o\", \"h\"]\n[23.988039, \"o\", \"a\"]\n[24.067824, \"o\", \"l\"]\n[24.11203, \"o\", \"i\"]\n[24.187865, \"o\", \"c\"]\n[24.264505, \"o\", \"e\"]\n[24.295824, \"o\", \" \"]\n[24.390982, \"o\", \"d\"]\n[24.534279, \"o\", \"e\"]\n[24.573985, \"o\", \"p\"]\n[24.621019, \"o\", \"l\"]\n[24.783132, \"o\", \"o\"]\n[24.891243, \"o\", \"y\"]\n[25.192351, \"o\", \"\\r\\n\"]\n[25.535327, \"o\", \"Creating deployment package.\\r\\n\"]\n[26.122212, \"o\", \"Creating IAM role: quickstart-dev\\r\\n\"]\n[26.291715, \"o\", \"Creating lambda function: quickstart-dev\\r\\n\"]\n[37.103502, \"o\", \"Creating Rest API\\r\\n\"]\n[38.421184, \"o\", \"Resources deployed:\\r\\n  - Lambda ARN: arn:aws:lambda:us-west-2:675687397822:function:quickstart-dev\\r\\n  - Rest API URL: https://vdpmshluug.execute-api.us-west-2.amazonaws.com/api/\\r\\n\"]\n[38.49129, \"o\", \"(venv37) /tmp/quickstart $ \"]\n[43.017523, \"o\", \"h\"]\n[43.12237, \"o\", \"t\"]\n[43.250348, \"o\", \"t\"]\n[43.329082, \"o\", \"p\"]\n[43.377087, \"o\", \" \"]\n[43.636587, \"o\", \"ht\"]\n[43.636784, \"o\", \"tps://vdpmshluug.e\"]\n[43.636878, \"o\", \"xecute-a\"]\n[43.636913, \"o\", \"pi.\"]\n[43.636942, \"o\", \"us-\"]\n[43.636967, \"o\", \"we\"]\n[43.636989, \"o\", \"st\"]\n[43.637241, \"o\", \"-2\"]\n[43.637339, \"o\", \".amazonaws.com/ \\rapi/\"]\n[44.53644, \"o\", \"\\r\\n\"]\n[45.509916, \"o\", \"\\u001b[34mHTTP\\u001b[39;49;00m/\\u001b[34m1.1\\u001b[39;49;00m \\u001b[34m200\\u001b[39;49;00m \\u001b[36mOK\\u001b[39;49;00m\\r\\n\\u001b[36mConnection\\u001b[39;49;00m: keep-alive\\r\\n\\u001b[36mContent-Length\\u001b[39;49;00m: 17\\r\\n\\u001b[36mContent-Type\\u001b[39;49;00m: application/json\\r\\n\\u001b[36mDate\\u001b[39;49;00m: Tue, 30 Jun 2020 15:35:15 GMT\\r\\n\\u001b[36mVia\\u001b[39;49;00m: 1.1 2d922ab79d41a826404f05ff416bb98c.cloudfront.net (CloudFront)\\r\\n\\u001b[36mX-Amz-Cf-Id\\u001b[39;49;00m: 4A5pVKErCSnHdfawudfAY12sTIIe4c1yaZ3VhGReZkcsPBoq3DIJpw==\\r\\n\\u001b[36mX-Amz-Cf-Pop\\u001b[39;49;00m: EWR53-C1\\r\\n\\u001b[36mX-Amzn-Trace-Id\\u001b[39;49;00m: Root=1-5efb5bb3-ece83f6c31b38a9f1fefd9ac;Sampled=0\\r\\n\\u001b[36mX-Cache\\u001b[39;49;00m: Miss from cloudfront\\r\\n\\u001b[36mx-amz-apigw-id\\u001b[39;49;00m: O8tEBGN4vHcFr7g=\\r\\n\\u001b[36mx-amzn-RequestId\\u001b[39;49;00m: 6a63cf9d-de05-4d36-aba1-67806adda070\\r\\r\\n\\r\\r\\n\"]\n[45.511603, \"o\", \"{\\r\\n    \\u001b[34;01m\\\"hello\\\"\\u001b[39;49;00m: \\u001b[33m\\\"world\\\"\\u001b[39;49;00m\\r\\n}\\r\\n\\r\\n\"]\n[45.553016, \"o\", \"(venv37) /tmp/quickstart $ \"]\n[47.356873, \"o\", \"v\"]\n[47.457163, \"o\", \"i\"]\n[47.496395, \"o\", \"m\"]\n[47.539968, \"o\", \" \"]\n[47.698229, \"o\", \"a\"]\n[47.778021, \"o\", \"p\"]\n[47.943521, \"o\", \"p.py \"]\n[48.343912, \"o\", \"\\r\\n\"]\n[48.398523, \"o\", \"\\u001b[?1000h\\u001b[?2004h\\u001b[?1049h\\u001b[?1h\\u001b=\\u001b[?2004h\"]\n[48.402476, \"o\", \"\\u001b[1;23r\\u001b[?12h\\u001b[?12l\\u001b[27m\\u001b[29m\\u001b[m\\u001b[H\\u001b[2J\\u001b[?25l\\u001b[23;1H\\\"app.py\\\"\"]\n[48.402636, \"o\", \" 29L, 735C\"]\n[48.46919, \"o\", \"\\u001b[1;1H\\u001b[93m  1 \\u001b[mfrom chalice import Chalice\\r\\n\\u001b[93m  2 \\r\\n  3 \\u001b[mapp = Chalice(app_name='quickstart')\\r\\n\\u001b[93m  4 \\r\\n  5 \\r\\n  6 \\u001b[m@app.route('/')\\r\\n\\u001b[93m  7 \\u001b[mdef index():\\r\\n\\u001b[93m  8 \\u001b[m    return {'hello': 'world'}\\r\\n\\u001b[93m  9 \\r\\n 10 \\r\\n 11 \\u001b[m# The view function above will return {\\\"hello\\\": \\\"world\\\"}\\r\\n\\u001b[93m 12 \\u001b[m# whenever you make an HTTP GET request to '/'.\\r\\n\\u001b[93m 13 \\u001b[m#\\r\\n\\u001b[93m 14 \\u001b[m# Here are a few more examples:\\r\\n\\u001b[93m 15 \\u001b[m#\\r\\n\\u001b[93m 16 \\u001b[m# @app.route('/hello/{name}')\\r\\n\\u001b[93m 17 \\u001b[m# def hello_name(name):\\r\\n\\u001b[93m 18 \\u001b[m#    # '/hello/james' -> {\\\"hello\\\": \\\"james\\\"}\\r\\n\\u001b[93m 19 \\u001b[m#    return {'hello': name}\\r\\n\\u001b[93m 20 \\u001b[m#\\r\\n\\u001b[93m 21 \\u001b[m# @app.route('/users', methods=['POST'])\\r\\n\\u001b[93m 22 \\u001b[m# def create_user():\\r\\n\\u001b[38;5;224m[Pymode] Activate virtualenv: /tmp/venv37\"]\n[48.499132, \"o\", \"\\u001b[2;1H▽\\u001b[6n\\u001b[2;1H  \\u001b[1;1H\\u001b[>c\"]\n[48.499279, \"o\", \"\\u001b]10;?\\u0007\\u001b]11;?\\u0007\"]\n[48.510137, \"o\", \"\\u001b[m\\u001b[1;5H\\u001b[38;5;81mfrom\\u001b[9Cimport\\u001b[m\\u001b[3;9H\\u001b[93m=\\u001b[17C=\\u001b[m\\u001b[95m'quickstart'\\u001b[m\\u001b[6;5H\\u001b[38;5;81m@\\u001b[m\\u001b[1m\\u001b[96mapp\\u001b[m.\\u001b[1m\\u001b[96mroute\\u001b[m(\\u001b[95m'/'\\u001b[m\\u001b[7;5H\\u001b[93mdef\\u001b[m \\u001b[1m\\u001b[96mindex\\u001b[m\\u001b[8;9H\\u001b[93mreturn\\u001b[m {\\u001b[95m'hello'\\u001b[m: \\u001b[95m'world'\\u001b[m\\u001b[11;5H\\u001b[96m# The view function above will return {\\\"hello\\\": \\\"world\\\"}\\u001b[12;5H# whenever you make an HTTP GET request to '/'.\\u001b[13;5H#\\u001b[14;5H# Here are a few more examples:\\u001b[15;5H#\\u001b[16;5H# @app.route('/hello/{name}')\\u001b[17;5H# def hello_name(name):\\u001b[18;5H#    # '/hello/james' -> {\\\"hello\\\": \\\"james\\\"}\\u001b[19;5H#    return {'hello': name}\\u001b[20;5H#\\u001b[21;5H# @app.route('/users', methods=['POST'])\\u001b[22;5H# def create_user():\\u001b[m\\u001b[23;70H1,1\\u001b[11CTop\\u001b[1;5H\\u001b[?25h\"]\n[48.510277, \"o\", \"\\u001b[?1000l\\u001b[?1006h\\u001b[?1002h\\u001b[?1006l\\u001b[?1002l\\u001b[?1006h\\u001b[?1002h\\u001b[?12$p\"]\n[49.693463, \"o\", \"\\u001b[?25l\\u001b[23;70H2,0-1\\u001b[2;5H\\u001b[?25h\"]\n[49.870311, \"o\", \"\\u001b[?25l\\u001b[23;70H3,1  \\u001b[3;5H\\u001b[?25h\"]\n[50.029084, \"o\", \"\\u001b[?25l\\u001b[23;70H4,0-1\\u001b[4;5H\\u001b[?25h\"]\n[50.167341, \"o\", \"\\u001b[?25l\\u001b[23;70H5\\u001b[5;5H\\u001b[?25h\"]\n[50.305287, \"o\", \"\\u001b[?25l\\u001b[23;70H6,1  \\u001b[6;5H\\u001b[?25h\"]\n[50.453483, \"o\", \"\\u001b[?25l\\u001b[23;70H7\\u001b[7;5H\\u001b[?25h\"]\n[50.77406, \"o\", \"\\u001b[?25l\\u001b[23;70H6\\u001b[6;5H\\u001b[?25h\"]\n[50.938923, \"o\", \"\\u001b[?25l\\u001b[1C\\u001b[1m\\u001b[96m\\u001b[48;5;242mapp\\u001b[m\\u001b[48;5;242m.\\u001b[m\\u001b[1m\\u001b[96m\\u001b[48;5;242mroute\\u001b[m\\u001b[48;5;242m(\\u001b[m\\u001b[95m\\u001b[48;5;242m'/'\\u001b[m\\u001b[48;5;242m) \\u001b[m\\u001b[23;1H\\u001b[1m-- VISUAL LINE --\\u001b[m\\u001b[23;18H\\u001b[K\\u001b[23;70H6,1\\u001b[11CTop\\u001b[6;5H\\u001b[?25h\"]\n[51.031843, \"o\", \"\\u001b[?25l\\u001b[38;5;81m\\u001b[48;5;242m@\\u001b[m\\u001b[7;6H\\u001b[93m\\u001b[48;5;242mef\\u001b[m\\u001b[48;5;242m \\u001b[m\\u001b[1m\\u001b[96m\\u001b[48;5;242mindex\\u001b[m\\u001b[48;5;242m(): \\u001b[m\\u001b[23;70H7\\u001b[7;5H\\u001b[?25h\"]\n[51.152844, \"o\", \"\\u001b[?25l\\u001b[93m\\u001b[48;5;242md\\u001b[m\\u001b[8;6H\\u001b[48;5;242m   \\u001b[m\\u001b[93m\\u001b[48;5;242mreturn\\u001b[m\\u001b[48;5;242m {\\u001b[m\\u001b[95m\\u001b[48;5;242m'hello'\\u001b[m\\u001b[48;5;242m: \\u001b[m\\u001b[95m\\u001b[48;5;242m'world'\\u001b[m\\u001b[48;5;242m} \\u001b[m\\u001b[23;70H8\\u001b[8;5H\\u001b[?25h\"]\n[51.462757, \"o\", \"\\u001b[?25l\\u001b[48;5;242m \\u001b[m\\u001b[23;70H9,0-1\\u001b[9;5H\\u001b[?25h\"]\n[51.617787, \"o\", \"\\u001b[?25l\\u001b[6;5H\\u001b[38;5;81m@\\u001b[m\\u001b[1m\\u001b[96mapp\\u001b[m.\\u001b[1m\\u001b[96mroute\\u001b[m(\\u001b[95m'/'\\u001b[m)\\u001b[6;20H\\u001b[K\\u001b[7;5H\\u001b[93mdef\\u001b[m \\u001b[1m\\u001b[96mindex\\u001b[m():\\u001b[7;17H\\u001b[K\\u001b[8;5H    \\u001b[93mreturn\\u001b[m {\\u001b[95m'hello'\\u001b[m: \\u001b[95m'world'\\u001b[m}\\u001b[8;34H\\u001b[K\\u001b[23;1H\\u001b[K\\u001b[23;70H6,1\\u001b[11CTop\\r4 lines yanked\\u001b[23;70H\\u001b[K\"]\n[51.617956, \"o\", \"\\u001b[23;70H6,1\\u001b[11CTop\\u001b[6;5H\\u001b[?25h\"]\n[51.7452, \"o\", \"\\u001b[?25l\\u001b[23;70H7\\u001b[7;5H\\u001b[?25h\"]\n[51.875628, \"o\", \"\\u001b[?25l\\u001b[23;70H8\\u001b[8;5H\\u001b[?25h\"]\n[52.020364, \"o\", \"\\u001b[?25l\\u001b[23;70H9,0-1\\u001b[9;5H\\u001b[?25h\"]\n[52.165269, \"o\", \"\\u001b[?25l\\u001b[23;3Hmore lines\\u001b[23;13H\\u001b[K\"]\n[52.172521, \"o\", \"\\u001b[10;5H\\u001b[38;5;81m@\\u001b[m\\u001b[1m\\u001b[96mapp\\u001b[m.\\u001b[1m\\u001b[96mroute\\u001b[m(\\u001b[95m'/'\\u001b[m)\\u001b[11;5H\\u001b[93mdef\\u001b[m \\u001b[1m\\u001b[96mindex\\u001b[m():\\u001b[11;17H\\u001b[K\\u001b[12;5H    \\u001b[93mreturn\\u001b[m {\\u001b[95m'hello'\\u001b[m: \\u001b[95m'world'\\u001b[m}\\u001b[12;34H\\u001b[K\\u001b[13;5H\\u001b[K\\u001b[14;5H\\u001b[K\\u001b[15;6H\\u001b[96m The view function above will return {\\\"hello\\\": \\\"world\\\"}\\u001b[16;7Hwhenever you make an HTTP GET request to '/'.\\u001b[m\\u001b[17;6H\\u001b[K\\u001b[18;7H\\u001b[96mHere are a few more examples:\\u001b[m\\u001b[18;36H\\u001b[K\\u001b[19;6H\\u001b[K\\u001b[20;6H\\u001b[96m @app.route('/hello/{name}')\\u001b[21;7Hdef hello_name(name):\\u001b[m\\u001b[21;28H\\u001b[K\\u001b[22;7H\\u001b[96m   # '/hello/james' -> {\\\"hello\\\": \\\"james\\\"}\\u001b[m\\u001b[23;70H10,1\\u001b[10CTop\\u001b[23;70H\\u001b[K\\u001b[23;70H10,1\\u001b[10CTop\\u001b[10;5H\\u001b[?25h\"]\n[52.884211, \"o\", \"\\u001b[?25l\\u001b[23;73H2\\u001b[10;6H\\u001b[?25h\"]\n[53.145411, \"o\", \"\\u001b[?25l\\u001b[9C\\u001b[46m(\\u001b[3C)\\u001b[m\\u001b[23;73H15\\u001b[10;19H\\u001b[?25h\"]\n[53.281847, \"o\", \"\\u001b[?25l\\b\\b\\b\\b(\\u001b[3C)\\u001b[23;74H4\\u001b[10;18H\\u001b[?25h\"]\n[53.396746, \"o\", \"\\u001b[?25l\\u001b[23;1H\\u001b[1m-- INSERT --\\u001b[m\\u001b[23;70H\\u001b[K\\u001b[23;70H10,14\\u001b[9CTop\"]\n[53.396918, \"o\", \"\\u001b[10;18H\\u001b[?25h\"]\n[53.560679, \"o\", \"\\u001b[?25l\\u001b[95mh'\\u001b[m)\\u001b[23;74H5\\u001b[10;19H\\u001b[?25h\"]\n[53.658424, \"o\", \"\\u001b[?25l\\u001b[95me'\\u001b[m)\\u001b[23;74H6\\u001b[10;20H\\u001b[?25h\"]\n[53.754007, \"o\", \"\\u001b[?25l\\u001b[95ml'\\u001b[m)\\u001b[23;74H7\\u001b[10;21H\\u001b[?25h\"]\n[53.843242, \"o\", \"\\u001b[?25l\\u001b[95ml'\\u001b[m)\\u001b[23;74H8\\u001b[10;22H\\u001b[?25h\"]\n[54.264358, \"o\", \"\\u001b[?25l\\u001b[95mo'\\u001b[m)\\u001b[23;74H9\\u001b[10;23H\\u001b[?25h\"]\n[54.570344, \"o\", \"\\u001b[?25l\\u001b[95m/'\\u001b[m)\\u001b[23;73H20\\u001b[10;24H\\u001b[?25h\"]\n[54.885885, \"o\", \"\\u001b[?25l\\u001b[95m{'\\u001b[m)\\u001b[23;74H1\\u001b[10;25H\\u001b[?25h\"]\n[55.06869, \"o\", \"\\u001b[?25l\\u001b[95mn'\\u001b[m)\\u001b[23;74H2\\u001b[10;26H\\u001b[?25h\"]\n[55.147467, \"o\", \"\\u001b[?25l\\u001b[95ma'\\u001b[m)\\u001b[23;74H3\\u001b[10;27H\\u001b[?25h\"]\n[55.224402, \"o\", \"\\u001b[?25l\\u001b[95mm'\\u001b[m)\\u001b[23;74H4\\u001b[10;28H\\u001b[?25h\"]\n[55.310708, \"o\", \"\\u001b[?25l\\u001b[95me'\\u001b[m)\\u001b[23;74H5\\u001b[10;29H\\u001b[?25h\"]\n[55.47705, \"o\", \"\\u001b[?25l\\b\\b\\b\\b\\b\\u001b[38;5;224m{name}\\u001b[m\\u001b[95m'\\u001b[m)\\u001b[23;74H6\\u001b[10;30H\\u001b[?25h\"]\n[55.681933, \"o\", \"\\u001b[23;1H\\u001b[K\\u001b[10;29H\"]\n[55.74385, \"o\", \"\\u001b[?25l\"]\n[55.74576, \"o\", \"\\u001b[23;70H10,25\\u001b[9CTop\\u001b[10;29H\\u001b[?25h\\u001b[?25l\\u001b[23;71H1,12\\u001b[11;16H\\u001b[?25h\"]\n[55.818802, \"o\", \"\\u001b[?25l\\u001b[23;74H \\u001b[11;5H\\u001b[?25h\"]\n[55.895759, \"o\", \"\\u001b[?25l\\u001b[23;73H5\\u001b[11;9H\\u001b[?25h\"]\n[56.207749, \"o\", \"\\u001b[?25l\\u001b[23;1H\\u001b[1m-- INSERT --\\u001b[m\\u001b[23;70H\\u001b[K\\u001b[23;70H11,5\\u001b[10CTop\"]\n[56.211248, \"o\", \"\\u001b[11;9H():\\u001b[11;12H\\u001b[K\\u001b[11;9H\\u001b[46m()\\b\\b\\u001b[?25h\"]\n[56.309676, \"o\", \"\\u001b[?25l\\u001b[m\\u001b[1m\\u001b[96m\\u001b[46mh\\u001b[m\\u001b[46m(\\u001b[m):\\b\\b\\b\\b\\u001b[1m\\u001b[96mh\\u001b[m\\u001b[46m()\\u001b[m\\u001b[23;73H6\\u001b[11;10H\\u001b[?25h\"]\n[56.422079, \"o\", \"\\u001b[?25l\\u001b[1m\\u001b[96m\\u001b[46me\\u001b[m\\u001b[46m(\\u001b[m):\\b\\b\\b\\b\\u001b[1m\\u001b[96me\\u001b[m\\u001b[46m()\\u001b[m\\u001b[23;73H7\\u001b[11;11H\\u001b[?25h\"]\n[56.502157, \"o\", \"\\u001b[?25l\\u001b[1m\\u001b[96m\\u001b[46ml\\u001b[m\\u001b[46m(\\u001b[m):\\b\\b\\b\\b\\u001b[1m\\u001b[96ml\\u001b[m\\u001b[46m()\\u001b[m\\u001b[23;73H8\\u001b[11;12H\\u001b[?25h\"]\n[56.599656, \"o\", \"\\u001b[?25l\\u001b[1m\\u001b[96m\\u001b[46ml\\u001b[m\\u001b[46m(\\u001b[m):\\b\\b\\b\\b\\u001b[1m\\u001b[96ml\\u001b[m\\u001b[46m()\\u001b[m\\u001b[23;73H9\\u001b[11;13H\\u001b[?25h\"]\n[56.764724, \"o\", \"\\u001b[?25l\\u001b[1m\\u001b[96m\\u001b[46mo\\u001b[m\\u001b[46m(\\u001b[m):\\b\\b\\b\\b\\u001b[1m\\u001b[96mo\\u001b[m\\u001b[46m()\\u001b[m\\u001b[23;73H10\\u001b[11;14H\\u001b[?25h\"]\n[56.941756, \"o\", \"\\u001b[23;1H\\u001b[K\\u001b[11;13H\"]\n[57.01945, \"o\", \"\\u001b[?25l\"]\n[57.022469, \"o\", \"\\u001b[1C()\\u001b[23;70H11,9\\u001b[10CTop\\u001b[11;13H\\u001b[?25h\\u001b[?25l\\u001b[1C\\u001b[46m()\\u001b[m\\u001b[23;73H10\\u001b[11;14H\\u001b[?25h\"]\n[57.136892, \"o\", \"\\u001b[?25l\\u001b[23;74H1\\u001b[11;15H\\u001b[?25h\"]\n[57.532494, \"o\", \"\\u001b[?25l\\u001b[23;1H\\u001b[1m-- INSERT --\\u001b[m\\u001b[23;70H\\u001b[K\\u001b[23;70H11,11\\u001b[9CTop\"]\n[57.532692, \"o\", \"\\u001b[11;15H\\u001b[?25h\"]\n[57.753309, \"o\", \"\\u001b[?25l\\u001b[46mn\\u001b[m):\\b\\b\\bn\\u001b[46m)\\u001b[m\\u001b[23;74H2\\u001b[11;16H\\u001b[?25h\"]\n[57.857846, \"o\", \"\\u001b[?25l\\u001b[46ma\\u001b[m):\\b\\b\\ba\\u001b[46m)\\u001b[m\\u001b[23;74H3\\u001b[11;17H\\u001b[?25h\"]\n[58.364461, \"o\", \"\\u001b[?25l\\u001b[46mm\\u001b[m):\\b\\b\\bm\\u001b[46m)\\u001b[m\\u001b[23;74H4\\u001b[11;18H\\u001b[?25h\"]\n[58.474158, \"o\", \"\\u001b[?25l\\u001b[46me\\u001b[m):\\b\\b\\be\\u001b[46m)\\u001b[m\\u001b[23;74H5\\u001b[11;19H\\u001b[?25h\"]\n[58.641462, \"o\", \"\\u001b[23;1H\\u001b[K\\u001b[11;18H\"]\n[58.743445, \"o\", \"\\u001b[?25l\"]\n[58.745529, \"o\", \"\\b\\b\\b\\b(name)\\u001b[23;70H11,14\\u001b[9CTop\\u001b[11;18H\\u001b[?25h\\u001b[?25l\\u001b[23;71H2\\u001b[12;18H\\u001b[?25h\"]\n[58.873794, \"o\", \"\\u001b[?25l\\u001b[23;74H9\\u001b[12;23H\\u001b[?25h\"]\n[59.019762, \"o\", \"\\u001b[?25l\\u001b[23;73H22\\u001b[12;26H\\u001b[?25h\"]\n[59.129529, \"o\", \"\\u001b[?25l\\u001b[23;74H3\\u001b[12;27H\\u001b[?25h\"]\n[59.612607, \"o\", \"\\u001b[?25l\\u001b[23;74H2\\u001b[12;26H\\u001b[?25h\"]\n[60.092337, \"o\", \"\\u001b[?25l\\u001b[23;1H\\u001b[1m-- INSERT --\\u001b[m\\u001b[23;70H\\u001b[K\\u001b[23;70H12,22\\u001b[9CTop\"]\n[60.096577, \"o\", \"\\u001b[12;26H}\\u001b[12;27H\\u001b[K\\u001b[12;16H\\u001b[46m{\\u001b[9C}\\b\\u001b[?25h\"]\n[60.363434, \"o\", \"\\u001b[?25ln\\u001b[m}\\b\\bn\\u001b[46m}\\u001b[m\\u001b[23;74H3\\u001b[12;27H\\u001b[?25h\"]\n[60.449639, \"o\", \"\\u001b[?25l\\u001b[46ma\\u001b[m}\\b\\ba\\u001b[46m}\\u001b[m\\u001b[23;74H4\\u001b[12;28H\\u001b[?25h\"]\n[60.522062, \"o\", \"\\u001b[?25l\\u001b[46mm\\u001b[m}\\b\\bm\\u001b[46m}\\u001b[m\\u001b[23;74H5\\u001b[12;29H\\u001b[?25h\"]\n[60.617924, \"o\", \"\\u001b[?25l\\u001b[46me\\u001b[m}\\b\\be\\u001b[46m}\\u001b[m\\u001b[23;74H6\\u001b[12;30H\\u001b[?25h\"]\n[60.748263, \"o\", \"\\u001b[23;1H\\u001b[K\\u001b[12;29H\"]\n[60.830157, \"o\", \"\\u001b[?25l\"]\n[60.831801, \"o\", \"\\u001b[12;16H{\\u001b[13C}\\u001b[23;70H12,25\\u001b[9CTop\\u001b[12;29H\\u001b[?25h\\u001b[?25l\\u001b[23;71H3,0-1\\u001b[13;5H\\u001b[?25h\"]\n[61.137592, \"o\", \"\\u001b[?25l\\u001b[23;1H21 fewer lines\\u001b[23;70H\\u001b[K\"]\n[61.138012, \"o\", \"\\u001b[13;1H\\u001b[94m~                                                                                      \\u001b[14;1H~                                                                                      \\u001b[15;1H~                                                                                      \\u001b[16;1H~                                                                                      \\u001b[17;1H~                                                                                      \\u001b[18;1H~                                                                                      \\u001b[19;1H~                                                                                      \\u001b[20;1H~                                                                                      \\u001b[21;1H~                                                                                      \\u001b[22;1H~                                                                                      \\u001b[m\\u001b[23;70H12,5\\u001b[10CAll\\u001b[23;70H\\u001b[K\"]\n[61.138212, \"o\", \"\\u001b[23;70H12,5\\u001b[10CAll\\u001b[12;9H\\u001b[?25h\"]\n[61.620498, \"o\", \"\\u0007\\u001b[?25l\\u001b[?25h\\u001b[?25l\\u001b[23;71H1\\u001b[11;9H\\u001b[?25h\"]\n[61.761612, \"o\", \"\\u001b[?25l\\u001b[23;71H0\\u001b[10;9H\\u001b[?25h\"]\n[61.889628, \"o\", \"\\u001b[?25l\\u001b[23;70H9,0-1\\u001b[9;5H\\u001b[?25h\"]\n[62.018677, \"o\", \"\\u001b[?25l\\u001b[23;1H\\u001b[1m-- INSERT --\\u001b[m\\u001b[23;13H\\u001b[K\\u001b[23;70H10,1\\u001b[10CTop\"]\n[62.021847, \"o\", \"\\u001b[10;5H\\u001b[K\\u001b[11;5H\\u001b[38;5;81m@\\u001b[m\\u001b[1m\\u001b[96mapp\\u001b[m.\\u001b[1m\\u001b[96mroute\\u001b[m(\\u001b[95m'/hello/\\u001b[m\\u001b[38;5;224m{name}\\u001b[m\\u001b[95m'\\u001b[m)\\u001b[12;5H\\u001b[93mdef\\u001b[m \\u001b[1m\\u001b[96mhello\\u001b[m(name):\\u001b[12;21H\\u001b[K\\u001b[13;1H\\u001b[93m 13 \\u001b[m    \\u001b[93mreturn\\u001b[m {\\u001b[95m'hello'\\u001b[m: name}\\u001b[13;31H\\u001b[K\\u001b[10;5H\\u001b[?25h\"]\n[62.315932, \"o\", \"\\u001b[23;1H\\u001b[K\\u001b[10;5H\"]\n[63.317825, \"o\", \"\\u001b[?25l\"]\n[63.318144, \"o\", \"\\u001b[23;70H10,0-1\\u001b[8CAll\\u001b[10;5H\\u001b[?25h\"]\n[65.342036, \"o\", \"\\u001b[?25l\\u001b[23;70H\\u001b[K\\u001b[23;1H:\\u001b[?2004h\"]\n[65.342284, \"o\", \"\\u001b[?25h\"]\n[65.436725, \"o\", \"w\\u001b[?25l\\u001b[?25h\"]\n[65.498001, \"o\", \"q\\u001b[?25l\\u001b[?25h\"]\n[65.789977, \"o\", \"\\r\"]\n[65.790186, \"o\", \"\\u001b[?25l\\u001b[?1006l\\u001b[?1002l\\u001b[?2004l\"]\n[65.790669, \"o\", \"\\\"app.py\\\"\"]\n[65.793193, \"o\", \" 13L, 201C written\"]\n[65.902159, \"o\", \"\\r\\r\\r\\n\\u001b[?2004l\\u001b[?1l\\u001b>\\u001b[?25h\\u001b[?1049l\"]\n[65.91921, \"o\", \"(venv37) /tmp/quickstart $ \"]\n[66.222652, \"o\", \"c\"]\n[66.318574, \"o\", \"h\"]\n[66.39079, \"o\", \"a\"]\n[66.463041, \"o\", \"l\"]\n[66.515746, \"o\", \"i\"]\n[66.578604, \"o\", \"c\"]\n[66.667005, \"o\", \"e\"]\n[66.702913, \"o\", \" \"]\n[66.818854, \"o\", \"d\"]\n[66.957969, \"o\", \"e\"]\n[67.025938, \"o\", \"p\"]\n[67.089679, \"o\", \"l\"]\n[67.242964, \"o\", \"o\"]\n[67.36255, \"o\", \"y\"]\n[67.764503, \"o\", \"\\r\\n\"]\n[68.096635, \"o\", \"Creating deployment package.\\r\\n\"]\n[69.001699, \"o\", \"Updating policy for IAM role: quickstart-dev\\r\\n\"]\n[69.076994, \"o\", \"Updating lambda function: quickstart-dev\\r\\n\"]\n[69.718098, \"o\", \"Updating rest API\\r\\n\"]\n[70.732661, \"o\", \"Resources deployed:\\r\\n  - Lambda ARN: arn:aws:lambda:us-west-2:675687397822:function:quickstart-dev\\r\\n  - Rest API URL: https://vdpmshluug.execute-api.us-west-2.amazonaws.com/api/\\r\\n\"]\n[70.801669, \"o\", \"(venv37) /tmp/quickstart $ \"]\n[75.836485, \"o\", \"h\"]\n[75.916746, \"o\", \"t\"]\n[76.050503, \"o\", \"t\"]\n[76.126675, \"o\", \"p\"]\n[76.19904, \"o\", \" \"]\n[76.388309, \"o\", \"htt\"]\n[76.388474, \"o\", \"ps://vdpmshluug\"]\n[76.388533, \"o\", \".execu\"]\n[76.388571, \"o\", \"te\"]\n[76.388605, \"o\", \"-api\"]\n[76.388636, \"o\", \".us\"]\n[76.388685, \"o\", \"-wes\"]\n[76.388934, \"o\", \"t-2.amazonaws.com\"]\n[76.389032, \"o\", \"/ \\rapi/\"]\n[77.149797, \"o\", \"h\"]\n[77.302346, \"o\", \"e\"]\n[77.402471, \"o\", \"l\"]\n[77.520633, \"o\", \"l\"]\n[77.717694, \"o\", \"o\"]\n[78.514487, \"o\", \"/\"]\n[78.87048, \"o\", \"j\"]\n[79.002202, \"o\", \"a\"]\n[79.058052, \"o\", \"m\"]\n[79.158039, \"o\", \"e\"]\n[79.193908, \"o\", \"s\"]\n[79.899556, \"o\", \"\\r\\n\"]\n[80.371799, \"o\", \"\\u001b[34mHTTP\\u001b[39;49;00m/\\u001b[34m1.1\\u001b[39;49;00m \\u001b[34m200\\u001b[39;49;00m \\u001b[36mOK\\u001b[39;49;00m\\r\\n\\u001b[36mConnection\\u001b[39;49;00m: keep-alive\\r\\n\\u001b[36mContent-Length\\u001b[39;49;00m: 17\\r\\n\\u001b[36mContent-Type\\u001b[39;49;00m: application/json\\r\\n\\u001b[36mDate\\u001b[39;49;00m: Tue, 30 Jun 2020 15:35:57 GMT\\r\\n\\u001b[36mVia\\u001b[39;49;00m: 1.1 f78e2a2d083c0945ee670c9d5d179e9e.cloudfront.net (CloudFront)\\r\\n\\u001b[36mX-Amz-Cf-Id\\u001b[39;49;00m: QBy4a8cMibLHz6UuJ3AGj3U68AhGC-nKETLPft_GOL1bko7N4rxeOg==\\r\\n\\u001b[36mX-Amz-Cf-Pop\\u001b[39;49;00m: EWR53-C1\\r\\n\\u001b[36mX-Amzn-Trace-Id\\u001b[39;49;00m: Root=1-5efb5bdd-2a649157a49e65e720b9f244;Sampled=0\\r\\n\\u001b[36mX-Cache\\u001b[39;49;00m: Miss from cloudfront\\r\\n\\u001b[36mx-amz-apigw-id\\u001b[39;49;00m: O8tKoH5QvHcF3ng=\\r\\n\\u001b[36mx-amzn-RequestId\\u001b[39;49;00m: 167d0760-64e3-4447-80de-01be0e8853e3\\r\\r\\n\\r\\r\\n\"]\n[80.373638, \"o\", \"{\\r\\n    \\u001b[34;01m\\\"hello\\\"\\u001b[39;49;00m: \\u001b[33m\\\"james\\\"\\u001b[39;49;00m\\r\\n}\\r\\n\\r\\n\"]\n[80.420141, \"o\", \"(venv37) /tmp/quickstart $ \"]\n[94.075245, \"o\", \"c\"]\n[94.142242, \"o\", \"h\"]\n[94.235231, \"o\", \"a\"]\n[94.303331, \"o\", \"l\"]\n[94.343169, \"o\", \"i\"]\n[94.430906, \"o\", \"c\"]\n[94.475222, \"o\", \"e\"]\n[94.547124, \"o\", \" \"]\n[94.682051, \"o\", \"d\"]\n[94.846182, \"o\", \"e\"]\n[94.986099, \"o\", \"l\"]\n[95.026072, \"o\", \"e\"]\n[95.162597, \"o\", \"t\"]\n[95.266324, \"o\", \"e\"]\n[95.628994, \"o\", \"\\r\\n\"]\n[96.026774, \"o\", \"Deleting Rest API: vdpmshluug\\r\\n\"]\n[96.802902, \"o\", \"Deleting function: arn:aws:lambda:us-west-2:675687397822:function:quickstart-dev\\r\\n\"]\n[97.219203, \"o\", \"Deleting IAM role: quickstart-dev\\r\\n\"]\n[98.090105, \"o\", \"(venv37) /tmp/quickstart $ \"]\n[101.249136, \"o\", \"exit\\r\\n\"]\n"
  },
  {
    "path": "docs/source/theme/smithy/static/custom-tabs.css",
    "content": "/**\n * Add overrides to how tabs are styled to make them less visually\n * obstrusive. Note, however, that this might need to be looked at in\n * the future if we ever want to use non-code-tabs.\n */\n\n /* No need for margin below code samples when in a tab. */\n.code-tab pre {\n    margin-bottom: 0;\n}\n\n.sphinx-tabs {\n    margin-top: 1rem;\n}\n\n/* Code tabs should encompass the entire tab. */\n.code-tab.tab {\n    padding: 0 !important;\n}\n\n.ui.tabular.menu {\n    border: none;\n}\n\n.ui.tabular.menu .item  {\n    font-size: 0.8em;\n    font-family: \"SFMono-Regular\",Consolas,\"Liberation Mono\",Menlo,Courier,monospace;\n    text-transform: lowercase;\n    border: none !important;\n}\n\n.ui.attached.segment.code-tab {\n    border: none !important;\n}\n\n/* Remove code tab headings and use the same color as code backgrounds */\n.ui.tabular.menu .active.item {\n    border: none !important;\n    background-color: inherit;\n    text-decoration: underline;\n    font-weight: normal;\n}\n"
  },
  {
    "path": "docs/source/theme/smithy/static/default.css_t",
    "content": "/* ----- Layout ------ */\n\nhtml {\n  font-family: {{ theme_regular_font }};\n  background-color: {{ theme_site_background }};\n}\n\n/* Base scaffolding taken from Markswatch theme */\nbody {\n  color: #24292e;\n  font-family: {{ theme_regular_font }};\n  font-size: 16px;\n  line-height: 1.5;\n  -ms-text-size-adjust: 100%;\n  -webkit-text-size-adjust: 100%;\n  word-wrap: break-word;\n  display: flex;\n  min-height: 100vh;\n  flex-direction: column;\n  background-color: {{ theme_site_background }};\n}\n\n/* Utility class used in things like hidden form fields */\n.hidden {\n  display: none;\n}\n\n#site-container {\n  color: #24292e;\n}\n\nblockquote {\n  padding: 0 0 0 1.5em;\n  margin: 0 2rem 1rem 0;\n  color: #777;\n  border-left: 0.5rem solid #eee;\n  font-family: {{ theme_code_font }};\n  font-size: 1em;\n}\n\n.width-wrapper  {\n  max-width: 1140px;\n  margin: auto;\n  position: relative;\n  padding: 0 1em;\n}\n\n/* ----- Headings ------ */\n\nh1, h2, h3, h4, h5, h6 {\n  color: #000;\n  line-height: 1.25;\n  margin-bottom: 1em;\n  font-weight: 600;\n}\n\nh1 a, h2 a, h3 a, h4 a, h5 a, h6 a, h7 a {\n  color: #000;\n}\n\nh1, h2, h3, h4, h5, h6, h7 {\n  border-bottom: 1px solid #ccc;\n  padding-bottom: 0.3em;\n}\n\nh2, h3, h4, h5, h6, h7 {\n  margin-top: 1.25em;\n}\n\nh1 {\n  margin-top: 0;\n  font-size: 2.4em;\n}\n\nh2 {\n  font-size: 2em;\n}\n\nh3 {\n  font-size: 1.8em;\n}\n\nh4 {\n  font-size: 1.6em;\n  font-weight: normal;\n}\n\nh5 {\n  font-size: 1.3em;\n  font-weight: normal;\n}\n\nh6 {\n  font-size: 1em;\n  font-weight: bold;\n}\n\nh7 {\n  font-size: 0.85em;\n  font-weight: bold;\n}\n\n\n/* ----- Landing page ------ */\n\n#splash {\n  padding: 3em 0 2em 0;\n  color: #fff;\n  margin-bottom: 1em;\n  background-color: {{ theme_dark_primary }};\n  /*\n   * Generated from https://www.heropatterns.com/, by Steve Schoger\n   * https://creativecommons.org/licenses/by/4.0/\n   */\n\n  background-color: #19222e;\n  background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Cg fill-rule='evenodd'%3E%3Cg fill='%23535060' fill-opacity='0.25'%3E%3Cpath opacity='.5' d='M96 95h4v1h-4v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9zm-1 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9z'/%3E%3Cpath d='M6 5V0H5v5H0v1h5v94h1V6h94V5H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E\");\n}\n\n#splash h1 {\n  color: #fff;\n  padding: 0;\n  margin: 0 0 1rem;\n  font-size: 1.7em;\n  border: none;\n  font-weight: bold;\n}\n\n.splash-highlight {\n  color: #FFF896;\n}\n\n#splash .highlight-chalice {\n  color: #232f3E;\n  background-color: #232f3e;\n}\n\n#splash .highlight-chalice .highlight {\n  color: #232f3E;\n  background-color: #232f3e;\n}\n\n#splash .highlight-chalice pre {\n  font-size: 0.9em;\n  background-color: #232f3e;\n  border-radius: 3px;\n  font-size: 1em;\n  color: #A9B7C6;\n  margin-bottom: 4px;\n  margin-top: 0;\n  padding-top: 0;\n}\n\n#splash .splash-column:first-child {\n  padding-right: 3%;\n  text-align: center;\n}\n\n#splash-logo {\n  max-width: 100%;\n}\n\n.lp-image {\n  width: 8%;\n  height: 8%;\n  padding-top: 32px;\n}\n\n.headline {\n  font-weight: 400;\n  color: #cfcedf;\n  font-size: 2.3em;\n  text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.3);\n}\n\n@media (max-width: 600px) {\n  #splash h1 {\n    font-size: 1.5em;\n  }\n  #splash .headline {\n    font-size: 18px;\n  }\n}\n\n.splash-link {\n  display: inline-block;\n  border-radius: 6px;\n  padding: 12px;\n  text-align: center;\n  margin: 2em auto 1em auto;\n  font-weight: 500;\n  width: 160px;\n  color: #fff;\n  background: {{ theme_medium_accent }};\n  text-transform: uppercase;\n}\n\n.splash-link:hover {\n  text-decoration: none;\n  color: {{ theme_medium_accent  }};\n  background: #fff;\n}\n\n.splash-link-small {\n  display: block;\n  text-decoration: underline;\n  color: #fff;\n  margin-bottom: 2em;\n}\n\n.splash-link-small:hover {\n  color: #fff;\n}\n\n.splash-row {\n  display: flex;\n}\n\n.splash-column {\n  display: flex;\n  flex-direction: column;\n  justify-content: top;\n  width: 50%;\n  padding: 0 20px;\n  margin-top: 2em;\n}\n\n.splash-box {\n  border: 1px solid #859191;\n  padding: 20px;\n  margin: 25px;\n}\n\n.splash-clickable {\n  text-decoration: none;\n}\n\n#splash .splash-column {\n  margin-top: 0;\n}\n\n.splash-column-one-third {\n  display: flex;\n  flex-direction: column;\n  justify-content: top;\n  width: 40%;\n  padding: 0 20px;\n  margin-top: 2em;\n}\n\n.splash-column-two-third {\n  display: flex;\n  flex-direction: column;\n  justify-content: top;\n  width: 60%;\n  padding: 0 20px;\n  margin-top: 2em;\n}\n\n.splash-column h2,\n.faq h2 {\n  color: {{ theme_medium_primary }};\n  padding: 0;\n  font-size: 1.25em;\n  text-align: left;\n  border: none;\n}\n\n.quickstart-vid {\n  background: #2BBA9C1A;\n  padding-bottom: 64px;\n}\n\n#quickstart-img {\n  width: 15%;\n  padding-top: 20px;\n  padding-right: 24px;\n  float: left;\n}\n\n#quickstart-title {\n  position: relative;\n  padding-top: 24px;\n}\n\n.faq .faq-heading {\n  text-align: center;\n  color: {{ theme_medium_primary }};\n  margin: 2rem 0;\n  font-size: 2em;\n}\n\n.splash-column h2 {\n  margin: 0 0 1rem 0;\n}\n\n.splash-column-one-third h2 {\n  margin: 0 0 1rem 0;\n  font-size: 1.50em;\n}\n\n#splash pre {\n  position: relative;\n}\n\n.see-full-example {\n  display: block;\n  position: absolute;\n  bottom: 0.5em;\n  right: 0.5em;\n  font-size: 12px;\n  color: #999;\n}\n\n.feature-desc {\n  font-size: 14px;\n}\n\n/* ----- Visual separations ------ */\n\n#page-container {\n  padding: 3rem 2rem 2rem 2em;\n  background: {{ theme_site_background }};\n  flex: 1;\n}\n\n/* Make document and right nav left and right aligned columns. */\n#page-container > .width-wrapper {\n  display: flex;\n  flex-flow: row;\n}\n\n#landing-container {\n  min-height: 500px;\n  background: {{ theme_site_background }};\n}\n\n#page-container li > p.first:last-child,\n#landing-container li > p.first:last-child {\n  margin-bottom: 0;\n}\n\ndt {\n  margin-bottom: 0.5em;\n}\n\ndd {\n  margin-left: 2em;\n}\n\ndd > ul {\n  padding-left: 0;\n  list-style-position: inside;\n}\n\nhr {\n  height: 0.25em;\n  padding: 0;\n  margin: 24px 0;\n  background-color: #e1e4e8;\n  border: 0;\n}\n\n\n/* ----- Anchors ------ */\n\na {\n  color: {{ theme_link_color }};\n  text-decoration: none;\n}\n\na:hover, .reference.external:hover {\n  text-decoration: underline;\n  color: {{ theme_link_color }};\n}\n\n.reference.external {\n  text-decoration: underline dotted #ccc;\n}\n\n.headerlink {\n  visibility: none;\n  margin-left: 1em;\n  font-size: 18px;\n  vertical-align: super;\n  line-height: 0;\n  opacity: 0;\n  transition: opacity .25s;\n}\n\n*:hover > .headerlink {\n  opacity: 1;\n}\n\n\n/* ----- Header and footer ------ */\n\nheader {\n  display: block;\n  width: 100%;\n  flex: none;\n  box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.2);\n  z-index: 1;\n}\n\n.header-flex {\n  display: flex;\n  justify-content: space-between;\n}\n\n.site-logo {\n  position: relative;\n  margin-top: 6px;\n}\n\n.site-logo a {\n  border-radius: 6px;\n  padding: 3px 6px;\n  color: {{ theme_medium_primary }};\n  font-weight: 500;\n  font-size: 22px;\n  display: block;\n}\n\n.site-logo a:hover {\n  text-decoration: none;\n}\n\n.site-logo .logo-text {\n  font-weight: 700;\n  padding-left: 6px;\n}\n\n.site-logo .version-text {\n  font-weight: 300;\n  font-size: 12px;\n  position: absolute;\n  right: -14px;\n  top: 4px;\n  color: {{ theme_medium_primary }};\n}\n\n.site-logo .logo-icon {\n  color: transparent;\n  text-shadow: 0 0 0 #fff;\n  font-size: 18px;\n  vertical-align: text-bottom;\n  padding: 2px 3px 2px 4px;\n  border-radius: 6px;\n}\n\n.logo-icon img {\n  height: 50%;\n  width: 50%;\n}\n\n#page-navigation {\n  padding: 10px 1em;\n  display: flex;\n  list-style: none;\n  margin-bottom: 0;\n}\n\n#page-navigation li {\n  cursor: pointer;\n}\n\n#page-navigation .site-page {\n  line-height: 1.8;\n  margin-right: 0 18px 0 0;\n}\n\n#page-navigation .site-page a {\n  display: block;\n  padding: 0 1em;\n  color: #444;\n}\n\n#page-navigation .site-page a:hover {\n  color: {{ theme_link_color }};\n  text-decoration: none;\n}\n\n#page-navigation .site-search {\n  margin-top: 3px;\n  margin-left: 2em;\n}\n\n#page-navigation .site-search .search-input {\n  padding: 0 1em;\n  color: #111;\n  border: solid 1px #999;\n  border-radius: 3px;\n  width: 180px;\n  font-size: 0.9em;\n  border-radius: 10px;\n}\n\n#page-navigation .site-search button {\n  border: none;\n  padding: 5px 10px;\n  background-color: {{ theme_site_background }};\n  color: #777;\n}\n\nfooter {\n  padding: 2em;\n  background-color: {{ theme_dark_primary }};\n  color: #fefefe;\n}\n\n.copyright {\n  text-align: center;\n  width: 100%;\n}\n\n.faq {\n  background: #ebe9e7;\n  padding: 2em 0 4em;\n  margin: 4em 0 0 0;\n}\n\n.faq h2 {\n  color: {{ theme_medium_primary }};\n  border-bottom: none;\n}\n\nimg.align-center, .figure.align-center, object.align-center {\n  display: block;\n  margin-left: auto;\n  margin-right: auto;\n}\n\n/* ----- Tables ------ */\n\ntable {\n  border-spacing: 0;\n  border-collapse: collapse;\n  border-width: 1px;\n  border-color: #ccc;\n  border-style: solid;\n  width: 100%;\n  margin: 0 0 1rem 0;\n}\n\ntr {\n  border-bottom: 1px solid #ccc;\n}\n\ntr.row-even {\n  background-color: #f7f7f7;\n}\n\nth {\n  text-align: left;\n  padding: 1.2rem 1.6em;\n  vertical-align: top;\n}\n\ntd, th {\n  padding: 8px 16px;\n  border: none;\n  border-right: 1px solid #ccc;\n}\n\n/* Field list tables */\n\ntable.field-list {\n  margin: 2em 0;\n}\n\ntable.field-list th {\n  background-color: {{ theme_site_background }};\n  color: #24292e;\n  border-right: 1px solid #e8e8e8;\n  min-width: 0;\n}\n\n/* hlist tables */\n\ntable.hlist {\n  border: none;\n}\n\n.hlist > tbody > tr {\n  border-bottom: none;\n}\n\n.hlist > tbody > tr > td {\n  border-right: 1px solid #ccc;\n  vertical-align: top;\n  padding-right: 12px;\n}\n\n.hlist > tbody > tr > td > ul {\n  list-style-type: none;\n  padding: 0;\n  margin: 0;\n}\n\n.hlist > tbody > tr > td:last-child {\n  border-right: none;\n}\n\n/* ----- Code and pre ------ */\n\ncode {\n  padding: 0.2em 0.4em;\n  margin: 0;\n  font-size: 85%;\n  background-color: rgba(27, 31, 35, 0.05);\n  border-radius: 3px;\n}\n\npre {\n  padding: 1em;\n  overflow: auto;\n  line-height: 1.45;\n  background-color: #f1f1f1;\n  border-radius: 3px;\n  font-size: 1em;\n  color: #6e6b5e;\n  overflow-x: auto\n  white-space: pre;\n  word-break: normal;\n  word-wrap: normal;\n/*\n  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.4);\n*/\n}\n\ncode, pre {\n  font-family: {{ theme_code_font }};\n}\n\npre a {\n  color: #A5C25C;\n  text-decoration: underline;\n}\n\npre a:hover {\n  color: #A5C25C;\n}\n\n/* Hacky but fixes links inside of grammars */\npre a code.xref {\n  font-size: 100%;\n}\n\n\n/* ----- Tables with line numbers (..code directives) ------ */\n\ntable.highlighttable {\n  margin: 0.5em 0 2em 0;\n  width: 100%;\n  overflow: auto;\n  display: block;\n  border: none;\n  background-color: #f1f1f1;\n}\n\n.highlighttable pre {\n  box-shadow: none;\n}\n\ntable.highlighttable td {\n  border: none;\n  padding: 0;\n}\n\ntable.highlighttable td.linenos {\n  border-right: 1px solid #ccc;\n  width: 20px; /* This will enlarge if needed. */\n  text-align: right;\n}\n\ntable.highlighttable tr {\n  border-bottom: none;\n}\n\ntable.highlighttable td.linenos pre {\n  padding-left: 0.8em;\n  color: #999;\n}\n\ntable.highlighttable pre {\n  margin: 0;\n}\n\n.code-block-caption .caption-text {\n  font-style: italic;\n  color: #666;\n}\n\n.code-block-caption:hover .headerlink {\n  visibility: visible;\n  opacity: 100;\n}\n\n\n/* ------- Misc. -------- */\n\n/* RFC directive styling */\n.rfc strong {\n  font-weight: normal;\n}\n\n/* Table of contents \"title\" */\n\n.contents {\n  padding: 1em 2em;\n  background-color: #eee;\n}\n\n.contents ul {\n  list-style-type: none;\n  margin: 0;\n  padding: 0;\n}\n\n.contents li {\n  text-indent: -1.5em;\n  padding-left: 1.5em;\n}\n\n.contents li::before {\n  content: \"•\";\n  color: rgba(0, 0, 0, 0.5);\n  padding-right: 0.5em;\n}\n\n.topic-title {\n  font-weight: 300;\n  color: rgba(0, 0, 0, 0.6);\n}\n\n/* -------- Document ---------- */\n/* TODO: These columns styles are convoluted. */\n\n.side-column {\n  min-height: 100px;\n  position: static;\n  flex: 0 0 300px;\n  top: 0;\n}\n\n.column-body {\n  position: sticky;\n  top: 30px;\n  overflow: auto;\n}\n\n#document-body {\n  padding: 0 3.5rem 0 0;\n  min-height: 300px;\n  flex: auto;\n  overflow: hidden;\n}\n\n#right-column {\n  font-size: 14px;\n  padding-top: 1em;\n  border-left: 1px solid rgba(0, 0, 0, 0.15);\n}\n\n#right-column > .column-body > .sidebar {\n  padding-left: 1em;\n}\n\n#right-column > .column-body > .sidebar > ul > li > a {\n  font-weight: bold;\n  color: {{ theme_primary }};\n}\n\n#right-column > .column-body > .sidebar > ul > li > ul {\n  margin: 8px 0 0 1em;\n}\n\n#sidebar-navigation {\n  font-size: 15px;\n  padding-right: 1em;\n}\n\n#sidebar-navigation ul {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n}\n\n#sidebar-navigation li {\n  margin-bottom: 8px;\n}\n\n#sidebar-navigation ul ul {\n  margin: 8px 0 0 1em;\n}\n\n#sidebar-navigation a {\n  color: rgba(0, 0, 0, 0.87);\n  display: block;\n}\n\n#right-column a {\n  color: rgba(0, 0, 0, 0.87);\n  display: block;\n}\n\n#right-column a:hover {\n  color: {{ theme_link_color }};\n}\n\n#right-column a code {\n  color: rgba(0, 0, 0, 0.87);\n}\n\n#right-column h3 {\n  font-size: 1em;\n  line-height: 1.2em;\n  font-weight: 200;\n  margin: 0 0 1em 0;\n}\n\n#right-column ul {\n  list-style: none;\n  padding: 0;\n  margin: 0 0 0 0.5em;\n}\n\n#right-column ul ul {\n  margin: 0 0 0 1.5em;\n}\n\n#right-column li {\n  margin-bottom: 5px;\n}\n\n.side-column a {\n  padding-left: 6px;\n  border-left: 2px solid rgba(0, 0, 0, 0);\n}\n\n.side-column a.current {\n  border-left: 2px solid {{ theme_primary }};\n  font-weight: bold;\n}\n\n/* -- admonitions ----------------------------------------------------------- */\n\n.rubric {\n  margin: 2em 0 1em 0;\n  font-weight: bold;\n}\n\n.admonition {\n  margin: 20px 0;\n  padding: 1em 0.8em;\n  border-bottom: 1px solid #ddd;\n}\n\n.admonition dt {\n  font-weight: bold;\n}\n\n.admonition dl {\n  margin-bottom: 0;\n}\n\n.admonition-title {\n  margin: 0px 0 1em;\n  padding: 0;\n  font-size: 1em;\n  line-height: 1.1;\n  font-weight: bold;\n  color: rgba(0, 0, 0, 0.6)\n}\n\n.admonition.danger,\n.admonition.error {\n  background-color: #f8d7da;\n}\n\n.admonition.important,\n.admonition.warning,\n.admonition.attention,\n.admonition.caution {\n  background-color: #f6eab7;\n}\n\n.admonition.note,\n.admonition.hint {\n  background-color: #ddecfc;\n}\n\n.admonition.tip {\n  background-color: #dff6da;\n}\n\ndiv.seealso {\n  background-color: #eee;\n}\n\ndiv.admonition tt.xref, div.admonition a tt {\n  border-bottom: 1px solid {{ theme_site_background }};\n}\n\ndiv.admonition p.last {\n  margin-bottom: 0;\n}\n\n\n/* -- search page ----------------------- */\n\nul.search {\n  margin: 10px 0 0 20px;\n  padding: 0;\n  list-style: none;\n}\n\nul.search li {\n  padding: 5px 0 5px 20px;\n  background: url(file.png) no-repeat 0 7px;\n}\n\nul.search li a {\n  font-weight: bold;\n}\n\nul.search li div.context {\n  color: #888;\n  margin: 2px 0 0 30px;\n  text-align: left;\n}\n\nul.keywordmatches li.goodmatch a {\n  font-weight: bold;\n}\n\ndt:target, .highlighted {\n    background-color: #fbe54e;\n}\n\n/* -------- Page relations (bottom next/previous links) ------- */\n\n.relations {\n  margin-top: 3em;\n  padding-top: 2em;\n  display: flex;\n  border-top: 1px solid rgba(0, 0, 0, 0.15);\n}\n\n.relations .previous-page {\n  margin-right: auto;\n}\n\n.relations .next-page {\n  margin-left: auto;\n}\n\n.relations a {\n  display: block;\n  color: {{ theme_medium_primary }};\n  border-radius: 2px;\n  border: 1px solid {{ theme_medium_primary }};\n  padding: 0.8rem 1em;\n  transition: background .25s, color 0.25s;\n}\n\n.relations a:hover {\n  color: #fff;\n  background: {{ theme_medium_primary }};\n  text-decoration: none;\n}\n\n.next-previous {\n  font-size: 0.8em;\n  text-align: right;\n  margin-bottom: 1em;\n}\n\n#right-column .next-previous a {\n  display: inline;\n  padding-right: 2em;\n  text-transform: uppercase;\n  font-weight: bold;\n  color: {{ theme_link_color }};\n}\n\n/* -------- toctree --------- */\n\n.large-toctree > ul {\n  padding-left: 0;\n}\n\n.large-toctree .toctree-l1 {\n  margin-bottom: 2em;\n  list-style-type: none;\n}\n\n.large-toctree .toctree-l1:not(:last-child) {\n  border-bottom: 1px solid #ccc;\n  padding-bottom: 2em;\n}\n\n.large-toctree .toctree-l1 > a {\n  font-weight: bold;\n  padding-bottom: 1em;\n  display: block;\n}\n\n.caption-text {\n  font-weight: 300;\n  color: #222;\n}\n\n/* -------- Parent links --------- */\n\n.rel-parents {\n  list-style-type: none;\n  padding: 0;\n  margin: 0 0 0.5rem 0;\n  opacity: 0.9;\n  font-size: 90%;\n}\n\n.rel-parents li {\n  padding: 0;\n  margin: 0;\n  display: inline;\n}\n\n.rel-parents li:after {\n  content: \" / \";\n}\n\n.rel-parents li:last-child:after {\n  content: \"\";\n}\n\n/* -------- media query helpers ------- */\n\n@media (max-width: 1100px) {\n    #right-column { display: none; }\n    #page-container { padding: 1.5em; }\n}\n\n@media (max-width: 991px) {\n  #left-column { font-size: 0.8em; }\n  .hidden-sm, tr.hidden-sm, th.hidden-sm, td.hidden-sm {\n    display: none !important\n  }\n}\n\n@media (max-width: 767px) {\n  #document-body { padding: 0; }\n  .hidden-xs, tr.hidden-xs, th.hidden-xs, td.hidden-xs {\n    display: none !important\n  }\n}\n\n@media (max-width: 600px) {\n  /* Make the header navigation usable on a small screen. */\n  header {\n    display: block;\n    padding: 1em 1em 1em 0;\n    border-bottom: 1px solid rgba(1, 1, 1, 0.2);\n  }\n\n  .header-flex, .site-logo, #page-navigation {\n    display: block;\n    margin-left: 0.5em;\n    margin: 0.5em 0 0 0.5em;\n    padding: 0;\n  }\n\n  header .logo-icon {\n    display: none;\n  }\n\n  .site-logo .logo-text {\n    padding-left: 16px;\n  }\n\n  .splash-row {\n    display: block;\n  }\n\n  .splash-column {\n    display: block;\n    width: 100%;\n    padding: 0;\n  }\n\n  body, pre {\n    font-size: 0.9em;\n  }\n}\n\n/* ------- Make tables scroll on smaller screens. ------ */\n\n@media (max-width: 992px) {\n  table {\n    display: block;\n    width: 100%;\n    overflow-x: auto;\n    -ms-overflow-style: -ms-autohiding-scrollbar;\n  }\n}\n\n/* ------- Printer friendly styling ------ */\n\n@media print{\n  /* Hide the header, footer, relations, and table of contents */\n  header, footer, .relations, .contents { display: none; }\n\n  /* Make text and padding a lot tighter */\n  body {\n    font-size: 12px;\n    line-height: 1.1;\n  }\n\n  h1, h2, h3, h4, h5, h6 {\n    margin-top: 1.5em;\n  }\n\n  .sphinx-tabs {\n    margin: 0.5em 0 !important;\n  }\n\n  /* Hide code-tab tabs */\n  .tabular {\n    display: none !important;\n  }\n\n  #document-body { padding: 0; height: 100%; }\n\n  .hidden-xs, tr.hidden-xs, th.hidden-xs, td.hidden-xs {\n    display: none !important\n  }\n}\n"
  },
  {
    "path": "docs/source/theme/smithy/theme.conf",
    "content": "[theme]\ninherit = basic\nstylesheet = default.css\n\n[options]\nprimary = #232f3E\ndark_primary = #232f3E\nmedium_primary = #232f3E\nlight_primary = #2BBA9C\n\nmedium_accent = #2BBA9C\ndark_accent = #283D3B\nsite_background = #fff\nlink_color = #00818e;\nregular_font = -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'\ncode_font = \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, Courier, monospace\nga_id =\n"
  },
  {
    "path": "docs/source/topics/authorizers.rst",
    "content": "Authorization\n=============\n\nChalice supports multiple mechanisms for authorization.  This topic\ncovers how you can integrate authorization into your Chalice applications.\n\nIn Chalice, all the authorizers are configured per-route and specified\nusing the ``authorizer`` kwarg to an ``@app.route()`` call.  You\ncontrol which type of authorizer to use based on what's passed as the\n``authorizer`` kwarg.  You can use the same authorizer instance for\nmultiple routes.\n\nThe first set of authorizers chalice supports cover the scenario where\nyou have some existing authorization mechanism that you just want your\nChalice app to use.\n\nChalice also supports built-in authorizers, which allows Chalice to\nmanage your custom authorizers as part of ``chalice deploy``.  This is\ncovered in the Built-in Authorizers section.\n\n\nAWS IAM Authorizer\n------------------\n\nThe IAM Authorizer allows you to control access to API Gateway with\n`IAM permissions`_\n\nTo associate an IAM authorizer with a route in chalice, you use the\n:class:`IAMAUthorizer` class:\n\n.. code-block:: python\n\n    from chalice import IAMAuthorizer\n\n    authorizer = IAMAuthorizer()\n\n    @app.route('/iam-auth', methods=['GET'], authorizer=authorizer)\n    def authenticated():\n        return {\"success\": True}\n\n\nSee the `API Gateway documentation\n<https://docs.aws.amazon.com/apigateway/latest/developerguide/permissions.html>`__\nfor more information on controlling access to API Gateway with IAM permissions.\n\nAmazon Cognito User Pools\n-------------------------\n\nIn addition to using IAM roles and policies with the :class:`IAMAuthorizer` you\ncan also use a `Cognito user pools`_ to control who can access your Chalice\napp.  A cognito user pool serves as your own identity provider to maintain a\nuser directory.\n\nTo integrate Cognito user pools with Chalice, you'll need to have an existing\ncognito user pool configured.\n\n\n.. code-block:: python\n\n    from chalice import CognitoUserPoolAuthorizer\n\n    authorizer = CognitoUserPoolAuthorizer(\n        'MyPool', provider_arns=['arn:aws:cognito:...:userpool/name'])\n\n    @app.route('/user-pools', methods=['GET'], authorizer=authorizer)\n    def authenticated():\n        return {\"success\": True}\n\n\nFor more information about using Cognito user pools with API Gateway,\nsee the `Use Amazon Cognito User Pools documentation\n<https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-integrate-with-cognito.html>`__.\n\n\nCustom Authorizers\n------------------\n\nAPI Gateway also lets you write custom authorizers using a Lambda function.\nYou can configure a Chalice route to use a pre-existing Lambda function as\na custom authorizer.  If you also want to write and manage your Lambda\nauthorizer using Chalice, see the next section, Built-in Authorizers.\n\nTo connect an existing Lambda function as a custom authorizer in chalice,\nyou use the ``CustomAuthorizer`` class:\n\n.. code-block:: python\n\n    from chalice import CustomAuthorizer\n\n    authorizer = CustomAuthorizer(\n        'MyCustomAuth', header='Authorization',\n        authorizer_uri=('arn:aws:apigateway:region:lambda:path/2015-03-31'\n                        '/functions/arn:aws:lambda:region:account-id:'\n                        'function:FunctionName/invocations'))\n\n    @app.route('/custom-auth', methods=['GET'], authorizer=authorizer)\n    def authenticated():\n        return {\"success\": True}\n\n\n.. _builtin-authorizers:\n\nBuilt-in Authorizers\n--------------------\n\nThe ``IAMAuthorizer``, ``CognitoUserPoolAuthorizer``, and the\n``CustomAuthorizer`` classes are all for cases where you have existing\nresources for managing authorization and you want to wire them together with\nyour Chalice app.  A Built-in authorizer is used when you'd like to write your\ncustom authorizer in Chalice, and have the additional Lambda functions managed\nwhen you run ``chalice deploy/delete``.  This section will cover how to use the\nbuilt-in authorizers in chalice.\n\nCreating an authorizer in chalice requires you use the ``@app.authorizer``\ndecorator to a function.  The function must accept a single arg, which will be\nan instance of :class:`AuthRequest`.  The function must return a\n:class:`AuthResponse`.  As an example, we'll port the example from the `API\nGateway documentation`_.  First, we'll show the code and then walk through it:\n\n.. code-block:: python\n\n    from chalice import Chalice, AuthResponse\n\n    app = Chalice(app_name='demoauth1')\n\n\n    @app.authorizer()\n    def demo_auth(auth_request):\n        token = auth_request.token\n        # This is just for demo purposes as shown in the API Gateway docs.\n        # Normally you'd call an oauth provider, validate the\n        # jwt token, etc.\n        # In this example, the token is treated as the status for demo\n        # purposes.\n        if token == 'allow':\n            return AuthResponse(routes=['/'], principal_id='user')\n        else:\n            # By specifying an empty list of routes,\n            # we're saying this user is not authorized\n            # for any URLs, which will result in an\n            # Unauthorized response.\n            return AuthResponse(routes=[], principal_id='user')\n\n\n    @app.route('/', authorizer=demo_auth)\n    def index():\n        return {'context': app.current_request.context}\n\n\nIn the example above we define a built-in authorizer by decorating\nthe ``demo_auth`` function with the ``@app.authorizer()`` decorator.\nNote you must use ``@app.authorizer()`` and not ``@app.authorizer``.\nA built-in authorizer function has this type signature::\n\n    def auth_handler(auth_request: AuthRequest) -> AuthResponse: ...\n\nWithin the auth handler you must determine if the request is\nauthorized or not.  The ``AuthResponse`` contains the allowed\nURLs as well as the principal id of the user.  You can optionally\nreturn a dictionary of key value pairs (as the ``context`` kwarg).\nThis dictionary will be passed through on subsequent requests.\nIn our example above we're not using the context dictionary.\nAPI Gateway will convert all the values in the ``context``\ndictionary to string values.\n\nNow let's deploy our app.  As usual, we just need to run\n``chalice deploy`` and chalice will automatically deploy all the\nnecessary Lambda functions for us.\n\nNow when we try to make a request, we'll get an Unauthorized error::\n\n  $ http https://api.us-west-2.amazonaws.com/api/\n  HTTP/1.1 401 Unauthorized\n\n  {\n      \"message\": \"Unauthorized\"\n  }\n\nIf we add the appropriate authorization header, we'll see the call succeed::\n\n  $ http https://api.us-west-2.amazonaws.com/api/ 'Authorization: allow'\n  HTTP/1.1 200 OK\n\n  {\n      \"context\": {\n          \"accountId\": \"12345\",\n          \"apiId\": \"api\",\n          \"authorizer\": {\n              \"principalId\": \"user\"\n          },\n          \"httpMethod\": \"GET\",\n          \"identity\": {\n              \"accessKey\": null,\n              \"accountId\": null,\n              \"apiKey\": \"\",\n              \"caller\": null,\n              \"cognitoAuthenticationProvider\": null,\n              \"cognitoAuthenticationType\": null,\n              \"cognitoIdentityId\": null,\n              \"cognitoIdentityPoolId\": null,\n              \"sourceIp\": \"1.1.1.1\",\n              \"user\": null,\n              \"userAgent\": \"HTTPie/0.9.9\",\n              \"userArn\": null\n          },\n          \"path\": \"/api/\",\n          \"requestId\": \"d35d2063-56be-11e7-9ce1-dd61c24a3668\",\n          \"resourceId\": \"id\",\n          \"resourcePath\": \"/\",\n          \"stage\": \"dev\"\n      }\n  }\n\nThe low level API for API Gateway's custom authorizer feature requires\nthat an IAM policy must be returned.  The :class:`AuthResponse` class we're\nusing is a wrapper over building the IAM policy ourselves.  If you want\nlow level control and would prefer to construct the IAM policy yourself\nyou can return a dictionary of the IAM policy instead of an instance of\n:class:`AuthResponse`.  If you do that, the dictionary is returned\nwithout modification back to API Gateway.\n\nFor more information on custom authorizers, see the\n`Use API Gateway Custom Authorizers\n<https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html>`__\npage in the API Gateway user guide.\n\n\nScopes\n-------------------------\n\nOAuth 2.0 and OpenID Connect (OIDC) scopes can be used to implement access\ncontrols in your Chalice app. Scopes are supported when using the Cognito\nAuthorizer, Custom Authorizers, and Built-In Authorizers.\n\nTo integrate Scopes with a Cognito Authorizer in Chalice, you'll need to have\nan existing `Cognito user pools`_ and `Cognito resource server`_ configured.\nScopes for Cognito Authorizers need to include the full identifier\nwhich is ``resourceServerIdentifier/scopeName``.\n\nScopes can be configured per-authorizer using the ``scopes`` attribute.\n\n.. code-block:: python\n\n    from chalice import CognitoUserPoolAuthorizer\n\n    authorizer = CognitoUserPoolAuthorizer(\n        'MyPool', provider_arns=['arn:aws:cognito:...:userpool/name'],\n        scopes=[\"https://mychaliceapp.example.com/todos.read\"])\n\n    @app.route('/user-pools', methods=['GET'], authorizer=authorizer)\n    def authenticated():\n        return {\"success\": True}\n\nScopes can be configured per-route for an Authorizer using ``with_scopes``.\n\n.. code-block:: python\n\n    from chalice import CognitoUserPoolAuthorizer\n\n    authorizer = CognitoUserPoolAuthorizer(\n        'MyPool', provider_arns=['arn:aws:cognito:...:userpool/name'])\n\n    @app.route(\n        '/user-pools',\n        methods=['GET'],\n        authorizer=authorizer.with_scopes([\"https://mychaliceapp.example.com/todos.read\"]))\n    def authenticated():\n        return {\"success\": True}\n\nScopes can also be used with custom authorizers and built-in authorizers.\nThese authorizers will need to inspect the access token to determine if access\nshould be granted based on the scopes configured for the authorizer and route.\n\n\n.. _IAM permissions: https://docs.aws.amazon.com/IAM/latest/UserGuide/access_controlling.html\n.. _Cognito User Pools: https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools.html\n.. _Cognito Resource Server: https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-define-resource-servers.html\n.. _API Gateway documentation: https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html\n"
  },
  {
    "path": "docs/source/topics/blueprints.rst",
    "content": "Blueprints\n==========\n\n\nChalice blueprints are used to organize your application into logical\ncomponents.  Using a blueprint, you define your resources and decorators in\nmodules outside of your ``app.py``.  You then register a blueprint in your main\n``app.py`` file.  Blueprints support any decorator available on an application\nobject.\n\n\n.. note::\n\n  The Chalice blueprints are conceptually similar to `Blueprints\n  <https://flask.palletsprojects.com/blueprints/>`__ in Flask.  Flask\n  blueprints allow you to define a set of URL routes separately from the main\n  ``Flask`` object.  This concept is extended to all resources in Chalice.  A\n  Chalice blueprint can have Lambda functions, event handlers, built-in\n  authorizers, etc. in addition to a collection of routes.\n\n\nExample\n-------\n\nIn this example, we'll create a blueprint with part of our routes defined in a\nseparate file.  First, let's create an application::\n\n    $ chalice new-project blueprint-demo\n    $ cd blueprint-demo\n    $ mkdir chalicelib\n    $ touch chalicelib/__init__.py\n    $ touch chalicelib/blueprints.py\n\nNext, we'll open the ``chalicelib/blueprints.py`` file:\n\n.. code-block:: python\n\n    from chalice import Blueprint\n\n\n    extra_routes = Blueprint(__name__)\n\n\n    @extra_routes.route('/foo')\n    def foo():\n        return {'foo': 'bar'}\n\n\nThe ``__name__`` is used to denote the import path of the blueprint.  This name\nmust match the import name of the module so the function can be properly\nimported when running in Lambda.  We'll now import this module in our\n``app.py`` and register this blueprint.  We'll also add a route in our\n``app.py`` directly:\n\n.. code-block:: python\n\n    from chalice import Chalice\n    from chalicelib.blueprints import extra_routes\n\n    app = Chalice(app_name='blueprint-demo')\n    app.register_blueprint(extra_routes)\n\n\n    @app.route('/')\n    def index():\n        return {'hello': 'world'}\n\nAt this point, we've defined two routes.  One route, ``/``, is directly defined\nin our ``app.py`` file.  The other route, ``/foo`` is defined in\n``chalicelib/blueprints.py``.  It was added to our Chalice app when we\nregistered it via ``app.register_blueprint(extra_routes)``.\n\nWe can deploy our application to verify this works as expected::\n\n    $ chalice deploy\n    Creating deployment package.\n    Creating IAM role: blueprint-demo-dev\n    Creating lambda function: blueprint-demo-dev\n    Creating Rest API\n    Resources deployed:\n      - Lambda ARN: arn:aws:lambda:us-west-2:1234:function:blueprint-demo-dev\n      - Rest API URL: https://rest-api.execute-api.us-west-2.amazonaws.com/api/\n\n\nWe should now be able to request the ``/`` and ``/foo`` routes::\n\n    $ http https://rest-api.execute-api.us-west-2.amazonaws.com/api/\n    HTTP/1.1 200 OK\n    Connection: keep-alive\n    Content-Length: 17\n    Content-Type: application/json\n    Date: Sat, 22 Dec 2018 01:05:48 GMT\n    Via: 1.1 5ab5dc09da67e3ea794ec8a82992cc89.cloudfront.net (CloudFront)\n    X-Amz-Cf-Id: Cdsow9--fnTH5EdjkjWBMWINCCMD4nGmi4S_3iMYMK0rpc8Mpiymgw==\n    X-Amzn-Trace-Id: Root=1-5c1d8dec-f1ef3ee83c7c654ca7fb3a70;Sampled=0\n    X-Cache: Miss from cloudfront\n    x-amz-apigw-id: SSMc6H_yvHcFcEw=\n    x-amzn-RequestId: b7bd0c87-0585-11e9-90cf-59b71c1a1de1\n\n    {\n        \"hello\": \"world\"\n    }\n\n    $ http https://rest-api.execute-api.us-west-2.amazonaws.com/api/foo\n    HTTP/1.1 200 OK\n    Connection: keep-alive\n    Content-Length: 13\n    Content-Type: application/json\n    Date: Sat, 22 Dec 2018 01:05:51 GMT\n    Via: 1.1 95b0ac620fa3a80ee590ecf1cda1c698.cloudfront.net (CloudFront)\n    X-Amz-Cf-Id: HX4l1BNdWvYDRXan17PFZya1vaomoJel4rP7d8_stdw2qT50v7Iybg==\n    X-Amzn-Trace-Id: Root=1-5c1d8def-214e7f681ff82c00fd81f37a;Sampled=0\n    X-Cache: Miss from cloudfront\n    x-amz-apigw-id: SSMdXF40vHcF-mg=\n    x-amzn-RequestId: b96f77bf-0585-11e9-b229-01305cd40040\n\n    {\n        \"foo\": \"bar\"\n    }\n\n\nBlueprint Registration\n----------------------\n\nThe ``app.register_blueprint`` function accepts two optional arguments,\n``name_prefix`` and ``url_prefix``.  This allows you to register the resources\nin your blueprint at a certain url and name prefix.  If you specify\n``url_prefix``, any routes defined in your blueprint will have the\n``url_prefix`` prepended to it.  If you specify the ``name_prefix``, any Lambda\nfunctions created will have the ``name_prefix`` prepended to the resource name.\n\n.. note::\n\n  The ``name_prefix`` parameter does not apply to the Lambda function\n  associated with API Gateway, which is anything decorated with\n  ``@app.route()``.\n\n\nAdvanced Example\n----------------\n\nLet's create a more advanced example.  If this application, let's say we want\nto organize our application into separate modules for our API and our event\nsources.  We can create an app with these files::\n\n    $ ls -la chalicelib/\n    __init__.py\n    api.py\n    events.py\n\n\nThe contents of ``api.py`` are:\n\n.. code-block:: python\n\n    from chalice import Blueprint\n\n\n    myapi = Blueprint(__name__)\n\n\n    @myapi.route('/')\n    def index():\n        return {'hello': 'world'}\n\n\n    @myapi.route('/foo')\n    def index():\n        return {'foo': 'bar'}\n\n\nThe contents of ``events.py`` are:\n\n.. code-block:: python\n\n    from chalice import Blueprint\n\n\n    myevents = Blueprint(__name__)\n\n\n    @myevents.schedule('rate(5 minutes)')\n    def cron(event):\n        pass\n\n\n    @myevents.on_sns_message('MyTopic')\n    def handle_sns_message(event):\n        pass\n\nIn our ``app.py`` we'll register these blueprints:\n\n.. code-block:: python\n\n    from chalice import Chalice\n    from chalicelib.events import myevents\n    from chalicelib.api import myapi\n\n    app = Chalice(app_name='blueprint-demo')\n    app.register_blueprint(myevents)\n    app.register_blueprint(myapi)\n\n\nNow our ``app.py`` only registers the necessary blueprints, and all our\nresources are defined in blueprints.\n"
  },
  {
    "path": "docs/source/topics/cd.rst",
    "content": "===========================\nContinuous Deployment (CD)\n===========================\n\nChalice can be used to set up a basic Continuous Deployment pipeline. The\n``chalice deploy`` command is good for getting up and running quickly with\nChalice, but in a team environment properly managing permissions and sharing\nand updating the ``deployed.json`` file will get messy.\n\nOne way to scale up your chalice app is to create a continuous deployment\npipeline. The pipeline can run tests on code changes and, if they pass, promote\nthe new build to a testing stage. More checks can be put in place to manually\npromote a build to production, or you can do so automatically. This model\ngreatly simplifies managing what resources belong to your Chalice app as they\nare all stored in the Continuous Deployment pipeline.\n\nChalice can generate a CloudFormation template that will create a starter CD\npipeline. By default it contains an AWS CodeCommit repo, an AWS CodeBuild stage\nfor packaging your chalice app, and an AWS CodePipeline stage to deploy your\napplication using CloudFormation.\n\nYou can also configure a source repository hosted on GitHub instead of\na CodeCommit repository.\n\nPipeline Template Versions\n==========================\n\nThis starter pipeline template can be generated using the ``generate-pipeline``\ncommand.  There are two versions of this pipeline.  The older ``v1`` template\nis the default (for backwards compatibility reasons), but the newer template\nversion, ``v2``, is recommended.  The version can be specified using the\n``--pipeline-version`` option.  These are the differences between ``v1`` and\n``v2`` templates:\n\n* The ``v1`` templates use version ``0.1`` of the CodeBuild buildspec, whereas\n  ``v2`` uses ``0.2`` of the CodeBuild buildspec.  Buildspec ``0.2`` is the\n  recommended version to use with CodeBuild.  See their\n  `documentation <https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html>`__\n  for more information.\n* The ``v2`` template uses `AWS Secrets Manager <https://aws.amazon.com/secrets-manager/>`__\n  to configure access to a GitHub repository.\n* The ``v2`` buildspec uses `runtime-versions <https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html#build-spec.phases.install.runtime-versions>`__\n  to configure which version of Python to use instead of a Python\n  version specific CodeBuild image.  For ``v2`` templates the\n  ``aws/codebuild/amazonlinux2-x86_64-standard`` image.\n\n**The v2 pipeline template requires Python 3.7 or higher.** If you're using\nPython versions less than 3.7 you must use the ``v1`` pipeline template.\n\n\nUsage example\n=============\n\nSetting up the deployment pipeline is a two step process. First use the\n``chalice generate-pipeline`` command to generate a base CloudFormation\ntemplate. Second use the AWS CLI to deploy the CloudFormation template using\nthe ``aws cloudformation deploy`` command. Below is an example.\n\n::\n\n   $ chalice generate-pipeline --pipeline-version v2 pipeline.json\n   $ aws cloudformation deploy --stack-name mystack\n         --template-file pipeline.json --capabilities CAPABILITY_IAM\n   Waiting for changeset to be created..\n   Waiting for stack create/update to complete\n   Successfully created/updated stack - mystack\n\n.. note::\n   To configure your Chalice app to use a GitHub repository instead of\n   CodeCommit see the :ref:`cicd-github-repo` section below.\n\n\nOnce the CloudFormation template has finished creating the stack, you will have\nseveral new AWS resources that make up a bare bones CD pipeline.\n\n* **CodeCommit Repository** - The `CodeCommit <https://aws.amazon.com/codecommit/>`_\n  repository is the entrypoint into the pipeline. Any code you want to deploy\n  should be pushed to this remote.\n* **CodePipeline Pipeline** - The\n  `CodePipeline <https://aws.amazon.com/codepipeline/>`_ is what coordinates\n  the build process, and pushes the released code out.\n* **CodeBuild Project** - The `CodeBuild <https://aws.amazon.com/codebuild/>`_\n  project is where the code bundle is built that will be pushed to Lambda. The\n  default CloudFormation template will create a CodeBuild stage that builds\n  a package using ``chalice package`` and then uploads those artifacts for\n  CodePipeline to deploy.\n* **S3 Buckets** - Two S3 buckets are created on your behalf.\n\n  * **artifactbucketstore** - This bucket stores artifacts that are built by\n    the CodeBuild project. The only artifact by default is the\n    ``transformed.yaml`` created by the ``aws cloudformation package`` command.\n  * **applicationbucket** - Stores the application bundle after the Chalice\n    application has been packaged in the CodeBuild stage.\n* Each resource is created with all the required IAM roles and policies.\n\n\nCodeCommit repository\n---------------------\n\nThe CodeCommit repository can be added as a git remote for deployment. This\nmakes it easy to kick off deployments. The developer doing the deployment only\nneeds to push the release code up to the CodeCommit repository master branch.\nAll the developer needs is keys that allow for push access to the CodeCommit\nrepository. This is a lot easier than managing a set of ``deployed.json``\nresources across a repsoitory and manually doing ``chalice deploy`` whenever\na change needs to be deployed.\n\nThe default CodeCommit repository that is created is empty, you will have to\npopulate it with the Chalice application code. Permissions will also need to be\nset up, you can find the documentation on how to do that\n`here <https://docs.aws.amazon.com/codebuild/latest/userguide/setting-up.html>`_\n.\n\nYou can retrieve the CodeCommit clone URL by searching for the\n``SourceRepoURL`` in the CloudFormation stack output::\n\n    $ aws cloudformation describe-stacks --stack-name mystack \\\n       --query \"Stacks[0].Outputs[?OutputKey=='SourceRepoURL'] | [0].OutputValue\"\n\n\nCodePipeline\n------------\n\nCodePipeline is the main coordinator between all the other resources. It\nwatches for changes on the CodeCommit repository, and triggers builds in the\nCodeBuild project. If the build succeeds then it will start a CloudFormation\ndeployment of the built artifacts to a beta stage. This should be treated as\na starting point, not a fully featured CD system.\n\n\nCodeBuild build script\n----------------------\n\nBy default Chalice will create the CodeBuild project with a default buildspec\nthat does the following.\n\n.. code-block:: yaml\n\n  version: 0.1\n  phases:\n    install:\n      commands:\n      - sudo pip install --upgrade awscli\n      - aws --version\n      - sudo pip install chalice\n      - sudo pip install -r requirements.txt\n      - chalice package /tmp/packaged\n      - aws cloudformation package --template-file\n          /tmp/packaged/sam.json --s3-bucket ${APP_S3_BUCKET}\n          --output-template-file transformed.yaml\n  artifacts:\n    type: zip\n    files:\n      - transformed.yaml\n\nThe CodeBuild stage installs both the AWS CLI and Chalice, then creates a\npackage out of your chalice project, pushing the package to the application\nS3 bucket that was created for you. The transformed CloudFormation template\nis the only artifact, and can be run by CodePipeline after the build has\nsucceeded.\n\n\nDeploying to beta stage\n-----------------------\n\nOnce the CodeBuild stage has finished building the Chalice package and\ncreating the ``transformed.yaml``, CodePipeline will take these artifacts and\nuse them to create or update the beta stage. The ``transformed.yaml``\nis a CloudFormation template that CodePipeline will execute, all the code it\nreferences has been uploaded to the application bucket by the AWS CLI in the\nCodeBuild stage, so this is the only artifact we need.\n\nOnce the CodePipeline beta build stage is finished, the beta version of the app\nis deployed and ready for testing.\n\n\nExtending\n---------\n\nIt is recommended to use this pipeline as a starting point. The default\ntemplate does not run any tests on the Chalice app before deploying to beta.\nThere is also no mechanism provided by Chalice for a production stage.\nIdeally the CodeBuild stage would be used to run unit and functional tests\nbefore deploying to beta. After the beta stage is up, integration tests can be\nrun against that endpoint, and if they all pass the beta stage could be\npromoted to a production stage using the CodePipeline manual approval feature.\n\n.. _cicd-github-repo:\n\nConfiguring a GitHub Repository\n===============================\n\nYou can configure a GitHub repository instead of a CodeCommit repo when\nsetting up your deployment pipeline by specifying the ``--source github``\noption.  When generating a CloudFormation template for a GitHub repository,\nthere are several parameters that are added to your template that allow\nyou to configure how to connect your GitHub repository with your CodePipeline.\n\nYou must store your OAuth token that enables access to a GitHub repository\nin AWS Secrets Manager.  You then specify the secret name/id and the JSON\nkey name as CloudFormation parameters.  These values default to a secret\nname of ``GithubRepoAccess`` and a JSON key name of ``OAuthToken``.\n\nBelow is an example of how to configure a GitHub repository as the\nsource for your deployment pipeline.\n\nFirst create a `GitHub token <https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token>`__\nthat can be used in this template.  Next create a secret in AWS Secrets\nManager.  You can either follow the documentation\n`here <https://docs.aws.amazon.com/secretsmanager/latest/userguide/manage_create-basic-secret.html>`__\nor use the AWS CLI or any AWS SDK.  For this example, we'll use the AWS CLI\nto create our secret.  Create a file named ``/tmp/secrets.json`` with these\ncontents::\n\n    {\"OAuthToken\": \"abcdefghhijklmnop\"}\n\nBe sure to replace the value of ``OAuthToken`` with the value of your GitHub\ntoken you created.  Next we can create the secret using this command::\n\n    $ aws secretsmanager create-secret --name GithubRepoAccess \\\n      --description \"Token for Github Repo Access\" \\\n      --secret-string file:///tmp/secrets.json\n\nNow we can generate our deployment pipeline::\n\n    $ chalice generate-pipeline --pipeline-version v2 \\\n      --source github --buildspec-file buildspec.yml pipeline.json\n\nThis will create two files, a ``pipeline.json`` file containing our\ndeployment pipeline and a ``buildspec.yml`` file.  This buildspec file\nlets us update what commands should be run as part of our build process\nwithout having to redeploy our CloudFormation template.\n\nWe now add and commit our changes to our repository.\n\n::\n\n    $ git add buildspec.yml pipeline.json\n    $ git commit -m \"Add deployment pipeline template\"\n    $ git push\n\nNow we're ready to deploy our CloudFormation template using the AWS CLI.  Be\nsure to replace the ``GithubOwner`` and ``GithubRepoName`` with your own\nvalues for your GitHub repository.  You'll also need to specify the\n``GithubRepoSecretId`` and ``GithubRepoSecretJSONKey`` if you used values\nother than the default vaues of ``GithubRepoAccess`` and ``OAuthToken`` when\ncreating your secret in Secrets Manager.\n\n::\n\n    $ aws cloudformation deploy --template-file pipeline.json \\\n      --stack-name MyChaliceApp --parameter-overrides \\\n      GithubOwner=repo-owner-name \\\n      GithubRepoName=repo-name \\\n      --capabilities CAPABILITY_IAM\n\nWe've now created a deployment pipeline that will automatically deploy our\nChalice app whenever we push to our GitHub repository.\n"
  },
  {
    "path": "docs/source/topics/cfn.rst",
    "content": "AWS CloudFormation Support\n==========================\n\nWhen you run ``chalice deploy``, chalice will deploy your application using the\n`AWS SDK for Python <http://boto3.readthedocs.io/en/docs/>`__).  Chalice also\nprovides functionality that allows you to manage deployments yourself using\ncloudformation.  This is provided via the ``chalice package`` command.\n\nWhen you run this command, chalice will generate the AWS Lambda deployment\npackage that contains your application as well as a `Serverless Application\nModel (SAM) <https://github.com/awslabs/serverless-application-model>`__\ntemplate.  You can then use a tool like the AWS CLI, or any cloudformation\ndeployment tools you use, to deploy your chalice application.\n\nConsiderations\n--------------\n\nUsing the ``chalice package`` command is useful when you don't want to\nuse ``chalice deploy`` to manage your deployments.  There's several reasons\nwhy you might want to do this:\n\n* You have pre-existing infrastructure and tooling set up to manage\n  cloudformation stacks.\n* You want to integrate with other cloudformation stacks to manage\n  all your AWS resources, including resources outside of your chalice\n  app.\n* You'd like to integrate with `AWS CodePipeline\n  <https://aws.amazon.com/codepipeline/>`__ to automatically deploy\n  changes when you push to a git repo.\n\nKeep in mind that you can't switch between ``chalice deploy`` and\n``chalice package`` + CloudFormation for deploying your app.\n\nIf you choose to use ``chalice package`` and CloudFormation to deploy\nyour app, you won't be able to switch back to ``chalice deploy``.\nRunning ``chalice deploy`` would create an entirely new set of AWS\nresources (API Gateway Rest API, AWS Lambda function, etc).\n\nTemplate Merge\n--------------\n\nIt's a common use case to need to modify a Chalice generated template\nbefore deployment. Often to inject extra resources, values, or\nconfigurations that are not supported directly by Chalice. It will\nalways be the case that something on AWS is not supported by Chalice\ndirectly that a consumer may want to interact with.\n\nThe package command can now be invoked with the ``--merge-template`` argument::\n\n  $ chalice package --merge-template extras.json out\n\nThis extras.json file should be a JSON formatted file which will be\ndeep-merged on top of the sam.json that is generated by Chalice.\n\nFor a simple example lets assume that we have the default new Chalice\nproject and that extras.json has the following content::\n\n    {\n      \"Resources\" : {\n        \"MusicTable\" : {\n          \"Type\" : \"AWS::DynamoDB::Table\",\n          \"Properties\" : {\n            \"TableName\" : \"MusicData\",\n            \"AttributeDefinitions\" : [\n              {\n                \"AttributeName\" : \"Album\",\n                \"AttributeType\" : \"S\"\n              },\n              {\n                \"AttributeName\" : \"Artist\",\n                \"AttributeType\" : \"S\"\n              }\n            ],\n            \"KeySchema\" : [\n              {\n                \"AttributeName\" : \"Album\",\n                \"KeyType\" : \"HASH\"\n              },\n              {\n                \"AttributeName\" : \"Artist\",\n                \"KeyType\" : \"RANGE\"\n              }\n            ],\n            \"ProvisionedThroughput\" : {\n              \"ReadCapacityUnits\" : \"5\",\n              \"WriteCapacityUnits\" : \"5\"\n            }\n          }\n        },\n        \"APIHandler\": {\n          \"Properties\": {\n            \"Environment\": {\n              \"Variables\": {\n                \"MUSIC_TABLE\": {\"Ref\": \"MusicTable\"}\n              }\n            }\n          }\n        }\n      }\n    }\n\n\nThe generated template located at out/sam.json will have the DynamoDB table\ninjected into the resource section, as well as the ``MUSIC_TABLE`` environment\nvariable added to the ``APIHandler`` Lambda function::\n\n    ...\n        \"APIHandler\": {\n          \"Type\": \"AWS::Serverless::Function\",\n          \"Properties\": {\n            \"Runtime\": \"python3.6\",\n            \"Handler\": \"app.app\",\n            \"CodeUri\": \"./deployment.zip\",\n            \"Tags\": {\n              \"aws-chalice\": \"version=1.10-:stage=dev:app=test\"\n            },\n            \"Timeout\": 60,\n            \"MemorySize\": 128,\n            \"Role\": {\n              \"Fn::GetAtt\": [\n                \"DefaultRole\",\n                \"Arn\"\n              ]\n            },\n            \"Environment\": {\n              \"Variables\": {\n                \"MUSIC_TABLE\": \"MusicData\"\n              }\n            }\n          }\n        },\n    ...\n\nThis gives us the ability to inject arbitrary resources into our Chalice\napplications, and reference them from our Chalice-deployed functions. We can\nnow rely on Chalice's policy auto-generation to generate a policy that allows\nDynamoDB access, inject our own policy modifications through the same\nextras.json file, or specify a custom policy using the config file.\n\n\nExample\n-------\n\nIn this example, we'll create a chalice app and deploy it using\nthe AWS CLI.\n\nFirst install the necessary packages::\n\n    $ virtualenv /tmp/venv\n    $ . /tmp/venv/bin/activate\n    $ pip install chalice awscli\n    $ chalice new-project test-cfn-deploy\n    $ cd test-cfn-deploy\n\nAt this point we've installed chalice and the AWS CLI and we have\na basic app created locally.  Next we'll run the ``package`` command\nand look at its contents::\n\n    $ $ chalice package /tmp/packaged-app/\n    Creating deployment package.\n    $ ls -la /tmp/packaged-app/\n    -rw-r--r--   1 j         wheel  3355270 May 25 14:20 deployment.zip\n    -rw-r--r--   1 j         wheel     3068 May 25 14:20 sam.json\n\n    $ unzip -l /tmp/packaged-app/deployment.zip  | tail -n 5\n        17292  05-25-17 14:19   chalice/app.py\n          283  05-25-17 14:19   chalice/__init__.py\n          796  05-25-17 14:20   app.py\n     --------                   -------\n      9826899                   723 files\n\n    $ head < /tmp/packaged-app/sam.json\n    {\n      \"AWSTemplateFormatVersion\": \"2010-09-09\",\n      \"Outputs\": {\n        \"RestAPIId\": {\n          \"Value\": {\n            \"Ref\": \"RestAPI\"\n          }\n        },\n        \"APIHandlerName\": {\n          \"Value\": {\n\nAs you can see in the above example, the ``package`` command created a\ndirectory that contained two files, a ``deployment.zip`` file, which is the\nLambda deployment package, and a ``sam.json`` file, which is the SAM template\nthat can be deployed using CloudFormation.  Next we're going to use the AWS CLI\nto deploy our app.  To this, we'll first run the ``aws cloudformation package``\ncommand, which will take our deployment.zip file and upload to an S3 bucket\nwe specify::\n\n    $ aws cloudformation package \\\n         --template-file /tmp/packaged-app/sam.json \\\n         --s3-bucket myapp-bucket \\\n         --output-template-file /tmp/packaged-app/packaged.yaml\n\nNow we can deploy our app using the ``aws cloudformation deploy`` command::\n\n    $ aws cloudformation deploy \\\n        --template-file /tmp/packaged-app/packaged.yaml \\\n        --stack-name test-cfn-stack \\\n        --capabilities CAPABILITY_IAM\n    Waiting for changeset to be created..\n    Waiting for stack create/update to complete\n    Successfully created/updated stack - test-cfn-stack\n\nThis will take a few minutes to complete, but once it's done, the endpoint url\nwill be available as an output::\n\n    $ aws cloudformation describe-stacks --stack-name test-cfn-stack \\\n      --query \"Stacks[].Outputs[?OutputKey=='EndpointURL'][] | [0].OutputValue\"\n    \"https://abc29hkq0i.execute-api.us-west-2.amazonaws.com/api/\"\n\n    $ http \"https://abc29hkq0i.execute-api.us-west-2.amazonaws.com/api/\"\n    HTTP/1.1 200 OK\n    Connection: keep-alive\n    Content-Length: 18\n    Content-Type: application/json\n    ...\n\n    {\n        \"hello\": \"world\"\n    }\n"
  },
  {
    "path": "docs/source/topics/configfile.rst",
    "content": "Configuration File\n==================\n\nWhenever you create a new project using\n``chalice new-project``, a ``.chalice`` directory is created\nfor you.  In this directory is a ``config.json`` file that\nyou can use to control what happens when you ``chalice deploy``::\n\n\n    $ tree -a\n    .\n    ├── .chalice\n    │   └── config.json\n    ├── app.py\n    └── requirements.txt\n\n    1 directory, 3 files\n\n\n.. _stage-config:\n\nStage Specific Configuration\n----------------------------\n\nAs of version 0.7.0 of chalice, you can specify configuration\nthat is specific to a chalice stage as well as configuration that should\nbe shared across all stages.  See the :doc:`stages` doc for more\ninformation about chalice stages.\n\n* ``stages`` - This value of this key is a mapping of chalice stage\n  name to stage configuration.  Chalice assumes a default stage name\n  of ``dev``.  If you run the ``chalice new-project`` command on\n  chalice 0.7.0 or higher, this key along with the default ``dev``\n  key will automatically be created for you.  See the examples\n  section below for some stage specific configurations.\n\nThe following config values can either be specified per stage config\nor as a top level key which is not tied to a specific stage.  Whenever\na stage specific configuration value is needed, the ``stages`` mapping\nis checked first.  If no value is found then the top level keys will\nbe checked.\n\n\n``api_gateway_endpoint_type``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe endpoint configuration of the deployed API Gateway which determines how the\nAPI will be accessed, can be EDGE, REGIONAL, PRIVATE. Note this value can only\nbe set as a top level key and defaults to EDGE. For more information see\nhttps://amzn.to/2LofApt\n\n\n``api_gateway_endpoint_vpce``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWhen configuring a Private API a VPC Endpoint id must be specified to configure\na default resource policy on the API if an explicit policy is not specified.\nThis value can be a list or a string of endpoint ids.\n\n\n``api_gateway_policy_file``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nA file pointing to an IAM resource policy for the REST API. If not specified\nchalice will autogenerate this policy when endpoint_type is PRIVATE. This\nfilename is relative to the ``.chalice`` directory.\n\n\n``api_gateway_stage``\n~~~~~~~~~~~~~~~~~~~~~\n\nThe name of the API gateway stage.  This will also be the URL prefix for your\nAPI (``https://endpoint/prefix/your-api``).\n\n\n``autogen_policy``\n~~~~~~~~~~~~~~~~~~\n\nA boolean value that indicates if chalice should try to automatically generate\nan IAM policy based on analyzing your application source code.  The default\nvalue is ``true``.  If this value is ``false`` then chalice will try to load an\nIAM policy from disk at ``.chalice/policy-<stage-name>.json`` instead of\nauto-generating a policy from source code analysis. You can change the filename\nby providing the ``iam_policy_file`` config option.\nSee :ref:`iam-role-pol-examples` for examples of how to configure IAM roles\nand policies.\n\n\n``environment_variables``\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\nA mapping of key value pairs.  These key value pairs will be set as environment\nvariables in your application.  All environment variables must be strings.  If\nthis key is specified in both a stage specific config option as well as a top\nlevel key, the stage specific environment variables will be merged into the top\nlevel keys.  See the :ref:`env-var-examples` section below for a concrete\nexample.\n\n\n``iam_policy_file``\n~~~~~~~~~~~~~~~~~~~\n\nWhen ``autogen_policy`` is ``false``, Chalice will try to load an IAM policy\nfrom disk instead of auto-generating one based on source code analysis.  The\ndefault location of this file is ``.chalice/policy-<stage-name>.json``, e.g\n``.chalice/policy-dev.json``, ``.chalice/policy-prod.json``, etc.  You can\nchange the filename by providing this ``iam_policy_file`` config option.  This\nfilename is relative to the ``.chalice`` directory.  For example, this config\nwill create an IAM role using the file in ``.chalice/my-policy.json``::\n\n  {\n    \"version\": \"2.0\",\n    \"app_name\": \"app\",\n    \"stages\": {\n      \"dev\": {\n        \"autogen_policy\": false,\n        \"iam_policy_file\": \"my-policy.json\"\n      }\n    }\n  }\n\n\nSee :ref:`iam-role-pol-examples` for more examples of how to configure IAM\nroles and policies.\n\n\n``iam_role_arn``\n~~~~~~~~~~~~~~~~\n\nIf ``manage_iam_role`` is ``false``, you must specify this value that indicates\nwhich IAM role arn to use when configuration your application.  This value is\nonly used if ``manage_iam_role`` is ``false``.\nSee :ref:`iam-role-pol-examples` for examples of how to configure IAM roles\nand policies.\n\n\n``lambda_memory_size``\n~~~~~~~~~~~~~~~~~~~~~~\n\nAn integer representing the amount of memory, in MB, your Lambda function is\ngiven. AWS Lambda uses this memory size to infer the amount of CPU allocated to\nyour function. The default ``lambda_memory_size`` value is ``128``. The value\nmust be a multiple of 64 MB.\n\n\n``lambda_timeout``\n~~~~~~~~~~~~~~~~~~\n\nAn integer representing the function execution time, in seconds, at which AWS\nLambda should terminate the function. The default ``lambda_timeout`` is ``60``\nseconds.\n\n\n``layers``\n~~~~~~~~~~\n\nA list of Lambda Layers arns. This value can be provided per stage as well as\nper Lambda function. See `AWS Lambda Layers Configuration`_.\n\n\n\n.. _automatic-layer-option:\n\n``automatic_layer``\n~~~~~~~~~~~~~~~~~~~~\n\nA boolean value that indicates whether chalice will automatically construct a\nsingle stage layer for all Lambda functions with requirements.txt libraries and\nvendored libraries.  Boolean value defaults to ``false`` if not specified.  See\n:ref:`package-3rd-party` for more information.\n\n\n.. _custom-domain-config-options:\n\n``api_gateway_custom_domain``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nA mapping of key value pairs. The following are required keys when\nspecifying an ``api_gateway_custom_domain``:\n\n- ``domain_name``: The custom domain name to associated with the REST API\n  (api.example.com)\n- ``certificate_arn``: the ARN of ACM certificate for the current domain name.\n  If you're using a ``REGIONAL`` endpoint type for your API, the ACM\n  certificate **must** be in the same region as your API.  If you're using an\n  ``EDGE`` endpoint type, the certificate must be in ``us-east-1``.\n\nYou can also provide the following optional configuration:\n\n- ``tls_version`` - The Transport Layer Security (TLS) version of the security\n  policy for this domain name.  Defaults to ``TLS_1_2``, you can also provide\n  ``TLS_1_0`` for REST APIs.\n- ``url_prefix`` - A custom domain name plus a url_prefix (BasePathMapping)\n  specification identifies a deployed REST API in a given stage. With custom\n  domain names, you can set up your API's hostname, and choose a base path (for\n  example, `myservice`) to map the alternative URL to your API (for example\n  ``https://api.example.com/myservice``).  If you don't set any ``url_prefix``,\n  the resulting API's base URL is the same as the custom domain (for example\n  ``https://api.example.com/``).\n- tags - A dictionary of tags with the keys being the tag key, and the values\n  being the value for the tag.\n\nSee the :doc:`domainname` documentation for more information on configuring\nyour Chalice application with a custom domain name.\n\nSee `AWS Custom Domain names setup`_ for the API Gateway documentation on\nconfiguring a custom domain name.\n\n.. _custom-domain-ws-config-options:\n\n``websocket_api_custom_domain``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nA mapping of key value pairs. The following are required keys when\nspecifying a ``websocket_api_custom_domain``:\n\n- ``domain_name``: The custom domain name to associated with the REST API\n  (api.example.com)\n- ``certificate_arn``: the ARN of ACM certificate for the current domain name.\n  If you're using a ``REGIONAL`` endpoint type for your API, the ACM\n  certificate **must** be in the same region as your API.  If you're using an\n  ``EDGE`` endpoint type, the certificate must be in ``us-east-1``.\n\nYou can also provide the following optional configuration:\n\n- ``tls_version`` - The Transport Layer Security (TLS) version of the security\n  policy for this domain name.  Defaults to ``TLS_1_2``, you can also provide\n  ``TLS_1_0`` for REST APIs.\n- ``url_prefix`` - A custom domain name plus a url_prefix (BasePathMapping)\n  specification identifies a deployed REST API in a given stage. With custom\n  domain names, you can set up your API's hostname, and choose a base path (for\n  example, `myservice`) to map the alternative URL to your API (for example\n  ``https://api.example.com/myservice``).  If you don't set any ``url_prefix``,\n  the resulting API's base URL is the same as the custom domain (for example\n  ``https://api.example.com/``).\n- tags - A dictionary of tags with the keys being the tag key, and the values\n  being the value for the tag.\n\nSee the :doc:`domainname` documentation for more information on configuring\nyour Chalice application with a custom domain name.\n\nSee `AWS Custom Domain names setup`_ for the API Gateway documentation on\nconfiguring a custom domain name.\n\n``manage_iam_role``\n~~~~~~~~~~~~~~~~~~~\n\n``true``/``false``.  Indicates if you want chalice to create and update the IAM\nrole used for your application.  By default, this value is ``true``.  However,\nif you have a pre-existing role you've created, you can set this value to\n``false`` and a role will not be created or updated.  ``\"manage_iam_role\":\nfalse`` means that you are responsible for managing the role and any associated\npolicies associated with that role.  If this value is ``false`` you must\nspecify an ``iam_role_arn``, otherwise an error is raised when you try to run\n``chalice deploy``.\n\n\n``minimum_compression_size``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nAn integer value that indicates the minimum compression size to apply to the\nAPI gateway. If this key is specified in both a stage specific config option as\nwell as a top level key, the stage specific key will override the top level key\nfor the given stage. For more information check out the `Service Docs\n<https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-gzip-compression-decompression.html>`__\n\n\n``reserved_concurrency``\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nAn integer representing each function's reserved concurrency.  This value can\nbe provided per stage as well as per Lambda function. AWS Lambda reserves this\nvalue of concurrency to each lambda deployed in this stage. If the value is set\nto 0, invocations to this function are blocked. If the value is unset, there\nwill be no reserved concurrency allocations. For more information, see `AWS\nDocumentation on managing concurrency`_.\n\n\n``subnet_ids``\n~~~~~~~~~~~~~~\n\nA list of subnet ids for VPC configuration.  This value can be provided per\nstage as well as per Lambda function.  In order for this value to take effect,\nyou must also provide the ``security_group_ids`` value.  When both values are\nprovided and ``autogen_policy`` is True, chalice will automatically update your\nIAM role with the necessary permissions to create, describe, and delete ENIs.\nIf you are managing the IAM role policy yourself, make sure to update your\npermissions accordingly, as described in the `AWS Lambda VPC documentation`_.\n\n\n``security_group_ids``\n~~~~~~~~~~~~~~~~~~~~~~\n\nA list of security groups for VPC configuration.  This value can be provided\nper stage as well as per Lambda function.  In order for this value to take\neffect, you must also provide the ``subnet_ids`` value.\n\n\n``tags``\n~~~~~~~~\n\nA mapping of key value pairs. These key value pairs will be set as the tags on\nthe resources running your deployed application. All tag keys and values must\nbe strings. Similar to ``environment_variables``, if a key is specified in both\na stage specific config option as well as a top level key, the stage specific\ntags will be merged into the top level keys. By default, all chalice deployed\nresources are tagged with the key ``'aws-chalice'`` whose value is\n``'version={chalice-version}:stage={stage-name}:app={app-name}'``.  Currently\nonly the following chalice deployed resources are tagged: Lambda functions.\n\n``xray``\n~~~~~~~~\n\nA boolean that turns on AWS XRay's Active tracing configuration.\nThis will turn on XRay for both Lambda functions and API Gateway stages.\n\n\n``log_retention_in_days``\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\nAn integer value that indicates the retention time to be applied to lambda\nfunction log groups. Only certain values are valid, see the `AWS CloudWatch\nLogs docs\n<https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutRetentionPolicy.html#API_PutRetentionPolicy_RequestParameters>`__\n\n.. warning::\n   If you using the `chalice package` command to generate a CloudFormation template,\n   a Log Group resource will be added to your template with the configured\n   ``log_retention_in_days``.  This will cause your deployment to fail\n   if this Log Group resource already exists (i.e. if the associated\n   Lambda function has previously been invoked which results in Lambda\n   automatically created a Log Group for the function).  In order to use\n   this configuration option, it should be part of the initial deployment\n   of the Lambda function.\n\n.. _lambda-config:\n\nLambda Specific Configuration\n-----------------------------\n\nIn addition to a chalice stage, there are also some configuration values\nthat can be specified per Lambda function.  A chalice app can have many\nstages, and a stage can have many Lambda functions.\n\nYou have the option to specify configuration for a lambda function across\nall your stages, or for a lambda function in a specific stage.\n\nTo configure per lambda configuration for a specific stage, you add a\n``lambda_functions`` key in your stage configuration::\n\n  {\n    \"version\": \"2.0\",\n    \"app_name\": \"app\",\n    \"stages\": {\n      \"dev\": {\n        \"lambda_functions\": {\n          \"foo\": {\n            \"lambda_timeout\": 120\n          }\n        }\n      }\n    }\n  }\n\nTo specify per lambda configuration across all stages, you add\na top level ``lambda_functions`` key::\n\n  {\n    \"version\": \"2.0\",\n    \"app_name\": \"app\",\n    \"lambda_functions\": {\n      \"foo\": {\n        \"lambda_timeout\": 120\n      }\n    }\n  }\n\n\nEach key in the ``lambda_functions`` dictionary is the name of a Lambda\nfunction in your app.  The value is a dictionary of configuration that\nwill be applied to that function.  These are the configuration options\nthat can be applied per function:\n\n* ``autogen_policy``\n* ``environment_variables``\n* ``iam_policy_file``\n* ``iam_role_arn``\n* ``lambda_memory_size``\n* ``lambda_timeout``\n* ``layers``\n* ``manage_iam_role``\n* ``reserved_concurrency``\n* ``security_group_ids``\n* ``subnet_ids``\n* ``tags``\n* ``log_retention_in_days``\n\n\nSee the :ref:`stage-config` section above for a description\nof these config options.\n\nIn general, the name of your lambda function will correspond to\nthe name of the function in your app.  For example:\n\n.. code-block:: python\n\n    @app.lambda_function()\n    def foo(event, context):\n        pass\n\nTo specify configuration for this function, you would use the\nkey of ``foo`` in the ``lambda_functions`` configuration.\n\nThere is one exception to this, which is any python function\ndecorated with the ``@app.route()`` decorator.  Chalice uses\na single Lambda function for all requests from API gateway,\nand this name is ``api_handler``.  So if you have an app\nlike this:\n\n.. code-block:: python\n\n    @app.route('/')\n    def index(): pass\n\n    @app.route('/foo/bar')\n    def other_handler(): pass\n\nThen to specify configuration values for the underlying\nlambda function, which ``index()`` and ``other_handler()`` share,\nyou would specify:\n\n.. code-block:: json\n\n   {\n      \"lambda_functions\": {\n        \"api_handler\": {\n          \"subnet_ids\": [\"sn-1\", \"sn-2\"],\n          \"security_group_ids\": [\"sg-10\", \"sg-11\"],\n          \"layers\": [\"layer-arn-1\", \"layer-arn-2\"],\n        }\n      }\n    }\n\n\nExamples\n--------\n\nBelow are examples that show how you can configure your chalice app.\n\nCustom Domain Name\n~~~~~~~~~~~~~~~~~~\n\nHere's an example for configuring Custom domain name for\ndev stage for REST API::\n\n  {\n    \"version\": \"2.0\",\n    \"app_name\": \"app\",\n    \"stages\": {\n      \"dev\": {\n        \"autogen_policy\": true,\n        \"api_gateway_stage\": \"dev\"\n        \"api_gateway_custom_domain\": {\n          \"domain_name\": \"api.example.com\",\n          \"security_policy\": \"TLS 1.2|TLS 1.0\",\n          \"certificate_arn\": \"arn:aws:acm:example.com\",\n          \"url_prefixes\": [\"foo\", \"bar\"],\n          \"tags\": {\n            \"key\": \"tag1\",\n            \"key1\": \"tag2\"\n          }\n        },\n      },\n    }\n  }\n\nIn this config file we're specifying ``dev`` stage for ApiGateway.\nIn the ``dev`` stage, chalice will automatically create ``custom domain name``\nwith specified ``url_prefixes`` that should contain information about\n`AWS Api Mapping key`_.\n\nIf there is Websocket API ``websocket_api_custom_domain`` should be used\ninstead of ``api_gateway_custom_domain``.\n\n.. _iam-role-pol-examples:\n\nIAM Roles and Policies\n~~~~~~~~~~~~~~~~~~~~~~\n\n\nHere's an example for configuring IAM policies across stages::\n\n  {\n    \"version\": \"2.0\",\n    \"app_name\": \"app\",\n    \"stages\": {\n      \"dev\": {\n        \"autogen_policy\": true,\n        \"api_gateway_stage\": \"dev\"\n      },\n      \"beta\": {\n        \"autogen_policy\": false,\n        \"iam_policy_file\": \"beta-app-policy.json\"\n      },\n      \"prod\": {\n        \"manage_iam_role\": false,\n        \"iam_role_arn\": \"arn:aws:iam::...:role/prod-role\"\n      }\n    }\n  }\n\nIn this config file we're specifying three stages, ``dev``, ``beta``,\nand ``prod``.  In the ``dev`` stage, chalice will automatically\ngenerate an IAM policy based on analyzing the application source code.\nFor the ``beta`` stage, chalice will load the\n``.chalice/beta-app-policy.json`` file and use it as the policy to\nassociate with the IAM role for that stage.  In the ``prod`` stage,\nchalice won't modify any IAM roles.  It will just set the IAM role\nfor the Lambda function to be ``arn:aws:iam::...:role/prod-role``.\n\nHere's an example that shows config precedence::\n\n\n  {\n    \"version\": \"2.0\",\n    \"app_name\": \"app\",\n    \"api_gateway_stage\": \"api\",\n    \"stages\": {\n      \"dev\": {\n      },\n      \"beta\": {\n      },\n      \"prod\": {\n        \"api_gateway_stage\": \"prod\",\n        \"manage_iam_role\": false,\n        \"iam_role_arn\": \"arn:aws:iam::...:role/prod-role\"\n      }\n    }\n  }\n\nIn this config file, both the ``dev`` and ``beta`` stage will\nhave an API gateway stage name of ``api`` because they will\ndefault to the top level ``api_gateway_stage`` key.\nHowever, the ``prod`` stage will have an API gateway stage\nname of ``prod`` because the ``api_gateway_stage`` is specified\nin ``{\"stages\": {\"prod\": ...}}`` mapping.\n\n\n\n.. _env-var-examples:\n\nEnvironment Variables\n~~~~~~~~~~~~~~~~~~~~~\n\n\nIn the following example, environment variables are specified\nboth as top level keys as well as per stage.  This allows us to\nprovide environment variables that all stages should have as well\nas stage specific environment variables::\n\n\n  {\n    \"version\": \"2.0\",\n    \"app_name\": \"app\",\n    \"environment_variables\": {\n      \"SHARED_CONFIG\": \"foo\",\n      \"OTHER_CONFIG\": \"from-top\"\n    },\n    \"stages\": {\n      \"dev\": {\n        \"environment_variables\": {\n          \"TABLE_NAME\": \"dev-table\",\n          \"OTHER_CONFIG\": \"dev-value\"\n        }\n      },\n      \"prod\": {\n        \"environment_variables\": {\n          \"TABLE_NAME\": \"prod-table\",\n          \"OTHER_CONFIG\": \"prod-value\"\n        }\n      }\n    }\n  }\n\nFor the above config, the ``dev`` stage will have the\nfollowing environment variables set::\n\n  {\n    \"SHARED_CONFIG\": \"foo\",\n    \"TABLE_NAME\": \"dev-table\",\n    \"OTHER_CONFIG\": \"dev-value\",\n  }\n\nThe ``prod`` stage will have these environment variables set::\n\n  {\n    \"SHARED_CONFIG\": \"foo\",\n    \"TABLE_NAME\": \"prod-table\",\n    \"OTHER_CONFIG\": \"prod-value\",\n  }\n\n\nPer Lambda Examples\n~~~~~~~~~~~~~~~~~~~\n\nSuppose we had the following chalice app:\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = Chalice(app_name='demo')\n\n    @app.lambda_function()\n    def foo(event, context):\n        pass\n\n    @app.lambda_function()\n    def bar(event, context):\n        pass\n\n\nGiven these two functions, we'd like to configure the functions\nas follows:\n\n* Both functions should have an environment variable ``OWNER`` with value\n  ``dev-team``.\n* The ``foo`` function should have an autogenerated IAM policy managed by\n  chalice.\n* The ``foo`` function should be run in a VPC with subnet ids ``sn-1`` and\n  ``sn-2``, with security groups ``sg-10`` and ``sg-11``.  Chalice should\n  also automatically configure the IAM policy with permissions to modify\n  EC2 network interfaces.\n* The ``foo`` function should have two connected layers as ``layer-arn-1`` and\n  ``layer-arn-2``. Chalice should automatically configure the IAM policy.\n* The ``bar`` function should use a pre-existing IAM role that was created\n  outside of chalice.  Chalice should not perform an IAM role management for\n  the ``bar`` function.\n* The ``bar`` function should have an environment variable ``TABLE_NAME`` with\n  value ``mytable``.\n\nWe can accomplish all this with this config file::\n\n  {\n    \"stages\": {\n      \"dev\": {\n        \"environment_variables\": {\n          \"OWNER\": \"dev-team\"\n        }\n        \"api_gateway_stage\": \"api\",\n        \"lambda_functions\": {\n          \"foo\": {\n            \"subnet_ids\": [\"sn-1\", \"sn-2\"],\n            \"security_group_ids\": [\"sg-10\", \"sg-11\"],\n            \"layers\": [\"layer-arn-1\", \"layer-arn-2\"],\n          },\n          \"bar\": {\n            \"manage_iam_role\": false,\n            \"iam_role_arn\": \"arn:aws:iam::my-role-name\",\n            \"environment_variables\": {\"TABLE_NAME\": \"mytable\"}\n          }\n        }\n      }\n    },\n    \"version\": \"2.0\",\n    \"app_name\": \"demo\"\n  }\n\n.. _AWS Lambda VPC documentation: https://docs.aws.amazon.com/lambda/latest/dg/vpc.html#vpc-configuring\n.. _AWS Documentation on managing concurrency: https://docs.aws.amazon.com/lambda/latest/dg/concurrent-executions.html\n.. _AWS Lambda Layers Configuration: https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html\n.. _AWS Custom Domain names setup: https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-custom-domains.html\n.. _AWS Api Mapping key:    https://docs.aws.amazon.com/apigatewayv2/latest/api-reference/domainnames-domainname-apimappings.html\n"
  },
  {
    "path": "docs/source/topics/domainname.rst",
    "content": "Custom Domain Names\n===================\n\nCustom domain names are simpler and more intuitive URLs\nthat you can provide to your API users.\nWith custom domain names, you can set up your API's hostname,\nand choose a base path to map the alternative URL to your API.\n\nYou must have an AWS managed certificate created or imported through\nAWS Certificate Manager (ACM) in order to configure a custom domain name\nfor REST and WebSocket APIs.\nSee `Get certificate in AWS Certificate Manager <https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-custom-domains-prerequisites.html>`__\nfor more information.\n\nCustom domain name can be configured per Chalice stage.\n\n.. note::\n    This document describes the configuration option and process\n    needed to configure a custom domain with your Chalice application.\n    If you'd like a step-by-step example that walks you through configuring\n    a custom domain for a Chalice app using Amazon Route53 and ACM, see\n    the :doc:`../tutorials/customdomain` tutorial.\n\nThere are two steps to configuring a custom domain name.  First you must\nconfigure your Chalice app such that it creates the necessary resources\nand configuration when provisioning your REST or WebSocket APIs.  This\nis explained in the next two sections below.\nThen you must configure your DNS configuration to point your custom domain\nname to the domain name created by API Gateway.  This is explained in the\n:ref:`dns-last-config` section below.\n\nConfigure custom domain name for REST API\n-----------------------------------------\n\nTo create custom domain name for REST API, add the\n``api_gateway_custom_domain`` configuration option to your\n``.chalice/config.json`` file.  You must specify the ``certificate_arn``,\nwhich is the ARN of your ACM certificate associated with your domain as\nwell as your ``domain_name``.  The remaining fields are optional and\nmay be omitted.  By default TLS 1.2 is used for your endpoint unless\notherwise specified.\n\nBelow is an example of all the configuration options you can specify\nwhen configuring a custom domain.  They are explained in the\n:ref:`custom-domain-config-options` section of the :doc:`configfile`\ndocumentation.\n\n.. code-block:: json\n\n    {\n        \"stages\": {\n            \"dev\": {\n                \"api_gateway_stage\": \"api\",\n                \"api_gateway_custom_domain\": {\n                    \"domain_name\": \"api.example.com\",\n                    \"tls_version\": \"TLS_1_2|TLS_1_0\",\n                    \"certificate_arn\": \"arn:aws:acm:example\",\n                    \"url_prefix\": \"foo\",\n                    \"tags\": {\n                        \"key\": \"tag1\",\n                        \"key1\": \"tag2\"\n                    }\n                }\n            }\n        }\n    }\n\nConfigure custom domain name for WebSocket\n------------------------------------------\n\nTo create custom domain name for WebSocket API, add the\n``websocket_api_custom_domain`` configuration option to your\n``.chalice/config.json`` file.\n\nBelow is an example of all the configuration options you can specify when\nconfiguring a custom domain for a WebSocket API.  They are explained in the\n:ref:`custom-domain-ws-config-options` section of the :doc:`configfile`\ndocumentation.\n\n.. code-block:: json\n\n    {\n        \"stages\": {\n            \"dev\": {\n                \"api_gateway_stage\": \"api\",\n                \"websocket_api_custom_domain\": {\n                    \"domain_name\": \"api.example.com\",\n                    \"tls_version\": \"TLS_1_2|TLS_1_0\",\n                    \"certificate_arn\": \"arn:aws:acm:example\",\n                    \"url_prefix\": \"foo\",\n                    \"tags\": {\n                        \"key\": \"tag1\",\n                        \"key1\": \"tag2\"\n                    }\n                }\n            }\n        }\n    }\n\n\n.. _dns-last-config:\n\nDNS Configuration\n-----------------\n\nChalice only configures your API Gateway API with the necessary resources\nand configuration so a custom domain can be used.  It does not alter any\nexisting DNS records you have associated with your domain name.  After you've\ndeployed your Chalice app with the configuration options described above,\nyou'll need to modify your DNS records to point to your API Gateway API\nusing the web interface or API of your domain registrar associated with\nyour domain name.  When you run ``chalice deploy`` with a custom domain\nconfigured, there will be two new fields in the output::\n\n    $ chalice deploy\n    Creating deployment package.\n    Updating policy for IAM role: customdomain-dev\n    Updating lambda function: customdomain-dev\n    Updating rest API\n    Creating custom domain name: api.chalice-demo-app.com\n    Creating api mapping: /\n    Resources deployed:\n      - Lambda ARN: arn:aws:lambda:us-west-2:0123456789:function:customdomain-dev\n      - Rest API URL: https://qxea58abcd.execute-api.us-west-2.amazonaws.com/api/\n      - Custom domain name:\n          HostedZoneId: Z1UJRXOUMOOFQ8\n          AliasDomainName: d-6vj4cynstd.execute-api.us-west-2.amazonaws.com\n\nIf you're using Route53 to manage your hosted zone, you'll need to create\nan Alias record using the ``HostedZoneId`` and ``AliasDomainName`` specified\nin the output of ``chalice deploy``.  If you're using a third party domain\nregistrar, you'll need to create a CNAME record to the ``AliasDomainName``.\nIf you'd like a step-by-step example of how to do this with Route53, see\nthe :doc:`../tutorials/customdomain` tutorial.\n"
  },
  {
    "path": "docs/source/topics/events.rst",
    "content": "====================\nLambda Event Sources\n====================\n\n\n.. _scheduled-events:\n\nScheduled Events\n================\n\nChalice has support for `scheduled events`_.  This feature allows you to\nperiodically invoke a lambda function based on some regular schedule.  You can\nspecify a fixed rate or a cron expression.\n\nTo create a scheduled event in chalice, you use the ``@app.schedule()``\ndecorator.  Let's look at an example.\n\n\n.. code-block:: python\n\n    app = chalice.Chalice(app_name='foo')\n\n    @app.schedule('rate(1 hour)')\n    def every_hour(event):\n        print(event.to_dict())\n\n\nIn this example, we have a single lambda function that we want automatically\ninvoked every hour.  When you run ``chalice deploy`` Chalice will create a\nlambda function as well as the necessary CloudWatch events/rules such that the\n``every_hour`` function is invoked every hour.\n\nThe :meth:`Chalice.schedule` method accepts either a string or an\ninstance of :class:`Rate` or :class:`Cron`.  For example:\n\n.. code-block:: python\n\n    app = chalice.Chalice(app_name='foo')\n\n    @app.schedule(Rate(1, unit=Rate.HOURS))\n    def every_hour(event):\n        print(event.to_dict())\n\n\nThe function you decorate must accept a single argument,\nwhich will be of type :class:`CloudWatchEvent`.\n\nYou can use the ``schedule()`` decorator multiple times\nin your chalice app.  Each ``schedule()`` decorator will\nresult in a new lambda function and associated CloudWatch\nevent rule.  For example:\n\n\n.. code-block:: python\n\n    app = chalice.Chalice(app_name='foo')\n\n    @app.schedule(Rate(1, unit=Rate.HOURS))\n    def every_hour(event):\n        print(event.to_dict())\n\n\n    @app.schedule(Rate(2, unit=Rate.HOURS))\n    def every_two_hours(event):\n        print(event.to_dict())\n\n\nIn the app above, chalice will create two lambda functions,\nand configure ``every_hour`` to be invoked once an hour,\nand ``every_two_hours`` to be invoked once every two hours.\n\n\n.. _cwe-events:\n\nCloudWatch Events\n==================\n\nYou can configure a lambda function to subscribe to\nany `CloudWatch Event <https://amzn.to/2SCgWA6>`__.\n\nTo subscribe to a CloudWatch Event in chalice, you use the\n``@app.on_cw_event()`` decorator.  Let's look at an example.\n\n\n.. code-block:: python\n\n    app = chalice.Chalice(app_name='foo')\n\n    @app.on_cw_event({\"source\": [\"aws.codecommit\"]})\n    def on_code_commit_changes(event):\n        print(event.to_dict())\n\nIn this example, we have a single lambda function that we subscribe to all\nevents from the AWS Code Commit service. The first parameter to the decorator\nis the event pattern that will be used to filter the events sent to the function.\n\nSee the `CloudWatch Event pattern docs <https://amzn.to/2OlqZso>`__\nfor additional syntax and examples.\n\nThe function you decorate must accept a single argument,\nwhich will be of type :class:`CloudWatchEvent`.\n\n.. _s3-events:\n\nS3 Events\n=========\n\nYou can configure a lambda function to be invoked whenever\ncertain events happen in an S3 bucket.  This uses the\n`event notifications`_ feature provided by Amazon S3.\n\nTo configure this, you just tell Chalice the name of an existing\nS3 bucket, along with what events should trigger the lambda function.\nThis is done with the :meth:`Chalice.on_s3_event` decorator.\n\nHere's an example:\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = chalice.Chalice(app_name='s3eventdemo')\n    app.debug = True\n\n    @app.on_s3_event(bucket='mybucket-name',\n                     events=['s3:ObjectCreated:*'])\n    def handle_s3_event(event):\n        app.log.debug(\"Received event for bucket: %s, key: %s\",\n                      event.bucket, event.key)\n\nIn this example above, Chalice connects the S3 bucket to the\n``handle_s3_event`` Lambda function such that whenever an object is uploaded\nto the ``mybucket-name`` bucket, the Lambda function will be invoked.\nThis example also uses the ``.bucket`` and ``.key`` attributes from the\n``event`` parameter, which is of type :class:`S3Event`.\n\nIt will automatically create the appropriate S3 notification configuration\nas needed.  Chalice will also leave any existing notification configuration\non the ``mybucket-name`` untouched.  It will only merge in the additional\nconfiguration needed for the ``handle_s3_event`` Lambda function.\n\n\n.. warning::\n\n  This feature only works when using `chalice deploy`.  Because you\n  configure the lambda function with the name of an existing S3 bucket,\n  it is not possible to describe this using a CloudFormation/SAM template.\n  The ``chalice package`` command will fail.  You will eventually be able\n  to request that chalice create a bucket for you, which will support\n  the ``chalice package`` command.\n\nThe function you decorate must accept a single argument,\nwhich will be of type :class:`S3Event`.\n\n.. _sns-events:\n\nSNS Events\n==========\n\nYou can configure a lambda function to be automatically invoked whenever\nsomething publishes to an SNS topic.  Chalice will automatically handle\ncreating the lambda function, subscribing the lambda function to the\nSNS topic, and modifying the lambda function policy to allow SNS to invoke\nthe function.\n\nTo configure this, you just need the name of an existing SNS topic you'd\nlike to subscribe to.  The SNS topic must already exist.\n\nBelow is an example of how to set this up.  The example uses boto3 to\ncreate the SNS topic.  If you don't have boto3 installed in your virtual\nenvironment, be sure to install it with::\n\n    $ pip install boto3\n\nFirst, we'll create an SNS topic using boto3.\n\n::\n\n    $ python\n    >>> import boto3\n    >>> sns = boto3.client('sns')\n    >>> sns.create_topic(Name='my-demo-topic')\n    {'TopicArn': 'arn:aws:sns:us-west-2:12345:my-demo-topic',\n     'ResponseMetadata': {}}\n\nNext, we'll create our chalice app::\n\n    $ chalice new-project chalice-demo-sns\n    $ cd chalice-demo-sns/\n\nWe'll update the ``app.py`` file to use the ``on_sns_message`` decorator:\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = Chalice(app_name='chalice-sns-demo')\n    app.debug = True\n\n    @app.on_sns_message(topic='my-demo-topic')\n    def handle_sns_message(event):\n        app.log.debug(\"Received message with subject: %s, message: %s\",\n                      event.subject, event.message)\n\nWe can now deploy our chalice app::\n\n    $ chalice deploy\n    Creating deployment package.\n    Creating IAM role: chalice-demo-sns-dev\n    Creating lambda function: chalice-demo-sns-dev-handle_sns_message\n    Subscribing chalice-demo-sns-dev-handle_sns_message to SNS topic my-demo-topic\n    Resources deployed:\n      - Lambda ARN: arn:aws:lambda:us-west-2:123:function:...\n\nAnd now we can test our app by publishing a few SNS messages to our topic.\nWe'll do this using boto3.  In the example below, we're using ``list_topics()``\nto find the ARN associated with our topic name before calling the ``publish()``\nmethod.\n\n::\n\n    $ python\n    >>> import boto3\n    >>> sns = boto3.client('sns')\n    >>> topic_arn = [t['TopicArn'] for t in sns.list_topics()['Topics']\n    ...              if t['TopicArn'].endswith(':my-demo-topic')][0]\n    >>> sns.publish(Message='TestMessage1', Subject='TestSubject1',\n    ...             TopicArn=topic_arn)\n    {'MessageId': '12345', 'ResponseMetadata': {}}\n    >>> sns.publish(Message='TestMessage2', Subject='TestSubject2',\n    ...             TopicArn=topic_arn)\n    {'MessageId': '54321', 'ResponseMetadata': {}}\n\nTo verify our function was called correctly, we can use the ``chalice logs``\ncommand::\n\n    $ chalice logs -n handle_sns_message\n    2018-06-28 17:49:30.513000 547e0f chalice-demo-sns - DEBUG - Received message with subject: TestSubject1, message: TestMessage1\n    2018-06-28 17:49:40.391000 547e0f chalice-demo-sns - DEBUG - Received message with subject: TestSubject2, message: TestMessage2\n\nIn this example we used the SNS topic name to register our handler, but you can\nalso use the topic arn. This can be useful if your topic is in another region\nor account.\n\n\n.. _sqs-events:\n\nSQS Events\n==========\n\nYou can configure a lambda function to be invoked whenever messages are\navailable on an SQS queue.  To configure this, use the\n:meth:`Chalice.on_sqs_message` decorator and provide the name of the SQS queue\nand an optional batch size.\n\nThe message visibility timeout of your SQS queue must be greater than or\nequal to the lambda timeout.  The default message visibility timeout\nwhen you create an SQS queue is 30 seconds, and the default timeout\nfor a Lambda function is 60 seconds, so you'll need to modify one of these\nvalues in order to successfully connect an SQS queue to a Lambda function.\n\nYou can check the visibility timeout of your queue using the\n``GetQueueAttributes`` API call.  Using the\n`AWS CLI <https://docs.aws.amazon.com/cli/latest/reference/sqs/get-queue-attributes.html>`__,\nyou can run this command to check the value::\n\n  $ aws sqs get-queue-attributes \\\n      --queue-url https://us-west-2.queue.amazonaws.com/1/testq \\\n      --attribute-names VisibilityTimeout\n  {\n      \"Attributes\": {\n          \"VisibilityTimeout\": \"30\"\n      }\n  }\n\nYou can set the visibility timeout of your SQS queue using the\n``SetQueueAttributes`` API call.  Again using the AWS CLI you can\nrun this command::\n\n  $ aws sqs set-queue-attributes \\\n      --queue-url https://us-west-2.queue.amazonaws.com/1/testq \\\n      --attributes VisibilityTimeout=60\n\nIf you would prefer to change the timeout of your lambda function instead,\nyou can specify this timeout value using the ``lambda_timeout`` config key\nif your ``.chalice/config.json`` file.\nSee :ref:`lambda-config` for a list of all supported lambda configuration\nvalues in chalice.  In this example below, we're setting the timeout\nof our ``handle_sqs_message`` lambda function to 30 seconds::\n\n  $ cat .chalice/config.json\n  {\n    \"stages\": {\n      \"dev\": {\n        \"lambda_functions\": {\n          \"handle_sqs_message\": {\n            \"lambda_timeout\": 30\n          }\n        }\n      }\n    },\n    \"version\": \"2.0\",\n    \"app_name\": \"chalice-sqs-demo\"\n  }\n\n\nIn this example below, we're connecting the ``handle_sqs_message`` lambda\nfunction to the ``my-queue`` SQS queue.  Note that we are specifying the\nqueue name, not the queue URL or queue ARN.  If you are connecting your\nlambda function to a FIFO queue, make sure you specify the ``.fifo``\nsuffix, e.g. ``my-queue.fifo``.\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = chalice.Chalice(app_name='chalice-sqs-demo')\n    app.debug = True\n\n    @app.on_sqs_message(queue='my-queue', batch_size=1)\n    def handle_sqs_message(event):\n        for record in event:\n            app.log.debug(\"Received message with contents: %s\", record.body)\n\n\nWhenever a message is sent to the SQS queue our function will be automatically\ninvoked.  The function argument is an :class:`SQSEvent` object, and each\n``record`` in the example above is of type :class:`SQSRecord`.  Lambda takes\ncare of automatically scaling your function as needed.  See `Understanding\nScaling Behavior`_ for more information on how Lambda scaling works.\n\nIf your lambda function completes without raising an exception, then\nLambda will automatically delete all the messages associated with the\n:class:`SQSEvent`.  You don't need to manually call ``sqs.delete_message()``\nin your lambda function.  If your lambda function raises an exception, then\nLambda won't delete any messages, and once the visibility timeout has been\nreached, the messages will be available again in the SQS queue.  Note that\nif you are using a batch size of more than one, the entire batch succeeds or\nfails.  This means that it is possible for your lambda function to see\na message multiple times, even if it's successfully processed the message\npreviously.  There are a few options available to mitigate this:\n\n* Use a batch size of 1 (the default value).\n* Use a separate data store to check if you've already processed an SQS\n  message.  You can use services such as Amazon DynamoDB or Amazon ElastiCache.\n* Manually call ``sqs.delete_message()`` in your Lambda function once you've\n  successfully processed a message.\n\nFor more information on Lambda and SQS,\nsee the `AWS documentation`_.\n\n.. _kinesis-events:\n\nKinesis Events\n==============\n\nYou can configure a Lambda function to be invoked whenever messages are\npublished to an Amazon Kinesis data stream.  To configure this, use the\n:meth:`Chalice.on_kinesis_record` decorator and provide the name of the\nKinesis stream.\n\nThe :class:`KinesisEvent` that is passed in as the ``event`` argument\nto the event handler is also iterable.  This allows you to iterate over\nall the records in the event.  Additionally, each record has a ``.data``\nattribute that is automatically base64 decoded for you.\n\nHere's an example:\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = chalice.Chalice(app_name='kinesiseventdemo')\n    app.debug = True\n\n    @app.on_kinesis_record(stream='mystream')\n    def handle_kinesis_message(event):\n        for record in event:\n            # The .data attribute is automatically base64 decoded for you.\n            app.log.debug(\"Received message with contents: %s\", record.data)\n\nFor more information on using Kinesis and Lambda, see\n`Using AWS Lambda with Amazon Kinesis <https://docs.aws.amazon.com/lambda/latest/dg/with-kinesis.html>`__.\n\n.. _dynamodb-events:\n\nDynamoDB Events\n===============\n\nYou can configure a Lambda function to be invoked whenever messages are\npublished to an Amazon DynamoDB stream.  To configure this, use the\n:meth:`Chalice.on_dynamodb_record` decorator and provide the name of the\nDynamoDB stream ARN.\n\n.. note::\n   Other event handlers such as :meth:`Chalice.on_kinesis_record`,\n   :meth:`Chalice.on_sqs_message`, and :meth:`Chalice.on_sns_message`\n   only require the resource name and not the full ARN.  In the case\n   of DynamoDB streams, there are auto-generated portions of the\n   stream ARN that cannot be computed based on the resource name.  This\n   is why Chalice requires that full stream ARN when configuring\n   a DynamoDB stream handler.\n\nThe :class:`DynamoDBEvent` that is passed in as the ``event`` argument\nto the event handler is also iterable.  This allows you to iterate over\nall the records in the event.\n\nHere's an example:\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = chalice.Chalice(app_name='ddb-event-demo')\n    app.debug = True\n\n    @app.on_dynamodb_record(stream_arn='arn:aws:dynamodb:.../stream/2020')\n    def handle_ddb_message(event):\n        for record in event:\n            app.log.debug(\"New: %s\", record.new_image)\n\n\nFor more information on using Lambda and DynamoDB, see\n`Using AWS Lambda with Amazon DynamoDB <https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html>`__.\n\n\n.. _event notifications: https://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html\n.. _AWS documentation: https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html\n.. _Understanding Scaling Behavior: https://docs.aws.amazon.com/lambda/latest/dg/scaling.html\n"
  },
  {
    "path": "docs/source/topics/experimental.rst",
    "content": "Experimental APIs\n=================\n\nChalice maintains backwards compatibility for all features that appear in this\ndocumentation.  Any Chalice application using version 1.x will continue to work\nfor all future versions of 1.x.\n\nWe also believe that Chalice has a lot of potential for new ideas and APIs,\nmany of which will take several iterations to get right.  We may implement a\nnew idea and need to make changes based on customer usage and feedback.  This\nmay include backwards incompatible changes all the way up to the removal of\na feature.\n\nTo accommodate these new features, Chalice has support for experimental APIs,\nwhich are features that are added to Chalice on a provisional basis.  Because\nthese features may include backwards incompatible changes, you must explicitly\nopt-in to using these features.  This makes it clear that you are using an\nexperimental feature that may change.\n\nOpting-in to Experimental APIs\n------------------------------\n\nEach experimental feature in chalice has a name associated with it.  To opt-in\nto an experimental API, you must have the feature name to the\n``experimental_feature_flags`` attribute on your ``app`` object.\nThis attribute's type is a set of strings.\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = Chalice('myapp')\n    app.experimental_feature_flags.update([\n        'MYFEATURE1',\n        'MYFEATURE2',\n    ])\n\n\nIf you use an experimental API without opting-in, you will receive\na message whenever you run a Chalice CLI command.  The error message\ntells you which feature flags you need to add::\n\n    $ chalice deploy\n    You are using experimental features without explicitly opting in.\n    Experimental features do not guarantee backwards compatibility and may be removed in the future.\n    If you still like to use these experimental features, you can opt-in by adding this to your app.py file:\n\n    app.experimental_feature_flags.update([\n        'FEATURE_FLAG_NAME'\n    ])\n\n\n    See https://aws.github.io/chalice/topics/experimental.rst for more details.\n\nThe feature flag only happens when running CLI commands.  There are no runtime\nchecks for experimental features once your application is deployed.\n\n\nList of Experimental APIs\n-------------------------\n\nIn the table below, the \"Feature Flag Name\" column is the value you\nmust add to the ``app.experimental_feature_flags`` attribute.\nThe status of an experimental API can be:\n\n* ``Trial`` - You must explicitly opt-in to use this feature.\n* ``Accepted`` - This feature has graduated from an experimental\n  feature to a fully supported, backwards compatible feature in Chalice.\n  Accepted features still appear in the table for auditing purposes.\n* ``Rejected`` - This feature has been removed.\n\n\n.. list-table:: Experimental APIs\n  :header-rows: 1\n\n  * - Feature\n    - Feature Flag Name\n    - Version Added\n    - Version Finalized\n    - Status\n    - GitHub Issue(s)\n  * - :doc:`blueprints`\n    - ``BLUEPRINTS``\n    - 1.7.0\n    - 1.15.0\n    - Accepted\n    - `#1023 <https://github.com/aws/chalice/pull/1023>`__,\n      `#651 <https://github.com/aws/chalice/pull/651>`__\n  * - :doc:`websockets`\n    - ``WEBSOCKETS``\n    - 1.10.0\n    - n/a\n    - Trial\n    - `#1041 <https://github.com/aws/chalice/pull/1041>`__,\n      `#1017 <https://github.com/aws/chalice/issues/1017>`__\n\n\nSee the `original discussion <https://github.com/aws/chalice/issues/1019>`__\nfor more background information and alternative proposals.\n"
  },
  {
    "path": "docs/source/topics/index.rst",
    "content": "Topics\n======\n\n.. toctree::\n   :maxdepth: 2\n\n   routing\n   views\n   configfile\n   multifile\n   logging\n   sdks\n   stages\n   packaging\n   pyversion\n   cfn\n   tf\n   authorizers\n   events\n   purelambda\n   blueprints\n   websockets\n   cd\n   domainname\n   experimental\n   testing\n   middleware\n"
  },
  {
    "path": "docs/source/topics/logging.rst",
    "content": "Logging\n=======\n\nYou have several options for logging in your\napplication.  You can use any of the options\navailable to lambda functions as outlined\nin the\n`AWS Lambda Docs <https://docs.aws.amazon.com/lambda/latest/dg/python-logging.html>`_.\nThe simplest option is to just use print statements.\nAnything you print will be accessible in cloudwatch logs\nas well as in the output of the ``chalice logs`` command.\n\nIn addition to using the stdlib ``logging`` module directly,\nthe framework offers a preconfigured logger designed to work\nnicely with Lambda.  This is offered purely as a convenience,\nyou can use ``print`` or the ``logging`` module directly if you prefer.\n\nYou can access this logger via the ``app.log``\nattribute, which is a logger specifically for your application.\nThis attribute is an instance of ``logging.getLogger(your_app_name_)``\nthat's been preconfigured with reasonable defaults:\n\n* StreamHandler associated with ``sys.stdout``.\n* Log level set to ``logging.ERROR`` by default.\n  You can also manually set the logging level by setting\n  ``app.log.setLevel(logging.DEBUG)``.\n* A logging formatter that displays the app name, level name,\n  and message.\n\n\nExamples\n--------\n\nIn the following application, we're using the application logger\nto emit two log messages, one at ``DEBUG`` and one at the ``ERROR``\nlevel:\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = Chalice(app_name='demolog')\n\n\n    @app.route('/')\n    def index():\n        app.log.debug(\"This is a debug statement\")\n        app.log.error(\"This is an error statement\")\n        return {'hello': 'world'}\n\n\nIf we make a request to this endpoint, and then look at\n``chalice logs`` we'll see the following log message::\n\n    2016-11-06 20:24:25.490000 9d2a92 demolog - ERROR - This is an error statement\n\nAs you can see, only the ``ERROR`` level log is emitted because\nthe default log level is ``ERROR``.  Also note the log message formatting.\nThis is the default format that's been automatically configured.\nWe can make a change to set our log level to debug:\n\n\n.. code-block:: python\n\n    import logging\n\n    from chalice import Chalice\n\n    app = Chalice(app_name='demolog')\n    # Enable DEBUG logs.\n    app.log.setLevel(logging.DEBUG)\n\n\n    @app.route('/')\n    def index():\n        app.log.debug(\"This is a debug statement\")\n        app.log.error(\"This is an error statement\")\n        return {'hello': 'world'}\n\nNow if we make a request to the ``/`` URL and look at the\noutput of ``chalice logs``, we'll see the following log message::\n\n    2016-11-07 12:29:15.714 431786 demolog - DEBUG - This is a debug statement\n    2016-11-07 12:29:15.714 431786 demolog - ERROR - This is an error statement\n\n\nAs you can see here, both the debug and error log message are shown.\n\nYou can use the ``-n/--name`` option to view the logs for a specific lambda\nfunction.  By default, the logs for the API handler lambda function are shown.\nThis corresponds to any log statements made within an ``@app.route()`` call.\nThe name option is the logical name of the lambda function.  This is the\nname of the python function by default, or whatever name you provided\nas the ``name`` kwarg to the ``@app.lambda_function()`` call.  For example,\ngiven this app:\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = Chalice(app_name='multilog')\n\n\n    @app.lambda_function()\n    def foo(event, context):\n        app.log.debug(\"Invoking from function foo\")\n        return {'hello': 'world'}\n\n\n    @app.lambda_function(name='MyFunction)\n    def bar(event, context):\n        incr_counter()\n        app.log.debug(\"Invoking from function bar\")\n        return {'hello': 'world'}\n\n\nYou can retrieve logs for the above function by running::\n\n    $ chalice logs --name foo\n    $ chalice logs --name MyFunction\n"
  },
  {
    "path": "docs/source/topics/middleware.rst",
    "content": "==========\nMiddleware\n==========\n\nChalice provides numerous features and capabilities right out of the box, but\nthere are often times where you'll want to customize the behavior of Chalice\nfor your specific needs.  You can accomplish this by using middleware, which\nlets you alter the request and response lifecycle.  Chalice middleware\nis a function that you register as part of your application that will\nautomatically be invoked by Chalice whenever your Lambda functions are called.\n\nBelow is an example of Chalice middleware:\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = Chalice(app_name='demo-middleware')\n\n    @app.middleware('all')\n    def my_middleware(event, get_response):\n        app.log.info(\"Before calling my main Lambda function.\")\n        response = get_response(event)\n        app.log.info(\"After calling my main Lambda function.\")\n        return response\n\n    @app.route('/')\n    def index():\n        return {'hello': 'world'}\n\n    @app.on_sns_message('mytopic')\n    def sns_handler(event):\n        pass\n\nIn this example, our middleware is emitting a log message before and after\nour Lambda function has been invoked.  Because we specified an event type of\n``all``, the ``my_middleware`` function will be called when either our REST\nAPI's ``index()`` or our ``sns_handler()`` Lambda function is invoked.\n\n\nWriting Middleware\n==================\n\nMiddleware must adhere to these requirements:\n\n* Must be a callable object that accepts two parameters, an ``event``, and\n  a ``get_response`` function.  The ``event`` type will depend on what type\n  of handlers the middleware has been registered for (see \"Registering\n  Middleware\" below).\n* Must return a response.  This will be the response that gets returned back\n  to the caller.\n* In order to invoke the next middleware in the chain and eventually call the\n  actual Lambda handler, it must invoke ``get_response(event)``.\n* Middleware can short-circuit the request by returning its own response.\n  It does not have to invoke ``get_response(event)`` if not needed.  The\n  response type should match the response type of the underlying Lambda\n  handler.\n\nBelow is the simplest middleware in Chalice that does nothing:\n\n.. code-block:: python\n\n   @app.middleware('all')\n   def noop_middleware(event, get_response):\n       # The `event` type will depend on what type of\n       # Lambda handler is being invoked.\n       return get_response(event)\n\n\nError Handling\n--------------\n\nWith the exception of middleware for REST APIs, all middleware follow the same\nerror handling strategy.  Any exceptions from a Lambda handler are propagated\nback to each middleware.  You can then catch these exceptions in your\nmiddleware and process them as needed.  For example:\n\n.. code-block:: python\n\n   @app.middleware('all')\n   def handle_errors(event, get_response):\n       try:\n           return get_response(event)\n       except MyCustomError as e:\n           # We don't want MyCustomError to propagate, instead\n           # we'll convert this to an error response dictionary.\n           return {\"Error\": e.__class__.__name__,\n                   \"Message\": str(e)}\n\n   @app.lambda_function()\n   def noop_middleware(event, context):\n       raise MyCustomError(\"Raising an error.\")\n\n\nIf an exception is raised in a Lambda handler and no middleware catches the\nexception, the exception will be returned back to the client that invoked\nthe Lambda function.\n\nRest APIs\n~~~~~~~~~\n\nRest APIs have special error processing for backwards compatibility purposes.\nIf a chalice view function (decorated via ``@app.route``) raises an exception\nChalice will automatically catch this exception and convert to a ``Response``\nobject with an appropriately set status code (see :ref:`view-error-handling`).\nAs a result, middleware for Rest APIs won't see exceptions propagate, they will\ninstead see a `Response` object as a result of calling ``get_response(event)``.\n\nIn the case where you want to allow an exception to propagate out of a view\nfunction, you can raise a ``chalice.ChaliceUnhandledError`` exception.\nFor example:\n\n.. code-block:: python\n\n   from chalice import ChaliceUnhandledError\n\n   @app.middleware('all')\n   def handle_errors(event, get_response):\n       try:\n           return get_response(event)\n       except ChaliceUnhandledError as e:\n           return Response(status_code=500, body=str(e),\n                           headers={'Content-Type': 'text/plain'})\n\n   @app.route('/')\n   def index():\n       # The handle_errors middleware will never see this exception.\n       # This will automatically be converted to a ``Response`` object\n       # with a status code of ``500``.\n       raise MyCustomError(\"Raising an error.\")\n\n   @app.route('/error')\n   def unhandled_error():\n       # The handle_errors middleware will see this exception because it's\n       # of type ChaliceUnhandledError.\n       raise ChaliceUnhandledError(\"Raising an error.\")\n\n\nThis is useful if you want to have middleware that applies to all event types\nthat has consistent error handling behavior.  If a\n``chalice.ChaliceUnhandledError`` error is raised and no middleware catches\nand processes this error, then the standard error processing behavior will\napply (a 500 response is returned back to the user, and if debug mode\nis enabled, the traceback is sent as the response body).\n\n\nRegistering Middleware\n----------------------\n\nIn order to register middleware, you use the ``@app.middleware()`` decorator.\nThis function accepts a single arg that specifies what type of Lambda function\nit wants to be registered for.  This allows you to apply middleware to only\nspecific type of event handlers, e.g. only for REST APIs, or Websockets, or\nS3 event handlers.  To register middleware for all Lambda functions, you can\nspecify ``all``.  Below are the supported event types along with the\ncorresponding type of event that will be provided to the middleware:\n\n* ``all`` - ``Any``\n* ``s3`` - :class:`S3Event`\n* ``sns`` - :class:`SNSEvent`\n* ``sqs`` - :class:`SQSEvent`\n* ``cloudwatch`` - :class:`CloudWatchEvent`\n* ``scheduled`` - :class:`CloudWatchEvent`\n* ``websocket`` - :class:`WebsocketEvent`\n* ``http`` - :class:`Request`\n* ``pure_lambda`` - :class:`LambdaFunctionEvent`\n\n.. note::\n   The ``chalice.LambdaFunctionEvent`` is the only case where the\n   event type for the middleware does not match the event type of the\n   corresponding Lambda handler.  For backwards compatibility reasons,\n   the existing signature of the ``@app.lambda_function()`` decorator\n   is preserved (it accepts an ``event`` and ``context``) whereas for\n   middleware, a consistent signature is needed, which is why the\n   ``chalice.LambdaFunctionEvent`` is used.\n\nYou can also use the :meth:`Chalice.register_middleware` method, which\nhas the same behavior as :meth:`Chalice.middleware` except you provide\nthe middleware function as an argument instead of decorating a function.\nThis is useful when you want to import third party functions and use\nthem as middleware.\n\n.. code-block:: python\n\n    import thirdparty\n\n    app.register_middleware(thirdparty.func, 'all')\n\nYou can also use the :class:`ConvertToMiddleware` class to convert an\nexisting Lambda wrapper to middleware.  For example, if you had the\nfollowing logging decorator:\n\n.. code-block:: python\n\n    def log_invocation(func):\n        def wrapper(event, context):\n            logger.debug(\"Before lambda function.\")\n            response = func(event, context)\n            logger.debug(\"After lambda function.\")\n        return wrapper\n\n    @app.lambda_function()\n    @log_invocation\n    def myfunction(event, context):\n        logger.debug(\"In myfunction().\")\n\n\nRather than decorate every Lambda function with the ``@log_invocation``\ndecorator, you can instead use ``ConvertToMiddleware`` to automatically\napply this wrapper to every Lambda function in your app.\n\n.. code-block:: python\n\n    from chalice import ConvertToMiddleware\n\n    app.register_middleware(ConvertToMiddleware(log_invoation))\n\nThis is also useful to integrate with existing libraries that provide\nLambda wrappers.  See :ref:`powertools-example` for a more complete\nexample.\n\nExamples\n========\n\nBelow are some examples of common middleware patterns.\n\nShort Circuiting a Request\n--------------------------\n\nIn this example, we want to return a 400 bad response if a specific\nheader is missing from a request.  Because this is HTTP specific, we only\nwant to register this handler for our ``http`` event type.\n\n.. code-block:: python\n\n   from chalice import Response\n\n   @app.middleware('http')\n   def require_header(event, get_response):\n       # From the list above, because this is an ``http`` event\n       # type, we know that event will be of type ``chalice.Request``.\n       if 'X-Custom-Header' not in event.headers:\n           return Response(\n               status_code=400,\n               body={\"Error\": \"Missing required 'X-Custom-Header'\"})\n       # If the header exists then we'll defer to our normal request flow.\n       return get_response(event)\n\nModifying a Response\n--------------------\n\nIn this example, we want to measure the processing time and inject it as\na key in our Lambda response.\n\n.. code-block:: python\n\n   import time\n\n   @app.middleware('pure_lambda')\n   def inject_time(event, get_response):\n       start = time.time()\n       response = get_response(event)\n       total = time.time() - start\n       response.setdefault('metadata', {})['duration'] = total\n       return response\n\n\n.. _powertools-example:\n\nIntegrating with AWS Lambda Powertools\n--------------------------------------\n\n`AWS Lambda Powertools\n<https://docs.powertools.aws.dev/lambda/python/latest/>`__ is a suite of\nutilities for AWS Lambda functions that makes tracing with AWS X-Ray,\nstructured logging and creating custom metrics asynchronously easier.\n\nYou can use Chalice middleware to easily integrate Lambda Powertools with\nyour Chalice apps.  In this example, we'll use the\n`Logger\n<https://docs.powertools.aws.dev/lambda/python/latest/core/logger/>`__\nand `Tracer <https://docs.powertools.aws.dev/lambda/python/latest/core/tracer/>`__\nand convert them to Chalice middleware so they will be automatically applied\nto all Lambda functions in our application.\n\n\n.. code-block:: python\n\n    from chalice import Chalice\n    from chalice.app import ConvertToMiddleware\n\n    # First, instead of using Chalice's built in logger, we'll instead use\n    # the structured logger from powertools.  In addition to automatically\n    # injecting lambda context, let's say we also want to inject which\n    # route is being invoked.\n    from aws_lambda_powertools import Logger\n    from aws_lambda_powertools import Tracer\n\n    app = Chalice(app_name='chalice-powertools')\n\n\n    logger = Logger(service=app.app_name)\n    tracer = Tracer(service=app.app_name)\n    # This will automatically convert any decorator on a lambda function\n    # into middleware that will be connected to every lambda function\n    # in our app.  This lets us avoid decoratoring every lambda function\n    # with this behavior, but it also works in cases where we don't control\n    # the code (e.g. registering blueprints).\n    app.register_middleware(ConvertToMiddleware(logger.inject_lambda_context))\n    app.register_middleware(\n        ConvertToMiddleware(\n            tracer.capture_lambda_handler(capture_response=False))\n    )\n\n    # Here we're writing Chalice specific middleware where for any HTTP\n    # APIs, we want to add the request path to our structured log message.\n    # This shows how we can combine both Chalice-style middleware with\n    # other existing tools.\n    @app.middleware('http')\n    def inject_route_info(event, get_response):\n        logger.structure_logs(append=True, request_path=event.path)\n        return get_response(event)\n\n\n    @app.route('/')\n    def index():\n        logger.info(\"In index() function, this will have a 'path' key.\")\n        return {'hello': 'world'}\n\n    @app.route('/foo/bar')\n    def foobar():\n        logger.info(\"In foobar() function\")\n        return {'foo': 'bar'}\n\n\n    @app.lambda_function()\n    def myfunction(event, context):\n        logger.info(\"In myfunction().\")\n        tracer.put_annotation(key=\"Status\", value=\"SUCCESS\")\n        return {}\n\n\nFor a more detailed walkthrough of configuring Chalice with Lambda Powertools,\nsee\n`Following serverless best practices with AWS Chalice and Lambda Powertools <https://aws.amazon.com/blogs/developer/following-serverless-best-practices-with-aws-chalice-and-lambda-powertools/>`__.\n"
  },
  {
    "path": "docs/source/topics/multifile.rst",
    "content": "Multifile Support\n=================\n\nThe ``app.py`` file contains all of your view functions and route\ninformation, but you don't have to keep all of your application\ncode in your ``app.py`` file.\n\nAs your application grows, you may reach out a point where you'd\nprefer to structure your application in multiple files.\nYou can create a ``chalicelib/`` directory, and anything\nin that directory is recursively included in the deployment\npackage.  This means that you can have files besides just\n``.py`` files in ``chalicelib/``, including ``.json`` files\nfor config, or any kind of binary assets.\n\nLet's take a look at a few examples.\n\nConsider the following app directory structure layout::\n\n    .\n    ├── app.py\n    ├── chalicelib\n    │   └── __init__.py\n    └── requirements.txt\n\nWhere ``chalicelib/__init__.py`` contains:\n\n.. code-block:: python\n\n    MESSAGE = 'world'\n\n\nand the ``app.py`` file contains:\n\n.. code-block:: python\n    :linenos:\n    :emphasize-lines: 2\n\n    from chalice import Chalice\n    from chalicelib import MESSAGE\n\n    app = Chalice(app_name=\"multifile\")\n\n    @app.route(\"/\")\n    def index():\n        return {\"hello\": MESSAGE}\n\n\nNote in line 2 we're importing the ``MESSAGE`` variable from\nthe ``chalicelib`` package, which is a top level directory\nin our project.  We've created a ``chalicelib/__init__.py``\nfile which turns the ``chalicelib`` directory into a python\npackage.\n\nWe can also use this directory to store config data.   Consider\nthis app structure layout::\n\n\n    .\n    ├── app.py\n    ├── chalicelib\n    │   └── config.json\n    └── requirements.txt\n\n\nWith ``chalicelib/config.json`` containing::\n\n    {\"message\": \"world\"}\n\n\nIn our ``app.py`` code, we can load and use our config file:\n\n.. code-block:: python\n    :linenos:\n\n    import os\n    import json\n\n    from chalice import Chalice\n\n    app = Chalice(app_name=\"multifile\")\n\n    filename = os.path.join(\n        os.path.dirname(__file__), 'chalicelib', 'config.json')\n    with open(filename) as f:\n        config = json.load(f)\n\n    @app.route(\"/\")\n    def index():\n        # We can access ``config`` here if we want.\n        return {\"hello\": config['message']}\n"
  },
  {
    "path": "docs/source/topics/packaging.rst",
    "content": "App Packaging\n=============\n\nIn order to deploy your Chalice app, a zip file is created that\ncontains your application and all third party packages your application\nrequires.  This file is used by AWS Lambda and is referred\nto as a deployment package.\n\nChalice will automatically create this deployment package for you, and offers\nseveral features to make this easier to manage.  Chalice allows you to\nclearly separate application specific modules and packages you are writing\nfrom 3rd party package dependencies.\n\nBy default, Chalice will create a single zip file containing everything\nnecessary to deploy your application to Lambda.  Chalice also has\nthe ability to split your code into multiple files to leverage\n`AWS Lambda layers <https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html>`__.\nThis is discussed in the :ref:`package-3rd-party` section below.\n\nApp Directories\n---------------\n\nYou have two options to structure application specific code/config:\n\n* **app.py** - This file includes all your route information and is always\n  included in the deployment package.\n* **chalicelib/** - This directory (if it exists) is included in the\n  deployment package.  This is where you can add config files and additional\n  application modules if you prefer not to have all your app code in the\n  ``app.py`` file.\n\nSee :doc:`multifile` for more info on the ``chalicelib/`` directory.  Both the\n``app.py`` and the ``chalicelib/`` directory are intended for code that you\nwrite yourself.\n\n\n.. _package-3rd-party:\n\n3rd Party Packages\n------------------\n\nWhen handling third party packages, you can have Chalice manage\nthese files as part of the deployment package of your Lambda function,\nor as a separate Lambda layer that's shared by all your Lambda functions.\nSee the :ref:`package-examples` section for examples of how this works.\n\nThere are two options for handling python package dependencies:\n\n* **requirements.txt** - During the packaging process, Chalice will\n  install any packages it finds or can build compatible wheels for.\n  Specifically all pure python packages as well as all packages that upload\n  wheel files for the ``manylinux1_x86_64`` platform will be automatically\n  installable.\n* **vendor/** - The *contents* of this directory are automatically added to\n  your deployment package and its location in your Lambda functions will\n  depend on whether you are using automatic Lambda Layers, described in the\n  :ref:`package-auto-layers` section.\n\nChalice will also check for an optional ``vendor/`` directory in the project\nroot directory.  The contents of this directory are automatically included in\nthe top level of the deployment package (see :ref:`package-examples` for\nspecific examples).  The ``vendor/`` directory is helpful in these scenarios:\n\n* You need to include custom packages or binary content that is not accessible\n  via ``pip``.  These may be internal packages that aren't public.\n* Wheel files are not available for a package you need from pip.\n* A package is installable with ``requirements.txt`` but has optional c\n  extensions. Chalice can build the dependency without the c extensions, but\n  if you want better performance you can vendor a version that is compiled.\n\nAs a general rule of thumb, code that you write goes in either ``app.py`` or\n``chalicelib/``, and dependencies are either specified in ``requirements.txt``\nor placed in the ``vendor/`` directory.\n\n.. _package-auto-layers:\n\nAutomatic Lambda Layers\n~~~~~~~~~~~~~~~~~~~~~~~\n\nBy default, Chalice will create a single zip file that contains all the\ncode needed to run your application.\n\nYou can set the :ref:`automatic-layer-option` in your ``.chalice/config.json``\nfile which will instruct Chalice to create your 3rd party packages as a\nseparate Lambda layer.\n\nThere are several benefits to this approach:\n\n* The layer is created once and then shared across all Lambda functions in\n  your application.\n* When creating or updating a Lambda function, you send the entire contents\n  of the zip file that contains your app.  This is repeated for each Lambda\n  function.  As a result, there is unnecessary time and network bandwidth\n  used to send the same 3rd party dependencies for each Lambda function.\n  When using layers, Chalice will specify the layer ARN when creating or\n  updating your Lambda function, which cuts down on the time it takes\n  to package and deploy your application.\n* Saves storage space in Lambda.  There is a 75GB maximum size for all\n  your Lambda functions.  If you're not using layers, each Lambda function\n  stores its own copies of your 3rd party dependencies.\n\nWe recommend setting ``\"automatic_layer\": true`` in your\n``.chalice/config.json`` due to these benefits.\n\nMigrating to ``\"automatic_layer\": true`` is mostly backwards compatible\nwith one notable exception: the location of the vendor files is different.\n\nWhen not using automatic layers, any files placed in ``vendor/`` will be\navailable in your CWD of your application.  However, when using layers,\nthese files will be unzipped to ``/opt/python/lib/pythonX.Y/site-packages``.\nIf you are using the ``vendor/`` directory to include custom built python\npackages then this change is transparent as that directory is automatically\nadded to the python path.  However, if you are trying to read a file from\n``vendor/`` directly, then this will no longer work.  For example, if you\nhave::\n\n    .\n    ├── app.py\n    └── vendor\n        └── myimage.png\n\nAnd your ``app.py`` attempts to read this file:\n\n.. code-block:: python\n\n   @app.lambda_function()\n   def handler(event, context):\n       with open('myimage.png') as f:\n           do_something(f)\n\n\nThis code will no longer work.  You have two options.  You can either place\nstatic assets in ``chalicelib/`` or you'll have to check both directories for\nyour file::\n\n'/opt/python/lib/python%s.%s/site-packages' % sys.version_info[:2]\n\n.. code-block:: python\n\n   @app.lambda_function()\n   def handler(event, context):\n       with open_vendor_file('myimage.png') as f:\n           do_something(f)\n\n   def open_vendor_file(filename):\n       directories = [\n           '.',\n           '/opt/python/lib/python%s.%s/site-packages' % sys.version_info[:2]\n       ]\n       for dirname in directories:\n           full_path = os.path.join(dirname, filename)\n           if os.path.isfile(full_path):\n               return open(full_path)\n\nEnvironment Variables\n---------------------\n\nAs part of the packaging and deployment process, Chalice will import your\n``app.py`` file.  This will result in any top level module code being\nexecuted.  This can sometimes have undesireable behavior.\nWhen running any Chalice CLI commands, a ``AWS_CHALICE_CLI_MODE`` environment\nvariable is set.  You can check if this env var is set in your ``app.py``\nif you have code that you don't want to run whenever your app is packaged\nand deployed.\n\n.. code-block:: python\n\n   import os\n\n   app = Chalice(app_name='testimport')\n\n   expensive_connection = None\n   if 'AWS_CHALICE_CLI_MODE' not in os.environ:\n       # We're running in Lambda, we want to start up\n       # our connection to our DB.\n       expensive_connection = ConnectToDB()\n\n\nChalice will also set any environment variables specified in your global or\nstage specific configuration whenever your app is packaged and deployed.\nPer-Lambda function environment variables are not set when importing your app\n(this would require importing your application for each Lambda function).  For\nexample, given the config below you would be able to access the ``STAGE_VAR``\nenvironment variable but not the ``PER_FUNCTION`` variable during the\nbuilding/packaging process when Chalice imports your application.  This\ncan be useful if you want to move configuration or resource names out of\nyour app.py file.\n\n::\n\n  {\n    \"stages\": {\n      \"dev\": {\n        \"environment_variables\": {\n          \"STAGE_VAR\": \"stage-var\"\n        }\n        \"api_gateway_stage\": \"api\",\n        \"lambda_functions\": {\n          \"foo\": {\n            \"environment_variables\": {\"PER_FUNCTION\": \"per-function\"}\n          }\n        }\n      }\n    },\n    \"version\": \"2.0\",\n    \"app_name\": \"demo\"\n  }\n\nThis only applies to the packaging stage.  When the ``foo`` function is invoked\non Lambda, the ``PER_FUNCTION`` environment variable will be set as expected.\n\n\n.. _package-examples:\n\nExamples\n--------\n\nSuppose I have the following app structure::\n\n    .\n    ├── app.py\n    ├── chalicelib\n    │   ├── __init__.py\n    │   └── utils.py\n    ├── requirements.txt\n    └── vendor\n        ├── myimage.png\n        └── internalpackage\n            └── __init__.py\n\nAnd the ``requirements.txt`` file had one requirement::\n\n    $ cat requirements.txt\n    sortedcontainers==1.5.4\n\nWith the default behavior of not using layers (``\"automatic_layer\": false``),\nthe final deployment package directory structure would look like this::\n\n    deployment.zip\n    .\n    ├── app.py\n    ├── chalicelib\n    │   ├── __init__.py\n    │   └── utils.py\n    ├── myimage.png\n    ├── internalpackage\n    │   └── __init__.py\n    └── sortedcontainers\n        └── __init__.py\n\n\nThis directory structure is then zipped up and sent to AWS Lambda during the\ndeployment process.  Suppose our application had two Lambda functions.\nEach Lambda function has its own copy of the application deployment package,\nas shown in the architecture diagram below.\n\n.. image:: ../img/no-auto-layer.png\n   :width: 50%\n   :align: center\n   :alt: Default behavior with no layers.\n\n\nIf you are using ``\"automatic_layer\": true``, then two zip files will be\ncreated. The deployment package used for the Lambda function will be::\n\n    deployment.zip\n    .\n    ├── app.py\n    └── chalicelib\n        ├── __init__.py\n        └── utils.py\n\nAnd the zip file for the shared lambda layer will look like this::\n\n    layer-deployment.zip\n    .\n    └── python\n        └── lib\n            └── python3.7\n                └── site-packages\n                    ├── myimage.png\n                    ├── internalpackage\n                    │   └── __init__.py\n                    └── sortedcontainers\n                        └── __init__.py\n\n\nBelow is an updated diagram of the same Chalice application using automatic\nlayers.  Both functions now share the same Lambda layer that contains the third\nparty packages used by the application.\n\n.. image:: ../img/auto-layer.png\n   :width: 80%\n   :align: center\n   :alt: Shared layer for 3rd party code.\n\n\nCryptography Example\n--------------------\n\n.. note::\n   Since the original version of this example was written, cryptography has\n   released version 2.0 which does have manylinux1 wheels available. This\n   means if you want to use cryptography in a Chalice app all you need to\n   do is add ``cryptography`` or ``cryptography>=2.0`` in your\n   requirements.txt file.\n\n   This example will use version 1.9 of Cryptography\n   because it is a good example of a library with C extensions and no wheel\n   files.\n\nBelow shows an example of how to use the\n`cryptography 1.9 <https://pypi.org/project/cryptography/1.9/>`__ package\nin a Chalice app for the ``python3.6`` lambda environment.\n\nSuppose you are on a Mac or Windows and want to deploy a Chalice app that\ndepends on the ``cryptography==1.9`` package. If you simply add it to your\n``requirements.txt`` file and try to deploy it with ``chalice deploy`` you will\nget the following warning during deployment::\n\n  $ cat requirements.txt\n  cryptography==1.9\n  $ chalice deploy\n  Updating IAM policy.\n  Updating lambda function...\n  Creating deployment package.\n\n  Could not install dependencies:\n  cryptography==1.9\n  You will have to build these yourself and vendor them in\n  the chalice vendor folder.\n\n  Your deployment will continue but may not work correctly\n  if missing dependencies are not present. For more information:\n  http://aws.github.io/chalice/topics/packaging.html\n\nThis happened because the ``cryptography`` version 1.9 does not have wheel\nfiles available on PyPi, and has C extensions. Since we are not on the same\nplatform as AWS Lambda, the compiled C extensions Chalice built were not\ncompatible. To get around this we are going to leverage the ``vendor/``\ndirectory, and build the ``cryptography`` package on a compatible linux system.\n\nYou can do this yourself by building ``cryptography`` on an Amazon Linux\ninstance running in EC2. All of the following commands were run inside a\n``python 3.6`` virtual environment.\n\n* Download the source first::\n\n    $ pip download cryptography==1.9\n\n  This will download all the requirements into the current working directory.\n  The directory should have the following contents:\n\n  * ``asn1crypto-0.22.0-py2.py3-none-any.whl``\n  * ``cffi-1.10.0-cp36-cp36m-manylinux1_x86_64.whl``\n  * ``cryptography-1.9.tar.gz``\n  * ``idna-2.5-py2.py3-none-any.whl``\n  * ``pycparser-2.17.tar.gz``\n  * ``six-1.10.0-py2.py3-none-any.whl``\n\n  This is a complete set of dependencies required for the cryptography package.\n  Most of these packages have wheels that were downloaded, which means they can\n  simply be put in the ``requirements.txt`` and Chalice will take care of\n  downloading them. That leaves ``cryptography`` itself and ``pycparser`` as\n  the only two that did not have a wheel file available for download.\n\n* Next build the ``cryptography`` source package into a wheel file::\n\n    $ pip wheel cryptography-1.9.tar.gz\n\n  This will take a few seconds and build a wheel file for both ``cryptography``\n  and ``pycparser``. The directory should now have two additional wheel files:\n\n  * ``cryptography-1.9-cp36-cp36m-linux_x86_64.whl``\n  * ``pycparser-2.17-py2.py3-none-any.whl``\n\n  The ``cryptography`` wheel file has been built with a compatible\n  architecture for Lambda (``linux_x86_64``) and the ``pycparser`` has been\n  built for ``any`` architecture which means it can also be automatically\n  packaged by Chalice if it is listed in the ``requirements.txt`` file.\n\n* Download the ``cryptography`` wheel file from the Amazon Linux instance and\n  unzip it into the ``vendor/`` directory in the root directory of your Chalice\n  app.\n\n  You should now have a project directory that looks like this::\n\n     $ tree\n     .\n     ├── app.py\n     ├── requirements.txt\n     └── vendor\n         ├── cryptography\n         │   ├── ... Lots of files\n         │\n         └── cryptography-1.9.dist-info\n             ├── DESCRIPTION.rst\n             ├── METADATA\n             ├── RECORD\n             ├── WHEEL\n             ├── entry_points.txt\n             ├── metadata.json\n             └── top_level.txt\n\n  The ``requirements.txt`` file should look like this::\n\n    $ cat requirements.txt\n    cffi==1.10.0\n    six==1.10.0\n    asn1crypto==0.22.0\n    idna==2.5\n    pycparser==2.17\n\n  In your ``app.py`` file you can now import ``cryptography``, and these\n  dependencies will all get included when the ``chalice deploy`` command is\n  run.\n"
  },
  {
    "path": "docs/source/topics/purelambda.rst",
    "content": "=====================\nPure Lambda Functions\n=====================\n\n\nChalice provides abstractions over AWS Lambda functions, including:\n\n* An API handler that coordinates with API Gateway for creating rest APIs.\n* A custom authorizer that allows you to integrate custom auth logic in your\n  rest API.\n* A scheduled event that includes managing the CloudWatch Event rules, targets,\n  and permissions.\n\nHowever, chalice also supports managing pure Lambda functions that don't have\nany abstractions built on top.  This is useful if you want to create a Lambda\nfunction for something that's not supported by chalice or if you just want to\ncreate Lambda functions but don't want to manage handling dependencies and\ndeployments yourself.\n\nIn order to do this, you can use the :meth:`Chalice.lambda_function` decorator\nto denote that this python function is a pure lambda function that should\nbe invoked as is, without any input or output mapping.  When you use\nthis function, you must provide a function that maps to the same function\nsignature expected by AWS Lambda as `defined here`_.\n\nLet's look at an example.\n\n.. code-block:: python\n\n    app = chalice.Chalice(app_name='foo')\n\n    @app.route('/')\n    def index():\n        return {'hello': 'world'}\n\n    @app.lambda_function()\n    def custom_lambda_function(event, context):\n        # Anything you want here.\n        return {}\n\n    @app.lambda_function(name='MyFunction')\n    def other_lambda_function(event, context):\n        # Anything you want here.\n        return {}\n\nIn this example, we've updated the starter hello world app with\ntwo extra Lambda functions.  When you run ``chalice deploy`` Chalice will create\nthree Lambda functions.  The first lambda function is for the API handler\nused by API gateway.  The second and third lambda functions will be pure lambda\nfunctions.  These two additional lambda functions won't be hooked up to anything.\nYou'll need to manage connecting them to any additional AWS Resources on your\nown.\n\n\n.. _defined here: https://docs.aws.amazon.com/lambda/latest/dg/python-programming-model-handler-types.html\n"
  },
  {
    "path": "docs/source/topics/pyversion.rst",
    "content": "Python Version Support\n======================\n\nChalice supports all versions of python supported by AWS Lambda, which is\ncurrently Python 3.6 and greater.  You can see the list of\nsupported python versions for Lambda in their\n`docs <https://docs.aws.amazon.com/lambda/latest/dg/lambda-python.html>`__.\n\nChalice will automatically pick which version of python to use for Lambda\nbased on the major version of python you are using.  You don't have to\nexplicitly configure which version of python you want to use. For example::\n\n    $ python --version\n    Python 3.6.1\n    $ chalice new-project test-versions\n    $ cd test-versions\n    $ chalice package test-package\n    $ grep -C 3 python test-package/sam.json\n        \"APIHandler\": {\n          \"Type\": \"AWS::Serverless::Function\",\n          \"Properties\": {\n            \"Runtime\": \"python3.6\",\n            \"Handler\": \"app.app\",\n            \"CodeUri\": \"./deployment.zip\",\n            \"Events\": {\n\n    # Similarly, if we were to run \"chalice deploy\" we'd\n    # use python3.6 for the runtime.\n    $ chalice --debug deploy\n    Initiating first time deployment...\n    Deploying to: dev\n    ...\n    \"Runtime\":\"python3.6\"\n    ...\n    https://rest-api-id.execute-api.us-west-2.amazonaws.com/api/\n\n\nIn the example above, we're using python 3.6.1 so chalice automatically\nselects the ``python3.6`` runtime for lambda.  If we were using python 3.9.6,\nchalice would automatically select ``python3.9`` as the runtime.\n\nChalice will emit a warning if the minor version does not match a python\nversion supported by Lambda.  Chalice will select the closest Lambda version\nin this scenario, as shown in the table below.\n\nWe strongly encourage you to develop your application using the same\nmajor/minor version of python you plan on using on AWS Lambda.\n\n\nChanging Python Runtime Versions\n================================\n\nThe version of the python runtime to use in AWS Lambda can be reconfigured\nwhenever you deploy your chalice app.  This allows you to migrate to newer\nPython versions in AWS Lambda by creating a new virtual environment that uses\npython3.  For example, suppose you have an existing chalice app that uses\nPython 3.6 ::\n\n    $ python --version\n    Python 3.6.1\n    $ chalice deploy\n    ...\n    https://endpoint/api\n\nTo upgrade the application to use Python 3.9, create a python3 virtual\nenvironment and redeploy.\n\n::\n\n    $ deactivate\n    $ python3 -m venv /tmp/venv3\n    $ source /tmp/venv3/bin/activate\n    $ python --version\n    Python 3.9.6\n    $ chalice deploy\n    ...\n"
  },
  {
    "path": "docs/source/topics/routing.rst",
    "content": "Routing\n=======\n\nThe :meth:`Chalice.route` method is used to construct which routes\nyou want to create for your API.  The concept is the same\nmechanism used by `Flask <https://palletsprojects.com/p/flask/>`__ and\n`bottle <http://bottlepy.org/docs/dev/index.html>`__.\nYou decorate a function with ``@app.route(...)``, and whenever\na user requests that URL, the function you've decorated is called.\nFor example, suppose you deployed this app:\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = Chalice(app_name='helloworld')\n\n\n    @app.route('/')\n    def index():\n        return {'view': 'index'}\n\n    @app.route('/a')\n    def a():\n        return {'view': 'a'}\n\n    @app.route('/b')\n    def b():\n        return {'view': 'b'}\n\n\nIf you go to ``https://endpoint/``, the ``index()`` function would be called.\nIf you went to ``https://endpoint/a`` and ``https://endpoint/b``, then the\n``a()`` and ``b()`` function would be called, respectively.\n\n.. note::\n\n  Do not end your route paths with a trailing slash.  If you do this, the\n  ``chalice deploy`` command will raise a validation error.\n\n\nYou can also create a route that captures part of the URL.  This captured value\nwill then be passed in as arguments to your view function:\n\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = Chalice(app_name='helloworld')\n\n\n    @app.route('/users/{name}')\n    def users(name):\n        return {'name': name}\n\n\nIf you then go to ``https://endpoint/users/james``, then the view function\nwill be called as: ``users('james')``.  The parameters are passed as\nkeyword parameters based on the name as they appear in the URL. The argument\nnames for the view function must match the name of the captured\nargument:\n\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = Chalice(app_name='helloworld')\n\n\n    @app.route('/a/{first}/b/{second}')\n    def users(first, second):\n        return {'first': first, 'second': second}\n\n\nOther Request Metadata\n----------------------\n\nThe route path can only contain ``[a-zA-Z0-9._-]`` chars and curly braces for\nparts of the URL you want to capture.  You do not need to model other parts of\nthe request you want to capture, including headers and query strings.  Within\na view function, you can introspect the current request using the\n:attr:`app.current_request <Chalice.current_request>` attribute.  This also\nmeans you cannot control the routing based on query strings or headers.\nHere's an example for accessing query string data in a view function:\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = Chalice(app_name='helloworld')\n\n\n    @app.route('/users/{name}')\n    def users(name):\n        result = {'name': name}\n        if app.current_request.query_params.get('include-greeting') == 'true':\n            result['greeting'] = 'Hello, %s' % name\n        return result\n\nIn the function above, if the user provides a ``?include-greeting=true`` in the\nHTTP request, then an additional ``greeting`` key will be returned::\n\n    $ http https://endpoint/api/users/bob\n\n    {\n        \"name\": \"bob\"\n    }\n\n    $ http https://endpoint/api/users/bob?include-greeting=true\n\n    {\n        \"greeting\": \"Hello, bob\",\n        \"name\": \"bob\"\n    }\n"
  },
  {
    "path": "docs/source/topics/sdks.rst",
    "content": "SDK Generation\n==============\n\nThe ``@app.route(...)`` information you provide chalice allows\nit to create corresponding routes in API Gateway.  One of the benefits of this\napproach is that we can leverage API Gateway's SDK generation process.\nChalice offers a ``chalice generate-sdk`` command that will automatically\ngenerate an SDK based on your declared routes.\n\n.. note::\n  The only supported language at this time is javascript.\n\nKeep in mind that chalice itself does not have any logic for generating\nSDKs.  The SDK generation happens service side in `API Gateway`_, the\n``chalice generate-sdk`` is just a high level wrapper around that\nfunctionality.\n\nTo generate an SDK for a chalice app, run this command from the project\ndirectory::\n\n    $ chalice generate-sdk /tmp/sdk\n\nYou should now have a generated javascript sdk in ``/tmp/sdk``.\nAPI Gateway includes a ``README.md`` as part of its SDK generation\nwhich contains details on how to use the javascript SDK.\n\nExample\n-------\n\nSuppose we have the following chalice app:\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = Chalice(app_name='sdktest')\n\n    @app.route('/', cors=True)\n    def index():\n        return {'hello': 'world'}\n\n    @app.route('/foo', cors=True)\n    def foo():\n        return {'foo': True}\n\n    @app.route('/hello/{name}', cors=True)\n    def hello_name(name):\n        return {'hello': name}\n\n    @app.route('/users/{user_id}', methods=['PUT'], cors=True)\n    def update_user(user_id):\n        return {\"msg\": \"fake updated user\", \"userId\": user_id}\n\n\nLet's generate a javascript SDK and test it out in the browser.\nRun the following command from the project dir::\n\n    $ chalice generate-sdk /tmp/sdkdemo\n    $ cd /tmp/sdkdemo\n    $ ls -la\n    -rw-r--r--   1 jamessar  r  3227 Nov 21 17:06 README.md\n    -rw-r--r--   1 jamessar  r  9243 Nov 21 17:06 apigClient.js\n    drwxr-xr-x   6 jamessar  r   204 Nov 21 17:06 lib\n\nYou should now be able to follow the instructions from API Gateway in the\n``README.md`` file. Below is a snippet that shows how the generated\njavascript SDK methods correspond to the ``@app.route()`` calls in chalice.\n\n.. code-block:: html\n\n  <script type=\"text/javascript\">\n    // Below are examples of how the javascript SDK methods\n    // correspond to chalice @app.routes()\n    var apigClient = apigClientFactory.newClient();\n\n    // @app.route('/')\n    apigClient.rootGet().then(result => {\n        document.getElementById('root-get').innerHTML = JSON.stringify(result.data);\n    });\n\n    // @app.route('/foo')\n    apigClient.fooGet().then(result => {\n        document.getElementById('foo-get').innerHTML = JSON.stringify(result.data);\n    });\n\n    // @app.route('/hello/{name}')\n    apigClient.helloNameGet({name: 'jimmy'}).then(result => {\n        document.getElementById('helloname-get').innerHTML = JSON.stringify(result.data);\n    });\n\n    // @app.route('/users/{user_id}', methods=['PUT'])\n    apigClient.usersUserIdPut({user_id: '123'}, 'body content').then(result => {\n        document.getElementById('users-userid-put').innerHTML = JSON.stringify(result.data);\n    });\n  </script>\n\n\n\n\n\nExample HTML File\n~~~~~~~~~~~~~~~~~\n\nIf you want to try out the example above, you can use the following index.html\npage to test:\n\n.. code-block:: html\n\n    <!DOCTYPE html>\n    <html lang=\"en\">\n        <head>\n            <title>SDK Test</title>\n            <meta charset=\"UTF-8\">\n            <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n            <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css\">\n            <script type=\"text/javascript\" src=\"lib/axios/dist/axios.standalone.js\"></script>\n            <script type=\"text/javascript\" src=\"lib/CryptoJS/rollups/hmac-sha256.js\"></script>\n            <script type=\"text/javascript\" src=\"lib/CryptoJS/rollups/sha256.js\"></script>\n            <script type=\"text/javascript\" src=\"lib/CryptoJS/components/hmac.js\"></script>\n            <script type=\"text/javascript\" src=\"lib/CryptoJS/components/enc-base64.js\"></script>\n            <script type=\"text/javascript\" src=\"lib/url-template/url-template.js\"></script>\n            <script type=\"text/javascript\" src=\"lib/apiGatewayCore/sigV4Client.js\"></script>\n            <script type=\"text/javascript\" src=\"lib/apiGatewayCore/apiGatewayClient.js\"></script>\n            <script type=\"text/javascript\" src=\"lib/apiGatewayCore/simpleHttpClient.js\"></script>\n            <script type=\"text/javascript\" src=\"lib/apiGatewayCore/utils.js\"></script>\n            <script type=\"text/javascript\" src=\"apigClient.js\"></script>\n\n\n            <script type=\"text/javascript\">\n              // Below are examples of how the javascript SDK methods\n              // correspond to chalice @app.routes()\n              var apigClient = apigClientFactory.newClient();\n\n              // @app.route('/')\n              apigClient.rootGet().then(result => {\n                  document.getElementById('root-get').innerHTML = JSON.stringify(result.data);\n              });\n\n              // @app.route('/foo')\n              apigClient.fooGet().then(result => {\n                  document.getElementById('foo-get').innerHTML = JSON.stringify(result.data);\n              });\n\n              // @app.route('/hello/{name}')\n              apigClient.helloNameGet({name: 'jimmy'}).then(result => {\n                  document.getElementById('helloname-get').innerHTML = JSON.stringify(result.data);\n              });\n\n              // @app.route('/users/{user_id}', methods=['PUT'])\n              apigClient.usersUserIdPut({user_id: '123'}, 'body content').then(result => {\n                  document.getElementById('users-userid-put').innerHTML = JSON.stringify(result.data);\n              });\n            </script>\n        </head>\n        <body>\n            <div><h5>result of rootGet()</h5><pre id=\"root-get\"></pre></div>\n            <div><h5>result of fooGet()</h5><pre id=\"foo-get\"></pre></div>\n            <div><h5>result of helloNameGet({name: 'jimmy'})</h5><pre id=\"helloname-get\"></pre></div>\n            <div><h5>result of usersUserIdPut({user_id: '123'})</h5><pre id=\"users-userid-put\"></pre></div>\n        </body>\n    </html>\n\n\n.. _API Gateway: https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-generate-sdk.html\n"
  },
  {
    "path": "docs/source/topics/stages.rst",
    "content": "Chalice Stages\n==============\n\nChalice has the concept of stages, which are completely\nseparate sets of AWS resources.  When you first create a chalice\nproject and run commands such as ``chalice deploy`` and ``chalice url``,\nyou don't have to specify any stage values or stage configuration.\nThis is because chalice will use a stage named ``dev`` by default.\n\nYou may eventually want to have multiple stages of your application.  A\ncommon configuration would be to have a ``dev``, ``beta`` and ``prod``\nstage.  A ``dev`` stage would be used by developers to test out new\nfeatures.  Completed features would be deployed to ``beta``, and the\n``prod`` stage would be used for serving production traffic.\n\nChalice can help you manage this.\n\nTo create a new chalice stage, specify the ``--stage`` argument.\nIf the stage does not exist yet, it will be created for you::\n\n    $ chalice deploy --stage prod\n\nBy creating a new chalice stage, a new API Gateway rest API, Lambda\nfunction, and potentially (depending on config settings) a new IAM role\nwill be created for you.\n\n\nExample\n-------\n\nLet's say we have a new app::\n\n    $ chalice new-project myapp\n    $ cd myapp\n    $ chalice deploy\n    ...\n    https://mmnkdi.execute-api.us-west-2.amazonaws.com/api/\n\nWe've just created our first stage, ``dev``.  We can iterate on our\napplication and continue to run ``chalice deploy`` to deploy our code\nto the ``dev`` stage.  Let's say we want to now create a ``prod`` stage.\nTo do this, we can run::\n\n    $ chalice deploy --stage prod\n    ...\n    https://wk9fhx.execute-api.us-west-2.amazonaws.com/api/\n\nWe now have two completely separate rest APIs::\n\n    $ chalice url --stage dev\n    https://mmnkdi.execute-api.us-west-2.amazonaws.com/api/\n\n    $ chalice url --stage prod\n    https://wk9fhx.execute-api.us-west-2.amazonaws.com/api/\n\nAdditionally, we can see all our deployed values by looking\nat the ``.chalice/deployed/dev.json`` or ``.chalice/deployed/prod.json`` files::\n\n    $ cat .chalice/deployed/dev.json\n    {\n      \"resources\": [\n        {\n          \"name\": \"api_handler\",\n          \"resource_type\": \"lambda_function\",\n          \"lambda_arn\": \"arn:aws:lambda:...:function:myapp-dev\"\n        },\n        {\n          \"name\": \"rest_api\",\n          \"resource_type\": \"rest_api\",\n          \"rest_api_id\": \"wk9fhx\",\n          \"rest_api_url\": \"https://wk9fhx.execute-api.us-west-2.amazonaws.com/api/\"\n        }\n      ],\n      \"schema_version\": \"2.0\",\n      \"backend\": \"api\"\n    }\n\n    $ cat .chalice/deployed/prod.json\n    {\n      \"resources\": [\n        {\n          \"name\": \"api_handler\",\n          \"resource_type\": \"lambda_function\",\n          \"lambda_arn\": \"arn:aws:lambda:...:function:myapp-prod\"\n        },\n        {\n          \"name\": \"rest_api\",\n          \"resource_type\": \"rest_api\",\n          \"rest_api_id\": \"mmnkdi\",\n          \"rest_api_url\": \"https://mmnkdi.execute-api.us-west-2.amazonaws.com/api/\"\n        }\n      ],\n      \"schema_version\": \"2.0\",\n      \"backend\": \"api\"\n    }\n"
  },
  {
    "path": "docs/source/topics/testing.rst",
    "content": "Testing\n=======\n\nChalice provides a :ref:`test client <testing-api>` in ``chalice.test`` that\nyou can use to test your Chalice applications.  This client lets you invoke\nLambda function and event handlers directly, as well as test your REST APIs.\n\n.. _testing-lambda-functions:\n\nLambda Functions\n----------------\n\nTo test lambda functions, use the\n:meth:`Client.lambda_.invoke <TestLambdaClient.invoke>` method.  The\ntest client is intended to be used as a context manager.  For example,\ngiven this sample app:\n\n\n.. code-block:: python\n\n   from chalice import Chalice\n\n   app = Chalice(app_name=\"testclient\")\n\n   @app.lambda_function()\n   def foo(event, context):\n       return {'hello': 'world'}\n\n   @app.lambda_function()\n   def bar(event, context):\n       return {'event': event}\n\n\nHere's how you can test these functions with the test client.  In our\nexample, we'll be using `pytest <https://docs.pytest.org/en/stable/>`__,\nbut the Chalice test client will work with any testing framework.\nWe'll create a new ``tests/`` directory and create a ``tests/__init__.py``\nand a ``tests/test_app.py`` file.\n\n::\n\n    $ mkdir tests\n    $ touch tests/{__init__.py,test_app.py}\n\nThe ``tests/test_app.py`` file should have the following contents:\n\n.. code-block:: python\n\n   from chalice.test import Client\n   from app import app\n\n   def test_foo_function():\n       with Client(app) as client:\n           result = client.lambda_.invoke('foo')\n           assert result.payload == {'hello': 'world'}\n\n   def test_bar_function():\n       with Client(app) as client:\n           result = client.lambda_.invoke(\n               'bar', {'my': 'event'})\n           assert result.payload == {'event': {'my': 'event'}}\n\nNow we can run our tests with ``pytest``::\n\n    $ pip install pytest\n    $ py.test tests/test_app.py\n    ========================= test session starts ==========================\n    platform darwin -- Python 3.7.3, pytest-5.3.1, py-1.5.3, pluggy-0.12.0\n    rootdir: /tmp/testclient\n    plugins: hypothesis-4.43.1, cov-2.8.1\n    collected 2 items\n\n    test_app.py ..                                                            [100%]\n\n    ========================= 2 passed in 0.32s ============================\n\n.. note::\n   See the :ref:`testing-pytest-fixtures` section for how to use pytest\n   fixtures with the Chalice test client.\n\nFor testing Lambda functions that are connected to specific events,\nyou can use the :attr:`Client.events` attribute to generate\nsample events.  For example:\n\n.. code-block:: python\n\n   from chalice import Chalice\n\n   @app.on_sns_message(topic='mytopic')\n   def foo(event):\n       return {'message': event.message}\n\n   # Test code\n\n   from chalice.test import Client\n\n   def test_sns_handler():\n       with Client(app) as client:\n           response = client.lambda_.invoke(\n               \"foo\",\n               client.events.generate_sns_event(message=\"hello world\")\n           )\n           assert response.payload == {'message': 'hello world'}\n\n\nEnvironment Variables\n~~~~~~~~~~~~~~~~~~~~~\n\nThe Chalice test client will also configure any environment variables you\nhave configured with your Lambda functions in your ``.chalice/config.json``\nfile.  For example, suppose you had these config file:\n\n.. code-block:: json\n\n   {\n       \"version\": \"2.0\",\n       \"app_name\": \"testenv\",\n       \"stages\": {\n           \"prod\": {\n               \"api_gateway_stage\": \"api\",\n               \"environment_variables\": {\n                   \"MY_ENV_VAR\": \"TOP LEVEL\"\n               },\n               \"lambda_functions\": {\n                   \"bar\": {\n                       \"environment_variables\": {\n                           \"MY_ENV_VAR\": \"OVERRIDE\"\n                       }\n                   }\n               }\n           }\n       }\n   }\n\nThese sets a ``MY_ENV_VAR`` environment variable for the ``prod`` stage.\nThe ``bar`` function overrides this environment variable with its own\ncustom value.  To test this, we need to specify the ``prod`` stage when\nwe create our test client:\n\n.. code-block:: python\n\n   from chalice import Chalice\n\n   app = Chalice(app_name=\"testclient\")\n\n   @app.lambda_function()\n   def foo(event, context):\n       return {'value': os.environ.get('MY_ENV_VAR')}\n\n   @app.lambda_function()\n   def bar(event, context):\n       return {'value': os.environ.get('MY_ENV_VAR')}\n\n    # Test code\n   from chalice.test import Client\n\n   def test_foo_function():\n       with Client(app, stage_name='prod') as client:\n           result = client.lambda_.invoke('foo')\n           assert result.payload == {'value': 'TOP LEVEL'}\n\n   def test_bar_function():\n       with Client(app) as client:\n           result = client.lambda_.invoke('bar')\n           assert result.payload == {'value': 'OVERRIDE'}\n\n\nREST APIs\n---------\n\nYou can test your REST API with the Chalice test client using the\n:attr:`Client.http` attribute.  For example, given this REST API:\n\n\n.. code-block:: python\n\n   from chalice import Chalice\n\n   app = Chalice(app_name=\"testclient\")\n\n   @app.route('/')\n   def index():\n       return {'hello': 'world'}\n\n\nYou can test this route with:\n\n.. code-block:: python\n\n   from chalice.test import Client\n   from app import app\n\n    def test_index():\n        with Client(app) as client:\n            response = client.http.get('/')\n            assert response.json_body == {'hello': 'world'}\n\nIf you want to access the response body's raw bytes, you can use the\n``body`` attribute:\n\n.. code-block:: python\n\n   from chalice.test import Client\n   from app import app\n\n    def test_index():\n        with Client(app) as client:\n            response = client.http.get('/')\n            assert response.body == b'{\"hello\":\"world\"}'\n\n\nYou can also test other HTTP methods by using the corresponding\n``post()``, ``put()``, ``delete()``, etc. method calls.\n\n.. code-block:: python\n\n   import json\n   from chalice import Chalice\n\n   app = Chalice(app_name=\"testclient\")\n\n   @app.route('/', methods=['POST'])\n   def index()\n       return app.current_request.json_body\n\n\n   def test_index():\n      with Client(app) as client:\n          response = client.http.post(\n              '/myview',\n              headers={'Content-Type':'application/json'},\n              body=json.dumps({'example':'json'})\n          )\n          assert response.json_body == {'example': 'json'}\n\n\nYou can also test builtin authorizers with the test client:\n\n.. code-block:: python\n\n   from chalice import Chalice\n\n   app = Chalice(app_name=\"testclient\")\n\n   @app.authorizer()\n   def myauth(event)\n       if event.token == 'allow':\n           return AuthResponse(['*'], principal_id='id')\n       return AuthResponse([], principal_id='noone')\n\n   @app.route('/needs-auth', authorizer=myauth)\n   def needs_auth()\n       return {'success': True}\n\n   #  Test code:\n   from chalice.test import Client\n\n    def test_needs_auth():\n        with Client(app) as client:\n            response = client.http.get(\n                '/needs-auth', headers={'Authorization': 'allow'})\n            assert response.json_body == {'success': True}\n            assert client.http.get(\n                '/needs-auth',\n                headers={'Authorization': 'deny'}).status_code == 403\n\n\n.. _testing-boto3-client-calls:\n\nTesting Boto3 Client Calls\n--------------------------\n\nIf your event handlers are making AWS API calls using boto3 or botocore,\nyou can use the `botocore stubber\n<https://botocore.amazonaws.com/v1/documentation/api/latest/reference/stubber.html>`__\nto test your API calls.  For example, suppose we have an app that makes an\nAPI call to Amazon Rekognition whenever an object is uploaded to S3:\n\n.. code-block:: python\n\n   import boto3\n\n   from chalice import Chalice\n\n   app = Chalice(app_name='testclient')\n   _REKOGNITION_CLIENT = None\n\n\n   def get_rekognition_client():\n       global _REKOGNITION_CLIENT\n       if _REKOGNITION_CLIENT is None:\n           _REKOGNITION_CLIENT = boto3.client('rekognition')\n       return _REKOGNITION_CLIENT\n\n\n   @app.on_s3_event(bucket='mybucket',\n                    events=['s3:ObjectCreated:*'])\n   def handle_object_created(event):\n       client = get_rekognition_client()\n       response = client.detect_labels(\n           Image={\n               'S3Object': {\n                   'Bucket': event.bucket,\n                   'Name': event.key,\n               },\n           },\n           MinConfidence=50.0\n       )\n       labels = [label['Name'] for label in response['Labels']]\n       # In the real app we'd now do something with these labels\n       # (e.g. store than in a database so we can query them later).\n       return labels\n\nTo test this, we'll combine the botocore stubber and the Chalice test client:\n\n.. code-block:: python\n\n   from chalice.test import Client\n   import app\n\n   from botocore.stub import Stubber\n\n   def test_calls_rekognition():\n       client = app.get_rekognition_client()\n       stub = Stubber(client)\n       stub.add_response(\n           'detect_labels',\n           expected_params={\n               'Image': {\n                   'S3Object': {\n                       'Bucket': 'mybucket',\n                       'Name': 'mykey',\n                   }\n               },\n               'MinConfidence': 50.0,\n           },\n           service_response={\n               'Labels': [\n                   {'Name': 'Dog', 'Confidence': 75.0},\n                   {'Name': 'Mountain', 'Confidence': 80.0},\n                   {'Name': 'Snow', 'Confidence': 85.0},\n               ]\n           },\n       )\n       with stub:\n           with Client(app.app) as client:\n               event = client.events.generate_s3_event(\n                   bucket='mybucket', key='mykey')\n               response = client.lambda_.invoke('handle_object_created', event)\n               assert response.payload == ['Dog', 'Mountain', 'Snow']\n           stub.assert_no_pending_responses()\n\n\nIn the testcase above, we first tell the stubber what API call we're expecting,\nalong with the parameters we'll send and the response we expect back from the\nRekognition service.  Next we use the ``with stub:`` line to activate our stubs.\nThis also ensures that when our test exits that we'll deactive the stubs for\nthis client.  Now we the ``client.lambda_.invoke`` method is called, our\nstubbed client will return the preconfigured response data instead of making\nan actual API call to the Rekognition service.\n\n\n.. _testing-pytest-fixtures:\n\nPytest Fixtures\n---------------\n\nBoth the Botocore stubber and the Chalice test client are used within\na context manager.  In our previous example, this resulted in multiple\nlevels of nesting, which is required for every test we write.  If you're\nusing pytest as your test framework, you can create\n`test fixtures <https://docs.pytest.org/en/stable/fixture.html>`__ to\nreduce the boiler plate code.  Let's rewrite several of these tests to use\npytest fixtures.\n\nFirst we'll create a test fixture for the Chalice test client:\n\n.. code-block:: python\n\n    import app\n    from pytest import fixture\n    from chalice.test import Client\n\n    @fixture\n    def test_client():\n        with Client(app.app) as client:\n            yield client\n\nNow our original tests for the ``foo`` and ``bar`` Lambda functions\nfrom the :ref:`testing-lambda-functions` section can be rewritten\nto use this fixture:\n\n.. code-block:: python\n\n   def test_foo_function(test_client):\n       result = test_client.lambda_.invoke('foo')\n       assert result.payload == {'hello': 'world'}\n\n   def test_bar_function(test_client):\n       result = test_client.lambda_.invoke(\n           'bar', {'my': 'event'})\n       assert result.payload == {'event': {'my': 'event'}}\n\nWe can also create a fixture for the botocore stubber.  This allows us\nto rewrite the ``test_calls_rekognition()`` test from the\n:ref:`previous section <testing-boto3-client-calls>` in a more simplified\nmanner.  Below is the entire test file using both the botocore and Chalice\ntest client fixtures:\n\n.. code-block:: python\n\n    import app\n    from pytest import fixture\n    from chalice.test import Client\n\n\n    @fixture\n    def test_client():\n        with Client(app.app) as client:\n            yield client\n\n\n    @fixture\n    def rekognition_stub():\n        client = app.get_rekognition_client()\n        stub = Stubber(client)\n        with stub:\n            yield stub\n\n\n    def test_calls_rekognition(test_client, rekognition_stub):\n        rekognition_stub.add_response(\n            'detect_labels',\n            expected_params={\n                'Image': {\n                    'S3Object': {\n                        'Bucket': 'mybucket',\n                        'Name': 'mykey',\n                    }\n                },\n                'MinConfidence': 50.0,\n            },\n            service_response={\n                'Labels': [\n                    {'Name': 'Dog', 'Confidence': 75.0},\n                    {'Name': 'Mountain', 'Confidence': 80.0},\n                    {'Name': 'Snow', 'Confidence': 85.0},\n                ]\n            },\n        )\n        event = test_client.events.generate_s3_event(\n            bucket='mybucket', key='mykey')\n        response = test_client.lambda_.invoke('handle_object_created', event)\n        assert response.payload == ['Dog', 'Mountain', 'Snow']\n        stub.assert_no_pending_responses()\n\n\nNext Steps\n----------\n\nFor reference documentation on the methods and attributes of the Chalice test\nclient, see the :ref:`test client <testing-api>` section in the API\ndocumentation.\n"
  },
  {
    "path": "docs/source/topics/tf.rst",
    "content": "Terraform Support\n=================\n\nWhen you run ``chalice deploy``, chalice will deploy your application using the\n`AWS SDK for Python <http://boto3.readthedocs.io/en/docs/>`__.  Chalice also\nprovides functionality that allows you to manage deployments yourself using\nterraform.  This is provided via the ``chalice package --pkg-format terraform``\ncommand.\n\nWhen you run this command, chalice will generate the AWS Lambda\ndeployment package that contains your application and a `Terraform\n<https://www.terraform.io>`__ configuration file. You can then use the\nterraform cli to deploy your chalice application.\n\nConsiderations\n--------------\n\nUsing the ``chalice package`` command is useful when you don't want to\nuse ``chalice deploy`` to manage your deployments.  There's several reasons\nwhy you might want to do this:\n\n* You have pre-existing infrastructure and tooling set up to manage\n  Terraform deployments.\n* You want to integrate with other Terraform resources to manage all\n  your application resources, including resources outside of your\n  chalice app.\n* You'd like to integrate with `AWS CodePipeline\n  <https://aws.amazon.com/codepipeline/>`__ to automatically deploy\n  changes when you push to a git repo.\n\nKeep in mind that you can't switch between ``chalice deploy`` and\n``chalice package`` + Terraform for deploying your app.\n\nIf you choose to use ``chalice package`` and Terraform to deploy\nyour app, you won't be able to switch back to ``chalice deploy``.\nRunning ``chalice deploy`` would create an entirely new set of AWS\nresources (API Gateway Rest API, AWS Lambda function, etc).\n\nExample\n-------\n\nIn this example, we'll create a chalice app and deploy it using\nthe AWS CLI.\n\nFirst install the necessary packages::\n\n    $ virtualenv /tmp/venv\n    $ . /tmp/venv/bin/activate\n    $ pip install chalice awscli\n    $ chalice new-project test-tf-deploy\n    $ cd test-tf-deploy\n\nAt this point we've installed chalice and the AWS CLI and we have\na basic app created locally.  Next we'll run the ``package`` command::\n\n    $ chalice package --pkg-format terraform /tmp/packaged-app/\n    Creating deployment package.\n    $ ls -la /tmp/packaged-app/\n    -rw-r--r--   1 j         wheel  3355270 May 25 14:20 deployment.zip\n    -rw-r--r--   1 j         wheel     3068 May 25 14:20 chalice.tf.json\n\n    $ unzip -l /tmp/packaged-app/deployment.zip  | tail -n 5\n        17292  05-25-17 14:19   chalice/app.py\n          283  05-25-17 14:19   chalice/__init__.py\n          796  05-25-17 14:20   app.py\n     --------                   -------\n      9826899                   723 files\n\n\nAs you can see in the above example, the ``package --pkg-format``\ncommand created a directory that contained two files, a\n``deployment.zip`` file, which is the Lambda deployment package, and a\n``chalice.tf.json`` file, which is the Terraform template that can be\ndeployed using Terraform.  Next we're going to use the Terraform CLI\nto deploy our app.\n\nNote terraform will deploy run against all terraform files in this\ndirectory, so we can add additional resources for our application by\nadding terraform additional files here. The Chalice terraform template\nincludes two static data values (`app` and `stage` names) that we can\noptionally use when constructing these additional resources,\nie. `${data.null_data_source.chalice.outputs.app}`\n\nFirst let's run Terraform init to install the AWS Terraform Provider::\n\n    $ cd /tmp/packaged-app\n    $ terraform init\n\nNow we can deploy our app using the ``terraform apply`` command::\n\n  $ terraform apply\n  data.aws_region.chalice: Refreshing state...\n  data.aws_caller_identity.chalice: Refreshing state...\n\n  An execution plan has been generated and is shown below.\n  Resource actions are indicated with the following symbols:\n  + create\n\n  ... (omit plan output)\n\n  Plan: 14 to add, 0 to change, 0 to destroy.\n\n  Do you want to perform these actions?\n    Terraform will perform the actions described above.\n    Only 'yes' will be accepted to approve.\n  Enter a value: yes\n\n  ... (omit apply output)\n\n  Apply complete! Resources: 14 added, 0 changed, 0 destroyed.\n\n  Outputs:\n\n  EndpointURL = https://7bnxriulj5.execute-api.us-east-1.amazonaws.com/dev\n  RestApiId = 7bnxriulj5\n\nThis will take a minute to complete, but once it's done, the endpoint url\nwill be available as an output which we can then curl::\n\n    $ http \"$(terraform output EndpointURL)\"\n    HTTP/1.1 200 OK\n    Connection: keep-alive\n    Content-Length: 18\n    Content-Type: application/json\n    ...\n\n    {\n        \"hello\": \"world\"\n    }\n"
  },
  {
    "path": "docs/source/topics/views.rst",
    "content": "Views\n=====\n\nA view function in chalice is the function attached to an\n``@app.route()`` decorator.  In the example below, ``index``\nis the view function:\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = Chalice(app_name='helloworld')\n\n\n    @app.route('/')\n    def index():\n        return {'view': 'index'}\n\n\nView Function Parameters\n------------------------\n\nA view function's parameters correspond to the number of captured\nURL parameters specified in the ``@app.route`` call.  In the example above,\nthe route ``/`` specifies no captured parameters so the ``index`` view\nfunction accepts no parameters.  However, in the view function below,\na single URL parameter, ``{city}`` is specified, so the view function\nmust accept a single parameter:\n\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = Chalice(app_name='helloworld')\n\n\n    @app.route('/cities/{city}')\n    def index(city):\n        return {'city': city}\n\n\nThis indicates that the value of ``{city}`` is variable, and whatever\nvalue is provided in the URL is passed to the ``index`` view function.\nFor example::\n\n    GET /cities/seattle   --> index('seattle')\n    GET /cities/portland  --> index('portland')\n\n\nIf you want to access any other metadata of the incoming HTTP request,\nyou can use the ``app.current_request`` property, which is an instance of\nthe the :class:`Request` class.\n\n\nView Function Return Values\n---------------------------\n\nThe response returned back to the client depends on the behavior\nof the view function.  There are several options available:\n\n* Returning an instance of :class:`Response`.  This gives you\n  complete control over what gets returned back to the customer.\n* A ``bytes`` type response body must have a ``Content-Type`` header value\n  that is present in the ``app.api.binary_types`` list in order to be handled\n  properly.\n* Any other return value will be serialized as JSON and sent back\n  as the response body with content type ``application/json``.\n* Any subclass of ``ChaliceViewError`` will result in an HTTP\n  response being returned with the status code associated with that\n  response, and a JSON response body containing a ``Code`` and a ``Message``.\n  This is discussed in more detail below.\n* Any other exception raised will result in a 500 HTTP response.\n  The body of that response depends on whether debug mode is enabled.\n\n\n.. _view-error-handling:\n\nError Handling\n--------------\n\nChalice provides a built in set of exception classes that map to common\nHTTP errors including:\n\n* ``BadRequestError``- returns a status code of 400\n* ``UnauthorizedError``- returns a status code of 401\n* ``ForbiddenError``- returns a status code of 403\n* ``NotFoundError``- returns a status code of 404\n* ``ConflictError``- returns a status code of 409\n* ``TooManyRequestsError``- returns a status code of 429\n* ``ChaliceViewError``- returns a status code of 500\n\nYou can raise these anywhere in your view functions and chalice will convert\nthese to the appropriate HTTP response.  The default chalice error responses\nwill send the error back as ``application/json`` with the response body\ncontaining a ``Code`` corresponding to the exception class name and a\n``Message`` key corresponding to the string provided when the exception\nwas instantiated.  For example:\n\n.. code-block:: python\n\n    from chalice import Chalice\n    from chalice import BadRequestError\n\n    app = Chalice(app_name=\"badrequest\")\n\n    @app.route('/badrequest')\n    def badrequest():\n        raise BadRequestError(\"This is a bad request\")\n\n\nThis view function will generate the following HTTP response::\n\n    $ http https://endpoint/api/badrequest\n    HTTP/1.1 400 Bad Request\n\n    {\n        \"Code\": \"BadRequestError\",\n        \"Message\": \"This is a bad request\"\n    }\n\n\nIn addition to the built in chalice exceptions, you can use the\n:class:`Response` class to customize the HTTP errors if you prefer to\neither not have JSON error responses or customize the JSON response body\nfor errors.  For example:\n\n.. code-block:: python\n\n    from chalice import Chalice, Response\n\n    app = Chalice(app_name=\"badrequest\")\n\n    @app.route('/badrequest')\n    def badrequest():\n        return Response(body='Plain text error message',\n                        headers={'Content-Type': 'text/plain'},\n                        status_code=400)\n\n\n\nSpecifying HTTP Methods\n-----------------------\n\nSo far, our examples have only allowed GET requests. It's actually possible\nto support additional HTTP methods. Here's an example of a view function that\nsupports PUT:\n\n.. code-block:: python\n\n    @app.route('/resource/{value}', methods=['PUT'])\n    def put_test(value):\n        return {\"value\": value}\n\nWe can test this method using the ``http`` command::\n\n    $ http PUT https://endpoint/api/resource/foo\n    HTTP/1.1 200 OK\n\n    {\n        \"value\": \"foo\"\n    }\n\nNote that the ``methods`` kwarg accepts a list of methods.  Your view function\nwill be called when any of the HTTP methods you specify are used for the\nspecified resource.  For example:\n\n.. code-block:: python\n\n    @app.route('/myview', methods=['POST', 'PUT'])\n    def myview():\n        pass\n\nThe above view function will be called when either an HTTP POST or\nPUT is sent to ``/myview`` as shown below::\n\n    POST /myview   --> myview()\n    PUT /myview  --> myview()\n\nAlternatively if you do not want to share the same view function across\nmultiple HTTP methods for the same route url, you may define separate view\nfunctions to the same route url but have the view functions differ by\nHTTP method. For example:\n\n.. code-block:: python\n\n    @app.route('/myview', methods=['POST'])\n    def myview_post():\n        pass\n\n    @app.route('/myview', methods=['PUT'])\n    def myview_put():\n        pass\n\nThis setup will route all HTTP POST's to ``/myview`` to the ``myview_post()``\nview function and route all HTTP PUT's to ``/myview`` to the ``myview_put()``\nview function as shown below::\n\n    POST /myview   --> myview_post()\n    PUT /myview  --> myview_put()\n\nIf you do chose to use separate view functions for the same route path, it is\nimportant to know:\n\n* View functions that share the same route cannot have the same names.\n  For example, two view functions that both share the same route path cannot\n  both be named ``view()``.\n\n* View functions that share the same route cannot overlap in supported HTTP\n  methods. For example if two view functions both share the same route path,\n  they both cannot contain ``'PUT'`` in their route ``methods`` list.\n\n* View functions that share the same route path and have CORS configured cannot\n  have differing CORS configuration. For example, if two view functions that\n  both share the same route path, the route configuration for one of the\n  view functions cannot set ``cors=True`` while having the route\n  configuration of the other view function be set to\n  ``cors=app.CORSConfig(allow_origin='https://foo.example.com')``.\n\n\nBinary Content\n--------------\n\nChalice supports binary payloads through its ``app.api.binary_types`` list. Any\ntype in this list is considered a binary ``Content-Type``. Whenever a request\nwith a ``Content-Type`` header is encountered that matches an entry in the\n``binary_types`` list, its body will be available as a ``bytes`` type on the\nproperty ``app.current_request.raw_body``. Similarly, in order to send binary\ndata back in a response, simply set your ``Content-Type`` header to something\npresent in the ``binary_types`` list. Note that you can override the default\ntypes by modifying the ``app.api.binary_types`` list at the module level.\n\nHere is an example app which simply echoes back binary content:\n\n.. code-block:: python\n\n   from chalice import Chalice, Response\n\n   app = Chalice(app_name=\"binary-response\")\n\n   @app.route('/bin-echo', methods=['POST'],\n              content_types=['application/octet-stream'])\n   def bin_echo():\n       raw_request_body = app.current_request.raw_body\n       return Response(body=raw_request_body,\n                       status_code=200,\n                       headers={'Content-Type': 'application/octet-stream'})\n\nYou can see this app echo back binary data sent to it::\n\n  $ echo -n -e \"\\xFE\\xED\" | http POST $(chalice url)bin-echo \\\n    Accept:application/octet-stream Content-Type:application/octet-stream | xxd\n  0000000: feed                                     ..\n\nNote that both the ``Accept`` and ``Content-Type`` headers are required. If\nyou fail to set the ``Content-Type`` header on the request will result in a\n``415 UnsupportedMediaType`` error. Care must be taken when configuring what\n``content_types`` a route accepts, they must all be valid binary types, or they\nmust all be non-binary types. The ``Accept`` header must also be set if the\ndata returned is to be the raw binary, if is omitted the call return a ``400``\nBad Request response.\n\nFor example, here is the same call as above without the ``Accept`` header::\n\n  $ echo -n -e \"\\xFE\\xED\" | http POST  $(chalice url)bin-echo \\\n    Content-Type:application/octet-stream\n  HTTP/1.1 400 Bad Request\n  Connection: keep-alive\n  Content-Length: 270\n  Content-Type: application/json\n  Date: Sat, 27 May 2017 07:09:51 GMT\n\n  {\n    \"Code\": \"BadRequest\",\n    \"Message\": \"Request did not specify an Accept header with\n      application/octet-stream, The response has a Content-Type of\n      application/octet-stream. If a response has a binary Content-Type then\n      the request must specify an Accept header that matches.\"\n  }\n\n\n\nUsage Recommendations\n---------------------\n\nIf you want to return a JSON response body, just return the corresponding\npython types directly.  You don't need to use the :class:`Response` class.\nChalice will automatically convert this to a JSON HTTP response as a\nconvenience for you.\n\nUse the :class:`Response` class when you want to return non-JSON content, or\nwhen you want to inject custom HTTP headers to your response.\n\nFor errors, raise the built in ``ChaliceViewError`` subclasses (e.g\n``BadRequestError``, ``NotFoundError``, ``ConflictError`` etc)  when you\nwant to return a HTTP error response with a preconfigured JSON body containing\na ``Code`` and ``Message``.\n\nUse the :class:`Response` class when you want to customize the error responses\nto either return a different JSON error response body, or to return an HTTP\nresponse that's not ``application/json``.\n"
  },
  {
    "path": "docs/source/topics/websockets.rst",
    "content": "Websockets\n==========\n\n.. warning::\n\n  Websockets are considered an experimental API.  You'll need to opt-in\n  to this feature using the ``WEBSOCKETS`` feature flag:\n\n  .. code-block:: python\n\n    app = Chalice('myapp')\n    app.experimental_feature_flags.update([\n        'WEBSOCKETS'\n    ])\n\n  See :doc:`experimental` for more information.\n\n\nChalice supports websockets through integration with an API Gateway Websocket\nAPI. If any of the decorators are present in a Chalice app, then an API\nGateway Websocket API will be deployed and wired to Lambda Functions.\n\n\nResponding to websocket events\n------------------------------\n\nIn a Chalice app the websocket API is accessed through the three decorators\n``on_ws_connect``, ``on_ws_message``, ``on_ws_disconnect``. These handle a new\nwebsocket connection, an incoming message on an existing connection, and a\nconnection being cleaned up respectively.\n\nA decorated websocket handler function takes one argument ``event`` with the\ntype :ref:`WebsocketEvent <websocket-api>`. This class allows easy access to\ninformation about the API Gateway Websocket API, and information about the\nparticular socket the handler is being invoked to serve.\n\nBelow is a simple working example application that prints to CloudWatch Logs\nfor each of the events.\n\n.. code-block:: python\n\n    from boto3.session import Session\n    from chalice import Chalice\n\n    app = Chalice(app_name='test-websockets')\n    app.experimental_feature_flags.update([\n        'WEBSOCKETS',\n    ])\n    app.websocket_api.session = Session()\n\n\n    @app.on_ws_connect()\n    def connect(event):\n        print('New connection: %s' % event.connection_id)\n\n\n    @app.on_ws_message()\n    def message(event):\n        print('%s: %s' % (event.connection_id, event.body))\n\n\n    @app.on_ws_disconnect()\n    def disconnect(event):\n        print('%s disconnected' % event.connection_id)\n\n\nSetting the websocket protocol on new connections\n-------------------------------------------------\n\nYou can return a dictionary or an instance of :class:`Response` in the\n``on_ws_connect`` handler, similar to what you'd do in a Rest API.  Note that\nAPI Gateway does not forward arbitrary headers or a response body back to the\nclient, so this is primarily used to set a ``Sec-WebSocket-Protocol`` header\nvalue.\n\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = Chalice(app_name='test-websockets')\n    app.experimental_feature_flags.update([\n        'WEBSOCKETS',\n    ])\n\n\n    @app.on_ws_connect()\n    def connect(event):\n        print('New connection: %s' % event.connection_id)\n        # We don't need to explicitly set a statusCode.\n        return {\n          'headers': {'Sec-WebSocket-Protocol': 'My-Protocol'},\n        }\n\n\nYou don't need to explicitly set a ``statusCode`` if you return a\ndictionary from the ``on_ws_connect`` header, but if want to return one you\nshould **not** set the status code to ``101``.  API Gateway will automatically\ndo this for you.  For successful connection handling you should return a\n``200`` status code if you want to explicitly set a ``statusCode``.\n\n\nSending a message over a websocket\n----------------------------------\n\nTo send a message to a websocket client Chalice, use the\n:ref:`app.websocket_api.send() <websocket-send>` method. This method will work in any\nof the decorated functions outlined in the above section.\n\nTwo pieces of information are needed to send a message. The identifier of the\nwebsocket, and the contents for the message. Below is a simple example that\nwhen it receives a message, it sends back the message ``\"I got your message!\"``\nover the same socket.\n\n.. code-block:: python\n\n    from boto3.session import Session\n    from chalice import Chalice\n\n    app = Chalice(app_name='test-websockets')\n    app.experimental_feature_flags.update([\n        'WEBSOCKETS',\n    ])\n    app.websocket_api.session = Session()\n\n\n    @app.on_ws_message()\n    def message(event):\n        app.websocket_api.send(event.connection_id, 'I got your message!')\n\n\nSee :ref:`websocket-tutorial` for completely worked example applications.\n"
  },
  {
    "path": "docs/source/tutorials/basicrestapi.rst",
    "content": "REST API Tutorial\n=================\n\nIn this tutorial, we create a REST API and explore\nChalice features that help us write REST APIs.\n\nInstallation and Configuration\n------------------------------\n\nIf you haven't already setup and configured Chalice, see the\n:doc:`../quickstart` for a step by step guide.  In a nutshell, you can get a\nbasic Chalice app created with::\n\n    $ python3 --version\n    Python 3.7.3\n    $ python3 -m venv venv37\n    $ . venv37/bin/activate\n    $ python3 -m pip install chalice\n    $ chalice new-project helloworld\n    $ cd helloworld\n\n\nURL Parameters\n--------------\n\nThe default template when you run the ``new-project`` generates a sample\nREST API for you:\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = Chalice(app_name='helloworld')\n\n\n    @app.route('/')\n    def index():\n        return {'hello': 'world'}\n\nWe're going to make a few changes to our ``app.py`` file that\ndemonstrate the capabilities provided by Chalice.\n\nOur application so far has a single view that allows you to make\nan HTTP GET request to ``/``.  Now let's suppose we want to capture\nparts of the URI:\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = Chalice(app_name='helloworld')\n\n    CITIES_TO_STATE = {\n        'seattle': 'WA',\n        'portland': 'OR',\n    }\n\n\n    @app.route('/')\n    def index():\n        return {'hello': 'world'}\n\n    @app.route('/cities/{city}')\n    def state_of_city(city):\n        return {'state': CITIES_TO_STATE[city]}\n\n\nIn the example above, we've now added a ``state_of_city`` view that allows\na user to specify a city name.  The view function takes the city\nname and returns the name of the state the city is in.  Notice that the\n``@app.route`` decorator has a URL pattern of ``/cities/{city}``.  This\nmeans that the value of ``{city}`` is captured and passed to the view\nfunction.  You can also see that the ``state_of_city`` takes a single\nargument.  This argument is the name of the city provided by the user.\nFor example::\n\n    GET /cities/seattle   --> state_of_city('seattle')\n    GET /cities/portland  --> state_of_city('portland')\n\nNow that we've updated our ``app.py`` file with this new view function,\nlet's redeploy our application.  You can run ``chalice deploy`` from\nthe ``helloworld`` directory and it will deploy your application::\n\n    $ chalice deploy\n\nLet's try it out.  Note the examples below use the ``http`` command from the\n``httpie`` package.  You can install this using ``pip install httpie``::\n\n    $ http https://endpoint/api/cities/seattle\n    HTTP/1.1 200 OK\n\n    {\n        \"state\": \"WA\"\n    }\n\n    $ http https://endpoint/api/cities/portland\n    HTTP/1.1 200 OK\n\n    {\n        \"state\": \"OR\"\n    }\n\n\nNotice what happens if we try to request a city that's not in our\n``CITIES_TO_STATE`` map::\n\n    $ http https://endpoint/api/cities/vancouver\n    HTTP/1.1 500 Internal Server Error\n    Content-Type: application/json\n    X-Cache: Error from cloudfront\n\n    {\n        \"Code\": \"ChaliceViewError\",\n        \"Message\": \"An internal server error occurred.\"\n    }\n\n\nIn the next section, we'll see how to fix this and provide better\nerror messages.\n\n\nError Messages\n--------------\n\nIn the example above, you'll notice that when our app raised\nan uncaught exception, a 500 internal server error was returned.\n\nIn this section, we're going to show how you can debug and improve\nthese error messages.\n\nThe first thing we're going to look at is how we can debug this\nissue.  By default, debugging is turned off, but you can\nenable debugging to get more information:\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = Chalice(app_name='helloworld')\n    app.debug = True\n\n\nThe ``app.debug = True`` enables debugging for your app.\nSave this file and redeploy your changes::\n\n    $ chalice deploy\n    ...\n    https://endpoint/api/\n\nNow, when you request the same URL that returned an internal\nserver error, you'll get back the original stack trace::\n\n    $ http https://endpoint/api/cities/vancouver\n    Traceback (most recent call last):\n      File \"/var/task/chalice/app.py\", line 304, in _get_view_function_response\n        response = view_function(*function_args)\n      File \"/var/task/app.py\", line 18, in state_of_city\n        return {'state': CITIES_TO_STATE[city]}\n    KeyError: u'vancouver'\n\n\nWe can see that the error is caused from an uncaught ``KeyError`` resulting\nfrom trying to access the ``vancouver`` key.\n\nNow that we know the error, we can fix our code.  What we'd like to do is\ncatch this exception and instead return a more helpful error message\nto the user.  Here's the updated code:\n\n.. code-block:: python\n\n    from chalice import BadRequestError\n\n    @app.route('/cities/{city}')\n    def state_of_city(city):\n        try:\n            return {'state': CITIES_TO_STATE[city]}\n        except KeyError:\n            raise BadRequestError(\"Unknown city '%s', valid choices are: %s\" % (\n                city, ', '.join(CITIES_TO_STATE.keys())))\n\n\nSave and deploy these changes::\n\n    $ chalice deploy\n    $ http https://endpoint/api/cities/vancouver\n    HTTP/1.1 400 Bad Request\n\n    {\n        \"Code\": \"BadRequestError\",\n        \"Message\": \"Unknown city 'vancouver', valid choices are: portland, seattle\"\n    }\n\nWe can see now that we have received a ``Code`` and ``Message`` key, with the\nmessage being the value we passed to ``BadRequestError``.  Whenever you raise a\n``BadRequestError`` from your view function, the framework will return an HTTP\nstatus code of 400 along with a JSON body with a ``Code`` and ``Message``.\nThere are a few additional exceptions you can raise from your python code::\n\n* BadRequestError - return a status code of 400\n* UnauthorizedError - return a status code of 401\n* ForbiddenError - return a status code of 403\n* NotFoundError - return a status code of 404\n* ConflictError - return a status code of 409\n* UnprocessableEntityError - return a status code of 422\n* TooManyRequestsError - return a status code of 429\n* ChaliceViewError - return a status code of 500\n\nYou can import these directly from the ``chalice`` package:\n\n.. code-block:: python\n\n    from chalice import UnauthorizedError\n\n\nAdditional Routing\n------------------\n\nSo far, our examples have only allowed GET requests.\nIt's actually possible to support additional HTTP methods.\nHere's an example of a view function that supports PUT:\n\n.. code-block:: python\n\n    @app.route('/resource/{value}', methods=['PUT'])\n    def put_test(value):\n        return {\"value\": value}\n\nWe can test this method using the ``http`` command::\n\n    $ http PUT https://endpoint/api/resource/foo\n    HTTP/1.1 200 OK\n\n    {\n        \"value\": \"foo\"\n    }\n\nNote that the ``methods`` kwarg accepts a list of methods.  Your view function\nwill be called when any of the HTTP methods you specify are used for the\nspecified resource.  For example:\n\n.. code-block:: python\n\n    @app.route('/myview', methods=['POST', 'PUT'])\n    def myview():\n        pass\n\nThe above view function will be called when either an HTTP POST or\nPUT is sent to ``/myview``.\n\nAlternatively if you do not want to share the same view function across\nmultiple HTTP methods for the same route url, you may define separate view\nfunctions to the same route url but have the view functions differ by\nHTTP method. For example:\n\n.. code-block:: python\n\n    @app.route('/myview', methods=['POST'])\n    def myview_post():\n        pass\n\n    @app.route('/myview', methods=['PUT'])\n    def myview_put():\n        pass\n\nThis setup routes HTTP POST requests to ``/myview`` to the ``myview_post()``\nview function and routes HTTP PUT requests to ``/myview`` to the ``myview_put()``\nview function. It is also important to note that the view functions\n**must** have unique names. For example, both view functions cannot be\nnamed ``myview()``.\n\nIn the next section we'll go over how you can introspect the given request\nin order to differentiate between various HTTP methods.\n\n\nRequest Metadata\n----------------\n\nIn the examples above, you saw how to create a view function that supports\nan HTTP PUT request as well as a view function that supports both POST and\nPUT via the same view function.  However, there's more information we\nmight need about a given request:\n\n* In a PUT/POST, you frequently send a request body.  We need some\n  way of accessing the contents of the request body.\n* For view functions that support multiple HTTP methods, we'd like\n  to detect which HTTP method was used so we can have different\n  code paths for PUTs vs. POSTs.\n\nAll of this and more is handled by the current request object that the\n``chalice`` library makes available to each view function when it's called.\n\nLet's see an example of this.  Suppose we want to create a view function\nthat allowed you to PUT data to an object and retrieve that data\nvia a corresponding GET.  We could accomplish that with the\nfollowing view function:\n\n.. code-block:: python\n\n    from chalice import NotFoundError\n\n    OBJECTS = {\n    }\n\n    @app.route('/objects/{key}', methods=['GET', 'PUT'])\n    def myobject(key):\n        request = app.current_request\n        if request.method == 'PUT':\n            OBJECTS[key] = request.json_body\n        elif request.method == 'GET':\n            try:\n                return {key: OBJECTS[key]}\n            except KeyError:\n                raise NotFoundError(key)\n\n\nSave this in your ``app.py`` file and rerun ``chalice deploy``.\nNow, you can make a PUT request to ``/objects/your-key`` with a request\nbody, and retrieve the value of that body by making a subsequent\n``GET`` request to the same resource.  Here's an example of its usage::\n\n    # First, trying to retrieve the key will return a 404.\n    $ http GET https://endpoint/api/objects/mykey\n    HTTP/1.1 404 Not Found\n\n    {\n        \"Code\": \"NotFoundError\",\n        \"Message\": \"mykey\"\n    }\n\n    # Next, we'll create that key by sending a PUT request.\n    $ echo '{\"foo\": \"bar\"}' | http PUT https://endpoint/api/objects/mykey\n    HTTP/1.1 200 OK\n\n    null\n\n    # And now we no longer get a 404, we instead get the value we previously\n    # put.\n    $ http GET https://endpoint/api/objects/mykey\n    HTTP/1.1 200 OK\n\n    {\n        \"mykey\": {\n            \"foo\": \"bar\"\n        }\n    }\n\nYou might see a problem with storing the objects in a module level\n``OBJECTS`` variable.  We address this in the next section.\n\nThe ``app.current_request`` object is an instance of the :class:`Request`\nclass, which also has the following properties.\n\n* ``current_request.query_params`` - A dict of the query params.\n* ``current_request.headers`` - A dict of the request headers.\n* ``current_request.uri_params`` - A dict of the captured URI params.\n* ``current_request.method`` -  The HTTP method (as a string).\n* ``current_request.json_body`` - The parsed JSON body.\n* ``current_request.raw_body`` - The raw HTTP body as bytes.\n* ``current_request.context`` - A dict of additional context information\n* ``current_request.stage_vars`` - Configuration for the API Gateway stage\n\nThe ``current_request`` object also has a ``to_dict`` method, which returns all\nthe information about the current request as a dictionary.  Let's use this\nmethod to write a view function that returns everything it knows about the\nrequest:\n\n.. code-block:: python\n\n    @app.route('/introspect')\n    def introspect():\n        return app.current_request.to_dict()\n\n\nSave this to your ``app.py`` file and redeploy with ``chalice deploy``.\nHere's an example of hitting the ``/introspect`` URL.  Note how we're\nsending a query string as well as a custom ``X-TestHeader`` header::\n\n\n    $ http 'https://endpoint/api/introspect?query1=value1&query2=value2' 'X-TestHeader: Foo'\n    HTTP/1.1 200 OK\n\n    {\n        \"context\": {\n            \"apiId\": \"apiId\",\n            \"httpMethod\": \"GET\",\n            \"identity\": {\n                \"accessKey\": null,\n                \"accountId\": null,\n                \"apiKey\": null,\n                \"caller\": null,\n                \"cognitoAuthenticationProvider\": null,\n                \"cognitoAuthenticationType\": null,\n                \"cognitoIdentityId\": null,\n                \"cognitoIdentityPoolId\": null,\n                \"sourceIp\": \"1.1.1.1\",\n                \"userAgent\": \"HTTPie/0.9.3\",\n                \"userArn\": null\n            },\n            \"requestId\": \"request-id\",\n            \"resourceId\": \"resourceId\",\n            \"resourcePath\": \"/introspect\",\n            \"stage\": \"dev\"\n        },\n        \"headers\": {\n            \"accept\": \"*/*\",\n            ...\n            \"x-testheader\": \"Foo\"\n        },\n        \"method\": \"GET\",\n        \"query_params\": {\n            \"query1\": \"value1\",\n            \"query2\": \"value2\"\n        },\n        \"raw_body\": null,\n        \"stage_vars\": null,\n        \"uri_params\": null\n    }\n\n\nRequest Content Types\n---------------------\n\nThe default behavior of a view function supports\na request body of ``application/json``.  When a request is\nmade with a ``Content-Type`` of ``application/json``, the\n``app.current_request.json_body`` attribute is automatically\nset for you.  This value is the parsed JSON body.\n\nYou can also configure a view function to support other\ncontent types.  You can do this by specifying the\n``content_types`` parameter value to your ``app.route``\nfunction.  This parameter is a list of acceptable content\ntypes.  Here's an example of this feature:\n\n.. code-block:: python\n\n    import sys\n\n    from chalice import Chalice\n    from urllib.parse import urlparse, parse_qs\n\n    app = Chalice(app_name='helloworld')\n\n\n    @app.route('/', methods=['POST'],\n               content_types=['application/x-www-form-urlencoded'])\n    def index():\n        parsed = parse_qs(app.current_request.raw_body.decode())\n        return {\n            'states': parsed.get('states', [])\n        }\n\nThere's a few things worth noting in this view function.\nFirst, we've specified that we only accept the\n``application/x-www-form-urlencoded`` content type.  If we\ntry to send a request with ``application/json``, we'll now\nget a ``415 Unsupported Media Type`` response::\n\n    $ http POST https://endpoint/api/ states=WA states=CA --debug\n    ...\n    >>> requests.request(**{'allow_redirects': False,\n     'headers': {'Accept': 'application/json',\n                 'Content-Type': 'application/json',\n    ...\n\n\n    HTTP/1.1 415 Unsupported Media Type\n\n    {\n        \"message\": \"Unsupported Media Type\"\n    }\n\nIf we use the ``--form`` argument, we can see the\nexpected behavior of this view function because ``httpie`` sets the\n``Content-Type`` header to ``application/x-www-form-urlencoded``::\n\n    $ http --form POST https://endpoint/api/formtest states=WA states=CA --debug\n    ...\n    >>> requests.request(**{'allow_redirects': False,\n     'headers': {'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',\n    ...\n\n    HTTP/1.1 200 OK\n    {\n        \"states\": [\n            \"WA\",\n            \"CA\"\n        ]\n    }\n\nThe second thing worth noting is that ``app.current_request.json_body``\n**is only available for the application/json content type.**\nIn our example above, we used ``app.current_request.raw_body`` to access\nthe raw body bytes:\n\n.. code-block:: python\n\n    parsed = parse_qs(app.current_request.raw_body)\n\n``app.current_request.json_body`` is set to ``None`` whenever the\n``Content-Type`` is not ``application/json``.  This means that\nyou will need to use ``app.current_request.raw_body`` and parse\nthe request body as needed.\n\n\nCustomizing the HTTP Response\n-----------------------------\n\nThe return value from a chalice view function is serialized as JSON as the\nresponse body returned back to the caller.  This makes it easy to create\nrest APIs that return JSON response bodies.\n\nChalice allows you to control this behavior by returning an instance of\na chalice specific ``Response`` class.  This behavior allows you to:\n\n* Specify the status code to return\n* Specify custom headers to add to the response\n* Specify response bodies that are not ``application/json``\n\nHere's an example of this:\n\n.. code-block:: python\n\n    from chalice import Chalice, Response\n\n    app = Chalice(app_name='custom-response')\n\n\n    @app.route('/')\n    def index():\n        return Response(body='hello world!',\n                        status_code=200,\n                        headers={'Content-Type': 'text/plain'})\n\nThis will result in a plain text response body::\n\n    $ http https://endpoint/api/\n    HTTP/1.1 200 OK\n    Content-Length: 12\n    Content-Type: text/plain\n\n    hello world!\n\n\nGZIP compression for JSON\n-------------------------\n\nThe return value from a chalice view function is serialized as JSON as the\nresponse body returned back to the caller.  This makes it easy to create\nrest APIs that return JSON response bodies.\n\nChalice allows you to control this behavior by returning an instance of\na chalice specific ``Response`` class.  This behavior allows you to:\n\n* Add ``application/json`` to binary_types\n* Specify the status code to return\n* Specify custom header ``Content-Type: application/json``\n* Specify custom header ``Content-Encoding: gzip``\n\nHere's an example of this:\n\n.. code-block:: python\n\n    import json\n    import gzip\n    from chalice import Chalice, Response\n\n    app = Chalice(app_name='compress-response')\n    app.api.binary_types.append('application/json')\n\n    @app.route('/')\n    def index():\n        blob = json.dumps({'hello': 'world'}).encode('utf-8')\n        payload = gzip.compress(blob)\n        custom_headers = {\n            'Content-Type': 'application/json',\n            'Content-Encoding': 'gzip'\n        }\n        return Response(body=payload,\n                        status_code=200,\n                        headers=custom_headers)\n\n\n\nCORS Support\n------------\n\nYou can specify whether a view supports CORS by adding the\n``cors=True`` parameter to your ``@app.route()`` call.  By\ndefault this value is ``False``. Global CORS can be set by\nsetting ``app.api.cors = True``.\n\n.. code-block:: python\n\n    @app.route('/supports-cors', methods=['PUT'], cors=True)\n    def supports_cors():\n        return {}\n\n\nSetting ``cors=True`` has similar behavior to enabling CORS\nusing the AWS Console.  This includes:\n\n* Injecting the ``Access-Control-Allow-Origin: *`` header to your\n  responses, including all error responses you can return.\n* Automatically adding an ``OPTIONS`` method to support preflighting\n  requests.\n\nThe preflight request will return a response that includes:\n\n* ``Access-Control-Allow-Origin: *``\n* The ``Access-Control-Allow-Methods`` header will return a list of all HTTP\n  methods you've called out in your view function.  In the example above,\n  this will be ``PUT,OPTIONS``.\n* ``Access-Control-Allow-Headers: Content-Type,X-Amz-Date,Authorization,\n  X-Api-Key,X-Amz-Security-Token``.\n\nIf more fine grained control of the CORS headers is desired, set the ``cors``\nparameter to an instance of ``CORSConfig`` instead of ``True``. The\n``CORSConfig`` object can be imported from from the ``chalice`` package it's\nconstructor takes the following keyword arguments that map to CORS headers:\n\n================= ==== ================================\nArgument          Type Header\n================= ==== ================================\nallow_origin      str  Access-Control-Allow-Origin\nallow_headers     list Access-Control-Allow-Headers\nexpose_headers    list Access-Control-Expose-Headers\nmax_age           int  Access-Control-Max-Age\nallow_credentials bool Access-Control-Allow-Credentials\n================= ==== ================================\n\nCode sample defining more CORS headers:\n\n.. code-block:: python\n\n    from chalice import CORSConfig\n    cors_config = CORSConfig(\n        allow_origin='https://foo.example.com',\n        allow_headers=['X-Special-Header'],\n        max_age=600,\n        expose_headers=['X-Special-Header'],\n        allow_credentials=True\n    )\n    @app.route('/custom-cors', methods=['GET'], cors=cors_config)\n    def supports_custom_cors():\n        return {'cors': True}\n\n\nThere's a couple of things to keep in mind when enabling cors for a view:\n\n* An ``OPTIONS`` method for preflighting is always injected.  Ensure that\n  you don't have ``OPTIONS`` in the ``methods=[...]`` list of your\n  view function.\n* Even though the ``Access-Control-Allow-Origin`` header can be set to a\n  string that is a space separated list of origins, this behavior does not\n  work on all clients that implement CORS. You should only supply a single\n  origin to the ``CORSConfig`` object. If you need to supply multiple origins\n  you will need to define a custom handler for it that accepts ``OPTIONS``\n  requests and matches the ``Origin`` header against a whitelist of origins.\n  If the match is successful then return just their ``Origin`` back to them\n  in the ``Access-Control-Allow-Origin`` header.\n\n  Example:\n\n.. code-block:: python\n\n    from chalice import Chalice, Response\n\n    app = Chalice(app_name='multipleorigincors')\n\n    _ALLOWED_ORIGINS = set([\n        'http://allowed1.example.com',\n        'http://allowed2.example.com',\n    ])\n\n    @app.route('/cors_multiple_origins', methods=['GET', 'OPTIONS'])\n    def supports_cors_multiple_origins():\n        method = app.current_request.method\n        if method == 'OPTIONS':\n            headers = {\n                'Access-Control-Allow-Method': 'GET,OPTIONS',\n                'Access-Control-Allow-Origin': ','.join(_ALLOWED_ORIGINS),\n                'Access-Control-Allow-Headers': 'X-Some-Header',\n            }\n            origin = app.current_request.headers.get('origin', '')\n            if origin in _ALLOWED_ORIGINS:\n                headers.update({'Access-Control-Allow-Origin': origin})\n            return Response(\n                body=None,\n                headers=headers,\n            )\n        elif method == 'GET':\n            return 'Foo'\n"
  },
  {
    "path": "docs/source/tutorials/cdk.rst",
    "content": "Deploying with the AWS CDK\n==========================\n\nIn this tutorial, we're going to create a REST API with an Amazon DynamoDB\ntable as our data store.  We'll be using the `AWS Cloud Development Kit (CDK)\n<https://aws.amazon.com/cdk/>`__\nto deploy our application, and we'll show how to use the integration between\nChalice and the CDK in order to build and deploy our application.\n\nBy combining Chalice and the CDK together, you can use Chalice to\nwrite your application code using its familiar, decorator-based APIs, and\nuse the CDK and the full breadth of its construct libraries to create the\nservice infrastructure and resources needed for your application.\nWe'll also see how we can use the Chalice construct to manipulate our\nChalice application using the CDK APIs as well as take resources from\nCDK constructs and map them into our Chalice application.\n\n\nInstallation and Configuration\n------------------------------\n\nThis tutorial requires that both Chalice and the AWS CDK is installed.\nThe CDK is written in Typescript and requires node and npm to be installed.\nSee the `Getting started with the AWS CDK <https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html#getting_started_prerequisites>`__\nfor more details on install the CDK.\n\nFirst, we'll install the CDK.\n\n::\n\n  $ npm install -g aws-cdk\n\nYou should now have a ``cdk`` executable you can run.\n\n::\n\n  $ cdk --version\n  1.83.0 (build 827c5f4)\n\nNext we'll create a Python virtual environment and install Chalice.  Be sure\nto use Python 3.6 or greater.\n\n::\n\n  $ python3 -m venv demo\n  $ . demo/bin/activate\n  $ python3 -m pip install chalice\n  $ chalice --version\n  chalice 1.22.0, python 3.7.8, darwin 19.6.0\n\nCDK integration with Chalice is available as an optional package installation.\nTo install the necessary dependencies run the following command:\n\n::\n\n  $ python3 -m pip install \"chalice[cdkv2]\"\n\n**Note:** Please use CDK version 2, support for CDK version 1 ended\non June 1, 2023. See\n`Working with the AWS CDK in Python Doc <https://docs.aws.amazon.com/cdk/v2/guide/work-with-cdk-python.html>`__\nfor more information.\n\nYou're now ready to create your first Chalice and CDK application.\n\n\nProject Creation\n----------------\n\nTo create a new project we'll use the ``chalice new-project`` command with no\narguments.  Enter a name for your project and select\n``[CDK] REST API with DynamoDB backend`` for the project type.\n\n::\n\n  $ chalice new-project\n\n\n     ___  _  _    _    _     ___  ___  ___\n    / __|| || |  /_\\  | |   |_ _|/ __|| __|\n   | (__ | __ | / _ \\ | |__  | || (__ | _|\n    \\___||_||_|/_/ \\_\\|____||___|\\___||___|\n\n\n  The python serverless microframework for AWS allows\n  you to quickly create and deploy applications using\n  Amazon API Gateway and AWS Lambda.\n\n  Please enter the project name\n  [?] Enter the project name: cdkdemo\n  [?] Select your project type: [CDK] REST API with DynamoDB backend\n     REST API\n     S3 Event Handler\n     Lambda Functions only\n     Legacy REST API Template\n     [CDK] REST API with DynamoDB backend\n\n  Your project has been generated in ./cdkdemo\n\nNext, we'll ``cd`` into the ``cdkdemo`` directory and see what Chalice has\ngenerated.\n\n::\n\n  $ cd cdkdemo\n  $ tree\n  .\n  ├── README.rst\n  ├── infrastructure           # CDK Application\n  │   ├── app.py\n  │   ├── cdk.json\n  │   ├── requirements.txt\n  │   └── stacks\n  │       ├── __init__.py\n  │       └── chaliceapp.py\n  ├── requirements.txt\n  └── runtime                  # Chalice Application\n      ├── app.py\n      └── requirements.txt\n\n\nThere's two top level directories, ``infrastructure`` and ``runtime``, which\ncorrespond to the CDK application and the Chalice application.  The\n``infrastructure`` directory is where we can add additional AWS resources\nneeded by our application, and the ``runtime`` directory is where we write\nour application code for our Lambda functions.  We'll look at these in more\ndetail, but first we'll deploy our application.\n\nIn order to build and deploy our application, we need to install the\ndependencies used by our application.  We can do this by installing the\nrequirements file in the top level directory of our project.\n\n::\n\n  $ python3 -m pip install -r requirements.txt\n\nIf this is your first time using the CDK, you'll need to bootstrap your\naccount, which will deploy an AWS CloudFormation stack that contains\nresources needed to store our application.  You can do this by running the\n``cdk bootstrap`` command from the ``infrastructure`` directory.\n\n\n::\n\n  $ cd infrastructure\n  $ cdk bootstrap\n  Packaging Chalice app for cdkdemo\n  Creating deployment package.\n  The stack cdkdemo already includes a CDKMetadata resource\n   ⏳  Bootstrapping environment aws://12345/us-west-2...\n  CDKToolkit: creating CloudFormation changeset...\n  [██████████████████████████████████████████████████████████] (3/3)\n\n\n   ✅  Environment aws://12345/us-west-2 bootstrapped.\n\nWe can now deploy our application using the ``cdk deploy`` command.  Make sure\nyou're still in the ``infrastructure`` directory.\n\n\n::\n\n  $ cdk deploy\n  Packaging Chalice app for cdkdemo\n  Creating deployment package.\n  Reusing existing deployment package.\n  The stack cdkdemo already includes a CDKMetadata resource\n  This deployment will make potentially sensitive changes according to your current security approval level (--require-approval broadening).\n  Please confirm you intend to make the following modifications:\n\n  ...\n\n  Do you wish to deploy these changes (y/n)? y\n  cdkdemo: deploying...\n  [0%] start: Publishing abcd:current\n  [100%] success: Published abcd:current\n  cdkdemo: creating CloudFormation changeset...\n  [██████████████████████████████████████████████████████████] (10/10)\n\n\n   ✅  cdkdemo\n\n  Outputs:\n  cdkdemo.APIHandlerArn = arn:aws:lambda:us-west-2:12345:function:cdkdemo-APIHandler-C8OLGQT9YIDO\n  cdkdemo.APIHandlerName = cdkdemo-APIHandler-C8OLGQT9YIDO\n  cdkdemo.AppTableName = cdkdemo-AppTable815C50BC-1OPGOPFYODZOJ\n  cdkdemo.EndpointURL = https://abcd.execute-api.us-west-2.amazonaws.com/api/\n  cdkdemo.RestAPIId = abcd\n\n  Stack ARN:\n  arn:aws:cloudformation:us-west-2:12345:stack/cdkdemo/574c4850-1d23-11eb-8cae-0aea264da24f\n\nWe've now deployed a Chalice application powered by the CDK.  We can now test\nour REST API.\n\n\n.. note::\n   If you've Chalice before, you may be familiar with the ``chalice deploy``\n   command.  When we use the AWS CDK to deploy our application we no longer\n   use ``chalice deploy`` and instead we run ``cdk deploy`` from the\n   ``infrastructure/`` directory.  You should not use ``chalice deploy``\n   to deploy your application when using Chalice's CDK integration.\n\nTesting\n-------\n\nTo test our application, we make HTTP requests to our ``EndpointUrl``, which is\nshown as the value for ``cdkdemo.EndpointUrl`` in the output section above.\nWe're using `httpie <https://httpie.io/>`__ to make our HTTP requests from the\ncommand line.\n\n::\n\n  $ python3 -m pip install httpie\n  $ http POST https://abcd.execute-api.us-west-2.amazonaws.com/api/users/ username=jamesls name=James\n  HTTP/1.1 200 OK\n  ...\n\n  {}\n\n  $ http https://abcd.execute-api.us-west-2.amazonaws.com/api/users/jamesls\n  HTTP/1.1 200 OK\n  Content-Type: application/json\n  ...\n\n  {\n      \"name\": \"James\",\n      \"username\": \"jamesls\"\n  }\n\nNow that we have our sample application up and running, let's walk through the\nproject code so we can better understand what's happening.\n\n\nCode Walkthrough\n----------------\n\nThe ``runtime/`` directory contains code where you define your Lambda event\nhandlers (e.g. ``@app.route()``, ``@app.on_s3_event()``, etc.).  When you\ncreate a Chalice application without the CDK, this is normally the root\ndirectory for your application.  You should also see your Chalice config file\nin ``.chalice/config.json``.  The ``infrastructure/`` directory contains the\ndefinitions for the AWS resources used by your application.  This is the\ndirectory structure that would be generated if you were only using the\nCDK and not Chalice.  This is why the combined Chalice/CDK application template\nhas a new top level directory with separate sub directories for the CDK app\nand the Chalice app.\n\nTo better understand how the two applications communicate with each other,\nwe'll examine how the DynamoDB table was added to the application.\n\nFirst, let’s look at the code for our REST API in ``runtime/app.py``.\n\n\n.. code-block:: python\n\n  import os\n  import boto3\n  from chalice import Chalice\n\n\n  app = Chalice(app_name='cdkdemo')\n  dynamodb = boto3.resource('dynamodb')\n  dynamodb_table = dynamodb.Table(os.environ.get('APP_TABLE_NAME', ''))\n\n\n  @app.route('/users', methods=['POST'])\n  def create_user():\n      ...\n\n\n  @app.route('/users/{username}', methods=['GET'])\n  def get_user(username):\n      ...\n\nThe name of the DynamoDB table is passed through an environment variable,\n``APP_TABLE_NAME``.  We then create a ``dynamodb.Table`` resource given this\nname.  This environment variable is generated and mapped in the CDK stack that\nChalice generated for us.  This is located in\n``../infrastructure/stacks/chaliceapp.py``.\n\nLet's look at the contents of the ``../infrastructure/stacks/chaliceapp.py``\nfile now.\n\n\n.. code-block:: python\n\n  import os\n\n  from aws_cdk import (\n      aws_dynamodb as dynamodb,\n      core as cdk\n  )\n  from chalice.cdk import Chalice\n\n\n  RUNTIME_SOURCE_DIR = os.path.join(\n      os.path.dirname(os.path.dirname(__file__)), os.pardir, 'runtime')\n\n\n  class ChaliceApp(cdk.Stack):\n\n      def __init__(self, scope: cdk.Construct, id: str, **kwargs) -> None:\n          super().__init__(scope, id, **kwargs)\n          self.dynamodb_table = self._create_ddb_table()\n          self.chalice = Chalice(\n              self, 'ChaliceApp', source_dir=RUNTIME_SOURCE_DIR,\n              stage_config={\n                  'environment_variables': {\n                      'APP_TABLE_NAME': self.dynamodb_table.table_name\n                  }\n              }\n          )\n          self.dynamodb_table.grant_read_write_data(\n              self.chalice.get_role('DefaultRole')\n          )\n\n      def _create_ddb_table(self):\n          dynamodb_table = dynamodb.Table(\n              self, 'AppTable',\n              partition_key=dynamodb.Attribute(\n                  name='PK', type=dynamodb.AttributeType.STRING),\n              sort_key=dynamodb.Attribute(\n                  name='SK', type=dynamodb.AttributeType.STRING\n              ),\n              removal_policy=cdk.RemovalPolicy.DESTROY)\n          cdk.CfnOutput(self, 'AppTableName',\n                        value=dynamodb_table.table_name)\n          return dynamodb_table\n\n\nOur CDK stack is using the Chalice construct from the ``chalice.cdk``\npackage.  This provides us two benefits.  First, we can generate CDK resources\nand pass them into our Chalice application by mapping environment variables.\nSecond, we can take resources generated in our Chalice application and\nreference them with the CDK API.  For example, we’re generating a DynamoDB\ntable in the ``self._create_ddb_table()`` method, and then mapping it into our\nChalice application by providing a ``stage_config`` override.  This dictionary\nis merged with the existing Chalice configuration located in\n./runtime/.chalice/config.json.  If we want to pass additional values into our\nChalice application we can update the environment_variables dictionary in our\nstage_config.\n\nWe’re also able to retrieve references to our resources in our Chalice\napplication and reference them in our CDK stack.  For example, once we’ve\ncreated our DynamoDB table we also need to grant the IAM role associated with\nyour Lambda function access to this table.  We do this by using the\n``grant_read_write_data`` method on our table resource, and we provide a\nreference to the default role that Chalice creates for us by using the\n``self.chalice.get_role()`` method.\n\n\nNext Steps\n----------\n\n\nFeel free to experiment with this sample app.  Add new resources to your\napplication by updating the ``infrastructure/stacks/chaliceapp.py`` file, map\nCDK resources into your Chalice app through environment variables, and\nredeploy your application by running ``cdk deploy`` from the\n``infrastructure/`` directory.\n"
  },
  {
    "path": "docs/source/tutorials/customdomain.rst",
    "content": "Custom Domain Names\n===================\n\nIn this tutorial, we're going to create a REST API and associate our own custom\ndomain with this REST API.  This allows us to not use the auto-generated domain\nname that API Gateway automatically creates when we deploy our REST API.\n\nInstallation and Configuration\n------------------------------\n\nIf you haven't already setup and configured Chalice, see the\n:doc:`../quickstart` for a step by step guide.  You can run these commands\nto create a basic Chalice app::\n\n    $ python3 --version\n    Python 3.9.22\n    $ python3 -m venv venv39\n    $ . venv39/bin/activate\n    $ python3 -m pip install chalice\n    $ chalice new-project customdomain\n    $ cd customdomain\n\n\nConfigure and Deploy a REGIONAL Endpoint\n----------------------------------------\n\nBefore we configure a custom domain for our REST API, we'll deploy\nour REST API so we can see the auto-generated domain name that API Gateway\ncreates for us.\n\nFirst, we'll change our endpoint type from EDGE (the default) to REGIONAL.\nUpdate your ``.chalice/config.json`` file so it looks like this::\n\n    $ cat .chalice/config.json\n    {\n      \"version\": \"2.0\",\n      \"app_name\": \"customdomain\",\n      \"api_gateway_endpoint_type\": \"REGIONAL\",\n      \"stages\": {\n        \"dev\": {\n          \"api_gateway_stage\": \"api\"\n        }\n      }\n    }\n\nNow we'll deploy our application.  Note the URL that's printed when\nyour application is deployed.\n\n::\n\n    $ chalice deploy\n    Creating deployment package.\n    Creating IAM role: customdomain-dev\n    Creating lambda function: customdomain-dev\n    Creating Rest API\n    Resources deployed:\n      - Lambda ARN: arn:aws:lambda:us-west-2:12345:function:customdomain-dev\n      - Rest API URL: https://qxea58abcd.execute-api.us-west-2.amazonaws.com/api/\n\nYou now have an API up and running using API Gateway and Lambda::\n\n    $ curl https://qxea58abcd.execute-api.us-west-2.amazonaws.com/api/\n    {\"hello\": \"world\"}\n\nThe ``qxea58abcd.execute-api.us-west-2.amazonaws.com`` domain name was\nauto-generated by API Gateway.  Replacing this domain name with our own\ncustom domain name allows us to use simpler and more intuitive URLs that\nwe can provide to our API users.\n\n\nConfiguring a Custom Domain\n---------------------------\n\nIn this tutorial, we're using Amazon Route53 to manage our DNS configuration.\nIf you're using a third-party domain registrar, the steps will be similar, but\nyou will have to create your DNS records using your provider's web interface or\nAPI.\n\nFor this tutorial, we'll configure the domain ``chalice-demo-app.com``.  Be\nsure to replace this value with your own domain name.\n\nCreating a Hosted Zone\n~~~~~~~~~~~~~~~~~~~~~~\n\nFirst, we'll need to create a hosted zone in Route 53.  If you already have\na hosted zone created for your domain, you can skip this step.\n\nWe'll be using the AWS CLI V2 to configure our domain.  You can follow\nthe\n`installation instructions <https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html>`__\nif you don't have the AWS CLI installed.\n\n::\n\n    $ aws route53 create-hosted-zone --name chalice-demo-app.com --caller-reference 12345\n    {\n        \"Location\": \"https://route53.amazonaws.com/2013-04-01/hostedzone/ZABCDEFGABCDEFGLDO822\",\n        \"HostedZone\": {\n            \"Id\": \"/hostedzone/ZABCDEFGABCDEFGLDO822\",\n            \"Name\": \"chalice-demo-app.com.\",\n            \"CallerReference\": \"12345\",\n            \"Config\": {\n                \"PrivateZone\": false\n            },\n            \"ResourceRecordSetCount\": 2\n        },\n        \"ChangeInfo\": {\n            \"Id\": \"/change/C07395431VDLB0CY65VP\",\n            \"Status\": \"PENDING\",\n            \"SubmittedAt\": \"2020-07-21T17:13:54.709000+00:00\"\n        },\n        \"DelegationSet\": {\n            \"NameServers\": [\n                \"ns-123.awsdns-31.net\",\n                \"ns-123.awsdns-05.com\",\n                \"ns-123.awsdns-09.org\",\n                \"ns-123.awsdns-40.co.uk\"\n            ]\n        }\n    }\n\nYou'll need to save the value of the hosted zone id for later.\nFrom the output above the line ``\"Id\": \"/hostedzone/ZABCDEFGABCDEFGLDO822\",``\ncontains our hosted zone id of ``ZABCDEFGABCDEFGLDO822``.  We'll refer\nto this value as ``$OUR_HOSTED_ZONE_ID`` later.\n\nYou'll now need to register the ``\"NameServers\"`` shown in the output above\nwith your domain registrar.\n\n\nCreating an ACM Certificate\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nNow that we have our hosted zone, we'll need to create an ACM certificate\nassociated with this domain.  This is the SSL/TLS certificate that will be\nused when requests are made to our custom domain.  In this example, we'll\ncreate a wildcard certificate for ``*.chalice-demo-app.com``.  Note that\nwe're creating a ``REGIONAL`` endpoint type for our REST API, which means\nthat our ACM certificate **must** be in the same region as our REST API.\nIn this example, we're using ``us-west-2``.  If you're using the default\n``EDGE`` endpoint type, the ACM cert must be in ``us-east-1``.  You can\nexplicitly specify the region using the ``--region`` CLI parameter if needed.\n\n::\n\n    $ aws acm request-certificate --domain-name \"*.chalice-demo-app.com\" \\\n        --validation-method DNS --idempotency-token 12345 \\\n        --options CertificateTransparencyLoggingPreference=DISABLED\n    {\n        \"CertificateArn\": \"arn:aws:acm:us-west-2:0123456789:certificate/578efbda-6bc7-4ae2-9964-6e6c3f58008b\"\n    }\n\nSave the value of ``CertificateArn`` shown in the output above.  We'll\nneed this value when we configure our app to use this custom domain.\n\nBefore we can use this certificate, we need to validate this certificate.\nThis process demonstrates that we own or control the domain name associated\nwith the certificate.\nIn the command above, we used the ``--validation-method DNS``, which\nrequires us to add CNAME records to validate we control our domain name.\nACM supports both `DNS validation <https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-validate-dns.html>`__\nas well as `email validation <https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-validate-email.html>`__.\n\nTo validate our domain, we'll now create the necessary CNAME records in our\nhosted zone using the Route53 API.  First, we need to retrieve the values\nfor our CNAME record.  Be sure to replace the value of ``--certificate-arn``\nwith your own certificate ARN in the command below::\n\n    $ aws acm describe-certificate --certificate-arn arn:aws:acm:us-west-2:0123456789:certificate/578efbda-6bc7-4ae2-9964-6e6c3f58008b \\\n        --query Certificate.DomainValidationOptions[0]\n    {\n        \"DomainName\": \"*.chalice-demo-app.com\",\n        \"ValidationDomain\": \"*.chalice-demo-app.com\",\n        \"ValidationStatus\": \"PENDING_VALIDATION\",\n        \"ResourceRecord\": {\n            \"Name\": \"_1234567891234567897eb5512d9fb554.chalice-demo-app.com.\",\n            \"Type\": \"CNAME\",\n            \"Value\": \"_123456789123456789e7495341c27cd1.jfrzftwwjs.acm-validations.aws.\"\n        },\n        \"ValidationMethod\": \"DNS\"\n    }\n\nNext we'll create a CNAME record for\n``_1234567891234567897eb5512d9fb554.chalice-demo-app.com.`` with a value of\n``_123456789123456789e7495341c27cd1.jfrzftwwjs.acm-validations.aws.``::\n\n    $ aws route53 change-resource-record-sets \\\n        --hosted-zone-id $OUR_HOSTED_ZONE_ID --change-batch \\\n    '{\n      \"Changes\": [\n        {\n          \"Action\": \"CREATE\",\n          \"ResourceRecordSet\": {\n            \"Name\": \"_0073e080112eb8de8c7eb5512d9fb554.chalice-demo-app.com.\",\n            \"Type\": \"CNAME\",\n            \"TTL\": 300,\n            \"ResourceRecords\": [{\"Value\": \"_6e560a5a9831aad210e7495341c27cd1.jfrzftwwjs.acm-validations.aws.\"}]\n          }\n        }\n      ]\n    }'\n\n    # Command output:\n    {\n        \"ChangeInfo\": {\n            \"Id\": \"/change/C0339874QPDDRA8TKT7U\",\n            \"Status\": \"PENDING\",\n            \"SubmittedAt\": \"2020-07-21T17:36:39.902000+00:00\"\n        }\n    }\n\nIt will take a few minutes before ACM validates your domain.  You can\nmove on to the next steps, or if you'd like to wait until the domain is\nvalidated you can use the CLI's ``certificate-validated`` waiter, which\nwill block until the ACM certificate is validated::\n\n    $ aws acm wait certificate-validated \\\n        --certificate-arn arn:aws:acm:us-west-2:0123456789:certificate/578efbda-6bc7-4ae2-9964-6e6c3f58008b\n\n\nChalice App Configuration\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\nNow that we have our hosted zone and ACM certificate created, we can configure\nour Chalice application with our custom domain.  To do so we need to add\n`api_gateway_custom_domain <https://aws.github.io/chalice/topics/configfile.html#api-gateway-custom-domain>`__\nconfiguration option and specify our ACM certificate ARN as well as the our\ncustom domain name.  You're ``.chalice/config.json`` file should look like\nthis:\n\n.. code-block:: json\n\n    {\n      \"version\": \"2.0\",\n      \"app_name\": \"customdomain\",\n      \"api_gateway_endpoint_type\": \"REGIONAL\",\n      \"stages\": {\n        \"dev\": {\n          \"api_gateway_custom_domain\": {\n            \"domain_name\": \"api.chalice-demo-app.com\",\n            \"certificate_arn\": \"arn:aws:acm:us-west-2:0123456789:certificate/578efbda-6bc7-4ae2-9964-6e6c3f58008b\"\n          },\n          \"api_gateway_stage\": \"api\"\n        }\n      }\n    }\n\nWe we rerun the ``chalice deploy`` command you'll notice there's a new\n``Custom domain name:`` line in the output::\n\n    $ chalice deploy\n    Creating deployment package.\n    Updating policy for IAM role: customdomain-dev\n    Updating lambda function: customdomain-dev\n    Updating rest API\n    Creating custom domain name: api.chalice-demo-app.com\n    Creating api mapping: /\n    Resources deployed:\n      - Lambda ARN: arn:aws:lambda:us-west-2:0123456789:function:customdomain-dev\n      - Rest API URL: https://qxea58abcd.execute-api.us-west-2.amazonaws.com/api/\n      - Custom domain name:\n          HostedZoneId: Z1UJRXOUMOOFQ8\n          AliasDomainName: d-6vj4cynstd.execute-api.us-west-2.amazonaws.com\n\nNow that we've configured our Chalice app with our custom domain, there's one\nstep left.  We need to update our DNS configuration to point to our REST API.\n\nTo do this, we'll use the values of ``HostedZoneId`` and ``AliasDomainName``\nin the output above to create an alias record in our hosted zone.\n\nAlias Record Configuration\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYou can run the following command to create an alias record to your REST API.\nNote that there are two different hosted zone id values here.  The value\nspecified as the ``--hosted-zone-id`` value is the ID of our hosted zone\nID (``$OUR_HOSTED_ZONE_ID``) that we created earlier in this example.\nThe value of the ``HostedZoneId`` in the ``AliasTarget`` section is the\nvalue of the ``HostedZoneId`` generated by API Gateway shown in the output\nof ``chalice deploy`` above.\n\n::\n\n    $ aws route53 change-resource-record-sets --hosted-zone-id ZABCDEFGABCDEFGLDO822 --change-batch \\\n    '{\n      \"Changes\": [\n        {\n          \"Action\": \"CREATE\",\n          \"ResourceRecordSet\": {\n            \"Name\": \"api.chalice-demo-app.com\",\n            \"Type\": \"A\",\n            \"AliasTarget\": {\n              \"DNSName\": \"d-6vj4cynstd.execute-api.us-west-2.amazonaws.com\",\n              \"HostedZoneId\": \"Z1UJRXOUMOOFQ8\",\n              \"EvaluateTargetHealth\": false\n            }\n          }\n        }\n      ]\n    }'\n\n    # Command output:\n    {\n        \"ChangeInfo\": {\n            \"Id\": \"/change/C0539657Y0HMX8XBC5EH\",\n            \"Status\": \"PENDING\",\n            \"SubmittedAt\": \"2020-07-21T17:52:34.935000+00:00\"\n        }\n    }\n\n\nVerification\n------------\n\nOur Chalice application is now configured to use our custom domain.\nWe can verify this by making a request to our custom domain.  In this\nexample, this is ``api.chalice-demo-app.com``::\n\n    $ curl -i https://api.chalice-demo-app.com/\n    HTTP/1.1 200 OK\n    Date: Tue, 21 Jul 2020 17:56:00 GMT\n    Content-Type: application/json\n    Content-Length: 17\n    Connection: keep-alive\n    x-amzn-RequestId: 9f33fbb9-6b10-469e-827f-f287199c9bc5\n    x-amz-apigw-id: QCPXoEwPIAMFi8Q=\n    X-Amzn-Trace-Id: Root=1-5f172c30-dccc232932a16a539dfc01b9;Sampled=0\n\n    {\"hello\":\"world\"}\n\n\nNext Steps\n----------\n\nFor more information on configuring custom domains, check out our\n:doc:`topic guide <../topics/domainname>` on custom domains as well\nas the config file reference for the\n:ref:`custom-domain-config-options` and the\n:ref:`custom-domain-ws-config-options` options.\n"
  },
  {
    "path": "docs/source/tutorials/events.rst",
    "content": "Event Sources Tutorial\n======================\n\nIn the :doc:`../quickstart` guide, we looked at how to create a\nREST API using the ``@app.route()`` decorator.  Chalice also has\nadditional decorators that connects your code to specific event sources.\nThis results in your code being invoked when a specific event occurs.\n\nIn this tutorial we'll look at a few examples.\n\nInstallation and Configuration\n------------------------------\n\nIf you haven't already setup and configured Chalice, see the\n:doc:`../quickstart` for a step by step guide.  In a nutshell, you can get a\nbasic Chalice app created with::\n\n    $ python3 --version\n    Python 3.9.22\n    $ python3 -m venv venv39\n    $ . venv39/bin/activate\n    $ python3 -m pip install chalice\n    $ chalice new-project chalice-sns-demo\n    $ cd chalice-sns-demo\n\n\nWe'll also be using the AWS CLI in this tutorial.  You can follow\n`these instructions <https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html>`__\nfor installing the AWS CLI v2.\n\n\nAmazon SNS Topics\n-----------------\n\nIn this first example, we'll create a Chalice application that will\ncall our Lambda function whenever a message is published to an\n`SNS Topic <https://aws.amazon.com/sns/>`__.\n\nFirst, we'll create an SNS topic.  This is what we'll connect to our\nLambda function::\n\n    $ aws sns create-topic --name MyDemoTopic\n    {\n        \"TopicArn\": \"arn:aws:sns:us-west-2:12345:MyDemoTopic\"\n    }\n\nBe sure to save the ``TopicArn`` value for later.  In this example\nthat would be ``arn:aws:sns:us-west-2:12345:MyDemoTopic``.\n\nNext, we'll update the ``app.py`` to create a lambda function that\nconnects to an SNS topic:\n\n.. code-block:: python\n\n    from chalice import Chalice\n\n    app = Chalice(app_name='chalice-sns-demo', debug=True)\n\n    @app.on_sns_message(topic='MyDemoTopic')\n    def handle_sns_message(event):\n        app.log.debug(\"Received message with subject: %s, message: %s\",\n                      event.subject, event.message)\n\nIn the code above, we're using the ``@app.on_sns_message()`` decorator to\nconnect the SNS topic named ``MyDemoTopic`` with the ``handle_sns_message``\nfunction.  Note that we're using the name of the topic and not the\n``TopicArn``.\n\nNow we can deploy our chalice app::\n\n    $ chalice deploy\n    Creating deployment package.\n    Creating IAM role: chalice-demo-sns-dev\n    Creating lambda function: chalice-demo-sns-dev-handle_sns_message\n    Subscribing chalice-demo-sns-dev-handle_sns_message to SNS topic my-demo-topic\n    Resources deployed:\n      - Lambda ARN: arn:aws:lambda:us-west-2:123:function:...\n\nNow we can test our app by publishing a few SNS messages to our topic.\n\n::\n\n    $ aws sns publish --topic-arn arn:aws:sns:us-west-2:12345:MyDemoTopic \\\n        --subject TestSubject --message TestMessage\n    {\n        \"MessageId\": \"abcdefgh-3e56-54bd-a471-72477b5388af\"\n    }\n    $ aws sns publish --topic-arn arn:aws:sns:us-west-2:12345:MyDemoTopic \\\n        --subject TestSubject2 --message TestMessage2\n    {\n        \"MessageId\": \"abcdefgh-3e56-54bd-a471-72477b5388ag\"\n    }\n\nWe should now see log messages showing that our Lambda function was invoked.\nWe can wait for the messages using the ``chalice logs`` command.\n\n::\n\n    $ chalice logs --follow -n handle_sns_message\n    ... 217378 chalice-sns-demo - DEBUG - Received message with subject: TestSubject, message: TestMessage\n    ... 217378 chalice-sns-demo - DEBUG - Received message with subject: TestSubject2, message: TestMessage2\n\nNext Steps\n----------\n\nIn addition to SNS, chalice supports other event sources including Amazon S3,\nAmazon SQS, as well as scheduled events.  You can check out the topic guide\non :doc:`../topics/events` for more details.\n\nCleaning Up\n-----------\n\nOnce you're done experimenting you can clean up by deleting the Chalice\napp and deleting the SNS topic::\n\n    $ chalice delete\n    Deleting function: arn:aws:lambda:us-west-2:21345:function:chalice-sns-demo...\n    Deleting IAM role: chalice-sns-demo-dev\n    $ aws sns delete-topic --topic-arn arn:aws:sns:us-west-2:12345:MyDemoTopic\n"
  },
  {
    "path": "docs/source/tutorials/index.rst",
    "content": "Tutorials\n=========\n\nThese step-by-step tutorials show you how to use various features of Chalice.\nThese are perfect if you're new to Chalice and want to learn what Chalice can\ndo.  If you want more complete, real-world examples, you can check out\nour :doc:`../samples/index`.\n\nRest API Tutorials\n------------------\n\n:doc:`basicrestapi`\n  This tutorial walks you through creating a REST API in\n  Chalice.  It covers features such as routing, URL parameters, error handling,\n  etc.\n:doc:`customdomain`\n  In this tutorial, we show you how to configure\n  a REST API with your own custom domain name.\n\n\n.. _websocket-tutorial:\n\nWebsocket Tutorials\n-------------------\n\n\n:doc:`wsecho`\n  Learn the basics of creating websocket APIs in Chalice.  This tutorial\n  creates an echo server that echoes back any message that the client sends to\n  the websocket server.\n:doc:`wschat`\n  In this more complete example, learn how to create a basic chat application\n  based on websockets.\n\n\nEvent Source Tutorials\n----------------------\n\n\n:doc:`events`\n  This tutorial shows you how to create an event handler\n  that's triggered whenever a message is published to an SNS topic.\n\n.. toctree::\n   :hidden:\n   :glob:\n\n   *\n\nAWS CDK Tutorials\n-----------------\n\n:doc:`cdk`\n  This tutorial walks you through creating a REST API with a DynamoDB data\n  store that's deployed using the AWS CDK.  It shows you how you can combine\n  the APIs of Chalice with CDK construct APIs\n"
  },
  {
    "path": "docs/source/tutorials/wschat.rst",
    "content": "Chat Server Example\n===================\n\n.. note::\n\n  This example is for illustration purposes and does not represent best\n  practices.\n\nA simple chat server example application. This example will walk through\ndeploying a chat application with separate chat rooms and nicknames. It uses\na DynamoDB table to store state like connection IDs between websocket messages.\n\n\nFirst install a copy of Chalice in a fresh environment, create a new project\nand cd into the directory::\n\n  $ pip install -U chalice\n  $ chalice new-project chalice-chat-example\n  $ cd chalice-chat-example\n\n\nOur Chalice application will need boto3 as a dependency for both DynamoDB\naccess and in order to communicate back with API Gateway to send websocket\nmessages. Let's add a boto3 to the ``requirements.txt`` file::\n\n  $ echo \"boto3>=1.9.91\" > requirements.txt\n\n\nNow that the requirement has been added. Let's install it locally since our\nnext script will need it as well::\n\n  $ pip install -r requirements.txt\n\nUnlike our previous example where we used ``chalice deploy``, we will use\n``chalice package`` to create a CloudFormation template. The AWS CLI will be\nused to deploy the template. To install the AWS CLI run the command::\n\n  $ pip install -U awscli\n\nStarting in Chalice 1.10, the package command has a ``--merge-template``\nargument that allows us to merge in a custom JSON file to the generated\nCloudFormation template. Since Chalice does not have any built-in support for\nDynamoDB currently, we will make a ``resources.json`` file with the DynamoDB\ndefinition. The template file will set the environment variable TABLE in all\nour Lambda functions as a CloudFormatiion reference to the DynamoDB table.\nFinally, the template will also override our IAM policy with a custom one to\nallow all the DynamoDB operations our application will need.\n\nBelow is the JSON file that contains all of our custom Cloudformation.\n\n.. code-block:: json\n   :caption: resources.json\n\n   {\n     \"Resources\": {\n       \"ChaliceChatTable\": {\n         \"Type\": \"AWS::DynamoDB::Table\",\n         \"Properties\": {\n           \"AttributeDefinitions\": [\n             {\n               \"AttributeName\": \"PK\",\n               \"AttributeType\": \"S\"\n             },\n             {\n               \"AttributeName\": \"SK\",\n               \"AttributeType\": \"S\"\n             }\n           ],\n           \"KeySchema\": [\n             {\n               \"AttributeName\": \"PK\",\n               \"KeyType\": \"HASH\"\n             },\n             {\n               \"AttributeName\": \"SK\",\n               \"KeyType\": \"RANGE\"\n             }\n           ],\n           \"GlobalSecondaryIndexes\": [\n             {\n               \"IndexName\": \"ReverseLookup\",\n               \"KeySchema\": [\n                 {\n                   \"AttributeName\": \"SK\",\n                   \"KeyType\": \"HASH\"\n                 },\n                 {\n                   \"AttributeName\": \"PK\",\n                   \"KeyType\": \"RANGE\"\n                 }\n               ],\n               \"Projection\": {\n                 \"ProjectionType\": \"ALL\"\n               },\n               \"ProvisionedThroughput\": {\n                 \"ReadCapacityUnits\": 1,\n                 \"WriteCapacityUnits\": 1\n               }\n             }\n           ],\n           \"ProvisionedThroughput\": {\n             \"ReadCapacityUnits\": 1,\n             \"WriteCapacityUnits\": 1\n           },\n           \"TableName\": \"ChaliceChat\"\n         }\n       },\n       \"WebsocketConnect\": {\n         \"Properties\": {\n           \"Environment\": {\n             \"Variables\": {\n               \"TABLE\": {\n                 \"Ref\": \"ChaliceChatTable\"\n               }\n             }\n           }\n         }\n       },\n       \"WebsocketMessage\": {\n         \"Properties\": {\n           \"Environment\": {\n             \"Variables\": {\n               \"TABLE\": {\n                 \"Ref\": \"ChaliceChatTable\"\n               }\n             }\n           }\n         }\n       },\n       \"WebsocketDisconnect\": {\n         \"Properties\": {\n           \"Environment\": {\n             \"Variables\": {\n               \"TABLE\": {\n                 \"Ref\": \"ChaliceChatTable\"\n               }\n             }\n           }\n         }\n       },\n       \"DefaultRole\": {\n         \"Type\": \"AWS::IAM::Role\",\n         \"Properties\": {\n           \"AssumeRolePolicyDocument\": {\n             \"Version\": \"2012-10-17\",\n             \"Statement\": [\n               {\n                 \"Sid\": \"\",\n                 \"Effect\": \"Allow\",\n                 \"Principal\": {\n                   \"Service\": \"lambda.amazonaws.com\"\n                 },\n                 \"Action\": \"sts:AssumeRole\"\n               }\n             ]\n           },\n           \"Policies\": [\n             {\n               \"PolicyName\": \"DefaultRolePolicy\",\n               \"PolicyDocument\": {\n                 \"Version\": \"2012-10-17\",\n                 \"Statement\": [\n                   {\n                     \"Effect\": \"Allow\",\n                     \"Action\": [\n                       \"logs:CreateLogGroup\",\n                       \"logs:CreateLogStream\",\n                       \"logs:PutLogEvents\"\n                     ],\n                     \"Resource\": \"arn:aws:logs:*:*:*\"\n                   },\n                   {\n                     \"Effect\": \"Allow\",\n                     \"Action\": [\n                       \"execute-api:ManageConnections\"\n                     ],\n                     \"Resource\": \"arn:aws:execute-api:*:*:*/@connections/*\"\n                   },\n                   {\n                     \"Effect\": \"Allow\",\n                     \"Action\": [\n                       \"dynamodb:DeleteItem\",\n                       \"dynamodb:PutItem\",\n                       \"dynamodb:GetItem\",\n                       \"dynamodb:UpdateItem\",\n                       \"dynamodb:Query\",\n                       \"dynamodb:Scan\"\n                     ],\n                     \"Resource\": [\n                       {\n                         \"Fn::Sub\": \"arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${ChaliceChatTable}\"\n                       },\n                       {\n                         \"Fn::Sub\": \"arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${ChaliceChatTable}/index/ReverseLookup\"\n                       }\n                     ]\n                   }\n                 ]\n               }\n             }\n           ]\n         }\n       }\n     }\n   }\n\nThe current directory layout should now look like this::\n\n $ tree -a .\n .\n ├── .chalice\n │   └── config.json\n ├── .gitignore\n ├── app.py\n ├── resources.json\n └── requirements.txt\n\n 1 directory, 5 files\n\n\nNext let's fill out the ``app.py`` file since it is pretty simple. Most of this\nexample code is contained in the ``chalicelib/`` directory.\n\n.. code-block:: python\n   :caption: chalice-chat-example/app.py\n\n   from boto3.session import Session\n\n   from chalice import Chalice\n\n   from chalicelib import Storage\n   from chalicelib import Sender\n   from chalicelib import Handler\n\n   app = Chalice(app_name=\"chalice-chat-example\")\n   app.websocket_api.session = Session()\n   app.experimental_feature_flags.update([\n       'WEBSOCKETS'\n   ])\n\n   STORAGE = Storage.from_env()\n   SENDER = Sender(app, STORAGE)\n   HANDLER = Handler(STORAGE, SENDER)\n\n\n   @app.on_ws_connect()\n   def connect(event):\n       STORAGE.create_connection(event.connection_id)\n\n\n   @app.on_ws_disconnect()\n   def disconnect(event):\n       STORAGE.delete_connection(event.connection_id)\n\n\n   @app.on_ws_message()\n   def message(event):\n       HANDLER.handle(event.connection_id, event.body)\n\n\nSimilar to the previous example. We need to use ``boto3`` to construct a\nSession and pass it to ``app.websocket_api.session``. We opt into the\nusage of the ``WEBSOCKET`` experimental feature. Most of the actual work is\ndone in some classes that we import from ``chalicelib/``. These classes are\ndetailed below, and the various parts are explained in comments and doc\nstrings. In addition to the previous example, we register a handler for\n``on_ws_connect`` and ``on_ws_disconnect`` to handle events from API gateway\nwhen a new socket is trying to connect, or an existing socket is disconnected.\n\nFinally before being able to deploy and test the app out, we need to fill out\nthe chalicelib directory. This is the bulk of the app and it is explained\ninline in comments. Create a new directory called ``chalicelib`` and inside\nthat directory create an ``__init__.py`` file and fill it out with the\nfollowing file.\n\n.. code-block:: python\n   :caption: chalice-chat-example/chalicelib/__init__.py\n\n   import os\n\n   import boto3\n   from boto3.dynamodb.conditions import Key\n\n   from chalice import WebsocketDisconnectedError\n\n\n   class Storage(object):\n       \"\"\"An abstraction to interact with the DynamoDB Table.\"\"\"\n       def __init__(self, table):\n           \"\"\"Initialize Storage object\n\n           :param table: A boto3 dynamodb Table resource object.\n           \"\"\"\n           self._table = table\n\n       @classmethod\n       def from_env(cls):\n           \"\"\"Create table from the environment.\n\n           The environment variable TABLE is present for a deployed application\n           since it is set in all of the Lambda functions by a CloudFormation\n           reference. We default to '', which will happen when we run\n           ``chalice package`` since it loads the application, and no\n           environment variable has been set. For local testing, a value should\n           be manually set in the environment if '' will not suffice.\n           \"\"\"\n           table_name = os.environ.get('TABLE', '')\n           table = boto3.resource('dynamodb').Table(table_name)\n           return cls(table)\n\n       def create_connection(self, connection_id):\n           \"\"\"Create a new connection object in the dtabase.\n\n           When a new connection is created, we create a stub for\n           it in the table. The stub uses a primary key of the\n           connection_id and a sort key of username_. This translates\n           to a connection with an unset username. The first message\n           sent over the wire from the connection is to be used as the\n           username, and this entry will be re-written.\n\n           :param connection_id: The connection id to write to\n               the table.\n           \"\"\"\n           self._table.put_item(\n               Item={\n                   'PK': connection_id,\n                   'SK': 'username_',\n               },\n           )\n\n       def set_username(self, connection_id, old_name, username):\n           \"\"\"Set the username.\n\n           The SK entry that goes with this connection id that starts\n           with username_ is taken to be the username. The previous\n           entry needs to be deleted, and a new entry needs to be\n           written.\n\n           :param connection_id: Connection id of the user trying to\n               change their name.\n\n           :param old_name: The original username. Since this is part of\n               the key, it needs to be deleted and re-created rather than\n               updated.\n\n           :param username: The new username the user wants.\n           \"\"\"\n           self._table.delete_item(\n               Key={\n                   'PK': connection_id,\n                   'SK': 'username_%s' % old_name,\n               },\n           )\n           self._table.put_item(\n               Item={\n                   'PK': connection_id,\n                   'SK': 'username_%s' % username,\n               },\n           )\n\n       def list_rooms(self):\n           \"\"\"Get a list of all rooms that exist.\n\n           Scan through the table looking for SKs that start with room_\n           which indicates a room that a user is in. Collect a unique set\n           of those and return them.\n           \"\"\"\n           r = self._table.scan()\n           rooms = set([item['SK'].split('_', 1)[1] for item in r['Items']\n                        if item['SK'].startswith('room_')])\n           return rooms\n\n       def set_room(self, connection_id, room):\n           \"\"\"Set the room a user is currently in.\n\n           The room a user is in is in the form of an SK that starts with\n           room_ prefix.\n\n           :param connection_id: The connection id to move to a room.\n\n           :param room: The room name to join.\n           \"\"\"\n           self._table.put_item(\n               Item={\n                   'PK': connection_id,\n                   'SK': 'room_%s' % room,\n               },\n           )\n\n       def remove_room(self, connection_id, room):\n           \"\"\"Remove a user from a room.\n\n           The room a user is in is in the form of an SK that starts with\n           room_ prefix. To leave a room we need to delete this entry.\n\n           :param connection_id: The connection id to move to a room.\n\n           :param room: The room name to join.\n           \"\"\"\n           self._table.delete_item(\n               Key={\n                   'PK': connection_id,\n                   'SK': 'room_%s' % room,\n               },\n           )\n\n       def get_connection_ids_by_room(self, room):\n           \"\"\"Find all connection ids that go to a room.\n\n           This is needed whenever we broadcast to a room. We collect all\n           their connection ids so we can send messages to them. We use a\n           ReverseLookup table here which inverts the PK, SK relationship\n           creating a partition called room_{room}. Everything in that\n           partition is a connection in the room.\n\n           :param room: Room name to get all connection ids from.\n           \"\"\"\n           r = self._table.query(\n               IndexName='ReverseLookup',\n               KeyConditionExpression=(\n                   Key('SK').eq('room_%s' % room)\n               ),\n               Select='ALL_ATTRIBUTES',\n           )\n           return [item['PK'] for item in r['Items']]\n\n       def delete_connection(self, connection_id):\n           \"\"\"Delete a connection.\n\n           Called when a connection is disconnected and all its entries need\n           to be deleted.\n\n           :param connection_id: The connection partition to delete from\n               the table.\n           \"\"\"\n           try:\n               r = self._table.query(\n                   KeyConditionExpression=(\n                       Key('PK').eq(connection_id)\n                   ),\n                   Select='ALL_ATTRIBUTES',\n               )\n               for item in r['Items']:\n                   self._table.delete_item(\n                       Key={\n                           'PK': connection_id,\n                           'SK': item['SK'],\n                       },\n                   )\n           except Exception as e:\n               print(e)\n\n       def get_record_by_connection(self, connection_id):\n           \"\"\"Get all the properties associated with a connection.\n\n           Each connection_id creates a partition in the table with multiple\n           SK entries. Each SK entry is in the format {property}_{value}.\n           This method reads all those records from the database and puts them\n           all into dictionary and returns it.\n\n           :param connection_id: The connection to get properties for.\n           \"\"\"\n           r = self._table.query(\n               KeyConditionExpression=(\n                   Key('PK').eq(connection_id)\n               ),\n               Select='ALL_ATTRIBUTES',\n           )\n           r = {\n               entry['SK'].split('_', 1)[0]: entry['SK'].split('_', 1)[1]\n               for entry in r['Items']\n           }\n           return r\n\n\n   class Sender(object):\n       \"\"\"Class to send messages over websockets.\"\"\"\n       def __init__(self, app, storage):\n           \"\"\"Initialize a sender object.\n\n           :param app: A Chalice application object.\n\n           :param storage: A Storage object.\n           \"\"\"\n           self._app = app\n           self._storage = storage\n\n       def send(self, connection_id, message):\n           \"\"\"Send a message over a websocket.\n\n           :param connection_id: API Gateway Connection ID to send a\n               message to.\n\n           :param message: The message to send to the connection.\n           \"\"\"\n           try:\n               # Call the chalice websocket api send method\n               self._app.websocket_api.send(connection_id, message)\n           except WebsocketDisconnectedError as e:\n               # If the websocket has been closed, we delete the connection\n               # from our database.\n               self._storage.delete_connection(e.connection_id)\n\n       def broadcast(self, connection_ids, message):\n           \"\"\"\"Send a message to multiple connections.\n\n           :param connection_id: A list of API Gateway Connection IDs to\n               send the message to.\n\n           :param message: The message to send to the connections.\n           \"\"\"\n           for cid in connection_ids:\n               self.send(cid, message)\n\n\n   class Handler(object):\n       \"\"\"Handler object that handles messages received from a websocket.\n\n       This class implements the bulk of our app behavior.\n       \"\"\"\n       def __init__(self, storage, sender):\n           \"\"\"Initialize a Handler object.\n\n           :param storage: Storage object to interact with database.\n\n           :param sender: Sender object to send messages to websockets.\n           \"\"\"\n           self._storage = storage\n           self._sender = sender\n           # Command table to translate a string command name into a\n           # method to call.\n           self._command_table = {\n               'help': self._help,\n               'nick': self._nick,\n               'join': self._join,\n               'room': self._room,\n               'quit': self._quit,\n               'ls': self._list,\n           }\n\n       def handle(self, connection_id, message):\n           \"\"\"Entry point for our application.\n\n           :param connection_id: Connection id that the message came from.\n\n           :param message: Message we got from the connection.\n           \"\"\"\n           # First look the user up in the database and get a record for it.\n           record = self._storage.get_record_by_connection(connection_id)\n           if record['username'] == '':\n               # If the user does not have a username, we assume that the message\n               # is the username they want and we call _handle_login_message.\n               self._handle_login_message(connection_id, message)\n           else:\n               # Otherwise we assume the user is logged in. So we call\n               # a method to handle the message. We pass along the\n               # record we loaded from the database so we don't need to\n               # again.\n               self._handle_message(connection_id, message, record)\n\n       def _handle_login_message(self, connection_id, message):\n           \"\"\"Handle a login message.\n\n           The message is the username to give the user. Re-write the\n           database entry for this user to reset their username from ''\n           to {message}. Once that is done send a message back to the user\n           to confirm the name choice. Also send a /help prompt.\n           \"\"\"\n           self._storage.set_username(connection_id, '', message)\n           self._sender.send(\n               connection_id,\n               'Using nickname: %s\\nType /help for list of commands.' % message\n           )\n\n       def _handle_message(self, connection_id, message, record):\n           \"\"\"\"Handle a message from a connected and logged in user.\n\n           If the message starts with a / it's a command. Otherwise its a\n           text message to send to all rooms in the room.\n\n           :param connection_id: Connection id that the message came from.\n\n           :param message: Message we got from the connection.\n\n           :param record: A data record about the sender.\n           \"\"\"\n           if message.startswith('/'):\n               self._handle_command(connection_id, message[1:], record)\n           else:\n               self._handle_text(connection_id, message, record)\n\n       def _handle_command(self, connection_id, message, record):\n           \"\"\"Handle a command message.\n\n           Check the command name and look it up in our command table.\n           If there is an entry, we call that method and pass along\n           the connection_id, arguments, and the loaded record.\n\n           :param connection_id: Connection id that the message came from.\n\n           :param message: Message we got from the connection.\n\n           :param record: A data record about the sender.\n           \"\"\"\n           args = message.split(' ')\n           command_name = args.pop(0).lower()\n           command = self._command_table.get(command_name)\n           if command:\n               command(connection_id, args, record)\n           else:\n               # If no command method is found, send an error message\n               # back to the user.\n               self._sender(\n                   connection_id, 'Unknown command: %s' % command_name)\n\n       def _handle_text(self, connection_id, message, record):\n           \"\"\"Handle a raw text message.\n\n           :param connection_id: Connection id that the message came from.\n\n           :param message: Message we got from the connection.\n\n           :param record: A data record about the sender.\n           \"\"\"\n           if 'room' not in record:\n               # If the user is not in a room send them an error message\n               # and return early.\n               self._sender.send(\n                   connection_id, 'Cannot send message if not in chatroom.')\n               return\n           # Collect a list of connection_ids in the same room as the message\n           # sender.\n           connection_ids = self._storage.get_connection_ids_by_room(\n               record['room'])\n           # Prefix the message with the sender's name.\n           message = '%s: %s' % (record['username'], message)\n           # Broadcast the new message to everyone in the room.\n           self._sender.broadcast(connection_ids, message)\n\n       def _help(self, connection_id, _message, _record):\n           \"\"\"Send the help message.\n\n           Build a help message and send back to the same connection.\n\n           :param connection_id: Connection id that the message came from.\n           \"\"\"\n           self._sender.send(\n               connection_id,\n               '\\n'.join([\n                   'Commands available:',\n                   '    /help',\n                   '          Display this message.',\n                   '    /join {chat_room_name}',\n                   '          Join a chatroom named {chat_room_name}.',\n                   '    /nick {nickname}',\n                   '          Change your name to {nickname}. If no {nickname}',\n                   '          is provided then your current name will be printed',\n                   '    /room',\n                   '          Print out the name of the room you are currently ',\n                   '          in.',\n                   '    /ls',\n                   '          If you are in a room, list all users also in the',\n                   '          room. Otherwise, list all rooms.',\n                   '    /quit',\n                   '          Leave current room.',\n                   '',\n                   'If you are in a room, raw text messages that do not start ',\n                   'with a / will be sent to everyone else in the room.',\n               ]),\n           )\n\n       def _nick(self, connection_id, args, record):\n           \"\"\"Change or check nickname (username).\n\n           :param connection_id: Connection id that the message came from.\n\n           :param args: Argument list that came after the command.\n\n           :param record: A data record about the sender.\n           \"\"\"\n           if not args:\n               # If a nickname argument was not provided, we just want to\n               # report the current nickname to the user.\n               self._sender.send(\n                   connection_id, 'Current nickname: %s' % record['username'])\n               return\n           # The first argument is assumed to be the new desired nickname.\n           nick = args[0]\n           # Change the username from record['username'] to nick in the storage\n           # layer.\n           self._storage.set_username(connection_id, record['username'], nick)\n           # Send a message to the requestor to confirm the nickname change.\n           self._sender.send(connection_id, 'Nickname is: %s' % nick)\n           # Get the room the user is in.\n           room = record.get('room')\n           if room:\n               # If the user was in a room, announce to the room they have\n               # changed their name. Don't send this me sage to the user since\n               # they already got a name change message.\n               room_connections = self._storage.get_connection_ids_by_room(room)\n               room_connections.remove(connection_id)\n               self._sender.broadcast(\n                   room_connections,\n                   '%s is now known as %s.' % (record['username'], nick))\n\n       def _join(self, connection_id, args, record):\n           \"\"\"Join a chat room.\n\n           :param connection_id: Connection id that the message came from.\n\n           :param args: Argument list. The first argument should be the\n              name of the room to join.\n\n           :param record: A data record about the sender.\n           \"\"\"\n           # Get the room name to join.\n           room = args[0]\n           # Call quit to leave the current room we are in if there is any.\n           self._quit(connection_id, '', record)\n           # Get a list of connections in the target chat room.\n           room_connections = self._storage.get_connection_ids_by_room(room)\n           # Join the target chat room.\n           self._storage.set_room(connection_id, room)\n           # Send a message to the requestor that they have joined the room.\n           # At the same time send an announcement to everyone who was already\n           # in the room to alert them of the new user.\n           self._sender.send(\n               connection_id, 'Joined chat room \"%s\"' % room)\n           message = '%s joined room.' % record['username']\n           self._sender.broadcast(room_connections, message)\n\n       def _room(self, connection_id, _args, record):\n           \"\"\"Report the name of the current room.\n\n           :param connection_id: Connection id that the message came from.\n\n           :param record: A data record about the sender.\n           \"\"\"\n           if 'room' in record:\n               # If the user is in a room send them the name back.\n               self._sender.send(connection_id, record['room'])\n           else:\n               # If the user is not in a room. Tell them so, and how to\n               # join a room.\n               self._sender.send(\n                   connection_id,\n                   'Not currently in a room. Type /join {room_name} to do so.'\n               )\n\n       def _quit(self, connection_id, _args, record):\n           \"\"\"Quit from a room.\n\n           :param connection_id: Connection id that the message came from.\n\n           :param record: A data record about the sender.\n           \"\"\"\n           if 'room' not in record:\n               # If the user is not in a room there is nothing to do.\n               return\n           # Find the current room name, and delete that entry from\n           # the database.\n           room_name = record['room']\n           self._storage.remove_room(connection_id, room_name)\n           # Send a message to the user to inform them they left the room.\n           self._sender.send(\n               connection_id, 'Left chat room \"%s\"' % room_name)\n           # Tell everyone in the room that the user has left.\n           self._sender.broadcast(\n               self._storage.get_connection_ids_by_room(room_name),\n               '%s left room.' % record['username'],\n           )\n\n       def _list(self, connection_id, _args, record):\n           \"\"\"Show a context dependent listing.\n\n           :param connection_id: Connection id that the message came from.\n\n           :param record: A data record about the sender.\n           \"\"\"\n           room = record.get('room')\n           if room:\n               # If the user is in a room, get a listing of everyone\n               # in the room.\n               result = [\n                   self._storage.get_record_by_connection(c_id)['username']\n                   for c_id in self._storage.get_connection_ids_by_room(room)\n               ]\n           else:\n               # If they are not in a room. Get a listing of all rooms\n               # currently open.\n               result = self._storage.list_rooms()\n           # Send the result list back to the requestor.\n           self._sender.send(connection_id, '\\n'.join(result))\n\n\nThe final directory layout should be ::\n\n    $ tree -a .\n    .\n    ├── .chalice\n    │   ├── config.json\n    ├── .gitignore\n    ├── app.py\n    ├── chalicelib\n    │   └── __init__.py\n    ├── resources.json\n    └── requirements.txt\n\n    2 directories, 6 files\n\n\nDeploying our app with CloudFormation requires 3 steps. First we use Chalice\nto package our app into a JSON CloudFormation template::\n\n  $ chalice package --merge-template resources.json out\n\nThis will result in a new directory called ``out`` being created, inside which\nthere is a ``sam.json`` file. This template contains our Chalice app as a\nCloudFormation template, merged with our ``resources.json`` template.\n\nNext we use the AWS CLI to package this template, and prepare it for\ndeployment. In order for this to work you will need to replace ``$BUCKET``\nwith the name of a bucket you control::\n\n  $ aws cloudformation package  --template-file out/sam.json \\\n      --s3-bucket $BUCKET --output-template-file out/template.yml\n\nOnce this is complete, a new template should be located at ``out/template.yml``\nthis is the final CloudFormation template which is ready for deployment.\nDeploying it with the AWS CLI can be done with the following command::\n\n  $ aws cloudformation deploy --template-file out/template.yml \\\n      --stack-name ChaliceChat --capabilities CAPABILITY_IAM\n\nThis command should wait awhile, and once it exits the app should be ready. To\nget the websocket connection URL, we can use the AWS CLI again to check the\nstack output ``WebsocketConnectEndpointURL``::\n\n  $ aws cloudformation describe-stacks --stack-name ChaliceChat \\\n      --query \"Stacks[0].Outputs[?OutputKey=='WebsocketConnectEndpointURL'].OutputValue\" \\\n      --output text\n  wss://{id}.execute-api.{region}.amazonaws.com/api/\n\n\nOnce deployed we can take the result of the previous command and connect to it\nusing ``wsdump.py``. Below is a sample of two running clients, the first\nmessage sent to the server is used as the client's username.\n\n\n.. code-block:: bash\n   :caption: client-1\n\n   $ wsdump.py wss://{id}.execute-api.{region}.amazonaws.com/api/\n   Press Ctrl+C to quit\n   > John\n   < Using nickname: John\n   Type /help for list of commands.\n   > /help\n   < Commands available:\n       /help\n             Display this message.\n       /join {chat_room_name}\n             Join a chatroom named {chat_room_name}.\n       /nick {nickname}\n             Change your name to {nickname}. If no {nickname}\n             is provided then your current name will be printed\n       /room\n             Print out the name of the room you are currently\n             in.\n       /ls\n             If you are in a room, list all users also in the\n             room. Otherwise, list all rooms.\n       /quit\n             Leave current room.\n\n   If you are in a room, raw text messages that do not start\n   with a / will be sent to everyone else in the room.\n   > /join chalice\n   < Joined chat room \"chalice\"\n   < Jenny joined room.\n   > Hi\n   < John: Hi\n   < Jenny is now known as JennyJones.\n   > /quit\n   < Left chat room \"chalice\"\n   > /ls\n   < chalice\n   > Ctrl-D\n\n.. code-block:: bash\n   :caption: client-2\n\n   $ wsdump.py wss://{id}.execute-api.{region}.amazonaws.com/api/\n   Press Ctrl+C to quit\n   > Jenny\n   < Using nickname: Jenny\n   Type /help for list of commands.\n   > /help\n   < Commands available:\n       /help\n             Display this message.\n       /join {chat_room_name}\n             Join a chatroom named {chat_room_name}.\n       /nick {nickname}\n             Change your name to {nickname}. If no {nickname}\n             is provided then your current name will be printed\n       /room\n             Print out the name of the room you are currently\n             in.\n       /ls\n             If you are in a room, list all users also in the\n             room. Otherwise, list all rooms.\n       /quit\n             Leave current room.\n\n   If you are in a room, raw text messages that do not start\n   with a / will be sent to everyone else in the room.\n   > /join chalice\n   < Joined chat room \"chalice\"\n   > /ls\n   < John\n   Jenny\n   < John: Hi\n   > /nick JennyJones\n   < Nickname is: JennyJones\n   < John left room.\n   > /ls\n   < JennyJones\n   > /room\n   < chalice\n   > /nick\n   < Current nickname: JennyJones\n   > Ctrl-D\n\n\nTo delete the resources you can run use the AWS CLI to delete the stack::\n\n  $ aws cloudformation delete-stack --stack-name ChaliceChat\n\n"
  },
  {
    "path": "docs/source/tutorials/wsecho.rst",
    "content": "Echo Server Example\n===================\n\nAn echo server is a simple server that echos any message it receives back to\nthe client that sent it.\n\nFirst install a copy of Chalice in a fresh environment, create a new project\nand cd into the directory::\n\n  $ pip install -U chalice\n  $ chalice new-project echo-server\n  $ cd echo-server\n\nOur Chalice application will need boto3 as a dependency for both API Gateway\nto send websocket messages. Let's add a boto3 to the ``requirements.txt``\nfile::\n\n  $ echo \"boto3>=1.9.91\" > requirements.txt\n\n\nNow that the requirement has been added. Let's install it locally since our\nnext script will need it as well::\n\n  $ pip install -r requirements.txt\n\n\nNext replace the contents of the ``app.py`` file with the code below.\n\n.. code-block:: python\n   :caption: app.py\n   :linenos:\n\n   from boto3.session import Session\n\n   from chalice import Chalice\n   from chalice import WebsocketDisconnectedError\n\n   app = Chalice(app_name=\"echo-server\")\n   app.websocket_api.session = Session()\n   app.experimental_feature_flags.update([\n       'WEBSOCKETS'\n   ])\n\n\n   @app.on_ws_message()\n   def message(event):\n       try:\n           app.websocket_api.send(\n               connection_id=event.connection_id,\n               message=event.body,\n           )\n       except WebsocketDisconnectedError as e:\n           pass  # Disconnected so we can't send the message back.\n\n\nStepping through this app line by line, the first thing to note is that we\nneed to import and instantiate a boto3 session. This session is manually\nassigned to ``app.websocket_api.session``.\nThis is needed because in order to send websocket responses to API Gateway we\nneed to construct a boto3 client. Chalice does not take a direct dependency\non boto3 or botocore, so we need to provide the Session ourselves.\n\n.. code-block:: python\n\n   from boto3.session import Session\n   app.websocket_api.session = Session()\n\n\nNext we enable the experimental feature ``WEBSOCKETS``. Websockets are an\nexperimental feature and are subject to API changes. This includes all aspects\nof the Websocket API exposted in Chalice. Including any public members of\n``app.websocket_api``, and the three decorators ``on_ws_connect``,\n``on_ws_message``, and ``on_ws_disconnect``.\n\n.. code-block:: python\n\n   app.experimental_feature_flags.update([\n       'WEBSOCKETS'\n   ])\n\n\nTo register a websocket handler, and cause Chalice to deploy an\nAPI Gateway Websocket API we use the ``app.on_ws_message()`` decorator.\nThe event parameter here is a wrapper object with some convenience\nparameters attached. The most useful are ``event.connection_id`` and\n``event.body``. The ``connection_id`` is an API Gateway specific identifier\nthat allows you to refer to the connection that sent the message. The ``body``\nis the content of the message.\n\n.. code-block:: python\n\n   @app.on_ws_message()\n   def message(event):\n\n\nSince this is an echo server, the message handler simply reads the content it\nreceived on the socket, and rewrites it back to the same socket. To send a\nmessage to a socket we call ``app.websocket_api.send(connection_id, message)``.\nIn this case, we just use the same ``connection_id`` we got the message from,\nand use the ``body`` we got from the event as the ``message`` to send.\n\n.. code-block:: python\n\n   app.websocket_api.send(\n       connection_id=event.connection_id,\n       message=event.body,\n    )\n\n\nFinally, we catch the exception ``WebsocketDisconnectedError`` which is raised\nby ``app.websocket_api.send`` if the provided ``connection_id`` is not\nconnected anymore. In our case this doesn't really matter since we don't have\nanything tracking our connections. The error has a ``connection_id`` property\nthat contains the offending connection id.\n\n.. code-block:: python\n\n   except WebsocketDisconnectedError as e:\n       pass  # Disconnected so we can't send the message back.\n\n\nNow that we understand the code, lets deploy it with ``chalice deploy``::\n\n   $ chalice deploy\n     Creating deployment package.\n     Creating IAM role: echo-server-dev\n     Creating lambda function: echo-server-dev-websocket_message\n     Creating websocket api: echo-server-dev-websocket-api\n     Resources deployed:\n       - Lambda ARN: arn:aws:lambda:region:0123456789:function:echo-server-dev-websocket_message\n       - Websocket API URL: wss://{websocket_api_id}.execute-api.region.amazonaws.com/api/\n\nTo test out the echo server we will use the  ``websocket-client`` package. You\ninstall it from PyPI::\n\n  $ pip install websocket-client\n\n\nAfter deploying the Chalice app the output will contain a URL for connecting\nto the websocket API labeled: ``- Websocket API URL:``. The\n``websocket-client`` package installs a command line tool called ``wsdump.py``\nwhich can be used to test websocket echo server::\n\n  $ wsdump wss://{websocket_api_id}.execute-api.region.amazonaws.com/api/\n  Press Ctrl+C to quit\n  > foo\n  < foo\n  > bar\n  < bar\n  > foo bar baz\n  < foo bar baz\n  >\n\n\nEvery message sent to the server (lines that start with ``>``) result in a\nmessage sent to us (lines that start with ``<``) with the same content.\n\nIf something goes wrong, you can check the chalice error logs using the\nfollowing command::\n\n  $ chalice logs -n websocket_message\n\n.. note::\n   If you encounter an Internal Server Error here it is likely that you forgot\n   to include ``boto3>=1.9.91`` in the ``requirements.txt`` file.\n\nTo tear down the example. Just run::\n\n  $ chalice delete\n    Deleting Websocket API: {websocket_api_id}\n    Deleting function: arn:aws:lambda:us-west-2:0123456789:function:echo-server-dev-websocket_message\n    Deleting IAM role: echo-server-dev\n\nNext Steps\n----------\n\nIn this tutorial, we created an echo server with websockets.\nIf you'd like to try something more ambitious, you can follow our\ntutorial for creating a sample :doc:`Chat application with websocket <wschat>`.\n"
  },
  {
    "path": "docs/source/upgrading.rst",
    "content": "Upgrade Notes\n=============\n\nThis document provides additional documentation\non upgrading your version of chalice.  If you're just\ninterested in the high level changes, see the\n`CHANGELOG.md <https://github.com/aws/chalice/blob/master/CHANGELOG.md>`__)\nfile.\n\n.. _v1-2-0:\n\n1.2.0\n-----\n\nThis release features a rewrite of the Chalice deployer\n(`#604 <https://github.com/aws/chalice/issues/604>`__).\nThis is a backwards compatible change, and should not have any\nnoticeable changes with deployments with the exception of\nfixing deployer bugs (e.g. https://github.com/aws/chalice/issues/604).\nThis code path affects the ``chalice deploy``, ``chalice delete``, and\n``chalice package`` commands.\n\nWhile this release is backwards compatible, you will notice several\nchanges when you upgrade to version 1.2.0.\n\nThe output of ``chalice deploy`` has changed in order to give\nmore details about the resources it creates along with a more detailed\nsummary at the end::\n\n    $ chalice deploy\n    Creating deployment package.\n    Creating IAM role: myapp-dev\n    Creating lambda function: myapp-dev-foo\n    Creating lambda function: myapp-dev\n    Creating Rest API\n    Resources deployed:\n      - Lambda ARN: arn:aws:lambda:us-west-2:12345:function:myapp-dev-foo\n      - Lambda ARN: arn:aws:lambda:us-west-2:12345:function:myapp-dev\n      - Rest API URL: https://abcd.execute-api.us-west-2.amazonaws.com/api/\n\nAlso, the files used to store deployed values has changed.  These files are\nused internally by the ``chalice deploy/delete`` commands and you typically\ndo not interact with these files directly.  It's mentioned here in case\nyou notice new files in your ``.chalice`` directory.  Note that these files\nare *not* part of the public interface of Chalice and are documented here\nfor completeness and to help with debugging issues.\n\nIn versions < 1.2.0, the value of deployed resources was stored in\n``.chalice/deployed.json`` and looked like this::\n\n  {\n    \"dev\": {\n      \"region\": \"us-west-2\",\n      \"api_handler_name\": \"demoauth4-dev\",\n      \"api_handler_arn\": \"arn:aws:lambda:us-west-2:123:function:myapp-dev\",\n      \"rest_api_id\": \"abcd\",\n      \"lambda_functions\": {\n        \"myapp-dev-foo\": {\n          \"type\": \"pure_lambda\",\n          \"arn\": \"arn:aws:lambda:us-west-2:123:function:myapp-dev-foo\"\n        }\n      },\n      \"chalice_version\": \"1.1.1\",\n      \"api_gateway_stage\": \"api\",\n      \"backend\": \"api\"\n    },\n    \"prod\": {...}\n  }\n\n\nIn version 1.2.0, the deployed resources are split into multiple files, one\nfile per chalice stage.  These files are in the\n``.chalice/deployed/<stage.json>``, so if you had a dev and a prod chalice\nstage you'd have ``.chalice/deployed/dev.json`` and\n``.chalice/deployed/prod.json``.  The schema has also changed and looks\nlike this::\n\n\n  $ cat .chalice/deployed/dev.json\n  {\n    \"schema_version\": \"2.0\",\n    \"resources\": [\n      {\n        \"role_name\": \"myapp-dev\",\n        \"role_arn\": \"arn:aws:iam::123:role/myapp-dev\",\n        \"name\": \"default-role\",\n        \"resource_type\": \"iam_role\"\n      },\n      {\n        \"lambda_arn\": \"arn:aws:lambda:us-west-2:123:function:myapp-dev-foo\",\n        \"name\": \"foo\",\n        \"resource_type\": \"lambda_function\"\n      },\n      {\n        \"lambda_arn\": \"arn:aws:lambda:us-west-2:123:function:myapp-dev\",\n        \"name\": \"api_handler\",\n        \"resource_type\": \"lambda_function\"\n      },\n      {\n        \"name\": \"rest_api\",\n        \"rest_api_id\": \"abcd\",\n        \"rest_api_url\": \"https://abcd.execute-api.us-west-2.amazonaws.com/api\",\n        \"resource_type\": \"rest_api\"\n      }\n    ],\n    \"backend\": \"api\"\n  }\n\nWhen you run ``chalice deploy`` for the first time after upgrading to version\n1.2.0, chalice will automatically converted ``.chalice/deployed.json`` over to\nthe format as you deploy a given stage.\n\n.. warning::\n\n  Once you upgrade to 1.2.0, chalice will only update the new\n  ``.chalice/deployed/<stage>.json``.  This means you cannot downgrade\n  to earlier versions of chalice unless you manually update\n  ``.chalice/deployed.json`` as well.\n\n\nThe ``chalice package`` command has also been updated to use the\ndeployer.  This results in several changes compared to the previous\nversion:\n\n* Pure lambdas are supported\n* Scheduled events are supported\n* Parity between the behavior of ``chalice deploy`` and ``chalice package``\n\nAs part of this change, the CFN resource names have been updated\nto use ``CamelCase`` names.  Previously, chalice converted your\npython function names to CFN resource names by removing all\nnon alphanumeric characters and appending an md5 checksum,\ne.g ``my_function -> myfunction3bfc``.  With this new packager\nupdate, the resource name would be converted as\n``my_function -> MyFunction``.  Note, the ``Outputs`` section\nrenames unchanged in order to preserve backwards compatibility.\nIn order to fix parity issues with ``chalice deploy`` and\n``chalice package``, we now explicitly create an IAM role\nresource as part of the default configuration.\n\n\n.. _v1-0-0b2:\n\n1.0.0b2\n-------\n\nThe url parameter names and the function argument names must match.\nPreviously, the routing code would use positional args ``handler(*args)``\nto invoke a view function.  In this version, kwargs are now used instead:\n``handler(**view_args)``.  For example, this code will no longer work:\n\n.. code-block:: python\n\n    @app.route('/{a}/{b}')\n    def myview(first, second)\n        return {}\n\n\nThe example above must be updated to:\n\n\n.. code-block:: python\n\n    @app.route('/{a}/{b}')\n    def myview(a, b)\n        return {}\n\nNow that functions are invoked with kwargs, the order doesn't matter.  You may\nalso write the above view function as:\n\n\n.. code-block:: python\n\n    @app.route('/{a}/{b}')\n    def myview(b, a)\n        return {}\n\n\nThis was done to have consistent behavior with other web frameworks such as\nFlask.\n\n.. _v1-0-0b1:\n\n1.0.0b1\n-------\n\nThe ``Chalice.define_authorizer`` method has been removed.  This has been\ndeprecated since v0.8.1.  See :doc:`topics/authorizers` for updated\ninformation on configuring authorizers in Chalice as well as the\noriginal deprecation notice in the :ref:`v0-8-1` upgrade notes.\n\nThe optional deprecated positional parameter in the ``chalice deploy`` command\nfor specifying the API Gateway stage has been removed.  If you want to\nspecify the API Gateway stage, you can use the ``--api-gateway-stage``\noption in the ``chalice deploy`` command::\n\n    # Deprecated and removed in 1.0.0b1\n    $ chalice deploy prod\n\n    # Equivalent and updated way to specify an API Gateway stage:\n    $ chalice deploy --api-gateway-stage prod\n\n\n.. _v0-9-0:\n\n0.9.0\n-----\n\nThe 0.9.0 release changed the type of ``app.current_request.raw_body`` to\nalways be of type ``bytes()``.  This only affects users that were using\npython3.  Previously you would get a type ``str()``, but with the introduction\nof `binary content type support\n<https://github.com/aws/chalice/issues/348>`__, the ``raw_body`` attribute\nwas made to consistently be of type ``bytes()``.\n\n\n.. _v0-8-1:\n\n0.8.1\n-----\n\nThe 0.8.1 changed the preferred way of specifying authorizers for view\nfunctions.  You now specify either an instance of\n``chalice.CognitoUserPoolAuthorizer`` or ``chalice.CustomAuthorizer``\nto an ``@app.route()`` function using the ``authorizer`` argument.\n\nDeprecated:\n\n.. code-block:: python\n\n    @app.route('/user-pools', methods=['GET'], authorizer_name='MyPool')\n    def authenticated():\n        return {\"secure\": True}\n\n    app.define_authorizer(\n        name='MyPool',\n        header='Authorization',\n        auth_type='cognito_user_pools',\n        provider_arns=['arn:aws:cognito:...:userpool/name']\n    )\n\nEquivalent, and preferred way\n\n.. code-block:: python\n\n    from chalice import CognitoUserPoolAuthorizer\n\n    authorizer = CognitoUserPoolAuthorizer(\n        'MyPool', header='Authorization',\n        provider_arns=['arn:aws:cognito:...:userpool/name'])\n\n    @app.route('/user-pools', methods=['GET'], authorizer=authorizer)\n    def authenticated():\n        return {\"secure\": True}\n\n\nThe ``define_authorizer`` is still available, but is now deprecated and will\nbe removed in future versions of chalice.  You can also use the new\n``authorizer`` argument to provider a ``CustomAuthorizer``:\n\n\n.. code-block:: python\n\n    from chalice import CustomAuthorizer\n\n    authorizer = CustomAuthorizer(\n        'MyCustomAuth', header='Authorization',\n        authorizer_uri=('arn:aws:apigateway:region:lambda:path/2015-03-01'\n                        '/functions/arn:aws:lambda:region:account-id:'\n                        'function:FunctionName/invocations'))\n\n    @app.route('/custom-auth', methods=['GET'], authorizer=authorizer)\n    def authenticated():\n        return {\"secure\": True}\n\n\n.. _v0-7-0:\n\n0.7.0\n-----\n\nThe 0.7.0 release adds several major features to chalice.  While the majority\nof these features are introduced in a backwards compatible way, there are a few\nbackwards incompatible changes that were made in order to support these new\nmajor features.\n\nSeparate Stages\n~~~~~~~~~~~~~~~\n\nPrior to this version, chalice had a notion of a \"stage\" that corresponded to\nan API gateway stage.  You can create and deploy a new API gateway stage by\nrunning ``chalice deploy <stage-name>``.  In 0.7.0, stage support was been\nreworked such that a chalice stage is a completely separate set of AWS\nresources.  This means that if you have two chalice stages, say ``dev`` and\n``prod``, then you will have two separate sets of AWS resources, one set per\nstage:\n\n* Two API Gateway Rest APIs\n* Two separate Lambda functions\n* Two separate IAM roles\n\nThe :doc:`topics/stages` doc has more details on the new chalice stages\nfeature.  This section highlights the key differences between the old stage\nbehavior and the new chalice stage functionality in 0.7.0.  In order to ease\ntransition to this new model, the following changes were made:\n\n* A new ``--stage`` argument was added to the ``deploy``, ``logs``, ``url``,\n  ``generate-sdk``, and ``package`` commands.  If this value is specified\n  and the stage does not exist, a new chalice stage with that name will\n  be created for you.\n* The existing form ``chalice deploy <stage-name>`` has been deprecated.\n  The command will still work in version 0.7.0, but a deprecation warning\n  will be printed to stderr.\n* If you want the pre-existing behavior of creating a new API gateway stage\n  (while using the same Lambda function), you can use the\n  ``--api-gateway-stage`` argument.  This is the replacement for the\n  deprecated form ``chalice deploy <stage-name>``.\n* The default stage if no ``--stage`` option is provided is ``dev``.  By\n  defaulting to a ``dev`` stage, the pre-existing behavior of not\n  specifying a stage name, e.g ``chalice deploy``, ``chalice url``, etc.\n  will still work exactly the same.\n* A new ``stages`` key is supported in the ``.chalice/config.json``.  This\n  allows you to specify configuration specific to a chalice stage.\n  See the :doc:`topics/configfile` doc for more information about stage\n  specific configuration.\n* Setting ``autogen_policy`` to false will result in chalice looking\n  for a IAM policy file named ``.chalice/policy-<stage-name>.json``.\n  Previously it would look for a file named ``.chalice/policy.json``.\n  You can also explicitly set this value to\n  In order to ease transition, chalice will check for a\n  ``.chalice/policy.json`` file when depoying to the ``dev`` stage.\n  Support for ``.chalice/policy.json`` will be removed in future\n  versions of chalice and users are encouraged to switch to the\n  stage specific ``.chalice/policy-<stage-name>.json`` files.\n\n\nSee the :doc:`topics/stages` doc for more details on the new chalice stages\nfeature.\n\n**Note, the AWS resource names it creates now have the form\n``<app-name>-<stage-name>``, e.g. ``myapp-dev``, ``myapp-prod``.**\n\nWe recommend using the new stage specific resource names.  However, If you\nwould like to use the existing resource names for a specific stage, you can\ncreate a ``.chalice/deployed.json`` file that specifies the existing values::\n\n  {\n    \"dev\": {\n      \"backend\": \"api\",\n      \"api_handler_arn\": \"lambda-function-arn\",\n      \"api_handler_name\": \"lambda-function-name\",\n      \"rest_api_id\": \"your-rest-api-id\",\n      \"api_gateway_stage\": \"dev\",\n      \"region\": \"your region (e.g us-west-2)\",\n      \"chalice_version\": \"0.7.0\",\n    }\n  }\n\n\nThis file is discussed in the next section.\n\nDeployed Values\n~~~~~~~~~~~~~~~\n\nIn version 0.7.0, the way deployed values are stored and retrieved\nhas changed.  In prior versions, only the ``lambda_arn`` was saved,\nand its value was written to the ``.chalice/config.json`` file.\nAny of other deployed values that were needed (for example the\nAPI Gateway rest API id) was dynamically queried by assuming the\nresource names matches the app name.  In this version of chalice,\na separate ``.chalice/deployed.json`` file is written on every\ndeployement which contains all the resources that have been created.\nWhile this should be a transparent change, you may noticed\nissues if you run commands such as ``chalice url`` and ``chalice logs``\nwithout first deploying.  To fix this issue, run ``chalice deploy``\nand version 0.7.0 of chalice so a ``.chalice/deployed.json`` will\nbe created for you.\n\n\nAuthorizer Changes\n~~~~~~~~~~~~~~~~~~\n\n**The ``authorizer_id`` and ``authorization_type`` args are\nno longer supported in ``@app.route(...)`` calls.**\n\n\nThey have been replaced with an ``authorizer_name`` parameter and an\n``app.define_authorizer`` method.\n\nThis version changed the internals of how an API gateway REST API is created.\nPrior to 0.7.0, the AWS SDK for Python was used to make the appropriate service\nAPI calls to API gateway include ``create_rest_api`` and ``put_method /\nput_method_response`` for each route.  In version 0.7.0, this internal\nmechanism was changed to instead generate a swagger document.  The rest api is\nthen created or updated by calling ``import_rest_api`` or ``put_rest_api`` and\nproviding the swagger document.  This simplifies the internals and also unifies\nthe code base for the newly added ``chalice package`` command (which uses a\nswagger document internally).  One consequence of this change is that the\nentire REST API must be defined in the swagger document.  With the previous\n``authorizer_id`` parameter, you would create/deploy a rest api, create your\nauthorizer, and then provide that ``authorizer_id`` in your ``@app.route``\ncalls.  Now they must be defined all at once in the ``app.py`` file:\n\n\n.. code-block:: python\n\n    app = chalice.Chalice(app_name='demo')\n\n    @app.route('/auth-required', authorizer_name='MyUserPool')\n    def foo():\n        return {}\n\n    app.define_authorizer(\n        name='MyUserPool',\n        header='Authorization',\n        auth_type='cognito_user_pools',\n        provider_arns=['arn:aws:cognito:...:userpool/name']\n    )\n\n\n.. _v0-6-0:\n\n0.6.0\n-----\n\nThis version changed how the internals of how API gateway resources are created\nby chalice.  The integration type changed from ``AWS`` to ``AWS_PROXY``.  This\nwas to enable additional functionality, notable to allows users to provide\nnon-JSON HTTP responses and inject arbitrary headers to the HTTP responses.\nWhile this change to the internals is primarily internal, there are several\nuser-visible changes.\n\n\n* Uncaught exceptions with ``app.debug = False`` (the default value)\n  will result in a more generic ``InternalServerError`` error.  The\n  previous behavior was to return a ``ChaliceViewError``.\n* When you enabled debug mode via ``app.debug = True``, the HTTP\n  response will contain the python stack trace as the entire request\n  body.  This is to improve the readability of stack traces.\n  For example::\n\n    $ http https://endpoint/dev/\n    HTTP/1.1 500 Internal Server Error\n    Content-Length: 358\n    Content-Type: text/plain\n\n    Traceback (most recent call last):\n      File \"/var/task/chalice/app.py\", line 286, in __call__\n        response = view_function(*function_args)\n      File \"/var/task/app.py\", line 12, in index\n        return a()\n      File \"/var/task/app.py\", line 16, in a\n        return b()\n      File \"/var/task/app.py\", line 19, in b\n        raise ValueError(\"Hello, error!\")\n    ValueError: Hello, error!\n\n* Content type validation now has error responses that match the same error\n  response format used for other chalice built in responses.  Chalice was\n  previously relying on API gateway to perform the content type validation.\n  As a result of the ``AWS_PROXY`` work, this logic has moved into the chalice\n  handler and now has a consistent error response::\n\n    $ http https://endpoint/dev/ 'Content-Type: text/plain'\n    HTTP/1.1 415 Unsupported Media Type\n    Content-Type: application/json\n\n    {\n        \"Code\": \"UnsupportedMediaType\",\n        \"Message\": \"Unsupported media type: text/plain\"\n    }\n* The keys in the ``app.current_request.to_dict()`` now match the casing used\n  by the ``AWS_PPROXY`` lambda integration, which are ``lowerCamelCased``.\n  This method is primarily intended for introspection purposes.\n"
  },
  {
    "path": "requirements-dev.in",
    "content": "-r requirements-test.in\npylint<4.0.0\ndoc8<1.0.0\npydocstyle\nflake8\nSphinx==4.3.2\ndocutils\nmypy\nwheel\npygments\ntypes-six\ntypes-python-dateutil\ntypes-PyYAML\nstandard-imghdr"
  },
  {
    "path": "requirements-dev.txt",
    "content": "#\n# This file is autogenerated by pip-compile with Python 3.9\n# by the following command:\n#\n#    pip-compile --output-file=requirements-dev.txt requirements-dev.in\n#\nalabaster==0.7.13\n    # via sphinx\nastroid==3.3.10\n    # via pylint\nattrs==24.2.0\n    # via hypothesis\nbabel==2.16.0\n    # via sphinx\nboto3==1.35.66\n    # via -r requirements-test.in\nbotocore==1.35.66\n    # via\n    #   boto3\n    #   s3transfer\ncertifi==2024.8.30\n    # via requests\ncharset-normalizer==3.4.0\n    # via requests\ncoverage[toml]==7.6.1\n    # via\n    #   -r requirements-test.in\n    #   pytest-cov\ndill==0.3.9\n    # via pylint\ndoc8==0.11.2\n    # via -r requirements-dev.in\ndocutils==0.17.1\n    # via\n    #   -r requirements-dev.in\n    #   doc8\n    #   restructuredtext-lint\n    #   sphinx\nexceptiongroup==1.2.2\n    # via\n    #   hypothesis\n    #   pytest\nflake8==7.1.1\n    # via -r requirements-dev.in\nhypothesis==6.113.0\n    # via -r requirements-test.in\nidna==3.10\n    # via requests\nimagesize==1.4.1\n    # via sphinx\niniconfig==2.0.0\n    # via pytest\nisort==5.13.2\n    # via pylint\njinja2==3.1.4\n    # via sphinx\njmespath==1.0.1\n    # via\n    #   boto3\n    #   botocore\nmarkupsafe==2.1.5\n    # via jinja2\nmccabe==0.7.0\n    # via\n    #   flake8\n    #   pylint\nmypy==1.13.0\n    # via -r requirements-dev.in\nmypy-extensions==1.0.0\n    # via mypy\npackaging==24.2\n    # via\n    #   pytest\n    #   sphinx\npbr==6.1.0\n    # via stevedore\nplatformdirs==4.3.6\n    # via pylint\npluggy==1.5.0\n    # via pytest\npycodestyle==2.12.1\n    # via flake8\npydocstyle==6.3.0\n    # via -r requirements-dev.in\npyflakes==3.2.0\n    # via flake8\npygments==2.18.0\n    # via\n    #   -r requirements-dev.in\n    #   doc8\n    #   sphinx\npylint==3.3.7\n    # via -r requirements-dev.in\npytest==8.3.3\n    # via\n    #   -r requirements-test.in\n    #   pytest-cov\npytest-cov==5.0.0\n    # via -r requirements-test.in\npython-dateutil==2.9.0.post0\n    # via botocore\nrequests==2.32.3\n    # via\n    #   -r requirements-test.in\n    #   sphinx\nrestructuredtext-lint==1.4.0\n    # via doc8\ns3transfer==0.10.4\n    # via boto3\nsix==1.16.0\n    # via python-dateutil\nsnowballstemmer==2.2.0\n    # via\n    #   pydocstyle\n    #   sphinx\nsortedcontainers==2.4.0\n    # via hypothesis\nsphinx==4.3.2\n    # via -r requirements-dev.in\nsphinxcontrib-applehelp==1.0.4\n    # via sphinx\nsphinxcontrib-devhelp==1.0.2\n    # via sphinx\nsphinxcontrib-htmlhelp==2.0.1\n    # via sphinx\nsphinxcontrib-jsmath==1.0.1\n    # via sphinx\nsphinxcontrib-qthelp==1.0.3\n    # via sphinx\nsphinxcontrib-serializinghtml==1.1.5\n    # via sphinx\nstandard-imghdr==3.13.0\n    # via -r requirements-dev.in\nstevedore==5.3.0\n    # via doc8\ntomli==2.1.0\n    # via\n    #   coverage\n    #   mypy\n    #   pylint\n    #   pytest\ntomlkit==0.13.2\n    # via pylint\ntypes-python-dateutil==2.9.0.20241003\n    # via -r requirements-dev.in\ntypes-pyyaml==6.0.12.20240917\n    # via -r requirements-dev.in\ntypes-six==1.16.21.20241105\n    # via -r requirements-dev.in\ntyping-extensions==4.12.2\n    # via\n    #   astroid\n    #   mypy\n    #   pylint\nurllib3==1.26.20\n    # via\n    #   botocore\n    #   requests\nwebsocket-client==1.8.0\n    # via -r requirements-test.in\nwheel==0.45.0\n    # via -r requirements-dev.in\n\n# The following packages are considered to be unsafe in a requirements file:\n# setuptools\n"
  },
  {
    "path": "requirements-test.in",
    "content": "pytest\nboto3<2.0.0\nhypothesis\ncoverage\nwebsocket-client<2.0.0\npytest-cov\nrequests\n"
  },
  {
    "path": "requirements-test.txt",
    "content": "#\n# This file is autogenerated by pip-compile with Python 3.9\n# by the following command:\n#\n#    pip-compile --output-file=requirements-test.txt requirements-test.in\n#\nattrs==23.1.0\n    # via hypothesis\nboto3==1.33.13\n    # via -r requirements-test.in\nbotocore==1.33.13\n    # via\n    #   boto3\n    #   s3transfer\ncertifi==2023.11.17\n    # via requests\ncharset-normalizer==3.3.2\n    # via requests\ncoverage[toml]==7.2.7\n    # via\n    #   -r requirements-test.in\n    #   pytest-cov\nexceptiongroup==1.2.0\n    # via\n    #   hypothesis\n    #   pytest\nhypothesis==6.79.4\n    # via -r requirements-test.in\nidna==3.6\n    # via requests\niniconfig==2.0.0\n    # via pytest\njmespath==1.0.1\n    # via\n    #   boto3\n    #   botocore\npackaging==23.2\n    # via pytest\npluggy==1.2.0\n    # via pytest\npytest==7.4.3\n    # via\n    #   -r requirements-test.in\n    #   pytest-cov\npytest-cov==4.1.0\n    # via -r requirements-test.in\npython-dateutil==2.8.2\n    # via botocore\nrequests==2.31.0\n    # via -r requirements-test.in\ns3transfer==0.8.2\n    # via boto3\nsix==1.16.0\n    # via python-dateutil\nsortedcontainers==2.4.0\n    # via hypothesis\ntomli==2.0.1\n    # via\n    #   coverage\n    #   pytest\nurllib3==1.26.18\n    # via\n    #   botocore\n    #   requests\nwebsocket-client==1.6.1\n    # via -r requirements-test.in\n"
  },
  {
    "path": "scripts/gh-page-docs",
    "content": "#!/bin/bash\n# Run this from the rootdir of the repository:\n#\n# ./scripts/gh-page-docs\n#\n# This script will check the docs for errors, render them\n# to html, then copy them over to a local (separate) checkout\n# of this repo's gh-pages branch.\n# Do not run this with a virtualenv activated.  This is intended\n# to be run in CI systems where you start with system python.\nset -e\n\nCHECKOUT_DIR=\"/tmp/chalice-gh-doc-build\"\nVENV_DIR=\"/tmp/chalice-gh-doc-build-venv37\"\n\n\necho \"Setting up environment\"\npython3 -m venv $VENV_DIR\nsource \"${VENV_DIR}/bin/activate\"\necho\necho\nwhich python3\npython3 -c \"import sys; print(sys.executable)\"\nwhich pip3\necho\necho\npython3 -m pip install -e .\npython3 -m pip install -r requirements-dev.txt\n\n\n# Don't allow docs to be deployed if there's any errors.\necho \"Linting docs and checking for errors\"\nmake doccheck\n\necho \"Building docs\"\ncd docs\nmake clean && make html\n\necho\necho \"Copy docs to local checkout\"\nrm -rf \"${CHECKOUT_DIR}\"\ngit clone https://github.com/aws/chalice.git --branch gh-pages \\\n\t--single-branch ${CHECKOUT_DIR}\nrsync -av --delete --exclude '.git' build/html/ ${CHECKOUT_DIR}/\n# Add a .nojekyll file so make sure we don't ignore _static\n# paths.\ntouch ${CHECKOUT_DIR}/.nojekyll\n\necho \"Commiting docs.\"\ncd ${CHECKOUT_DIR}\ngit add -A .\ngit commit -am \"Updating generated documentation\"\ngit remote add upstream git@github.com:aws/chalice.git\necho \"Docs are available at ${CHECKOUT_DIR}\"\n# This step is usually handled by the CI system that has access\n# to the creds needed to push back to github.\necho \"Run 'git push upstream gh-pages' to deploy the docs to github pages\"\n"
  },
  {
    "path": "scripts/release",
    "content": "#!/usr/bin/env python3\nimport os\nimport re\nimport subprocess\n\nimport click\n\n\nROOT_DIR = os.path.dirname(\n    os.path.dirname(os.path.abspath(__file__)),\n)\n\n\n@click.group()\ndef cli():\n    \"\"\"Command line tool for managing releases.\n\n    To do a chalice release, run these commands::\n\n        \\b\n        $ NEXT_VERSION=$(jmeslog query next-version)\n        $ scripts/release bump-version --version-number ${NEXT_VERSION}\n        $ git add -A .\n        $ git commit -m \"Bumping version to $NEXT_VERSION\"\n        $ scripts/release tag-release\n        $ scripts/release build-release\n        $ git push upstream master --tags\n        $ twine upload dist/chalice-*\n\n    \"\"\"\n    pass\n\n\n@cli.command('bump-version')\n@click.option('--version-number')\ndef bump_version(version_number):\n    \"\"\"Update necessary files with next version number.\"\"\"\n    print(f\"Bumping version to: {version_number}\")\n    _create_new_changelog_release()\n    for filename, replacer in get_files_to_change().items():\n        print(\"Bumping version in %s\" % filename)\n        with open(filename, 'r') as f:\n            contents = f.read()\n            if callable(replacer):\n                new_contents = replacer(version_number, contents)\n            else:\n                new_contents = _regex_based_version_bump(\n                    version_number,\n                    replacer,\n                    contents)\n            with open(filename, 'w') as f:\n                f.write(new_contents)\n\n\ndef _create_new_changelog_release():\n    # This takes everything from .changes/next-release/ and creates\n    # a new release entry for them.\n    subprocess.check_call(['jmeslog', 'new-release'])\n\n\n@cli.command('build-release')\ndef build_release():\n    \"\"\"Build sdist/whl files.\"\"\"\n    original = os.getcwd()\n    os.chdir(ROOT_DIR)\n    try:\n        subprocess.check_call(\n            ['python', 'setup.py', 'sdist', 'bdist_wheel']\n        )\n    finally:\n        os.chdir(original)\n\n\n@cli.command('tag-release')\ndef tag_release():\n    \"\"\"Create a git tag based on the current version number.\"\"\"\n    # We're assuming that setup.py has already been updated\n    # manually or using scripts/release/bump-version so the\n    # current version in setup.py is the version number we should tag.\n    version_number = get_current_version_number()\n    click.echo(\"Tagging %s release\" % version_number)\n    subprocess.check_call(\n        ['git', 'tag', '-a', version_number,\n         '-m', 'Tagging %s release' % version_number],\n    )\n\n\n@cli.command('get-version')\ndef get_version():\n    \"\"\"Print the current version number in setup.py.\"\"\"\n    click.echo(get_current_version_number())\n\n\ndef get_files_to_change():\n    # A mapping of all files that require version bumps.\n    # You can either specify:\n    # * Tuple[str, str] - regex to search, replacement string\n    # * Callable[[str, str], str] - function to handle custom logic\n    files_with_version_numbers = {\n        'chalice/app.py': (\n            \"__version__: str = '.*'\", \"__version__: str = '{version}'\"),\n        'CHANGELOG.md': update_changelog,\n        'docs/source/conf.py': update_doc_conf,\n        'setup.py': (\"version='(.*)'\", \"version='{version}'\"),\n    }\n    return files_with_version_numbers\n\n\ndef _regex_based_version_bump(next_version_number, replacer, contents):\n    regex = replacer[0]\n    replacement = replacer[1].format(version=next_version_number)\n    new_contents = re.sub(regex, replacement, contents)\n    return new_contents\n\n\ndef update_changelog(next_version_number, contents):\n    output = subprocess.check_output(['jmeslog', 'render', '-t', 'changelog'])\n    return output.decode('utf-8')\n\n\ndef update_doc_conf(next_version_number, contents):\n    # For the docs the 'version' is only X.Y\n    # and the release is X.Y.Z\n    version = '.'.join(next_version_number.split('.')[:2])\n    release = next_version_number\n    new_contents = []\n    for line in contents.splitlines():\n        if line.startswith('version ='):\n            new_contents.append(\"version = u'%s'\" % version)\n        elif line.startswith('release = '):\n            new_contents.append(\"release = u'%s'\" % release)\n        else:\n            new_contents.append(line)\n    # Ensure the file ends with a newline.\n    new_contents.append('')\n    return '\\n'.join(new_contents)\n\n\ndef get_next_version_number(release_type):\n    # Returns a string like '1.0.0'.\n    current = get_current_version_number()\n    # Convert to a list of ints: [1, 0, 0].\n    version_parts = list(int(i) for i in current.split('.'))\n    # We've already validated that release_type is from a fixed\n    # list of choices so we know it's going to be one of these.\n    # We only support integer version parts, which shouldn't be\n    # a problem now that we're post 1.0.\n    if release_type == 'patch':\n        version_parts[-1] += 1\n    elif release_type == 'minor':\n        version_parts[1] += 1\n        version_parts[-1] = 0\n    return '.'.join(str(i) for i in version_parts)\n\n\ndef get_current_version_number():\n    # We can avoid executing setup.py because we know\n    # specifically how the version is hardcoded in the setup.py file.\n    # This won't work for the general case.\n    regex = re.compile(\"version='(.*)',\")\n    with open(os.path.join(ROOT_DIR, 'setup.py')) as f:\n        for line in f:\n            match = regex.search(line)\n            if match is not None:\n                return match.groups()[0]\n    raise RuntimeError(\"Could not find version number from setup.py\")\n\n\ndef main():\n    return cli()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "setup.cfg",
    "content": "[mypy]\n\n[mypy-chalice.vendored.*]\nignore_errors = true\n\n[mypy-chalice.templates.*]\nignore_errors = true\n"
  },
  {
    "path": "setup.py",
    "content": "#!/usr/bin/env python\nimport os\nfrom setuptools import setup, find_packages\n\n\nwith open('README.rst') as readme_file:\n    README = readme_file.read()\n\n\ndef recursive_include(relative_dir):\n    all_paths = []\n    root_prefix = os.path.join(\n        os.path.dirname(os.path.abspath(__file__)), 'chalice')\n    full_path = os.path.join(root_prefix, relative_dir)\n    for rootdir, _, filenames in os.walk(full_path):\n        for filename in filenames:\n            abs_filename = os.path.join(rootdir, filename)\n            all_paths.append(abs_filename[len(root_prefix) + 1:])\n    return all_paths\n\n\ninstall_requires = [\n    'click>=7,<9.0',\n    'botocore>=1.14.0,<2.0.0',\n    'six>=1.10.0,<2.0.0',\n    'pip>=9,<25.1',\n    'jmespath>=0.9.3,<2.0.0',\n    'pyyaml>=5.3.1,<7.0.0',\n    'inquirer>=3.0.0,<4.0.0',\n    'wheel',\n    'setuptools'\n]\n\nsetup(\n    name='chalice',\n    version='1.32.0',\n    description=\"Microframework\",\n    long_description=README,\n    author=\"James Saryerwinnie\",\n    author_email='js@jamesls.com',\n    url='https://github.com/aws/chalice',\n    packages=find_packages(exclude=['tests', 'tests.*']),\n    install_requires=install_requires,\n    extras_require={\n        'event-file-poller': ['watchdog==2.3.1'],\n        'cdk': [\n            'aws_cdk.aws_iam>=1.85.0,<2.0',\n            'aws_cdk.aws-s3-assets>=1.85.0,<2.0',\n            'aws_cdk.cloudformation-include>=1.85.0,<2.0',\n            'aws_cdk.core>=1.85.0,<2.0',\n        ],\n        'cdkv2': [\"aws-cdk-lib>2.0,<3.0\"]\n    },\n    license=\"Apache License 2.0\",\n    package_data={'chalice': [\n        '*.json', '*.pyi', 'py.typed'] + recursive_include('templates')},\n    include_package_data=True,\n    zip_safe=False,\n    keywords='chalice',\n    entry_points={\n        'console_scripts': [\n            'chalice = chalice.cli:main',\n        ]\n    },\n    classifiers=[\n        'Development Status :: 5 - Production/Stable',\n        'Intended Audience :: Developers',\n        'License :: OSI Approved :: Apache Software License',\n        'Natural Language :: English',\n        \"Programming Language :: Python :: 3\",\n        'Programming Language :: Python :: 3.9',\n        'Programming Language :: Python :: 3.10',\n        'Programming Language :: Python :: 3.11',\n        'Programming Language :: Python :: 3.12',\n        'Programming Language :: Python :: 3.13',\n    ],\n)\n"
  },
  {
    "path": "tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/aws/__init__.py",
    "content": ""
  },
  {
    "path": "tests/aws/conftest.py",
    "content": "DEPLOY_TEST_BASENAME = 'test_features.py'\n\n\ndef pytest_collection_modifyitems(session, config, items):\n    # Ensure that all tests with require a redeploy are run after\n    # tests that don't need a redeploy.\n    start, end = _get_start_end_index(DEPLOY_TEST_BASENAME, items)\n    marked = []\n    unmarked = []\n    for item in items[start:end]:\n        if item.get_closest_marker('on_redeploy') is not None:\n            marked.append(item)\n        else:\n            unmarked.append(item)\n    items[start:end] = unmarked + marked\n\n\ndef _get_start_end_index(basename, items):\n    # precondition: all the tests for test_features.py are\n    # in a contiguous range.  This is the case because pytest\n    # will group all tests in a module together.\n    matched = [item.fspath.basename == basename for item in items]\n    if not any(matched):\n        return 0, len(items)\n    return (\n        matched.index(True),\n        len(matched) - list(reversed(matched)).index(True)\n    )\n"
  },
  {
    "path": "tests/aws/test_features.py",
    "content": "import json\nimport os\nimport time\nimport shutil\nimport uuid\nfrom unittest import mock\n\nimport botocore.session\nimport pytest\nimport requests\nimport websocket\n\nfrom chalice.cli.factory import CLIFactory\nfrom chalice.utils import OSUtils, UI\nfrom chalice.deploy.deployer import ChaliceDeploymentError\nfrom chalice.config import DeployedResources\n\n\nCURRENT_DIR = os.path.dirname(os.path.abspath(__file__))\nPROJECT_DIR = os.path.join(CURRENT_DIR, 'testapp')\nAPP_FILE = os.path.join(PROJECT_DIR, 'app.py')\nRANDOM_APP_NAME = 'smoketest-%s' % str(uuid.uuid4())[:13]\n\n\ndef retry(max_attempts, delay):\n    def _create_wrapped_retry_function(function):\n        def _wrapped_with_retry(*args, **kwargs):\n            for _ in range(max_attempts):\n                result = function(*args, **kwargs)\n                if result is not None:\n                    return result\n                time.sleep(delay)\n            raise RuntimeError(\"Exhausted max retries of %s for function: %s\"\n                               % (max_attempts, function))\n        return _wrapped_with_retry\n    return _create_wrapped_retry_function\n\n\nclass InternalServerError(Exception):\n    pass\n\n\nclass SmokeTestApplication(object):\n\n    # Number of seconds to wait after redeploy before starting\n    # to poll for successful 200.\n    _REDEPLOY_SLEEP = 30\n    # Seconds to wait between poll attempts after redeploy.\n    _POLLING_DELAY = 5\n    # Number of successful wait attempts before we consider the app\n    # stabilized.\n    _NUM_SUCCESS = 3\n\n    def __init__(self, deployed_values, stage_name, app_name,\n                 app_dir, region):\n        self._deployed_resources = DeployedResources(deployed_values)\n        self.stage_name = stage_name\n        self.app_name = app_name\n        # The name of the tmpdir where the app is copied.\n        self.app_dir = app_dir\n        self._has_redeployed = False\n        self._region = region\n\n    @property\n    def url(self):\n        return (\n            \"https://{rest_api_id}.execute-api.{region}.amazonaws.com/\"\n            \"{api_gateway_stage}\".format(rest_api_id=self.rest_api_id,\n                                         region=self._region,\n                                         api_gateway_stage='api')\n        )\n\n    @property\n    def rest_api_id(self):\n        return self._deployed_resources.resource_values(\n            'rest_api')['rest_api_id']\n\n    @property\n    def websocket_api_id(self):\n        return self._deployed_resources.resource_values(\n            'websocket_api')['websocket_api_id']\n\n    @property\n    def websocket_connect_url(self):\n        return (\n            \"wss://{websocket_api_id}.execute-api.{region}.amazonaws.com/\"\n            \"{api_gateway_stage}\".format(\n                websocket_api_id=self.websocket_api_id,\n                region=self._region,\n                api_gateway_stage='api',\n            )\n        )\n\n    @retry(max_attempts=10, delay=5)\n    def get_json(self, url):\n        try:\n            return self._get_json(url)\n        except requests.exceptions.HTTPError:\n            pass\n\n    def _get_json(self, url):\n        if not url.startswith('/'):\n            url = '/' + url\n        response = requests.get(self.url + url)\n        response.raise_for_status()\n        return response.json()\n\n    @retry(max_attempts=10, delay=5)\n    def get_response(self, url, headers=None):\n        try:\n            return self._send_request('GET', url, headers=headers)\n        except InternalServerError:\n            pass\n\n    def _send_request(self, http_method, url, headers=None, data=None):\n        kwargs = {}\n        if headers is not None:\n            kwargs['headers'] = headers\n        if data is not None:\n            kwargs['data'] = data\n        response = requests.request(http_method, self.url + url, **kwargs)\n        if response.status_code >= 500:\n            raise InternalServerError()\n        return response\n\n    @retry(max_attempts=10, delay=5)\n    def post_response(self, url, headers=None, data=None):\n        try:\n            return self._send_request('POST', url, headers=headers, data=data)\n        except InternalServerError:\n            pass\n\n    @retry(max_attempts=10, delay=5)\n    def put_response(self, url):\n        try:\n            return self._send_request('PUT', url)\n        except InternalServerError:\n            pass\n\n    @retry(max_attempts=10, delay=5)\n    def options_response(self, url):\n        try:\n            return self._send_request('OPTIONS', url)\n        except InternalServerError:\n            pass\n\n    def redeploy_once(self):\n        # Redeploy the application once.  If a redeploy\n        # has already happened, this function is a noop.\n        if self._has_redeployed:\n            return\n        new_file = os.path.join(self.app_dir, 'app-redeploy.py')\n        original_app_py = os.path.join(self.app_dir, 'app.py')\n        shutil.move(original_app_py, original_app_py + '.bak')\n        shutil.copy(new_file, original_app_py)\n        _deploy_app(self.app_dir)\n        self._has_redeployed = True\n        # Give it settling time before running more tests.\n        time.sleep(self._REDEPLOY_SLEEP)\n        for _ in range(self._NUM_SUCCESS):\n            self._wait_for_stablize()\n            time.sleep(self._POLLING_DELAY)\n\n    def _wait_for_stablize(self):\n        # After a deployment we sometimes need to wait for\n        # API Gateway to propagate all of its changes.\n        # We're going to give it num_attempts to give us a\n        # 200 response before failing.\n        return self.get_json('/')\n\n\n@pytest.fixture\ndef apig_client():\n    s = botocore.session.get_session()\n    return s.create_client('apigateway')\n\n\n@pytest.fixture(scope='module')\ndef smoke_test_app(tmpdir_factory):\n    # We can't use the monkeypatch fixture here because this is a module scope\n    # fixture and monkeypatch is a function scoped fixture.\n    os.environ['APP_NAME'] = RANDOM_APP_NAME\n    tmpdir = str(tmpdir_factory.mktemp(RANDOM_APP_NAME))\n    OSUtils().copytree(PROJECT_DIR, tmpdir)\n    _inject_app_name(tmpdir)\n    application = _deploy_app(tmpdir)\n    yield application\n    _delete_app(application, tmpdir)\n    os.environ.pop('APP_NAME')\n\n\ndef _inject_app_name(dirname):\n    config_filename = os.path.join(dirname, '.chalice', 'config.json')\n    with open(config_filename) as f:\n        data = json.load(f)\n    data['app_name'] = RANDOM_APP_NAME\n    data['stages']['dev']['environment_variables']['APP_NAME'] = \\\n        RANDOM_APP_NAME\n    with open(config_filename, 'w') as f:\n        f.write(json.dumps(data, indent=2))\n\n\ndef _deploy_app(temp_dirname):\n    factory = CLIFactory(temp_dirname)\n    config = factory.create_config_obj(\n        chalice_stage_name='dev',\n        autogen_policy=True\n    )\n    session = factory.create_botocore_session()\n    d = factory.create_default_deployer(session, config, UI())\n    region = session.get_config_variable('region')\n    deployed = _deploy_with_retries(d, config)\n    application = SmokeTestApplication(\n        region=region,\n        deployed_values=deployed,\n        stage_name='dev',\n        app_name=RANDOM_APP_NAME,\n        app_dir=temp_dirname,\n    )\n    return application\n\n\n@retry(max_attempts=10, delay=20)\ndef _deploy_with_retries(deployer, config):\n    try:\n        deployed_stages = deployer.deploy(config, 'dev')\n        return deployed_stages\n    except ChaliceDeploymentError as e:\n        # API Gateway aggressively throttles deployments.\n        # If we run into this case, we just wait and try\n        # again.\n        error_code = _get_error_code_from_exception(e)\n        if error_code != 'TooManyRequestsException':\n            raise\n\n\ndef _get_error_code_from_exception(exception):\n    error_response = getattr(exception.original_error, 'response', None)\n    if error_response is None:\n        return None\n    return error_response.get('Error', {}).get('Code')\n\n\ndef _delete_app(application, temp_dirname):\n    factory = CLIFactory(temp_dirname)\n    config = factory.create_config_obj(\n        chalice_stage_name='dev',\n        autogen_policy=True\n    )\n    session = factory.create_botocore_session()\n    d = factory.create_deletion_deployer(session, UI())\n    _deploy_with_retries(d, config)\n\n\ndef test_returns_simple_response(smoke_test_app):\n    assert smoke_test_app.get_json('/') == {'hello': 'world'}\n\n\ndef test_can_have_nested_routes(smoke_test_app):\n    assert smoke_test_app.get_json('/a/b/c/d/e/f/g') == {'nested': True}\n\n\ndef test_supports_path_params(smoke_test_app):\n    assert smoke_test_app.get_json('/path/foo') == {'path': 'foo'}\n    assert smoke_test_app.get_json('/path/bar') == {'path': 'bar'}\n\n\ndef test_path_params_mapped_in_api(smoke_test_app, apig_client):\n    # Use the API Gateway API to ensure that path parameters\n    # are modeled as such.  Otherwise this will break\n    # SDK generation and any future features that depend\n    # on params.  We could try to verify the generated\n    # javascript SDK looks ok.  Instead we're going to\n    # query the resources we've created in API gateway\n    # and make sure requestParameters are present.\n    rest_api_id = smoke_test_app.rest_api_id\n    response = apig_client.get_export(restApiId=rest_api_id,\n                                      stageName='api',\n                                      exportType='swagger')\n    swagger_doc = json.loads(response['body'].read())\n    route_config = swagger_doc['paths']['/path/{name}']['get']\n    assert route_config.get('parameters', {}) == [\n        {'name': 'name', 'in': 'path', 'required': True, 'type': 'string'},\n    ]\n\n\ndef test_single_doc_mapped_in_api(smoke_test_app, apig_client):\n    # We'll use the same API Gateway technique as in\n    # test_path_params_mapped_in_api()\n    rest_api_id = smoke_test_app.rest_api_id\n    doc_parts = apig_client.get_documentation_parts(\n        restApiId=rest_api_id,\n        type='METHOD',\n        path='/singledoc'\n    )\n    doc_props = json.loads(doc_parts['items'][0]['properties'])\n    assert 'summary' in doc_props\n    assert 'description' not in doc_props\n    assert doc_props['summary'] == 'Single line docstring.'\n\n\ndef test_multi_doc_mapped_in_api(smoke_test_app, apig_client):\n    # We'll use the same API Gateway technique as in\n    # test_path_params_mapped_in_api()\n    rest_api_id = smoke_test_app.rest_api_id\n    doc_parts = apig_client.get_documentation_parts(\n        restApiId=rest_api_id,\n        type='METHOD',\n        path='/multidoc'\n    )\n    doc_props = json.loads(doc_parts['items'][0]['properties'])\n    assert 'summary' in doc_props\n    assert 'description' in doc_props\n    assert doc_props['summary'] == 'Multi-line docstring.'\n    assert doc_props['description'] == 'And here is another line.'\n\n\n@retry(max_attempts=18, delay=10)\ndef _get_resource_id(apig_client, rest_api_id, path):\n    # This is the resource id for the '/path/{name}'\n    # route.  As far as I know this is the best way to get\n    # this id.\n    matches = [\n        resource for resource in\n        apig_client.get_resources(restApiId=rest_api_id)['items']\n        if resource['path'] == path\n    ]\n    if matches:\n        return matches[0]['id']\n\n\ndef test_supports_post(smoke_test_app):\n    response = smoke_test_app.post_response('/post')\n    response.raise_for_status()\n    assert response.json() == {'success': True}\n    with pytest.raises(requests.HTTPError):\n        # Only POST is supported.\n        response = smoke_test_app.get_response('/post')\n        response.raise_for_status()\n\n\ndef test_supports_put(smoke_test_app):\n    response = smoke_test_app.put_response('/put')\n    response.raise_for_status()\n    assert response.json() == {'success': True}\n    with pytest.raises(requests.HTTPError):\n        # Only PUT is supported.\n        response = smoke_test_app.get_response('/put')\n        response.raise_for_status()\n\n\ndef test_supports_shared_routes(smoke_test_app):\n    response = smoke_test_app.get_json('/shared')\n    assert response == {'method': 'GET'}\n    response = smoke_test_app.post_response('/shared')\n    assert response.json() == {'method': 'POST'}\n\n\ndef test_can_read_json_body_on_post(smoke_test_app):\n    response = smoke_test_app.post_response(\n        '/jsonpost', data=json.dumps({'hello': 'world'}),\n        headers={'Content-Type': 'application/json'})\n    response.raise_for_status()\n    assert response.json() == {'json_body': {'hello': 'world'}}\n\n\ndef test_can_raise_bad_request(smoke_test_app):\n    response = smoke_test_app.get_response('/badrequest')\n    assert response.status_code == 400\n    assert response.json()['Code'] == 'BadRequestError'\n    assert response.json()['Message'] == 'Bad request.'\n\n\ndef test_can_raise_not_found(smoke_test_app):\n    response = smoke_test_app.get_response('/notfound')\n    assert response.status_code == 404\n    assert response.json()['Code'] == 'NotFoundError'\n\n\ndef test_unexpected_error_raises_500_in_prod_mode(smoke_test_app):\n    # Can't use smoke_test_app.get_response() because we're explicitly\n    # testing for a 500.\n    response = requests.get(smoke_test_app.url + '/arbitrary-error')\n    assert response.status_code == 500\n    assert response.json()['Code'] == 'InternalServerError'\n    assert 'internal server error' in response.json()['Message']\n\n\ndef test_can_route_multiple_methods_in_one_view(smoke_test_app):\n    response = smoke_test_app.get_response('/multimethod')\n    response.raise_for_status()\n    assert response.json()['method'] == 'GET'\n\n    response = smoke_test_app.post_response('/multimethod')\n    response.raise_for_status()\n    assert response.json()['method'] == 'POST'\n\n\ndef test_form_encoded_content_type(smoke_test_app):\n    response = smoke_test_app.post_response('/formencoded',\n                                            data={'foo': 'bar'})\n    response.raise_for_status()\n    assert response.json() == {'parsed': {'foo': ['bar']}}\n\n\ndef test_can_round_trip_binary(smoke_test_app):\n    # xde xed xbe xef will fail unicode decoding because xbe is an invalid\n    # start byte in utf-8.\n    bin_data = b'\\xDE\\xAD\\xBE\\xEF'\n    response = smoke_test_app.post_response(\n        '/binary',\n        headers={'Content-Type': 'application/octet-stream',\n                 'Accept': 'application/octet-stream'},\n        data=bin_data)\n    response.raise_for_status()\n    assert response.content == bin_data\n\n\ndef test_can_round_trip_binary_custom_content_type(smoke_test_app):\n    bin_data = b'\\xDE\\xAD\\xBE\\xEF'\n    response = smoke_test_app.post_response(\n        '/custom-binary',\n        headers={'Content-Type': 'application/binary',\n                 'Accept': 'application/binary'},\n        data=bin_data\n    )\n    assert response.content == bin_data\n\n\ndef test_can_return_default_binary_data_to_a_browser(smoke_test_app):\n    base64encoded_response = b'3q2+7w=='\n    accept = 'text/html,application/xhtml+xml;q=0.9,image/webp,*/*;q=0.8'\n    response = smoke_test_app.get_response(\n        '/get-binary', headers={'Accept': accept})\n    response.raise_for_status()\n    assert response.content == base64encoded_response\n\n\ndef _assert_contains_access_control_allow_methods(headers, methods):\n    actual_methods = headers['Access-Control-Allow-Methods'].split(',')\n    assert sorted(methods) == sorted(actual_methods), (\n        'The expected allowed methods does not match the actual allowed '\n        'methods for CORS.')\n\n\ndef test_can_support_cors(smoke_test_app):\n    response = smoke_test_app.get_response('/cors')\n    response.raise_for_status()\n    assert response.headers['Access-Control-Allow-Origin'] == '*'\n\n    # Should also have injected an OPTIONs request.\n    response = smoke_test_app.options_response('/cors')\n    response.raise_for_status()\n    headers = response.headers\n    assert headers['Access-Control-Allow-Origin'] == '*'\n    assert headers['Access-Control-Allow-Headers'] == (\n        'Authorization,Content-Type,X-Amz-Date,X-Amz-Security-Token,'\n        'X-Api-Key')\n    _assert_contains_access_control_allow_methods(\n        headers, ['GET', 'POST', 'PUT', 'OPTIONS'])\n\n\ndef test_can_support_custom_cors(smoke_test_app):\n    response = smoke_test_app.get_response('/custom_cors')\n    response.raise_for_status()\n    expected_allow_origin = 'https://foo.example.com'\n    assert response.headers[\n        'Access-Control-Allow-Origin'] == expected_allow_origin\n\n    # Should also have injected an OPTIONs request.\n    response = smoke_test_app.options_response('/custom_cors')\n    response.raise_for_status()\n    headers = response.headers\n    assert headers['Access-Control-Allow-Origin'] == expected_allow_origin\n    assert headers['Access-Control-Allow-Headers'] == (\n        'Authorization,Content-Type,X-Amz-Date,X-Amz-Security-Token,'\n        'X-Api-Key,X-Special-Header')\n    _assert_contains_access_control_allow_methods(\n        headers, ['GET', 'POST', 'PUT', 'OPTIONS'])\n    assert headers['Access-Control-Max-Age'] == '600'\n    assert headers['Access-Control-Expose-Headers'] == 'X-Special-Header'\n    assert headers['Access-Control-Allow-Credentials'] == 'true'\n\n\ndef test_to_dict_is_also_json_serializable(smoke_test_app):\n    assert 'headers' in smoke_test_app.get_json('/todict')\n\n\ndef test_multifile_support(smoke_test_app):\n    response = smoke_test_app.get_json('/multifile')\n    assert response == {'message': 'success'}\n\n\ndef test_custom_response(smoke_test_app):\n    response = smoke_test_app.get_response('/custom-response')\n    response.raise_for_status()\n    # Custom header\n    assert response.headers['Content-Type'] == 'text/plain'\n    # Multi headers\n    assert response.headers['Set-Cookie'] == 'key=value, foo=bar'\n    # Custom status code\n    assert response.status_code == 204\n\n\ndef test_api_key_required_fails_with_no_key(smoke_test_app):\n    response = smoke_test_app.get_response('/api-key-required')\n    # Request should fail because we're not providing\n    # an API key.\n    assert response.status_code == 403\n\n\ndef test_can_handle_charset(smoke_test_app):\n    # Should pass content type validation even with charset specified.\n    response = smoke_test_app.get_response(\n        '/json-only',\n        headers={'Content-Type': 'application/json; charset=utf-8'}\n    )\n    assert response.status_code == 200\n\n\ndef test_can_use_builtin_custom_auth(smoke_test_app):\n    url = '/builtin-auth'\n    # First time without an Auth header, we should fail.\n    response = smoke_test_app.get_response(url)\n    assert response.status_code == 401\n    # Now with the proper auth header, things should work.\n    response = smoke_test_app.get_response(\n        url, headers={'Authorization': 'yes'}\n    )\n    assert response.status_code == 200\n    context = response.json()['context']\n    assert 'authorizer' in context\n    # The keyval context we added shuld also be in the authorizer\n    # dict.\n    assert context['authorizer']['foo'] == 'bar'\n\n\ndef test_can_use_shared_auth(smoke_test_app):\n    response = smoke_test_app.get_response('/fake-profile')\n    # GETs are allowed\n    assert response.status_code == 200\n    # However, POSTs require auth.\n    # This has the same auth config as /builtin-auth,\n    # so we're testing the auth handler can be shared.\n    assert smoke_test_app.post_response('/fake-profile').status_code == 401\n    response = smoke_test_app.post_response('/fake-profile',\n                                            headers={'Authorization': 'yes'})\n    assert response.status_code == 200\n    context = response.json()['context']\n    assert 'authorizer' in context\n    assert context['authorizer']['foo'] == 'bar'\n\n\ndef test_empty_raw_body(smoke_test_app):\n    response = smoke_test_app.post_response('/repr-raw-body')\n    response.raise_for_status()\n    assert response.json() == {'repr-raw-body': ''}\n\n\ndef test_websocket_lifecycle(smoke_test_app):\n    ws = websocket.create_connection(smoke_test_app.websocket_connect_url)\n    ws.send(\"Hello, World 1\")\n    ws.recv()\n    ws.close()\n    ws = websocket.create_connection(smoke_test_app.websocket_connect_url)\n    ws.send(\"Hello, World 2\")\n    second_response = json.loads(ws.recv())\n    ws.close()\n\n    expected_second_response = [\n        [mock.ANY, 'Hello, World 1'],\n        [mock.ANY, 'Hello, World 2']\n    ]\n    assert expected_second_response == second_response\n    assert second_response[0][0] != second_response[1][0]\n\n\n@pytest.mark.on_redeploy\ndef test_redeploy_no_change_view(smoke_test_app):\n    smoke_test_app.redeploy_once()\n    assert smoke_test_app.get_json('/') == {'hello': 'world'}\n\n\n@pytest.mark.on_redeploy\ndef test_redeploy_changed_function(smoke_test_app):\n    smoke_test_app.redeploy_once()\n    assert smoke_test_app.get_json('/a/b/c/d/e/f/g') == {\n        'redeployed': True}\n\n\n@pytest.mark.on_redeploy\ndef test_redeploy_new_function(smoke_test_app):\n    smoke_test_app.redeploy_once()\n    assert smoke_test_app.get_json('/redeploy') == {'success': True}\n\n\n@pytest.mark.on_redeploy\ndef test_redeploy_change_route_info(smoke_test_app):\n    smoke_test_app.redeploy_once()\n    # POST is no longer allowed:\n    assert smoke_test_app.post_response('/multimethod').status_code == 403\n    # But PUT is now allowed in the redeployed app.py\n    assert smoke_test_app.put_response('/multimethod').status_code == 200\n\n\n@pytest.mark.on_redeploy\ndef test_redeploy_view_deleted(smoke_test_app):\n    smoke_test_app.redeploy_once()\n    response = smoke_test_app.get_response('/path/foo')\n    # Request should fail because it's not in the redeployed\n    # app.py\n    assert response.status_code == 403\n"
  },
  {
    "path": "tests/aws/test_websockets.py",
    "content": "import os\nimport json\nimport uuid\nimport threading\nimport shutil\nimport time\n\nimport pytest\nimport websocket\n\nfrom chalice.cli.factory import CLIFactory\nfrom chalice.utils import OSUtils, UI\nfrom chalice.deploy.deployer import ChaliceDeploymentError\nfrom chalice.config import DeployedResources\n\n\nCURRENT_DIR = os.path.dirname(os.path.abspath(__file__))\nPROJECT_DIR = os.path.join(CURRENT_DIR, 'testwebsocketapp')\nAPP_FILE = os.path.join(PROJECT_DIR, 'app.py')\nRANDOM_APP_NAME = 'smoketest-%s' % str(uuid.uuid4())[:13]\n\n\ndef retry(max_attempts, delay):\n    def _create_wrapped_retry_function(function):\n        def _wrapped_with_retry(*args, **kwargs):\n            for _ in range(max_attempts):\n                result = function(*args, **kwargs)\n                if result is not None:\n                    return result\n                time.sleep(delay)\n            raise RuntimeError(\"Exhausted max retries of %s for function: %s\"\n                               % (max_attempts, function))\n        return _wrapped_with_retry\n    return _create_wrapped_retry_function\n\n\ndef _create_ws_connection(url, attempts=5, delay=5):\n    for _ in range(attempts):\n        try:\n            ws = websocket.create_connection(url)\n            return ws\n        except websocket.WebSocketBadStatusException:\n            time.sleep(delay)\n\n\ndef _inject_app_name(dirname):\n    config_filename = os.path.join(dirname, '.chalice', 'config.json')\n    with open(config_filename) as f:\n        data = json.load(f)\n    data['app_name'] = RANDOM_APP_NAME\n    data['stages']['dev']['environment_variables']['APP_NAME'] = \\\n        RANDOM_APP_NAME\n    with open(config_filename, 'w') as f:\n        f.write(json.dumps(data, indent=2))\n\n\ndef _deploy_app(temp_dirname):\n    factory = CLIFactory(temp_dirname)\n    config = factory.create_config_obj(\n        chalice_stage_name='dev',\n        autogen_policy=True\n    )\n    session = factory.create_botocore_session()\n    d = factory.create_default_deployer(session, config, UI())\n    region = session.get_config_variable('region')\n    deployed = _deploy_with_retries(d, config)\n    application = SmokeTestApplication(\n        region=region,\n        deployed_values=deployed,\n        stage_name='dev',\n        app_name=RANDOM_APP_NAME,\n        app_dir=temp_dirname,\n    )\n    return application\n\n\n@retry(max_attempts=10, delay=20)\ndef _deploy_with_retries(deployer, config):\n    try:\n        deployed_stages = deployer.deploy(config, 'dev')\n        return deployed_stages\n    except ChaliceDeploymentError as e:\n        # API Gateway aggressively throttles deployments.\n        # If we run into this case, we just wait and try\n        # again.\n        error_code = _get_error_code_from_exception(e)\n        if error_code != 'TooManyRequestsException':\n            raise\n\n\ndef _get_error_code_from_exception(exception):\n    error_response = getattr(exception.original_error, 'response', None)\n    if error_response is None:\n        return None\n    return error_response.get('Error', {}).get('Code')\n\n\ndef _delete_app(application, temp_dirname):\n    factory = CLIFactory(temp_dirname)\n    config = factory.create_config_obj(\n        chalice_stage_name='dev',\n        autogen_policy=True\n    )\n    session = factory.create_botocore_session()\n    d = factory.create_deletion_deployer(session, UI())\n    _deploy_with_retries(d, config)\n\n\nclass SmokeTestApplication(object):\n\n    # Number of seconds to wait after redeploy before starting\n    # to poll for successful 200.\n    _REDEPLOY_SLEEP = 20\n    # Seconds to wait between poll attempts after redeploy.\n    _POLLING_DELAY = 5\n\n    def __init__(self, deployed_values, stage_name, app_name,\n                 app_dir, region):\n        self._deployed_resources = DeployedResources(deployed_values)\n        self.stage_name = stage_name\n        self.app_name = app_name\n        # The name of the tmpdir where the app is copied.\n        self.app_dir = app_dir\n        self._has_redeployed = False\n        self._region = region\n\n    @property\n    def websocket_api_id(self):\n        return self._deployed_resources.resource_values(\n            'websocket_api')['websocket_api_id']\n\n    @property\n    def websocket_connect_url(self):\n        return (\n            \"wss://{websocket_api_id}.execute-api.{region}.amazonaws.com/\"\n            \"{api_gateway_stage}\".format(\n                websocket_api_id=self.websocket_api_id,\n                region=self._region,\n                api_gateway_stage='api',\n            )\n        )\n\n    @property\n    def websocket_message_handler_arn(self):\n        return self._deployed_resources.resource_values(\n            'websocket_message')['lambda_arn']\n\n    @property\n    def region(self):\n        return self._region\n\n    def redeploy_once(self):\n        # Redeploy the application once.  If a redeploy\n        # has already happened, this function is a noop.\n        if self._has_redeployed:\n            return\n        new_file = os.path.join(self.app_dir, 'app-redeploy.py')\n        original_app_py = os.path.join(self.app_dir, 'app.py')\n        shutil.move(original_app_py, original_app_py + '.bak')\n        shutil.copy(new_file, original_app_py)\n        _deploy_app(self.app_dir)\n        self._has_redeployed = True\n        # Give it settling time before running more tests.\n        time.sleep(self._REDEPLOY_SLEEP)\n\n\n@pytest.fixture\ndef smoke_test_app_ws(tmpdir_factory):\n    # We can't use the monkeypatch fixture here because this is a module scope\n    # fixture and monkeypatch is a function scoped fixture.\n    os.environ['APP_NAME'] = RANDOM_APP_NAME\n    tmpdir = str(tmpdir_factory.mktemp(RANDOM_APP_NAME))\n    _create_dynamodb_table(RANDOM_APP_NAME, tmpdir)\n    OSUtils().copytree(PROJECT_DIR, tmpdir)\n    _inject_app_name(tmpdir)\n    application = _deploy_app(tmpdir)\n    yield application\n    _delete_app(application, tmpdir)\n    _delete_dynamodb_table(RANDOM_APP_NAME, tmpdir)\n    os.environ.pop('APP_NAME')\n\n\ndef _create_dynamodb_table(table_name, temp_dirname):\n    factory = CLIFactory(temp_dirname)\n    session = factory.create_botocore_session()\n    ddb = session.create_client('dynamodb')\n    ddb.create_table(\n        TableName=table_name,\n        AttributeDefinitions=[\n            {\n                'AttributeName': 'entry',\n                'AttributeType': 'N',\n            },\n        ],\n        KeySchema=[\n            {\n                'AttributeName': 'entry',\n                'KeyType': 'HASH',\n            },\n        ],\n        ProvisionedThroughput={\n            'ReadCapacityUnits': 50,\n            'WriteCapacityUnits': 50,\n        },\n    )\n\n\ndef _delete_dynamodb_table(table_name, temp_dirname):\n    factory = CLIFactory(temp_dirname)\n    session = factory.create_botocore_session()\n    ddb = session.create_client('dynamodb')\n    ddb.delete_table(\n        TableName=table_name,\n    )\n\n\nclass Task(threading.Thread):\n    def __init__(self, action, delay=0.05):\n        threading.Thread.__init__(self)\n        self._action = action\n        self._done = threading.Event()\n        self._delay = delay\n\n    def run(self):\n        while not self._done.is_set():\n            self._action()\n            time.sleep(self._delay)\n\n    def stop(self):\n        self._done.set()\n\n\ndef counter():\n    \"\"\"Generator of sequential increasing numbers\"\"\"\n    count = 1\n    while True:\n        yield count\n        count += 1\n\n\nclass CountingMessageSender(object):\n    \"\"\"Class to send values from a counter over a websocket.\"\"\"\n    def __init__(self, ws, counter):\n        self._ws = ws\n        self._counter = counter\n        self._last_sent = None\n\n    def send(self):\n        value = next(self._counter)\n        self._ws.send('%s' % value)\n        self._last_sent = value\n\n    @property\n    def last_sent(self):\n        return self._last_sent\n\n\ndef get_numbers_from_dynamodb(temp_dirname):\n    \"\"\"Get numbers from DynamoDB in the format written by testwebsocketapp.\n    \"\"\"\n    factory = CLIFactory(temp_dirname)\n    session = factory.create_botocore_session()\n    ddb = session.create_client('dynamodb')\n    paginator = ddb.get_paginator('scan')\n    numbers = sorted([\n        int(item['entry']['N'])\n        for page in paginator.paginate(\n                TableName=RANDOM_APP_NAME,\n                ConsistentRead=True,\n        )\n        for item in page['Items']\n    ])\n    return numbers\n\n\ndef get_errors_from_dynamodb(temp_dirname):\n    factory = CLIFactory(temp_dirname)\n    session = factory.create_botocore_session()\n    ddb = session.create_client('dynamodb')\n    item = ddb.get_item(TableName=RANDOM_APP_NAME,\n                        Key={'entry': {'N': '-9999'}})\n    if 'Item' not in item:\n        return None\n    return item['Item']['errormsg']['S']\n\n\ndef find_skips_in_seq(numbers):\n    \"\"\"Find non-sequential gaps in a sequence of numbers\n\n    :type numbers: Iterable of ints\n    :param numbers: Iterable to check for gaps\n\n    :returns: List of tuples with the gaps in the format\n        [(start_of_gap, end_of_gap, ...)]. If the list is empty then there\n        are no gaps.\n    \"\"\"\n    last = numbers[0] - 1\n    skips = []\n    for elem in numbers:\n        if elem != last + 1:\n            skips.append((last, elem))\n        last = elem\n    return skips\n\n\ndef test_websocket_redployment_does_not_lose_messages(smoke_test_app_ws):\n    # This test is to check if one persistant connection is affected by an app\n    # redeployment. A connetion is made to the app, and a sequence of numbers\n    # is sent over the socket and written to a DynamoDB table. The app is\n    # redeployed in a seprate thread. After the redeployment we wait a\n    # second to ensure more numbers have been sent. Finally we inspect the\n    # DynamoDB table to ensure there are no gaps in the numbers we saw on the\n    # server side, and that the first and last number we sent is also present.\n    ws = _create_ws_connection(smoke_test_app_ws.websocket_connect_url)\n    counter_generator = counter()\n    sender = CountingMessageSender(ws, counter_generator)\n    ping_endpoint = Task(sender.send)\n    ping_endpoint.start()\n    smoke_test_app_ws.redeploy_once()\n    time.sleep(1)\n    ping_endpoint.stop()\n\n    errors = get_errors_from_dynamodb(smoke_test_app_ws.app_dir)\n    assert errors is None\n    numbers = get_numbers_from_dynamodb(smoke_test_app_ws.app_dir)\n    assert 1 in numbers\n    assert sender.last_sent in numbers\n    skips = find_skips_in_seq(numbers)\n    assert skips == []\n"
  },
  {
    "path": "tests/aws/testapp/.chalice/config.json",
    "content": "{\n  \"stages\": {\n    \"dev\": {\n      \"api_gateway_stage\": \"api\",\n      \"environment_variables\": {\n        \"APP_NAME\": \"replaceme\"\n      }\n    }\n  },\n  \"version\": \"2.0\",\n  \"app_name\": \"replaceme\"\n}\n"
  },
  {
    "path": "tests/aws/testapp/app-redeploy.py",
    "content": "\"\"\"Test app redeploy.\n\nThis file is copied over to app.py during the integration\ntests to test behavior on redeploys.\n\n\"\"\"\nimport os\n\nfrom chalice import Chalice\n\n\napp = Chalice(app_name=os.environ['APP_NAME'])\n\n\n# Test an unchanged view, this is the exact\n# version from app.py\n@app.route('/')\ndef index():\n    return {'hello': 'world'}\n\n\n# Test same route info but changed view code.\n@app.route('/a/b/c/d/e/f/g')\ndef nested_route():\n    return {'redeployed': True}\n\n\n# Test route deletion.  This view is in the original\n# app.py but is now deleted.\n# @app.route('/path/{name}')\n# def supports_path_params(name):\n#     return {'path': name}\n\n# Test route modification with the same view code.\n# The original version had methods=['GET', 'POST']\n@app.route('/multimethod', methods=['GET', 'PUT'])\ndef multiple_methods():\n    return {'method': app.current_request.method}\n\n\n# Test new view function added that wasn't in the original\n# app.py file.\n@app.route('/redeploy')\ndef redeploy():\n    return {'success': True}\n"
  },
  {
    "path": "tests/aws/testapp/app.py",
    "content": "import os\nimport json\ntry:\n    from urllib.parse import parse_qs\nexcept ImportError:\n    from urlparse import parse_qs\n\n\nimport boto3.session\nfrom chalice import (\n    Chalice, BadRequestError, NotFoundError, Response,\n    CORSConfig, UnauthorizedError, AuthResponse, AuthRoute,\n)\n\n\n# This is a test app that is used by integration tests.\n# This app exercises all the major features of chalice\n# and helps prevent regressions.\n\napp = Chalice(app_name=os.environ['APP_NAME'])\napp.websocket_api.session = boto3.session.Session()\napp.experimental_feature_flags.update([\n    'WEBSOCKETS'\n])\napp.api.binary_types.append('application/binary')\n\n\n@app.authorizer(ttl_seconds=300)\ndef dummy_auth(auth_request):\n    if auth_request.token == 'yes':\n        return AuthResponse(\n            routes=['/builtin-auth',\n                    AuthRoute('/fake-profile', methods=['POST'])],\n            context={'foo': 'bar'},\n            principal_id='foo'\n        )\n    else:\n        raise UnauthorizedError('Authorization failed')\n\n\n@app.route('/')\ndef index():\n    return {'hello': 'world'}\n\n\n@app.route('/a/b/c/d/e/f/g')\ndef nested_route():\n    return {'nested': True}\n\n\n@app.route('/path/{name}')\ndef supports_path_params(name):\n    return {'path': name}\n\n\n@app.route('/singledoc')\ndef single_doc():\n    \"\"\"Single line docstring.\"\"\"\n    return {'docstring': 'single'}\n\n\n@app.route('/multidoc')\ndef multi_doc():\n    \"\"\"Multi-line docstring.\n\n    And here is another line.\n    \"\"\"\n    return {'docstring': 'multi'}\n\n\n@app.route('/post', methods=['POST'])\ndef supports_only_post():\n    return {'success': True}\n\n\n@app.route('/put', methods=['PUT'])\ndef supports_only_put():\n    return {'success': True}\n\n\n@app.route('/jsonpost', methods=['POST'])\ndef supports_post_body_as_json():\n    json_body = app.current_request.json_body\n    return {'json_body': json_body}\n\n\n@app.route('/multimethod', methods=['GET', 'POST'])\ndef multiple_methods():\n    return {'method': app.current_request.method}\n\n\n@app.route('/badrequest')\ndef bad_request_error():\n    raise BadRequestError(\"Bad request.\")\n\n\n@app.route('/notfound')\ndef not_found_error():\n    raise NotFoundError(\"Not found\")\n\n\n@app.route('/arbitrary-error')\ndef raise_arbitrary_error():\n    raise TypeError(\"Uncaught exception\")\n\n\n@app.route('/formencoded', methods=['POST'],\n           content_types=['application/x-www-form-urlencoded'])\ndef form_encoded():\n    parsed = parse_qs(app.current_request.raw_body.decode('utf-8'))\n    return {\n        'parsed': parsed\n    }\n\n\n@app.route('/json-only', content_types=['application/json'])\ndef json_only():\n    return {'success': True}\n\n\n@app.route('/cors', methods=['GET', 'POST', 'PUT'], cors=True)\ndef supports_cors():\n    # It doesn't really matter what we return here because\n    # we'll be checking the response headers to verify CORS support.\n    return {'cors': True}\n\n\n@app.route('/custom_cors', methods=['GET', 'POST', 'PUT'], cors=CORSConfig(\n    allow_origin='https://foo.example.com',\n    allow_headers=['X-Special-Header'],\n    max_age=600,\n    expose_headers=['X-Special-Header'],\n    allow_credentials=True))\ndef supports_custom_cors():\n    return {'cors': True}\n\n\n@app.route('/todict', methods=['GET'])\ndef todict():\n    return app.current_request.to_dict()\n\n\n@app.route('/multifile')\ndef multifile():\n    from chalicelib import MESSAGE\n    return {\"message\": MESSAGE}\n\n\n@app.route('/custom-response', methods=['GET'])\ndef custom_response():\n    return Response(\n        status_code=204,\n        body='',\n        headers={\n            'Content-Type': 'text/plain',\n            'Set-Cookie': ['key=value', 'foo=bar'],\n        },\n    )\n\n\n@app.route('/api-key-required', methods=['GET'], api_key_required=True)\ndef api_key_required():\n    return {\"success\": True}\n\n\n@app.route('/binary', methods=['POST'],\n           content_types=['application/octet-stream'])\ndef binary_round_trip():\n    return Response(\n        app.current_request.raw_body,\n        headers={\n            'Content-Type': 'application/octet-stream'\n        },\n        status_code=200)\n\n\n@app.route('/custom-binary', methods=['POST'],\n           content_types=['application/binary'])\ndef custom_binary_round_trip():\n    return Response(\n        app.current_request.raw_body,\n        headers={\n            'Content-Type': 'application/binary'\n        },\n        status_code=200)\n\n\n@app.route('/get-binary', methods=['GET'])\ndef binary_response():\n    return Response(\n        body=b'\\xDE\\xAD\\xBE\\xEF',\n        headers={\n            'Content-Type': 'application/octet-stream'\n        },\n        status_code=200)\n\n\n@app.route('/shared', methods=['GET'])\ndef shared_get():\n    return {'method': 'GET'}\n\n\n@app.route('/shared', methods=['POST'])\ndef shared_post():\n    return {'method': 'POST'}\n\n\n@app.route('/builtin-auth', authorizer=dummy_auth)\ndef builtin_auth():\n    return {'success': True, 'context': app.current_request.context}\n\n\n# Testing a common use case where you can have read only GET access\n# but you need to be auth'd to POST.\n\n@app.route('/fake-profile', methods=['GET'])\ndef fake_profile_read_only():\n    return {'success': True, 'context': app.current_request.context}\n\n\n@app.route('/fake-profile', authorizer=dummy_auth,\n           methods=['POST'])\ndef fake_profile_post():\n    return {'success': True, 'context': app.current_request.context}\n\n\n@app.route('/repr-raw-body', methods=['POST'])\ndef repr_raw_body():\n    return {'repr-raw-body': app.current_request.raw_body.decode('utf-8')}\n\n\nSOCKET_MESSAGES = []\n\n\n@app.on_ws_connect()\ndef connect(event):\n    pass\n\n\n@app.on_ws_message()\ndef message(event):\n    SOCKET_MESSAGES.append((event.connection_id, event.body))\n    app.websocket_api.send(event.connection_id, json.dumps(SOCKET_MESSAGES))\n\n\n@app.on_ws_disconnect()\ndef disconnect(event):\n    pass\n"
  },
  {
    "path": "tests/aws/testapp/chalicelib/__init__.py",
    "content": "MESSAGE = \"success\"\n"
  },
  {
    "path": "tests/aws/testapp/requirements.txt",
    "content": "boto3==1.38.15\n"
  },
  {
    "path": "tests/aws/testwebsocketapp/.chalice/config.json",
    "content": "{\n  \"version\": \"2.0\",\n  \"app_name\": \"testwebsocketapp\",\n  \"stages\": {\n    \"dev\": {\n\t\"api_gateway_stage\": \"api\",\n\t\"environment_variables\": {}\n    }\n  }\n}\n"
  },
  {
    "path": "tests/aws/testwebsocketapp/.gitignore",
    "content": ".chalice/deployments/\n.chalice/venv/\n"
  },
  {
    "path": "tests/aws/testwebsocketapp/app-redeploy.py",
    "content": "import os\n\nimport boto3\nfrom chalice import Chalice\n\napp = Chalice(app_name=os.environ['APP_NAME'])\napp.websocket_api.session = boto3.session.Session()\napp.experimental_feature_flags.update([\n    'WEBSOCKETS'\n])\nddb = boto3.client('dynamodb')\n\n\n# This comment is to cause a change which triggers a redeployment\n# of the Lambda Function, this is needed to properly test redeployment.\n@app.on_ws_message()\ndef message(event):\n    ddb.put_item(\n        TableName=os.environ['APP_NAME'],\n        Item={\n            'entry': {\n                'N': event.body\n            },\n        },\n    )\n"
  },
  {
    "path": "tests/aws/testwebsocketapp/app.py",
    "content": "import os\n\nimport boto3\nfrom chalice import Chalice\n\napp = Chalice(app_name=os.environ['APP_NAME'])\napp.websocket_api.session = boto3.session.Session()\napp.experimental_feature_flags.update([\n    'WEBSOCKETS'\n])\nddb = boto3.client('dynamodb')\n\n\n@app.on_ws_message()\ndef message(event):\n    try:\n        ddb.put_item(\n            TableName=os.environ['APP_NAME'],\n            Item={\n                'entry': {\n                    'N': event.body\n                },\n            },\n        )\n    except Exception as e:\n        # If we get an exception, we need to log it somehow.  We can't\n        # return this back to the user so we'll add something to the ddb\n        # table to denote that we failed.\n        ddb.put_item(\n            TableName=os.environ['APP_NAME'],\n            Item={\n                'entry': {\n                    'N': \"-9999\"\n                },\n                'errormsg': {\n                    'S': '%s: %s,\\noriginal event: %s' % (\n                        e.__class__, e, event.to_dict())\n                }\n            }\n        )\n"
  },
  {
    "path": "tests/aws/testwebsocketapp/requirements.txt",
    "content": "boto3==1.38.15\n"
  },
  {
    "path": "tests/conftest.py",
    "content": "import sys\n\nimport botocore.session\nfrom botocore.stub import Stubber\nimport pytest\nfrom pytest import fixture\n\n\ndef pytest_addoption(parser):\n    parser.addoption('--skip-slow', action='store_true',\n                     help='Skip slow tests')\n\n\ndef pytest_configure(config):\n    config.addinivalue_line(\"markers\", \"slow: mark test as slow to run\")\n    config.addinivalue_line(\n        \"markers\", (\n            \"on_redeploy: mark an integration test to be run after \"\n            \"the app is redeployed\"\n        )\n    )\n\n\ndef pytest_collection_modifyitems(config, items):\n    if config.getoption(\"--skip-slow\"):\n        skip_slow = pytest.mark.skip(reason=\"Skipping due to --skip-slow\")\n        for item in items:\n            if \"slow\" in item.keywords:\n                item.add_marker(skip_slow)\n\n\n@fixture(autouse=True)\ndef teardown_function():\n    sys.modules.pop('app', None)\n    sys.path_importer_cache.clear()\n\n\nclass StubbedSession(botocore.session.Session):\n    def __init__(self, *args, **kwargs):\n        super(StubbedSession, self).__init__(*args, **kwargs)\n        self._cached_clients = {}\n        self._client_stubs = {}\n\n    def create_client(self, service_name, *args, **kwargs):\n        if service_name not in self._cached_clients:\n            client = self._create_stubbed_client(service_name, *args, **kwargs)\n            self._cached_clients[service_name] = client\n        return self._cached_clients[service_name]\n\n    def _create_stubbed_client(self, service_name, *args, **kwargs):\n        client = super(StubbedSession, self).create_client(\n            service_name, *args, **kwargs)\n        stubber = StubBuilder(ChaliceStubber(client))\n        self._client_stubs[service_name] = stubber\n        return client\n\n    def stub(self, service_name):\n        if service_name not in self._client_stubs:\n            self.create_client(service_name)\n        return self._client_stubs[service_name]\n\n    def activate_stubs(self):\n        for stub in self._client_stubs.values():\n            stub.activate()\n\n    def verify_stubs(self):\n        for stub in self._client_stubs.values():\n            stub.assert_no_pending_responses()\n\n\nclass StubBuilder(object):\n    def __init__(self, stub):\n        self.stub = stub\n        self.activated = False\n        self.pending_args = {}\n\n    def __getattr__(self, name):\n        if self.activated:\n            # I want to be strict here to guide common test behavior.\n            # This helps encourage the \"record\" \"replay\" \"verify\"\n            # idiom in traditional mock frameworks.\n            raise RuntimeError(\"Stub has already been activated: %s, \"\n                               \"you must set up your stub calls before \"\n                               \"calling .activate()\" % self.stub)\n        if not name.startswith('_'):\n            # Assume it's an API call.\n            self.pending_args['operation_name'] = name\n            return self\n\n    def assert_no_pending_responses(self):\n        self.stub.assert_no_pending_responses()\n\n    def activate(self):\n        self.activated = True\n        self.stub.activate()\n\n    def returns(self, response):\n        self.pending_args['service_response'] = response\n        # returns() is essentially our \"build()\" method and triggers\n        # creations of a stub response creation.\n        p = self.pending_args\n        self.stub.add_response(p['operation_name'],\n                               expected_params=p['expected_params'],\n                               service_response=p['service_response'])\n        # And reset the pending_args for the next stub creation.\n        self.pending_args = {}\n\n    def raises_error(self, error_code=None, message=None, error=None):\n        p = self.pending_args\n        if error_code is not None and message is not None:\n            self.stub.add_client_error(p['operation_name'],\n                                       service_error_code=error_code,\n                                       service_message=message)\n        elif error is not None:\n            self.stub.add_response_error(p['operation_name'],\n                                         error)\n        else:\n            raise ValueError(\n                'Either error_code and message must be provided or '\n                'error must be provided'\n            )\n        # Reset pending args for next expectation.\n        self.pending_args = {}\n\n    def __call__(self, **kwargs):\n        self.pending_args['expected_params'] = kwargs\n        return self\n\n\n# TODO: Port this functionality to inject non-ClientErrors back to botocore\nclass ChaliceStubber(Stubber):\n    def add_response_error(self, method, error, expected_params=None):\n        \"\"\"Adds a custom exception to the response queue\n\n        :type method: str\n        :param method: The name of the service method to raise the error\n            on.\n\n        :type error: Exception\n        :param error: The customer exception to raise\n\n        :type expected_params: dict\n        :param expected_params: A dictionary of the expected parameters to\n            be called for the provided service response. The parameters match\n            the names of keyword arguments passed to that client call. If\n            any of the parameters differ a ``StubResponseError`` is thrown.\n            You can use stub.ANY to indicate a particular parameter to ignore\n            in validation. stub.ANY is only valid for top level params.\n        \"\"\"\n        operation_name = self.client.meta.method_to_api_mapping.get(method)\n        response = {\n            'operation_name': operation_name,\n            'response': error,\n            'expected_params': expected_params,\n        }\n        self._queue.append(response)\n\n    def _get_response_handler(self, model, params, **kwargs):\n        response = super(ChaliceStubber, self)._get_response_handler(\n            model, params, **kwargs)\n        if isinstance(response, Exception):\n            raise response\n        return response\n\n\n@fixture\ndef stubbed_session():\n    s = StubbedSession()\n    return s\n\n\n@fixture\ndef no_local_config(monkeypatch):\n    \"\"\"Ensure no local AWS configuration is used.\n\n    This is useful for unit/functional tests so we\n    can ensure that local configuration does not affect\n    the results of the test.\n\n    \"\"\"\n    monkeypatch.setenv('AWS_DEFAULT_REGION', 'us-west-2')\n    monkeypatch.setenv('AWS_ACCESS_KEY_ID', 'foo')\n    monkeypatch.setenv('AWS_SECRET_ACCESS_KEY', 'bar')\n    monkeypatch.delenv('AWS_PROFILE', raising=False)\n    monkeypatch.delenv('AWS_DEFAULT_PROFILE', raising=False)\n    # Ensure that the existing ~/.aws/{config,credentials} file\n    # don't influence test results.\n    monkeypatch.setenv('AWS_CONFIG_FILE', '/tmp/asdfasdfaf/does/not/exist')\n    monkeypatch.setenv('AWS_SHARED_CREDENTIALS_FILE',\n                       '/tmp/asdfasdfaf/does/not/exist2')\n"
  },
  {
    "path": "tests/functional/__init__.py",
    "content": ""
  },
  {
    "path": "tests/functional/api/__init__.py",
    "content": ""
  },
  {
    "path": "tests/functional/api/test_package.py",
    "content": "import os\nimport json\n\nimport pytest\nfrom click.testing import CliRunner\n\nfrom chalice.cli import newproj\nfrom chalice.api import package_app\n\n\n@pytest.fixture\ndef runner():\n    return CliRunner()\n\n\n@pytest.mark.parametrize('package_format,template_format,expected_filename', [\n    ('cloudformation', 'json', 'sam.json'),\n    ('cloudformation', 'yaml', 'sam.yaml'),\n    ('terraform', 'json', 'chalice.tf.json'),\n])\ndef test_can_package_different_formats(runner, package_format,\n                                       template_format,\n                                       expected_filename):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        package_app('testproject', output_dir='packagedir', stage='dev',\n                    package_format=package_format,\n                    template_format=template_format)\n        app_contents = os.listdir('packagedir')\n        assert expected_filename in app_contents\n        assert 'deployment.zip' in app_contents\n\n\ndef test_can_override_chalice_config(runner):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        chalice_config = {\n            'environment_variables': {\n                'FOO': 'BAR',\n            }\n        }\n        package_app('testproject', output_dir='packagedir', stage='dev',\n                    chalice_config=chalice_config)\n        app_contents = os.listdir('packagedir')\n        assert 'sam.json' in app_contents\n        with open(os.path.join('packagedir', 'sam.json')) as f:\n            data = json.loads(f.read())\n        properties = data['Resources']['APIHandler']['Properties']\n        assert properties['Environment'] == {\n            'Variables': {\n                'FOO': 'BAR',\n            }\n        }\n"
  },
  {
    "path": "tests/functional/basicapp/.chalice/config.json",
    "content": "{\n  \"version\": \"2.0\",\n  \"app_name\": \"basicapp\",\n  \"stages\": {\n    \"dev\": {\n      \"api_gateway_stage\": \"api\"\n    }\n  }\n}\n"
  },
  {
    "path": "tests/functional/basicapp/.gitignore",
    "content": ".chalice/deployments/\n.chalice/venv/\n"
  },
  {
    "path": "tests/functional/basicapp/app.py",
    "content": "from chalice import Chalice\n\napp = Chalice(app_name='basicapp')\n\n\n@app.route('/')\ndef index():\n    return {'version': 'original'}\n"
  },
  {
    "path": "tests/functional/basicapp/requirements.txt",
    "content": ""
  },
  {
    "path": "tests/functional/cdk/__init__.py",
    "content": ""
  },
  {
    "path": "tests/functional/cdk/test_construct.py",
    "content": "import os\nimport sys\nimport json\n\nimport pytest\nfrom click.testing import CliRunner\n\nfrom chalice.cli import newproj\n\n\ntry:\n    from aws_cdk import core as cdk\n    CDK_VERSION = 1\nexcept ImportError:\n    try:\n        import aws_cdk as cdk\n        CDK_VERSION = 2\n    except ImportError:\n        pytestmark = pytest.mark.skip(\n            \"aws_cdk package needed to run CDK tests.\")\n\n\n@pytest.fixture\ndef runner():\n    return CliRunner()\n\n\ndef verify_code_asset_exists_v1(uri_props):\n    bucket_ref = uri_props['Bucket']['Ref']\n    assert bucket_ref.startswith('AssetParameters')\n\n\ndef verify_code_asset_exists_v2(uri_props):\n    bucket_ref = uri_props['Bucket']['Fn::Sub']\n    # Actual sub value will look something like this:\n    # 'cdk-abcdefghi-assets-${AWS::AccountId}-${AWS::Region}'},\n    assert bucket_ref.startswith('cdk-')\n\n\n@pytest.fixture\ndef verify_code_asset_exists():\n    if CDK_VERSION == 1:\n        return verify_code_asset_exists_v1\n    elif CDK_VERSION == 2:\n        return verify_code_asset_exists_v2\n\n\ndef load_chalice_construct(dirname, stack_name='testcdk'):\n    try:\n        sys.path.append(dirname)\n        sys.modules.pop('stacks.chaliceapp', None)\n        sys.modules.pop('stacks', None)\n        import stacks.chaliceapp\n        app = cdk.App()\n        chalice_app = stacks.chaliceapp.ChaliceApp(app, stack_name)\n        return app, chalice_app.chalice\n    finally:\n        sys.modules.pop('app', None)\n        sys.modules.pop('stacks', None)\n        sys.path.pop()\n        sys.path_importer_cache.clear()\n\n\ndef filter_resources(template, resource_type):\n    return [(name, props) for name, props in template['Resources'].items()\n            if props['Type'] == resource_type]\n\n\ndef test_cdk_construct_api(runner):\n    # The CDK loading/synth can take a while so we're testing the\n    # various APIs out in one test to cut down on test time.\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton(\n            'testcdk', project_type='cdk-ddb')\n        dirname = os.path.abspath(os.path.join('testcdk', 'infrastructure'))\n        os.chdir(dirname)\n        cdk_app, chalice_app = load_chalice_construct(dirname, 'testcdk')\n        api_handler = chalice_app.get_function('APIHandler')\n        assert api_handler == chalice_app.get_function('APIHandler')\n        role = chalice_app.get_role('DefaultRole')\n        assert hasattr(role, 'role_name')\n\n\ndef test_can_package_as_cdk_app(runner, verify_code_asset_exists):\n    # The CDK loading/synth can take a while so we're testing the\n    # various APIs out in one test to cut down on test time.\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton(\n            'testcdkpackage', project_type='cdk-ddb')\n        dirname = os.path.abspath(os.path.join('testcdkpackage',\n                                               'infrastructure'))\n        os.chdir(dirname)\n        cdk_app, chalice_app = load_chalice_construct(\n            dirname, 'testcdkpackage')\n        assembly = cdk_app.synth()\n        stack = assembly.get_stack_by_name('testcdkpackage')\n        cfn_template = stack.template\n        resources = cfn_template['Resources']\n        # Sanity check that we have the resources from Chalice as well as the\n        # resources from the ChaliceApp construct.\n        assert 'APIHandler' in resources\n        assert 'DefaultRole' in resources\n        ddb_tables = filter_resources(cfn_template, 'AWS::DynamoDB::Table')[0]\n        # CDK adds a random suffix to our resource name so we verify that\n        # the name starts with our provided name \"AppTable\".\n        assert ddb_tables[0].startswith('AppTable')\n        # We also need to verify that we've replaces the CodUri with the\n        # CDK specific assets.\n        functions = filter_resources(\n            cfn_template, 'AWS::Serverless::Function')[0]\n        verify_code_asset_exists(functions[1]['Properties']['CodeUri'])\n\n\ndef test_can_package_managed_layer(runner, verify_code_asset_exists):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton(\n            'testcdklayers', project_type='cdk-ddb')\n        project_dir = os.path.abspath('testcdklayers')\n        config_file = os.path.join(project_dir, 'runtime',\n                                   '.chalice', 'config.json')\n        with open(config_file) as f:\n            config = json.load(f)\n            config['automatic_layer'] = True\n        with open(config_file, 'w') as f:\n            f.write(json.dumps(config))\n        infrastructure_dir = os.path.join(project_dir, 'infrastructure')\n        os.chdir(infrastructure_dir)\n        cdk_app, chalice_app = load_chalice_construct(infrastructure_dir,\n                                                      'testcdklayers')\n        assembly = cdk_app.synth()\n        stack = assembly.get_stack_by_name('testcdklayers')\n        cfn_template = stack.template\n        layers = filter_resources(\n            cfn_template, 'AWS::Serverless::LayerVersion')[0]\n        verify_code_asset_exists(layers[1]['Properties']['ContentUri'])\n"
  },
  {
    "path": "tests/functional/cli/__init__.py",
    "content": ""
  },
  {
    "path": "tests/functional/cli/test_cli.py",
    "content": "import json\nimport zipfile\nimport os\nimport sys\nimport re\nfrom unittest import mock\n\nimport pytest\nfrom click.testing import CliRunner\nfrom botocore.exceptions import ClientError\n\nfrom chalice import cli\nfrom chalice.cli import factory\nfrom chalice.cli import newproj\nfrom chalice.config import Config, DeployedResources\nfrom chalice.utils import record_deployed_values\nfrom chalice.utils import PipeReader\nfrom chalice.constants import DEFAULT_APIGATEWAY_STAGE_NAME\nfrom chalice.logs import LogRetriever, LogRetrieveOptions\nfrom chalice.invoke import LambdaInvokeHandler\nfrom chalice.invoke import UnhandledLambdaError\nfrom chalice.awsclient import ReadTimeout\nfrom chalice.deploy.validate import ExperimentalFeatureError\n\n\nclass FakeConfig(object):\n    def __init__(self, deployed_resources):\n        self._deployed_resources = deployed_resources\n\n    def deployed_resources(self, chalice_stage_name):\n        return self._deployed_resources\n\n\n@pytest.fixture\ndef runner():\n    return CliRunner()\n\n\n@pytest.fixture\ndef mock_cli_factory():\n    cli_factory = mock.Mock(spec=factory.CLIFactory)\n    cli_factory.create_config_obj.return_value = Config.create(project_dir='.')\n    cli_factory.create_botocore_session.return_value = mock.sentinel.Session\n    return cli_factory\n\n\ndef teardown_function(function):\n    sys.modules.pop('app', None)\n    sys.path_importer_cache.clear()\n\n\ndef assert_chalice_app_structure_created(dirname):\n    app_contents = os.listdir(os.path.join(os.getcwd(), dirname))\n    assert 'app.py' in app_contents\n    assert 'requirements.txt' in app_contents\n    assert '.chalice' in app_contents\n    assert '.gitignore' in app_contents\n\n\ndef _run_cli_command(runner, function, args, cli_factory=None):\n    # Handles passing in 'obj' so we can get commands\n    # that use @pass_context to work properly.\n    # click doesn't support this natively so we have to duplicate\n    # what 'def cli(...)' is doing.\n    if cli_factory is None:\n        cli_factory = factory.CLIFactory('.')\n    result = runner.invoke(\n        function, args, obj={'project_dir': '.', 'debug': False,\n                             'factory': cli_factory})\n    return result\n\n\ndef test_create_new_project_creates_app(runner):\n    with runner.isolated_filesystem():\n        result = runner.invoke(cli.new_project, ['testproject'], obj={})\n        assert result.exit_code == 0\n\n        # The 'new-project' command creates a directory based on\n        # the project name\n        assert os.listdir(os.getcwd()) == ['testproject']\n        assert_chalice_app_structure_created(dirname='testproject')\n\n\ndef test_create_project_with_prompted_app_name(runner):\n    with runner.isolated_filesystem():\n        result = runner.invoke(\n            cli.new_project, input=b'', obj={\n                'prompter': lambda: {'project_name': 'testproject',\n                                     'project_type': 'legacy'}\n            }\n        )\n        print(result.stdout)\n        assert result.exit_code == 0\n        assert os.listdir(os.getcwd()) == ['testproject']\n        assert_chalice_app_structure_created(dirname='testproject')\n\n\ndef test_error_raised_if_dir_already_exists(runner):\n    with runner.isolated_filesystem():\n        os.mkdir('testproject')\n        result = runner.invoke(cli.new_project, ['testproject'], obj={})\n        assert result.exit_code == 1\n        assert 'Directory already exists: testproject' in result.output\n\n\ndef test_can_load_project_config_after_project_creation(runner):\n    with runner.isolated_filesystem():\n        result = runner.invoke(cli.new_project, ['testproject'], obj={})\n        assert result.exit_code == 0\n        config = factory.CLIFactory('testproject').load_project_config()\n        assert config == {\n            'version': '2.0',\n            'app_name': 'testproject',\n            'stages': {\n                'dev': {'api_gateway_stage': DEFAULT_APIGATEWAY_STAGE_NAME},\n            }\n        }\n\n\ndef test_default_new_project_adds_index_route(runner):\n    with runner.isolated_filesystem():\n        result = runner.invoke(cli.new_project, ['testproject'], obj={})\n        assert result.exit_code == 0\n        app = factory.CLIFactory('testproject').load_chalice_app()\n        assert '/' in app.routes\n\n\ndef test_gen_policy_command_creates_policy(runner):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = runner.invoke(cli.cli, ['gen-policy'], obj={})\n        assert result.exit_code == 0\n        # The output should be valid JSON.\n        parsed_policy = json.loads(result.output)\n        # We don't want to validate the specific parts of the policy\n        # (that's tested elsewhere), but we'll check to make sure\n        # it looks like a policy document.\n        assert 'Version' in parsed_policy\n        assert 'Statement' in parsed_policy\n\n\ndef test_does_fail_to_generate_swagger_if_no_rest_api(runner):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        with open('app.py', 'w') as f:\n            f.write(\n                'from chalice import Chalice\\n'\n                'app = Chalice(\"myapp\")\\n'\n            )\n        result = _run_cli_command(runner, cli.generate_models, [])\n        assert result.exit_code == 1\n        assert result.output == (\n            'No REST API found to generate model from.\\n'\n            'Aborted!\\n'\n        )\n\n\ndef test_can_write_swagger_model(runner):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = _run_cli_command(runner, cli.generate_models, [])\n        assert result.exit_code == 0\n        model = json.loads(result.output)\n        assert model == {\n            \"swagger\": \"2.0\",\n            \"info\": {\n                \"version\": \"1.0\",\n                \"title\": \"testproject\"\n            },\n            \"schemes\": [\n                \"https\"\n            ],\n            \"paths\": {\n                \"/\": {\n                    \"get\": {\n                        \"consumes\": [\n                            \"application/json\"\n                        ],\n                        \"produces\": [\n                            \"application/json\"\n                        ],\n                        \"responses\": {\n                            \"200\": {\n                                \"description\": \"200 response\",\n                                \"schema\": {\n                                    \"$ref\": \"#/definitions/Empty\"\n                                }\n                            }\n                        },\n                        \"x-amazon-apigateway-integration\": {\n                            \"responses\": {\n                                \"default\": {\n                                    \"statusCode\": \"200\"\n                                }\n                            },\n                            \"uri\": (\n                                \"arn:{partition}:apigateway:{region_name}\"\n                                \":lambda:path/2015-03-31/functions/\"\n                                \"{api_handler_lambda_arn}/invocations\"\n                            ),\n                            \"passthroughBehavior\": \"when_no_match\",\n                            \"httpMethod\": \"POST\",\n                            \"contentHandling\": \"CONVERT_TO_TEXT\",\n                            \"type\": \"aws_proxy\"\n                        }\n                    }\n                }\n            },\n            \"definitions\": {\n                \"Empty\": {\n                    \"type\": \"object\",\n                    \"title\": \"Empty Schema\"\n                }\n            },\n            \"x-amazon-apigateway-binary-media-types\": [\n                \"application/octet-stream\",\n                \"application/x-tar\",\n                \"application/zip\",\n                \"audio/basic\",\n                \"audio/ogg\",\n                \"audio/mp4\",\n                \"audio/mpeg\",\n                \"audio/wav\",\n                \"audio/webm\",\n                \"image/png\",\n                \"image/jpg\",\n                \"image/jpeg\",\n                \"image/gif\",\n                \"video/ogg\",\n                \"video/mpeg\",\n                \"video/webm\"\n            ]\n        }\n\n\ndef test_can_package_command(runner, mock_cli_factory):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = _run_cli_command(runner, cli.package, ['outdir'])\n        assert result.exit_code == 0, result.output\n        assert os.path.isdir('outdir')\n        dir_contents = os.listdir('outdir')\n        assert 'sam.json' in dir_contents\n        assert 'deployment.zip' in dir_contents\n\n\ndef test_can_package_with_yaml_command(runner):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = _run_cli_command(runner, cli.package,\n                                  ['--template-format', 'yaml', 'outdir'])\n        assert result.exit_code == 0, result.output\n        assert os.path.isdir('outdir')\n        dir_contents = os.listdir('outdir')\n        assert 'sam.yaml' in dir_contents\n        assert 'deployment.zip' in dir_contents\n\n\ndef test_case_insensitive_template_format(runner):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = _run_cli_command(runner, cli.package,\n                                  ['--template-format', 'YAML', 'outdir'])\n        assert result.exit_code == 0, result.output\n        assert os.path.isdir('outdir')\n        assert 'sam.yaml' in os.listdir('outdir')\n\n\ndef test_can_package_with_single_file(runner):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = _run_cli_command(\n            runner, cli.package, ['--single-file', 'package.zip'])\n        assert result.exit_code == 0, result.output\n        assert os.path.isfile('package.zip')\n        with zipfile.ZipFile('package.zip', 'r') as f:\n            assert sorted(f.namelist()) == [\n                'deployment.zip', 'sam.json']\n\n\ndef test_package_terraform_err_with_single_file_or_merge(runner):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = _run_cli_command(\n            runner, cli.package, ['--pkg-format', 'terraform',\n                                  '--single-file', 'module'])\n        assert result.exit_code == 1, result.output\n        assert \"Terraform format does not support\" in result.output\n\n        result = _run_cli_command(\n            runner, cli.package, ['--pkg-format', 'terraform',\n                                  '--merge-template', 'foo.json', 'module'])\n        assert result.exit_code == 1, result.output\n        assert \"Terraform format does not support\" in result.output\n\n\ndef test_debug_flag_enables_logging(runner):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = runner.invoke(\n            cli.cli, ['--debug', 'package', 'outdir'], obj={})\n        assert result.exit_code == 0\n        assert re.search('[DEBUG].*Creating deployment package',\n                         result.output) is not None\n\n\ndef test_does_deploy_with_default_api_gateway_stage_name(runner,\n                                                         mock_cli_factory):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        # This isn't perfect as we're assuming we know how to\n        # create the config_obj like the deploy() command does,\n        # it should give us more confidence that the api gateway\n        # stage defaults are still working.\n        cli_factory = factory.CLIFactory('.')\n        config = cli_factory.create_config_obj(\n            chalice_stage_name='dev',\n            autogen_policy=None,\n            api_gateway_stage=None\n        )\n        assert config.api_gateway_stage == DEFAULT_APIGATEWAY_STAGE_NAME\n\n\ndef test_can_specify_api_gateway_stage(runner, mock_cli_factory):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = _run_cli_command(runner, cli.deploy,\n                                  ['--api-gateway-stage', 'notdev'],\n                                  cli_factory=mock_cli_factory)\n        assert result.exit_code == 0\n        mock_cli_factory.create_config_obj.assert_called_with(\n            autogen_policy=None, chalice_stage_name='dev',\n            api_gateway_stage='notdev'\n        )\n\n\ndef test_can_deploy_specify_connection_timeout(runner, mock_cli_factory):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = _run_cli_command(runner, cli.deploy,\n                                  ['--connection-timeout', 100],\n                                  cli_factory=mock_cli_factory)\n        assert result.exit_code == 0\n        mock_cli_factory.create_botocore_session.assert_called_with(\n            connection_timeout=100\n        )\n\n\ndef test_can_retrieve_url(runner, mock_cli_factory):\n    deployed_values_dev = {\n        \"schema_version\": \"2.0\",\n        \"resources\": [\n            {\"rest_api_url\": \"https://dev-url/\",\n             \"name\": \"rest_api\",\n             \"resource_type\": \"rest_api\"},\n        ]\n    }\n    deployed_values_prod = {\n        \"schema_version\": \"2.0\",\n        \"resources\": [\n            {\"rest_api_url\": \"https://prod-url/\",\n             \"name\": \"rest_api\",\n             \"resource_type\": \"rest_api\"},\n        ]\n    }\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        deployed_dir = os.path.join('.chalice', 'deployed')\n        os.makedirs(deployed_dir)\n        record_deployed_values(\n            deployed_values_dev,\n            os.path.join(deployed_dir, 'dev.json')\n        )\n        record_deployed_values(\n            deployed_values_prod,\n            os.path.join(deployed_dir, 'prod.json')\n        )\n        result = _run_cli_command(runner, cli.url, [],\n                                  cli_factory=mock_cli_factory)\n        assert result.exit_code == 0\n        assert result.output == 'https://dev-url/\\n'\n\n        prod_result = _run_cli_command(runner, cli.url, ['--stage', 'prod'],\n                                       cli_factory=mock_cli_factory)\n        assert prod_result.exit_code == 0\n        assert prod_result.output == 'https://prod-url/\\n'\n\n\ndef test_error_when_no_deployed_record(runner, mock_cli_factory):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = _run_cli_command(runner, cli.url, [],\n                                  cli_factory=mock_cli_factory)\n        assert result.exit_code == 2\n        assert 'not find' in result.output\n\n\n@pytest.mark.skipif(\n    (3, 9) <= sys.version_info[:2] <= (3, 13),\n    reason=(\"Cannot generate pipeline for python3.9 - python3.13\"),\n)\ndef test_can_generate_pipeline_for_all(runner):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = _run_cli_command(\n            runner, cli.generate_pipeline, ['pipeline.json'])\n        assert result.exit_code == 0, result.output\n        assert os.path.isfile('pipeline.json')\n        with open('pipeline.json', 'r') as f:\n            template = json.load(f)\n            # The actual contents are tested in the unit\n            # tests.  Just a sanity check that it looks right.\n            assert \"AWSTemplateFormatVersion\" in template\n            assert \"Outputs\" in template\n\n\ndef test_no_errors_if_override_codebuild_image(runner):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = _run_cli_command(\n            runner, cli.generate_pipeline,\n            ['-i', 'python:3.6.1', 'pipeline.json'])\n        assert result.exit_code == 0, result.output\n        assert os.path.isfile('pipeline.json')\n        with open('pipeline.json', 'r') as f:\n            template = json.load(f)\n            # The actual contents are tested in the unit\n            # tests.  Just a sanity check that it looks right.\n            image = template['Parameters']['CodeBuildImage']['Default']\n            assert image == 'python:3.6.1'\n\n\ndef test_can_configure_github(runner):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        # The -i option is provided so we don't have to skip this\n        # test on python3.6\n        result = _run_cli_command(\n            runner, cli.generate_pipeline,\n            ['--source', 'github', '-i' 'python:3.6.1', 'pipeline.json'])\n        assert result.exit_code == 0, result.output\n        assert os.path.isfile('pipeline.json')\n        with open('pipeline.json', 'r') as f:\n            template = json.load(f)\n            # The template is already tested in the unit tests\n            # for template generation.  We just want a basic\n            # sanity check to make sure things are mapped\n            # properly.\n            assert 'GithubOwner' in template['Parameters']\n            assert 'GithubRepoName' in template['Parameters']\n\n\ndef test_can_extract_buildspec_yaml(runner):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = _run_cli_command(\n            runner, cli.generate_pipeline,\n            ['--buildspec-file', 'buildspec.yml',\n             '-i', 'python:3.6.1',\n             'pipeline.json'])\n        assert result.exit_code == 0, result.output\n        assert os.path.isfile('buildspec.yml')\n        with open('buildspec.yml') as f:\n            data = f.read()\n            # The contents of this file are tested elsewhere,\n            # we just want a basic sanity check here.\n            assert 'chalice package' in data\n\n\ndef test_can_specify_profile_for_logs(runner, mock_cli_factory):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = _run_cli_command(\n            runner, cli.logs, ['--profile', 'my-profile'],\n            cli_factory=mock_cli_factory\n        )\n        assert result.exit_code == 0\n        assert mock_cli_factory.profile == 'my-profile'\n\n\ndef test_can_provide_lambda_name_for_logs(runner, mock_cli_factory):\n    deployed_resources = DeployedResources({\n        \"resources\": [\n            {\"name\": \"foo\",\n             \"lambda_arn\": \"arn:aws:lambda::app-dev-foo\",\n             \"resource_type\": \"lambda_function\"}]\n    })\n    mock_cli_factory.create_config_obj.return_value = FakeConfig(\n        deployed_resources)\n    log_retriever = mock.Mock(spec=LogRetriever)\n    log_retriever.retrieve_logs.return_value = []\n    mock_cli_factory.create_log_retriever.return_value = log_retriever\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = _run_cli_command(\n            runner, cli.logs, ['--name', 'foo'],\n            cli_factory=mock_cli_factory\n        )\n        assert result.exit_code == 0\n    log_retriever.retrieve_logs.assert_called_with(\n        LogRetrieveOptions(\n            include_lambda_messages=False, max_entries=None)\n    )\n    mock_cli_factory.create_log_retriever.assert_called_with(\n        mock.sentinel.Session, 'arn:aws:lambda::app-dev-foo', False\n    )\n\n\ndef test_can_follow_logs_with_option(runner, mock_cli_factory):\n    deployed_resources = DeployedResources({\n        \"resources\": [\n            {\"name\": \"foo\",\n             \"lambda_arn\": \"arn:aws:lambda::app-dev-foo\",\n             \"resource_type\": \"lambda_function\"}]\n    })\n    mock_cli_factory.create_config_obj.return_value = FakeConfig(\n        deployed_resources)\n    log_retriever = mock.Mock(spec=LogRetriever)\n    log_retriever.retrieve_logs.return_value = []\n    mock_cli_factory.create_log_retriever.return_value = log_retriever\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = _run_cli_command(\n            runner, cli.logs, ['--name', 'foo', '--follow'],\n            cli_factory=mock_cli_factory\n        )\n        assert result.exit_code == 0\n    log_retriever.retrieve_logs.assert_called_with(\n        LogRetrieveOptions(\n            include_lambda_messages=False, max_entries=None)\n    )\n    mock_cli_factory.create_log_retriever.assert_called_with(\n        mock.sentinel.Session, 'arn:aws:lambda::app-dev-foo', True\n    )\n\n\ndef test_can_call_invoke(runner, mock_cli_factory, monkeypatch):\n    invoke_handler = mock.Mock(spec=LambdaInvokeHandler)\n    mock_cli_factory.create_lambda_invoke_handler.return_value = invoke_handler\n    mock_reader = mock.Mock(spec=PipeReader)\n    mock_reader.read.return_value = 'barbaz'\n    mock_cli_factory.create_stdin_reader.return_value = mock_reader\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = _run_cli_command(runner, cli.invoke, ['-n', 'foo'],\n                                  cli_factory=mock_cli_factory)\n        assert result.exit_code == 0\n    assert invoke_handler.invoke.call_args == mock.call('barbaz')\n\n\ndef test_invoke_does_raise_if_service_error(runner, mock_cli_factory):\n    deployed_resources = DeployedResources({\"resources\": []})\n    mock_cli_factory.create_config_obj.return_value = FakeConfig(\n        deployed_resources)\n    invoke_handler = mock.Mock(spec=LambdaInvokeHandler)\n    invoke_handler.invoke.side_effect = ClientError(\n        {\n            'Error': {\n                'Code': 'LambdaError',\n                'Message': 'Error message'\n            }\n        },\n        'Invoke'\n    )\n    mock_cli_factory.create_lambda_invoke_handler.return_value = invoke_handler\n    mock_reader = mock.Mock(spec=PipeReader)\n    mock_reader.read.return_value = 'barbaz'\n    mock_cli_factory.create_stdin_reader.return_value = mock_reader\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = _run_cli_command(runner, cli.invoke, ['-n', 'foo'],\n                                  cli_factory=mock_cli_factory)\n        assert result.exit_code == 1\n    assert invoke_handler.invoke.call_args == mock.call('barbaz')\n    assert (\n        \"Error: got 'LambdaError' exception back from Lambda\\n\"\n        \"Error message\"\n    ) in result.output\n\n\ndef test_invoke_does_raise_if_unhandled_error(runner, mock_cli_factory):\n    deployed_resources = DeployedResources({\"resources\": []})\n    mock_cli_factory.create_config_obj.return_value = FakeConfig(\n        deployed_resources)\n    invoke_handler = mock.Mock(spec=LambdaInvokeHandler)\n    invoke_handler.invoke.side_effect = UnhandledLambdaError('foo')\n    mock_cli_factory.create_lambda_invoke_handler.return_value = invoke_handler\n    mock_reader = mock.Mock(spec=PipeReader)\n    mock_reader.read.return_value = 'barbaz'\n    mock_cli_factory.create_stdin_reader.return_value = mock_reader\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = _run_cli_command(runner, cli.invoke, ['-n', 'foo'],\n                                  cli_factory=mock_cli_factory)\n        assert result.exit_code == 1\n    assert invoke_handler.invoke.call_args == mock.call('barbaz')\n    assert 'Unhandled exception in Lambda function, details above.' \\\n        in result.output\n\n\ndef test_invoke_does_raise_if_read_timeout(runner, mock_cli_factory):\n    mock_cli_factory.create_lambda_invoke_handler.side_effect = \\\n        ReadTimeout('It took too long')\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = _run_cli_command(runner, cli.invoke, ['-n', 'foo'],\n                                  cli_factory=mock_cli_factory)\n        assert result.exit_code == 1\n        assert 'It took too long' in result.output\n\n\ndef test_invoke_does_raise_if_no_function_found(runner, mock_cli_factory):\n    mock_cli_factory.create_lambda_invoke_handler.side_effect = \\\n        factory.NoSuchFunctionError('foo')\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = _run_cli_command(runner, cli.invoke, ['-n', 'foo'],\n                                  cli_factory=mock_cli_factory)\n        assert result.exit_code == 2\n        assert 'foo' in result.output\n\n\ndef test_error_message_displayed_when_missing_feature_opt_in(runner):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        with open(os.path.join('testproject', 'app.py'), 'w') as f:\n            # Rather than pick an existing experimental feature, we're\n            # manually injecting a feature flag into our app.  This ensures\n            # we don't have to update this test if a feature graduates\n            # from trial to accepted.  The '_features_used' is a \"package\n            # private\" var for chalice code.\n            f.write(\n                'from chalice import Chalice\\n'\n                'app = Chalice(\"myapp\")\\n'\n                'app._features_used.add(\"MYTESTFEATURE\")\\n'\n            )\n        os.chdir('testproject')\n        result = _run_cli_command(runner, cli.package, ['out'])\n        assert isinstance(result.exception, ExperimentalFeatureError)\n        assert 'MYTESTFEATURE' in str(result.exception)\n\n\n@pytest.mark.parametrize(\n    \"path\",\n    [\n        None,\n        '.',\n        os.getcwd,\n    ],\n)\ndef test_cli_with_absolute_path(runner, path):\n    with runner.isolated_filesystem():\n        if callable(path):\n            path = path()\n        result = runner.invoke(\n            cli.cli,\n            ['--project-dir', path, 'new-project', 'testproject'],\n            obj={})\n        assert result.exit_code == 0\n        assert os.listdir(os.getcwd()) == ['testproject']\n        assert_chalice_app_structure_created(dirname='testproject')\n\n\ndef test_can_generate_dev_plan(runner, mock_cli_factory):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = _run_cli_command(runner, cli.plan, [],\n                                  cli_factory=mock_cli_factory)\n        deployer = mock_cli_factory.create_plan_only_deployer.return_value\n        call_args = deployer.deploy.call_args\n        assert result.exit_code == 0\n        assert isinstance(call_args[0][0], Config)\n        assert call_args[1] == {'chalice_stage_name': 'dev'}\n\n\n# The appgraph command actually works on py27, but due to a bug in click's\n# testing (https://github.com/pallets/click/issues/848), it assumes\n# stdout must be ascii.\n# stdout is a cStringIO.StringIO, which doesn't accept unicode.\n# See: https://docs.python.org/2/library/stringio.html#cStringIO.StringIO\n@pytest.mark.skipif(sys.version_info[0] == 2,\n                    reason=\"Click bug when writing unicode to stdout.\")\ndef test_can_generate_appgraph(runner, mock_cli_factory):\n    with runner.isolated_filesystem():\n        newproj.create_new_project_skeleton('testproject')\n        os.chdir('testproject')\n        result = _run_cli_command(runner, cli.appgraph, [])\n        assert result.exit_code == 0\n        # Just sanity checking some of the output\n        assert 'Application' in result.output\n        assert 'RestAPI(' in result.output\n\n\ndef test_chalice_cli_mode_env_var_always_set(runner):\n    with runner.isolated_filesystem():\n        result = runner.invoke(cli.new_project, ['testproject'], obj={})\n        assert result.exit_code == 0\n        assert os.environ['AWS_CHALICE_CLI_MODE'] == 'true'\n"
  },
  {
    "path": "tests/functional/cli/test_factory.py",
    "content": "import os\nimport sys\nimport json\nimport logging\n\nimport pytest\nfrom pytest import fixture\n\nfrom chalice.cli import factory\nfrom chalice.deploy.deployer import Deployer, DeploymentReporter\nfrom chalice.config import Config\nfrom chalice.config import DeployedResources\nfrom chalice import local\nfrom chalice.package import PackageOptions\nfrom chalice.utils import UI\nfrom chalice import Chalice\nfrom chalice.logs import LogRetriever\nfrom chalice.invoke import LambdaInvokeHandler\n\n\n@fixture\ndef no_deployed_values():\n    return DeployedResources({'resources': [], 'schema_version': '2.0'})\n\n\n@fixture\ndef clifactory(tmpdir):\n    appdir = tmpdir.mkdir('app')\n    appdir.join('app.py').write(\n        '# Test app\\n'\n        'import chalice\\n'\n        'app = chalice.Chalice(app_name=\"test\")\\n'\n    )\n    chalice_dir = appdir.mkdir('.chalice')\n    chalice_dir.join('config.json').write('{}')\n    return factory.CLIFactory(str(appdir))\n\n\ndef assert_has_no_request_body_filter(log_name):\n    log = logging.getLogger(log_name)\n    assert not any(\n        isinstance(f, factory.LargeRequestBodyFilter) for f in log.filters)\n\n\ndef assert_request_body_filter_in_log(log_name):\n    log = logging.getLogger(log_name)\n    assert any(\n        isinstance(f, factory.LargeRequestBodyFilter) for f in log.filters)\n\n\ndef test_can_create_botocore_session():\n    session = factory.create_botocore_session()\n    assert session.user_agent().startswith('aws-chalice/')\n    assert session.get_default_client_config() is None\n\n\ndef test_can_create_botocore_session_debug():\n    log_name = 'botocore.endpoint'\n    assert_has_no_request_body_filter(log_name)\n\n    factory.create_botocore_session(debug=True)\n\n    assert_request_body_filter_in_log(log_name)\n    assert logging.getLogger('').level == logging.DEBUG\n\n\ndef test_can_create_botocore_session_connection_timeout():\n    session = factory.create_botocore_session(connection_timeout=100)\n    assert vars(session.get_default_client_config())['connect_timeout'] == 100\n\n\ndef test_can_create_botocore_session_read_timeout():\n    session = factory.create_botocore_session(read_timeout=50)\n    assert vars(session.get_default_client_config())['read_timeout'] == 50\n\n\ndef test_can_create_botocore_session_max_retries():\n    session = factory.create_botocore_session(max_retries=2)\n    assert vars(\n        session.get_default_client_config())['retries']['max_attempts'] == 2\n\n\ndef test_can_create_botocore_session_with_multiple_configs():\n    session = factory.create_botocore_session(\n        connection_timeout=100,\n        read_timeout=50,\n        max_retries=5,\n    )\n    assert vars(session.get_default_client_config())['connect_timeout'] == 100\n    assert vars(session.get_default_client_config())['read_timeout'] == 50\n    assert vars(\n        session.get_default_client_config())['retries']['max_attempts'] == 5\n\n\ndef test_can_create_botocore_session_cli_factory(clifactory):\n    clifactory.profile = 'myprofile'\n    session = clifactory.create_botocore_session()\n    assert session.profile == 'myprofile'\n\n\ndef test_can_create_deletion_deployer(clifactory):\n    session = clifactory.create_botocore_session()\n    deployer = clifactory.create_deletion_deployer(session, UI())\n    assert isinstance(deployer, Deployer)\n\n\ndef test_can_create_plan_only_deployer(clifactory):\n    session = clifactory.create_botocore_session()\n    config = clifactory.create_config_obj(chalice_stage_name='dev')\n    deployer = clifactory.create_plan_only_deployer(\n        session=session, config=config, ui=UI())\n    assert isinstance(deployer, Deployer)\n\n\ndef test_can_create_config_obj(clifactory):\n    obj = clifactory.create_config_obj()\n    assert isinstance(obj, Config)\n\n\ndef test_can_create_config_obj_default_autogen_policy_true(clifactory):\n    config = clifactory.create_config_obj()\n    assert config.autogen_policy is True\n\n\ndef test_provided_autogen_policy_overrides_config_file(clifactory):\n    config_file = os.path.join(\n        clifactory.project_dir, '.chalice', 'config.json')\n    with open(config_file, 'w') as f:\n        f.write('{\"autogen_policy\": false}')\n    config = clifactory.create_config_obj(autogen_policy=True)\n    assert config.autogen_policy is True\n\n\ndef test_can_create_config_obj_with_override_autogen(clifactory):\n    config = clifactory.create_config_obj(autogen_policy=False)\n    assert config.autogen_policy is False\n\n\ndef test_config_file_override_autogen_policy(clifactory):\n    config_file = os.path.join(\n        clifactory.project_dir, '.chalice', 'config.json')\n    with open(config_file, 'w') as f:\n        f.write('{\"autogen_policy\": false}')\n    config = clifactory.create_config_obj()\n    assert config.autogen_policy is False\n\n\ndef test_can_create_config_obj_with_api_gateway_stage(clifactory):\n    config = clifactory.create_config_obj(api_gateway_stage='custom-stage')\n    assert config.api_gateway_stage == 'custom-stage'\n\n\ndef test_can_create_config_obj_with_default_api_gateway_stage(clifactory):\n    config = clifactory.create_config_obj()\n    assert config.api_gateway_stage == 'api'\n\n\ndef test_cant_load_config_obj_with_bad_project(clifactory):\n    clifactory.project_dir = 'nowhere-asdfasdfasdfas'\n    with pytest.raises(RuntimeError):\n        clifactory.create_config_obj()\n\n\ndef test_error_raised_on_unknown_config_version(clifactory):\n    filename = os.path.join(\n        clifactory.project_dir, '.chalice', 'config.json')\n    with open(filename, 'w') as f:\n        f.write(json.dumps({\"version\": \"100.0\"}))\n\n    with pytest.raises(factory.UnknownConfigFileVersion):\n        clifactory.create_config_obj()\n\n\ndef test_filename_and_lineno_included_in_syntax_error(clifactory):\n    filename = os.path.join(clifactory.project_dir, 'app.py')\n    with open(filename, 'w') as f:\n        f.write(\"this is a syntax error\\n\")\n    # If this app has been previously imported in another app\n    # we need to remove it from the cached modules to ensure\n    # we get the syntax error on import.\n    with pytest.raises(RuntimeError) as excinfo:\n        clifactory.load_chalice_app()\n    message = str(excinfo.value)\n    assert 'app.py' in message\n    assert 'line 1' in message\n\n\ndef test_can_import_vendor_package(clifactory):\n    # Tests that vendor packages can be imported during config loading.\n    vendor_lib = os.path.join(clifactory.project_dir, 'vendor')\n    vendedlib_dir = os.path.join(vendor_lib, 'vendedlib')\n    os.makedirs(vendedlib_dir)\n    open(os.path.join(vendedlib_dir, '__init__.py'), 'a').close()\n    with open(os.path.join(vendedlib_dir, 'submodule.py'), 'a') as f:\n        f.write('CONST = \"foo bar\"\\n')\n    app_py = os.path.join(clifactory.project_dir, 'app.py')\n    with open(app_py, 'a') as f:\n        f.write('from vendedlib import submodule\\n')\n        f.write('app.imported_value = submodule.CONST\\n')\n    app = clifactory.load_chalice_app()\n    assert app.imported_value == 'foo bar'\n    assert sys.path[-1] == vendor_lib\n\n\ndef test_error_raised_on_invalid_config_json(clifactory):\n    filename = os.path.join(\n        clifactory.project_dir, '.chalice', 'config.json')\n    with open(filename, 'w') as f:\n        f.write(\"INVALID_JSON\")\n\n    with pytest.raises(RuntimeError):\n        clifactory.create_config_obj()\n\n\ndef test_can_create_local_server(clifactory):\n    app = clifactory.load_chalice_app()\n    config = clifactory.create_config_obj()\n    server = clifactory.create_local_server(app, config, '0.0.0.0', 8000)\n    assert isinstance(server, local.LocalDevServer)\n    assert server.host == '0.0.0.0'\n    assert server.port == 8000\n\n\ndef test_can_create_deployment_reporter(clifactory):\n    ui = UI()\n    reporter = clifactory.create_deployment_reporter(ui=ui)\n    assert isinstance(reporter, DeploymentReporter)\n\n\ndef test_can_access_lazy_loaded_app(clifactory):\n    config = clifactory.create_config_obj()\n    assert isinstance(config.chalice_app, Chalice)\n\n\ndef test_can_create_log_retriever(clifactory):\n    session = clifactory.create_botocore_session()\n    lambda_arn = (\n        'arn:aws:lambda:us-west-2:1:function:app-dev-foo'\n    )\n    logs = clifactory.create_log_retriever(session, lambda_arn,\n                                           follow_logs=False)\n    assert isinstance(logs, LogRetriever)\n\n\ndef test_can_create_follow_logs_retriever(clifactory):\n    session = clifactory.create_botocore_session()\n    lambda_arn = (\n        'arn:aws:lambda:us-west-2:1:function:app-dev-foo'\n    )\n    logs = clifactory.create_log_retriever(session, lambda_arn,\n                                           follow_logs=True)\n    assert isinstance(logs, LogRetriever)\n\n\ndef test_can_create_lambda_invoke_handler(clifactory):\n    lambda_arn = (\n        'arn:aws:lambda:us-west-2:1:function:app-dev-foo'\n    )\n    stage = 'dev'\n    deployed_dir = os.path.join(clifactory.project_dir, '.chalice', 'deployed')\n    os.mkdir(deployed_dir)\n    deployed_file = os.path.join(deployed_dir, '%s.json' % stage)\n    with open(deployed_file, 'w') as f:\n        f.write(json.dumps({\n            'resources': [\n                {\n                    'name': 'foobar',\n                    'resource_type': 'lambda_function',\n                    'lambda_arn': lambda_arn,\n                },\n            ], 'schema_version': '2.0'\n        }))\n\n    invoker = clifactory.create_lambda_invoke_handler('foobar', stage)\n    assert isinstance(invoker, LambdaInvokeHandler)\n\n\ndef test_does_raise_not_found_error_when_no_function_found(\n        clifactory, no_deployed_values):\n    with pytest.raises(factory.NoSuchFunctionError) as e:\n        clifactory.create_lambda_invoke_handler('function_name', 'stage')\n    assert e.value.name == 'function_name'\n\n\ndef test_does_raise_not_found_error_when_resource_is_not_lambda(clifactory):\n    stage = 'dev'\n    deployed_dir = os.path.join(clifactory.project_dir, '.chalice', 'deployed')\n    os.mkdir(deployed_dir)\n    deployed_file = os.path.join(deployed_dir, '%s.json' % stage)\n    with open(deployed_file, 'w') as f:\n        f.write(json.dumps({\n            'resources': [\n                {\n                    'name': 'foobar',\n                    'resource_type': 'iam_role',\n                    'role_arn': 'bazbuz',\n                },\n            ], 'schema_version': '2.0'\n        }))\n    with pytest.raises(factory.NoSuchFunctionError) as e:\n        clifactory.create_lambda_invoke_handler('foobar', stage)\n    assert e.value.name == 'foobar'\n\n\ndef test_can_create_package_options(clifactory):\n    options = clifactory.create_package_options()\n    assert isinstance(options, PackageOptions)\n"
  },
  {
    "path": "tests/functional/cli/test_reloader.py",
    "content": "import pytest\nfrom unittest import mock\nimport threading\nimport os\nimport unittest\nimport time\n\nfrom chalice.cli.filewatch.stat import StatWorkerProcess\ntry:\n    from chalice.cli.filewatch.eventbased import WatchdogWorkerProcess\n    WATCHDOG_AVAILABLE = True\nexcept ImportError:\n    WATCHDOG_AVAILABLE = False\n\nimport chalice.local\n\n\nDEFAULT_DELAY = 0.1\nSETTLE_DELAY = 1\nMAX_TIMEOUT = 5.0\nuse_all_watcher_types = pytest.mark.parametrize(\n    ['worker_class_type'], [('watchdog',), ('stat',)])\n\n\ndef modify_file_after_n_seconds(filename, contents, delay=DEFAULT_DELAY):\n    t = threading.Timer(delay, function=modify_file, args=(filename, contents))\n    t.daemon = True\n    t.start()\n\n\ndef delete_file_after_n_seconds(filename, delay=DEFAULT_DELAY):\n    t = threading.Timer(delay, function=os.remove, args=(filename,))\n    t.daemon = True\n    t.start()\n\n\ndef modify_file(filename, contents):\n    if filename is None:\n        return\n    with open(filename, 'w') as f:\n        f.write(contents)\n\n\ndef assert_reload_happens(root_dir, when_modified_file, using_worker_class):\n    http_thread = mock.Mock(spec=chalice.local.HTTPServerThread)\n    worker_cls = get_worker_cls(using_worker_class)\n    p = worker_cls(http_thread)\n    if isinstance(when_modified_file, tuple):\n        if when_modified_file[1] == 'is_deleted':\n            delete_file_after_n_seconds(when_modified_file[0])\n    else:\n        modify_file_after_n_seconds(when_modified_file, 'contents')\n    rc = p.main(root_dir, MAX_TIMEOUT)\n    assert rc == chalice.cli.filewatch.RESTART_REQUEST_RC\n\n\ndef get_worker_cls(worker_class_name):\n    if worker_class_name == 'watchdog':\n        if not WATCHDOG_AVAILABLE:\n            raise unittest.SkipTest(\"Test requires watchdog package.\")\n        else:\n            return WatchdogWorkerProcess\n    elif worker_class_name == 'stat':\n        return StatWorkerProcess\n    else:\n        raise RuntimeError(\"Unknown worker class type name: %s\"\n                           % worker_class_name)\n\n\n@use_all_watcher_types\ndef test_can_reload_when_file_created(tmpdir, worker_class_type):\n    top_level_file = str(tmpdir.join('foo'))\n    assert_reload_happens(str(tmpdir), when_modified_file=top_level_file,\n                          using_worker_class=worker_class_type)\n\n\n@use_all_watcher_types\ndef test_can_reload_when_subdir_file_created(tmpdir, worker_class_type):\n    subdir_file = str(tmpdir.join('subdir').mkdir().join('foo.txt'))\n    assert_reload_happens(str(tmpdir), when_modified_file=subdir_file,\n                          using_worker_class=worker_class_type)\n\n\n@use_all_watcher_types\ndef test_can_reload_when_file_modified(tmpdir, worker_class_type):\n    top_level_file = tmpdir.join('foo')\n    top_level_file.write('original contents')\n    # If you write to the file and immediately start the reloader, it\n    # won't see the initial write() above.  I tried out a few delay options,\n    # and a separate SETTLE_DELAY was necessary in order to prevent\n    # intermittent failures.\n    time.sleep(SETTLE_DELAY)\n    assert_reload_happens(str(tmpdir), when_modified_file=str(top_level_file),\n                          using_worker_class=worker_class_type)\n\n\n@use_all_watcher_types\ndef test_can_reload_when_file_removed(tmpdir, worker_class_type):\n    top_level_file = tmpdir.join('foo')\n    top_level_file.write('original contents')\n    assert_reload_happens(\n        str(tmpdir), when_modified_file=(str(top_level_file), 'is_deleted'),\n        using_worker_class=worker_class_type\n    )\n\n\n@use_all_watcher_types\ndef test_rc_0_when_no_file_modified(tmpdir, worker_class_type):\n    http_thread = mock.Mock(spec=chalice.local.HTTPServerThread)\n    worker_cls = get_worker_cls(worker_class_type)\n    p = worker_cls(http_thread)\n    rc = p.main(str(tmpdir), timeout=0.2)\n    assert rc == 0\n"
  },
  {
    "path": "tests/functional/conftest.py",
    "content": "from pytest import fixture\n\n\n@fixture(autouse=True)\ndef ensure_no_local_config(no_local_config):\n    pass\n"
  },
  {
    "path": "tests/functional/envapp/.chalice/config.json",
    "content": "{\n  \"stages\": {\n    \"dev\": {\n      \"api_gateway_stage\": \"api\",\n      \"environment_variables\": {\"FOO\": \"bar\"}\n    }\n  },\n  \"version\": \"2.0\",\n  \"app_name\": \"env\"\n}\n"
  },
  {
    "path": "tests/functional/envapp/.gitignore",
    "content": ".chalice/deployments/\n.chalice/venv/\n"
  },
  {
    "path": "tests/functional/envapp/app.py",
    "content": "import os\nimport sys\nfrom chalice import Chalice\n\napp = Chalice(app_name='env')\n\ntry:\n    foo = os.environ['FOO']\nexcept KeyError:\n    raise AssertionError(\"Env vars were not loaded at import time.\")\n\n\n@app.route('/')\ndef index():\n    return {'hello': foo}\n\n\nsys.stderr.write(\"READY\")\nsys.stderr.flush()\n"
  },
  {
    "path": "tests/functional/envapp/requirements.txt",
    "content": ""
  },
  {
    "path": "tests/functional/test_awsclient.py",
    "content": "import json\nimport datetime\nimport time\nfrom unittest import mock\n\nimport pytest\nimport botocore.exceptions\nfrom botocore.vendored.requests import ConnectionError as \\\n    RequestsConnectionError\nfrom botocore.vendored.requests.exceptions import ReadTimeout as \\\n    RequestsReadTimeout\nfrom botocore import stub\nfrom botocore.utils import datetime2timestamp\n\nfrom chalice.awsclient import TypedAWSClient\nfrom chalice.awsclient import ResourceDoesNotExistError\nfrom chalice.awsclient import DeploymentPackageTooLargeError\nfrom chalice.awsclient import LambdaClientError\nfrom chalice.awsclient import ReadTimeout\n\n\ndef create_policy_statement(source_arn, service_name, statement_id,\n                            account_id=None):\n    policy_statement = {\n        'Action': 'lambda:InvokeFunction',\n        'Condition': {\n            'ArnLike': {\n                'AWS:SourceArn': source_arn,\n            }\n        },\n        'Effect': 'Allow',\n        'Principal': {'Service': '%s.amazonaws.com' % service_name},\n        'Resource': 'function-arn',\n        'Sid': statement_id,\n    }\n    if account_id is not None:\n        policy_statement['Condition']['StringEquals'] = {\n            'AWS:SourceAccount': account_id,\n        }\n    return policy_statement\n\n\ndef test_region_name_is_exposed(stubbed_session):\n    assert TypedAWSClient(stubbed_session).region_name == 'us-west-2'\n\n\ndef test_deploy_rest_api(stubbed_session):\n    stub_client = stubbed_session.stub('apigateway')\n    stub_client.create_deployment(\n        restApiId='api_id', stageName='stage',\n        tracingEnabled=False).returns({})\n\n    stubbed_session.activate_stubs()\n    awsclient = TypedAWSClient(stubbed_session)\n    awsclient.deploy_rest_api('api_id', 'stage', False)\n    stubbed_session.verify_stubs()\n\n\ndef test_defaults_to_false_if_none_deploy_rest_api(stubbed_session):\n    stub_client = stubbed_session.stub('apigateway')\n    stub_client.create_deployment(\n        restApiId='api_id', stageName='stage',\n        tracingEnabled=False).returns({})\n\n    stubbed_session.activate_stubs()\n    awsclient = TypedAWSClient(stubbed_session)\n    awsclient.deploy_rest_api('api_id', 'stage', None)\n    stubbed_session.verify_stubs()\n\n\ndef test_put_role_policy(stubbed_session):\n    stubbed_session.stub('iam').put_role_policy(\n        RoleName='role_name',\n        PolicyName='policy_name',\n        PolicyDocument=json.dumps({'foo': 'bar'}, indent=2)\n    ).returns({})\n    stubbed_session.activate_stubs()\n\n    awsclient = TypedAWSClient(stubbed_session)\n    awsclient.put_role_policy('role_name', 'policy_name', {'foo': 'bar'})\n\n    stubbed_session.verify_stubs()\n\n\ndef test_rest_api_exists(stubbed_session):\n    stubbed_session.stub('apigateway').get_rest_api(\n        restApiId='api').returns({'id': 'api'})\n    stubbed_session.activate_stubs()\n\n    awsclient = TypedAWSClient(stubbed_session)\n    assert awsclient.get_rest_api('api')\n\n    stubbed_session.verify_stubs()\n\n\ndef test_rest_api_not_exists(stubbed_session):\n    stubbed_session.stub('apigateway').get_rest_api(\n        restApiId='api').raises_error(\n            error_code='NotFoundException',\n            message='ResourceNotFound')\n    stubbed_session.activate_stubs()\n\n    awsclient = TypedAWSClient(stubbed_session)\n    assert not awsclient.get_rest_api('api')\n\n    stubbed_session.verify_stubs()\n\n\ndef test_can_get_function_configuration(stubbed_session):\n    stubbed_session.stub('lambda').get_function_configuration(\n        FunctionName='myfunction',\n    ).returns({\n        \"FunctionName\": \"myfunction\",\n        \"MemorySize\": 128,\n        \"Handler\": \"app.app\",\n        \"Runtime\": \"python3.6\",\n    })\n\n    stubbed_session.activate_stubs()\n    awsclient = TypedAWSClient(stubbed_session)\n    assert (awsclient.get_function_configuration('myfunction')['Runtime'] ==\n            'python3.6')\n\n\ndef test_can_iterate_logs(stubbed_session):\n    stubbed_session.stub('logs').filter_log_events(\n        logGroupName='loggroup', interleaved=True).returns({\n            \"events\": [{\n                \"logStreamName\": \"logStreamName\",\n                \"timestamp\": 1501278366000,\n                \"message\": \"message\",\n                \"ingestionTime\": 1501278366000,\n                \"eventId\": \"eventId\"\n            }],\n        })\n\n    stubbed_session.activate_stubs()\n\n    awsclient = TypedAWSClient(stubbed_session)\n    logs = list(awsclient.iter_log_events('loggroup'))\n    timestamp = datetime.datetime.utcfromtimestamp(1501278366)\n    assert logs == [\n        {'logStreamName': 'logStreamName',\n         # We should have converted the ints to timestamps.\n         'timestamp': timestamp,\n         'message': 'message',\n         'ingestionTime': timestamp,\n         'eventId': 'eventId'}\n    ]\n\n    stubbed_session.verify_stubs()\n\n\ndef test_can_provide_optional_start_time_iter_logs(stubbed_session):\n    timestamp = int(datetime2timestamp(datetime.datetime.utcnow()) * 1000)\n    # We need to convert back from timestamp instead of using utcnow() directly\n    # because the loss of precision in sub ms time.\n    datetime_now = datetime.datetime.utcfromtimestamp(timestamp / 1000.0)\n    stubbed_session.stub('logs').filter_log_events(\n        logGroupName='loggroup', interleaved=True).returns({\n            \"events\": [{\n                \"logStreamName\": \"logStreamName\",\n                \"timestamp\": timestamp,\n                \"message\": \"message\",\n                \"ingestionTime\": timestamp,\n                \"eventId\": \"eventId\"\n            }],\n        })\n\n    stubbed_session.activate_stubs()\n\n    awsclient = TypedAWSClient(stubbed_session)\n    logs = list(awsclient.iter_log_events('loggroup', start_time=datetime_now))\n    assert logs == [\n        {'logStreamName': 'logStreamName',\n         'timestamp': datetime_now,\n         'message': 'message',\n         'ingestionTime': datetime_now,\n         'eventId': 'eventId'}\n    ]\n\n    stubbed_session.verify_stubs()\n\n\ndef test_missing_log_messages_doesnt_fail(stubbed_session):\n    stubbed_session.stub('logs').filter_log_events(\n        logGroupName='loggroup', interleaved=True).raises_error(\n            error_code='ResourceNotFoundException',\n            message='ResourceNotFound')\n    stubbed_session.activate_stubs()\n\n    awsclient = TypedAWSClient(stubbed_session)\n    logs = list(awsclient.iter_log_events('loggroup'))\n    assert logs == []\n\n\ndef test_can_call_filter_log_events(stubbed_session):\n    stubbed_session.stub('logs').filter_log_events(\n        logGroupName='loggroup', interleaved=True,\n        nextToken='nexttoken', startTime=1577836800000.0\n    ).returns({\n        \"events\": [{\n            \"logStreamName\": \"logStreamName\",\n            \"timestamp\": 1501278366000,\n            \"message\": \"message\",\n            \"ingestionTime\": 1501278366000,\n            \"eventId\": \"eventId\"\n        }],\n    })\n    stubbed_session.activate_stubs()\n    timestamp = datetime.datetime.utcfromtimestamp(1501278366)\n    awsclient = TypedAWSClient(stubbed_session)\n    assert awsclient.filter_log_events(\n        log_group_name='loggroup',\n        next_token='nexttoken',\n        start_time=datetime.datetime(2020, 1, 1)\n    ) == {\n        'events': [{\n            \"logStreamName\": \"logStreamName\",\n            \"timestamp\": timestamp,\n            \"message\": \"message\",\n            \"ingestionTime\": timestamp,\n            \"eventId\": \"eventId\"\n        }]\n    }\n\n\ndef test_optional_kwarg_on_filter_logs_omitted(stubbed_session):\n    stubbed_session.stub('logs').filter_log_events(\n        logGroupName='loggroup', interleaved=True,\n    ).returns({\n        \"events\": [{\n            \"logStreamName\": \"logStreamName\",\n            \"timestamp\": 1501278366000,\n            \"message\": \"message\",\n            \"ingestionTime\": 1501278366000,\n            \"eventId\": \"eventId\"\n        }],\n    })\n    stubbed_session.activate_stubs()\n    timestamp = datetime.datetime.utcfromtimestamp(1501278366)\n    awsclient = TypedAWSClient(stubbed_session)\n    assert awsclient.filter_log_events(\n        log_group_name='loggroup',\n    ) == {\n        'events': [{\n            \"logStreamName\": \"logStreamName\",\n            \"timestamp\": timestamp,\n            \"message\": \"message\",\n            \"ingestionTime\": timestamp,\n            \"eventId\": \"eventId\"\n        }]\n    }\n\n\ndef test_missing_log_events_returns_empty_response(stubbed_session):\n    stubbed_session.stub('logs').filter_log_events(\n        logGroupName='loggroup', interleaved=True).raises_error(\n            error_code='ResourceNotFoundException',\n            message='ResourceNotFound')\n    stubbed_session.activate_stubs()\n\n    awsclient = TypedAWSClient(stubbed_session)\n    assert awsclient.filter_log_events(\n        log_group_name='loggroup',\n    ) == {'events': []}\n\n\ndef test_rule_arn_requires_expression_or_pattern(stubbed_session):\n    client = TypedAWSClient(stubbed_session)\n    with pytest.raises(ValueError):\n        client.get_or_create_rule_arn(\"foo\")\n\n\nclass TestLambdaLayer(object):\n\n    def test_layer_exists(self, stubbed_session):\n        stubbed_session.stub('lambda').get_layer_version_by_arn(\n            Arn='arn:xyz').returns(\n                {'LayerVersionArn': 'arn:xyz'})\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.get_layer_version('arn:xyz') == {\n            'LayerVersionArn': 'arn:xyz'}\n\n    def test_layer_exists_not_found_error(self, stubbed_session):\n        stubbed_session.stub('lambda').get_layer_version_by_arn(\n            Arn='arn:xyz').raises_error(\n                error_code='ResourceNotFoundException',\n                message='Not Found')\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.get_layer_version('arn:xyz') == {}\n\n    def test_layer_delete_not_found_error(self, stubbed_session):\n        stubbed_session.stub('lambda').delete_layer_version(\n            LayerName='xyz',\n            VersionNumber=4).raises_error(\n                error_code='ResourceNotFoundException',\n                message='Not Found')\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.delete_layer_version('arn:xyz:4') is None\n\n    def test_publish_layer_propagate_error(self, stubbed_session):\n        stubbed_session.stub('lambda').publish_layer_version(\n            LayerName='name',\n            CompatibleRuntimes=['python2.7'],\n            Content={'ZipFile': b'foo'},\n        ).raises_error(error_code='UnexpectedError',\n                       message='Unknown')\n        stubbed_session.activate_stubs()\n\n        awsclient = TypedAWSClient(stubbed_session)\n        with pytest.raises(LambdaClientError) as excinfo:\n            awsclient.publish_layer(\n                'name', b'foo', 'python2.7') == 'arn:12345:name'\n        assert isinstance(\n            excinfo.value.original_error, botocore.exceptions.ClientError)\n        stubbed_session.verify_stubs()\n\n    def test_can_publish_layer(self, stubbed_session):\n        stubbed_session.stub('lambda').publish_layer_version(\n            LayerName='name',\n            CompatibleRuntimes=['python2.7'],\n            Content={'ZipFile': b'foo'},\n        ).returns({'LayerVersionArn': 'arn:12345:name:3'})\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.publish_layer(\n            'name', b'foo', 'python2.7') == 'arn:12345:name:3'\n        stubbed_session.verify_stubs()\n\n\nclass TestLambdaFunctionExists(object):\n\n    def test_can_query_lambda_function_exists(self, stubbed_session):\n        stubbed_session.stub('lambda').get_function(FunctionName='myappname')\\\n                .returns({'Code': {}, 'Configuration': {}})\n\n        stubbed_session.activate_stubs()\n\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.lambda_function_exists(name='myappname')\n\n        stubbed_session.verify_stubs()\n\n    def test_can_query_lambda_function_does_not_exist(self, stubbed_session):\n        stubbed_session.stub('lambda').get_function(FunctionName='myappname')\\\n                .raises_error(error_code='ResourceNotFoundException',\n                              message='ResourceNotFound')\n\n        stubbed_session.activate_stubs()\n\n        awsclient = TypedAWSClient(stubbed_session)\n        assert not awsclient.lambda_function_exists(name='myappname')\n\n        stubbed_session.verify_stubs()\n\n    def test_lambda_function_bad_error_propagates(self, stubbed_session):\n        stubbed_session.stub('lambda').get_function(FunctionName='myappname')\\\n                .raises_error(error_code='UnexpectedError',\n                              message='Unknown')\n\n        stubbed_session.activate_stubs()\n\n        awsclient = TypedAWSClient(stubbed_session)\n        with pytest.raises(botocore.exceptions.ClientError):\n            awsclient.lambda_function_exists(name='myappname')\n\n        stubbed_session.verify_stubs()\n\n\nclass TestDeleteLambdaFunction(object):\n    def test_lambda_delete_function(self, stubbed_session):\n        stubbed_session.stub('lambda')\\\n                       .delete_function(FunctionName='name').returns({})\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.delete_function('name') is None\n        stubbed_session.verify_stubs()\n\n    def test_lambda_delete_function_already_deleted(self, stubbed_session):\n        stubbed_session.stub('lambda')\\\n                       .delete_function(FunctionName='name')\\\n                       .raises_error(error_code='ResourceNotFoundException',\n                                     message='Unknown')\n        stubbed_session.activate_stubs()\n\n        awsclient = TypedAWSClient(stubbed_session)\n        with pytest.raises(ResourceDoesNotExistError):\n            assert awsclient.delete_function('name')\n\n\nclass TestDeleteRestAPI(object):\n    def test_rest_api_delete(self, stubbed_session):\n        stubbed_session.stub('apigateway')\\\n                       .delete_rest_api(restApiId='name').returns({})\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.delete_rest_api('name') is None\n        stubbed_session.verify_stubs()\n\n    def test_rest_api_delete_already_deleted(self, stubbed_session):\n        stubbed_session.stub('apigateway')\\\n                       .delete_rest_api(restApiId='name')\\\n                       .raises_error(error_code='NotFoundException',\n                                     message='Unknown')\n        stubbed_session.activate_stubs()\n\n        awsclient = TypedAWSClient(stubbed_session)\n        with pytest.raises(ResourceDoesNotExistError):\n            assert awsclient.delete_rest_api('name')\n\n\nclass TestGetDomainName(object):\n    def test_get_domain_name(self, stubbed_session):\n        domain_name = 'test_domain'\n        certificate_arn = 'arn:aws:acm:us-east-1:aws_id:certificate/12345'\n        regional_name = 'test.execute-api.us-east-1.amazonaws.com'\n        stubbed_session.stub('apigateway')\\\n            .get_domain_name(domainName=domain_name)\\\n            .returns({\n                'domainName': 'test_domain',\n                'certificateUploadDate': datetime.datetime.now(),\n                'regionalDomainName': regional_name,\n                'regionalHostedZoneId': 'TEST1TEST1TESTQ1',\n                'regionalCertificateArn': certificate_arn,\n                'endpointConfiguration': {\n                    'types': ['REGIONAL']\n                },\n                'domainNameStatus': 'AVAILABLE',\n                'securityPolicy': 'TLS_1_0',\n                'tags': {\n                    'some_key1': 'test_value1',\n                    'some_key2': 'test_value2'\n                }\n            })\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        result = awsclient.get_domain_name(domain_name)['domainName']\n        assert result == domain_name\n\n    def test_get_domain_name_failed(self, stubbed_session):\n        domain_name = 'unknown_domain'\n        stubbed_session.stub('apigateway') \\\n            .get_domain_name(domainName=domain_name) \\\n            .raises_error(error_code='NotFoundException',\n                          message='Unknown')\n        stubbed_session.activate_stubs()\n\n        awsclient = TypedAWSClient(stubbed_session)\n        with pytest.raises(ResourceDoesNotExistError):\n            assert awsclient.get_domain_name(domain_name)\n\n    def test_domain_name_exists(self, stubbed_session):\n        domain_name = 'test_domain'\n        certificate_arn = 'arn:aws:acm:us-east-1:aws_id:certificate/12345'\n        regional_name = 'test.execute-api.us-east-1.amazonaws.com'\n        stubbed_session.stub('apigateway')\\\n            .get_domain_name(domainName=domain_name)\\\n            .returns({\n                'domainName': 'test_domain',\n                'certificateUploadDate': datetime.datetime.now(),\n                'regionalDomainName': regional_name,\n                'regionalHostedZoneId': 'TEST1TEST1TESTQ1',\n                'regionalCertificateArn': certificate_arn,\n                'endpointConfiguration': {\n                    'types': ['REGIONAL']\n                },\n                'domainNameStatus': 'AVAILABLE',\n                'securityPolicy': 'TLS_1_0',\n                'tags': {\n                    'some_key1': 'test_value1',\n                    'some_key2': 'test_value2'\n                }\n            })\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.domain_name_exists(domain_name)\n\n    def test_domain_name_does_not_exist(self, stubbed_session):\n        domain_name = 'unknown_domain'\n        stubbed_session.stub('apigateway') \\\n            .get_domain_name(domainName=domain_name) \\\n            .raises_error(error_code='NotFoundException',\n                          message='Unknown')\n        stubbed_session.activate_stubs()\n\n        awsclient = TypedAWSClient(stubbed_session)\n        assert not awsclient.domain_name_exists(domain_name)\n\n    def test_domain_name_exists_v2(self, stubbed_session):\n        domain_name = 'test_domain'\n        certificate_arn = 'arn:aws:acm:us-east-1:aws_id:certificate/12345'\n        regional_name = 'test.execute-api.us-east-1.amazonaws.com'\n        stubbed_session.stub('apigatewayv2') \\\n            .get_domain_name(DomainName=domain_name) \\\n            .returns({\n                'DomainName': 'test_domain',\n                'DomainNameConfigurations': [{\n                    'ApiGatewayDomainName': regional_name,\n                    'CertificateArn': certificate_arn,\n                    'EndpointType': 'REGIONAL',\n                    'HostedZoneId': 'TEST1TEST1TESTQ1',\n                    'SecurityPolicy': 'TLS_1_0',\n                    'DomainNameStatus': 'AVAILABLE'\n                }],\n                'Tags': {\n                    'some_key1': 'some_value1',\n                    'some_key2': 'some_value2'\n                }\n            })\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.domain_name_exists_v2(domain_name)\n\n    def test_domain_name_does_not_exist_v2(self, stubbed_session):\n        domain_name = 'unknown_domain'\n        stubbed_session.stub('apigatewayv2') \\\n            .get_domain_name(DomainName=domain_name) \\\n            .raises_error(\n                error_code='NotFoundException',\n                message='Unknown'\n            )\n        stubbed_session.activate_stubs()\n\n        awsclient = TypedAWSClient(stubbed_session)\n        assert not awsclient.domain_name_exists_v2(domain_name)\n\n\nclass TestGetApiMapping(object):\n    def test_api_mapping_exists(self, stubbed_session):\n        domain_name = 'test_domain'\n        path = '(none)'\n        stubbed_session.stub('apigatewayv2') \\\n            .get_api_mappings(\n                DomainName=domain_name,\n            ).returns({\n                'Items': [{\n                    'ApiMappingKey': '(none)',\n                    'ApiMappingId': 'mapping_id',\n                    'ApiId': 'rest_api_id',\n                    'Stage': 'test'\n                }]\n            })\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.api_mapping_exists(domain_name, path)\n\n    def test_api_mapping_not_exists(self, stubbed_session):\n        domain_name = 'test_domain'\n        path = 'path-key'\n        stubbed_session.stub('apigatewayv2') \\\n            .get_api_mappings(\n                DomainName=domain_name,\n            ).returns({\n                'Items': [{\n                    'ApiMappingKey': '(none)',\n                    'ApiMappingId': 'mapping_id',\n                    'ApiId': 'rest_api_id',\n                    'Stage': 'test'\n                }]\n            })\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert not awsclient.api_mapping_exists(domain_name, path)\n\n    def test_api_mapping_does_not_exist(self, stubbed_session):\n        domain_name = 'unknown_domain'\n        path = '/unknown'\n        stubbed_session.stub('apigatewayv2') \\\n            .get_api_mappings(\n                DomainName=domain_name,\n            ).raises_error(\n                error_code='NotFoundException',\n                message='Unknown'\n            )\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert not awsclient.api_mapping_exists(domain_name, path)\n\n\nclass TestCreateApiMapping(object):\n    def test_create_api_mapping(self, stubbed_session):\n        domain_name = 'test_domain'\n        path_key = '(none)'\n        api_id = 'rest_api_id'\n        stage = 'test'\n        stubbed_session.stub('apigatewayv2') \\\n            .create_api_mapping(\n            DomainName=domain_name,\n            ApiMappingKey=path_key,\n            ApiId=api_id,\n            Stage=stage\n        ).returns({\n            'ApiId': api_id,\n            'ApiMappingId': 'key_id',\n            'ApiMappingKey': '(none)',\n            'Stage': stage\n        })\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.create_api_mapping(\n            domain_name=domain_name,\n            path_key=path_key,\n            api_id=api_id,\n            stage=stage\n        ) == {\n            'key': '/'\n        }\n\n    def test_create_api_mapping_with_path(self, stubbed_session):\n        domain_name = 'test_domain'\n        path_key = 'path-key'\n        api_id = 'rest_api_id'\n        stage = 'test'\n        stubbed_session.stub('apigatewayv2') \\\n            .create_api_mapping(\n            DomainName=domain_name,\n            ApiMappingKey=path_key,\n            ApiId=api_id,\n            Stage=stage\n        ).returns({\n            'ApiId': api_id,\n            'ApiMappingId': 'key_id',\n            'ApiMappingKey': 'path-key',\n            'Stage': stage\n        })\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.create_api_mapping(\n            domain_name=domain_name,\n            path_key=path_key,\n            api_id=api_id,\n            stage=stage\n        ) == {\n            'key': '/path-key'\n        }\n\n\nclass TestCreateBasePathMapping(object):\n    def test_create_base_path_mapping(self, stubbed_session):\n        domain_name = 'test_domain'\n        path_key = '(none)'\n        api_id = 'rest_api_id'\n        stage = 'test'\n        stubbed_session.stub('apigateway') \\\n            .create_base_path_mapping(\n                domainName=domain_name,\n                basePath=path_key,\n                restApiId=api_id,\n                stage=stage\n            ).returns({\n                'restApiId': api_id,\n                'basePath': '(none)',\n                'stage': stage\n            })\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.create_base_path_mapping(\n            domain_name=domain_name,\n            path_key=path_key,\n            api_id=api_id,\n            stage=stage\n        ) == {\n            'key': '/'\n        }\n\n    def test_create_base_path_mapping_with_path(self, stubbed_session):\n        domain_name = 'test_domain'\n        path_key = 'path-key'\n        api_id = 'rest_api_id'\n        stage = 'test'\n        stubbed_session.stub('apigateway') \\\n            .create_base_path_mapping(\n                domainName=domain_name,\n                basePath=path_key,\n                restApiId=api_id,\n                stage=stage\n            ).returns({\n                'restApiId': api_id,\n                'basePath': 'path-key',\n                'stage': stage\n            })\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.create_base_path_mapping(\n            domain_name=domain_name,\n            path_key=path_key,\n            api_id=api_id,\n            stage=stage\n        ) == {\n            'key': '/path-key'\n        }\n\n    def test_create_base_path_mapping_failed(self, stubbed_session):\n        domain_name = 'test_domain'\n        path_key = '/test'\n        api_id = 'rest_api_id'\n        stage = 'test'\n\n        err_msg = 'An ApiMapping key may contain only letters, ' \\\n                  'numbers and one of $-_.+!*\\'()'\n        stubbed_session.stub('apigateway') \\\n            .create_base_path_mapping(\n                domainName=domain_name,\n                basePath=path_key,\n                restApiId=api_id,\n                stage=stage\n            ).raises_error(\n                error_code='BadRequestException',\n                message=err_msg\n            )\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        with pytest.raises(botocore.exceptions.ClientError):\n            awsclient.create_base_path_mapping(\n                domain_name=domain_name,\n                path_key=path_key,\n                api_id=api_id,\n                stage=stage\n            )\n\n\nclass TestCreateDomainName(object):\n\n    def test_create_domain_name_with_unsupported_protocol(\n            self, stubbed_session\n    ):\n        awsclient = TypedAWSClient(stubbed_session)\n        params = {\n            'protocol': 'SOME_PROTOCOL',\n            'domain_name': 'test_domain',\n            'endpoint_type': 'REGIONAL',\n            'security_policy': 'TLS_1_0',\n            'certificate_arn': 'certificate_arn',\n            'tags': None\n        }\n        with pytest.raises(ValueError):\n            awsclient.create_domain_name(**params)\n\n    def test_create_rest_api_domain_name(self, stubbed_session):\n        stubbed_session.stub('apigateway') \\\n            .create_domain_name(\n                domainName='test_domain',\n                endpointConfiguration={\n                    'types': ['REGIONAL']\n                },\n                securityPolicy='TLS_1_0',\n                tags={\n                  'some_key1': 'some_value1',\n                  'some_key2': 'some_value2'\n                },\n                regionalCertificateArn='certificate_arn',\n            ).returns({\n                'domainName': 'test_domain',\n                'regionalCertificateName': 'certificate_name',\n                'regionalCertificateArn': 'certificate_arn',\n                'regionalDomainName': 'regional_domain_name',\n                'regionalHostedZoneId': 'hosted_zone_id',\n                'endpointConfiguration': {\n                    'types': ['REGIONAL'],\n                },\n                'domainNameStatus': 'AVAILABLE',\n                'domainNameStatusMessage': 'string',\n                'securityPolicy': 'TLS_1_0',\n                'tags': {\n                    'some_key1': 'some_value1',\n                    'some_key2': 'some_value2'\n                }\n            })\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.create_domain_name(\n            protocol='HTTP',\n            domain_name='test_domain',\n            endpoint_type='REGIONAL',\n            security_policy='TLS_1_0',\n            certificate_arn='certificate_arn',\n            tags={\n              'some_key1': 'some_value1',\n              'some_key2': 'some_value2'\n            }\n        ) == {\n            'domain_name': 'test_domain',\n            'security_policy': 'TLS_1_0',\n            'alias_domain_name': 'regional_domain_name',\n            'hosted_zone_id': 'hosted_zone_id',\n            'certificate_arn': 'certificate_arn'\n        }\n        stubbed_session.verify_stubs()\n\n    def test_create_rest_api_domain_name_no_regional(self, stubbed_session):\n        stubbed_session.stub('apigateway') \\\n            .create_domain_name(\n                domainName='test_domain',\n                endpointConfiguration={\n                    'types': ['EDGE']\n                },\n                securityPolicy='TLS_1_0',\n                tags={\n                  'some_key1': 'some_value1',\n                  'some_key2': 'some_value2'\n                },\n                certificateArn='certificate_arn',\n            ).returns({\n                'domainName': 'test_domain',\n                'certificateName': 'certificate_name',\n                'certificateArn': 'certificate_arn',\n                'certificateUploadDate': datetime.datetime.now(),\n                'endpointConfiguration': {\n                    'types': ['EDGE'],\n                },\n                'distributionDomainName': 'dist_test_domain',\n                'distributionHostedZoneId': 'hosted_zone_id',\n                'domainNameStatus': 'AVAILABLE',\n                'domainNameStatusMessage': 'string',\n                'securityPolicy': 'TLS_1_0',\n                'tags': {\n                    'some_key1': 'some_value1',\n                    'some_key2': 'some_value2'\n                }\n            })\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.create_domain_name(\n            protocol='HTTP',\n            domain_name='test_domain',\n            endpoint_type='EDGE',\n            security_policy='TLS_1_0',\n            certificate_arn='certificate_arn',\n            tags={\n              'some_key1': 'some_value1',\n              'some_key2': 'some_value2'\n            }\n        ) == {\n            'domain_name': 'test_domain',\n            'security_policy': 'TLS_1_0',\n            'hosted_zone_id': 'hosted_zone_id',\n            'alias_domain_name': 'dist_test_domain',\n            'certificate_arn': 'certificate_arn'\n        }\n        stubbed_session.verify_stubs()\n\n    def test_create_websocket_api_custom_domain(self, stubbed_session):\n        stubbed_session.stub('apigatewayv2') \\\n            .create_domain_name(\n                DomainName='test_websocket_domain',\n                DomainNameConfigurations=[{\n                    'ApiGatewayDomainName': 'test_websocket_domain',\n                    'CertificateArn': 'certificate_arn',\n                    'EndpointType': 'REGIONAL',\n                    'SecurityPolicy': 'TLS_1_2',\n                    'DomainNameStatus': 'AVAILABLE',\n                }],\n                Tags={\n                    'some_key1': 'some_value1',\n                    'some_key2': 'some_value2'\n                }\n            ).returns({\n                'DomainName': 'test_websocket_domain',\n                'DomainNameConfigurations': [\n                    {\n                        'ApiGatewayDomainName': 'd-1234',\n                        'CertificateArn': 'certificate_arn',\n                        'CertificateName': 'certificate_name',\n                        'CertificateUploadDate': datetime.datetime.now(),\n                        'EndpointType': 'REGIONAL',\n                        'HostedZoneId': 'hosted_zone_id',\n                        'SecurityPolicy': 'TLS_1_2',\n                        'DomainNameStatus': 'AVAILABLE',\n                    },\n                ],\n                'Tags': {\n                    'some_key1': 'some_value1',\n                    'some_key2': 'some_value2'\n                }\n            })\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.create_domain_name(\n            protocol='WEBSOCKET',\n            domain_name='test_websocket_domain',\n            endpoint_type='REGIONAL',\n            security_policy='TLS_1_2',\n            certificate_arn='certificate_arn',\n            tags={\n              'some_key1': 'some_value1',\n              'some_key2': 'some_value2'\n            }\n        ) == {\n            'domain_name': 'test_websocket_domain',\n            'alias_domain_name': 'd-1234',\n            'security_policy': 'TLS_1_2',\n            'hosted_zone_id': 'hosted_zone_id',\n            'certificate_arn': 'certificate_arn'\n        }\n        stubbed_session.verify_stubs()\n\n    def test_get_custom_domain_params_v2(self, stubbed_session):\n        awsclient = TypedAWSClient(stubbed_session)\n        result = awsclient.get_custom_domain_params_v2(\n            domain_name='test_domain_name',\n            endpoint_type='EDGE',\n            security_policy='TLS_1_2',\n            certificate_arn='certificate_arn',\n            tags={\n              'some_key1': 'some_value1',\n              'some_key2': 'some_value2'\n            },\n        )\n        assert result == {\n            'DomainName': 'test_domain_name',\n            'DomainNameConfigurations': [\n                {\n                    'ApiGatewayDomainName': 'test_domain_name',\n                    'CertificateArn': 'certificate_arn',\n                    'EndpointType': 'EDGE',\n                    'SecurityPolicy': 'TLS_1_2',\n                    'DomainNameStatus': 'AVAILABLE',\n                },\n            ],\n            'Tags': {\n                'some_key1': 'some_value1',\n                'some_key2': 'some_value2'\n            }\n        }\n\n    def test_create_domain_name_max_retries(self, stubbed_session):\n        for _ in range(6):\n            stubbed_session.stub('apigateway') \\\n                .create_domain_name(\n                domainName='test_domain',\n                endpointConfiguration={\n                    'types': ['EDGE']\n                },\n                securityPolicy='TLS_1_0',\n                tags={\n                    'some_key1': 'some_value1',\n                    'some_key2': 'some_value2'\n                },\n                certificateArn='certificate_arn'\n            ).raises_error(\n                error_code='TooManyRequestsException',\n                message='Too Many Requests'\n            )\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n        with pytest.raises(botocore.exceptions.ClientError):\n            awsclient.create_domain_name(\n                protocol='HTTP',\n                domain_name='test_domain',\n                endpoint_type='EDGE',\n                security_policy='TLS_1_0',\n                certificate_arn='certificate_arn',\n                tags={\n                    'some_key1': 'some_value1',\n                    'some_key2': 'some_value2'\n                }\n            )\n\n    def test_create_domain_name_v2_max_retries(self, stubbed_session):\n        for _ in range(6):\n            stubbed_session.stub('apigatewayv2') \\\n                .create_domain_name(\n                DomainName='test_websocket_domain',\n                DomainNameConfigurations=[{\n                    'ApiGatewayDomainName': 'test_websocket_domain',\n                    'CertificateArn': 'certificate_arn',\n                    'EndpointType': 'REGIONAL',\n                    'SecurityPolicy': 'TLS_1_2',\n                    'DomainNameStatus': 'AVAILABLE',\n                }],\n                Tags={\n                    'some_key1': 'some_value1',\n                    'some_key2': 'some_value2'\n                }\n            ).raises_error(\n                error_code='TooManyRequestsException',\n                message='Too Many Requests'\n            )\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n        with pytest.raises(botocore.exceptions.ClientError):\n            awsclient.create_domain_name(\n                protocol='WEBSOCKET',\n                domain_name='test_websocket_domain',\n                endpoint_type='REGIONAL',\n                security_policy='TLS_1_2',\n                certificate_arn='certificate_arn',\n                tags={\n                    'some_key1': 'some_value1',\n                    'some_key2': 'some_value2'\n                }\n            )\n\n\nclass TestUpdateDomainName(object):\n    def test_update_domain_name_websocket(self,\n                                          stubbed_session):\n        stubbed_session.stub('apigatewayv2') \\\n            .update_domain_name(\n            DomainName='test_domain',\n            DomainNameConfigurations=[{\n                'ApiGatewayDomainName': 'test_domain',\n                'CertificateArn': 'certificate_arn',\n                'EndpointType': 'REGIONAL',\n                'SecurityPolicy': 'TLS_1_2',\n                'DomainNameStatus': 'AVAILABLE',\n            }]\n        ).returns({\n            'DomainName': 'test_domain',\n            'DomainNameConfigurations': [\n                {\n                    'ApiGatewayDomainName': 'd-1234',\n                    'CertificateArn': 'certificate_arn',\n                    'CertificateName': 'certificate_name',\n                    'CertificateUploadDate': datetime.datetime.now(),\n                    'EndpointType': 'REGIONAL',\n                    'HostedZoneId': 'hosted_zone_id',\n                    'SecurityPolicy': 'TLS_1_2',\n                    'DomainNameStatus': 'AVAILABLE',\n                },\n            ]\n        })\n        arn = 'arn:aws:apigateway:us-west-2::/domainnames/test_domain'\n        stubbed_session.stub('apigatewayv2') \\\n            .get_tags(ResourceArn=arn) \\\n            .returns({\n                'Tags': {}\n            })\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.update_domain_name(\n            protocol='WEBSOCKET',\n            domain_name='test_domain',\n            endpoint_type='REGIONAL',\n            security_policy='TLS_1_2',\n            certificate_arn='certificate_arn',\n        ) == {\n            'domain_name': 'test_domain',\n            'alias_domain_name': 'd-1234',\n            'security_policy': 'TLS_1_2',\n            'hosted_zone_id': 'hosted_zone_id',\n            'certificate_arn': 'certificate_arn'\n        }\n        stubbed_session.verify_stubs()\n\n    def test_update_domain_name_failed(self, stubbed_session):\n        err_msg = 'The resource specified in the request was not found.'\n        stubbed_session.stub('apigatewayv2') \\\n            .update_domain_name(\n            DomainName='unknown_domain',\n            DomainNameConfigurations=[{\n                'ApiGatewayDomainName': 'unknown_domain',\n                'CertificateArn': 'certificate_arn',\n                'EndpointType': 'REGIONAL',\n                'SecurityPolicy': 'TLS_1_2',\n                'DomainNameStatus': 'AVAILABLE',\n            }]).raises_error(\n                error_code='NotFoundException',\n                message=err_msg\n            )\n\n        awsclient = TypedAWSClient(stubbed_session)\n        with pytest.raises(botocore.exceptions.ClientError):\n            awsclient.update_domain_name(\n                protocol='WEBSOCKET',\n                domain_name='unknown_domain',\n                endpoint_type='REGIONAL',\n                security_policy='TLS_1_2',\n                certificate_arn='certificate_arn',\n            )\n\n    def test_update_domain_v2_name_max_retries(self, stubbed_session):\n        for _ in range(6):\n            stubbed_session.stub('apigatewayv2') \\\n                .update_domain_name(\n                DomainName='test_domain',\n                DomainNameConfigurations=[{\n                    'ApiGatewayDomainName': 'test_domain',\n                    'CertificateArn': 'certificate_arn',\n                    'EndpointType': 'REGIONAL',\n                    'SecurityPolicy': 'TLS_1_2',\n                    'DomainNameStatus': 'AVAILABLE',\n                }],\n                Tags={\n                    'some_key1': 'some_value1',\n                    'some_key2': 'some_value2'\n                }\n            ).raises_error(\n                error_code='TooManyRequestsException',\n                message='Too Many Requests'\n            )\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n        with pytest.raises(botocore.exceptions.ClientError):\n            awsclient.update_domain_name(\n                protocol='WEBSOCKET',\n                domain_name='test_domain',\n                endpoint_type='REGIONAL',\n                security_policy='TLS_1_2',\n                certificate_arn='certificate_arn',\n                tags={\n                    'some_key1': 'some_value1',\n                    'some_key2': 'some_value2'\n                }\n            )\n\n    def test_unsupported_protocol(self, stubbed_session):\n        awsclient = TypedAWSClient(stubbed_session)\n        with pytest.raises(ValueError) as err:\n            awsclient.update_domain_name(\n                protocol='unsupported',\n                domain_name='unknown_domain',\n                endpoint_type='REGIONAL',\n                security_policy='TLS_1_2',\n                certificate_arn='certificate_arn',\n            )\n        assert str(err.value) == 'Unsupported protocol value.'\n\n    def test_get_custom_domain_patch_operations(self, stubbed_session):\n        awsclient = TypedAWSClient(stubbed_session)\n        patch_operations = awsclient.get_custom_domain_patch_operations(\n            security_policy='TLS_1_0',\n            certificate_arn='certificate_arn',\n            endpoint_type='EDGE',\n        )\n        assert patch_operations == [\n            {\n                'op': 'replace',\n                'path': '/securityPolicy',\n                'value': 'TLS_1_0',\n            },\n            {\n                'op': 'replace',\n                'path': '/certificateArn',\n                'value': 'certificate_arn',\n            }\n        ]\n\n    def test_get_custom_domain_patch_operations_regional(\n            self,\n            stubbed_session\n    ):\n        awsclient = TypedAWSClient(stubbed_session)\n        patch_operations = awsclient.get_custom_domain_patch_operations(\n            security_policy='TLS_1_2',\n            certificate_arn='regional_certificate_arn',\n            endpoint_type='REGIONAL',\n        )\n        assert patch_operations == [\n            {\n                'op': 'replace',\n                'path': '/securityPolicy',\n                'value': 'TLS_1_2',\n            },\n            {\n                'op': 'replace',\n                'path': '/regionalCertificateArn',\n                'value': 'regional_certificate_arn',\n            }\n        ]\n\n    def test_update_domain_name_http_protocol_regional(\n        self,\n        stubbed_session\n    ):\n        stubbed_session.stub('apigateway') \\\n            .update_domain_name(\n            domainName='test_domain',\n            patchOperations=[\n                {\n                    'op': 'replace',\n                    'path': '/securityPolicy',\n                    'value': 'TLS_1_2',\n                },\n            ]\n        ).returns({\n            'domainName': 'test_domain',\n            'regionalHostedZoneId': 'hosted_zone_id',\n            'regionalDomainName': 'regional_domain_name',\n            'regionalCertificateArn': 'old_regional_certificate_arn',\n            'endpointConfiguration': {\n                'types': [\n                    'REGIONAL',\n                ],\n            },\n            'domainNameStatus': 'AVAILABLE',\n            'securityPolicy': 'TLS_1_2'\n        })\n        stubbed_session.stub('apigateway') \\\n            .update_domain_name(\n            domainName='test_domain',\n            patchOperations=[\n                {\n                    'op': 'replace',\n                    'path': '/regionalCertificateArn',\n                    'value': 'regional_certificate_arn',\n                }\n            ]\n        ).returns({\n            'domainName': 'test_domain',\n            'regionalHostedZoneId': 'hosted_zone_id',\n            'regionalCertificateArn': 'regional_certificate_arn',\n            'regionalDomainName': 'regional_domain_name',\n            'endpointConfiguration': {\n                'types': [\n                    'REGIONAL',\n                ],\n            },\n            'domainNameStatus': 'AVAILABLE',\n            'securityPolicy': 'TLS_1_2'\n        })\n        arn = 'arn:aws:apigateway:us-west-2::/domainnames/test_domain'\n        stubbed_session.stub('apigatewayv2') \\\n            .get_tags(ResourceArn=arn) \\\n            .returns({\n                'Tags': {}\n            })\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.update_domain_name(\n            protocol='HTTP',\n            domain_name='test_domain',\n            endpoint_type='REGIONAL',\n            security_policy='TLS_1_2',\n            certificate_arn='regional_certificate_arn',\n        ) == {\n            'domain_name': 'test_domain',\n            'alias_domain_name': 'regional_domain_name',\n            'security_policy': 'TLS_1_2',\n            'hosted_zone_id': 'hosted_zone_id',\n            'certificate_arn': 'regional_certificate_arn'\n        }\n        stubbed_session.verify_stubs()\n\n    def test_update_domain_name_http_protocol(\n            self,\n            stubbed_session\n    ):\n        stubbed_session.stub('apigateway') \\\n            .update_domain_name(\n            domainName='test_domain',\n            patchOperations=[\n                {\n                    'op': 'replace',\n                    'path': '/securityPolicy',\n                    'value': 'TLS_1_0',\n                },\n            ]\n        ).returns({\n            'domainName': 'test_domain',\n            'distributionHostedZoneId': 'hosted_zone_id',\n            'certificateArn': 'old_certificate_arn',\n            'distributionDomainName': 'dist_domain_name',\n            'endpointConfiguration': {\n                'types': [\n                    'EDGE',\n                ],\n            },\n            'domainNameStatus': 'AVAILABLE',\n            'securityPolicy': 'TLS_1_0'\n        })\n        stubbed_session.stub('apigateway') \\\n            .update_domain_name(\n            domainName='test_domain',\n            patchOperations=[\n                {\n                    'op': 'replace',\n                    'path': '/certificateArn',\n                    'value': 'certificate_arn',\n                }\n            ]\n        ).returns({\n            'domainName': 'test_domain',\n            'distributionHostedZoneId': 'hosted_zone_id',\n            'distributionDomainName': 'dist_domain_name',\n            'certificateArn': 'certificate_arn',\n            'endpointConfiguration': {\n                'types': [\n                    'EDGE',\n                ],\n            },\n            'domainNameStatus': 'AVAILABLE',\n            'securityPolicy': 'TLS_1_0'\n        })\n        arn = 'arn:aws:apigateway:us-west-2::/domainnames/test_domain'\n        stubbed_session.stub('apigatewayv2') \\\n            .get_tags(ResourceArn=arn) \\\n            .returns({\n                'Tags': {}\n            })\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.update_domain_name(\n            protocol='HTTP',\n            domain_name='test_domain',\n            endpoint_type='EDGE',\n            security_policy='TLS_1_0',\n            certificate_arn='certificate_arn',\n        ) == {\n            'domain_name': 'test_domain',\n            'security_policy': 'TLS_1_0',\n            'alias_domain_name': 'dist_domain_name',\n            'hosted_zone_id': 'hosted_zone_id',\n            'certificate_arn': 'certificate_arn'\n        }\n        stubbed_session.verify_stubs()\n\n    def test_update_domain_name_govcloud(self, stubbed_session):\n        stubbed_session.create_client(\n            'apigateway', region_name='us-gov-west-1')\n        apig = stubbed_session.stub('apigateway')\n        self._setup_expected_update_calls(apig)\n        # Verify we use the aws-us-gov partition in our ARN.\n        arn = (\n            'arn:aws-us-gov:apigateway:us-gov-west-1::/domainnames/test_domain'\n        )\n        stubbed_session.stub('apigatewayv2') \\\n            .get_tags(ResourceArn=arn) \\\n            .returns({\n                'Tags': {}\n            })\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        awsclient.update_domain_name(\n            protocol='HTTP',\n            domain_name='test_domain',\n            endpoint_type='EDGE',\n            security_policy='TLS_1_0',\n            certificate_arn='certificate_arn',\n        )\n        stubbed_session.verify_stubs()\n\n    def _setup_expected_update_calls(self, apig):\n        apig.update_domain_name(\n            domainName='test_domain',\n            patchOperations=[\n                {\n                    'op': 'replace',\n                    'path': '/securityPolicy',\n                    'value': 'TLS_1_0',\n                },\n            ]\n        ).returns({\n            'domainName': 'test_domain',\n            'distributionHostedZoneId': 'hosted_zone_id',\n            'certificateArn': 'old_certificate_arn',\n            'distributionDomainName': 'dist_domain_name',\n            'endpointConfiguration': {\n                'types': [\n                    'EDGE',\n                ],\n            },\n            'domainNameStatus': 'AVAILABLE',\n            'securityPolicy': 'TLS_1_0'\n        })\n        apig.update_domain_name(\n            domainName='test_domain',\n            patchOperations=[\n                {\n                    'op': 'replace',\n                    'path': '/certificateArn',\n                    'value': 'certificate_arn',\n                }\n            ]\n        ).returns({\n            'domainName': 'test_domain',\n            'distributionHostedZoneId': 'hosted_zone_id',\n            'distributionDomainName': 'dist_domain_name',\n            'certificateArn': 'certificate_arn',\n            'endpointConfiguration': {\n                'types': [\n                    'EDGE',\n                ],\n            },\n            'domainNameStatus': 'AVAILABLE',\n            'securityPolicy': 'TLS_1_0'\n        })\n\n    def test_update_domain_name_max_retries(self, stubbed_session):\n        for _ in range(6):\n            stubbed_session.stub('apigateway') \\\n                .update_domain_name(\n                domainName='test_domain',\n                patchOperations=[\n                    {\n                        'op': 'replace',\n                        'path': '/certificateArn',\n                        'value': 'certificate_arn',\n                    }\n                ]\n            ).raises_error(\n                error_code='TooManyRequestsException',\n                message='Too Many Requests'\n            )\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n        with pytest.raises(botocore.exceptions.ClientError):\n            awsclient.update_domain_name(\n                protocol='HTTP',\n                domain_name='test_domain',\n                endpoint_type='EDGE',\n                security_policy='TLS_1_0',\n                certificate_arn='certificate_arn',\n            )\n\n    def test_update_resource_tags(self, stubbed_session):\n        arn = 'arn:aws:apigateway:us-west-2::/domainnames/test_domain'\n        stubbed_session.stub('apigatewayv2') \\\n            .get_tags(\n            ResourceArn=arn\n        ).returns({\n            'Tags': {\n                'key': 'value',\n                'key1': 'value1'\n            }\n        })\n        stubbed_session.stub('apigatewayv2') \\\n            .untag_resource(\n            ResourceArn=arn,\n            TagKeys=['key1']\n        ).returns({})\n        stubbed_session.stub('apigatewayv2') \\\n            .tag_resource(\n            ResourceArn=arn,\n            Tags={\n                'key2': 'value2'\n            }\n        ).returns({})\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        tags = {\n            'key': 'value',\n            'key2': 'value2'\n        }\n        awsclient._update_resource_tags(arn, tags)\n        stubbed_session.verify_stubs()\n\n\nclass TestDeleteDomainName(object):\n    def test_delete_domain_name(self, stubbed_session):\n        domain_name = 'test_domain'\n        stubbed_session.stub('apigatewayv2') \\\n            .delete_domain_name(DomainName=domain_name).returns({})\n        stubbed_session.activate_stubs()\n\n        awsclient = TypedAWSClient(stubbed_session)\n        awsclient.delete_domain_name(domain_name=domain_name)\n        stubbed_session.verify_stubs()\n\n    def test_delete_domain_name_failed(self, stubbed_session):\n        domain_name = 'unknown_domain'\n        err_msg = 'The resource specified in the request was not found.'\n        stubbed_session.stub('apigatewayv2') \\\n            .delete_domain_name(DomainName=domain_name) \\\n            .raises_error(\n                error_code='NotFoundException',\n                message=err_msg\n            )\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        with pytest.raises(botocore.exceptions.ClientError):\n            awsclient.delete_domain_name(domain_name=domain_name)\n\n    def test_delete_domain_name_max_retries(self, stubbed_session):\n        for _ in range(6):\n            stubbed_session.stub('apigatewayv2') \\\n                .delete_domain_name(\n                domainName='test_domain',\n            ).raises_error(\n                error_code='TooManyRequestsException',\n                message='Too Many Requests'\n            )\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n        with pytest.raises(botocore.exceptions.ClientError):\n            awsclient.delete_domain_name(domain_name='test_domain')\n\n\nclass TestDeleteApiMapping(object):\n    def test_delete_api_mapping(self, stubbed_session):\n        domain_name = 'test_domain'\n        stubbed_session.stub('apigateway') \\\n            .delete_base_path_mapping(\n                domainName=domain_name,\n                basePath='foo'\n            ).returns({})\n        stubbed_session.activate_stubs()\n\n        awsclient = TypedAWSClient(stubbed_session)\n        awsclient.delete_api_mapping(\n            domain_name=domain_name,\n            path_key='foo'\n        )\n        stubbed_session.verify_stubs()\n\n    def test_delete_api_mapping_failed(self, stubbed_session):\n        domain_name = 'unknown_domain'\n        err_msg = 'The resource specified in the request was not found.'\n        stubbed_session.stub('apigateway') \\\n            .delete_base_path_mapping(\n                domainName=domain_name,\n                basePath='foo'\n            ).raises_error(\n                error_code='NotFoundException',\n                message=err_msg\n            )\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        with pytest.raises(botocore.exceptions.ClientError):\n            awsclient.delete_api_mapping(\n                domain_name=domain_name,\n                path_key='foo'\n            )\n\n\nclass TestGetRestAPI(object):\n    def test_rest_api_exists(self, stubbed_session):\n        desired_name = 'myappname'\n        stubbed_session.stub('apigateway').get_rest_apis()\\\n            .returns(\n                {'items': [\n                    {'createdDate': 1, 'id': 'wrongid1', 'name': 'wrong1'},\n                    {'createdDate': 2, 'id': 'correct', 'name': desired_name},\n                    {'createdDate': 3, 'id': 'wrongid3', 'name': 'wrong3'},\n                ]})\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.get_rest_api_id(desired_name) == 'correct'\n        stubbed_session.verify_stubs()\n\n    def test_rest_api_does_not_exist(self, stubbed_session):\n        stubbed_session.stub('apigateway').get_rest_apis()\\\n            .returns(\n                {'items': [\n                    {'createdDate': 1, 'id': 'wrongid1', 'name': 'wrong1'},\n                    {'createdDate': 2, 'id': 'wrongid1', 'name': 'wrong2'},\n                    {'createdDate': 3, 'id': 'wrongid3', 'name': 'wrong3'},\n                ]})\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.get_rest_api_id('myappname') is None\n        stubbed_session.verify_stubs()\n\n\nclass TestGetRoleArn(object):\n    def test_get_role_arn_for_name_found(self, stubbed_session):\n        # Need len(20) to pass param validation.\n        good_arn = 'good_arn' * 3\n        role_id = 'abcd' * 4\n        today = datetime.datetime.today()\n        stubbed_session.stub('iam').get_role(RoleName='Yes').returns({\n            'Role': {\n                'Path': '/',\n                'RoleName': 'Yes',\n                'RoleId': role_id,\n                'CreateDate': today,\n                'Arn': good_arn\n            }\n        })\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.get_role_arn_for_name(name='Yes') == good_arn\n        stubbed_session.verify_stubs()\n\n    def test_got_role_arn_not_found_raises_value_error(self, stubbed_session):\n        stubbed_session.stub('iam').get_role(RoleName='Yes').raises_error(\n            error_code='NoSuchEntity',\n            message='Foo')\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        with pytest.raises(ResourceDoesNotExistError):\n            awsclient.get_role_arn_for_name(name='Yes')\n        stubbed_session.verify_stubs()\n\n    def test_unexpected_error_is_propagated(self, stubbed_session):\n        stubbed_session.stub('iam').get_role(RoleName='Yes').raises_error(\n            error_code='InternalError',\n            message='Foo')\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        with pytest.raises(botocore.exceptions.ClientError):\n            awsclient.get_role_arn_for_name(name='Yes')\n        stubbed_session.verify_stubs()\n\n\nclass TestGetRole(object):\n    def test_get_role_success(self, stubbed_session):\n        today = datetime.datetime.today()\n        response = {\n            'Role': {\n                'Path': '/',\n                'RoleName': 'Yes',\n                'RoleId': 'abcd' * 4,\n                'CreateDate': today,\n                'Arn': 'good_arn' * 3,\n            }\n        }\n        stubbed_session.stub('iam').get_role(RoleName='Yes').returns(response)\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        actual = awsclient.get_role(name='Yes')\n        assert actual == response['Role']\n        stubbed_session.verify_stubs()\n\n    def test_get_role_raises_exception_when_no_exists(self, stubbed_session):\n        stubbed_session.stub('iam').get_role(RoleName='Yes').raises_error(\n            error_code='NoSuchEntity',\n            message='Foo')\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        with pytest.raises(ResourceDoesNotExistError):\n            awsclient.get_role(name='Yes')\n        stubbed_session.verify_stubs()\n\n\nclass TestCreateRole(object):\n    def test_create_role(self, stubbed_session):\n        arn = 'good_arn' * 3\n        role_id = 'abcd' * 4\n        today = datetime.datetime.today()\n        stubbed_session.stub('iam').create_role(\n            RoleName='role_name',\n            AssumeRolePolicyDocument=json.dumps({'trust': 'policy'})\n        ).returns({'Role': {\n            'RoleName': 'No', 'Arn': arn, 'Path': '/',\n            'RoleId': role_id, 'CreateDate': today}}\n        )\n        stubbed_session.stub('iam').put_role_policy(\n            RoleName='role_name',\n            PolicyName='role_name',\n            PolicyDocument=json.dumps({'policy': 'document'}, indent=2)\n        ).returns({})\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        actual = awsclient.create_role(\n            'role_name', {'trust': 'policy'}, {'policy': 'document'})\n        assert actual == arn\n        stubbed_session.verify_stubs()\n\n    def test_create_role_raises_error_on_failure(self, stubbed_session):\n        arn = 'good_arn' * 3\n        role_id = 'abcd' * 4\n        today = datetime.datetime.today()\n        stubbed_session.stub('iam').create_role(\n            RoleName='role_name',\n            AssumeRolePolicyDocument=json.dumps({'trust': 'policy'})\n        ).returns({'Role': {\n            'RoleName': 'No', 'Arn': arn, 'Path': '/',\n            'RoleId': role_id, 'CreateDate': today}}\n        )\n        stubbed_session.stub('iam').put_role_policy(\n            RoleName='role_name',\n            PolicyName='role_name',\n            PolicyDocument={'policy': 'document'}\n        ).raises_error(\n            error_code='MalformedPolicyDocumentException',\n            message='MalformedPolicyDocument'\n        )\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        with pytest.raises(botocore.exceptions.ClientError):\n            awsclient.create_role(\n                'role_name', {'trust': 'policy'}, {'policy': 'document'})\n        stubbed_session.verify_stubs()\n\n\nclass TestInvokeLambdaFunction(object):\n    def test_invoke_no_payload_no_context(self, stubbed_session):\n        stubbed_session.stub('lambda').invoke(\n            FunctionName='name',\n            InvocationType='RequestResponse',\n        ).returns({})\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.invoke_function('name') == {}\n        stubbed_session.verify_stubs()\n\n    def test_invoke_payload_provided(self, stubbed_session):\n        stubbed_session.stub('lambda').invoke(\n            FunctionName='name',\n            Payload=b'payload',\n            InvocationType='RequestResponse',\n        ).returns({})\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.invoke_function('name', payload=b'payload') == {}\n        stubbed_session.verify_stubs()\n\n    def test_invoke_read_timeout_raises_correct_error(self, stubbed_session):\n        stubbed_session.stub('lambda').invoke(\n            FunctionName='name',\n            Payload=b'payload',\n            InvocationType='RequestResponse',\n        ).raises_error(error=RequestsReadTimeout())\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        with pytest.raises(ReadTimeout):\n            awsclient.invoke_function('name', payload=b'payload') == {}\n\n\nclass TestCreateLambdaFunction(object):\n\n    SUCCESS_RESPONSE = {\n        'FunctionArn': 'arn:12345:name',\n        'State': 'Active',\n    }\n\n    def test_create_function_succeeds_first_try(self, stubbed_session):\n        stubbed_session.stub('lambda').create_function(\n            FunctionName='name',\n            Runtime='python2.7',\n            Code={'ZipFile': b'foo'},\n            Handler='app.app',\n            Role='myarn'\n        ).returns(self.SUCCESS_RESPONSE)\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.create_function(\n            'name', 'myarn', b'foo',\n            'python2.7', 'app.app') == 'arn:12345:name'\n        stubbed_session.verify_stubs()\n\n    def test_create_function_with_non_python2_runtime(self, stubbed_session):\n        stubbed_session.stub('lambda').create_function(\n            FunctionName='name',\n            Runtime='python3.6',\n            Code={'ZipFile': b'foo'},\n            Handler='app.app',\n            Role='myarn',\n        ).returns(self.SUCCESS_RESPONSE)\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.create_function(\n            'name', 'myarn', b'foo', runtime='python3.6',\n            handler='app.app') == 'arn:12345:name'\n        stubbed_session.verify_stubs()\n\n    def test_create_function_wait_for_active_state(self, stubbed_session,\n                                                   monkeypatch):\n        client = stubbed_session.stub('lambda')\n        client.create_function(\n            FunctionName='name',\n            Runtime='python2.7',\n            Code={'ZipFile': b'foo'},\n            Handler='app.app',\n            Role='myarn'\n        ).returns({'FunctionArn': 'arn:12345:name', 'State': 'Pending'})\n        client.get_function_configuration(\n            FunctionName='name',\n        ).returns({'State': 'Pending'})\n        client.get_function_configuration(\n            FunctionName='name',\n        ).returns({'State': 'Active'})\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        monkeypatch.setattr(time, 'sleep', mock.Mock(spec=time.sleep))\n        assert awsclient.create_function(\n            'name', 'myarn', b'foo',\n            'python2.7', 'app.app') == 'arn:12345:name'\n        stubbed_session.verify_stubs()\n\n    def test_create_function_with_environment_variables(self, stubbed_session):\n        stubbed_session.stub('lambda').create_function(\n            FunctionName='name',\n            Runtime='python2.7',\n            Code={'ZipFile': b'foo'},\n            Handler='app.app',\n            Role='myarn',\n            Environment={'Variables': {'FOO': 'BAR'}}\n        ).returns(self.SUCCESS_RESPONSE)\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.create_function(\n            'name', 'myarn', b'foo', 'python2.7',\n            handler='app.app',\n            environment_variables={'FOO': 'BAR'}) == 'arn:12345:name'\n        stubbed_session.verify_stubs()\n\n    def test_create_function_with_tags(self, stubbed_session):\n        stubbed_session.stub('lambda').create_function(\n            FunctionName='name',\n            Runtime='python2.7',\n            Code={'ZipFile': b'foo'},\n            Handler='app.app',\n            Role='myarn',\n            Timeout=240\n        ).returns(self.SUCCESS_RESPONSE)\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.create_function(\n            'name', 'myarn', b'foo', 'python2.7', 'app.app',\n            timeout=240) == 'arn:12345:name'\n        stubbed_session.verify_stubs()\n\n    def test_create_function_with_timeout(self, stubbed_session):\n        stubbed_session.stub('lambda').create_function(\n            FunctionName='name',\n            Runtime='python2.7',\n            Code={'ZipFile': b'foo'},\n            Handler='app.app',\n            Role='myarn',\n            Tags={'mykey': 'myvalue'}\n        ).returns(self.SUCCESS_RESPONSE)\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.create_function(\n            'name', 'myarn', b'foo', 'python2.7', 'app.app',\n            tags={'mykey': 'myvalue'}) == 'arn:12345:name'\n        stubbed_session.verify_stubs()\n\n    def test_create_function_with_memory_size(self, stubbed_session):\n        stubbed_session.stub('lambda').create_function(\n            FunctionName='name',\n            Runtime='python2.7',\n            Code={'ZipFile': b'foo'},\n            Handler='app.app',\n            Role='myarn',\n            MemorySize=256\n        ).returns(self.SUCCESS_RESPONSE)\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.create_function(\n            'name', 'myarn', b'foo', 'python2.7', 'app.app',\n            memory_size=256) == 'arn:12345:name'\n        stubbed_session.verify_stubs()\n\n    def test_create_function_with_vpc_config(self, stubbed_session):\n        stubbed_session.stub('lambda').create_function(\n            FunctionName='name',\n            Runtime='python2.7',\n            Code={'ZipFile': b'foo'},\n            Handler='app.app',\n            Role='myarn',\n            VpcConfig={\n                'SecurityGroupIds': ['sg1', 'sg2'],\n                'SubnetIds': ['sn1', 'sn2']\n            }\n        ).returns(self.SUCCESS_RESPONSE)\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.create_function(\n            'name', 'myarn', b'foo', 'python2.7', 'app.app',\n            subnet_ids=['sn1', 'sn2'],\n            security_group_ids=['sg1', 'sg2'],\n            ) == 'arn:12345:name'\n        stubbed_session.verify_stubs()\n\n    def test_create_function_with_layers(self, stubbed_session):\n        layers = ['arn:aws:lambda:us-east-1:111:layer:test_layer:1']\n        stubbed_session.stub('lambda').create_function(\n            FunctionName='name',\n            Runtime='python2.7',\n            Code={'ZipFile': b'foo'},\n            Handler='app.app',\n            Role='myarn',\n            Layers=layers\n        ).returns(self.SUCCESS_RESPONSE)\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.create_function(\n            'name', 'myarn', b'foo', 'python2.7', 'app.app',\n            layers=layers\n        ) == 'arn:12345:name'\n        stubbed_session.verify_stubs()\n\n    def test_create_function_is_retried_and_succeeds(self, stubbed_session):\n        kwargs = {\n            'FunctionName': 'name',\n            'Runtime': 'python2.7',\n            'Code': {'ZipFile': b'foo'},\n            'Handler': 'app.app',\n            'Role': 'myarn',\n        }\n        stubbed_session.stub('lambda').create_function(\n            **kwargs).raises_error(\n            error_code='InvalidParameterValueException',\n            message=('The role defined for the function cannot '\n                     'be assumed by Lambda.'))\n        stubbed_session.stub('lambda').create_function(\n            **kwargs).raises_error(\n            error_code='InvalidParameterValueException',\n            message=('The role defined for the function cannot '\n                     'be assumed by Lambda.'))\n        stubbed_session.stub('lambda').create_function(\n            **kwargs).returns(self.SUCCESS_RESPONSE)\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n        assert awsclient.create_function(\n            'name', 'myarn', b'foo',\n            'python2.7', 'app.app') == 'arn:12345:name'\n        stubbed_session.verify_stubs()\n\n    def test_create_function_retries_on_kms_errors(self, stubbed_session):\n        # You'll sometimes get this message when you first create a role.\n        # We want to ensure that we're trying when this happens.\n        error_code = 'InvalidParameterValueException'\n        error_message = (\n            'Lambda was unable to configure access to your '\n            'environment variables because the KMS key '\n            'is invalid for CreateGrant. Please '\n            'check your KMS key settings. '\n            'KMS Exception: InvalidArnException KMS Message: '\n            'ARN does not refer to a valid principal'\n        )\n        kwargs = {\n            'FunctionName': 'name',\n            'Runtime': 'python2.7',\n            'Code': {'ZipFile': b'foo'},\n            'Handler': 'app.app',\n            'Role': 'myarn',\n        }\n        client = stubbed_session.stub('lambda')\n        client.create_function(**kwargs).raises_error(\n            error_code=error_code,\n            message=error_message\n        )\n        client.create_function(**kwargs).returns(self.SUCCESS_RESPONSE)\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n        assert awsclient.create_function(\n            'name', 'myarn', b'foo',\n            'python2.7', 'app.app') == 'arn:12345:name'\n        stubbed_session.verify_stubs()\n\n    def test_retry_happens_on_insufficient_permissions(self, stubbed_session):\n        # This can happen if we deploy a lambda in a VPC.  Instead of the role\n        # not being able to be assumed, we can instead not have permissions\n        # to modify ENIs.  These can be retried.\n        kwargs = {\n            'FunctionName': 'name',\n            'Runtime': 'python2.7',\n            'Code': {'ZipFile': b'foo'},\n            'Handler': 'app.app',\n            'Role': 'myarn',\n            'VpcConfig': {'SubnetIds': ['sn-1'],\n                          'SecurityGroupIds': ['sg-1']},\n        }\n        stubbed_session.stub('lambda').create_function(\n            **kwargs).raises_error(\n            error_code='InvalidParameterValueException',\n            message=('The provided execution role does not have permissions '\n                     'to call CreateNetworkInterface on EC2 be assumed by '\n                     'Lambda.'))\n        stubbed_session.stub('lambda').create_function(\n            **kwargs).returns(self.SUCCESS_RESPONSE)\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n        assert awsclient.create_function(\n            'name', 'myarn', b'foo',\n            'python2.7', 'app.app', security_group_ids=['sg-1'],\n            subnet_ids=['sn-1']) == 'arn:12345:name'\n        stubbed_session.verify_stubs()\n\n    def test_create_function_fails_after_max_retries(self, stubbed_session):\n        kwargs = {\n            'FunctionName': 'name',\n            'Runtime': 'python2.7',\n            'Code': {'ZipFile': b'foo'},\n            'Handler': 'app.app',\n            'Role': 'myarn',\n        }\n        for _ in range(TypedAWSClient.LAMBDA_CREATE_ATTEMPTS):\n            stubbed_session.stub('lambda').create_function(\n                **kwargs).raises_error(\n                error_code='InvalidParameterValueException',\n                message=('The role defined for the function cannot '\n                         'be assumed by Lambda.')\n                )\n\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n        with pytest.raises(LambdaClientError) as excinfo:\n            awsclient.create_function('name', 'myarn', b'foo', 'python2.7',\n                                      'app.app')\n        assert isinstance(\n            excinfo.value.original_error, botocore.exceptions.ClientError)\n        stubbed_session.verify_stubs()\n\n    def test_can_pass_python_runtime(self, stubbed_session):\n        stubbed_session.stub('lambda').create_function(\n            FunctionName='name',\n            Runtime='python3.6',\n            Code={'ZipFile': b'foo'},\n            Handler='app.app',\n            Role='myarn',\n        ).returns(self.SUCCESS_RESPONSE)\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.create_function(\n            'name', 'myarn', b'foo',\n            runtime='python3.6', handler='app.app') == 'arn:12345:name'\n        stubbed_session.verify_stubs()\n\n    def test_create_function_propagates_unknown_error(self, stubbed_session):\n        kwargs = {\n            'FunctionName': 'name',\n            'Runtime': 'python2.7',\n            'Code': {'ZipFile': b'foo'},\n            'Handler': 'app.app',\n            'Role': 'myarn',\n        }\n        stubbed_session.stub('lambda').create_function(\n            **kwargs).raises_error(\n            error_code='UnknownException', message='')\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n        with pytest.raises(LambdaClientError) as excinfo:\n            awsclient.create_function('name', 'myarn', b'foo', 'pytohn2.7',\n                                      'app.app')\n        assert isinstance(\n            excinfo.value.original_error, botocore.exceptions.ClientError)\n        stubbed_session.verify_stubs()\n\n    def test_can_provide_tags(self, stubbed_session):\n        stubbed_session.stub('lambda').create_function(\n            FunctionName='name',\n            Runtime='python2.7',\n            Code={'ZipFile': b'foo'},\n            Handler='app.app',\n            Role='myarn',\n            Tags={'key': 'value'},\n        ).returns(self.SUCCESS_RESPONSE)\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        assert awsclient.create_function(\n            function_name='name',\n            role_arn='myarn',\n            zip_contents=b'foo',\n            runtime='python2.7',\n            tags={'key': 'value'},\n            handler='app.app') == 'arn:12345:name'\n        stubbed_session.verify_stubs()\n\n    def test_raises_large_deployment_error_for_connection_error(\n            self, stubbed_session):\n        too_large_content = b'a' * 60 * (1024 ** 2)\n        kwargs = {\n            'FunctionName': 'name',\n            'Runtime': 'python2.7',\n            'Code': {'ZipFile': too_large_content},\n            'Handler': 'app.app',\n            'Role': 'myarn',\n        }\n\n        stubbed_session.stub('lambda').create_function(\n            **kwargs).raises_error(error=RequestsConnectionError())\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n        with pytest.raises(DeploymentPackageTooLargeError) as excinfo:\n            awsclient.create_function('name', 'myarn', too_large_content,\n                                      'python2.7', 'app.app')\n        stubbed_session.verify_stubs()\n        assert excinfo.value.context.function_name == 'name'\n        assert excinfo.value.context.client_method_name == 'create_function'\n        assert excinfo.value.context.deployment_size == 60 * (1024 ** 2)\n\n    def test_no_raise_large_deployment_error_when_small_deployment_size(\n            self, stubbed_session):\n        kwargs = {\n            'FunctionName': 'name',\n            'Runtime': 'python2.7',\n            'Code': {'ZipFile': b'foo'},\n            'Handler': 'app.app',\n            'Role': 'myarn',\n        }\n\n        stubbed_session.stub('lambda').create_function(\n            **kwargs).raises_error(error=RequestsConnectionError())\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n        with pytest.raises(LambdaClientError) as excinfo:\n            awsclient.create_function('name', 'myarn', b'foo',\n                                      'python2.7', 'app.app')\n        stubbed_session.verify_stubs()\n        assert not isinstance(excinfo.value, DeploymentPackageTooLargeError)\n        assert isinstance(\n            excinfo.value.original_error, RequestsConnectionError)\n\n    def test_raises_large_deployment_error_request_entity_to_large(\n            self, stubbed_session):\n        kwargs = {\n            'FunctionName': 'name',\n            'Runtime': 'python2.7',\n            'Code': {'ZipFile': b'foo'},\n            'Handler': 'app.app',\n            'Role': 'myarn',\n        }\n        stubbed_session.stub('lambda').create_function(\n            **kwargs).raises_error(\n                error_code='RequestEntityTooLargeException',\n                message='')\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n        with pytest.raises(DeploymentPackageTooLargeError):\n            awsclient.create_function('name', 'myarn', b'foo', 'python2.7',\n                                      'app.app')\n        stubbed_session.verify_stubs()\n\n    def test_raises_large_deployment_error_for_too_large_unzip(\n            self, stubbed_session):\n        kwargs = {\n            'FunctionName': 'name',\n            'Runtime': 'python2.7',\n            'Code': {'ZipFile': b'foo'},\n            'Handler': 'app.app',\n            'Role': 'myarn',\n        }\n        stubbed_session.stub('lambda').create_function(\n            **kwargs).raises_error(\n                error_code='InvalidParameterValueException',\n                message='Unzipped size must be smaller than ...')\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n        with pytest.raises(DeploymentPackageTooLargeError):\n            awsclient.create_function('name', 'myarn', b'foo', 'python2.7',\n                                      'app.app')\n        stubbed_session.verify_stubs()\n\n\nclass TestUpdateLambdaFunction(object):\n\n    SUCCESS_RESPONSE = {\n        'LastUpdateStatus': 'Successful',\n    }\n\n    def test_always_update_function_code(self, stubbed_session):\n        lambda_client = stubbed_session.stub('lambda')\n        lambda_client.update_function_code(\n            FunctionName='name', ZipFile=b'foo').returns(self.SUCCESS_RESPONSE)\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        awsclient.update_function('name', b'foo')\n        stubbed_session.verify_stubs()\n\n    def test_update_function_code_with_runtime(self, stubbed_session):\n        lambda_client = stubbed_session.stub('lambda')\n        lambda_client.update_function_code(\n            FunctionName='name', ZipFile=b'foo').returns(self.SUCCESS_RESPONSE)\n        lambda_client.update_function_configuration(\n            FunctionName='name',\n            Runtime='python3.6').returns(self.SUCCESS_RESPONSE)\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        awsclient.update_function('name', b'foo', runtime='python3.6')\n        stubbed_session.verify_stubs()\n\n    def test_update_function_code_with_environment_vars(self, stubbed_session):\n        lambda_client = stubbed_session.stub('lambda')\n        lambda_client.update_function_code(\n            FunctionName='name', ZipFile=b'foo').returns(self.SUCCESS_RESPONSE)\n        lambda_client.update_function_configuration(\n            FunctionName='name',\n            Environment={'Variables': {\"FOO\": \"BAR\"}}).returns(\n                self.SUCCESS_RESPONSE)\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        awsclient.update_function(\n            'name', b'foo', {\"FOO\": \"BAR\"})\n        stubbed_session.verify_stubs()\n\n    def test_update_function_code_with_timeout(self, stubbed_session):\n        lambda_client = stubbed_session.stub('lambda')\n        lambda_client.update_function_code(\n            FunctionName='name', ZipFile=b'foo').returns(self.SUCCESS_RESPONSE)\n        lambda_client.update_function_configuration(\n            FunctionName='name',\n            Timeout=240).returns(self.SUCCESS_RESPONSE)\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        awsclient.update_function('name', b'foo', timeout=240)\n        stubbed_session.verify_stubs()\n\n    def test_update_function_code_with_memory(self, stubbed_session):\n        lambda_client = stubbed_session.stub('lambda')\n        lambda_client.update_function_code(\n            FunctionName='name', ZipFile=b'foo').returns(self.SUCCESS_RESPONSE)\n        lambda_client.update_function_configuration(\n            FunctionName='name',\n            MemorySize=256).returns(self.SUCCESS_RESPONSE)\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        awsclient.update_function('name', b'foo', memory_size=256)\n        stubbed_session.verify_stubs()\n\n    def test_update_function_with_vpc_config(self, stubbed_session):\n        lambda_client = stubbed_session.stub('lambda')\n        lambda_client.update_function_code(\n            FunctionName='name', ZipFile=b'foo').returns(self.SUCCESS_RESPONSE)\n        lambda_client.update_function_configuration(\n            FunctionName='name', VpcConfig={\n                'SecurityGroupIds': ['sg1', 'sg2'],\n                'SubnetIds': ['sn1', 'sn2']\n            }\n        ).returns(self.SUCCESS_RESPONSE)\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        awsclient.update_function(\n            'name', b'foo',\n            subnet_ids=['sn1', 'sn2'],\n            security_group_ids=['sg1', 'sg2'],\n        )\n        stubbed_session.verify_stubs()\n\n    def test_update_function_with_layers_config(self, stubbed_session):\n        layers = ['arn:aws:lambda:us-east-1:111:layer:test_layer:1']\n        lambda_client = stubbed_session.stub('lambda')\n        lambda_client.update_function_code(\n            FunctionName='name', ZipFile=b'foo').returns(self.SUCCESS_RESPONSE)\n        lambda_client.update_function_configuration(\n            FunctionName='name', Layers=layers\n        ).returns(self.SUCCESS_RESPONSE)\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        awsclient.update_function(\n            'name', b'foo',\n            layers=layers\n        )\n        stubbed_session.verify_stubs()\n\n    def test_update_function_with_adding_tags(self, stubbed_session):\n        function_arn = 'arn'\n\n        lambda_client = stubbed_session.stub('lambda')\n        lambda_client.update_function_code(\n            FunctionName='name', ZipFile=b'foo').returns(\n                {'FunctionArn': function_arn,\n                 'LastUpdateStatus': 'Successful'})\n        lambda_client.list_tags(\n            Resource=function_arn).returns({'Tags': {}})\n        lambda_client.tag_resource(\n            Resource=function_arn, Tags={'MyKey': 'MyValue'}).returns({})\n        stubbed_session.activate_stubs()\n\n        awsclient = TypedAWSClient(stubbed_session)\n        awsclient.update_function('name', b'foo', tags={'MyKey': 'MyValue'})\n        stubbed_session.verify_stubs()\n\n    def test_update_function_with_updating_tags(self, stubbed_session):\n        function_arn = 'arn'\n\n        lambda_client = stubbed_session.stub('lambda')\n        lambda_client.update_function_code(\n            FunctionName='name', ZipFile=b'foo').returns(\n                {'FunctionArn': function_arn,\n                 'LastUpdateStatus': 'Successful'})\n        lambda_client.list_tags(\n            Resource=function_arn).returns({'Tags': {'MyKey': 'MyOrigValue'}})\n        lambda_client.tag_resource(\n            Resource=function_arn, Tags={'MyKey': 'MyNewValue'}).returns({})\n        stubbed_session.activate_stubs()\n\n        awsclient = TypedAWSClient(stubbed_session)\n        awsclient.update_function('name', b'foo', tags={'MyKey': 'MyNewValue'})\n        stubbed_session.verify_stubs()\n\n    def test_update_function_with_removing_tags(self, stubbed_session):\n        function_arn = 'arn'\n\n        lambda_client = stubbed_session.stub('lambda')\n        lambda_client.update_function_code(\n            FunctionName='name', ZipFile=b'foo').returns(\n                {'FunctionArn': function_arn,\n                 'LastUpdateStatus': 'Successful'})\n        lambda_client.list_tags(\n            Resource=function_arn).returns(\n                {'Tags': {'KeyToRemove': 'Value'}})\n        lambda_client.untag_resource(\n            Resource=function_arn, TagKeys=['KeyToRemove']).returns({})\n        stubbed_session.activate_stubs()\n\n        awsclient = TypedAWSClient(stubbed_session)\n        awsclient.update_function('name', b'foo', tags={})\n        stubbed_session.verify_stubs()\n\n    def test_update_function_with_no_tag_updates_needed(self, stubbed_session):\n        function_arn = 'arn'\n\n        lambda_client = stubbed_session.stub('lambda')\n        lambda_client.update_function_code(\n            FunctionName='name', ZipFile=b'foo').returns(\n                {'FunctionArn': function_arn,\n                 'LastUpdateStatus': 'Successful'})\n        lambda_client.list_tags(\n            Resource=function_arn).returns({'Tags': {'MyKey': 'SameValue'}})\n        stubbed_session.activate_stubs()\n\n        awsclient = TypedAWSClient(stubbed_session)\n        awsclient.update_function('name', b'foo', tags={'MyKey': 'SameValue'})\n        stubbed_session.verify_stubs()\n\n    def test_update_function_with_iam_role(self, stubbed_session):\n        function_arn = 'arn'\n\n        lambda_client = stubbed_session.stub('lambda')\n        lambda_client.update_function_code(\n            FunctionName='name', ZipFile=b'foo').returns(\n                {'FunctionArn': function_arn,\n                 'LastUpdateStatus': 'Successful'})\n        lambda_client.update_function_configuration(\n            FunctionName='name',\n            Role='role-arn').returns(self.SUCCESS_RESPONSE)\n        stubbed_session.activate_stubs()\n\n        awsclient = TypedAWSClient(stubbed_session)\n        awsclient.update_function('name', b'foo', role_arn='role-arn')\n        stubbed_session.verify_stubs()\n\n    def test_update_function_is_retried_and_succeeds(self, stubbed_session):\n        stubbed_session.stub('lambda').update_function_code(\n            FunctionName='name', ZipFile=b'foo').returns(\n                {'FunctionArn': 'arn', 'LastUpdateStatus': 'Successful'})\n\n        update_config_kwargs = {\n            'FunctionName': 'name',\n            'Role': 'role-arn'\n        }\n        # This should fail two times with retryable exceptions and\n        # then succeed to update the lambda function.\n        stubbed_session.stub('lambda').update_function_configuration(\n            **update_config_kwargs).raises_error(\n                error_code='InvalidParameterValueException',\n                message=('The role defined for the function cannot '\n                         'be assumed by Lambda.'))\n        stubbed_session.stub('lambda').update_function_configuration(\n            **update_config_kwargs).raises_error(\n            error_code='InvalidParameterValueException',\n            message=('The role defined for the function cannot '\n                     'be assumed by Lambda.'))\n        stubbed_session.stub('lambda').update_function_configuration(\n            **update_config_kwargs).returns(self.SUCCESS_RESPONSE)\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n        awsclient.update_function('name', b'foo', role_arn='role-arn')\n        stubbed_session.verify_stubs()\n\n    def test_update_function_fails_after_max_retries(self, stubbed_session):\n        stubbed_session.stub('lambda').update_function_code(\n            FunctionName='name', ZipFile=b'foo').returns(self.SUCCESS_RESPONSE)\n\n        update_config_kwargs = {\n            'FunctionName': 'name',\n            'Role': 'role-arn'\n        }\n        for _ in range(TypedAWSClient.LAMBDA_CREATE_ATTEMPTS):\n            stubbed_session.stub('lambda').update_function_configuration(\n                **update_config_kwargs).raises_error(\n                    error_code='InvalidParameterValueException',\n                    message=('The role defined for the function cannot '\n                             'be assumed by Lambda.'))\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n\n        with pytest.raises(botocore.exceptions.ClientError):\n            awsclient.update_function('name', b'foo', role_arn='role-arn')\n        stubbed_session.verify_stubs()\n\n    def test_raises_large_deployment_error_for_connection_error(\n            self, stubbed_session):\n        too_large_content = b'a' * 60 * (1024 ** 2)\n        stubbed_session.stub('lambda').update_function_code(\n            FunctionName='name', ZipFile=too_large_content).raises_error(\n                error=RequestsConnectionError())\n\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n        with pytest.raises(DeploymentPackageTooLargeError) as excinfo:\n            awsclient.update_function('name', too_large_content)\n        stubbed_session.verify_stubs()\n        assert excinfo.value.context.function_name == 'name'\n        assert (\n            excinfo.value.context.client_method_name == 'update_function_code')\n        assert excinfo.value.context.deployment_size == 60 * (1024 ** 2)\n\n    def test_no_raise_large_deployment_error_when_small_deployment_size(\n            self, stubbed_session):\n        stubbed_session.stub('lambda').update_function_code(\n            FunctionName='name', ZipFile=b'foo').raises_error(\n                error=RequestsConnectionError())\n\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n        with pytest.raises(LambdaClientError) as excinfo:\n            awsclient.update_function('name', b'foo')\n        stubbed_session.verify_stubs()\n        assert not isinstance(excinfo.value, DeploymentPackageTooLargeError)\n        assert isinstance(\n            excinfo.value.original_error, RequestsConnectionError)\n\n    def test_raises_large_deployment_error_request_entity_to_large(\n            self, stubbed_session):\n        stubbed_session.stub('lambda').update_function_code(\n            FunctionName='name', ZipFile=b'foo').raises_error(\n                error_code='RequestEntityTooLargeException',\n                message='')\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n        with pytest.raises(DeploymentPackageTooLargeError):\n            awsclient.update_function('name', b'foo')\n        stubbed_session.verify_stubs()\n\n    def test_raises_large_deployment_error_for_too_large_unzip(\n            self, stubbed_session):\n        stubbed_session.stub('lambda').update_function_code(\n            FunctionName='name', ZipFile=b'foo').raises_error(\n                error_code='InvalidParameterValueException',\n                message='Unzipped size must be smaller than ...')\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n        with pytest.raises(DeploymentPackageTooLargeError):\n            awsclient.update_function('name', b'foo')\n        stubbed_session.verify_stubs()\n\n    def test_update_function_waits_for_active(self, stubbed_session,\n                                              monkeypatch):\n        lambda_client = stubbed_session.stub('lambda')\n        lambda_client.update_function_code(\n            FunctionName='name', ZipFile=b'foo').returns({\n                'LastUpdateStatus': 'InProgress',\n            })\n        lambda_client.get_function_configuration(\n            FunctionName='name',\n        ).returns({'LastUpdateStatus': 'InProgress'})\n        lambda_client.get_function_configuration(\n            FunctionName='name',\n        ).returns({'LastUpdateStatus': 'Successful'})\n        stubbed_session.activate_stubs()\n        monkeypatch.setattr(time, 'sleep', mock.Mock(spec=time.sleep))\n        awsclient = TypedAWSClient(stubbed_session)\n        awsclient.update_function('name', b'foo')\n        stubbed_session.verify_stubs()\n\n    def test_update_function_config_waits_for_active(self, stubbed_session,\n                                                     monkeypatch):\n        lambda_client = stubbed_session.stub('lambda')\n        lambda_client.update_function_code(\n            FunctionName='name', ZipFile=b'foo').returns(self.SUCCESS_RESPONSE)\n        lambda_client.update_function_configuration(\n            FunctionName='name',\n            Role='role-arn').returns({'LastUpdateStatus': 'InProgress'})\n\n        lambda_client.get_function_configuration(\n            FunctionName='name',\n        ).returns({'LastUpdateStatus': 'InProgress'})\n        lambda_client.get_function_configuration(\n            FunctionName='name',\n        ).returns({'LastUpdateStatus': 'Successful'})\n        stubbed_session.activate_stubs()\n        monkeypatch.setattr(time, 'sleep', mock.Mock(spec=time.sleep))\n\n        awsclient = TypedAWSClient(stubbed_session)\n        awsclient.update_function('name', b'foo', role_arn='role-arn')\n        stubbed_session.verify_stubs()\n\n\nclass TestPutFunctionConcurrency(object):\n    def test_put_function_concurrency(self, stubbed_session):\n        lambda_client = stubbed_session.stub('lambda')\n        lambda_client.put_function_concurrency(\n            FunctionName='name', ReservedConcurrentExecutions=5).returns({})\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        awsclient.put_function_concurrency('name', 5)\n        stubbed_session.verify_stubs()\n\n\nclass TestDeleteFunctionConcurrency(object):\n    def test_delete_function_concurrency(self, stubbed_session):\n        lambda_client = stubbed_session.stub('lambda')\n        lambda_client.delete_function_concurrency(\n            FunctionName='name').returns({})\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        awsclient.delete_function_concurrency('name')\n        stubbed_session.verify_stubs()\n\n\nclass TestCanDeleteRolePolicy(object):\n    def test_can_delete_role_policy(self, stubbed_session):\n        stubbed_session.stub('iam').delete_role_policy(\n            RoleName='myrole', PolicyName='mypolicy'\n        ).returns({})\n        stubbed_session.activate_stubs()\n        awsclient = TypedAWSClient(stubbed_session)\n        awsclient.delete_role_policy('myrole', 'mypolicy')\n        stubbed_session.verify_stubs()\n\n\nclass TestCanDeleteRole(object):\n    def test_can_delete_role(self, stubbed_session):\n        stubbed_session.stub('iam').list_role_policies(\n            RoleName='myrole').returns({\n                'PolicyNames': ['mypolicy']\n            })\n        stubbed_session.stub('iam').delete_role_policy(\n            RoleName='myrole',\n            PolicyName='mypolicy').returns({})\n        stubbed_session.stub('iam').delete_role(\n            RoleName='myrole'\n        ).returns({})\n        stubbed_session.activate_stubs()\n\n        awsclient = TypedAWSClient(stubbed_session)\n        awsclient.delete_role('myrole')\n        stubbed_session.verify_stubs()\n\n\nclass TestAddPermissionsForAPIGateway(object):\n    def should_call_add_permission(self, lambda_stub,\n                                   statement_id=stub.ANY):\n        lambda_stub.add_permission(\n            Action='lambda:InvokeFunction',\n            FunctionName='name',\n            StatementId=statement_id,\n            Principal='apigateway.amazonaws.com',\n            SourceArn='arn:aws:execute-api:us-west-2:123:rest-api-id/*',\n        ).returns({})\n\n    def test_can_add_permission_for_apigateway_needed(self, stubbed_session):\n        # An empty policy means we need to add permissions.\n        lambda_stub = stubbed_session.stub('lambda')\n        lambda_stub.get_policy(FunctionName='name').returns({'Policy': '{}'})\n        self.should_call_add_permission(lambda_stub)\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        client.add_permission_for_apigateway(\n            'name', 'us-west-2', '123', 'rest-api-id')\n        stubbed_session.verify_stubs()\n\n    def test_can_add_permission_random_id_optional(self, stubbed_session):\n        lambda_stub = stubbed_session.stub('lambda')\n        lambda_stub.get_policy(FunctionName='name').returns({'Policy': '{}'})\n        self.should_call_add_permission(lambda_stub)\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        client.add_permission_for_apigateway(\n            'name', 'us-west-2', '123', 'rest-api-id')\n        stubbed_session.verify_stubs()\n\n    def test_can_add_permission_for_apigateway_not_needed(self,\n                                                          stubbed_session):\n        source_arn = 'arn:aws:execute-api:us-west-2:123:rest-api-id/*'\n        wrong_action = {\n            'Action': 'lambda:NotInvoke',\n            'Condition': {\n                'ArnLike': {\n                    'AWS:SourceArn': source_arn,\n                }\n            },\n            'Effect': 'Allow',\n            'Principal': {'Service': 'apigateway.amazonaws.com'},\n            'Resource': 'arn:aws:lambda:us-west-2:account_id:function:name',\n            'Sid': 'e4755709-067e-4254-b6ec-e7f9639e6f7b',\n        }\n        wrong_service_name = {\n            'Action': 'lambda:Invoke',\n            'Condition': {\n                'ArnLike': {\n                    'AWS:SourceArn': source_arn,\n                }\n            },\n            'Effect': 'Allow',\n            'Principal': {'Service': 'NOT-apigateway.amazonaws.com'},\n            'Resource': 'arn:aws:lambda:us-west-2:account_id:function:name',\n            'Sid': 'e4755709-067e-4254-b6ec-e7f9639e6f7b',\n        }\n        correct_statement = {\n            'Action': 'lambda:InvokeFunction',\n            'Condition': {\n                'ArnLike': {\n                    'AWS:SourceArn': source_arn,\n                }\n            },\n            'Effect': 'Allow',\n            'Principal': {'Service': 'apigateway.amazonaws.com'},\n            'Resource': 'arn:aws:lambda:us-west-2:account_id:function:name',\n            'Sid': 'e4755709-067e-4254-b6ec-e7f9639e6f7b',\n        }\n        policy = {\n            'Id': 'default',\n            'Statement': [\n                wrong_action,\n                wrong_service_name,\n                correct_statement,\n            ],\n            'Version': '2012-10-17'\n        }\n        stubbed_session.stub('lambda').get_policy(\n            FunctionName='name').returns({'Policy': json.dumps(policy)})\n\n        # Because the policy above indicates that API gateway already has the\n        # necessary permissions, we should not call add_permission.\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        client.add_permission_for_apigateway(\n            'name', 'us-west-2', '123', 'rest-api-id')\n        stubbed_session.verify_stubs()\n\n    def test_can_add_permission_when_policy_does_not_exist(self,\n                                                           stubbed_session):\n        # It's also possible to receive a ResourceNotFoundException\n        # if you call get_policy() on a lambda function with no policy.\n        lambda_stub = stubbed_session.stub('lambda')\n        lambda_stub.get_policy(FunctionName='name').raises_error(\n            error_code='ResourceNotFoundException', message='Does not exist.')\n        self.should_call_add_permission(lambda_stub)\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        client.add_permission_for_apigateway(\n            'name', 'us-west-2', '123', 'rest-api-id', 'random-id')\n        stubbed_session.verify_stubs()\n\n\nclass TestAddPermissionsForAPIGatewayV2(object):\n    def should_call_add_permission(self, lambda_stub,\n                                   statement_id=stub.ANY):\n        lambda_stub.add_permission(\n            Action='lambda:InvokeFunction',\n            FunctionName='name',\n            StatementId=statement_id,\n            Principal='apigateway.amazonaws.com',\n            SourceArn='arn:aws:execute-api:us-west-2:123:websocket-api-id/*',\n        ).returns({})\n\n    def test_can_add_permission_for_apigateway_v2_needed(self,\n                                                         stubbed_session):\n        # An empty policy means we need to add permissions.\n        lambda_stub = stubbed_session.stub('lambda')\n        lambda_stub.get_policy(FunctionName='name').returns({'Policy': '{}'})\n        self.should_call_add_permission(lambda_stub)\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        client.add_permission_for_apigateway_v2(\n            'name', 'us-west-2', '123', 'websocket-api-id')\n        stubbed_session.verify_stubs()\n\n    def test_can_add_permission_random_id_optional(self, stubbed_session):\n        lambda_stub = stubbed_session.stub('lambda')\n        lambda_stub.get_policy(FunctionName='name').returns({'Policy': '{}'})\n        self.should_call_add_permission(lambda_stub)\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        client.add_permission_for_apigateway_v2(\n            'name', 'us-west-2', '123', 'websocket-api-id')\n        stubbed_session.verify_stubs()\n\n    def test_can_add_permission_for_apigateway_v2_not_needed(self,\n                                                             stubbed_session):\n        source_arn = 'arn:aws:execute-api:us-west-2:123:websocket-api-id/*'\n        wrong_action = {\n            'Action': 'lambda:NotInvoke',\n            'Condition': {\n                'ArnLike': {\n                    'AWS:SourceArn': source_arn,\n                }\n            },\n            'Effect': 'Allow',\n            'Principal': {'Service': 'apigateway.amazonaws.com'},\n            'Resource': 'arn:aws:lambda:us-west-2:account_id:function:name',\n            'Sid': 'e4755709-067e-4254-b6ec-e7f9639e6f7b',\n        }\n        wrong_service_name = {\n            'Action': 'lambda:Invoke',\n            'Condition': {\n                'ArnLike': {\n                    'AWS:SourceArn': source_arn,\n                }\n            },\n            'Effect': 'Allow',\n            'Principal': {'Service': 'NOT-apigateway.amazonaws.com'},\n            'Resource': 'arn:aws:lambda:us-west-2:account_id:function:name',\n            'Sid': 'e4755709-067e-4254-b6ec-e7f9639e6f7b',\n        }\n        correct_statement = {\n            'Action': 'lambda:InvokeFunction',\n            'Condition': {\n                'ArnLike': {\n                    'AWS:SourceArn': source_arn,\n                }\n            },\n            'Effect': 'Allow',\n            'Principal': {'Service': 'apigateway.amazonaws.com'},\n            'Resource': 'arn:aws:lambda:us-west-2:account_id:function:name',\n            'Sid': 'e4755709-067e-4254-b6ec-e7f9639e6f7b',\n        }\n        policy = {\n            'Id': 'default',\n            'Statement': [\n                wrong_action,\n                wrong_service_name,\n                correct_statement,\n            ],\n            'Version': '2012-10-17'\n        }\n        stubbed_session.stub('lambda').get_policy(\n            FunctionName='name').returns({'Policy': json.dumps(policy)})\n\n        # Because the policy above indicates that API gateway already has the\n        # necessary permissions, we should not call add_permission.\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        client.add_permission_for_apigateway(\n            'name', 'us-west-2', '123', 'websocket-api-id')\n        stubbed_session.verify_stubs()\n\n    def test_can_add_permission_when_policy_does_not_exist(self,\n                                                           stubbed_session):\n        # It's also possible to receive a ResourceNotFoundException\n        # if you call get_policy() on a lambda function with no policy.\n        lambda_stub = stubbed_session.stub('lambda')\n        lambda_stub.get_policy(FunctionName='name').raises_error(\n            error_code='ResourceNotFoundException', message='Does not exist.')\n        self.should_call_add_permission(lambda_stub)\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        client.add_permission_for_apigateway_v2(\n            'name', 'us-west-2', '123', 'websocket-api-id', 'random-id')\n        stubbed_session.verify_stubs()\n\n\nclass TestWebsocketAPI(object):\n    def test_can_create_websocket_api(self, stubbed_session):\n        stubbed_session.stub('apigatewayv2').create_api(\n            Name='name',\n            ProtocolType='WEBSOCKET',\n            RouteSelectionExpression='$request.body.action',\n        ).returns({'ApiId': 'id'})\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        api_id = client.create_websocket_api('name')\n        stubbed_session.verify_stubs()\n        assert api_id == 'id'\n\n    def test_can_get_websocket_api(self, stubbed_session):\n        stubbed_session.stub('apigatewayv2').get_apis(\n        ).returns({\n            'Items': [\n                {'Name': 'some-other-api',\n                 'ApiId': 'foo bar',\n                 'RouteSelectionExpression': 'unused',\n                 'ProtocolType': 'WEBSOCKET'},\n                {'Name': 'target-api',\n                 'ApiId': 'id',\n                 'RouteSelectionExpression': 'unused',\n                 'ProtocolType': 'WEBSOCKET'},\n            ],\n        })\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        api_id = client.get_websocket_api_id('target-api')\n        stubbed_session.verify_stubs()\n        assert api_id == 'id'\n\n    def test_does_return_none_on_websocket_api_missing(self, stubbed_session):\n        stubbed_session.stub('apigatewayv2').get_apis(\n        ).returns({\n            'Items': [],\n        })\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        api_id = client.get_websocket_api_id('target-api')\n        stubbed_session.verify_stubs()\n        assert api_id is None\n\n    def test_can_check_get_websocket_api_exists(self, stubbed_session):\n        stubbed_session.stub('apigatewayv2').get_api(\n            ApiId='api-id',\n        ).returns({})\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        exists = client.websocket_api_exists('api-id')\n        stubbed_session.verify_stubs()\n        assert exists is True\n\n    def test_can_check_get_websocket_api_not_exists(self, stubbed_session):\n        stubbed_session.stub('apigatewayv2').get_api(\n            ApiId='api-id',\n        ).raises_error(\n            error_code='NotFoundException',\n            message='Does not exists.',\n        )\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        exists = client.websocket_api_exists('api-id')\n        stubbed_session.verify_stubs()\n        assert exists is False\n\n    def test_can_delete_websocket_api(self, stubbed_session):\n        stubbed_session.stub('apigatewayv2').delete_api(\n            ApiId='id',\n        ).returns({})\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        client.delete_websocket_api('id')\n        stubbed_session.verify_stubs()\n\n    def test_rest_api_delete_already_deleted(self, stubbed_session):\n        stubbed_session.stub('apigatewayv2')\\\n                       .delete_api(ApiId='name')\\\n                       .raises_error(error_code='NotFoundException',\n                                     message='Unknown')\n        stubbed_session.activate_stubs()\n\n        awsclient = TypedAWSClient(stubbed_session)\n        with pytest.raises(ResourceDoesNotExistError):\n            assert awsclient.delete_websocket_api('name')\n\n    def test_can_create_integration(self, stubbed_session):\n        stubbed_session.stub('apigatewayv2').create_integration(\n            ApiId='api-id',\n            ConnectionType='INTERNET',\n            ContentHandlingStrategy='CONVERT_TO_TEXT',\n            Description='connect',\n            IntegrationType='AWS_PROXY',\n            IntegrationUri='arn:aws:lambda',\n        ).returns({'IntegrationId': 'integration-id'})\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        integration_id = client.create_websocket_integration(\n            api_id='api-id',\n            lambda_function='arn:aws:lambda',\n            handler_type='connect',\n        )\n        stubbed_session.verify_stubs()\n        assert integration_id == 'integration-id'\n\n    def test_can_create_route(self, stubbed_session):\n        stubbed_session.stub('apigatewayv2').create_route(\n            ApiId='api-id',\n            RouteKey='route-key',\n            RouteResponseSelectionExpression='$default',\n            Target='integrations/integration-id',\n        ).returns({})\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        client.create_websocket_route(\n            api_id='api-id',\n            route_key='route-key',\n            integration_id='integration-id',\n        )\n        stubbed_session.verify_stubs()\n\n    def test_can_delete_all_websocket_routes(self, stubbed_session):\n        stubbed_session.stub('apigatewayv2').delete_route(\n            ApiId='api-id',\n            RouteId='route-id',\n        ).returns({})\n        stubbed_session.stub('apigatewayv2').delete_route(\n            ApiId='api-id',\n            RouteId='old-route-id',\n        ).returns({})\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        client.delete_websocket_routes(\n            api_id='api-id',\n            routes=['route-id', 'old-route-id'],\n        )\n        stubbed_session.verify_stubs()\n\n    def test_can_delete_all_websocket_integrations(self, stubbed_session):\n        stubbed_session.stub('apigatewayv2').delete_integration(\n            ApiId='api-id',\n            IntegrationId='integration-id',\n        ).returns({})\n        stubbed_session.stub('apigatewayv2').delete_integration(\n            ApiId='api-id',\n            IntegrationId='old-integration-id',\n        ).returns({})\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        client.delete_websocket_integrations(\n            api_id='api-id',\n            integrations=['integration-id', 'old-integration-id'],\n        )\n        stubbed_session.verify_stubs()\n\n    def test_can_deploy_websocket_api(self, stubbed_session):\n        stubbed_session.stub('apigatewayv2').create_deployment(\n            ApiId='api-id',\n        ).returns({'DeploymentId': 'deployment-id'})\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        deployment_id = client.deploy_websocket_api(\n            api_id='api-id',\n        )\n        stubbed_session.verify_stubs()\n        assert deployment_id == 'deployment-id'\n\n    def test_can_get_routes(self, stubbed_session):\n        stubbed_session.stub('apigatewayv2').get_routes(\n            ApiId='api-id',\n        ).returns(\n            {\n                'Items': [\n                    {'RouteKey': 'route-key-foo',\n                     'RouteId': 'route-id-foo'},\n                    {'RouteKey': 'route-key-bar',\n                     'RouteId': 'route-id-bar'},\n                ],\n            }\n        )\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        routes = client.get_websocket_routes(\n            api_id='api-id',\n        )\n        stubbed_session.verify_stubs()\n        assert routes == ['route-id-foo', 'route-id-bar']\n\n    def test_can_get_integrations(self, stubbed_session):\n        stubbed_session.stub('apigatewayv2').get_integrations(\n            ApiId='api-id',\n        ).returns(\n            {\n                'Items': [\n                    {\n                        'Description': 'connect',\n                        'IntegrationId': 'connect-integration-id'\n                    },\n                    {\n                        'Description': 'message',\n                        'IntegrationId': 'message-integration-id'\n                    },\n                    {\n                        'Description': 'disconnect',\n                        'IntegrationId': 'disconnect-integration-id'\n                    },\n                ]\n            }\n        )\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        integration_ids = client.get_websocket_integrations(\n            api_id='api-id',\n        )\n        stubbed_session.verify_stubs()\n        assert integration_ids == [\n            'connect-integration-id',\n            'message-integration-id',\n            'disconnect-integration-id',\n        ]\n\n    def test_can_create_stage(self, stubbed_session):\n        stubbed_session.stub('apigatewayv2').create_stage(\n            ApiId='api-id',\n            StageName='stage-name',\n            DeploymentId='deployment-id',\n        ).returns({})\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        client.create_stage(\n            api_id='api-id',\n            stage_name='stage-name',\n            deployment_id='deployment-id',\n        )\n        stubbed_session.verify_stubs()\n\n\nclass TestAddPermissionsForAuthorizer(object):\n\n    FUNCTION_ARN = (\n        'arn:aws:lambda:us-west-2:1:function:app-dev-name'\n    )\n    GOOD_ARN = (\n        'arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/'\n        '%s/invocations' % FUNCTION_ARN\n    )\n\n    def test_can_add_permission_for_authorizer(self, stubbed_session):\n        apigateway = stubbed_session.stub('apigateway')\n        apigateway.get_authorizers(restApiId='rest-api-id').returns({\n            'items': [\n                {'authorizerUri': 'not:arn', 'id': 'bad'},\n                {'authorizerUri': self.GOOD_ARN, 'id': 'good'},\n            ]\n        })\n        source_arn = (\n            'arn:aws:execute-api:us-west-2:1:rest-api-id/authorizers/good'\n        )\n        # We should call the appropriate add_permission call.\n        lambda_client = stubbed_session.stub('lambda')\n        lambda_client.add_permission(\n            Action='lambda:InvokeFunction',\n            FunctionName='app-dev-name',\n            StatementId='random-id',\n            Principal='apigateway.amazonaws.com',\n            SourceArn=source_arn\n        ).returns({})\n        stubbed_session.activate_stubs()\n\n        TypedAWSClient(stubbed_session).add_permission_for_authorizer(\n            'rest-api-id', self.FUNCTION_ARN, 'random-id'\n        )\n        stubbed_session.verify_stubs()\n\n    def test_random_id_can_be_omitted(self, stubbed_session):\n        stubbed_session.stub('apigateway').get_authorizers(\n            restApiId='rest-api-id').returns({\n                'items': [{'authorizerUri': self.GOOD_ARN, 'id': 'good'}]})\n        source_arn = (\n            'arn:aws:execute-api:us-west-2:1:rest-api-id/authorizers/good'\n        )\n        stubbed_session.stub('lambda').add_permission(\n            Action='lambda:InvokeFunction',\n            FunctionName='app-dev-name',\n            # Autogenerated value here.\n            StatementId=stub.ANY,\n            Principal='apigateway.amazonaws.com',\n            SourceArn=source_arn\n        ).returns({})\n        stubbed_session.activate_stubs()\n        # Note the omission of the random id.\n        TypedAWSClient(stubbed_session).add_permission_for_authorizer(\n            'rest-api-id', self.FUNCTION_ARN\n        )\n        stubbed_session.verify_stubs()\n\n    def test_value_error_raised_for_unknown_function(self, stubbed_session):\n        apigateway = stubbed_session.stub('apigateway')\n        apigateway.get_authorizers(restApiId='rest-api-id').returns({\n            'items': [\n                {'authorizerUri': 'not:arn', 'id': 'bad'},\n                {'authorizerUri': 'also-not:arn', 'id': 'alsobad'},\n            ]\n        })\n        stubbed_session.activate_stubs()\n\n        unknown_function_arn = 'function:arn'\n        with pytest.raises(ResourceDoesNotExistError):\n            TypedAWSClient(stubbed_session).add_permission_for_authorizer(\n                'rest-api-id', unknown_function_arn, 'random-id'\n            )\n        stubbed_session.verify_stubs()\n\n\ndef test_get_sdk(stubbed_session):\n    apig = stubbed_session.stub('apigateway')\n    apig.get_sdk(\n        restApiId='rest-api-id',\n        stageName='dev',\n        sdkType='javascript').returns({'body': 'foo'})\n    stubbed_session.activate_stubs()\n    awsclient = TypedAWSClient(stubbed_session)\n    response = awsclient.get_sdk_download_stream(\n        'rest-api-id', 'dev', 'javascript')\n    stubbed_session.verify_stubs()\n    assert response == 'foo'\n\n\ndef test_import_rest_api(stubbed_session):\n    apig = stubbed_session.stub('apigateway')\n    swagger_doc = {'swagger': 'doc'}\n    apig.import_rest_api(\n        parameters={'endpointConfigurationTypes': 'EDGE'},\n        body=json.dumps(swagger_doc, indent=2)).returns(\n            {'id': 'rest_api_id'})\n\n    stubbed_session.activate_stubs()\n    awsclient = TypedAWSClient(stubbed_session)\n    rest_api_id = awsclient.import_rest_api(swagger_doc, 'EDGE')\n    stubbed_session.verify_stubs()\n    assert rest_api_id == 'rest_api_id'\n\n\ndef test_update_api_from_swagger(stubbed_session):\n    apig = stubbed_session.stub('apigateway')\n    swagger_doc = {'swagger': 'doc'}\n    apig.put_rest_api(\n        restApiId='rest_api_id',\n        mode='overwrite',\n        body=json.dumps(swagger_doc, indent=2)).returns({})\n\n    stubbed_session.activate_stubs()\n    awsclient = TypedAWSClient(stubbed_session)\n\n    awsclient.update_api_from_swagger('rest_api_id', swagger_doc)\n    stubbed_session.verify_stubs()\n\n\ndef test_update_rest_api(stubbed_session):\n    apig = stubbed_session.stub('apigateway')\n    patch_operations = [{'op': 'replace',\n                         'path': '/minimumCompressionSize',\n                         'value': '2'}]\n    apig.update_rest_api(\n        restApiId='rest_api_id',\n        patchOperations=patch_operations).returns({})\n\n    stubbed_session.activate_stubs()\n    awsclient = TypedAWSClient(stubbed_session)\n\n    awsclient.update_rest_api('rest_api_id',\n                              patch_operations)\n    stubbed_session.verify_stubs()\n\n\ndef test_can_get_or_create_rule_arn_with_pattern(stubbed_session):\n    events = stubbed_session.stub('events')\n    events.put_rule(\n        Name='rule-name',\n        EventPattern='{\"source\": [\"aws.ec2\"]}').returns({\n            'RuleArn': 'rule-arn',\n        })\n\n    stubbed_session.activate_stubs()\n    awsclient = TypedAWSClient(stubbed_session)\n    result = awsclient.get_or_create_rule_arn(\n        rule_name='rule-name',\n        event_pattern='{\"source\": [\"aws.ec2\"]}')\n    stubbed_session.verify_stubs()\n    assert result == 'rule-arn'\n\n\ndef test_can_get_or_create_rule_arn(stubbed_session):\n    events = stubbed_session.stub('events')\n    events.put_rule(\n        Name='rule-name',\n        Description='rule-description',\n        ScheduleExpression='rate(1 hour)').returns({\n            'RuleArn': 'rule-arn',\n        })\n\n    stubbed_session.activate_stubs()\n    awsclient = TypedAWSClient(stubbed_session)\n    result = awsclient.get_or_create_rule_arn(\n        'rule-name',\n        schedule_expression='rate(1 hour)',\n        rule_description='rule-description'\n    )\n    stubbed_session.verify_stubs()\n    assert result == 'rule-arn'\n\n\ndef test_can_connect_rule_to_lambda(stubbed_session):\n    events = stubbed_session.stub('events')\n    events.put_targets(\n        Rule='rule-name',\n        Targets=[{'Id': '1', 'Arn': 'function-arn'}]).returns({})\n\n    stubbed_session.activate_stubs()\n    awsclient = TypedAWSClient(stubbed_session)\n    awsclient.connect_rule_to_lambda('rule-name', 'function-arn')\n    stubbed_session.verify_stubs()\n\n\ndef test_add_permission_for_scheduled_event(stubbed_session):\n    lambda_client = stubbed_session.stub('lambda')\n    lambda_client.get_policy(FunctionName='function-arn').returns(\n        {'Policy': '{}'})\n    lambda_client.add_permission(\n        Action='lambda:InvokeFunction',\n        FunctionName='function-arn',\n        StatementId=stub.ANY,\n        Principal='events.amazonaws.com',\n        SourceArn='arn:aws:events:us-east-1:123456789012:rule/MyScheduledRule'\n    ).returns({})\n\n    stubbed_session.activate_stubs()\n\n    awsclient = TypedAWSClient(stubbed_session)\n    awsclient.add_permission_for_cloudwatch_event(\n        'arn:aws:events:us-east-1:123456789012:rule/MyScheduledRule',\n        'function-arn')\n\n    stubbed_session.verify_stubs()\n\n\ndef test_skip_if_permission_already_granted(stubbed_session):\n    lambda_client = stubbed_session.stub('lambda')\n    policy = {\n        'Id': 'default',\n        'Statement': [\n            {'Action': 'lambda:InvokeFunction',\n                'Condition': {\n                    'ArnLike': {\n                        'AWS:SourceArn': 'arn:aws:events:us-east-1'\n                                         ':123456789012:rule/MyScheduledRule',\n                    }\n                },\n                'Effect': 'Allow',\n                'Principal': {'Service': 'events.amazonaws.com'},\n                'Resource': 'resource-arn',\n                'Sid': 'statement-id'},\n        ],\n        'Version': '2012-10-17'\n    }\n    lambda_client.get_policy(\n        FunctionName='function-arn').returns({'Policy': json.dumps(policy)})\n\n    stubbed_session.activate_stubs()\n    awsclient = TypedAWSClient(stubbed_session)\n    awsclient.add_permission_for_cloudwatch_event(\n        'arn:aws:events:us-east-1:123456789012:rule/MyScheduledRule',\n        'function-arn')\n    stubbed_session.verify_stubs()\n\n\ndef test_can_delete_rule(stubbed_session):\n    events = stubbed_session.stub('events')\n    events.remove_targets(\n        Rule='rule-name',\n        Ids=['1']).returns({})\n    events.delete_rule(Name='rule-name').returns({})\n\n    stubbed_session.activate_stubs()\n    awsclient = TypedAWSClient(stubbed_session)\n    awsclient.delete_rule('rule-name')\n    stubbed_session.verify_stubs()\n\n\ndef test_can_connect_bucket_to_lambda_new_config(stubbed_session):\n    s3 = stubbed_session.stub('s3')\n    s3.get_bucket_notification_configuration(Bucket='mybucket').returns({\n        'ResponseMetadata': {},\n    })\n    s3.put_bucket_notification_configuration(\n        Bucket='mybucket',\n        NotificationConfiguration={\n            'LambdaFunctionConfigurations': [{\n                'LambdaFunctionArn': 'function-arn',\n                'Events': ['s3:ObjectCreated:*'],\n            }]\n        }\n    ).returns({})\n\n    stubbed_session.activate_stubs()\n    awsclient = TypedAWSClient(stubbed_session)\n    awsclient.connect_s3_bucket_to_lambda(\n        'mybucket', 'function-arn', ['s3:ObjectCreated:*'])\n    stubbed_session.verify_stubs()\n\n\ndef test_can_connect_bucket_with_prefix_and_suffix(stubbed_session):\n    s3 = stubbed_session.stub('s3')\n    s3.get_bucket_notification_configuration(Bucket='mybucket').returns({})\n    s3.put_bucket_notification_configuration(\n        Bucket='mybucket',\n        NotificationConfiguration={\n            'LambdaFunctionConfigurations': [{\n                'LambdaFunctionArn': 'function-arn',\n                'Filter': {\n                    'Key': {\n                        'FilterRules': [\n                            {\n                                'Name': 'Prefix',\n                                'Value': 'images/'\n                            },\n                            {\n                                'Name': 'Suffix',\n                                'Value': '.jpg'\n                            }\n                        ]\n                    }\n                },\n                'Events': ['s3:ObjectCreated:*'],\n            }]\n        }\n    ).returns({})\n\n    stubbed_session.activate_stubs()\n    awsclient = TypedAWSClient(stubbed_session)\n    awsclient.connect_s3_bucket_to_lambda(\n        'mybucket', 'function-arn', ['s3:ObjectCreated:*'],\n        prefix='images/', suffix='.jpg',\n    )\n    stubbed_session.verify_stubs()\n\n\ndef test_can_merge_s3_notification_config(stubbed_session):\n    s3 = stubbed_session.stub('s3')\n    s3.get_bucket_notification_configuration(Bucket='mybucket').returns({\n        'LambdaFunctionConfigurations': [\n            {'Events': ['s3:ObjectCreated:*'],\n             'LambdaFunctionArn': 'other-function-arn'}],\n    })\n    s3.put_bucket_notification_configuration(\n        Bucket='mybucket',\n        NotificationConfiguration={\n            'LambdaFunctionConfigurations': [\n                # The existing function arn remains untouched.\n                {'LambdaFunctionArn': 'other-function-arn',\n                 'Events': ['s3:ObjectCreated:*']},\n                # This is the new function arn that we've injected.\n                {'LambdaFunctionArn': 'function-arn',\n                 'Events': ['s3:ObjectCreated:*']},\n            ]\n        }\n    ).returns({})\n\n    stubbed_session.activate_stubs()\n    awsclient = TypedAWSClient(stubbed_session)\n    awsclient.connect_s3_bucket_to_lambda(\n        'mybucket', 'function-arn', ['s3:ObjectCreated:*'])\n    stubbed_session.verify_stubs()\n\n\ndef test_can_replace_existing_config(stubbed_session):\n    s3 = stubbed_session.stub('s3')\n    s3.get_bucket_notification_configuration(Bucket='mybucket').returns({\n        'LambdaFunctionConfigurations': [\n            {'Events': ['s3:ObjectRemoved:*'],\n             'LambdaFunctionArn': 'function-arn'}],\n    })\n    s3.put_bucket_notification_configuration(\n        Bucket='mybucket',\n        NotificationConfiguration={\n            'LambdaFunctionConfigurations': [\n                # Note the event is replaced from ObjectRemoved\n                # to ObjectCreated.\n                {'LambdaFunctionArn': 'function-arn',\n                 'Events': ['s3:ObjectCreated:*']},\n            ]\n        }\n    ).returns({})\n\n    stubbed_session.activate_stubs()\n    awsclient = TypedAWSClient(stubbed_session)\n    awsclient.connect_s3_bucket_to_lambda(\n        'mybucket', 'function-arn', ['s3:ObjectCreated:*'])\n    stubbed_session.verify_stubs()\n\n\ndef test_add_permission_for_s3_event(stubbed_session):\n    lambda_client = stubbed_session.stub('lambda')\n    lambda_client.get_policy(FunctionName='function-arn').returns(\n        {'Policy': '{}'})\n    lambda_client.add_permission(\n        Action='lambda:InvokeFunction',\n        FunctionName='function-arn',\n        StatementId=stub.ANY,\n        Principal='s3.amazonaws.com',\n        SourceArn='arn:aws:s3:::mybucket',\n        SourceAccount='12345',\n    ).returns({})\n    stubbed_session.activate_stubs()\n\n    awsclient = TypedAWSClient(stubbed_session)\n    awsclient.add_permission_for_s3_event(\n        'mybucket', 'function-arn', '12345')\n    stubbed_session.verify_stubs()\n\n\ndef test_skip_if_permission_already_granted_to_s3(stubbed_session):\n    lambda_client = stubbed_session.stub('lambda')\n    policy = {\n        'Id': 'default',\n        'Statement': [{\n            'Action': 'lambda:InvokeFunction',\n            'Condition': {\n                'StringEquals': {\n                    'AWS:SourceAccount': '12345',\n                },\n                'ArnLike': {\n                    'AWS:SourceArn': 'arn:aws:s3:::mybucket',\n                }\n            },\n            'Effect': 'Allow',\n            'Principal': {'Service': 's3.amazonaws.com'},\n            'Resource': 'resource-arn',\n            'Sid': 'statement-id',\n        }],\n        'Version': '2012-10-17'\n    }\n    lambda_client.get_policy(\n        FunctionName='function-arn').returns({'Policy': json.dumps(policy)})\n    stubbed_session.activate_stubs()\n    awsclient = TypedAWSClient(stubbed_session)\n    awsclient.add_permission_for_s3_event(\n        'mybucket', 'function-arn', '12345')\n    stubbed_session.verify_stubs()\n\n\ndef test_can_disconnect_bucket_to_lambda_merged(stubbed_session):\n    s3 = stubbed_session.stub('s3')\n    s3.get_bucket_notification_configuration(Bucket='mybucket').returns({\n        'LambdaFunctionConfigurations': [\n            {'Events': ['s3:ObjectRemoved:*'],\n             'LambdaFunctionArn': 'function-arn-1'},\n            {'Events': ['s3:ObjectCreated:*'],\n             'LambdaFunctionArn': 'function-arn-2'}\n        ],\n        'ResponseMetadata': {},\n    })\n    s3.put_bucket_notification_configuration(\n        Bucket='mybucket',\n        NotificationConfiguration={\n            'LambdaFunctionConfigurations': [\n                {'Events': ['s3:ObjectCreated:*'],\n                 'LambdaFunctionArn': 'function-arn-2'}\n            ],\n        },\n    ).returns({})\n\n    stubbed_session.activate_stubs()\n    awsclient = TypedAWSClient(stubbed_session)\n    awsclient.disconnect_s3_bucket_from_lambda(\n        'mybucket', 'function-arn-1')\n    stubbed_session.verify_stubs()\n\n\ndef test_can_disconnect_bucket_to_lambda_not_exists(stubbed_session):\n    s3 = stubbed_session.stub('s3')\n    s3.get_bucket_notification_configuration(Bucket='mybucket').returns({\n        'LambdaFunctionConfigurations': [\n            {'Events': ['s3:ObjectRemoved:*'],\n             'LambdaFunctionArn': 'function-arn-1'},\n            {'Events': ['s3:ObjectCreated:*'],\n             'LambdaFunctionArn': 'function-arn-2'}\n        ],\n    })\n    s3.put_bucket_notification_configuration(\n        Bucket='mybucket',\n        NotificationConfiguration={\n            'LambdaFunctionConfigurations': [\n                {'Events': ['s3:ObjectRemoved:*'],\n                 'LambdaFunctionArn': 'function-arn-1'},\n                {'Events': ['s3:ObjectCreated:*'],\n                 'LambdaFunctionArn': 'function-arn-2'}\n            ],\n        },\n    ).returns({})\n\n    stubbed_session.activate_stubs()\n    awsclient = TypedAWSClient(stubbed_session)\n    awsclient.disconnect_s3_bucket_from_lambda('mybucket', 'some-other-arn')\n    stubbed_session.verify_stubs()\n\n\ndef test_add_permission_for_sns_publish(stubbed_session):\n    lambda_client = stubbed_session.stub('lambda')\n    lambda_client.get_policy(FunctionName='function-arn').returns(\n        {'Policy': '{\"Statement\": []}'}\n    )\n    lambda_client.add_permission(\n        Action='lambda:InvokeFunction',\n        FunctionName='function-arn',\n        StatementId=stub.ANY,\n        Principal='sns.amazonaws.com',\n        SourceArn='arn:aws:sns:us-west-2:12345:my-demo-topic',\n    ).returns({})\n\n    stubbed_session.activate_stubs()\n    awsclient = TypedAWSClient(stubbed_session)\n    awsclient.add_permission_for_sns_topic(\n        'arn:aws:sns:us-west-2:12345:my-demo-topic', 'function-arn')\n    stubbed_session.verify_stubs()\n\n\ndef test_subscribe_function_to_arn(stubbed_session):\n    sns_client = stubbed_session.stub('sns')\n    topic_arn = 'arn:aws:sns:topic-arn'\n    sns_client.subscribe(\n        TopicArn=topic_arn,\n        Protocol='lambda',\n        Endpoint='function-arn'\n    ).returns({'SubscriptionArn': 'subscribe-arn'})\n\n    stubbed_session.activate_stubs()\n\n    awsclient = TypedAWSClient(stubbed_session)\n    awsclient.subscribe_function_to_topic(\n        'arn:aws:sns:topic-arn', 'function-arn')\n    stubbed_session.verify_stubs()\n\n\ndef test_can_unsubscribe_from_topic(stubbed_session):\n    sns_client = stubbed_session.stub('sns')\n    subscription_arn = 'arn:aws:sns:subscribe-arn'\n    sns_client.unsubscribe(\n        SubscriptionArn=subscription_arn,\n    ).returns({})\n\n    stubbed_session.activate_stubs()\n\n    awsclient = TypedAWSClient(stubbed_session)\n    awsclient.unsubscribe_from_topic(subscription_arn)\n    stubbed_session.verify_stubs()\n\n\n@pytest.mark.parametrize('topic_arn,function_arn,is_verified', [\n    ('arn:aws:sns:mytopic', 'arn:aws:lambda:myfunction', True),\n    ('arn:aws:sns:NEW-TOPIC', 'arn:aws:lambda:myfunction', False),\n    ('arn:aws:sns:mytopic', 'arn:aws:lambda:NEW-FUNCTION', False),\n    ('arn:aws:sns:NEW-TOPIC', 'arn:aws:lambda:NEW-FUNCTION', False),\n])\ndef test_subscription_exists(stubbed_session, topic_arn,\n                             function_arn, is_verified):\n    sns_client = stubbed_session.stub('sns')\n    subscription_arn = 'arn:aws:sns:subscribe-arn'\n    sns_client.get_subscription_attributes(\n        SubscriptionArn=subscription_arn,\n    ).returns({\n        \"Attributes\": {\n            \"Owner\": \"12345\",\n            \"RawMessageDelivery\": \"false\",\n            \"TopicArn\": topic_arn,\n            \"Endpoint\": function_arn,\n            \"Protocol\": \"lambda\",\n            \"PendingConfirmation\": \"false\",\n            \"ConfirmationWasAuthenticated\": \"true\",\n            \"SubscriptionArn\": subscription_arn,\n        }\n    })\n\n    stubbed_session.activate_stubs()\n\n    awsclient = TypedAWSClient(stubbed_session)\n    assert awsclient.verify_sns_subscription_current(\n        subscription_arn,\n        topic_name='mytopic',\n        function_arn='arn:aws:lambda:myfunction',\n    ) == is_verified\n    stubbed_session.verify_stubs()\n\n\ndef test_subscription_not_exists(stubbed_session):\n    sns_client = stubbed_session.stub('sns')\n    subscription_arn = 'arn:aws:sns:subscribe-arn'\n    sns_client.get_subscription_attributes(\n        SubscriptionArn=subscription_arn,\n    ).raises_error(error_code='NotFound', message='Does not exists.')\n\n    stubbed_session.activate_stubs()\n\n    awsclient = TypedAWSClient(stubbed_session)\n    assert not awsclient.verify_sns_subscription_current(\n        subscription_arn, 'topic-arn', 'function-arn')\n    stubbed_session.verify_stubs()\n\n\ndef test_can_remove_lambda_sns_permission(stubbed_session):\n    topic_arn = 'arn:aws:sns:us-west-2:12345:my-demo-topic'\n    policy = {\n        'Id': 'default',\n        'Statement': [create_policy_statement(topic_arn,\n                                              service_name='sns',\n                                              statement_id='12345')],\n        'Version': '2012-10-17'\n    }\n    lambda_stub = stubbed_session.stub('lambda')\n    lambda_stub.get_policy(\n        FunctionName='name').returns({'Policy': json.dumps(policy)})\n    lambda_stub.remove_permission(\n        FunctionName='name', StatementId='12345',\n    ).returns({})\n\n    # Because the policy above indicates that API gateway already has the\n    # necessary permissions, we should not call add_permission.\n    stubbed_session.activate_stubs()\n    client = TypedAWSClient(stubbed_session)\n    client.remove_permission_for_sns_topic(\n        topic_arn, 'name')\n    stubbed_session.verify_stubs()\n\n\ndef test_can_remove_s3_permission(stubbed_session):\n    policy = {\n        'Id': 'default',\n        'Statement': [create_policy_statement('arn:aws:s3:::mybucket',\n                                              service_name='s3',\n                                              statement_id='12345',\n                                              account_id='67890')],\n        'Version': '2012-10-17'\n    }\n    lambda_stub = stubbed_session.stub('lambda')\n    lambda_stub.get_policy(\n        FunctionName='name').returns({'Policy': json.dumps(policy)})\n    lambda_stub.remove_permission(\n        FunctionName='name', StatementId='12345',\n    ).returns({})\n\n    # Because the policy above indicates that API gateway already has the\n    # necessary permissions, we should not call add_permission.\n    stubbed_session.activate_stubs()\n    client = TypedAWSClient(stubbed_session)\n    client.remove_permission_for_s3_event(\n        'mybucket', 'name', '67890')\n    stubbed_session.verify_stubs()\n\n\ndef test_can_create_kinesis_event_source(stubbed_session):\n    kinesis_arn = 'arn:aws:kinesis:us-west-2:...:stream/MyStream'\n    function_name = 'myfunction'\n    batch_size = 100\n    starting_position = 'TRIM_HORIZON'\n    lambda_stub = stubbed_session.stub('lambda')\n    lambda_stub.create_event_source_mapping(\n        EventSourceArn=kinesis_arn,\n        FunctionName=function_name,\n        BatchSize=batch_size,\n        StartingPosition=starting_position,\n        MaximumBatchingWindowInSeconds=0\n    ).returns({'UUID': 'my-uuid'})\n\n    stubbed_session.activate_stubs()\n    client = TypedAWSClient(stubbed_session)\n    result = client.create_lambda_event_source(\n        kinesis_arn, function_name, batch_size, starting_position\n    )\n    assert result == 'my-uuid'\n    stubbed_session.verify_stubs()\n\n\ndef test_can_create_kinesis_event_source_batching_window(stubbed_session):\n    kinesis_arn = 'arn:aws:kinesis:us-west-2:...:stream/MyStream'\n    function_name = 'myfunction'\n    batch_size = 100\n    starting_position = 'TRIM_HORIZON'\n    maximum_batching_window_in_seconds = 60\n\n    lambda_stub = stubbed_session.stub('lambda')\n    lambda_stub.create_event_source_mapping(\n        EventSourceArn=kinesis_arn,\n        FunctionName=function_name,\n        BatchSize=batch_size,\n        StartingPosition=starting_position,\n        MaximumBatchingWindowInSeconds=maximum_batching_window_in_seconds\n\n    ).returns({'UUID': 'my-uuid'})\n\n    stubbed_session.activate_stubs()\n    client = TypedAWSClient(stubbed_session)\n    result = client.create_lambda_event_source(\n        kinesis_arn, function_name, batch_size, starting_position,\n        maximum_batching_window_in_seconds\n    )\n    assert result == 'my-uuid'\n    stubbed_session.verify_stubs()\n\n\ndef test_can_create_sqs_event_source(stubbed_session):\n    queue_arn = 'arn:sqs:queue-name'\n    function_name = 'myfunction'\n    batch_size = 100\n    maximum_batching_window_in_seconds = 60\n\n    lambda_stub = stubbed_session.stub('lambda')\n    lambda_stub.create_event_source_mapping(\n        EventSourceArn=queue_arn,\n        FunctionName=function_name,\n        BatchSize=batch_size,\n        MaximumBatchingWindowInSeconds=maximum_batching_window_in_seconds\n    ).returns({'UUID': 'my-uuid'})\n\n    stubbed_session.activate_stubs()\n    client = TypedAWSClient(stubbed_session)\n    result = client.create_lambda_event_source(\n        queue_arn, function_name, batch_size,\n        maximum_batching_window_in_seconds=maximum_batching_window_in_seconds\n    )\n    assert result == 'my-uuid'\n    stubbed_session.verify_stubs()\n\n\ndef test_can_retry_create_sqs_event_source(stubbed_session):\n    queue_arn = 'arn:sqs:queue-name'\n    function_name = 'myfunction'\n    batch_size = 100\n\n    lambda_stub = stubbed_session.stub('lambda')\n    lambda_stub.create_event_source_mapping(\n        EventSourceArn=queue_arn,\n        FunctionName=function_name,\n        BatchSize=batch_size,\n        MaximumBatchingWindowInSeconds=0\n    ).raises_error(\n        error_code='InvalidParameterValueException',\n        message=('The provided execution role does not '\n                 'have permissions to call ReceiveMessage on SQS')\n    )\n    lambda_stub.create_event_source_mapping(\n        EventSourceArn=queue_arn,\n        FunctionName=function_name,\n        BatchSize=batch_size,\n        MaximumBatchingWindowInSeconds=0\n    ).returns({'UUID': 'my-uuid'})\n\n    stubbed_session.activate_stubs()\n    client = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n    result = client.create_lambda_event_source(\n        queue_arn, function_name, batch_size\n    )\n    assert result == 'my-uuid'\n\n    stubbed_session.verify_stubs()\n\n\ndef test_can_delete_sqs_event_source(stubbed_session):\n    lambda_stub = stubbed_session.stub('lambda')\n    lambda_stub.delete_event_source_mapping(\n        UUID='my-uuid',\n    ).returns({})\n\n    stubbed_session.activate_stubs()\n    client = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n    client.remove_lambda_event_source(\n        'my-uuid',\n    )\n    stubbed_session.verify_stubs()\n\n\ndef test_can_retry_delete_event_source(stubbed_session):\n    lambda_stub = stubbed_session.stub('lambda')\n    lambda_stub.delete_event_source_mapping(\n        UUID='my-uuid',\n    ).raises_error(\n        error_code='ResourceInUseException',\n        message=('Cannot update the event source mapping '\n                 'because it is in use.')\n    )\n    lambda_stub.delete_event_source_mapping(\n        UUID='my-uuid',\n    ).returns({})\n\n    stubbed_session.activate_stubs()\n    client = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n    client.remove_lambda_event_source(\n        'my-uuid',\n    )\n    stubbed_session.verify_stubs()\n\n\ndef test_only_retry_settling_errors(stubbed_session):\n    lambda_stub = stubbed_session.stub('lambda')\n    lambda_stub.delete_event_source_mapping(\n        UUID='my-uuid',\n    ).raises_error(\n        error_code='ResourceInUseException',\n        message='Wrong message'\n    )\n    stubbed_session.activate_stubs()\n    client = TypedAWSClient(stubbed_session, mock.Mock(spec=time.sleep))\n    with pytest.raises(botocore.exceptions.ClientError):\n        client.remove_lambda_event_source('my-uuid')\n    stubbed_session.verify_stubs()\n\n\ndef test_can_retry_update_event_source(stubbed_session):\n    lambda_stub = stubbed_session.stub('lambda')\n    lambda_stub.update_event_source_mapping(\n        UUID='my-uuid',\n        BatchSize=5,\n        MaximumBatchingWindowInSeconds=0,\n    ).returns({})\n\n    stubbed_session.activate_stubs()\n    client = TypedAWSClient(stubbed_session)\n    client.update_lambda_event_source(\n        event_uuid='my-uuid', batch_size=5\n    )\n    stubbed_session.verify_stubs()\n\n\ndef test_can_retry_update_event_source_batching_window(stubbed_session):\n    lambda_stub = stubbed_session.stub('lambda')\n    lambda_stub.update_event_source_mapping(\n        UUID='my-uuid',\n        BatchSize=5,\n        MaximumBatchingWindowInSeconds=60,\n    ).returns({})\n\n    stubbed_session.activate_stubs()\n    client = TypedAWSClient(stubbed_session)\n    client.update_lambda_event_source(\n        event_uuid='my-uuid', batch_size=5,\n        maximum_batching_window_in_seconds=60\n    )\n    stubbed_session.verify_stubs()\n\n\n@pytest.mark.parametrize('resource_name,service_name,is_verified', [\n    ('queue-name', 'sqs', True),\n    ('queue-name', 'not-sqs', False),\n    ('not-queue-name', 'sqs', False),\n    ('not-queue-name', 'not-sqs', False),\n])\ndef test_verify_event_source_current(stubbed_session, resource_name,\n                                     service_name, is_verified):\n    client = stubbed_session.stub('lambda')\n    uuid = 'uuid-12345'\n    client.get_event_source_mapping(\n        UUID=uuid,\n    ).returns({\n        'UUID': uuid,\n        'BatchSize': 10,\n        'EventSourceArn': 'arn:aws:sqs:us-west-2:123:queue-name',\n        'FunctionArn': 'arn:aws:lambda:function-arn',\n        'LastModified': '2018-07-02T18:19:03.958000-07:00',\n        'State': 'Enabled',\n        'StateTransitionReason': 'USER_INITIATED'\n    })\n    stubbed_session.activate_stubs()\n\n    awsclient = TypedAWSClient(stubbed_session)\n    assert awsclient.verify_event_source_current(\n        uuid, resource_name=resource_name, service_name=service_name,\n        function_arn='arn:aws:lambda:function-arn',\n    ) == is_verified\n    stubbed_session.verify_stubs()\n\n\ndef test_verify_event_source_arn_current(stubbed_session):\n    client = stubbed_session.stub('lambda')\n    uuid = 'uuid-12345'\n    client.get_event_source_mapping(\n        UUID=uuid,\n    ).returns({\n        'UUID': uuid,\n        'BatchSize': 10,\n        'EventSourceArn': 'arn:aws:dynamodb:...:table/MyTable/stream/2020',\n        'FunctionArn': 'arn:aws:lambda:function-arn',\n        'LastModified': '2018-07-02T18:19:03.958000-07:00',\n        'State': 'Enabled',\n        'StateTransitionReason': 'USER_INITIATED'\n    })\n    stubbed_session.activate_stubs()\n\n    awsclient = TypedAWSClient(stubbed_session)\n    assert awsclient.verify_event_source_arn_current(\n        uuid,\n        event_source_arn='arn:aws:dynamodb:...:table/MyTable/stream/2020',\n        function_arn='arn:aws:lambda:function-arn',\n    )\n    stubbed_session.verify_stubs()\n\n\ndef test_event_source_uuid_does_not_exist(stubbed_session):\n    client = stubbed_session.stub('lambda')\n    uuid = 'uuid-12345'\n    client.get_event_source_mapping(\n        UUID=uuid,\n    ).raises_error(error_code='ResourceNotFoundException',\n                   message='Does not exists.')\n\n    stubbed_session.activate_stubs()\n\n    awsclient = TypedAWSClient(stubbed_session)\n    assert not awsclient.verify_event_source_arn_current(\n        uuid, event_source_arn='arn:aws:dynamodb:...',\n        function_arn='arn:aws:lambda:...',\n    )\n    stubbed_session.verify_stubs()\n\n\ndef test_event_source_does_not_exist(stubbed_session):\n    client = stubbed_session.stub('lambda')\n    uuid = 'uuid-12345'\n    client.get_event_source_mapping(\n        UUID=uuid,\n    ).raises_error(error_code='ResourceNotFoundException',\n                   message='Does not exists.')\n\n    stubbed_session.activate_stubs()\n\n    awsclient = TypedAWSClient(stubbed_session)\n    assert not awsclient.verify_event_source_current(\n        uuid, 'myqueue', 'sqs', 'function-arn')\n    stubbed_session.verify_stubs()\n\n\ndef test_can_update_lambda_event_source(stubbed_session):\n    lambda_stub = stubbed_session.stub('lambda')\n    lambda_stub.update_event_source_mapping(\n        UUID='my-uuid',\n        BatchSize=5,\n        MaximumBatchingWindowInSeconds=60,\n    ).returns({})\n\n    stubbed_session.activate_stubs()\n    client = TypedAWSClient(stubbed_session)\n    client.update_lambda_event_source(\n        event_uuid='my-uuid', batch_size=5,\n        maximum_batching_window_in_seconds=60\n    )\n    stubbed_session.verify_stubs()\n\n\ndef test_can_create_log_group(stubbed_session):\n    logs_stub = stubbed_session.stub('logs')\n    logs_stub.create_log_group(\n        logGroupName='mygroup'\n    ).returns({})\n    stubbed_session.activate_stubs()\n    client = TypedAWSClient(stubbed_session)\n    client.create_log_group(log_group_name='mygroup')\n    stubbed_session.verify_stubs()\n\n\ndef test_can_delete_log_group(stubbed_session):\n    logs_stub = stubbed_session.stub('logs')\n    logs_stub.delete_log_group(\n        logGroupName='mygroup'\n    ).returns({})\n    stubbed_session.activate_stubs()\n    client = TypedAWSClient(stubbed_session)\n    client.delete_log_group(log_group_name='mygroup')\n    stubbed_session.verify_stubs()\n\n\ndef test_can_delete_retention_policy(stubbed_session):\n    logs_stub = stubbed_session.stub('logs')\n    logs_stub.delete_retention_policy(\n        logGroupName='mygroup'\n    ).returns({})\n    stubbed_session.activate_stubs()\n    client = TypedAWSClient(stubbed_session)\n    client.delete_retention_policy(log_group_name='mygroup')\n    stubbed_session.verify_stubs()\n"
  },
  {
    "path": "tests/functional/test_deployer.py",
    "content": "import os\nimport zipfile\nimport json\nfrom unittest import mock\nimport hashlib\n\nfrom pytest import fixture\nimport pytest\n\nimport chalice.deploy.deployer\nimport chalice.deploy.packager\nfrom chalice.awsclient import TypedAWSClient\nimport chalice.utils\nfrom chalice.config import Config\nfrom chalice import Chalice\nfrom chalice.deploy.packager import MissingDependencyError\nfrom chalice.deploy.packager import EmptyPackageError\nfrom chalice.deploy.packager import LambdaDeploymentPackager\nfrom chalice.deploy.packager import DependencyBuilder\nfrom chalice.deploy.packager import Package\n\n\nslow = pytest.mark.slow\n\n\n@fixture\ndef chalice_deployer():\n    ui = chalice.utils.UI()\n    osutils = chalice.utils.OSUtils()\n    dependency_builder = mock.Mock(spec=DependencyBuilder)\n    d = chalice.deploy.packager.LambdaDeploymentPackager(\n        osutils=osutils, dependency_builder=dependency_builder,\n        ui=ui\n    )\n    return d\n\n\n@fixture\ndef app_only_packager():\n    ui = chalice.utils.UI()\n    osutils = chalice.utils.OSUtils()\n    dependency_builder = mock.Mock(spec=DependencyBuilder)\n    d = chalice.deploy.packager.AppOnlyDeploymentPackager(\n        osutils=osutils, dependency_builder=dependency_builder,\n        ui=ui\n    )\n    return d, dependency_builder\n\n\n@fixture\ndef layer_packager():\n    ui = chalice.utils.UI()\n    osutils = chalice.utils.OSUtils()\n    dependency_builder = mock.Mock(spec=DependencyBuilder)\n    d = chalice.deploy.packager.LayerDeploymentPackager(\n        osutils=osutils, dependency_builder=dependency_builder,\n        ui=ui\n    )\n    return d, dependency_builder\n\n\ndef _create_app_structure(tmpdir):\n    appdir = tmpdir.mkdir('app')\n    appdir.join('app.py').write('# Test app')\n    appdir.mkdir('.chalice')\n    return appdir\n\n\n@slow\ndef test_can_create_deployment_package(tmpdir, chalice_deployer):\n    appdir = _create_app_structure(tmpdir)\n    appdir.join('app.py').write('# Test app')\n    chalice_dir = appdir.join('.chalice')\n    chalice_deployer.create_deployment_package(\n        str(appdir), 'python3.11')\n    # There should now be a zip file created.\n    contents = chalice_dir.join('deployments').listdir()\n    assert len(contents) == 1\n    assert str(contents[0]).endswith('.zip')\n\n\n@slow\ndef test_can_inject_latest_app(tmpdir, chalice_deployer):\n    appdir = _create_app_structure(tmpdir)\n    appdir.join('app.py').write('# Test app v1')\n    chalice_dir = appdir.join('.chalice')\n    name = chalice_deployer.create_deployment_package(\n        str(appdir), 'python3.11')\n\n    # Now suppose we update our app code but not any deps.\n    # We can use inject_latest_app.\n    appdir.join('app.py').write('# Test app NEW VERSION')\n    # There should now be a zip file created.\n    chalice_deployer.inject_latest_app(name, str(appdir))\n    contents = chalice_dir.join('deployments').listdir()\n    assert len(contents) == 1\n    assert str(contents[0]) == name\n    with zipfile.ZipFile(name) as f:\n        contents = f.read('app.py')\n        assert contents == b'# Test app NEW VERSION'\n\n\n@slow\ndef test_zipfile_hash_only_based_on_contents(tmpdir, chalice_deployer):\n    appdir = _create_app_structure(tmpdir)\n    appdir.join('app.py').write('# Test app v1')\n    name = chalice_deployer.create_deployment_package(\n        str(appdir), 'python3.11')\n    with open(name, 'rb') as f:\n        original_checksum = hashlib.md5(f.read()).hexdigest()\n\n    # Now we'll modify the file our app file with the same contents\n    # but it will change the mtime.\n    app_file = appdir.join('app.py')\n    app_file.write('# Test app v1')\n    # Set the mtime to something different (1990-1-1T00:00:00).\n    # This would normally result in the zipfile having a different\n    # checksum.\n    os.utime(str(app_file), (631152000.0, 631152000.0))\n    name = chalice_deployer.create_deployment_package(\n        str(appdir), 'python3.11')\n    with open(name, 'rb') as f:\n        new_checksum = hashlib.md5(f.read()).hexdigest()\n    assert new_checksum == original_checksum\n\n\n@slow\ndef test_app_injection_still_compresses_file(tmpdir, chalice_deployer):\n    appdir = _create_app_structure(tmpdir)\n    appdir.join('app.py').write('# Test app v1')\n    name = chalice_deployer.create_deployment_package(\n        str(appdir), 'python3.11')\n    original_size = os.path.getsize(name)\n    appdir.join('app.py').write('# Test app v2')\n    chalice_deployer.inject_latest_app(name, str(appdir))\n    new_size = os.path.getsize(name)\n    # The new_size won't be exactly the same as the original,\n    # we just want to make sure it wasn't converted to\n    # ZIP_STORED, so there's a 5% tolerance.\n    assert new_size < (original_size * 1.05)\n\n\n@slow\ndef test_no_error_message_printed_on_empty_reqs_file(tmpdir,\n                                                     chalice_deployer,\n                                                     capfd):\n    appdir = _create_app_structure(tmpdir)\n    appdir.join('app.py').write('# Foo')\n    appdir.join('requirements.txt').write('\\n')\n    chalice_deployer.create_deployment_package(\n        str(appdir), 'python3.11')\n    out, err = capfd.readouterr()\n    assert err.strip() == ''\n\n\ndef test_osutils_proxies_os_functions(tmpdir):\n    appdir = _create_app_structure(tmpdir)\n    appdir.join('app.py').write(b'hello')\n\n    osutils = chalice.utils.OSUtils()\n\n    app_file = str(appdir.join('app.py'))\n    assert osutils.file_exists(app_file)\n    assert osutils.get_file_contents(app_file) == b'hello'\n    assert osutils.open(app_file, 'rb').read() == b'hello'\n    osutils.remove_file(app_file)\n    # Removing again doesn't raise an error.\n    osutils.remove_file(app_file)\n    assert not osutils.file_exists(app_file)\n\n\n@slow\ndef test_includes_app_and_chalicelib_dir(tmpdir, chalice_deployer):\n    appdir = _create_app_structure(tmpdir)\n    # We're now also going to create additional files\n    chalicelib = appdir.mkdir('chalicelib')\n    appdir.join('chalicelib', '__init__.py').write('# Test package')\n    appdir.join('chalicelib', 'mymodule.py').write('# Test module')\n    appdir.join('chalicelib', 'config.json').write('{\"test\": \"config\"}')\n    # Should also include sub directories\n    subdir = chalicelib.mkdir('subdir')\n    subdir.join('submodule.py').write('# Test submodule')\n    subdir.join('subconfig.json').write('{\"test\": \"subconfig\"}')\n    name = chalice_deployer.create_deployment_package(\n        str(appdir), 'python3.11')\n    with zipfile.ZipFile(name) as f:\n        _assert_in_zip('chalicelib/__init__.py', b'# Test package', f)\n        _assert_in_zip('chalicelib/mymodule.py', b'# Test module', f)\n        _assert_in_zip('chalicelib/config.json', b'{\"test\": \"config\"}', f)\n        _assert_in_zip('chalicelib/subdir/submodule.py',\n                       b'# Test submodule', f)\n        _assert_in_zip('chalicelib/subdir/subconfig.json',\n                       b'{\"test\": \"subconfig\"}', f)\n\n\ndef _assert_in_zip(path, contents, zip):\n    allfiles = zip.namelist()\n    assert path in allfiles\n    assert zip.read(path) == contents\n\n\ndef _assert_not_in_zip(path, zip):\n    allfiles = zip.namelist()\n    assert path not in allfiles\n\n\n@slow\ndef test_subsequent_deploy_replaces_chalicelib(tmpdir, chalice_deployer):\n    appdir = _create_app_structure(tmpdir)\n    chalicelib = appdir.mkdir('chalicelib')\n    appdir.join('chalicelib', '__init__.py').write('# Test package')\n    subdir = chalicelib.mkdir('subdir')\n    subdir.join('submodule.py').write('# Test submodule')\n\n    name = chalice_deployer.create_deployment_package(\n        str(appdir), 'python3.11')\n    subdir.join('submodule.py').write('# Test submodule v2')\n    appdir.join('chalicelib', '__init__.py').remove()\n    chalice_deployer.inject_latest_app(name, str(appdir))\n    with zipfile.ZipFile(name) as f:\n        _assert_in_zip('chalicelib/subdir/submodule.py',\n                       b'# Test submodule v2', f)\n        # And chalicelib/__init__.py should no longer be\n        # in the zipfile because we deleted it in the appdir.\n        assert 'chalicelib/__init__.py' not in f.namelist()\n\n\n@slow\ndef test_vendor_dir_included(tmpdir, chalice_deployer):\n    appdir = _create_app_structure(tmpdir)\n    vendor = appdir.mkdir('vendor')\n    extra_package = vendor.mkdir('mypackage')\n    extra_package.join('__init__.py').write('# Test package')\n    name = chalice_deployer.create_deployment_package(\n        str(appdir), 'python3.11')\n    with zipfile.ZipFile(name) as f:\n        _assert_in_zip('mypackage/__init__.py', b'# Test package', f)\n\n\n@slow\ndef test_no_vendor_in_app_only_packager(tmpdir, app_only_packager):\n    packager, deps_builder = app_only_packager\n    appdir = _create_app_structure(tmpdir)\n    appdir.mkdir('chalicelib')\n    appdir.join('requirements.txt').write('boto3')\n    appdir.join('chalicelib', '__init__.py').write('# Test package')\n    vendor = appdir.mkdir('vendor')\n    extra_package = vendor.mkdir('mypackage')\n    extra_package.join('__init__.py').write('# Test package')\n    name = packager.create_deployment_package(\n        str(appdir), 'python3.11')\n    with zipfile.ZipFile(name) as f:\n        _assert_not_in_zip('mypackage/__init__.py', f)\n        _assert_in_zip('chalicelib/__init__.py', b'# Test package', f)\n        _assert_in_zip('app.py', b'# Test app', f)\n    assert not deps_builder.build_site_packages.called\n\n\n@slow\ndef test_py_deps_in_layer_package(tmpdir, layer_packager):\n    packager, deps_builder = layer_packager\n    appdir = _create_app_structure(tmpdir)\n    appdir.mkdir('chalicelib')\n    appdir.join('requirements.txt').write('boto3')\n    appdir.join('chalicelib', '__init__.py').write('# Test package')\n    vendor = appdir.mkdir('vendor')\n    extra_package = vendor.mkdir('mypackage')\n    extra_package.join('__init__.py').write('# Test package')\n    name = packager.create_deployment_package(\n        str(appdir), 'python3.11')\n    assert os.path.basename(name).startswith('managed-layer-')\n    with zipfile.ZipFile(name) as f:\n        prefix = 'python/lib/python3.11/site-packages'\n        _assert_in_zip(\n            '%s/mypackage/__init__.py' % prefix, b'# Test package', f)\n        _assert_not_in_zip('%s/chalicelib/__init__.py' % prefix, f)\n        _assert_not_in_zip('%s/app.py' % prefix, f)\n    deps_builder.build_site_packages.assert_called_with(\n        'cp311', str(appdir.join('requirements.txt')), mock.ANY\n    )\n\n\ndef test_empty_layer_package_raises_error(tmpdir, layer_packager):\n    packager, deps_builder = layer_packager\n    appdir = _create_app_structure(tmpdir)\n    appdir.mkdir('chalicelib')\n    appdir.join('requirements.txt').write('')\n    appdir.join('chalicelib', '__init__.py').write('# Test package')\n    filename = packager.deployment_package_filename(str(appdir), 'python3.11')\n    with pytest.raises(EmptyPackageError):\n        packager.create_deployment_package(\n            str(appdir), 'python3.11')\n    # We should also verify that the file does not exist so it doesn't\n    # get reused in subsequent caches.  This shouldn't affect anything,\n    # we're just trying to cleanup properly.\n    assert not os.path.isfile(filename)\n\n\n@slow\ndef test_subsequent_deploy_replaces_vendor_dir(tmpdir, chalice_deployer):\n    appdir = _create_app_structure(tmpdir)\n    vendor = appdir.mkdir('vendor')\n    extra_package = vendor.mkdir('mypackage')\n    extra_package.join('__init__.py').write('# v1')\n    name = chalice_deployer.create_deployment_package(\n        str(appdir), 'python3.11')\n    # Now we update a package in vendor/ with a new version.\n    extra_package.join('__init__.py').write('# v2')\n    name = chalice_deployer.create_deployment_package(\n        str(appdir), 'python3.11')\n    with zipfile.ZipFile(name) as f:\n        _assert_in_zip('mypackage/__init__.py', b'# v2', f)\n\n\n@slow\ndef test_vendor_symlink_included(tmpdir, chalice_deployer):\n    appdir = _create_app_structure(tmpdir)\n    extra_package = tmpdir.mkdir('mypackage')\n    extra_package.join('__init__.py').write('# Test package')\n    vendor = appdir.mkdir('vendor')\n    os.symlink(str(extra_package), str(vendor.join('otherpackage')))\n    name = chalice_deployer.create_deployment_package(\n        str(appdir), 'python3.11')\n    with zipfile.ZipFile(name) as f:\n        _assert_in_zip('otherpackage/__init__.py', b'# Test package', f)\n\n\n@slow\ndef test_subsequent_deploy_replaces_vendor_symlink(tmpdir, chalice_deployer):\n    appdir = _create_app_structure(tmpdir)\n    extra_package = tmpdir.mkdir('mypackage')\n    extra_package.join('__init__.py').write('# v1')\n    vendor = appdir.mkdir('vendor')\n    os.symlink(str(extra_package), str(vendor.join('otherpackage')))\n    name = chalice_deployer.create_deployment_package(\n        str(appdir), 'python3.11')\n    with zipfile.ZipFile(name) as f:\n        _assert_in_zip('otherpackage/__init__.py', b'# v1', f)\n    # Now we update a package in vendor/ with a new version.\n    extra_package.join('__init__.py').write('# v2')\n    name = chalice_deployer.create_deployment_package(\n        str(appdir), 'python3.11')\n    with zipfile.ZipFile(name) as f:\n        _assert_in_zip('otherpackage/__init__.py', b'# v2', f)\n\n\ndef test_zip_filename_changes_on_vendor_update(tmpdir, chalice_deployer):\n    appdir = _create_app_structure(tmpdir)\n    vendor = appdir.mkdir('vendor')\n    extra_package = vendor.mkdir('mypackage')\n    extra_package.join('__init__.py').write('# v1')\n    first = chalice_deployer.deployment_package_filename(\n        str(appdir), 'python3.6')\n    extra_package.join('__init__.py').write('# v2')\n    second = chalice_deployer.deployment_package_filename(\n        str(appdir), 'python3.6')\n    assert first != second\n\n\ndef test_zip_filename_changes_on_vendor_symlink(tmpdir, chalice_deployer):\n    appdir = _create_app_structure(tmpdir)\n    vendor = appdir.mkdir('vendor')\n    extra_package = tmpdir.mkdir('mypackage')\n    extra_package.join('__init__.py').write('# v1')\n    os.symlink(str(extra_package), str(vendor.join('otherpackage')))\n    first = chalice_deployer.deployment_package_filename(\n        str(appdir), 'python3.6')\n    extra_package.join('__init__.py').write('# v2')\n    second = chalice_deployer.deployment_package_filename(\n        str(appdir), 'python3.6')\n    assert first != second\n\n\n@slow\ndef test_chalice_runtime_injected_on_change(tmpdir, chalice_deployer):\n    appdir = _create_app_structure(tmpdir)\n    name = chalice_deployer.create_deployment_package(\n        str(appdir), 'python3.11')\n    # We're verifying that we always inject the chalice runtime\n    # but we can't actually modify the runtime in this repo, so\n    # instead we'll modify the deployment package and change the\n    # runtime.\n    # We'll then verify when we inject the latest app the runtime\n    # has been re-added.  This should give us enough confidence\n    # that the runtime is always being inserted.\n    _remove_runtime_from_deployment_package(name)\n    with zipfile.ZipFile(name) as z:\n        assert 'chalice/app.py' not in z.namelist()\n    chalice_deployer.inject_latest_app(name, str(appdir))\n    with zipfile.ZipFile(name) as z:\n        assert 'chalice/app.py' in z.namelist()\n\n\ndef test_does_handle_missing_dependency_error(tmpdir):\n    appdir = _create_app_structure(tmpdir)\n    builder = mock.Mock(spec=DependencyBuilder)\n    fake_package = mock.Mock(spec=Package)\n    fake_package.identifier = 'foo==1.2'\n    builder.build_site_packages.side_effect = MissingDependencyError(\n        set([fake_package]))\n    ui = mock.Mock(spec=chalice.utils.UI)\n    osutils = chalice.utils.OSUtils()\n    packager = LambdaDeploymentPackager(\n        osutils=osutils,\n        dependency_builder=builder,\n        ui=ui,\n    )\n    packager.create_deployment_package(str(appdir), 'python3.11')\n\n    output = ''.join([call[0][0] for call in ui.write.call_args_list])\n    assert 'Could not install dependencies:\\nfoo==1.2' in output\n\n\ndef _remove_runtime_from_deployment_package(filename):\n    new_filename = os.path.join(os.path.dirname(filename), 'new.zip')\n    with zipfile.ZipFile(filename, 'r') as original:\n        with zipfile.ZipFile(new_filename, 'w',\n                             compression=zipfile.ZIP_DEFLATED) as z:\n            for item in original.infolist():\n                if item.filename.startswith('chalice/'):\n                    continue\n                contents = original.read(item.filename)\n                z.writestr(item, contents)\n    os.remove(filename)\n    os.rename(new_filename, filename)\n\n\ndef test_can_delete_app(tmpdir):\n    # This is just a sanity check that deletions are working\n    # as expected now that there's no separate interface\n    # for deletion at the deployer layer.\n    appdir = _create_app_structure(tmpdir)\n    appdir.join('app.py').write(\n        'from chalice import Chalice\\n'\n        'app = Chalice(\"testapp\")'\n    )\n    deployed_json = {\n        'schema_version': '2.0',\n        'backend': 'api',\n        'resources': [\n            {'name': 'role-index',\n                'resource_type': 'iam_role',\n                'role_name': 'testapp-dev-index',\n                'role_arn': 'arn:aws:iam::1:role/testapp-dev-index'},\n            {'lambda_arn': 'arn:aws:lambda:r:1:f:testapp-dev-index',\n                'name': 'index', 'resource_type': 'lambda_function'},\n            {'name': 'role-james', 'resource_type': 'iam_role',\n                'role_name': 'testapp-dev-foo',\n                'role_arn': 'arn:aws:iam::1:role/testapp-dev-foo'},\n            {'lambda_arn': 'arn:aws:lambda:r:1:f:testapp-dev-foo',\n                'name': 'james', 'resource_type': 'lambda_function'}\n        ]\n    }\n    deployed_dir = appdir.join('.chalice', 'deployed')\n    deployed_dir.mkdir()\n    deployed_dir.join('dev.json').write(\n        json.dumps(deployed_json))\n    mock_client = mock.Mock(spec=TypedAWSClient)\n    ui = mock.Mock(spec=chalice.utils.UI)\n    d = chalice.deploy.deployer.create_deletion_deployer(mock_client, ui)\n\n    config = Config(\n        chalice_stage='dev',\n        user_provided_params={\n            'chalice_app': Chalice('testapp'),\n            'project_dir': str(appdir),\n        },\n        config_from_disk={},\n        default_params={}\n    )\n    returned_values = d.deploy(config, 'dev')\n    assert returned_values == {\n        'schema_version': '2.0',\n        'backend': 'api',\n        'resources': [],\n    }\n    call = mock.call\n    expected_calls = [\n        call.delete_function(\n            function_name=u'arn:aws:lambda:r:1:f:testapp-dev-foo'),\n        call.delete_role(name=u'testapp-dev-foo'),\n        call.delete_function(\n            function_name=u'arn:aws:lambda:r:1:f:testapp-dev-index'),\n        call.delete_role(name=u'testapp-dev-index')\n    ]\n    assert expected_calls == mock_client.method_calls\n"
  },
  {
    "path": "tests/functional/test_local.py",
    "content": "import os\nimport socket\nimport time\nimport contextlib\nfrom threading import Thread\nfrom threading import Event\nfrom threading import Lock\nimport json\nimport subprocess\nfrom contextlib import contextmanager\n\nimport pytest\nimport requests\nfrom urllib3.util.retry import Retry\nfrom requests.adapters import HTTPAdapter\n\nfrom chalice import app\nfrom chalice.local import create_local_server\nfrom chalice.config import Config\nfrom chalice.utils import OSUtils\n\n\nAPPS_DIR = os.path.dirname(os.path.abspath(__file__))\nENV_APP_DIR = os.path.join(APPS_DIR, 'envapp')\nBASIC_APP = os.path.join(APPS_DIR, 'basicapp')\n\n\nNEW_APP_VERSION = \"\"\"\nfrom chalice import Chalice\n\napp = Chalice(app_name='basicapp')\n\n\n@app.route('/')\ndef index():\n    return {'version': 'reloaded'}\n\"\"\"\n\n\n@contextmanager\ndef cd(path):\n    try:\n        original_dir = os.getcwd()\n        os.chdir(path)\n        yield\n    finally:\n        os.chdir(original_dir)\n\n\n@pytest.fixture()\ndef basic_app(tmpdir):\n    tmpdir = str(tmpdir.mkdir('basicapp'))\n    OSUtils().copytree(BASIC_APP, tmpdir)\n    return tmpdir\n\n\nclass ThreadedLocalServer(Thread):\n    def __init__(self, port, host='localhost'):\n        super(ThreadedLocalServer, self).__init__()\n        self._app_object = None\n        self._config = None\n        self._host = host\n        self._port = port\n        self._server = None\n        self._server_ready = Event()\n\n    def wait_for_server_ready(self):\n        self._server_ready.wait()\n\n    def configure(self, app_object, config):\n        self._app_object = app_object\n        self._config = config\n\n    def run(self):\n        self._server = create_local_server(\n            self._app_object, self._config, self._host, self._port)\n        self._server_ready.set()\n        self._server.serve_forever()\n\n    def make_call(self, method, path, port, timeout=0.5):\n        self._server_ready.wait()\n        return method('http://{host}:{port}{path}'.format(\n            path=path, host=self._host, port=port), timeout=timeout)\n\n    def shutdown(self):\n        if self._server is not None:\n            self._server.server.shutdown()\n\n\n@pytest.fixture\ndef config():\n    return Config()\n\n\n@pytest.fixture()\ndef unused_tcp_port():\n    with contextlib.closing(socket.socket()) as sock:\n        sock.bind(('127.0.0.1', 0))\n        return sock.getsockname()[1]\n\n\n@pytest.fixture()\ndef http_session():\n    session = requests.Session()\n    retry = Retry(\n        # How many connection-related errors to retry on.\n        connect=10,\n        # A backoff factor to apply between attempts after the second try.\n        backoff_factor=2,\n        allowed_methods=['GET', 'POST', 'PUT'],\n    )\n    session.mount('http://', HTTPAdapter(max_retries=retry))\n    return HTTPFetcher(session)\n\n\nclass HTTPFetcher(object):\n    def __init__(self, session):\n        self.session = session\n\n    def json_get(self, url):\n        response = self.session.get(url)\n        response.raise_for_status()\n        return json.loads(response.content)\n\n\n@pytest.fixture()\ndef local_server_factory(unused_tcp_port):\n    threaded_server = ThreadedLocalServer(unused_tcp_port)\n\n    def create_server(app_object, config):\n        threaded_server.configure(app_object, config)\n        threaded_server.start()\n        return threaded_server, unused_tcp_port\n\n    try:\n        yield create_server\n    finally:\n        threaded_server.shutdown()\n\n\n@pytest.fixture\ndef sample_app():\n    demo = app.Chalice('demo-app')\n\n    thread_safety_check = []\n    lock = Lock()\n\n    @demo.route('/', methods=['GET'])\n    def index():\n        return {'hello': 'world'}\n\n    @demo.route('/test-cors', methods=['POST'], cors=True)\n    def test_cors():\n        return {'hello': 'world'}\n\n    @demo.route('/count', methods=['POST'])\n    def record_counter():\n        # An extra delay helps ensure we consistently fail if we're\n        # not thread safe.\n        time.sleep(0.001)\n        count = int(demo.current_request.json_body['counter'])\n        with lock:\n            thread_safety_check.append(count)\n\n    @demo.route('/count', methods=['GET'])\n    def get_record_counter():\n        return thread_safety_check[:]\n\n    return demo\n\n\ndef test_has_thread_safe_current_request(config, sample_app,\n                                         local_server_factory):\n    local_server, port = local_server_factory(sample_app, config)\n    local_server.wait_for_server_ready()\n\n    num_requests = 25\n    num_threads = 5\n\n    # The idea here is that each requests.post() has a unique 'counter'\n    # integer.  If the current request is thread safe we should see a number\n    # for each 0 - (num_requests * num_threads).  If it's not thread safe\n    # we'll see missing numbers and/or duplicates.\n    def make_requests(counter_start):\n        for i in range(counter_start * num_requests,\n                       (counter_start + 1) * num_requests):\n            # We're slowing the sending rate down a bit.  The threaded\n            # http server is good, but not great.  You can still overwhelm\n            # it pretty easily.\n            time.sleep(0.001)\n            requests.post(\n                'http://localhost:%s/count' % port, json={'counter': i})\n\n    threads = []\n    for i in range(num_threads):\n        threads.append(Thread(target=make_requests, args=(i,)))\n    for thread in threads:\n        thread.start()\n    for thread in threads:\n        thread.join()\n    response = requests.get('http://localhost:%s/count' % port)\n    assert len(response.json()) == len(range(num_requests * num_threads))\n    assert sorted(response.json()) == list(range(num_requests * num_threads))\n\n\ndef test_can_accept_get_request(config, sample_app, local_server_factory):\n    local_server, port = local_server_factory(sample_app, config)\n    response = local_server.make_call(requests.get, '/', port)\n    assert response.status_code == 200\n    assert response.text == '{\"hello\":\"world\"}'\n\n\ndef test_can_get_unicode_string_content_length(\n        config, local_server_factory):\n    demo = app.Chalice('app-name')\n\n    @demo.route('/')\n    def index_view():\n        return u'\\u2713'\n\n    local_server, port = local_server_factory(demo, config)\n    response = local_server.make_call(requests.get, '/', port)\n    assert response.headers['Content-Length'] == '3'\n\n\ndef test_can_accept_options_request(config, sample_app, local_server_factory):\n    local_server, port = local_server_factory(sample_app, config)\n    response = local_server.make_call(requests.options, '/test-cors', port)\n    assert response.headers['Content-Length'] == '0'\n    assert response.headers['Access-Control-Allow-Methods'] == 'POST,OPTIONS'\n    assert response.text == ''\n\n\ndef test_can_accept_multiple_options_request(config, sample_app,\n                                             local_server_factory):\n    local_server, port = local_server_factory(sample_app, config)\n\n    response = local_server.make_call(requests.options, '/test-cors', port)\n    assert response.headers['Content-Length'] == '0'\n    assert response.headers['Access-Control-Allow-Methods'] == 'POST,OPTIONS'\n    assert response.text == ''\n\n    response = local_server.make_call(requests.options, '/test-cors', port)\n    assert response.headers['Content-Length'] == '0'\n    assert response.headers['Access-Control-Allow-Methods'] == 'POST,OPTIONS'\n    assert response.text == ''\n\n\ndef test_can_accept_multiple_connections(config, sample_app,\n                                         local_server_factory):\n    # When a GET request is made to Chalice from a browser, it will send the\n    # connection keep-alive header in order to hold the connection open and\n    # reuse it for subsequent requests. If the conncetion close header is sent\n    # back by the server the connection will be closed, but the browser will\n    # reopen a new connection just in order to have it ready when needed.\n    # In this case, since it does not send any content we do not have the\n    # opportunity to send a connection close header back in a response to\n    # force it to close the socket.\n    # This is an issue in Chalice since the single threaded local server will\n    # now be blocked waiting for IO from the browser socket. If a request from\n    # any other source is made it will be blocked until the browser sends\n    # another request through, giving us a chance to read from another socket.\n    local_server, port = local_server_factory(sample_app, config)\n    local_server.wait_for_server_ready()\n    # We create a socket here to emulate a browser's open connection and then\n    # make a request. The request should succeed.\n    socket.create_connection(('localhost', port), timeout=1)\n    try:\n        response = local_server.make_call(requests.get, '/', port)\n    except requests.exceptions.ReadTimeout:\n        assert False, (\n            'Read timeout occurred, the socket is blocking the next request '\n            'from going though.'\n        )\n    assert response.status_code == 200\n    assert response.text == '{\"hello\":\"world\"}'\n\n\ndef test_can_import_env_vars(unused_tcp_port, http_session):\n    with cd(ENV_APP_DIR):\n        p = subprocess.Popen(['chalice', 'local', '--port',\n                              str(unused_tcp_port)],\n                             stdout=subprocess.PIPE,\n                             stderr=subprocess.PIPE)\n        _wait_for_server_ready(p)\n        try:\n            _assert_env_var_loaded(unused_tcp_port, http_session)\n        finally:\n            p.terminate()\n\n\ndef _wait_for_server_ready(process):\n    if process.poll() is not None:\n        raise AssertionError(\n            'Local server immediately exited with rc: %s' % process.poll()\n        )\n\n\ndef _assert_env_var_loaded(port_number, http_session):\n    response = http_session.json_get('http://localhost:%s/' % port_number)\n    assert response == {'hello': 'bar'}\n\n\ndef test_can_reload_server(unused_tcp_port, basic_app, http_session):\n    with cd(basic_app):\n        p = subprocess.Popen(['chalice', 'local', '--port',\n                              str(unused_tcp_port)],\n                             stdout=subprocess.PIPE,\n                             stderr=subprocess.PIPE)\n        _wait_for_server_ready(p)\n        url = 'http://localhost:%s/' % unused_tcp_port\n        try:\n            assert http_session.json_get(url) == {'version': 'original'}\n            # Updating the app should trigger a reload.\n            with open(os.path.join(basic_app, 'app.py'), 'w') as f:\n                f.write(NEW_APP_VERSION)\n            time.sleep(2)\n            assert http_session.json_get(url) == {'version': 'reloaded'}\n        finally:\n            p.terminate()\n"
  },
  {
    "path": "tests/functional/test_package.py",
    "content": "import os\nimport zipfile\nimport tarfile\nimport io\nfrom unittest import mock\nfrom collections import defaultdict, namedtuple\n\nimport pytest\n\nfrom chalice.awsclient import TypedAWSClient\nfrom chalice.config import Config\nfrom chalice import Chalice\nfrom chalice import package\nfrom chalice.deploy.packager import PipRunner\nfrom chalice.deploy.packager import DependencyBuilder\nfrom chalice.deploy.packager import Package\nfrom chalice.deploy.packager import MissingDependencyError\nfrom chalice.deploy.packager import SubprocessPip\nfrom chalice.deploy.packager import SDistMetadataFetcher\nfrom chalice.deploy.packager import InvalidSourceDistributionNameError\nfrom chalice.deploy.packager import UnsupportedPackageError\nfrom chalice.compat import pip_no_compile_c_env_vars\nfrom chalice.compat import pip_no_compile_c_shim\nfrom chalice.package import PackageOptions\nfrom chalice.utils import OSUtils\n\n\nFakePipCall = namedtuple('FakePipEntry', ['args', 'env_vars', 'shim'])\n\n\ndef _create_app_structure(tmpdir):\n    appdir = tmpdir.mkdir('app')\n    appdir.join('app.py').write('# Test app')\n    appdir.mkdir('.chalice')\n    return appdir\n\n\ndef sample_app():\n    app = Chalice(\"sample_app\")\n\n    @app.route('/')\n    def index():\n        return {\"hello\": \"world\"}\n\n    return app\n\n\n@pytest.fixture\ndef sdist_reader():\n    return SDistMetadataFetcher()\n\n\n@pytest.fixture\ndef sdist_builder():\n    s = FakeSdistBuilder()\n    return s\n\n\nclass FakeSdistBuilder(object):\n    _SETUP_PY = (\n        'from setuptools import setup\\n'\n        'setup(\\n'\n        '    name=\"%s\",\\n'\n        '    version=\"%s\"\\n'\n        ')\\n'\n    )\n\n    def write_fake_sdist(self, directory, name, version):\n        filename = '%s-%s.zip' % (name, version)\n        path = '%s/%s' % (directory, filename)\n        with zipfile.ZipFile(path, 'w',\n                             compression=zipfile.ZIP_DEFLATED) as z:\n            z.writestr('sdist/setup.py', self._SETUP_PY % (name, version))\n        return directory, filename\n\n\nclass PathArgumentEndingWith(object):\n    def __init__(self, filename):\n        self._filename = filename\n\n    def __eq__(self, other):\n        if isinstance(other, str):\n            filename = os.path.split(other)[-1]\n            return self._filename == filename\n        return False\n\n\nclass FakePip(object):\n    def __init__(self):\n        self._calls = defaultdict(lambda: [])\n        self._call_history = []\n        self._side_effects = defaultdict(lambda: [])\n        self._return_tuple = (0, b'', b'')\n\n    def main(self, args, env_vars=None, shim=None):\n        cmd, args = args[0], args[1:]\n        self._calls[cmd].append((args, env_vars, shim))\n        try:\n            side_effects = self._side_effects[cmd].pop(0)\n            for side_effect in side_effects:\n                self._call_history.append((\n                    FakePipCall(args, env_vars, shim),\n                    FakePipCall(side_effect.expected_args,\n                                side_effect.expected_env_vars,\n                                side_effect.expected_shim)))\n                side_effect.execute(args)\n        except IndexError:\n            pass\n        return self._return_tuple\n\n    def set_return_tuple(self, rc, out, err):\n        self._return_tuple = (rc, out, err)\n\n    def packages_to_download(self, expected_args, packages, whl_contents=None):\n        side_effects = [PipSideEffect(pkg,\n                                      '--dest',\n                                      expected_args,\n                                      whl_contents)\n                        for pkg in packages]\n        self._side_effects['download'].append(side_effects)\n\n    def wheels_to_build(self, expected_args, wheels_to_build,\n                        expected_env_vars=None, expected_shim=None):\n        # The SubprocessPip class handles injecting the\n        # subprocess_python_base_environ into the env vars if needed,\n        # so at this level of abstraction the env vars just default\n        # to an empty dict if None is provided.\n        if expected_env_vars is None:\n            expected_env_vars = {}\n        if expected_shim is None:\n            expected_shim = ''\n        side_effects = [PipSideEffect(pkg, '--wheel-dir', expected_args,\n                                      expected_env_vars=expected_env_vars,\n                                      expected_shim=expected_shim)\n                        for pkg in wheels_to_build]\n        self._side_effects['wheel'].append(side_effects)\n\n    @property\n    def calls(self):\n        return self._calls\n\n    def validate(self):\n        for calls in self._call_history:\n            actual_call, expected_call = calls\n            assert actual_call.args == expected_call.args\n            assert actual_call.env_vars == expected_call.env_vars\n            assert actual_call.shim == expected_call.shim\n\n\nclass PipSideEffect(object):\n    def __init__(self, filename, dirarg, expected_args, whl_contents=None,\n                 expected_env_vars=None, expected_shim=None):\n        self._filename = filename\n        self._package_name = filename.split('-')[0]\n        self._dirarg = dirarg\n        self.expected_args = expected_args\n        self.expected_env_vars = expected_env_vars\n        self.expected_shim = expected_shim\n        if whl_contents is None:\n            whl_contents = ['{package_name}/placeholder']\n        self._whl_contents = whl_contents\n\n    def _build_fake_whl(self, directory, filename):\n        filepath = os.path.join(directory, filename)\n        if not os.path.isfile(filepath):\n            package = Package(directory, filename)\n            with zipfile.ZipFile(filepath, 'w') as z:\n                for content_path in self._whl_contents:\n                    z.writestr(content_path.format(\n                        package_name=self._package_name,\n                        data_dir=package.data_dir\n                    ), b'')\n\n    def _build_fake_sdist(self, filepath):\n        # tar.gz is the same no reason to test it here as it is tested in\n        # unit.deploy.TestSdistMetadataFetcher\n        assert filepath.endswith('.zip')\n        components = os.path.split(filepath)\n        prefix, filename = components[:-1], components[-1]\n        directory = os.path.join(*prefix)\n        filename_without_ext = filename[:-4]\n        pkg_name, pkg_version = filename_without_ext.split('-')\n        builder = FakeSdistBuilder()\n        builder.write_fake_sdist(directory, pkg_name, pkg_version)\n\n    def execute(self, args):\n        \"\"\"Generate the file in the target_dir.\"\"\"\n        if self._dirarg:\n            target_dir = None\n            for i, arg in enumerate(args):\n                if arg == self._dirarg:\n                    target_dir = args[i+1]\n            if target_dir:\n                filepath = os.path.join(target_dir, self._filename)\n                if filepath.endswith('.whl'):\n                    self._build_fake_whl(target_dir, self._filename)\n                else:\n                    self._build_fake_sdist(filepath)\n\n\n@pytest.fixture\ndef osutils():\n    return OSUtils()\n\n\n@pytest.fixture\ndef empty_env_osutils():\n    class EmptyEnv(object):\n        def environ(self):\n            return {}\n    return EmptyEnv()\n\n\n@pytest.fixture\ndef pip_runner(empty_env_osutils):\n    pip = FakePip()\n    pip_runner = PipRunner(pip, osutils=empty_env_osutils)\n    return pip, pip_runner\n\n\nclass TestDependencyBuilder(object):\n    def _write_requirements_txt(self, packages, directory):\n        contents = '\\n'.join(packages)\n        filepath = os.path.join(directory, 'requirements.txt')\n        with open(filepath, 'w') as f:\n            f.write(contents)\n\n    def _make_appdir_and_dependency_builder(self, reqs, tmpdir, runner):\n        appdir = str(_create_app_structure(tmpdir))\n        self._write_requirements_txt(reqs, appdir)\n        builder = DependencyBuilder(OSUtils(), runner)\n        return appdir, builder\n\n    def test_can_build_local_dir_as_whl(self, tmpdir, pip_runner):\n        reqs = ['../foo']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        pip.set_return_tuple(0, (b\"Processing ../foo\\n\"\n                                 b\"  Link is a directory,\"\n                                 b\" ignoring download_dir\"), b'')\n        pip.wheels_to_build(\n            expected_args=['--no-deps', '--wheel-dir', mock.ANY, '../foo'],\n            wheels_to_build=[\n                'foo-1.2-cp36-none-any.whl'\n            ]\n        )\n\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        builder.build_site_packages('cp36m', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        pip.validate()\n        assert ['foo'] == installed_packages\n\n    def test_can_get_sdist_if_missing_initially(self, tmpdir, pip_runner):\n        reqs = ['foo']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        # Initial download yields  an incompatible wheel\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=[\n                'foo-1.2-cp36-cp36m-macosx_10_6_intel.whl'\n            ]\n        )\n        # Secondary download for a compatible only one fails\n        pip.packages_to_download(\n            expected_args=[\n                '--only-binary=:all:', '--no-deps', '--platform',\n                'manylinux2014_x86_64', '--implementation', 'cp',\n                '--abi', 'cp36m', '--dest', mock.ANY,\n                'foo==1.2'\n            ],\n            packages=[]\n        )\n        # Third download for an sdist succeeds\n        pip.packages_to_download(\n            expected_args=[\n                '--no-binary=:all:', '--no-deps', '--dest', mock.ANY,\n                'foo==1.2'\n            ],\n            packages=[\n                'foo-1.2.zip',\n            ]\n        )\n        # Wheel successfully builds\n        pip.wheels_to_build(\n            expected_args=['--no-deps', '--wheel-dir', mock.ANY,\n                           PathArgumentEndingWith('foo-1.2.zip')],\n            wheels_to_build=[\n                'foo-1.2-cp36-none-any.whl'\n            ]\n        )\n\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        builder.build_site_packages('cp36m', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        pip.validate()\n        for req in reqs:\n            assert req in installed_packages\n\n    def test_can_get_whls_all_manylinux(self, tmpdir, pip_runner):\n        reqs = ['foo', 'bar']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=[\n                'foo-1.2-cp36-cp36m-manylinux1_x86_64.whl',\n                'bar-1.2-cp36-cp36m-manylinux1_x86_64.whl'\n            ]\n        )\n\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        builder.build_site_packages('cp36m', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        pip.validate()\n        for req in reqs:\n            assert req in installed_packages\n\n    def test_can_support_new_wheel_tags(self, tmpdir, pip_runner):\n        reqs = ['numpy']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        # This is the actual filename from numpy v1.20.3.\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=[\n                'numpy-1.20.3-cp37-cp37m-manylinux_2_12_x86_64.whl',\n            ]\n        )\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        builder.build_site_packages('cp37m', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        pip.validate()\n        for req in reqs:\n            assert req in installed_packages\n\n    def test_can_support_compressed_tags(self, tmpdir, pip_runner):\n        reqs = ['numpy']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        # This is the actual filename from numpy v1.20.3.\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=[\n                'numpy-1.20.3-cp37-cp37m-manylinux_2_12_x86_64'\n                '.manylinux2010_x86_64.whl',\n            ]\n        )\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        builder.build_site_packages('cp37m', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        pip.validate()\n        for req in reqs:\n            assert req in installed_packages\n\n    def test_can_use_abi3_whl_for_any_python3(self, tmpdir, pip_runner):\n        reqs = ['foo', 'bar', 'baz', 'qux']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=[\n                'foo-1.2-cp33-abi3-manylinux1_x86_64.whl',\n                'bar-1.2-cp34-abi3-manylinux1_x86_64.whl',\n                'baz-1.2-cp35-abi3-manylinux1_x86_64.whl',\n                'qux-1.2-cp36-abi3-manylinux1_x86_64.whl',\n            ]\n        )\n\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        builder.build_site_packages('cp36m', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        pip.validate()\n        for req in reqs:\n            assert req in installed_packages\n\n    def test_can_expand_purelib_whl(self, tmpdir, pip_runner):\n        reqs = ['foo']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=[\n                'foo-1.2-cp36-cp36m-manylinux1_x86_64.whl'\n            ],\n            whl_contents=['foo-1.2.data/purelib/foo/']\n        )\n\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        builder.build_site_packages('cp36m', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        pip.validate()\n        for req in reqs:\n            assert req in installed_packages\n\n    def test_can_normalize_dirname_for_purelib_whl(self, tmpdir, pip_runner):\n        reqs = ['foo']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=[\n                'foo-1.2-cp36-cp36m-manylinux1_x86_64.whl'\n            ],\n            whl_contents=['Foo-1.2.data/purelib/foo/']\n        )\n\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        builder.build_site_packages('cp36m', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        pip.validate()\n        for req in reqs:\n            assert req in installed_packages\n\n    def test_can_expand_platlib_whl(self, tmpdir, pip_runner):\n        reqs = ['foo']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=[\n                'foo-1.2-cp36-cp36m-manylinux1_x86_64.whl'\n            ],\n            whl_contents=['Foo-1.2.data/platlib/foo/']\n        )\n\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        builder.build_site_packages('cp36m', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        pip.validate()\n        for req in reqs:\n            assert req in installed_packages\n\n    def test_can_expand_platlib_and_purelib(self, tmpdir, pip_runner):\n        # This wheel installs two importable libraries foo and bar, one from\n        # the wheels purelib and one from its platlib.\n        reqs = ['foo', 'bar']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=[\n                'foo-1.2-cp36-cp36m-manylinux1_x86_64.whl'\n            ],\n            whl_contents=[\n                'foo-1.2.data/platlib/foo/',\n                'foo-1.2.data/purelib/bar/'\n            ]\n        )\n\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        builder.build_site_packages('cp36m', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        pip.validate()\n        for req in reqs:\n            assert req in installed_packages\n\n    def test_does_ignore_data(self, tmpdir, pip_runner):\n        # Make sure the wheel installer does not copy the data directory\n        # up to the root.\n        reqs = ['foo']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=[\n                'foo-1.2-cp36-cp36m-manylinux1_x86_64.whl'\n            ],\n            whl_contents=[\n                'foo/placeholder',\n                'foo-1.2.data/data/bar/'\n            ]\n        )\n\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        builder.build_site_packages('cp36m', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        pip.validate()\n        for req in reqs:\n            assert req in installed_packages\n        assert 'bar' not in installed_packages\n\n    def test_does_ignore_include(self, tmpdir, pip_runner):\n        # Make sure the wheel installer does not copy the includes directory\n        # up to the root.\n        reqs = ['foo']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=[\n                'foo-1.2-cp36-cp36m-manylinux1_x86_64.whl'\n            ],\n            whl_contents=[\n                'foo/placeholder',\n                'foo.1.2.data/includes/bar/'\n            ]\n        )\n\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        builder.build_site_packages('cp36m', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        pip.validate()\n        for req in reqs:\n            assert req in installed_packages\n        assert 'bar' not in installed_packages\n\n    def test_does_ignore_scripts(self, tmpdir, pip_runner):\n        # Make sure the wheel isntaller does not copy the scripts directory\n        # up to the root.\n        reqs = ['foo']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=[\n                'foo-1.2-cp36-cp36m-manylinux1_x86_64.whl'\n            ],\n            whl_contents=[\n                '{package_name}/placeholder',\n                '{data_dir}/scripts/bar/placeholder'\n            ]\n        )\n\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        builder.build_site_packages('cp36m', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        pip.validate()\n        for req in reqs:\n            assert req in installed_packages\n        assert 'bar' not in installed_packages\n\n    def test_can_expand_platlib_and_platlib_and_root(self, tmpdir, pip_runner):\n        # This wheel installs three import names foo, bar and baz.\n        # they are from the root install directory and the platlib and purelib\n        # subdirectories in the platlib.\n        reqs = ['foo', 'bar', 'baz']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=[\n                'foo-1.2-cp36-cp36m-manylinux1_x86_64.whl'\n            ],\n            whl_contents=[\n                '{package_name}/placeholder',\n                '{data_dir}/platlib/bar/placeholder',\n                '{data_dir}/purelib/baz/placeholder'\n            ]\n        )\n\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        builder.build_site_packages('cp36m', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        pip.validate()\n        for req in reqs:\n            assert req in installed_packages\n\n    def test_can_get_whls_mixed_compat(self, tmpdir, osutils, pip_runner):\n        reqs = ['foo', 'bar', 'baz']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=[\n                'foo-1.0-cp36-none-any.whl',\n                'bar-1.2-cp36-cp36m-manylinux1_x86_64.whl',\n                'baz-1.5-cp36-cp36m-linux_x86_64.whl'\n            ]\n        )\n\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        builder.build_site_packages('cp36m', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        pip.validate()\n        for req in reqs:\n            assert req in installed_packages\n\n    def test_can_get_py27_whls(self, tmpdir, osutils, pip_runner):\n        reqs = ['foo', 'bar', 'baz']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=[\n                'foo-1.0-cp27-none-any.whl',\n                'bar-1.2-cp27-none-manylinux1_x86_64.whl',\n                'baz-1.5-cp27-cp27mu-linux_x86_64.whl'\n            ]\n        )\n\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        builder.build_site_packages('cp27mu', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        pip.validate()\n        for req in reqs:\n            assert req in installed_packages\n\n    def test_does_fail_on_invalid_local_package(self, tmpdir, osutils,\n                                                pip_runner):\n        reqs = ['../foo']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        pip.set_return_tuple(0, (b\"Processing ../foo\\n\"\n                                 b\"  Link is a directory,\"\n                                 b\" ignoring download_dir\"), b'')\n        pip.wheels_to_build(\n            expected_args=['--no-deps', '--wheel-dir', mock.ANY, '../foo'],\n            wheels_to_build=[\n                'foo-1.2-cp36-cp36m-macosx_10_6_intel.whl'\n            ]\n        )\n\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        with pytest.raises(MissingDependencyError) as e:\n            builder.build_site_packages(\n                'cp36m', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n        missing_packages = list(e.value.missing)\n\n        pip.validate()\n        assert len(missing_packages) == 1\n        assert missing_packages[0].identifier == 'foo==1.2'\n        assert len(installed_packages) == 0\n\n    def test_does_fail_on_narrow_py27_unicode(self, tmpdir, osutils,\n                                              pip_runner):\n        reqs = ['baz']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=[\n                'baz-1.5-cp27-cp27m-linux_x86_64.whl'\n            ]\n        )\n\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        with pytest.raises(MissingDependencyError) as e:\n            builder.build_site_packages(\n                'cp27mu', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        missing_packages = list(e.value.missing)\n        pip.validate()\n        assert len(missing_packages) == 1\n        assert missing_packages[0].identifier == 'baz==1.5'\n        assert len(installed_packages) == 0\n\n    def test_does_fail_on_python_1_whl(self, tmpdir, osutils, pip_runner):\n        reqs = ['baz']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=[\n                'baz-1.5-cp14-cp14m-linux_x86_64.whl'\n            ]\n        )\n\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        with pytest.raises(MissingDependencyError) as e:\n            builder.build_site_packages(\n                'cp36m', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        missing_packages = list(e.value.missing)\n        pip.validate()\n        assert len(missing_packages) == 1\n        assert missing_packages[0].identifier == 'baz==1.5'\n        assert len(installed_packages) == 0\n\n    def test_can_replace_incompat_whl(self, tmpdir, osutils, pip_runner):\n        reqs = ['foo', 'bar']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=[\n                'foo-1.0-cp36-none-any.whl',\n                'bar-1.2-cp36-cp36m-macosx_10_6_intel.whl'\n            ]\n        )\n        # Once the initial download has 1 incompatible whl file. The second,\n        # more targeted download, finds manylinux1_x86_64 and downloads that.\n        pip.packages_to_download(\n            expected_args=[\n                '--only-binary=:all:', '--no-deps', '--platform',\n                'manylinux2014_x86_64', '--implementation', 'cp',\n                '--abi', 'cp36m', '--dest', mock.ANY,\n                'bar==1.2'\n            ],\n            packages=[\n                'bar-1.2-cp36-cp36m-manylinux1_x86_64.whl'\n            ]\n        )\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        builder.build_site_packages('cp36m', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        pip.validate()\n        for req in reqs:\n            assert req in installed_packages\n\n    @pytest.mark.parametrize(\n        'package,package_filename', [\n            # package: The name you would provide in requirements.txt\n            # package_filename: The package name used in the .whl file.\n            ('sqlalchemy', 'SQLAlchemy'),\n            ('pyyaml', 'PyYAML'),\n        ]\n    )\n    def test_whitelist_sqlalchemy(self, tmpdir, osutils, pip_runner,\n                                  package, package_filename):\n        reqs = ['%s==1.1.18' % package]\n        abi = 'cp36m'\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=[\n                '%s-1.1.18-cp36-cp36m-macosx_10_11_x86_64.whl'\n                % package_filename\n            ]\n        )\n        pip.packages_to_download(\n            expected_args=[\n                '--only-binary=:all:', '--no-deps', '--platform',\n                'manylinux2014_x86_64', '--implementation', 'cp',\n                '--abi', abi, '--dest', mock.ANY,\n                '%s==1.1.18' % package\n            ],\n            packages=[\n                '%s-1.1.18-cp36-cp36m-macosx_10_11_x86_64.whl'\n                % package_filename\n            ]\n        )\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        builder.build_site_packages(abi, requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        pip.validate()\n        assert installed_packages == [package_filename]\n\n    def test_can_build_sdist(self, tmpdir, osutils, pip_runner):\n        reqs = ['foo', 'bar']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=[\n                'foo-1.2.zip',\n                'bar-1.2-cp36-cp36m-manylinux1_x86_64.whl'\n            ]\n        )\n        # Foo is built from and is pure python so it yields a compatible\n        # wheel file.\n        pip.wheels_to_build(\n            expected_args=['--no-deps', '--wheel-dir', mock.ANY,\n                           PathArgumentEndingWith('foo-1.2.zip')],\n            wheels_to_build=[\n                'foo-1.2-cp36-none-any.whl'\n            ]\n        )\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        builder.build_site_packages('cp36m', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        pip.validate()\n        for req in reqs:\n            assert req in installed_packages\n\n    def test_build_sdist_makes_incompatible_whl(self, tmpdir, osutils,\n                                                pip_runner):\n        reqs = ['foo', 'bar']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=[\n                'foo-1.2.zip',\n                'bar-1.2-cp36-cp36m-manylinux1_x86_64.whl'\n            ]\n        )\n        # foo is compiled since downloading it failed to get any wheels. And\n        # the second download for manylinux1_x86_64 wheels failed as well.\n        # building in this case yields a platform specific wheel file that is\n        # not compatible. In this case currently there is nothing that chalice\n        # can do to install this package.\n        pip.wheels_to_build(\n            expected_args=['--no-deps', '--wheel-dir', mock.ANY,\n                           PathArgumentEndingWith('foo-1.2.zip')],\n            wheels_to_build=[\n                'foo-1.2-cp36-cp36m-macosx_10_6_intel.whl'\n            ]\n        )\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        with pytest.raises(MissingDependencyError) as e:\n            builder.build_site_packages(\n                'cp36m', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        # bar should succeed and foo should failed.\n        missing_packages = list(e.value.missing)\n        pip.validate()\n        assert len(missing_packages) == 1\n        assert missing_packages[0].identifier == 'foo==1.2'\n        assert installed_packages == ['bar']\n\n    def test_can_build_package_with_optional_c_speedups_and_no_wheel(\n            self, tmpdir, osutils, pip_runner):\n        reqs = ['foo']\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        # In this scenario we are downloading a package that has no wheel files\n        # at all, and optional c speedups. The initial download will yield an\n        # sdist since there were no wheels.\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=['foo-1.2.zip']\n        )\n\n        # Chalice should now try and build this into a wheel file. Since it has\n        # optional c speedups it will build a platform dependent wheel file\n        # which is not compatible with lambda.\n        pip.wheels_to_build(\n            expected_args=['--no-deps', '--wheel-dir', mock.ANY,\n                           PathArgumentEndingWith('foo-1.2.zip')],\n            wheels_to_build=[\n                'foo-1.2-cp36-cp36m-macosx_10_6_intel.whl'\n            ]\n        )\n\n        # Now chalice should make a last ditch effort to build the package by\n        # trying once again to build the sdist, but this time it will prevent\n        # c extensions from compiling by force. If the package had optional\n        # c speedups (which in this scenario it did) then it will\n        # successfully fall back to building a pure python wheel file.\n        pip.wheels_to_build(\n            expected_args=['--no-deps', '--wheel-dir', mock.ANY,\n                           PathArgumentEndingWith('foo-1.2.zip')],\n            expected_env_vars=pip_no_compile_c_env_vars,\n            expected_shim=pip_no_compile_c_shim,\n            wheels_to_build=[\n                'foo-1.2-cp36-none-any.whl'\n            ]\n        )\n\n        site_packages = os.path.join(appdir, '.chalice.', 'site-packages')\n        builder.build_site_packages('cp36m', requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        # Now we should have successfully built the foo package.\n        pip.validate()\n        assert installed_packages == ['foo']\n\n    def test_build_into_existing_dir_with_preinstalled_packages(\n            self, tmpdir, osutils, pip_runner):\n        # Same test as above so we should get foo failing and bar succeeding\n        # but in this test we started with a .chalice/site-packages directory\n        # with both foo and bar already installed. It should still fail since\n        # they may be there by happenstance, or from an incompatible version\n        # of python.\n        reqs = ['foo', 'bar']\n        abi = 'cp36m'\n        pip, runner = pip_runner\n        appdir, builder = self._make_appdir_and_dependency_builder(\n            reqs, tmpdir, runner)\n        requirements_file = os.path.join(appdir, 'requirements.txt')\n        pip.packages_to_download(\n            expected_args=['-r', requirements_file, '--dest', mock.ANY],\n            packages=[\n                'foo-1.2.zip',\n                'bar-1.2-cp36-cp36m-manylinux1_x86_64.whl'\n            ]\n        )\n        pip.packages_to_download(\n            expected_args=[\n                '--only-binary=:all:', '--no-deps', '--platform',\n                'manylinux2014_x86_64', '--implementation', 'cp',\n                '--abi', abi, '--dest', mock.ANY,\n                'foo==1.2'\n            ],\n            packages=[\n                'foo-1.2-cp36-cp36m-macosx_10_6_intel.whl'\n            ]\n        )\n\n        # Add two fake packages foo and bar that have previously been\n        # installed in the site-packages directory.\n        site_packages = os.path.join(appdir, '.chalice', 'site-packages')\n        foo = os.path.join(site_packages, 'foo')\n        os.makedirs(foo)\n        bar = os.path.join(site_packages, 'bar')\n        os.makedirs(bar)\n        with pytest.raises(MissingDependencyError) as e:\n            builder.build_site_packages(\n                abi, requirements_file, site_packages)\n        installed_packages = os.listdir(site_packages)\n\n        # bar should succeed and foo should failed.\n        missing_packages = list(e.value.missing)\n        pip.validate()\n        assert len(missing_packages) == 1\n        assert missing_packages[0].identifier == 'foo==1.2'\n        assert installed_packages == ['bar']\n\n\ndef test_can_create_app_packager_with_no_autogen(tmpdir, stubbed_session):\n    appdir = _create_app_structure(tmpdir)\n\n    outdir = tmpdir.mkdir('outdir')\n    default_params = {'autogen_policy': True}\n    config = Config.create(project_dir=str(appdir),\n                           chalice_app=sample_app(),\n                           **default_params)\n    options = PackageOptions(TypedAWSClient(session=stubbed_session))\n    p = package.create_app_packager(config, options)\n    p.package_app(config, str(outdir), 'dev')\n    # We're not concerned with the contents of the files\n    # (those are tested in the unit tests), we just want to make\n    # sure they're written to disk and look (mostly) right.\n    contents = os.listdir(str(outdir))\n    assert 'deployment.zip' in contents\n    assert 'sam.json' in contents\n\n\ndef test_can_create_app_packager_with_yaml_extention(tmpdir, stubbed_session):\n    appdir = _create_app_structure(tmpdir)\n\n    outdir = tmpdir.mkdir('outdir')\n    default_params = {'autogen_policy': True}\n    extras_file = tmpdir.join('extras.yaml')\n    extras_file.write(\"foo: bar\")\n    config = Config.create(project_dir=str(appdir),\n                           chalice_app=sample_app(),\n                           **default_params)\n    options = PackageOptions(TypedAWSClient(session=stubbed_session))\n    p = package.create_app_packager(config, options,\n                                    merge_template=str(extras_file))\n\n    p.package_app(config, str(outdir), 'dev')\n    contents = os.listdir(str(outdir))\n    assert 'deployment.zip' in contents\n    assert 'sam.yaml' in contents\n\n\ndef test_can_specify_yaml_output(tmpdir, stubbed_session):\n    appdir = _create_app_structure(tmpdir)\n\n    outdir = tmpdir.mkdir('outdir')\n    default_params = {'autogen_policy': True}\n    config = Config.create(project_dir=str(appdir),\n                           chalice_app=sample_app(),\n                           **default_params)\n    options = PackageOptions(TypedAWSClient(session=stubbed_session))\n    p = package.create_app_packager(config, options, template_format='yaml')\n\n    p.package_app(config, str(outdir), 'dev')\n    contents = os.listdir(str(outdir))\n    assert 'deployment.zip' in contents\n    assert 'sam.yaml' in contents\n\n\ndef test_will_create_outdir_if_needed(tmpdir, stubbed_session):\n    appdir = _create_app_structure(tmpdir)\n    outdir = str(appdir.join('outdir'))\n    default_params = {'autogen_policy': True}\n    config = Config.create(project_dir=str(appdir),\n                           chalice_app=sample_app(),\n                           **default_params)\n    options = PackageOptions(TypedAWSClient(session=stubbed_session))\n    p = package.create_app_packager(config, options)\n    p.package_app(config, str(outdir), 'dev')\n    contents = os.listdir(str(outdir))\n    assert 'deployment.zip' in contents\n    assert 'sam.json' in contents\n\n\ndef test_includes_layer_package_with_sam(tmpdir, stubbed_session):\n    appdir = _create_app_structure(tmpdir)\n    appdir.mkdir('vendor').join('hello').write('hello\\n')\n    outdir = str(appdir.join('outdir'))\n    default_params = {'autogen_policy': True}\n    config = Config.create(project_dir=str(appdir),\n                           chalice_app=sample_app(),\n                           automatic_layer=True,\n                           **default_params)\n    options = PackageOptions(TypedAWSClient(session=stubbed_session))\n    p = package.create_app_packager(config, options)\n    p.package_app(config, str(outdir), 'dev')\n    contents = os.listdir(str(outdir))\n    assert 'deployment.zip' in contents\n    assert 'layer-deployment.zip' in contents\n    assert 'sam.json' in contents\n\n\ndef test_includes_layer_package_with_terraform(tmpdir, stubbed_session):\n    appdir = _create_app_structure(tmpdir)\n    appdir.mkdir('vendor').join('hello').write('hello\\n')\n    outdir = str(appdir.join('outdir'))\n    default_params = {'autogen_policy': True}\n    config = Config.create(project_dir=str(appdir),\n                           chalice_app=sample_app(),\n                           automatic_layer=True,\n                           **default_params)\n    options = PackageOptions(TypedAWSClient(session=stubbed_session))\n    p = package.create_app_packager(config, options,\n                                    package_format='terraform')\n    p.package_app(config, str(outdir), 'dev')\n    contents = os.listdir(str(outdir))\n    assert 'deployment.zip' in contents\n    assert 'layer-deployment.zip' in contents\n    assert 'chalice.tf.json' in contents\n\n\nclass TestSubprocessPip(object):\n    def test_can_invoke_pip(self):\n        pip = SubprocessPip()\n        rc, out, err = pip.main(['--version'])\n        # Simple assertion that we can execute pip and it gives us some output\n        # and nothing on stderr.\n        print(out, err)\n        assert rc == 0\n        assert err == b''\n\n    def test_does_error_code_propagate(self):\n        pip = SubprocessPip()\n        rc, _, err = pip.main(['badcommand'])\n        assert rc != 0\n        # Don't want to depend on a particular error message from pip since it\n        # may change if we pin a differnet version to Chalice at some point.\n        # But there should be a non-empty error message of some kind.\n        assert err != b''\n\n\nclass TestSdistMetadataFetcher(object):\n    _SETUPTOOLS = 'from setuptools import setup'\n    _DISTUTILS = 'from distutils.core import setup'\n    _BOTH = (\n        'try:\\n'\n        '    from setuptools import setup\\n'\n        'except ImportError:\\n'\n        '    from distutils.core import setuptools\\n'\n    )\n\n    _SETUP_PY = (\n        '%s\\n'\n        'setup(\\n'\n        '    name=\"%s\",\\n'\n        '    version=\"%s\"\\n'\n        ')\\n'\n    )\n    _VALID_TAR_FORMATS = ['tar.gz', 'tar.bz2']\n\n    def _write_fake_sdist(self, setup_py, directory, ext,\n                          pkg_info_contents=None):\n        filename = 'sdist.%s' % ext\n        path = '%s/%s' % (directory, filename)\n        if ext == 'zip':\n            with zipfile.ZipFile(path, 'w',\n                                 compression=zipfile.ZIP_DEFLATED) as z:\n                z.writestr('sdist/setup.py', setup_py)\n                if pkg_info_contents is not None:\n                    z.writestr('sdist/PKG-INFO', pkg_info_contents)\n        elif ext in self._VALID_TAR_FORMATS:\n            compression_format = ext.split('.')[1]\n            with tarfile.open(path, 'w:%s' % compression_format) as tar:\n                tarinfo = tarfile.TarInfo('sdist/setup.py')\n                tarinfo.size = len(setup_py)\n                tar.addfile(tarinfo, io.BytesIO(setup_py.encode()))\n                if pkg_info_contents is not None:\n                    tarinfo = tarfile.TarInfo('sdist/PKG-INFO')\n                    tarinfo.size = len(pkg_info_contents)\n                    tar.addfile(tarinfo,\n                                io.BytesIO(pkg_info_contents.encode()))\n        else:\n            open(path, 'a').close()\n        filepath = os.path.join(directory, filename)\n        return filepath\n\n    def test_setup_tar_gz(self, osutils, sdist_reader):\n        setup_py = self._SETUP_PY % (\n            self._SETUPTOOLS, 'foo', '1.0'\n        )\n        with osutils.tempdir() as tempdir:\n            filepath = self._write_fake_sdist(setup_py, tempdir, 'tar.gz')\n            name, version = sdist_reader.get_package_name_and_version(\n                filepath)\n        assert name == 'foo'\n        assert version == '1.0'\n\n    def test_setup_tar_bz2(self, osutils, sdist_reader):\n        setup_py = self._SETUP_PY % (\n            self._SETUPTOOLS, 'foo', '1.0'\n        )\n        with osutils.tempdir() as tempdir:\n            filepath = self._write_fake_sdist(setup_py, tempdir, 'tar.bz2')\n            name, version = sdist_reader.get_package_name_and_version(\n                filepath)\n        assert name == 'foo'\n        assert version == '1.0'\n\n    def test_setup_zip(self, osutils, sdist_reader):\n        setup_py = self._SETUP_PY % (\n            self._SETUPTOOLS, 'foo', '1.0'\n        )\n        with osutils.tempdir() as tempdir:\n            filepath = self._write_fake_sdist(setup_py, tempdir, 'zip')\n            name, version = sdist_reader.get_package_name_and_version(\n                filepath)\n        assert name == 'foo'\n        assert version == '1.0'\n\n    def test_distutil_tar_gz(self, osutils, sdist_reader):\n        setup_py = self._SETUP_PY % (\n            self._DISTUTILS, 'foo', '1.0'\n        )\n        with osutils.tempdir() as tempdir:\n            filepath = self._write_fake_sdist(setup_py, tempdir, 'tar.gz')\n            name, version = sdist_reader.get_package_name_and_version(\n                filepath)\n        assert name == 'foo'\n        assert version == '1.0'\n\n    def test_distutil_tar_bz2(self, osutils, sdist_reader):\n        setup_py = self._SETUP_PY % (\n            self._DISTUTILS, 'foo', '1.0'\n        )\n        with osutils.tempdir() as tempdir:\n            filepath = self._write_fake_sdist(setup_py, tempdir, 'tar.bz2')\n            name, version = sdist_reader.get_package_name_and_version(\n                filepath)\n        assert name == 'foo'\n        assert version == '1.0'\n\n    def test_distutil_zip(self, osutils, sdist_reader):\n        setup_py = self._SETUP_PY % (\n            self._DISTUTILS, 'foo', '1.0'\n        )\n        with osutils.tempdir() as tempdir:\n            filepath = self._write_fake_sdist(setup_py, tempdir, 'zip')\n            name, version = sdist_reader.get_package_name_and_version(\n                filepath)\n        assert name == 'foo'\n        assert version == '1.0'\n\n    def test_both_zip(self, osutils, sdist_reader):\n        setup_py = self._SETUP_PY % (\n            self._BOTH, 'foo', '1.0'\n        )\n        with osutils.tempdir() as tempdir:\n            filepath = self._write_fake_sdist(setup_py, tempdir, 'zip')\n            name, version = sdist_reader.get_package_name_and_version(\n                filepath)\n        assert name == 'foo'\n        assert version == '1.0'\n\n    def test_bad_format(self, osutils, sdist_reader):\n        setup_py = self._SETUP_PY % (\n            self._BOTH, 'foo', '1.0'\n        )\n        with osutils.tempdir() as tempdir:\n            filepath = self._write_fake_sdist(setup_py, tempdir, 'tar.gz2')\n            with pytest.raises(InvalidSourceDistributionNameError):\n                name, version = sdist_reader.get_package_name_and_version(\n                    filepath)\n\n    def test_cant_get_egg_info_filename(self, osutils, sdist_reader):\n        # In this scenario the setup.py file will fail with an import\n        # error so we should verify we try a fallback to look for\n        # PKG-INFO.\n        bad_setup_py = self._SETUP_PY % (\n            'import some_build_dependency', 'foo', '1.0',\n        )\n        pkg_info_file = (\n            'Name: foo\\n'\n            'Version: 1.0\\n'\n        )\n        with osutils.tempdir() as tempdir:\n            filepath = self._write_fake_sdist(bad_setup_py, tempdir,\n                                              'zip', pkg_info_file)\n            name, version = sdist_reader.get_package_name_and_version(\n                filepath)\n        assert name == 'foo'\n        assert version == '1.0'\n\n    def test_pkg_info_fallback_fails_raises_error(self, osutils, sdist_reader):\n        setup_py = self._SETUP_PY % (\n            'import build_time_dependency', 'foo', '1.0'\n        )\n        with osutils.tempdir() as tempdir:\n            filepath = self._write_fake_sdist(setup_py, tempdir, 'tar.gz')\n            with pytest.raises(UnsupportedPackageError):\n                sdist_reader.get_package_name_and_version(filepath)\n\n\nclass TestPackage(object):\n\n    def test_same_pkg_sdist_and_wheel_collide(self, osutils, sdist_builder):\n        with osutils.tempdir() as tempdir:\n            sdist_builder.write_fake_sdist(tempdir, 'foobar', '1.0')\n            pkgs = set()\n            pkgs.add(Package('', 'foobar-1.0-py3-none-any.whl'))\n            pkgs.add(Package(tempdir, 'foobar-1.0.zip'))\n            assert len(pkgs) == 1\n\n    def test_ensure_sdist_name_normalized_for_comparison(self, osutils,\n                                                         sdist_builder):\n        with osutils.tempdir() as tempdir:\n            sdist_builder.write_fake_sdist(tempdir, 'Foobar', '1.0')\n            pkgs = set()\n            pkgs.add(Package('', 'foobar-1.0-py3-none-any.whl'))\n            pkgs.add(Package(tempdir, 'Foobar-1.0.zip'))\n            assert len(pkgs) == 1\n\n    def test_ensure_wheel_name_normalized_for_comparison(self, osutils,\n                                                         sdist_builder):\n        with osutils.tempdir() as tempdir:\n            sdist_builder.write_fake_sdist(tempdir, 'foobar', '1.0')\n            pkgs = set()\n            pkgs.add(Package('', 'Foobar-1.0-py3-none-any.whl'))\n            pkgs.add(Package(tempdir, 'foobar-1.0.zip'))\n            assert len(pkgs) == 1\n"
  },
  {
    "path": "tests/functional/test_utils.py",
    "content": "import zipfile\nimport json\nimport os\nimport io\nimport tarfile\n\nimport pytest\n\nfrom chalice import utils\n\n\n@pytest.fixture\ndef osutils():\n    return utils.OSUtils()\n\n\ndef test_can_zip_single_file(tmpdir):\n    source = tmpdir.mkdir('sourcedir')\n    source.join('hello.txt').write(b'hello world')\n    outfile = str(tmpdir.join('out.zip'))\n    utils.create_zip_file(source_dir=str(source),\n                          outfile=outfile)\n    with zipfile.ZipFile(outfile) as f:\n        contents = f.read('hello.txt')\n        assert contents == b'hello world'\n        assert f.namelist() == ['hello.txt']\n\n\ndef test_can_zip_recursive_contents(tmpdir):\n    source = tmpdir.mkdir('sourcedir')\n    source.join('hello.txt').write(b'hello world')\n    subdir = source.mkdir('subdir')\n    subdir.join('sub.txt').write(b'sub.txt')\n    subdir.join('sub2.txt').write(b'sub2.txt')\n    subsubdir = subdir.mkdir('subsubdir')\n    subsubdir.join('leaf.txt').write(b'leaf.txt')\n\n    outfile = str(tmpdir.join('out.zip'))\n    utils.create_zip_file(source_dir=str(source),\n                          outfile=outfile)\n    with zipfile.ZipFile(outfile) as f:\n        assert sorted(f.namelist()) == sorted([\n            'hello.txt',\n            'subdir/sub.txt',\n            'subdir/sub2.txt',\n            'subdir/subsubdir/leaf.txt',\n        ])\n        assert f.read('subdir/subsubdir/leaf.txt') == b'leaf.txt'\n\n\ndef test_can_write_recorded_values(tmpdir):\n    filename = str(tmpdir.join('deployed.json'))\n    utils.record_deployed_values({'dev': {'deployed': 'foo'}}, filename)\n    with open(filename, 'r') as f:\n        assert json.load(f) == {'dev': {'deployed': 'foo'}}\n\n\ndef test_can_merge_recorded_values(tmpdir):\n    filename = str(tmpdir.join('deployed.json'))\n    first = {'dev': {'deployed': 'values'}}\n    second = {'prod': {'deployed': 'values'}}\n    utils.record_deployed_values(first, filename)\n    utils.record_deployed_values(second, filename)\n    combined = first.copy()\n    combined.update(second)\n    with open(filename, 'r') as f:\n        data = json.load(f)\n    assert data == combined\n\n\ndef test_can_remove_stage_from_deployed_values(tmpdir):\n    filename = str(tmpdir.join('deployed.json'))\n    deployed = {\n        'dev': {'deployed': 'values'},\n    }\n    left_after_removal = {\n        'prod': {'deployed': 'values'}\n    }\n    deployed.update(left_after_removal)\n    with open(filename, 'wb') as f:\n        f.write(json.dumps(deployed).encode('utf-8'))\n    utils.remove_stage_from_deployed_values('dev', filename)\n\n    with open(filename, 'r') as f:\n        data = json.load(f)\n    assert data == left_after_removal\n\n\ndef test_remove_stage_from_deployed_values_already_removed(tmpdir):\n    filename = str(tmpdir.join('deployed.json'))\n    deployed = {\n        'dev': {'deployed': 'values'},\n        'prod': {'deployed': 'values'}\n    }\n    with open(filename, 'wb') as f:\n        f.write(json.dumps(deployed).encode('utf-8'))\n    utils.remove_stage_from_deployed_values('fake_key', filename)\n\n    with open(filename, 'r') as f:\n        data = json.load(f)\n    assert data == deployed\n\n\ndef test_remove_stage_from_deployed_values_no_file(tmpdir):\n    filename = str(tmpdir.join('deployed.json'))\n    utils.remove_stage_from_deployed_values('fake_key', filename)\n\n    # Make sure it doesn't create the file if it didn't already exist\n    assert not os.path.isfile(filename)\n\n\ndef test_error_raised_on_tar_out_of_extract_dir(tmp_path, osutils):\n    filepath = tmp_path / 'badfile'\n    filepath.write_text('single file')\n    badtarpath = tmp_path / 'badtar.tar.gz'\n    extractdir = tmp_path / 'nest1' / 'nest2' / 'nest3'\n    with tarfile.open(badtarpath, 'w:gz') as tar:\n        tar.add(filepath, arcname='../../escaped-dir.txt')\n    with pytest.raises(RuntimeError):\n        osutils.extract_tarfile(str(badtarpath), extractdir)\n\n\ndef test_error_raise_tar_symlink_out_of_extract_dir(tmp_path, osutils):\n    dir_with_symlink = tmp_path / 'nest1' / 'nest2'\n    dir_with_symlink.mkdir(parents=True, exist_ok=True)\n    outside_file = tmp_path / 'outside.txt'\n    outside_file.write_text('outside of dir')\n    symlink_file = dir_with_symlink / 'myfile.txt'\n    os.symlink(outside_file, symlink_file)\n    tarpath = dir_with_symlink / 'badtar.tar.gz'\n    with tarfile.open(tarpath, 'w:gz') as tar:\n        tar.add(symlink_file)\n    with pytest.raises(RuntimeError):\n        osutils.extract_tarfile(str(tarpath), dir_with_symlink)\n\n\nclass TestOSUtils(object):\n    def test_can_read_unicode(self, tmpdir, osutils):\n        filename = str(tmpdir.join('file.txt'))\n        checkmark = u'\\2713'\n        with io.open(filename, 'w', encoding='utf-16') as f:\n            f.write(checkmark)\n\n        content = osutils.get_file_contents(filename, binary=False,\n                                            encoding='utf-16')\n        assert content == checkmark\n"
  },
  {
    "path": "tests/integration/__init__.py",
    "content": ""
  },
  {
    "path": "tests/integration/conftest.py",
    "content": "from pytest import fixture\n\n\n@fixture(autouse=True)\ndef ensure_no_local_config(no_local_config):\n    pass\n"
  },
  {
    "path": "tests/integration/test_cli.py",
    "content": "import os\nimport subprocess\n\nimport pytest\nfrom chalice.utils import OSUtils\n\nCURRENT_DIR = os.path.dirname(os.path.abspath(__file__))\nPROJECT_DIR = os.path.join(\n    os.path.dirname(CURRENT_DIR),\n    'aws',\n    'testapp',\n)\n\n\n@pytest.fixture\ndef local_app(tmpdir):\n    temp_dir_path = str(tmpdir)\n    OSUtils().copytree(PROJECT_DIR, temp_dir_path)\n    old_dir = os.getcwd()\n    try:\n        os.chdir(temp_dir_path)\n        yield temp_dir_path\n    finally:\n        os.chdir(old_dir)\n\n\ndef test_stack_trace_printed_on_error(local_app):\n    app_file = os.path.join(local_app, 'app.py')\n    with open(app_file, 'w') as f:\n        f.write(\n            'from chalice import Chalice\\n'\n            'app = Chalice(app_name=\"test\")\\n'\n            'foobarbaz\\n'\n        )\n    p = subprocess.Popen(['chalice', 'local', '--no-autoreload'],\n                         stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n    stderr = p.communicate()[1].decode('ascii')\n    rc = p.returncode\n\n    assert rc == 2\n    assert 'Traceback' in stderr\n    assert 'foobarbaz' in stderr\n"
  },
  {
    "path": "tests/integration/test_package.py",
    "content": "import os\nimport sys\nimport stat\nimport uuid\nimport fnmatch\nfrom zipfile import ZipFile\nimport hashlib\nfrom contextlib import contextmanager\n\nfrom click.testing import CliRunner\nimport pytest\n\nfrom chalice import cli\nfrom chalice.cli import factory\nfrom chalice.cli.newproj import create_new_project_skeleton\nfrom chalice.deploy.packager import NoSuchPackageError\n\n\nPY_VERSION = sys.version_info[:2]\nVERSION_CUTOFF = (3, 11)\n# We're being cautious here, but we want to fix the package versions we\n# try to install on older versions of python.\n# If the python version being tested is less than or equal to VERSION_CUTOFF,\n# then we'll install the `legacy_version` in the packages below.  This is to\n# ensure we don't regress on being able to package older package versions on\n# older versions on python. Any python version above the VERSION_CUTOFF will\n# install the `version` identifier.  That way newer versions of python won't\n# need to update this list as long as a package can still be installed on\n# versions greater than VERSION_CUTOFF.\nPACKAGES_TO_TEST = {\n    'pandas': {\n        'version': '2.2.3',\n        'legacy_version': '1.5.3',\n        'contents': [\n            'pandas/*__init__.py',\n            'pandas/*cpython-*-x86_64-linux-gnu.so'\n        ],\n    },\n    'SQLAlchemy': {\n        'version': '2.0.40',\n        'legacy_version': '1.4.47',\n        'contents': [\n            'sqlalchemy/__init__.py',\n            'sqlalchemy/*cpython-*-x86_64-linux-gnu.so'\n        ],\n    },\n    'numpy': {\n        'version': '2.2.5',\n        'legacy_version': '1.23.3',\n        'contents': [\n            'numpy/__init__.py',\n            'numpy/*cpython-*-x86_64-linux-gnu.so'\n        ],\n    },\n    'cryptography': {\n        'version': '44.0.3',\n        'legacy_version': '39.0.0',\n        'contents': [\n            'cryptography/__init__.py',\n            'cryptography/*.so'\n        ],\n    },\n    'Jinja2': {\n        'version': '3.1.6',\n        'legacy_version': '2.11.2',\n        'contents': ['jinja2/__init__.py'],\n    },\n    'Mako': {\n        'version': '1.3.10',\n        'legacy_version': '1.1.3',\n        'contents': ['mako/__init__.py'],\n    },\n    'MarkupSafe': {\n        'version': '3.0.2',\n        'legacy_version': '1.1.1',\n        'contents': ['markupsafe/__init__.py'],\n    },\n    'scipy': {\n        'version': '1.15.3',\n        'legacy_version': '1.10.1',\n        'contents': [\n            'scipy/__init__.py',\n            'scipy/cluster/_hierarchy.cpython-*-x86_64-linux-gnu.so'\n        ],\n    },\n    'cffi': {\n        'version': '1.17.1',\n        'legacy_version': '1.15.1',\n        'contents': ['_cffi_backend.cpython-*-x86_64-linux-gnu.so'],\n    },\n    'pygit2': {\n        'version': '1.17.0',\n        'legacy_version': '1.10.1',\n        'contents': ['pygit2/_pygit2.cpython-*-x86_64-linux-gnu.so'],\n    },\n    'pyrsistent': {\n        'version': '0.20.0',\n        'legacy_version': '0.17.3',\n        'contents': ['pyrsistent/__init__.py'],\n    },\n}\n\n\n@contextmanager\ndef cd(path):\n    original_dir = os.getcwd()\n    try:\n        os.chdir(path)\n        yield\n    finally:\n        os.chdir(original_dir)\n\n\n@pytest.fixture\ndef runner():\n    return CliRunner()\n\n\n@pytest.fixture\ndef app_skeleton(tmpdir, runner):\n    project_name = 'deployment-integ-test'\n    with cd(str(tmpdir)):\n        create_new_project_skeleton(project_name)\n    return str(tmpdir.join(project_name))\n\n\ndef _get_random_package_name():\n    return 'foobar-%s' % str(uuid.uuid4())[:8]\n\n\ndef _get_package_install_test_cases():\n    testcases = []\n    if PY_VERSION <= VERSION_CUTOFF:\n        version_key = 'legacy_version'\n    else:\n        version_key = 'version'\n    for package, config in PACKAGES_TO_TEST.items():\n        package_version = f'{package}=={config[version_key]}'\n        testcases.append(\n            (package_version, config['contents'])\n        )\n    return testcases\n\n\n# This test can take a while, but you can set this env var to make sure that\n# the commonly used python packages can be packaged successfully.\n@pytest.mark.skipif(not os.environ.get('CHALICE_TEST_EXTENDED_PACKAGING'),\n                    reason='Set CHALICE_TEST_EXTENDED_PACKAGING for extended '\n                           'packaging tests.')\n@pytest.mark.parametrize('package,contents', _get_package_install_test_cases())\ndef test_package_install_smoke_tests(package, contents, runner, app_skeleton):\n    assert_can_package_dependency(runner, app_skeleton, package, contents)\n\n\ndef assert_can_package_dependency(\n        runner, app_skeleton, package, contents):\n    req = os.path.join(app_skeleton, 'requirements.txt')\n    with open(req, 'w') as f:\n        f.write('%s\\n' % package)\n    cli_factory = factory.CLIFactory(app_skeleton)\n    package_output_location = os.path.join(app_skeleton, 'pkg')\n    result = runner.invoke(\n        cli.package, [package_output_location],\n        obj={'project_dir': app_skeleton,\n             'debug': False,\n             'factory': cli_factory})\n    if result.exit_code != 0:\n        raise AssertionError(\n            f\"Non-zero RC when packaging {package}\") from result.exception\n    assert result.exit_code == 0\n    assert result.output.strip() == 'Creating deployment package.'\n    package_path = os.path.join(app_skeleton, 'pkg', 'deployment.zip')\n    package_file = ZipFile(package_path)\n    package_content = package_file.namelist()\n    for content in contents:\n        assert any(fnmatch.fnmatch(filename, content)\n                   for filename in package_content), (\n                       \"No match found for %s\" % content)\n\n\nclass TestPackage(object):\n    def test_can_package_with_dashes_in_name(self, runner, app_skeleton,\n                                             no_local_config):\n        assert_can_package_dependency(\n            runner,\n            app_skeleton,\n            'googleapis-common-protos==1.5.2',\n            contents=[\n                'google/api/__init__.py',\n            ],\n        )\n\n    def test_can_package_simplejson(self, runner, app_skeleton,\n                                    no_local_config):\n        assert_can_package_dependency(\n            runner,\n            app_skeleton,\n            'simplejson==3.17.0',\n            contents=[\n                'simplejson/__init__.py',\n            ],\n        )\n\n    def test_can_package_sqlalchemy(self, runner, app_skeleton,\n                                    no_local_config):\n        # SQLAlchemy is used quite often with Chalice so we want to ensure\n        # we can package it correctly.\n        assert_can_package_dependency(\n            runner,\n            app_skeleton,\n            'SQLAlchemy==1.3.13',\n            contents=[\n                'sqlalchemy/__init__.py',\n            ],\n        )\n\n    def test_can_package_pandas(self, runner, app_skeleton, no_local_config):\n        version = '2.2.3' if sys.version_info[1] >= 10 else '2.0.3'\n        assert_can_package_dependency(\n            runner,\n            app_skeleton,\n            'pandas==' + version,\n            contents=[\n                'pandas/_libs/__init__.py',\n            ],\n        )\n\n    def test_does_not_package_bad_requirements_file(\n            self, runner, app_skeleton):\n        req = os.path.join(app_skeleton, 'requirements.txt')\n        package = _get_random_package_name()\n        with open(req, 'w') as f:\n            f.write('%s\\n' % package)\n        cli_factory = factory.CLIFactory(app_skeleton)\n\n        # Try to build a deployment package from the bad requirements file.\n        # It should fail with a NoSuchPackageError error since the package\n        # should not exist.\n        result = runner.invoke(\n            cli.package, ['package'], obj={'project_dir': app_skeleton,\n                                           'debug': False,\n                                           'factory': cli_factory})\n        assert result.exception is not None\n        ex = result.exception\n        assert isinstance(ex, NoSuchPackageError)\n        assert str(ex) == 'Could not satisfy the requirement: %s' % package\n\n    def test_packaging_requirements_keeps_same_hash(self, runner,\n                                                    app_skeleton,\n                                                    no_local_config):\n        req = os.path.join(app_skeleton, 'requirements.txt')\n        package = 'botocore==1.12.202'\n        with open(req, 'w') as f:\n            f.write('%s\\n' % package)\n        cli_factory = factory.CLIFactory(app_skeleton)\n        package_output_location = os.path.join(app_skeleton, 'pkg')\n        self._run_package_cmd(package_output_location, app_skeleton,\n                              cli_factory, runner)\n        original_checksum = self._calculate_checksum(package_output_location)\n        self._run_package_cmd(package_output_location, app_skeleton,\n                              cli_factory, runner)\n        new_checksum = self._calculate_checksum(package_output_location)\n        assert original_checksum == new_checksum\n\n    def test_preserves_executable_permissions(self, runner, app_skeleton,\n                                              no_local_config):\n        vendor = os.path.join(app_skeleton, 'vendor')\n        os.makedirs(vendor)\n        executable_file = os.path.join(vendor, 'myscript.sh')\n        with open(executable_file, 'w') as f:\n            f.write('#!/bin/bash\\necho foo\\n')\n        os.chmod(executable_file, 0o755)\n        cli_factory = factory.CLIFactory(app_skeleton)\n        package_output_location = os.path.join(app_skeleton, 'pkg')\n        self._run_package_cmd(package_output_location, app_skeleton,\n                              cli_factory, runner)\n        self._verify_file_is_executable(package_output_location,\n                                        'myscript.sh')\n        original_checksum = self._calculate_checksum(package_output_location)\n        self._run_package_cmd(package_output_location, app_skeleton,\n                              cli_factory, runner)\n        new_checksum = self._calculate_checksum(package_output_location)\n        assert original_checksum == new_checksum\n\n    def _calculate_checksum(self, package_output_location):\n        zip_filename = os.path.join(package_output_location, 'deployment.zip')\n        with open(zip_filename, 'rb') as f:\n            return hashlib.md5(f.read()).hexdigest()\n\n    def _run_package_cmd(self, package_output_location, app_skeleton,\n                         cli_factory, runner, expected_exit_code=0):\n        result = runner.invoke(\n            cli.package, [package_output_location],\n            obj={'project_dir': app_skeleton,\n                 'debug': False,\n                 'factory': cli_factory})\n        assert result.exit_code == expected_exit_code\n        return result\n\n    def _verify_file_is_executable(self, package_output_location, filename):\n        zip_filename = os.path.join(package_output_location, 'deployment.zip')\n        with ZipFile(zip_filename) as zip:\n            zipinfo = zip.getinfo(filename)\n            assert (zipinfo.external_attr >> 16) & stat.S_IXUSR\n"
  },
  {
    "path": "tests/plugins/codelinter.py",
    "content": "# These are linting checks used in the chalice codebase itself.\n# These are used to enforce specific coding standards and constraints.\nfrom pylint.checkers import BaseChecker\nfrom astroid.exceptions import InferenceError\nimport astroid\n\n\ndef register(linter):\n    linter.register_checker(ConditionalImports(linter))\n\n\nclass ConditionalImports(BaseChecker):\n    # This is used to ensure that any imports that rely on conditional\n    # dependencies must be wrapped in a try/except ImportError.\n    name = 'must-catch-import-error'\n    msgs = {\n        'C9997': ('Importing this module must catch ImportError.',\n                  'must-catch-import-error',\n                  'Importing this module must catch ImportError.'),\n    }\n\n    def visit_import(self, node):\n        names = [name[0] for name in node.names]\n        if 'chalice.cli.filewatch.eventbased' in names:\n            if not self._is_in_try_except_import_error(node):\n                self.add_message('must-catch-import-error', node=node)\n                return\n\n    def visit_importfrom(self, node):\n        if node.modname == 'chalice.cli.filewatch.eventbased':\n            names = [name[0] for name in node.names]\n            if 'WatchdogWorkerProcess' in names:\n                # Ensure this is wrapped in a try/except.\n                # Technically we should ensure anywhere in the call stack\n                # we're wrapped in a try/except, but in practice we'll just\n                # enforce you did that in the same scope as your import.\n                if not self._is_in_try_except_import_error(node):\n                    self.add_message('must-catch-import-error', node=node)\n                    return\n\n    def _is_in_try_except_import_error(self, node):\n        if not isinstance(node.parent, astroid.Try):\n            return False\n        caught_exceptions = [\n            handler.type.name for handler in node.parent.handlers]\n        if 'ImportError' not in caught_exceptions:\n            # They wrapped a try/except but aren't catching\n            # ImportError.\n            return False\n        return True\n"
  },
  {
    "path": "tests/plugins/testlinter.py",
    "content": "from pylint.checkers import BaseChecker\nfrom astroid.exceptions import InferenceError\n\n\ndef register(linter):\n    linter.register_checker(PatchChecker(linter))\n    linter.register_checker(MocksUseSpecArg(linter))\n\n\nclass PatchChecker(BaseChecker):\n    name = 'patching-banned'\n    msgs = {\n        'C9999': ('Use of mock.patch is not allowed',\n                  'patch-call',\n                  'Use of mock.patch not allowed')\n    }\n    patch_pytype = 'unittest.mock._patch'\n\n    def visit_call(self, node):\n        try:\n            for inferred_type in node.infer():\n                if inferred_type.pytype() == self.patch_pytype:\n                    self.add_message('patch-call', node=node)\n        except InferenceError:\n            # It's ok if we can't work out what type the function\n            # call is.\n            pass\n\n\nclass MocksUseSpecArg(BaseChecker):\n\n    name = 'mocks-use-spec'\n    msgs = {\n        'C9998': ('mock.Mock() must provide \"spec=\" argument',\n                  'mock-missing-spec',\n                  'mock.Mock() must provide \"spec=\" argument')\n    }\n    mock_pytype = 'unittest.mock.Mock'\n    required_kwarg = 'spec'\n\n    def visit_call(self, node):\n        try:\n            for inferred_type in node.infer():\n                if inferred_type.pytype() == self.mock_pytype:\n                    self._verify_spec_arg_provided(node)\n        except InferenceError:\n            pass\n\n    def _verify_spec_arg_provided(self, node):\n        if not node.keywords:\n            self.add_message('mock-missing-spec', node=node)\n            return\n        kwargs = [kwarg.arg for kwarg in node.keywords]\n        if self.required_kwarg not in kwargs:\n            self.add_message('mock-missing-spec', node=node)\n"
  },
  {
    "path": "tests/unit/__init__.py",
    "content": ""
  },
  {
    "path": "tests/unit/cli/__init__.py",
    "content": ""
  },
  {
    "path": "tests/unit/cli/filewatch/test_eventbased.py",
    "content": "import threading\nfrom subprocess import Popen\nfrom unittest import mock\n\nimport pytest\n\ntry:\n    from watchdog.events import FileSystemEvent, DirModifiedEvent\n    from chalice.cli.filewatch.eventbased import WatchdogRestarter\n    HAS_WATCHDOG = True\nexcept ImportError:\n    HAS_WATCHDOG = False\n\nimport chalice.local\nfrom chalice.cli import reloader\nfrom chalice.local import LocalDevServer\n\n\n# This will skip all the tests in this module if watchdog is not installed.\npytestmark = pytest.mark.skipif(not HAS_WATCHDOG,\n                                reason='Tests require watchdog package.')\n\n\n# NOTE: Most of the reloader module relies on threads, subprocesses,\n# and process exiting with specific return codes.  This is quite difficult\n# to unit test, so the more realistic tests are over in function/test_local.py.\nclass RecordingPopen(object):\n    def __init__(self, process, return_codes=None):\n        self.process = process\n        self.recorded_args = []\n        if return_codes is None:\n            return_codes = []\n        self.return_codes = return_codes\n\n    def __call__(self, *args, **kwargs):\n        self.recorded_args.append((args, kwargs))\n        if self.return_codes:\n            rc = self.return_codes.pop(0)\n            self.process.returncode = rc\n        return self.process\n\n\ndef test_restarter_triggers_event():\n    restart_event = threading.Event()\n    restarter = WatchdogRestarter(restart_event)\n    app_modified = FileSystemEvent(src_path='./app.py')\n    restarter.on_any_event(app_modified)\n    assert restart_event.is_set()\n\n\ndef test_directory_events_ignored():\n    restart_event = threading.Event()\n    restarter = WatchdogRestarter(restart_event)\n    app_modified = DirModifiedEvent(src_path='./')\n    restarter.on_any_event(app_modified)\n    assert not restart_event.is_set()\n\n\ndef test_http_server_thread_starts_server_and_shutsdown():\n    server = mock.Mock(spec=LocalDevServer)\n    thread = chalice.local.HTTPServerThread(lambda: server)\n    thread.run()\n    thread.shutdown()\n    server.serve_forever.assert_called_with()\n    server.shutdown.assert_called_with()\n\n\ndef test_shutdown_noop_if_server_not_started():\n    server = mock.Mock(spec=LocalDevServer)\n    thread = chalice.local.HTTPServerThread(lambda: server)\n    thread.shutdown()\n    assert not server.shutdown.called\n\n\ndef test_parent_process_starts_child_with_worker_env_var():\n    process = mock.Mock(spec=Popen)\n    process.returncode = 0\n    popen = RecordingPopen(process)\n    env = {'original-env': 'foo'}\n    parent = reloader.ParentProcess(env, popen)\n\n    parent.main()\n\n    assert len(popen.recorded_args) == 1\n    kwargs = popen.recorded_args[-1][1]\n    assert kwargs == {'env': {'original-env': 'foo',\n                              'CHALICE_WORKER': 'true'}}\n\n\ndef test_assert_child_restarted_until_not_restart_rc():\n    process = mock.Mock(spec=Popen)\n    popen = RecordingPopen(\n        process, return_codes=[chalice.cli.filewatch.RESTART_REQUEST_RC, 0])\n    parent = reloader.ParentProcess({}, popen)\n\n    parent.main()\n\n    # The child process should have been invoked twice, the first one\n    # was with RESTART_REQUEST_RC so that should trigger a restart,\n    # then second one was rc 0 the process should just exit.\n    assert len(popen.recorded_args) == 2\n\n\ndef test_ctrl_c_kill_child_process():\n    process = mock.Mock(spec=Popen)\n    process.communicate.side_effect = KeyboardInterrupt\n    popen = RecordingPopen(process)\n    parent = reloader.ParentProcess({}, popen)\n\n    with pytest.raises(KeyboardInterrupt):\n        parent.main()\n\n    assert process.terminate.called\n"
  },
  {
    "path": "tests/unit/cli/filewatch/test_stat.py",
    "content": "import os\nimport time\n\nfrom chalice.cli.filewatch import stat\n\n\nclass FakeOSUtils(object):\n    def __init__(self):\n        self.initial_scan = True\n\n    def walk(self, rootdir):\n        yield 'rootdir', [], ['bad-file', 'baz']\n        if self.initial_scan:\n            self.initial_scan = False\n\n    def joinpath(self, *parts):\n        return os.path.join(*parts)\n\n    def mtime(self, path):\n        if self.initial_scan:\n            return 1\n        if path.endswith('bad-file'):\n            raise OSError(\"Bad file\")\n        return 2\n\n\ndef test_can_ignore_stat_errors():\n    calls = []\n\n    def callback(*args, **kwargs):\n        calls.append((args, kwargs))\n\n    watcher = stat.StatFileWatcher(FakeOSUtils())\n    watcher.watch_for_file_changes('rootdir', callback)\n    for _ in range(10):\n        if len(calls) == 1:\n            break\n        time.sleep(0.2)\n    else:\n        raise AssertionError(\"Expected callback to be invoked but was not.\")\n"
  },
  {
    "path": "tests/unit/cli/test_cli.py",
    "content": "from unittest import mock\nimport pytest\nimport re\n\nfrom chalice import cli\nfrom chalice.cli.factory import CLIFactory\nfrom chalice.local import LocalDevServer\n\n\ndef test_cannot_run_local_mode_with_trailing_slash_route():\n    local_stage_test = 'local_test'\n    factory = mock.Mock(spec=CLIFactory)\n    factory.create_config_obj.return_value.environment_variables = {}\n    factory.create_config_obj.return_value.chalice_app.routes = {\n        'foobar/': None\n    }\n    local_server = mock.Mock(spec=LocalDevServer)\n    factory.create_local_server.return_value = local_server\n    with pytest.raises(ValueError) as e:\n        cli.run_local_server(factory, 'localhost', 8000, local_stage_test)\n    assert str(e.value) == 'Route cannot end with a trailing slash: foobar/'\n\n\ndef test_get_system_info():\n    system_info = cli.get_system_info()\n    assert re.match(r'python\\s*([\\d.]+),?\\s*(.*) (.*)', system_info)\n"
  },
  {
    "path": "tests/unit/cli/test_newproj.py",
    "content": "import os\n\nimport pytest\n\nfrom chalice.cli import newproj\n\n\nclass InMemoryOSUtils(object):\n    def __init__(self, filemap=None):\n        if filemap is None:\n            filemap = {}\n        self.filemap = filemap\n        self.walk_return_val = None\n\n    def dirname(self, name):\n        return os.path.dirname(name)\n\n    def get_directory_contents(self, dirname):\n        full_paths = [f for f in self.filemap if f.startswith(dirname)]\n        return [p.split(os.sep)[1] for p in full_paths]\n\n    def file_exists(self, filename):\n        return filename in self.filemap\n\n    def joinpath(self, *args):\n        return os.path.join(*args)\n\n    def walk(self, root_dir):\n        return self.walk_return_value\n\n    def directory_exists(self, dirname):\n        return True\n\n    def get_file_contents(self, filename, binary=True):\n        return self.filemap[filename]\n\n    def set_file_contents(self, filename, contents, binary=True):\n        self.filemap[filename] = contents\n\n\n@pytest.mark.parametrize(\n    'contents,template_kwargs,expected', [\n        ('{{myvar}}', {'myvar': 'foo'}, 'foo'),\n        ('{{myvar}}', {'myvar': 'foo', 'myvar2': 'bar'}, 'foo'),\n        ('before {{myvar}} after', {'myvar': 'foo'}, 'before foo after'),\n        ('newlines\\n{{myvar}}\\nbar', {'myvar': 'foo'}, 'newlines\\nfoo\\nbar'),\n        ('NAME = \"{{myvar}}\"', {'myvar': 'foo'}, 'NAME = \"foo\"'),\n        ('{{one}}{{two}}', {'one': 'foo', 'two': 'bar'}, 'foobar'),\n        ('{nomatch}', {'nomatch': 'bar'}, '{nomatch}'),\n        ('no template', {'nomatch': 'bar'}, 'no template'),\n        ('', {}, ''),\n        ('{{noclose', {}, '{{noclose'),\n        ('nostart}}', {}, 'nostart}}'),\n        ('{{unknown_var}}', {}, newproj.BadTemplateError()),\n    ]\n)\ndef test_can_get_templated_content(contents, template_kwargs, expected):\n    if isinstance(expected, Exception):\n        with pytest.raises(expected.__class__):\n            newproj.get_templated_content(contents, template_kwargs)\n    else:\n        newproj.get_templated_content(contents, template_kwargs) == expected\n\n\ndef test_newproj_copies_and_templates_files():\n    fake_osutils = InMemoryOSUtils()\n    fake_osutils.walk_return_value = [\n        ('source_dir', [], ['foo', 'bar']),\n    ]\n    fake_osutils.filemap = {\n        os.path.join('source_dir', 'foo'): 'hello',\n        os.path.join('source_dir', 'bar'): '{{who}}',\n    }\n    creator = newproj.ProjectCreator(fake_osutils)\n    creator.create_new_project('source_dir', 'dest_dir', {'who': 'world'})\n    assert fake_osutils.filemap[os.path.join('dest_dir', 'foo')] == 'hello'\n    assert fake_osutils.filemap[os.path.join('dest_dir', 'bar')] == 'world'\n\n\ndef test_can_list_available_projects():\n    fake_osutils = InMemoryOSUtils()\n    join = os.path.join\n    first_dir = join('template-dir', '0001-first-proj')\n    second_dir = join('template-dir', '0002-second-proj')\n    fake_osutils.filemap = {\n        join(first_dir, 'metadata.json'): '{\"description\": \"First template\"}',\n        join(second_dir, 'metadata.json'): '{\"description\": \"Second\"}',\n\n    }\n    results = newproj.list_available_projects('template-dir', fake_osutils)\n    assert results == [\n        newproj.ProjectTemplate(\n            dirname='0001-first-proj',\n            metadata={'description': 'First template'},\n            key='first-proj',\n        ),\n        newproj.ProjectTemplate(\n            dirname='0002-second-proj',\n            metadata={'description': 'Second'},\n            key='second-proj',\n        ),\n    ]\n"
  },
  {
    "path": "tests/unit/conftest.py",
    "content": "import json\nimport os\n\nfrom pytest import fixture\nfrom hypothesis import settings, HealthCheck\n\nfrom chalice.app import Chalice\n\n# From:\n# http://hypothesis.readthedocs.io/en/latest/settings.html#settings-profiles\n# On travis we'll have it run through more iterations.\nfrom chalice.deploy import models\n\nsettings.register_profile(\n    'ci', settings(max_examples=2000,\n                   suppress_health_check=[HealthCheck.too_slow]),\n)\n# When you're developing locally, we'll only run a few examples\n# to keep unit tests fast.  If you want to run more iterations\n# locally just set HYPOTHESIS_PROFILE=ci.\nsettings.register_profile('dev', settings(max_examples=10))\nsettings.load_profile(os.getenv('HYPOTHESIS_PROFILE', 'dev'))\n\nprint(\"HYPOTHESIS PROFILE: %s\" % os.environ.get(\"HYPOTHESIS_PROFILE\"))\n\n\n@fixture(autouse=True)\ndef ensure_no_local_config(no_local_config):\n    pass\n\n\n@fixture\ndef sample_app():\n    app = Chalice('sample')\n\n    @app.route('/')\n    def foo():\n        return {}\n\n    return app\n\n\n@fixture\ndef sample_app_with_auth():\n    app = Chalice('sampleauth')\n\n    @app.authorizer('myauth')\n    def myauth(auth_request):\n        pass\n\n    @app.route('/', authorizer=myauth)\n    def foo():\n        return {}\n\n    return app\n\n\n@fixture\ndef sample_app_schedule_only():\n    app = Chalice('schedule_only')\n\n    @app.schedule('rate(5 minutes)')\n    def cron(event):\n        pass\n\n    return app\n\n\n@fixture\ndef sample_sqs_event_app():\n    app = Chalice('sqs-event')\n\n    @app.on_sqs_message(queue='myqueue')\n    def handler(event):\n        pass\n\n    return app\n\n\n@fixture\ndef sample_kinesis_event_app():\n    app = Chalice('kinesis-event')\n\n    @app.on_kinesis_record(stream='mystream')\n    def handler(event):\n        pass\n\n    return app\n\n\n@fixture\ndef sample_ddb_event_app():\n    app = Chalice('ddb-event')\n\n    @app.on_dynamodb_record(stream_arn='arn:aws:...:stream')\n    def handler(event):\n        pass\n\n    return app\n\n\n@fixture\ndef sample_app_lambda_only():\n    app = Chalice('lambda_only')\n\n    @app.lambda_function()\n    def myfunction(event, context):\n        pass\n\n    return app\n\n\n@fixture\ndef sample_websocket_app():\n    app = Chalice('sample')\n\n    @app.on_ws_connect()\n    def connect():\n        pass\n\n    @app.on_ws_message()\n    def message():\n        pass\n\n    @app.on_ws_disconnect()\n    def disconnect():\n        pass\n\n    return app\n\n\n@fixture\ndef sample_s3_event_app():\n    app = Chalice('s3-event')\n\n    @app.on_s3_event(bucket='mybucket')\n    def handler(event):\n        pass\n\n    return app\n\n\n@fixture\ndef sample_sns_event_app():\n    app = Chalice('sns-event')\n\n    @app.on_sns_message(topic='mytopic')\n    def handler(event):\n        pass\n\n    return app\n\n\n@fixture\ndef sample_cloudwatch_event_app():\n    app = Chalice('cloudwatch-event')\n\n    @app.on_cw_event({'source': {'source': ['aws.ec2']}})\n    def foo(event):\n        return event\n\n    return app\n\n\n@fixture\ndef create_event():\n    def create_event_inner(uri, method, path, content_type='application/json'):\n        return {\n            'requestContext': {\n                'httpMethod': method,\n                'resourcePath': uri,\n            },\n            'headers': {\n                'Content-Type': content_type,\n            },\n            'pathParameters': path,\n            'multiValueQueryStringParameters': None,\n            'body': \"\",\n            'stageVariables': {},\n        }\n    return create_event_inner\n\n\n@fixture\ndef create_websocket_event():\n    def create_event_inner(\n            route_key, body='',\n            endpoint='abcd1234.execute-api.us-west-2.amazonaws.com'):\n        return {\n            'requestContext': {\n                'routeKey': route_key,\n                'domainName': endpoint,\n                'stage': 'api',\n                'connectionId': 'ABCD1234=',\n                'apiId': 'abcd1234',\n            },\n            'body': body,\n        }\n    return create_event_inner\n\n\n@fixture\ndef create_empty_header_event():\n    def create_empty_header_event_inner(uri, method, path,\n                                        content_type='application/json'):\n        return {\n            'requestContext': {\n                'httpMethod': method,\n                'resourcePath': uri,\n            },\n            'headers': None,\n            'pathParameters': path,\n            'multiValueQueryStringParameters': None,\n            'body': \"\",\n            'stageVariables': {},\n        }\n    return create_empty_header_event_inner\n\n\n@fixture\ndef create_event_with_body(create_event):\n    def create_event_with_body_inner(body, uri='/', method='POST',\n                                     content_type='application/json'):\n        event = create_event(uri, method, {}, content_type)\n        if content_type == 'application/json':\n            body = json.dumps(body)\n        event['body'] = body\n        return event\n    return create_event_with_body_inner\n\n\n@fixture\ndef lambda_function():\n    return models.LambdaFunction(\n        resource_name='foo',\n        function_name='app-stage-foo',\n        deployment_package=None,\n        environment_variables={},\n        runtime='python2.7',\n        handler='app.app',\n        tags={},\n        timeout=None,\n        memory_size=None,\n        role=models.PreCreatedIAMRole(role_arn='foobar'),\n        security_group_ids=[],\n        subnet_ids=[],\n        layers=[],\n        reserved_concurrency=None,\n        xray=None,\n    )\n"
  },
  {
    "path": "tests/unit/deploy/__init__.py",
    "content": ""
  },
  {
    "path": "tests/unit/deploy/test_appgraph.py",
    "content": "import pytest\n\nfrom chalice.app import Chalice\nfrom chalice.config import Config\nfrom chalice.constants import LAMBDA_TRUST_POLICY\nfrom chalice.deploy import models\nfrom chalice.deploy.appgraph import ApplicationGraphBuilder, ChaliceBuildError\nfrom chalice.deploy.deployer import BuildStage, PolicyGenerator\nfrom chalice.utils import serialize_to_json, OSUtils\n\n\n@pytest.fixture\ndef websocket_app_without_connect():\n    app = Chalice('websocket-event-no-connect')\n\n    @app.on_ws_message()\n    def message(event):\n        pass\n\n    @app.on_ws_disconnect()\n    def disconnect(event):\n        pass\n\n    return app\n\n\n@pytest.fixture\ndef websocket_app_without_message():\n    app = Chalice('websocket-event-no-message')\n\n    @app.on_ws_connect()\n    def connect(event):\n        pass\n\n    @app.on_ws_disconnect()\n    def disconnect(event):\n        pass\n\n    return app\n\n\n@pytest.fixture\ndef websocket_app_without_disconnect():\n    app = Chalice('websocket-event-no-disconnect')\n\n    @app.on_ws_connect()\n    def connect(event):\n        pass\n\n    @app.on_ws_message()\n    def message(event):\n        pass\n\n    return app\n\n\nclass TestApplicationGraphBuilder(object):\n\n    def create_config(self, app, app_name='lambda-only',\n                      iam_role_arn=None, policy_file=None,\n                      api_gateway_stage='api',\n                      autogen_policy=False, security_group_ids=None,\n                      subnet_ids=None, reserved_concurrency=None, layers=None,\n                      automatic_layer=False,\n                      api_gateway_endpoint_type=None,\n                      api_gateway_endpoint_vpce=None,\n                      api_gateway_policy_file=None,\n                      api_gateway_custom_domain=None,\n                      websocket_api_custom_domain=None,\n                      log_retention_in_days=None,\n                      project_dir='.'):\n        kwargs = {\n            'chalice_app': app,\n            'app_name': app_name,\n            'project_dir': project_dir,\n            'automatic_layer': automatic_layer,\n            'api_gateway_stage': api_gateway_stage,\n            'api_gateway_policy_file': api_gateway_policy_file,\n            'api_gateway_endpoint_type': api_gateway_endpoint_type,\n            'api_gateway_endpoint_vpce': api_gateway_endpoint_vpce,\n            'api_gateway_custom_domain': api_gateway_custom_domain,\n            'websocket_api_custom_domain': websocket_api_custom_domain,\n        }\n        if iam_role_arn is not None:\n            # We want to use an existing role.\n            # This will skip all the autogen-policy\n            # and role creation.\n            kwargs['manage_iam_role'] = False\n            kwargs['iam_role_arn'] = 'role:arn'\n        elif policy_file is not None:\n            # Otherwise this setting is when a user wants us to\n            # manage the role, but they've written a policy file\n            # they'd like us to use.\n            kwargs['autogen_policy'] = False\n            kwargs['iam_policy_file'] = policy_file\n        elif autogen_policy:\n            kwargs['autogen_policy'] = True\n        if security_group_ids is not None and subnet_ids is not None:\n            kwargs['security_group_ids'] = security_group_ids\n            kwargs['subnet_ids'] = subnet_ids\n        if reserved_concurrency is not None:\n            kwargs['reserved_concurrency'] = reserved_concurrency\n        if log_retention_in_days is not None:\n            kwargs['log_retention_in_days'] = log_retention_in_days\n        kwargs['layers'] = layers\n        config = Config.create(**kwargs)\n        return config\n\n    def test_can_build_single_lambda_function_app(self,\n                                                  sample_app_lambda_only):\n        # This is the simplest configuration we can get.\n        builder = ApplicationGraphBuilder()\n        config = self.create_config(sample_app_lambda_only,\n                                    automatic_layer=False,\n                                    iam_role_arn='role:arn')\n        application = builder.build(config, stage_name='dev')\n        # The top level resource is always an Application.\n        assert isinstance(application, models.Application)\n        assert len(application.resources) == 1\n        assert application.resources[0] == models.LambdaFunction(\n            resource_name='myfunction',\n            function_name='lambda-only-dev-myfunction',\n            environment_variables={},\n            runtime=config.lambda_python_version,\n            handler='app.myfunction',\n            tags=config.tags,\n            timeout=None,\n            memory_size=None,\n            deployment_package=models.DeploymentPackage(\n                models.Placeholder.BUILD_STAGE),\n            role=models.PreCreatedIAMRole('role:arn'),\n            security_group_ids=[],\n            subnet_ids=[],\n            layers=[],\n            reserved_concurrency=None,\n            managed_layer=None,\n            xray=None,\n        )\n\n    def test_can_build_single_lambda_function_app_with_log_retention(\n            self, sample_app_lambda_only):\n        # This is the simplest configuration we can get.\n        builder = ApplicationGraphBuilder()\n        config = self.create_config(sample_app_lambda_only,\n                                    automatic_layer=False,\n                                    iam_role_arn='role:arn',\n                                    log_retention_in_days=14)\n        application = builder.build(config, stage_name='dev')\n        # The top level resource is always an Application.\n        assert isinstance(application, models.Application)\n        assert len(application.resources) == 1\n        assert isinstance(application.resources[0].log_group, models.LogGroup)\n        assert application.resources[0] == models.LambdaFunction(\n            resource_name='myfunction',\n            function_name='lambda-only-dev-myfunction',\n            environment_variables={},\n            runtime=config.lambda_python_version,\n            handler='app.myfunction',\n            tags=config.tags,\n            timeout=None,\n            memory_size=None,\n            deployment_package=models.DeploymentPackage(\n                models.Placeholder.BUILD_STAGE),\n            role=models.PreCreatedIAMRole('role:arn'),\n            security_group_ids=[],\n            subnet_ids=[],\n            layers=[],\n            reserved_concurrency=None,\n            managed_layer=None,\n            xray=None,\n            log_group=models.LogGroup(\n                resource_name='myfunction-log-group',\n                log_group_name='/aws/lambda/%s-%s-%s' %\n                              (config.app_name, 'dev', 'myfunction'),\n                retention_in_days=14)\n        )\n\n    def test_can_build_single_lambda_function_app_with_managed_layer(\n            self, sample_app_lambda_only):\n        # This is the simplest configuration we can get.\n        builder = ApplicationGraphBuilder()\n        config = self.create_config(\n            sample_app_lambda_only,\n            iam_role_arn='role:arn', automatic_layer=True)\n        application = builder.build(config, stage_name='dev')\n        # The top level resource is always an Application.\n        assert isinstance(application, models.Application)\n        assert len(application.resources) == 1\n        assert application.resources[0] == models.LambdaFunction(\n            resource_name='myfunction',\n            function_name='lambda-only-dev-myfunction',\n            environment_variables={},\n            runtime=config.lambda_python_version,\n            handler='app.myfunction',\n            tags=config.tags,\n            timeout=None,\n            memory_size=None,\n            deployment_package=models.DeploymentPackage(\n                models.Placeholder.BUILD_STAGE),\n            role=models.PreCreatedIAMRole('role:arn'),\n            security_group_ids=[],\n            subnet_ids=[],\n            layers=[],\n            managed_layer=models.LambdaLayer(\n                resource_name='managed-layer',\n                layer_name='lambda-only-dev-managed-layer',\n                runtime=config.lambda_python_version,\n                deployment_package=models.DeploymentPackage(\n                    models.Placeholder.BUILD_STAGE,\n                )\n            ),\n            reserved_concurrency=None,\n            xray=None,\n        )\n\n    def test_all_lambda_functions_share_managed_layer(\n            self, sample_app_lambda_only):\n\n        @sample_app_lambda_only.lambda_function()\n        def second(event, context):\n            pass\n\n        builder = ApplicationGraphBuilder()\n        config = self.create_config(\n            sample_app_lambda_only,\n            iam_role_arn='role:arn', automatic_layer=True)\n        application = builder.build(config, stage_name='dev')\n        assert len(application.resources) == 2\n        first_layer = application.resources[0].managed_layer\n        second_layer = application.resources[1].managed_layer\n        assert first_layer == second_layer\n\n    def test_can_build_lambda_function_with_layers(self,\n                                                   sample_app_lambda_only):\n        # This is the simplest configuration we can get.\n        builder = ApplicationGraphBuilder()\n        layers = ['arn:aws:lambda:us-east-1:111:layer:test_layer:1']\n        config = self.create_config(sample_app_lambda_only,\n                                    iam_role_arn='role:arn',\n                                    layers=layers)\n        application = builder.build(config, stage_name='dev')\n        # The top level resource is always an Application.\n        assert isinstance(application, models.Application)\n        assert len(application.resources) == 1\n        assert application.resources[0] == models.LambdaFunction(\n            resource_name='myfunction',\n            function_name='lambda-only-dev-myfunction',\n            environment_variables={},\n            runtime=config.lambda_python_version,\n            handler='app.myfunction',\n            tags=config.tags,\n            timeout=None,\n            memory_size=None,\n            deployment_package=models.DeploymentPackage(\n                models.Placeholder.BUILD_STAGE),\n            role=models.PreCreatedIAMRole('role:arn'),\n            security_group_ids=[],\n            subnet_ids=[],\n            layers=layers,\n            reserved_concurrency=None,\n            xray=None,\n        )\n\n    def test_can_build_app_with_domain_name(self, sample_app):\n        domain_name = {\n            'domain_name': 'example.com',\n            'tls_version': 'TLS_1_0',\n            'certificate_arn': 'certificate_arn',\n            'tags': {\n                'some_key1': 'some_value1',\n                'some_key2': 'some_value2'\n            },\n            'url_prefix': '/'\n        }\n        config = self.create_config(sample_app,\n                                    app_name='rest-api-app',\n                                    api_gateway_endpoint_type='REGIONAL',\n                                    api_gateway_custom_domain=domain_name,\n                                    )\n        builder = ApplicationGraphBuilder()\n        application = builder.build(config, stage_name='dev')\n        rest_api = application.resources[0]\n        assert isinstance(rest_api, models.RestAPI)\n        domain_name = rest_api.domain_name\n        api_mapping = domain_name.api_mapping\n        assert isinstance(domain_name, models.DomainName)\n        assert isinstance(api_mapping, models.APIMapping)\n        assert api_mapping.mount_path == '(none)'\n\n    def test_can_build_lambda_function_app_with_vpc_config(\n            self, sample_app_lambda_only\n    ):\n        @sample_app_lambda_only.lambda_function()\n        def foo(event, context):\n            pass\n\n        builder = ApplicationGraphBuilder()\n        config = self.create_config(sample_app_lambda_only,\n                                    iam_role_arn='role:arn',\n                                    security_group_ids=['sg1', 'sg2'],\n                                    subnet_ids=['sn1', 'sn2'])\n        application = builder.build(config, stage_name='dev')\n\n        assert application.resources[0] == models.LambdaFunction(\n            resource_name='myfunction',\n            function_name='lambda-only-dev-myfunction',\n            environment_variables={},\n            runtime=config.lambda_python_version,\n            handler='app.myfunction',\n            tags=config.tags,\n            timeout=None,\n            memory_size=None,\n            deployment_package=models.DeploymentPackage(\n                models.Placeholder.BUILD_STAGE),\n            role=models.PreCreatedIAMRole('role:arn'),\n            security_group_ids=['sg1', 'sg2'],\n            subnet_ids=['sn1', 'sn2'],\n            layers=[],\n            reserved_concurrency=None,\n            xray=None,\n        )\n\n    def test_vpc_trait_added_when_vpc_configured(self, sample_app_lambda_only):\n        @sample_app_lambda_only.lambda_function()\n        def foo(event, context):\n            pass\n\n        builder = ApplicationGraphBuilder()\n        config = self.create_config(sample_app_lambda_only,\n                                    autogen_policy=True,\n                                    security_group_ids=['sg1', 'sg2'],\n                                    subnet_ids=['sn1', 'sn2'])\n        application = builder.build(config, stage_name='dev')\n\n        policy = application.resources[0].role.policy\n        assert policy == models.AutoGenIAMPolicy(\n            document=models.Placeholder.BUILD_STAGE,\n            traits=set([models.RoleTraits.VPC_NEEDED]),\n        )\n\n    def test_exception_raised_when_missing_vpc_params(self,\n                                                      sample_app_lambda_only):\n        @sample_app_lambda_only.lambda_function()\n        def foo(event, context):\n            pass\n\n        builder = ApplicationGraphBuilder()\n        config = self.create_config(sample_app_lambda_only,\n                                    iam_role_arn='role:arn',\n                                    security_group_ids=['sg1', 'sg2'],\n                                    subnet_ids=[])\n        with pytest.raises(ChaliceBuildError):\n            builder.build(config, stage_name='dev')\n\n    def test_can_build_lambda_function_app_with_reserved_concurrency(\n            self, sample_app_lambda_only):\n        # This is the simplest configuration we can get.\n        builder = ApplicationGraphBuilder()\n        config = self.create_config(sample_app_lambda_only,\n                                    iam_role_arn='role:arn',\n                                    reserved_concurrency=5)\n        application = builder.build(config, stage_name='dev')\n        # The top level resource is always an Application.\n        assert isinstance(application, models.Application)\n        assert len(application.resources) == 1\n        assert application.resources[0] == models.LambdaFunction(\n            resource_name='myfunction',\n            function_name='lambda-only-dev-myfunction',\n            environment_variables={},\n            runtime=config.lambda_python_version,\n            handler='app.myfunction',\n            tags=config.tags,\n            timeout=None,\n            memory_size=None,\n            deployment_package=models.DeploymentPackage(\n                models.Placeholder.BUILD_STAGE),\n            role=models.PreCreatedIAMRole('role:arn'),\n            security_group_ids=[],\n            subnet_ids=[],\n            layers=[],\n            reserved_concurrency=5,\n            xray=None,\n        )\n\n    def test_multiple_lambda_functions_share_role_and_package(\n            self, sample_app_lambda_only):\n        # We're going to add another lambda_function to our app.\n        @sample_app_lambda_only.lambda_function()\n        def bar(event, context):\n            return {}\n\n        builder = ApplicationGraphBuilder()\n        config = self.create_config(sample_app_lambda_only,\n                                    iam_role_arn='role:arn')\n        application = builder.build(config, stage_name='dev')\n        assert len(application.resources) == 2\n        # The lambda functions by default share the same role\n        assert application.resources[0].role == application.resources[1].role\n        # Not just in equality but the exact same role objects.\n        assert application.resources[0].role is application.resources[1].role\n        # And all lambda functions share the same deployment package.\n        assert (application.resources[0].deployment_package ==\n                application.resources[1].deployment_package)\n\n    def test_autogen_policy_for_function(self, sample_app_lambda_only):\n        # This test is just a sanity test that verifies all the params\n        # for an ManagedIAMRole.  The various combinations for role\n        # configuration is all tested via RoleTestCase.\n        config = self.create_config(sample_app_lambda_only,\n                                    autogen_policy=True)\n        builder = ApplicationGraphBuilder()\n        application = builder.build(config, stage_name='dev')\n        function = application.resources[0]\n        role = function.role\n        # We should have linked a ManagedIAMRole\n        assert isinstance(role, models.ManagedIAMRole)\n        assert role == models.ManagedIAMRole(\n            resource_name='default-role',\n            role_name='lambda-only-dev',\n            trust_policy=LAMBDA_TRUST_POLICY,\n            policy=models.AutoGenIAMPolicy(models.Placeholder.BUILD_STAGE),\n        )\n\n    def test_cloudwatch_event_models(self, sample_cloudwatch_event_app):\n        config = self.create_config(sample_cloudwatch_event_app,\n                                    app_name='cloudwatch-event',\n                                    autogen_policy=True)\n        builder = ApplicationGraphBuilder()\n        application = builder.build(config, stage_name='dev')\n        assert len(application.resources) == 1\n        event = application.resources[0]\n        assert isinstance(event, models.CloudWatchEvent)\n        assert event.resource_name == 'foo-event'\n        assert event.rule_name == 'cloudwatch-event-dev-foo-event'\n        assert isinstance(event.lambda_function, models.LambdaFunction)\n        assert event.lambda_function.resource_name == 'foo'\n\n    def test_scheduled_event_models(self, sample_app_schedule_only):\n        config = self.create_config(sample_app_schedule_only,\n                                    app_name='scheduled-event',\n                                    autogen_policy=True)\n        builder = ApplicationGraphBuilder()\n        application = builder.build(config, stage_name='dev')\n        assert len(application.resources) == 1\n        event = application.resources[0]\n        assert isinstance(event, models.ScheduledEvent)\n        assert event.resource_name == 'cron-event'\n        assert event.rule_name == 'scheduled-event-dev-cron-event'\n        assert isinstance(event.lambda_function, models.LambdaFunction)\n        assert event.lambda_function.resource_name == 'cron'\n\n    def test_can_build_private_rest_api(self, sample_app):\n        config = self.create_config(sample_app,\n                                    app_name='sample-app',\n                                    api_gateway_endpoint_type='PRIVATE',\n                                    api_gateway_endpoint_vpce='vpce-abc123')\n        builder = ApplicationGraphBuilder()\n        application = builder.build(config, stage_name='dev')\n        rest_api = application.resources[0]\n        assert isinstance(rest_api, models.RestAPI)\n        assert rest_api.policy.document == {\n            'Version': '2012-10-17',\n            'Statement': [\n                {'Action': 'execute-api:Invoke',\n                 'Effect': 'Allow',\n                 'Principal': '*',\n                 'Resource': 'arn:*:execute-api:*:*:*',\n                 'Condition': {\n                     'StringEquals': {\n                         'aws:SourceVpce': 'vpce-abc123'}}},\n            ]\n        }\n\n    def test_can_build_private_rest_api_custom_policy(\n            self, tmpdir, sample_app):\n        config = self.create_config(sample_app,\n                                    app_name='rest-api-app',\n                                    api_gateway_policy_file='foo.json',\n                                    api_gateway_endpoint_type='PRIVATE',\n                                    project_dir=str(tmpdir))\n        tmpdir.mkdir('.chalice').join('foo.json').write(\n            serialize_to_json({'Version': '2012-10-17', 'Statement': []}))\n        application_builder = ApplicationGraphBuilder()\n        build_stage = BuildStage(\n            steps=[\n                PolicyGenerator(osutils=OSUtils(), policy_gen=None)\n            ]\n         )\n        application = application_builder.build(config, stage_name='dev')\n        build_stage.execute(config, application.resources)\n        rest_api = application.resources[0]\n        assert rest_api.policy.document == {\n                'Version': '2012-10-17', 'Statement': []\n            }\n\n    def test_can_build_rest_api(self, sample_app):\n        config = self.create_config(sample_app,\n                                    app_name='sample-app',\n                                    autogen_policy=True)\n        builder = ApplicationGraphBuilder()\n        application = builder.build(config, stage_name='dev')\n        assert len(application.resources) == 1\n        rest_api = application.resources[0]\n        assert isinstance(rest_api, models.RestAPI)\n        assert rest_api.resource_name == 'rest_api'\n        assert rest_api.api_gateway_stage == 'api'\n        assert rest_api.lambda_function.resource_name == 'api_handler'\n        assert rest_api.lambda_function.function_name == 'sample-app-dev'\n        # The swagger document is validated elsewhere so we just\n        # make sure it looks right.\n        assert rest_api.swagger_doc == models.Placeholder.BUILD_STAGE\n\n    def test_can_build_rest_api_with_authorizer(self, sample_app_with_auth):\n        config = self.create_config(sample_app_with_auth,\n                                    app_name='rest-api-app',\n                                    autogen_policy=True)\n        builder = ApplicationGraphBuilder()\n        application = builder.build(config, stage_name='dev')\n        rest_api = application.resources[0]\n        assert len(rest_api.authorizers) == 1\n        assert isinstance(rest_api.authorizers[0], models.LambdaFunction)\n\n    def test_can_create_s3_event_handler(self, sample_s3_event_app):\n        # TODO: don't require app name, get it from app obj.\n        config = self.create_config(sample_s3_event_app,\n                                    app_name='s3-event-app',\n                                    autogen_policy=True)\n        builder = ApplicationGraphBuilder()\n        application = builder.build(config, stage_name='dev')\n        assert len(application.resources) == 1\n        s3_event = application.resources[0]\n        assert isinstance(s3_event, models.S3BucketNotification)\n        assert s3_event.resource_name == 'handler-s3event'\n        assert s3_event.bucket == 'mybucket'\n        assert s3_event.events == ['s3:ObjectCreated:*']\n        lambda_function = s3_event.lambda_function\n        assert lambda_function.resource_name == 'handler'\n        assert lambda_function.handler == 'app.handler'\n\n    def test_can_create_sns_event_handler(self, sample_sns_event_app):\n        config = self.create_config(sample_sns_event_app,\n                                    app_name='s3-event-app',\n                                    autogen_policy=True)\n        builder = ApplicationGraphBuilder()\n        application = builder.build(config, stage_name='dev')\n        assert len(application.resources) == 1\n        sns_event = application.resources[0]\n        assert isinstance(sns_event, models.SNSLambdaSubscription)\n        assert sns_event.resource_name == 'handler-sns-subscription'\n        assert sns_event.topic == 'mytopic'\n        lambda_function = sns_event.lambda_function\n        assert lambda_function.resource_name == 'handler'\n        assert lambda_function.handler == 'app.handler'\n\n    def test_can_create_sqs_event_handler(self, sample_sqs_event_app):\n        config = self.create_config(sample_sqs_event_app,\n                                    app_name='sqs-event-app',\n                                    autogen_policy=True)\n        builder = ApplicationGraphBuilder()\n        application = builder.build(config, stage_name='dev')\n        assert len(application.resources) == 1\n        sqs_event = application.resources[0]\n        assert isinstance(sqs_event, models.SQSEventSource)\n        assert sqs_event.resource_name == 'handler-sqs-event-source'\n        assert sqs_event.queue == 'myqueue'\n        lambda_function = sqs_event.lambda_function\n        assert lambda_function.resource_name == 'handler'\n        assert lambda_function.handler == 'app.handler'\n\n    def test_can_create_sqs_handler_with_queue_arn(self, sample_sqs_event_app):\n        @sample_sqs_event_app.on_sqs_message(queue_arn='arn:my:queue')\n        def new_handler(event):\n            pass\n\n        config = self.create_config(sample_sqs_event_app,\n                                    app_name='sqs-event-app',\n                                    autogen_policy=True)\n        builder = ApplicationGraphBuilder()\n        application = builder.build(config, stage_name='dev')\n        sqs_event = application.resources[1]\n        assert sqs_event.queue == models.QueueARN(arn='arn:my:queue')\n        lambda_function = sqs_event.lambda_function\n        assert lambda_function.resource_name == 'new_handler'\n        assert lambda_function.handler == 'app.new_handler'\n\n    def test_can_create_kinesis_event_handler(self, sample_kinesis_event_app):\n        config = self.create_config(sample_kinesis_event_app,\n                                    app_name='kinesis-event-app',\n                                    autogen_policy=True)\n        builder = ApplicationGraphBuilder()\n        application = builder.build(config, stage_name='dev')\n        assert len(application.resources) == 1\n        kinesis_event = application.resources[0]\n        assert isinstance(kinesis_event, models.KinesisEventSource)\n        assert kinesis_event.resource_name == 'handler-kinesis-event-source'\n        assert kinesis_event.stream == 'mystream'\n        lambda_function = kinesis_event.lambda_function\n        assert lambda_function.resource_name == 'handler'\n        assert lambda_function.handler == 'app.handler'\n\n    def test_can_create_ddb_event_handler(self, sample_ddb_event_app):\n        config = self.create_config(sample_ddb_event_app,\n                                    app_name='ddb-event-app',\n                                    autogen_policy=True)\n        builder = ApplicationGraphBuilder()\n        application = builder.build(config, stage_name='dev')\n        assert len(application.resources) == 1\n        ddb_event = application.resources[0]\n        assert isinstance(ddb_event, models.DynamoDBEventSource)\n        assert ddb_event.resource_name == 'handler-dynamodb-event-source'\n        assert ddb_event.stream_arn == 'arn:aws:...:stream'\n        lambda_function = ddb_event.lambda_function\n        assert lambda_function.resource_name == 'handler'\n        assert lambda_function.handler == 'app.handler'\n\n    def test_can_create_websocket_event_handler(self, sample_websocket_app):\n        config = self.create_config(sample_websocket_app,\n                                    app_name='websocket-app',\n                                    autogen_policy=True)\n        builder = ApplicationGraphBuilder()\n        application = builder.build(config, stage_name='dev')\n        assert len(application.resources) == 1\n        websocket_api = application.resources[0]\n        assert isinstance(websocket_api, models.WebsocketAPI)\n        assert websocket_api.resource_name == 'websocket_api'\n        assert sorted(websocket_api.routes) == sorted(\n            ['$connect', '$default', '$disconnect'])\n        assert websocket_api.api_gateway_stage == 'api'\n\n        connect_function = websocket_api.connect_function\n        assert connect_function.resource_name == 'websocket_connect'\n        assert connect_function.handler == 'app.connect'\n\n        message_function = websocket_api.message_function\n        assert message_function.resource_name == 'websocket_message'\n        assert message_function.handler == 'app.message'\n\n        disconnect_function = websocket_api.disconnect_function\n        assert disconnect_function.resource_name == 'websocket_disconnect'\n        assert disconnect_function.handler == 'app.disconnect'\n\n    def test_can_create_websocket_api_with_domain_name(self,\n                                                       sample_websocket_app):\n        domain_name = {\n            'domain_name': 'example.com',\n            'tls_version': 'TLS_1_2',\n            'certificate_arn': 'certificate_arn',\n            'tags': {\n                'tag_key1': 'tag_value1',\n                'tag_key2': 'tag_value2'\n            }\n        }\n        config = self.create_config(sample_websocket_app,\n                                    app_name='websocket-app',\n                                    autogen_policy=True,\n                                    websocket_api_custom_domain=domain_name)\n        builder = ApplicationGraphBuilder()\n        application = builder.build(config, stage_name='dev')\n        websocket_api = application.resources[0]\n        assert isinstance(websocket_api, models.WebsocketAPI)\n\n        domain_name = websocket_api.domain_name\n        assert isinstance(domain_name, models.DomainName)\n        assert isinstance(domain_name.api_mapping, models.APIMapping)\n        assert domain_name.api_mapping.mount_path == '(none)'\n\n    def test_can_create_websocket_app_missing_connect(\n            self, websocket_app_without_connect):\n        config = self.create_config(websocket_app_without_connect,\n                                    app_name='websocket-app',\n                                    autogen_policy=True)\n        builder = ApplicationGraphBuilder()\n        application = builder.build(config, stage_name='dev')\n        assert len(application.resources) == 1\n        websocket_api = application.resources[0]\n        assert isinstance(websocket_api, models.WebsocketAPI)\n        assert websocket_api.resource_name == 'websocket_api'\n        assert sorted(websocket_api.routes) == sorted(\n            ['$default', '$disconnect'])\n        assert websocket_api.api_gateway_stage == 'api'\n\n        connect_function = websocket_api.connect_function\n        assert connect_function is None\n\n        message_function = websocket_api.message_function\n        assert message_function.resource_name == 'websocket_message'\n        assert message_function.handler == 'app.message'\n\n        disconnect_function = websocket_api.disconnect_function\n        assert disconnect_function.resource_name == 'websocket_disconnect'\n        assert disconnect_function.handler == 'app.disconnect'\n\n    def test_can_create_websocket_app_missing_message(\n            self, websocket_app_without_message):\n        config = self.create_config(websocket_app_without_message,\n                                    app_name='websocket-app',\n                                    autogen_policy=True)\n        builder = ApplicationGraphBuilder()\n        application = builder.build(config, stage_name='dev')\n        assert len(application.resources) == 1\n        websocket_api = application.resources[0]\n        assert isinstance(websocket_api, models.WebsocketAPI)\n        assert websocket_api.resource_name == 'websocket_api'\n        assert sorted(websocket_api.routes) == sorted(\n            ['$connect', '$disconnect'])\n        assert websocket_api.api_gateway_stage == 'api'\n\n        connect_function = websocket_api.connect_function\n        assert connect_function.resource_name == 'websocket_connect'\n        assert connect_function.handler == 'app.connect'\n\n        disconnect_function = websocket_api.disconnect_function\n        assert disconnect_function.resource_name == 'websocket_disconnect'\n        assert disconnect_function.handler == 'app.disconnect'\n\n    def test_can_create_websocket_app_missing_disconnect(\n            self, websocket_app_without_disconnect):\n        config = self.create_config(websocket_app_without_disconnect,\n                                    app_name='websocket-app',\n                                    autogen_policy=True)\n        builder = ApplicationGraphBuilder()\n        application = builder.build(config, stage_name='dev')\n        assert len(application.resources) == 1\n        websocket_api = application.resources[0]\n        assert isinstance(websocket_api, models.WebsocketAPI)\n        assert websocket_api.resource_name == 'websocket_api'\n        assert sorted(websocket_api.routes) == sorted(\n            ['$connect', '$default'])\n        assert websocket_api.api_gateway_stage == 'api'\n\n        connect_function = websocket_api.connect_function\n        assert connect_function.resource_name == 'websocket_connect'\n        assert connect_function.handler == 'app.connect'\n\n        message_function = websocket_api.message_function\n        assert message_function.resource_name == 'websocket_message'\n        assert message_function.handler == 'app.message'\n"
  },
  {
    "path": "tests/unit/deploy/test_deployer.py",
    "content": "from __future__ import annotations\nimport os\nfrom dataclasses import dataclass\nimport socket\n\nimport botocore.session\n\nimport pytest\nfrom unittest import mock\nfrom botocore.stub import Stubber\nfrom botocore.vendored.requests import ConnectionError as \\\n    RequestsConnectionError\nfrom pytest import fixture\n\nfrom chalice.app import Chalice\nfrom chalice.awsclient import LambdaClientError, AWSClientError\nfrom chalice.awsclient import DeploymentPackageTooLargeError\nfrom chalice.awsclient import LambdaErrorContext\nfrom chalice.config import Config\nfrom chalice.policy import AppPolicyGenerator\nfrom chalice.deploy.deployer import ChaliceDeploymentError\nfrom chalice.utils import UI\nimport unittest\n\nfrom chalice.awsclient import TypedAWSClient\nfrom chalice.utils import OSUtils, serialize_to_json\nfrom chalice.deploy import models\nfrom chalice.deploy import packager\nfrom chalice.deploy.deployer import create_default_deployer, \\\n    create_deletion_deployer, Deployer, BaseDeployStep, \\\n    InjectDefaults, DeploymentPackager, SwaggerBuilder, \\\n    PolicyGenerator, BuildStage, ResultsRecorder, DeploymentReporter, \\\n    ManagedLayerDeploymentPackager\nfrom chalice.deploy.appgraph import ApplicationGraphBuilder, \\\n    DependencyBuilder\nfrom chalice.deploy.executor import Executor\nfrom chalice.deploy.swagger import SwaggerGenerator, TemplatedSwaggerGenerator\nfrom chalice.deploy.planner import PlanStage\nfrom chalice.deploy.planner import StringFormat\nfrom chalice.deploy.sweeper import ResourceSweeper\nfrom chalice.deploy.models import APICall\nfrom chalice.constants import VPC_ATTACH_POLICY\nfrom chalice.constants import SQS_EVENT_SOURCE_POLICY\nfrom chalice.constants import KINESIS_EVENT_SOURCE_POLICY\nfrom chalice.constants import DDB_EVENT_SOURCE_POLICY\nfrom chalice.constants import POST_TO_WEBSOCKET_CONNECTION_POLICY\nfrom chalice.deploy.deployer import LambdaEventSourcePolicyInjector\nfrom chalice.deploy.deployer import WebsocketPolicyInjector\n\n\n_SESSION = None\n\n\nclass InMemoryOSUtils(object):\n    def __init__(self, filemap=None):\n        if filemap is None:\n            filemap = {}\n        self.filemap = filemap\n\n    def file_exists(self, filename):\n        return filename in self.filemap\n\n    def get_file_contents(self, filename, binary=True):\n        return self.filemap[filename]\n\n    def set_file_contents(self, filename, contents, binary=True):\n        self.filemap[filename] = contents\n\n\n@fixture\ndef in_memory_osutils():\n    return InMemoryOSUtils()\n\n\ndef stubbed_client(service_name):\n    global _SESSION\n    if _SESSION is None:\n        _SESSION = botocore.session.get_session()\n    client = _SESSION.create_client(service_name,\n                                    region_name='us-west-2')\n    stubber = Stubber(client)\n    return client, stubber\n\n\n@fixture\ndef config_obj(sample_app):\n    config = Config.create(\n        chalice_app=sample_app,\n        stage='dev',\n        api_gateway_stage='api',\n    )\n    return config\n\n\n@fixture\ndef ui():\n    return mock.Mock(spec=UI)\n\n\nclass TestChaliceDeploymentError(object):\n    def test_general_exception(self):\n        general_exception = Exception('My Exception')\n        deploy_error = ChaliceDeploymentError(general_exception)\n        deploy_error_msg = str(deploy_error)\n        assert (\n            'ERROR - While deploying your chalice application'\n            in deploy_error_msg\n        )\n        assert 'My Exception' in deploy_error_msg\n\n    def test_lambda_client_error(self):\n        lambda_error = LambdaClientError(\n            Exception('My Exception'),\n            context=LambdaErrorContext(\n                function_name='foo',\n                client_method_name='create_function',\n                deployment_size=1024 ** 2\n            )\n        )\n        deploy_error = ChaliceDeploymentError(lambda_error)\n        deploy_error_msg = str(deploy_error)\n        assert (\n            'ERROR - While sending your chalice handler code to '\n            'Lambda to create function \\n\"foo\"' in deploy_error_msg\n        )\n        assert 'My Exception' in deploy_error_msg\n\n    def test_lambda_client_error_wording_for_update(self):\n        lambda_error = LambdaClientError(\n            Exception('My Exception'),\n            context=LambdaErrorContext(\n                function_name='foo',\n                client_method_name='update_function_code',\n                deployment_size=1024 ** 2\n            )\n        )\n        deploy_error = ChaliceDeploymentError(lambda_error)\n        deploy_error_msg = str(deploy_error)\n        assert (\n            'sending your chalice handler code to '\n            'Lambda to update function' in deploy_error_msg\n        )\n\n    def test_gives_where_and_suggestion_for_too_large_deployment_error(self):\n        too_large_error = DeploymentPackageTooLargeError(\n            Exception('Too large of deployment pacakge'),\n            context=LambdaErrorContext(\n                function_name='foo',\n                client_method_name='create_function',\n                deployment_size=1024 ** 2,\n            )\n        )\n        deploy_error = ChaliceDeploymentError(too_large_error)\n        deploy_error_msg = str(deploy_error)\n        assert (\n            'ERROR - While sending your chalice handler code to '\n            'Lambda to create function \\n\"foo\"' in deploy_error_msg\n        )\n        assert 'Too large of deployment pacakge' in deploy_error_msg\n        assert (\n            'To avoid this error, decrease the size of your chalice '\n            'application ' in deploy_error_msg\n        )\n\n    def test_include_size_context_for_too_large_deployment_error(self):\n        too_large_error = DeploymentPackageTooLargeError(\n            Exception('Too large of deployment pacakge'),\n            context=LambdaErrorContext(\n                function_name='foo',\n                client_method_name='create_function',\n                deployment_size=58 * (1024 ** 2),\n            )\n        )\n        deploy_error = ChaliceDeploymentError(\n            too_large_error)\n        deploy_error_msg = str(deploy_error)\n        print(repr(deploy_error_msg))\n        assert 'deployment package is 58.0 MB' in deploy_error_msg\n        assert '50.0 MB or less' in deploy_error_msg\n        assert 'To avoid this error' in deploy_error_msg\n\n    def test_error_msg_for_general_connection(self):\n        lambda_error = DeploymentPackageTooLargeError(\n            RequestsConnectionError(\n                Exception(\n                    'Connection aborted.',\n                    socket.error('Some vague reason')\n                )\n            ),\n            context=LambdaErrorContext(\n                function_name='foo',\n                client_method_name='create_function',\n                deployment_size=1024 ** 2\n            )\n        )\n        deploy_error = ChaliceDeploymentError(lambda_error)\n        deploy_error_msg = str(deploy_error)\n        assert 'Connection aborted.' in deploy_error_msg\n        assert 'Some vague reason' not in deploy_error_msg\n\n    def test_simplifies_error_msg_for_broken_pipe(self):\n        lambda_error = DeploymentPackageTooLargeError(\n            RequestsConnectionError(\n                Exception(\n                    'Connection aborted.',\n                    socket.error(32, 'Broken pipe')\n                )\n            ),\n            context=LambdaErrorContext(\n                function_name='foo',\n                client_method_name='create_function',\n                deployment_size=1024 ** 2\n            )\n        )\n        deploy_error = ChaliceDeploymentError(lambda_error)\n        deploy_error_msg = str(deploy_error)\n        assert (\n            'Connection aborted. Lambda closed the connection' in\n            deploy_error_msg\n        )\n\n    def test_simplifies_error_msg_for_timeout(self):\n        lambda_error = DeploymentPackageTooLargeError(\n            RequestsConnectionError(\n                Exception(\n                    'Connection aborted.',\n                    socket.timeout('The write operation timed out')\n                )\n            ),\n            context=LambdaErrorContext(\n                function_name='foo',\n                client_method_name='create_function',\n                deployment_size=1024 ** 2\n            )\n        )\n        deploy_error = ChaliceDeploymentError(lambda_error)\n        deploy_error_msg = str(deploy_error)\n        assert (\n            'Connection aborted. Timed out sending your app to Lambda.' in\n            deploy_error_msg\n        )\n\n\n@dataclass\nclass FooResource(models.Model):\n    name: str\n    leaf: LeafResource\n\n    def dependencies(self):\n        if not isinstance(self.leaf, list):\n            return [self.leaf]\n        return self.leaf\n\n\n@dataclass\nclass LeafResource(models.Model):\n    name: str\n\n\n@fixture\ndef mock_client():\n    return mock.Mock(spec=TypedAWSClient)\n\n\n@fixture\ndef mock_osutils():\n    return mock.Mock(spec=OSUtils)\n\n\ndef create_function_resource(name):\n    return models.LambdaFunction(\n        resource_name=name,\n        function_name='appname-dev-%s' % name,\n        environment_variables={},\n        runtime='python2.7',\n        handler='app.app',\n        tags={},\n        timeout=60,\n        memory_size=128,\n        deployment_package=models.DeploymentPackage(\n            models.Placeholder.BUILD_STAGE\n        ),\n        xray=False,\n        role=models.PreCreatedIAMRole(role_arn='role:arn'),\n        security_group_ids=[],\n        subnet_ids=[],\n        layers=[],\n        reserved_concurrency=None,\n    )\n\n\nclass TestDependencyBuilder(object):\n    def test_can_build_resource_with_single_dep(self):\n        role = models.PreCreatedIAMRole(role_arn='foo')\n        app = models.Application(stage='dev', resources=[role])\n\n        dep_builder = DependencyBuilder()\n        deps = dep_builder.build_dependencies(app)\n        assert deps == [role]\n\n    def test_can_build_resource_with_dag_deps(self):\n        shared_leaf = LeafResource(name='leaf-resource')\n        first_parent = FooResource(name='first', leaf=shared_leaf)\n        second_parent = FooResource(name='second', leaf=shared_leaf)\n        app = models.Application(\n            stage='dev', resources=[first_parent, second_parent])\n\n        dep_builder = DependencyBuilder()\n        deps = dep_builder.build_dependencies(app)\n        assert deps == [shared_leaf, first_parent, second_parent]\n\n    def test_is_first_element_in_list(self):\n        shared_leaf = LeafResource(name='leaf-resource')\n        first_parent = FooResource(name='first', leaf=shared_leaf)\n        app = models.Application(\n            stage='dev', resources=[first_parent, shared_leaf],\n        )\n        dep_builder = DependencyBuilder()\n        deps = dep_builder.build_dependencies(app)\n        assert deps == [shared_leaf, first_parent]\n\n    def test_can_compares_with_identity_not_equality(self):\n        first_leaf = LeafResource(name='same-name')\n        second_leaf = LeafResource(name='same-name')\n        first_parent = FooResource(name='first', leaf=first_leaf)\n        second_parent = FooResource(name='second', leaf=second_leaf)\n        app = models.Application(\n            stage='dev', resources=[first_parent, second_parent])\n\n        dep_builder = DependencyBuilder()\n        deps = dep_builder.build_dependencies(app)\n        assert deps == [first_leaf, first_parent, second_leaf, second_parent]\n\n    def test_no_duplicate_depedencies(self):\n        leaf = LeafResource(name='leaf')\n        second_parent = FooResource(name='second', leaf=leaf)\n        first_parent = FooResource(name='first', leaf=[leaf, second_parent])\n        app = models.Application(\n            stage='dev', resources=[first_parent])\n\n        dep_builder = DependencyBuilder()\n        deps = dep_builder.build_dependencies(app)\n        assert deps == [leaf, second_parent, first_parent]\n\n\nclass RoleTestCase(object):\n    def __init__(self, given, roles, app_name='appname'):\n        self.given = given\n        self.roles = roles\n        self.app_name = app_name\n\n    def build(self):\n        app = Chalice(self.app_name)\n\n        for name in self.given:\n            def foo(event, context):\n                return {}\n            foo.__name__ = name\n            app.lambda_function(name)(foo)\n\n        user_provided_params = {\n            'chalice_app': app,\n            'app_name': self.app_name,\n            'project_dir': '.',\n        }\n        lambda_functions = {}\n        for key, value in self.given.items():\n            lambda_functions[key] = value\n        config_from_disk = {\n            'stages': {\n                'dev': {\n                    'lambda_functions': lambda_functions,\n                }\n            }\n        }\n        config = Config(chalice_stage='dev',\n                        user_provided_params=user_provided_params,\n                        config_from_disk=config_from_disk)\n        return app, config\n\n    def assert_required_roles_created(self, application):\n        resources = application.resources\n        assert len(resources) == len(self.given)\n        functions_by_name = {\n            f.function_name: f for f in resources\n            if isinstance(f, models.LambdaFunction)}\n        # Roles that have the same name/arn should be the same\n        # object.  If we encounter a role that's already in\n        # roles_by_identifier, we'll verify that it's the exact same object.\n        roles_by_identifier = {}\n        for function_name, expected in self.roles.items():\n            full_name = 'appname-dev-%s' % function_name\n            assert full_name in functions_by_name\n            actual_role = functions_by_name[full_name].role\n            expectations = self.roles[function_name]\n            if not expectations.get('managed_role', True):\n                actual_role_arn = actual_role.role_arn\n                assert isinstance(actual_role, models.PreCreatedIAMRole)\n                assert expectations['iam_role_arn'] == actual_role_arn\n                if actual_role_arn in roles_by_identifier:\n                    assert roles_by_identifier[actual_role_arn] is actual_role\n                roles_by_identifier[actual_role_arn] = actual_role\n                continue\n            actual_name = actual_role.role_name\n            assert expectations['name'] == actual_name\n            if actual_name in roles_by_identifier:\n                assert roles_by_identifier[actual_name] is actual_role\n            roles_by_identifier[actual_name] = actual_role\n            is_autogenerated = expectations.get('autogenerated', False)\n            policy_file = expectations.get('policy_file')\n            if is_autogenerated:\n                assert isinstance(actual_role, models.ManagedIAMRole)\n                assert isinstance(actual_role.policy, models.AutoGenIAMPolicy)\n            if policy_file is not None and not is_autogenerated:\n                assert isinstance(actual_role, models.ManagedIAMRole)\n                assert isinstance(actual_role.policy,\n                                  models.FileBasedIAMPolicy)\n                assert actual_role.policy.filename == os.path.join(\n                    '.', '.chalice', expectations['policy_file'])\n\n\n# How to read these tests:\n# 'given' is a mapping of lambda function name to config values.\n# 'roles' is a mapping of lambda function to expected attributes\n# of the role associated with the given function.\n# The first test case is explained in more detail as an example.\nROLE_TEST_CASES = [\n    # Default case, we use the shared 'appname-dev' role.\n    RoleTestCase(\n        # Given we have a lambda function in our app.py named 'a',\n        # and we have our config file state that the 'a' function\n        # should have an autogen'd policy,\n        given={'a': {'autogen_policy': True}},\n        # then we expect the IAM role associated with the lambda\n        # function 'a' should be named 'appname-dev', and it should\n        # be an autogenerated role/policy.\n        roles={'a': {'name': 'appname-dev', 'autogenerated': True}}),\n    # If you specify an explicit policy, we generate a function\n    # specific role.\n    RoleTestCase(\n        given={'a': {'autogen_policy': False,\n                     'iam_policy_file': 'mypolicy.json'}},\n        roles={'a': {'name': 'appname-dev-a',\n                     'autogenerated': False,\n                     'policy_file': 'mypolicy.json'}}),\n    # Multiple lambda functions that use autogen policies share\n    # the same 'appname-dev' role.\n    RoleTestCase(\n        given={'a': {'autogen_policy': True},\n               'b': {'autogen_policy': True}},\n        roles={'a': {'name': 'appname-dev'},\n               'b': {'name': 'appname-dev'}}),\n    # Multiple lambda functions with separate policies result\n    # in separate roles.\n    RoleTestCase(\n        given={'a': {'autogen_policy': False,\n                     'iam_policy_file': 'a.json'},\n               'b': {'autogen_policy': False,\n                     'iam_policy_file': 'b.json'}},\n        roles={'a': {'name': 'appname-dev-a',\n                     'autogenerated': False,\n                     'policy_file': 'a.json'},\n               'b': {'name': 'appname-dev-b',\n                     'autogenerated': False,\n                     'policy_file': 'b.json'}}),\n    # You can mix autogen and explicit policy files.  Autogen will\n    # always use the '{app}-{stage}' role.\n    RoleTestCase(\n        given={'a': {'autogen_policy': True},\n               'b': {'autogen_policy': False,\n                     'iam_policy_file': 'b.json'}},\n        roles={'a': {'name': 'appname-dev',\n                     'autogenerated': True},\n               'b': {'name': 'appname-dev-b',\n                     'autogenerated': False,\n                     'policy_file': 'b.json'}}),\n    # Default location if no policy file is given is\n    # policy-dev.json\n    RoleTestCase(\n        given={'a': {'autogen_policy': False}},\n        roles={'a': {'name': 'appname-dev-a',\n                     'autogenerated': False,\n                     'policy_file': 'policy-dev.json'}}),\n    # As soon as autogen_policy is false, we will *always*\n    # create a function specific role.\n    RoleTestCase(\n        given={'a': {'autogen_policy': False},\n               'b': {'autogen_policy': True}},\n        roles={'a': {'name': 'appname-dev-a',\n                     'autogenerated': False,\n                     'policy_file': 'policy-dev.json'},\n               'b': {'name': 'appname-dev'}}),\n    RoleTestCase(\n        given={'a': {'manage_iam_role': False, 'iam_role_arn': 'role:arn'}},\n        # 'managed_role' will verify the associated role is a\n        # models.PreCreatedIAMRoleType with the provided iam_role_arn.\n        roles={'a': {'managed_role': False, 'iam_role_arn': 'role:arn'}}),\n    # Verify that we can use the same non-managed role for multiple\n    # lambda functions.\n    RoleTestCase(\n        given={'a': {'manage_iam_role': False, 'iam_role_arn': 'role:arn'},\n               'b': {'manage_iam_role': False, 'iam_role_arn': 'role:arn'}},\n        roles={'a': {'managed_role': False, 'iam_role_arn': 'role:arn'},\n               'b': {'managed_role': False, 'iam_role_arn': 'role:arn'}}),\n    RoleTestCase(\n        given={'a': {'manage_iam_role': False, 'iam_role_arn': 'role:arn'},\n               'b': {'autogen_policy': True}},\n        roles={'a': {'managed_role': False, 'iam_role_arn': 'role:arn'},\n               'b': {'name': 'appname-dev', 'autogenerated': True}}),\n\n    # Functions that mix all four options:\n    RoleTestCase(\n        # 2 functions with autogen'd policies.\n        given={\n            'a': {'autogen_policy': True},\n            'b': {'autogen_policy': True},\n            # 2 functions with various iam role arns.\n            'c': {'manage_iam_role': False, 'iam_role_arn': 'role:arn'},\n            'd': {'manage_iam_role': False, 'iam_role_arn': 'role:arn2'},\n            # A function with a default filename for a policy.\n            'e': {'autogen_policy': False},\n            # Even though this uses the same policy as 'e', we will\n            # still create a new role.  This could be optimized in the\n            # future.\n            'f': {'autogen_policy': False},\n            # And finally 2 functions that have their own policy files.\n            'g': {'autogen_policy': False, 'iam_policy_file': 'g.json'},\n            'h': {'autogen_policy': False, 'iam_policy_file': 'h.json'}\n        },\n        roles={\n            'a': {'name': 'appname-dev', 'autogenerated': True},\n            'b': {'name': 'appname-dev', 'autogenerated': True},\n            'c': {'managed_role': False, 'iam_role_arn': 'role:arn'},\n            'd': {'managed_role': False, 'iam_role_arn': 'role:arn2'},\n            'e': {'name': 'appname-dev-e',\n                  'autogenerated': False,\n                  'policy_file': 'policy-dev.json'},\n            'f': {'name': 'appname-dev-f',\n                  'autogenerated': False,\n                  'policy_file': 'policy-dev.json'},\n            'g': {'name': 'appname-dev-g',\n                  'autogenerated': False,\n                  'policy_file': 'g.json'},\n            'h': {'name': 'appname-dev-h',\n                  'autogenerated': False,\n                  'policy_file': 'h.json'},\n        }),\n]\n\n\n@pytest.mark.parametrize('case', ROLE_TEST_CASES)\ndef test_role_creation(case):\n    _, config = case.build()\n    builder = ApplicationGraphBuilder()\n    application = builder.build(config, stage_name='dev')\n    case.assert_required_roles_created(application)\n\n\nclass TestDefaultsInjector(object):\n    def test_inject_when_values_are_none(self):\n        injector = InjectDefaults(\n            lambda_timeout=100,\n            lambda_memory_size=512,\n        )\n        function = models.LambdaFunction(\n            # The timeout/memory_size are set to\n            # None, so the injector should fill them\n            # in the with the default values above.\n            timeout=None,\n            memory_size=None,\n            resource_name='foo',\n            function_name='app-dev-foo',\n            environment_variables={},\n            runtime='python2.7',\n            handler='app.app',\n            tags={},\n            xray=None,\n            deployment_package=None,\n            role=None,\n            security_group_ids=[],\n            subnet_ids=[],\n            layers=[],\n            reserved_concurrency=None,\n        )\n        config = Config.create()\n        injector.handle(config, function)\n        assert function.timeout == 100\n        assert function.memory_size == 512\n\n    def test_no_injection_when_values_are_set(self):\n        injector = InjectDefaults(\n            lambda_timeout=100,\n            lambda_memory_size=512,\n        )\n        function = models.LambdaFunction(\n            # The timeout/memory_size are set to\n            # None, so the injector should fill them\n            # in the with the default values above.\n            timeout=1,\n            memory_size=1,\n            resource_name='foo',\n            function_name='app-stage-foo',\n            environment_variables={},\n            runtime='python2.7',\n            handler='app.app',\n            tags={},\n            xray=None,\n            deployment_package=None,\n            role=None,\n            security_group_ids=[],\n            subnet_ids=[],\n            layers=[],\n            reserved_concurrency=None,\n        )\n        config = Config.create()\n        injector.handle(config, function)\n        assert function.timeout == 1\n        assert function.memory_size == 1\n\n    def test_default_tls_version_on_domain_name(self):\n        injector = InjectDefaults(tls_version='TLS_1_2')\n        domain_name = models.DomainName(\n            resource_name='my_domain_name',\n            domain_name='example.com',\n            protocol=models.APIType.HTTP,\n            certificate_arn='myarn',\n            api_mapping=models.APIMapping(resource_name='mymapping',\n                                          mount_path='(none)',\n                                          api_gateway_stage='api')\n        )\n        config = Config.create()\n        injector.handle(config, domain_name)\n        assert domain_name.tls_version == models.TLSVersion.TLS_1_2\n\n\nclass TestPolicyGeneratorStage(object):\n    def setup_method(self):\n        self.osutils = mock.Mock(spec=OSUtils)\n\n    def create_policy_generator(self, generator=None):\n        if generator is None:\n            generator = mock.Mock(spec=AppPolicyGenerator)\n        p = PolicyGenerator(generator, self.osutils)\n        return p\n\n    def test_invokes_policy_generator(self):\n        generator = mock.Mock(spec=AppPolicyGenerator)\n        generator.generate_policy.return_value = {'policy': 'doc'}\n        policy = models.AutoGenIAMPolicy(models.Placeholder.BUILD_STAGE)\n        config = Config.create()\n\n        p = self.create_policy_generator(generator)\n        p.handle(config, policy)\n\n        assert policy.document == {'policy': 'doc'}\n\n    def test_no_policy_generated_if_exists(self):\n        generator = mock.Mock(spec=AppPolicyGenerator)\n        generator.generate_policy.return_value = {'policy': 'new'}\n        policy = models.AutoGenIAMPolicy(document={'policy': 'original'})\n        config = Config.create()\n\n        p = self.create_policy_generator(generator)\n        p.handle(config, policy)\n\n        assert policy.document == {'policy': 'original'}\n        assert not generator.generate_policy.called\n\n    def test_policy_loaded_from_file_if_needed(self):\n        p = self.create_policy_generator()\n        policy = models.FileBasedIAMPolicy(\n            filename='foo.json', document=models.Placeholder.BUILD_STAGE)\n        self.osutils.get_file_contents.return_value = '{\"iam\": \"policy\"}'\n\n        p.handle(Config.create(), policy)\n\n        assert policy.document == {'iam': 'policy'}\n        self.osutils.get_file_contents.assert_called_with('foo.json')\n\n    def test_error_raised_if_file_policy_not_exists(self):\n        p = self.create_policy_generator()\n        policy = models.FileBasedIAMPolicy(\n            filename='foo.json', document=models.Placeholder.BUILD_STAGE)\n        self.osutils.get_file_contents.side_effect = IOError()\n\n        with pytest.raises(RuntimeError):\n            p.handle(Config.create(), policy)\n\n    def test_vpc_policy_inject_if_needed(self):\n        generator = mock.Mock(spec=AppPolicyGenerator)\n        generator.generate_policy.return_value = {'Statement': []}\n        policy = models.AutoGenIAMPolicy(\n            document=models.Placeholder.BUILD_STAGE,\n            traits=set([models.RoleTraits.VPC_NEEDED]),\n        )\n        config = Config.create()\n\n        p = self.create_policy_generator(generator)\n        p.handle(config, policy)\n\n        assert policy.document['Statement'][0] == VPC_ATTACH_POLICY\n\n\nclass TestSwaggerBuilder(object):\n    def test_can_generate_swagger_builder(self):\n        generator = mock.Mock(spec=SwaggerGenerator)\n        generator.generate_swagger.return_value = {'swagger': '2.0'}\n\n        rest_api = models.RestAPI(\n            resource_name='foo',\n            swagger_doc=models.Placeholder.BUILD_STAGE,\n            minimum_compression='',\n            endpoint_type='EDGE',\n            api_gateway_stage='api',\n            lambda_function=None,\n            xray=False,\n        )\n        app = Chalice(app_name='foo')\n        config = Config.create(chalice_app=app)\n        p = SwaggerBuilder(generator)\n        p.handle(config, rest_api)\n        assert rest_api.swagger_doc == {'swagger': '2.0'}\n        generator.generate_swagger.assert_called_with(app, rest_api)\n\n\nclass TestDeploymentPackager(object):\n    def test_can_generate_layer_package(self):\n        function = create_function_resource('myfunction')\n        function.managed_layer = models.LambdaLayer(\n            resource_name='managed-layer',\n            layer_name='appname-dev-managed-layer',\n            runtime='python2.7',\n            deployment_package=models.DeploymentPackage(\n                models.Placeholder.BUILD_STAGE\n            )\n        )\n        lambda_packager = mock.Mock(spec=packager.BaseLambdaDeploymentPackager)\n        layer_packager = mock.Mock(spec=packager.BaseLambdaDeploymentPackager)\n        lambda_packager.create_deployment_package.return_value = 'package.zip'\n        layer_packager.create_deployment_package.return_value = (\n            'package-layer.zip')\n\n        config = Config.create(project_dir='.')\n\n        p = ManagedLayerDeploymentPackager(lambda_packager, layer_packager)\n        p.handle(config, function.managed_layer)\n        p.handle(config, function)\n        assert function.deployment_package.filename == 'package.zip'\n        lambda_packager.create_deployment_package.assert_called_with(\n            '.', config.lambda_python_version\n        )\n        assert function.managed_layer.deployment_package.filename == (\n            'package-layer.zip'\n        )\n        layer_packager.create_deployment_package.assert_called_with(\n            '.', config.lambda_python_version\n        )\n\n    def test_layer_package_not_generated_if_filename_populated(self):\n        generator = mock.Mock(spec=packager.BaseLambdaDeploymentPackager)\n\n        function = create_function_resource('myfunction')\n        layer = models.LambdaLayer(\n            resource_name='layer',\n            layer_name='name',\n            runtime='python2.7',\n            deployment_package=models.DeploymentPackage(\n                filename='original.zip')\n        )\n        function.managed_layer = layer\n        config = Config.create(project_dir='.')\n\n        p = ManagedLayerDeploymentPackager(None, generator)\n        p.handle(config, layer)\n\n        assert layer.deployment_package.filename == 'original.zip'\n        assert not generator.create_deployment_package.called\n\n    def test_managed_layer_removed_if_no_deps(self):\n        function = create_function_resource('myfunction')\n        function.managed_layer = models.LambdaLayer(\n            resource_name='managed-layer',\n            layer_name='appname-dev-managed-layer',\n            runtime='python2.7',\n            deployment_package=models.DeploymentPackage(\n                models.Placeholder.BUILD_STAGE\n            )\n        )\n        lambda_packager = mock.Mock(spec=packager.BaseLambdaDeploymentPackager)\n        layer_packager = mock.Mock(spec=packager.BaseLambdaDeploymentPackager)\n        lambda_packager.create_deployment_package.return_value = 'package.zip'\n        layer_packager.create_deployment_package.side_effect = \\\n            packager.EmptyPackageError()\n\n        config = Config.create(project_dir='.')\n\n        p = ManagedLayerDeploymentPackager(lambda_packager, layer_packager)\n        p.handle(config, function.managed_layer)\n        p.handle(config, function)\n        # If the deployment package for layers would result in an empty\n        # deployment package, we expect that resource to be removed, it can't\n        # be created on the service.\n        assert function.managed_layer is None\n\n    def test_can_generate_package(self):\n        generator = mock.Mock(spec=packager.LambdaDeploymentPackager)\n        generator.create_deployment_package.return_value = 'package.zip'\n\n        package = models.DeploymentPackage(models.Placeholder.BUILD_STAGE)\n        config = Config.create()\n\n        p = DeploymentPackager(generator)\n        p.handle(config, package)\n\n        assert package.filename == 'package.zip'\n\n    def test_package_not_generated_if_filename_populated(self):\n        generator = mock.Mock(spec=packager.LambdaDeploymentPackager)\n        generator.create_deployment_package.return_value = 'NEWPACKAGE.zip'\n\n        package = models.DeploymentPackage(filename='original-name.zip')\n        config = Config.create()\n\n        p = DeploymentPackager(generator)\n        p.handle(config, package)\n\n        assert package.filename == 'original-name.zip'\n        assert not generator.create_deployment_package.called\n\n\ndef test_build_stage():\n    first = mock.Mock(spec=BaseDeployStep)\n    second = mock.Mock(spec=BaseDeployStep)\n    build = BuildStage([first, second])\n\n    foo_resource = mock.sentinel.foo_resource\n    bar_resource = mock.sentinel.bar_resource\n    config = Config.create()\n    build.execute(config, [foo_resource, bar_resource])\n\n    assert first.handle.call_args_list == [\n        mock.call(config, foo_resource),\n        mock.call(config, bar_resource),\n    ]\n    assert second.handle.call_args_list == [\n        mock.call(config, foo_resource),\n        mock.call(config, bar_resource),\n    ]\n\n\nclass TestDeployer(unittest.TestCase):\n    def setUp(self):\n        self.resource_builder = mock.Mock(spec=ApplicationGraphBuilder)\n        self.deps_builder = mock.Mock(spec=DependencyBuilder)\n        self.build_stage = mock.Mock(spec=BuildStage)\n        self.plan_stage = mock.Mock(spec=PlanStage)\n        self.sweeper = mock.Mock(spec=ResourceSweeper)\n        self.executor = mock.Mock(spec=Executor)\n        self.recorder = mock.Mock(spec=ResultsRecorder)\n        self.chalice_app = Chalice(app_name='foo')\n\n    def create_deployer(self):\n        return Deployer(\n            self.resource_builder,\n            self.deps_builder,\n            self.build_stage,\n            self.plan_stage,\n            self.sweeper,\n            self.executor,\n            self.recorder,\n        )\n\n    def test_deploy_delegates_properly(self):\n        app = mock.Mock(spec=models.Application)\n        resources = [mock.Mock(spec=models.Model)]\n        api_calls = [mock.Mock(spec=APICall)]\n\n        self.resource_builder.build.return_value = app\n        self.deps_builder.build_dependencies.return_value = resources\n        self.plan_stage.execute.return_value = api_calls\n        self.executor.resource_values = {'foo': {'name': 'bar'}}\n\n        deployer = self.create_deployer()\n        config = Config.create(project_dir='.', chalice_app=self.chalice_app)\n        result = deployer.deploy(config, 'dev')\n\n        self.resource_builder.build.assert_called_with(config, 'dev')\n        self.deps_builder.build_dependencies.assert_called_with(app)\n        self.build_stage.execute.assert_called_with(config, resources)\n        self.plan_stage.execute.assert_called_with(resources)\n        self.sweeper.execute.assert_called_with(api_calls, config)\n        self.executor.execute.assert_called_with(api_calls)\n\n        expected_result = {\n            'resources': {'foo': {'name': 'bar'}},\n            'schema_version': '2.0',\n            'backend': 'api',\n        }\n\n        self.recorder.record_results.assert_called_with(\n            expected_result, 'dev', '.')\n        assert result == expected_result\n\n    def test_deploy_errors_raises_chalice_error(self):\n        self.resource_builder.build.side_effect = AWSClientError()\n\n        deployer = self.create_deployer()\n        config = Config.create(project_dir='.', chalice_app=self.chalice_app)\n        with pytest.raises(ChaliceDeploymentError):\n            deployer.deploy(config, 'dev')\n\n    def test_validation_errors_raise_failure(self):\n\n        @self.chalice_app.route('')\n        def bad_route_empty_string():\n            return {}\n\n        deployer = self.create_deployer()\n        config = Config.create(project_dir='.', chalice_app=self.chalice_app)\n        with pytest.raises(ChaliceDeploymentError):\n            deployer.deploy(config, 'dev')\n\n\ndef test_can_create_default_deployer():\n    session = botocore.session.get_session()\n    deployer = create_default_deployer(session, Config.create(\n        project_dir='.',\n        chalice_stage='dev',\n    ), UI())\n    assert isinstance(deployer, Deployer)\n\n\ndef test_can_create_deployer_with_layer_builds():\n    session = botocore.session.get_session()\n    deployer = create_default_deployer(session, Config.create(\n        project_dir='.',\n        chalice_stage='dev',\n        automatic_layer=True,\n    ), UI())\n    assert isinstance(deployer, Deployer)\n\n\ndef test_can_create_deletion_deployer():\n    session = botocore.session.get_session()\n    deployer = create_deletion_deployer(TypedAWSClient(session), UI())\n    assert isinstance(deployer, Deployer)\n\n\ndef test_templated_swagger_generator(sample_app):\n    doc = TemplatedSwaggerGenerator().generate_swagger(sample_app)\n    uri = doc['paths']['/']['get']['x-amazon-apigateway-integration']['uri']\n    assert isinstance(uri, StringFormat)\n    assert uri.template == (\n        'arn:{partition}:apigateway:{region_name}:lambda:path'\n        '/2015-03-31/functions/{api_handler_lambda_arn}/invocations'\n    )\n    assert uri.variables == ['partition', 'region_name',\n                             'api_handler_lambda_arn']\n\n\ndef test_templated_swagger_with_auth_uri(sample_app_with_auth):\n    doc = TemplatedSwaggerGenerator().generate_swagger(sample_app_with_auth)\n    uri = doc['securityDefinitions']['myauth'][\n        'x-amazon-apigateway-authorizer']['authorizerUri']\n    assert isinstance(uri, StringFormat)\n    assert uri.template == (\n        'arn:{partition}:apigateway:{region_name}:lambda:path'\n        '/2015-03-31/functions/{myauth_lambda_arn}/invocations'\n    )\n    assert uri.variables == ['partition', 'region_name', 'myauth_lambda_arn']\n\n\nclass TestRecordResults(object):\n    def setup_method(self):\n        self.osutils = mock.Mock(spec=OSUtils)\n        self.recorder = ResultsRecorder(self.osutils)\n        self.deployed_values = {\n            'stages': {\n                'dev': {'resources': []},\n            },\n            'schema_version': '2.0',\n        }\n        self.osutils.joinpath = os.path.join\n        self.deployed_dir = os.path.join('.', '.chalice', 'deployed')\n\n    def test_can_record_results_initial_deploy(self):\n        expected_filename = os.path.join(self.deployed_dir, 'dev.json')\n        self.osutils.file_exists.return_value = False\n        self.osutils.directory_exists.return_value = False\n        self.recorder.record_results(\n            self.deployed_values, 'dev', '.',\n        )\n        expected_contents = serialize_to_json(self.deployed_values)\n        # Verify we created the deployed dir on an initial deploy.\n        self.osutils.makedirs.assert_called_with(self.deployed_dir)\n        self.osutils.set_file_contents.assert_called_with(\n            filename=expected_filename,\n            contents=expected_contents,\n            binary=False\n        )\n\n\nclass TestDeploymentReporter(object):\n    def setup_method(self):\n        self.ui = mock.Mock(spec=UI)\n        self.reporter = DeploymentReporter(ui=self.ui)\n\n    def test_can_generate_report(self):\n        certificate_arn = \"arn:aws:acm:us-east-1:account_id:\" \\\n                         \"certificate/e2600f49-f6b7-4105-aaf6-63b2f018a030\"\n        deployed_values = {\n            \"resources\": [\n                {\"role_name\": \"james2-dev\",\n                 \"role_arn\": \"my-role-arn\",\n                 \"name\": \"default-role\",\n                 \"resource_type\": \"iam_role\"},\n                {\"resource_type\": \"lambda_layer\",\n                 \"name\": \"layer\",\n                 \"layer_version_arn\": \"arn:layer:4\"},\n                {\"lambda_arn\": \"lambda-arn-foo\",\n                 \"name\": \"foo\",\n                 \"resource_type\": \"lambda_function\"},\n                {\"lambda_arn\": \"lambda-arn-dev\",\n                 \"name\": \"api_handler\",\n                 \"resource_type\": \"lambda_function\"},\n                {\"name\": \"rest_api\",\n                 \"rest_api_id\": \"rest_api_id\",\n                 \"rest_api_url\": \"https://host/api\",\n                 \"resource_type\": \"rest_api\"},\n                {\"name\": \"websocket_api\",\n                 \"websocket_api_id\": \"websocket_api_id\",\n                 \"websocket_api_url\": \"wss://host/api\",\n                 \"resource_type\": \"websocket_api\"},\n                {\"name\": \"api_gateway_custom_domain\",\n                 \"resource_type\": \"domain_name\",\n                 \"hosted_zone_id\": \"A1FDTDATADATA0\",\n                 \"certificate_arn\": certificate_arn,\n                 \"alias_domain_name\": \"alias.domain.com\",\n                 \"security_policy\": \"TLS_1_0\",\n                 \"domain_name\": \"api.domain\",\n                 \"api_mapping\": [\n                     {\n                         \"key\": \"/test1\"\n                     }\n                 ]}\n            ],\n        }\n        report = self.reporter.generate_report(deployed_values)\n        assert report == (\n            \"Resources deployed:\\n\"\n            \"  - Lambda Layer ARN: arn:layer:4\\n\"\n            \"  - Lambda ARN: lambda-arn-foo\\n\"\n            \"  - Lambda ARN: lambda-arn-dev\\n\"\n            \"  - Rest API URL: https://host/api\\n\"\n            \"  - Websocket API URL: wss://host/api\\n\"\n            \"  - Custom domain name:\\n\"\n            \"      HostedZoneId: A1FDTDATADATA0\\n\"\n            \"      AliasDomainName: alias.domain.com\\n\"\n        )\n\n    def test_can_display_report(self):\n        deployed_values = {\n            'resources': []\n        }\n        self.reporter.display_report(deployed_values)\n        self.ui.write.assert_called_with('Resources deployed:\\n')\n\n\nclass TestLambdaEventSourcePolicyInjector(object):\n    def create_model_from_app(self, app, config):\n        builder = ApplicationGraphBuilder()\n        application = builder.build(config, stage_name='dev')\n        return application.resources[0]\n\n    def test_can_inject_policy(self, sample_sqs_event_app):\n        config = Config.create(chalice_app=sample_sqs_event_app,\n                               autogen_policy=True,\n                               project_dir='.')\n        event_source = self.create_model_from_app(sample_sqs_event_app, config)\n        role = event_source.lambda_function.role\n        role.policy.document = {'Statement': []}\n        injector = LambdaEventSourcePolicyInjector()\n        injector.handle(config, event_source)\n        assert role.policy.document == {\n            'Statement': [SQS_EVENT_SOURCE_POLICY.copy()],\n        }\n\n    def test_no_inject_if_not_autogen_policy(self, sample_sqs_event_app):\n        config = Config.create(chalice_app=sample_sqs_event_app,\n                               autogen_policy=False,\n                               project_dir='.')\n        event_source = self.create_model_from_app(sample_sqs_event_app, config)\n        role = event_source.lambda_function.role\n        role.policy.document = {'Statement': []}\n        injector = LambdaEventSourcePolicyInjector()\n        injector.handle(config, event_source)\n        assert role.policy.document == {'Statement': []}\n\n    def test_no_inject_is_already_injected(self, sample_sqs_event_app):\n        @sample_sqs_event_app.on_sqs_message(queue='second-queue')\n        def second_handler(event):\n            pass\n\n        config = Config.create(chalice_app=sample_sqs_event_app,\n                               autogen_policy=True,\n                               project_dir='.')\n        builder = ApplicationGraphBuilder()\n        application = builder.build(config, stage_name='dev')\n        event_sources = application.resources\n        role = event_sources[1].lambda_function.role\n        role.policy.document = {'Statement': []}\n        injector = LambdaEventSourcePolicyInjector()\n        injector.handle(config, event_sources[0])\n        injector.handle(config, event_sources[1])\n        # Even though we have two queue handlers, we only need to\n        # inject the policy once.\n        assert role.policy.document == {\n            'Statement': [SQS_EVENT_SOURCE_POLICY.copy()],\n        }\n\n    def test_can_inject_policy_for_kinesis(self, sample_kinesis_event_app):\n        config = Config.create(chalice_app=sample_kinesis_event_app,\n                               autogen_policy=True,\n                               project_dir='.')\n        event_source = self.create_model_from_app(sample_kinesis_event_app,\n                                                  config)\n        role = event_source.lambda_function.role\n        role.policy.document = {'Statement': []}\n        injector = LambdaEventSourcePolicyInjector()\n        injector.handle(config, event_source)\n        assert role.policy.document == {\n            'Statement': [KINESIS_EVENT_SOURCE_POLICY],\n        }\n\n    def test_can_inject_policy_for_ddb(self, sample_ddb_event_app):\n        config = Config.create(chalice_app=sample_ddb_event_app,\n                               autogen_policy=True,\n                               project_dir='.')\n        event_source = self.create_model_from_app(sample_ddb_event_app, config)\n        role = event_source.lambda_function.role\n        role.policy.document = {'Statement': []}\n        injector = LambdaEventSourcePolicyInjector()\n        injector.handle(config, event_source)\n        assert role.policy.document == {\n            'Statement': [DDB_EVENT_SOURCE_POLICY],\n        }\n\n\nclass TestWebsocketPolicyInjector(object):\n    def create_model_from_app(self, app, config):\n        builder = ApplicationGraphBuilder()\n        application = builder.build(config, stage_name='dev')\n        return application.resources[0]\n\n    def test_can_inject_policy(self, sample_websocket_app):\n        config = Config.create(chalice_app=sample_websocket_app,\n                               autogen_policy=True,\n                               project_dir='.')\n        event_source = self.create_model_from_app(\n            sample_websocket_app, config)\n        role = event_source.connect_function.role\n        role.policy.document = {'Statement': []}\n        injector = WebsocketPolicyInjector()\n        injector.handle(config, event_source)\n        assert role.policy.document == {\n            'Statement': [POST_TO_WEBSOCKET_CONNECTION_POLICY.copy()],\n        }\n\n    def test_no_inject_if_not_autogen_policy(self, sample_websocket_app):\n        config = Config.create(chalice_app=sample_websocket_app,\n                               autogen_policy=False,\n                               project_dir='.')\n        event_source = self.create_model_from_app(sample_websocket_app, config)\n        role = event_source.connect_function.role\n        role.policy.document = {'Statement': []}\n        injector = LambdaEventSourcePolicyInjector()\n        injector.handle(config, event_source)\n        assert role.policy.document == {'Statement': []}\n"
  },
  {
    "path": "tests/unit/deploy/test_executor.py",
    "content": "import re\nfrom unittest import mock\nimport pytest\n\nfrom chalice.awsclient import TypedAWSClient\nfrom chalice.deploy import models\nfrom chalice.deploy.executor import Executor, UnresolvedValueError, \\\n    VariableResolver, DisplayOnlyExecutor\nfrom chalice.deploy.models import APICall, RecordResourceVariable, \\\n    RecordResourceValue, StoreValue, JPSearch, BuiltinFunction, Instruction, \\\n    CopyVariable\nfrom chalice.deploy.planner import Variable, StringFormat, KeyDataVariable\nfrom chalice.utils import UI\n\n\nclass TestExecutor(object):\n    def setup_method(self):\n        self.mock_client = mock.Mock(spec=TypedAWSClient)\n        self.mock_client.endpoint_dns_suffix.return_value = 'amazonaws.com'\n        self.ui = mock.Mock(spec=UI)\n        self.executor = Executor(self.mock_client, self.ui)\n\n    def execute(self, instructions, messages=None):\n        if messages is None:\n            messages = {}\n        self.executor.execute(models.Plan(instructions, messages))\n\n    def test_can_invoke_api_call_with_no_output(self):\n        params = {'name': 'foo', 'trust_policy': {'trust': 'policy'},\n                  'policy': {'iam': 'policy'}}\n        call = APICall('create_role', params)\n\n        self.execute([call])\n\n        self.mock_client.create_role.assert_called_with(**params)\n\n    def test_can_store_api_result(self):\n        params = {'name': 'foo', 'trust_policy': {'trust': 'policy'},\n                  'policy': {'iam': 'policy'}}\n        apicall = APICall('create_role', params, output_var='my_variable_name')\n        self.mock_client.create_role.return_value = 'myrole:arn'\n\n        self.execute([apicall])\n\n        assert self.executor.variables['my_variable_name'] == 'myrole:arn'\n\n    def test_can_store_multiple_value(self):\n        instruction = models.StoreMultipleValue(\n            name='list_data',\n            value=['first_elem']\n        )\n\n        self.execute([instruction])\n        assert self.executor.variables['list_data'] == ['first_elem']\n\n        instruction = models.StoreMultipleValue(\n            name='list_data',\n            value=['second_elem']\n        )\n\n        self.execute([instruction])\n        assert self.executor.variables['list_data'] == [\n            'first_elem', 'second_elem'\n        ]\n\n    def test_can_reference_stored_results_in_api_calls(self):\n        params = {\n            'name': Variable('role_name'),\n            'trust_policy': {'trust': 'policy'},\n            'policy': {'iam': 'policy'}\n        }\n        call = APICall('create_role', params)\n        self.mock_client.create_role.return_value = 'myrole:arn'\n\n        self.executor.variables['role_name'] = 'myrole-name'\n        self.execute([call])\n\n        self.mock_client.create_role.assert_called_with(\n            name='myrole-name',\n            trust_policy={'trust': 'policy'},\n            policy={'iam': 'policy'},\n        )\n\n    def test_can_return_created_resources(self):\n        params = {}\n        call = APICall('create_function', params,\n                       output_var='myfunction_arn')\n        self.mock_client.create_function.return_value = 'function:arn'\n        record_instruction = RecordResourceVariable(\n            resource_type='lambda_function',\n            resource_name='myfunction',\n            name='myfunction_arn',\n            variable_name='myfunction_arn',\n        )\n        self.execute([call, record_instruction])\n        assert self.executor.resource_values == [{\n            'name': 'myfunction',\n            'myfunction_arn': 'function:arn',\n            'resource_type': 'lambda_function',\n        }]\n\n    def test_can_reference_varname(self):\n        self.mock_client.create_function.return_value = 'function:arn'\n        self.execute([\n            APICall('create_function', {}, output_var='myvarname'),\n            RecordResourceVariable(\n                resource_type='lambda_function',\n                resource_name='myfunction',\n                name='myfunction_arn',\n                variable_name='myvarname',\n            ),\n        ])\n        assert self.executor.resource_values == [{\n            'name': 'myfunction',\n            'resource_type': 'lambda_function',\n            'myfunction_arn': 'function:arn',\n        }]\n\n    def test_can_record_value_directly(self):\n        self.execute([\n            RecordResourceValue(\n                resource_type='lambda_function',\n                resource_name='myfunction',\n                name='myfunction_arn',\n                value='arn:foo',\n            )\n        ])\n        assert self.executor.resource_values == [{\n            'name': 'myfunction',\n            'resource_type': 'lambda_function',\n            'myfunction_arn': 'arn:foo',\n        }]\n\n    def test_can_aggregate_multiple_resource_values(self):\n        self.execute([\n            RecordResourceValue(\n                resource_type='lambda_function',\n                resource_name='myfunction',\n                name='key1',\n                value='value1',\n            ),\n            RecordResourceValue(\n                resource_type='lambda_function',\n                resource_name='myfunction',\n                name='key2',\n                value='value2',\n            )\n        ])\n        assert self.executor.resource_values == [{\n            'name': 'myfunction',\n            'resource_type': 'lambda_function',\n            'key1': 'value1',\n            'key2': 'value2',\n        }]\n\n    def test_new_keys_override_old_keys(self):\n        self.execute([\n            RecordResourceValue(\n                resource_type='lambda_function',\n                resource_name='myfunction',\n                name='key1',\n                value='OLD',\n            ),\n            RecordResourceValue(\n                resource_type='lambda_function',\n                resource_name='myfunction',\n                name='key1',\n                value='NEW',\n            )\n        ])\n        assert self.executor.resource_values == [{\n            'name': 'myfunction',\n            'resource_type': 'lambda_function',\n            'key1': 'NEW',\n        }]\n\n    def test_validates_no_unresolved_deploy_vars(self):\n        params = {'zip_contents': models.Placeholder.BUILD_STAGE}\n        call = APICall('create_function', params)\n        self.mock_client.create_function.return_value = 'function:arn'\n        # We should raise an exception because a param has\n        # a models.Placeholder.BUILD_STAGE value which should have\n        # been handled in an earlier stage.\n        with pytest.raises(UnresolvedValueError):\n            self.execute([call])\n\n    def test_can_jp_search(self):\n        self.execute([\n            StoreValue(name='searchval', value={'foo': {'bar': 'baz'}}),\n            JPSearch('foo.bar', input_var='searchval', output_var='result'),\n        ])\n        assert self.executor.variables['result'] == 'baz'\n\n    def test_can_copy_variable(self):\n        self.execute([\n            StoreValue(name='foo', value='bar'),\n            CopyVariable(from_var='foo', to_var='baz'),\n        ])\n        assert self.executor.variables['baz'] == 'bar'\n\n    def test_can_call_builtin_function(self):\n        self.execute([\n            StoreValue(\n                name='my_arn',\n                value='arn:aws:lambda:us-west-2:123:function:name'),\n            BuiltinFunction(\n                function_name='parse_arn',\n                args=[Variable('my_arn')],\n                output_var='result',\n            )\n        ])\n        assert self.executor.variables['result'] == {\n            'partition': 'aws',\n            'account_id': '123',\n            'region': 'us-west-2',\n            'service': 'lambda',\n            'dns_suffix': 'amazonaws.com'\n        }\n\n    def test_built_in_function_interrogate_profile(self):\n        self.mock_client.region_name = 'us-west-2'\n        self.mock_client.partition_name = 'aws'\n        self.execute([\n            BuiltinFunction(\n                function_name='interrogate_profile',\n                args=[],\n                output_var='result',\n            )\n        ])\n        assert self.executor.variables['result'] == {\n            'partition': 'aws',\n            'region': 'us-west-2',\n            'dns_suffix': 'amazonaws.com'\n        }\n\n    def test_built_in_function_service_principal(self):\n        self.mock_client.region_name = 'us-west-2'\n        self.mock_client.partition_name = 'aws'\n        self.mock_client.service_principal.return_value = \\\n            'apigateway.amazonaws.com'\n        self.execute([\n            BuiltinFunction(\n                function_name='service_principal',\n                args=['apigateway'],\n                output_var='result',\n            )\n        ])\n\n        self.mock_client.service_principal \\\n            .assert_called_once_with('apigateway',\n                                     'us-west-2',\n                                     'amazonaws.com')\n        assert self.executor.variables['result'] == {\n            'principal': 'apigateway.amazonaws.com'\n        }\n\n    def test_errors_out_on_unknown_function(self):\n        with pytest.raises(ValueError):\n            self.execute([\n                BuiltinFunction(\n                    function_name='unknown_foo',\n                    args=[],\n                    output_var=None,\n                )\n            ])\n\n    def test_can_print_ui_messages(self):\n        params = {'name': 'foo', 'trust_policy': {'trust': 'policy'},\n                  'policy': {'iam': 'policy'}}\n        call = APICall('create_role', params)\n        messages = {id(call): 'Creating role'}\n        self.execute([call], messages)\n        self.mock_client.create_role.assert_called_with(**params)\n        self.ui.write.assert_called_with('Creating role')\n\n    def test_error_out_on_unknown_instruction(self):\n\n        class CustomInstruction(Instruction):\n            pass\n\n        with pytest.raises(RuntimeError):\n            self.execute([CustomInstruction()])\n\n\nclass TestDisplayOnlyExecutor(object):\n\n    # Note: This executor doesn't have any guarantees on its output,\n    # it's primarily to help debug/understand chalice.  The tests here\n    # check the basic structure of the output, but try to not be overly strict.\n\n    def setup_method(self):\n        self.mock_client = mock.Mock(spec=TypedAWSClient)\n        self.ui = mock.Mock(spec=UI)\n        self.executor = DisplayOnlyExecutor(self.mock_client, self.ui)\n\n    def execute(self, instructions, messages=None):\n        if messages is None:\n            messages = {}\n        self.executor.execute(models.Plan(instructions, messages))\n\n    def get_plan_output(self, instructions):\n        self.executor.execute(models.Plan(instructions, {}))\n        return ''.join(args[0][0] for args in self.ui.write.call_args_list)\n\n    def test_can_display_plan(self):\n        params = {'name': 'foo', 'trust_policy': {'trust': 'policy'},\n                  'policy': {'iam': 'policy'}}\n        call = APICall('create_role', params)\n\n        plan_output = self.get_plan_output([call])\n        # Should have a plan title.\n        assert plan_output.startswith('Plan\\n====')\n        # Should print the api call in upper camel case.\n        assert 'API_CALL' in plan_output\n        # Should print the name of the method in the plan.\n        assert 'method_name: create_role' in plan_output\n        # Should print out the api call arguments in output.\n        assert 'name: foo' in plan_output\n        # The values for these are in the tests for the variable pool.\n        assert 'trust_policy: ' in plan_output\n        assert 'policy: ' in plan_output\n\n    def test_variable_pool_printed_if_needed(self):\n        params = {'name': 'foo', 'policy': {'iam': 'policy'}}\n        call = APICall('create_role', params)\n\n        plan_output = self.get_plan_output([call])\n        # Dictionaries for param values are printed at the end so they\n        # don't clutter the plan output.  We should see a placeholder here.\n        assert 'policy: ${POLICY_0}' in plan_output\n        assert 'Variable Pool' in plan_output\n        assert \"${POLICY_0}:\\n{'iam': 'policy'}\" in plan_output\n\n    def test_variable_pool_omitted_if_empty(self):\n        params = {'name': 'foo'}\n        call = APICall('create_role', params)\n\n        plan_output = self.get_plan_output([call])\n        assert 'Variable Pool' not in plan_output\n\n    def test_byte_value_replaced_if_over_length(self):\n        params = {'name': 'foo', 'zip_contents': b'\\x01' * 50}\n        call = APICall('create_role', params)\n\n        plan_output = self.get_plan_output([call])\n        assert 'zip_contents: <bytes>' in plan_output\n\n    def test_can_print_multiple_instructions(self):\n        instructions = [\n            JPSearch(expression='foo.bar', input_var='in1', output_var='out1'),\n            JPSearch(expression='foo.baz', input_var='in2', output_var='out2'),\n        ]\n        plan_output = self.get_plan_output(instructions)\n        # Use a regex to ensure they're printed in order.\n        assert re.search(\n            'JP_SEARCH.*expression: foo.bar.*'\n            'JP_SEARCH.*expression: foo.baz', plan_output,\n            re.MULTILINE | re.DOTALL\n        ) is not None\n\n    def test_empty_values_omitted(self):\n        params = {'name': 'foo', 'empty_list': [],\n                  'empty_dict': {}, 'empty_str': ''}\n        call = APICall('create_role', params)\n\n        plan_output = self.get_plan_output([call])\n        assert 'empty_list' not in plan_output\n        assert 'empty_dict' not in plan_output\n        assert 'empty_str' not in plan_output\n\n\nclass TestResolveVariables(object):\n\n    def resolve_vars(self, params, variables):\n        return VariableResolver().resolve_variables(\n            params, variables\n        )\n\n    def test_resolve_top_level_vars(self):\n        assert self.resolve_vars(\n            {'foo': Variable('myvar')},\n            {'myvar': 'value'}\n        ) == {'foo': 'value'}\n\n    def test_can_resolve_multiple_vars(self):\n        assert self.resolve_vars(\n            {'foo': Variable('myvar'),\n             'bar': Variable('myvar')},\n            {'myvar': 'value'}\n        ) == {'foo': 'value', 'bar': 'value'}\n\n    def test_unsolved_error_raises_error(self):\n        with pytest.raises(UnresolvedValueError) as excinfo:\n            self.resolve_vars({'foo': models.Placeholder.BUILD_STAGE}, {})\n        raised_exception = excinfo.value\n        assert raised_exception.key == 'foo'\n        assert raised_exception.value == models.Placeholder.BUILD_STAGE\n\n    def test_can_resolve_nested_variable_refs(self):\n        assert self.resolve_vars(\n            {'foo': {'bar': Variable('myvar')}},\n            {'myvar': 'value'}\n        ) == {'foo': {'bar': 'value'}}\n\n    def test_can_resolve_vars_in_list(self):\n        assert self.resolve_vars(\n            {'foo': [0, 1, Variable('myvar')]},\n            {'myvar': 2}\n        ) == {'foo': [0, 1, 2]}\n\n    def test_deeply_nested(self):\n        nested = {\n            'a': {\n                'b': {\n                    'c': {\n                        'd': [{'e': {'f': Variable('foo')}}],\n                    }\n                }\n            }\n        }\n        variables = {'foo': 'value'}\n        assert self.resolve_vars(nested, variables) == {\n            'a': {\n                'b': {\n                    'c': {\n                        'd': [{'e': {'f': 'value'}}],\n                    }\n                }\n            }\n        }\n\n    def test_can_handle_format_string(self):\n        params = {'bar': StringFormat('value: {my_var}', ['my_var'])}\n        variables = {'my_var': 'foo'}\n        assert self.resolve_vars(params, variables) == {\n            'bar': 'value: foo',\n        }\n\n    def test_can_handle_deeply_nested_format_string(self):\n        nested = {\n            'a': {\n                'b': {\n                    'c': {\n                        'd': [{'e': {'f': StringFormat(\n                            'foo: {myvar}', ['myvar'])}}],\n                    }\n                }\n            }\n        }\n        variables = {'myvar': 'value'}\n        assert self.resolve_vars(nested, variables) == {\n            'a': {\n                'b': {\n                    'c': {\n                        'd': [{'e': {'f': 'foo: value'}}],\n                    }\n                }\n            }\n        }\n\n    def test_can_handle_dict_value_by_key(self):\n        variables = {\n            'domain_name': {\n                'base_path_mapping': {\n                    'path': '/'\n                }\n            }\n        }\n        assert self.resolve_vars(\n            KeyDataVariable('domain_name', 'base_path_mapping'), variables\n        ) == {'path': '/'}\n"
  },
  {
    "path": "tests/unit/deploy/test_models.py",
    "content": "from dataclasses import replace\n\nfrom chalice.deploy import models\n\n\ndef test_can_instantiate_empty_application():\n    app = models.Application(stage='dev', resources=[])\n    assert app.dependencies() == []\n\n\ndef test_can_instantiate_app_with_deps():\n    role = models.PreCreatedIAMRole(role_arn='foo')\n    app = models.Application(stage='dev', resources=[role])\n    assert app.dependencies() == [role]\n\n\ndef test_can_default_to_no_auths_in_rest_api(lambda_function):\n    rest_api = models.RestAPI(\n        resource_name='rest_api',\n        swagger_doc={'swagger': '2.0'},\n        minimum_compression='',\n        api_gateway_stage='api',\n        endpoint_type='EDGE',\n        lambda_function=lambda_function,\n        xray=False\n    )\n    assert rest_api.dependencies() == [lambda_function]\n\n\ndef test_can_add_authorizers_to_dependencies(lambda_function):\n    auth1 = replace(lambda_function, resource_name='auth1')\n    auth2 = replace(lambda_function, resource_name='auth2')\n    rest_api = models.RestAPI(\n        resource_name='rest_api',\n        swagger_doc={'swagger': '2.0'},\n        minimum_compression='',\n        api_gateway_stage='api',\n        endpoint_type='EDGE',\n        lambda_function=lambda_function,\n        xray=False,\n        authorizers=[auth1, auth2],\n    )\n    assert rest_api.dependencies() == [lambda_function, auth1, auth2]\n\n\ndef test_can_add_connect_to_dependencies(lambda_function):\n    api = models.WebsocketAPI(\n        resource_name='websocket_api',\n        name='name',\n        api_gateway_stage='api',\n        routes=['$connect'],\n        connect_function=lambda_function,\n        message_function=None,\n        disconnect_function=None,\n    )\n    assert api.dependencies() == [lambda_function]\n\n\ndef test_can_add_message_to_dependencies(lambda_function):\n    api = models.WebsocketAPI(\n        resource_name='websocket_api',\n        name='name',\n        api_gateway_stage='api',\n        routes=['$default'],\n        connect_function=None,\n        message_function=lambda_function,\n        disconnect_function=None,\n    )\n    assert api.dependencies() == [lambda_function]\n\n\ndef test_can_add_disconnect_to_dependencies(lambda_function):\n    api = models.WebsocketAPI(\n        resource_name='websocket_api',\n        name='name',\n        api_gateway_stage='api',\n        routes=['$disconnect'],\n        connect_function=None,\n        message_function=None,\n        disconnect_function=lambda_function,\n    )\n    assert api.dependencies() == [lambda_function]\n"
  },
  {
    "path": "tests/unit/deploy/test_packager.py",
    "content": "import pytest\nfrom collections import namedtuple\n\nfrom chalice.utils import OSUtils\nfrom chalice.compat import pip_no_compile_c_env_vars\nfrom chalice.compat import pip_no_compile_c_shim\nfrom chalice.deploy.packager import Package\nfrom chalice.deploy.packager import PipRunner\nfrom chalice.deploy.packager import SubprocessPip\nfrom chalice.deploy.packager import InvalidSourceDistributionNameError\nfrom chalice.deploy.packager import NoSuchPackageError\nfrom chalice.deploy.packager import PackageDownloadError\n\n\nFakePipCall = namedtuple('FakePipEntry', ['args', 'env_vars', 'shim'])\n\n\nclass FakePip(object):\n    def __init__(self):\n        self._calls = []\n        self._returns = []\n\n    def main(self, args, env_vars=None, shim=None):\n        self._calls.append(FakePipCall(args, env_vars, shim))\n        if self._returns:\n            return self._returns.pop(0)\n        # Return an rc of 0 and an empty stderr and stdout\n        return 0, b'', b''\n\n    def add_return(self, return_pair):\n        self._returns.append(return_pair)\n\n    @property\n    def calls(self):\n        return self._calls\n\n\n@pytest.fixture\ndef pip_factory():\n    def create_pip_runner(osutils=None):\n        pip = FakePip()\n        pip_runner = PipRunner(pip, osutils=osutils)\n        return pip, pip_runner\n    return create_pip_runner\n\n\nclass CustomEnv(OSUtils):\n    def __init__(self, env):\n        self._env = env\n\n    def environ(self):\n        return self._env\n\n\n@pytest.fixture\ndef osutils():\n    return OSUtils()\n\n\nclass FakePopen(object):\n    def __init__(self, rc, out, err):\n        self.returncode = 0\n        self._out = out\n        self._err = err\n\n    def communicate(self):\n        return self._out, self._err\n\n\nclass FakePopenOSUtils(OSUtils):\n    def __init__(self, processes):\n        self.popens = []\n        self._processes = processes\n\n    def popen(self, *args, **kwargs):\n        self.popens.append((args, kwargs))\n        return self._processes.pop()\n\n\nclass TestPackage(object):\n    def test_can_create_package_with_custom_osutils(self, osutils):\n        pkg = Package('', 'foobar-1.0-py3-none-any.whl', osutils)\n        assert pkg._osutils == osutils\n\n    def test_wheel_package(self):\n        filename = 'foobar-1.0-py3-none-any.whl'\n        pkg = Package('', filename)\n        assert pkg.dist_type == 'wheel'\n        assert pkg.filename == filename\n        assert pkg.identifier == 'foobar==1.0'\n        assert str(pkg) == 'foobar==1.0(wheel)'\n\n    def test_invalid_package(self):\n        with pytest.raises(InvalidSourceDistributionNameError):\n            Package('', 'foobar.jpg')\n\n    def test_diff_pkg_sdist_and_whl_do_not_collide(self):\n        pkgs = set()\n        pkgs.add(Package('', 'foobar-1.0-py3-none-any.whl'))\n        pkgs.add(Package('', 'badbaz-1.0-py3-none-any.whl'))\n        assert len(pkgs) == 2\n\n    def test_same_pkg_is_eq(self):\n        pkg = Package('', 'foobar-1.0-py3-none-any.whl')\n        assert pkg == pkg\n\n    def test_pkg_is_eq_to_similar_pkg(self):\n        pure_pkg = Package('', 'foobar-1.0-py3-none-any.whl')\n        plat_pkg = Package('', 'foobar-1.0-py3-py36m-manylinux1_x86_64.whl')\n        assert pure_pkg == plat_pkg\n\n    def test_pkg_is_not_equal_to_different_type(self):\n        pkg = Package('', 'foobar-1.0-py3-none-any.whl')\n        non_package_type = 1\n        assert not (pkg == non_package_type)\n\n    def test_pkg_repr(self):\n        pkg = Package('', 'foobar-1.0-py3-none-any.whl')\n        assert repr(pkg) == 'foobar==1.0(wheel)'\n\n    def test_wheel_data_dir(self):\n        pkg = Package('', 'foobar-2.0-py3-none-any.whl')\n        assert pkg.data_dir == 'foobar-2.0.data'\n\n    def test_can_read_packages_with_underscore_in_name(self):\n        pkg = Package('', 'foo_bar-2.0-py3-none-any.whl')\n        assert pkg.identifier == 'foo-bar==2.0'\n\n    def test_can_read_packages_with_period_in_name(self):\n        pkg = Package('', 'foo.bar-2.0-py3-none-any.whl')\n        assert pkg.identifier == 'foo-bar==2.0'\n\n    def test_can_normalize_data_dir(self):\n        pkg = Package('', 'Foobar-2.0-py3-none-any.whl')\n        assert pkg.data_dir == 'foobar-2.0.data'\n\n    def test_can_normalize_dirname_comparisons(self):\n        pkg = Package('', 'Foobar-2.0-py3-none-any.whl')\n        assert pkg.matches_data_dir('Foobar-2.0.data')\n        assert pkg.matches_data_dir('foobar-2.0.data')\n        assert not pkg.matches_data_dir('other-2.0.data')\n        assert not pkg.matches_data_dir('foobar-2.0.datastuff')\n        assert not pkg.matches_data_dir('foobar-2.0')\n\n\nclass TestPipRunner(object):\n    def test_does_propagate_env_vars(self, pip_factory):\n        osutils = CustomEnv({'foo': 'bar'})\n        pip, runner = pip_factory(osutils)\n        wheel = 'foobar-1.2-py3-none-any.whl'\n        directory = 'directory'\n        runner.build_wheel(wheel, directory)\n        call = pip.calls[0]\n\n        assert 'foo' in call.env_vars\n        assert call.env_vars['foo'] == 'bar'\n\n    def test_build_wheel(self, pip_factory):\n        # Test that `pip wheel` is called with the correct params\n        pip, runner = pip_factory()\n        wheel = 'foobar-1.0-py3-none-any.whl'\n        directory = 'directory'\n        runner.build_wheel(wheel, directory)\n\n        assert len(pip.calls) == 1\n        call = pip.calls[0]\n        assert call.args == ['wheel', '--no-deps', '--wheel-dir',\n                             directory, wheel]\n        for compile_env_var in pip_no_compile_c_env_vars:\n            assert compile_env_var not in call.env_vars\n        assert call.shim == ''\n\n    def test_build_wheel_without_c_extensions(self, pip_factory):\n        # Test that `pip wheel` is called with the correct params when we\n        # call it with compile_c=False. These will differ by platform.\n        pip, runner = pip_factory()\n        wheel = 'foobar-1.0-py3-none-any.whl'\n        directory = 'directory'\n        runner.build_wheel(wheel, directory, compile_c=False)\n\n        assert len(pip.calls) == 1\n        call = pip.calls[0]\n        assert call.args == ['wheel', '--no-deps', '--wheel-dir',\n                             directory, wheel]\n        for compile_env_var in pip_no_compile_c_env_vars:\n            assert compile_env_var in call.env_vars\n        assert call.shim == pip_no_compile_c_shim\n\n    def test_download_all_deps(self, pip_factory):\n        # Make sure that `pip download` is called with the correct arguments\n        # for getting all sdists.\n        pip, runner = pip_factory()\n        runner.download_all_dependencies('requirements.txt', 'directory')\n\n        assert len(pip.calls) == 1\n        call = pip.calls[0]\n        assert call.args == ['download', '-r',\n                             'requirements.txt', '--dest', 'directory']\n        assert call.env_vars is None\n        assert call.shim is None\n\n    def test_download_sdist(self, pip_factory):\n        pip, runner = pip_factory()\n        packages = ['foo', 'bar', 'baz']\n        runner.download_sdists(packages, 'directory')\n        expected_prefix = ['download', '--no-binary=:all:', '--no-deps',\n                           '--dest', 'directory']\n        for i, package in enumerate(packages):\n            assert pip.calls[i].args == expected_prefix + [package]\n            assert pip.calls[i].env_vars is None\n            assert pip.calls[i].shim is None\n\n    def test_download_wheels(self, pip_factory):\n        # Make sure that `pip download` is called with the correct arguments\n        # for getting lambda compatible wheels.\n        pip, runner = pip_factory()\n        packages = ['foo', 'bar', 'baz']\n        abi = 'cp37m'\n        runner.download_manylinux_wheels(abi, packages, 'directory')\n        expected_prefix = ['download', '--only-binary=:all:', '--no-deps',\n                           '--platform', 'manylinux2014_x86_64',\n                           '--implementation', 'cp', '--abi', abi,\n                           '--dest', 'directory']\n        for i, package in enumerate(packages):\n            assert pip.calls[i].args == expected_prefix + [package]\n            assert pip.calls[i].env_vars is None\n            assert pip.calls[i].shim is None\n\n    def test_download_wheels_no_wheels(self, pip_factory):\n        pip, runner = pip_factory()\n        runner.download_manylinux_wheels('cp36m', [], 'directory')\n        assert len(pip.calls) == 0\n\n    def test_does_find_local_directory(self, pip_factory):\n        pip, runner = pip_factory()\n        pip.add_return((0,\n                        (b\"Processing ../local-dir\\n\"\n                         b\"  Link is a directory,\"\n                         b\" ignoring download_dir\"),\n                        b''))\n        runner.download_all_dependencies('requirements.txt', 'directory')\n        assert len(pip.calls) == 2\n        assert pip.calls[1].args == ['wheel', '--no-deps', '--wheel-dir',\n                                     'directory', '../local-dir']\n\n    def test_does_find_multiple_local_directories(self, pip_factory):\n        pip, runner = pip_factory()\n        pip.add_return((0,\n                        (b\"Processing ../local-dir-1\\n\"\n                         b\"  Link is a directory,\"\n                         b\" ignoring download_dir\"\n                         b\"\\nsome pip output...\\n\"\n                         b\"Processing ../local-dir-2\\n\"\n                         b\"  Link is a directory,\"\n                         b\" ignoring download_dir\"),\n                        b''))\n        runner.download_all_dependencies('requirements.txt', 'directory')\n        assert len(pip.calls) == 3\n        assert pip.calls[1].args == ['wheel', '--no-deps', '--wheel-dir',\n                                     'directory', '../local-dir-1']\n        assert pip.calls[2].args == ['wheel', '--no-deps', '--wheel-dir',\n                                     'directory', '../local-dir-2']\n\n    def test_raise_no_such_package_error(self, pip_factory):\n        pip, runner = pip_factory()\n        pip.add_return((1, b'',\n                        (b'Could not find a version that satisfies the '\n                         b'requirement BadPackageName ')))\n        with pytest.raises(NoSuchPackageError) as einfo:\n            runner.download_all_dependencies('requirements.txt', 'directory')\n        assert str(einfo.value) == ('Could not satisfy the requirement: '\n                                    'BadPackageName')\n\n    def test_raise_other_unknown_error_during_downloads(self, pip_factory):\n        pip, runner = pip_factory()\n        pip.add_return((1, b'', b'SomeNetworkingError: Details here.'))\n        with pytest.raises(PackageDownloadError) as einfo:\n            runner.download_all_dependencies('requirements.txt', 'directory')\n        assert str(einfo.value) == 'SomeNetworkingError: Details here.'\n\n    def test_inject_unknown_error_if_no_stderr(self, pip_factory):\n        pip, runner = pip_factory()\n        pip.add_return((1, None, None))\n        with pytest.raises(PackageDownloadError) as einfo:\n            runner.download_all_dependencies('requirements.txt', 'directory')\n        assert str(einfo.value) == 'Unknown error'\n\n\nclass TestSubprocessPip(object):\n    def test_does_use_custom_pip_import_string(self):\n        fake_osutils = FakePopenOSUtils([FakePopen(0, '', '')])\n        expected_import_statement = 'foobarbaz'\n        pip = SubprocessPip(osutils=fake_osutils,\n                            import_string=expected_import_statement)\n        pip.main(['--version'])\n\n        pip_execution_string = fake_osutils.popens[0][0][0][2]\n        import_statement = pip_execution_string.split(';')[1].strip()\n        assert import_statement == expected_import_statement\n"
  },
  {
    "path": "tests/unit/deploy/test_planner.py",
    "content": "from unittest import mock\nfrom dataclasses import replace, dataclass\nfrom typing import Tuple  # noqa\n\nimport pytest\n\nfrom chalice.awsclient import TypedAWSClient, ResourceDoesNotExistError\nfrom chalice.deploy import models\nfrom chalice.config import DeployedResources\nfrom chalice.utils import OSUtils\nfrom chalice.deploy.planner import PlanStage, Variable, RemoteState, \\\n    KeyDataVariable\nfrom chalice.deploy.planner import StringFormat\nfrom chalice.deploy.models import APICall\nfrom chalice.deploy.sweeper import ResourceSweeper\n\n\ndef create_function_resource(name, function_name=None,\n                             environment_variables=None,\n                             runtime='python2.7', handler='app.app',\n                             tags=None, timeout=60,\n                             memory_size=128, deployment_package=None,\n                             role=None, layers=None, managed_layer=None):\n    if function_name is None:\n        function_name = 'appname-dev-%s' % name\n    if environment_variables is None:\n        environment_variables = {}\n    if tags is None:\n        tags = {}\n    if deployment_package is None:\n        deployment_package = models.DeploymentPackage(filename='foo')\n    if role is None:\n        role = models.PreCreatedIAMRole(role_arn='role:arn')\n    return models.LambdaFunction(\n        resource_name=name,\n        function_name=function_name,\n        environment_variables=environment_variables,\n        runtime=runtime,\n        handler=handler,\n        tags=tags,\n        timeout=timeout,\n        memory_size=memory_size,\n        xray=None,\n        deployment_package=deployment_package,\n        role=role,\n        security_group_ids=[],\n        subnet_ids=[],\n        layers=layers,\n        reserved_concurrency=None,\n        managed_layer=managed_layer,\n    )\n\n\ndef create_managed_layer():\n    layer = models.LambdaLayer(\n        resource_name='layer',\n        layer_name='bar',\n        runtime='python2.7',\n        deployment_package=models.DeploymentPackage(\n            filename='foo')\n    )\n    return layer\n\n\ndef create_api_mapping():\n    return models.APIMapping(\n        resource_name='api_mapping',\n        mount_path='(none)',\n        api_gateway_stage='dev'\n    )\n\n\ndef create_http_domain_name():\n    return models.DomainName(\n        protocol=models.APIType.HTTP,\n        resource_name='api_gateway_custom_domain',\n        domain_name='example.com',\n        tls_version=models.TLSVersion.TLS_1_0,\n        api_mapping=create_api_mapping(),\n        certificate_arn='certificate_arn',\n    )\n\n\ndef create_websocket_domain_name():\n    return models.DomainName(\n        protocol=models.APIType.WEBSOCKET,\n        resource_name='websocket_api_custom_domain',\n        domain_name='example.com',\n        tls_version=models.TLSVersion.TLS_1_0,\n        api_mapping=create_api_mapping(),\n        certificate_arn='certificate_arn',\n    )\n\n\n@pytest.fixture\ndef no_deployed_values():\n    return DeployedResources({'resources': [], 'schema_version': '2.0'})\n\n\nclass FakeConfig(object):\n    def __init__(self, deployed_values):\n        self._deployed_values = deployed_values\n        self.chalice_stage = 'dev'\n        self.api_gateway_stage = 'dev'\n\n    def deployed_resources(self, chalice_stage_name):\n        return DeployedResources(self._deployed_values)\n\n\nclass InMemoryRemoteState(object):\n    def __init__(self, known_resources=None):\n        if known_resources is None:\n            known_resources = {}\n        self.known_resources = known_resources\n        self.deployed_values = {}\n\n    def resource_exists(self, resource, *args):\n        if resource.resource_type == 'api_mapping':\n            return (\n                (resource.resource_type, resource.mount_path)\n                in self.known_resources\n            )\n        return (\n            (resource.resource_type, resource.resource_name)\n            in self.known_resources\n        )\n\n    def get_remote_model(self, resource):\n        key = (resource.resource_type, resource.resource_name)\n        return self.known_resources.get(key)\n\n    def declare_resource_exists(self, resource, **deployed_values):\n        key = (resource.resource_type, resource.resource_name)\n        self.known_resources[key] = resource\n        if deployed_values:\n            deployed_values['name'] = resource.resource_name\n            self.deployed_values[resource.resource_name] = deployed_values\n            if resource.resource_type == 'domain_name':\n                key = (resource.api_mapping.resource_type,\n                       resource.api_mapping.mount_path)\n                self.known_resources[key] = resource\n\n    def declare_no_resources_exists(self):\n        self.known_resources = {}\n\n    def resource_deployed_values(self, resource):\n        return self.deployed_values[resource.resource_name]\n\n\nclass BasePlannerTests(object):\n    def setup_method(self):\n        self.osutils = mock.Mock(spec=OSUtils)\n        self.remote_state = InMemoryRemoteState()\n        self.last_plan = None\n\n    def assert_apicall_equals(self, expected, actual_api_call):\n        # models.APICall has its own __eq__ method from attrs,\n        # but in practice the assertion errors are unreadable and\n        # it's not always clear which part of the API call object is\n        # wrong.  To get better error messages each field is individually\n        # compared.\n        assert isinstance(expected, models.APICall)\n        assert isinstance(actual_api_call, models.APICall)\n        assert expected.method_name == actual_api_call.method_name\n        assert expected.params == actual_api_call.params\n\n    def determine_plan(self, resource):\n        planner = PlanStage(self.remote_state, self.osutils)\n        self.last_plan = planner.execute([resource])\n        return self.last_plan.instructions\n\n    def filter_api_calls(self, plan):\n        api_calls = []\n        for instruction in plan:\n            if isinstance(instruction, models.APICall):\n                api_calls.append(instruction)\n        return api_calls\n\n    def assert_recorded_values(self, plan, resource_type, resource_name,\n                               expected_mapping):\n        actual = {}\n        for step in plan:\n            if isinstance(step, models.RecordResourceValue):\n                actual[step.name] = step.value\n            elif isinstance(step, models.RecordResourceVariable):\n                actual[step.name] = Variable(step.variable_name)\n        assert actual == expected_mapping\n\n\nclass TestPlanManagedRole(BasePlannerTests):\n    def test_can_plan_for_iam_role_creation(self):\n        self.remote_state.declare_no_resources_exists()\n        resource = models.ManagedIAMRole(\n            resource_name='default-role',\n            role_name='myrole',\n            trust_policy={'trust': 'policy'},\n            policy=models.AutoGenIAMPolicy(document={'iam': 'policy'}),\n        )\n        plan = self.determine_plan(resource)\n        expected = models.APICall(\n            method_name='create_role',\n            params={'name': 'myrole',\n                    'trust_policy': Variable('lambda_trust_policy'),\n                    'policy': {'iam': 'policy'}},\n        )\n        self.assert_apicall_equals(plan[4], expected)\n        assert list(self.last_plan.messages.values()) == [\n            'Creating IAM role: myrole\\n'\n        ]\n\n    def test_can_create_plan_for_filebased_role(self):\n        self.remote_state.declare_no_resources_exists()\n        resource = models.ManagedIAMRole(\n            resource_name='default-role',\n            role_name='myrole',\n            trust_policy={'trust': 'policy'},\n            policy=models.FileBasedIAMPolicy(\n                filename='foo.json', document={'iam': 'policy'}),\n        )\n        plan = self.determine_plan(resource)\n        expected = models.APICall(\n            method_name='create_role',\n            params={'name': 'myrole',\n                    'trust_policy': Variable('lambda_trust_policy'),\n                    'policy': {'iam': 'policy'}},\n        )\n        self.assert_apicall_equals(plan[4], expected)\n        assert list(self.last_plan.messages.values()) == [\n            'Creating IAM role: myrole\\n'\n        ]\n\n    def test_can_update_managed_role(self):\n        role = models.ManagedIAMRole(\n            resource_name='resource_name',\n            role_name='myrole',\n            trust_policy={},\n            policy=models.AutoGenIAMPolicy(document={'role': 'policy'}),\n        )\n        self.remote_state.declare_resource_exists(\n            role, role_arn='myrole:arn')\n        plan = self.determine_plan(role)\n        assert plan[0] == models.StoreValue(\n            name='myrole_role_arn', value='myrole:arn')\n        self.assert_apicall_equals(\n            plan[1],\n            models.APICall(\n                method_name='put_role_policy',\n                params={'role_name': 'myrole',\n                        'policy_name': 'myrole',\n                        'policy_document': {'role': 'policy'}},\n            )\n        )\n        assert plan[-2].variable_name == 'myrole_role_arn'\n        assert plan[-1].value == 'myrole'\n        assert list(self.last_plan.messages.values()) == [\n            'Updating policy for IAM role: myrole\\n'\n        ]\n\n    def test_can_update_file_based_policy(self):\n        role = models.ManagedIAMRole(\n            resource_name='resource_name',\n            role_name='myrole',\n            trust_policy={},\n            policy=models.FileBasedIAMPolicy(\n                filename='foo.json',\n                document={'iam': 'policy'}),\n        )\n        self.remote_state.declare_resource_exists(role, role_arn='myrole:arn')\n        plan = self.determine_plan(role)\n        assert plan[0] == models.StoreValue(\n            name='myrole_role_arn', value='myrole:arn')\n        self.assert_apicall_equals(\n            plan[1],\n            models.APICall(\n                method_name='put_role_policy',\n                params={'role_name': 'myrole',\n                        'policy_name': 'myrole',\n                        'policy_document': {'iam': 'policy'}},\n            )\n        )\n\n    def test_no_update_for_non_managed_role(self):\n        role = models.PreCreatedIAMRole(role_arn='role:arn')\n        plan = self.determine_plan(role)\n        assert plan == []\n\n\nclass TestPlanCreateUpdateAPIMapping(BasePlannerTests):\n    def test_can_create_api_mapping(self, lambda_function):\n        rest_api = models.RestAPI(\n            resource_name='rest_api',\n            swagger_doc={'swagger': '2.0'},\n            minimum_compression='',\n            api_gateway_stage='api',\n            endpoint_type='EDGE',\n            lambda_function=lambda_function,\n            domain_name=create_http_domain_name()\n        )\n\n        self.remote_state.declare_no_resources_exists()\n        plan = self.determine_plan(rest_api)\n        params = {\n            'domain_name': rest_api.domain_name.domain_name,\n            'path_key': '(none)',\n            'stage': 'dev',\n            'api_id': Variable('rest_api_id')\n        }\n        expected = [\n            models.APICall(\n                method_name='create_base_path_mapping',\n                params=params,\n                output_var='base_path_mapping'\n            ),\n        ]\n        # Create api mapping.\n        self.assert_apicall_equals(plan[-3], expected[0])\n        msg = 'Creating api mapping: /\\n'\n        assert list(self.last_plan.messages.values())[-1] == msg\n\n    def test_can_create_websocket_api_mapping_with_path(self):\n        domain_name = create_websocket_domain_name()\n        domain_name.api_mapping.mount_path = 'path-key'\n\n        connect_function = create_function_resource(\n            'function_name_connect')\n        message_function = create_function_resource(\n            'function_name_message')\n        disconnect_function = create_function_resource(\n            'function_name_disconnect')\n\n        websocket_api = models.WebsocketAPI(\n            resource_name='websocket_api',\n            name='app-dev-websocket-api',\n            api_gateway_stage='api',\n            routes=['$connect', '$default', '$disconnect'],\n            connect_function=connect_function,\n            message_function=message_function,\n            disconnect_function=disconnect_function,\n            domain_name=domain_name\n        )\n\n        self.remote_state.declare_no_resources_exists()\n        plan = self.determine_plan(websocket_api)\n        params = {\n            'domain_name': domain_name.domain_name,\n            'path_key': 'path-key',\n            'stage': 'dev',\n            'api_id': Variable('websocket_api_id')\n        }\n        expected = [\n            models.APICall(\n                method_name='create_api_mapping',\n                params=params,\n                output_var='api_mapping'\n            ),\n        ]\n        # create api mapping\n        self.assert_apicall_equals(plan[-3], expected[0])\n        msg = 'Creating api mapping: /path-key\\n'\n        assert list(self.last_plan.messages.values())[-1] == msg\n\n    def test_store_api_mapping_if_already_exists(self, lambda_function):\n        domain_name = create_http_domain_name()\n        domain_name.api_mapping.mount_path = 'test-path'\n        rest_api = models.RestAPI(\n            resource_name='rest_api',\n            swagger_doc={'swagger': '2.0'},\n            minimum_compression='',\n            api_gateway_stage='api',\n            endpoint_type='EDGE',\n            lambda_function=lambda_function,\n            domain_name=domain_name\n        )\n\n        deployed_value = {\n            'name': 'api_gateway_custom_domain',\n            'resource_type': 'domain_name',\n            'hosted_zone_id': 'hosted_zone_id',\n            'certificate_arn': 'certificate_arn',\n            'security_policy': 'TLS_1_0',\n            'domain_name': 'example.com',\n            'api_mapping': [\n                {\n                    'key': '/test-path'\n                },\n                {\n                    'key': '/test-path-2'\n                }\n            ]\n        }\n\n        self.remote_state.declare_resource_exists(domain_name,\n                                                  **deployed_value)\n        plan = self.determine_plan(rest_api)\n        expected = [\n            models.StoreMultipleValue(\n                name='rest_api_mapping',\n                value=[{\n                    'key': '/test-path'\n                }]\n            )\n        ]\n        assert plan[-2].name == expected[0].name\n        assert plan[-2].value == expected[0].value\n        assert isinstance(expected[0], models.StoreMultipleValue)\n        assert isinstance(plan[-2], models.StoreMultipleValue)\n\n    def test_store_api_mapping_none_if_already_exists(self, lambda_function):\n        domain_name = create_http_domain_name()\n        domain_name.api_mapping.mount_path = '(none)'\n        rest_api = models.RestAPI(\n            resource_name='rest_api',\n            swagger_doc={'swagger': '2.0'},\n            minimum_compression='',\n            api_gateway_stage='api',\n            endpoint_type='EDGE',\n            lambda_function=lambda_function,\n            domain_name=domain_name\n        )\n\n        deployed_value = {\n            'name': 'api_gateway_custom_domain',\n            'resource_type': 'domain_name',\n            'hosted_zone_id': 'hosted_zone_id',\n            'certificate_arn': 'certificate_arn',\n            'security_policy': 'TLS_1_0',\n            'domain_name': 'example.com',\n            'api_mapping': [\n                {\n                    'key': '/'\n                },\n            ]\n        }\n\n        self.remote_state.declare_resource_exists(domain_name,\n                                                  **deployed_value)\n        plan = self.determine_plan(rest_api)\n        expected = [\n            models.StoreMultipleValue(\n                name='rest_api_mapping',\n                value=[{\n                    'key': '/'\n                }]\n            )\n        ]\n        assert plan[-2].name == expected[0].name\n        assert plan[-2].value == expected[0].value\n        assert isinstance(expected[0], models.StoreMultipleValue)\n        assert isinstance(plan[-2], models.StoreMultipleValue)\n\n\nclass TestPlanCreateUpdateDomainName(BasePlannerTests):\n    def test_can_create_domain_name(self, lambda_function):\n        domain_name = create_http_domain_name()\n        rest_api = models.RestAPI(\n            resource_name='rest_api',\n            swagger_doc={'swagger': '2.0'},\n            minimum_compression='',\n            api_gateway_stage='api',\n            endpoint_type='EDGE',\n            lambda_function=lambda_function,\n            domain_name=domain_name\n        )\n\n        params = {\n            'protocol': domain_name.protocol.value,\n            'domain_name': domain_name.domain_name,\n            'security_policy': domain_name.tls_version.value,\n            'certificate_arn': domain_name.certificate_arn,\n            'endpoint_type': 'EDGE',\n            'tags': None\n        }\n        self.remote_state.declare_no_resources_exists()\n        plan = self.determine_plan(rest_api)\n        expected = [\n            models.APICall(\n                method_name='create_domain_name',\n                params=params,\n                output_var=domain_name.resource_name\n            )\n        ]\n        # create domain name\n        self.assert_apicall_equals(plan[13], expected[0])\n        msg = 'Creating custom domain name: example.com\\n'\n        assert list(self.last_plan.messages.values())[-2] == msg\n\n    def test_can_update_domain_name(self):\n        deployed_value = {\n            'name': 'rest_api_domain_name',\n            'resource_type': 'domain_name',\n            'hosted_zone_id': 'hosted_zone_id',\n            'certificate_arn': 'certificate_arn',\n            'security_policy': 'TLS_1_0',\n            'domain_name': 'example.com',\n        }\n        domain_name = create_http_domain_name()\n        domain_name.security_policy = 'TLS_1_2'\n        domain_name.certificate_arn = 'certificate_arn_1'\n        domain_name.hosted_zone_id = ' hosted_zone_1'\n\n        params = {\n            'protocol': domain_name.protocol.value,\n            'domain_name': domain_name.domain_name,\n            'security_policy': domain_name.tls_version.value,\n            'certificate_arn': domain_name.certificate_arn,\n            'endpoint_type': 'EDGE',\n            'tags': None\n        }\n        self.remote_state.declare_resource_exists(\n            domain_name, **deployed_value\n        )\n        planner = PlanStage(self.remote_state, self.osutils)\n\n        plan = planner._add_domainname_plan(domain_name, 'EDGE')\n        expected = [\n            models.APICall(\n                method_name='update_domain_name',\n                params=params,\n                output_var=domain_name.resource_name\n            )\n        ]\n        # update domain name\n        self.assert_apicall_equals(plan[0][0], expected[0])\n        assert plan[0][1] == 'Updating custom domain name: example.com\\n'\n\n\nclass TestPlanLambdaFunction(BasePlannerTests):\n\n    def test_can_create_layer(self):\n        layer = models.LambdaLayer(\n            resource_name='layer',\n            layer_name='bar',\n            runtime='python2.7',\n            deployment_package=models.DeploymentPackage(\n                filename='foo')\n        )\n        plan = self.determine_plan(layer)\n        expected = [models.APICall(\n            method_name='publish_layer',\n            params={\n                'layer_name': 'bar',\n                'zip_contents': mock.ANY,\n                'runtime': 'python2.7'})\n        ]\n        self.assert_apicall_equals(plan[0], expected[0])\n        assert list(self.last_plan.messages.values()) == [\n            'Creating lambda layer: bar\\n',\n        ]\n\n    def test_can_update_layer(self):\n        layer = models.LambdaLayer(\n            resource_name='layer',\n            layer_name='bar',\n            runtime='python2.7',\n            deployment_package=models.DeploymentPackage(\n                filename='foo')\n        )\n        copy_of_layer = replace(layer)\n        self.remote_state.declare_resource_exists(\n            copy_of_layer,\n            layer_version_arn='arn:bar:4'\n        )\n\n        plan = self.determine_plan(layer)\n        expected = [\n            models.APICall(\n                method_name='delete_layer_version',\n                params={'layer_version_arn': 'arn:bar:4'}),\n            models.APICall(\n                method_name='publish_layer',\n                params={\n                    'layer_name': 'bar',\n                    'zip_contents': mock.ANY,\n                    'runtime': 'python2.7'}),\n            models.RecordResourceVariable(\n                resource_type='lambda_layer',\n                resource_name='layer',\n                name='layer_version_arn',\n                variable_name='layer_version_arn')\n        ]\n        assert len(plan) == 3\n        assert plan[0] == expected[0]\n        assert plan[2] == expected[2]\n        self.assert_apicall_equals(plan[1], expected[1])\n        assert list(self.last_plan.messages.values()) == [\n            'Updating lambda layer: bar\\n',\n        ]\n\n    def test_can_create_function(self):\n        function = create_function_resource('function_name')\n        self.remote_state.declare_no_resources_exists()\n        plan = self.determine_plan(function)\n        expected = [models.APICall(\n            method_name='create_function',\n            params={\n                'function_name': 'appname-dev-function_name',\n                'role_arn': 'role:arn',\n                'zip_contents': mock.ANY,\n                'runtime': 'python2.7',\n                'handler': 'app.app',\n                'environment_variables': {},\n                'tags': {},\n                'xray': None,\n                'timeout': 60,\n                'memory_size': 128,\n                'security_group_ids': [],\n                'subnet_ids': [],\n                'layers': [],\n            },\n        ),\n            models.APICall(\n            method_name='delete_function_concurrency',\n            params={\n                'function_name': 'appname-dev-function_name',\n            },\n            output_var='reserved_concurrency_result',\n        )]\n\n        # create_function\n        self.assert_apicall_equals(plan[0], expected[0])\n        # delete_function_concurrency\n        self.assert_apicall_equals(plan[2], expected[1])\n\n        assert list(self.last_plan.messages.values()) == [\n            'Creating lambda function: appname-dev-function_name\\n',\n        ]\n\n    def test_create_function_with_layers(self):\n        layers = ['arn:aws:lambda:us-east-1:111:layer:test_layer:1']\n        function = create_function_resource(\n            'function_name', layers=layers,\n            managed_layer=create_managed_layer()\n        )\n        self.remote_state.declare_no_resources_exists()\n        plan = self.filter_api_calls(\n            self.determine_plan(function.managed_layer))\n        plan.extend(self.filter_api_calls(self.determine_plan(function)))\n        expected = [models.APICall(\n            method_name='publish_layer',\n            params={\n                'layer_name': 'bar',\n                'zip_contents': mock.ANY,\n                'runtime': 'python2.7'}\n        ),\n            models.APICall(\n            method_name='create_function',\n            params={\n                'function_name': 'appname-dev-function_name',\n                'role_arn': 'role:arn',\n                'zip_contents': mock.ANY,\n                'runtime': 'python2.7',\n                'handler': 'app.app',\n                'environment_variables': {},\n                'tags': {},\n                'timeout': 60,\n                'xray': None,\n                'memory_size': 128,\n                'security_group_ids': [],\n                'subnet_ids': [],\n                'layers': [Variable('layer_version_arn')] + layers\n            },\n        ),\n            models.APICall(\n                method_name='delete_function_concurrency',\n                params={\n                    'function_name': 'appname-dev-function_name',\n                },\n                output_var='reserved_concurrency_result',\n            )]\n\n        # create_function\n        self.assert_apicall_equals(plan[0], expected[0])\n        # delete_function_concurrency\n        self.assert_apicall_equals(plan[1], expected[1])\n\n    def test_can_update_lambda_function_code(self):\n        function = create_function_resource('function_name')\n        copy_of_function = replace(function)\n        self.remote_state.declare_resource_exists(copy_of_function)\n        # Now let's change the memory size and ensure we\n        # get an update.\n        function.memory_size = 256\n        plan = self.determine_plan(function)\n        existing_params = {\n            'function_name': 'appname-dev-function_name',\n            'role_arn': 'role:arn',\n            'zip_contents': mock.ANY,\n            'runtime': 'python2.7',\n            'environment_variables': {},\n            'xray': None,\n            'tags': {},\n            'timeout': 60,\n            'security_group_ids': [],\n            'subnet_ids': [],\n            'layers': [],\n        }\n        expected_params = dict(memory_size=256, **existing_params)\n        expected = [models.APICall(\n            method_name='update_function',\n            params=expected_params,\n        ),\n            models.APICall(\n            method_name='delete_function_concurrency',\n            params={\n                'function_name': 'appname-dev-function_name',\n            },\n            output_var='reserved_concurrency_result',\n        )]\n\n        # update_function\n        self.assert_apicall_equals(plan[0], expected[0])\n        # delete_function_concurrency\n        self.assert_apicall_equals(plan[3], expected[1])\n\n        assert list(self.last_plan.messages.values()) == [\n            'Updating lambda function: appname-dev-function_name\\n',\n        ]\n\n    def test_can_update_lambda_function_with_managed_layer(self):\n        function = create_function_resource(\n            'function_name',\n            managed_layer=create_managed_layer(),\n        )\n        copy_of_function = replace(function)\n        self.remote_state.declare_resource_exists(copy_of_function)\n        copy_of_layer = replace(function.managed_layer)\n        self.remote_state.declare_resource_exists(\n            copy_of_layer,\n            layer_version_arn='arn:bar:4'\n        )\n        plan = self.determine_plan(function.managed_layer)\n        plan.extend(self.determine_plan(function))\n        self.assert_apicall_equals(plan[0], models.APICall(\n            method_name='delete_layer_version',\n            params={'layer_version_arn': 'arn:bar:4'},\n        ))\n        assert plan[3].method_name == 'update_function'\n        assert plan[3].params['layers'] == [Variable('layer_version_arn')]\n\n    def test_can_create_function_with_reserved_concurrency(self):\n        function = create_function_resource('function_name')\n        function.reserved_concurrency = 5\n        self.remote_state.declare_no_resources_exists()\n        plan = self.determine_plan(function)\n        expected = [models.APICall(\n            method_name='create_function',\n            params={\n                'function_name': 'appname-dev-function_name',\n                'role_arn': 'role:arn',\n                'zip_contents': mock.ANY,\n                'runtime': 'python2.7',\n                'handler': 'app.app',\n                'environment_variables': {},\n                'tags': {},\n                'xray': None,\n                'timeout': 60,\n                'memory_size': 128,\n                'security_group_ids': [],\n                'subnet_ids': [],\n                'layers': [],\n            },\n        ),\n            models.APICall(\n            method_name='put_function_concurrency',\n            params={\n                'function_name': 'appname-dev-function_name',\n                'reserved_concurrent_executions': 5\n            },\n            output_var='reserved_concurrency_result',\n        )]\n\n        # create_function\n        self.assert_apicall_equals(plan[0], expected[0])\n        # put_function_concurrency\n        self.assert_apicall_equals(plan[2], expected[1])\n\n        assert list(self.last_plan.messages.values()) == [\n            'Creating lambda function: appname-dev-function_name\\n',\n            'Updating lambda function concurrency limit:'\n            ' appname-dev-function_name\\n',\n        ]\n\n    def test_can_set_variables_when_needed(self):\n        function = create_function_resource('function_name')\n        self.remote_state.declare_no_resources_exists()\n        function.role = models.ManagedIAMRole(\n            resource_name='myrole',\n            role_name='myrole-dev',\n            trust_policy={'trust': 'policy'},\n            policy=models.FileBasedIAMPolicy(\n                filename='foo.json', document={'iam': 'role'}),\n        )\n        plan = self.determine_plan(function)\n        call = plan[0]\n        assert call.method_name == 'create_function'\n        # The params are verified in test_can_create_function,\n        # we just care about how the role_arn Variable is constructed.\n        role_arn = call.params['role_arn']\n        assert isinstance(role_arn, Variable)\n        assert role_arn.name == 'myrole-dev_role_arn'\n\n\nclass TestPlanS3Events(BasePlannerTests):\n    def test_can_plan_s3_event(self):\n        function = create_function_resource('function_name')\n        bucket_event = models.S3BucketNotification(\n            resource_name='function_name-s3event',\n            bucket='mybucket',\n            events=['s3:ObjectCreated:*'],\n            prefix=None,\n            suffix=None,\n            lambda_function=function,\n        )\n        full_plan = self.determine_plan(bucket_event)\n        setup_plan, plan = full_plan[:4], full_plan[4:]\n        assert setup_plan[0:4] == [\n            models.BuiltinFunction(\n                'parse_arn', [Variable(\"function_name_lambda_arn\")],\n                output_var='parsed_lambda_arn',\n            ),\n            models.JPSearch('account_id',\n                            input_var='parsed_lambda_arn',\n                            output_var='account_id'),\n            models.JPSearch('region',\n                            input_var='parsed_lambda_arn',\n                            output_var='region_name'),\n            models.JPSearch('partition',\n                            input_var='parsed_lambda_arn',\n                            output_var='partition')\n        ]\n        self.assert_apicall_equals(\n            plan[0],\n            models.APICall(\n                method_name='add_permission_for_s3_event',\n                params={\n                    'bucket': 'mybucket',\n                    'function_arn': Variable('function_name_lambda_arn'),\n                    'account_id': Variable('account_id'),\n                },\n            )\n        )\n        self.assert_apicall_equals(\n            plan[1],\n            models.APICall(\n                method_name='connect_s3_bucket_to_lambda',\n                params={\n                    'bucket': 'mybucket',\n                    'function_arn': Variable('function_name_lambda_arn'),\n                    'events': ['s3:ObjectCreated:*'],\n                    'prefix': None,\n                    'suffix': None,\n                },\n            )\n        )\n        assert plan[2] == models.RecordResourceValue(\n            resource_type='s3_event',\n            resource_name='function_name-s3event',\n            name='bucket',\n            value='mybucket',\n        )\n        assert plan[3] == models.RecordResourceVariable(\n            resource_type='s3_event',\n            resource_name='function_name-s3event',\n            name='lambda_arn',\n            variable_name='function_name_lambda_arn',\n        )\n\n\nclass TestPlanCloudWatchEvent(BasePlannerTests):\n\n    def test_can_plan_cloudwatch_event(self):\n        function = create_function_resource('function_name')\n        event = models.CloudWatchEvent(\n            resource_name='bar',\n            rule_name='myrulename',\n            event_pattern='\"source\": [\"aws.ec2\"]',\n            lambda_function=function,\n        )\n        plan = self.determine_plan(event)\n        assert len(plan) == 4\n        self.assert_apicall_equals(\n            plan[0],\n            models.APICall(\n                method_name='get_or_create_rule_arn',\n                params={\n                    'rule_name': 'myrulename',\n                    'event_pattern': '\"source\": [\"aws.ec2\"]'\n                },\n                output_var='rule-arn',\n            )\n        )\n        self.assert_apicall_equals(\n            plan[1],\n            models.APICall(\n                method_name='connect_rule_to_lambda',\n                params={'rule_name': 'myrulename',\n                        'function_arn': Variable('function_name_lambda_arn')}\n            )\n        )\n        self.assert_apicall_equals(\n            plan[2],\n            models.APICall(\n                method_name='add_permission_for_cloudwatch_event',\n                params={\n                    'rule_arn': Variable('rule-arn'),\n                    'function_arn': Variable('function_name_lambda_arn'),\n                },\n            )\n        )\n        assert plan[3] == models.RecordResourceValue(\n            resource_type='cloudwatch_event',\n            resource_name='bar',\n            name='rule_name',\n            value='myrulename',\n        )\n\n\nclass TestPlanScheduledEvent(BasePlannerTests):\n    def test_can_plan_scheduled_event(self):\n        function = create_function_resource('function_name')\n        event = models.ScheduledEvent(\n            resource_name='bar',\n            rule_name='myrulename',\n            rule_description=\"my rule description\",\n            schedule_expression='rate(5 minutes)',\n            lambda_function=function,\n        )\n        plan = self.determine_plan(event)\n        assert len(plan) == 4\n        self.assert_apicall_equals(\n            plan[0],\n            models.APICall(\n                method_name='get_or_create_rule_arn',\n                params={\n                    'rule_name': 'myrulename',\n                    'rule_description': 'my rule description',\n                    'schedule_expression': 'rate(5 minutes)',\n                },\n                output_var='rule-arn',\n            )\n        )\n        self.assert_apicall_equals(\n            plan[1],\n            models.APICall(\n                method_name='connect_rule_to_lambda',\n                params={'rule_name': 'myrulename',\n                        'function_arn': Variable('function_name_lambda_arn')}\n            )\n        )\n        self.assert_apicall_equals(\n            plan[2],\n            models.APICall(\n                method_name='add_permission_for_cloudwatch_event',\n                params={\n                    'rule_arn': Variable('rule-arn'),\n                    'function_arn': Variable('function_name_lambda_arn'),\n                },\n            )\n        )\n        assert plan[3] == models.RecordResourceValue(\n            resource_type='cloudwatch_event',\n            resource_name='bar',\n            name='rule_name',\n            value='myrulename',\n        )\n\n    def test_can_plan_scheduled_event_can_omit_description(self):\n        function = create_function_resource('function_name')\n        event = models.ScheduledEvent(\n            resource_name='bar',\n            rule_name='myrulename',\n            schedule_expression='rate(5 minutes)',\n            lambda_function=function,\n        )\n        plan = self.determine_plan(event)\n        self.assert_apicall_equals(\n            plan[0],\n            models.APICall(\n                method_name='get_or_create_rule_arn',\n                params={\n                    'rule_name': 'myrulename',\n                    'schedule_expression': 'rate(5 minutes)',\n                },\n                output_var='rule-arn',\n            )\n        )\n\n\nclass TestPlanWebsocketAPI(BasePlannerTests):\n    def assert_loads_needed_variables(self, plan):\n        # Parse arn and store region/account id for future\n        # API calls.\n        assert plan[0:5] == [\n            models.BuiltinFunction(\n                'parse_arn', [Variable('function_name_connect_lambda_arn')],\n                output_var='parsed_lambda_arn',\n            ),\n            models.JPSearch('account_id',\n                            input_var='parsed_lambda_arn',\n                            output_var='account_id'),\n            models.JPSearch('region',\n                            input_var='parsed_lambda_arn',\n                            output_var='region_name'),\n            models.JPSearch('partition',\n                            input_var='parsed_lambda_arn',\n                            output_var='partition'),\n            models.JPSearch('dns_suffix',\n                            input_var='parsed_lambda_arn',\n                            output_var='dns_suffix'),\n        ]\n\n    def test_can_plan_websocket_api(self):\n        connect_function = create_function_resource(\n            'function_name_connect')\n        message_function = create_function_resource(\n            'function_name_message')\n        disconnect_function = create_function_resource(\n            'function_name_disconnect')\n        websocket_api = models.WebsocketAPI(\n            resource_name='websocket_api',\n            name='app-dev-websocket-api',\n            api_gateway_stage='api',\n            routes=['$connect', '$default', '$disconnect'],\n            connect_function=connect_function,\n            message_function=message_function,\n            disconnect_function=disconnect_function,\n        )\n        plan = self.determine_plan(websocket_api)\n        self.assert_loads_needed_variables(plan)\n        assert plan[5:] == [\n            models.APICall(\n                method_name='create_websocket_api',\n                params={'name': 'app-dev-websocket-api'},\n                output_var='websocket_api_id',\n            ),\n            models.StoreValue(\n                name='routes',\n                value=[],\n            ),\n            models.StoreValue(\n                name='websocket-connect-integration-lambda-path',\n                value=StringFormat(\n                    'arn:{partition}:apigateway:{region_name}:lambda:path/'\n                    '2015-03-31/functions/arn:{partition}:lambda'\n                    ':{region_name}:{account_id}:function:%s/'\n                    'invocations' % 'appname-dev-function_name_connect',\n                    ['partition', 'region_name', 'account_id'],\n                ),\n            ),\n            models.APICall(\n                method_name='create_websocket_integration',\n                params={\n                    'api_id': Variable('websocket_api_id'),\n                    'lambda_function': Variable(\n                        'websocket-connect-integration-lambda-path'),\n                    'handler_type': 'connect',\n                },\n                output_var='connect-integration-id',\n            ),\n            models.StoreValue(\n                name='websocket-message-integration-lambda-path',\n                value=StringFormat(\n                    'arn:{partition}:apigateway:{region_name}:lambda:path/'\n                    '2015-03-31/functions/arn:{partition}:lambda'\n                    ':{region_name}:{account_id}:function:%s/'\n                    'invocations' % 'appname-dev-function_name_message',\n                    ['partition', 'region_name', 'account_id'],\n                ),\n            ),\n            models.APICall(\n                method_name='create_websocket_integration',\n                params={\n                    'api_id': Variable('websocket_api_id'),\n                    'lambda_function': Variable(\n                        'websocket-message-integration-lambda-path'),\n                    'handler_type': 'message',\n                },\n                output_var='message-integration-id',\n            ),\n            models.StoreValue(\n                name='websocket-disconnect-integration-lambda-path',\n                value=StringFormat(\n                    'arn:{partition}:apigateway:{region_name}:lambda:path/'\n                    '2015-03-31/functions/arn:{partition}:lambda'\n                    ':{region_name}:{account_id}:function:%s/'\n                    'invocations' % 'appname-dev-function_name_disconnect',\n                    ['partition', 'region_name', 'account_id'],\n                ),\n            ),\n            models.APICall(\n                method_name='create_websocket_integration',\n                params={\n                    'api_id': Variable('websocket_api_id'),\n                    'lambda_function': Variable(\n                        'websocket-disconnect-integration-lambda-path'),\n                    'handler_type': 'disconnect',\n                },\n                output_var='disconnect-integration-id',\n            ),\n            models.APICall(\n                method_name='create_websocket_route',\n                params={\n                    'api_id': Variable('websocket_api_id'),\n                    'route_key': '$connect',\n                    'integration_id': Variable('connect-integration-id'),\n                },\n            ),\n            models.APICall(\n                method_name='create_websocket_route',\n                params={\n                    'api_id': Variable('websocket_api_id'),\n                    'route_key': '$default',\n                    'integration_id': Variable('message-integration-id'),\n                },\n            ),\n            models.APICall(\n                method_name='create_websocket_route',\n                params={\n                    'api_id': Variable('websocket_api_id'),\n                    'route_key': '$disconnect',\n                    'integration_id': Variable('disconnect-integration-id'),\n                },\n            ),\n            models.APICall(\n                method_name='deploy_websocket_api',\n                params={\n                    'api_id': Variable('websocket_api_id'),\n                },\n                output_var='deployment-id',\n            ),\n            models.APICall(\n                method_name='create_stage',\n                params={\n                    'api_id': Variable('websocket_api_id'),\n                    'stage_name': 'api',\n                    'deployment_id': Variable('deployment-id'),\n                }\n            ),\n            models.StoreValue(\n                name='websocket_api_url',\n                value=StringFormat(\n                    'wss://{websocket_api_id}.execute-api.{region_name}'\n                    '.{dns_suffix}/%s/' % 'api',\n                    ['websocket_api_id', 'region_name', 'dns_suffix'],\n                ),\n            ),\n            models.RecordResourceVariable(\n                resource_type='websocket_api',\n                resource_name='websocket_api',\n                name='websocket_api_url',\n                variable_name='websocket_api_url',\n            ),\n            models.RecordResourceVariable(\n                resource_type='websocket_api',\n                resource_name='websocket_api',\n                name='websocket_api_id',\n                variable_name='websocket_api_id',\n            ),\n            models.APICall(\n                method_name='add_permission_for_apigateway_v2',\n                params={'function_name': 'appname-dev-function_name_connect',\n                        'region_name': Variable('region_name'),\n                        'account_id': Variable('account_id'),\n                        'api_id': Variable('websocket_api_id')},\n            ),\n            models.APICall(\n                method_name='add_permission_for_apigateway_v2',\n                params={'function_name': 'appname-dev-function_name_message',\n                        'region_name': Variable('region_name'),\n                        'account_id': Variable('account_id'),\n                        'api_id': Variable('websocket_api_id')},\n            ),\n            models.APICall(\n                method_name='add_permission_for_apigateway_v2',\n                params={\n                    'function_name': 'appname-dev-function_name_disconnect',\n                    'region_name': Variable('region_name'),\n                    'account_id': Variable('account_id'),\n                    'api_id': Variable('websocket_api_id')},\n            ),\n        ]\n\n    def test_can_update_websocket_api(self):\n        connect_function = create_function_resource(\n            'function_name_connect')\n        message_function = create_function_resource(\n            'function_name_message')\n        disconnect_function = create_function_resource(\n            'function_name_disconnect')\n        websocket_api = models.WebsocketAPI(\n            resource_name='websocket_api',\n            name='app-dev-websocket-api',\n            api_gateway_stage='api',\n            routes=['$connect', '$default', '$disconnect'],\n            connect_function=connect_function,\n            message_function=message_function,\n            disconnect_function=disconnect_function,\n        )\n        self.remote_state.declare_resource_exists(websocket_api)\n        self.remote_state.deployed_values['websocket_api'] = {\n            'websocket_api_id': 'my_websocket_api_id',\n        }\n        plan = self.determine_plan(websocket_api)\n        self.assert_loads_needed_variables(plan)\n        assert plan[5:] == [\n            models.StoreValue(\n                name='websocket_api_id',\n                value='my_websocket_api_id',\n            ),\n            models.APICall(\n                method_name='get_websocket_routes',\n                params={'api_id': Variable('websocket_api_id')},\n                output_var='routes',\n            ),\n            models.APICall(\n                method_name='delete_websocket_routes',\n                params={'api_id': Variable('websocket_api_id'),\n                        'routes': Variable('routes')},\n            ),\n            models.APICall(\n                method_name='get_websocket_integrations',\n                params={'api_id': Variable('websocket_api_id')},\n                output_var='integrations',\n            ),\n            models.APICall(\n                method_name='delete_websocket_integrations',\n                params={'api_id': Variable('websocket_api_id'),\n                        'integrations': Variable('integrations')},\n            ),\n            models.StoreValue(\n                name='websocket-connect-integration-lambda-path',\n                value=StringFormat(\n                    'arn:{partition}:apigateway:{region_name}:lambda:path/'\n                    '2015-03-31/functions/arn:{partition}:lambda'\n                    ':{region_name}:{account_id}:function:%s/'\n                    'invocations' % 'appname-dev-function_name_connect',\n                    ['partition', 'region_name', 'account_id'],\n                ),\n            ),\n            models.APICall(\n                method_name='create_websocket_integration',\n                params={\n                    'api_id': Variable('websocket_api_id'),\n                    'lambda_function': Variable(\n                        'websocket-connect-integration-lambda-path'),\n                    'handler_type': 'connect',\n                },\n                output_var='connect-integration-id',\n            ),\n            models.StoreValue(\n                name='websocket-message-integration-lambda-path',\n                value=StringFormat(\n                    'arn:{partition}:apigateway:{region_name}:lambda:path/'\n                    '2015-03-31/functions/arn:{partition}:lambda'\n                    ':{region_name}:{account_id}:function:%s/'\n                    'invocations' % 'appname-dev-function_name_message',\n                    ['partition', 'region_name', 'account_id'],\n                ),\n            ),\n            models.APICall(\n                method_name='create_websocket_integration',\n                params={\n                    'api_id': Variable('websocket_api_id'),\n                    'lambda_function': Variable(\n                        'websocket-message-integration-lambda-path'),\n                    'handler_type': 'message',\n                },\n                output_var='message-integration-id',\n            ),\n            models.StoreValue(\n                name='websocket-disconnect-integration-lambda-path',\n                value=StringFormat(\n                    'arn:{partition}:apigateway:{region_name}:lambda:path/'\n                    '2015-03-31/functions/arn:{partition}:lambda'\n                    ':{region_name}:{account_id}:function:%s/'\n                    'invocations' % 'appname-dev-function_name_disconnect',\n                    ['partition', 'region_name', 'account_id'],\n                ),\n            ),\n            models.APICall(\n                method_name='create_websocket_integration',\n                params={\n                    'api_id': Variable('websocket_api_id'),\n                    'lambda_function': Variable(\n                        'websocket-disconnect-integration-lambda-path'),\n                    'handler_type': 'disconnect',\n                },\n                output_var='disconnect-integration-id',\n            ),\n            models.APICall(\n                method_name='create_websocket_route',\n                params={\n                    'api_id': Variable('websocket_api_id'),\n                    'route_key': '$connect',\n                    'integration_id': Variable('connect-integration-id'),\n                },\n            ),\n            models.APICall(\n                method_name='create_websocket_route',\n                params={\n                    'api_id': Variable('websocket_api_id'),\n                    'route_key': '$default',\n                    'integration_id': Variable('message-integration-id'),\n                },\n            ),\n            models.APICall(\n                method_name='create_websocket_route',\n                params={\n                    'api_id': Variable('websocket_api_id'),\n                    'route_key': '$disconnect',\n                    'integration_id': Variable('disconnect-integration-id'),\n                },\n            ),\n            models.StoreValue(\n                name='websocket_api_url',\n                value=StringFormat(\n                    'wss://{websocket_api_id}.execute-api.{region_name}'\n                    '.{dns_suffix}/%s/' % 'api',\n                    ['websocket_api_id', 'region_name', 'dns_suffix'],\n                ),\n            ),\n            models.RecordResourceVariable(\n                resource_type='websocket_api',\n                resource_name='websocket_api',\n                name='websocket_api_url',\n                variable_name='websocket_api_url',\n            ),\n            models.RecordResourceVariable(\n                resource_type='websocket_api',\n                resource_name='websocket_api',\n                name='websocket_api_id',\n                variable_name='websocket_api_id',\n            ),\n            models.APICall(\n                method_name='add_permission_for_apigateway_v2',\n                params={'function_name': 'appname-dev-function_name_connect',\n                        'region_name': Variable('region_name'),\n                        'account_id': Variable('account_id'),\n                        'api_id': Variable('websocket_api_id')},\n            ),\n            models.APICall(\n                method_name='add_permission_for_apigateway_v2',\n                params={'function_name': 'appname-dev-function_name_message',\n                        'region_name': Variable('region_name'),\n                        'account_id': Variable('account_id'),\n                        'api_id': Variable('websocket_api_id')},\n            ),\n            models.APICall(\n                method_name='add_permission_for_apigateway_v2',\n                params={\n                    'function_name': 'appname-dev-function_name_disconnect',\n                    'region_name': Variable('region_name'),\n                    'account_id': Variable('account_id'),\n                    'api_id': Variable('websocket_api_id'),\n                },\n            ),\n        ]\n\n\nclass TestPlanRestAPI(BasePlannerTests):\n    def assert_loads_needed_variables(self, plan):\n        # Parse arn and store region/account id for future\n        # API calls.\n        assert plan[0:6] == [\n            models.BuiltinFunction(\n                'parse_arn', [Variable('function_name_lambda_arn')],\n                output_var='parsed_lambda_arn',\n            ),\n            models.JPSearch('account_id',\n                            input_var='parsed_lambda_arn',\n                            output_var='account_id'),\n            models.JPSearch('region',\n                            input_var='parsed_lambda_arn',\n                            output_var='region_name'),\n            models.JPSearch('partition',\n                            input_var='parsed_lambda_arn',\n                            output_var='partition'),\n            models.JPSearch('dns_suffix',\n                            input_var='parsed_lambda_arn',\n                            output_var='dns_suffix'),\n            # Verify we copy the function arn as needed.\n            models.CopyVariable(\n                from_var='function_name_lambda_arn',\n                to_var='api_handler_lambda_arn'),\n        ]\n\n    def test_can_plan_rest_api(self):\n        function = create_function_resource('function_name')\n        rest_api = models.RestAPI(\n            resource_name='rest_api',\n            swagger_doc={'swagger': '2.0'},\n            endpoint_type='EDGE',\n            minimum_compression='100',\n            api_gateway_stage='api',\n            xray=False,\n            lambda_function=function,\n        )\n        plan = self.determine_plan(rest_api)\n        self.assert_loads_needed_variables(plan)\n\n        assert plan[6:] == [\n            models.APICall(\n                method_name='import_rest_api',\n                params={'swagger_document': {'swagger': '2.0'},\n                        'endpoint_type': 'EDGE'},\n                output_var='rest_api_id',\n            ),\n            models.RecordResourceVariable(\n                resource_type='rest_api',\n                resource_name='rest_api',\n                name='rest_api_id',\n                variable_name='rest_api_id',\n            ),\n            models.APICall(\n                method_name='update_rest_api',\n                params={\n                    'rest_api_id': Variable('rest_api_id'),\n                    'patch_operations': [{\n                        'op': 'replace',\n                        'path': '/minimumCompressionSize',\n                        'value': '100',\n                    }],\n                }\n            ),\n            models.APICall(\n                method_name='add_permission_for_apigateway',\n                params={\n                    'function_name': 'appname-dev-function_name',\n                    'region_name': Variable('region_name'),\n                    'account_id': Variable('account_id'),\n                    'rest_api_id': Variable('rest_api_id'),\n                }\n            ),\n            models.APICall(method_name='deploy_rest_api',\n                           params={'rest_api_id': Variable('rest_api_id'),\n                                   'xray': False,\n                                   'api_gateway_stage': 'api'}),\n            models.StoreValue(\n                name='rest_api_url',\n                value=StringFormat(\n                    'https://{rest_api_id}.execute-api.{region_name}'\n                    '.{dns_suffix}/api/',\n                    ['rest_api_id', 'region_name', 'dns_suffix'],\n                ),\n            ),\n            models.RecordResourceVariable(\n                resource_type='rest_api',\n                resource_name='rest_api',\n                name='rest_api_url',\n                variable_name='rest_api_url'\n            ),\n        ]\n        assert list(self.last_plan.messages.values()) == [\n            'Creating Rest API\\n'\n        ]\n\n    def test_can_update_rest_api_with_policy(self):\n        function = create_function_resource('function_name')\n        rest_api = models.RestAPI(\n            resource_name='rest_api',\n            swagger_doc={'swagger': '2.0'},\n            minimum_compression='',\n            api_gateway_stage='api',\n            endpoint_type='EDGE',\n            policy=\"{'Statement': []}\",\n            lambda_function=function,\n        )\n        self.remote_state.declare_resource_exists(rest_api)\n        self.remote_state.deployed_values['rest_api'] = {\n            'rest_api_id': 'my_rest_api_id',\n        }\n        plan = self.determine_plan(rest_api)\n\n        assert plan[10].params == {\n            'patch_operations': [\n                {'op': 'replace',\n                 'path': '/minimumCompressionSize',\n                 'value': ''},\n                {'op': 'replace',\n                 'path': StringFormat(\n                     (\"/endpointConfiguration/types/\"\n                      \"{rest_api[endpointConfiguration][types][0]}\"),\n                     ['rest_api']),\n                 'value': 'EDGE'}\n            ],\n            'rest_api_id': Variable(\"rest_api_id\")\n        }\n\n    def test_can_update_rest_api(self):\n        function = create_function_resource('function_name')\n        rest_api = models.RestAPI(\n            resource_name='rest_api',\n            swagger_doc={'swagger': '2.0'},\n            minimum_compression='',\n            api_gateway_stage='api',\n            endpoint_type='REGIONAL',\n            xray=False,\n            lambda_function=function,\n        )\n        self.remote_state.declare_resource_exists(rest_api)\n        self.remote_state.deployed_values['rest_api'] = {\n            'rest_api_id': 'my_rest_api_id',\n        }\n        plan = self.determine_plan(rest_api)\n        self.assert_loads_needed_variables(plan)\n\n        assert plan[6:] == [\n            models.StoreValue(name='rest_api_id', value='my_rest_api_id'),\n            models.RecordResourceVariable(\n                resource_type='rest_api',\n                resource_name='rest_api',\n                name='rest_api_id',\n                variable_name='rest_api_id',\n            ),\n            models.APICall(\n                method_name='update_api_from_swagger',\n                params={\n                    'rest_api_id': Variable('rest_api_id'),\n                    'swagger_document': {'swagger': '2.0'},\n                },\n            ),\n            models.APICall(\n                method_name='get_rest_api',\n                params={'rest_api_id': Variable('rest_api_id')},\n                output_var='rest_api'\n            ),\n            models.APICall(\n                method_name='update_rest_api',\n                params={\n                    'rest_api_id': Variable('rest_api_id'),\n                    'patch_operations': [{\n                        'op': 'replace',\n                        'path': '/minimumCompressionSize',\n                        'value': ''},\n                        {'op': 'replace',\n                         'value': 'REGIONAL',\n                         'path': StringFormat(\n                             '/endpointConfiguration/types/%s' % (\n                                '{rest_api[endpointConfiguration][types][0]}'),\n                             ['rest_api'])},\n                    ],\n                },\n            ),\n            models.APICall(\n                method_name='add_permission_for_apigateway',\n                params={'rest_api_id': Variable(\"rest_api_id\"),\n                        'region_name': Variable(\"region_name\"),\n                        'account_id': Variable(\"account_id\"),\n                        'function_name': 'appname-dev-function_name'},\n                output_var=None),\n            models.APICall(\n                method_name='deploy_rest_api',\n                params={'rest_api_id': Variable('rest_api_id'),\n                        'xray': False,\n                        'api_gateway_stage': 'api'},\n            ),\n            models.StoreValue(\n                name='rest_api_url',\n                value=StringFormat(\n                    'https://{rest_api_id}.execute-api.{region_name}'\n                    '.{dns_suffix}/api/',\n                    ['rest_api_id', 'region_name', 'dns_suffix'],\n                ),\n            ),\n            models.RecordResourceVariable(\n                resource_type='rest_api',\n                resource_name='rest_api',\n                name='rest_api_url',\n                variable_name='rest_api_url'\n            ),\n        ]\n\n\nclass TestPlanSNSSubscription(BasePlannerTests):\n    def test_can_plan_sns_subscription(self):\n        function = create_function_resource('function_name')\n        sns_subscription = models.SNSLambdaSubscription(\n            resource_name='function_name-sns-subscription',\n            topic='mytopic',\n            lambda_function=function\n        )\n        plan = self.determine_plan(sns_subscription)\n        plan_parse_arn = plan[:5]\n        assert plan_parse_arn == [\n            models.BuiltinFunction(\n                function_name='parse_arn',\n                args=[Variable(\"function_name_lambda_arn\")],\n                output_var='parsed_lambda_arn'),\n            models.JPSearch(\n                expression='account_id',\n                input_var='parsed_lambda_arn',\n                output_var='account_id'),\n            models.JPSearch(\n                expression='region',\n                input_var='parsed_lambda_arn',\n                output_var='region_name'),\n            models.JPSearch(\n                expression='partition',\n                input_var='parsed_lambda_arn',\n                output_var='partition'),\n            models.StoreValue(\n                name='function_name-sns-subscription_topic_arn',\n                value=StringFormat(\n                    \"arn:{partition}:sns:{region_name}:{account_id}:mytopic\",\n                    variables=['partition', 'region_name', 'account_id'],\n                )\n            ),\n        ]\n        topic_arn_var = Variable(\"function_name-sns-subscription_topic_arn\")\n        assert plan[5:7] == [\n            models.APICall(\n                method_name='add_permission_for_sns_topic',\n                params={\n                    'function_arn': Variable(\"function_name_lambda_arn\"),\n                    'topic_arn': topic_arn_var,\n                },\n                output_var=None\n            ),\n            models.APICall(\n                method_name='subscribe_function_to_topic',\n                params={\n                    'function_arn': Variable(\"function_name_lambda_arn\"),\n                    'topic_arn': topic_arn_var,\n                },\n                output_var='function_name-sns-subscription_subscription_arn'\n            ),\n        ]\n        self.assert_recorded_values(\n            plan, 'sns_event', 'function_name-sns-subscription', {\n                'topic': 'mytopic',\n                'lambda_arn': Variable('function_name_lambda_arn'),\n                'subscription_arn': Variable(\n                    'function_name-sns-subscription_subscription_arn'),\n                'topic_arn': Variable(\n                    'function_name-sns-subscription_topic_arn'),\n            }\n        )\n\n    def test_can_plan_sns_arn_subscription(self):\n        function = create_function_resource('function_name')\n        topic_arn = 'arn:aws:sns:mars-west-2:123456789:mytopic'\n        sns_subscription = models.SNSLambdaSubscription(\n            resource_name='function_name-sns-subscription',\n            topic=topic_arn,\n            lambda_function=function\n        )\n        plan = self.determine_plan(sns_subscription)\n        plan_parse_arn = plan[0]\n        assert plan_parse_arn == models.StoreValue(\n            name='function_name-sns-subscription_topic_arn',\n            value=topic_arn,\n        )\n        topic_arn_var = Variable(\"function_name-sns-subscription_topic_arn\")\n        assert plan[1:3] == [\n            models.APICall(\n                method_name='add_permission_for_sns_topic',\n                params={\n                    'function_arn': Variable(\"function_name_lambda_arn\"),\n                    'topic_arn': topic_arn_var,\n                },\n                output_var=None\n            ),\n            models.APICall(\n                method_name='subscribe_function_to_topic',\n                params={\n                    'function_arn': Variable(\"function_name_lambda_arn\"),\n                    'topic_arn': topic_arn_var,\n                },\n                output_var='function_name-sns-subscription_subscription_arn'\n            ),\n        ]\n        self.assert_recorded_values(\n            plan, 'sns_event', 'function_name-sns-subscription', {\n                'topic': topic_arn,\n                'lambda_arn': Variable('function_name_lambda_arn'),\n                'subscription_arn': Variable(\n                    'function_name-sns-subscription_subscription_arn'),\n                'topic_arn': Variable(\n                    'function_name-sns-subscription_topic_arn'),\n            }\n        )\n\n    def test_sns_subscription_exists_is_noop_for_planner(self):\n        function = create_function_resource('function_name')\n        sns_subscription = models.SNSLambdaSubscription(\n            resource_name='function_name-sns-subscription',\n            topic='mytopic',\n            lambda_function=function\n        )\n        self.remote_state.declare_resource_exists(\n            sns_subscription,\n            topic='mytopic',\n            resource_type='sns_event',\n            lambda_arn='arn:lambda',\n            subscription_arn='arn:aws:subscribe',\n        )\n        plan = self.determine_plan(sns_subscription)\n        plan_parse_arn = plan[:5]\n        assert plan_parse_arn == [\n            models.BuiltinFunction(\n                function_name='parse_arn',\n                args=[Variable(\"function_name_lambda_arn\")],\n                output_var='parsed_lambda_arn'),\n            models.JPSearch(\n                expression='account_id',\n                input_var='parsed_lambda_arn',\n                output_var='account_id'),\n            models.JPSearch(\n                expression='region',\n                input_var='parsed_lambda_arn',\n                output_var='region_name'),\n            models.JPSearch(\n                expression='partition',\n                input_var='parsed_lambda_arn',\n                output_var='partition'),\n            models.StoreValue(\n                name='function_name-sns-subscription_topic_arn',\n                value=StringFormat(\n                    \"arn:{partition}:sns:{region_name}:{account_id}:mytopic\",\n                    variables=['partition', 'region_name', 'account_id'],\n                )\n            ),\n        ]\n        self.assert_recorded_values(\n            plan, 'sns_event', 'function_name-sns-subscription', {\n                'topic': 'mytopic',\n                'lambda_arn': Variable('function_name_lambda_arn'),\n                'subscription_arn': 'arn:aws:subscribe',\n                'topic_arn': Variable(\n                    'function_name-sns-subscription_topic_arn'),\n            }\n        )\n\n\nclass TestPlanSQSSubscription(BasePlannerTests):\n    def test_can_plan_sqs_event_source(self):\n        function = create_function_resource('function_name')\n        sqs_event_source = models.SQSEventSource(\n            resource_name='function_name-sqs-event-source',\n            queue='myqueue',\n            batch_size=10,\n            lambda_function=function,\n            maximum_batching_window_in_seconds=60\n        )\n        plan = self.determine_plan(sqs_event_source)\n        plan_parse_arn = plan[:5]\n        assert plan_parse_arn == [\n            models.BuiltinFunction(\n                function_name='parse_arn',\n                args=[Variable(\"function_name_lambda_arn\")],\n                output_var='parsed_lambda_arn'\n            ),\n            models.JPSearch(\n                expression='account_id',\n                input_var='parsed_lambda_arn',\n                output_var='account_id'\n            ),\n            models.JPSearch(\n                expression='region',\n                input_var='parsed_lambda_arn',\n                output_var='region_name'\n            ),\n            models.JPSearch(\n                expression='partition',\n                input_var='parsed_lambda_arn',\n                output_var='partition'\n            ),\n            models.StoreValue(\n                name='function_name-sqs-event-source_queue_arn',\n                value=StringFormat(\n                    \"arn:{partition}:sqs:{region_name}:{account_id}:myqueue\",\n                    variables=['partition', 'region_name', 'account_id'],\n                ),\n            )\n        ]\n        assert plan[5] == models.APICall(\n            method_name='create_lambda_event_source',\n            params={\n                'event_source_arn': Variable(\n                    \"function_name-sqs-event-source_queue_arn\"\n                ),\n                'batch_size': 10,\n                'maximum_batching_window_in_seconds': 60,\n                'function_name': Variable(\"function_name_lambda_arn\"),\n                'maximum_concurrency': None\n            },\n            output_var='function_name-sqs-event-source_uuid'\n        )\n        self.assert_recorded_values(\n            plan, 'sqs_event', 'function_name-sqs-event-source', {\n                'queue_arn': Variable(\n                    'function_name-sqs-event-source_queue_arn'),\n                'event_uuid': Variable(\n                    'function_name-sqs-event-source_uuid'),\n                'queue': 'myqueue',\n                'lambda_arn': Variable(\n                    'function_name_lambda_arn')\n            }\n        )\n\n    def test_sqs_event_supports_queue_arn(self):\n        function = create_function_resource('function_name')\n        sqs_event_source = models.SQSEventSource(\n            resource_name='function_name-sqs-event-source',\n            queue=models.QueueARN(arn='arn:us-west-2:myqueue'),\n            batch_size=10,\n            lambda_function=function,\n            maximum_batching_window_in_seconds=0\n        )\n        plan = self.determine_plan(sqs_event_source)\n        assert plan[0] == models.StoreValue(\n            name='function_name-sqs-event-source_queue_arn',\n            value='arn:us-west-2:myqueue',\n        )\n        assert plan[1] == models.APICall(\n            method_name='create_lambda_event_source',\n            params={\n                'event_source_arn': Variable(\n                    \"function_name-sqs-event-source_queue_arn\"\n                ),\n                'batch_size': 10,\n                'maximum_batching_window_in_seconds': 0,\n                'function_name': Variable(\"function_name_lambda_arn\"),\n                'maximum_concurrency': None,\n            },\n            output_var='function_name-sqs-event-source_uuid'\n        )\n        self.assert_recorded_values(\n            plan, 'sqs_event', 'function_name-sqs-event-source', {\n                'queue_arn': Variable(\n                    'function_name-sqs-event-source_queue_arn'),\n                'event_uuid': Variable(\n                    'function_name-sqs-event-source_uuid'),\n                'queue': 'myqueue',\n                'lambda_arn': Variable(\n                    'function_name_lambda_arn')\n            }\n        )\n\n    def test_can_update_sqs_event_with_queue_arn(self):\n        function = create_function_resource('function_name')\n        sqs_event_source = models.SQSEventSource(\n            resource_name='function_name-sqs-event-source',\n            queue=models.QueueARN(arn='arn:sqs:myqueue'),\n            batch_size=10,\n            lambda_function=function,\n            maximum_batching_window_in_seconds=0\n        )\n        self.remote_state.declare_resource_exists(\n            sqs_event_source,\n            queue='myqueue',\n            queue_arn='arn:sqs:myqueue',\n            resource_type='sqs_event',\n            lambda_arn='arn:lambda',\n            event_uuid='my-uuid',\n        )\n        plan = self.determine_plan(sqs_event_source)\n        assert plan[0] == models.StoreValue(\n            name='function_name-sqs-event-source_queue_arn',\n            value='arn:sqs:myqueue',\n        )\n        assert plan[1] == models.APICall(\n            method_name='update_lambda_event_source',\n            params={\n                'event_uuid': 'my-uuid',\n                'batch_size': 10,\n                'maximum_batching_window_in_seconds': 0,\n                'maximum_concurrency': None,\n            },\n        )\n        self.assert_recorded_values(\n            plan, 'sqs_event', 'function_name-sqs-event-source', {\n                'queue_arn': 'arn:sqs:myqueue',\n                'event_uuid': 'my-uuid',\n                'queue': 'myqueue',\n                'lambda_arn': 'arn:lambda'\n            }\n        )\n\n    def test_sqs_event_source_exists_updates_batch_size(self):\n        function = create_function_resource('function_name')\n        sqs_event_source = models.SQSEventSource(\n            resource_name='function_name-sqs-event-source',\n            queue='myqueue',\n            batch_size=10,\n            lambda_function=function,\n            maximum_batching_window_in_seconds=0\n        )\n        self.remote_state.declare_resource_exists(\n            sqs_event_source,\n            queue='myqueue',\n            queue_arn='arn:sqs:myqueue',\n            resource_type='sqs_event',\n            lambda_arn='arn:lambda',\n            event_uuid='my-uuid',\n        )\n        plan = self.determine_plan(sqs_event_source)\n        plan_parse_arn = plan[:5]\n        assert plan_parse_arn == [\n            models.BuiltinFunction(\n                function_name='parse_arn',\n                args=[Variable(\"function_name_lambda_arn\")],\n                output_var='parsed_lambda_arn'),\n            models.JPSearch(\n                expression='account_id',\n                input_var='parsed_lambda_arn',\n                output_var='account_id'),\n            models.JPSearch(\n                expression='region',\n                input_var='parsed_lambda_arn',\n                output_var='region_name'),\n            models.JPSearch(\n                expression='partition',\n                input_var='parsed_lambda_arn',\n                output_var='partition'\n            ),\n            models.StoreValue(\n                name='function_name-sqs-event-source_queue_arn',\n                value=StringFormat(\n                    \"arn:{partition}:sqs:{region_name}:{account_id}:myqueue\",\n                    variables=['partition', 'region_name', 'account_id'],\n                ),\n            )\n        ]\n        assert plan[5] == models.APICall(\n            method_name='update_lambda_event_source',\n            params={\n                'event_uuid': 'my-uuid',\n                'batch_size': 10,\n                'maximum_batching_window_in_seconds': 0,\n                'maximum_concurrency': None,\n            },\n        )\n        self.assert_recorded_values(\n            plan, 'sqs_event', 'function_name-sqs-event-source', {\n                'queue_arn': 'arn:sqs:myqueue',\n                'event_uuid': 'my-uuid',\n                'queue': 'myqueue',\n                'lambda_arn': 'arn:lambda'\n            }\n        )\n\n    def test_sqs_event_supports_maximum_concurrency(self):\n        function = create_function_resource('function_name')\n        sqs_event_source = models.SQSEventSource(\n            resource_name='function_name-sqs-event-source',\n            queue=models.QueueARN(arn='arn:us-west-2:myqueue'),\n            batch_size=10,\n            lambda_function=function,\n            maximum_batching_window_in_seconds=0,\n            maximum_concurrency=2\n        )\n        plan = self.determine_plan(sqs_event_source)\n        assert plan[1] == models.APICall(\n            method_name='create_lambda_event_source',\n            params={\n                'event_source_arn': Variable(\n                    \"function_name-sqs-event-source_queue_arn\"\n                ),\n                'batch_size': 10,\n                'maximum_batching_window_in_seconds': 0,\n                'function_name': Variable(\"function_name_lambda_arn\"),\n                'maximum_concurrency': 2,\n            },\n            output_var='function_name-sqs-event-source_uuid'\n        )\n        self.assert_recorded_values(\n            plan, 'sqs_event', 'function_name-sqs-event-source', {\n                'queue_arn': Variable(\n                    'function_name-sqs-event-source_queue_arn'),\n                'event_uuid': Variable(\n                    'function_name-sqs-event-source_uuid'),\n                'queue': 'myqueue',\n                'lambda_arn': Variable(\n                    'function_name_lambda_arn')\n            }\n        )\n\n    def test_sqs_event_source_exists_updates_maximum_concurrency(self):\n        function = create_function_resource('function_name')\n        sqs_event_source = models.SQSEventSource(\n            resource_name='function_name-sqs-event-source',\n            queue='myqueue',\n            batch_size=10,\n            lambda_function=function,\n            maximum_batching_window_in_seconds=0,\n            maximum_concurrency=2\n        )\n        self.remote_state.declare_resource_exists(\n            sqs_event_source,\n            queue='myqueue',\n            queue_arn='arn:sqs:myqueue',\n            resource_type='sqs_event',\n            lambda_arn='arn:lambda',\n            event_uuid='my-uuid',\n        )\n        plan = self.determine_plan(sqs_event_source)\n        assert plan[5] == models.APICall(\n            method_name='update_lambda_event_source',\n            params={\n                'event_uuid': 'my-uuid',\n                'batch_size': 10,\n                'maximum_batching_window_in_seconds': 0,\n                'maximum_concurrency': 2,\n            },\n        )\n        self.assert_recorded_values(\n            plan, 'sqs_event', 'function_name-sqs-event-source', {\n                'queue_arn': 'arn:sqs:myqueue',\n                'event_uuid': 'my-uuid',\n                'queue': 'myqueue',\n                'lambda_arn': 'arn:lambda'\n            }\n        )\n\n    @pytest.mark.parametrize('functions,integration_injected', [\n        (\n            (create_function_resource('connect'), None, None),\n            'connect'\n        ),\n        (\n            (None, create_function_resource('message'), None),\n            'message'\n        ),\n        (\n            (None, None, create_function_resource('disconnect')),\n            'disconnect'\n        ),\n    ])\n    def test_websocket_api_plan_omits_unused_lambdas(\n            self, functions, integration_injected):\n        websocket_api = models.WebsocketAPI(\n            resource_name='websocket_api',\n            name='app-dev-websocket-api',\n            api_gateway_stage='api',\n            routes=['$connect', '$default', '$disconnect'],\n            connect_function=functions[0],\n            message_function=functions[1],\n            disconnect_function=functions[2],\n        )\n        plan = self.determine_plan(websocket_api)\n        integrations = [\n            code.params['handler_type'] for code in plan\n            if isinstance(code, APICall)\n            and code.method_name == 'create_websocket_integration'\n        ]\n\n        assert len(integrations) == 1\n        assert integrations[0] == integration_injected\n\n\nclass TestPlanKinesisSubscription(BasePlannerTests):\n    def test_can_plan_kinesis_event_source(self):\n        function = create_function_resource('function_name')\n        kinesis_event_source = models.KinesisEventSource(\n            resource_name='function_name-kinesis-event-source',\n            stream='mystream',\n            batch_size=10,\n            starting_position='LATEST',\n            maximum_batching_window_in_seconds=0,\n            lambda_function=function\n        )\n        plan = self.determine_plan(kinesis_event_source)\n        plan_parse_arn = plan[:5]\n        assert plan_parse_arn == [\n            models.BuiltinFunction(\n                function_name='parse_arn',\n                args=[Variable(\"function_name_lambda_arn\")],\n                output_var='parsed_lambda_arn'\n            ),\n            models.JPSearch(\n                expression='account_id',\n                input_var='parsed_lambda_arn',\n                output_var='account_id'\n            ),\n            models.JPSearch(\n                expression='region',\n                input_var='parsed_lambda_arn',\n                output_var='region_name'\n            ),\n            models.JPSearch(\n                expression='partition',\n                input_var='parsed_lambda_arn',\n                output_var='partition'\n            ),\n            models.StoreValue(\n                name='function_name-kinesis-event-source_stream_arn',\n                value=StringFormat(\n                    (\"arn:{partition}:kinesis:{region_name}:{account_id}:\"\n                     \"stream/mystream\"),\n                    variables=['partition', 'region_name', 'account_id'],\n                ),\n            )\n        ]\n        assert plan[5] == models.APICall(\n            method_name='create_lambda_event_source',\n            params={\n                'event_source_arn': Variable(\n                    \"function_name-kinesis-event-source_stream_arn\"\n                ),\n                'batch_size': 10,\n                'starting_position': 'LATEST',\n                'maximum_batching_window_in_seconds': 0,\n                'function_name': Variable(\"function_name_lambda_arn\")\n            },\n            output_var='function_name-kinesis-event-source_uuid'\n        )\n        self.assert_recorded_values(\n            plan, 'kinesis_event', 'function_name-kinesis-event-source', {\n                'kinesis_arn': Variable(\n                    'function_name-kinesis-event-source_stream_arn'),\n                'event_uuid': Variable(\n                    'function_name-kinesis-event-source_uuid'),\n                'stream': 'mystream',\n                'lambda_arn': Variable(\n                    'function_name_lambda_arn')\n            }\n        )\n\n    def test_can_update_kinesis_event_source(self):\n        function = create_function_resource('function_name')\n        kinesis_event_source = models.KinesisEventSource(\n            resource_name='function_name-kinesis-event-source',\n            stream='mystream',\n            batch_size=10,\n            starting_position='LATEST',\n            maximum_batching_window_in_seconds=60,\n            lambda_function=function\n        )\n        self.remote_state.declare_resource_exists(\n            kinesis_event_source,\n            stream='mystream',\n            kinesis_arn='arn:aws:kinesis:stream',\n            resource_type='kinesis_event',\n            lambda_arn='arn:lambda',\n            event_uuid='my-uuid',\n        )\n        plan = self.determine_plan(kinesis_event_source)\n        assert plan[5] == models.APICall(\n            method_name='update_lambda_event_source',\n            params={\n                'event_uuid': 'my-uuid',\n                'batch_size': 10,\n                'maximum_batching_window_in_seconds': 60,\n            }\n        )\n\n\nclass TestPlanDynamoDBSubscription(BasePlannerTests):\n    def test_can_plan_dynamodb_event_source(self):\n        function = create_function_resource('function_name')\n        event_source = models.DynamoDBEventSource(\n            resource_name='handler-dynamodb-event-source',\n            stream_arn='arn:stream', batch_size=100,\n            maximum_batching_window_in_seconds=0,\n            starting_position='LATEST', lambda_function=function)\n        plan = self.determine_plan(event_source)\n        assert plan[0] == models.APICall(\n            method_name='create_lambda_event_source',\n            params={\n                'event_source_arn': 'arn:stream',\n                'batch_size': 100,\n                'function_name': Variable('function_name_lambda_arn'),\n                'starting_position': 'LATEST',\n                'maximum_batching_window_in_seconds': 0,\n            },\n            output_var='handler-dynamodb-event-source_uuid',\n        )\n\n    def test_can_plan_dynamodb_event_source_update(self):\n        function = create_function_resource('function_name')\n        event_source = models.DynamoDBEventSource(\n            resource_name='handler-dynamodb-event-source',\n            stream_arn='arn:stream', batch_size=100,\n            maximum_batching_window_in_seconds=60,\n            starting_position='LATEST', lambda_function=function)\n        self.remote_state.declare_resource_exists(\n            event_source,\n            stream_arn='arn:stream',\n            resource_type='dynamodb_event',\n            lambda_arn='arn:lambda',\n            event_uuid='my-uuid',\n        )\n        plan = self.determine_plan(event_source)\n        assert plan[0] == models.APICall(\n            method_name='update_lambda_event_source',\n            params={\n                'event_uuid': 'my-uuid',\n                'batch_size': 100,\n                'maximum_batching_window_in_seconds': 60\n            },\n        )\n\n\nclass TestRemoteState(object):\n    def setup_method(self):\n        self.client = mock.Mock(spec=TypedAWSClient)\n        self.config = FakeConfig({'resources': []})\n        self.remote_state = RemoteState(\n            self.client, self.config.deployed_resources('dev'),\n        )\n\n    def create_rest_api_model(self):\n        rest_api = models.RestAPI(\n            resource_name='rest_api',\n            swagger_doc={'swagger': '2.0'},\n            minimum_compression='',\n            endpoint_type='EDGE',\n            api_gateway_stage='api',\n            xray=False,\n            lambda_function=None,\n        )\n        return rest_api\n\n    def create_api_mapping(self):\n        api_mapping = models.APIMapping(\n            resource_name='api_mapping',\n            mount_path='(none)',\n            api_gateway_stage='dev'\n        )\n        return api_mapping\n\n    def create_domain_name(self):\n        domain_name = models.DomainName(\n            protocol=models.APIType.HTTP,\n            resource_name='api_gateway_custom_domain',\n            domain_name='example.com',\n            tls_version=models.TLSVersion.TLS_1_0,\n            certificate_arn='certificate_arn',\n            api_mapping=self.create_api_mapping()\n        )\n        return domain_name\n\n    def create_websocket_api_model(self):\n        websocket_api = models.WebsocketAPI(\n            resource_name='websocket_api',\n            name='app-stage-websocket-api',\n            api_gateway_stage='api',\n            routes=[],\n            connect_function=None,\n            message_function=None,\n            disconnect_function=None,\n        )\n        return websocket_api\n\n    def test_role_exists(self):\n        self.client.get_role_arn_for_name.return_value = 'role:arn'\n        role = models.ManagedIAMRole('my_role',\n                                     role_name='app-dev', trust_policy={},\n                                     policy=None)\n        assert self.remote_state.resource_exists(role)\n        self.client.get_role_arn_for_name.assert_called_with('app-dev')\n\n    def test_role_does_not_exist(self):\n        client = self.client\n        client.get_role_arn_for_name.side_effect = ResourceDoesNotExistError()\n        role = models.ManagedIAMRole('my_role',\n                                     role_name='app-dev', trust_policy={},\n                                     policy=None)\n        assert not self.remote_state.resource_exists(role)\n        self.client.get_role_arn_for_name.assert_called_with('app-dev')\n\n    def test_lambda_layer_not_exists(self):\n        layer = models.LambdaLayer(\n            resource_name='layer',\n            layer_name='bar',\n            runtime='python2.7',\n            deployment_package=models.DeploymentPackage(\n                filename='foo')\n        )\n        assert not self.remote_state.resource_exists(layer)\n\n    def test_lambda_layer_exists(self):\n        layer = models.LambdaLayer(\n            resource_name='layer',\n            layer_name='bar',\n            runtime='python2.7',\n            deployment_package=models.DeploymentPackage(\n                filename='foo')\n        )\n        deployed_resources = {\n            'resources': [{\n                'name': 'layer',\n                'resource_type': 'lambda_layer',\n                'layer_version_arn': 'arn:layer:4'\n            }]\n        }\n        self.client.get_layer_version.return_value = {\n            'LayerVersionArn': 'arn:layer:4'}\n        remote_state = RemoteState(\n            self.client, DeployedResources(deployed_resources))\n        assert remote_state.resource_exists(layer)\n\n    def test_lambda_function_exists(self):\n        function = create_function_resource('function-name')\n        self.client.lambda_function_exists.return_value = True\n        assert self.remote_state.resource_exists(function)\n        self.client.lambda_function_exists.assert_called_with(\n            function.function_name)\n\n    def test_lambda_function_does_not_exist(self):\n        function = create_function_resource('function-name')\n        self.client.lambda_function_exists.return_value = False\n        assert not self.remote_state.resource_exists(function)\n        self.client.lambda_function_exists.assert_called_with(\n            function.function_name)\n\n    def test_api_gateway_domain_name_exists(self):\n        domain_name = self.create_domain_name()\n        self.client.domain_name_exists.return_value = True\n        assert self.remote_state.resource_exists(domain_name)\n\n    def test_websocket_domain_name_exists(self):\n        domain_name = self.create_domain_name()\n        domain_name.protocol = models.APIType.WEBSOCKET\n        domain_name.resource_name = 'websocket_api_custom_domain'\n        self.client.domain_name_exists_v2.return_value = True\n        assert self.remote_state.resource_exists(domain_name)\n\n    def test_none_api_mapping_exists(self):\n        api_mapping = self.create_api_mapping()\n        self.client.api_mapping_exists.return_value = True\n        assert self.remote_state.resource_exists(api_mapping, 'domain_name')\n\n    def test_path_api_mapping_exists_with_slash(self):\n        api_mapping = self.create_api_mapping()\n        api_mapping.mount_path = '/path'\n        self.client.api_mapping_exists.return_value = True\n        assert self.remote_state.resource_exists(api_mapping, 'domain_name')\n\n    def test_path_api_mapping_exists(self):\n        api_mapping = self.create_api_mapping()\n        api_mapping.mount_path = 'path'\n        self.client.api_mapping_exists.return_value = True\n        assert self.remote_state.resource_exists(api_mapping, 'domain_name')\n\n    def test_domain_name_does_not_exist(self):\n        domain_name = self.create_domain_name()\n        self.client.domain_name_exists.return_value = False\n        assert not self.remote_state.resource_exists(domain_name)\n\n        domain_name.protocol = models.APIType.WEBSOCKET\n        domain_name.resource_name = 'websocket_api_custom_domain'\n        self.client.domain_name_exists_v2.return_value = False\n        assert not self.remote_state.resource_exists(domain_name)\n\n    def test_exists_check_is_cached(self):\n        function = create_function_resource('function-name')\n        self.client.lambda_function_exists.return_value = True\n        assert self.remote_state.resource_exists(function)\n        # Now if we call this method repeatedly we should only invoke\n        # the underlying client method once.  Subsequent calls are cached.\n        assert self.remote_state.resource_exists(function)\n        assert self.remote_state.resource_exists(function)\n\n        assert self.client.lambda_function_exists.call_count == 1\n\n    def test_exists_check_is_cached_api_mapping(self):\n        api_mapping = models.APIMapping(\n            resource_name='api_mapping',\n            mount_path='(none)',\n            api_gateway_stage='dev'\n        )\n        self.client.api_mapping_exists.return_value = True\n        assert self.remote_state.resource_exists(api_mapping, 'domain_name')\n        assert self.remote_state.resource_exists(api_mapping, 'domain_name')\n        assert self.remote_state.resource_exists(api_mapping, 'domain_name')\n\n    def test_rest_api_exists_no_deploy(self, no_deployed_values):\n        rest_api = self.create_rest_api_model()\n        remote_state = RemoteState(\n            self.client, no_deployed_values)\n        assert not remote_state.resource_exists(rest_api)\n        assert not self.client.get_rest_api.called\n\n    def test_rest_api_exists_with_existing_deploy(self):\n        rest_api = self.create_rest_api_model()\n        deployed_resources = {\n            'resources': [{\n                'name': 'rest_api',\n                'resource_type': 'rest_api',\n                'rest_api_id': 'my_rest_api_id',\n            }]\n        }\n        self.client.get_rest_api.return_value = {'apiId': 'my_rest_api_id'}\n        remote_state = RemoteState(\n            self.client, DeployedResources(deployed_resources))\n        assert remote_state.resource_exists(rest_api)\n        self.client.get_rest_api.assert_called_with('my_rest_api_id')\n\n    def test_rest_api_not_exists_with_preexisting_deploy(self):\n        rest_api = self.create_rest_api_model()\n        deployed_resources = {\n            'resources': [{\n                'name': 'rest_api',\n                'resource_type': 'rest_api',\n                'rest_api_id': 'my_rest_api_id',\n            }]\n        }\n        self.client.get_rest_api.return_value = {}\n        remote_state = RemoteState(\n            self.client, DeployedResources(deployed_resources))\n        assert not remote_state.resource_exists(rest_api)\n        self.client.get_rest_api.assert_called_with('my_rest_api_id')\n\n    def test_websocket_api_exists_no_deploy(self, no_deployed_values):\n        rest_api = self.create_websocket_api_model()\n        remote_state = RemoteState(\n            self.client, no_deployed_values)\n        assert not remote_state.resource_exists(rest_api)\n        assert not self.client.websocket_api_exists.called\n\n    def test_websocket_api_exists_with_existing_deploy(self):\n        websocket_api = self.create_websocket_api_model()\n        deployed_resources = {\n            'resources': [{\n                'name': 'websocket_api',\n                'resource_type': 'websocket_api',\n                'websocket_api_id': 'my_websocket_api_id',\n            }]\n        }\n        self.client.websocket_api_exists.return_value = True\n        remote_state = RemoteState(\n            self.client, DeployedResources(deployed_resources))\n        assert remote_state.resource_exists(websocket_api)\n        self.client.websocket_api_exists.assert_called_with(\n            'my_websocket_api_id')\n\n    def test_websocket_api_not_exists_with_preexisting_deploy(self):\n        websocket_api = self.create_websocket_api_model()\n        deployed_resources = {\n            'resources': [{\n                'name': 'websocket_api',\n                'resource_type': 'websocket_api',\n                'websocket_api_id': 'my_websocket_api_id',\n            }]\n        }\n        self.client.websocket_api_exists.return_value = False\n        remote_state = RemoteState(\n            self.client, DeployedResources(deployed_resources))\n        assert not remote_state.resource_exists(websocket_api)\n        self.client.websocket_api_exists.assert_called_with(\n            'my_websocket_api_id')\n\n    def test_can_get_deployed_values(self):\n        remote_state = RemoteState(\n            self.client, DeployedResources({'resources': [\n                {'name': 'rest_api', 'rest_api_id': 'foo'}]})\n        )\n        rest_api = self.create_rest_api_model()\n        values = remote_state.resource_deployed_values(rest_api)\n        assert values == {'name': 'rest_api', 'rest_api_id': 'foo'}\n\n    def test_value_error_raised_on_no_deployed_values(self,\n                                                      no_deployed_values):\n        remote_state = RemoteState(self.client,\n                                   deployed_resources=no_deployed_values)\n        rest_api = self.create_rest_api_model()\n        with pytest.raises(ValueError):\n            remote_state.resource_deployed_values(rest_api)\n\n    def test_value_error_raised_for_unknown_resource_name(self):\n        remote_state = RemoteState(\n            self.client, DeployedResources({'resources': [\n                {'name': 'not_rest_api', 'rest_api_id': 'foo'}]})\n        )\n        rest_api = self.create_rest_api_model()\n        with pytest.raises(ValueError):\n            remote_state.resource_deployed_values(rest_api)\n\n    def test_dynamically_lookup_iam_role(self):\n        remote_state = RemoteState(\n            self.client, DeployedResources({'resources': [\n                {'name': 'rest_api', 'rest_api_id': 'foo'}]})\n        )\n        resource = models.ManagedIAMRole(\n            resource_name='default-role',\n            role_name='myrole',\n            trust_policy={'trust': 'policy'},\n            policy=models.AutoGenIAMPolicy(document={'iam': 'policy'}),\n        )\n        self.client.get_role_arn_for_name.return_value = 'my-role-arn'\n        values = remote_state.resource_deployed_values(resource)\n        assert values == {\n            'name': 'default-role',\n            'resource_type': 'iam_role',\n            'role_arn': 'my-role-arn',\n            'role_name': 'myrole'\n        }\n\n    def test_unknown_model_type_raises_error(self):\n\n        @dataclass\n        class Foo(models.ManagedModel):\n            resource_type = 'foo'\n\n        foo = Foo(resource_name='myfoo')\n        with pytest.raises(ValueError):\n            self.remote_state.resource_exists(foo)\n\n    @pytest.mark.parametrize(\n        'resource_topic,deployed_topic,is_current,expected_result', [\n            ('mytopic', 'mytopic', True, True),\n            ('mytopic-new', 'mytopic-old', False, False),\n        ]\n    )\n    def test_sns_subscription_exists(self, resource_topic, deployed_topic,\n                                     is_current, expected_result):\n        sns_subscription = models.SNSLambdaSubscription(\n            topic=resource_topic, resource_name='handler-sns-subscription',\n            lambda_function=None\n        )\n        deployed_resources = {\n            'resources': [{\n                'name': 'handler-sns-subscription',\n                'topic': deployed_topic,\n                'resource_type': 'sns_event',\n                'lambda_arn': 'arn:lambda',\n                'subscription_arn': 'arn:aws:subscribe',\n            }]\n        }\n        self.client.verify_sns_subscription_current.return_value = \\\n            is_current\n        remote_state = RemoteState(\n            self.client, DeployedResources(deployed_resources))\n        assert (\n            remote_state.resource_exists(sns_subscription) == expected_result\n        )\n        self.client.verify_sns_subscription_current.assert_called_with(\n            'arn:aws:subscribe',\n            topic_name=resource_topic,\n            function_arn='arn:lambda',\n        )\n\n    def test_sns_subscription_not_in_deployed_values(self):\n        sns_subscription = models.SNSLambdaSubscription(\n            topic='mytopic', resource_name='handler-sns-subscription',\n            lambda_function=None\n        )\n        deployed_resources = {'resources': []}\n        remote_state = RemoteState(\n            self.client, DeployedResources(deployed_resources))\n        assert not remote_state.resource_exists(sns_subscription)\n        assert not self.client.verify_sns_subscription_current.called\n\n    @pytest.mark.parametrize(\n        'new_queue,deployed_queue,expected_result', [\n            ('queue', 'queue', True),\n            ('new-queue', 'queue', False),\n            ('new-queue', None, False),\n        ]\n    )\n    def test_sqs_event_source_exists(self, new_queue, deployed_queue,\n                                     expected_result):\n        event_source = models.SQSEventSource(\n            resource_name='handler-sqs-event-source',\n            maximum_batching_window_in_seconds=0,\n            queue=new_queue, batch_size=100, lambda_function=None\n        )\n        if deployed_queue is not None:\n            deployed_resources = {\n                'resources': [{\n                    'queue': deployed_queue,\n                    'queue_arn': 'arn:aws:sqs:us-west-2:123:myqueue',\n                    'name': 'handler-sqs-event-source',\n                    'lambda_arn': 'arn:aws:lambda:handler',\n                    'event_uuid': 'event-uid-123',\n                    'resource_type': 'sqs_event'\n                }]\n            }\n        else:\n            deployed_resources = {'resources': []}\n        self.client.verify_event_source_current.return_value = \\\n            new_queue == deployed_queue\n        remote_state = RemoteState(\n            self.client, DeployedResources(deployed_resources),\n        )\n        assert remote_state.resource_exists(event_source) == expected_result\n        if deployed_queue is not None:\n            self.client.verify_event_source_current.assert_called_with(\n                event_uuid='event-uid-123',\n                resource_name=new_queue,\n                service_name='sqs',\n                function_arn='arn:aws:lambda:handler',\n            )\n\n    def test_kinesis_event_source_not_exists(self):\n        event_source = models.KinesisEventSource(\n            resource_name='handler-kinesis-event-source',\n            stream='mystream', batch_size=100, starting_position='LATEST',\n            maximum_batching_window_in_seconds=0,\n            lambda_function=None)\n        deployed_resources = {'resources': []}\n        remote_state = RemoteState(\n            self.client, DeployedResources(deployed_resources),\n        )\n        assert not remote_state.resource_exists(event_source)\n\n    def test_kinesis_event_source_exists(self):\n        event_source = models.KinesisEventSource(\n            resource_name='handler-kinesis-event-source',\n            stream='mystream', batch_size=100, starting_position='LATEST',\n            maximum_batching_window_in_seconds=0,\n            lambda_function=None)\n        deployed_resources = {\n            'resources': [{\n                'name': 'handler-kinesis-event-source',\n                'resource_type': 'kinesis_event',\n                'kinesis_arn': 'arn:aws:kinesis:...:stream/mystream',\n                'event_uuid': 'abcd',\n                'stream': 'mystream',\n                'lambda_arn': 'arn:aws:lambda:function:test-dev-index'\n            }]\n        }\n        remote_state = RemoteState(\n            self.client, DeployedResources(deployed_resources),\n        )\n        self.client.verify_event_source_current.return_value = True\n        assert remote_state.resource_exists(event_source)\n\n    def test_ddb_event_source_not_exists(self):\n        event_source = models.DynamoDBEventSource(\n            resource_name='handler-dynamodb-event-source',\n            stream_arn='arn:stream', batch_size=100,\n            maximum_batching_window_in_seconds=0,\n            starting_position='LATEST', lambda_function=None)\n        deployed_resources = {'resources': []}\n        remote_state = RemoteState(\n            self.client, DeployedResources(deployed_resources),\n        )\n        assert not remote_state.resource_exists(event_source)\n\n    def test_ddb_event_source_exists(self):\n        event_source = models.KinesisEventSource(\n            resource_name='handler-kinesis-event-source',\n            stream='mystream', batch_size=100, starting_position='LATEST',\n            maximum_batching_window_in_seconds=0,\n            lambda_function=None)\n        deployed_resources = {\n            'resources': [{\n                'name': 'handler-kinesis-event-source',\n                'resource_type': 'kinesis_event',\n                'stream_arn': 'arn:aws:kinesis:...:stream/mystream',\n                'event_uuid': 'abcd',\n                'stream': 'mystream',\n                'lambda_arn': 'arn:aws:lambda:function:test-dev-index'\n            }]\n        }\n        remote_state = RemoteState(\n            self.client, DeployedResources(deployed_resources),\n        )\n        self.client.verify_event_source_arn_current.return_value = True\n        assert remote_state.resource_exists(event_source)\n\n\nclass TestUnreferencedResourcePlanner(BasePlannerTests):\n    def setup_method(self):\n        super(TestUnreferencedResourcePlanner, self).setup_method()\n        self.sweeper = ResourceSweeper()\n\n    def execute(self, plan, config):\n        self.sweeper.execute(models.Plan(plan, messages={}), config)\n\n    @pytest.fixture\n    def function_resource(self):\n        return create_function_resource('myfunction')\n\n    def one_deployed_lambda_function(self, name='myfunction', arn='arn'):\n        return {\n            'resources': [{\n                'name': name,\n                'resource_type': 'lambda_function',\n                'lambda_arn': arn,\n            }]\n        }\n\n    def test_noop_when_all_resources_accounted_for(self, function_resource):\n        plan = [\n            models.RecordResource(\n                resource_type='lambda_function',\n                resource_name='myfunction',\n                name='foo',\n            )\n        ]\n        original_plan = plan[:]\n        deployed = self.one_deployed_lambda_function(name='myfunction')\n        config = FakeConfig(deployed)\n        self.execute(plan, config)\n        # We shouldn't add anything to the list.\n        assert plan == original_plan\n\n    def test_will_delete_unreferenced_resource(self):\n        plan = []\n        deployed = self.one_deployed_lambda_function()\n        config = FakeConfig(deployed)\n        self.execute(plan, config)\n        assert len(plan) == 1\n        assert plan[0].method_name == 'delete_function'\n        assert plan[0].params == {'function_name': 'arn'}\n\n    def test_will_delete_log_group(self):\n        plan = []\n        deployed = {\n            'resources': [{\n                'resource_type': 'log_group',\n                'name': 'my-log-group',\n                'log_group_name': '/aws/lambda/mygroup',\n            }],\n        }\n        config = FakeConfig(deployed)\n        self.execute(plan, config)\n        assert len(plan) == 1\n        assert plan[0].method_name == 'delete_retention_policy'\n        assert plan[0].params == {'log_group_name': '/aws/lambda/mygroup'}\n\n    def test_supports_multiple_unreferenced_and_unchanged(self):\n        first = create_function_resource('first')\n        second = create_function_resource('second')\n        third = create_function_resource('third')\n        plan = [\n            models.RecordResource(\n                resource_type='lambda_function',\n                resource_name=first.resource_name,\n                name='foo',\n            ),\n            models.RecordResource(\n                resource_type='asdf',\n                resource_name=second.resource_name,\n                name='foo',\n            )\n        ]\n        deployed = {\n            'resources': [{\n                'name': second.resource_name,\n                'resource_type': 'lambda_function',\n                'lambda_arn': 'second_arn',\n            }, {\n                'name': third.resource_name,\n                'resource_type': 'lambda_function',\n                'lambda_arn': 'third_arn',\n            }]\n        }\n        config = FakeConfig(deployed)\n        self.execute(plan, config)\n        assert len(plan) == 3\n        assert plan[2].method_name == 'delete_function'\n        assert plan[2].params == {'function_name': 'third_arn'}\n\n    def test_can_delete_iam_role(self):\n        plan = []\n        deployed = {\n            'resources': [{\n                'name': 'myrole',\n                'resource_type': 'iam_role',\n                'role_name': 'myrole',\n                'role_arn': 'arn:role/myrole',\n            }]\n        }\n        config = FakeConfig(deployed)\n        self.execute(plan, config)\n        assert len(plan) == 1\n        assert plan[0].method_name == 'delete_role'\n        assert plan[0].params == {'name': 'myrole'}\n\n    def test_correct_deletion_order_for_dependencies(self):\n        plan = []\n        deployed = {\n            # This is the order they were deployed.  While not\n            # strictly required for IAM Roles, we typically\n            # want to delete resources in the reverse order they\n            # were created.\n            'resources': [\n                {\n                    'name': 'myrole',\n                    'resource_type': 'iam_role',\n                    'role_name': 'myrole',\n                    'role_arn': 'arn:role/myrole',\n                },\n                {\n                    'name': 'myrole2',\n                    'resource_type': 'iam_role',\n                    'role_name': 'myrole2',\n                    'role_arn': 'arn:role/myrole2',\n                },\n                {\n                    'name': 'myfunction',\n                    'resource_type': 'lambda_function',\n                    'lambda_arn': 'my:arn',\n                }\n            ]\n        }\n        config = FakeConfig(deployed)\n        self.execute(plan, config)\n        assert len(plan) == 3\n        expected_api_calls = [p.method_name for p in plan]\n        assert expected_api_calls == ['delete_function',\n                                      'delete_role',\n                                      'delete_role']\n\n        expected_api_args = [p.params for p in plan]\n        assert expected_api_args == [\n            {'function_name': 'my:arn'},\n            {'name': 'myrole2'},\n            {'name': 'myrole'},\n        ]\n\n    def test_can_delete_lambda_layer(self):\n        plan = []\n        deployed = {\n            'resources': [{\n                'name': 'layer',\n                'resource_type': 'lambda_layer',\n                'layer_version_arn': 'arn'}]}\n        config = FakeConfig(deployed)\n        self.execute(plan, config)\n        assert plan == [\n            models.APICall(\n                method_name='delete_layer_version',\n                params={'layer_version_arn': 'arn'})]\n\n    def test_can_delete_scheduled_event(self):\n        plan = []\n        deployed = {\n            'resources': [{\n                'name': 'index-event',\n                'resource_type': 'cloudwatch_event',\n                'rule_name': 'app-dev-index-event',\n            }]\n        }\n        config = FakeConfig(deployed)\n        self.execute(plan, config)\n        assert plan == [\n            models.APICall(\n                method_name='delete_rule',\n                params={'rule_name': 'app-dev-index-event'},\n            )\n        ]\n\n    def test_can_delete_s3_event(self):\n        plan = []\n        deployed = {\n            'resources': [{\n                'name': 'test-s3-event',\n                'resource_type': 's3_event',\n                'bucket': 'mybucket',\n                'lambda_arn': 'lambda_arn',\n            }]\n        }\n        config = FakeConfig(deployed)\n        self.execute(plan, config)\n        assert plan == [\n            models.BuiltinFunction(\n                function_name='parse_arn', args=['lambda_arn'],\n                output_var='parsed_lambda_arn'),\n            models.JPSearch(expression='account_id',\n                            input_var='parsed_lambda_arn',\n                            output_var='account_id'),\n            models.APICall(\n                method_name='disconnect_s3_bucket_from_lambda',\n                params={'bucket': 'mybucket', 'function_arn': 'lambda_arn'},\n            ),\n            models.APICall(\n                method_name='remove_permission_for_s3_event',\n                params={'bucket': 'mybucket', 'function_arn': 'lambda_arn',\n                        'account_id': Variable(\"account_id\")}\n            )\n        ]\n\n    def test_can_delete_rest_api(self):\n        plan = []\n        deployed = {\n            'resources': [{\n                'name': 'rest_api',\n                'rest_api_id': 'my_rest_api_id',\n                'resource_type': 'rest_api',\n            }]\n        }\n        config = FakeConfig(deployed)\n        self.execute(plan, config)\n        assert plan == [\n            models.APICall(\n                method_name='delete_rest_api',\n                params={'rest_api_id': 'my_rest_api_id'},\n            )\n        ]\n\n    def test_can_delete_websocket_api(self):\n        plan = []\n        deployed = {\n            'resources': [{\n                'name': 'websocket_api',\n                'websocket_api_id': 'my_websocket_api_id',\n                'resource_type': 'websocket_api',\n            }]\n        }\n        config = FakeConfig(deployed)\n        self.execute(plan, config)\n        assert plan == [\n            models.APICall(\n                method_name='delete_websocket_api',\n                params={'api_id': 'my_websocket_api_id'},\n            )\n        ]\n\n    def test_can_handle_when_resource_changes_values(self):\n        plan = self.determine_plan(\n            models.S3BucketNotification(\n                resource_name='test-s3-event',\n                bucket='NEWBUCKET',\n                events=['s3:ObjectCreated:*'],\n                prefix=None,\n                suffix=None,\n                lambda_function=create_function_resource('function_name'),\n            )\n        )\n        deployed = {\n            'resources': [{\n                'name': 'test-s3-event',\n                'resource_type': 's3_event',\n                'bucket': 'OLDBUCKET',\n                'lambda_arn': 'lambda_arn',\n            }]\n        }\n        config = FakeConfig(deployed)\n        self.execute(plan, config)\n        assert plan[-2:] == [\n            models.APICall(\n                method_name='disconnect_s3_bucket_from_lambda',\n                params={'bucket': 'OLDBUCKET', 'function_arn': 'lambda_arn'},\n            ),\n            models.APICall(\n                method_name='remove_permission_for_s3_event',\n                params={'bucket': 'OLDBUCKET', 'function_arn': 'lambda_arn',\n                        'account_id': Variable('account_id')},\n            ),\n        ]\n\n    def test_no_sweeping_when_resource_value_unchanged(self):\n        plan = self.determine_plan(\n            models.S3BucketNotification(\n                resource_name='test-s3-event',\n                bucket='EXISTING-BUCKET',\n                events=['s3:ObjectCreated:*'],\n                prefix=None,\n                suffix=None,\n                lambda_function=create_function_resource('function_name'),\n            )\n        )\n        deployed = {\n            'resources': [{\n                'name': 'test-s3-event',\n                'resource_type': 's3_event',\n                'bucket': 'EXISTING-BUCKET',\n                'lambda_arn': 'lambda_arn',\n            }]\n        }\n        config = FakeConfig(deployed)\n        original_plan = plan[:]\n        self.execute(plan, config)\n        assert plan == original_plan\n\n    def test_can_delete_sns_subscription(self):\n        plan = []\n        deployed = {\n            'resources': [{\n                'name': 'handler-sns-subscription',\n                'topic': 'mytopic',\n                'topic_arn': 'arn:mytopic',\n                'resource_type': 'sns_event',\n                'lambda_arn': 'arn:lambda',\n                'subscription_arn': 'arn:aws:subscribe',\n            }]\n        }\n        config = FakeConfig(deployed)\n        self.execute(plan, config)\n        assert plan == [\n            models.APICall(\n                method_name='unsubscribe_from_topic',\n                params={'subscription_arn': 'arn:aws:subscribe'},\n            ),\n            models.APICall(\n                method_name='remove_permission_for_sns_topic',\n                params={\n                    'topic_arn': 'arn:mytopic',\n                    'function_arn': 'arn:lambda',\n                },\n            )\n        ]\n\n    def test_no_deletion_when_no_changes(self):\n        plan = self.determine_plan(\n            models.SNSLambdaSubscription(\n                resource_name='handler-sns-subscription',\n                topic='mytopic',\n                lambda_function=create_function_resource('function_name')\n            )\n        )\n        deployed = {\n            'resources': [{\n                'name': 'handler-sns-subscription',\n                'topic': 'mytopic',\n                'resource_type': 'sns_event',\n                'lambda_arn': 'arn:lambda',\n                'subscription_arn': 'arn:aws:subscribe',\n            }]\n        }\n        config = FakeConfig(deployed)\n        original_plan = plan[:]\n        self.execute(plan, config)\n        # We shouldn't have added anything to the plan.\n        assert plan == original_plan\n\n    def test_handles_when_topic_name_change(self):\n        # So let's say we subscribed to a topic 'old-topic'\n        # and deployed our app:\n        deployed = {\n            'resources': [{\n                'name': 'handler-sns-subscription',\n                'topic': 'old-topic',\n                'topic_arn': 'arn:old-topic',\n                'resource_type': 'sns_event',\n                'lambda_arn': 'arn:lambda',\n                'subscription_arn': 'arn:aws:subscribe',\n            }]\n        }\n        # Now we update our app and change the topic param\n        # to 'new-topic'\n        plan = self.determine_plan(\n            models.SNSLambdaSubscription(\n                resource_name='handler-sns-subscription',\n                topic='new-topic',\n                lambda_function=create_function_resource('function_name')\n            )\n        )\n        config = FakeConfig(deployed)\n        self.execute(plan, config)\n        # Then we should unsubscribe from the old-topic because it's\n        # no longer referenced in our app.\n        assert plan[-2:] == [\n            models.APICall(\n                method_name='unsubscribe_from_topic',\n                params={'subscription_arn': 'arn:aws:subscribe'},\n            ),\n            models.APICall(\n                method_name='remove_permission_for_sns_topic',\n                params={\n                    'topic_arn': 'arn:old-topic',\n                    'function_arn': 'arn:lambda',\n                },\n            ),\n        ]\n\n    def test_no_sqs_deletion_when_no_changes(self):\n        plan = self.determine_plan(\n            models.SQSEventSource(\n                resource_name='handler-sqs-event-source',\n                queue='my-queue',\n                batch_size=10,\n                lambda_function=create_function_resource('function_name'),\n                maximum_batching_window_in_seconds=0\n            )\n        )\n        deployed = {\n            'resources': [{\n                'name': 'handler-sqs-event-source',\n                'queue': 'my-queue',\n                'resource_type': 'sqs_event',\n                'lambda_arn': 'arn:lambda',\n                'event_uuid': 'event-uuid',\n            }]\n        }\n        config = FakeConfig(deployed)\n        original_plan = plan[:]\n        self.execute(plan, config)\n        assert plan == original_plan\n\n    def test_can_delete_sqs_subscription(self):\n        plan = []\n        deployed = {\n            'resources': [{\n                'name': 'handler-sqs-event-source',\n                'queue': 'my-queue',\n                'resource_type': 'sqs_event',\n                'lambda_arn': 'arn:lambda',\n                'event_uuid': 'event-uuid',\n            }]\n        }\n        config = FakeConfig(deployed)\n        self.execute(plan, config)\n        assert plan == [\n            models.APICall(\n                method_name='remove_lambda_event_source',\n                params={'event_uuid': 'event-uuid'},\n            ),\n        ]\n\n    def test_handles_when_queue_name_change(self):\n        deployed = {\n            'resources': [{\n                'name': 'handler-sqs-event-source',\n                'queue': 'my-queue',\n                'resource_type': 'sqs_event',\n                'lambda_arn': 'arn:lambda',\n                'event_uuid': 'event-uuid',\n            }]\n        }\n        plan = self.determine_plan(\n            models.SQSEventSource(\n                resource_name='handler-sqs-event-source',\n                queue='my-new-queue',\n                batch_size=10,\n                lambda_function=create_function_resource('function_name'),\n                maximum_batching_window_in_seconds=0\n            )\n        )\n        config = FakeConfig(deployed)\n        self.execute(plan, config)\n        assert plan[-1:] == [\n            models.APICall(\n                method_name='remove_lambda_event_source',\n                params={'event_uuid': 'event-uuid'},\n            ),\n        ]\n\n    def test_can_delete_domain_name(self):\n        deployed = {\n            'resources': [{\n                'name': 'api_gateway_custom_domain',\n                'resource_type': 'domain_name',\n                'domain_name': 'example.com'\n            }]\n        }\n        plan = []\n        config = FakeConfig(deployed)\n        self.execute(plan, config)\n        assert plan[-1:] == [\n            models.APICall(\n                method_name='delete_domain_name',\n                params={'domain_name': 'example.com'},\n            ),\n        ]\n\n    def test_can_handle_domain_name_without_api_mapping(self):\n        deployed = {\n            'resources': [{\n                'name': 'api_gateway_custom_domain',\n                'resource_type': 'domain_name',\n                'domain_name': 'example.com',\n            }]\n        }\n\n        function = create_function_resource('function_name')\n        domain_name = create_http_domain_name()\n        rest_api = models.RestAPI(\n            resource_name='rest_api',\n            swagger_doc={'swagger': '2.0'},\n            endpoint_type='EDGE',\n            minimum_compression='100',\n            api_gateway_stage='api',\n            lambda_function=function,\n            domain_name=domain_name\n        )\n        plan = self.determine_plan(\n            rest_api\n        )\n        config = FakeConfig(deployed)\n        self.execute(plan, config)\n        assert plan[-1] == models.RecordResourceVariable(\n            resource_type='domain_name',\n            resource_name='api_gateway_custom_domain',\n            name='api_mapping',\n            variable_name='rest_api_mapping'\n        )\n\n    def test_can_delete_api_mapping(self):\n        deployed = {\n            'resources': [{\n                'name': 'api_gateway_custom_domain',\n                'resource_type': 'domain_name',\n                'domain_name': 'example.com',\n                'api_mapping':  [\n                    {'key': '/path_key'}\n                ]\n            }]\n        }\n\n        domain_name = create_http_domain_name()\n        plan = [\n            models.RecordResourceVariable(\n                resource_type='domain_name',\n                resource_name=domain_name.resource_name,\n                name='api_mapping',\n                variable_name='rest_api_mapping'\n            )\n        ]\n        config = FakeConfig(deployed)\n        self.execute(plan, config)\n        assert self.sweeper.plan.instructions[0] == models.APICall(\n            method_name='delete_api_mapping',\n            params={'domain_name': 'example.com',\n                    'path_key': 'path_key'},\n            output_var=None\n        )\n\n    def test_can_delete_api_mapping_none(self):\n        deployed = {\n            'resources': [{\n                'name': 'api_gateway_custom_domain',\n                'resource_type': 'domain_name',\n                'domain_name': 'example.com',\n                'api_mapping':  [\n                    {'key': '/'}\n                ]\n            }]\n        }\n        domain_name = create_http_domain_name()\n        plan = [\n            models.RecordResourceVariable(\n                resource_type='domain_name',\n                resource_name=domain_name.resource_name,\n                name='api_mapping',\n                variable_name='rest_api_mapping'\n            )\n        ]\n        config = FakeConfig(deployed)\n        self.execute(plan, config)\n        assert self.sweeper.plan.instructions[0] == models.APICall(\n            method_name='delete_api_mapping',\n            params={'domain_name': 'example.com',\n                    'path_key': '(none)'},\n            output_var=None\n        )\n\n    def test_raise_error_not_existed_resource_delete(self):\n        deployed = {\n            'resources': [{\n                'name': 'name',\n                'resource_type': 'not_existed',\n            }]\n        }\n        config = FakeConfig(deployed)\n        with pytest.raises(RuntimeError):\n            self.execute([], config)\n\n    def test_update_plan_with_insert_without_message(self):\n        instructions = (\n            models.APICall(\n                method_name='unsubscribe_from_topic',\n                params={'subscription_arn': 'subscription_arn'},\n            ),\n            models.APICall(\n                method_name='remove_permission_for_sns_topic',\n                params={\n                    'topic_arn': 'topic_arn',\n                    'function_arn': 'lambda_arn',\n                },\n            ),\n        )  # type: Tuple[models.Instruction]\n        self.sweeper._update_plan(instructions, insert=True)\n        assert len(self.sweeper.plan.instructions) == 2\n\n\nclass TestKeyVariable(object):\n    def test_key_variable_str(self):\n        key_var = KeyDataVariable('name', 'key')\n        assert str(key_var) == 'KeyDataVariable(\"name\", \"key\")'\n\n    def test_key_variables_equal(self):\n        key_var = KeyDataVariable('name', 'key')\n        key_var_1 = KeyDataVariable('name', 'key_1')\n        assert not key_var == key_var_1\n\n        key_var_2 = KeyDataVariable('name', 'key')\n        assert key_var == key_var_2\n\n\nclass TestPlanLogGroup(BasePlannerTests):\n    def test_can_create_log_group(self):\n        self.remote_state.declare_no_resources_exists()\n        resource = models.LogGroup(\n            resource_name='default-log-group',\n            log_group_name='/aws/lambda/func-name',\n            retention_in_days=14,\n        )\n        plan = self.determine_plan(resource)\n        assert plan == [\n            models.APICall(\n                method_name='create_log_group',\n                params={'log_group_name': '/aws/lambda/func-name'}\n            ),\n            models.APICall(\n                method_name='put_retention_policy',\n                params={'name': '/aws/lambda/func-name',\n                        'retention_in_days': 14},\n            ),\n            models.RecordResourceValue(\n                resource_type='log_group',\n                resource_name='default-log-group',\n                name='log_group_name',\n                value='/aws/lambda/func-name'),\n        ]\n\n    def test_can_update_log_group(self):\n        resource = models.LogGroup(\n            resource_name='default-log-group',\n            log_group_name='/aws/lambda/func-name',\n            retention_in_days=14,\n        )\n        self.remote_state.declare_resource_exists(resource)\n        plan = self.determine_plan(resource)\n        assert plan == [\n            models.APICall(\n                method_name='put_retention_policy',\n                params={'name': '/aws/lambda/func-name',\n                        'retention_in_days': 14},\n            ),\n            models.RecordResourceValue(\n                resource_type='log_group',\n                resource_name='default-log-group',\n                name='log_group_name',\n                value='/aws/lambda/func-name'),\n        ]\n"
  },
  {
    "path": "tests/unit/deploy/test_swagger.py",
    "content": "from unittest import mock\n\nfrom chalice.deploy.swagger import (\n    SwaggerGenerator, CFNSwaggerGenerator, TerraformSwaggerGenerator)\nfrom chalice import CORSConfig\nfrom chalice.app import CustomAuthorizer, CognitoUserPoolAuthorizer\nfrom chalice.app import IAMAuthorizer, Chalice\nfrom chalice.deploy.models import RestAPI, IAMPolicy\nfrom pytest import fixture\n\n\n@fixture\ndef swagger_gen():\n    return SwaggerGenerator(\n        region='us-west-2',\n        deployed_resources={\n            'api_handler_arn': 'arn:aws:lambda:mars-west-1:123456789'\n                               ':function:lambda_arn'\n        })\n\n\ndef test_can_add_binary_media_types(swagger_gen):\n    app = Chalice('test-binary')\n    doc = swagger_gen.generate_swagger(app)\n    media_types = doc.get('x-amazon-apigateway-binary-media-types')\n    assert sorted(media_types) == sorted(app.api.binary_types)\n\n\ndef test_can_produce_swagger_top_level_keys(sample_app, swagger_gen):\n    swagger_doc = swagger_gen.generate_swagger(sample_app)\n    assert swagger_doc['swagger'] == '2.0'\n    assert swagger_doc['info']['title'] == 'sample'\n    assert swagger_doc['schemes'] == ['https']\n    assert '/' in swagger_doc['paths'], swagger_doc['paths']\n    index_config = swagger_doc['paths']['/']\n    assert 'get' in index_config\n\n\ndef test_can_produce_doc_for_method(sample_app, swagger_gen):\n    doc = swagger_gen.generate_swagger(sample_app)\n    single_method = doc['paths']['/']['get']\n    assert single_method['consumes'] == ['application/json']\n    assert single_method['produces'] == ['application/json']\n    # 'responses' is validated in a separate test,\n    # it's all boilerplate anyways.\n    # Same for x-amazon-apigateway-integration.\n\n\ndef test_can_produce_doc_for_no_docstring(sample_app, swagger_gen):\n    @sample_app.route('/method')\n    def method():\n        pass\n\n    doc = swagger_gen.generate_swagger(sample_app)\n    view_config = doc['paths']['/method']['get']\n    assert 'summary' not in view_config\n    assert 'description' not in view_config\n\n\ndef test_can_produce_doc_for_single_docstring(sample_app, swagger_gen):\n    @sample_app.route('/method1')\n    def method1():\n        \"\"\"Single line method summary\"\"\"\n        pass\n\n    @sample_app.route('/method2')\n    def method2():\n        '''Single line method summary'''\n        pass\n\n    @sample_app.route('/method3')\n    def method3():\n        \"\"\"\n        Single line method summary\n        \"\"\"\n        pass\n\n    doc = swagger_gen.generate_swagger(sample_app)\n    for method in [1, 2, 3]:\n        view_config = doc['paths']['/method' + str(method)]['get']\n        assert 'summary' in view_config\n        assert 'description' not in view_config\n        assert view_config['summary'] == 'Single line method summary'\n\n\ndef test_can_produce_doc_for_multi_docstring(sample_app, swagger_gen):\n    @sample_app.route('/method1')\n    def method1():\n        \"\"\"Multiline method summary\n\n        And here is a more detailed description that can span multiple\n        lines. It can also handle indenting for things like method\n        arguments like so:\n            param1 - description\n            param2 - description\n        \"\"\"\n        pass\n\n    @sample_app.route('/method2')\n    def method2():\n        \"\"\"Multiline method summary\n\n            And here is a more detailed description that can span multiple\n            lines. It can also handle indenting for things like method\n            arguments like so:\n                param1 - description\n                param2 - description\n        \"\"\"\n        pass\n\n    @sample_app.route('/method3')\n    def method3():\n        \"\"\"Multiline method summary\n        And here is a more detailed description that can span multiple\n        lines. It can also handle indenting for things like method\n        arguments like so:\n            param1 - description\n            param2 - description\n\n\n        \"\"\"\n        pass\n\n    doc = swagger_gen.generate_swagger(sample_app)\n    for method in [1, 2, 3]:\n        view_config = doc['paths']['/method' + str(method)]['get']\n        assert 'summary' in view_config\n        assert 'description' in view_config\n        assert view_config['summary'] == 'Multiline method summary'\n        assert view_config['description'] == (\n            'And here is a more detailed description that can span multiple\\n'\n            'lines. It can also handle indenting for things like method\\n'\n            'arguments like so:\\n'\n            '    param1 - description\\n'\n            '    param2 - description'\n        )\n\n\ndef test_apigateway_integration_generation(sample_app, swagger_gen):\n    doc = swagger_gen.generate_swagger(sample_app)\n    single_method = doc['paths']['/']['get']\n    apig_integ = single_method['x-amazon-apigateway-integration']\n    assert apig_integ['passthroughBehavior'] == 'when_no_match'\n    assert apig_integ['httpMethod'] == 'POST'\n    assert apig_integ['type'] == 'aws_proxy'\n    assert apig_integ['uri'] == (\n        \"arn:aws:apigateway:us-west-2:lambda:path\"\n        \"/2015-03-31/functions/\"\n        \"arn:aws:lambda:mars-west-1:123456789:function:lambda_arn/invocations\"\n    )\n    assert 'responses' in apig_integ\n    responses = apig_integ['responses']\n    assert responses['default'] == {'statusCode': '200'}\n\n\ndef test_can_add_url_captures_to_params(sample_app, swagger_gen):\n    @sample_app.route('/path/{capture}')\n    def foo(name):\n        return {}\n\n    doc = swagger_gen.generate_swagger(sample_app)\n    single_method = doc['paths']['/path/{capture}']['get']\n    assert 'parameters' in single_method\n    assert single_method['parameters'] == [\n        {'name': \"capture\", \"in\": \"path\", \"required\": True, \"type\": \"string\"}\n    ]\n\n\ndef test_can_add_multiple_http_methods(sample_app, swagger_gen):\n    @sample_app.route('/multimethod', methods=['GET', 'POST'])\n    def multiple_methods():\n        pass\n\n    doc = swagger_gen.generate_swagger(sample_app)\n    view_config = doc['paths']['/multimethod']\n    assert 'get' in view_config\n    assert 'post' in view_config\n    assert view_config['get'] == view_config['post']\n\n\ndef test_can_use_same_route_with_diff_http_methods(sample_app, swagger_gen):\n    @sample_app.route('/multimethod', methods=['GET'])\n    def multiple_methods_get():\n        pass\n\n    @sample_app.route('/multimethod', methods=['POST'])\n    def multiple_methods_post():\n        pass\n\n    doc = swagger_gen.generate_swagger(sample_app)\n    view_config = doc['paths']['/multimethod']\n    assert 'get' in view_config\n    assert 'post' in view_config\n    assert view_config['get'] == view_config['post']\n\n\nclass TestPreflightCORS(object):\n    def get_access_control_methods(self, view_config):\n        return view_config['options'][\n            'x-amazon-apigateway-integration']['responses']['default'][\n                'responseParameters'][\n                    'method.response.header.Access-Control-Allow-Methods']\n\n    def test_can_add_preflight_cors(self, sample_app, swagger_gen):\n        @sample_app.route('/cors', methods=['GET', 'POST'], cors=CORSConfig(\n            allow_origin='http://foo.com',\n            allow_headers=['X-ZZ-Top', 'X-Special-Header'],\n            expose_headers=['X-Exposed', 'X-Special'],\n            max_age=600,\n            allow_credentials=True))\n        def cors_request():\n            pass\n\n        doc = swagger_gen.generate_swagger(sample_app)\n        view_config = doc['paths']['/cors']\n        # We should add an OPTIONS preflight request automatically.\n        assert 'options' in view_config, (\n            'Preflight OPTIONS method not added to CORS view')\n        options = view_config['options']\n        expected_response_params = {\n            'method.response.header.Access-Control-Allow-Methods': mock.ANY,\n            'method.response.header.Access-Control-Allow-Headers': (\n                \"'Authorization,Content-Type,X-Amz-Date,X-Amz-Security-Token,\"\n                \"X-Api-Key,X-Special-Header,X-ZZ-Top'\"),\n            'method.response.header.Access-Control-Allow-Origin': (\n                \"'http://foo.com'\"),\n            'method.response.header.Access-Control-Expose-Headers': (\n                \"'X-Exposed,X-Special'\"),\n            'method.response.header.Access-Control-Max-Age': (\n                \"'600'\"),\n            'method.response.header.Access-Control-Allow-Credentials': (\n                \"'true'\"),\n\n        }\n        assert options == {\n            'consumes': ['application/json'],\n            'produces': ['application/json'],\n            'responses': {\n                '200': {\n                    'description': '200 response',\n                    'schema': {\n                        '$ref': '#/definitions/Empty'\n                    },\n                    'headers': {\n                        'Access-Control-Allow-Origin': {'type': 'string'},\n                        'Access-Control-Allow-Methods': {'type': 'string'},\n                        'Access-Control-Allow-Headers': {'type': 'string'},\n                        'Access-Control-Expose-Headers': {'type': 'string'},\n                        'Access-Control-Max-Age': {'type': 'string'},\n                        'Access-Control-Allow-Credentials': {'type': 'string'},\n                    }\n                }\n            },\n            'x-amazon-apigateway-integration': {\n                'responses': {\n                    'default': {\n                        'statusCode': '200',\n                        'responseParameters': expected_response_params,\n                    }\n                },\n                'requestTemplates': {\n                    'application/json': '{\"statusCode\": 200}'\n                },\n                'passthroughBehavior': 'when_no_match',\n                'type': 'mock',\n                'contentHandling': 'CONVERT_TO_TEXT'\n            },\n        }\n\n        allow_methods = self.get_access_control_methods(view_config)\n        # Typically the header will follow the form of:\n        # \"METHOD,METHOD,...OPTIONS\"\n        # The individual assertions is needed because there is no guarantee\n        # on the order of these methods in the string because the order is\n        # derived from iterating through a dictionary, which is not ordered\n        # in python 2.7. So instead assert the correct methods are present in\n        # the string.\n        assert 'GET' in allow_methods\n        assert 'POST' in allow_methods\n        assert 'OPTIONS' in allow_methods\n\n    def test_can_add_preflight_custom_cors(self, sample_app, swagger_gen):\n        @sample_app.route('/cors', methods=['GET', 'POST'], cors=True)\n        def cors_request():\n            pass\n\n        doc = swagger_gen.generate_swagger(sample_app)\n        view_config = doc['paths']['/cors']\n        # We should add an OPTIONS preflight request automatically.\n        assert 'options' in view_config, (\n            'Preflight OPTIONS method not added to CORS view')\n        options = view_config['options']\n        expected_response_params = {\n            'method.response.header.Access-Control-Allow-Methods': mock.ANY,\n            'method.response.header.Access-Control-Allow-Headers': (\n                \"'Authorization,Content-Type,X-Amz-Date,X-Amz-Security-Token,\"\n                \"X-Api-Key'\"),\n            'method.response.header.Access-Control-Allow-Origin': \"'*'\",\n        }\n        assert options == {\n            'consumes': ['application/json'],\n            'produces': ['application/json'],\n            'responses': {\n                '200': {\n                    'description': '200 response',\n                    'schema': {\n                        '$ref': '#/definitions/Empty'\n                    },\n                    'headers': {\n                        'Access-Control-Allow-Origin': {'type': 'string'},\n                        'Access-Control-Allow-Methods': {'type': 'string'},\n                        'Access-Control-Allow-Headers': {'type': 'string'},\n                    }\n                }\n            },\n            'x-amazon-apigateway-integration': {\n                'responses': {\n                    'default': {\n                        'statusCode': '200',\n                        'responseParameters': expected_response_params,\n                    }\n                },\n                'requestTemplates': {\n                    'application/json': '{\"statusCode\": 200}'\n                },\n                'passthroughBehavior': 'when_no_match',\n                'type': 'mock',\n                'contentHandling': 'CONVERT_TO_TEXT'\n            },\n        }\n\n        allow_methods = self.get_access_control_methods(view_config)\n        # Typically the header will follow the form of:\n        # \"METHOD,METHOD,...OPTIONS\"\n        # The individual assertions is needed because there is no guarantee\n        # on the order of these methods in the string because the order is\n        # derived from iterating through a dictionary, which is not ordered\n        # in python 2.7. So instead assert the correct methods are present in\n        # the string.\n        assert 'GET' in allow_methods\n        assert 'POST' in allow_methods\n        assert 'OPTIONS' in allow_methods\n\n    def test_can_add_preflight_cors_for_shared_routes(\n            self, sample_app, swagger_gen):\n\n        @sample_app.route('/cors', methods=['GET'], cors=True)\n        def cors_request():\n            pass\n\n        @sample_app.route('/cors', methods=['PUT'])\n        def non_cors_request():\n            pass\n\n        doc = swagger_gen.generate_swagger(sample_app)\n        view_config = doc['paths']['/cors']\n        # We should add an OPTIONS preflight request automatically.\n        assert 'options' in view_config, (\n            'Preflight OPTIONS method not added to CORS view')\n        allow_methods = self.get_access_control_methods(view_config)\n        # PUT should not be included in allowed methods as it was not enabled\n        # for CORS.\n        assert allow_methods == \"'GET,OPTIONS'\"\n\n\ndef test_can_add_api_key(sample_app, swagger_gen):\n    @sample_app.route('/api-key-required', api_key_required=True)\n    def foo(name):\n        return {}\n    doc = swagger_gen.generate_swagger(sample_app)\n    single_method = doc['paths']['/api-key-required']['get']\n    assert 'security' in single_method\n    assert single_method['security'] == [{\n        'api_key': []\n    }]\n    # Also need to add in the api_key definition in the top level\n    # security definitions.\n    assert 'securityDefinitions' in doc\n    assert 'api_key' in doc['securityDefinitions']\n    assert doc['securityDefinitions']['api_key'] == {\n        'type': 'apiKey',\n        'name': 'x-api-key',\n        'in': 'header'\n    }\n\n\ndef test_can_use_authorizer_object(sample_app, swagger_gen):\n    authorizer = CustomAuthorizer(\n        'MyAuth', authorizer_uri='auth-uri', header='Authorization'\n    )\n\n    @sample_app.route('/auth', authorizer=authorizer)\n    def auth():\n        return {'foo': 'bar'}\n\n    doc = swagger_gen.generate_swagger(sample_app)\n    single_method = doc['paths']['/auth']['get']\n    assert single_method.get('security') == [{'MyAuth': []}]\n    security_definitions = doc['securityDefinitions']\n    assert 'MyAuth' in security_definitions\n    my_auth = security_definitions['MyAuth']\n    # authorizerCredentials should not be in this dict because it's None.\n    assert my_auth['x-amazon-apigateway-authorizer'] == {\n        'authorizerUri': 'auth-uri',\n        'type': 'token',\n        'authorizerResultTtlInSeconds': 300,\n    }\n\n\ndef test_can_use_authorizer_object_with_role_arn(sample_app, swagger_gen):\n    authorizer = CustomAuthorizer(\n        'MyAuth', authorizer_uri='auth-uri', header='Authorization',\n        invoke_role_arn='role-arn')\n\n    @sample_app.route('/auth', authorizer=authorizer)\n    def auth():\n        return {'foo': 'bar'}\n\n    doc = swagger_gen.generate_swagger(sample_app)\n    single_method = doc['paths']['/auth']['get']\n    assert single_method.get('security') == [{'MyAuth': []}]\n    security_definitions = doc['securityDefinitions']\n    assert 'MyAuth' in security_definitions\n    assert security_definitions['MyAuth'] == {\n        'type': 'apiKey',\n        'name': 'Authorization',\n        'in': 'header',\n        'x-amazon-apigateway-authtype': 'custom',\n        'x-amazon-apigateway-authorizer': {\n            'authorizerUri': 'auth-uri',\n            'type': 'token',\n            'authorizerResultTtlInSeconds': 300,\n            'authorizerCredentials': 'role-arn'\n        }\n    }\n\n\ndef test_can_use_authorizer_object_scopes(sample_app, swagger_gen):\n    authorizer = CustomAuthorizer(\n        'MyAuth', authorizer_uri='auth-uri', header='Authorization',\n        invoke_role_arn='role-arn', scopes=[\"write:test\", \"read:test\"])\n\n    @sample_app.route('/auth', authorizer=authorizer)\n    def auth():\n        return {'foo': 'bar'}\n\n    doc = swagger_gen.generate_swagger(sample_app)\n    single_method = doc['paths']['/auth']['get']\n    assert single_method.get('security') == [{\n        'MyAuth': [\"write:test\", \"read:test\"]\n    }]\n    security_definitions = doc['securityDefinitions']\n    assert 'MyAuth' in security_definitions\n    assert security_definitions['MyAuth'] == {\n        'type': 'apiKey',\n        'name': 'Authorization',\n        'in': 'header',\n        'x-amazon-apigateway-authtype': 'custom',\n        'x-amazon-apigateway-authorizer': {\n            'authorizerUri': 'auth-uri',\n            'type': 'token',\n            'authorizerResultTtlInSeconds': 300,\n            'authorizerCredentials': 'role-arn'\n        }\n    }\n\n\ndef test_can_use_authorizer_object_with_scopes(sample_app, swagger_gen):\n    authorizer = CustomAuthorizer(\n        'MyAuth', authorizer_uri='auth-uri', header='Authorization',\n        invoke_role_arn='role-arn')\n\n    @sample_app.route(\n        '/auth',\n        authorizer=authorizer.with_scopes([\"write:test\", \"read:test\"])\n    )\n    def auth():\n        return {'foo': 'bar'}\n\n    doc = swagger_gen.generate_swagger(sample_app)\n    single_method = doc['paths']['/auth']['get']\n    assert single_method.get('security') == [{\n        'MyAuth': [\"write:test\", \"read:test\"]\n    }]\n    security_definitions = doc['securityDefinitions']\n    assert 'MyAuth' in security_definitions\n    assert security_definitions['MyAuth'] == {\n        'type': 'apiKey',\n        'name': 'Authorization',\n        'in': 'header',\n        'x-amazon-apigateway-authtype': 'custom',\n        'x-amazon-apigateway-authorizer': {\n            'authorizerUri': 'auth-uri',\n            'type': 'token',\n            'authorizerResultTtlInSeconds': 300,\n            'authorizerCredentials': 'role-arn'\n        }\n    }\n\n\ndef test_can_use_api_key_and_authorizers(sample_app, swagger_gen):\n    authorizer = CustomAuthorizer(\n        'MyAuth', authorizer_uri='auth-uri', header='Authorization')\n\n    @sample_app.route('/auth', authorizer=authorizer, api_key_required=True)\n    def auth():\n        return {'foo': 'bar'}\n\n    doc = swagger_gen.generate_swagger(sample_app)\n    single_method = doc['paths']['/auth']['get']\n    assert single_method.get('security') == [\n        {'api_key': []},\n        {'MyAuth': []},\n    ]\n\n\ndef test_can_use_api_key_and_authorizers_with_scopes(sample_app, swagger_gen):\n    authorizer = CustomAuthorizer(\n        'MyAuth', authorizer_uri='auth-uri', header='Authorization')\n\n    @sample_app.route(\n        '/auth',\n        authorizer=authorizer.with_scopes([\"write:test\", \"read:test\"]),\n        api_key_required=True\n    )\n    def auth():\n        return {'foo': 'bar'}\n\n    doc = swagger_gen.generate_swagger(sample_app)\n    single_method = doc['paths']['/auth']['get']\n    assert single_method.get('security') == [\n        {'api_key': []},\n        {'MyAuth': [\"write:test\", \"read:test\"]},\n    ]\n\n\ndef test_can_use_iam_authorizer_object(sample_app, swagger_gen):\n    authorizer = IAMAuthorizer()\n\n    @sample_app.route('/auth', authorizer=authorizer)\n    def auth():\n        return {'foo': 'bar'}\n\n    doc = swagger_gen.generate_swagger(sample_app)\n    single_method = doc['paths']['/auth']['get']\n    assert single_method.get('security') == [{'sigv4': []}]\n    security_definitions = doc['securityDefinitions']\n    assert 'sigv4' in security_definitions\n    assert security_definitions['sigv4'] == {\n          \"in\": \"header\",\n          \"type\": \"apiKey\",\n          \"name\": \"Authorization\",\n          \"x-amazon-apigateway-authtype\": \"awsSigv4\"\n    }\n\n\ndef test_can_use_cognito_auth_object(sample_app, swagger_gen):\n    authorizer = CognitoUserPoolAuthorizer('MyUserPool',\n                                           header='Authorization',\n                                           provider_arns=['myarn'])\n\n    @sample_app.route('/api-key-required', authorizer=authorizer)\n    def foo():\n        return {}\n\n    doc = swagger_gen.generate_swagger(sample_app)\n    single_method = doc['paths']['/api-key-required']['get']\n    assert single_method.get('security') == [{'MyUserPool': []}]\n    assert 'securityDefinitions' in doc\n    assert doc['securityDefinitions'].get('MyUserPool') == {\n        'in': 'header',\n        'type': 'apiKey',\n        'name': 'Authorization',\n        'x-amazon-apigateway-authtype': 'cognito_user_pools',\n        'x-amazon-apigateway-authorizer': {\n            'type': 'cognito_user_pools',\n            'providerARNs': ['myarn']\n        }\n    }\n\n\ndef test_can_use_cognito_auth_object_with_scopes(sample_app, swagger_gen):\n    authorizer = CognitoUserPoolAuthorizer('MyUserPool',\n                                           header='Authorization',\n                                           provider_arns=['myarn'])\n\n    @sample_app.route(\n        '/api-key-required',\n        authorizer=authorizer.with_scopes([\"write:test\", \"read:test\"])\n    )\n    def foo():\n        return {}\n\n    doc = swagger_gen.generate_swagger(sample_app)\n    single_method = doc['paths']['/api-key-required']['get']\n    assert single_method.get('security') == [{\n        'MyUserPool': [\"write:test\", \"read:test\"]\n    }]\n    assert 'securityDefinitions' in doc\n    assert doc['securityDefinitions'].get('MyUserPool') == {\n        'in': 'header',\n        'type': 'apiKey',\n        'name': 'Authorization',\n        'x-amazon-apigateway-authtype': 'cognito_user_pools',\n        'x-amazon-apigateway-authorizer': {\n            'type': 'cognito_user_pools',\n            'providerARNs': ['myarn']\n        }\n    }\n\n\ndef test_auth_defined_for_multiple_methods(sample_app, swagger_gen):\n    authorizer = CognitoUserPoolAuthorizer('MyUserPool',\n                                           header='Authorization',\n                                           provider_arns=['myarn'])\n\n    @sample_app.route('/pool1', authorizer=authorizer)\n    def foo():\n        return {}\n\n    @sample_app.route('/pool2', authorizer=authorizer)\n    def bar():\n        return {}\n\n    doc = swagger_gen.generate_swagger(sample_app)\n    assert 'securityDefinitions' in doc\n    assert len(doc['securityDefinitions']) == 1\n\n\ndef test_builtin_auth(sample_app):\n    swagger_gen = SwaggerGenerator(\n        region='us-west-2',\n        deployed_resources={\n            'api_handler_arn': 'arn:aws:lambda:mars-west-1:123456789'\n                               ':function:lambda_arn',\n            'api_handler_name': 'api-dev',\n            'lambda_functions': {\n                'api-dev-myauth': {\n                    'arn': 'arn:aws:lambda:mars-west-1:123456789'\n                           ':function:auth_arn',\n                    'type': 'authorizer',\n                }\n            }\n        }\n    )\n\n    @sample_app.authorizer(name='myauth',\n                           ttl_seconds=10,\n                           execution_role='arn:role')\n    def auth(auth_request):\n        pass\n\n    @sample_app.route('/auth', authorizer=auth)\n    def foo():\n        pass\n\n    doc = swagger_gen.generate_swagger(sample_app)\n    single_method = doc['paths']['/auth']['get']\n    assert single_method.get('security') == [{'myauth': []}]\n    assert 'securityDefinitions' in doc\n    assert doc['securityDefinitions']['myauth'] == {\n        'in': 'header',\n        'name': 'Authorization',\n        'type': 'apiKey',\n        'x-amazon-apigateway-authtype': 'custom',\n        'x-amazon-apigateway-authorizer': {\n            'type': 'token',\n            'authorizerCredentials': 'arn:role',\n            'authorizerResultTtlInSeconds': 10,\n            'authorizerUri': ('arn:aws:apigateway:us-west-2:lambda:path'\n                              '/2015-03-31/functions/arn:aws:lambda:'\n                              'mars-west-1:123456789:function:auth_arn'\n                              '/invocations'),\n        }\n    }\n\n\ndef test_builtin_auth_with_custom_header(sample_app):\n    swagger_gen = SwaggerGenerator(\n        region='us-west-2',\n        deployed_resources={\n            'api_handler_arn': 'arn:aws:lambda:mars-west-1:123456789'\n                               ':function:lambda_arn',\n            'api_handler_name': 'api-dev',\n            'lambda_functions': {\n                'api-dev-myauth': {\n                    'arn': 'arn:aws:lambda:mars-west-1:123456789'\n                           ':function:auth_arn',\n                    'type': 'authorizer',\n                }\n            }\n        }\n    )\n\n    @sample_app.authorizer(name='myauth',\n                           ttl_seconds=10,\n                           execution_role='arn:role',\n                           header='FOO')\n    def auth(auth_request):\n        pass\n\n    @sample_app.route('/auth', authorizer=auth)\n    def foo():\n        pass\n\n    doc = swagger_gen.generate_swagger(sample_app)\n    single_method = doc['paths']['/auth']['get']\n    assert single_method.get('security') == [{'myauth': []}]\n    assert 'securityDefinitions' in doc\n    assert doc['securityDefinitions']['myauth'] == {\n        'in': 'header',\n        'name': 'FOO',\n        'type': 'apiKey',\n        'x-amazon-apigateway-authtype': 'custom',\n        'x-amazon-apigateway-authorizer': {\n            'type': 'token',\n            'authorizerCredentials': 'arn:role',\n            'authorizerResultTtlInSeconds': 10,\n            'authorizerUri': ('arn:aws:apigateway:us-west-2:lambda:path'\n                              '/2015-03-31/functions/arn:aws:lambda:'\n                              'mars-west-1:123456789:function:auth_arn'\n                              '/invocations'),\n        }\n    }\n\n\ndef test_builtin_auth_with_scopes(sample_app):\n    swagger_gen = SwaggerGenerator(\n        region='us-west-2',\n        deployed_resources={\n            'api_handler_arn': 'arn:aws:lambda:mars-west-1:123456789'\n                               ':function:lambda_arn',\n            'api_handler_name': 'api-dev',\n            'lambda_functions': {\n                'api-dev-myauth': {\n                    'arn': 'arn:aws:lambda:mars-west-1:123456789'\n                           ':function:auth_arn',\n                    'type': 'authorizer',\n                }\n            }\n        }\n    )\n\n    @sample_app.authorizer(name='myauth',\n                           ttl_seconds=10,\n                           execution_role='arn:role')\n    def auth(auth_request):\n        pass\n\n    @sample_app.route(\n        '/auth',\n        authorizer=auth.with_scopes([\"write:test\", \"read:test\"])\n    )\n    def foo():\n        pass\n\n    doc = swagger_gen.generate_swagger(sample_app)\n    single_method = doc['paths']['/auth']['get']\n    assert single_method.get('security') == [{\n        'myauth': [\"write:test\", \"read:test\"]\n    }]\n    assert 'securityDefinitions' in doc\n    assert doc['securityDefinitions']['myauth'] == {\n        'in': 'header',\n        'name': 'Authorization',\n        'type': 'apiKey',\n        'x-amazon-apigateway-authtype': 'custom',\n        'x-amazon-apigateway-authorizer': {\n            'type': 'token',\n            'authorizerCredentials': 'arn:role',\n            'authorizerResultTtlInSeconds': 10,\n            'authorizerUri': ('arn:aws:apigateway:us-west-2:'\n                              'lambda:path/2015-03-31/functions'\n                              '/arn:aws:lambda:mars-west-1:123456789:'\n                              'function:auth_arn/invocations'),\n        }\n    }\n\n\ndef test_will_default_to_function_name_for_auth(sample_app):\n    swagger_gen = SwaggerGenerator(\n        region='us-west-2',\n        deployed_resources={\n            'api_handler_arn': 'arn:aws:lambda:mars-west-1:123456789'\n                               ':function:lambda_arn',\n            'api_handler_name': 'api-dev',\n            'lambda_functions': {\n                'api-dev-auth': {\n                    'arn': 'arn:aws:lambda:mars-west-1:123456789'\n                           ':function:auth_arn',\n                    'type': 'authorizer',\n                }\n            }\n        }\n    )\n\n    # No \"name=\" kwarg provided should default\n    # to a name of \"auth\".\n    @sample_app.authorizer(ttl_seconds=10, execution_role='arn:role')\n    def auth(auth_request):\n        pass\n\n    @sample_app.route('/auth', authorizer=auth)\n    def foo():\n        pass\n\n    doc = swagger_gen.generate_swagger(sample_app)\n    single_method = doc['paths']['/auth']['get']\n    assert single_method.get('security') == [{'auth': []}]\n    assert 'securityDefinitions' in doc\n    assert doc['securityDefinitions']['auth'] == {\n        'in': 'header',\n        'name': 'Authorization',\n        'type': 'apiKey',\n        'x-amazon-apigateway-authtype': 'custom',\n        'x-amazon-apigateway-authorizer': {\n            'type': 'token',\n            'authorizerCredentials': 'arn:role',\n            'authorizerResultTtlInSeconds': 10,\n            'authorizerUri': ('arn:aws:apigateway:us-west-2:lambda:path'\n                              '/2015-03-31/functions/'\n                              'arn:aws:lambda:mars-west-1:123456789:function'\n                              ':auth_arn/invocations'),\n        }\n    }\n\n\ndef test_can_custom_resource_policy(sample_app, swagger_gen):\n    rest_api = RestAPI(\n        resource_name='dev',\n        swagger_doc={},\n        lambda_function=None,\n        minimum_compression=\"\",\n        api_gateway_stage=\"xyz\",\n        endpoint_type=\"PRIVATE\",\n        policy=IAMPolicy({\n            'Statement': [{\n                \"Effect\": \"Allow\",\n                \"Principal\": \"*\",\n                \"Action\": \"execute-api:Invoke\",\n                \"Resource\": [\n                    \"arn:aws:execute-api:*:*:*\",\n                    \"arn:aws:exceute-api:*:*:*/*\"\n                ],\n                \"Condition\": {\n                    \"StringEquals\": {\n                        \"aws:SourceVpce\": \"vpce-abc123\"\n                    }\n                }\n            }]\n        })\n    )\n\n    doc = swagger_gen.generate_swagger(sample_app, rest_api)\n    assert doc['x-amazon-apigateway-policy'] == {\n        'Statement': [{\n            'Action': 'execute-api:Invoke',\n            'Condition': {'StringEquals': {\n                'aws:SourceVpce': 'vpce-abc123'}},\n            'Effect': 'Allow',\n            'Principal': '*',\n            'Resource': [\n                'arn:aws:execute-api:*:*:*',\n                \"arn:aws:exceute-api:*:*:*/*\"]\n            }]\n    }\n\n\ndef test_can_vpce(sample_app, swagger_gen):\n    rest_api = RestAPI(\n        resource_name='dev',\n        swagger_doc={},\n        lambda_function=None,\n        minimum_compression=\"\",\n        api_gateway_stage=\"xyz\",\n        endpoint_type=\"PRIVATE\",\n        vpce_ids=[\"vpce-12346\", \"vpce-abc123\"],\n    )\n\n    doc = swagger_gen.generate_swagger(sample_app, rest_api)\n    assert doc['x-amazon-apigateway-endpoint-configuration'] == {\n        \"vpcEndpointIds\": [\"vpce-12346\", \"vpce-abc123\"]\n    }\n\n\ndef test_can_auto_resource_policy_with_cfn(sample_app):\n    swagger_gen = CFNSwaggerGenerator()\n    rest_api = RestAPI(\n        resource_name='dev',\n        swagger_doc={},\n        lambda_function=None,\n        minimum_compression=\"\",\n        api_gateway_stage=\"xyz\",\n        endpoint_type=\"PRIVATE\",\n        policy=IAMPolicy({\n            'Statement': [{\n                \"Effect\": \"Allow\",\n                \"Principal\": \"*\",\n                \"Action\": \"execute-api:Invoke\",\n                \"Resource\": \"arn:aws:execute-api:*:*:*/*\",\n                \"Condition\": {\n                    \"StringEquals\": {\n                        \"aws:SourceVpce\": \"vpce-abc123\"\n                    }\n                }\n            }]\n        })\n    )\n\n    doc = swagger_gen.generate_swagger(sample_app, rest_api)\n    assert doc['x-amazon-apigateway-policy'] == {\n        'Statement': [{\n            'Action': 'execute-api:Invoke',\n            'Condition': {'StringEquals': {\n                'aws:SourceVpce': 'vpce-abc123'}},\n            'Effect': 'Allow',\n            'Principal': '*',\n            'Resource': 'arn:aws:execute-api:*:*:*/*',\n            }]\n    }\n\n\ndef test_will_custom_auth_with_cfn(sample_app):\n    swagger_gen = CFNSwaggerGenerator()\n\n    # No \"name=\" kwarg provided should default\n    # to a name of \"auth\".\n    @sample_app.authorizer(ttl_seconds=10, execution_role='arn:role')\n    def auth(auth_request):\n        pass\n\n    @sample_app.route('/auth', authorizer=auth)\n    def foo():\n        pass\n\n    doc = swagger_gen.generate_swagger(sample_app)\n    assert 'securityDefinitions' in doc\n    assert doc['securityDefinitions']['auth'] == {\n        'in': 'header',\n        'name': 'Authorization',\n        'type': 'apiKey',\n        'x-amazon-apigateway-authtype': 'custom',\n        'x-amazon-apigateway-authorizer': {\n            'type': 'token',\n            'authorizerCredentials': 'arn:role',\n            'authorizerResultTtlInSeconds': 10,\n            'authorizerUri': {\n                'Fn::Sub': (\n                    'arn:${AWS::Partition}:apigateway:${AWS::Region}'\n                    ':lambda:path/2015-03-31/functions/'\n                    '${Auth.Arn}/invocations'\n                )\n            }\n        }\n    }\n\n\ndef test_custom_auth_with_tf(sample_app):\n    swagger_gen = TerraformSwaggerGenerator()\n\n    # No \"name=\" kwarg provided should default\n    # to a name of \"auth\".\n    @sample_app.authorizer(ttl_seconds=10, execution_role='arn:role')\n    def auth(auth_request):\n        pass\n\n    @sample_app.route('/auth', authorizer=auth)\n    def foo():\n        pass\n\n    doc = swagger_gen.generate_swagger(sample_app)\n    assert 'securityDefinitions' in doc\n    assert doc['securityDefinitions']['auth'] == {\n        'in': 'header',\n        'name': 'Authorization',\n        'type': 'apiKey',\n        'x-amazon-apigateway-authtype': 'custom',\n        'x-amazon-apigateway-authorizer': {\n            'type': 'token',\n            'authorizerCredentials': 'arn:role',\n            'authorizerResultTtlInSeconds': 10,\n            'authorizerUri': '${aws_lambda_function.auth.invoke_arn}'\n        }\n    }\n"
  },
  {
    "path": "tests/unit/deploy/test_validate.py",
    "content": "import pytest\nimport warnings\nfrom unittest import mock\n\nfrom chalice.app import Chalice\nfrom chalice.config import Config\nfrom chalice import CORSConfig\nfrom chalice.constants import MIN_COMPRESSION_SIZE\nfrom chalice.constants import MAX_COMPRESSION_SIZE\nfrom chalice.deploy.validate import validate_configuration\nfrom chalice.deploy.validate import validate_routes\nfrom chalice.deploy.validate import validate_python_version\nfrom chalice.deploy.validate import validate_route_content_types\nfrom chalice.deploy.validate import validate_unique_function_names\nfrom chalice.deploy.validate import validate_feature_flags\nfrom chalice.deploy.validate import validate_endpoint_type\nfrom chalice.deploy.validate import validate_resource_policy\nfrom chalice.deploy.validate import ExperimentalFeatureError\n\n\ndef test_trailing_slash_routes_result_in_error():\n    app = Chalice('appname')\n    app.routes = {'/trailing-slash/': None}\n    config = Config.create(chalice_app=app)\n    with pytest.raises(ValueError):\n        validate_configuration(config)\n\n\ndef test_empty_route_results_in_error():\n    app = Chalice('appname')\n    app.routes = {'': {}}\n    config = Config.create(chalice_app=app)\n    with pytest.raises(ValueError):\n        validate_configuration(config)\n\n\ndef test_validate_python_version_invalid():\n    config = mock.Mock(spec=Config)\n    config.lambda_python_version = 'python1.0'\n    with pytest.warns(UserWarning):\n        validate_python_version(config)\n\n\ndef test_python_version_invalid_from_real_config():\n    config = Config.create()\n    with pytest.warns(UserWarning):\n        validate_python_version(config, 'python1.0')\n\n\ndef test_python_version_is_valid():\n    config = Config.create()\n    with warnings.catch_warnings():\n        warnings.simplefilter('error')\n        validate_python_version(config, config.lambda_python_version)\n\n\ndef test_manage_iam_role_false_requires_role_arn(sample_app):\n    config = Config.create(chalice_app=sample_app, manage_iam_role=False,\n                           iam_role_arn='arn:::foo')\n    assert validate_configuration(config) is None\n\n\ndef test_validation_error_if_no_role_provided_when_manage_false(sample_app):\n    # We're indicating that we should not be managing the\n    # IAM role, but we're not giving a role ARN to use.\n    # This is a validation error.\n    config = Config.create(chalice_app=sample_app, manage_iam_role=False)\n    with pytest.raises(ValueError):\n        validate_configuration(config)\n\n\ndef test_validate_unique_lambda_function_names(sample_app):\n    @sample_app.lambda_function()\n    def foo(event, context):\n        pass\n\n    # This will cause a validation error because\n    # 'foo' is already registered as a lambda function.\n    @sample_app.lambda_function(name='foo')\n    def bar(event, context):\n        pass\n\n    config = Config.create(chalice_app=sample_app, manage_iam_role=False)\n    with pytest.raises(ValueError):\n        validate_unique_function_names(config)\n\n\ndef test_validate_names_across_function_types(sample_app):\n    @sample_app.lambda_function()\n    def foo(event, context):\n        pass\n\n    @sample_app.schedule('rate(1 hour)', name='foo')\n    def bar(event):\n        pass\n\n    config = Config.create(chalice_app=sample_app, manage_iam_role=False)\n    with pytest.raises(ValueError):\n        validate_unique_function_names(config)\n\n\ndef test_validate_names_using_name_kwarg(sample_app):\n    @sample_app.authorizer(name='duplicate')\n    def foo(auth_request):\n        pass\n\n    @sample_app.lambda_function(name='duplicate')\n    def bar(event):\n        pass\n\n    config = Config.create(chalice_app=sample_app, manage_iam_role=False)\n    with pytest.raises(ValueError):\n        validate_unique_function_names(config)\n\n\nclass TestValidateCORS(object):\n    def test_cant_have_options_with_cors(self, sample_app):\n        @sample_app.route('/badcors', methods=['GET', 'OPTIONS'], cors=True)\n        def badview():\n            pass\n\n        with pytest.raises(ValueError):\n            validate_routes(sample_app.routes)\n\n    def test_cant_have_differing_cors_configurations(self, sample_app):\n        custom_cors = CORSConfig(\n            allow_origin='https://foo.example.com',\n            allow_headers=['X-Special-Header'],\n            max_age=600,\n            expose_headers=['X-Special-Header'],\n            allow_credentials=True\n        )\n\n        @sample_app.route('/cors', methods=['GET'], cors=True)\n        def cors():\n            pass\n\n        @sample_app.route('/cors', methods=['PUT'], cors=custom_cors)\n        def different_cors():\n            pass\n\n        with pytest.raises(ValueError):\n            validate_routes(sample_app.routes)\n\n    def test_can_have_same_cors_configurations(self, sample_app):\n        @sample_app.route('/cors', methods=['GET'], cors=True)\n        def cors():\n            pass\n\n        @sample_app.route('/cors', methods=['PUT'], cors=True)\n        def same_cors():\n            pass\n\n        try:\n            validate_routes(sample_app.routes)\n        except ValueError:\n            pytest.fail(\n                'A ValueError was unexpectedly thrown. Applications '\n                'may have multiple view functions that share the same '\n                'route and CORS configuration.'\n            )\n\n    def test_can_have_same_custom_cors_configurations(self, sample_app):\n        custom_cors = CORSConfig(\n            allow_origin='https://foo.example.com',\n            allow_headers=['X-Special-Header'],\n            max_age=600,\n            expose_headers=['X-Special-Header'],\n            allow_credentials=True\n        )\n\n        @sample_app.route('/cors', methods=['GET'], cors=custom_cors)\n        def cors():\n            pass\n\n        same_custom_cors = CORSConfig(\n            allow_origin='https://foo.example.com',\n            allow_headers=['X-Special-Header'],\n            max_age=600,\n            expose_headers=['X-Special-Header'],\n            allow_credentials=True\n        )\n\n        @sample_app.route('/cors', methods=['PUT'], cors=same_custom_cors)\n        def same_cors():\n            pass\n\n        try:\n            validate_routes(sample_app.routes)\n        except ValueError:\n            pytest.fail(\n                'A ValueError was unexpectedly thrown. Applications '\n                'may have multiple view functions that share the same '\n                'route and CORS configuration.'\n            )\n\n    def test_can_have_one_cors_configured_and_others_not(self, sample_app):\n        @sample_app.route('/cors', methods=['GET'], cors=True)\n        def cors():\n            pass\n\n        @sample_app.route('/cors', methods=['PUT'])\n        def no_cors():\n            pass\n\n        try:\n            validate_routes(sample_app.routes)\n        except ValueError:\n            pytest.fail(\n                'A ValueError was unexpectedly thrown. Applications '\n                'may have multiple view functions that share the same '\n                'route but only one is configured for CORS.'\n            )\n\n\ndef test_cant_have_mixed_content_types(sample_app):\n    @sample_app.route('/index', content_types=['application/octet-stream',\n                                               'text/plain'])\n    def index():\n        return {'hello': 'world'}\n\n    with pytest.raises(ValueError):\n        validate_route_content_types(sample_app.routes,\n                                     sample_app.api.binary_types)\n\n\ndef test_can_validate_updated_custom_binary_types(sample_app):\n    sample_app.api.binary_types.extend(['text/plain'])\n\n    @sample_app.route('/index', content_types=['application/octet-stream',\n                                               'text/plain'])\n    def index():\n        return {'hello': 'world'}\n\n    assert validate_route_content_types(sample_app.routes,\n                                        sample_app.api.binary_types) is None\n\n\ndef test_can_validate_resource_policy(sample_app):\n    config = Config.create(\n        chalice_app=sample_app, api_gateway_endpoint_type='PRIVATE')\n    with pytest.raises(ValueError):\n        validate_resource_policy(config)\n\n    config = Config.create(\n        chalice_app=sample_app,\n        api_gateway_endpoint_vpce='vpce-abc123',\n        api_gateway_endpoint_type='PRIVATE')\n    validate_resource_policy(config)\n\n    config = Config.create(\n        chalice_app=sample_app,\n        api_gateway_endpoint_vpce='vpce-abc123',\n        api_gateway_endpoint_type='REGIONAL')\n    with pytest.raises(ValueError):\n        validate_resource_policy(config)\n\n    config = Config.create(\n        chalice_app=sample_app,\n        api_gateway_policy_file='xyz.json',\n        api_gateway_endpoint_type='PRIVATE')\n    validate_resource_policy(config)\n\n    config = Config.create(\n        chalice_app=sample_app,\n        api_gateway_endpoint_vpce=['vpce-abc123', 'vpce-bdef'],\n        api_gateway_policy_file='bar.json',\n        api_gateway_endpoint_type='PRIVATE')\n    with pytest.raises(ValueError):\n        validate_resource_policy(config)\n\n\ndef test_can_validate_endpoint_type(sample_app):\n    config = Config.create(\n        chalice_app=sample_app, api_gateway_endpoint_type='EDGE2')\n    with pytest.raises(ValueError):\n        validate_endpoint_type(config)\n\n    config = Config.create(\n        chalice_app=sample_app, api_gateway_endpoint_type='REGIONAL')\n    validate_endpoint_type(config)\n\n\ndef test_can_validate_feature_flags(sample_app):\n    # The _features_used is marked internal because we don't want\n    # chalice users to access it, but this attribute is intended to be\n    # accessed by anything within the chalice codebase.\n    sample_app._features_used.add('SOME_NEW_FEATURE')\n    with pytest.raises(ExperimentalFeatureError):\n        validate_feature_flags(sample_app)\n    # Now if we opt in, validation is fine.\n    sample_app.experimental_feature_flags.add('SOME_NEW_FEATURE')\n    try:\n        validate_feature_flags(sample_app)\n    except ExperimentalFeatureError:\n        raise AssertionError(\"App was not suppose to raise an error when \"\n                             \"opting in to features via a feature flag.\")\n\n\ndef test_validation_error_if_minimum_compression_size_not_int(sample_app):\n    config = Config.create(chalice_app=sample_app,\n                           minimum_compression_size='not int')\n    with pytest.raises(ValueError):\n        validate_configuration(config)\n\n\ndef test_validation_error_if_minimum_compression_size_invalid_int(sample_app):\n    config = Config.create(chalice_app=sample_app,\n                           minimum_compression_size=MIN_COMPRESSION_SIZE-1)\n    with pytest.raises(ValueError):\n        validate_configuration(config)\n\n    config = Config.create(chalice_app=sample_app,\n                           minimum_compression_size=MAX_COMPRESSION_SIZE+1)\n    with pytest.raises(ValueError):\n        validate_configuration(config)\n\n\ndef test_valid_minimum_compression_size(sample_app):\n    config = Config.create(chalice_app=sample_app,\n                           minimum_compression_size=1)\n    assert validate_configuration(config) is None\n\n\ndef test_validate_sqs_queue_name(sample_app):\n\n    @sample_app.on_sqs_message(\n        queue='https://sqs.us-west-2.amazonaws.com/12345/myqueue')\n    def handler(event):\n        pass\n\n    config = Config.create(chalice_app=sample_app)\n    with pytest.raises(ValueError):\n        validate_configuration(config)\n\n\ndef test_can_use_queue_arn(sample_app):\n\n    @sample_app.on_sqs_message(queue_arn='arn:sqs:...:myqueue')\n    def handler(event):\n        pass\n\n    config = Config.create(chalice_app=sample_app)\n    assert validate_configuration(config) is None\n\n\ndef test_queue_arn_must_be_arn(sample_app):\n    @sample_app.on_sqs_message(\n        queue_arn='https://sqs.us-west-2.amazonaws.com/12345/myqueue')\n    def handler(event):\n        pass\n\n    config = Config.create(chalice_app=sample_app)\n    with pytest.raises(ValueError):\n        validate_configuration(config)\n\n\ndef test_validate_environment_variables_value_type_not_str(sample_app):\n    config = Config.create(chalice_app=sample_app,\n                           environment_variables={\"ENV_KEY\": 1})\n    with pytest.raises(ValueError):\n        validate_configuration(config)\n\n\ndef test_validate_unicode_is_valid_env_var(sample_app):\n    config = Config.create(chalice_app=sample_app,\n                           environment_variables={\"ENV_KEY\": u'unicode-val'})\n    assert validate_configuration(config) is None\n\n\ndef test_validate_env_var_is_string_for_lambda_functions(sample_app):\n    @sample_app.lambda_function()\n    def foo(event, context):\n        pass\n\n    config = Config(\n        chalice_stage='dev',\n        config_from_disk={\n            'stages': {\n                'dev': {\n                    'lambda_functions': {\n                        'foo': {'environment_variables': {'BAR': 2}}}\n                }\n            }\n        },\n        user_provided_params={'chalice_app': sample_app}\n    )\n    with pytest.raises(ValueError):\n        validate_configuration(config)\n"
  },
  {
    "path": "tests/unit/test_analyzer.py",
    "content": "import sys\nimport pytest\n\nfrom textwrap import dedent\n\nfrom chalice import analyzer\nfrom chalice.analyzer import Boto3ModuleType, Boto3CreateClientType\nfrom chalice.analyzer import Boto3ClientType, Boto3ClientMethodType\nfrom chalice.analyzer import Boto3ClientMethodCallType\nfrom chalice.analyzer import FunctionType\n\n\ndef aws_calls(source_code):\n    real_source_code = dedent(source_code)\n    calls = analyzer.get_client_calls(real_source_code)\n    return calls\n\n\ndef chalice_aws_calls(source_code):\n    real_source_code = dedent(source_code)\n    calls = analyzer.get_client_calls_for_app(real_source_code)\n    return calls\n\n\ndef known_types_for_module(source_code):\n    real_source_code = dedent(source_code)\n    compiled = analyzer.parse_code(real_source_code)\n    t = analyzer.SymbolTableTypeInfer(compiled)\n    t.bind_types()\n    known = t.known_types()\n    return known\n\n\ndef known_types_for_function(source_code, name):\n    real_source_code = dedent(source_code)\n    compiled = analyzer.parse_code(real_source_code)\n    t = analyzer.SymbolTableTypeInfer(compiled)\n    t.bind_types()\n    known = t.known_types(scope_name=name)\n    return known\n\n\ndef test_can_analyze_chalice_app():\n    assert chalice_aws_calls(\"\"\"\\\n        from chalice import Chalice\n        import boto3\n\n        app = Chalice(app_name='james1')\n        ec2 = boto3.client('ec2')\n\n\n        @app.route('/')\n        def index():\n            ec2.describe_instances()\n            return {}\n    \"\"\") == {'ec2': set(['describe_instances'])}\n\n\ndef test_inferred_module_type():\n    assert known_types_for_module(\"\"\"\\\n        import boto3\n        import os\n        a = 1\n    \"\"\") == {'boto3': Boto3ModuleType()}\n\n\ndef test_recursive_function_none():\n    assert aws_calls(\"\"\"\\\n        def recursive_function():\n            recursive_function()\n        recursive_function()\n    \"\"\") == {}\n\n\ndef test_recursive_comprehension_none():\n    assert aws_calls(\"\"\"\\\n        xs = []\n        def recursive_function():\n            [recursive_function() for x in xs]\n        recursive_function()\n    \"\"\") == {}\n\n\ndef test_recursive_function_client_calls():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        def recursive_function():\n            recursive_function()\n            boto3.client('ec2').describe_instances()\n        recursive_function()\n    \"\"\") == {'ec2': set(['describe_instances'])}\n\n\ndef test_mutual_recursion():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        ec2 = boto3.client('ec2')\n\n        def a():\n            b()\n            ec2.run_instances()\n\n\n        def b():\n            ec2.describe_instances()\n            a()\n        a()\n    \"\"\") == {'ec2': set(['describe_instances', 'run_instances'])}\n\n\ndef test_inferred_module_type_tracks_assignment():\n    assert known_types_for_module(\"\"\"\\\n        import boto3\n        a = boto3\n    \"\"\") == {'boto3': Boto3ModuleType(),\n             'a': Boto3ModuleType()}\n\n\ndef test_inferred_module_type_tracks_multi_assignment():\n    assert known_types_for_module(\"\"\"\\\n        import boto3\n        a = b = c = boto3\n    \"\"\") == {'boto3': Boto3ModuleType(),\n             'a': Boto3ModuleType(),\n             'b': Boto3ModuleType(),\n             'c': Boto3ModuleType()}\n\n\ndef test_inferred_client_create_type():\n    assert known_types_for_module(\"\"\"\\\n        import boto3\n        a = boto3.client\n    \"\"\") == {'boto3': Boto3ModuleType(),\n             'a': Boto3CreateClientType()}\n\n\ndef test_inferred_client_type():\n    assert known_types_for_module(\"\"\"\\\n        import boto3\n        a = boto3.client('ec2')\n    \"\"\") == {'boto3': Boto3ModuleType(),\n             'a': Boto3ClientType('ec2')}\n\n\ndef test_inferred_client_type_each_part():\n    assert known_types_for_module(\"\"\"\\\n        import boto3\n        a = boto3.client\n        b = a('ec2')\n    \"\"\") == {'boto3': Boto3ModuleType(),\n             'a': Boto3CreateClientType(),\n             'b': Boto3ClientType('ec2')}\n\n\ndef test_infer_client_method():\n    assert known_types_for_module(\"\"\"\\\n        import boto3\n        a = boto3.client('ec2').describe_instances\n    \"\"\") == {'boto3': Boto3ModuleType(),\n             'a': Boto3ClientMethodType('ec2', 'describe_instances')}\n\n\ndef test_infer_client_method_called():\n    assert known_types_for_module(\"\"\"\\\n        import boto3\n        a = boto3.client('ec2').describe_instances()\n    \"\"\") == {'boto3': Boto3ModuleType(),\n             'a': Boto3ClientMethodCallType('ec2', 'describe_instances')}\n\n\ndef test_infer_type_on_function_scope():\n    assert known_types_for_function(\"\"\"\\\n        import boto3\n        def foo():\n            d = boto3.client('dynamodb')\n            e = d.list_tables()\n        foo()\n    \"\"\", name='foo') == {\n        'd': Boto3ClientType('dynamodb'),\n        'e': Boto3ClientMethodCallType('dynamodb', 'list_tables')\n    }\n\n\ndef test_can_understand_return_types():\n    assert known_types_for_module(\"\"\"\\\n        import boto3\n        def create_client():\n            d = boto3.client('dynamodb')\n            return d\n        e = create_client()\n    \"\"\") == {\n        'boto3': Boto3ModuleType(),\n        'create_client': FunctionType(Boto3ClientType('dynamodb')),\n        'e': Boto3ClientType('dynamodb'),\n    }\n\n\ndef test_type_equality():\n    assert Boto3ModuleType() == Boto3ModuleType()\n    assert Boto3CreateClientType() == Boto3CreateClientType()\n    assert Boto3ModuleType() != Boto3CreateClientType()\n\n    assert Boto3ClientType('s3') == Boto3ClientType('s3')\n    assert Boto3ClientType('s3') != Boto3ClientType('ec2')\n    assert Boto3ClientType('s3') == Boto3ClientType('s3')\n\n    assert (Boto3ClientMethodType('s3', 'list_objects') ==\n            Boto3ClientMethodType('s3', 'list_objects'))\n    assert (Boto3ClientMethodType('ec2', 'describe_instances') !=\n            Boto3ClientMethodType('s3', 'list_object'))\n    assert (Boto3ClientMethodType('ec2', 'describe_instances') !=\n            Boto3CreateClientType())\n\n\ndef test_single_call():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        d = boto3.client('dynamodb')\n        d.list_tables()\n    \"\"\") == {'dynamodb': set(['list_tables'])}\n\n\ndef test_multiple_calls():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        d = boto3.client('dynamodb')\n        d.list_tables()\n        d.create_table(TableName='foobar')\n    \"\"\") == {'dynamodb': set(['list_tables', 'create_table'])}\n\n\ndef test_multiple_services():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        d = boto3.client('dynamodb')\n        asdf = boto3.client('s3')\n        d.list_tables()\n        asdf.get_object(Bucket='foo', Key='bar')\n        d.create_table(TableName='foobar')\n    \"\"\") == {'dynamodb': set(['list_tables', 'create_table']),\n             's3': set(['get_object'])}\n\n\ndef test_basic_aliasing():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        d = boto3.client('dynamodb')\n        alias = d\n        alias.list_tables()\n    \"\"\") == {'dynamodb': set(['list_tables'])}\n\n\ndef test_multiple_aliasing():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        d = boto3.client('dynamodb')\n        alias = d\n        alias2 = alias\n        alias3 = alias2\n        alias3.list_tables()\n    \"\"\") == {'dynamodb': set(['list_tables'])}\n\n\ndef test_multiple_aliasing_non_chained():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        d = boto3.client('dynamodb')\n        alias = d\n        alias2 = alias\n        alias3 = alias\n        alias3.list_tables()\n    \"\"\") == {'dynamodb': set(['list_tables'])}\n\n\ndef test_no_calls_found():\n    assert aws_calls(\"\"\"\\\n        import boto3\n    \"\"\") == {}\n\n\ndef test_original_name_replaced():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        import some_other_thing\n        d = boto3.client('dynamodb')\n        d.list_tables()\n        d = some_other_thing\n        d.create_table()\n    \"\"\") == {'dynamodb': set(['list_tables'])}\n\n\ndef test_multiple_targets():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        a = b = boto3.client('dynamodb')\n        b.list_tables()\n        a.create_table()\n    \"\"\") == {'dynamodb': set(['create_table', 'list_tables'])}\n\n\ndef test_in_function():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        def foo():\n            d = boto3.client('dynamodb')\n            d.list_tables()\n        foo()\n    \"\"\") == {'dynamodb': set(['list_tables'])}\n\n\ndef test_ignores_built_in_scope():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        a = boto3.client('dynamodb')\n        def foo():\n            if a is not None:\n                try:\n                    a.list_tables()\n                except Exception as e:\n                    a.create_table()\n        foo()\n    \"\"\") == {'dynamodb': set(['create_table', 'list_tables'])}\n\n\ndef test_understands_scopes():\n    assert aws_calls(\"\"\"\\\n        import boto3, mock\n        d = mock.Mock()\n        def foo():\n            d = boto3.client('dynamodb')\n        d.list_tables()\n    \"\"\") == {}\n\n\ndef test_function_return_types():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        def create_client():\n            return boto3.client('dynamodb')\n        create_client().list_tables()\n    \"\"\") == {'dynamodb': set(['list_tables'])}\n\n\ndef test_propagates_return_types():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        def create_client1():\n            return create_client2()\n        def create_client2():\n            return create_client3()\n        def create_client3():\n            return boto3.client('dynamodb')\n        create_client1().list_tables()\n    \"\"\") == {'dynamodb': set(['list_tables'])}\n\n\ndef test_decorator_list_is_ignored():\n    assert known_types_for_function(\"\"\"\\\n        import boto3\n        import decorators\n\n        @decorators.retry(10)\n        def foo():\n            d = boto3.client('dynamodb')\n            e = d.list_tables()\n        foo()\n    \"\"\", name='foo') == {\n        'd': Boto3ClientType('dynamodb'),\n        'e': Boto3ClientMethodCallType('dynamodb', 'list_tables')\n    }\n\n\ndef test_can_map_function_params():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        d = boto3.client('dynamodb')\n        def make_call(client):\n            a = 1\n            return client.list_tables()\n        make_call(d)\n    \"\"\") == {'dynamodb': set(['list_tables'])}\n\n\ndef test_can_understand_shadowed_vars_from_func_arg():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        d = boto3.client('dynamodb')\n        def make_call(d):\n            return d.list_tables()\n        make_call('foo')\n    \"\"\") == {}\n\n\ndef test_can_understand_shadowed_vars_from_local_scope():\n    assert aws_calls(\"\"\"\\\n        import boto3, mock\n        d = boto3.client('dynamodb')\n        def make_call(e):\n            d = mock.Mock()\n            return d.list_tables()\n        make_call(d)\n    \"\"\") == {}\n\n\ndef test_can_map_function_with_multiple_args():\n    assert aws_calls(\"\"\"\\\n        import boto3, mock\n        m = mock.Mock()\n        d = boto3.client('dynamodb')\n        def make_call(other, client):\n            a = 1\n            other.create_table()\n            return client.list_tables()\n        make_call(m, d)\n    \"\"\") == {'dynamodb': set(['list_tables'])}\n\n\ndef test_multiple_function_calls():\n    assert aws_calls(\"\"\"\\\n        import boto3, mock\n        m = mock.Mock()\n        d = boto3.client('dynamodb')\n        def make_call(other, client):\n            a = 1\n            other.create_table()\n            return other_call(a, 2, 3, client)\n        def other_call(a, b, c, client):\n            return client.list_tables()\n        make_call(m, d)\n    \"\"\") == {'dynamodb': set(['list_tables'])}\n\n\ndef test_can_lookup_var_names_to_functions():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        service_name = 'dynamodb'\n        d = boto3.client(service_name)\n        d.list_tables()\n    \"\"\") == {'dynamodb': set(['list_tables'])}\n\n\ndef test_map_string_literals_across_scopes():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        service_name = 'dynamodb'\n        def foo():\n            service_name = 's3'\n            d = boto3.client(service_name)\n            d.list_buckets()\n        d = boto3.client(service_name)\n        d.list_tables()\n        foo()\n    \"\"\") == {'s3': set(['list_buckets']), 'dynamodb': set(['list_tables'])}\n\n\ndef test_can_handle_lambda_keyword():\n    assert aws_calls(\"\"\"\\\n        def foo(a):\n            return sorted(bar.values(),\n                          key=lambda x: x.baz[a - 1],\n                          reverse=True)\n        bar = {}\n        foo(12)\n    \"\"\") == {}\n\n\ndef test_dict_comp_with_no_client_calls():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        foo = {i: i for i in range(10)}\n    \"\"\") == {}\n\n\ndef test_can_handle_gen_expr():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        ('a' for y in [1,2,3])\n    \"\"\") == {}\n\n\ndef test_can_detect_calls_in_gen_expr():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        service_name = 'dynamodb'\n        d = boto3.client('dynamodb')\n        (d.list_tables() for i in [1,2,3])\n    \"\"\") == {'dynamodb': set(['list_tables'])}\n\n\ndef test_can_handle_gen_from_call():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        service_name = 'dynamodb'\n        d = boto3.client('dynamodb')\n        (i for i in d.list_tables())\n    \"\"\") == {'dynamodb': set(['list_tables'])}\n\n\ndef test_can_detect_calls_in_multiple_gen_exprs():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        d = boto3.client('dynamodb')\n        (d for i in [1,2,3])\n        (d.list_tables() for j in [1,2,3])\n    \"\"\") == {'dynamodb': set(['list_tables'])}\n\n\ndef test_multiple_gen_exprs():\n    assert aws_calls(\"\"\"\\\n        (i for i in [1,2,3])\n        (j for j in [1,2,3])\n    \"\"\") == {}\n\n\ndef test_can_handle_list_expr_with_api_calls():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        d = boto3.client('dynamodb')\n        [d.list_tables() for y in [1,2,3]]\n    \"\"\") == {'dynamodb': set(['list_tables'])}\n\n\ndef test_can_handle_multiple_listcomps():\n    assert aws_calls(\"\"\"\\\n        bar_key = 'bar'\n        baz_key = 'baz'\n        items = [{'foo': 'sun', 'bar': 'moon', 'baz': 'stars'}]\n        foos = [i['foo'] for i in items]\n        bars = [j[bar_key] for j in items]\n        bazs = [k[baz_key] for k in items]\n    \"\"\") == {}\n\n\ndef test_can_analyze_lambda_function():\n    assert chalice_aws_calls(\"\"\"\\\n        from chalice import Chalice\n        import boto3\n        app = Chalice(app_name='james1')\n        ec2 = boto3.client('ec2')\n        @app.lambda_function(name='lambda1')\n        def index():\n            ec2.describe_instances()\n            return {}\n    \"\"\") == {'ec2': set(['describe_instances'])}\n\n\ndef test_can_analyze_schedule():\n    assert chalice_aws_calls(\"\"\"\\\n        from chalice import Chalice\n        import boto3\n        app = Chalice(app_name='james1')\n        s3cli = boto3.client('s3')\n        @app.schedule('rate(1 hour)')\n        def index():\n            s3cli.list_buckets()\n            return {}\n    \"\"\") == {'s3': set(['list_buckets'])}\n\n\ndef test_can_analyze_combination():\n    assert chalice_aws_calls(\"\"\"\\\n        from chalice import Chalice\n        import boto3\n        app = Chalice(app_name='james1')\n        s3 = boto3.client('s3')\n        ec = boto3.client('ec2')\n        @app.route('/')\n        def index():\n            ec2.describe_instances()\n            return {}\n        @app.schedule('rate(1 hour)')\n        def index_sc():\n            s3.list_buckets()\n            return {}\n\n        @app.lambda_function(name='lambda1')\n        def index_lm():\n            ec.describe_instances()\n            return {}\n\n        @random\n        def foo():\n            return {}\n\n    \"\"\") == {'s3': set(['list_buckets']),\n             'ec2': set(['describe_instances'])}\n\n\ndef test_can_handle_dict_comp():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        ddb = boto3.client('dynamodb')\n        tables = {t: t for t in ddb.list_tables()}\n    \"\"\") == {'dynamodb': set(['list_tables'])}\n\n\ndef test_can_handle_dict_comp_if():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        ddb = boto3.client('dynamodb')\n        tables = {t: t for t in [1] if ddb.list_tables()}\n    \"\"\") == {'dynamodb': set(['list_tables'])}\n\n\ndef test_can_handle_comp_ifs():\n    assert aws_calls(\"\"\"\\\n        [(x,y) for x in [1,2,3,4] for y in [1,2,3,4] if x % 2 == 0]\n    \"\"\") == {}\n\n\ndef test_can_handle_dict_comp_ifs():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        d = boto3.client('dynamodb')\n        {x: y for x in d.create_table()\\\n         for y in d.update_table()\\\n         if d.list_tables()}\n        {x: y for x in d.create_table()\\\n         for y in d.update_table()\\\n         if d.list_tables()}\n    \"\"\") == {'dynamodb': set(['list_tables', 'create_table', 'update_table'])}\n\n\n@pytest.mark.skipif(sys.version[0] == '2', reason=(\n    'Async await syntax is not in Python 2'\n))\ndef test_can_handle_async_await():\n    assert aws_calls(\"\"\"\\\n        import boto3\n        import asyncio\n        async def test():\n            d = boto3.client('dynamodb')\n            d.list_tables()\n            await asyncio.sleep(1)\n        test()\n    \"\"\") == {'dynamodb': set(['list_tables'])}\n\n\ndef test_can_analyze_custom_auth():\n    assert chalice_aws_calls(\"\"\"\\\n        from chalice import Chalice\n        import boto3\n\n        ec2 = boto3.client('ec2')\n        app = Chalice(app_name='custom-auth')\n\n        @app.authorizer()\n        def index(auth_request):\n            ec2.describe_instances()\n            return {}\n    \"\"\") == {'ec2': set(['describe_instances'])}\n\n\ndef test_can_analyze_s3_events():\n    assert chalice_aws_calls(\"\"\"\\\n        from chalice import Chalice\n        import boto3\n\n        s3 = boto3.client('s3')\n        app = Chalice(app_name='s3-event')\n\n        @app.on_s3_event(bucket='mybucket')\n        def index(event):\n            s3.list_buckets()\n            return {}\n    \"\"\") == {'s3': set(['list_buckets'])}\n\n\ndef test_can_analyze_sns_events():\n    assert chalice_aws_calls(\"\"\"\\\n        from chalice import Chalice\n        import boto3\n\n        s3 = boto3.client('s3')\n        app = Chalice(app_name='sns-event')\n\n        @app.on_sns_message(topic='mytopic')\n        def index(event):\n            s3.list_buckets()\n            return {}\n    \"\"\") == {'s3': set(['list_buckets'])}\n\n\ndef test_can_analyze_sqs_events():\n    assert chalice_aws_calls(\"\"\"\\\n        from chalice import Chalice\n        import boto3\n\n        s3 = boto3.client('s3')\n        app = Chalice(app_name='sqs-event')\n\n        @app.on_sqs_message(queue='myqueue')\n        def index(event):\n            s3.list_buckets()\n            return {}\n    \"\"\") == {'s3': set(['list_buckets'])}\n\n\ndef test_can_analyze_transfer_manager_methods():\n    assert chalice_aws_calls(\"\"\"\\\n        from chalice import Chalice\n        import boto3\n\n        s3 = boto3.client('s3')\n        app = Chalice(app_name='sqs-event')\n\n        @app.on_s3_event(bucket='mybucket')\n        def index(event):\n            s3.download_file(event.bucket, event.key, 'foo')\n            return {}\n    \"\"\") == {'s3': set(['download_file'])}\n\n\ndef test_can_handle_replacing_function_name():\n    assert chalice_aws_calls(\"\"\"\\\n        from chalice import Chalice\n        import boto3\n\n        app = Chalice(app_name='sqs-event')\n\n        def index():\n            pass\n\n        @app.on_sqs_message(queue='myqueue')\n        def index(event):\n            foo = boto3.client('s3').list_buckets()\n\n    \"\"\") == {'s3': set(['list_buckets'])}\n\n\ndef test_can_handle_multiple_shadowing():\n    assert chalice_aws_calls(\"\"\"\\\n        from chalice import Chalice\n        import boto3\n\n        app = Chalice(app_name='sqs-event')\n\n        def index():\n            pass\n\n        @app.on_sqs_message(queue='myqueue')\n        def index(event):\n            foo = boto3.client('s3').list_buckets()\n\n        @app.on_s3_event(bucket='mybucket')\n        def index(event):\n            bar = boto3.client('s3').head_bucket(Bucket='foo')\n\n    \"\"\") == {'s3': set(['list_buckets', 'head_bucket'])}\n\n\ndef test_can_handle_forward_declaration():\n    assert chalice_aws_calls(\"\"\"\\\n        from chalice import Chalice\n        import boto3\n\n        app = Chalice(app_name='forward-declaration')\n\n        def get_regions():\n            return boto3.client('s3').list_buckets()\n\n        @app.route('/')\n        def index():\n            return get_regions()\n\n    \"\"\") == {'s3': set(['list_buckets'])}\n\n\ndef test_can_handle_post_declaration():\n    assert chalice_aws_calls(\"\"\"\\\n        from chalice import Chalice\n        import boto3\n\n        app = Chalice(app_name='post-declaration')\n\n        @app.route('/')\n        def index():\n            return get_regions()\n\n        def get_regions():\n            return boto3.client('s3').list_buckets()\n\n    \"\"\") == {'s3': set(['list_buckets'])}\n\n\ndef test_can_handle_shadowed_declaration():\n    assert chalice_aws_calls(\"\"\"\\\n        from chalice import Chalice\n        import boto3\n\n        app = Chalice(app_name='shadowed-declaration')\n\n        def get_regions():\n            return boto3.client('s3').list_buckets()\n\n        @app.route('/')\n        def index():\n            return get_regions()\n\n        def get_regions():\n            return boto3.client('s3').head_bucket(Bucket='foo')\n\n    \"\"\") == {'s3': set(['head_bucket'])}\n\n\n# def test_tuple_assignment():\n#     assert aws_calls(\"\"\"\\\n#         import boto3\n#         import some_other_thing\n#         a, d = (1, boto3.client('dynamodb'))\n#         d.list_tables()\n#         d.create_table()\n#     \"\"\") == {'dynamodb': set(['list_tables'])}\n\n\n# def test_multiple_client_assignment():\n#     assert aws_calls(\"\"\"\\\n#         import boto3\n#         import some_other_thing\n#         s3, db = (boto3.client('s3'), boto3.client('dynamodb'))\n#         db.list_tables()\n#         s3.get_object(Bucket='a', Key='b')\n#     \"\"\") == {'dynamodb': set(['list_tables'])\n#             's3': set(['get_object'])}\n\n\n# def test_understands_instance_methods():\n#     assert aws_calls(\"\"\"\\\n#         import boto3, mock\n#         class Foo(object):\n#             def make_call(self, client):\n#                 return client.list_tables()\n#\n#         d = boto3.client('dynamodb')\n#         instance = Foo()\n#         instance.make_call(d)\n#     \"\"\") == {'dynamodb': set(['list_tables'])}\n\n\n# def test_understands_function_and_methods():\n#     assert aws_calls(\"\"\"\\\n#         import boto3, mock\n#         class Foo(object):\n#             def make_call(self, client):\n#                 return foo_call(1, client)\n#\n#         def foo_call(a, client):\n#             return client.list_tables()\n#\n#         d = boto3.client('dynamodb')\n#         instance = Foo()\n#         instance.make_call(d)\n#     \"\"\") == {'dynamodb': set(['list_tables'])}\n\n\n# def test_can_track_across_classes():\n#     assert aws_calls(\"\"\"\\\n#         import boto3\n#         ddb = boto3.client('dynamodb')\n#         class Helper(object):\n#             def __init__(self, client):\n#                 self.client = client\n#             def foo(self):\n#                 return self.client.list_tables()\n#         h = Helper(ddb)\n#         h.foo()\n#     \"\"\") == {'dynamodb': set(['list_tables'])}\n"
  },
  {
    "path": "tests/unit/test_app.py",
    "content": "import sys\nimport base64\nimport logging\nimport json\nimport gzip\nimport inspect\nimport collections\nfrom copy import deepcopy\nfrom datetime import datetime\n\nimport pytest\nfrom pytest import fixture\nimport hypothesis.strategies as st\nfrom hypothesis import given, assume\nimport six\n\nfrom chalice import app\nfrom chalice import NotFoundError\nfrom chalice.test import Client\nfrom chalice.app import (\n    APIGateway,\n    Request,\n    Response,\n    handle_extra_types,\n    MultiDict,\n    WebsocketEvent,\n    BadRequestError,\n    WebsocketDisconnectedError,\n    WebsocketEventSourceHandler,\n    ConvertToMiddleware,\n    WebsocketAPI,\n    ChaliceUnhandledError,\n)\nfrom chalice import __version__ as chalice_version\nfrom chalice.deploy.validate import ExperimentalFeatureError\nfrom chalice.deploy.validate import validate_feature_flags\n\n\n# These are used to generate sample data for hypothesis tests.\nSTR_MAP = st.dictionaries(st.text(), st.text())\nSTR_TO_LIST_MAP = st.dictionaries(\n    st.text(),\n    st.lists(elements=st.text(), min_size=1, max_size=5)\n)\nHTTP_METHOD = st.sampled_from(['GET', 'POST', 'PUT', 'PATCH',\n                               'OPTIONS', 'HEAD', 'DELETE'])\nPATHS = st.sampled_from(['/', '/foo/bar'])\nHTTP_BODY = st.none() | st.text()\nHTTP_REQUEST = st.fixed_dictionaries({\n    'query_params': STR_TO_LIST_MAP,\n    'headers': STR_MAP,\n    'uri_params': STR_MAP,\n    'method': HTTP_METHOD,\n    'body': HTTP_BODY,\n    'context': STR_MAP,\n    'stage_vars': STR_MAP,\n    'is_base64_encoded': st.booleans(),\n    'path': PATHS,\n})\nHTTP_REQUEST = st.fixed_dictionaries({\n    'multiValueQueryStringParameters': st.fixed_dictionaries({}),\n    'headers': STR_MAP,\n    'pathParameters': STR_MAP,\n    'requestContext': st.fixed_dictionaries({\n        'httpMethod': HTTP_METHOD,\n        'resourcePath': PATHS,\n    }),\n    'body': HTTP_BODY,\n    'stageVariables': STR_MAP,\n    'isBase64Encoded': st.booleans(),\n})\nBINARY_TYPES = APIGateway().binary_types\n\n\nclass FakeLambdaContextIdentity(object):\n    def __init__(self, cognito_identity_id, cognito_identity_pool_id):\n        self.cognito_identity_id = cognito_identity_id\n        self.cognito_identity_pool_id = cognito_identity_pool_id\n\n\nclass FakeLambdaContext(object):\n    def __init__(self):\n        self.function_name = 'test_name'\n        self.function_version = 'version'\n        self.invoked_function_arn = 'arn'\n        self.memory_limit_in_mb = 256\n        self.aws_request_id = 'id'\n        self.log_group_name = 'log_group_name'\n        self.log_stream_name = 'log_stream_name'\n        self.identity = FakeLambdaContextIdentity('id', 'id_pool')\n        # client_context is set by the mobile SDK and wont be set for chalice\n        self.client_context = None\n\n    def get_remaining_time_in_millis(self):\n        return 500\n\n    def serialize(self):\n        serialized = {}\n        serialized.update(vars(self))\n        serialized['identity'] = vars(self.identity)\n        return serialized\n\n\nclass FakeGoneException(Exception):\n    pass\n\n\nclass FakeExceptionFactory(object):\n    def __init__(self):\n        self.GoneException = FakeGoneException\n\n\nclass FakeClient(object):\n    def __init__(self, errors=None, infos=None):\n        if errors is None:\n            errors = []\n        if infos is None:\n            infos = []\n        self._errors = errors\n        self._infos = infos\n        self.calls = collections.defaultdict(lambda: [])\n        self.exceptions = FakeExceptionFactory()\n\n    def post_to_connection(self, ConnectionId, Data):\n        self._call('post_to_connection', ConnectionId, Data)\n\n    def delete_connection(self, ConnectionId):\n        self._call('close', ConnectionId)\n\n    def get_connection(self, ConnectionId):\n        self._call('info', ConnectionId)\n        if self._infos is not None:\n            info = self._infos.pop()\n            return info\n\n    def _call(self, name, *args):\n        self.calls[name].append(tuple(args))\n        if self._errors:\n            error = self._errors.pop()\n            raise error\n\n\nclass FakeSession(object):\n    def __init__(self, client=None, region_name='us-west-2'):\n        self.calls = []\n        self._client = client\n        self.region_name = region_name\n\n    def client(self, name, endpoint_url=None):\n        self.calls.append((name, endpoint_url))\n        return self._client\n\n\n@pytest.fixture\ndef view_function():\n    def _func():\n        return {\"hello\": \"world\"}\n\n\ndef create_request_with_content_type(content_type):\n    body = '{\"json\": \"body\"}'\n    event = {\n        'multiValueQueryStringParameters': '',\n        'headers': {'Content-Type': content_type},\n        'pathParameters': {},\n        'requestContext': {\n            'httpMethod': 'GET',\n            'resourcePath': '/',\n        },\n        'body': body,\n        'stageVariables': {},\n        'isBase64Encoded': False,\n    }\n    return app.Request(event, FakeLambdaContext())\n\n\ndef assert_response_body_is(response, body):\n    assert json.loads(response['body']) == body\n\n\ndef json_response_body(response):\n    return json.loads(response['body'])\n\n\ndef assert_requires_opt_in(app, flag):\n    with pytest.raises(ExperimentalFeatureError):\n        validate_feature_flags(app)\n    # Now ensure if we opt in to the feature, we don't\n    # raise an exception.\n    app.experimental_feature_flags.add(flag)\n    try:\n        validate_feature_flags(app)\n    except ExperimentalFeatureError:\n        raise AssertionError(\n            \"Opting in to feature %s still raises an \"\n            \"ExperimentalFeatureError.\" % flag\n        )\n\n\ndef websocket_handler_for_route(route, app):\n    fn = app.websocket_handlers[route].handler_function\n    handler = WebsocketEventSourceHandler(\n        fn, WebsocketEvent, app.websocket_api)\n    return handler\n\n\n@fixture\ndef sample_app():\n    demo = app.Chalice('demo-app')\n\n    @demo.route('/index', methods=['GET'])\n    def index():\n        return {'hello': 'world'}\n\n    @demo.route('/name/{name}', methods=['GET'])\n    def name(name):\n        return {'provided-name': name}\n\n    return demo\n\n\n@fixture\ndef sample_app_with_cors():\n    demo = app.Chalice('demo-app')\n\n    @demo.route('/image', methods=['POST'], cors=True,\n                content_types=['image/gif'])\n    def image():\n        return {'image': True}\n\n    return demo\n\n\n@fixture\ndef sample_app_with_default_cors():\n    demo = app.Chalice('demo-app')\n    demo.api.cors = True\n\n    @demo.route('/on', methods=['POST'],\n                content_types=['image/gif'])\n    def on():\n        return {'image': True}\n\n    @demo.route('/off', methods=['POST'], cors=False,\n                content_types=['image/gif'])\n    def off():\n        return {'image': True}\n\n    @demo.route('/default', methods=['POST'], cors=None,\n                content_types=['image/gif'])\n    def default():\n        return {'image': True}\n\n    return demo\n\n\n@fixture\ndef sample_websocket_app():\n    demo = app.Chalice('app-name')\n    demo.websocket_api.session = FakeSession()\n\n    calls = []\n\n    @demo.on_ws_connect()\n    def connect(event):\n        demo.websocket_api.send(event.connection_id, 'connected')\n        calls.append(('connect', event))\n\n    @demo.on_ws_disconnect()\n    def disconnect(event):\n        demo.websocket_api.send(event.connection_id, 'message')\n        calls.append(('disconnect', event))\n\n    @demo.on_ws_message()\n    def message(event):\n        demo.websocket_api.send(event.connection_id, 'disconnected')\n        calls.append(('default', event))\n\n    return demo, calls\n\n\n@fixture\ndef sample_middleware_app():\n    demo = app.Chalice('app-name')\n    demo.calls = []\n\n    @demo.middleware('all')\n    def mymiddleware(event, get_response):\n        demo.calls.append({'type': 'all',\n                           'event': event.__class__.__name__})\n        return get_response(event)\n\n    @demo.middleware('s3')\n    def mymiddleware_s3(event, get_response):\n        demo.calls.append({'type': 's3',\n                           'event': event.__class__.__name__})\n        return get_response(event)\n\n    @demo.middleware('sns')\n    def mymiddleware_sns(event, get_response):\n        demo.calls.append({'type': 'sns',\n                           'event': event.__class__.__name__})\n        return get_response(event)\n\n    @demo.middleware('http')\n    def mymiddleware_http(event, get_response):\n        demo.calls.append({'type': 'http',\n                           'event': event.__class__.__name__})\n        return get_response(event)\n\n    @demo.middleware('websocket')\n    def mymiddleware_websocket(event, get_response):\n        demo.calls.append({'type': 'websocket',\n                           'event': event.__class__.__name__})\n        return get_response(event)\n\n    @demo.middleware('pure_lambda')\n    def mymiddleware_pure_lambda(event, get_response):\n        demo.calls.append({'type': 'pure_lambda',\n                           'event': event.__class__.__name__})\n        return get_response(event)\n\n    @demo.route('/')\n    def index():\n        return {}\n\n    @demo.on_s3_event(bucket='foo')\n    def s3_handler(event):\n        pass\n\n    @demo.on_sns_message(topic='foo')\n    def sns_handler(event):\n        pass\n\n    @demo.on_sqs_message(queue='foo')\n    def sqs_handler(event):\n        pass\n\n    @demo.lambda_function()\n    def lambda_handler(event, context):\n        pass\n\n    @demo.on_ws_message()\n    def ws_handler(event):\n        pass\n\n    return demo\n\n\n@fixture\ndef auth_request():\n    method_arn = (\n        \"arn:aws:execute-api:us-west-2:123:rest-api-id/dev/GET/needs/auth\")\n    request = app.AuthRequest('TOKEN', 'authtoken', method_arn)\n    return request\n\n\n@pytest.mark.skipif(sys.version[0] == '2',\n                    reason=('Test is irrelevant under python 2, since str and '\n                            'bytes are interchangeable.'))\ndef test_invalid_binary_response_body_throws_value_error(sample_app):\n    response = app.Response(\n        status_code=200,\n        body={'foo': 'bar'},\n        headers={'Content-Type': 'application/octet-stream'}\n    )\n    with pytest.raises(ValueError):\n        response.to_dict(sample_app.api.binary_types)\n\n\ndef test_invalid_JSON_response_body_throws_type_error(sample_app):\n    response = app.Response(\n        status_code=200,\n        body={'foo': object()},\n        headers={'Content-Type': 'application/json'}\n    )\n    with pytest.raises(TypeError):\n        response.to_dict()\n\n\ndef test_can_encode_binary_body_as_base64(sample_app):\n    response = app.Response(\n        status_code=200,\n        body=b'foobar',\n        headers={'Content-Type': 'application/octet-stream'}\n    )\n    encoded_response = response.to_dict(sample_app.api.binary_types)\n    assert encoded_response['body'] == 'Zm9vYmFy'\n\n\ndef test_can_return_unicode_body(sample_app):\n    unicode_data = u'\\u2713'\n    response = app.Response(\n        status_code=200,\n        body=unicode_data\n    )\n    encoded_response = response.to_dict()\n    assert encoded_response['body'] == unicode_data\n\n\ndef test_can_encode_binary_body_with_header_charset(sample_app):\n    response = app.Response(\n        status_code=200,\n        body=b'foobar',\n        headers={'Content-Type': 'application/octet-stream; charset=binary'}\n    )\n    encoded_response = response.to_dict(sample_app.api.binary_types)\n    assert encoded_response['body'] == 'Zm9vYmFy'\n\n\ndef test_can_encode_binary_json(sample_app):\n    sample_app.api.binary_types.extend(['application/json'])\n    response = app.Response(\n        status_code=200,\n        body={'foo': 'bar'},\n        headers={'Content-Type': 'application/json'}\n    )\n    encoded_response = response.to_dict(sample_app.api.binary_types)\n    assert encoded_response['body'] == 'eyJmb28iOiJiYXIifQ=='\n\n\ndef test_wildcard_accepts_with_native_python_types_serializes_json(\n        sample_app, create_event):\n    sample_app.api.binary_types = ['*/*']\n\n    @sample_app.route('/py-dict')\n    def py_dict():\n        return {'foo': 'bar'}\n\n    event = create_event('/py-dict', 'GET', {})\n    event['headers']['Accept'] = '*/*'\n    response = sample_app(event, context=None)\n    # In this case, they've return a native python dict type, which should\n    # be serialized to JSON and returned back to the user as JSON.  Because\n    # we also have ``*/*`` as a binary type, we'll return the response\n    # as a binary response type.\n    assert base64.b64decode(response['body']) == b'{\"foo\":\"bar\"}'\n    assert response['isBase64Encoded']\n\n\ndef test_wildcard_accepts_with_response_class(\n        sample_app, create_event):\n    sample_app.api.binary_types = ['*/*']\n\n    @sample_app.route('/py-dict')\n    def py_dict():\n        return Response(body=json.dumps({'foo': 'bar'}).encode('utf-8'),\n                        headers={'Content-Type': 'application/json'},\n                        status_code=200)\n\n    event = create_event('/py-dict', 'GET', {})\n    event['headers']['Accept'] = '*/*'\n    response = sample_app(event, context=None)\n    # Because our binary types is '*/*' we should be returning this\n    # content as binary.\n    assert base64.b64decode(response['body']) == b'{\"foo\": \"bar\"}'\n    assert response['isBase64Encoded']\n\n\ndef test_can_parse_route_view_args():\n    entry = app.RouteEntry(lambda: {\"foo\": \"bar\"}, 'view-name',\n                           '/foo/{bar}/baz/{qux}', method='GET')\n    assert entry.view_args == ['bar', 'qux']\n\n\ndef test_can_route_single_view():\n    demo = app.Chalice('app-name')\n\n    @demo.route('/index')\n    def index_view():\n        return {}\n\n    assert demo.routes['/index']['GET'] == app.RouteEntry(\n        index_view, 'index_view', '/index', 'GET',\n        content_types=['application/json'])\n\n\ndef test_can_handle_multiple_routes():\n    demo = app.Chalice('app-name')\n\n    @demo.route('/index')\n    def index_view():\n        return {}\n\n    @demo.route('/other')\n    def other_view():\n        return {}\n\n    assert len(demo.routes) == 2, demo.routes\n    assert '/index' in demo.routes, demo.routes\n    assert '/other' in demo.routes, demo.routes\n    assert demo.routes['/index']['GET'].view_function == index_view\n    assert demo.routes['/other']['GET'].view_function == other_view\n\n\ndef test_error_on_unknown_event(sample_app):\n    bad_event = {'random': 'event'}\n    raw_response = sample_app(bad_event, context=None)\n    assert raw_response['statusCode'] == 500\n    assert json_response_body(raw_response)['Code'] == 'InternalServerError'\n\n\ndef test_can_route_api_call_to_view_function(sample_app, create_event):\n    event = create_event('/index', 'GET', {})\n    response = sample_app(event, context=None)\n    assert_response_body_is(response, {'hello': 'world'})\n\n\ndef test_can_call_to_dict_on_current_request(sample_app, create_event):\n    @sample_app.route('/todict')\n    def todict():\n        return sample_app.current_request.to_dict()\n    event = create_event('/todict', 'GET', {})\n    response = json_response_body(sample_app(event, context=None))\n    assert isinstance(response, dict)\n    # The dict can change over time so we'll just pick\n    # out a few keys as a basic sanity test.\n    assert response['method'] == 'GET'\n    # We also want to verify that to_dict() is always\n    # JSON serializable so we check we can roundtrip\n    # the data to/from JSON.\n    assert isinstance(json.loads(json.dumps(response)), dict)\n\n\ndef test_can_call_to_dict_on_request_with_querystring(sample_app,\n                                                      create_event):\n    @sample_app.route('/todict')\n    def todict():\n        return sample_app.current_request.to_dict()\n\n    event = create_event('/todict', 'GET', {})\n    event['multiValueQueryStringParameters'] = {\n        'key': ['val1', 'val2'],\n        'key2': ['val']\n    }\n    response = json_response_body(sample_app(event, context=None))\n    assert isinstance(response, dict)\n    # The dict can change over time so we'll just pick\n    # out a few keys as a basic sanity test.\n    assert response['method'] == 'GET'\n    assert response['query_params'] is not None\n    assert response['query_params']['key'] == 'val2'\n    assert response['query_params']['key2'] == 'val'\n    # We also want to verify that to_dict() is always\n    # JSON serializable so we check we can roundtrip\n    # the data to/from JSON.\n    assert isinstance(json.loads(json.dumps(response)), dict)\n\n\ndef test_request_to_dict_does_not_contain_internal_attrs(sample_app,\n                                                         create_event):\n    @sample_app.route('/todict')\n    def todict():\n        return sample_app.current_request.to_dict()\n    event = create_event('/todict', 'GET', {})\n    response = json_response_body(sample_app(event, context=None))\n    internal_attrs = [key for key in response if key.startswith('_')]\n    assert not internal_attrs\n\n\ndef test_will_pass_captured_params_to_view(sample_app, create_event):\n    event = create_event('/name/{name}', 'GET', {'name': 'james'})\n    response = sample_app(event, context=None)\n    response = json_response_body(response)\n    assert response == {'provided-name': 'james'}\n\n\ndef test_error_on_unsupported_method(sample_app, create_event):\n    event = create_event('/name/{name}', 'POST', {'name': 'james'})\n    raw_response = sample_app(event, context=None)\n    assert raw_response['statusCode'] == 405\n    assert raw_response['headers']['Allow'] == 'GET'\n    assert json_response_body(raw_response)['Code'] == 'MethodNotAllowedError'\n\n\ndef test_error_on_unsupported_method_gives_feedback_on_method(sample_app,\n                                                              create_event):\n    method = 'POST'\n    event = create_event('/name/{name}', method, {'name': 'james'})\n    raw_response = sample_app(event, context=None)\n    assert 'POST' in json_response_body(raw_response)['Message']\n\n\ndef test_error_contains_cors_headers(sample_app_with_cors, create_event):\n    event = create_event('/image', 'POST', {'not': 'image'})\n    raw_response = sample_app_with_cors(event, context=None)\n    assert raw_response['statusCode'] == 415\n    assert 'Access-Control-Allow-Origin' in raw_response['headers']\n\n\nclass TestDefaultCORS(object):\n    def test_cors_enabled(self, sample_app_with_default_cors, create_event):\n        event = create_event('/on', 'POST', {'not': 'image'})\n        raw_response = sample_app_with_default_cors(event, context=None)\n        assert raw_response['statusCode'] == 415\n        assert 'Access-Control-Allow-Origin' in raw_response['headers']\n\n    def test_cors_none(self, sample_app_with_default_cors, create_event):\n        event = create_event('/default', 'POST', {'not': 'image'})\n        raw_response = sample_app_with_default_cors(event, context=None)\n        assert raw_response['statusCode'] == 415\n        assert 'Access-Control-Allow-Origin' in raw_response['headers']\n\n    def test_cors_disabled(self, sample_app_with_default_cors, create_event):\n        event = create_event('/off', 'POST', {'not': 'image'})\n        raw_response = sample_app_with_default_cors(event, context=None)\n        assert raw_response['statusCode'] == 415\n        assert 'Access-Control-Allow-Origin' not in raw_response['headers']\n\n\ndef test_can_access_context(create_event):\n    demo = app.Chalice('app-name')\n\n    @demo.route('/index')\n    def index_view():\n        serialized = demo.lambda_context.serialize()\n        return serialized\n\n    event = create_event('/index', 'GET', {})\n    lambda_context = FakeLambdaContext()\n    result = demo(event, lambda_context)\n    result = json_response_body(result)\n    serialized_lambda_context = lambda_context.serialize()\n    assert result == serialized_lambda_context\n\n\ndef test_can_access_raw_body(create_event):\n    demo = app.Chalice('app-name')\n\n    @demo.route('/index')\n    def index_view():\n        return {'rawbody': demo.current_request.raw_body.decode('utf-8')}\n\n    event = create_event('/index', 'GET', {})\n    event['body'] = '{\"hello\": \"world\"}'\n    result = demo(event, context=None)\n    result = json_response_body(result)\n    assert result == {'rawbody': '{\"hello\": \"world\"}'}\n\n\ndef test_raw_body_cache_returns_same_result(create_event):\n    demo = app.Chalice('app-name')\n\n    @demo.route('/index')\n    def index_view():\n        # The first raw_body decodes base64,\n        # the second value should return the cached value.\n        # Both should be the same value\n        return {'rawbody': demo.current_request.raw_body.decode('utf-8'),\n                'rawbody2': demo.current_request.raw_body.decode('utf-8')}\n\n    event = create_event('/index', 'GET', {})\n    event['base64-body'] = base64.b64encode(\n        b'{\"hello\": \"world\"}').decode('ascii')\n\n    result = demo(event, context=None)\n    result = json_response_body(result)\n    assert result['rawbody'] == result['rawbody2']\n\n\ndef test_can_have_views_of_same_route_but_different_methods(create_event):\n    demo = app.Chalice('app-name')\n\n    @demo.route('/index', methods=['GET'])\n    def get_view():\n        return {'method': 'GET'}\n\n    @demo.route('/index', methods=['PUT'])\n    def put_view():\n        return {'method': 'PUT'}\n\n    assert demo.routes['/index']['GET'].view_function == get_view\n    assert demo.routes['/index']['PUT'].view_function == put_view\n\n    event = create_event('/index', 'GET', {})\n    result = demo(event, context=None)\n    assert json_response_body(result) == {'method': 'GET'}\n\n    event = create_event('/index', 'PUT', {})\n    result = demo(event, context=None)\n    assert json_response_body(result) == {'method': 'PUT'}\n\n\ndef test_error_on_duplicate_route_methods():\n    demo = app.Chalice('app-name')\n\n    @demo.route('/index', methods=['PUT'])\n    def index_view():\n        return {'foo': 'bar'}\n\n    with pytest.raises(ValueError):\n        @demo.route('/index', methods=['PUT'])\n        def index_view_dup():\n            return {'foo': 'bar'}\n\n\ndef test_json_body_available_with_right_content_type(create_event):\n    demo = app.Chalice('demo-app')\n\n    @demo.route('/', methods=['POST'])\n    def index():\n        return demo.current_request.json_body\n\n    event = create_event('/', 'POST', {})\n    event['body'] = json.dumps({'foo': 'bar'})\n\n    result = demo(event, context=None)\n    result = json_response_body(result)\n    assert result == {'foo': 'bar'}\n\n\ndef test_json_body_none_with_malformed_json(create_event):\n    demo = app.Chalice('demo-app')\n\n    @demo.route('/', methods=['POST'])\n    def index():\n        return demo.current_request.json_body\n\n    event = create_event('/', 'POST', {})\n    event['body'] = '{\"foo\": \"bar\"'\n\n    result = demo(event, context=None)\n    assert result['statusCode'] == 400\n    assert json_response_body(result)['Code'] == 'BadRequestError'\n\n\ndef test_cant_access_json_body_with_wrong_content_type(create_event):\n    demo = app.Chalice('demo-app')\n\n    @demo.route('/', methods=['POST'], content_types=['application/xml'])\n    def index():\n        return (demo.current_request.json_body,\n                demo.current_request.raw_body.decode('utf-8'))\n\n    event = create_event('/', 'POST', {}, content_type='application/xml')\n    event['body'] = '<Message>hello</Message>'\n\n    response = json_response_body(demo(event, context=None))\n    json_body, raw_body = response\n    assert json_body is None\n    assert raw_body == '<Message>hello</Message>'\n\n\ndef test_json_body_available_on_multiple_content_types(create_event_with_body):\n    demo = app.Chalice('demo-app')\n\n    @demo.route('/', methods=['POST'],\n                content_types=['application/xml', 'application/json'])\n    def index():\n        return (demo.current_request.json_body,\n                demo.current_request.raw_body.decode('utf-8'))\n\n    event = create_event_with_body('<Message>hello</Message>',\n                                   content_type='application/xml')\n\n    response = json_response_body(demo(event, context=None))\n    json_body, raw_body = response\n    assert json_body is None\n    assert raw_body == '<Message>hello</Message>'\n\n    # Now if we create an event with JSON, we should be able\n    # to access .json_body as well.\n    event = create_event_with_body({'foo': 'bar'},\n                                   content_type='application/json')\n    response = json_response_body(demo(event, context=None))\n    json_body, raw_body = response\n    assert json_body == {'foo': 'bar'}\n    assert raw_body == '{\"foo\": \"bar\"}'\n\n\ndef test_json_body_available_with_lowercase_content_type_key(\n        create_event_with_body):\n    demo = app.Chalice('demo-app')\n\n    @demo.route('/', methods=['POST'])\n    def index():\n        return (demo.current_request.json_body,\n                demo.current_request.raw_body.decode('utf-8'))\n\n    event = create_event_with_body({'foo': 'bar'})\n    del event['headers']['Content-Type']\n    event['headers']['content-type'] = 'application/json'\n\n    json_body, raw_body = json_response_body(demo(event, context=None))\n    assert json_body == {'foo': 'bar'}\n    assert raw_body == '{\"foo\": \"bar\"}'\n\n\ndef test_content_types_must_be_lists():\n    demo = app.Chalice('app-name')\n\n    with pytest.raises(ValueError):\n        @demo.route('/index', content_types='application/not-a-list')\n        def index_post():\n            return {'foo': 'bar'}\n\n\ndef test_content_type_validation_raises_error_on_unknown_types(create_event):\n    demo = app.Chalice('demo-app')\n\n    @demo.route('/', methods=['POST'], content_types=['application/xml'])\n    def index():\n        return \"success\"\n\n    bad_content_type = 'application/bad-xml'\n    event = create_event('/', 'POST', {}, content_type=bad_content_type)\n    event['body'] = 'Request body'\n\n    json_response = json_response_body(demo(event, context=None))\n    assert json_response['Code'] == 'UnsupportedMediaType'\n    assert 'application/bad-xml' in json_response['Message']\n\n\ndef test_content_type_with_charset(create_event):\n    demo = app.Chalice('demo-app')\n\n    @demo.route('/', content_types=['application/json'])\n    def index():\n        return {'foo': 'bar'}\n\n    event = create_event('/', 'GET', {}, 'application/json; charset=utf-8')\n    response = json_response_body(demo(event, context=None))\n    assert response == {'foo': 'bar'}\n\n\ndef test_can_return_response_object(create_event):\n    demo = app.Chalice('app-name')\n\n    @demo.route('/index')\n    def index_view():\n        return app.Response(\n            status_code=200,\n            body={'foo': 'bar'},\n            headers={\n                'Content-Type': 'application/json',\n                'Set-Cookie': ['key=value', 'foo=bar'],\n            },\n        )\n\n    event = create_event('/index', 'GET', {})\n    response = demo(event, context=None)\n    assert response == {\n        'statusCode': 200,\n        'body': '{\"foo\":\"bar\"}',\n\n        'headers': {'Content-Type': 'application/json'},\n        'multiValueHeaders': {'Set-Cookie': ['key=value', 'foo=bar']},\n    }\n\n\ndef test_headers_have_basic_validation(create_event):\n    demo = app.Chalice('app-name')\n\n    @demo.route('/index')\n    def index_view():\n        return app.Response(\n            status_code=200, body='{}',\n            headers={'Invalid-Header': 'foo\\nbar'})\n\n    event = create_event('/index', 'GET', {})\n    response = demo(event, context=None)\n    assert response['statusCode'] == 500\n    assert 'Invalid-Header' not in response['headers']\n    assert json.loads(response['body'])['Code'] == 'InternalServerError'\n\n\ndef test_empty_headers_have_basic_validation(create_empty_header_event):\n    demo = app.Chalice('app-name')\n\n    @demo.route('/index')\n    def index_view():\n        return app.Response(\n            status_code=200, body='{}', headers={})\n\n    event = create_empty_header_event('/index', 'GET', {})\n    response = demo(event, context=None)\n    assert response['statusCode'] == 200\n\n\ndef test_no_content_type_is_still_allowed(create_event):\n    # When the content type validation happens in API gateway, it appears\n    # to assume a default of application/json, so the chalice handler needs\n    # to emulate that behavior.\n\n    demo = app.Chalice('demo-app')\n\n    @demo.route('/', methods=['POST'], content_types=['application/json'])\n    def index():\n        return {'success': True}\n\n    event = create_event('/', 'POST', {})\n    del event['headers']['Content-Type']\n\n    json_response = json_response_body(demo(event, context=None))\n    assert json_response == {'success': True}\n\n\n@pytest.mark.parametrize('content_type,accept', [\n    ('application/octet-stream', 'application/octet-stream'),\n    (\n        'application/octet-stream', (\n            'text/html,application/xhtml+xml,application/xml'\n            ';q=0.9,image/webp,*/*;q=0.8'\n        )\n    ),\n    ('image/gif', 'text/html,image/gif'),\n    ('image/gif', 'text/html ,image/gif'),\n    ('image/gif', 'text/html, image/gif'),\n    ('image/gif', 'text/html;q=0.8, image/gif ;q=0.5'),\n    ('image/gif', 'text/html,image/png'),\n    ('image/png', 'text/html,image/gif'),\n])\ndef test_can_base64_encode_binary_multiple_media_types(\n        create_event, content_type, accept):\n    demo = app.Chalice('demo-app')\n\n    @demo.route('/index')\n    def index_view():\n        return app.Response(\n            status_code=200,\n            body=u'\\u2713'.encode('utf-8'),\n            headers={'Content-Type': content_type})\n\n    event = create_event('/index', 'GET', {})\n    event['headers']['Accept'] = accept\n    response = demo(event, context=None)\n    assert response['statusCode'] == 200\n    assert response['isBase64Encoded'] is True\n    assert response['body'] == '4pyT'\n    assert response['headers']['Content-Type'] == content_type\n\n\ndef test_can_return_text_even_with_binary_content_type_configured(\n        create_event):\n    demo = app.Chalice('demo-app')\n\n    @demo.route('/index')\n    def index_view():\n        return app.Response(\n            status_code=200,\n            body='Plain text',\n            headers={'Content-Type': 'text/plain'})\n\n    event = create_event('/index', 'GET', {})\n    event['headers']['Accept'] = 'application/octet-stream'\n    response = demo(event, context=None)\n    assert response['statusCode'] == 200\n    assert response['body'] == 'Plain text'\n    assert response['headers']['Content-Type'] == 'text/plain'\n\n\ndef test_route_equality(view_function):\n    a = app.RouteEntry(\n        view_function,\n        view_name='myview', path='/',\n        method='GET',\n        api_key_required=True,\n        content_types=['application/json'],\n    )\n    b = app.RouteEntry(\n        view_function,\n        view_name='myview', path='/',\n        method='GET',\n        api_key_required=True,\n        content_types=['application/json'],\n    )\n    assert a == b\n\n\ndef test_route_inequality(view_function):\n    a = app.RouteEntry(\n        view_function,\n        view_name='myview', path='/',\n        method='GET',\n        api_key_required=True,\n        content_types=['application/json'],\n    )\n    b = app.RouteEntry(\n        view_function,\n        view_name='myview', path='/',\n        method='GET',\n        api_key_required=True,\n        # Different content types\n        content_types=['application/xml'],\n    )\n    assert not a == b\n\n\ndef test_exceptions_raised_as_chalice_errors(sample_app, create_event):\n\n    @sample_app.route('/error')\n    def raise_error():\n        raise TypeError(\"Raising arbitrary error, should never see.\")\n\n    event = create_event('/error', 'GET', {})\n    # This is intentional behavior.  If we're not in debug mode\n    # we don't want to surface internal errors that get raised.\n    # We should reply with a general internal server error.\n    raw_response = sample_app(event, context=None)\n    response = json_response_body(raw_response)\n    assert response['Code'] == 'InternalServerError'\n    assert raw_response['statusCode'] == 500\n\n\ndef test_original_exception_raised_in_debug_mode(sample_app, create_event):\n    sample_app.debug = True\n\n    @sample_app.route('/error')\n    def raise_error():\n        raise ValueError(\"You will see this error\")\n\n    event = create_event('/error', 'GET', {})\n    response = sample_app(event, context=None)\n    # In debug mode, we let the original exception propagate.\n    # This includes the original type as well as the message.\n    assert response['statusCode'] == 500\n    assert 'ValueError' in response['body']\n    assert 'You will see this error' in response['body']\n\n\ndef test_chalice_view_errors_propagate_in_non_debug_mode(sample_app,\n                                                         create_event):\n    @sample_app.route('/notfound')\n    def notfound():\n        raise NotFoundError(\"resource not found\")\n\n    event = create_event('/notfound', 'GET', {})\n    raw_response = sample_app(event, context=None)\n    assert raw_response['statusCode'] == 404\n    assert json_response_body(raw_response)['Code'] == 'NotFoundError'\n\n\ndef test_chalice_view_errors_propagate_in_debug_mode(sample_app, create_event):\n    @sample_app.route('/notfound')\n    def notfound():\n        raise NotFoundError(\"resource not found\")\n    sample_app.debug = True\n\n    event = create_event('/notfound', 'GET', {})\n    raw_response = sample_app(event, context=None)\n    assert raw_response['statusCode'] == 404\n    assert json_response_body(raw_response)['Code'] == 'NotFoundError'\n\n\ndef test_case_insensitive_mapping():\n    mapping = app.CaseInsensitiveMapping({'HEADER': 'Value'})\n\n    assert mapping['hEAdEr']\n    assert mapping.get('hEAdEr')\n    assert 'hEAdEr' in mapping\n    assert repr({'header': 'Value'}) in repr(mapping)\n\n\ndef test_unknown_kwargs_raise_error(sample_app, create_event):\n    with pytest.raises(TypeError):\n        @sample_app.route('/foo', unknown_kwargs='foo')\n        def badkwargs():\n            pass\n\n\ndef test_name_kwargs_does_not_raise_error(sample_app):\n    try:\n        @sample_app.route('/foo', name='foo')\n        def name_kwarg():\n            pass\n    except TypeError:\n        pytest.fail('route name kwarg should not raise TypeError.')\n\n\ndef test_default_logging_handlers_created():\n    handlers_before = logging.getLogger('log_app').handlers[:]\n    # configure_logs = True is the default, but we're\n    # being explicit here.\n    app.Chalice('log_app', configure_logs=True)\n    handlers_after = logging.getLogger('log_app').handlers[:]\n    new_handlers = set(handlers_after) - set(handlers_before)\n    # Should have added a new handler\n    assert len(new_handlers) == 1\n\n\ndef test_default_logging_only_added_once():\n    # And creating the same app object means we shouldn't\n    # configure logging again.\n    handlers_before = logging.getLogger('added_once').handlers[:]\n    app.Chalice('added_once', configure_logs=True)\n    # The same app name, we should still only configure logs\n    # once.\n    app.Chalice('added_once', configure_logs=True)\n    handlers_after = logging.getLogger('added_once').handlers[:]\n    new_handlers = set(handlers_after) - set(handlers_before)\n    # Should have added a new handler\n    assert len(new_handlers) == 1\n\n\ndef test_logs_can_be_disabled():\n    handlers_before = logging.getLogger('log_app').handlers[:]\n    app.Chalice('log_app', configure_logs=False)\n    handlers_after = logging.getLogger('log_app').handlers[:]\n    new_handlers = set(handlers_after) - set(handlers_before)\n    assert len(new_handlers) == 0\n\n\n@pytest.mark.parametrize('content_type,is_json', [\n    ('application/json', True),\n    ('application/json;charset=UTF-8', True),\n    ('application/notjson', False),\n])\ndef test_json_body_available_when_content_type_matches(content_type, is_json):\n    request = create_request_with_content_type(content_type)\n    if is_json:\n        assert request.json_body == {'json': 'body'}\n    else:\n        assert request.json_body is None\n\n\ndef test_can_receive_binary_data(create_event_with_body):\n    content_type = 'application/octet-stream'\n    demo = app.Chalice('demo-app')\n\n    @demo.route('/bincat', methods=['POST'], content_types=[content_type])\n    def bincat():\n        raw_body = demo.current_request.raw_body\n        return app.Response(\n            raw_body,\n            headers={'Content-Type': content_type},\n            status_code=200)\n\n    body = 'L3UyNzEz'\n    event = create_event_with_body(body, '/bincat', 'POST', content_type)\n    event['headers']['Accept'] = content_type\n    event['isBase64Encoded'] = True\n    response = demo(event, context=None)\n\n    assert response['statusCode'] == 200\n    assert response['body'] == body\n\n\ndef test_cannot_receive_base64_string_with_binary_response(\n        create_event_with_body):\n    content_type = 'application/octet-stream'\n    demo = app.Chalice('demo-app')\n\n    @demo.route('/bincat', methods=['GET'], content_types=[content_type])\n    def bincat():\n        return app.Response(\n            status_code=200,\n            body=u'\\u2713'.encode('utf-8'),\n            headers={'Content-Type': content_type})\n\n    event = create_event_with_body('', '/bincat', 'GET', content_type)\n    response = demo(event, context=None)\n\n    assert response['statusCode'] == 400\n\n\ndef test_can_serialize_cognito_auth():\n    auth = app.CognitoUserPoolAuthorizer(\n        'Name', provider_arns=['Foo'], header='Authorization')\n    assert auth.to_swagger() == {\n        'in': 'header',\n        'type': 'apiKey',\n        'name': 'Authorization',\n        'x-amazon-apigateway-authtype': 'cognito_user_pools',\n        'x-amazon-apigateway-authorizer': {\n            'type': 'cognito_user_pools',\n            'providerARNs': ['Foo'],\n        }\n    }\n\n\ndef test_can_serialize_iam_auth():\n    auth = app.IAMAuthorizer()\n    assert auth.to_swagger() == {\n            'in': 'header',\n            'type': 'apiKey',\n            'name': 'Authorization',\n            'x-amazon-apigateway-authtype': 'awsSigv4',\n        }\n\n\ndef test_typecheck_list_type():\n    with pytest.raises(TypeError):\n        app.CognitoUserPoolAuthorizer('Name', 'Authorization',\n                                      provider_arns='foo')\n\n\ndef test_can_serialize_custom_authorizer():\n    auth = app.CustomAuthorizer(\n        'Name', 'myuri', ttl_seconds=10, header='NotAuth',\n        invoke_role_arn='role-arn'\n    )\n    assert auth.to_swagger() == {\n        'in': 'header',\n        'type': 'apiKey',\n        'name': 'NotAuth',\n        'x-amazon-apigateway-authtype': 'custom',\n        'x-amazon-apigateway-authorizer': {\n            'type': 'token',\n            'authorizerUri': 'myuri',\n            'authorizerResultTtlInSeconds': 10,\n            'authorizerCredentials': 'role-arn',\n        }\n    }\n\n\nclass TestCORSConfig(object):\n    def test_eq(self):\n        cors_config = app.CORSConfig()\n        other_cors_config = app.CORSConfig()\n        assert cors_config == other_cors_config\n\n    def test_not_eq_different_type(self):\n        cors_config = app.CORSConfig()\n        different_type_obj = object()\n        assert not cors_config == different_type_obj\n\n    def test_not_eq_differing_configurations(self):\n        cors_config = app.CORSConfig()\n        differing_cors_config = app.CORSConfig(\n            allow_origin='https://foo.example.com')\n        assert cors_config != differing_cors_config\n\n    def test_eq_non_default_configurations(self):\n        custom_cors = app.CORSConfig(\n            allow_origin='https://foo.example.com',\n            allow_headers=['X-Special-Header'],\n            max_age=600,\n            expose_headers=['X-Special-Header'],\n            allow_credentials=True\n        )\n        same_custom_cors = app.CORSConfig(\n            allow_origin='https://foo.example.com',\n            allow_headers=['X-Special-Header'],\n            max_age=600,\n            expose_headers=['X-Special-Header'],\n            allow_credentials=True\n        )\n        assert custom_cors == same_custom_cors\n\n\ndef test_can_handle_builtin_auth():\n    demo = app.Chalice('builtin-auth')\n\n    @demo.authorizer()\n    def my_auth(auth_request):\n        pass\n\n    @demo.route('/', authorizer=my_auth)\n    def index_view():\n        return {}\n\n    assert len(demo.builtin_auth_handlers) == 1\n    authorizer = demo.builtin_auth_handlers[0]\n    assert isinstance(authorizer, app.BuiltinAuthConfig)\n    assert authorizer.name == 'my_auth'\n    assert authorizer.handler_string == 'app.my_auth'\n\n\ndef test_builtin_auth_can_transform_event():\n    event = {\n        'type': 'TOKEN',\n        'authorizationToken': 'authtoken',\n        'methodArn': 'arn:aws:execute-api:...:foo',\n    }\n    auth_app = app.Chalice('builtin-auth')\n\n    request = []\n\n    @auth_app.authorizer()\n    def builtin_auth(auth_request):\n        request.append(auth_request)\n\n    builtin_auth(event, None)\n\n    assert len(request) == 1\n    transformed = request[0]\n    assert transformed.auth_type == 'TOKEN'\n    assert transformed.token == 'authtoken'\n    assert transformed.method_arn == 'arn:aws:execute-api:...:foo'\n\n\ndef test_can_return_auth_dict_directly():\n    # A user can bypass our AuthResponse and return the auth response\n    # dict that API gateway expects.\n    event = {\n        'type': 'TOKEN',\n        'authorizationToken': 'authtoken',\n        'methodArn': 'arn:aws:execute-api:...:foo',\n    }\n    auth_app = app.Chalice('builtin-auth')\n\n    response = {\n        'context': {'foo': 'bar'},\n        'principalId': 'user',\n        'policyDocument': {\n            'Version': '2012-10-17',\n            'Statement': []\n        }\n    }\n\n    @auth_app.authorizer()\n    def builtin_auth(auth_request):\n        return response\n\n    actual = builtin_auth(event, None)\n    assert actual == response\n\n\ndef test_can_specify_extra_auth_attributes():\n    auth_app = app.Chalice('builtin-auth')\n\n    @auth_app.authorizer(ttl_seconds=10, execution_role='arn:my-role')\n    def builtin_auth(auth_request):\n        pass\n\n    handler = auth_app.builtin_auth_handlers[0]\n    assert handler.ttl_seconds == 10\n    assert handler.execution_role == 'arn:my-role'\n\n\ndef test_validation_raised_on_unknown_kwargs():\n    auth_app = app.Chalice('builtin-auth')\n\n    with pytest.raises(TypeError):\n        @auth_app.authorizer(this_is_an_unknown_kwarg=True)\n        def builtin_auth(auth_request):\n            pass\n\n\ndef test_can_return_auth_response():\n    event = {\n        'type': 'TOKEN',\n        'authorizationToken': 'authtoken',\n        'methodArn': 'arn:aws:execute-api:us-west-2:1:id/dev/GET/a',\n    }\n    auth_app = app.Chalice('builtin-auth')\n\n    response = {\n        'context': {},\n        'principalId': 'principal',\n        'policyDocument': {\n            'Version': '2012-10-17',\n            'Statement': [\n                {'Action': 'execute-api:Invoke',\n                 'Effect': 'Allow',\n                 'Resource': [\n                     'arn:aws:execute-api:us-west-2:1:id/dev/*/a'\n                 ]}\n            ]\n        }\n    }\n\n    @auth_app.authorizer()\n    def builtin_auth(auth_request):\n        return app.AuthResponse(['/a'], 'principal')\n\n    actual = builtin_auth(event, None)\n    assert actual == response\n\n\ndef test_auth_response_with_colon_chars():\n    event = {\n        'type': 'TOKEN',\n        'authorizationToken': 'authtoken',\n        'methodArn': 'arn:aws:execute-api:us-west-2:1:id/api/GET/foo/a:b:c:d',\n    }\n    auth_app = app.Chalice('builtin-auth')\n\n    response = {\n        'context': {},\n        'principalId': 'principal',\n        'policyDocument': {\n            'Version': '2012-10-17',\n            'Statement': [\n                {'Action': 'execute-api:Invoke',\n                 'Effect': 'Allow',\n                 'Resource': [\n                     'arn:aws:execute-api:us-west-2:1:id/api/*/foo/*'\n                 ]}\n            ]\n        }\n    }\n\n    @auth_app.authorizer()\n    def builtin_auth(auth_request):\n        return app.AuthResponse(['/foo/*'], 'principal')\n\n    actual = builtin_auth(event, None)\n    assert actual == response\n\n\ndef test_auth_response_serialization():\n    method_arn = (\n        \"arn:aws:execute-api:us-west-2:123:rest-api-id/dev/GET/needs/auth\")\n    request = app.AuthRequest('TOKEN', 'authtoken', method_arn)\n    response = app.AuthResponse(routes=['/needs/auth'], principal_id='foo')\n    response_dict = response.to_dict(request)\n    expected = [method_arn.replace('GET', '*')]\n    assert response_dict == {\n        'policyDocument': {\n            'Version': '2012-10-17',\n            'Statement': [\n                {\n                    'Action': 'execute-api:Invoke',\n                    'Resource': expected,\n                    'Effect': 'Allow'\n                }\n            ]\n        },\n        'context': {},\n        'principalId': 'foo',\n    }\n\n\ndef test_auth_response_can_include_context(auth_request):\n    response = app.AuthResponse(['/foo'], 'principal', {'foo': 'bar'})\n    serialized = response.to_dict(auth_request)\n    assert serialized['context'] == {'foo': 'bar'}\n\n\ndef test_can_use_auth_routes_instead_of_strings(auth_request):\n    expected = [\n        \"arn:aws:execute-api:us-west-2:123:rest-api-id/dev/GET/a\",\n        \"arn:aws:execute-api:us-west-2:123:rest-api-id/dev/GET/a/b\",\n        \"arn:aws:execute-api:us-west-2:123:rest-api-id/dev/POST/a/b\",\n    ]\n    response = app.AuthResponse(\n        [app.AuthRoute('/a', ['GET']),\n         app.AuthRoute('/a/b', ['GET', 'POST'])],\n        'principal')\n    serialized = response.to_dict(auth_request)\n    assert serialized['policyDocument'] == {\n        'Version': '2012-10-17',\n        'Statement': [{\n            'Action': 'execute-api:Invoke',\n            'Effect': 'Allow',\n            'Resource': expected,\n        }]\n    }\n\n\ndef test_auth_response_wildcard(auth_request):\n    response = app.AuthResponse(\n        routes=[app.AuthRoute(path='*', methods=['*'])],\n        principal_id='user')\n    serialized = response.to_dict(auth_request)\n    assert serialized['policyDocument'] == {\n        'Statement': [\n            {'Action': 'execute-api:Invoke',\n             'Effect': 'Allow',\n             'Resource': [\n                 'arn:aws:execute-api:us-west-2:123:rest-api-id/dev/*/*']}],\n        'Version': '2012-10-17'\n    }\n\n\ndef test_auth_response_wildcard_string(auth_request):\n    response = app.AuthResponse(\n        routes=['*'], principal_id='user')\n    serialized = response.to_dict(auth_request)\n    assert serialized['policyDocument'] == {\n        'Statement': [\n            {'Action': 'execute-api:Invoke',\n             'Effect': 'Allow',\n             'Resource': [\n                 'arn:aws:execute-api:us-west-2:123:rest-api-id/dev/*/*']}],\n        'Version': '2012-10-17'\n    }\n\n\ndef test_can_mix_auth_routes_and_strings(auth_request):\n    expected = [\n        'arn:aws:execute-api:us-west-2:123:rest-api-id/dev/*/a',\n        'arn:aws:execute-api:us-west-2:123:rest-api-id/dev/GET/a/b',\n    ]\n    response = app.AuthResponse(\n        ['/a', app.AuthRoute('/a/b', ['GET'])],\n        'principal')\n    serialized = response.to_dict(auth_request)\n    assert serialized['policyDocument'] == {\n        'Version': '2012-10-17',\n        'Statement': [{\n            'Action': 'execute-api:Invoke',\n            'Effect': 'Allow',\n            'Resource': expected,\n        }]\n    }\n\n\ndef test_root_resource(auth_request):\n    auth_request.method_arn = (\n        \"arn:aws:execute-api:us-west-2:123:rest-api-id/dev/GET/\")\n    expected = [\n        \"arn:aws:execute-api:us-west-2:123:rest-api-id/dev/GET/\"\n    ]\n    response = app.AuthResponse(\n        [app.AuthRoute('/', ['GET'])],\n        'principal')\n    serialized = response.to_dict(auth_request)\n    assert serialized['policyDocument'] == {\n        'Version': '2012-10-17',\n        'Statement': [{\n            'Action': 'execute-api:Invoke',\n            'Effect': 'Allow',\n            'Resource': expected,\n        }]\n    }\n\n\ndef test_can_register_scheduled_event_with_str(sample_app):\n    @sample_app.schedule('rate(1 minute)')\n    def foo(event):\n        pass\n\n    assert len(sample_app.event_sources) == 1\n    event_source = sample_app.event_sources[0]\n    assert event_source.name == 'foo'\n    assert event_source.schedule_expression == 'rate(1 minute)'\n    assert event_source.handler_string == 'app.foo'\n\n\ndef test_can_register_scheduled_event_with_rate(sample_app):\n    @sample_app.schedule(app.Rate(value=2, unit=app.Rate.HOURS))\n    def foo(event):\n        pass\n\n    # We don't convert the rate down to its string form until\n    # we actually deploy.\n    assert len(sample_app.event_sources) == 1\n    expression = sample_app.event_sources[0].schedule_expression\n    # We already check the event source in the test above, so we're\n    # only interested in the schedule expression here.\n    assert expression.value == 2\n    assert expression.unit == app.Rate.HOURS\n\n\ndef test_can_register_scheduled_event_with_event(sample_app):\n    @sample_app.schedule(app.Cron(0, 10, '*', '*', '?', '*'))\n    def foo(event):\n        pass\n\n    assert len(sample_app.event_sources) == 1\n    expression = sample_app.event_sources[0].schedule_expression\n    assert expression.minutes == 0\n    assert expression.hours == 10\n    assert expression.day_of_month == '*'\n    assert expression.month == '*'\n    assert expression.day_of_week == '?'\n    assert expression.year == '*'\n\n\n@pytest.mark.parametrize('value,unit,expected', [\n    (1, app.Rate.MINUTES, 'rate(1 minute)'),\n    (2, app.Rate.MINUTES, 'rate(2 minutes)'),\n    (1, app.Rate.HOURS, 'rate(1 hour)'),\n    (2, app.Rate.HOURS, 'rate(2 hours)'),\n    (1, app.Rate.DAYS, 'rate(1 day)'),\n    (2, app.Rate.DAYS, 'rate(2 days)'),\n])\ndef test_rule_object_converts_to_str(value, unit, expected):\n    assert app.Rate(value=value, unit=unit).to_string() == expected\n\n\n@pytest.mark.parametrize(('minutes,hours,day_of_month,month,'\n                          'day_of_week,year,expected'), [\n    # These are taken from the scheduled events docs page.\n    # Invoke a Lambda function at 10:00am (UTC) everyday\n    (0, 10, '*', '*', '?', '*', 'cron(0 10 * * ? *)'),\n    # Invoke a Lambda function 12:15pm (UTC) everyday\n    (15, 12, '*', '*', '?', '*', 'cron(15 12 * * ? *)'),\n    # Invoke a Lambda function at 06:00pm (UTC) every Mon-Fri\n    (0, 18, '?', '*', 'MON-FRI', '*', 'cron(0 18 ? * MON-FRI *)'),\n    # Invoke a Lambda function at 8:00am (UTC) every first day of the month\n    (0, 8, 1, '*', '?', '*', 'cron(0 8 1 * ? *)'),\n    # Invoke a Lambda function every 10 min Mon-Fri\n    ('0/10', '*', '?', '*', 'MON-FRI', '*', 'cron(0/10 * ? * MON-FRI *)'),\n    # Invoke a Lambda function every 5 minutes Mon-Fri between 8:00am and\n    # 5:55pm (UTC)\n    ('0/5', '8-17', '?', '*', 'MON-FRI', '*', 'cron(0/5 8-17 ? * MON-FRI *)'),\n    # Invoke a Lambda function at 9 a.m. (UTC) the first Monday of each month\n    (0, 9, '?', '*', '2#1', '*', 'cron(0 9 ? * 2#1 *)'),\n])\ndef test_cron_expression_converts_to_str(minutes, hours, day_of_month, month,\n                                         day_of_week, year, expected):\n    assert app.Cron(\n        minutes=minutes,\n        hours=hours,\n        day_of_month=day_of_month,\n        month=month,\n        day_of_week=day_of_week,\n        year=year,\n    ).to_string() == expected\n\n\ndef test_can_map_schedule_event_dict_to_object(sample_app):\n\n    @sample_app.schedule('rate(1 hour)')\n    def handler(event):\n        return event\n\n    # This is the event dict that lambda provides\n    # to the lambda handler\n    lambda_event = {\n        \"version\": \"0\",\n        \"account\": \"123456789012\",\n        \"region\": \"us-west-2\",\n        \"detail\": {},\n        \"detail-type\": \"Scheduled Event\",\n        \"source\": \"aws.events\",\n        \"time\": \"1970-01-01T00:00:00Z\",\n        \"id\": \"event-id\",\n        \"resources\": [\n          \"arn:aws:events:us-west-2:123456789012:rule/my-schedule\"\n        ]\n    }\n\n    event_object = handler(lambda_event, context=None)\n    assert event_object.version == '0'\n    assert event_object.event_id == 'event-id'\n    assert event_object.source == 'aws.events'\n    assert event_object.account == '123456789012'\n    assert event_object.time == '1970-01-01T00:00:00Z'\n    assert event_object.region == 'us-west-2'\n    assert event_object.resources == [\n        \"arn:aws:events:us-west-2:123456789012:rule/my-schedule\"\n    ]\n    assert event_object.detail == {}\n    assert event_object.detail_type == \"Scheduled Event\"\n    # This is meant as a fall back in case you need access to\n    # the raw lambda event dict.\n    assert event_object.to_dict() == lambda_event\n\n\ndef test_can_create_cwe_event_handler(sample_app):\n\n    @sample_app.on_cw_event({'source': ['aws.ec2']})\n    def handler(event):\n        pass\n\n    assert len(sample_app.event_sources) == 1\n    event = sample_app.event_sources[0]\n    assert event.name == 'handler'\n    assert event.event_pattern == {'source': ['aws.ec2']}\n    assert event.handler_string == 'app.handler'\n\n\ndef test_can_map_cwe_event_dict_to_object(sample_app):\n\n    @sample_app.on_cw_event({'source': ['aws.ec2']})\n    def handler(event):\n        return event\n\n    lambda_event = {\n        \"version\": 0,\n        \"id\": \"7bf73129-1428-4cd3-a780-95db273d1602\",\n        \"detail-type\": \"EC2 Instance State-change Notification\",\n        \"source\": \"aws.ec2\",\n        \"account\": \"123456789012\",\n        \"time\": \"2015-11-11T21:29:54Z\",\n        \"region\": \"us-east-1\",\n        \"resources\": [\n            \"arn:aws:ec2:us-east-1:123456789012:instance/i-abcd1111\"\n        ],\n        \"detail\": {\n            \"instance-id\": \"i-abcd1111\",\n            \"state\": \"pending\"\n        }\n    }\n\n    event_object = handler(lambda_event, context=None)\n    assert event_object.detail_type == \"EC2 Instance State-change Notification\"\n    assert event_object.account == '123456789012'\n    assert event_object.region == 'us-east-1'\n    assert event_object.detail == {\n        'instance-id': 'i-abcd1111',\n        'state': 'pending'\n    }\n\n\ndef test_pure_lambda_function_direct_mapping(sample_app):\n    @sample_app.lambda_function()\n    def handler(event, context):\n        return event, context\n\n    return_value = handler({'fake': 'event'}, {'fake': 'context'})\n    assert return_value[0] == {'fake': 'event'}\n    assert return_value[1] == {'fake': 'context'}\n\n\ndef test_pure_lambda_functions_are_registered_in_app(sample_app):\n    @sample_app.lambda_function()\n    def handler(event, context):\n        pass\n\n    assert len(sample_app.pure_lambda_functions) == 1\n    lambda_function = sample_app.pure_lambda_functions[0]\n    assert lambda_function.name == 'handler'\n    assert lambda_function.handler_string == 'app.handler'\n\n\ndef test_aws_execution_env_set():\n    env = {'AWS_EXECUTION_ENV': 'AWS_Lambda_python2.7'}\n    app.Chalice('app-name', env=env)\n    assert env['AWS_EXECUTION_ENV'] == (\n        'AWS_Lambda_python2.7 aws-chalice/%s' % chalice_version\n    )\n\n\ndef test_can_use_out_of_order_args(create_event):\n    demo = app.Chalice('demo-app')\n\n    # Note how the url params and function args are out of order.\n    @demo.route('/{a}/{b}', methods=['GET'])\n    def index(b, a):\n        return {'a': a, 'b': b}\n    event = create_event('/{a}/{b}', 'GET', {'a': 'first', 'b': 'second'})\n    response = demo(event, context=None)\n    response = json_response_body(response)\n    assert response == {'a': 'first', 'b': 'second'}\n\n\ndef test_ensure_debug_mode_is_false_by_default():\n    # These logger tests need to each have a unique name because the Chalice\n    # app creates a logger with it's name. If these tests are run in a batch\n    # the logger names will overlap in the logging module and cause test\n    # failures.\n    test_app = app.Chalice('logger-test-1')\n    assert test_app.debug is False\n    assert test_app.log.getEffectiveLevel() == logging.ERROR\n\n\ndef test_can_explicitly_set_debug_false_in_initializer():\n    test_app = app.Chalice('logger-test-2', debug=False)\n    assert test_app.debug is False\n    assert test_app.log.getEffectiveLevel() == logging.ERROR\n\n\ndef test_can_set_debug_mode_in_initialzier():\n    test_app = app.Chalice('logger-test-3', debug=True)\n    assert test_app.debug is True\n    assert test_app.log.getEffectiveLevel() == logging.DEBUG\n\n\ndef test_debug_mode_changes_log_level():\n    test_app = app.Chalice('logger-test-4', debug=False)\n    test_app.debug = True\n    assert test_app.debug is True\n    assert test_app.log.getEffectiveLevel() == logging.DEBUG\n\n\ndef test_internal_exception_debug_false(capsys, create_event):\n    test_app = app.Chalice('logger-test-5', debug=False)\n\n    @test_app.route('/error')\n    def error():\n        raise Exception('Something bad happened')\n\n    event = create_event('/error', 'GET', {})\n    test_app(event, context=None)\n    out, err = capsys.readouterr()\n    assert 'logger-test-5' in out\n    assert 'Caught exception' in out\n    assert 'Something bad happened' in out\n\n\ndef test_raw_body_is_none_if_body_is_none():\n    event = {\n        'body': None,\n        'multiValueQueryStringParameters': '',\n        'headers': {},\n        'pathParameters': {},\n        'requestContext': {\n            'httpMethod': 'GET',\n            'resourcePath': '/',\n        },\n        'stageVariables': {},\n        'isBase64Encoded': False,\n    }\n    request = app.Request(event, FakeLambdaContext())\n    assert request.raw_body == b''\n\n\n@given(http_request_event=HTTP_REQUEST)\ndef test_http_request_to_dict_is_json_serializable(http_request_event):\n    # We have to do some slight pre-preprocessing here\n    # to maintain preconditions.  If the\n    # is_base64_encoded arg is True, we'll\n    # base64 encode the body.  We assume API Gateway\n    # upholds this precondition.\n    is_base64_encoded = http_request_event['isBase64Encoded']\n    if is_base64_encoded:\n        # Confirmed that if you send an empty body,\n        # API Gateway will always say the body is *not*\n        # base64 encoded.\n        assume(http_request_event['body'] is not None)\n        body = base64.b64encode(\n            http_request_event['body'].encode('utf-8'))\n        http_request_event['body'] = body.decode('ascii')\n\n    request = Request(http_request_event, FakeLambdaContext())\n    assert isinstance(request.raw_body, bytes)\n    request_dict = request.to_dict()\n    # We should always be able to dump the request dict\n    # to JSON.\n    assert json.dumps(request_dict, default=handle_extra_types)\n\n\n@given(body=st.text(), headers=STR_MAP,\n       status_code=st.integers(min_value=200, max_value=599))\ndef test_http_response_to_dict(body, headers, status_code):\n    r = Response(body=body, headers=headers, status_code=status_code)\n    serialized = r.to_dict()\n    assert 'headers' in serialized\n    assert 'statusCode' in serialized\n    assert 'body' in serialized\n    assert isinstance(serialized['body'], six.string_types)\n\n\n@given(body=st.binary(), content_type=st.sampled_from(BINARY_TYPES))\ndef test_handles_binary_responses(body, content_type):\n    r = Response(body=body, headers={'Content-Type': content_type})\n    serialized = r.to_dict(BINARY_TYPES)\n    # A binary response should always result in the\n    # response being base64 encoded.\n    assert serialized['isBase64Encoded']\n    assert isinstance(serialized['body'], six.string_types)\n    assert isinstance(base64.b64decode(serialized['body']), bytes)\n\n\ndef test_can_create_s3_event_handler(sample_app):\n    @sample_app.on_s3_event(bucket='mybucket')\n    def handler(event):\n        pass\n\n    assert len(sample_app.event_sources) == 1\n    event = sample_app.event_sources[0]\n    assert event.name == 'handler'\n    assert event.bucket == 'mybucket'\n    assert event.events == ['s3:ObjectCreated:*']\n    assert event.handler_string == 'app.handler'\n\n\ndef test_can_map_to_s3_event_object(sample_app):\n    @sample_app.on_s3_event(bucket='mybucket')\n    def handler(event):\n        return event\n\n    s3_event = {\n        'Records': [\n            {'awsRegion': 'us-west-2',\n             'eventName': 'ObjectCreated:Put',\n             'eventSource': 'aws:s3',\n             'eventTime': '2018-05-22T04:41:23.823Z',\n             'eventVersion': '2.0',\n             'requestParameters': {'sourceIPAddress': '174.127.235.55'},\n             'responseElements': {\n                'x-amz-id-2': 'request-id-2',\n                'x-amz-request-id': 'request-id-1'},\n             's3': {\n                 'bucket': {\n                     'arn': 'arn:aws:s3:::mybucket',\n                     'name': 'mybucket',\n                     'ownerIdentity': {\n                         'principalId': 'ABCD'\n                     }\n                 },\n                 'configurationId': 'config-id',\n                 'object': {\n                     'eTag': 'd41d8cd98f00b204e9800998ecf8427e',\n                     'key': 'hello-world.txt',\n                     'sequencer': '005B039F73C627CE8B',\n                     'size': 0\n                 },\n                 's3SchemaVersion': '1.0'\n             },\n             'userIdentity': {'principalId': 'AWS:XYZ'}\n             }\n        ]\n    }\n    actual_event = handler(s3_event, context=None)\n    assert actual_event.bucket == 'mybucket'\n    assert actual_event.key == 'hello-world.txt'\n    assert actual_event.to_dict() == s3_event\n\n\ndef test_s3_event_urldecodes_keys():\n    s3_event = {\n        'Records': [\n            {'s3': {\n                 'bucket': {\n                     'arn': 'arn:aws:s3:::mybucket',\n                     'name': 'mybucket',\n                 },\n                 'object': {\n                     'key': 'file+with+spaces',\n                     'sequencer': '005B039F73C627CE8B',\n                     'size': 0\n                 },\n            }},\n        ]\n    }\n    event = app.S3Event(s3_event, FakeLambdaContext())\n    # We should urldecode the key name.\n    assert event.key == 'file with spaces'\n    # But the key should remain unchanged in to_dict().\n    assert event.to_dict() == s3_event\n\n\ndef test_s3_event_urldecodes_unicode_keys():\n    s3_event = {\n        'Records': [\n            {'s3': {\n                 'bucket': {\n                     'arn': 'arn:aws:s3:::mybucket',\n                     'name': 'mybucket',\n                 },\n                 'object': {\n                     # This is u'\\u2713'\n                     'key': '%E2%9C%93',\n                     'sequencer': '005B039F73C627CE8B',\n                     'size': 0\n                 },\n            }},\n        ]\n    }\n    event = app.S3Event(s3_event, FakeLambdaContext())\n    # We should urldecode the key name.\n    assert event.key == u'\\u2713'\n    assert event.bucket == u'mybucket'\n    # But the key should remain unchanged in to_dict().\n    assert event.to_dict() == s3_event\n\n\ndef test_can_create_sns_handler(sample_app):\n    @sample_app.on_sns_message(topic='MyTopic')\n    def handler(event):\n        pass\n\n    assert len(sample_app.event_sources) == 1\n    event = sample_app.event_sources[0]\n    assert event.name == 'handler'\n    assert event.topic == 'MyTopic'\n    assert event.handler_string == 'app.handler'\n\n\ndef test_can_map_sns_event(sample_app):\n    @sample_app.on_sns_message(topic='MyTopic')\n    def handler(event):\n        return event\n\n    sns_event = {'Records': [{\n        'EventSource': 'aws:sns',\n        'EventSubscriptionArn': 'arn:subscription-arn',\n        'EventVersion': '1.0',\n        'Sns': {\n            'Message': 'This is a raw message',\n            'MessageAttributes': {\n                'AttributeKey': {\n                    'Type': 'String',\n                    'Value': 'AttributeValue'\n                }\n            },\n            'MessageId': 'abcdefgh-51e4-5ae2-9964-b296c8d65d1a',\n            'Signature': 'signature',\n            'SignatureVersion': '1',\n            'SigningCertUrl': 'https://sns.us-west-2.amazonaws.com/cert.pen',\n            'Subject': 'ThisIsTheSubject',\n            'Timestamp': '2018-06-26T19:41:38.695Z',\n            'TopicArn': 'arn:aws:sns:us-west-2:12345:ConsoleTestTopic',\n            'Type': 'Notification',\n            'UnsubscribeUrl': 'https://unsubscribe-url/'}}]}\n    lambda_context = FakeLambdaContext()\n    actual_event = handler(sns_event, context=lambda_context)\n    assert actual_event.message == 'This is a raw message'\n    assert actual_event.subject == 'ThisIsTheSubject'\n    assert actual_event.to_dict() == sns_event\n    assert actual_event.context == lambda_context\n\n\ndef test_can_create_sqs_handler(sample_app):\n    @sample_app.on_sqs_message(queue='MyQueue', batch_size=200)\n    def handler(event):\n        pass\n\n    assert len(sample_app.event_sources) == 1\n    event = sample_app.event_sources[0]\n    assert event.queue == 'MyQueue'\n    assert event.batch_size == 200\n    assert event.maximum_batching_window_in_seconds == 0\n    assert event.handler_string == 'app.handler'\n\n\ndef test_can_set_sqs_handler_name(sample_app):\n    @sample_app.on_sqs_message(queue='MyQueue', name='sqs_handler')\n    def handler(event):\n        pass\n\n    assert len(sample_app.event_sources) == 1\n    event = sample_app.event_sources[0]\n    assert event.name == 'sqs_handler'\n\n\ndef test_can_set_sqs_handler_maximum_batching_window_in_seconds(sample_app):\n    @sample_app.on_sqs_message(\n        queue='MyQueue', maximum_batching_window_in_seconds=60)\n    def handler(event):\n        pass\n\n    assert len(sample_app.event_sources) == 1\n    event = sample_app.event_sources[0]\n    assert event.maximum_batching_window_in_seconds == 60\n\n\ndef test_can_map_sqs_event(sample_app):\n    @sample_app.on_sqs_message(queue='queue-name')\n    def handler(event):\n        return event\n\n    sqs_event = {'Records': [{\n        'attributes': {\n            'ApproximateFirstReceiveTimestamp': '1530576251596',\n            'ApproximateReceiveCount': '1',\n            'SenderId': 'sender-id',\n            'SentTimestamp': '1530576251595'\n        },\n        'awsRegion': 'us-west-2',\n        'body': 'queue message body',\n        'eventSource': 'aws:sqs',\n        'eventSourceARN': 'arn:aws:sqs:us-west-2:12345:queue-name',\n        'md5OfBody': '754ac2f7a12df38320e0c5eafd060145',\n        'messageAttributes': {},\n        'messageId': 'message-id',\n        'receiptHandle': 'receipt-handle'\n    }]}\n    lambda_context = FakeLambdaContext()\n    actual_event = handler(sqs_event, context=lambda_context)\n    records = list(actual_event)\n    assert len(records) == 1\n    first_record = records[0]\n    assert first_record.body == 'queue message body'\n    assert first_record.receipt_handle == 'receipt-handle'\n    assert first_record.to_dict() == sqs_event['Records'][0]\n    assert actual_event.to_dict() == sqs_event\n    assert actual_event.context == lambda_context\n\n\ndef test_can_create_kinesis_handler(sample_app):\n    @sample_app.on_kinesis_record(stream='MyStream',\n                                  batch_size=1,\n                                  starting_position='TRIM_HORIZON')\n    def handler(event):\n        pass\n\n    assert len(sample_app.event_sources) == 1\n    config = sample_app.event_sources[0]\n    assert config.stream == 'MyStream'\n    assert config.batch_size == 1\n    assert config.starting_position == 'TRIM_HORIZON'\n    assert config.maximum_batching_window_in_seconds == 0\n\n\ndef test_can_set_kinesis_handler_maximum_batching_window(sample_app):\n    @sample_app.on_kinesis_record(stream='MyStream',\n                                  batch_size=1,\n                                  starting_position='TRIM_HORIZON',\n                                  maximum_batching_window_in_seconds=60)\n    def handler(event):\n        pass\n\n    assert len(sample_app.event_sources) == 1\n    config = sample_app.event_sources[0]\n    assert config.maximum_batching_window_in_seconds == 60\n\n\ndef test_can_map_kinesis_event(sample_app):\n    @sample_app.on_kinesis_record(stream='MyStream')\n    def handler(event):\n        return event\n\n    kinesis_event = {\n        \"Records\": [\n            {\n                \"kinesis\": {\n                    \"kinesisSchemaVersion\": \"1.0\",\n                    \"partitionKey\": \"1\",\n                    \"sequenceNumber\": \"12345\",\n                    \"data\": \"SGVsbG8sIHRoaXMgaXMgYSB0ZXN0Lg==\",\n                    \"approximateArrivalTimestamp\": 1545084650.987\n                },\n                \"eventSource\": \"aws:kinesis\",\n                \"eventVersion\": \"1.0\",\n                \"eventID\": \"shardId-000000000006:12345\",\n                \"eventName\": \"aws:kinesis:record\",\n                \"invokeIdentityArn\": \"arn:aws:iam::123:role/lambda-role\",\n                \"awsRegion\": \"us-east-2\",\n                \"eventSourceARN\": (\n                    \"arn:aws:kinesis:us-east-2:123:stream/lambda-stream\"\n                )\n            },\n            {\n                \"kinesis\": {\n                    \"kinesisSchemaVersion\": \"1.0\",\n                    \"partitionKey\": \"1\",\n                    \"sequenceNumber\": \"12346\",\n                    \"data\": \"VGhpcyBpcyBvbmx5IGEgdGVzdC4=\",\n                    \"approximateArrivalTimestamp\": 1545084711.166\n                },\n                \"eventSource\": \"aws:kinesis\",\n                \"eventVersion\": \"1.0\",\n                \"eventID\": \"shardId-000000000006:12346\",\n                \"eventName\": \"aws:kinesis:record\",\n                \"invokeIdentityArn\": \"arn:aws:iam::123:role/lambda-role\",\n                \"awsRegion\": \"us-east-2\",\n                \"eventSourceARN\": (\n                    \"arn:aws:kinesis:us-east-2:123:stream/lambda-stream\"\n                )\n            }\n        ]\n    }\n    lambda_context = FakeLambdaContext()\n    actual_event = handler(kinesis_event, context=lambda_context)\n    records = list(actual_event)\n    assert len(records) == 2\n    assert records[0].data == b'Hello, this is a test.'\n    assert records[0].sequence_number == \"12345\"\n    assert records[0].partition_key == \"1\"\n    assert records[0].schema_version == \"1.0\"\n    assert records[0].timestamp == datetime(2018, 12, 17, 22, 10, 50, 987000)\n    assert records[1].data == b'This is only a test.'\n\n\ndef test_can_create_ddb_handler(sample_app):\n    @sample_app.on_dynamodb_record(\n        stream_arn='arn:aws:dynamodb:...:stream', batch_size=10,\n        starting_position='TRIM_HORIZON')\n    def handler(event):\n        pass\n\n    assert len(sample_app.event_sources) == 1\n    config = sample_app.event_sources[0]\n    assert config.stream_arn == 'arn:aws:dynamodb:...:stream'\n    assert config.batch_size == 10\n    assert config.starting_position == 'TRIM_HORIZON'\n    assert config.maximum_batching_window_in_seconds == 0\n\n\ndef test_can_set_ddb_handler_maximum_batching_window_in_seconds(sample_app):\n    @sample_app.on_dynamodb_record(\n        stream_arn='arn:aws:dynamodb:...:stream', batch_size=10,\n        starting_position='TRIM_HORIZON',\n        maximum_batching_window_in_seconds=60)\n    def handler(event):\n        pass\n\n    assert len(sample_app.event_sources) == 1\n    config = sample_app.event_sources[0]\n    assert config.maximum_batching_window_in_seconds == 60\n\n\ndef test_can_map_ddb_event(sample_app):\n    @sample_app.on_dynamodb_record(stream_arn='arn:aws:...:stream')\n    def handler(event):\n        return event\n\n    ddb_event = {\n        'Records': [\n            {'awsRegion': 'us-west-2',\n             'dynamodb': {'ApproximateCreationDateTime': 1601317140.0,\n                          'Keys': {'PK': {'S': 'foo'}, 'SK': {'S': 'bar'}},\n                          'NewImage': {'PK': {'S': 'foo'}, 'SK': {'S': 'bar'}},\n                          'SequenceNumber': '1700000000020701978607',\n                          'SizeBytes': 20,\n                          'StreamViewType': 'NEW_AND_OLD_IMAGES'},\n             'eventID': 'da037887f71a88a1f6f4cfd149709d5a',\n             'eventName': 'INSERT',\n             'eventSource': 'aws:dynamodb',\n             'eventSourceARN': (\n                 'arn:aws:dynamodb:us-west-2:12345:table/MyTable/stream/'\n                 '2020-09-28T16:49:14.209'\n             ),\n             'eventVersion': '1.1'}\n        ]\n    }\n    lambda_context = FakeLambdaContext()\n    actual_event = handler(ddb_event, context=lambda_context)\n    records = list(actual_event)\n    assert len(records) == 1\n    assert records[0].timestamp == datetime(2020, 9, 28, 18, 19)\n    assert records[0].keys == {'PK': {'S': 'foo'}, 'SK': {'S': 'bar'}}\n    assert records[0].new_image == {'PK': {'S': 'foo'}, 'SK': {'S': 'bar'}}\n    assert records[0].old_image is None\n    assert records[0].sequence_number == '1700000000020701978607'\n    assert records[0].size_bytes == 20\n    assert records[0].stream_view_type == 'NEW_AND_OLD_IMAGES'\n    # Mapping from top level keys in a record.\n    assert records[0].aws_region == 'us-west-2'\n    assert records[0].event_id == 'da037887f71a88a1f6f4cfd149709d5a'\n    assert records[0].event_name == 'INSERT'\n    assert records[0].event_source_arn == (\n        'arn:aws:dynamodb:us-west-2:12345:table/MyTable/stream/'\n        '2020-09-28T16:49:14.209')\n    # Computed value.\n    assert records[0].table_name == 'MyTable'\n\n\ndef test_bytes_when_binary_type_is_application_json():\n    demo = app.Chalice('demo-app')\n    demo.api.binary_types.append('application/json')\n\n    @demo.route('/compress_response')\n    def index():\n        blob = json.dumps({'hello': 'world'}).encode('utf-8')\n        payload = gzip.compress(blob)\n        custom_headers = {\n            'Content-Type': 'application/json',\n            'Content-Encoding': 'gzip'\n        }\n        return Response(body=payload, status_code=200, headers=custom_headers)\n\n    return demo\n\n\ndef test_can_register_blueprint_on_app():\n    myapp = app.Chalice('myapp')\n    foo = app.Blueprint('foo')\n\n    @foo.route('/foo')\n    def first():\n        pass\n\n    myapp.register_blueprint(foo)\n    assert sorted(list(myapp.routes.keys())) == ['/foo']\n\n\ndef test_can_combine_multiple_blueprints_in_single_app():\n    myapp = app.Chalice('myapp')\n    foo = app.Blueprint('foo')\n    bar = app.Blueprint('bar')\n\n    @foo.route('/foo')\n    def myfoo():\n        pass\n\n    @bar.route('/bar')\n    def mybar():\n        pass\n\n    myapp.register_blueprint(foo)\n    myapp.register_blueprint(bar)\n\n    assert sorted(list(myapp.routes)) == ['/bar', '/foo']\n\n\ndef test_can_preserve_signature_on_blueprint():\n    myapp = app.Chalice('myapp')\n    foo = app.Blueprint('foo')\n\n    @foo.lambda_function()\n    def first(event, context):\n        return {'foo': 'bar'}\n\n    myapp.register_blueprint(foo)\n\n    # The handler string given to a blueprint\n    # is the \"module.function_name\" so we have\n    # to ensure we can continue to invoke the\n    # function with its expected signature.\n    assert first({}, None) == {'foo': 'bar'}\n\n\ndef test_doc_saved_on_route():\n    myapp = app.Chalice('myapp')\n\n    @myapp.route('/')\n    def index():\n        \"\"\"My index docstring.\"\"\"\n        pass\n\n    assert index.__doc__ == 'My index docstring.'\n\n\ndef test_blueprint_docstring_is_preserved():\n    foo = app.Blueprint('foo')\n\n    @foo.route('/foo')\n    def first():\n        \"\"\"Blueprint docstring.\"\"\"\n\n    assert first.__doc__ == 'Blueprint docstring.'\n\n\ndef test_can_mount_apis_at_url_prefix():\n    myapp = app.Chalice('myapp')\n    foo = app.Blueprint('foo')\n\n    @foo.route('/foo')\n    def myfoo():\n        pass\n\n    @foo.route('/bar')\n    def mybar():\n        pass\n\n    myapp.register_blueprint(foo, url_prefix='/myprefix')\n    assert list(sorted(myapp.routes)) == ['/myprefix/bar', '/myprefix/foo']\n\n\ndef test_can_mount_root_url_in_blueprint():\n    myapp = app.Chalice('myapp')\n    foo = app.Blueprint('foo')\n    root = app.Blueprint('root')\n\n    @root.route('/')\n    def myroot():\n        pass\n\n    @foo.route('/')\n    def myfoo():\n        pass\n\n    @foo.route('/bar')\n    def mybar():\n        pass\n\n    myapp.register_blueprint(foo, url_prefix='/foo')\n    myapp.register_blueprint(root)\n    assert list(sorted(myapp.routes)) == ['/', '/foo', '/foo/bar']\n\n\ndef test_can_combine_lambda_functions_and_routes_in_blueprints():\n    myapp = app.Chalice('myapp')\n\n    foo = app.Blueprint('app.chalicelib.blueprints.foo')\n\n    @foo.route('/foo')\n    def myfoo():\n        pass\n\n    @foo.lambda_function()\n    def myfunction(event, context):\n        pass\n\n    myapp.register_blueprint(foo)\n    assert len(myapp.pure_lambda_functions) == 1\n    lambda_function = myapp.pure_lambda_functions[0]\n    assert lambda_function.name == 'myfunction'\n    assert lambda_function.handler_string == (\n        'app.chalicelib.blueprints.foo.myfunction')\n\n    assert list(myapp.routes) == ['/foo']\n\n\ndef test_can_mount_lambda_functions_with_name_prefix():\n    myapp = app.Chalice('myapp')\n    foo = app.Blueprint('app.chalicelib.blueprints.foo')\n\n    @foo.lambda_function()\n    def myfunction(event, context):\n        return event\n\n    myapp.register_blueprint(foo, name_prefix='myprefix_')\n    assert len(myapp.pure_lambda_functions) == 1\n    lambda_function = myapp.pure_lambda_functions[0]\n    assert lambda_function.name == 'myprefix_myfunction'\n    assert lambda_function.handler_string == (\n        'app.chalicelib.blueprints.foo.myfunction')\n\n    with Client(myapp) as c:\n        response = c.lambda_.invoke(\n            'myprefix_myfunction', {'foo': 'bar'}\n        )\n    assert response.payload == {'foo': 'bar'}\n\n\ndef test_can_mount_event_sources_with_blueprint():\n    myapp = app.Chalice('myapp')\n    foo = app.Blueprint('app.chalicelib.blueprints.foo')\n\n    @foo.schedule('rate(5 minutes)')\n    def myfunction(event):\n        return event\n\n    myapp.register_blueprint(foo, name_prefix='myprefix_')\n    assert len(myapp.event_sources) == 1\n    event_source = myapp.event_sources[0]\n    assert event_source.name == 'myprefix_myfunction'\n    assert event_source.schedule_expression == 'rate(5 minutes)'\n    assert event_source.handler_string == (\n        'app.chalicelib.blueprints.foo.myfunction')\n\n\ndef test_can_mount_all_decorators_in_blueprint():\n    myapp = app.Chalice('myapp')\n    foo = app.Blueprint('app.chalicelib.blueprints.foo')\n\n    @foo.route('/foo')\n    def routefoo():\n        pass\n\n    @foo.lambda_function(name='mylambdafunction')\n    def mylambda(event, context):\n        pass\n\n    @foo.schedule('rate(5 minutes)')\n    def bar(event):\n        pass\n\n    @foo.on_s3_event('MyBucket')\n    def on_s3(event):\n        pass\n\n    @foo.on_sns_message('MyTopic')\n    def on_sns(event):\n        pass\n\n    @foo.on_sqs_message('MyQueue')\n    def on_sqs(event):\n        pass\n\n    myapp.register_blueprint(foo, name_prefix='myprefix_', url_prefix='/bar')\n    event_sources = myapp.event_sources\n    assert len(event_sources) == 4\n    lambda_functions = myapp.pure_lambda_functions\n    assert len(lambda_functions) == 1\n    # Handles the name prefix and the name='' override in the decorator.\n    assert lambda_functions[0].name == 'myprefix_mylambdafunction'\n    assert list(myapp.routes) == ['/bar/foo']\n\n\ndef test_can_call_current_request_on_blueprint_when_mounted(create_event):\n    myapp = app.Chalice('myapp')\n    bp = app.Blueprint('app.chalicelib.blueprints.foo')\n\n    @bp.route('/todict')\n    def todict():\n        return bp.current_request.to_dict()\n\n    myapp.register_blueprint(bp)\n    event = create_event('/todict', 'GET', {})\n    response = json_response_body(myapp(event, context=None))\n    assert isinstance(response, dict)\n    assert response['method'] == 'GET'\n\n\ndef test_can_call_current_app_on_blueprint_when_mounted(create_event):\n    myapp = app.Chalice('myapp')\n    bp = app.Blueprint('app.chalicelib.blueprints.foo')\n\n    @bp.route('/appname')\n    def appname():\n        return {'name': bp.current_app.app_name}\n\n    myapp.register_blueprint(bp)\n    event = create_event('/appname', 'GET', {})\n    response = json_response_body(myapp(event, context=None))\n    assert response == {'name': 'myapp'}\n\n\ndef test_can_call_lambda_context_on_blueprint_when_mounted(create_event):\n    myapp = app.Chalice('myapp')\n    bp = app.Blueprint('app.chalicelib.blueprints.foo')\n\n    @bp.route('/context')\n    def context():\n        return bp.lambda_context\n\n    myapp.register_blueprint(bp)\n    event = create_event('/context', 'GET', {})\n    response = json_response_body(myapp(event, context={'context': 'foo'}))\n    assert response == {'context': 'foo'}\n\n\ndef test_can_access_log_when_mounted(create_event):\n    myapp = app.Chalice('myapp')\n    bp = app.Blueprint('app.chalicelib.blueprints.foo')\n\n    @bp.route('/log')\n    def log_message():\n        # We shouldn't get an error because we've registered it to\n        # an app.\n        bp.log.info(\"test log message\")\n        return {}\n\n    myapp.register_blueprint(bp)\n    event = create_event('/log', 'GET', {})\n    response = json_response_body(myapp(event, context={'context': 'foo'}))\n    assert response == {}\n\n\ndef test_can_add_authorizer_with_url_prefix_and_routes():\n    myapp = app.Chalice('myapp')\n    foo = app.Blueprint('app.chalicelib.blueprints.foo')\n\n    @foo.authorizer()\n    def myauth(event):\n        pass\n\n    @foo.route('/foo', authorizer=myauth)\n    def routefoo():\n        pass\n\n    myapp.register_blueprint(foo, url_prefix='/bar')\n    assert len(myapp.builtin_auth_handlers) == 1\n    authorizer = myapp.builtin_auth_handlers[0]\n    assert isinstance(authorizer, app.BuiltinAuthConfig)\n    assert authorizer.name == 'myauth'\n    assert authorizer.handler_string == 'app.chalicelib.blueprints.foo.myauth'\n\n\ndef test_runtime_error_if_current_request_access_on_non_registered_blueprint():\n    bp = app.Blueprint('app.chalicelib.blueprints.foo')\n    with pytest.raises(RuntimeError):\n        bp.current_request\n\n\ndef test_every_decorator_added_to_blueprint():\n    def is_public_method(obj):\n        return inspect.isfunction(obj) and not obj.__name__.startswith('_')\n    public_api = inspect.getmembers(\n        app.DecoratorAPI,\n        predicate=is_public_method\n    )\n    blueprint_api = [\n        i[0] for i in\n        inspect.getmembers(app.Blueprint, predicate=is_public_method)\n    ]\n    for method_name, _ in public_api:\n        assert method_name in blueprint_api\n\n\n@pytest.mark.parametrize('input_dict', [\n    {},\n    {'key': []}\n])\ndef test_multidict_raises_keyerror(input_dict):\n    d = MultiDict(input_dict)\n    with pytest.raises(KeyError):\n        val = d['key']\n        assert val is val\n\n\ndef test_multidict_pop_raises_del_error():\n    d = MultiDict({})\n    with pytest.raises(KeyError):\n        del d['key']\n\n\ndef test_multidict_getlist_does_raise_keyerror():\n    d = MultiDict({})\n    with pytest.raises(KeyError):\n        d.getlist('key')\n\n\n@pytest.mark.parametrize('input_dict', [\n    {'key': ['value']},\n    {'key': ['']},\n    {'key': ['value1', 'value2', 'value3']},\n    {'key': ['value1', 'value2', None]}\n])\ndef test_multidict_returns_lastvalue(input_dict):\n    d = MultiDict(input_dict)\n    assert d['key'] == input_dict['key'][-1]\n\n\n@pytest.mark.parametrize('input_dict', [\n    {'key': ['value']},\n    {'key': ['']},\n    {'key': ['value1', 'value2', 'value3']},\n    {'key': ['value1', 'value2', None]}\n])\ndef test_multidict_returns_all_values(input_dict):\n    d = MultiDict(input_dict)\n    assert d.getlist('key') == input_dict['key']\n\n\n@pytest.mark.parametrize('input_dict', [\n    {'key': ['value']},\n    {'key': ['']},\n    {'key': ['value1', 'value2', 'value3']},\n    {'key': ['value1', 'value2', None]}\n])\ndef test_multidict_list_wont_change_source(input_dict):\n    d = MultiDict(input_dict)\n    dict_copy = deepcopy(input_dict)\n    d.getlist('key')[0] = 'othervalue'\n    assert d.getlist('key') == dict_copy['key']\n\n\n@pytest.mark.parametrize('input_dict,key,popped,leftover', [\n    (\n        {'key': ['value'], 'key2': [[]]},\n        'key',\n        'value',\n        {'key2': []},\n    ),\n    (\n        {'key': [''], 'key2': [[]]},\n        'key',\n        '',\n        {'key2': []},\n    ),\n    (\n        {'key': ['value1', 'value2', 'value3'],\n         'key2': [[]]},\n        'key',\n        'value3',\n        {'key2': []},\n    ),\n])\ndef test_multidict_list_can_pop_value(input_dict, key, popped, leftover):\n    d = MultiDict(input_dict)\n    pop_result = d.pop(key)\n    assert popped == pop_result\n    assert leftover == {key: d[key] for key in d}\n\n\ndef test_multidict_assignment():\n    d = MultiDict({})\n    d['key'] = 'value'\n    assert d['key'] == 'value'\n\n\ndef test_multidict_get_reassigned_value():\n    d = MultiDict({})\n    d['key'] = 'value'\n    assert d['key'] == 'value'\n    assert d.get('key') == 'value'\n    assert d.getlist('key') == ['value']\n\n\ndef test_multidict_get_list_wraps_key():\n    d = MultiDict({})\n    d['key'] = ['value']\n    assert d.getlist('key') == [['value']]\n\n\ndef test_multidict_repr():\n    d = MultiDict({\n        'foo': ['bar', 'baz'],\n        'buz': ['qux'],\n    })\n    rep = repr(d)\n    assert rep.startswith('MultiDict({')\n    assert \"'foo': ['bar', 'baz']\" in rep\n    assert \"'buz': ['qux']\" in rep\n\n\ndef test_multidict_str():\n    d = MultiDict({\n        'foo': ['bar', 'baz'],\n        'buz': ['qux'],\n    })\n    rep = str(d)\n    assert rep.startswith('MultiDict({')\n    assert \"'foo': ['bar', 'baz']\" in rep\n    assert \"'buz': ['qux']\" in rep\n\n\ndef test_can_configure_websockets(sample_websocket_app):\n    demo, _ = sample_websocket_app\n\n    assert len(demo.websocket_handlers) == 3, demo.websocket_handlers\n    assert '$connect' in demo.websocket_handlers, demo.websocket_handlers\n    assert '$disconnect' in demo.websocket_handlers, demo.websocket_handlers\n    assert '$default' in demo.websocket_handlers, demo.websocket_handlers\n\n\ndef test_websocket_event_json_body_available(sample_websocket_app,\n                                             create_websocket_event):\n    demo = app.Chalice('demo-app')\n    called = {'wascalled': False}\n\n    @demo.on_ws_message()\n    def message(event):\n        called['wascalled'] = True\n        assert event.json_body == {'foo': 'bar'}\n        # Second access hits the cache. Test that that works as well.\n        assert event.json_body == {'foo': 'bar'}\n\n    event = create_websocket_event('$default', body='{\"foo\": \"bar\"}')\n    handler = websocket_handler_for_route('$default', demo)\n\n    handler(event, context=None)\n    assert called['wascalled'] is True\n\n\ndef test_websocket_event_json_body_can_raise_error(sample_websocket_app,\n                                                   create_websocket_event):\n    demo = app.Chalice('demo-app')\n    called = {'wascalled': False}\n\n    @demo.on_ws_message()\n    def message(event):\n        called['wascalled'] = True\n        with pytest.raises(BadRequestError):\n            event.json_body\n\n    event = create_websocket_event('$default', body='{\"foo\": \"bar\"')\n    handler = websocket_handler_for_route('$default', demo)\n\n    handler(event, context=None)\n    assert called['wascalled'] is True\n\n\ndef test_can_route_websocket_connect_message(sample_websocket_app,\n                                             create_websocket_event):\n    demo, calls = sample_websocket_app\n    client = FakeClient()\n    demo.websocket_api.session = FakeSession(client)\n    event = create_websocket_event('$connect')\n    handler = websocket_handler_for_route('$connect', demo)\n    response = handler(event, context=None)\n\n    assert response == {'statusCode': 200}\n    assert len(calls) == 1\n    assert calls[0][0] == 'connect'\n    event = calls[0][1]\n    assert isinstance(event, WebsocketEvent)\n    assert event.domain_name == 'abcd1234.execute-api.us-west-2.amazonaws.com'\n    assert event.stage == 'api'\n    assert event.connection_id == 'ABCD1234='\n\n\ndef test_can_route_websocket_connect_response_dict(create_websocket_event):\n    demo = app.Chalice('app-name')\n    client = FakeClient()\n    demo.websocket_api.session = FakeSession(client)\n\n    @demo.on_ws_connect()\n    def connect(event):\n        return dict(\n            headers={\"Sec-WebSocket-Protocol\": \"Test-Protocol\"},\n            statusCode=200,\n            body=\"Connected.\",\n        )\n\n    event = create_websocket_event('$connect')\n    handler = websocket_handler_for_route('$connect', demo)\n    response = handler(event, context=None)\n    assert response == {\n        'headers': {'Sec-WebSocket-Protocol': 'Test-Protocol'},\n        'statusCode': 200,\n        'body': 'Connected.'\n    }\n\n\ndef test_can_route_websocket_connect_response_obj(create_websocket_event):\n    demo = app.Chalice('app-name')\n    client = FakeClient()\n    demo.websocket_api.session = FakeSession(client)\n\n    @demo.on_ws_connect()\n    def connect(event):\n        return Response(\n            \"Connected.\",\n            status_code=200,\n            headers={\n                \"Sec-WebSocket-Protocol\": \"Test-Protocol\",\n            },\n        )\n\n    event = create_websocket_event('$connect')\n    handler = websocket_handler_for_route('$connect', demo)\n    response = handler(event, context=None)\n    assert response == {\n        'headers': {'Sec-WebSocket-Protocol': 'Test-Protocol'},\n        'multiValueHeaders': {},\n        'statusCode': 200,\n        'body': 'Connected.',\n    }\n\n\ndef test_can_route_websocket_disconnect_message(sample_websocket_app,\n                                                create_websocket_event):\n    demo, calls = sample_websocket_app\n    client = FakeClient()\n    demo.websocket_api.session = FakeSession(client)\n    event = create_websocket_event('$disconnect')\n    handler = websocket_handler_for_route('$disconnect', demo)\n    response = handler(event, context=None)\n\n    assert response == {'statusCode': 200}\n    assert len(calls) == 1\n    assert calls[0][0] == 'disconnect'\n    event = calls[0][1]\n    assert isinstance(event, WebsocketEvent)\n    assert event.domain_name == 'abcd1234.execute-api.us-west-2.amazonaws.com'\n    assert event.stage == 'api'\n    assert event.connection_id == 'ABCD1234='\n\n\ndef test_can_route_websocket_default_message(sample_websocket_app,\n                                             create_websocket_event):\n    demo, calls = sample_websocket_app\n    client = FakeClient()\n    demo.websocket_api.session = FakeSession(client)\n    event = create_websocket_event('$default', body='foo bar')\n    handler = websocket_handler_for_route('$default', demo)\n    response = handler(event, context=None)\n\n    assert response == {'statusCode': 200}\n    assert len(calls) == 1\n    assert calls[0][0] == 'default'\n    event = calls[0][1]\n    assert isinstance(event, WebsocketEvent)\n    assert event.domain_name == 'abcd1234.execute-api.us-west-2.amazonaws.com'\n    assert event.stage == 'api'\n    assert event.connection_id == 'ABCD1234='\n    assert event.body == 'foo bar'\n\n\ndef test_can_configure_client_on_connect(sample_websocket_app,\n                                         create_websocket_event):\n    demo, calls = sample_websocket_app\n    client = FakeClient()\n    demo.websocket_api.session = FakeSession(client)\n    event = create_websocket_event('$connect')\n    handler = websocket_handler_for_route('$connect', demo)\n    handler(event, context=None)\n\n    assert demo.websocket_api.session.calls == [\n        ('apigatewaymanagementapi',\n         'https://abcd1234.execute-api.us-west-2.amazonaws.com/api'),\n    ]\n\n\ndef test_can_configure_non_aws_partition_clients(sample_websocket_app,\n                                                 create_websocket_event,\n                                                 monkeypatch):\n    # tests/conftest.py already monkeypatches out environment variables,\n    # so if we want a test with a specific region we have to use the\n    # same approach and monkeypatch the os.environ.\n    monkeypatch.setenv('AWS_REGION', 'cn-north-1')\n    demo, _ = sample_websocket_app\n    client = FakeClient()\n    demo.websocket_api.session = FakeSession(client)\n    event = create_websocket_event(\n        '$connect',\n        endpoint='abcd1234.execute-api.cn-north-1.amazonaws.com.cn')\n    handler = websocket_handler_for_route('$connect', demo)\n    handler(event, context=None)\n\n    assert demo.websocket_api.session.calls == [\n        ('apigatewaymanagementapi',\n         'https://abcd1234.execute-api.cn-north-1.amazonaws.com.cn/api'),\n    ]\n\n\ndef test_uses_api_id_not_domain_name(sample_websocket_app,\n                                     create_websocket_event):\n    demo, calls = sample_websocket_app\n    client = FakeClient()\n    demo.websocket_api.session = FakeSession(client)\n    event = create_websocket_event('$connect')\n    # If you configure a custom domain name, we should still use the\n    # original domainName generated from API gateway when configuring\n    # the apigatewaymanagementapi client.\n    event['requestContext']['domainName'] = 'api.custom-domain-name.com'\n    handler = websocket_handler_for_route('$connect', demo)\n    handler(event, context=None)\n    assert demo.websocket_api.session.calls == [\n        ('apigatewaymanagementapi',\n         'https://abcd1234.execute-api.us-west-2.amazonaws.com/api'),\n    ]\n\n\ndef test_fallsback_to_session_if_needed(sample_websocket_app,\n                                        create_websocket_event):\n    demo, calls = sample_websocket_app\n    client = FakeClient()\n    demo.websocket_api = WebsocketAPI(env={})\n    demo.websocket_api.session = FakeSession(client, region_name='us-east-2')\n    event = create_websocket_event('$connect')\n    # If you configure a custom domain name, we should still use the\n    # original domainName generated from API gateway when configuring\n    # the apigatewaymanagementapi client.\n    event['requestContext']['domainName'] = 'api.custom-domain-name.com'\n    handler = websocket_handler_for_route('$connect', demo)\n    handler(event, context=None)\n    assert demo.websocket_api.session.calls == [\n        ('apigatewaymanagementapi',\n         'https://abcd1234.execute-api.us-east-2.amazonaws.com/api'),\n    ]\n\n\ndef test_can_configure_client_on_disconnect(sample_websocket_app,\n                                            create_websocket_event):\n    demo, calls = sample_websocket_app\n    client = FakeClient()\n    demo.websocket_api.session = FakeSession(client)\n    event = create_websocket_event('$disconnect')\n    handler = websocket_handler_for_route('$disconnect', demo)\n    handler(event, context=None)\n\n    assert demo.websocket_api.session.calls == [\n        ('apigatewaymanagementapi',\n         'https://abcd1234.execute-api.us-west-2.amazonaws.com/api'),\n    ]\n\n\ndef test_can_configure_client_on_message(sample_websocket_app,\n                                         create_websocket_event):\n    demo, calls = sample_websocket_app\n    client = FakeClient()\n    demo.websocket_api.session = FakeSession(client)\n    event = create_websocket_event('$default', body='foo bar')\n    handler = websocket_handler_for_route('$default', demo)\n\n    handler(event, context=None)\n\n    assert demo.websocket_api.session.calls == [\n        ('apigatewaymanagementapi',\n         'https://abcd1234.execute-api.us-west-2.amazonaws.com/api'),\n    ]\n\n\ndef test_does_only_configure_client_once(sample_websocket_app,\n                                         create_websocket_event):\n    demo, calls = sample_websocket_app\n    client = FakeClient()\n    demo.websocket_api.session = FakeSession(client)\n    event = create_websocket_event('$default', body='foo bar')\n    handler = websocket_handler_for_route('$default', demo)\n\n    handler(event, context=None)\n    handler(event, context=None)\n\n    assert demo.websocket_api.session.calls == [\n        ('apigatewaymanagementapi',\n         'https://abcd1234.execute-api.us-west-2.amazonaws.com/api'),\n    ]\n\n\ndef test_cannot_configure_client_without_session(sample_websocket_app,\n                                                 create_websocket_event):\n    demo, calls = sample_websocket_app\n    demo.websocket_api.session = None\n    event = create_websocket_event('$default', body='foo bar')\n    handler = websocket_handler_for_route('$default', demo)\n    with pytest.raises(ValueError) as e:\n        handler(event, context=None)\n\n    assert str(e.value) == (\n        'Assign app.websocket_api.session to a boto3 session before using '\n        'the WebsocketAPI'\n    )\n\n\ndef test_cannot_send_websocket_message_without_configure(\n        sample_websocket_app, create_websocket_event):\n    demo = app.Chalice('app-name')\n    client = FakeClient()\n    demo.websocket_api.session = FakeSession(client)\n\n    @demo.on_ws_message()\n    def message_handler(event):\n        demo.websocket_api.send('connection_id', event.body)\n\n    event = create_websocket_event('$default', body='foo bar')\n    event_obj = WebsocketEvent(event, None)\n    handler = demo.websocket_handlers['$default'].handler_function\n    with pytest.raises(ValueError) as e:\n        handler(event_obj)\n    assert str(e.value) == (\n        'WebsocketAPI.configure must be called before using the WebsocketAPI'\n    )\n\n\ndef test_can_close_websocket_connection(create_websocket_event):\n    demo = app.Chalice('app-name')\n    client = FakeClient()\n    demo.websocket_api.session = FakeSession(client)\n\n    @demo.on_ws_message()\n    def message_handler(event):\n        demo.websocket_api.close('connection_id')\n\n    event = create_websocket_event('$default', body='foo bar')\n    handler = websocket_handler_for_route('$default', demo)\n    handler(event, context=None)\n\n    calls = client.calls['close']\n    assert len(calls) == 1\n    call = calls[0]\n    connection_id = call[0]\n    assert connection_id == 'connection_id'\n\n\ndef test_close_does_fail_if_already_disconnected(create_websocket_event):\n    demo = app.Chalice('app-name')\n    client = FakeClient(errors=[FakeGoneException])\n    demo.websocket_api.session = FakeSession(client)\n\n    @demo.on_ws_message()\n    def message_handler(event):\n        with pytest.raises(WebsocketDisconnectedError) as e:\n            demo.websocket_api.close('connection_id')\n        assert e.value.connection_id == 'connection_id'\n\n    event = create_websocket_event('$default', body='foo bar')\n    handler = websocket_handler_for_route('$default', demo)\n    handler(event, context=None)\n\n    calls = client.calls['close']\n    assert len(calls) == 1\n    call = calls[0]\n    connection_id = call[0]\n    assert connection_id == 'connection_id'\n\n\ndef test_info_does_fail_if_already_disconnected(create_websocket_event):\n    demo = app.Chalice('app-name')\n    client = FakeClient(errors=[FakeGoneException])\n    demo.websocket_api.session = FakeSession(client)\n\n    @demo.on_ws_message()\n    def message_handler(event):\n        with pytest.raises(WebsocketDisconnectedError) as e:\n            demo.websocket_api.info('connection_id')\n        assert e.value.connection_id == 'connection_id'\n\n    event = create_websocket_event('$default', body='foo bar')\n    handler = websocket_handler_for_route('$default', demo)\n    handler(event, context=None)\n\n    calls = client.calls['info']\n    assert len(calls) == 1\n    call = calls[0]\n    connection_id = call[0]\n    assert connection_id == 'connection_id'\n\n\ndef test_can__about_websocket_connection(create_websocket_event):\n    demo = app.Chalice('app-name')\n    client = FakeClient(infos=[{'foo': 'bar'}])\n    demo.websocket_api.session = FakeSession(client)\n    closure = {}\n\n    @demo.on_ws_message()\n    def message_handler(event):\n        closure['info'] = demo.websocket_api.info('connection_id')\n\n    event = create_websocket_event('$default', body='foo bar')\n    handler = websocket_handler_for_route('$default', demo)\n    handler(event, context=None)\n\n    assert closure['info'] == {'foo': 'bar'}\n    calls = client.calls['info']\n    assert len(calls) == 1\n    call = calls[0]\n    connection_id = call[0]\n    assert connection_id == 'connection_id'\n\n\ndef test_can_send_websocket_message(create_websocket_event):\n    demo = app.Chalice('app-name')\n    client = FakeClient()\n    demo.websocket_api.session = FakeSession(client)\n\n    @demo.on_ws_message()\n    def message_handler(event):\n        demo.websocket_api.send('connection_id', event.body)\n\n    event = create_websocket_event('$default', body='foo bar')\n    handler = websocket_handler_for_route('$default', demo)\n    handler(event, context=None)\n\n    calls = client.calls['post_to_connection']\n    assert len(calls) == 1\n    call = calls[0]\n    connection_id, message = call\n    assert connection_id == 'connection_id'\n    assert message == 'foo bar'\n\n\ndef test_does_raise_on_send_to_bad_websocket(create_websocket_event):\n    demo = app.Chalice('app-name')\n    client = FakeClient(errors=[FakeGoneException])\n    demo.websocket_api.session = FakeSession(client)\n\n    @demo.on_ws_message()\n    def message_handler(event):\n        with pytest.raises(WebsocketDisconnectedError) as e:\n            demo.websocket_api.send('connection_id', event.body)\n        assert e.value.connection_id == 'connection_id'\n\n    event = create_websocket_event('$default', body='foo bar')\n    handler = websocket_handler_for_route('$default', demo)\n    handler(event, context=None)\n\n\ndef test_does_reraise_on_websocket_send_error(create_websocket_event):\n    class SomeOtherError(Exception):\n        pass\n\n    demo = app.Chalice('app-name')\n    fake_418_error = SomeOtherError()\n    fake_418_error.response = {'ResponseMetadata': {'HTTPStatusCode': 418}}\n    client = FakeClient(errors=[fake_418_error])\n    demo.websocket_api.session = FakeSession(client)\n\n    @demo.on_ws_message()\n    def message_handler(event):\n        with pytest.raises(SomeOtherError):\n            demo.websocket_api.send('connection_id', event.body)\n\n    event = create_websocket_event('$default', body='foo bar')\n    handler = websocket_handler_for_route('$default', demo)\n    handler(event, context=None)\n\n\ndef test_does_reraise_on_other_send_exception(create_websocket_event):\n    demo = app.Chalice('app-name')\n    fake_500_error = Exception()\n    fake_500_error.response = {'ResponseMetadata': {'HTTPStatusCode': 500}}\n    fake_500_error.key = 'foo'\n    client = FakeClient(errors=[fake_500_error])\n    demo.websocket_api.session = FakeSession(client)\n\n    @demo.on_ws_message()\n    def message_handler(event):\n        with pytest.raises(Exception) as e:\n            demo.websocket_api.send('connection_id', event.body)\n        assert e.value.key == 'foo'\n\n    event = create_websocket_event('$default', body='foo bar')\n    demo(event, context=None)\n\n\ndef test_cannot_send_message_on_unconfigured_app():\n    demo = app.Chalice('app-name')\n    demo.websocket_api.session = None\n\n    with pytest.raises(ValueError) as e:\n        demo.websocket_api.send('connection_id', 'body')\n\n    assert str(e.value) == (\n        'Assign app.websocket_api.session to a boto3 session before '\n        'using the WebsocketAPI'\n    )\n\n\ndef test_cannot_re_register_websocket_handlers(create_websocket_event):\n    demo = app.Chalice('app-name')\n\n    @demo.on_ws_message()\n    def message_handler(event):\n        pass\n\n    with pytest.raises(ValueError) as e:\n        @demo.on_ws_message()\n        def message_handler_2(event):\n            pass\n\n    assert str(e.value) == (\n        \"Duplicate websocket handler: 'on_ws_message'. There can only be one \"\n        \"handler for each websocket decorator.\"\n    )\n\n    @demo.on_ws_connect()\n    def connect_handler(event):\n        pass\n\n    with pytest.raises(ValueError) as e:\n        @demo.on_ws_connect()\n        def conncet_handler_2(event):\n            pass\n\n    assert str(e.value) == (\n        \"Duplicate websocket handler: 'on_ws_connect'. There can only be one \"\n        \"handler for each websocket decorator.\"\n    )\n\n    @demo.on_ws_disconnect()\n    def disconnect_handler(event):\n        pass\n\n    with pytest.raises(ValueError) as e:\n        @demo.on_ws_disconnect()\n        def disconncet_handler_2(event):\n            pass\n\n    assert str(e.value) == (\n        \"Duplicate websocket handler: 'on_ws_disconnect'. There can only be \"\n        \"one handler for each websocket decorator.\"\n    )\n\n\ndef test_can_parse_json_websocket_body(create_websocket_event):\n    demo = app.Chalice('app-name')\n    client = FakeClient()\n    demo.websocket_api.session = FakeSession(client)\n\n    @demo.on_ws_message()\n    def message(event):\n        assert event.json_body == {'foo': 'bar'}\n\n    event = create_websocket_event('$default', body='{\"foo\": \"bar\"}')\n    demo(event, context=None)\n\n\ndef test_can_access_websocket_json_body_twice(create_websocket_event):\n    demo = app.Chalice('app-name')\n    client = FakeClient()\n    demo.websocket_api.session = FakeSession(client)\n\n    @demo.on_ws_message()\n    def message(event):\n        assert event.json_body == {'foo': 'bar'}\n        assert event.json_body == {'foo': 'bar'}\n\n    event = create_websocket_event('$default', body='{\"foo\": \"bar\"}')\n    demo(event, context=None)\n\n\ndef test_does_raise_on_invalid_json_wbsocket_body(create_websocket_event):\n    demo = app.Chalice('app-name')\n    client = FakeClient()\n    demo.websocket_api.session = FakeSession(client)\n\n    @demo.on_ws_message()\n    def message(event):\n        with pytest.raises(BadRequestError) as e:\n            event.json_body\n        assert 'Error Parsing JSON' in str(e.value)\n\n    event = create_websocket_event('$default', body='foo bar')\n    demo(event, context=None)\n\n\nclass TestMiddleware:\n    def test_middleware_basic_api(self):\n        demo = app.Chalice('app-name')\n        called = []\n\n        @demo.middleware('all')\n        def myhandler(event, get_response):\n            called.append({'name': 'myhandler', 'bucket': event.bucket})\n            return get_response(event)\n\n        @demo.middleware('all')\n        def myhandler2(event, get_response):\n            called.append({'name': 'myhandler2', 'bucket': event.bucket})\n            return get_response(event)\n\n        @demo.on_s3_event('mybucket')\n        def handler(event):\n            called.append({'name': 'main', 'bucket': event.bucket})\n            return {'bucket': event.bucket}\n\n        with Client(demo) as c:\n            response = c.lambda_.invoke(\n                'handler', c.events.generate_s3_event('mybucket', 'key')\n            )\n        assert response.payload == {'bucket': 'mybucket'}\n        assert called == [\n            {'name': 'myhandler', 'bucket': 'mybucket'},\n            {'name': 'myhandler2', 'bucket': 'mybucket'},\n            {'name': 'main', 'bucket': 'mybucket'},\n        ]\n\n    def test_can_access_original_event_and_context_in_http(self):\n        demo = app.Chalice('app-name')\n        called = []\n\n        @demo.middleware('http')\n        def myhandler(event, get_response):\n            called.append({'event': event})\n            return get_response(event)\n\n        @demo.route('/')\n        def index():\n            return {'hello': 'world'}\n\n        with Client(demo) as c:\n            response = c.http.get('/')\n        assert response.json_body == {'hello': 'world'}\n        actual_event = called[0]['event']\n        assert actual_event.path == '/'\n        assert actual_event.lambda_context.function_name == 'api_handler'\n        assert actual_event.to_original_event()[\n            'requestContext']['resourcePath'] == '/'\n\n    def test_can_short_circuit_response(self):\n        demo = app.Chalice('app-name')\n        called = []\n\n        @demo.middleware('all')\n        def myhandler(event, get_response):\n            called.append({'name': 'myhandler', 'bucket': event.bucket})\n            return {'short-circuit': True}\n\n        @demo.middleware('all')\n        def myhandler2(event, get_response):\n            called.append({'name': 'myhandler2', 'bucket': event.bucket})\n            return get_response(event)\n\n        @demo.on_s3_event('mybucket')\n        def handler(event):\n            called.append({'name': 'main', 'bucket': event.bucket})\n            return {'bucket': event.bucket}\n\n        with Client(demo) as c:\n            response = c.lambda_.invoke(\n                'handler', c.events.generate_s3_event('mybucket', 'key')\n            )\n        assert response.payload == {'short-circuit': True}\n        assert called == [\n            {'name': 'myhandler', 'bucket': 'mybucket'},\n        ]\n\n    def test_can_alter_response(self):\n        demo = app.Chalice('app-name')\n        called = []\n\n        @demo.middleware('all')\n        def myhandler(event, get_response):\n            called.append({'name': 'myhandler', 'bucket': event.bucket})\n            response = get_response(event)\n            response['myhandler'] = True\n            return response\n\n        @demo.middleware('all')\n        def myhandler2(event, get_response):\n            called.append({'name': 'myhandler2', 'bucket': event.bucket})\n            response = get_response(event)\n            response['myhandler2'] = True\n            return response\n\n        @demo.on_s3_event('mybucket')\n        def handler(event):\n            called.append({'name': 'main', 'bucket': event.bucket})\n            return {'bucket': event.bucket}\n\n        with Client(demo) as c:\n            response = c.lambda_.invoke(\n                'handler', c.events.generate_s3_event('mybucket', 'key')\n            )\n        assert response.payload == {\n            'bucket': 'mybucket',\n            'myhandler': True,\n            'myhandler2': True,\n        }\n        assert called == [\n            {'name': 'myhandler', 'bucket': 'mybucket'},\n            {'name': 'myhandler2', 'bucket': 'mybucket'},\n            {'name': 'main', 'bucket': 'mybucket'},\n        ]\n\n    def test_can_change_order_of_definitions(self):\n        demo = app.Chalice('app-name')\n        called = []\n\n        @demo.on_s3_event('mybucket')\n        def handler(event):\n            called.append({'name': 'main', 'bucket': event.bucket})\n            return {'bucket': event.bucket}\n\n        @demo.middleware('all')\n        def myhandler(event, get_response):\n            called.append({'name': 'myhandler', 'bucket': event.bucket})\n            response = get_response(event)\n            response['myhandler'] = True\n            return response\n\n        @demo.middleware('all')\n        def myhandler2(event, get_response):\n            called.append({'name': 'myhandler2', 'bucket': event.bucket})\n            response = get_response(event)\n            response['myhandler2'] = True\n            return response\n\n        with Client(demo) as c:\n            response = c.lambda_.invoke(\n                'handler', c.events.generate_s3_event('mybucket', 'key')\n            )\n        assert response.payload == {\n            'bucket': 'mybucket',\n            'myhandler': True,\n            'myhandler2': True,\n        }\n        assert called == [\n            {'name': 'myhandler', 'bucket': 'mybucket'},\n            {'name': 'myhandler2', 'bucket': 'mybucket'},\n            {'name': 'main', 'bucket': 'mybucket'},\n        ]\n\n    def test_can_use_middleware_for_pure_lambda(self):\n        demo = app.Chalice('app-name')\n        called = []\n\n        @demo.middleware('all')\n        def mymiddleware(event, get_response):\n            called.append({'name': 'mymiddleware', 'event': event.to_dict()})\n            return get_response(event)\n\n        @demo.lambda_function()\n        def myfunction(event, context):\n            called.append({'name': 'myfunction', 'event': event})\n            return {'foo': 'bar'}\n\n        with Client(demo) as c:\n            response = c.lambda_.invoke(\n                'myfunction', {'input-event': True}\n            )\n\n        assert response.payload == {'foo': 'bar'}\n        assert called == [\n            {'name': 'mymiddleware', 'event': {'input-event': True}},\n            {'name': 'myfunction', 'event': {'input-event': True}},\n        ]\n\n    def test_can_use_for_websocket_handlers(self):\n        demo = app.Chalice('app-name')\n        called = []\n\n        @demo.middleware('all')\n        def mymiddleware(event, get_response):\n            called.append({'name': 'mymiddleware', 'event': event.to_dict()})\n            return get_response(event)\n\n        @demo.on_ws_message()\n        def myfunction(event):\n            called.append({'name': 'myfunction', 'event': event.to_dict()})\n            return {'foo': 'bar'}\n\n        with Client(demo) as c:\n            event = {\n                'requestContext': {\n                    'domainName': 'example.com',\n                    'stage': 'dev',\n                    'connectionId': 'abcd',\n                    'apiId': 'abcd1234',\n                },\n                'body': \"body\"\n            }\n            response = c.lambda_.invoke('myfunction', event)\n\n        assert response.payload == {'foo': 'bar', 'statusCode': 200}\n        assert called == [\n            {'name': 'mymiddleware', 'event': event},\n            {'name': 'myfunction', 'event': event},\n        ]\n\n    def test_can_use_rest_api_for_middleware(self):\n        demo = app.Chalice('app-name')\n        called = []\n\n        @demo.middleware('all')\n        def mymiddleware(event, get_response):\n            called.append({'name': 'mymiddleware', 'method': event.method})\n            response = get_response(event)\n            response.status_code = 201\n            return response\n\n        @demo.route('/')\n        def index():\n            called.append({'url': '/'})\n            return {'index': True}\n\n        @demo.route('/hello')\n        def hello():\n            called.append({'url': '/hello'})\n            return {'hello': True}\n\n        with Client(demo) as c:\n            assert c.http.get('/').json_body == {'index': True}\n            response = c.http.get('/hello')\n            assert response.json_body == {'hello': True}\n            # Verify middleware can alter the response.\n            assert response.status_code == 201\n\n        assert called == [\n            {'name': 'mymiddleware', 'method': 'GET'},\n            {'url': '/'},\n            {'name': 'mymiddleware', 'method': 'GET'},\n            {'url': '/hello'},\n        ]\n\n    def test_error_handler_rest_api_untouched(self):\n        demo = app.Chalice('app-name')\n\n        @demo.middleware('all')\n        def mymiddleware(event, get_response):\n            return get_response(event)\n\n        @demo.route('/error')\n        def index():\n            raise NotFoundError(\"resource not found\")\n\n        with Client(demo) as c:\n            response = c.http.get('/error')\n            assert response.status_code == 404\n            assert response.json_body == {\n                'Code': 'NotFoundError',\n                'Message': 'resource not found'\n            }\n\n    def test_unhandled_error_not_caught(self):\n        demo = app.Chalice('app-name')\n\n        @demo.middleware('all')\n        def mymiddleware(event, get_response):\n            try:\n                return get_response(event)\n            except ChaliceUnhandledError:\n                return Response(body={'foo': 'bar'}, status_code=200)\n\n        @demo.route('/error')\n        def index():\n            raise ChaliceUnhandledError(\"unhandled\")\n\n        with Client(demo) as c:\n            response = c.http.get('/error')\n            assert response.status_code == 200\n            assert response.json_body == {'foo': 'bar'}\n\n    def test_middleware_errors_return_500_still_caught(self):\n        demo = app.Chalice('app-name')\n\n        @demo.middleware('all')\n        def mymiddleware(event, get_response):\n            return get_response(event)\n\n        @demo.route('/error')\n        def index():\n            raise ChaliceUnhandledError(\"unhandled\")\n\n        with Client(demo) as c:\n            # An uncaught ChaliceUnhandledError should still result\n            # in the standard error handler processing for REST APIs\n            # if the exception propagates out of the middleware stack.\n            response = c.http.get('/error')\n            assert response.status_code == 500\n            assert response.json_body == {\n                'Code': 'InternalServerError',\n                'Message': 'An internal server error occurred.'\n            }\n\n    def test_middleware_errors_result_in_500(self):\n        demo = app.Chalice('app-name')\n\n        @demo.middleware('all')\n        def mymiddleware(event, get_response):\n            raise Exception(\"Error from middleware.\")\n\n        @demo.route('/')\n        def index():\n            return {}\n\n        with Client(demo) as c:\n            response = c.http.get('/')\n            assert response.status_code == 500\n            assert response.json_body['Code'] == 'InternalServerError'\n\n    def test_can_filter_middleware_registration(self, sample_middleware_app):\n        with Client(sample_middleware_app) as c:\n            c.http.get('/')\n            assert sample_middleware_app.calls == [\n                {'type': 'all', 'event': 'Request'},\n                {'type': 'http', 'event': 'Request'},\n            ]\n            sample_middleware_app.calls[:] = []\n            c.lambda_.invoke(\n                's3_handler', c.events.generate_s3_event('bucket', 'key'))\n            assert sample_middleware_app.calls == [\n                {'type': 'all', 'event': 'S3Event'},\n                {'type': 's3', 'event': 'S3Event'},\n            ]\n            sample_middleware_app.calls[:] = []\n            c.lambda_.invoke(\n                'sns_handler', c.events.generate_sns_event('topic', 'message'))\n            assert sample_middleware_app.calls == [\n                {'type': 'all', 'event': 'SNSEvent'},\n                {'type': 'sns', 'event': 'SNSEvent'},\n            ]\n            sample_middleware_app.calls[:] = []\n            c.lambda_.invoke(\n                'sqs_handler', c.events.generate_sns_event('queue', 'message'))\n            # There is no sqs specific middleware.\n            assert sample_middleware_app.calls == [\n                {'type': 'all', 'event': 'SQSEvent'},\n            ]\n            sample_middleware_app.calls[:] = []\n            c.lambda_.invoke('lambda_handler', {})\n            assert sample_middleware_app.calls == [\n                {'type': 'all', 'event': 'LambdaFunctionEvent'},\n                {'type': 'pure_lambda', 'event': 'LambdaFunctionEvent'},\n            ]\n            sample_middleware_app.calls[:] = []\n            c.lambda_.invoke('ws_handler', {\n                'requestContext': {\n                    'domainName': 'example.com',\n                    'stage': 'dev',\n                    'connectionId': 'abcd',\n                    'apiId': 'abcd1234',\n                },\n                'body': \"body\"\n            })\n            assert sample_middleware_app.calls == [\n                {'type': 'all', 'event': 'WebsocketEvent'},\n                {'type': 'websocket', 'event': 'WebsocketEvent'},\n            ]\n\n    def test_can_register_middleware_on_blueprints(self):\n        demo = app.Chalice('app-name')\n        bp = app.Blueprint('bpmiddleware')\n        called = []\n\n        @demo.middleware('all')\n        def mymiddleware(event, get_response):\n            called.append({'name': 'fromapp', 'bucket': event.bucket})\n            return get_response(event)\n\n        @bp.middleware('all')\n        def bp_middleware(event, get_response):\n            called.append({'name': 'frombp', 'bucket': event.bucket})\n            return get_response(event)\n\n        @bp.on_s3_event('mybucket')\n        def bp_handler(event):\n            called.append({'name': 'bp_handler', 'bucket': event.bucket})\n            return {'bucket': event.bucket}\n\n        @bp.route('/')\n        def index():\n            pass\n\n        @demo.on_s3_event('mybucket')\n        def handler(event):\n            called.append({'name': 'main', 'bucket': event.bucket})\n            return {'bucket': event.bucket}\n\n        demo.register_blueprint(bp)\n\n        with Client(demo) as c:\n            # The order is particular here.  When we're invoking the lambda\n            # function from the \"app\" (demo) object, we expect\n            # the order to be mymiddleware, bp_middleware because mymiddleware\n            # is registered before the .register_blueprint().\n            response = c.lambda_.invoke(\n                'handler', c.events.generate_s3_event('mybucket', 'key')\n            )\n            assert response.payload == {'bucket': 'mybucket'}\n            assert called == [\n                {'name': 'fromapp', 'bucket': 'mybucket'},\n                {'name': 'frombp', 'bucket': 'mybucket'},\n                {'name': 'main', 'bucket': 'mybucket'},\n            ]\n            called[:] = []\n            response = c.lambda_.invoke(\n                'bp_handler', c.events.generate_s3_event('mybucket', 'key')\n            )\n            assert response.payload == {'bucket': 'mybucket'}\n            assert called == [\n                {'name': 'fromapp', 'bucket': 'mybucket'},\n                {'name': 'frombp', 'bucket': 'mybucket'},\n                {'name': 'bp_handler', 'bucket': 'mybucket'},\n            ]\n\n    def test_blueprint_gets_middlware_added(self):\n        demo = app.Chalice('app-name')\n        bp = app.Blueprint('bpmiddleware')\n        called = []\n\n        @bp.middleware('all')\n        def bp_middleware(event, get_response):\n            called.append({'name': 'frombp', 'bucket': 'mybucket'})\n            return get_response(event)\n\n        @demo.on_s3_event('mybucket')\n        def handler(event):\n            called.append({'name': 'main', 'bucket': event.bucket})\n            return {'bucket': event.bucket}\n\n        demo.register_blueprint(bp)\n\n        with Client(demo) as c:\n            response = c.lambda_.invoke(\n                'handler', c.events.generate_s3_event('mybucket', 'key')\n            )\n\n        assert response.payload == {'bucket': 'mybucket'}\n        assert called == [\n            {'name': 'frombp', 'bucket': 'mybucket'},\n            {'name': 'main', 'bucket': 'mybucket'},\n        ]\n\n    def test_can_register_middleware_without_decorator(self):\n        demo = app.Chalice('app-name')\n        called = []\n\n        def mymiddleware(event, get_response):\n            called.append({'name': 'mymiddleware', 'event': event.to_dict()})\n            return get_response(event)\n\n        @demo.lambda_function()\n        def myfunction(event, context):\n            called.append({'name': 'myfunction', 'event': event})\n            return {'foo': 'bar'}\n\n        demo.register_middleware(mymiddleware, 'all')\n\n        with Client(demo) as c:\n            response = c.lambda_.invoke(\n                'myfunction', {'input-event': True}\n            )\n\n        assert response.payload == {'foo': 'bar'}\n        assert called == [\n            {'name': 'mymiddleware', 'event': {'input-event': True}},\n            {'name': 'myfunction', 'event': {'input-event': True}},\n        ]\n\n    def test_can_convert_existing_lambda_decorator_to_middleware(self):\n        demo = app.Chalice('app-name')\n        called = []\n\n        def mydecorator(func):\n            def _wrapped(event, context):\n                called.append({'name': 'wrapped', 'event': event})\n                return func(event, context)\n            return _wrapped\n\n        @demo.middleware('all')\n        def second_middleware(event, get_response):\n            called.append({'name': 'second', 'event': event.to_dict()})\n            return get_response(event)\n\n        @demo.lambda_function()\n        def myfunction(event, context):\n            called.append({'name': 'myfunction', 'event': event})\n            return {'foo': 'bar'}\n\n        demo.register_middleware(ConvertToMiddleware(mydecorator))\n\n        with Client(demo) as c:\n            response = c.lambda_.invoke(\n                'myfunction', {'input-event': True}\n            )\n\n        assert response.payload == {'foo': 'bar'}\n        assert called == [\n            {'name': 'second', 'event': {'input-event': True}},\n            {'name': 'wrapped', 'event': {'input-event': True}},\n            {'name': 'myfunction', 'event': {'input-event': True}},\n        ]\n"
  },
  {
    "path": "tests/unit/test_awsclient.py",
    "content": "from collections import OrderedDict\n\nimport pytest\n\nfrom chalice.awsclient import TypedAWSClient\n\n\n@pytest.mark.parametrize('service,region,endpoint', [\n    ('sns', 'us-east-1',\n     OrderedDict([('partition', 'aws'),\n                  ('endpointName', 'us-east-1'),\n                  ('protocols', ['http', 'https']),\n                  ('hostname', 'sns.us-east-1.amazonaws.com'),\n                  ('signatureVersions', ['v4']),\n                  ('dnsSuffix', 'amazonaws.com')])),\n    ('sqs', 'cn-north-1',\n     OrderedDict([('partition', 'aws-cn'),\n                  ('endpointName', 'cn-north-1'),\n                  ('protocols', ['http', 'https']),\n                  ('sslCommonName', 'cn-north-1.queue.amazonaws.com.cn'),\n                  ('hostname', 'sqs.cn-north-1.amazonaws.com.cn'),\n                  ('signatureVersions', ['v4']),\n                  ('dnsSuffix', 'amazonaws.com.cn')])),\n    ('dynamodb', 'mars-west-1', None)\n])\ndef test_resolve_endpoint(stubbed_session, service, region, endpoint):\n    awsclient = TypedAWSClient(stubbed_session)\n    if endpoint is None:\n        assert awsclient.resolve_endpoint(service, region) is None\n    else:\n        assert endpoint.items() <= awsclient.resolve_endpoint(\n            service, region).items()\n\n\n@pytest.mark.parametrize('arn,endpoint', [\n    ('arn:aws:sns:us-east-1:123456:MyTopic',\n     OrderedDict([('partition', 'aws'),\n                  ('endpointName', 'us-east-1'),\n                  ('protocols', ['http', 'https']),\n                  ('hostname', 'sns.us-east-1.amazonaws.com'),\n                  ('signatureVersions', ['v4']),\n                  ('dnsSuffix', 'amazonaws.com')])),\n    ('arn:aws-cn:sqs:cn-north-1:444455556666:queue1',\n     OrderedDict([('partition', 'aws-cn'),\n                  ('endpointName', 'cn-north-1'),\n                  ('protocols', ['http', 'https']),\n                  ('sslCommonName', 'cn-north-1.queue.amazonaws.com.cn'),\n                  ('hostname', 'sqs.cn-north-1.amazonaws.com.cn'),\n                  ('signatureVersions', ['v4']),\n                  ('dnsSuffix', 'amazonaws.com.cn')])),\n    ('arn:aws:dynamodb:mars-west-1:123456:table/MyTable', None)\n])\ndef test_endpoint_from_arn(stubbed_session, arn, endpoint):\n    awsclient = TypedAWSClient(stubbed_session)\n    if endpoint is None:\n        assert awsclient.endpoint_from_arn(arn) is None\n    else:\n        assert endpoint.items() <= awsclient.endpoint_from_arn(\n            arn).items()\n\n\n@pytest.mark.parametrize('service,region,dns_suffix', [\n    ('sns', 'us-east-1', 'amazonaws.com'),\n    ('sns', 'cn-north-1', 'amazonaws.com.cn'),\n    ('dynamodb', 'mars-west-1', 'amazonaws.com')\n])\ndef test_endpoint_dns_suffix(stubbed_session, service, region, dns_suffix):\n    awsclient = TypedAWSClient(stubbed_session)\n    assert dns_suffix == awsclient.endpoint_dns_suffix(service, region)\n\n\n@pytest.mark.parametrize('arn,dns_suffix', [\n    ('arn:aws:sns:us-east-1:123456:MyTopic', 'amazonaws.com'),\n    ('arn:aws-cn:sqs:cn-north-1:444455556666:queue1', 'amazonaws.com.cn'),\n    ('arn:aws:dynamodb:mars-west-1:123456:table/MyTable', 'amazonaws.com')\n])\ndef test_endpoint_dns_suffix_from_arn(stubbed_session, arn, dns_suffix):\n    awsclient = TypedAWSClient(stubbed_session)\n    assert dns_suffix == awsclient.endpoint_dns_suffix_from_arn(arn)\n\n\nclass TestServicePrincipal(object):\n\n    @pytest.fixture\n    def region(self):\n        return 'bermuda-triangle-42'\n\n    @pytest.fixture\n    def url_suffix(self):\n        return '.nowhere.null'\n\n    @pytest.fixture\n    def non_iso_suffixes(self):\n        return ['', '.amazonaws.com', '.amazonaws.com.cn']\n\n    @pytest.fixture\n    def awsclient(self, stubbed_session):\n        return TypedAWSClient(stubbed_session)\n\n    def test_unmatched_service(self, awsclient):\n        assert awsclient.service_principal('taco.magic.food.com',\n                                           'us-east-1',\n                                           'amazonaws.com') == \\\n               'taco.magic.food.com'\n\n    def test_defaults(self, awsclient):\n        assert awsclient.service_principal('lambda') == 'lambda.amazonaws.com'\n\n    def test_states(self, awsclient, region, url_suffix, non_iso_suffixes):\n        services = ['states']\n        for suffix in non_iso_suffixes:\n            for service in services:\n                assert awsclient.service_principal('{}{}'.format(service,\n                                                                 suffix),\n                                                   region, url_suffix) == \\\n                       '{}.{}.amazonaws.com'.format(service, region)\n\n    def test_codedeploy_and_logs(self, awsclient, region, url_suffix,\n                                 non_iso_suffixes):\n        services = ['codedeploy', 'logs']\n        for suffix in non_iso_suffixes:\n            for service in services:\n                assert awsclient.service_principal('{}{}'.format(service,\n                                                                 suffix),\n                                                   region, url_suffix) == \\\n                       '{}.{}.{}'.format(service, region, url_suffix)\n\n    def test_ec2(self, awsclient, region, url_suffix, non_iso_suffixes):\n        services = ['ec2']\n        for suffix in non_iso_suffixes:\n            for service in services:\n                assert awsclient.service_principal('{}{}'.format(service,\n                                                                 suffix),\n                                                   region, url_suffix) == \\\n                       '{}.{}'.format(service, url_suffix)\n\n    def test_others(self, awsclient, region, url_suffix, non_iso_suffixes):\n        services = ['autoscaling', 'lambda', 'events', 'sns', 'sqs',\n                    'foo-service']\n        for suffix in non_iso_suffixes:\n            for service in services:\n                assert awsclient.service_principal('{}{}'.format(service,\n                                                                 suffix),\n                                                   region, url_suffix) == \\\n                       '{}.amazonaws.com'.format(service)\n\n    def test_local_suffix(self, awsclient, region, url_suffix):\n        assert awsclient.service_principal('foo-service.local',\n                                           region,\n                                           url_suffix) == 'foo-service.local'\n\n    def test_states_iso(self, awsclient):\n        assert awsclient.service_principal('states.amazonaws.com',\n                                           'us-iso-east-1',\n                                           'c2s.ic.gov') == \\\n               'states.amazonaws.com'\n\n    def test_states_isob(self, awsclient):\n        assert awsclient.service_principal('states.amazonaws.com',\n                                           'us-isob-east-1',\n                                           'sc2s.sgov.gov') == \\\n               'states.amazonaws.com'\n\n    def test_iso_exceptions(self, awsclient):\n        services = ['cloudhsm', 'config', 'workspaces']\n        for service in services:\n            assert awsclient.service_principal(\n                '{}.amazonaws.com'.format(service),\n                'us-iso-east-1',\n                'c2s.ic.gov') == '{}.c2s.ic.gov'.format(service)\n"
  },
  {
    "path": "tests/unit/test_config.py",
    "content": "import os\nimport sys\nimport pytest\n\nfrom chalice import __version__ as chalice_version\nfrom chalice.config import Config\nfrom chalice.config import DeployedResources\nfrom chalice import Chalice\n\n\nclass FixedDataConfig(Config):\n    def __init__(self, files_to_content, app_name='app'):\n        self.files_to_content = files_to_content\n        self._app_name = app_name\n\n    @property\n    def app_name(self):\n        return self._app_name\n\n    @property\n    def project_dir(self):\n        return '.'\n\n    def _load_json_file(self, filename):\n        return self.files_to_content.get(filename)\n\n\ndef test_config_create_method():\n    c = Config.create(app_name='foo')\n    assert c.app_name == 'foo'\n    # Otherwise attributes default to None meaning 'not set'.\n    assert c.profile is None\n    assert c.api_gateway_stage is None\n\n\ndef test_default_chalice_stage():\n    c = Config()\n    assert c.chalice_stage == 'dev'\n\n\ndef test_version_defaults_to_1_when_missing():\n    c = Config()\n    assert c.config_file_version == '1.0'\n\n\ndef test_default_value_of_manage_iam_role():\n    c = Config.create()\n    assert c.manage_iam_role\n\n\ndef test_can_lazy_load_chalice_app():\n\n    app = Chalice(app_name='foo')\n    calls = []\n\n    def call_recorder(*args, **kwargs):\n        calls.append((args, kwargs))\n        return app\n\n    c = Config.create(chalice_app=call_recorder)\n    # Accessing the property multiple times will only\n    # invoke the call once.\n    assert isinstance(c.chalice_app, Chalice)\n    assert isinstance(c.chalice_app, Chalice)\n    assert len(calls) == 1\n\n\ndef test_lazy_load_chalice_app_must_be_callable():\n    c = Config.create(chalice_app='not a callable')\n    with pytest.raises(TypeError):\n        c.chalice_app\n\n\ndef test_manage_iam_role_explicitly_set():\n    c = Config.create(manage_iam_role=False)\n    assert not c.manage_iam_role\n    c = Config.create(manage_iam_role=True)\n    assert c.manage_iam_role\n\n\ndef test_can_chain_lookup():\n    user_provided_params = {\n        'api_gateway_stage': 'user_provided_params',\n    }\n\n    config_from_disk = {\n        'api_gateway_stage': 'config_from_disk',\n        'app_name': 'config_from_disk',\n    }\n\n    default_params = {\n        'api_gateway_stage': 'default_params',\n        'app_name': 'default_params',\n        'project_dir': 'default_params',\n    }\n\n    c = Config(chalice_stage='dev',\n               user_provided_params=user_provided_params,\n               config_from_disk=config_from_disk,\n               default_params=default_params)\n    assert c.api_gateway_stage == 'user_provided_params'\n    assert c.app_name == 'config_from_disk'\n    assert c.project_dir == 'default_params'\n\n    assert c.config_from_disk == config_from_disk\n\n\ndef test_user_params_is_optional():\n    c = Config(config_from_disk={'api_gateway_stage': 'config_from_disk'},\n               default_params={'api_gateway_stage': 'default_params'})\n    assert c.api_gateway_stage == 'config_from_disk'\n\n\ndef test_can_chain_chalice_stage_values():\n    disk_config = {\n        'api_gateway_stage': 'dev',\n        'stages': {\n            'dev': {\n            },\n            'prod': {\n                'api_gateway_stage': 'prod',\n                'iam_role_arn': 'foobar',\n                'manage_iam_role': False,\n            }\n        }\n    }\n    c = Config(chalice_stage='dev',\n               config_from_disk=disk_config)\n    assert c.api_gateway_stage == 'dev'\n    assert c.manage_iam_role\n\n    prod = Config(chalice_stage='prod',\n                  config_from_disk=disk_config)\n    assert prod.api_gateway_stage == 'prod'\n    assert prod.iam_role_arn == 'foobar'\n    assert not prod.manage_iam_role\n\n\ndef test_can_chain_function_values():\n    disk_config = {\n        'lambda_timeout': 10,\n        'lambda_functions': {\n            'api_handler': {\n                'lambda_timeout': 15,\n                }\n            },\n        'stages': {\n            'dev': {\n                'lambda_timeout': 20,\n                'lambda_functions': {\n                    'api_handler': {\n                        'lambda_timeout': 30,\n                        }\n                    }\n                }\n            }\n        }\n    c = Config(chalice_stage='dev',\n               config_from_disk=disk_config)\n    assert c.lambda_timeout == 30\n\n\ndef test_can_set_stage_independent_function_values():\n    disk_config = {\n        'lambda_timeout': 10,\n        'lambda_functions': {\n            'api_handler': {\n                'lambda_timeout': 15,\n                }\n            }\n        }\n    c = Config(chalice_stage='dev',\n               config_from_disk=disk_config)\n    assert c.lambda_timeout == 15\n\n\ndef test_stage_overrides_function_values():\n    disk_config = {\n        'lambda_timeout': 10,\n        'lambda_functions': {\n            'api_handler': {\n                'lambda_timeout': 15,\n                }\n            },\n        'stages': {\n            'dev': {\n                'lambda_timeout': 20,\n                }\n            }\n        }\n    c = Config(chalice_stage='dev',\n               config_from_disk=disk_config)\n    assert c.lambda_timeout == 20\n\n\ndef test_can_create_scope_obj_with_new_function():\n    disk_config = {\n        'lambda_timeout': 10,\n        'stages': {\n            'dev': {\n                'manage_iam_role': True,\n                'iam_role_arn': 'role-arn',\n                'autogen_policy': True,\n                'iam_policy_file': 'policy.json',\n                'environment_variables': {'env': 'stage'},\n                'lambda_timeout': 1,\n                'lambda_memory_size': 1,\n                'tags': {'tag': 'stage'},\n                'lambda_functions': {\n                    'api_handler': {\n                        'lambda_timeout': 30,\n                    },\n                    'myauth': {\n                        # We're purposefully using different\n                        # values for everything in the stage\n                        # level config to ensure we can pull\n                        # from function scoped config properly.\n                        'manage_iam_role': True,\n                        'iam_role_arn': 'auth-role-arn',\n                        'autogen_policy': True,\n                        'iam_policy_file': 'function.json',\n                        'environment_variables': {'env': 'function'},\n                        'lambda_timeout': 2,\n                        'lambda_memory_size': 2,\n                        'tags': {'tag': 'function'},\n                    }\n                }\n            }\n        }\n    }\n    c = Config(chalice_stage='dev', config_from_disk=disk_config)\n    new_config = c.scope(chalice_stage='dev',\n                         function_name='myauth')\n    assert new_config.manage_iam_role\n    assert new_config.iam_role_arn == 'auth-role-arn'\n    assert new_config.autogen_policy\n    assert new_config.iam_policy_file == 'function.json'\n    assert new_config.environment_variables == {'env': 'function'}\n    assert new_config.lambda_timeout == 2\n    assert new_config.lambda_memory_size == 2\n    assert new_config.tags['tag'] == 'function'\n\n\n@pytest.mark.parametrize('stage_name,function_name,expected', [\n    ('dev', 'api_handler', 'dev-api-handler'),\n    ('dev', 'myauth', 'dev-myauth'),\n    ('beta', 'api_handler', 'beta-api-handler'),\n    ('beta', 'myauth', 'beta-myauth'),\n    ('prod', 'api_handler', 'prod-stage'),\n    ('prod', 'myauth', 'prod-stage'),\n    ('foostage', 'api_handler', 'global'),\n    ('foostage', 'myauth', 'global'),\n])\ndef test_can_create_scope_new_stage_and_function(stage_name, function_name,\n                                                 expected):\n    disk_config = {\n        'environment_variables': {'from': 'global'},\n        'stages': {\n            'dev': {\n                'environment_variables': {'from': 'dev-stage'},\n                'lambda_functions': {\n                    'api_handler': {\n                        'environment_variables': {\n                            'from': 'dev-api-handler',\n                        }\n                    },\n                    'myauth': {\n                        'environment_variables': {\n                            'from': 'dev-myauth',\n                        }\n                    }\n                }\n            },\n            'beta': {\n                'environment_variables': {'from': 'beta-stage'},\n                'lambda_functions': {\n                    'api_handler': {\n                        'environment_variables': {\n                            'from': 'beta-api-handler',\n                        }\n                    },\n                    'myauth': {\n                        'environment_variables': {\n                            'from': 'beta-myauth',\n                        }\n                    }\n                }\n            },\n            'prod': {\n                'environment_variables': {'from': 'prod-stage'},\n            }\n        }\n    }\n    c = Config(chalice_stage='dev', config_from_disk=disk_config)\n    new_config = c.scope(chalice_stage=stage_name,\n                         function_name=function_name)\n    assert new_config.environment_variables == {'from': expected}\n\n\ndef test_new_scope_config_is_separate_copy():\n    original = Config(chalice_stage='dev', function_name='foo')\n    new_config = original.scope(chalice_stage='prod', function_name='bar')\n\n    # The original should not have been mutated.\n    assert original.chalice_stage == 'dev'\n    assert original.function_name == 'foo'\n\n    assert new_config.chalice_stage == 'prod'\n    assert new_config.function_name == 'bar'\n\n\ndef test_environment_from_top_level():\n    config_from_disk = {'environment_variables': {\"foo\": \"bar\"}}\n    c = Config('dev', config_from_disk=config_from_disk)\n    assert c.environment_variables == config_from_disk['environment_variables']\n\n\ndef test_environment_from_stage_level():\n    config_from_disk = {\n        'stages': {\n            'prod': {\n                'environment_variables': {\"foo\": \"bar\"}\n            }\n        }\n    }\n    c = Config('prod', config_from_disk=config_from_disk)\n    assert c.environment_variables == (\n        config_from_disk['stages']['prod']['environment_variables'])\n\n\ndef test_env_vars_chain_merge():\n    config_from_disk = {\n        'environment_variables': {\n            'top_level': 'foo',\n            'shared_stage_key': 'from-top',\n            'shared_stage': 'from-top',\n        },\n        'stages': {\n            'prod': {\n                'environment_variables': {\n                    'stage_var': 'bar',\n                    'shared_stage_key': 'from-stage',\n                    'shared_stage': 'from-stage',\n                },\n                'lambda_functions': {\n                    'api_handler': {\n                        'environment_variables': {\n                            'function_key': 'from-function',\n                            'shared_stage': 'from-function',\n                        }\n                    }\n                }\n            }\n        }\n    }\n    c = Config('prod', config_from_disk=config_from_disk)\n    resolved = c.environment_variables\n    assert resolved == {\n        'top_level': 'foo',\n        'stage_var': 'bar',\n        'shared_stage': 'from-function',\n        'function_key': 'from-function',\n        'shared_stage_key': 'from-stage',\n    }\n\n\ndef test_can_load_python_version():\n    c = Config('dev')\n    major, minor = sys.version_info[0], sys.version_info[1]\n    assert c.lambda_python_version == f'python{major}.{minor}'\n\n\nclass TestConfigureMinimumCompressionSize(object):\n    def test_not_set(self):\n        c = Config('dev', config_from_disk={})\n        assert c.minimum_compression_size is None\n\n    def test_set_minimum_compression_size_global(self):\n        config_from_disk = {\n            'minimum_compression_size': 5000\n        }\n        c = Config('dev', config_from_disk=config_from_disk)\n        assert c.minimum_compression_size == 5000\n\n    def test_set_minimum_compression_size_stage(self):\n        config_from_disk = {\n            'stages': {\n                'dev': {\n                    'minimum_compression_size': 5000\n                }\n            }\n        }\n        c = Config('dev', config_from_disk=config_from_disk)\n        assert c.minimum_compression_size == 5000\n\n    def test_set_minimum_compression_size_override(self):\n        config_from_disk = {\n            'minimum_compression_size': 0,\n            'stages': {\n                'dev': {\n                    'minimum_compression_size': 5000\n                }\n            }\n        }\n        c = Config('dev', config_from_disk=config_from_disk)\n        assert c.minimum_compression_size == 5000\n\n\nclass TestConfigureLambdaMemorySize(object):\n    def test_not_set(self):\n        c = Config('dev', config_from_disk={})\n        assert c.lambda_memory_size is None\n\n    def test_set_lambda_memory_size_global(self):\n        config_from_disk = {\n            'lambda_memory_size': 256\n        }\n        c = Config('dev', config_from_disk=config_from_disk)\n        assert c.lambda_memory_size == 256\n\n    def test_set_lambda_memory_size_stage(self):\n        config_from_disk = {\n            'stages': {\n                'dev': {\n                    'lambda_memory_size': 256\n                }\n            }\n        }\n        c = Config('dev', config_from_disk=config_from_disk)\n        assert c.lambda_memory_size == 256\n\n    def test_set_lambda_memory_size_override(self):\n        config_from_disk = {\n            'lambda_memory_size': 128,\n            'stages': {\n                'dev': {\n                    'lambda_memory_size': 256\n                }\n            }\n        }\n        c = Config('dev', config_from_disk=config_from_disk)\n        assert c.lambda_memory_size == 256\n\n\nclass TestConfigureLambdaTimeout(object):\n    def test_not_set(self):\n        c = Config('dev', config_from_disk={})\n        assert c.lambda_timeout is None\n\n    def test_set_lambda_timeout_global(self):\n        config_from_disk = {\n            'lambda_timeout': 120\n        }\n        c = Config('dev', config_from_disk=config_from_disk)\n        assert c.lambda_timeout == 120\n\n    def test_set_lambda_memory_size_stage(self):\n        config_from_disk = {\n            'stages': {\n                'dev': {\n                    'lambda_timeout': 120\n                }\n            }\n        }\n        c = Config('dev', config_from_disk=config_from_disk)\n        assert c.lambda_timeout == 120\n\n    def test_set_lambda_memory_size_override(self):\n        config_from_disk = {\n            'lambda_timeout': 60,\n            'stages': {\n                'dev': {\n                    'lambda_timeout': 120\n                }\n            }\n        }\n        c = Config('dev', config_from_disk=config_from_disk)\n        assert c.lambda_timeout == 120\n\n\nclass TestConfigureTags(object):\n    def test_default_tags(self):\n        c = Config('dev', config_from_disk={'app_name': 'myapp'})\n        assert c.tags == {\n            'aws-chalice': 'version=%s:stage=dev:app=myapp' % chalice_version\n        }\n\n    def test_tags_global(self):\n        config_from_disk = {\n            'app_name': 'myapp',\n            'tags': {'mykey': 'myvalue'}\n        }\n        c = Config('dev', config_from_disk=config_from_disk)\n        assert c.tags == {\n            'mykey': 'myvalue',\n            'aws-chalice': 'version=%s:stage=dev:app=myapp' % chalice_version\n        }\n\n    def test_tags_stage(self):\n        config_from_disk = {\n            'app_name': 'myapp',\n            'stages': {\n                'dev': {\n                    'tags': {'mykey': 'myvalue'}\n                }\n            }\n        }\n        c = Config('dev', config_from_disk=config_from_disk)\n        assert c.tags == {\n            'mykey': 'myvalue',\n            'aws-chalice': 'version=%s:stage=dev:app=myapp' % chalice_version\n        }\n\n    def test_tags_merge(self):\n        config_from_disk = {\n            'app_name': 'myapp',\n            'tags': {\n                'onlyglobalkey': 'globalvalue',\n                'sharedkey': 'globalvalue',\n                'sharedstage': 'globalvalue',\n            },\n            'stages': {\n                'dev': {\n                    'tags': {\n                        'sharedkey': 'stagevalue',\n                        'sharedstage': 'stagevalue',\n                        'onlystagekey': 'stagevalue',\n                    },\n                    'lambda_functions': {\n                        'api_handler': {\n                            'tags': {\n                                'sharedkey': 'functionvalue',\n                                'onlyfunctionkey': 'functionvalue',\n                            }\n                        }\n                    }\n                }\n            }\n        }\n        c = Config('dev', config_from_disk=config_from_disk)\n        assert c.tags == {\n            'onlyglobalkey': 'globalvalue',\n            'onlystagekey': 'stagevalue',\n            'onlyfunctionkey': 'functionvalue',\n            'sharedstage': 'stagevalue',\n            'sharedkey': 'functionvalue',\n            'aws-chalice': 'version=%s:stage=dev:app=myapp' % chalice_version\n        }\n\n    def test_tags_specified_does_not_override_chalice_tag(self):\n        c = Config.create(\n            chalice_stage='dev', app_name='myapp',\n            tags={'aws-chalice': 'attempted-override'})\n        assert c.tags == {\n            'aws-chalice': 'version=%s:stage=dev:app=myapp' % chalice_version,\n        }\n\n\ndef test_deployed_resource_does_not_exist():\n    deployed = DeployedResources(\n        {'resources': [{'name': 'foo'}]}\n    )\n    with pytest.raises(ValueError):\n        deployed.resource_values('bar')\n\n\ndef test_deployed_api_mapping_resource():\n    deployed = DeployedResources(\n        {'resources': [\n            {'name': 'foo'},\n            {\n                \"name\": \"api_gateway_custom_domain\",\n                \"resource_type\": \"domain_name\",\n                \"api_mapping\": [\n                    {\n                        \"key\": \"path_key\"\n                    }\n                ]\n            }\n        ]}\n    )\n\n    name = 'api_gateway_custom_domain.api_mapping.path_key'\n    result = deployed.resource_values(name)\n    assert result == {\n        \"name\": \"api_gateway_custom_domain\",\n        \"resource_type\": \"domain_name\",\n        \"api_mapping\": [\n            {\n                \"key\": \"path_key\"\n            }\n        ]\n    }\n\n\ndef test_deployed_resource_exists():\n    deployed = DeployedResources(\n        {'resources': [{'name': 'foo'}]}\n    )\n    assert deployed.resource_values('foo') == {'name': 'foo'}\n    assert deployed.resource_names() == ['foo']\n\n\nclass TestUpgradeNewDeployer(object):\n    def setup_method(self):\n        # This is the \"old deployer\" format.\n        deployed = {\n            \"region\": \"us-west-2\",\n            \"api_handler_name\": \"app-dev\",\n            \"api_handler_arn\": (\n                \"arn:aws:lambda:us-west-2:123:function:app-dev\"),\n            \"rest_api_id\": \"my_rest_api_id\",\n            \"lambda_functions\": {\n                \"app-dev-foo\": {\n                    \"type\": \"pure_lambda\",\n                    \"arn\": (\n                        \"arn:aws:lambda:us-west-2:123:function:app-dev-foo\"\n                    )},\n            },\n            \"chalice_version\": \"1.1.1\",\n            \"api_gateway_stage\": \"api\",\n            \"backend\": \"api\",\n        }\n        self.old_deployed = {\"dev\": deployed}\n        # This is \"new deployer\" format.  The deployed resources\n        # are just a list of resources.\n        resources = [\n            {\"role_name\": \"app-dev\",\n             \"role_arn\": \"arn:aws:iam::123:role/app-dev\",\n             \"name\": \"default-role\",\n             \"resource_type\": \"iam_role\"},\n            {\"lambda_arn\": \"arn:aws:lambda:us-west-2:123:function:app-dev-foo\",\n             \"name\": \"foo\",\n             \"resource_type\": \"lambda_function\"},\n            {\"lambda_arn\": (\n                \"arn:aws:lambda:us-west-2:123:function:app-dev\"),\n             \"name\": \"api_handler\",\n             \"resource_type\": \"lambda_function\"},\n            {\"rest_api_id\": \"my_rest_api_id\",\n             \"name\": \"rest_api\",\n             \"resource_type\": \"rest_api\"}\n        ]\n        self.new_deployed = {\n            'stages': {\n                'dev': {\n                    'resources': resources\n                }\n            },\n            'schema_version': '2.0',\n        }\n        self.deployed_filename = os.path.join('.', '.chalice', 'deployed.json')\n        self.config = FixedDataConfig(\n            {self.deployed_filename: self.old_deployed},\n        )\n\n    def test_can_upgrade_rest_api(self):\n        resources = self.config.deployed_resources('dev')\n        # The 'default-role' isn't in this list because\n        # it's not in the old deployed.json so it's filled\n        # in on the first deploy with the new deployer.\n        assert sorted(resources.resource_names()) == [\n             'api_handler', 'foo', 'rest_api',\n        ]\n        assert resources.resource_values('rest_api') == {\n            'rest_api_id': 'my_rest_api_id',\n            'name': 'rest_api',\n            'resource_type': 'rest_api',\n        }\n\n    def test_upgrade_for_new_stage_gives_empty_values(self):\n        resources = self.config.deployed_resources('prod')\n        assert resources.resource_names() == []\n\n    def test_can_upgrade_pre10_lambda_functions(self):\n        deployed = {\n            \"region\": \"us-west-2\",\n            \"api_handler_name\": \"app-dev\",\n            \"api_handler_arn\": (\n                \"arn:aws:lambda:us-west-2:123:function:app-dev\"),\n            \"rest_api_id\": \"my_rest_api_id\",\n            \"lambda_functions\": {\n                # This is the old < 1.0 style where the\n                # value was just the lambda arn.\n                \"app-dev-foo\": \"my-lambda-arn\",\n            },\n            \"chalice_version\": \"0.10.0\",\n            \"api_gateway_stage\": \"api\",\n            \"backend\": \"api\",\n        }\n        self.old_deployed = {\"dev\": deployed}\n        self.config = FixedDataConfig(\n            {self.deployed_filename: self.old_deployed},\n        )\n        resources = self.config.deployed_resources('dev')\n        assert sorted(resources.resource_names()) == [\n            'api_handler', 'foo', 'rest_api',\n        ]\n        assert resources.resource_values('foo') == {\n            'lambda_arn': 'my-lambda-arn',\n            'name': 'foo',\n            'resource_type': 'lambda_function',\n        }\n"
  },
  {
    "path": "tests/unit/test_invoke.py",
    "content": "import json\n\nimport pytest\n\nfrom chalice.awsclient import TypedAWSClient\nfrom chalice.invoke import LambdaInvoker\nfrom chalice.invoke import LambdaInvokeHandler\nfrom chalice.invoke import LambdaResponseFormatter\nfrom chalice.invoke import UnhandledLambdaError\n\n\nclass FakeUI(object):\n    def __init__(self):\n        self.writes = []\n        self.errors = []\n\n    def write(self, value):\n        self.writes.append(value)\n\n    def error(self, value):\n        self.errors.append(value)\n\n\nclass FakeStreamingBody(object):\n    def __init__(self, value):\n        self._value = value\n\n    def read(self):\n        return self._value\n\n\nclass TestLambdaInvokeHandler(object):\n    def test_invoke_can_format_and_write_success_case(self, stubbed_session):\n        arn = 'arn:aws:lambda:region:id:function:name-dev'\n        stubbed_session.stub('lambda').invoke(\n            FunctionName=arn,\n            InvocationType='RequestResponse'\n        ).returns({\n            'StatusCode': 200,\n            'ExecutedVersion': '$LATEST',\n            'Payload': FakeStreamingBody(b'foobarbaz')\n        })\n        ui = FakeUI()\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        invoker = LambdaInvoker(arn, client)\n        formatter = LambdaResponseFormatter()\n        invoke_handler = LambdaInvokeHandler(invoker, formatter, ui)\n        invoke_handler.invoke()\n\n        stubbed_session.verify_stubs()\n        assert ['foobarbaz\\n'] == ui.writes\n\n    def test_invoke_can_format_and_write_error_case(self, stubbed_session):\n        arn = 'arn:aws:lambda:region:id:function:name-dev'\n        error = {\n            \"errorMessage\": \"Something bad happened\",\n            \"errorType\": \"Error\",\n            \"stackTrace\": [\n                [\"/path/file.py\", 123, \"main\", \"foo(bar)\"],\n                [\"/path/other_file.py\", 456, \"function\", \"bar = baz\"]\n            ]\n        }\n        serialized_error = json.dumps(error).encode('utf-8')\n        stubbed_session.stub('lambda').invoke(\n            FunctionName=arn,\n            InvocationType='RequestResponse'\n        ).returns({\n            'StatusCode': 200,\n            'FunctionError': 'Unhandled',\n            'ExecutedVersion': '$LATEST',\n            'Payload': FakeStreamingBody(serialized_error)\n        })\n        ui = FakeUI()\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        invoker = LambdaInvoker(arn, client)\n        formatter = LambdaResponseFormatter()\n        invoke_handler = LambdaInvokeHandler(invoker, formatter, ui)\n        with pytest.raises(UnhandledLambdaError):\n            invoke_handler.invoke()\n\n        stubbed_session.verify_stubs()\n        assert [(\n            'Traceback (most recent call last):\\n'\n            '  File \"/path/file.py\", line 123, in main\\n'\n            '    foo(bar)\\n'\n            '  File \"/path/other_file.py\", line 456, in function\\n'\n            '    bar = baz\\n'\n            'Error: Something bad happened\\n'\n        )] == ui.errors\n\n    def test_invoke_can_format_and_write_small_error_case(self,\n                                                          stubbed_session):\n        # Some error response payloads do not have the errorType or\n        # stackTrace key.\n        arn = 'arn:aws:lambda:region:id:function:name-dev'\n        error = {\n            \"errorMessage\": \"Something bad happened\",\n        }\n        serialized_error = json.dumps(error).encode('utf-8')\n        stubbed_session.stub('lambda').invoke(\n            FunctionName=arn,\n            InvocationType='RequestResponse'\n        ).returns({\n            'StatusCode': 200,\n            'FunctionError': 'Unhandled',\n            'ExecutedVersion': '$LATEST',\n            'Payload': FakeStreamingBody(serialized_error)\n        })\n        ui = FakeUI()\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        invoker = LambdaInvoker(arn, client)\n        formatter = LambdaResponseFormatter()\n        invoke_handler = LambdaInvokeHandler(invoker, formatter, ui)\n        with pytest.raises(UnhandledLambdaError):\n            invoke_handler.invoke()\n\n        stubbed_session.verify_stubs()\n        assert [(\n            'Something bad happened\\n'\n        )] == ui.errors\n\n\nclass TestLambdaInvoker(object):\n    def test_invoke_can_call_api_handler(self, stubbed_session):\n        arn = 'arn:aws:lambda:region:id:function:name-dev'\n        stubbed_session.stub('lambda').invoke(\n            FunctionName=arn,\n            InvocationType='RequestResponse'\n        ).returns({})\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        invoker = LambdaInvoker(arn, client)\n        invoker.invoke()\n        stubbed_session.verify_stubs()\n\n    def test_invoke_does_forward_payload(self, stubbed_session):\n        arn = 'arn:aws:lambda:region:id:function:name-dev'\n        stubbed_session.stub('lambda').invoke(\n            FunctionName=arn,\n            InvocationType='RequestResponse',\n            Payload=b'foobar',\n        ).returns({})\n        stubbed_session.activate_stubs()\n        client = TypedAWSClient(stubbed_session)\n        invoker = LambdaInvoker(arn, client)\n        invoker.invoke(b'foobar')\n        stubbed_session.verify_stubs()\n\n\nclass TestLambdaResponseFormatter(object):\n    def test_formatter_can_format_success(self):\n        formatter = LambdaResponseFormatter()\n        formatted = formatter.format_response({\n            'StatusCode': 200,\n            'ExecutedVersion': '$LATEST',\n            'Payload': FakeStreamingBody(b'foobarbaz')\n        })\n        assert 'foobarbaz\\n' == formatted\n\n    def test_formatter_can_format_list_stack_trace(self):\n        error = {\n            \"errorMessage\": \"Something bad happened\",\n            \"errorType\": \"Error\",\n            \"stackTrace\": [\n                [\"/path/file.py\", 123, \"main\", \"foo(bar)\"],\n                [\"/path/more.py\", 456, \"func\", \"bar = baz\"]\n            ]\n        }\n        serialized_error = json.dumps(error).encode('utf-8')\n        formatter = LambdaResponseFormatter()\n        formatted = formatter.format_response({\n            'StatusCode': 200,\n            'FunctionError': 'Unhandled',\n            'ExecutedVersion': '$LATEST',\n            'Payload': FakeStreamingBody(serialized_error)\n        })\n\n        assert (\n            'Traceback (most recent call last):\\n'\n            '  File \"/path/file.py\", line 123, in main\\n'\n            '    foo(bar)\\n'\n            '  File \"/path/more.py\", line 456, in func\\n'\n            '    bar = baz\\n'\n            'Error: Something bad happened\\n'\n        ) == formatted\n\n    def test_formatter_can_format_string_stack_trace(self):\n        error = {\n            \"errorMessage\": \"Something bad happened\",\n            \"errorType\": \"Error\",\n            \"stackTrace\": [\n                '  File \"/path/file.py\", line 123, in main\\n    foo(bar)\\n',\n                '  File \"/path/more.py\", line 456, in func\\n    bar = baz\\n',\n            ]\n        }\n        serialized_error = json.dumps(error).encode('utf-8')\n        formatter = LambdaResponseFormatter()\n        formatted = formatter.format_response({\n            'StatusCode': 200,\n            'FunctionError': 'Unhandled',\n            'ExecutedVersion': '$LATEST',\n            'Payload': FakeStreamingBody(serialized_error)\n        })\n\n        assert (\n            'Traceback (most recent call last):\\n'\n            '  File \"/path/file.py\", line 123, in main\\n'\n            '    foo(bar)\\n'\n            '  File \"/path/more.py\", line 456, in func\\n'\n            '    bar = baz\\n'\n            'Error: Something bad happened\\n'\n        ) == formatted\n\n    def test_formatter_can_format_simple_error(self):\n        error = {\n            \"errorMessage\": \"Something bad happened\",\n        }\n        serialized_error = json.dumps(error).encode('utf-8')\n        formatter = LambdaResponseFormatter()\n        formatted = formatter.format_response({\n            'StatusCode': 200,\n            'FunctionError': 'Unhandled',\n            'ExecutedVersion': '$LATEST',\n            'Payload': FakeStreamingBody(serialized_error)\n        })\n\n        assert 'Something bad happened\\n' == formatted\n"
  },
  {
    "path": "tests/unit/test_local.py",
    "content": "import re\nimport json\nimport decimal\nfrom unittest import mock\n\nimport pytest\nfrom pytest import fixture\nfrom six import BytesIO\nfrom six.moves.BaseHTTPServer import HTTPServer\n\nfrom chalice import app\nfrom chalice import local, BadRequestError, CORSConfig\nfrom chalice import Response\nfrom chalice import IAMAuthorizer\nfrom chalice import CognitoUserPoolAuthorizer\nfrom chalice.config import Config\nfrom chalice.local import LambdaContext\nfrom chalice.local import LocalARNBuilder\nfrom chalice.local import LocalGateway\nfrom chalice.local import LocalGatewayAuthorizer\nfrom chalice.local import NotAuthorizedError\nfrom chalice.local import ForbiddenError\nfrom chalice.local import InvalidAuthorizerError\nfrom chalice.local import LocalDevServer\n\n\nAWS_REQUEST_ID_PATTERN = re.compile(\n    '^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$',\n    re.I)\n\n\nclass FakeTimeSource(object):\n    def __init__(self, times):\n        \"\"\"Create a fake source of second-precision time.\n\n        :type time: List\n        :param time: List of times that the time source should return in the\n            order it should return them. These should be in seconds.\n        \"\"\"\n        self._times = times\n\n    def time(self):\n        \"\"\"Get the next time.\n\n        This is for mimicing the Clock interface used in local.\n        \"\"\"\n        time = self._times.pop(0)\n        return time\n\n\nclass ChaliceStubbedHandler(local.ChaliceRequestHandler):\n    requestline = ''\n    request_version = 'HTTP/1.1'\n\n    def setup(self):\n        self.rfile = BytesIO()\n        self.wfile = BytesIO()\n        self.requestline = ''\n\n    def finish(self):\n        pass\n\n\nclass CustomSampleChalice(app.Chalice):\n    def custom_method(self):\n        return \"foo\"\n\n\n@pytest.fixture\ndef arn_builder():\n    return LocalARNBuilder()\n\n\n@pytest.fixture\ndef lambda_context_args():\n    # LambdaContext has several positional args before the ones that we\n    # care about for the timing tests, this gives reasonable defaults for\n    # those arguments.\n    return ['lambda_name', 256]\n\n\n@fixture\ndef custom_sample_app():\n    demo = CustomSampleChalice(app_name='custom-demo-app')\n    demo.debug = True\n\n    return demo\n\n\n@fixture\ndef sample_app():\n    demo = app.Chalice('demo-app')\n    demo.debug = True\n\n    @demo.route('/index', methods=['GET'])\n    def index():\n        return {'hello': 'world'}\n\n    @demo.route('/names/{name}', methods=['GET'])\n    def name(name):\n        return {'provided-name': name}\n\n    @demo.route('/put', methods=['PUT'])\n    def put():\n        return {'body': demo.current_request.json_body}\n\n    @demo.route('/cors', methods=['GET', 'PUT'], cors=True)\n    def cors():\n        return {'cors': True}\n\n    @demo.route('/custom_cors', methods=['GET', 'PUT'], cors=CORSConfig(\n        allow_origin='https://foo.bar',\n        allow_headers=['Header-A', 'Header-B'],\n        expose_headers=['Header-A', 'Header-B'],\n        max_age=600,\n        allow_credentials=True\n    ))\n    def custom_cors():\n        return {'cors': True}\n\n    @demo.route('/cors-enabled-for-one-method', methods=['GET'])\n    def without_cors():\n        return {'ok': True}\n\n    @demo.route('/cors-enabled-for-one-method', methods=['POST'], cors=True)\n    def with_cors():\n        return {'ok': True}\n\n    @demo.route('/options', methods=['OPTIONS'])\n    def options():\n        return {'options': True}\n\n    @demo.route('/delete', methods=['DELETE'])\n    def delete():\n        return {'delete': True}\n\n    @demo.route('/patch', methods=['PATCH'])\n    def patch():\n        return {'patch': True}\n\n    @demo.route('/badrequest')\n    def badrequest():\n        raise BadRequestError('bad-request')\n\n    @demo.route('/decimals')\n    def decimals():\n        return decimal.Decimal('100')\n\n    @demo.route('/query-string')\n    def query_string():\n        return demo.current_request.query_params\n\n    @demo.route('/query-string-multi')\n    def query_string_multi():\n        params = demo.current_request.query_params\n        keys = {k: params.getlist(k) for k in params}\n        return keys\n\n    @demo.route('/custom-response')\n    def custom_response():\n        return Response(body='text',\n                        status_code=200,\n                        headers={'Content-Type': 'text/plain'})\n\n    @demo.route('/binary', methods=['POST'],\n                content_types=['application/octet-stream'])\n    def binary_round_trip():\n        return Response(body=demo.current_request.raw_body,\n                        status_code=200,\n                        headers={'Content-Type': 'application/octet-stream'})\n\n    @demo.route('/multi-value-header')\n    def multi_value_header():\n        return Response(body={},\n                        status_code=200,\n                        headers={\n                            'Set-Cookie': ['CookieA=ValueA', 'CookieB=ValueB']\n                        })\n\n    return demo\n\n\n@fixture\ndef demo_app_auth():\n    demo = app.Chalice('app-name')\n\n    def _policy(effect, resource, action='execute-api:Invoke'):\n        return {\n            'context': {},\n            'principalId': 'user',\n            'policyDocument': {\n                'Version': '2012-10-17',\n                'Statement': [\n                    {\n                        'Action': action,\n                        'Effect': effect,\n                        'Resource': resource,\n                    }\n                ]\n            }\n        }\n\n    @demo.authorizer()\n    def auth_with_explicit_policy(auth_request):\n        token = auth_request.token\n        if token == 'allow':\n            return _policy(\n                effect='Allow', resource=[\n                    \"arn:aws:execute-api:mars-west-1:123456789012:\"\n                    \"ymy8tbxw7b/api/GET/explicit\"])\n        else:\n            return _policy(\n                effect='Deny', resource=[\n                    \"arn:aws:execute-api:mars-west-1:123456789012:\"\n                    \"ymy8tbxw7b/api/GET/explicit\"])\n\n    @demo.authorizer()\n    def demo_authorizer_returns_none(auth_request):\n        return None\n\n    @demo.authorizer()\n    def auth_with_multiple_actions(auth_request):\n        return _policy(\n            effect='Allow', resource=[\n                    \"arn:aws:execute-api:mars-west-1:123456789012:\"\n                    \"ymy8tbxw7b/api/GET/multi\"],\n            action=['execute-api:Invoke', 'execute-api:Other']\n        )\n\n    @demo.authorizer()\n    def demo_auth(auth_request):\n        token = auth_request.token\n        if token == 'allow':\n            return app.AuthResponse(routes=['/index'], principal_id='user')\n        else:\n            return app.AuthResponse(routes=[], principal_id='user')\n\n    @demo.authorizer()\n    def resource_auth(auth_request):\n        token = auth_request.token\n        if token == 'allow':\n            return app.AuthResponse(routes=['/resource/foobar'],\n                                    principal_id='user')\n        else:\n            return app.AuthResponse(routes=[], principal_id='user')\n\n    @demo.authorizer()\n    def all_auth(auth_request):\n        token = auth_request.token\n        if token == 'allow':\n            return app.AuthResponse(routes=['*'], principal_id='user')\n        else:\n            return app.AuthResponse(routes=[], principal_id='user')\n\n    @demo.authorizer()\n    def landing_page_auth(auth_request):\n        token = auth_request.token\n        if token == 'allow':\n            return app.AuthResponse(routes=['/'], principal_id='user')\n        else:\n            return app.AuthResponse(routes=[], principal_id='user')\n\n    iam_authorizer = IAMAuthorizer()\n\n    cognito_authorizer = CognitoUserPoolAuthorizer('app-name', [])\n\n    @demo.route('/', authorizer=landing_page_auth)\n    def landing_view():\n        return {}\n\n    @demo.route('/index', authorizer=demo_auth)\n    def index_view():\n        return {}\n\n    @demo.route('/secret', authorizer=demo_auth)\n    def secret_view():\n        return {}\n\n    @demo.route('/resource/{name}', authorizer=resource_auth)\n    def single_value(name):\n        return {'resource': name}\n\n    @demo.route('/secret/{value}', authorizer=all_auth)\n    def secret_view_value(value):\n        return {'secret': value}\n\n    @demo.route('/explicit', authorizer=auth_with_explicit_policy)\n    def explicit():\n        return {}\n\n    @demo.route('/multi', authorizer=auth_with_multiple_actions)\n    def multi():\n        return {}\n\n    @demo.route('/iam', authorizer=iam_authorizer)\n    def iam_route():\n        return {}\n\n    @demo.route('/cognito', authorizer=cognito_authorizer)\n    def cognito_route():\n        return {}\n\n    @demo.route('/none', authorizer=demo_authorizer_returns_none)\n    def none_auth():\n        return {}\n\n    return demo\n\n\n@fixture\ndef handler(sample_app):\n    config = Config()\n    chalice_handler = ChaliceStubbedHandler(\n        None, ('127.0.0.1', 2000), None, app_object=sample_app, config=config)\n    chalice_handler.sample_app = sample_app\n    return chalice_handler\n\n\n@fixture\ndef auth_handler(demo_app_auth):\n    config = Config()\n    chalice_handler = ChaliceStubbedHandler(\n        None, ('127.0.0.1', 2000), None, app_object=demo_app_auth,\n        config=config)\n    chalice_handler.sample_app = demo_app_auth\n    return chalice_handler\n\n\ndef _get_raw_body_from_response_stream(handler):\n    # This is going to include things like status code and\n    # response headers in the raw stream.  We just care about the\n    # body for now so we'll split lines.\n    raw_response = handler.wfile.getvalue()\n    body = raw_response.splitlines()[-1]\n    return body\n\n\ndef _get_body_from_response_stream(handler):\n    body = _get_raw_body_from_response_stream(handler)\n    return json.loads(body)\n\n\ndef set_current_request(handler, method, path, headers=None):\n    if headers is None:\n        headers = {'content-type': 'application/json'}\n    handler.command = method\n    handler.path = path\n    handler.headers = headers\n\n\ndef test_can_convert_request_handler_to_lambda_event(handler):\n    set_current_request(handler, method='GET', path='/index')\n    handler.do_GET()\n    assert _get_body_from_response_stream(handler) == {'hello': 'world'}\n\n\ndef test_uses_http_11(handler):\n    set_current_request(handler, method='GET', path='/index')\n    handler.do_GET()\n    response_lines = handler.wfile.getvalue().splitlines()\n    assert b'HTTP/1.1 200 OK' in response_lines\n\n\ndef test_can_route_url_params(handler):\n    set_current_request(handler, method='GET', path='/names/james')\n    handler.do_GET()\n    assert _get_body_from_response_stream(handler) == {\n        'provided-name': 'james'}\n\n\ndef test_can_route_put_with_body(handler):\n    body = b'{\"foo\": \"bar\"}'\n    headers = {'content-type': 'application/json',\n               'content-length': len(body)}\n    set_current_request(handler, method='PUT', path='/put',\n                        headers=headers)\n    handler.rfile.write(body)\n    handler.rfile.seek(0)\n\n    handler.do_PUT()\n    assert _get_body_from_response_stream(handler) == {\n        'body': {'foo': 'bar'}}\n\n\ndef test_will_respond_with_cors_enabled(handler):\n    headers = {'content-type': 'application/json', 'origin': 'null'}\n    set_current_request(handler, method='GET', path='/cors', headers=headers)\n    handler.do_GET()\n    response_lines = handler.wfile.getvalue().splitlines()\n    assert b'Access-Control-Allow-Origin: *' in response_lines\n\n\ndef test_will_respond_with_custom_cors_enabled(handler):\n    headers = {'content-type': 'application/json', 'origin': 'null'}\n    set_current_request(handler, method='GET', path='/custom_cors',\n                        headers=headers)\n    handler.do_GET()\n    response = handler.wfile.getvalue().splitlines()\n    assert b'HTTP/1.1 200 OK' in response\n    assert b'Access-Control-Allow-Origin: https://foo.bar' in response\n    assert (b'Access-Control-Allow-Headers: Authorization,Content-Type,'\n            b'Header-A,Header-B,X-Amz-Date,X-Amz-Security-Token,'\n            b'X-Api-Key') in response\n    assert b'Access-Control-Expose-Headers: Header-A,Header-B' in response\n    assert b'Access-Control-Max-Age: 600' in response\n    assert b'Access-Control-Allow-Credentials: true' in response\n\n\ndef test_will_respond_with_custom_cors_enabled_options(handler):\n    headers = {'content-type': 'application/json', 'origin': 'null'}\n    set_current_request(handler, method='OPTIONS', path='/custom_cors',\n                        headers=headers)\n    handler.do_OPTIONS()\n    response = handler.wfile.getvalue().decode().splitlines()\n    assert 'HTTP/1.1 200 OK' in response\n    assert 'Access-Control-Allow-Origin: https://foo.bar' in response\n    assert ('Access-Control-Allow-Headers: Authorization,Content-Type,'\n            'Header-A,Header-B,X-Amz-Date,X-Amz-Security-Token,'\n            'X-Api-Key') in response\n    assert 'Access-Control-Expose-Headers: Header-A,Header-B' in response\n    assert 'Access-Control-Max-Age: 600' in response\n    assert 'Access-Control-Allow-Credentials: true' in response\n    assert 'Content-Length: 0' in response\n\n    # Ensure that the Access-Control-Allow-Methods header is sent\n    # and that it sends all the correct methods over.\n    methods_lines = [line for line in response\n                     if line.startswith('Access-Control-Allow-Methods')]\n    assert len(methods_lines) == 1\n    method_line = methods_lines[0]\n    _, methods_header_value = method_line.split(': ')\n    methods = methods_header_value.strip().split(',')\n    assert ['GET', 'OPTIONS', 'PUT'] == sorted(methods)\n\n\ndef test_can_preflight_request(handler):\n    headers = {'content-type': 'application/json', 'origin': 'null'}\n    set_current_request(handler, method='OPTIONS', path='/cors',\n                        headers=headers)\n    handler.do_OPTIONS()\n    response_lines = handler.wfile.getvalue().splitlines()\n    assert b'Access-Control-Allow-Origin: *' in response_lines\n\n\ndef test_non_preflight_options_request(handler):\n    headers = {'content-type': 'application/json', 'origin': 'null'}\n    set_current_request(handler, method='OPTIONS', path='/options',\n                        headers=headers)\n    handler.do_OPTIONS()\n    assert _get_body_from_response_stream(handler) == {'options': True}\n\n\ndef test_preflight_request_should_succeed_even_if_cors_disabled(handler):\n    headers = {'content-type': 'application/json', 'origin': 'null'}\n    set_current_request(handler, method='OPTIONS', path='/index',\n                        headers=headers)\n    handler.do_OPTIONS()\n    response_lines = handler.wfile.getvalue().splitlines()\n    assert b'HTTP/1.1 200 OK' in response_lines\n\n\ndef test_preflight_returns_correct_methods_in_access_allow_header(handler):\n    headers = {'content-type': 'application/json', 'origin': 'null'}\n    set_current_request(handler, method='OPTIONS',\n                        path='/cors-enabled-for-one-method',\n                        headers=headers)\n    handler.do_OPTIONS()\n    response_lines = handler.wfile.getvalue().splitlines()\n    assert b'HTTP/1.1 200 OK' in response_lines\n    assert b'Access-Control-Allow-Methods: POST,OPTIONS' in response_lines\n\n\ndef test_errors_converted_to_json_response(handler):\n    set_current_request(handler, method='GET', path='/badrequest')\n    handler.do_GET()\n    assert _get_body_from_response_stream(handler) == {\n        'Code': 'BadRequestError',\n        'Message': 'bad-request'\n    }\n\n\ndef test_can_support_delete_method(handler):\n    set_current_request(handler, method='DELETE', path='/delete')\n    handler.do_DELETE()\n    assert _get_body_from_response_stream(handler) == {'delete': True}\n\n\ndef test_can_support_patch_method(handler):\n    set_current_request(handler, method='PATCH', path='/patch')\n    handler.do_PATCH()\n    assert _get_body_from_response_stream(handler) == {'patch': True}\n\n\ndef test_can_support_decimals(handler):\n    set_current_request(handler, method='GET', path='/decimals')\n    handler.do_PATCH()\n    assert _get_body_from_response_stream(handler) == 100\n\n\ndef test_unsupported_methods_raise_error(handler):\n    set_current_request(handler, method='POST', path='/index')\n    handler.do_POST()\n    assert _get_body_from_response_stream(handler) == {\n        'Code': 'MethodNotAllowedError',\n        'Message': 'Unsupported method: POST'\n    }\n\n\ndef test_can_round_trip_binary(handler):\n    body = b'\\xFE\\xED'\n    set_current_request(\n        handler, method='POST', path='/binary',\n        headers={\n            'content-type': 'application/octet-stream',\n            'accept': 'application/octet-stream',\n            'content-length': len(body)\n        }\n    )\n    handler.rfile.write(body)\n    handler.rfile.seek(0)\n\n    handler.do_POST()\n    response = _get_raw_body_from_response_stream(handler)\n    assert response == body\n\n\ndef test_querystring_is_mapped(handler):\n    set_current_request(handler, method='GET', path='/query-string?a=b&c=d')\n    handler.do_GET()\n    assert _get_body_from_response_stream(handler) == {'a': 'b', 'c': 'd'}\n\n\ndef test_empty_querystring_is_none(handler):\n    set_current_request(handler, method='GET', path='/query-string')\n    handler.do_GET()\n    assert _get_body_from_response_stream(handler) is None\n\n\ndef test_querystring_list_is_mapped(handler):\n    set_current_request(\n        handler,\n        method='GET', path='/query-string-multi?a=b&c=d&a=c&e='\n    )\n    handler.do_GET()\n    expected = {'a': ['b', 'c'], 'c': ['d'], 'e': ['']}\n    assert _get_body_from_response_stream(handler) == expected\n\n\ndef test_querystring_undefined_is_mapped_consistent_with_apigateway(handler):\n    # API Gateway picks up the last element of duplicate keys in a\n    # querystring\n    set_current_request(handler, method='GET', path='/query-string?a=b&a=c')\n    handler.do_GET()\n    assert _get_body_from_response_stream(handler) == {'a': 'c'}\n\n\ndef test_content_type_included_once(handler):\n    set_current_request(handler, method='GET', path='/custom-response')\n    handler.do_GET()\n    value = handler.wfile.getvalue()\n    response_lines = value.splitlines()\n    content_header_lines = [line for line in response_lines\n                            if line.startswith(b'Content-Type')]\n    assert len(content_header_lines) == 1\n\n\ndef test_can_deny_unauthed_request(auth_handler):\n    set_current_request(auth_handler, method='GET', path='/index')\n    auth_handler.do_GET()\n    value = auth_handler.wfile.getvalue()\n    response_lines = value.splitlines()\n    assert b'HTTP/1.1 401 Unauthorized' in response_lines\n    assert b'x-amzn-ErrorType: UnauthorizedException' in response_lines\n    assert b'Content-Type: application/json' in response_lines\n    assert b'{\"message\":\"Unauthorized\"}' in response_lines\n\n\ndef test_multi_value_header(handler):\n    set_current_request(handler, method='GET', path='/multi-value-header')\n    handler.do_GET()\n    response = handler.wfile.getvalue().decode().splitlines()\n    assert 'Set-Cookie: CookieA=ValueA' in response\n    assert 'Set-Cookie: CookieB=ValueB' in response\n\n\n@pytest.mark.parametrize('actual_url,matched_url', [\n    ('/foo', '/foo'),\n    ('/foo/', '/foo'),\n    ('/foo/bar', '/foo/bar'),\n    ('/foo/other', '/foo/{capture}'),\n    ('/names/foo', '/names/{capture}'),\n    ('/names/bar', '/names/{capture}'),\n    ('/names/bar/', '/names/{capture}'),\n    ('/names/', None),\n    ('/nomatch', None),\n    ('/names/bar/wrong', None),\n    ('/a/z/c', '/a/{capture}/c'),\n    ('/a/b/c', '/a/b/c'),\n])\ndef test_can_match_exact_route(actual_url, matched_url):\n    matcher = local.RouteMatcher([\n        '/foo', '/foo/{capture}', '/foo/bar',\n        '/names/{capture}',\n        '/a/{capture}/c', '/a/b/c'\n    ])\n    if matched_url is not None:\n        assert matcher.match_route(actual_url).route == matched_url\n    else:\n        with pytest.raises(ValueError):\n            matcher.match_route(actual_url)\n\n\ndef test_lambda_event_contains_source_ip():\n    converter = local.LambdaEventConverter(\n        local.RouteMatcher(['/foo/bar']))\n    event = converter.create_lambda_event(\n        method='GET',\n        path='/foo/bar',\n        headers={'content-type': 'application/json'}\n    )\n    source_ip = event.get('requestContext').get('identity').get('sourceIp')\n    assert source_ip == local.LambdaEventConverter.LOCAL_SOURCE_IP\n\n\ndef test_can_create_lambda_event():\n    converter = local.LambdaEventConverter(\n        local.RouteMatcher(['/foo/bar', '/foo/{capture}']))\n    event = converter.create_lambda_event(\n        method='GET',\n        path='/foo/other',\n        headers={'content-type': 'application/json'}\n    )\n    assert event == {\n        'requestContext': {\n            'httpMethod': 'GET',\n            'resourcePath': '/foo/{capture}',\n            'path': '/foo/other',\n            'identity': {\n                'sourceIp': local.LambdaEventConverter.LOCAL_SOURCE_IP\n            },\n        },\n        'headers': {'content-type': 'application/json'},\n        'pathParameters': {'capture': 'other'},\n        'multiValueQueryStringParameters': None,\n        'body': None,\n        'stageVariables': {},\n    }\n\n\ndef test_parse_query_string():\n    converter = local.LambdaEventConverter(\n        local.RouteMatcher(['/foo/bar', '/foo/{capture}']))\n    event = converter.create_lambda_event(\n        method='GET',\n        path='/foo/other?a=1&b=&c=3',\n        headers={'content-type': 'application/json'}\n    )\n    assert event == {\n        'requestContext': {\n            'httpMethod': 'GET',\n            'resourcePath': '/foo/{capture}',\n            'path': '/foo/other',\n            'identity': {\n                'sourceIp': local.LambdaEventConverter.LOCAL_SOURCE_IP\n            },\n        },\n        'headers': {'content-type': 'application/json'},\n        'pathParameters': {'capture': 'other'},\n        'multiValueQueryStringParameters': {'a': ['1'], 'b': [''], 'c': ['3']},\n        'body': None,\n        'stageVariables': {},\n    }\n\n\ndef test_can_create_lambda_event_for_put_request():\n    converter = local.LambdaEventConverter(\n        local.RouteMatcher(['/foo/bar', '/foo/{capture}']))\n    event = converter.create_lambda_event(\n        method='PUT',\n        path='/foo/other',\n        headers={'content-type': 'application/json'},\n        body='{\"foo\": \"bar\"}',\n    )\n    assert event == {\n        'requestContext': {\n            'httpMethod': 'PUT',\n            'resourcePath': '/foo/{capture}',\n            'path': '/foo/other',\n            'identity': {\n                'sourceIp': local.LambdaEventConverter.LOCAL_SOURCE_IP\n            },\n        },\n        'headers': {'content-type': 'application/json'},\n        'pathParameters': {'capture': 'other'},\n        'multiValueQueryStringParameters': None,\n        'body': '{\"foo\": \"bar\"}',\n        'stageVariables': {},\n    }\n\n\ndef test_can_create_lambda_event_for_post_with_formencoded_body():\n    converter = local.LambdaEventConverter(\n        local.RouteMatcher(['/foo/bar', '/foo/{capture}']))\n    form_body = 'foo=bar&baz=qux'\n    event = converter.create_lambda_event(\n        method='POST',\n        path='/foo/other',\n        headers={'content-type': 'application/x-www-form-urlencoded'},\n        body=form_body,\n    )\n    assert event == {\n        'requestContext': {\n            'httpMethod': 'POST',\n            'resourcePath': '/foo/{capture}',\n            'path': '/foo/other',\n            'identity': {\n                'sourceIp': local.LambdaEventConverter.LOCAL_SOURCE_IP\n            },\n        },\n        'headers': {'content-type': 'application/x-www-form-urlencoded'},\n        'pathParameters': {'capture': 'other'},\n        'multiValueQueryStringParameters': None,\n        'body': form_body,\n        'stageVariables': {},\n    }\n\n\ndef test_can_provide_port_to_local_server(sample_app):\n    dev_server = local.create_local_server(sample_app, None, '127.0.0.1',\n                                           port=23456)\n    assert dev_server.server.server_port == 23456\n\n\ndef test_can_provide_host_to_local_server(sample_app):\n    dev_server = local.create_local_server(sample_app, None, host='0.0.0.0',\n                                           port=23456)\n    assert dev_server.host == '0.0.0.0'\n\n\ndef test_wraps_custom_sample_app_with_local_chalice(custom_sample_app):\n    dev_server = local.create_local_server(custom_sample_app, None,\n                                           host='0.0.0.0', port=23456)\n    assert isinstance(dev_server.app_object, local.LocalChalice)\n    assert isinstance(dev_server.app_object, custom_sample_app.__class__)\n    assert dev_server.app_object.custom_method() == 'foo'\n\n\nclass TestLambdaContext(object):\n    def test_can_get_remaining_time_once(self, lambda_context_args):\n        time_source = FakeTimeSource([0, 5])\n        context = LambdaContext(*lambda_context_args, max_runtime_ms=10000,\n                                time_source=time_source)\n        time_remaining = context.get_remaining_time_in_millis()\n        assert time_remaining == 5000\n\n    def test_can_get_remaining_time_multiple(self, lambda_context_args):\n        time_source = FakeTimeSource([0, 3, 7, 9])\n        context = LambdaContext(*lambda_context_args, max_runtime_ms=10000,\n                                time_source=time_source)\n\n        time_remaining = context.get_remaining_time_in_millis()\n        assert time_remaining == 7000\n        time_remaining = context.get_remaining_time_in_millis()\n        assert time_remaining == 3000\n        time_remaining = context.get_remaining_time_in_millis()\n        assert time_remaining == 1000\n\n    def test_does_populate_aws_request_id_with_valid_uuid(self,\n                                                          lambda_context_args):\n        context = LambdaContext(*lambda_context_args)\n        assert AWS_REQUEST_ID_PATTERN.match(context.aws_request_id)\n\n    def test_does_set_version_to_latest(self, lambda_context_args):\n        context = LambdaContext(*lambda_context_args)\n        assert context.function_version == '$LATEST'\n\n\nclass TestLocalGateway(object):\n    def test_can_invoke_function(self):\n        demo = app.Chalice('app-name')\n\n        @demo.route('/')\n        def index_view():\n            return {'foo': 'bar'}\n\n        gateway = LocalGateway(demo, Config())\n        response = gateway.handle_request('GET', '/', {}, '')\n        body = json.loads(response['body'])\n        assert body['foo'] == 'bar'\n\n    def test_does_populate_context(self):\n        demo = app.Chalice('app-name')\n\n        @demo.route('/context')\n        def context_view():\n            context = demo.lambda_context\n            return {\n                'name': context.function_name,\n                'memory': context.memory_limit_in_mb,\n                'version': context.function_version,\n                'timeout': context.get_remaining_time_in_millis(),\n                'request_id': context.aws_request_id,\n            }\n\n        disk_config = {\n            'lambda_timeout': 10,\n            'lambda_memory_size': 256,\n        }\n        config = Config(chalice_stage='api', config_from_disk=disk_config)\n        gateway = LocalGateway(demo, config)\n        response = gateway.handle_request('GET', '/context', {}, '')\n        body = json.loads(response['body'])\n        assert body['name'] == 'api_handler'\n        assert body['memory'] == 256\n        assert body['version'] == '$LATEST'\n        assert body['timeout'] > 10\n        assert body['timeout'] <= 10000\n        assert AWS_REQUEST_ID_PATTERN.match(body['request_id'])\n\n    def test_defaults_timeout_if_needed(self):\n        demo = app.Chalice('app-name')\n\n        @demo.route('/context')\n        def context_view():\n            context = demo.lambda_context\n            return {\n                'remaining': context.get_remaining_time_in_millis(),\n            }\n\n        disk_config = {}\n        config = Config(chalice_stage='api', config_from_disk=disk_config)\n        gateway = LocalGateway(demo, config)\n        response = gateway.handle_request('GET', '/context', {}, '')\n        body = json.loads(response['body'])\n        assert body['remaining'] <= gateway.MAX_LAMBDA_EXECUTION_TIME * 1000\n\n    def test_can_validate_route_with_variables(self, demo_app_auth):\n        gateway = LocalGateway(demo_app_auth, Config())\n        response = gateway.handle_request(\n            'GET', '/secret/foobar', {'Authorization': 'allow'}, '')\n        json_body = json.loads(response['body'])\n        assert json_body['secret'] == 'foobar'\n\n    def test_can_allow_route_with_variables(self, demo_app_auth):\n        gateway = LocalGateway(demo_app_auth, Config())\n        response = gateway.handle_request(\n            'GET', '/resource/foobar', {'Authorization': 'allow'}, '')\n        json_body = json.loads(response['body'])\n        assert json_body['resource'] == 'foobar'\n\n    def test_does_send_500_when_authorizer_returns_none(self, demo_app_auth):\n        gateway = LocalGateway(demo_app_auth, Config())\n        with pytest.raises(InvalidAuthorizerError):\n            gateway.handle_request(\n                'GET', '/none', {'Authorization': 'foobarbaz'}, '')\n\n    def test_can_deny_route_with_variables(self, demo_app_auth):\n        gateway = LocalGateway(demo_app_auth, Config())\n        with pytest.raises(ForbiddenError):\n            gateway.handle_request(\n                'GET', '/resource/foobarbaz', {'Authorization': 'allow'}, '')\n\n    def test_does_deny_unauthed_request(self, demo_app_auth):\n        gateway = LocalGateway(demo_app_auth, Config())\n        with pytest.raises(ForbiddenError) as ei:\n            gateway.handle_request(\n                'GET', '/index', {'Authorization': 'deny'}, '')\n        exception_body = str(ei.value.body)\n        assert ('{\"Message\": '\n                '\"User is not authorized to '\n                'access this resource\"}') in exception_body\n\n    def test_does_throw_unauthorized_when_no_auth_token_present_on_valid_route(\n            self, demo_app_auth):\n        gateway = LocalGateway(demo_app_auth, Config())\n        with pytest.raises(NotAuthorizedError) as ei:\n            gateway.handle_request(\n                'GET', '/index', {}, '')\n        exception_body = str(ei.value.body)\n        assert '{\"message\":\"Unauthorized\"}' in exception_body\n\n    def test_does_deny_with_forbidden_when_route_not_found(\n            self, demo_app_auth):\n        gateway = LocalGateway(demo_app_auth, Config())\n        with pytest.raises(ForbiddenError) as ei:\n            gateway.handle_request('GET', '/badindex', {}, '')\n        exception_body = str(ei.value.body)\n        assert 'Missing Authentication Token' in exception_body\n\n    def test_does_deny_with_forbidden_when_auth_token_present(\n            self, demo_app_auth):\n        gateway = LocalGateway(demo_app_auth, Config())\n        with pytest.raises(ForbiddenError) as ei:\n            gateway.handle_request('GET', '/badindex',\n                                   {'Authorization': 'foobar'}, '')\n        # The message should be a more complicated error message to do with\n        # signing the request. It always ends with the Authorization token\n        # that we passed up, so we can check for that.\n        exception_body = str(ei.value.body)\n        assert 'Authorization=foobar' in exception_body\n\n\nclass TestLocalBuiltinAuthorizers(object):\n    def test_can_authorize_empty_path(self, lambda_context_args,\n                                      demo_app_auth, create_event):\n        # Ensures that / routes work since that is a special case in the\n        # API Gateway arn generation where an extra / is appended to the end\n        # of the arn.\n        authorizer = LocalGatewayAuthorizer(demo_app_auth)\n        path = '/'\n        event = create_event(path, 'GET', {})\n        event['headers']['authorization'] = 'allow'\n        context = LambdaContext(*lambda_context_args)\n        event, context = authorizer.authorize(path, event, context)\n        assert event['requestContext']['authorizer']['principalId'] == 'user'\n\n    def test_can_call_method_without_auth(self, lambda_context_args,\n                                          create_event):\n        demo = app.Chalice('app-name')\n\n        @demo.route('/index')\n        def index_view():\n            return {}\n\n        path = '/index'\n        authorizer = LocalGatewayAuthorizer(demo)\n        original_event = create_event(path, 'GET', {})\n        original_context = LambdaContext(*lambda_context_args)\n        event, context = authorizer.authorize(\n            path, original_event, original_context)\n        # Assert that when the authorizer.authorize is called and there is no\n        # authorizer defined for a particular route that it is a noop.\n        assert original_event == event\n        assert original_context == context\n\n    def test_does_raise_not_authorized_error(self, demo_app_auth,\n                                             lambda_context_args,\n                                             create_event):\n        authorizer = LocalGatewayAuthorizer(demo_app_auth)\n        path = '/index'\n        event = create_event(path, 'GET', {})\n        context = LambdaContext(*lambda_context_args)\n        with pytest.raises(NotAuthorizedError):\n            authorizer.authorize(path, event, context)\n\n    def test_does_authorize_valid_requests(self, demo_app_auth,\n                                           lambda_context_args, create_event):\n        authorizer = LocalGatewayAuthorizer(demo_app_auth)\n        path = '/index'\n        event = create_event(path, 'GET', {})\n        event['headers']['authorization'] = 'allow'\n        context = LambdaContext(*lambda_context_args)\n        event, context = authorizer.authorize(path, event, context)\n        assert event['requestContext']['authorizer']['principalId'] == 'user'\n\n    def test_does_authorize_unsupported_authorizer(self, demo_app_auth,\n                                                   lambda_context_args,\n                                                   create_event):\n        authorizer = LocalGatewayAuthorizer(demo_app_auth)\n        path = '/iam'\n        event = create_event(path, 'GET', {})\n        context = LambdaContext(*lambda_context_args)\n        with pytest.warns(Warning) as recorded_warnings:\n            new_event, new_context = authorizer.authorize(path, event, context)\n        assert event == new_event\n        assert context == new_context\n        assert len(recorded_warnings) == 1\n        warning = recorded_warnings[0]\n        assert issubclass(warning.category, UserWarning)\n        assert ('IAMAuthorizer is not a supported in local '\n                'mode. All requests made against a route will be authorized'\n                ' to allow local testing.') in str(warning.message)\n\n    def test_cannot_access_view_without_permission(self, demo_app_auth,\n                                                   lambda_context_args,\n                                                   create_event):\n        authorizer = LocalGatewayAuthorizer(demo_app_auth)\n        path = '/secret'\n        event = create_event(path, 'GET', {})\n        event['headers']['authorization'] = 'allow'\n        context = LambdaContext(*lambda_context_args)\n        with pytest.raises(ForbiddenError):\n            authorizer.authorize(path, event, context)\n\n    def test_can_understand_explicit_auth_policy(self, demo_app_auth,\n                                                 lambda_context_args,\n                                                 create_event):\n        authorizer = LocalGatewayAuthorizer(demo_app_auth)\n        path = '/explicit'\n        event = create_event(path, 'GET', {})\n        event['headers']['authorization'] = 'allow'\n        context = LambdaContext(*lambda_context_args)\n        event, context = authorizer.authorize(path, event, context)\n        assert event['requestContext']['authorizer']['principalId'] == 'user'\n\n    def test_can_understand_explicit_deny_policy(self, demo_app_auth,\n                                                 lambda_context_args,\n                                                 create_event):\n        # Our auto-generated policies from the AuthResponse object do not\n        # contain any Deny clauses, however we also allow the user to return\n        # a dictionary that is transated into a policy, so we have to\n        # account for the ability for a user to set an explicit deny policy.\n        # It should behave exactly as not getting permission added with an\n        # allow.\n        authorizer = LocalGatewayAuthorizer(demo_app_auth)\n        path = '/explicit'\n        event = create_event(path, 'GET', {})\n        context = LambdaContext(*lambda_context_args)\n        with pytest.raises(NotAuthorizedError):\n            authorizer.authorize(path, event, context)\n\n    def test_can_understand_multi_actions(self, demo_app_auth,\n                                          lambda_context_args,\n                                          create_event):\n        authorizer = LocalGatewayAuthorizer(demo_app_auth)\n        path = '/multi'\n        event = create_event(path, 'GET', {})\n        event['headers']['authorization'] = 'allow'\n        context = LambdaContext(*lambda_context_args)\n        event, context = authorizer.authorize(path, event, context)\n        assert event['requestContext']['authorizer']['principalId'] == 'user'\n\n    def test_can_understand_cognito_token(self, lambda_context_args,\n                                          demo_app_auth, create_event):\n        # Ensures that / routes work since that is a special case in the\n        # API Gateway arn generation where an extra / is appended to the end\n        # of the arn.\n        authorizer = LocalGatewayAuthorizer(demo_app_auth)\n        path = '/cognito'\n        event = create_event(path, 'GET', {})\n        event[\"headers\"][\"authorization\"] = \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhYWFhYWFhYS1iYmJiLWNjY2MtZGRkZC1lZWVlZWVlZWVlZWUiLCJhdWQiOiJ4eHh4eHh4eHh4eHhleGFtcGxlIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInRva2VuX3VzZSI6ImlkIiwiYXV0aF90aW1lIjoxNTAwMDA5NDAwLCJpc3MiOiJodHRwczovL2NvZ25pdG8taWRwLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tL3VzLWVhc3QtMV9leGFtcGxlIiwiY29nbml0bzp1c2VybmFtZSI6ImphbmVkb2UiLCJleHAiOjE1ODQ3MjM2MTYsImdpdmVuX25hbWUiOiJKYW5lIiwiaWF0IjoxNTAwMDA5NDAwLCJlbWFpbCI6ImphbmVkb2VAZXhhbXBsZS5jb20iLCJqdGkiOiJkN2UxMTMzYS0xZTNhLTQyMzEtYWU3Yi0yOGQ4NWVlMGIxNGQifQ.p35Yj9KJD5RbfPWGL08IJHgson8BhdGLPQqUOiF0-KM\"  # noqa\n        context = LambdaContext(*lambda_context_args)\n        event, context = authorizer.authorize(path, event, context)\n        principal_id = event['requestContext']['authorizer']['principalId']\n        assert principal_id == 'janedoe'\n\n    def test_does_authorize_unsupported_cognito_token(self,\n                                                      lambda_context_args,\n                                                      demo_app_auth,\n                                                      create_event):\n        authorizer = LocalGatewayAuthorizer(demo_app_auth)\n        path = '/cognito'\n        event = create_event(path, 'GET', {})\n        event[\"headers\"][\"authorization\"] = \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhYWFhYWFhYS1iYmJiLWNjY2MtZGRkZC1lZWVlZWVlZWVlZWUiLCJhdWQiOiJ4eHh4eHh4eHh4eHhleGFtcGxlIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInRva2VuX3VzZSI6ImlkIiwiYXV0aF90aW1lIjoxNTAwMDA5NDAwLCJpc3MiOiJodHRwczovL2NvZ25pdG8taWRwLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tL3VzLWVhc3QtMV9leGFtcGxlIiwiZXhwIjoxNTg0NzIzNjE2LCJnaXZlbl9uYW1lIjoiSmFuZSIsImlhdCI6MTUwMDAwOTQwMCwiZW1haWwiOiJqYW5lZG9lQGV4YW1wbGUuY29tIiwianRpIjoiZDdlMTEzM2EtMWUzYS00MjMxLWFlN2ItMjhkODVlZTBiMTRkIn0.SN5n-A3kxboNYg0sGIOipVUksCdn6xRJmAK9kSZof10\"  # noqa\n        context = LambdaContext(*lambda_context_args)\n        with pytest.warns(Warning) as recorded_warnings:\n            new_event, new_context = authorizer.authorize(path, event, context)\n        assert event == new_event\n        assert context == new_context\n        assert len(recorded_warnings) == 1\n        warning = recorded_warnings[0]\n        assert issubclass(warning.category, UserWarning)\n        assert ('CognitoUserPoolAuthorizer for machine-to-machine '\n                'communicaiton is not supported in local mode. All requests '\n                'made against a route will be authorized to allow local '\n                'testing.') in str(warning.message)\n\n\nclass TestArnBuilder(object):\n    def test_can_create_basic_arn(self, arn_builder):\n        arn = ('arn:aws:execute-api:mars-west-1:123456789012:ymy8tbxw7b'\n               '/api/GET/resource')\n        built_arn = arn_builder.build_arn('GET', '/resource')\n        assert arn == built_arn\n\n    def test_can_create_root_arn(self, arn_builder):\n        arn = ('arn:aws:execute-api:mars-west-1:123456789012:ymy8tbxw7b'\n               '/api/GET//')\n        built_arn = arn_builder.build_arn('GET', '/')\n        assert arn == built_arn\n\n    def test_can_create_multi_part_arn(self, arn_builder):\n        arn = ('arn:aws:execute-api:mars-west-1:123456789012:ymy8tbxw7b'\n               '/api/GET/path/to/resource')\n        built_arn = arn_builder.build_arn('GET', '/path/to/resource')\n        assert arn == built_arn\n\n    def test_can_create_glob_method_arn(self, arn_builder):\n        arn = ('arn:aws:execute-api:mars-west-1:123456789012:ymy8tbxw7b'\n               '/api/*/resource')\n        built_arn = arn_builder.build_arn('*', '/resource')\n        assert arn == built_arn\n\n    def test_build_arn_with_query_params(self, arn_builder):\n        arn = ('arn:aws:execute-api:mars-west-1:123456789012:ymy8tbxw7b/api/'\n               '*/resource')\n        built_arn = arn_builder.build_arn('*', '/resource?foo=bar')\n        assert arn == built_arn\n\n\n@pytest.mark.parametrize('arn,pattern', [\n    ('mars-west-2:123456789012:ymy8tbxw7b/api/GET/foo',\n     'mars-west-2:123456789012:ymy8tbxw7b/api/GET/foo'\n     ),\n    ('mars-west-1:123456789012:ymy8tbxw7b/api/GET/foobar',\n     'mars-west-1:123456789012:ymy8tbxw7b/api/GET/*'\n     ),\n    ('mars-west-1:123456789012:ymy8tbxw7b/api/PUT/foobar',\n     'mars-west-1:123456789012:ymy8tbxw7b/api/???/foobar'\n     ),\n    ('mars-west-1:123456789012:ymy8tbxw7b/api/GET/foobar',\n     'mars-west-1:123456789012:ymy8tbxw7b/api/???/*'\n     ),\n    ('mars-west-1:123456789012:ymy8tbxw7b/api/GET/foobar',\n     'mars-west-1:123456789012:*/api/GET/*'\n     ),\n    ('mars-west-2:123456789012:ymy8tbxw7b/api/GET/foobar',\n     '*'\n     ),\n    ('mars-west-2:123456789012:ymy8tbxw7b/api/GET/foo.bar',\n     'mars-west-2:123456789012:ymy8tbxw7b/*/GET/*')\n])\ndef test_can_allow_route_arns(arn, pattern):\n    prefix = 'arn:aws:execute-api:'\n    full_arn = '%s%s' % (prefix, arn)\n    full_pattern = '%s%s' % (prefix, pattern)\n    matcher = local.ARNMatcher(full_arn)\n    does_match = matcher.does_any_resource_match([full_pattern])\n    assert does_match is True\n\n\n@pytest.mark.parametrize('arn,pattern', [\n    ('mars-west-1:123456789012:ymy8tbxw7b/api/GET/foobar',\n     'mars-west-1:123456789012:ymy8tbxw7b/api/PUT/*'\n     ),\n    ('mars-west-1:123456789012:ymy8tbxw7b/api/GET/foobar',\n     'mars-west-1:123456789012:ymy8tbxw7b/api/??/foobar'\n     ),\n    ('mars-west-1:123456789012:ymy8tbxw7b/api/GET/foobar',\n     'mars-west-2:123456789012:ymy8tbxw7b/api/???/*'\n     ),\n    ('mars-west-2:123456789012:ymy8tbxw7b/api/GET/foobar',\n     'mars-west-2:123456789012:ymy8tbxw7b/*/GET/foo...')\n])\ndef test_can_deny_route_arns(arn, pattern):\n    prefix = 'arn:aws:execute-api:'\n    full_arn = '%s%s' % (prefix, arn)\n    full_pattern = '%s%s' % (prefix, pattern)\n    matcher = local.ARNMatcher(full_arn)\n    does_match = matcher.does_any_resource_match([full_pattern])\n    assert does_match is False\n\n\n@pytest.mark.parametrize('arn,patterns', [\n    ('mars-west-1:123456789012:ymy8tbxw7b/api/GET/foobar',\n     [\n         'mars-west-1:123456789012:ymy8tbxw7b/api/PUT/*',\n         'mars-west-1:123456789012:ymy8tbxw7b/api/GET/foobar'\n     ]),\n    ('mars-west-1:123456789012:ymy8tbxw7b/api/GET/foobar',\n     [\n         'mars-west-1:123456789012:ymy8tbxw7b/api/GET/foobar',\n         'mars-west-1:123456789012:ymy8tbxw7b/api/PUT/*'\n     ]),\n    ('mars-west-1:123456789012:ymy8tbxw7b/api/GET/foobar',\n     [\n         'mars-west-1:123456789012:ymy8tbxw7b/api/PUT/foobar',\n         '*'\n     ])\n])\ndef test_can_allow_multiple_resource_arns(arn, patterns):\n    prefix = 'arn:aws:execute-api:'\n    full_arn = '%s%s' % (prefix, arn)\n    full_patterns = ['%s%s' % (prefix, pattern) for pattern in patterns]\n    matcher = local.ARNMatcher(full_arn)\n    does_match = matcher.does_any_resource_match(full_patterns)\n    assert does_match is True\n\n\n@pytest.mark.parametrize('arn,patterns', [\n    ('mars-west-1:123456789012:ymy8tbxw7b/api/GET/foobar',\n     [\n         'mars-west-1:123456789012:ymy8tbxw7b/api/POST/*',\n         'mars-west-1:123456789012:ymy8tbxw7b/api/PUT/foobar'\n     ]),\n    ('mars-west-1:123456789012:ymy8tbxw7b/api/GET/foobar',\n     [\n         'mars-west-2:123456789012:ymy8tbxw7b/api/GET/foobar',\n         'mars-west-2:123456789012:ymy8tbxw7b/api/*/*'\n     ])\n])\ndef test_can_deny_multiple_resource_arns(arn, patterns):\n    prefix = 'arn:aws:execute-api:'\n    full_arn = '%s%s' % (prefix, arn)\n    full_patterns = ['%s%s' % (prefix, pattern) for pattern in patterns]\n    matcher = local.ARNMatcher(full_arn)\n    does_match = matcher.does_any_resource_match(full_patterns)\n    assert does_match is False\n\n\nclass TestLocalDevServer(object):\n    def test_can_delegate_to_server(self, sample_app):\n        http_server = mock.Mock(spec=HTTPServer)\n        dev_server = LocalDevServer(\n            sample_app, Config(), '0.0.0.0', 8000,\n            server_cls=lambda *args: http_server,\n        )\n\n        dev_server.handle_single_request()\n        http_server.handle_request.assert_called_with()\n\n        dev_server.serve_forever()\n        http_server.serve_forever.assert_called_with()\n\n    def test_host_and_port_forwarded_to_server_creation(self, sample_app):\n        provided_args = []\n\n        def args_recorder(*args):\n            provided_args[:] = list(args)\n\n        LocalDevServer(\n            sample_app, Config(), '0.0.0.0', 8000,\n            server_cls=args_recorder,\n        )\n\n        assert provided_args[0] == ('0.0.0.0', 8000)\n\n    def test_does_use_daemon_threads(self, sample_app):\n        server = LocalDevServer(\n            sample_app, Config(), '0.0.0.0', 8000\n        )\n\n        assert server.server.daemon_threads\n"
  },
  {
    "path": "tests/unit/test_logs.py",
    "content": "import time\nfrom unittest import mock\nfrom datetime import datetime, timedelta\n\nfrom chalice import logs\nfrom chalice.awsclient import TypedAWSClient\nfrom six import StringIO\n\n\nNO_OPTIONS = logs.LogRetrieveOptions()\n\n\ndef message(log_message, log_stream_name='logStreamName'):\n    return {\n        'logStreamName': log_stream_name,\n        'message': log_message,\n    }\n\n\ndef test_can_convert_since_to_start_time():\n    options = logs.LogRetrieveOptions.create(\n        follow=True, since='2020-01-01T00:00:00',\n        include_lambda_messages=False)\n    assert options.max_entries is None\n    assert options.start_time == datetime(2020, 1, 1, 0, 0, 0)\n    assert not options.include_lambda_messages\n\n\ndef test_can_retrieve_all_logs():\n    client = mock.Mock(spec=TypedAWSClient)\n    log_message = message('first')\n    client.iter_log_events.return_value = [log_message]\n    retriever = logs.LogRetriever(client, 'loggroup')\n    messages = list(retriever.retrieve_logs(NO_OPTIONS))\n    expected = log_message.copy()\n    # We also inject a logShortId.\n    expected['logShortId'] = 'logStreamName'\n    assert messages == [expected]\n\n\ndef test_can_support_max_entries():\n    client = mock.Mock(spec=TypedAWSClient)\n    client.iter_log_events.return_value = [message('first'), message('second')]\n    retriever = logs.LogRetriever(client, 'loggroup')\n    messages = list(\n        retriever.retrieve_logs(logs.LogRetrieveOptions(max_entries=1)))\n    assert len(messages) == 1\n    assert messages[0]['message'] == 'first'\n\n\ndef test_can_exclude_lambda_messages():\n    client = mock.Mock(spec=TypedAWSClient)\n    client.iter_log_events.return_value = [\n        message('START RequestId: id Version: $LATEST'),\n        message('END RequestId: id'),\n        message('REPORT RequestId: id Duration: 0.42 ms   '\n                'Billed Duration: 100 ms     '\n                'Memory Size: 128 MB Max Memory Used: 19 MB'),\n        message('Not a lambda message'),\n    ]\n    retriever = logs.LogRetriever(client, 'loggroup')\n    messages = list(retriever.retrieve_logs(\n        logs.LogRetrieveOptions(include_lambda_messages=False)))\n    assert len(messages) == 1\n    assert messages[0]['message'] == 'Not a lambda message'\n\n\ndef test_can_parse_short_id():\n    log_message = message(\n        'Log Message',\n        '2017/04/28/[$LATEST]fc219a0d613b40e9b5c58e6b8fd2320c'\n    )\n    client = mock.Mock(spec=TypedAWSClient)\n    client.iter_log_events.return_value = [log_message]\n    retriever = logs.LogRetriever(client, 'loggroup')\n    messages = list(retriever.retrieve_logs(\n        logs.LogRetrieveOptions(include_lambda_messages=False)))\n    assert len(messages) == 1\n    assert messages[0]['logShortId'] == 'fc219a'\n\n\ndef test_can_create_from_arn():\n    retriever = logs.LogRetriever.create_from_lambda_arn(\n        mock.sentinel.client,\n        'arn:aws:lambda:us-east-1:123:function:my-function'\n    )\n    assert isinstance(retriever, logs.LogRetriever)\n\n\ndef test_can_display_logs():\n    retriever = mock.Mock(spec=logs.LogRetriever)\n    retriever.retrieve_logs.return_value = [\n        {'timestamp': 'NOW', 'logShortId': 'shortId', 'message': 'One'},\n        {'timestamp': 'NOW', 'logShortId': 'shortId', 'message': 'Two'},\n        {'timestamp': 'NOW', 'logShortId': 'shortId', 'message': 'Three'},\n    ]\n    stream = StringIO()\n    logs.display_logs(retriever, retrieve_options=NO_OPTIONS, stream=stream)\n    assert stream.getvalue().splitlines() == [\n        'NOW shortId One',\n        'NOW shortId Two',\n        'NOW shortId Three',\n    ]\n\n\ndef test_can_iterate_through_all_log_events():\n    client = mock.Mock(spec=TypedAWSClient)\n    client.iter_log_events.return_value = [\n        {'timestamp': 'NOW', 'logShortId': 'shortId', 'message': 'One'},\n        {'timestamp': 'NOW', 'logShortId': 'shortId', 'message': 'Two'},\n        {'timestamp': 'NOW', 'logShortId': 'shortId', 'message': 'Three'},\n    ]\n    event_gen = logs.LogEventGenerator(client)\n    assert list(event_gen.iter_log_events(\n        log_group_name='mygroup', options=NO_OPTIONS)) == [\n        {'timestamp': 'NOW', 'logShortId': 'shortId', 'message': 'One'},\n        {'timestamp': 'NOW', 'logShortId': 'shortId', 'message': 'Two'},\n        {'timestamp': 'NOW', 'logShortId': 'shortId', 'message': 'Three'},\n    ]\n\n\ndef test_can_follow_log_events():\n    sleep = mock.Mock(spec=time.sleep)\n    client = mock.Mock(spec=TypedAWSClient)\n    client.filter_log_events.side_effect = [\n        # First page of results has nextToken indicating there's\n        # more results.\n        {'events': [{'eventId': '1', 'timestamp': 1},\n                    {'eventId': '2', 'timestamp': 2},\n                    {'eventId': '3', 'timestamp': 3}],\n         'nextToken': 'nextToken1'},\n        # Second page with no more results, also note the\n        # timestamps are out of order.\n        {'events': [{'eventId': '4', 'timestamp': 4},\n                    {'eventId': '6', 'timestamp': 6},\n                    {'eventId': '5', 'timestamp': 5}]},\n        # We then poll again with no new results for timestamp=6.\n        {'events': [{'eventId': '6', 'timestamp': 6}]},\n        # And now we get new results.\n        {'events': [{'eventId': '6', 'timestamp': 6},\n                    # Same timestamp we're querying (6) but a new event.\n                    {'eventId': '6NEW', 'timestamp': 6},\n                    {'eventId': '7', 'timestamp': 7},\n                    {'eventId': '8', 'timestamp': 8}]},\n        KeyboardInterrupt(),\n    ]\n    event_gen = logs.FollowLogEventGenerator(client, sleep)\n    options = logs.LogRetrieveOptions(start_time=1)\n    assert list(event_gen.iter_log_events(\n        log_group_name='mygroup', options=options)) == [\n        {'eventId': '1', 'timestamp': 1},\n        {'eventId': '2', 'timestamp': 2},\n        {'eventId': '3', 'timestamp': 3},\n        {'eventId': '4', 'timestamp': 4},\n        # Note we don't try to sort these entries.\n        {'eventId': '6', 'timestamp': 6},\n        {'eventId': '5', 'timestamp': 5},\n        {'eventId': '6NEW', 'timestamp': 6},\n        {'eventId': '7', 'timestamp': 7},\n        {'eventId': '8', 'timestamp': 8},\n    ]\n    assert client.filter_log_events.call_args_list == [\n        mock.call(log_group_name='mygroup', start_time=1),\n        mock.call(log_group_name='mygroup', start_time=1,\n                  next_token='nextToken1'),\n        mock.call(log_group_name='mygroup', start_time=6),\n        mock.call(log_group_name='mygroup', start_time=6),\n        mock.call(log_group_name='mygroup', start_time=8),\n    ]\n\n\ndef test_follow_logs_initially_empty():\n    sleep = mock.Mock(spec=time.sleep)\n    client = mock.Mock(spec=TypedAWSClient)\n    client.filter_log_events.side_effect = [\n        {'events': []},\n        {'events': []},\n        {'events': [{'eventId': '1', 'timestamp': 1},\n                    {'eventId': '2', 'timestamp': 2},\n                    {'eventId': '3', 'timestamp': 3}]},\n        KeyboardInterrupt(),\n    ]\n    event_gen = logs.FollowLogEventGenerator(client, sleep)\n    assert list(event_gen.iter_log_events(\n        log_group_name='mygroup', options=NO_OPTIONS)) == [\n        {'eventId': '1', 'timestamp': 1},\n        {'eventId': '2', 'timestamp': 2},\n        {'eventId': '3', 'timestamp': 3},\n    ]\n\n\ndef test_follow_logs_single_pages_only():\n    sleep = mock.Mock(spec=time.sleep)\n    client = mock.Mock(spec=TypedAWSClient)\n    client.filter_log_events.side_effect = [\n        {'events': [{'eventId': '1', 'timestamp': 1}]},\n        {'events': [{'eventId': '2', 'timestamp': 2}]},\n        {'events': [{'eventId': '3', 'timestamp': 3}]},\n        KeyboardInterrupt(),\n    ]\n    event_gen = logs.FollowLogEventGenerator(client, sleep)\n    assert list(event_gen.iter_log_events(\n        log_group_name='mygroup', options=NO_OPTIONS)) == [\n        {'eventId': '1', 'timestamp': 1},\n        {'eventId': '2', 'timestamp': 2},\n        {'eventId': '3', 'timestamp': 3},\n    ]\n\n\ndef test_follow_logs_last_page_empty():\n    sleep = mock.Mock(spec=time.sleep)\n    client = mock.Mock(spec=TypedAWSClient)\n    client.filter_log_events.side_effect = [\n        {'events': [{'eventId': '1', 'timestamp': 1},\n                    {'eventId': '2', 'timestamp': 2},\n                    {'eventId': '3', 'timestamp': 3}],\n         'nextToken': 'nextToken1'},\n        {'events': [{'eventId': '4', 'timestamp': 4},\n                    {'eventId': '6', 'timestamp': 6},\n                    {'eventId': '5', 'timestamp': 5}],\n         'nextToken': 'nextToken2'},\n        # You can sometimes get a next token but with no events.\n        {'events': [], 'nextToken': 'nextToken3'},\n        {'events': []},\n        {'events': [{'eventId': '7', 'timestamp': 7}]},\n        KeyboardInterrupt(),\n    ]\n    event_gen = logs.FollowLogEventGenerator(client, sleep)\n    options = logs.LogRetrieveOptions(start_time=1)\n    assert list(event_gen.iter_log_events(\n        log_group_name='mygroup', options=options)) == [\n        {'eventId': '1', 'timestamp': 1},\n        {'eventId': '2', 'timestamp': 2},\n        {'eventId': '3', 'timestamp': 3},\n        {'eventId': '4', 'timestamp': 4},\n        {'eventId': '6', 'timestamp': 6},\n        {'eventId': '5', 'timestamp': 5},\n        {'eventId': '7', 'timestamp': 7},\n    ]\n    assert client.filter_log_events.call_args_list == [\n        mock.call(log_group_name='mygroup', start_time=1),\n        mock.call(log_group_name='mygroup', start_time=1,\n                  next_token='nextToken1'),\n        mock.call(log_group_name='mygroup', start_time=1,\n                  next_token='nextToken2'),\n        mock.call(log_group_name='mygroup', start_time=1,\n                  next_token='nextToken3'),\n        mock.call(log_group_name='mygroup', start_time=6),\n        mock.call(log_group_name='mygroup', start_time=7),\n    ]\n\n\ndef test_follow_logs_all_pages_empty_with_pagination():\n    sleep = mock.Mock(spec=time.sleep)\n    client = mock.Mock(spec=TypedAWSClient)\n    client.filter_log_events.side_effect = [\n        {'events': [], 'nextToken': 'nextToken1'},\n        {'events': [], 'nextToken': 'nextToken2'},\n        {'events': [], 'nextToken': 'nextToken3'},\n        {'events': []},\n        KeyboardInterrupt(),\n    ]\n    event_gen = logs.FollowLogEventGenerator(client, sleep)\n    options = logs.LogRetrieveOptions(start_time=1)\n    assert list(event_gen.iter_log_events(\n        log_group_name='mygroup', options=options)) == []\n    assert client.filter_log_events.call_args_list == [\n        mock.call(log_group_name='mygroup', start_time=1),\n        mock.call(log_group_name='mygroup', start_time=1,\n                  next_token='nextToken1'),\n        mock.call(log_group_name='mygroup', start_time=1,\n                  next_token='nextToken2'),\n        mock.call(log_group_name='mygroup', start_time=1,\n                  next_token='nextToken3'),\n        # The last call should not use a next token.\n        mock.call(log_group_name='mygroup', start_time=1)\n    ]\n\n\ndef test_follow_logs_defaults_to_ten_minutes():\n    # To avoid having to patch out/pass in utcnow(), we'll just make sure\n    # that the start_time used is more recent than 10 minutes from now.\n    # This is a safe assumption because we're saving the current time before\n    # we invoke iter_log_events().\n    ten_minutes = datetime.utcnow() - timedelta(minutes=10)\n    options = logs.LogRetrieveOptions.create(follow=True)\n    assert options.start_time >= ten_minutes\n\n\ndef test_dont_default_if_explicit_since_is_provided():\n    utcnow = datetime.utcnow()\n    options = logs.LogRetrieveOptions.create(follow=True, since=str(utcnow))\n    assert options.start_time == utcnow\n"
  },
  {
    "path": "tests/unit/test_package.py",
    "content": "import os\nimport json\nfrom unittest import mock\n\nimport pytest\nfrom chalice.config import Config\nfrom chalice import package\nfrom chalice.constants import LAMBDA_TRUST_POLICY\nfrom chalice.deploy.appgraph import ApplicationGraphBuilder, DependencyBuilder\nfrom chalice.awsclient import TypedAWSClient\nfrom chalice.deploy.deployer import BuildStage\nfrom chalice.deploy import models\nfrom chalice.deploy.swagger import SwaggerGenerator\nfrom chalice.package import PackageOptions\nfrom chalice.utils import OSUtils\n\n\n@pytest.fixture\ndef mock_swagger_generator():\n    return mock.Mock(spec=SwaggerGenerator)\n\n\ndef test_can_create_app_packager():\n    config = Config()\n    options = PackageOptions(mock.Mock(spec=TypedAWSClient))\n    packager = package.create_app_packager(config, options)\n    assert isinstance(packager, package.AppPackager)\n\n\ndef test_can_create_terraform_app_packager():\n    config = Config()\n    options = PackageOptions(mock.Mock(spec=TypedAWSClient))\n    packager = package.create_app_packager(config, options,  'terraform')\n    assert isinstance(packager, package.AppPackager)\n\n\ndef test_template_post_processor_moves_files_once():\n    mock_osutils = mock.Mock(spec=OSUtils)\n    p = package.SAMCodeLocationPostProcessor(mock_osutils)\n    template = {\n        'Resources': {\n            'foo': {\n                'Type': 'AWS::Serverless::Function',\n                'Properties': {\n                    'CodeUri': 'old-dir.zip',\n                }\n            },\n            'bar': {\n                'Type': 'AWS::Serverless::Function',\n                'Properties': {\n                    'CodeUri': 'old-dir.zip',\n                }\n            },\n        }\n    }\n    p.process(template, config=None,\n              outdir='outdir', chalice_stage_name='dev')\n    mock_osutils.copy.assert_called_with(\n        'old-dir.zip', os.path.join('outdir', 'deployment.zip'))\n    assert mock_osutils.copy.call_count == 1\n    assert template['Resources']['foo']['Properties']['CodeUri'] == (\n        './deployment.zip'\n    )\n    assert template['Resources']['bar']['Properties']['CodeUri'] == (\n        './deployment.zip'\n    )\n\n\ndef test_terraform_post_processor_moves_files_once():\n    mock_osutils = mock.Mock(spec=OSUtils)\n    p = package.TerraformCodeLocationPostProcessor(mock_osutils)\n    template = {\n        'resource': {\n            'aws_lambda_function': {\n                'foo': {'filename': 'old-dir.zip'},\n                'bar': {'filename': 'old-dir.zip'},\n            }\n        }\n    }\n\n    p.process(template, config=None,\n              outdir='outdir', chalice_stage_name='dev')\n    mock_osutils.copy.assert_called_with(\n        'old-dir.zip', os.path.join('outdir', 'deployment.zip'))\n    assert mock_osutils.copy.call_count == 1\n    assert template['resource']['aws_lambda_function'][\n        'foo']['filename'] == ('${path.module}/deployment.zip')\n    assert template['resource']['aws_lambda_function'][\n        'bar']['filename'] == ('${path.module}/deployment.zip')\n\n\ndef test_template_generator_default():\n    tgen = package.TemplateGenerator(Config(),\n                                     PackageOptions(\n                                         mock.Mock(spec=TypedAWSClient)\n                                     ))\n\n    with pytest.raises(package.UnsupportedFeatureError):\n        tgen.dispatch(models.Model(), {})\n\n\nclass TestTemplateMergePostProcessor(object):\n    def _test_can_call_merge(self, file_template, template_name):\n        mock_osutils = mock.Mock(spec=OSUtils)\n        mock_osutils.get_file_contents.return_value = json.dumps(file_template)\n        mock_merger = mock.Mock(spec=package.TemplateMerger)\n        mock_merger.merge.return_value = {}\n        p = package.TemplateMergePostProcessor(\n            mock_osutils, mock_merger, package.JSONTemplateSerializer(),\n            merge_template=template_name)\n        template = {\n            'Resources': {\n                'foo': {\n                    'Type': 'AWS::Serverless::Function',\n                    'Properties': {\n                        'CodeUri': 'old-dir.zip',\n                    }\n                },\n                'bar': {\n                    'Type': 'AWS::Serverless::Function',\n                    'Properties': {\n                        'CodeUri': 'old-dir.zip',\n                    }\n                },\n            }\n        }\n\n        config = mock.MagicMock(spec=Config)\n\n        p.process(\n            template, config=config, outdir='outdir', chalice_stage_name='dev')\n\n        assert mock_osutils.file_exists.call_count == 1\n        assert mock_osutils.get_file_contents.call_count == 1\n        mock_merger.merge.assert_called_once_with(file_template, template)\n\n    def test_can_call_merge(self):\n        file_template = {\n            \"Resources\": {\n                \"foo\": {\n                    \"Properties\": {\n                        \"Environment\": {\n                            \"Variables\": {\"Name\": \"Foo\"}\n                        }\n                    }\n                }\n            }\n        }\n\n        self._test_can_call_merge(file_template, 'extras.json')\n\n    def test_can_call_merge_with_yaml(self):\n        file_template = '''\n            Resources:\n              foo:\n                Properties:\n                  Environment:\n                    Variables:\n                      Name: Foo\n        '''\n\n        self._test_can_call_merge(file_template, 'extras.yaml')\n\n    def test_raise_on_bad_json(self):\n        mock_osutils = mock.Mock(spec=OSUtils)\n        mock_osutils.get_file_contents.return_value = (\n            '{'\n            '  \"Resources\": {'\n            '    \"foo\": {'\n            '      \"Properties\": {'\n            '        \"Environment\": {'\n            '          \"Variables\": {\"Name\": \"Foo\"}'\n            ''\n        )\n        mock_merger = mock.Mock(spec=package.TemplateMerger)\n        p = package.TemplateMergePostProcessor(\n            mock_osutils, mock_merger, package.JSONTemplateSerializer(),\n            merge_template='extras.json')\n        template = {}\n\n        config = mock.MagicMock(spec=Config)\n        with pytest.raises(RuntimeError) as e:\n            p.process(\n                template,\n                config=config,\n                outdir='outdir',\n                chalice_stage_name='dev',\n            )\n        assert str(e.value).startswith('Expected')\n        assert 'to be valid JSON template' in str(e.value)\n        assert mock_merger.merge.call_count == 0\n\n    def test_raise_on_bad_yaml(self):\n        mock_osutils = mock.Mock(spec=OSUtils)\n        mock_osutils.get_file_contents.return_value = (\n            '---'\n            'Resources:'\n            '    foo:'\n            '      Properties:'\n            '        Environment:'\n            '          - 123'\n            ''\n        )\n        mock_merger = mock.Mock(spec=package.TemplateMerger)\n        p = package.TemplateMergePostProcessor(\n            mock_osutils, mock_merger, package.YAMLTemplateSerializer(),\n            merge_template='extras.yaml')\n        template = {}\n\n        config = mock.MagicMock(spec=Config)\n        with pytest.raises(RuntimeError) as e:\n            p.process(\n                template,\n                config=config,\n                outdir='outdir',\n                chalice_stage_name='dev',\n            )\n        assert str(e.value).startswith('Expected')\n        assert 'to be valid YAML template' in str(e.value)\n        assert mock_merger.merge.call_count == 0\n\n    def test_raise_if_file_does_not_exist(self):\n        mock_osutils = mock.Mock(spec=OSUtils)\n        mock_osutils.file_exists.return_value = False\n        mock_merger = mock.Mock(spec=package.TemplateMerger)\n        p = package.TemplateMergePostProcessor(\n            mock_osutils, mock_merger, package.JSONTemplateSerializer(),\n            merge_template='extras.json')\n        template = {}\n\n        config = mock.MagicMock(spec=Config)\n        with pytest.raises(RuntimeError) as e:\n            p.process(\n                template,\n                config=config,\n                outdir='outdir',\n                chalice_stage_name='dev',\n            )\n        assert str(e.value).startswith('Cannot find template file:')\n        assert mock_merger.merge.call_count == 0\n\n\nclass TestCompositePostProcessor(object):\n    def test_can_call_no_processors(self):\n        processor = package.CompositePostProcessor([])\n        template = {}\n        config = mock.MagicMock(spec=Config)\n        processor.process(template, config, 'out', 'dev')\n\n        assert template == {}\n\n    def test_does_call_processors_once(self):\n        mock_processor_a = mock.Mock(spec=package.TemplatePostProcessor)\n        mock_processor_b = mock.Mock(spec=package.TemplatePostProcessor)\n        processor = package.CompositePostProcessor(\n            [mock_processor_a, mock_processor_b])\n        template = {}\n        config = mock.MagicMock(spec=Config)\n        processor.process(template, config, 'out', 'dev')\n\n        mock_processor_a.process.assert_called_once_with(\n            template, config, 'out', 'dev')\n        mock_processor_b.process.assert_called_once_with(\n            template, config, 'out', 'dev')\n\n\nclass TemplateTestBase(object):\n\n    template_gen_factory = None\n\n    def setup_method(self, stubbed_session):\n        self.resource_builder = package.ResourceBuilder(\n            application_builder=ApplicationGraphBuilder(),\n            deps_builder=DependencyBuilder(),\n            build_stage=mock.Mock(spec=BuildStage)\n        )\n        client = TypedAWSClient(None)\n        m_client = mock.Mock(wraps=client, spec=TypedAWSClient)\n        type(m_client).region_name = mock.PropertyMock(\n            return_value='us-west-2')\n        self.pkg_options = PackageOptions(m_client)\n        self.template_gen = self.template_gen_factory(\n            Config(), self.pkg_options)\n\n    def generate_template(self, config, chalice_stage_name='dev',\n                          options=None):\n        resources = self.resource_builder.construct_resources(\n            config, chalice_stage_name)\n        if options is None:\n            options = self.pkg_options\n        return self.template_gen_factory(config, options).generate(resources)\n\n    def lambda_function(self):\n        return models.LambdaFunction(\n            resource_name='foo',\n            function_name='app-dev-foo',\n            environment_variables={},\n            runtime='python27',\n            handler='app.app',\n            tags={'foo': 'bar'},\n            timeout=120,\n            xray=None,\n            memory_size=128,\n            deployment_package=models.DeploymentPackage(filename='foo.zip'),\n            role=models.PreCreatedIAMRole(role_arn='role:arn'),\n            security_group_ids=[],\n            subnet_ids=[],\n            layers=[],\n            reserved_concurrency=None,\n        )\n\n    def managed_layer(self):\n        return models.LambdaLayer(\n            resource_name='layer',\n            layer_name='bar',\n            runtime='python2.7',\n            deployment_package=models.DeploymentPackage(filename='layer.zip')\n        )\n\n\nclass TestPackageOptions(object):\n\n    def test_service_principal(self):\n        awsclient = mock.Mock(spec=TypedAWSClient)\n        awsclient.region_name = 'us-east-1'\n        awsclient.endpoint_dns_suffix.return_value = 'amazonaws.com'\n        awsclient.service_principal.return_value = 'lambda.amazonaws.com'\n        options = package.PackageOptions(awsclient)\n        principal = options.service_principal('lambda')\n        assert principal == 'lambda.amazonaws.com'\n\n        awsclient.endpoint_dns_suffix.assert_called_once_with('lambda',\n                                                              'us-east-1')\n        awsclient.service_principal.assert_called_once_with('lambda',\n                                                            'us-east-1',\n                                                            'amazonaws.com')\n\n\nclass TestTerraformTemplate(TemplateTestBase):\n\n    template_gen_factory = package.TerraformGenerator\n\n    EmptyPolicy = {\n        'Version': '2012-10-18',\n        'Statement': {\n            'Sid': '',\n            'Effect': 'Allow',\n            'Action': 'lambda:*'\n        }\n    }\n\n    def generate_template(self, config, chalice_stage_name='dev',\n                          options=None):\n        resources = self.resource_builder.construct_resources(\n            config, chalice_stage_name)\n\n        # Patch up resources that have mocks (due to build stage)\n        # that we need to serialize to json.\n        for r in resources:\n            # For terraform rest api construction, we need a swagger\n            # doc on the api resource as we'll be serializing it to\n            # json.\n            if isinstance(r, models.RestAPI):\n                r.swagger_doc = {\n                    'info': {'title': 'some-app'},\n                    'x-amazon-apigateway-binary-media-types': []\n                }\n            if (isinstance(r, models.RestAPI) and\n                    config.api_gateway_endpoint_type == 'PRIVATE'):\n                r.swagger_doc['x-amazon-apigateway-policy'] = (\n                    r.policy.document)\n\n            # Same for iam policies on roles\n            elif isinstance(r, models.FileBasedIAMPolicy):\n                r.document = self.EmptyPolicy\n\n        if options is None:\n            options = self.pkg_options\n        return self.template_gen_factory(config, options).generate(resources)\n\n    def get_function(self, template):\n        functions = list(template['resource'][\n            'aws_lambda_function'].values())\n        assert len(functions) == 1\n        return functions[0]\n\n    def test_supports_precreated_role(self):\n        builder = DependencyBuilder()\n        resources = builder.build_dependencies(\n            models.Application(\n                stage='dev',\n                resources=[self.lambda_function()],\n            )\n        )\n        template = self.template_gen.generate(resources)\n        assert template['resource'][\n            'aws_lambda_function']['foo']['role'] == 'role:arn'\n\n    def test_adds_env_vars_when_provided(self, sample_app):\n        function = self.lambda_function()\n        function.environment_variables = {'foo': 'bar'}\n        template = self.template_gen.generate([function])\n        tf_resource = self.get_function(template)\n        assert tf_resource['environment'] == {\n            'variables': {\n                'foo': 'bar'\n            }\n        }\n\n    def test_adds_vpc_config_when_provided(self):\n        function = self.lambda_function()\n        function.security_group_ids = ['sg1', 'sg2']\n        function.subnet_ids = ['sn1', 'sn2']\n        template = self.template_gen.generate([function])\n        tf_resource = self.get_function(template)\n        assert tf_resource['vpc_config'] == {\n            'subnet_ids': ['sn1', 'sn2'],\n            'security_group_ids': ['sg1', 'sg2']}\n\n    def test_adds_layers_when_provided(self):\n        function = self.lambda_function()\n        function.layers = layers = ['arn://layer1', 'arn://layer2']\n        template = self.template_gen.generate([function])\n        tf_resource = self.get_function(template)\n        assert tf_resource['layers'] == layers\n\n    def test_adds_managed_layer_when_provided(self):\n        function = self.lambda_function()\n        function.layers = ['arn://layer1', 'arn://layer2']\n        function.managed_layer = self.managed_layer()\n        template = self.template_gen.generate(\n            [function.managed_layer, function])\n        tf_resource = self.get_function(template)\n        assert tf_resource['layers'] == [\n            '${aws_lambda_layer_version.layer.arn}',\n            'arn://layer1',\n            'arn://layer2',\n        ]\n        assert template['resource']['aws_lambda_layer_version']['layer'] == {\n            'layer_name': 'bar',\n            'compatible_runtimes': ['python2.7'],\n            'filename': 'layer.zip',\n        }\n\n    def test_adds_reserved_concurrency_when_provided(self, sample_app):\n        function = self.lambda_function()\n        function.reserved_concurrency = 5\n        template = self.template_gen.generate([function])\n        tf_resource = self.get_function(template)\n        assert tf_resource['reserved_concurrent_executions'] == 5\n\n    def test_adds_log_group_resource_when_configured(self, sample_app):\n        function = self.lambda_function()\n        name = function.resource_name + '-log-group'\n        function.log_group = models.LogGroup(\n            resource_name=name,\n            log_group_name='/aws/lambda/%s' % function.function_name,\n            retention_in_days=7)\n        template = self.template_gen.generate([function])\n        log_resource = template['resource']['aws_cloudwatch_log_group'][name]\n        assert log_resource == {\n            'name': name,\n            'retention_in_days': 7,\n        }\n\n    def test_can_add_tracing_config(self, sample_app):\n        function = self.lambda_function()\n        function.xray = True\n        template = self.template_gen.generate([function])\n        tf_resource = self.get_function(template)\n        assert tf_resource['tracing_config']['mode'] == 'Active'\n\n    def test_can_generate_cloudwatch_event(self):\n        function = self.lambda_function()\n        event = models.CloudWatchEvent(\n            resource_name='foo-event',\n            rule_name='myrule',\n            event_pattern='{\"source\": [\"aws.ec2\"]}',\n            lambda_function=function,\n        )\n        template = self.template_gen.generate(\n            [function, event]\n        )\n        rule = template['resource'][\n            'aws_cloudwatch_event_rule'][event.resource_name]\n        assert rule == {\n            'name': event.resource_name,\n            'event_pattern': event.event_pattern}\n        target = template['resource'][\n            'aws_cloudwatch_event_target'][event.resource_name]\n        assert target == {\n            'target_id': 'foo-event',\n            'rule': '${aws_cloudwatch_event_rule.foo-event.name}',\n            'arn': '${aws_lambda_function.foo.arn}',\n        }\n\n    def test_can_generate_scheduled_event(self):\n        function = self.lambda_function()\n        event = models.ScheduledEvent(\n            resource_name='foo-event',\n            rule_name='myrule',\n            schedule_expression='rate(5 minutes)',\n            lambda_function=function,\n            rule_description='description',\n        )\n        template = self.template_gen.generate(\n            [function, event]\n        )\n        rule = template['resource'][\n            'aws_cloudwatch_event_rule'][event.resource_name]\n\n        assert rule == {\n            'name': event.resource_name,\n            'schedule_expression': 'rate(5 minutes)',\n            'description': 'description',\n        }\n\n    def test_can_generate_rest_api(self, sample_app_with_auth):\n        config = Config.create(chalice_app=sample_app_with_auth,\n                               project_dir='.',\n                               minimum_compression_size=8192,\n                               api_gateway_endpoint_type='PRIVATE',\n                               api_gateway_endpoint_vpce='vpce-abc123',\n                               app_name='sample_app',\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n        resources = template['resource']\n        # Lambda function should be created.\n        assert resources['aws_lambda_function']\n        # Along with permission to invoke from API Gateway.\n        assert list(resources['aws_lambda_permission'].values())[0] == {\n            'function_name': '${aws_lambda_function.api_handler.arn}',\n            'action': 'lambda:InvokeFunction',\n            'principal': 'apigateway.amazonaws.com',\n            'source_arn': (\n                '${aws_api_gateway_rest_api.rest_api.execution_arn}/*')\n        }\n        assert 'aws_api_gateway_rest_api' in resources\n        assert 'rest_api' in resources['aws_api_gateway_rest_api']\n        resource_policy = resources[\n            'aws_api_gateway_rest_api']['rest_api']['policy']\n        assert json.loads(resource_policy) == {\n            'Version': '2012-10-17',\n            'Statement': [\n                {\n                    'Action': 'execute-api:Invoke',\n                    'Resource': 'arn:*:execute-api:*:*:*',\n                    'Effect': 'Allow',\n                    'Condition': {\n                        'StringEquals': {\n                            'aws:SourceVpce': 'vpce-abc123'\n                        }\n                    },\n                    'Principal': '*'\n                }\n            ]\n        }\n        assert resources['aws_api_gateway_rest_api'][\n            'rest_api']['minimum_compression_size'] == 8192\n        assert resources['aws_api_gateway_rest_api'][\n            'rest_api']['endpoint_configuration'] == {'types': ['PRIVATE']}\n\n        assert 'aws_api_gateway_stage' not in resources\n        assert resources['aws_api_gateway_deployment']['rest_api'] == {\n            'rest_api_id': '${aws_api_gateway_rest_api.rest_api.id}',\n            'stage_description': (\n                '${md5(local.chalice_api_swagger)}'),\n            'stage_name': 'api',\n            'lifecycle': {'create_before_destroy': True}\n        }\n\n        # We should also create the auth lambda function.\n        assert 'myauth' in resources['aws_lambda_function']\n\n        # Along with permission to invoke from API Gateway.\n        assert resources['aws_lambda_permission']['myauth_invoke'] == {\n            'action': 'lambda:InvokeFunction',\n            'function_name': '${aws_lambda_function.myauth.arn}',\n            'principal': 'apigateway.amazonaws.com',\n            'source_arn': (\n                '${aws_api_gateway_rest_api.rest_api.execution_arn}/*')\n        }\n\n        # Also verify we add the expected outputs when using\n        # a Rest API.\n        assert template['output'] == {\n            'EndpointURL': {\n                'value': '${aws_api_gateway_deployment.rest_api.invoke_url}'},\n            'RestAPIId': {\n                'value': '${aws_api_gateway_rest_api.rest_api.id}'}\n        }\n\n    def test_can_package_s3_event_handler_with_tf_ref(self, sample_app):\n        @sample_app.on_s3_event(\n            bucket='${aws_s3_bucket.my_data_bucket.id}')\n        def handler(event):\n            pass\n\n        config = Config.create(chalice_app=sample_app,\n                               project_dir='.',\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n        assert template['resource']['aws_s3_bucket_notification'][\n                   'my_data_bucket_notify'] == {\n                   'bucket': '${aws_s3_bucket.my_data_bucket.id}',\n                   'lambda_function': [{\n                       'events': ['s3:ObjectCreated:*'],\n                       'lambda_function_arn': (\n                           '${aws_lambda_function.handler.arn}')\n                   }]\n               }\n\n    def test_can_generate_chalice_terraform_static_data(self, sample_app):\n        config = Config.create(chalice_app=sample_app,\n                               project_dir='.',\n                               app_name='myfoo',\n                               api_gateway_stage='dev')\n        template = self.generate_template(config)\n        assert template['data']['null_data_source']['chalice']['inputs'] == {\n            'app': 'myfoo',\n            'stage': 'dev'\n        }\n\n    def test_can_package_s3_event_handler_sans_filters(self, sample_app):\n        @sample_app.on_s3_event(bucket='foo')\n        def handler(event):\n            pass\n\n        config = Config.create(chalice_app=sample_app,\n                               project_dir='.',\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n        assert template['resource']['aws_s3_bucket_notification'][\n                   'foo_notify'] == {\n                   'bucket': 'foo',\n                   'lambda_function': [{\n                       'events': ['s3:ObjectCreated:*'],\n                       'lambda_function_arn': (\n                           '${aws_lambda_function.handler.arn}')\n                   }]\n               }\n\n    def test_can_package_s3_event_handler(self, sample_app):\n        @sample_app.on_s3_event(\n            bucket='foo', prefix='incoming', suffix='.csv')\n        def handler(event):\n            pass\n\n        config = Config.create(chalice_app=sample_app,\n                               project_dir='.',\n                               app_name='sample_app',\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n        assert template['resource']['aws_lambda_permission'][\n                   'handler-s3event'] == {\n                   'action': 'lambda:InvokeFunction',\n                   'function_name': '${aws_lambda_function.handler.arn}',\n                   'principal': 's3.amazonaws.com',\n                   'source_account': (\n                       '${data.aws_caller_identity.chalice.account_id}'),\n                   'source_arn': (\n                       'arn:${data.aws_partition.chalice.partition}:s3:::foo'),\n                   'statement_id': 'handler-s3event'\n               }\n\n        assert template['resource']['aws_s3_bucket_notification'][\n                   'foo_notify'] == {\n                   'bucket': 'foo',\n                   'lambda_function': [{\n                       'events': ['s3:ObjectCreated:*'],\n                       'filter_prefix': 'incoming',\n                       'filter_suffix': '.csv',\n                       'lambda_function_arn': (\n                           '${aws_lambda_function.handler.arn}')\n                   }]\n               }\n\n    def test_can_package_sns_handler(self, sample_app):\n        @sample_app.on_sns_message(topic='foo')\n        def handler(event):\n            pass\n\n        config = Config.create(chalice_app=sample_app,\n                               project_dir='.',\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n\n        assert template['resource']['aws_sns_topic_subscription'][\n                   'handler-sns-subscription'] == {\n                   'topic_arn': (\n                       'arn:${data.aws_partition.chalice.partition}:sns'\n                       ':${data.aws_region.chalice.name}:'\n                       '${data.aws_caller_identity.chalice.account_id}:foo'),\n                   'protocol': 'lambda',\n                   'endpoint': '${aws_lambda_function.handler.arn}'\n               }\n\n    def test_can_package_sns_arn_handler(self, sample_app):\n        arn = 'arn:aws:sns:space-leo-1:1234567890:foo'\n\n        @sample_app.on_sns_message(topic=arn)\n        def handler(event):\n            pass\n\n        config = Config.create(chalice_app=sample_app,\n                               project_dir='.',\n                               app_name='sample_app',\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n\n        assert template['resource']['aws_sns_topic_subscription'][\n                   'handler-sns-subscription'] == {\n                   'topic_arn': arn,\n                   'protocol': 'lambda',\n                   'endpoint': '${aws_lambda_function.handler.arn}'\n               }\n\n        assert template['resource']['aws_lambda_permission'][\n                   'handler-sns-subscription'] == {\n                   'function_name': '${aws_lambda_function.handler.arn}',\n                   'action': 'lambda:InvokeFunction',\n                   'principal': 'sns.amazonaws.com',\n                   'source_arn': 'arn:aws:sns:space-leo-1:1234567890:foo'\n               }\n\n    def test_can_package_sqs_handler(self, sample_app):\n        @sample_app.on_sqs_message(queue='foo', batch_size=5)\n        def handler(event):\n            pass\n\n        config = Config.create(chalice_app=sample_app,\n                               project_dir='.',\n                               app_name='sample_app',\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n\n        assert template['resource'][\n                   'aws_lambda_event_source_mapping'][\n                   'handler-sqs-event-source'] == {\n                   'event_source_arn': (\n                       'arn:${data.aws_partition.chalice.partition}:sqs'\n                       ':${data.aws_region.chalice.name}:'\n                       '${data.aws_caller_identity.chalice.account_id}:foo'),\n                   'function_name': '${aws_lambda_function.handler.arn}',\n                   'batch_size': 5,\n                   'maximum_batching_window_in_seconds': 0\n        }\n\n    def test_can_package_sqs_handler_with_max_concurrency(self, sample_app):\n        @sample_app.on_sqs_message(\n            queue='foo',\n            batch_size=5,\n            maximum_concurrency=2\n        )\n        def handler(event):\n            pass\n\n        config = Config.create(chalice_app=sample_app,\n                               project_dir='.',\n                               app_name='sample_app',\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n\n        assert template['resource'][\n                   'aws_lambda_event_source_mapping'][\n                   'handler-sqs-event-source'] == {\n                   'event_source_arn': (\n                       'arn:${data.aws_partition.chalice.partition}:sqs'\n                       ':${data.aws_region.chalice.name}:'\n                       '${data.aws_caller_identity.chalice.account_id}:foo'),\n                   'function_name': '${aws_lambda_function.handler.arn}',\n                   'batch_size': 5,\n                   'maximum_batching_window_in_seconds': 0,\n                   'scaling_config': {'maximum_concurrency': 2}\n        }\n\n    def test_sqs_arn_does_not_use_fn_sub(self, sample_app):\n        @sample_app.on_sqs_message(queue_arn='arn:foo:bar', batch_size=5)\n        def handler(event):\n            pass\n\n        config = Config.create(chalice_app=sample_app,\n                               project_dir='.',\n                               app_name='sample_app',\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n\n        assert template['resource'][\n                   'aws_lambda_event_source_mapping'][\n                   'handler-sqs-event-source'] == {\n                   'event_source_arn': 'arn:foo:bar',\n                   'function_name': '${aws_lambda_function.handler.arn}',\n                   'batch_size': 5,\n                   'maximum_batching_window_in_seconds': 0\n               }\n\n    def test_can_package_kinesis_handler(self, sample_app):\n        @sample_app.on_kinesis_record(stream='mystream', batch_size=5,\n                                      starting_position='TRIM_HORIZON')\n        def handler(event):\n            pass\n\n        config = Config.create(chalice_app=sample_app,\n                               project_dir='.',\n                               app_name='sample_app',\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n\n        assert template['resource'][\n                   'aws_lambda_event_source_mapping'][\n                   'handler-kinesis-event-source'] == {\n                   'event_source_arn': (\n                       'arn:${data.aws_partition.chalice.partition}:kinesis'\n                       ':${data.aws_region.chalice.name}:'\n                       '${data.aws_caller_identity.chalice.account_id}'\n                       ':stream/mystream'),\n                   'function_name': '${aws_lambda_function.handler.arn}',\n                   'starting_position': 'TRIM_HORIZON',\n                   'batch_size': 5,\n                   'maximum_batching_window_in_seconds': 0\n               }\n\n    def test_can_package_dynamodb_handler(self, sample_app):\n        @sample_app.on_dynamodb_record(stream_arn='arn:aws:...:stream',\n                                       batch_size=5,\n                                       starting_position='TRIM_HORIZON')\n        def handler(event):\n            pass\n\n        config = Config.create(chalice_app=sample_app,\n                               project_dir='.',\n                               app_name='sample_app',\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n\n        assert template['resource'][\n                   'aws_lambda_event_source_mapping'][\n                   'handler-dynamodb-event-source'] == {\n                       'event_source_arn': 'arn:aws:...:stream',\n                       'function_name': '${aws_lambda_function.handler.arn}',\n                       'starting_position': 'TRIM_HORIZON',\n                       'batch_size': 5,\n                       'maximum_batching_window_in_seconds': 0\n                   }\n\n    def test_can_generate_websockets_api(self, sample_websocket_app):\n        config = Config.create(chalice_app=sample_websocket_app,\n                               project_dir='.',\n                               app_name='sample_app',\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n\n        assert template['output'] == {\n            'WebsocketAPIId': {\n                'value': '${aws_apigatewayv2_api.websocket_api.id}'\n            },\n            'WebsocketConnectHandlerArn': {\n                'value': '${aws_lambda_function.websocket_connect.arn}'\n            },\n            'WebsocketConnectHandlerName': {\n                'value': (\n                    '${aws_lambda_function.websocket_connect.function_name}')\n            },\n            'WebsocketMessageHandlerArn': {\n                'value': '${aws_lambda_function.websocket_message.arn}'\n            },\n            'WebsocketMessageHandlerName': {\n                'value': (\n                    '${aws_lambda_function.websocket_message.function_name}')\n            },\n            'WebsocketDisconnectHandlerArn': {\n                'value': '${aws_lambda_function.websocket_disconnect.arn}'\n            },\n            'WebsocketDisconnectHandlerName': {\n                'value': (\n                    '${aws_lambda_function.websocket_disconnect'\n                    '.function_name}')\n            },\n            'WebsocketConnectEndpointURL': {\n                'value': 'wss://${aws_apigatewayv2_api.websocket_api.id}'\n                         '.execute-api.${data.aws_region.chalice.name}'\n                         '.amazonaws.com/api/'\n            }\n        }\n\n        assert template['resource']['aws_apigatewayv2_api'] == {\n            'websocket_api': {\n                'name': 'sample_app-dev-websocket-api',\n                'route_selection_expression': '$request.body.action',\n                'protocol_type': 'WEBSOCKET'\n            }\n        }\n\n        assert template['resource']['aws_apigatewayv2_integration'] == {\n            'websocket_connect_api_integration': {\n                'api_id': '${aws_apigatewayv2_api.websocket_api.id}',\n                'connection_type': 'INTERNET',\n                'content_handling_strategy': 'CONVERT_TO_TEXT',\n                'integration_type': 'AWS_PROXY',\n                'integration_uri': 'arn:${data.aws_partition.chalice'\n                                   '.partition}:apigateway:'\n                                   '${data.aws_region.chalice.name}'\n                                   ':lambda:path/2015-03-31/functions/arn'\n                                   ':${data.aws_partition.chalice.partition}'\n                                   ':lambda:${data.aws_region.chalice.name}'\n                                   ':${data.aws_caller_identity'\n                                   '.chalice.account_id}:function'\n                                   ':${aws_lambda_function.websocket_connect'\n                                   '.function_name}/invocations'\n            },\n            'websocket_message_api_integration': {\n                'api_id': '${aws_apigatewayv2_api.websocket_api.id}',\n                'connection_type': 'INTERNET',\n                'content_handling_strategy': 'CONVERT_TO_TEXT',\n                'integration_type': 'AWS_PROXY',\n                'integration_uri': 'arn:${data.aws_partition.chalice'\n                                   '.partition}:apigateway'\n                                   ':${data.aws_region.chalice.name}'\n                                   ':lambda:path/2015-03-31/functions/arn'\n                                   ':${data.aws_partition.chalice.partition}'\n                                   ':lambda:${data.aws_region.chalice.name}'\n                                   ':${data.aws_caller_identity.chalice'\n                                   '.account_id}:function'\n                                   ':${aws_lambda_function.websocket_message'\n                                   '.function_name}/invocations'\n            },\n            'websocket_disconnect_api_integration': {\n                'api_id': '${aws_apigatewayv2_api.websocket_api.id}',\n                'connection_type': 'INTERNET',\n                'content_handling_strategy': 'CONVERT_TO_TEXT',\n                'integration_type': 'AWS_PROXY',\n                'integration_uri': 'arn:${data.aws_partition'\n                                   '.chalice.partition}:apigateway'\n                                   ':${data.aws_region.chalice.name}'\n                                   ':lambda:path/2015-03-31/functions/arn'\n                                   ':${data.aws_partition.chalice.partition}'\n                                   ':lambda:${data.aws_region.chalice.name}'\n                                   ':${data.aws_caller_identity'\n                                   '.chalice.account_id}:function'\n                                   ':${aws_lambda_function'\n                                   '.websocket_disconnect.function_name}'\n                                   '/invocations'\n            }\n        }\n\n        assert template['resource']['aws_lambda_permission'] == {\n            'websocket_connect_invoke_permission': {\n                'function_name': '${aws_lambda_function.websocket_connect'\n                                 '.function_name}',\n                'action': 'lambda:InvokeFunction',\n                'principal': 'apigateway.amazonaws.com',\n                'source_arn': 'arn:${data.aws_partition.chalice.partition}'\n                              ':execute-api:${data.aws_region.chalice.name}'\n                              ':${data.aws_caller_identity.chalice.account_id}'\n                              ':${aws_apigatewayv2_api.websocket_api.id}/*'\n            },\n            'websocket_message_invoke_permission': {\n                'function_name': '${aws_lambda_function.websocket_message'\n                                 '.function_name}',\n                'action': 'lambda:InvokeFunction',\n                'principal': 'apigateway.amazonaws.com',\n                'source_arn': 'arn:${data.aws_partition.chalice.partition}'\n                              ':execute-api:${data.aws_region.chalice.name}'\n                              ':${data.aws_caller_identity.chalice.account_id}'\n                              ':${aws_apigatewayv2_api.websocket_api.id}/*'\n            },\n            'websocket_disconnect_invoke_permission': {\n                'function_name': '${aws_lambda_function.websocket_disconnect'\n                                 '.function_name}',\n                'action': 'lambda:InvokeFunction',\n                'principal': 'apigateway.amazonaws.com',\n                'source_arn': 'arn:${data.aws_partition.chalice.partition}'\n                              ':execute-api:${data.aws_region.chalice.name}'\n                              ':${data.aws_caller_identity.chalice.account_id}'\n                              ':${aws_apigatewayv2_api.websocket_api.id}/*'\n            }\n        }\n\n        assert template['resource']['aws_apigatewayv2_route'] == {\n            'websocket_connect_route': {\n                'api_id': '${aws_apigatewayv2_api.websocket_api.id}',\n                'route_key': '$connect',\n                'target': 'integrations/${aws_apigatewayv2_integration'\n                          '.websocket_connect_api_integration.id}'\n            },\n            'websocket_message_route': {\n                'api_id': '${aws_apigatewayv2_api.websocket_api.id}',\n                'route_key': '$default',\n                'target': 'integrations/${aws_apigatewayv2_integration'\n                          '.websocket_message_api_integration.id}'\n            },\n            'websocket_disconnect_route': {\n                'api_id': '${aws_apigatewayv2_api.websocket_api.id}',\n                'route_key': '$disconnect',\n                'target': 'integrations/${aws_apigatewayv2_integration'\n                          '.websocket_disconnect_api_integration.id}'\n            }\n        }\n\n        assert template['resource']['aws_apigatewayv2_deployment'] == {\n            'websocket_api_deployment': {\n                'api_id': '${aws_apigatewayv2_api.websocket_api.id}',\n                'depends_on': [\n                    'aws_apigatewayv2_route.websocket_connect_route',\n                    'aws_apigatewayv2_route.websocket_message_route',\n                    'aws_apigatewayv2_route.websocket_disconnect_route'\n                ]}}\n\n        assert template['resource']['aws_apigatewayv2_stage'] == {\n            'websocket_api_stage': {\n                'api_id': '${aws_apigatewayv2_api.websocket_api.id}',\n                'deployment_id': '${aws_apigatewayv2_deployment'\n                                 '.websocket_api_deployment.id}',\n                'name': 'api'\n            }\n        }\n\n    def test_can_generate_custom_domain_name(self, sample_app):\n        config = Config.create(\n            chalice_app=sample_app,\n            project_dir='.',\n            api_gateway_stage='api',\n            api_gateway_endpoint_type='EDGE',\n            api_gateway_custom_domain={\n                \"certificate_arn\": \"my_cert_arn\",\n                \"domain_name\": \"example.com\",\n                \"tls_version\": \"TLS_1_2\",\n                \"tags\": {\"foo\": \"bar\"},\n            }\n        )\n        template = self.generate_template(config)\n        assert template['resource']['aws_api_gateway_domain_name'][\n            'api_gateway_custom_domain'] == {\n                'domain_name': 'example.com',\n                'certificate_arn': 'my_cert_arn',\n                'security_policy': 'TLS_1_2',\n                'endpoint_configuration': {'types': ['EDGE']},\n                'tags': {'foo': 'bar'},\n        }\n        assert template['resource']['aws_api_gateway_base_path_mapping'][\n            'api_gateway_custom_domain_mapping'] == {\n                'api_id': '${aws_api_gateway_rest_api.rest_api.id}',\n                'stage_name': 'api',\n                'domain_name': 'example.com',\n        }\n        outputs = template['output']\n        assert outputs['AliasDomainName']['value'] == (\n            '${aws_api_gateway_domain_name.api_gateway_custom_domain'\n            '.cloudfront_domain_name}'\n        )\n        assert outputs['HostedZoneId']['value'] == (\n            '${aws_api_gateway_domain_name.api_gateway_custom_domain'\n            '.cloudfront_zone_id}'\n        )\n\n    def test_can_generate_domain_for_regional_endpoint(self, sample_app):\n        config = Config.create(\n            chalice_app=sample_app,\n            project_dir='.',\n            api_gateway_stage='api',\n            api_gateway_endpoint_type='REGIONAL',\n            api_gateway_custom_domain={\n                \"certificate_arn\": \"my_cert_arn\",\n                \"domain_name\": \"example.com\",\n            }\n        )\n        template = self.generate_template(config)\n        assert template['resource']['aws_api_gateway_domain_name'][\n            'api_gateway_custom_domain'] == {\n                'domain_name': 'example.com',\n                'regional_certificate_arn': 'my_cert_arn',\n                'endpoint_configuration': {'types': ['REGIONAL']},\n        }\n        assert template['resource']['aws_api_gateway_base_path_mapping'][\n            'api_gateway_custom_domain_mapping'] == {\n                'api_id': '${aws_api_gateway_rest_api.rest_api.id}',\n                'stage_name': 'api',\n                'domain_name': 'example.com',\n        }\n        outputs = template['output']\n        assert outputs['AliasDomainName']['value'] == (\n            '${aws_api_gateway_domain_name.api_gateway_custom_domain'\n            '.regional_domain_name}'\n        )\n        assert outputs['HostedZoneId']['value'] == (\n            '${aws_api_gateway_domain_name.api_gateway_custom_domain'\n            '.regional_zone_id}'\n        )\n\n\nclass TestSAMTemplate(TemplateTestBase):\n\n    template_gen_factory = package.SAMTemplateGenerator\n\n    def test_sam_generates_sam_template_basic(self, sample_app):\n        config = Config.create(chalice_app=sample_app,\n                               project_dir='.',\n                               automatic_layer=True,\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n        # Verify the basic structure is in place.  The specific parts\n        # are validated in other tests.\n        assert template['AWSTemplateFormatVersion'] == '2010-09-09'\n        assert template['Transform'] == 'AWS::Serverless-2016-10-31'\n        assert 'Outputs' in template\n        assert 'Resources' in template\n        assert list(sorted(template['Resources'])) == [\n            'APIHandler',\n            'APIHandlerInvokePermission',\n            # This casing on the ApiHandlerRole name is unfortunate, but the 3\n            # other resources in this list are hardcoded from the old deployer.\n            'ApiHandlerRole',\n            'ManagedLayer',\n            'RestAPI'\n        ]\n\n    def test_can_generate_lambda_layer_if_configured(self, sample_app):\n        config = Config.create(chalice_app=sample_app,\n                               app_name='testapp',\n                               project_dir='.',\n                               automatic_layer=True,\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n        managed_layer = template['Resources']['ManagedLayer']\n        assert managed_layer == {\n            'Type': 'AWS::Serverless::LayerVersion',\n            'Properties': {\n                'CompatibleRuntimes': [config.lambda_python_version],\n                'LayerName': 'testapp-dev-managed-layer',\n                'ContentUri': models.Placeholder.BUILD_STAGE,\n            }\n        }\n        assert template['Resources']['APIHandler']['Properties']['Layers'] == [\n            {'Ref': 'ManagedLayer'}\n        ]\n\n    def test_adds_single_layer_for_multiple_lambdas(self, sample_app):\n        config = Config.create(chalice_app=sample_app,\n                               app_name='testapp',\n                               project_dir='.',\n                               automatic_layer=True,\n                               layers=['arn:aws:mylayer'],\n                               api_gateway_stage='api')\n\n        @sample_app.lambda_function()\n        def first(event, context):\n            pass\n\n        @sample_app.lambda_function()\n        def second(event, context):\n            pass\n\n        template = self.generate_template(config)\n        managed_layer = template['Resources']['ManagedLayer']\n        assert managed_layer == {\n            'Type': 'AWS::Serverless::LayerVersion',\n            'Properties': {\n                'CompatibleRuntimes': [config.lambda_python_version],\n                'LayerName': 'testapp-dev-managed-layer',\n                'ContentUri': models.Placeholder.BUILD_STAGE,\n            }\n        }\n        assert template['Resources']['First']['Properties']['Layers'] == [\n            {'Ref': 'ManagedLayer'},\n            'arn:aws:mylayer',\n        ]\n\n    def test_supports_precreated_role(self):\n        builder = DependencyBuilder()\n        resources = builder.build_dependencies(\n            models.Application(\n                stage='dev',\n                resources=[self.lambda_function()],\n            )\n        )\n        template = self.template_gen.generate(resources)\n        assert template['Resources']['Foo']['Properties']['Role'] == 'role:arn'\n\n    def test_sam_injects_policy(self, sample_app):\n        function = models.LambdaFunction(\n            resource_name='foo',\n            function_name='app-dev-foo',\n            environment_variables={},\n            runtime='python27',\n            handler='app.app',\n            tags={'foo': 'bar'},\n            timeout=120,\n            memory_size=128,\n            xray=None,\n            deployment_package=models.DeploymentPackage(filename='foo.zip'),\n            role=models.ManagedIAMRole(\n                resource_name='role',\n                role_name='app-role',\n                trust_policy={},\n                policy=models.AutoGenIAMPolicy(document={'iam': 'policy'}),\n            ),\n            security_group_ids=[],\n            subnet_ids=[],\n            layers=[],\n            reserved_concurrency=None,\n        )\n        template = self.template_gen.generate([function])\n        cfn_resource = list(template['Resources'].values())[0]\n        assert cfn_resource == {\n            'Type': 'AWS::Serverless::Function',\n            'Properties': {\n                'CodeUri': 'foo.zip',\n                'Handler': 'app.app',\n                'MemorySize': 128,\n                'Tracing': 'PassThrough',\n                'Role': {'Fn::GetAtt': ['Role', 'Arn']},\n                'Runtime': 'python27',\n                'Tags': {'foo': 'bar'},\n                'Timeout': 120\n            },\n        }\n\n    def test_adds_env_vars_when_provided(self, sample_app):\n        function = self.lambda_function()\n        function.environment_variables = {'foo': 'bar'}\n        template = self.template_gen.generate([function])\n        cfn_resource = list(template['Resources'].values())[0]\n        assert cfn_resource['Properties']['Environment'] == {\n            'Variables': {\n                'foo': 'bar'\n            }\n        }\n\n    def test_adds_vpc_config_when_provided(self):\n        function = self.lambda_function()\n        function.security_group_ids = ['sg1', 'sg2']\n        function.subnet_ids = ['sn1', 'sn2']\n        template = self.template_gen.generate([function])\n        cfn_resource = list(template['Resources'].values())[0]\n        assert cfn_resource['Properties']['VpcConfig'] == {\n            'SecurityGroupIds': ['sg1', 'sg2'],\n            'SubnetIds': ['sn1', 'sn2'],\n        }\n\n    def test_adds_reserved_concurrency_when_provided(self, sample_app):\n        function = self.lambda_function()\n        function.reserved_concurrency = 5\n        template = self.template_gen.generate([function])\n        cfn_resource = list(template['Resources'].values())[0]\n        assert cfn_resource['Properties']['ReservedConcurrentExecutions'] == 5\n\n    def test_adds_log_group_resource_when_configured(self, sample_app):\n        function = self.lambda_function()\n        function.log_group = models.LogGroup(\n            resource_name=function.resource_name + '-log-group',\n            log_group_name='/aws/lambda/%s' % function.function_name,\n            retention_in_days=7)\n        template = self.template_gen.generate([function])\n        log_resource = template['Resources']['FooLogGroup']\n        assert log_resource == {\n            'Type': 'AWS::Logs::LogGroup',\n            'Properties': {\n                'LogGroupName': {'Fn::Sub': '/aws/lambda/${Foo}'},\n                'RetentionInDays': 7\n            }\n        }\n\n    def test_adds_layers_when_provided(self, sample_app):\n        function = self.lambda_function()\n        function.layers = ['arn:aws:layer1', 'arn:aws:layer2']\n        template = self.template_gen.generate([function])\n        cfn_resource = list(template['Resources'].values())[0]\n        assert cfn_resource['Properties']['Layers'] == [\n            'arn:aws:layer1',\n            'arn:aws:layer2'\n            ]\n\n    def test_duplicate_resource_name_raises_error(self):\n        one = self.lambda_function()\n        two = self.lambda_function()\n        one.resource_name = 'foo_bar'\n        two.resource_name = 'foo__bar'\n        with pytest.raises(package.DuplicateResourceNameError):\n            self.template_gen.generate([one, two])\n\n    def test_role_arn_inserted_when_necessary(self):\n        function = models.LambdaFunction(\n            resource_name='foo',\n            function_name='app-dev-foo',\n            environment_variables={},\n            runtime='python27',\n            handler='app.app',\n            tags={'foo': 'bar'},\n            timeout=120,\n            memory_size=128,\n            xray=None,\n            deployment_package=models.DeploymentPackage(filename='foo.zip'),\n            role=models.PreCreatedIAMRole(role_arn='role:arn'),\n            security_group_ids=[],\n            subnet_ids=[],\n            layers=[],\n            reserved_concurrency=None,\n        )\n        template = self.template_gen.generate([function])\n        cfn_resource = list(template['Resources'].values())[0]\n        assert cfn_resource == {\n            'Type': 'AWS::Serverless::Function',\n            'Properties': {\n                'CodeUri': 'foo.zip',\n                'Handler': 'app.app',\n                'MemorySize': 128,\n                'Role': 'role:arn',\n                'Tracing': 'PassThrough',\n                'Runtime': 'python27',\n                'Tags': {'foo': 'bar'},\n                'Timeout': 120\n            },\n        }\n\n    def test_can_generate_cloudwatch_event(self):\n        function = self.lambda_function()\n        event = models.CloudWatchEvent(\n            resource_name='foo-event',\n            rule_name='myrule',\n            event_pattern='{\"source\": [\"aws.ec2\"]}',\n            lambda_function=function,\n        )\n        template = self.template_gen.generate(\n            [function, event]\n        )\n        resources = template['Resources']\n        assert len(resources) == 1\n        cfn_resource = list(resources.values())[0]\n        assert cfn_resource['Properties']['Events'] == {\n            'FooEvent': {\n                'Type': 'CloudWatchEvent',\n                'Properties': {\n                    'Pattern': {\n                        'source': [\n                            'aws.ec2'\n                        ]\n                    }\n                },\n            },\n        }\n\n    def test_can_generate_scheduled_event(self):\n        function = self.lambda_function()\n        event = models.ScheduledEvent(\n            resource_name='foo-event',\n            rule_name='myrule',\n            rule_description=\"my rule description\",\n            schedule_expression='rate(5 minutes)',\n            lambda_function=function,\n        )\n        template = self.template_gen.generate(\n            [function, event]\n        )\n        resources = template['Resources']\n        assert len(resources) == 1\n        cfn_resource = list(resources.values())[0]\n        assert cfn_resource['Properties']['Events'] == {\n            'FooEvent': {\n                'Type': 'Schedule',\n                'Properties': {\n                    'Schedule': 'rate(5 minutes)'\n                },\n            },\n        }\n\n    def test_can_generate_rest_api_without_compression(\n            self, sample_app_with_auth):\n        config = Config.create(chalice_app=sample_app_with_auth,\n                               project_dir='.',\n                               api_gateway_stage='api',\n                               )\n        template = self.generate_template(config)\n        resources = template['Resources']\n        assert 'MinimumCompressionSize' not in \\\n            resources['RestAPI']['Properties']\n\n    def test_can_generate_rest_api(self, sample_app_with_auth):\n        config = Config.create(chalice_app=sample_app_with_auth,\n                               project_dir='.',\n                               api_gateway_stage='api',\n                               minimum_compression_size=100,\n                               )\n        template = self.generate_template(config)\n        resources = template['Resources']\n        # Lambda function should be created.\n        assert resources['APIHandler']['Type'] == 'AWS::Serverless::Function'\n        # Along with permission to invoke from API Gateway.\n        assert resources['APIHandlerInvokePermission'] == {\n            'Type': 'AWS::Lambda::Permission',\n            'Properties': {\n                'Action': 'lambda:InvokeFunction',\n                'FunctionName': {'Ref': 'APIHandler'},\n                'Principal': 'apigateway.amazonaws.com',\n                'SourceArn': {\n                    'Fn::Sub': [\n                        ('arn:${AWS::Partition}:execute-api:${AWS::Region}'\n                         ':${AWS::AccountId}:${RestAPIId}/*'),\n                        {'RestAPIId': {'Ref': 'RestAPI'}}]}},\n        }\n        assert resources['RestAPI']['Type'] == 'AWS::Serverless::Api'\n        assert resources['RestAPI']['Properties']['MinimumCompressionSize'] \\\n            == 100\n        # We should also create the auth lambda function.\n        assert resources['Myauth']['Type'] == 'AWS::Serverless::Function'\n        # Along with permission to invoke from API Gateway.\n        assert resources['MyauthInvokePermission'] == {\n            'Type': 'AWS::Lambda::Permission',\n            'Properties': {\n                'Action': 'lambda:InvokeFunction',\n                'FunctionName': {'Fn::GetAtt': ['Myauth', 'Arn']},\n                'Principal': 'apigateway.amazonaws.com',\n                'SourceArn': {\n                    'Fn::Sub': [\n                        ('arn:${AWS::Partition}:execute-api:${AWS::Region}'\n                         ':${AWS::AccountId}:${RestAPIId}/*'),\n                        {'RestAPIId': {'Ref': 'RestAPI'}}]}},\n        }\n        # Also verify we add the expected outputs when using\n        # a Rest API.\n        assert template['Outputs'] == {\n            'APIHandlerArn': {\n                'Value': {\n                    'Fn::GetAtt': ['APIHandler', 'Arn']\n                }\n            },\n            'APIHandlerName': {'Value': {'Ref': 'APIHandler'}},\n            'EndpointURL': {\n                'Value': {\n                    'Fn::Sub': (\n                        'https://${RestAPI}.execute-api.'\n                        '${AWS::Region}.${AWS::URLSuffix}/api/'\n                    )\n                }\n            },\n            'RestAPIId': {'Value': {'Ref': 'RestAPI'}}\n        }\n\n    @pytest.mark.parametrize('route_key,route', [\n        ('$default', 'WebsocketMessageRoute'),\n        ('$connect', 'WebsocketConnectRoute'),\n        ('$disconnect', 'WebsocketDisconnectRoute')]\n    )\n    def test_generate_partial_websocket_api(\n            self, route_key, route, sample_websocket_app):\n        # Remove all but one websocket route.\n        sample_websocket_app.websocket_handlers = {\n            name: handler for name, handler in\n            sample_websocket_app.websocket_handlers.items()\n            if name == route_key\n        }\n        config = Config.create(chalice_app=sample_websocket_app,\n                               project_dir='.',\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n        resources = template['Resources']\n\n        # Check that the template's deployment only depends on the one route.\n        depends_on = resources['WebsocketAPIDeployment'].pop('DependsOn')\n        assert [route] == depends_on\n\n    def test_generate_websocket_api(self, sample_websocket_app):\n        config = Config.create(chalice_app=sample_websocket_app,\n                               project_dir='.',\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n        resources = template['Resources']\n\n        assert resources['WebsocketAPI']['Type'] == 'AWS::ApiGatewayV2::Api'\n\n        for handler, route in (('WebsocketConnect', '$connect'),\n                               ('WebsocketMessage', '$default'),\n                               ('WebsocketDisconnect', '$disconnect'),):\n            # Lambda function should be created.\n            assert resources[handler]['Type'] == 'AWS::Serverless::Function'\n\n            # Along with permission to invoke from API Gateway.\n            assert resources['%sInvokePermission' % handler] == {\n                'Type': 'AWS::Lambda::Permission',\n                'Properties': {\n                    'Action': 'lambda:InvokeFunction',\n                    'FunctionName': {'Ref': handler},\n                    'Principal': 'apigateway.amazonaws.com',\n                    'SourceArn': {\n                        'Fn::Sub': [\n                            (\n                                'arn:${AWS::Partition}:execute-api'\n                                ':${AWS::Region}:${AWS::'\n                                'AccountId}:${WebsocketAPIId}/*'\n                            ),\n                            {'WebsocketAPIId': {'Ref': 'WebsocketAPI'}}]}},\n            }\n\n            # Ensure Integration is created.\n            assert resources['%sAPIIntegration' % handler] == {\n                'Type': 'AWS::ApiGatewayV2::Integration',\n                'Properties': {\n                    'ApiId': {\n                        'Ref': 'WebsocketAPI'\n                    },\n                    'ConnectionType': 'INTERNET',\n                    'ContentHandlingStrategy': 'CONVERT_TO_TEXT',\n                    'IntegrationType': 'AWS_PROXY',\n                    'IntegrationUri': {\n                        'Fn::Sub': [\n                            (\n                                'arn:${AWS::Partition}:apigateway'\n                                ':${AWS::Region}:lambda:path'\n                                '/2015-03-31/functions/arn:${AWS::Partition}'\n                                ':lambda:${AWS::Region}:${AWS::AccountId}'\n                                ':function:${WebsocketHandler}/invocations'\n                            ),\n                            {'WebsocketHandler': {'Ref': handler}}\n                        ],\n                    }\n                }\n            }\n\n            # Route for the handler.\n            assert resources['%sRoute' % handler] == {\n                'Type': 'AWS::ApiGatewayV2::Route',\n                'Properties': {\n                    'ApiId': {\n                        'Ref': 'WebsocketAPI'\n                    },\n                    'RouteKey': route,\n                    'Target': {\n                        'Fn::Join': [\n                            '/',\n                            [\n                                'integrations',\n                                {'Ref': '%sAPIIntegration' % handler},\n                            ]\n                        ]\n                    }\n                }\n            }\n\n        # Ensure the deployment is created. It must manually depend on\n        # the routes since it cannot be created for WebsocketAPI that has no\n        # routes. The API has no such implicit contract so CloudFormation can\n        # deploy things out of order without the explicit DependsOn.\n        depends_on = set(resources['WebsocketAPIDeployment'].pop('DependsOn'))\n        assert set(['WebsocketConnectRoute',\n                    'WebsocketMessageRoute',\n                    'WebsocketDisconnectRoute']) == depends_on\n        assert resources['WebsocketAPIDeployment'] == {\n            'Type': 'AWS::ApiGatewayV2::Deployment',\n            'Properties': {\n                'ApiId': {\n                    'Ref': 'WebsocketAPI'\n                }\n            }\n        }\n\n        # Ensure the stage is created.\n        resources['WebsocketAPIStage'] = {\n            'Type': 'AWS::ApiGatewayV2::Stage',\n            'Properties': {\n                'ApiId': {\n                    'Ref': 'WebsocketAPI'\n                },\n                'DeploymentId': {'Ref': 'WebsocketAPIDeployment'},\n                'StageName': 'api',\n            }\n        }\n\n        # Ensure the outputs are created\n        assert template['Outputs'] == {\n            'WebsocketConnectHandlerArn': {\n                'Value': {\n                    'Fn::GetAtt': ['WebsocketConnect', 'Arn']\n                }\n            },\n            'WebsocketConnectHandlerName': {\n                'Value': {'Ref': 'WebsocketConnect'}},\n            'WebsocketMessageHandlerArn': {\n                'Value': {\n                    'Fn::GetAtt': ['WebsocketMessage', 'Arn']\n                }\n            },\n            'WebsocketMessageHandlerName': {\n                'Value': {'Ref': 'WebsocketMessage'}},\n            'WebsocketDisconnectHandlerArn': {\n                'Value': {\n                    'Fn::GetAtt': ['WebsocketDisconnect', 'Arn']\n                }\n            },\n            'WebsocketDisconnectHandlerName': {'Value': {\n                'Ref': 'WebsocketDisconnect'}},\n            'WebsocketConnectEndpointURL': {\n                'Value': {\n                    'Fn::Sub': (\n                        'wss://${WebsocketAPI}.execute-api.'\n                        '${AWS::Region}.${AWS::URLSuffix}/api/'\n                    )\n                }\n            },\n            'WebsocketAPIId': {'Value': {'Ref': 'WebsocketAPI'}}\n        }\n\n    def test_managed_iam_role(self):\n        role = models.ManagedIAMRole(\n            resource_name='default_role',\n            role_name='app-dev',\n            trust_policy=LAMBDA_TRUST_POLICY,\n            policy=models.AutoGenIAMPolicy(document={'iam': 'policy'}),\n        )\n        template = self.template_gen.generate([role])\n        resources = template['Resources']\n        assert len(resources) == 1\n        cfn_role = resources['DefaultRole']\n        assert cfn_role['Type'] == 'AWS::IAM::Role'\n        assert cfn_role['Properties']['Policies'] == [\n            {'PolicyName': 'DefaultRolePolicy',\n             'PolicyDocument': {'iam': 'policy'}}\n        ]\n        # Verify the trust policy is specific to the region\n        assert cfn_role['Properties']['AssumeRolePolicyDocument'] == {\n            'Statement': [{'Action': 'sts:AssumeRole',\n                           'Effect': 'Allow',\n                           'Principal': {'Service': 'lambda.amazonaws.com'},\n                           'Sid': ''}],\n            'Version': '2012-10-17'}\n\n        # Ensure the RoleName is not in the resource properties\n        # so we don't require CAPABILITY_NAMED_IAM.\n        assert 'RoleName' not in cfn_role['Properties']\n\n    def test_single_role_generated_for_default_config(self,\n                                                      sample_app_lambda_only):\n        # The sample_app has one lambda function.\n        # We'll add a few more and verify they all share the same role.\n        @sample_app_lambda_only.lambda_function()\n        def second(event, context):\n            pass\n\n        @sample_app_lambda_only.lambda_function()\n        def third(event, context):\n            pass\n\n        config = Config.create(chalice_app=sample_app_lambda_only,\n                               project_dir='.',\n                               autogen_policy=True,\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n        roles = [resource for resource in template['Resources'].values()\n                 if resource['Type'] == 'AWS::IAM::Role']\n        assert len(roles) == 1\n        # The lambda functions should all reference this role.\n        functions = [\n            resource for resource in template['Resources'].values()\n            if resource['Type'] == 'AWS::Serverless::Function'\n        ]\n        role_names = [\n            function['Properties']['Role'] for function in functions\n        ]\n        assert role_names == [\n            {'Fn::GetAtt': ['DefaultRole', 'Arn']},\n            {'Fn::GetAtt': ['DefaultRole', 'Arn']},\n            {'Fn::GetAtt': ['DefaultRole', 'Arn']},\n        ]\n\n    def test_vpc_config_added_to_function(self, sample_app_lambda_only):\n        config = Config.create(chalice_app=sample_app_lambda_only,\n                               project_dir='.',\n                               autogen_policy=True,\n                               api_gateway_stage='api',\n                               security_group_ids=['sg1', 'sg2'],\n                               subnet_ids=['sn1', 'sn2'])\n        template = self.generate_template(config)\n        resources = template['Resources'].values()\n        lambda_fns = [resource for resource in resources\n                      if resource['Type'] == 'AWS::Serverless::Function']\n        assert len(lambda_fns) == 1\n\n        vpc_config = lambda_fns[0]['Properties']['VpcConfig']\n        assert vpc_config['SubnetIds'] == ['sn1', 'sn2']\n        assert vpc_config['SecurityGroupIds'] == ['sg1', 'sg2']\n\n    def test_helpful_error_message_on_s3_event(self, sample_app):\n        @sample_app.on_s3_event(bucket='foo')\n        def handler(event):\n            pass\n\n        config = Config.create(chalice_app=sample_app,\n                               project_dir='.',\n                               api_gateway_stage='api')\n        options = PackageOptions(mock.Mock(spec=TypedAWSClient))\n        with pytest.raises(NotImplementedError) as excinfo:\n            self.generate_template(config, 'dev', options)\n        # Should mention the decorator name.\n        assert '@app.on_s3_event' in str(excinfo.value)\n        # Should mention you can use `chalice deploy`.\n        assert 'chalice deploy' in str(excinfo.value)\n\n    def test_can_package_sns_handler(self, sample_app):\n        @sample_app.on_sns_message(topic='foo')\n        def handler(event):\n            pass\n\n        config = Config.create(chalice_app=sample_app,\n                               project_dir='.',\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n        sns_handler = template['Resources']['Handler']\n        assert sns_handler['Properties']['Events'] == {\n            'HandlerSnsSubscription': {\n                'Type': 'SNS',\n                'Properties': {\n                    'Topic': {\n                        'Fn::Sub': (\n                            'arn:${AWS::Partition}:sns:${AWS::Region}'\n                            ':${AWS::AccountId}:foo'\n                        )\n                    }\n                },\n            }\n        }\n\n    def test_can_package_sns_arn_handler(self, sample_app):\n        arn = 'arn:aws:sns:space-leo-1:1234567890:foo'\n\n        @sample_app.on_sns_message(topic=arn)\n        def handler(event):\n            pass\n\n        config = Config.create(chalice_app=sample_app,\n                               project_dir='.',\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n        sns_handler = template['Resources']['Handler']\n        assert sns_handler['Properties']['Events'] == {\n            'HandlerSnsSubscription': {\n                'Type': 'SNS',\n                'Properties': {\n                    'Topic': arn,\n                }\n            }\n        }\n\n    def test_can_package_sqs_handler(self, sample_app):\n        @sample_app.on_sqs_message(queue='foo', batch_size=5)\n        def handler(event):\n            pass\n\n        config = Config.create(chalice_app=sample_app,\n                               project_dir='.',\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n        sns_handler = template['Resources']['Handler']\n        assert sns_handler['Properties']['Events'] == {\n            'HandlerSqsEventSource': {\n                'Type': 'SQS',\n                'Properties': {\n                    'Queue': {\n                        'Fn::Sub': (\n                            'arn:${AWS::Partition}:sqs:${AWS::Region}'\n                            ':${AWS::AccountId}:foo'\n                        )\n                    },\n                    'BatchSize': 5,\n                    'MaximumBatchingWindowInSeconds': 0,\n                },\n            }\n        }\n\n    def test_can_package_sqs_handler_with_max_concurrency(self, sample_app):\n        @sample_app.on_sqs_message(\n            queue='foo',\n            batch_size=5,\n            maximum_concurrency=2\n        )\n        def handler(event):\n            pass\n\n        config = Config.create(chalice_app=sample_app,\n                               project_dir='.',\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n        sns_handler = template['Resources']['Handler']\n        assert sns_handler['Properties']['Events'] == {\n            'HandlerSqsEventSource': {\n                'Type': 'SQS',\n                'Properties': {\n                    'Queue': {\n                        'Fn::Sub': (\n                            'arn:${AWS::Partition}:sqs:${AWS::Region}'\n                            ':${AWS::AccountId}:foo'\n                        )\n                    },\n                    'BatchSize': 5,\n                    'MaximumBatchingWindowInSeconds': 0,\n                    'ScalingConfig': {'MaximumConcurrency': 2}\n                },\n            }\n        }\n\n    def test_sqs_arn_does_not_use_fn_sub(self, sample_app):\n        @sample_app.on_sqs_message(queue_arn='arn:foo:bar', batch_size=5)\n        def handler(event):\n            pass\n\n        config = Config.create(chalice_app=sample_app,\n                               project_dir='.',\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n        sqs_handler = template['Resources']['Handler']\n        assert sqs_handler['Properties']['Events'] == {\n            'HandlerSqsEventSource': {\n                'Type': 'SQS',\n                'Properties': {\n                    'Queue': 'arn:foo:bar',\n                    'BatchSize': 5,\n                    'MaximumBatchingWindowInSeconds': 0,\n                },\n            }\n        }\n\n    def test_can_package_kinesis_handler(self, sample_app):\n        @sample_app.on_kinesis_record(stream='mystream', batch_size=5)\n        def handler(event):\n            pass\n\n        config = Config.create(chalice_app=sample_app,\n                               project_dir='.',\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n        sns_handler = template['Resources']['Handler']\n        assert sns_handler['Properties']['Events'] == {\n            'HandlerKinesisEventSource': {\n                'Type': 'Kinesis',\n                'Properties': {\n                    'Stream': {\n                        'Fn::Sub': (\n                            'arn:${AWS::Partition}:kinesis:${AWS::Region}'\n                            ':${AWS::AccountId}:stream/mystream'\n                        )\n                    },\n                    'BatchSize': 5,\n                    'StartingPosition': 'LATEST',\n                    'MaximumBatchingWindowInSeconds': 0,\n                },\n            }\n        }\n\n    def test_can_package_dynamodb_handler(self, sample_app):\n        @sample_app.on_dynamodb_record(stream_arn='arn:aws:...:stream',\n                                       batch_size=5)\n        def handler(event):\n            pass\n\n        config = Config.create(chalice_app=sample_app,\n                               project_dir='.',\n                               api_gateway_stage='api')\n        template = self.generate_template(config)\n        ddb_handler = template['Resources']['Handler']\n        assert ddb_handler['Properties']['Events'] == {\n            'HandlerDynamodbEventSource': {\n                'Type': 'DynamoDB',\n                'Properties': {\n                    'Stream': 'arn:aws:...:stream',\n                    'BatchSize': 5,\n                    'StartingPosition': 'LATEST',\n                    'MaximumBatchingWindowInSeconds': 0,\n                },\n            }\n        }\n\n    def test_can_generate_custom_domain_name(self, sample_app):\n        config = Config.create(\n            chalice_app=sample_app,\n            project_dir='.',\n            api_gateway_stage='api',\n            api_gateway_endpoint_type='EDGE',\n            api_gateway_custom_domain={\n                \"certificate_arn\": \"my_cert_arn\",\n                \"domain_name\": \"example.com\",\n                \"tls_version\": \"TLS_1_2\",\n                \"tags\": {\"foo\": \"bar\", \"bar\": \"baz\"},\n            }\n        )\n        template = self.generate_template(config)\n        domain = template['Resources']['ApiGatewayCustomDomain']\n        mapping = template['Resources']['ApiGatewayCustomDomainMapping']\n        assert domain == {\n            'Type': 'AWS::ApiGateway::DomainName',\n            'Properties': {\n                'CertificateArn': 'my_cert_arn',\n                'DomainName': 'example.com',\n                'SecurityPolicy': 'TLS_1_2',\n                'EndpointConfiguration': {\n                    'Types': ['EDGE']\n                },\n                'Tags': [\n                    {'Key': 'bar',\n                     'Value': 'baz'},\n                    {'Key': 'foo',\n                     'Value': 'bar'}\n                ]\n            }\n        }\n        assert mapping == {\n            'Type': 'AWS::ApiGateway::BasePathMapping',\n            'Properties': {\n                'DomainName': {'Ref': 'ApiGatewayCustomDomain'},\n                'RestApiId': {'Ref': 'RestAPI'},\n                'Stage': config.api_gateway_stage,\n                'BasePath': '(none)',\n            }\n        }\n\n    def test_can_generate_domain_for_regional_endpoint(self, sample_app):\n        config = Config.create(\n            chalice_app=sample_app,\n            project_dir='.',\n            api_gateway_stage='api',\n            api_gateway_endpoint_type='REGIONAL',\n            api_gateway_custom_domain={\n                \"certificate_arn\": \"my_cert_arn\",\n                \"domain_name\": \"example.com\",\n            }\n        )\n        template = self.generate_template(config)\n        domain = template['Resources']['ApiGatewayCustomDomain']\n        mapping = template['Resources']['ApiGatewayCustomDomainMapping']\n        assert domain == {\n            'Type': 'AWS::ApiGateway::DomainName',\n            'Properties': {\n                'RegionalCertificateArn': 'my_cert_arn',\n                'DomainName': 'example.com',\n                'EndpointConfiguration': {\n                    'Types': ['REGIONAL']\n                }\n            }\n        }\n        assert mapping == {\n            'Type': 'AWS::ApiGateway::BasePathMapping',\n            'Properties': {\n                'DomainName': {'Ref': 'ApiGatewayCustomDomain'},\n                'RestApiId': {'Ref': 'RestAPI'},\n                'Stage': config.api_gateway_stage,\n                'BasePath': '(none)',\n            }\n        }\n\n    def test_can_generate_domain_for_ws_endpoint(self, sample_websocket_app):\n        config = Config.create(\n            chalice_app=sample_websocket_app,\n            project_dir='.',\n            api_gateway_stage='api',\n            websocket_api_custom_domain={\n                \"certificate_arn\": \"my_cert_arn\",\n                \"domain_name\": \"example.com\",\n                'tags': {'foo': 'bar', 'bar': 'baz'},\n            }\n        )\n        template = self.generate_template(config)\n        domain = template['Resources']['WebsocketApiCustomDomain']\n        mapping = template['Resources']['WebsocketApiCustomDomainMapping']\n        assert domain == {\n            'Type': 'AWS::ApiGatewayV2::DomainName',\n            'Properties': {\n                'DomainName': 'example.com',\n                'DomainNameConfigurations': [\n                    {'CertificateArn': 'my_cert_arn',\n                     'EndpointType': 'REGIONAL'},\n                ],\n                'Tags': {\n                    'foo': 'bar',\n                    'bar': 'baz'\n                },\n            }\n        }\n        assert mapping == {\n            'Type': 'AWS::ApiGatewayV2::ApiMapping',\n            'Properties': {\n                'DomainName': {'Ref': 'WebsocketApiCustomDomain'},\n                'ApiId': {'Ref': 'WebsocketAPI'},\n                'Stage': {'Ref': 'WebsocketAPIStage'},\n                'ApiMappingKey': '(none)',\n            }\n        }\n\n\nclass TestTemplateDeepMerger(object):\n    def test_can_merge_without_changing_identity(self):\n        merger = package.TemplateDeepMerger()\n        src = {}\n        dst = {}\n\n        result = merger.merge(src, dst)\n        assert result is not src\n        assert result is not dst\n        assert src is not dst\n\n    def test_does_not_mutate(self):\n        merger = package.TemplateDeepMerger()\n        src = {'foo': 'bar'}\n        dst = {'baz': 'buz'}\n\n        merger.merge(src, dst)\n        assert src == {'foo': 'bar'}\n        assert dst == {'baz': 'buz'}\n\n    def test_can_add_element(self):\n        merger = package.TemplateDeepMerger()\n        src = {'foo': 'bar'}\n        dst = {'baz': 'buz'}\n\n        result = merger.merge(src, dst)\n        assert result == {\n            'foo': 'bar',\n            'baz': 'buz',\n        }\n\n    def test_can_replace_element(self):\n        merger = package.TemplateDeepMerger()\n        src = {'foo': 'bar'}\n        dst = {'foo': 'buz'}\n\n        result = merger.merge(src, dst)\n        assert result == {\n            'foo': 'bar',\n        }\n\n    def test_can_merge_list(self):\n        merger = package.TemplateDeepMerger()\n        src = {'foo': [1, 2, 3]}\n        dst = {}\n\n        result = merger.merge(src, dst)\n        assert result == {\n            'foo': [1, 2, 3],\n        }\n\n    def test_can_merge_nested_elements(self):\n        merger = package.TemplateDeepMerger()\n        src = {\n            'foo': {\n                'bar': 'baz',\n            },\n        }\n        dst = {\n            'foo': {\n                'qux': 'quack',\n            },\n        }\n\n        result = merger.merge(src, dst)\n        assert result == {\n            'foo': {\n                'bar': 'baz',\n                'qux': 'quack',\n            }\n        }\n\n    def test_can_merge_nested_list(self):\n        merger = package.TemplateDeepMerger()\n        src = {\n            'foo': {\n                'bar': 'baz',\n            },\n        }\n        dst = {\n            'foo': {\n                'qux': [1, 2, 3, 4],\n            },\n        }\n\n        result = merger.merge(src, dst)\n        assert result == {\n            'foo': {\n                'bar': 'baz',\n                'qux': [1, 2, 3, 4],\n            }\n        }\n\n    def test_list_elements_are_replaced(self):\n        merger = package.TemplateDeepMerger()\n        src = {\n            'list': [{'foo': 'bar'}],\n        }\n        dst = {\n            'list': [{'foo': 'buz'}],\n        }\n\n        result = merger.merge(src, dst)\n        assert result == {\n            'list': [{'foo': 'bar'}],\n        }\n\n    def test_merge_can_change_type(self):\n        merger = package.TemplateDeepMerger()\n        src = {\n            'key': 'foo',\n        }\n        dst = {\n            'key': 1,\n        }\n\n        result = merger.merge(src, dst)\n        assert result == {\n            'key': 'foo'\n        }\n\n\n@pytest.mark.parametrize('filename,is_yaml', [\n    ('extras.yaml', True),\n    ('extras.YAML', True),\n    ('extras.yml', True),\n    ('extras.YML', True),\n    ('extras.foo.yml', True),\n    ('extras', False),\n    ('extras.json', False),\n    ('extras.yaml.json', False),\n    ('foo/bar/extras.yaml', True),\n    ('foo/bar/extras.YAML', True),\n])\ndef test_to_cfn_resource_name(filename, is_yaml):\n    assert package.YAMLTemplateSerializer.is_yaml_template(filename) == is_yaml\n\n\n@pytest.mark.parametrize('yaml_contents,expected', [\n    ('foo: bar', {'foo': 'bar'}),\n    ('foo: !Ref bar', {'foo': {'Ref': 'bar'}}),\n    ('foo: !GetAtt Bar.Baz', {'foo': {'Fn::GetAtt': ['Bar', 'Baz']}}),\n    ('foo: !FooBar [!Baz YetAnother, \"hello\"]',\n     {'foo': {'Fn::FooBar': [{'Fn::Baz': 'YetAnother'}, 'hello']}}),\n    ('foo: !SomeTag {\"a\": \"1\"}', {'foo': {'Fn::SomeTag': {'a': '1'}}}),\n    ('foo: !GetAtt Foo.Bar.Baz', {'foo': {'Fn::GetAtt': ['Foo', 'Bar.Baz']}}),\n    ('foo: !Condition Other', {'foo': {'Condition': 'Other'}}),\n    ('foo: !GetAtt [\"a\", \"b\"]', {'foo': {'Fn::GetAtt': ['a', 'b']}}),\n])\ndef test_supports_custom_tags(yaml_contents, expected):\n    serialize = package.YAMLTemplateSerializer()\n    actual = serialize.load_template(yaml_contents)\n    assert actual == expected\n    # Also verify we can serialize then parse them back to what we originally\n    # loaded.  Note that this is not the same thing as round tripping as\n    # we convert things like '!Ref foo' over to {'Ref': 'foo'}.\n    yaml_str = serialize.serialize_template(actual).strip()\n    reparsed = serialize.load_template(yaml_str)\n    assert actual == reparsed\n"
  },
  {
    "path": "tests/unit/test_pipeline.py",
    "content": "import pytest\n\nfrom chalice import pipeline\nfrom chalice import __version__ as chalice_version\nfrom chalice.pipeline import InvalidCodeBuildPythonVersion, PipelineParameters\n\n\n@pytest.fixture\ndef pipeline_gen():\n    return pipeline.CreatePipelineTemplateLegacy()\n\n\n@pytest.fixture\ndef pipeline_params():\n    return pipeline.PipelineParameters('appname', 'python2.7')\n\n\nclass TestPipelineGenLegacy(object):\n\n    def setup_method(self):\n        self.pipeline_gen = pipeline.CreatePipelineTemplateLegacy()\n\n    def generate_template(self, app_name='appname',\n                          lambda_python_version='python2.7',\n                          codebuild_image=None, code_source='codecommit'):\n        params = PipelineParameters(\n            app_name=app_name,\n            lambda_python_version=lambda_python_version,\n            codebuild_image=codebuild_image,\n            code_source=code_source,\n        )\n        template = self.pipeline_gen.create_template(params)\n        return template\n\n    def test_app_name_in_param_default(self):\n        template = self.generate_template(app_name='app')\n        assert template['Parameters']['ApplicationName']['Default'] == 'app'\n\n    def test_python_version_in_param_default(self):\n        template = self.generate_template(lambda_python_version='python2.7')\n        assert template['Parameters']['CodeBuildImage']['Default'] == \\\n            'aws/codebuild/python:2.7.12'\n\n    def test_python_36_in_param_default(self):\n        template = self.generate_template(lambda_python_version='python3.6')\n        assert template['Parameters']['CodeBuildImage']['Default'] == \\\n            'aws/codebuild/python:3.6.5'\n\n    def test_invalid_python_throws_error(self):\n        with pytest.raises(InvalidCodeBuildPythonVersion):\n            self.generate_template('app', 'python2.6')\n\n    def test_nonsense_py_version_throws_error(self):\n        with pytest.raises(InvalidCodeBuildPythonVersion):\n            self.generate_template('app', 'foobar')\n\n    def test_can_provide_codebuild_image(self):\n        template = self.generate_template('appname', 'python2.7',\n                                          codebuild_image='python:3.6.1')\n        default_image = template['Parameters']['CodeBuildImage']['Default']\n        assert default_image == 'python:3.6.1'\n\n    def test_no_source_resource_when_using_github(self):\n        template = self.generate_template(code_source='github')\n        resources = template['Resources']\n        assert 'SourceRepository' not in set(resources)\n\n    def test_can_add_github_as_source_stage(self):\n        template = self.generate_template(code_source='github')\n        resources = template['Resources']\n        source_stage = resources['AppPipeline']['Properties']['Stages'][0]\n        assert source_stage['Name'] == 'Source'\n        actions = source_stage['Actions']\n        assert len(actions) == 1\n        action = actions[0]\n        assert action['ActionTypeId'] == {\n            'Category': 'Source',\n            'Provider': 'GitHub',\n            'Owner': 'ThirdParty',\n            'Version': '1',\n        }\n        assert action['RunOrder'] == 1\n        assert action['OutputArtifacts'] == [{'Name': 'SourceRepo'}]\n        assert action['Configuration'] == {\n            'Owner': {'Ref': 'GithubOwner'},\n            'Repo': {'Ref': 'GithubRepoName'},\n            'OAuthToken': {'Ref': 'GithubPersonalToken'},\n            'Branch': 'master',\n            'PollForSourceChanges': True,\n        }\n\n\nclass TestPipelineGenV2(object):\n\n    def setup_method(self):\n        self.pipeline_gen = pipeline.CreatePipelineTemplateV2()\n\n    def generate_template(self, app_name='appname',\n                          lambda_python_version='python3.9',\n                          codebuild_image=None, code_source='github',\n                          pipeline_version='v2'):\n        params = PipelineParameters(\n            app_name=app_name,\n            lambda_python_version=lambda_python_version,\n            codebuild_image=codebuild_image,\n            code_source=code_source,\n            pipeline_version=pipeline_version,\n        )\n        template = self.pipeline_gen.create_template(params)\n        return template\n\n    def test_new_default_codebuild_image(self):\n        template = self.generate_template(app_name='app')\n        assert template['Parameters']['CodeBuildImage']['Default'] == (\n            \"aws/codebuild/amazonlinux2-x86_64-standard:3.0\"\n        )\n\n    def test_validate_python_versions(self):\n        with pytest.raises(InvalidCodeBuildPythonVersion):\n            self.generate_template(lambda_python_version='python2.7')\n\n    def test_uses_v2_codebuild_spec(self):\n        # The codebuild v2 spec is tested separately, we just need a\n        # sanity check to ensure we're using the v0.2 buildspec version.\n        template = self.generate_template(app_name='app')\n        codebuild_job = template['Resources']['AppPackageBuild']\n        assert \"version: '0.2'\" in codebuild_job[\n            'Properties']['Source']['BuildSpec']\n\n    def test_github_source_uses_secretsmanager_in_v2(self):\n        template = self.generate_template(code_source='github')\n        source_stage = template['Resources'][\n            'AppPipeline']['Properties']['Stages'][0]\n        assert source_stage['Name'] == 'Source'\n        oauth_token = source_stage['Actions'][0]['Configuration']['OAuthToken']\n        assert oauth_token == {\n            'Fn::Join': [\n                '', ['{{resolve:secretsmanager:',\n                     {'Ref': 'GithubRepoSecretId'},\n                     ':SecretString:',\n                     {'Ref': 'GithubRepoSecretJSONKey'},\n                     '}}']\n            ]\n        }\n        # We should also add these Refs to our Parameters.\n        params = template['Parameters']\n        assert 'GithubRepoSecretId' in params\n        assert 'GithubRepoSecretJSONKey' in params\n\n\ndef test_source_repo_resource(pipeline_params):\n    template = {}\n    pipeline.CodeCommitSourceRepository().add_to_template(\n        template, pipeline_params)\n    assert template == {\n        \"Resources\": {\n            \"SourceRepository\": {\n                \"Type\": \"AWS::CodeCommit::Repository\",\n                \"Properties\": {\n                    \"RepositoryName\": {\n                        \"Ref\": \"ApplicationName\"\n                    },\n                    \"RepositoryDescription\": {\n                        \"Fn::Sub\": \"Source code for ${ApplicationName}\"\n                    }\n                }\n            }\n        },\n        \"Outputs\": {\n            \"SourceRepoURL\": {\n                \"Value\": {\n                    \"Fn::GetAtt\": \"SourceRepository.CloneUrlHttp\"\n                }\n            }\n        }\n    }\n\n\ndef test_codebuild_resource(pipeline_params):\n    template = {}\n    pipeline.CodeBuild().add_to_template(template, pipeline_params)\n    resources = template['Resources']\n    assert 'ApplicationBucket' in resources\n    assert 'CodeBuildRole' in resources\n    assert 'CodeBuildPolicy' in resources\n    assert 'AppPackageBuild' in resources\n    assert resources['ApplicationBucket'] == {'Type': 'AWS::S3::Bucket'}\n    assert template['Outputs']['CodeBuildRoleArn'] == {\n        'Value': {'Fn::GetAtt': 'CodeBuildRole.Arn'}\n    }\n\n\ndef test_codepipeline_resource(pipeline_params):\n    template = {}\n    pipeline.CodePipeline().add_to_template(template, pipeline_params)\n    resources = template['Resources']\n    assert 'AppPipeline' in resources\n    assert 'ArtifactBucketStore' in resources\n    assert 'CodePipelineRole' in resources\n    assert 'CFNDeployRole' in resources\n    # Some basic sanity checks\n    assert resources['AppPipeline']['Type'] == 'AWS::CodePipeline::Pipeline'\n    assert resources['ArtifactBucketStore']['Type'] == 'AWS::S3::Bucket'\n    assert resources['CodePipelineRole']['Type'] == 'AWS::IAM::Role'\n    assert resources['CFNDeployRole']['Type'] == 'AWS::IAM::Role'\n    properties = resources['AppPipeline']['Properties']\n    stages = properties['Stages']\n    beta_stage = stages[2]\n    beta_config = beta_stage['Actions'][0]['Configuration']\n    assert beta_config == {\n        'ActionMode': 'CHANGE_SET_REPLACE',\n        'Capabilities': 'CAPABILITY_IAM',\n        'ChangeSetName': {'Fn::Sub': '${ApplicationName}ChangeSet'},\n        'RoleArn': {'Fn::GetAtt': 'CFNDeployRole.Arn'},\n        'StackName': {'Fn::Sub': '${ApplicationName}BetaStack'},\n        'TemplatePath': 'CompiledCFNTemplate::transformed.yaml'\n    }\n\n\ndef test_install_requirements_in_buildspec(pipeline_params):\n    template = {}\n    pipeline_params.chalice_version_range = '>=1.0.0,<2.0.0'\n    pipeline.CodeBuild().add_to_template(template, pipeline_params)\n    build = template['Resources']['AppPackageBuild']\n    build_spec = build['Properties']['Source']['BuildSpec']\n    assert 'pip install -r requirements.txt' in build_spec\n    assert \"pip install 'chalice>=1.0.0,<2.0.0'\" in build_spec\n\n\ndef test_default_version_range_locks_minor_version():\n    parts = [int(p) for p in chalice_version.split('.')]\n    min_version = '%s.%s.%s' % (parts[0], parts[1], 0)\n    max_version = '%s.%s.%s' % (parts[0], parts[1] + 1, 0)\n    params = pipeline.PipelineParameters('appname', 'python2.7')\n    assert params.chalice_version_range == '>=%s,<%s' % (\n        min_version, max_version\n    )\n\n\ndef test_can_validate_python_version():\n    with pytest.raises(InvalidCodeBuildPythonVersion):\n        pipeline.PipelineParameters(\n            'myapp', lambda_python_version='bad-python-value'\n        )\n\n\ndef test_can_extract_python_version():\n    assert pipeline.PipelineParameters('app', 'python3.9').py_major_minor == (\n        '3.9')\n\n\ndef test_can_generate_github_source(pipeline_params):\n    template = {}\n    pipeline_params.code_source = 'github'\n    pipeline.GithubSource().add_to_template(template, pipeline_params)\n    cfn_params = template['Parameters']\n    assert set(cfn_params) == set(['GithubOwner', 'GithubRepoName',\n                                   'GithubPersonalToken'])\n\n\ndef test_can_create_buildspec_v2():\n    params = pipeline.PipelineParameters('myapp', 'python3.9')\n    buildspec = pipeline.create_buildspec_v2(params)\n    assert buildspec['phases']['install']['runtime-versions'] == {\n        'python': '3.9',\n    }\n\n\ndef test_build_extractor():\n    template = {\n        'Resources': {\n            'AppPackageBuild': {\n                'Properties': {\n                    'Source': {\n                        'BuildSpec': 'foobar'\n                    }\n                }\n            }\n        }\n    }\n    extract = pipeline.BuildSpecExtractor()\n    extracted = extract.extract_buildspec(template)\n    assert extracted == 'foobar'\n    assert 'BuildSpec' not in template[\n        'Resources']['AppPackageBuild']['Properties']['Source']\n"
  },
  {
    "path": "tests/unit/test_policy.py",
    "content": "from chalice.config import Config\nfrom chalice.policy import PolicyBuilder, AppPolicyGenerator\nfrom chalice.policy import diff_policies\nfrom chalice.utils import OSUtils  # noqa\n\n\nclass OsUtilsMock(OSUtils):\n    def file_exists(self, *args, **kwargs):\n        return True\n\n    def get_file_contents(selfs, *args, **kwargs):\n        return ''\n\n\ndef iam_policy(client_calls):\n    builder = PolicyBuilder()\n    policy = builder.build_policy_from_api_calls(client_calls)\n    return policy\n\n\ndef test_app_policy_generator_vpc_policy():\n    config = Config.create(\n        subnet_ids=['sn1', 'sn2'],\n        security_group_ids=['sg1', 'sg2'],\n        project_dir='.'\n    )\n    generator = AppPolicyGenerator(OsUtilsMock())\n    policy = generator.generate_policy(config)\n    assert policy == {'Statement': [\n        {'Action': ['logs:CreateLogGroup',\n                    'logs:CreateLogStream',\n                    'logs:PutLogEvents'],\n         'Effect': 'Allow',\n         'Resource': 'arn:*:logs:*:*:*'},\n        {'Action': ['ec2:CreateNetworkInterface',\n                    'ec2:DescribeNetworkInterfaces',\n                    'ec2:DetachNetworkInterface',\n                    'ec2:DeleteNetworkInterface'],\n         'Effect': 'Allow',\n         'Resource': '*'},\n    ], 'Version': '2012-10-17'}\n\n\ndef assert_policy_is(actual, expected):\n    # Prune out the autogen's stuff we don't\n    # care about.\n    statements = actual['Statement']\n    for s in statements:\n        del s['Sid']\n    assert expected == statements\n\n\ndef test_single_call():\n    assert_policy_is(iam_policy({'dynamodb': set(['list_tables'])}), [{\n        'Effect': 'Allow',\n        'Action': [\n            'dynamodb:ListTables'\n        ],\n        'Resource': [\n            '*',\n        ]\n    }])\n\n\ndef test_multiple_calls_in_same_service():\n    expected_policy = [{\n        'Effect': 'Allow',\n        'Action': [\n            'dynamodb:DescribeTable',\n            'dynamodb:ListTables',\n        ],\n        'Resource': [\n            '*',\n        ]\n    }]\n    assert_policy_is(\n        iam_policy({'dynamodb': set(['list_tables', 'describe_table'])}),\n        expected_policy\n    )\n\n\ndef test_multiple_services_used():\n    client_calls = {\n        'dynamodb': set(['list_tables']),\n        'cloudformation': set(['create_stack']),\n    }\n    assert_policy_is(iam_policy(client_calls), [\n        {\n            'Effect': 'Allow',\n            'Action': [\n                'cloudformation:CreateStack',\n            ],\n            'Resource': [\n                '*',\n            ]\n        },\n        {\n            'Effect': 'Allow',\n            'Action': [\n                'dynamodb:ListTables',\n            ],\n            'Resource': [\n                '*',\n            ]\n        },\n    ])\n\n\ndef test_not_one_to_one_mapping():\n    client_calls = {\n        's3': set(['list_buckets', 'list_objects',\n                   'create_multipart_upload']),\n    }\n    assert_policy_is(iam_policy(client_calls), [\n        {\n            'Effect': 'Allow',\n            'Action': [\n                's3:ListAllMyBuckets',\n                's3:ListBucket',\n                's3:PutObject',\n            ],\n            'Resource': [\n                '*',\n            ]\n        },\n    ])\n\n\ndef test_can_diff_policy_removed():\n    first = iam_policy({'s3': {'list_buckets', 'list_objects'}})\n    second = iam_policy({'s3': {'list_buckets'}})\n    assert diff_policies(first, second) == {'removed': {'s3:ListBucket'}}\n\n\ndef test_can_diff_policy_added():\n    first = iam_policy({'s3': {'list_buckets'}})\n    second = iam_policy({'s3': {'list_buckets', 'list_objects'}})\n    assert diff_policies(first, second) == {'added': {'s3:ListBucket'}}\n\n\ndef test_can_diff_multiple_services():\n    first = iam_policy({\n        's3': {'list_buckets'},\n        'dynamodb': {'create_table'},\n        'cloudformation': {'create_stack', 'delete_stack'},\n    })\n    second = iam_policy({\n        's3': {'list_buckets', 'list_objects'},\n        'cloudformation': {'create_stack', 'update_stack'},\n    })\n    assert diff_policies(first, second) == {\n        'added': {'s3:ListBucket', 'cloudformation:UpdateStack'},\n        'removed': {'cloudformation:DeleteStack', 'dynamodb:CreateTable'},\n    }\n\n\ndef test_no_changes():\n    first = iam_policy({'s3': {'list_buckets', 'list_objects'}})\n    second = iam_policy({'s3': {'list_buckets', 'list_objects'}})\n    assert diff_policies(first, second) == {}\n\n\ndef test_can_handle_high_level_abstractions():\n    policy = iam_policy({\n        's3': set(['download_file', 'upload_file', 'copy'])\n    })\n    assert_policy_is(policy, [{\n        'Effect': 'Allow',\n        'Action': [\n            's3:AbortMultipartUpload',\n            's3:GetObject',\n            's3:PutObject',\n        ],\n        'Resource': [\n            '*',\n        ]\n    }])\n\n\ndef test_noop_for_unknown_methods():\n    assert_policy_is(iam_policy({'s3': set(['unknown_method'])}), [])\n"
  },
  {
    "path": "tests/unit/test_test.py",
    "content": "import os\nimport json\n\nimport pytest\n\nfrom chalice.test import Client, FunctionNotFoundError\nfrom chalice import Response, BadRequestError, Chalice, Blueprint, AuthResponse\n\n\ndef test_can_make_http_request(sample_app):\n    with Client(sample_app) as client:\n        response = client.http.get('/')\n        assert response.status_code == 200\n        assert response.json_body == {}\n        assert response.body == b'{}'\n\n\ndef test_can_pass_http_url(sample_app):\n\n    @sample_app.route('/{name}')\n    def hello(name):\n        return {'hello': name}\n\n    with Client(sample_app) as client:\n        response = client.http.get('/james')\n        assert response.json_body == {'hello': 'james'}\n\n\ndef test_make_other_http_methods_request(sample_app):\n\n    @sample_app.route('/methods', methods=['POST', 'PUT', 'PATCH', 'OPTIONS',\n                                           'DELETE', 'HEAD'])\n    def method():\n        return {'method': sample_app.current_request.method}\n\n    with Client(sample_app) as client:\n        assert client.http.post('/methods').json_body == {'method': 'POST'}\n        assert client.http.put('/methods').json_body == {'method': 'PUT'}\n        assert client.http.patch('/methods').json_body == {'method': 'PATCH'}\n        assert client.http.delete('/methods').json_body == {'method': 'DELETE'}\n        assert client.http.head('/methods').json_body == {'method': 'HEAD'}\n        assert client.http.options('/methods').json_body == {\n            'method': 'OPTIONS'}\n\n\ndef test_can_provide_http_headers(sample_app):\n    @sample_app.route('/header')\n    def headers():\n        return {'value': sample_app.current_request.headers['x-my-header']}\n\n    with Client(sample_app) as client:\n        response = client.http.get('/header', headers={'x-my-header': 'foo'})\n        assert response.json_body == {'value': 'foo'}\n\n\ndef test_can_return_error_message(sample_app):\n    @sample_app.route('/error')\n    def error():\n        raise BadRequestError(\"bad request\")\n\n    with Client(sample_app) as client:\n        response = client.http.get('/error')\n        assert response.status_code == 400\n        assert response.json_body['Code'] == 'BadRequestError'\n        assert 'bad request' in response.json_body['Message']\n\n\ndef test_can_return_binary_data(sample_app):\n    @sample_app.route('/bin-echo')\n    def bin_echo():\n        raw_request_body = sample_app.current_request.raw_body\n        return Response(body=raw_request_body,\n                        status_code=200,\n                        headers={'Content-Type': 'application/octet-stream'})\n\n    with Client(sample_app) as client:\n        random_bytes = os.urandom(16)\n        response = client.http.get(\n            '/bin-echo', body=random_bytes,\n            headers={'Accept': 'application/octet-stream'})\n        assert response.body == random_bytes\n        assert response.json_body is None\n\n\ndef test_can_access_env_vars_in_rest_api(sample_app, tmpdir):\n    fake_config = {\n        \"version\": \"2.0\",\n        \"app_name\": \"testenv\",\n        \"stages\": {\n            \"prod\": {\n                \"api_gateway_stage\": \"api\",\n                \"environment_variables\": {\n                    \"MY_ENV_VAR\": \"TOP LEVEL\"\n                },\n            }\n        }\n    }\n    tmpdir.mkdir('.chalice').join('config.json').write(\n        json.dumps(fake_config).encode('utf-8'))\n    project_dir = str(tmpdir)\n    os.environ.pop('MY_ENV_VAR', None)\n\n    @sample_app.route('/env')\n    def env_vars():\n        return {'value': os.environ.get('MY_ENV_VAR')}\n\n    with Client(sample_app, project_dir=project_dir,\n                stage_name='prod') as client:\n        response = client.http.get('/env')\n        assert response.json_body == {'value': 'TOP LEVEL'}\n\n\ndef test_authorizers_return_http_response_on_error(sample_app):\n    @sample_app.authorizer()\n    def myauth(event):\n        if event.token == 'allow':\n            return AuthResponse(['*'], principal_id='id')\n        return AuthResponse([], principal_id='noone')\n\n    @sample_app.route('/needs-auth', authorizer=myauth)\n    def needs_auth():\n        return {'success': True}\n\n    with Client(sample_app) as client:\n        response = client.http.get('/needs-auth',\n                                   headers={'Authorization': 'deny'})\n        assert response.status_code == 403\n        assert client.http.get('/needs-auth').status_code == 401\n\n\ndef test_can_test_authorizers(sample_app):\n    @sample_app.authorizer()\n    def myauth(event):\n        if event.token == 'allow':\n            return AuthResponse(['*'], principal_id='id')\n\n    @sample_app.route('/needs-auth', authorizer=myauth)\n    def needs_auth():\n        return {'success': True}\n\n    with Client(sample_app) as client:\n        response = client.http.get('/needs-auth',\n                                   headers={'Authorization': 'allow'})\n        assert response.json_body == {'success': True}\n\n\n# Tests for pure lambda and event handlers.\n\ndef test_can_invoke_pure_lambda_function():\n    app = Chalice('lambda-only')\n\n    @app.lambda_function()\n    def foo(event, context):\n        return {'event': event}\n\n    with Client(app) as client:\n        response = client.lambda_.invoke('foo', {'hello': 'world'})\n        assert response.payload == {'event': {'hello': 'world'}}\n\n\ndef test_error_if_function_does_not_exist():\n    app = Chalice('lambda-only')\n\n    with Client(app) as client:\n        with pytest.raises(FunctionNotFoundError):\n            client.lambda_.invoke('unknown-function', {})\n\n\ndef test_payload_not_required_for_invoke():\n    app = Chalice('lambda-only')\n\n    @app.lambda_function()\n    def foo(event, context):\n        return {'event': event}\n\n    with Client(app) as client:\n        response = client.lambda_.invoke('foo')\n        assert response.payload == {'event': {}}\n\n\ndef test_can_access_environment_variables_in_function(tmpdir):\n    app = Chalice('lambda-only')\n    fake_config = {\n        \"version\": \"2.0\",\n        \"app_name\": \"testenv\",\n        \"stages\": {\n            \"prod\": {\n                \"api_gateway_stage\": \"api\",\n                \"environment_variables\": {\n                    \"MY_ENV_VAR\": \"TOP LEVEL\"\n                },\n                \"lambda_functions\": {\n                    \"bar\": {\n                        \"environment_variables\": {\n                            \"MY_ENV_VAR\": \"OVERRIDE\"\n                        }\n                    }\n                }\n            }\n        }\n    }\n    tmpdir.mkdir('.chalice').join('config.json').write(\n        json.dumps(fake_config).encode('utf-8'))\n    project_dir = str(tmpdir)\n    os.environ.pop('MY_ENV_VAR', None)\n\n    @app.lambda_function()\n    def foo(event, context):\n        return {'myvalue': os.environ.get('MY_ENV_VAR')}\n\n    @app.lambda_function()\n    def bar(event, context):\n        return {'myvalue': os.environ.get('MY_ENV_VAR')}\n\n    with Client(app, project_dir=project_dir, stage_name='prod') as client:\n        assert client.lambda_.invoke('foo', {}).payload == {\n            'myvalue': 'TOP LEVEL'\n        }\n        assert client.lambda_.invoke('bar', {}).payload == {\n            'myvalue': 'OVERRIDE'\n        }\n    assert 'MY_ENV_VAR' not in os.environ\n\n\ndef test_can_invoke_event_handler():\n    app = Chalice('lambda-only')\n\n    @app.on_sns_message(topic='mytopic')\n    def foo(event):\n        return {'message': event.message,\n                'subject': event.subject,\n                'message_attributes': event.message_attributes}\n\n    with Client(app) as client:\n        event = client.events.generate_sns_event(message='my message',\n                                                 subject='hello',\n                                                 message_attributes={\n                                                     \"my_attr\": {\n                                                         'Type': 'String',\n                                                         'Value': 'some_attr'\n                                                     }\n                                                 })\n        response = client.lambda_.invoke('foo', event)\n        assert response.payload == {'message': 'my message',\n                                    'subject': 'hello',\n                                    'message_attributes': {\n                                        \"my_attr\": {\n                                            'Type': 'String',\n                                            'Value': 'some_attr'\n                                        }\n                                    }}\n\n\ndef test_can_generate_s3_event():\n    app = Chalice('lambda-only')\n\n    @app.on_s3_event(bucket='mybucket')\n    def foo(event):\n        return {'bucket': event.bucket,\n                'key': event.key}\n\n    with Client(app) as client:\n        event = client.events.generate_s3_event(\n            bucket='mybucket', key='mykey')\n        response = client.lambda_.invoke('foo', event)\n        assert response.payload == {'bucket': 'mybucket',\n                                    'key': 'mykey'}\n\n\ndef test_can_generate_sqs_event():\n    app = Chalice('lambda-only')\n\n    @app.on_sqs_message(queue='myqueue')\n    def foo(event):\n        return [record.body for record in event]\n\n    with Client(app) as client:\n        event = client.events.generate_sqs_event(\n            message_bodies=['foo', 'bar', 'baz'])\n        response = client.lambda_.invoke('foo', event)\n        assert response.payload == ['foo', 'bar', 'baz']\n\n\ndef test_can_generate_cloudwatch_event():\n    app = Chalice('lambda-only')\n\n    @app.on_cw_event({'source': ['aws.ec2']})\n    def foo(event):\n        return {'detail': event.detail}\n\n    with Client(app) as client:\n        event = client.events.generate_cw_event(\n            source='aws.ec2', detail_type='EC2 State-change',\n            resources=['arn:aws:ec2:...:instance/i-abc'],\n            detail={'instance-id': 'i-1234', 'state': 'pending'}\n        )\n        response = client.lambda_.invoke('foo', event)\n        assert response.payload == {'detail': {'instance-id': 'i-1234',\n                                               'state': 'pending'}}\n\n\ndef test_can_generate_kinesis_event():\n    app = Chalice('kinesis')\n\n    @app.on_kinesis_record(stream='mystream')\n    def foo(event):\n        return [record.data for record in event]\n\n    with Client(app) as client:\n        event = client.events.generate_kinesis_event(\n            message_bodies=[b'foo', b'bar', b'baz'])\n        response = client.lambda_.invoke('foo', event)\n        assert response.payload == [b'foo', b'bar', b'baz']\n\n\ndef test_can_mix_pure_lambda_and_event_handlers():\n    app = Chalice('lambda-only')\n\n    @app.on_sns_message(topic='mytopic')\n    def foo(event):\n        return {'message': event.message,\n                'subject': event.subject}\n\n    @app.lambda_function()\n    def bar(event, context):\n        return {'event': event}\n\n    @app.route('/')\n    def index():\n        return {'hello': 'restapi'}\n\n    with Client(app) as client:\n        assert client.lambda_.invoke(\n            'foo',\n            client.events.generate_sns_event(\n                message='my message', subject='hello')\n        ).payload == {'message': 'my message', 'subject': 'hello'}\n        assert client.lambda_.invoke(\n            'bar', {'hello': 'world'}\n        ).payload == {'event': {'hello': 'world'}}\n        assert client.http.get('/').json_body == {'hello': 'restapi'}\n\n\ndef test_can_invoke_handler_from_blueprint():\n    bp = Blueprint('testblueprint')\n\n    @bp.lambda_function()\n    def my_foo(event, context):\n        return {'event': event}\n\n    app = Chalice('myapp')\n    app.register_blueprint(bp)\n\n    with Client(app) as client:\n        response = client.lambda_.invoke('my_foo', {'hello': 'world'})\n        assert response.payload == {'event': {'hello': 'world'}}\n\n\ndef test_can_invoke_handler_with_blueprint_prefix():\n    bp = Blueprint('testblueprint')\n\n    @bp.lambda_function()\n    def my_foo(event, context):\n        return {'event': event}\n\n    app = Chalice('myapp')\n    app.register_blueprint(bp, name_prefix='bp_prefix_')\n\n    with Client(app) as client:\n        response = client.lambda_.invoke('bp_prefix_my_foo',\n                                         {'hello': 'world'})\n        assert response.payload == {'event': {'hello': 'world'}}\n\n\ndef test_lambda_function_with_custom_name():\n    app = Chalice('lambda-only')\n\n    @app.lambda_function(name='my-custom-name')\n    def foo(event, context):\n        return {'event': event}\n\n    with Client(app) as client:\n        response = client.lambda_.invoke('my-custom-name', {'hello': 'world'})\n        assert response.payload == {'event': {'hello': 'world'}}\n"
  },
  {
    "path": "tests/unit/test_utils.py",
    "content": "import os\nimport re\nfrom unittest import mock\nimport sys\n\nimport click\nimport pytest\nfrom six import StringIO\nfrom hypothesis.strategies import text\nfrom hypothesis import given\nimport string\nfrom dateutil import tz\nfrom datetime import datetime\n\nfrom chalice import utils\n\n\nclass TestUI:\n    def setup_method(self):\n        self.out = StringIO()\n        self.err = StringIO()\n        self.ui = utils.UI(self.out, self.err)\n\n    def test_write_goes_to_out_obj(self):\n        self.ui.write(\"Foo\")\n        assert self.out.getvalue() == 'Foo'\n        assert self.err.getvalue() == ''\n\n    def test_error_goes_to_err_obj(self):\n        self.ui.error(\"Foo\")\n        assert self.err.getvalue() == 'Foo'\n        assert self.out.getvalue() == ''\n\n    def test_confirm_raises_own_exception(self):\n        confirm = mock.Mock(spec=click.confirm)\n        confirm.side_effect = click.Abort()\n        ui = utils.UI(self.out, self.err, confirm)\n        with pytest.raises(utils.AbortedError):\n            ui.confirm(\"Confirm?\")\n\n    def test_confirm_returns_value(self):\n        confirm = mock.Mock(spec=click.confirm)\n        confirm.return_value = 'foo'\n        ui = utils.UI(self.out, self.err, confirm)\n        return_value = ui.confirm(\"Confirm?\")\n        assert return_value == 'foo'\n\n\nclass TestChaliceZip(object):\n\n    def test_chalice_zip_file(self, tmpdir):\n        tmpdir.mkdir('foo').join('app.py').write('# Test app')\n        zip_path = tmpdir.join('app.zip')\n        app_filename = str(tmpdir.join('foo', 'app.py'))\n        # Add an executable file to test preserving permissions.\n        script_obj = tmpdir.join('foo', 'myscript.sh')\n        script_obj.write('echo foo')\n        script_file = str(script_obj)\n        os.chmod(script_file, 0o755)\n\n        with utils.ChaliceZipFile(str(zip_path), 'w') as z:\n            z.write(app_filename)\n            z.write(script_file)\n\n        with utils.ChaliceZipFile(str(zip_path)) as z:\n            assert len(z.infolist()) == 2\n            # Remove the leading '/'.\n            app = z.getinfo(app_filename[1:])\n            assert app.date_time == (1980, 1, 1, 0, 0, 0)\n            assert app.external_attr >> 16 == os.stat(app_filename).st_mode\n            # Verify executable permission is preserved.\n            script = z.getinfo(script_file[1:])\n            assert script.date_time == (1980, 1, 1, 0, 0, 0)\n            assert script.external_attr >> 16 == os.stat(script_file).st_mode\n\n\nclass TestPipeReader(object):\n    def test_pipe_reader_does_read_pipe(self):\n        mock_stream = mock.Mock(spec=sys.stdin)\n        mock_stream.isatty.return_value = False\n        mock_stream.read.return_value = 'foobar'\n        reader = utils.PipeReader(mock_stream)\n        value = reader.read()\n        assert value == 'foobar'\n\n    def test_pipe_reader_does_not_read_tty(self):\n        mock_stream = mock.Mock(spec=sys.stdin)\n        mock_stream.isatty.return_value = True\n        mock_stream.read.return_value = 'foobar'\n        reader = utils.PipeReader(mock_stream)\n        value = reader.read()\n        assert value is None\n\n\ndef test_serialize_json():\n    assert utils.serialize_to_json({'foo': 'bar'}) == (\n        '{\\n'\n        '  \"foo\": \"bar\"\\n'\n        '}\\n'\n    )\n\n\n@pytest.mark.parametrize('name,cfn_name', [\n    ('f', 'F'),\n    ('foo', 'Foo'),\n    ('foo_bar', 'FooBar'),\n    ('foo_bar_baz', 'FooBarBaz'),\n    ('F', 'F'),\n    ('FooBar', 'FooBar'),\n    ('S3Bucket', 'S3Bucket'),\n    ('s3Bucket', 'S3Bucket'),\n    ('123', '123'),\n    ('foo-bar-baz', 'FooBarBaz'),\n    ('foo_bar-baz', 'FooBarBaz'),\n    ('foo-bar_baz', 'FooBarBaz'),\n    # Not actually possible, but we should\n    # ensure we only have alphanumeric chars.\n    ('foo_bar!?', 'FooBar'),\n    ('_foo_bar', 'FooBar'),\n])\ndef test_to_cfn_resource_name(name, cfn_name):\n    assert utils.to_cfn_resource_name(name) == cfn_name\n\n\n@given(name=text(alphabet=string.ascii_letters + string.digits + '-_'))\ndef test_to_cfn_resource_name_properties(name):\n    try:\n        result = utils.to_cfn_resource_name(name)\n    except ValueError:\n        # This is acceptable, the function raises ValueError\n        # on bad input.\n        pass\n    else:\n        assert re.search('[^A-Za-z0-9]', result) is None\n\n\nclass TestTimestampUtils:\n    def setup_method(self):\n        self.mock_now = mock.Mock(spec=datetime.utcnow)\n        self.set_now()\n        self.timestamp_convert = utils.TimestampConverter(self.mock_now)\n\n    def set_now(self, year=2020, month=1, day=1, hour=0, minute=0, sec=0):\n        self.now = datetime(\n            year, month, day, hour, minute, sec, tzinfo=tz.tzutc())\n        self.mock_now.return_value = self.now\n\n    def test_iso_no_timezone(self):\n        assert self.timestamp_convert.timestamp_to_datetime(\n            '2020-01-01T00:00:01.000000') == datetime(2020, 1, 1, 0, 0, 1)\n\n    def test_iso_with_timezone(self):\n        assert (\n            self.timestamp_convert.timestamp_to_datetime(\n                '2020-01-01T00:00:01.000000-01:00'\n            ) == datetime(2020, 1, 1, 0, 0, 1, tzinfo=tz.tzoffset(None, -3600))\n        )\n\n    def test_to_datetime_relative_second(self):\n        self.set_now(sec=2)\n        assert (\n            self.timestamp_convert.timestamp_to_datetime('1s') ==\n            datetime(2020, 1, 1, 0, 0, 1, tzinfo=tz.tzutc())\n        )\n\n    def test_to_datetime_relative_multiple_seconds(self):\n        self.set_now(sec=5)\n        assert (\n            self.timestamp_convert.timestamp_to_datetime('2s') ==\n            datetime(2020, 1, 1, 0, 0, 3, tzinfo=tz.tzutc())\n        )\n\n    def test_to_datetime_relative_minute(self):\n        self.set_now(minute=2)\n        assert (\n            self.timestamp_convert.timestamp_to_datetime('1m') ==\n            datetime(2020, 1, 1, 0, 1, 0, tzinfo=tz.tzutc())\n        )\n\n    def test_to_datetime_relative_hour(self):\n        self.set_now(hour=2)\n        assert (\n            self.timestamp_convert.timestamp_to_datetime('1h') ==\n            datetime(2020, 1, 1, 1, 0, 0, tzinfo=tz.tzutc())\n        )\n\n    def test_to_datetime_relative_day(self):\n        self.set_now(day=3)  # 1970-01-03\n        assert (\n            self.timestamp_convert.timestamp_to_datetime('1d') ==\n            datetime(2020, 1, 2, 0, 0, 0, tzinfo=tz.tzutc())\n        )\n\n    def test_to_datetime_relative_week(self):\n        self.set_now(day=14)\n        assert (\n            self.timestamp_convert.timestamp_to_datetime('1w') ==\n            datetime(2020, 1, 7, 0, 0, 0, tzinfo=tz.tzutc())\n        )\n\n\n@pytest.mark.parametrize('timestamp,expected', [\n    ('2020-01-01', datetime(2020, 1, 1)),\n    ('2020-01-01T00:00:01', datetime(2020, 1, 1, 0, 0, 1)),\n    ('2020-02-02T01:02:03', datetime(2020, 2, 2, 1, 2, 3)),\n    ('2020-01-01T00:00:00Z', datetime(2020, 1, 1, 0, 0, tzinfo=tz.tzutc())),\n    ('2020-01-01T00:00:00-04:00', datetime(2020, 1, 1, 0, 0, 0,\n                                           tzinfo=tz.tzoffset('EDT', -14400))),\n])\ndef test_parse_iso8601_timestamp(timestamp, expected):\n    timestamp_convert = utils.TimestampConverter()\n    assert timestamp_convert.parse_iso8601_timestamp(timestamp) == expected\n"
  },
  {
    "path": "tests/unit/vendored/__init__.py",
    "content": ""
  },
  {
    "path": "tests/unit/vendored/botocore/__init__.py",
    "content": ""
  },
  {
    "path": "tests/unit/vendored/botocore/test_regions.py",
    "content": "# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"). You\n# may not use this file except in compliance with the License. A copy of\n# the License is located at\n#\n# http://aws.amazon.com/apache2.0/\n#\n# or in the \"license\" file accompanying this file. This file is\n# distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n# ANY KIND, either express or implied. See the License for the specific\n# language governing permissions and limitations under the License.\n\n\"\"\"\nThis file was 'vendored' from botocore core\nbotocore/tests/unit/test_regions.py from commit\n0c55d6c3f900fc856e818f06b31c22c6dbc56788.\n\nThe vendoring/duplication was due to the concern of utilizing a unexposed class\ninternal to the botocore library for functionality necessary to implicitly\nsupport partitions within the chalice microframework. More specifically the\ndetermination of the dns suffix for service endpoints based on service and\nregion.\n\"\"\"\n\nimport pytest\nfrom botocore.exceptions import NoRegionError\n\nfrom chalice.vendored.botocore import regions\n\n\n@pytest.fixture\ndef endpoints_template():\n    return {\n        'partitions': [\n            {\n                'partition': 'aws',\n                'dnsSuffix': 'amazonaws.com',\n                'regionRegex': r'^(us|eu)\\-\\w+$',\n                'defaults': {\n                    'hostname': '{service}.{region}.{dnsSuffix}'\n                },\n                'regions': {\n                    'us-foo': {'regionName': 'a'},\n                    'us-bar': {'regionName': 'b'},\n                    'eu-baz': {'regionName': 'd'}\n                },\n                'services': {\n                    'ec2': {\n                        'endpoints': {\n                            'us-foo': {},\n                            'us-bar': {},\n                            'eu-baz': {},\n                            'd': {}\n                        }\n                    },\n                    's3': {\n                        'defaults': {\n                            'sslCommonName': '{service}.{region}.{dnsSuffix}'\n                        },\n                        'endpoints': {\n                            'us-foo': {\n                                'sslCommonName':\n                                    '{region}.{service}.{dnsSuffix}'\n                            },\n                            'us-bar': {},\n                            'eu-baz': {'hostname': 'foo'}\n                        }\n                    },\n                    'not-regionalized': {\n                        'isRegionalized': False,\n                        'partitionEndpoint': 'aws',\n                        'endpoints': {\n                            'aws': {'hostname': 'not-regionalized'},\n                            'us-foo': {},\n                            'eu-baz': {}\n                        }\n                    },\n                    'non-partition': {\n                        'partitionEndpoint': 'aws',\n                        'endpoints': {\n                            'aws': {'hostname': 'host'},\n                            'us-foo': {}\n                        }\n                    },\n                    'merge': {\n                        'defaults': {\n                            'signatureVersions': ['v2'],\n                            'protocols': ['http']\n                        },\n                        'endpoints': {\n                            'us-foo': {'signatureVersions': ['v4']},\n                            'us-bar': {'protocols': ['https']}\n                        }\n                    }\n                }\n            },\n            {\n                'partition': 'foo',\n                'dnsSuffix': 'foo.com',\n                'regionRegex': r'^(foo)\\-\\w+$',\n                'defaults': {\n                    'hostname': '{service}.{region}.{dnsSuffix}',\n                    'protocols': ['http'],\n                    'foo': 'bar'\n                },\n                'regions': {\n                    'foo-1': {'regionName': '1'},\n                    'foo-2': {'regionName': '2'},\n                    'foo-3': {'regionName': '3'}\n                },\n                'services': {\n                    'ec2': {\n                        'endpoints': {\n                            'foo-1': {\n                                'foo': 'baz'\n                            },\n                            'foo-2': {},\n                            'foo-3': {}\n                        }\n                    }\n                }\n            }\n        ]\n    }\n\n\ndef test_ensures_region_is_not_none(endpoints_template):\n    with pytest.raises(NoRegionError):\n        resolver = regions.EndpointResolver(endpoints_template)\n        resolver.construct_endpoint('foo', None)\n\n\ndef test_ensures_required_keys_present(endpoints_template):\n    with pytest.raises(ValueError):\n        regions.EndpointResolver({})\n\n\ndef test_returns_empty_list_when_listing_for_different_partition(\n        endpoints_template):\n    resolver = regions.EndpointResolver(endpoints_template)\n    assert resolver.get_available_endpoints('ec2', 'bar') == []\n\n\ndef test_returns_empty_list_when_no_service_found(endpoints_template):\n    resolver = regions.EndpointResolver(endpoints_template)\n    assert resolver.get_available_endpoints('what?') == []\n\n\ndef test_gets_endpoint_names(endpoints_template):\n    resolver = regions.EndpointResolver(endpoints_template)\n    result = resolver.get_available_endpoints('ec2', allow_non_regional=True)\n    assert sorted(result) == ['d', 'eu-baz', 'us-bar', 'us-foo']\n\n\ndef test_gets_endpoint_names_for_partition(endpoints_template):\n    resolver = regions.EndpointResolver(endpoints_template)\n    result = resolver.get_available_endpoints(\n        'ec2', allow_non_regional=True, partition_name='foo')\n    assert sorted(result) == ['foo-1', 'foo-2', 'foo-3']\n\n\ndef test_list_regional_endpoints_only(endpoints_template):\n    resolver = regions.EndpointResolver(endpoints_template)\n    result = resolver.get_available_endpoints(\n        'ec2', allow_non_regional=False)\n    assert sorted(result) == ['eu-baz', 'us-bar', 'us-foo']\n\n\ndef test_returns_none_when_no_match(endpoints_template):\n    resolver = regions.EndpointResolver(endpoints_template)\n    assert resolver.construct_endpoint('foo', 'baz') is None\n\n\ndef test_constructs_regionalized_endpoints_for_exact_matches(\n        endpoints_template):\n    resolver = regions.EndpointResolver(endpoints_template)\n    result = resolver.construct_endpoint('not-regionalized', 'eu-baz')\n    assert result['hostname'] == 'not-regionalized.eu-baz.amazonaws.com'\n    assert result['partition'] == 'aws'\n    assert result['endpointName'] == 'eu-baz'\n\n\ndef test_constructs_partition_endpoints_for_real_partition_region(\n        endpoints_template):\n    resolver = regions.EndpointResolver(endpoints_template)\n    result = resolver.construct_endpoint('not-regionalized', 'us-bar')\n    assert result['hostname'] == 'not-regionalized'\n    assert result['partition'] == 'aws'\n    assert result['endpointName'] == 'aws'\n\n\ndef test_constructs_partition_endpoints_for_regex_match(endpoints_template):\n    resolver = regions.EndpointResolver(endpoints_template)\n    result = resolver.construct_endpoint('not-regionalized', 'us-abc')\n    assert result['hostname'] == 'not-regionalized'\n\n\ndef test_constructs_endpoints_for_regionalized_regex_match(endpoints_template):\n    resolver = regions.EndpointResolver(endpoints_template)\n    result = resolver.construct_endpoint('s3', 'us-abc')\n    assert result['hostname'] == 's3.us-abc.amazonaws.com'\n\n\ndef test_constructs_endpoints_for_unknown_service_but_known_region(\n        endpoints_template):\n    resolver = regions.EndpointResolver(endpoints_template)\n    result = resolver.construct_endpoint('unknown', 'us-foo')\n    assert result['hostname'] == 'unknown.us-foo.amazonaws.com'\n\n\ndef test_merges_service_keys(endpoints_template):\n    resolver = regions.EndpointResolver(endpoints_template)\n    us_foo = resolver.construct_endpoint('merge', 'us-foo')\n    us_bar = resolver.construct_endpoint('merge', 'us-bar')\n    assert us_foo['protocols'] == ['http']\n    assert us_foo['signatureVersions'] == ['v4']\n    assert us_bar['protocols'] == ['https']\n    assert us_bar['signatureVersions'] == ['v2']\n\n\ndef test_merges_partition_default_keys_with_no_overwrite(endpoints_template):\n    resolver = regions.EndpointResolver(endpoints_template)\n    resolved = resolver.construct_endpoint('ec2', 'foo-1')\n    assert resolved['foo'] == 'baz'\n    assert resolved['protocols'] == ['http']\n\n\ndef test_merges_partition_default_keys_with_overwrite(endpoints_template):\n    resolver = regions.EndpointResolver(endpoints_template)\n    resolved = resolver.construct_endpoint('ec2', 'foo-2')\n    assert resolved['foo'] == 'bar'\n    assert resolved['protocols'] == ['http']\n\n\ndef test_gives_hostname_and_common_name_unaltered(endpoints_template):\n    resolver = regions.EndpointResolver(endpoints_template)\n    result = resolver.construct_endpoint('s3', 'eu-baz')\n    assert result['sslCommonName'] == 's3.eu-baz.amazonaws.com'\n    assert result['hostname'] == 'foo'\n\n\ndef tests_uses_partition_endpoint_when_no_region_provided(endpoints_template):\n    resolver = regions.EndpointResolver(endpoints_template)\n    result = resolver.construct_endpoint('not-regionalized')\n    assert result['hostname'] == 'not-regionalized'\n    assert result['endpointName'] == 'aws'\n\n\ndef test_returns_dns_suffix_if_available(endpoints_template):\n    resolver = regions.EndpointResolver(endpoints_template)\n    result = resolver.construct_endpoint('not-regionalized')\n    assert result['dnsSuffix'] == 'amazonaws.com'\n"
  }
]