From 2fd51d0be9600bda98af35927c1286baa668d24c Mon Sep 17 00:00:00 2001 From: "s.shevtsov" Date: Mon, 18 Nov 2019 11:19:38 +0300 Subject: [PATCH 1/4] Added AstraOne adapter --- modules/astraoneBidAdapter.js | 136 ++++++++++++++++++++++++++++++++++ modules/astraoneBidAdapter.md | 91 +++++++++++++++++++++++ 2 files changed, 227 insertions(+) create mode 100644 modules/astraoneBidAdapter.js create mode 100644 modules/astraoneBidAdapter.md diff --git a/modules/astraoneBidAdapter.js b/modules/astraoneBidAdapter.js new file mode 100644 index 00000000000..be41ee3e17b --- /dev/null +++ b/modules/astraoneBidAdapter.js @@ -0,0 +1,136 @@ +import * as utils from '../src/utils' +import { registerBidder } from '../src/adapters/bidderFactory' +import { BANNER } from '../src/mediaTypes' +import {Renderer} from '../src/Renderer'; + +const BIDDER_CODE = 'astraone'; +const SSP_ENDPOINT = 'https://ssp.astraone.io/auction/prebid'; +const TTL = 60; + +function buildBidRequests(validBidRequests) { + return utils._map(validBidRequests, function(validBidRequest) { + const params = validBidRequest.params; + const bidRequest = { + bidId: validBidRequest.bidId, + transactionId: validBidRequest.transactionId, + sizes: validBidRequest.sizes, + adUnitId: params.adUnitId, + placement: params.placement, + placeId: params.placeId, + imageUrl: params.imageUrl + }; + + return bidRequest; + }) +} + +function inImageRender(bid) { + bid.renderer.push(() => { + window._ao_ssp.registerInImage(bid); + }); +} + +function createRenderer() { + const renderer = Renderer.install({ + url: 'https://st.astraone.io/prebidrenderer.js', + loaded: false + }); + + try { + renderer.setRender(inImageRender); + } catch (err) { + utils.logWarn('Prebid Error calling setRender on renderer', err); + } + + return renderer; +} + +function buildBid(bidDada) { + const bid = { + requestId: bidDada.bidId, + cpm: bidDada.price, + width: bidDada.width, + height: bidDada.height, + creativeId: bidDada.content.seanceId, + currency: bidDada.currency, + netRevenue: true, + mediaType: BANNER, + ttl: TTL, + ad: bidDada.content.content, + content: bidDada.content + }; + + Object.assign(bid, { + renderer: createRenderer(bid) + }); + + return bid; +} + +function getMediaTypeFromBid(bid) { + return bid.mediaTypes && Object.keys(bid.mediaTypes)[0] +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid(bid) { + return getMediaTypeFromBid(bid) === BANNER; + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ + buildRequests(validBidRequests, bidderRequest) { + const payload = { + url: bidderRequest.refererInfo.referer, + cmp: !!bidderRequest.gdprConsent, + bidRequests: buildBidRequests(validBidRequests) + }; + + if (payload.cmp) { + const gdprApplies = bidderRequest.gdprConsent.gdprApplies; + if (gdprApplies !== undefined) payload['ga'] = gdprApplies; + payload['cs'] = bidderRequest.gdprConsent.consentString; + } + + const payloadString = JSON.stringify(payload); + return { + method: 'POST', + url: SSP_ENDPOINT, + data: payloadString, + options: { + contentType: 'application/json' + } + } + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function(serverResponse) { + const serverBody = serverResponse.body; + if (serverBody && utils.isArray(serverBody)) { + return utils._map(serverBody, function(bid) { + return buildBid(bid); + }); + } else { + return []; + } + } + +} +registerBidder(spec); diff --git a/modules/astraoneBidAdapter.md b/modules/astraoneBidAdapter.md new file mode 100644 index 00000000000..9a93e6e1ea3 --- /dev/null +++ b/modules/astraoneBidAdapter.md @@ -0,0 +1,91 @@ +# Overview + + +**Module Name**: AstraOne Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: prebid@astraone.io + +# Description + +You can use this adapter to get a bid from AstraOne. +Please reach out to your AstraOne account team before using this plugin to get placeId. +The code below returns a demo ad. + +About us: https://astraone.io + +# Test Parameters +```js +var adUnits = [{ + code: 'test-div', + mediaTypes: { + banner: { + sizes: [], + } + }, + bids: [{ + bidder: "astraone", + params: { + placement: "inImage", + placeId: "5af45ad34d506ee7acad0c26", + imageUrl: "https://creative.astraone.io/files/default_image-1-600x400.jpg" + } + }] +}]; +``` + +# Example page + +```html + + + + + Prebid.js Banner Example + + + + + +

Prebid.js InImage Banner Test

+ +
+ +
+ + + +``` + From eb90a6c15da176b06efe007784459257cb268ad2 Mon Sep 17 00:00:00 2001 From: "s.shevtsov" Date: Wed, 20 Nov 2019 09:29:00 +0300 Subject: [PATCH 2/4] Fixed an argument in function createRenderer --- modules/astraoneBidAdapter.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/modules/astraoneBidAdapter.js b/modules/astraoneBidAdapter.js index be41ee3e17b..d05a05ec19f 100644 --- a/modules/astraoneBidAdapter.js +++ b/modules/astraoneBidAdapter.js @@ -30,8 +30,9 @@ function inImageRender(bid) { }); } -function createRenderer() { +function createRenderer(bid) { const renderer = Renderer.install({ + id: bid.requestId, url: 'https://st.astraone.io/prebidrenderer.js', loaded: false }); @@ -60,9 +61,7 @@ function buildBid(bidDada) { content: bidDada.content }; - Object.assign(bid, { - renderer: createRenderer(bid) - }); + bid.renderer = createRenderer(bid); return bid; } From 7b23cef81c8151bc418aefab462bf1cb42fe0912 Mon Sep 17 00:00:00 2001 From: "s.shevtsov" Date: Tue, 3 Dec 2019 17:24:26 +0300 Subject: [PATCH 3/4] Added unit tests. Added example with GPT in the documentation. Removed bid renderer. --- modules/astraoneBidAdapter.js | 59 +++--- modules/astraoneBidAdapter.md | 151 +++++++++++-- test/spec/modules/astraoneBidAdapter_spec.js | 210 +++++++++++++++++++ 3 files changed, 371 insertions(+), 49 deletions(-) create mode 100644 test/spec/modules/astraoneBidAdapter_spec.js diff --git a/modules/astraoneBidAdapter.js b/modules/astraoneBidAdapter.js index d05a05ec19f..d6ea7c5859c 100644 --- a/modules/astraoneBidAdapter.js +++ b/modules/astraoneBidAdapter.js @@ -1,7 +1,6 @@ import * as utils from '../src/utils' import { registerBidder } from '../src/adapters/bidderFactory' import { BANNER } from '../src/mediaTypes' -import {Renderer} from '../src/Renderer'; const BIDDER_CODE = 'astraone'; const SSP_ENDPOINT = 'https://ssp.astraone.io/auction/prebid'; @@ -14,7 +13,6 @@ function buildBidRequests(validBidRequests) { bidId: validBidRequest.bidId, transactionId: validBidRequest.transactionId, sizes: validBidRequest.sizes, - adUnitId: params.adUnitId, placement: params.placement, placeId: params.placeId, imageUrl: params.imageUrl @@ -24,28 +22,6 @@ function buildBidRequests(validBidRequests) { }) } -function inImageRender(bid) { - bid.renderer.push(() => { - window._ao_ssp.registerInImage(bid); - }); -} - -function createRenderer(bid) { - const renderer = Renderer.install({ - id: bid.requestId, - url: 'https://st.astraone.io/prebidrenderer.js', - loaded: false - }); - - try { - renderer.setRender(inImageRender); - } catch (err) { - utils.logWarn('Prebid Error calling setRender on renderer', err); - } - - return renderer; -} - function buildBid(bidDada) { const bid = { requestId: bidDada.bidId, @@ -57,11 +33,10 @@ function buildBid(bidDada) { netRevenue: true, mediaType: BANNER, ttl: TTL, - ad: bidDada.content.content, content: bidDada.content }; - bid.renderer = createRenderer(bid); + bid.ad = wrapAd(bid, bidDada); return bid; } @@ -70,6 +45,30 @@ function getMediaTypeFromBid(bid) { return bid.mediaTypes && Object.keys(bid.mediaTypes)[0] } +function wrapAd(bid, bidDada) { + return ` + + + + + + + + +
+ + + `; +} + export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER], @@ -81,7 +80,13 @@ export const spec = { * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid(bid) { - return getMediaTypeFromBid(bid) === BANNER; + return ( + getMediaTypeFromBid(bid) === BANNER && + !!bid.params.placeId && + !!bid.params.imageUrl && + !!bid.params.placement && + (bid.params.placement === 'inImage') + ); }, /** diff --git a/modules/astraoneBidAdapter.md b/modules/astraoneBidAdapter.md index 9a93e6e1ea3..0c7b79489a5 100644 --- a/modules/astraoneBidAdapter.md +++ b/modules/astraoneBidAdapter.md @@ -42,10 +42,104 @@ var adUnits = [{ Prebid.js Banner Example - + + + +

Prebid.js InImage Banner Test

+ +
+ + +
+ + + +``` +# Example page with GPT + +```html + + + + + Prebid.js Banner Example + + + + - -

Prebid.js InImage Banner Test

+

Prebid.js Banner Ad Unit Test

-
+
+ +
- ``` - diff --git a/test/spec/modules/astraoneBidAdapter_spec.js b/test/spec/modules/astraoneBidAdapter_spec.js new file mode 100644 index 00000000000..cd80e742b7d --- /dev/null +++ b/test/spec/modules/astraoneBidAdapter_spec.js @@ -0,0 +1,210 @@ +import { expect } from 'chai' +import { spec } from 'modules/astraoneBidAdapter' + +function getSlotConfigs(mediaTypes, params) { + return { + params: params, + sizes: [], + bidId: '2df8c0733f284e', + bidder: 'astraone', + mediaTypes: mediaTypes, + transactionId: '31a58515-3634-4e90-9c96-f86196db1459' + } +} + +describe('AstraOne Adapter', function() { + describe('isBidRequestValid method', function() { + const PLACE_ID = '5af45ad34d506ee7acad0c26'; + const IMAGE_URL = 'https://creative.astraone.io/files/default_image-1-600x400.jpg'; + + describe('returns true', function() { + describe('when banner slot config has all mandatory params', () => { + describe('and placement has the correct value', function() { + const createBannerSlotConfig = placement => { + return getSlotConfigs( + { banner: {} }, + { + placeId: PLACE_ID, + imageUrl: IMAGE_URL, + placement + } + ) + } + const placements = ['inImage']; + placements.forEach(placement => { + it('should be ' + placement, function() { + const isBidRequestValid = spec.isBidRequestValid( + createBannerSlotConfig(placement) + ) + expect(isBidRequestValid).to.equal(true) + }) + }) + }) + }) + }) + describe('returns false', function() { + describe('when params are not correct', function() { + function createSlotconfig(params) { + return getSlotConfigs({ banner: {} }, params) + } + it('does not have the placeId.', function() { + const isBidRequestValid = spec.isBidRequestValid( + createSlotconfig({ + imageUrl: IMAGE_URL, + placement: 'inImage' + }) + ) + expect(isBidRequestValid).to.equal(false) + }) + it('does not have the imageUrl.', function() { + const isBidRequestValid = spec.isBidRequestValid( + createSlotconfig({ + placeId: PLACE_ID, + placement: 'inImage' + }) + ) + expect(isBidRequestValid).to.equal(false) + }) + it('does not have the placement.', function() { + const isBidRequestValid = spec.isBidRequestValid( + createSlotconfig({ + placeId: PLACE_ID, + imageUrl: IMAGE_URL, + }) + ) + expect(isBidRequestValid).to.equal(false) + }) + it('does not have a the correct placement.', function() { + const isBidRequestValid = spec.isBidRequestValid( + createSlotconfig({ + placeId: PLACE_ID, + imageUrl: IMAGE_URL, + placement: 'something' + }) + ) + expect(isBidRequestValid).to.equal(false) + }) + }) + }) + }) + + describe('buildRequests method', function() { + const bidderRequest = { + refererInfo: { referer: 'referer' } + } + const mandatoryParams = { + placeId: '5af45ad34d506ee7acad0c26', + imageUrl: 'https://creative.astraone.io/files/default_image-1-600x400.jpg', + placement: 'inImage' + } + const validBidRequests = [ + getSlotConfigs({ banner: {} }, mandatoryParams) + ] + it('Url params should be correct ', function() { + const request = spec.buildRequests(validBidRequests, bidderRequest) + expect(request.method).to.equal('POST') + expect(request.url).to.equal('https://ssp.astraone.io/auction/prebid') + }) + + it('Common data request should be correct', function() { + const request = spec.buildRequests(validBidRequests, bidderRequest) + const data = JSON.parse(request.data) + expect(Array.isArray(data.bidRequests)).to.equal(true) + data.bidRequests.forEach(bid => { + expect(bid.placeId).to.equal('5af45ad34d506ee7acad0c26') + expect(typeof bid.imageUrl).to.equal('string') + }) + }) + + describe('GDPR params', function() { + describe('when there are not consent management platform', function() { + it('cmp should be false', function() { + const request = spec.buildRequests(validBidRequests, bidderRequest) + const data = JSON.parse(request.data) + expect(data.cmp).to.equal(false) + }) + }) + describe('when there are consent management platform', function() { + it('cmps should be true and ga should not sended, when gdprApplies is undefined', function() { + bidderRequest['gdprConsent'] = { + gdprApplies: undefined, + consentString: 'consentString' + } + const request = spec.buildRequests(validBidRequests, bidderRequest) + const data = JSON.parse(request.data) + expect(data.cmp).to.equal(true) + expect(Object.keys(data).indexOf('data')).to.equal(-1) + expect(data.cs).to.equal('consentString') + }) + it('cmps should be true and all gdpr parameters should be sended, when there are gdprApplies', function() { + bidderRequest['gdprConsent'] = { + gdprApplies: true, + consentString: 'consentString' + } + const request = spec.buildRequests(validBidRequests, bidderRequest) + const data = JSON.parse(request.data) + expect(data.cmp).to.equal(true) + expect(data.ga).to.equal(true) + expect(data.cs).to.equal('consentString') + }) + }) + }) + + describe('BidRequests params', function() { + const request = spec.buildRequests(validBidRequests, bidderRequest) + const data = JSON.parse(request.data) + const bidRequests = data.bidRequests + it('should request a Banner', function() { + const bannerBid = bidRequests[0] + expect(bannerBid.bidId).to.equal('2df8c0733f284e') + expect(bannerBid.transactionId).to.equal('31a58515-3634-4e90-9c96-f86196db1459') + expect(bannerBid.placeId).to.equal('5af45ad34d506ee7acad0c26') + }) + }) + }) + + describe('interpret response method', function() { + it('should return a void array, when the server response have not got bids.', function() { + const serverResponse = { + body: [] + } + const bids = spec.interpretResponse(serverResponse) + expect(Array.isArray(bids)).to.equal(true) + expect(bids.length).to.equal(0) + }) + describe('when the server response return a bid', function() { + describe('the bid is a banner', function() { + it('should return a banner bid', function() { + const serverResponse = { + body: [ + { + bidId: '2df8c0733f284e', + price: 0.5, + currency: 'USD', + content: { + content: 'html', + actionUrls: {}, + seanceId: '123123' + }, + width: 100, + height: 100, + ttl: 360 + } + ] + } + const bids = spec.interpretResponse(serverResponse) + expect(bids.length).to.equal(1) + expect(bids[0].requestId).to.equal('2df8c0733f284e') + expect(bids[0].creativeId).to.equal('123123') + expect(bids[0].cpm).to.equal(0.5) + expect(bids[0].width).to.equal(100) + expect(bids[0].height).to.equal(100) + expect(bids[0].currency).to.equal('USD') + expect(bids[0].netRevenue).to.equal(true) + expect(typeof bids[0].ad).to.equal('string') + expect(typeof bids[0].content).to.equal('object') + }) + }) + }) + }) +}) From 417d2dd1fe943220d000390da809bfe9ce057cee Mon Sep 17 00:00:00 2001 From: "s.shevtsov" Date: Wed, 4 Dec 2019 17:38:45 +0300 Subject: [PATCH 4/4] Fixed a small typo --- modules/astraoneBidAdapter.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/astraoneBidAdapter.js b/modules/astraoneBidAdapter.js index d6ea7c5859c..efff699b0dc 100644 --- a/modules/astraoneBidAdapter.js +++ b/modules/astraoneBidAdapter.js @@ -22,21 +22,21 @@ function buildBidRequests(validBidRequests) { }) } -function buildBid(bidDada) { +function buildBid(bidData) { const bid = { - requestId: bidDada.bidId, - cpm: bidDada.price, - width: bidDada.width, - height: bidDada.height, - creativeId: bidDada.content.seanceId, - currency: bidDada.currency, + requestId: bidData.bidId, + cpm: bidData.price, + width: bidData.width, + height: bidData.height, + creativeId: bidData.content.seanceId, + currency: bidData.currency, netRevenue: true, mediaType: BANNER, ttl: TTL, - content: bidDada.content + content: bidData.content }; - bid.ad = wrapAd(bid, bidDada); + bid.ad = wrapAd(bid, bidData); return bid; } @@ -45,7 +45,7 @@ function getMediaTypeFromBid(bid) { return bid.mediaTypes && Object.keys(bid.mediaTypes)[0] } -function wrapAd(bid, bidDada) { +function wrapAd(bid, bidData) { return ` @@ -55,7 +55,7 @@ function wrapAd(bid, bidDada) { -
+