Skip to content

Commit

Permalink
Snigel Bid Adapter: initial adapter release (#8723)
Browse files Browse the repository at this point in the history
* add snigel prebid adapter, docs and tests

* fix small oversight

* improve user sync behavior

* implement PR feedback, add support for schain, floors and user IDs
  • Loading branch information
snigelweb authored Aug 5, 2022
1 parent a93a7c9 commit 81a9e50
Show file tree
Hide file tree
Showing 3 changed files with 492 additions and 0 deletions.
150 changes: 150 additions & 0 deletions modules/snigelBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import {config} from '../src/config.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER} from '../src/mediaTypes.js';
import {deepAccess, isArray, isFn, isPlainObject} from '../src/utils.js';
import {hasPurpose1Consent} from '../src/utils/gpdr.js';

const BIDDER_CODE = 'snigel';
const GVLID = 1076;
const DEFAULT_URL = 'https://adserv.snigelweb.com/bp/v1/prebid';
const DEFAULT_TTL = 60;
const DEFAULT_CURRENCIES = ['USD'];
const FLOOR_MATCH_ALL_SIZES = '*';

const getConfig = config.getConfig;

export const spec = {
code: BIDDER_CODE,
gvlid: GVLID,
supportedMediaTypes: [BANNER],

isBidRequestValid: function (bidRequest) {
return !!bidRequest.params.placement;
},

buildRequests: function (bidRequests, bidderRequest) {
const gdprApplies = deepAccess(bidderRequest, 'gdprConsent.gdprApplies');
return {
method: 'POST',
url: getEndpoint(),
data: JSON.stringify({
id: bidderRequest.bidderRequestId,
cur: getCurrencies(),
test: getTestFlag(),
devw: window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth,
devh: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight,
version: $$PREBID_GLOBAL$$.version,
gdprApplies: gdprApplies,
gdprConsentString: gdprApplies === true ? deepAccess(bidderRequest, 'gdprConsent.consentString') : undefined,
gdprConsentProv: gdprApplies === true ? deepAccess(bidderRequest, 'gdprConsent.addtlConsent') : undefined,
uspConsent: deepAccess(bidderRequest, 'uspConsent'),
coppa: getConfig('coppa'),
eids: deepAccess(bidRequests, '0.userIdAsEids'),
schain: deepAccess(bidRequests, '0.schain'),
page: getPage(bidderRequest),
placements: bidRequests.map((r) => {
return {
uuid: r.bidId,
name: r.params.placement,
sizes: r.sizes,
floor: getPriceFloor(r, BANNER, FLOOR_MATCH_ALL_SIZES),
};
}),
}),
bidderRequest,
};
},

interpretResponse: function (serverResponse) {
if (!serverResponse.body || !serverResponse.body.bids) {
return [];
}

return serverResponse.body.bids.map((bid) => {
return {
requestId: bid.uuid,
cpm: bid.price,
creativeId: bid.crid,
currency: serverResponse.body.cur,
width: bid.width,
height: bid.height,
ad: bid.ad,
netRevenue: true,
ttl: bid.ttl || DEFAULT_TTL,
meta: bid.meta,
};
});
},

getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) {
const syncUrl = getSyncUrl(responses || []);
if (syncUrl && syncOptions.iframeEnabled && hasSyncConsent(gdprConsent, uspConsent)) {
return [{type: 'iframe', url: getSyncEndpoint(syncUrl, gdprConsent)}];
}
},
};

registerBidder(spec);

function getPage(bidderRequest) {
return (
getConfig(`${BIDDER_CODE}.page`) || deepAccess(bidderRequest, 'refererInfo.canonicalUrl') || window.location.href
);
}

function getEndpoint() {
return getConfig(`${BIDDER_CODE}.url`) || DEFAULT_URL;
}

function getTestFlag() {
return getConfig(`${BIDDER_CODE}.test`) === true;
}

function getCurrencies() {
const currencyOverrides = getConfig(`${BIDDER_CODE}.cur`);
if (currencyOverrides !== undefined && (!isArray(currencyOverrides) || currencyOverrides.length === 0)) {
throw Error('Currency override must be an array with at least one currency');
}
return currencyOverrides || DEFAULT_CURRENCIES;
}

function getFloorCurrency() {
return getConfig(`${BIDDER_CODE}.floorCur`) || getCurrencies()[0];
}

function getPriceFloor(bidRequest, mediaType, size) {
if (isFn(bidRequest.getFloor)) {
const cur = getFloorCurrency();
const floorInfo = bidRequest.getFloor({
currency: cur,
mediaType: mediaType,
size: size,
});
if (isPlainObject(floorInfo) && !isNaN(floorInfo.floor)) {
return {
cur: floorInfo.currency || cur,
value: floorInfo.floor,
};
}
}
}

function hasSyncConsent(gdprConsent, uspConsent) {
if (gdprConsent?.gdprApplies && !hasPurpose1Consent(gdprConsent)) {
return false;
} else if (uspConsent && uspConsent[1] === 'Y' && uspConsent[2] === 'Y') {
return false;
} else {
return true;
}
}

function getSyncUrl(responses) {
return getConfig(`${BIDDER_CODE}.syncUrl`) || deepAccess(responses[0], 'body.syncUrl');
}

function getSyncEndpoint(url, gdprConsent) {
return `${url}?gdpr=${gdprConsent?.gdprApplies ? 1 : 0}&gdpr_consent=${encodeURIComponent(
gdprConsent?.consentString || ''
)}`;
}
46 changes: 46 additions & 0 deletions modules/snigelBidAdapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Overview

- Module name: Snigel Bid Adapter
- Module type: Bidder Adapter
- Maintainer: [email protected]
- Bidder code: snigel
- Supported media types: Banner

# Description

Connects to Snigel demand sources for bids.

**Note:** This bid adapter requires our ad operation experts to create an optimized setup for the desired placements on your property.
Please reach out to us [through our contact form](https://snigel.com/get-in-touch). We will reply as soon as possible.

# Parameters

| Name | Required | Description | Example |
| :--- | :-------- | :---------- | :------ |
| placement | Yes | Placement identifier | top_leaderboard |

# Test

```js
var adUnits = [
{
code: "example",
mediaTypes: {
banner: {
sizes: [
[970, 90],
[728, 90],
],
},
},
bids: [
{
bidder: "snigel",
params: {
placement: "prebid_test_placement",
},
},
],
},
];
```
Loading

0 comments on commit 81a9e50

Please sign in to comment.