@open-rlb/nestjs-amqp 2.1.0 → 2.2.0

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 (198) hide show
  1. package/package.json +6 -5
  2. package/schematics/add-auth-provider/index.d.ts +3 -0
  3. package/schematics/add-auth-provider/index.js +60 -0
  4. package/schematics/add-auth-provider/index.js.map +1 -0
  5. package/schematics/add-auth-provider/index.spec.ts +83 -0
  6. package/schematics/add-auth-provider/index.ts +83 -0
  7. package/schematics/add-auth-provider/schema.d.ts +18 -0
  8. package/schematics/add-auth-provider/schema.js +3 -0
  9. package/schematics/add-auth-provider/schema.js.map +1 -0
  10. package/schematics/add-auth-provider/schema.json +59 -0
  11. package/schematics/add-auth-provider/schema.ts +35 -0
  12. package/schematics/add-exchange/index.d.ts +3 -0
  13. package/schematics/add-exchange/index.js +49 -0
  14. package/schematics/add-exchange/index.js.map +1 -0
  15. package/schematics/add-exchange/index.spec.ts +65 -0
  16. package/schematics/add-exchange/index.ts +65 -0
  17. package/schematics/add-exchange/schema.d.ts +14 -0
  18. package/schematics/add-exchange/schema.js +3 -0
  19. package/schematics/add-exchange/schema.js.map +1 -0
  20. package/schematics/add-exchange/schema.json +50 -0
  21. package/schematics/add-exchange/schema.ts +26 -0
  22. package/schematics/add-exchange-binding/index.d.ts +3 -0
  23. package/schematics/add-exchange-binding/index.js +53 -0
  24. package/schematics/add-exchange-binding/index.js.map +1 -0
  25. package/schematics/add-exchange-binding/index.spec.ts +73 -0
  26. package/schematics/add-exchange-binding/index.ts +64 -0
  27. package/schematics/add-exchange-binding/schema.d.ts +8 -0
  28. package/schematics/add-exchange-binding/schema.js +3 -0
  29. package/schematics/add-exchange-binding/schema.js.map +1 -0
  30. package/schematics/add-exchange-binding/schema.json +36 -0
  31. package/schematics/add-exchange-binding/schema.ts +14 -0
  32. package/schematics/add-gateway/index.d.ts +4 -0
  33. package/schematics/add-gateway/index.js +105 -0
  34. package/schematics/add-gateway/index.js.map +1 -0
  35. package/schematics/add-gateway/index.spec.ts +163 -0
  36. package/schematics/add-gateway/index.ts +141 -0
  37. package/schematics/add-gateway/paths.d.ts +12 -0
  38. package/schematics/add-gateway/paths.js +87 -0
  39. package/schematics/add-gateway/paths.js.map +1 -0
  40. package/schematics/add-gateway/paths.ts +103 -0
  41. package/schematics/add-gateway/schema.d.ts +13 -0
  42. package/schematics/add-gateway/schema.js +3 -0
  43. package/schematics/add-gateway/schema.js.map +1 -0
  44. package/schematics/add-gateway/schema.json +63 -0
  45. package/schematics/add-gateway/schema.ts +24 -0
  46. package/schematics/add-gateway/wiring.d.ts +14 -0
  47. package/schematics/add-gateway/wiring.js +102 -0
  48. package/schematics/add-gateway/wiring.js.map +1 -0
  49. package/schematics/add-gateway/wiring.ts +115 -0
  50. package/schematics/add-metrics-topic/index.d.ts +3 -0
  51. package/schematics/add-metrics-topic/index.js +31 -0
  52. package/schematics/add-metrics-topic/index.js.map +1 -0
  53. package/schematics/add-metrics-topic/index.spec.ts +79 -0
  54. package/schematics/add-metrics-topic/index.ts +49 -0
  55. package/schematics/add-metrics-topic/schema.d.ts +10 -0
  56. package/schematics/add-metrics-topic/schema.js +3 -0
  57. package/schematics/add-metrics-topic/schema.js.map +1 -0
  58. package/schematics/add-metrics-topic/schema.json +48 -0
  59. package/schematics/add-metrics-topic/schema.ts +18 -0
  60. package/schematics/add-queue/index.d.ts +3 -0
  61. package/schematics/add-queue/index.js +48 -0
  62. package/schematics/add-queue/index.js.map +1 -0
  63. package/schematics/add-queue/index.spec.ts +75 -0
  64. package/schematics/add-queue/index.ts +74 -0
  65. package/schematics/add-queue/schema.d.ts +17 -0
  66. package/schematics/add-queue/schema.js +3 -0
  67. package/schematics/add-queue/schema.js.map +1 -0
  68. package/schematics/add-queue/schema.json +54 -0
  69. package/schematics/add-queue/schema.ts +32 -0
  70. package/schematics/add-reply-queue/index.d.ts +3 -0
  71. package/schematics/add-reply-queue/index.js +26 -0
  72. package/schematics/add-reply-queue/index.js.map +1 -0
  73. package/schematics/add-reply-queue/index.spec.ts +69 -0
  74. package/schematics/add-reply-queue/index.ts +33 -0
  75. package/schematics/add-reply-queue/schema.d.ts +6 -0
  76. package/schematics/add-reply-queue/schema.js +3 -0
  77. package/schematics/add-reply-queue/schema.js.map +1 -0
  78. package/schematics/add-reply-queue/schema.json +28 -0
  79. package/schematics/add-reply-queue/schema.ts +10 -0
  80. package/schematics/add-route/index.d.ts +3 -0
  81. package/schematics/add-route/index.js +57 -0
  82. package/schematics/add-route/index.js.map +1 -0
  83. package/schematics/add-route/index.spec.ts +75 -0
  84. package/schematics/add-route/index.ts +74 -0
  85. package/schematics/add-route/schema.d.ts +22 -0
  86. package/schematics/add-route/schema.js +3 -0
  87. package/schematics/add-route/schema.js.map +1 -0
  88. package/schematics/add-route/schema.json +74 -0
  89. package/schematics/add-route/schema.ts +43 -0
  90. package/schematics/add-topic/index.d.ts +3 -0
  91. package/schematics/add-topic/index.js +69 -0
  92. package/schematics/add-topic/index.js.map +1 -0
  93. package/schematics/add-topic/index.spec.ts +100 -0
  94. package/schematics/add-topic/index.ts +104 -0
  95. package/schematics/add-topic/schema.d.ts +18 -0
  96. package/schematics/add-topic/schema.js +3 -0
  97. package/schematics/add-topic/schema.js.map +1 -0
  98. package/schematics/add-topic/schema.json +63 -0
  99. package/schematics/add-topic/schema.ts +34 -0
  100. package/schematics/add-ws-event/index.d.ts +3 -0
  101. package/schematics/add-ws-event/index.js +53 -0
  102. package/schematics/add-ws-event/index.js.map +1 -0
  103. package/schematics/add-ws-event/index.spec.ts +85 -0
  104. package/schematics/add-ws-event/index.ts +72 -0
  105. package/schematics/add-ws-event/schema.d.ts +17 -0
  106. package/schematics/add-ws-event/schema.js +3 -0
  107. package/schematics/add-ws-event/schema.js.map +1 -0
  108. package/schematics/add-ws-event/schema.json +53 -0
  109. package/schematics/add-ws-event/schema.ts +33 -0
  110. package/schematics/collection.json +81 -1
  111. package/schematics/configure-broker/index.d.ts +3 -0
  112. package/schematics/configure-broker/index.js +41 -0
  113. package/schematics/configure-broker/index.js.map +1 -0
  114. package/schematics/configure-broker/index.spec.ts +62 -0
  115. package/schematics/configure-broker/index.ts +51 -0
  116. package/schematics/configure-broker/schema.d.ts +11 -0
  117. package/schematics/configure-broker/schema.js +3 -0
  118. package/schematics/configure-broker/schema.js.map +1 -0
  119. package/schematics/configure-broker/schema.json +47 -0
  120. package/schematics/configure-broker/schema.ts +20 -0
  121. package/schematics/enable-load-config/index.d.ts +3 -0
  122. package/schematics/enable-load-config/index.js +32 -0
  123. package/schematics/enable-load-config/index.js.map +1 -0
  124. package/schematics/enable-load-config/index.spec.ts +61 -0
  125. package/schematics/enable-load-config/index.ts +39 -0
  126. package/schematics/enable-load-config/schema.d.ts +8 -0
  127. package/schematics/enable-load-config/schema.js +3 -0
  128. package/schematics/enable-load-config/schema.js.map +1 -0
  129. package/schematics/enable-load-config/schema.json +36 -0
  130. package/schematics/enable-load-config/schema.ts +14 -0
  131. package/schematics/enable-retry/index.d.ts +3 -0
  132. package/schematics/enable-retry/index.js +51 -0
  133. package/schematics/enable-retry/index.js.map +1 -0
  134. package/schematics/enable-retry/index.spec.ts +73 -0
  135. package/schematics/enable-retry/index.ts +62 -0
  136. package/schematics/enable-retry/schema.d.ts +13 -0
  137. package/schematics/enable-retry/schema.js +3 -0
  138. package/schematics/enable-retry/schema.js.map +1 -0
  139. package/schematics/enable-retry/schema.json +64 -0
  140. package/schematics/enable-retry/schema.ts +24 -0
  141. package/schematics/enable-route-discovery/index.d.ts +3 -0
  142. package/schematics/enable-route-discovery/index.js +36 -0
  143. package/schematics/enable-route-discovery/index.js.map +1 -0
  144. package/schematics/enable-route-discovery/index.spec.ts +55 -0
  145. package/schematics/enable-route-discovery/index.ts +48 -0
  146. package/schematics/enable-route-discovery/schema.d.ts +9 -0
  147. package/schematics/enable-route-discovery/schema.js +3 -0
  148. package/schematics/enable-route-discovery/schema.js.map +1 -0
  149. package/schematics/enable-route-discovery/schema.json +44 -0
  150. package/schematics/enable-route-discovery/schema.ts +16 -0
  151. package/schematics/harden-gateway/index.d.ts +3 -0
  152. package/schematics/harden-gateway/index.js +93 -0
  153. package/schematics/harden-gateway/index.js.map +1 -0
  154. package/schematics/harden-gateway/index.spec.ts +105 -0
  155. package/schematics/harden-gateway/index.ts +108 -0
  156. package/schematics/harden-gateway/schema.d.ts +11 -0
  157. package/schematics/harden-gateway/schema.js +3 -0
  158. package/schematics/harden-gateway/schema.js.map +1 -0
  159. package/schematics/harden-gateway/schema.json +47 -0
  160. package/schematics/harden-gateway/schema.ts +20 -0
  161. package/schematics/nest-add/index.js +55 -832
  162. package/schematics/nest-add/index.js.map +1 -1
  163. package/schematics/nest-add/index.spec.ts +73 -0
  164. package/schematics/nest-add/index.ts +76 -917
  165. package/schematics/nest-add/init.schema.d.ts +1 -0
  166. package/schematics/nest-add/init.schema.ts +3 -0
  167. package/schematics/nest-add/schema.json +4 -43
  168. package/schematics/set-connection-name/index.d.ts +3 -0
  169. package/schematics/set-connection-name/index.js +34 -0
  170. package/schematics/set-connection-name/index.js.map +1 -0
  171. package/schematics/set-connection-name/index.spec.ts +48 -0
  172. package/schematics/set-connection-name/index.ts +42 -0
  173. package/schematics/set-connection-name/schema.d.ts +5 -0
  174. package/schematics/set-connection-name/schema.js +3 -0
  175. package/schematics/set-connection-name/schema.js.map +1 -0
  176. package/schematics/set-connection-name/schema.json +24 -0
  177. package/schematics/set-connection-name/schema.ts +8 -0
  178. package/schematics/utils/broker-yaml.util.d.ts +53 -0
  179. package/schematics/utils/broker-yaml.util.js +91 -0
  180. package/schematics/utils/broker-yaml.util.js.map +1 -0
  181. package/schematics/utils/broker-yaml.util.ts +172 -0
  182. package/schematics/utils/nest-wiring.util.d.ts +30 -0
  183. package/schematics/utils/nest-wiring.util.js +170 -0
  184. package/schematics/utils/nest-wiring.util.js.map +1 -0
  185. package/schematics/utils/nest-wiring.util.ts +211 -0
  186. package/schematics/utils/schematic-prompt.util.d.ts +7 -0
  187. package/schematics/utils/schematic-prompt.util.js +57 -0
  188. package/schematics/utils/schematic-prompt.util.js.map +1 -0
  189. package/schematics/utils/schematic-prompt.util.ts +82 -0
  190. package/schematics/utils/yaml-config.util.d.ts +22 -0
  191. package/schematics/utils/yaml-config.util.js +119 -0
  192. package/schematics/utils/yaml-config.util.js.map +1 -0
  193. package/schematics/utils/yaml-config.util.ts +167 -0
  194. /package/schematics/{nest-add → add-gateway}/files/acl/src/cache/in-memory-acl-store.ts +0 -0
  195. /package/schematics/{nest-add → add-gateway}/files/acl/src/modules/database/repository/acl.repository.ts +0 -0
  196. /package/schematics/{nest-add → add-gateway}/files/db-core/src/modules/database/repository/in-memory-collection.ts +0 -0
  197. /package/schematics/{nest-add → add-gateway}/files/gateway-admin/src/modules/database/repository/gateway.repository.ts +0 -0
  198. /package/schematics/{nest-add → add-gateway}/files/gateway-admin/src/modules/database/repository/route-sync.repository.ts +0 -0
