From f7e8034b45c661a0d2f242584341894932a2b8ad Mon Sep 17 00:00:00 2001 From: Ariel <155993606+arielmtk@users.noreply.github.com> Date: Mon, 2 Dec 2024 14:54:40 -0300 Subject: [PATCH] Mobian Bid Adapter : push context data to GAM (#12389) * Push context data to GAM * Update browsi to set gpt key values * fix browsi * Revamps module to make it configurable * Revamps module and tests, adds config * Adds more config and documentation * Updates mock emotion --------- Co-authored-by: Demetrio Girardi --- libraries/gptUtils/gptUtils.js | 12 + modules/browsiRtdProvider.js | 10 +- modules/mobianRtdProvider.js | 241 ++++++++---- modules/mobianRtdProvider.md | 34 +- test/spec/modules/mobianRtdProvider_spec.js | 393 +++++++++----------- 5 files changed, 406 insertions(+), 284 deletions(-) diff --git a/libraries/gptUtils/gptUtils.js b/libraries/gptUtils/gptUtils.js index 68ce29ef168..923d207c0d9 100644 --- a/libraries/gptUtils/gptUtils.js +++ b/libraries/gptUtils/gptUtils.js @@ -11,6 +11,18 @@ export function isSlotMatchingAdUnitCode(adUnitCode) { return (slot) => compareCodeAndSlot(slot, adUnitCode); } +/** + * @summary Export a k-v pair to GAM + */ +export function setKeyValue(key, value) { + if (!key || typeof key !== 'string') return false; + window.googletag = window.googletag || {cmd: []}; + window.googletag.cmd = window.googletag.cmd || []; + window.googletag.cmd.push(() => { + window.googletag.pubads().setTargeting(key, value); + }); +} + /** * @summary Uses the adUnit's code in order to find a matching gpt slot object on the page */ diff --git a/modules/browsiRtdProvider.js b/modules/browsiRtdProvider.js index 8f5fea80ffa..6ac19b39c79 100644 --- a/modules/browsiRtdProvider.js +++ b/modules/browsiRtdProvider.js @@ -26,6 +26,7 @@ import {getGlobal} from '../src/prebidGlobal.js'; import * as events from '../src/events.js'; import {EVENTS} from '../src/constants.js'; import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; +import {setKeyValue as setGptKeyValue} from '../libraries/gptUtils/gptUtils.js'; /** * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule @@ -67,14 +68,7 @@ export function addBrowsiTag(data) { return script; } -export function setKeyValue(key) { - if (!key || typeof key !== 'string') return false; - window.googletag = window.googletag || {cmd: []}; - window.googletag.cmd = window.googletag.cmd || []; - window.googletag.cmd.push(() => { - window.googletag.pubads().setTargeting(key, RANDOM.toString()); - }); -} +export const setKeyValue = (key) => setGptKeyValue(key, RANDOM.toString()); export function sendPageviewEvent(eventType) { if (eventType === 'PAGEVIEW') { diff --git a/modules/mobianRtdProvider.js b/modules/mobianRtdProvider.js index 3b9d2632246..02f2d1b83cf 100644 --- a/modules/mobianRtdProvider.js +++ b/modules/mobianRtdProvider.js @@ -4,85 +4,200 @@ */ import { submodule } from '../src/hook.js'; import { ajaxBuilder } from '../src/ajax.js'; -import { deepSetValue, safeJSONParse } from '../src/utils.js'; +import { safeJSONParse, logMessage as _logMessage } from '../src/utils.js'; +import { setKeyValue } from '../libraries/gptUtils/gptUtils.js'; /** * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule */ +/** + * @typedef {Object} MobianConfig + * @property {MobianConfigParams} params + */ + +/** + * @typedef {Object} MobianConfigParams + * @property {string} [prefix] - Optional prefix for targeting keys (default: 'mobian') + * @property {boolean} [publisherTargeting] - Optional boolean to enable targeting for publishers (default: false) + * @property {boolean} [advertiserTargeting] - Optional boolean to enable targeting for advertisers (default: false) + */ + +/** + * @typedef {Object} MobianContextData + * @property {Object} apValues + * @property {string[]} categories + * @property {string[]} emotions + * @property {string[]} genres + * @property {string} risk + * @property {string} sentiment + * @property {string[]} themes + * @property {string[]} tones + */ + export const MOBIAN_URL = 'https://prebid.outcomes.net/api/prebid/v1/assessment/async'; -/** @type {RtdSubmodule} */ -export const mobianBrandSafetySubmodule = { - name: 'mobianBrandSafety', - init: init, - getBidRequestData: getBidRequestData +export const CONTEXT_KEYS = [ + 'apValues', + 'categories', + 'emotions', + 'genres', + 'risk', + 'sentiment', + 'themes', + 'tones' +]; + +const AP_KEYS = ['a0', 'a1', 'p0', 'p1']; + +const logMessage = (...args) => { + _logMessage('Mobian', ...args); }; -function init() { - return true; +function makeMemoizedFetch() { + let cachedResponse = null; + return async function () { + if (cachedResponse) { + return Promise.resolve(cachedResponse); + } + try { + const response = await fetchContextData(); + cachedResponse = makeDataFromResponse(response); + return cachedResponse; + } catch (error) { + logMessage('error', error); + return Promise.resolve({}); + } + } } -function getBidRequestData(bidReqConfig, callback, config) { - const { site: ortb2Site } = bidReqConfig.ortb2Fragments.global; - const pageUrl = encodeURIComponent(getPageUrl()); +export const getContextData = makeMemoizedFetch(); + +export async function fetchContextData() { + const pageUrl = encodeURIComponent(window.location.href); const requestUrl = `${MOBIAN_URL}?url=${pageUrl}`; + const request = ajaxBuilder(); + + return new Promise((resolve, reject) => { + request(requestUrl, { success: resolve, error: reject }); + }); +} + +export function getConfig(config) { + const [advertiserTargeting, publisherTargeting] = ['advertiserTargeting', 'publisherTargeting'].map((key) => { + const value = config?.params?.[key]; + if (!value) { + return []; + } else if (value === true) { + return CONTEXT_KEYS; + } else if (Array.isArray(value) && value.length) { + return value.filter((key) => CONTEXT_KEYS.includes(key)); + } + return []; + }); + + const prefix = config?.params?.prefix || 'mobian'; + return { advertiserTargeting, prefix, publisherTargeting }; +} + +/** + * @param {MobianConfigParams} parsedConfig + * @param {MobianContextData} contextData + * @returns {function} + */ +export function setTargeting(parsedConfig, contextData) { + const { publisherTargeting, prefix } = parsedConfig; + logMessage('context', contextData); + + CONTEXT_KEYS.forEach((key) => { + if (!publisherTargeting.includes(key)) return; - const ajax = ajaxBuilder(); - - return new Promise((resolve) => { - ajax(requestUrl, { - success: function(responseData) { - let response = safeJSONParse(responseData); - if (!response || !response.meta.has_results) { - resolve({}); - callback(); - return; - } - - const results = response.results; - const mobianRisk = results.mobianRisk || 'unknown'; - const contentCategories = results.mobianContentCategories || []; - const sentiment = results.mobianSentiment || 'unknown'; - const emotions = results.mobianEmotions || []; - const themes = results.mobianThemes || []; - const tones = results.mobianTones || []; - const genres = results.mobianGenres || []; - const apValues = results.ap || {}; - - const risk = { - risk: mobianRisk, - contentCategories: contentCategories, - sentiment: sentiment, - emotions: emotions, - themes: themes, - tones: tones, - genres: genres, - apValues: apValues, - }; - - deepSetValue(ortb2Site, 'ext.data.mobianRisk', mobianRisk); - deepSetValue(ortb2Site, 'ext.data.mobianContentCategories', contentCategories); - deepSetValue(ortb2Site, 'ext.data.mobianSentiment', sentiment); - deepSetValue(ortb2Site, 'ext.data.mobianEmotions', emotions); - deepSetValue(ortb2Site, 'ext.data.mobianThemes', themes); - deepSetValue(ortb2Site, 'ext.data.mobianTones', tones); - deepSetValue(ortb2Site, 'ext.data.mobianGenres', genres); - deepSetValue(ortb2Site, 'ext.data.apValues', apValues); - - resolve(risk); - callback(); - }, - error: function () { - resolve({}); - callback(); - } - }); + if (key === 'apValues') { + AP_KEYS.forEach((apKey) => { + if (!contextData[key]?.[apKey]?.length) return; + logMessage(`${prefix}_ap_${apKey}`, contextData[key][apKey]); + setKeyValue(`${prefix}_ap_${apKey}`, contextData[key][apKey]); + }); + return; + } + + if (contextData[key]?.length) { + logMessage(`${prefix}_${key}`, contextData[key]); + setKeyValue(`${prefix}_${key}`, contextData[key]); + } }); } -function getPageUrl() { - return window.location.href; +export function makeDataFromResponse(contextData) { + const data = typeof contextData === 'string' ? safeJSONParse(contextData) : contextData; + const results = data.results; + if (!results) { + return {}; + } + return { + apValues: results.ap || {}, + categories: results.mobianContentCategories, + emotions: results.mobianEmotions, + genres: results.mobianGenres, + risk: results.mobianRisk || 'unknown', + sentiment: results.mobianSentiment || 'unknown', + themes: results.mobianThemes, + tones: results.mobianTones, + }; +} + +export function extendBidRequestConfig(bidReqConfig, contextData) { + logMessage('extendBidRequestConfig', bidReqConfig, contextData); + const { site: ortb2Site } = bidReqConfig.ortb2Fragments.global; + + ortb2Site.ext = ortb2Site.ext || {}; + ortb2Site.ext.data = { + ...(ortb2Site.ext.data || {}), + ...contextData + }; + + return bidReqConfig; } +/** + * @param {MobianConfig} config + * @returns {boolean} + */ +function init(config) { + logMessage('init', config); + + const parsedConfig = getConfig(config); + + if (parsedConfig.publisherTargeting.length) { + getContextData().then((contextData) => setTargeting(parsedConfig, contextData)); + } + + return true; +} + +function getBidRequestData(bidReqConfig, callback, config) { + logMessage('getBidRequestData', bidReqConfig); + + const { advertiserTargeting } = getConfig(config); + + if (!advertiserTargeting.length) { + callback(); + return; + } + + getContextData() + .then((contextData) => { + extendBidRequestConfig(bidReqConfig, contextData); + }) + .catch(() => {}) + .finally(() => callback()); +} + +/** @type {RtdSubmodule} */ +export const mobianBrandSafetySubmodule = { + name: 'mobianBrandSafety', + init: init, + getBidRequestData: getBidRequestData +}; + submodule('realTimeData', mobianBrandSafetySubmodule); diff --git a/modules/mobianRtdProvider.md b/modules/mobianRtdProvider.md index e7475eb40fb..e974e56dcdd 100644 --- a/modules/mobianRtdProvider.md +++ b/modules/mobianRtdProvider.md @@ -1,11 +1,41 @@ -# Overview +# Mobian Rtd Provider + +## Overview Module Name: Mobian Rtd Provider Module Type: Rtd Provider Maintainer: rich.rodriguez@themobian.com -# Description +## Description RTD provider for themobian Brand Safety determinations. Publishers should use this to get Mobian's GARM Risk evaluations for a URL. + +## Configuration + +```js +pbjs.setConfig({ + realTimeData: { + dataProviders: [{ + name: 'mobianBrandSafety', + params: { + // Prefix for the targeting keys (default: 'mobian') + prefix: 'mobian', + + // Enable targeting keys for advertiser data + advertiserTargeting: true, + // Or set it as an array to pick specific targeting keys: + // advertiserTargeting: ['genres', 'emotions', 'themes'], + // Available values: 'apValues', 'categories', 'emotions', 'genres', 'risk', 'sentiment', 'themes', 'tones' + + // Enable targeting keys for publisher data + publisherTargeting: true, + // Or set it as an array to pick specific targeting keys: + // publisherTargeting: ['tones', 'risk'], + // Available values: 'apValues', 'categories', 'emotions', 'genres', 'risk', 'sentiment', 'themes', 'tones' + } + }] + } +}); +``` diff --git a/test/spec/modules/mobianRtdProvider_spec.js b/test/spec/modules/mobianRtdProvider_spec.js index 088e5559a17..796a79e4e1c 100644 --- a/test/spec/modules/mobianRtdProvider_spec.js +++ b/test/spec/modules/mobianRtdProvider_spec.js @@ -1,11 +1,49 @@ import { expect } from 'chai'; import sinon from 'sinon'; -import { mobianBrandSafetySubmodule, MOBIAN_URL } from 'modules/mobianRtdProvider.js'; import * as ajax from 'src/ajax.js'; +import * as gptUtils from 'libraries/gptUtils/gptUtils.js'; +import { + CONTEXT_KEYS, + extendBidRequestConfig, + fetchContextData, + getConfig, + getContextData, + makeDataFromResponse, + setTargeting, +} from 'modules/mobianRtdProvider.js'; describe('Mobian RTD Submodule', function () { let ajaxStub; let bidReqConfig; + let setKeyValueSpy; + + const mockResponse = JSON.stringify({ + meta: { + url: 'https://example.com', + has_results: true + }, + results: { + ap: { a0: [], a1: [2313, 12], p0: [1231231, 212], p1: [231, 419] }, + mobianContentCategories: [], + mobianEmotions: ['affection'], + mobianGenres: [], + mobianRisk: 'low', + mobianSentiment: 'positive', + mobianThemes: [], + mobianTones: [], + } + }); + + const mockContextData = { + apValues: { a0: [], a1: [2313, 12], p0: [1231231, 212], p1: [231, 419] }, + categories: [], + emotions: ['affection'], + genres: [], + risk: 'low', + sentiment: 'positive', + themes: [], + tones: [], + } beforeEach(function () { bidReqConfig = { @@ -19,255 +57,188 @@ describe('Mobian RTD Submodule', function () { } } }; + + setKeyValueSpy = sinon.spy(gptUtils, 'setKeyValue'); }); afterEach(function () { ajaxStub.restore(); + setKeyValueSpy.restore(); }); - it('should set key-value pairs when server responds with valid data', function () { - ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) { - callbacks.success(JSON.stringify({ - meta: { - url: 'https://example.com', - has_results: true - }, - results: { - mobianRisk: 'low', - mobianSentiment: 'positive', - mobianContentCategories: [], - mobianEmotions: ['joy'], - mobianThemes: [], - mobianTones: [], - mobianGenres: [], - ap: { a0: [], a1: [2313, 12], p0: [1231231, 212], p1: [231, 419] } - } - })); - }); - - return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, {}, {}).then((result) => { - expect(result).to.deep.equal({ - risk: 'low', - contentCategories: [], - sentiment: 'positive', - emotions: ['joy'], - themes: [], - tones: [], - genres: [], - apValues: { a0: [], a1: [2313, 12], p0: [1231231, 212], p1: [231, 419] } - }); - expect(bidReqConfig.ortb2Fragments.global.site.ext.data).to.deep.include({ - mobianRisk: 'low', - mobianContentCategories: [], - mobianSentiment: 'positive', - mobianEmotions: ['joy'], - mobianThemes: [], - mobianTones: [], - mobianGenres: [], - apValues: { a0: [], a1: [2313, 12], p0: [1231231, 212], p1: [231, 419] } + describe('fetchContextData', function () { + it('should return fetched context data', async function () { + ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) { + callbacks.success(mockResponse); }); - }); - }); - it('should handle response with content categories, multiple emotions, and ap values', function () { - ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) { - callbacks.success(JSON.stringify({ - meta: { - url: 'https://example.com', - has_results: true - }, - results: { - mobianRisk: 'medium', - mobianSentiment: 'negative', - mobianContentCategories: ['arms', 'crime'], - mobianEmotions: ['anger', 'fear'], - mobianThemes: ['conflict', 'international relations'], - mobianTones: ['factual', 'serious'], - mobianGenres: ['news', 'political_analysis'], - ap: { a0: [100], a1: [200, 300], p0: [400, 500], p1: [600] } - } - })); + const contextData = await fetchContextData(); + expect(contextData).to.deep.equal(mockResponse); }); + }); - return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, {}, {}).then((result) => { - expect(result).to.deep.equal({ - risk: 'medium', - contentCategories: ['arms', 'crime'], - sentiment: 'negative', - emotions: ['anger', 'fear'], - themes: ['conflict', 'international relations'], - tones: ['factual', 'serious'], - genres: ['news', 'political_analysis'], - apValues: { a0: [100], a1: [200, 300], p0: [400, 500], p1: [600] } - }); - expect(bidReqConfig.ortb2Fragments.global.site.ext.data).to.deep.include({ - mobianRisk: 'medium', - mobianContentCategories: ['arms', 'crime'], - mobianSentiment: 'negative', - mobianEmotions: ['anger', 'fear'], - mobianThemes: ['conflict', 'international relations'], - mobianTones: ['factual', 'serious'], - mobianGenres: ['news', 'political_analysis'], - apValues: { a0: [100], a1: [200, 300], p0: [400, 500], p1: [600] } + describe('makeDataFromResponse', function () { + it('should format context data response', async function () { + ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) { + callbacks.success(mockResponse); }); + + const data = makeDataFromResponse(mockResponse); + expect(data).to.deep.equal(mockContextData); }); }); - it('should return empty object when server responds with has_results: false', function () { - ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) { - callbacks.success(JSON.stringify({ - meta: { - url: 'https://example.com', - has_results: false - }, - results: {} - })); - }); + describe('getContextData', function () { + it('should return formatted context data', async function () { + ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) { + callbacks.success(mockResponse); + }); - return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, {}, {}).then((result) => { - expect(result).to.deep.equal({}); - expect(bidReqConfig.ortb2Fragments.global.site.ext.data).to.not.have.any.keys( - 'mobianRisk', 'mobianContentCategories', 'mobianSentiment', 'mobianEmotions', 'mobianThemes', 'mobianTones', 'mobianGenres', 'apValues' - ); + const data = await getContextData(); + expect(data).to.deep.equal(mockContextData); }); }); - it('should return empty object when server response is not valid JSON', function () { - ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) { - callbacks.success('unexpected output not even of the right type'); - }); - const originalConfig = JSON.parse(JSON.stringify(bidReqConfig)); - return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, {}, {}).then((result) => { - expect(result).to.deep.equal({}); - // Check that bidReqConfig hasn't been modified - expect(bidReqConfig).to.deep.equal(originalConfig); + describe('setTargeting', function () { + it('should set targeting key-value pairs as per config', function () { + const parsedConfig = { + prefix: 'mobian', + publisherTargeting: ['apValues', 'emotions', 'risk', 'sentiment', 'themes', 'tones', 'genres'], + }; + setTargeting(parsedConfig, mockContextData); + + expect(setKeyValueSpy.callCount).to.equal(6); + expect(setKeyValueSpy.calledWith('mobian_ap_a1', [2313, 12])).to.equal(true); + expect(setKeyValueSpy.calledWith('mobian_ap_p0', [1231231, 212])).to.equal(true); + expect(setKeyValueSpy.calledWith('mobian_ap_p1', [231, 419])).to.equal(true); + expect(setKeyValueSpy.calledWith('mobian_emotions', ['affection'])).to.equal(true); + expect(setKeyValueSpy.calledWith('mobian_risk', 'low')).to.equal(true); + expect(setKeyValueSpy.calledWith('mobian_sentiment', 'positive')).to.equal(true); + + expect(setKeyValueSpy.calledWith('mobian_ap_a0')).to.equal(false); + expect(setKeyValueSpy.calledWith('mobian_themes')).to.equal(false); + expect(setKeyValueSpy.calledWith('mobian_tones')).to.equal(false); + expect(setKeyValueSpy.calledWith('mobian_genres')).to.equal(false); + }); + + it('should not set key-value pairs if context data is empty', function () { + const parsedConfig = { + prefix: 'mobian', + publisherTargeting: ['apValues', 'emotions', 'risk', 'sentiment', 'themes', 'tones', 'genres'], + }; + setTargeting(parsedConfig, {}); + + expect(setKeyValueSpy.callCount).to.equal(0); + }); + + it('should only set key-value pairs for the keys specified in config', function () { + const parsedConfig = { + prefix: 'mobian', + publisherTargeting: ['emotions', 'risk'], + }; + + setTargeting(parsedConfig, mockContextData); + + expect(setKeyValueSpy.callCount).to.equal(2); + expect(setKeyValueSpy.calledWith('mobian_emotions', ['affection'])).to.equal(true); + expect(setKeyValueSpy.calledWith('mobian_risk', 'low')).to.equal(true); + + expect(setKeyValueSpy.calledWith('mobian_ap_a0')).to.equal(false); + expect(setKeyValueSpy.calledWith('mobian_ap_a1')).to.equal(false); + expect(setKeyValueSpy.calledWith('mobian_ap_p0')).to.equal(false); + expect(setKeyValueSpy.calledWith('mobian_ap_p1')).to.equal(false); + expect(setKeyValueSpy.calledWith('mobian_themes')).to.equal(false); + expect(setKeyValueSpy.calledWith('mobian_tones')).to.equal(false); + expect(setKeyValueSpy.calledWith('mobian_genres')).to.equal(false); }); }); - it('should handle error response', function () { - ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) { - callbacks.error(); + describe('extendBidRequestConfig', function () { + it('should extend bid request config with context data', function () { + const extendedConfig = extendBidRequestConfig(bidReqConfig, mockContextData); + expect(extendedConfig.ortb2Fragments.global.site.ext.data).to.deep.equal(mockContextData); }); - return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, {}, {}).then((result) => { - expect(result).to.deep.equal({}); - }); - }); + it('should not override existing data', function () { + bidReqConfig.ortb2Fragments.global.site.ext.data = { + existing: 'data' + }; - it('should use default values when fields are missing in the response', function () { - ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) { - callbacks.success(JSON.stringify({ - meta: { - url: 'https://example.com', - has_results: true - }, - results: { - mobianRisk: 'high' - // Missing other fields - } - })); + const extendedConfig = extendBidRequestConfig(bidReqConfig, mockContextData); + expect(extendedConfig.ortb2Fragments.global.site.ext.data).to.deep.equal({ + existing: 'data', + ...mockContextData + }); }); - return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, {}, {}).then((result) => { - expect(result).to.deep.equal({ - risk: 'high', - contentCategories: [], - sentiment: 'unknown', - emotions: [], - themes: [], - tones: [], - genres: [], - apValues: {} - }); - expect(bidReqConfig.ortb2Fragments.global.site.ext.data).to.deep.include({ - mobianRisk: 'high', - mobianContentCategories: [], - mobianSentiment: 'unknown', - mobianEmotions: [], - mobianThemes: [], - mobianTones: [], - mobianGenres: [], - apValues: {} - }); + it('should create data object if missing', function () { + delete bidReqConfig.ortb2Fragments.global.site.ext.data; + const extendedConfig = extendBidRequestConfig(bidReqConfig, mockContextData); + expect(extendedConfig.ortb2Fragments.global.site.ext.data).to.deep.equal(mockContextData); }); }); - it('should handle response with only ap values', function () { - ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) { - callbacks.success(JSON.stringify({ - meta: { - url: 'https://example.com', - has_results: true - }, - results: { - ap: { a0: [1, 2], a1: [3, 4], p0: [5, 6], p1: [7, 8] } + describe('getConfig', function () { + it('should return config with correct keys', function () { + const config = getConfig({ + name: 'mobianBrandSafety', + params: { + prefix: 'mobiantest', + publisherTargeting: ['apValues'], + advertiserTargeting: ['emotions'], } - })); + }); + expect(config).to.deep.equal({ + prefix: 'mobiantest', + publisherTargeting: ['apValues'], + advertiserTargeting: ['emotions'], + }); }); - return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, {}, {}).then((result) => { - expect(result).to.deep.equal({ - risk: 'unknown', - contentCategories: [], - sentiment: 'unknown', - emotions: [], - themes: [], - tones: [], - genres: [], - apValues: { a0: [1, 2], a1: [3, 4], p0: [5, 6], p1: [7, 8] } + it('should set default values for configs not set', function () { + const config = getConfig({ + name: 'mobianBrandSafety', + params: { + publisherTargeting: ['apValues'], + } }); - expect(bidReqConfig.ortb2Fragments.global.site.ext.data).to.deep.include({ - mobianRisk: 'unknown', - mobianContentCategories: [], - mobianSentiment: 'unknown', - mobianEmotions: [], - mobianThemes: [], - mobianTones: [], - mobianGenres: [], - apValues: { a0: [1, 2], a1: [3, 4], p0: [5, 6], p1: [7, 8] } + expect(config).to.deep.equal({ + prefix: 'mobian', + publisherTargeting: ['apValues'], + advertiserTargeting: [], }); }); - }); - it('should set key-value pairs when ext object is missing', function () { - bidReqConfig = { - ortb2Fragments: { - global: { - site: {} - } - } - }; + it('should set default values if not provided', function () { + const config = getConfig({}); + expect(config).to.deep.equal({ + prefix: 'mobian', + publisherTargeting: [], + advertiserTargeting: [], + }); + }); - ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) { - callbacks.success(JSON.stringify({ - meta: { - url: 'https://example.com', - has_results: true - }, - results: { - mobianRisk: 'low', - mobianSentiment: 'positive', - mobianContentCategories: [], - mobianEmotions: ['joy'], - mobianThemes: [], - mobianTones: [], - mobianGenres: [], - ap: { a0: [], a1: [2313, 12], p0: [1231231, 212], p1: [231, 419] } - } - })); + it('should set default values if no config is provided', function () { + const config = getConfig(); + expect(config).to.deep.equal({ + prefix: 'mobian', + publisherTargeting: [], + advertiserTargeting: [], + }); }); - return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, () => {}, {}).then(() => { - expect(bidReqConfig.ortb2Fragments.global.site.ext).to.exist; - expect(bidReqConfig.ortb2Fragments.global.site.ext.data).to.deep.include({ - mobianRisk: 'low', - mobianContentCategories: [], - mobianSentiment: 'positive', - mobianEmotions: ['joy'], - mobianThemes: [], - mobianTones: [], - mobianGenres: [], - apValues: { a0: [], a1: [2313, 12], p0: [1231231, 212], p1: [231, 419] } + it('should set all tarteging values if value is true', function () { + const config = getConfig({ + name: 'mobianBrandSafety', + params: { + publisherTargeting: true, + advertiserTargeting: true, + } + }); + expect(config).to.deep.equal({ + prefix: 'mobian', + publisherTargeting: CONTEXT_KEYS, + advertiserTargeting: CONTEXT_KEYS, }); }); });