Skip to content

Commit 1d8b5fe

Browse files
CKBrennanMSubikclaude
authored
Overtone: changing internal API endpoint (prebid#14625)
* overtoneRtdProvider and overtoneRtdProvider_spec * Added markdown * Updated overtoneRtdProvider.md with relevant changes * Update overtoneRtdProvider.md Updated markdown text for clarification * Update overtoneRtdProvider_spec.mjs Removed timeout and added additional tests * Modified for getBidRequestData test case * Switch RTD module from /contextual to /VendorService endpoint - Endpoint: prebid-1.overtone.ai/contextual → prebid-1.overtone.ai/VendorService - URL param: ?URL={url}&InApp=False → ?pageUrl={url} - Response parsing: extract segment IDs from sources[0].segments[].id - Output format unchanged: { categories: ["ovtn_001", ...] } - Updated tests to match new response format Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Namespace Overtone data under site.ext.data.overtone to avoid overwriting Uses Prebid mergeDeep utility to safely merge into ortb2Fragments instead of replacing site.ext.data. Preserves pre-existing FPD and data from other RTD modules. Added tests verifying merge behavior with and without existing data. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix lint errors: merge utils imports, use log helper instead of reassigning logMessage Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Subiksha <bikshaaa@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent a9e4783 commit 1d8b5fe

2 files changed

Lines changed: 76 additions & 58 deletions

File tree

modules/overtoneRtdProvider.js

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,46 @@
11
import { submodule } from '../src/hook.js';
22
import { ajaxBuilder } from '../src/ajax.js';
3-
import { safeJSONParse, logMessage as _logMessage } from '../src/utils.js';
3+
import { mergeDeep, safeJSONParse, logMessage } from '../src/utils.js';
44

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

7-
// eslint-disable-next-line no-restricted-syntax
8-
const logMessage = (...args) => {
9-
_logMessage('Overtone', ...args);
10-
};
7+
function log(...args) {
8+
logMessage('Overtone', ...args);
9+
}
1110

1211
export async function fetchContextData(url = window.location.href) {
1312
const pageUrl = encodeURIComponent(url);
14-
const requestUrl = `${OVERTONE_URL}?URL=${pageUrl}&InApp=False`;
13+
const requestUrl = `${OVERTONE_URL}?pageUrl=${pageUrl}`;
1514
const request = window.ajaxBuilder || ajaxBuilder();
1615

1716
return new Promise((resolve, reject) => {
18-
logMessage('Sending request to:', requestUrl);
17+
log('Sending request to:', requestUrl);
1918
request(requestUrl, {
2019
success: (response) => {
2120
const data = safeJSONParse(response);
22-
logMessage('Fetched data:', data);
21+
log('Fetched data:', data);
2322

24-
if (!data || typeof data.status !== 'number') {
23+
if (!data || !data.vendor) {
2524
reject(new Error('Invalid response format'));
2625
return;
2726
}
2827

29-
switch (data.status) {
30-
case 1: // Success
31-
resolve({ categories: data.categories || [] });
32-
break;
33-
case 3: // Fail
34-
case 4: // Ignore
35-
resolve({ categories: [] });
36-
break;
37-
default:
38-
reject(new Error(`Unexpected response status: ${data.status}`));
39-
}
28+
const segments = (data.sources && data.sources[0] && data.sources[0].segments)
29+
? data.sources[0].segments.map(s => s.id)
30+
: [];
31+
32+
resolve({ categories: segments });
4033
},
4134
error: (err) => {
42-
logMessage('Error during request:', err);
35+
log('Error during request:', err);
4336
reject(err);
4437
},
4538
});
4639
});
4740
}
4841

4942
function init(config) {
50-
logMessage('init', config);
43+
log('init', config);
5144
return true;
5245
}
5346

@@ -58,16 +51,15 @@ export const overtoneRtdProvider = {
5851
fetchContextData()
5952
.then((contextData) => {
6053
if (contextData) {
61-
if (!bidReqConfig.ortb2Fragments.global.site.ext) {
62-
bidReqConfig.ortb2Fragments.global.site.ext = {};
63-
}
64-
65-
bidReqConfig.ortb2Fragments.global.site.ext.data = contextData;
54+
log('Fetched context data', contextData);
55+
mergeDeep(bidReqConfig.ortb2Fragments.global, {
56+
site: { ext: { data: { overtone: contextData } } }
57+
});
6658
}
6759
callback();
6860
})
6961
.catch((error) => {
70-
logMessage('Error fetching context data', error);
62+
log('Error fetching context data', error);
7163
callback();
7264
});
7365
},

test/spec/modules/overtoneRtdProvider_spec.mjs

Lines changed: 55 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,71 +6,97 @@ import { logMessage } from '../../../src/utils.js';
66
const TEST_URLS = {
77
success: 'https://www.theguardian.com/film/2024/nov/15/duncan-cowles-silent-men-interview',
88
fail: 'https://www.nytimes.com',
9-
ignore: 'https://wsj.com',
9+
empty: 'https://wsj.com',
1010
};
1111

1212
describe('Overtone RTD Submodule with Test URLs', function () {
13+
this.timeout(120000);
14+
1315
let fetchContextDataStub;
14-
let getBidRequestDataStub;
1516

1617
beforeEach(function () {
1718
fetchContextDataStub = sinon.stub(overtoneModule, 'fetchContextData').callsFake(async (url) => {
1819
if (url === TEST_URLS.success) {
19-
return { categories: ['ovtn_004', 'ovtn_104', 'ovtn_309', 'ovtn_202'], status: 1 };
20+
return { categories: ['ovtn_004', 'ovtn_104', 'ovtn_309', 'ovtn_202'] };
2021
}
2122
if (url === TEST_URLS.fail) {
22-
return { categories: [], status: 3 };
23+
throw new Error('Invalid response format');
2324
}
24-
if (url === TEST_URLS.ignore) {
25-
return { categories: [], status: 4 };
25+
if (url === TEST_URLS.empty) {
26+
return { categories: [] };
2627
}
2728
throw new Error('Unexpected URL in test');
2829
});
29-
30-
getBidRequestDataStub = sinon.stub(overtoneRtdProvider, 'getBidRequestData').callsFake((config, callback) => {
31-
if (config.shouldFail) {
32-
return;
33-
}
34-
callback();
35-
});
3630
});
3731

3832
afterEach(function () {
3933
fetchContextDataStub.restore();
40-
getBidRequestDataStub.restore();
4134
});
4235

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

52-
it('should return the expected structure for the fail URL', async function () {
53-
const data = await overtoneModule.fetchContextData(TEST_URLS.fail);
44+
it('should reject for an invalid response', async function () {
45+
try {
46+
await overtoneModule.fetchContextData(TEST_URLS.fail);
47+
expect.fail('Should have thrown');
48+
} catch (err) {
49+
expect(err.message).to.equal('Invalid response format');
50+
}
51+
});
52+
53+
it('should return empty categories when no segments are present', async function () {
54+
const data = await overtoneModule.fetchContextData(TEST_URLS.empty);
5455
expect(data).to.deep.equal({
5556
categories: [],
56-
status: 3,
5757
});
5858
});
5959

60-
it('should return the expected structure for the ignore URL', async function () {
61-
const data = await overtoneModule.fetchContextData(TEST_URLS.ignore);
62-
expect(data).to.deep.equal({
63-
categories: [],
64-
status: 4,
60+
it('should merge into site.ext.data without overwriting existing data', function (done) {
61+
const bidReqConfig = {
62+
ortb2Fragments: {
63+
global: {
64+
site: {
65+
ext: {
66+
data: {
67+
existingProvider: { segments: ['seg_123'] }
68+
}
69+
}
70+
}
71+
}
72+
}
73+
};
74+
75+
overtoneRtdProvider.getBidRequestData(bidReqConfig, () => {
76+
const data = bidReqConfig.ortb2Fragments.global.site.ext.data;
77+
// Existing FPD must be preserved
78+
expect(data.existingProvider).to.deep.equal({ segments: ['seg_123'] });
79+
// Overtone data must be namespaced under 'overtone'
80+
expect(data.overtone).to.exist;
81+
expect(data.overtone.categories).to.be.an('array');
82+
done();
6583
});
6684
});
6785

68-
describe('getBidRequestData', function () {
69-
it('should not call callback if config has shouldFail set to true', function () {
70-
const bidReqConfig = { shouldFail: true, ortb2Fragments: { global: { site: { ext: {} } } } };
71-
const callbackSpy = sinon.spy();
72-
overtoneRtdProvider.getBidRequestData(bidReqConfig, callbackSpy);
73-
sinon.assert.notCalled(callbackSpy);
86+
it('should namespace under overtone when no prior site.ext.data exists', function (done) {
87+
const bidReqConfig = {
88+
ortb2Fragments: {
89+
global: {
90+
site: {}
91+
}
92+
}
93+
};
94+
95+
overtoneRtdProvider.getBidRequestData(bidReqConfig, () => {
96+
const data = bidReqConfig.ortb2Fragments.global.site.ext.data;
97+
expect(data.overtone).to.exist;
98+
expect(data.overtone.categories).to.be.an('array');
99+
done();
74100
});
75101
});
76102
});

0 commit comments

Comments
 (0)