@opra/kafka 1.0.3 → 1.0.5

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.
@@ -11,6 +11,7 @@ const request_parser_js_1 = require("./request-parser.js");
11
11
  const globalErrorTypes = ['unhandledRejection', 'uncaughtException'];
12
12
  const signalTraps = ['SIGTERM', 'SIGINT', 'SIGUSR2'];
13
13
  const kGroupId = Symbol('kGroupId');
14
+ const noOp = () => undefined;
14
15
  /**
15
16
  *
16
17
  * @class KafkaAdapter
@@ -22,22 +23,15 @@ class KafkaAdapter extends core_1.PlatformAdapter {
22
23
  * @constructor
23
24
  */
24
25
  constructor(config) {
25
- super(config.document, config);
26
+ super(config);
26
27
  this._controllerInstances = new Map();
27
28
  this._consumers = new Map();
28
29
  this._handlerArgs = [];
30
+ this._started = false;
29
31
  this.protocol = 'rpc';
30
32
  this.platform = KafkaAdapter.PlatformName;
31
- if (!(config.document.api instanceof common_1.RpcApi && config.document.api.platform === KafkaAdapter.PlatformName)) {
32
- throw new TypeError(`The document doesn't expose a Kafka Api`);
33
- }
34
33
  this._config = config;
35
34
  this.interceptors = [...(config.interceptors || [])];
36
- this.kafka = new kafkajs_1.Kafka({
37
- ...config.client,
38
- logCreator: config.logger ? () => this._createLogCreator(config.logger, config.logExtra) : undefined,
39
- });
40
- this._logger = config.logger;
41
35
  globalErrorTypes.forEach(type => {
42
36
  process.on(type, e => {
43
37
  this._emitError(e);
@@ -51,17 +45,29 @@ class KafkaAdapter extends core_1.PlatformAdapter {
51
45
  get api() {
52
46
  return this.document.rpcApi;
53
47
  }
48
+ get kafka() {
49
+ return this._kafka;
50
+ }
51
+ async initialize(document) {
52
+ if (this._document)
53
+ throw new TypeError(`${this.constructor.name} already initialized.`);
54
+ if (!(document.api instanceof common_1.RpcApi && document.api.platform === KafkaAdapter.PlatformName)) {
55
+ throw new TypeError(`The document doesn't expose a Kafka Api`);
56
+ }
57
+ this._document = document;
58
+ this._kafka = new kafkajs_1.Kafka({
59
+ ...this._config.client,
60
+ logCreator: this.logger ? () => this._createLogCreator(this.logger, this._config.logExtra) : undefined,
61
+ });
62
+ await this._createAllConsumers();
63
+ }
54
64
  /**
55
65
  * Starts the service
56
66
  */
57
67
  async start() {
58
- /* istanbul ignore next */
59
- if (this._consumers.size > 0)
60
- return;
61
- /* istanbul ignore next */
62
- if (this._consumers.size > 0)
68
+ if (this._started)
63
69
  return;
64
- await this._createAllConsumers();
70
+ this._started = true;
65
71
  /** Connect all consumers */
66
72
  for (const consumer of this._consumers.values()) {
67
73
  await consumer.connect().catch(e => {
@@ -181,6 +187,7 @@ class KafkaAdapter extends core_1.PlatformAdapter {
181
187
  else {
182
188
  const x = this._config.consumers?.[kafkaMetadata.consumer];
183
189
  if (x) {
190
+ operationConfig.consumer.groupId = kafkaMetadata.consumer;
184
191
  Object.assign(operationConfig.consumer, x);
185
192
  }
186
193
  }
@@ -292,7 +299,7 @@ class KafkaAdapter extends core_1.PlatformAdapter {
292
299
  return;
293
300
  }
294
301
  /** Create context */
295
- const ctx = new kafka_context_js_1.KafkaContext({
302
+ const context = new kafka_context_js_1.KafkaContext({
296
303
  adapter: this,
297
304
  platform: this.platform,
298
305
  controller,
@@ -308,15 +315,42 @@ class KafkaAdapter extends core_1.PlatformAdapter {
308
315
  heartbeat,
309
316
  pause,
310
317
  });
311
- await this.emitAsync('before-execute', ctx);
312
- const result = await operationHandler.call(instance, ctx);
313
- await this.emitAsync('after-execute', ctx, result);
318
+ await this.emitAsync('before-execute', context);
319
+ try {
320
+ /** Call operation handler */
321
+ const result = await operationHandler.call(instance, context);
322
+ await this.emitAsync('after-execute', context, result);
323
+ }
324
+ catch (e) {
325
+ this._emitError(e, context);
326
+ }
314
327
  };
315
328
  }
316
- _emitError(e) {
317
- this._logger?.error(e);
318
- if (this.listenerCount('error'))
319
- this.emit('error', e);
329
+ _emitError(e, context) {
330
+ Promise.resolve()
331
+ .then(async () => {
332
+ const logger = this.logger;
333
+ if (context) {
334
+ context.errors = this._wrapExceptions(context.errors);
335
+ if (context.listenerCount('error')) {
336
+ await this.emitAsync('error', context.errors[0], context);
337
+ }
338
+ if (logger?.error) {
339
+ context.errors.forEach(err => logger.error(err, context));
340
+ }
341
+ return;
342
+ }
343
+ this.logger?.error(e);
344
+ if (this.listenerCount('error'))
345
+ this.emit('error', e);
346
+ })
347
+ .catch(noOp);
348
+ }
349
+ _wrapExceptions(exceptions) {
350
+ const wrappedErrors = exceptions.map(e => (e instanceof common_1.OpraException ? e : new common_1.OpraException(e)));
351
+ if (!wrappedErrors.length)
352
+ wrappedErrors.push(new common_1.OpraException('Internal Server Error'));
353
+ return wrappedErrors;
320
354
  }
321
355
  _createLogCreator(logger, logExtra) {
322
356
  return ({ namespace, level, log }) => {
@@ -1,4 +1,4 @@
1
- import { RPC_CONTROLLER_METADATA, RpcApi } from '@opra/common';
1
+ import { OpraException, RPC_CONTROLLER_METADATA, RpcApi, } from '@opra/common';
2
2
  import { kAssetCache, PlatformAdapter } from '@opra/core';
3
3
  import { Kafka, logLevel } from 'kafkajs';
4
4
  import { vg } from 'valgen';
@@ -8,6 +8,7 @@ import { RequestParser } from './request-parser.js';
8
8
  const globalErrorTypes = ['unhandledRejection', 'uncaughtException'];
9
9
  const signalTraps = ['SIGTERM', 'SIGINT', 'SIGUSR2'];
10
10
  const kGroupId = Symbol('kGroupId');
11
+ const noOp = () => undefined;
11
12
  /**
12
13
  *
13
14
  * @class KafkaAdapter
@@ -19,22 +20,15 @@ export class KafkaAdapter extends PlatformAdapter {
19
20
  * @constructor
20
21
  */
21
22
  constructor(config) {
22
- super(config.document, config);
23
+ super(config);
23
24
  this._controllerInstances = new Map();
24
25
  this._consumers = new Map();
25
26
  this._handlerArgs = [];
27
+ this._started = false;
26
28
  this.protocol = 'rpc';
27
29
  this.platform = KafkaAdapter.PlatformName;
28
- if (!(config.document.api instanceof RpcApi && config.document.api.platform === KafkaAdapter.PlatformName)) {
29
- throw new TypeError(`The document doesn't expose a Kafka Api`);
30
- }
31
30
  this._config = config;
32
31
  this.interceptors = [...(config.interceptors || [])];
33
- this.kafka = new Kafka({
34
- ...config.client,
35
- logCreator: config.logger ? () => this._createLogCreator(config.logger, config.logExtra) : undefined,
36
- });
37
- this._logger = config.logger;
38
32
  globalErrorTypes.forEach(type => {
39
33
  process.on(type, e => {
40
34
  this._emitError(e);
@@ -48,17 +42,29 @@ export class KafkaAdapter extends PlatformAdapter {
48
42
  get api() {
49
43
  return this.document.rpcApi;
50
44
  }
45
+ get kafka() {
46
+ return this._kafka;
47
+ }
48
+ async initialize(document) {
49
+ if (this._document)
50
+ throw new TypeError(`${this.constructor.name} already initialized.`);
51
+ if (!(document.api instanceof RpcApi && document.api.platform === KafkaAdapter.PlatformName)) {
52
+ throw new TypeError(`The document doesn't expose a Kafka Api`);
53
+ }
54
+ this._document = document;
55
+ this._kafka = new Kafka({
56
+ ...this._config.client,
57
+ logCreator: this.logger ? () => this._createLogCreator(this.logger, this._config.logExtra) : undefined,
58
+ });
59
+ await this._createAllConsumers();
60
+ }
51
61
  /**
52
62
  * Starts the service
53
63
  */
54
64
  async start() {
55
- /* istanbul ignore next */
56
- if (this._consumers.size > 0)
57
- return;
58
- /* istanbul ignore next */
59
- if (this._consumers.size > 0)
65
+ if (this._started)
60
66
  return;
61
- await this._createAllConsumers();
67
+ this._started = true;
62
68
  /** Connect all consumers */
63
69
  for (const consumer of this._consumers.values()) {
64
70
  await consumer.connect().catch(e => {
@@ -178,6 +184,7 @@ export class KafkaAdapter extends PlatformAdapter {
178
184
  else {
179
185
  const x = this._config.consumers?.[kafkaMetadata.consumer];
180
186
  if (x) {
187
+ operationConfig.consumer.groupId = kafkaMetadata.consumer;
181
188
  Object.assign(operationConfig.consumer, x);
182
189
  }
183
190
  }
@@ -289,7 +296,7 @@ export class KafkaAdapter extends PlatformAdapter {
289
296
  return;
290
297
  }
291
298
  /** Create context */
292
- const ctx = new KafkaContext({
299
+ const context = new KafkaContext({
293
300
  adapter: this,
294
301
  platform: this.platform,
295
302
  controller,
@@ -305,15 +312,42 @@ export class KafkaAdapter extends PlatformAdapter {
305
312
  heartbeat,
306
313
  pause,
307
314
  });
308
- await this.emitAsync('before-execute', ctx);
309
- const result = await operationHandler.call(instance, ctx);
310
- await this.emitAsync('after-execute', ctx, result);
315
+ await this.emitAsync('before-execute', context);
316
+ try {
317
+ /** Call operation handler */
318
+ const result = await operationHandler.call(instance, context);
319
+ await this.emitAsync('after-execute', context, result);
320
+ }
321
+ catch (e) {
322
+ this._emitError(e, context);
323
+ }
311
324
  };
312
325
  }
313
- _emitError(e) {
314
- this._logger?.error(e);
315
- if (this.listenerCount('error'))
316
- this.emit('error', e);
326
+ _emitError(e, context) {
327
+ Promise.resolve()
328
+ .then(async () => {
329
+ const logger = this.logger;
330
+ if (context) {
331
+ context.errors = this._wrapExceptions(context.errors);
332
+ if (context.listenerCount('error')) {
333
+ await this.emitAsync('error', context.errors[0], context);
334
+ }
335
+ if (logger?.error) {
336
+ context.errors.forEach(err => logger.error(err, context));
337
+ }
338
+ return;
339
+ }
340
+ this.logger?.error(e);
341
+ if (this.listenerCount('error'))
342
+ this.emit('error', e);
343
+ })
344
+ .catch(noOp);
345
+ }
346
+ _wrapExceptions(exceptions) {
347
+ const wrappedErrors = exceptions.map(e => (e instanceof OpraException ? e : new OpraException(e)));
348
+ if (!wrappedErrors.length)
349
+ wrappedErrors.push(new OpraException('Internal Server Error'));
350
+ return wrappedErrors;
317
351
  }
318
352
  _createLogCreator(logger, logExtra) {
319
353
  return ({ namespace, level, log }) => {
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@opra/kafka",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Opra Kafka package",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
7
7
  "dependencies": {
8
- "@opra/common": "^1.0.3",
9
- "@opra/core": "^1.0.3",
8
+ "@opra/common": "^1.0.5",
9
+ "@opra/core": "^1.0.5",
10
10
  "node-events-async": "^1.0.0",
11
- "tslib": "^2.7.0",
11
+ "tslib": "^2.8.0",
12
12
  "valgen": "^5.10.0"
13
13
  },
14
14
  "peerDependencies": {
@@ -1,4 +1,4 @@
1
- import { ApiDocument, OpraSchema, RpcApi, RpcController, RpcOperation } from '@opra/common';
1
+ import { ApiDocument, OpraException, OpraSchema, RpcApi, RpcController, RpcOperation } from '@opra/common';
2
2
  import { type ILogger, PlatformAdapter } from '@opra/core';
3
3
  import { type Consumer, ConsumerConfig, EachMessageHandler, Kafka, type KafkaConfig } from 'kafkajs';
4
4
  import type { StrictOmit } from 'ts-gems';
@@ -17,9 +17,7 @@ export declare namespace KafkaAdapter {
17
17
  fromBeginning?: boolean;
18
18
  };
19
19
  };
20
- document: ApiDocument;
21
20
  interceptors?: (InterceptorFunction | IKafkaInterceptor)[];
22
- logger?: ILogger;
23
21
  logExtra?: boolean;
24
22
  }
25
23
  interface OperationOptions {
@@ -66,11 +64,11 @@ export declare class KafkaAdapter extends PlatformAdapter {
66
64
  protected _controllerInstances: Map<RpcController, any>;
67
65
  protected _consumers: Map<string, Consumer>;
68
66
  protected _handlerArgs: HandlerArguments[];
69
- protected _logger?: ILogger;
70
- readonly kafka: Kafka;
67
+ protected _started: boolean;
68
+ protected _kafka: Kafka;
71
69
  readonly protocol: OpraSchema.Transport;
72
70
  readonly platform = "kafka";
73
- interceptors: (KafkaAdapter.InterceptorFunction | KafkaAdapter.IKafkaInterceptor)[];
71
+ readonly interceptors: (KafkaAdapter.InterceptorFunction | KafkaAdapter.IKafkaInterceptor)[];
74
72
  /**
75
73
  *
76
74
  * @param config
@@ -78,6 +76,8 @@ export declare class KafkaAdapter extends PlatformAdapter {
78
76
  */
79
77
  constructor(config: KafkaAdapter.Config);
80
78
  get api(): RpcApi;
79
+ get kafka(): Kafka;
80
+ initialize(document: ApiDocument): Promise<void>;
81
81
  /**
82
82
  * Starts the service
83
83
  */
@@ -112,7 +112,8 @@ export declare class KafkaAdapter extends PlatformAdapter {
112
112
  * @protected
113
113
  */
114
114
  protected _createHandler(args: HandlerArguments): void;
115
- protected _emitError(e: any): void;
115
+ protected _emitError(e: any, context?: KafkaContext): void;
116
+ protected _wrapExceptions(exceptions: any[]): OpraException[];
116
117
  protected _createLogCreator(logger: ILogger, logExtra?: boolean): ({ namespace, level, log }: {
117
118
  namespace: any;
118
119
  level: any;