Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TW-1563 Add specifications for theming native ads #178

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
21 changes: 14 additions & 7 deletions src/advertising/external-ads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ export interface ExtVersionConstraints {
extVersion: string;
}

interface BrowserConstraints {
enableForMises?: boolean;
enableForNonMises?: boolean;
}

export interface AdPlacesRule extends ExtVersionConstraints {
urlRegexes: string[];
selector: {
Expand All @@ -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;
Expand Down Expand Up @@ -137,18 +142,19 @@ export interface PermanentAdPlacesRule extends ExtVersionConstraints {
stylesOverrides?: AdStylesOverrides[];
shouldHideOriginal?: boolean;
displayWidth?: string;
supportsTheming?: boolean;
fontSampleSelector?: string;
}

export interface AdProvidersByDomainRule extends ExtVersionConstraints {
urlRegexes: string[];
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 {
Expand Down Expand Up @@ -217,18 +223,19 @@ export const hypelabCampaignsBlacklistMethods = setStorageMethodsFactory(HYPELAB
const FALLBACK_VERSION = '0.0.0';

export function filterRules<T extends ExtVersionConstraints>(rules: T[], version: string | undefined): T[];
export function filterRules<T extends ExtVersionConstraints & { enableForMises?: boolean }>(
export function filterRules<T extends ExtVersionConstraints & BrowserConstraints>(
rules: T[],
version: string | undefined,
isMisesBrowser: boolean
): T[];
export function filterRules<T extends ExtVersionConstraints & { enableForMises?: boolean }>(
export function filterRules<T extends ExtVersionConstraints & BrowserConstraints>(
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)
);
}
27 changes: 26 additions & 1 deletion src/routers/slise-ad-rules/ad-places.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
} from '../../utils/schemas';

const transformAdPlaces = <T extends ExtVersionConstraints>(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 = <T extends ExtVersionConstraints>(rules: Record<string, T[]>, req: Request) =>
transformValues(rules, value => transformAdPlaces(value, req));

Expand Down Expand Up @@ -306,6 +306,21 @@ const transformAdPlacesDictionary = <T extends ExtVersionConstraints>(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
* enableForMises:
* type: boolean
* default: true
* example:
* urlRegexes:
* - '^https://etherscan\.io/tx/'
Expand Down Expand Up @@ -402,6 +417,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
Expand All @@ -425,6 +445,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
Expand Down
3 changes: 3 additions & 0 deletions src/routers/slise-ad-rules/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ import {
* enableForMises:
* type: boolean
* default: true
* enableForNonMises:
* type: boolean
* default: true
* AdByProviderSelector:
* oneOf:
* - type: string
Expand Down
150 changes: 76 additions & 74 deletions src/utils/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,90 +153,91 @@
adPlacesRulesSchema
).required();

const permanentAdPlacesRulesSchema = arraySchema()
.of(
objectSchema()
const permanentAdPlacesRuleSchema: IObjectSchema<PermanentAdPlacesRule> = 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;
})
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') {

Check warning on line 189 in src/utils/schemas.ts

View workflow job for this annotation

GitHub Actions / Checks if ts and lint works

Unexpected any value in conditional. An explicit comparison or type cast is required
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') {

Check warning on line 193 in src/utils/schemas.ts

View workflow job for this annotation

GitHub Actions / Checks if ts and lint works

Unexpected any. Specify a different type
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<Record<string, PermanentAdPlacesRule[]>> =
makeDictionarySchema(hostnameSchema, permanentAdPlacesRulesSchema).required();
makeDictionarySchema(
hostnameSchema,
arraySchema().of(permanentAdPlacesRuleSchema.clone().required()).required()
).required();

const adProvidersByDomainRulesSchema = arraySchema()
.of(
Expand All @@ -258,7 +259,8 @@
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<AdProviderSelectorsRule[]>(
Expand Down
Loading