From c23a90772afc0357e48ba5649521fe8a75e7528e Mon Sep 17 00:00:00 2001 From: antonbabak Date: Tue, 8 Jul 2025 15:33:08 +0200 Subject: [PATCH 1/3] Pubmatic Adapter: Set bid.meta.mediaType=video when bid.ext.ibv=true --- .../bidder/pubmatic/PubmaticBidder.java | 129 +++++++++------ .../model/response/PubmaticBidExt.java | 8 +- .../bidder/pubmatic/PubmaticBidderTest.java | 151 +++++++++++++----- .../test-auction-pubmatic-response.json | 5 +- .../pubmatic/test-pubmatic-bid-response.json | 6 +- 5 files changed, 197 insertions(+), 102 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/pubmatic/PubmaticBidder.java b/src/main/java/org/prebid/server/bidder/pubmatic/PubmaticBidder.java index 09e43473dfa..cb8ca10db0a 100644 --- a/src/main/java/org/prebid/server/bidder/pubmatic/PubmaticBidder.java +++ b/src/main/java/org/prebid/server/bidder/pubmatic/PubmaticBidder.java @@ -42,6 +42,7 @@ import org.prebid.server.proto.openrtb.ext.request.pubmatic.ExtImpPubmaticKeyVal; import org.prebid.server.proto.openrtb.ext.response.BidType; import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebid; +import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebidMeta; import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebidVideo; import org.prebid.server.proto.openrtb.ext.response.ExtIgi; import org.prebid.server.proto.openrtb.ext.response.ExtIgiIgs; @@ -504,6 +505,7 @@ public CompositeBidderResponse makeBidderResponse(BidderCall httpCal return CompositeBidderResponse.builder() .bids(extractBids(bidResponse, errors)) .igi(extractIgi(bidResponse)) + .errors(errors) .build(); } catch (DecodeException | PreBidException e) { return CompositeBidderResponse.withError(BidderError.badServerResponse(e.getMessage())); @@ -516,6 +518,59 @@ private List extractBids(PubmaticBidResponse bidResponse, List errors) { + return switch (bid.getMtype()) { + case 1 -> BidType.banner; + case 2 -> BidType.video; + case 3 -> BidType.audio; + case 4 -> BidType.xNative; + case null, default -> { + errors.add(BidderError.badServerResponse("failed to parse bid mtype (%d) for impression id %s" + .formatted(bid.getMtype(), bid.getImpid()))); + yield null; + } + }; + } + + private static Integer getDuration(PubmaticBidExt bidExt) { + return Optional.ofNullable(bidExt) + .map(PubmaticBidExt::getVideo) + .map(VideoCreativeInfo::getDuration) + .orElse(null); + } + + private PubmaticBidExt parseBidExt(ObjectNode bidExt, List errors) { + try { + return bidExt != null ? mapper.mapper().treeToValue(bidExt, PubmaticBidExt.class) : null; + } catch (JsonProcessingException e) { + errors.add(BidderError.badServerResponse(e.getMessage())); + return null; + } + } + + private static boolean getInBannerVideo(PubmaticBidExt bidExt) { + return Optional.ofNullable(bidExt) + .map(PubmaticBidExt::getInBannerVideo) + .orElse(false); + } + + private String resolveNativeAdm(String adm, List bidderErrors) { + final JsonNode admNode; + try { + admNode = mapper.mapper().readTree(adm); + } catch (JsonProcessingException e) { + bidderErrors.add(BidderError.badServerResponse("Unable to parse native adm: %s".formatted(adm))); + return null; + } + + final JsonNode nativeNode = admNode.get("native"); + if (nativeNode != null && !nativeNode.isMissingNode()) { + return nativeNode.toString(); + } + + return null; + } + private List bidsFromResponse(PubmaticBidResponse bidResponse, List bidderErrors) { return bidResponse.getSeatbid().stream() .filter(Objects::nonNull) @@ -523,6 +578,7 @@ private List bidsFromResponse(PubmaticBidResponse bidResponse, List resolveBidderBid(bid, bidResponse.getCur(), bidderErrors)) + .filter(Objects::nonNull) .toList(); } @@ -533,21 +589,22 @@ private BidderBid resolveBidderBid(Bid bid, String currency, List b : null; final PubmaticBidExt pubmaticBidExt = parseBidExt(bid.getExt(), bidderErrors); - final Integer duration = getDuration(pubmaticBidExt); - final BidType bidType = getBidType(pubmaticBidExt); + final BidType bidType = getBidType(bid, bidderErrors); + + if (bidType == null) { + return null; + } final String bidAdm = bid.getAdm(); final String resolvedAdm = bidAdm != null && bidType == BidType.xNative ? resolveNativeAdm(bidAdm, bidderErrors) : bidAdm; - final Bid updatedBid = firstCat != null || duration != null || resolvedAdm != null - ? bid.toBuilder() + final Bid updatedBid = bid.toBuilder() .cat(firstCat) .adm(resolvedAdm != null ? resolvedAdm : bidAdm) - .ext(duration != null ? updateBidExtWithExtPrebid(duration, bid.getExt()) : bid.getExt()) - .build() - : bid; + .ext(updateBidExtWithExtPrebid(pubmaticBidExt, bidType, bid.getExt())) + .build(); return BidderBid.builder() .bid(updatedBid) @@ -558,54 +615,20 @@ private BidderBid resolveBidderBid(Bid bid, String currency, List b .build(); } - private PubmaticBidExt parseBidExt(ObjectNode bidExt, List errors) { - try { - return bidExt != null ? mapper.mapper().treeToValue(bidExt, PubmaticBidExt.class) : null; - } catch (JsonProcessingException e) { - errors.add(BidderError.badServerResponse(e.getMessage())); - return null; - } - } - - private static Integer getDuration(PubmaticBidExt bidExt) { - return Optional.ofNullable(bidExt) - .map(PubmaticBidExt::getVideo) - .map(VideoCreativeInfo::getDuration) - .orElse(null); - } - - private static BidType getBidType(PubmaticBidExt bidExt) { - final int bidType = Optional.ofNullable(bidExt) - .map(PubmaticBidExt::getBidType) - .orElse(0); - - return switch (bidType) { - case 1 -> BidType.video; - case 2 -> BidType.xNative; - default -> BidType.banner; - }; - } - - private String resolveNativeAdm(String adm, List bidderErrors) { - final JsonNode admNode; - try { - admNode = mapper.mapper().readTree(adm); - } catch (JsonProcessingException e) { - bidderErrors.add(BidderError.badServerResponse("Unable to parse native adm: %s".formatted(adm))); - return null; - } - - final JsonNode nativeNode = admNode.get("native"); - if (nativeNode != null && !nativeNode.isMissingNode()) { - return nativeNode.toString(); - } + private ObjectNode updateBidExtWithExtPrebid(PubmaticBidExt pubmaticBidExt, BidType type, ObjectNode extBid) { + final Integer duration = getDuration(pubmaticBidExt); + final boolean inBannerVideo = getInBannerVideo(pubmaticBidExt); - return null; - } + final ExtBidPrebid extBidPrebid = ExtBidPrebid.builder() + .video(duration != null ? ExtBidPrebidVideo.of(duration, null) : null) + .meta(ExtBidPrebidMeta.builder() + .mediaType(inBannerVideo ? BidType.video.getName() : type.getName()) + .build()) + .build(); - private ObjectNode updateBidExtWithExtPrebid(Integer duration, ObjectNode extBid) { - final ExtBidPrebid extBidPrebid = ExtBidPrebid.builder().video(ExtBidPrebidVideo.of(duration, null)).build(); - return extBid.set(PREBID, mapper.mapper().valueToTree(extBidPrebid)); + return extBid != null + ? extBid.set(PREBID, mapper.mapper().valueToTree(extBidPrebid)) + : mapper.mapper().createObjectNode().set(PREBID, mapper.mapper().valueToTree(extBidPrebid)); } private static Integer getDealPriority(PubmaticBidExt bidExt) { diff --git a/src/main/java/org/prebid/server/bidder/pubmatic/model/response/PubmaticBidExt.java b/src/main/java/org/prebid/server/bidder/pubmatic/model/response/PubmaticBidExt.java index 3aa8db2a2f5..a368a98cc68 100644 --- a/src/main/java/org/prebid/server/bidder/pubmatic/model/response/PubmaticBidExt.java +++ b/src/main/java/org/prebid/server/bidder/pubmatic/model/response/PubmaticBidExt.java @@ -1,20 +1,18 @@ package org.prebid.server.bidder.pubmatic.model.response; -import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Value; @Value(staticConstructor = "of") public class PubmaticBidExt { - @JsonProperty("BidType") - @JsonAlias({"bidtype", "bidType"}) - Integer bidType; - VideoCreativeInfo video; @JsonProperty("prebiddealpriority") Integer prebidDealPriority; String marketplace; + + @JsonProperty("ibv") + Boolean inBannerVideo; } diff --git a/src/test/java/org/prebid/server/bidder/pubmatic/PubmaticBidderTest.java b/src/test/java/org/prebid/server/bidder/pubmatic/PubmaticBidderTest.java index b44b7d4768d..054d2f2d13e 100644 --- a/src/test/java/org/prebid/server/bidder/pubmatic/PubmaticBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/pubmatic/PubmaticBidderTest.java @@ -41,6 +41,7 @@ import org.prebid.server.proto.openrtb.ext.request.pubmatic.ExtImpPubmatic; import org.prebid.server.proto.openrtb.ext.request.pubmatic.ExtImpPubmaticKeyVal; import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebid; +import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebidMeta; import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebidVideo; import org.prebid.server.proto.openrtb.ext.response.ExtIgi; import org.prebid.server.proto.openrtb.ext.response.ExtIgiIgs; @@ -59,6 +60,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.Assertions.tuple; +import static org.prebid.server.proto.openrtb.ext.response.BidType.audio; import static org.prebid.server.proto.openrtb.ext.response.BidType.banner; import static org.prebid.server.proto.openrtb.ext.response.BidType.video; import static org.prebid.server.proto.openrtb.ext.response.BidType.xNative; @@ -1086,48 +1088,101 @@ public void makeBidderResponseShouldReturnEmptyListIfBidResponseSeatBidIsNull() public void makeBidderResponseShouldReturnBannerBid() throws JsonProcessingException { // given final BidderCall httpCall = givenHttpCall( - mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("123")))); + mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("123").mtype(1)))); // when final CompositeBidderResponse result = target.makeBidderResponse(httpCall, null); // then assertThat(result.getErrors()).isEmpty(); - assertThat(result.getBids()) - .containsExactly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD")); + + final ObjectNode expectedBidExt = mapper.createObjectNode().set("prebid", mapper.createObjectNode() + .set("meta", mapper.createObjectNode().put("mediaType", "banner"))); + + assertThat(result.getBids()).containsExactly(BidderBid.of( + Bid.builder().impid("123").mtype(1).ext(expectedBidExt).build(), banner, "USD")); } @Test - public void makeBidderResponseShouldReturnVideoBidIfExtBidContainsBidTypeOne() throws JsonProcessingException { + public void makeBidderResponseShouldReturnVideoBidWhenInBannerVideoIsTrue() throws JsonProcessingException { + // given + final ObjectNode givenBidExt = mapper.createObjectNode().put("ibv", true); + final BidderCall httpCall = givenHttpCall( + mapper.writeValueAsString(givenBidResponse( + bidBuilder -> bidBuilder.impid("123").mtype(1).ext(givenBidExt)))); + + // when + final CompositeBidderResponse result = target.makeBidderResponse(httpCall, null); + + // then + assertThat(result.getErrors()).isEmpty(); + + final ObjectNode expectedBidExt = mapper.createObjectNode() + .put("ibv", true) + .set("prebid", mapper.createObjectNode() + .set("meta", mapper.createObjectNode().put("mediaType", "video"))); + + assertThat(result.getBids()).containsExactly(BidderBid.of( + Bid.builder().impid("123").mtype(1).ext(expectedBidExt).build(), banner, "USD")); + } + + @Test + public void makeBidderResponseShouldReturnVideoBidIfExtBidContainsMtypeTwo() throws JsonProcessingException { // given - final ObjectNode bidType = mapper.createObjectNode().put("BidType", 1); final BidderCall httpCall = givenHttpCall( mapper.writeValueAsString( - givenBidResponse(bidBuilder -> bidBuilder.impid("123").ext(bidType)))); + givenBidResponse(bidBuilder -> bidBuilder.impid("123").mtype(2)))); // when final CompositeBidderResponse result = target.makeBidderResponse(httpCall, null); // then assertThat(result.getErrors()).isEmpty(); - assertThat(result.getBids()) - .containsExactly(BidderBid.of(Bid.builder().impid("123").ext(bidType).build(), video, "USD")); + + final ObjectNode expectedBidExt = mapper.createObjectNode().set("prebid", mapper.createObjectNode() + .set("meta", mapper.createObjectNode().put("mediaType", "video"))); + + assertThat(result.getBids()).containsExactly( + BidderBid.of(Bid.builder().impid("123").mtype(2).ext(expectedBidExt).build(), video, "USD")); } @Test - public void makeBidderResponseShouldReturnXNativeBidIfExtBidContainsBidTypeTwo() throws JsonProcessingException { + public void makeBidderResponseShouldReturnAudioBidIfExtBidContainsMtype3() throws JsonProcessingException { // given - final ObjectNode bidType = mapper.createObjectNode().put("BidType", 2); final BidderCall httpCall = givenHttpCall( - mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("123").ext(bidType)))); + mapper.writeValueAsString( + givenBidResponse(bidBuilder -> bidBuilder.impid("123").mtype(3)))); // when final CompositeBidderResponse result = target.makeBidderResponse(httpCall, null); // then assertThat(result.getErrors()).isEmpty(); - assertThat(result.getBids()) - .containsExactly(BidderBid.of(Bid.builder().impid("123").ext(bidType).build(), xNative, "USD")); + + final ObjectNode expectedBidExt = mapper.createObjectNode().set("prebid", mapper.createObjectNode() + .set("meta", mapper.createObjectNode().put("mediaType", "audio"))); + + assertThat(result.getBids()).containsExactly( + BidderBid.of(Bid.builder().impid("123").mtype(3).ext(expectedBidExt).build(), audio, "USD")); + } + + @Test + public void makeBidderResponseShouldReturnXNativeBidIfExtBidContainsMtypeFour() throws JsonProcessingException { + // given + final BidderCall httpCall = givenHttpCall( + mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("123").mtype(4)))); + + // when + final CompositeBidderResponse result = target.makeBidderResponse(httpCall, null); + + // then + assertThat(result.getErrors()).isEmpty(); + + final ObjectNode expectedBidExt = mapper.createObjectNode().set("prebid", mapper.createObjectNode() + .set("meta", mapper.createObjectNode().put("mediaType", "native"))); + + assertThat(result.getBids()).containsExactly( + BidderBid.of(Bid.builder().impid("123").mtype(4).ext(expectedBidExt).build(), xNative, "USD")); } @Test @@ -1136,9 +1191,9 @@ public void makeBidderResponseShouldFillExtBidPrebidVideoDurationIfDurationIsNot // given final BidderCall httpCall = givenHttpCall( mapper.writeValueAsString( - givenBidResponse(bidBuilder -> bidBuilder.impid("123") + givenBidResponse(bidBuilder -> bidBuilder.impid("123").mtype(2) .ext(mapper.valueToTree( - PubmaticBidExt.of(null, VideoCreativeInfo.of(1), null, null)))))); + PubmaticBidExt.of(VideoCreativeInfo.of(1), null, null, null)))))); // when final CompositeBidderResponse result = target.makeBidderResponse(httpCall, null); @@ -1148,9 +1203,12 @@ public void makeBidderResponseShouldFillExtBidPrebidVideoDurationIfDurationIsNot final ObjectNode bidExt = mapper.createObjectNode(); bidExt.set("video", mapper.valueToTree(VideoCreativeInfo.of(1))); - bidExt.set("prebid", mapper.valueToTree(ExtBidPrebid.builder().video(ExtBidPrebidVideo.of(1, null)).build())); - assertThat(result.getBids()) - .containsExactly(BidderBid.of(Bid.builder().impid("123").ext(bidExt).build(), banner, "USD")); + bidExt.set("prebid", mapper.valueToTree(ExtBidPrebid.builder() + .meta(ExtBidPrebidMeta.builder().mediaType("video").build()) + .video(ExtBidPrebidVideo.of(1, null)) + .build())); + assertThat(result.getBids()).containsExactly( + BidderBid.of(Bid.builder().mtype(2).impid("123").ext(bidExt).build(), video, "USD")); } @Test @@ -1161,7 +1219,7 @@ public void makeBidderResponseShouldNotFillExtBidPrebidVideoDurationIfDurationIs mapper.writeValueAsString( givenBidResponse(bidBuilder -> bidBuilder.impid("123") .ext(mapper.valueToTree( - PubmaticBidExt.of(null, VideoCreativeInfo.of(null), null, null)))))); + PubmaticBidExt.of(VideoCreativeInfo.of(null), null, null, null)))))); // when final CompositeBidderResponse result = target.makeBidderResponse(httpCall, null); @@ -1170,9 +1228,12 @@ public void makeBidderResponseShouldNotFillExtBidPrebidVideoDurationIfDurationIs assertThat(result.getErrors()).isEmpty(); final ObjectNode bidExt = mapper.createObjectNode(); bidExt.set("video", mapper.valueToTree(VideoCreativeInfo.of(null))); + bidExt.set("prebid", mapper.valueToTree(ExtBidPrebid.builder() + .meta(ExtBidPrebidMeta.builder().mediaType("banner").build()) + .build())); - assertThat(result.getBids()) - .containsExactly(BidderBid.of(Bid.builder().impid("123").ext(bidExt).build(), banner, "USD")); + assertThat(result.getBids()).containsExactly( + BidderBid.of(Bid.builder().impid("123").mtype(1).ext(bidExt).build(), banner, "USD")); } @Test @@ -1189,8 +1250,11 @@ public void makeBidderResponseShouldNotFillExtBidPrebidVideoDurationIfVideoIsNul // then assertThat(result.getErrors()).isEmpty(); - assertThat(result.getBids()).containsExactly( - BidderBid.of(Bid.builder().impid("123").ext(mapper.createObjectNode()).build(), banner, "USD")); + final ObjectNode expectedBidExt = mapper.createObjectNode().set("prebid", mapper.createObjectNode() + .set("meta", mapper.createObjectNode().put("mediaType", "banner"))); + + assertThat(result.getBids()).containsExactly(BidderBid.of( + Bid.builder().impid("123").mtype(1).ext(expectedBidExt).build(), banner, "USD")); } @Test @@ -1200,7 +1264,7 @@ public void makeBidderResponseShouldFillDealPriorityData() throws JsonProcessing mapper.writeValueAsString( givenBidResponse(bidBuilder -> bidBuilder.impid("123") .ext(mapper.valueToTree( - PubmaticBidExt.of(null, null, 12, null)))))); + PubmaticBidExt.of(null, 12, null, null)))))); // when final CompositeBidderResponse result = target.makeBidderResponse(httpCall, null); @@ -1219,7 +1283,7 @@ public void makeBidderResponseShouldFillSeat() throws JsonProcessingException { mapper.writeValueAsString( givenBidResponse(bidBuilder -> bidBuilder.impid("123") .ext(mapper.valueToTree( - PubmaticBidExt.of(null, null, 12, "marketplace")))))); + PubmaticBidExt.of(null, 12, "marketplace", null)))))); // when final CompositeBidderResponse result = target.makeBidderResponse(httpCall, null); @@ -1240,10 +1304,10 @@ public void makeBidderResponseShouldParseNativeAdmData() throws JsonProcessingEx admNode.set("native", nativeNode); final BidderCall httpCall = givenHttpCall( mapper.writeValueAsString( - givenBidResponse(bidBuilder -> bidBuilder.impid("123") + givenBidResponse(bidBuilder -> bidBuilder.impid("123").mtype(4) .adm(admNode.toString()) .ext(mapper.valueToTree( - PubmaticBidExt.of(2, null, 12, null)))))); + PubmaticBidExt.of(null, 12, null, null)))))); // when final CompositeBidderResponse result = target.makeBidderResponse(httpCall, null); @@ -1269,24 +1333,32 @@ public void makeBidderResponseShouldTakeOnlyFirstCatElement() throws JsonProcess // then assertThat(result.getErrors()).isEmpty(); - assertThat(result.getBids()).containsExactly( - BidderBid.of(Bid.builder().impid("123").cat(singletonList("cat1")).build(), banner, "USD")); + final ObjectNode expectedBidExt = mapper.createObjectNode().set("prebid", mapper.createObjectNode() + .set("meta", mapper.createObjectNode().put("mediaType", "banner"))); + + assertThat(result.getBids()).containsExactly(BidderBid.of( + Bid.builder() + .impid("123") + .mtype(1) + .cat(singletonList("cat1")) + .ext(expectedBidExt).build(), + banner, + "USD")); } @Test public void makeBidderResponseShouldReturnBannerBidIfExtBidContainsIllegalBidType() throws JsonProcessingException { // given - final ObjectNode bidType = mapper.createObjectNode().put("BidType", 100); final BidderCall httpCall = givenHttpCall( - mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("123").ext(bidType)))); + mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("123").mtype(100)))); // when final CompositeBidderResponse result = target.makeBidderResponse(httpCall, null); // then - assertThat(result.getErrors()).isEmpty(); - assertThat(result.getBids()) - .containsExactly(BidderBid.of(Bid.builder().impid("123").ext(bidType).build(), banner, "USD")); + assertThat(result.getErrors()).containsExactly( + BidderError.badServerResponse("failed to parse bid mtype (100) for impression id 123")); + assertThat(result.getBids()).isEmpty(); } @Test @@ -1306,8 +1378,11 @@ public void makeBidderResponseShouldReturnFledgeAuctionConfig() throws JsonProce // then assertThat(result.getErrors()).isEmpty(); - assertThat(result.getBids()) - .containsExactly(BidderBid.of(Bid.builder().impid("imp_id").build(), banner, "USD")); + final ObjectNode expectedBidExt = mapper.createObjectNode().set("prebid", mapper.createObjectNode() + .set("meta", mapper.createObjectNode().put("mediaType", "banner"))); + + assertThat(result.getBids()).containsExactly(BidderBid.of( + Bid.builder().impid("imp_id").mtype(1).ext(expectedBidExt).build(), banner, "USD")); final ExtIgi igi = ExtIgi.builder() .igs(singletonList(ExtIgiIgs.builder().impId("imp_id").config(auctionConfig).build())) @@ -1325,7 +1400,7 @@ public void makeBidderResponseShouldNotModifyAdmWhenNativeNodeIsNull() throws Js givenBidResponse(bidBuilder -> bidBuilder.impid("123") .adm(admNode.toString()) .ext(mapper.valueToTree( - PubmaticBidExt.of(2, null, 12, null)))))); + PubmaticBidExt.of(null, 12, null, null)))))); // when final CompositeBidderResponse result = target.makeBidderResponse(httpCall, null); @@ -1413,7 +1488,7 @@ private static BidResponse givenBidResponse(UnaryOperator bidCus return BidResponse.builder() .cur("USD") .seatbid(singletonList(SeatBid.builder() - .bid(singletonList(bidCustomizer.apply(Bid.builder()).build())) + .bid(singletonList(bidCustomizer.apply(Bid.builder().mtype(1)).build())) .build())) .build(); } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/pubmatic/test-auction-pubmatic-response.json b/src/test/resources/org/prebid/server/it/openrtb2/pubmatic/test-auction-pubmatic-response.json index 964d4ea072b..66a4e966a31 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/pubmatic/test-auction-pubmatic-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/pubmatic/test-auction-pubmatic-response.json @@ -12,12 +12,13 @@ "crid": "crid9", "w": 300, "h": 600, + "mtype": 2, "ext": { - "bidtype": 1, "prebid": { "type": "video", "meta": { - "adaptercode": "pubmatic" + "adaptercode": "pubmatic", + "mediaType": "video" } }, "origbidcpm": 4.75 diff --git a/src/test/resources/org/prebid/server/it/openrtb2/pubmatic/test-pubmatic-bid-response.json b/src/test/resources/org/prebid/server/it/openrtb2/pubmatic/test-pubmatic-bid-response.json index 713640bcf40..b8a8151aff8 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/pubmatic/test-pubmatic-bid-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/pubmatic/test-pubmatic-bid-response.json @@ -9,11 +9,9 @@ "price": 4.75, "adm": "adm9", "crid": "crid9", + "mtype": "2", "w": 300, - "h": 600, - "ext": { - "bidtype": 1 - } + "h": 600 } ], "seat": "seatId9", From 44c37f1531ca30180f6a26d935e27d32d1f99bba Mon Sep 17 00:00:00 2001 From: antonbabak Date: Tue, 8 Jul 2025 15:44:38 +0200 Subject: [PATCH 2/3] Fix order --- .../bidder/pubmatic/PubmaticBidder.java | 72 +++++++++---------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/pubmatic/PubmaticBidder.java b/src/main/java/org/prebid/server/bidder/pubmatic/PubmaticBidder.java index cb8ca10db0a..2762b86f9e9 100644 --- a/src/main/java/org/prebid/server/bidder/pubmatic/PubmaticBidder.java +++ b/src/main/java/org/prebid/server/bidder/pubmatic/PubmaticBidder.java @@ -518,42 +518,6 @@ private List extractBids(PubmaticBidResponse bidResponse, List errors) { - return switch (bid.getMtype()) { - case 1 -> BidType.banner; - case 2 -> BidType.video; - case 3 -> BidType.audio; - case 4 -> BidType.xNative; - case null, default -> { - errors.add(BidderError.badServerResponse("failed to parse bid mtype (%d) for impression id %s" - .formatted(bid.getMtype(), bid.getImpid()))); - yield null; - } - }; - } - - private static Integer getDuration(PubmaticBidExt bidExt) { - return Optional.ofNullable(bidExt) - .map(PubmaticBidExt::getVideo) - .map(VideoCreativeInfo::getDuration) - .orElse(null); - } - - private PubmaticBidExt parseBidExt(ObjectNode bidExt, List errors) { - try { - return bidExt != null ? mapper.mapper().treeToValue(bidExt, PubmaticBidExt.class) : null; - } catch (JsonProcessingException e) { - errors.add(BidderError.badServerResponse(e.getMessage())); - return null; - } - } - - private static boolean getInBannerVideo(PubmaticBidExt bidExt) { - return Optional.ofNullable(bidExt) - .map(PubmaticBidExt::getInBannerVideo) - .orElse(false); - } - private String resolveNativeAdm(String adm, List bidderErrors) { final JsonNode admNode; try { @@ -615,6 +579,29 @@ private BidderBid resolveBidderBid(Bid bid, String currency, List b .build(); } + private PubmaticBidExt parseBidExt(ObjectNode bidExt, List errors) { + try { + return bidExt != null ? mapper.mapper().treeToValue(bidExt, PubmaticBidExt.class) : null; + } catch (JsonProcessingException e) { + errors.add(BidderError.badServerResponse(e.getMessage())); + return null; + } + } + + private static BidType getBidType(Bid bid, List errors) { + return switch (bid.getMtype()) { + case 1 -> BidType.banner; + case 2 -> BidType.video; + case 3 -> BidType.audio; + case 4 -> BidType.xNative; + case null, default -> { + errors.add(BidderError.badServerResponse("failed to parse bid mtype (%d) for impression id %s" + .formatted(bid.getMtype(), bid.getImpid()))); + yield null; + } + }; + } + private ObjectNode updateBidExtWithExtPrebid(PubmaticBidExt pubmaticBidExt, BidType type, ObjectNode extBid) { final Integer duration = getDuration(pubmaticBidExt); final boolean inBannerVideo = getInBannerVideo(pubmaticBidExt); @@ -631,6 +618,19 @@ private ObjectNode updateBidExtWithExtPrebid(PubmaticBidExt pubmaticBidExt, BidT : mapper.mapper().createObjectNode().set(PREBID, mapper.mapper().valueToTree(extBidPrebid)); } + private static Integer getDuration(PubmaticBidExt bidExt) { + return Optional.ofNullable(bidExt) + .map(PubmaticBidExt::getVideo) + .map(VideoCreativeInfo::getDuration) + .orElse(null); + } + + private static boolean getInBannerVideo(PubmaticBidExt bidExt) { + return Optional.ofNullable(bidExt) + .map(PubmaticBidExt::getInBannerVideo) + .orElse(false); + } + private static Integer getDealPriority(PubmaticBidExt bidExt) { return Optional.ofNullable(bidExt) .map(PubmaticBidExt::getPrebidDealPriority) From 369b1f768d963bb4e178130f8b3a503bd182b07b Mon Sep 17 00:00:00 2001 From: antonbabak Date: Fri, 25 Jul 2025 09:28:57 +0200 Subject: [PATCH 3/3] fix comments --- .../bidder/pubmatic/PubmaticBidder.java | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/pubmatic/PubmaticBidder.java b/src/main/java/org/prebid/server/bidder/pubmatic/PubmaticBidder.java index 2762b86f9e9..ffd6c08d184 100644 --- a/src/main/java/org/prebid/server/bidder/pubmatic/PubmaticBidder.java +++ b/src/main/java/org/prebid/server/bidder/pubmatic/PubmaticBidder.java @@ -518,23 +518,6 @@ private List extractBids(PubmaticBidResponse bidResponse, List bidderErrors) { - final JsonNode admNode; - try { - admNode = mapper.mapper().readTree(adm); - } catch (JsonProcessingException e) { - bidderErrors.add(BidderError.badServerResponse("Unable to parse native adm: %s".formatted(adm))); - return null; - } - - final JsonNode nativeNode = admNode.get("native"); - if (nativeNode != null && !nativeNode.isMissingNode()) { - return nativeNode.toString(); - } - - return null; - } - private List bidsFromResponse(PubmaticBidResponse bidResponse, List bidderErrors) { return bidResponse.getSeatbid().stream() .filter(Objects::nonNull) @@ -602,6 +585,23 @@ private static BidType getBidType(Bid bid, List errors) { }; } + private String resolveNativeAdm(String adm, List bidderErrors) { + final JsonNode admNode; + try { + admNode = mapper.mapper().readTree(adm); + } catch (JsonProcessingException e) { + bidderErrors.add(BidderError.badServerResponse("Unable to parse native adm: %s".formatted(adm))); + return null; + } + + final JsonNode nativeNode = admNode.get("native"); + if (nativeNode != null && !nativeNode.isMissingNode()) { + return nativeNode.toString(); + } + + return null; + } + private ObjectNode updateBidExtWithExtPrebid(PubmaticBidExt pubmaticBidExt, BidType type, ObjectNode extBid) { final Integer duration = getDuration(pubmaticBidExt); final boolean inBannerVideo = getInBannerVideo(pubmaticBidExt);