fastcodedog 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. fastcodedog-0.1.0/PKG-INFO +31 -0
  2. fastcodedog-0.1.0/README.md +3 -0
  3. fastcodedog-0.1.0/fastcodedog/__init__.py +1 -0
  4. fastcodedog-0.1.0/fastcodedog/api/__init__.py +1 -0
  5. fastcodedog-0.1.0/fastcodedog/api/api.py +177 -0
  6. fastcodedog-0.1.0/fastcodedog/api/api_package.py +49 -0
  7. fastcodedog-0.1.0/fastcodedog/api/app_function.py +22 -0
  8. fastcodedog-0.1.0/fastcodedog/api/config.py +68 -0
  9. fastcodedog-0.1.0/fastcodedog/api/db.py +31 -0
  10. fastcodedog-0.1.0/fastcodedog/api/main.py +60 -0
  11. fastcodedog-0.1.0/fastcodedog/api/oauth.py +120 -0
  12. fastcodedog-0.1.0/fastcodedog/cli.py +37 -0
  13. fastcodedog-0.1.0/fastcodedog/context/__init__.py +1 -0
  14. fastcodedog-0.1.0/fastcodedog/context/base.py +9 -0
  15. fastcodedog-0.1.0/fastcodedog/context/context.py +32 -0
  16. fastcodedog-0.1.0/fastcodedog/context/context_instance.py +52 -0
  17. fastcodedog-0.1.0/fastcodedog/context/contextbase.py +37 -0
  18. fastcodedog-0.1.0/fastcodedog/context/modules.py +12 -0
  19. fastcodedog-0.1.0/fastcodedog/context/oauth2.py +11 -0
  20. fastcodedog-0.1.0/fastcodedog/context/query.py +25 -0
  21. fastcodedog-0.1.0/fastcodedog/context/schema.py +32 -0
  22. fastcodedog-0.1.0/fastcodedog/context/tenant.py +11 -0
  23. fastcodedog-0.1.0/fastcodedog/crud/__init__.py +1 -0
  24. fastcodedog-0.1.0/fastcodedog/crud/_crud.py +168 -0
  25. fastcodedog-0.1.0/fastcodedog/crud/crud.py +78 -0
  26. fastcodedog-0.1.0/fastcodedog/crud/crud_package.py +26 -0
  27. fastcodedog-0.1.0/fastcodedog/crud/method_block.py +14 -0
  28. fastcodedog-0.1.0/fastcodedog/crud/param.py +13 -0
  29. fastcodedog-0.1.0/fastcodedog/diagram/README.MD +3 -0
  30. fastcodedog-0.1.0/fastcodedog/diagram/__init__.py +1 -0
  31. fastcodedog-0.1.0/fastcodedog/diagram/column.py +64 -0
  32. fastcodedog-0.1.0/fastcodedog/diagram/diagram.py +188 -0
  33. fastcodedog-0.1.0/fastcodedog/diagram/reference.xml +38 -0
  34. fastcodedog-0.1.0/fastcodedog/diagram/table.py +169 -0
  35. fastcodedog-0.1.0/fastcodedog/diagram/table.xml +130 -0
  36. fastcodedog-0.1.0/fastcodedog/generation/__init__.py +1 -0
  37. fastcodedog-0.1.0/fastcodedog/generation/classs_block.py +36 -0
  38. fastcodedog-0.1.0/fastcodedog/generation/file.py +90 -0
  39. fastcodedog-0.1.0/fastcodedog/generation/function_block.py +23 -0
  40. fastcodedog-0.1.0/fastcodedog/generation/generationbase.py +4 -0
  41. fastcodedog-0.1.0/fastcodedog/generation/import_stmt.py +62 -0
  42. fastcodedog-0.1.0/fastcodedog/generation/package.py +18 -0
  43. fastcodedog-0.1.0/fastcodedog/generation/param.py +15 -0
  44. fastcodedog-0.1.0/fastcodedog/generation/variable.py +35 -0
  45. fastcodedog-0.1.0/fastcodedog/model/__init__.py +1 -0
  46. fastcodedog-0.1.0/fastcodedog/model/_base.py +12 -0
  47. fastcodedog-0.1.0/fastcodedog/model/attribute.py +34 -0
  48. fastcodedog-0.1.0/fastcodedog/model/init.py +19 -0
  49. fastcodedog-0.1.0/fastcodedog/model/model.py +76 -0
  50. fastcodedog-0.1.0/fastcodedog/model/model_package.py +52 -0
  51. fastcodedog-0.1.0/fastcodedog/model/relation_object.py +39 -0
  52. fastcodedog-0.1.0/fastcodedog/model/sub_object.py +100 -0
  53. fastcodedog-0.1.0/fastcodedog/model/unique_stmt.py +16 -0
  54. fastcodedog-0.1.0/fastcodedog/schema/__init__.py +1 -0
  55. fastcodedog-0.1.0/fastcodedog/schema/attribute.py +29 -0
  56. fastcodedog-0.1.0/fastcodedog/schema/schema.py +119 -0
  57. fastcodedog-0.1.0/fastcodedog/schema/schema_additional.py +62 -0
  58. fastcodedog-0.1.0/fastcodedog/schema/schema_additional_class_block.py +92 -0
  59. fastcodedog-0.1.0/fastcodedog/schema/schema_additional_package.py +27 -0
  60. fastcodedog-0.1.0/fastcodedog/schema/schema_class_block.py +16 -0
  61. fastcodedog-0.1.0/fastcodedog/schema/schema_package.py +44 -0
  62. fastcodedog-0.1.0/fastcodedog/test/__init__.py +1 -0
  63. fastcodedog-0.1.0/fastcodedog/test/codes/__init__.py +1 -0
  64. fastcodedog-0.1.0/fastcodedog/test/codes/hello.py +9 -0
  65. fastcodedog-0.1.0/fastcodedog/test/codes/test_import.py +13 -0
  66. fastcodedog-0.1.0/fastcodedog/test/fakeapp/__init__.py +0 -0
  67. fastcodedog-0.1.0/fastcodedog/test/fakeapp/api/__init__.py +0 -0
  68. fastcodedog-0.1.0/fastcodedog/test/fakeapp/api/admin/__init__.py +0 -0
  69. fastcodedog-0.1.0/fastcodedog/test/fakeapp/api/admin/menu.py +48 -0
  70. fastcodedog-0.1.0/fastcodedog/test/fakeapp/api/admin/organization.py +48 -0
  71. fastcodedog-0.1.0/fastcodedog/test/fakeapp/api/admin/position.py +58 -0
  72. fastcodedog-0.1.0/fastcodedog/test/fakeapp/api/admin/tenant.py +38 -0
  73. fastcodedog-0.1.0/fastcodedog/test/fakeapp/api/admin/user.py +102 -0
  74. fastcodedog-0.1.0/fastcodedog/test/fakeapp/api/define/__init__.py +0 -0
  75. fastcodedog-0.1.0/fastcodedog/test/fakeapp/api/define/attribute.py +38 -0
  76. fastcodedog-0.1.0/fastcodedog/test/fakeapp/api/define/attribute_select_item.py +52 -0
  77. fastcodedog-0.1.0/fastcodedog/test/fakeapp/api/define/attribute_verification.py +38 -0
  78. fastcodedog-0.1.0/fastcodedog/test/fakeapp/api/oauth/__init__.py +0 -0
  79. fastcodedog-0.1.0/fastcodedog/test/fakeapp/api/oauth/oauth.py +53 -0
  80. fastcodedog-0.1.0/fastcodedog/test/fakeapp/api/operation/__init__.py +0 -0
  81. fastcodedog-0.1.0/fastcodedog/test/fakeapp/api/operation/api_log.py +38 -0
  82. fastcodedog-0.1.0/fastcodedog/test/fakeapp/config/__init__.py +0 -0
  83. fastcodedog-0.1.0/fastcodedog/test/fakeapp/config/config.ini +5 -0
  84. fastcodedog-0.1.0/fastcodedog/test/fakeapp/config/config.py +28 -0
  85. fastcodedog-0.1.0/fastcodedog/test/fakeapp/crud/__init__.py +0 -0
  86. fastcodedog-0.1.0/fastcodedog/test/fakeapp/crud/admin/__init__.py +0 -0
  87. fastcodedog-0.1.0/fastcodedog/test/fakeapp/crud/admin/menu.py +60 -0
  88. fastcodedog-0.1.0/fastcodedog/test/fakeapp/crud/admin/organization.py +60 -0
  89. fastcodedog-0.1.0/fastcodedog/test/fakeapp/crud/admin/position.py +83 -0
  90. fastcodedog-0.1.0/fastcodedog/test/fakeapp/crud/admin/tenant.py +37 -0
  91. fastcodedog-0.1.0/fastcodedog/test/fakeapp/crud/admin/user.py +113 -0
  92. fastcodedog-0.1.0/fastcodedog/test/fakeapp/crud/define/__init__.py +0 -0
  93. fastcodedog-0.1.0/fastcodedog/test/fakeapp/crud/define/attribute.py +37 -0
  94. fastcodedog-0.1.0/fastcodedog/test/fakeapp/crud/define/attribute_select_item.py +40 -0
  95. fastcodedog-0.1.0/fastcodedog/test/fakeapp/crud/define/attribute_verification.py +40 -0
  96. fastcodedog-0.1.0/fastcodedog/test/fakeapp/crud/operation/__init__.py +0 -0
  97. fastcodedog-0.1.0/fastcodedog/test/fakeapp/crud/operation/api_log.py +37 -0
  98. fastcodedog-0.1.0/fastcodedog/test/fakeapp/db.py +21 -0
  99. fastcodedog-0.1.0/fastcodedog/test/fakeapp/main.py +49 -0
  100. fastcodedog-0.1.0/fastcodedog/test/fakeapp/model/__init__.py +20 -0
  101. fastcodedog-0.1.0/fastcodedog/test/fakeapp/model/admin/__init__.py +0 -0
  102. fastcodedog-0.1.0/fastcodedog/test/fakeapp/model/admin/menu.py +30 -0
  103. fastcodedog-0.1.0/fastcodedog/test/fakeapp/model/admin/organization.py +29 -0
  104. fastcodedog-0.1.0/fastcodedog/test/fakeapp/model/admin/position.py +25 -0
  105. fastcodedog-0.1.0/fastcodedog/test/fakeapp/model/admin/position_menu.py +17 -0
  106. fastcodedog-0.1.0/fastcodedog/test/fakeapp/model/admin/tenant.py +20 -0
  107. fastcodedog-0.1.0/fastcodedog/test/fakeapp/model/admin/user.py +29 -0
  108. fastcodedog-0.1.0/fastcodedog/test/fakeapp/model/admin/user_organization.py +17 -0
  109. fastcodedog-0.1.0/fastcodedog/test/fakeapp/model/admin/user_position.py +17 -0
  110. fastcodedog-0.1.0/fastcodedog/test/fakeapp/model/base.py +12 -0
  111. fastcodedog-0.1.0/fastcodedog/test/fakeapp/model/define/__init__.py +0 -0
  112. fastcodedog-0.1.0/fastcodedog/test/fakeapp/model/define/attribute.py +31 -0
  113. fastcodedog-0.1.0/fastcodedog/test/fakeapp/model/define/attribute_select_item.py +25 -0
  114. fastcodedog-0.1.0/fastcodedog/test/fakeapp/model/define/attribute_verification.py +23 -0
  115. fastcodedog-0.1.0/fastcodedog/test/fakeapp/model/operation/__init__.py +0 -0
  116. fastcodedog-0.1.0/fastcodedog/test/fakeapp/model/operation/api_log.py +28 -0
  117. fastcodedog-0.1.0/fastcodedog/test/fakeapp/schema/__init__.py +0 -0
  118. fastcodedog-0.1.0/fastcodedog/test/fakeapp/schema/admin/__init__.py +0 -0
  119. fastcodedog-0.1.0/fastcodedog/test/fakeapp/schema/admin/menu.py +38 -0
  120. fastcodedog-0.1.0/fastcodedog/test/fakeapp/schema/admin/organization.py +37 -0
  121. fastcodedog-0.1.0/fastcodedog/test/fakeapp/schema/admin/position.py +35 -0
  122. fastcodedog-0.1.0/fastcodedog/test/fakeapp/schema/admin/tenant.py +40 -0
  123. fastcodedog-0.1.0/fastcodedog/test/fakeapp/schema/admin/user.py +40 -0
  124. fastcodedog-0.1.0/fastcodedog/test/fakeapp/schema/admin/user_additional.py +51 -0
  125. fastcodedog-0.1.0/fastcodedog/test/fakeapp/schema/callback/__init__.py +0 -0
  126. fastcodedog-0.1.0/fastcodedog/test/fakeapp/schema/callback/get_dynamic_attributes.py +15 -0
  127. fastcodedog-0.1.0/fastcodedog/test/fakeapp/schema/define/__init__.py +0 -0
  128. fastcodedog-0.1.0/fastcodedog/test/fakeapp/schema/define/attribute.py +39 -0
  129. fastcodedog-0.1.0/fastcodedog/test/fakeapp/schema/define/attribute_select_item.py +37 -0
  130. fastcodedog-0.1.0/fastcodedog/test/fakeapp/schema/define/attribute_select_item_additional.py +14 -0
  131. fastcodedog-0.1.0/fastcodedog/test/fakeapp/schema/define/attribute_verification.py +35 -0
  132. fastcodedog-0.1.0/fastcodedog/test/fakeapp/schema/operation/__init__.py +0 -0
  133. fastcodedog-0.1.0/fastcodedog/test/fakeapp/schema/operation/api_log.py +45 -0
  134. fastcodedog-0.1.0/fastcodedog/test/fastcodedog.api.oauth.json5 +21 -0
  135. fastcodedog-0.1.0/fastcodedog/test/fastcodedog.base.json5 +11 -0
  136. fastcodedog-0.1.0/fastcodedog/test/fastcodedog.crud.user.json5 +63 -0
  137. fastcodedog-0.1.0/fastcodedog/test/fastcodedog.diagram.json5 +43 -0
  138. fastcodedog-0.1.0/fastcodedog/test/fastcodedog.model.json5 +7 -0
  139. fastcodedog-0.1.0/fastcodedog/test/fastcodedog.schema.json5 +49 -0
  140. fastcodedog-0.1.0/fastcodedog/test/fastcodedog.schema.select_item.json5 +11 -0
  141. fastcodedog-0.1.0/fastcodedog/test/fastcodedog.schema.user.json5 +50 -0
  142. fastcodedog-0.1.0/fastcodedog/test/test_context.py +8 -0
  143. fastcodedog-0.1.0/fastcodedog/test/test_generate_all.py +57 -0
  144. fastcodedog-0.1.0/fastcodedog/test/test_generated_model.py +99 -0
  145. fastcodedog-0.1.0/fastcodedog/test/test_generated_model_with_tenant.py +109 -0
  146. fastcodedog-0.1.0/fastcodedog/todo.txt +5 -0
  147. fastcodedog-0.1.0/fastcodedog/util/__init__.py +1 -0
  148. fastcodedog-0.1.0/fastcodedog/util/case_converter.py +32 -0
  149. fastcodedog-0.1.0/fastcodedog/util/deep_update.py +35 -0
  150. fastcodedog-0.1.0/fastcodedog/util/find_file.py +15 -0
  151. fastcodedog-0.1.0/fastcodedog/util/indent.py +34 -0
  152. fastcodedog-0.1.0/fastcodedog/util/inflect_wrapper.py +22 -0
  153. fastcodedog-0.1.0/fastcodedog/util/make_dirs.py +27 -0
  154. fastcodedog-0.1.0/fastcodedog/util/singleton.py +15 -0
  155. fastcodedog-0.1.0/fastcodedog/util/type_converter.py +59 -0
  156. fastcodedog-0.1.0/fastcodedog/util/write_file.py +26 -0
  157. fastcodedog-0.1.0/pyproject.toml +36 -0
