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

packages = \
['symmetric']

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

install_requires = \
['flask>=1.1.1,<2.0.0']

entry_points = \
{'console_scripts': ['symmetric = symmetric.command_line_interface:dispatcher']}

setup_kwargs = {
    'name': 'symmetric',
    'version': '2.1.1',
    'description': 'A powerful yet lean wrapper over Flask to massively speed up API creations and enable super fast module-to-API transformations.',
    'long_description': '# Symmetric\n\n![PyPI - Downloads](https://img.shields.io/pypi/dm/symmetric?style=for-the-badge&logo=python&color=306998)\n\nA powerful yet lean wrapper over **[Flask](https://github.com/pallets/flask)** to massively speed up **[API](https://en.wikipedia.org/wiki/Web_API)** creations and enable super fast module-to-**[API](https://en.wikipedia.org/wiki/Web_API)** transformations.\n\n![GitHub Workflow Status](https://img.shields.io/github/workflow/status/daleal/symmetric/tests?label=tests&logo=github&style=for-the-badge)\n![GitHub Workflow Status](https://img.shields.io/github/workflow/status/daleal/symmetric/linters?label=linters&logo=github&style=for-the-badge)\n\n## Why Symmetric?\n\nRaw developing speed and ease of use, that\'s why. While `Flask` is a powerful tool to have, getting it to work from scratch can be a bit of a pain, especially if you have never used it before. The idea behind `symmetric` is to be able to take any module **already written** and transform it into a working API in a matter of minutes, instead of having to design the module ground-up to work with `Flask` (it can also be used to build an API from scratch really fast). With `symmetric`, you will also get some neat features, namely:\n\n- Auto logging.\n- Server-side error detection and exception handling.\n- Native support for an authentication token on a per-endpoint basis.\n- Auto-generated documentation for your API.\n\n## Installing\n\nInstall using pip!\n\n```bash\npip install symmetric\n```\n\n## Usage\n\n### Running the development server\n\nTo start the development server, just run:\n\n```bash\nsymmetric run <module>\n```\n\nWhere `<module>` is your module name (in the examples, we will be writing in a file named `module.py`, so the module name will be just `module`). A `Flask` instance will be spawned immediately and can be reached at [http://127.0.0.1:5000](http://127.0.0.1:5000) by default. We don\'t have any endpoints yet, so we\'ll add some later. **Do not use this in production**. The `Flask` server is meant for development only. Instead, you can use any `WSGI` server to run the API. For example, to run the API using [gunicorn](https://gunicorn.org/), you just need to run `gunicorn module:symmetric` and a production ready server will be spawned.\n\n### Defining the API endpoints\n\nThe module consists of a main object called `symmetric`, which includes an important element: the `router` decorator. Let\'s analyze it:\n\n```py\nfrom symmetric import symmetric\n\n@symmetric.router("/some-route", methods=["get"], response_code=200, auth_token=False)\n```\n\nThe decorator recieves 4 arguments: the `route` argument (the endpoint of the API to which the decorated function will map), the `methods` argument (a list of the methods accepted to connect to that endpoint, defaults in only `GET` requests), the `response_code` argument (the response code of the endpoint if everything goes according to the plan. Defaults to `200`) and the `auth_token` argument (a boolean stating if the endpoint requires authentication using a `symmetric` token. Defaults to `False`).\n\nNow let\'s imagine that we have the following method:\n\n```py\ndef some_function():\n    """Greets the world."""\n    return "Hello World!"\n```\n\nTo transform that method into an API endpoint, all you need to do is add one line:\n\n```py\n@symmetric.router("/sample")\ndef some_function():\n    """Greets the world."""\n    return "Hello World!"\n```\n\nRun `symmetric run module` and send a `GET` request to `http://127.0.0.1:5000/sample`. You should get a `Hello World!` in response! (To try it with a browser, make sure to run the above command and click [this link](http://127.0.0.1:5000/sample)).\n\nBut what about methods with arguments? Of course they can be API\'d too! Let\'s now say that you have the following function:\n\n```py\ndef another_function(a, b=372):\n    """\n    Adds :a and :b and returns the result of\n    that operation.\n    """\n    return a + b\n```\n\nTo transform that method into an API endpoint, all you need to do, again, is add one line:\n\n```py\n@symmetric.router("/add")\ndef another_function(a, b=372):\n    """\n    Adds :a and :b and returns the result of\n    that operation.\n    """\n    return a + b\n```\n\n### Querying API endpoints\n\nTo give parameters to a function, all we need to do is send a `json` body with the names of the parameters as keys. Let\'s see how! Run `symmetric run module` and send a `GET` request to `http://127.0.0.1:5000/add`, now using the `requests` module.\n\n```python\nimport requests\n\npayload = {\n    "a": 48,\n    "b": 21\n}\nresponse = requests.get("http://127.0.0.1:5000/add", json=payload)\nprint(response.json())\n```\n\nWe got a `69` response! (`48 + 21 = 69`). Of course, you can return dictionaries from your methods and those will get returned as a `json` body in the response object **automagically**!\n\nWith this in mind, you can transform any existing project into a usable API very quickly!\n\n### The `symmetric` token authentication\n\nTo speed up your API creation even more, `symmetric` includes native support for a simple token authentication. It works like this:\n\n1. **Set up the token in the server.**\n\n    In the environment where your API is going to run, add an environmental variable named `SYMMETRIC_API_KEY` and set its value to be the _[pre-shared](https://en.wikipedia.org/wiki/Pre-shared_key)_ token. If you don\'t set the environmental key, the _default_ `SYMMETRIC_API_KEY` value will be `symmetric_token` (in your development environment that\'s probably fine, but in the production server you should **never** use the default value of the `symmetric` token).\n\n2. **Force one of your endpoints to use an authentication token.**\n\n    Let\'s say your module has a method like this:\n\n    ```py\n    def secret_function():\n        """Greets the world (secretly)."""\n        return "Hello World in secret!"\n    ```\n\n    Add the `symmetric` router decorator in the following manner:\n\n    ```py\n    @symmetric.router("/secret", auth_token=True)\n    def secret_function():\n        """Greets the world (secretly)."""\n        return "Hello World in secret!"\n    ```\n\n    Now, your endpoint won\'t respond to any request that is not correctly authenticated.\n\n3. **Query your endpoint.**\n\n    To query your endpoint, the request body must include a key named `symmetric_api_key` with a value to match the one of the environment\'s `SYMMETRIC_API_KEY`. So, for instance, if you are using the default `SYMMETRIC_API_KEY` value (`symmetric_token`), the request body for the `/secrets` endpoint should be:\n\n    ```py\n    payload = {\n        "symmetric_api_key": "symmetric_token"\n    }\n    ```\n\n    By sending that payload in the request body as a `json`, the request can access the endpoint correctly.\n\n### Auto-generating the API documentation\n\nGenerating API documentation is simple with `symmetric`. Just run the following command:\n\n```bash\nsymmetric docs <module>\n```\n\nThis will **automagically** generate a markdown file documenting each endpoint with the function docstring, required arguments and `HTTP` methods. Seems too simple to be true, right? Go ahead, try it yourself!\n\nYou can also specify the name of the documentation file (defaults to `documentation.md`) using the `-f` or the `--filename` flag.\n\n### The whole example\n\nTo sum up, if the original `module.py` file looked like this before `symmetric`:\n\n```py\ndef some_function():\n    """Greets the world."""\n    return "Hello World!"\n\n\ndef another_function(a, b=372):\n    """\n    Adds :a and :b and returns the result of\n    that operation.\n    """\n    return a + b\n\n\ndef secret_function():\n    """Greets the world (secretly)."""\n    return "Hello World in secret!"\n```\n\nThe complete final `module.py` file with `symmetric` should look like this:\n\n```py\nfrom symmetric import symmetric\n\n\n@symmetric.router("/sample")\ndef some_function():\n    """Greets the world."""\n    return "Hello World!"\n\n\n@symmetric.router("/add")\ndef another_function(a, b=372):\n    """\n    Adds :a and :b and returns the result of\n    that operation.\n    """\n    return a + b\n\n\n@symmetric.router("/secret", auth_token=True)\ndef secret_function():\n    """Greets the world (secretly)."""\n    return "Hello World in secret!"\n```\n\nTo run the server, just run `symmetric run module`. Now, you can send `GET` requests to `http://127.0.0.1:5000/sample`, `http://127.0.0.1:5000/add` and `http://127.0.0.1:5000/secret`. Here is a simple file to get you started querying your API:\n\n```py\nimport requests\n\n\ndef call_sample():\n    response = requests.get("http://127.0.0.1:5000/sample")\n    return response.text\n\n\ndef call_add():\n    payload = {\n        "a": 48,\n        "b": 21\n    }\n    response = requests.get("http://127.0.0.1:5000/add", json=payload)\n    return response.json()\n\n\ndef call_secret():\n    payload = {\n        "symmetric_api_key": "symmetric_token"\n    }\n    response = requests.get("http://127.0.0.1:5000/secret", json=payload)\n    return response.text\n\n\nif __name__ == \'__main__\':\n    print(call_sample())\n    print(call_add())\n    print(call_secret())\n```\n\nRunning `symmetric docs module` would result in a file `documentation.md` being created with the following content:\n\n``````pandoc\n# Module API Documentation\n\nEndpoints that require an authentication token should send it in a key named `symmetric_api_key` inside the request body.\n\n## `/add`\n\n### Description\n\nAdds :a and :b and returns the result of\nthat operation.\n\n### Metadata\n\n`HTTP` methods accepted: `GET`\n\nDoes not require an authentication token.\n\n### Parameters\n\n```py\n{\n    a,\n    b,  # defaults to 372\n}\n```\n\n## `/sample`\n\n### Description\n\nGreets the world.\n\n### Metadata\n\n`HTTP` methods accepted: `GET`\n\nDoes not require an authentication token.\n\n### Parameters\n\nNo required parameters.\n\n## `/secret`\n\n### Description\n\nGreets the world (secretly).\n\n### Metadata\n\n`HTTP` methods accepted: `GET`\n\nRequires an authentication token.\n\n### Parameters\n\nNo required parameters.\n\n``````\n\n## Logging\n\nBy default, the logs in the server will be written into the `stdout` and into a file named `symmetric.log`. You can change the name of the file by specifying the `LOG_FILE` environmental variable, if you want to.\n\n## Route rules\n\nThere are some rules regarding the correct routes that can be used. Failing to follow the `symmetric` route rules will result in the API not being run and an error being thrown and logged. To follow the rules, a route:\n\n1. **Can\'t** be defined twice.\n2. **Can\'t** have repetitions of `/`, `-` or `_`.\n3. **Can\'t** have concatenations of `/` with `-` or of `_` with `-`.\n4. **Can\'t** include characters other than letters (uppercase and/or lowercase), `/`, `-` and `_`.\n5. **Can\'t** end with a `/`, a `-` or a `_`. The **only** exception of this rule is when the route is just `/`, in which case, it **can** end with `/`.\n6. **Must** start with a `/`.\n\nHere are some examples.\n\n### Correct route patterns\n\n- `/`\n- `/symmetric`\n- `/hi/hello`\n- `/hello-world/basic_syntax`\n- `/_element/BIGelement`\n\n### Incorrect route patterns\n\n- `/hi//hello`\n- `element`\n- `/another-element/`\n- `/bad-_composition`\n- `/-worse`\n- `/element__two`\n- `/element2`\n- `/oof-number-one-`\n- `/oof_number_two_`\n\n## Developing\n\nClone the repository:\n\n```bash\ngit clone https://github.com/daleal/symmetric.git\n\ncd symmetric\n```\n\nRecreate environment:\n\n```bash\n./environment.sh\n\n. .venv/bin/activate\n```\n\nTest install:\n\n```bash\npoetry install  # will also install the symmetric CLI\n```\n\nRun the tests:\n\n```bash\npython -m unittest\n```\n\nBuild the project:\n\n```bash\npoetry build\n```\n\nPush to `TestPyPi`:\n\n```bash\npoetry config repositories.testpypi https://test.pypi.org/legacy/\npoetry publish -r testpypi\n```\n\nDownload from `TestPyPi`:\n\n```bash\ndeactivate\nrm -rf .testing-venv\npython3 -m venv .testing-venv\n. .testing-venv/bin/activate\npip install click\npython -m pip install --index-url https://test.pypi.org/simple/ symmetric\n```\n\nPush to `PyPi`:\n\n```bash\npoetry publish\n```\n',
    'author': 'Daniel Leal',
    'author_email': 'dlleal@uc.cl',
    'maintainer': 'Daniel Leal',
    'maintainer_email': 'dlleal@uc.cl',
    'url': 'https://github.com/daleal/symmetric',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'entry_points': entry_points,
    'python_requires': '>=3.6,<4.0',
}


setup(**setup_kwargs)
