Skip to content
Open
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
22 changes: 9 additions & 13 deletions modules/yieldmoBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,16 +147,12 @@ export const spec = {
serverRequest.eids = JSON.stringify(eids);
};

// Blocklists (request-level): merge ortb2 + params, send as comma-delimited
// params per the ad server's prebid-js endpoint (AS-5349). Omitted when empty.
// bcat (request-level): merge ortb2 + params, send as comma-delimited
// param per the ad server's prebid-js endpoint (AS-5349). Omitted when empty.
const bcat = getBlocklist(bidderRequest, bannerBidRequests[0], 'bcat');
if (bcat.length) {
serverRequest.bcat = bcat.join(',');
}
const badv = getBlocklist(bidderRequest, bannerBidRequests[0], 'badv');
if (badv.length) {
serverRequest.badv = badv.join(',');
}

// check if url exceeded max length
const fullUrl = `${bannerUrl}?${parseQueryStringParameters(serverRequest)}`;
Expand Down Expand Up @@ -453,7 +449,7 @@ function openRtbRequest(bidRequests, bidderRequest) {
imp: bidRequests.map(bidRequest => openRtbImpression(bidRequest)),
site: openRtbSite(bidRequests[0], bidderRequest),
device: deepAccess(bidderRequest, 'ortb2.device'),
badv: getBlocklist(bidderRequest, bidRequests[0], 'badv'),
badv: bidRequests[0].params.badv || [],
bcat: getBlocklist(bidderRequest, bidRequests[0], 'bcat'),
ext: {
prebid: '$prebid.version$',
Expand Down Expand Up @@ -721,6 +717,8 @@ function validateVideoParams(bid) {
validate('video.skippable', val => !isDefined(val) || isBoolean(val), paramInvalid);
validate('video.skipafter', val => !isDefined(val) || isNumber(val), paramInvalid);
validate('video.pos', val => !isDefined(val) || isNumber(val), paramInvalid);
validate('params.badv', val => !isDefined(val) || isArray(val), paramInvalid,
'array of strings, ex: ["ford.com","pepsi.com"]');
return true;
} catch (e) {
logError(e.message);
Expand All @@ -729,10 +727,10 @@ function validateVideoParams(bid) {
}

/**
* Validate the publisher-set blocklist params (`bcat`/`badv`) for all media types
* (banner and video). A missing value is allowed; a value that is present but is
* not an array is rejected (drops the bid) — the agreed middle ground between
* dropping nothing and dropping over an absent optional field. See FS-12403.
* Validate the publisher-set `bcat` param for all media types (banner and video).
* A missing value is allowed; a value that is present but is not an array is
* rejected (drops the bid) — the agreed middle ground between dropping nothing and
* dropping over an absent optional field. See FS-12403.
* @param {BidRequest} bid bid request
* @return {boolean} true if valid (or absent), false if present but malformed
*/
Expand All @@ -741,8 +739,6 @@ function validateBlocklistParams(bid) {
try {
validate('params.bcat', val => !isDefined(val) || isArray(val), paramInvalid,
'array of strings, ex: ["IAB1-5","IAB1-6"]');
validate('params.badv', val => !isDefined(val) || isArray(val), paramInvalid,
'array of strings, ex: ["ford.com","pepsi.com"]');
return true;
} catch (e) {
logError(e.message);
Expand Down
27 changes: 15 additions & 12 deletions modules/yieldmoBidAdapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ var adUnits = [{ // Banner adUnit
placementId: '1779781193098233305', // string with at most 19 characters (may include numbers only)
bidFloor: .28, // optional param
lr_env: '***', // Optional. Live Ramp ATS envelope
bcat: ['IAB1-5', 'IAB1-6'], // optional, array of blocked IAB content categories (strings)
badv: ['ford.com', 'pepsi.com'] // optional, array of blocked advertiser domains (strings)
bcat: ['IAB1-5', 'IAB1-6'] // optional, array of blocked IAB content categories (strings)
}
}]
}];
Expand Down Expand Up @@ -70,7 +69,7 @@ var adUnits = [{ // Video adUnit
},
lr_env: '***', // Optional. Live Ramp ATS envelope
bcat: ['IAB1-5', 'IAB1-6'], // optional, array of blocked IAB content categories (strings)
badv: ['ford.com', 'pepsi.com'] // optional, array of blocked advertiser domains (strings)
badv: ['ford.com', 'pepsi.com'] // optional (video only), array of blocked advertiser domains (strings)
}
}]
}];
Expand Down Expand Up @@ -111,17 +110,21 @@ They can be specified in `mediaTypes.video` or in `bids[].params.video`.

