Skip to content

Commit 31057ff

Browse files
authored
CondorX Bid Adapter : initial release (prebid#12643)
* Add CondorX Bid Adapter * use inIframe utils function
1 parent 789d302 commit 31057ff

3 files changed

Lines changed: 488 additions & 0 deletions

File tree

modules/condorxBidAdapter.js

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import { BANNER, NATIVE } from '../src/mediaTypes.js';
2+
import { createTrackPixelHtml, inIframe } from '../src/utils.js';
3+
import { registerBidder } from '../src/adapters/bidderFactory.js';
4+
import { convertOrtbRequestToProprietaryNative } from '../src/native.js';
5+
6+
const BIDDER_CODE = 'condorx';
7+
const API_URL = 'https://api.condorx.io/cxb/get.json';
8+
const REQUEST_METHOD = 'GET';
9+
const MAX_SIZE_DEVIATION = 0.05;
10+
const SUPPORTED_AD_SIZES = [
11+
[100, 100], [200, 200], [300, 250], [400, 200], [300, 200], [600, 600], [650, 1168], [236, 202], [1080, 1920], [300, 374]
12+
];
13+
14+
function getBidRequestUrl(bidRequest, bidderRequest) {
15+
if (bidRequest.params.url && bidRequest.params.url !== 'current url') {
16+
return bidRequest.params.url;
17+
}
18+
if (bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.page) {
19+
return bidderRequest.refererInfo.page;
20+
}
21+
const pageUrl = inIframe() && document.referrer ? document.referrer : window.location.href;
22+
return encodeURIComponent(pageUrl);
23+
}
24+
25+
function getTileImageUrl(tile) {
26+
return tile.imageUrl.indexOf('http') === -1 ? 'https:' + tile.imageUrl : tile.imageUrl;
27+
}
28+
29+
function collectImpressionTrackers(tile, response) {
30+
const trackers = [response.widgetViewPixel];
31+
if (!tile.trackers) return trackers;
32+
const impressionTrackers = tile.trackers.impressionPixels || [];
33+
const viewTrackers = tile.trackers.viewPixels || [];
34+
return [...impressionTrackers, ...viewTrackers, ...trackers];
35+
}
36+
37+
function parseNativeAdResponse(tile, response) {
38+
return {
39+
title: tile.title,
40+
body: '',
41+
image: {
42+
url: getTileImageUrl(tile),
43+
width: response.imageWidth,
44+
height: response.imageHeight
45+
},
46+
privacyLink: '',
47+
clickUrl: tile.clickUrl,
48+
displayUrl: tile.url,
49+
cta: '',
50+
sponsoredBy: tile.displayName,
51+
impressionTrackers: collectImpressionTrackers(tile, response),
52+
};
53+
}
54+
55+
function parseBannerAdResponse(tile, response) {
56+
if (tile.tag) {
57+
return tile.tag;
58+
}
59+
let style = '';
60+
try {
61+
const config = JSON.parse(response.widget.config);
62+
const css = config.css || '';
63+
style = css ? `<style>${css}</style>` : '';
64+
} catch (e) {
65+
style = '';
66+
}
67+
const title = tile.title && tile.title.trim() ? `<div class="__condorx_banner_title" style="display: none">${tile.title}</div>` : '';
68+
const displayName = tile.displayName && title ? `<div class="__condorx_banner_branding" style="display: none">${tile.displayName}</div>` : '';
69+
const trackers = collectImpressionTrackers(tile, response)
70+
.map((url) => createTrackPixelHtml(url))
71+
.join('');
72+
return `<html><body>${style}<div id="__CONDORX__BANNER"><a href="${tile.clickUrl}" target=_blank><img class="__condorx_banner_image" src="${getTileImageUrl(tile)}" style="width:${response.imageWidth}px;height:${response.imageHeight}px;" alt="${tile.title}"/>${displayName}${title}</a>${trackers}</div></body></html>`;
73+
}
74+
75+
function getAdSize(bidRequest) {
76+
if (bidRequest.sizes && bidRequest.sizes.length > 0) {
77+
return bidRequest.sizes[0];
78+
} else if (bidRequest.nativeParams && bidRequest.nativeParams.image && bidRequest.nativeParams.image.sizes) {
79+
return bidRequest.nativeParams.image.sizes;
80+
}
81+
return [-1, -1];
82+
}
83+
84+
function isValidAdSize([width, height]) {
85+
if (!width || !height) {
86+
return false;
87+
}
88+
return SUPPORTED_AD_SIZES.some(([supportedWidth, supportedHeight]) => {
89+
if (supportedWidth === width && supportedHeight === height) {
90+
return true;
91+
}
92+
const supportedRatio = supportedWidth / supportedHeight;
93+
const ratioDeviation = supportedRatio / width * height;
94+
return Math.abs(ratioDeviation - 1) <= MAX_SIZE_DEVIATION && (supportedWidth > width || (width - supportedWidth) / width <= MAX_SIZE_DEVIATION);
95+
});
96+
}
97+
98+
export const bidderSpec = {
99+
code: BIDDER_CODE,
100+
supportedMediaTypes: [BANNER, NATIVE],
101+
102+
isBidRequestValid: function (bidRequest) {
103+
return bidRequest &&
104+
bidRequest.params &&
105+
bidRequest.params.hasOwnProperty('widget') &&
106+
bidRequest.params.hasOwnProperty('website') &&
107+
!isNaN(bidRequest.params.widget) &&
108+
!isNaN(bidRequest.params.website) &&
109+
isValidAdSize(getAdSize(bidRequest));
110+
},
111+
112+
buildRequests: function (validBidRequests, bidderRequest) {
113+
validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests);
114+
115+
if (!validBidRequests) {
116+
return [];
117+
}
118+
return validBidRequests.map(bidRequest => {
119+
if (bidRequest.params) {
120+
const mediaType = bidRequest.hasOwnProperty('nativeParams') ? 1 : 2;
121+
const [imageWidth, imageHeight] = getAdSize(bidRequest);
122+
const widgetId = bidRequest.params.widget;
123+
const websiteId = bidRequest.params.website;
124+
const pageUrl = getBidRequestUrl(bidRequest, bidderRequest);
125+
const bidId = bidRequest.bidId;
126+
let apiUrl = `${API_URL}?w=${websiteId}&wg=${widgetId}&u=${pageUrl}&p=0&ireqid=${bidId}&prebid=${mediaType}&imgw=${imageWidth}&imgh=${imageHeight}`;
127+
if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprApplies && bidderRequest.consentString) {
128+
apiUrl += `&g=1&gc=${bidderRequest.consentString}`;
129+
}
130+
return {
131+
url: apiUrl,
132+
method: REQUEST_METHOD,
133+
data: ''
134+
};
135+
}
136+
}).filter(Boolean);
137+
},
138+
139+
interpretResponse: function (serverResponse, bidRequest) {
140+
if (!serverResponse.body || !serverResponse.body.tiles || !serverResponse.body.tiles.length) {
141+
return [];
142+
}
143+
const response = serverResponse.body;
144+
const isNative = response.pbtypeId === 1;
145+
return response.tiles.map(tile => {
146+
let bid = {
147+
requestId: response.ireqId,
148+
width: response.imageWidth,
149+
height: response.imageHeight,
150+
creativeId: tile.postId,
151+
cpm: tile.pecpm || (tile.ecpm / 100),
152+
currency: 'USD',
153+
netRevenue: !!tile.pecpm,
154+
ttl: 360,
155+
meta: { advertiserDomains: tile.domain ? [tile.domain] : [] },
156+
};
157+
if (isNative) {
158+
bid.native = parseNativeAdResponse(tile, response);
159+
} else {
160+
bid.ad = parseBannerAdResponse(tile, response);
161+
}
162+
return bid;
163+
});
164+
}
165+
};
166+
167+
registerBidder(bidderSpec);

