@apso/cli 0.7.3 → 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.
- package/dist/autogen/index.d.ts +2 -0
- package/dist/autogen/index.js +3 -0
- package/dist/commands/server/new.d.ts +4 -1
- package/dist/commands/server/new.js +115 -22
- package/dist/commands/server/scaffold.d.ts +11 -0
- package/dist/commands/server/scaffold.js +213 -12
- package/dist/lib/api/client.d.ts +65 -0
- package/dist/lib/api/client.js +305 -0
- package/dist/lib/api/index.d.ts +8 -0
- package/dist/lib/api/index.js +22 -0
- package/dist/lib/api/services.d.ts +166 -0
- package/dist/lib/api/services.js +202 -0
- package/dist/lib/api/types.d.ts +212 -0
- package/dist/lib/api/types.js +7 -0
- package/dist/lib/apsorc-parser.d.ts +3 -0
- package/dist/lib/apsorc-parser.js +4 -0
- package/dist/lib/config/index.d.ts +8 -0
- package/dist/lib/config/index.js +17 -0
- package/dist/lib/config/manager.d.ts +127 -0
- package/dist/lib/config/manager.js +304 -0
- package/dist/lib/config/paths.d.ts +65 -0
- package/dist/lib/config/paths.js +127 -0
- package/dist/lib/config/types.d.ts +230 -0
- package/dist/lib/config/types.js +30 -0
- package/dist/lib/generators/base.d.ts +93 -0
- package/dist/lib/generators/base.js +90 -0
- package/dist/lib/generators/go.d.ts +37 -0
- package/dist/lib/generators/go.js +340 -0
- package/dist/lib/generators/index.d.ts +21 -0
- package/dist/lib/generators/index.js +51 -0
- package/dist/lib/generators/python.d.ts +21 -0
- package/dist/lib/generators/python.js +253 -0
- package/dist/lib/generators/typescript.d.ts +25 -0
- package/dist/lib/generators/typescript.js +373 -0
- package/dist/lib/index.d.ts +2 -0
- package/dist/lib/index.js +9 -1
- package/dist/lib/templates/entities/entity-col-timestamp.eta +9 -2
- package/dist/lib/templates/entities/entity-col-timestamptz.eta +9 -2
- package/dist/lib/templates/entities/entity.eta +18 -5
- package/dist/lib/templates/go/dto/dto-col-array.eta +7 -0
- package/dist/lib/templates/go/dto/dto-col-boolean.eta +7 -0
- package/dist/lib/templates/go/dto/dto-col-date.eta +7 -0
- package/dist/lib/templates/go/dto/dto-col-decimal.eta +7 -0
- package/dist/lib/templates/go/dto/dto-col-enum.eta +8 -0
- package/dist/lib/templates/go/dto/dto-col-float.eta +7 -0
- package/dist/lib/templates/go/dto/dto-col-integer.eta +7 -0
- package/dist/lib/templates/go/dto/dto-col-json.eta +7 -0
- package/dist/lib/templates/go/dto/dto-col-jsonb.eta +7 -0
- package/dist/lib/templates/go/dto/dto-col-numeric.eta +7 -0
- package/dist/lib/templates/go/dto/dto-col-string.eta +7 -0
- package/dist/lib/templates/go/dto/dto-col-text.eta +7 -0
- package/dist/lib/templates/go/dto/dto-col-timestamp.eta +7 -0
- package/dist/lib/templates/go/dto/dto-col-timestamptz.eta +7 -0
- package/dist/lib/templates/go/dto/dto-col-uuid.eta +7 -0
- package/dist/lib/templates/go/dto/dto-col-varchar.eta +7 -0
- package/dist/lib/templates/go/dto/dto.eta +78 -0
- package/dist/lib/templates/go/enums.eta +44 -0
- package/dist/lib/templates/go/handlers/handler.eta +212 -0
- package/dist/lib/templates/go/header.eta +3 -0
- package/dist/lib/templates/go/index-module.eta +53 -0
- package/dist/lib/templates/go/middleware/auth.eta +101 -0
- package/dist/lib/templates/go/models/model-col-array.eta +1 -0
- package/dist/lib/templates/go/models/model-col-boolean.eta +1 -0
- package/dist/lib/templates/go/models/model-col-date.eta +1 -0
- package/dist/lib/templates/go/models/model-col-decimal.eta +1 -0
- package/dist/lib/templates/go/models/model-col-enum.eta +2 -0
- package/dist/lib/templates/go/models/model-col-float.eta +1 -0
- package/dist/lib/templates/go/models/model-col-integer.eta +1 -0
- package/dist/lib/templates/go/models/model-col-json.eta +1 -0
- package/dist/lib/templates/go/models/model-col-jsonb.eta +1 -0
- package/dist/lib/templates/go/models/model-col-numeric.eta +1 -0
- package/dist/lib/templates/go/models/model-col-string.eta +1 -0
- package/dist/lib/templates/go/models/model-col-text.eta +1 -0
- package/dist/lib/templates/go/models/model-col-timestamp.eta +1 -0
- package/dist/lib/templates/go/models/model-col-timestamptz.eta +1 -0
- package/dist/lib/templates/go/models/model-col-uuid.eta +1 -0
- package/dist/lib/templates/go/models/model-col-varchar.eta +1 -0
- package/dist/lib/templates/go/models/model.eta +53 -0
- package/dist/lib/templates/go/services/service.eta +190 -0
- package/dist/lib/templates/python/enums.eta +10 -0
- package/dist/lib/templates/python/header.eta +6 -0
- package/dist/lib/templates/python/index-module.eta +18 -0
- package/dist/lib/templates/python/models/model-col-array.eta +4 -0
- package/dist/lib/templates/python/models/model-col-boolean.eta +5 -0
- package/dist/lib/templates/python/models/model-col-date.eta +4 -0
- package/dist/lib/templates/python/models/model-col-decimal.eta +4 -0
- package/dist/lib/templates/python/models/model-col-enum.eta +16 -0
- package/dist/lib/templates/python/models/model-col-float.eta +5 -0
- package/dist/lib/templates/python/models/model-col-integer.eta +6 -0
- package/dist/lib/templates/python/models/model-col-json.eta +4 -0
- package/dist/lib/templates/python/models/model-col-jsonb.eta +4 -0
- package/dist/lib/templates/python/models/model-col-numeric.eta +4 -0
- package/dist/lib/templates/python/models/model-col-string.eta +6 -0
- package/dist/lib/templates/python/models/model-col-text.eta +6 -0
- package/dist/lib/templates/python/models/model-col-timestamp.eta +11 -0
- package/dist/lib/templates/python/models/model-col-timestamptz.eta +11 -0
- package/dist/lib/templates/python/models/model-col-uuid.eta +5 -0
- package/dist/lib/templates/python/models/model-col-varchar.eta +6 -0
- package/dist/lib/templates/python/models/model.eta +124 -0
- package/dist/lib/templates/python/routers/router.eta +206 -0
- package/dist/lib/templates/python/schemas/schema-col-array.eta +1 -0
- package/dist/lib/templates/python/schemas/schema-col-boolean.eta +1 -0
- package/dist/lib/templates/python/schemas/schema-col-date.eta +1 -0
- package/dist/lib/templates/python/schemas/schema-col-decimal.eta +1 -0
- package/dist/lib/templates/python/schemas/schema-col-enum.eta +6 -0
- package/dist/lib/templates/python/schemas/schema-col-float.eta +1 -0
- package/dist/lib/templates/python/schemas/schema-col-integer.eta +1 -0
- package/dist/lib/templates/python/schemas/schema-col-json.eta +1 -0
- package/dist/lib/templates/python/schemas/schema-col-jsonb.eta +1 -0
- package/dist/lib/templates/python/schemas/schema-col-numeric.eta +1 -0
- package/dist/lib/templates/python/schemas/schema-col-string.eta +1 -0
- package/dist/lib/templates/python/schemas/schema-col-text.eta +1 -0
- package/dist/lib/templates/python/schemas/schema-col-timestamp.eta +1 -0
- package/dist/lib/templates/python/schemas/schema-col-timestamptz.eta +1 -0
- package/dist/lib/templates/python/schemas/schema-col-uuid.eta +1 -0
- package/dist/lib/templates/python/schemas/schema-col-varchar.eta +1 -0
- package/dist/lib/templates/python/schemas/schema.eta +87 -0
- package/dist/lib/templates/python/services/service.eta +163 -0
- package/dist/lib/templates/rest/controller-rest.eta +13 -12
- package/dist/lib/templates/rest/dto-rest.eta +173 -59
- package/dist/lib/templates/rest/service-rest.eta +50 -10
- package/dist/lib/types/generator.d.ts +241 -0
- package/dist/lib/types/generator.js +2 -0
- package/dist/lib/types/index.d.ts +1 -0
- package/dist/lib/utils/casing.d.ts +1 -0
- package/dist/lib/utils/casing.js +11 -1
- package/oclif.manifest.json +39 -3
- package/package.json +4 -2
- package/dist/lib/utils/relationships/parse.spec.d.ts +0 -1
- package/dist/lib/utils/relationships/parse.spec.js +0 -402
- package/dist/tests/templates/entities/types/array-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/array-template.spec.js +0 -82
- package/dist/tests/templates/entities/types/bigint-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/bigint-template.spec.js +0 -82
- package/dist/tests/templates/entities/types/boolean-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/boolean-template.spec.js +0 -77
- package/dist/tests/templates/entities/types/bytea-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/bytea-template.spec.js +0 -68
- package/dist/tests/templates/entities/types/char-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/char-template.spec.js +0 -68
- package/dist/tests/templates/entities/types/date-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/date-template.spec.js +0 -56
- package/dist/tests/templates/entities/types/decimal-field-validation.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/decimal-field-validation.spec.js +0 -100
- package/dist/tests/templates/entities/types/decimal-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/decimal-template.spec.js +0 -86
- package/dist/tests/templates/entities/types/double-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/double-template.spec.js +0 -70
- package/dist/tests/templates/entities/types/enum-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/enum-template.spec.js +0 -85
- package/dist/tests/templates/entities/types/float-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/float-template.spec.js +0 -70
- package/dist/tests/templates/entities/types/geometry-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/geometry-template.spec.js +0 -58
- package/dist/tests/templates/entities/types/inet-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/inet-template.spec.js +0 -69
- package/dist/tests/templates/entities/types/int4range-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/int4range-template.spec.js +0 -72
- package/dist/tests/templates/entities/types/integer-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/integer-template.spec.js +0 -79
- package/dist/tests/templates/entities/types/interval-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/interval-template.spec.js +0 -68
- package/dist/tests/templates/entities/types/json-plain-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/json-plain-template.spec.js +0 -54
- package/dist/tests/templates/entities/types/json-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/json-template.spec.js +0 -40
- package/dist/tests/templates/entities/types/jsonb-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/jsonb-template.spec.js +0 -40
- package/dist/tests/templates/entities/types/money-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/money-template.spec.js +0 -68
- package/dist/tests/templates/entities/types/numeric-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/numeric-template.spec.js +0 -84
- package/dist/tests/templates/entities/types/point-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/point-template.spec.js +0 -64
- package/dist/tests/templates/entities/types/polygon-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/polygon-template.spec.js +0 -70
- package/dist/tests/templates/entities/types/real-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/real-template.spec.js +0 -67
- package/dist/tests/templates/entities/types/serial-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/serial-template.spec.js +0 -113
- package/dist/tests/templates/entities/types/smallint-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/smallint-template.spec.js +0 -82
- package/dist/tests/templates/entities/types/string-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/string-template.spec.js +0 -111
- package/dist/tests/templates/entities/types/text-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/text-template.spec.js +0 -70
- package/dist/tests/templates/entities/types/time-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/time-template.spec.js +0 -68
- package/dist/tests/templates/entities/types/timestamp-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/timestamp-template.spec.js +0 -74
- package/dist/tests/templates/entities/types/timetz-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/timetz-template.spec.js +0 -68
- package/dist/tests/templates/entities/types/tsvector-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/tsvector-template.spec.js +0 -55
- package/dist/tests/templates/entities/types/uuid-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/uuid-template.spec.js +0 -82
- package/dist/tests/templates/entities/types/varchar-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/varchar-template.spec.js +0 -105
- package/dist/tests/templates/entities/types/xml-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/types/xml-template.spec.js +0 -55
- package/dist/tests/templates/entities/uuid-entity-template.spec.d.ts +0 -1
- package/dist/tests/templates/entities/uuid-entity-template.spec.js +0 -277
- package/dist/tests/utils/relationship-deduplication.spec.d.ts +0 -1
- 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,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,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,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,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,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})>"
|