Skip to content

Commit 2c868e6

Browse files
dennisjaymkdefinemediaDennis Joest
authored andcommitted
Define Media Bid Adapter: initial release (prebid#13713)
* defineMediaBidAdapter checkpoint * test page * clean up * first tests Better integration test Push integration test Update Readme and remove publisherId from required parameters * Make Adapter feasable for multiple BidRequests Comment out not working tests * Fix some adapter props. Not all unittest working * WIP on master Some test fixes, but there ist still noch page object Updated Unittests with real testdata simplifies and minimizes test data and adds more detailed object checks * cleanup debug scripts * Fix/Delete testfiles * Change maintainer * Restore helo world * Leave out additional options fot http to rely on default and avoid additional cors requests * gvl id fix * Only import used utils by name * Remove accidential commited test code for XHR * small linter error * clarify supplierDomainName usage - clarify that publishers do not need to host sellers.json - explain supplierDomainName is used for schain attribution - add onboarding/transparency notes (account management) * Define Media Bid Adapter: initial release (prebid#13713) * Fix schain handling * Add detailed JSDoc comments explaining adapter functionality, parameters, and compliance requirements * Add comprehensive inline comments for code maintainability * Improve error handling in onBidderError with structured error categorization * Define Media Bid Adapter: initial release (prebid#13713) *fix linting errors --------- Co-authored-by: Michael Klumpp <[email protected]> Co-authored-by: Dennis Joest <[email protected]>
1 parent 41dec6e commit 2c868e6

File tree

3 files changed

+1142
-0
lines changed

3 files changed

+1142
-0
lines changed

modules/defineMediaBidAdapter.js

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
/**
2+
* Define Media Bid Adapter for Prebid.js
3+
*
4+
* This adapter connects publishers to Define Media's programmatic advertising platform
5+
* via OpenRTB 2.5 protocol. It supports banner ad formats and includes proper
6+
* supply chain transparency through sellers.json compliance.
7+
*
8+
* @module defineMediaBidAdapter
9+
* @version 1.0.0
10+
*/
11+
12+
import {logInfo, logError, logWarn } from "../src/utils.js";
13+
import { registerBidder } from '../src/adapters/bidderFactory.js';
14+
import { BANNER } from '../src/mediaTypes.js';
15+
import {ortbConverter} from '../libraries/ortbConverter/converter.js'
16+
import { ajax } from '../src/ajax.js';
17+
18+
// Bidder identification and compliance constants
19+
const BIDDER_CODE = 'defineMedia';
20+
const IAB_GVL_ID = 440; // IAB Global Vendor List ID for GDPR compliance
21+
const SUPPORTED_MEDIA_TYPES = [BANNER]; // Currently only banner ads are supported
22+
23+
// Default bid response configuration
24+
const DEFAULT_TTL = 1000; // Default time-to-live for bids in seconds
25+
const DEFAULT_NET_REVENUE = true; // Revenue is reported as net (after platform fees)
26+
27+
// Endpoint URLs for different environments
28+
const ENDPOINT_URL_DEV = 'https://rtb-dev.conative.network/openrtb2/auction'; // Development/testing endpoint
29+
const ENDPOINT_URL_PROD = 'https://rtb.conative.network/openrtb2/auction'; // Production endpoint
30+
const METHOD = 'POST'; // HTTP method for bid requests
31+
32+
/**
33+
* Default ORTB converter instance with standard configuration
34+
* This handles the conversion between Prebid.js bid objects and OpenRTB format
35+
*/
36+
const converter = ortbConverter({
37+
context: {
38+
netRevenue: DEFAULT_NET_REVENUE,
39+
ttl: DEFAULT_TTL
40+
}
41+
});
42+
43+
export const spec = {
44+
code: BIDDER_CODE,
45+
gvlid: IAB_GVL_ID,
46+
supportedMediaTypes: SUPPORTED_MEDIA_TYPES,
47+
48+
/**
49+
* Determines if a bid request is valid for this adapter
50+
*
51+
* Required parameters:
52+
* - supplierDomainName: Domain name for supply chain transparency
53+
* - mediaTypes.banner: Must include banner media type configuration
54+
*
55+
* Optional parameters:
56+
* - devMode: Boolean flag to use development endpoint
57+
* - ttl: Custom time-to-live for the bid response (only honored when devMode is true)
58+
*
59+
* @param {Object} bid - The bid request object from Prebid.js
60+
* @returns {boolean} True if the bid request is valid
61+
*/
62+
isBidRequestValid: (bid) => {
63+
// Ensure we have a valid bid object
64+
if (!bid || typeof bid !== 'object') {
65+
logInfo(`[${BIDDER_CODE}] isBidRequestValid: Invalid bid object`);
66+
return false;
67+
}
68+
69+
// Validate required parameters
70+
const hasSupplierDomainName = Boolean(bid?.params?.supplierDomainName);
71+
const hasValidMediaType = Boolean(bid?.mediaTypes && bid.mediaTypes.banner);
72+
const isDevMode = Boolean(bid?.params?.devMode);
73+
74+
logInfo(`[${BIDDER_CODE}] isBidRequestValid called with:`, {
75+
bidId: bid.bidId,
76+
hasSupplierDomainName,
77+
hasValidMediaType,
78+
isDevMode
79+
});
80+
81+
const isValid = hasSupplierDomainName && hasValidMediaType;
82+
logInfo(`[${BIDDER_CODE}] isBidRequestValid returned:`, isValid);
83+
return isValid;
84+
},
85+
86+
/**
87+
* Builds OpenRTB bid requests from validated Prebid.js bid requests
88+
*
89+
* This method:
90+
* 1. Creates individual OpenRTB requests for each valid bid
91+
* 2. Sets up dynamic TTL based on bid parameters (only in devMode)
92+
* 3. Configures supply chain transparency (schain)
93+
* 4. Selects appropriate endpoint based on devMode flag
94+
*
95+
* @param {Array} validBidRequests - Array of valid bid request objects
96+
* @param {Object} bidderRequest - Bidder-level request data from Prebid.js
97+
* @returns {Array} Array of bid request objects to send to the server
98+
*/
99+
buildRequests: (validBidRequests, bidderRequest) => {
100+
return validBidRequests?.map(function(req) {
101+
// DeepCopy the request to avoid modifying the original object
102+
const oneBidRequest = [JSON.parse(JSON.stringify(req))];
103+
104+
// Get parameters and check devMode first
105+
const params = oneBidRequest[0].params;
106+
const isDevMode = Boolean(params?.devMode);
107+
108+
// Custom TTL is only allowed in development mode for security and consistency
109+
const ttl = isDevMode && params?.ttl ? params.ttl : DEFAULT_TTL;
110+
111+
// Create converter with TTL (custom only in devMode, otherwise default)
112+
const dynamicConverter = ortbConverter({
113+
context: {
114+
netRevenue: DEFAULT_NET_REVENUE,
115+
ttl: ttl
116+
}
117+
});
118+
119+
// Convert Prebid.js request to OpenRTB format
120+
const ortbRequest = dynamicConverter.toORTB({
121+
bidderRequest: bidderRequest,
122+
bidRequests: oneBidRequest
123+
});
124+
125+
// Select endpoint based on development mode flag
126+
const endpointUrl = isDevMode ? ENDPOINT_URL_DEV : ENDPOINT_URL_PROD;
127+
128+
// Configure supply chain transparency (sellers.json compliance)
129+
// Preserve existing schain if present, otherwise create minimal schain
130+
if (bidderRequest?.source?.schain) {
131+
// Preserve existing schain structure from bidderRequest
132+
ortbRequest.source = bidderRequest.source;
133+
} else {
134+
// Create minimal schain only if none exists
135+
if (!ortbRequest.source) {
136+
ortbRequest.source = {};
137+
}
138+
if (!ortbRequest.source.schain) {
139+
ortbRequest.source.schain = {
140+
complete: 1, // Indicates this is a complete supply chain
141+
nodes: [{
142+
asi: params.supplierDomainName // Advertising system identifier
143+
}]
144+
};
145+
}
146+
}
147+
148+
logInfo(`[${BIDDER_CODE}] Mapped ORTB Request from`, oneBidRequest, ' to ', ortbRequest, ' with bidderRequest ', bidderRequest);
149+
150+
return {
151+
method: METHOD,
152+
url: endpointUrl,
153+
data: ortbRequest,
154+
converter: dynamicConverter // Attach converter for response processing
155+
}
156+
});
157+
},
158+
159+
/**
160+
* Processes bid responses from the Define Media server
161+
*
162+
* This method:
163+
* 1. Validates the server response structure
164+
* 2. Uses the appropriate ORTB converter (request-specific or default)
165+
* 3. Converts OpenRTB response back to Prebid.js bid format
166+
* 4. Handles errors gracefully and returns empty array on failure
167+
*
168+
* @param {Object} serverResponse - Response from the bid server
169+
* @param {Object} request - Original request object containing converter
170+
* @returns {Array} Array of bid objects for Prebid.js
171+
*/
172+
interpretResponse: (serverResponse, request) => {
173+
logInfo(`[${BIDDER_CODE}] interpretResponse called with:`, { serverResponse, request });
174+
175+
// Validate server response structure
176+
if (!serverResponse?.body) {
177+
logWarn(`[${BIDDER_CODE}] No response body received`);
178+
return [];
179+
}
180+
181+
try {
182+
// Use the converter from the request if available (with custom TTL), otherwise use default
183+
const responseConverter = request.converter || converter;
184+
const bids = responseConverter.fromORTB({response: serverResponse.body, request: request.data}).bids;
185+
logInfo(`[${BIDDER_CODE}] Successfully parsed ${bids.length} bids`);
186+
return bids;
187+
} catch (error) {
188+
logError(`[${BIDDER_CODE}] Error parsing response:`, error);
189+
return [];
190+
}
191+
},
192+
193+
/**
194+
* Handles bid request timeouts
195+
* Currently logs timeout events for monitoring and debugging
196+
*
197+
* @param {Array|Object} timeoutData - Timeout data from Prebid.js
198+
*/
199+
onTimeout: (timeoutData) => {
200+
logInfo(`[${BIDDER_CODE}] onTimeout called with:`, timeoutData);
201+
},
202+
203+
/**
204+
* Handles successful bid wins
205+
*
206+
* This method:
207+
* 1. Fires win notification URL (burl) if present in bid
208+
* 2. Logs win event for analytics and debugging
209+
*
210+
* @param {Object} bid - The winning bid object
211+
*/
212+
onBidWon: (bid) => {
213+
// Fire win notification URL for server-side tracking
214+
if (bid?.burl) {
215+
ajax(bid.burl, null, null);
216+
}
217+
logInfo(`[${BIDDER_CODE}] onBidWon called with bid:`, bid);
218+
},
219+
220+
/**
221+
* Handles bidder errors with comprehensive error categorization
222+
*
223+
* This method:
224+
* 1. Categorizes errors by type (timeout, network, client/server errors)
225+
* 2. Collects relevant context for debugging
226+
* 3. Logs structured error information for monitoring
227+
*
228+
* Error categories:
229+
* - timeout: Request exceeded time limit
230+
* - network: Network connectivity issues
231+
* - client_error: 4xx HTTP status codes
232+
* - server_error: 5xx HTTP status codes
233+
* - unknown: Uncategorized errors
234+
*
235+
* @param {Object} params - Error parameters
236+
* @param {Object} params.error - Error object
237+
* @param {Object} params.bidderRequest - Original bidder request
238+
*/
239+
onBidderError: ({ error, bidderRequest }) => {
240+
// Collect comprehensive error information for debugging
241+
const errorInfo = {
242+
message: error?.message || 'Unknown error',
243+
type: error?.type || 'general',
244+
code: error?.code || null,
245+
bidderCode: BIDDER_CODE,
246+
auctionId: bidderRequest?.auctionId || 'unknown',
247+
bidderRequestId: bidderRequest?.bidderRequestId || 'unknown',
248+
timeout: bidderRequest?.timeout || null,
249+
bids: bidderRequest?.bids?.length || 0
250+
};
251+
252+
// Categorize error types for better debugging and monitoring
253+
if (error?.message?.includes('timeout')) {
254+
errorInfo.category = 'timeout';
255+
} else if (error?.message?.includes('network')) {
256+
errorInfo.category = 'network';
257+
} else if (error?.code >= 400 && error?.code < 500) {
258+
errorInfo.category = 'client_error';
259+
} else if (error?.code >= 500) {
260+
errorInfo.category = 'server_error';
261+
} else {
262+
errorInfo.category = 'unknown';
263+
}
264+
265+
logError(`[${BIDDER_CODE}] Bidder error occurred:`, errorInfo);
266+
},
267+
268+
/**
269+
* Handles successful ad rendering events
270+
* Currently logs render success for analytics and debugging
271+
*
272+
* @param {Object} bid - The successfully rendered bid object
273+
*/
274+
onAdRenderSucceeded: (bid) => {
275+
logInfo(`[${BIDDER_CODE}] onAdRenderSucceeded called with bid:`, bid);
276+
}
277+
};
278+
279+
// Register the bidder with Prebid.js
280+
registerBidder(spec);

modules/defineMediaBidAdapter.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Overview
2+
3+
```
4+
Module Name: Define Media Bid Adapter
5+
Module Type: Bidder Adapter
6+
Maintainer: [email protected]
7+
```
8+
9+
# Description
10+
11+
This is the official Define Media Bid Adapter for Prebid.js. It currently supports **Banner**. Delivery is handled by Define Media’s own RTB server.
12+
Publishers are onboarded and activated via Define Media **Account Management** (no self-service keys required).
13+
14+
# Bid Parameters
15+
16+
| Name | Scope | Type | Description | Example |
17+
|---------------------|----------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------|
18+
| `supplierDomainName`| required | string | **Identifier used for the supply chain (schain)**. Populates `source.schain.nodes[0].asi` to attribute traffic to Define Media’s supply path. **Publishers do not need to host a sellers.json under this domain.** | `definemedia.de` |
19+
| `devMode` | optional | boolean | Sends requests to the development endpoint. Requests with `devMode: true` are **not billable**. | `true` |
20+
21+
22+
# How it works
23+
24+
- The adapter converts Prebid bid requests to ORTB and sets:
25+
- `source.schain.complete = 1`
26+
- `source.schain.nodes[0].asi = supplierDomainName`
27+
- This ensures buyers can resolve the **supply chain** correctly without requiring any sellers.json hosted by the publisher.
28+
29+
# Example Prebid Configuration
30+
31+
```js
32+
pbjs.addAdUnits([{
33+
code: 'div-gpt-ad-123',
34+
mediaTypes: { banner: { sizes: [[300, 250]] } },
35+
bids: [{
36+
bidder: 'defineMedia',
37+
params: {
38+
supplierDomainName: 'definemedia.de',
39+
// set only for non-billable tests
40+
devMode: false
41+
}
42+
}]
43+
}]);
44+
```
45+
46+
# Notes
47+
48+
- **Onboarding**: Publishers must be enabled by Define Media Account Management before traffic is accepted.
49+
- **Transparency**: Seller transparency is enforced on Define Media’s side via account setup and standard industry mechanisms (e.g., schain). No publisher-hosted sellers.json is expected or required.

0 commit comments

Comments
 (0)