@rsdk/cli 3.3.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/bin.d.ts +2 -0
  3. package/dist/bin.js +21 -0
  4. package/dist/bin.js.map +1 -0
  5. package/dist/kafka/commands/extract/extract.command.d.ts +2 -0
  6. package/dist/kafka/commands/extract/extract.command.js +17 -0
  7. package/dist/kafka/commands/extract/extract.command.js.map +1 -0
  8. package/dist/kafka/commands/extract/kafka-config.extractor.d.ts +6 -0
  9. package/dist/kafka/commands/extract/kafka-config.extractor.js +40 -0
  10. package/dist/kafka/commands/extract/kafka-config.extractor.js.map +1 -0
  11. package/dist/kafka/commands/extract/types.d.ts +85 -0
  12. package/dist/kafka/commands/extract/types.js +3 -0
  13. package/dist/kafka/commands/extract/types.js.map +1 -0
  14. package/dist/kafka/commands/schema/print-schema.d.ts +5 -0
  15. package/dist/kafka/commands/schema/print-schema.js +11 -0
  16. package/dist/kafka/commands/schema/print-schema.js.map +1 -0
  17. package/dist/kafka/commands/schema/schema.command.d.ts +2 -0
  18. package/dist/kafka/commands/schema/schema.command.js +12 -0
  19. package/dist/kafka/commands/schema/schema.command.js.map +1 -0
  20. package/dist/kafka/commands/sync/check-exist.d.ts +7 -0
  21. package/dist/kafka/commands/sync/check-exist.js +57 -0
  22. package/dist/kafka/commands/sync/check-exist.js.map +1 -0
  23. package/dist/kafka/commands/sync/context.d.ts +3 -0
  24. package/dist/kafka/commands/sync/context.js +3 -0
  25. package/dist/kafka/commands/sync/context.js.map +1 -0
  26. package/dist/kafka/commands/sync/create-topic.task.d.ts +5 -0
  27. package/dist/kafka/commands/sync/create-topic.task.js +28 -0
  28. package/dist/kafka/commands/sync/create-topic.task.js.map +1 -0
  29. package/dist/kafka/commands/sync/get-config.d.ts +5 -0
  30. package/dist/kafka/commands/sync/get-config.js +28 -0
  31. package/dist/kafka/commands/sync/get-config.js.map +1 -0
  32. package/dist/kafka/commands/sync/kafka.creds-provider.d.ts +4 -0
  33. package/dist/kafka/commands/sync/kafka.creds-provider.js +27 -0
  34. package/dist/kafka/commands/sync/kafka.creds-provider.js.map +1 -0
  35. package/dist/kafka/commands/sync/sync.command.d.ts +2 -0
  36. package/dist/kafka/commands/sync/sync.command.js +49 -0
  37. package/dist/kafka/commands/sync/sync.command.js.map +1 -0
  38. package/dist/kafka/config.schema.d.ts +34 -0
  39. package/dist/kafka/config.schema.js +26 -0
  40. package/dist/kafka/config.schema.js.map +1 -0
  41. package/dist/kafka/schemas/string-enum.d.ts +2 -0
  42. package/dist/kafka/schemas/string-enum.js +14 -0
  43. package/dist/kafka/schemas/string-enum.js.map +1 -0
  44. package/package.json +29 -0
  45. package/src/bin.ts +26 -0
  46. package/src/kafka/commands/extract/extract.command.ts +16 -0
  47. package/src/kafka/commands/extract/kafka-config.extractor.ts +47 -0
  48. package/src/kafka/commands/extract/types.ts +101 -0
  49. package/src/kafka/commands/schema/print-schema.ts +15 -0
  50. package/src/kafka/commands/schema/schema.command.ts +10 -0
  51. package/src/kafka/commands/sync/check-exist.ts +68 -0
  52. package/src/kafka/commands/sync/context.ts +3 -0
  53. package/src/kafka/commands/sync/create-topic.task.ts +35 -0
  54. package/src/kafka/commands/sync/get-config.ts +25 -0
  55. package/src/kafka/commands/sync/kafka.creds-provider.ts +21 -0
  56. package/src/kafka/commands/sync/sync.command.ts +66 -0
  57. package/src/kafka/config.schema.ts +31 -0
  58. package/src/kafka/schemas/string-enum.ts +15 -0
  59. package/tsconfig.json +7 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,14 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file.
