diff --git a/modules/conceptxBidAdapter.js b/modules/conceptxBidAdapter.js new file mode 100644 index 00000000000..1147a50e71f --- /dev/null +++ b/modules/conceptxBidAdapter.js @@ -0,0 +1,70 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +// import { logError, logInfo, logWarn, parseUrl } from '../src/utils.js'; + +const BIDDER_CODE = 'conceptx'; +let ENDPOINT_URL = 'https://conceptx.cncpt-central.com/openrtb'; +// const LOG_PREFIX = 'ConceptX: '; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + isBidRequestValid: function (bid) { + return !!(bid.bidId); + }, + + buildRequests: function (validBidRequests, bidderRequest) { + // logWarn(LOG_PREFIX + 'all native assets containing URL should be sent as placeholders with sendId(icon, image, clickUrl, displayUrl, privacyLink, privacyIcon)'); + const requests = []; + + if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) { + ENDPOINT_URL += '?gdpr_applies=' + bidderRequest.gdprConsent.gdprApplies; + ENDPOINT_URL += '&consentString=' + bidderRequest.gdprConsent.consentString; + } + for (var i = 0; i < validBidRequests.length; i++) { + const requestParent = { adUnits: [], meta: {} }; + const bid = validBidRequests[i] + const { adUnitCode, auctionId, bidId, bidder, bidderRequestId, ortb2 } = bid + requestParent.meta = { adUnitCode, auctionId, bidId, bidder, bidderRequestId, ortb2 } + + const { site, adunit } = bid.params + const adUnit = { site, adunit, targetId: bid.bidId } + if (bid.mediaTypes && bid.mediaTypes.banner && bid.mediaTypes.banner.sizes) adUnit.dimensions = bid.mediaTypes.banner.sizes + requestParent.adUnits.push(adUnit); + requests.push({ + method: 'POST', + url: ENDPOINT_URL, + options: { + withCredentials: false, + }, + data: JSON.stringify(requestParent), + }); + } + + return requests; + }, + + interpretResponse: function (serverResponse, bidRequest) { + const bidResponsesFromServer = serverResponse.body.bidResponses; + const firstDummy = bidResponsesFromServer[0] + const firstSeat = firstDummy.ads[0] + const bidResponses = []; + const bidResponse = { + requestId: firstSeat.requestId, + cpm: firstSeat.cpm, + width: firstSeat.width, + height: firstSeat.height, + creativeId: firstSeat.creativeId, + dealId: firstSeat.dealId, + currency: firstSeat.currency, + netRevenue: true, + ttl: firstSeat.ttl, + referrer: firstSeat.referrer, + ad: firstSeat.html + }; + bidResponses.push(bidResponse); + return bidResponses; + }, + +} +registerBidder(spec); diff --git a/modules/conceptxBidAdapter.md b/modules/conceptxBidAdapter.md new file mode 100644 index 00000000000..1464c04025a --- /dev/null +++ b/modules/conceptxBidAdapter.md @@ -0,0 +1,36 @@ +# Overview + +``` +Module Name: ConceptX Bidder Adapter +Module Type: Bidder Adapter +Maintainer: info@concept.dk +``` + +# Description + +ConceptX Bidder Adapter for Prebid.js. +Only Banner format is supported. + +# Test Parameters +``` + var adUnits = [ + { + code: "test-div", + mediaTypes: { + banner: { + sizes: [[980, 180]] + } + }, + bids: [ + { + bidder: "conceptx", + params: { + site: "example", + adunit: "some-id-3", + } + }, + ] + }, + + ]; +``` diff --git a/test/spec/modules/conceptxBidAdapter_spec.js b/test/spec/modules/conceptxBidAdapter_spec.js new file mode 100644 index 00000000000..349ee765b71 --- /dev/null +++ b/test/spec/modules/conceptxBidAdapter_spec.js @@ -0,0 +1,136 @@ +// import or require modules necessary for the test, e.g.: +import { expect } from 'chai'; // may prefer 'assert' in place of 'expect' +import { spec } from 'modules/conceptxBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +// import { config } from 'src/config.js'; + +describe('conceptxBidAdapter', function () { + const URL = 'https://conceptx.cncpt-central.com/openrtb'; + + // before(() => { + + // }); + + // after(() => { + // // $$PREBID_GLOBAL$$.bidderSettings = {}; + // }); + + // afterEach(function () { + // config.resetConfig(); + // }); + + const ENDPOINT_URL = `${URL}`; + const ENDPOINT_URL_CONSENT = `${URL}?gdpr_applies=true&consentString=ihaveconsented`; + const adapter = newBidder(spec); + + const bidderRequests = [ + { + bidId: '123', + bidder: 'conceptx', + params: { + site: 'example', + adunit: 'some-id-3' + }, + mediaTypes: { + banner: { + sizes: [[930, 180]], + } + }, + } + ] + + const singleBidRequest = { + bid: [ + { + bidId: '123', + } + ] + } + + const serverResponse = { + body: { + 'bidResponses': [ + { + 'ads': [ + { + 'referrer': 'http://localhost/prebidpage_concept_bidder.html', + 'ttl': 360, + 'html': '

DUMMY

', + 'requestId': '214dfadd1f8826', + 'cpm': 46, + 'currency': 'DKK', + 'width': 930, + 'height': 180, + 'creativeId': 'FAKE-ID', + 'meta': { + 'mediaType': 'banner' + }, + 'netRevenue': true, + 'destinationUrls': { + 'destination': 'https://concept.dk' + } + } + ], + 'matchedAdCount': 1, + 'targetId': '214dfadd1f8826' + } + ] + } + } + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bidderRequests[0])).to.equal(true); + }); + }); + + describe('buildRequests', function () { + it('Test requests', function () { + const request = spec.buildRequests(bidderRequests, {}); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('data'); + const bid = JSON.parse(request[0].data).adUnits[0] + expect(bid.site).to.equal('example'); + expect(bid.adunit).to.equal('some-id-3'); + expect(JSON.stringify(bid.dimensions)).to.equal(JSON.stringify([ + [930, 180]])); + }); + }); + + describe('user privacy', function () { + it('should NOT send GDPR Consent data if gdprApplies equals undefined', function () { + let request = spec.buildRequests(bidderRequests, { gdprConsent: { gdprApplies: undefined, consentString: 'iDoNotConsent' } }); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('url') + expect(request[0].url).to.equal(ENDPOINT_URL); + }); + it('should send GDPR Consent data if gdprApplies', function () { + let request = spec.buildRequests(bidderRequests, { gdprConsent: { gdprApplies: true, consentString: 'ihaveconsented' } }); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('url') + expect(request[0].url).to.equal(ENDPOINT_URL_CONSENT); + }); + }); + + describe('interpretResponse', function () { + it('should return valid response when passed valid server response', function () { + const interpretedResponse = spec.interpretResponse(serverResponse, singleBidRequest); + const ad = serverResponse.body.bidResponses[0].ads[0] + expect(interpretedResponse).to.have.lengthOf(1); + expect(interpretedResponse[0].cpm).to.equal(ad.cpm); + expect(interpretedResponse[0].width).to.equal(Number(ad.width)); + expect(interpretedResponse[0].height).to.equal(Number(ad.height)); + expect(interpretedResponse[0].creativeId).to.equal(ad.creativeId); + expect(interpretedResponse[0].currency).to.equal(ad.currency); + expect(interpretedResponse[0].netRevenue).to.equal(true); + expect(interpretedResponse[0].ad).to.equal(ad.html); + expect(interpretedResponse[0].ttl).to.equal(360); + }); + }); +});