@lucaapp/service-utils 1.8.0 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -2,3 +2,5 @@ export * from './lib/kafka';
2
2
  export * from './lib/serviceIdentity';
3
3
  export * from './lib/metrics';
4
4
  export * from './lib/wsEvent';
5
+ export * from './lib/lifecycle';
6
+ export * from './lib/logger';
package/dist/index.js CHANGED
@@ -18,3 +18,5 @@ __exportStar(require("./lib/kafka"), exports);
18
18
  __exportStar(require("./lib/serviceIdentity"), exports);
19
19
  __exportStar(require("./lib/metrics"), exports);
20
20
  __exportStar(require("./lib/wsEvent"), exports);
21
+ __exportStar(require("./lib/lifecycle"), exports);
22
+ __exportStar(require("./lib/logger"), exports);
@@ -21,7 +21,7 @@ declare class KafkaClient {
21
21
  private verifySignature;
22
22
  private parseValue;
23
23
  private ensureTopics;
24
- consume: <T extends KafkaTopic>(groupId: string, kafkaTopic: T, handler: EventPayloadHandler<T>, fromBeginning?: boolean) => Promise<Consumer>;
24
+ consume: <T extends KafkaTopic>(kafkaTopic: T, handler: EventPayloadHandler<T>, fromBeginning?: boolean) => Promise<Consumer>;
25
25
  produce: <T extends KafkaTopic>(kafkaTopic: T, key: string, value: KafkaEvent<T>) => Promise<void>;
26
26
  }
27
27
  export { KafkaClient };
@@ -154,8 +154,9 @@ class KafkaClient {
154
154
  });
155
155
  this.logger.debug(`Topic=${topic} created.`);
156
156
  };
157
- this.consume = async (groupId, kafkaTopic, handler, fromBeginning = false) => {
157
+ this.consume = async (kafkaTopic, handler, fromBeginning = false) => {
158
158
  const topic = await this.getTopic(kafkaTopic);
159
+ const groupId = `${this.environment.valueOf()}_${this.serviceIdentity.identityName}`;
159
160
  try {
160
161
  const consumer = this.kafkaClient.consumer({ groupId });
161
162
  await consumer.connect();
@@ -180,6 +181,7 @@ class KafkaClient {
180
181
  messageConsumedErrorCounter
181
182
  .labels({ topic: kafkaTopic.valueOf(), groupId })
182
183
  .inc();
184
+ throw error;
183
185
  }
184
186
  },
185
187
  });
