@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,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/schema",
|
|
3
|
+
"$id": "SchematicsRlbAmqpAddQueue",
|
|
4
|
+
"title": "Add a broker queue to config.yaml",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"name": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"description": "Queue name (the key).",
|
|
10
|
+
"x-prompt": "Queue name?",
|
|
11
|
+
"$default": { "$source": "argv", "index": 0 }
|
|
12
|
+
},
|
|
13
|
+
"exchange": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"default": "rlb",
|
|
16
|
+
"description": "Exchange the queue binds to."
|
|
17
|
+
},
|
|
18
|
+
"routingKey": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"description": "routingKey (required — defaulted to the queue name — when the exchange is a topic)."
|
|
21
|
+
},
|
|
22
|
+
"createIfNotExists": {
|
|
23
|
+
"type": "boolean",
|
|
24
|
+
"default": true,
|
|
25
|
+
"description": "Emit createQueueIfNotExists (false => the queue must pre-exist)."
|
|
26
|
+
},
|
|
27
|
+
"durable": { "type": "boolean", "default": true, "description": "options.durable" },
|
|
28
|
+
"exclusive": { "type": "boolean", "default": false, "description": "options.exclusive" },
|
|
29
|
+
"autoDelete": { "type": "boolean", "default": false, "description": "options.autoDelete" },
|
|
30
|
+
"messageTtl": { "type": "number", "description": "options.messageTtl (ms)." },
|
|
31
|
+
"maxLength": { "type": "number", "description": "options.maxLength." },
|
|
32
|
+
"expires": { "type": "number", "description": "options.expires (ms) — unused-queue TTL." },
|
|
33
|
+
"createExchange": {
|
|
34
|
+
"type": "boolean",
|
|
35
|
+
"description": "Create the exchange when it is missing (default: false interactive, true when passed)."
|
|
36
|
+
},
|
|
37
|
+
"exchangeType": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"enum": ["direct", "topic", "fanout", "headers"],
|
|
40
|
+
"default": "direct",
|
|
41
|
+
"description": "Type of the exchange created via createExchange."
|
|
42
|
+
},
|
|
43
|
+
"overwrite": {
|
|
44
|
+
"type": "boolean",
|
|
45
|
+
"default": false,
|
|
46
|
+
"description": "Update the entry if it already exists (default: leave it untouched)."
|
|
47
|
+
},
|
|
48
|
+
"config": {
|
|
49
|
+
"type": "string",
|
|
50
|
+
"description": "Path to config.yaml (default: auto-detected)."
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"required": []
|
|
54
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ExchangeType } from '../utils/broker-yaml.util';
|
|
2
|
+
|
|
3
|
+
export interface AddQueueOptions {
|
|
4
|
+
/** Queue name (the key). Prompted when omitted. */
|
|
5
|
+
name?: string;
|
|
6
|
+
/** Exchange the queue binds to. Default: 'rlb'. */
|
|
7
|
+
exchange?: string;
|
|
8
|
+
/** routingKey (required — and defaulted to the queue name — when the exchange is a topic). */
|
|
9
|
+
routingKey?: string;
|
|
10
|
+
/** Emit `createQueueIfNotExists`. Default: true. */
|
|
11
|
+
createIfNotExists?: boolean;
|
|
12
|
+
/** options.durable. Default: true. */
|
|
13
|
+
durable?: boolean;
|
|
14
|
+
/** options.exclusive. Default: false. */
|
|
15
|
+
exclusive?: boolean;
|
|
16
|
+
/** options.autoDelete. Default: false. */
|
|
17
|
+
autoDelete?: boolean;
|
|
18
|
+
/** options.messageTtl (ms) — growth bound. */
|
|
19
|
+
messageTtl?: number;
|
|
20
|
+
/** options.maxLength — growth bound. */
|
|
21
|
+
maxLength?: number;
|
|
22
|
+
/** options.expires (ms) — unused-queue TTL. */
|
|
23
|
+
expires?: number;
|
|
24
|
+
/** Create the exchange when it is missing (default: prompt→false, or true when the flag is passed). */
|
|
25
|
+
createExchange?: boolean;
|
|
26
|
+
/** Type of the exchange created via createExchange. Default: 'direct'. */
|
|
27
|
+
exchangeType?: ExchangeType;
|
|
28
|
+
/** Update the entry when it already exists (default: leave it untouched). */
|
|
29
|
+
overwrite?: boolean;
|
|
30
|
+
/** Path to config.yaml (default: auto-detected, typically config/config.yaml). */
|
|
31
|
+
config?: string;
|
|
32
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
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.exchange !== undefined;
|
|
9
|
+
const prompts = (0, schematic_prompt_util_1.loadPrompts)(context, flagsProvided);
|
|
10
|
+
const exchange = options.exchange || (await (0, schematic_prompt_util_1.askText)(prompts, 'Exchange?', ''));
|
|
11
|
+
const queue = options.queue || (await (0, schematic_prompt_util_1.askText)(prompts, 'Reply queue name?', ''));
|
|
12
|
+
if (!exchange || !queue) {
|
|
13
|
+
context.logger.error('[rlb-amqp] add-reply-queue: both exchange and queue are required.');
|
|
14
|
+
return tree;
|
|
15
|
+
}
|
|
16
|
+
const configPath = (0, yaml_config_util_1.findConfigYaml)(tree, options.config);
|
|
17
|
+
const { doc, existed } = (0, yaml_config_util_1.readConfigDoc)(tree, configPath);
|
|
18
|
+
if (!existed)
|
|
19
|
+
context.logger.info(`[rlb-amqp] ${configPath} not found — creating it.`);
|
|
20
|
+
const outcome = (0, yaml_config_util_1.setIn)(doc, ['broker', 'replyQueues', exchange], queue);
|
|
21
|
+
(0, schematic_prompt_util_1.logOutcome)(context, `reply queue for '${exchange}'`, outcome);
|
|
22
|
+
(0, yaml_config_util_1.writeConfigDoc)(tree, configPath, doc);
|
|
23
|
+
return tree;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../libs/rlb-nestjs-amqp/src/schematics/add-reply-queue/index.ts"],"names":[],"mappings":";;AAUA,oBAsBC;AA/BD,0EAAkF;AAClF,gEAAiG;AAQjG,SAAgB,IAAI,CAAC,OAA6B;IAChD,OAAO,KAAK,EAAE,IAAU,EAAE,OAAyB,EAAE,EAAE;QACrD,MAAM,aAAa,GAAG,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC;QACrD,MAAM,OAAO,GAAG,IAAA,mCAAW,EAAC,OAAO,EAAE,aAAa,CAAC,CAAC;QAEpD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,CAAC,MAAM,IAAA,+BAAO,EAAC,OAAO,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC;QAC/E,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,MAAM,IAAA,+BAAO,EAAC,OAAO,EAAE,mBAAmB,EAAE,EAAE,CAAC,CAAC,CAAC;QACjF,IAAI,CAAC,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC;YACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mEAAmE,CAAC,CAAC;YAC1F,OAAO,IAAI,CAAC;QACd,CAAC;QAED,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,OAAO,GAAG,IAAA,wBAAK,EAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,KAAK,CAAC,CAAC;QACvE,IAAA,kCAAU,EAAC,OAAO,EAAE,oBAAoB,QAAQ,GAAG,EAAE,OAAO,CAAC,CAAC;QAE9D,IAAA,iCAAc,EAAC,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;QACtC,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
|
|
2
|
+
import { parseDocument } from 'yaml';
|
|
3
|
+
import { main } from './index';
|
|
4
|
+
import { AddReplyQueueOptions } 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 `exchange` 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: AddReplyQueueOptions, 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-reply-queue schematic', () => {
|
|
35
|
+
it('sets the exchange → reply queue mapping, preserving comments', async () => {
|
|
36
|
+
const tree = await run({ exchange: 'rlb', queue: 'rlb-reply' }, seedConfig());
|
|
37
|
+
expect(String(doc(tree).getIn(['broker', 'replyQueues', 'rlb']))).toBe('rlb-reply');
|
|
38
|
+
expect(read(tree)).toContain('# my config');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('is idempotent: re-adding the same mapping leaves a single key', async () => {
|
|
42
|
+
let tree = await run({ exchange: 'rlb', queue: 'rlb-reply' }, seedConfig());
|
|
43
|
+
tree = await run({ exchange: 'rlb', queue: 'rlb-reply' }, tree);
|
|
44
|
+
const replyQueues = doc(tree).getIn(['broker', 'replyQueues']) as any;
|
|
45
|
+
expect(replyQueues.items).toHaveLength(1);
|
|
46
|
+
expect(String(doc(tree).getIn(['broker', 'replyQueues', 'rlb']))).toBe('rlb-reply');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('updates the queue for an existing exchange key', async () => {
|
|
50
|
+
let tree = await run({ exchange: 'rlb', queue: 'rlb-reply' }, seedConfig());
|
|
51
|
+
tree = await run({ exchange: 'rlb', queue: 'rlb-reply-2' }, tree);
|
|
52
|
+
const replyQueues = doc(tree).getIn(['broker', 'replyQueues']) as any;
|
|
53
|
+
expect(replyQueues.items).toHaveLength(1);
|
|
54
|
+
expect(String(doc(tree).getIn(['broker', 'replyQueues', 'rlb']))).toBe('rlb-reply-2');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('supports multiple exchanges in the map', async () => {
|
|
58
|
+
let tree = await run({ exchange: 'rlb', queue: 'rlb-reply' }, seedConfig());
|
|
59
|
+
tree = await run({ exchange: 'events', queue: 'events-reply' }, tree);
|
|
60
|
+
expect(String(doc(tree).getIn(['broker', 'replyQueues', 'rlb']))).toBe('rlb-reply');
|
|
61
|
+
expect(String(doc(tree).getIn(['broker', 'replyQueues', 'events']))).toBe('events-reply');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('creates config.yaml when it does not exist', async () => {
|
|
65
|
+
const tree = await run({ exchange: 'rlb', queue: 'rlb-reply' }, Tree.empty());
|
|
66
|
+
expect(tree.exists('config/config.yaml')).toBe(true);
|
|
67
|
+
expect(String(doc(tree).getIn(['broker', 'replyQueues', 'rlb']))).toBe('rlb-reply');
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
|
|
2
|
+
import { askText, loadPrompts, logOutcome } from '../utils/schematic-prompt.util';
|
|
3
|
+
import { findConfigYaml, readConfigDoc, setIn, writeConfigDoc } from '../utils/yaml-config.util';
|
|
4
|
+
import { AddReplyQueueOptions } from './schema';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `add-reply-queue` — idempotent set of a `broker.replyQueues[exchange] = queue` mapping,
|
|
8
|
+
* comment-preserving. Unlike exchanges/queues/topics this section is a MAP (exchange → reply queue),
|
|
9
|
+
* not a seq, so we go straight through the generic setIn helper, which reports created/updated/unchanged.
|
|
10
|
+
*/
|
|
11
|
+
export function main(options: AddReplyQueueOptions): Rule {
|
|
12
|
+
return async (tree: Tree, context: SchematicContext) => {
|
|
13
|
+
const flagsProvided = options.exchange !== undefined;
|
|
14
|
+
const prompts = loadPrompts(context, flagsProvided);
|
|
15
|
+
|
|
16
|
+
const exchange = options.exchange || (await askText(prompts, 'Exchange?', ''));
|
|
17
|
+
const queue = options.queue || (await askText(prompts, 'Reply queue name?', ''));
|
|
18
|
+
if (!exchange || !queue) {
|
|
19
|
+
context.logger.error('[rlb-amqp] add-reply-queue: both exchange and queue are required.');
|
|
20
|
+
return tree;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const configPath = findConfigYaml(tree, options.config);
|
|
24
|
+
const { doc, existed } = readConfigDoc(tree, configPath);
|
|
25
|
+
if (!existed) context.logger.info(`[rlb-amqp] ${configPath} not found — creating it.`);
|
|
26
|
+
|
|
27
|
+
const outcome = setIn(doc, ['broker', 'replyQueues', exchange], queue);
|
|
28
|
+
logOutcome(context, `reply queue for '${exchange}'`, outcome);
|
|
29
|
+
|
|
30
|
+
writeConfigDoc(tree, configPath, doc);
|
|
31
|
+
return tree;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../libs/rlb-nestjs-amqp/src/schematics/add-reply-queue/schema.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/schema",
|
|
3
|
+
"$id": "SchematicsRlbAmqpAddReplyQueue",
|
|
4
|
+
"title": "Map an exchange to an RPC reply queue via broker.replyQueues",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"exchange": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"description": "Exchange whose RPC replies route to `queue` (the map key).",
|
|
10
|
+
"x-prompt": "Exchange?",
|
|
11
|
+
"$default": { "$source": "argv", "index": 0 }
|
|
12
|
+
},
|
|
13
|
+
"queue": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "The reply queue name (the map value)."
|
|
16
|
+
},
|
|
17
|
+
"overwrite": {
|
|
18
|
+
"type": "boolean",
|
|
19
|
+
"default": false,
|
|
20
|
+
"description": "Overwrite an existing mapping for this exchange."
|
|
21
|
+
},
|
|
22
|
+
"config": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"description": "Path to config.yaml (default: auto-detected)."
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"required": []
|
|
28
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface AddReplyQueueOptions {
|
|
2
|
+
/** Exchange whose RPC replies route to `queue` (the map key). Prompted when omitted. */
|
|
3
|
+
exchange?: string;
|
|
4
|
+
/** The reply queue name (the map value). Prompted when omitted. */
|
|
5
|
+
queue?: string;
|
|
6
|
+
/** Overwrite an existing mapping for this exchange (setIn is idempotent regardless). */
|
|
7
|
+
overwrite?: boolean;
|
|
8
|
+
/** Path to config.yaml (default: auto-detected, typically config/config.yaml). */
|
|
9
|
+
config?: string;
|
|
10
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
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 METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'];
|
|
7
|
+
const MODES = ['rpc', 'event'];
|
|
8
|
+
function main(options) {
|
|
9
|
+
return async (tree, context) => {
|
|
10
|
+
const flagsProvided = options.name !== undefined;
|
|
11
|
+
const prompts = (0, schematic_prompt_util_1.loadPrompts)(context, flagsProvided);
|
|
12
|
+
const name = options.name || (await (0, schematic_prompt_util_1.askText)(prompts, 'Route name?', ''));
|
|
13
|
+
if (!name) {
|
|
14
|
+
context.logger.error('[rlb-amqp] add-route: a name is required.');
|
|
15
|
+
return tree;
|
|
16
|
+
}
|
|
17
|
+
const method = options.method || (await (0, schematic_prompt_util_1.askSelect)(prompts, 'HTTP method?', METHODS, 'GET'));
|
|
18
|
+
const path = options.path || (await (0, schematic_prompt_util_1.askText)(prompts, 'URL path?', ''));
|
|
19
|
+
const topic = options.topic || (await (0, schematic_prompt_util_1.askText)(prompts, 'Topic?', ''));
|
|
20
|
+
const action = options.action || (await (0, schematic_prompt_util_1.askText)(prompts, 'Action?', ''));
|
|
21
|
+
const mode = options.mode || (await (0, schematic_prompt_util_1.askSelect)(prompts, 'Mode?', MODES, 'rpc'));
|
|
22
|
+
const dataSource = options.dataSource || (method === 'GET' ? 'query' : 'body');
|
|
23
|
+
const configPath = (0, yaml_config_util_1.findConfigYaml)(tree, options.config);
|
|
24
|
+
const { doc, existed } = (0, yaml_config_util_1.readConfigDoc)(tree, configPath);
|
|
25
|
+
if (!existed)
|
|
26
|
+
context.logger.info(`[rlb-amqp] ${configPath} not found — creating it.`);
|
|
27
|
+
const item = {
|
|
28
|
+
name,
|
|
29
|
+
method,
|
|
30
|
+
path,
|
|
31
|
+
dataSource,
|
|
32
|
+
topic,
|
|
33
|
+
action,
|
|
34
|
+
mode,
|
|
35
|
+
timeout: options.timeout,
|
|
36
|
+
auth: options.auth,
|
|
37
|
+
allowAnonymous: options.allowAnonymous,
|
|
38
|
+
actions: options.actions,
|
|
39
|
+
successStatusCode: options.successStatusCode,
|
|
40
|
+
binary: options.binary,
|
|
41
|
+
redirect: options.redirect,
|
|
42
|
+
parseRaw: options.parseRaw,
|
|
43
|
+
};
|
|
44
|
+
const hasActions = options.actions !== undefined && (!Array.isArray(options.actions) || options.actions.length > 0);
|
|
45
|
+
if (hasActions && !options.auth && !options.allowAnonymous) {
|
|
46
|
+
context.logger.warn('[rlb-amqp] add-route: actions require auth on the same route (fails closed → every request 403).');
|
|
47
|
+
}
|
|
48
|
+
if (options.parseRaw) {
|
|
49
|
+
context.logger.warn('[rlb-amqp] add-route: parseRaw needs rawBody:true at bootstrap (NestFactory.create({rawBody:true})).');
|
|
50
|
+
}
|
|
51
|
+
const outcome = (0, yaml_config_util_1.upsertSeqItemByKey)(doc, ['gateway', 'paths'], 'name', item, { overwrite: options.overwrite });
|
|
52
|
+
(0, schematic_prompt_util_1.logOutcome)(context, `route '${name}'`, outcome);
|
|
53
|
+
(0, yaml_config_util_1.writeConfigDoc)(tree, configPath, doc);
|
|
54
|
+
return tree;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../libs/rlb-nestjs-amqp/src/schematics/add-route/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
|
|
2
|
+
import { parseDocument } from 'yaml';
|
|
3
|
+
import { main } from './index';
|
|
4
|
+
import { AddRouteOptions } from './schema';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Schematics are tested by invoking the factory Rule directly against a Tree, NOT via
|
|
8
|
+
* SchematicTestRunner (its testing entrypoint eagerly loads `ora`, an ESM-only module jest cannot
|
|
9
|
+
* transform here). Passing `name` makes `flagsProvided` true, so no interactive prompt is attempted.
|
|
10
|
+
*/
|
|
11
|
+
function makeCtx(warnings: string[] = []): SchematicContext {
|
|
12
|
+
return { logger: { info() {}, warn: (m: string) => warnings.push(m), error() {} } } as unknown as SchematicContext;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function run(options: AddRouteOptions, tree: Tree, ctx = makeCtx()): Promise<Tree> {
|
|
16
|
+
const rule = main(options) as Rule;
|
|
17
|
+
const result = await (rule as (t: Tree, c: SchematicContext) => Promise<Tree>)(tree, ctx);
|
|
18
|
+
return result || tree;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function read(tree: Tree, path = 'config/config.yaml'): string {
|
|
22
|
+
return tree.read(path)!.toString('utf-8');
|
|
23
|
+
}
|
|
24
|
+
function doc(tree: Tree) {
|
|
25
|
+
return parseDocument(read(tree));
|
|
26
|
+
}
|
|
27
|
+
function seedConfig(): Tree {
|
|
28
|
+
const tree = Tree.empty();
|
|
29
|
+
tree.create(
|
|
30
|
+
'config/config.yaml',
|
|
31
|
+
['# my config', 'gateway:', ' paths:', ' - name: health', ' method: GET', ' path: /health', ''].join(
|
|
32
|
+
'\n',
|
|
33
|
+
),
|
|
34
|
+
);
|
|
35
|
+
return tree;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe('add-route schematic', () => {
|
|
39
|
+
it('appends a POST route defaulting dataSource to body, preserving comments', async () => {
|
|
40
|
+
const tree = await run({ name: 'create-order', method: 'POST', path: '/orders', topic: 'orders', action: 'create' }, seedConfig());
|
|
41
|
+
const paths = doc(tree).getIn(['gateway', 'paths']) as any;
|
|
42
|
+
expect(paths.items.map((i: any) => String(i.get('name')))).toEqual(['health', 'create-order']);
|
|
43
|
+
const route = paths.items[1];
|
|
44
|
+
expect(String(route.get('dataSource'))).toBe('body');
|
|
45
|
+
expect(String(route.get('mode'))).toBe('rpc');
|
|
46
|
+
expect(String(route.get('topic'))).toBe('orders');
|
|
47
|
+
expect(read(tree)).toContain('# my config');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('defaults GET routes to dataSource query and is idempotent', async () => {
|
|
51
|
+
let tree = await run({ name: 'get-order', method: 'GET', path: '/orders/:id', topic: 'orders', action: 'get' }, seedConfig());
|
|
52
|
+
const route = (doc(tree).getIn(['gateway', 'paths']) as any).items[1];
|
|
53
|
+
expect(String(route.get('dataSource'))).toBe('query');
|
|
54
|
+
tree = await run({ name: 'get-order', method: 'GET', path: '/orders/:id', topic: 'orders', action: 'get' }, tree);
|
|
55
|
+
const names = (doc(tree).getIn(['gateway', 'paths']) as any).items.map((i: any) => String(i.get('name')));
|
|
56
|
+
expect(names.filter((n: string) => n === 'get-order')).toHaveLength(1);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('warns when actions are set without auth (ACL fails closed → 403)', async () => {
|
|
60
|
+
const warnings: string[] = [];
|
|
61
|
+
await run(
|
|
62
|
+
{ name: 'gated', method: 'GET', path: '/x', topic: 't', action: 'a', actions: ['gateway-access'] },
|
|
63
|
+
seedConfig(),
|
|
64
|
+
makeCtx(warnings),
|
|
65
|
+
);
|
|
66
|
+
expect(warnings.some((w) => w.includes('actions require auth'))).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('creates config.yaml when it does not exist', async () => {
|
|
70
|
+
const tree = await run({ name: 'health', method: 'GET', path: '/health', topic: 'admin', action: 'gw-health' }, Tree.empty());
|
|
71
|
+
expect(tree.exists('config/config.yaml')).toBe(true);
|
|
72
|
+
const route = (doc(tree).getIn(['gateway', 'paths']) as any).items[0];
|
|
73
|
+
expect(String(route.get('name'))).toBe('health');
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
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 { AddRouteOptions, RouteDataSource, RouteMethod, RouteMode } from './schema';
|
|
5
|
+
|
|
6
|
+
const METHODS: readonly RouteMethod[] = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'];
|
|
7
|
+
const MODES: readonly RouteMode[] = ['rpc', 'event'];
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* `add-route <name>` — idempotent upsert of a `gateway.paths[]` entry, comment-preserving.
|
|
11
|
+
* Maps an HTTP method+path onto a broker topic/action. Only defined fields are written, so a route
|
|
12
|
+
* stays as terse as the sample configs. `dataSource` defaults to the natural source for the method.
|
|
13
|
+
*/
|
|
14
|
+
export function main(options: AddRouteOptions): 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, 'Route name?', ''));
|
|
20
|
+
if (!name) {
|
|
21
|
+
context.logger.error('[rlb-amqp] add-route: a name is required.');
|
|
22
|
+
return tree;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const method = (options.method as RouteMethod) || (await askSelect(prompts, 'HTTP method?', METHODS, 'GET'));
|
|
26
|
+
const path = options.path || (await askText(prompts, 'URL path?', ''));
|
|
27
|
+
const topic = options.topic || (await askText(prompts, 'Topic?', ''));
|
|
28
|
+
const action = options.action || (await askText(prompts, 'Action?', ''));
|
|
29
|
+
const mode = (options.mode as RouteMode) || (await askSelect(prompts, 'Mode?', MODES, 'rpc'));
|
|
30
|
+
// GET has no body, so read from the query string; everything else defaults to the JSON body.
|
|
31
|
+
const dataSource: RouteDataSource = options.dataSource || (method === 'GET' ? 'query' : 'body');
|
|
32
|
+
|
|
33
|
+
const configPath = findConfigYaml(tree, options.config);
|
|
34
|
+
const { doc, existed } = readConfigDoc(tree, configPath);
|
|
35
|
+
if (!existed) context.logger.info(`[rlb-amqp] ${configPath} not found — creating it.`);
|
|
36
|
+
|
|
37
|
+
const item: Record<string, unknown> = {
|
|
38
|
+
name,
|
|
39
|
+
method,
|
|
40
|
+
path,
|
|
41
|
+
dataSource,
|
|
42
|
+
topic,
|
|
43
|
+
action,
|
|
44
|
+
mode,
|
|
45
|
+
timeout: options.timeout,
|
|
46
|
+
auth: options.auth,
|
|
47
|
+
allowAnonymous: options.allowAnonymous,
|
|
48
|
+
actions: options.actions,
|
|
49
|
+
successStatusCode: options.successStatusCode,
|
|
50
|
+
binary: options.binary,
|
|
51
|
+
redirect: options.redirect,
|
|
52
|
+
parseRaw: options.parseRaw,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// The ACL gate fails closed: actions with no auth means no userId to check → every request 403s.
|
|
56
|
+
const hasActions = options.actions !== undefined && (!Array.isArray(options.actions) || options.actions.length > 0);
|
|
57
|
+
if (hasActions && !options.auth && !options.allowAnonymous) {
|
|
58
|
+
context.logger.warn(
|
|
59
|
+
'[rlb-amqp] add-route: actions require auth on the same route (fails closed → every request 403).',
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
if (options.parseRaw) {
|
|
63
|
+
context.logger.warn(
|
|
64
|
+
'[rlb-amqp] add-route: parseRaw needs rawBody:true at bootstrap (NestFactory.create({rawBody:true})).',
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const outcome = upsertSeqItemByKey(doc, ['gateway', 'paths'], 'name', item, { overwrite: options.overwrite });
|
|
69
|
+
logOutcome(context, `route '${name}'`, outcome);
|
|
70
|
+
|
|
71
|
+
writeConfigDoc(tree, configPath, doc);
|
|
72
|
+
return tree;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type RouteMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
2
|
+
export type RouteDataSource = 'body' | 'query' | 'params' | 'body-query' | 'query-body';
|
|
3
|
+
export type RouteMode = 'rpc' | 'event';
|
|
4
|
+
export interface AddRouteOptions {
|
|
5
|
+
name?: string;
|
|
6
|
+
method?: RouteMethod;
|
|
7
|
+
path?: string;
|
|
8
|
+
dataSource?: RouteDataSource;
|
|
9
|
+
topic?: string;
|
|
10
|
+
action?: string;
|
|
11
|
+
mode?: RouteMode;
|
|
12
|
+
timeout?: number;
|
|
13
|
+
auth?: string;
|
|
14
|
+
allowAnonymous?: boolean;
|
|
15
|
+
actions?: string | string[];
|
|
16
|
+
successStatusCode?: number;
|
|
17
|
+
binary?: boolean;
|
|
18
|
+
redirect?: number;
|
|
19
|
+
parseRaw?: boolean;
|
|
20
|
+
overwrite?: boolean;
|
|
21
|
+
config?: string;
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../libs/rlb-nestjs-amqp/src/schematics/add-route/schema.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/schema",
|
|
3
|
+
"$id": "SchematicsRlbAmqpAddRoute",
|
|
4
|
+
"title": "Add an HTTP route (gateway.paths[]) to config.yaml",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"name": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"description": "Route name (the key).",
|
|
10
|
+
"x-prompt": "Route name?",
|
|
11
|
+
"$default": { "$source": "argv", "index": 0 }
|
|
12
|
+
},
|
|
13
|
+
"method": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"enum": ["GET", "POST", "PUT", "DELETE", "PATCH"],
|
|
16
|
+
"default": "GET",
|
|
17
|
+
"description": "HTTP method."
|
|
18
|
+
},
|
|
19
|
+
"path": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": "URL path (e.g. /orders/:id).",
|
|
22
|
+
"x-prompt": "URL path?"
|
|
23
|
+
},
|
|
24
|
+
"dataSource": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"enum": ["body", "query", "params", "body-query", "query-body"],
|
|
27
|
+
"description": "Where to read the payload from (default: query for GET, body otherwise)."
|
|
28
|
+
},
|
|
29
|
+
"topic": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"description": "Broker topic the request forwards to.",
|
|
32
|
+
"x-prompt": "Topic?"
|
|
33
|
+
},
|
|
34
|
+
"action": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"description": "Broker action invoked on that topic.",
|
|
37
|
+
"x-prompt": "Action?"
|
|
38
|
+
},
|
|
39
|
+
"mode": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"enum": ["rpc", "event"],
|
|
42
|
+
"default": "rpc",
|
|
43
|
+
"description": "rpc (wait reply) | event (fire-and-forget)."
|
|
44
|
+
},
|
|
45
|
+
"timeout": { "type": "number", "description": "RPC reply timeout in ms." },
|
|
46
|
+
"auth": { "type": "string", "description": "Auth-provider name that must verify the caller." },
|
|
47
|
+
"allowAnonymous": {
|
|
48
|
+
"type": "boolean",
|
|
49
|
+
"description": "Allow anonymous callers even when auth is set."
|
|
50
|
+
},
|
|
51
|
+
"actions": {
|
|
52
|
+
"type": "array",
|
|
53
|
+
"items": { "type": "string" },
|
|
54
|
+
"description": "Actions the caller must hold (OR-semantics) — gated via the ACL. Requires auth."
|
|
55
|
+
},
|
|
56
|
+
"successStatusCode": { "type": "number", "description": "HTTP status returned on success." },
|
|
57
|
+
"binary": { "type": "boolean", "description": "Return the reply as a raw binary buffer." },
|
|
58
|
+
"redirect": { "type": "number", "description": "Respond with this redirect status instead of a body." },
|
|
59
|
+
"parseRaw": {
|
|
60
|
+
"type": "boolean",
|
|
61
|
+
"description": "Forward the raw (unparsed) request body — needs rawBody:true at bootstrap."
|
|
62
|
+
},
|
|
63
|
+
"overwrite": {
|
|
64
|
+
"type": "boolean",
|
|
65
|
+
"default": false,
|
|
66
|
+
"description": "Update the entry if it already exists (default: leave it untouched)."
|
|
67
|
+
},
|
|
68
|
+
"config": {
|
|
69
|
+
"type": "string",
|
|
70
|
+
"description": "Path to config.yaml (default: auto-detected)."
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"required": []
|
|
74
|
+
}
|