# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['idempotency_header_middleware', 'idempotency_header_middleware.backends']

package_data = \
{'': ['*']}

extras_require = \
{'all': ['fastapi>=0.70.0,<0.71.0', 'starlette', 'redis>=4.2,<5.0', 'lupa'],
 'fastapi': ['fastapi>=0.70.0,<0.71.0'],
 'redis': ['redis>=4.2,<5.0', 'lupa'],
 'starlette': ['starlette']}

setup_kwargs = {
    'name': 'asgi-idempotency-header',
    'version': '0.2.0rc1',
    'description': 'Enable idempotent operations for your endpoints.',
    'long_description': '[![tests](https://github.com/sondrelg/asgi-idempotency-header/actions/workflows/test.yml/badge.svg)](https://github.com/sondrelg/asgi-idempotency-header/actions/workflows/test.yml)\n[![pypi](https://img.shields.io/pypi/v/asgi-idempotency-header.svg)](https://pypi.org/project/drf-openapi-tester/)\n[![python-versions](https://img.shields.io/badge/python-3.8%2B-blue)](https://pypi.org/project/asgi-idempotency-header)\n[![codecov](https://codecov.io/gh/sondrelg/asgi-idempotency-header/branch/main/graph/badge.svg?token=UOJTCSY8H7)](https://codecov.io/gh/sondrelg/asgi-idempotency-header)\n\n# Idempotency Header ASGI Middleware\n\nA middleware for making endpoints idempotent.\n\nThe purpose of the middleware is to guarantee that execution of mutating endpoints happens exactly once,\nregardless of the number of requests.\nWe achieve this by caching responses, and returning already-saved responses to the user on repeated requests.\nResponses are only cached when an idempotency-key HTTP header is present, so clients must opt-into this behaviour.\nAdditionally, only configured HTTP methods (by default, `POST` and `PATCH`) that return JSON payloads are cached and replayed.\n\nThis is largely modelled after [stripe\' implementation](https://stripe.com/docs/api/idempotent_requests).\n\nThe middleware is compatible with both [Starlette](https://github.com/encode/starlette)\nand [FastAPI](https://github.com/tiangolo/fastapi) apps.\n\n## Installation\n\n```\npip install asgi-idempotency-header\n```\n\n## Setup\n\nAdd the middleware to your app like this:\n\n```python\nfrom fastapi import FastAPI\n\nfrom idempotency_header_middleware import IdempotencyHeaderMiddleware\nfrom idempotency_header_middleware.backends import RedisBackend\n\n\nbackend = RedisBackend(redis=redis)\n\napp = FastAPI()\napp.add_middleware(IdempotencyHeaderMiddleware(backend=backend))\n```\n\nor like this:\n\n```python\nfrom fastapi import FastAPI\nfrom fastapi.middleware import Middleware\n\nfrom idempotency_header_middleware import IdempotencyHeaderMiddleware\nfrom idempotency_header_middleware.backends import RedisBackend\n\n\nbackend = RedisBackend(redis=redis)\n\napp = FastAPI(\n    middleware=[\n        Middleware(\n            IdempotencyHeaderMiddleware,\n            backend=backend,\n        )\n    ]\n)\n```\n\nIf you\'re using `Starlette`, just substitute `FastAPI` for `Starlette` and it should work the same.\n\n## Configuration\n\nThe middleware takes a few arguments. A full example looks like this:\n\n```python\nfrom aioredis import from_url\n\nfrom idempotency_header_middleware import IdempotencyHeaderMiddleware\nfrom idempotency_header_middleware.backends import RedisBackend\n\n\nredis = from_url(redis_url)\nbackend = RedisBackend(redis=redis)\n\nIdempotencyHeaderMiddleware(\n    backend,\n    idempotency_header_key=\'Idempotency-Key\',\n    replay_header_key=\'Idempotent-Replayed\',\n    enforce_uuid4_formatting=False,\n    expiry=60 * 60 * 24,\n    applicable_methods=[\'POST\', \'PATCH\']\n)\n```\n\nThe following section describes each argument:\n\n### Backend\n\n```python\nfrom idempotency_header_middleware.backends import RedisBackend, MemoryBackend\n\nbackend: Union[RedisBackend, MemoryBackend]\n```\n\nThe backend is the only required argument, as it defines **how** and **where** to store a response.\n\nThe package comes with an [aioredis](https://github.com/aio-libs/aioredis-py) backend implementation, and a\nmemory-backend for testing.\n\nContributions for more backends are welcomed, and configuring a custom backend is pretty simple - just take a look at\nthe existing ones.\n\n### Idempotency header key\n\n```python\nidempotency_header_key: str = \'Idempotency-Key\'\n```\n\nThe idempotency header key is the header value to check for. When present, the middleware will be used if the HTTP\nmethod is in the [applicable methods](#applicable-methods).\n\nThe default value is `"Idempotency-Key"`, but it can be defined as any string.\n\n### Replay header key\n\n```python\nreplay_header_key: str = \'Idempotent-Replayed\'\n```\n\nThe replay header is added to replayed responses. It provides a way for the client to tell whether the action was\nperformed for the first time or not.\n\n### Enforce UUID formatting\n\n```python\nenforce_uuid4_formatting: bool = False\n```\n\nConvenience option for stricter header value validation.\n\nClients can technically set any value they want in their header,\nbut the shorter the key value is, the higher the risk of value-collisions is from other users.\nIf two users accidentally send in the same header value for what\'s meant to be two separate requests,\nthe middleware will interpret them as the same.\n\nBy enabling this option, you can force users to use UUIDs as header values, and pretty much eliminate this risk.\n\nWhen validation fails, a 422 response is returned from the middleware, informing the user that the header value is malformed.\n\n### Expiry\n\n```python\nexpiry: int = 60 * 60 * 24\n```\n\nHow long to cache responses for, measured in seconds. Set to 24 hours by default.\n\n### Applicable Methods\n\n```python\napplicable_methods=[\'POST\', \'PATCH\']\n```\n\nWhat HTTP methods to consider for idempotency. If the request method is one of the methods in this list, and the\n[idempotency header](#idempotency-header-key) is sent, the middleware will be used. By default, only `POST`\nand `PATCH` methods are cached and replayed.\n\n## Quick summary of behaviours\n\nBriefly summarized, this is how the middleware functions:\n\n- The first request is processed, and consequent requests are replayed, until the response expires.\n  `expiry` *can* be set to `None` to skip expiry, but most likely you will want to expire responses\n  after a while.\n- If two requests comes in at the same time - i.e., if a second request hits the middlware *before*\n  the first request has finished, the middleware will return a 409, informing the user that a request\n  is being processed, and that we cannot handle the second request.\n- The middleware only handles HTTP requests.\n- By default, the middleware only handles requests with `POST` and `PATCH` methods. Other HTTP methods skip this middleware.\n- Only valid JSON responses with `content-type` == `application/json` are cached.\n',
    'author': 'Sondre Lillebø Gundersen',
    'author_email': 'sondrelg@live.no',
    'maintainer': 'Patrick Gleeson',
    'maintainer_email': 'pgleeson@spoton.com',
    'url': 'https://github.com/snok/asgi-idempotency-header',
    'packages': packages,
    'package_data': package_data,
    'extras_require': extras_require,
    'python_requires': '>=3.8,<4.0',
}


setup(**setup_kwargs)
