├── __init__.py ├── CONTRIBUTING.md ├── frameworks ├── flask │ └── __init__.py ├── django │ └── __init__.py ├── fastapi │ ├── __init__.py │ ├── dbs │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-310.pyc │ │ │ └── templates_mongo.cpython-310.pyc │ │ ├── templates_mongo.py │ │ └── templates_relational.py │ ├── utils.py │ └── framework.py └── __init__.py ├── requirements.txt ├── pastelaria.png ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_reporter.md ├── setup.py ├── LICENSE ├── README.md ├── pastelaria.py └── .gitignore /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frameworks/flask/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frameworks/django/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frameworks/fastapi/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frameworks/fastapi/dbs/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frameworks/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.0.1" -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | cutie>=0.3.2 2 | click==8.1.3 3 | -------------------------------------------------------------------------------- /pastelaria.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thaisribeiro/pastelaria/HEAD/pastelaria.png -------------------------------------------------------------------------------- /frameworks/fastapi/dbs/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thaisribeiro/pastelaria/HEAD/frameworks/fastapi/dbs/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /frameworks/fastapi/dbs/__pycache__/templates_mongo.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thaisribeiro/pastelaria/HEAD/frameworks/fastapi/dbs/__pycache__/templates_mongo.cpython-310.pyc -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: fedden, big-c-note 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_reporter.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: fedden 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to path '...' 16 | 2. Run command '....' 17 | 3. Check output of '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. Linux, Mac OS X, Windows] 28 | - Python version: [e.g 3.8, 3.10] 29 | - Browser [e.g. chrome, safari] 30 | - Any other relevant environment information... 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import frameworks 2 | from setuptools import setup, find_packages 3 | 4 | with open("README.md", "r", encoding="utf-8") as fh: 5 | long_description = fh.read() 6 | with open("requirements.txt", "r", encoding="utf-8") as fh: 7 | requirements = fh.read() 8 | 9 | setup(name="pastelaria", 10 | version=frameworks.__version__, 11 | author='Thais Ribeiro', 12 | author_email='thaisribeirodn@gmail.com', 13 | description='CLI for generating API boilerplates', 14 | long_description=long_description, 15 | long_description_content_type="text/markdown", 16 | url="https://github.com/thaisribeiro/pastelaria", 17 | packages=find_packages(), 18 | py_modules = ['pastelaria', 'frameworks'], 19 | install_requires=[requirements], 20 | classifiers=[ 21 | "Programming Language :: Python :: 3.10", 22 | "License :: OSI Approved :: MIT License", 23 | "Operating System :: OS Independent", 24 | ], 25 | entry_points=''' 26 | [console_scripts] 27 | pastelaria=pastelaria:cli 28 | ''', 29 | python_requires=">=3.10", 30 | ) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Thais Ribeiro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🌮 PASTELARIA 2 | 3 | ![pastelaria](https://raw.githubusercontent.com/thaisribeiro/pastelaria/main/pastelaria.png) 4 | 5 | This repository is a cli that automatically creates api boilerplates for some python frameworks 6 | 7 | _Made with love by [Thais](https://github.com/thaisribeiro). - CX software engineer at Luizalabs (Magalu)_ 8 | 9 | # Prerequisites 10 | This repository assumes that Python 3.10 or newer is used. 11 | 12 | # Installation 13 | 14 | Use `pip` to download project dependencies: 15 | 16 | ``` shell 17 | pip install pastelaria 18 | ``` 19 | # Dependencies 20 | 21 | ```shell 22 | pip install cutie 23 | ``` 24 | # Command Line Interface (CLI) 25 | 26 | A CLI will be installed through the package via pip. To get help on any option, just add the `--help` flag when invoking the CLI. 27 | 28 | to get the list of commands do: 29 | 30 | ```bash 31 | pastelaria --help 32 | ``` 33 | To start building the boilerplate, do: 34 | 35 | ```bash 36 | pastelaria init 37 | ``` 38 | Select framework and database options. 39 | 40 | ⚠️ The project is under construction, due to this some boilerplates are still being built and will be released soon 41 | 42 | ## Contributing 43 | 44 | This is an open source work, feel free to contribute, ideas and criticism are welcome. 45 | 46 | First of all, please check out the [CONTRIBUTING](/CONTRIBUTING.md) guide. 47 | 48 | Feel free to start a thread about github issues or contact me at thaisribeirodn@gmail.com 49 | ## Finally 50 | Thank you for making it this far, we are in the beginning, we expect contributions and patience for the finalization of the CLI 51 | 52 | -------------------------------------------------------------------------------- /frameworks/fastapi/utils.py: -------------------------------------------------------------------------------- 1 | SETTINGS='''from pydantic import BaseSettings 2 | from decouple import config 3 | 4 | class Settings(BaseSettings): 5 | NAME_APP: str = config("NAME_APP") 6 | HOST: str = config("HOST") 7 | PORT: int = config("PORT") 8 | DATABASE_HOST: str = config("DATABASE_HOST") 9 | NAME_DATABASE: str = config("NAME_DATABASE") 10 | 11 | settings = Settings() 12 | ''' 13 | 14 | INIT_ROUTER='''from fastapi import APIRouter 15 | from api.routers import user 16 | 17 | api_routers = APIRouter() 18 | 19 | api_routers.include_router(user.router) 20 | ''' 21 | 22 | README = '''#### 🤖 Este readme foi gerado pela Pastelaria 23 | This is a boilerplate using the fastAPI framework 24 | 25 | # Installation 26 | 27 | create a virtual environment 28 | 29 | ``` shell 30 | virtualenv venv --python=3.10 31 | ``` 32 | activate the virtual environment 33 | 34 | ```shell 35 | source venv/bin/activate 36 | ``` 37 | # Dependencies 38 | 39 | Use `pip` to download project dependencies: 40 | 41 | ```shell 42 | pip install -r requirements.txt 43 | ``` 44 | # Settings 45 | 46 | Configure environments in `.env` 47 | 48 | # Execution 49 | Run 50 | ```shell 51 | uvicorn main:app --reload 52 | ``` 53 | # Routes 54 | url: http://localhost:8000/docs 55 | ''' 56 | 57 | ENV = '''NAME_APP='nome_do_app' 58 | HOST='127.0.0.1:8000' 59 | PORT='8000' 60 | DATABASE_HOST='host' 61 | NAME_DATABASE='nome_database' 62 | ''' 63 | MAIN_UTIL='''import uvicorn 64 | from fastapi import FastAPI 65 | from pydantic import BaseModel 66 | 67 | app = FastAPI(title='nome_app') 68 | 69 | class User(BaseModel): 70 | name: str 71 | address: str 72 | email: str 73 | 74 | 75 | users = [] 76 | 77 | @app.post("/users") 78 | async def create_user(user: User): 79 | users.append(user.dict()) 80 | return users 81 | 82 | if __name__ == "__main__": 83 | uvicorn.main(app, reload=True) 84 | 85 | 86 | ''' -------------------------------------------------------------------------------- /frameworks/fastapi/framework.py: -------------------------------------------------------------------------------- 1 | import os 2 | from frameworks.fastapi.utils import SETTINGS, INIT_ROUTER, README, ENV, MAIN_UTIL 3 | 4 | class FastApiFramework: 5 | def __init__(self): 6 | self.path = 'api' 7 | self.folders = ['server', 'repositories', 'routers', 'config', 'schemas'] 8 | self.is_exists = os.path.exists(self.path) 9 | 10 | def create_fastapi_architecture(self, db): 11 | if db == 'mongodb': 12 | from frameworks.fastapi.dbs.templates_mongo import MAIN, ROUTER, SCHEMA, DATABASE, REPOSITORY, REQUIREMENTS 13 | elif db in ['mysql', 'postgresql']: 14 | from frameworks.fastapi.dbs.templates_relational import MAIN, ROUTER, SCHEMA, DATABASE, REPOSITORY, MODELS, REQUIREMENTS 15 | self.folders.append('models') 16 | else: 17 | self.handle_the_code('main.py', MAIN_UTIL) 18 | return True 19 | 20 | self.__create_three_directories() 21 | 22 | paths = [('main.py', MAIN), 23 | ('__init__.py', ''), 24 | ('requirements.txt', REQUIREMENTS), 25 | ('README.md', README), 26 | ('.env', ENV), 27 | (f'{self.path}/server/database.py', DATABASE), 28 | (f'{self.path}/config/settings.py', SETTINGS), 29 | (f'{self.path}/routers/user.py', ROUTER), 30 | (f'{self.path}/routers/__init__.py', INIT_ROUTER), 31 | (f'{self.path}/schemas/user.py', SCHEMA), 32 | (f'{self.path}/repositories/user.py', REPOSITORY)] 33 | 34 | if db != 'mongodb': 35 | paths.append((f'{self.path}/models/user.py', MODELS)) 36 | 37 | for path in paths: 38 | self.handle_the_code(path[0], path[1]) 39 | 40 | def handle_the_code(self, path, code): 41 | with open(path, 'w') as f: 42 | f.write(code) 43 | 44 | def __create_three_directories(self): 45 | if not self.is_exists: 46 | os.makedirs(self.path) 47 | with open(f'{self.path}/__init__.py', 'w'): pass 48 | for folder in self.folders: 49 | os.mkdir(os.path.join(self.path, folder)) 50 | with open(f'{self.path}/{folder}/__init__.py', 'w'): pass -------------------------------------------------------------------------------- /pastelaria.py: -------------------------------------------------------------------------------- 1 | import click 2 | import cutie 3 | 4 | LOGO = """ 5 | ────────────────────────────────── 6 | ────────────────────────────────── 7 | ────────────────────────────────── 8 | ──────────█▀▀▀▀▀▀▀▀▀▀▀▀█────────── 9 | ──────────█ PASTELARIA █────────── 10 | ─────▒▒▒▒▒▒▒▒▒▒▒▒██▒▒██▒▒██▒▒██─── 11 | ─────▒▒▒▒▒▒▒▒▒▒▒▒██▒▒██▒▒██▒▒██─── 12 | ─────███┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼▐───── 13 | ─────███░███████▌░░▐▀▀▀▀▀▀▀█▐───── 14 | ─────███░▌░░▐░░░▌░░▐═══════█▐───── 15 | ─────███░▌░░▐░░░▌░░▐═══════█▐───── 16 | ─────███░███████▌░░▐═══════█▐───── 17 | ─────███░░░░░░░░░░░▐▀▀▀▀▀▀▀█▐───── 18 | ─────███░░░░░░░░░░░▐═══════█▐───── 19 | ─────███░░░░░░░░░░░▐═══════█▐───── 20 | ────▄████████████████████████▄──── 21 | ────────────────────────────────── 22 | ────────────────────────────────── 23 | ────────────────────────────────── 24 | 25 | Bem-vindes a pastelaria, aqui 26 | seu boilerplate de API é montado na 27 | hora! 28 | 29 | """ 30 | 31 | @click.group() 32 | def cli(): 33 | print(LOGO) 34 | print("Cárdapio:") 35 | print(" init initialize app") 36 | pass 37 | 38 | def abort_if_false(ctx, param, value): 39 | if not value: 40 | ctx.abort() 41 | 42 | @cli.command() 43 | @click.option('--yes', is_flag=True, callback=abort_if_false, 44 | expose_value=False, 45 | prompt='Vamos começar o preparo?\n') 46 | def init(): 47 | name = input("Como posso te chamar? ") 48 | menu_f = [ 49 | "Escolha seu recheio (framework):", 50 | "FastAPI", 51 | "Flask (em construção)", 52 | "Django (em construção)" 53 | ] 54 | 55 | menu_db = [ 56 | "Agora um topping (database):", 57 | "mongoDB", 58 | "MySQL", 59 | "PostgreSQL", 60 | "Nenhum" 61 | ] 62 | 63 | click.echo(f"{name}, para inciarmos o preparo, informe as informações do cardápio abaixo: \n") 64 | 65 | framework = menu_f[cutie.select(menu_f, caption_indices=[0, 4, 4], selected_index=1)] 66 | database = menu_db[cutie.select(menu_db, caption_indices=[0, 5, 5], selected_index=1)] 67 | 68 | switcher = { 69 | 'fastapi': handle_fast_api, 70 | 'flask': handle_flask, 71 | 'django': django 72 | } 73 | 74 | try: 75 | switcher[framework.lower()](database.lower()) 76 | except: 77 | click.echo('Framework não suportado, tente novamente com um destes: FastAPI, Flask ou Django') 78 | 79 | def handle_fast_api(db): 80 | print('DB', db) 81 | from frameworks.fastapi.framework import FastApiFramework 82 | fastapi = FastApiFramework() 83 | 84 | fastapi.create_fastapi_architecture(db) 85 | 86 | def handle_flask(db): 87 | click.echo("Oh não! Essa é uma receita nova e ainda está sendo construída, logo mais no cardápio") 88 | 89 | def django(db): 90 | click.echo("Oh não! Essa é uma receita nova e ainda está sendo construída, logo mais no cardápio") 91 | 92 | 93 | # cli.add_command(init) 94 | 95 | if __name__ == "__main__": 96 | cli() 97 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | research/ 2 | tags 3 | tags/ 4 | *.pkl 5 | *.gz 6 | 7 | ### WEB STUFF 8 | 9 | build 10 | dist 11 | *.egg-info 12 | # Logs 13 | logs 14 | *.log 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | lerna-debug.log* 19 | 20 | # Diagnostic reports (https://nodejs.org/api/report.html) 21 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 22 | 23 | # Runtime data 24 | pids 25 | *.pid 26 | *.seed 27 | *.pid.lock 28 | 29 | # Directory for instrumented libs generated by jscoverage/JSCover 30 | lib-cov 31 | 32 | # Coverage directory used by tools like istanbul 33 | coverage 34 | *.lcov 35 | 36 | # nyc test coverage 37 | .nyc_output 38 | 39 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 40 | .grunt 41 | 42 | # Bower dependency directory (https://bower.io/) 43 | bower_components 44 | 45 | # node-waf configuration 46 | .lock-wscript 47 | 48 | # Compiled binary addons (https://nodejs.org/api/addons.html) 49 | build/Release 50 | 51 | # Dependency directories 52 | node_modules/ 53 | jspm_packages/ 54 | 55 | # Snowpack dependency directory (https://snowpack.dev/) 56 | web_modules/ 57 | 58 | # TypeScript cache 59 | *.tsbuildinfo 60 | 61 | # Optional npm cache directory 62 | .npm 63 | 64 | # Optional eslint cache 65 | .eslintcache 66 | 67 | # Microbundle cache 68 | .rpt2_cache/ 69 | .rts2_cache_cjs/ 70 | .rts2_cache_es/ 71 | .rts2_cache_umd/ 72 | 73 | # Optional REPL history 74 | .node_repl_history 75 | 76 | # Output of 'npm pack' 77 | *.tgz 78 | 79 | # Yarn Integrity file 80 | .yarn-integrity 81 | 82 | # dotenv environment variables file 83 | .env 84 | .env.test 85 | 86 | # parcel-bundler cache (https://parceljs.org/) 87 | .cache 88 | .parcel-cache 89 | 90 | # Next.js build output 91 | .next 92 | 93 | # Nuxt.js build / generate output 94 | .nuxt 95 | dist 96 | 97 | # Gatsby files 98 | .cache/ 99 | # Comment in the public line in if your project uses Gatsby and not Next.js 100 | # https://nextjs.org/blog/next-9-1#public-directory-support 101 | # public 102 | 103 | # vuepress build output 104 | .vuepress/dist 105 | 106 | # Serverless directories 107 | .serverless/ 108 | 109 | # FuseBox cache 110 | .fusebox/ 111 | 112 | # DynamoDB Local files 113 | .dynamodb/ 114 | 115 | # TernJS port file 116 | .tern-port 117 | 118 | # Stores VSCode versions used for testing VSCode extensions 119 | .vscode-test 120 | 121 | # yarn v2 122 | 123 | .yarn/cache 124 | .yarn/unplugged 125 | .yarn/build-state.yml 126 | .pnp.* 127 | 128 | ### PYTHON STUFF 129 | 130 | # Swap 131 | [._]*.s[a-v][a-z] 132 | !*.svg # comment out if you don't need vector files 133 | [._]*.sw[a-p] 134 | [._]s[a-rt-v][a-z] 135 | [._]ss[a-gi-z] 136 | [._]sw[a-p] 137 | 138 | # Session 139 | Session.vim 140 | Sessionx.vim 141 | 142 | # Temporary 143 | .netrwhist 144 | *~ 145 | # Auto-generated tag files 146 | tags 147 | # Persistent undo 148 | [._]*.un~ 149 | 150 | # Byte-compiled / optimized / DLL files 151 | __pycache__/ 152 | *.py[cod] 153 | *$py.class 154 | 155 | # C extensions 156 | *.so 157 | 158 | # Distribution / packaging 159 | .Python 160 | build/ 161 | develop-eggs/ 162 | dist/ 163 | downloads/ 164 | eggs/ 165 | .eggs/ 166 | lib/ 167 | lib64/ 168 | parts/ 169 | sdist/ 170 | var/ 171 | wheels/ 172 | pip-wheel-metadata/ 173 | share/python-wheels/ 174 | *.egg-info/ 175 | .installed.cfg 176 | *.egg 177 | MANIFEST 178 | 179 | # PyInstaller 180 | # Usually these files are written by a python script from a template 181 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 182 | *.manifest 183 | *.spec 184 | 185 | # Installer logs 186 | pip-log.txt 187 | pip-delete-this-directory.txt 188 | 189 | # Unit test / coverage reports 190 | htmlcov/ 191 | .tox/ 192 | .nox/ 193 | .coverage 194 | .coverage.* 195 | .cache 196 | nosetests.xml 197 | coverage.xml 198 | *.cover 199 | *.py,cover 200 | .hypothesis/ 201 | .pytest_cache/ 202 | 203 | # Translations 204 | *.mo 205 | *.pot 206 | 207 | # Django stuff: 208 | *.log 209 | local_settings.py 210 | db.sqlite3 211 | db.sqlite3-journal 212 | 213 | # Flask stuff: 214 | instance/ 215 | .webassets-cache 216 | 217 | # Scrapy stuff: 218 | .scrapy 219 | 220 | # Sphinx documentation 221 | docs/_build/ 222 | 223 | # PyBuilder 224 | target/ 225 | 226 | # Jupyter Notebook 227 | .ipynb_checkpoints 228 | 229 | # IPython 230 | profile_default/ 231 | ipython_config.py 232 | 233 | # pyenv 234 | .python-version 235 | 236 | # pipenv 237 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 238 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 239 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 240 | # install all needed dependencies. 241 | #Pipfile.lock 242 | 243 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 244 | __pypackages__/ 245 | 246 | # Celery stuff 247 | celerybeat-schedule 248 | celerybeat.pid 249 | 250 | # SageMath parsed files 251 | *.sage.py 252 | 253 | # Environments 254 | .env 255 | .venv 256 | env/ 257 | venv/ 258 | ENV/ 259 | env.bak/ 260 | venv.bak/ 261 | 262 | # Spyder project settings 263 | .spyderproject 264 | .spyproject 265 | 266 | # Rope project settings 267 | .ropeproject 268 | 269 | # mkdocs documentation 270 | /site 271 | 272 | # mypy 273 | .mypy_cache/ 274 | .dmypy.json 275 | dmypy.json 276 | 277 | # Pyre type checker 278 | .pyre/ 279 | 280 | # PyCharm 281 | .idea/ 282 | 283 | # Mac stuff 284 | .DS_Store 285 | -------------------------------------------------------------------------------- /frameworks/fastapi/dbs/templates_mongo.py: -------------------------------------------------------------------------------- 1 | DATABASE = '''from api.config.settings import settings 2 | from motor.motor_asyncio import AsyncIOMotorClient 3 | 4 | class Database: 5 | client: AsyncIOMotorClient = None 6 | user_db = None 7 | 8 | db = Database() 9 | 10 | async def connect_db(): 11 | db.client = AsyncIOMotorClient(settings.DATABASE_HOST, maxPoolSize=10, minPoolSize=10) 12 | db.user_db = db.client[settings.NAME_DATABASE].user 13 | 14 | async def close_conn_db(): 15 | db.client.close() 16 | ''' 17 | 18 | MAIN = '''import uvicorn 19 | from fastapi import FastAPI 20 | from api.routers import api_routers 21 | from api.config.settings import settings 22 | from api.server.database import connect_db, close_conn_db 23 | 24 | app = FastAPI(title=settings.NAME_APP) 25 | 26 | # add connection database 27 | app.add_event_handler("startup", connect_db) 28 | app.add_event_handler('shutdown', close_conn_db) 29 | 30 | # add routers 31 | app.include_router(api_routers) 32 | 33 | if __name__ == "__main__": 34 | uvicorn.main(app, host=settings.HOST, port=settings.PORT, reload=True) 35 | 36 | ''' 37 | 38 | ROUTER = '''from fastapi import APIRouter 39 | from typing import List 40 | from api.schemas.user import UserSchemaBase, UserSchemaUpdate 41 | from api.repositories.user import create_user, list_user, list_users, update_user, delete_user 42 | 43 | router = APIRouter(tags=["User"], prefix="/user") 44 | 45 | @router.post("", status_code=201) 46 | async def add_user(user: UserSchemaBase): 47 | user = await create_user(user) 48 | return user 49 | 50 | @router.get("", status_code=200) 51 | async def get_users(skip: int, limit: int): 52 | users = await list_users(skip, limit) 53 | return users 54 | 55 | @router.get("/{user_id}", status_code=200) 56 | async def get_user(user_id: str): 57 | user = await list_user(user_id) 58 | return user 59 | 60 | @router.put("/{user_id}", status_code=200) 61 | async def modify_user(user_id: str, user: UserSchemaUpdate): 62 | user = await update_user(user_id, user) 63 | return user 64 | 65 | @router.delete("/{user_id}", status_code=200) 66 | async def deleted_user(user_id): 67 | await delete_user(user_id) 68 | return "User deleted successfully" 69 | ''' 70 | 71 | SCHEMA = '''from typing import Optional 72 | from pydantic import BaseModel, EmailStr 73 | 74 | class UserSchemaBase(BaseModel): 75 | name: str 76 | address: str 77 | email: EmailStr 78 | 79 | class Config: 80 | schema_extra = { 81 | "example": { 82 | "name": "John Doe", 83 | "address": "R dos Blocos, 10", 84 | "email": "jdoe@x.edu.ng" 85 | } 86 | } 87 | 88 | class UserSchemaUpdate(BaseModel): 89 | name: Optional[str] 90 | address: Optional[str] 91 | email: Optional[EmailStr] 92 | 93 | class Config: 94 | schema_extra = { 95 | "example": { 96 | "name": "John Doe", 97 | "address": "R dos Blocos, 10", 98 | "email": "jdoe@x.edu.ng" 99 | } 100 | } 101 | ''' 102 | 103 | REPOSITORY = '''from fastapi import HTTPException, status 104 | from bson.objectid import ObjectId 105 | from api.server.database import db 106 | from api.schemas.user import UserSchemaBase, UserSchemaUpdate 107 | 108 | async def create_user(user: UserSchemaBase): 109 | try: 110 | user = user if type(user) == dict else user.dict() 111 | user = await db.user_db.insert_one(user) 112 | 113 | if user.inserted_id: 114 | return await list_user(user.inserted_id) 115 | except Exception: 116 | raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY) 117 | 118 | async def list_users(skip: int=0, limit: int=100): 119 | try: 120 | users_cursor = db.user_db.find().skip(int(skip)).limit(int(limit)) 121 | users = await users_cursor.to_list(length=int(limit)) 122 | return list(map(fix_user_id, users)) 123 | except Exception: 124 | raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST) 125 | 126 | async def list_user(user_id): 127 | try: 128 | user = await _get_user_or_404(user_id) 129 | return user 130 | except Exception: 131 | raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST) 132 | 133 | async def update_user(user_id, user_data: UserSchemaUpdate): 134 | try: 135 | user = user_data if type(user_data) == dict else user_data.dict() 136 | user = {k: v for k, v in user.items() if v is not None} 137 | user_op = await db.user_db.update_one({"_id": validate_object_id(user_id)}, {"$set": user}) 138 | 139 | if user_op.modified_count: 140 | return await _get_user_or_404(user_id) 141 | 142 | raise HTTPException(status_code=status.HTTP_304_NOT_MODIFIED) 143 | except Exception: 144 | raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST) 145 | 146 | async def delete_user(user_id): 147 | try: 148 | await _get_user_or_404(user_id) 149 | user = await db.user_db.delete_one({"_id": validate_object_id(user_id)}) 150 | 151 | if user.deleted_count: 152 | return {"status": "deleted product"} 153 | except Exception: 154 | raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST) 155 | 156 | async def _get_user_or_404(id): 157 | user = await db.user_db.find_one({"_id": validate_object_id(id)}) 158 | if user: 159 | return fix_user_id(user) 160 | 161 | raise HTTPException(status_code=404, detail="User not found") 162 | 163 | def fix_user_id(user): 164 | if user.get("_id", False): 165 | user["_id"] = str(user["_id"]) 166 | return user 167 | 168 | raise ValueError("No _id found") 169 | 170 | def validate_object_id(id): 171 | try: 172 | _id = ObjectId(id) 173 | except Exception: 174 | raise HTTPException(status_code=400, detail="invalid id") 175 | 176 | return _id 177 | ''' 178 | REQUIREMENTS = '''fastapi==0.92.0 179 | fastapi-mail==1.2.5 180 | uvicorn==0.20.0 181 | pydantic==1.10.5 182 | python-decouple==3.7 183 | motor==3.1.1 184 | ''' -------------------------------------------------------------------------------- /frameworks/fastapi/dbs/templates_relational.py: -------------------------------------------------------------------------------- 1 | DATABASE='''from api.config.settings import settings 2 | from sqlalchemy import create_engine 3 | from sqlalchemy.ext.declarative import declarative_base 4 | from sqlalchemy.orm import sessionmaker 5 | 6 | DATABASE_URL = settings.DATABASE_HOST 7 | 8 | db_engine = create_engine(DATABASE_URL) 9 | SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=db_engine) 10 | 11 | Base = declarative_base() 12 | 13 | def get_db(): 14 | db = None 15 | try: 16 | db = SessionLocal() 17 | yield db 18 | finally: 19 | db.close() 20 | ''' 21 | 22 | MAIN='''import uvicorn 23 | import api.models.user as models 24 | 25 | from fastapi import FastAPI 26 | from api.routers import api_routers 27 | from api.config.settings import settings 28 | from api.server.database import db_engine 29 | 30 | app = FastAPI(title=settings.NAME_APP) 31 | 32 | models.Base.metadata.create_all(bind=db_engine) 33 | 34 | # add routers 35 | app.include_router(api_routers) 36 | 37 | if __name__ == "__main__": 38 | uvicorn.main(app, host=settings.HOST, port=settings.PORT, reload=True) 39 | ''' 40 | 41 | ROUTER = '''from fastapi import APIRouter, Depends, HTTPException 42 | from typing import List 43 | from sqlalchemy.orm import Session 44 | from api.schemas.user import UserSchemaBase, UserSchemaList 45 | from api.server.database import get_db 46 | from api.repositories.user import create_user, list_users, list_user, update_user, delete_user 47 | 48 | router = APIRouter(tags=["Users"], prefix="/users") 49 | 50 | @router.post("", response_model=UserSchemaBase, status_code=201) 51 | async def add_user(user: UserSchemaBase, db: Session = Depends(get_db)): 52 | user = await create_user(db, user) 53 | return user 54 | 55 | @router.get("", response_model=List[UserSchemaList], status_code=200) 56 | def get_users(skip: int = 1, limit: int = 100, db: Session = Depends(get_db)): 57 | return list_users(db, skip, limit) 58 | 59 | @router.get("/{user_id}", response_model=UserSchemaList, status_code=200) 60 | def get_user(user_id: int, db: Session = Depends(get_db)): 61 | user = list_user(db, user_id) 62 | if user is None: 63 | raise HTTPException(status_code=404, detail='User not found') 64 | return user 65 | 66 | @router.put("/{user_id}", response_model=UserSchemaList, status_code=200) 67 | async def modify_user(user_id: int, user: UserSchemaBase, db: Session = Depends(get_db)): 68 | user = await update_user(db, user_id, user) 69 | return user 70 | 71 | @router.delete("/{user_id}", status_code=200) 72 | async def deleted_user(user_id: int, db: Session = Depends(get_db)): 73 | await delete_user(db, user_id) 74 | return "User deleted successfully" 75 | ''' 76 | 77 | SCHEMA = '''from typing import Optional 78 | from pydantic import BaseModel, EmailStr 79 | 80 | class UserSchemaBase(BaseModel): 81 | name: str 82 | address: str 83 | email: EmailStr 84 | 85 | class Config: 86 | orm_mode = True # allows the app to take ORM objects and translate them into responses automatically 87 | schema_extra = { 88 | "example": { 89 | "name": "John Doe", 90 | "address": "R dos Blocos, 10", 91 | "email": "jdoe@x.edu.ng" 92 | } 93 | 94 | } 95 | 96 | class UserSchemaUpdate(BaseModel): 97 | name: Optional[str] 98 | address: Optional[str] 99 | email: Optional[EmailStr] 100 | 101 | class Config: 102 | orm_mode = True 103 | schema_extra = { 104 | "example": { 105 | "name": "John Doe", 106 | "address": "R dos Blocos, 10", 107 | "email": "jdoe@x.edu.ng" 108 | } 109 | } 110 | 111 | class UserSchemaList(UserSchemaBase): 112 | id: str 113 | 114 | class Config: 115 | orm_mode = True 116 | schema_extra = { 117 | "example": { 118 | "id": "1", 119 | "name": "John Doe", 120 | "address": "R dos Blocos, 10", 121 | "email": "jdoe@x.edu.ng" 122 | } 123 | } 124 | ''' 125 | 126 | REPOSITORY='''from fastapi import HTTPException, status 127 | from fastapi.encoders import jsonable_encoder 128 | from sqlalchemy.orm import Session 129 | from api.models.user import User 130 | from api.schemas.user import UserSchemaBase, UserSchemaUpdate 131 | 132 | async def create_user(db: Session, user: UserSchemaBase): 133 | try: 134 | user = user if type(user) == dict else user.dict() 135 | user = User(name=user['name'], address=user['address'], email=user['email']) 136 | db.add(user) 137 | db.commit() 138 | db.refresh(user) 139 | return user 140 | except Exception: 141 | raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY) 142 | 143 | 144 | def list_users(db: Session, skip: int = 0, limit: int = 100): 145 | try: 146 | users = db.query(User).offset(skip).limit(limit).all() 147 | return users 148 | except Exception as e: 149 | print(e) 150 | raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST) 151 | 152 | def list_user(db: Session, user_id): 153 | try: 154 | user = db.query(User).filter(User.id == user_id).first() 155 | return user 156 | except Exception: 157 | raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST) 158 | 159 | async def update_user(db: Session, user_id, user: UserSchemaUpdate): 160 | try: 161 | db_user_id = list_user(db, user_id) 162 | if db_user_id: 163 | update_user_encoded = jsonable_encoder(user) 164 | db_user_id.name = update_user_encoded.get('name', db_user_id.name) 165 | db_user_id.address = update_user_encoded.get('address', db_user_id.address) 166 | db_user_id.email = update_user_encoded.get('email', db_user_id.email) 167 | updated_user = db.merge(db_user_id) 168 | db.commit() 169 | return updated_user 170 | except Exception: 171 | raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST) 172 | 173 | async def delete_user(db: Session, user_id): 174 | try: 175 | db_user = db.query(User).filter_by(id=user_id).first() 176 | db.delete(db_user) 177 | db.commit() 178 | except Exception: 179 | raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST) 180 | ''' 181 | 182 | MODELS='''from sqlalchemy import Column, Integer, String 183 | from api.server.database import Base 184 | 185 | class User(Base): 186 | __tablename__ = "users" 187 | id = Column(Integer, primary_key=True, index=True) 188 | name = Column(String(255)) 189 | address = Column(String(255)) 190 | email = Column(String(255), index=True) 191 | 192 | def __repr__(self): 193 | return 'UserModel(name=%s, address=%s, email=%s)' % (self.name, self.address, self.email) 194 | 195 | ''' 196 | 197 | REQUIREMENTS = '''fastapi==0.92.0 198 | fastapi-mail==1.2.5 199 | uvicorn==0.20.0 200 | pydantic==1.10.5 201 | python-decouple==3.7 202 | SQLAlchemy==2.0.4 203 | ''' --------------------------------------------------------------------------------