Skip to content

Optout Bid Adapter: migrate endpoints and batch requests with custom params#14282

Merged
patmmccann merged 31 commits intoprebid:masterfrom
optoutadvertising-com:master
Mar 12, 2026
Merged

Optout Bid Adapter: migrate endpoints and batch requests with custom params#14282
patmmccann merged 31 commits intoprebid:masterfrom
optoutadvertising-com:master

Conversation

@doelwit
Copy link
Copy Markdown
Contributor

@doelwit doelwit commented Dec 18, 2025

Type of change

  • Bugfix

  • Feature

  • New bidder adapter

  • Updated bidder adapter

  • Code style update (formatting, local variables)

  • Refactoring (no functional changes, no api changes)

  • Build related changes

  • CI related changes

  • Does this change affect user-facing APIs or examples documented on http://prebid.org?

  • Other

Description of change

I have checked both new and updated because the old adapter didnt even work anymore because the urls became obsolete. Also added some new features like custom paramters and the single request

Other information

doelwit and others added 6 commits December 18, 2025 11:46
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings December 18, 2025 13:38
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread modules/optoutBidAdapter.js
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the optout bid adapter with significant architectural changes. The adapter migrates from obsolete URLs to new domains (optoutadserving.com/optinadserving.com), implements single-request batching for multiple bids, and adds support for custom parameters and ortb2 data.

Key changes:

  • Switches from per-bid requests to a single batched request for all bids
  • Adds custom parameter normalization with support for arrays, objects, and circular reference handling
  • Implements conditional ortb2 payload inclusion via per-bid flag
  • Updates endpoint URLs to new domain infrastructure

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 20 comments.

File Description
modules/optoutBidAdapter.js Core adapter logic updated to batch requests, normalize custom parameters, handle ortb2 data, and route to new endpoints based on GDPR consent
test/spec/modules/optoutBidAdapter_spec.js Comprehensive test suite rewritten to cover single request batching, custom parameter normalization, GDPR routing logic, and response interpretation

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +44 to +55
const bidRequests = [
{
bidder: 'optout',
params: { adSlot: 'prebid_demo', publisher: '8' },
bidId: 'bidA'
},
'auctionId': 'e97cafd0-ebfc-4f5c-b7c9-baa0fd335a4a'
}];
{
bidder: 'optout',
params: { adSlot: 'testslot2', publisher: '8' },
bidId: 'bidB'
}
];
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

There's no test coverage for the scenario where multiple bid requests have different publisher values. Given that buildRequests now batches all bids into a single request and only uses the publisher from the first bid, this edge case should be tested to verify the expected behavior.

Copilot uses AI. Check for mistakes.
Comment thread modules/optoutBidAdapter.js Outdated
Comment on lines +263 to +321
describe('interpretResponse', function () {
it('maps bids correctly using slot id or requestId', function () {
const bidRequest = {
data: {
slots: [
{ id: 'slotA', requestId: 'bidA' },
{ id: 'slotB', requestId: 'bidB' }
]
}
};

const serverResponse = {
body: {
bids: [
{ requestId: 'bidA', cpm: 1, currency: 'EUR', width: 300, height: 250, ad: '<div/>', ttl: 300, creativeId: 'c1' },
{ requestId: 'slotB', cpm: 2, currency: 'EUR', width: 728, height: 90, ad: '<div/>', ttl: 300, creativeId: 'c2' }
]
}
};

const out = spec.interpretResponse(serverResponse, bidRequest);
expect(out).to.have.lengthOf(2);
expect(out[0].netRevenue).to.equal(true);
});

it('filters bids whose requestId does not map to a sent slot/requestId', function () {
const bidRequest = {
data: {
slots: [{ id: 'slotA', requestId: 'bidA' }]
}
};

const serverResponse = {
body: {
bids: [{ requestId: 'unknown', cpm: 1, currency: 'EUR', width: 300, height: 250, ad: '<div/>', ttl: 300, creativeId: 'c1' }]
}
};

const out = spec.interpretResponse(serverResponse, bidRequest);
expect(out).to.deep.equal([]);
});

it('supports serverResponse.body as an array (fallback behavior)', function () {
const bidRequest = {
data: {
slots: [{ id: 'slotA', requestId: 'bidA' }]
}
};

const serverResponse = {
body: [{ requestId: 'bidA', cpm: '1.2', currency: 'EUR', width: '300', height: '250', ad: '<div/>', ttl: '120', creativeId: 'c1' }]
};

const out = spec.interpretResponse(serverResponse, bidRequest);
expect(out).to.have.lengthOf(1);
expect(out[0].cpm).to.equal(1.2);
expect(out[0].ttl).to.equal(120);
});
});
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

