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

MobianRtdModule: Add more signals from API endpoint to first-party data #11999

1 change: 1 addition & 0 deletions modules/.submodules.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
"jwplayerRtdProvider",
"medianetRtdProvider",
"mgidRtdProvider",
"mobianRtdProvider",
"neuwoRtdProvider",
"oneKeyRtdProvider",
"optimeraRtdProvider",
Expand Down
52 changes: 39 additions & 13 deletions modules/mobianRtdProvider.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
/**
* This module adds the Mobian RTD provider to the real time data module
* The {@link module:modules/realTimeData} module is required
* @module modules/anonymisedRtdProvider
* @requires module:modules/realTimeData
*/
import { submodule } from '../src/hook.js';
import { ajaxBuilder } from '../src/ajax.js';
import { deepSetValue } from '../src/utils.js';
import { deepSetValue, safeJSONParse } from '../src/utils.js';

/**
* @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule
Expand All @@ -24,6 +22,7 @@ export const mobianBrandSafetySubmodule = {
function init() {
return true;
}

function getBidRequestData(bidReqConfig, callback, config) {
const { site: ortb2Site } = bidReqConfig.ortb2Fragments.global;
const pageUrl = encodeURIComponent(getPageUrl());
Expand All @@ -33,20 +32,47 @@ function getBidRequestData(bidReqConfig, callback, config) {

return new Promise((resolve) => {
ajax(requestUrl, {
success: function(response) {
const risks = ['garm_high_risk', 'garm_medium_risk', 'garm_low_risk', 'garm_no_risk'];
const riskLevels = ['high_risk', 'medium_risk', 'low_risk', 'no_risk'];

let mobianGarmRisk = 'unknown';
for (let i = 0; i < risks.length; i++) {
if (response[risks[i]]) {
mobianGarmRisk = riskLevels[i];
break;
success: function(responseData) {
let response = safeJSONParse(responseData);
if (!response) {
resolve({});
callback();
return;
}

let mobianGarmRisk = response.garm_risk || 'unknown';

if (mobianGarmRisk === 'unknown') {
const risks = ['garm_high_risk', 'garm_medium_risk', 'garm_low_risk', 'garm_no_risk'];
const riskLevels = ['high', 'medium', 'low', 'none'];

for (let i = 0; i < risks.length; i++) {
if (response[risks[i]]) {
mobianGarmRisk = riskLevels[i];
break;
}
}
}

const garmCategories = Object.keys(response)
.filter(key => key.startsWith('garm_content_category_') && response[key])
.map(key => key.replace('garm_content_category_', ''));

const sentiment = Object.keys(response)
.find(key => key.startsWith('sentiment_') && response[key])
?.replace('sentiment_', '') || 'unknown';

const emotions = Object.keys(response)
.filter(key => key.startsWith('emotion_') && response[key])
.map(key => key.replace('emotion_', ''));

const risk = {
'mobianGarmRisk': mobianGarmRisk
'mobianGarmRisk': mobianGarmRisk,
'garmContentCategories': garmCategories,
'sentiment': sentiment,
'emotions': emotions
};

resolve(risk);
deepSetValue(ortb2Site.ext, 'data.mobian', risk);
callback()
Expand Down
85 changes: 68 additions & 17 deletions test/spec/modules/mobianRtdProvider_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,81 +25,132 @@ describe('Mobian RTD Submodule', function () {

it('should return no_risk when server responds with garm_no_risk', function () {
ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) {
callbacks.success({
callbacks.success(JSON.stringify({
garm_no_risk: true,
garm_low_risk: false,
garm_medium_risk: false,
garm_high_risk: false
});
}));
});

return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, {}, {}).then((risk) => {
expect(risk).to.have.property('mobianGarmRisk');
expect(risk['mobianGarmRisk']).to.equal('no_risk');
expect(risk['mobianGarmRisk']).to.equal('none');
expect(bidReqConfig.ortb2Fragments.global.site.ext.data.mobian).to.deep.equal(risk);
});
});

it('should return low_risk when server responds with garm_no_risk', function () {
it('should return low_risk when server responds with garm_low_risk', function () {
ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) {
callbacks.success({
callbacks.success(JSON.stringify({
garm_no_risk: false,
garm_low_risk: true,
garm_medium_risk: false,
garm_high_risk: false
});
}));
});

return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, {}, {}).then((risk) => {
expect(risk).to.have.property('mobianGarmRisk');
expect(risk['mobianGarmRisk']).to.equal('low_risk');
expect(risk['mobianGarmRisk']).to.equal('low');
expect(bidReqConfig.ortb2Fragments.global.site.ext.data.mobian).to.deep.equal(risk);
});
});

it('should return medium_risk when server responds with garm_medium_risk', function () {
ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) {
callbacks.success({
callbacks.success(JSON.stringify({
garm_no_risk: false,
garm_low_risk: false,
garm_medium_risk: true,
garm_high_risk: false
});
}));
});

return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, {}, {}).then((risk) => {
expect(risk).to.have.property('mobianGarmRisk');
expect(risk['mobianGarmRisk']).to.equal('medium_risk');
expect(risk['mobianGarmRisk']).to.equal('medium');
expect(bidReqConfig.ortb2Fragments.global.site.ext.data.mobian).to.deep.equal(risk);
});
});

it('should return high_risk when server responds with garm_high_risk', function () {
ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) {
callbacks.success({
callbacks.success(JSON.stringify({
garm_no_risk: false,
garm_low_risk: false,
garm_medium_risk: false,
garm_high_risk: true
});
}));
});

return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, {}, {}).then((risk) => {
expect(risk).to.have.property('mobianGarmRisk');
expect(risk['mobianGarmRisk']).to.equal('high_risk');
expect(risk['mobianGarmRisk']).to.equal('high');
expect(bidReqConfig.ortb2Fragments.global.site.ext.data.mobian).to.deep.equal(risk);
});
});

it('should return unknown when server response is not of the expected shape', function () {
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((risk) => {
expect(risk).to.deep.equal({});
// Check that bidReqConfig hasn't been modified
expect(bidReqConfig).to.deep.equal(originalConfig);
});
});

it('should handle response with direct garm_risk field', function () {
ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) {
callbacks.success(JSON.stringify({
garm_risk: 'low',
sentiment_positive: true,
emotion_joy: true
}));
});

return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, {}, {}).then((risk) => {
expect(risk).to.have.property('mobianGarmRisk');
expect(risk['mobianGarmRisk']).to.equal('unknown');
expect(bidReqConfig.ortb2Fragments.global.site.ext.data.mobian).to.deep.equal(risk);
expect(risk).to.deep.equal({
mobianGarmRisk: 'low',
garmContentCategories: [],
sentiment: 'positive',
emotions: ['joy']
});
});
});

it('should handle response with GARM content categories, sentiment, and emotions', function () {
ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) {
callbacks.success(JSON.stringify({
garm_risk: 'medium',
garm_content_category_arms: true,
garm_content_category_crime: true,
sentiment_negative: true,
emotion_anger: true,
emotion_fear: true
}));
});

return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, {}, {}).then((risk) => {
expect(risk).to.deep.equal({
mobianGarmRisk: 'medium',
garmContentCategories: ['arms', 'crime'],
sentiment: 'negative',
emotions: ['anger', 'fear']
});
});
});

it('should handle error response', function () {
ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) {
callbacks.error();
});

return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, {}, {}).then((risk) => {
expect(risk).to.deep.equal({});
});
});
});