@squiz/optimization-utils 2.0.0 → 2.0.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.
Files changed (80) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cloudflare/ImplCloudflareKVHttpService.d.ts +2 -2
  3. package/dist/cloudflare/ImplCloudflareKVHttpService.js +8 -10
  4. package/dist/cloudflare/ImplCloudflareKVHttpService.js.map +1 -1
  5. package/dist/config/ConfigurationLoader.d.ts +1 -1
  6. package/dist/config/ConfigurationLoader.js +3 -4
  7. package/dist/config/ConfigurationLoader.js.map +1 -1
  8. package/dist/index.d.ts +0 -11
  9. package/dist/index.js +0 -11
  10. package/dist/index.js.map +1 -1
  11. package/dist/scheduler/EventBridgeScheduler.d.ts +1 -1
  12. package/dist/scheduler/EventBridgeScheduler.js +4 -5
  13. package/dist/scheduler/EventBridgeScheduler.js.map +1 -1
  14. package/dist/testing/mock.d.ts +0 -8
  15. package/dist/testing/mock.js +1 -35
  16. package/dist/testing/mock.js.map +1 -1
  17. package/package.json +4 -1
  18. package/src/cloudflare/ImplCloudflareKVHttpService.ts +5 -4
  19. package/src/cloudflare/__tests__/ImplCloudflareKVHttpService.spec.ts +4 -4
  20. package/src/config/ConfigurationLoader.ts +4 -2
  21. package/src/config/__tests__/ConfigurationLoader.spec.ts +3 -3
  22. package/src/event/__tests__/DynamoDBEventMapper.spec.ts +1 -1
  23. package/src/index.ts +0 -16
  24. package/src/scheduler/EventBridgeScheduler.ts +1 -2
  25. package/src/scheduler/__tests__/EventBridgeScheduler.spec.ts +2 -5
  26. package/src/testing/mock.ts +0 -47
  27. package/dist/exception/DomainException.d.ts +0 -18
  28. package/dist/exception/DomainException.js +0 -41
  29. package/dist/exception/DomainException.js.map +0 -1
  30. package/dist/httpClient/FetchHttpClient.d.ts +0 -7
  31. package/dist/httpClient/FetchHttpClient.js +0 -86
  32. package/dist/httpClient/FetchHttpClient.js.map +0 -1
  33. package/dist/httpClient/HttpClient.d.ts +0 -25
  34. package/dist/httpClient/HttpClient.js +0 -45
  35. package/dist/httpClient/HttpClient.js.map +0 -1
  36. package/dist/httpClient/HttpRequestBuilder.d.ts +0 -22
  37. package/dist/httpClient/HttpRequestBuilder.js +0 -126
  38. package/dist/httpClient/HttpRequestBuilder.js.map +0 -1
  39. package/dist/logger/Logger.d.ts +0 -10
  40. package/dist/logger/Logger.js +0 -30
  41. package/dist/logger/Logger.js.map +0 -1
  42. package/dist/logger/LoggerMessage.d.ts +0 -43
  43. package/dist/logger/LoggerMessage.js +0 -111
  44. package/dist/logger/LoggerMessage.js.map +0 -1
  45. package/dist/logger/LogsHandler.d.ts +0 -11
  46. package/dist/logger/LogsHandler.js +0 -66
  47. package/dist/logger/LogsHandler.js.map +0 -1
  48. package/dist/logger/LogsLambdaHandler.d.ts +0 -13
  49. package/dist/logger/LogsLambdaHandler.js +0 -31
  50. package/dist/logger/LogsLambdaHandler.js.map +0 -1
  51. package/dist/logger/RemoteLogger.d.ts +0 -30
  52. package/dist/logger/RemoteLogger.js +0 -35
  53. package/dist/logger/RemoteLogger.js.map +0 -1
  54. package/dist/logger/SquizRemoteLogger.d.ts +0 -53
  55. package/dist/logger/SquizRemoteLogger.js +0 -128
  56. package/dist/logger/SquizRemoteLogger.js.map +0 -1
  57. package/dist/validation/handleValidation.d.ts +0 -2
  58. package/dist/validation/handleValidation.js +0 -11
  59. package/dist/validation/handleValidation.js.map +0 -1
  60. package/dist/valueObject/TenantId.d.ts +0 -10
  61. package/dist/valueObject/TenantId.js +0 -23
  62. package/dist/valueObject/TenantId.js.map +0 -1
  63. package/src/exception/DomainException.ts +0 -34
  64. package/src/httpClient/FetchHttpClient.ts +0 -92
  65. package/src/httpClient/HttpClient.ts +0 -46
  66. package/src/httpClient/HttpRequestBuilder.ts +0 -120
  67. package/src/httpClient/__tests__/FetchHttpClient.spec.ts +0 -146
  68. package/src/httpClient/__tests__/HttpClient.spec.ts +0 -52
  69. package/src/httpClient/__tests__/httpRequestBuilder.spec.ts +0 -75
  70. package/src/logger/Logger.ts +0 -40
  71. package/src/logger/LoggerMessage.ts +0 -179
  72. package/src/logger/LogsHandler.ts +0 -66
  73. package/src/logger/LogsLambdaHandler.ts +0 -43
  74. package/src/logger/RemoteLogger.ts +0 -32
  75. package/src/logger/SquizRemoteLogger.ts +0 -154
  76. package/src/logger/__tests__/LoggerMessage.spec.ts +0 -147
  77. package/src/logger/__tests__/LogsHandler.spec.ts +0 -77
  78. package/src/logger/__tests__/SquizRemoteLogger.spec.ts +0 -185
  79. package/src/validation/handleValidation.ts +0 -13
  80. package/src/valueObject/TenantId.ts +0 -27