4
+ See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
+
6
+ ## [3.3.0-next.0](https://github.com/R-Vision/rsdk/compare/v3.2.3...v3.3.0-next.0) (2023-08-10)
7
+
8
+ ### Features
9
+
10
+ * **cli:** create cli package and kafka commands ([7583da8](https://github.com/R-Vision/rsdk/commit/7583da8f0cdf2e88ff8b0065d10f386a8bdd2713))
11
+
12
+ ### Bug Fixes
13
+
14
+ * **cli:** handle empty topic name in `rsdk.json` ([f053d05](https://github.com/R-Vision/rsdk/commit/f053d0581d0eeea41f849df8ce284d3307b7200d))
package/dist/bin.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/bin.js ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const extract_command_1 = require("./kafka/commands/extract/extract.command");
6
+ const schema_command_1 = require("./kafka/commands/schema/schema.command");
7
+ const sync_command_1 = require("./kafka/commands/sync/sync.command");
8
+ // TODO: сделать более понятным, переиспользуемым, консистентным и контролируем
9
+ process.env.RSDK_CLI_MODE = 'true';
10
+ const kafkaCommand = new commander_1.Command('kafka');
11
+ commander_1.program.addCommand(kafkaCommand
12
+ .addCommand(sync_command_1.KafkaSyncCommand)
13
+ .addCommand(extract_command_1.KafkaExtractCommand)
14
+ .addCommand(schema_command_1.SchemaCommand));
15
+ const execute = () => {
16
+ commander_1.program.parse();
17
+ };
18
+ if (require.main === module) {
19
+ execute();
20
+ }
21
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";;;AACA,yCAA6C;AAE7C,8EAA+E;AAC/E,2EAAuE;AACvE,qEAAsE;AAEtE,+EAA+E;AAC/E,OAAO,CAAC,GAAG,CAAC,aAAa,GAAG,MAAM,CAAC;AAEnC,MAAM,YAAY,GAAG,IAAI,mBAAO,CAAC,OAAO,CAAC,CAAC;AAE1C,mBAAO,CAAC,UAAU,CAChB,YAAY;KACT,UAAU,CAAC,+BAAgB,CAAC;KAC5B,UAAU,CAAC,qCAAmB,CAAC;KAC/B,UAAU,CAAC,8BAAa,CAAC,CAC7B,CAAC;AAEF,MAAM,OAAO,GAAG,GAAS,EAAE;IACzB,mBAAO,CAAC,KAAK,EAAE,CAAC;AAClB,CAAC,CAAC;AAEF,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE;IAC3B,OAAO,EAAE,CAAC;CACX"}
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const KafkaExtractCommand: Command;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KafkaExtractCommand = void 0;
4
+ const commander_1 = require("commander");
5
+ const print_schema_1 = require("../schema/print-schema");
6
+ const kafka_config_extractor_1 = require("./kafka-config.extractor");
7
+ exports.KafkaExtractCommand = new commander_1.Command('extract')
8
+ .description('extract kafka.yml from rsdk.json')
9
+ .option('--with-schema', 'print and include $schema to yaml for autocomplete')
10
+ .action(async (_, cmd) => {
11
+ const options = cmd.opts();
12
+ if (options.withSchema) {
13
+ await (0, print_schema_1.printSchema)({ outFile: print_schema_1.printDefaultOutFile, pretty: true });
14
+ }
15
+ await kafka_config_extractor_1.KafkaConfigExtractor.extractCommand(print_schema_1.printDefaultOutFile);
16
+ });
17
+ //# sourceMappingURL=extract.command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.command.js","sourceRoot":"","sources":["../../../../src/kafka/commands/extract/extract.command.ts"],"names":[],"mappings":";;;AAAA,yCAAoC;AAEpC,yDAA0E;AAE1E,qEAAgE;AAEnD,QAAA,mBAAmB,GAAG,IAAI,mBAAO,CAAC,SAAS,CAAC;KACtD,WAAW,CAAC,kCAAkC,CAAC;KAC/C,MAAM,CAAC,eAAe,EAAE,oDAAoD,CAAC;KAC7E,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,GAAY,EAAE,EAAE;IAChC,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,OAAO,CAAC,UAAU,EAAE;QACtB,MAAM,IAAA,0BAAW,EAAC,EAAE,OAAO,EAAE,kCAAmB,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;KACnE;IACD,MAAM,6CAAoB,CAAC,cAAc,CAAC,kCAAmB,CAAC,CAAC;AACjE,CAAC,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Static } from '@sinclair/typebox';
2
+ import type { CliKafkaConfigSchema } from '../../config.schema';
3
+ export declare class KafkaConfigExtractor {
4
+ static extractCommand(includeSchema?: string): Promise<void>;
5
+ static extract(): Promise<Static<typeof CliKafkaConfigSchema>>;
6
+ }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.KafkaConfigExtractor = void 0;
7
+ const core_1 = require("@rsdk/core");
8
+ const promises_1 = require("node:fs/promises");
9
+ const node_path_1 = require("node:path");
10
+ const yaml_1 = __importDefault(require("yaml"));
11
+ class KafkaConfigExtractor {
12
+ static async extractCommand(includeSchema) {
13
+ const resultKafkaConfig = await this.extract();
14
+ await (0, promises_1.writeFile)((0, node_path_1.resolve)('kafka.yml'), yaml_1.default.stringify({
15
+ ...(includeSchema && { $schema: includeSchema }),
16
+ ...resultKafkaConfig,
17
+ }));
18
+ }
19
+ static async extract() {
20
+ const pathToRsdkJson = (0, node_path_1.resolve)('autodoc/rsdk.json');
21
+ const rsdkJson = JSON.parse(await (0, promises_1.readFile)(pathToRsdkJson, 'utf8'));
22
+ const kafkaMetadata = rsdkJson.rsdkMetadata
23
+ .filter(({ scope }) => scope === 'kafka-topic')
24
+ .map(({ value }) => value);
25
+ const topics = kafkaMetadata.map((meta) => {
26
+ const topicName = meta.topicName;
27
+ if (!topicName) {
28
+ throw new core_1.InputException('topicName must be provided', {
29
+ details: meta,
30
+ });
31
+ }
32
+ return {
33
+ name: topicName,
34
+ };
35
+ });
36
+ return { topics };
37
+ }
38
+ }
39
+ exports.KafkaConfigExtractor = KafkaConfigExtractor;
40
+ //# sourceMappingURL=kafka-config.extractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kafka-config.extractor.js","sourceRoot":"","sources":["../../../../src/kafka/commands/extract/kafka-config.extractor.ts"],"names":[],"mappings":";;;;;;AAAA,qCAA4C;AAE5C,+CAAuD;AACvD,yCAAoC;AACpC,gDAAwB;AAMxB,MAAa,oBAAoB;IAC/B,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,aAAsB;QAChD,MAAM,iBAAiB,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QAE/C,MAAM,IAAA,oBAAS,EACb,IAAA,mBAAO,EAAC,WAAW,CAAC,EACpB,cAAI,CAAC,SAAS,CAAC;YACb,GAAG,CAAC,aAAa,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;YAChD,GAAG,iBAAiB;SACrB,CAAC,CACH,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,OAAO;QAClB,MAAM,cAAc,GAAG,IAAA,mBAAO,EAAC,mBAAmB,CAAC,CAAC;QACpD,MAAM,QAAQ,GAAS,IAAI,CAAC,KAAK,CAAC,MAAM,IAAA,mBAAQ,EAAC,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC;QAE1E,MAAM,aAAa,GAAG,QAAQ,CAAC,YAAY;aACxC,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,KAAK,aAAa,CAAC;aAC9C,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;QAE7B,MAAM,MAAM,GACV,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACzB,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;YACjC,IAAI,CAAC,SAAS,EAAE;gBACd,MAAM,IAAI,qBAAc,CAAC,4BAA4B,EAAE;oBACrD,OAAO,EAAE,IAAI;iBACd,CAAC,CAAC;aACJ;YACD,OAAO;gBACL,IAAI,EAAE,SAAS;aAChB,CAAC;QACJ,CAAC,CAAC,CAAC;QAEL,OAAO,EAAE,MAAM,EAAE,CAAC;IACpB,CAAC;CACF;AApCD,oDAoCC"}
@@ -0,0 +1,85 @@
1
+ export interface Root {
2
+ metadata: Metadata;
3
+ rsdkMetadata: RsdkMetadaum[];
4
+ }
5
+ export interface Metadata {
6
+ name: string;
7
+ version: string;
8
+ description: string;
9
+ protocols: string[];
10
+ config: Config;
11
+ }
12
+ export interface Config {
13
+ prefix: string;
14
+ sections: Section[];
15
+ sources: any[];
16
+ properties: any[];
17
+ }
18
+ export interface Section {
19
+ tags?: string[];
20
+ constructorName: string;
21
+ properties: Property[];
22
+ name?: string;
23
+ }
24
+ export interface Property {
25
+ expectedInEnv: boolean;
26
+ key: string;
27
+ description: string;
28
+ parser: Parser;
29
+ defaultValue: any;
30
+ }
31
+ export interface Parser {
32
+ type: string;
33
+ description: string;
34
+ }
35
+ export interface RsdkMetadaum {
36
+ scope: string;
37
+ value: Value;
38
+ }
39
+ export interface Value {
40
+ global?: boolean;
41
+ imports?: Import[];
42
+ providers?: Provider2[];
43
+ exports?: string[];
44
+ token?: string;
45
+ partitionKeyField: any;
46
+ topicName?: string;
47
+ group?: string;
48
+ type?: number;
49
+ eventType?: EventType;
50
+ options?: Options3;
51
+ constructorName?: string;
52
+ }
53
+ export interface Import {
54
+ providers: Provider[];
55
+ exports: Export[];
56
+ }
57
+ export interface Provider {
58
+ inject: Inject | undefined[];
59
+ }
60
+ export interface Inject {
61
+ optional: boolean;
62
+ }
63
+ export interface Export {
64
+ inject: Inject2 | undefined[];
65
+ }
66
+ export interface Inject2 {
67
+ optional: boolean;
68
+ }
69
+ export interface Provider2 {
70
+ provide: string;
71
+ useValue: UseValue;
72
+ }
73
+ export interface UseValue {
74
+ context: string;
75
+ }
76
+ export interface EventType {
77
+ $group: string;
78
+ $partitionKeyField: any;
79
+ $type: string;
80
+ }
81
+ export interface Options3 {
82
+ key: string;
83
+ scope: string;
84
+ description: string;
85
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/kafka/commands/extract/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ export declare const printDefaultOutFile = "./kafka.schema.json";
2
+ export declare function printSchema(options?: {
3
+ outFile?: string;
4
+ pretty?: boolean;
5
+ }): Promise<void>;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.printSchema = exports.printDefaultOutFile = void 0;
4
+ const promises_1 = require("node:fs/promises");
5
+ const config_schema_1 = require("../../config.schema");
6
+ exports.printDefaultOutFile = './kafka.schema.json';
7
+ async function printSchema(options) {
8
+ await (0, promises_1.writeFile)(options?.outFile ?? exports.printDefaultOutFile, JSON.stringify(config_schema_1.CliKafkaConfigSchema, null, options?.pretty ? 2 : 0));
9
+ }
10
+ exports.printSchema = printSchema;
11
+ //# sourceMappingURL=print-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"print-schema.js","sourceRoot":"","sources":["../../../../src/kafka/commands/schema/print-schema.ts"],"names":[],"mappings":";;;AAAA,+CAA6C;AAE7C,uDAA2D;AAE9C,QAAA,mBAAmB,GAAG,qBAAqB,CAAC;AAElD,KAAK,UAAU,WAAW,CAAC,OAGjC;IACC,MAAM,IAAA,oBAAS,EACb,OAAO,EAAE,OAAO,IAAI,2BAAmB,EACvC,IAAI,CAAC,SAAS,CAAC,oCAAoB,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CACpE,CAAC;AACJ,CAAC;AARD,kCAQC"}
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const SchemaCommand: Command;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SchemaCommand = void 0;
4
+ const commander_1 = require("commander");
5
+ const print_schema_1 = require("./print-schema");
6
+ exports.SchemaCommand = new commander_1.Command('schema')
7
+ .description('print json schema for kafka.yml')
8
+ .option('-o, --out-file', 'output file name', 'kafka.schema.json')
9
+ .action(async (_, cmd) => {
10
+ await (0, print_schema_1.printSchema)(cmd.opts().outFile);
11
+ });
12
+ //# sourceMappingURL=schema.command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.command.js","sourceRoot":"","sources":["../../../../src/kafka/commands/schema/schema.command.ts"],"names":[],"mappings":";;;AAAA,yCAAoC;AAEpC,iDAA6C;AAEhC,QAAA,aAAa,GAAG,IAAI,mBAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,iCAAiC,CAAC;KAC9C,MAAM,CAAC,gBAAgB,EAAE,kBAAkB,EAAE,mBAAmB,CAAC;KACjE,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,GAAY,EAAE,EAAE;IAChC,MAAM,IAAA,0BAAW,EAAC,GAAG,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,CAAC;AACxC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { Static } from '@sinclair/typebox';
2
+ import type { Admin } from 'kafkajs';
3
+ import type { Listr, ListrRendererFactory, ListrTaskWrapper } from 'listr2';
4
+ import type { TopicSchema } from '../../config.schema';
5
+ export declare class KafkaTopicConfigurator {
6
+ static configure(topic: Static<typeof TopicSchema>, admin: Admin, parentTask: ListrTaskWrapper<unknown, ListrRendererFactory>): Promise<Listr | void>;
7
+ }
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KafkaTopicConfigurator = void 0;
4
+ const core_1 = require("@rsdk/core");
5
+ const kafkajs_1 = require("kafkajs");
6
+ const listr2_1 = require("listr2");
7
+ class KafkaTopicConfigurator {
8
+ static async configure(topic, admin, parentTask) {
9
+ const metadata = await admin.fetchTopicMetadata({
10
+ topics: [topic.name],
11
+ });
12
+ const partitions = metadata.topics[0].partitions.length;
13
+ if (topic.spec?.partitions && partitions !== topic.spec.partitions) {
14
+ throw new Error(`partitoins for ${topic.name} settled to ${partitions}, but expected ${topic.spec.partitions}`);
15
+ }
16
+ const topicConfig = await admin.describeConfigs({
17
+ resources: [
18
+ {
19
+ type: kafkajs_1.ConfigResourceTypes.TOPIC,
20
+ configNames: ['cleanup.policy'],
21
+ name: topic.name,
22
+ },
23
+ ],
24
+ includeSynonyms: true,
25
+ });
26
+ const cleanupPolicy = topicConfig.resources.find(({ resourceName }) => resourceName === topic.name);
27
+ if (!cleanupPolicy) {
28
+ throw new core_1.InternalException('cleanupPolicy not found');
29
+ }
30
+ const currentValue = cleanupPolicy?.configEntries[0].configValue;
31
+ const configValue = topic?.spec?.settings?.['cleanup.policy'];
32
+ if (configValue && currentValue !== configValue) {
33
+ parentTask.output = `Alter cleanup.policy from ${currentValue} to ${configValue}`;
34
+ await (0, listr2_1.delay)(3e3);
35
+ await admin.alterConfigs({
36
+ validateOnly: false,
37
+ resources: [
38
+ {
39
+ type: kafkajs_1.ConfigResourceTypes.TOPIC,
40
+ configEntries: [
41
+ {
42
+ value: configValue,
43
+ name: 'cleanup.policy',
44
+ },
45
+ ],
46
+ name: topic.name,
47
+ },
48
+ ],
49
+ });
50
+ parentTask.output = `Alter cleanup.policy from ${currentValue} to ${configValue} completed!`;
51
+ await (0, listr2_1.delay)(3e3);
52
+ }
53
+ parentTask.title = `topic ${topic.name} exists and respond config`;
54
+ }
55
+ }
56
+ exports.KafkaTopicConfigurator = KafkaTopicConfigurator;
57
+ //# sourceMappingURL=check-exist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-exist.js","sourceRoot":"","sources":["../../../../src/kafka/commands/sync/check-exist.ts"],"names":[],"mappings":";;;AAAA,qCAA+C;AAG/C,qCAA8C;AAE9C,mCAA+B;AAI/B,MAAa,sBAAsB;IACjC,MAAM,CAAC,KAAK,CAAC,SAAS,CACpB,KAAiC,EACjC,KAAY,EACZ,UAA2D;QAE3D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,kBAAkB,CAAC;YAC9C,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;SACrB,CAAC,CAAC;QACH,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC;QACxD,IAAI,KAAK,CAAC,IAAI,EAAE,UAAU,IAAI,UAAU,KAAK,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE;YAClE,MAAM,IAAI,KAAK,CACb,kBAAkB,KAAK,CAAC,IAAI,eAAe,UAAU,kBAAkB,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,CAC/F,CAAC;SACH;QACD,MAAM,WAAW,GAAG,MAAM,KAAK,CAAC,eAAe,CAAC;YAC9C,SAAS,EAAE;gBACT;oBACE,IAAI,EAAE,6BAAmB,CAAC,KAAK;oBAC/B,WAAW,EAAE,CAAC,gBAAgB,CAAC;oBAC/B,IAAI,EAAE,KAAK,CAAC,IAAI;iBACjB;aACF;YACD,eAAe,EAAE,IAAI;SACtB,CAAC,CAAC;QAEH,MAAM,aAAa,GAAG,WAAW,CAAC,SAAS,CAAC,IAAI,CAC9C,CAAC,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,YAAY,KAAK,KAAK,CAAC,IAAI,CAClD,CAAC;QACF,IAAI,CAAC,aAAa,EAAE;YAClB,MAAM,IAAI,wBAAiB,CAAC,yBAAyB,CAAC,CAAC;SACxD;QACD,MAAM,YAAY,GAAG,aAAa,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;QACjE,MAAM,WAAW,GAAG,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,gBAAgB,CAAC,CAAC;QAE9D,IAAI,WAAW,IAAI,YAAY,KAAK,WAAW,EAAE;YAC/C,UAAU,CAAC,MAAM,GAAG,6BAA6B,YAAY,OAAO,WAAW,EAAE,CAAC;YAClF,MAAM,IAAA,cAAK,EAAC,GAAG,CAAC,CAAC;YACjB,MAAM,KAAK,CAAC,YAAY,CAAC;gBACvB,YAAY,EAAE,KAAK;gBACnB,SAAS,EAAE;oBACT;wBACE,IAAI,EAAE,6BAAmB,CAAC,KAAK;wBAC/B,aAAa,EAAE;4BACb;gCACE,KAAK,EAAE,WAAW;gCAClB,IAAI,EAAE,gBAAgB;6BACvB;yBACF;wBACD,IAAI,EAAE,KAAK,CAAC,IAAI;qBACjB;iBACF;aACF,CAAC,CAAC;YACH,UAAU,CAAC,MAAM,GAAG,6BAA6B,YAAY,OAAO,WAAW,aAAa,CAAC;YAC7F,MAAM,IAAA,cAAK,EAAC,GAAG,CAAC,CAAC;SAClB;QACD,UAAU,CAAC,KAAK,GAAG,SAAS,KAAK,CAAC,IAAI,4BAA4B,CAAC;IACrE,CAAC;CACF;AA1DD,wDA0DC"}
@@ -0,0 +1,3 @@
1
+ export type Context = {
2
+ requireCreate: boolean;
3
+ };
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../../src/kafka/commands/sync/context.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ import type { Static } from '@sinclair/typebox';
2
+ import type { Admin } from 'kafkajs';
3
+ import type { ListrRendererFactory, ListrTaskWrapper } from 'listr2';
4
+ import type { TopicSchema } from '../../config.schema';
5
+ export declare function createTopic(topic: Static<typeof TopicSchema>, admin: Admin, parentTask: ListrTaskWrapper<unknown, ListrRendererFactory>): Promise<void>;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createTopic = void 0;
4
+ const listr2_1 = require("listr2");
5
+ async function createTopic(topic, admin, parentTask) {
6
+ await (0, listr2_1.delay)(1000);
7
+ const numPartitions = topic.spec?.partitions ?? 2;
8
+ const replicationFactor = topic.spec?.replicationFactor;
9
+ const result = await admin.createTopics({
10
+ topics: [
11
+ {
12
+ topic: topic.name,
13
+ ...(replicationFactor && {
14
+ replicationFactor: replicationFactor,
15
+ }),
16
+ numPartitions: numPartitions,
17
+ },
18
+ ],
19
+ });
20
+ if (result) {
21
+ console.log('create topic %s complete!', topic.name);
22
+ parentTask.title = `create topic ${topic.name} complete!`;
23
+ return;
24
+ }
25
+ throw new Error(`create topic ${topic.name} failed!`);
26
+ }
27
+ exports.createTopic = createTopic;
28
+ //# sourceMappingURL=create-topic.task.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-topic.task.js","sourceRoot":"","sources":["../../../../src/kafka/commands/sync/create-topic.task.ts"],"names":[],"mappings":";;;AAGA,mCAA+B;AAIxB,KAAK,UAAU,WAAW,CAC/B,KAAiC,EACjC,KAAY,EACZ,UAA2D;IAE3D,MAAM,IAAA,cAAK,EAAC,IAAI,CAAC,CAAC;IAClB,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,EAAE,UAAU,IAAI,CAAC,CAAC;IAElD,MAAM,iBAAiB,GAAG,KAAK,CAAC,IAAI,EAAE,iBAAiB,CAAC;IAExD,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,YAAY,CAAC;QACtC,MAAM,EAAE;YACN;gBACE,KAAK,EAAE,KAAK,CAAC,IAAI;gBACjB,GAAG,CAAC,iBAAiB,IAAI;oBACvB,iBAAiB,EAAE,iBAAiB;iBACrC,CAAC;gBACF,aAAa,EAAE,aAAa;aAC7B;SACF;KACF,CAAC,CAAC;IACH,IAAI,MAAM,EAAE;QACV,OAAO,CAAC,GAAG,CAAC,2BAA2B,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACrD,UAAU,CAAC,KAAK,GAAG,gBAAgB,KAAK,CAAC,IAAI,YAAY,CAAC;QAC1D,OAAO;KACR;IACD,MAAM,IAAI,KAAK,CAAC,gBAAgB,KAAK,CAAC,IAAI,UAAU,CAAC,CAAC;AACxD,CAAC;AA3BD,kCA2BC"}
@@ -0,0 +1,5 @@
1
+ import type { Static } from '@sinclair/typebox';
2
+ import type { CliKafkaConfigSchema } from '../../config.schema';
3
+ export declare class KafkaExpectedConfigProvider {
4
+ static getConfig(): Promise<Static<typeof CliKafkaConfigSchema>>;
5
+ }
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.KafkaExpectedConfigProvider = void 0;
7
+ const core_1 = require("@rsdk/core");
8
+ const promises_1 = require("node:fs/promises");
9
+ const node_path_1 = require("node:path");
10
+ const yaml_1 = __importDefault(require("yaml"));
11
+ const config_schema_1 = require("../../config.schema");
12
+ class KafkaExpectedConfigProvider {
13
+ static async getConfig() {
14
+ const kafkaYml = await (0, promises_1.readFile)((0, node_path_1.resolve)('kafka.yml'), {
15
+ encoding: 'utf8',
16
+ });
17
+ const config = yaml_1.default.parse(kafkaYml);
18
+ const isValid = config_schema_1.CliKafkaConfigSchemaChecker.Check(config);
19
+ if (!isValid) {
20
+ throw new core_1.InputException('Invalid config', {
21
+ cause: [...config_schema_1.CliKafkaConfigSchemaChecker.Errors(config)],
22
+ });
23
+ }
24
+ return config;
25
+ }
26
+ }
27
+ exports.KafkaExpectedConfigProvider = KafkaExpectedConfigProvider;
28
+ //# sourceMappingURL=get-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-config.js","sourceRoot":"","sources":["../../../../src/kafka/commands/sync/get-config.ts"],"names":[],"mappings":";;;;;;AAAA,qCAA4C;AAE5C,+CAA4C;AAC5C,yCAAoC;AACpC,gDAAwB;AAGxB,uDAAkE;AAElE,MAAa,2BAA2B;IACtC,MAAM,CAAC,KAAK,CAAC,SAAS;QACpB,MAAM,QAAQ,GAAG,MAAM,IAAA,mBAAQ,EAAC,IAAA,mBAAO,EAAC,WAAW,CAAC,EAAE;YACpD,QAAQ,EAAE,MAAM;SACjB,CAAC,CAAC;QACH,MAAM,MAAM,GAAwC,cAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAEzE,MAAM,OAAO,GAAG,2CAA2B,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC1D,IAAI,CAAC,OAAO,EAAE;YACZ,MAAM,IAAI,qBAAc,CAAC,gBAAgB,EAAE;gBACzC,KAAK,EAAE,CAAC,GAAG,2CAA2B,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;aACvD,CAAC,CAAC;SACJ;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF;AAfD,kEAeC"}
@@ -0,0 +1,4 @@
1
+ import type { KafkaConfig } from 'kafkajs';
2
+ export declare class KafkaCredsProvider {
3
+ static getKafkaCreds(): KafkaConfig;
4
+ }
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.KafkaCredsProvider = void 0;
7
+ const core_1 = require("@rsdk/core");
8
+ const dotenv_1 = __importDefault(require("dotenv"));
9
+ class KafkaCredsProvider {
10
+ static getKafkaCreds() {
11
+ const parsed = dotenv_1.default.config();
12
+ if (parsed.error) {
13
+ throw new core_1.InternalException('Error read config', {
14
+ cause: parsed.error,
15
+ });
16
+ }
17
+ const kafkaHost = parsed.parsed?.KAFKA_HOST;
18
+ if (!kafkaHost) {
19
+ throw new core_1.InternalException('KAFKA_HOST not found');
20
+ }
21
+ return {
22
+ brokers: [kafkaHost],
23
+ };
24
+ }
25
+ }
26
+ exports.KafkaCredsProvider = KafkaCredsProvider;
27
+ //# sourceMappingURL=kafka.creds-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kafka.creds-provider.js","sourceRoot":"","sources":["../../../../src/kafka/commands/sync/kafka.creds-provider.ts"],"names":[],"mappings":";;;;;;AAAA,qCAA+C;AAC/C,oDAA4B;AAG5B,MAAa,kBAAkB;IAC7B,MAAM,CAAC,aAAa;QAClB,MAAM,MAAM,GAAG,gBAAM,CAAC,MAAM,EAAE,CAAC;QAC/B,IAAI,MAAM,CAAC,KAAK,EAAE;YAChB,MAAM,IAAI,wBAAiB,CAAC,mBAAmB,EAAE;gBAC/C,KAAK,EAAE,MAAM,CAAC,KAAK;aACpB,CAAC,CAAC;SACJ;QACD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC;QAC5C,IAAI,CAAC,SAAS,EAAE;YACd,MAAM,IAAI,wBAAiB,CAAC,sBAAsB,CAAC,CAAC;SACrD;QACD,OAAO;YACL,OAAO,EAAE,CAAC,SAAS,CAAC;SACrB,CAAC;IACJ,CAAC;CACF;AAhBD,gDAgBC"}
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const KafkaSyncCommand: Command;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KafkaSyncCommand = void 0;
4
+ const commander_1 = require("commander");
5
+ const kafkajs_1 = require("kafkajs");
6
+ const listr2_1 = require("listr2");
7
+ const kafka_config_extractor_1 = require("../extract/kafka-config.extractor");
8
+ const check_exist_1 = require("./check-exist");
9
+ const create_topic_task_1 = require("./create-topic.task");
10
+ const get_config_1 = require("./get-config");
11
+ const kafka_creds_provider_1 = require("./kafka.creds-provider");
12
+ async function createAdmin(kafkaCreds) {
13
+ const kafka = new kafkajs_1.Kafka(kafkaCreds);
14
+ const admin = kafka.admin();
15
+ await admin.connect();
16
+ return admin;
17
+ }
18
+ exports.KafkaSyncCommand = new commander_1.Command('sync')
19
+ .description('try sync kafka state from kafka.yml file')
20
+ .option('--from-rsdk', 'run command based on rsdk.json, instead of kafka.yml')
21
+ .action(async function (_, command) {
22
+ const options = command.opts();
23
+ const config = options.fromRsdk
24
+ ? await kafka_config_extractor_1.KafkaConfigExtractor.extract()
25
+ : await get_config_1.KafkaExpectedConfigProvider.getConfig();
26
+ const kafkaCreds = kafka_creds_provider_1.KafkaCredsProvider.getKafkaCreds();
27
+ const admin = await createAdmin(kafkaCreds);
28
+ const existedTopic = await admin.listTopics();
29
+ await new listr2_1.Listr(config.topics.map((topic) => ({
30
+ title: topic.name,
31
+ task: async (_ctx, parentTask) => {
32
+ const requireCreate = !existedTopic.includes(topic.name);
33
+ return parentTask.newListr([
34
+ {
35
+ title: requireCreate ? 'create' : 'configure',
36
+ task: async () => {
37
+ if (requireCreate) {
38
+ await (0, create_topic_task_1.createTopic)(topic, admin, parentTask);
39
+ return;
40
+ }
41
+ await check_exist_1.KafkaTopicConfigurator.configure(topic, admin, parentTask);
42
+ },
43
+ },
44
+ ], { concurrent: false });
45
+ },
46
+ })), { concurrent: true }).run({ requireCreate: true });
47
+ await admin.disconnect();
48
+ });
49
+ //# sourceMappingURL=sync.command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.command.js","sourceRoot":"","sources":["../../../../src/kafka/commands/sync/sync.command.ts"],"names":[],"mappings":";;;AAAA,yCAAoC;AAEpC,qCAAgC;AAChC,mCAA+B;AAE/B,8EAAyE;AAEzE,+CAAuD;AAEvD,2DAAkD;AAClD,6CAA2D;AAC3D,iEAA4D;AAE5D,KAAK,UAAU,WAAW,CAAC,UAAuB;IAChD,MAAM,KAAK,GAAG,IAAI,eAAK,CAAC,UAAU,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;IAE5B,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC;IACtB,OAAO,KAAK,CAAC;AACf,CAAC;AAEY,QAAA,gBAAgB,GAAG,IAAI,mBAAO,CAAC,MAAM,CAAC;KAChD,WAAW,CAAC,0CAA0C,CAAC;KACvD,MAAM,CAAC,aAAa,EAAE,sDAAsD,CAAC;KAC7E,MAAM,CAAC,KAAK,WAAW,CAAC,EAAE,OAAgB;IACzC,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ;QAC7B,CAAC,CAAC,MAAM,6CAAoB,CAAC,OAAO,EAAE;QACtC,CAAC,CAAC,MAAM,wCAA2B,CAAC,SAAS,EAAE,CAAC;IAElD,MAAM,UAAU,GAAG,yCAAkB,CAAC,aAAa,EAAE,CAAC;IACtD,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,UAAU,CAAC,CAAC;IAE5C,MAAM,YAAY,GAAG,MAAM,KAAK,CAAC,UAAU,EAAE,CAAC;IAE9C,MAAM,IAAI,cAAK,CACb,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC5B,KAAK,EAAE,KAAK,CAAC,IAAI;QACjB,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAA4B,EAAE;YACzD,MAAM,aAAa,GAAG,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAEzD,OAAO,UAAU,CAAC,QAAQ,CACxB;gBACE;oBACE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW;oBAC7C,IAAI,EAAE,KAAK,IAA2B,EAAE;wBACtC,IAAI,aAAa,EAAE;4BACjB,MAAM,IAAA,+BAAW,EAAC,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;4BAC5C,OAAO;yBACR;wBACD,MAAM,oCAAsB,CAAC,SAAS,CACpC,KAAK,EACL,KAAK,EACL,UAAU,CACX,CAAC;oBACJ,CAAC;iBACF;aACF,EACD,EAAE,UAAU,EAAE,KAAK,EAAE,CACtB,CAAC;QACJ,CAAC;KACF,CAAC,CAAC,EACH,EAAE,UAAU,EAAE,IAAI,EAAE,CACrB,CAAC,GAAG,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/B,MAAM,KAAK,CAAC,UAAU,EAAE,CAAC;AAC3B,CAAC,CAAC,CAAC"}
@@ -0,0 +1,34 @@
1
+ export declare const TopicSchema: import("@sinclair/typebox").TObject<{
2
+ name: import("@sinclair/typebox").TString;
3
+ spec: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
4
+ partitions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
5
+ replicationFactor: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
6
+ settings: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
7
+ 'cleanup.policy': import("@sinclair/typebox").TUnsafe<"delete" | "compact">;
8
+ }>>;
9
+ }>>>;
10
+ }>;
11
+ export declare const CliKafkaConfigSchema: import("@sinclair/typebox").TObject<{
12
+ topics: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
13
+ name: import("@sinclair/typebox").TString;
14
+ spec: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
15
+ partitions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
16
+ replicationFactor: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
17
+ settings: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
18
+ 'cleanup.policy': import("@sinclair/typebox").TUnsafe<"delete" | "compact">;
19
+ }>>;
20
+ }>>>;
21
+ }>>;
22
+ }>;
23
+ export declare const CliKafkaConfigSchemaChecker: import("@sinclair/typebox/compiler").TypeCheck<import("@sinclair/typebox").TObject<{
24
+ topics: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
25
+ name: import("@sinclair/typebox").TString;
26
+ spec: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
27
+ partitions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
28
+ replicationFactor: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
29
+ settings: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
30
+ 'cleanup.policy': import("@sinclair/typebox").TUnsafe<"delete" | "compact">;
31
+ }>>;
32
+ }>>>;
33
+ }>>;
34
+ }>>;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CliKafkaConfigSchemaChecker = exports.CliKafkaConfigSchema = exports.TopicSchema = void 0;
4
+ const typebox_1 = require("@sinclair/typebox");
5
+ const compiler_1 = require("@sinclair/typebox/compiler");
6
+ const string_enum_1 = require("./schemas/string-enum");
7
+ const specSchema = typebox_1.Type.Optional(typebox_1.Type.Partial(typebox_1.Type.Object({
8
+ partitions: typebox_1.Type.Integer({
9
+ default: 2,
10
+ }),
11
+ replicationFactor: typebox_1.Type.Integer({
12
+ default: 1,
13
+ }),
14
+ settings: typebox_1.Type.Object({
15
+ 'cleanup.policy': (0, string_enum_1.StringEnum)(['delete', 'compact']),
16
+ }),
17
+ })));
18
+ exports.TopicSchema = typebox_1.Type.Object({
19
+ name: typebox_1.Type.String(),
20
+ spec: typebox_1.Type.Optional(specSchema),
21
+ });
22
+ exports.CliKafkaConfigSchema = typebox_1.Type.Object({
23
+ topics: typebox_1.Type.Array(exports.TopicSchema),
24
+ });
25
+ exports.CliKafkaConfigSchemaChecker = compiler_1.TypeCompiler.Compile(exports.CliKafkaConfigSchema);
26
+ //# sourceMappingURL=config.schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.schema.js","sourceRoot":"","sources":["../../src/kafka/config.schema.ts"],"names":[],"mappings":";;;AAAA,+CAAyC;AACzC,yDAA0D;AAE1D,uDAAmD;AAEnD,MAAM,UAAU,GAAG,cAAI,CAAC,QAAQ,CAC9B,cAAI,CAAC,OAAO,CACV,cAAI,CAAC,MAAM,CAAC;IACV,UAAU,EAAE,cAAI,CAAC,OAAO,CAAC;QACvB,OAAO,EAAE,CAAC;KACX,CAAC;IACF,iBAAiB,EAAE,cAAI,CAAC,OAAO,CAAC;QAC9B,OAAO,EAAE,CAAC;KACX,CAAC;IACF,QAAQ,EAAE,cAAI,CAAC,MAAM,CAAC;QACpB,gBAAgB,EAAE,IAAA,wBAAU,EAAC,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;KACpD,CAAC;CACH,CAAC,CACH,CACF,CAAC;AAEW,QAAA,WAAW,GAAG,cAAI,CAAC,MAAM,CAAC;IACrC,IAAI,EAAE,cAAI,CAAC,MAAM,EAAE;IACnB,IAAI,EAAE,cAAI,CAAC,QAAQ,CAAC,UAAU,CAAC;CAChC,CAAC,CAAC;AAEU,QAAA,oBAAoB,GAAG,cAAI,CAAC,MAAM,CAAC;IAC9C,MAAM,EAAE,cAAI,CAAC,KAAK,CAAC,mBAAW,CAAC;CAChC,CAAC,CAAC;AACU,QAAA,2BAA2B,GACtC,uBAAY,CAAC,OAAO,CAAC,4BAAoB,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { TUnsafe } from '@sinclair/typebox';
2
+ export declare const StringEnum: <T extends string[]>(values: [...T]) => TUnsafe<T[number]>;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StringEnum = void 0;
4
+ const typebox_1 = require("@sinclair/typebox");
5
+ typebox_1.TypeRegistry.Set('StringEnum', (schema, value) => {
6
+ return schema.enum.includes(value);
7
+ });
8
+ const StringEnum = (values) => typebox_1.Type.Unsafe({
9
+ [typebox_1.Kind]: 'StringEnum',
10
+ type: 'string',
11
+ enum: values,
12
+ });
13
+ exports.StringEnum = StringEnum;
14
+ //# sourceMappingURL=string-enum.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"string-enum.js","sourceRoot":"","sources":["../../../src/kafka/schemas/string-enum.ts"],"names":[],"mappings":";;;AACA,+CAA6D;AAE7D,sBAAY,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,MAAW,EAAE,KAAU,EAAE,EAAE;IACzD,OAAO,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACrC,CAAC,CAAC,CAAC;AAEI,MAAM,UAAU,GAAG,CACxB,MAAc,EACM,EAAE,CACtB,cAAI,CAAC,MAAM,CAAY;IACrB,CAAC,cAAI,CAAC,EAAE,YAAY;IACpB,IAAI,EAAE,QAAQ;IACd,IAAI,EAAE,MAAM;CACb,CAAC,CAAC;AAPQ,QAAA,UAAU,cAOlB"}
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@rsdk/cli",
3
+ "version": "3.3.0-next.0",
4
+ "description": "entry package to cli functions",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "rsdk": "dist/bin.js"
8
+ },
9
+ "publishConfig": {
10
+ "access": "public"
11
+ },
12
+ "keywords": [
13
+ "rsdk",
14
+ "cli"
15
+ ],
16
+ "license": "Apache License 2.0",
17
+ "dependencies": {
18
+ "@sinclair/typebox": "^0.30.4",
19
+ "commander": "^11.0.0",
20
+ "dotenv": "^16.3.1",
21
+ "kafkajs": "^2.2.4",
22
+ "listr2": "^6.6.1",
23
+ "yaml": "^2.3.1"
24
+ },
25
+ "peerDependencies": {
26
+ "@rsdk/core": "^3.3.0-next.0"
27
+ },
28
+ "gitHead": "58a1a6d19248e79dfa11c8a25c1ead3be02710ff"
29
+ }
package/src/bin.ts ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ import { Command, program } from 'commander';
3
+
4
+ import { KafkaExtractCommand } from './kafka/commands/extract/extract.command';
5
+ import { SchemaCommand } from './kafka/commands/schema/schema.command';
6
+ import { KafkaSyncCommand } from './kafka/commands/sync/sync.command';
7
+
8
+ // TODO: сделать более понятным, переиспользуемым, консистентным и контролируем
9
+ process.env.RSDK_CLI_MODE = 'true';
10
+
11
+ const kafkaCommand = new Command('kafka');
12
+
13
+ program.addCommand(
14
+ kafkaCommand
15
+ .addCommand(KafkaSyncCommand)
16
+ .addCommand(KafkaExtractCommand)
17
+ .addCommand(SchemaCommand),
18
+ );
19
+
20
+ const execute = (): void => {
21
+ program.parse();
22
+ };
23
+
24
+ if (require.main === module) {
25
+ execute();
26
+ }
@@ -0,0 +1,16 @@
1
+ import { Command } from 'commander';
2
+
3
+ import { printDefaultOutFile, printSchema } from '../schema/print-schema';
4
+
5
+ import { KafkaConfigExtractor } from './kafka-config.extractor';
6
+
7
+ export const KafkaExtractCommand = new Command('extract')
8
+ .description('extract kafka.yml from rsdk.json')
9
+ .option('--with-schema', 'print and include $schema to yaml for autocomplete')
10
+ .action(async (_, cmd: Command) => {
11
+ const options = cmd.opts();
12
+ if (options.withSchema) {
13
+ await printSchema({ outFile: printDefaultOutFile, pretty: true });
14
+ }
15
+ await KafkaConfigExtractor.extractCommand(printDefaultOutFile);
16
+ });
@@ -0,0 +1,47 @@
1
+ import { InputException } from '@rsdk/core';
2
+ import type { Static } from '@sinclair/typebox';
3
+ import { readFile, writeFile } from 'node:fs/promises';
4
+ import { resolve } from 'node:path';
5
+ import yaml from 'yaml';
6
+
7
+ import type { CliKafkaConfigSchema } from '../../config.schema';
8
+
9
+ import type { Root } from './types';
10
+
11
+ export class KafkaConfigExtractor {
12
+ static async extractCommand(includeSchema?: string): Promise<void> {
13
+ const resultKafkaConfig = await this.extract();
14
+
15
+ await writeFile(
16
+ resolve('kafka.yml'),
17
+ yaml.stringify({
18
+ ...(includeSchema && { $schema: includeSchema }),
19
+ ...resultKafkaConfig,
20
+ }),
21
+ );
22
+ }
23
+
24
+ static async extract(): Promise<Static<typeof CliKafkaConfigSchema>> {
25
+ const pathToRsdkJson = resolve('autodoc/rsdk.json');
26
+ const rsdkJson: Root = JSON.parse(await readFile(pathToRsdkJson, 'utf8'));
27
+
28
+ const kafkaMetadata = rsdkJson.rsdkMetadata
29
+ .filter(({ scope }) => scope === 'kafka-topic')
30
+ .map(({ value }) => value);
31
+
32
+ const topics: Static<typeof CliKafkaConfigSchema>['topics'] =
33
+ kafkaMetadata.map((meta) => {
34
+ const topicName = meta.topicName;
35
+ if (!topicName) {
36
+ throw new InputException('topicName must be provided', {
37
+ details: meta,
38
+ });
39
+ }
40
+ return {
41
+ name: topicName,
42
+ };
43
+ });
44
+
45
+ return { topics };
46
+ }
47
+ }
@@ -0,0 +1,101 @@
1
+ export interface Root {
2
+ metadata: Metadata;
3
+ rsdkMetadata: RsdkMetadaum[];
4
+ }
5
+
6
+ export interface Metadata {
7
+ name: string;
8
+ version: string;
9
+ description: string;
10
+ protocols: string[];
11
+ config: Config;
12
+ }
13
+
14
+ export interface Config {
15
+ prefix: string;
16
+ sections: Section[];
17
+ sources: any[];
18
+ properties: any[];
19
+ }
20
+
21
+ export interface Section {
22
+ tags?: string[];
23
+ constructorName: string;
24
+ properties: Property[];
25
+ name?: string;
26
+ }
27
+
28
+ export interface Property {
29
+ expectedInEnv: boolean;
30
+ key: string;
31
+ description: string;
32
+ parser: Parser;
33
+ defaultValue: any;
34
+ }
35
+
36
+ export interface Parser {
37
+ type: string;
38
+ description: string;
39
+ }
40
+
41
+ export interface RsdkMetadaum {
42
+ scope: string;
43
+ value: Value;
44
+ }
45
+
46
+ export interface Value {
47
+ global?: boolean;
48
+ imports?: Import[];
49
+ providers?: Provider2[];
50
+ exports?: string[];
51
+ token?: string;
52
+ partitionKeyField: any;
53
+ topicName?: string;
54
+ group?: string;
55
+ type?: number;
56
+ eventType?: EventType;
57
+ options?: Options3;
58
+ constructorName?: string;
59
+ }
60
+
61
+ export interface Import {
62
+ providers: Provider[];
63
+ exports: Export[];
64
+ }
65
+
66
+ export interface Provider {
67
+ inject: Inject | undefined[];
68
+ }
69
+
70
+ export interface Inject {
71
+ optional: boolean;
72
+ }
73
+
74
+ export interface Export {
75
+ inject: Inject2 | undefined[];
76
+ }
77
+
78
+ export interface Inject2 {
79
+ optional: boolean;
80
+ }
81
+
82
+ export interface Provider2 {
83
+ provide: string;
84
+ useValue: UseValue;
85
+ }
86
+
87
+ export interface UseValue {
88
+ context: string;
89
+ }
90
+
91
+ export interface EventType {
92
+ $group: string;
93
+ $partitionKeyField: any;
94
+ $type: string;
95
+ }
96
+
97
+ export interface Options3 {
98
+ key: string;
99
+ scope: string;
100
+ description: string;
101
+ }
@@ -0,0 +1,15 @@
1
+ import { writeFile } from 'node:fs/promises';
2
+
3
+ import { CliKafkaConfigSchema } from '../../config.schema';
4
+
5
+ export const printDefaultOutFile = './kafka.schema.json';
6
+
7
+ export async function printSchema(options?: {
8
+ outFile?: string;
9
+ pretty?: boolean;
10
+ }): Promise<void> {
11
+ await writeFile(
12
+ options?.outFile ?? printDefaultOutFile,
13
+ JSON.stringify(CliKafkaConfigSchema, null, options?.pretty ? 2 : 0),
14
+ );
15
+ }
@@ -0,0 +1,10 @@
1
+ import { Command } from 'commander';
2
+
3
+ import { printSchema } from './print-schema';
4
+
5
+ export const SchemaCommand = new Command('schema')
6
+ .description('print json schema for kafka.yml')
7
+ .option('-o, --out-file', 'output file name', 'kafka.schema.json')
8
+ .action(async (_, cmd: Command) => {
9
+ await printSchema(cmd.opts().outFile);
10
+ });
@@ -0,0 +1,68 @@
1
+ import { InternalException } from '@rsdk/core';
2
+ import type { Static } from '@sinclair/typebox';
3
+ import type { Admin } from 'kafkajs';
4
+ import { ConfigResourceTypes } from 'kafkajs';
5
+ import type { Listr, ListrRendererFactory, ListrTaskWrapper } from 'listr2';
6
+ import { delay } from 'listr2';
7
+
8
+ import type { TopicSchema } from '../../config.schema';
9
+
10
+ export class KafkaTopicConfigurator {
11
+ static async configure(
12
+ topic: Static<typeof TopicSchema>,
13
+ admin: Admin,
14
+ parentTask: ListrTaskWrapper<unknown, ListrRendererFactory>,
15
+ ): Promise<Listr | void> {
16
+ const metadata = await admin.fetchTopicMetadata({
17
+ topics: [topic.name],
18
+ });
19
+ const partitions = metadata.topics[0].partitions.length;
20
+ if (topic.spec?.partitions && partitions !== topic.spec.partitions) {
21
+ throw new Error(
22
+ `partitoins for ${topic.name} settled to ${partitions}, but expected ${topic.spec.partitions}`,
23
+ );
24
+ }
25
+ const topicConfig = await admin.describeConfigs({
26
+ resources: [
27
+ {
28
+ type: ConfigResourceTypes.TOPIC,
29
+ configNames: ['cleanup.policy'],
30
+ name: topic.name,
31
+ },
32
+ ],
33
+ includeSynonyms: true,
34
+ });
35
+
36
+ const cleanupPolicy = topicConfig.resources.find(
37
+ ({ resourceName }) => resourceName === topic.name,
38
+ );
39
+ if (!cleanupPolicy) {
40
+ throw new InternalException('cleanupPolicy not found');
41
+ }
42
+ const currentValue = cleanupPolicy?.configEntries[0].configValue;
43
+ const configValue = topic?.spec?.settings?.['cleanup.policy'];
44
+
45
+ if (configValue && currentValue !== configValue) {
46
+ parentTask.output = `Alter cleanup.policy from ${currentValue} to ${configValue}`;
47
+ await delay(3e3);
48
+ await admin.alterConfigs({
49
+ validateOnly: false,
50
+ resources: [
51
+ {
52
+ type: ConfigResourceTypes.TOPIC,
53
+ configEntries: [
54
+ {
55
+ value: configValue,
56
+ name: 'cleanup.policy',
57
+ },
58
+ ],
59
+ name: topic.name,
60
+ },
61
+ ],
62
+ });
63
+ parentTask.output = `Alter cleanup.policy from ${currentValue} to ${configValue} completed!`;
64
+ await delay(3e3);
65
+ }
66
+ parentTask.title = `topic ${topic.name} exists and respond config`;
67
+ }
68
+ }
@@ -0,0 +1,3 @@
1
+ export type Context = {
2
+ requireCreate: boolean;
3
+ };
@@ -0,0 +1,35 @@
1
+ import type { Static } from '@sinclair/typebox';
2
+ import type { Admin } from 'kafkajs';
3
+ import type { ListrRendererFactory, ListrTaskWrapper } from 'listr2';
4
+ import { delay } from 'listr2';
5
+
6
+ import type { TopicSchema } from '../../config.schema';
7
+
8
+ export async function createTopic(
9
+ topic: Static<typeof TopicSchema>,
10
+ admin: Admin,
11
+ parentTask: ListrTaskWrapper<unknown, ListrRendererFactory>,
12
+ ): Promise<void> {
13
+ await delay(1000);
14
+ const numPartitions = topic.spec?.partitions ?? 2;
15
+
16
+ const replicationFactor = topic.spec?.replicationFactor;
17
+
18
+ const result = await admin.createTopics({
19
+ topics: [
20
+ {
21
+ topic: topic.name,
22
+ ...(replicationFactor && {
23
+ replicationFactor: replicationFactor,
24
+ }),
25
+ numPartitions: numPartitions,
26
+ },
27
+ ],
28
+ });
29
+ if (result) {
30
+ console.log('create topic %s complete!', topic.name);
31
+ parentTask.title = `create topic ${topic.name} complete!`;
32
+ return;
33
+ }
34
+ throw new Error(`create topic ${topic.name} failed!`);
35
+ }
@@ -0,0 +1,25 @@
1
+ import { InputException } from '@rsdk/core';
2
+ import type { Static } from '@sinclair/typebox';
3
+ import { readFile } from 'node:fs/promises';
4
+ import { resolve } from 'node:path';
5
+ import yaml from 'yaml';
6
+
7
+ import type { CliKafkaConfigSchema } from '../../config.schema';
8
+ import { CliKafkaConfigSchemaChecker } from '../../config.schema';
9
+
10
+ export class KafkaExpectedConfigProvider {
11
+ static async getConfig(): Promise<Static<typeof CliKafkaConfigSchema>> {
12
+ const kafkaYml = await readFile(resolve('kafka.yml'), {
13
+ encoding: 'utf8',
14
+ });
15
+ const config: Static<typeof CliKafkaConfigSchema> = yaml.parse(kafkaYml);
16
+
17
+ const isValid = CliKafkaConfigSchemaChecker.Check(config);
18
+ if (!isValid) {
19
+ throw new InputException('Invalid config', {
20
+ cause: [...CliKafkaConfigSchemaChecker.Errors(config)],
21
+ });
22
+ }
23
+ return config;
24
+ }
25
+ }
@@ -0,0 +1,21 @@
1
+ import { InternalException } from '@rsdk/core';
2
+ import dotenv from 'dotenv';
3
+ import type { KafkaConfig } from 'kafkajs';
4
+
5
+ export class KafkaCredsProvider {
6
+ static getKafkaCreds(): KafkaConfig {
7
+ const parsed = dotenv.config();
8
+ if (parsed.error) {
9
+ throw new InternalException('Error read config', {
10
+ cause: parsed.error,
11
+ });
12
+ }
13
+ const kafkaHost = parsed.parsed?.KAFKA_HOST;
14
+ if (!kafkaHost) {
15
+ throw new InternalException('KAFKA_HOST not found');
16
+ }
17
+ return {
18
+ brokers: [kafkaHost],
19
+ };
20
+ }
21
+ }
@@ -0,0 +1,66 @@
1
+ import { Command } from 'commander';
2
+ import type { Admin, KafkaConfig } from 'kafkajs';
3
+ import { Kafka } from 'kafkajs';
4
+ import { Listr } from 'listr2';
5
+
6
+ import { KafkaConfigExtractor } from '../extract/kafka-config.extractor';
7
+
8
+ import { KafkaTopicConfigurator } from './check-exist';
9
+ import type { Context } from './context';
10
+ import { createTopic } from './create-topic.task';
11
+ import { KafkaExpectedConfigProvider } from './get-config';
12
+ import { KafkaCredsProvider } from './kafka.creds-provider';
13
+
14
+ async function createAdmin(kafkaCreds: KafkaConfig): Promise<Admin> {
15
+ const kafka = new Kafka(kafkaCreds);
16
+ const admin = kafka.admin();
17
+
18
+ await admin.connect();
19
+ return admin;
20
+ }
21
+
22
+ export const KafkaSyncCommand = new Command('sync')
23
+ .description('try sync kafka state from kafka.yml file')
24
+ .option('--from-rsdk', 'run command based on rsdk.json, instead of kafka.yml')
25
+ .action(async function (_, command: Command) {
26
+ const options = command.opts();
27
+ const config = options.fromRsdk
28
+ ? await KafkaConfigExtractor.extract()
29
+ : await KafkaExpectedConfigProvider.getConfig();
30
+
31
+ const kafkaCreds = KafkaCredsProvider.getKafkaCreds();
32
+ const admin = await createAdmin(kafkaCreds);
33
+
34
+ const existedTopic = await admin.listTopics();
35
+
36
+ await new Listr<Context>(
37
+ config.topics.map((topic) => ({
38
+ title: topic.name,
39
+ task: async (_ctx, parentTask): Promise<boolean | Listr> => {
40
+ const requireCreate = !existedTopic.includes(topic.name);
41
+
42
+ return parentTask.newListr(
43
+ [
44
+ {
45
+ title: requireCreate ? 'create' : 'configure',
46
+ task: async (): Promise<void | Listr> => {
47
+ if (requireCreate) {
48
+ await createTopic(topic, admin, parentTask);
49
+ return;
50
+ }
51
+ await KafkaTopicConfigurator.configure(
52
+ topic,
53
+ admin,
54
+ parentTask,
55
+ );
56
+ },
57
+ },
58
+ ],
59
+ { concurrent: false },
60
+ );
61
+ },
62
+ })),
63
+ { concurrent: true },
64
+ ).run({ requireCreate: true });
65
+ await admin.disconnect();
66
+ });
@@ -0,0 +1,31 @@
1
+ import { Type } from '@sinclair/typebox';
2
+ import { TypeCompiler } from '@sinclair/typebox/compiler';
3
+
4
+ import { StringEnum } from './schemas/string-enum';
5
+
6
+ const specSchema = Type.Optional(
7
+ Type.Partial(
8
+ Type.Object({
9
+ partitions: Type.Integer({
10
+ default: 2,
11
+ }),
12
+ replicationFactor: Type.Integer({
13
+ default: 1,
14
+ }),
15
+ settings: Type.Object({
16
+ 'cleanup.policy': StringEnum(['delete', 'compact']),
17
+ }),
18
+ }),
19
+ ),
20
+ );
21
+
22
+ export const TopicSchema = Type.Object({
23
+ name: Type.String(),
24
+ spec: Type.Optional(specSchema),
25
+ });
26
+
27
+ export const CliKafkaConfigSchema = Type.Object({
28
+ topics: Type.Array(TopicSchema),
29
+ });
30
+ export const CliKafkaConfigSchemaChecker =
31
+ TypeCompiler.Compile(CliKafkaConfigSchema);
@@ -0,0 +1,15 @@
1
+ import type { TUnsafe } from '@sinclair/typebox';
2
+ import { Kind, Type, TypeRegistry } from '@sinclair/typebox';
3
+
4
+ TypeRegistry.Set('StringEnum', (schema: any, value: any) => {
5
+ return schema.enum.includes(value);
6
+ });
7
+
8
+ export const StringEnum = <T extends string[]>(
9
+ values: [...T],
10
+ ): TUnsafe<T[number]> =>
11
+ Type.Unsafe<T[number]>({
12
+ [Kind]: 'StringEnum',
13
+ type: 'string',
14
+ enum: values,
15
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "@rsdk/tsconfig/base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src",
6
+ }
7
+ }