go-duck-cli 1.1.37 → 1.1.42

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.
@@ -147,6 +147,8 @@ export const generateKratosCode = async (entities, projectRootDir, projectName,
147
147
  annotation: entity.annotation,
148
148
  isDocument: entity.isDocument || false,
149
149
  isAudited: entity.isAudited || false,
150
+ isFederated: entity.isFederated || false,
151
+ needFmt: (entity.isDocument || false) || !(entity.isFederated || false),
150
152
  projectName,
151
153
  enums
152
154
  };
@@ -278,7 +280,8 @@ import (
278
280
 
279
281
  func TenantServerInterceptor(conf *config.Config, db *gorm.DB) middleware.Middleware {
280
282
  mgr := my_middleware.GetTenantManager(db, conf)
281
- fallbackDB := strings.ToLower(conf.GoDuck.Name) + "_fallback"
283
+ fallbackDB := conf.GoDuck.Datasource.Database
284
+ fallbackMongoDB := conf.GoDuck.Datasource.MongoDB.Database
282
285
 
283
286
  return func(handler middleware.Handler) middleware.Handler {
284
287
  return func(ctx context.Context, req interface{}) (interface{}, error) {
@@ -342,9 +345,9 @@ func TenantServerInterceptor(conf *config.Config, db *gorm.DB) middleware.Middle
342
345
  ctx = context.WithValue(ctx, "tenantDBConn", conn)
343
346
 
344
347
  if conf.GoDuck.Datasource.MongoDB.Enabled {
345
- mClient, err := mgr.GetMongoClient(fallbackDB)
348
+ mClient, err := mgr.GetMongoClient(fallbackMongoDB)
346
349
  if err == nil && mClient != nil {
347
- mDB := mClient.Database(fallbackDB)
350
+ mDB := mClient.Database(fallbackMongoDB)
348
351
  mongoConnections["fallback"] = mDB
349
352
  ctx = context.WithValue(ctx, "tenantMongoDB", mDB)
350
353
  }
@@ -129,7 +129,8 @@ func (m *TenantDBManager) GetMongoClient(dbName string) (*mongo.Client, error) {
129
129
 
130
130
  func TenantMiddleware(db *gorm.DB, cfg *config.Config) gin.HandlerFunc {
131
131
  mgr := GetTenantManager(db, cfg)
132
- fallbackDB := strings.ToLower(cfg.GoDuck.Name) + "_fallback"
132
+ fallbackDB := cfg.GoDuck.Datasource.Database
133
+ fallbackMongoDB := cfg.GoDuck.Datasource.MongoDB.Database
133
134
 
134
135
  return func(c *gin.Context) {
135
136
  userRolesInterface, exists := c.Get("UserRoles")
@@ -207,13 +208,13 @@ func TenantMiddleware(db *gorm.DB, cfg *config.Config) gin.HandlerFunc {
207
208
  c.Set("tenantDBConn", conn)
208
209
 
209
210
  if cfg.GoDuck.Datasource.MongoDB.Enabled {
210
- mClient, err := mgr.GetMongoClient(fallbackDB)
211
+ mClient, err := mgr.GetMongoClient(fallbackMongoDB)
211
212
  if err != nil || mClient == nil {
212
213
  c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to resolve tenant fallback MongoDB connection"})
213
214
  c.Abort()
214
215
  return
215
216
  }
216
- mDB := mClient.Database(fallbackDB)
217
+ mDB := mClient.Database(fallbackMongoDB)
217
218
  mongoConnections["fallback"] = mDB
218
219
  c.Set("tenantMongoDB", mDB)
219
220
  }
@@ -285,7 +286,8 @@ func TenantMiddleware(db *gorm.DB, cfg *config.Config) gin.HandlerFunc {
285
286
 
286
287
  func PublicTenantMiddleware(db *gorm.DB, cfg *config.Config) gin.HandlerFunc {
287
288
  mgr := GetTenantManager(db, cfg)
288
- fallbackDB := strings.ToLower(cfg.GoDuck.Name) + "_fallback"
289
+ fallbackDB := cfg.GoDuck.Datasource.Database
290
+ fallbackMongoDB := cfg.GoDuck.Datasource.MongoDB.Database
289
291
 
290
292
  return func(c *gin.Context) {
291
293
  requestedTenantRaw := strings.ToLower(c.GetHeader("X-Tenant-ID"))
@@ -308,8 +310,10 @@ func PublicTenantMiddleware(db *gorm.DB, cfg *config.Config) gin.HandlerFunc {
308
310
  }
309
311
 
310
312
  dbName := fallbackDB
313
+ mongoDbName := fallbackMongoDB
311
314
  if len(mappings) > 0 {
312
315
  dbName = mappings[0].DBName
316
+ mongoDbName = mappings[0].DBName
313
317
  }
314
318
 
315
319
  tenantConn, err := mgr.GetDB(dbName)
@@ -322,9 +326,9 @@ func PublicTenantMiddleware(db *gorm.DB, cfg *config.Config) gin.HandlerFunc {
322
326
  c.Set("tenantDBConn", tenantConn)
323
327
 
324
328
  if cfg.GoDuck.Datasource.MongoDB.Enabled {
325
- mClient, err := mgr.GetMongoClient(dbName)
329
+ mClient, err := mgr.GetMongoClient(mongoDbName)
326
330
  if err == nil && mClient != nil {
327
- c.Set("tenantMongoDB", mClient.Database(dbName))
331
+ c.Set("tenantMongoDB", mClient.Database(mongoDbName))
328
332
  }
329
333
  }
330
334
 
@@ -140,11 +140,11 @@ export const generatePostmanCollection = async (config, entities, outputDir, ope
140
140
  { key: "X-Tenant-ID", value: "{{tenant}}" }
141
141
  ],
142
142
  url: {
143
- raw: "http://{{host}}:{{port}}/api/audit",
143
+ raw: "http://{{host}}:{{port}}/api/admin/audit",
144
144
  protocol: "http",
145
145
  host: ["{{host}}"],
146
146
  port: "{{port}}",
147
- path: ["api", "audit"]
147
+ path: ["api", "admin", "audit"]
148
148
  }
149
149
  }
150
150
  },
@@ -305,6 +305,17 @@ export const generatePostmanCollection = async (config, entities, outputDir, ope
305
305
  }
306
306
  ];
307
307
 
308
+ if (entity.isSearchable && config.elasticsearch?.enabled) {
309
+ privateItems.push({
310
+ name: `Elasticsearch Search (${capitalized})`,
311
+ request: {
312
+ method: "GET",
313
+ header: [ { key: "Authorization", value: "Bearer {{token}}" }, { key: "X-Tenant-ID", value: "{{tenant}}" } ],
314
+ url: { raw: `http://{{host}}:{{port}}/api/search/${name}?q=Sample`, protocol: "http", host: ["{{host}}"], port: "{{port}}", path: ["api", "search", name], query: [ { key: "q", value: "Sample" } ] }
315
+ }
316
+ });
317
+ }
318
+
308
319
  folder.item.push({ name: "Private (Auth Required) CRUD", item: privateItems });
309
320
  restFolder.item.push(folder);
310
321
  }
@@ -208,7 +208,7 @@ export const generateSwaggerDocs = async (config, entities, outputDir, openEntit
208
208
  }
209
209
 
210
210
  // 2. Add System Paths
211
- swagger.paths['/rpc/{table}'] = {
211
+ swagger.paths['/api/rpc/{table}'] = {
212
212
  get: {
213
213
  tags: ['Search Engine'],
214
214
  summary: 'Generic PostgREST RPC Engine',
@@ -240,7 +240,7 @@ export const generateSwaggerDocs = async (config, entities, outputDir, openEntit
240
240
  }
241
241
  };
242
242
 
243
- swagger.paths['/audit'] = {
243
+ swagger.paths['/api/admin/audit'] = {
244
244
  get: {
245
245
  tags: ['Observability'],
246
246
  summary: 'Fetch Audit Trail',
@@ -249,6 +249,68 @@ export const generateSwaggerDocs = async (config, entities, outputDir, openEntit
249
249
  }
250
250
  };
251
251
 
252
+ swagger.paths['/management/tenant/assign'] = {
253
+ post: {
254
+ tags: ['Management'],
255
+ summary: 'Provision and Assign Tenant Database',
256
+ description: 'Creates a dedicated database for the specified tenant and runs all migrations.',
257
+ requestBody: {
258
+ required: true,
259
+ content: {
260
+ 'application/json': {
261
+ schema: {
262
+ type: 'object',
263
+ properties: {
264
+ roleName: { type: 'string', example: 'tenant_1_admin', description: 'The realm role name mapped to this database context' },
265
+ dbName: { type: 'string', example: 'tenant_1', description: 'The physical name of the database to create and provision' }
266
+ },
267
+ required: ['roleName', 'dbName']
268
+ }
269
+ }
270
+ }
271
+ },
272
+ parameters: [
273
+ { name: 'X-Tenant-ID', in: 'header', required: true, schema: { type: 'string', default: 'master_internal' }, description: 'SuperAdmin internal master bypass token' }
274
+ ],
275
+ responses: {
276
+ 200: { description: 'Success' },
277
+ 401: { description: 'Unauthorized' },
278
+ 403: { description: 'Forbidden (Requires SuperAdmin role)' }
279
+ }
280
+ }
281
+ };
282
+
283
+ if (config.elasticsearch?.enabled) {
284
+ swagger.paths['/api/search/{entity}'] = {
285
+ get: {
286
+ tags: ['Search Engine'],
287
+ summary: 'Elasticsearch Global Search',
288
+ description: 'High-performance full-text search with fuzzy matching across federated silos using Elasticsearch.',
289
+ parameters: [
290
+ ...commonHeaders,
291
+ { name: 'entity', in: 'path', required: true, schema: { type: 'string' }, description: 'The entity name to search (e.g., institute)' },
292
+ { name: 'q', in: 'query', schema: { type: 'string' }, description: 'The search query (Spring-style fuzzy match)' }
293
+ ],
294
+ responses: {
295
+ 200: {
296
+ description: 'OK',
297
+ content: {
298
+ 'application/json': {
299
+ schema: {
300
+ type: 'object',
301
+ properties: {
302
+ total: { type: 'integer', description: 'Total search hits matching query' },
303
+ hits: { type: 'array', items: { type: 'object' }, description: 'Search hits (matching source documents)' }
304
+ }
305
+ }
306
+ }
307
+ }
308
+ }
309
+ }
310
+ }
311
+ };
312
+ }
313
+
252
314
  await fs.writeJson(path.join(docsDir, 'swagger.json'), swagger, { spaces: 2 });
253
315
  console.log(chalk.gray(' Generated Swagger Documentation: swagger.json'));
254
316
  };
package/index.js CHANGED
@@ -642,6 +642,47 @@ program
642
642
  if (await fs.pathExists(p)) await fs.remove(p);
643
643
  }
644
644
 
645
+ const goModPath = path.join(absoluteOutputDir, 'go.mod');
646
+ if (!await fs.pathExists(goModPath)) {
647
+ const goModContent = `module ${config.name}
648
+
649
+ go 1.24
650
+
651
+ require (
652
+ github.com/gin-gonic/gin v1.10.0
653
+ github.com/go-kratos/kratos/v2 v2.8.2
654
+ github.com/google/uuid v1.6.0
655
+ github.com/spf13/viper v1.19.0
656
+ go.opentelemetry.io/otel v1.26.0
657
+ go.opentelemetry.io/otel/trace v1.26.0
658
+ go.opentelemetry.io/otel/sdk v1.26.0
659
+ go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.51.0
660
+ google.golang.org/grpc v1.64.0
661
+ gorm.io/datatypes v1.2.0
662
+ gorm.io/driver/postgres v1.5.7
663
+ gorm.io/gorm v1.25.10
664
+ gorm.io/plugin/opentelemetry v0.1.5
665
+ github.com/golang-jwt/jwt/v5 v5.2.1
666
+ github.com/eclipse/paho.mqtt.golang v1.4.3
667
+ github.com/sony/gobreaker v0.5.0
668
+ github.com/redis/go-redis/v9 v9.5.1
669
+ github.com/gorilla/websocket v1.5.1
670
+ github.com/99designs/gqlgen v0.17.44
671
+ github.com/vektah/gqlparser/v2 v2.5.11
672
+ github.com/aws/aws-lambda-go v1.47.0
673
+ github.com/awslabs/aws-lambda-go-api-proxy v0.16.1
674
+ github.com/GoogleCloudPlatform/functions-framework-go v1.8.1
675
+ go.mongodb.org/mongo-driver v1.17.1
676
+ github.com/gin-contrib/cors v1.7.1
677
+ github.com/go-resty/resty/v2 v2.16.0
678
+ github.com/olivere/elastic/v7 v7.0.32
679
+ github.com/nats-io/nats.go v1.38.0
680
+ )
681
+ `;
682
+ await fs.writeFile(goModPath, goModContent);
683
+ console.log(chalk.green('✅ go.mod file created!'));
684
+ }
685
+
645
686
  await generateConfigLoader(absoluteOutputDir);
646
687
  await generateLoggerCode(config, absoluteOutputDir);
647
688
  await generateMQTTCode(config, absoluteOutputDir);
@@ -657,6 +698,13 @@ program
657
698
  await generateGraphQLCode(config, entities, relationships, absoluteOutputDir, enums);
658
699
  await generatePostgRESTCode(config, absoluteOutputDir);
659
700
 
701
+ if (config.multitenancy?.enabled) await generateMultitenancy(config, absoluteOutputDir, entities);
702
+ await generateAuditCode(config, absoluteOutputDir);
703
+ await generateMeteringCode(config, absoluteOutputDir);
704
+ await generateOutbox(absoluteOutputDir, config);
705
+ await generateNATSCode(config, absoluteOutputDir);
706
+ await generateBrokerCode(config, absoluteOutputDir);
707
+
660
708
  // Sync Search & Security
661
709
  await generateElasticsearchLayer(config, entities, absoluteOutputDir);
662
710
  await generateSecurityMiddleware(config, absoluteOutputDir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "go-duck-cli",
3
- "version": "1.1.37",
3
+ "version": "1.1.42",
4
4
  "description": "The Ultimate Evolutionary Go Microservice Scaffolder.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -147,6 +147,11 @@ func (ctrl *{{capitalize name}}Controller) GetAll(c *gin.Context) {
147
147
  page, _ := strconv.Atoi(c.DefaultQuery("page", "0"))
148
148
  size, _ := strconv.Atoi(c.DefaultQuery("size", "20"))
149
149
 
150
+ offset := 0
151
+ if page > 0 {
152
+ offset = (page - 1) * size
153
+ }
154
+
150
155
  {{#if isDocument}}
151
156
  // 🦆 MongoDB Path
152
157
  {{#if isFederated}}
@@ -163,7 +168,7 @@ func (ctrl *{{capitalize name}}Controller) GetAll(c *gin.Context) {
163
168
  go func(r string, db *mongo.Database) {
164
169
  defer wg.Done()
165
170
  var entities []models.{{capitalize name}}
166
- opts := options.Find().SetSkip(int64(page * size)).SetLimit(int64(size))
171
+ opts := options.Find().SetSkip(int64(offset)).SetLimit(int64(size))
167
172
  cursor, err := db.Collection("{{toLowerCase name}}s").Find(ctx, bson.M{}, opts)
168
173
  if err == nil {
169
174
  cursor.All(ctx, &entities)
@@ -194,7 +199,7 @@ func (ctrl *{{capitalize name}}Controller) GetAll(c *gin.Context) {
194
199
  c.Header("X-Total-Count", strconv.FormatInt(totalCount, 10))
195
200
 
196
201
  var entities []models.{{capitalize name}}
197
- opts := options.Find().SetSkip(int64(page * size)).SetLimit(int64(size))
202
+ opts := options.Find().SetSkip(int64(offset)).SetLimit(int64(size))
198
203
  cursor, err := tenantDB.Collection("{{toLowerCase name}}s").Find(ctx, filter, opts)
199
204
  if err != nil {
200
205
  c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
@@ -222,7 +227,7 @@ func (ctrl *{{capitalize name}}Controller) GetAll(c *gin.Context) {
222
227
  go func(r string, db *gorm.DB) {
223
228
  defer wg.Done()
224
229
  var entities []models.{{capitalize name}}
225
- if err := db.WithContext(ctx).Offset(page * size).Limit(size).Find(&entities).Error; err == nil {
230
+ if err := db.WithContext(ctx).Offset(offset).Limit(size).Find(&entities).Error; err == nil {
226
231
  mu.Lock()
227
232
  federatedData[r] = entities
228
233
  mu.Unlock()
@@ -243,7 +248,7 @@ func (ctrl *{{capitalize name}}Controller) GetAll(c *gin.Context) {
243
248
  c.Header("X-Total-Count", strconv.FormatInt(totalCount, 10))
244
249
 
245
250
  var entities []models.{{capitalize name}}
246
- if err := tenantDB.WithContext(ctx).Offset(page * size).Limit(size).Find(&entities).Error; err != nil {
251
+ if err := tenantDB.WithContext(ctx).Offset(offset).Limit(size).Find(&entities).Error; err != nil {
247
252
  c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
248
253
  return
249
254
  }
@@ -5,7 +5,6 @@ import (
5
5
  "fmt"
6
6
  "log"
7
7
  "net/http"
8
- "strings"
9
8
  "time"
10
9
 
11
10
  "github.com/gin-gonic/gin"
@@ -87,29 +86,32 @@ func SetupRouter(appConfig *config.Config) *gin.Engine {
87
86
  logger.Error("Goose Migration failed: %v", err)
88
87
  }
89
88
 
90
- // 10b. Initialize and Migrate Fallback DB if multi-tenancy is enabled
89
+ // 10b. Initialize and Migrate Fallback DB if multi-tenancy is enabled and different from master DB
91
90
  if appConfig.GoDuck.Multitenancy.Enabled {
92
- fallbackDB := strings.ToLower(appConfig.GoDuck.Name) + "_fallback"
93
- var exists int
94
- masterDB.Raw("SELECT 1 FROM pg_database WHERE datname = ?", fallbackDB).Scan(&exists)
95
- if exists == 0 {
96
- if err := masterDB.Exec(fmt.Sprintf("CREATE DATABASE \"%s\"", fallbackDB)).Error; err != nil {
97
- log.Printf("Warning: Failed to create fallback database %s: %v", fallbackDB, err)
98
- } else {
99
- log.Printf(" Created fallback database: %s", fallbackDB)
91
+ fallbackDB := appConfig.GoDuck.Datasource.Database
92
+ masterDBName := appConfig.GoDuck.Datasource.Database
93
+ if fallbackDB != masterDBName {
94
+ var exists int
95
+ masterDB.Raw("SELECT 1 FROM pg_database WHERE datname = ?", fallbackDB).Scan(&exists)
96
+ if exists == 0 {
97
+ if err := masterDB.Exec(fmt.Sprintf("CREATE DATABASE \"%s\"", fallbackDB)).Error; err != nil {
98
+ log.Printf("Warning: Failed to create fallback database %s: %v", fallbackDB, err)
99
+ } else {
100
+ log.Printf("✅ Created fallback database: %s", fallbackDB)
101
+ }
100
102
  }
101
- }
102
103
 
103
- // Run tenant migrations on the fallback database
104
- mgr := middleware.GetTenantManager(masterDB, appConfig)
105
- if fDB, err := mgr.GetDB(fallbackDB); err == nil {
106
- if err := migrations.RunGoNativeMigrationsForTenant(fDB); err != nil {
107
- log.Printf("Warning: Failed to run migrations on fallback database: %v", err)
104
+ // Run tenant migrations on the fallback database
105
+ mgr := middleware.GetTenantManager(masterDB, appConfig)
106
+ if fDB, err := mgr.GetDB(fallbackDB); err == nil {
107
+ if err := migrations.RunGoNativeMigrationsForTenant(fDB); err != nil {
108
+ log.Printf("Warning: Failed to run migrations on fallback database: %v", err)
109
+ } else {
110
+ log.Printf("✅ Migrated fallback database: %s", fallbackDB)
111
+ }
108
112
  } else {
109
- log.Printf(" Migrated fallback database: %s", fallbackDB)
113
+ log.Printf("Warning: Failed to connect to fallback database for migration: %v", err)
110
114
  }
111
- } else {
112
- log.Printf("Warning: Failed to connect to fallback database for migration: %v", err)
113
115
  }
114
116
  }
115
117
  }
@@ -2,7 +2,7 @@ package service
2
2
 
3
3
  import (
4
4
  "context"
5
- {{#if isDocument}}
5
+ {{#if needFmt}}
6
6
  "fmt"
7
7
  {{/if}}
8
8
  {{#if (hasNested fields)}}
@@ -38,10 +38,19 @@ func New{{capitalize name}}Service(repo *repository.Repository) *{{capitalize na
38
38
 
39
39
  func (s *{{capitalize name}}Service) Create{{capitalize name}}(ctx context.Context, req *pb.Create{{capitalize name}}Request) (*pb.{{capitalize name}}Reply, error) {
40
40
  {{#if isDocument}}
41
+ {{#if isFederated}}
42
+ siloConns, _ := ctx.Value("tenantMongoConnections").(map[string]*mongo.Database)
43
+ {{else}}
41
44
  db, ok := ctx.Value("tenantMongoDB").(*mongo.Database)
42
45
  if !ok { return nil, fmt.Errorf("mongo database not found in context") }
46
+ {{/if}}
43
47
  {{else}}
48
+ {{#if isFederated}}
44
49
  siloConns, _ := ctx.Value("tenantSiloConns").(map[string]*gorm.DB)
50
+ {{else}}
51
+ db, ok := ctx.Value("tenantDBConn").(*gorm.DB)
52
+ if !ok { return nil, fmt.Errorf("database not found in context") }
53
+ {{/if}}
45
54
  {{/if}}
46
55
  federatedData := make(map[string]*pb.{{capitalize name}})
47
56
 
@@ -67,17 +76,33 @@ func (s *{{capitalize name}}Service) Create{{capitalize name}}(ctx context.Conte
67
76
  {{#each fields}}{{#if isNested}}
68
77
  json.Unmarshal([]byte(req.{{toProtoFieldName name}}), &entity.{{capitalize name}})
69
78
  {{/if}}{{/each}}
79
+ {{#if isFederated}}
80
+ for role, dbCon := range siloConns {
81
+ res, err := dbCon.Collection("{{toLowerCase name}}s").InsertOne(ctx, entity)
82
+ if err == nil {
83
+ entity.ID = fmt.Sprintf("%v", res.InsertedID)
84
+ federatedData[role] = map{{capitalize name}}ToPb(entity)
85
+ }
86
+ }
87
+ {{else}}
70
88
  res, err := db.Collection("{{toLowerCase name}}s").InsertOne(ctx, entity)
71
89
  if err == nil {
72
90
  entity.ID = fmt.Sprintf("%v", res.InsertedID)
73
91
  federatedData["tenant"] = map{{capitalize name}}ToPb(entity)
74
92
  }
93
+ {{/if}}
75
94
  {{else}}
76
- for role, db := range siloConns {
77
- if err := db.WithContext(ctx).Create(entity).Error; err == nil {
95
+ {{#if isFederated}}
96
+ for role, dbCon := range siloConns {
97
+ if err := dbCon.WithContext(ctx).Create(entity).Error; err == nil {
78
98
  federatedData[role] = map{{capitalize name}}ToPb(entity)
79
99
  }
80
100
  }
101
+ {{else}}
102
+ if err := db.WithContext(ctx).Create(entity).Error; err == nil {
103
+ federatedData["tenant"] = map{{capitalize name}}ToPb(entity)
104
+ }
105
+ {{/if}}
81
106
  {{/if}}
82
107
 
83
108
  return &pb.{{capitalize name}}Reply{
@@ -89,12 +114,25 @@ func (s *{{capitalize name}}Service) Get{{capitalize name}}(ctx context.Context,
89
114
  federatedData := make(map[string]*pb.{{capitalize name}})
90
115
 
91
116
  {{#if isDocument}}
92
- db, _ := ctx.Value("tenantMongoDB").(*mongo.Database)
93
- var entity models.{{capitalize name}}
94
- if err := db.Collection("{{toLowerCase name}}s").FindOne(ctx, bson.M{"_id": req.Id}).Decode(&entity); err == nil {
95
- federatedData["tenant"] = map{{capitalize name}}ToPb(&entity)
117
+ {{#if isFederated}}
118
+ siloConns, _ := ctx.Value("tenantMongoConnections").(map[string]*mongo.Database)
119
+ for role, db := range siloConns {
120
+ var entity models.{{capitalize name}}
121
+ if err := db.Collection("{{toLowerCase name}}s").FindOne(ctx, bson.M{"_id": req.Id}).Decode(&entity); err == nil {
122
+ federatedData[role] = map{{capitalize name}}ToPb(&entity)
123
+ }
124
+ }
125
+ {{else}}
126
+ db, ok := ctx.Value("tenantMongoDB").(*mongo.Database)
127
+ if ok {
128
+ var entity models.{{capitalize name}}
129
+ if err := db.Collection("{{toLowerCase name}}s").FindOne(ctx, bson.M{"_id": req.Id}).Decode(&entity); err == nil {
130
+ federatedData["tenant"] = map{{capitalize name}}ToPb(&entity)
131
+ }
96
132
  }
133
+ {{/if}}
97
134
  {{else}}
135
+ {{#if isFederated}}
98
136
  siloConns, _ := ctx.Value("tenantSiloConns").(map[string]*gorm.DB)
99
137
  for role, db := range siloConns {
100
138
  var entity models.{{capitalize name}}
@@ -102,6 +140,15 @@ func (s *{{capitalize name}}Service) Get{{capitalize name}}(ctx context.Context,
102
140
  federatedData[role] = map{{capitalize name}}ToPb(&entity)
103
141
  }
104
142
  }
143
+ {{else}}
144
+ db, ok := ctx.Value("tenantDBConn").(*gorm.DB)
145
+ if ok {
146
+ var entity models.{{capitalize name}}
147
+ if err := db.WithContext(ctx).First(&entity, req.Id).Error; err == nil {
148
+ federatedData["tenant"] = map{{capitalize name}}ToPb(&entity)
149
+ }
150
+ }
151
+ {{/if}}
105
152
  {{/if}}
106
153
 
107
154
  return &pb.{{capitalize name}}Reply{
@@ -133,17 +180,37 @@ func (s *{{capitalize name}}Service) Update{{capitalize name}}(ctx context.Conte
133
180
  {{/each}}
134
181
 
135
182
  {{#if isDocument}}
136
- db, _ := ctx.Value("tenantMongoDB").(*mongo.Database)
137
- if _, err := db.Collection("{{toLowerCase name}}s").ReplaceOne(ctx, bson.M{"_id": req.Id}, entity); err == nil {
138
- federatedData["tenant"] = map{{capitalize name}}ToPb(&entity)
183
+ {{#if isFederated}}
184
+ siloConns, _ := ctx.Value("tenantMongoConnections").(map[string]*mongo.Database)
185
+ for role, db := range siloConns {
186
+ if _, err := db.Collection("{{toLowerCase name}}s").ReplaceOne(ctx, bson.M{"_id": req.Id}, entity); err == nil {
187
+ federatedData[role] = map{{capitalize name}}ToPb(&entity)
188
+ }
139
189
  }
140
190
  {{else}}
191
+ db, ok := ctx.Value("tenantMongoDB").(*mongo.Database)
192
+ if ok {
193
+ if _, err := db.Collection("{{toLowerCase name}}s").ReplaceOne(ctx, bson.M{"_id": req.Id}, entity); err == nil {
194
+ federatedData["tenant"] = map{{capitalize name}}ToPb(&entity)
195
+ }
196
+ }
197
+ {{/if}}
198
+ {{else}}
199
+ {{#if isFederated}}
141
200
  siloConns, _ := ctx.Value("tenantSiloConns").(map[string]*gorm.DB)
142
201
  for role, db := range siloConns {
143
202
  if err := db.WithContext(ctx).Model(&models.{{capitalize name}}{}).Where("id = ?", req.Id).Save(&entity).Error; err == nil {
144
203
  federatedData[role] = map{{capitalize name}}ToPb(&entity)
145
204
  }
146
205
  }
206
+ {{else}}
207
+ db, ok := ctx.Value("tenantDBConn").(*gorm.DB)
208
+ if ok {
209
+ if err := db.WithContext(ctx).Model(&models.{{capitalize name}}{}).Where("id = ?", req.Id).Save(&entity).Error; err == nil {
210
+ federatedData["tenant"] = map{{capitalize name}}ToPb(&entity)
211
+ }
212
+ }
213
+ {{/if}}
147
214
  {{/if}}
148
215
 
149
216
  return &pb.{{capitalize name}}Reply{
@@ -153,13 +220,29 @@ func (s *{{capitalize name}}Service) Update{{capitalize name}}(ctx context.Conte
153
220
 
154
221
  func (s *{{capitalize name}}Service) Delete{{capitalize name}}(ctx context.Context, req *pb.Delete{{capitalize name}}Request) (*pb.Delete{{capitalize name}}Reply, error) {
155
222
  {{#if isDocument}}
156
- db, _ := ctx.Value("tenantMongoDB").(*mongo.Database)
157
- db.Collection("{{toLowerCase name}}s").DeleteOne(ctx, bson.M{"_id": req.Id})
223
+ {{#if isFederated}}
224
+ siloConns, _ := ctx.Value("tenantMongoConnections").(map[string]*mongo.Database)
225
+ for _, db := range siloConns {
226
+ db.Collection("{{toLowerCase name}}s").DeleteOne(ctx, bson.M{"_id": req.Id})
227
+ }
228
+ {{else}}
229
+ db, ok := ctx.Value("tenantMongoDB").(*mongo.Database)
230
+ if ok {
231
+ db.Collection("{{toLowerCase name}}s").DeleteOne(ctx, bson.M{"_id": req.Id})
232
+ }
233
+ {{/if}}
158
234
  {{else}}
235
+ {{#if isFederated}}
159
236
  siloConns, _ := ctx.Value("tenantSiloConns").(map[string]*gorm.DB)
160
237
  for _, db := range siloConns {
161
238
  db.WithContext(ctx).Where("id = ?", req.Id).Delete(&models.{{capitalize name}}{})
162
239
  }
240
+ {{else}}
241
+ db, ok := ctx.Value("tenantDBConn").(*gorm.DB)
242
+ if ok {
243
+ db.WithContext(ctx).Where("id = ?", req.Id).Delete(&models.{{capitalize name}}{})
244
+ }
245
+ {{/if}}
163
246
  {{/if}}
164
247
  return &pb.Delete{{capitalize name}}Reply{Message: "Delete completed"}, nil
165
248
  }
@@ -169,21 +252,43 @@ func (s *{{capitalize name}}Service) List{{capitalize name}}(ctx context.Context
169
252
  var total int64
170
253
 
171
254
  {{#if isDocument}}
172
- db, _ := ctx.Value("tenantMongoDB").(*mongo.Database)
173
- var results []models.{{capitalize name}}
174
- opts := options.Find().SetSkip(int64((req.Page - 1) * req.PageSize)).SetLimit(int64(req.PageSize))
175
- cursor, err := db.Collection("{{toLowerCase name}}s").Find(ctx, bson.M{}, opts)
176
- if err == nil {
177
- cursor.All(ctx, &results)
178
- items := make([]*pb.{{capitalize name}}, len(results))
179
- for i, r := range results {
180
- items[i] = map{{capitalize name}}ToPb(&r)
255
+ {{#if isFederated}}
256
+ siloConns, _ := ctx.Value("tenantMongoConnections").(map[string]*mongo.Database)
257
+ for role, db := range siloConns {
258
+ var results []models.{{capitalize name}}
259
+ opts := options.Find().SetSkip(int64((req.Page - 1) * req.PageSize)).SetLimit(int64(req.PageSize))
260
+ cursor, err := db.Collection("{{toLowerCase name}}s").Find(ctx, bson.M{}, opts)
261
+ if err == nil {
262
+ cursor.All(ctx, &results)
263
+ items := make([]*pb.{{capitalize name}}, len(results))
264
+ for i, r := range results {
265
+ items[i] = map{{capitalize name}}ToPb(&r)
266
+ }
267
+ federatedResults[role] = &pb.{{capitalize name}}List{Items: items}
268
+ count, _ := db.Collection("{{toLowerCase name}}s").CountDocuments(ctx, bson.M{})
269
+ total += count
181
270
  }
182
- federatedResults["tenant"] = &pb.{{capitalize name}}List{Items: items}
183
- count, _ := db.Collection("{{toLowerCase name}}s").CountDocuments(ctx, bson.M{})
184
- total = count
185
271
  }
186
272
  {{else}}
273
+ db, ok := ctx.Value("tenantMongoDB").(*mongo.Database)
274
+ if ok {
275
+ var results []models.{{capitalize name}}
276
+ opts := options.Find().SetSkip(int64((req.Page - 1) * req.PageSize)).SetLimit(int64(req.PageSize))
277
+ cursor, err := db.Collection("{{toLowerCase name}}s").Find(ctx, bson.M{}, opts)
278
+ if err == nil {
279
+ cursor.All(ctx, &results)
280
+ items := make([]*pb.{{capitalize name}}, len(results))
281
+ for i, r := range results {
282
+ items[i] = map{{capitalize name}}ToPb(&r)
283
+ }
284
+ federatedResults["tenant"] = &pb.{{capitalize name}}List{Items: items}
285
+ count, _ := db.Collection("{{toLowerCase name}}s").CountDocuments(ctx, bson.M{})
286
+ total = count
287
+ }
288
+ }
289
+ {{/if}}
290
+ {{else}}
291
+ {{#if isFederated}}
187
292
  siloConns, _ := ctx.Value("tenantSiloConns").(map[string]*gorm.DB)
188
293
  for role, db := range siloConns {
189
294
  var results []models.{{capitalize name}}
@@ -202,6 +307,26 @@ func (s *{{capitalize name}}Service) List{{capitalize name}}(ctx context.Context
202
307
  federatedResults[role] = &pb.{{capitalize name}}List{Items: items}
203
308
  }
204
309
  }
310
+ {{else}}
311
+ db, ok := ctx.Value("tenantDBConn").(*gorm.DB)
312
+ if ok {
313
+ var results []models.{{capitalize name}}
314
+ query := db.WithContext(ctx).Model(&models.{{capitalize name}}{})
315
+
316
+ var count int64
317
+ query.Count(&count)
318
+ total = count
319
+
320
+ offset := (req.Page - 1) * req.PageSize
321
+ if err := query.Limit(int(req.PageSize)).Offset(int(offset)).Find(&results).Error; err == nil {
322
+ items := make([]*pb.{{capitalize name}}, len(results))
323
+ for i, r := range results {
324
+ items[i] = map{{capitalize name}}ToPb(&r)
325
+ }
326
+ federatedResults["tenant"] = &pb.{{capitalize name}}List{Items: items}
327
+ }
328
+ }
329
+ {{/if}}
205
330
  {{/if}}
206
331
 
207
332
  return &pb.List{{capitalize name}}Reply{