diff --git a/modules/dasBidAdapter.js b/modules/dasBidAdapter.js new file mode 100644 index 00000000000..5d85644531f --- /dev/null +++ b/modules/dasBidAdapter.js @@ -0,0 +1,373 @@ +import { getAllOrtbKeywords } from '../libraries/keywords/keywords.js'; +import { getAdUnitSizes } from '../libraries/sizeUtils/sizeUtils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; +import { deepAccess, safeJSONParse } from '../src/utils.js'; + +const BIDDER_CODE = 'das'; +const GDE_SCRIPT_URL = 'https://ocdn.eu/adp/static/embedgde/latest/bundle.min.js'; +const GDE_PARAM_PREFIX = 'gde_'; +const REQUIRED_GDE_PARAMS = [ + 'gde_subdomena', + 'gde_id', + 'gde_stparam', + 'gde_fastid', + 'gde_inscreen' +]; + +function parseNativeResponse(ad) { + if (!(ad.data?.fields && ad.data?.meta)) { + return false; + } + + const { click, Thirdpartyimpressiontracker, Thirdpartyimpressiontracker2, thirdPartyClickTracker2, imp, impression, impression1, impressionJs1, image, Image, title, leadtext, url, Calltoaction, Body, Headline, Thirdpartyclicktracker, adInfo, partner_logo: partnerLogo } = ad.data.fields; + + const { dsaurl, height, width, adclick } = ad.data.meta; + const emsLink = ad.ems_link; + const link = adclick + (url || click); + const nativeResponse = { + sendTargetingKeys: false, + title: title || Headline || '', + image: { + url: image || Image || '', + width, + height + }, + icon: { + url: partnerLogo || '', + width, + height + }, + clickUrl: link, + cta: Calltoaction || '', + body: leadtext || Body || '', + body2: adInfo || '', + sponsoredBy: deepAccess(ad, 'data.meta.advertiser_name', null) || '', + }; + + nativeResponse.impressionTrackers = [emsLink, imp, impression, impression1, Thirdpartyimpressiontracker, Thirdpartyimpressiontracker2].filter(Boolean); + nativeResponse.javascriptTrackers = [impressionJs1, getGdeScriptUrl(ad.data.fields)].map(url => url ? `` : null).filter(Boolean); + nativeResponse.clickTrackers = [Thirdpartyclicktracker, thirdPartyClickTracker2].filter(Boolean); + + if (dsaurl) { + nativeResponse.privacyLink = dsaurl; + } + + return nativeResponse +} + +function getGdeScriptUrl(adDataFields) { + if (REQUIRED_GDE_PARAMS.every(param => adDataFields[param])) { + const params = new URLSearchParams(); + Object.entries(adDataFields) + .filter(([key]) => key.startsWith(GDE_PARAM_PREFIX)) + .forEach(([key, value]) => params.append(key, value)); + + return `${GDE_SCRIPT_URL}?${params.toString()}`; + } + return null; +} + +function getEndpoint(network) { + return `https://csr.onet.pl/${encodeURIComponent(network)}/bid`; +} + +function parseParams(params, bidderRequest) { + const customParams = {}; + const keyValues = {}; + + if (params.adbeta) { + customParams.adbeta = params.adbeta; + } + + if (params.site) { + customParams.site = params.site; + } + + if (params.area) { + customParams.area = params.area; + } + + if (params.network) { + customParams.network = params.network; + } + + // Custom parameters + if (params.customParams && typeof params.customParams === 'object') { + Object.assign(customParams, params.customParams); + } + + const pageContext = params.pageContext; + if (pageContext) { + // Document URL override + if (pageContext.du) { + customParams.du = pageContext.du; + } + + // Referrer override + if (pageContext.dr) { + customParams.dr = pageContext.dr; + } + + // Document virtual address + if (pageContext.dv) { + customParams.DV = pageContext.dv; + } + + // Keywords + const keywords = getAllOrtbKeywords( + bidderRequest?.ortb2, + pageContext.keyWords, + ); + if (keywords.length > 0) { + customParams.kwrd = keywords.join('+'); + } + + // Local capping + if (pageContext.capping) { + customParams.local_capping = pageContext.capping; + } + + // Key values + if (pageContext.keyValues && typeof pageContext.keyValues === 'object') { + Object.entries(pageContext.keyValues).forEach(([key, value]) => { + keyValues[`kv${key}`] = value; + }); + } + } + + const du = customParams.du || deepAccess(bidderRequest, 'refererInfo.page'); + const dr = customParams.dr || deepAccess(bidderRequest, 'refererInfo.ref'); + + if (du) customParams.du = du; + if (dr) customParams.dr = dr; + + const dsaRequired = deepAccess(bidderRequest, 'ortb2.regs.ext.dsa.required'); + if (dsaRequired !== undefined) { + customParams.dsainfo = dsaRequired; + } + + return { + customParams, + keyValues, + }; +} + +function buildUserIds(customParams) { + const userIds = {}; + if (customParams.lu) { + userIds.lu = customParams.lu; + } + if (customParams.aid) { + userIds.aid = customParams.aid; + } + return userIds; +} + +function getNpaFromPubConsent(pubConsent) { + const params = new URLSearchParams(pubConsent); + return params.get('npa') === '1'; +} + +function buildOpenRTBRequest(bidRequests, bidderRequest) { + const { customParams, keyValues } = parseParams( + bidRequests[0].params, + bidderRequest, + ); + const imp = bidRequests.map((bid) => { + const sizes = getAdUnitSizes(bid); + const imp = { + id: bid.bidId, + tagid: bid.params.slot, + secure: 1, + }; + if (bid.params.slotSequence) { + imp.ext = { + pos: String(bid.params.slotSequence) + } + } + + if (bid.mediaTypes?.banner) { + imp.banner = { + format: sizes.map((size) => ({ + w: size[0], + h: size[1], + })), + }; + } + if (bid.mediaTypes?.native) { + imp.native = { + request: '{}', + ver: '1.2', + }; + } + + return imp; + }); + + const request = { + id: bidderRequest.bidderRequestId, + imp, + site: { + ...bidderRequest.ortb2?.site, + id: customParams.site, + page: customParams.du, + ref: customParams.dr, + ext: { + ...bidderRequest.ortb2?.site?.ext, + area: customParams.area, + kwrd: customParams.kwrd, + dv: customParams.DV + }, + }, + user: { + ext: { + ids: buildUserIds(customParams), + }, + }, + ext: { + network: customParams.network, + keyvalues: keyValues, + }, + at: 1, + tmax: bidderRequest.timeout + }; + + if (customParams.adbeta) { + request.ext.adbeta = customParams.adbeta; + } + + if (bidderRequest.device) { + request.device = bidderRequest.device; + } + + if (bidderRequest.gdprConsent) { + request.user = { + ext: { + npa: getNpaFromPubConsent(customParams.pubconsent), + localcapping: customParams.local_capping, + localadpproduts: customParams.adp_products, + ...request.user.ext, + }, + }; + request.regs = { + gpp: bidderRequest.gdprConsent.consentString, + gdpr: bidderRequest.gdprConsent.gdprApplies ? 1 : 0, + ext: { + dsa: customParams.dsainfo, + }, + } + } + + return request; +} + +function prepareNativeMarkup(bid) { + const parsedNativeMarkup = safeJSONParse(bid.adm) + const ad = { + data: parsedNativeMarkup || {}, + ems_link: bid.ext?.ems_link || '', + }; + const nativeResponse = parseNativeResponse(ad) || {}; + return nativeResponse; +} + +function interpretResponse(serverResponse) { + const bidResponses = []; + const response = serverResponse.body; + + if (!response || !response.seatbid || !response.seatbid.length) { + return bidResponses; + } + + response.seatbid.forEach((seatbid) => { + seatbid.bid.forEach((bid) => { + const bidResponse = { + requestId: bid.impid, + cpm: bid.price, + currency: response.cur || 'USD', + width: bid.w, + height: bid.h, + creativeId: bid.crid || bid.id, + netRevenue: true, + dealId: bid.dealid || null, + actgMatch: bid.ext?.actgMatch || 0, + ttl: 300, + meta: { + advertiserDomains: bid.adomain || [], + }, + }; + + if (bid.mtype === 1) { + bidResponse.mediaType = BANNER; + bidResponse.ad = bid.adm; + } else if (bid.mtype === 4) { + bidResponse.mediaType = NATIVE; + bidResponse.native = prepareNativeMarkup(bid); + delete bidResponse.ad; + } + bidResponses.push(bidResponse); + }); + }); + + return bidResponses; +} + +export const spec = { + code: BIDDER_CODE, + aliases: ['ringieraxelspringer'], + supportedMediaTypes: [BANNER, NATIVE], + + isBidRequestValid: function (bid) { + if (!bid || !bid.params) { + return false; + } + return !!( + bid.params?.network && + bid.params?.site && + bid.params?.area && + bid.params?.slot + ); + }, + + buildRequests: function (validBidRequests, bidderRequest) { + const data = buildOpenRTBRequest(validBidRequests, bidderRequest); + const jsonData = JSON.stringify(data); + const baseUrl = getEndpoint(data.ext.network); + const fullUrl = `${baseUrl}?data=${encodeURIComponent(jsonData)}`; + + // adbeta needs credentials omitted to avoid CORS issues, especially in Firefox + const useCredentials = !data.ext?.adbeta; + + // Switch to POST if URL exceeds 8k characters + if (fullUrl.length > 8192) { + return { + method: 'POST', + url: baseUrl, + data: jsonData, + options: { + withCredentials: useCredentials, + crossOrigin: true, + customHeaders: { + 'Content-Type': 'text/plain' + } + }, + }; + } + + return { + method: 'GET', + url: fullUrl, + options: { + withCredentials: useCredentials, + crossOrigin: true, + }, + }; + }, + + interpretResponse: function (serverResponse) { + return interpretResponse(serverResponse); + }, +}; + +registerBidder(spec); diff --git a/modules/dasBidAdapter.md b/modules/dasBidAdapter.md new file mode 100644 index 00000000000..2b28415df9b --- /dev/null +++ b/modules/dasBidAdapter.md @@ -0,0 +1,52 @@ +# Overview + +``` +Module Name: DAS Bidder Adapter +Module Type: Bidder Adapter +Maintainer: support@ringpublishing.com +``` + +# Description + +Module that connects to DAS demand sources. +Only banner and native format is supported. + +# Test Parameters +```js +var adUnits = [{ + code: 'test-div-ad', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + bids: [{ + bidder: 'das', + params: { + network: '4178463', + site: 'test', + area: 'areatest', + slot: 'slot' + } + }] +}]; +``` + +# Parameters + +| Name | Scope | Type | Description | Example | +|------------------------------|----------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------| +| network | required | String | Specific identifier provided by DAS | `"4178463"` | +| site | required | String | Specific identifier name (case-insensitive) that is associated with this ad unit. Represents the website/domain in the ad unit hierarchy | `"example_com"` | +| area | required | String | Ad unit category name; only case-insensitive alphanumeric with underscores and hyphens are allowed. Represents the content section or category | `"sport"` | +| slot | required | String | Ad unit placement name (case-insensitive) | `"slot"` | +| slotSequence | optional | Number | Ad unit sequence position provided by DAS | `1` | +| pageContext | optional | Object | Web page context data | `{}` | +| pageContext.dr | optional | String | Document referrer URL address | `"https://example.com/"` | +| pageContext.du | optional | String | Document URL address | `"https://example.com/sport/football/article.html?id=932016a5-02fc-4d5c-b643-fafc2f270f06"` | +| pageContext.dv | optional | String | Document virtual address as slash-separated path that may consist of any number of parts (case-insensitive alphanumeric with underscores and hyphens); first part should be the same as `site` value and second as `area` value; next parts may reflect website navigation | `"example_com/sport/football"` | +| pageContext.keyWords | optional | String[] | List of keywords associated with this ad unit; only case-insensitive alphanumeric with underscores and hyphens are allowed | `["euro", "lewandowski"]` | +| pageContext.keyValues | optional | Object | Key-values associated with this ad unit (case-insensitive); following characters are not allowed in the values: `" ' = ! + # * ~ ; ^ ( ) < > [ ] & @` | `{}` | +| pageContext.keyValues.ci | optional | String | Content unique identifier | `"932016a5-02fc-4d5c-b643-fafc2f270f06"` | +| pageContext.keyValues.adunit | optional | String | Ad unit name | `"example_com/sport"` | +| customParams | optional | Object | Custom request params | `{}` | diff --git a/modules/ringieraxelspringerBidAdapter.js b/modules/ringieraxelspringerBidAdapter.js index 10cccecccf4..c5b7e000f87 100644 --- a/modules/ringieraxelspringerBidAdapter.js +++ b/modules/ringieraxelspringerBidAdapter.js @@ -1,402 +1,8 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE } from '../src/mediaTypes.js'; -import { - isEmpty, - parseSizesInput, - deepAccess -} from '../src/utils.js'; -import { getAllOrtbKeywords } from '../libraries/keywords/keywords.js'; -import { getAdUnitSizes } from '../libraries/sizeUtils/sizeUtils.js'; -import { BO_CSR_ONET } from '../libraries/paapiTools/buyerOrigins.js'; - -const BIDDER_CODE = 'ringieraxelspringer'; -const VERSION = '1.0'; - -const getEndpoint = (network) => { - return `https://csr.onet.pl/${encodeURIComponent(network)}/csr-006/csr.json?nid=${encodeURIComponent(network)}&`; -}; - -function parseParams(params, bidderRequest) { - const newParams = {}; - if (params.customParams && typeof params.customParams === 'object') { - for (const param in params.customParams) { - if (params.customParams.hasOwnProperty(param)) { - newParams[param] = params.customParams[param]; - } - } - } - const du = deepAccess(bidderRequest, 'refererInfo.page'); - const dr = deepAccess(bidderRequest, 'refererInfo.ref'); - if (du) { - newParams.du = du; - } - if (dr) { - newParams.dr = dr; - } - const pageContext = params.pageContext; - if (!pageContext) { - return newParams; - } - if (pageContext.du) { - newParams.du = pageContext.du; - } - if (pageContext.dr) { - newParams.dr = pageContext.dr; - } - if (pageContext.dv) { - newParams.DV = pageContext.dv; - } - const keywords = getAllOrtbKeywords(bidderRequest?.ortb2, pageContext.keyWords) - if (keywords.length > 0) { - newParams.kwrd = keywords.join('+') - } - if (pageContext.capping) { - newParams.local_capping = pageContext.capping; - } - if (pageContext.keyValues && typeof pageContext.keyValues === 'object') { - for (const param in pageContext.keyValues) { - if (pageContext.keyValues.hasOwnProperty(param)) { - const kvName = 'kv' + param; - newParams[kvName] = pageContext.keyValues[param]; - } - } - } - if (bidderRequest?.ortb2?.regs?.ext?.dsa?.required !== undefined) { - newParams.dsainfo = bidderRequest?.ortb2?.regs?.ext?.dsa?.required; - } - return newParams; -} - /** - * @param url string - * @param type number // 1 - img, 2 - js - * @returns an object { event: 1, method: 1 or 2, url: 'string' } + * Backward-compatibility shim for ringieraxelspringer bidder. + * This bidder has been renamed to 'das'. + * + * This file will be removed in Prebid 11. + * See dasBidAdapter.js for implementation. */ -function prepareItemEventtrackers(url, type) { - return { - event: 1, - method: type, - url: url - }; -} - -function prepareEventtrackers(emsLink, imp, impression, impression1, impressionJs1) { - const eventtrackers = [prepareItemEventtrackers(emsLink, 1)]; - - if (imp) { - eventtrackers.push(prepareItemEventtrackers(imp, 1)); - } - - if (impression) { - eventtrackers.push(prepareItemEventtrackers(impression, 1)); - } - - if (impression1) { - eventtrackers.push(prepareItemEventtrackers(impression1, 1)); - } - - if (impressionJs1) { - eventtrackers.push(prepareItemEventtrackers(impressionJs1, 2)); - } - - return eventtrackers; -} - -function parseOrtbResponse(ad) { - if (!(ad.data?.fields && ad.data?.meta)) { - return false; - } - - const { image, Image, title, url, Headline, Thirdpartyclicktracker, thirdPartyClickTracker2, imp, impression, impression1, impressionJs1, partner_logo: partnerLogo, adInfo, body } = ad.data.fields; - const { dsaurl, height, width, adclick } = ad.data.meta; - const emsLink = ad.ems_link; - const link = adclick + (url || Thirdpartyclicktracker); - const eventtrackers = prepareEventtrackers(emsLink, imp, impression, impression1, impressionJs1); - const clicktrackers = thirdPartyClickTracker2 ? [thirdPartyClickTracker2] : []; - - const ortb = { - ver: '1.2', - assets: [ - { - id: 0, - data: { - value: body || '', - type: 2 - }, - }, - { - id: 1, - data: { - value: adInfo || '', - // Body2 type - type: 10 - }, - }, - { - id: 3, - img: { - type: 1, - url: partnerLogo || '', - w: width, - h: height - } - }, - { - id: 4, - img: { - type: 3, - url: image || Image || '', - w: width, - h: height - } - }, - { - id: 5, - data: { - value: deepAccess(ad, 'data.meta.advertiser_name', null), - type: 1 - } - }, - { - id: 6, - title: { - text: title || Headline || '' - } - }, - ], - link: { - url: link, - clicktrackers - }, - eventtrackers - }; - - if (dsaurl) { - ortb.privacy = dsaurl - } - - return ortb -} - -function parseNativeResponse(ad) { - if (!(ad.data?.fields && ad.data?.meta)) { - return false; - } - - const { image, Image, title, leadtext, url, Calltoaction, Body, Headline, Thirdpartyclicktracker, adInfo, partner_logo: partnerLogo } = ad.data.fields; - const { dsaurl, height, width, adclick } = ad.data.meta; - const link = adclick + (url || Thirdpartyclicktracker); - const nativeResponse = { - title: title || Headline || '', - image: { - url: image || Image || '', - width, - height - }, - icon: { - url: partnerLogo || '', - width, - height - }, - clickUrl: link, - cta: Calltoaction || '', - body: leadtext || Body || '', - body2: adInfo || '', - sponsoredBy: deepAccess(ad, 'data.meta.advertiser_name', null) || '', - ortb: parseOrtbResponse(ad) - }; - - if (dsaurl) { - nativeResponse.privacyLink = dsaurl; - } - - return nativeResponse -} - -const buildBid = (ad, mediaType) => { - if (ad.type === 'empty' || mediaType === undefined) { - return null; - } - - const data = { - requestId: ad.id, - cpm: ad.bid_rate ? ad.bid_rate.toFixed(2) : 0, - ttl: 300, - creativeId: ad.adid ? parseInt(ad.adid.split(',')[2], 10) : 0, - netRevenue: true, - currency: ad.currency || 'USD', - dealId: ad.prebid_deal || null, - actgMatch: ad.actg_match || 0, - meta: { mediaType: BANNER }, - mediaType: BANNER, - ad: ad.html || null, - width: ad.width || 0, - height: ad.height || 0 - } - - if (mediaType === 'native') { - data.meta = { mediaType: NATIVE }; - data.mediaType = NATIVE; - data.native = parseNativeResponse(ad) || {}; - - delete data.ad; - } - - return data; -}; - -const getContextParams = (bidRequests, bidderRequest) => { - const bid = bidRequests[0]; - const { params } = bid; - const requestParams = { - site: params.site, - area: params.area, - cre_format: 'html', - systems: 'das', - kvprver: VERSION, - ems_url: 1, - bid_rate: 1, - ...parseParams(params, bidderRequest) - }; - return Object.keys(requestParams).map((key) => encodeURIComponent(key) + '=' + encodeURIComponent(requestParams[key])).join('&'); -}; - -const getSlots = (bidRequests) => { - let queryString = ''; - const batchSize = bidRequests.length; - for (let i = 0; i < batchSize; i++) { - const adunit = bidRequests[i]; - const slotSequence = deepAccess(adunit, 'params.slotSequence'); - const creFormat = getAdUnitCreFormat(adunit); - const sizes = creFormat === 'native' ? 'fluid' : parseSizesInput(getAdUnitSizes(adunit)).join(','); - - queryString += `&slot${i}=${encodeURIComponent(adunit.params.slot)}&id${i}=${encodeURIComponent(adunit.bidId)}&composition${i}=CHILD`; - - if (creFormat === 'native') { - queryString += `&cre_format${i}=native`; - } - - queryString += `&kvhb_format${i}=${creFormat === 'native' ? 'native' : 'banner'}`; - - if (sizes) { - queryString += `&iusizes${i}=${encodeURIComponent(sizes)}`; - } - - if (slotSequence !== undefined && slotSequence !== null) { - queryString += `&pos${i}=${encodeURIComponent(slotSequence)}`; - } - } - - return queryString; -}; - -const getGdprParams = (bidderRequest) => { - const gdprApplies = deepAccess(bidderRequest, 'gdprConsent.gdprApplies'); - const consentString = deepAccess(bidderRequest, 'gdprConsent.consentString'); - let queryString = ''; - if (gdprApplies !== undefined) { - queryString += `&gdpr_applies=${encodeURIComponent(gdprApplies)}`; - } - if (consentString !== undefined) { - queryString += `&euconsent=${encodeURIComponent(consentString)}`; - } - return queryString; -}; - -const parseAuctionConfigs = (serverResponse, bidRequest) => { - if (isEmpty(bidRequest)) { - return null; - } - const auctionConfigs = []; - const gctx = serverResponse && serverResponse.body?.gctx; - - bidRequest.bidIds.filter(bid => bid.fledgeEnabled).forEach((bid) => { - auctionConfigs.push({ - 'bidId': bid.bidId, - 'config': { - 'seller': BO_CSR_ONET, - 'decisionLogicUrl': `${BO_CSR_ONET}/${encodeURIComponent(bid.params.network)}/v1/protected-audience-api/decision-logic.js`, - 'interestGroupBuyers': [ BO_CSR_ONET ], - 'auctionSignals': { - 'params': bid.params, - 'sizes': bid.sizes, - 'gctx': gctx - } - } - }); - }); - - if (auctionConfigs.length === 0) { - return null; - } else { - return auctionConfigs; - } -} - -const getAdUnitCreFormat = (adUnit) => { - if (!adUnit) { - return; - } - - let creFormat = 'html'; - const mediaTypes = Object.keys(adUnit.mediaTypes); - - if (mediaTypes && mediaTypes.length === 1 && mediaTypes.includes('native')) { - creFormat = 'native'; - } - - return creFormat; -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, NATIVE], - - isBidRequestValid: function (bidRequest) { - if (!bidRequest || !bidRequest.params || typeof bidRequest.params !== 'object') { - return; - } - const { params } = bidRequest; - return Boolean(params.network && params.site && params.area && params.slot); - }, - - buildRequests: function (bidRequests, bidderRequest) { - const slotsQuery = getSlots(bidRequests); - const contextQuery = getContextParams(bidRequests, bidderRequest); - const gdprQuery = getGdprParams(bidderRequest); - const fledgeEligible = Boolean(bidderRequest?.paapi?.enabled); - const network = bidRequests[0].params.network; - const bidIds = bidRequests.map((bid) => ({ - slot: bid.params.slot, - bidId: bid.bidId, - sizes: getAdUnitSizes(bid), - params: bid.params, - fledgeEnabled: fledgeEligible, - mediaType: (bid.mediaTypes && bid.mediaTypes.banner) ? 'display' : NATIVE - })); - - return [{ - method: 'GET', - url: getEndpoint(network) + contextQuery + slotsQuery + gdprQuery, - bidIds: bidIds - }]; - }, - - interpretResponse: function (serverResponse, bidRequest) { - const response = serverResponse.body; - const fledgeAuctionConfigs = parseAuctionConfigs(serverResponse, bidRequest); - const bids = (!response || !response.ads || response.ads.length === 0) ? [] : response.ads.map((ad, index) => buildBid( - ad, - bidRequest?.bidIds?.[index]?.mediaType || 'banner' - )).filter((bid) => !isEmpty(bid)); - - if (fledgeAuctionConfigs) { - // Return a tuple of bids and auctionConfigs. It is possible that bids could be null. - return {bids, paapi: fledgeAuctionConfigs}; - } else { - return bids; - } - } -}; - -registerBidder(spec); +export { spec } from './dasBidAdapter.js'; // eslint-disable-line prebid/validate-imports diff --git a/modules/ringieraxelspringerBidAdapter.md b/modules/ringieraxelspringerBidAdapter.md index b3a716f9f56..4527d9f8c6d 100644 --- a/modules/ringieraxelspringerBidAdapter.md +++ b/modules/ringieraxelspringerBidAdapter.md @@ -1,52 +1,8 @@ # Overview -``` -Module Name: Ringier Axel Springer Bidder Adapter -Module Type: Bidder Adapter -Maintainer: support@ringpublishing.com -``` +The `ringieraxelspringer` bidder has been renamed to `das`. +Please use the `das` bidder code instead. -# Description +See [dasBidAdapter.md](./dasBidAdapter.md) for documentation. -Module that connects to Ringer Axel Springer demand sources. -Only banner and native format is supported. - -# Test Parameters -```js -var adUnits = [{ - code: 'test-div-ad', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]] - } - }, - bids: [{ - bidder: 'ringieraxelspringer', - params: { - network: '4178463', - site: 'test', - area: 'areatest', - slot: 'slot' - } - }] -}]; -``` - -# Parameters - -| Name | Scope | Type | Description | Example | -|------------------------------|----------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------| -| network | required | String | Specific identifier provided by Ringier Axel Springer | `"4178463"` | -| site | required | String | Specific identifier name (case-insensitive) that is associated with this ad unit and provided by Ringier Axel Springer | `"example_com"` | -| area | required | String | Ad unit category name; only case-insensitive alphanumeric with underscores and hyphens are allowed | `"sport"` | -| slot | required | String | Ad unit placement name (case-insensitive) provided by Ringier Axel Springer | `"slot"` | -| slotSequence | optional | Number | Ad unit sequence position provided by Ringier Axel Springer | `1` | -| pageContext | optional | Object | Web page context data | `{}` | -| pageContext.dr | optional | String | Document referrer URL address | `"https://example.com/"` | -| pageContext.du | optional | String | Document URL address | `"https://example.com/sport/football/article.html?id=932016a5-02fc-4d5c-b643-fafc2f270f06"` | -| pageContext.dv | optional | String | Document virtual address as slash-separated path that may consist of any number of parts (case-insensitive alphanumeric with underscores and hyphens); first part should be the same as `site` value and second as `area` value; next parts may reflect website navigation | `"example_com/sport/football"` | -| pageContext.keyWords | optional | String[] | List of keywords associated with this ad unit; only case-insensitive alphanumeric with underscores and hyphens are allowed | `["euro", "lewandowski"]` | -| pageContext.keyValues | optional | Object | Key-values associated with this ad unit (case-insensitive); following characters are not allowed in the values: `" ' = ! + # * ~ ; ^ ( ) < > [ ] & @` | `{}` | -| pageContext.keyValues.ci | optional | String | Content unique identifier | `"932016a5-02fc-4d5c-b643-fafc2f270f06"` | -| pageContext.keyValues.adunit | optional | String | Ad unit name | `"example_com/sport"` | -| customParams | optional | Object | Custom request params | `{}` | +This adapter will be removed in Prebid 11. diff --git a/test/spec/modules/dasBidAdapter_spec.js b/test/spec/modules/dasBidAdapter_spec.js new file mode 100644 index 00000000000..edb7a85ee0f --- /dev/null +++ b/test/spec/modules/dasBidAdapter_spec.js @@ -0,0 +1,507 @@ +import { expect } from 'chai'; +import { spec } from 'modules/dasBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; + +describe('dasBidAdapter', function () { + const adapter = newBidder(spec); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + const validBid = { + params: { + site: 'site1', + area: 'area1', + slot: 'slot1', + network: 'network1' + } + }; + + it('should return true when required params are present', function () { + expect(spec.isBidRequestValid(validBid)).to.be.true; + }); + + it('should return false when required params are missing', function () { + expect(spec.isBidRequestValid({})).to.be.false; + expect(spec.isBidRequestValid({ params: {} })).to.be.false; + expect(spec.isBidRequestValid({ params: { site: 'site1' } })).to.be.false; + expect(spec.isBidRequestValid({ params: { area: 'area1' } })).to.be.false; + expect(spec.isBidRequestValid({ params: { slot: 'slot1' } })).to.be.false; + }); + + it('should return true with additional optional params', function () { + const bidWithOptional = { + params: { + site: 'site1', + area: 'area1', + slot: 'slot1', + network: 'network1', + customParams: { + param1: 'value1' + }, + pageContext: { + du: 'https://example.com', + dr: 'https://referrer.com', + dv: '1.0', + keyWords: ['key1', 'key2'], + capping: 'cap1', + keyValues: { + key1: 'value1' + } + } + } + }; + expect(spec.isBidRequestValid(bidWithOptional)).to.be.true; + }); + + it('should return false when params is undefined', function () { + expect(spec.isBidRequestValid()).to.be.false; + }); + + it('should return false when required params are empty strings', function () { + const bidWithEmptyStrings = { + params: { + site: '', + area: '', + slot: '' + } + }; + expect(spec.isBidRequestValid(bidWithEmptyStrings)).to.be.false; + }); + + it('should return false when required params are non-string values', function () { + const bidWithNonStringValues = { + params: { + site: 123, + area: true, + slot: {} + } + }; + expect(spec.isBidRequestValid(bidWithNonStringValues)).to.be.false; + }); + + it('should return false when params is null', function () { + const bidWithNullParams = { + params: null + }; + expect(spec.isBidRequestValid(bidWithNullParams)).to.be.false; + }); + + it('should return true with minimal valid params', function () { + const minimalBid = { + params: { + site: 'site1', + area: 'area1', + slot: 'slot1', + network: 'network1' + } + }; + expect(spec.isBidRequestValid(minimalBid)).to.be.true; + }); + + it('should return false with partial required params', function () { + const partialBids = [ + { params: { site: 'site1', area: 'area1' } }, + { params: { site: 'site1', slot: 'slot1' } }, + { params: { area: 'area1', slot: 'slot1' } } + ]; + + partialBids.forEach(bid => { + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); + }); + + describe('buildRequests', function () { + const bidRequests = [{ + bidId: 'bid123', + params: { + site: 'site1', + area: 'area1', + slot: 'slot1', + network: 'network1', + pageContext: { + du: 'https://example.com', + dr: 'https://referrer.com', + dv: '1.0', + keyWords: ['key1', 'key2'], + capping: 'cap1', + keyValues: { key1: 'value1' } + } + }, + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + } + }]; + + const bidderRequest = { + bidderRequestId: 'reqId123', + timeout: 1000, + refererInfo: { + page: 'https://example.com', + ref: 'https://referrer.com' + }, + gdprConsent: { + consentString: 'consent123', + gdprApplies: true + }, + ortb2: { + site: {} + } + }; + + it('should return proper request object', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + + expect(request.method).to.equal('GET'); + expect(request.options.withCredentials).to.be.true; + expect(request.options.crossOrigin).to.be.true; + + expect(request.url).to.include('https://csr.onet.pl/network1/bid?data='); + const urlParts = request.url.split('?'); + expect(urlParts.length).to.equal(2); + + const params = new URLSearchParams(urlParts[1]); + expect(params.has('data')).to.be.true; + + const payload = JSON.parse(decodeURIComponent(params.get('data'))); + expect(payload.id).to.equal('reqId123'); + expect(payload.imp[0].id).to.equal('bid123'); + expect(payload.imp[0].tagid).to.equal('slot1'); + expect(payload.imp[0].banner.format[0]).to.deep.equal({ w: 300, h: 250 }); + }); + + it('should use GET method when URL is under 8192 characters', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.method).to.equal('GET'); + expect(request.url).to.include('?data='); + expect(request.data).to.be.undefined; + }); + + it('should switch to POST method when URL exceeds 8192 characters', function () { + // Create a large bid request that will result in URL > 8k characters + const largeBidRequests = []; + for (let i = 0; i < 50; i++) { + largeBidRequests.push({ + bidId: `bid${i}`.repeat(20), // Make bid IDs longer + params: { + site: `site${i}`.repeat(50), + area: `area${i}`.repeat(50), + slot: `slot${i}`.repeat(50), + network: 'network1', + pageContext: { + du: `https://very-long-url-example-${i}.com`.repeat(10), + dr: `https://very-long-referrer-url-${i}.com`.repeat(10), + dv: `version-${i}`.repeat(20), + keyWords: Array(20).fill(`keyword${i}`), + capping: `cap${i}`.repeat(20), + keyValues: { + [`key${i}`]: `value${i}`.repeat(50) + } + }, + customParams: { + [`param${i}`]: `value${i}`.repeat(50) + } + }, + mediaTypes: { + banner: { + sizes: [[300, 250], [728, 90], [970, 250]] + } + } + }); + } + + const request = spec.buildRequests(largeBidRequests, bidderRequest); + + expect(request.method).to.equal('POST'); + expect(request.url).to.equal('https://csr.onet.pl/network1/bid'); + expect(request.data).to.be.a('string'); + + // Check if data is valid JSON (not URL-encoded form data) + const payload = JSON.parse(request.data); + expect(payload.id).to.equal('reqId123'); + expect(payload.imp).to.be.an('array'); + expect(request.options.customHeaders['Content-Type']).to.equal('text/plain'); + }); + + it('should create valid POST data format', function () { + // Create a request that will trigger POST + const largeBidRequests = Array(50).fill(0).map((_, i) => ({ + bidId: `bid${i}`.repeat(20), + params: { + site: `site${i}`.repeat(50), + area: `area${i}`.repeat(50), + slot: `slot${i}`.repeat(50), + network: 'network1', + pageContext: { + du: `https://very-long-url-example-${i}.com`.repeat(10), + dr: `https://very-long-referrer-url-${i}.com`.repeat(10), + keyWords: Array(10).fill(`keyword${i}`) + } + }, + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + } + })); + + const request = spec.buildRequests(largeBidRequests, bidderRequest); + + expect(request.method).to.equal('POST'); + + // Parse the POST data as JSON (not URL-encoded) + const payload = JSON.parse(request.data); + expect(payload.id).to.equal('reqId123'); + expect(payload.imp).to.be.an('array').with.length(50); + expect(payload.ext.network).to.equal('network1'); + }); + + it('should set withCredentials to false when adbeta flag is present', function () { + const bidRequestsWithAdbeta = [{ + bidId: 'bid123', + params: { + site: 'site1', + area: 'area1', + slot: 'slot1', + network: 'network1', + adbeta: 'l1021885!slot.nativestd', + pageContext: {} + }, + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + } + }]; + + const bidderRequestWithAdbeta = { + bidderRequestId: 'reqId123', + ortb2: {}, + adbeta: true + }; + + const request = spec.buildRequests(bidRequestsWithAdbeta, bidderRequestWithAdbeta); + + expect(request.options.withCredentials).to.be.false; + }); + + describe('interpretResponse', function () { + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: 'bid123', + price: 3.5, + w: 300, + h: 250, + adm: '', + crid: 'crid123', + mtype: 1, + adomain: ['advertiser.com'] + }] + }], + cur: 'USD' + } + }; + + it('should return proper bid response', function () { + const bidResponses = spec.interpretResponse(serverResponse); + + expect(bidResponses).to.be.an('array').with.lengthOf(1); + expect(bidResponses[0]).to.deep.include({ + requestId: 'bid123', + cpm: 3.5, + currency: 'USD', + width: 300, + height: 250, + ad: '', + creativeId: 'crid123', + netRevenue: true, + ttl: 300, + mediaType: 'banner' + }); + expect(bidResponses[0].meta.advertiserDomains).to.deep.equal(['advertiser.com']); + }); + + it('should return empty array when no valid responses', function () { + expect(spec.interpretResponse({ body: null })).to.be.an('array').that.is.empty; + expect(spec.interpretResponse({ body: {} })).to.be.an('array').that.is.empty; + expect(spec.interpretResponse({ body: { seatbid: [] } })).to.be.an('array').that.is.empty; + }); + + it('should return proper bid response for native', function () { + const nativeResponse = { + body: { + seatbid: [{ + bid: [{ + impid: 'bid123', + price: 3.5, + w: 1, + h: 1, + adm: JSON.stringify({ + fields: { + Body: 'Ruszyła sprzedaż mieszkań przy Metrze Dworzec Wileński', + Calltoaction: 'SPRAWDŹ', + Headline: 'Gotowe mieszkania w świetnej lokalizacji (test)', + Image: 'https://ocdn.eu/example.jpg', + Sponsorlabel: 'tak', + Thirdpartyclicktracker: '', + Thirdpartyimpressiontracker: '', + Thirdpartyimpressiontracker2: '', + borderColor: '#CECECE', + click: 'https://mieszkaniaprzymetrodworzec.pl', + responsive: 'nie' + }, + tplCode: '1746213/Native-In-Feed', + meta: { + inIFrame: false, + autoscale: 0, + width: '1', + height: '1', + adid: 'das,1778361,669261', + actioncount: 'https://csr.onet.pl/eclk/...', + slot: 'right2', + adclick: 'https://csr.onet.pl/clk/...', + container_wrapper: '
REKLAMA
', + prebid_native: true + } + }), + mtype: 4 + }] + }], + cur: 'USD' + } + }; + + const bidResponses = spec.interpretResponse(nativeResponse); + + expect(bidResponses).to.be.an('array').with.lengthOf(1); + expect(bidResponses[0]).to.deep.include({ + requestId: 'bid123', + cpm: 3.5, + currency: 'USD', + width: 1, + height: 1, + native: { + title: 'Gotowe mieszkania w świetnej lokalizacji (test)', + body: 'Ruszyła sprzedaż mieszkań przy Metrze Dworzec Wileński', + cta: 'SPRAWDŹ', + image: { + url: 'https://ocdn.eu/example.jpg', + width: '1', + height: '1' + }, + icon: { + url: '', + width: '1', + height: '1' + }, + clickUrl: 'https://csr.onet.pl/clk/...https://mieszkaniaprzymetrodworzec.pl', + body2: '', + sponsoredBy: '', + clickTrackers: [], + impressionTrackers: [], + javascriptTrackers: [], + sendTargetingKeys: false + }, + netRevenue: true, + ttl: 300, + mediaType: 'native' + }); + + expect(bidResponses[0]).to.not.have.property('ad'); + }); + }); + + it('should return empty object when adm in a native response is not JSON', function () { + const nativeResponse = { + body: { + seatbid: [{ + bid: [{ + impid: 'bad1', + price: 2.0, + w: 1, + h: 1, + adm: 'not-a-json-string', + mtype: 4 + }] + }], + cur: 'USD' + } + }; + + const bidResponses = spec.interpretResponse(nativeResponse); + expect(bidResponses[0].native).to.deep.equal({}); + }); + + it('should return empty object when adm in a native response is missing required fields/meta', function () { + const nativeResponse = { + body: { + seatbid: [{ + bid: [{ + impid: 'bad2', + price: 2.0, + w: 1, + h: 1, + adm: JSON.stringify({ fields: {} }), + mtype: 4 + }] + }], + cur: 'USD' + } + }; + + const bidResponses = spec.interpretResponse(nativeResponse); + expect(bidResponses[0].native).to.deep.equal({}); + }); + + it('should return empty object when adm in a native response is empty JSON object', function () { + const nativeResponse = { + body: { + seatbid: [{ + bid: [{ + impid: 'bad3', + price: 2.0, + w: 1, + h: 1, + adm: JSON.stringify({}), + mtype: 4 + }] + }], + cur: 'USD' + } + }; + + const bidResponses = spec.interpretResponse(nativeResponse); + expect(bidResponses[0].native).to.deep.equal({}); + }); + + it('should return empty object when adm in a native response is null or missing', function () { + const nativeResponse = { + body: { + seatbid: [{ + bid: [{ + impid: 'bad4', + price: 2.0, + w: 1, + h: 1, + adm: null, + mtype: 4 + }] + }], + cur: 'USD' + } + }; + + const bidResponses = spec.interpretResponse(nativeResponse); + expect(bidResponses[0].native).to.deep.equal({}); + }); + }); +}); diff --git a/test/spec/modules/ringieraxelspringerBidAdapter_spec.js b/test/spec/modules/ringieraxelspringerBidAdapter_spec.js index 08587e5174f..0318a6987c6 100644 --- a/test/spec/modules/ringieraxelspringerBidAdapter_spec.js +++ b/test/spec/modules/ringieraxelspringerBidAdapter_spec.js @@ -1,676 +1,10 @@ import { expect } from 'chai'; import { spec } from 'modules/ringieraxelspringerBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; -const CSR_ENDPOINT = 'https://csr.onet.pl/4178463/csr-006/csr.json?nid=4178463&'; - -describe('ringieraxelspringerBidAdapter', function () { - const adapter = newBidder(spec); - - describe('inherited functions', function () { - it('exists and is a function', function () { - expect(adapter.callBids).to.exist.and.to.be.a('function'); - }); - }); - - describe('isBidRequestValid', function () { - it('should return true when required params found', function () { - const bid = { - sizes: [[300, 250], [300, 600]], - bidder: 'ringieraxelspringer', - params: { - slot: 'slot', - area: 'areatest', - site: 'test', - network: '4178463' - } - }; - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params not found', function () { - const failBid = { - sizes: [[300, 250], [300, 300]], - bidder: 'ringieraxelspringer', - params: { - site: 'test', - network: '4178463' - } - }; - expect(spec.isBidRequestValid(failBid)).to.equal(false); - }); - - it('should return nothing when bid request is malformed', function () { - const failBid = { - sizes: [[300, 250], [300, 300]], - bidder: 'ringieraxelspringer', - }; - expect(spec.isBidRequestValid(failBid)).to.equal(undefined); - }); - }); - - describe('buildRequests', function () { - const bid = { - sizes: [[300, 250], [300, 600]], - bidder: 'ringieraxelspringer', - bidId: 1, - params: { - slot: 'test', - area: 'areatest', - site: 'test', - slotSequence: '0', - network: '4178463', - customParams: { - test: 'name=value' - } - }, - mediaTypes: { - banner: { - sizes: [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ] - } - } - }; - const bid2 = { - sizes: [[750, 300]], - bidder: 'ringieraxelspringer', - bidId: 2, - params: { - slot: 'test2', - area: 'areatest', - site: 'test', - network: '4178463' - }, - mediaTypes: { - banner: { - sizes: [ - [ - 750, - 300 - ] - ] - } - } - }; - - it('should parse bids to request', function () { - const requests = spec.buildRequests([bid], { - 'gdprConsent': { - 'gdprApplies': true, - 'consentString': 'some-consent-string' - }, - 'refererInfo': { - 'ref': 'https://example.org/', - 'page': 'https://example.com/' - } - }); - expect(requests[0].url).to.have.string(CSR_ENDPOINT); - expect(requests[0].url).to.have.string('slot0=test'); - expect(requests[0].url).to.have.string('id0=1'); - expect(requests[0].url).to.have.string('site=test'); - expect(requests[0].url).to.have.string('area=areatest'); - expect(requests[0].url).to.have.string('cre_format=html'); - expect(requests[0].url).to.have.string('systems=das'); - expect(requests[0].url).to.have.string('ems_url=1'); - expect(requests[0].url).to.have.string('bid_rate=1'); - expect(requests[0].url).to.have.string('gdpr_applies=true'); - expect(requests[0].url).to.have.string('euconsent=some-consent-string'); - expect(requests[0].url).to.have.string('du=https%3A%2F%2Fexample.com%2F'); - expect(requests[0].url).to.have.string('dr=https%3A%2F%2Fexample.org%2F'); - expect(requests[0].url).to.have.string('test=name%3Dvalue'); - }); - - it('should return empty consent string when undefined', function () { - const requests = spec.buildRequests([bid]); - const gdpr = requests[0].url.search('gdpr_applies'); - const euconsent = requests[0].url.search('euconsent='); - expect(gdpr).to.equal(-1); - expect(euconsent).to.equal(-1); - }); - - it('should parse bids to request from pageContext', function () { - const bidCopy = { ...bid }; - bidCopy.params = { - ...bid.params, - pageContext: { - dv: 'test/areatest', - du: 'https://example.com/', - dr: 'https://example.org/', - keyWords: ['val1', 'val2'], - keyValues: { - adunit: 'test/areatest' - } - } - }; - const requests = spec.buildRequests([bidCopy, bid2]); - - expect(requests[0].url).to.have.string(CSR_ENDPOINT); - expect(requests[0].url).to.have.string('slot0=test'); - expect(requests[0].url).to.have.string('id0=1'); - expect(requests[0].url).to.have.string('iusizes0=300x250%2C300x600'); - expect(requests[0].url).to.have.string('slot1=test2'); - expect(requests[0].url).to.have.string('kvhb_format0=banner'); - expect(requests[0].url).to.have.string('id1=2'); - expect(requests[0].url).to.have.string('iusizes1=750x300'); - expect(requests[0].url).to.have.string('kvhb_format1=banner'); - expect(requests[0].url).to.have.string('site=test'); - expect(requests[0].url).to.have.string('area=areatest'); - expect(requests[0].url).to.have.string('cre_format=html'); - expect(requests[0].url).to.have.string('systems=das'); - expect(requests[0].url).to.have.string('ems_url=1'); - expect(requests[0].url).to.have.string('bid_rate=1'); - expect(requests[0].url).to.have.string('du=https%3A%2F%2Fexample.com%2F'); - expect(requests[0].url).to.have.string('dr=https%3A%2F%2Fexample.org%2F'); - expect(requests[0].url).to.have.string('DV=test%2Fareatest'); - expect(requests[0].url).to.have.string('kwrd=val1%2Bval2'); - expect(requests[0].url).to.have.string('kvadunit=test%2Fareatest'); - expect(requests[0].url).to.have.string('pos0=0'); - }); - - it('should parse dsainfo when available', function () { - const bidCopy = { ...bid }; - bidCopy.params = { - ...bid.params, - pageContext: { - dv: 'test/areatest', - du: 'https://example.com/', - dr: 'https://example.org/', - keyWords: ['val1', 'val2'], - keyValues: { - adunit: 'test/areatest' - } - } - }; - const bidderRequest = { - ortb2: { - regs: { - ext: { - dsa: { - required: 1 - } - } - } - } - }; - let requests = spec.buildRequests([bidCopy], bidderRequest); - expect(requests[0].url).to.have.string('dsainfo=1'); - - bidderRequest.ortb2.regs.ext.dsa.required = 0; - requests = spec.buildRequests([bidCopy], bidderRequest); - expect(requests[0].url).to.have.string('dsainfo=0'); - }); - }); - - describe('interpretResponse', function () { - const response = { - 'adsCheck': 'ok', - 'geoloc': {}, - 'ir': '92effd60-0c84-4dac-817e-763ea7b8ac65', - 'ads': [ - { - 'id': 'flat-belkagorna', - 'slot': 'flat-belkagorna', - 'prio': 10, - 'type': 'html', - 'bid_rate': 0.321123, - 'adid': 'das,50463,152276', - 'id_3': '12734', - 'html': '' - } - ], - 'iv': '202003191334467636346500' - }; - - it('should get correct bid response', function () { - const resp = spec.interpretResponse({ body: response }, { bidIds: [{ slot: 'flat-belkagorna', bidId: 1 }] }); - expect(resp[0]).to.have.all.keys('cpm', 'currency', 'netRevenue', 'requestId', 'ttl', 'width', 'height', 'creativeId', 'dealId', 'ad', 'meta', 'actgMatch', 'mediaType'); - expect(resp.length).to.equal(1); - }); - - it('should handle empty ad', function () { - const res = { - 'ads': [{ - type: 'empty' - }] - }; - const resp = spec.interpretResponse({ body: res }, {}); - expect(resp).to.deep.equal([]); - }); - - it('should handle empty server response', function () { - const res = { - 'ads': [] - }; - const resp = spec.interpretResponse({ body: res }, {}); - expect(resp).to.deep.equal([]); - }); - - it('should generate auctionConfig when fledge is enabled', function () { - const bidRequest = { - method: 'GET', - url: 'https://example.com', - bidIds: [{ - slot: 'top', - bidId: '123', - network: 'testnetwork', - sizes: ['300x250'], - params: { - site: 'testsite', - area: 'testarea', - network: 'testnetwork' - }, - fledgeEnabled: true - }, - { - slot: 'top', - bidId: '456', - network: 'testnetwork', - sizes: ['300x250'], - params: { - site: 'testsite', - area: 'testarea', - network: 'testnetwork' - }, - fledgeEnabled: false - }] - }; - - const auctionConfigs = [{ - 'bidId': '123', - 'config': { - 'seller': 'https://csr.onet.pl', - 'decisionLogicUrl': 'https://csr.onet.pl/testnetwork/v1/protected-audience-api/decision-logic.js', - 'interestGroupBuyers': ['https://csr.onet.pl'], - 'auctionSignals': { - 'params': { - site: 'testsite', - area: 'testarea', - network: 'testnetwork' - }, - 'sizes': ['300x250'], - 'gctx': '1234567890' - } - } - }]; - const resp = spec.interpretResponse({body: {gctx: '1234567890'}}, bidRequest); - expect(resp).to.deep.equal({bids: [], paapi: auctionConfigs}); - }); - }); - - describe('buildNativeRequests', function () { - const bid = { - sizes: 'fluid', - bidder: 'ringieraxelspringer', - bidId: 1, - params: { - slot: 'nativestd', - area: 'areatest', - site: 'test', - slotSequence: '0', - network: '4178463', - customParams: { - test: 'name=value' - } - }, - mediaTypes: { - native: { - clickUrl: { - required: true - }, - image: { - required: true - }, - sponsoredBy: { - len: 25, - required: true - }, - title: { - len: 50, - required: true - } - } - } - }; - - it('should parse bids to native request', function () { - const requests = spec.buildRequests([bid], { - 'gdprConsent': { - 'gdprApplies': true, - 'consentString': 'some-consent-string' - }, - 'refererInfo': { - 'ref': 'https://example.org/', - 'page': 'https://example.com/' - } - }); - - expect(requests[0].url).to.have.string(CSR_ENDPOINT); - expect(requests[0].url).to.have.string('slot0=nativestd'); - expect(requests[0].url).to.have.string('id0=1'); - expect(requests[0].url).to.have.string('site=test'); - expect(requests[0].url).to.have.string('area=areatest'); - expect(requests[0].url).to.have.string('cre_format=html'); - expect(requests[0].url).to.have.string('systems=das'); - expect(requests[0].url).to.have.string('ems_url=1'); - expect(requests[0].url).to.have.string('bid_rate=1'); - expect(requests[0].url).to.have.string('gdpr_applies=true'); - expect(requests[0].url).to.have.string('euconsent=some-consent-string'); - expect(requests[0].url).to.have.string('du=https%3A%2F%2Fexample.com%2F'); - expect(requests[0].url).to.have.string('dr=https%3A%2F%2Fexample.org%2F'); - expect(requests[0].url).to.have.string('test=name%3Dvalue'); - expect(requests[0].url).to.have.string('cre_format0=native'); - expect(requests[0].url).to.have.string('kvhb_format0=native'); - expect(requests[0].url).to.have.string('iusizes0=fluid'); - }); - }); - - describe('interpretNativeResponse', function () { - const response = { - 'adsCheck': 'ok', - 'geoloc': {}, - 'ir': '92effd60-0c84-4dac-817e-763ea7b8ac65', - 'iv': '202003191334467636346500', - 'ads': [ - { - 'id': 'nativestd', - 'slot': 'nativestd', - 'prio': 10, - 'type': 'native', - 'bid_rate': 0.321123, - 'adid': 'das,50463,152276', - 'id_3': '12734' - } - ] - }; - const responseTeaserStandard = { - adsCheck: 'ok', - geoloc: {}, - ir: '92effd60-0c84-4dac-817e-763ea7b8ac65', - iv: '202003191334467636346500', - ads: [ - { - id: 'nativestd', - slot: 'nativestd', - prio: 10, - type: 'native', - bid_rate: 0.321123, - adid: 'das,50463,152276', - id_3: '12734', - data: { - fields: { - leadtext: 'BODY', - title: 'Headline', - image: '//img.url', - url: '//link.url', - partner_logo: '//logo.url', - adInfo: 'REKLAMA', - impression: '//impression.url', - impression1: '//impression1.url', - impressionJs1: '//impressionJs1.url' - }, - meta: { - slot: 'nativestd', - height: 1, - width: 1, - advertiser_name: 'Test Onet', - dsaurl: '//dsa.url', - adclick: '//adclick.url' - } - }, - ems_link: '//ems.url' - } - ] - }; - const responseNativeInFeed = { - adsCheck: 'ok', - geoloc: {}, - ir: '92effd60-0c84-4dac-817e-763ea7b8ac65', - iv: '202003191334467636346500', - ads: [ - { - id: 'nativestd', - slot: 'nativestd', - prio: 10, - type: 'native', - bid_rate: 0.321123, - adid: 'das,50463,152276', - id_3: '12734', - data: { - fields: { - Body: 'BODY', - Calltoaction: 'Calltoaction', - Headline: 'Headline', - Image: '//img.url', - adInfo: 'REKLAMA', - Thirdpartyclicktracker: '//link.url', - imp: '//imp.url', - thirdPartyClickTracker2: '//thirdPartyClickTracker.url' - }, - meta: { - slot: 'nativestd', - height: 1, - width: 1, - advertiser_name: 'Test Onet', - dsaurl: '//dsa.url', - adclick: '//adclick.url' - } - }, - ems_link: '//ems.url' - } - ] - }; - const expectedTeaserStandardOrtbResponse = { - ver: '1.2', - assets: [ - { - id: 0, - data: { - value: '', - type: 2 - }, - }, - { - id: 1, - data: { - value: 'REKLAMA', - type: 10 - }, - }, - { - id: 3, - img: { - type: 1, - url: '//logo.url', - w: 1, - h: 1 - } - }, - { - id: 4, - img: { - type: 3, - url: '//img.url', - w: 1, - h: 1 - } - }, - { - id: 5, - data: { - value: 'Test Onet', - type: 1 - }, - }, - { - id: 6, - title: { - text: 'Headline' - } - }, - ], - link: { - url: '//adclick.url//link.url', - clicktrackers: [] - }, - eventtrackers: [ - { - event: 1, - method: 1, - url: '//ems.url' - }, - { - event: 1, - method: 1, - url: '//impression.url' - }, - { - event: 1, - method: 1, - url: '//impression1.url' - }, - { - event: 1, - method: 2, - url: '//impressionJs1.url' - } - ], - privacy: '//dsa.url' - }; - const expectedTeaserStandardResponse = { - title: 'Headline', - image: { - url: '//img.url', - width: 1, - height: 1 - }, - icon: { - url: '//logo.url', - width: 1, - height: 1 - }, - clickUrl: '//adclick.url//link.url', - cta: '', - body: 'BODY', - body2: 'REKLAMA', - sponsoredBy: 'Test Onet', - ortb: expectedTeaserStandardOrtbResponse, - privacyLink: '//dsa.url' - }; - const expectedNativeInFeedOrtbResponse = { - ver: '1.2', - assets: [ - { - id: 0, - data: { - value: '', - type: 2 - }, - }, - { - id: 1, - data: { - value: 'REKLAMA', - type: 10 - }, - }, - { - id: 3, - img: { - type: 1, - url: '', - w: 1, - h: 1 - } - }, - { - id: 4, - img: { - type: 3, - url: '//img.url', - w: 1, - h: 1 - } - }, - { - id: 5, - data: { - value: 'Test Onet', - type: 1 - }, - }, - { - id: 6, - title: { - text: 'Headline' - } - }, - ], - link: { - url: '//adclick.url//link.url', - clicktrackers: ['//thirdPartyClickTracker.url'] - }, - eventtrackers: [ - { - event: 1, - method: 1, - url: '//ems.url' - }, - { - event: 1, - method: 1, - url: '//imp.url' - } - ], - privacy: '//dsa.url', - }; - const expectedNativeInFeedResponse = { - title: 'Headline', - image: { - url: '//img.url', - width: 1, - height: 1 - }, - icon: { - url: '', - width: 1, - height: 1 - }, - clickUrl: '//adclick.url//link.url', - cta: 'Calltoaction', - body: 'BODY', - body2: 'REKLAMA', - sponsoredBy: 'Test Onet', - ortb: expectedNativeInFeedOrtbResponse, - privacyLink: '//dsa.url' - }; - - it('should get correct bid native response', function () { - const resp = spec.interpretResponse({ body: response }, { bidIds: [{ slot: 'nativestd', bidId: 1, mediaType: 'native' }] }); - - expect(resp[0]).to.have.all.keys('cpm', 'currency', 'netRevenue', 'requestId', 'ttl', 'width', 'height', 'creativeId', 'dealId', 'meta', 'actgMatch', 'mediaType', 'native'); - expect(resp.length).to.equal(1); - }); - - it('should get correct native response for TeaserStandard', function () { - const resp = spec.interpretResponse({ body: responseTeaserStandard }, { bidIds: [{ slot: 'nativestd', bidId: 1, mediaType: 'native' }] }); - const teaserStandardResponse = resp[0].native; - - expect(JSON.stringify(teaserStandardResponse)).to.equal(JSON.stringify(expectedTeaserStandardResponse)); - }); - - it('should get correct native response for NativeInFeed', function () { - const resp = spec.interpretResponse({ body: responseNativeInFeed }, { bidIds: [{ slot: 'nativestd', bidId: 1, mediaType: 'native' }] }); - const nativeInFeedResponse = resp[0].native; - - expect(JSON.stringify(nativeInFeedResponse)).to.equal(JSON.stringify(expectedNativeInFeedResponse)); - }); +describe('ringieraxelspringer backward-compatibility shim', function () { + it('should re-export spec from dasBidAdapter', function () { + expect(spec).to.exist; + expect(spec.code).to.equal('das'); + expect(spec.aliases).to.include('ringieraxelspringer'); }); });