Skip to content

Commit

Permalink
Equativ Bid Adapter: support native bid requests (#12566)
Browse files Browse the repository at this point in the history
* add support of dsa

* restore topics

* DSA fix for UT

* drafy of adapter

* fixes after dev test

* make world simpler

* fix prev commit

* return empty userSyncs array by default

* adjustments

* apply prettier

* unit tests for Equativ adapter

* add dsp user sync

* add readme

* body can be undef

* support additional br params

* remove user sync

* do not send dt param

* handle floors and network id

* handle empty media types

* get min floor

* fix desc for u.t.

* better name for u.t.

* add u.t. for not supported media type

* improve currency u.t.

* updates after pr review

* SADR-6484: initial video setup for new PBJS adapter

* SADR-6484: Adding logging requirement missed earlier

* SADR-6484: handle ext.rewarded prop for video with new oRTBConverter

* SADR-6484: test revision + not sending bid requests where video obj is empty

* refactoring and u.t.

* rename variable

* Equativ: SADR-6615: adding unit tests for and additional logging to bid adapter to support native requests

* revert changes rel. to test endpoint

* revert changes rel. to test endpoint

* split imp[0] into seperate requests and fix u.t.

* Equativ bid adapter: adding support for native media type

* Equativ bid adapter: update unit test for native-support work

* Equativ bid adapter: removing console.log from unit test file

* Equativ bid adapter: clarifying refinements regarding native-request processing

* Equativ Bid Adapter: updating unit tests for native requests

* PR feedback

---------

Co-authored-by: Elżbieta SZPONDER <[email protected]>
Co-authored-by: eszponder <[email protected]>
Co-authored-by: Krzysztof Sokół <[email protected]>
Co-authored-by: Krzysztof Sokół <[email protected]>
Co-authored-by: janzych-smart <[email protected]>
  • Loading branch information
6 people authored Dec 11, 2024
1 parent b4d6dd4 commit 892f2bc
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 13 deletions.
33 changes: 22 additions & 11 deletions modules/equativBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { config } from '../src/config.js';
import { BANNER, VIDEO } from '../src/mediaTypes.js';
import { getBidFloor } from '../libraries/equativUtils/equativUtils.js'
import { getStorageManager } from '../src/storageManager.js';

import { getBidFloor } from '../libraries/equativUtils/equativUtils.js';
import { ortbConverter } from '../libraries/ortbConverter/converter.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { config } from '../src/config.js';
import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
import { getStorageManager } from '../src/storageManager.js';
import { deepAccess, deepSetValue, logError, logWarn, mergeDeep } from '../src/utils.js';

/**
Expand All @@ -18,22 +19,22 @@ const LOG_PREFIX = 'Equativ:';
const PID_COOKIE_NAME = 'eqt_pid';

/**
* Evaluates a bid request for validity. Returns false if the
* request contains a video media type with no properties, true
* otherwise.
* Evaluates impressions for validity. The entry evaluated is considered valid if NEITHER of these conditions are met:
* 1) it has a `video` property defined for `mediaTypes.video` which is an empty object
* 2) it has a `native` property defined for `mediaTypes.native` which is an empty object
* @param {*} bidReq A bid request object to evaluate
* @returns boolean
*/
function isValid(bidReq) {
return !(bidReq.mediaTypes.video && JSON.stringify(bidReq.mediaTypes.video) === '{}');
return !(bidReq.mediaTypes.video && JSON.stringify(bidReq.mediaTypes.video) === '{}') && !(bidReq.mediaTypes.native && JSON.stringify(bidReq.mediaTypes.native) === '{}');
}

export const storage = getStorageManager({ bidderCode: BIDDER_CODE });

export const spec = {
code: BIDDER_CODE,
gvlid: 45,
supportedMediaTypes: [BANNER, VIDEO],
supportedMediaTypes: [BANNER, VIDEO, NATIVE],

/**
* @param bidRequests
Expand All @@ -42,7 +43,7 @@ export const spec = {
*/
buildRequests: (bidRequests, bidderRequest) => {
if (bidRequests.filter(isValid).length === 0) {
logError(`${LOG_PREFIX} No useful bid requests to process. No request will be sent.`, bidRequests);
logError(`${LOG_PREFIX} No useful bid requests to process. No requests will be sent.`, bidRequests);
return undefined;
}

Expand Down Expand Up @@ -121,7 +122,6 @@ export const converter = ortbConverter({

imp.bidfloor = imp.bidfloor || getBidFloor(bidRequest, config.getConfig('currency.adServerCurrency'), mediaType);
imp.secure = 1;

imp.tagid = bidRequest.adUnitCode;

if (!deepAccess(bidRequest, 'ortb2Imp.rwdd') && deepAccess(bidRequest, 'mediaTypes.video.ext.rewarded')) {
Expand Down Expand Up @@ -151,6 +151,17 @@ export const converter = ortbConverter({
});
}

// "assets" is not included as a property to check here because the
// ortbConverter library checks for it already and will skip processing
// the request if it is missing
if (deepAccess(bid, 'mediaTypes.native')) {
['privacy', 'plcmttype', 'eventtrackers'].forEach(prop => {
if (!bid.mediaTypes.native.ortb[prop]) {
logWarn(`${LOG_PREFIX} Property "${prop}" is missing from request. Request will proceed, but the use of ${prop} for native requests is strongly encouraged.`, bid);
}
});
}

const pid = storage.getCookie(PID_COOKIE_NAME);
if (pid) {
deepSetValue(req, 'user.buyeruid', pid);
Expand Down
160 changes: 158 additions & 2 deletions test/spec/modules/equativBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BANNER } from 'src/mediaTypes.js';
import { getBidFloor } from 'libraries/equativUtils/equativUtils.js'
import { getBidFloor } from 'libraries/equativUtils/equativUtils.js';
import { converter, spec, storage } from 'modules/equativBidAdapter.js';
import { BANNER } from 'src/mediaTypes.js';
import * as utils from '../../../src/utils.js';

describe('Equativ bid adapter tests', () => {
Expand Down Expand Up @@ -78,6 +78,64 @@ describe('Equativ bid adapter tests', () => {
}
];

const nativeOrtbRequest = {
assets: [{
id: 0,
required: 1,
title: {
len: 140
}
},
{
id: 1,
required: 1,
img: {
type: 3,
w: 300,
h: 600
}
},
{
id: 2,
required: 1,
data: {
type: 1
}
}],
context: 1,
eventtrackers: [{
event: 1,
methods: [1, 2]
}],
plcmttype: 1,
privacy: 1,
ver: '1.2',
};
const DEFAULT_NATIVE_BID_REQUESTS = [
{
adUnitCode: 'equativ_native_42',
bidId: 'equativ_native_bidid_42',
mediaTypes: {
native: {
ortb: {
...nativeOrtbRequest
}
},
},
nativeOrtbRequest,
bidder: 'equativ',
params: {
networkId: 777,
},
requestId: 'equativ_native_reqid_42',
ortb2Imp: {
ext: {
tid: 'equativ_native_tid_42',
},
},
}
];

const DEFAULT_BANNER_BIDDER_REQUEST = {
bidderCode: 'equativ',
bids: DEFAULT_BANNER_BID_REQUESTS,
Expand All @@ -88,6 +146,11 @@ describe('Equativ bid adapter tests', () => {
bids: DEFAULT_VIDEO_BID_REQUESTS,
};

const DEFAULT_NATIVE_BIDDER_REQUEST = {
bidderCode: 'equativ',
bids: DEFAULT_NATIVE_BID_REQUESTS,
};

const SAMPLE_RESPONSE = {
body: {
id: '12h712u7-k22g-8124-ab7a-h268s22dy271',
Expand Down Expand Up @@ -495,6 +558,99 @@ describe('Equativ bid adapter tests', () => {
expect(utils.logError.args[0][0]).to.satisfy(arg => arg.includes('No request'));
expect(request).to.be.undefined;
});

it('should build a native request properly under normal circumstances', () => {
if (FEATURES.NATIVE) {
// ASSEMBLE
const expectedResult = true;

// ACT
const request = spec.buildRequests(DEFAULT_NATIVE_BID_REQUESTS, {})[0].data;

// ASSERT
expect(request.imp[0]).to.have.property('native');

const nativeObj = request.imp[0].native;
expect(nativeObj).to.have.property('ver').and.to.equal('1.2');
expect(nativeObj).to.have.property('request').and.to.be.a('string');

const requestObj = JSON.parse(nativeObj.request);
expect(requestObj).to.have.property('assets').and.to.be.an('array');
expect(requestObj).to.have.property('eventtrackers').and.to.be.an('array');
expect(requestObj).to.have.property('plcmttype').and.to.equal(1);
expect(requestObj).to.have.property('privacy').and.to.equal(1);
expect(requestObj).to.have.property('ver').and.to.equal('1.2');
}
});

it('should not send a native request when it has an empty body and no other impressions with any media types are defined', () => {
if (FEATURES.NATIVE) {
// ASSEMBLE
const emptyNativeRequest = {
...DEFAULT_NATIVE_BID_REQUESTS[0],
mediaTypes: {
native: {}
}
};
const bidRequests = [ emptyNativeRequest ];
const bidderRequest = { ...DEFAULT_NATIVE_BIDDER_REQUEST, bids: bidRequests };

// ACT
const request = spec.buildRequests(bidRequests, bidderRequest);

// ASSERT
expect(utils.logError.calledOnce).to.equal(true);
expect(utils.logError.args[0][0]).to.satisfy(arg => arg.includes('No request'));
expect(request).to.be.undefined;
}
});

it('should warn about missing "assets" property for native requests', () => {
if (FEATURES.NATIVE) {
// ASSEMBLE
const missingRequiredNativeRequest = DEFAULT_NATIVE_BID_REQUESTS[0];

// removing just "assets" for this test
delete missingRequiredNativeRequest.nativeOrtbRequest.assets;
const bidRequests = [ missingRequiredNativeRequest ];
const bidderRequest = { ...DEFAULT_NATIVE_BIDDER_REQUEST, bids: bidRequests };

// this value comes from native.js, part of the ortbConverter library
const warningMsgFromLibrary = 'mediaTypes.native is set, but no assets were specified. Native request skipped.'

// ACT
spec.buildRequests(bidRequests, bidderRequest);

// ASSERT
expect(utils.logWarn.callCount).to.equal(1)
expect(utils.logWarn.getCall(0).args[0]).to.satisfy(arg => arg.includes(warningMsgFromLibrary));
}
});

it('should warn about other missing required properties for native requests', () => {
if (FEATURES.NATIVE) {
// ASSEMBLE
const missingRequiredNativeRequest = DEFAULT_NATIVE_BID_REQUESTS[0];

// ortbConverter library will warn about missing assets; we supply warnings for these properties here
delete missingRequiredNativeRequest.mediaTypes.native.ortb.eventtrackers;
delete missingRequiredNativeRequest.mediaTypes.native.ortb.plcmttype;
delete missingRequiredNativeRequest.mediaTypes.native.ortb.privacy;

const bidRequests = [ missingRequiredNativeRequest ];
const bidderRequest = { ...DEFAULT_NATIVE_BIDDER_REQUEST, bids: bidRequests };

// ACT
spec.buildRequests(bidRequests, bidderRequest);

// ASSERT
expect(utils.logWarn.callCount).to.equal(4); // the first message, regarding missing assets, is supplied by the ortbConverter library
expect(utils.logWarn.getCall(0).args[0]).to.satisfy(arg => arg.includes('no assets were specified'));
expect(utils.logWarn.getCall(1).args[0]).to.satisfy(arg => arg.includes('"privacy" is missing'));
expect(utils.logWarn.getCall(2).args[0]).to.satisfy(arg => arg.includes('"plcmttype" is missing'));
expect(utils.logWarn.getCall(3).args[0]).to.satisfy(arg => arg.includes('"eventtrackers" is missing'));
}
});
});

describe('getBidFloor', () => {
Expand Down

0 comments on commit 892f2bc

Please sign in to comment.