Skip to content

Commit

Permalink
Unruly: Add duration to video bids (#3345)
Browse files Browse the repository at this point in the history
  • Loading branch information
SerhiiNahornyi committed Aug 5, 2024
1 parent 9a919bc commit 42b0883
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 14 deletions.
52 changes: 44 additions & 8 deletions src/main/java/org/prebid/server/bidder/unruly/UnrulyBidder.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.prebid.server.bidder.unruly;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.iab.openrtb.request.BidRequest;
import com.iab.openrtb.request.Imp;
import com.iab.openrtb.response.Bid;
Expand All @@ -19,6 +21,8 @@
import org.prebid.server.proto.openrtb.ext.ExtPrebid;
import org.prebid.server.proto.openrtb.ext.request.unruly.ExtImpUnruly;
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.BidderUtil;
import org.prebid.server.util.HttpUtil;

Expand Down Expand Up @@ -84,18 +88,18 @@ public Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequ
}
}

private static List<BidderBid> extractBids(BidRequest bidRequest,
BidResponse bidResponse,
List<BidderError> errors) {
private List<BidderBid> extractBids(BidRequest bidRequest,
BidResponse bidResponse,
List<BidderError> errors) {

return bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())
? Collections.emptyList()
: bidsFromResponse(bidRequest, bidResponse, errors);
}

private static List<BidderBid> bidsFromResponse(BidRequest bidRequest,
BidResponse bidResponse,
List<BidderError> errors) {
private List<BidderBid> bidsFromResponse(BidRequest bidRequest,
BidResponse bidResponse,
List<BidderError> errors) {

return bidResponse.getSeatbid().stream()
.filter(Objects::nonNull)
Expand All @@ -107,9 +111,13 @@ private static List<BidderBid> bidsFromResponse(BidRequest bidRequest,
.toList();
}

private static BidderBid resolveBidderBid(Bid bid, String currency, List<Imp> imps, List<BidderError> errors) {
private BidderBid resolveBidderBid(Bid bid, String currency, List<Imp> imps, List<BidderError> errors) {
try {
return BidderBid.of(bid, getBidType(bid.getImpid(), imps), currency);
final BidType bidType = getBidType(bid.getImpid(), imps);
return BidderBid.of(
bidType == BidType.video ? resolveBid(bid) : bid,
getBidType(bid.getImpid(), imps),
currency);
} catch (PreBidException e) {
errors.add(BidderError.badServerResponse(e.getMessage()));
return BidderBid.of(bid, BidType.banner, currency);
Expand All @@ -135,4 +143,32 @@ private static BidType getBidType(String impId, List<Imp> imps) {
throw new PreBidException(
"Bid response imp ID " + impId + " not found in bid request containing imps" + unmatchedImpIds);
}

private Bid resolveBid(Bid bid) {
final Integer duration = bid.getDur();
if (duration == null || duration <= 0) {
return bid;
}

return bid.toBuilder().ext(resolveBidExt(duration, bid.getExt())).build();
}

private ObjectNode resolveBidExt(Integer duration, ObjectNode bidExt) {
final ObjectNode bidExtUpdated = bidExt != null && !bidExt.isMissingNode()
? bidExt
: mapper.mapper().createObjectNode();
final JsonNode bidExtPrebid = resolveBidExtPrebid(duration, bidExtUpdated.get("prebid"));

return bidExtUpdated.set("prebid", bidExtPrebid);
}

private ObjectNode resolveBidExtPrebid(Integer duration, JsonNode bidExtPrebid) {
final ExtBidPrebidVideo extBidPrebidVideo = ExtBidPrebidVideo.of(duration, null);
if (bidExtPrebid == null || bidExtPrebid.isMissingNode()) {
return mapper.mapper().valueToTree(ExtBidPrebid.builder().video(extBidPrebidVideo).build());
}

final ObjectNode bidExtPrebidCasted = (ObjectNode) bidExtPrebid;
return bidExtPrebidCasted.set("video", mapper.mapper().valueToTree(extBidPrebidVideo));
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
package org.prebid.server.proto.openrtb.ext.response;

import lombok.AllArgsConstructor;
import lombok.Value;

/**
* Defines the contract for bidresponse.seatbid.bid[i].ext.prebid.video
*/
@AllArgsConstructor(staticName = "of")
@Value
@Value(staticConstructor = "of")
public class ExtBidPrebidVideo {

Integer duration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.IntNode;
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;
Expand Down Expand Up @@ -166,6 +167,88 @@ public void makeBidsShouldReturnBannerBid() throws JsonProcessingException {
.containsExactly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD"));
}

@Test
public void makeBidsShouldSetDurationFromBidDurField() throws JsonProcessingException {
// given
final BidderCall<BidRequest> httpCall = givenHttpCall(
BidRequest.builder()
.imp(singletonList(Imp.builder().video(Video.builder().build()).id("123").build()))
.build(),
mapper.writeValueAsString(
givenBidResponse(bidBuilder -> bidBuilder.dur(12).impid("123"))));

// when
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue())
.extracting(BidderBid::getBid)
.extracting(Bid::getExt)
.extracting(bidExt -> bidExt.get("prebid"))
.extracting(bidExtPrebid -> bidExtPrebid.get("video"))
.extracting(bidExtPrebidVideo -> bidExtPrebidVideo.get("duration"))
.containsExactly(IntNode.valueOf(12));
}

@Test
public void makeBidsShouldSetDurationFromBidDurFieldAndTolerateExistingBidExt() throws JsonProcessingException {
// given
final ObjectNode givenBidExt = mapper.createObjectNode();
final ObjectNode givenBidExtPrebid = mapper.createObjectNode();
givenBidExtPrebid.set("property1", new TextNode("someValue"));
givenBidExt.set("prebid", givenBidExtPrebid);
final BidderCall<BidRequest> httpCall = givenHttpCall(
BidRequest.builder()
.imp(singletonList(Imp.builder().video(Video.builder().build()).id("123").build()))
.build(),
mapper.writeValueAsString(
givenBidResponse(bidBuilder -> bidBuilder
.ext(givenBidExt)
.dur(12)
.impid("123"))));

// when
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);

// then
final ObjectNode expectedBidExt = mapper.createObjectNode();
final ObjectNode expectedBidExtPrebid = mapper.createObjectNode();
expectedBidExtPrebid.set("property1", new TextNode("someValue"));
final ObjectNode expectedBidExtPrebidVideo = mapper.createObjectNode();
expectedBidExtPrebidVideo.set("duration", IntNode.valueOf(12));
expectedBidExtPrebid.set("property1", new TextNode("someValue"));
expectedBidExtPrebid.set("video", expectedBidExtPrebidVideo);
expectedBidExt.set("prebid", expectedBidExtPrebid);

assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue())
.extracting(BidderBid::getBid)
.extracting(Bid::getExt)
.containsExactly(expectedBidExt);
}

@Test
public void makeBidsShouldNotSetDurationFromBidDurFieldForBannerBids() throws JsonProcessingException {
// given
final BidderCall<BidRequest> httpCall = givenHttpCall(
BidRequest.builder()
.imp(singletonList(Imp.builder().banner(Banner.builder().build()).id("123").build()))
.build(),
mapper.writeValueAsString(
givenBidResponse(bidBuilder -> bidBuilder.dur(12).impid("123"))));

// when
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue())
.extracting(BidderBid::getBid)
.extracting(Bid::getExt)
.containsOnlyNulls();
}

@Test
public void makeBidsShouldReturnErrorNotFoundBidRequest() throws JsonProcessingException {
// given
Expand Down

0 comments on commit 42b0883

Please sign in to comment.