@apso/cli 0.7.4 → 0.8.2

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 (203) hide show
  1. package/dist/autogen/index.d.ts +2 -0
  2. package/dist/autogen/index.js +3 -0
  3. package/dist/commands/server/new.d.ts +4 -1
  4. package/dist/commands/server/new.js +115 -22
  5. package/dist/commands/server/scaffold.d.ts +10 -0
  6. package/dist/commands/server/scaffold.js +200 -10
  7. package/dist/lib/api/client.d.ts +65 -0
  8. package/dist/lib/api/client.js +305 -0
  9. package/dist/lib/api/index.d.ts +8 -0
  10. package/dist/lib/api/index.js +22 -0
  11. package/dist/lib/api/services.d.ts +166 -0
  12. package/dist/lib/api/services.js +202 -0
  13. package/dist/lib/api/types.d.ts +212 -0
  14. package/dist/lib/api/types.js +7 -0
  15. package/dist/lib/apsorc-parser.d.ts +3 -0
  16. package/dist/lib/apsorc-parser.js +4 -0
  17. package/dist/lib/config/index.d.ts +8 -0
  18. package/dist/lib/config/index.js +17 -0
  19. package/dist/lib/config/manager.d.ts +127 -0
  20. package/dist/lib/config/manager.js +304 -0
  21. package/dist/lib/config/paths.d.ts +65 -0
  22. package/dist/lib/config/paths.js +127 -0
  23. package/dist/lib/config/types.d.ts +230 -0
  24. package/dist/lib/config/types.js +30 -0
  25. package/dist/lib/generators/base.d.ts +93 -0
  26. package/dist/lib/generators/base.js +90 -0
  27. package/dist/lib/generators/go.d.ts +37 -0
  28. package/dist/lib/generators/go.js +340 -0
  29. package/dist/lib/generators/index.d.ts +21 -0
  30. package/dist/lib/generators/index.js +51 -0
  31. package/dist/lib/generators/python.d.ts +21 -0
  32. package/dist/lib/generators/python.js +253 -0
  33. package/dist/lib/generators/typescript.d.ts +25 -0
  34. package/dist/lib/generators/typescript.js +373 -0
  35. package/dist/lib/index.d.ts +2 -0
  36. package/dist/lib/index.js +9 -1
  37. package/dist/lib/templates/entities/entity-col-timestamp.eta +9 -2
  38. package/dist/lib/templates/entities/entity-col-timestamptz.eta +9 -2
  39. package/dist/lib/templates/entities/entity.eta +18 -5
  40. package/dist/lib/templates/go/dto/dto-col-array.eta +7 -0
  41. package/dist/lib/templates/go/dto/dto-col-boolean.eta +7 -0
  42. package/dist/lib/templates/go/dto/dto-col-date.eta +7 -0
  43. package/dist/lib/templates/go/dto/dto-col-decimal.eta +7 -0
  44. package/dist/lib/templates/go/dto/dto-col-enum.eta +8 -0
  45. package/dist/lib/templates/go/dto/dto-col-float.eta +7 -0
  46. package/dist/lib/templates/go/dto/dto-col-integer.eta +7 -0
  47. package/dist/lib/templates/go/dto/dto-col-json.eta +7 -0
  48. package/dist/lib/templates/go/dto/dto-col-jsonb.eta +7 -0
  49. package/dist/lib/templates/go/dto/dto-col-numeric.eta +7 -0
  50. package/dist/lib/templates/go/dto/dto-col-string.eta +7 -0
  51. package/dist/lib/templates/go/dto/dto-col-text.eta +7 -0
  52. package/dist/lib/templates/go/dto/dto-col-timestamp.eta +7 -0
  53. package/dist/lib/templates/go/dto/dto-col-timestamptz.eta +7 -0
  54. package/dist/lib/templates/go/dto/dto-col-uuid.eta +7 -0
  55. package/dist/lib/templates/go/dto/dto-col-varchar.eta +7 -0
  56. package/dist/lib/templates/go/dto/dto.eta +78 -0
  57. package/dist/lib/templates/go/enums.eta +44 -0
  58. package/dist/lib/templates/go/handlers/handler.eta +212 -0
  59. package/dist/lib/templates/go/header.eta +3 -0
  60. package/dist/lib/templates/go/index-module.eta +53 -0
  61. package/dist/lib/templates/go/middleware/auth.eta +101 -0
  62. package/dist/lib/templates/go/models/model-col-array.eta +1 -0
  63. package/dist/lib/templates/go/models/model-col-boolean.eta +1 -0
  64. package/dist/lib/templates/go/models/model-col-date.eta +1 -0
  65. package/dist/lib/templates/go/models/model-col-decimal.eta +1 -0
  66. package/dist/lib/templates/go/models/model-col-enum.eta +2 -0
  67. package/dist/lib/templates/go/models/model-col-float.eta +1 -0
  68. package/dist/lib/templates/go/models/model-col-integer.eta +1 -0
  69. package/dist/lib/templates/go/models/model-col-json.eta +1 -0
  70. package/dist/lib/templates/go/models/model-col-jsonb.eta +1 -0
  71. package/dist/lib/templates/go/models/model-col-numeric.eta +1 -0
  72. package/dist/lib/templates/go/models/model-col-string.eta +1 -0
  73. package/dist/lib/templates/go/models/model-col-text.eta +1 -0
  74. package/dist/lib/templates/go/models/model-col-timestamp.eta +1 -0
  75. package/dist/lib/templates/go/models/model-col-timestamptz.eta +1 -0
  76. package/dist/lib/templates/go/models/model-col-uuid.eta +1 -0
  77. package/dist/lib/templates/go/models/model-col-varchar.eta +1 -0
  78. package/dist/lib/templates/go/models/model.eta +53 -0
  79. package/dist/lib/templates/go/services/service.eta +190 -0
  80. package/dist/lib/templates/python/enums.eta +10 -0
  81. package/dist/lib/templates/python/header.eta +6 -0
  82. package/dist/lib/templates/python/index-module.eta +18 -0
  83. package/dist/lib/templates/python/models/model-col-array.eta +4 -0
  84. package/dist/lib/templates/python/models/model-col-boolean.eta +5 -0
  85. package/dist/lib/templates/python/models/model-col-date.eta +4 -0
  86. package/dist/lib/templates/python/models/model-col-decimal.eta +4 -0
  87. package/dist/lib/templates/python/models/model-col-enum.eta +16 -0
  88. package/dist/lib/templates/python/models/model-col-float.eta +5 -0
  89. package/dist/lib/templates/python/models/model-col-integer.eta +6 -0
  90. package/dist/lib/templates/python/models/model-col-json.eta +4 -0
  91. package/dist/lib/templates/python/models/model-col-jsonb.eta +4 -0
  92. package/dist/lib/templates/python/models/model-col-numeric.eta +4 -0
  93. package/dist/lib/templates/python/models/model-col-string.eta +6 -0
  94. package/dist/lib/templates/python/models/model-col-text.eta +6 -0
  95. package/dist/lib/templates/python/models/model-col-timestamp.eta +11 -0
  96. package/dist/lib/templates/python/models/model-col-timestamptz.eta +11 -0
  97. package/dist/lib/templates/python/models/model-col-uuid.eta +5 -0
  98. package/dist/lib/templates/python/models/model-col-varchar.eta +6 -0
  99. package/dist/lib/templates/python/models/model.eta +124 -0
  100. package/dist/lib/templates/python/routers/router.eta +206 -0
  101. package/dist/lib/templates/python/schemas/schema-col-array.eta +1 -0
  102. package/dist/lib/templates/python/schemas/schema-col-boolean.eta +1 -0
  103. package/dist/lib/templates/python/schemas/schema-col-date.eta +1 -0
  104. package/dist/lib/templates/python/schemas/schema-col-decimal.eta +1 -0
  105. package/dist/lib/templates/python/schemas/schema-col-enum.eta +6 -0
  106. package/dist/lib/templates/python/schemas/schema-col-float.eta +1 -0
  107. package/dist/lib/templates/python/schemas/schema-col-integer.eta +1 -0
  108. package/dist/lib/templates/python/schemas/schema-col-json.eta +1 -0
  109. package/dist/lib/templates/python/schemas/schema-col-jsonb.eta +1 -0
  110. package/dist/lib/templates/python/schemas/schema-col-numeric.eta +1 -0
  111. package/dist/lib/templates/python/schemas/schema-col-string.eta +1 -0
  112. package/dist/lib/templates/python/schemas/schema-col-text.eta +1 -0
  113. package/dist/lib/templates/python/schemas/schema-col-timestamp.eta +1 -0
  114. package/dist/lib/templates/python/schemas/schema-col-timestamptz.eta +1 -0
  115. package/dist/lib/templates/python/schemas/schema-col-uuid.eta +1 -0
  116. package/dist/lib/templates/python/schemas/schema-col-varchar.eta +1 -0
  117. package/dist/lib/templates/python/schemas/schema.eta +87 -0
  118. package/dist/lib/templates/python/services/service.eta +163 -0
  119. package/dist/lib/templates/rest/controller-rest.eta +13 -12
  120. package/dist/lib/templates/rest/dto-rest.eta +173 -59
  121. package/dist/lib/types/generator.d.ts +241 -0
  122. package/dist/lib/types/generator.js +2 -0
  123. package/dist/lib/types/index.d.ts +1 -0
  124. package/dist/lib/utils/casing.d.ts +1 -0
  125. package/dist/lib/utils/casing.js +11 -1
  126. package/oclif.manifest.json +32 -3
  127. package/package.json +4 -2
  128. package/dist/lib/utils/relationships/parse.spec.d.ts +0 -1
  129. package/dist/lib/utils/relationships/parse.spec.js +0 -402
  130. package/dist/tests/templates/entities/types/array-template.spec.d.ts +0 -1
  131. package/dist/tests/templates/entities/types/array-template.spec.js +0 -82
  132. package/dist/tests/templates/entities/types/bigint-template.spec.d.ts +0 -1
  133. package/dist/tests/templates/entities/types/bigint-template.spec.js +0 -82
  134. package/dist/tests/templates/entities/types/boolean-template.spec.d.ts +0 -1
  135. package/dist/tests/templates/entities/types/boolean-template.spec.js +0 -77
  136. package/dist/tests/templates/entities/types/bytea-template.spec.d.ts +0 -1
  137. package/dist/tests/templates/entities/types/bytea-template.spec.js +0 -68
  138. package/dist/tests/templates/entities/types/char-template.spec.d.ts +0 -1
  139. package/dist/tests/templates/entities/types/char-template.spec.js +0 -68
  140. package/dist/tests/templates/entities/types/date-template.spec.d.ts +0 -1
  141. package/dist/tests/templates/entities/types/date-template.spec.js +0 -56
  142. package/dist/tests/templates/entities/types/decimal-field-validation.spec.d.ts +0 -1
  143. package/dist/tests/templates/entities/types/decimal-field-validation.spec.js +0 -100
  144. package/dist/tests/templates/entities/types/decimal-template.spec.d.ts +0 -1
  145. package/dist/tests/templates/entities/types/decimal-template.spec.js +0 -86
  146. package/dist/tests/templates/entities/types/double-template.spec.d.ts +0 -1
  147. package/dist/tests/templates/entities/types/double-template.spec.js +0 -70
  148. package/dist/tests/templates/entities/types/enum-template.spec.d.ts +0 -1
  149. package/dist/tests/templates/entities/types/enum-template.spec.js +0 -85
  150. package/dist/tests/templates/entities/types/float-template.spec.d.ts +0 -1
  151. package/dist/tests/templates/entities/types/float-template.spec.js +0 -70
  152. package/dist/tests/templates/entities/types/geometry-template.spec.d.ts +0 -1
  153. package/dist/tests/templates/entities/types/geometry-template.spec.js +0 -58
  154. package/dist/tests/templates/entities/types/inet-template.spec.d.ts +0 -1
  155. package/dist/tests/templates/entities/types/inet-template.spec.js +0 -69
  156. package/dist/tests/templates/entities/types/int4range-template.spec.d.ts +0 -1
  157. package/dist/tests/templates/entities/types/int4range-template.spec.js +0 -72
  158. package/dist/tests/templates/entities/types/integer-template.spec.d.ts +0 -1
  159. package/dist/tests/templates/entities/types/integer-template.spec.js +0 -79
  160. package/dist/tests/templates/entities/types/interval-template.spec.d.ts +0 -1
  161. package/dist/tests/templates/entities/types/interval-template.spec.js +0 -68
  162. package/dist/tests/templates/entities/types/json-plain-template.spec.d.ts +0 -1
  163. package/dist/tests/templates/entities/types/json-plain-template.spec.js +0 -54
  164. package/dist/tests/templates/entities/types/json-template.spec.d.ts +0 -1
  165. package/dist/tests/templates/entities/types/json-template.spec.js +0 -40
  166. package/dist/tests/templates/entities/types/jsonb-template.spec.d.ts +0 -1
  167. package/dist/tests/templates/entities/types/jsonb-template.spec.js +0 -40
  168. package/dist/tests/templates/entities/types/money-template.spec.d.ts +0 -1
  169. package/dist/tests/templates/entities/types/money-template.spec.js +0 -68
  170. package/dist/tests/templates/entities/types/numeric-template.spec.d.ts +0 -1
  171. package/dist/tests/templates/entities/types/numeric-template.spec.js +0 -84
  172. package/dist/tests/templates/entities/types/point-template.spec.d.ts +0 -1
  173. package/dist/tests/templates/entities/types/point-template.spec.js +0 -64
  174. package/dist/tests/templates/entities/types/polygon-template.spec.d.ts +0 -1
  175. package/dist/tests/templates/entities/types/polygon-template.spec.js +0 -70
  176. package/dist/tests/templates/entities/types/real-template.spec.d.ts +0 -1
  177. package/dist/tests/templates/entities/types/real-template.spec.js +0 -67
  178. package/dist/tests/templates/entities/types/serial-template.spec.d.ts +0 -1
  179. package/dist/tests/templates/entities/types/serial-template.spec.js +0 -113
  180. package/dist/tests/templates/entities/types/smallint-template.spec.d.ts +0 -1
  181. package/dist/tests/templates/entities/types/smallint-template.spec.js +0 -82
  182. package/dist/tests/templates/entities/types/string-template.spec.d.ts +0 -1
  183. package/dist/tests/templates/entities/types/string-template.spec.js +0 -111
  184. package/dist/tests/templates/entities/types/text-template.spec.d.ts +0 -1
  185. package/dist/tests/templates/entities/types/text-template.spec.js +0 -70
  186. package/dist/tests/templates/entities/types/time-template.spec.d.ts +0 -1
  187. package/dist/tests/templates/entities/types/time-template.spec.js +0 -68
  188. package/dist/tests/templates/entities/types/timestamp-template.spec.d.ts +0 -1
  189. package/dist/tests/templates/entities/types/timestamp-template.spec.js +0 -74
  190. package/dist/tests/templates/entities/types/timetz-template.spec.d.ts +0 -1
  191. package/dist/tests/templates/entities/types/timetz-template.spec.js +0 -68
  192. package/dist/tests/templates/entities/types/tsvector-template.spec.d.ts +0 -1
  193. package/dist/tests/templates/entities/types/tsvector-template.spec.js +0 -55
  194. package/dist/tests/templates/entities/types/uuid-template.spec.d.ts +0 -1
  195. package/dist/tests/templates/entities/types/uuid-template.spec.js +0 -82
  196. package/dist/tests/templates/entities/types/varchar-template.spec.d.ts +0 -1
  197. package/dist/tests/templates/entities/types/varchar-template.spec.js +0 -105
  198. package/dist/tests/templates/entities/types/xml-template.spec.d.ts +0 -1
  199. package/dist/tests/templates/entities/types/xml-template.spec.js +0 -55
  200. package/dist/tests/templates/entities/uuid-entity-template.spec.d.ts +0 -1
  201. package/dist/tests/templates/entities/uuid-entity-template.spec.js +0 -277
  202. package/dist/tests/utils/relationship-deduplication.spec.d.ts +0 -1
  203. package/dist/tests/utils/relationship-deduplication.spec.js +0 -98
