Skip to content

Commit 580394a

Browse files
authored
Digitalcaramel Bid Adapter: initial release (#13731)
* move common code to lib and add digitalcaramel adapter * more deduplication
1 parent 5958e27 commit 580394a

File tree

6 files changed

+478
-166
lines changed

6 files changed

+478
-166
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import { hasPurpose1Consent } from '../../src/utils/gdpr.js';
2+
import { BANNER, VIDEO } from '../../src/mediaTypes.js';
3+
import { deepAccess, isArray, parseSizesInput } from '../../src/utils.js';
4+
5+
export function getUserSyncs(syncEndpoint, paramNames) {
6+
return function(syncOptions, serverResponses, gdprConsent, uspConsent) {
7+
const syncs = [];
8+
9+
if (!hasPurpose1Consent(gdprConsent)) {
10+
return syncs;
11+
}
12+
13+
let params = `${paramNames?.usp ?? 'us_privacy'}=${uspConsent ?? ''}&${paramNames?.consent ?? 'gdpr_consent'}=${gdprConsent?.consentString ?? ''}`;
14+
15+
if (typeof gdprConsent?.gdprApplies === 'boolean') {
16+
params += `&gdpr=${Number(gdprConsent.gdprApplies)}`;
17+
}
18+
19+
if (syncOptions.iframeEnabled) {
20+
syncs.push({
21+
type: 'iframe',
22+
url: `//${syncEndpoint}/match/sp.ifr?${params}`,
23+
});
24+
}
25+
26+
if (syncOptions.pixelEnabled) {
27+
syncs.push({
28+
type: 'image',
29+
url: `//${syncEndpoint}/match/sp?${params}`,
30+
});
31+
}
32+
33+
return syncs;
34+
}
35+
}
36+
37+
export function sspInterpretResponse(ttl, adomain) {
38+
return function(serverResponse, request) {
39+
if (!serverResponse?.body?.content?.data) {
40+
return [];
41+
}
42+
43+
const bidResponses = [];
44+
const body = serverResponse.body;
45+
46+
let mediaType = BANNER;
47+
let ad, vastXml;
48+
let width;
49+
let height;
50+
51+
const sizes = getSize(body.size);
52+
if (isArray(sizes)) {
53+
[width, height] = sizes;
54+
}
55+
56+
if (body.type.format != '') {
57+
// banner
58+
ad = body.content.data;
59+
if (body.content.imps?.length) {
60+
for (const imp of body.content.imps) {
61+
ad += `<script src="${imp}"></script>`;
62+
}
63+
}
64+
} else {
65+
// video
66+
vastXml = body.content.data;
67+
mediaType = VIDEO;
68+
69+
if (!width || !height) {
70+
const pSize = deepAccess(request.bidRequest, 'mediaTypes.video.playerSize');
71+
const reqSize = getSize(pSize);
72+
if (isArray(reqSize)) {
73+
[width, height] = reqSize;
74+
}
75+
}
76+
}
77+
78+
const bidResponse = {
79+
requestId: request.bidRequest.bidId,
80+
cpm: body.cpm,
81+
currency: body.currency || 'USD',
82+
width: parseInt(width),
83+
height: parseInt(height),
84+
creativeId: body.id,
85+
netRevenue: true,
86+
ttl: ttl,
87+
ad: ad,
88+
mediaType: mediaType,
89+
vastXml: vastXml,
90+
meta: {
91+
advertiserDomains: [adomain],
92+
}
93+
};
94+
95+
if ((mediaType === VIDEO && request.bidRequest.mediaTypes?.video) || (mediaType === BANNER && request.bidRequest.mediaTypes?.banner)) {
96+
bidResponses.push(bidResponse);
97+
}
98+
99+
return bidResponses;
100+
}
101+
}
102+
103+
export function sspBuildRequests(defaultEndpoint) {
104+
return function(validBidRequests, bidderRequest) {
105+
const requests = [];
106+
for (const bid of validBidRequests) {
107+
const endpoint = bid.params.endpoint || defaultEndpoint;
108+
109+
requests.push({
110+
method: 'GET',
111+
url: `https://${endpoint}/get`,
112+
data: {
113+
site_id: bid.params.siteId,
114+
placement_id: bid.params.placementId,
115+
prebid: true,
116+
},
117+
bidRequest: bid,
118+
});
119+
}
120+
121+
return requests;
122+
}
123+
}
124+
125+
export function sspValidRequest(bid) {
126+
const valid = bid.params.siteId && bid.params.placementId;
127+
128+
return !!valid;
129+
}
130+
131+
function getSize(paramSizes) {
132+
const parsedSizes = parseSizesInput(paramSizes);
133+
const sizes = parsedSizes.map(size => {
134+
const [width, height] = size.split('x');
135+
const w = parseInt(width, 10);
136+
const h = parseInt(height, 10);
137+
return [w, h];
138+
});
139+
140+
return sizes[0] || null;
141+
}

modules/digitalcaramelBidAdapter.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { registerBidder } from '../src/adapters/bidderFactory.js';
2+
import { BANNER, VIDEO } from '../src/mediaTypes.js';
3+
import { getUserSyncs, sspBuildRequests, sspInterpretResponse, sspValidRequest } from '../libraries/vizionikUtils/vizionikUtils.js';
4+
5+
const BIDDER_CODE = 'digitalcaramel';
6+
const DEFAULT_ENDPOINT = 'ssp-asr.digitalcaramel.com';
7+
const SYNC_ENDPOINT = 'sync.digitalcaramel.com';
8+
const ADOMAIN = 'digitalcaramel.com';
9+
const TIME_TO_LIVE = 360;
10+
11+
export const spec = {
12+
code: BIDDER_CODE,
13+
14+
isBidRequestValid: sspValidRequest,
15+
buildRequests: sspBuildRequests(DEFAULT_ENDPOINT),
16+
interpretResponse: sspInterpretResponse(TIME_TO_LIVE, ADOMAIN),
17+
getUserSyncs: getUserSyncs(SYNC_ENDPOINT, {usp: 'usp', consent: 'consent'}),
18+
supportedMediaTypes: [ BANNER, VIDEO ]
19+
}
20+
21+
registerBidder(spec);

modules/digitalcaramelBidAdapter.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Overview
2+
3+
```
4+
Module Name: Digitalcaramel Bid Adapter
5+
Module Type: Bidder Adapter
6+
Maintainer: [email protected]
7+
```
8+
9+
# Description
10+
Connects to Digitalcaramel server for bids.
11+
Module supports banner and video mediaType.
12+
13+
# Test Parameters
14+
15+
```
16+
var adUnits = [{
17+
code: '/test/div',
18+
mediaTypes: {
19+
banner: {
20+
sizes: [[300, 250]]
21+
}
22+
},
23+
bids: [{
24+
bidder: 'digitalcaramel',
25+
params: {
26+
siteId: 'd1d83nbdi0fs73874a0g',
27+
placementId: 'd1d8493di0fs73874a10'
28+
}
29+
}]
30+
},
31+
{
32+
code: '/test/div',
33+
mediaTypes: {
34+
video: {
35+
playerSize: [[640, 360]]
36+
}
37+
},
38+
bids: [{
39+
bidder: 'digitalcaramel',
40+
params: {
41+
siteId: 'd1d83nbdi0fs73874a0g',
42+
placementId: 'd24v2ijdi0fs73874afg'
43+
}
44+
}]
45+
},];
46+
```

modules/programmaticaBidAdapter.js

Lines changed: 5 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { registerBidder } from '../src/adapters/bidderFactory.js';
22
import { BANNER, VIDEO } from '../src/mediaTypes.js';
3-
import { hasPurpose1Consent } from '../src/utils/gdpr.js';
4-
import { deepAccess, parseSizesInput, isArray } from '../src/utils.js';
3+
import { getUserSyncs, sspBuildRequests, sspInterpretResponse, sspValidRequest } from '../libraries/vizionikUtils/vizionikUtils.js';
54

65
const BIDDER_CODE = 'programmatica';
76
const DEFAULT_ENDPOINT = 'asr.programmatica.com';
@@ -12,142 +11,11 @@ const TIME_TO_LIVE = 360;
1211
export const spec = {
1312
code: BIDDER_CODE,
1413

15-
isBidRequestValid: function(bid) {
16-
const valid = bid.params.siteId && bid.params.placementId;
17-
18-
return !!valid;
19-
},
20-
21-
buildRequests: function(validBidRequests, bidderRequest) {
22-
const requests = [];
23-
for (const bid of validBidRequests) {
24-
const endpoint = bid.params.endpoint || DEFAULT_ENDPOINT;
25-
26-
requests.push({
27-
method: 'GET',
28-
url: `https://${endpoint}/get`,
29-
data: {
30-
site_id: bid.params.siteId,
31-
placement_id: bid.params.placementId,
32-
prebid: true,
33-
},
34-
bidRequest: bid,
35-
});
36-
}
37-
38-
return requests;
39-
},
40-
41-
interpretResponse: function(serverResponse, request) {
42-
if (!serverResponse?.body?.content?.data) {
43-
return [];
44-
}
45-
46-
const bidResponses = [];
47-
const body = serverResponse.body;
48-
49-
let mediaType = BANNER;
50-
let ad, vastXml;
51-
let width;
52-
let height;
53-
54-
const sizes = getSize(body.size);
55-
if (isArray(sizes)) {
56-
[width, height] = sizes;
57-
}
58-
59-
if (body.type.format != '') {
60-
// banner
61-
ad = body.content.data;
62-
if (body.content.imps?.length) {
63-
for (const imp of body.content.imps) {
64-
ad += `<script src="${imp}"></script>`;
65-
}
66-
}
67-
} else {
68-
// video
69-
vastXml = body.content.data;
70-
mediaType = VIDEO;
71-
72-
if (!width || !height) {
73-
const pSize = deepAccess(request.bidRequest, 'mediaTypes.video.playerSize');
74-
const reqSize = getSize(pSize);
75-
if (isArray(reqSize)) {
76-
[width, height] = reqSize;
77-
}
78-
}
79-
}
80-
81-
const bidResponse = {
82-
requestId: request.bidRequest.bidId,
83-
cpm: body.cpm,
84-
currency: body.currency || 'USD',
85-
width: parseInt(width),
86-
height: parseInt(height),
87-
creativeId: body.id,
88-
netRevenue: true,
89-
ttl: TIME_TO_LIVE,
90-
ad: ad,
91-
mediaType: mediaType,
92-
vastXml: vastXml,
93-
meta: {
94-
advertiserDomains: [ADOMAIN],
95-
}
96-
};
97-
98-
if ((mediaType === VIDEO && request.bidRequest.mediaTypes?.video) || (mediaType === BANNER && request.bidRequest.mediaTypes?.banner)) {
99-
bidResponses.push(bidResponse);
100-
}
101-
102-
return bidResponses;
103-
},
104-
105-
getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) {
106-
const syncs = []
107-
108-
if (!hasPurpose1Consent(gdprConsent)) {
109-
return syncs;
110-
}
111-
112-
let params = `usp=${uspConsent ?? ''}&consent=${gdprConsent?.consentString ?? ''}`;
113-
if (typeof gdprConsent?.gdprApplies === 'boolean') {
114-
params += `&gdpr=${Number(gdprConsent.gdprApplies)}`;
115-
}
116-
117-
if (syncOptions.iframeEnabled) {
118-
syncs.push({
119-
type: 'iframe',
120-
url: `//${SYNC_ENDPOINT}/match/sp.ifr?${params}`
121-
});
122-
}
123-
124-
if (syncOptions.pixelEnabled) {
125-
syncs.push({
126-
type: 'image',
127-
url: `//${SYNC_ENDPOINT}/match/sp?${params}`
128-
});
129-
}
130-
131-
return syncs;
132-
},
133-
134-
onTimeout: function(timeoutData) {},
135-
onBidWon: function(bid) {},
136-
onSetTargeting: function(bid) {},
137-
onBidderError: function() {},
14+
isBidRequestValid: sspValidRequest,
15+
buildRequests: sspBuildRequests(DEFAULT_ENDPOINT),
16+
interpretResponse: sspInterpretResponse(TIME_TO_LIVE, ADOMAIN),
17+
getUserSyncs: getUserSyncs(SYNC_ENDPOINT, {usp: 'usp', consent: 'consent'}),
13818
supportedMediaTypes: [ BANNER, VIDEO ]
13919
}
14020

14121
registerBidder(spec);
142-
143-
function getSize(paramSizes) {
144-
const parsedSizes = parseSizesInput(paramSizes);
145-
const sizes = parsedSizes.map(size => {
146-
const [width, height] = size.split('x');
147-
const w = parseInt(width, 10);
148-
const h = parseInt(height, 10);
149-
return [w, h];
150-
});
151-
152-
return sizes[0] || null;
153-
}

0 commit comments

Comments
 (0)