Skip to content

New adapter: OCM - #4652

Open
ocm-ntinos wants to merge 2 commits into
prebid:masterfrom
ocm-ntinos:new-adapter-ocm
Open

ocm-ntinos wants to merge 2 commits into
prebid:masterfrom
ocm-ntinos:new-adapter-ocm

Conversation

@ocm-ntinos

Copy link
Copy Markdown

🔧 Type of changes

  • new bid adapter
  • bid adapter update
  • new feature
  • new analytics adapter
  • new module
  • module update
  • bugfix
  • documentation
  • configuration
  • dependency update
  • tech debt (test coverage, refactorings, etc.)

✨ What's the context?

Adds a bid adapter for Orange Click Media (bidder code ocm, GVL vendor ID 1148). It is the PBS-Java port of the Go adapter in prebid/prebid-server#4982. The docs are in prebid/prebid.github.io#6732.

The OCM endpoint is itself a Prebid Server that holds stored impression configurations. The adapter therefore turns the publisher's params into references that server can resolve:

  • placementId is sent as imp.ext.prebid.storedrequest.id. imp.ext.bidder is removed and other imp.ext members are kept.
  • publisherId becomes the request-level publisher ID on every distribution channel present (site, app, dooh), and publisher.ext.prebid.parentAccount is removed. All impressions in a request must share one publisherId; a mismatch rejects the request as bad input.
  • An optional tmaxBufferMs param (default 250) is deducted from tmax before forwarding. When impressions disagree, the largest buffer wins. The result never exceeds the original tmax, and never drops below the buffer unless the original tmax was already lower. A missing tmax is left as is.

🧠 Rationale behind the change

Because the endpoint is another PBS, the forwarded request is cleaned of data that only the host can act on. The receiving PBS would otherwise act on it a second time under the OCM account, or reject the whole auction:

  • ext.prebid.storedrequest and ext.prebid.profiles were already resolved by the host. Forwarded, they would be looked up again under the OCM account, and an unknown ID fails the auction.
  • ext.prebid.bidderparams holds only OCM's global params, which are already merged into the impressions. Forwarded, the receiving PBS would merge them into every stored impression and add OCM back.
  • ext.prebid.bidadjustmentfactors and bidadjustments are applied by the host. They are keyed by the requested bidder name, which may be an alias the receiving PBS does not know, and an unknown bidder in bidadjustmentfactors fails validation.
  • user.buyeruid (OCM's own user ID) is moved to user.id. Left in buyeruid, the receiving PBS would hand it to every one of its bidders, because a set buyeruid takes precedence there.

The bidder ships disabled (enabled: false). If it were enabled on the OCM server itself, a stored request naming ocm would call itself. Hosts enable it with adapters.ocm.enabled=true.

Responses: the media type comes from bid.mtype. Only when mtype is unset does the adapter fall back to bid.ext.prebid.type, since an upstream PBS passes through whatever its winning adapter set. An unsupported mtype rejects only that bid. Video bids keep duration and primary_category from bid.ext.prebid.video. The response currency is used when present, otherwise USD.

Trade-off: the Java adapter cleans more of the request than the Go one, because PBS-Java core differs (profiles, buyeruid precedence, the global bidder-params merge, and alias/adjustment handling).

🔎 New Bid Adapter Checklist

  • verify email contact works
  • NO fully dynamic hostnames
  • geographic host parameters are NOT required
  • direct use of HTTP is prohibited - implement an existing Bidder interface that will do all the job
  • if the ORTB is just forwarded to the endpoint, use the generic adapter - define the new adapter as the alias of the generic adapter (N/A: the request is rewritten as described above, not just forwarded)
  • cover an adapter configuration with an integration test

🧪 Test plan

  • OcmBidderTest: 64 unit tests covering param validation, the publisher ID mismatch, the stored-request mapping, publisher handling for site, app and dooh, the tmax buffer cases, the host-only data removal, the buyeruid move, media-type resolution (mtype and the fallback), errors on individual bids, currency, video metadata, and no changes to the incoming request.
  • OcmTest: integration test through /openrtb2/auction with WireMock.
  • JaCoCo for OcmBidder: 98.8% of lines and 97.5% of branches covered.
  • Checkstyle: 0 violations. Built and tested on JDK 25.

🏎 Quality check

  • Are your changes following our code style guidelines?
  • Are there any breaking changes in your code?
  • Does your test coverage exceed 90%?
  • Are there any erroneous console logs, debuggers or leftover code in your changes?

The OCM endpoint is itself a Prebid Server that expects `placementId` as a stored request ID.
This adapter modifies the outgoing BidRequest to conform to the OCM exchange's expectations:
*   Propagates `publisherId` to request-level publisher objects.
*   Reduces `tmax` to account for round-trip latency and downstream processing.
*   Moves `user.buyeruid` to `user.id`.
*   Removes host-specific Prebid extensions that would be misinterpreted by the OCM exchange.
The order of `BidRequest` field modifications within the `OcmBidder` was adjusted for consistency. Test JSONs were simplified by removing fields not critical to OCM's core bid request transformation logic, making tests clearer and more focused on relevant parameters.
@ocm-ntinos ocm-ntinos changed the title New adapter OCM New adapter: OCM Oct 13, 2026
* Orange Click Media publisher identifier. It is propagated to the request-level publisher id,
* so every impression in a single request must carry the same value.
*/
@JsonProperty("publisherId")

Copy link
Copy Markdown
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 those are needed.

* not discard the rest of the seat.
*/
@Override
public final Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequest bidRequest) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
public final Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequest bidRequest) {
public Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequest bidRequest) {

return Result.withError(BidderError.badInput(e.getMessage()));
}

tmaxBufferMs = resolveTmaxBufferMs(tmaxBufferMs, extImpOcm.getTmaxBufferMs());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Wondering if we could use the tmax-deduction-ms instead? - https://github.com/prebid/prebid-server-java/blob/master/docs/config-app.md#adapters

Comment on lines +97 to +102
extImpOcm = parseImpExt(imp);
validateImpExt(extImpOcm, imp.getId());
publisherId = resolvePublisherId(publisherId, extImpOcm.getPublisherId(), imp.getId());
} catch (PreBidException e) {
return Result.withError(BidderError.badInput(e.getMessage()));
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just making sure it's expected that any errors during parsing of imp.ext fails whole request not a single impression. Given ```publisherId` requirement it makes sense.

return dooh != null
? dooh.toBuilder().publisher(modifyPublisher(dooh.getPublisher(), publisherId)).build()
: null;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Comment on lines +113 to +115
public void makeHttpRequestsShouldReportPublisherIdFirstWhenBothParamsAreBlank() {
// given
final BidRequest bidRequest = givenBidRequest(givenImp(imp -> imp.id("imp1").ext(givenImpExt(null, null))));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

To be consistent with the test name it should be

Suggested change
public void makeHttpRequestsShouldReportPublisherIdFirstWhenBothParamsAreBlank() {
// given
final BidRequest bidRequest = givenBidRequest(givenImp(imp -> imp.id("imp1").ext(givenImpExt(null, null))));
public void makeHttpRequestsShouldReportPublisherIdFirstWhenBothParamsAreBlank() {
// given
final BidRequest bidRequest = givenBidRequest(givenImp(imp -> imp.id("imp1").ext(givenImpExt(" ", " "))));

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.

3 participants