Skip to content

Commit

Permalink
rework gateways
Browse files Browse the repository at this point in the history
  • Loading branch information
bbohec committed Jun 23, 2023
1 parent 8027e8c commit dc61730
Show file tree
Hide file tree
Showing 25 changed files with 365 additions and 342 deletions.
4 changes: 2 additions & 2 deletions back/src/_testBuilders/buildTestApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import { InMemoryAddressGateway } from "../adapters/secondary/addressGateway/InM
import { BasicEventCrawler } from "../adapters/secondary/core/EventCrawlerImplementations";
import { CustomTimeGateway } from "../adapters/secondary/core/TimeGateway/CustomTimeGateway";
import { StubDashboardGateway } from "../adapters/secondary/dashboardGateway/StubDashboardGateway";
import { NotImplementedDocumentGateway } from "../adapters/secondary/documentGateway/NotImplementedDocumentGateway";
import { InMemoryEmailValidationGateway } from "../adapters/secondary/emailValidationGateway/InMemoryEmailValidationGateway";
import { InMemoryLaBonneBoiteAPI } from "../adapters/secondary/immersionOffer/laBonneBoite/InMemoryLaBonneBoiteAPI";
import { InMemoryPassEmploiGateway } from "../adapters/secondary/immersionOffer/passEmploi/InMemoryPassEmploiGateway";
import { InMemoryPoleEmploiGateway } from "../adapters/secondary/immersionOffer/poleEmploi/InMemoryPoleEmploiGateway";
import { InMemoryInclusionConnectGateway } from "../adapters/secondary/InclusionConnectGateway/InMemoryInclusionConnectGateway";
import type { InMemoryNotificationGateway } from "../adapters/secondary/notificationGateway/InMemoryNotificationGateway";
import { NotImplementedDocumentGateway } from "../adapters/secondary/NotImplementedDocumentGateway";
import { InMemoryPeConnectGateway } from "../adapters/secondary/PeConnectGateway/InMemoryPeConnectGateway";
import { InMemoryPoleEmploiGateway } from "../adapters/secondary/poleEmploi/InMemoryPoleEmploiGateway";
import { DeterministShortLinkIdGeneratorGateway } from "../adapters/secondary/shortLinkIdGeneratorGateway/DeterministShortLinkIdGeneratorGateway";
import { InMemorySiretGateway } from "../adapters/secondary/siret/InMemorySiretGateway";
import {
Expand Down
6 changes: 2 additions & 4 deletions back/src/adapters/primary/config/appConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import {
} from "shared";
import { DomainTopic } from "../../../domain/core/eventBus/events";
import { InclusionConnectConfig } from "../../../domain/inclusionConnect/useCases/InitiateInclusionConnect";
import { S3Params } from "../../secondary/documentGateway/S3DocumentGateway";
import { EmailableApiKey } from "../../secondary/emailValidationGateway/EmailableEmailValidationGateway.dto";
import { S3Params } from "../../secondary/S3DocumentGateway";

export type AccessTokenConfig = {
immersionFacileBaseUrl: AbsoluteUrl;
Expand Down Expand Up @@ -296,9 +296,7 @@ export class AppConfig {
peAuthCandidatUrl: this.peAuthCandidatUrl,
peEnterpriseUrl: this.peEnterpriseUrl,
clientId: this.poleEmploiClientId,
clientSecret: this.throwIfNotDefinedOrDefault(
"POLE_EMPLOI_CLIENT_SECRET",
),
clientSecret: this.poleEmploiClientSecret,
};
}

Expand Down
56 changes: 25 additions & 31 deletions back/src/adapters/primary/config/createGateways.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
immersionFacileContactEmail,
pipeWithValue,
} from "shared";
import { GetAccessTokenResponse } from "../../../domain/convention/ports/PoleEmploiGateway";
import { noRetries } from "../../../domain/core/ports/RetryStrategy";
import { TimeGateway } from "../../../domain/core/ports/TimeGateway";
import { DashboardGateway } from "../../../domain/dashboard/port/DashboardGateway";
Expand All @@ -15,35 +16,33 @@ import { createLogger } from "../../../utils/logger";
import { HttpAddressGateway } from "../../secondary/addressGateway/HttpAddressGateway";
import { addressesExternalTargets } from "../../secondary/addressGateway/HttpAddressGateway.targets";
import { InMemoryAddressGateway } from "../../secondary/addressGateway/InMemoryAddressGateway";
import { CachingAccessTokenGateway } from "../../secondary/core/CachingAccessTokenGateway";
import { InMemoryCachingGateway } from "../../secondary/core/InMemoryCachingGateway";
import { CustomTimeGateway } from "../../secondary/core/TimeGateway/CustomTimeGateway";
import { RealTimeGateway } from "../../secondary/core/TimeGateway/RealTimeGateway";
import { MetabaseDashboardGateway } from "../../secondary/dashboardGateway/MetabaseDashboardGateway";
import { StubDashboardGateway } from "../../secondary/dashboardGateway/StubDashboardGateway";
import { NotImplementedDocumentGateway } from "../../secondary/documentGateway/NotImplementedDocumentGateway";
import { S3DocumentGateway } from "../../secondary/documentGateway/S3DocumentGateway";
import { EmailableEmailValidationGateway } from "../../secondary/emailValidationGateway/EmailableEmailValidationGateway";
import { emailableValidationTargets } from "../../secondary/emailValidationGateway/EmailableEmailValidationGateway.targets";
import { InMemoryEmailValidationGateway } from "../../secondary/emailValidationGateway/InMemoryEmailValidationGateway";
import { InMemoryAccessTokenGateway } from "../../secondary/immersionOffer/InMemoryAccessTokenGateway";
import { HttpLaBonneBoiteAPI } from "../../secondary/immersionOffer/laBonneBoite/HttpLaBonneBoiteAPI";
import { InMemoryLaBonneBoiteAPI } from "../../secondary/immersionOffer/laBonneBoite/InMemoryLaBonneBoiteAPI";
import { createLbbTargets } from "../../secondary/immersionOffer/laBonneBoite/LaBonneBoiteTargets";
import { HttpPassEmploiGateway } from "../../secondary/immersionOffer/passEmploi/HttpPassEmploiGateway";
import { InMemoryPassEmploiGateway } from "../../secondary/immersionOffer/passEmploi/InMemoryPassEmploiGateway";
import { HttpPoleEmploiGateway } from "../../secondary/immersionOffer/poleEmploi/HttpPoleEmploiGateway";
import { InMemoryPoleEmploiGateway } from "../../secondary/immersionOffer/poleEmploi/InMemoryPoleEmploiGateway";
import { createPoleEmploiTargets } from "../../secondary/immersionOffer/poleEmploi/PoleEmploi.targets";
import { PoleEmploiAccessTokenGateway } from "../../secondary/immersionOffer/PoleEmploiAccessTokenGateway";
import { HttpInclusionConnectGateway } from "../../secondary/InclusionConnectGateway/HttpInclusionConnectGateway";
import { makeInclusionConnectExternalTargets } from "../../secondary/InclusionConnectGateway/inclusionConnectExternal.targets";
import { InMemoryInclusionConnectGateway } from "../../secondary/InclusionConnectGateway/InMemoryInclusionConnectGateway";
import { BrevoNotificationGateway } from "../../secondary/notificationGateway/BrevoNotificationGateway";
import { brevoNotificationGatewayTargets } from "../../secondary/notificationGateway/BrevoNotificationGateway.targets";
import { InMemoryNotificationGateway } from "../../secondary/notificationGateway/InMemoryNotificationGateway";
import { NotImplementedDocumentGateway } from "../../secondary/NotImplementedDocumentGateway";
import { HttpPeConnectGateway } from "../../secondary/PeConnectGateway/HttpPeConnectGateway";
import { InMemoryPeConnectGateway } from "../../secondary/PeConnectGateway/InMemoryPeConnectGateway";
import { makePeConnectExternalTargets } from "../../secondary/PeConnectGateway/peConnectApi.targets";
import { S3DocumentGateway } from "../../secondary/S3DocumentGateway";
import { HttpPoleEmploiGateway } from "../../secondary/poleEmploi/HttpPoleEmploiGateway";
import { InMemoryPoleEmploiGateway } from "../../secondary/poleEmploi/InMemoryPoleEmploiGateway";
import { createPoleEmploiTargets } from "../../secondary/poleEmploi/PoleEmploi.targets";
import { DeterministShortLinkIdGeneratorGateway } from "../../secondary/shortLinkIdGeneratorGateway/DeterministShortLinkIdGeneratorGateway";
import { NanoIdShortLinkIdGeneratorGateway } from "../../secondary/shortLinkIdGeneratorGateway/NanoIdShortLinkIdGeneratorGateway";
import { AnnuaireDesEntreprisesSiretGateway } from "../../secondary/siret/AnnuaireDesEntreprisesSiretGateway";
Expand Down Expand Up @@ -95,23 +94,27 @@ export const createGateways = async (config: AppConfig) => {
apiAddress: config.apiAddress,
});

