@yolo-croket-dev/core 1.10.0-jun.0 → 1.10.0-jun.2

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.
@@ -2,11 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GrpcParam = void 0;
4
4
  const common_1 = require("@nestjs/common");
5
+ const grpc_param_metadata_function_1 = require("../functions/grpc-param-metadata.function");
5
6
  exports.GrpcParam = (0, common_1.createParamDecorator)((data, ctx) => {
6
7
  const metadata = ctx.switchToRpc().getContext();
7
- const param = metadata.get('param');
8
- if (!param || !param[0]) {
9
- return null;
10
- }
11
- return JSON.parse(param[0]);
8
+ return (0, grpc_param_metadata_function_1.parseGrpcParamMetadata)(metadata);
12
9
  });
@@ -39,6 +39,26 @@ const CroketExceptions = __importStar(require("@yolo-croket-dev/core/exceptions"
39
39
  */
40
40
  const getCroketExceptionClasses = () => Object.values(CroketExceptions).filter((value) => typeof value === 'function' && value.prototype && value.prototype.constructor === value);
41
41
  const croketExceptionClasses = getCroketExceptionClasses();
42
+ const GRPC_DEBUG_ERROR_METADATA_KEY = 'debug-error';
43
+ function isRecord(value) {
44
+ return !!value && typeof value === 'object' && !Array.isArray(value);
45
+ }
46
+ function getStringProperty(value, key) {
47
+ if (!isRecord(value)) {
48
+ return undefined;
49
+ }
50
+ const propertyValue = value[key];
51
+ return typeof propertyValue === 'string' ? propertyValue : undefined;
52
+ }
53
+ function buildDebugErrorMetadata(exception) {
54
+ var _a;
55
+ return {
56
+ type: ((_a = exception === null || exception === void 0 ? void 0 : exception.constructor) === null || _a === void 0 ? void 0 : _a.name) || 'Error',
57
+ name: getStringProperty(exception, 'name'),
58
+ message: getStringProperty(exception, 'message'),
59
+ kind: getStringProperty(exception, 'kind'),
60
+ };
61
+ }
42
62
  let GrpcExceptionFilter = class GrpcExceptionFilter extends microservices_1.BaseRpcExceptionFilter {
43
63
  constructor() {
44
64
  super(...arguments);
@@ -88,6 +108,7 @@ let GrpcExceptionFilter = class GrpcExceptionFilter extends microservices_1.Base
88
108
  else {
89
109
  errorMessage = (exception === null || exception === void 0 ? void 0 : exception.message) || 'Internal Server Error';
90
110
  grpcStatus = grpc_js_1.status.INTERNAL;
111
+ metadata.set(GRPC_DEBUG_ERROR_METADATA_KEY, Buffer.from(JSON.stringify(buildDebugErrorMetadata(exception))).toString('base64'));
91
112
  }
92
113
  // BaseRpcExceptionFilter의 catch 메서드를 사용하여 metadata를 포함한 RpcException 생성
93
114
  const rpcException = new microservices_1.RpcException({
@@ -131,7 +152,7 @@ let GrpcExceptionFilter = class GrpcExceptionFilter extends microservices_1.Base
131
152
  }
132
153
  }
133
154
  };
134
- exports.GrpcExceptionFilter = GrpcExceptionFilter;
135
- exports.GrpcExceptionFilter = GrpcExceptionFilter = __decorate([
155
+ GrpcExceptionFilter = __decorate([
136
156
  (0, common_1.Catch)()
137
157
  ], GrpcExceptionFilter);
158
+ exports.GrpcExceptionFilter = GrpcExceptionFilter;
@@ -0,0 +1,22 @@
1
+ import { Metadata } from '@grpc/grpc-js';
2
+ import { AbstractParam } from '../interfaces';
3
+ export declare const GRPC_PARAM_METADATA_KEY = "param";
4
+ export declare type GrpcParamMetadata = AbstractParam;
5
+ /**
6
+ * @author jun
7
+ * @description gRPC metadata에 AMQP param 호환 컨텍스트를 저장합니다.
8
+ * @process 1. AbstractParam 검증 -> 2. JSON 직렬화 -> 3. metadata param key에 저장
9
+ */
10
+ export declare function setGrpcParamMetadata(metadata: Metadata, param: GrpcParamMetadata): void;
11
+ /**
12
+ * @author jun
13
+ * @description gRPC metadata에서 AMQP param 호환 컨텍스트를 안전하게 읽습니다.
14
+ * @process 1. metadata param key 조회 -> 2. 문자열 검증 -> 3. JSON 파싱 실패 시 null 반환
15
+ */
16
+ export declare function parseGrpcParamMetadata(metadata: Metadata): GrpcParamMetadata | null;
17
+ /**
18
+ * @author jun
19
+ * @description gRPC metadata에서 필수 param 컨텍스트를 읽고 없거나 잘못되면 예외를 던집니다.
20
+ * @process 1. metadata param 파싱 -> 2. 유효성 확인 -> 3. 유효하지 않으면 BadRequest 발생
21
+ */
22
+ export declare function assertGrpcParamMetadata(metadata: Metadata): GrpcParamMetadata;
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertGrpcParamMetadata = exports.parseGrpcParamMetadata = exports.setGrpcParamMetadata = exports.GRPC_PARAM_METADATA_KEY = void 0;
4
+ const bad_request_1 = require("../exceptions/bad-request");
5
+ exports.GRPC_PARAM_METADATA_KEY = 'param';
6
+ const OPTIONAL_STRING_KEYS = [
7
+ 'userId',
8
+ 'provider',
9
+ 'clientId',
10
+ 'requestIp',
11
+ 'tossRequestId',
12
+ 'from',
13
+ ];
14
+ const OPTIONAL_BOOLEAN_KEYS = [
15
+ 'isAdmin',
16
+ 'isInternal',
17
+ ];
18
+ const OPTIONAL_NUMBER_KEYS = [
19
+ 'retryCount',
20
+ 'timeout',
21
+ ];
22
+ const OPTIONAL_OBJECT_KEYS = [
23
+ 'device',
24
+ 'externalTokenValue',
25
+ 'requestLog',
26
+ ];
27
+ /**
28
+ * @author jun
29
+ * @description gRPC param metadata 검증을 위한 record 타입 가드입니다.
30
+ * @process 1. object 여부 확인 -> 2. null/array 제외 -> 3. record로 좁힘
31
+ */
32
+ function isRecord(value) {
33
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
34
+ }
35
+ function isValidOptionalStringArray(value) {
36
+ return value === undefined || (Array.isArray(value) && value.every(item => typeof item === 'string'));
37
+ }
38
+ function hasInvalidOptionalFields(param) {
39
+ const hasInvalidString = OPTIONAL_STRING_KEYS.some(key => param[key] !== undefined && typeof param[key] !== 'string');
40
+ if (hasInvalidString) {
41
+ return true;
42
+ }
43
+ const hasInvalidBoolean = OPTIONAL_BOOLEAN_KEYS.some(key => param[key] !== undefined && typeof param[key] !== 'boolean');
44
+ if (hasInvalidBoolean) {
45
+ return true;
46
+ }
47
+ const hasInvalidNumber = OPTIONAL_NUMBER_KEYS.some(key => (param[key] !== undefined
48
+ && (typeof param[key] !== 'number' || !Number.isFinite(param[key]))));
49
+ if (hasInvalidNumber) {
50
+ return true;
51
+ }
52
+ const hasInvalidObject = OPTIONAL_OBJECT_KEYS.some(key => param[key] !== undefined && !isRecord(param[key]));
53
+ if (hasInvalidObject) {
54
+ return true;
55
+ }
56
+ return !isValidOptionalStringArray(param.froms);
57
+ }
58
+ function toGrpcParamMetadata(value) {
59
+ if (!isRecord(value)) {
60
+ return null;
61
+ }
62
+ if (typeof value.traceId !== 'string' || value.traceId.length === 0) {
63
+ return null;
64
+ }
65
+ if (hasInvalidOptionalFields(value)) {
66
+ return null;
67
+ }
68
+ return value;
69
+ }
70
+ function getMetadataStringValue(metadata) {
71
+ const values = metadata.get(exports.GRPC_PARAM_METADATA_KEY);
72
+ const value = values[0];
73
+ if (!value) {
74
+ return null;
75
+ }
76
+ if (typeof value === 'string') {
77
+ return value;
78
+ }
79
+ return value.toString('utf8');
80
+ }
81
+ function createInvalidGrpcParamMetadataError() {
82
+ return new bad_request_1.BadRequest('INVALID_GRPC_PARAM_METADATA', 'gRPC param metadata가 올바르지 않습니다.');
83
+ }
84
+ /**
85
+ * @author jun
86
+ * @description gRPC metadata에 AMQP param 호환 컨텍스트를 저장합니다.
87
+ * @process 1. AbstractParam 검증 -> 2. JSON 직렬화 -> 3. metadata param key에 저장
88
+ */
89
+ function setGrpcParamMetadata(metadata, param) {
90
+ const grpcParam = toGrpcParamMetadata(param);
91
+ if (!grpcParam) {
92
+ throw createInvalidGrpcParamMetadataError();
93
+ }
94
+ metadata.set(exports.GRPC_PARAM_METADATA_KEY, JSON.stringify(grpcParam));
95
+ }
96
+ exports.setGrpcParamMetadata = setGrpcParamMetadata;
97
+ /**
98
+ * @author jun
99
+ * @description gRPC metadata에서 AMQP param 호환 컨텍스트를 안전하게 읽습니다.
100
+ * @process 1. metadata param key 조회 -> 2. 문자열 검증 -> 3. JSON 파싱 실패 시 null 반환
101
+ */
102
+ function parseGrpcParamMetadata(metadata) {
103
+ const rawParam = getMetadataStringValue(metadata);
104
+ if (!rawParam) {
105
+ return null;
106
+ }
107
+ try {
108
+ return toGrpcParamMetadata(JSON.parse(rawParam));
109
+ }
110
+ catch (error) {
111
+ return null;
112
+ }
113
+ }
114
+ exports.parseGrpcParamMetadata = parseGrpcParamMetadata;
115
+ /**
116
+ * @author jun
117
+ * @description gRPC metadata에서 필수 param 컨텍스트를 읽고 없거나 잘못되면 예외를 던집니다.
118
+ * @process 1. metadata param 파싱 -> 2. 유효성 확인 -> 3. 유효하지 않으면 BadRequest 발생
119
+ */
120
+ function assertGrpcParamMetadata(metadata) {
121
+ const grpcParam = parseGrpcParamMetadata(metadata);
122
+ if (!grpcParam) {
123
+ throw createInvalidGrpcParamMetadataError();
124
+ }
125
+ return grpcParam;
126
+ }
127
+ exports.assertGrpcParamMetadata = assertGrpcParamMetadata;
@@ -3,6 +3,7 @@ export * from './generate-trace-id.function';
3
3
  export * from './amqp-publish.function';
4
4
  export * from './rewrite-file-value.function';
5
5
  export * from './amqp-call-manager.function';
6
+ export * from './grpc-param-metadata.function';
6
7
  export * from './extract-redis-url.function';
7
8
  export * from './abort';
8
9
  export * from './uncaught-exception-handler';
@@ -19,6 +19,7 @@ __exportStar(require("./generate-trace-id.function"), exports);
19
19
  __exportStar(require("./amqp-publish.function"), exports);
20
20
  __exportStar(require("./rewrite-file-value.function"), exports);
21
21
  __exportStar(require("./amqp-call-manager.function"), exports);
22
+ __exportStar(require("./grpc-param-metadata.function"), exports);
22
23
  __exportStar(require("./extract-redis-url.function"), exports);
23
24
  __exportStar(require("./abort"), exports);
24
25
  __exportStar(require("./uncaught-exception-handler"), exports);
@@ -39,6 +39,7 @@ const common_1 = require("@nestjs/common");
39
39
  const msa_logger_1 = __importStar(require("@yolo-croket-dev/msa-logger"));
40
40
  const operators_1 = require("rxjs/operators");
41
41
  const dd_trace_1 = __importDefault(require("dd-trace"));
42
+ const grpc_param_metadata_function_1 = require("../functions/grpc-param-metadata.function");
42
43
  const { SERVER_NAME } = process.env;
43
44
  const msaLogger = new msa_logger_1.default(msa_logger_1.ServerTypeEnum.SERVICE);
44
45
  const MAX_CUT_LENGTH = 500;
@@ -75,6 +76,7 @@ let LoggingServiceInterceptor = class LoggingServiceInterceptor {
75
76
  return data;
76
77
  }
77
78
  intercept(context, next) {
79
+ var _a;
78
80
  const now = Date.now();
79
81
  const args = context.getArgs();
80
82
  const rpcContext = context.switchToRpc();
@@ -87,13 +89,11 @@ let LoggingServiceInterceptor = class LoggingServiceInterceptor {
87
89
  let pattern; /** @xxx: Grpc: path , Mq: 메시지 패턴명 */
88
90
  let payload;
89
91
  if (isGrpc) {
90
- const metadataParam = metadata.get('param')[0] || JSON.stringify({
91
- param: {
92
- from: '-',
93
- froms: '-',
94
- },
95
- });
96
- param = JSON.parse(metadataParam);
92
+ param = (_a = (0, grpc_param_metadata_function_1.parseGrpcParamMetadata)(metadata)) !== null && _a !== void 0 ? _a : {
93
+ traceId: '-',
94
+ from: '-',
95
+ froms: ['-'],
96
+ };
97
97
  originalMessage = args[0];
98
98
  const gRPCArg = args[2];
99
99
  pattern = gRPCArg.path;
@@ -116,11 +116,11 @@ let LoggingServiceInterceptor = class LoggingServiceInterceptor {
116
116
  const tmpFroms = JSON.parse(JSON.stringify(pastFroms));
117
117
  tmpFroms.push(tmpFrom);
118
118
  if (isGrpc) {
119
- metadata.set('param', JSON.stringify({
119
+ (0, grpc_param_metadata_function_1.setGrpcParamMetadata)(metadata, {
120
120
  ...pastParam,
121
121
  from: tmpFrom,
122
122
  froms: tmpFroms,
123
- }));
123
+ });
124
124
  }
125
125
  else {
126
126
  data.param.from = tmpFrom;
@@ -219,7 +219,7 @@ let LoggingServiceInterceptor = class LoggingServiceInterceptor {
219
219
  }));
220
220
  }
221
221
  };
222
- exports.LoggingServiceInterceptor = LoggingServiceInterceptor;
223
- exports.LoggingServiceInterceptor = LoggingServiceInterceptor = __decorate([
222
+ LoggingServiceInterceptor = __decorate([
224
223
  (0, common_1.Injectable)()
225
224
  ], LoggingServiceInterceptor);
225
+ exports.LoggingServiceInterceptor = LoggingServiceInterceptor;
@@ -370,9 +370,9 @@ let RPCInterceptor = class RPCInterceptor {
370
370
  })));
371
371
  }
372
372
  };
373
- exports.RPCInterceptor = RPCInterceptor;
374
- exports.RPCInterceptor = RPCInterceptor = __decorate([
373
+ RPCInterceptor = __decorate([
375
374
  (0, common_1.Injectable)(),
376
375
  __metadata("design:paramtypes", [core_1.Reflector,
377
376
  graceful_shutdown_service_1.GracefulShutdownService])
378
377
  ], RPCInterceptor);
378
+ exports.RPCInterceptor = RPCInterceptor;
@@ -18,10 +18,9 @@ var ErrActionTypeEnum;
18
18
  ErrActionTypeEnum["LEGACY_SHOW_TOAST"] = "LEGACY_SHOW_TOAST";
19
19
  ErrActionTypeEnum["FORCE_UPDATE"] = "FORCE_UPDATE";
20
20
  ErrActionTypeEnum["UPDATE_ACCESS_TOKEN"] = "UPDATE_ACCESS_TOKEN";
21
- })(ErrActionTypeEnum || (exports.ErrActionTypeEnum = ErrActionTypeEnum = {}));
21
+ })(ErrActionTypeEnum = exports.ErrActionTypeEnum || (exports.ErrActionTypeEnum = {}));
22
22
  class ErrAction {
23
23
  }
24
- exports.ErrAction = ErrAction;
25
24
  __decorate([
26
25
  (0, swagger_1.ApiProperty)({
27
26
  description: '액션 타입',
@@ -58,9 +57,9 @@ __decorate([
58
57
  }),
59
58
  __metadata("design:type", String)
60
59
  ], ErrAction.prototype, "webViewUrl", void 0);
60
+ exports.ErrAction = ErrAction;
61
61
  class ErrInfo {
62
62
  }
63
- exports.ErrInfo = ErrInfo;
64
63
  __decorate([
65
64
  (0, swagger_1.ApiProperty)({
66
65
  description: '에러 종류 식별자',
@@ -89,3 +88,4 @@ __decorate([
89
88
  }),
90
89
  __metadata("design:type", Object)
91
90
  ], ErrInfo.prototype, "errOption", void 0);
91
+ exports.ErrInfo = ErrInfo;
@@ -21,4 +21,4 @@ var LogLevelEnum;
21
21
  LogLevelEnum["DEBUG"] = "debug";
22
22
  LogLevelEnum["WARN"] = "warn";
23
23
  LogLevelEnum["ERROR"] = "error";
24
- })(LogLevelEnum || (exports.LogLevelEnum = LogLevelEnum = {}));
24
+ })(LogLevelEnum = exports.LogLevelEnum || (exports.LogLevelEnum = {}));
@@ -27,7 +27,7 @@ export interface MsaException {
27
27
  errOption?: MsaExceptionOption;
28
28
  }
29
29
  /** HttpException과 MsaException을 결합한 타입 */
30
- export type MsaHttpException = MsaException & HttpException;
30
+ export declare type MsaHttpException = MsaException & HttpException;
31
31
  /**
32
32
  * @description MsaException 타입 가드
33
33
  * @param error 검사할 에러 객체
@@ -48,8 +48,7 @@ let GracefulShutdownModule = class GracefulShutdownModule {
48
48
  msaLogger.info('GracefulShutdownService.onModuleDestroy: all interceptors finished, shutting down', { traceId: 'ON_MODULE_DESTROY' });
49
49
  }
50
50
  };
51
- exports.GracefulShutdownModule = GracefulShutdownModule;
52
- exports.GracefulShutdownModule = GracefulShutdownModule = __decorate([
51
+ GracefulShutdownModule = __decorate([
53
52
  (0, common_1.Global)(),
54
53
  (0, common_1.Module)({
55
54
  providers: [graceful_shutdown_service_1.GracefulShutdownService],
@@ -57,3 +56,4 @@ exports.GracefulShutdownModule = GracefulShutdownModule = __decorate([
57
56
  }),
58
57
  __metadata("design:paramtypes", [graceful_shutdown_service_1.GracefulShutdownService])
59
58
  ], GracefulShutdownModule);
59
+ exports.GracefulShutdownModule = GracefulShutdownModule;
@@ -1,7 +1,7 @@
1
1
  import { RmqOptions, ServerRMQ } from '@nestjs/microservices';
2
2
  import { Channel } from 'amqp-connection-manager';
3
3
  import { ConsumeMessage } from 'amqplib';
4
- export type GracefulRmqOptions = RmqOptions['options'] & {
4
+ export declare type GracefulRmqOptions = RmqOptions['options'] & {
5
5
  waitingEndingHandlersTimeoutMs?: number;
6
6
  waitingEndingHandlersIntervalMs?: number;
7
7
  };
@@ -133,7 +133,7 @@ let GracefulShutdownService = class GracefulShutdownService {
133
133
  });
134
134
  }
135
135
  };
136
- exports.GracefulShutdownService = GracefulShutdownService;
137
- exports.GracefulShutdownService = GracefulShutdownService = __decorate([
136
+ GracefulShutdownService = __decorate([
138
137
  (0, common_1.Injectable)()
139
138
  ], GracefulShutdownService);
139
+ exports.GracefulShutdownService = GracefulShutdownService;
@@ -15,10 +15,10 @@ const redisProvider = {
15
15
  };
16
16
  let RedisModule = class RedisModule {
17
17
  };
18
- exports.RedisModule = RedisModule;
19
- exports.RedisModule = RedisModule = __decorate([
18
+ RedisModule = __decorate([
20
19
  (0, common_1.Module)({
21
20
  providers: [redisProvider],
22
21
  exports: [redisProvider],
23
22
  })
24
23
  ], RedisModule);
24
+ exports.RedisModule = RedisModule;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yolo-croket-dev/core",
3
- "version": "1.10.0-jun.0",
3
+ "version": "1.10.0-jun.2",
4
4
  "author": "Yolo Co., Ltd.",
5
5
  "description": "Yolo commonization module",
6
6
  "main": "index.js",
@@ -31,8 +31,8 @@ let ValidationPipe = class ValidationPipe {
31
31
  return value;
32
32
  }
33
33
  };
34
- exports.ValidationPipe = ValidationPipe;
35
- exports.ValidationPipe = ValidationPipe = __decorate([
34
+ ValidationPipe = __decorate([
36
35
  (0, common_1.Injectable)(),
37
36
  __metadata("design:paramtypes", [Object])
38
37
  ], ValidationPipe);
38
+ exports.ValidationPipe = ValidationPipe;