├── {{cookiecutter.repo_name}} ├── tests │ ├── requirements.txt │ ├── __init__.py │ └── test_api.py ├── setup.cfg ├── MANIFEST.in ├── {{cookiecutter.package_name}} │ ├── api │ │ ├── __init__.py │ │ ├── v1.py │ │ ├── parsers.py │ │ └── sample.py │ ├── config.py │ ├── __init__.py │ ├── core.py │ ├── error_handlers.py │ └── server.py ├── requirements.txt ├── .gitignore ├── setup.py ├── README.md └── LICENSE ├── LICENSE ├── cookiecutter.json ├── .gitignore └── README.md /{{cookiecutter.repo_name}}/tests/requirements.txt: -------------------------------------------------------------------------------- 1 | pytest -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include requirements.txt 3 | include tests/requirements.txt -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright {% now 'local', '%Y' %} {{ cookiecutter.author }} 2 | # See LICENSE for details. 3 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/api/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright {% now 'local', '%Y' %} {{ cookiecutter.author }} 2 | # See LICENSE for details. 3 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/config.py: -------------------------------------------------------------------------------- 1 | # Copyright {% now 'local', '%Y' %} {{ cookiecutter.author }} 2 | # See LICENSE for details. 3 | 4 | RESTPLUS_MASK_SWAGGER = False 5 | 6 | ERROR_400_HELP = False 7 | ERROR_404_HELP = False 8 | ERROR_500_HELP = False 9 | 10 | FLASK_DEBUG = False -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/api/v1.py: -------------------------------------------------------------------------------- 1 | # Copyright {% now 'local', '%Y' %} {{ cookiecutter.author }} 2 | # See LICENSE for details. 3 | 4 | from flask_restx import Api 5 | 6 | api = Api(version='{{ cookiecutter.version }}', 7 | title='{{ cookiecutter.package_name }}', 8 | description='{{ cookiecutter.project_description }}') -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright {% now 'local', '%Y' %} {{ cookiecutter.author }} 2 | # See LICENSE for details. 3 | 4 | __author__ = '{{ cookiecutter.author }}, {{ cookiecutter.github_username }} @ GitHub' 5 | __email__ = '{{ cookiecutter.email }}' 6 | __version__ = '{{ cookiecutter.version }}' 7 | __license__ = '{{ cookiecutter.license }}' -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/api/parsers.py: -------------------------------------------------------------------------------- 1 | # Copyright {% now 'local', '%Y' %} {{ cookiecutter.author }} 2 | # See LICENSE for details. 3 | 4 | from flask_restx import reqparse, inputs 5 | 6 | sample_parser = reqparse.RequestParser() 7 | 8 | sample_parser.add_argument( 9 | 'test', type=inputs.boolean, help='This is just a test parameter', 10 | required=False 11 | ) 12 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask>=1.1.1 2 | flask_restx>=0.2.0 3 | setuptools>=41.1.0 4 | pyopenssl>=19.1.0 5 | click>=7.1.2 6 | {%- if cookiecutter.flask_cache|lower == 'yes' %} 7 | Flask_Caching>=1.7.2 8 | {%- endif %} 9 | {%- if cookiecutter.flask_limiter|lower == 'yes' %} 10 | Flask_Limiter>=1.0.1 11 | {%- endif %} 12 | {%- if cookiecutter.flask_cors|lower == 'yes' %} 13 | Flask_Cors>=3.0.8 14 | {%- endif %} -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/tests/test_api.py: -------------------------------------------------------------------------------- 1 | # Copyright {% now 'local', '%Y' %} {{ cookiecutter.author }} 2 | # See LICENSE for details. 3 | 4 | import pytest 5 | 6 | from {{ cookiecutter.package_name }}.server import app 7 | 8 | 9 | @pytest.fixture 10 | def client(): 11 | client = app.test_client() 12 | yield client 13 | 14 | 15 | def test_api(client): 16 | response = client.get('/v1/') 17 | assert response._status_code == 200 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2020 Alvaro Bartolome 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/core.py: -------------------------------------------------------------------------------- 1 | # Copyright {% now 'local', '%Y' %} {{ cookiecutter.author }} 2 | # See LICENSE for details. 3 | {% if cookiecutter.flask_cache|lower == 'yes' %} 4 | from flask_caching import Cache 5 | 6 | cache = Cache( 7 | config={ 8 | 'CACHE_TYPE': 'simple' 9 | } 10 | ) 11 | {%- endif %} 12 | {%- if cookiecutter.flask_limiter|lower == 'yes' %} 13 | 14 | from flask_limiter import Limiter 15 | from flask_limiter.util import get_remote_address 16 | 17 | limiter = Limiter( 18 | key_func=get_remote_address, 19 | default_limits=["100 per hour"] 20 | ) 21 | {%- endif %} -------------------------------------------------------------------------------- /cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Alvaro Bartolome del Canto", 3 | "email": "alvarobartt@example.com", 4 | "github_username": "alvarobartt", 5 | "project_name": "Flask Restx API", 6 | "project_description": "This project is a sample Python Flask Restx API", 7 | "repo_name": "{{ cookiecutter.project_name.lower().replace(' ', '-') }}", 8 | "package_name": "{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}", 9 | "pypi_username": "{{ cookiecutter.github_username }}", 10 | "version": "1", 11 | "flask_limiter": "yes", 12 | "flask_cache": "yes", 13 | "flask_cors": "yes", 14 | "license": [ 15 | "MIT License", "BSD License", "ISC License", 16 | "Apache Software License 2.0", "GNU General Public License v3", 17 | "WTFPL License", "None" 18 | ] 19 | } -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/error_handlers.py: -------------------------------------------------------------------------------- 1 | # Copyright {% now 'local', '%Y' %} {{ cookiecutter.author }} 2 | # See LICENSE for details. 3 | 4 | def handle400error(namespace, error_desc): 5 | """ 6 | This function handles & formats the introduced error_desc into a HTTP response which will be returned in 7 | case of exception raising, as an abort() method from the specified namespace. HTTP 400 error code stands for 8 | Bad Request, which means that the request parameters were not properly sent in the GET request. 9 | """ 10 | 11 | return namespace.abort(400, status=error_desc, statusCode="400") 12 | 13 | 14 | def handle404error(namespace, error_desc): 15 | """ 16 | This function handles & formats the introduced error_desc into a HTTP response which will be returned in 17 | case of exception raising, as an abort() method from the specified namespace. HTTP 404 error code stands for 18 | Not Found, which means that the received request could not be resolved. 19 | """ 20 | 21 | return namespace.abort(404, status=error_desc, statusCode="404") 22 | 23 | 24 | def handle500error(namespace): 25 | """ 26 | This function handles & formats unknown errors into a HTTP response which will be returned in 27 | case of exception raising, as an abort() method from the specified namespace. HTTP 500 error code stands for 28 | Internal Server Error, which encompasses all the unhandled errors. 29 | """ 30 | 31 | error_desc = "Unknown error, please contact API administrator: {{ cookiecutter.email }}, {{ cookiecutter.github_username }} @ GitHub." 32 | 33 | return namespace.abort(500, status=error_desc, statusCode="500") 34 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/api/sample.py: -------------------------------------------------------------------------------- 1 | # Copyright {% now 'local', '%Y' %} {{ cookiecutter.author }} 2 | # See LICENSE for details. 3 | 4 | from flask_restx import Resource 5 | 6 | from .parsers import sample_parser 7 | 8 | from ..server import api 9 | 10 | {% if cookiecutter.flask_limiter|lower == 'yes' %} 11 | from ..core import limiter 12 | {%- endif %} 13 | {%- if cookiecutter.flask_cache|lower == 'yes' %} 14 | from ..core import cache 15 | {%- endif %} 16 | 17 | from ..error_handlers import handle400error, handle404error, handle500error 18 | 19 | sample_ns = api.namespace('sample', description='{{ cookiecutter.package_name }} - This is a sample namespace') 20 | 21 | 22 | @sample_ns.route('/data') 23 | class SampleData(Resource): 24 | 25 | @api.expect(sample_parser) 26 | @api.response(400, 'Invalid parameters') 27 | @api.response(404, 'Data not found') 28 | @api.response(500, 'Unhandled errors') 29 | {%- if cookiecutter.flask_limiter|lower == 'yes' %} 30 | @limiter.limit('100/hour') 31 | {%- endif %} 32 | {%- if cookiecutter.flask_cache|lower == 'yes' %} 33 | @cache.cached(timeout=120, query_string=True) 34 | {%- endif %} 35 | def get(self): 36 | """ 37 | This is a sample HTTP GET method 38 | """ 39 | 40 | args = sample_parser.parse_args() 41 | 42 | return args 43 | 44 | @api.expect(sample_parser) 45 | @api.response(400, 'Invalid parameters') 46 | @api.response(404, 'Data not found') 47 | @api.response(500, 'Unhandled errors') 48 | {%- if cookiecutter.flask_limiter|lower == 'yes' %} 49 | @limiter.limit('100/hour') 50 | {%- endif %} 51 | {%- if cookiecutter.flask_cache|lower == 'yes' %} 52 | @cache.memoize(timeout=120) 53 | {%- endif %} 54 | def post(self): 55 | """ 56 | This is a sample HTTP POST method 57 | """ 58 | 59 | args = sample_parser.parse_args() 60 | 61 | return args 62 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/server.py: -------------------------------------------------------------------------------- 1 | # Copyright {% now 'local', '%Y' %} {{ cookiecutter.author }} 2 | # See LICENSE for details. 3 | 4 | from flask import Flask, Blueprint 5 | 6 | import click 7 | 8 | import re 9 | import pkg_resources 10 | 11 | from .__init__ import __author__, __version__, __license__ 12 | 13 | from . import config 14 | {% if cookiecutter.flask_limiter|lower == 'yes' %} 15 | from .core import limiter 16 | {%- endif %} 17 | {%- if cookiecutter.flask_cache|lower == 'yes' %} 18 | from .core import cache 19 | {%- endif %} 20 | {% if cookiecutter.flask_cors|lower == 'yes' %} 21 | from flask_cors import CORS 22 | {%- endif %} 23 | 24 | from .api.v1 import api 25 | 26 | from .api.sample import sample_ns 27 | 28 | app = Flask('{{ cookiecutter.package_name }}') 29 | 30 | 31 | def configure_app(flask_app): 32 | """This function configures the Flask application with the settings specified in `config.py`.""" 33 | 34 | flask_app.config.from_object(config) 35 | 36 | 37 | def initialize_app(flask_app): 38 | """This function initializes the Flask application, adds the namespace and registers the blueprints.""" 39 | 40 | configure_app(flask_app) 41 | 42 | v1 = Blueprint('api', '{{ cookiecutter.package_name }}', url_prefix='/v1') 43 | api.init_app(v1) 44 | 45 | {% if cookiecutter.flask_limiter|lower == 'yes' %} 46 | limiter.init_app(flask_app) 47 | {%- endif %} 48 | {%- if cookiecutter.flask_cache|lower == 'yes' %} 49 | cache.init_app(flask_app) 50 | {%- endif %} 51 | 52 | api.add_namespace(sample_ns) 53 | 54 | flask_app.register_blueprint(v1) 55 | 56 | 57 | initialize_app(app) 58 | {%- if cookiecutter.flask_cors|lower == 'yes' %} 59 | CORS(app) 60 | {%- endif %} 61 | 62 | 63 | @click.command() 64 | @click.option("--enable-ssl", default=False, is_flag=True, help="Use this argument to enable SSL.") 65 | def run(enable_ssl): 66 | """This function deploys the API in the public IP (0.0.0.0) in the port 5000.""" 67 | 68 | print("=========================================") 69 | print(f"Author: {__author__}") 70 | print(f"Version: {__version__}") 71 | print(f"License: {__license__}") 72 | if enable_ssl is True: 73 | print('SSL is enabled!') 74 | else: 75 | print('SSL is disabled!') 76 | print("=========================================") 77 | 78 | if enable_ssl is True: 79 | app.run(host='0.0.0.0', port='5000', debug=config.FLASK_DEBUG, ssl_context='adhoc') 80 | else: 81 | app.run(host='0.0.0.0', port='5000', debug=config.FLASK_DEBUG) 82 | 83 | 84 | if __name__ == '__main__': 85 | run() 86 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # Visual Studio Code configuration files 132 | .vscode 133 | .vscode/ 134 | .vscode/* 135 | 136 | # PyCharm configuration files 137 | .idea 138 | .idea/ 139 | .idea/* 140 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # Visual Studio Code configuration files 132 | .vscode 133 | .vscode/ 134 | .vscode/* 135 | 136 | # PyCharm configuration files 137 | .idea 138 | .idea/ 139 | .idea/* 140 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright {% now 'local', '%Y' %} {{ cookiecutter.author }} 2 | # See LICENSE for details. 3 | 4 | import io 5 | 6 | from setuptools import setup, find_packages 7 | 8 | 9 | def readme(): 10 | with io.open('README.md', encoding='utf-8') as f: 11 | return f.read() 12 | 13 | def requirements(filename): 14 | reqs = list() 15 | with io.open(filename, encoding='utf-8') as f: 16 | for line in f.readlines(): 17 | reqs.append(line.strip()) 18 | return reqs 19 | 20 | 21 | setup( 22 | name='{{ cookiecutter.package_name }}', 23 | version='{{ cookiecutter.version }}', 24 | packages=find_packages(), 25 | url="https://www.github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }}", 26 | download_url='https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }}/archive/{{ cookiecutter.version }}.tar.gz', 27 | license='{{ cookiecutter.license }}', 28 | author='{{ cookiecutter.author }}', 29 | author_email='{{ cookiecutter.email }}', 30 | description='{{ cookiecutter.project_description }}', 31 | long_description=readme(), 32 | long_description_content_type='text/markdown', 33 | install_requires=requirements(filename='requirements.txt'), 34 | data_files=[], 35 | entry_points={ 36 | 'console_scripts': [ 37 | '{{ cookiecutter.package_name }}={{ cookiecutter.package_name }}.server:run' 38 | ], 39 | }, 40 | include_package_data=True, 41 | classifiers=[ 42 | "Development Status :: 4 - Beta", 43 | "Programming Language :: Python :: 3 :: Only", 44 | "Programming Language :: Python :: 3.6", 45 | "Programming Language :: Python :: 3.7", 46 | "Programming Language :: Python :: 3.8", 47 | {%- if cookiecutter.license == "MIT License" %} 48 | "License :: OSI Approved :: MIT License", 49 | {%- endif %} 50 | {%- if cookiecutter.license == "BSD License" %} 51 | "License :: OSI Approved :: BSD License", 52 | {%- endif %} 53 | {%- if cookiecutter.license == "ISC License" %} 54 | "License :: OSI Approved :: ISC License", 55 | {%- endif %} 56 | {%- if cookiecutter.license == "Apache Software License 2.0" %} 57 | "License :: OSI Approved :: Apache Software License", 58 | {%- endif %} 59 | {%- if cookiecutter.license == "GNU General Public License v3" %} 60 | "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", 61 | {%- endif %} 62 | "Intended Audience :: Developers" 63 | ], 64 | extras_require={ 65 | "tests": requirements(filename='tests/requirements.txt'), 66 | }, 67 | python_requires='>=3.6', 68 | project_urls={ 69 | 'Bug Reports': 'https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }}/issues', 70 | 'Source': 'https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }}' 71 | }, 72 | ) 73 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/README.md: -------------------------------------------------------------------------------- 1 | # {{ cookiecutter.package_name }} - {{ cookiecutter.project_description }} 2 | 3 | ## Introduction 4 | 5 | {{ cookiecutter.project_description }} 6 | 7 | ## Installation 8 | 9 | In order to get this package working you will need to **install it via pip** (with a Python 3.6 version or higher) on the terminal by typing: 10 | 11 | ``$ pip install {{ cookiecutter.package_name }}`` 12 | 13 | If the package is not uploaded to neither PyPI nor Anaconda Cloud, you can easily install it from source as it follows: 14 | 15 | ``` 16 | $ git clone https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }}.git 17 | $ cd {{ cookiecutter.repo_name }} 18 | $ pip install . 19 | ``` 20 | 21 | ## Documentation 22 | 23 | You can find the **complete API documentation** at: https://0.0.0.0:5000/v1, if the default configuration is used (default IP & default Port). Documentation has been automatically generated using [Swagger](https://swagger.io/), since it is completely integrated with **flask-restx**. 24 | 25 | ## Usage 26 | 27 | So as to launch this Flask RESTX API with the default configuration, just use the following command, once the package is properly installed: 28 | 29 | ``` 30 | $ {{ cookiecutter.package_name }} 31 | ``` 32 | 33 | So on, the previous command is the API entry point whose default output will look like: 34 | 35 | ``` 36 | ========================================= 37 | Author: {{ cookiecutter.author }} | {{ cookiecutter.github_username }} @ GitHub 38 | Version: 1 39 | SSL is disabled! 40 | ========================================= 41 | * Serving Flask app "{{ cookiecutter.package_name }}" (lazy loading) 42 | * Environment: production 43 | WARNING: This is a development server. Do not use it in a production deployment. 44 | Use a production WSGI server instead. 45 | * Debug mode: off 46 | * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit) 47 | ``` 48 | 49 | ## Contribute 50 | 51 | As this is an open source project it is **open to contributions, bug reports, bug fixes, documentation improvements, enhancements and ideas**. There is an open tab of [issues](https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }}/issues) where anyone can open new issues if needed or navigate through them in order to solve them or contribute to its solving. Remember that **issues are not threads to describe multiple problems**, this does not mean that issues can't be discussed, but so to keep a structured project management, the same issue should not describe different problems, just the main one and some nested/related errors that may be found. 52 | 53 | ## Citation 54 | 55 | When citing this repository on your publications please use the following **BibTeX** citation: 56 | 57 | ``` 58 | @misc{ 59 | {{ cookiecutter.package_name }}, 60 | author = { {{ cookiecutter.author }} }, 61 | title = { {{ cookiecutter.package_name }} - {{ cookiecutter.project_description }} }, 62 | year = { {% now 'local', '%Y' %} }, 63 | publisher = {GitHub}, 64 | journal = {GitHub Repository}, 65 | howpublished = {\url{https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }}}} 66 | } 67 | ``` 68 | 69 | ### This repository has been generated using [restx-cookie](https://github.com/alvarobartt/restx-cookie) 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Flask-RESTX API as a Python Package Cookie 2 |
3 |
4 | cookie-monster 5 |
6 | 7 | ## Features 8 | 9 | This cookie provides the following features: 10 | 11 | * A [Flask-RESTX](https://github.com/python-restx/flask-restx) API properly defined skeleton 12 | * An [argparse](https://docs.python.org/3/library/argparse.html) client which defines the entry point of the API 13 | * [Pytest](https://docs.pytest.org/en/latest/) unit tests 14 | * [Swagger.io](https://swagger.io/) automatic API reference documentation 15 | * and much more that will be progressively included... 16 | 17 | ## Installation 18 | 19 | Firstly, you will need to install [cookiecutter](https://github.com/cookiecutter/cookiecutter) using pip from a Python3.6 version or higher since **this cookie recipe just works on Python3.6+**; use the following command: 20 | 21 | ``pip install cookiecutter`` 22 | 23 | So as **to create your own cookie from this recipe you will need to clone this repository** using the following command: 24 | 25 | ``git clone https://github.com/alvarobartt/restx-cookie`` 26 | 27 | Once it is properly cloned, from the working directory, you will need to **pass the cloned cookie as an argument to the cookiecutter entry point** as it follows: 28 | 29 | ``cookiecutter /path/to/restx-cookie`` 30 | 31 | This command will launch the **cookiecutter prompt into your terminal**, which will ask you some configuration options as specified in the cookie recipe for you to select the most suitable ones according to your needs. 32 | 33 | So to bake this cookie, the cookiecutter prompt will ask you to select the following ingredients (configuration) in case you want to name your cookie (project), for example, lets suppose that your project will be named `awesome_cookie` its configuration will be: 34 | 35 | ``` 36 | author [Alvaro Bartolome del Canto]: Cookie Monster 37 | email [alvarobartt@example.com]: cookiemonster@sesamestreet.com 38 | github_username [alvarobartt]: cookie-monster 39 | project_name [Flask Restx API]: awesome-cookie 40 | project_description [This project is a sample Python Flask Restx API]: This is an awesome cookie! 41 | repo_name [awesome_cookie]: awesome_cookie 42 | package_name [awesome_cookie]: awesome_cookie 43 | pypi_username [cookie-monster]: cookie-monster 44 | version [1]: 1 45 | flask_limiter [yes]: yes 46 | flask_cache [yes]: yes 47 | flask_cors [yes]: yes 48 | Select license: 49 | 1 - MIT License 50 | 2 - BSD License 51 | 3 - ISC License 52 | 4 - Apache Software License 2.0 53 | 5 - GNU General Public License v3 54 | 6 - WTFPL License 55 | 7 - None 56 | Choose from 1, 2, 3, 4, 5, 6, 7 [1]: 1 57 | ``` 58 | 59 | When this process is finished, automatically a directory named as specified in `repo_name` will be created containing the following files and directories: 60 | 61 | ``awesome_cookie/ MANIFEST.in README.md requirements.txt setup.cfg setup.py tests/`` 62 | 63 | **Congratulations! You already baked your own restx-cookie!** 64 | 65 | ## Usage 66 | 67 | **Once the cookie is completely baked, you can take it off the oven!** So on, from the `repo_name` previously created directory, so as to **install the Flask RESTX API** (since it is a Python package) you will need to run on of the following commands: `pip install .` or `python setup.py install`, which will install not just the package but all its dependencies. 68 | 69 | Once the newly created cookie is installed, just paste its **entry point** on the command line as it follows: 70 | 71 | `awesome_cookie` 72 | 73 | **The entry-point will launch the Flask RESTX API in your public IP (0.0.0.0) in the port 5000 using the current version** (which by deafult is the v1 version), so the complete address of the API is: `http://0.0.0.0:5000/v1`. In that address you will find the Swagger.io documentation automatically generated so as to know which endpoints are available, which data models are integrated, which is the input/output structure, etc. 74 | 75 | **Now you are completely free to eat your cookie!** 76 | 77 | ## Cookie recipe created by... 78 | 79 | 80 | 81 | 82 | 83 | 84 |