@@ -1,154 +0,0 @@
1
- import { z } from 'zod';
2
- import { RemoteLog, RemoteLogLevel, RemoteLogger } from './RemoteLogger';
3
- import { TenantId } from '../valueObject/TenantId';
4
- import { injectable } from 'inversify';
5
- import { Logger } from './Logger';
6
- import { HttpRequestBuilderFactory } from '../httpClient/HttpRequestBuilder';
7
- import { HttpMethod } from '../httpClient/HttpClient';
8
- import { createLog } from './LoggerMessage';
9
-
10
- export type SquizRemoteLoggerServiceConfig = {
11
- loggerServiceUrl: URL;
12
- createLogsPath: string;
13
- apiKey: string;
14
- serviceName: string;
15
- };
16
- export type SquizRemoteLoggerServiceConfigProvider =
17
- () => Promise<SquizRemoteLoggerServiceConfig>;
18
-
19
- const CREATE_LOGS_REQUEST_BODY_DTO_SCHEMA = z
20
- .object({
21
- level: z.enum(['INFO', 'WARNING', 'ERROR']),
22
- service: z.string(),
23
- timestamp: z.string().datetime(),
24
- host: z.string().optional(),
25
- userid: z.string().optional(),
26
- message: z.string().optional(),
27
- tags: z.string().optional(),
28
- traceid: z.string().optional(),
29
- })
30
- .array();
31
-
32
- export type CreateLogsRequestBodyDto = z.infer<
33
- typeof CREATE_LOGS_REQUEST_BODY_DTO_SCHEMA
34
- >;
35
-
36
- @injectable()
37
- export class SquizRemoteLogger implements RemoteLogger {
38
- constructor(
39
- private readonly config: SquizRemoteLoggerServiceConfigProvider,
40
- private readonly logger: Logger,
41
- private readonly httpRequestBuilderFactory: HttpRequestBuilderFactory,
42
- ) {}
43
-
44
- async info(log: Omit<RemoteLog, 'level'>): Promise<void> {
45
- await this.callApi(log.tenantId, [
46
- await this.mapRemoteToRequestBody({
47
- ...log,
48
- level: RemoteLogLevel.INFO,
49
- }),
50
- ]);
51
- }
52
-
53
- async warn(log: Omit<RemoteLog, 'level'>): Promise<void> {
54
- await this.callApi(log.tenantId, [
55
- await this.mapRemoteToRequestBody({
56
- ...log,
57
- level: RemoteLogLevel.WARN,
58
- }),
59
- ]);
60
- }
61
-
62
- async error(log: Omit<RemoteLog, 'level'>): Promise<void> {
63
- await this.callApi(log.tenantId, [
64
- await this.mapRemoteToRequestBody({
65
- ...log,
66
- level: RemoteLogLevel.ERROR,
67
- }),
68
- ]);
69
- }
70
-
71
- async log(logs: ReadonlyArray<RemoteLog>): Promise<void> {
72
- const logsGrouped = await logs.reduce(
73
- async (acc, log) => {
74
- const resolvedAcc = await acc;
75
- const tenantLogs = resolvedAcc[log.tenantId.valueOf()];
76
- const mappedLog = await this.mapRemoteToRequestBody(log);
77
- const mappedLogs: CreateLogsRequestBodyDto = tenantLogs
78
- ? [...tenantLogs, mappedLog]
79
- : [mappedLog];
80
-
81
- return {
82
- ...resolvedAcc,
83
- [log.tenantId.valueOf()]: mappedLogs,
84
- };
85
- },
86
- Promise.resolve({}) as Promise<Record<string, CreateLogsRequestBodyDto>>,
87
- );
88
-
89
- const entries = Object.entries(logsGrouped);
90
- const promises = entries.map(async ([tenantId, logs]) => {
91
- await this.callApi(new TenantId(tenantId), logs);
92
- });
93
-
94
- await Promise.all(promises);
95
- }
96
-
97
- private async mapRemoteToRequestBody({
98
- tenantId,
99
- ...log
100
- }: RemoteLog): Promise<CreateLogsRequestBodyDto[number]> {
101
- const config = await this.config();
102
- const logLevels = new Map<
103
- RemoteLogLevel,
104
- CreateLogsRequestBodyDto[number]['level']
105
- >([
106
- [RemoteLogLevel.INFO, 'INFO'],
107
- [RemoteLogLevel.WARN, 'WARNING'],
108
- [RemoteLogLevel.ERROR, 'ERROR'],
109
- ]);
110
-
111
- return {
112
- ...log,
113
- timestamp: log.timestamp
114
- ? log.timestamp.toISOString()
115
- : new Date().toISOString(),
116
- level: logLevels.get(
117
- log.level,
118
- ) as CreateLogsRequestBodyDto[number]['level'],
119
- service: config.serviceName,
120
- };
121
- }
122
-
123
- private async callApi(
124
- tenantId: TenantId,
125
- logs: CreateLogsRequestBodyDto,
126
- ): Promise<void> {
127
- if (!logs.length) {
128
- return;
129
- }
130
-
131
- const logMessage = createLog().attachTenantId(tenantId).create(this);
132
-
133
- this.logger.debug(...logMessage(`started receiving config`));
134
- const config = await this.config();
135
-
136
- this.logger.debug(...logMessage(`finished receiving config`));
137
-
138
- const url = new URL(
139
- config.createLogsPath + `/${tenantId.valueOf()}`,
140
- config.loggerServiceUrl,
141
- );
142
-
143
- this.logger.debug(...logMessage(`started request to: ${url.toString()}`));
144
- await this.httpRequestBuilderFactory
145
- .create()
146
- .url(url)
147
- .method(HttpMethod.POST)
148
- .applicationJson()
149
- .authorizationByXApiKey(config.apiKey)
150
- .body(logs)
151
- .sendRequest();
152
- this.logger.debug(...logMessage(`finished request to: ${url.toString()}`));
153
- }
154
- }
@@ -1,147 +0,0 @@
1
- import { randomUUID } from 'crypto';
2
- import { createLog, createLogMessage } from '../LoggerMessage';
3
- import { TenantId } from '../../valueObject/TenantId';
4
-
5
- jest.mock('crypto');
6
-
7
- describe('createLogMessage', () => {
8
- class ExampleClass {}
9
-
10
- const mockUUID = (uuid: string): jest.Mock =>
11
- (randomUUID as jest.Mock).mockReturnValueOnce(uuid);
12
-
13
- beforeEach(() => {
14
- jest.resetAllMocks();
15
- });
16
-
17
- it('should create a log message with the instance of class', () => {
18
- mockUUID('some-uuid');
19
-
20
- const logMessage = createLogMessage(new ExampleClass());
21
-
22
- expect(logMessage('example message')).toBe(
23
- '[ExampleClass][Context: some-uuid] example message',
24
- );
25
- });
26
-
27
- it('should create a log message with given metadata', () => {
28
- mockUUID('some-uuid');
29
-
30
- const logMessage = createLogMessage(
31
- new ExampleClass(),
32
- 'secondMetadata',
33
- 'thirdMetadata',
34
- );
35
-
36
- expect(logMessage('example message')).toBe(
37
- '[ExampleClass][secondMetadata][thirdMetadata][Context: some-uuid] example message',
38
- );
39
- });
40
-
41
- it('should create a log message without additional metadata', () => {
42
- mockUUID('some-uuid');
43
-
44
- const logMessage = createLogMessage();
45
-
46
- expect(logMessage('example message')).toBe(
47
- '[Context: some-uuid] example message',
48
- );
49
- });
50
- });
51
-
52
- describe('createLog', () => {
53
- const mockUUID: jest.Mock<
54
- ReturnType<typeof randomUUID>,
55
- Parameters<typeof randomUUID>
56
- > = randomUUID as jest.Mock;
57
-
58
- beforeEach(() => {
59
- mockUUID.mockReset();
60
- });
61
-
62
- it('should create a log message with attached tenant id as metadata', () => {
63
- const logMessage = createLog().attachTenantId(new TenantId('1')).create();
64
-
65
- const [, metadata] = logMessage('test');
66
-
67
- expect(metadata).toEqual({ tenantId: '1' });
68
- });
69
-
70
- it('should create a log message with attached processable = true as metadata', () => {
71
- const logMessage = createLog().isProcessable().create();
72
-
73
- const [, metadata] = logMessage('test');
74
-
75
- expect(metadata).toEqual({ processable: true });
76
- });
77
-
78
- it('should create a log message with given message', () => {
79
- const logMessage = createLog().isProcessable().create();
80
-
81
- const [message] = logMessage('test');
82
-
83
- expect(message).toContain('test');
84
- });
85
-
86
- it('should create a log message with message and caller', () => {
87
- class ExampleClass {}
88
-
89
- const logMessage = createLog().isProcessable().create(new ExampleClass());
90
-
91
- const [message] = logMessage('test');
92
-
93
- expect(message).toContain('test');
94
- });
95
-
96
- it('should combine metadata', () => {
97
- mockUUID.mockReturnValue('70c1d16a-c15f-462b-844b-c2cd379f425f');
98
-
99
- const logMessage = createLog()
100
- .attachTenantId(new TenantId('1'))
101
- .isProcessable()
102
- .attachMetadata({ some: 'metadata' })
103
- .create('SomeCaller');
104
-
105
- const [, metadata] = logMessage('test');
106
-
107
- expect(metadata).toEqual({
108
- tenantId: '1',
109
- processable: true,
110
- some: 'metadata',
111
- context: '70c1d16a-c15f-462b-844b-c2cd379f425f',
112
- caller: 'SomeCaller',
113
- });
114
- });
115
-
116
- it('should take the context from the base createLog function', () => {
117
- mockUUID
118
- .mockReturnValueOnce('70c1d16a-c15f-462b-844b-c2cd379f425f')
119
- .mockReturnValue('16ed5709-8e7f-4fcd-a4e4-73dd01a66914');
120
-
121
- const baseLogger = createLog();
122
-
123
- const logMessage = createLog({
124
- metadata: baseLogger.getMetadata(),
125
- }).create();
126
-
127
- const [, metadata] = logMessage('test');
128
-
129
- expect(metadata).toEqual({
130
- context: '70c1d16a-c15f-462b-844b-c2cd379f425f',
131
- });
132
- });
133
-
134
- it('should attach remote to the log', () => {
135
- const logMessage = createLog().create();
136
-
137
- const result = logMessage.remote(
138
- 'example message',
139
- new TenantId('example-tenant'),
140
- );
141
-
142
- expect(result).toEqual([
143
- 'example message',
144
- { remote: true, tenantId: 'example-tenant' },
145
- ]);
146
- });
147
- });
@@ -1,77 +0,0 @@
1
- import { LogsHandler } from '../LogsHandler';
2
- import zlib from 'zlib';
3
- import { CloudWatchLogsEvent } from 'aws-lambda';
4
- import { faker } from '@faker-js/faker';
5
- import { CloudWatchLogsLogEvent } from 'aws-lambda/trigger/cloudwatch-logs';
6
- import { RemoteLog, RemoteLogger, RemoteLogLevel } from '../RemoteLogger';
7
- import { createLoggerMock, createRemoteLoggerMock } from '../../testing/mock';
8
- import { TenantId } from '../../valueObject/TenantId';
9
-
10
- const createCloudWatchEvent = (
11
- logs: Array<Record<string, unknown>>,
12
- ): CloudWatchLogsEvent => {
13
- const cloudWatchLogsLogEvents: Array<CloudWatchLogsLogEvent> = logs.map(
14
- (l) => {
15
- return {
16
- id: faker.number.bigInt().toString(),
17
- timestamp: faker.date.past().valueOf(),
18
- message: JSON.stringify(l),
19
- };
20
- },
21
- );
22
- const base64 = Buffer.from(
23
- JSON.stringify({ logEvents: cloudWatchLogsLogEvents }),
24
- );
25
- const zipped = zlib.gzipSync(base64);
26
-
27
- return {
28
- awslogs: {
29
- data: zipped.toString('base64'),
30
- },
31
- };
32
- };
33
-
34
- describe('LogsHandler', () => {
35
- let logsHandler: LogsHandler;
36
- let remoteLogger: RemoteLogger;
37
- const remoteLog: Omit<RemoteLog, 'tenantId'> & {
38
- tenantId: string;
39
- } = {
40
- level: faker.helpers.arrayElement(Object.values(RemoteLogLevel)),
41
- message: faker.word.words(),
42
- tenantId: faker.word.words(),
43
- timestamp: faker.date.past(),
44
- };
45
-
46
- beforeEach(() => {
47
- remoteLogger = createRemoteLoggerMock();
48
-
49
- logsHandler = new LogsHandler(remoteLogger, createLoggerMock());
50
- });
51
-
52
- it('should decode a CloudWatchLogsEvent and send to the RemoteLogger', async () => {
53
- await logsHandler.handle(createCloudWatchEvent([remoteLog]));
54
-
55
- expect(remoteLogger.log).toHaveBeenCalledWith([
56
- {
57
- ...remoteLog,
58
- tenantId: new TenantId(remoteLog.tenantId),
59
- },
60
- ]);
61
- });
62
-
63
- it('should decode a CloudWatchLogsEvent and send only the RemoteLogs to the RemoteLogger', async () => {
64
- const otherLog = {
65
- test: faker.word.words(),
66
- };
67
-
68
- await logsHandler.handle(createCloudWatchEvent([remoteLog, otherLog]));
69
-
70
- expect(remoteLogger.log).toHaveBeenCalledWith([
71
- {
72
- ...remoteLog,
73
- tenantId: new TenantId(remoteLog.tenantId),
74
- },
75
- ]);
76
- });
77
- });
@@ -1,185 +0,0 @@
1
- import { faker } from '@faker-js/faker';
2
- import { createHttpClientMock, createLoggerMock } from '../../testing/mock';
3
- import { Container } from 'inversify';
4
- import { HttpRequestBuilderFactory } from '../../httpClient/HttpRequestBuilder';
5
- import {
6
- HttpClient,
7
- HttpMethod,
8
- HttpRequestOptions,
9
- HttpResponse,
10
- } from '../../httpClient/HttpClient';
11
- import { Logger } from '../Logger';
12
- import {
13
- CreateLogsRequestBodyDto,
14
- SquizRemoteLogger,
15
- SquizRemoteLoggerServiceConfigProvider,
16
- } from '../SquizRemoteLogger';
17
- import { RemoteLogLevel, RemoteLogger } from '../RemoteLogger';
18
- import { TenantId } from '../../valueObject/TenantId';
19
-
20
- describe('SquizRemoteLogger', () => {
21
- let logger: RemoteLogger;
22
- let httpClient: HttpClient;
23
- const config: Awaited<ReturnType<SquizRemoteLoggerServiceConfigProvider>> = {
24
- loggerServiceUrl: new URL(
25
- 'https://develop-apps-dxp-console.dev.dxp.squiz.cloud',
26
- ),
27
- createLogsPath: '/__dxp/us/logging',
28
- apiKey: 'apiKey',
29
- serviceName: 'experiment',
30
- };
31
- const expectedPayload = (
32
- tenantId: string,
33
- body: CreateLogsRequestBodyDto,
34
- ): HttpRequestOptions => {
35
- return {
36
- url: new URL(
37
- `https://develop-apps-dxp-console.dev.dxp.squiz.cloud/__dxp/us/logging/${tenantId}`,
38
- ),
39
- method: HttpMethod.POST,
40
- body,
41
- headers: {
42
- 'Content-Type': 'application/json',
43
- 'x-api-key': 'apiKey',
44
- },
45
- };
46
- };
47
-
48
- beforeEach(() => {
49
- const container = new Container({ defaultScope: 'Singleton' });
50
-
51
- container.bind(SquizRemoteLogger).toDynamicValue(({ container: c }) => {
52
- return new SquizRemoteLogger(
53
- () => Promise.resolve(config),
54
- c.get(Logger),
55
- c.get(HttpRequestBuilderFactory),
56
- );
57
- });
58
- container.bind(Logger).toConstantValue(createLoggerMock());
59
- container.bind(HttpRequestBuilderFactory).toSelf();
60
- container.bind(HttpClient).toConstantValue(createHttpClientMock());
61
-
62
- httpClient = container.get(HttpClient);
63
- logger = container.get(SquizRemoteLogger);
64
- });
65
-
66
- describe('batch logging', () => {
67
- it('should send logs for particular tenant in one call', async () => {
68
- jest
69
- .spyOn(httpClient, 'sendRequest')
70
- .mockResolvedValueOnce(new HttpResponse({ statusCode: 201, body: {} }))
71
- .mockResolvedValueOnce(new HttpResponse({ statusCode: 201, body: {} }));
72
- const logs = [
73
- {
74
- level: RemoteLogLevel.INFO,
75
- message: faker.word.words(),
76
- timestamp: faker.date.past(),
77
- tenantId: new TenantId('1'),
78
- },
79
- {
80
- level: RemoteLogLevel.INFO,
81
- message: faker.word.words(),
82
- timestamp: faker.date.past(),
83
- tenantId: new TenantId('1'),
84
- },
85
- {
86
- level: RemoteLogLevel.INFO,
87
- message: faker.word.words(),
88
- timestamp: faker.date.past(),
89
- tenantId: new TenantId('2'),
90
- },
91
- ] as const;
92
-
93
- await logger.log(logs);
94
-
95
- expect(httpClient.sendRequest).toHaveBeenCalledWith(
96
- expectedPayload('1', [
97
- {
98
- level: 'INFO',
99
- message: logs[0].message,
100
- timestamp: logs[0].timestamp.toISOString(),
101
- service: 'experiment',
102
- },
103
- {
104
- level: 'INFO',
105
- message: logs[1].message,
106
- timestamp: logs[1].timestamp.toISOString(),
107
- service: 'experiment',
108
- },
109
- ]),
110
- );
111
- expect(httpClient.sendRequest).toHaveBeenCalledWith(
112
- expectedPayload('2', [
113
- {
114
- level: 'INFO',
115
- message: logs[2].message,
116
- timestamp: logs[2].timestamp.toISOString(),
117
- service: 'experiment',
118
- },
119
- ]),
120
- );
121
- });
122
- });
123
-
124
- describe('simple logging', () => {
125
- const log = {
126
- message: faker.word.words(),
127
- timestamp: faker.date.past(),
128
- tenantId: new TenantId('1'),
129
- };
130
-
131
- beforeEach(() => {
132
- jest.spyOn(httpClient, 'sendRequest').mockResolvedValueOnce(
133
- new HttpResponse({
134
- statusCode: 201,
135
- body: {},
136
- }),
137
- );
138
- });
139
-
140
- it('should send INFO log', async () => {
141
- await logger.info(log);
142
-
143
- expect(httpClient.sendRequest).toHaveBeenCalledWith(
144
- expectedPayload(log.tenantId.valueOf(), [
145
- {
146
- message: log.message,
147
- timestamp: log.timestamp.toISOString(),
148
- level: 'INFO',
149
- service: 'experiment',
150
- },
151
- ]),
152
- );
153
- });
154
-
155
- it('should send WARN log', async () => {
156
- await logger.warn(log);
157
-
158
- expect(httpClient.sendRequest).toHaveBeenCalledWith(
159
- expectedPayload(log.tenantId.valueOf(), [
160
- {
161
- message: log.message,
162
- timestamp: log.timestamp.toISOString(),
163
- level: 'WARNING',
164
- service: 'experiment',
165
- },
166
- ]),
167
- );
168
- });
169
-
170
- it('should send ERROR log', async () => {
171
- await logger.error(log);
172
-
173
- expect(httpClient.sendRequest).toHaveBeenCalledWith(
174
- expectedPayload(log.tenantId.valueOf(), [
175
- {
176
- message: log.message,
177
- timestamp: log.timestamp.toISOString(),
178
- level: 'ERROR',
179
- service: 'experiment',
180
- },
181
- ]),
182
- );
183
- });
184
- });
185
- });
@@ -1,13 +0,0 @@
1
- import { SafeParseReturnType } from 'zod';
2
- import { ValidationException } from '../exception/DomainException';
3
-
4
- export function handleValidation<T, O>(
5
- safeParseResult: SafeParseReturnType<T, O>,
6
- errorMessage = 'Validation Failed',
7
- ): O {
8
- if (!safeParseResult.success) {
9
- throw new ValidationException(errorMessage, safeParseResult.error.issues);
10
- }
11
-
12
- return safeParseResult.data;
13
- }
@@ -1,27 +0,0 @@
1
- import { z } from 'zod';
2
- import { handleValidation } from '../validation/handleValidation';
3
-
4
- export const TENANT_ID_SCHEMA = z.string().min(1);
5
-
6
- export type TenantIdValue = z.infer<typeof TENANT_ID_SCHEMA>;
7
-
8
- export class TenantId {
9
- static parse(data: unknown): TenantId {
10
- return new TenantId(
11
- handleValidation(
12
- TENANT_ID_SCHEMA.safeParse(data),
13
- 'The validation of tenant id failed',
14
- ),
15
- );
16
- }
17
-
18
- constructor(private readonly value: TenantIdValue) {}
19
-
20
- valueOf(): TenantIdValue {
21
- return this.value;
22
- }
23
-
24
- equals(tenantId: TenantId): boolean {
25
- return this.value === tenantId.value;
26
- }
27
- }