Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Missena Bid Adapter: floor implementation #10534

Merged
merged 4 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 66 additions & 4 deletions modules/missenaBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { buildUrl, formatQS, logInfo, triggerPixel } from '../src/utils.js';
import {
buildUrl,
deepAccess,
formatQS,
isFn,
logInfo,
parseSizesInput,
triggerPixel,
} from '../src/utils.js';
import { config } from '../src/config.js';
import { BANNER } from '../src/mediaTypes.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';

Expand All @@ -7,6 +16,48 @@ const ENDPOINT_URL = 'https://bid.missena.io/';
const EVENTS_DOMAIN = 'events.missena.io';
const EVENTS_DOMAIN_DEV = 'events.staging.missena.xyz';

function getSize(sizesArray) {
const firstSize = sizesArray[0];
if (typeof firstSize !== 'string') return {};

const [widthStr, heightStr] = firstSize.toUpperCase().split('X');
return {
width: parseInt(widthStr, 10) || undefined,
height: parseInt(heightStr, 10) || undefined,
};
}

/* Get Floor price information */
function getFloor(bidRequest) {
if (!isFn(bidRequest.getFloor)) {
return {};
}
const sizesArray = getSizeArray(bidRequest);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is necessary, the floors module will already pick size from the request when it makes sense. What this does is force using the first size when there are multiple instead of looking for a floor that applies to all sizes.

Unless that was your intention I recommend removing this. (I am learning now that a lot of other adapters do this, so I suspect you're just following the pattern, which is unfortunately not a good one).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify - if you just call getFloor(), it will attempt to find reasonable defaults:

  • currency defaults to 'USD'
  • mediaType defaults to the request's if it contains only one
  • size defaults to the request's if it contains only one (which implies it also has only one mediaType)

Copy link
Contributor Author

@pdamoc pdamoc Oct 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dgirardi the functions you mentioned have been removed by @ysfbsf (my colleague who wrote this code). We will rely on our partners to properly configure their bidFloors (the code was there for corner cases).

Please let us know if we need to change anything else.

const size = getSize(sizesArray);

const bidFloors = bidRequest.getFloor({
currency: 'USD',
mediaType: BANNER,
size: [size.width, size.height],
});

if (!isNaN(bidFloors.floor)) {
return bidFloors;
}
}

function getSizeArray(bid) {
let inputSize = deepAccess(bid, 'mediaTypes.banner.sizes') || bid.sizes || [];

if (Array.isArray(bid.params?.size)) {
inputSize = !Array.isArray(bid.params.size[0])
? [bid.params.size]
: bid.params.size;
}

return parseSizesInput(inputSize);
}

export const spec = {
aliases: ['msna'],
code: BIDDER_CODE,
Expand Down Expand Up @@ -61,6 +112,12 @@ export const spec = {
payload.is_internal = bidRequest.params.isInternal;
}
payload.userEids = bidRequest.userIdAsEids || [];

const bidFloor = getFloor(bidRequest);
payload.floor = bidFloor?.floor;
payload.floor_currency = bidFloor?.currency;
payload.currency = config.getConfig('currency.adServerCurrency') || 'EUR';

return {
method: 'POST',
url: baseUrl + '?' + formatQS({ t: bidRequest.params.apiKey }),
Expand Down Expand Up @@ -89,7 +146,7 @@ export const spec = {
syncOptions,
serverResponses,
gdprConsent,
uspConsent
uspConsent,
) {
if (!syncOptions.iframeEnabled) {
return [];
Expand Down Expand Up @@ -128,8 +185,13 @@ export const spec = {
protocol: 'https',
hostname,
pathname: '/v1/bidsuccess',
search: { t: bid.params[0].apiKey, provider: bid.meta?.networkName, cpm: bid.cpm, currency: bid.currency },
})
search: {
t: bid.params[0].apiKey,
provider: bid.meta?.networkName,
cpm: bid.originalCpm,
currency: bid.originalCurrency,
},
}),
);
logInfo('Missena - Bid won', bid);
},
Expand Down
44 changes: 38 additions & 6 deletions test/spec/modules/missenaBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { expect } from 'chai';
import { spec, _getPlatform } from 'modules/missenaBidAdapter.js';
import { newBidder } from 'src/adapters/bidderFactory.js';
import { BANNER } from '../../../src/mediaTypes.js';

describe('Missena Adapter', function () {
const adapter = newBidder(spec);
Expand All @@ -11,6 +12,28 @@ describe('Missena Adapter', function () {
bidder: 'missena',
bidId: bidId,
sizes: [[1, 1]],
mediaTypes: { banner: { sizes: [[1, 1]] } },
params: {
apiKey: 'PA-34745704',
placement: 'sticky',
formats: ['sticky-banner'],
},
getFloor: (inputParams) => {
if (inputParams.mediaType === BANNER) {
return {
currency: 'EUR',
floor: 3.5,
};
} else {
return {};
}
},
};
const bidWithoutFloor = {
bidder: 'missena',
bidId: bidId,
sizes: [[1, 1]],
mediaTypes: { banner: { sizes: [[1, 1]] } },
params: {
apiKey: 'PA-34745704',
placement: 'sticky',
Expand All @@ -31,13 +54,13 @@ describe('Missena Adapter', function () {

it('should return false if the apiKey is missing', function () {
expect(
spec.isBidRequestValid(Object.assign(bid, { params: {} }))
spec.isBidRequestValid(Object.assign(bid, { params: {} })),
).to.equal(false);
});

it('should return false if the apiKey is an empty string', function () {
expect(
spec.isBidRequestValid(Object.assign(bid, { params: { apiKey: '' } }))
spec.isBidRequestValid(Object.assign(bid, { params: { apiKey: '' } })),
).to.equal(false);
});
});
Expand All @@ -56,9 +79,10 @@ describe('Missena Adapter', function () {
},
};

const requests = spec.buildRequests([bid, bid], bidderRequest);
const requests = spec.buildRequests([bid, bidWithoutFloor], bidderRequest);
const request = requests[0];
const payload = JSON.parse(request.data);
const payloadNoFloor = JSON.parse(requests[1].data);

it('should return as many server requests as bidder requests', function () {
expect(requests.length).to.equal(2);
Expand Down Expand Up @@ -89,6 +113,14 @@ describe('Missena Adapter', function () {
expect(payload.consent_string).to.equal(consentString);
expect(payload.consent_required).to.equal(true);
});
it('should send floor data', function () {
expect(payload.floor).to.equal(3.5);
expect(payload.floor_currency).to.equal('EUR');
});
it('should not send floor data if not available', function () {
expect(payloadNoFloor.floor).to.equal(undefined);
expect(payloadNoFloor.floor_currency).to.equal(undefined);
});
});

describe('interpretResponse', function () {
Expand Down Expand Up @@ -121,22 +153,22 @@ describe('Missena Adapter', function () {
expect(result.length).to.equal(1);

expect(Object.keys(result[0])).to.have.members(
Object.keys(serverResponse)
Object.keys(serverResponse),
);
});

it('should return an empty response when the server answers with a timeout', function () {
const result = spec.interpretResponse(
{ body: serverTimeoutResponse },
bid
bid,
);
expect(result).to.deep.equal([]);
});

it('should return an empty response when the server answers with an empty ad', function () {
const result = spec.interpretResponse(
{ body: serverEmptyAdResponse },
bid
bid,
);
expect(result).to.deep.equal([]);
});
Expand Down