Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
08acc89
defineMediaBidAdapter checkpoint
mkdefinemedia Sep 19, 2024
3b00023
test page
mkdefinemedia Sep 19, 2024
6a2ad57
clean up
mkdefinemedia Sep 20, 2024
617a831
first tests
dennisjay Jan 30, 2025
5b9f824
Make Adapter feasable for multiple BidRequests
Apr 25, 2025
370a1d0
Fix some adapter props. Not all unittest working
Jul 18, 2025
c3c8df3
WIP on master
Jul 25, 2025
43b34c1
cleanup debug scripts
dennisjay Aug 1, 2025
0a7ccd9
Fix/Delete testfiles
dennisjay Aug 1, 2025
54cb79e
Change maintainer
dennisjay Aug 1, 2025
fea656e
Restore helo world
dennisjay Aug 1, 2025
b5bf173
Merge branch 'master' into feature/defineMediaC2SAdapter
dennisjay Aug 8, 2025
be60356
Leave out additional options fot http to rely on default and avoid a…
dennisjay Aug 8, 2025
43e839a
gvl id fix
dennisjay Aug 8, 2025
6e16b5a
Only import used utils by name
dennisjay Aug 8, 2025
7df1d3b
Remove accidential commited test code for XHR
dennisjay Aug 8, 2025
b00477b
small linter error
dennisjay Aug 8, 2025
2104fa3
Merge branch 'master' into feature/defineMediaC2SAdapter
mkdefinemedia Aug 14, 2025
c103ea3
Merge branch 'master' into feature/defineMediaC2SAdapter
mkdefinemedia Aug 15, 2025
2f3cd11
Merge branch 'master' into feature/defineMediaC2SAdapter
mkdefinemedia Aug 15, 2025
b67f6a7
Merge branch 'master' into feature/defineMediaC2SAdapter
mkdefinemedia Aug 21, 2025
2b491e5
Merge branch 'master' into feature/defineMediaC2SAdapter
mkdefinemedia Sep 17, 2025
d78f77c
Merge branch 'master' into feature/defineMediaC2SAdapter
mkdefinemedia Sep 17, 2025
b078cfc
Merge branch 'master' into feature/defineMediaC2SAdapter
mkdefinemedia Sep 18, 2025
8d8ed53
clarify supplierDomainName usage
mkdefinemedia Sep 23, 2025
fb6190a
Merge branch 'master' into feature/defineMediaC2SAdapter
mkdefinemedia Sep 23, 2025
199d8aa
Merge branch 'master' into feature/defineMediaC2SAdapter
mkdefinemedia Sep 23, 2025
5b09080
Merge branch 'master' into feature/defineMediaC2SAdapter
mkdefinemedia Sep 24, 2025
e51f5a0
Merge branch 'master' into feature/defineMediaC2SAdapter
mkdefinemedia Sep 25, 2025
72b31c1
Merge branch 'master' into feature/defineMediaC2SAdapter
mkdefinemedia Sep 28, 2025
e413a57
Merge branch 'master' into feature/defineMediaC2SAdapter
mkdefinemedia Sep 29, 2025
157de70
Define Media Bid Adapter: initial release (#13713)
mkdefinemedia Oct 7, 2025
51feaf6
Define Media Bid Adapter: initial release (#13713)
mkdefinemedia Oct 7, 2025
56f73f4
Merge branch 'master' into feature/defineMediaC2SAdapter
mkdefinemedia Oct 7, 2025
9dc4747
Merge branch 'master' into feature/defineMediaC2SAdapter
mkdefinemedia Oct 13, 2025
9d76ad4
Merge branch 'master' into feature/defineMediaC2SAdapter
mkdefinemedia Oct 16, 2025
b066ac5
Merge branch 'master' into feature/defineMediaC2SAdapter
mkdefinemedia Oct 17, 2025
2317074
Merge branch 'master' into feature/defineMediaC2SAdapter
mkdefinemedia Oct 19, 2025
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
280 changes: 280 additions & 0 deletions modules/defineMediaBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
/**
* Define Media Bid Adapter for Prebid.js
*
* This adapter connects publishers to Define Media's programmatic advertising platform
* via OpenRTB 2.5 protocol. It supports banner ad formats and includes proper
* supply chain transparency through sellers.json compliance.
*
* @module defineMediaBidAdapter
* @version 1.0.0
*/

import {logInfo, logError, logWarn } from "../src/utils.js";
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { BANNER } from '../src/mediaTypes.js';
import {ortbConverter} from '../libraries/ortbConverter/converter.js'
import { ajax } from '../src/ajax.js';

Comment thread
patmmccann marked this conversation as resolved.
// Bidder identification and compliance constants
const BIDDER_CODE = 'defineMedia';
const IAB_GVL_ID = 440; // IAB Global Vendor List ID for GDPR compliance
const SUPPORTED_MEDIA_TYPES = [BANNER]; // Currently only banner ads are supported

// Default bid response configuration
const DEFAULT_TTL = 1000; // Default time-to-live for bids in seconds
const DEFAULT_NET_REVENUE = true; // Revenue is reported as net (after platform fees)

// Endpoint URLs for different environments
const ENDPOINT_URL_DEV = 'https://rtb-dev.conative.network/openrtb2/auction'; // Development/testing endpoint
const ENDPOINT_URL_PROD = 'https://rtb.conative.network/openrtb2/auction'; // Production endpoint
const METHOD = 'POST'; // HTTP method for bid requests

/**
* Default ORTB converter instance with standard configuration
* This handles the conversion between Prebid.js bid objects and OpenRTB format
*/
const converter = ortbConverter({
context: {
netRevenue: DEFAULT_NET_REVENUE,
ttl: DEFAULT_TTL
}
});

export const spec = {
code: BIDDER_CODE,
gvlid: IAB_GVL_ID,
supportedMediaTypes: SUPPORTED_MEDIA_TYPES,

/**
* Determines if a bid request is valid for this adapter
*
* Required parameters:
* - supplierDomainName: Domain name for supply chain transparency
* - mediaTypes.banner: Must include banner media type configuration
*
* Optional parameters:
* - devMode: Boolean flag to use development endpoint
* - ttl: Custom time-to-live for the bid response (only honored when devMode is true)
*
* @param {Object} bid - The bid request object from Prebid.js
* @returns {boolean} True if the bid request is valid
*/
isBidRequestValid: (bid) => {
// Ensure we have a valid bid object
if (!bid || typeof bid !== 'object') {
logInfo(`[${BIDDER_CODE}] isBidRequestValid: Invalid bid object`);
return false;
}

// Validate required parameters
const hasSupplierDomainName = Boolean(bid?.params?.supplierDomainName);
const hasValidMediaType = Boolean(bid?.mediaTypes && bid.mediaTypes.banner);
const isDevMode = Boolean(bid?.params?.devMode);

logInfo(`[${BIDDER_CODE}] isBidRequestValid called with:`, {
bidId: bid.bidId,
hasSupplierDomainName,
hasValidMediaType,
isDevMode
});

const isValid = hasSupplierDomainName && hasValidMediaType;
logInfo(`[${BIDDER_CODE}] isBidRequestValid returned:`, isValid);
return isValid;
},

/**
* Builds OpenRTB bid requests from validated Prebid.js bid requests
*
* This method:
* 1. Creates individual OpenRTB requests for each valid bid
* 2. Sets up dynamic TTL based on bid parameters (only in devMode)
* 3. Configures supply chain transparency (schain)
* 4. Selects appropriate endpoint based on devMode flag
*
* @param {Array} validBidRequests - Array of valid bid request objects
* @param {Object} bidderRequest - Bidder-level request data from Prebid.js
* @returns {Array} Array of bid request objects to send to the server
*/
buildRequests: (validBidRequests, bidderRequest) => {
return validBidRequests?.map(function(req) {
// DeepCopy the request to avoid modifying the original object
const oneBidRequest = [JSON.parse(JSON.stringify(req))];

// Get parameters and check devMode first
const params = oneBidRequest[0].params;
const isDevMode = Boolean(params?.devMode);

// Custom TTL is only allowed in development mode for security and consistency
const ttl = isDevMode && params?.ttl ? params.ttl : DEFAULT_TTL;

// Create converter with TTL (custom only in devMode, otherwise default)
const dynamicConverter = ortbConverter({
context: {
netRevenue: DEFAULT_NET_REVENUE,
ttl: ttl
}
});

// Convert Prebid.js request to OpenRTB format
const ortbRequest = dynamicConverter.toORTB({
bidderRequest: bidderRequest,
bidRequests: oneBidRequest
});

// Select endpoint based on development mode flag
const endpointUrl = isDevMode ? ENDPOINT_URL_DEV : ENDPOINT_URL_PROD;

// Configure supply chain transparency (sellers.json compliance)
// Preserve existing schain if present, otherwise create minimal schain
if (bidderRequest?.source?.schain) {
// Preserve existing schain structure from bidderRequest
ortbRequest.source = bidderRequest.source;
} else {
// Create minimal schain only if none exists
if (!ortbRequest.source) {
ortbRequest.source = {};
}
if (!ortbRequest.source.schain) {
ortbRequest.source.schain = {
complete: 1, // Indicates this is a complete supply chain
nodes: [{
asi: params.supplierDomainName // Advertising system identifier
}]
};
}
}

logInfo(`[${BIDDER_CODE}] Mapped ORTB Request from`, oneBidRequest, ' to ', ortbRequest, ' with bidderRequest ', bidderRequest);

return {
method: METHOD,
url: endpointUrl,
data: ortbRequest,
converter: dynamicConverter // Attach converter for response processing
}
});
},

/**
* Processes bid responses from the Define Media server
*
* This method:
* 1. Validates the server response structure
* 2. Uses the appropriate ORTB converter (request-specific or default)
* 3. Converts OpenRTB response back to Prebid.js bid format
* 4. Handles errors gracefully and returns empty array on failure
*
* @param {Object} serverResponse - Response from the bid server
* @param {Object} request - Original request object containing converter
* @returns {Array} Array of bid objects for Prebid.js
*/
interpretResponse: (serverResponse, request) => {
logInfo(`[${BIDDER_CODE}] interpretResponse called with:`, { serverResponse, request });

// Validate server response structure
if (!serverResponse?.body) {
logWarn(`[${BIDDER_CODE}] No response body received`);
return [];
}

try {
// Use the converter from the request if available (with custom TTL), otherwise use default
const responseConverter = request.converter || converter;
const bids = responseConverter.fromORTB({response: serverResponse.body, request: request.data}).bids;
logInfo(`[${BIDDER_CODE}] Successfully parsed ${bids.length} bids`);
return bids;
} catch (error) {
logError(`[${BIDDER_CODE}] Error parsing response:`, error);
return [];
}
},

/**
* Handles bid request timeouts
* Currently logs timeout events for monitoring and debugging
*
* @param {Array|Object} timeoutData - Timeout data from Prebid.js
*/
onTimeout: (timeoutData) => {
logInfo(`[${BIDDER_CODE}] onTimeout called with:`, timeoutData);
},

/**
* Handles successful bid wins
*
* This method:
* 1. Fires win notification URL (burl) if present in bid
* 2. Logs win event for analytics and debugging
*
* @param {Object} bid - The winning bid object
*/
onBidWon: (bid) => {
// Fire win notification URL for server-side tracking
if (bid?.burl) {
ajax(bid.burl, null, null);
}
logInfo(`[${BIDDER_CODE}] onBidWon called with bid:`, bid);
},

/**
* Handles bidder errors with comprehensive error categorization
*
* This method:
* 1. Categorizes errors by type (timeout, network, client/server errors)
* 2. Collects relevant context for debugging
* 3. Logs structured error information for monitoring
*
* Error categories:
* - timeout: Request exceeded time limit
* - network: Network connectivity issues
* - client_error: 4xx HTTP status codes
* - server_error: 5xx HTTP status codes
* - unknown: Uncategorized errors
*
* @param {Object} params - Error parameters
* @param {Object} params.error - Error object
* @param {Object} params.bidderRequest - Original bidder request
*/
onBidderError: ({ error, bidderRequest }) => {
// Collect comprehensive error information for debugging
const errorInfo = {
message: error?.message || 'Unknown error',
type: error?.type || 'general',
code: error?.code || null,
bidderCode: BIDDER_CODE,
auctionId: bidderRequest?.auctionId || 'unknown',
bidderRequestId: bidderRequest?.bidderRequestId || 'unknown',
timeout: bidderRequest?.timeout || null,
bids: bidderRequest?.bids?.length || 0
};

// Categorize error types for better debugging and monitoring
if (error?.message?.includes('timeout')) {
errorInfo.category = 'timeout';
} else if (error?.message?.includes('network')) {
errorInfo.category = 'network';
} else if (error?.code >= 400 && error?.code < 500) {
errorInfo.category = 'client_error';
} else if (error?.code >= 500) {
errorInfo.category = 'server_error';
} else {
errorInfo.category = 'unknown';
}

logError(`[${BIDDER_CODE}] Bidder error occurred:`, errorInfo);
},

/**
* Handles successful ad rendering events
* Currently logs render success for analytics and debugging
*
* @param {Object} bid - The successfully rendered bid object
*/
onAdRenderSucceeded: (bid) => {
logInfo(`[${BIDDER_CODE}] onAdRenderSucceeded called with bid:`, bid);
}
};

// Register the bidder with Prebid.js
registerBidder(spec);
49 changes: 49 additions & 0 deletions modules/defineMediaBidAdapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Overview

```
Module Name: Define Media Bid Adapter
Module Type: Bidder Adapter
Maintainer: m.klumpp@definemedia.de
```

# Description

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.
Publishers are onboarded and activated via Define Media **Account Management** (no self-service keys required).

# Bid Parameters

| Name | Scope | Type | Description | Example |
|---------------------|----------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------|
| `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` |
| `devMode` | optional | boolean | Sends requests to the development endpoint. Requests with `devMode: true` are **not billable**. | `true` |


# How it works

- The adapter converts Prebid bid requests to ORTB and sets:
- `source.schain.complete = 1`
- `source.schain.nodes[0].asi = supplierDomainName`
- This ensures buyers can resolve the **supply chain** correctly without requiring any sellers.json hosted by the publisher.

# Example Prebid Configuration

```js
pbjs.addAdUnits([{
code: 'div-gpt-ad-123',
mediaTypes: { banner: { sizes: [[300, 250]] } },
bids: [{
bidder: 'defineMedia',
params: {
supplierDomainName: 'definemedia.de',
// set only for non-billable tests
devMode: false
}
}]
}]);
```

# Notes

- **Onboarding**: Publishers must be enabled by Define Media Account Management before traffic is accepted.
- **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.
3 changes: 2 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading