Skip to content
Merged
5 changes: 5 additions & 0 deletions .changeset/small-weeks-call.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@guardian/support-dotcom-components': patch
---

optional sectionId and tagsId properties added to HeaderTargeting model
1 change: 1 addition & 0 deletions cdk/lib/__snapshots__/dotcom-components.test.ts.snap

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cdk/lib/dotcom-components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ sudo amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:/opt/aws/amazon-
`${this.stage}/banner-deploy/*`,
`${this.stage}/channel-switches.json`,
`${this.stage}/configured-amounts-v3.json`,
`${this.stage}/exclusions.json`,
`${this.stage}/guardian-weekly-propensity-test/*`,
`PROD/auxia-credentials.json`,
],
Expand Down
10 changes: 6 additions & 4 deletions src/server/api/bannerRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {
Tracking,
} from '../../shared/types';
import { channelFromBannerChannel } from '../../shared/types';
import type { ExclusionSettings } from '../channelExclusions';
import type { ChannelSwitches } from '../channelSwitches';
import type { Auxia, GetTreatmentsAttributes } from '../lib/auxia';
import { getChoiceCardsSettings } from '../lib/choiceCards/choiceCards';
Expand All @@ -31,7 +32,7 @@ import type { BannerDeployTimesProvider } from '../tests/banners/bannerDeployTim
import { selectBannerTest } from '../tests/banners/bannerSelection';
import { getDesignForVariant } from '../tests/banners/channelBannerTests';
import type { Debug } from '../tests/epics/epicSelection';
import { shouldSuppressBannerForSectionDate } from '../utils/bannerSectionSuppression';
import { inExclusions } from '../utils/channelExclusionsMatcher';
import type { ValueProvider } from '../utils/valueReloader';

