Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TECH ajout de logs sur tous les appels vers des API patennaires (quand on utilise shared-routes), en vu d'un monitoring sur datadog #2493

Merged
merged 4 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions back/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
"querystring": "^0.2.1",
"ramda": "^0.30.1",
"shared": "workspace:*",
"shared-routes": "^0.8.23",
"shared-routes": "^0.8.27",
"ts-node": "^10.9.1",
"ts-pattern": "4.2.2",
"uuid": "^8.3.2",
Expand All @@ -101,8 +101,8 @@
"@types/jsonwebtoken": "^9.0.2",
"@types/multer": "^1.4.7",
"@types/node": "^20.15.0",
"@types/papaparse": "^5.3.7",
"@types/node-fetch": "^2.6.11",
"@types/papaparse": "^5.3.7",
"@types/pg": "^8.10.2",
"@types/pg-format": "^1.0.2",
"@types/pino": "^7.0.4",
Expand Down
105 changes: 70 additions & 35 deletions back/src/config/bootstrap/createGateways.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ import {
AppConfig,
makeEmailAllowListPredicate,
} from "./appConfig";
import { logPartnerResponses } from "./logPartnerResponses";

const logger = createLogger(__filename);

Expand Down Expand Up @@ -105,19 +106,34 @@ export const createGetPgPoolFn = (config: AppConfig): GetPgPoolFn => {

const configureCreateAxiosHttpClientForExternalAPIs =
(config: AppConfig) =>
<R extends Record<string, UnknownSharedRoute>>(routes: R) =>
<R extends Record<string, UnknownSharedRoute>>({
routes,
partnerName,
}: { routes: R; partnerName: string }) =>
createAxiosSharedClient(
routes,
axios.create({
timeout: config.externalAxiosTimeout,
}),
{ skipResponseValidation: true },
{
skipResponseValidation: true,
onResponseSideEffect: logPartnerResponses(partnerName),
},
);

const configureCreateFetchHttpClientForExternalAPIs =
() =>
<R extends Record<string, UnknownSharedRoute>>(routes: R) =>
createFetchSharedClient(routes, fetch, { skipResponseValidation: true });
<R extends Record<string, UnknownSharedRoute>>({
routes,
partnerName,
}: {
routes: R;
partnerName: string;
}) =>
createFetchSharedClient(routes, fetch, {
skipResponseValidation: true,
onResponseSideEffect: logPartnerResponses(partnerName),
});

// prettier-ignore
export type Gateways = ReturnType<typeof createGateways> extends Promise<
Expand Down Expand Up @@ -156,9 +172,10 @@ export const createGateways = async (
const poleEmploiGateway =
config.poleEmploiGateway === "HTTPS"
? new HttpPoleEmploiGateway(
createLegacyAxiosHttpClientForExternalAPIs(
createPoleEmploiRoutes(config.peApiUrl),
),
createLegacyAxiosHttpClientForExternalAPIs({
partnerName: "France Travail - API",
routes: createPoleEmploiRoutes(config.peApiUrl),
}),
new InMemoryCachingGateway<AccessTokenResponse>(
timeGateway,
"expires_in",
Expand All @@ -173,12 +190,13 @@ export const createGateways = async (
const peConnectGateway: PeConnectGateway =
config.peConnectGateway === "HTTPS"
? new HttpPeConnectGateway(
createLegacyAxiosHttpClientForExternalAPIs(
makePeConnectExternalRoutes({
createLegacyAxiosHttpClientForExternalAPIs({
partnerName: "France Travail - FT Connect",
routes: makePeConnectExternalRoutes({
peApiUrl: config.peApiUrl,
peAuthCandidatUrl: config.peAuthCandidatUrl,
}),
),
}),
{
immersionFacileBaseUrl: config.immersionFacileBaseUrl,
poleEmploiClientId: config.poleEmploiClientId,
Expand All @@ -190,14 +208,18 @@ export const createGateways = async (
const oAuthGateway: OAuthGateway =
config.inclusionConnectGateway === "HTTPS"
? new HttpOAuthGateway(
createLegacyAxiosHttpClientForExternalAPIs(
makeInclusionConnectRoutes(
createLegacyAxiosHttpClientForExternalAPIs({
partnerName: "Inclusion Connect",
routes: makeInclusionConnectRoutes(
config.inclusionConnectConfig.providerBaseUri,
),
),
createLegacyAxiosHttpClientForExternalAPIs(
makeProConnectRoutes(config.proConnectConfig.providerBaseUri),
),
}),
createLegacyAxiosHttpClientForExternalAPIs({
partnerName: "ProConnect",
routes: makeProConnectRoutes(
config.proConnectConfig.providerBaseUri,
),
}),
config.inclusionConnectConfig,
config.proConnectConfig,
)
Expand All @@ -208,7 +230,10 @@ export const createGateways = async (
IN_MEMORY: () => new InMemoryEmailValidationGateway(),
EMAILABLE: () =>
new EmailableEmailValidationGateway(
createLegacyAxiosHttpClientForExternalAPIs(emailableValidationRoutes),
createLegacyAxiosHttpClientForExternalAPIs({
partnerName: "Emailable",
routes: emailableValidationRoutes,
}),
config.emailableApiKey,
),
})[config.emailValidationGateway]();
Expand All @@ -218,9 +243,10 @@ export const createGateways = async (
IN_MEMORY: () => new InMemoryAppellationsGateway(),
DIAGORIENTE: () =>
new DiagorienteAppellationsGateway(
createLegacyAxiosHttpClientForExternalAPIs(
diagorienteAppellationsRoutes,
),
createFetchHttpClientForExternalAPIs({
partnerName: "Diagoriente",
routes: diagorienteAppellationsRoutes,
}),
new InMemoryCachingGateway<DiagorienteAccessTokenResponse>(
timeGateway,
diagorienteTokenScope,
Expand All @@ -236,7 +262,10 @@ export const createGateways = async (
IN_MEMORY: () => new InMemoryAddressGateway(),
OPEN_CAGE_DATA: () =>
new HttpAddressGateway(
createLegacyAxiosHttpClientForExternalAPIs(addressesExternalRoutes),
createFetchHttpClientForExternalAPIs({
partnerName: "OpenCageData",
routes: addressesExternalRoutes,
}),
config.apiKeyOpenCageDataGeocoding,
config.apiKeyOpenCageDataGeosearch,
),
Expand All @@ -251,9 +280,10 @@ export const createGateways = async (

const brevoNotificationGateway = new BrevoNotificationGateway(
{
httpClient: createLegacyAxiosHttpClientForExternalAPIs(
brevoNotificationGatewayRoutes,
),
httpClient: createLegacyAxiosHttpClientForExternalAPIs({
partnerName: "Brevo Notifications",
routes: brevoNotificationGatewayRoutes,
}),
blackListedEmailDomains: config.emailDomainBlackList,
defaultSender: immersionFacileNoReplyEmailSender,
emailAllowListPredicate: makeEmailAllowListPredicate({
Expand Down Expand Up @@ -303,9 +333,10 @@ export const createGateways = async (
IN_MEMORY: () => new InMemorySiretGateway(),
ANNUAIRE_DES_ENTREPRISES: () =>
new AnnuaireDesEntreprisesSiretGateway(
createLegacyAxiosHttpClientForExternalAPIs(
annuaireDesEntreprisesSiretRoutes,
),
createFetchHttpClientForExternalAPIs({
partnerName: "Annuaire des entreprises",
routes: annuaireDesEntreprisesSiretRoutes,
}),
new InseeSiretGateway(
config.inseeHttpConfig,
timeGateway,
Expand All @@ -328,9 +359,10 @@ export const createGateways = async (
IN_MEMORY: () => new InMemoryPdfGeneratorGateway(),
SCALINGO: () =>
new ScalingoPdfGeneratorGateway(
createLegacyAxiosHttpClientForExternalAPIs(
makeScalingoPdfGeneratorRoutes(config.pdfGenerator.baseUrl),
),
createLegacyAxiosHttpClientForExternalAPIs({
partnerName: "Pdf Generator App",
routes: makeScalingoPdfGeneratorRoutes(config.pdfGenerator.baseUrl),
}),
config.pdfGenerator.apiKey,
uuidGenerator,
),
Expand All @@ -351,9 +383,10 @@ export const createGateways = async (
laBonneBoiteGateway:
config.laBonneBoiteGateway === "HTTPS"
? new HttpLaBonneBoiteGateway(
createFetchHttpClientForExternalAPIs(
createLbbRoutes(config.peApiUrl),
),
createFetchHttpClientForExternalAPIs({
partnerName: "La Bonne Boite",
routes: createLbbRoutes(config.peApiUrl),
}),
poleEmploiGateway,
config.poleEmploiClientId,
)
Expand All @@ -379,8 +412,10 @@ export const createGateways = async (
? new BrevoEstablishmentMarketingGateway({
apiKey: config.apiKeyBrevo,
establishmentContactListId: config.brevoEstablishmentContactListId,
httpClient:
createLegacyAxiosHttpClientForExternalAPIs(brevoContactRoutes),
httpClient: createLegacyAxiosHttpClientForExternalAPIs({
partnerName: "Brevo Establishment Marketing",
routes: brevoContactRoutes,
}),
})
: new InMemoryEstablishmentMarketingGateway(),
siret: getSiretGateway(config.siretGateway, config, timeGateway),
Expand Down
44 changes: 44 additions & 0 deletions back/src/config/bootstrap/logPartnerResponses.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { HttpClientOptions } from "shared-routes/validations";
import { createLogger } from "../../utils/logger";

const logger = createLogger(__filename);

type LogPartnerResponses = (
partnerName: string,
) => HttpClientOptions["onResponseSideEffect"];
export const logPartnerResponses: LogPartnerResponses =
(partnerName) =>
({ response, route, durationInMs, input }) => {
const common = {
partnerName: partnerName,
route: {
method: route.method,
url: route.url,
},
durationInMs,
};

if (response.status >= 200 && response.status < 400) {
logger.info({
partnerApiCall: {
...common,
response: {
kind: "success",
status: response.status,
},
},
});
} else {
logger.error({
partnerApiCall: {
...common,
response: {
kind: "failure",
status: response.status,
input,
body: response.body,
},
},
});
}
};
48 changes: 7 additions & 41 deletions back/src/domains/core/address/adapters/HttpAddressGateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import {
OpenCageGeoSearchKey,
Postcode,
StreetNumberAndAddress,
calculateDurationInSecondsFrom,
castError,
errors,
filterNotFalsy,
getDepartmentCodeFromDepartmentNameOrCity,
Expand All @@ -20,7 +18,6 @@ import {
lookupStreetAddressSpecialCharsRegex,
} from "shared";
import { HttpClient } from "shared-routes";
import { createLogger } from "../../../../utils/logger";
import { AddressGateway } from "../ports/AddressGateway";
import {
OpenCageDataAddressComponents,
Expand All @@ -29,8 +26,6 @@ import {
} from "./HttpAddressGateway.dto";
import { AddressesRoutes } from "./HttpAddressGateway.routes";

const logger = createLogger(__filename);

const openCageDateMaxRequestsPerSeconds = 15;

export class HttpAddressGateway implements AddressGateway {
Expand All @@ -49,7 +44,7 @@ export class HttpAddressGateway implements AddressGateway {
public async getAddressFromPosition(
position: GeoPositionDto,
): Promise<AddressDto | undefined> {
const { body } = await this.#limiter.schedule(() =>
const { status, body } = await this.#limiter.schedule(() =>
this.httpClient.geocoding({
queryParams: {
countrycode: franceAndAttachedTerritoryCountryCodes,
Expand All @@ -61,6 +56,8 @@ export class HttpAddressGateway implements AddressGateway {
}),
);

if (status !== 200) return;

const addresses: AddressDto[] = body.features
.map(this.#featureToAddress)
.filter(filterNotFalsy);
Expand All @@ -71,7 +68,6 @@ export class HttpAddressGateway implements AddressGateway {
public async lookupLocationName(
query: string,
): Promise<LookupSearchResult[]> {
const startDate = new Date();
const queryMinLength = 3;

return this.#limiter
Expand All @@ -96,31 +92,16 @@ export class HttpAddressGateway implements AddressGateway {
});
})
.then((response) => {
const lookupSearchResult = lookupSearchResultsSchema.parse(
if (response.status !== 200) return [];
return lookupSearchResultsSchema.parse(
toLookupSearchResults(response.body),
);
logger.info({
message: "HttpAddressGateway.lookupLocationName",
sharedRouteResponse: response,
durationInSeconds: calculateDurationInSecondsFrom(startDate),
});
return lookupSearchResult;
})
.catch((error: unknown) => {
logger.error({
message: "HttpAddressGateway.lookupLocationName",
error: castError(error),
durationInSeconds: calculateDurationInSecondsFrom(startDate),
});
throw error;
});
}

public async lookupStreetAddress(
query: string,
): Promise<AddressAndPosition[]> {
const startDate = new Date();

return this.#limiter
.schedule(() => {
if (
Expand All @@ -141,25 +122,10 @@ export class HttpAddressGateway implements AddressGateway {
});
})
.then((response) => {
const features = response.body.features
if (response.status !== 200) return [];
return response.body.features
.map((feature) => this.#toAddressAndPosition(feature))
.filter(filterNotFalsy);

logger.info({
message: "HttpAddressGateway.lookupStreetAddress",
sharedRouteResponse: response,
durationInSeconds: calculateDurationInSecondsFrom(startDate),
});

return features;
})
.catch((error: unknown) => {
logger.error({
message: "HttpAddressGateway.lookupStreetAddress",
error: castError(error),
durationInSeconds: calculateDurationInSecondsFrom(startDate),
});
throw error;
});
}

Expand Down
Loading
Loading