Missing test coverage for the interpretResponse function when the server returns bids with missing or invalid requestId fields. This edge case should be tested to ensure the adapter handles malformed server responses gracefully.

Copilot uses AI. Check for mistakes.
Comment on lines +180 to +192
return {
requestId: prebidRequestId,
cpm: Number(bid.cpm) || 0,
currency: bid.currency,
width: Number(bid.width),
height: Number(bid.height),
ad: bid.ad,
ttl: Number(bid.ttl) || 300,
creativeId: bid.creativeId,
netRevenue: true,
optOutExt: bid.optOutExt,
meta: bid.meta
};
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

The interpretResponse function should validate required bid fields before constructing the bid object. Currently, if bid.currency, bid.ad, bid.width, or bid.height are missing, they'll be undefined in the returned object. Consider adding validation to filter out incomplete bids: if (!bid.cpm || !bid.currency || !bid.ad || !bid.width || !bid.height) return null;

Copilot uses AI. Check for mistakes.
Comment on lines +323 to 377
describe('getUserSyncs', function () {
it('returns [] when gdprConsent missing', function () {
const out = spec.getUserSyncs({ iframeEnabled: true }, [], null);
expect(out).to.deep.equal([]);
});

it('returns [] when iframeEnabled is false', function () {
const out = spec.getUserSyncs(
{ iframeEnabled: false },
[],
{ gdprApplies: false, consentString: 'abc' }
);
expect(out).to.deep.equal([]);
});

it('returns iframe sync when iframeEnabled and gdprApplies is false', function () {
const out = spec.getUserSyncs(
{ iframeEnabled: true },
[],
{ gdprApplies: false, consentString: 'abc' }
);

expect(out).to.have.lengthOf(1);
expect(out[0].type).to.equal('iframe');
expect(out[0].url).to.include('gdpr=0');
expect(out[0].url).to.include('gdpr_consent=' + encodeURIComponent('abc'));
});

it('returns iframe sync when gdprApplies true and purpose1 consent true', function () {
sinon.stub(gdprUtils, 'hasPurpose1Consent').returns(true);

const out = spec.getUserSyncs(
{ iframeEnabled: true },
[],
{ gdprApplies: true, consentString: 'CONSENT' }
);

expect(out).to.have.lengthOf(1);
expect(out[0].type).to.equal('iframe');
expect(out[0].url).to.include('gdpr=1');
expect(out[0].url).to.include('gdpr_consent=' + encodeURIComponent('CONSENT'));
});

it('returns [] when gdprApplies true and purpose1 consent false', function () {
sinon.stub(gdprUtils, 'hasPurpose1Consent').returns(false);

const out = spec.getUserSyncs(
{ iframeEnabled: true },
[],
{ gdprApplies: true, consentString: 'CONSENT' }
);

expect(out).to.deep.equal([]);
});
});
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

Missing test coverage for getUserSyncs when gdprConsent.consentString is undefined or when syncOptions has both iframeEnabled and pixelEnabled set. These scenarios should be tested to ensure the function handles all possible input combinations.

Copilot uses AI. Check for mistakes.
Comment thread modules/optoutBidAdapter.js Outdated
},
}

const addOrtb = validBidRequests.some((f) => !!f?.params?.includeOrtb2);
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

The variable 'addOrtb' is abbreviated, which is inconsistent with the more descriptive naming style used elsewhere in the code (e.g., 'consentString', 'gdprConsent'). Consider renaming to 'includeOrtb2' or 'shouldIncludeOrtb2' for better clarity.

Copilot uses AI. Check for mistakes.
Comment thread modules/optoutBidAdapter.js Outdated
'https://umframe.optinadserving.com/matching/iframe?gdpr=' +
gdpr +
'&gdpr_consent=' +
encodeURIComponent(gdprConsent.consentString || '')
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

