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

33across Bid Adapter : support for ORTB video.plcmt field #11641

Merged
merged 1 commit into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
28 changes: 22 additions & 6 deletions modules/33acrossBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const VIDEO_ORTB_PARAMS = [
'minduration',
'maxduration',
'placement',
'plcmt',
'protocols',
'startdelay',
'skip',
Expand Down Expand Up @@ -140,10 +141,10 @@ function _validateVideo(bid) {
}

// If placement if defined, it must be a number
if (
typeof videoParams.placement !== 'undefined' &&
typeof videoParams.placement !== 'number'
) {
if ([ videoParams.placement, videoParams.plcmt ].some(value => (
typeof value !== 'undefined' &&
typeof value !== 'number'
))) {
return false;
}

Expand Down Expand Up @@ -490,12 +491,27 @@ function _buildVideoORTB(bidRequest) {

// Placement Inference Rules:
// - If no placement is defined then default to 2 (In Banner)
// - If the old deprecated field is defined, use its value for the recent placement field
// - If product is instream (for instream context) then override placement to 1
video.placement = video.placement || 2;

const calculatePlacementValue = () => {
const IN_BANNER_PLACEMENT_VALUE = 2;

if (video.placement) {
logWarn('[33Across Adapter] The ORTB field `placement` is deprecated, please use `plcmt` instead');

return video.placement;
}

return IN_BANNER_PLACEMENT_VALUE;
}

video.plcmt ??= calculatePlacementValue();

if (product === PRODUCT.INSTREAM) {
video.startdelay = video.startdelay || 0;
video.placement = 1;
video.plcmt = 1;
video.placement &&= 1;
}

// bidfloors
Expand Down
221 changes: 154 additions & 67 deletions test/spec/modules/33acrossBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ describe('33acrossBidAdapter:', function () {
video: {
w: 300,
h: 250,
placement: 2,
plcmt: 2,
...params
}
});
Expand Down Expand Up @@ -733,6 +733,11 @@ describe('33acrossBidAdapter:', function () {
'foo'
];

invalidPlacement.forEach((placement) => {
this.bid.mediaTypes.video.plcmt = placement;
expect(spec.isBidRequestValid(this.bid)).to.be.false;
});

invalidPlacement.forEach((placement) => {
this.bid.mediaTypes.video.placement = placement;
expect(spec.isBidRequestValid(this.bid)).to.be.false;
Expand Down Expand Up @@ -1520,89 +1525,171 @@ describe('33acrossBidAdapter:', function () {
});
});

context('when mediaType has video only and context is instream', function() {
it('builds instream request with default params', function() {
const bidRequests = (
new BidRequestsBuilder()
.withVideo({context: 'instream'})
.build()
);
context('when mediaType has video only', function() {
context('and context is instream', function() {
it('builds instream request with default params', function() {
const bidRequests = (
new BidRequestsBuilder()
.withVideo({context: 'instream'})
.build()
);

const ttxRequest = new TtxRequestBuilder()
.withVideo()
.withProduct('instream')
.build();
const ttxRequest = new TtxRequestBuilder()
.withVideo()
.withProduct('instream')
.build();

ttxRequest.imp[0].video.placement = 1;
ttxRequest.imp[0].video.startdelay = 0;
ttxRequest.imp[0].video.plcmt = 1;
ttxRequest.imp[0].video.startdelay = 0;

const serverRequest = new ServerRequestBuilder()
.withData(ttxRequest)
.build();
const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest);
const serverRequest = new ServerRequestBuilder()
.withData(ttxRequest)
.build();
const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest);

validateBuiltServerRequest(builtServerRequest, serverRequest);
});
validateBuiltServerRequest(builtServerRequest, serverRequest);
});

it('builds instream request with params passed', function() {
const bidRequests = (
new BidRequestsBuilder()
.withVideo({context: 'instream', startdelay: -2})
.build()
);
it('builds instream request with params passed', function() {
const bidRequests = (
new BidRequestsBuilder()
.withVideo({context: 'instream', startdelay: -2})
.build()
);

const ttxRequest = new TtxRequestBuilder()
.withVideo({startdelay: -2, placement: 1})
.withProduct('instream')
.build();
const ttxRequest = new TtxRequestBuilder()
.withVideo({startdelay: -2, plcmt: 1})
.withProduct('instream')
.build();

const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest);
const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest);

expect(JSON.parse(builtServerRequest.data)).to.deep.equal(ttxRequest);
});
});
expect(JSON.parse(builtServerRequest.data)).to.deep.equal(ttxRequest);
});

context('when mediaType has video only and context is outstream', function() {
it('builds siab request with video only with default params', function() {
const bidRequests = (
new BidRequestsBuilder()
.withVideo({context: 'outstream'})
.build()
);
it('overrides the placement value', function() {
const bidRequests = (
new BidRequestsBuilder()
.withVideo({
plcmt: 2, // Incorrect placement value for an instream video
placement: 2, // Placement specified in the DEPRECATED field.
context: 'instream'
})
.build()
);

const ttxRequest = new TtxRequestBuilder()
.withVideo()
.withProduct('siab')
.build();
const ttxRequest = new TtxRequestBuilder()
.withVideo()
.withProduct('instream')
.build();

ttxRequest.imp[0].video.placement = 2;
ttxRequest.imp[0].video.plcmt = 1;
ttxRequest.imp[0].video.placement = 1;
ttxRequest.imp[0].video.startdelay = 0;

const serverRequest = new ServerRequestBuilder()
.withData(ttxRequest)
.build();
const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest);
const serverRequest = new ServerRequestBuilder()
.withData(ttxRequest)
.build();
const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest);

validateBuiltServerRequest(builtServerRequest, serverRequest);
expect(JSON.parse(builtServerRequest.data)).to.deep.equal(ttxRequest);
});

context('when the placement is still specified in the DEPRECATED `placement` field', function() {
it('overwrites its value and sets it in the recent `plcmt` field as well', function() {
const bidRequests = (
new BidRequestsBuilder()
.withVideo({
placement: 2, // Incorrect placement for an instream video
context: 'instream'
})
.build()
);

const ttxRequest = new TtxRequestBuilder()
.withVideo()
.withProduct('instream')
.build();

ttxRequest.imp[0].video.plcmt = 1;
ttxRequest.imp[0].video.placement = 1;
ttxRequest.imp[0].video.startdelay = 0;

const serverRequest = new ServerRequestBuilder()
.withData(ttxRequest)
.build();
const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest);

expect(JSON.parse(builtServerRequest.data)).to.deep.equal(ttxRequest);
});
});
});

it('builds siab request with video params passed', function() {
const bidRequests = (
new BidRequestsBuilder()
.withVideo({context: 'outstream', placement: 3, playbackmethod: [2]})
.build()
);
context('and context is outstream', function() {
it('builds siab request with video only with default params', function() {
const bidRequests = (
new BidRequestsBuilder()
.withVideo({context: 'outstream'})
.build()
);

const ttxRequest = new TtxRequestBuilder()
.withVideo({placement: 3, playbackmethod: [2]})
.withProduct('siab')
.build();
const ttxRequest = new TtxRequestBuilder()
.withVideo()
.withProduct('siab')
.build();

const serverRequest = new ServerRequestBuilder()
.withData(ttxRequest)
.build();
const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest);
// No placement specified, final value should default to 2.
ttxRequest.imp[0].video.plcmt = 2;

validateBuiltServerRequest(builtServerRequest, serverRequest);
const serverRequest = new ServerRequestBuilder()
.withData(ttxRequest)
.build();
const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest);

validateBuiltServerRequest(builtServerRequest, serverRequest);
});

it('builds siab request with video params passed', function() {
const bidRequests = (
new BidRequestsBuilder()
.withVideo({context: 'outstream', plcmt: 3, playbackmethod: [2]})
.build()
);

const ttxRequest = new TtxRequestBuilder()
.withVideo({plcmt: 3, playbackmethod: [2]})
.withProduct('siab')
.build();

const serverRequest = new ServerRequestBuilder()
.withData(ttxRequest)
.build();
const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest);

validateBuiltServerRequest(builtServerRequest, serverRequest);
});

context('and the placement is specified in the DEPRECATED `placement` field', function() {
it('sets the recent `plcmt` field', function() {
const bidRequests = (
new BidRequestsBuilder()
.withVideo({context: 'outstream', placement: 3, playbackmethod: [2]})
.build()
);

const ttxRequest = new TtxRequestBuilder()
.withVideo({plcmt: 3, placement: 3, playbackmethod: [2]})
.withProduct('siab')
.build();

const serverRequest = new ServerRequestBuilder()
.withData(ttxRequest)
.build();
const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest);

validateBuiltServerRequest(builtServerRequest, serverRequest);
});
});
});
});

Expand Down Expand Up @@ -1686,7 +1773,7 @@ describe('33acrossBidAdapter:', function () {
.withProduct('siab')
.build();

ttxRequest.imp[0].video.placement = 2;
ttxRequest.imp[0].video.plcmt = 2;

const serverRequest = new ServerRequestBuilder()
.withData(ttxRequest)
Expand Down