@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.
- package/package.json +6 -5
- package/schematics/add-auth-provider/index.d.ts +3 -0
- package/schematics/add-auth-provider/index.js +60 -0
- package/schematics/add-auth-provider/index.js.map +1 -0
- package/schematics/add-auth-provider/index.spec.ts +83 -0
- package/schematics/add-auth-provider/index.ts +83 -0
- package/schematics/add-auth-provider/schema.d.ts +18 -0
- package/schematics/add-auth-provider/schema.js +3 -0
- package/schematics/add-auth-provider/schema.js.map +1 -0
- package/schematics/add-auth-provider/schema.json +59 -0
- package/schematics/add-auth-provider/schema.ts +35 -0
- package/schematics/add-exchange/index.d.ts +3 -0
- package/schematics/add-exchange/index.js +49 -0
- package/schematics/add-exchange/index.js.map +1 -0
- package/schematics/add-exchange/index.spec.ts +65 -0
- package/schematics/add-exchange/index.ts +65 -0
- package/schematics/add-exchange/schema.d.ts +14 -0
- package/schematics/add-exchange/schema.js +3 -0
- package/schematics/add-exchange/schema.js.map +1 -0
- package/schematics/add-exchange/schema.json +50 -0
- package/schematics/add-exchange/schema.ts +26 -0
- package/schematics/add-exchange-binding/index.d.ts +3 -0
- package/schematics/add-exchange-binding/index.js +53 -0
- package/schematics/add-exchange-binding/index.js.map +1 -0
- package/schematics/add-exchange-binding/index.spec.ts +73 -0
- package/schematics/add-exchange-binding/index.ts +64 -0
- package/schematics/add-exchange-binding/schema.d.ts +8 -0
- package/schematics/add-exchange-binding/schema.js +3 -0
- package/schematics/add-exchange-binding/schema.js.map +1 -0
- package/schematics/add-exchange-binding/schema.json +36 -0
- package/schematics/add-exchange-binding/schema.ts +14 -0
- package/schematics/add-gateway/index.d.ts +4 -0
- package/schematics/add-gateway/index.js +105 -0
- package/schematics/add-gateway/index.js.map +1 -0
- package/schematics/add-gateway/index.spec.ts +163 -0
- package/schematics/add-gateway/index.ts +141 -0
- package/schematics/add-gateway/paths.d.ts +12 -0
- package/schematics/add-gateway/paths.js +87 -0
- package/schematics/add-gateway/paths.js.map +1 -0
- package/schematics/add-gateway/paths.ts +103 -0
- package/schematics/add-gateway/schema.d.ts +13 -0
- package/schematics/add-gateway/schema.js +3 -0
- package/schematics/add-gateway/schema.js.map +1 -0
- package/schematics/add-gateway/schema.json +63 -0
- package/schematics/add-gateway/schema.ts +24 -0
- package/schematics/add-gateway/wiring.d.ts +14 -0
- package/schematics/add-gateway/wiring.js +102 -0
- package/schematics/add-gateway/wiring.js.map +1 -0
- package/schematics/add-gateway/wiring.ts +115 -0
- package/schematics/add-metrics-topic/index.d.ts +3 -0
- package/schematics/add-metrics-topic/index.js +31 -0
- package/schematics/add-metrics-topic/index.js.map +1 -0
- package/schematics/add-metrics-topic/index.spec.ts +79 -0
- package/schematics/add-metrics-topic/index.ts +49 -0
- package/schematics/add-metrics-topic/schema.d.ts +10 -0
- package/schematics/add-metrics-topic/schema.js +3 -0
- package/schematics/add-metrics-topic/schema.js.map +1 -0
- package/schematics/add-metrics-topic/schema.json +48 -0
- package/schematics/add-metrics-topic/schema.ts +18 -0
- package/schematics/add-queue/index.d.ts +3 -0
- package/schematics/add-queue/index.js +48 -0
- package/schematics/add-queue/index.js.map +1 -0
- package/schematics/add-queue/index.spec.ts +75 -0
- package/schematics/add-queue/index.ts +74 -0
- package/schematics/add-queue/schema.d.ts +17 -0
- package/schematics/add-queue/schema.js +3 -0
- package/schematics/add-queue/schema.js.map +1 -0
- package/schematics/add-queue/schema.json +54 -0
- package/schematics/add-queue/schema.ts +32 -0
- package/schematics/add-reply-queue/index.d.ts +3 -0
- package/schematics/add-reply-queue/index.js +26 -0
- package/schematics/add-reply-queue/index.js.map +1 -0
- package/schematics/add-reply-queue/index.spec.ts +69 -0
- package/schematics/add-reply-queue/index.ts +33 -0
- package/schematics/add-reply-queue/schema.d.ts +6 -0
- package/schematics/add-reply-queue/schema.js +3 -0
- package/schematics/add-reply-queue/schema.js.map +1 -0
- package/schematics/add-reply-queue/schema.json +28 -0
- package/schematics/add-reply-queue/schema.ts +10 -0
- package/schematics/add-route/index.d.ts +3 -0
- package/schematics/add-route/index.js +57 -0
- package/schematics/add-route/index.js.map +1 -0
- package/schematics/add-route/index.spec.ts +75 -0
- package/schematics/add-route/index.ts +74 -0
- package/schematics/add-route/schema.d.ts +22 -0
- package/schematics/add-route/schema.js +3 -0
- package/schematics/add-route/schema.js.map +1 -0
- package/schematics/add-route/schema.json +74 -0
- package/schematics/add-route/schema.ts +43 -0
- package/schematics/add-topic/index.d.ts +3 -0
- package/schematics/add-topic/index.js +69 -0
- package/schematics/add-topic/index.js.map +1 -0
- package/schematics/add-topic/index.spec.ts +100 -0
- package/schematics/add-topic/index.ts +104 -0
- package/schematics/add-topic/schema.d.ts +18 -0
- package/schematics/add-topic/schema.js +3 -0
- package/schematics/add-topic/schema.js.map +1 -0
- package/schematics/add-topic/schema.json +63 -0
- package/schematics/add-topic/schema.ts +34 -0
- package/schematics/add-ws-event/index.d.ts +3 -0
- package/schematics/add-ws-event/index.js +53 -0
- package/schematics/add-ws-event/index.js.map +1 -0
- package/schematics/add-ws-event/index.spec.ts +85 -0
- package/schematics/add-ws-event/index.ts +72 -0
- package/schematics/add-ws-event/schema.d.ts +17 -0
- package/schematics/add-ws-event/schema.js +3 -0
- package/schematics/add-ws-event/schema.js.map +1 -0
- package/schematics/add-ws-event/schema.json +53 -0
- package/schematics/add-ws-event/schema.ts +33 -0
- package/schematics/collection.json +81 -1
- package/schematics/configure-broker/index.d.ts +3 -0
- package/schematics/configure-broker/index.js +41 -0
- package/schematics/configure-broker/index.js.map +1 -0
- package/schematics/configure-broker/index.spec.ts +62 -0
- package/schematics/configure-broker/index.ts +51 -0
- package/schematics/configure-broker/schema.d.ts +11 -0
- package/schematics/configure-broker/schema.js +3 -0
- package/schematics/configure-broker/schema.js.map +1 -0
- package/schematics/configure-broker/schema.json +47 -0
- package/schematics/configure-broker/schema.ts +20 -0
- package/schematics/enable-load-config/index.d.ts +3 -0
- package/schematics/enable-load-config/index.js +32 -0
- package/schematics/enable-load-config/index.js.map +1 -0
- package/schematics/enable-load-config/index.spec.ts +61 -0
- package/schematics/enable-load-config/index.ts +39 -0
- package/schematics/enable-load-config/schema.d.ts +8 -0
- package/schematics/enable-load-config/schema.js +3 -0
- package/schematics/enable-load-config/schema.js.map +1 -0
- package/schematics/enable-load-config/schema.json +36 -0
- package/schematics/enable-load-config/schema.ts +14 -0
- package/schematics/enable-retry/index.d.ts +3 -0
- package/schematics/enable-retry/index.js +51 -0
- package/schematics/enable-retry/index.js.map +1 -0
- package/schematics/enable-retry/index.spec.ts +73 -0
- package/schematics/enable-retry/index.ts +62 -0
- package/schematics/enable-retry/schema.d.ts +13 -0
- package/schematics/enable-retry/schema.js +3 -0
- package/schematics/enable-retry/schema.js.map +1 -0
- package/schematics/enable-retry/schema.json +64 -0
- package/schematics/enable-retry/schema.ts +24 -0
- package/schematics/enable-route-discovery/index.d.ts +3 -0
- package/schematics/enable-route-discovery/index.js +36 -0
- package/schematics/enable-route-discovery/index.js.map +1 -0
- package/schematics/enable-route-discovery/index.spec.ts +55 -0
- package/schematics/enable-route-discovery/index.ts +48 -0
- package/schematics/enable-route-discovery/schema.d.ts +9 -0
- package/schematics/enable-route-discovery/schema.js +3 -0
- package/schematics/enable-route-discovery/schema.js.map +1 -0
- package/schematics/enable-route-discovery/schema.json +44 -0
- package/schematics/enable-route-discovery/schema.ts +16 -0
- package/schematics/harden-gateway/index.d.ts +3 -0
- package/schematics/harden-gateway/index.js +93 -0
- package/schematics/harden-gateway/index.js.map +1 -0
- package/schematics/harden-gateway/index.spec.ts +105 -0
- package/schematics/harden-gateway/index.ts +108 -0
- package/schematics/harden-gateway/schema.d.ts +11 -0
- package/schematics/harden-gateway/schema.js +3 -0
- package/schematics/harden-gateway/schema.js.map +1 -0
- package/schematics/harden-gateway/schema.json +47 -0
- package/schematics/harden-gateway/schema.ts +20 -0
- package/schematics/nest-add/index.js +55 -832
- package/schematics/nest-add/index.js.map +1 -1
- package/schematics/nest-add/index.spec.ts +73 -0
- package/schematics/nest-add/index.ts +76 -917
- package/schematics/nest-add/init.schema.d.ts +1 -0
- package/schematics/nest-add/init.schema.ts +3 -0
- package/schematics/nest-add/schema.json +4 -43
- package/schematics/set-connection-name/index.d.ts +3 -0
- package/schematics/set-connection-name/index.js +34 -0
- package/schematics/set-connection-name/index.js.map +1 -0
- package/schematics/set-connection-name/index.spec.ts +48 -0
- package/schematics/set-connection-name/index.ts +42 -0
- package/schematics/set-connection-name/schema.d.ts +5 -0
- package/schematics/set-connection-name/schema.js +3 -0
- package/schematics/set-connection-name/schema.js.map +1 -0
- package/schematics/set-connection-name/schema.json +24 -0
- package/schematics/set-connection-name/schema.ts +8 -0
- package/schematics/utils/broker-yaml.util.d.ts +53 -0
- package/schematics/utils/broker-yaml.util.js +91 -0
- package/schematics/utils/broker-yaml.util.js.map +1 -0
- package/schematics/utils/broker-yaml.util.ts +172 -0
- package/schematics/utils/nest-wiring.util.d.ts +30 -0
- package/schematics/utils/nest-wiring.util.js +170 -0
- package/schematics/utils/nest-wiring.util.js.map +1 -0
- package/schematics/utils/nest-wiring.util.ts +211 -0
- package/schematics/utils/schematic-prompt.util.d.ts +7 -0
- package/schematics/utils/schematic-prompt.util.js +57 -0
- package/schematics/utils/schematic-prompt.util.js.map +1 -0
- package/schematics/utils/schematic-prompt.util.ts +82 -0
- package/schematics/utils/yaml-config.util.d.ts +22 -0
- package/schematics/utils/yaml-config.util.js +119 -0
- package/schematics/utils/yaml-config.util.js.map +1 -0
- package/schematics/utils/yaml-config.util.ts +167 -0
- /package/schematics/{nest-add → add-gateway}/files/acl/src/cache/in-memory-acl-store.ts +0 -0
- /package/schematics/{nest-add → add-gateway}/files/acl/src/modules/database/repository/acl.repository.ts +0 -0
- /package/schematics/{nest-add → add-gateway}/files/db-core/src/modules/database/repository/in-memory-collection.ts +0 -0
- /package/schematics/{nest-add → add-gateway}/files/gateway-admin/src/modules/database/repository/gateway.repository.ts +0 -0
- /package/schematics/{nest-add → add-gateway}/files/gateway-admin/src/modules/database/repository/route-sync.repository.ts +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/schema",
|
|
3
|
+
"$id": "SchematicsRlbAmqpAddExchange",
|
|
4
|
+
"title": "Add a broker exchange to config.yaml",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"name": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"description": "Exchange name (the key).",
|
|
10
|
+
"x-prompt": "Exchange name?",
|
|
11
|
+
"$default": { "$source": "argv", "index": 0 }
|
|
12
|
+
},
|
|
13
|
+
"type": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"enum": ["direct", "topic", "fanout", "headers"],
|
|
16
|
+
"default": "direct",
|
|
17
|
+
"description": "Exchange type."
|
|
18
|
+
},
|
|
19
|
+
"createIfNotExists": {
|
|
20
|
+
"type": "boolean",
|
|
21
|
+
"default": true,
|
|
22
|
+
"description": "Emit createExchangeIfNotExists (false => the exchange must pre-exist)."
|
|
23
|
+
},
|
|
24
|
+
"durable": { "type": "boolean", "default": true, "description": "options.durable" },
|
|
25
|
+
"autoDelete": { "type": "boolean", "default": false, "description": "options.autoDelete" },
|
|
26
|
+
"internal": { "type": "boolean", "default": false, "description": "options.internal" },
|
|
27
|
+
"withQueue": {
|
|
28
|
+
"type": "boolean",
|
|
29
|
+
"description": "Also create a queue bound to this exchange."
|
|
30
|
+
},
|
|
31
|
+
"queueName": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"description": "Name of the bound queue (default: the exchange name)."
|
|
34
|
+
},
|
|
35
|
+
"routingKey": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"description": "routingKey for the bound queue (required when type=topic; default: the queue name)."
|
|
38
|
+
},
|
|
39
|
+
"overwrite": {
|
|
40
|
+
"type": "boolean",
|
|
41
|
+
"default": false,
|
|
42
|
+
"description": "Update the entry if it already exists (default: leave it untouched)."
|
|
43
|
+
},
|
|
44
|
+
"config": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"description": "Path to config.yaml (default: auto-detected)."
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"required": []
|
|
50
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ExchangeType } from '../utils/broker-yaml.util';
|
|
2
|
+
|
|
3
|
+
export interface AddExchangeOptions {
|
|
4
|
+
/** Exchange name (the key). Prompted when omitted. */
|
|
5
|
+
name?: string;
|
|
6
|
+
/** direct | topic | fanout | headers. Default: direct. */
|
|
7
|
+
type?: ExchangeType;
|
|
8
|
+
/** Emit `createExchangeIfNotExists`. Default: true. */
|
|
9
|
+
createIfNotExists?: boolean;
|
|
10
|
+
/** options.durable. Default: true. */
|
|
11
|
+
durable?: boolean;
|
|
12
|
+
/** options.autoDelete. Default: false. */
|
|
13
|
+
autoDelete?: boolean;
|
|
14
|
+
/** options.internal. Default: false. */
|
|
15
|
+
internal?: boolean;
|
|
16
|
+
/** Also create a queue bound to this exchange. */
|
|
17
|
+
withQueue?: boolean;
|
|
18
|
+
/** Name of that queue. Default: the exchange name. */
|
|
19
|
+
queueName?: string;
|
|
20
|
+
/** routingKey for the bound queue (required when type=topic). Default: the queue name. */
|
|
21
|
+
routingKey?: string;
|
|
22
|
+
/** Update the entry when it already exists (default: leave it untouched). */
|
|
23
|
+
overwrite?: boolean;
|
|
24
|
+
/** Path to config.yaml (default: auto-detected, typically config/config.yaml). */
|
|
25
|
+
config?: string;
|
|
26
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.main = main;
|
|
4
|
+
const yaml_1 = require("yaml");
|
|
5
|
+
const schematic_prompt_util_1 = require("../utils/schematic-prompt.util");
|
|
6
|
+
const yaml_config_util_1 = require("../utils/yaml-config.util");
|
|
7
|
+
const BINDINGS = ['broker', 'exchangeBindings'];
|
|
8
|
+
function main(options) {
|
|
9
|
+
return async (tree, context) => {
|
|
10
|
+
const flagsProvided = options.source !== undefined;
|
|
11
|
+
const prompts = (0, schematic_prompt_util_1.loadPrompts)(context, flagsProvided);
|
|
12
|
+
const source = options.source || (await (0, schematic_prompt_util_1.askText)(prompts, 'Source exchange?', ''));
|
|
13
|
+
const destination = options.destination || (await (0, schematic_prompt_util_1.askText)(prompts, 'Destination exchange?', ''));
|
|
14
|
+
if (!source || !destination) {
|
|
15
|
+
context.logger.error('[rlb-amqp] add-exchange-binding: source and destination are required.');
|
|
16
|
+
return tree;
|
|
17
|
+
}
|
|
18
|
+
const pattern = options.pattern ?? (await (0, schematic_prompt_util_1.askText)(prompts, 'Binding pattern (routing key)?', ''));
|
|
19
|
+
const configPath = (0, yaml_config_util_1.findConfigYaml)(tree, options.config);
|
|
20
|
+
const { doc, existed } = (0, yaml_config_util_1.readConfigDoc)(tree, configPath);
|
|
21
|
+
if (!existed)
|
|
22
|
+
context.logger.info(`[rlb-amqp] ${configPath} not found — creating it.`);
|
|
23
|
+
const item = { source, destination, pattern };
|
|
24
|
+
if (options.args !== undefined)
|
|
25
|
+
item.args = options.args;
|
|
26
|
+
const seq = (0, yaml_config_util_1.ensureSeqAt)(doc, BINDINGS);
|
|
27
|
+
const existing = seq.items.find((it) => (0, yaml_1.isMap)(it) &&
|
|
28
|
+
String(it.get('source')) === String(source) &&
|
|
29
|
+
String(it.get('destination')) === String(destination) &&
|
|
30
|
+
String(it.get('pattern')) === String(pattern));
|
|
31
|
+
let outcome;
|
|
32
|
+
if (!existing) {
|
|
33
|
+
const node = doc.createNode(item);
|
|
34
|
+
seq.flow = false;
|
|
35
|
+
node.flow = false;
|
|
36
|
+
seq.add(node);
|
|
37
|
+
outcome = 'created';
|
|
38
|
+
}
|
|
39
|
+
else if (options.overwrite) {
|
|
40
|
+
const before = existing.toString();
|
|
41
|
+
for (const [k, v] of Object.entries(item))
|
|
42
|
+
existing.set(k, doc.createNode(v));
|
|
43
|
+
outcome = existing.toString() === before ? 'unchanged' : 'updated';
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
outcome = 'unchanged';
|
|
47
|
+
}
|
|
48
|
+
(0, schematic_prompt_util_1.logOutcome)(context, `exchange binding '${source}' → '${destination}'`, outcome);
|
|
49
|
+
(0, yaml_config_util_1.writeConfigDoc)(tree, configPath, doc);
|
|
50
|
+
return tree;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../libs/rlb-nestjs-amqp/src/schematics/add-exchange-binding/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
|
|
2
|
+
import { parseDocument } from 'yaml';
|
|
3
|
+
import { main } from './index';
|
|
4
|
+
import { AddExchangeBindingOptions } from './schema';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Invoke the factory Rule directly against a Tree (NOT SchematicTestRunner — it eagerly loads `ora`,
|
|
8
|
+
* an ESM-only module jest cannot transform here). Passing `source` makes `flagsProvided` true, so no
|
|
9
|
+
* interactive prompt is attempted.
|
|
10
|
+
*/
|
|
11
|
+
const ctx = { logger: { info() {}, warn() {}, error() {} } } as unknown as SchematicContext;
|
|
12
|
+
|
|
13
|
+
async function run(options: AddExchangeBindingOptions, tree: Tree): Promise<Tree> {
|
|
14
|
+
const rule = main(options) as Rule;
|
|
15
|
+
const result = await (rule as (t: Tree, c: SchematicContext) => Promise<Tree>)(tree, ctx);
|
|
16
|
+
return result || tree;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function read(tree: Tree, path = 'config/config.yaml'): string {
|
|
20
|
+
return tree.read(path)!.toString('utf-8');
|
|
21
|
+
}
|
|
22
|
+
function doc(tree: Tree) {
|
|
23
|
+
return parseDocument(read(tree));
|
|
24
|
+
}
|
|
25
|
+
function seedConfig(): Tree {
|
|
26
|
+
const tree = Tree.empty();
|
|
27
|
+
tree.create(
|
|
28
|
+
'config/config.yaml',
|
|
29
|
+
['# my config', 'broker:', ' exchanges:', ' - name: rlb', ' type: direct', 'topics: []', ''].join('\n'),
|
|
30
|
+
);
|
|
31
|
+
return tree;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe('add-exchange-binding schematic', () => {
|
|
35
|
+
it('appends a new binding, preserving comments', async () => {
|
|
36
|
+
const tree = await run({ source: 'rlb', destination: 'audit', pattern: 'order.*' }, seedConfig());
|
|
37
|
+
const bindings = doc(tree).getIn(['broker', 'exchangeBindings']) as any;
|
|
38
|
+
expect(bindings.items).toHaveLength(1);
|
|
39
|
+
expect(String(bindings.items[0].get('source'))).toBe('rlb');
|
|
40
|
+
expect(String(bindings.items[0].get('destination'))).toBe('audit');
|
|
41
|
+
expect(String(bindings.items[0].get('pattern'))).toBe('order.*');
|
|
42
|
+
expect(read(tree)).toContain('# my config');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('is idempotent: re-adding the same triple leaves a single entry', async () => {
|
|
46
|
+
let tree = await run({ source: 'rlb', destination: 'audit', pattern: 'order.*' }, seedConfig());
|
|
47
|
+
tree = await run({ source: 'rlb', destination: 'audit', pattern: 'order.*' }, tree);
|
|
48
|
+
const bindings = doc(tree).getIn(['broker', 'exchangeBindings']) as any;
|
|
49
|
+
expect(bindings.items).toHaveLength(1);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('treats a different pattern as a distinct binding', async () => {
|
|
53
|
+
let tree = await run({ source: 'rlb', destination: 'audit', pattern: 'order.*' }, seedConfig());
|
|
54
|
+
tree = await run({ source: 'rlb', destination: 'audit', pattern: 'user.*' }, tree);
|
|
55
|
+
const bindings = doc(tree).getIn(['broker', 'exchangeBindings']) as any;
|
|
56
|
+
expect(bindings.items).toHaveLength(2);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('updates fields on an existing binding only when overwrite is set', async () => {
|
|
60
|
+
let tree = await run({ source: 'rlb', destination: 'audit', pattern: 'order.*' }, seedConfig());
|
|
61
|
+
tree = await run(
|
|
62
|
+
{ source: 'rlb', destination: 'audit', pattern: 'order.*', args: { 'x-match': 'all' }, overwrite: true },
|
|
63
|
+
tree,
|
|
64
|
+
);
|
|
65
|
+
const binding = (doc(tree).getIn(['broker', 'exchangeBindings']) as any).items[0];
|
|
66
|
+
expect((binding.get('args') as any).get('x-match')).toBe('all');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('creates config.yaml when it does not exist', async () => {
|
|
70
|
+
const tree = await run({ source: 'rlb', destination: 'audit', pattern: 'order.*' }, Tree.empty());
|
|
71
|
+
expect(tree.exists('config/config.yaml')).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
|
|
2
|
+
import { isMap, YAMLMap } from 'yaml';
|
|
3
|
+
import { askText, loadPrompts, logOutcome } from '../utils/schematic-prompt.util';
|
|
4
|
+
import { ensureSeqAt, findConfigYaml, readConfigDoc, UpsertOutcome, writeConfigDoc } from '../utils/yaml-config.util';
|
|
5
|
+
import { AddExchangeBindingOptions } from './schema';
|
|
6
|
+
|
|
7
|
+
const BINDINGS = ['broker', 'exchangeBindings'];
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* `add-exchange-binding` — idempotent upsert of a `broker.exchangeBindings[]` entry, comment-preserving.
|
|
11
|
+
* Unlike exchanges/queues/topics there is no single `name` key, so identity is the (source, destination,
|
|
12
|
+
* pattern) triple. We scan the seq for a matching item ourselves rather than reusing upsertSeqItemByKey.
|
|
13
|
+
*/
|
|
14
|
+
export function main(options: AddExchangeBindingOptions): Rule {
|
|
15
|
+
return async (tree: Tree, context: SchematicContext) => {
|
|
16
|
+
const flagsProvided = options.source !== undefined;
|
|
17
|
+
const prompts = loadPrompts(context, flagsProvided);
|
|
18
|
+
|
|
19
|
+
const source = options.source || (await askText(prompts, 'Source exchange?', ''));
|
|
20
|
+
const destination = options.destination || (await askText(prompts, 'Destination exchange?', ''));
|
|
21
|
+
if (!source || !destination) {
|
|
22
|
+
context.logger.error('[rlb-amqp] add-exchange-binding: source and destination are required.');
|
|
23
|
+
return tree;
|
|
24
|
+
}
|
|
25
|
+
const pattern = options.pattern ?? (await askText(prompts, 'Binding pattern (routing key)?', ''));
|
|
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
|
+
const item: Record<string, unknown> = { source, destination, pattern };
|
|
32
|
+
if (options.args !== undefined) item.args = options.args;
|
|
33
|
+
|
|
34
|
+
const seq = ensureSeqAt(doc, BINDINGS);
|
|
35
|
+
// Identity = source + destination + pattern (pattern discriminates fanout vs. topic bindings).
|
|
36
|
+
const existing = seq.items.find(
|
|
37
|
+
(it): it is YAMLMap =>
|
|
38
|
+
isMap(it) &&
|
|
39
|
+
String(it.get('source')) === String(source) &&
|
|
40
|
+
String(it.get('destination')) === String(destination) &&
|
|
41
|
+
String(it.get('pattern')) === String(pattern),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
let outcome: UpsertOutcome;
|
|
45
|
+
if (!existing) {
|
|
46
|
+
const node = doc.createNode(item) as YAMLMap;
|
|
47
|
+
// Keep the container block-styled even if it was seeded as an empty flow seq (`exchangeBindings: []`).
|
|
48
|
+
seq.flow = false;
|
|
49
|
+
node.flow = false;
|
|
50
|
+
seq.add(node);
|
|
51
|
+
outcome = 'created';
|
|
52
|
+
} else if (options.overwrite) {
|
|
53
|
+
const before = existing.toString();
|
|
54
|
+
for (const [k, v] of Object.entries(item)) existing.set(k, doc.createNode(v));
|
|
55
|
+
outcome = existing.toString() === before ? 'unchanged' : 'updated';
|
|
56
|
+
} else {
|
|
57
|
+
outcome = 'unchanged';
|
|
58
|
+
}
|
|
59
|
+
logOutcome(context, `exchange binding '${source}' → '${destination}'`, outcome);
|
|
60
|
+
|
|
61
|
+
writeConfigDoc(tree, configPath, doc);
|
|
62
|
+
return tree;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../libs/rlb-nestjs-amqp/src/schematics/add-exchange-binding/schema.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/schema",
|
|
3
|
+
"$id": "SchematicsRlbAmqpAddExchangeBinding",
|
|
4
|
+
"title": "Bind two exchanges via broker.exchangeBindings[]",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"source": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"description": "Source exchange.",
|
|
10
|
+
"x-prompt": "Source exchange?",
|
|
11
|
+
"$default": { "$source": "argv", "index": 0 }
|
|
12
|
+
},
|
|
13
|
+
"destination": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "Destination exchange."
|
|
16
|
+
},
|
|
17
|
+
"pattern": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"description": "Binding pattern / routing key."
|
|
20
|
+
},
|
|
21
|
+
"args": {
|
|
22
|
+
"type": "object",
|
|
23
|
+
"description": "Optional binding arguments."
|
|
24
|
+
},
|
|
25
|
+
"overwrite": {
|
|
26
|
+
"type": "boolean",
|
|
27
|
+
"default": false,
|
|
28
|
+
"description": "Update the entry if it already exists (default: leave it untouched)."
|
|
29
|
+
},
|
|
30
|
+
"config": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"description": "Path to config.yaml (default: auto-detected)."
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"required": []
|
|
36
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface AddExchangeBindingOptions {
|
|
2
|
+
/** Source exchange (mirrors RabbitMQExchangeBindingConfig.source). Prompted when omitted. */
|
|
3
|
+
source?: string;
|
|
4
|
+
/** Destination exchange (mirrors RabbitMQExchangeBindingConfig.destination). */
|
|
5
|
+
destination?: string;
|
|
6
|
+
/** Binding pattern / routing key (mirrors RabbitMQExchangeBindingConfig.pattern). */
|
|
7
|
+
pattern?: string;
|
|
8
|
+
/** Optional binding arguments (mirrors RabbitMQExchangeBindingConfig.args). */
|
|
9
|
+
args?: Record<string, unknown>;
|
|
10
|
+
/** Update the entry when it already exists (default: leave it untouched). */
|
|
11
|
+
overwrite?: boolean;
|
|
12
|
+
/** Path to config.yaml (default: auto-detected, typically config/config.yaml). */
|
|
13
|
+
config?: string;
|
|
14
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.main = main;
|
|
4
|
+
exports.configAndWireRule = configAndWireRule;
|
|
5
|
+
const schematics_1 = require("@angular-devkit/schematics");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const broker_yaml_util_1 = require("../utils/broker-yaml.util");
|
|
8
|
+
const nest_wiring_util_1 = require("../utils/nest-wiring.util");
|
|
9
|
+
const yaml_config_util_1 = require("../utils/yaml-config.util");
|
|
10
|
+
const paths_1 = require("./paths");
|
|
11
|
+
const wiring_1 = require("./wiring");
|
|
12
|
+
function main(options) {
|
|
13
|
+
const r = resolveFlags(options);
|
|
14
|
+
return (0, schematics_1.chain)([
|
|
15
|
+
configAndWireRule(options),
|
|
16
|
+
copyAsset('db-core'),
|
|
17
|
+
r.acl ? copyAsset('acl') : (0, schematics_1.noop)(),
|
|
18
|
+
r.anyAdmin ? copyAsset('gateway-admin') : (0, schematics_1.noop)(),
|
|
19
|
+
]);
|
|
20
|
+
}
|
|
21
|
+
function resolveFlags(options) {
|
|
22
|
+
const exchange = options.exchange || 'rlb';
|
|
23
|
+
const aclQueue = options.aclQueue || 'rlb-acl';
|
|
24
|
+
const adminQueue = options.adminQueue || 'rlb-gateway-admin';
|
|
25
|
+
const controlTopic = options.controlTopic || 'rlb-gateway-control';
|
|
26
|
+
const routeExchange = options.routeExchange || 'rlb-route-discovery';
|
|
27
|
+
const routeQueue = options.routeQueue || 'rlb-route-sync';
|
|
28
|
+
const acl = options.acl ?? true;
|
|
29
|
+
const admin = options.admin ?? true;
|
|
30
|
+
const routeReception = options.routeReception ?? true;
|
|
31
|
+
const anyAdmin = admin || routeReception;
|
|
32
|
+
const features = { acl, admin, routeReception, routeExchange, routeQueue };
|
|
33
|
+
return { exchange, aclQueue, adminQueue, controlTopic, routeExchange, routeQueue, acl, admin, routeReception, anyAdmin, features };
|
|
34
|
+
}
|
|
35
|
+
function configAndWireRule(options) {
|
|
36
|
+
return (tree, context) => {
|
|
37
|
+
const r = resolveFlags(options);
|
|
38
|
+
const opts = { overwrite: options.overwrite };
|
|
39
|
+
const configPath = (0, yaml_config_util_1.findConfigYaml)(tree, options.config);
|
|
40
|
+
const { doc, existed } = (0, yaml_config_util_1.readConfigDoc)(tree, configPath);
|
|
41
|
+
if (!existed)
|
|
42
|
+
context.logger.info(`[rlb-amqp] ${configPath} not found — creating it.`);
|
|
43
|
+
if (doc.hasIn(['broker', 'routeDiscovery'])) {
|
|
44
|
+
doc.deleteIn(['broker', 'routeDiscovery']);
|
|
45
|
+
context.logger.info('[rlb-amqp] removed broker.routeDiscovery (a gateway consumes routes, it does not publish its own).');
|
|
46
|
+
}
|
|
47
|
+
(0, broker_yaml_util_1.ensureExchange)(doc, { name: r.exchange, type: 'direct' }, opts);
|
|
48
|
+
if (r.acl) {
|
|
49
|
+
(0, broker_yaml_util_1.ensureQueue)(doc, { name: r.aclQueue, exchange: r.exchange, routingKey: r.aclQueue, durable: true }, opts);
|
|
50
|
+
(0, broker_yaml_util_1.ensureTopic)(doc, { name: 'rlb-acl', mode: 'rpc', queue: r.aclQueue, exchange: r.exchange, routingKey: r.aclQueue }, opts);
|
|
51
|
+
}
|
|
52
|
+
if (r.anyAdmin) {
|
|
53
|
+
(0, broker_yaml_util_1.ensureQueue)(doc, { name: r.adminQueue, exchange: r.exchange, routingKey: r.adminQueue, durable: true }, opts);
|
|
54
|
+
(0, broker_yaml_util_1.ensureTopic)(doc, { name: 'rlb-gateway-admin', mode: 'rpc', queue: r.adminQueue, exchange: r.exchange, routingKey: r.adminQueue }, opts);
|
|
55
|
+
(0, broker_yaml_util_1.ensureTopic)(doc, { name: r.controlTopic, mode: 'broadcast', exchange: r.exchange, routingKey: r.controlTopic }, opts);
|
|
56
|
+
}
|
|
57
|
+
if (r.routeReception) {
|
|
58
|
+
(0, broker_yaml_util_1.ensureExchange)(doc, { name: r.routeExchange, type: 'fanout' }, opts);
|
|
59
|
+
(0, broker_yaml_util_1.ensureQueue)(doc, { name: r.routeQueue, exchange: r.routeExchange, durable: true }, opts);
|
|
60
|
+
}
|
|
61
|
+
if (!doc.hasIn(['gateway', 'events']))
|
|
62
|
+
(0, yaml_config_util_1.setIn)(doc, ['gateway', 'events'], []);
|
|
63
|
+
if (!doc.hasIn(['gateway', 'ws']))
|
|
64
|
+
(0, yaml_config_util_1.setIn)(doc, ['gateway', 'ws'], { heartbeatIntervalMs: 30000 });
|
|
65
|
+
if (r.anyAdmin) {
|
|
66
|
+
if (!doc.hasIn(['gateway', 'reloadTopic']))
|
|
67
|
+
(0, yaml_config_util_1.setIn)(doc, ['gateway', 'reloadTopic'], r.controlTopic);
|
|
68
|
+
if (!doc.hasIn(['gateway', 'metrics']))
|
|
69
|
+
(0, yaml_config_util_1.setIn)(doc, ['gateway', 'metrics'], { topic: 'rlb-gateway-admin', action: 'gw-metrics-track' });
|
|
70
|
+
if (!doc.hasIn(['gateway', 'loadConfig']))
|
|
71
|
+
(0, yaml_config_util_1.setIn)(doc, ['gateway', 'loadConfig'], { paths: { topic: 'rlb-gateway-admin', action: 'gw-path-export' } });
|
|
72
|
+
}
|
|
73
|
+
if (!doc.hasIn(['gateway', 'paths']))
|
|
74
|
+
(0, yaml_config_util_1.setIn)(doc, ['gateway', 'paths'], []);
|
|
75
|
+
const paths = [...(r.acl ? (0, paths_1.aclManagementPaths)() : []), ...(r.anyAdmin ? (0, paths_1.adminManagementPaths)(r.controlTopic) : [])];
|
|
76
|
+
for (const p of paths)
|
|
77
|
+
(0, yaml_config_util_1.upsertSeqItemByKey)(doc, ['gateway', 'paths'], 'name', p, opts);
|
|
78
|
+
(0, yaml_config_util_1.writeConfigDoc)(tree, configPath, doc);
|
|
79
|
+
(0, nest_wiring_util_1.createConfigLoader)(tree);
|
|
80
|
+
const modulePath = (0, nest_wiring_util_1.wireAppModule)(tree, {
|
|
81
|
+
libSymbols: (0, wiring_1.libSymbols)(r.features),
|
|
82
|
+
importLines: (0, wiring_1.importLines)(r.features),
|
|
83
|
+
entries: (0, wiring_1.moduleEntries)(r.features),
|
|
84
|
+
});
|
|
85
|
+
if (!modulePath) {
|
|
86
|
+
context.logger.warn('[rlb-amqp] app.module.ts not found — module wiring skipped. Wire Broker/Proxy/Http' + (r.acl ? '/Acl' : '') + (r.anyAdmin ? '/GatewayAdmin' : '') + ' modules manually.');
|
|
87
|
+
}
|
|
88
|
+
if (!(0, nest_wiring_util_1.configureMainForGateway)(tree)) {
|
|
89
|
+
context.logger.warn('[rlb-amqp] main.ts not found — enable rawBody + WsAdapter manually.');
|
|
90
|
+
}
|
|
91
|
+
(0, nest_wiring_util_1.addDeps)(tree, {
|
|
92
|
+
'@nestjs/config': '^4.0.4',
|
|
93
|
+
'js-yaml': '^4.1.0',
|
|
94
|
+
'@nestjs/axios': '^4.0.1',
|
|
95
|
+
'@nestjs/platform-ws': '^11.0.1',
|
|
96
|
+
'@nestjs/websockets': '^11.0.1',
|
|
97
|
+
ws: '^8.21.0',
|
|
98
|
+
});
|
|
99
|
+
return tree;
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function copyAsset(name) {
|
|
103
|
+
return (0, schematics_1.mergeWith)((0, schematics_1.apply)((0, schematics_1.url)(`./files/${name}`), [(0, schematics_1.move)((0, path_1.normalize)('.'))]), schematics_1.MergeStrategy.Overwrite);
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../libs/rlb-nestjs-amqp/src/schematics/add-gateway/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
|
|
2
|
+
import { parseDocument } from 'yaml';
|
|
3
|
+
import { configAndWireRule } from './index';
|
|
4
|
+
import { AddGatewayOptions } from './schema';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Tests target `configAndWireRule` (config.yaml + TS wiring) directly against a Tree. The full `main`
|
|
8
|
+
* additionally copies in-memory-repo assets via url()/mergeWith, which needs a real engine context —
|
|
9
|
+
* that path is covered by the end-to-end CLI run, not this unit test.
|
|
10
|
+
*/
|
|
11
|
+
const ctx = { logger: { info() {}, warn() {}, error() {} } } as unknown as SchematicContext;
|
|
12
|
+
|
|
13
|
+
async function run(options: AddGatewayOptions, tree: Tree): Promise<Tree> {
|
|
14
|
+
const rule = configAndWireRule(options) as (t: Tree, c: SchematicContext) => Promise<Tree>;
|
|
15
|
+
return (await rule(tree, ctx)) || tree;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function read(tree: Tree, path = 'config/config.yaml'): string {
|
|
19
|
+
return tree.read(path)!.toString('utf-8');
|
|
20
|
+
}
|
|
21
|
+
const doc = (tree: Tree) => parseDocument(read(tree));
|
|
22
|
+
const topicNames = (tree: Tree): string[] =>
|
|
23
|
+
((doc(tree).getIn(['topics']) as any)?.items || []).map((i: any) => String(i.get('name')));
|
|
24
|
+
const queueNames = (tree: Tree): string[] =>
|
|
25
|
+
((doc(tree).getIn(['broker', 'queues']) as any)?.items || []).map((i: any) => String(i.get('name')));
|
|
26
|
+
const pathNames = (tree: Tree): string[] =>
|
|
27
|
+
((doc(tree).getIn(['gateway', 'paths']) as any)?.items || []).map((i: any) => String(i.get('name')));
|
|
28
|
+
|
|
29
|
+
function seedMicroservice(): Tree {
|
|
30
|
+
const tree = Tree.empty();
|
|
31
|
+
tree.create(
|
|
32
|
+
'config/config.yaml',
|
|
33
|
+
[
|
|
34
|
+
'# my service',
|
|
35
|
+
'broker:',
|
|
36
|
+
' routeDiscovery:',
|
|
37
|
+
' serviceName: "svc"',
|
|
38
|
+
' exchange: rlb-route-discovery',
|
|
39
|
+
' queue: rlb-route-sync',
|
|
40
|
+
' exchanges: []',
|
|
41
|
+
' queues: []',
|
|
42
|
+
'topics: []',
|
|
43
|
+
'',
|
|
44
|
+
].join('\n'),
|
|
45
|
+
);
|
|
46
|
+
return tree;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const APP_MODULE = `import { Module } from '@nestjs/common';
|
|
50
|
+
|
|
51
|
+
@Module({ imports: [] })
|
|
52
|
+
export class AppModule {}
|
|
53
|
+
`;
|
|
54
|
+
const MAIN_TS = `import { NestFactory } from '@nestjs/core';
|
|
55
|
+
import { AppModule } from './app.module';
|
|
56
|
+
|
|
57
|
+
async function bootstrap() {
|
|
58
|
+
const app = await NestFactory.create(AppModule);
|
|
59
|
+
await app.listen(3000);
|
|
60
|
+
}
|
|
61
|
+
bootstrap();
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
function seedProject(): Tree {
|
|
65
|
+
const tree = seedMicroservice();
|
|
66
|
+
tree.create('src/app.module.ts', APP_MODULE);
|
|
67
|
+
tree.create('src/main.ts', MAIN_TS);
|
|
68
|
+
tree.create('package.json', JSON.stringify({ name: 'demo', dependencies: {} }, null, 2));
|
|
69
|
+
return tree;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
describe('add-gateway schematic', () => {
|
|
73
|
+
describe('config.yaml', () => {
|
|
74
|
+
it('removes publisher route-discovery and declares the fixed acl/admin/control topics', async () => {
|
|
75
|
+
const tree = await run({}, seedMicroservice());
|
|
76
|
+
const d = doc(tree);
|
|
77
|
+
expect(d.hasIn(['broker', 'routeDiscovery'])).toBe(false);
|
|
78
|
+
expect(topicNames(tree)).toEqual(expect.arrayContaining(['rlb-acl', 'rlb-gateway-admin', 'rlb-gateway-control']));
|
|
79
|
+
const byName = (n: string) => (d.getIn(['topics']) as any).items.find((i: any) => String(i.get('name')) === n);
|
|
80
|
+
expect(String(byName('rlb-gateway-control').get('mode'))).toBe('broadcast');
|
|
81
|
+
expect(queueNames(tree)).toEqual(expect.arrayContaining(['rlb-acl', 'rlb-gateway-admin', 'rlb-route-sync']));
|
|
82
|
+
expect(String(d.getIn(['gateway', 'reloadTopic']))).toBe('rlb-gateway-control');
|
|
83
|
+
expect(read(tree)).toContain('# my service');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('seeds the ACL + admin management routes into gateway.paths', async () => {
|
|
87
|
+
const tree = await run({}, seedMicroservice());
|
|
88
|
+
const names = pathNames(tree);
|
|
89
|
+
expect(names).toEqual(expect.arrayContaining(['acl-check-action', 'acl-grant', 'gw-path-list', 'gw-reload', 'health']));
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('does not clobber a pre-existing gateway section', async () => {
|
|
93
|
+
const tree = seedMicroservice();
|
|
94
|
+
tree.overwrite(
|
|
95
|
+
'config/config.yaml',
|
|
96
|
+
read(tree) + ['gateway:', ' ws:', ' heartbeatIntervalMs: 9999', ' reloadTopic: my-custom-topic', ''].join('\n'),
|
|
97
|
+
);
|
|
98
|
+
const d = doc(await run({}, tree));
|
|
99
|
+
expect(String(d.getIn(['gateway', 'ws', 'heartbeatIntervalMs']))).toBe('9999');
|
|
100
|
+
expect(String(d.getIn(['gateway', 'reloadTopic']))).toBe('my-custom-topic');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('is idempotent: re-running duplicates neither topics nor management routes', async () => {
|
|
104
|
+
let tree = await run({}, seedMicroservice());
|
|
105
|
+
tree = await run({}, tree);
|
|
106
|
+
expect(topicNames(tree).filter((n) => n === 'rlb-acl')).toHaveLength(1);
|
|
107
|
+
expect(pathNames(tree).filter((n) => n === 'acl-check-action')).toHaveLength(1);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('respects feature toggles (acl/routeReception off)', async () => {
|
|
111
|
+
const tree = await run({ acl: false, routeReception: false }, seedMicroservice());
|
|
112
|
+
expect(topicNames(tree)).not.toContain('rlb-acl');
|
|
113
|
+
expect(pathNames(tree)).not.toContain('acl-check-action');
|
|
114
|
+
expect(topicNames(tree)).toContain('rlb-gateway-admin');
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('TypeScript wiring', () => {
|
|
119
|
+
it('wires the modules into AppModule and merges the lib import', async () => {
|
|
120
|
+
const tree = await run({}, seedProject());
|
|
121
|
+
const mod = tree.read('src/app.module.ts')!.toString('utf-8');
|
|
122
|
+
expect(mod).toContain('BrokerModule.forRootAsync');
|
|
123
|
+
expect(mod).toContain('ProxyModule.forRootAsync');
|
|
124
|
+
expect(mod).toContain('AclModule.forRoot');
|
|
125
|
+
expect(mod).toContain('GatewayAdminModule.forRoot');
|
|
126
|
+
expect(mod).toContain('HttpModule');
|
|
127
|
+
expect(mod).toContain(`from '@open-rlb/nestjs-amqp'`);
|
|
128
|
+
expect(mod).toContain(`from '@nestjs/config'`);
|
|
129
|
+
expect(tree.exists('src/config/config.loader.ts')).toBe(true);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('patches main.ts (rawBody + WsAdapter) and package.json deps', async () => {
|
|
133
|
+
const tree = await run({}, seedProject());
|
|
134
|
+
const main = tree.read('src/main.ts')!.toString('utf-8');
|
|
135
|
+
expect(main).toContain('rawBody: true');
|
|
136
|
+
expect(main).toContain('useWebSocketAdapter(new WsAdapter');
|
|
137
|
+
const pkg = JSON.parse(tree.read('package.json')!.toString('utf-8'));
|
|
138
|
+
expect(pkg.dependencies['@nestjs/axios']).toBeDefined();
|
|
139
|
+
expect(pkg.dependencies['@nestjs/platform-ws']).toBeDefined();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('is idempotent: re-running does not duplicate the module entries', async () => {
|
|
143
|
+
let tree = await run({}, seedProject());
|
|
144
|
+
tree = await run({}, tree);
|
|
145
|
+
const mod = tree.read('src/app.module.ts')!.toString('utf-8');
|
|
146
|
+
expect(mod.match(/ProxyModule\.forRootAsync/g)).toHaveLength(1);
|
|
147
|
+
expect(mod.match(/BrokerModule\.forRootAsync/g)).toHaveLength(1);
|
|
148
|
+
expect(mod.match(/AclModule\.forRoot/g)).toHaveLength(1);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('merges into an existing lib import without duplicating it', async () => {
|
|
152
|
+
const tree = seedProject();
|
|
153
|
+
tree.overwrite(
|
|
154
|
+
'src/app.module.ts',
|
|
155
|
+
`import { Module } from '@nestjs/common';\nimport { BrokerModule } from '@open-rlb/nestjs-amqp';\n\n@Module({ imports: [] })\nexport class AppModule {}\n`,
|
|
156
|
+
);
|
|
157
|
+
const mod = (await run({}, tree)).read('src/app.module.ts')!.toString('utf-8');
|
|
158
|
+
expect(mod.match(/from '@open-rlb\/nestjs-amqp'/g)).toHaveLength(1);
|
|
159
|
+
expect(mod).toContain('ProxyModule');
|
|
160
|
+
expect(mod).toContain('AclModule');
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
});
|