@@ -0,0 +1,17 @@
1
+ import { Logger, LoggerOptions } from 'pino';
2
+ declare class Lifecycle {
3
+ state: {
4
+ isShuttingDown: boolean;
5
+ };
6
+ private readonly shutdownDelay;
7
+ private readonly logger;
8
+ constructor(shutdownDelay: number, logger: Logger<LoggerOptions>);
9
+ shutdownHandlers: Array<() => void>;
10
+ gracefulShutdown: (isImmediate?: boolean) => Promise<void>;
11
+ private sigtermHandler;
12
+ unhandledRejectionHandler: (error: unknown) => void;
13
+ uncaughtExceptionHandler: (error: unknown) => void;
14
+ registerShutdownHandler: (shutdownHandler: () => void) => void;
15
+ registerHooks: () => void;
16
+ }
17
+ export { Lifecycle };
@@ -0,0 +1,54 @@
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.Lifecycle = void 0;
7
+ const moment_1 = __importDefault(require("moment"));
8
+ const sleep_1 = require("./sleep");
9
+ class Lifecycle {
10
+ constructor(shutdownDelay, logger) {
11
+ this.state = {
12
+ isShuttingDown: false,
13
+ };
14
+ this.shutdownHandlers = [];
15
+ this.gracefulShutdown = async (isImmediate = false) => {
16
+ if (this.state.isShuttingDown)
17
+ return;
18
+ this.state.isShuttingDown = true;
19
+ if (!isImmediate) {
20
+ this.logger.info(`shutting down in ${this.shutdownDelay.as('seconds')}s.`);
21
+ await (0, sleep_1.sleep)(this.shutdownDelay.as('milliseconds'));
22
+ }
23
+ this.logger.info('starting shutdown');
24
+ for (const shutdownHandler of this.shutdownHandlers) {
25
+ shutdownHandler();
26
+ }
27
+ this.logger.info('shutdown complete');
28
+ };
29
+ this.sigtermHandler = async () => {
30
+ this.logger.info('SIGTERM received.');
31
+ await this.gracefulShutdown();
32
+ process.exit(0);
33
+ };
34
+ this.unhandledRejectionHandler = (error) => {
35
+ this.logger.error(error, 'unhandledRejection');
36
+ process.exit(1);
37
+ };
38
+ this.uncaughtExceptionHandler = (error) => {
39
+ this.logger.error(error, 'uncaughtException');
40
+ process.exit(1);
41
+ };
42
+ this.registerShutdownHandler = (shutdownHandler) => {
43
+ this.shutdownHandlers.push(shutdownHandler);
44
+ };
45
+ this.registerHooks = () => {
46
+ process.on('SIGTERM', this.sigtermHandler);
47
+ process.on('uncaughtException', this.uncaughtExceptionHandler);
48
+ process.on('unhandledRejection', this.unhandledRejectionHandler);
49
+ };
50
+ this.shutdownDelay = moment_1.default.duration(shutdownDelay, 'seconds');
51
+ this.logger = logger;
52
+ }
53
+ }
54
+ exports.Lifecycle = Lifecycle;
@@ -0,0 +1,3 @@
1
+ declare const sleep: (msToSleep: number) => Promise<void>;
2
+ declare const randomSleep: () => Promise<void>;
3
+ export { sleep, randomSleep };
@@ -0,0 +1,13 @@
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.randomSleep = exports.sleep = void 0;
7
+ const crypto_1 = __importDefault(require("crypto"));
8
+ const util_1 = require("util");
9
+ const setTimeoutPromise = (0, util_1.promisify)(setTimeout);
10
+ const sleep = (msToSleep) => setTimeoutPromise(msToSleep);
11
+ exports.sleep = sleep;
12
+ const randomSleep = () => setTimeoutPromise(crypto_1.default.randomInt(100, 500));
13
+ exports.randomSleep = randomSleep;
@@ -0,0 +1,6 @@
1
+ import { Logger, LoggerOptions } from 'pino';
2
+ import { HttpLoggerCreationOptions } from './utils';
3
+ export declare class LoggerFactory {
4
+ static getLogger(logLevel: string): Logger<LoggerOptions>;
5
+ static getHttpLogger(options: HttpLoggerCreationOptions): import("pino-http").HttpLogger;
6
+ }
@@ -0,0 +1,89 @@
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.LoggerFactory = void 0;
7
+ const pino_1 = __importDefault(require("pino"));
8
+ const pino_http_1 = __importDefault(require("pino-http"));
9
+ const pick_1 = __importDefault(require("lodash/pick"));
10
+ const uuid_1 = require("uuid");
11
+ class LoggerFactory {
12
+ static getLogger(logLevel) {
13
+ return (0, pino_1.default)({ level: logLevel });
14
+ }
15
+ static getHttpLogger(options) {
16
+ const { requestSerializer, responseSerializer, errorSerializer, defaultHttpLogLevel, customMessage, } = new HttpLoggerHelpers(options);
17
+ const logger = this.getLogger(options.logLevel);
18
+ return (0, pino_http_1.default)({
19
+ logger: logger,
20
+ serializers: {
21
+ req: requestSerializer,
22
+ res: responseSerializer,
23
+ err: errorSerializer,
24
+ },
25
+ customLogLevel: (response, error) => {
26
+ if (response.statusCode >= 400 && response.statusCode < 500)
27
+ return 'warn';
28
+ if (response.statusCode >= 500 || error)
29
+ return 'error';
30
+ return defaultHttpLogLevel;
31
+ },
32
+ customSuccessMessage: customMessage,
33
+ customErrorMessage: (_, response) => customMessage(response),
34
+ genReqId: () => (0, uuid_1.v4)(),
35
+ });
36
+ }
37
+ }
38
+ exports.LoggerFactory = LoggerFactory;
39
+ class HttpLoggerHelpers {
40
+ constructor({ debug, e2e, defaultHttpLogLevel }) {
41
+ this.requestSerializer = (request) => {
42
+ if (this.debug || this.e2e)
43
+ return request;
44
+ request.headers = (0, pick_1.default)(request.headers, [
45
+ 'connection',
46
+ 'host',
47
+ 'origin',
48
+ 'x-real-ip',
49
+ 'x-forwarded-for',
50
+ 'x-forwarded-proto',
51
+ 'x-forwarded-host',
52
+ 'x-forwarded-port',
53
+ 'x-scheme',
54
+ 'user-agent',
55
+ 'content-type',
56
+ 'accept',
57
+ 'referer',
58
+ 'accept-encoding',
59
+ 'ssl-client-subject-dn',
60
+ ]);
61
+ return request;
62
+ };
63
+ this.responseSerializer = (response) => {
64
+ if (this.debug)
65
+ return response;
66
+ response.headers = (0, pick_1.default)(response.headers, [
67
+ 'content-type',
68
+ 'content-length',
69
+ ]);
70
+ return response;
71
+ };
72
+ this.errorSerializer = (error) => {
73
+ if (error?.parameters)
74
+ error.parameters = undefined;
75
+ if (error?.parent?.parameters)
76
+ error.parent.parameters = undefined;
77
+ if (error?.original?.parameters)
78
+ error.original.parameters = undefined;
79
+ return error;
80
+ };
81
+ this.customMessage = (response) => {
82
+ const { req: { method, originalUrl }, statusCode, } = response;
83
+ return `${statusCode} ${method} ${originalUrl}`;
84
+ };
85
+ this.debug = !!debug;
86
+ this.e2e = !!e2e;
87
+ this.defaultHttpLogLevel = defaultHttpLogLevel || 'info';
88
+ }
89
+ }
@@ -0,0 +1,9 @@
1
+ import { Service } from '../serviceIdentity';
2
+ import { Level } from 'pino';
3
+ export interface HttpLoggerCreationOptions {
4
+ logLevel: string;
5
+ defaultHttpLogLevel: Level;
6
+ service: Service;
7
+ debug: boolean;
8
+ e2e: boolean;
9
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,19 +1,19 @@
1
1
  declare enum Environment {
2
- LOCAL = "LOCAL",
3
- DEV = "DEV",
4
- QS = "QS",
5
- AQS = "AQS",
6
- RELEASE = "RELEASE",
7
- HOTFIX = "HOTFIX",
8
- PENTEST = "PENTEST",
9
- P1 = "P1",
10
- P2 = "P2",
11
- P3 = "P3",
12
- P4 = "P4",
13
- P5 = "P5",
14
- P6 = "P6",
15
- DEMO = "DEMO",
16
- PREPROD = "PREPROD",
17
- PROD = "PROD"
2
+ LOCAL = "local",
3
+ DEV = "dev",
4
+ QS = "qs",
5
+ AQS = "aqs",
6
+ RELEASE = "release",
7
+ HOTFIX = "hotfix",
8
+ PENTEST = "pentest",
9
+ P1 = "p1",
10
+ P2 = "p2",
11
+ P3 = "p3",
12
+ P4 = "p4",
13
+ P5 = "p5",
14
+ P6 = "p6",
15
+ DEMO = "demo",
16
+ PREPROD = "preprod",
17
+ PROD = "prod"
18
18
  }
19
19
  export { Environment };
@@ -3,21 +3,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Environment = void 0;
4
4
  var Environment;
5
5
  (function (Environment) {
6
- Environment["LOCAL"] = "LOCAL";
7
- Environment["DEV"] = "DEV";
8
- Environment["QS"] = "QS";
9
- Environment["AQS"] = "AQS";
10
- Environment["RELEASE"] = "RELEASE";
11
- Environment["HOTFIX"] = "HOTFIX";
12
- Environment["PENTEST"] = "PENTEST";
13
- Environment["P1"] = "P1";
14
- Environment["P2"] = "P2";
15
- Environment["P3"] = "P3";
16
- Environment["P4"] = "P4";
17
- Environment["P5"] = "P5";
18
- Environment["P6"] = "P6";
19
- Environment["DEMO"] = "DEMO";
20
- Environment["PREPROD"] = "PREPROD";
21
- Environment["PROD"] = "PROD";
6
+ Environment["LOCAL"] = "local";
7
+ Environment["DEV"] = "dev";
8
+ Environment["QS"] = "qs";
9
+ Environment["AQS"] = "aqs";
10
+ Environment["RELEASE"] = "release";
11
+ Environment["HOTFIX"] = "hotfix";
12
+ Environment["PENTEST"] = "pentest";
13
+ Environment["P1"] = "p1";
14
+ Environment["P2"] = "p2";
15
+ Environment["P3"] = "p3";
16
+ Environment["P4"] = "p4";
17
+ Environment["P5"] = "p5";
18
+ Environment["P6"] = "p6";
19
+ Environment["DEMO"] = "demo";
20
+ Environment["PREPROD"] = "preprod";
21
+ Environment["PROD"] = "prod";
22
22
  })(Environment || (Environment = {}));
23
23
  exports.Environment = Environment;
@@ -2,6 +2,8 @@ declare enum Service {
2
2
  BACKEND = "backend",
3
3
  BACKEND_PAY = "backend-pay",
4
4
  BACKEND_POS = "backend-pos",
5
+ BACKEND_ID = "backend-id",
6
+ BACKEND_ATTESTATION = "backend-attestation",
5
7
  BACKEND_PUBSUB = "backend-pubsub"
6
8
  }
7
9
  export { Service };
@@ -6,6 +6,8 @@ var Service;
6
6
  Service["BACKEND"] = "backend";
7
7
  Service["BACKEND_PAY"] = "backend-pay";
8
8
  Service["BACKEND_POS"] = "backend-pos";
9
+ Service["BACKEND_ID"] = "backend-id";
10
+ Service["BACKEND_ATTESTATION"] = "backend-attestation";
9
11
  Service["BACKEND_PUBSUB"] = "backend-pubsub";
10
12
  })(Service || (Service = {}));
