diff --git a/back/_doc/env-vars.md b/back/_doc/env-vars.md index 494c8c1e1..a6e91a0b9 100644 --- a/back/_doc/env-vars.md +++ b/back/_doc/env-vars.md @@ -80,12 +80,11 @@ ### csmr-rie -| Var Name | Inferred type | -| ------------------------ | ------------- | -| APP_NAME | string | -| LoggerLegacy_FILE | string | -| Logger_THRESHOLD | string | -| REQUEST_TIMEOUT | string | -| RieBroker_PROXY_DISABLED | boolean | -| RieBroker_QUEUE | string | -| RieBroker_URLS | json | +| Var Name | Inferred type | +| ----------------- | ------------- | +| APP_NAME | string | +| LoggerLegacy_FILE | string | +| Logger_THRESHOLD | string | +| REQUEST_TIMEOUT | string | +| RieBroker_QUEUE | string | +| RieBroker_URLS | json | diff --git a/back/apps/csmr-rie/src/app.module.ts b/back/apps/csmr-rie/src/app.module.ts index 7b7b4a8c2..062832c26 100644 --- a/back/apps/csmr-rie/src/app.module.ts +++ b/back/apps/csmr-rie/src/app.module.ts @@ -1,7 +1,7 @@ import { ConfigModule, ConfigService } from "@fc/config"; import { LoggerModule } from "@fc/logger"; import { DynamicModule, Module } from "@nestjs/common"; -import { CsmrHttpProxyModule } from "./csmr-http-proxy.module"; +import { CsmrHttpProxyController } from "./controllers"; @Module({}) export class AppModule { @@ -13,9 +13,8 @@ export class AppModule { ConfigModule.forRoot(configService), // 2. Load logger module next LoggerModule.forRoot(), - // 3. Load other modules - CsmrHttpProxyModule, ], + controllers: [CsmrHttpProxyController], }; } } diff --git a/back/apps/csmr-rie/src/config/rie-broker.ts b/back/apps/csmr-rie/src/config/rie-broker.ts index 2e6506fcc..85598e659 100644 --- a/back/apps/csmr-rie/src/config/rie-broker.ts +++ b/back/apps/csmr-rie/src/config/rie-broker.ts @@ -1,5 +1,5 @@ import { ConfigParser } from "@fc/config"; -import { HttpProxyBrokerConfig } from "@fc/csmr-http-proxy"; +import { RabbitmqConfig } from "@fc/rabbitmq"; const env = new ConfigParser(process.env, "RieBroker"); @@ -13,5 +13,4 @@ export default { // Global request timeout used for any outgoing app requests. requestTimeout: parseInt(process.env.REQUEST_TIMEOUT, 10), - proxyDisabled: env.boolean("PROXY_DISABLED"), -} as HttpProxyBrokerConfig; +} as RabbitmqConfig; diff --git a/back/apps/csmr-rie/src/controllers/csmr-http-proxy.controller.spec.ts b/back/apps/csmr-rie/src/controllers/csmr-http-proxy.controller.spec.ts index a567047fd..0f6188666 100644 --- a/back/apps/csmr-rie/src/controllers/csmr-http-proxy.controller.spec.ts +++ b/back/apps/csmr-rie/src/controllers/csmr-http-proxy.controller.spec.ts @@ -1,165 +1,307 @@ -import { - BridgeError, - BridgeProtocol, - BridgeResponse, - MessageType, -} from "@fc/hybridge-http-proxy"; +import { MessageType } from "@fc/hybridge-http-proxy"; import { LoggerService } from "@fc/logger"; +import { HttpProxyProtocol } from "@fc/microservices"; import { getLoggerMock } from "@mocks/logger"; import { Test, TestingModule } from "@nestjs/testing"; -import { BridgePayloadDto } from "../dto"; -import { CsmrHttpProxyService } from "../services"; import { CsmrHttpProxyController } from "./csmr-http-proxy.controller"; describe("CsmrHttpProxyController", () => { - let csmrHttpProxyController: CsmrHttpProxyController; + let controller: CsmrHttpProxyController; - const loggerMock = getLoggerMock(); + const loggerServiceMock = getLoggerMock(); - const csmrHttpProxyMock = { - forwardRequest: jest.fn(), + const payloadMock = { + url: "https://example.com/foo", + method: "get", + headers: { + host: "example.com", + connection: "keep-alive", + }, + data: "foo=bar", }; beforeEach(async () => { jest.resetAllMocks(); jest.restoreAllMocks(); - const app: TestingModule = await Test.createTestingModule({ + const module: TestingModule = await Test.createTestingModule({ controllers: [CsmrHttpProxyController], - providers: [LoggerService, CsmrHttpProxyService], + providers: [LoggerService], }) .overrideProvider(LoggerService) - .useValue(loggerMock) - .overrideProvider(CsmrHttpProxyService) - .useValue(csmrHttpProxyMock) + .useValue(loggerServiceMock) .compile(); - csmrHttpProxyController = app.get( - CsmrHttpProxyController, - ); + controller = module.get(CsmrHttpProxyController); }); - it("should get the controller defined", () => { - expect(csmrHttpProxyController).toBeDefined(); + it("should be defined", () => { + expect(controller).toBeDefined(); }); describe("ping()", () => { it('should return "pong"', () => { - expect(csmrHttpProxyController.ping()).toBe("pong"); + expect(controller.ping()).toBe("pong"); }); }); describe("proxyRequest()", () => { - const baseBridgePayloadMock: BridgePayloadDto = Object.freeze({ - url: "https://test.com/getToken", - method: "get", - headers: { - test: "world", - }, - data: null, - } as unknown as BridgePayloadDto); - - it("should get the data from external service without data params", async () => { - // Arrange - const payload: BridgePayloadDto = { - ...baseBridgePayloadMock, - }; - - const dataMock = { + it("should log received command", async () => { + // Given + const fetchResponse = { status: 200, - data: null, - statusText: "Success", - headers: { - "content-type": "text/html; charset=UTF-8", - }, + statusText: "OK", + text: jest.fn().mockResolvedValueOnce("response body"), + headers: new Headers({ "content-type": "text/html" }), }; + jest.spyOn(global, "fetch").mockResolvedValueOnce(fetchResponse as any); - const resultMock: BridgeProtocol = { - type: MessageType.DATA, - data: { + // When + await controller.proxyRequest(payloadMock as any); + + // Then + expect(loggerServiceMock.debug).toHaveBeenCalledTimes(1); + expect(loggerServiceMock.debug).toHaveBeenCalledWith({ + msg: `received new ${HttpProxyProtocol.Commands.HTTP_PROXY} command`, + payload: payloadMock, + }); + }); + + describe("connection header handling", () => { + it("should keep connection header when value is 'keep-alive'", async () => { + // Given + const payload = { + ...payloadMock, + headers: { ...payloadMock.headers, connection: "keep-alive" }, + }; + const fetchResponse = { status: 200, - data: null, - statusText: "Success", + statusText: "OK", + text: jest.fn().mockResolvedValueOnce(""), + headers: new Headers(), + }; + jest.spyOn(global, "fetch").mockResolvedValueOnce(fetchResponse as any); + + // When + await controller.proxyRequest(payload as any); + + // Then + const calledHeaders = (global.fetch as jest.Mock).mock.calls[0][1] + .headers as Headers; + expect(calledHeaders.get("connection")).toBe("keep-alive"); + }); + + it("should keep connection header when value is 'close'", async () => { + // Given + const payload = { + ...payloadMock, + headers: { ...payloadMock.headers, connection: "close" }, + }; + const fetchResponse = { + status: 200, + statusText: "OK", + text: jest.fn().mockResolvedValueOnce(""), + headers: new Headers(), + }; + jest.spyOn(global, "fetch").mockResolvedValueOnce(fetchResponse as any); + + // When + await controller.proxyRequest(payload as any); + + // Then + const calledHeaders = (global.fetch as jest.Mock).mock.calls[0][1] + .headers as Headers; + expect(calledHeaders.get("connection")).toBe("close"); + }); + + it("should delete connection header when value is not 'keep-alive' or 'close'", async () => { + // Given + const payload = { + ...payloadMock, headers: { - "content-type": "text/html; charset=UTF-8", + ...payloadMock.headers, + connection: "upgrade, keep-alive", }, - }, - }; - csmrHttpProxyMock.forwardRequest.mockResolvedValueOnce(dataMock); + }; + const fetchResponse = { + status: 200, + statusText: "OK", + text: jest.fn().mockResolvedValueOnce(""), + headers: new Headers(), + }; + jest.spyOn(global, "fetch").mockResolvedValueOnce(fetchResponse as any); + + // When + await controller.proxyRequest(payload as any); - // Action - const result = await csmrHttpProxyController.proxyRequest(payload); - // Assert - expect(result).toStrictEqual(resultMock); + // Then + const calledHeaders = (global.fetch as jest.Mock).mock.calls[0][1] + .headers as Headers; + expect(calledHeaders.get("connection")).toBeNull(); + }); }); - it("should get the data from external ressources with data params", async () => { - // Arrange - const payload: BridgePayloadDto = { - ...baseBridgePayloadMock, - method: "post", - data: '{"sarah":"connor"}', - }; + describe("successful fetch", () => { + it("should call fetch with correct parameters", async () => { + // Given + const fetchResponse = { + status: 200, + statusText: "OK", + text: jest.fn().mockResolvedValueOnce("response body"), + headers: new Headers({ "content-type": "text/html" }), + }; + jest.spyOn(global, "fetch").mockResolvedValueOnce(fetchResponse as any); - const dataMock = { - status: 200, - statusText: "Success", - headers: { - "content-type": "text/html; charset=UTF-8", - }, - data: "{\nhello: 'world'\n}", - }; + // When + await controller.proxyRequest(payloadMock as any); + + // Then + expect(global.fetch).toHaveBeenCalledTimes(1); + expect(global.fetch).toHaveBeenCalledWith( + payloadMock.url, + expect.objectContaining({ + method: payloadMock.method, + body: payloadMock.data, + }), + ); + }); - const resultMock: BridgeProtocol = { - type: MessageType.DATA, - data: { + it("should pass null body when data is undefined", async () => { + // Given + const payload = { ...payloadMock, data: undefined }; + const fetchResponse = { status: 200, - statusText: "Success", - headers: { - "content-type": "text/html; charset=UTF-8", + statusText: "OK", + text: jest.fn().mockResolvedValueOnce(""), + headers: new Headers(), + }; + jest.spyOn(global, "fetch").mockResolvedValueOnce(fetchResponse as any); + + // When + await controller.proxyRequest(payload as any); + + // Then + expect(global.fetch).toHaveBeenCalledWith( + payload.url, + expect.objectContaining({ + body: null, + }), + ); + }); + + it("should return DATA response with status, data, statusText and headers", async () => { + // Given + const fetchResponse = { + status: 200, + statusText: "OK", + text: jest.fn().mockResolvedValueOnce("response body"), + headers: new Headers({ + "content-type": "text/html", + "x-custom": "value", + }), + }; + jest.spyOn(global, "fetch").mockResolvedValueOnce(fetchResponse as any); + + // When + const result = await controller.proxyRequest(payloadMock as any); + + // Then + expect(result).toEqual({ + type: MessageType.DATA, + data: { + status: 200, + data: "response body", + statusText: "OK", + headers: { + "content-type": "text/html", + "x-custom": "value", + }, }, - data: "{\nhello: 'world'\n}", - }, - }; - csmrHttpProxyMock.forwardRequest.mockResolvedValueOnce(dataMock); - // Action - const result = await csmrHttpProxyController.proxyRequest(payload); - // Assert - expect(result).toStrictEqual(resultMock); + }); + }); }); - it("should fail to get the data from external ressource with unknown error", async () => { - const errorMock = new Error("Unknown Error"); + describe("failed fetch", () => { + it("should return ERROR response when fetch throws", async () => { + // Given + const error = new Error("fetch failed"); + jest.spyOn(global, "fetch").mockRejectedValueOnce(error); - csmrHttpProxyMock.forwardRequest.mockImplementationOnce(() => { - throw errorMock; + // When + const result = await controller.proxyRequest(payloadMock as any); + + // Then + expect(result).toEqual({ + type: MessageType.ERROR, + data: { + reason: "fetch failed", + name: "Error", + code: undefined, + }, + }); }); - // Arrange - const payload = { - url: "https://test.com/getToken", - method: "post", - headers: { - test: "world", - }, - data: '{"sarah":"connor"}', - } as unknown as BridgePayloadDto; - - const resultMock: BridgeProtocol = { - type: MessageType.ERROR, - data: { - name: "Error", - reason: "Unknown Error", - code: undefined, - }, - }; - // Action - const result = await csmrHttpProxyController.proxyRequest(payload); + it("should include cause message in reason when error has a cause", async () => { + // Given + const error = new Error("fetch failed", { + cause: new Error("connection refused"), + }); + jest.spyOn(global, "fetch").mockRejectedValueOnce(error); - // Assert - expect(result).toStrictEqual(resultMock); + // When + const result = await controller.proxyRequest(payloadMock as any); - expect(loggerMock.error).toHaveBeenCalledTimes(1); + // Then + expect(result).toEqual({ + type: MessageType.ERROR, + data: { + reason: "fetch failed (connection refused)", + name: "Error", + code: undefined, + }, + }); + }); + + it("should use cause name and code when available", async () => { + // Given + const cause = new Error("connection refused"); + (cause as any).name = "ConnectionError"; + (cause as any).code = "ECONNREFUSED"; + const error = new Error("fetch failed", { cause }); + jest.spyOn(global, "fetch").mockRejectedValueOnce(error); + + // When + const result = await controller.proxyRequest(payloadMock as any); + + // Then + expect(result).toEqual({ + type: MessageType.ERROR, + data: { + reason: "fetch failed (connection refused)", + name: "ConnectionError", + code: "ECONNREFUSED", + }, + }); + }); + + it("should log error data", async () => { + // Given + const error = new Error("fetch failed"); + jest.spyOn(global, "fetch").mockRejectedValueOnce(error); + + // When + await controller.proxyRequest(payloadMock as any); + + // Then + expect(loggerServiceMock.error).toHaveBeenCalledTimes(1); + expect(loggerServiceMock.error).toHaveBeenCalledWith({ + errorData: { + reason: "fetch failed", + name: "Error", + code: undefined, + }, + }); + }); }); }); }); diff --git a/back/apps/csmr-rie/src/controllers/csmr-http-proxy.controller.ts b/back/apps/csmr-rie/src/controllers/csmr-http-proxy.controller.ts index cc77cb134..307c2fb2f 100644 --- a/back/apps/csmr-rie/src/controllers/csmr-http-proxy.controller.ts +++ b/back/apps/csmr-rie/src/controllers/csmr-http-proxy.controller.ts @@ -9,14 +9,10 @@ import { HttpProxyProtocol } from "@fc/microservices"; import { Controller, UsePipes, ValidationPipe } from "@nestjs/common"; import { MessagePattern, Payload } from "@nestjs/microservices"; import { BridgePayloadDto } from "../dto"; -import { CsmrHttpProxyService } from "../services"; @Controller() export class CsmrHttpProxyController { - constructor( - private readonly logger: LoggerService, - private readonly proxy: CsmrHttpProxyService, - ) {} + constructor(private readonly logger: LoggerService) {} @MessagePattern("ping") ping(): string { @@ -35,45 +31,55 @@ export class CsmrHttpProxyController { async proxyRequest( @Payload() payload: BridgePayloadDto, ): Promise> { - this.logger.debug( - `received new ${HttpProxyProtocol.Commands.HTTP_PROXY} command`, - ); + this.logger.debug({ + msg: `received new ${HttpProxyProtocol.Commands.HTTP_PROXY} command`, + payload, + }); let response; try { - const data = await this.proxy.forwardRequest(payload); + // Workaround for undici issue with Connection header values: + // > When using undici to make HTTP requests, attempting to send a Connection + // header with a value that lists other header names (per RFC 7230 Section 6.1) + // results in an InvalidArgumentError: invalid connection header. + // Reference: https://github.com/nodejs/undici/issues/4774 + if (!["keep-alive", "close"].includes(payload.headers.connection)) { + delete payload.headers.connection; + } + + const fetchOptions: RequestInit = { + method: payload.method, + headers: new Headers(payload.headers), + body: payload.data || null, + }; + + const res = await fetch(payload.url, fetchOptions); + + const responseData = await res.text(); response = { type: MessageType.DATA, - data, + data: { + status: res.status, + data: responseData, + statusText: res.statusText, + headers: Object.fromEntries(res.headers.entries()), + }, }; } catch (error) { - this.logger.error(JSON.stringify(error.stack)); - response = this.formatError(error); - } + const errorData = { + reason: `${error.message}${error?.cause?.message ? ` (${error.cause.message})` : ""}`, + name: error?.cause?.name || error.name, + code: error?.cause?.code || error.code, + }; - return response; - } + this.logger.error({ errorData }); - formatError(error: Error): BridgeProtocol { - this.logger.debug("build error message from internal Error"); - const { message: reason, name } = error; - /** - * @todo #825 - * Gestion des erreurs améliorée attendues avec code et documentation - * - * Author: Arnaud PSA - * Date: 02/12/21 - */ - const { code } = error as any; - const response = { - type: MessageType.ERROR, - data: { - reason, - name, - code, - }, - }; + response = { + type: MessageType.ERROR, + data: errorData, + }; + } return response; } diff --git a/back/apps/csmr-rie/src/csmr-http-proxy.module.spec.ts b/back/apps/csmr-rie/src/csmr-http-proxy.module.spec.ts deleted file mode 100644 index 6fac4d92c..000000000 --- a/back/apps/csmr-rie/src/csmr-http-proxy.module.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { ConfigModule } from "@fc/config"; -import { LoggerModule } from "@fc/logger"; -import { getConfigMock } from "@mocks/config"; -import { Test } from "@nestjs/testing"; -import { CsmrHttpProxyModule } from "./csmr-http-proxy.module"; - -describe("CsmrHttpProxyModule Dependency Validation", () => { - const baseConfig = { - Logger: { threshold: "info" }, - LoggerLegacy: {}, - HttpProxyBroker: { requestTimeout: 5000, proxyDisabled: false }, - }; - - it("should compile successfully with minimal config", async () => { - const configServiceMock = getConfigMock(); - configServiceMock.get.mockImplementation( - (key: string) => baseConfig[key] ?? null, - ); - - const moduleFixture = await Test.createTestingModule({ - imports: [ - CsmrHttpProxyModule, - ConfigModule.forRoot(configServiceMock as any), - LoggerModule.forRoot([]), - ], - }).compile(); - - expect(moduleFixture).toBeDefined(); - expect(moduleFixture.get(CsmrHttpProxyModule)).toBeDefined(); - }); - - it("should compile with proxy: false when proxyDisabled is true", async () => { - const configServiceMock = getConfigMock(); - configServiceMock.get.mockImplementation( - (key: string) => - ({ - ...baseConfig, - HttpProxyBroker: { - ...baseConfig.HttpProxyBroker, - proxyDisabled: true, - }, - })[key] ?? null, - ); - - const moduleFixture = await Test.createTestingModule({ - imports: [ - CsmrHttpProxyModule, - ConfigModule.forRoot(configServiceMock as any), - LoggerModule.forRoot([]), - ], - }).compile(); - - expect(moduleFixture).toBeDefined(); - expect(moduleFixture.get(CsmrHttpProxyModule)).toBeDefined(); - }); -}); diff --git a/back/apps/csmr-rie/src/csmr-http-proxy.module.ts b/back/apps/csmr-rie/src/csmr-http-proxy.module.ts deleted file mode 100644 index 1e55687a5..000000000 --- a/back/apps/csmr-rie/src/csmr-http-proxy.module.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { AsyncLocalStorageModule } from "@fc/async-local-storage"; -import { ConfigModule, ConfigService } from "@fc/config"; -import { RabbitmqModule } from "@fc/rabbitmq"; -import { HttpModule } from "@nestjs/axios"; -import { Module } from "@nestjs/common"; -import { CsmrHttpProxyController, HealthController } from "./controllers"; -import { HttpProxyBrokerConfig } from "./dto"; -import { rawTransform, validateStatus } from "./http"; -import { CsmrHttpProxyService } from "./services"; - -@Module({ - imports: [ - HttpModule.registerAsync({ - imports: [ConfigModule, AsyncLocalStorageModule], - useFactory: (configService: ConfigService) => { - const { requestTimeout: timeout, proxyDisabled } = - configService.get("HttpProxyBroker"); - return { - timeout, - validateStatus, - transformResponse: rawTransform, - ...(proxyDisabled && { proxy: false }), - }; - }, - inject: [ConfigService], - }), - RabbitmqModule.registerFor("HttpProxy"), - ], - controllers: [CsmrHttpProxyController, HealthController], - providers: [CsmrHttpProxyService], -}) -export class CsmrHttpProxyModule {} diff --git a/back/apps/csmr-rie/src/dto/bridge-payload.dto.ts b/back/apps/csmr-rie/src/dto/bridge-payload.dto.ts index cebd62b09..ab58d8243 100644 --- a/back/apps/csmr-rie/src/dto/bridge-payload.dto.ts +++ b/back/apps/csmr-rie/src/dto/bridge-payload.dto.ts @@ -1,5 +1,4 @@ import { BridgePayload, ValidateHttpHeaders } from "@fc/hybridge-http-proxy"; -import { type AxiosResponseHeaders } from "axios"; import { Transform } from "class-transformer"; import { IsIn, IsObject, IsOptional, IsString, IsUrl } from "class-validator"; @@ -16,7 +15,7 @@ export class BridgePayloadDto implements BridgePayload { @IsObject() @ValidateHttpHeaders() - readonly headers: AxiosResponseHeaders; + readonly headers: Record; /** * this parameter is voluntary abstract. diff --git a/back/apps/csmr-rie/src/dto/csmr-http-proxy.config.ts b/back/apps/csmr-rie/src/dto/csmr-http-proxy.config.ts index f92e78d3c..0c7e27fa4 100644 --- a/back/apps/csmr-rie/src/dto/csmr-http-proxy.config.ts +++ b/back/apps/csmr-rie/src/dto/csmr-http-proxy.config.ts @@ -2,18 +2,7 @@ import { AppRmqConfig } from "@fc/app"; import { LoggerConfig, LoggerLegacyConfig } from "@fc/logger"; import { RabbitmqConfig } from "@fc/rabbitmq"; import { Type } from "class-transformer"; -import { - IsBoolean, - IsObject, - IsOptional, - ValidateNested, -} from "class-validator"; - -export class HttpProxyBrokerConfig extends RabbitmqConfig { - @IsBoolean() - @IsOptional() - readonly proxyDisabled?: boolean; -} +import { IsObject, ValidateNested } from "class-validator"; export class CsmrHttpProxyConfig { @IsObject() @@ -33,6 +22,6 @@ export class CsmrHttpProxyConfig { @IsObject() @ValidateNested() - @Type(() => HttpProxyBrokerConfig) - readonly HttpProxyBroker: HttpProxyBrokerConfig; + @Type(() => RabbitmqConfig) + readonly HttpProxyBroker: RabbitmqConfig; } diff --git a/back/apps/csmr-rie/src/http/http-module.config.spec.ts b/back/apps/csmr-rie/src/http/http-module.config.spec.ts deleted file mode 100644 index 6ba7c3162..000000000 --- a/back/apps/csmr-rie/src/http/http-module.config.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { validateStatus } from "./http-module.config"; - -describe("validateStatus()", () => { - it("should failed to validate status if status is not a number", () => { - // Given - // When - const result = validateStatus("hello world" as unknown as number); - // Then - expect(result).toBe(false); - }); - it("should validate status if status is a number greater than 99", () => { - // Given - // When - const result = validateStatus(100); - // Then - expect(result).toBe(true); - }); - - it("should failed to validate status if status is a number less than 100", () => { - // Given - // When - const result = validateStatus(99); - // Then - expect(result).toBe(false); - }); - - it("should validate status if status is a number less than 600", () => { - // Given - // When - const result = validateStatus(599); - // Then - expect(result).toBe(true); - }); - - it("should failed to validate status if status is a number greater than 599", () => { - // Given - // When - const result = validateStatus(600); - // Then - expect(result).toBe(false); - }); -}); diff --git a/back/apps/csmr-rie/src/http/http-module.config.ts b/back/apps/csmr-rie/src/http/http-module.config.ts deleted file mode 100644 index 0eec8d5bc..000000000 --- a/back/apps/csmr-rie/src/http/http-module.config.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * allow to accept all requests ,bad or successful from external ressource - * @param status - * @returns - */ -export function validateStatus(status: number) { - return 100 <= status && status < 600; -} - -/** - * avoid decompress (JSON.parse...) process on Axios response - * @param raw - * @returns - */ -export const rawTransform = (raw) => raw; diff --git a/back/apps/csmr-rie/src/http/index.ts b/back/apps/csmr-rie/src/http/index.ts deleted file mode 100644 index 3f254a559..000000000 --- a/back/apps/csmr-rie/src/http/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./http-module.config"; diff --git a/back/apps/csmr-rie/src/index.ts b/back/apps/csmr-rie/src/index.ts index 7309c008c..0392b1b43 100644 --- a/back/apps/csmr-rie/src/index.ts +++ b/back/apps/csmr-rie/src/index.ts @@ -1,2 +1 @@ -export * from "./csmr-http-proxy.module"; export * from "./dto"; diff --git a/back/apps/csmr-rie/src/services/csmr-http-proxy.service.spec.ts b/back/apps/csmr-rie/src/services/csmr-http-proxy.service.spec.ts deleted file mode 100644 index 350de11af..000000000 --- a/back/apps/csmr-rie/src/services/csmr-http-proxy.service.spec.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { BridgePayload, BridgeResponse } from "@fc/hybridge-http-proxy"; -import { LoggerService } from "@fc/logger"; -import { getLoggerMock } from "@mocks/logger"; -import { HttpService } from "@nestjs/axios"; -import { Test, TestingModule } from "@nestjs/testing"; -import { lastValueFrom } from "rxjs"; -import { CsmrHttpProxyService } from "./csmr-http-proxy.service"; - -jest.mock("rxjs"); - -describe("CsmrHttpProxyService", () => { - let service: CsmrHttpProxyService; - - const httpService = { - get: jest.fn(), - post: jest.fn(), - }; - - const loggerMock = getLoggerMock(); - - const lastValueMock = jest.mocked(lastValueFrom); - - const httpResponseMock = Symbol("httpResponseMock"); - - beforeEach(async () => { - jest.resetAllMocks(); - jest.restoreAllMocks(); - - const module: TestingModule = await Test.createTestingModule({ - providers: [HttpService, LoggerService, CsmrHttpProxyService], - }) - .overrideProvider(HttpService) - .useValue(httpService) - .overrideProvider(LoggerService) - .useValue(loggerMock) - .compile(); - - service = module.get(CsmrHttpProxyService); - }); - - it("should be defined", () => { - // Given - // When - // Then - expect(service).toBeDefined(); - }); - - describe("forwardRequest()", () => { - const baseResponseMock = Object.freeze({ - status: 200, - statusText: "statusTextValue", - headers: { - "content-type": "text/html; charset=UTF-8", - }, - }); - const resultMock: BridgeResponse = { - headers: { - "content-type": "text/html; charset=UTF-8", - }, - statusText: "statusTextValue", - status: 200, - data: undefined, - }; - - const options: BridgePayload = Object.freeze({ - url: "http://test.com/token?code=33EFGRGRG44556GH6J", - method: "get", - headers: { - hello: "world", - }, - data: null, - }); - - beforeEach(() => { - httpService.get.mockResolvedValueOnce(httpResponseMock); - httpService.post.mockResolvedValueOnce(httpResponseMock); - }); - - it("should resolve without data for given options", async () => { - // Given - - const requestMock = [ - "http://test.com/token?code=33EFGRGRG44556GH6J", - { - headers: { hello: "world" }, - }, - ]; - - lastValueMock.mockResolvedValueOnce(baseResponseMock); - // When - const result = await service.forwardRequest(options); - // Then - expect(result).toStrictEqual(resultMock); - expect(httpService.get).toHaveBeenCalledTimes(1); - expect(httpService.get).toHaveBeenCalledWith(...requestMock); - }); - - it("should resolve with data for given options", async () => { - // Given - - const mockResponse = { - ...baseResponseMock, - data: { - hello: "world", - }, - }; - - const mockOptions = { - ...options, - method: "post", - data: "{\nexemple: 'example value'\n}", - }; - - const requestMock = [ - "http://test.com/token?code=33EFGRGRG44556GH6J", - "{\nexemple: 'example value'\n}", - { - headers: { hello: "world" }, - }, - ]; - - lastValueMock.mockResolvedValueOnce(mockResponse); - // When - const result = await service.forwardRequest(mockOptions); - // Then - expect(result).toStrictEqual(mockResponse); - expect(httpService.post).toHaveBeenCalledTimes(1); - expect(httpService.post).toHaveBeenCalledWith(...requestMock); - }); - - it("should call lastValueFrom when request is called", async () => { - // Given - httpService.get.mockReset().mockReturnValueOnce(httpResponseMock); - lastValueMock.mockResolvedValueOnce(baseResponseMock); - - // When - await service.forwardRequest(options); - // Then - expect(lastValueMock).toHaveBeenCalledTimes(1); - expect(lastValueMock).toHaveBeenCalledWith(httpResponseMock); - }); - - it("should throw an error if request failed", async () => { - // Given - const errorMock = new Error("Unknown Error"); - - lastValueMock.mockImplementationOnce(() => { - throw errorMock; - }); - - await expect( - // When - service.forwardRequest(options), - // Then - ).rejects.toThrow(errorMock); - }); - }); -}); diff --git a/back/apps/csmr-rie/src/services/csmr-http-proxy.service.ts b/back/apps/csmr-rie/src/services/csmr-http-proxy.service.ts deleted file mode 100644 index da999349f..000000000 --- a/back/apps/csmr-rie/src/services/csmr-http-proxy.service.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { BridgePayload, BridgeResponse } from "@fc/hybridge-http-proxy"; -import { LoggerService } from "@fc/logger"; -import { HttpService } from "@nestjs/axios"; -import { Injectable } from "@nestjs/common"; -import { AxiosRequestConfig, AxiosResponse } from "axios"; -import { lastValueFrom } from "rxjs"; - -@Injectable() -export class CsmrHttpProxyService { - constructor( - private readonly logger: LoggerService, - private http: HttpService, - ) {} - - /** - * get the data from FI based on Bridge request params - * @param {HttpProxyRequest} options - * @returns {Promise} - */ - async forwardRequest(commands: BridgePayload): Promise { - const { - url, - method, - headers: requestHeaders, - data: requestData, - } = commands; - - const config: AxiosRequestConfig = { - headers: requestHeaders, - }; - - const options: Array = [url, requestData, config].filter(Boolean); - - this.logger.debug({ - config, - method, - msg: `${method.toUpperCase()} ${url}`, - requestData, - url, - }); - const { status, statusText, headers, data } = await lastValueFrom< - AxiosResponse - >(this.http[method](...options)); - - this.logger.debug({ - data, - headers, - method, - msg: `${method.toUpperCase()} ${url} ${status}`, - status, - statusText, - url, - }); - - const response: BridgeResponse = { - status, - data, - statusText, - headers: headers as Record, - }; - - return response; - } -} diff --git a/back/apps/csmr-rie/src/services/index.ts b/back/apps/csmr-rie/src/services/index.ts deleted file mode 100644 index dba703fa3..000000000 --- a/back/apps/csmr-rie/src/services/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./csmr-http-proxy.service"; diff --git a/back/package.json b/back/package.json index 8e8ab5e0a..e5075a8b2 100644 --- a/back/package.json +++ b/back/package.json @@ -37,7 +37,6 @@ }, "dependencies": { "@gouvfr/dsfr": "^1.14.4", - "@nestjs/axios": "4.0.1", "@nestjs/common": "^10.4.20", "@nestjs/core": "^10.2.3", "@nestjs/cqrs": "^11.0.3", @@ -54,7 +53,6 @@ "amqp-connection-manager": "^5.0.0", "amqplib": "^1.0.3", "apache-ignite-client": "^1.0.0", - "axios": "1.15.2", "body-parser": "^2.2.2", "class-transformer": "^0.5.1", "class-validator": "proconnect-gouv/class-validator#build-0.15.1-proconnect.1", diff --git a/back/yarn.lock b/back/yarn.lock index d772ba435..b1d822ca4 100644 --- a/back/yarn.lock +++ b/back/yarn.lock @@ -1107,11 +1107,6 @@ dependencies: sparse-bitfield "^3.0.3" -"@nestjs/axios@4.0.1": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@nestjs/axios/-/axios-4.0.1.tgz#7ff73f47727b67dc04410ac6e9b3329401ebbb65" - integrity sha512-68pFJgu+/AZbWkGu65Z3r55bTsCPlgyKaV4BSG8yUAD72q1PPuyVRgUwFv6BxdnibTUHlyxm06FmYWNC+bjN7A== - "@nestjs/cli@^11.0.18": version "11.0.18" resolved "https://registry.yarnpkg.com/@nestjs/cli/-/cli-11.0.18.tgz#c00f15c1b4222f33c8c4d1811ae4555ad67e4915" @@ -2108,25 +2103,11 @@ async@^3.2.6: resolved "https://registry.npmjs.org/async/-/async-3.2.6.tgz" integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - atomic-sleep@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz" integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== -axios@1.15.2: - version "1.15.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.15.2.tgz#eb8fb6d30349abace6ade5b4cb4d9e8a0dc23e5b" - integrity sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A== - dependencies: - follow-redirects "^1.15.11" - form-data "^4.0.5" - proxy-from-env "^2.1.0" - b4a@^1.6.4: version "1.7.3" resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.7.3.tgz#24cf7ccda28f5465b66aec2bac69e32809bf112f" @@ -2619,13 +2600,6 @@ color-name@~1.1.4: resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - commander@4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz" @@ -2833,11 +2807,6 @@ defer-to-connect@^2.0.0, defer-to-connect@^2.0.1: resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz" integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - delegates@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" @@ -2986,16 +2955,6 @@ es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: dependencies: es-errors "^1.3.0" -es-set-tostringtag@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" - integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== - dependencies: - es-errors "^1.3.0" - get-intrinsic "^1.2.6" - has-tostringtag "^1.0.2" - hasown "^2.0.2" - esbuild@~0.27.0: version "0.27.3" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.27.3.tgz#5859ca8e70a3af956b26895ce4954d7e73bd27a8" @@ -3356,17 +3315,6 @@ form-data-encoder@^2.1.2: resolved "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz" integrity sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw== -form-data@^4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053" - integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - es-set-tostringtag "^2.1.0" - hasown "^2.0.2" - mime-types "^2.1.12" - forwarded@0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" @@ -3421,7 +3369,7 @@ get-caller-file@^2.0.5: resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.3.0: +get-intrinsic@^1.2.5, get-intrinsic@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== @@ -3586,7 +3534,7 @@ has-symbols@^1.1.0: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== -has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: +has-tostringtag@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz" integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== @@ -4666,7 +4614,7 @@ mime-db@^1.54.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== -mime-types@^2.1.12, mime-types@^2.1.18, mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.18, mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -5443,11 +5391,6 @@ proxy-addr@^2.0.7, proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" -proxy-from-env@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz#a7487568adad577cfaaa7e88c49cab3ab3081aba" - integrity sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA== - pump@^3.0.0: version "3.0.2" resolved "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz"