Skip to content

Commit 3fbe4f3

Browse files
Merge pull request #3516 from RedisInsight/feature/RI-5749_cover_422_error
#RI-5749 - cover 422 error
2 parents 0d54fa4 + a334e3a commit 3fbe4f3

File tree

28 files changed

+291
-117
lines changed

28 files changed

+291
-117
lines changed

redisinsight/api/src/constants/custom-error-codes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,5 @@ export enum CustomErrorCodes {
5757
RdiDeployPipelineFailure = 11_401,
5858
RdiUnauthorized = 11_402,
5959
RdiInternalServerError = 11_403,
60-
60+
RdiValidationError = 11_404,
6161
}

redisinsight/api/src/constants/error-messages.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,5 @@ export default {
110110

111111
RDI_DEPLOY_PIPELINE_FAILURE: 'Failed to deploy pipeline',
112112
RDI_TIMEOUT_ERROR: 'Encountered a timeout error while attempting to retrieve data',
113+
RDI_VALIDATION_ERROR: 'Validation error',
113114
};

redisinsight/api/src/modules/rdi/client/api.rdi.client.ts

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import axios, { AxiosInstance } from 'axios';
22
import { plainToClass } from 'class-transformer';
33
import { decode } from 'jsonwebtoken';
4-
import { Request } from 'express';
54

