From 3c2557388c9b5fc6dbd73cbe5c0843a1bba2c44d Mon Sep 17 00:00:00 2001 From: Rafal Sieczka Date: Thu, 27 Aug 2026 11:26:13 +0200 Subject: [PATCH 01/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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); } From 15e2758e3fb9c9db655bb0a40348a659700aeef8 Mon Sep 17 00:00:00 2001 From: "kacper.fus" Date: Mon, 21 Sep 2026 12:51:59 +0200 Subject: [PATCH 14/15] refactor --- .../server/bidder/openx/OpenxBidder.java | 122 +++++++----------- 1 file changed, 49 insertions(+), 73 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 1ddb092a5e7..7da18272dc5 100644 --- a/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java +++ b/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java @@ -66,19 +66,56 @@ public OpenxBidder(String endpointUrl, JacksonMapper mapper) { @Override public Result>> makeHttpRequests(BidRequest bidRequest) { - final Map> partitionedImps = bidRequest.getImp().stream() - .filter(Objects::nonNull) - .collect(Collectors.partitioningBy(OpenxBidder::isSupportedImpType)); + final List modifiedImps = new ArrayList<>(); + final List errors = new ArrayList<>(); + final ExtImpOpenx firstValidImpExt = processImps(bidRequest.getImp(), modifiedImps, errors); + + if (modifiedImps.isEmpty()) { + return Result.withErrors(errors); + } + + final BidRequest modifiedBidRequest = modifyBidRequest(bidRequest, modifiedImps, firstValidImpExt); + return Result.of(Collections.singletonList(makeRequest(modifiedBidRequest)), errors); + } + + private ExtImpOpenx processImps(List imps, List modifiedImps, List errors) { + ExtImpOpenx firstValidImpExt = null; + for (Imp imp : imps) { + if (!isSupportedImpType(imp)) { + errors.add(unsupportedImpTypeError(imp)); + continue; + } + + final ExtPrebid impExt; + try { + impExt = parseOpenxExt(imp); + } catch (PreBidException e) { + errors.add(invalidImpError(imp, e)); + continue; + } - final List processingErrors = new ArrayList<>(); - final List outgoingRequests = makeRequests( - bidRequest, - partitionedImps.get(Boolean.TRUE), - processingErrors); + modifiedImps.add(makeImp(imp, impExt)); + if (firstValidImpExt == null) { + firstValidImpExt = impExt.getBidder(); + } + } + return firstValidImpExt; + } - final List errors = errors(partitionedImps.get(Boolean.FALSE), processingErrors); + private static BidderError unsupportedImpTypeError(Imp imp) { + return BidderError.badInput( + "OpenX only supports banner, video and native imps. Ignoring imp id=" + imp.getId()); + } - return Result.of(createHttpRequests(outgoingRequests), errors); + private static BidderError invalidImpError(Imp imp, PreBidException e) { + return BidderError.badInput("imp id=%s: %s".formatted(imp.getId(), e.getMessage())); + } + + private BidRequest modifyBidRequest(BidRequest bidRequest, List imps, ExtImpOpenx firstValidImpExt) { + return bidRequest.toBuilder() + .imp(imps) + .ext(makeReqExt(firstValidImpExt)) + .build(); } @Override @@ -95,18 +132,6 @@ private static boolean isSupportedImpType(Imp imp) { return imp.getBanner() != null || imp.getVideo() != null || imp.getXNative() != null; } - private List makeRequests( - BidRequest bidRequest, - List imps, - List errors) { - - final BidRequest request = createSingleRequest(imps, bidRequest, errors); - if (request != null) { - return Collections.singletonList(request); - } - return Collections.emptyList(); - } - private static BidType resolveBidType(Imp imp) { if (imp.getBanner() != null) { return BidType.banner; @@ -120,57 +145,8 @@ private static BidType resolveBidType(Imp imp) { return BidType.banner; } - private List errors(List notSupportedImps, List processingErrors) { - final List errors = new ArrayList<>(); - // add errors for imps with unsupported media types - if (CollectionUtils.isNotEmpty(notSupportedImps)) { - errors.addAll( - notSupportedImps.stream() - .map(imp -> - "OpenX only supports banner, video and native imps. Ignoring imp id=" + imp.getId()) - .map(BidderError::badInput) - .toList()); - } - - // add errors detected during requests creation - errors.addAll(processingErrors); - - return errors; - } - - private List> createHttpRequests(List bidRequests) { - return bidRequests.stream() - .filter(Objects::nonNull) - .map(singleBidRequest -> BidderUtil.defaultRequest(singleBidRequest, endpointUrl, mapper)) - .toList(); - } - - private BidRequest createSingleRequest(List imps, BidRequest bidRequest, List errors) { - if (CollectionUtils.isEmpty(imps)) { - return null; - } - - final List processedImps = new ArrayList<>(); - ExtRequest requestExt = null; - for (Imp imp : imps) { - try { - final ExtPrebid impExt = parseOpenxExt(imp); - processedImps.add(makeImp(imp, impExt)); - - if (requestExt == null) { - requestExt = makeReqExt(impExt.getBidder()); - } - } catch (PreBidException e) { - errors.add(BidderError.badInput("imp id=%s: %s".formatted(imp.getId(), e.getMessage()))); - } - } - - return CollectionUtils.isNotEmpty(processedImps) - ? bidRequest.toBuilder() - .imp(processedImps) - .ext(requestExt) - .build() - : null; + private HttpRequest makeRequest(BidRequest bidRequest) { + return BidderUtil.defaultRequest(bidRequest, endpointUrl, mapper); } private Imp makeImp(Imp imp, ExtPrebid impExt) { From 46c641418099d320b679640d22f45eb218b51d13 Mon Sep 17 00:00:00 2001 From: "kacper.fus" Date: Wed, 23 Sep 2026 09:46:31 +0200 Subject: [PATCH 15/15] refactor --- .../server/bidder/openx/OpenxBidder.java | 170 ++++++++---------- .../server/bidder/openx/OpenxBidderTest.java | 70 +------- 2 files changed, 80 insertions(+), 160 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 7da18272dc5..ab856f280d3 100644 --- a/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java +++ b/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java @@ -68,29 +68,20 @@ public OpenxBidder(String endpointUrl, JacksonMapper mapper) { public Result>> makeHttpRequests(BidRequest bidRequest) { final List modifiedImps = new ArrayList<>(); final List errors = new ArrayList<>(); - final ExtImpOpenx firstValidImpExt = processImps(bidRequest.getImp(), modifiedImps, errors); - - if (modifiedImps.isEmpty()) { - return Result.withErrors(errors); - } - - final BidRequest modifiedBidRequest = modifyBidRequest(bidRequest, modifiedImps, firstValidImpExt); - return Result.of(Collections.singletonList(makeRequest(modifiedBidRequest)), errors); - } - - private ExtImpOpenx processImps(List imps, List modifiedImps, List errors) { ExtImpOpenx firstValidImpExt = null; - for (Imp imp : imps) { + + for (Imp imp : bidRequest.getImp()) { if (!isSupportedImpType(imp)) { - errors.add(unsupportedImpTypeError(imp)); + errors.add(BidderError.badInput( + "OpenX only supports banner, video and native imps. Ignoring imp id=" + imp.getId())); continue; } final ExtPrebid impExt; try { - impExt = parseOpenxExt(imp); + impExt = parseImpExt(imp); } catch (PreBidException e) { - errors.add(invalidImpError(imp, e)); + errors.add(BidderError.badInput("imp id=%s: %s".formatted(imp.getId(), e.getMessage()))); continue; } @@ -99,59 +90,34 @@ private ExtImpOpenx processImps(List imps, List modifiedImps, List imps, ExtImpOpenx firstValidImpExt) { - return bidRequest.toBuilder() - .imp(imps) - .ext(makeReqExt(firstValidImpExt)) - .build(); - } + final BidRequest modifiedBidRequest = modifyBidRequest(bidRequest, modifiedImps, firstValidImpExt); - @Override - public Result> makeBids(BidderCall httpCall, BidRequest bidRequest) { - try { - final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); - return Result.withValues(extractBids(bidRequest, bidResponse)); - } catch (DecodeException e) { - return Result.withError(BidderError.badServerResponse(e.getMessage())); - } + return Result.of( + Collections.singletonList(BidderUtil.defaultRequest(modifiedBidRequest, endpointUrl, mapper)), + errors); } 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; - } - if (imp.getVideo() != null) { - return BidType.video; - } - if (imp.getXNative() != null) { - return BidType.xNative; + private ExtPrebid parseImpExt(Imp imp) { + try { + return mapper.mapper().convertValue(imp.getExt(), OPENX_EXT_TYPE_REFERENCE); + } catch (IllegalArgumentException e) { + throw new PreBidException(e.getMessage()); } - return BidType.banner; - } - - private HttpRequest makeRequest(BidRequest bidRequest) { - return BidderUtil.defaultRequest(bidRequest, endpointUrl, mapper); } private Imp makeImp(Imp imp, ExtPrebid impExt) { final ExtImpOpenx openxImpExt = impExt.getBidder(); final ExtImpPrebid prebidImpExt = impExt.getPrebid(); + final Imp.ImpBuilder impBuilder = imp.toBuilder() .tagid(openxImpExt.getUnit()) .bidfloor(resolveBidFloor(imp.getBidfloor(), openxImpExt.getCustomFloor())) @@ -160,10 +126,12 @@ private Imp makeImp(Imp imp, ExtPrebid impExt) { if (imp.getVideo() != null && prebidImpExt != null && Objects.equals(prebidImpExt.getIsRewardedInventory(), 1)) { + impBuilder.video(imp.getVideo().toBuilder() .ext(mapper.mapper().valueToTree(OpenxVideoExt.of(1))) .build()); } + return impBuilder.build(); } @@ -173,66 +141,80 @@ private static BigDecimal resolveBidFloor(BigDecimal impBidFloor, BigDecimal cus : impBidFloor; } + private ObjectNode makeImpExt(ObjectNode impExt, boolean addCustomParams) { + final ObjectNode openxImpExt = impExt.deepCopy(); + if (addCustomParams) { + openxImpExt.set(CUSTOM_PARAMS_KEY, openxImpExt.get(BIDDER_EXT).get(CUSTOM_PARAMS_KEY)); + } + openxImpExt.remove(IMP_EXT_SKIP_FIELDS); + + return openxImpExt; + } + + private BidRequest modifyBidRequest(BidRequest bidRequest, List imps, ExtImpOpenx openxImpExt) { + return bidRequest.toBuilder() + .imp(imps) + .ext(makeReqExt(openxImpExt)) + .build(); + } + private ExtRequest makeReqExt(ExtImpOpenx openxImpExt) { return mapper.fillExtension( ExtRequest.empty(), OpenxRequestExt.of(openxImpExt.getDelDomain(), openxImpExt.getPlatform(), OPENX_CONFIG)); } - private ExtPrebid parseOpenxExt(Imp imp) { - final ObjectNode impExtRaw = imp.getExt(); - final ExtPrebid impExt; - if (impExtRaw == null) { - throw new PreBidException("openx parameters section is missing"); - } - + @Override + public Result> makeBids(BidderCall httpCall, BidRequest bidRequest) { try { - impExt = mapper.mapper().convertValue(impExtRaw, OPENX_EXT_TYPE_REFERENCE); - } catch (IllegalArgumentException e) { - throw new PreBidException(e.getMessage()); - } - - final ExtImpOpenx impExtOpenx = impExt != null ? impExt.getBidder() : null; - if (impExtOpenx == null) { - throw new PreBidException("openx parameters section is missing"); - } - return impExt; - } - - private ObjectNode makeImpExt(ObjectNode impExt, boolean addCustomParams) { - final ObjectNode openxImpExt = impExt.deepCopy(); - openxImpExt.remove(IMP_EXT_SKIP_FIELDS); - if (addCustomParams) { - openxImpExt.set(CUSTOM_PARAMS_KEY, impExt.get(BIDDER_EXT).get(CUSTOM_PARAMS_KEY).deepCopy()); + final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); + return Result.withValues(extractBids(bidRequest, bidResponse)); + } catch (DecodeException e) { + return Result.withError(BidderError.badServerResponse(e.getMessage())); } - return openxImpExt; } private List extractBids(BidRequest bidRequest, BidResponse bidResponse) { - return bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid()) - ? Collections.emptyList() - : bidsFromResponse(bidRequest, bidResponse); - } + if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) { + return Collections.emptyList(); + } - private List bidsFromResponse(BidRequest bidRequest, BidResponse bidResponse) { final Map impIdToBidType = impIdToBidType(bidRequest); - final String bidCurrency = StringUtils.isNotBlank(bidResponse.getCur()) - ? bidResponse.getCur() - : DEFAULT_BID_CURRENCY; + final String bidCurrency = StringUtils.defaultIfBlank(bidResponse.getCur(), DEFAULT_BID_CURRENCY); return bidResponse.getSeatbid().stream() .filter(Objects::nonNull) .map(SeatBid::getBid) .filter(Objects::nonNull) .flatMap(Collection::stream) + .filter(Objects::nonNull) .map(bid -> toBidderBid(bid, impIdToBidType, bidCurrency)) .toList(); } + private static Map impIdToBidType(BidRequest bidRequest) { + return bidRequest.getImp().stream() + .collect(Collectors.toMap(Imp::getId, OpenxBidder::resolveBidType)); + } + + private static BidType resolveBidType(Imp imp) { + if (imp.getBanner() != null) { + return BidType.banner; + } + if (imp.getVideo() != null) { + return BidType.video; + } + if (imp.getXNative() != null) { + return BidType.xNative; + } + return BidType.banner; + } + private BidderBid toBidderBid(Bid bid, Map impIdToBidType, String bidCurrency) { final BidType bidType = getBidType(bid, impIdToBidType); final ExtBidPrebidVideo videoInfo = bidType == BidType.video ? getVideoInfo(bid) : null; + return BidderBid.builder() .bid(bid.toBuilder().ext(getBidExt(bid)).build()) .type(bidType) @@ -241,16 +223,6 @@ private BidderBid toBidderBid(Bid bid, Map impIdToBidType, Stri .build(); } - private static ExtBidPrebidVideo getVideoInfo(Bid bid) { - final String primaryCategory = CollectionUtils.isEmpty(bid.getCat()) ? null : bid.getCat().getFirst(); - return ExtBidPrebidVideo.of(bid.getDur(), primaryCategory); - } - - private static Map impIdToBidType(BidRequest bidRequest) { - return bidRequest.getImp().stream() - .collect(Collectors.toMap(Imp::getId, OpenxBidder::resolveBidType)); - } - private static BidType getBidType(Bid bid, Map impIdToBidType) { return switch (bid.getMtype()) { case 1 -> BidType.banner; @@ -260,6 +232,12 @@ private static BidType getBidType(Bid bid, Map impIdToBidType) }; } + private static ExtBidPrebidVideo getVideoInfo(Bid bid) { + return ExtBidPrebidVideo.of( + bid.getDur(), + CollectionUtils.isEmpty(bid.getCat()) ? null : bid.getCat().getFirst()); + } + private ObjectNode getBidExt(Bid bid) { final ObjectNode ext = bid.getExt(); if (ext == null) { 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 74888aa1d68..00bd83aca86 100644 --- a/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java @@ -94,66 +94,6 @@ public void makeHttpRequestsShouldReturnResultWithErrorWhenAudioImpsPresent() { "OpenX only supports banner, video and native imps. Ignoring imp id=impId2")); } - @Test - public void makeHttpRequestsShouldReturnResultWithErrorWhenImpExtOmitted() { - // given - final BidRequest bidRequest = BidRequest.builder() - .imp(singletonList(Imp.builder() - .id("impId1") - .banner(Banner.builder().build()) - .build())) - .build(); - - // when - final Result>> result = target.makeHttpRequests(bidRequest); - - // then - assertThat(result.getValue()).isEmpty(); - assertThat(result.getErrors()).hasSize(1) - .containsExactly(BidderError.badInput("imp id=impId1: openx parameters section is missing")); - } - - @Test - public void makeHttpRequestsShouldReturnResultWithErrorWhenImpExtMalformed() { - // given - final BidRequest bidRequest = BidRequest.builder() - .imp(singletonList(Imp.builder() - .id("impId1") - .banner(Banner.builder().build()) - .ext(mapper.createObjectNode()) - .build())) - .build(); - - // when - final Result>> result = target.makeHttpRequests(bidRequest); - - // then - assertThat(result.getValue()).isEmpty(); - assertThat(result.getErrors()).hasSize(1) - .containsExactly(BidderError.badInput("imp id=impId1: openx parameters section is missing")); - } - - @Test - 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))) - .build())) - .build(); - - // when - final Result>> result = target.makeHttpRequests(bidRequest); - - // then - assertThat(result.getValue()).isEmpty(); - assertThat(result.getErrors()).hasSize(1) - .containsExactly(BidderError.badInput("imp id=impId1: openx parameters section is missing")); - } - @Test public void makeHttpRequestsShouldReturnResultWithErrorWhenImpExtOpenxMalformed() { // given @@ -375,10 +315,12 @@ public void makeHttpRequestsShouldSkipMalformedFirstImpAndDeriveRequestExtFromLa Imp.builder() .id("badImp") .banner(Banner.builder().build()) + .ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode()))) .build(), Imp.builder() .id("anotherBadImp") .banner(Banner.builder().build()) + .ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode()))) .build(), Imp.builder() .id("goodImp") @@ -396,10 +338,10 @@ public void makeHttpRequestsShouldSkipMalformedFirstImpAndDeriveRequestExtFromLa final Result>> result = target.makeHttpRequests(bidRequest); // then - assertThat(result.getErrors()).hasSize(2) - .containsExactly( - BidderError.badInput("imp id=badImp: openx parameters section is missing"), - BidderError.badInput("imp id=anotherBadImp: openx parameters section is missing")); + assertThat(result.getErrors()).hasSize(2); + assertThat(result.getErrors().get(0).getMessage()).startsWith("imp id=badImp: Cannot deserialize value of"); + assertThat(result.getErrors().get(1).getMessage()) + .startsWith("imp id=anotherBadImp: Cannot deserialize value of"); assertThat(result.getValue()).hasSize(1) .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))