Skip to content

Commit 22c6187

Browse files
authored
TW-1563: Add specifications for theming native ads (#178)
* TW-1563 Add specifications for theming native ads * TW-1563 Minor refactoring * TW-1563 Make ts types match validation schemas and swagger types
1 parent 9a6d786 commit 22c6187

File tree

4 files changed

+119
-82
lines changed

4 files changed

+119
-82
lines changed

src/advertising/external-ads.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ export interface ExtVersionConstraints {
9494
extVersion: string;
9595
}
9696

97+
interface BrowserConstraints {
98+
enableForMises?: boolean;
99+
enableForNonMises?: boolean;
100+
}
101+
97102
export interface AdPlacesRule extends ExtVersionConstraints {
98103
urlRegexes: string[];
99104
selector: {
@@ -108,7 +113,7 @@ export interface AdPlacesRule extends ExtVersionConstraints {
108113
isNative?: boolean;
109114
}
110115

111-
export interface PermanentAdPlacesRule extends ExtVersionConstraints {
116+
export interface PermanentAdPlacesRule extends ExtVersionConstraints, BrowserConstraints {
112117
urlRegexes: string[];
113118
adSelector: {
114119
isMultiple: boolean;
@@ -137,18 +142,19 @@ export interface PermanentAdPlacesRule extends ExtVersionConstraints {
137142
stylesOverrides?: AdStylesOverrides[];
138143
shouldHideOriginal?: boolean;
139144
displayWidth?: string;
145+
supportsTheming?: boolean;
146+
fontSampleSelector?: string;
140147
}
141148

142149
export interface AdProvidersByDomainRule extends ExtVersionConstraints {
143150
urlRegexes: string[];
144151
providers: string[];
145152
}
146153

147-
export interface AdProviderSelectorsRule extends ExtVersionConstraints {
154+
export interface AdProviderSelectorsRule extends ExtVersionConstraints, BrowserConstraints {
148155
selectors: string[];
149156
negativeSelectors?: string[];
150157
parentDepth?: number;
151-
enableForMises?: boolean;
152158
}
153159

154160
export interface AdProviderForAllSitesRule extends ExtVersionConstraints {
@@ -217,18 +223,19 @@ export const hypelabCampaignsBlacklistMethods = setStorageMethodsFactory(HYPELAB
217223
const FALLBACK_VERSION = '0.0.0';
218224

219225
export function filterRules<T extends ExtVersionConstraints>(rules: T[], version: string | undefined): T[];
220-
export function filterRules<T extends ExtVersionConstraints & { enableForMises?: boolean }>(
226+
export function filterRules<T extends ExtVersionConstraints & BrowserConstraints>(
221227
rules: T[],
222228
version: string | undefined,
223229
isMisesBrowser: boolean
224230
): T[];
225-
export function filterRules<T extends ExtVersionConstraints & { enableForMises?: boolean }>(
231+
export function filterRules<T extends ExtVersionConstraints & BrowserConstraints>(
226232
rules: T[],
227233
version: string | undefined,
228234
isMisesBrowser = false
229235
) {
230236
return rules.filter(
231-
({ extVersion, enableForMises = true }) =>
232-
versionSatisfiesRange(version ?? FALLBACK_VERSION, extVersion) && (!isMisesBrowser || enableForMises)
237+
({ extVersion, enableForMises = true, enableForNonMises = true }) =>
238+
versionSatisfiesRange(version ?? FALLBACK_VERSION, extVersion) &&
239+
(isMisesBrowser ? enableForMises : enableForNonMises)
233240
);
234241
}

src/routers/slise-ad-rules/ad-places.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
} from '../../utils/schemas';
1919

2020
const transformAdPlaces = <T extends ExtVersionConstraints>(value: T[], req: Request) =>
21-
filterRules(value, req.query.extVersion as string | undefined);
21+
filterRules(value, req.query.extVersion as string | undefined, req.query.isMisesBrowser === 'true');
2222
const transformAdPlacesDictionary = <T extends ExtVersionConstraints>(rules: Record<string, T[]>, req: Request) =>
2323
transformValues(rules, value => transformAdPlaces(value, req));
2424

@@ -306,6 +306,21 @@ const transformAdPlacesDictionary = <T extends ExtVersionConstraints>(rules: Rec
306306
* A range of display widths in a semver-like format where the rule is applicable. Numbers can be only
307307
* integers. If not specified, the rule is applicable for all display widths.
308308
* example: '>=1024 <1280'
309+
* supportsTheming:
310+
* type: boolean
311+
* description: Whether our banner that is inserted supports theming
312+
* default: false
313+
* fontSampleSelector:
314+
* type: string
315+
* description: >
316+
* A selector of the element which should be measured to define font size and line height.
317+
* If not specified, the font size and line height will be taken from the page body.
318+
* enableForNonMises:
319+
* type: boolean
320+
* default: true
321+
* enableForMises:
322+
* type: boolean
323+
* default: true
309324
* example:
310325
* urlRegexes:
311326
* - '^https://etherscan\.io/tx/'
@@ -402,6 +417,11 @@ export const adPlacesRulesRouter = Router();
402417
* type: string
403418
* default: '0.0.0'
404419
* description: The extension version for which the rules should be returned
420+
* - in: query
421+
* name: isMisesBrowser
422+
* schema:
423+
* type: boolean
424+
* default: false
405425
* responses:
406426
* '200':
407427
* description: Rules list
@@ -425,6 +445,11 @@ export const adPlacesRulesRouter = Router();
425445
* type: string
426446
* default: '0.0.0'
427447
* description: The extension version for which the rules should be returned
448+
* - in: query
449+
* name: isMisesBrowser
450+
* schema:
451+
* type: boolean
452+
* default: false
428453
* responses:
429454
* '200':
430455
* description: Domain - rules list dictionary

src/routers/slise-ad-rules/providers.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ import {
8787
* enableForMises:
8888
* type: boolean
8989
* default: true
90+
* enableForNonMises:
91+
* type: boolean
92+
* default: true
9093
* AdByProviderSelector:
9194
* oneOf:
9295
* - type: string

src/utils/schemas.ts

Lines changed: 76 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -162,90 +162,91 @@ export const adPlacesRulesDictionarySchema: IObjectSchema<Record<string, AdPlace
162162
adPlacesRulesSchema
163163
).required();
164164

165-
const permanentAdPlacesRulesSchema = arraySchema()
166-
.of(
167-
objectSchema()
165+
const permanentAdPlacesRuleSchema: IObjectSchema<PermanentAdPlacesRule> = objectSchema()
166+
.shape({
167+
urlRegexes: arraySchema().of(regexStringSchema.clone().required()).required(),
168+
adSelector: objectSchema()
168169
.shape({
169-
urlRegexes: arraySchema().of(regexStringSchema.clone().required()).required(),
170-
adSelector: objectSchema()
171-
.shape({
172-
isMultiple: booleanSchema().required(),
173-
cssString: cssSelectorSchema.clone().required(),
174-
parentDepth: numberSchema().integer().min(0).required()
175-
})
176-
.required(),
177-
parentSelector: objectSchema()
178-
.shape({
179-
isMultiple: booleanSchema().required(),
180-
cssString: cssSelectorSchema.clone().required(),
181-
parentDepth: numberSchema().integer().min(0).required()
182-
})
183-
.required(),
184-
insertionIndex: numberSchema().integer(),
185-
insertBeforeSelector: cssSelectorSchema,
186-
insertAfterSelector: cssSelectorSchema,
187-
insertionsCount: numberSchema().integer().min(1),
188-
shouldUseDivWrapper: booleanSchema(),
189-
wrapperType: stringSchema().oneOf(['div', 'tbody']),
190-
colsBefore: numberSchema().integer().min(0),
191-
colspan: numberSchema().integer().min(1),
192-
colsAfter: numberSchema().integer().min(0),
193-
elementStyle: styleSchema,
194-
divWrapperStyle: styleSchema,
195-
wrapperStyle: styleSchema,
196-
elementToMeasureSelector: cssSelectorSchema,
197-
elementsToMeasureSelectors: objectSchema()
198-
.shape({ width: cssSelectorSchema.clone(), height: cssSelectorSchema.clone() })
199-
.test('all-fields-present', function (value: unknown) {
200-
if (!value || typeof value !== 'object') {
201-
return true;
202-
}
203-
204-
if (typeof (value as any).width === 'string' && typeof (value as any).height === 'string') {
205-
return true;
206-
}
207-
208-
throw this.createError({ path: this.path, message: 'Both `width` and `height` fields must be specified' });
209-
})
210-
.default(undefined) as unknown as IObjectSchema<{ width: string; height: string } | undefined>,
211-
stylesOverrides: arraySchema().of(adStylesOverridesSchema.clone().required()),
212-
shouldHideOriginal: booleanSchema(),
213-
extVersion: versionRangeSchema.clone().required(),
214-
displayWidth: versionRangeSchema.clone().test('valid-boundary-values', (value: string | undefined) => {
215-
if (!isDefined(value) || value.length === 0) {
216-
return true;
217-
}
218-
219-
const nonIntegerNumberMatches = value.match(/\d+\.\d+/g);
220-
if (isDefined(nonIntegerNumberMatches)) {
221-
throw new Error('Display width must be an integer');
222-
}
223-
224-
return true;
225-
})
170+
isMultiple: booleanSchema().required(),
171+
cssString: cssSelectorSchema.clone().required(),
172+
parentDepth: numberSchema().integer().min(0).required()
226173
})
227-
.test('insertion-place-specified', (value: PermanentAdPlacesRule | undefined) => {
228-
if (!value) {
174+
.required(),
175+
parentSelector: objectSchema()
176+
.shape({
177+
isMultiple: booleanSchema().required(),
178+
cssString: cssSelectorSchema.clone().required(),
179+
parentDepth: numberSchema().integer().min(0).required()
180+
})
181+
.required(),
182+
insertionIndex: numberSchema().integer(),
183+
insertBeforeSelector: cssSelectorSchema,
184+
insertAfterSelector: cssSelectorSchema,
185+
insertionsCount: numberSchema().integer().min(1),
186+
shouldUseDivWrapper: booleanSchema(),
187+
wrapperType: stringSchema().oneOf(['div', 'tbody']),
188+
colsBefore: numberSchema().integer().min(0),
189+
colspan: numberSchema().integer().min(1),
190+
colsAfter: numberSchema().integer().min(0),
191+
elementStyle: styleSchema,
192+
divWrapperStyle: styleSchema,
193+
wrapperStyle: styleSchema,
194+
elementToMeasureSelector: cssSelectorSchema,
195+
elementsToMeasureSelectors: objectSchema()
196+
.shape({ width: cssSelectorSchema.clone(), height: cssSelectorSchema.clone() })
197+
.test('all-fields-present', function (value: unknown) {
198+
if (!value || typeof value !== 'object') {
229199
return true;
230200
}
231201

232-
const { insertionIndex, insertBeforeSelector, insertAfterSelector } = value;
233-
const definedValuesCount = [insertionIndex, insertBeforeSelector, insertAfterSelector].filter(isDefined).length;
234-
235-
if (definedValuesCount !== 1) {
236-
throw new Error(
237-
'Exactly one of insertionIndex, insertBeforeSelector and insertAfterSelector must be specified'
238-
);
202+
if (typeof (value as any).width === 'string' && typeof (value as any).height === 'string') {
203+
return true;
239204
}
240205

241-
return true;
206+
throw this.createError({ path: this.path, message: 'Both `width` and `height` fields must be specified' });
242207
})
243-
.required()
244-
)
208+
.default(undefined) as unknown as IObjectSchema<{ width: string; height: string } | undefined>,
209+
stylesOverrides: arraySchema().of(adStylesOverridesSchema.clone().required()),
210+
shouldHideOriginal: booleanSchema(),
211+
extVersion: versionRangeSchema.clone().required(),
212+
displayWidth: versionRangeSchema.clone().test('valid-boundary-values', (value: string | undefined) => {
213+
if (!isDefined(value) || value.length === 0) {
214+
return true;
215+
}
216+
217+
const nonIntegerNumberMatches = value.match(/\d+\.\d+/g);
218+
if (isDefined(nonIntegerNumberMatches)) {
219+
throw new Error('Display width must be an integer');
220+
}
221+
222+
return true;
223+
}),
224+
supportsTheming: booleanSchema().default(false),
225+
fontSampleSelector: cssSelectorSchema.clone(),
226+
enableForMises: booleanSchema().default(true),
227+
enableForNonMises: booleanSchema().default(true)
228+
})
229+
.test('insertion-place-specified', (value: PermanentAdPlacesRule | undefined) => {
230+
if (!value) {
231+
return true;
232+
}
233+
234+
const { insertionIndex, insertBeforeSelector, insertAfterSelector } = value;
235+
const definedValuesCount = [insertionIndex, insertBeforeSelector, insertAfterSelector].filter(isDefined).length;
236+
237+
if (definedValuesCount !== 1) {
238+
throw new Error('Exactly one of insertionIndex, insertBeforeSelector and insertAfterSelector must be specified');
239+
}
240+
241+
return true;
242+
})
245243
.required();
246244

247245
export const permanentAdPlacesRulesDictionarySchema: IObjectSchema<Record<string, PermanentAdPlacesRule[]>> =
248-
makeDictionarySchema(hostnameSchema, permanentAdPlacesRulesSchema).required();
246+
makeDictionarySchema(
247+
hostnameSchema,
248+
arraySchema().of(permanentAdPlacesRuleSchema.clone().required()).required()
249+
).required();
249250

250251
const adProvidersByDomainRulesSchema = arraySchema()
251252
.of(
@@ -267,7 +268,8 @@ const adProvidersSelectorsRuleSchema: IObjectSchema<AdProviderSelectorsRule> = o
267268
negativeSelectors: cssSelectorsListSchema.clone(),
268269
extVersion: versionRangeSchema.clone().required(),
269270
parentDepth: numberSchema().integer().min(0).default(0),
270-
enableForMises: booleanSchema().default(true)
271+
enableForMises: booleanSchema().default(true),
272+
enableForNonMises: booleanSchema().default(true)
271273
});
272274

273275
export const adProvidersDictionarySchema = makeDictionarySchema<AdProviderSelectorsRule[]>(

0 commit comments

Comments
 (0)