Skip to content

Commit ce193a8

Browse files
JoseClimacoCopilot
authored andcommitted
InsurAds Rtd Provider: initial release (prebid#14558)
* InsurAds Rtd Provider * Update Example with Rtd config * Enrich the slot with targeting * Wait for context before executing the callback * Remove import not in use * Remove changes that are not needed anymore on interpretResponse and import it directly * Update test/spec/modules/insuradsRtdProvider_spec.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Clear keyvalues on non-ok responses * Initialize api call promise as resolved as per suggestion Promise.race([apiCallPromise, timeoutPromise]) will resolve immediately if apiCallPromise is still null/undefined (race treats non-Promise values as already-resolved). To ensure the intended behavior, default it to a resolved Promise (or initialize apiCallPromise in module scope) before racing. * Update test/spec/modules/insuradsRtdProvider_spec.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Clear keyValues on error or empty responses * Remove tests on functions that doesn't exist anymore * Fix module name * Improve rtdData handling * Remove tests not needed * Fix tests to match updated rtcData location * Fix failing tests * InsurAds RTD Provider: Register in parentModules map and standardize naming - Add insuradsRtdProvider to parentModules.rtdModule in modules/.submodules.json - Rename module from insurAdsRtdProvider to insuradsRtdProvider for consistency - Rename file from insurAdsRtdProvider.ts to insuradsRtdProvider.ts - Update export and references in module and test files * InsurAds Bid Adapter: Replace any type with AdapterRequest in getRtdTargetingFromRequest * InsurAds RTD Provider: Remove keepalive option from fetch request * InsurAds RTD Provider: Remove hardcoded credentials to honor privacy consent * InsurAds RTD Provider: Fix unit test expectations for fetch options * InsurAds RTD Provider: type config and validate publicId * align docs with bidder-only enrichment * Update test/spec/modules/insuradsBidAdapter_spec.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * make async test deterministic * Update modules/insuradsBidAdapter.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update test/spec/modules/insuradsRtdProvider_spec.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * document params.timeout --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent d66ef99 commit ce193a8

7 files changed

Lines changed: 541 additions & 293 deletions

File tree

integrationExamples/gpt/insurads.html

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<script async src="https://securepubads.g.doubleclick.net/tag/js/gpt.js"></script>
66
<script>
77
var FAILSAFE_TIMEOUT = 5300;
8-
var PREBID_TIMEOUT = 2000;
8+
var PREBID_TIMEOUT = 5000;
99

1010
var adUnits = [{
1111
code: 'div-gpt-ad-1460505748561-0',
@@ -41,6 +41,17 @@
4141
pbjs.setConfig({
4242
enableTIDs: true,
4343
debug: true, // use only for testing, remove in production
44+
// Uncomment the following to test InsurAds real-time data integration with your adapter
45+
// realTimeData: {
46+
// auctionDelay: 1000,
47+
// dataProviders: [{
48+
// name: 'insuradsRtd',
49+
// waitForIt: true,
50+
// params: {
51+
// publicId: 'ALIYJZJD'
52+
// }
53+
// }]
54+
// }
4455
});
4556

4657
// You can set up gzip compression like so, and the adapter will use this setting when sending bid requests
@@ -51,10 +62,10 @@
5162
// }
5263
// });
5364

54-
65+
5566
pbjs.bidderSettings = {
5667
insurads: {
57-
storageAllowed : true
68+
storageAllowed: true
5869
}
5970
}
6071

modules/.submodules.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
"hadronRtdProvider",
9999
"humansecurityRtdProvider",
100100
"iasRtdProvider",
101+
"insuradsRtdProvider",
101102
"imRtdProvider",
102103
"intersectionRtdProvider",
103104
"jwplayerRtdProvider",

modules/insuradsBidAdapter.ts

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { AdapterRequest, BidderSpec, registerBidder } from '../src/adapters/bidd
44
import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
55
import { ortbConverter } from '../libraries/ortbConverter/converter.js'
66

7-
import { interpretResponse, enrichImp, enrichRequest, getAmxId, getLocalStorageFunctionGenerator, getUserSyncs } from '../libraries/nexx360Utils/index.js';
7+
import { interpretResponse as nexxInterpretResponse, enrichImp, enrichRequest, getAmxId, getLocalStorageFunctionGenerator, getUserSyncs } from '../libraries/nexx360Utils/index.js';
88
import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingClientRect.js';
99
import { BidRequest, ClientBidderRequest } from '../src/adapterManager.js';
1010
import { ORTBImp, ORTBRequest } from '../src/prebid.public.js';
@@ -36,11 +36,12 @@ type InsurAdsBidParams = RequireAtLeastOne<{
3636
allBids?: boolean;
3737
customId?: string;
3838
bidders?: Record<string, unknown>;
39+
rtdData?: Record<string, unknown>;
3940
}, "tagId" | "placement">;
4041

4142
declare module '../src/adUnits' {
4243
interface BidderParams {
43-
['nexx360']: InsurAdsBidParams;
44+
['insurads']: InsurAdsBidParams;
4445
}
4546
}
4647

@@ -82,6 +83,11 @@ const converter = ortbConverter({
8283
deepSetValue(imp, 'ext.dimensions.cssMaxW', slotEl.style?.maxWidth);
8384
deepSetValue(imp, 'ext.dimensions.cssMaxH', slotEl.style?.maxHeight);
8485
}
86+
if (bidRequest.params.rtdData) {
87+
deepSetValue(imp, 'ext.rtdData', bidRequest.params.rtdData);
88+
delete bidRequest.params.rtdData;
89+
}
90+
8591
deepSetValue(imp, 'ext.nexx360', bidRequest.params);
8692
deepSetValue(imp, 'ext.nexx360.divId', divId);
8793
if (bidRequest.params.adUnitPath) deepSetValue(imp, 'ext.adUnitPath', bidRequest.params.adUnitPath);
@@ -96,6 +102,24 @@ const converter = ortbConverter({
96102
},
97103
});
98104

105+
function getRtdTargetingFromRequest(request: AdapterRequest): Record<string, string> {
106+
const targeting: Record<string, string> = {};
107+
const imps = request?.data?.imp;
108+
if (!Array.isArray(imps)) return targeting;
109+
110+
for (const imp of imps) {
111+
const rtdData = imp?.ext?.rtdData;
112+
if (!rtdData || typeof rtdData !== 'object') continue;
113+
114+
for (const [key, value] of Object.entries(rtdData)) {
115+
if (value === null || value === undefined) continue;
116+
targeting[key] = String(value);
117+
}
118+
}
119+
120+
return targeting;
121+
}
122+
99123
const isBidRequestValid = (bid: BidRequest<typeof BIDDER_CODE>): boolean => {
100124
if (bid.params.adUnitName && (typeof bid.params.adUnitName !== 'string' || bid.params.adUnitName === '')) {
101125
logError('bid.params.adUnitName needs to be a string');
@@ -136,14 +160,33 @@ const buildRequests = (
136160
return adapterRequest;
137161
}
138162

163+
const interpretResponse = (serverResponse, request) => {
164+
const responses: any[] = nexxInterpretResponse(serverResponse) as any;
165+
const rtdTargeting = getRtdTargetingFromRequest(request);
166+
167+
if (!rtdTargeting || Object.keys(rtdTargeting).length === 0) {
168+
return responses;
169+
}
170+
171+
return responses.map((bidResponse) => {
172+
return {
173+
...bidResponse,
174+
adserverTargeting: {
175+
...(bidResponse?.adserverTargeting || {}),
176+
...rtdTargeting,
177+
}
178+
};
179+
});
180+
};
181+
139182
export const spec: BidderSpec<typeof BIDDER_CODE> = {
140183
code: BIDDER_CODE,
141184
gvlid: GVLID,
142185
supportedMediaTypes: [BANNER, VIDEO, NATIVE],
143186
isBidRequestValid,
144187
buildRequests,
145188
interpretResponse,
146-
getUserSyncs,
189+
getUserSyncs
147190
};
148191

149192
registerBidder(spec);

modules/insuradsRtdProvider.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
## InsurAds Real-time Data Submodule
2+
3+
The [InsurAds](https://insurads.com) real-time data module in Prebid enables publishers to leverage
4+
contextual targeting and audience segmentation capabilities. This module provides real-time
5+
key-value targeting data that seamlessly integrates into your existing Prebid deployment,
6+
helping you maximize your advertising strategies.
7+
8+
## Building Prebid with InsurAds Support
9+
10+
Compile the InsurAds RTD module into your Prebid build:
11+
12+
`gulp serve --modules=rtdModule,insuradsBidAdapter,insuradsRtdProvider`
13+
14+
Please visit https://insurads.com/ for more information.
15+
16+
```javascript
17+
pbjs.setConfig({
18+
...
19+
realTimeData: {
20+
auctionDelay: 1000,
21+
dataProviders: [{
22+
name: 'insuradsRtd',
23+
waitForIt: true,
24+
params: {
25+
publicId: 'YOUR_PUBLIC_ID',
26+
timeout: 1000
27+
}
28+
}]
29+
}
30+
...
31+
});
32+
```
33+
34+
### Parameter Descriptions for the InsurAds Configuration Section
35+
36+
| Name | Type | Description | Notes |
37+
|:------------------|:--------|:--------------------------------------------------------------------------|:-------------------------|
38+
| name | String | Real time data module name | Always 'insuradsRtd' |
39+
| waitForIt | Boolean | When true, delays the auction until this provider calls back (bounded by `auctionDelay` / `params.timeout`) | Optional. Defaults to false |
40+
| params | Object | | |
41+
| params.publicId | String | This is the Public ID value obtained from InsurAds | Required |
42+
| params.timeout | Number | Max time (ms) to wait for the InsurAds API response before continuing the auction without enrichment | Optional. Defaults to 1000 |
43+
44+
## Testing
45+
46+
To view an example of InsurAds RTD provider:
47+
48+
`gulp serve --modules=rtdModule,insuradsBidAdapter,insuradsRtdProvider`
49+
50+
and then point your browser at:
51+
52+
`http://localhost:9999/integrationExamples/gpt/insurads.html`
53+
54+
## How It Works
55+
56+
The InsurAds RTD provider:
57+
58+
1. Fetches contextual targeting data from the InsurAds API using your `publicId`
59+
2. Stores the returned key-values internally
60+
3. Enriches bid requests for the `insurads` bidder by attaching the key-values onto `bid.params.rtdData`
61+
4. Leaves other bidders unchanged

modules/insuradsRtdProvider.ts

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import { submodule } from '../src/hook.js';
2+
import { logInfo } from '../src/utils.js'
3+
import { fetch } from '../src/ajax.js';
4+
import type { RtdProviderSpec } from './rtdModule/spec.ts';
5+
6+
/**
7+
* @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule
8+
*/
9+
10+
const SERVER_IP = 'https://services.insurads.com';
11+
const MODULE_NAME = 'insuradsRtd';
12+
const ENDPOINT = `${SERVER_IP}/core/init/prebid`;
13+
const LOG_PREFIX = 'insurads Rtd: ';
14+
const GVLID = 596;
15+
16+
// Internal state to store keyValues
17+
let keyValues = {};
18+
let apiCallPromise = Promise.resolve();
19+
20+
declare module './rtdModule/spec.ts' {
21+
interface ProviderConfig {
22+
insuradsRtd: {
23+
params: {
24+
publicId: string;
25+
timeout?: number;
26+
};
27+
};
28+
}
29+
}
30+
31+
export const insuradsRtdProvider: RtdProviderSpec<'insuradsRtd'> = {
32+
name: MODULE_NAME,
33+
gvlid: GVLID,
34+
init: (config, userConsent) => {
35+
const publicId = config?.params?.publicId;
36+
37+
if (typeof publicId !== 'string' || publicId.trim().length === 0) {
38+
logInfo(LOG_PREFIX + 'publicId is required and must be a non-empty string');
39+
return false;
40+
}
41+
42+
// Reset keyValues to avoid stale values after failed/empty response
43+
keyValues = {};
44+
45+
// Start fetch immediately without blocking init
46+
apiCallPromise = makeApiCall(publicId);
47+
48+
logInfo(LOG_PREFIX + 'submodule init', config, userConsent);
49+
return true;
50+
},
51+
getBidRequestData: (reqBidsConfigObj, callback, config, userConsent) => {
52+
logInfo(LOG_PREFIX + 'submodule getBidRequestData', reqBidsConfigObj, config, userConsent);
53+
54+
// Wait for API call to complete before enriching bid requests
55+
const timeout = config?.params?.timeout || 1000; // Default 1 second timeout
56+
const timeoutPromise = new Promise((resolve) => setTimeout(resolve, timeout));
57+
58+
Promise.race([apiCallPromise, timeoutPromise])
59+
.then(() => {
60+
// Enrich bid requests with RTD data for insurads bidder
61+
if (keyValues && Object.keys(keyValues).length > 0) {
62+
reqBidsConfigObj.adUnits.forEach(adUnit => {
63+
adUnit.bids.forEach(bid => {
64+
if (bid.bidder === 'insurads') {
65+
// Add RTD data to bid params so it can be accessed by the adapter
66+
bid.params = bid.params || {};
67+
68+
(bid.params as any).rtdData = keyValues;
69+
logInfo(LOG_PREFIX + 'Enriched bid request for insurads', (bid.params as any).rtdData);
70+
}
71+
});
72+
});
73+
} else {
74+
logInfo(LOG_PREFIX + 'No keyValues available for bid enrichment');
75+
}
76+
77+
callback();
78+
})
79+
.catch((_e) => {
80+
logInfo(LOG_PREFIX + 'Error waiting for API call');
81+
callback();
82+
});
83+
},
84+
};
85+
86+
async function makeApiCall(publicId: string) {
87+
const currentUrl = encodeURIComponent(location.href);
88+
89+
try {
90+
const response = await fetch(`${ENDPOINT}/${publicId}?url=${currentUrl}`, {
91+
method: 'GET',
92+
headers: {
93+
'Content-Type': 'application/json'
94+
}
95+
});
96+
if (!response.ok) {
97+
keyValues = {};
98+
logInfo(LOG_PREFIX + `Network error: ${response.status} ${response.statusText}, cleared cached values`);
99+
return;
100+
}
101+
if (response.status === 204) {
102+
keyValues = {};
103+
logInfo(LOG_PREFIX + '204 No Content received, cleared cached values');
104+
return;
105+
}
106+
107+
const data = await response.json();
108+
if (data && data.keyValues) {
109+
keyValues = data.keyValues;
110+
logInfo(LOG_PREFIX + 'Received keyValues from endpoint', data.keyValues);
111+
} else {
112+
keyValues = {};
113+
logInfo(LOG_PREFIX + 'No keyValues in response, cleared cached values');
114+
}
115+
} catch (_e) {
116+
keyValues = {};
117+
logInfo(LOG_PREFIX + 'API call failed, cleared cached keyValues');
118+
}
119+
}
120+
121+
function beforeInit() {
122+
// take actions to get data as soon as possible
123+
submodule('realTimeData', insuradsRtdProvider);
124+
}
125+
126+
beforeInit();

0 commit comments

Comments
 (0)