From 3c2557388c9b5fc6dbd73cbe5c0843a1bba2c44d Mon Sep 17 00:00:00 2001 From: Rafal Sieczka Date: Thu, 27 Aug 2026 11:26:13 +0200 Subject: [PATCH 01/13] Make single request for multiple imps regardless of their type --- .../server/bidder/openx/OpenxBidder.java | 38 +++++++------------ 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java b/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java index 6483b491a61..770ae18c0e3 100644 --- a/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java +++ b/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java @@ -68,18 +68,17 @@ public OpenxBidder(String endpointUrl, JacksonMapper mapper) { @Override public Result>> makeHttpRequests(BidRequest bidRequest) { - final Map> differentiatedImps = bidRequest.getImp().stream() - .collect(Collectors.groupingBy(OpenxBidder::resolveImpType)); + final Map> partitionedImps = bidRequest.getImp().stream() + .filter(Objects::nonNull) + .collect(Collectors.partitioningBy(OpenxBidder::isSupportedImpType)); final List processingErrors = new ArrayList<>(); final List outgoingRequests = makeRequests( bidRequest, - differentiatedImps.get(OpenxImpType.banner), - differentiatedImps.get(OpenxImpType.video), - differentiatedImps.get(OpenxImpType.xNative), + partitionedImps.get(Boolean.TRUE), processingErrors); - final List errors = errors(differentiatedImps.get(OpenxImpType.other), processingErrors); + final List errors = errors(partitionedImps.get(Boolean.FALSE), processingErrors); return Result.of(createHttpRequests(outgoingRequests), errors); } @@ -96,32 +95,21 @@ public Result> makeBids(BidderCall httpCall, BidRequ private List makeRequests( BidRequest bidRequest, - List bannerImps, - List videoImps, - List nativeImps, + List supportedImps, List errors) { final List bidRequests = new ArrayList<>(); - // single request for all banner and native imps - final List bannerAndNativeImps = Stream.of(bannerImps, nativeImps) - .filter(Objects::nonNull) - .flatMap(Collection::stream) - .toList(); - final BidRequest bannerAndNativeImpsRequest = createSingleRequest(bannerAndNativeImps, bidRequest, errors); - if (bannerAndNativeImpsRequest != null) { - bidRequests.add(bannerAndNativeImpsRequest); - } - if (CollectionUtils.isNotEmpty(videoImps)) { - // single request for each video imp - bidRequests.addAll(videoImps.stream() - .map(Collections::singletonList) - .map(imps -> createSingleRequest(imps, bidRequest, errors)) - .filter(Objects::nonNull) - .toList()); + final BidRequest request = createSingleRequest(supportedImps, bidRequest, errors); + if (request != null) { + bidRequests.add(request); } return bidRequests; } + private static boolean isSupportedImpType(Imp imp) { + return imp.getBanner() != null || imp.getVideo() != null || imp.getXNative() != null; + } + private static OpenxImpType resolveImpType(Imp imp) { if (imp.getBanner() != null) { return OpenxImpType.banner; From 69624cf13195ccf39e33d735a1780f9de770a912 Mon Sep 17 00:00:00 2001 From: Rafal Sieczka Date: Thu, 27 Aug 2026 11:26:21 +0200 Subject: [PATCH 02/13] Fix test --- .../server/bidder/openx/OpenxBidderTest.java | 121 +----------------- 1 file changed, 3 insertions(+), 118 deletions(-) diff --git a/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java b/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java index f5ba6094c17..a809a13ce6f 100644 --- a/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java @@ -233,10 +233,9 @@ public void makeHttpRequestsShouldReturnResultWithExpectedFieldsSet() { .containsExactly(BidderError.badInput( "OpenX only supports banner, video and native imps. Ignoring imp id=impId1")); - assertThat(result.getValue()).hasSize(3) + assertThat(result.getValue()).hasSize(1) .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class)) .containsExactly( - // check if all banner imps are part of single bidRequest BidRequest.builder() .id("bidRequestId") .imp(asList( @@ -262,19 +261,7 @@ public void makeHttpRequestsShouldReturnResultWithExpectedFieldsSet() { .customParams( givenCustomParams("foo2", "bar2")) .build())) - .build())) - .ext(jacksonMapper.fillExtension( - ExtRequest.empty(), - OpenxRequestExt.of("se-demo-d.openx.net", null, "hb_pbs_1.0.0"))) - .user(User.builder() - .ext(ExtUser.builder().consent("consent").build()) - .build()) - .regs(Regs.builder().coppa(0).ext(ExtRegs.of(1, null, null, null)).build()) - .build(), - // check if each of video imps is a part of separate bidRequest and impId3 is rewarded video - BidRequest.builder() - .id("bidRequestId") - .imp(singletonList( + .build(), Imp.builder() .id("impId3") .video(Video.builder() @@ -288,20 +275,7 @@ public void makeHttpRequestsShouldReturnResultWithExpectedFieldsSet() { .customParams( givenCustomParams("foo3", "bar3")) .build())) - .build())) - - .ext(jacksonMapper.fillExtension( - ExtRequest.empty(), - OpenxRequestExt.of("se-demo-d.openx.net", null, "hb_pbs_1.0.0"))) - .user(User.builder() - .ext(ExtUser.builder().consent("consent").build()) - .build()) - .regs(Regs.builder().coppa(0).ext(ExtRegs.of(1, null, null, null)).build()) - .build(), - // check if each of video imps is a part of separate bidRequest - BidRequest.builder() - .id("bidRequestId") - .imp(singletonList( + .build(), Imp.builder() .id("impId4") .video(Video.builder().build()) @@ -312,95 +286,6 @@ public void makeHttpRequestsShouldReturnResultWithExpectedFieldsSet() { givenCustomParams("foo4", "bar4")) .build())) .build())) - .ext(jacksonMapper.fillExtension( - ExtRequest.empty(), OpenxRequestExt.of(null, "PLATFORM", "hb_pbs_1.0.0"))) - .user(User.builder() - .ext(ExtUser.builder().consent("consent").build()) - .build()) - .regs(Regs.builder().coppa(0).ext(ExtRegs.of(1, null, null, null)).build()) - .build()); - } - - @Test - public void makeHttpRequestsShouldReturnResultWithSingleBidRequestForMultipleBannerAndNativeImps() { - // given - final BidRequest bidRequest = BidRequest.builder() - .id("bidRequestId") - .imp(asList( - Imp.builder() - .id("impId4") - .banner(Banner.builder().build()) - .ext(mapper.valueToTree( - ExtPrebid.of(null, - ExtImpOpenx.builder() - .customParams(givenCustomParams("foo4", "bar4")) - .delDomain("se-demo-d.openx.net") - .unit("4").build()))).build(), - Imp.builder() - .id("impId5") - .xNative(Native.builder().request("{\"testreq\":1}").build()) - .ext(mapper.valueToTree( - ExtPrebid.of(null, - ExtImpOpenx.builder() - .customParams(givenCustomParams("foo5", "bar5")) - .delDomain("se-demo-d.openx.net") - .unit("5").build()))).build(), - Imp.builder() - .id("impId6") - .xNative(Native.builder().build()) - .ext(mapper.valueToTree( - ExtPrebid.of(null, - ExtImpOpenx.builder() - .customParams(givenCustomParams("foo6", "bar6")) - .delDomain("se-demo-d.openx.net") - .unit("6").build()))).build())) - .user(User.builder().ext(ExtUser.builder().consent("consent").build()).build()) - .regs(Regs.builder().coppa(0).ext(ExtRegs.of(1, null, null, null)).build()) - .build(); - - // when - final Result>> result = target.makeHttpRequests(bidRequest); - - // then - assertThat(result.getErrors()).isEmpty(); - - assertThat(result.getValue()).hasSize(1) - .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class)) - .containsExactly( - // check if all native and banner imps are part of single bidRequest - BidRequest.builder() - .id("bidRequestId") - .imp(asList( - Imp.builder() - .id("impId4") - .tagid("4") - .banner(Banner.builder().build()) - .ext(mapper.valueToTree( - ExtImpOpenx.builder() - .customParams( - givenCustomParams("foo4", "bar4")) - .build())) - .build(), - Imp.builder() - .id("impId5") - .tagid("5") - .xNative(Native.builder().request("{\"testreq\":1}").build()) - .ext(mapper.valueToTree( - ExtImpOpenx.builder() - .customParams( - givenCustomParams("foo5", "bar5")) - .build())) - .build(), - Imp.builder() - .id("impId6") - .tagid("6") - .xNative(Native.builder().build()) - .ext(mapper.valueToTree( - ExtImpOpenx.builder() - .customParams( - givenCustomParams("foo6", "bar6")) - .build())) - .build())) .ext(jacksonMapper.fillExtension( ExtRequest.empty(), OpenxRequestExt.of("se-demo-d.openx.net", null, "hb_pbs_1.0.0"))) From af5bef316b6059eaa324b3b7563c78bf114be839 Mon Sep 17 00:00:00 2001 From: Rafal Sieczka Date: Thu, 27 Aug 2026 12:53:09 +0200 Subject: [PATCH 03/13] Process imps in for loop --- .../server/bidder/openx/OpenxBidder.java | 38 ++++++++----------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java b/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java index 770ae18c0e3..fcdce673936 100644 --- a/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java +++ b/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java @@ -16,7 +16,6 @@ import org.prebid.server.bidder.model.BidderError; import org.prebid.server.bidder.model.HttpRequest; import org.prebid.server.bidder.model.Result; -import org.prebid.server.bidder.openx.model.OpenxImpType; import org.prebid.server.bidder.openx.proto.OpenxBidExt; import org.prebid.server.bidder.openx.proto.OpenxRequestExt; import org.prebid.server.bidder.openx.proto.OpenxVideoExt; @@ -43,7 +42,6 @@ import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; public class OpenxBidder implements Bidder { @@ -107,20 +105,7 @@ private List makeRequests( } private static boolean isSupportedImpType(Imp imp) { - return imp.getBanner() != null || imp.getVideo() != null || imp.getXNative() != null; - } - - private static OpenxImpType resolveImpType(Imp imp) { - if (imp.getBanner() != null) { - return OpenxImpType.banner; - } - if (imp.getVideo() != null) { - return OpenxImpType.video; - } - if (imp.getXNative() != null) { - return OpenxImpType.xNative; - } - return OpenxImpType.other; + return imp.getBanner() != null || imp.getVideo() != null || imp.getXNative() != null; } private static BidType resolveBidType(Imp imp) { @@ -166,17 +151,24 @@ private BidRequest createSingleRequest(List imps, BidRequest bidRequest, Li return null; } - List processedImps = null; - try { - processedImps = imps.stream().map(this::makeImp).toList(); - } catch (PreBidException e) { - errors.add(BidderError.badInput(e.getMessage())); + final List processedImps = new ArrayList<>(); + ExtRequest requestExt = null; + for (Imp imp : imps) { + try { + processedImps.add(makeImp(imp)); + // the first successfully parsed imp's delDomain/platform win; other imps' values are ignored + if (requestExt == null) { + requestExt = makeReqExt(imp); + } + } catch (PreBidException e) { + errors.add(BidderError.badInput(e.getMessage())); + } } return CollectionUtils.isNotEmpty(processedImps) ? bidRequest.toBuilder() .imp(processedImps) - .ext(makeReqExt(imps.getFirst())) + .ext(requestExt) .build() : null; } @@ -190,7 +182,7 @@ private Imp makeImp(Imp imp) { .bidfloor(resolveBidFloor(imp.getBidfloor(), openxImpExt.getCustomFloor())) .ext(makeImpExt(imp.getExt(), MapUtils.isNotEmpty(openxImpExt.getCustomParams()))); - if (resolveImpType(imp) == OpenxImpType.video + if (imp.getVideo() != null && prebidImpExt != null && Objects.equals(prebidImpExt.getIsRewardedInventory(), 1)) { impBuilder.video(imp.getVideo().toBuilder() From 0a4c3318b3ed771af3637320e0487477e5ea6721 Mon Sep 17 00:00:00 2001 From: Rafal Sieczka Date: Thu, 27 Aug 2026 12:53:21 +0200 Subject: [PATCH 04/13] Remove unused enum --- .../prebid/server/bidder/openx/model/OpenxImpType.java | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 src/main/java/org/prebid/server/bidder/openx/model/OpenxImpType.java diff --git a/src/main/java/org/prebid/server/bidder/openx/model/OpenxImpType.java b/src/main/java/org/prebid/server/bidder/openx/model/OpenxImpType.java deleted file mode 100644 index c872e7f97e6..00000000000 --- a/src/main/java/org/prebid/server/bidder/openx/model/OpenxImpType.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.prebid.server.bidder.openx.model; - -public enum OpenxImpType { - - // supported - banner, video, xNative, - // not supported - other -} From 512015a7a15f1573b1f5a7590a5c10a380dccdff Mon Sep 17 00:00:00 2001 From: Rafal Sieczka Date: Thu, 27 Aug 2026 12:53:47 +0200 Subject: [PATCH 05/13] Add test case for getting ext from first valid imp --- .../server/bidder/openx/OpenxBidderTest.java | 173 +++++++++++++++++- 1 file changed, 172 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java b/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java index a809a13ce6f..ffbc45f87b6 100644 --- a/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java @@ -268,7 +268,6 @@ public void makeHttpRequestsShouldReturnResultWithExpectedFieldsSet() { .ext(mapper.valueToTree(OpenxVideoExt.of(1))) .build()) .tagid("555555") - // check if each of video imps is a part of separate bidRequest .bidfloor(BigDecimal.valueOf(0.1)) .ext(mapper.valueToTree( ExtImpOpenx.builder() @@ -358,6 +357,178 @@ public void makeHttpRequestsShouldReturnResultWithSingleBidRequestForMultiFormat .build()); } + @Test + public void makeHttpRequestsShouldSkipMalformedFirstImpAndDeriveRequestExtFromLaterValidImp() { + // given + final BidRequest bidRequest = BidRequest.builder() + .id("bidRequestId") + .imp(asList( + Imp.builder() + .id("badImp") + .banner(Banner.builder().build()) + .build(), + Imp.builder() + .id("anotherBadImp") + .banner(Banner.builder().build()) + .build(), + Imp.builder() + .id("goodImp") + .banner(Banner.builder().build()) + .ext(mapper.valueToTree( + ExtPrebid.of(null, + ExtImpOpenx.builder() + .delDomain("se-demo-d.openx.net") + .platform("PLATFORM") + .unit("555555").build()))) + .build())) + .build(); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).hasSize(2) + .containsExactly( + BidderError.badInput("openx parameters section is missing"), + BidderError.badInput("openx parameters section is missing")); + + assertThat(result.getValue()).hasSize(1) + .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class)) + .containsExactly( + BidRequest.builder() + .id("bidRequestId") + .imp(singletonList( + Imp.builder() + .id("goodImp") + .banner(Banner.builder().build()) + .tagid("555555") + .ext(mapper.valueToTree(ExtImpOpenx.builder().build())) + .build())) + .ext(jacksonMapper.fillExtension( + ExtRequest.empty(), + OpenxRequestExt.of("se-demo-d.openx.net", "PLATFORM", "hb_pbs_1.0.0"))) + .build()); + } + + @Test + public void makeHttpRequestsShouldReturnResultWithSingleBidRequestForMultipleNativeImpsAndVideoImp() { + // given + final BidRequest bidRequest = BidRequest.builder() + .id("bidRequestId") + .imp(asList( + Imp.builder() + .id("impId1") + .xNative(Native.builder().request("{\"version\":1}").build()) + .ext(mapper.valueToTree( + ExtPrebid.of(null, ExtImpOpenx.builder().unit("1").build()))) + .build(), + Imp.builder() + .id("impId2") + .xNative(Native.builder().request("{\"version\":2}").build()) + .ext(mapper.valueToTree( + ExtPrebid.of(null, ExtImpOpenx.builder().unit("2").build()))) + .build(), + Imp.builder() + .id("impId3") + .video(Video.builder().maxduration(10).build()) + .ext(mapper.valueToTree( + ExtPrebid.of(null, ExtImpOpenx.builder().unit("3").build()))) + .build())) + .build(); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1) + .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class)) + .extracting(BidRequest::getImp) + .containsExactly(asList( + Imp.builder() + .id("impId1") + .tagid("1") + .xNative(Native.builder().request("{\"version\":1}").build()) + .ext(mapper.valueToTree(ExtImpOpenx.builder().build())) + .build(), + Imp.builder() + .id("impId2") + .tagid("2") + .xNative(Native.builder().request("{\"version\":2}").build()) + .ext(mapper.valueToTree(ExtImpOpenx.builder().build())) + .build(), + Imp.builder() + .id("impId3") + .tagid("3") + .video(Video.builder().maxduration(10).build()) + .ext(mapper.valueToTree(ExtImpOpenx.builder().build())) + .build())); + } + + @Test + public void makeHttpRequestsShouldAttachRewardedVideoExtWhenImpHasBothBannerAndVideo() { + // given + final BidRequest bidRequest = BidRequest.builder() + .id("bidRequestId") + .imp(singletonList(Imp.builder() + .id("impId1") + .banner(Banner.builder().build()) + .video(Video.builder().build()) + .ext(mapper.valueToTree( + ExtPrebid.of( + ExtImpPrebid.builder().isRewardedInventory(1).build(), + ExtImpOpenx.builder().unit("1").build()))) + .build())) + .build(); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1) + .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class)) + .flatExtracting(BidRequest::getImp) + .containsExactly(Imp.builder() + .id("impId1") + .tagid("1") + .banner(Banner.builder().build()) + .video(Video.builder().ext(mapper.valueToTree(OpenxVideoExt.of(1))).build()) + .ext(mapper.valueToTree(ExtImpOpenx.builder().build())) + .build()); + } + + @Test + public void makeHttpRequestsShouldNotAttachRewardedVideoExtWhenImpHasNoVideo() { + // given + final BidRequest bidRequest = BidRequest.builder() + .id("bidRequestId") + .imp(singletonList(Imp.builder() + .id("impId1") + .banner(Banner.builder().build()) + .ext(mapper.valueToTree( + ExtPrebid.of( + ExtImpPrebid.builder().isRewardedInventory(1).build(), + ExtImpOpenx.builder().unit("1").build()))) + .build())) + .build(); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1) + .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class)) + .flatExtracting(BidRequest::getImp) + .containsExactly(Imp.builder() + .id("impId1") + .tagid("1") + .banner(Banner.builder().build()) + .ext(mapper.valueToTree(ExtImpOpenx.builder().build())) + .build()); + } + @Test public void makeHttpRequestsShouldPassThroughImpExt() { // given From 69ca46601fcf873e049fa63d8cfb4ff250a825a2 Mon Sep 17 00:00:00 2001 From: Rafal Sieczka Date: Thu, 27 Aug 2026 13:40:00 +0200 Subject: [PATCH 06/13] Add imp id to error message and compute imp ext only once per imp --- .../org/prebid/server/bidder/openx/OpenxBidder.java | 13 ++++++------- .../prebid/server/bidder/openx/OpenxBidderTest.java | 12 ++++++------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java b/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java index fcdce673936..90af6bf7ff3 100644 --- a/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java +++ b/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java @@ -155,13 +155,14 @@ private BidRequest createSingleRequest(List imps, BidRequest bidRequest, Li ExtRequest requestExt = null; for (Imp imp : imps) { try { - processedImps.add(makeImp(imp)); + final ExtPrebid impExt = parseOpenxExt(imp); + processedImps.add(makeImp(imp, impExt)); // the first successfully parsed imp's delDomain/platform win; other imps' values are ignored if (requestExt == null) { - requestExt = makeReqExt(imp); + requestExt = makeReqExt(impExt.getBidder()); } } catch (PreBidException e) { - errors.add(BidderError.badInput(e.getMessage())); + errors.add(BidderError.badInput("imp id=" + imp.getId() + ": " + e.getMessage())); } } @@ -173,8 +174,7 @@ private BidRequest createSingleRequest(List imps, BidRequest bidRequest, Li : null; } - private Imp makeImp(Imp imp) { - final ExtPrebid impExt = parseOpenxExt(imp); + private Imp makeImp(Imp imp, ExtPrebid impExt) { final ExtImpOpenx openxImpExt = impExt.getBidder(); final ExtImpPrebid prebidImpExt = impExt.getPrebid(); final Imp.ImpBuilder impBuilder = imp.toBuilder() @@ -198,8 +198,7 @@ private static BigDecimal resolveBidFloor(BigDecimal impBidFloor, BigDecimal cus : impBidFloor; } - private ExtRequest makeReqExt(Imp imp) { - final ExtImpOpenx openxImpExt = parseOpenxExt(imp).getBidder(); + private ExtRequest makeReqExt(ExtImpOpenx openxImpExt) { return mapper.fillExtension( ExtRequest.empty(), OpenxRequestExt.of(openxImpExt.getDelDomain(), openxImpExt.getPlatform(), OPENX_CONFIG)); diff --git a/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java b/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java index ffbc45f87b6..10a209c8414 100644 --- a/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java @@ -109,7 +109,7 @@ public void makeHttpRequestsShouldReturnResultWithErrorWhenImpExtOmitted() { // then assertThat(result.getValue()).isEmpty(); assertThat(result.getErrors()).hasSize(1) - .containsExactly(BidderError.badInput("openx parameters section is missing")); + .containsExactly(BidderError.badInput("imp id=null: openx parameters section is missing")); } @Test @@ -128,7 +128,7 @@ public void makeHttpRequestsShouldReturnResultWithErrorWhenImpExtMalformed() { // then assertThat(result.getValue()).isEmpty(); assertThat(result.getErrors()).hasSize(1) - .containsExactly(BidderError.badInput("openx parameters section is missing")); + .containsExactly(BidderError.badInput("imp id=null: openx parameters section is missing")); } @Test @@ -148,7 +148,7 @@ public void makeHttpRequestsShouldReturnResultWithErrorWhenImpExtOpenxEmpty() { // then assertThat(result.getValue()).isEmpty(); assertThat(result.getErrors()).hasSize(1) - .containsExactly(BidderError.badInput("openx parameters section is missing")); + .containsExactly(BidderError.badInput("imp id=null: openx parameters section is missing")); } @Test @@ -167,7 +167,7 @@ public void makeHttpRequestsShouldReturnResultWithErrorWhenImpExtOpenxMalformed( // then assertThat(result.getValue()).isEmpty(); assertThat(result.getErrors().getFirst().getMessage()) - .startsWith("Cannot deserialize value of"); + .startsWith("imp id=null: Cannot deserialize value of"); } @Test @@ -389,8 +389,8 @@ public void makeHttpRequestsShouldSkipMalformedFirstImpAndDeriveRequestExtFromLa // then assertThat(result.getErrors()).hasSize(2) .containsExactly( - BidderError.badInput("openx parameters section is missing"), - BidderError.badInput("openx parameters section is missing")); + BidderError.badInput("imp id=badImp: openx parameters section is missing"), + BidderError.badInput("imp id=anotherBadImp: openx parameters section is missing")); assertThat(result.getValue()).hasSize(1) .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class)) From 2d9acebe29e10596aeaa4889a0f890720fa5dcf6 Mon Sep 17 00:00:00 2001 From: Rafal Sieczka Date: Thu, 27 Aug 2026 13:44:36 +0200 Subject: [PATCH 07/13] Move method higher --- .../java/org/prebid/server/bidder/openx/OpenxBidder.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java b/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java index 90af6bf7ff3..f27c2a1eec9 100644 --- a/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java +++ b/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java @@ -91,6 +91,10 @@ public Result> makeBids(BidderCall httpCall, BidRequ } } + private static boolean isSupportedImpType(Imp imp) { + return imp.getBanner() != null || imp.getVideo() != null || imp.getXNative() != null; + } + private List makeRequests( BidRequest bidRequest, List supportedImps, @@ -104,10 +108,6 @@ private List makeRequests( return bidRequests; } - private static boolean isSupportedImpType(Imp imp) { - return imp.getBanner() != null || imp.getVideo() != null || imp.getXNative() != null; - } - private static BidType resolveBidType(Imp imp) { if (imp.getBanner() != null) { return BidType.banner; From 6a21883c776f74c058d1afee3a9e9363bdee003f Mon Sep 17 00:00:00 2001 From: Rafal Sieczka Date: Thu, 27 Aug 2026 13:45:28 +0200 Subject: [PATCH 08/13] Rename argument --- src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java b/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java index f27c2a1eec9..cdeb750824f 100644 --- a/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java +++ b/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java @@ -97,11 +97,11 @@ private static boolean isSupportedImpType(Imp imp) { private List makeRequests( BidRequest bidRequest, - List supportedImps, + List imps, List errors) { final List bidRequests = new ArrayList<>(); - final BidRequest request = createSingleRequest(supportedImps, bidRequest, errors); + final BidRequest request = createSingleRequest(imps, bidRequest, errors); if (request != null) { bidRequests.add(request); } From a75aafd2409356bca1a457786c0c9877e5bb1d51 Mon Sep 17 00:00:00 2001 From: Rafal Sieczka Date: Thu, 27 Aug 2026 13:48:08 +0200 Subject: [PATCH 09/13] Remove comment --- src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java b/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java index cdeb750824f..2aa90032057 100644 --- a/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java +++ b/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java @@ -157,7 +157,7 @@ private BidRequest createSingleRequest(List imps, BidRequest bidRequest, Li try { final ExtPrebid impExt = parseOpenxExt(imp); processedImps.add(makeImp(imp, impExt)); - // the first successfully parsed imp's delDomain/platform win; other imps' values are ignored + if (requestExt == null) { requestExt = makeReqExt(impExt.getBidder()); } From 0cb24424986737d977711a2d528d84fd0299773f Mon Sep 17 00:00:00 2001 From: Rafal Sieczka Date: Thu, 27 Aug 2026 13:58:46 +0200 Subject: [PATCH 10/13] Make test assert case for multi imp request --- .../server/bidder/openx/OpenxBidderTest.java | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java b/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java index 10a209c8414..3c9c29fb76c 100644 --- a/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java @@ -296,7 +296,7 @@ public void makeHttpRequestsShouldReturnResultWithExpectedFieldsSet() { } @Test - public void makeHttpRequestsShouldReturnResultWithSingleBidRequestForMultiFormatImps() { + public void makeHttpRequestsShouldReturnResultWithSingleBidRequestForMultipleImpsWithDifferentFormat() { // given final BidRequest bidRequest = BidRequest.builder() .id("bidRequestId") @@ -304,16 +304,20 @@ public void makeHttpRequestsShouldReturnResultWithSingleBidRequestForMultiFormat Imp.builder() .id("impId1") .banner(Banner.builder().w(320).h(200).build()) - .video(Video.builder().maxduration(10).build()) .ext(mapper.valueToTree( ExtPrebid.of(null, ExtImpOpenx.builder().unit("1").build()))) .build(), Imp.builder() .id("impId2") - .banner(Banner.builder().w(300).h(150).build()) .xNative(Native.builder().request("{\"version\":1}").build()) .ext(mapper.valueToTree( ExtPrebid.of(null, ExtImpOpenx.builder().unit("2").build()))) + .build(), + Imp.builder() + .id("impId3") + .video(Video.builder().maxduration(10).build()) + .ext(mapper.valueToTree( + ExtPrebid.of(null, ExtImpOpenx.builder().unit("3").build()))) .build())) .user(User.builder().ext(ExtUser.builder().consent("consent").build()).build()) .regs(Regs.builder().coppa(0).ext(ExtRegs.of(1, null, null, null)).build()) @@ -328,24 +332,25 @@ public void makeHttpRequestsShouldReturnResultWithSingleBidRequestForMultiFormat assertThat(result.getValue()).hasSize(1) .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class)) .containsExactly( - // check if all native and banner imps are part of single bidRequest BidRequest.builder() .id("bidRequestId") .imp(asList( - // verify banner and video media types are preserved in a single imp Imp.builder() .id("impId1") .tagid("1") .banner(Banner.builder().w(320).h(200).build()) - .video(Video.builder().maxduration(10).build()) .ext(mapper.valueToTree(ExtImpOpenx.builder().build())).build(), - // verify banner and native media types are preserved in a single imp Imp.builder() .id("impId2") .tagid("2") - .banner(Banner.builder().w(300).h(150).build()) .xNative(Native.builder().request("{\"version\":1}").build()) .ext(mapper.valueToTree(ExtImpOpenx.builder().build())) + .build(), + Imp.builder() + .id("impId3") + .tagid("3") + .video(Video.builder().maxduration(10).build()) + .ext(mapper.valueToTree(ExtImpOpenx.builder().build())) .build())) .ext(jacksonMapper.fillExtension( ExtRequest.empty(), From 3d7b00d292f674538bff9e860d87881265c2eecb Mon Sep 17 00:00:00 2001 From: Rafal Sieczka Date: Thu, 27 Aug 2026 14:00:26 +0200 Subject: [PATCH 11/13] Remove not needed test --- .../server/bidder/openx/OpenxBidderTest.java | 55 ------------------- 1 file changed, 55 deletions(-) diff --git a/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java b/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java index 3c9c29fb76c..1c3e201adcb 100644 --- a/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java @@ -415,61 +415,6 @@ public void makeHttpRequestsShouldSkipMalformedFirstImpAndDeriveRequestExtFromLa .build()); } - @Test - public void makeHttpRequestsShouldReturnResultWithSingleBidRequestForMultipleNativeImpsAndVideoImp() { - // given - final BidRequest bidRequest = BidRequest.builder() - .id("bidRequestId") - .imp(asList( - Imp.builder() - .id("impId1") - .xNative(Native.builder().request("{\"version\":1}").build()) - .ext(mapper.valueToTree( - ExtPrebid.of(null, ExtImpOpenx.builder().unit("1").build()))) - .build(), - Imp.builder() - .id("impId2") - .xNative(Native.builder().request("{\"version\":2}").build()) - .ext(mapper.valueToTree( - ExtPrebid.of(null, ExtImpOpenx.builder().unit("2").build()))) - .build(), - Imp.builder() - .id("impId3") - .video(Video.builder().maxduration(10).build()) - .ext(mapper.valueToTree( - ExtPrebid.of(null, ExtImpOpenx.builder().unit("3").build()))) - .build())) - .build(); - - // when - final Result>> result = target.makeHttpRequests(bidRequest); - - // then - assertThat(result.getErrors()).isEmpty(); - assertThat(result.getValue()).hasSize(1) - .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class)) - .extracting(BidRequest::getImp) - .containsExactly(asList( - Imp.builder() - .id("impId1") - .tagid("1") - .xNative(Native.builder().request("{\"version\":1}").build()) - .ext(mapper.valueToTree(ExtImpOpenx.builder().build())) - .build(), - Imp.builder() - .id("impId2") - .tagid("2") - .xNative(Native.builder().request("{\"version\":2}").build()) - .ext(mapper.valueToTree(ExtImpOpenx.builder().build())) - .build(), - Imp.builder() - .id("impId3") - .tagid("3") - .video(Video.builder().maxduration(10).build()) - .ext(mapper.valueToTree(ExtImpOpenx.builder().build())) - .build())); - } - @Test public void makeHttpRequestsShouldAttachRewardedVideoExtWhenImpHasBothBannerAndVideo() { // given From 77b4afba517b010bd0ba58afb9c2e6457dc53da3 Mon Sep 17 00:00:00 2001 From: Rafal Sieczka Date: Thu, 27 Aug 2026 14:18:45 +0200 Subject: [PATCH 12/13] Use singletonList and emptyList and formatted for error message --- .../org/prebid/server/bidder/openx/OpenxBidder.java | 12 +++++++----- .../prebid/server/bidder/openx/OpenxBidderTest.java | 12 ++++++++---- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java b/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java index 2aa90032057..1d5699cbfd9 100644 --- a/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java +++ b/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java @@ -1,5 +1,8 @@ package org.prebid.server.bidder.openx; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; + import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; @@ -99,13 +102,12 @@ private List makeRequests( BidRequest bidRequest, List imps, List errors) { - final List bidRequests = new ArrayList<>(); final BidRequest request = createSingleRequest(imps, bidRequest, errors); if (request != null) { - bidRequests.add(request); + return singletonList(request); } - return bidRequests; + return emptyList(); } private static BidType resolveBidType(Imp imp) { @@ -162,7 +164,7 @@ private BidRequest createSingleRequest(List imps, BidRequest bidRequest, Li requestExt = makeReqExt(impExt.getBidder()); } } catch (PreBidException e) { - errors.add(BidderError.badInput("imp id=" + imp.getId() + ": " + e.getMessage())); + errors.add(BidderError.badInput("imp id=%s: %s".formatted(imp.getId(), e.getMessage()))); } } @@ -235,7 +237,7 @@ private ObjectNode makeImpExt(ObjectNode impExt, boolean addCustomParams) { private List extractBids(BidRequest bidRequest, BidResponse bidResponse) { return bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid()) - ? Collections.emptyList() + ? emptyList() : bidsFromResponse(bidRequest, bidResponse); } diff --git a/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java b/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java index 1c3e201adcb..74888aa1d68 100644 --- a/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java @@ -99,6 +99,7 @@ public void makeHttpRequestsShouldReturnResultWithErrorWhenImpExtOmitted() { // given final BidRequest bidRequest = BidRequest.builder() .imp(singletonList(Imp.builder() + .id("impId1") .banner(Banner.builder().build()) .build())) .build(); @@ -109,7 +110,7 @@ public void makeHttpRequestsShouldReturnResultWithErrorWhenImpExtOmitted() { // then assertThat(result.getValue()).isEmpty(); assertThat(result.getErrors()).hasSize(1) - .containsExactly(BidderError.badInput("imp id=null: openx parameters section is missing")); + .containsExactly(BidderError.badInput("imp id=impId1: openx parameters section is missing")); } @Test @@ -117,6 +118,7 @@ public void makeHttpRequestsShouldReturnResultWithErrorWhenImpExtMalformed() { // given final BidRequest bidRequest = BidRequest.builder() .imp(singletonList(Imp.builder() + .id("impId1") .banner(Banner.builder().build()) .ext(mapper.createObjectNode()) .build())) @@ -128,7 +130,7 @@ public void makeHttpRequestsShouldReturnResultWithErrorWhenImpExtMalformed() { // then assertThat(result.getValue()).isEmpty(); assertThat(result.getErrors()).hasSize(1) - .containsExactly(BidderError.badInput("imp id=null: openx parameters section is missing")); + .containsExactly(BidderError.badInput("imp id=impId1: openx parameters section is missing")); } @Test @@ -136,6 +138,7 @@ public void makeHttpRequestsShouldReturnResultWithErrorWhenImpExtOpenxEmpty() { // given final BidRequest bidRequest = BidRequest.builder() .imp(singletonList(Imp.builder() + .id("impId1") .video(Video.builder().build()) .ext(mapper.valueToTree( ExtPrebid.of(null, null))) @@ -148,7 +151,7 @@ public void makeHttpRequestsShouldReturnResultWithErrorWhenImpExtOpenxEmpty() { // then assertThat(result.getValue()).isEmpty(); assertThat(result.getErrors()).hasSize(1) - .containsExactly(BidderError.badInput("imp id=null: openx parameters section is missing")); + .containsExactly(BidderError.badInput("imp id=impId1: openx parameters section is missing")); } @Test @@ -156,6 +159,7 @@ public void makeHttpRequestsShouldReturnResultWithErrorWhenImpExtOpenxMalformed( // given final BidRequest bidRequest = BidRequest.builder() .imp(singletonList(Imp.builder() + .id("impId1") .banner(Banner.builder().build()) .ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode()))) .build())) @@ -167,7 +171,7 @@ public void makeHttpRequestsShouldReturnResultWithErrorWhenImpExtOpenxMalformed( // then assertThat(result.getValue()).isEmpty(); assertThat(result.getErrors().getFirst().getMessage()) - .startsWith("imp id=null: Cannot deserialize value of"); + .startsWith("imp id=impId1: Cannot deserialize value of"); } @Test From f2d04cbe99f38b5292296216998ff8b57363d8a6 Mon Sep 17 00:00:00 2001 From: Rafal Sieczka Date: Thu, 27 Aug 2026 14:20:30 +0200 Subject: [PATCH 13/13] Use qualified name for collections methods --- .../java/org/prebid/server/bidder/openx/OpenxBidder.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java b/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java index 1d5699cbfd9..1ddb092a5e7 100644 --- a/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java +++ b/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java @@ -1,8 +1,5 @@ package org.prebid.server.bidder.openx; -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; - import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; @@ -105,9 +102,9 @@ private List makeRequests( final BidRequest request = createSingleRequest(imps, bidRequest, errors); if (request != null) { - return singletonList(request); + return Collections.singletonList(request); } - return emptyList(); + return Collections.emptyList(); } private static BidType resolveBidType(Imp imp) { @@ -237,7 +234,7 @@ private ObjectNode makeImpExt(ObjectNode impExt, boolean addCustomParams) { private List extractBids(BidRequest bidRequest, BidResponse bidResponse) { return bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid()) - ? emptyList() + ? Collections.emptyList() : bidsFromResponse(bidRequest, bidResponse); }