go-duck-cli 1.0.8 → 1.1.1
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/README.md +30 -15
- package/generators/ai_docs.js +130 -0
- package/generators/broker.js +63 -0
- package/generators/config.js +149 -7
- package/generators/devops.js +210 -43
- package/generators/docs.js +23 -4
- package/generators/elasticsearch.js +263 -0
- package/generators/kratos.js +229 -41
- package/generators/metering.js +280 -48
- package/generators/migrations.js +92 -198
- package/generators/mqtt.js +2 -39
- package/generators/multitenancy.js +274 -71
- package/generators/nats.js +39 -0
- package/generators/outbox.js +171 -0
- package/generators/postgrest.js +7 -3
- package/generators/postman.js +405 -0
- package/generators/repository.js +27 -0
- package/generators/router.js +27 -0
- package/generators/security.js +95 -14
- package/generators/serverless.js +147 -0
- package/generators/storage.js +589 -0
- package/generators/swagger.js +84 -60
- package/generators/telemetry.js +23 -32
- package/generators/websocket.js +55 -21
- package/index.js +481 -116
- package/package.json +6 -4
- package/parser/gdl.js +163 -24
- package/templates/docs/index.html.hbs +5 -5
- package/templates/docs/layout.hbs +221 -62
- package/templates/docs/pages/audit.hbs +83 -35
- package/templates/docs/pages/cli.hbs +18 -0
- package/templates/docs/pages/configuration.hbs +241 -0
- package/templates/docs/pages/datadog.hbs +46 -0
- package/templates/docs/pages/elasticsearch.hbs +121 -0
- package/templates/docs/pages/federation.hbs +241 -0
- package/templates/docs/pages/gdl-advanced.hbs +91 -0
- package/templates/docs/pages/gdl-annotations.hbs +137 -0
- package/templates/docs/pages/gdl-entities.hbs +134 -0
- package/templates/docs/pages/gdl-relationships.hbs +80 -0
- package/templates/docs/pages/gdl.hbs +60 -204
- package/templates/docs/pages/graphql.hbs +58 -44
- package/templates/docs/pages/grpc.hbs +53 -90
- package/templates/docs/pages/hybrid-store.hbs +127 -0
- package/templates/docs/pages/index.hbs +418 -149
- package/templates/docs/pages/keycloak.hbs +43 -0
- package/templates/docs/pages/legend.hbs +116 -0
- package/templates/docs/pages/mosquitto.hbs +39 -0
- package/templates/docs/pages/multitenancy.hbs +139 -71
- package/templates/docs/pages/otel.hbs +40 -0
- package/templates/docs/pages/realtime.hbs +38 -12
- package/templates/docs/pages/redis.hbs +40 -0
- package/templates/docs/pages/rest.hbs +120 -202
- package/templates/docs/pages/saga.hbs +94 -0
- package/templates/docs/pages/security.hbs +150 -44
- package/templates/docs/pages/serverless.hbs +157 -0
- package/templates/docs/pages/storage.hbs +127 -0
- package/templates/docs/pages/wizard.hbs +683 -0
- package/templates/docs/triple_identity_registry.png +0 -0
- package/templates/go/controller.go.hbs +287 -283
- package/templates/go/entity.go.hbs +17 -15
- package/templates/go/main.go.hbs +47 -180
- package/templates/go/migrator.go.hbs +65 -0
- package/templates/go/router.go.hbs +272 -0
- package/templates/graphql/resolver.go.hbs +53 -34
- package/templates/graphql/schema.graphql.hbs +17 -5
- package/templates/kratos/service.go.hbs +169 -34
- package/templates/proto/entity.proto.hbs +10 -14
- package/test_nested.gdl +21 -0
- package/templates/docs/intro.mp4 +0 -0
- package/test_parser.js +0 -9
package/generators/swagger.js
CHANGED
|
@@ -2,7 +2,7 @@ import fs from 'fs-extra';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
|
|
5
|
-
export const generateSwaggerDocs = async (config, entities, outputDir) => {
|
|
5
|
+
export const generateSwaggerDocs = async (config, entities, outputDir, openEntities = []) => {
|
|
6
6
|
const docsDir = path.join(outputDir, 'docs');
|
|
7
7
|
await fs.ensureDir(docsDir);
|
|
8
8
|
|
|
@@ -14,7 +14,7 @@ export const generateSwaggerDocs = async (config, entities, outputDir) => {
|
|
|
14
14
|
description: `Generated documentation for ${config.name} microservice`
|
|
15
15
|
},
|
|
16
16
|
servers: [
|
|
17
|
-
{ url: 'http://localhost:8080
|
|
17
|
+
{ url: 'http://localhost:8080', description: 'Local Development Server' }
|
|
18
18
|
],
|
|
19
19
|
paths: {},
|
|
20
20
|
components: {
|
|
@@ -28,7 +28,7 @@ export const generateSwaggerDocs = async (config, entities, outputDir) => {
|
|
|
28
28
|
type: 'apiKey',
|
|
29
29
|
in: 'header',
|
|
30
30
|
name: 'X-Tenant-ID',
|
|
31
|
-
description: 'The unique identifier for the tenant
|
|
31
|
+
description: 'The unique identifier for the tenant context'
|
|
32
32
|
}
|
|
33
33
|
},
|
|
34
34
|
schemas: {
|
|
@@ -40,6 +40,8 @@ export const generateSwaggerDocs = async (config, entities, outputDir) => {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
},
|
|
43
|
+
// Global security applies to /api by default in our implementation,
|
|
44
|
+
// but Swagger paths can override this.
|
|
43
45
|
security: [
|
|
44
46
|
{ BearerAuth: [], TenantID: [] }
|
|
45
47
|
]
|
|
@@ -49,6 +51,25 @@ export const generateSwaggerDocs = async (config, entities, outputDir) => {
|
|
|
49
51
|
{ name: 'X-Tenant-ID', in: 'header', required: true, schema: { type: 'string', default: 'default' }, description: 'Multi-tenancy context identifier' }
|
|
50
52
|
];
|
|
51
53
|
|
|
54
|
+
const isOpen = (entityName, action) => {
|
|
55
|
+
if (!openEntities || !Array.isArray(openEntities)) return false;
|
|
56
|
+
|
|
57
|
+
// Check wildcard first
|
|
58
|
+
const wildcard = openEntities.find(e => e.name === '*');
|
|
59
|
+
if (wildcard) {
|
|
60
|
+
if (!action) return true;
|
|
61
|
+
if (wildcard.actions.includes(action.toLowerCase())) return true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const entry = openEntities.find(e => e.name.toLowerCase() === entityName.toLowerCase());
|
|
65
|
+
if (entry) {
|
|
66
|
+
if (!action) return true;
|
|
67
|
+
if (entry.actions.includes(action.toLowerCase())) return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return false;
|
|
71
|
+
};
|
|
72
|
+
|
|
52
73
|
// 1. Add Entity Paths
|
|
53
74
|
for (const entity of entities) {
|
|
54
75
|
const name = entity.name.toLowerCase();
|
|
@@ -68,22 +89,34 @@ export const generateSwaggerDocs = async (config, entities, outputDir) => {
|
|
|
68
89
|
}
|
|
69
90
|
};
|
|
70
91
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
92
|
+
const addEntityOperations = (basePath, isPublic) => {
|
|
93
|
+
const security = isPublic ? [{ TenantID: [] }] : undefined; // undefined uses global security
|
|
94
|
+
|
|
95
|
+
// Unified helper for path registration based on permissions
|
|
96
|
+
const regPath = (path, method, op, action) => {
|
|
97
|
+
if (isPublic && !isOpen(capitalized, action)) return;
|
|
98
|
+
|
|
99
|
+
if (!swagger.paths[path]) swagger.paths[path] = {};
|
|
100
|
+
swagger.paths[path][method] = {
|
|
101
|
+
...op,
|
|
102
|
+
tags: [isPublic ? `${capitalized} (Public)` : capitalized],
|
|
103
|
+
summary: `${op.summary} ${isPublic ? '(Public)' : ''}`,
|
|
104
|
+
security: security
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// CRUD Operations
|
|
109
|
+
regPath(`${basePath}/${name}s`, 'post', {
|
|
75
110
|
summary: `Create a new ${capitalized}`,
|
|
76
111
|
parameters: [...commonHeaders],
|
|
77
112
|
requestBody: {
|
|
78
113
|
required: true,
|
|
79
114
|
content: { 'application/json': { schema: { $ref: `#/components/schemas/${capitalized}` } } }
|
|
80
115
|
},
|
|
81
|
-
responses: {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
},
|
|
85
|
-
get: {
|
|
86
|
-
tags: [capitalized],
|
|
116
|
+
responses: { 201: { description: 'Created', content: { 'application/json': { schema: { $ref: `#/components/schemas/${capitalized}` } } } } }
|
|
117
|
+
}, 'create');
|
|
118
|
+
|
|
119
|
+
regPath(`${basePath}/${name}s`, 'get', {
|
|
87
120
|
summary: `Get all ${capitalized}s`,
|
|
88
121
|
parameters: [
|
|
89
122
|
...commonHeaders,
|
|
@@ -91,72 +124,59 @@ export const generateSwaggerDocs = async (config, entities, outputDir) => {
|
|
|
91
124
|
{ name: 'size', in: 'query', schema: { type: 'integer' }, description: 'Records per page' },
|
|
92
125
|
{ name: 'eager', in: 'query', schema: { type: 'boolean' }, description: 'If true, performs SQL Join to fetch relations' }
|
|
93
126
|
],
|
|
94
|
-
responses: {
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
};
|
|
127
|
+
responses: { 200: { description: 'OK', content: { 'application/json': { schema: { type: 'array', items: { $ref: `#/components/schemas/${capitalized}` } } } } } }
|
|
128
|
+
}, 'read');
|
|
99
129
|
|
|
100
|
-
|
|
101
|
-
swagger.paths[`/${name}s/{id}`] = {
|
|
102
|
-
get: {
|
|
103
|
-
tags: [capitalized],
|
|
130
|
+
regPath(`${basePath}/${name}s/{id}`, 'get', {
|
|
104
131
|
summary: `Get ${capitalized} by ID`,
|
|
105
132
|
parameters: [
|
|
106
133
|
...commonHeaders,
|
|
107
134
|
{ name: 'id', in: 'path', required: true, schema: { type: 'integer' } },
|
|
108
135
|
{ name: 'eager', in: 'query', schema: { type: 'boolean' } }
|
|
109
136
|
],
|
|
110
|
-
responses: {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
},
|
|
114
|
-
put: {
|
|
115
|
-
tags: [capitalized],
|
|
137
|
+
responses: { 200: { description: 'OK', content: { 'application/json': { schema: { $ref: `#/components/schemas/${capitalized}` } } } } }
|
|
138
|
+
}, 'read');
|
|
139
|
+
|
|
140
|
+
regPath(`${basePath}/${name}s/{id}`, 'put', {
|
|
116
141
|
summary: `Update ${capitalized}`,
|
|
117
142
|
parameters: [...commonHeaders, { name: 'id', in: 'path', required: true, schema: { type: 'integer' } }],
|
|
118
|
-
responses: {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
},
|
|
122
|
-
|
|
123
|
-
|
|
143
|
+
responses: { 200: { description: 'Updated', content: { 'application/json': { schema: { $ref: `#/components/schemas/${capitalized}` } } } } }
|
|
144
|
+
}, 'update');
|
|
145
|
+
|
|
146
|
+
regPath(`${basePath}/${name}s/{id}`, 'patch', {
|
|
147
|
+
summary: `Patch ${capitalized}`,
|
|
148
|
+
parameters: [...commonHeaders, { name: 'id', in: 'path', required: true, schema: { type: 'integer' } }],
|
|
149
|
+
responses: { 200: { description: 'Patched' } }
|
|
150
|
+
}, 'update');
|
|
151
|
+
|
|
152
|
+
regPath(`${basePath}/${name}s/{id}`, 'delete', {
|
|
124
153
|
summary: `Delete ${capitalized}`,
|
|
125
154
|
parameters: [...commonHeaders, { name: 'id', in: 'path', required: true, schema: { type: 'integer' } }],
|
|
126
|
-
responses: {
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
};
|
|
155
|
+
responses: { 204: { description: 'No Content' } }
|
|
156
|
+
}, 'delete');
|
|
131
157
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
post: {
|
|
135
|
-
tags: [capitalized],
|
|
158
|
+
// Bulk Operations
|
|
159
|
+
regPath(`${basePath}/${name}s/bulk`, 'post', {
|
|
136
160
|
summary: `Bulk Create ${capitalized}s`,
|
|
137
161
|
parameters: [...commonHeaders],
|
|
138
162
|
requestBody: {
|
|
139
163
|
required: true,
|
|
140
164
|
content: { 'application/json': { schema: { type: 'array', items: { $ref: `#/components/schemas/${capitalized}` } } } }
|
|
141
165
|
},
|
|
142
|
-
responses: {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
},
|
|
146
|
-
put: {
|
|
147
|
-
tags: [capitalized],
|
|
166
|
+
responses: { 201: { description: 'Created' } }
|
|
167
|
+
}, 'create');
|
|
168
|
+
|
|
169
|
+
regPath(`${basePath}/${name}s/bulk`, 'put', {
|
|
148
170
|
summary: `Bulk Update ${capitalized}s`,
|
|
149
171
|
parameters: [...commonHeaders],
|
|
150
172
|
requestBody: {
|
|
151
173
|
required: true,
|
|
152
174
|
content: { 'application/json': { schema: { type: 'array', items: { $ref: `#/components/schemas/${capitalized}` } } } }
|
|
153
175
|
},
|
|
154
|
-
responses: {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
},
|
|
158
|
-
patch: {
|
|
159
|
-
tags: [capitalized],
|
|
176
|
+
responses: { 200: { description: 'Updated' } }
|
|
177
|
+
}, 'update');
|
|
178
|
+
|
|
179
|
+
regPath(`${basePath}/${name}s/bulk`, 'patch', {
|
|
160
180
|
summary: `Bulk Patch ${capitalized}s`,
|
|
161
181
|
parameters: [...commonHeaders],
|
|
162
182
|
requestBody: {
|
|
@@ -173,14 +193,18 @@ export const generateSwaggerDocs = async (config, entities, outputDir) => {
|
|
|
173
193
|
}
|
|
174
194
|
}
|
|
175
195
|
}
|
|
176
|
-
}
|
|
196
|
+
}
|
|
177
197
|
}
|
|
178
198
|
},
|
|
179
|
-
responses: {
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
}
|
|
199
|
+
responses: { 200: { description: 'Patched' } }
|
|
200
|
+
}, 'update');
|
|
183
201
|
};
|
|
202
|
+
|
|
203
|
+
// 1a. Secured Paths
|
|
204
|
+
addEntityOperations('/api', false);
|
|
205
|
+
|
|
206
|
+
// 1b. Public Paths (if marked as open)
|
|
207
|
+
addEntityOperations('/open/api', true);
|
|
184
208
|
}
|
|
185
209
|
|
|
186
210
|
// 2. Add System Paths
|
package/generators/telemetry.js
CHANGED
|
@@ -4,7 +4,7 @@ import chalk from 'chalk';
|
|
|
4
4
|
|
|
5
5
|
export const generateTelemetryCode = async (config, outputDir) => {
|
|
6
6
|
const telemetryDir = path.join(outputDir, 'internal/telemetry');
|
|
7
|
-
const k8sDir = path.join(outputDir, 'k8s');
|
|
7
|
+
const k8sDir = path.join(outputDir, 'devops/k8s');
|
|
8
8
|
|
|
9
9
|
await fs.ensureDir(telemetryDir);
|
|
10
10
|
await fs.ensureDir(k8sDir);
|
|
@@ -16,7 +16,6 @@ import (
|
|
|
16
16
|
"context"
|
|
17
17
|
"fmt"
|
|
18
18
|
"log"
|
|
19
|
-
"time"
|
|
20
19
|
|
|
21
20
|
"{{app_name}}/config"
|
|
22
21
|
"go.opentelemetry.io/otel"
|
|
@@ -82,36 +81,28 @@ func InitTelemetry(cfg *config.Config) (func(context.Context) error, error) {
|
|
|
82
81
|
`;
|
|
83
82
|
|
|
84
83
|
const otelCollectorK8s = `
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
tls:
|
|
108
|
-
insecure: true
|
|
109
|
-
service:
|
|
110
|
-
pipelines:
|
|
111
|
-
traces:
|
|
112
|
-
receivers: [otlp]
|
|
113
|
-
processors: [batch, resourcedetection]
|
|
114
|
-
exporters: [logging, otlp]
|
|
84
|
+
receivers:
|
|
85
|
+
otlp:
|
|
86
|
+
protocols:
|
|
87
|
+
grpc:
|
|
88
|
+
http:
|
|
89
|
+
processors:
|
|
90
|
+
batch:
|
|
91
|
+
resourcedetection:
|
|
92
|
+
detectors: [env, system]
|
|
93
|
+
exporters:
|
|
94
|
+
logging:
|
|
95
|
+
loglevel: debug
|
|
96
|
+
otlp:
|
|
97
|
+
endpoint: "jaeger:4317"
|
|
98
|
+
tls:
|
|
99
|
+
insecure: true
|
|
100
|
+
service:
|
|
101
|
+
pipelines:
|
|
102
|
+
traces:
|
|
103
|
+
receivers: [otlp]
|
|
104
|
+
processors: [batch, resourcedetection]
|
|
105
|
+
exporters: [logging, otlp]
|
|
115
106
|
`;
|
|
116
107
|
|
|
117
108
|
await fs.writeFile(path.join(telemetryDir, 'otel.go'), otelGo.replace(/{{app_name}}/g, config.name));
|
package/generators/websocket.js
CHANGED
|
@@ -18,8 +18,6 @@ import (
|
|
|
18
18
|
"fmt"
|
|
19
19
|
"log"
|
|
20
20
|
"net/http"
|
|
21
|
-
"sync"
|
|
22
|
-
"{{app_name}}/controllers"
|
|
23
21
|
|
|
24
22
|
"github.com/gin-gonic/gin"
|
|
25
23
|
"github.com/gorilla/websocket"
|
|
@@ -98,9 +96,16 @@ func (d *RESToverWSDispatcher) HandleConnection(c *gin.Context) {
|
|
|
98
96
|
span.End()
|
|
99
97
|
continue
|
|
100
98
|
}
|
|
99
|
+
siloConns, exists := c.Get("tenantSiloConns")
|
|
100
|
+
if !exists {
|
|
101
|
+
d.sendError(conn, "Unauthorized: No tenant context", wsMsg.Action, wsMsg.TraceParent)
|
|
102
|
+
span.End()
|
|
103
|
+
continue
|
|
104
|
+
}
|
|
105
|
+
conns := siloConns.(map[string]*gorm.DB)
|
|
101
106
|
|
|
102
|
-
// 4. Dispatch to MVC Controllers
|
|
103
|
-
d.dispatch(childCtx, conn, wsMsg)
|
|
107
|
+
// 4. Dispatch to MVC Controllers (Federated)
|
|
108
|
+
d.dispatch(childCtx, conn, wsMsg, conns)
|
|
104
109
|
span.End()
|
|
105
110
|
}
|
|
106
111
|
}
|
|
@@ -112,13 +117,30 @@ func (d *RESToverWSDispatcher) verifySignature(payload []byte, signature string)
|
|
|
112
117
|
return hmac.Equal([]byte(expectedSignature), []byte(signature))
|
|
113
118
|
}
|
|
114
119
|
|
|
115
|
-
func (d *RESToverWSDispatcher) dispatch(ctx context.Context, conn *websocket.Conn, msg WSMessage) {
|
|
120
|
+
func (d *RESToverWSDispatcher) dispatch(ctx context.Context, conn *websocket.Conn, msg WSMessage, conns map[string]*gorm.DB) {
|
|
116
121
|
// Inject current span into response header for client to trace back
|
|
117
122
|
carrier := propagation.HeaderCarrier{}
|
|
118
123
|
otel.GetTextMapPropagator().Inject(ctx, carrier)
|
|
119
124
|
tp := carrier.Get("traceparent")
|
|
120
125
|
|
|
121
126
|
switch msg.Action {
|
|
127
|
+
{{#each entities}}
|
|
128
|
+
case "GET_{{upper name}}S":
|
|
129
|
+
federatedData := make(map[string]interface{})
|
|
130
|
+
for role, db := range conns {
|
|
131
|
+
var list []map[string]interface{}
|
|
132
|
+
db.WithContext(ctx).Table("{{lower name}}").Find(&list)
|
|
133
|
+
federatedData[role] = list
|
|
134
|
+
}
|
|
135
|
+
d.sendResponse(conn, msg.Action, federatedData, tp)
|
|
136
|
+
case "CREATE_{{upper name}}":
|
|
137
|
+
for role, db := range conns {
|
|
138
|
+
// Proxy creation to DB
|
|
139
|
+
db.WithContext(ctx).Exec("INSERT INTO {{lower name}} (data) VALUES (?)", string(msg.Payload))
|
|
140
|
+
log.Printf("Federated JS broadcast to silo: %s", role)
|
|
141
|
+
}
|
|
142
|
+
d.sendResponse(conn, msg.Action, "Federated broadcast complete", tp)
|
|
143
|
+
{{/each}}
|
|
122
144
|
default:
|
|
123
145
|
d.sendError(conn, "Unknown action", msg.Action, tp)
|
|
124
146
|
}
|
|
@@ -135,28 +157,40 @@ func (d *RESToverWSDispatcher) sendError(conn *websocket.Conn, err string, actio
|
|
|
135
157
|
}
|
|
136
158
|
`;
|
|
137
159
|
|
|
160
|
+
// JavaScript Entity helper injection
|
|
161
|
+
let entitiesBlock = "";
|
|
162
|
+
// ... handled by template string above ...
|
|
163
|
+
|
|
138
164
|
const wsContent = wsHandler.replace(/{{app_name}}/g, config.name);
|
|
139
165
|
let finalContent = wsContent;
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
166
|
+
|
|
167
|
+
// Handled via Replace logic to inject entities into the switch
|
|
168
|
+
let entitiesIter = entities.map(e => {
|
|
169
|
+
return `
|
|
170
|
+
case "GET_${e.name.toUpperCase()}S":
|
|
171
|
+
federatedData := make(map[string]interface{})
|
|
172
|
+
for role, db := range conns {
|
|
173
|
+
var list []map[string]interface{}
|
|
174
|
+
db.WithContext(ctx).Table("${e.name.toLowerCase()}").Find(&list)
|
|
175
|
+
federatedData[role] = list
|
|
176
|
+
}
|
|
177
|
+
d.sendResponse(conn, msg.Action, federatedData, tp)
|
|
178
|
+
case "CREATE_${e.name.toUpperCase()}":
|
|
179
|
+
for role, db := range conns {
|
|
180
|
+
_ = db // Acknowledge for POC
|
|
181
|
+
// Simplified broadcast for POC
|
|
182
|
+
d.sendResponse(conn, msg.Action, "Federated broadcast for ${e.name} to " + role, tp)
|
|
183
|
+
}
|
|
184
|
+
`;
|
|
185
|
+
}).join('\n');
|
|
152
186
|
|
|
153
|
-
|
|
154
|
-
|
|
187
|
+
finalContent = finalContent.replace('{{#each entities}}', '').replace('{{/each}}', '');
|
|
188
|
+
const startTag = "switch msg.Action {";
|
|
155
189
|
const endTag = "default:";
|
|
156
190
|
const parts = finalContent.split(startTag);
|
|
157
191
|
const endParts = parts[1].split(endTag);
|
|
158
|
-
finalContent = parts[0] + startTag +
|
|
192
|
+
finalContent = parts[0] + startTag + entitiesIter + endTag + endParts[1];
|
|
159
193
|
|
|
160
194
|
await fs.writeFile(path.join(wsDir, 'handler.go'), finalContent);
|
|
161
|
-
console.log(chalk.
|
|
195
|
+
console.log(chalk.green('✅ Federated REST-over-WS Dispatcher generated!'));
|
|
162
196
|
};
|