11
13
  exports.Service = Service;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lucaapp/service-utils",
3
- "version": "1.8.0",
3
+ "version": "1.9.1",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [
@@ -20,6 +20,7 @@
20
20
  "devDependencies": {
21
21
  "@types/express": "4.17.13",
22
22
  "@types/jest": "^28.1.5",
23
+ "@types/lodash": "^4.14.187",
23
24
  "@types/uuid": "^8.3.4",
24
25
  "@typescript-eslint/eslint-plugin": "^5.30.6",
25
26
  "@typescript-eslint/parser": "^5.30.6",
@@ -31,7 +32,6 @@
31
32
  "semantic-release": "^19.0.3",
32
33
  "semantic-release-monorepo": "^7.0.5",
33
34
  "ts-jest": "^28.0.5",
34
- "pino": "^8.6.1",
35
35
  "typescript": "^4.7.4"
36
36
  },
37
37
  "dependencies": {
@@ -40,7 +40,9 @@
40
40
  "axios": "^0.27.2",
41
41
  "jose": "4.9.2",
42
42
  "kafkajs": "2.1.0",
43
+ "lodash": "^4.17.21",
43
44
  "moment": "^2.29.4",
45
+ "pino-http": "6.6.0",
44
46
  "prom-client": "14.1.0",
45
47
  "uuid": "^9.0.0"
46
48
  }