interface BannerDataResponse {
Expand All @@ -58,6 +59,7 @@ export const buildBannerRouter = (
mParticle: MParticle,
okta: Okta,
auxia: Auxia,
channelExclusions: ValueProvider<ExclusionSettings>,
): Router => {
const router = Router();

Expand All @@ -73,7 +75,7 @@ export const buildBannerRouter = (
): Promise<BannerDataResponse> => {
const { enableBanners, enableHardcodedBannerTests, enableScheduledBannerDeploys } =
channelSwitches.get();
const now = new Date();
const channelExclusionsData = channelExclusions.get();

if (!enableBanners) {
return {};
Expand All @@ -83,7 +85,7 @@ export const buildBannerRouter = (
return {};
}

if (shouldSuppressBannerForSectionDate(targeting.sectionId, now)) {
if (inExclusions(targeting, channelExclusionsData.banner)) {
return {};
}

Expand All @@ -96,7 +98,7 @@ export const buildBannerRouter = (
enableScheduledDeploys: enableScheduledBannerDeploys,
banditData: banditData.get(),
getMParticleProfile,
now,
now: new Date(),
forcedTestVariant: params.force,
checkAuxiaSuppression,
});
Expand Down
13 changes: 13 additions & 0 deletions src/server/api/epicRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
Tracking,
WeeklyArticleLog,
} from '../../shared/types';
import type { ExclusionSettings } from '../channelExclusions';
import type { ChannelSwitches } from '../channelSwitches';
import { getChoiceCardsSettings } from '../lib/choiceCards/choiceCards';
import { getDeviceType } from '../lib/deviceType';
Expand All @@ -31,6 +32,7 @@ import { selectAmountsTestVariant } from '../selection/ab';
import type { BanditData } from '../selection/banditData';
import type { Debug } from '../tests/epics/epicSelection';
import { findForcedTestAndVariant, findTestAndVariant } from '../tests/epics/epicSelection';
import { inExclusions } from '../utils/channelExclusionsMatcher';
import { logWarn } from '../utils/logging';
import type { ValueProvider } from '../utils/valueReloader';

Expand Down Expand Up @@ -61,6 +63,7 @@ export const buildEpicRouter = (
promotions: ValueProvider<PromotionsCache>,
mParticle: MParticle,
okta: Okta,
channelExclusions: ValueProvider<ExclusionSettings>,
): Router => {
const router = Router();

Expand Down Expand Up @@ -92,6 +95,7 @@ export const buildEpicRouter = (
getMParticleProfile: () => Promise<MParticleProfile | undefined>,
): Promise<EpicDataResponse> => {
const { enableEpics, enableSuperMode, enableHardcodedEpicTests } = channelSwitches.get();
const channelExclusionsData = channelExclusions.get();
if (!enableEpics) {
return {};
}
Expand All @@ -100,6 +104,15 @@ export const buildEpicRouter = (
return {};
}

if (
inExclusions(
{ ...targeting, tagIds: targeting.tags.map(({ id }) => id) },
channelExclusionsData.epic,
)
) {
return {};
}

const targetingMvtId = targeting.mvtId ?? 1;

const tests =
Expand Down
8 changes: 8 additions & 0 deletions src/server/api/gutterRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
TestTracking,
Tracking,
} from '../../shared/types';
import type { ExclusionSettings } from '../channelExclusions';
import type { ChannelSwitches } from '../channelSwitches';
import { getDeviceType } from '../lib/deviceType';
import { baseUrl } from '../lib/env';
Expand All @@ -16,6 +17,7 @@ import { pageIdIsExcluded } from '../lib/targeting';
import { buildGutterCampaignCode } from '../lib/tracking';
import { bodyContainsAllFields } from '../middleware';
import { selectGutterTest } from '../tests/gutters/gutterSelection';
import { inExclusions } from '../utils/channelExclusionsMatcher';
import type { ValueProvider } from '../utils/valueReloader';

interface GutterDataResponse {
Expand All @@ -31,6 +33,7 @@ interface GutterDataResponse {
export const buildGutterRouter = (
channelSwitches: ValueProvider<ChannelSwitches>,
tests: ValueProvider<GutterTest[]>,
channelExclusions: ValueProvider<ExclusionSettings>,
): Router => {
const router = Router();

Expand All @@ -41,6 +44,7 @@ export const buildGutterRouter = (
req: express.Request,
): GutterDataResponse => {
const { enableGutterLiveblogs } = channelSwitches.get();
const channelExclusionsData = channelExclusions.get();
if (!enableGutterLiveblogs) {
return {};
}
Expand All @@ -49,6 +53,10 @@ export const buildGutterRouter = (
return {};
}

if (inExclusions(targeting, channelExclusionsData.gutterAsk)) {
return {};
}

const testSelection = selectGutterTest(
targeting,
tests.get(),
Expand Down
9 changes: 9 additions & 0 deletions src/server/api/headerRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
TestTracking,
Tracking,
} from '../../shared/types';
import type { ExclusionSettings } from '../channelExclusions';
import type { ChannelSwitches } from '../channelSwitches';
import { getDeviceType } from '../lib/deviceType';
import { baseUrl } from '../lib/env';
Expand All @@ -16,6 +17,7 @@ import { getQueryParams } from '../lib/params';
import type { Params } from '../lib/params';
import { bodyContainsAllFields } from '../middleware';
import { selectHeaderTest } from '../tests/headers/headerSelection';
import { inExclusions } from '../utils/channelExclusionsMatcher';
import type { ValueProvider } from '../utils/valueReloader';

interface HeaderDataResponse {
Expand All @@ -33,6 +35,7 @@ export const buildHeaderRouter = (
tests: ValueProvider<HeaderTest[]>,
mParticle: MParticle,
okta: Okta,
channelExclusions: ValueProvider<ExclusionSettings>,
): Router => {
const router = Router();

Expand All @@ -44,9 +47,15 @@ export const buildHeaderRouter = (
getMParticleProfile: () => Promise<MParticleProfile | undefined>,
): Promise<HeaderDataResponse> => {
const { enableHeaders } = channelSwitches.get();
const channelExclusionsData = channelExclusions.get();
if (!enableHeaders) {
return {};
}

if (inExclusions(targeting, channelExclusionsData.header)) {
return {};
}

const testSelection = await selectHeaderTest(
targeting,
tests.get(),
Expand Down
52 changes: 52 additions & 0 deletions src/server/channelExclusions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { isProd } from './lib/env';
import { logWarn } from './utils/logging';
import { fetchS3Data } from './utils/S3';
import type { ValueReloader } from './utils/valueReloader';
import { buildReloader } from './utils/valueReloader';

export interface DateRange {
start: string; // ISO date "YYYY-MM-DD", inclusive
end: string; // ISO date "YYYY-MM-DD", inclusive
}

export interface ExclusionRule {
name: string;
sectionIds?: string[];
tagIds?: string[];
dateRange?: DateRange;
contentTypes?: Array<'Fronts' | 'Articles'>;
}

export interface ChannelExclusions {
rules: ExclusionRule[];
}

export interface ExclusionSettings {
epic?: ChannelExclusions;
banner?: ChannelExclusions;
gutterAsk?: ChannelExclusions;
header?: ChannelExclusions;
}

const emptyExclusions: ExclusionSettings = {};

const getExclusions = async (): Promise<ExclusionSettings> => {
try {
const data = await fetchS3Data(
'support-admin-console',
`${isProd ? 'PROD' : 'CODE'}/exclusions.json`,
);
const parsed = JSON.parse(data) as ExclusionSettings;
return parsed;
} catch (error) {
logWarn(
`Failed to load exclusions config from S3: ${String(error)}. Proceeding with no exclusions.`,
);
return emptyExclusions;
}
};

const buildChannelExclusionsReloader = (): Promise<ValueReloader<ExclusionSettings>> =>
buildReloader(getExclusions, 60);

export { buildChannelExclusionsReloader };
9 changes: 7 additions & 2 deletions src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { buildEpicRouter } from './api/epicRouter';
import { buildGutterRouter } from './api/gutterRouter';
import { buildHeaderRouter } from './api/headerRouter';
import { buildTickerRouter } from './api/tickerRouter';
import { buildChannelExclusionsReloader } from './channelExclusions';
import { buildChannelSwitchesReloader } from './channelSwitches';
import { buildChoiceCardAmountsReloader } from './choiceCardAmounts';
import { Auxia } from './lib/auxia';
Expand Down Expand Up @@ -88,6 +89,7 @@ const buildApp = async (): Promise<Express> => {
gutterLiveblogTests,
productCatalog,
promotions,
channelExclusions,
] = await Promise.all([
buildChannelSwitchesReloader(),
buildSuperModeArticlesReloader(),
Expand All @@ -102,6 +104,7 @@ const buildApp = async (): Promise<Express> => {
buildGutterLiveblogTestsReloader(),
buildProductCatalogReloader(),
buildPromotionsReloader(),
buildChannelExclusionsReloader(),
]);

const banditData = await buildBanditDataReloader(articleEpicTests, bannerTests);
Expand Down Expand Up @@ -129,6 +132,7 @@ const buildApp = async (): Promise<Express> => {
promotions,
mParticle,
okta,
channelExclusions,
),
);
app.use(
Expand All @@ -145,13 +149,14 @@ const buildApp = async (): Promise<Express> => {
mParticle,
okta,
auxia,
channelExclusions,
),
);
app.use(buildHeaderRouter(channelSwitches, headerTests, mParticle, okta));
app.use(buildHeaderRouter(channelSwitches, headerTests, mParticle, okta, channelExclusions));

app.use(buildAuxiaProxyRouter(channelSwitches, auxiaConfig));

app.use(buildGutterRouter(channelSwitches, gutterLiveblogTests));
app.use(buildGutterRouter(channelSwitches, gutterLiveblogTests, channelExclusions));

app.use(buildTickerRouter(tickerData));

Expand Down
63 changes: 0 additions & 63 deletions src/server/utils/bannerSectionSuppression.test.ts

This file was deleted.

Loading
Loading