65
import { RdiClient } from 'src/modules/rdi/client/rdi.client';
76
import {
@@ -14,10 +13,10 @@ import {
1413
import {
1514
RdiDryRunJobDto,
1615
RdiDryRunJobResponseDto,
16+
RdiTemplateResponseDto,
1717
RdiTestConnectionsResponseDto,
1818
} from 'src/modules/rdi/dto';
1919
import {
20-
RdiPipelineDeployFailedException,
2120
RdiPipelineInternalServerErrorException,
2221
wrapRdiPipelineError,
2322
} from 'src/modules/rdi/exceptions';
@@ -29,8 +28,7 @@ import {
2928
} from 'src/modules/rdi/models';
3029
import { convertKeysToCamelCase } from 'src/utils/base.helper';
3130
import { RdiPipelineTimeoutException } from 'src/modules/rdi/exceptions/rdi-pipeline.timeout-error.exception';
32-
33-
const RDI_DEPLOY_FAILED_STATUS = 'failed';
31+
import * as https from 'https';
3432

3533
export class ApiRdiClient extends RdiClient {
3634
protected readonly client: AxiosInstance;
@@ -42,6 +40,9 @@ export class ApiRdiClient extends RdiClient {
4240
this.client = axios.create({
4341
baseURL: rdi.url,
4442
timeout: RDI_TIMEOUT,
43+
httpsAgent: new https.Agent({
44+
rejectUnauthorized: false,
45+
}),
4546
});
4647
}
4748

@@ -72,25 +73,32 @@ export class ApiRdiClient extends RdiClient {
7273
}
7374
}
7475

75-
async getTemplate(options: object): Promise<object> {
76+
async getConfigTemplate(pipelineType: string, dbType: string): Promise<RdiTemplateResponseDto> {
7677
try {
77-
const response = await this.client.get(RdiUrl.GetTemplate, { params: options });
78+
const response = await this.client.get(`${RdiUrl.GetConfigTemplate}/${pipelineType}/${dbType}`);
7879
return response.data;
7980
} catch (error) {
8081
throw wrapRdiPipelineError(error);
8182
}
8283
}
8384

84-
async deploy(pipeline: RdiPipeline): Promise<void> {
85-
let response;
85+
async getJobTemplate(pipelineType: string): Promise<RdiTemplateResponseDto> {
8686
try {
87-
response = await this.client.post(RdiUrl.Deploy, { ...pipeline });
87+
const response = await this.client.get(`${RdiUrl.GetJobTemplate}/${pipelineType}`);
88+
return response.data;
8889
} catch (error) {
89-
throw wrapRdiPipelineError(error, error.response.data.message);
90+
throw wrapRdiPipelineError(error);
9091
}
92+
}
9193

92-
if (response.data?.status === RDI_DEPLOY_FAILED_STATUS) {
93-
throw new RdiPipelineDeployFailedException(undefined, { error: response.data?.error });
94+
async deploy(pipeline: RdiPipeline): Promise<void> {
95+
try {
96+
const response = await this.client.post(RdiUrl.Deploy, { ...pipeline });
97+
const actionId = response.data.action_id;
98+
99+
return this.pollActionStatus(actionId);
100+
} catch (error) {
101+
throw wrapRdiPipelineError(error, error.response.data.message);
94102
}
95103
}
96104

@@ -103,21 +111,14 @@ export class ApiRdiClient extends RdiClient {
103111
}
104112
}
105113

106-
async testConnections(config: string, req: Request): Promise<RdiTestConnectionsResponseDto> {
114+
async testConnections(config: object): Promise<RdiTestConnectionsResponseDto> {
107115
try {
108-
const abortController = new AbortController();
109-
req.socket.on('close', () => {
110-
abortController.abort();
111-
});
112116
const response = await this.client.post(
113117
RdiUrl.TestConnections,
114118
config,
115-
{ signal: abortController.signal },
116119
);
117120

118-
const actionId = response.data.action_id;
119-
120-
return this.pollActionStatus(actionId, abortController.signal);
121+
return response.data;
121122
} catch (e) {
122123
throw wrapRdiPipelineError(e);
123124
}
@@ -147,7 +148,7 @@ export class ApiRdiClient extends RdiClient {
147148

148149
async getJobFunctions(): Promise<object> {
149150
try {
150-
const response = await this.client.post(RdiUrl.JobFunctions);
151+
const response = await this.client.get(RdiUrl.JobFunctions);
151152
return response.data;
152153
} catch (e) {
153154
throw wrapRdiPipelineError(e);
@@ -178,10 +179,10 @@ export class ApiRdiClient extends RdiClient {
178179
}
179180
}
180181

181-
private async pollActionStatus(actionId: string, abortSignal: AbortSignal): Promise<any> {
182+
private async pollActionStatus(actionId: string, abortSignal?: AbortSignal): Promise<any> {
182183
const startTime = Date.now();
183184
while (true) {
184-
if (abortSignal.aborted) {
185+
if (abortSignal?.aborted) {
185186
throw new RdiPipelineInternalServerErrorException();
186187
}
187188
if (Date.now() - startTime > MAX_POLLING_TIME) {

redisinsight/api/src/modules/rdi/client/rdi.client.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import { Request } from 'express';
21
import {
32
Rdi,
43
RdiClientMetadata, RdiPipeline, RdiStatisticsResult,
54
} from 'src/modules/rdi/models';
65
import {
7-
RdiDryRunJobDto, RdiDryRunJobResponseDto, RdiTestConnectionsResponseDto,
6+
RdiDryRunJobDto, RdiDryRunJobResponseDto, RdiTemplateResponseDto, RdiTestConnectionsResponseDto,
87
} from 'src/modules/rdi/dto';
98
import { IDLE_TRESHOLD } from 'src/modules/rdi/constants';
109

@@ -28,16 +27,17 @@ export abstract class RdiClient {
2827

2928
abstract getPipeline(): Promise<RdiPipeline>;
3029

31-
// TODO validate options and response
32-
abstract getTemplate(options: object): Promise<unknown>;
30+
abstract getConfigTemplate(pipelineType: string, dbType: string): Promise<RdiTemplateResponseDto>;
31+
32+
abstract getJobTemplate(pipelineType: string): Promise<RdiTemplateResponseDto>;
3333

3434
abstract getStrategies(): Promise<object>;
3535

3636
abstract deploy(pipeline: RdiPipeline): Promise<void>;
3737

3838
abstract dryRunJob(data: RdiDryRunJobDto): Promise<RdiDryRunJobResponseDto>;
3939

40-
abstract testConnections(config: string, req: Request): Promise<RdiTestConnectionsResponseDto>;
40+
abstract testConnections(config: object): Promise<RdiTestConnectionsResponseDto>;
4141

4242
abstract getStatistics(sections?: string): Promise<RdiStatisticsResult>;
4343

redisinsight/api/src/modules/rdi/constants/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ export enum RdiUrl {
22
GetSchema = 'api/v1/schemas',
33
GetPipeline = 'api/v1/pipelines',
44
GetStrategies = 'api/v1/pipelines/strategies',
5-
GetTemplate = 'deployments/templates',
5+
GetConfigTemplate = 'api/v1/pipelines/config/templates',
6+
GetJobTemplate = 'api/v1/pipelines/jobs/templates',
67
DryRunJob = 'api/v1/pipelines/jobs/dry-run',
78
JobFunctions = '/api/v1/pipelines/jobs/functions',
89
Deploy = 'api/v1/pipelines',

redisinsight/api/src/modules/rdi/dto/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export * from './update.rdi.dto';
33
export * from './rdi.dry-run.job.dto';
44
export * from './rdi.dry-run.job.response.dto';
55
export * from './rdi-test-connections.response.dto';
6+
export * from './rdi-template.response.dto';
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { ApiProperty } from '@nestjs/swagger';
2+
import { Expose } from 'class-transformer';
3+
4+
export class RdiTemplateResponseDto {
5+
@ApiProperty({
6+
description: 'Template for rdi file',
7+
type: String,
8+
})
9+
@Expose()
10+
template: string;
11+
}

redisinsight/api/src/modules/rdi/exceptions/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export * from './rdi-pipiline.error.handler';
33
export * from './rdi-pipeline.internal-server-error.exception';
44
export * from './rdi-pipeline.not-found.exception';
55
export * from './rdi-pipeline.unauthorized.exception';
6+
export * from './rdi-pipeline.validation.exception';
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { HttpException, HttpExceptionOptions, HttpStatus } from '@nestjs/common';
2+
import { CustomErrorCodes } from 'src/constants';
3+
import ERROR_MESSAGES from 'src/constants/error-messages';
4+
5+
export class RdiPipelineValidationException extends HttpException {
6+
constructor(
7+
message = ERROR_MESSAGES.RDI_VALIDATION_ERROR,
8+
options?: HttpExceptionOptions & { details?: unknown },
9+
) {
10+
const response = {
11+
message,
12+
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
13+
error: 'RdiValidationError',
14+
errorCode: CustomErrorCodes.RdiValidationError,
15+
details: options,
16+
};
17+
18+
super(response, response.statusCode, options);
19+
}
20+
}

redisinsight/api/src/modules/rdi/exceptions/rdi-pipiline.error.handler.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { AxiosError } from 'axios';
22
import { HttpException } from '@nestjs/common';
33
import {
44
RdiPipelineInternalServerErrorException,
5-
RdiPipelineUnauthorizedException, RdiPipelineNotFoundException,
5+
RdiPipelineUnauthorizedException, RdiPipelineNotFoundException, RdiPipelineValidationException,
66
} from 'src/modules/rdi/exceptions';
77

88
export const wrapRdiPipelineError = (error: AxiosError<any>, message?: string): HttpException => {
@@ -11,11 +11,14 @@ export const wrapRdiPipelineError = (error: AxiosError<any>, message?: string):
1111
}
1212

1313
const { response } = error;
14+
1415
if (response) {
1516
const errorOptions = response?.data?.detail;
1617
switch (response?.status) {
1718
case 401:
1819
return new RdiPipelineUnauthorizedException(message, errorOptions);
20+
case 422:
21+
return new RdiPipelineValidationException(message, errorOptions);
1922
default:
2023
return new RdiPipelineNotFoundException(message, errorOptions);
2124
}

0 commit comments

Comments
 (0)