Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 51 additions & 4 deletions src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.prebid.server.bidder.model.HttpRequest;
import org.prebid.server.bidder.model.Result;
import org.prebid.server.bidder.openx.model.OpenxImpType;
import org.prebid.server.bidder.openx.proto.OpenxBidExt;
import org.prebid.server.bidder.openx.proto.OpenxBidResponse;
import org.prebid.server.bidder.openx.proto.OpenxBidResponseExt;
import org.prebid.server.bidder.openx.proto.OpenxRequestExt;
Expand All @@ -29,6 +30,8 @@
import org.prebid.server.proto.openrtb.ext.request.ExtRequest;
import org.prebid.server.proto.openrtb.ext.request.openx.ExtImpOpenx;
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.ExtBidPrebidMeta;
import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebidVideo;
import org.prebid.server.proto.openrtb.ext.response.ExtIgi;
import org.prebid.server.proto.openrtb.ext.response.ExtIgiIgs;
Expand Down Expand Up @@ -270,13 +273,13 @@ private ObjectNode makeImpExt(ObjectNode impExt, boolean addCustomParams) {
return openxImpExt;
}

private static List<BidderBid> extractBids(BidRequest bidRequest, OpenxBidResponse bidResponse) {
private List<BidderBid> extractBids(BidRequest bidRequest, OpenxBidResponse bidResponse) {
return bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())
? Collections.emptyList()
: bidsFromResponse(bidRequest, bidResponse);
}

