diff --git a/src/main/java/org/prebid/server/bidder/ix/IxBidder.java b/src/main/java/org/prebid/server/bidder/ix/IxBidder.java index e12efde3deb..cac81a91cec 100644 --- a/src/main/java/org/prebid/server/bidder/ix/IxBidder.java +++ b/src/main/java/org/prebid/server/bidder/ix/IxBidder.java @@ -1,6 +1,8 @@ package org.prebid.server.bidder.ix; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.Banner; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Format; @@ -24,6 +26,8 @@ import org.prebid.server.proto.openrtb.ext.ExtPrebid; import org.prebid.server.proto.openrtb.ext.request.ix.ExtImpIx; 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.ExtBidPrebidVideo; import org.prebid.server.util.HttpUtil; import java.util.ArrayList; @@ -194,21 +198,52 @@ public Result> makeBids(HttpCall httpCall, BidReques } } - private static List extractBids(BidResponse bidResponse, BidRequest bidRequest) { + private List extractBids(BidResponse bidResponse, BidRequest bidRequest) { return bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid()) ? Collections.emptyList() : bidsFromResponse(bidResponse, bidRequest); } - private static List bidsFromResponse(BidResponse bidResponse, BidRequest bidRequest) { + private List bidsFromResponse(BidResponse bidResponse, BidRequest bidRequest) { return bidResponse.getSeatbid().stream() + .filter(Objects::nonNull) .map(SeatBid::getBid) + .filter(Objects::nonNull) .flatMap(Collection::stream) - .map(bid -> prepareBid(bid, bidRequest)) - .map(bid -> BidderBid.of(bid, getBidType(bid.getImpid(), bidRequest.getImp()), bidResponse.getCur())) + .map(bid -> toBidderBid(bid, bidRequest, bidResponse)) .collect(Collectors.toList()); } + private BidderBid toBidderBid(Bid bid, BidRequest bidRequest, BidResponse bidResponse) { + final BidType bidType = getBidType(bid.getImpid(), bidRequest.getImp()); + final ObjectNode bidExt = bid.getExt(); + final ExtBidPrebid extPrebid = bidExt != null ? parseBidExt(bidExt) : null; + final ExtBidPrebidVideo extVideo = extPrebid != null ? extPrebid.getVideo() : null; + final boolean bidHasNoSizes = bid.getH() == null || bid.getW() == null; + final Banner banner = bidRequest.getImp().get(0).getBanner(); + + if ((bidHasNoSizes && banner != null) || (extVideo != null)) { + final Bid.BidBuilder bidBuilder = bid.toBuilder(); + + if (bidType == BidType.banner && bidHasNoSizes && banner != null) { + bidBuilder + .w(banner.getW()) + .h(banner.getH()) + .build(); + } + + if (bidType == BidType.video && extVideo != null) { + bidBuilder.ext(resolveBidExt(extVideo.getDuration())); + if (CollectionUtils.isEmpty(bid.getCat())) { + bidBuilder.cat(Collections.singletonList(extVideo.getPrimaryCategory())).build(); + } + } + bid = bidBuilder.build(); + } + + return BidderBid.of(bid, bidType, bidResponse.getCur()); + } + private static BidType getBidType(String impId, List imps) { for (Imp imp : imps) { if (imp.getId().equals(impId)) { @@ -226,16 +261,17 @@ private static BidType getBidType(String impId, List imps) { throw new PreBidException(String.format("Unmatched impression id %s", impId)); } - private static Bid prepareBid(Bid bid, BidRequest bidRequest) { - // Current implementation ensure that we have at least one imp in request - final boolean bidHasNoSizes = bid.getH() == null || bid.getW() == null; - final Banner banner = bidRequest.getImp().get(0).getBanner(); - if (bidHasNoSizes && banner != null) { - return bid.toBuilder() - .w(banner.getW()) - .h(banner.getH()) - .build(); + private ExtBidPrebid parseBidExt(ObjectNode bidExt) { + try { + return mapper.mapper().treeToValue(bidExt, ExtBidPrebid.class); + } catch (JsonProcessingException e) { + throw new PreBidException(e.getMessage()); } - return bid; + } + + private ObjectNode resolveBidExt(Integer duration) { + return mapper.mapper().valueToTree(ExtBidPrebid.builder() + .video(ExtBidPrebidVideo.of(duration, null)) + .build()); } } diff --git a/src/main/resources/bidder-config/ix.yaml b/src/main/resources/bidder-config/ix.yaml index 85483b59718..a58ea0263b7 100644 --- a/src/main/resources/bidder-config/ix.yaml +++ b/src/main/resources/bidder-config/ix.yaml @@ -1,7 +1,7 @@ adapters: ix: enabled: false - endpoint: http://rubicon-us-east.lb.indexww.com/transbidder?p=189517 + endpoint: https:// pbs-enforces-gdpr: true pbs-enforces-ccpa: true modifying-vast-xml-allowed: true @@ -22,8 +22,8 @@ adapters: supported-vendors: vendor-id: 10 usersync: - url: - redirect-url: + url: https://ssum.casalemedia.com/usermatchredir?s=189517&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&cb= + redirect-url: /setuid?bidder=ix&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&uid= cookie-family-name: ix type: redirect support-cors: false diff --git a/src/test/java/org/prebid/server/bidder/ix/IxBidderTest.java b/src/test/java/org/prebid/server/bidder/ix/IxBidderTest.java index 0fc5e634268..74977212cc6 100644 --- a/src/test/java/org/prebid/server/bidder/ix/IxBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/ix/IxBidderTest.java @@ -23,6 +23,8 @@ import org.prebid.server.proto.openrtb.ext.ExtPrebid; import org.prebid.server.proto.openrtb.ext.request.ix.ExtImpIx; 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.ExtBidPrebidVideo; import java.util.ArrayList; import java.util.List; @@ -33,6 +35,7 @@ import static java.util.function.Function.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; public class IxBidderTest extends VertxTest { @@ -374,6 +377,36 @@ public void makeBidsShouldReturnBannerBidWithRequestImpSizeWhenBidSizeIsEmpty() .containsOnly(BidderBid.of(Bid.builder().impid("123").w(300).h(200).build(), BidType.banner, "EUR")); } + @Test + public void makeBidsShouldReturnBidWithVideoExt() throws JsonProcessingException { + // given + final Video video = Video.builder().build(); + final HttpCall httpCall = givenHttpCall( + BidRequest.builder() + .imp(singletonList(Imp.builder().id("123").video(video).build())) + .build(), + mapper.writeValueAsString( + givenBidResponse( + bidBuilder -> bidBuilder + .impid("123") + .ext(mapper.valueToTree(ExtBidPrebid.builder() + .video(ExtBidPrebidVideo.of(1, "cat")) + .build()))))); + + // when + final Result> result = ixBidder.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(BidderBid::getBid) + .extracting(Bid::getExt) + .extracting(node -> mapper.treeToValue(node, ExtBidPrebid.class)) + .extracting(ExtBidPrebid::getVideo) + .extracting(ExtBidPrebidVideo::getDuration, ExtBidPrebidVideo::getPrimaryCategory) + .containsExactly(tuple(1, null)); + } + private static BidRequest givenBidRequest( Function bidRequestCustomizer, Function impCustomizer) {