diff --git a/modules/responsiveAdsBidAdapter.js b/modules/responsiveAdsBidAdapter.js new file mode 100644 index 00000000000..a33a52f5644 --- /dev/null +++ b/modules/responsiveAdsBidAdapter.js @@ -0,0 +1,80 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js' +import { + logMessage, + isSafeFrameWindow, + mergeDeep, + canAccessWindowTop, +} from '../src/utils.js'; + +const BIDDER_VERSION = '1.0'; +const BIDDER_CODE = 'responsiveads'; +const ENDPOINT_URL = 'https://ve60c4xzl9.execute-api.us-east-1.amazonaws.com/prod/prebidjs'; +const DEFAULT_CURRENCY = 'USD'; +const GVLID = 1189; + +const converter = ortbConverter({ + context: { + mediaType: BANNER, + netRevenue: true, + ttl: 300, + currency: DEFAULT_CURRENCY, + }, + request(buildRequest, imps, bidderRequest, context) { + const req = buildRequest(imps, bidderRequest, context); + // add additional information we might need on the backend + mergeDeep(req, { + ext: { + prebid: { + adapterVersion: `${BIDDER_VERSION}`, + }, + }, + }); + return req; + }, +}); + +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + supportedMediaTypes: [BANNER], + isBidRequestValid: function(bid) { + // validate the bid request + return !!(bid.params); + }, + buildRequests: function(bidRequests, bidderRequest) { + // we only want to bid if we are not in a safeframe + if (isSafeFrameWindow()) { + return null; + } + + // if we can't access top we don't want to bid + if (!canAccessWindowTop()) { + return null; + } + const data = converter.toORTB({ bidRequests, bidderRequest }); + return { + method: 'POST', + url: ENDPOINT_URL, + data: data, + options: { + contentType: 'application/json', + withCredentials: false + }, + bidderRequest + }; + }, + interpretResponse: function(response, request) { + const res = converter.fromORTB({ response: response.body, request: request.data }); + const bids = res.bids; + return bids; + }, + + onBidWon: (bid) => { + logMessage('onBidWon', bid); + } + +}; + +registerBidder(spec); diff --git a/modules/responsiveAdsBidAdapter.md b/modules/responsiveAdsBidAdapter.md new file mode 100644 index 00000000000..af3b59e80e3 --- /dev/null +++ b/modules/responsiveAdsBidAdapter.md @@ -0,0 +1,38 @@ +# Overview + +```markdown +Module Name: responsiveAdsBidAdapter +Module Type: Bidder Adapter +Maintainer: support@responsiveads.com +``` + + +# Description +Module that connects to ResponsiveAds Programmatic Fluid demand. + + +## Running the code +To view an example of the on page setup required: + +```bash +gulp serve-and-test --file test/spec/modules/responsiveAdsBidAdapter_spec.js +``` + +# Test Parameters +``` +var adUnits = [{ + code: 'div-gpt-ad-1460505748561-0', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + + // Replace this object to test a new Adapter! + bids: [{ + bidder: 'responsiveads', + params: {} + }] + +}]; +``` diff --git a/test/spec/modules/responsiveAdsBidAdapter_spec.js b/test/spec/modules/responsiveAdsBidAdapter_spec.js new file mode 100644 index 00000000000..83202ad0916 --- /dev/null +++ b/test/spec/modules/responsiveAdsBidAdapter_spec.js @@ -0,0 +1,145 @@ +import { expect } from 'chai'; +import { spec } from 'modules/responsiveAdsBidAdapter.js'; +import * as utils from 'src/utils.js'; + +describe('responsiveAdsBidAdapter', function() { + let bidRequests; + let bidderRequest; + let sandbox; + + beforeEach(function () { + sandbox = sinon.sandbox.create(); + sandbox.stub(utils, 'isSafeFrameWindow').returns(false); + sandbox.stub(utils, 'canAccessWindowTop').returns(true); + bidRequests = [{ + bidder: 'responsiveads', + params: { + placementId: '1', + }, + adUnitCode: '/3434399/header-bid-tag-1', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]], + } + }, + bidId: '123', + auctionId: '456', + bidderRequestId: '789', + transactionId: '123' + }]; + + bidderRequest = { + timeout: 3000, + } + }); + + afterEach(function () { + sandbox.restore(); + }); + + describe('Check if bid is valid', function() { + it('Should accept valid bid', function() { + const validBid = { + bidder: 'responsiveads', + params: {}, + }; + + const isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(true); + }); + + it('Should not reject bid if missing placementId', function() { + const validBid = { + bidder: 'responsiveads', + params: {} + }; + + const isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(true); + }); + }); + + describe('Build requests', function () { + it('Should not bit on safeframe', function() { + utils.isSafeFrameWindow.restore(); + sandbox.stub(utils, 'isSafeFrameWindow').returns(true); + + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests).to.be.null; + }); + + it('Should not bit if cant access window top', function () { + utils.canAccessWindowTop.restore(); + sandbox.stub(utils, 'canAccessWindowTop').returns(false); + + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests).to.be.null; + }); + + it('Should use POST and have URL', function() { + const request = spec.buildRequests(bidRequests, bidderRequest); + + expect(request.method).to.exist; + expect(request.method).to.equal('POST'); + expect(request.url).to.exist; + }); + + it('Should add adapter version', function() { + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.ext.prebid.adapterVersion).to.exist; + }); + }); + + describe('Handling responses', function() { + it('Should return complete bid response', function() { + const serverResponse = { + body: { + id: 'response-id', + cur: 'USD', + seatbid: [ + { + bid: [ + { + id: '123', + impid: '123', + price: 0.5, + adm: ``, + nurl: 'https://example.com/win', + crid: '662d13e12e0c567af92d0918', + w: 300, + h: 250, + mediaType: 'banner', + adomain: ['responsiveads.com'], + attr: [1], + cat: ['IAB1'] + } + ] + } + ] + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const bids = spec.interpretResponse(serverResponse, request); + + expect(bids).to.be.lengthOf(1); + expect(bids[0].requestId).to.equal('123'); + expect(bids[0].cpm).to.equal(0.5); + expect(bids[0].width).to.equal(300); + expect(bids[0].height).to.equal(250); + expect(bids[0].ad).to.have.length.above(1); + expect(bids[0].meta.advertiserDomains).to.deep.equal(['responsiveads.com']); + }); + + it('should return empty bid response', function () { + const emptyServerResponse = { + body: [] + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const bids = spec.interpretResponse(emptyServerResponse, request); + + expect(bids).to.be.lengthOf(0); + }); + }); +});