const cachingAccessTokenGateway = [
config.laBonneBoiteGateway,
config.poleEmploiGateway,
].includes("HTTPS")
? new CachingAccessTokenGateway(
new PoleEmploiAccessTokenGateway(
config.poleEmploiAccessTokenConfig,
noRetries,
),
)
: new InMemoryAccessTokenGateway();

const timeGateway =
config.timeGateway === "CUSTOM"
? new CustomTimeGateway()
: new RealTimeGateway();

const poleEmploiGateway =
config.poleEmploiGateway === "HTTPS"
? new HttpPoleEmploiGateway(
configureCreateHttpClientForExternalApi(
axios.create({ timeout: config.externalAxiosTimeout }),
)(createPoleEmploiTargets(config.peApiUrl)),
new InMemoryCachingGateway<GetAccessTokenResponse>(
timeGateway,
"expires_in",
),
config.peApiUrl,
config.poleEmploiAccessTokenConfig,
noRetries,
)
: new InMemoryPoleEmploiGateway();

return {
addressApi: createAddressGateway(config),
dashboardGateway: createDashboardGateway(config),
Expand All @@ -128,7 +131,7 @@ export const createGateways = async (config: AppConfig) => {
timeout: config.externalAxiosTimeout,
}),
)(createLbbTargets(config.peApiUrl)),
cachingAccessTokenGateway,
poleEmploiGateway,
config.poleEmploiClientId,
)
: new InMemoryLaBonneBoiteAPI(),
Expand All @@ -137,16 +140,7 @@ export const createGateways = async (config: AppConfig) => {
? new HttpPassEmploiGateway(config.passEmploiUrl, config.passEmploiKey)
: new InMemoryPassEmploiGateway(),
peConnectGateway: createPoleEmploiConnectGateway(config),
poleEmploiGateway:
config.poleEmploiGateway === "HTTPS"
? new HttpPoleEmploiGateway(
configureCreateHttpClientForExternalApi(
axios.create({ timeout: config.externalAxiosTimeout }),
)(createPoleEmploiTargets(config.peApiUrl)),
config.peApiUrl,
cachingAccessTokenGateway,
)
: new InMemoryPoleEmploiGateway(),
poleEmploiGateway,
timeGateway,
siret: getSiretGateway(config.siretGateway, config, timeGateway),
shortLinkGenerator:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,38 @@
import axios from "axios";
import { Pool } from "pg";
import { GetAccessTokenResponse } from "../../../domain/convention/ports/PoleEmploiGateway";
import { UpdateAllPeAgencies } from "../../../domain/convention/useCases/agencies/UpdateAllPeAgencies";
import { noRetries } from "../../../domain/core/ports/RetryStrategy";
import { HttpAddressGateway } from "../../secondary/addressGateway/HttpAddressGateway";
import { addressesExternalTargets } from "../../secondary/addressGateway/HttpAddressGateway.targets";
import { ConsoleAppLogger } from "../../secondary/core/ConsoleAppLogger";
import { InMemoryCachingGateway } from "../../secondary/core/InMemoryCachingGateway";
import { RealTimeGateway } from "../../secondary/core/TimeGateway/RealTimeGateway";
import { UuidV4Generator } from "../../secondary/core/UuidGeneratorImplementations";
import { HttpPeAgenciesReferential } from "../../secondary/immersionOffer/peAgenciesReferential/HttpPeAgenciesReferential";
import { PoleEmploiAccessTokenGateway } from "../../secondary/immersionOffer/PoleEmploiAccessTokenGateway";
import { HttpPoleEmploiGateway } from "../../secondary/poleEmploi/HttpPoleEmploiGateway";
import { createPoleEmploiTargets } from "../../secondary/poleEmploi/PoleEmploi.targets";
import { AppConfig } from "../config/appConfig";
import { configureCreateHttpClientForExternalApi } from "../config/createHttpClientForExternalApi";
import { createUowPerformer } from "../config/uowConfig";

const updateAllPeAgenciesScript = async () => {
const config = AppConfig.createFromEnv();
const accessTokenGateway = new PoleEmploiAccessTokenGateway(
config.poleEmploiAccessTokenConfig,
noRetries,
);

const httpPeAgenciesReferential = new HttpPeAgenciesReferential(
config.peApiUrl,
accessTokenGateway,
new HttpPoleEmploiGateway(
configureCreateHttpClientForExternalApi(
axios.create({ timeout: config.externalAxiosTimeout }),
)(createPoleEmploiTargets(config.peApiUrl)),
new InMemoryCachingGateway<GetAccessTokenResponse>(
new RealTimeGateway(),
"expires_in",
),
config.peApiUrl,
config.poleEmploiAccessTokenConfig,
noRetries,
),
config.poleEmploiClientId,
);

Expand Down
62 changes: 0 additions & 62 deletions back/src/adapters/secondary/core/CachingAccessTokenGateway.ts

This file was deleted.

52 changes: 52 additions & 0 deletions back/src/adapters/secondary/core/InMemoryCachingGateway.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { addSeconds } from "date-fns";
import isAfter from "date-fns/isAfter";
import { TimeGateway } from "../../../domain/core/ports/TimeGateway";
import { RealTimeGateway } from "./TimeGateway/RealTimeGateway";

export class InMemoryCachingGateway<T> {
public constructor(
private readonly timeGateway: TimeGateway = new RealTimeGateway(),
private responseExpireInSecondsProp: keyof T,
) {}

public async caching(
value: string,
onCacheMiss: () => Promise<T>,
): Promise<T> {
const cache = this.cache[value];
return cache === undefined || this.isExpired(await cache)
? this.onBadCache(value, onCacheMiss)
: (await cache).response;
}

private onBadCache(value: string, onCacheMiss: () => Promise<T>): Promise<T> {
this.cache[value] = this.refreshCache(onCacheMiss);
return this.caching(value, onCacheMiss);
}

private async refreshCache(
onCacheMiss: () => Promise<T>,
): Promise<CacheEntry<T>> {
const response = await onCacheMiss();
return {
response,
expirationTime: addSeconds(
this.timeGateway.now(),
Number(response[this.responseExpireInSecondsProp]) -
this.minimumCacheLifetime || 0,
),
};
}

private isExpired(entry: CacheEntry<T>): boolean {
return isAfter(this.timeGateway.now(), entry.expirationTime);
}

private readonly cache: Partial<Record<string, Promise<CacheEntry<T>>>> = {};
private readonly minimumCacheLifetime = 30;
}

type CacheEntry<T> = {
response: T;
expirationTime: Date;
};
Loading

0 comments on commit dc61730

Please sign in to comment.