@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,190 @@
1
+ <%~ includeFile('../../header.eta', it) %>
2
+
3
+ package services
4
+
5
+ import (
6
+ "context"
7
+ "errors"
8
+
9
+ "app/dto"
10
+ "app/models"
11
+ "gorm.io/gorm"
12
+ )
13
+
14
+ type <%= it.entityName %>Service struct {
15
+ db *gorm.DB
16
+ }
17
+
18
+ func New<%= it.entityName %>Service(db *gorm.DB) *<%= it.entityName %>Service {
19
+ return &<%= it.entityName %>Service{db: db}
20
+ }
21
+
22
+ // FindAll retrieves a paginated list of <%= it.pluralName %>
23
+ func (s *<%= it.entityName %>Service) FindAll(ctx context.Context, page, pageSize int) (*dto.<%= it.entityName %>PaginatedResponse, error) {
24
+ var total int64
25
+ var items []models.<%= it.entityName %>
26
+
27
+ offset := (page - 1) * pageSize
28
+
29
+ if err := s.db.WithContext(ctx).Model(&models.<%= it.entityName %>{}).Count(&total).Error; err != nil {
30
+ return nil, err
31
+ }
32
+
33
+ query := s.db.WithContext(ctx)
34
+ <% it.associations.filter(a => a.eager).forEach((assoc) => { %>
35
+ query = query.Preload("<%= assoc.referenceName || assoc.name %>")
36
+ <% }) %>
37
+ if err := query.Offset(offset).Limit(pageSize).Find(&items).Error; err != nil {
38
+ return nil, err
39
+ }
40
+
41
+ totalPages := int(total) / pageSize
42
+ if int(total)%pageSize > 0 {
43
+ totalPages++
44
+ }
45
+
46
+ response := make([]dto.<%= it.entityName %>Response, len(items))
47
+ for i, item := range items {
48
+ response[i] = s.toResponse(&item)
49
+ }
50
+
51
+ return &dto.<%= it.entityName %>PaginatedResponse{
52
+ Data: response,
53
+ Total: total,
54
+ Page: page,
55
+ PageSize: pageSize,
56
+ TotalPages: totalPages,
57
+ }, nil
58
+ }
59
+
60
+ // FindOne retrieves a single <%= it.entityName %> by ID
61
+ func (s *<%= it.entityName %>Service) FindOne(ctx context.Context, id string) (*dto.<%= it.entityName %>Response, error) {
62
+ var item models.<%= it.entityName %>
63
+
64
+ query := s.db.WithContext(ctx)
65
+ <% it.associations.filter(a => a.eager).forEach((assoc) => { %>
66
+ query = query.Preload("<%= assoc.referenceName || assoc.name %>")
67
+ <% }) %>
68
+
69
+ <% if (it.primaryKeyType === 'uuid') { %>
70
+ if err := query.Where("id = ?", id).First(&item).Error; err != nil {
71
+ <% } else { %>
72
+ if err := query.First(&item, id).Error; err != nil {
73
+ <% } %>
74
+ if errors.Is(err, gorm.ErrRecordNotFound) {
75
+ return nil, errors.New("<%= it.entityName %> not found")
76
+ }
77
+ return nil, err
78
+ }
79
+
80
+ response := s.toResponse(&item)
81
+ return &response, nil
82
+ }
83
+
84
+ // Create creates a new <%= it.entityName %>
85
+ func (s *<%= it.entityName %>Service) Create(ctx context.Context, input *dto.<%= it.entityName %>Create) (*dto.<%= it.entityName %>Response, error) {
86
+ item := models.<%= it.entityName %>{
87
+ <% it.columns.filter(c => !c.generated && !c.primary).forEach((column) => { %>
88
+ <% if (column.required) { %>
89
+ <%= column.pascalName %>: input.<%= column.pascalName %>,
90
+ <% } else { %>
91
+ <%= column.pascalName %>: input.<%= column.pascalName %>,
92
+ <% } %>
93
+ <% }) %>
94
+ <% it.associations.filter(a => a.type === 'ManyToOne' || (a.type === 'OneToOne' && a.isOwner)).forEach((assoc) => { %>
95
+ <%= assoc.referenceName || assoc.name %>ID: input.<%= assoc.referenceName || assoc.name %>ID,
96
+ <% }) %>
97
+ }
98
+
99
+ if err := s.db.WithContext(ctx).Create(&item).Error; err != nil {
100
+ return nil, err
101
+ }
102
+
103
+ response := s.toResponse(&item)
104
+ return &response, nil
105
+ }
106
+
107
+ // Update updates an existing <%= it.entityName %>
108
+ func (s *<%= it.entityName %>Service) Update(ctx context.Context, id string, input *dto.<%= it.entityName %>Update) (*dto.<%= it.entityName %>Response, error) {
109
+ var item models.<%= it.entityName %>
110
+
111
+ <% if (it.primaryKeyType === 'uuid') { %>
112
+ if err := s.db.WithContext(ctx).Where("id = ?", id).First(&item).Error; err != nil {
113
+ <% } else { %>
114
+ if err := s.db.WithContext(ctx).First(&item, id).Error; err != nil {
115
+ <% } %>
116
+ if errors.Is(err, gorm.ErrRecordNotFound) {
117
+ return nil, errors.New("<%= it.entityName %> not found")
118
+ }
119
+ return nil, err
120
+ }
121
+
122
+ updates := make(map[string]interface{})
123
+ <% it.columns.filter(c => !c.generated && !c.primary).forEach((column) => { %>
124
+ if input.<%= column.pascalName %> != nil {
125
+ updates["<%= column.snakeName %>"] = *input.<%= column.pascalName %>
126
+ }
127
+ <% }) %>
128
+ <% it.associations.filter(a => a.type === 'ManyToOne' || (a.type === 'OneToOne' && a.isOwner)).forEach((assoc) => { %>
129
+ if input.<%= assoc.referenceName || assoc.name %>ID != nil {
130
+ updates["<%= assoc.snakeReferenceName || assoc.snakeName %>_id"] = *input.<%= assoc.referenceName || assoc.name %>ID
131
+ }
132
+ <% }) %>
133
+
134
+ if len(updates) > 0 {
135
+ if err := s.db.WithContext(ctx).Model(&item).Updates(updates).Error; err != nil {
136
+ return nil, err
137
+ }
138
+ }
139
+
140
+ // Reload with associations
141
+ query := s.db.WithContext(ctx)
142
+ <% it.associations.filter(a => a.eager).forEach((assoc) => { %>
143
+ query = query.Preload("<%= assoc.referenceName || assoc.name %>")
144
+ <% }) %>
145
+ <% if (it.primaryKeyType === 'uuid') { %>
146
+ if err := query.Where("id = ?", id).First(&item).Error; err != nil {
147
+ <% } else { %>
148
+ if err := query.First(&item, id).Error; err != nil {
149
+ <% } %>
150
+ return nil, err
151
+ }
152
+
153
+ response := s.toResponse(&item)
154
+ return &response, nil
155
+ }
156
+
157
+ // Delete removes a <%= it.entityName %> by ID
158
+ func (s *<%= it.entityName %>Service) Delete(ctx context.Context, id string) error {
159
+ <% if (it.primaryKeyType === 'uuid') { %>
160
+ result := s.db.WithContext(ctx).Where("id = ?", id).Delete(&models.<%= it.entityName %>{})
161
+ <% } else { %>
162
+ result := s.db.WithContext(ctx).Delete(&models.<%= it.entityName %>{}, id)
163
+ <% } %>
164
+ if result.Error != nil {
165
+ return result.Error
166
+ }
167
+ if result.RowsAffected == 0 {
168
+ return errors.New("<%= it.entityName %> not found")
169
+ }
170
+ return nil
171
+ }
172
+
173
+ // toResponse converts a model to a response DTO
174
+ func (s *<%= it.entityName %>Service) toResponse(item *models.<%= it.entityName %>) dto.<%= it.entityName %>Response {
175
+ return dto.<%= it.entityName %>Response{
176
+ ID: item.ID,
177
+ <% it.columns.forEach((column) => { %>
178
+ <%= column.pascalName %>: item.<%= column.pascalName %>,
179
+ <% }) %>
180
+ <% if (it.createdAt) { %>
181
+ CreatedAt: item.CreatedAt,
182
+ <% } %>
183
+ <% if (it.updatedAt) { %>
184
+ UpdatedAt: item.UpdatedAt,
185
+ <% } %>
186
+ <% it.associations.filter(a => a.type === 'ManyToOne' || (a.type === 'OneToOne' && a.isOwner)).forEach((assoc) => { %>
187
+ <%= assoc.referenceName || assoc.name %>ID: item.<%= assoc.referenceName || assoc.name %>ID,
188
+ <% }) %>
189
+ }
190
+ }
@@ -0,0 +1,10 @@
1
+ <%~ includeFile('./header.eta', it) %>
2
+
3
+ from enum import Enum
4
+ <% for (const field of it.allEnumsFields) { %>
5
+
6
+ class <%= field.name %>(str, Enum):
7
+ <% for (const value of (field.values || [])) { %>
8
+ <%= value.toUpperCase().replace(/[^A-Z0-9_]/g, '_') %> = "<%= value %>"
9
+ <% } %>
10
+ <% } %>
@@ -0,0 +1,6 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated: <%= it.generatedAt %>
3
+
4
+ # Auto-generated by Apso CLI
5
+ # DO NOT EDIT - This file is managed by Apso and will be overwritten on scaffold
6
+
@@ -0,0 +1,18 @@
1
+ <%~ includeFile('./header.eta', it) %>
2
+
3
+ from fastapi import APIRouter
4
+
5
+ <% it.entities.forEach((entity) => { %>
6
+ from .routers.<%= entity.name.toLowerCase() %> import router as <%= entity.name.toLowerCase() %>_router
7
+ <% }) %>
8
+
9
+ # Create main router for autogenerated endpoints
10
+ autogen_router = APIRouter(prefix="/api")
11
+
12
+ # Include all entity routers
13
+ <% it.entities.forEach((entity) => { %>
14
+ autogen_router.include_router(<%= entity.name.toLowerCase() %>_router)
15
+ <% }) %>
16
+
17
+ # Export for use in main.py
18
+ __all__ = ["autogen_router"]
@@ -0,0 +1,4 @@
1
+ <%= it.field.name %>: Mapped[<% if (!it.field.required) { %>Optional[list]<% } else { %>list<% } %>] = mapped_column(
2
+ ARRAY(<%= it.field.arrayType === 'integer' ? 'Integer' : 'String' %>),
3
+ nullable=<%= it.field.required ? 'False' : 'True' %>
4
+ )
@@ -0,0 +1,5 @@
1
+ <%= it.field.name %>: Mapped[<% if (!it.field.required) { %>Optional[bool]<% } else { %>bool<% } %>] = mapped_column(
2
+ Boolean,
3
+ nullable=<%= it.field.required ? 'False' : 'True' %><% if (it.field.default !== undefined) { %>,
4
+ default=<%= it.field.default === true ? 'True' : (it.field.default === false ? 'False' : it.field.default) %><% } %>
5
+ )
@@ -0,0 +1,4 @@
1
+ <%= it.field.name %>: Mapped[<% if (!it.field.required) { %>Optional[date]<% } else { %>date<% } %>] = mapped_column(
2
+ Date,
3
+ nullable=<%= it.field.required ? 'False' : 'True' %>
4
+ )
@@ -0,0 +1,4 @@
1
+ <%= it.field.name %>: Mapped[<% if (!it.field.required) { %>Optional[Decimal]<% } else { %>Decimal<% } %>] = mapped_column(
2
+ Numeric(precision=<%= it.field.precision || 10 %>, scale=<%= it.field.scale || 2 %>),
3
+ nullable=<%= it.field.required ? 'False' : 'True' %>
4
+ )
@@ -0,0 +1,16 @@
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
+ // Generate the PostgreSQL enum type name (snake_case version)
6
+ const pgEnumName = (it.entityName + '_' + it.field.name + '_enum').toLowerCase().replace(/([a-z])([A-Z])/g, '$1_$2').toLowerCase();
7
+ -%>
8
+ <%= it.field.name %>: Mapped[<% if (!it.field.required) { %>Optional[enums.<%= enumTypeName %>]<% } else { %>enums.<%= enumTypeName %><% } %>] = mapped_column(
9
+ SAEnum(
10
+ enums.<%= enumTypeName %>,
11
+ name="<%= pgEnumName %>",
12
+ values_callable=lambda x: [e.value for e in x],
13
+ create_type=False
14
+ ),
15
+ nullable=<%= it.field.required ? 'False' : 'True' %>
16
+ )
@@ -0,0 +1,5 @@
1
+ <%= it.field.name %>: Mapped[<% if (!it.field.required) { %>Optional[float]<% } else { %>float<% } %>] = mapped_column(
2
+ Float,
3
+ nullable=<%= it.field.required ? 'False' : 'True' %><% if (it.field.default !== undefined) { %>,
4
+ default=<%= it.field.default %><% } %>
5
+ )
@@ -0,0 +1,6 @@
1
+ <%= it.field.name %>: Mapped[<% if (!it.field.required) { %>Optional[int]<% } else { %>int<% } %>] = mapped_column(
2
+ Integer,
3
+ nullable=<%= it.field.required ? 'False' : 'True' %><% if (it.field.unique) { %>,
4
+ unique=True<% } %><% if (it.field.default !== undefined) { %>,
5
+ default=<%= it.field.default %><% } %>
6
+ )
@@ -0,0 +1,4 @@
1
+ <%= it.field.name %>: Mapped[<% if (!it.field.required) { %>Optional[dict]<% } else { %>dict<% } %>] = mapped_column(
2
+ JSON,
3
+ nullable=<%= it.field.required ? 'False' : 'True' %>
4
+ )
@@ -0,0 +1,4 @@
1
+ <%= it.field.name %>: Mapped[<% if (!it.field.required) { %>Optional[dict]<% } else { %>dict<% } %>] = mapped_column(
2
+ JSONB,
3
+ nullable=<%= it.field.required ? 'False' : 'True' %>
4
+ )
@@ -0,0 +1,4 @@
1
+ <%= it.field.name %>: Mapped[<% if (!it.field.required) { %>Optional[Decimal]<% } else { %>Decimal<% } %>] = mapped_column(
2
+ Numeric(precision=<%= it.field.precision || 10 %>, scale=<%= it.field.scale || 2 %>),
3
+ nullable=<%= it.field.required ? 'False' : 'True' %>
4
+ )
@@ -0,0 +1,6 @@
1
+ <%= it.field.name %>: Mapped[<% if (!it.field.required) { %>Optional[str]<% } else { %>str<% } %>] = mapped_column(
2
+ String(<%= it.field.length || 255 %>),
3
+ nullable=<%= it.field.required ? 'False' : 'True' %><% if (it.field.unique) { %>,
4
+ unique=True<% } %><% if (it.field.default !== undefined && it.field.default !== null) { %>,
5
+ default=<%= JSON.stringify(it.field.default) %><% } %>
6
+ )
@@ -0,0 +1,6 @@
1
+ <%= it.field.name %>: Mapped[<% if (!it.field.required) { %>Optional[str]<% } else { %>str<% } %>] = mapped_column(
2
+ Text,
3
+ nullable=<%= it.field.required ? 'False' : 'True' %><% if (it.field.unique) { %>,
4
+ unique=True<% } %><% if (it.field.default !== undefined && it.field.default !== null) { %>,
5
+ default=<%= JSON.stringify(it.field.default) %><% } %>
6
+ )
@@ -0,0 +1,11 @@
1
+ <%
2
+ const isCreatedAt = ['createdAt', 'created_at'].includes(it.field.name);
3
+ const isUpdatedAt = ['updatedAt', 'updated_at'].includes(it.field.name);
4
+ -%>
5
+ <%= it.field.name %>: Mapped[<% if (!it.field.required) { %>Optional[datetime]<% } else { %>datetime<% } %>] = mapped_column(
6
+ DateTime,
7
+ nullable=<%= it.field.required ? 'False' : 'True' %><% if (isCreatedAt) { %>,
8
+ server_default=func.now()<% } %><% if (isUpdatedAt) { %>,
9
+ server_default=func.now(),
10
+ onupdate=func.now()<% } %>
11
+ )
@@ -0,0 +1,11 @@
1
+ <%
2
+ const isCreatedAt = ['createdAt', 'created_at'].includes(it.field.name);
3
+ const isUpdatedAt = ['updatedAt', 'updated_at'].includes(it.field.name);
4
+ -%>
5
+ <%= it.field.name %>: Mapped[<% if (!it.field.required) { %>Optional[datetime]<% } else { %>datetime<% } %>] = mapped_column(
6
+ DateTime(timezone=True),
7
+ nullable=<%= it.field.required ? 'False' : 'True' %><% if (isCreatedAt) { %>,
8
+ server_default=func.now()<% } %><% if (isUpdatedAt) { %>,
9
+ server_default=func.now(),
10
+ onupdate=func.now()<% } %>
11
+ )
@@ -0,0 +1,5 @@
1
+ <%= it.field.name %>: Mapped[<% if (!it.field.required) { %>Optional[UUID]<% } else { %>UUID<% } %>] = mapped_column(
2
+ PGUUID(as_uuid=True),
3
+ nullable=<%= it.field.required ? 'False' : 'True' %><% if (it.field.unique) { %>,
4
+ unique=True<% } %>
5
+ )
@@ -0,0 +1,6 @@
1
+ <%= it.field.name %>: Mapped[<% if (!it.field.required) { %>Optional[str]<% } else { %>str<% } %>] = mapped_column(
2
+ String(<%= it.field.length || 255 %>),
3
+ nullable=<%= it.field.required ? 'False' : 'True' %><% if (it.field.unique) { %>,
4
+ unique=True<% } %><% if (it.field.default !== undefined) { %>,
5
+ default="<%= it.field.default %>"<% } %>
6
+ )
@@ -0,0 +1,124 @@
1
+ <%~ includeFile('../header.eta', it) %>
2
+
3
+ from __future__ import annotations
4
+ from typing import TYPE_CHECKING, Optional, List
5
+ from datetime import datetime, date
6
+ from decimal import Decimal
7
+ from uuid import UUID
8
+
9
+ from sqlalchemy import Column, Integer, String, Text, Boolean, Float, Numeric, Date, DateTime, JSON, Enum as SAEnum, ForeignKey, Index, UniqueConstraint, Table
10
+ from sqlalchemy.dialects.postgresql import UUID as PGUUID, JSONB, ARRAY, BYTEA, INET, INTERVAL, TSVECTOR
11
+ from sqlalchemy.orm import relationship, Mapped, mapped_column
12
+ from sqlalchemy.sql import func
13
+
14
+ from app.database import Base
15
+ <% if (it.importEnums) { %>
16
+ from . import enums
17
+ <% } %>
18
+
19
+ <% if (it.entitiesToImport && it.entitiesToImport.length > 0) { %>
20
+ if TYPE_CHECKING:
21
+ <% it.entitiesToImport.forEach((assocName) => { %>
22
+ from .<%= assocName.toLowerCase() %> import <%= assocName %>
23
+ <% }) %>
24
+ <% } %>
25
+
26
+ <% /* Generate ManyToMany junction tables */ %>
27
+ <% it.associations.filter(a => a.type === 'ManyToMany' && a.joinTable).forEach((assoc) => { %>
28
+ <%= assoc.joinTableName || `${it.snakeCasedName}_${assoc.name.toLowerCase()}` %> = Table(
29
+ "<%= assoc.joinTableName || `${it.snakeCasedName}_${assoc.name.toLowerCase()}` %>",
30
+ Base.metadata,
31
+ Column("<%= assoc.joinColumnName || `${it.snakeCasedName}_id` %>", <% if (it.primaryKeyType === 'uuid') { %>PGUUID(as_uuid=True)<% } else { %>Integer<% } %>, ForeignKey("<%= it.snakeCasedName %>.id"), primary_key=True),
32
+ Column("<%= assoc.inverseJoinColumnName || `${assoc.name.toLowerCase()}_id` %>", <% if (assoc.referencedEntityPrimaryKeyType === 'uuid') { %>PGUUID(as_uuid=True)<% } else { %>Integer<% } %>, ForeignKey("<%= assoc.name.toLowerCase() %>.id"), primary_key=True),
33
+ )
34
+ <% }) %>
35
+
36
+ class <%= it.name %>(Base):
37
+ """
38
+ SQLAlchemy model for <%= it.name %>
39
+ """
40
+ __tablename__ = "<%= it.snakeCasedName %>"
41
+
42
+ <% /* Generate table arguments for indexes and unique constraints */ %>
43
+ <% if ((it.indexes && it.indexes.length > 0) || (it.uniques && it.uniques.length > 0)) { %>
44
+ __table_args__ = (
45
+ <% it.indexes.forEach((index) => { %>
46
+ Index("<%= index.name || `idx_${it.snakeCasedName}_${index.fields.join('_')}` %>", <%~ index.fields.map(f => `"${f}"`).join(', ') %><% if (index.unique) { %>, unique=True<% } %>),
47
+ <% }) %>
48
+ <% it.uniques.forEach((unique) => { %>
49
+ UniqueConstraint(<%~ unique.fields.map(f => `"${f}"`).join(', ') %><% if (unique.name) { %>, name="<%= unique.name %>"<% } %>),
50
+ <% }) %>
51
+ )
52
+ <% } %>
53
+
54
+ # Primary Key
55
+ <% if (it.createPrimaryKey) { %>
56
+ <% if (it.primaryKeyType === 'uuid') { %>
57
+ id: Mapped[UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True)
58
+ <% } else { %>
59
+ id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
60
+ <% } %>
61
+ <% } %>
62
+
63
+ <% /* Timestamp columns */ %>
64
+ <% if (it.createdAt) { %>
65
+ created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
66
+ <% } %>
67
+ <% if (it.updatedAt) { %>
68
+ updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
69
+ <% } %>
70
+
71
+ # Columns
72
+ <%
73
+ // Filter out id (if createPrimaryKey), createdAt, and updatedAt since they're handled separately
74
+ const columnsToProcess = it.columns.filter(c => {
75
+ if (it.createPrimaryKey && c.name === 'id') return false;
76
+ if (it.createdAt && c.name === 'createdAt') return false;
77
+ if (it.updatedAt && c.name === 'updatedAt') return false;
78
+ return true;
79
+ });
80
+ columnsToProcess.forEach((field) => { -%>
81
+ <%~ includeFile(`./model-col-${field.type}`, { field, entityName: it.name }) %>
82
+ <% }); %>
83
+
84
+ # Relationships
85
+ <% it.associations.forEach((assoc) => { %>
86
+ <% if (assoc.type === 'OneToMany') { %>
87
+ <%= assoc.pluralizedRelationshipName %>: Mapped[List["<%= assoc.name %>"]] = relationship(
88
+ "<%= assoc.name %>",
89
+ back_populates="<%= assoc.inverseSidePropertyName %>",
90
+ lazy="selectin"
91
+ )
92
+ <% } else if (assoc.type === 'ManyToOne') { %>
93
+ # Foreign key for <%= assoc.name %>
94
+ <%= assoc.camelCasedId %>: Mapped[<% if (assoc.referencedEntityPrimaryKeyType === 'uuid') { %>Optional[UUID]<% } else { %>Optional[int]<% } %>] = mapped_column(
95
+ <% if (assoc.referencedEntityPrimaryKeyType === 'uuid') { %>PGUUID(as_uuid=True)<% } else { %>Integer<% } %>,
96
+ ForeignKey("<%= assoc.name.toLowerCase() %>.id"<% if (assoc.cascadeDelete) { %>, ondelete="CASCADE"<% } %>),
97
+ nullable=<%= assoc.nullable ? 'True' : 'False' %><% if (assoc.index) { %>,
98
+ index=True<% } %>
99
+ )
100
+ <%= assoc.relationshipName %>: Mapped[Optional["<%= assoc.name %>"]] = relationship(
101
+ "<%= assoc.name %>",
102
+ <% if (assoc.biDirectional) { %>back_populates="<%= assoc.inverseSidePropertyName %>",<% } %>
103
+ lazy="selectin"
104
+ )
105
+ <% } else if (assoc.type === 'ManyToMany') { %>
106
+ <%= assoc.pluralizedRelationshipName %>: Mapped[List["<%= assoc.name %>"]] = relationship(
107
+ "<%= assoc.name %>",
108
+ secondary=<%= assoc.joinTableName || `${it.snakeCasedName}_${assoc.name.toLowerCase()}` %>,
109
+ <% if (assoc.biDirectional) { %>back_populates="<%= assoc.inverseSidePropertyName %>",<% } %>
110
+ lazy="selectin"
111
+ )
112
+ <% } else if (assoc.type === 'OneToOne') { %>
113
+ <%= assoc.relationshipName %>: Mapped[Optional["<%= assoc.name %>"]] = relationship(
114
+ "<%= assoc.name %>",
115
+ <% if (assoc.biDirectional) { %>back_populates="<%= assoc.inverseSidePropertyName %>",<% } %>
116
+ uselist=False,
117
+ cascade="all, delete-orphan",
118
+ lazy="selectin"
119
+ )
120
+ <% } %>
121
+ <% }) %>
122
+
123
+ def __repr__(self) -> str:
124
+ return f"<<%= it.name %>(id={self.id})>"