@open-rlb/nestjs-amqp 2.1.1 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/modules/acl/const.d.ts +2 -0
- package/modules/acl/const.js +2 -0
- package/modules/acl/const.js.map +1 -1
- package/modules/acl/services/acl-management.service.d.ts +4 -0
- package/modules/acl/services/acl-management.service.js +36 -3
- package/modules/acl/services/acl-management.service.js.map +1 -1
- 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,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
|
+
});
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { apply, chain, MergeStrategy, mergeWith, move, noop, Rule, SchematicContext, Tree, url } from '@angular-devkit/schematics';
|
|
2
|
+
import { normalize } from 'path';
|
|
3
|
+
import { ensureExchange, ensureQueue, ensureTopic } from '../utils/broker-yaml.util';
|
|
4
|
+
import { addDeps, configureMainForGateway, createConfigLoader, wireAppModule } from '../utils/nest-wiring.util';
|
|
5
|
+
import { findConfigYaml, readConfigDoc, setIn, upsertSeqItemByKey, writeConfigDoc } from '../utils/yaml-config.util';
|
|
6
|
+
import { aclManagementPaths, adminManagementPaths } from './paths';
|
|
7
|
+
import { AddGatewayOptions } from './schema';
|
|
8
|
+
import { GwFeatures, importLines, libSymbols, moduleEntries } from './wiring';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* `add-gateway` — promote a plain microservice into a working gateway. It edits config.yaml (drop
|
|
12
|
+
* publisher route-discovery; declare the acl/admin/control + route-sync infra; seed the `gateway:`
|
|
13
|
+
* section AND its management routes) AND wires the app: AppModule (Broker/Proxy/Http/Acl/GatewayAdmin
|
|
14
|
+
* modules), main.ts (rawBody + WsAdapter), the config loader, in-memory repositories, and package.json
|
|
15
|
+
* deps. Every step is idempotent, so re-running (or running on an already-partly-wired app) is safe.
|
|
16
|
+
*/
|
|
17
|
+
export function main(options: AddGatewayOptions): Rule {
|
|
18
|
+
const r = resolveFlags(options);
|
|
19
|
+
return chain([
|
|
20
|
+
// Config edits + TypeScript wiring (directly unit-testable — no engine/url source needed).
|
|
21
|
+
configAndWireRule(options),
|
|
22
|
+
// In-memory starter repositories referenced by the wiring (Overwrite = idempotent re-copy).
|
|
23
|
+
copyAsset('db-core'),
|
|
24
|
+
r.acl ? copyAsset('acl') : noop(),
|
|
25
|
+
r.anyAdmin ? copyAsset('gateway-admin') : noop(),
|
|
26
|
+
]);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface Resolved {
|
|
30
|
+
exchange: string;
|
|
31
|
+
aclQueue: string;
|
|
32
|
+
adminQueue: string;
|
|
33
|
+
controlTopic: string;
|
|
34
|
+
routeExchange: string;
|
|
35
|
+
routeQueue: string;
|
|
36
|
+
acl: boolean;
|
|
37
|
+
admin: boolean;
|
|
38
|
+
routeReception: boolean;
|
|
39
|
+
anyAdmin: boolean;
|
|
40
|
+
features: GwFeatures;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function resolveFlags(options: AddGatewayOptions): Resolved {
|
|
44
|
+
const exchange = options.exchange || 'rlb';
|
|
45
|
+
const aclQueue = options.aclQueue || 'rlb-acl';
|
|
46
|
+
const adminQueue = options.adminQueue || 'rlb-gateway-admin';
|
|
47
|
+
const controlTopic = options.controlTopic || 'rlb-gateway-control';
|
|
48
|
+
const routeExchange = options.routeExchange || 'rlb-route-discovery';
|
|
49
|
+
const routeQueue = options.routeQueue || 'rlb-route-sync';
|
|
50
|
+
const acl = options.acl ?? true;
|
|
51
|
+
const admin = options.admin ?? true;
|
|
52
|
+
const routeReception = options.routeReception ?? true;
|
|
53
|
+
const anyAdmin = admin || routeReception;
|
|
54
|
+
const features: GwFeatures = { acl, admin, routeReception, routeExchange, routeQueue };
|
|
55
|
+
return { exchange, aclQueue, adminQueue, controlTopic, routeExchange, routeQueue, acl, admin, routeReception, anyAdmin, features };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** The config.yaml edits + config-loader/AppModule/main.ts/package.json wiring — everything except the
|
|
59
|
+
* url()-based asset copy, so it can be invoked directly against a Tree in unit tests. */
|
|
60
|
+
export function configAndWireRule(options: AddGatewayOptions): Rule {
|
|
61
|
+
return (tree: Tree, context: SchematicContext) => {
|
|
62
|
+
const r = resolveFlags(options);
|
|
63
|
+
const opts = { overwrite: options.overwrite };
|
|
64
|
+
const configPath = findConfigYaml(tree, options.config);
|
|
65
|
+
const { doc, existed } = readConfigDoc(tree, configPath);
|
|
66
|
+
if (!existed) context.logger.info(`[rlb-amqp] ${configPath} not found — creating it.`);
|
|
67
|
+
|
|
68
|
+
// 1. A gateway consumes routes, it does not publish its own → drop publisher route-discovery.
|
|
69
|
+
if (doc.hasIn(['broker', 'routeDiscovery'])) {
|
|
70
|
+
doc.deleteIn(['broker', 'routeDiscovery']);
|
|
71
|
+
context.logger.info('[rlb-amqp] removed broker.routeDiscovery (a gateway consumes routes, it does not publish its own).');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 2. Main exchange backing the acl/admin queues.
|
|
75
|
+
ensureExchange(doc, { name: r.exchange, type: 'direct' }, opts);
|
|
76
|
+
|
|
77
|
+
// 3. ACL infra — topic name is FIXED 'rlb-acl' (decorator-bound in the lib).
|
|
78
|
+
if (r.acl) {
|
|
79
|
+
ensureQueue(doc, { name: r.aclQueue, exchange: r.exchange, routingKey: r.aclQueue, durable: true }, opts);
|
|
80
|
+
ensureTopic(doc, { name: 'rlb-acl', mode: 'rpc', queue: r.aclQueue, exchange: r.exchange, routingKey: r.aclQueue }, opts);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 4. Gateway-admin infra — 'rlb-gateway-admin' is decorator-bound; controlTopic is a broadcast.
|
|
84
|
+
if (r.anyAdmin) {
|
|
85
|
+
ensureQueue(doc, { name: r.adminQueue, exchange: r.exchange, routingKey: r.adminQueue, durable: true }, opts);
|
|
86
|
+
ensureTopic(doc, { name: 'rlb-gateway-admin', mode: 'rpc', queue: r.adminQueue, exchange: r.exchange, routingKey: r.adminQueue }, opts);
|
|
87
|
+
ensureTopic(doc, { name: r.controlTopic, mode: 'broadcast', exchange: r.exchange, routingKey: r.controlTopic }, opts);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// 5. Consumer-side route-sync infra (fanout exchange + durable sync queue).
|
|
91
|
+
if (r.routeReception) {
|
|
92
|
+
ensureExchange(doc, { name: r.routeExchange, type: 'fanout' }, opts);
|
|
93
|
+
ensureQueue(doc, { name: r.routeQueue, exchange: r.routeExchange, durable: true }, opts);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 6. gateway: section — set each key ONLY if absent, so user edits are never clobbered.
|
|
97
|
+
if (!doc.hasIn(['gateway', 'events'])) setIn(doc, ['gateway', 'events'], []);
|
|
98
|
+
if (!doc.hasIn(['gateway', 'ws'])) setIn(doc, ['gateway', 'ws'], { heartbeatIntervalMs: 30000 });
|
|
99
|
+
if (r.anyAdmin) {
|
|
100
|
+
if (!doc.hasIn(['gateway', 'reloadTopic'])) setIn(doc, ['gateway', 'reloadTopic'], r.controlTopic);
|
|
101
|
+
if (!doc.hasIn(['gateway', 'metrics'])) setIn(doc, ['gateway', 'metrics'], { topic: 'rlb-gateway-admin', action: 'gw-metrics-track' });
|
|
102
|
+
if (!doc.hasIn(['gateway', 'loadConfig'])) setIn(doc, ['gateway', 'loadConfig'], { paths: { topic: 'rlb-gateway-admin', action: 'gw-path-export' } });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 7. Management routes so the wired ACL / gateway-admin modules are reachable over HTTP.
|
|
106
|
+
if (!doc.hasIn(['gateway', 'paths'])) setIn(doc, ['gateway', 'paths'], []);
|
|
107
|
+
const paths = [...(r.acl ? aclManagementPaths() : []), ...(r.anyAdmin ? adminManagementPaths(r.controlTopic) : [])];
|
|
108
|
+
for (const p of paths) upsertSeqItemByKey(doc, ['gateway', 'paths'], 'name', p as unknown as Record<string, unknown>, opts);
|
|
109
|
+
|
|
110
|
+
writeConfigDoc(tree, configPath, doc);
|
|
111
|
+
|
|
112
|
+
// 8. TypeScript wiring: config loader, AppModule modules, main.ts, package.json deps.
|
|
113
|
+
createConfigLoader(tree);
|
|
114
|
+
const modulePath = wireAppModule(tree, {
|
|
115
|
+
libSymbols: libSymbols(r.features),
|
|
116
|
+
importLines: importLines(r.features),
|
|
117
|
+
entries: moduleEntries(r.features),
|
|
118
|
+
});
|
|
119
|
+
if (!modulePath) {
|
|
120
|
+
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.');
|
|
121
|
+
}
|
|
122
|
+
if (!configureMainForGateway(tree)) {
|
|
123
|
+
context.logger.warn('[rlb-amqp] main.ts not found — enable rawBody + WsAdapter manually.');
|
|
124
|
+
}
|
|
125
|
+
addDeps(tree, {
|
|
126
|
+
'@nestjs/config': '^4.0.4',
|
|
127
|
+
'js-yaml': '^4.1.0',
|
|
128
|
+
'@nestjs/axios': '^4.0.1',
|
|
129
|
+
'@nestjs/platform-ws': '^11.0.1',
|
|
130
|
+
'@nestjs/websockets': '^11.0.1',
|
|
131
|
+
ws: '^8.21.0',
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
return tree;
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Copy a static in-memory-repository asset tree under ./files/<name> to the project root. */
|
|
139
|
+
function copyAsset(name: string): Rule {
|
|
140
|
+
return mergeWith(apply(url(`./files/${name}`), [move(normalize('.'))]), MergeStrategy.Overwrite);
|
|
141
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface GatewayPath {
|
|
2
|
+
name: string;
|
|
3
|
+
method: string;
|
|
4
|
+
path: string;
|
|
5
|
+
dataSource: string;
|
|
6
|
+
topic: string;
|
|
7
|
+
action: string;
|
|
8
|
+
mode: string;
|
|
9
|
+
headers?: Record<string, string>;
|
|
10
|
+
}
|
|
11
|
+
export declare function aclManagementPaths(): GatewayPath[];
|
|
12
|
+
export declare function adminManagementPaths(controlTopic: string): GatewayPath[];
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.aclManagementPaths = aclManagementPaths;
|
|
4
|
+
exports.adminManagementPaths = adminManagementPaths;
|
|
5
|
+
function aclManagementPaths() {
|
|
6
|
+
const rpc = (name, method, path, dataSource, action) => ({
|
|
7
|
+
name,
|
|
8
|
+
method,
|
|
9
|
+
path,
|
|
10
|
+
dataSource,
|
|
11
|
+
topic: 'rlb-acl',
|
|
12
|
+
action,
|
|
13
|
+
mode: 'rpc',
|
|
14
|
+
});
|
|
15
|
+
return [
|
|
16
|
+
rpc('acl-action-list', 'GET', '/acl/actions', 'query', 'acl-action-list'),
|
|
17
|
+
rpc('acl-action-get', 'GET', '/acl/actions/get', 'query', 'acl-action-get'),
|
|
18
|
+
rpc('acl-action-upsert', 'PUT', '/acl/actions', 'body', 'acl-action-update'),
|
|
19
|
+
rpc('acl-action-delete', 'DELETE', '/acl/actions', 'body', 'acl-action-delete'),
|
|
20
|
+
rpc('acl-action-search', 'GET', '/acl/actions/search', 'query', 'acl-action-search'),
|
|
21
|
+
rpc('acl-role-list', 'GET', '/acl/roles', 'query', 'acl-role-list'),
|
|
22
|
+
rpc('acl-role-get', 'GET', '/acl/roles/get', 'query', 'acl-role-get'),
|
|
23
|
+
rpc('acl-role-upsert', 'PUT', '/acl/roles', 'body', 'acl-role-update'),
|
|
24
|
+
rpc('acl-role-delete', 'DELETE', '/acl/roles', 'body', 'acl-role-delete'),
|
|
25
|
+
rpc('acl-role-search', 'GET', '/acl/roles/search', 'query', 'acl-role-search'),
|
|
26
|
+
rpc('acl-grant', 'POST', '/acl/grants', 'body', 'acl-grant'),
|
|
27
|
+
rpc('acl-revoke', 'DELETE', '/acl/grants', 'body', 'acl-revoke'),
|
|
28
|
+
rpc('acl-grant-search', 'GET', '/acl/grants/search', 'query', 'acl-grant-search'),
|
|
29
|
+
rpc('acl-check-action', 'GET', '/acl/check', 'query', 'acl-check-action'),
|
|
30
|
+
rpc('acl-list-resources-by-user', 'GET', '/acl/resources', 'query', 'acl-list-resources-by-user'),
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
function adminManagementPaths(controlTopic) {
|
|
34
|
+
const rpc = (name, method, path, dataSource, action) => ({
|
|
35
|
+
name,
|
|
36
|
+
method,
|
|
37
|
+
path,
|
|
38
|
+
dataSource,
|
|
39
|
+
topic: 'rlb-gateway-admin',
|
|
40
|
+
action,
|
|
41
|
+
mode: 'rpc',
|
|
42
|
+
});
|
|
43
|
+
return [
|
|
44
|
+
rpc('health', 'GET', '/health', 'query', 'gw-health'),
|
|
45
|
+
rpc('gw-path-create', 'POST', '/admin/paths', 'body', 'gw-path-create'),
|
|
46
|
+
rpc('gw-path-list', 'GET', '/admin/paths', 'query', 'gw-path-list'),
|
|
47
|
+
rpc('gw-path-search', 'GET', '/admin/paths/search', 'query', 'gw-path-search'),
|
|
48
|
+
rpc('gw-path-export', 'GET', '/admin/paths/export', 'query', 'gw-path-export'),
|
|
49
|
+
rpc('gw-path-update', 'PUT', '/admin/paths', 'body', 'gw-path-update'),
|
|
50
|
+
rpc('gw-path-get', 'GET', '/admin/paths/get', 'query', 'gw-path-get'),
|
|
51
|
+
rpc('gw-path-delete', 'DELETE', '/admin/paths', 'body', 'gw-path-delete'),
|
|
52
|
+
rpc('gw-route-log-list', 'GET', '/admin/route-log', 'query', 'gw-route-log-list'),
|
|
53
|
+
rpc('gw-route-log-search', 'GET', '/admin/route-log/search', 'query', 'gw-route-log-search'),
|
|
54
|
+
rpc('gw-auth-list', 'GET', '/admin/auth', 'query', 'gw-auth-list'),
|
|
55
|
+
rpc('gw-auth-search', 'GET', '/admin/auth/search', 'query', 'gw-auth-search'),
|
|
56
|
+
rpc('gw-auth-upsert', 'PUT', '/admin/auth', 'body', 'gw-auth-update'),
|
|
57
|
+
rpc('gw-auth-get', 'GET', '/admin/auth/get', 'query', 'gw-auth-get'),
|
|
58
|
+
rpc('gw-auth-delete', 'DELETE', '/admin/auth', 'body', 'gw-auth-delete'),
|
|
59
|
+
rpc('gw-metrics-get', 'GET', '/admin/metrics', 'query', 'gw-metrics-get'),
|
|
60
|
+
rpc('gw-metrics-series', 'GET', '/admin/metrics/series', 'query', 'gw-metrics-series'),
|
|
61
|
+
rpc('gw-metrics-points', 'GET', '/admin/metrics/points', 'query', 'gw-metrics-points'),
|
|
62
|
+
rpc('gw-metrics-summary', 'GET', '/admin/metrics/summary', 'query', 'gw-metrics-summary'),
|
|
63
|
+
{
|
|
64
|
+
name: 'gw-metrics-prometheus',
|
|
65
|
+
method: 'GET',
|
|
66
|
+
path: '/admin/metrics/prometheus',
|
|
67
|
+
dataSource: 'query',
|
|
68
|
+
topic: 'rlb-gateway-admin',
|
|
69
|
+
action: 'gw-metrics-prometheus',
|
|
70
|
+
mode: 'rpc',
|
|
71
|
+
headers: { 'Content-Type': 'text/plain; version=0.0.4' },
|
|
72
|
+
},
|
|
73
|
+
rpc('gw-metrics-rollups', 'GET', '/admin/metrics/rollups', 'query', 'gw-metrics-rollups'),
|
|
74
|
+
{
|
|
75
|
+
name: 'gw-metrics-track',
|
|
76
|
+
method: 'POST',
|
|
77
|
+
path: '/admin/metrics/track',
|
|
78
|
+
dataSource: 'body',
|
|
79
|
+
topic: 'rlb-gateway-admin',
|
|
80
|
+
action: 'gw-metrics-track',
|
|
81
|
+
mode: 'event',
|
|
82
|
+
},
|
|
83
|
+
{ name: 'gw-reload', method: 'POST', path: '/admin/reload', dataSource: 'body', topic: controlTopic, action: 'gw-reload', mode: 'event' },
|
|
84
|
+
{ name: 'gw-auth-reload', method: 'POST', path: '/admin/auth/reload', dataSource: 'body', topic: controlTopic, action: 'gw-auth-reload', mode: 'event' },
|
|
85
|
+
];
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../libs/rlb-nestjs-amqp/src/schematics/add-gateway/paths.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The management HTTP routes a gateway exposes so the ACL and gateway-admin RPC surfaces are
|
|
3
|
+
* reachable over HTTP. Seeded into `gateway.paths[]` (idempotent upsert by `name`) so the wired
|
|
4
|
+
* AclModule / GatewayAdminModule are actually usable — a gateway with the modules but no routes
|
|
5
|
+
* is inert. Name-keyed resources use PUT-upsert (no POST); `acl-grant`/`acl-revoke` are the exception.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface GatewayPath {
|
|
9
|
+
name: string;
|
|
10
|
+
method: string;
|
|
11
|
+
path: string;
|
|
12
|
+
dataSource: string;
|
|
13
|
+
topic: string;
|
|
14
|
+
action: string;
|
|
15
|
+
mode: string;
|
|
16
|
+
headers?: Record<string, string>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** ACL management + check routes (topic `rlb-acl`). */
|
|
20
|
+
export function aclManagementPaths(): GatewayPath[] {
|
|
21
|
+
const rpc = (name: string, method: string, path: string, dataSource: string, action: string): GatewayPath => ({
|
|
22
|
+
name,
|
|
23
|
+
method,
|
|
24
|
+
path,
|
|
25
|
+
dataSource,
|
|
26
|
+
topic: 'rlb-acl',
|
|
27
|
+
action,
|
|
28
|
+
mode: 'rpc',
|
|
29
|
+
});
|
|
30
|
+
return [
|
|
31
|
+
rpc('acl-action-list', 'GET', '/acl/actions', 'query', 'acl-action-list'),
|
|
32
|
+
rpc('acl-action-get', 'GET', '/acl/actions/get', 'query', 'acl-action-get'),
|
|
33
|
+
rpc('acl-action-upsert', 'PUT', '/acl/actions', 'body', 'acl-action-update'),
|
|
34
|
+
rpc('acl-action-delete', 'DELETE', '/acl/actions', 'body', 'acl-action-delete'),
|
|
35
|
+
rpc('acl-action-search', 'GET', '/acl/actions/search', 'query', 'acl-action-search'),
|
|
36
|
+
rpc('acl-role-list', 'GET', '/acl/roles', 'query', 'acl-role-list'),
|
|
37
|
+
rpc('acl-role-get', 'GET', '/acl/roles/get', 'query', 'acl-role-get'),
|
|
38
|
+
rpc('acl-role-upsert', 'PUT', '/acl/roles', 'body', 'acl-role-update'),
|
|
39
|
+
rpc('acl-role-delete', 'DELETE', '/acl/roles', 'body', 'acl-role-delete'),
|
|
40
|
+
rpc('acl-role-search', 'GET', '/acl/roles/search', 'query', 'acl-role-search'),
|
|
41
|
+
rpc('acl-grant', 'POST', '/acl/grants', 'body', 'acl-grant'),
|
|
42
|
+
rpc('acl-revoke', 'DELETE', '/acl/grants', 'body', 'acl-revoke'),
|
|
43
|
+
rpc('acl-grant-search', 'GET', '/acl/grants/search', 'query', 'acl-grant-search'),
|
|
44
|
+
rpc('acl-check-action', 'GET', '/acl/check', 'query', 'acl-check-action'),
|
|
45
|
+
rpc('acl-list-resources-by-user', 'GET', '/acl/resources', 'query', 'acl-list-resources-by-user'),
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Gateway-admin routes (topic `rlb-gateway-admin`; the two reloads publish on `controlTopic`). */
|
|
50
|
+
export function adminManagementPaths(controlTopic: string): GatewayPath[] {
|
|
51
|
+
const rpc = (name: string, method: string, path: string, dataSource: string, action: string): GatewayPath => ({
|
|
52
|
+
name,
|
|
53
|
+
method,
|
|
54
|
+
path,
|
|
55
|
+
dataSource,
|
|
56
|
+
topic: 'rlb-gateway-admin',
|
|
57
|
+
action,
|
|
58
|
+
mode: 'rpc',
|
|
59
|
+
});
|
|
60
|
+
return [
|
|
61
|
+
rpc('health', 'GET', '/health', 'query', 'gw-health'),
|
|
62
|
+
rpc('gw-path-create', 'POST', '/admin/paths', 'body', 'gw-path-create'),
|
|
63
|
+
rpc('gw-path-list', 'GET', '/admin/paths', 'query', 'gw-path-list'),
|
|
64
|
+
rpc('gw-path-search', 'GET', '/admin/paths/search', 'query', 'gw-path-search'),
|
|
65
|
+
rpc('gw-path-export', 'GET', '/admin/paths/export', 'query', 'gw-path-export'),
|
|
66
|
+
rpc('gw-path-update', 'PUT', '/admin/paths', 'body', 'gw-path-update'),
|
|
67
|
+
rpc('gw-path-get', 'GET', '/admin/paths/get', 'query', 'gw-path-get'),
|
|
68
|
+
rpc('gw-path-delete', 'DELETE', '/admin/paths', 'body', 'gw-path-delete'),
|
|
69
|
+
rpc('gw-route-log-list', 'GET', '/admin/route-log', 'query', 'gw-route-log-list'),
|
|
70
|
+
rpc('gw-route-log-search', 'GET', '/admin/route-log/search', 'query', 'gw-route-log-search'),
|
|
71
|
+
rpc('gw-auth-list', 'GET', '/admin/auth', 'query', 'gw-auth-list'),
|
|
72
|
+
rpc('gw-auth-search', 'GET', '/admin/auth/search', 'query', 'gw-auth-search'),
|
|
73
|
+
rpc('gw-auth-upsert', 'PUT', '/admin/auth', 'body', 'gw-auth-update'),
|
|
74
|
+
rpc('gw-auth-get', 'GET', '/admin/auth/get', 'query', 'gw-auth-get'),
|
|
75
|
+
rpc('gw-auth-delete', 'DELETE', '/admin/auth', 'body', 'gw-auth-delete'),
|
|
76
|
+
rpc('gw-metrics-get', 'GET', '/admin/metrics', 'query', 'gw-metrics-get'),
|
|
77
|
+
rpc('gw-metrics-series', 'GET', '/admin/metrics/series', 'query', 'gw-metrics-series'),
|
|
78
|
+
rpc('gw-metrics-points', 'GET', '/admin/metrics/points', 'query', 'gw-metrics-points'),
|
|
79
|
+
rpc('gw-metrics-summary', 'GET', '/admin/metrics/summary', 'query', 'gw-metrics-summary'),
|
|
80
|
+
{
|
|
81
|
+
name: 'gw-metrics-prometheus',
|
|
82
|
+
method: 'GET',
|
|
83
|
+
path: '/admin/metrics/prometheus',
|
|
84
|
+
dataSource: 'query',
|
|
85
|
+
topic: 'rlb-gateway-admin',
|
|
86
|
+
action: 'gw-metrics-prometheus',
|
|
87
|
+
mode: 'rpc',
|
|
88
|
+
headers: { 'Content-Type': 'text/plain; version=0.0.4' },
|
|
89
|
+
},
|
|
90
|
+
rpc('gw-metrics-rollups', 'GET', '/admin/metrics/rollups', 'query', 'gw-metrics-rollups'),
|
|
91
|
+
{
|
|
92
|
+
name: 'gw-metrics-track',
|
|
93
|
+
method: 'POST',
|
|
94
|
+
path: '/admin/metrics/track',
|
|
95
|
+
dataSource: 'body',
|
|
96
|
+
topic: 'rlb-gateway-admin',
|
|
97
|
+
action: 'gw-metrics-track',
|
|
98
|
+
mode: 'event',
|
|
99
|
+
},
|
|
100
|
+
{ name: 'gw-reload', method: 'POST', path: '/admin/reload', dataSource: 'body', topic: controlTopic, action: 'gw-reload', mode: 'event' },
|
|
101
|
+
{ name: 'gw-auth-reload', method: 'POST', path: '/admin/auth/reload', dataSource: 'body', topic: controlTopic, action: 'gw-auth-reload', mode: 'event' },
|
|
102
|
+
];
|
|
103
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface AddGatewayOptions {
|
|
2
|
+
exchange?: string;
|
|
3
|
+
aclQueue?: string;
|
|
4
|
+
adminQueue?: string;
|
|
5
|
+
controlTopic?: string;
|
|
6
|
+
routeExchange?: string;
|
|
7
|
+
routeQueue?: string;
|
|
8
|
+
acl?: boolean;
|
|
9
|
+
admin?: boolean;
|
|
10
|
+
routeReception?: boolean;
|
|
11
|
+
overwrite?: boolean;
|
|
12
|
+
config?: string;
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../libs/rlb-nestjs-amqp/src/schematics/add-gateway/schema.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/schema",
|
|
3
|
+
"$id": "SchematicsRlbAmqpAddGateway",
|
|
4
|
+
"title": "Promote a microservice config to a gateway (config.yaml only)",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"exchange": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"default": "rlb",
|
|
10
|
+
"description": "Main AMQP exchange backing the acl/admin queues."
|
|
11
|
+
},
|
|
12
|
+
"aclQueue": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"default": "rlb-acl",
|
|
15
|
+
"description": "Queue backing the fixed rlb-acl topic."
|
|
16
|
+
},
|
|
17
|
+
"adminQueue": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"default": "rlb-gateway-admin",
|
|
20
|
+
"description": "Queue backing the fixed rlb-gateway-admin topic."
|
|
21
|
+
},
|
|
22
|
+
"controlTopic": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"default": "rlb-gateway-control",
|
|
25
|
+
"description": "Broadcast control/reload topic name."
|
|
26
|
+
},
|
|
27
|
+
"routeExchange": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"default": "rlb-route-discovery",
|
|
30
|
+
"description": "Route-discovery fanout exchange (consumer side)."
|
|
31
|
+
},
|
|
32
|
+
"routeQueue": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"default": "rlb-route-sync",
|
|
35
|
+
"description": "Route-sync durable queue (consumer side)."
|
|
36
|
+
},
|
|
37
|
+
"acl": {
|
|
38
|
+
"type": "boolean",
|
|
39
|
+
"default": true,
|
|
40
|
+
"description": "Include ACL infra (rlb-acl topic/queue)."
|
|
41
|
+
},
|
|
42
|
+
"admin": {
|
|
43
|
+
"type": "boolean",
|
|
44
|
+
"default": true,
|
|
45
|
+
"description": "Include gateway-admin infra (rlb-gateway-admin topic/queue + control topic)."
|
|
46
|
+
},
|
|
47
|
+
"routeReception": {
|
|
48
|
+
"type": "boolean",
|
|
49
|
+
"default": true,
|
|
50
|
+
"description": "Consume routes auto-published by microservices (route-sync infra)."
|
|
51
|
+
},
|
|
52
|
+
"overwrite": {
|
|
53
|
+
"type": "boolean",
|
|
54
|
+
"default": false,
|
|
55
|
+
"description": "Update entries that already exist (default: leave them untouched)."
|
|
56
|
+
},
|
|
57
|
+
"config": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"description": "Path to config.yaml (default: auto-detected)."
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"required": []
|
|
63
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface AddGatewayOptions {
|
|
2
|
+
/** Main AMQP exchange backing the acl/admin queues. Default: 'rlb'. */
|
|
3
|
+
exchange?: string;
|
|
4
|
+
/** Queue backing the fixed rlb-acl topic. Default: 'rlb-acl'. */
|
|
5
|
+
aclQueue?: string;
|
|
6
|
+
/** Queue backing the fixed rlb-gateway-admin topic. Default: 'rlb-gateway-admin'. */
|
|
7
|
+
adminQueue?: string;
|
|
8
|
+
/** Broadcast control/reload topic name. Default: 'rlb-gateway-control'. */
|
|
9
|
+
controlTopic?: string;
|
|
10
|
+
/** Route-discovery fanout exchange (consumer side). Default: 'rlb-route-discovery'. */
|
|
11
|
+
routeExchange?: string;
|
|
12
|
+
/** Route-sync durable queue (consumer side). Default: 'rlb-route-sync'. */
|
|
13
|
+
routeQueue?: string;
|
|
14
|
+
/** Include ACL infra (rlb-acl topic/queue). Default: true. */
|
|
15
|
+
acl?: boolean;
|
|
16
|
+
/** Include gateway-admin infra (rlb-gateway-admin topic/queue + control topic). Default: true. */
|
|
17
|
+
admin?: boolean;
|
|
18
|
+
/** Consume routes auto-published by microservices (route-sync infra). Default: true. */
|
|
19
|
+
routeReception?: boolean;
|
|
20
|
+
/** Update entries that already exist (default: leave them untouched). */
|
|
21
|
+
overwrite?: boolean;
|
|
22
|
+
/** Path to config.yaml (default: auto-detected, typically config/config.yaml). */
|
|
23
|
+
config?: string;
|
|
24
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { WireEntry } from '../utils/nest-wiring.util';
|
|
2
|
+
export interface GwFeatures {
|
|
3
|
+
acl: boolean;
|
|
4
|
+
admin: boolean;
|
|
5
|
+
routeReception: boolean;
|
|
6
|
+
routeExchange: string;
|
|
7
|
+
routeQueue: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function libSymbols(f: GwFeatures): string[];
|
|
10
|
+
export declare function importLines(f: GwFeatures): {
|
|
11
|
+
line: string;
|
|
12
|
+
marker: string;
|
|
13
|
+
}[];
|
|
14
|
+
export declare function moduleEntries(f: GwFeatures): WireEntry[];
|