diff --git a/mocks/routes/cms.js b/mocks/routes/cms.js index 5cd8f38bb..5eab6c3d6 100644 --- a/mocks/routes/cms.js +++ b/mocks/routes/cms.js @@ -1,5 +1,5 @@ -const settings = require('../settings'); const loadFixtureAndReplaceBaseUrl = require('../loadFixtureAndReplaceBaseUrl'); +const settings = require('../settings'); const ALLE_RESPONSE = loadFixtureAndReplaceBaseUrl( 'cms-maintenance-notifications-alle.json' @@ -65,10 +65,17 @@ module.exports = [ variants: [ { id: 'standard', - type: 'json', + type: 'middleware', options: { - status: 200, - body: PRODUCTEN_OP_MA, + middleware: (req, res, next, core) => { + const { articleslug } = req.params; + if (articleslug === 'overzicht-producten-ondernemers') { + const productenOndernemer = structuredClone(PRODUCTEN_OP_MA); + productenOndernemer.applicatie.inhoud.inleiding = `

Mock content voor BEDRIJVEN

`; + return res.send(productenOndernemer); + } + return res.send(PRODUCTEN_OP_MA); + }, }, }, ], diff --git a/src/mijnamsterdam.d.ts b/src/mijnamsterdam.d.ts index db2eb41cd..16f1d978f 100644 --- a/src/mijnamsterdam.d.ts +++ b/src/mijnamsterdam.d.ts @@ -22,10 +22,6 @@ type ReturnTypeAsync any> = T extends ( ? R : any; -type ProfileType = 'private' | 'private-attributes' | 'commercial'; - -type AuthMethod = 'digid' | 'eherkenning'; - type Optional = Pick, K> & Omit; type Prettify = { diff --git a/src/server/auth/auth-helpers.ts b/src/server/auth/auth-helpers.ts index 226a40d80..82b45b673 100644 --- a/src/server/auth/auth-helpers.ts +++ b/src/server/auth/auth-helpers.ts @@ -19,6 +19,7 @@ import { } from './auth-types'; import { FeatureToggle } from '../../universal/config/feature-toggles'; import { AppRoutes } from '../../universal/config/routes'; +import { PROFILE_TYPES } from '../../universal/types/App.types'; import { ExternalConsumerEndpoints } from '../routing/bff-routes'; import { generateFullApiUrlBFF } from '../routing/route-helpers'; import { captureException } from '../services/monitoring'; @@ -172,3 +173,7 @@ export function createLogoutHandler( return res.redirect(postLogoutRedirectUrl); }; } + +export function isValidProfileType(profileType: unknown) { + return PROFILE_TYPES.includes(profileType as ProfileType); +} diff --git a/src/server/config/source-api.ts b/src/server/config/source-api.ts index d4404aff7..e188d1d94 100644 --- a/src/server/config/source-api.ts +++ b/src/server/config/source-api.ts @@ -12,7 +12,6 @@ export interface DataRequestConfig extends AxiosRequestConfig { cacheTimeout?: number; cancelTimeout?: number; postponeFetch?: boolean; - urls?: Record; // Construct an url that will be assigned to the url key in the local requestConfig. // Example: formatUrl: (requestConfig) => requestConfig.url + '/some/additional/path/segments/, @@ -205,11 +204,7 @@ export const ApiConfig: ApiDataRequestConfig = { CMS_CONTENT_GENERAL_INFO: { // eslint-disable-next-line no-magic-numbers cacheTimeout: 4 * ONE_HOUR_MS, - urls: { - private: `${getFromEnv('BFF_CMS_BASE_URL')}/mijn-content/artikelen/ziet-amsterdam/?AppIdt=app-data`, - 'private-attributes': `${getFromEnv('BFF_CMS_BASE_URL')}/mijn-content/artikelen/ziet-amsterdam/?AppIdt=app-data`, - commercial: `${getFromEnv('BFF_CMS_BASE_URL')}/mijn-content/artikelen/overzicht-producten-ondernemers/?AppIdt=app-data`, - }, + url: `${getFromEnv('BFF_CMS_BASE_URL')}/mijn-content/artikelen`, }, CMS_CONTENT_FOOTER: { url: `${getFromEnv('BFF_CMS_BASE_URL')}/algemene_onderdelen/overige/footer/?AppIdt=app-data`, diff --git a/src/server/services/cms-content.ts b/src/server/services/cms-content.ts index 530ef4c4e..912819cfc 100644 --- a/src/server/services/cms-content.ts +++ b/src/server/services/cms-content.ts @@ -15,7 +15,7 @@ import { } from '../../universal/helpers/api'; import { hash } from '../../universal/helpers/utils'; import { LinkProps } from '../../universal/types/App.types'; -import { DataRequestConfig } from '../config/source-api'; +import { isValidProfileType } from '../auth/auth-helpers'; import FileCache from '../helpers/file-cache'; import { getApiConfig } from '../helpers/source-api-helpers'; import { requestData } from '../helpers/source-api-request'; @@ -211,14 +211,14 @@ async function getGeneralPage( sanitizeCmsContent(responseData.applicatie.inhoud.tekst), }; }, + formatUrl({ url }) { + return profileType === 'commercial' + ? `${url}/overzicht-producten-ondernemers/?AppIdt=app-data` + : `${url}/ziet-amsterdam/?AppIdt=app-data`; + }, }); - const requestConfigFinal: DataRequestConfig = { - ...requestConfig, - url: requestConfig.urls![profileType], - }; - - return requestData(requestConfigFinal, requestID).then( + return requestData(requestConfig, requestID).then( (apiData) => { if ( apiData.status === 'OK' && @@ -285,11 +285,14 @@ async function fetchCmsBase( requestID: RequestID, query?: QueryParamsCMSFooter ) { - const forceRenew = !!(query?.forceRenew === 'true'); - + const forceRenew = query?.forceRenew === 'true'; + const profileType = + query?.profileType && isValidProfileType(query?.profileType) + ? query.profileType + : undefined; const generalInfoPageRequest = getGeneralPage( requestID, - query?.profileType as ProfileType, + profileType, forceRenew ); @@ -310,10 +313,10 @@ async function fetchCmsBase( }; } -export interface QueryParamsCMSFooter extends Record { - forceRenew: 'true'; - profileType: ProfileType; -} +export type QueryParamsCMSFooter = { + forceRenew?: 'true'; + profileType?: ProfileType; +}; export async function fetchCmsFooter( requestID: RequestID, diff --git a/src/server/services/controller.test.ts b/src/server/services/controller.test.ts index 2da3e1362..6a8661f14 100644 --- a/src/server/services/controller.test.ts +++ b/src/server/services/controller.test.ts @@ -9,13 +9,19 @@ import { vi, } from 'vitest'; +import { fetchCMSCONTENT } from './cms-content'; import { addServiceResultHandler, + forTesting, getServiceResultsForTips, getTipNotifications, servicesTipsByProfileType, } from './controller'; -import { getReqMockWithOidc, ResponseMock } from '../../testing/utils'; +import { + getReqMockWithOidc, + RequestMock, + ResponseMock, +} from '../../testing/utils'; const mocks = vi.hoisted(() => { return { @@ -35,6 +41,12 @@ const mocks = vi.hoisted(() => { }; }); +vi.mock('./cms-content', () => { + return { + fetchCMSCONTENT: vi.fn(), + }; +}); + vi.mock('./tips-and-notifications', async () => { return { getTipsAndNotificationsFromApiResults: vi.fn(), @@ -192,3 +204,57 @@ describe('controller', () => { expect(result).toEqual(data); }); }); + +describe('request handlers', () => { + describe('CMS_CONTENT', async () => { + const reqID = 'xx-req-id-yy'; + + test('profileType: private', async () => { + const reqMock = await getReqMockWithOidc({ + sid: 'x123y', + authMethod: 'digid', + profileType: 'private', + id: '9988', + }); + + await forTesting.CMS_CONTENT(reqID, reqMock); + + expect(fetchCMSCONTENT).toHaveBeenCalledWith(reqID, { + profileType: 'private', + }); + }); + + test('profileType: commercial', async () => { + const reqMock = await getReqMockWithOidc({ + sid: 'x123y', + authMethod: 'eherkenning', + profileType: 'commercial', + id: '9988', + }); + + await forTesting.CMS_CONTENT(reqID, reqMock); + + expect(fetchCMSCONTENT).toHaveBeenCalledWith(reqID, { + profileType: 'commercial', + }); + }); + + test('arbitrary query params are passed', async () => { + const reqMock = await getReqMockWithOidc({ + sid: 'x123y', + authMethod: 'eherkenning', + profileType: 'commercial', + id: '9988', + }); + + (reqMock as unknown as RequestMock).setQuery({ forceRenew: 'true' }); + + await forTesting.CMS_CONTENT(reqID, reqMock); + + expect(fetchCMSCONTENT).toHaveBeenCalledWith(reqID, { + profileType: 'commercial', + forceRenew: 'true', + }); + }); + }); +}); diff --git a/src/server/services/controller.ts b/src/server/services/controller.ts index 8bc047d20..4931c5c4d 100644 --- a/src/server/services/controller.ts +++ b/src/server/services/controller.ts @@ -117,7 +117,13 @@ export function addServiceResultHandler( * The service methods */ // Public services -const CMS_CONTENT = callPublicService(fetchCMSCONTENT); +const CMS_CONTENT = (requestID: RequestID, req: Request) => { + const auth = getAuth(req); + return fetchCMSCONTENT(requestID, { + profileType: auth?.profile.profileType, + ...queryParams(req), + }); +}; const CMS_MAINTENANCE_NOTIFICATIONS = callPublicService( fetchMaintenanceNotificationsActual ); @@ -485,3 +491,7 @@ export async function getTipNotifications( return []; } + +export const forTesting = { + CMS_CONTENT, +}; diff --git a/src/universal/types/App.types.ts b/src/universal/types/App.types.ts index 31a6b7015..910c3f518 100644 --- a/src/universal/types/App.types.ts +++ b/src/universal/types/App.types.ts @@ -122,3 +122,16 @@ export interface Match { path: string; url: string; } + +export const PROFILE_TYPES = [ + 'private', + 'commercial', + 'private-attributes', +] as const; + +export const AUTH_METHODS = ['eherkenning', 'digid'] as const; + +declare global { + type ProfileType = (typeof PROFILE_TYPES)[number]; + type AuthMethod = (typeof AUTH_METHODS)[number]; +}