@@ -0,0 +1,31 @@
1
+ Metadata-Version: 2.1
2
+ Name: fastcodedog
3
+ Version: 0.1.0
4
+ Summary:
5
+ License: MIT
6
+ Author: taohoo
7
+ Author-email: taohoo@163.com
8
+ Requires-Python: >=3.10
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Requires-Dist: autopep8 (>=2.2.0)
15
+ Requires-Dist: fastapi (>=0.100.0)
16
+ Requires-Dist: fastoauth (>=0.1.0)
17
+ Requires-Dist: inflect (>=7.2.1)
18
+ Requires-Dist: json5 (>=0.9.25)
19
+ Requires-Dist: psycopg2[binary] (>=2.9.5)
20
+ Requires-Dist: pydantic (>=1.10.10)
21
+ Requires-Dist: pyopenssl (>=24.0.0)
22
+ Requires-Dist: python-jose (>=3.3.0)
23
+ Requires-Dist: python-multipart (>=0.0.9)
24
+ Requires-Dist: redis (>=5.0.5)
25
+ Requires-Dist: sqlalchemy (>=2.0.23)
26
+ Requires-Dist: uvicorn (>=0.30.0)
27
+ Description-Content-Type: text/markdown
28
+
29
+ 基于pdm文件,自动生成fastapi工程。
30
+ 包括sqlalchemy的model,crud。
31
+ fastapi的schema,api。
@@ -0,0 +1,3 @@
1
+ 基于pdm文件,自动生成fastapi工程。
2
+ 包括sqlalchemy的model,crud。
3
+ fastapi的schema,api。
@@ -0,0 +1 @@
1
+ # -*- coding: utf-8 -*-
@@ -0,0 +1 @@
1
+ # -*- coding: utf-8 -*-
@@ -0,0 +1,177 @@
1
+ # -*- coding: utf-8 -*-
2
+ from fastcodedog.context.context_instance import ctx_instance
3
+ from fastcodedog.crud.param import Param
4
+ from fastcodedog.generation.file import File
5
+ from fastcodedog.generation.function_block import FunctionBlock
6
+ from fastcodedog.generation.variable import Variable
7
+ from fastcodedog.util.case_converter import camel_to_snake
8
+ from .app_function import AppFunction
9
+ from .oauth import enable_api_oauth2
10
+ from ..util.inflect_wrapper import plural
11
+
12
+
13
+ class Api(File):
14
+ def __init__(self, crud, path, package, desc=None):
15
+ super().__init__(path, package, desc)
16
+ self.code = crud.code
17
+ self.crud = crud
18
+ self.schema = self.crud.schema
19
+ self.model = self.schema.model
20
+ self.schema_additional = ctx_instance.schema_additional_package.shcema_additionals.get(self.code)
21
+
22
+ # 需要计算属性
23
+ self.response_model = None
24
+ self.response_model_list = None
25
+ self.validate_response_function = None # 可用于判断是否有多个返回值
26
+ self.extra_function_params = {} # 额外请求参数,比如session,每个请求中都隐含
27
+ self.extra_function_params_when_get = {} # get请求的额外参数,比如response_model, session
28
+ self.extra_function_params_when_get_list = {} # get请求列表的额外参数,比如skip, limit, response_model, session
29
+
30
+ # 计算属性
31
+ self._init_import_stmt()
32
+ self._init_variable() # 会同步计算response_model、response_model_list和validate_response_model
33
+ self._init_properties()
34
+ # 检查是否启用oauth2
35
+ enable_api_oauth2(self)
36
+ self._init_method()
37
+
38
+ def _init_method(self):
39
+ for function_block in self.crud.function_blocks.values():
40
+ method = self.get_app_method(function_block.option)
41
+ model_id_name = camel_to_snake(self.model.class_name) + '_id'
42
+ url = f'/{plural(camel_to_snake(self.model.class_name))}' if function_block.return_list else f'/{camel_to_snake(self.model.class_name)}'
43
+ if not function_block.return_list and model_id_name in function_block.params.keys():
44
+ url += '/{' + model_id_name + '}'
45
+ if function_block.query_name:
46
+ url += '/' + camel_to_snake(function_block.query_name)
47
+ elif function_block.schema_additional_name:
48
+ url += '/' + camel_to_snake(function_block.schema_additional_name)
49
+ elif function_block.sub_object_name:
50
+ url += '/' + camel_to_snake(function_block.sub_object_name)
51
+ app_func = AppFunction(function_block.name, self, method, url)
52
+ app_func.params = {k: v for k, v in function_block.params.items() if
53
+ k not in self.extra_function_params.keys()}
54
+ for k, v in function_block.params.items():
55
+ if k in self.extra_function_params.keys():
56
+ continue
57
+ app_func.params[k] = Param(k, parent=self, default=v.default, nullable=v.nullable,
58
+ type=v.type if not v.schema_class_block else v.schema_class_block.name)
59
+
60
+ app_func.content = f'crud.{function_block.name}(' + ', '.join(
61
+ [f'{k}={v.code}' for k, v in function_block.params.items()]) + ')'
62
+ if function_block.return_list:
63
+ app_func.reponse_model = self.response_model_list
64
+ app_func.params.update(self.extra_function_params_when_get_list)
65
+ if self.validate_response_function:
66
+ app_func.content = f'return [response_model.from_orm({camel_to_snake(self.model.class_name)}) for {camel_to_snake(self.model.class_name)} in {app_func.content}]'
67
+ else:
68
+ app_func.content = f'return {app_func.content}'
69
+ elif method != 'delete':
70
+ app_func.reponse_model = self.response_model
71
+ app_func.params.update(self.extra_function_params_when_get)
72
+ if self.validate_response_function:
73
+ app_func.content = f'return response_model.from_orm({app_func.content})'
74
+ else:
75
+ app_func.content = f'return {app_func.content}'
76
+ else:
77
+ app_func.params.update(self.extra_function_params)
78
+ app_func.content += '\n'
79
+ self.function_blocks[app_func.name] = app_func
80
+ ...
81
+
82
+ def get_app_method(self, option):
83
+ if option == 'get':
84
+ return 'get'
85
+ if option == 'create':
86
+ return 'post'
87
+ if option == 'update':
88
+ return 'put'
89
+ if option == 'delete':
90
+ return 'delete'
91
+
92
+ def _init_properties(self):
93
+ # 要先初始化_init_variable,确保validate_response_function已经被正确设置
94
+ self.extra_function_params['session'] = Param('session', parent=self, type='Session',
95
+ default='Depends(get_session)')
96
+ if self.validate_response_function:
97
+ self.extra_function_params_when_get['response_model'] = Param('response_model', parent=self,
98
+ type='BaseModel',
99
+ default=f'Depends({self.validate_response_function})')
100
+ self.extra_function_params_when_get.update(self.extra_function_params)
101
+ # self.extra_function_params_when_get_list['skip'] = Param('skip', parent=self, type='int', default=0)
102
+ # self.extra_function_params_when_get_list['limit'] = Param('limit', parent=self, type='int', default=50)
103
+ self.extra_function_params_when_get_list.update(self.extra_function_params_when_get)
104
+
105
+ def _init_variable(self):
106
+ self.variables['app'] = Variable('app', parent=self, default='FastAPI()')
107
+ response_class_blocks = []
108
+ for class_block in self.schema.class_blocks.values():
109
+ if class_block.method == 'response':
110
+ response_class_blocks.append(class_block)
111
+ # 所有的schema_additional
112
+ if self.schema_additional:
113
+ for class_block in self.schema_additional.class_blocks.values():
114
+ if class_block.method == 'response':
115
+ response_class_blocks.append(class_block)
116
+ if len(response_class_blocks) == 1:
117
+ self.response_model = response_class_blocks[0].name
118
+ self.response_model_list = f'list[{response_class_blocks[0].name}]'
119
+ elif len(response_class_blocks) > 1:
120
+ self.response_model = f'ALL_{self.model.class_name.upper()}_RESPONSE_MODEL'
121
+ self.response_model_list = f'ALL_{self.model.class_name.upper()}_LIST_RESPONSE_MODEL'
122
+ self.variables[self.response_model] = Variable(self.response_model, parent=self, type='Union',
123
+ default=f'Union[{", ".join([response_class_block.name for response_class_block in response_class_blocks])}]')
124
+ self.variables[self.response_model_list] = Variable(self.response_model_list, parent=self, type='Union',
125
+ default=f'Union[{", ".join(["list[" + response_class_block.name + "]" for response_class_block in response_class_blocks])}]')
126
+ self._add_validate_response_model_function(response_class_blocks)
127
+
128
+ def _add_validate_response_model_function(self, response_class_blocks):
129
+ function_name = f'validate_{self.model.class_name.lower()}_response_model'
130
+ function = FunctionBlock(function_name, parent=self)
131
+ request_allow = [f"'{camel_to_snake(response_class_block.name)}'" for response_class_block in
132
+ response_class_blocks]
133
+ param_default = f"Query(None, description=\"指定返回的数据类型,默认为'{camel_to_snake(self.model.class_name)}'\", enum=[{', '.join(request_allow)}])"
134
+ function.params['response_model'] = Param('response_model', parent=function, type='str', default=param_default)
135
+ function.content = f"""if response_model and response_model not in [{', '.join(request_allow)}]:
136
+ raise HTTPException(status_code=400,
137
+ detail="Invalid response_model. Must in {', '.join(request_allow)}.")
138
+ """
139
+ for class_block in response_class_blocks:
140
+ function.content += f"if response_model == '{camel_to_snake(class_block.name)}':\n"
141
+ function.content += f" return {class_block.name}\n"
142
+ function.content += f"return {self.model.class_name}\n"
143
+
144
+ self.validate_response_function = function_name
145
+ self.function_blocks[function_name] = function
146
+
147
+ def _init_import_stmt(self):
148
+ self.import_stmt.add_import('fastapi', 'FastAPI')
149
+ self.import_stmt.add_import('fastapi', 'Depends')
150
+ self.import_stmt.add_import('pydantic', 'BaseModel')
151
+ self.import_stmt.add_import(ctx_instance.base.package, ctx_instance.base.class_name)
152
+ self.import_stmt.add_import(f'{ctx_instance.project_package}.db', 'Session')
153
+ self.import_stmt.add_import(f'{ctx_instance.project_package}.db', 'get_session')
154
+ self.import_stmt.add_import(from_=None, import_=self.crud.package, as_='crud')
155
+ response_model_count = 0
156
+ # 所有的schema
157
+ for class_block in self.schema.class_blocks.values():
158
+ if class_block.method in ['response', 'create', 'update']:
159
+ self.import_stmt.add_import(self.schema.package, class_block.name)
160
+ for attribute in class_block.attributes.values():
161
+ if attribute.nullable:
162
+ self.import_stmt.add_import('typing', 'Optional')
163
+ if class_block.method == 'response':
164
+ response_model_count += 1
165
+ # 所有的schema_additional
166
+ if self.schema_additional:
167
+ for class_block in self.schema_additional.class_blocks.values():
168
+ self.import_stmt.add_import(self.schema_additional.package, class_block.name)
169
+ for attribute in class_block.attributes.values():
170
+ if attribute.nullable:
171
+ self.import_stmt.add_import('typing', 'Optional')
172
+ if class_block.method == 'response':
173
+ response_model_count += 1
174
+ if response_model_count > 1:
175
+ self.import_stmt.add_import('typing', 'Union')
176
+ self.import_stmt.add_import('fastapi', 'Query')
177
+ self.import_stmt.add_import('fastapi', 'HTTPException')
@@ -0,0 +1,49 @@
1
+ # -*- coding: utf-8 -*-
2
+ import os
3
+
4
+ from fastcodedog.api.api import Api
5
+ from fastcodedog.api.config import Config
6
+ from fastcodedog.api.db import Db
7
+ from fastcodedog.api.main import Main
8
+ from fastcodedog.api.oauth import OAuth
9
+ from fastcodedog.context.context_instance import ctx_instance
10
+ from fastcodedog.generation.package import Package
11
+ from fastcodedog.util.case_converter import camel_to_snake
12
+
13
+
14
+ class ApiPackage(Package):
15
+ def __init__(self):
16
+ self.package = f'{ctx_instance.project_package}.api'
17
+ self.dir = os.path.join(ctx_instance.project_dir, 'api')
18
+
19
+ self.config_dir = os.path.join(ctx_instance.project_dir, 'config')
20
+
21
+ # 固定的文件
22
+ self.config = Config(os.path.join(self.config_dir, 'config.py'), f'{ctx_instance.project_package}.config')
23
+ self.db = Db(os.path.join(ctx_instance.project_dir, 'db.py'), f'{ctx_instance.project_package}.db')
24
+ self.main = Main(os.path.join(ctx_instance.project_dir, 'main.py'), f'{ctx_instance.project_package}.main')
25
+ self.oauth = OAuth(os.path.join(self.dir, 'oauth', 'oauth.py'),
26
+ f'{self.package}.oauth.oauth') if ctx_instance.oauth2.enabled else None
27
+ # 计算属性
28
+ self.apis = {}
29
+
30
+ for code, crud in ctx_instance.crud_package.cruds.items():
31
+ table = crud.schema.model.table
32
+ path = os.path.join(self.dir, table.module, self.get_file_name(table))
33
+ package = f'{self.package}.{table.module}.{camel_to_snake(self.get_class_name(table))}'
34
+ api = Api(crud, path, package)
35
+ self.apis[code] = api
36
+
37
+ for api in self.apis.values():
38
+ self.main.add_api(api)
39
+ if self.oauth:
40
+ self.main.add_api(self.oauth, as_='oauth_app')
41
+
42
+ def save(self):
43
+ self.config.save()
44
+ self.db.save()
45
+ for api in self.apis.values():
46
+ api.save()
47
+ self.main.save()
48
+ if self.oauth:
49
+ self.oauth.save()
@@ -0,0 +1,22 @@
1
+ # -*- coding: utf-8 -*-
2
+ from fastcodedog.generation.function_block import FunctionBlock
3
+ from fastcodedog.util.indent import add_indent
4
+
5
+
6
+ class AppFunction(FunctionBlock):
7
+ def __init__(self, name, parent, method, url, reponse_model=None, response_model_exclude_none=True, params=None):
8
+ super().__init__(name, parent, params)
9
+ self.method = method
10
+ self.url = url
11
+ self.reponse_model = reponse_model
12
+ self.response_model_exclude_none = response_model_exclude_none
13
+
14
+ def serialize(self, indent=''):
15
+ app_params = [f"'{self.url}'"]
16
+ if self.reponse_model:
17
+ app_params.append(f'response_model={self.reponse_model}')
18
+ if self.response_model_exclude_none:
19
+ app_params.append('response_model_exclude_none=True')
20
+ content = f'@app.{self.method}({", ".join(app_params)})\n'
21
+ content += super().serialize()
22
+ return add_indent(content, indent)
@@ -0,0 +1,68 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from fastcodedog.generation.file import File
4
+ from fastcodedog.generation.function_block import FunctionBlock
5
+ from fastcodedog.generation.variable import Variable
6
+
7
+
8
+ class Config(File):
9
+ def __init__(self, path, package, desc=None):
10
+ super().__init__(path, package, desc)
11
+ self.config_file = path[:-3] + '.ini'
12
+ self.config_content = self._get_config_content()
13
+ self._init_import_stmt()
14
+ self._init_funciton()
15
+ self._init_variables()
16
+
17
+ def _init_variables(self):
18
+ self.variables['_all_configs_'] = Variable('_all_configs_', self, type=dict, default={})
19
+ self.variables['listion_port'] = Variable('listion_port', self, type='function',
20
+ default="get_config('app', 'port', 8000)")
21
+ self.variables['db_url'] = Variable('db_url', self, type='function',
22
+ default="get_config('database', 'url', None)")
23
+
24
+ def _init_funciton(self):
25
+ func = FunctionBlock('get_config', self)
26
+ func.params['section'] = Variable('section', func)
27
+ func.params['key'] = Variable('key', func)
28
+ func.params['default'] = Variable('default', func, default=None)
29
+ func.content = f"""c_key = f'{{section}}.{{key}}'
30
+ if c_key not in _all_configs_:
31
+ confif_file = os.path.join(os.path.dirname(__file__), 'config.ini')
32
+ config = configparser.ConfigParser()
33
+ config.read(confif_file)
34
+ _all_configs_[c_key] = None
35
+ if section in config and config.get(section, key):
36
+ _all_configs_[c_key] = config.get(section, key)
37
+ if c_key in _all_configs_ and _all_configs_[c_key] is not None:
38
+ return _all_configs_[c_key]
39
+ return default
40
+ """
41
+ self.function_blocks['get_config'] = func
42
+
43
+ def _init_import_stmt(self):
44
+ self.import_stmt.add_import(from_=None, import_='configparser')
45
+ self.import_stmt.add_import(from_=None, import_='os')
46
+
47
+ def _get_config_content(self):
48
+ return """[app]
49
+ port=8000
50
+
51
+ [database]
52
+ url=postgresql://ccuser:Cc_12345678@192.168.44.128:15432/ccdb
53
+ """
54
+
55
+ def serialize(self):
56
+ content = self.annotation_stmt.serialize()
57
+ content += self.import_stmt.serialize()
58
+ content += '\n'
59
+ content += self.serialize_function_blocks()
60
+ content += self.serialize_variable_stmt()
61
+ content += self.serialize_class_blocks()
62
+ return content
63
+
64
+ def save(self):
65
+ super().save()
66
+ # 写一份配置文件
67
+ with open(self.config_file, 'w', encoding='utf-8') as f:
68
+ f.write(self.config_content)
@@ -0,0 +1,31 @@
1
+ # -*- coding: utf-8 -*-
2
+ from fastcodedog.generation.file import File
3
+ from fastcodedog.generation.function_block import FunctionBlock
4
+ from fastcodedog.generation.variable import Variable
5
+
6
+
7
+ class Db(File):
8
+ def __init__(self, path, package, desc=None):
9
+ super().__init__(path, package, desc)
10
+ self._init_import_stmt()
11
+ self._init_funciton()
12
+ self._init_variables()
13
+
14
+ def _init_import_stmt(self):
15
+ self.import_stmt.add_import('sqlalchemy', 'create_engine')
16
+ self.import_stmt.add_import('sqlalchemy.orm', 'sessionmaker')
17
+ self.import_stmt.add_import('.config.config', 'db_url')
18
+
19
+ def _init_variables(self):
20
+ self.variables['engine'] = Variable('engine', self, type='function', default="create_engine(db_url)")
21
+ self.variables['Session'] = Variable('Session', self, type='function', default="sessionmaker(bind=engine)")
22
+
23
+ def _init_funciton(self):
24
+ func = FunctionBlock('get_session', self)
25
+ func.content = f"""session = Session()
26
+ try:
27
+ yield session
28
+ finally:
29
+ session.close()
30
+ """
31
+ self.function_blocks[func.name] = func
@@ -0,0 +1,60 @@
1
+ # -*- coding: utf-8 -*-
2
+ from fastcodedog.context.context_instance import ctx_instance
3
+ from fastcodedog.generation.file import File
4
+ from fastcodedog.generation.variable import Variable
5
+ from fastcodedog.util.case_converter import camel_to_snake
6
+
7
+
8
+ class Main(File):
9
+ def __init__(self, path, package, desc=None):
10
+ super().__init__(path, package, desc)
11
+
12
+ self.script_lines = []
13
+ self.main_content = """if __name__ == "__main__":
14
+ uvicorn.run(app, host="0.0.0.0", port=8000)
15
+ """
16
+ self._init_import_stmt()
17
+ self._init_variables()
18
+ self._init_script_lines()
19
+ self._init_extended_apps()
20
+
21
+ def _init_extended_apps(self):
22
+ for api_name, api_package in ctx_instance.extended_apps.items():
23
+ self.import_stmt.add_import(api_package, 'app', f'{api_name}_app')
24
+ self.script_lines.append(f'app.include_router({api_name}_app.router)')
25
+
26
+ def _init_script_lines(self):
27
+ self.script_lines.extend("""@app.exception_handler(SQLAlchemyError)
28
+ async def sqlalchemy_exception_handler(request, exc):
29
+ raise HTTPException(
30
+ status_code=HTTP_500_INTERNAL_SERVER_ERROR,
31
+ detail=f"Database operation failed: {exc}",
32
+ )
33
+ """.splitlines())
34
+ self.script_lines.append('logging.basicConfig()')
35
+ self.script_lines.append("logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)")
36
+
37
+ def _init_variables(self):
38
+ self.variables['app'] = Variable('app', self, default='FastAPI()')
39
+
40
+ def _init_import_stmt(self):
41
+ self.import_stmt.add_import(None, 'uvicorn')
42
+ self.import_stmt.add_import(None, 'logging')
43
+ self.import_stmt.add_import('fastapi', 'FastAPI')
44
+ self.import_stmt.add_import('fastapi', 'HTTPException')
45
+ self.import_stmt.add_import('sqlalchemy.exc', 'SQLAlchemyError')
46
+ self.import_stmt.add_import('starlette.status', 'HTTP_500_INTERNAL_SERVER_ERROR')
47
+
48
+ def add_api(self, api, as_=None):
49
+ if not as_:
50
+ as_ = f'{camel_to_snake(api.model.class_name)}_app'
51
+ self.import_stmt.add_import(api.package, 'app', as_)
52
+ self.script_lines.append(f'app.include_router({as_}.router)')
53
+
54
+ def serialize(self):
55
+ content = super().serialize()
56
+ for line in self.script_lines:
57
+ content += line + '\n'
58
+ content += '\n'
59
+ content += self.main_content
60
+ return content
@@ -0,0 +1,120 @@
1
+ # -*- coding: utf-8 -*-
2
+ from fastcodedog.api.app_function import AppFunction
3
+ from fastcodedog.context.context_instance import ctx_instance
4
+ from fastcodedog.crud.param import Param
5
+ from fastcodedog.generation.file import File
6
+ from fastcodedog.generation.variable import Variable
7
+
8
+
9
+ def enable_api_oauth2(cls):
10
+ if ctx_instance.oauth2.enabled:
11
+ cls.import_stmt.add_import('fastapi.security', 'OAuth2PasswordBearer')
12
+ cls.import_stmt.add_import('typing', 'Annotated')
13
+ cls.variables['oauth2_scheme '] = Variable('oauth2_scheme ', cls,
14
+ default="OAuth2PasswordBearer(tokenUrl='/oauth/token')")
15
+ cls.extra_function_params['token'] = Param('token', parent=cls, type='Annotated[str, Depends(oauth2_scheme)]',
16
+ nullable=False)
17
+ cls.extra_function_params_when_get['token'] = Param('token', parent=cls,
18
+ type='Annotated[str, Depends(oauth2_scheme)]',
19
+ nullable=False)
20
+ cls.extra_function_params_when_get_list['token'] = Param('token', parent=cls,
21
+ type='Annotated[str, Depends(oauth2_scheme)]',
22
+ nullable=False)
23
+
24
+
25
+ class OAuth(File):
26
+ def __init__(self, path, package, desc=None):
27
+ desc = """
28
+ OAuth2.SECRET_KEY =
29
+ OAuth2.EXPIRE_SECONDS = 60*60*2
30
+ OAuth2.REDIS_URL = 'redis://localhost:6379/oauth2'
31
+ """
32
+ super().__init__(path, package, desc)
33
+
34
+ self.user_model = ctx_instance.model_package.models.get(ctx_instance.oauth2.user_table)
35
+ self.tenant_model = ctx_instance.model_package.models.get(
36
+ ctx_instance.tenant.tenant_table_code) if ctx_instance.tenant.enabled else None
37
+ self.param_session = Param('session', parent=self, type='Session', default='Depends(get_session)')
38
+ self.param_token = Param('token', parent=self, type='Annotated[str, Depends(oauth2_scheme)]', nullable=False)
39
+ self._init_import_stmt()
40
+ self._init_variables()
41
+ self._init_functions()
42
+
43
+ def _init_functions(self):
44
+ self._init_login_function()
45
+ self._init_remove_token_function()
46
+ self._init_refresh_token_function()
47
+ self._init_read_user_me_function()
48
+
49
+ def _init_read_user_me_function(self):
50
+ app_func = AppFunction('read_users_me', self, 'get', '/oauth/me')
51
+ app_func.params['token'] = self.param_token
52
+ app_func.content = 'return OAuth2.get_token_user(token)\n'
53
+ self.function_blocks[app_func.name] = app_func
54
+
55
+ def _init_refresh_token_function(self):
56
+ app_func = AppFunction('refresh_token', self, 'post', '/oauth/refresh_token', reponse_model='Token')
57
+ app_func.params['token'] = self.param_token
58
+ app_func.content = 'return OAuth2.refresh_token(token)\n'
59
+ self.function_blocks[app_func.name] = app_func
60
+
61
+ def _init_remove_token_function(self):
62
+ app_func = AppFunction('remove_token', self, 'post', '/oauth/logout')
63
+ app_func.params['token'] = self.param_token
64
+ app_func.content = 'OAuth2.remove_token(token)\n'
65
+ app_func.content += 'return {}\n'
66
+ self.function_blocks[app_func.name] = app_func
67
+
68
+ def _init_login_function(self):
69
+ app_func = AppFunction('login_for_access_token', self, 'post', '/oauth/token', reponse_model='Token')
70
+ app_func.params['form_data'] = Param('form_data', parent=self,
71
+ type='Annotated[OAuth2PasswordRequestForm, Depends()]', nullable=False)
72
+ app_func.params['session'] = self.param_session
73
+ app_func.content = 'username = form_data.username\n'
74
+ app_func.content += 'password = form_data.password\n'
75
+ if ctx_instance.tenant.enabled:
76
+ app_func.content += "if username.find('@') == -1:\n"
77
+ app_func.content += ' raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username. Must contain \'@\'")\n'
78
+ app_func.content += 'domain = username.split(\'@\')[1]\n'
79
+ app_func.content += 'username = username.split(\'@\')[0]\n'
80
+ app_func.content += (f'user = session.query({self.user_model.class_name})'
81
+ f'.join({self.tenant_model.class_name}, {self.user_model.class_name}.{ctx_instance.tenant.tenant_column}=={self.tenant_model.class_name}.id)'
82
+ f'.filter({self.user_model.class_name}.{ctx_instance.oauth2.user_name_field}==username)'
83
+ f'.filter({self.user_model.class_name}.{ctx_instance.oauth2.user_password_field}==password)'
84
+ f'.filter({self.tenant_model.class_name}.domain==domain)'
85
+ f'.first()\n')
86
+ else:
87
+ app_func.content += (f'user = session.query({self.user_model.class_name})'
88
+ f'.filter({self.user_model.class_name}.{ctx_instance.oauth2.user_name_field}==username)'
89
+ f'.filter({self.user_model.class_name}.{ctx_instance.oauth2.user_password_field}==password)'
90
+ f'.first()\n')
91
+ app_func.content += 'if user:\n'
92
+ token_content = ', '.join([f'"{k}": user.{k}' for k in self.user_model.table.get_primary_keys()])
93
+ token_content += f', "{ctx_instance.oauth2.user_name_field}": username'
94
+ if ctx_instance.tenant.enabled:
95
+ token_content += f', "{ctx_instance.tenant.tenant_column}": user.{ctx_instance.tenant.tenant_column}'
96
+ token_content = '{' + token_content + '}'
97
+ app_func.content += f' return OAuth2.create_access_token({token_content})\n'
98
+ app_func.content += 'raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password")\n'
99
+ self.function_blocks[app_func.name] = app_func
100
+
101
+ def _init_variables(self):
102
+ self.variables['app'] = Variable('app', self, default="FastAPI()")
103
+ self.variables['oauth2_scheme '] = Variable('oauth2_scheme ', self,
104
+ default="OAuth2PasswordBearer(tokenUrl='/oauth/token')")
105
+
106
+ def _init_import_stmt(self):
107
+ self.import_stmt.add_import('typing', 'Annotated')
108
+ self.import_stmt.add_import('fastoauth', 'OAuth2')
109
+ self.import_stmt.add_import('fastoauth', 'Token')
110
+ self.import_stmt.add_import('fastapi', 'Depends')
111
+ self.import_stmt.add_import('fastapi', 'FastAPI')
112
+ self.import_stmt.add_import('fastapi', 'HTTPException')
113
+ self.import_stmt.add_import('fastapi', 'status')
114
+ self.import_stmt.add_import('fastapi.security', 'OAuth2PasswordBearer')
115
+ self.import_stmt.add_import('fastapi.security', 'OAuth2PasswordRequestForm')
116
+ self.import_stmt.add_import(f'{ctx_instance.project_package}.db', 'Session')
117
+ self.import_stmt.add_import(f'{ctx_instance.project_package}.db', 'get_session')
118
+ self.import_stmt.add_import(self.user_model.package, self.user_model.class_name)
119
+ if self.tenant_model:
120
+ self.import_stmt.add_import(self.tenant_model.package, self.tenant_model.class_name)
@@ -0,0 +1,37 @@
1
+ # -*- coding: utf-8 -*-
2
+ import sys
3
+
4
+ from fastcodedog.api.api_package import ApiPackage
5
+ from fastcodedog.context.context_instance import ctx_instance
6
+ from fastcodedog.crud.crud_package import CrudPackage
7
+ from fastcodedog.diagram.diagram import Diagram
8
+ from fastcodedog.model.model_package import ModelPackage
9
+ from fastcodedog.schema.schema_additional_package import SchemaAdditionalPackage
10
+ from fastcodedog.schema.schema_package import SchemaPackage
11
+
12
+
13
+ def main():
14
+ if len(sys.argv) == 1:
15
+ raise ValueError('缺少参数,请传入json5配置文件或者pdm文件')
16
+ ctx_instance.load_config(*(sys.argv[1:]))
17
+ ctx_instance.diagram = Diagram()
18
+ ctx_instance.diagram.load()
19
+ ctx_instance.model_package = ModelPackage()
20
+ ctx_instance.model_package.save()
21
+ ctx_instance.schema_package = SchemaPackage()
22
+ ctx_instance.schema_package.save()
23
+ ctx_instance.schema_additional_package = SchemaAdditionalPackage()
24
+ ctx_instance.schema_additional_package.save()
25
+ ctx_instance.crud_package = CrudPackage()
26
+ ctx_instance.crud_package.save()
27
+ ctx_instance.api_package = ApiPackage()
28
+ ctx_instance.api_package.save()
29
+
30
+
31
+ if __name__ == '__main__':
32
+ """
33
+ $env:PYTHONPATH = "."
34
+ python fastcodedog/cli.py fastcodegen/test/fastframe.diagram.json5 fastcodegen/test/fastframe.model.json5
35
+ """
36
+ main()
37
+
@@ -0,0 +1 @@
1
+ # -*- coding: utf-8 -*-
@@ -0,0 +1,9 @@
1
+ # -*- coding: utf-8 -*-
2
+ from .contextbase import ContextBase
3
+
4
+
5
+ class Base(ContextBase):
6
+ def __init__(self):
7
+ super().__init__()
8
+ self.class_name = None
9
+ self.package = None
@@ -0,0 +1,32 @@
1
+ # -*- coding: utf-8 -*-
2
+ from .base import Base
3
+ from .contextbase import ContextBase
4
+ from .modules import Modules
5
+ from .oauth2 import OAuth2
6
+ from .schema import Schema
7
+ from .tenant import Tenant
8
+
9
+
10
+ class Context(ContextBase):
11
+ def __init__(self):
12
+ super().__init__()
13
+ self.pdm_files = []
14
+ self.project_name = None
15
+ self.author = None
16
+ self.project_package = None
17
+ self.project_dir = None
18
+ # diagram的配置,这些配置也会在后续的生成代码中使用
19
+ self.modules = Modules()
20
+ self.tenant = Tenant()
21
+ # model的配置
22
+ self.base = Base()
23
+ # schema的配置
24
+ self.schema = Schema()
25
+ self.response_schemas = {}
26
+ self.create_schemas = {}
27
+ self.update_schemas = {}
28
+ # crud的配置
29
+ self.queries = {}
30
+ # api的配置
31
+ self.extended_apps = {}
32
+ self.oauth2 = OAuth2()