Skip to content

Commit 4fac737

Browse files
authored
Merge pull request #768 from companieshouse/feature/update-psc-verification-link-error-response
IDVA3-3008: Convert error attributes to camel case in psc-verification-link service
2 parents 9f12cca + 0c093d8 commit 4fac737

File tree

3 files changed

+117
-26
lines changed

3 files changed

+117
-26
lines changed

src/services/psc-verification-link/service.ts

Lines changed: 80 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,65 +4,99 @@ import { HttpResponse, IHttpClient } from "../../http";
44
import Resource, { ApiErrorResponse, ApiResponse } from "../resource";
55
import Mapping from "../../mapping/mapping";
66
import { PersonWithSignificantControlResource } from "../psc/types";
7+
78
/**
8-
* The PSC Verification Service expects request body data to be configured in camelCase format and will
9-
* unwrap this data into snake case format before submitting this on to the PSC verification API. Response
10-
* body data received from the API is then converted from snake case back into camel case before it is returned.
9+
* Service class for handling PSC (Person with Significant Control) verification-related operations.
10+
* This class provides methods to interact with the PSC verification API, including creating, retrieving,
11+
* updating, and checking the validation status of PSC verifications, as well as checking planned maintenance.
1112
*/
1213
export default class PscVerificationService {
1314
constructor (private readonly client: IHttpClient) {}
1415

16+
/**
17+
* Submits a new PSC verification for a given transaction.
18+
*
19+
* @param transactionId - The unique identifier of the transaction.
20+
* @param pscVerification - The PSC verification data to be submitted.
21+
* @returns A promise that resolves to either:
22+
* - A `Resource<PscVerification>` object containing the created PSC verification details.
23+
* - An `ApiErrorResponse` object if an error occurs during the request.
24+
*/
1525
public async postPscVerification (transactionId: string, pscVerification: PscVerificationData): Promise<Resource<PscVerification> | ApiErrorResponse> {
1626
const resourceUri = `/transactions/${transactionId}/persons-with-significant-control-verification`;
1727
const pscVerificationResource = Mapping.snakeCaseKeys(pscVerification);
1828
const response = await this.client.httpPost(resourceUri, pscVerificationResource);
1929

2030
if (response.error) {
21-
return {
22-
httpStatusCode: response.status,
23-
errors: [response.error]
24-
}
31+
return this.handleErrorResponse(response);
2532
}
2633

2734
return this.populateFrontEndResource(response);
2835
}
2936

37+
/**
38+
* Retrieves a specific PSC verification by its ID for a given transaction.
39+
*
40+
* @param transactionId - The unique identifier of the transaction.
41+
* @param pscVerificationId - The unique identifier of the PSC verification.
42+
* @returns A promise that resolves to either:
43+
* - A `Resource<PscVerification>` object containing the PSC verification details.
44+
* - An `ApiErrorResponse` object if an error occurs during the request.
45+
*/
3046
public async getPscVerification (transactionId: string, pscVerificationId: string): Promise<Resource<PscVerification> | ApiErrorResponse> {
3147
const resourceUri = `/transactions/${transactionId}/persons-with-significant-control-verification/${pscVerificationId}`;
3248
const response = await this.client.httpGet(resourceUri);
3349

3450
if (response.error) {
35-
return {
36-
httpStatusCode: response.status,
37-
errors: [response.error]
38-
}
51+
return this.handleErrorResponse(response);
3952
}
4053

4154
return this.populateFrontEndResource(response);
4255
}
4356

44-
public async patchPscVerification (transactionId: string, filingId: string, pscVerificationPatch: PscVerificationData): Promise<Resource<PscVerification> | ApiErrorResponse> {
57+
/**
58+
* Updates a PSC verification using a PATCH request for a given transaction and filing ID.
59+
*
60+
* @param transactionId - The unique identifier of the transaction.
61+
* @param pscVerificationId - The unique identifier of the filing.
62+
* @param pscVerificationPatch - The PSC verification data to be updated.
63+
* @returns A promise that resolves to either:
64+
* - A `Resource<PscVerification>` object containing the updated PSC verification details.
65+
* - An `ApiErrorResponse` object if an error occurs during the request.
66+
*/
67+
public async patchPscVerification (transactionId: string, pscVerificationId: string, pscVerificationPatch: PscVerificationData): Promise<Resource<PscVerification> | ApiErrorResponse> {
4568
const additionalHeaders = { "Content-Type": "application/merge-patch+json" };
46-
const resourceUri = `/transactions/${transactionId}/persons-with-significant-control-verification/${filingId}`;
69+
const resourceUri = `/transactions/${transactionId}/persons-with-significant-control-verification/${pscVerificationId}`;
4770
const pscVerificationPatchResource = Mapping.snakeCaseKeys(pscVerificationPatch);
4871
const response = await this.client.httpPatch(resourceUri, pscVerificationPatchResource, additionalHeaders);
4972

5073
if (response.error) {
51-
return {
52-
httpStatusCode: response.status,
53-
errors: [response.error]
54-
}
74+
return this.handleErrorResponse(response);
5575
}
5676

5777
return this.populateFrontEndResource(response);
5878
}
5979

80+
/**
81+
* Retrieves the validation status of a Person with Significant Control (PSC) verification.
82+
*
83+
* @param transactionId - The unique identifier of the transaction.
84+
* @param pscVerificationId - The unique identifier of the PSC verification.
85+
* @returns A promise that resolves to either:
86+
* - A `Resource<PscVerification>` object containing the validation status details.
87+
* - An `ApiErrorResponse` object if an error occurs during the request.
88+
*
89+
* The method constructs the resource URI using the provided `transactionId` and `pscVerificationId`,
90+
* performs an HTTP GET request, and processes the response. If an error is encountered, it is handled
91+
* using the `handleErrorResponse` method. Otherwise, the response body is mapped to camelCase keys
92+
* and returned as part of the resource.
93+
*/
6094
public async getValidationStatus (transactionId: string, pscVerificationId: string): Promise<Resource<PscVerification> | ApiErrorResponse> {
6195
const resourceUri = `/transactions/${transactionId}/persons-with-significant-control-verification/${pscVerificationId}/validation_status`;
6296
const response = await this.client.httpGet(resourceUri);
6397

64-
if (response.status >= 400) {
65-
return { httpStatusCode: response.status, errors: [response.error] };
98+
if (response.error) {
99+
return this.handleErrorResponse(response);
66100
}
67101

68102
const resource: Resource<ValidationStatusResponse> = { httpStatusCode: response.status };
@@ -74,15 +108,19 @@ export default class PscVerificationService {
74108
return resource;
75109
}
76110

111+
/**
112+
* Checks if there is any planned maintenance for the PSC verification service.
113+
*
114+
* @returns A promise that resolves to either:
115+
* - An `ApiResponse<PlannedMaintenance>` object containing maintenance details.
116+
* - An `ApiErrorResponse` object if an error occurs during the request.
117+
*/
77118
public async checkPlannedMaintenance (): Promise<ApiResponse<PlannedMaintenance> | ApiErrorResponse> {
78119
const maintenanceUri = `/persons-with-significant-control-verification/maintenance`;
79120
const response = await this.client.httpGet(maintenanceUri);
80121

81122
if (response.error) {
82-
return {
83-
httpStatusCode: response.status,
84-
errors: [response.error]
85-
}
123+
return this.handleErrorResponse(response);
86124
}
87125

88126
return {
@@ -91,6 +129,12 @@ export default class PscVerificationService {
91129
};
92130
}
93131

132+
/**
133+
* Maps the response body to a front-end resource format with camelCase keys.
134+
*
135+
* @param response - The HTTP response received from the API.
136+
* @returns A `Resource<PscVerification>` object containing the mapped resource.
137+
*/
94138
private populateFrontEndResource (response: HttpResponse): Resource<PscVerification> {
95139
const frontEndResource: Resource<PscVerification> = {
96140
httpStatusCode: response.status,
@@ -102,4 +146,17 @@ export default class PscVerificationService {
102146

103147
return frontEndResource;
104148
}
149+
150+
/**
151+
* Handles error responses from the API by mapping error details to camelCase keys.
152+
*
153+
* @param response - The HTTP response containing the error details.
154+
* @returns An `ApiErrorResponse` object with the mapped error details.
155+
*/
156+
private handleErrorResponse (response: HttpResponse): ApiErrorResponse {
157+
return {
158+
httpStatusCode: response.status,
159+
errors: [Mapping.camelCaseKeys(response.error)]
160+
};
161+
}
105162
}

test/services/psc-verification-link/service.mock.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ export const mockPscVerificationPatchInd: PscVerification = {
9595
export const mockPscVerificationCreatedResponse = {
9696
201: { status: StatusCodes.CREATED, body: mockPscVerificationCreatedResource },
9797
400: { status: StatusCodes.BAD_REQUEST, error: ReasonPhrases.BAD_REQUEST },
98-
401: { status: StatusCodes.UNAUTHORIZED, error: ReasonPhrases.UNAUTHORIZED }
98+
401: { status: StatusCodes.UNAUTHORIZED, error: ReasonPhrases.UNAUTHORIZED },
99+
500: { status: StatusCodes.INTERNAL_SERVER_ERROR, error: ReasonPhrases.INTERNAL_SERVER_ERROR }
99100
};
100101

101102
const PSC_VERIFICATION_IND_RESOURCE: PscVerificationDataResource = {
@@ -180,14 +181,16 @@ const mockValidationStatusResponseErrorsResource: ValidationStatusResponseResour
180181
export const mockPscVerificationIndResponse = {
181182
200: { status: StatusCodes.OK, body: mockPscVerificationIndResource },
182183
401: { status: StatusCodes.UNAUTHORIZED, error: ReasonPhrases.UNAUTHORIZED },
183-
404: { status: StatusCodes.NOT_FOUND, error: ReasonPhrases.NOT_FOUND }
184+
404: { status: StatusCodes.NOT_FOUND, error: ReasonPhrases.NOT_FOUND },
185+
500: { status: StatusCodes.INTERNAL_SERVER_ERROR, error: ReasonPhrases.INTERNAL_SERVER_ERROR }
184186
};
185187

186188
export const mockPscVerificationPatchIndResponse = {
187189
200: { status: StatusCodes.OK, body: mockPscVerificationPatchIndResource },
188190
400: { status: StatusCodes.BAD_REQUEST, error: ReasonPhrases.BAD_REQUEST },
189191
401: { status: StatusCodes.UNAUTHORIZED, error: ReasonPhrases.UNAUTHORIZED },
190-
404: { status: StatusCodes.NOT_FOUND, error: ReasonPhrases.NOT_FOUND }
192+
404: { status: StatusCodes.NOT_FOUND, error: ReasonPhrases.NOT_FOUND },
193+
500: { status: StatusCodes.INTERNAL_SERVER_ERROR, error: ReasonPhrases.INTERNAL_SERVER_ERROR }
191194
};
192195

193196
export const mockPlannedMaintenanceResponse = {

test/services/psc-verification-link/service.spec.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@ describe("PSC Verification Link", () => {
4242
expect(data.httpStatusCode).to.equal(StatusCodes.BAD_REQUEST);
4343
expect(data.errors?.[0]).to.equal(ReasonPhrases.BAD_REQUEST);
4444
});
45+
46+
it("should return status 500 Internal Server Error if a server error occurs", async () => {
47+
sinon.stub(requestClient, "httpPost").resolves(mockPscVerificationCreatedResponse[500]);
48+
49+
const data = await pscService.postPscVerification(TRANSACTION_ID, { companyNumber: COMPANY_NUMBER }) as ApiErrorResponse;
50+
51+
expect(data.httpStatusCode).to.equal(StatusCodes.INTERNAL_SERVER_ERROR);
52+
expect(data.errors?.[0]).to.equal(ReasonPhrases.INTERNAL_SERVER_ERROR);
53+
});
4554
});
4655

4756
describe("GET endpoint", () => {
@@ -74,6 +83,15 @@ describe("PSC Verification Link", () => {
7483
expect(response.httpStatusCode).to.equal(StatusCodes.NOT_FOUND);
7584
expect(response.errors?.[0]).to.equal(ReasonPhrases.NOT_FOUND);
7685
});
86+
87+
it("should return status 500 Internal Server Error if a server error occurs", async () => {
88+
sinon.stub(requestClient, "httpGet").resolves(mockPscVerificationIndResponse[500]);
89+
90+
const response = await pscService.getPscVerification(TRANSACTION_ID, PSC_NOTIFICATION_ID) as ApiErrorResponse;
91+
92+
expect(response.httpStatusCode).to.equal(StatusCodes.INTERNAL_SERVER_ERROR);
93+
expect(response.errors?.[0]).to.equal(ReasonPhrases.INTERNAL_SERVER_ERROR);
94+
});
7795
});
7896

7997
describe("PATCH endpoint", () => {
@@ -104,6 +122,19 @@ describe("PSC Verification Link", () => {
104122
expect(response.httpStatusCode).to.equal(StatusCodes.UNAUTHORIZED);
105123
expect(response.errors?.[0]).to.equal(ReasonPhrases.UNAUTHORIZED);
106124
});
125+
126+
it("should return a status 500 Internal Server Error when a server error occurs", async () => {
127+
sinon.stub(requestClient, "httpPatch").resolves(mockPscVerificationPatchIndResponse[500]);
128+
129+
const response = await pscService.patchPscVerification(
130+
TRANSACTION_ID,
131+
FILING_ID,
132+
PSC_VERIFICATION_IND
133+
) as ApiErrorResponse;
134+
135+
expect(response.httpStatusCode).to.equal(StatusCodes.INTERNAL_SERVER_ERROR);
136+
expect(response.errors?.[0]).to.equal(ReasonPhrases.INTERNAL_SERVER_ERROR);
137+
});
107138
});
108139

109140
describe("Validation status GET endpoint", () => {

0 commit comments

Comments
 (0)