diff --git a/src/main/java/org/prebid/server/bidder/telaria/TelariaBidder.java b/src/main/java/org/prebid/server/bidder/telaria/TelariaBidder.java index 8cc87a89ad0..ee09d8b4c1f 100644 --- a/src/main/java/org/prebid/server/bidder/telaria/TelariaBidder.java +++ b/src/main/java/org/prebid/server/bidder/telaria/TelariaBidder.java @@ -31,11 +31,11 @@ import org.prebid.server.proto.openrtb.ext.response.BidType; import org.prebid.server.util.HttpUtil; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; +import java.util.stream.IntStream; public class TelariaBidder implements Bidder { @@ -53,7 +53,6 @@ public TelariaBidder(String endpointUrl, JacksonMapper mapper) { @Override public Result>> makeHttpRequests(BidRequest bidRequest) { - final List validImps = new ArrayList<>(); try { validateImp(bidRequest.getImp()); } catch (PreBidException e) { @@ -62,37 +61,43 @@ public Result>> makeHttpRequests(BidRequest bidRequ final String publisherId = getPublisherId(bidRequest); String seatCode = null; - final BidRequest.BidRequestBuilder requestBuilder = bidRequest.toBuilder(); ExtImpTelaria extImp = null; - for (Imp imp : bidRequest.getImp()) { - try { - extImp = parseImpExt(imp); - seatCode = extImp.getSeatCode(); - validImps.add(updateImp(imp, extImp, publisherId)); - } catch (PreBidException e) { - return Result.withError(BidderError.badInput(e.getMessage())); - } - } + Imp modifyImp = bidRequest.getImp().get(0); - if (extImp != null && extImp.getExtra() != null) { - requestBuilder.ext(mapper.fillExtension(ExtRequest.empty(), TelariaRequestExt.of(extImp.getExtra()))); + try { + extImp = parseImpExt(modifyImp); + seatCode = extImp.getSeatCode(); + modifyImp = updateImp(modifyImp, extImp, publisherId); + } catch (PreBidException e) { + return Result.withError(BidderError.badInput(e.getMessage())); } + final BidRequest outgoingRequest = updateBidRequest(bidRequest, extImp, seatCode, modifyImp); + return Result.withValue(makeHttpRequest(outgoingRequest)); + } + + private HttpRequest makeHttpRequest(BidRequest bidRequest) { + return HttpRequest.builder() + .method(HttpMethod.POST) + .uri(endpointUrl) + .headers(headers(bidRequest)) + .payload(bidRequest) + .body(mapper.encodeToBytes(bidRequest)) + .build(); + } + + private BidRequest updateBidRequest(BidRequest bidRequest, ExtImpTelaria extImp, String seatCode, Imp modifyImp) { + final BidRequest.BidRequestBuilder bidRequestBuilder = bidRequest.toBuilder(); if (bidRequest.getSite() != null) { - requestBuilder.site(modifySite(bidRequest.getSite(), seatCode)); + bidRequestBuilder.site(modifySite(bidRequest.getSite(), seatCode)); } else if (bidRequest.getApp() != null) { - requestBuilder.app(modifyApp(bidRequest.getApp(), seatCode)); + bidRequestBuilder.app(modifyApp(bidRequest.getApp(), seatCode)); } - final BidRequest outgoingRequest = requestBuilder.imp(validImps).build(); - - return Result.withValue(HttpRequest.builder() - .method(HttpMethod.POST) - .uri(endpointUrl) - .headers(headers(bidRequest)) - .payload(outgoingRequest) - .body(mapper.encodeToBytes(outgoingRequest)) - .build()); + return bidRequestBuilder + .ext(modifyExt(extImp)) + .imp(Collections.singletonList(modifyImp)) + .build(); } private static void validateImp(List imps) { @@ -136,6 +141,12 @@ private Imp updateImp(Imp imp, ExtImpTelaria extImp, String publisherId) { .build(); } + private ExtRequest modifyExt(ExtImpTelaria extImp) { + return extImp != null + ? mapper.fillExtension(ExtRequest.empty(), TelariaRequestExt.of(extImp.getExtra())) + : null; + } + private static Site modifySite(Site site, String seatCode) { return site.toBuilder().publisher(createPublisher(site.getPublisher(), seatCode)).build(); } @@ -169,29 +180,37 @@ private static MultiMap headers(BidRequest bidRequest) { public Result> makeBids(HttpCall httpCall, BidRequest bidRequest) { try { final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); - return Result.withValues(extractBids(bidResponse)); + return Result.withValues(extractBids(bidResponse, bidRequest)); } catch (PreBidException | DecodeException e) { return Result.withError(BidderError.badServerResponse(e.getMessage())); } } - private List extractBids(BidResponse bidResponse) { + private List extractBids(BidResponse bidResponse, BidRequest bidRequest) { if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) { return Collections.emptyList(); } - return bidsFromResponse(bidResponse); + return bidsFromResponse(bidResponse, bidRequest); } - private static List bidsFromResponse(BidResponse bidResponse) { + private static List bidsFromResponse(BidResponse bidResponse, BidRequest bidRequest) { final SeatBid firstSeatBid = bidResponse.getSeatbid().get(0); final List bids = firstSeatBid.getBid(); + final List imps = bidRequest.getImp(); if (CollectionUtils.isEmpty(bids)) { return Collections.emptyList(); } - return bids.stream() - .filter(Objects::nonNull) - .map(bid -> BidderBid.of(bid, BidType.video, bidResponse.getCur())) + + return IntStream.range(0, bids.size()) + .filter(i -> bids.get(i) != null) + .filter(i -> i < imps.size()) + .mapToObj(i -> makeBidderBid(bids.get(i), bidResponse, imps.get(i))) .collect(Collectors.toList()); } + + private static BidderBid makeBidderBid(Bid bid, BidResponse bidResponse, Imp imp) { + final Bid modifyBid = bid.toBuilder().impid(imp.getId()).build(); + return BidderBid.of(modifyBid, BidType.video, bidResponse.getCur()); + } } diff --git a/src/test/java/org/prebid/server/bidder/telaria/TelariaBidderTest.java b/src/test/java/org/prebid/server/bidder/telaria/TelariaBidderTest.java index e47e8a24e9e..67a295c0533 100644 --- a/src/test/java/org/prebid/server/bidder/telaria/TelariaBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/telaria/TelariaBidderTest.java @@ -30,10 +30,10 @@ import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.function.Function; +import java.util.function.UnaryOperator; 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.assertj.core.api.Assertions.tuple; @@ -225,38 +225,6 @@ public void makeHttpRequestsShouldSetSitePublisherIdIfSiteIsPresent() { .containsExactly("seatCode"); } - @Test - public void makeBidsShouldReturnEmptyBidderBidsFromSecondSeatBid() throws JsonProcessingException { - // given - final SeatBid firstSeatBId = SeatBid.builder() - .bid(singletonList(Bid.builder() - .impid("123") - .build())) - .build(); - - final SeatBid secondSeatBid = SeatBid.builder() - .bid(singletonList(Bid.builder() - .impid("456") - .build())) - .build(); - - final HttpCall httpCall = givenHttpCall( - BidRequest.builder() - .imp(singletonList(Imp.builder().id("123").banner(Banner.builder().build()).build())) - .build(), - mapper.writeValueAsString(BidResponse.builder() - .seatbid(Arrays.asList(firstSeatBId, secondSeatBid)) - .build())); - - // when - final Result> result = telariaBidder.makeBids(httpCall, null); - - // then - assertThat(result.getErrors()).isEmpty(); - assertThat(result.getValue()) - .containsExactly(BidderBid.of(Bid.builder().impid("123").build(), video, null)); - } - @Test public void makeHttpRequestsShouldReturnResultWithHttpRequestsContainingExpectedHeaders() { // given @@ -287,6 +255,40 @@ public void makeHttpRequestsShouldReturnResultWithHttpRequestsContainingExpected tuple("DNT", "0")); } + @Test + public void makeBidsShouldReturnEmptyBidderBidsFromSecondSeatBid() throws JsonProcessingException { + // given + final SeatBid firstSeatBId = SeatBid.builder() + .bid(singletonList(Bid.builder() + .impid("123") + .build())) + .build(); + + final SeatBid secondSeatBid = SeatBid.builder() + .bid(singletonList(Bid.builder() + .impid("456") + .build())) + .build(); + + final BidRequest bidRequest = BidRequest.builder() + .imp(singletonList(Imp.builder().id("123").banner(Banner.builder().build()).build())) + .build(); + + final HttpCall httpCall = givenHttpCall( + bidRequest, + mapper.writeValueAsString(BidResponse.builder() + .seatbid(Arrays.asList(firstSeatBId, secondSeatBid)) + .build())); + + // when + final Result> result = telariaBidder.makeBids(httpCall, bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .containsExactly(BidderBid.of(Bid.builder().impid("123").build(), video, null)); + } + @Test public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() { // given @@ -319,14 +321,15 @@ public void makeBidsShouldReturnEmptyListIfBidResponseSeatBidIsNull() throws Jso } @Test - public void makeBidsShouldReturnErrorIfNoBidsFromSeatArePresent() throws JsonProcessingException { + public void makeBidsShouldReturnEmptyListIfNoBidsFromSeatArePresent() throws JsonProcessingException { // given - final HttpCall httpCall = givenHttpCall(null, + final BidRequest bidRequest = BidRequest.builder().build(); + final HttpCall httpCall = givenHttpCall(bidRequest, mapper.writeValueAsString(BidResponse.builder() .seatbid(singletonList(SeatBid.builder().build())).build())); // when - final Result> result = telariaBidder.makeBids(httpCall, null); + final Result> result = telariaBidder.makeBids(httpCall, bidRequest); // then assertThat(result.getErrors()).isEmpty(); @@ -336,15 +339,17 @@ public void makeBidsShouldReturnErrorIfNoBidsFromSeatArePresent() throws JsonPro @Test public void makeBidsShouldReturnVideoBidIfVideoIsPresentInRequestImp() throws JsonProcessingException { // given + final BidRequest bidRequest = BidRequest.builder() + .imp(singletonList(Imp.builder().id("123").video(Video.builder().build()).build())) + .build(); + final HttpCall httpCall = givenHttpCall( - BidRequest.builder() - .imp(singletonList(Imp.builder().id("123").video(Video.builder().build()).build())) - .build(), + bidRequest, mapper.writeValueAsString( givenBidResponse(bidBuilder -> bidBuilder.impid("123")))); // when - final Result> result = telariaBidder.makeBids(httpCall, null); + final Result> result = telariaBidder.makeBids(httpCall, bidRequest); // then assertThat(result.getErrors()).isEmpty(); @@ -352,29 +357,51 @@ public void makeBidsShouldReturnVideoBidIfVideoIsPresentInRequestImp() throws Js .containsExactly(BidderBid.of(Bid.builder().impid("123").build(), video, "USD")); } + @Test + public void makeBidsShouldReturnProperImpidFromBidRequestImpId() throws JsonProcessingException { + // given + final BidRequest bidRequest = BidRequest.builder() + .imp(singletonList(Imp.builder().id("312").video(Video.builder().build()).build())) + .build(); + + final HttpCall httpCall = givenHttpCall( + bidRequest, + mapper.writeValueAsString(givenBidResponse(identity()))); + + // when + final Result> result = telariaBidder.makeBids(httpCall, bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(BidderBid::getBid) + .extracting(Bid::getImpid) + .containsExactly("312"); + } + private static BidRequest givenBidRequest( - Function bidRequestCustomizer, - Function impCustomizer) { + UnaryOperator bidRequestCustomizer, + UnaryOperator impCustomizer) { return bidRequestCustomizer.apply(BidRequest.builder() - .imp(singletonList(givenImp(impCustomizer)))) + .imp(singletonList(givenImp(impCustomizer)))) .build(); } - private static BidRequest givenBidRequest(Function impCustomizer) { + private static BidRequest givenBidRequest(UnaryOperator impCustomizer) { return givenBidRequest(identity(), impCustomizer); } - private static Imp givenImp(Function impCustomizer) { + private static Imp givenImp(UnaryOperator impCustomizer) { return impCustomizer.apply(Imp.builder() - .id("123") - .video(Video.builder().build()) - .ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpTelaria.of("adCode", "seatCode", null))))) + .id("123") + .video(Video.builder().build()) + .ext(mapper.valueToTree(ExtPrebid.of(null, + ExtImpTelaria.of("adCode", "seatCode", null))))) .build(); } - private static BidResponse givenBidResponse(Function bidCustomizer) { + private static BidResponse givenBidResponse(UnaryOperator bidCustomizer) { return BidResponse.builder() .cur("USD") .seatbid(singletonList(SeatBid.builder().bid(singletonList(bidCustomizer.apply(Bid.builder()).build()))