From 0606e77e0baa9cb75e819412154c0a40fff6abc3 Mon Sep 17 00:00:00 2001 From: Solta Date: Tue, 20 Feb 2024 19:02:24 +0300 Subject: [PATCH] Kimberlite Bidder Adapter: initial commit (#11032) * Kimberlite bid adapter (#1) * initial: bid adapter * styling * Fix: lint (#2) * Fix: lint (#4) * review fixes (#6) * Change: filling request.ext.prebid section (#7) --------- Co-authored-by: Oleg --- modules/kimberliteBidAdapter.js | 71 ++++++++ modules/kimberliteBidAdapter.md | 36 ++++ .../spec/modules/kimberliteBidAdapter_spec.js | 171 ++++++++++++++++++ 3 files changed, 278 insertions(+) create mode 100644 modules/kimberliteBidAdapter.js create mode 100644 modules/kimberliteBidAdapter.md create mode 100644 test/spec/modules/kimberliteBidAdapter_spec.js diff --git a/modules/kimberliteBidAdapter.js b/modules/kimberliteBidAdapter.js new file mode 100644 index 00000000000..72df921e18f --- /dev/null +++ b/modules/kimberliteBidAdapter.js @@ -0,0 +1,71 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js' +import { deepSetValue } from '../src/utils.js'; + +const VERSION = '1.0.0'; + +const BIDDER_CODE = 'kimberlite'; +const METHOD = 'POST'; +const ENDPOINT_URL = 'https://kimberlite.io/rtb/bid/pbjs'; + +const VERSION_INFO = { + ver: '$prebid.version$', + adapterVer: `${VERSION}` +}; + +const converter = ortbConverter({ + context: { + mediaType: BANNER, + netRevenue: true, + ttl: 300 + }, + + request(buildRequest, imps, bidderRequest, context) { + const bidRequest = buildRequest(imps, bidderRequest, context); + deepSetValue(bidRequest, 'site.publisher.domain', bidderRequest.refererInfo.domain); + deepSetValue(bidRequest, 'site.page', bidderRequest.refererInfo.page); + deepSetValue(bidRequest, 'ext.prebid.ver', VERSION_INFO.ver); + deepSetValue(bidRequest, 'ext.prebid.adapterVer', VERSION_INFO.adapterVer); + bidRequest.at = 1; + return bidRequest; + }, + + imp (buildImp, bidRequest, context) { + const imp = buildImp(bidRequest, context); + imp.tagid = bidRequest.params.placementId; + return imp; + } +}); + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + + isBidRequestValid: (bidRequest = {}) => { + const { params, mediaTypes } = bidRequest; + let isValid = Boolean(params && params.placementId); + if (mediaTypes && mediaTypes[BANNER]) { + isValid = isValid && Boolean(mediaTypes[BANNER].sizes); + } else { + isValid = false; + } + + return isValid; + }, + + buildRequests: function (bidRequests, bidderRequest) { + return { + method: METHOD, + url: ENDPOINT_URL, + data: converter.toORTB({ bidderRequest, bidRequests }) + } + }, + + interpretResponse(serverResponse, bidRequest) { + const bids = converter.fromORTB({response: serverResponse.body, request: bidRequest.data}).bids; + return bids; + } +}; + +registerBidder(spec); diff --git a/modules/kimberliteBidAdapter.md b/modules/kimberliteBidAdapter.md new file mode 100644 index 00000000000..c165f1073aa --- /dev/null +++ b/modules/kimberliteBidAdapter.md @@ -0,0 +1,36 @@ +# Overview + +```markdown +Module Name: Kimberlite Bid Adapter +Module Type: Bidder Adapter +Maintainer: dev@solta.io +``` + +# Description + +Kimberlite exchange adapter. + +# Test Parameters + +## Banner AdUnit + +```javascript +var adUnits = [ + { + code: 'test-div', + mediaTypes: { + banner: { + sizes: [[320, 250], [640, 480]], + } + }, + bids: [ + { + bidder: "kimberlite", + params: { + placementId: 'testBanner' + } + } + ] + } +] +``` diff --git a/test/spec/modules/kimberliteBidAdapter_spec.js b/test/spec/modules/kimberliteBidAdapter_spec.js new file mode 100644 index 00000000000..1480f1cc768 --- /dev/null +++ b/test/spec/modules/kimberliteBidAdapter_spec.js @@ -0,0 +1,171 @@ +import { spec } from 'modules/kimberliteBidAdapter.js'; +import { assert } from 'chai'; +import { BANNER } from '../../../src/mediaTypes.js'; + +const BIDDER_CODE = 'kimberlite'; + +describe('kimberliteBidAdapter', function () { + const sizes = [[640, 480]]; + + describe('isBidRequestValid', function () { + let bidRequest; + + beforeEach(function () { + bidRequest = { + mediaTypes: { + [BANNER]: { + sizes: [[320, 240]] + } + }, + params: { + placementId: 'test-placement' + } + }; + }); + + it('pass on valid bidRequest', function () { + assert.isTrue(spec.isBidRequestValid(bidRequest)); + }); + + it('fails on missed placementId', function () { + delete bidRequest.params.placementId; + assert.isFalse(spec.isBidRequestValid(bidRequest)); + }); + + it('fails on empty banner', function () { + delete bidRequest.mediaTypes.banner; + assert.isFalse(spec.isBidRequestValid(bidRequest)); + }); + + it('fails on empty banner.sizes', function () { + delete bidRequest.mediaTypes.banner.sizes; + assert.isFalse(spec.isBidRequestValid(bidRequest)); + }); + + it('fails on empty request', function () { + assert.isFalse(spec.isBidRequestValid()); + }); + }); + + describe('buildRequests', function () { + let bidRequests, bidderRequest; + + beforeEach(function () { + bidRequests = [{ + mediaTypes: { + [BANNER]: {sizes: sizes} + }, + params: { + placementId: 'test-placement' + } + }]; + + bidderRequest = { + refererInfo: { + domain: 'example.com', + page: 'https://www.example.com/test.html', + }, + bids: [{ + mediaTypes: { + [BANNER]: {sizes: sizes} + } + }] + }; + }); + + it('valid bid request', function () { + const bidRequest = spec.buildRequests(bidRequests, bidderRequest); + assert.equal(bidRequest.method, 'POST'); + assert.ok(bidRequest.data); + + const requestData = bidRequest.data; + expect(requestData.site.page).to.equal(bidderRequest.refererInfo.page); + expect(requestData.site.publisher.domain).to.equal(bidderRequest.refererInfo.domain); + + expect(requestData.imp).to.be.an('array').and.is.not.empty; + + expect(requestData.ext).to.be.an('Object').and.have.all.keys('prebid'); + expect(requestData.ext.prebid).to.be.an('Object').and.have.all.keys('ver', 'adapterVer'); + + const impData = requestData.imp[0]; + expect(impData.banner).is.to.be.an('Object').and.have.all.keys(['format', 'topframe']); + + const bannerData = impData.banner; + expect(bannerData.format).to.be.an('array').and.is.not.empty; + + const formatData = bannerData.format[0]; + expect(formatData).to.be.an('Object').and.have.all.keys('w', 'h'); + + assert.equal(formatData.w, sizes[0][0]); + assert.equal(formatData.h, sizes[0][1]); + }); + }); + + describe('interpretResponse', function () { + let bidderResponse, bidderRequest, bidRequest, expectedBid; + + const requestId = '07fba8b0-8812-4dc6-b91e-4a525d81729c'; + const bidId = '222209853178'; + const impId = 'imp-id'; + const crId = 'creative-id'; + const adm = 'landing'; + + beforeEach(function () { + bidderResponse = { + body: { + id: requestId, + seatbid: [{ + bid: [{ + crid: crId, + id: bidId, + impid: impId, + price: 1, + adm: adm + }] + }] + } + }; + + bidderRequest = { + refererInfo: { + domain: 'example.com', + page: 'https://www.example.com/test.html', + }, + bids: [{ + bidId: impId, + mediaTypes: { + [BANNER]: {sizes: sizes} + }, + params: { + placementId: 'test-placement' + } + }] + }; + + expectedBid = { + mediaType: 'banner', + requestId: 'imp-id', + seatBidId: '222209853178', + cpm: 1, + creative_id: 'creative-id', + creativeId: 'creative-id', + ttl: 300, + netRevenue: true, + ad: adm, + meta: {} + }; + + bidRequest = spec.buildRequests(bidderRequest.bids, bidderRequest); + }); + + it('pass on valid request', function () { + const bids = spec.interpretResponse(bidderResponse, bidRequest); + assert.deepEqual(bids[0], expectedBid); + }); + + it('fails on empty response', function () { + const bids = spec.interpretResponse({body: ''}, bidRequest); + assert.empty(bids); + }); + }); +});