├── .gitignore ├── LICENSE ├── README.md ├── app ├── API │ ├── ClusterHealth │ │ ├── __pycache__ │ │ │ ├── views.cpython-36.pyc │ │ │ ├── views.cpython-37.pyc │ │ │ └── views.cpython-39.pyc │ │ └── views.py │ ├── __init__.py │ └── __pycache__ │ │ ├── __init__.cpython-36.pyc │ │ ├── __init__.cpython-37.pyc │ │ └── __init__.cpython-39.pyc ├── __pycache__ │ ├── app.cpython-36.pyc │ ├── app.cpython-37.pyc │ └── app.cpython-39.pyc ├── app.py ├── automation.py ├── coverage.json ├── dev.env ├── requirements.txt └── test_unittest_api.py ├── docker-compose.yml ├── release.Dockerfile └── test.Dockerfile /.gitignore: -------------------------------------------------------------------------------- 1 | # Build and Release Folders 2 | bin-debug/ 3 | bin-release/ 4 | [Oo]bj/ 5 | [Bb]in/ 6 | 7 | # Other files and folders 8 | .settings/ 9 | 10 | # Executables 11 | *.swf 12 | *.air 13 | *.ipa 14 | *.apk 15 | 16 | # Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties` 17 | # should NOT be excluded as they contain compiler settings and other important 18 | # information for Eclipse / Flash Builder. 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FlaskRestful-Swagger-UI-Docker-Compose-Unit-Test-How-to-organize-Python-Code-for-REST-API 2 | FlaskRestful + Swagger UI + Docker Compose + Unit Test | How to organize Python Code for REST API 3 | 4 | ![JobTarget](https://user-images.githubusercontent.com/39345855/172057846-f05915cf-f68c-4667-a4e5-1774f6ace99b.jpg) 5 | 6 | 7 | 8 | Flask API with Swagger UI: 9 | 10 | 11 | https://github.com/soumilshah1995/FlaskRestful-Swagger-UI-Docker-Compose-Unit-Test-How-to-organize-Python-Code-for-REST-API 12 | 13 | 14 | 15 | 16 | -------popular playlist----- 17 | 18 | 19 | 20 | AWS Multi Region Applications 21 | 22 | 23 | https://www.youtube.com/playlist?list=PLL2hlSFBmWww_HZ5lQlldwlJe977teSnt 24 | 25 | 26 | 27 | Learn About Serverless Framework 28 | 29 | 30 | https://www.youtube.com/playlist?list=PLL2hlSFBmWwzA7ut0KKYM6F8LKfu84-5c 31 | 32 | 33 | 34 | AWS Step Functions 35 | 36 | 37 | https://www.youtube.com/playlist?list=PLL2hlSFBmWwyvD04slTKH-s8hVkCw8h2C 38 | 39 | 40 | 41 | ----------Watch ----------- 42 | 43 | 44 | FlaskRestful + Swagger UI + Docker Compose + Unit Test | How to organize Python Code for REST API 45 | 46 | 47 | https://www.youtube.com/watch?v=Qm8DSKctffM 48 | 49 | 50 | 51 | 52 | ------------------------- Connect With Me ---------------------------------- 53 | 54 | 55 | Website : https://soumilshah.herokuapp.com 56 | 57 | 58 | Github : https://github.com/soumilshah1995 59 | 60 | 61 | Linkedin : https://www.linkedin.com/in/shah-soumil/ 62 | 63 | 64 | Blog : https://soumilshah1995.blogspot.com/ 65 | 66 | 67 | Youtube : https://www.youtube.com/channel/UC_eOodxvwS_H7x2uLQa-svw?view_as=subscriber 68 | 69 | 70 | Donate Me : https://www.paypal.com/paypalme/soumilshah1995 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /app/API/ClusterHealth/__pycache__/views.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/FlaskRestful-Swagger-UI-Docker-Compose-Unit-Test-How-to-organize-Python-Code-for-REST-API/19fbe66a32852b31b9b8e2e7eead6edff8705097/app/API/ClusterHealth/__pycache__/views.cpython-36.pyc -------------------------------------------------------------------------------- /app/API/ClusterHealth/__pycache__/views.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/FlaskRestful-Swagger-UI-Docker-Compose-Unit-Test-How-to-organize-Python-Code-for-REST-API/19fbe66a32852b31b9b8e2e7eead6edff8705097/app/API/ClusterHealth/__pycache__/views.cpython-37.pyc -------------------------------------------------------------------------------- /app/API/ClusterHealth/__pycache__/views.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/FlaskRestful-Swagger-UI-Docker-Compose-Unit-Test-How-to-organize-Python-Code-for-REST-API/19fbe66a32852b31b9b8e2e7eead6edff8705097/app/API/ClusterHealth/__pycache__/views.cpython-39.pyc -------------------------------------------------------------------------------- /app/API/ClusterHealth/views.py: -------------------------------------------------------------------------------- 1 | try: 2 | from flask import Flask 3 | from flask_restful import Resource, Api 4 | from apispec import APISpec 5 | from marshmallow import Schema, fields 6 | from apispec.ext.marshmallow import MarshmallowPlugin 7 | from flask_apispec.extension import FlaskApiSpec 8 | from flask_apispec.views import MethodResource 9 | from flask_apispec import marshal_with, doc, use_kwargs 10 | 11 | print("All imports are ok............") 12 | except Exception as e: 13 | print("Error: {} ".format(e)) 14 | 15 | 16 | class HeathController(MethodResource, Resource): 17 | 18 | @doc(description='This is health Endpoint', tags=['Health Endpoint']) 19 | def get(self): 20 | 21 | ''' 22 | Get method represents a GET API method 23 | ''' 24 | _ = {'message': 'APi are working fine'} 25 | print(str(_)) 26 | return _ -------------------------------------------------------------------------------- /app/API/__init__.py: -------------------------------------------------------------------------------- 1 | try: 2 | from flask import Flask 3 | from flask_restful import Resource, Api 4 | from apispec import APISpec 5 | from marshmallow import Schema, fields 6 | from apispec.ext.marshmallow import MarshmallowPlugin 7 | from flask_apispec.extension import FlaskApiSpec 8 | from flask_apispec.views import MethodResource 9 | from flask_apispec import marshal_with, doc, use_kwargs 10 | 11 | from API.ClusterHealth.views import HeathController 12 | 13 | except Exception as e: 14 | print("__init Modules are Missing {}".format(e)) 15 | 16 | app = Flask(__name__) # Flask app instance initiated 17 | api = Api(app) # Flask restful wraps Flask app around it. 18 | app.config.update({ 19 | 'APISPEC_SPEC': APISpec( 20 | title='Awesome Project', 21 | version='v1', 22 | plugins=[MarshmallowPlugin()], 23 | openapi_version='2.0.0' 24 | ), 25 | 'APISPEC_SWAGGER_URL': '/swagger/', # URI to access API Doc JSON 26 | 'APISPEC_SWAGGER_UI_URL': '/swagger-ui/' # URI to access UI of API Doc 27 | }) 28 | docs = FlaskApiSpec(app) 29 | -------------------------------------------------------------------------------- /app/API/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/FlaskRestful-Swagger-UI-Docker-Compose-Unit-Test-How-to-organize-Python-Code-for-REST-API/19fbe66a32852b31b9b8e2e7eead6edff8705097/app/API/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /app/API/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/FlaskRestful-Swagger-UI-Docker-Compose-Unit-Test-How-to-organize-Python-Code-for-REST-API/19fbe66a32852b31b9b8e2e7eead6edff8705097/app/API/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /app/API/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/FlaskRestful-Swagger-UI-Docker-Compose-Unit-Test-How-to-organize-Python-Code-for-REST-API/19fbe66a32852b31b9b8e2e7eead6edff8705097/app/API/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /app/__pycache__/app.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/FlaskRestful-Swagger-UI-Docker-Compose-Unit-Test-How-to-organize-Python-Code-for-REST-API/19fbe66a32852b31b9b8e2e7eead6edff8705097/app/__pycache__/app.cpython-36.pyc -------------------------------------------------------------------------------- /app/__pycache__/app.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/FlaskRestful-Swagger-UI-Docker-Compose-Unit-Test-How-to-organize-Python-Code-for-REST-API/19fbe66a32852b31b9b8e2e7eead6edff8705097/app/__pycache__/app.cpython-37.pyc -------------------------------------------------------------------------------- /app/__pycache__/app.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/FlaskRestful-Swagger-UI-Docker-Compose-Unit-Test-How-to-organize-Python-Code-for-REST-API/19fbe66a32852b31b9b8e2e7eead6edff8705097/app/__pycache__/app.cpython-39.pyc -------------------------------------------------------------------------------- /app/app.py: -------------------------------------------------------------------------------- 1 | from dotenv import load_dotenv 2 | load_dotenv() 3 | 4 | try: 5 | from API import (app, 6 | api, 7 | HeathController,docs 8 | 9 | ) 10 | except Exception as e: 11 | print("Modules are Missing : {} ".format(e)) 12 | 13 | api.add_resource(HeathController, '/health_check') 14 | docs.register(HeathController) 15 | 16 | -------------------------------------------------------------------------------- /app/automation.py: -------------------------------------------------------------------------------- 1 | try: 2 | import os 3 | import json 4 | import abc 5 | import string 6 | from enum import Enum 7 | except Exception as e: 8 | print("Error some module are missing :{} ".format(e)) 9 | 10 | class Parameters(object): 11 | 12 | def __init__(self): 13 | 14 | self.data = { 15 | 16 | "ApiName":"Code Coverage", 17 | "tag":"Code Coverage", 18 | "ApiDescription":"Code Coverage", 19 | "python_library":["import os", "from enum import Enum",], 20 | "field":[], 21 | 22 | } 23 | 24 | class CRUDEnum(Enum): 25 | GET_REQUEST = 1 26 | POST_REQUEST = 2 27 | DELETE_REQUEST = 3 28 | 29 | class CodeGenerator(object): 30 | 31 | def __init__(self, instance): 32 | self.instance = instance 33 | self.code = """""" 34 | 35 | def run(self): 36 | 37 | """Main Controller class""" 38 | 39 | self.step1_generate_imports() 40 | self.step2_generate_schemas() 41 | self.step3_generate_controller() 42 | print(self.code) 43 | self.step4_save_package() 44 | 45 | def step1_generate_imports(self): 46 | 47 | default_imports = """ 48 | try: 49 | from flask import app,Flask, request 50 | from flask_restful import Resource, Api, reqparse 51 | import json 52 | import os 53 | from marshmallow import Schema, fields 54 | from flask_apispec.views import MethodResource 55 | from flask_apispec import marshal_with, doc, use_kwargs""" 56 | if self.instance.data.get("python_library") is not []: 57 | for imports_ in self.instance.data.get("python_library"):default_imports += """ 58 | {}""".format(imports_) 59 | default_imports += """ 60 | print("All Modules are loaded ...") 61 | except Exception as e: 62 | print("some modules are Missing ") 63 | """ 64 | self.code += default_imports 65 | 66 | def step2_generate_schemas(self): 67 | 68 | controller_name = self.instance.data.get("ApiName") 69 | controller_schema_name = "" 70 | 71 | for x in CRUDEnum: 72 | 73 | if x.value == 2: 74 | controller_schema_name = controller_name+"Put"+"Schema" 75 | if self.instance.data.get("field") is not None: 76 | if len(self.instance.data.get("field")): 77 | class_name = """ 78 | class {}(Schema):""".format(controller_schema_name) 79 | self.code += class_name 80 | 81 | for feild in self.instance.data.get("field"): 82 | if feild.get("type") == "String": 83 | _ = """ 84 | {} = fields.String(required={}, description= "{}" )""".format(feild.get("name"), 85 | feild.get("required"), 86 | feild.get("description"), 87 | ) 88 | self.code += _ 89 | self.code += "\n" 90 | 91 | if x.value == 3: 92 | controller_schema_name = controller_name+"Delete"+"Schema" 93 | if self.instance.data.get("field") is not None: 94 | if len(self.instance.data.get("field")): 95 | class_name = """ 96 | class {}(Schema):""".format(controller_schema_name) 97 | self.code += class_name 98 | for feild in self.instance.data.get("field"): 99 | if feild.get("type") == "String": 100 | _ = """ 101 | {} = fields.String(required={}, description= "{}" )""".format(feild.get("name"), 102 | feild.get("required"), 103 | feild.get("description"), 104 | ) 105 | self.code += _ 106 | self.code += "\n" 107 | 108 | def step3_generate_controller(self): 109 | 110 | self.code += """ 111 | class {}Controller(MethodResource, Resource): 112 | """.format(self.instance.data.get("ApiName")) 113 | controller_schema_name = "" 114 | 115 | for x in CRUDEnum: 116 | 117 | if x.value == 1: 118 | 119 | self.code += """ 120 | @doc(description='Add Documentation ', tags=[ "{}" ] )""".format(self.instance.data.get("tag")) 121 | 122 | if self.instance.data.get("DataBaseRequired"): 123 | function_name = """ 124 | def get(self, **kwargs): 125 | try: 126 | 'Developer writes the code here ' 127 | 128 | JSON = kwargs 129 | helper = Database() 130 | 131 | query = "QUERY GOES HERE" 132 | response = helper.get(query=query) 133 | 134 | data = {"data":response} 135 | return data 136 | 137 | except Exception as e: 138 | print(e) 139 | return { 140 | "status":-1, 141 | "error":{ 142 | "message":str(e) 143 | } 144 | } 145 | \n""" 146 | 147 | else: 148 | function_name = """ 149 | def get(self, **kwargs): 150 | try: 151 | 'Developer writes the code here ' 152 | pass 153 | except Exception as e: 154 | print(e) 155 | return { 156 | "status":-1, 157 | "error":{ 158 | "message":str(e) 159 | } 160 | } 161 | \n""" 162 | 163 | if x.value == 2: 164 | 165 | self.code += """ 166 | @doc(description='Add Documentation ', tags=[ "{}" ] )""".format(self.instance.data.get("tag")) 167 | 168 | if self.instance.data.get("field") is not None: 169 | if len(self.instance.data.get("field")): 170 | self.code += """ 171 | @use_kwargs({}, location=('json'))""".format(self.instance.data.get("ApiName")+"Put"+"Schema") 172 | 173 | if self.instance.data.get("DataBaseRequired"): 174 | function_name = """ 175 | def post (self, **kwargs): 176 | try: 177 | 'Developer writes the code here ' 178 | 179 | JSON = kwargs 180 | helper = Database() 181 | 182 | query = "QUERY GOES HERE" 183 | response = helper.get(query=query) 184 | 185 | data = {"data":response} 186 | return data 187 | 188 | except Exception as e: 189 | print(e) 190 | return { 191 | "status":-1, 192 | "error":{ 193 | "message":str(e) 194 | } 195 | } 196 | """ 197 | 198 | else: 199 | function_name = """ 200 | def post (self, **kwargs): 201 | try: 202 | 'Developer writes the code here ' 203 | pass 204 | except Exception as e: 205 | print(e) 206 | return { 207 | "status":-1, 208 | "error":{ 209 | "message":str(e) 210 | } 211 | } 212 | """ 213 | 214 | if x.value == 3: 215 | 216 | self.code += """ 217 | @doc(description='Add Documentation ', tags=[ "{}" ] )""".format(self.instance.data.get("tag")) 218 | if self.instance.data.get("field") is not None: 219 | if len(self.instance.data.get("field")): 220 | self.code += """ 221 | @use_kwargs({}, location=('json'))""".format(self.instance.data.get("ApiName")+"Delete"+"Schema") 222 | 223 | if self.instance.data.get("DataBaseRequired"): 224 | function_name = """ 225 | def delete (self, **kwargs): 226 | try: 227 | 'Developer writes the code here ' 228 | 229 | JSON = kwargs 230 | helper = Database() 231 | 232 | query = "QUERY GOES HERE" 233 | response = helper.get(query=query) 234 | 235 | data = {"data":response} 236 | return data 237 | 238 | except Exception as e: 239 | print(e) 240 | return { 241 | "status":-1, 242 | "error":{ 243 | "message":str(e) 244 | } 245 | } 246 | """ 247 | else: 248 | function_name = """ 249 | def delete (self, **kwargs): 250 | try: 251 | 'Developer writes the code here ' 252 | pass 253 | except Exception as e: 254 | print(e) 255 | return { 256 | "status":-1, 257 | "error":{ 258 | "message":str(e) 259 | } 260 | } 261 | """ 262 | 263 | self.code += function_name 264 | 265 | def step4_save_package(self): 266 | try: 267 | viewName = """{}Views""".format(self.instance.data.get("ApiName")) 268 | 269 | os.mkdir( 270 | os.path.join( 271 | os.getcwd(), 272 | "API/{}".format(viewName) 273 | ) 274 | ) 275 | path = os.path.join(os.getcwd(),"API/{}/views.py".format(viewName)) 276 | with open(path, "w") as f: 277 | f.write(self.code) 278 | 279 | except Exception as e: 280 | print("Directory exists ") 281 | 282 | def main(): 283 | helper = Parameters() 284 | code_instance = CodeGenerator(instance=helper) 285 | code_instance.run() 286 | 287 | 288 | if __name__ == "__main__": 289 | main() -------------------------------------------------------------------------------- /app/coverage.json: -------------------------------------------------------------------------------- 1 | {"meta": {"version": "6.0.2", "timestamp": "2021-10-17T21:24:55.037910", "branch_coverage": false, "show_contexts": false}, "files": {"API/ClusterHealth/views.py": {"executed_lines": [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 16, 18, 19, 24], "summary": {"covered_lines": 14, "num_statements": 16, "percent_covered": 87.5, "percent_covered_display": "88", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [12, 13], "excluded_lines": []}, "API/__init__.py": {"executed_lines": [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 16, 17, 18, 19], "summary": {"covered_lines": 13, "num_statements": 17, "percent_covered": 76.47058823529412, "percent_covered_display": "76", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [12, 14, 15, 29], "excluded_lines": []}, "app.py": {"executed_lines": [1, 2, 4, 5, 13, 14], "summary": {"covered_lines": 6, "num_statements": 10, "percent_covered": 60.0, "percent_covered_display": "60", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [10, 11, 16, 17], "excluded_lines": []}, "test_unittest_api.py": {"executed_lines": [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 15, 17, 18, 20, 22, 23, 25, 27, 30, 32, 34, 36, 38, 43, 44, 45, 46, 48, 53, 54, 55, 56, 60], "summary": {"covered_lines": 32, "num_statements": 43, "percent_covered": 74.4186046511628, "percent_covered_display": "74", "missing_lines": 11, "excluded_lines": 0}, "missing_lines": [12, 13, 58, 64, 65, 66, 67, 68, 69, 72, 73], "excluded_lines": []}}, "totals": {"covered_lines": 65, "num_statements": 86, "percent_covered": 75.5813953488372, "percent_covered_display": "76", "missing_lines": 21, "excluded_lines": 0}} -------------------------------------------------------------------------------- /app/dev.env: -------------------------------------------------------------------------------- 1 | ROLE=DEV -------------------------------------------------------------------------------- /app/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask-RESTful==0.3.7 2 | Flask==1.1.1 3 | gunicorn==20.0.4 4 | python-dotenv==0.19.0 5 | flask-apispec==0.11.0 6 | jinja2 -------------------------------------------------------------------------------- /app/test_unittest_api.py: -------------------------------------------------------------------------------- 1 | try: 2 | import os 3 | import unittest 4 | import app 5 | import json 6 | from API import app 7 | from dotenv import load_dotenv 8 | import os 9 | import subprocess 10 | 11 | print("test file ok...........") 12 | except Exception as e: 13 | print(e) 14 | 15 | class FlaskTestCase(unittest.TestCase): 16 | 17 | @classmethod 18 | def setUpClass(cls): 19 | # code that is executed before all tests in one test run 20 | pass 21 | 22 | @classmethod 23 | def tearDownClass(cls): 24 | # code that is executed after all tests in one test run 25 | pass 26 | 27 | def setUp(self): 28 | # creates a test client 29 | # code that is executed before each test 30 | self.app = app.test_client() 31 | # propagate the exceptions to the test client 32 | self.app.testing = True 33 | 34 | def tearDown(self): 35 | # code that is executed after each test 36 | pass 37 | 38 | def test_health_endpoint(self): 39 | """ 40 | Check Whether Response is 200 41 | 42 | """ 43 | tester = app.test_client(self) 44 | response = tester.get("/health_check") 45 | statuscode = response.status_code 46 | self.assertEqual(statuscode, 200) 47 | 48 | def test_health_response(self): 49 | """ 50 | Check Whether Response is 200 51 | 52 | """ 53 | tester = app.test_client(self) 54 | response = tester.get("/health_check") 55 | data = json.loads(response.get_data(as_text=True)) 56 | self.assertEqual(data.get("message"), "APi are working fine") 57 | 58 | def test_code_coverage(self): 59 | 60 | """ 61 | Check Whether Response is 200 62 | 63 | """ 64 | print("\n") 65 | print("\n") 66 | import subprocess 67 | subprocess.run(["coverage", "report"]) 68 | print("\n") 69 | print("\n") 70 | 71 | 72 | if __name__ == "__main__": 73 | unittest.main() 74 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | 3 | services: 4 | myapitemplate: 5 | build: 6 | context: ./ 7 | dockerfile: release.Dockerfile 8 | container_name: myapi-template 9 | restart: always 10 | env_file: 11 | - app/dev.env 12 | volumes: 13 | - "./app:/app" 14 | ports: 15 | - "8080:8080" 16 | -------------------------------------------------------------------------------- /release.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6 2 | 3 | RUN python3 --version 4 | RUN pip3 --version 5 | 6 | WORKDIR /app 7 | COPY ./app /app 8 | 9 | RUN pip3 install --no-cache-dir -r requirements.txt 10 | RUN apt-get install openssl 11 | 12 | EXPOSE 8080 13 | 14 | CMD ["gunicorn", "--bind", "0.0.0.0:8080", "app:app"] -------------------------------------------------------------------------------- /test.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9-buster 2 | 3 | ENV PYTHONDONTWRITEBYTECODE=1 4 | ENV PYTHONUNBUFFERED=1 5 | ENV FLASK_APP=app.py 6 | ENV FLASK_ENV=development 7 | 8 | RUN python3 --version 9 | RUN pip3 --version 10 | 11 | WORKDIR /app 12 | COPY ./app /app 13 | 14 | RUN pip3 install --no-cache-dir -r requirements.txt 15 | RUN apt-get install openssl 16 | 17 | CMD ["python", "test_unittest_api.py"] 18 | --------------------------------------------------------------------------------