From 08acc896eaad40d20962abf2841b093e48e84d9a Mon Sep 17 00:00:00 2001 From: Michael Klumpp Date: Thu, 19 Sep 2024 18:57:06 +0200 Subject: [PATCH 01/19] defineMediaBidAdapter checkpoint --- gulpfile.js | 4 + modules/defineMediaBidAdapter.js | 84 +++++++++++++ modules/defineMediaBidAdapter.md | 16 +++ .../modules/defineMediaBidAdapter_spec.js | 111 ++++++++++++++++++ 4 files changed, 215 insertions(+) create mode 100644 modules/defineMediaBidAdapter.js create mode 100644 modules/defineMediaBidAdapter.md create mode 100755 test/spec/modules/defineMediaBidAdapter_spec.js diff --git a/gulpfile.js b/gulpfile.js index 0d6025d066d..e339917dc83 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -480,6 +480,10 @@ function startLocalServer(options = {}) { return [ function (req, res, next) { res.setHeader('Ad-Auction-Allowed', 'True'); + res.setHeader('Access-Control-Allow-Origin', '*'); // Allow all origins or specify your domain + res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE'); + res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); + res.setHeader('Access-Control-Allow-Credentials', true); next(); } ]; diff --git a/modules/defineMediaBidAdapter.js b/modules/defineMediaBidAdapter.js new file mode 100644 index 00000000000..4d37359079a --- /dev/null +++ b/modules/defineMediaBidAdapter.js @@ -0,0 +1,84 @@ +import * as utils from '../src/utils'; +import { registerBidder } from '../src/adapters/bidderFactory'; +import { config } from '../src/config'; +import { BANNER } from '../src/mediaTypes.js'; +import {ortbConverter} from '../libraries/ortbConverter/converter.js' + +const BIDDER_CODE = 'defineMedia'; +const IAB_GVL_ID = 755; +const SUPPORTED_MEDIA_TYPES = [BANNER]; +// const ENDPOINT_URL = 'http://prebid-endpoint.local:3000/prebid'; +const ENDPOINT_URL = 'https://rtb-dev.conative.network/openrtb2/auction' +const METHOD = 'POST'; + +const converter = ortbConverter({ + context: { + netRevenue: true, + ttl: 1000 + } +}); + +export const spec = { + code: BIDDER_CODE, + gvlid: IAB_GVL_ID, + supportedMediaTypes: SUPPORTED_MEDIA_TYPES, + + isBidRequestValid: (bid) => { + const isValid = !!(bid.params && bid.params.mandantId); + utils.logInfo(`[${BIDDER_CODE}] isBidRequestValid for bid ${bid.bidId}:`, isValid); + return isValid; + }, + + buildRequests: (validBidRequests, bidderRequest) => { + utils.logInfo(`[${BIDDER_CODE}] buildRequests called with:`, { validBidRequests, bidderRequest }); + + let ortbRequest = converter.toORTB({validBidRequests, bidderRequest}) + + utils.deepSetValue(ortbRequest.imp[0], 'ext.bidder.mandantId', validBidRequests[0].params.mandantId); + + utils.logInfo(`[${BIDDER_CODE}] Manually Mapped ORTB Request:`, ortbRequest); + + return [{ + method: METHOD, + url: ENDPOINT_URL, + data: ortbRequest, + options: { + contentType: 'application/json', + withCredentials: false + } + }]; + }, + + interpretResponse: (serverResponse, request) => { + utils.logInfo(`[${BIDDER_CODE}] interpretResponse called with:`, { serverResponse, request }); + + if (!serverResponse || !serverResponse.body) { + utils.logWarn(`[${BIDDER_CODE}] No response body received`); + return []; + } + const responseData = serverResponse.body + const requestData = request.data + + const bids = converter.fromORTB({response: responseData, request: requestData}).bids; + + return bids; + }, + + onTimeout: (timeoutData) => { + utils.logInfo(`[${BIDDER_CODE}] onTimeout called with:`, timeoutData); + }, + + onBidWon: (bid) => { + utils.logInfo(`[${BIDDER_CODE}] onBidWon called with bid:`, bid); + }, + + onBidderError: ({ error, bidderRequest }) => { + utils.logError(`[${BIDDER_CODE}] onBidderError called with:`, { error, bidderRequest }); + }, + + onAdRenderSucceeded: (bid) => { + utils.logInfo(`[${BIDDER_CODE}] onAdRenderSucceeded called with bid:`, bid); + } +}; + +registerBidder(spec); diff --git a/modules/defineMediaBidAdapter.md b/modules/defineMediaBidAdapter.md new file mode 100644 index 00000000000..ec4aa262d12 --- /dev/null +++ b/modules/defineMediaBidAdapter.md @@ -0,0 +1,16 @@ +# Overview + +``` +Module Name: Define Media Bid Adapter +Module Type: Bidder Adapter +Maintainer: developement@definemedia.de +``` + +# Description + +Connects to Define Media for bids. + +# Test Parameters +``` +// TODO +``` \ No newline at end of file diff --git a/test/spec/modules/defineMediaBidAdapter_spec.js b/test/spec/modules/defineMediaBidAdapter_spec.js new file mode 100755 index 00000000000..0c920423a49 --- /dev/null +++ b/test/spec/modules/defineMediaBidAdapter_spec.js @@ -0,0 +1,111 @@ +// jshint esversion: 6, es3: false, node: true +import {assert, expect} from 'chai'; +import {getStorageManager} from 'src/storageManager.js'; +import {spec} from 'modules/defineMediaBidAdapter.js'; + +describe('Define Media Bid Adapter', function () { + let serverResponse, bidRequest, bidResponses; + let bid = { + 'bidder': 'defineMedia', + 'params': { + 'mandantId': '5' + } + }; + + let validBidRequests = [{ + bidId: 'bidId', + params: {}, + mediaType: { + native: {} + } + }]; + + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { + assert(spec.isBidRequestValid(bid)); + }); + + it('should return false when MandantId is not set', function () { + delete bid.params.mandantId; + assert.isFalse(spec.isBidRequestValid(bid)); + }); + }); + + describe('buildRequests', function () { + it('should send request with correct structure', function () { + let request = spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }); + + assert.equal(request.method, 'POST'); + assert.ok(request.data); + }); + + it('should have default request structure', function () { + let keys = 'site,cur,imp,regs'.split(','); + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); + let data = Object.keys(request); + + assert.includeDeepMembers(data, keys); + }); + + it('Verify the site url', function () { + let siteUrl = 'https://www.yourdomain.tld/your-directory/'; + validBidRequests[0].params.url = siteUrl; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); + + assert.equal(request.site.page, siteUrl); + }); + }); + + describe('interpretResponse', function () { + const goodBannerResponse = { + body: { + cur: 'EUR', + id: 'bidid1', + seatbid: [ + { + seat: 'seedingAlliance', + bid: [{ + adm: '', + impid: 1, + price: 0.90, + h: 250, + w: 300 + }] + } + ] + } + }; + + const badResponse = { body: { + cur: 'EUR', + id: 'bidid1', + seatbid: [] + }}; + + const bidBannerRequest = { + data: {}, + bidRequests: [{bidId: '1', sizes: [300, 250]}] + }; + + it('should return null if body is missing or empty', function () { + const result = spec.interpretResponse(badResponse, bidBannerRequest); + assert.equal(result.length, 0); + }); + + it('should return the correct params', function () { + const resultBanner = spec.interpretResponse(goodBannerResponse, bidBannerRequest); + + assert.deepEqual(resultBanner[0].mediaType, 'banner'); + assert.deepEqual(resultBanner[0].width, bidBannerRequest.bidRequests[0].sizes[0]); + assert.deepEqual(resultBanner[0].height, bidBannerRequest.bidRequests[0].sizes[1]); + }); + + it('should return the correct banner content', function () { + const result = spec.interpretResponse(goodBannerResponse, bidBannerRequest); + const bid = goodBannerResponse.body.seatbid[0].bid[0]; + const regExpContent = new RegExp(''); + + assert.ok(result[0].ad.search(regExpContent) > -1); + }); + }); +}); From 3b000231073776830aaa3f547f9b428f8e4cc9ca Mon Sep 17 00:00:00 2001 From: Michael Klumpp Date: Thu, 19 Sep 2024 18:57:26 +0200 Subject: [PATCH 02/19] test page --- integrationExamples/gpt/hello_world.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/integrationExamples/gpt/hello_world.html b/integrationExamples/gpt/hello_world.html index a7d69d21788..e45c6605bdb 100644 --- a/integrationExamples/gpt/hello_world.html +++ b/integrationExamples/gpt/hello_world.html @@ -13,8 +13,8 @@ + + + + + + + + + +

Prebid.js Test

+
Div-1
+
+ +
+ + + diff --git a/modules.json b/modules.json new file mode 100644 index 00000000000..f3c7d945d1e --- /dev/null +++ b/modules.json @@ -0,0 +1,3 @@ +[ + "defineMediaBidAdapter" +] diff --git a/modules/defineMediaBidAdapter.js b/modules/defineMediaBidAdapter.js index f466b822c7a..6ad31dc9d45 100644 --- a/modules/defineMediaBidAdapter.js +++ b/modules/defineMediaBidAdapter.js @@ -1,5 +1,5 @@ -import * as utils from '../src/utils'; -import { registerBidder } from '../src/adapters/bidderFactory'; +import * as utils 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'; @@ -7,7 +7,9 @@ import { ajax } from '../src/ajax.js'; const BIDDER_CODE = 'defineMedia'; const IAB_GVL_ID = 755; const SUPPORTED_MEDIA_TYPES = [BANNER]; -const ENDPOINT_URL = 'https://rtb-dev.conative.network/openrtb2/auction'; + +const ENDPOINT_URL_DEV = 'https://rtb-dev.conative.network/openrtb2/auction'; +const ENDPOINT_URL_PROD = 'https://rtb.conative.network/openrtb2/auction'; const METHOD = 'POST'; const converter = ortbConverter({ @@ -23,26 +25,33 @@ export const spec = { supportedMediaTypes: SUPPORTED_MEDIA_TYPES, isBidRequestValid: (bid) => { - const isValid = Boolean(bid?.params?.mandantId); - utils.logInfo(`[${BIDDER_CODE}] isBidRequestValid for bid ${bid.bidId}:`, isValid); + const hasSupplierDomainName = Boolean(bid?.params?.supplierDomainName); + const isDevMode = Boolean(bid?.params?.devMode); + utils.logInfo(`[${BIDDER_CODE}] isBidRequestValid called with:`, { bid, hasSupplierDomainName, isDevMode }); + const isValid = hasSupplierDomainName; + utils.logInfo(`[${BIDDER_CODE}] isBidRequestValid returned:`, isValid); return isValid; }, buildRequests: (validBidRequests, bidderRequest) => { - utils.logInfo(`[${BIDDER_CODE}] buildRequests called with:`, { validBidRequests, bidderRequest }); - const isValid = Boolean(validBidRequests[0]?.params?.mandantId); - - if(!isValid) return []; + if (validBidRequests.length !== 1) { + utils.logWarn(`[${BIDDER_CODE}] buildRequests called with invalid number of validBidRequests:`, validBidRequests.length); + return []; + } const ortbRequest = converter.toORTB({validBidRequests, bidderRequest}); + const params = validBidRequests[0].params; + const isDevMode = Boolean(params?.devMode); + const endpointUrl = isDevMode ? ENDPOINT_URL_DEV : ENDPOINT_URL_PROD; - utils.deepSetValue(ortbRequest.imp[0], 'ext.bidder.mandantId', validBidRequests[0].params.mandantId); + utils.deepSetValue(ortbRequest, 'source.schain.complete', 1); + utils.deepSetValue(ortbRequest, 'source.schain.nodes.0.asi', '' + params.supplierDomainName); utils.logInfo(`[${BIDDER_CODE}] Mapped ORTB Request:`, ortbRequest); return [{ method: METHOD, - url: ENDPOINT_URL, + url: endpointUrl, data: ortbRequest, options: { contentType: 'application/json', @@ -68,15 +77,15 @@ export const spec = { onBidWon: (bid) => { if (bid?.burl) { - ajax(bid.burl, null); + ajax(bid.burl, null, null); } utils.logInfo(`[${BIDDER_CODE}] onBidWon called with bid:`, bid); }, onBidderError: ({ error, bidderRequest }) => { - if (bid?.lurl) { - ajax(bid.lurl, null); - } + /* if (bid?.lurl) { + ajax(bid.lurl, null, null); + } */ utils.logError(`[${BIDDER_CODE}] onBidderError called with:`, { error, bidderRequest }); }, diff --git a/modules/defineMediaBidAdapter.md b/modules/defineMediaBidAdapter.md index ec4aa262d12..838d4368ffc 100644 --- a/modules/defineMediaBidAdapter.md +++ b/modules/defineMediaBidAdapter.md @@ -3,14 +3,17 @@ ``` Module Name: Define Media Bid Adapter Module Type: Bidder Adapter -Maintainer: developement@definemedia.de +Maintainer: n.leidig@definemedia.de ``` # Description -Connects to Define Media for bids. +This is the official Define Media Bid Adapter for Prebid.js. An open-source project maintained by Define Media. +This adapter only supports Banner Ads at the moment. In the backend we use our own RTB-Server to deliver the ads. -# Test Parameters -``` -// TODO -``` \ No newline at end of file +# Bid Parameters + +| Name | Scope | Type | Description | Example +| ---- | ----- |---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------| ------- +| `supplierDomainName` | required | String | The domain name of the last supplier in the chain. Under this domain a sellers.json must be available under https://${supplierDomainName}/sellers.json | definemedia.de +| `devMode` | optional | boolean | This parameter enables our development endpoint instead of the production endpoint. All requests done with this parameterer set to "true" are *NOT* billable | true diff --git a/package-lock.json b/package-lock.json index 9083185ca6e..139d4e644f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5820,7 +5820,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chai": { "version": "4.4.1", diff --git a/test/spec/modules/defineMediaBidAdapter_spec.js b/test/spec/modules/defineMediaBidAdapter_spec.js index 0c920423a49..c3dd8cc0691 100755 --- a/test/spec/modules/defineMediaBidAdapter_spec.js +++ b/test/spec/modules/defineMediaBidAdapter_spec.js @@ -8,7 +8,9 @@ describe('Define Media Bid Adapter', function () { let bid = { 'bidder': 'defineMedia', 'params': { - 'mandantId': '5' + 'rtbPublisherId': '08159', + 'supplierDomainName': 'definemedia.de', + 'devMode': true } }; @@ -16,7 +18,7 @@ describe('Define Media Bid Adapter', function () { bidId: 'bidId', params: {}, mediaType: { - native: {} + banner: {} } }]; @@ -25,15 +27,20 @@ describe('Define Media Bid Adapter', function () { assert(spec.isBidRequestValid(bid)); }); - it('should return false when MandantId is not set', function () { - delete bid.params.mandantId; + it('should return false when rtbPublisherId is not set', function () { + delete bid.params.rtbPublisherId; + assert.isFalse(spec.isBidRequestValid(bid)); + }); + + it('should return false when supplierDomainName is not set', function () { + delete bid.params.supplierDomainName; assert.isFalse(spec.isBidRequestValid(bid)); }); }); describe('buildRequests', function () { it('should send request with correct structure', function () { - let request = spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }); + let request = spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}); assert.equal(request.method, 'POST'); assert.ok(request.data); @@ -41,7 +48,7 @@ describe('Define Media Bid Adapter', function () { it('should have default request structure', function () { let keys = 'site,cur,imp,regs'.split(','); - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); let data = Object.keys(request); assert.includeDeepMembers(data, keys); @@ -50,13 +57,13 @@ describe('Define Media Bid Adapter', function () { it('Verify the site url', function () { let siteUrl = 'https://www.yourdomain.tld/your-directory/'; validBidRequests[0].params.url = siteUrl; - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); assert.equal(request.site.page, siteUrl); }); }); - describe('interpretResponse', function () { + /* describe('interpretResponse', function () { const goodBannerResponse = { body: { cur: 'EUR', @@ -107,5 +114,5 @@ describe('Define Media Bid Adapter', function () { assert.ok(result[0].ad.search(regExpContent) > -1); }); - }); + }); */ }); From 5b9f82420e5a09d9999ec4f670ee3d037e92ff7d Mon Sep 17 00:00:00 2001 From: Dennis Joest Date: Fri, 25 Apr 2025 14:52:33 +0200 Subject: [PATCH 05/19] Make Adapter feasable for multiple BidRequests Comment out not working tests --- modules/defineMediaBidAdapter.js | 45 +++++++++---------- .../modules/defineMediaBidAdapter_spec.js | 9 +--- 2 files changed, 24 insertions(+), 30 deletions(-) diff --git a/modules/defineMediaBidAdapter.js b/modules/defineMediaBidAdapter.js index 6ad31dc9d45..baa0a1ba763 100644 --- a/modules/defineMediaBidAdapter.js +++ b/modules/defineMediaBidAdapter.js @@ -34,30 +34,29 @@ export const spec = { }, buildRequests: (validBidRequests, bidderRequest) => { - if (validBidRequests.length !== 1) { - utils.logWarn(`[${BIDDER_CODE}] buildRequests called with invalid number of validBidRequests:`, validBidRequests.length); - return []; - } - - const ortbRequest = converter.toORTB({validBidRequests, bidderRequest}); - const params = validBidRequests[0].params; - const isDevMode = Boolean(params?.devMode); - const endpointUrl = isDevMode ? ENDPOINT_URL_DEV : ENDPOINT_URL_PROD; - - utils.deepSetValue(ortbRequest, 'source.schain.complete', 1); - utils.deepSetValue(ortbRequest, 'source.schain.nodes.0.asi', '' + params.supplierDomainName); - - utils.logInfo(`[${BIDDER_CODE}] Mapped ORTB Request:`, ortbRequest); - - return [{ - method: METHOD, - url: endpointUrl, - data: ortbRequest, - options: { - contentType: 'application/json', - withCredentials: false + return validBidRequests?.map(function(req) { + console.log(req) + const oneBidRequest = [JSON.parse(JSON.stringify(req))]; + const ortbRequest = converter.toORTB({oneBidRequest, bidderRequest}); + + const params = oneBidRequest[0].params; + const isDevMode = Boolean(params?.devMode); + const endpointUrl = isDevMode ? ENDPOINT_URL_DEV : ENDPOINT_URL_PROD; + + utils.deepSetValue(ortbRequest, 'source.schain.complete', 1); + utils.deepSetValue(ortbRequest, 'source.schain.nodes.0.asi', '' + params.supplierDomainName); + + utils.logInfo(`[${BIDDER_CODE}] Mapped ORTB Request from`, oneBidRequest, ' to ', ortbRequest); + return { + method: METHOD, + url: endpointUrl, + data: ortbRequest, + options: { + contentType: 'application/json', + withCredentials: false + } } - }]; + }); }, interpretResponse: (serverResponse, request) => { diff --git a/test/spec/modules/defineMediaBidAdapter_spec.js b/test/spec/modules/defineMediaBidAdapter_spec.js index c3dd8cc0691..9b04ac82266 100755 --- a/test/spec/modules/defineMediaBidAdapter_spec.js +++ b/test/spec/modules/defineMediaBidAdapter_spec.js @@ -8,7 +8,6 @@ describe('Define Media Bid Adapter', function () { let bid = { 'bidder': 'defineMedia', 'params': { - 'rtbPublisherId': '08159', 'supplierDomainName': 'definemedia.de', 'devMode': true } @@ -27,17 +26,13 @@ describe('Define Media Bid Adapter', function () { assert(spec.isBidRequestValid(bid)); }); - it('should return false when rtbPublisherId is not set', function () { - delete bid.params.rtbPublisherId; - assert.isFalse(spec.isBidRequestValid(bid)); - }); - it('should return false when supplierDomainName is not set', function () { delete bid.params.supplierDomainName; assert.isFalse(spec.isBidRequestValid(bid)); }); }); + /* describe('buildRequests', function () { it('should send request with correct structure', function () { let request = spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}); @@ -63,7 +58,7 @@ describe('Define Media Bid Adapter', function () { }); }); - /* describe('interpretResponse', function () { + describe('interpretResponse', function () { const goodBannerResponse = { body: { cur: 'EUR', From 370a1d0c975f3ea46e95cfcb9c1cbcaaacf4fa06 Mon Sep 17 00:00:00 2001 From: Dennis Joest Date: Fri, 18 Jul 2025 14:36:04 +0200 Subject: [PATCH 06/19] Fix some adapter props. Not all unittest working --- modules/defineMediaBidAdapter.js | 7 +- .../modules/defineMediaBidAdapter_spec.js | 221 ++++++++++++------ 2 files changed, 161 insertions(+), 67 deletions(-) diff --git a/modules/defineMediaBidAdapter.js b/modules/defineMediaBidAdapter.js index baa0a1ba763..ed8e2c7f97f 100644 --- a/modules/defineMediaBidAdapter.js +++ b/modules/defineMediaBidAdapter.js @@ -35,9 +35,12 @@ export const spec = { buildRequests: (validBidRequests, bidderRequest) => { return validBidRequests?.map(function(req) { - console.log(req) + // DeepCopy the request to avoid modifying the original object const oneBidRequest = [JSON.parse(JSON.stringify(req))]; - const ortbRequest = converter.toORTB({oneBidRequest, bidderRequest}); + const ortbRequest = converter.toORTB({ + bidderRequest: bidderRequest, + bidRequests: oneBidRequest + }); const params = oneBidRequest[0].params; const isDevMode = Boolean(params?.devMode); diff --git a/test/spec/modules/defineMediaBidAdapter_spec.js b/test/spec/modules/defineMediaBidAdapter_spec.js index 9b04ac82266..bf26b54d34e 100755 --- a/test/spec/modules/defineMediaBidAdapter_spec.js +++ b/test/spec/modules/defineMediaBidAdapter_spec.js @@ -4,110 +4,201 @@ import {getStorageManager} from 'src/storageManager.js'; import {spec} from 'modules/defineMediaBidAdapter.js'; describe('Define Media Bid Adapter', function () { - let serverResponse, bidRequest, bidResponses; - let bid = { - 'bidder': 'defineMedia', - 'params': { - 'supplierDomainName': 'definemedia.de', - 'devMode': true + const mockValidBidParams = [ + { + 'bidder': 'defineMedia', + 'params': { + 'supplierDomainName': 'definemedia.de', + 'devMode': true + } + }, + { + 'bidder': 'defineMedia', + 'mediaTypes': ['banner'], + 'params': { + 'supplierDomainName': 'definemedia.de', + 'devMode': false + } } - }; + ]; - let validBidRequests = [{ + const mockInvalidBidParams = [ + { + 'bidder': 'defineMedia', + 'params': {} + } + ]; + + const mockBidRequests = [{ bidId: 'bidId', - params: {}, + params: { + 'supplierDomainName': 'definemedia.de', + 'devMode': false + }, mediaType: { banner: {} } }]; + describe('isBidRequestValid', function () { it('should return true when required params found', function () { - assert(spec.isBidRequestValid(bid)); + for (const bidRequest of mockValidBidParams) { + assert.isTrue(spec.isBidRequestValid(bidRequest)); + } }); it('should return false when supplierDomainName is not set', function () { - delete bid.params.supplierDomainName; - assert.isFalse(spec.isBidRequestValid(bid)); + for (const bidRequest of mockInvalidBidParams) { + assert.isFalse(spec.isBidRequestValid(bidRequest)); + } }); }); - /* + describe('buildRequests', function () { + const mockBidderRequest = { + refererInfo: { + referer: 'page' + } + } + + it('should send request with correct structure', function () { - let request = spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}); + let requests = spec.buildRequests(mockBidRequests, mockBidderRequest); - assert.equal(request.method, 'POST'); - assert.ok(request.data); + for (const request of requests) { + assert.equal(request.method, 'POST'); + assert.ok(request.data); + } }); + it('should have default request structure', function () { - let keys = 'site,cur,imp,regs'.split(','); - let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); - let data = Object.keys(request); + let keys = 'id,imp,source'.split(','); + let requests = spec.buildRequests(mockBidRequests, mockBidderRequest); - assert.includeDeepMembers(data, keys); + for (const request of requests) { + let data = Object.keys(request.data); + assert.includeDeepMembers(data, keys); + } }); it('Verify the site url', function () { let siteUrl = 'https://www.yourdomain.tld/your-directory/'; - validBidRequests[0].params.url = siteUrl; - let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); + let bidderRequest = JSON.parse(JSON.stringify(mockBidderRequest)); + let validBidRequests = JSON.parse(JSON.stringify(mockBidRequests)); + + for (let req of validBidRequests) { + req.params.url = siteUrl; + } + + bidderRequest.refererInfo.referer = siteUrl; + let requests = spec.buildRequests(validBidRequests, bidderRequest); - assert.equal(request.site.page, siteUrl); + for (const request of requests) { + console.log(JSON.stringify(request.data, null, 2)); + assert.equal(request.data.site.page, siteUrl); + } }); }); +}); - describe('interpretResponse', function () { - const goodBannerResponse = { - body: { - cur: 'EUR', - id: 'bidid1', - seatbid: [ - { - seat: 'seedingAlliance', - bid: [{ - adm: '', - impid: 1, - price: 0.90, - h: 250, - w: 300 - }] - } - ] - } - }; +/*describe('interpretResponse', function () { + const goodBannerResponse = { + body: { + cur: 'EUR', + id: 'bidid1', + seatbid: [ + { + seat: 'seedingAlliance', + bid: [{ + adm: '', + impid: 1, + price: 0.90, + h: 250, + w: 300 + }] + } + ] + } + }; - const badResponse = { body: { + const badResponse = { + body: { cur: 'EUR', id: 'bidid1', seatbid: [] - }}; + } + }; - const bidBannerRequest = { - data: {}, - bidRequests: [{bidId: '1', sizes: [300, 250]}] - }; + const bidBannerRequest = { + data: {}, + bidRequests: [{bidId: '1', sizes: [300, 250]}] + }; - it('should return null if body is missing or empty', function () { - const result = spec.interpretResponse(badResponse, bidBannerRequest); - assert.equal(result.length, 0); - }); + it('should return null if body is missing or empty', function () { + const result = spec.interpretResponse(badResponse, bidBannerRequest); + assert.equal(result.length, 0); + }); +}); - it('should return the correct params', function () { - const resultBanner = spec.interpretResponse(goodBannerResponse, bidBannerRequest); +}) +; - assert.deepEqual(resultBanner[0].mediaType, 'banner'); - assert.deepEqual(resultBanner[0].width, bidBannerRequest.bidRequests[0].sizes[0]); - assert.deepEqual(resultBanner[0].height, bidBannerRequest.bidRequests[0].sizes[1]); - }); - it('should return the correct banner content', function () { - const result = spec.interpretResponse(goodBannerResponse, bidBannerRequest); - const bid = goodBannerResponse.body.seatbid[0].bid[0]; - const regExpContent = new RegExp(''); +/* +describe('interpretResponse', function () { + const goodBannerResponse = { + body: { + cur: 'EUR', + id: 'bidid1', + seatbid: [ + { + seat: 'seedingAlliance', + bid: [{ + adm: '', + impid: 1, + price: 0.90, + h: 250, + w: 300 + }] + } + ] + } + }; - assert.ok(result[0].ad.search(regExpContent) > -1); - }); - }); */ + const badResponse = { body: { + cur: 'EUR', + id: 'bidid1', + seatbid: [] + }}; + + const bidBannerRequest = { + data: {}, + bidRequests: [{bidId: '1', sizes: [300, 250]}] + }; + + it('should return null if body is missing or empty', function () { + const result = spec.interpretResponse(badResponse, bidBannerRequest); + assert.equal(result.length, 0); + }); + + it('should return the correct params', function () { + const resultBanner = spec.interpretResponse(goodBannerResponse, bidBannerRequest); + + assert.deepEqual(resultBanner[0].mediaType, 'banner'); + assert.deepEqual(resultBanner[0].width, bidBannerRequest.bidRequests[0].sizes[0]); + assert.deepEqual(resultBanner[0].height, bidBannerRequest.bidRequests[0].sizes[1]); + }); + + it('should return the correct banner content', function () { + const result = spec.interpretResponse(goodBannerResponse, bidBannerRequest); + const bid = goodBannerResponse.body.seatbid[0].bid[0]; + const regExpContent = new RegExp(''); + + assert.ok(result[0].ad.search(regExpContent) > -1); + }); }); +}) +;*/ From c3c8df3efcdd4d0d76c05a0016cefc57ed78e3fd Mon Sep 17 00:00:00 2001 From: Dennis Joest Date: Fri, 25 Jul 2025 13:45:23 +0200 Subject: [PATCH 07/19] 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 --- modules/defineMediaBidAdapter.js | 2 +- .../modules/defineMediaBidAdapter_spec.js | 897 +++++++++++++++--- 2 files changed, 755 insertions(+), 144 deletions(-) diff --git a/modules/defineMediaBidAdapter.js b/modules/defineMediaBidAdapter.js index ed8e2c7f97f..f13d76cdf16 100644 --- a/modules/defineMediaBidAdapter.js +++ b/modules/defineMediaBidAdapter.js @@ -49,7 +49,7 @@ export const spec = { utils.deepSetValue(ortbRequest, 'source.schain.complete', 1); utils.deepSetValue(ortbRequest, 'source.schain.nodes.0.asi', '' + params.supplierDomainName); - utils.logInfo(`[${BIDDER_CODE}] Mapped ORTB Request from`, oneBidRequest, ' to ', ortbRequest); + utils.logInfo(`[${BIDDER_CODE}] Mapped ORTB Request from`, oneBidRequest, ' to ', ortbRequest, ' with bidderRequest ', bidderRequest); return { method: METHOD, url: endpointUrl, diff --git a/test/spec/modules/defineMediaBidAdapter_spec.js b/test/spec/modules/defineMediaBidAdapter_spec.js index bf26b54d34e..1f461526780 100755 --- a/test/spec/modules/defineMediaBidAdapter_spec.js +++ b/test/spec/modules/defineMediaBidAdapter_spec.js @@ -1,82 +1,373 @@ // jshint esversion: 6, es3: false, node: true -import {assert, expect} from 'chai'; -import {getStorageManager} from 'src/storageManager.js'; -import {spec} from 'modules/defineMediaBidAdapter.js'; +import { assert } from 'chai'; +import { spec } from 'modules/defineMediaBidAdapter.js'; +import { deepClone } from 'src/utils.js'; + +const BIDDER_CODE = 'defineMedia'; describe('Define Media Bid Adapter', function () { - const mockValidBidParams = [ + const mockValidBids = [ { - 'bidder': 'defineMedia', - 'params': { - 'supplierDomainName': 'definemedia.de', - 'devMode': true - } + "bidder": "defineMedia", + "params": { + "supplierDomainName": "traffective.com", + "devMode": false + }, + "mediaTypes": { + "banner": { + "sizes": [ + [ + 1, + 1 + ], + [ + 300, + 250 + ] + ] + } + }, + "adUnitCode": "custom-adunit-code", + "transactionId": "9af02bbf-558f-4328-a7b3-0b67bac44dbc", + "adUnitId": "e9a971c1-7ce9-4bcf-8b64-611e79f6e35c", + "sizes": [ + [ + 1, + 1 + ], + [ + 300, + 250 + ] + ], + "bidId": "464ae0039a4147", + "bidderRequestId": "3a7736f5f19f638", + "auctionId": "586233c7-4e5d-4231-9f06-b1ff37b0db53", + "src": "client", + "auctionsCount": 1, + "bidRequestsCount": 22, + "bidderRequestsCount": 1, + "bidderWinsCount": 0, + "deferBilling": false, }, { - 'bidder': 'defineMedia', - 'mediaTypes': ['banner'], - 'params': { - 'supplierDomainName': 'definemedia.de', - 'devMode': false - } - } - ]; - - const mockInvalidBidParams = [ - { - 'bidder': 'defineMedia', - 'params': {} + "bidder": "defineMedia", + "params": { + "supplierDomainName": "traffective.com", + "devMode": false + }, + "mediaTypes": { + "banner": { + "sizes": [ + [ + 300, + 250 + ], + [ + 1, + 1 + ] + ] + } + }, + "adUnitCode": "custim-adunit-code-2", + "transactionId": "3f7fa504-f29f-49cc-8edb-31f8b404e27f", + "adUnitId": "1e5fdfe3-b5c7-4dd4-83d1-770bce897773", + "sizes": [ + [ + 300, + 250 + ], + [ + 1, + 1 + ] + ], + "bidId": "53836dbf7d7aac8", + "bidderRequestId": "3a7736f5f19f638", + "auctionId": "586233c7-4e5d-4231-9f06-b1ff37b0db53", + "src": "client", + "auctionsCount": 1, + "bidRequestsCount": 19, + "bidderRequestsCount": 1, + "bidderWinsCount": 0, + "deferBilling": false, } - ]; - - const mockBidRequests = [{ - bidId: 'bidId', - params: { - 'supplierDomainName': 'definemedia.de', - 'devMode': false + ] + + const mockBidderRequest = { + "bidderCode": "defineMedia", + "auctionId": "586233c7-4e5d-4231-9f06-b1ff37b0db53", + "bidderRequestId": "3a7736f5f19f638", + "bids": mockValidBids, + "auctionStart": 1753448647982, + "timeout": 1500, + "refererInfo": { + "reachedTop": true, + "isAmp": false, + "numIframes": 0, + "stack": [], + "topmostLocation": "https://www.any-random-page.com/", + "location": "https://www.any-random-page.com/", + "canonicalUrl": "https://www.any-random-page.com/", + "page": "https://www.any-random-page.com/", + "domain": "www.any-random-page.com", + "ref": "https://www.any-random-page.com/", + "legacy": { + "reachedTop": true, + "isAmp": false, + "numIframes": 0, + "stack": [], + "referer": "https://www.any-random-page.com/", + "canonicalUrl": "https://www.any-random-page.com/" + } }, - mediaType: { - banner: {} - } - }]; - + "ortb2": { + "source": {}, + "site": { + "domain": "any-random-page.com", + "publisher": { + "domain": "any-random-page.com" + }, + "page": "https://www.any-random-page.com/", + "ref": "https://www.any-random-page.com/", + "content": { + "data": [ + { + "name": "articlegenius.ai", + "ext": { + "segtax": 7 + }, + "segment": [ + { + "id": "186" + }, + { + "id": "387" + }, + { + "id": "400" + } + ] + } + ], + "language": "de" + }, + "ext": { + "data": { + "pagetype": "article", + "category": "localnews", + "documentLang": "de", + "adg_rtd": { + "uid": "c7910f59-446a-4786-8826-8181e884afd6", + "pageviewId": "915818a5-73f2-4efb-8eff-dd312755dd4a", + "features": { + "page_dimensions": "1235x6597", + "viewport_dimensions": "1250x959", + "user_timestamp": "1753455847", + "dom_loading": "204" + }, + "session": { + "rnd": 0.8341928086704196, + "pages": 4, + "new": false, + "vwSmplg": 0.1, + "vwSmplgNxt": 0.05, + "expiry": 1753450360922, + "lastActivityTime": 1753448560922, + "id": "bd8b3c7a-ff7f-4433-a5fd-a06cf0fa6e1f" + } + } + } + } + }, + "regs": { + "ext": { + "gdpr": 1 + } + }, + "user": { + "ext": { + "consent": "RANDOMCONSENTSTRING", + "eids": [ + { + "source": "criteo.com", + "uids": [ + { + "id": "random-id", + "atype": 1 + } + ] + } + ] + } + }, + "ext": { + "prebid": { + "adServerCurrency": "EUR" + } + }, + "device": { + "w": 1920, + "h": 1080, + "dnt": 0, + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36", + "language": "de", + "ext": { + "vpw": 1250, + "vph": 959 + }, + "sua": { + "source": 1, + "platform": { + "brand": "Linux" + }, + "browsers": [ + { + "brand": "Google Chrome", + "version": [ + "137" + ] + }, + { + "brand": "Chromium", + "version": [ + "137" + ] + }, + { + "brand": "Not/A)Brand", + "version": [ + "24" + ] + } + ], + "mobile": 0 + } + } + }, + "gdprConsent": { + "consentString": "RANDOMCONSENTSTRING", + "vendorData": { + "cmpId": 21, + "cmpVersion": 2, + "gdprApplies": true, + "tcfPolicyVersion": 5, + "tcString": "RANDOMTCSTRING", + "listenerId": 12, + "eventStatus": "tcloaded", + "cmpStatus": "loaded", + "isServiceSpecific": true, + "useNonStandardTexts": false, + "publisherCC": "DE", + "purposeOneTreatment": false, + "outOfBand": { + "allowedVendors": {}, + "disclosedVendors": {} + }, + "purpose": { + "consents": { + "1": true, + "2": true, + "3": true, + "4": true, + "5": true, + "6": true, + "7": true, + "8": true, + "9": true, + "10": true, + "11": true + }, + "legitimateInterests": { + "1": false, + "2": true, + "3": false, + "4": false, + "5": false, + "6": false, + "7": true, + "8": true, + "9": true, + "10": true, + "11": true + } + }, + "vendor": { + "consents": { + "755": true, + }, + "legitimateInterests": { + "755": true, + } + }, + "specialFeatureOptins": { + "1": true, + "2": true + }, + "publisher": { + "consents": {}, + "legitimateInterests": {}, + "customPurpose": { + "consents": {}, + "legitimateInterests": {} + }, + "restrictions": {} + }, + "opencmp": { + "consentType": "tcf", + "googleConsent": { + "ad_storage": "granted", + "ad_user_data": "granted", + "ad_personalization": "granted", + "analytics_storage": "granted" + } + }, + "addtlConsent": "2~89~dv.", + "customVendors": { + "consents": { + "45": true + }, + "legitimateInterests": { + "45": false + } + } + }, + "gdprApplies": true, + "apiVersion": 2, + "addtlConsent": "2~89~dv." + }, + "start": 1753448648053 + } describe('isBidRequestValid', function () { it('should return true when required params found', function () { - for (const bidRequest of mockValidBidParams) { + for (const bidRequest of mockValidBids) { assert.isTrue(spec.isBidRequestValid(bidRequest)); } }); it('should return false when supplierDomainName is not set', function () { - for (const bidRequest of mockInvalidBidParams) { + let invalidBids = deepClone(mockValidBids); + for (const bidRequest of invalidBids) { + bidRequest.params = {}; assert.isFalse(spec.isBidRequestValid(bidRequest)); } }); }); - describe('buildRequests', function () { - const mockBidderRequest = { - refererInfo: { - referer: 'page' - } - } - - it('should send request with correct structure', function () { - let requests = spec.buildRequests(mockBidRequests, mockBidderRequest); - + let requests = spec.buildRequests(mockValidBids, mockBidderRequest); for (const request of requests) { assert.equal(request.method, 'POST'); assert.ok(request.data); } }); - it('should have default request structure', function () { - let keys = 'id,imp,source'.split(','); - let requests = spec.buildRequests(mockBidRequests, mockBidderRequest); + let keys = 'id,imp,site,source,device'.split(','); + let requests = spec.buildRequests(mockValidBids, mockBidderRequest); for (const request of requests) { let data = Object.keys(request.data); @@ -86,119 +377,439 @@ describe('Define Media Bid Adapter', function () { it('Verify the site url', function () { let siteUrl = 'https://www.yourdomain.tld/your-directory/'; - let bidderRequest = JSON.parse(JSON.stringify(mockBidderRequest)); - let validBidRequests = JSON.parse(JSON.stringify(mockBidRequests)); + let bidderRequest = deepClone(mockBidderRequest); - for (let req of validBidRequests) { - req.params.url = siteUrl; - } + bidderRequest.ortb2.site.page = siteUrl; + bidderRequest.refererInfo.page = siteUrl; - bidderRequest.refererInfo.referer = siteUrl; - let requests = spec.buildRequests(validBidRequests, bidderRequest); + let requests = spec.buildRequests(mockValidBids, bidderRequest); for (const request of requests) { - console.log(JSON.stringify(request.data, null, 2)); assert.equal(request.data.site.page, siteUrl); } }); }); -}); +}) -/*describe('interpretResponse', function () { - const goodBannerResponse = { - body: { - cur: 'EUR', - id: 'bidid1', - seatbid: [ - { - seat: 'seedingAlliance', - bid: [{ - adm: '', - impid: 1, - price: 0.90, - h: 250, - w: 300 - }] +describe('interpretResponse', function () { + const formerBids = [ + { + "bidder": "defineMedia", + "params": { + "supplierDomainName": "traffective.com", + "devMode": false + }, + "mediaTypes": { + "banner": { + "sizes": [ + [ + 1, + 1 + ], + [ + 300, + 250 + ] + ] + }, + }, + "adUnitCode": "custom-adunit-code", + "transactionId": null, + "adUnitId": "1eb2de11-c637-4175-b560-002fc4160841", + "sizes": [ + [ + 1, + 1 + ], + [ + 300, + 250 + ] + ], + "bidId": "8566b4bbc519b58", + "bidderRequestId": "7a7870d573e715", + "auctionId": null, + "src": "client", + "auctionsCount": 1, + "bidRequestsCount": 22, + "bidderRequestsCount": 1, + "bidderWinsCount": 0, + "deferBilling": false, + "ortb2": { + "source": {}, + "site": { + "domain": "any-random-page.com", + "publisher": { + "domain": "any-random-page.com" + }, + "page": "https://www.any-random-page.com/", + "ref": "https://www.any-random-page.com/", + "content": { + "data": [ + { + "name": "articlegenius.ai", + "ext": { + "segtax": 7 + }, + "segment": [ + { + "id": "186" + }, + { + "id": "387" + }, + { + "id": "400" + } + ] + } + ], + "language": "de" + }, + "ext": { + "data": { + "pagetype": "article", + "category": "localnews", + "documentLang": "de" + } + } + }, + "device": { + "w": 1920, + "h": 1080, + "dnt": 0, + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36", + "language": "de", + "ext": { + "vpw": 1250, + "vph": 959 + }, + "sua": { + "source": 1, + "platform": { + "brand": "Linux" + }, + "browsers": [ + { + "brand": "Google Chrome", + "version": [ + "137" + ] + }, + { + "brand": "Chromium", + "version": [ + "137" + ] + }, + { + "brand": "Not/A)Brand", + "version": [ + "24" + ] + } + ], + "mobile": 0 + } } - ] + } } - }; - - const badResponse = { - body: { - cur: 'EUR', - id: 'bidid1', - seatbid: [] + ] + const formerBidRequest = { + "bidderCode": "defineMedia", + "auctionId": "0720d855-f13d-41b7-b5cf-41d6c89454af", + "bidderRequestId": "7a7870d573e715", + "bids": formerBids, + "auctionStart": 1753451739223, + "timeout": 1500, + "refererInfo": { + "reachedTop": true, + "isAmp": false, + "numIframes": 0, + "stack": [ + "https://www.any-random-page.com/" + ], + "topmostLocation": "https://www.any-random-page.com/", + "location": "https://www.any-random-page.com/", + "canonicalUrl": "https://www.any-random-page.com/", + "page": "https://www.any-random-page.com/", + "domain": "www.any-random-page.com", + "ref": "https://www.any-random-page.com/", + "legacy": { + "reachedTop": true, + "isAmp": false, + "numIframes": 0, + "stack": [ + "https://www.any-random-page.com/" + ], + "referer": "https://www.any-random-page.com/", + "canonicalUrl": "https://www.any-random-page.com" + } + }, + "ortb2": { + "source": {}, + "site": { + "domain": "any-random-page.com", + "publisher": { + "domain": "any-random-page.com" + }, + "page": "https://www.any-random-page.com/", + "ref": "https://www.any-random-page.com/", + "content": { + "data": [ + { + "name": "articlegenius.ai", + "ext": { + "segtax": 7 + }, + "segment": [ + { + "id": "186" + }, + { + "id": "387" + }, + { + "id": "400" + } + ] + } + ], + "language": "de" + }, + "ext": { + "data": { + "pagetype": "article", + "category": "localnews", + "documentLang": "de" + } + } + }, + "device": { + "w": 1920, + "h": 1080, + "dnt": 0, + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36", + "language": "de", + "ext": { + "vpw": 1250, + "vph": 959 + }, + "sua": { + "source": 1, + "platform": { + "brand": "Linux" + }, + "browsers": [ + { + "brand": "Google Chrome", + "version": [ + "137" + ] + }, + { + "brand": "Chromium", + "version": [ + "137" + ] + }, + { + "brand": "Not/A)Brand", + "version": [ + "24" + ] + } + ], + "mobile": 0 + } + } + }, + "start": 1753451739307 + } + + const goodBannerRequest = { + "imp": [ + { + "id": "8566b4bbc519b58", + "banner": { + "topframe": 0, + "format": [ + { + "w": 1, + "h": 1 + }, + { + "w": 300, + "h": 250 + } + ] + }, + "secure": 1, + } + ], + "source": { + "schain": { + "complete": 1, + "nodes": [ + { + "asi": "traffective.com" + } + ] + } + }, + "site": { + "domain": "any-random-page.com", + "publisher": { + "domain": "any-random-page.com" + }, + "page": "https://www.any-random-page.com/", + "ref": "https://www.any-random-page.com/", + "content": { + "data": [ + { + "name": "articlegenius.ai", + "ext": { + "segtax": 7 + }, + "segment": [ + { + "id": "186" + }, + { + "id": "387" + }, + { + "id": "400" + } + ] + } + ], + "language": "de" + }, + "ext": { + "data": { + "pagetype": "article", + "category": "localnews", + "documentLang": "de" + } + } + }, + "device": { + "w": 1920, + "h": 1080, + "dnt": 0, + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36", + "language": "de", + "ext": { + "vpw": 1250, + "vph": 959 + }, + "sua": { + "source": 1, + "platform": { + "brand": "Linux" + }, + "browsers": [ + { + "brand": "Google Chrome", + "version": [ + "137" + ] + }, + { + "brand": "Chromium", + "version": [ + "137" + ] + }, + { + "brand": "Not/A)Brand", + "version": [ + "24" + ] + } + ], + "mobile": 0 + } + }, + // "id": "15009fd8-a057-458f-9819-5ddcbf474cfe", //this is a random uuid, so it is not set here + "test": 0, + "tmax": 1500 + } + const goodBannerResponse = { + // "id": "15009fd8-a057-458f-9819-5ddcbf474cfe", //this is a random uuid, so it is not set here + "seatbid": [ + { + "bid": [ + { + "id": "23da7c99-8945-4ff9-6426-3da72d25e73a", + "impid": "8566b4bbc519b58", + "price": 1.0, + "burl": "https://somewhere-in-the-internet.com", + "lurl": "https://somewhere-in-the-internet.com", + "adm": "

ad markup

", + "adid": "e44efd3c-0b58-4834-8fc0-d9d3f658fa1c", + "adomain": [ + "definemedia.de" + ], + "crid": "dim_playout$6b3082ae93341939", + "w": 800, + "h": 250, + "mtype": 1, + "ext": { + "prebid": { + "type": "banner" + } + } + } + ], + "seat": "definemedia" + } + ], + "cur": "EUR" + } + const goodInterpretedBannerResponses = [ + { + "mediaType": "banner", + "ad": "

ad markup

", + "requestId": "8566b4bbc519b58", + "seatBidId": "23da7c99-8945-4ff9-6426-3da72d25e73a", + "cpm": 1.0, + "currency": "EUR", + "width": 800, + "height": 250, + "creative_id": "dim_playout$6b3082ae93341939", + "creativeId": "dim_playout$6b3082ae93341939", + "burl": "https://somewhere-in-the-internet.com", + "ttl": 1000, + "netRevenue": true, + "meta": { "advertiserDomains": ["definemedia.de"] } } - }; - - const bidBannerRequest = { - data: {}, - bidRequests: [{bidId: '1', sizes: [300, 250]}] - }; - + ] it('should return null if body is missing or empty', function () { - const result = spec.interpretResponse(badResponse, bidBannerRequest); - assert.equal(result.length, 0); - }); -}); - -}) -; - - -/* -describe('interpretResponse', function () { - const goodBannerResponse = { - body: { - cur: 'EUR', - id: 'bidid1', - seatbid: [ - { - seat: 'seedingAlliance', - bid: [{ - adm: '', - impid: 1, - price: 0.90, - h: 250, - w: 300 - }] - } - ] + let serverResponse = { + body: null + } + let request = { + data: deepClone(goodBannerRequest) } - }; - - const badResponse = { body: { - cur: 'EUR', - id: 'bidid1', - seatbid: [] - }}; - - const bidBannerRequest = { - data: {}, - bidRequests: [{bidId: '1', sizes: [300, 250]}] - }; - it('should return null if body is missing or empty', function () { - const result = spec.interpretResponse(badResponse, bidBannerRequest); + const result = spec.interpretResponse(serverResponse, request); assert.equal(result.length, 0); }); it('should return the correct params', function () { - const resultBanner = spec.interpretResponse(goodBannerResponse, bidBannerRequest); - - assert.deepEqual(resultBanner[0].mediaType, 'banner'); - assert.deepEqual(resultBanner[0].width, bidBannerRequest.bidRequests[0].sizes[0]); - assert.deepEqual(resultBanner[0].height, bidBannerRequest.bidRequests[0].sizes[1]); - }); - - it('should return the correct banner content', function () { - const result = spec.interpretResponse(goodBannerResponse, bidBannerRequest); - const bid = goodBannerResponse.body.seatbid[0].bid[0]; - const regExpContent = new RegExp(''); + const computedRequest = spec.buildRequests(formerBids, formerBidRequest)[0] + let computedRequestExpected = deepClone(computedRequest.data); + assert.deepInclude(computedRequestExpected, goodBannerRequest) + let serverResponse = { + body: deepClone(goodBannerResponse) + } + const result = spec.interpretResponse(serverResponse, computedRequest); + assert.notEqual(result, null); - assert.ok(result[0].ad.search(regExpContent) > -1); + const bid = result[0].cpm + assert.isAbove(bid, 0.01, "Bid price should be higher 0.0"); + assert.deepInclude(result[0], goodInterpretedBannerResponses[0]) }); -}); }) -;*/ From 43b34c1d2890af3fd33cc4dbbac358c45959f82d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Jo=CC=88st?= Date: Fri, 1 Aug 2025 16:20:59 +0200 Subject: [PATCH 08/19] cleanup debug scripts --- integrationExamples/aws_push.sh | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 integrationExamples/aws_push.sh diff --git a/integrationExamples/aws_push.sh b/integrationExamples/aws_push.sh deleted file mode 100644 index 9f933bf9857..00000000000 --- a/integrationExamples/aws_push.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -aws s3 cp ./gpt/definemedia_hello_world.html s3://conative-testpages/test.conative.de/prebid_js/definemedia_hello_world.html -aws s3 cp ./gpt/definemedia_integration-test.html s3://conative-testpages/test.conative.de/prebid_js/definemedia_integration-test.html - -aws s3 cp ../build s3://conative-testpages/test.conative.de/build --recursive From 0a7ccd947469eb66f8b4c05f8bb6c278c8270e2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Jo=CC=88st?= Date: Fri, 1 Aug 2025 16:23:24 +0200 Subject: [PATCH 09/19] Fix/Delete testfiles --- .../gpt/definemedia_hello_world.html | 84 ---------------- integrationExamples/gpt/hello_world.html | 99 ------------------- modules.json | 3 - 3 files changed, 186 deletions(-) delete mode 100644 integrationExamples/gpt/definemedia_hello_world.html delete mode 100644 integrationExamples/gpt/hello_world.html delete mode 100644 modules.json diff --git a/integrationExamples/gpt/definemedia_hello_world.html b/integrationExamples/gpt/definemedia_hello_world.html deleted file mode 100644 index 67244456bd1..00000000000 --- a/integrationExamples/gpt/definemedia_hello_world.html +++ /dev/null @@ -1,84 +0,0 @@ - - - - - Prebid.js Banner gpt Example - - - - - - - - - - -

Prebid.js Test

-
Div-1
-
- -
- - - diff --git a/integrationExamples/gpt/hello_world.html b/integrationExamples/gpt/hello_world.html deleted file mode 100644 index e45c6605bdb..00000000000 --- a/integrationExamples/gpt/hello_world.html +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - - - - - - - - -

Prebid.js Test

-
Div-1
-
- -
- - - diff --git a/modules.json b/modules.json deleted file mode 100644 index f3c7d945d1e..00000000000 --- a/modules.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - "defineMediaBidAdapter" -] From 54cb79eebb34d18171cc486d019a8d64d39e9a5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Jo=CC=88st?= Date: Fri, 1 Aug 2025 16:25:43 +0200 Subject: [PATCH 10/19] Change maintainer --- modules/defineMediaBidAdapter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/defineMediaBidAdapter.md b/modules/defineMediaBidAdapter.md index 838d4368ffc..8d37283264e 100644 --- a/modules/defineMediaBidAdapter.md +++ b/modules/defineMediaBidAdapter.md @@ -3,7 +3,7 @@ ``` Module Name: Define Media Bid Adapter Module Type: Bidder Adapter -Maintainer: n.leidig@definemedia.de +Maintainer: m.klumpp@definemedia.de ``` # Description From fea656ec86500f540ab0340002f600ec69d6b71f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Jo=CC=88st?= Date: Fri, 1 Aug 2025 16:36:43 +0200 Subject: [PATCH 11/19] Restore helo world --- integrationExamples/gpt/hello_world.html | 99 ++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 integrationExamples/gpt/hello_world.html diff --git a/integrationExamples/gpt/hello_world.html b/integrationExamples/gpt/hello_world.html new file mode 100644 index 00000000000..a7d69d21788 --- /dev/null +++ b/integrationExamples/gpt/hello_world.html @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + +

Prebid.js Test

+
Div-1
+
+ +
+ + + From be6035690f0f626913abdbe2659b1a4748c29d6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Jo=CC=88st?= Date: Fri, 8 Aug 2025 10:46:41 +0200 Subject: [PATCH 12/19] Leave out additional options fot http to rely on default and avoid additional cors requests --- modules/defineMediaBidAdapter.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/defineMediaBidAdapter.js b/modules/defineMediaBidAdapter.js index f13d76cdf16..2dd1c88e3f8 100644 --- a/modules/defineMediaBidAdapter.js +++ b/modules/defineMediaBidAdapter.js @@ -54,10 +54,6 @@ export const spec = { method: METHOD, url: endpointUrl, data: ortbRequest, - options: { - contentType: 'application/json', - withCredentials: false - } } }); }, From 43e839adddeb523e13409140c02b5c8a1f09dfc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Jo=CC=88st?= Date: Fri, 8 Aug 2025 10:54:16 +0200 Subject: [PATCH 13/19] gvl id fix --- modules/defineMediaBidAdapter.js | 2 +- test/spec/modules/defineMediaBidAdapter_spec.js | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/defineMediaBidAdapter.js b/modules/defineMediaBidAdapter.js index 2dd1c88e3f8..887565bfc30 100644 --- a/modules/defineMediaBidAdapter.js +++ b/modules/defineMediaBidAdapter.js @@ -5,7 +5,7 @@ import {ortbConverter} from '../libraries/ortbConverter/converter.js' import { ajax } from '../src/ajax.js'; const BIDDER_CODE = 'defineMedia'; -const IAB_GVL_ID = 755; +const IAB_GVL_ID = 440; const SUPPORTED_MEDIA_TYPES = [BANNER]; const ENDPOINT_URL_DEV = 'https://rtb-dev.conative.network/openrtb2/auction'; diff --git a/test/spec/modules/defineMediaBidAdapter_spec.js b/test/spec/modules/defineMediaBidAdapter_spec.js index 1f461526780..a2adc024526 100755 --- a/test/spec/modules/defineMediaBidAdapter_spec.js +++ b/test/spec/modules/defineMediaBidAdapter_spec.js @@ -3,8 +3,6 @@ import { assert } from 'chai'; import { spec } from 'modules/defineMediaBidAdapter.js'; import { deepClone } from 'src/utils.js'; -const BIDDER_CODE = 'defineMedia'; - describe('Define Media Bid Adapter', function () { const mockValidBids = [ { From 6e16b5a33b1f05a72f2befbd23a5ce101a7fb8e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Jo=CC=88st?= Date: Fri, 8 Aug 2025 11:00:47 +0200 Subject: [PATCH 14/19] Only import used utils by name --- modules/defineMediaBidAdapter.js | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/modules/defineMediaBidAdapter.js b/modules/defineMediaBidAdapter.js index 887565bfc30..554bc00355d 100644 --- a/modules/defineMediaBidAdapter.js +++ b/modules/defineMediaBidAdapter.js @@ -1,4 +1,5 @@ -import * as utils from '../src/utils.js'; +import {logInfo, logError, logWarn, deepSetValue } from "../src/utils"; + import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import {ortbConverter} from '../libraries/ortbConverter/converter.js' @@ -27,9 +28,9 @@ export const spec = { isBidRequestValid: (bid) => { const hasSupplierDomainName = Boolean(bid?.params?.supplierDomainName); const isDevMode = Boolean(bid?.params?.devMode); - utils.logInfo(`[${BIDDER_CODE}] isBidRequestValid called with:`, { bid, hasSupplierDomainName, isDevMode }); + logInfo(`[${BIDDER_CODE}] isBidRequestValid called with:`, { bid, hasSupplierDomainName, isDevMode }); const isValid = hasSupplierDomainName; - utils.logInfo(`[${BIDDER_CODE}] isBidRequestValid returned:`, isValid); + logInfo(`[${BIDDER_CODE}] isBidRequestValid returned:`, isValid); return isValid; }, @@ -46,10 +47,10 @@ export const spec = { const isDevMode = Boolean(params?.devMode); const endpointUrl = isDevMode ? ENDPOINT_URL_DEV : ENDPOINT_URL_PROD; - utils.deepSetValue(ortbRequest, 'source.schain.complete', 1); - utils.deepSetValue(ortbRequest, 'source.schain.nodes.0.asi', '' + params.supplierDomainName); + deepSetValue(ortbRequest, 'source.schain.complete', 1); + deepSetValue(ortbRequest, 'source.schain.nodes.0.asi', '' + params.supplierDomainName); - utils.logInfo(`[${BIDDER_CODE}] Mapped ORTB Request from`, oneBidRequest, ' to ', ortbRequest, ' with bidderRequest ', bidderRequest); + logInfo(`[${BIDDER_CODE}] Mapped ORTB Request from`, oneBidRequest, ' to ', ortbRequest, ' with bidderRequest ', bidderRequest); return { method: METHOD, url: endpointUrl, @@ -59,10 +60,10 @@ export const spec = { }, interpretResponse: (serverResponse, request) => { - utils.logInfo(`[${BIDDER_CODE}] interpretResponse called with:`, { serverResponse, request }); + logInfo(`[${BIDDER_CODE}] interpretResponse called with:`, { serverResponse, request }); if (!serverResponse?.body) { - utils.logWarn(`[${BIDDER_CODE}] No response body received`); + logWarn(`[${BIDDER_CODE}] No response body received`); return []; } @@ -70,25 +71,25 @@ export const spec = { }, onTimeout: (timeoutData) => { - utils.logInfo(`[${BIDDER_CODE}] onTimeout called with:`, timeoutData); + logInfo(`[${BIDDER_CODE}] onTimeout called with:`, timeoutData); }, onBidWon: (bid) => { if (bid?.burl) { ajax(bid.burl, null, null); } - utils.logInfo(`[${BIDDER_CODE}] onBidWon called with bid:`, bid); + logInfo(`[${BIDDER_CODE}] onBidWon called with bid:`, bid); }, onBidderError: ({ error, bidderRequest }) => { /* if (bid?.lurl) { ajax(bid.lurl, null, null); } */ - utils.logError(`[${BIDDER_CODE}] onBidderError called with:`, { error, bidderRequest }); + logError(`[${BIDDER_CODE}] onBidderError called with:`, { error, bidderRequest }); }, onAdRenderSucceeded: (bid) => { - utils.logInfo(`[${BIDDER_CODE}] onAdRenderSucceeded called with bid:`, bid); + logInfo(`[${BIDDER_CODE}] onAdRenderSucceeded called with bid:`, bid); } }; From 7df1d3ba97fa610f8afb3bc7362e33577ea5b005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Jo=CC=88st?= Date: Fri, 8 Aug 2025 11:03:10 +0200 Subject: [PATCH 15/19] Remove accidential commited test code for XHR --- gulpfile.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index c3d6d698386..57c67cccd1e 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -473,10 +473,6 @@ function startLocalServer(options = {}) { return [ function (req, res, next) { res.setHeader('Ad-Auction-Allowed', 'True'); - res.setHeader('Access-Control-Allow-Origin', '*'); // Allow all origins or specify your domain - res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE'); - res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); - res.setHeader('Access-Control-Allow-Credentials', true); next(); } ]; From b00477bf46e7cc285b442ba63a27fec2203e53ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Jo=CC=88st?= Date: Fri, 8 Aug 2025 11:10:25 +0200 Subject: [PATCH 16/19] small linter error --- modules/defineMediaBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/defineMediaBidAdapter.js b/modules/defineMediaBidAdapter.js index 554bc00355d..db17b74070f 100644 --- a/modules/defineMediaBidAdapter.js +++ b/modules/defineMediaBidAdapter.js @@ -1,4 +1,4 @@ -import {logInfo, logError, logWarn, deepSetValue } from "../src/utils"; +import {logInfo, logError, logWarn, deepSetValue } from "../src/utils.js"; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; From 8d8ed53113704bee7f88af33b4f5106763c5d67e Mon Sep 17 00:00:00 2001 From: Michael Klumpp Date: Tue, 23 Sep 2025 12:29:10 +0200 Subject: [PATCH 17/19] 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) --- modules/defineMediaBidAdapter.js | 2 ++ modules/defineMediaBidAdapter.md | 42 +++++++++++++++++++++++++++----- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/modules/defineMediaBidAdapter.js b/modules/defineMediaBidAdapter.js index db17b74070f..f2ddfa12cd3 100644 --- a/modules/defineMediaBidAdapter.js +++ b/modules/defineMediaBidAdapter.js @@ -47,6 +47,8 @@ export const spec = { const isDevMode = Boolean(params?.devMode); const endpointUrl = isDevMode ? ENDPOINT_URL_DEV : ENDPOINT_URL_PROD; + // use supplierDomainName purely as schain 'asi' identifier for our supply path; + // no sellers.json is expected on the publisher side deepSetValue(ortbRequest, 'source.schain.complete', 1); deepSetValue(ortbRequest, 'source.schain.nodes.0.asi', '' + params.supplierDomainName); diff --git a/modules/defineMediaBidAdapter.md b/modules/defineMediaBidAdapter.md index 8d37283264e..7930446e305 100644 --- a/modules/defineMediaBidAdapter.md +++ b/modules/defineMediaBidAdapter.md @@ -8,12 +8,42 @@ Maintainer: m.klumpp@definemedia.de # Description -This is the official Define Media Bid Adapter for Prebid.js. An open-source project maintained by Define Media. -This adapter only supports Banner Ads at the moment. In the backend we use our own RTB-Server to deliver the ads. +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 | The domain name of the last supplier in the chain. Under this domain a sellers.json must be available under https://${supplierDomainName}/sellers.json | definemedia.de -| `devMode` | optional | boolean | This parameter enables our development endpoint instead of the production endpoint. All requests done with this parameterer set to "true" are *NOT* billable | true +| 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. From 157de70843999ff5801a210145a6e779ed2d3362 Mon Sep 17 00:00:00 2001 From: Michael Klumpp Date: Tue, 7 Oct 2025 14:54:13 +0200 Subject: [PATCH 18/19] Define Media Bid Adapter: initial release (#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 --- modules/defineMediaBidAdapter.js | 226 ++++++++++++++++++++++++++++--- 1 file changed, 204 insertions(+), 22 deletions(-) diff --git a/modules/defineMediaBidAdapter.js b/modules/defineMediaBidAdapter.js index f2ddfa12cd3..a3ebf2fc05e 100644 --- a/modules/defineMediaBidAdapter.js +++ b/modules/defineMediaBidAdapter.js @@ -1,22 +1,42 @@ -import {logInfo, logError, logWarn, deepSetValue } from "../src/utils.js"; +/** + * 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, deepSetValue } 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'; +// Bidder identification and compliance constants const BIDDER_CODE = 'defineMedia'; -const IAB_GVL_ID = 440; -const SUPPORTED_MEDIA_TYPES = [BANNER]; +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) -const ENDPOINT_URL_DEV = 'https://rtb-dev.conative.network/openrtb2/auction'; -const ENDPOINT_URL_PROD = 'https://rtb.conative.network/openrtb2/auction'; -const METHOD = 'POST'; +// 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: true, - ttl: 1000 + netRevenue: DEFAULT_NET_REVENUE, + ttl: DEFAULT_TTL } }); @@ -25,74 +45,236 @@ export const spec = { 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:`, { bid, hasSupplierDomainName, isDevMode }); - const isValid = hasSupplierDomainName; + + 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))]; - const ortbRequest = converter.toORTB({ + + // 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 }); - const params = oneBidRequest[0].params; - const isDevMode = Boolean(params?.devMode); + // Select endpoint based on development mode flag const endpointUrl = isDevMode ? ENDPOINT_URL_DEV : ENDPOINT_URL_PROD; - // use supplierDomainName purely as schain 'asi' identifier for our supply path; - // no sellers.json is expected on the publisher side - deepSetValue(ortbRequest, 'source.schain.complete', 1); - deepSetValue(ortbRequest, 'source.schain.nodes.0.asi', '' + params.supplierDomainName); + // 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 []; } - return converter.fromORTB({response: serverResponse.body, request: request.data}).bids; + 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 }) => { - /* if (bid?.lurl) { - ajax(bid.lurl, null, null); - } */ - logError(`[${BIDDER_CODE}] onBidderError called with:`, { 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); From 51feaf6f098fbb9a55104b59a30b8615a7f3e937 Mon Sep 17 00:00:00 2001 From: Michael Klumpp Date: Tue, 7 Oct 2025 15:09:05 +0200 Subject: [PATCH 19/19] Define Media Bid Adapter: initial release (#13713) *fix linting errors --- modules/defineMediaBidAdapter.js | 52 ++++++++++++++++---------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/modules/defineMediaBidAdapter.js b/modules/defineMediaBidAdapter.js index a3ebf2fc05e..937ad9fb8d8 100644 --- a/modules/defineMediaBidAdapter.js +++ b/modules/defineMediaBidAdapter.js @@ -1,15 +1,15 @@ /** * 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, deepSetValue } from "../src/utils.js"; +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' @@ -47,15 +47,15 @@ export const spec = { /** * 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 */ @@ -70,14 +70,14 @@ export const spec = { 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:`, { + + logInfo(`[${BIDDER_CODE}] isBidRequestValid called with:`, { bidId: bid.bidId, - hasSupplierDomainName, + hasSupplierDomainName, hasValidMediaType, isDevMode }); - + const isValid = hasSupplierDomainName && hasValidMediaType; logInfo(`[${BIDDER_CODE}] isBidRequestValid returned:`, isValid); return isValid; @@ -85,13 +85,13 @@ export const spec = { /** * 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 @@ -100,14 +100,14 @@ export const spec = { 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: { @@ -115,7 +115,7 @@ export const spec = { ttl: ttl } }); - + // Convert Prebid.js request to OpenRTB format const ortbRequest = dynamicConverter.toORTB({ bidderRequest: bidderRequest, @@ -146,7 +146,7 @@ export const spec = { } logInfo(`[${BIDDER_CODE}] Mapped ORTB Request from`, oneBidRequest, ' to ', ortbRequest, ' with bidderRequest ', bidderRequest); - + return { method: METHOD, url: endpointUrl, @@ -158,13 +158,13 @@ export const spec = { /** * 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 @@ -193,7 +193,7 @@ export const spec = { /** * Handles bid request timeouts * Currently logs timeout events for monitoring and debugging - * + * * @param {Array|Object} timeoutData - Timeout data from Prebid.js */ onTimeout: (timeoutData) => { @@ -202,11 +202,11 @@ export const spec = { /** * 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) => { @@ -219,19 +219,19 @@ export const spec = { /** * 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 @@ -268,7 +268,7 @@ export const spec = { /** * Handles successful ad rendering events * Currently logs render success for analytics and debugging - * + * * @param {Object} bid - The successfully rendered bid object */ onAdRenderSucceeded: (bid) => {