@rsdk/kafka.producer.outbox 4.0.0-next.10

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/CHANGELOG.md ADDED
@@ -0,0 +1,18 @@
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
+ ## [4.0.0-next.10](https://github.com/R-Vision/rsdk/compare/v4.0.0-next.9...v4.0.0-next.10) (2023-11-29)
7
+
8
+ **Note:** Version bump only for package @rsdk/kafka.producer.outbox
9
+
10
+ ## [4.0.0-next.9](https://github.com/R-Vision/rsdk/compare/v4.0.0-next.8...v4.0.0-next.9) (2023-11-29)
11
+
12
+ **Note:** Version bump only for package @rsdk/kafka.producer.outbox
13
+
14
+ ## [4.0.0-next.8](https://github.com/R-Vision/rsdk/compare/v4.0.0-next.7...v4.0.0-next.8) (2023-11-29)
15
+
16
+ ### Features
17
+
18
+ * Kafka batch producer ([#150](https://github.com/R-Vision/rsdk/issues/150)) ([2faa2f4](https://github.com/R-Vision/rsdk/commit/2faa2f41ca0dbb6d8a92cbf0ab20cf5cefeaf4e9))
@@ -0,0 +1,11 @@
1
+ import type { MaybeReadonlyArray } from '@rsdk/common.node';
2
+ import type { EventType } from '@rsdk/kafka.common';
3
+ /**
4
+ * Имя outbox-таблицы по умолчанию
5
+ */
6
+ export declare const DEFAULT_OUTBOX_TABLE_NAME = "outbox";
7
+ export declare const OUTBOX_FACTORY_PROVIDER_TOKEN = "OUTBOX_FACTORY_PROVIDER";
8
+ export declare const BATCH_OUTBOX_FACTORY_PROVIDER_TOKEN = "BATCH_OUTBOX_FACTORY_PROVIDER";
9
+ export declare const createOutboxProducerToken: <T extends EventType<any>>(eventType: T) => string;
10
+ export declare const createOutboxBatchProducerToken: <T extends MaybeReadonlyArray<EventType<any>>>(eventType: T) => string;
11
+ export declare const OUTBOX_RSDK_METADATA_SCOPE = "OUTBOX_RSDK_METADATA_SCOPE";
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OUTBOX_RSDK_METADATA_SCOPE = exports.createOutboxBatchProducerToken = exports.createOutboxProducerToken = exports.BATCH_OUTBOX_FACTORY_PROVIDER_TOKEN = exports.OUTBOX_FACTORY_PROVIDER_TOKEN = exports.DEFAULT_OUTBOX_TABLE_NAME = void 0;
4
+ const common_1 = require("@rsdk/common");
5
+ /**
6
+ * Имя outbox-таблицы по умолчанию
7
+ */
8
+ exports.DEFAULT_OUTBOX_TABLE_NAME = 'outbox';
9
+ exports.OUTBOX_FACTORY_PROVIDER_TOKEN = 'OUTBOX_FACTORY_PROVIDER';
10
+ exports.BATCH_OUTBOX_FACTORY_PROVIDER_TOKEN = 'BATCH_OUTBOX_FACTORY_PROVIDER';
11
+ const createOutboxProducerToken = (eventType) => `KAFKA_OUTBOX_PRODUCER_${common_1.Case.toUpperSnake(eventType.$type)}`;
12
+ exports.createOutboxProducerToken = createOutboxProducerToken;
13
+ const createOutboxBatchProducerToken = (eventType) => `KAFKA_OUTBOX_BATCH_PRODUCER_${common_1.Case.toUpperSnake(eventType
14
+ .map((e) => e.$type)
15
+ .sort()
16
+ .join('_'))}`;
17
+ exports.createOutboxBatchProducerToken = createOutboxBatchProducerToken;
18
+ exports.OUTBOX_RSDK_METADATA_SCOPE = 'OUTBOX_RSDK_METADATA_SCOPE';
19
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";;;AAAA,yCAAoC;AAIpC;;GAEG;AACU,QAAA,yBAAyB,GAAG,QAAQ,CAAC;AACrC,QAAA,6BAA6B,GAAG,yBAAyB,CAAC;AAC1D,QAAA,mCAAmC,GAC9C,+BAA+B,CAAC;AAC3B,MAAM,yBAAyB,GAAG,CACvC,SAAY,EACJ,EAAE,CAAC,yBAAyB,aAAI,CAAC,YAAY,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;AAF9D,QAAA,yBAAyB,6BAEqC;AAEpE,MAAM,8BAA8B,GAAG,CAG5C,SAAY,EACJ,EAAE,CACV,+BAA+B,aAAI,CAAC,YAAY,CAC9C,SAAS;KACN,GAAG,CAAC,CAAC,CAAY,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;KAC9B,IAAI,EAAE;KACN,IAAI,CAAC,GAAG,CAAC,CACb,EAAE,CAAC;AAVO,QAAA,8BAA8B,kCAUrC;AACO,QAAA,0BAA0B,GAAG,4BAA4B,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { MaybeReadonlyArray } from '@rsdk/common.node';
2
+ import type { EventType } from '@rsdk/kafka.common';
3
+ export declare const InjectOutboxProducer: (eventType: EventType) => ParameterDecorator;
4
+ export declare const InjectOutboxBatchProducer: (eventType: MaybeReadonlyArray<EventType>) => ParameterDecorator;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InjectOutboxBatchProducer = exports.InjectOutboxProducer = void 0;
4
+ const common_1 = require("@nestjs/common");
5
+ const metadata_1 = require("@rsdk/metadata");
6
+ const constants_1 = require("./constants");
7
+ const outbox_module_generator_1 = require("./outbox.module-generator");
8
+ const InjectOutboxProducer = (eventType) => {
9
+ return (target, propertyKey, parameterIndex) => {
10
+ const producerToken = (0, constants_1.createOutboxProducerToken)(eventType);
11
+ (0, common_1.Inject)(producerToken)(target, propertyKey, parameterIndex);
12
+ metadata_1.RsdkMetadata.setWithScope(target, metadata_1.PLATFORM_RAW_GLOBAL_METADATA_SCOPE, producerToken, outbox_module_generator_1.OutboxModuleGenerator.createOutboxModuleDefinition(eventType), true);
13
+ };
14
+ };
15
+ exports.InjectOutboxProducer = InjectOutboxProducer;
16
+ const InjectOutboxBatchProducer = (eventType) => {
17
+ return (target, propertyKey, parameterIndex) => {
18
+ const batchProducerToken = (0, constants_1.createOutboxBatchProducerToken)(eventType);
19
+ (0, common_1.Inject)(batchProducerToken)(target, propertyKey, parameterIndex);
20
+ metadata_1.RsdkMetadata.setWithScope(target, metadata_1.PLATFORM_RAW_GLOBAL_METADATA_SCOPE, batchProducerToken, outbox_module_generator_1.OutboxModuleGenerator.createBatchOutboxModuleDefinition(eventType), true);
21
+ };
22
+ };
23
+ exports.InjectOutboxBatchProducer = InjectOutboxBatchProducer;
24
+ //# sourceMappingURL=decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorator.js","sourceRoot":"","sources":["../src/decorator.ts"],"names":[],"mappings":";;;AAAA,2CAAwC;AAGxC,6CAGwB;AAExB,2CAGqB;AACrB,uEAAkE;AAE3D,MAAM,oBAAoB,GAAG,CAClC,SAAoB,EACA,EAAE;IACtB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,EAAE;QAC7C,MAAM,aAAa,GAAG,IAAA,qCAAyB,EAAC,SAAS,CAAC,CAAC;QAE3D,IAAA,eAAM,EAAC,aAAa,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,cAAc,CAAC,CAAC;QAC3D,uBAAY,CAAC,YAAY,CACvB,MAAM,EACN,6CAAkC,EAClC,aAAa,EACb,+CAAqB,CAAC,4BAA4B,CAAC,SAAS,CAAC,EAC7D,IAAI,CACL,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC,CAAC;AAfW,QAAA,oBAAoB,wBAe/B;AAEK,MAAM,yBAAyB,GAAG,CACvC,SAAwC,EACpB,EAAE;IACtB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,EAAE;QAC7C,MAAM,kBAAkB,GAAG,IAAA,0CAA8B,EAAC,SAAS,CAAC,CAAC;QAErE,IAAA,eAAM,EAAC,kBAAkB,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,cAAc,CAAC,CAAC;QAChE,uBAAY,CAAC,YAAY,CACvB,MAAM,EACN,6CAAkC,EAClC,kBAAkB,EAClB,+CAAqB,CAAC,iCAAiC,CAAC,SAAS,CAAC,EAClE,IAAI,CACL,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC,CAAC;AAfW,QAAA,yBAAyB,6BAepC"}
@@ -0,0 +1,4 @@
1
+ export { InjectOutboxProducer, InjectOutboxBatchProducer } from './decorator';
2
+ export * from './outbox-events.factory';
3
+ export * from './constants';
4
+ export * from './types';
package/dist/index.js ADDED
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.InjectOutboxBatchProducer = exports.InjectOutboxProducer = void 0;
18
+ var decorator_1 = require("./decorator");
19
+ Object.defineProperty(exports, "InjectOutboxProducer", { enumerable: true, get: function () { return decorator_1.InjectOutboxProducer; } });
20
+ Object.defineProperty(exports, "InjectOutboxBatchProducer", { enumerable: true, get: function () { return decorator_1.InjectOutboxBatchProducer; } });
21
+ __exportStar(require("./outbox-events.factory"), exports);
22
+ __exportStar(require("./constants"), exports);
23
+ __exportStar(require("./types"), exports);
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,yCAA8E;AAArE,iHAAA,oBAAoB,OAAA;AAAE,sHAAA,yBAAyB,OAAA;AAExD,0DAAwC;AACxC,8CAA4B;AAC5B,0CAAwB"}
@@ -0,0 +1,10 @@
1
+ import type { EventPayload, EventType } from '@rsdk/kafka.common';
2
+ import type { PublishOptions } from '@rsdk/kafka.producer';
3
+ import { EventsFactory } from '@rsdk/kafka.producer';
4
+ import type { OutboxTable } from './types';
5
+ /**
6
+ * Подготавливает данные для вставки в таблицу outbox
7
+ */
8
+ export declare class OutboxEventsFactory<E extends EventType> extends EventsFactory<E, OutboxTable> {
9
+ create<P extends EventPayload<E> = EventPayload<E>>(payload: P, options?: PublishOptions): OutboxTable;
10
+ }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OutboxEventsFactory = void 0;
4
+ const kafka_producer_1 = require("@rsdk/kafka.producer");
5
+ const node_crypto_1 = require("node:crypto");
6
+ /**
7
+ * Подготавливает данные для вставки в таблицу outbox
8
+ */
9
+ class OutboxEventsFactory extends kafka_producer_1.EventsFactory {
10
+ create(payload, options) {
11
+ return {
12
+ id: (0, node_crypto_1.randomUUID)(),
13
+ group: this.eventType.$group,
14
+ metadata: this.getMetadata(options?.metadata),
15
+ partition_key: this.getPartitionKey(this.eventType, options),
16
+ payload: this.serializePayload(payload),
17
+ type: this.eventType.$type,
18
+ };
19
+ }
20
+ }
21
+ exports.OutboxEventsFactory = OutboxEventsFactory;
22
+ //# sourceMappingURL=outbox-events.factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox-events.factory.js","sourceRoot":"","sources":["../src/outbox-events.factory.ts"],"names":[],"mappings":";;;AAEA,yDAAqD;AACrD,6CAAyC;AAIzC;;GAEG;AACH,MAAa,mBAAyC,SAAQ,8BAG7D;IACC,MAAM,CACJ,OAAU,EACV,OAAwB;QAExB,OAAO;YACL,EAAE,EAAE,IAAA,wBAAU,GAAE;YAChB,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM;YAC5B,QAAQ,EAAE,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,QAAQ,CAAC;YAC7C,aAAa,EAAE,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC;YAC5D,OAAO,EAAE,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC;YACvC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK;SAC3B,CAAC;IACJ,CAAC;CACF;AAjBD,kDAiBC"}
@@ -0,0 +1,8 @@
1
+ import type { DocumentResolver } from '@rsdk/autodoc.protocol';
2
+ import { Composite } from '@rsdk/autodoc.protocol';
3
+ import type { RsdkMetadataProvider } from '@rsdk/metadata';
4
+ declare class OutboxAutodocResolver implements DocumentResolver {
5
+ getNode(rsdkMetadataProvider: RsdkMetadataProvider): Composite | null;
6
+ }
7
+ export declare const outboxAutodocResolver: OutboxAutodocResolver;
8
+ export {};
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.outboxAutodocResolver = void 0;
4
+ const autodoc_protocol_1 = require("@rsdk/autodoc.protocol");
5
+ const kafka_common_1 = require("@rsdk/kafka.common");
6
+ const constants_1 = require("./constants");
7
+ class OutboxAutodocResolver {
8
+ getNode(rsdkMetadataProvider) {
9
+ const resources = rsdkMetadataProvider.get(constants_1.OUTBOX_RSDK_METADATA_SCOPE);
10
+ if (!resources) {
11
+ return null;
12
+ }
13
+ return new autodoc_protocol_1.Composite(new autodoc_protocol_1.Table(resources.map((resource) => {
14
+ const { $type, $partitionKeyField } = resource.value.eventType;
15
+ return {
16
+ Type: $type,
17
+ TopicName: (0, kafka_common_1.getTopicName)(resource.value.eventType),
18
+ PartitionKeyField: $partitionKeyField ?? '-',
19
+ };
20
+ })), 'Kafka outbox');
21
+ }
22
+ }
23
+ exports.outboxAutodocResolver = new OutboxAutodocResolver();
24
+ //# sourceMappingURL=outbox.autodoc-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox.autodoc-resolver.js","sourceRoot":"","sources":["../src/outbox.autodoc-resolver.ts"],"names":[],"mappings":";;;AACA,6DAA0D;AAC1D,qDAAkD;AAGlD,2CAAyD;AAGzD,MAAM,qBAAqB;IACzB,OAAO,CAAC,oBAA0C;QAChD,MAAM,SAAS,GAAG,oBAAoB,CAAC,GAAG,CACxC,sCAA0B,CAC3B,CAAC;QAEF,IAAI,CAAC,SAAS,EAAE;YACd,OAAO,IAAI,CAAC;SACb;QAED,OAAO,IAAI,4BAAS,CAClB,IAAI,wBAAK,CACP,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE;YACzB,MAAM,EAAE,KAAK,EAAE,kBAAkB,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC;YAE/D,OAAO;gBACL,IAAI,EAAE,KAAK;gBACX,SAAS,EAAE,IAAA,2BAAY,EAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC;gBACjD,iBAAiB,EAAE,kBAAkB,IAAI,GAAG;aAC7C,CAAC;QACJ,CAAC,CAAC,CACH,EACD,cAAc,CACf,CAAC;IACJ,CAAC;CACF;AAEY,QAAA,qBAAqB,GAAG,IAAI,qBAAqB,EAAE,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { DynamicModule } from '@nestjs/common';
2
+ import type { MaybeReadonlyArray } from '@rsdk/common.node';
3
+ import type { EventType } from '@rsdk/kafka.common';
4
+ export declare class OutboxModuleGenerator {
5
+ static createOutboxModuleDefinition(eventType: EventType): DynamicModule;
6
+ static createBatchOutboxModuleDefinition(eventTypes: MaybeReadonlyArray<EventType>): DynamicModule;
7
+ }
@@ -0,0 +1,70 @@
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.OutboxModuleGenerator = void 0;
7
+ const autodoc_protocol_1 = require("@rsdk/autodoc.protocol");
8
+ const kafka_producer_1 = require("@rsdk/kafka.producer");
9
+ const metadata_1 = require("@rsdk/metadata");
10
+ const omit_1 = __importDefault(require("lodash/omit"));
11
+ const constants_1 = require("./constants");
12
+ const outbox_autodoc_resolver_1 = require("./outbox.autodoc-resolver");
13
+ class OutboxModuleGenerator {
14
+ static createOutboxModuleDefinition(eventType) {
15
+ const providers = [
16
+ {
17
+ inject: [constants_1.OUTBOX_FACTORY_PROVIDER_TOKEN, kafka_producer_1.ProducerConfig],
18
+ /**
19
+ * TODO: А если пользователь будет использовать одновременно knex и typeorm?
20
+ * Сейчас произойдёт неопределённое поведение. А надо наверное ошибку кидать?
21
+ * (В целом следуем концепции того что в приложении не должно быть два плагина для работы с СУБД)
22
+ */
23
+ provide: (0, constants_1.createOutboxProducerToken)(eventType),
24
+ useFactory: (outboxProducerFactory, config) => outboxProducerFactory.createProducer(eventType, config),
25
+ },
26
+ ];
27
+ const moduleDef = {
28
+ providers,
29
+ module: OutboxModuleGenerator,
30
+ exports: providers,
31
+ };
32
+ const rsdkMetadata = new metadata_1.RsdkMetadata(moduleDef, constants_1.OUTBOX_RSDK_METADATA_SCOPE);
33
+ rsdkMetadata.add({
34
+ eventType,
35
+ eventData: (0, omit_1.default)(eventType, 'toJSON'),
36
+ });
37
+ autodoc_protocol_1.AutodocMetadata.defineResolver(moduleDef, constants_1.OUTBOX_RSDK_METADATA_SCOPE, outbox_autodoc_resolver_1.outboxAutodocResolver);
38
+ return moduleDef;
39
+ }
40
+ static createBatchOutboxModuleDefinition(eventTypes) {
41
+ const providers = [
42
+ {
43
+ inject: [constants_1.BATCH_OUTBOX_FACTORY_PROVIDER_TOKEN, kafka_producer_1.ProducerConfig],
44
+ /**
45
+ * TODO: А если пользователь будет использовать одновременно knex и typeorm?
46
+ * Сейчас произойдёт неопределённое поведение. А надо наверное ошибку кидать?
47
+ * (В целом следуем концепции того что в приложении не должно быть два плагина для работы с СУБД)
48
+ */
49
+ provide: (0, constants_1.createOutboxBatchProducerToken)(eventTypes),
50
+ useFactory: (outboxProducerFactory, config) => outboxProducerFactory.createBatchProducer(eventTypes, config),
51
+ },
52
+ ];
53
+ const moduleDef = {
54
+ providers,
55
+ module: OutboxModuleGenerator,
56
+ exports: providers,
57
+ };
58
+ const rsdkMetadata = new metadata_1.RsdkMetadata(moduleDef, constants_1.OUTBOX_RSDK_METADATA_SCOPE);
59
+ for (const eventType of eventTypes) {
60
+ rsdkMetadata.add({
61
+ eventType: eventType,
62
+ eventData: (0, omit_1.default)(eventType, 'toJSON'),
63
+ });
64
+ }
65
+ autodoc_protocol_1.AutodocMetadata.defineResolver(moduleDef, constants_1.OUTBOX_RSDK_METADATA_SCOPE, outbox_autodoc_resolver_1.outboxAutodocResolver);
66
+ return moduleDef;
67
+ }
68
+ }
69
+ exports.OutboxModuleGenerator = OutboxModuleGenerator;
70
+ //# sourceMappingURL=outbox.module-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox.module-generator.js","sourceRoot":"","sources":["../src/outbox.module-generator.ts"],"names":[],"mappings":";;;;;;AACA,6DAAyD;AAIzD,yDAAsD;AACtD,6CAA8C;AAC9C,uDAA+B;AAE/B,2CAMqB;AACrB,uEAAkE;AAOlE,MAAa,qBAAqB;IAChC,MAAM,CAAC,4BAA4B,CAAC,SAAoB;QACtD,MAAM,SAAS,GAAG;YAChB;gBACE,MAAM,EAAE,CAAC,yCAA6B,EAAE,+BAAc,CAAC;gBACvD;;;;mBAIG;gBACH,OAAO,EAAE,IAAA,qCAAyB,EAAC,SAAS,CAAC;gBAC7C,UAAU,EAAE,CACV,qBAAiD,EACjD,MAAsB,EACI,EAAE,CAC5B,qBAAqB,CAAC,cAAc,CAAC,SAAS,EAAE,MAAM,CAAC;aAC1D;SACF,CAAC;QACF,MAAM,SAAS,GAAkB;YAC/B,SAAS;YACT,MAAM,EAAE,qBAAqB;YAC7B,OAAO,EAAE,SAAS;SACnB,CAAC;QAEF,MAAM,YAAY,GAAG,IAAI,uBAAY,CACnC,SAAS,EACT,sCAA0B,CAC3B,CAAC;QAEF,YAAY,CAAC,GAAG,CAAC;YACf,SAAS;YACT,SAAS,EAAE,IAAA,cAAI,EAAC,SAAS,EAAE,QAAQ,CAAC;SACrC,CAAC,CAAC;QAEH,kCAAe,CAAC,cAAc,CAC5B,SAAS,EACT,sCAA0B,EAC1B,+CAAqB,CACtB,CAAC;QACF,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,CAAC,iCAAiC,CACtC,UAAyC;QAEzC,MAAM,SAAS,GAAG;YAChB;gBACE,MAAM,EAAE,CAAC,+CAAmC,EAAE,+BAAc,CAAC;gBAC7D;;;;mBAIG;gBACH,OAAO,EAAE,IAAA,0CAA8B,EAAC,UAAU,CAAC;gBACnD,UAAU,EAAE,CACV,qBAAsD,EACtD,MAAsB,EACI,EAAE,CAC5B,qBAAqB,CAAC,mBAAmB,CAAC,UAAU,EAAE,MAAM,CAAC;aAChE;SACF,CAAC;QACF,MAAM,SAAS,GAAkB;YAC/B,SAAS;YACT,MAAM,EAAE,qBAAqB;YAC7B,OAAO,EAAE,SAAS;SACnB,CAAC;QAEF,MAAM,YAAY,GAAG,IAAI,uBAAY,CACnC,SAAS,EACT,sCAA0B,CAC3B,CAAC;QAEF,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE;YAClC,YAAY,CAAC,GAAG,CAAC;gBACf,SAAS,EAAE,SAAS;gBACpB,SAAS,EAAE,IAAA,cAAI,EAAC,SAAS,EAAE,QAAQ,CAAC;aACrC,CAAC,CAAC;SACJ;QAED,kCAAe,CAAC,cAAc,CAC5B,SAAS,EACT,sCAA0B,EAC1B,+CAAqB,CACtB,CAAC;QACF,OAAO,SAAS,CAAC;IACnB,CAAC;CACF;AAtFD,sDAsFC"}
@@ -0,0 +1,51 @@
1
+ /// <reference types="node" />
2
+ import type { EventType } from '@rsdk/kafka.common';
3
+ import type { KafkaBatchProducer, KafkaProducer, ProducerConfig, ProducerOptions } from '@rsdk/kafka.producer';
4
+ /**
5
+ * Структура таблицы outbox (имя может быть переопределено)
6
+ */
7
+ export interface OutboxTable {
8
+ /**
9
+ * Идентификатор события (UUID)
10
+ */
11
+ id: string;
12
+ /**
13
+ * Тип события: FQN из protobuf
14
+ */
15
+ type: string;
16
+ /**
17
+ * Группа - указывается в опции event в protobuf
18
+ */
19
+ group: string;
20
+ /**
21
+ * Ключ партиционирования. Вычисляется в зависимости от
22
+ * переданной опции partitionKeyField (в protobuf) и структуры события.
23
+ * Может быть явно задан при публикации.
24
+ */
25
+ partition_key: string;
26
+ /**
27
+ * Метаданные события (включая служебные)
28
+ *
29
+ * NOTE: Здесь используется any, а не unknown из-за особенностей
30
+ * типизации typeorm
31
+ */
32
+ metadata: Record<string, any> | null;
33
+ /**
34
+ * Сериализованные данные события
35
+ */
36
+ payload: Buffer;
37
+ }
38
+ export interface OutboxProducerOptions extends ProducerOptions {
39
+ /** Переопределяет имя таблицы (default: "outbox") */
40
+ tableName?: string;
41
+ }
42
+ export type OutboxMetadata = {
43
+ eventType: EventType;
44
+ eventData: Omit<EventType, 'toJSON'>;
45
+ };
46
+ export interface OutboxProducerFactory<T extends EventType> {
47
+ createProducer(eventType: T, producerConfig: ProducerConfig): KafkaProducer<T>;
48
+ }
49
+ export interface OutboxBatchProducerFactory<T extends EventType[]> {
50
+ createBatchProducer(eventTypes: T, producerConfig: ProducerConfig): KafkaBatchProducer<T>;
51
+ }
package/dist/types.js ADDED
@@ -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/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@rsdk/kafka.producer.outbox",
3
+ "version": "4.0.0-next.10",
4
+ "description": "Commonly types and utils for outbox",
5
+ "license": "Apache License 2.0",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "repository": {
10
+ "url": "https://github.com/R-Vision/rsdk"
11
+ },
12
+ "main": "dist/index.js",
13
+ "peerDependencies": {
14
+ "@nestjs/common": "^10.0.0",
15
+ "@nestjs/core": "^10.0.0",
16
+ "@nestjs/microservices": "^10.0.0",
17
+ "@rsdk/autodoc.protocol": "^4.0.0-next.10",
18
+ "@rsdk/common": "^4.0.0-next.10",
19
+ "@rsdk/core": "^4.0.0-next.10",
20
+ "@rsdk/kafka.common": "^4.0.0-next.10",
21
+ "@rsdk/kafka.producer": "^4.0.0-next.10",
22
+ "@rsdk/logging": "^4.0.0-next.10",
23
+ "@rsdk/metadata": "^4.0.0-next.10",
24
+ "kafkajs": "^2.2.4",
25
+ "reflect-metadata": "^0.1.13",
26
+ "rxjs": "^7.0.0"
27
+ },
28
+ "dependencies": {
29
+ "@rsdk/nest-tools": "^4.0.0-next.10",
30
+ "lodash": "^4.17.21"
31
+ },
32
+ "gitHead": "c41d4da9aa0318d5e6417514c763618ce924f9b7"
33
+ }
@@ -0,0 +1,27 @@
1
+ import { Case } from '@rsdk/common';
2
+ import type { MaybeReadonlyArray } from '@rsdk/common.node';
3
+ import type { EventType } from '@rsdk/kafka.common';
4
+
5
+ /**
6
+ * Имя outbox-таблицы по умолчанию
7
+ */
8
+ export const DEFAULT_OUTBOX_TABLE_NAME = 'outbox';
9
+ export const OUTBOX_FACTORY_PROVIDER_TOKEN = 'OUTBOX_FACTORY_PROVIDER';
10
+ export const BATCH_OUTBOX_FACTORY_PROVIDER_TOKEN =
11
+ 'BATCH_OUTBOX_FACTORY_PROVIDER';
12
+ export const createOutboxProducerToken = <T extends EventType>(
13
+ eventType: T,
14
+ ): string => `KAFKA_OUTBOX_PRODUCER_${Case.toUpperSnake(eventType.$type)}`;
15
+
16
+ export const createOutboxBatchProducerToken = <
17
+ T extends MaybeReadonlyArray<EventType>,
18
+ >(
19
+ eventType: T,
20
+ ): string =>
21
+ `KAFKA_OUTBOX_BATCH_PRODUCER_${Case.toUpperSnake(
22
+ eventType
23
+ .map((e: EventType) => e.$type)
24
+ .sort()
25
+ .join('_'),
26
+ )}`;
27
+ export const OUTBOX_RSDK_METADATA_SCOPE = 'OUTBOX_RSDK_METADATA_SCOPE';
@@ -0,0 +1,47 @@
1
+ import { Inject } from '@nestjs/common';
2
+ import type { MaybeReadonlyArray } from '@rsdk/common.node';
3
+ import type { EventType } from '@rsdk/kafka.common';
4
+ import {
5
+ PLATFORM_RAW_GLOBAL_METADATA_SCOPE,
6
+ RsdkMetadata,
7
+ } from '@rsdk/metadata';
8
+
9
+ import {
10
+ createOutboxBatchProducerToken,
11
+ createOutboxProducerToken,
12
+ } from './constants';
13
+ import { OutboxModuleGenerator } from './outbox.module-generator';
14
+
15
+ export const InjectOutboxProducer = (
16
+ eventType: EventType,
17
+ ): ParameterDecorator => {
18
+ return (target, propertyKey, parameterIndex) => {
19
+ const producerToken = createOutboxProducerToken(eventType);
20
+
21
+ Inject(producerToken)(target, propertyKey, parameterIndex);
22
+ RsdkMetadata.setWithScope(
23
+ target,
24
+ PLATFORM_RAW_GLOBAL_METADATA_SCOPE,
25
+ producerToken,
26
+ OutboxModuleGenerator.createOutboxModuleDefinition(eventType),
27
+ true,
28
+ );
29
+ };
30
+ };
31
+
32
+ export const InjectOutboxBatchProducer = (
33
+ eventType: MaybeReadonlyArray<EventType>,
34
+ ): ParameterDecorator => {
35
+ return (target, propertyKey, parameterIndex) => {
36
+ const batchProducerToken = createOutboxBatchProducerToken(eventType);
37
+
38
+ Inject(batchProducerToken)(target, propertyKey, parameterIndex);
39
+ RsdkMetadata.setWithScope(
40
+ target,
41
+ PLATFORM_RAW_GLOBAL_METADATA_SCOPE,
42
+ batchProducerToken,
43
+ OutboxModuleGenerator.createBatchOutboxModuleDefinition(eventType),
44
+ true,
45
+ );
46
+ };
47
+ };
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ export { InjectOutboxProducer, InjectOutboxBatchProducer } from './decorator';
2
+
3
+ export * from './outbox-events.factory';
4
+ export * from './constants';
5
+ export * from './types';
@@ -0,0 +1,28 @@
1
+ import type { EventPayload, EventType } from '@rsdk/kafka.common';
2
+ import type { PublishOptions } from '@rsdk/kafka.producer';
3
+ import { EventsFactory } from '@rsdk/kafka.producer';
4
+ import { randomUUID } from 'node:crypto';
5
+
6
+ import type { OutboxTable } from './types';
7
+
8
+ /**
9
+ * Подготавливает данные для вставки в таблицу outbox
10
+ */
11
+ export class OutboxEventsFactory<E extends EventType> extends EventsFactory<
12
+ E,
13
+ OutboxTable
14
+ > {
15
+ create<P extends EventPayload<E> = EventPayload<E>>(
16
+ payload: P,
17
+ options?: PublishOptions,
18
+ ): OutboxTable {
19
+ return {
20
+ id: randomUUID(),
21
+ group: this.eventType.$group,
22
+ metadata: this.getMetadata(options?.metadata),
23
+ partition_key: this.getPartitionKey(this.eventType, options),
24
+ payload: this.serializePayload(payload),
25
+ type: this.eventType.$type,
26
+ };
27
+ }
28
+ }
@@ -0,0 +1,36 @@
1
+ import type { DocumentResolver } from '@rsdk/autodoc.protocol';
2
+ import { Composite, Table } from '@rsdk/autodoc.protocol';
3
+ import { getTopicName } from '@rsdk/kafka.common';
4
+ import type { RsdkMetadataProvider } from '@rsdk/metadata';
5
+
6
+ import { OUTBOX_RSDK_METADATA_SCOPE } from './constants';
7
+ import type { OutboxMetadata } from './types';
8
+
9
+ class OutboxAutodocResolver implements DocumentResolver {
10
+ getNode(rsdkMetadataProvider: RsdkMetadataProvider): Composite | null {
11
+ const resources = rsdkMetadataProvider.get<OutboxMetadata>(
12
+ OUTBOX_RSDK_METADATA_SCOPE,
13
+ );
14
+
15
+ if (!resources) {
16
+ return null;
17
+ }
18
+
19
+ return new Composite(
20
+ new Table(
21
+ resources.map((resource) => {
22
+ const { $type, $partitionKeyField } = resource.value.eventType;
23
+
24
+ return {
25
+ Type: $type,
26
+ TopicName: getTopicName(resource.value.eventType),
27
+ PartitionKeyField: $partitionKeyField ?? '-',
28
+ };
29
+ }),
30
+ ),
31
+ 'Kafka outbox',
32
+ );
33
+ }
34
+ }
35
+
36
+ export const outboxAutodocResolver = new OutboxAutodocResolver();
@@ -0,0 +1,110 @@
1
+ import type { DynamicModule } from '@nestjs/common';
2
+ import { AutodocMetadata } from '@rsdk/autodoc.protocol';
3
+ import type { MaybeReadonlyArray } from '@rsdk/common.node';
4
+ import type { EventType } from '@rsdk/kafka.common';
5
+ import type { KafkaProducer } from '@rsdk/kafka.producer';
6
+ import { ProducerConfig } from '@rsdk/kafka.producer';
7
+ import { RsdkMetadata } from '@rsdk/metadata';
8
+ import omit from 'lodash/omit';
9
+
10
+ import {
11
+ BATCH_OUTBOX_FACTORY_PROVIDER_TOKEN,
12
+ createOutboxBatchProducerToken,
13
+ createOutboxProducerToken,
14
+ OUTBOX_FACTORY_PROVIDER_TOKEN,
15
+ OUTBOX_RSDK_METADATA_SCOPE,
16
+ } from './constants';
17
+ import { outboxAutodocResolver } from './outbox.autodoc-resolver';
18
+ import type {
19
+ OutboxBatchProducerFactory,
20
+ OutboxMetadata,
21
+ OutboxProducerFactory,
22
+ } from './types';
23
+
24
+ export class OutboxModuleGenerator {
25
+ static createOutboxModuleDefinition(eventType: EventType): DynamicModule {
26
+ const providers = [
27
+ {
28
+ inject: [OUTBOX_FACTORY_PROVIDER_TOKEN, ProducerConfig],
29
+ /**
30
+ * TODO: А если пользователь будет использовать одновременно knex и typeorm?
31
+ * Сейчас произойдёт неопределённое поведение. А надо наверное ошибку кидать?
32
+ * (В целом следуем концепции того что в приложении не должно быть два плагина для работы с СУБД)
33
+ */
34
+ provide: createOutboxProducerToken(eventType),
35
+ useFactory: (
36
+ outboxProducerFactory: OutboxProducerFactory<any>,
37
+ config: ProducerConfig,
38
+ ): KafkaProducer<EventType> =>
39
+ outboxProducerFactory.createProducer(eventType, config),
40
+ },
41
+ ];
42
+ const moduleDef: DynamicModule = {
43
+ providers,
44
+ module: OutboxModuleGenerator,
45
+ exports: providers,
46
+ };
47
+
48
+ const rsdkMetadata = new RsdkMetadata<OutboxMetadata>(
49
+ moduleDef,
50
+ OUTBOX_RSDK_METADATA_SCOPE,
51
+ );
52
+
53
+ rsdkMetadata.add({
54
+ eventType, // Потому что https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#:~:text=If%20the%20value%20has%20a%20toJSON()%20method%2C%20it%27s%20responsible%20to%20define%20what%20data%20will%20be%20serialized.
55
+ eventData: omit(eventType, 'toJSON'),
56
+ });
57
+
58
+ AutodocMetadata.defineResolver(
59
+ moduleDef,
60
+ OUTBOX_RSDK_METADATA_SCOPE,
61
+ outboxAutodocResolver,
62
+ );
63
+ return moduleDef;
64
+ }
65
+
66
+ static createBatchOutboxModuleDefinition(
67
+ eventTypes: MaybeReadonlyArray<EventType>,
68
+ ): DynamicModule {
69
+ const providers = [
70
+ {
71
+ inject: [BATCH_OUTBOX_FACTORY_PROVIDER_TOKEN, ProducerConfig],
72
+ /**
73
+ * TODO: А если пользователь будет использовать одновременно knex и typeorm?
74
+ * Сейчас произойдёт неопределённое поведение. А надо наверное ошибку кидать?
75
+ * (В целом следуем концепции того что в приложении не должно быть два плагина для работы с СУБД)
76
+ */
77
+ provide: createOutboxBatchProducerToken(eventTypes),
78
+ useFactory: (
79
+ outboxProducerFactory: OutboxBatchProducerFactory<any>,
80
+ config: ProducerConfig,
81
+ ): KafkaProducer<EventType> =>
82
+ outboxProducerFactory.createBatchProducer(eventTypes, config),
83
+ },
84
+ ];
85
+ const moduleDef: DynamicModule = {
86
+ providers,
87
+ module: OutboxModuleGenerator,
88
+ exports: providers,
89
+ };
90
+
91
+ const rsdkMetadata = new RsdkMetadata<OutboxMetadata>(
92
+ moduleDef,
93
+ OUTBOX_RSDK_METADATA_SCOPE,
94
+ );
95
+
96
+ for (const eventType of eventTypes) {
97
+ rsdkMetadata.add({
98
+ eventType: eventType, // Потому что https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#:~:text=If%20the%20value%20has%20a%20toJSON()%20method%2C%20it%27s%20responsible%20to%20define%20what%20data%20will%20be%20serialized.
99
+ eventData: omit(eventType, 'toJSON'),
100
+ });
101
+ }
102
+
103
+ AutodocMetadata.defineResolver(
104
+ moduleDef,
105
+ OUTBOX_RSDK_METADATA_SCOPE,
106
+ outboxAutodocResolver,
107
+ );
108
+ return moduleDef;
109
+ }
110
+ }
package/src/types.ts ADDED
@@ -0,0 +1,71 @@
1
+ import type { EventType } from '@rsdk/kafka.common';
2
+ import type {
3
+ KafkaBatchProducer,
4
+ KafkaProducer,
5
+ ProducerConfig,
6
+ ProducerOptions,
7
+ } from '@rsdk/kafka.producer';
8
+
9
+ /**
10
+ * Структура таблицы outbox (имя может быть переопределено)
11
+ */
12
+ export interface OutboxTable {
13
+ /**
14
+ * Идентификатор события (UUID)
15
+ */
16
+ id: string;
17
+
18
+ /**
19
+ * Тип события: FQN из protobuf
20
+ */
21
+ type: string;
22
+
23
+ /**
24
+ * Группа - указывается в опции event в protobuf
25
+ */
26
+ group: string;
27
+
28
+ /**
29
+ * Ключ партиционирования. Вычисляется в зависимости от
30
+ * переданной опции partitionKeyField (в protobuf) и структуры события.
31
+ * Может быть явно задан при публикации.
32
+ */
33
+ partition_key: string;
34
+
35
+ /**
36
+ * Метаданные события (включая служебные)
37
+ *
38
+ * NOTE: Здесь используется any, а не unknown из-за особенностей
39
+ * типизации typeorm
40
+ */
41
+ metadata: Record<string, any> | null;
42
+
43
+ /**
44
+ * Сериализованные данные события
45
+ */
46
+ payload: Buffer;
47
+ }
48
+
49
+ export interface OutboxProducerOptions extends ProducerOptions {
50
+ /** Переопределяет имя таблицы (default: "outbox") */
51
+ tableName?: string;
52
+ }
53
+
54
+ export type OutboxMetadata = {
55
+ eventType: EventType;
56
+ eventData: Omit<EventType, 'toJSON'>;
57
+ };
58
+
59
+ export interface OutboxProducerFactory<T extends EventType> {
60
+ createProducer(
61
+ eventType: T,
62
+ producerConfig: ProducerConfig,
63
+ ): KafkaProducer<T>;
64
+ }
65
+
66
+ export interface OutboxBatchProducerFactory<T extends EventType[]> {
67
+ createBatchProducer(
68
+ eventTypes: T,
69
+ producerConfig: ProducerConfig,
70
+ ): KafkaBatchProducer<T>;
71
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "@rsdk/tsconfig/base.json",
3
+ "compilerOptions": {
4
+ "declaration": true,
5
+ "outDir": "dist"
6
+ },
7
+ "include": ["src/**/*"],
8
+ "exclude": ["node_modules", "dist", "test", "**/*.spec.ts", "**/*.test.ts", "**/*.spec.e2e.ts", "**/*.test.e2e.ts"]
9
+ }