diff --git a/modules/blueBidAdapter.js b/modules/blueBidAdapter.js new file mode 100644 index 00000000000..ba8d0a9e8fe --- /dev/null +++ b/modules/blueBidAdapter.js @@ -0,0 +1,122 @@ +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { deepSetValue, isFn, isPlainObject } from '../src/utils.js'; + +const BIDDER_CODE = 'blue'; +const ENDPOINT_URL = 'https://bidder-us-east-1.getblue.io/engine/?src=prebid'; +const GVLID = 620; // GVLID for your bidder +const COOKIE_NAME = 'ckid'; // Cookie name for identifying users +const CURRENCY = 'USD'; // Currency used in bid floors + +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); + +const converter = ortbConverter({ + context: { + netRevenue: true, // Default netRevenue setting + ttl: 100, // Default time-to-live for bid responses + }, + imp, + request, +}); + +function request(buildRequest, imps, bidderRequest, context) { + let request = buildRequest(imps, bidderRequest, context); + deepSetValue(request, 'site.publisher.id', context.publisherId); + return request; +} + +function imp(buildImp, bidRequest, context) { + const imp = buildImp(bidRequest, context); + const floor = getBidFloor(bidRequest); + if (floor) { + imp.bidfloor = floor; + imp.bidfloorcur = CURRENCY; + } + return imp; +} + +function getBidFloor(bid) { + if (isFn(bid.getFloor)) { + let floor = bid.getFloor({ + currency: CURRENCY, + mediaType: BANNER, + size: '*', + }); + if ( + isPlainObject(floor) && + !isNaN(floor.floor) && + floor.currency === CURRENCY + ) { + return floor.floor; + } + } + return null; +} + +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + supportedMediaTypes: [BANNER], // Supported ad types + + // Validate bid request + isBidRequestValid: function (bid) { + return !!bid.params.placementId && !!bid.params.publisherId; + }, + + // Build OpenRTB requests using `ortbConverter` + buildRequests: function (validBidRequests, bidderRequest) { + const context = { + publisherId: validBidRequests.find( + (bidRequest) => bidRequest.params?.publisherId + )?.params.publisherId, + }; + + const ortbRequest = converter.toORTB({ + bidRequests: validBidRequests, + bidderRequest, + context, + }); + + // Add GVLID and cookie ID to the request + ortbRequest.ext = ortbRequest.ext || {}; + deepSetValue(ortbRequest, 'ext.gvlid', GVLID); + + // Include user cookie if available + const ckid = storage.getDataFromLocalStorage('blueID') || storage.getCookie(COOKIE_NAME) || null; + if (ckid) { + deepSetValue(ortbRequest, 'user.ext.buyerid', ckid); + } + + return { + method: 'POST', + url: ENDPOINT_URL, + data: JSON.stringify(ortbRequest), + options: { + contentType: 'text/plain', + }, + }; + }, + + // Interpret OpenRTB responses using `ortbConverter` + interpretResponse: function (serverResponse, request) { + const ortbResponse = serverResponse.body; + + // Parse the OpenRTB response into Prebid bid responses + const prebidResponses = converter.fromORTB({ + response: ortbResponse, + request: request.data, + }).bids; + + // Example: Modify bid responses if needed + prebidResponses.forEach((bid) => { + bid.meta = bid.meta || {}; + bid.meta.adapterVersion = '1.0.0'; + }); + + return prebidResponses; + }, +}; + +registerBidder(spec); diff --git a/modules/blueBidAdapter.md b/modules/blueBidAdapter.md new file mode 100644 index 00000000000..4f446b1ff4b --- /dev/null +++ b/modules/blueBidAdapter.md @@ -0,0 +1,28 @@ +# Overview + +Module Name: Blue Bidder Adapter +Module Type: Bidder Adapter +Maintainer: celsooliveira@getblue.io + +# Description + +Module that connects to Blue's demand sources. + +# Test Parameters +``` + var adUnits = [ + { + code: 'banner-ad-div', + sizes: [[300, 250], [728, 90]], + bids: [ + { + bidder: 'blue', + params: { + publisherId: "xpto", + placementId: "xpto", + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/blueBidAdapter_spec.js b/test/spec/modules/blueBidAdapter_spec.js new file mode 100755 index 00000000000..eb79ec6f2ea --- /dev/null +++ b/test/spec/modules/blueBidAdapter_spec.js @@ -0,0 +1,91 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; +import { spec, storage } from 'modules/blueBidAdapter.js'; + +const BIDDER_CODE = 'blue'; +const ENDPOINT_URL = 'https://bidder-us-east-1.getblue.io/engine/?src=prebid'; +const GVLID = 620; +const COOKIE_NAME = 'ckid'; +const CURRENCY = 'USD'; + +describe('blueBidAdapter:', function () { + let sandbox; + + beforeEach(function () { + sandbox = sinon.createSandbox(); + }); + + afterEach(function () { + sandbox.restore(); + }); + + describe('isBidRequestValid:', function () { + it('should return true for valid bid requests', function () { + const validBid = { + params: { + placementId: '12345', + publisherId: '67890', + }, + }; + expect(spec.isBidRequestValid(validBid)).to.be.true; + }); + + it('should return false for invalid bid requests', function () { + const invalidBid = { + params: { + placementId: '12345', + }, + }; + expect(spec.isBidRequestValid(invalidBid)).to.be.false; + }); + }); + + describe('buildRequests:', function () { + let validBidRequests; + let bidderRequest; + + beforeEach(function () { + validBidRequests = [ + { + bidId: 'bid1', + params: { + placementId: '12345', + publisherId: '67890', + }, + getFloor: () => ({ currency: CURRENCY, floor: 1.5 }), + }, + ]; + + bidderRequest = { + refererInfo: { + page: 'https://example.com', + }, + }; + + sandbox.stub(storage, 'getDataFromLocalStorage').returns('testBuyerId'); + }); + + it('should build a valid OpenRTB request', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + + expect(request.method).to.equal('POST'); + expect(request.url).to.equal(ENDPOINT_URL); + expect(request.options.contentType).to.equal('test/plain'); + + const ortbRequest = JSON.parse(request.data); + expect(ortbRequest.ext.gvlid).to.equal(GVLID); + expect(ortbRequest.user.ext.buyerid).to.equal('testBuyerId'); + expect(ortbRequest.imp[0].bidfloor).to.equal(1.5); + expect(ortbRequest.imp[0].bidfloorcur).to.equal(CURRENCY); + }); + + it('should omit bidfloor if getFloor is not implemented', function () { + validBidRequests[0].getFloor = undefined; + + const request = spec.buildRequests(validBidRequests, bidderRequest); + const ortbRequest = JSON.parse(request.data); + + expect(ortbRequest.imp[0].bidfloor).to.be.undefined; + }); + }); +});