From 3b8bee32123dc1dc1179cb10296549d86f9699ac Mon Sep 17 00:00:00 2001 From: Anton Babak <76536883+AntoxaAntoxic@users.noreply.github.com> Date: Wed, 27 Mar 2024 11:46:56 +0100 Subject: [PATCH] Adprime: Resolve BidType from Mtype (#3036) --- .../server/bidder/adprime/AdprimeBidder.java | 43 ++-- .../bidder/adprime/AdprimeBidderTest.java | 199 +++++++----------- .../adprime/test-adprime-bid-response.json | 5 +- .../test-auction-adprime-response.json | 1 + 4 files changed, 102 insertions(+), 146 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/adprime/AdprimeBidder.java b/src/main/java/org/prebid/server/bidder/adprime/AdprimeBidder.java index 2cb736f2aa3..f78d6c34f49 100644 --- a/src/main/java/org/prebid/server/bidder/adprime/AdprimeBidder.java +++ b/src/main/java/org/prebid/server/bidder/adprime/AdprimeBidder.java @@ -122,36 +122,34 @@ public final Result> makeBids(BidderCall httpCall, B try { final List errors = new ArrayList<>(); final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); - return Result.of(extractBids(httpCall.getRequest().getPayload(), bidResponse, errors), errors); + return Result.of(extractBids(bidResponse, errors), errors); } catch (DecodeException | PreBidException e) { return Result.withError(BidderError.badServerResponse(e.getMessage())); } } - private static List extractBids(BidRequest bidRequest, BidResponse bidResponse, - List errors) { + private static List extractBids(BidResponse bidResponse, List errors) { if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) { return Collections.emptyList(); } - return bidsFromResponse(bidRequest, bidResponse, errors); + return bidsFromResponse(bidResponse, errors); } - private static List bidsFromResponse(BidRequest bidRequest, BidResponse bidResponse, - List errors) { + private static List bidsFromResponse(BidResponse bidResponse, List errors) { return bidResponse.getSeatbid().stream() .filter(Objects::nonNull) .map(SeatBid::getBid) .filter(Objects::nonNull) .flatMap(Collection::stream) - .map(bid -> resolveBidderBid(bid, bidResponse.getCur(), bidRequest.getImp(), errors)) + .map(bid -> resolveBidderBid(bid, bidResponse.getCur(), errors)) .filter(Objects::nonNull) .toList(); } - private static BidderBid resolveBidderBid(Bid bid, String currency, List imps, List errors) { + private static BidderBid resolveBidderBid(Bid bid, String currency, List errors) { final BidType bidType; try { - bidType = getBidType(bid.getImpid(), imps); + bidType = getBidType(bid); } catch (PreBidException e) { errors.add(BidderError.badServerResponse(e.getMessage())); return null; @@ -159,22 +157,19 @@ private static BidderBid resolveBidderBid(Bid bid, String currency, List im return BidderBid.of(bid, bidType, currency); } - private static BidType getBidType(String impId, List imps) { - for (Imp imp : imps) { - if (imp.getId().equals(impId)) { - if (imp.getBanner() != null) { - return BidType.banner; - } - if (imp.getVideo() != null) { - return BidType.video; - } - if (imp.getXNative() != null) { - return BidType.xNative; - } - throw new PreBidException("Unknown impression type for ID: '%s'".formatted(impId)); - } + private static BidType getBidType(Bid bid) { + final Integer markupType = bid.getMtype(); + if (markupType == null) { + throw new PreBidException("Missing MType for bid: " + bid.getId()); } - throw new PreBidException("Failed to find impression for ID: '%s'".formatted(impId)); + + return switch (markupType) { + case 1 -> BidType.banner; + case 2 -> BidType.video; + case 4 -> BidType.xNative; + default -> throw new PreBidException( + "Unable to fetch mediaType " + bid.getMtype() + " in multi-format: " + bid.getImpid()); + }; } } diff --git a/src/test/java/org/prebid/server/bidder/adprime/AdprimeBidderTest.java b/src/test/java/org/prebid/server/bidder/adprime/AdprimeBidderTest.java index 73a257cfc2c..fd7532c8eaa 100644 --- a/src/test/java/org/prebid/server/bidder/adprime/AdprimeBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/adprime/AdprimeBidderTest.java @@ -1,15 +1,13 @@ package org.prebid.server.bidder.adprime; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TextNode; -import com.iab.openrtb.request.Banner; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Imp; -import com.iab.openrtb.request.Native; import com.iab.openrtb.request.Site; import com.iab.openrtb.request.User; -import com.iab.openrtb.request.Video; import com.iab.openrtb.response.Bid; import com.iab.openrtb.response.BidResponse; import com.iab.openrtb.response.SeatBid; @@ -25,12 +23,12 @@ import org.prebid.server.proto.openrtb.ext.request.adprime.ExtImpAdprime; import java.util.List; -import java.util.function.Function; +import java.util.function.UnaryOperator; +import java.util.stream.Stream; -import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; -import static java.util.function.Function.identity; +import static java.util.function.UnaryOperator.identity; 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.banner; @@ -51,8 +49,9 @@ public void creationShouldFailOnInvalidEndpointUrl() { @Test public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() { // given - final BidRequest bidRequest = givenBidRequestWithDefaultBidRequest(impCustomizer -> impCustomizer - .ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode())))); + final ArrayNode givenImpExt = mapper.createArrayNode(); + final BidRequest bidRequest = givenBidRequest( + impCustomizer -> impCustomizer.ext(mapper.valueToTree(ExtPrebid.of(null, givenImpExt)))); // when final Result>> result = target.makeHttpRequests(bidRequest); @@ -66,11 +65,8 @@ public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() { @Test public void makeHttpRequestsWithNoKeywordsAndAudiencesReturnsNullSiteAndUser() { // given - final BidRequest bidRequest = givenBidRequestWithDefaultBidRequest(impCustomizer -> impCustomizer - .ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpAdprime.of("otherTagId", - null, - null))))); + final ExtImpAdprime impExt = ExtImpAdprime.of("otherTagId", null, null); + final BidRequest bidRequest = givenBidRequest(impCustomizer -> impCustomizer.ext(givenImpExt(impExt))); // when final Result>> result = target.makeHttpRequests(bidRequest); @@ -90,14 +86,12 @@ public void makeHttpRequestsWithNoKeywordsAndAudiencesReturnsNullSiteAndUser() { @Test public void makeHttpRequestsWithSiteUserAndAudiencesShouldReturnUserWithCustomData() { // given - final BidRequest bidRequest = givenBidRequest( - bidRequestCustomizer -> bidRequestCustomizer - .site(Site.builder().build()) - .user(User.builder().build()), - singletonList(impCustomizer -> impCustomizer.ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpAdprime.of("otherTagId", - null, - List.of("audience1"))))))); + final ExtImpAdprime impExt = ExtImpAdprime.of("otherTagId", null, List.of("audience1")); + final BidRequest bidRequest = givenBidRequest(impCustomizer -> impCustomizer.ext(givenImpExt(impExt))) + .toBuilder() + .site(Site.builder().build()) + .user(User.builder().build()) + .build(); // when final Result>> result = target.makeHttpRequests(bidRequest); @@ -114,13 +108,11 @@ public void makeHttpRequestsWithSiteUserAndAudiencesShouldReturnUserWithCustomDa @Test public void makeHttpRequestsWithNoUserShouldReturnNewUserWithAudiences() { // given - final BidRequest bidRequest = givenBidRequest( - bidRequestCustomizer -> bidRequestCustomizer - .site(Site.builder().build()), - singletonList(impCustomizer -> impCustomizer.ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpAdprime.of("otherTagId", - null, - List.of("audience1"))))))); + final ExtImpAdprime impExt = ExtImpAdprime.of("otherTagId", null, List.of("audience1")); + final BidRequest bidRequest = givenBidRequest(impCustomizer -> impCustomizer.ext(givenImpExt(impExt))) + .toBuilder() + .site(Site.builder().build()) + .build(); // when final Result>> result = target.makeHttpRequests(bidRequest); @@ -137,12 +129,11 @@ public void makeHttpRequestsWithNoUserShouldReturnNewUserWithAudiences() { @Test public void makeHttpRequestsWithSiteAndKeywordsShouldReturnSiteWithKeywords() { // given - final BidRequest bidRequest = givenBidRequest(bidRequestCustomizer -> bidRequestCustomizer - .site(Site.builder().build()), - singletonList(impCustomizer -> impCustomizer.ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpAdprime.of("otherTagId", - List.of("keyword1"), - null)))))); + final ExtImpAdprime impExt = ExtImpAdprime.of("otherTagId", List.of("keyword1"), null); + final BidRequest bidRequest = givenBidRequest(impCustomizer -> impCustomizer.ext(givenImpExt(impExt))) + .toBuilder() + .site(Site.builder().build()) + .build(); // when final Result>> result = target.makeHttpRequests(bidRequest); @@ -159,22 +150,24 @@ public void makeHttpRequestsWithSiteAndKeywordsShouldReturnSiteWithKeywords() { @Test public void makeHttpRequestsShouldReturnExpectedBidRequest() { // given - final BidRequest bidRequest = givenBidRequest(bidRequestCustomizer -> bidRequestCustomizer - .site(Site.builder().keywords("keyword1,keyword2").build()) - .user(User.builder().customdata("audience1,audience2").build()), - singletonList(impCustomizer -> impCustomizer.tagid("someTagId"))); + final BidRequest bidRequest = givenBidRequest(impCustomizer -> impCustomizer.tagid("someTagId")) + .toBuilder() + .site(Site.builder().keywords("keyword1,keyword2").build()) + .user(User.builder().customdata("audience1,audience2").build()) + .build(); final ObjectNode modifiedImpExtBidder = createModifiedImpExtBidder(); // when final Result>> result = target.makeHttpRequests(bidRequest); // then - final BidRequest expectedRequest = givenBidRequest(bidRequestCustomizer -> bidRequestCustomizer - .site(Site.builder().keywords("keyword1,keyword2").build()) - .user(User.builder().customdata("audience1,audience2").build()), - singletonList(impCustomizer -> impCustomizer - .tagid("someTagId") - .ext(mapper.createObjectNode().set("bidder", modifiedImpExtBidder)))); + final BidRequest expectedRequest = givenBidRequest(impCustomizer -> impCustomizer + .tagid("someTagId") + .ext(mapper.createObjectNode().set("bidder", modifiedImpExtBidder))) + .toBuilder() + .site(Site.builder().keywords("keyword1,keyword2").build()) + .user(User.builder().customdata("audience1,audience2").build()) + .build(); assertThat(result.getErrors()).isEmpty(); assertThat(result.getValue()).hasSize(1) @@ -185,9 +178,10 @@ public void makeHttpRequestsShouldReturnExpectedBidRequest() { @Test public void makeHttpRequestsShouldMakeOneRequestPerImp() { // given - final BidRequest bidRequest = givenBidRequestWithDefaultBidRequest(identity(), impCustomizer -> impCustomizer - .ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpAdprime.of("otherTagId", emptyList(), emptyList()))))); + final ExtImpAdprime impExt = ExtImpAdprime.of("otherTagId", emptyList(), emptyList()); + final BidRequest bidRequest = givenBidRequest( + identity(), + impCustomizer -> impCustomizer.ext(givenImpExt(impExt))); // when final Result>> result = target.makeHttpRequests(bidRequest); @@ -204,7 +198,7 @@ public void makeHttpRequestsShouldMakeOneRequestPerImp() { @Test public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() { // given - final BidderCall httpCall = givenHttpCall(null, "invalid"); + final BidderCall httpCall = givenHttpCall("invalid"); // when final Result> result = target.makeBids(httpCall, null); @@ -219,7 +213,7 @@ public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() { @Test public void makeBidsShouldReturnEmptyListIfBidResponseIsNull() throws JsonProcessingException { // given - final BidderCall httpCall = givenHttpCall(null, mapper.writeValueAsString(null)); + final BidderCall httpCall = givenHttpCall(mapper.writeValueAsString(null)); // when final Result> result = target.makeBids(httpCall, null); @@ -232,8 +226,7 @@ public void makeBidsShouldReturnEmptyListIfBidResponseIsNull() throws JsonProces @Test public void makeBidsShouldReturnEmptyListIfBidResponseSeatBidIsNull() throws JsonProcessingException { // given - final BidderCall httpCall = givenHttpCall(null, - mapper.writeValueAsString(BidResponse.builder().build())); + final BidderCall httpCall = givenHttpCall(mapper.writeValueAsString(BidResponse.builder().build())); // when final Result> result = target.makeBids(httpCall, null); @@ -244,109 +237,71 @@ public void makeBidsShouldReturnEmptyListIfBidResponseSeatBidIsNull() throws Jso } @Test - public void makeBidsShouldReturnErrorIfNoBidTypeIsPresent() throws JsonProcessingException { + public void makeBidsShouldReturnBannerBidSuccessfully() throws JsonProcessingException { // given - final BidderCall httpCall = givenHttpCall( - givenBidRequestWithDefaultBidRequest(impCustomizer -> impCustomizer.id("123")), - mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("123")))); + final Bid bannerBid = Bid.builder().impid("1").mtype(1).build(); - // when - final Result> result = target.makeBids(httpCall, null); - - // then - assertThat(result.getErrors().get(0).getMessage()).startsWith("Unknown impression type for ID:"); - - } - - @Test - public void makeBidsShouldReturnVideoBidIfNoBannerAndHasVideo() throws JsonProcessingException { - // given - final BidderCall httpCall = givenHttpCall( - givenBidRequestWithDefaultBidRequest(impCustomizer -> impCustomizer - .id("123").video(Video.builder().build())), - mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("123")))); + final BidderCall httpCall = givenHttpCall(givenBidResponse(bannerBid)); // when final Result> result = target.makeBids(httpCall, null); // then - final BidderBid expectedBidderBid = BidderBid.of(Bid.builder().impid("123").build(), video, "USD"); - assertThat(result.getErrors()).isEmpty(); - assertThat(result.getValue()).containsExactly(expectedBidderBid); + assertThat(result.getValue()).containsOnly(BidderBid.of(bannerBid, banner, "USD")); + } @Test - public void makeBidsShouldReturnNativeBidIfHasNativeInImpression() throws JsonProcessingException { + public void makeBidsShouldReturnVideoBidSuccessfully() throws JsonProcessingException { // given - final BidderCall httpCall = givenHttpCall( - givenBidRequestWithDefaultBidRequest(impCustomizer -> impCustomizer - .id("123").xNative(Native.builder().build())), - mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("123")))); + final Bid videoBid = Bid.builder().impid("2").mtype(2).build(); + final BidderCall httpCall = givenHttpCall(givenBidResponse(videoBid)); // when final Result> result = target.makeBids(httpCall, null); // then - final BidderBid expectedBidderBid = BidderBid.of(Bid.builder().impid("123").build(), xNative, "USD"); - assertThat(result.getErrors()).isEmpty(); - assertThat(result.getValue()).containsExactly(expectedBidderBid); + assertThat(result.getValue()).containsOnly(BidderBid.of(videoBid, video, "USD")); } @Test - public void makeBidsShouldReturnBannerBidIfHasBothBannerAndVideo() throws JsonProcessingException { + public void makeBidsShouldReturnNativeBidSuccessfully() throws JsonProcessingException { // given - final BidderCall httpCall = givenHttpCall( - givenBidRequest(identity(), - singletonList(impCustomizer -> impCustomizer - .banner(Banner.builder().build()) - .video(Video.builder().build()))), - mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("123")))); + final Bid nativeBid = Bid.builder().impid("4").mtype(4).build(); + + final BidderCall httpCall = givenHttpCall(givenBidResponse(nativeBid)); // when final Result> result = target.makeBids(httpCall, null); // then - final BidderBid expectedBidderBid = BidderBid.of(Bid.builder().impid("123").build(), banner, "USD"); - assertThat(result.getErrors()).isEmpty(); - assertThat(result.getValue()).containsExactly(expectedBidderBid); + assertThat(result.getValue()).containsOnly(BidderBid.of(nativeBid, xNative, "USD")); } @Test - public void makeBidsShouldThrowExcceptionIsNoImpressionWithIdFound() throws JsonProcessingException { + public void makeBidsShouldReturnErrorWhenImpTypeIsNotSupported() throws JsonProcessingException { // given - final BidderCall httpCall = givenHttpCall( - givenBidRequestWithDefaultBidRequest(identity(), identity()), - mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("321")))); + final Bid audioBid = Bid.builder().impid("id").mtype(3).build(); + final BidderCall httpCall = givenHttpCall(givenBidResponse(audioBid)); // when final Result> result = target.makeBids(httpCall, null); - // then - assertThat(result.getErrors()).hasSize(1); - assertThat(result.getErrors().get(0).getMessage()).startsWith("Failed to find impression"); - } - - private static BidRequest givenBidRequest( - Function bidRequestCustomizer, - List> impCustomizers) { - - return bidRequestCustomizer.apply(BidRequest.builder() - .imp(impCustomizers.stream() - .map(AdprimeBidderTest::givenImp) - .toList())) - .build(); + assertThat(result.getErrors()) + .containsExactly(BidderError.badServerResponse("Unable to fetch mediaType 3 in multi-format: id")); } @SafeVarargs - private static BidRequest givenBidRequestWithDefaultBidRequest(Function... impCustomizers) { - return givenBidRequest(identity(), asList(impCustomizers)); + private static BidRequest givenBidRequest(UnaryOperator... impCustomizers) { + return BidRequest.builder() + .imp(Stream.of(impCustomizers).map(AdprimeBidderTest::givenImp).toList()) + .build(); } - private static Imp givenImp(Function impCustomizer) { + private static Imp givenImp(UnaryOperator impCustomizer) { return impCustomizer.apply(Imp.builder() .id("123") .ext(mapper.valueToTree(ExtPrebid.of(null, @@ -356,21 +311,25 @@ private static Imp givenImp(Function impCustomiz .build(); } - private static BidResponse givenBidResponse( - Function bidCustomizer) { - return BidResponse.builder() + private static String givenBidResponse(Bid bid) throws JsonProcessingException { + return mapper.writeValueAsString(BidResponse.builder() .cur("USD") .seatbid(singletonList(SeatBid.builder() - .bid(singletonList(bidCustomizer.apply(Bid.builder()).build())) + .bid(singletonList(bid)) .build())) - .build(); + .build()); } - private static BidderCall givenHttpCall(BidRequest bidRequest, String body) { - return BidderCall.succeededHttp(HttpRequest.builder().payload(bidRequest).build(), + private static BidderCall givenHttpCall(String body) { + return BidderCall.succeededHttp( + HttpRequest.builder().payload(BidRequest.builder().build()).build(), HttpResponse.of(200, null, body), null); } + private ObjectNode givenImpExt(ExtImpAdprime impExt) { + return mapper.valueToTree(ExtPrebid.of(null, impExt)); + } + private ObjectNode createModifiedImpExtBidder() { final ObjectNode modifiedImpExtBidder = mapper.createObjectNode(); modifiedImpExtBidder.set("TagID", TextNode.valueOf("someTagId")); diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adprime/test-adprime-bid-response.json b/src/test/resources/org/prebid/server/it/openrtb2/adprime/test-adprime-bid-response.json index 35a9d7ca47f..0c39fe64552 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adprime/test-adprime-bid-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adprime/test-adprime-bid-response.json @@ -10,10 +10,11 @@ "crid": "crid001", "adm": "adm001", "h": 250, - "w": 300 + "w": 300, + "mtype": 1 } ] } ], "bidid": "bid_id" -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adprime/test-auction-adprime-response.json b/src/test/resources/org/prebid/server/it/openrtb2/adprime/test-auction-adprime-response.json index 157e64739a3..5b7e94562fd 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adprime/test-auction-adprime-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adprime/test-auction-adprime-response.json @@ -11,6 +11,7 @@ "crid": "crid001", "w": 300, "h": 250, + "mtype": 1, "ext": { "prebid": { "type": "banner"