@@ -0,0 +1,72 @@
1
+ import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
2
+ import { askSelect, askText, loadPrompts, logOutcome } from '../utils/schematic-prompt.util';
3
+ import { findConfigYaml, readConfigDoc, upsertSeqItemByKey, writeConfigDoc } from '../utils/yaml-config.util';
4
+ import { AddWsEventOptions, WsEventType } from './schema';
5
+
6
+ const TYPES: readonly WsEventType[] = ['ws', 'http'];
7
+
8
+ /**
9
+ * `add-ws-event <name>` — idempotent upsert of a `gateway.events[]` entry, comment-preserving.
10
+ * A `ws` event pushes broker messages to subscribed clients (exchange/routingKey + auth/scoping);
11
+ * an `http` event fans a broker message out to a webhook (url/method/timeout). Only the fields that
12
+ * apply to the chosen `type` are written, and `httpMethod` maps onto WebSocketEvent's `method`.
13
+ */
14
+ export function main(options: AddWsEventOptions): Rule {
15
+ return async (tree: Tree, context: SchematicContext) => {
16
+ const flagsProvided = options.name !== undefined;
17
+ const prompts = loadPrompts(context, flagsProvided);
18
+
19
+ const name = options.name || (await askText(prompts, 'Event name?', ''));
20
+ if (!name) {
21
+ context.logger.error('[rlb-amqp] add-ws-event: a name is required.');
22
+ return tree;
23
+ }
24
+
25
+ const type = (options.type as WsEventType) || (await askSelect(prompts, 'Event type?', TYPES, 'ws'));
26
+
27
+ const configPath = findConfigYaml(tree, options.config);
28
+ const { doc, existed } = readConfigDoc(tree, configPath);
29
+ if (!existed) context.logger.info(`[rlb-amqp] ${configPath} not found — creating it.`);
30
+
31
+ // http events talk to a webhook (url/method/timeout); ws events push to subscribers with the
32
+ // full auth/scoping surface. Building type-specific items keeps stray fields out of the config.
33
+ const item: Record<string, unknown> =
34
+ type === 'http'
35
+ ? {
36
+ name,
37
+ type,
38
+ url: options.url,
39
+ method: options.httpMethod,
40
+ timeout: options.timeout,
41
+ }
42
+ : {
43
+ name,
44
+ type,
45
+ exchange: options.exchange,
46
+ routingKey: options.routingKey,
47
+ auth: options.auth,
48
+ requireAuth: options.requireAuth,
49
+ actions: options.actions,
50
+ scopeClaim: options.scopeClaim,
51
+ payloadKey: options.payloadKey,
52
+ };
53
+
54
+ // Per-user scoping needs BOTH halves: scopeClaim (the client's value) and payloadKey (the message
55
+ // field to compare it to). With only scopeClaim, the compare has nothing to match → nothing ships.
56
+ if (options.scopeClaim && !options.payloadKey) {
57
+ context.logger.warn(
58
+ '[rlb-amqp] add-ws-event: scopeClaim without payloadKey denies every message (per-user isolation needs both).',
59
+ );
60
+ }
61
+ const hasActions = options.actions !== undefined && (!Array.isArray(options.actions) || options.actions.length > 0);
62
+ if (hasActions && !options.auth) {
63
+ context.logger.warn('[rlb-amqp] add-ws-event: actions require auth.');
64
+ }
65
+
66
+ const outcome = upsertSeqItemByKey(doc, ['gateway', 'events'], 'name', item, { overwrite: options.overwrite });
67
+ logOutcome(context, `ws-event '${name}'`, outcome);
68
+
69
+ writeConfigDoc(tree, configPath, doc);
70
+ return tree;
71
+ };
72
+ }
@@ -0,0 +1,17 @@
1
+ export type WsEventType = 'ws' | 'http';
2
+ export interface AddWsEventOptions {
3
+ name?: string;
4
+ type?: WsEventType;
5
+ exchange?: string;
6
+ routingKey?: string;
7
+ auth?: string;
8
+ requireAuth?: boolean;
9
+ actions?: string | string[];
10
+ scopeClaim?: string;
11
+ payloadKey?: string;
12
+ url?: string;
13
+ httpMethod?: string;
14
+ timeout?: number;
15
+ overwrite?: boolean;
16
+ config?: string;
17
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../libs/rlb-nestjs-amqp/src/schematics/add-ws-event/schema.ts"],"names":[],"mappings":""}
@@ -0,0 +1,53 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "$id": "SchematicsRlbAmqpAddWsEvent",
4
+ "title": "Add a WebSocket/webhook event (gateway.events[]) to config.yaml",
5
+ "type": "object",
6
+ "properties": {
7
+ "name": {
8
+ "type": "string",
9
+ "description": "Event name (the key).",
10
+ "x-prompt": "Event name?",
11
+ "$default": { "$source": "argv", "index": 0 }
12
+ },
13
+ "type": {
14
+ "type": "string",
15
+ "enum": ["ws", "http"],
16
+ "default": "ws",
17
+ "description": "ws (push to subscribed clients) | http (webhook POST)."
18
+ },
19
+ "exchange": { "type": "string", "description": "Source exchange the gateway binds to (ws)." },
20
+ "routingKey": { "type": "string", "description": "routingKey bound on that exchange (ws)." },
21
+ "auth": { "type": "string", "description": "Auth-provider name used to verify the subscribe token." },
22
+ "requireAuth": {
23
+ "type": "boolean",
24
+ "description": "Set false to make auth optional (anonymous may subscribe)."
25
+ },
26
+ "actions": {
27
+ "type": "array",
28
+ "items": { "type": "string" },
29
+ "description": "Actions the subscriber must hold (OR-semantics). Requires auth."
30
+ },
31
+ "scopeClaim": {
32
+ "type": "string",
33
+ "description": "Claim used to scope delivery per-user; requires payloadKey."
34
+ },
35
+ "payloadKey": {
36
+ "type": "string",
37
+ "description": "Payload field compared against scopeClaim — only matching messages are forwarded."
38
+ },
39
+ "url": { "type": "string", "description": "Webhook URL to POST to (http)." },
40
+ "httpMethod": { "type": "string", "description": "HTTP method for the webhook (http) — written as method." },
41
+ "timeout": { "type": "number", "description": "Webhook request timeout in ms (http)." },
42
+ "overwrite": {
43
+ "type": "boolean",
44
+ "default": false,
45
+ "description": "Update the entry if it already exists (default: leave it untouched)."
46
+ },
47
+ "config": {
48
+ "type": "string",
49
+ "description": "Path to config.yaml (default: auto-detected)."
50
+ }
51
+ },
52
+ "required": []
53
+ }
@@ -0,0 +1,33 @@
1
+ /** Delivery transport for the event. Mirrors WebSocketEvent['type'] (ws push or http webhook). */
2
+ export type WsEventType = 'ws' | 'http';
3
+
4
+ export interface AddWsEventOptions {
5
+ /** Event name (the key). Prompted when omitted. */
6
+ name?: string;
7
+ /** ws (push to subscribed clients) | http (webhook POST). Default: ws. */
8
+ type?: WsEventType;
9
+ /** Source exchange the gateway binds to (ws). */
10
+ exchange?: string;
11
+ /** routingKey bound on that exchange (ws). */
12
+ routingKey?: string;
13
+ /** Auth-provider name used to verify the subscribe token AND map its claims. */
14
+ auth?: string;
15
+ /** Set false to make `auth` optional (anonymous may subscribe; authed still get claims mapped). */
16
+ requireAuth?: boolean;
17
+ /** Actions the subscriber must hold (OR-semantics), checked resource-agnostically. Requires `auth`. */
18
+ actions?: string | string[];
19
+ /** Claim used to scope delivery per-user; requires `payloadKey` to compare against. */
20
+ scopeClaim?: string;
21
+ /** Payload field compared against `scopeClaim` — only matching messages are forwarded. */
22
+ payloadKey?: string;
23
+ /** Webhook URL to POST to (http). */
24
+ url?: string;
25
+ /** HTTP method for the webhook (http) — written as `method`. */
26
+ httpMethod?: string;
27
+ /** Webhook request timeout in ms (http). */
28
+ timeout?: number;
29
+ /** Update the entry when it already exists (default: leave it untouched). */
30
+ overwrite?: boolean;
31
+ /** Path to config.yaml (default: auto-detected, typically config/config.yaml). */
32
+ config?: string;
33
+ }
@@ -5,6 +5,86 @@
5
5
  "description": "Add RabbitMQ support to a NestJS project",
