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

Overtone RTD Module : initial release #12681

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
79 changes: 79 additions & 0 deletions modules/overtoneRtdProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { submodule } from '../src/hook.js';
import { ajaxBuilder } from '../src/ajax.js';
import { safeJSONParse, logMessage as _logMessage } from '../src/utils.js';

export const OVERTONE_URL = 'https://prebid-1.overtone.ai/contextual';

const logMessage = (...args) => {
_logMessage('Overtone', ...args);
};

export async function fetchContextData(url = window.location.href) {
const pageUrl = encodeURIComponent(url);
const requestUrl = `${OVERTONE_URL}?URL=${pageUrl}&InApp=False`;
const request = window.ajaxBuilder || ajaxBuilder();

return new Promise((resolve, reject) => {
logMessage('Sending request to:', requestUrl);
request(requestUrl, {
success: (response) => {
const data = safeJSONParse(response);
logMessage('Fetched data:', data);

if (!data || typeof data.status !== 'number') {
reject(new Error('Invalid response format'));
return;
}

switch (data.status) {
case 1: // Success
resolve({ categories: data.categories || [] });
break;
case 3: // Fail
case 4: // Ignore
resolve({ categories: [] });
break;
default:
reject(new Error(`Unexpected response status: ${data.status}`));
}
},
error: (err) => {
logMessage('Error during request:', err);
reject(err);
},
});
});
}

function init(config) {
logMessage('init', config);
return true;
}

export const overtoneRtdProvider = {
name: 'overtone',
init: init,
getBidRequestData: function (bidReqConfig, callback) {
fetchContextData()
.then((contextData) => {
if (contextData) {
logMessage('Fetched context data', contextData);
bidReqConfig.ortb2Fragments.global.site.ext = {
...bidReqConfig.ortb2Fragments.global.site.ext,
data: contextData,
};
}
callback();
})
.catch((error) => {
logMessage('Error fetching context data', error);
callback();
});
},
};

submodule('realTimeData', overtoneRtdProvider);

export const overtoneModule = {
fetchContextData,
};
97 changes: 97 additions & 0 deletions modules/overtoneRtdProvider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Overtone Rtd Provider

## Overview

Module Name: Overtone Rtd Provider

Module Type: Rtd Provider

Maintainer: [email protected]

The Overtone Real-Time Data (RTD) Module is a plug-and-play Prebid.js adapter designed to provide contextual classification results on the publisher’s page through Overtone’s contextual API.


## Downloading and Configuring the Overtone RTD Module

Navigate to https://docs.prebid.org/download.html and select the box labeled Overtone Prebid Contextual Evaluation. If Prebid.js is already installed on your site, ensure other necessary modules and adapters are selected. Upon clicking the "Get Prebid.js" button, a customized Prebid.js version will be built with your selections.

Direct link to the Overtone module in the Prebid.js repository:

The client must provide Overtone with all the addresses using the Prebid module to whitelist those domains. Failure to whitelist addresses will result in an invalid request to the Overtone Contextual API.


## Functionality

At a high level, the Overtone RTD Module makes requests to the Overtone Contextual API during page load. It fetches and categorizes content for each page, which is then available for targeting in Prebid.js. Contextual data includes content classifications, which help advertisers make informed decisions about ad placements.


## Available Classifications

Content Categories:

Key: categories

Possible Values: Various identifiers such as ovtn_004, ovtn_104, etc.

Description: Content Categories represent Overtone’s classification of page content based on its contextual analysis.

Please contact [email protected] for more information about our exact categories in brand safety, type, and tone.


## Configuration Highlight

The configuration for the Overtone RTD module in Prebid.js might resemble the following:

pbjs.setConfig({
realTimeData: {
dataProviders: [{
name: 'overtone',
params: {

}
}]
}
});


## API Response Handling

The Overtone RTD module processes responses from the Overtone Contextual API. A typical response might include the following:

Status: Indicates the API request status (1 for success, 3 for fail, 4 for ignore).

Categories: An array of classification identifiers.

For example:

{
"categories": ["ovtn_004", "ovtn_104", "ovtn_309", "ovtn_202"],
"status": 1
}

The module ensures that these values are integrated into Prebid.js’s targeting configuration for the current page.


## Testing and Validation

The functionality of the Overtone RTD module can be validated using the associated test suite provided in overtoneRtdProvider_spec.mjs. The test suite simulates different API response scenarios to verify module behavior under varied conditions.

Example Test Cases:

Successful Data Retrieval:

Input: URL with valid classification data.

Expected Output: Categories array populated with identifiers.

Failed Request:

Input: URL resulting in a failure.

Expected Output: Empty categories array.

Ignored URL:

Input: URL to be ignored by the API.

Expected Output: Empty categories array.
60 changes: 60 additions & 0 deletions test/spec/modules/overtoneRtdProvider_spec.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { expect } from 'chai';
import sinon from 'sinon';
import { overtoneModule } from '../../../modules/overtoneRtdProvider.js';
import { logMessage } from '../../../src/utils.js';

const TEST_URLS = {
success: 'https://www.theguardian.com/film/2024/nov/15/duncan-cowles-silent-men-interview',
fail: 'https://www.nytimes.com',
ignore: 'https://wsj.com',
};

describe('Overtone RTD Submodule with Test URLs', function () {
this.timeout(120000);

let fetchContextDataStub;

beforeEach(function () {
fetchContextDataStub = sinon.stub(overtoneModule, 'fetchContextData').callsFake(async (url) => {
if (url === TEST_URLS.success) {
return { categories: ['ovtn_004', 'ovtn_104', 'ovtn_309', 'ovtn_202'], status: 1 };
}
if (url === TEST_URLS.fail) {
return { categories: [], status: 3 };
}
if (url === TEST_URLS.ignore) {
return { categories: [], status: 4 };
}
throw new Error('Unexpected URL in test');
});
});

afterEach(function () {
fetchContextDataStub.restore();
});

it('should fetch and return categories for the success URL', async function () {
const data = await overtoneModule.fetchContextData(TEST_URLS.success);
logMessage(data);
expect(data).to.deep.equal({
categories: ['ovtn_004', 'ovtn_104', 'ovtn_309', 'ovtn_202'],
status: 1,
});
});

it('should return the expected structure for the fail URL', async function () {
const data = await overtoneModule.fetchContextData(TEST_URLS.fail);
expect(data).to.deep.equal({
categories: [],
status: 3,
});
});

it('should return the expected structure for the ignore URL', async function () {
const data = await overtoneModule.fetchContextData(TEST_URLS.ignore);
expect(data).to.deep.equal({
categories: [],
status: 4,
});
});
});
Loading