@@ -0,0 +1,206 @@
1
+ <%~ includeFile('../header.eta', it) %>
2
+
3
+ from typing import List, Optional
4
+ from fastapi import APIRouter, Depends, HTTPException, Query, status
5
+ from sqlalchemy.ext.asyncio import AsyncSession
6
+
7
+ from app.database import get_db
8
+ from ..models.<%= it.entityName.toLowerCase() %> import <%= it.entityName -%>
9
+
10
+ from ..schemas.<%= it.entityName.toLowerCase() %> import (
11
+ <%= it.entityName %> as <%= it.entityName %>Schema,
12
+ <%= it.entityName %>Create,
13
+ <%= it.entityName %>Update,
14
+ <%= it.entityName %>List,
15
+ )
16
+ from ..services.<%= it.entityName.toLowerCase() %> import <%= it.entityName %>Service
17
+
18
+ router = APIRouter(
19
+ prefix="/<%= it.pluralEntityName.toLowerCase() %>",
20
+ tags=["<%= it.pluralEntityName %>"],
21
+ )
22
+
23
+
24
+ @router.get(
25
+ "",
26
+ response_model=<%= it.entityName %>List,
27
+ summary="Retrieve multiple <%= it.pluralEntityName %>",
28
+ )
29
+ async def get_many(
30
+ db: AsyncSession = Depends(get_db),
31
+ skip: int = Query(0, ge=0),
32
+ limit: int = Query(10, ge=1, le=100),
33
+ sort: Optional[str] = Query(None, description="Sort field (prefix with - for descending)"),
34
+ <% /* Add filter parameters for common fields */ %>
35
+ <% it.associations.filter(a => a.type === 'ManyToOne').forEach((assoc) => { %>
36
+ <%= assoc.camelCasedId %>: Optional[<% if (assoc.referencedEntityPrimaryKeyType === 'uuid') { %>str<% } else { %>int<% } %>] = Query(None),
37
+ <% }) %>
38
+ ) -> <%= it.entityName %>List:
39
+ """
40
+ Retrieve a paginated list of <%= it.pluralEntityName %>.
41
+ """
42
+ service = <%= it.entityName %>Service(db)
43
+
44
+ filters = {}
45
+ <% it.associations.filter(a => a.type === 'ManyToOne').forEach((assoc) => { %>
46
+ if <%= assoc.camelCasedId %> is not None:
47
+ filters["<%= assoc.camelCasedId %>"] = <%= assoc.camelCasedId %>
48
+ <% }) %>
49
+
50
+ items, total = await service.get_many(
51
+ skip=skip,
52
+ limit=limit,
53
+ sort=sort,
54
+ filters=filters,
55
+ )
56
+
57
+ return <%= it.entityName %>List(
58
+ data=items,
59
+ total=total,
60
+ page=(skip // limit) + 1 if limit > 0 else 1,
61
+ pageSize=limit,
62
+ totalPages=(total + limit - 1) // limit if limit > 0 else 1,
63
+ )
64
+
65
+
66
+ @router.get(
67
+ "/{id}",
68
+ response_model=<%= it.entityName %>Schema,
69
+ summary="Retrieve a single <%= it.entityName %>",
70
+ )
71
+ async def get_one(
72
+ id: <% if (it.primaryKeyType === 'uuid') { %>str<% } else { %>int<% } %>,
73
+ db: AsyncSession = Depends(get_db),
74
+ <% /* Add join parameters */ %>
75
+ <% it.associations.forEach((assoc) => { %>
76
+ <% if (assoc.type === 'OneToMany' || assoc.type === 'ManyToMany') { %>
77
+ join_<%= assoc.pluralizedRelationshipName %>: bool = Query(False, alias="join[<%= assoc.pluralizedRelationshipName %>]"),
78
+ <% } else { %>
79
+ join_<%= assoc.relationshipName %>: bool = Query(False, alias="join[<%= assoc.relationshipName %>]"),
80
+ <% } %>
81
+ <% }) %>
82
+ ) -> <%= it.entityName %>Schema:
83
+ """
84
+ Retrieve a single <%= it.entityName %> by ID.
85
+ """
86
+ service = <%= it.entityName %>Service(db)
87
+
88
+ joins = []
89
+ <% it.associations.forEach((assoc) => { %>
90
+ <% if (assoc.type === 'OneToMany' || assoc.type === 'ManyToMany') { %>
91
+ if join_<%= assoc.pluralizedRelationshipName %>:
92
+ joins.append("<%= assoc.pluralizedRelationshipName %>")
93
+ <% } else { %>
94
+ if join_<%= assoc.relationshipName %>:
95
+ joins.append("<%= assoc.relationshipName %>")
96
+ <% } %>
97
+ <% }) %>
98
+
99
+ item = await service.get_one(id, joins=joins)
100
+ if not item:
101
+ raise HTTPException(
102
+ status_code=status.HTTP_404_NOT_FOUND,
103
+ detail=f"<%= it.entityName %> with id {id} not found",
104
+ )
105
+ return item
106
+
107
+
108
+ @router.post(
109
+ "",
110
+ response_model=<%= it.entityName %>Schema,
111
+ status_code=status.HTTP_201_CREATED,
112
+ summary="Create a single <%= it.entityName %>",
113
+ )
114
+ async def create_one(
115
+ data: <%= it.entityName %>Create,
116
+ db: AsyncSession = Depends(get_db),
117
+ ) -> <%= it.entityName %>Schema:
118
+ """
119
+ Create a new <%= it.entityName %>.
120
+ """
121
+ service = <%= it.entityName %>Service(db)
122
+ return await service.create_one(data)
123
+
124
+
125
+ @router.post(
126
+ "/bulk",
127
+ response_model=List[<%= it.entityName %>Schema],
128
+ status_code=status.HTTP_201_CREATED,
129
+ summary="Create multiple <%= it.pluralEntityName %>",
130
+ )
131
+ async def create_many(
132
+ data: List[<%= it.entityName %>Create],
133
+ db: AsyncSession = Depends(get_db),
134
+ ) -> List[<%= it.entityName %>Schema]:
135
+ """
136
+ Create multiple <%= it.pluralEntityName %>.
137
+ """
138
+ service = <%= it.entityName %>Service(db)
139
+ return await service.create_many(data)
140
+
141
+
142
+ @router.patch(
143
+ "/{id}",
144
+ response_model=<%= it.entityName %>Schema,
145
+ summary="Update a single <%= it.entityName %>",
146
+ )
147
+ async def update_one(
148
+ id: <% if (it.primaryKeyType === 'uuid') { %>str<% } else { %>int<% } %>,
149
+ data: <%= it.entityName %>Update,
150
+ db: AsyncSession = Depends(get_db),
151
+ ) -> <%= it.entityName %>Schema:
152
+ """
153
+ Update an existing <%= it.entityName %>.
154
+ """
155
+ service = <%= it.entityName %>Service(db)
156
+ item = await service.update_one(id, data)
157
+ if not item:
158
+ raise HTTPException(
159
+ status_code=status.HTTP_404_NOT_FOUND,
160
+ detail=f"<%= it.entityName %> with id {id} not found",
161
+ )
162
+ return item
163
+
164
+
165
+ @router.put(
166
+ "/{id}",
167
+ response_model=<%= it.entityName %>Schema,
168
+ summary="Replace a single <%= it.entityName %>",
169
+ )
170
+ async def replace_one(
171
+ id: <% if (it.primaryKeyType === 'uuid') { %>str<% } else { %>int<% } %>,
172
+ data: <%= it.entityName %>Create,
173
+ db: AsyncSession = Depends(get_db),
174
+ ) -> <%= it.entityName %>Schema:
175
+ """
176
+ Replace an existing <%= it.entityName %>.
177
+ """
178
+ service = <%= it.entityName %>Service(db)
179
+ item = await service.replace_one(id, data)
180
+ if not item:
181
+ raise HTTPException(
182
+ status_code=status.HTTP_404_NOT_FOUND,
183
+ detail=f"<%= it.entityName %> with id {id} not found",
184
+ )
185
+ return item
186
+
187
+
188
+ @router.delete(
189
+ "/{id}",
190
+ status_code=status.HTTP_204_NO_CONTENT,
191
+ summary="Delete a single <%= it.entityName %>",
192
+ )
193
+ async def delete_one(
194
+ id: <% if (it.primaryKeyType === 'uuid') { %>str<% } else { %>int<% } %>,
195
+ db: AsyncSession = Depends(get_db),
196
+ ) -> None:
197
+ """
198
+ Delete a <%= it.entityName %> by ID.
199
+ """
200
+ service = <%= it.entityName %>Service(db)
201
+ deleted = await service.delete_one(id)
202
+ if not deleted:
203
+ raise HTTPException(
204
+ status_code=status.HTTP_404_NOT_FOUND,
205
+ detail=f"<%= it.entityName %> with id {id} not found",
206
+ )
@@ -0,0 +1 @@
1
+ <%= it.field.name %>: <% if (!it.field.required || it.forUpdate) { %>Optional[List[<%= it.field.arrayType === 'integer' ? 'int' : 'str' %>]] = None<% } else { %>List[<%= it.field.arrayType === 'integer' ? 'int' : 'str' %>]<% } %>
@@ -0,0 +1 @@
1
+ <%= it.field.name %>: <% if (!it.field.required || it.forUpdate) { %>Optional[bool] = None<% } else { %>bool<% if (it.field.default !== undefined) { %> = <%= it.field.default %><% } %><% } %>
@@ -0,0 +1 @@
1
+ <%= it.field.name %>: <% if (!it.field.required || it.forUpdate) { %>Optional[date] = None<% } else { %>date<% } %>
@@ -0,0 +1 @@
1
+ <%= it.field.name %>: <% if (!it.field.required || it.forUpdate) { %>Optional[Decimal] = None<% } else { %>Decimal<% } %>
@@ -0,0 +1,6 @@
1
+ <%
2
+ // Generate the enum type name: EntityName + FieldNamePascal + Enum
3
+ const fieldNamePascal = it.field.name.charAt(0).toUpperCase() + it.field.name.slice(1);
4
+ const enumTypeName = it.field.enumType || `${it.entityName}${fieldNamePascal}Enum`;
5
+ -%>
6
+ <%= it.field.name %>: <% if (!it.field.required || it.forUpdate) { %>Optional[enums.<%= enumTypeName %>] = None<% } else { %>enums.<%= enumTypeName %><% } %>
@@ -0,0 +1 @@
1
+ <%= it.field.name %>: <% if (!it.field.required || it.forUpdate) { %>Optional[float] = None<% } else { %>float<% } %>
@@ -0,0 +1 @@
1
+ <%= it.field.name %>: <% if (!it.field.required || it.forUpdate) { %>Optional[int] = None<% } else { %>int<% } %>
@@ -0,0 +1 @@
1
+ <%= it.field.name %>: <% if (!it.field.required || it.forUpdate) { %>Optional[dict] = None<% } else { %>dict<% } %>
@@ -0,0 +1 @@
1
+ <%= it.field.name %>: <% if (!it.field.required || it.forUpdate) { %>Optional[dict] = None<% } else { %>dict<% } %>
@@ -0,0 +1 @@
1
+ <%= it.field.name %>: <% if (!it.field.required || it.forUpdate) { %>Optional[Decimal] = None<% } else { %>Decimal<% } %>
@@ -0,0 +1 @@
1
+ <%= it.field.name %>: <% if (!it.field.required || it.forUpdate) { %>Optional[str] = None<% } else { %>str = Field(max_length=<%= it.field.length || 255 %>)<% } %>
@@ -0,0 +1 @@
1
+ <%= it.field.name %>: <% if (!it.field.required || it.forUpdate) { %>Optional[str] = None<% } else { %>str<% } %>
@@ -0,0 +1 @@
1
+ <%= it.field.name %>: <% if (!it.field.required || it.forUpdate) { %>Optional[datetime] = None<% } else { %>datetime<% } %>
@@ -0,0 +1 @@
1
+ <%= it.field.name %>: <% if (!it.field.required || it.forUpdate) { %>Optional[datetime] = None<% } else { %>datetime<% } %>
@@ -0,0 +1 @@
1
+ <%= it.field.name %>: <% if (!it.field.required || it.forUpdate) { %>Optional[UUID] = None<% } else { %>UUID<% } %>
@@ -0,0 +1 @@
1
+ <%= it.field.name %>: <% if (!it.field.required || it.forUpdate) { %>Optional[str] = None<% } else { %>str = Field(max_length=<%= it.field.length || 255 %>)<% } %>
@@ -0,0 +1,87 @@
1
+ <%~ includeFile('../header.eta', it) %>
2
+
3
+ from __future__ import annotations
4
+ from typing import Optional, List, Any
5
+ from datetime import datetime, date
6
+ from decimal import Decimal
7
+ from uuid import UUID
8
+ from pydantic import BaseModel, Field, ConfigDict
9
+ <% if (it.importEnums) { -%>
10
+ from ..models import enums
11
+ <% } -%>
12
+
13
+
14
+ class <%= it.entityName %>Base(BaseModel):
15
+ """Base schema with common fields for <%= it.entityName %>"""
16
+ <% it.columns.filter(c => !['id', 'createdAt', 'updatedAt', 'created_at', 'updated_at'].includes(c.name)).forEach((field) => { -%>
17
+ <%~ includeFile(`./schema-col-${field.type}`, { field, entityName: it.entityName }) %>
18
+ <% }); -%>
19
+
20
+ <% /* Add foreign key fields for ManyToOne relationships */ -%>
21
+ <% it.associations.filter(a => a.type === 'ManyToOne').forEach((assoc) => { -%>
22
+ <%= assoc.camelCasedId %>: Optional[<% if (assoc.referencedEntityPrimaryKeyType === 'uuid') { %>UUID<% } else { %>int<% } %>] = None
23
+ <% }); -%>
24
+
25
+
26
+ class <%= it.entityName %>Create(BaseModel):
27
+ """Schema for creating a new <%= it.entityName %>"""
28
+ <% it.columns.filter(c => !['id', 'createdAt', 'updatedAt', 'created_at', 'updated_at'].includes(c.name)).forEach((field) => { -%>
29
+ <%~ includeFile(`./schema-col-${field.type}`, { field, entityName: it.entityName, forCreate: true }) %>
30
+ <% }); -%>
31
+ <% /* Add foreign key fields for ManyToOne relationships */ -%>
32
+ <% it.associations.filter(a => a.type === 'ManyToOne').forEach((assoc) => { -%>
33
+ <%= assoc.camelCasedId %>: <% if (assoc.nullable) { %>Optional[<% if (assoc.referencedEntityPrimaryKeyType === 'uuid') { %>UUID<% } else { %>int<% } %>] = None<% } else { %><% if (assoc.referencedEntityPrimaryKeyType === 'uuid') { %>UUID<% } else { %>int<% } %><% } %>
34
+ <% }); -%>
35
+
36
+
37
+ class <%= it.entityName %>Update(BaseModel):
38
+ """Schema for updating an existing <%= it.entityName %>"""
39
+ <% it.columns.filter(c => !['id', 'createdAt', 'updatedAt', 'created_at', 'updated_at'].includes(c.name)).forEach((field) => { -%>
40
+ <%~ includeFile(`./schema-col-${field.type}`, { field, entityName: it.entityName, forUpdate: true }) %>
41
+ <% }); -%>
42
+ <% /* Add foreign key fields for ManyToOne relationships */ -%>
43
+ <% it.associations.filter(a => a.type === 'ManyToOne').forEach((assoc) => { -%>
44
+ <%= assoc.camelCasedId %>: Optional[<% if (assoc.referencedEntityPrimaryKeyType === 'uuid') { %>UUID<% } else { %>int<% } %>] = None
45
+ <% }); -%>
46
+
47
+
48
+ class <%= it.entityName %>(BaseModel):
49
+ """Full schema for <%= it.entityName %> including all fields"""
50
+ model_config = ConfigDict(from_attributes=True, populate_by_name=True)
51
+
52
+ <% if (it.addDefaultPKProperty) { -%>
53
+ <% if (it.primaryKeyType === 'uuid') { -%>
54
+ id: UUID
55
+ <% } else { -%>
56
+ id: int
57
+ <% } -%>
58
+ <% } -%>
59
+ <% if (it.createdAt) { -%>
60
+ createdAt: datetime = Field(alias="created_at")
61
+ <% } -%>
62
+ <% if (it.updatedAt) { -%>
63
+ updatedAt: datetime = Field(alias="updated_at")
64
+ <% } -%>
65
+ <% it.columns.filter(c => !['id', 'createdAt', 'updatedAt', 'created_at', 'updated_at'].includes(c.name)).forEach((field) => { -%>
66
+ <%~ includeFile(`./schema-col-${field.type}`, { field, entityName: it.entityName }) %>
67
+ <% }); -%>
68
+ <% /* Add foreign key fields for ManyToOne relationships */ -%>
69
+ <% it.associations.filter(a => a.type === 'ManyToOne').forEach((assoc) => { -%>
70
+ <%= assoc.camelCasedId %>: Optional[<% if (assoc.referencedEntityPrimaryKeyType === 'uuid') { %>UUID<% } else { %>int<% } %>] = None
71
+ <% }); -%>
72
+ <% /* Add relationship fields */ -%>
73
+ <% it.associations.filter(a => a.type === 'OneToMany' || a.type === 'ManyToMany').forEach((assoc) => { -%>
74
+ <%= assoc.pluralizedRelationshipName %>: Optional[List[Any]] = None
75
+ <% }); -%>
76
+ <% it.associations.filter(a => a.type === 'ManyToOne' || a.type === 'OneToOne').forEach((assoc) => { -%>
77
+ <%= assoc.relationshipName %>: Optional[Any] = None
78
+ <% }); -%>
79
+
80
+
81
+ class <%= it.entityName %>List(BaseModel):
82
+ """Paginated list response for <%= it.entityName %>"""
83
+ data: List[<%= it.entityName %>]
84
+ total: int
85
+ page: int
86
+ pageSize: int
87
+ totalPages: int
@@ -0,0 +1,163 @@
1
+ <%~ includeFile('../header.eta', it) %>
2
+
3
+ from typing import List, Optional, Tuple, Any, Dict
4
+ from sqlalchemy import select, func, desc, asc
5
+ from sqlalchemy.ext.asyncio import AsyncSession
6
+ from sqlalchemy.orm import selectinload
7
+
8
+ from ..models.<%= it.entityName.toLowerCase() %> import <%= it.entityName %>
9
+ from ..schemas.<%= it.entityName.toLowerCase() %> import <%= it.entityName %>Create, <%= it.entityName %>Update
10
+
11
+
12
+ class <%= it.entityName %>Service:
13
+ """
14
+ Service layer for <%= it.entityName %> CRUD operations.
15
+ """
16
+
17
+ def __init__(self, db: AsyncSession):
18
+ self.db = db
19
+
20
+ async def get_many(
21
+ self,
22
+ skip: int = 0,
23
+ limit: int = 10,
24
+ sort: Optional[str] = None,
25
+ filters: Optional[Dict[str, Any]] = None,
26
+ joins: Optional[List[str]] = None,
27
+ ) -> Tuple[List[<%= it.entityName %>], int]:
28
+ """
29
+ Retrieve a paginated list of <%= it.pluralEntityName %>.
30
+ """
31
+ # Build base query
32
+ query = select(<%= it.entityName %>)
33
+
34
+ # Apply filters
35
+ if filters:
36
+ for field, value in filters.items():
37
+ if hasattr(<%= it.entityName %>, field) and value is not None:
38
+ query = query.where(getattr(<%= it.entityName %>, field) == value)
39
+
40
+ # Apply sorting
41
+ if sort:
42
+ if sort.startswith("-"):
43
+ query = query.order_by(desc(getattr(<%= it.entityName %>, sort[1:])))
44
+ else:
45
+ query = query.order_by(asc(getattr(<%= it.entityName %>, sort)))
46
+ else:
47
+ query = query.order_by(<%= it.entityName %>.id)
48
+
49
+ # Apply joins
50
+ if joins:
51
+ for join in joins:
52
+ if hasattr(<%= it.entityName %>, join):
53
+ query = query.options(selectinload(getattr(<%= it.entityName %>, join)))
54
+
55
+ # Get total count
56
+ count_query = select(func.count()).select_from(<%= it.entityName %>)
57
+ if filters:
58
+ for field, value in filters.items():
59
+ if hasattr(<%= it.entityName %>, field) and value is not None:
60
+ count_query = count_query.where(getattr(<%= it.entityName %>, field) == value)
61
+ total_result = await self.db.execute(count_query)
62
+ total = total_result.scalar() or 0
63
+
64
+ # Apply pagination
65
+ query = query.offset(skip).limit(limit)
66
+
67
+ # Execute query
68
+ result = await self.db.execute(query)
69
+ items = list(result.scalars().all())
70
+
71
+ return items, total
72
+
73
+ async def get_one(
74
+ self,
75
+ id: Any,
76
+ joins: Optional[List[str]] = None,
77
+ ) -> Optional[<%= it.entityName %>]:
78
+ """
79
+ Retrieve a single <%= it.entityName %> by ID.
80
+ """
81
+ query = select(<%= it.entityName %>).where(<%= it.entityName %>.id == id)
82
+
83
+ # Apply joins
84
+ if joins:
85
+ for join in joins:
86
+ if hasattr(<%= it.entityName %>, join):
87
+ query = query.options(selectinload(getattr(<%= it.entityName %>, join)))
88
+
89
+ result = await self.db.execute(query)
90
+ return result.scalar_one_or_none()
91
+
92
+ async def create_one(self, data: <%= it.entityName %>Create) -> <%= it.entityName %>:
93
+ """
94
+ Create a new <%= it.entityName %>.
95
+ """
96
+ item = <%= it.entityName %>(**data.model_dump(exclude_unset=True))
97
+ self.db.add(item)
98
+ await self.db.commit()
99
+ await self.db.refresh(item)
100
+ return item
101
+
102
+ async def create_many(self, data: List[<%= it.entityName %>Create]) -> List[<%= it.entityName %>]:
103
+ """
104
+ Create multiple <%= it.pluralEntityName %>.
105
+ """
106
+ items = [<%= it.entityName %>(**item.model_dump(exclude_unset=True)) for item in data]
107
+ self.db.add_all(items)
108
+ await self.db.commit()
109
+ for item in items:
110
+ await self.db.refresh(item)
111
+ return items
112
+
113
+ async def update_one(
114
+ self,
115
+ id: Any,
116
+ data: <%= it.entityName %>Update,
117
+ ) -> Optional[<%= it.entityName %>]:
118
+ """
119
+ Update an existing <%= it.entityName %>.
120
+ """
121
+ item = await self.get_one(id)
122
+ if not item:
123
+ return None
124
+
125
+ update_data = data.model_dump(exclude_unset=True)
126
+ for field, value in update_data.items():
127
+ setattr(item, field, value)
128
+
129
+ await self.db.commit()
130
+ await self.db.refresh(item)
131
+ return item
132
+
133
+ async def replace_one(
134
+ self,
135
+ id: Any,
136
+ data: <%= it.entityName %>Create,
137
+ ) -> Optional[<%= it.entityName %>]:
138
+ """
139
+ Replace an existing <%= it.entityName %>.
140
+ """
141
+ item = await self.get_one(id)
142
+ if not item:
143
+ return None
144
+
145
+ replace_data = data.model_dump()
146
+ for field, value in replace_data.items():
147
+ setattr(item, field, value)
148
+
149
+ await self.db.commit()
150
+ await self.db.refresh(item)
151
+ return item
152
+
153
+ async def delete_one(self, id: Any) -> bool:
154
+ """
155
+ Delete a <%= it.entityName %> by ID.
156
+ """
157
+ item = await self.get_one(id)
158
+ if not item:
159
+ return False
160
+
161
+ await self.db.delete(item)
162
+ await self.db.commit()
163
+ return True
@@ -1,9 +1,9 @@
1
1
  <%~ includeFile('./header.eta') %>
2
2
 
3
- import { Controller } from "@nestjs/common";
3
+ import { Controller, Body } from "@nestjs/common";
4
4
  import { Crud, CrudController, Override, ParsedRequest, CrudRequest, ParsedBody, CreateManyDto } from '@nestjsx/crud';
5
5
  import { ApiTags, ApiResponse, ApiOperation, ApiBody } from '@nestjs/swagger';
6
- import {<%= it.entityName %>, <%= it.entityName %>Create} from './dtos/<%= it.entityName%>.dto'
6
+ import {<%= it.entityName %>, <%= it.entityName %>Create, <%= it.entityName %>Update} from './dtos/<%= it.entityName%>.dto'
7
7
  import {<%= it.svcName %>} from './<%= it.entityName %>.service';
8
8
 
9
9
  @Crud({
@@ -12,9 +12,10 @@ import {<%= it.svcName %>} from './<%= it.entityName %>.service';
12
12
  },
13
13
  dto: {
14
14
  create: <%= it.entityName %>Create,
15
- update: <%= it.entityName %>,
16
- replace: <%= it.entityName %>,
15
+ update: <%= it.entityName %>Update,
16
+ replace: <%= it.entityName %>Update,
17
17
  },
18
+ validation: false,
18
19
  params: {
19
20
  id: {
20
21
  field: 'id',
@@ -134,36 +135,36 @@ export class <%= it.ctrlName %> implements CrudController<<%= it.entityName %>>{
134
135
  })
135
136
  create(
136
137
  @ParsedRequest() req: CrudRequest,
137
- @ParsedBody() dto: <%= it.entityName %>,
138
+ @Body() dto: <%= it.entityName %>Create,
138
139
  ) {
139
- return this.base.createOneBase(req, dto);
140
+ return this.base.createOneBase(req, dto as unknown as <%= it.entityName %>);
140
141
  }
141
142
 
142
143
  @Override('createManyBase')
143
144
  @ApiOperation({ summary: 'Create multiple<%= it.pluralEntityName %>' })
144
145
  createMany(
145
146
  @ParsedRequest() req: CrudRequest,
146
- @ParsedBody() dto: CreateManyDto<<%= it.entityName %>>
147
+ @Body() dto: CreateManyDto<<%= it.entityName %>Create>
147
148
  ) {
148
- return this.base.createManyBase(req, dto);
149
+ return this.base.createManyBase(req, dto as unknown as CreateManyDto<<%= it.entityName %>>);
149
150
  }
150
151
 
151
152
  @Override('updateOneBase')
152
153
  @ApiOperation({ summary: 'Update a single <%= it.entityName %>' })
153
154
  update(
154
155
  @ParsedRequest() req: CrudRequest,
155
- @ParsedBody() dto: <%= it.entityName %>,
156
+ @Body() dto: <%= it.entityName %>Update,
156
157
  ) {
157
- return this.base.updateOneBase(req, dto);
158
+ return this.base.updateOneBase(req, dto as unknown as <%= it.entityName %>);
158
159
  }
159
160
 
160
161
  @Override('replaceOneBase')
161
162
  @ApiOperation({ summary: 'Replace a single <%= it.entityName %>' })
162
163
  replace(
163
164
  @ParsedRequest() req: CrudRequest,
164
- @ParsedBody() dto: <%= it.entityName %>,
165
+ @Body() dto: <%= it.entityName %>Update,
165
166
  ) {
166
- return this.base.replaceOneBase(req, dto);
167
+ return this.base.replaceOneBase(req, dto as unknown as <%= it.entityName %>);
167
168
  }
168
169
 
169
170
  @Override('deleteOneBase')