6
6
  "factory": "./nest-add/index#main",
7
7
  "schema": "./nest-add/schema.json"
8
+ },
9
+ "add-exchange": {
10
+ "description": "Add a broker exchange to config.yaml (optionally with a bound queue)",
11
+ "factory": "./add-exchange/index#main",
12
+ "schema": "./add-exchange/schema.json"
13
+ },
14
+ "add-queue": {
15
+ "description": "Add a broker queue to config.yaml (creates the exchange if missing; growth bounds)",
16
+ "factory": "./add-queue/index#main",
17
+ "schema": "./add-queue/schema.json"
18
+ },
19
+ "add-topic": {
20
+ "description": "Add a broker topic to config.yaml (creates queue/exchange if missing)",
21
+ "factory": "./add-topic/index#main",
22
+ "schema": "./add-topic/schema.json"
23
+ },
24
+ "add-exchange-binding": {
25
+ "description": "Bind two exchanges via broker.exchangeBindings[]",
26
+ "factory": "./add-exchange-binding/index#main",
27
+ "schema": "./add-exchange-binding/schema.json"
28
+ },
29
+ "add-reply-queue": {
30
+ "description": "Map an exchange to an RPC reply queue via broker.replyQueues",
31
+ "factory": "./add-reply-queue/index#main",
32
+ "schema": "./add-reply-queue/schema.json"
33
+ },
34
+ "enable-retry": {
35
+ "description": "Configure a bounded retry policy (broker.retry or topics[].retry) + declare the dead-letter exchange",
36
+ "factory": "./enable-retry/index#main",
37
+ "schema": "./enable-retry/schema.json"
38
+ },
39
+ "enable-route-discovery": {
40
+ "description": "Enable publisher-side route auto-discovery (broker.routeDiscovery + fanout exchange)",
41
+ "factory": "./enable-route-discovery/index#main",
42
+ "schema": "./enable-route-discovery/schema.json"
43
+ },
44
+ "set-connection-name": {
45
+ "description": "Set the logical AMQP connection_name (the lib auto-suffixes -hostname-pid per instance)",
46
+ "factory": "./set-connection-name/index#main",
47
+ "schema": "./set-connection-name/schema.json"
48
+ },
49
+ "configure-broker": {
50
+ "description": "Set broker connection scalars (uri, prefetchCount, defaultRpcTimeout, init/manager options, credentials)",
51
+ "factory": "./configure-broker/index#main",
52
+ "schema": "./configure-broker/schema.json"
53
+ },
54
+ "add-auth-provider": {
55
+ "description": "Add an auth-providers[] entry (jwt | jwks | basic | str-compare | none)",
56
+ "factory": "./add-auth-provider/index#main",
57
+ "schema": "./add-auth-provider/schema.json"
58
+ },
59
+ "add-gateway": {
60
+ "description": "Promote a config to a gateway: remove publisher route-discovery, add rlb-acl/admin/control infra + gateway section",
61
+ "factory": "./add-gateway/index#main",
62
+ "schema": "./add-gateway/schema.json"
63
+ },
64
+ "add-route": {
65
+ "description": "Add an HTTP route (gateway.paths[]) forwarding to a topic/action",
66
+ "factory": "./add-route/index#main",
67
+ "schema": "./add-route/schema.json"
68
+ },
69
+ "add-ws-event": {
70
+ "description": "Add a WebSocket/webhook event (gateway.events[])",
71
+ "factory": "./add-ws-event/index#main",
72
+ "schema": "./add-ws-event/schema.json"
73
+ },
74
+ "add-metrics-topic": {
75
+ "description": "Isolate gw-metrics-track on a dedicated bounded topic/queue and point gateway.metrics at it",
76
+ "factory": "./add-metrics-topic/index#main",
77
+ "schema": "./add-metrics-topic/schema.json"
78
+ },
79
+ "enable-load-config": {
80
+ "description": "Add gateway.loadConfig (pull DB-managed routes/events, merged with YAML, on (re)load)",
81
+ "factory": "./enable-load-config/index#main",
82
+ "schema": "./enable-load-config/schema.json"
83
+ },
84
+ "harden-gateway": {
85
+ "description": "Add gateway hardening limits (maxConcurrentRequests, maxBodyBytes + main.ts body parser, upload, ws.maxBufferedBytes)",
86
+ "factory": "./harden-gateway/index#main",
87
+ "schema": "./harden-gateway/schema.json"
8
88
  }
