From f5a365c7bf12523e0fbecb57c6aa049f072e7a84 Mon Sep 17 00:00:00 2001 From: Aman Jain <34913883+amanpatniajmer@users.noreply.github.com> Date: Tue, 2 Apr 2024 19:41:27 +0530 Subject: [PATCH] Medianet: Using mtype for getting bidtype (#3048) * Adds gpp, gdpr params for usersync and gzip compression for medianet adapter * Updates medianet adapter to read bidtype from mtype and ext.prebid.type * retrigger checks * Fixed checkstyle errors * Fixes invalid mtype exception handling --------- Co-authored-by: Aman Jain Co-authored-by: rajatgoyal2510 --- .../bidder/medianet/MedianetBidder.java | 49 +++++++- .../bidder/medianet/MedianetBidderTest.java | 107 ++++++++++++++++++ 2 files changed, 151 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/medianet/MedianetBidder.java b/src/main/java/org/prebid/server/bidder/medianet/MedianetBidder.java index 9f6181bcdf8..2e88d1becd7 100644 --- a/src/main/java/org/prebid/server/bidder/medianet/MedianetBidder.java +++ b/src/main/java/org/prebid/server/bidder/medianet/MedianetBidder.java @@ -2,6 +2,7 @@ import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Imp; +import com.iab.openrtb.response.Bid; import com.iab.openrtb.response.BidResponse; import com.iab.openrtb.response.SeatBid; import org.apache.commons.collections4.CollectionUtils; @@ -11,12 +12,14 @@ 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.exception.PreBidException; import org.prebid.server.json.DecodeException; import org.prebid.server.json.JacksonMapper; import org.prebid.server.proto.openrtb.ext.response.BidType; import org.prebid.server.util.BidderUtil; import org.prebid.server.util.HttpUtil; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -39,15 +42,21 @@ public Result>> makeHttpRequests(BidRequest bidRequ @Override public final Result> makeBids(BidderCall httpCall, BidRequest bidRequest) { + final BidResponse bidResponse; try { - final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); - return Result.withValues(extractBids(httpCall.getRequest().getPayload(), bidResponse)); + bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); } catch (DecodeException e) { return Result.withError(BidderError.badServerResponse(e.getMessage())); } + + final List errors = new ArrayList<>(); + final List bids = extractBids(httpCall.getRequest().getPayload(), bidResponse, errors); + + return Result.of(bids, errors); } - private static List extractBids(BidRequest bidRequest, BidResponse bidResponse) { + private static List extractBids(BidRequest bidRequest, BidResponse bidResponse, + List errors) { if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) { return Collections.emptyList(); } @@ -59,11 +68,41 @@ private static List extractBids(BidRequest bidRequest, BidResponse bi .filter(Objects::nonNull) .flatMap(Collection::stream) .filter(Objects::nonNull) - .map(bid -> BidderBid.of(bid, resolveBidType(bid.getImpid(), bidRequest.getImp()), currency)) + .map(bid -> makeBidderBid(bid, bidRequest.getImp(), currency, errors)) + .filter(Objects::nonNull) .toList(); } - private static BidType resolveBidType(String impId, List imps) { + private static BidType resolveBidType(Bid bid, List imps) { + final Integer markupType = bid.getMtype(); + if (markupType == null) { + return resolveBidTypeFromImpId(bid.getImpid(), imps); + } + + return switch (markupType) { + case 1 -> BidType.banner; + case 2 -> BidType.video; + case 3 -> BidType.audio; + case 4 -> BidType.xNative; + default -> + throw new PreBidException("Unable to fetch mediaType: %s" + .formatted(bid.getImpid())); + }; + } + + private static BidderBid makeBidderBid(Bid bid, List imps, String cur, List errors) { + final BidType bidType; + try { + bidType = resolveBidType(bid, imps); + } catch (PreBidException e) { + errors.add(BidderError.badServerResponse(e.getMessage())); + return null; + } + + return BidderBid.of(bid, bidType, cur); + } + + private static BidType resolveBidTypeFromImpId(String impId, List imps) { for (Imp imp : imps) { if (Objects.equals(impId, imp.getId())) { if (imp.getBanner() != null) { diff --git a/src/test/java/org/prebid/server/bidder/medianet/MedianetBidderTest.java b/src/test/java/org/prebid/server/bidder/medianet/MedianetBidderTest.java index b19a7539401..fb541c35d5d 100644 --- a/src/test/java/org/prebid/server/bidder/medianet/MedianetBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/medianet/MedianetBidderTest.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Imp; +import com.iab.openrtb.request.Video; import com.iab.openrtb.response.Bid; import com.iab.openrtb.response.BidResponse; import com.iab.openrtb.response.SeatBid; @@ -16,13 +17,17 @@ import org.prebid.server.bidder.model.Result; import org.prebid.server.proto.openrtb.ext.ExtPrebid; +import java.util.ArrayList; import java.util.List; import java.util.function.Function; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +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; public class MedianetBidderTest extends VertxTest { @@ -110,6 +115,88 @@ public void makeBidsShouldReturnBannerBidIfBannerIsPresent() throws JsonProcessi .containsExactly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD")); } + @Test + public void makeBidsShouldReturnCorrespondingMtypesAndAdTypes() throws JsonProcessingException { + final List bids = new ArrayList<>(); + final Bid bid1 = Bid.builder().impid("imp_id").mtype(1).build(); + final Bid bid2 = Bid.builder().impid("imp_id").mtype(2).build(); + final Bid bid3 = Bid.builder().impid("imp_id").mtype(3).build(); + final Bid bid4 = Bid.builder().impid("imp_id").mtype(4).build(); + bids.add(bid1); + bids.add(bid2); + bids.add(bid3); + bids.add(bid4); + + // given + final BidderCall httpCall = sampleHttpCall( + givenBidRequest(), + mapper.writeValueAsString(sampleMultiFormatBidResponse(bids))); + + // when + final Result> result = target.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(4); + final BidderBid bannerBid = BidderBid.of(bid1, banner, "USD"); + final BidderBid videoBid = BidderBid.of(bid2, video, "USD"); + final BidderBid audioBid = BidderBid.of(bid3, audio, "USD"); + final BidderBid xNativeBid = BidderBid.of(bid4, xNative, "USD"); + assertThat(result.getValue()).containsExactlyInAnyOrder(bannerBid, videoBid, audioBid, xNativeBid); + } + + @Test + public void makeBidsShouldReturnAdTypeAccordingToImpressionIfMtypeIsAbsent() throws JsonProcessingException { + // given + final BidderCall httpCall = sampleHttpCall( + givenVideoBidRequest(), + mapper.writeValueAsString(sampleBidResponse(bidBuilder -> bidBuilder.impid("imp_id")))); + + // when + final Result> result = target.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1); + final BidderBid videoBid = BidderBid.of(Bid.builder().impid("imp_id").build(), video, "USD"); + assertThat(result.getValue()).containsExactly(videoBid); + } + + @Test + public void makeBidsShouldReturnBannerAdTypeIfMtypeIsAbsentAndIfNoImpressionIdMatches() + throws JsonProcessingException { + // given + final BidderCall httpCall = sampleHttpCall( + givenVideoBidRequest(), + mapper.writeValueAsString(sampleBidResponse(bidBuilder -> bidBuilder.impid("imp_id2")))); + + // when + final Result> result = target.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1); + final BidderBid bannerBid = BidderBid.of(Bid.builder().impid("imp_id2").build(), banner, "USD"); + assertThat(result.getValue()).containsExactly(bannerBid); + } + + @Test + public void makeBidsShouldReturnErrorIfMtypeIsWrong() throws JsonProcessingException { + // given + final BidderCall httpCall = sampleHttpCall( + givenVideoBidRequest(), + mapper.writeValueAsString(sampleBidResponse(bidBuilder -> bidBuilder.impid("imp_id").mtype(5)))); + + // when + final Result> result = target.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).hasSize(1); + assertThat(result.getValue()).isEmpty(); + final BidderError error = BidderError.badServerResponse("Unable to fetch mediaType: imp_id"); + assertThat(result.getErrors()).containsExactly(error); + } + private static BidResponse sampleBidResponse(Function bidCustomizer) { return BidResponse.builder() @@ -120,6 +207,15 @@ private static BidResponse sampleBidResponse(Function bids) { + return BidResponse.builder() + .cur("USD") + .seatbid(singletonList(SeatBid.builder() + .bid(bids) + .build())) + .build(); + } + private static BidderCall sampleHttpCall(BidRequest bidRequest, String body) { return BidderCall.succeededHttp( HttpRequest.builder().payload(bidRequest).build(), @@ -136,4 +232,15 @@ private static BidRequest givenBidRequest() { .build())) .build(); } + + private static BidRequest givenVideoBidRequest() { + return BidRequest.builder() + .id("request_id") + .imp(singletonList(Imp.builder() + .id("imp_id") + .video(Video.builder().build()) + .ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createObjectNode()))) + .build())) + .build(); + } }