private static List<BidderBid> bidsFromResponse(BidRequest bidRequest, OpenxBidResponse bidResponse) {
private List<BidderBid> bidsFromResponse(BidRequest bidRequest, OpenxBidResponse bidResponse) {
final Map<String, BidType> impIdToBidType = impIdToBidType(bidRequest);

final String bidCurrency = StringUtils.isNotBlank(bidResponse.getCur())
Expand All @@ -292,11 +295,11 @@ private static List<BidderBid> bidsFromResponse(BidRequest bidRequest, OpenxBidR
.toList();
}

private static BidderBid toBidderBid(Bid bid, Map<String, BidType> impIdToBidType, String bidCurrency) {
private BidderBid toBidderBid(Bid bid, Map<String, BidType> impIdToBidType, String bidCurrency) {
final BidType bidType = getBidType(bid, impIdToBidType);
final ExtBidPrebidVideo videoInfo = bidType == BidType.video ? getVideoInfo(bid) : null;
return BidderBid.builder()
.bid(bid)
.bid(bid.toBuilder().ext(getBidExt(bid)).build())
.type(bidType)
.bidCurrency(bidCurrency)
.videoInfo(videoInfo)
Expand Down Expand Up @@ -334,4 +337,48 @@ private static List<ExtIgi> extractIgi(OpenxBidResponse bidResponse) {

return igs.isEmpty() ? null : Collections.singletonList(ExtIgi.builder().igs(igs).build());
}

private ObjectNode getBidExt(Bid bid) {
final ObjectNode ext = bid.getExt();
if (ext == null) {
return null;
}

final OpenxBidExt openxBidExt = parseOpenxBidExt(ext);
final Integer buyerId = parseStringToInt(openxBidExt.getBuyerId());
final Integer dspId = parseStringToInt(openxBidExt.getDspId());
final Integer brandId = parseStringToInt(openxBidExt.getBrandId());

if (buyerId == null && dspId == null && brandId == null) {
return ext;
}

final ExtBidPrebidMeta meta = ExtBidPrebidMeta.builder()
.networkId(dspId)
.advertiserId(buyerId)
.brandId(brandId)
.build();

final ExtBidPrebid extBidPrebid = ExtBidPrebid.builder().meta(meta).build();

ext.set(PREBID_EXT, mapper.mapper().valueToTree(extBidPrebid));

return ext;
}

private OpenxBidExt parseOpenxBidExt(ObjectNode ext) {
try {
return mapper.mapper().convertValue(ext, OpenxBidExt.class);
} catch (IllegalArgumentException e) {
return OpenxBidExt.builder().build();
}
}

private static Integer parseStringToInt(String value) {
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.prebid.server.bidder.openx.proto;

import lombok.Builder;
import lombok.Value;

@Builder
@Value
public class OpenxBidExt {

String dspId;

String buyerId;

String brandId;
}
150 changes: 150 additions & 0 deletions src/test/java/org/prebid/server/bidder/openx/OpenxBidderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.prebid.server.bidder.model.HttpRequest;
import org.prebid.server.bidder.model.HttpResponse;
import org.prebid.server.bidder.model.Result;
import org.prebid.server.bidder.openx.proto.OpenxBidExt;
import org.prebid.server.bidder.openx.proto.OpenxBidResponse;
import org.prebid.server.bidder.openx.proto.OpenxBidResponseExt;
import org.prebid.server.bidder.openx.proto.OpenxRequestExt;
Expand Down Expand Up @@ -983,6 +984,155 @@ public void makeBidsShouldReturnResultContainingEmptyValueAndErrorsWhenSeatBidEm
.containsOnly(Collections.emptyList(), Collections.emptyList());
}

@Test
public void makeBidShouldReturnBidWithExtPrebidMetaContainingAllFieldsFromBidExt() throws JsonProcessingException {
// given
final ObjectNode bidExt = mapper.valueToTree(OpenxBidExt.builder()
.dspId("1")
.buyerId("2")
.brandId("3")
.build());
final BidderCall<BidRequest> httpCall = givenHttpCall(mapper.writeValueAsString(
BidResponse.builder()
.seatbid(singletonList(SeatBid.builder()
.bid(List.of(
Bid.builder()
.w(200)
.h(150)
.price(BigDecimal.ONE)
.impid("impId1")
.dealid("dealid")
.adm("<div>This is an Ad</div>")
.ext(bidExt)
.build()))
.build()))
.build()));

final BidRequest bidRequest = BidRequest.builder()
.id("bidRequestId")
.imp(List.of(
Imp.builder()
.id("impId1")
.banner(Banner.builder().build())
.build()))
.build();

// when
final CompositeBidderResponse result = target.makeBidderResponse(httpCall, bidRequest);

// then
final ObjectNode expectedExtWithBidMeta = mapper.createObjectNode()
.put("dsp_id", "1")
.put("buyer_id", "2")
.put("brand_id", "3")
.set("prebid", mapper.createObjectNode()
.set("meta", mapper.createObjectNode()
.put("advertiserId", 2)
.put("brandId", 3)
.put("networkId", 1)));
assertThat(result.getErrors()).isEmpty();
assertThat(result.getBids()).hasSize(1)
.extracting(BidderBid::getBid)
.extracting(Bid::getExt)
.containsExactly(expectedExtWithBidMeta);
}

@Test
public void makeBidShouldReturnBidWithExtPrebidMetaContainingBrandIdFieldOnly() throws JsonProcessingException {
// given
final ObjectNode bidExt = mapper.valueToTree(OpenxBidExt.builder()
.brandId("4")
.build());
final BidderCall<BidRequest> httpCall = givenHttpCall(mapper.writeValueAsString(
BidResponse.builder()
.seatbid(singletonList(SeatBid.builder()
.bid(List.of(
Bid.builder()
.w(200)
.h(150)
.price(BigDecimal.ONE)
.impid("impId1")
.dealid("dealid")
.adm("<div>This is an Ad</div>")
.ext(bidExt)
.build()))
.build()))
.build()));

final BidRequest bidRequest = BidRequest.builder()
.id("bidRequestId")
.imp(List.of(
Imp.builder()
.id("impId1")
.banner(Banner.builder().build())
.build()))
.build();

// when
final CompositeBidderResponse result = target.makeBidderResponse(httpCall, bidRequest);

// then
final ObjectNode expectedExtWithBidMeta = mapper.createObjectNode()
.put("brand_id", "4")
.set("prebid", mapper.createObjectNode()
.set("meta", mapper.createObjectNode()
.put("brandId", 4)));
assertThat(result.getErrors()).isEmpty();
assertThat(result.getBids()).hasSize(1)
.extracting(BidderBid::getBid)
.extracting(Bid::getExt)
.containsExactly(expectedExtWithBidMeta);
}

@Test
public void makeBidShouldReturnBidWithExtPrebidMetaNotContainingFieldsWithInvalidValues()
throws JsonProcessingException {
// given
final ObjectNode bidExt = mapper.valueToTree(OpenxBidExt.builder()
.dspId("abc")
.buyerId("xyz")
.brandId("cba")
.build());
final BidderCall<BidRequest> httpCall = givenHttpCall(mapper.writeValueAsString(
BidResponse.builder()
.seatbid(singletonList(SeatBid.builder()
.bid(List.of(
Bid.builder()
.w(200)
.h(150)
.price(BigDecimal.ONE)
.impid("impId1")
.dealid("dealid")
.adm("<div>This is an Ad</div>")
.ext(bidExt)
.build()))
.build()))
.build()));

final BidRequest bidRequest = BidRequest.builder()
.id("bidRequestId")
.imp(List.of(
Imp.builder()
.id("impId1")
.banner(Banner.builder().build())
.build()))
.build();

// when
final CompositeBidderResponse result = target.makeBidderResponse(httpCall, bidRequest);

// then
final ObjectNode expectedExtWithBidMeta = mapper.createObjectNode()
.put("dsp_id", "abc")
.put("buyer_id", "xyz")
.put("brand_id", "cba");
assertThat(result.getErrors()).isEmpty();
assertThat(result.getBids()).hasSize(1)
.extracting(BidderBid::getBid)
.extracting(Bid::getExt)
.containsExactly(expectedExtWithBidMeta);
}

private static Map<String, JsonNode> givenCustomParams(String key, Object values) {
return singletonMap(key, mapper.valueToTree(values));
}
Expand Down
Loading