diff --git a/modules/performaxBidAdapter.js b/modules/performaxBidAdapter.js new file mode 100644 index 00000000000..a765c4d9d78 --- /dev/null +++ b/modules/performaxBidAdapter.js @@ -0,0 +1,77 @@ +import { deepSetValue, deepAccess } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js' +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; + +const BIDDER_CODE = 'performax'; +const BIDDER_SHORT_CODE = 'px'; +const GVLID = 732 +const ENDPOINT = 'https://dale.performax.cz/ortb' +export const converter = ortbConverter({ + + imp(buildImp, bidRequest, context) { + const imp = buildImp(bidRequest, context); + deepSetValue(imp, 'tagid', bidRequest.params.tagid); + return imp; + }, + + bidResponse(buildBidResponse, bid, context) { + context.netRevenue = deepAccess(bid, 'netRevenue'); + context.mediaType = deepAccess(bid, 'mediaType'); + context.currency = deepAccess(bid, 'currency'); + + return buildBidResponse(bid, context) + }, + + context: { + ttl: 360, + } +}) + +export const spec = { + code: BIDDER_CODE, + aliases: [BIDDER_SHORT_CODE], + gvlid: GVLID, + supportedMediaTypes: [BANNER], + + isBidRequestValid: function (bid) { + return !!bid.params.tagid; + }, + + buildRequests: function (bidRequests, bidderRequest) { + let data = converter.toORTB({bidderRequest, bidRequests}) + return [{ + method: 'POST', + url: ENDPOINT, + options: {'contentType': 'application/json'}, + data: data + }] + }, + + interpretResponse: function (bidderResponse, request) { + if (!bidderResponse.body) return []; + const response = bidderResponse.body + const data = { + + seatbid: response.seatbid.map(seatbid => ({ + seat: seatbid.seat, + bid: seatbid.bid.map(bid => ({ + impid: bid.imp_id, + w: bid.w, + h: bid.h, + requestId: request.data.id, + price: bid.price, + currency: response.cur, + adm: bid.adm, + crid: bid.id, + netRevenue: true, + mediaType: BANNER, + })) + })) + }; + return converter.fromORTB({ response: data, request: request.data }).bids + }, + +} + +registerBidder(spec); diff --git a/modules/performaxBidAdapter.md b/modules/performaxBidAdapter.md new file mode 100644 index 00000000000..8b5b702a8e6 --- /dev/null +++ b/modules/performaxBidAdapter.md @@ -0,0 +1,36 @@ +# Overview + +``` +Module Name: Performax Bid Adapter +Module Type: Bidder Adapter +Maintainer: development@performax.cz +``` + +# Description + +Connects to Performax exchange for bids. + +Performax bid adapter supports Banner. + + +# Sample Banner Ad Unit: For Publishers + +```javascript + var adUnits = [ + { + code: 'performax-div', + mediaTypes: { + banner: {sizes: [[300, 300]]}, + }, + bids: [ + { + bidder: "performax", + params: { + tagid: "sample" // required + } + } + ] + }, + ]; +``` + diff --git a/test/spec/modules/performaxBidAdapter_spec.js b/test/spec/modules/performaxBidAdapter_spec.js new file mode 100644 index 00000000000..49a6a83e29d --- /dev/null +++ b/test/spec/modules/performaxBidAdapter_spec.js @@ -0,0 +1,175 @@ +import { expect } from 'chai'; +import { spec, converter } from 'modules/performaxBidAdapter.js'; + +describe('Performax adapter', function () { + let bids = [{ + bidder: 'performax', + params: { + tagid: 'sample' + }, + ortb2Imp: { + ext: {} + }, + mediaTypes: { + banner: { + sizes: [ + [300, 300], + ]}}, + adUnitCode: 'postbid_iframe', + transactionId: '84deda92-e9ba-4b0d-a797-43be5e522430', + adUnitId: '4ee4643b-931f-4a17-a571-ccba57886dc8', + sizes: [ + [300, 300], + ], + bidId: '2bc545c347dbbe', + bidderRequestId: '1534dec005b9a', + auctionId: 'acd97e55-01e1-45ad-813c-67fa27fc5c1b', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0, + ortb2: { + source: {}, + site: {}, + device: {} + }, + }, + + { + bidder: 'performax', + params: { + tagid: '1545' + }, + ortb2Imp: { + ext: {} + }, + mediaTypes: { + banner: { + sizes: [ + [300, 600], + ]}}, + adUnitCode: 'postbid_halfpage_iframe', + transactionId: '84deda92-e9ba-4b0d-a797-43be5e522430', + adUnitId: '4ee4643b-931f-4a17-a571-ccba57886dc8', + sizes: [ + [300, 600], + ], + bidId: '3dd53d30c691fe', + bidderRequestId: '1534dec005b9a', + auctionId: 'acd97e55-01e1-45ad-813c-67fa27fc5c1b', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0, + ortb2: { + source: {}, + site: {}, + device: {} + }}]; + + let bidderRequest = { + bidderCode: 'performax2', + auctionId: 'acd97e55-01e1-45ad-813c-67fa27fc5c1b', + id: 'acd97e55-01e1-45ad-813c-67fa27fc5c1b', + bidderRequestId: '1534dec005b9a', + bids: bids, + ortb2: { + regs: { + ext: { + gdpr: 1 + }}, + user: { + ext: { + consent: 'consent-string' + } + }, + site: {}, + device: {} + }}; + + let serverResponse = { + body: { + cur: 'CZK', + seatbid: [ + { + seat: 'performax', + bid: [ + { + id: 'sample', + price: 20, + w: 300, + h: 300, + adm: 'My ad' + } + ]}]}, + } + + describe('isBidRequestValid', function () { + let bid = {}; + it('should return false when missing "tagid" param', function() { + bid.params = {slotId: 'param'}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return true when tagid is correct', function() { + bid.params = {tagid: 'sample'}; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + }) + + describe('buildRequests', function () { + it('should set correct request method and url', function () { + let requests = spec.buildRequests([bids[0]], bidderRequest); + expect(requests).to.be.an('array').that.has.lengthOf(1); + let request = requests[0]; + expect(request.method).to.equal('POST'); + expect(request.url).to.equal('https://dale.performax.cz/ortb'); + expect(request.data).to.be.an('object'); + }); + + it('should pass correct imp', function () { + let requests = spec.buildRequests([bids[0]], bidderRequest); + let {data} = requests[0]; + let {imp} = data; + expect(imp).to.be.an('array').that.has.lengthOf(1); + expect(imp[0]).to.be.an('object'); + let bid = imp[0]; + expect(bid.id).to.equal('2bc545c347dbbe'); + expect(bid.banner).to.deep.equal({topframe: 0, format: [{w: 300, h: 300}]}); + }); + + it('should process multiple bids', function () { + let requests = spec.buildRequests(bids, bidderRequest); + expect(requests).to.be.an('array').that.has.lengthOf(1); + let {data} = requests[0]; + let {imp} = data; + expect(imp).to.be.an('array').that.has.lengthOf(bids.length); + let bid1 = imp[0]; + expect(bid1.banner).to.deep.equal({topframe: 0, format: [{w: 300, h: 300}]}); + let bid2 = imp[1]; + expect(bid2.banner).to.deep.equal({topframe: 0, format: [{w: 300, h: 600}]}); + }); + }); + + describe('interpretResponse', function () { + it('should map params correctly', function () { + let ortbRequest = {data: converter.toORTB({bidderRequest, bids})}; + serverResponse.body.id = ortbRequest.data.id; + serverResponse.body.seatbid[0].bid[0].imp_id = ortbRequest.data.imp[0].id; + + let result = spec.interpretResponse(serverResponse, ortbRequest); + expect(result).to.be.an('array').that.has.lengthOf(1); + let bid = result[0]; + + expect(bid.cpm).to.equal(20); + expect(bid.ad).to.equal('My ad'); + expect(bid.currency).to.equal('CZK'); + expect(bid.mediaType).to.equal('banner'); + expect(bid.netRevenue).to.equal(true); + expect(bid.ttl).to.equal(360); + expect(bid.creativeId).to.equal('sample'); + }); + }); +});