From 3560a62ea306e69270f68a863ef6acc68fecee61 Mon Sep 17 00:00:00 2001 From: Inokentii Mazhara Date: Wed, 13 Nov 2024 17:45:01 +0200 Subject: [PATCH 1/3] TW-1563 Add specifications for theming native ads --- src/advertising/external-ads.ts | 19 ++++++++++++++----- src/routers/slise-ad-rules/ad-places.ts | 24 +++++++++++++++++++++++- src/utils/schemas.ts | 5 ++++- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/advertising/external-ads.ts b/src/advertising/external-ads.ts index 75373d8..73e805d 100644 --- a/src/advertising/external-ads.ts +++ b/src/advertising/external-ads.ts @@ -94,6 +94,11 @@ export interface ExtVersionConstraints { extVersion: string; } +interface BrowserConstraints { + enableForMises?: boolean; + enableForNonMises?: boolean; +} + export interface AdPlacesRule extends ExtVersionConstraints { urlRegexes: string[]; selector: { @@ -108,7 +113,7 @@ export interface AdPlacesRule extends ExtVersionConstraints { isNative?: boolean; } -export interface PermanentAdPlacesRule extends ExtVersionConstraints { +export interface PermanentAdPlacesRule extends ExtVersionConstraints, BrowserConstraints { urlRegexes: string[]; adSelector: { isMultiple: boolean; @@ -137,6 +142,9 @@ export interface PermanentAdPlacesRule extends ExtVersionConstraints { stylesOverrides?: AdStylesOverrides[]; shouldHideOriginal?: boolean; displayWidth?: string; + supportsTheming?: boolean; + fontSampleSelector?: string; + enableForNonMises?: boolean; } export interface AdProvidersByDomainRule extends ExtVersionConstraints { @@ -217,18 +225,19 @@ export const hypelabCampaignsBlacklistMethods = setStorageMethodsFactory(HYPELAB const FALLBACK_VERSION = '0.0.0'; export function filterRules(rules: T[], version: string | undefined): T[]; -export function filterRules( +export function filterRules( rules: T[], version: string | undefined, isMisesBrowser: boolean ): T[]; -export function filterRules( +export function filterRules( rules: T[], version: string | undefined, isMisesBrowser = false ) { return rules.filter( - ({ extVersion, enableForMises = true }) => - versionSatisfiesRange(version ?? FALLBACK_VERSION, extVersion) && (!isMisesBrowser || enableForMises) + ({ extVersion, enableForMises = true, enableForNonMises = true }) => + versionSatisfiesRange(version ?? FALLBACK_VERSION, extVersion) && + (isMisesBrowser ? enableForMises : enableForNonMises) ); } diff --git a/src/routers/slise-ad-rules/ad-places.ts b/src/routers/slise-ad-rules/ad-places.ts index 4fbd172..f4f6108 100644 --- a/src/routers/slise-ad-rules/ad-places.ts +++ b/src/routers/slise-ad-rules/ad-places.ts @@ -18,7 +18,7 @@ import { } from '../../utils/schemas'; const transformAdPlaces = (value: T[], req: Request) => - filterRules(value, req.query.extVersion as string | undefined); + filterRules(value, req.query.extVersion as string | undefined, req.query.isMisesBrowser === 'true'); const transformAdPlacesDictionary = (rules: Record, req: Request) => transformValues(rules, value => transformAdPlaces(value, req)); @@ -306,6 +306,18 @@ const transformAdPlacesDictionary = (rules: Rec * A range of display widths in a semver-like format where the rule is applicable. Numbers can be only * integers. If not specified, the rule is applicable for all display widths. * example: '>=1024 <1280' + * supportsTheming: + * type: boolean + * description: Whether our banner that is inserted supports theming + * default: false + * fontSampleSelector: + * type: string + * description: > + * A selector of the element which should be measured to define font size and line height. + * If not specified, the font size and line height will be taken from the page body. + * enableForNonMises: + * type: boolean + * default: true * example: * urlRegexes: * - '^https://etherscan\.io/tx/' @@ -402,6 +414,11 @@ export const adPlacesRulesRouter = Router(); * type: string * default: '0.0.0' * description: The extension version for which the rules should be returned + * - in: query + * name: isMisesBrowser + * schema: + * type: boolean + * default: false * responses: * '200': * description: Rules list @@ -425,6 +442,11 @@ export const adPlacesRulesRouter = Router(); * type: string * default: '0.0.0' * description: The extension version for which the rules should be returned + * - in: query + * name: isMisesBrowser + * schema: + * type: boolean + * default: false * responses: * '200': * description: Domain - rules list dictionary diff --git a/src/utils/schemas.ts b/src/utils/schemas.ts index 49f3e93..f3900b9 100644 --- a/src/utils/schemas.ts +++ b/src/utils/schemas.ts @@ -213,7 +213,10 @@ const permanentAdPlacesRulesSchema = arraySchema() } return true; - }) + }), + supportsTheming: booleanSchema().default(false), + fontSampleSelector: cssSelectorSchema.clone(), + enableForNonMises: booleanSchema().default(true) }) .test('insertion-place-specified', (value: PermanentAdPlacesRule | undefined) => { if (!value) { From 7c28860c2faf4598e61cc52531ecd97844dfb014 Mon Sep 17 00:00:00 2001 From: Inokentii Mazhara Date: Mon, 18 Nov 2024 09:36:18 +0200 Subject: [PATCH 2/3] TW-1563 Minor refactoring --- src/advertising/external-ads.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/advertising/external-ads.ts b/src/advertising/external-ads.ts index 73e805d..74a1968 100644 --- a/src/advertising/external-ads.ts +++ b/src/advertising/external-ads.ts @@ -144,7 +144,6 @@ export interface PermanentAdPlacesRule extends ExtVersionConstraints, BrowserCon displayWidth?: string; supportsTheming?: boolean; fontSampleSelector?: string; - enableForNonMises?: boolean; } export interface AdProvidersByDomainRule extends ExtVersionConstraints { From 29f531530136efc345ba193f5f0e5334b98283f6 Mon Sep 17 00:00:00 2001 From: Inokentii Mazhara Date: Mon, 18 Nov 2024 09:49:40 +0200 Subject: [PATCH 3/3] TW-1563 Make ts types match validation schemas and swagger types --- src/advertising/external-ads.ts | 3 +- src/routers/slise-ad-rules/ad-places.ts | 3 + src/routers/slise-ad-rules/providers.ts | 3 + src/utils/schemas.ts | 153 ++++++++++++------------ 4 files changed, 83 insertions(+), 79 deletions(-) diff --git a/src/advertising/external-ads.ts b/src/advertising/external-ads.ts index 74a1968..5a8517c 100644 --- a/src/advertising/external-ads.ts +++ b/src/advertising/external-ads.ts @@ -151,11 +151,10 @@ export interface AdProvidersByDomainRule extends ExtVersionConstraints { providers: string[]; } -export interface AdProviderSelectorsRule extends ExtVersionConstraints { +export interface AdProviderSelectorsRule extends ExtVersionConstraints, BrowserConstraints { selectors: string[]; negativeSelectors?: string[]; parentDepth?: number; - enableForMises?: boolean; } export interface AdProviderForAllSitesRule extends ExtVersionConstraints { diff --git a/src/routers/slise-ad-rules/ad-places.ts b/src/routers/slise-ad-rules/ad-places.ts index f4f6108..4adbae6 100644 --- a/src/routers/slise-ad-rules/ad-places.ts +++ b/src/routers/slise-ad-rules/ad-places.ts @@ -318,6 +318,9 @@ const transformAdPlacesDictionary = (rules: Rec * enableForNonMises: * type: boolean * default: true + * enableForMises: + * type: boolean + * default: true * example: * urlRegexes: * - '^https://etherscan\.io/tx/' diff --git a/src/routers/slise-ad-rules/providers.ts b/src/routers/slise-ad-rules/providers.ts index cbbe99b..b5c1597 100644 --- a/src/routers/slise-ad-rules/providers.ts +++ b/src/routers/slise-ad-rules/providers.ts @@ -87,6 +87,9 @@ import { * enableForMises: * type: boolean * default: true + * enableForNonMises: + * type: boolean + * default: true * AdByProviderSelector: * oneOf: * - type: string diff --git a/src/utils/schemas.ts b/src/utils/schemas.ts index f3900b9..f9a1008 100644 --- a/src/utils/schemas.ts +++ b/src/utils/schemas.ts @@ -153,93 +153,91 @@ export const adPlacesRulesDictionarySchema: IObjectSchema = objectSchema() + .shape({ + urlRegexes: arraySchema().of(regexStringSchema.clone().required()).required(), + adSelector: objectSchema() .shape({ - urlRegexes: arraySchema().of(regexStringSchema.clone().required()).required(), - adSelector: objectSchema() - .shape({ - isMultiple: booleanSchema().required(), - cssString: cssSelectorSchema.clone().required(), - parentDepth: numberSchema().integer().min(0).required() - }) - .required(), - parentSelector: objectSchema() - .shape({ - isMultiple: booleanSchema().required(), - cssString: cssSelectorSchema.clone().required(), - parentDepth: numberSchema().integer().min(0).required() - }) - .required(), - insertionIndex: numberSchema().integer(), - insertBeforeSelector: cssSelectorSchema, - insertAfterSelector: cssSelectorSchema, - insertionsCount: numberSchema().integer().min(1), - shouldUseDivWrapper: booleanSchema(), - wrapperType: stringSchema().oneOf(['div', 'tbody']), - colsBefore: numberSchema().integer().min(0), - colspan: numberSchema().integer().min(1), - colsAfter: numberSchema().integer().min(0), - elementStyle: styleSchema, - divWrapperStyle: styleSchema, - wrapperStyle: styleSchema, - elementToMeasureSelector: cssSelectorSchema, - elementsToMeasureSelectors: objectSchema() - .shape({ width: cssSelectorSchema.clone(), height: cssSelectorSchema.clone() }) - .test('all-fields-present', function (value: unknown) { - if (!value || typeof value !== 'object') { - return true; - } - - if (typeof (value as any).width === 'string' && typeof (value as any).height === 'string') { - return true; - } - - throw this.createError({ path: this.path, message: 'Both `width` and `height` fields must be specified' }); - }) - .default(undefined) as unknown as IObjectSchema<{ width: string; height: string } | undefined>, - stylesOverrides: arraySchema().of(adStylesOverridesSchema.clone().required()), - shouldHideOriginal: booleanSchema(), - extVersion: versionRangeSchema.clone().required(), - displayWidth: versionRangeSchema.clone().test('valid-boundary-values', (value: string | undefined) => { - if (!isDefined(value) || value.length === 0) { - return true; - } - - const nonIntegerNumberMatches = value.match(/\d+\.\d+/g); - if (isDefined(nonIntegerNumberMatches)) { - throw new Error('Display width must be an integer'); - } - - return true; - }), - supportsTheming: booleanSchema().default(false), - fontSampleSelector: cssSelectorSchema.clone(), - enableForNonMises: booleanSchema().default(true) + isMultiple: booleanSchema().required(), + cssString: cssSelectorSchema.clone().required(), + parentDepth: numberSchema().integer().min(0).required() }) - .test('insertion-place-specified', (value: PermanentAdPlacesRule | undefined) => { - if (!value) { + .required(), + parentSelector: objectSchema() + .shape({ + isMultiple: booleanSchema().required(), + cssString: cssSelectorSchema.clone().required(), + parentDepth: numberSchema().integer().min(0).required() + }) + .required(), + insertionIndex: numberSchema().integer(), + insertBeforeSelector: cssSelectorSchema, + insertAfterSelector: cssSelectorSchema, + insertionsCount: numberSchema().integer().min(1), + shouldUseDivWrapper: booleanSchema(), + wrapperType: stringSchema().oneOf(['div', 'tbody']), + colsBefore: numberSchema().integer().min(0), + colspan: numberSchema().integer().min(1), + colsAfter: numberSchema().integer().min(0), + elementStyle: styleSchema, + divWrapperStyle: styleSchema, + wrapperStyle: styleSchema, + elementToMeasureSelector: cssSelectorSchema, + elementsToMeasureSelectors: objectSchema() + .shape({ width: cssSelectorSchema.clone(), height: cssSelectorSchema.clone() }) + .test('all-fields-present', function (value: unknown) { + if (!value || typeof value !== 'object') { return true; } - const { insertionIndex, insertBeforeSelector, insertAfterSelector } = value; - const definedValuesCount = [insertionIndex, insertBeforeSelector, insertAfterSelector].filter(isDefined).length; - - if (definedValuesCount !== 1) { - throw new Error( - 'Exactly one of insertionIndex, insertBeforeSelector and insertAfterSelector must be specified' - ); + if (typeof (value as any).width === 'string' && typeof (value as any).height === 'string') { + return true; } - return true; + throw this.createError({ path: this.path, message: 'Both `width` and `height` fields must be specified' }); }) - .required() - ) + .default(undefined) as unknown as IObjectSchema<{ width: string; height: string } | undefined>, + stylesOverrides: arraySchema().of(adStylesOverridesSchema.clone().required()), + shouldHideOriginal: booleanSchema(), + extVersion: versionRangeSchema.clone().required(), + displayWidth: versionRangeSchema.clone().test('valid-boundary-values', (value: string | undefined) => { + if (!isDefined(value) || value.length === 0) { + return true; + } + + const nonIntegerNumberMatches = value.match(/\d+\.\d+/g); + if (isDefined(nonIntegerNumberMatches)) { + throw new Error('Display width must be an integer'); + } + + return true; + }), + supportsTheming: booleanSchema().default(false), + fontSampleSelector: cssSelectorSchema.clone(), + enableForMises: booleanSchema().default(true), + enableForNonMises: booleanSchema().default(true) + }) + .test('insertion-place-specified', (value: PermanentAdPlacesRule | undefined) => { + if (!value) { + return true; + } + + const { insertionIndex, insertBeforeSelector, insertAfterSelector } = value; + const definedValuesCount = [insertionIndex, insertBeforeSelector, insertAfterSelector].filter(isDefined).length; + + if (definedValuesCount !== 1) { + throw new Error('Exactly one of insertionIndex, insertBeforeSelector and insertAfterSelector must be specified'); + } + + return true; + }) .required(); export const permanentAdPlacesRulesDictionarySchema: IObjectSchema> = - makeDictionarySchema(hostnameSchema, permanentAdPlacesRulesSchema).required(); + makeDictionarySchema( + hostnameSchema, + arraySchema().of(permanentAdPlacesRuleSchema.clone().required()).required() + ).required(); const adProvidersByDomainRulesSchema = arraySchema() .of( @@ -261,7 +259,8 @@ const adProvidersSelectorsRuleSchema: IObjectSchema = o negativeSelectors: cssSelectorsListSchema.clone(), extVersion: versionRangeSchema.clone().required(), parentDepth: numberSchema().integer().min(0).default(0), - enableForMises: booleanSchema().default(true) + enableForMises: booleanSchema().default(true), + enableForNonMises: booleanSchema().default(true) }); export const adProvidersDictionarySchema = makeDictionarySchema(