9
89
  }
10
- }
90
+ }
@@ -0,0 +1,3 @@
1
+ import { Rule } from '@angular-devkit/schematics';
2
+ import { ConfigureBrokerOptions } from './schema';
3
+ export declare function main(options: ConfigureBrokerOptions): Rule;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.main = main;
4
+ const schematic_prompt_util_1 = require("../utils/schematic-prompt.util");
5
+ const yaml_config_util_1 = require("../utils/yaml-config.util");
6
+ const MANAGER = ['broker', 'connectionManagerOptions'];
7
+ const CREDS = [...MANAGER, 'connectionOptions', 'credentials'];
8
+ function main(options) {
9
+ return async (tree, context) => {
10
+ const flagsProvided = options.uri !== undefined;
11
+ (0, schematic_prompt_util_1.loadPrompts)(context, flagsProvided);
12
+ const configPath = (0, yaml_config_util_1.findConfigYaml)(tree, options.config);
13
+ const { doc, existed } = (0, yaml_config_util_1.readConfigDoc)(tree, configPath);
14
+ if (!existed)
15
+ context.logger.info(`[rlb-amqp] ${configPath} not found — creating it.`);
16
+ const edits = [
17
+ [options.uri, ['broker', 'uri'], 'broker.uri'],
18
+ [options.prefetchCount, ['broker', 'prefetchCount'], 'broker.prefetchCount'],
19
+ [options.defaultRpcTimeout, ['broker', 'defaultRpcTimeout'], 'broker.defaultRpcTimeout'],
20
+ [options.heartbeatIntervalInSeconds, [...MANAGER, 'heartbeatIntervalInSeconds'], 'heartbeatIntervalInSeconds'],
21
+ [options.reconnectTimeInSeconds, [...MANAGER, 'reconnectTimeInSeconds'], 'reconnectTimeInSeconds'],
22
+ [options.mechanism, [...CREDS, 'mechanism'], 'credentials.mechanism'],
23
+ [options.username, [...CREDS, 'username'], 'credentials.username'],
24
+ [options.password, [...CREDS, 'password'], 'credentials.password'],
25
+ ];
26
+ let applied = 0;
27
+ for (const [value, path, label] of edits) {
28
+ if (value === undefined)
29
+ continue;
30
+ (0, schematic_prompt_util_1.logOutcome)(context, label, (0, yaml_config_util_1.setIn)(doc, path, value));
31
+ applied++;
32
+ }
33
+ if (applied === 0) {
34
+ context.logger.warn('[rlb-amqp] configure-broker: no options provided — nothing to set.');
35
+ return tree;
36
+ }
37
+ (0, yaml_config_util_1.writeConfigDoc)(tree, configPath, doc);
38
+ return tree;
39
+ };
40
+ }
41
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../libs/rlb-nestjs-amqp/src/schematics/configure-broker/index.ts"],"names":[],"mappings":";;AAaA,oBAqCC;AAjDD,0EAAyE;AACzE,gEAAiG;AAGjG,MAAM,OAAO,GAAG,CAAC,QAAQ,EAAE,0BAA0B,CAAC,CAAC;AACvD,MAAM,KAAK,GAAG,CAAC,GAAG,OAAO,EAAE,mBAAmB,EAAE,aAAa,CAAC,CAAC;AAO/D,SAAgB,IAAI,CAAC,OAA+B;IAClD,OAAO,KAAK,EAAE,IAAU,EAAE,OAAyB,EAAE,EAAE;QAErD,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC;QAChD,IAAA,mCAAW,EAAC,OAAO,EAAE,aAAa,CAAC,CAAC;QAEpC,MAAM,UAAU,GAAG,IAAA,iCAAc,EAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QACxD,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,IAAA,gCAAa,EAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QACzD,IAAI,CAAC,OAAO;YAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,UAAU,2BAA2B,CAAC,CAAC;QAGvF,MAAM,KAAK,GAAkD;YAC3D,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,YAAY,CAAC;YAC9C,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,QAAQ,EAAE,eAAe,CAAC,EAAE,sBAAsB,CAAC;YAC5E,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,QAAQ,EAAE,mBAAmB,CAAC,EAAE,0BAA0B,CAAC;YACxF,CAAC,OAAO,CAAC,0BAA0B,EAAE,CAAC,GAAG,OAAO,EAAE,4BAA4B,CAAC,EAAE,4BAA4B,CAAC;YAC9G,CAAC,OAAO,CAAC,sBAAsB,EAAE,CAAC,GAAG,OAAO,EAAE,wBAAwB,CAAC,EAAE,wBAAwB,CAAC;YAClG,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,GAAG,KAAK,EAAE,WAAW,CAAC,EAAE,uBAAuB,CAAC;YACrE,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,KAAK,EAAE,UAAU,CAAC,EAAE,sBAAsB,CAAC;YAClE,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,KAAK,EAAE,UAAU,CAAC,EAAE,sBAAsB,CAAC;SACnE,CAAC;QAEF,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC;YACzC,IAAI,KAAK,KAAK,SAAS;gBAAE,SAAS;YAClC,IAAA,kCAAU,EAAC,OAAO,EAAE,KAAK,EAAE,IAAA,wBAAK,EAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;YACpD,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;YAClB,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,oEAAoE,CAAC,CAAC;YAC1F,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAA,iCAAc,EAAC,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;QACtC,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,62 @@
1
+ import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
2
+ import { parseDocument } from 'yaml';
3
+ import { main } from './index';
4
+ import { ConfigureBrokerOptions } from './schema';
5
+
6
+ /** Invoke the factory Rule directly (SchematicTestRunner eagerly loads ESM `ora`). */
7
+ const ctx = { logger: { info() {}, warn() {}, error() {} } } as unknown as SchematicContext;
8
+
9
+ async function run(options: ConfigureBrokerOptions, tree: Tree): Promise<Tree> {
10
+ const rule = main(options) as Rule;
11
+ const result = await (rule as (t: Tree, c: SchematicContext) => Promise<Tree>)(tree, ctx);
12
+ return result || tree;
13
+ }
14
+
15
+ function read(tree: Tree, path = 'config/config.yaml'): string {
16
+ return tree.read(path)!.toString('utf-8');
17
+ }
18
+ function doc(tree: Tree) {
19
+ return parseDocument(read(tree));
20
+ }
21
+ function seedConfig(): Tree {
22
+ const tree = Tree.empty();
23
+ tree.create(
24
+ 'config/config.yaml',
25
+ ['# my config', 'broker:', ' uri: amqp://old', ' prefetchCount: 10', 'topics: []', ''].join('\n'),
26
+ );
27
+ return tree;
28
+ }
29
+
30
+ describe('configure-broker schematic', () => {
31
+ it('sets only the provided scalars at their nested paths', async () => {
32
+ const tree = await run(
33
+ { uri: 'amqp://new', defaultRpcTimeout: 5000, heartbeatIntervalInSeconds: 15, mechanism: 'PLAIN', username: 'u', password: 'p' },
34
+ seedConfig(),
35
+ );
36
+ const d = doc(tree);
37
+ expect(String(d.getIn(['broker', 'uri']))).toBe('amqp://new');
38
+ expect(Number(d.getIn(['broker', 'defaultRpcTimeout']))).toBe(5000);
39
+ expect(Number(d.getIn(['broker', 'connectionManagerOptions', 'heartbeatIntervalInSeconds']))).toBe(15);
40
+ const creds = ['broker', 'connectionManagerOptions', 'connectionOptions', 'credentials'];
41
+ expect(String(d.getIn([...creds, 'mechanism']))).toBe('PLAIN');
42
+ expect(String(d.getIn([...creds, 'username']))).toBe('u');
43
+ expect(String(d.getIn([...creds, 'password']))).toBe('p');
44
+ // Untouched pre-existing value survives.
45
+ expect(Number(d.getIn(['broker', 'prefetchCount']))).toBe(10);
46
+ });
47
+
48
+ it('leaves unprovided keys untouched (no clobbering)', async () => {
49
+ const tree = await run({ defaultRpcTimeout: 3000 }, seedConfig());
50
+ const d = doc(tree);
51
+ expect(String(d.getIn(['broker', 'uri']))).toBe('amqp://old');
52
+ expect(Number(d.getIn(['broker', 'prefetchCount']))).toBe(10);
53
+ expect(Number(d.getIn(['broker', 'defaultRpcTimeout']))).toBe(3000);
54
+ });
55
+
56
+ it('is idempotent and makes no change when no options are provided', async () => {
57
+ const seed = seedConfig();
58
+ const before = read(seed);
59
+ const tree = await run({}, seed);
60
+ expect(read(tree)).toBe(before);
61
+ });
62
+ });
@@ -0,0 +1,51 @@
1
+ import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
2
+ import { loadPrompts, logOutcome } from '../utils/schematic-prompt.util';
3
+ import { findConfigYaml, readConfigDoc, setIn, writeConfigDoc } from '../utils/yaml-config.util';
4
+ import { ConfigureBrokerOptions } from './schema';
5
+
6
+ const MANAGER = ['broker', 'connectionManagerOptions'];
7
+ const CREDS = [...MANAGER, 'connectionOptions', 'credentials'];
8
+
9
+ /**
10
+ * `configure-broker` — set broker connection scalars, ONLY for values that were actually provided
11
+ * (so re-running with a single flag never clobbers the rest of the config). Mostly non-interactive:
12
+ * it just applies whatever flags were passed.
13
+ */
14
+ export function main(options: ConfigureBrokerOptions): Rule {
15
+ return async (tree: Tree, context: SchematicContext) => {
16
+ // `uri` is the closest thing to a primary field; otherwise this schematic is flag-driven.
17
+ const flagsProvided = options.uri !== undefined;
18
+ loadPrompts(context, flagsProvided);
19
+
20
+ const configPath = findConfigYaml(tree, options.config);
21
+ const { doc, existed } = readConfigDoc(tree, configPath);
22
+ if (!existed) context.logger.info(`[rlb-amqp] ${configPath} not found — creating it.`);
23
+
24
+ // (option value, target path, label) — only entries whose value is defined get applied.
25
+ const edits: Array<[unknown, (string | number)[], string]> = [
26
+ [options.uri, ['broker', 'uri'], 'broker.uri'],
27
+ [options.prefetchCount, ['broker', 'prefetchCount'], 'broker.prefetchCount'],
28
+ [options.defaultRpcTimeout, ['broker', 'defaultRpcTimeout'], 'broker.defaultRpcTimeout'],
29
+ [options.heartbeatIntervalInSeconds, [...MANAGER, 'heartbeatIntervalInSeconds'], 'heartbeatIntervalInSeconds'],
30
+ [options.reconnectTimeInSeconds, [...MANAGER, 'reconnectTimeInSeconds'], 'reconnectTimeInSeconds'],
31
+ [options.mechanism, [...CREDS, 'mechanism'], 'credentials.mechanism'],
32
+ [options.username, [...CREDS, 'username'], 'credentials.username'],
33
+ [options.password, [...CREDS, 'password'], 'credentials.password'],
34
+ ];
35
+
36
+ let applied = 0;
37
+ for (const [value, path, label] of edits) {
38
+ if (value === undefined) continue;
39
+ logOutcome(context, label, setIn(doc, path, value));
40
+ applied++;
41
+ }
42
+
43
+ if (applied === 0) {
44
+ context.logger.warn('[rlb-amqp] configure-broker: no options provided — nothing to set.');
45
+ return tree;
46
+ }
47
+
48
+ writeConfigDoc(tree, configPath, doc);
49
+ return tree;
50
+ };
51
+ }
@@ -0,0 +1,11 @@
1
+ export interface ConfigureBrokerOptions {
2
+ uri?: string;
3
+ prefetchCount?: number;
4
+ defaultRpcTimeout?: number;
5
+ heartbeatIntervalInSeconds?: number;
6
+ reconnectTimeInSeconds?: number;
7
+ mechanism?: 'PLAIN' | 'EXTERNAL' | 'AMQPLAIN';
8
+ username?: string;
9
+ password?: string;
10
+ config?: string;
11
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../libs/rlb-nestjs-amqp/src/schematics/configure-broker/schema.ts"],"names":[],"mappings":""}
@@ -0,0 +1,47 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "$id": "SchematicsRlbAmqpConfigureBroker",
4
+ "title": "Set broker connection scalars in config.yaml",
5
+ "type": "object",
6
+ "properties": {
7
+ "uri": {
8
+ "type": "string",
9
+ "description": "broker.uri (the AMQP connection string).",
10
+ "$default": { "$source": "argv", "index": 0 }
11
+ },
12
+ "prefetchCount": {
13
+ "type": "number",
14
+ "description": "broker.prefetchCount (channel prefetch)."
15
+ },
16
+ "defaultRpcTimeout": {
17
+ "type": "number",
18
+ "description": "broker.defaultRpcTimeout (ms)."
19
+ },
20
+ "heartbeatIntervalInSeconds": {
21
+ "type": "number",
22
+ "description": "connectionManagerOptions.heartbeatIntervalInSeconds."
23
+ },
24
+ "reconnectTimeInSeconds": {
25
+ "type": "number",
26
+ "description": "connectionManagerOptions.reconnectTimeInSeconds."
27
+ },
28
+ "mechanism": {
29
+ "type": "string",
30
+ "enum": ["PLAIN", "EXTERNAL", "AMQPLAIN"],
31
+ "description": "SASL mechanism used in connectionOptions.credentials."
32
+ },
33
+ "username": {
34
+ "type": "string",
35
+ "description": "connectionOptions.credentials.username."
36
+ },
37
+ "password": {
38
+ "type": "string",
39
+ "description": "connectionOptions.credentials.password."
40
+ },
41
+ "config": {
42
+ "type": "string",
43
+ "description": "Path to config.yaml (default: auto-detected)."
44
+ }
45
+ },
46
+ "required": []
47
+ }
@@ -0,0 +1,20 @@
1
+ export interface ConfigureBrokerOptions {
2
+ /** broker.uri (the AMQP connection string). */
3
+ uri?: string;
4
+ /** broker.prefetchCount (channel prefetch). */
5
+ prefetchCount?: number;
6
+ /** broker.defaultRpcTimeout (ms). */
7
+ defaultRpcTimeout?: number;
8
+ /** connectionManagerOptions.heartbeatIntervalInSeconds. */
9
+ heartbeatIntervalInSeconds?: number;
10
+ /** connectionManagerOptions.reconnectTimeInSeconds. */
11
+ reconnectTimeInSeconds?: number;
12
+ /** SASL mechanism used in connectionOptions.credentials. */
13
+ mechanism?: 'PLAIN' | 'EXTERNAL' | 'AMQPLAIN';
14
+ /** connectionOptions.credentials.username. */
15
+ username?: string;
16
+ /** connectionOptions.credentials.password. */
17
+ password?: string;
18
+ /** Path to config.yaml (default: auto-detected, typically config/config.yaml). */
19
+ config?: string;
20
+ }
@@ -0,0 +1,3 @@
1
+ import { Rule } from '@angular-devkit/schematics';
2
+ import { EnableLoadConfigOptions } from './schema';
3
+ export declare function main(options: EnableLoadConfigOptions): Rule;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.main = main;
4
+ const schematic_prompt_util_1 = require("../utils/schematic-prompt.util");
5
+ const yaml_config_util_1 = require("../utils/yaml-config.util");
6
+ function main(options) {
7
+ return async (tree, context) => {
8
+ const flagsProvided = options.pathsTopic !== undefined || options.eventsTopic !== undefined;
9
+ const prompts = (0, schematic_prompt_util_1.loadPrompts)(context, flagsProvided);
10
+ const pathsTopic = options.pathsTopic || (await (0, schematic_prompt_util_1.askText)(prompts, 'Paths export topic?', 'rlb-gateway-admin'));
11
+ const pathsAction = options.pathsAction || (await (0, schematic_prompt_util_1.askText)(prompts, 'Paths export action?', 'gw-path-export'));
12
+ const configPath = (0, yaml_config_util_1.findConfigYaml)(tree, options.config);
13
+ const { doc, existed } = (0, yaml_config_util_1.readConfigDoc)(tree, configPath);
14
+ if (!existed)
15
+ context.logger.info(`[rlb-amqp] ${configPath} not found — creating it.`);
16
+ const pOutcome = (0, yaml_config_util_1.setIn)(doc, ['gateway', 'loadConfig', 'paths'], { topic: pathsTopic, action: pathsAction });
17
+ (0, schematic_prompt_util_1.logOutcome)(context, 'gateway.loadConfig.paths', pOutcome);
18
+ if (options.eventsTopic && options.eventsAction) {
19
+ const eOutcome = (0, yaml_config_util_1.setIn)(doc, ['gateway', 'loadConfig', 'events'], {
20
+ topic: options.eventsTopic,
21
+ action: options.eventsAction,
22
+ });
23
+ (0, schematic_prompt_util_1.logOutcome)(context, 'gateway.loadConfig.events', eOutcome);
24
+ }
25
+ else if (options.eventsTopic || options.eventsAction) {
26
+ context.logger.warn('[rlb-amqp] enable-load-config: events needs BOTH eventsTopic and eventsAction — skipped.');
27
+ }
28
+ (0, yaml_config_util_1.writeConfigDoc)(tree, configPath, doc);
29
+ return tree;
30
+ };
31
+ }
32
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../libs/rlb-nestjs-amqp/src/schematics/enable-load-config/index.ts"],"names":[],"mappings":";;AAUA,oBA4BC;AArCD,0EAAkF;AAClF,gEAAiG;AAQjG,SAAgB,IAAI,CAAC,OAAgC;IACnD,OAAO,KAAK,EAAE,IAAU,EAAE,OAAyB,EAAE,EAAE;QACrD,MAAM,aAAa,GAAG,OAAO,CAAC,UAAU,KAAK,SAAS,IAAI,OAAO,CAAC,WAAW,KAAK,SAAS,CAAC;QAC5F,MAAM,OAAO,GAAG,IAAA,mCAAW,EAAC,OAAO,EAAE,aAAa,CAAC,CAAC;QAEpD,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,CAAC,MAAM,IAAA,+BAAO,EAAC,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,CAAC,CAAC,CAAC;QAC9G,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,CAAC,MAAM,IAAA,+BAAO,EAAC,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,CAAC,CAAC,CAAC;QAE9G,MAAM,UAAU,GAAG,IAAA,iCAAc,EAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QACxD,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,IAAA,gCAAa,EAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QACzD,IAAI,CAAC,OAAO;YAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,UAAU,2BAA2B,CAAC,CAAC;QAEvF,MAAM,QAAQ,GAAG,IAAA,wBAAK,EAAC,GAAG,EAAE,CAAC,SAAS,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;QAC5G,IAAA,kCAAU,EAAC,OAAO,EAAE,0BAA0B,EAAE,QAAQ,CAAC,CAAC;QAE1D,IAAI,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;YAChD,MAAM,QAAQ,GAAG,IAAA,wBAAK,EAAC,GAAG,EAAE,CAAC,SAAS,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE;gBAC/D,KAAK,EAAE,OAAO,CAAC,WAAW;gBAC1B,MAAM,EAAE,OAAO,CAAC,YAAY;aAC7B,CAAC,CAAC;YACH,IAAA,kCAAU,EAAC,OAAO,EAAE,2BAA2B,EAAE,QAAQ,CAAC,CAAC;QAC7D,CAAC;aAAM,IAAI,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;YACvD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,0FAA0F,CAAC,CAAC;QAClH,CAAC;QAED,IAAA,iCAAc,EAAC,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;QACtC,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,61 @@
1
+ import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
2
+ import { parseDocument } from 'yaml';
3
+ import { main } from './index';
4
+ import { EnableLoadConfigOptions } from './schema';
5
+
6
+ /**
7
+ * Factory invoked directly against a Tree (not SchematicTestRunner — ESM `ora`). Passing
8
+ * `pathsTopic` makes `flagsProvided` true → no prompts.
9
+ */
10
+ const ctx = { logger: { info() {}, warn() {}, error() {} } } as unknown as SchematicContext;
11
+
12
+ async function run(options: EnableLoadConfigOptions, tree: Tree): Promise<Tree> {
13
+ const rule = main(options) as Rule;
14
+ const result = await (rule as (t: Tree, c: SchematicContext) => Promise<Tree>)(tree, ctx);
15
+ return result || tree;
16
+ }
17
+
18
+ function read(tree: Tree, path = 'config/config.yaml'): string {
19
+ return tree.read(path)!.toString('utf-8');
20
+ }
21
+ function doc(tree: Tree) {
22
+ return parseDocument(read(tree));
23
+ }
24
+ function seedConfig(): Tree {
25
+ const tree = Tree.empty();
26
+ tree.create('config/config.yaml', ['# my config', 'gateway:', ' paths: []', ''].join('\n'));
27
+ return tree;
28
+ }
29
+
30
+ describe('enable-load-config schematic', () => {
31
+ it('sets gateway.loadConfig.paths with defaults, preserving comments', async () => {
32
+ const tree = await run({ pathsTopic: 'rlb-gateway-admin' }, seedConfig());
33
+ const d = doc(tree);
34
+ expect(String(d.getIn(['gateway', 'loadConfig', 'paths', 'topic']))).toBe('rlb-gateway-admin');
35
+ expect(String(d.getIn(['gateway', 'loadConfig', 'paths', 'action']))).toBe('gw-path-export');
36
+ expect(d.hasIn(['gateway', 'loadConfig', 'events'])).toBe(false);
37
+ expect(read(tree)).toContain('# my config');
38
+ });
39
+
40
+ it('adds events only when both eventsTopic and eventsAction are provided', async () => {
41
+ const tree = await run(
42
+ { pathsTopic: 'p', pathsAction: 'pa', eventsTopic: 'e', eventsAction: 'ea' },
43
+ seedConfig(),
44
+ );
45
+ const d = doc(tree);
46
+ expect(String(d.getIn(['gateway', 'loadConfig', 'events', 'topic']))).toBe('e');
47
+ expect(String(d.getIn(['gateway', 'loadConfig', 'events', 'action']))).toBe('ea');
48
+ });
49
+
50
+ it('skips events when only one of the pair is given', async () => {
51
+ const tree = await run({ pathsTopic: 'p', eventsTopic: 'e' }, seedConfig());
52
+ expect(doc(tree).hasIn(['gateway', 'loadConfig', 'events'])).toBe(false);
53
+ });
54
+
55
+ it('is idempotent', async () => {
56
+ let tree = await run({ pathsTopic: 'rlb-gateway-admin' }, seedConfig());
57
+ tree = await run({ pathsTopic: 'rlb-gateway-admin' }, tree);
58
+ const d = doc(tree);
59
+ expect(String(d.getIn(['gateway', 'loadConfig', 'paths', 'topic']))).toBe('rlb-gateway-admin');
60
+ });
61
+ });