modules/condorxBidAdapter.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Overview
2+
3+
```
4+
Module Name: CondorX's Bidder Adapter
5+
Module Type: Bidder Adapter
6+
Maintainer: prebid@condorx.io
7+
```
8+
9+
# Description
10+
11+
Module that connects to CondorX bidder to fetch bids.
12+
13+
# Test Parameters
14+
```
15+
var adUnits = [{
16+
code: 'condorx-container-id',
17+
mediaTypes: {
18+
banner: {
19+
sizes: [[300, 250]],
20+
}
21+
},
22+
bids: [{
23+
bidder: "condorx",
24+
params: {
25+
widget: 'widget id by CondorX',
26+
website: 'website id by CondorX',
27+
url:'current url'
28+
}
29+
}]
30+
},
31+
{
32+
code: 'condorx-container-id',
33+
mediaTypes: {
34+
native: {
35+
image: {
36+
required: true,
37+
sizes: [236, 202]
38+
},
39+
title: {
40+
required: true,
41+
len: 100
42+
},
43+
sponsoredBy: {
44+
required: true
45+
},
46+
clickUrl: {
47+
required: true
48+
},
49+
body: {
50+
required: true
51+
}
52+
}
53+
},
54+
bids: [{
55+
bidder: "condorx",
56+
params: {
57+
widget: 'widget id by CondorX',
58+
website: 'website id by CondorX',
59+
url:'current url'
60+
}
61+
}]
62+
}
63+
}];
64+
```

0 commit comments

Comments
 (0)