# Blocklists (bcat / badv)

`bcat` (blocked IAB content categories) and `badv` (blocked advertiser domains) are
supported for **both banner and video**. Each is an optional array of strings and can
be supplied from either source:
`bcat` (blocked IAB content categories) is supported for **both banner and video**. It
is an optional array of strings and can be supplied from either source:

* the standardized first-party-data global — `ortb2.bcat` / `ortb2.badv`, or
* the Yieldmo bid params — `bids[].params.bcat` / `bids[].params.badv`.
* the standardized first-party-data global — `ortb2.bcat`, or
* the Yieldmo bid param — `bids[].params.bcat`.

When both sources are present they are **merged** (union) and de-duplicated — neither
source overrides the other.

Validation: a **missing** `bcat`/`badv` is always allowed, but if `params.bcat` /
`params.badv` is **present and not an array** the bid is rejected (`isBidRequestValid`
returns false). Non-string / empty elements inside an otherwise-valid array, and a
malformed `ortb2` value, are dropped with a console warning rather than failing the bid.
Validation: a **missing** `bcat` is always allowed, but if `params.bcat` is **present
and not an array** the bid is rejected (`isBidRequestValid` returns false). Non-string /
empty elements inside an otherwise-valid array, and a malformed `ortb2` value, are
dropped with a console warning rather than failing the bid.

`badv` (blocked advertiser domains) is supported for **video only**, via the Yieldmo bid
param `bids[].params.badv` (an optional array of strings). It is **not** read from
`ortb2.badv`, and banner requests never send `badv`. If `params.badv` is present on a
video bid but is not an array, the bid is rejected (video-only validation).
66 changes: 43 additions & 23 deletions test/spec/modules/yieldmoBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,26 +149,35 @@ describe('YieldmoAdapter', function () {
});
});