The getUserSyncs function accesses gdprConsent.consentString without verifying that gdprConsent is an object. Although there's an early return if !gdprConsent, between lines 198 and 216, gdprConsent could theoretically be a non-object truthy value. While unlikely in practice, add type validation for defensive programming.

Copilot uses AI. Check for mistakes.
describe('optoutAdapterTest', function () {
describe('bidRequestValidity', function () {
it('bidRequest with adslot param', function () {
describe('optoutAdapterTest (new adapter)', function () {
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

The test description says 'optoutAdapterTest (new adapter)' but this is an update to an existing adapter. The description should be more accurate, such as 'optoutAdapterTest' or 'Optout Bid Adapter Test'.

Suggested change
describe('optoutAdapterTest (new adapter)', function () {
describe('Optout Bid Adapter Test', function () {

Copilot uses AI. Check for mistakes.
Comment on lines +68 to 105
it('uses optout endpoint when no gdprConsent', function () {
const requests = spec.buildRequests(bidRequests, {});
requests.forEach(function(requestItem) {
expect(requestItem.url).to.match(new RegExp('adscience-nocookie\\.nl/prebid/display'));
});
expect(requests[0].url).to.match(/optoutadserving\.com\/prebid\/display/);
});

it('uses optin endpoint when gdprApplies is false', function () {
const bidderRequest = {
gdprConsent: {
gdprApplies: false,
consentString: 'test',
apiVersion: 2
}
};

const requests = spec.buildRequests(bidRequests, bidderRequest);
expect(requests[0].url).to.match(/optinadserving\.com\/prebid\/display/);
expect(requests[0].data.gdpr).to.equal(0);
});

it('always includes sdk_version=prebid', function () {
const requests = spec.buildRequests(bidRequests, {});
expect(requests[0].data.sdk_version).to.equal('prebid');
});

it('bidRequest id', function () {
it('currency defaults to EUR when not configured', function () {
config.resetConfig();
const requests = spec.buildRequests(bidRequests, {});
expect(requests[0].data.requestId).to.equal('9304jr394ddfj');
expect(requests[1].data.requestId).to.equal('893j4f94e8jei');
expect(requests[0].data.cur.adServerCurrency).to.equal('EUR');
});

it('bidRequest with config for currency', function () {
it('currency uses config when provided', function () {
config.setConfig({
currency: {
adServerCurrency: 'USD',
granularityMultiplier: 1
}
})
currency: { adServerCurrency: 'USD', granularityMultiplier: 1 }
});

const requests = spec.buildRequests(bidRequests, {});
expect(requests[0].data.cur.adServerCurrency).to.equal('USD');
expect(requests[1].data.cur.adServerCurrency).to.equal('USD');
});
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

Missing test coverage for the scenario when buildRequests is called with different GDPR consent configurations (e.g., when apiVersion is 1 vs 2, or when consentString is an empty string). These edge cases should be tested to ensure the adapter handles various GDPR scenarios correctly.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 8 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread modules/optoutBidAdapter.js Outdated
Comment on lines +137 to +144
const mergedCustoms = Object.assign(
{},
firstBid?.params?.customs,
bidderRequest?.ortb2?.ext?.data,
bidderRequest?.ortb2?.site?.ext?.data,
bidderRequest?.ortb2?.app?.ext?.data,
bidderRequest?.ortb2?.user?.ext?.data
);
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

The first bid's params.customs are being merged into request-level customs (line 139) which will apply to all slots in the batched request, not just the first slot. This means that customs intended for only the first bid will incorrectly be applied to all bids in the batch.

Consider removing firstBid?.params?.customs from the mergedCustoms assignment, since per-bid customs are already handled separately at the slot level (line 125). Request-level customs should only come from ortb2 data sources.

Copilot uses AI. Check for mistakes.
Comment thread modules/optoutBidAdapter.js Outdated
Comment thread modules/optoutBidAdapter.js Outdated
Comment thread modules/optoutBidAdapter.js Outdated
Comment thread modules/optoutBidAdapter.js Outdated
Comment thread modules/optoutBidAdapter.js
const customs = normalizeCustoms(mergedCustoms);

const data = {
publisher: firstBid.params.publisher, // intentionally uses the first bid's publisher when batching
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

When batching multiple bids with different publishers, only the first bid's publisher is used for all slots in the request. This could lead to incorrect attribution or billing when multiple publishers are involved in a single auction.

Consider either: (1) creating separate requests per publisher, or (2) documenting this batching limitation prominently in the adapter documentation, or (3) validating that all bids share the same publisher and returning separate requests otherwise.

Copilot uses AI. Check for mistakes.
doelwit and others added 6 commits December 18, 2025 15:32
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.

Comments suppressed due to low confidence (1)

modules/optoutBidAdapter.js:53

  • This negation always evaluates to false.
  if (!input || typeof input !== 'object') return {};

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread modules/optoutBidAdapter.js Outdated
Comment thread modules/optoutBidAdapter.js Outdated
Comment on lines +131 to +133

const slot = {
adSlot: b.params.adSlot,
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

Critical mismatch between slot construction and response mapping. Line 133 sets slot.adSlot = b.params.adSlot but line 192 in interpretResponse tries to map using s.id. The slot object should set id property with the adSlot value (line 133 should be id: b.params.adSlot || b.params.adslot), otherwise the response mapping will fail because s.id will be undefined for most slots (only defined when params.id is explicitly provided).

Suggested change
const slot = {
adSlot: b.params.adSlot,
const adSlot = b?.params?.adSlot || b?.params?.adslot;
const slot = {
adSlot: adSlot,
id: adSlot,

Copilot uses AI. Check for mistakes.
const customs = normalizeCustoms(mergedCustoms);

const data = {
publisher: firstBid.params.publisher, // intentionally uses the first bid's publisher when batching
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

The comment at line 156 states "intentionally uses the first bid's publisher when batching", but there's no validation that all bids in the batch have the same publisher. This could lead to incorrect behavior when bids from different publishers are batched together, as only the first publisher ID will be used for all slots. Consider either validating that all bids have the same publisher, or creating separate requests per publisher.

Copilot uses AI. Check for mistakes.
Comment thread modules/optoutBidAdapter.js Outdated
const slotCustoms = normalizeCustoms(b?.params?.customs);

const slot = {
adSlot: b.params.adSlot,
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

Property naming inconsistency: the slot object uses 'adSlot' (line 133) while the validator checks for both 'adSlot' and 'adslot' (line 95). This inconsistency should be resolved by standardizing on one property name throughout the adapter. The validator suggests supporting both for backward compatibility, but the buildRequests function only uses params.adSlot.

Copilot uses AI. Check for mistakes.
Comment on lines +144 to +153
const mergedCustoms = Object.assign(
{},
firstBid?.params?.customs,
bidderRequest?.ortb2?.ext?.data,
bidderRequest?.ortb2?.site?.ext?.data,
bidderRequest?.ortb2?.app?.ext?.data,
bidderRequest?.ortb2?.user?.ext?.data
);

const customs = normalizeCustoms(mergedCustoms);
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

The merging logic for customs on lines 144-151 could lead to unexpected behavior. It merges customs from firstBid.params.customs with multiple ortb2 data sources, but then applies normalizeCustoms to the merged result. Since normalizeCustoms expects to work with a clean object and doesn't handle nested merging well, properties from later sources will overwrite earlier ones even if they're of different types (e.g., an array from params could be overwritten by a string from ortb2). Consider documenting this merge precedence or handling the merge more carefully to preserve intended data types.

Copilot uses AI. Check for mistakes.
Comment on lines +137 to +139
if (b.params && b.params.id != null) {
slot.id = String(b.params.id);
}
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

Lines 137-139 set slot.id conditionally from params.id, which conflicts with the expected behavior where slot.id should be the adSlot identifier. This logic suggests params.id is meant to override the slot identifier, but this creates confusion. If params.id is meant to be a different field (like a publisher-specific ID), it should use a different property name. Otherwise, this conditional assignment should be removed once slot.id is properly set to the adSlot value.

Copilot uses AI. Check for mistakes.
Comment thread modules/optoutBidAdapter.js
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@doelwit doelwit marked this pull request as draft December 18, 2025 17:06
@github-actions
Copy link
Copy Markdown

Tread carefully! This PR adds 1 linter error (possibly disabled through directives):

  • modules/optoutBidAdapter.js (+1 error)

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread modules/optoutBidAdapter.js Outdated
Comment thread test/spec/modules/optoutBidAdapter_spec.js Outdated
Comment thread test/spec/modules/optoutBidAdapter_spec.js Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 876b23d8b4

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +226 to +230
const bids = Array.isArray(body)
? body
: Array.isArray(body?.bids)
? body.bids
: [];
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Accept single-bid response bodies

The new response parsing only accepts serverResponse.body when it is an array or when it has a bids array. In the previous adapter, a single bid object returned from the endpoint was passed through as-is and could be handled by Prebid. Now, if the server responds with a single bid object (e.g., {requestId, cpm, ...} for a single-slot request), bids becomes [] and the adapter drops the bid entirely, resulting in no bids returned for that auction.

Useful? React with 👍 / 👎.

doelwit and others added 3 commits January 13, 2026 09:27
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@doelwit
Copy link
Copy Markdown
Contributor Author

doelwit commented Jan 21, 2026

@gwhigs It seems the "duplicate code" check is failing but I dont know why. Are you gonna do the manual review anyway or are you waiting until all tests are succesful? Thanks

@doelwit doelwit requested a review from patmmccann January 28, 2026 11:36
@doelwit
Copy link
Copy Markdown
Contributor Author

doelwit commented Feb 5, 2026

Hello @gwhigs @patmmccann somebody please help ?

@patmmccann
Copy link
Copy Markdown
Collaborator

@codex review

@patmmccann
Copy link
Copy Markdown
Collaborator

Can you pull master? not sure why this duplicate code check is failing to launch

@patmmccann
Copy link
Copy Markdown
Collaborator

@codex can you suggest a better title for this pr

@chatgpt-codex-connector
Copy link
Copy Markdown

Suggested PR title

Optout Bid Adapter: migrate endpoints and batch requests with custom params

Why this is better

  • It follows Prebid’s expected Module: Change format from the PR template. .github/PULL_REQUEST_TEMPLATE.mdL4
  • It also matches the repo-specific guidance to title module changes as X Adapter: short description. AGENTS.mdL16
  • It describes the main scope you outlined (adapter update + batching/custom params), which is clearer than update optout bidadapter.

If you want a shorter variant:

  • Optout Bid Adapter: update endpoints and request batching

Checks/commands I ran

  • ⚠️ curl -I 'https://duckduckgo.com/html/?q=Prebid.js%20pull%20request%20title%20Module:%20Change' | head (web search attempt blocked with HTTP 403 in this environment)
  • nl -ba AGENTS.md | sed -n '1,220p'
  • nl -ba .github/PULL_REQUEST_TEMPLATE.md | sed -n '1,90p'

View task →

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d8b5218c9f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const customs = normalizeCustoms(mergedCustoms);

const data = {
publisher: firstBid.params.publisher, // intentionally uses the first bid's publisher when batching
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve per-slot publisher when batching requests

The batched payload always uses firstBid.params.publisher, so if a page sends multiple optout bids with different params.publisher values, every slot after the first is submitted under the wrong publisher account. The previous implementation sent one request per bid and preserved each publisher, so this introduces a functional regression that can return incorrect demand or misattribute traffic.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

it does not make sense to have multiple publisher ids in one request or on one page/site so this is fine behavior.

if (b.params && b.params.id != null) {
slot.id = String(b.params.id);
} else {
slot.id = adSlotValue;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Prevent duplicate slot IDs from clobbering bid mapping

When params.id is not provided, slot.id falls back to adSlot, but interpretResponse later maps responses with a Map keyed by that id. If two requests share the same adSlot, the later entry overwrites the earlier one, so responses keyed by slot id can be attached to the wrong requestId (or effectively drop one slot). This is a new batching regression because the old per-request flow never had this collision path.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

@doelwit doelwit Mar 9, 2026

Choose a reason for hiding this comment

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

This is fine, it is intentional. If you want to re-use an adslot you need to use the "id" parameter. Typical users do not re-use adslots. Re-using the same adslot however should result in the same bidding parameters so it doesn't matter if they get mixed up.

@patmmccann patmmccann changed the title update optout bidadapter migrate endpoints and batch requests with custom params Feb 23, 2026
@patmmccann patmmccann changed the title migrate endpoints and batch requests with custom params Optout Bid Adapter: migrate endpoints and batch requests with custom params Feb 23, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 061f3910b8

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const customs = normalizeCustoms(mergedCustoms);

const data = {
publisher: firstBid.params.publisher, // intentionally uses the first bid's publisher when batching
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Group batched slots by publisher

The batched payload always uses firstBid.params.publisher, so a single request can silently mix slots from different params.publisher values under the wrong account. This is a regression from the previous per-bid request behavior: if one auction includes two optout bids with different publishers, at least one slot will be sent with an incorrect publisher and can be rejected or misattributed by the endpoint. Please split batched requests by publisher (or enforce/validate a single publisher per batch).

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

it does not make sense to have multiple publisher ids in one request or on one page/site so this is fine behavior.

Comment on lines +235 to +236
sentSlots.map((s) => [String(s.id), String(s.requestId)])
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve one-to-one slot ID mapping in interpretResponse

interpretResponse builds slotIdToPrebidId with a Map keyed by slot.id, but slot.id defaults to adSlot and is not guaranteed unique across bids. When duplicate slot IDs exist and the server responds with slot IDs (the code explicitly supports this path), earlier entries are overwritten and bids are mapped to the wrong requestId, causing missed or misrouted bid responses.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is fine, it is intentional. If you want to re-use an adslot you need to use the "id" parameter. Typical users do not re-use adslots. Re-using the same adslot however should result in the same bidding parameters so it doesn't matter if they get mixed up.

@coveralls
Copy link
Copy Markdown
Collaborator

coveralls commented Mar 5, 2026

Pull Request Test Coverage Report for Build 22892980852

Details

  • 431 of 434 (99.31%) changed or added relevant lines in 2 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.008%) to 96.335%

Changes Missing Coverage Covered Lines Changed/Added Lines %
modules/optoutBidAdapter.js 92 95 96.84%
Totals Coverage Status
Change from base Build 22873923570: 0.008%
Covered Lines: 217908
Relevant Lines: 226198

💛 - Coveralls

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c1ad316e26

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const customs = normalizeCustoms(mergedCustoms);

const data = {
publisher: firstBid.params.publisher, // intentionally uses the first bid's publisher when batching
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve per-slot publisher IDs when batching

This batching logic always sends data.publisher from the first bid, so auctions containing multiple optout bids with different params.publisher values will submit later slots under the wrong publisher account. The previous implementation sent each bid with its own publisher, so this is a behavior regression that can cause mismatched account routing and missed bids; requests should be split by publisher (or include publisher per slot).

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

it does not make sense to have multiple publisher ids in one request or on one page/site so this is fine behavior.

@doelwit
Copy link
Copy Markdown
Contributor Author

doelwit commented Mar 8, 2026

I dont get it, somehow I suddenly had to fix a merge conflict. after this the linter fails on some other bid adapter.. this is getting frustrating.

@patmmccann patmmccann merged commit e512167 into prebid:master Mar 12, 2026
102 checks passed
mike-lei pushed a commit to wishabi/Prebid.js that referenced this pull request Apr 21, 2026
…params (prebid#14282)

* updated optout adapter to include single request, custom params

* Update modules/optoutBidAdapter.js

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update modules/optoutBidAdapter.js

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update modules/optoutBidAdapter.js

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* changes for copilot review

* more fixes for copilot review

* Update modules/optoutBidAdapter.js

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update modules/optoutBidAdapter.js

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update modules/optoutBidAdapter.js

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update modules/optoutBidAdapter.js

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update modules/optoutBidAdapter.js

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update modules/optoutBidAdapter.js

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update modules/optoutBidAdapter.js

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* add more test coverage and improvements to adapter

* fix errors

* remove duplicate code/refactored code

* fixed test case with invalid url

* fixed test case with invalid url attempt 2

* remove invalid url check altogether

* remove trailing spaces

* remove trailing spaces

* Update modules/optoutBidAdapter.js

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update test/spec/modules/optoutBidAdapter_spec.js

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update test/spec/modules/optoutBidAdapter_spec.js

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants