Skip to content
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: 3 additions & 1 deletion src/server/api/auxiaProxyRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,13 @@ export const buildAuxiaProxyRouter = (config: AuxiaRouterConfig): Router => {
// Nullable attributes:
// 'browserId'
// 'showDefaultGate'
// 'hideSupportMessagingTimestamp'

async (req: express.Request, res: express.Response, next: express.NextFunction) => {
try {
const now = Date.now(); // current time in milliseconds since epoch
const payload = req.body as GetTreatmentsRequestPayload;
const gateType = getTreatmentsRequestPayloadToGateType(payload);
const gateType = getTreatmentsRequestPayloadToGateType(payload, now);
const envelop = await gateTypeToUserTreatmentsEnvelop(config, gateType, payload);
if (envelop !== undefined) {
const data = userTreatmentsEnvelopToProxyGetTreatmentsAnswerData(envelop);
Expand Down
88 changes: 73 additions & 15 deletions src/server/signin-gate/libPure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,9 +362,46 @@ export const userHasConsented = (payload: GetTreatmentsRequestPayload): boolean
return payload.hasConsented;
};

export const hideSupportMessagingHasOverride = (
payload: GetTreatmentsRequestPayload,
now: number,
): boolean => {
// Purpose:
// Return true if we have a hideSupportMessagingTimestamp and it's less than 30 days old

// Parameters:
// - payload
// - now: current time in milliseconds since epoch
// (nb: We pass now instead of getting it within the body
// to make the function pure and testable)

// Date: 1 September 2025
//
// The payload.hideSupportMessagingTimestamp, could be in the future,
// this happens if the user has performed a recurring contribution.
// We have guarded against that situation client side
// https://github.com/guardian/dotcom-rendering/pull/14462
// but also guard against it here.

if (payload.hideSupportMessagingTimestamp === undefined) {
return false;
}
if (!Number.isInteger(payload.hideSupportMessagingTimestamp)) {
return false;
}
if (payload.hideSupportMessagingTimestamp > now) {
return false;
}
const limit = 86400 * 30 * 1000; // milliseconds over 30 days
return now - payload.hideSupportMessagingTimestamp < limit;
};

export const getTreatmentsRequestPayloadToGateType = (
payload: GetTreatmentsRequestPayload,
now: number,
): GateType => {
// now: current time in milliseconds since epoch

// This function is a pure function (without any side effects) which gets the body
// of a '/auxia/get-treatments' request and returns the correct GateType
// It was introduced to separate the choice of the gate from it's actual build,
Expand Down Expand Up @@ -450,8 +487,13 @@ export const getTreatmentsRequestPayloadToGateType = (
//
// effects:
// - Notify Auxia for analytics
// - No gate display the first 3 page views
// - Gate: 3x dismissal, then mandatory
// - Guardian drives the gate:
// - No gate for 30 days after a single contribution event (gu_hide_support_messaging; hideSupportMessagingTimestamp)
// - No gate display the first 3 page views
// - 3x dismissal, then mandatory
if (hideSupportMessagingHasOverride(payload, now)) {
return 'AuxiaAnalyticsThenNone';
}
if (payload.dailyArticleCount < 3) {
return 'AuxiaAnalyticsThenNone';
}
Expand All @@ -477,9 +519,12 @@ export const getTreatmentsRequestPayloadToGateType = (
// effects:
// - No Auxia notification
// - Guardian drives the gate:
// - No gate display the first 3 page views
// - Gate: dismissible gates
// then no gate after 5 dismisses
// - No gate for 30 days after a single contribution event (gu_hide_support_messaging; hideSupportMessagingTimestamp)
// - No gate display the first 3 page views
// - Dismissible gates then no gate after 5 dismisses
if (hideSupportMessagingHasOverride(payload, now)) {
return 'None';
}
if (payload.dailyArticleCount < 3) {
return 'None';
}
Expand All @@ -505,8 +550,12 @@ export const getTreatmentsRequestPayloadToGateType = (
// effects:
// - Notify Auxia for analytics
// - Guardian drives the gate:
// - No gate display the first 3 page views
// - Gate: 3x dismissal, then mandatory
// - No gate for 30 days after a single contribution event (gu_hide_support_messaging; hideSupportMessagingTimestamp)
// - No gate display the first 3 page views
// - 3x dismissal, then mandatory
if (hideSupportMessagingHasOverride(payload, now)) {
return 'AuxiaAnalyticsThenNone';
}
if (payload.dailyArticleCount < 3) {
return 'AuxiaAnalyticsThenNone';
}
Expand Down Expand Up @@ -551,9 +600,12 @@ export const getTreatmentsRequestPayloadToGateType = (
// effects:
// - No Auxia notification
// - Guardian drives the gate:
// - No gate for 30 days after a single contribution event (gu_hide_support_messaging; hideSupportMessagingTimestamp)
// - No gate display the first 3 page views
// - Gate: dismissible gates
// then no gate after 5 dismisses
// - Dismissible gates then no gate after 5 dismisses
if (hideSupportMessagingHasOverride(payload, now)) {
return 'None';
}
if (payload.dailyArticleCount < 3) {
return 'None';
}
Expand All @@ -579,9 +631,12 @@ export const getTreatmentsRequestPayloadToGateType = (
// effects:
// - No Auxia notification
// - Guardian drives the gate:
// - No gate display the first 3 page views
// - Gate: dismissible gates
// then no gate after 5 dismisses
// - No gate for 30 days after a single contribution event (gu_hide_support_messaging; hideSupportMessagingTimestamp)
// - No gate display the first 3 page views
// - Dismissible gates then no gate after 5 dismisses
if (hideSupportMessagingHasOverride(payload, now)) {
return 'None';
}
if (payload.dailyArticleCount < 3) {
return 'None';
}
Expand All @@ -607,9 +662,12 @@ export const getTreatmentsRequestPayloadToGateType = (
// effects:
// - No Auxia notification
// - Guardian drives the gate:
// - No gate display the first 3 page views
// - Gate: dismissible gates
// then no gate after 5 dismisses
// - No gate for 30 days after a single contribution event (gu_hide_support_messaging; hideSupportMessagingTimestamp)
// - No gate display the first 3 page views
// - Dismissible gates then no gate after 5 dismisses
if (hideSupportMessagingHasOverride(payload, now)) {
return 'None';
}
if (payload.dailyArticleCount < 3) {
return 'None';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ describe('getTreatmentsRequestPayloadToGateType (ireland)', () => {
shouldServeDismissible: false,
showDefaultGate: undefined,
gateDisplayCount: 0,
hideSupportMessagingTimestamp: undefined,
};
const gateType = getTreatmentsRequestPayloadToGateType(payload);
const now = 1756568322187; // current time in milliseconds since epoch
const gateType = getTreatmentsRequestPayloadToGateType(payload, now);
expect(gateType).toStrictEqual('AuxiaAPI');
});

Expand All @@ -46,9 +48,9 @@ describe('getTreatmentsRequestPayloadToGateType (ireland)', () => {
// effects:
// - No Auxia notification
// - Guardian drives the gate:
// - No gate display the first 3 page views
// - Gate: dismissible gates
// then no gate after 5 dismisses
// - No gate for 30 days after a single contribution event (gu_hide_support_messaging; hideSupportMessagingTimestamp)
// - No gate display the first 3 page views
// - Dismissible gates then no gate after 5 dismisses

const payload: GetTreatmentsRequestPayload = {
browserId: 'sample',
Expand All @@ -67,8 +69,10 @@ describe('getTreatmentsRequestPayloadToGateType (ireland)', () => {
shouldServeDismissible: false,
showDefaultGate: undefined,
gateDisplayCount: 4,
hideSupportMessagingTimestamp: undefined,
};
const gateType = getTreatmentsRequestPayloadToGateType(payload);
const now = 1756568322187; // current time in milliseconds since epoch
const gateType = getTreatmentsRequestPayloadToGateType(payload, now);
expect(gateType).toStrictEqual('None');
});

Expand All @@ -83,9 +87,9 @@ describe('getTreatmentsRequestPayloadToGateType (ireland)', () => {
// effects:
// - No Auxia notification
// - Guardian drives the gate:
// - No gate display the first 3 page views
// - Gate: dismissible gates
// then no gate after 5 dismisses
// - No gate for 30 days after a single contribution event (gu_hide_support_messaging; hideSupportMessagingTimestamp)
// - No gate display the first 3 page views
// - Dismissible gates then no gate after 5 dismisses

const payload: GetTreatmentsRequestPayload = {
browserId: 'sample',
Expand All @@ -104,8 +108,10 @@ describe('getTreatmentsRequestPayloadToGateType (ireland)', () => {
shouldServeDismissible: false,
showDefaultGate: undefined,
gateDisplayCount: 4,
hideSupportMessagingTimestamp: undefined,
};
const gateType = getTreatmentsRequestPayloadToGateType(payload);
const now = 1756568322187; // current time in milliseconds since epoch
const gateType = getTreatmentsRequestPayloadToGateType(payload, now);
expect(gateType).toStrictEqual('GuDismissible');
});

Expand All @@ -120,9 +126,9 @@ describe('getTreatmentsRequestPayloadToGateType (ireland)', () => {
// effects:
// - No Auxia notification
// - Guardian drives the gate:
// - No gate display the first 3 page views
// - Gate: dismissible gates
// then no gate after 5 dismisses
// - No gate for 30 days after a single contribution event (gu_hide_support_messaging; hideSupportMessagingTimestamp)
// - No gate display the first 3 page views
// - Dismissible gates then no gate after 5 dismisses

const payload: GetTreatmentsRequestPayload = {
browserId: 'sample',
Expand All @@ -141,8 +147,10 @@ describe('getTreatmentsRequestPayloadToGateType (ireland)', () => {
shouldServeDismissible: false,
showDefaultGate: undefined,
gateDisplayCount: 8,
hideSupportMessagingTimestamp: undefined,
};
const gateType = getTreatmentsRequestPayloadToGateType(payload);
const now = 1756568322187; // current time in milliseconds since epoch
const gateType = getTreatmentsRequestPayloadToGateType(payload, now);
expect(gateType).toStrictEqual('None');
});

Expand All @@ -156,8 +164,10 @@ describe('getTreatmentsRequestPayloadToGateType (ireland)', () => {
//
// effects:
// - Notify Auxia for analytics
// - No gate display the first 3 page views
// - Gate: 3x dismissal, then mandatory
// - Guardian drives the gate:
// - No gate for 30 days after a single contribution event (gu_hide_support_messaging; hideSupportMessagingTimestamp)
// - No gate display the first 3 page views
// - 3x dismissal, then mandatory

const payload: GetTreatmentsRequestPayload = {
browserId: 'sample',
Expand All @@ -176,8 +186,10 @@ describe('getTreatmentsRequestPayloadToGateType (ireland)', () => {
shouldServeDismissible: false,
showDefaultGate: undefined,
gateDisplayCount: 8,
hideSupportMessagingTimestamp: undefined,
};
const gateType = getTreatmentsRequestPayloadToGateType(payload);
const now = 1756568322187; // current time in milliseconds since epoch
const gateType = getTreatmentsRequestPayloadToGateType(payload, now);
expect(gateType).toStrictEqual('AuxiaAnalyticsThenNone');
});

Expand All @@ -191,8 +203,10 @@ describe('getTreatmentsRequestPayloadToGateType (ireland)', () => {
//
// effects:
// - Notify Auxia for analytics
// - No gate display the first 3 page views
// - Gate: 3x dismissal, then mandatory
// - Guardian drives the gate:
// - No gate for 30 days after a single contribution event (gu_hide_support_messaging; hideSupportMessagingTimestamp)
// - No gate display the first 3 page views
// - 3x dismissal, then mandatory

const payload: GetTreatmentsRequestPayload = {
browserId: 'sample',
Expand All @@ -211,8 +225,10 @@ describe('getTreatmentsRequestPayloadToGateType (ireland)', () => {
shouldServeDismissible: false,
showDefaultGate: undefined,
gateDisplayCount: 1,
hideSupportMessagingTimestamp: undefined,
};
const gateType = getTreatmentsRequestPayloadToGateType(payload);
const now = 1756568322187; // current time in milliseconds since epoch
const gateType = getTreatmentsRequestPayloadToGateType(payload, now);
expect(gateType).toStrictEqual('AuxiaAnalyticsThenGuDismissible');
});

Expand All @@ -226,8 +242,10 @@ describe('getTreatmentsRequestPayloadToGateType (ireland)', () => {
//
// effects:
// - Notify Auxia for analytics
// - No gate display the first 3 page views
// - Gate: 3x dismissal, then mandatory
// - Guardian drives the gate:
// - No gate for 30 days after a single contribution event (gu_hide_support_messaging; hideSupportMessagingTimestamp)
// - No gate display the first 3 page views
// - 3x dismissal, then mandatory

const payload: GetTreatmentsRequestPayload = {
browserId: 'sample',
Expand All @@ -246,8 +264,10 @@ describe('getTreatmentsRequestPayloadToGateType (ireland)', () => {
shouldServeDismissible: false,
showDefaultGate: undefined,
gateDisplayCount: 5,
hideSupportMessagingTimestamp: undefined,
};
const gateType = getTreatmentsRequestPayloadToGateType(payload);
const now = 1756568322187; // current time in milliseconds since epoch
const gateType = getTreatmentsRequestPayloadToGateType(payload, now);
expect(gateType).toStrictEqual('AuxiaAnalyticsThenGuMandatory');
});

Expand All @@ -262,6 +282,7 @@ describe('getTreatmentsRequestPayloadToGateType (ireland)', () => {
// effects:
// - Notify Auxia for analytics
// - Guardian drives the gate:
// - No gate display the first 3 page views
// - Gate: 3x dismissal, then mandatory
// - No gate for 30 days after a single contribution event (gu_hide_support_messaging; hideSupportMessagingTimestamp)
// - No gate display the first 3 page views
// - 3x dismissal, then mandatory
});
Loading