Álvaro Bartolomé

💻 📖🤔

Gabriel Martín

💻🤔
85 | 86 | ## Want more cookies? 87 | 88 | You can find a curated collection of self made cookies at [cookie-jar](https://github.com/alvarobartt/cookie-jar) 89 | 90 | ## You don't like this cookie? You don't like the chef/chefs? 91 | 92 | If you don't like neither the cookie neither the chef (or chefs), to each his own... here we do not judge you (just a little), here you have a list of some **similar cookie recipes you may like**: 93 | 94 | - [cookiecutter-flask](https://github.com/cookiecutter-flask/cookiecutter-flask) 95 | - [cookiecutter-flask-skeleton](https://github.com/realpython/cookiecutter-flask-skeleton) 96 | - [flask-empty](https://github.com/italomaia/flask-empty) 97 | - [cookiecutter-flask-restful](https://github.com/karec/cookiecutter-flask-restful) 98 | 99 | ## You want to become a chef too? 100 | 101 | Maybe after seeing this cookie recipe your inner chef spirit came out and you decided to become a chef, well, we already thought this may happen, so here you have a list of **useful links on your way to become a real chef** (well, not a real one just a programming chef somehow): 102 | 103 | - [cookiecutter](https://github.com/cookiecutter/cookiecutter) 104 | - [Getting to know Cookiecutter!](https://cookiecutter.readthedocs.io/en/1.7.0/tutorial1.html) 105 | - [Additional Tutorials](https://cookiecutter.readthedocs.io/en/latest/tutorials.html) 106 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/LICENSE: -------------------------------------------------------------------------------- 1 | {% if cookiecutter.license == 'MIT License' -%} 2 | MIT License 3 | 4 | Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.author }} <{{ cookiecutter.email }}> 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | {% elif cookiecutter.license == 'BSD License' %} 24 | 25 | BSD License 26 | 27 | Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.author }} <{{ cookiecutter.email }}> 28 | All rights reserved. 29 | 30 | Redistribution and use in source and binary forms, with or without modification, 31 | are permitted provided that the following conditions are met: 32 | 33 | * Redistributions of source code must retain the above copyright notice, this 34 | list of conditions and the following disclaimer. 35 | 36 | * Redistributions in binary form must reproduce the above copyright notice, this 37 | list of conditions and the following disclaimer in the documentation and/or 38 | other materials provided with the distribution. 39 | 40 | * Neither the name of the copyright holder nor the names of its 41 | contributors may be used to endorse or promote products derived from this 42 | software without specific prior written permission. 43 | 44 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 45 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 46 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 47 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 48 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 49 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 50 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 51 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 52 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 53 | OF THE POSSIBILITY OF SUCH DAMAGE. 54 | {% elif cookiecutter.license == 'ISC License' -%} 55 | ISC License 56 | 57 | Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.author }} <{{ cookiecutter.email }}> 58 | 59 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 60 | 61 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 62 | {% elif cookiecutter.license == 'Apache Software License 2.0' -%} 63 | Apache Software License 2.0 64 | 65 | Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.author }} <{{ cookiecutter.email }}> 66 | 67 | Licensed under the Apache License, Version 2.0 (the "License"); 68 | you may not use this file except in compliance with the License. 69 | You may obtain a copy of the License at 70 | 71 | http://www.apache.org/licenses/LICENSE-2.0 72 | 73 | Unless required by applicable law or agreed to in writing, software 74 | distributed under the License is distributed on an "AS IS" BASIS, 75 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 76 | See the License for the specific language governing permissions and 77 | limitations under the License. 78 | {% elif cookiecutter.license == 'GNU General Public License v3' -%} 79 | GNU GENERAL PUBLIC LICENSE 80 | Version 3, 29 June 2007 81 | 82 | Copyright (C) {% now 'local', '%Y' %}, {{ cookiecutter.author }} <{{ cookiecutter.email }}> 83 | 84 | This program is free software: you can redistribute it and/or modify 85 | it under the terms of the GNU General Public License as published by 86 | the Free Software Foundation, either version 3 of the License, or 87 | (at your option) any later version. 88 | 89 | This program is distributed in the hope that it will be useful, 90 | but WITHOUT ANY WARRANTY; without even the implied warranty of 91 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 92 | GNU General Public License for more details. 93 | 94 | You should have received a copy of the GNU General Public License 95 | along with this program. If not, see . 96 | 97 | Also add information on how to contact you by electronic and paper mail. 98 | 99 | You should also get your employer (if you work as a programmer) or school, 100 | if any, to sign a "copyright disclaimer" for the program, if necessary. 101 | For more information on this, and how to apply and follow the GNU GPL, see 102 | . 103 | 104 | The GNU General Public License does not permit incorporating your program 105 | into proprietary programs. If your program is a subroutine library, you 106 | may consider it more useful to permit linking proprietary applications with 107 | the library. If this is what you want to do, use the GNU Lesser General 108 | Public License instead of this License. But first, please read 109 | . 110 | {% elif cookiecutter.license == 'WTFPL License' -%} 111 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 112 | Version 2, December 2004 113 | 114 | Copyright (C) {% now 'local', '%Y' %}, {{ cookiecutter.author }} <{{ cookiecutter.email }}> 115 | 116 | Everyone is permitted to copy and distribute verbatim or modified 117 | copies of this license document, and changing it is allowed as long 118 | as the name is changed. 119 | 120 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 121 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 122 | 123 | 0. You just DO WHAT THE FUCK YOU WANT TO. 124 | {% endif %} 125 | --------------------------------------------------------------------------------