describe('Blocklist params (bcat / badv):', function () {
it('allows a bid when bcat/badv are absent (missing is fine)', function () {
describe('Blocklist param (bcat):', function () {
it('allows a bid when bcat is absent (missing is fine)', function () {
expect(spec.isBidRequestValid(mockBannerBid())).to.be.true;
expect(spec.isBidRequestValid(mockVideoBid())).to.be.true;
});

it('allows a bid when bcat/badv are arrays', function () {
expect(spec.isBidRequestValid(mockBannerBid({}, { bcat: ['IAB1-1'], badv: ['x.com'] }))).to.be.true;
expect(spec.isBidRequestValid(mockVideoBid({}, { bcat: ['IAB1-1'], badv: ['x.com'] }))).to.be.true;
it('allows a bid when bcat is an array', function () {
expect(spec.isBidRequestValid(mockBannerBid({}, { bcat: ['IAB1-1'] }))).to.be.true;
expect(spec.isBidRequestValid(mockVideoBid({}, { bcat: ['IAB1-1'] }))).to.be.true;
});

it('drops a bid when bcat is present but not an array', function () {
expect(spec.isBidRequestValid(mockBannerBid({}, { bcat: 'IAB1-1' }))).to.be.false;
expect(spec.isBidRequestValid(mockVideoBid({}, { bcat: 'IAB1-1' }))).to.be.false;
});
});

describe('badv param (video-only validation):', function () {
it('allows a video bid when badv is an array', function () {
expect(spec.isBidRequestValid(mockVideoBid({}, { badv: ['ford.com'] }))).to.be.true;
});

it('drops a bid when badv is present but not an array', function () {
expect(spec.isBidRequestValid(mockBannerBid({}, { badv: 'ford.com' }))).to.be.false;
it('drops a video bid when badv is present but not an array', function () {
expect(spec.isBidRequestValid(mockVideoBid({}, { badv: 'ford.com' }))).to.be.false;
});

it('does not validate badv for banner bids (allowed even when not an array)', function () {
expect(spec.isBidRequestValid(mockBannerBid({}, { badv: 'ford.com' }))).to.be.true;
});
});
});

Expand Down Expand Up @@ -838,12 +847,11 @@ describe('YieldmoAdapter', function () {
});
});

describe('bcat / badv blocklists (FS-12403)', function () {
it('banner: sends merged bcat/badv as comma-delimited GET params', function () {
const bidderReq = mockBidderRequest({ ortb2: { bcat: ['IAB1-1'], badv: ['ortb.com'] } });
const data = buildAndGetData([mockBannerBid({}, { bcat: ['IAB2-2'], badv: ['param.com'] })], 0, bidderReq);
describe('bcat blocklist (FS-12403)', function () {
it('banner: sends merged bcat as a comma-delimited GET param', function () {
const bidderReq = mockBidderRequest({ ortb2: { bcat: ['IAB1-1'] } });
const data = buildAndGetData([mockBannerBid({}, { bcat: ['IAB2-2'] })], 0, bidderReq);
expect(data.bcat).to.equal('IAB1-1,IAB2-2');
expect(data.badv).to.equal('ortb.com,param.com');
});

it('banner: unions ortb2 + params (neither source silently wins)', function () {
Expand All @@ -859,29 +867,31 @@ describe('YieldmoAdapter', function () {
});

it('banner: reads from ortb2 alone', function () {
const bidderReq = mockBidderRequest({ ortb2: { bcat: ['IAB1-1'], badv: ['x.com'] } });
const bidderReq = mockBidderRequest({ ortb2: { bcat: ['IAB1-1'] } });
const data = buildAndGetData([mockBannerBid()], 0, bidderReq);
expect(data.bcat).to.equal('IAB1-1');
expect(data.badv).to.equal('x.com');
});

it('banner: reads from params alone', function () {
const data = buildAndGetData([mockBannerBid({}, { bcat: ['IAB1-1'], badv: ['x.com'] })], 0, mockBidderRequest());
const data = buildAndGetData([mockBannerBid({}, { bcat: ['IAB1-1'] })], 0, mockBidderRequest());
expect(data.bcat).to.equal('IAB1-1');
expect(data.badv).to.equal('x.com');
});

it('banner: omits bcat/badv entirely when empty', function () {
it('banner: omits bcat entirely when empty', function () {
const data = buildAndGetData([mockBannerBid()], 0, mockBidderRequest());
expect(data).to.not.have.property('bcat');
});

it('banner: never sends badv (reverted in FS-12411)', function () {
const bidderReq = mockBidderRequest({ ortb2: { badv: ['ortb.com'] } });
const data = buildAndGetData([mockBannerBid({}, { badv: ['param.com'] })], 0, bidderReq);
expect(data).to.not.have.property('badv');
});

it('video: sends merged bcat/badv as deduped arrays', function () {
const bidderReq = mockBidderRequest({ ortb2: { bcat: ['IAB1-1'], badv: ['ortb.com'] } }, [mockVideoBid()]);
const payload = buildAndGetData([mockVideoBid({}, { bcat: ['IAB2-2'], badv: ['param.com'] })], 0, bidderReq);
it('video: sends merged bcat as a deduped array', function () {
const bidderReq = mockBidderRequest({ ortb2: { bcat: ['IAB1-1'] } }, [mockVideoBid()]);
const payload = buildAndGetData([mockVideoBid({}, { bcat: ['IAB2-2'] })], 0, bidderReq);
expect(payload.bcat).to.deep.equal(['IAB1-1', 'IAB2-2']);
expect(payload.badv).to.deep.equal(['ortb.com', 'param.com']);
});

it('video: reads ortb2.bcat (not the legacy bidderRequest.bcat path)', function () {
Expand All @@ -890,13 +900,23 @@ describe('YieldmoAdapter', function () {
expect(payload.bcat).to.deep.equal(['RIGHT']);
});

it('video: defaults to empty arrays when no blocklists are set', function () {
it('video: defaults bcat to an empty array when unset', function () {
const payload = buildAndGetData([mockVideoBid()], 0, mockBidderRequest({}, [mockVideoBid()]));
expect(payload.bcat).to.deep.equal([]);
});

it('video: sends badv from params only — no ortb2/merge (reverted in FS-12411)', function () {
const bidderReq = mockBidderRequest({ ortb2: { badv: ['ortb.com'] } }, [mockVideoBid()]);
const payload = buildAndGetData([mockVideoBid({}, { badv: ['param.com'] })], 0, bidderReq);
expect(payload.badv).to.deep.equal(['param.com']);
});

it('video: defaults badv to an empty array when unset', function () {
const payload = buildAndGetData([mockVideoBid()], 0, mockBidderRequest({}, [mockVideoBid()]));
expect(payload.badv).to.deep.equal([]);
});

describe('blocklist normalization in mergeBlocklist', function () {
describe('bcat normalization in getBlocklist', function () {
let logWarnStub;
beforeEach(function () { logWarnStub = sinon.stub(utils, 'logWarn'); });
afterEach(function () { logWarnStub.restore(); });
Expand Down
Loading