From 57fc9bbffde0a348855011b9115082d0d31acbeb Mon Sep 17 00:00:00 2001
From: Sacha <35510349+thebraveio@users.noreply.github.com>
Date: Thu, 5 Aug 2021 16:23:38 +0300
Subject: [PATCH 1/8] added Brave bidder adapter with test and docs
Commit has standard bidder adapter 2 new files adapter js, adapter md
---
modules/braveBidAdapter.js | 267 +++++++++++++++++++++++++++++++++++++
modules/braveBidAdapter.md | 135 +++++++++++++++++++
2 files changed, 402 insertions(+)
create mode 100644 modules/braveBidAdapter.js
create mode 100644 modules/braveBidAdapter.md
diff --git a/modules/braveBidAdapter.js b/modules/braveBidAdapter.js
new file mode 100644
index 00000000000..7ea3eb9cd9b
--- /dev/null
+++ b/modules/braveBidAdapter.js
@@ -0,0 +1,267 @@
+import * as utils from '../src/utils.js';
+import { registerBidder } from '../src/adapters/bidderFactory.js';
+import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
+import { config } from '../src/config.js';
+
+const BIDDER_CODE = 'brave';
+const DEFAULT_CUR = 'USD';
+const ENDPOINT_URL = `https://point.bravegroup.tv/?t=2&partner=hash`;
+
+const NATIVE_ASSETS_IDS = { 1: 'title', 2: 'icon', 3: 'image', 4: 'body', 5: 'sponsoredBy', 6: 'cta' };
+const NATIVE_ASSETS = {
+ title: { id: 1, name: 'title' },
+ icon: { id: 2, type: 1, name: 'img' },
+ image: { id: 3, type: 3, name: 'img' },
+ body: { id: 4, type: 2, name: 'data' },
+ sponsoredBy: { id: 5, type: 1, name: 'data' },
+ cta: { id: 6, type: 12, name: 'data' }
+};
+
+export const spec = {
+ code: BIDDER_CODE,
+ supportedMediaTypes: [BANNER, VIDEO, NATIVE],
+
+ /**
+ * Determines whether or not the given bid request is valid.
+ *
+ * @param {object} bid The bid to validate.
+ * @return boolean True if this is a valid bid, and false otherwise.
+ */
+ isBidRequestValid: (bid) => {
+ return !!(bid.params.placementId && bid.params.placementId.toString().length === 32);
+ },
+
+ /**
+ * Make a server request from the list of BidRequests.
+ *
+ * @param {BidRequest[]} validBidRequests A non-empty list of valid bid requests that should be sent to the Server.
+ * @return ServerRequest Info describing the request to the server.
+ */
+ buildRequests: (validBidRequests, bidderRequest) => {
+ if (validBidRequests.length === 0 || !bidderRequest) return [];
+
+ const endpointURL = ENDPOINT_URL.replace('hash', validBidRequests[0].params.placementId);
+
+ let imp = validBidRequests.map(br => {
+ let impObject = {
+ id: br.bidId,
+ secure: 1
+ };
+
+ if (br.mediaTypes.banner) {
+ impObject.banner = createBannerRequest(br);
+ } else if (br.mediaTypes.video) {
+ impObject.video = createVideoRequest(br);
+ } else if (br.mediaTypes.native) {
+ impObject.native = {
+ id: br.transactionId,
+ ver: '1.2',
+ request: createNativeRequest(br)
+ };
+ }
+ return impObject;
+ });
+
+ let w = window;
+ let l = w.document.location.href;
+ let r = w.document.referrer;
+
+ let loopChecker = 0;
+ while (w !== w.parent) {
+ if (++loopChecker == 10) break;
+ try {
+ w = w.parent;
+ l = w.location.href;
+ r = w.document.referrer;
+ } catch (e) {
+ break;
+ }
+ }
+
+ let page = l || bidderRequest.refererInfo.referer;
+
+ let data = {
+ id: bidderRequest.bidderRequestId,
+ cur: [ DEFAULT_CUR ],
+ device: {
+ w: screen.width,
+ h: screen.height,
+ language: (navigator && navigator.language) ? navigator.language.indexOf('-') != -1 ? navigator.language.split('-')[0] : navigator.language : '',
+ ua: navigator.userAgent,
+ },
+ site: {
+ domain: utils.parseUrl(page).hostname,
+ page: page,
+ },
+ tmax: bidderRequest.timeout || config.getConfig('bidderTimeout') || 500,
+ imp
+ };
+
+ if (r) {
+ data.site.ref = r;
+ }
+
+ if (bidderRequest.gdprConsent) {
+ data['regs'] = {'ext': {'gdpr': bidderRequest.gdprConsent.gdprApplies ? 1 : 0}};
+ data['user'] = {'ext': {'consent': bidderRequest.gdprConsent.consentString ? bidderRequest.gdprConsent.consentString : ''}};
+ }
+
+ if (bidderRequest.uspConsent !== undefined) {
+ if (!data['regs'])data['regs'] = {'ext': {}};
+ data['regs']['ext']['us_privacy'] = bidderRequest.uspConsent;
+ }
+
+ if (config.getConfig('coppa') === true) {
+ if (!data['regs'])data['regs'] = {'coppa': 1};
+ else data['regs']['coppa'] = 1;
+ }
+
+ if (validBidRequests[0].schain) {
+ data['source'] = {'ext': {'schain': validBidRequests[0].schain}};
+ }
+
+ return {
+ method: 'POST',
+ url: endpointURL,
+ data: data
+ };
+ },
+
+ /**
+ * Unpack the response from the server into a list of bids.
+ *
+ * @param {*} serverResponse A successful response from the server.
+ * @return {Bid[]} An array of bids which were nested inside the server.
+ */
+ interpretResponse: (serverResponse) => {
+ if (!serverResponse || utils.isEmpty(serverResponse.body)) return [];
+
+ let bids = [];
+ serverResponse.body.seatbid.forEach(response => {
+ response.bid.forEach(bid => {
+ let mediaType = bid.ext && bid.ext.mediaType ? bid.ext.mediaType : 'banner';
+
+ let bidObj = {
+ requestId: bid.impid,
+ cpm: bid.price,
+ width: bid.w,
+ height: bid.h,
+ ttl: 1200,
+ currency: DEFAULT_CUR,
+ netRevenue: true,
+ creativeId: bid.crid,
+ dealId: bid.dealid || null,
+ mediaType: mediaType
+ };
+
+ switch (mediaType) {
+ case 'video':
+ bidObj.vastUrl = bid.adm;
+ break;
+ case 'native':
+ bidObj.native = parseNative(bid.adm);
+ break;
+ default:
+ bidObj.ad = bid.adm;
+ }
+
+ bids.push(bidObj);
+ });
+ });
+
+ return bids;
+ },
+
+ onBidWon: (bid) => {
+ if (utils.isStr(bid.nurl) && bid.nurl !== '') {
+ utils.triggerPixel(bid.nurl);
+ }
+ }
+};
+
+const parseNative = adm => {
+ let bid = {
+ clickUrl: adm.native.link && adm.native.link.url,
+ impressionTrackers: adm.native.imptrackers || [],
+ clickTrackers: (adm.native.link && adm.native.link.clicktrackers) || [],
+ jstracker: adm.native.jstracker || []
+ };
+ adm.native.assets.forEach(asset => {
+ let kind = NATIVE_ASSETS_IDS[asset.id];
+ let content = kind && asset[NATIVE_ASSETS[kind].name];
+ if (content) {
+ bid[kind] = content.text || content.value || { url: content.url, width: content.w, height: content.h };
+ }
+ });
+
+ return bid;
+}
+
+const createNativeRequest = br => {
+ let impObject = {
+ ver: '1.2',
+ assets: []
+ };
+
+ let keys = Object.keys(br.mediaTypes.native);
+
+ for (let key of keys) {
+ const props = NATIVE_ASSETS[key];
+ if (props) {
+ const asset = {
+ required: br.mediaTypes.native[key].required ? 1 : 0,
+ id: props.id,
+ [props.name]: {}
+ };
+
+ if (props.type) asset[props.name]['type'] = props.type;
+ if (br.mediaTypes.native[key].len) asset[props.name]['len'] = br.mediaTypes.native[key].len;
+ if (br.mediaTypes.native[key].sizes && br.mediaTypes.native[key].sizes[0]) {
+ asset[props.name]['w'] = br.mediaTypes.native[key].sizes[0];
+ asset[props.name]['h'] = br.mediaTypes.native[key].sizes[1];
+ }
+
+ impObject.assets.push(asset);
+ }
+ }
+
+ return impObject;
+}
+
+const createBannerRequest = br => {
+ let size = [];
+
+ if (br.mediaTypes.banner.sizes && Array.isArray(br.mediaTypes.banner.sizes)) {
+ if (Array.isArray(br.mediaTypes.banner.sizes[0])) { size = br.mediaTypes.banner.sizes[0]; } else { size = br.mediaTypes.banner.sizes; }
+ } else size = [300, 250];
+
+ return { id: br.transactionId, w: size[0], h: size[1] };
+};
+
+const createVideoRequest = br => {
+ let videoObj = {id: br.transactionId};
+ let supportParamsList = ['mimes', 'minduration', 'maxduration', 'protocols', 'startdelay', 'skip', 'minbitrate', 'maxbitrate', 'api', 'linearity'];
+
+ for (let param of supportParamsList) {
+ if (br.mediaTypes.video[param] !== undefined) {
+ videoObj[param] = br.mediaTypes.video[param];
+ }
+ }
+
+ if (br.mediaTypes.video.playerSize && Array.isArray(br.mediaTypes.video.playerSize)) {
+ if (Array.isArray(br.mediaTypes.video.playerSize[0])) {
+ videoObj.w = br.mediaTypes.video.playerSize[0][0];
+ videoObj.h = br.mediaTypes.video.playerSize[0][1];
+ } else {
+ videoObj.w = br.mediaTypes.video.playerSize[0];
+ videoObj.h = br.mediaTypes.video.playerSize[1];
+ }
+ } else {
+ videoObj.w = 640;
+ videoObj.h = 480;
+ }
+
+ return videoObj;
+}
+
+registerBidder(spec);
diff --git a/modules/braveBidAdapter.md b/modules/braveBidAdapter.md
new file mode 100644
index 00000000000..77d2338ff16
--- /dev/null
+++ b/modules/braveBidAdapter.md
@@ -0,0 +1,135 @@
+# Overview
+
+```
+Module Name: Brave Bidder Adapter
+Module Type: Bidder Adapter
+Maintainer: support@thebrave.io
+```
+
+# Description
+
+Module which connects to Brave SSP demand sources
+
+# Test Parameters
+
+
+250x300 banner test
+```
+var adUnits = [{
+ code: 'brave-prebid',
+ mediaTypes: {
+ banner: {
+ sizes: [[300, 250]]
+ }
+ },
+ bids: [{
+ bidder: 'brave',
+ params : {
+ placementId : "to0QI2aPgkbBZq6vgf0oHitouZduz0qw" // test placementId, please replace after test
+ }
+ }]
+}];
+```
+
+native test
+```
+var adUnits = [{
+ code: 'brave-native-prebid',
+ mediaTypes: {
+ native: {
+ title: {
+ required: true,
+ len: 800
+ },
+ image: {
+ required: true,
+ len: 80
+ },
+ sponsoredBy: {
+ required: true
+ },
+ clickUrl: {
+ required: true
+ },
+ privacyLink: {
+ required: false
+ },
+ body: {
+ required: true
+ },
+ icon: {
+ required: true,
+ sizes: [50, 50]
+ }
+ }
+ },
+ bids: [{
+ bidder: 'brave',
+ params: {
+ placementId : "to0QI2aPgkbBZq6vgf0oHitouZduz0qw" // test placementId, please replace after test
+ }
+ }]
+}];
+```
+
+video test
+```
+var adUnits = [{
+ code: 'brave-video-prebid',
+ mediaTypes: {
+ video: {
+ minduration:1,
+ maxduration:999,
+ boxingallowed:1,
+ skip:0,
+ mimes:[
+ 'application/javascript',
+ 'video/mp4'
+ ],
+ playerSize: [[768, 1024]],
+ protocols:[
+ 2,3
+ ],
+ linearity:1,
+ api:[
+ 1,
+ 2
+ ]
+ }
+ },
+ bids: [{
+ bidder: 'brave',
+ params: {
+ placementId : "to0QI2aPgkbBZq6vgf0oHitouZduz0qw" // test placementId, please replace after test
+ }
+ }]
+}];
+```
+
+# Bid Parameters
+## Banner
+
+| Name | Scope | Type | Description | Example
+| ---- | ----- | ---- | ----------- | -------
+| `placementId` | required | String | The placement ID from Brave | "to0QI2aPgkbBZq6vgf0oHitouZduz0qw"
+
+
+# Ad Unit and page Setup:
+
+```html
+
+
+
+```
From 60ac8ba9508049763c0d7e41aa88e859007f5ac2 Mon Sep 17 00:00:00 2001
From: Sacha <35510349+thebraveio@users.noreply.github.com>
Date: Thu, 5 Aug 2021 16:24:49 +0300
Subject: [PATCH 2/8] added test spec file witch covered code least 80 %
---
test/spec/modules/braveBidAdapter_spec.js | 363 ++++++++++++++++++++++
1 file changed, 363 insertions(+)
create mode 100644 test/spec/modules/braveBidAdapter_spec.js
diff --git a/test/spec/modules/braveBidAdapter_spec.js b/test/spec/modules/braveBidAdapter_spec.js
new file mode 100644
index 00000000000..392f3b9f263
--- /dev/null
+++ b/test/spec/modules/braveBidAdapter_spec.js
@@ -0,0 +1,363 @@
+import { expect } from 'chai';
+import { spec } from 'modules/braveBidAdapter.js';
+
+const request_native = {
+ code: 'brave-native-prebid',
+ mediaTypes: {
+ native: {
+ title: {
+ required: true,
+ len: 800
+ },
+ image: {
+ required: true,
+ len: 80
+ },
+ sponsoredBy: {
+ required: true
+ },
+ clickUrl: {
+ required: true
+ },
+ privacyLink: {
+ required: false
+ },
+ body: {
+ required: true
+ },
+ icon: {
+ required: true,
+ sizes: [50, 50]
+ }
+ }
+ },
+ bidder: 'brave',
+ params: {
+ placementId: 'to0QI2aPgkbBZq6vgf0oHitouZduz0qw'
+ }
+};
+
+const request_banner = {
+ code: 'brave-prebid',
+ mediaTypes: {
+ banner: {
+ sizes: [[300, 250]]
+ }
+ },
+ bidder: 'brave',
+ params: {
+ placementId: 'to0QI2aPgkbBZq6vgf0oHitouZduz0qw'
+ }
+}
+
+const bidRequest = {
+ gdprConsent: {
+ consentString: 'HFIDUYFIUYIUYWIPOI87392DSU',
+ gdprApplies: true
+ },
+ uspConsent: 'uspConsentString',
+ bidderRequestId: 'testid',
+ refererInfo: {
+ referer: 'testdomain.com'
+ },
+ timeout: 700
+}
+
+const request_video = {
+ code: 'brave-video-prebid',
+ mediaTypes: { video: {
+ minduration: 1,
+ maxduration: 999,
+ boxingallowed: 1,
+ skip: 0,
+ mimes: [
+ 'application/javascript',
+ 'video/mp4'
+ ],
+ playerSize: [[768, 1024]],
+ protocols: [
+ 2, 3
+ ],
+ linearity: 1,
+ api: [
+ 1,
+ 2
+ ]
+ }
+ },
+
+ bidder: 'brave',
+ params: {
+ placementId: 'to0QI2aPgkbBZq6vgf0oHitouZduz0qw'
+ }
+
+}
+
+const response_banner = {
+ id: 'request_id',
+ bidid: 'request_imp_id',
+ seatbid: [{
+ bid: [{
+ id: 'bid_id',
+ impid: 'request_imp_id',
+ price: 5,
+ adomain: ['example.com'],
+ adm: 'admcode',
+ crid: 'crid',
+ ext: {
+ mediaType: 'banner'
+ }
+ }]
+ }]
+};
+
+const response_video = {
+ id: 'request_id',
+ bidid: 'request_imp_id',
+ seatbid: [{
+ bid: [{
+ id: 'bid_id',
+ impid: 'request_imp_id',
+ price: 5,
+ adomain: ['example.com'],
+ adm: 'admcode',
+ crid: 'crid',
+ ext: {
+ mediaType: 'video'
+ }
+ }],
+ }],
+};
+
+let imgData = {
+ url: `https://example.com/image`,
+ w: 1200,
+ h: 627
+};
+
+const response_native = {
+ id: 'request_id',
+ bidid: 'request_imp_id',
+ seatbid: [{
+ bid: [{
+ id: 'bid_id',
+ impid: 'request_imp_id',
+ price: 5,
+ adomain: ['example.com'],
+ adm: { native:
+ {
+ assets: [
+ {id: 1, title: 'dummyText'},
+ {id: 3, image: imgData},
+ {
+ id: 5,
+ data: {value: 'organization.name'}
+ }
+ ],
+ link: {url: 'example.com'},
+ imptrackers: ['tracker1.com', 'tracker2.com', 'tracker3.com'],
+ jstracker: 'tracker1.com'
+ }
+ },
+ crid: 'crid',
+ ext: {
+ mediaType: 'native'
+ }
+ }],
+ }],
+};
+
+describe('BraveBidAdapter', function() {
+ describe('isBidRequestValid', function() {
+ it('should return true when required params found', function () {
+ expect(spec.isBidRequestValid(request_banner)).to.equal(true);
+ });
+
+ it('should return false when required params are not passed', function () {
+ let bid = Object.assign({}, request_banner);
+ bid.params = {
+ 'IncorrectParam': 0
+ };
+ expect(spec.isBidRequestValid(bid)).to.equal(false);
+ });
+ });
+
+ describe('build Native Request', function () {
+ const request = spec.buildRequests([request_native], bidRequest);
+
+ it('Creates a ServerRequest object with method, URL and data', function () {
+ expect(request).to.exist;
+ expect(request.method).to.exist;
+ expect(request.url).to.exist;
+ expect(request.data).to.exist;
+ });
+
+ it('sends bid request to our endpoint via POST', function () {
+ expect(request.method).to.equal('POST');
+ });
+
+ it('Returns valid URL', function () {
+ expect(request.url).to.equal('https://point.bravegroup.tv/?t=2&partner=to0QI2aPgkbBZq6vgf0oHitouZduz0qw');
+ });
+
+ it('Returns empty data if no valid requests are passed', function () {
+ let serverRequest = spec.buildRequests([]);
+ expect(serverRequest).to.be.an('array').that.is.empty;
+ });
+ });
+
+ describe('build Banner Request', function () {
+ const request = spec.buildRequests([request_banner], bidRequest);
+
+ it('Creates a ServerRequest object with method, URL and data', function () {
+ expect(request).to.exist;
+ expect(request.method).to.exist;
+ expect(request.url).to.exist;
+ expect(request.data).to.exist;
+ });
+
+ it('sends bid request to our endpoint via POST', function () {
+ expect(request.method).to.equal('POST');
+ });
+
+ it('Returns valid URL', function () {
+ expect(request.url).to.equal('https://point.bravegroup.tv/?t=2&partner=to0QI2aPgkbBZq6vgf0oHitouZduz0qw');
+ });
+ });
+
+ describe('build Video Request', function () {
+ const request = spec.buildRequests([request_video], bidRequest);
+
+ it('Creates a ServerRequest object with method, URL and data', function () {
+ expect(request).to.exist;
+ expect(request.method).to.exist;
+ expect(request.url).to.exist;
+ expect(request.data).to.exist;
+ });
+
+ it('sends bid request to our endpoint via POST', function () {
+ expect(request.method).to.equal('POST');
+ });
+
+ it('Returns valid URL', function () {
+ expect(request.url).to.equal('https://point.bravegroup.tv/?t=2&partner=to0QI2aPgkbBZq6vgf0oHitouZduz0qw');
+ });
+ });
+
+ describe('interpretResponse', function () {
+ it('Empty response must return empty array', function() {
+ const emptyResponse = null;
+ let response = spec.interpretResponse(emptyResponse);
+
+ expect(response).to.be.an('array').that.is.empty;
+ })
+
+ it('Should interpret banner response', function () {
+ const bannerResponse = {
+ body: response_banner
+ }
+
+ const expectedBidResponse = {
+ requestId: response_banner.seatbid[0].bid[0].impid,
+ cpm: response_banner.seatbid[0].bid[0].price,
+ width: response_banner.seatbid[0].bid[0].w,
+ height: response_banner.seatbid[0].bid[0].h,
+ ttl: response_banner.ttl || 1200,
+ currency: response_banner.cur || 'USD',
+ netRevenue: true,
+ creativeId: response_banner.seatbid[0].bid[0].crid,
+ dealId: response_banner.seatbid[0].bid[0].dealid,
+ mediaType: 'banner',
+ ad: response_banner.seatbid[0].bid[0].adm
+ }
+
+ let bannerResponses = spec.interpretResponse(bannerResponse);
+
+ expect(bannerResponses).to.be.an('array').that.is.not.empty;
+ let dataItem = bannerResponses[0];
+ expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId',
+ 'netRevenue', 'currency', 'dealId', 'mediaType');
+ expect(dataItem.requestId).to.equal(expectedBidResponse.requestId);
+ expect(dataItem.cpm).to.equal(expectedBidResponse.cpm);
+ expect(dataItem.ad).to.equal(expectedBidResponse.ad);
+ expect(dataItem.ttl).to.equal(expectedBidResponse.ttl);
+ expect(dataItem.creativeId).to.equal(expectedBidResponse.creativeId);
+ expect(dataItem.netRevenue).to.be.true;
+ expect(dataItem.currency).to.equal(expectedBidResponse.currency);
+ expect(dataItem.width).to.equal(expectedBidResponse.width);
+ expect(dataItem.height).to.equal(expectedBidResponse.height);
+ });
+
+ it('Should interpret video response', function () {
+ const videoResponse = {
+ body: response_video
+ }
+
+ const expectedBidResponse = {
+ requestId: response_video.seatbid[0].bid[0].impid,
+ cpm: response_video.seatbid[0].bid[0].price,
+ width: response_video.seatbid[0].bid[0].w,
+ height: response_video.seatbid[0].bid[0].h,
+ ttl: response_video.ttl || 1200,
+ currency: response_video.cur || 'USD',
+ netRevenue: true,
+ creativeId: response_video.seatbid[0].bid[0].crid,
+ dealId: response_video.seatbid[0].bid[0].dealid,
+ mediaType: 'video',
+ vastUrl: response_video.seatbid[0].bid[0].adm
+ }
+
+ let videoResponses = spec.interpretResponse(videoResponse);
+
+ expect(videoResponses).to.be.an('array').that.is.not.empty;
+ let dataItem = videoResponses[0];
+ expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'vastUrl', 'ttl', 'creativeId',
+ 'netRevenue', 'currency', 'dealId', 'mediaType');
+ expect(dataItem.requestId).to.equal(expectedBidResponse.requestId);
+ expect(dataItem.cpm).to.equal(expectedBidResponse.cpm);
+ expect(dataItem.vastUrl).to.equal(expectedBidResponse.vastUrl)
+ expect(dataItem.ttl).to.equal(expectedBidResponse.ttl);
+ expect(dataItem.creativeId).to.equal(expectedBidResponse.creativeId);
+ expect(dataItem.netRevenue).to.be.true;
+ expect(dataItem.currency).to.equal(expectedBidResponse.currency);
+ expect(dataItem.width).to.equal(expectedBidResponse.width);
+ expect(dataItem.height).to.equal(expectedBidResponse.height);
+ });
+
+ it('Should interpret native response', function () {
+ const nativeResponse = {
+ body: response_native
+ }
+
+ const expectedBidResponse = {
+ requestId: response_native.seatbid[0].bid[0].impid,
+ cpm: response_native.seatbid[0].bid[0].price,
+ width: response_native.seatbid[0].bid[0].w,
+ height: response_native.seatbid[0].bid[0].h,
+ ttl: response_native.ttl || 1200,
+ currency: response_native.cur || 'USD',
+ netRevenue: true,
+ creativeId: response_native.seatbid[0].bid[0].crid,
+ dealId: response_native.seatbid[0].bid[0].dealid,
+ mediaType: 'native',
+ native: {clickUrl: response_native.seatbid[0].bid[0].adm.native.link.url}
+ }
+
+ let nativeResponses = spec.interpretResponse(nativeResponse);
+
+ expect(nativeResponses).to.be.an('array').that.is.not.empty;
+ let dataItem = nativeResponses[0];
+ expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'native', 'ttl', 'creativeId',
+ 'netRevenue', 'currency', 'dealId', 'mediaType');
+ expect(dataItem.requestId).to.equal(expectedBidResponse.requestId);
+ expect(dataItem.cpm).to.equal(expectedBidResponse.cpm);
+ expect(dataItem.native.clickUrl).to.equal(expectedBidResponse.native.clickUrl)
+ expect(dataItem.ttl).to.equal(expectedBidResponse.ttl);
+ expect(dataItem.creativeId).to.equal(expectedBidResponse.creativeId);
+ expect(dataItem.netRevenue).to.be.true;
+ expect(dataItem.currency).to.equal(expectedBidResponse.currency);
+ expect(dataItem.width).to.equal(expectedBidResponse.width);
+ expect(dataItem.height).to.equal(expectedBidResponse.height);
+ });
+ });
+})
From 2f68f67b9db0180e9f09f827a4c5aa1211c44a6a Mon Sep 17 00:00:00 2001
From: Sacha <35510349+thebraveio@users.noreply.github.com>
Date: Thu, 21 Apr 2022 15:42:51 +0300
Subject: [PATCH 3/8] adding videoHeroes bidder adapter
---
modules/videoheroesBidAdapter.js | 267 +++++++++++++++++++++++++++++++
modules/videoheroesBidAdapter.md | 134 ++++++++++++++++
2 files changed, 401 insertions(+)
create mode 100644 modules/videoheroesBidAdapter.js
create mode 100644 modules/videoheroesBidAdapter.md
diff --git a/modules/videoheroesBidAdapter.js b/modules/videoheroesBidAdapter.js
new file mode 100644
index 00000000000..769f354294e
--- /dev/null
+++ b/modules/videoheroesBidAdapter.js
@@ -0,0 +1,267 @@
+import * as utils from '../src/utils.js';
+import { registerBidder } from '../src/adapters/bidderFactory.js';
+import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
+import { config } from '../src/config.js';
+
+const BIDDER_CODE = 'videoheroes';
+const DEFAULT_CUR = 'USD';
+const ENDPOINT_URL = `https://point.contextualadv.com/?t=2&partner=hash`;
+
+const NATIVE_ASSETS_IDS = { 1: 'title', 2: 'icon', 3: 'image', 4: 'body', 5: 'sponsoredBy', 6: 'cta' };
+const NATIVE_ASSETS = {
+ title: { id: 1, name: 'title' },
+ icon: { id: 2, type: 1, name: 'img' },
+ image: { id: 3, type: 3, name: 'img' },
+ body: { id: 4, type: 2, name: 'data' },
+ sponsoredBy: { id: 5, type: 1, name: 'data' },
+ cta: { id: 6, type: 12, name: 'data' }
+};
+
+export const spec = {
+ code: BIDDER_CODE,
+ supportedMediaTypes: [BANNER, VIDEO, NATIVE],
+
+ /**
+ * Determines whether or not the given bid request is valid.
+ *
+ * @param {object} bid The bid to validate.
+ * @return boolean True if this is a valid bid, and false otherwise.
+ */
+ isBidRequestValid: (bid) => {
+ return !!(bid.params.placementId && bid.params.placementId.toString().length === 32);
+ },
+
+ /**
+ * Make a server request from the list of BidRequests.
+ *
+ * @param {BidRequest[]} validBidRequests A non-empty list of valid bid requests that should be sent to the Server.
+ * @return ServerRequest Info describing the request to the server.
+ */
+ buildRequests: (validBidRequests, bidderRequest) => {
+ if (validBidRequests.length === 0 || !bidderRequest) return [];
+
+ const endpointURL = ENDPOINT_URL.replace('hash', validBidRequests[0].params.placementId);
+
+ let imp = validBidRequests.map(br => {
+ let impObject = {
+ id: br.bidId,
+ secure: 1
+ };
+
+ if (br.mediaTypes.banner) {
+ impObject.banner = createBannerRequest(br);
+ } else if (br.mediaTypes.video) {
+ impObject.video = createVideoRequest(br);
+ } else if (br.mediaTypes.native) {
+ impObject.native = {
+ id: br.transactionId,
+ ver: '1.2',
+ request: createNativeRequest(br)
+ };
+ }
+ return impObject;
+ });
+
+ let w = window;
+ let l = w.document.location.href;
+ let r = w.document.referrer;
+
+ let loopChecker = 0;
+ while (w !== w.parent) {
+ if (++loopChecker == 10) break;
+ try {
+ w = w.parent;
+ l = w.location.href;
+ r = w.document.referrer;
+ } catch (e) {
+ break;
+ }
+ }
+
+ let page = l || bidderRequest.refererInfo.referer;
+
+ let data = {
+ id: bidderRequest.bidderRequestId,
+ cur: [ DEFAULT_CUR ],
+ device: {
+ w: screen.width,
+ h: screen.height,
+ language: (navigator && navigator.language) ? navigator.language.indexOf('-') != -1 ? navigator.language.split('-')[0] : navigator.language : '',
+ ua: navigator.userAgent,
+ },
+ site: {
+ domain: utils.parseUrl(page).hostname,
+ page: page,
+ },
+ tmax: bidderRequest.timeout || config.getConfig('bidderTimeout') || 500,
+ imp
+ };
+
+ if (r) {
+ data.site.ref = r;
+ }
+
+ if (bidderRequest.gdprConsent) {
+ data['regs'] = {'ext': {'gdpr': bidderRequest.gdprConsent.gdprApplies ? 1 : 0}};
+ data['user'] = {'ext': {'consent': bidderRequest.gdprConsent.consentString ? bidderRequest.gdprConsent.consentString : ''}};
+ }
+
+ if (bidderRequest.uspConsent !== undefined) {
+ if (!data['regs'])data['regs'] = {'ext': {}};
+ data['regs']['ext']['us_privacy'] = bidderRequest.uspConsent;
+ }
+
+ if (config.getConfig('coppa') === true) {
+ if (!data['regs'])data['regs'] = {'coppa': 1};
+ else data['regs']['coppa'] = 1;
+ }
+
+ if (validBidRequests[0].schain) {
+ data['source'] = {'ext': {'schain': validBidRequests[0].schain}};
+ }
+
+ return {
+ method: 'POST',
+ url: endpointURL,
+ data: data
+ };
+ },
+
+ /**
+ * Unpack the response from the server into a list of bids.
+ *
+ * @param {*} serverResponse A successful response from the server.
+ * @return {Bid[]} An array of bids which were nested inside the server.
+ */
+ interpretResponse: (serverResponse) => {
+ if (!serverResponse || utils.isEmpty(serverResponse.body)) return [];
+
+ let bids = [];
+ serverResponse.body.seatbid.forEach(response => {
+ response.bid.forEach(bid => {
+ let mediaType = bid.ext && bid.ext.mediaType ? bid.ext.mediaType : 'banner';
+
+ let bidObj = {
+ requestId: bid.impid,
+ cpm: bid.price,
+ width: bid.w,
+ height: bid.h,
+ ttl: 1200,
+ currency: DEFAULT_CUR,
+ netRevenue: true,
+ creativeId: bid.crid,
+ dealId: bid.dealid || null,
+ mediaType: mediaType
+ };
+
+ switch (mediaType) {
+ case 'video':
+ bidObj.vastUrl = bid.adm;
+ break;
+ case 'native':
+ bidObj.native = parseNative(bid.adm);
+ break;
+ default:
+ bidObj.ad = bid.adm;
+ }
+
+ bids.push(bidObj);
+ });
+ });
+
+ return bids;
+ },
+
+ onBidWon: (bid) => {
+ if (utils.isStr(bid.nurl) && bid.nurl !== '') {
+ utils.triggerPixel(bid.nurl);
+ }
+ }
+};
+
+const parseNative = adm => {
+ let bid = {
+ clickUrl: adm.native.link && adm.native.link.url,
+ impressionTrackers: adm.native.imptrackers || [],
+ clickTrackers: (adm.native.link && adm.native.link.clicktrackers) || [],
+ jstracker: adm.native.jstracker || []
+ };
+ adm.native.assets.forEach(asset => {
+ let kind = NATIVE_ASSETS_IDS[asset.id];
+ let content = kind && asset[NATIVE_ASSETS[kind].name];
+ if (content) {
+ bid[kind] = content.text || content.value || { url: content.url, width: content.w, height: content.h };
+ }
+ });
+
+ return bid;
+}
+
+const createNativeRequest = br => {
+ let impObject = {
+ ver: '1.2',
+ assets: []
+ };
+
+ let keys = Object.keys(br.mediaTypes.native);
+
+ for (let key of keys) {
+ const props = NATIVE_ASSETS[key];
+ if (props) {
+ const asset = {
+ required: br.mediaTypes.native[key].required ? 1 : 0,
+ id: props.id,
+ [props.name]: {}
+ };
+
+ if (props.type) asset[props.name]['type'] = props.type;
+ if (br.mediaTypes.native[key].len) asset[props.name]['len'] = br.mediaTypes.native[key].len;
+ if (br.mediaTypes.native[key].sizes && br.mediaTypes.native[key].sizes[0]) {
+ asset[props.name]['w'] = br.mediaTypes.native[key].sizes[0];
+ asset[props.name]['h'] = br.mediaTypes.native[key].sizes[1];
+ }
+
+ impObject.assets.push(asset);
+ }
+ }
+
+ return impObject;
+}
+
+const createBannerRequest = br => {
+ let size = [];
+
+ if (br.mediaTypes.banner.sizes && Array.isArray(br.mediaTypes.banner.sizes)) {
+ if (Array.isArray(br.mediaTypes.banner.sizes[0])) { size = br.mediaTypes.banner.sizes[0]; } else { size = br.mediaTypes.banner.sizes; }
+ } else size = [300, 250];
+
+ return { id: br.transactionId, w: size[0], h: size[1] };
+};
+
+const createVideoRequest = br => {
+ let videoObj = {id: br.transactionId};
+ let supportParamsList = ['mimes', 'minduration', 'maxduration', 'protocols', 'startdelay', 'skip', 'minbitrate', 'maxbitrate', 'api', 'linearity'];
+
+ for (let param of supportParamsList) {
+ if (br.mediaTypes.video[param] !== undefined) {
+ videoObj[param] = br.mediaTypes.video[param];
+ }
+ }
+
+ if (br.mediaTypes.video.playerSize && Array.isArray(br.mediaTypes.video.playerSize)) {
+ if (Array.isArray(br.mediaTypes.video.playerSize[0])) {
+ videoObj.w = br.mediaTypes.video.playerSize[0][0];
+ videoObj.h = br.mediaTypes.video.playerSize[0][1];
+ } else {
+ videoObj.w = br.mediaTypes.video.playerSize[0];
+ videoObj.h = br.mediaTypes.video.playerSize[1];
+ }
+ } else {
+ videoObj.w = 640;
+ videoObj.h = 480;
+ }
+
+ return videoObj;
+}
+
+registerBidder(spec);
diff --git a/modules/videoheroesBidAdapter.md b/modules/videoheroesBidAdapter.md
new file mode 100644
index 00000000000..2848f1462cd
--- /dev/null
+++ b/modules/videoheroesBidAdapter.md
@@ -0,0 +1,134 @@
+# Overview
+
+```
+Module Name: Video Heroes Bidder Adapter
+Module Type: Bidder Adapter
+Maintainer: support@videoheroes.tv
+```
+
+# Description
+
+Module which connects to VideoHeroes SSP demand sources
+
+# Test Parameters
+
+250x300 banner test
+```
+var adUnits = [{
+ code: 'videoheroes-prebid',
+ mediaTypes: {
+ banner: {
+ sizes: [[300, 250]]
+ }
+ },
+ bids: [{
+ bidder: 'videoheroes',
+ params : {
+ placementId : "to0QI2aPgkbBZq6vgf0oHitouZduz0qw" // test placementId, please replace after test
+ }
+ }]
+}];
+```
+
+native test
+```
+var adUnits = [{
+ code: 'videoheroes-native-prebid',
+ mediaTypes: {
+ native: {
+ title: {
+ required: true,
+ len: 800
+ },
+ image: {
+ required: true,
+ len: 80
+ },
+ sponsoredBy: {
+ required: true
+ },
+ clickUrl: {
+ required: true
+ },
+ privacyLink: {
+ required: false
+ },
+ body: {
+ required: true
+ },
+ icon: {
+ required: true,
+ sizes: [50, 50]
+ }
+ }
+ },
+ bids: [{
+ bidder: 'videoheroes',
+ params: {
+ placementId : "to0QI2aPgkbBZq6vgf0oHitouZduz0qw" // test placementId, please replace after test
+ }
+ }]
+}];
+```
+
+video test
+```
+var adUnits = [{
+ code: 'videoheroes-video-prebid',
+ mediaTypes: {
+ video: {
+ minduration:1,
+ maxduration:999,
+ boxingallowed:1,
+ skip:0,
+ mimes:[
+ 'application/javascript',
+ 'video/mp4'
+ ],
+ playerSize: [[768, 1024]],
+ protocols:[
+ 2,3
+ ],
+ linearity:1,
+ api:[
+ 1,
+ 2
+ ]
+ }
+ },
+ bids: [{
+ bidder: 'videoheroes',
+ params: {
+ placementId : "to0QI2aPgkbBZq6vgf0oHitouZduz0qw" // test placementId, please replace after test
+ }
+ }]
+}];
+```
+
+# Bid Parameters
+## Banner
+
+| Name | Scope | Type | Description | Example
+| ---- | ----- | ---- | ----------- | -------
+| `placementId` | required | String | The placement ID from Video Heroes | "to0QI2aPgkbBZq6vgf0oHitouZduz0qw"
+
+
+# Ad Unit and page Setup:
+
+```html
+
+
+
+```
From 532844dfdd9605206b1a84dd993697a30c2cc944 Mon Sep 17 00:00:00 2001
From: Sacha <35510349+thebraveio@users.noreply.github.com>
Date: Thu, 21 Apr 2022 15:49:27 +0300
Subject: [PATCH 4/8] added videoHeroes test spec
---
.../modules/videoheroesBidAdapter_spec.js | 363 ++++++++++++++++++
1 file changed, 363 insertions(+)
create mode 100644 test/spec/modules/videoheroesBidAdapter_spec.js
diff --git a/test/spec/modules/videoheroesBidAdapter_spec.js b/test/spec/modules/videoheroesBidAdapter_spec.js
new file mode 100644
index 00000000000..8f99ca4d17d
--- /dev/null
+++ b/test/spec/modules/videoheroesBidAdapter_spec.js
@@ -0,0 +1,363 @@
+import { expect } from 'chai';
+import { spec } from 'modules/videoheroesBidAdapter.js';
+
+const request_native = {
+ code: 'videoheroes-native-prebid',
+ mediaTypes: {
+ native: {
+ title: {
+ required: true,
+ len: 800
+ },
+ image: {
+ required: true,
+ len: 80
+ },
+ sponsoredBy: {
+ required: true
+ },
+ clickUrl: {
+ required: true
+ },
+ privacyLink: {
+ required: false
+ },
+ body: {
+ required: true
+ },
+ icon: {
+ required: true,
+ sizes: [50, 50]
+ }
+ }
+ },
+ bidder: 'videoheroes',
+ params: {
+ placementId: '1a8d9c22db19906cb8a5fd4518d05f62'
+ }
+};
+
+const request_banner = {
+ code: 'videoheroes-prebid',
+ mediaTypes: {
+ banner: {
+ sizes: [[300, 250]]
+ }
+ },
+ bidder: 'videoheroes',
+ params: {
+ placementId: '1a8d9c22db19906cb8a5fd4518d05f62'
+ }
+}
+
+const bidRequest = {
+ gdprConsent: {
+ consentString: 'HFIDUYFIUYIUYWIPOI87392DSU',
+ gdprApplies: true
+ },
+ uspConsent: 'uspConsentString',
+ bidderRequestId: 'testid',
+ refererInfo: {
+ referer: 'testdomain.com'
+ },
+ timeout: 700
+}
+
+const request_video = {
+ code: 'videoheroes-video-prebid',
+ mediaTypes: { video: {
+ minduration: 1,
+ maxduration: 999,
+ boxingallowed: 1,
+ skip: 0,
+ mimes: [
+ 'application/javascript',
+ 'video/mp4'
+ ],
+ playerSize: [[768, 1024]],
+ protocols: [
+ 2, 3
+ ],
+ linearity: 1,
+ api: [
+ 1,
+ 2
+ ]
+ }
+ },
+
+ bidder: 'videoheroes',
+ params: {
+ placementId: '1a8d9c22db19906cb8a5fd4518d05f62'
+ }
+
+}
+
+const response_banner = {
+ id: 'request_id',
+ bidid: 'request_imp_id',
+ seatbid: [{
+ bid: [{
+ id: 'bid_id',
+ impid: 'request_imp_id',
+ price: 5,
+ adomain: ['example.com'],
+ adm: 'admcode',
+ crid: 'crid',
+ ext: {
+ mediaType: 'banner'
+ }
+ }]
+ }]
+};
+
+const response_video = {
+ id: 'request_id',
+ bidid: 'request_imp_id',
+ seatbid: [{
+ bid: [{
+ id: 'bid_id',
+ impid: 'request_imp_id',
+ price: 5,
+ adomain: ['example.com'],
+ adm: 'admcode',
+ crid: 'crid',
+ ext: {
+ mediaType: 'video'
+ }
+ }],
+ }],
+};
+
+let imgData = {
+ url: `https://example.com/image`,
+ w: 1200,
+ h: 627
+};
+
+const response_native = {
+ id: 'request_id',
+ bidid: 'request_imp_id',
+ seatbid: [{
+ bid: [{
+ id: 'bid_id',
+ impid: 'request_imp_id',
+ price: 5,
+ adomain: ['example.com'],
+ adm: { native:
+ {
+ assets: [
+ {id: 1, title: 'dummyText'},
+ {id: 3, image: imgData},
+ {
+ id: 5,
+ data: {value: 'organization.name'}
+ }
+ ],
+ link: {url: 'example.com'},
+ imptrackers: ['tracker1.com', 'tracker2.com', 'tracker3.com'],
+ jstracker: 'tracker1.com'
+ }
+ },
+ crid: 'crid',
+ ext: {
+ mediaType: 'native'
+ }
+ }],
+ }],
+};
+
+describe('VideoheroesBidAdapter', function() {
+ describe('isBidRequestValid', function() {
+ it('should return true when required params found', function () {
+ expect(spec.isBidRequestValid(request_banner)).to.equal(true);
+ });
+
+ it('should return false when required params are not passed', function () {
+ let bid = Object.assign({}, request_banner);
+ bid.params = {
+ 'IncorrectParam': 0
+ };
+ expect(spec.isBidRequestValid(bid)).to.equal(false);
+ });
+ });
+
+ describe('build Native Request', function () {
+ const request = spec.buildRequests([request_native], bidRequest);
+
+ it('Creates a ServerRequest object with method, URL and data', function () {
+ expect(request).to.exist;
+ expect(request.method).to.exist;
+ expect(request.url).to.exist;
+ expect(request.data).to.exist;
+ });
+
+ it('sends bid request to our endpoint via POST', function () {
+ expect(request.method).to.equal('POST');
+ });
+
+ it('Returns valid URL', function () {
+ expect(request.url).to.equal('https://point.contextualadv.com/?t=2&partner=1a8d9c22db19906cb8a5fd4518d05f62');
+ });
+
+ it('Returns empty data if no valid requests are passed', function () {
+ let serverRequest = spec.buildRequests([]);
+ expect(serverRequest).to.be.an('array').that.is.empty;
+ });
+ });
+
+ describe('build Banner Request', function () {
+ const request = spec.buildRequests([request_banner], bidRequest);
+
+ it('Creates a ServerRequest object with method, URL and data', function () {
+ expect(request).to.exist;
+ expect(request.method).to.exist;
+ expect(request.url).to.exist;
+ expect(request.data).to.exist;
+ });
+
+ it('sends bid request to our endpoint via POST', function () {
+ expect(request.method).to.equal('POST');
+ });
+
+ it('Returns valid URL', function () {
+ expect(request.url).to.equal('https://point.contextualadv.com/?t=2&partner=1a8d9c22db19906cb8a5fd4518d05f62');
+ });
+ });
+
+ describe('build Video Request', function () {
+ const request = spec.buildRequests([request_video], bidRequest);
+
+ it('Creates a ServerRequest object with method, URL and data', function () {
+ expect(request).to.exist;
+ expect(request.method).to.exist;
+ expect(request.url).to.exist;
+ expect(request.data).to.exist;
+ });
+
+ it('sends bid request to our endpoint via POST', function () {
+ expect(request.method).to.equal('POST');
+ });
+
+ it('Returns valid URL', function () {
+ expect(request.url).to.equal('https://point.contextualadv.com/?t=2&partner=1a8d9c22db19906cb8a5fd4518d05f62');
+ });
+ });
+
+ describe('interpretResponse', function () {
+ it('Empty response must return empty array', function() {
+ const emptyResponse = null;
+ let response = spec.interpretResponse(emptyResponse);
+
+ expect(response).to.be.an('array').that.is.empty;
+ })
+
+ it('Should interpret banner response', function () {
+ const bannerResponse = {
+ body: response_banner
+ }
+
+ const expectedBidResponse = {
+ requestId: response_banner.seatbid[0].bid[0].impid,
+ cpm: response_banner.seatbid[0].bid[0].price,
+ width: response_banner.seatbid[0].bid[0].w,
+ height: response_banner.seatbid[0].bid[0].h,
+ ttl: response_banner.ttl || 1200,
+ currency: response_banner.cur || 'USD',
+ netRevenue: true,
+ creativeId: response_banner.seatbid[0].bid[0].crid,
+ dealId: response_banner.seatbid[0].bid[0].dealid,
+ mediaType: 'banner',
+ ad: response_banner.seatbid[0].bid[0].adm
+ }
+
+ let bannerResponses = spec.interpretResponse(bannerResponse);
+
+ expect(bannerResponses).to.be.an('array').that.is.not.empty;
+ let dataItem = bannerResponses[0];
+ expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId',
+ 'netRevenue', 'currency', 'dealId', 'mediaType');
+ expect(dataItem.requestId).to.equal(expectedBidResponse.requestId);
+ expect(dataItem.cpm).to.equal(expectedBidResponse.cpm);
+ expect(dataItem.ad).to.equal(expectedBidResponse.ad);
+ expect(dataItem.ttl).to.equal(expectedBidResponse.ttl);
+ expect(dataItem.creativeId).to.equal(expectedBidResponse.creativeId);
+ expect(dataItem.netRevenue).to.be.true;
+ expect(dataItem.currency).to.equal(expectedBidResponse.currency);
+ expect(dataItem.width).to.equal(expectedBidResponse.width);
+ expect(dataItem.height).to.equal(expectedBidResponse.height);
+ });
+
+ it('Should interpret video response', function () {
+ const videoResponse = {
+ body: response_video
+ }
+
+ const expectedBidResponse = {
+ requestId: response_video.seatbid[0].bid[0].impid,
+ cpm: response_video.seatbid[0].bid[0].price,
+ width: response_video.seatbid[0].bid[0].w,
+ height: response_video.seatbid[0].bid[0].h,
+ ttl: response_video.ttl || 1200,
+ currency: response_video.cur || 'USD',
+ netRevenue: true,
+ creativeId: response_video.seatbid[0].bid[0].crid,
+ dealId: response_video.seatbid[0].bid[0].dealid,
+ mediaType: 'video',
+ vastUrl: response_video.seatbid[0].bid[0].adm
+ }
+
+ let videoResponses = spec.interpretResponse(videoResponse);
+
+ expect(videoResponses).to.be.an('array').that.is.not.empty;
+ let dataItem = videoResponses[0];
+ expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'vastUrl', 'ttl', 'creativeId',
+ 'netRevenue', 'currency', 'dealId', 'mediaType');
+ expect(dataItem.requestId).to.equal(expectedBidResponse.requestId);
+ expect(dataItem.cpm).to.equal(expectedBidResponse.cpm);
+ expect(dataItem.vastUrl).to.equal(expectedBidResponse.vastUrl)
+ expect(dataItem.ttl).to.equal(expectedBidResponse.ttl);
+ expect(dataItem.creativeId).to.equal(expectedBidResponse.creativeId);
+ expect(dataItem.netRevenue).to.be.true;
+ expect(dataItem.currency).to.equal(expectedBidResponse.currency);
+ expect(dataItem.width).to.equal(expectedBidResponse.width);
+ expect(dataItem.height).to.equal(expectedBidResponse.height);
+ });
+
+ it('Should interpret native response', function () {
+ const nativeResponse = {
+ body: response_native
+ }
+
+ const expectedBidResponse = {
+ requestId: response_native.seatbid[0].bid[0].impid,
+ cpm: response_native.seatbid[0].bid[0].price,
+ width: response_native.seatbid[0].bid[0].w,
+ height: response_native.seatbid[0].bid[0].h,
+ ttl: response_native.ttl || 1200,
+ currency: response_native.cur || 'USD',
+ netRevenue: true,
+ creativeId: response_native.seatbid[0].bid[0].crid,
+ dealId: response_native.seatbid[0].bid[0].dealid,
+ mediaType: 'native',
+ native: {clickUrl: response_native.seatbid[0].bid[0].adm.native.link.url}
+ }
+
+ let nativeResponses = spec.interpretResponse(nativeResponse);
+
+ expect(nativeResponses).to.be.an('array').that.is.not.empty;
+ let dataItem = nativeResponses[0];
+ expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'native', 'ttl', 'creativeId',
+ 'netRevenue', 'currency', 'dealId', 'mediaType');
+ expect(dataItem.requestId).to.equal(expectedBidResponse.requestId);
+ expect(dataItem.cpm).to.equal(expectedBidResponse.cpm);
+ expect(dataItem.native.clickUrl).to.equal(expectedBidResponse.native.clickUrl)
+ expect(dataItem.ttl).to.equal(expectedBidResponse.ttl);
+ expect(dataItem.creativeId).to.equal(expectedBidResponse.creativeId);
+ expect(dataItem.netRevenue).to.be.true;
+ expect(dataItem.currency).to.equal(expectedBidResponse.currency);
+ expect(dataItem.width).to.equal(expectedBidResponse.width);
+ expect(dataItem.height).to.equal(expectedBidResponse.height);
+ });
+ });
+})
From 7641e7ab99ea47cce2321c7a34fc1f872d4a2123 Mon Sep 17 00:00:00 2001
From: Sacha <35510349+thebraveio@users.noreply.github.com>
Date: Thu, 21 Apr 2022 22:43:02 +0300
Subject: [PATCH 5/8] Update braveBidAdapter.js
---
modules/braveBidAdapter.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/modules/braveBidAdapter.js b/modules/braveBidAdapter.js
index d616772cb5b..18bad6b0f75 100644
--- a/modules/braveBidAdapter.js
+++ b/modules/braveBidAdapter.js
@@ -173,7 +173,6 @@ export const spec = {
},
onBidWon: (bid) => {
-
if (isStr(bid.nurl) && bid.nurl !== '') {
triggerPixel(bid.nurl);
}
From 3ec4a440815cf962ee668284e9b1b16de4b6e006 Mon Sep 17 00:00:00 2001
From: Sacha <35510349+thebraveio@users.noreply.github.com>
Date: Thu, 21 Apr 2022 22:58:50 +0300
Subject: [PATCH 6/8] Update braveBidAdapter.js
From 128679424a7d08e289a756ab54b00ffc2f76e916 Mon Sep 17 00:00:00 2001
From: Sacha <35510349+thebraveio@users.noreply.github.com>
Date: Thu, 21 Apr 2022 23:32:53 +0300
Subject: [PATCH 7/8] Update videoheroesBidAdapter.md
---
modules/videoheroesBidAdapter.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/modules/videoheroesBidAdapter.md b/modules/videoheroesBidAdapter.md
index 2848f1462cd..f2a2ca9f7ba 100644
--- a/modules/videoheroesBidAdapter.md
+++ b/modules/videoheroesBidAdapter.md
@@ -24,7 +24,7 @@ var adUnits = [{
bids: [{
bidder: 'videoheroes',
params : {
- placementId : "to0QI2aPgkbBZq6vgf0oHitouZduz0qw" // test placementId, please replace after test
+ placementId : "1a8d9c22db19906cb8a5fd4518d05f62" // test placementId, please replace after test
}
}]
}];
@@ -65,7 +65,7 @@ var adUnits = [{
bids: [{
bidder: 'videoheroes',
params: {
- placementId : "to0QI2aPgkbBZq6vgf0oHitouZduz0qw" // test placementId, please replace after test
+ placementId : "1a8d9c22db19906cb8a5fd4518d05f62" // test placementId, please replace after test
}
}]
}];
@@ -99,7 +99,7 @@ var adUnits = [{
bids: [{
bidder: 'videoheroes',
params: {
- placementId : "to0QI2aPgkbBZq6vgf0oHitouZduz0qw" // test placementId, please replace after test
+ placementId : "1a8d9c22db19906cb8a5fd4518d05f62" // test placementId, please replace after test
}
}]
}];
@@ -110,7 +110,7 @@ var adUnits = [{
| Name | Scope | Type | Description | Example
| ---- | ----- | ---- | ----------- | -------
-| `placementId` | required | String | The placement ID from Video Heroes | "to0QI2aPgkbBZq6vgf0oHitouZduz0qw"
+| `placementId` | required | String | The placement ID from Video Heroes | "1a8d9c22db19906cb8a5fd4518d05f62"
# Ad Unit and page Setup:
From 584bf432e93eab5e115f11fb318b2921dbe65f37 Mon Sep 17 00:00:00 2001
From: Sacha <35510349+thebraveio@users.noreply.github.com>
Date: Sun, 15 May 2022 21:52:06 +0300
Subject: [PATCH 8/8] upd import of utils lib to load only certain fnc
---
modules/videoheroesBidAdapter.js | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/modules/videoheroesBidAdapter.js b/modules/videoheroesBidAdapter.js
index 769f354294e..edcdd698153 100644
--- a/modules/videoheroesBidAdapter.js
+++ b/modules/videoheroesBidAdapter.js
@@ -1,4 +1,4 @@
-import * as utils from '../src/utils.js';
+import { isEmpty, parseUrl, isStr, triggerPixel } from '../src/utils.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
import { config } from '../src/config.js';
@@ -90,7 +90,7 @@ export const spec = {
ua: navigator.userAgent,
},
site: {
- domain: utils.parseUrl(page).hostname,
+ domain: parseUrl(page).hostname,
page: page,
},
tmax: bidderRequest.timeout || config.getConfig('bidderTimeout') || 500,
@@ -134,7 +134,7 @@ export const spec = {
* @return {Bid[]} An array of bids which were nested inside the server.
*/
interpretResponse: (serverResponse) => {
- if (!serverResponse || utils.isEmpty(serverResponse.body)) return [];
+ if (!serverResponse || isEmpty(serverResponse.body)) return [];
let bids = [];
serverResponse.body.seatbid.forEach(response => {
@@ -173,8 +173,8 @@ export const spec = {
},
onBidWon: (bid) => {
- if (utils.isStr(bid.nurl) && bid.nurl !== '') {
- utils.triggerPixel(bid.nurl);
+ if (isStr(bid.nurl) && bid.nurl !== '') {
+ triggerPixel(bid.nurl);
}
}
};