Skip to content

Commit

Permalink
Adxcg: MType Support (#2891)
Browse files Browse the repository at this point in the history
  • Loading branch information
AntoxaAntoxic authored Jan 18, 2024
1 parent 5358485 commit e43a2ae
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 89 deletions.
49 changes: 21 additions & 28 deletions src/main/java/org/prebid/server/bidder/adxcg/AdxcgBidder.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.prebid.server.bidder.adxcg;

import com.iab.openrtb.request.BidRequest;
import com.iab.openrtb.request.Imp;
import com.iab.openrtb.response.Bid;
import com.iab.openrtb.response.BidResponse;
import com.iab.openrtb.response.SeatBid;
Expand Down Expand Up @@ -46,59 +45,53 @@ public Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequ
final List<BidderError> errors = new ArrayList<>();
try {
final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class);
return Result.of(extractBids(bidResponse, bidRequest, errors), errors);
return Result.of(extractBids(bidResponse, errors), errors);
} catch (DecodeException e) {
return Result.withError(BidderError.badServerResponse(e.getMessage()));
}
}

private static List<BidderBid> extractBids(BidResponse bidResponse, BidRequest bidRequest,
List<BidderError> errors) {
private static List<BidderBid> extractBids(BidResponse bidResponse, List<BidderError> errors) {
if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) {
return Collections.emptyList();
}
return bidsFromResponse(bidResponse, bidRequest, errors);
return bidsFromResponse(bidResponse, errors);
}

private static List<BidderBid> bidsFromResponse(BidResponse bidResponse,
BidRequest bidRequest,
List<BidderError> errors) {
private static List<BidderBid> bidsFromResponse(BidResponse bidResponse, List<BidderError> errors) {
return bidResponse.getSeatbid().stream()
.filter(Objects::nonNull)
.map(SeatBid::getBid)
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.map(bid -> createBidderBid(bid, bidRequest, bidResponse, errors))
.map(bid -> makeBid(bid, bidResponse.getCur(), errors))
.filter(Objects::nonNull)
.toList();
}

private static BidderBid createBidderBid(Bid bid,
BidRequest bidRequest,
BidResponse bidResponse,
List<BidderError> errors) {
private static BidderBid makeBid(Bid bid, String currency, List<BidderError> errors) {
try {
final BidType bidType = getBidType(bid, bidRequest.getImp());
return BidderBid.of(bid, bidType, bidResponse.getCur());
final BidType bidType = getBidMediaType(bid);
return BidderBid.of(bid, bidType, currency);
} catch (PreBidException e) {
errors.add(BidderError.badInput(e.getMessage()));
return null;
}
}

private static BidType getBidType(Bid bid, List<Imp> imps) {
final String impId = bid.getImpid();
for (Imp imp : imps) {
if (imp.getId().equals(impId)) {
if (imp.getXNative() != null) {
return BidType.xNative;
} else if (imp.getBanner() != null) {
return BidType.banner;
} else if (imp.getVideo() != null) {
return BidType.video;
}
}
private static BidType getBidMediaType(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 native/banner/video impression " + impId);

return switch (markupType) {
case 1 -> BidType.banner;
case 2 -> BidType.video;
case 3 -> BidType.audio;
case 4 -> BidType.xNative;
default -> throw new PreBidException(
"Unable to fetch mediaType " + bid.getMtype() + " in multi-format: " + bid.getImpid());
};
}
}
126 changes: 67 additions & 59 deletions src/test/java/org/prebid/server/bidder/adxcg/AdxcgBidderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
import com.iab.openrtb.request.BidRequest;
import com.iab.openrtb.request.Format;
import com.iab.openrtb.request.Imp;
import com.iab.openrtb.request.Native;
import com.iab.openrtb.request.Video;
import com.iab.openrtb.response.Bid;
import com.iab.openrtb.response.BidResponse;
import com.iab.openrtb.response.SeatBid;
Expand All @@ -21,7 +19,7 @@
import org.prebid.server.proto.openrtb.ext.ExtPrebid;
import org.prebid.server.proto.openrtb.ext.request.adxcg.ExtImpAdxcg;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;

Expand All @@ -30,6 +28,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.prebid.server.proto.openrtb.ext.response.BidType.audio;
import static org.prebid.server.proto.openrtb.ext.response.BidType.banner;
import static org.prebid.server.proto.openrtb.ext.response.BidType.video;
import static org.prebid.server.proto.openrtb.ext.response.BidType.xNative;
Expand Down Expand Up @@ -83,8 +82,7 @@ public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() {
@Test
public void makeBidsShouldReturnEmptyListIfBidResponseSeatBidIsNull() throws JsonProcessingException {
// given
final BidderCall<BidRequest> httpCall = givenHttpCall(null,
mapper.writeValueAsString(BidResponse.builder().build()));
final BidderCall<BidRequest> httpCall = givenHttpCall(null, givenBidResponse());

// when
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
Expand All @@ -95,89 +93,98 @@ public void makeBidsShouldReturnEmptyListIfBidResponseSeatBidIsNull() throws Jso
}

@Test
public void makeBidsShouldReturnBannerBidIfBannerIsPresent() throws JsonProcessingException {
public void makeBidsShouldReturnBannerWhenMtypeIsOne() throws JsonProcessingException {
// given
final Bid bannerBid = Bid.builder().impid("1").mtype(1).build();

final BidderCall<BidRequest> httpCall = givenHttpCall(
BidRequest.builder()
.imp(singletonList(Imp.builder().id("123").banner(Banner.builder().build()).build()))
.build(),
mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
final BidRequest bidRequest = givenBidRequest(identity());
givenBidRequest(identity()),
givenBidResponse(bannerBid));

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

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue())
.containsOnly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD"));
assertThat(result.getValue()).containsOnly(BidderBid.of(bannerBid, banner, "USD"));

}

@Test
public void makeBidsShouldReturnVideoBidIfVideoIsPresent() throws JsonProcessingException {
public void makeBidsShouldReturnBannerWhenMtypeIsThree() throws JsonProcessingException {
// given
final BidderCall<BidRequest> httpCall = givenHttpCall(
BidRequest.builder()
.imp(singletonList(Imp.builder().id("123").video(Video.builder().build()).build()))
.build(),
mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
final BidRequest bidRequest = givenBidRequest(impBuilder -> impBuilder.video(Video.builder().build())
.banner(null));
final Bid audioBid = Bid.builder().impid("3").mtype(3).build();

final BidderCall<BidRequest> httpCall = givenHttpCall(givenBidRequest(identity()), givenBidResponse(audioBid));

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

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue())
.containsOnly(BidderBid.of(Bid.builder().impid("123").build(), video, "USD"));
assertThat(result.getValue()).containsOnly(BidderBid.of(audioBid, audio, "USD"));
}

@Test
public void makeBidsShouldReturnNativeBidIfNativeIsPresent() throws JsonProcessingException {
public void makeBidsShouldReturnBannerWhenMtypeIsTwo() throws JsonProcessingException {
// given
final BidderCall<BidRequest> httpCall = givenHttpCall(
BidRequest.builder()
.imp(singletonList(Imp.builder().id("123").xNative(Native.builder().build()).build()))
.build(),
mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
final BidRequest bidRequest = givenBidRequest(impBuilder -> impBuilder.xNative(Native.builder().build()));
final Bid videoBid = Bid.builder().impid("2").mtype(2).build();

final BidderCall<BidRequest> httpCall = givenHttpCall(givenBidRequest(identity()), givenBidResponse(videoBid));
// when
final Result<List<BidderBid>> result = target.makeBids(httpCall, bidRequest);
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue())
.containsOnly(BidderBid.of(Bid.builder().impid("123").build(), xNative, "USD"));
assertThat(result.getValue()).containsOnly(BidderBid.of(videoBid, video, "USD"));
}

@Test
public void makeBidsShouldReturnBannerWhenMtypeIsFour() throws JsonProcessingException {
// given
final Bid nativeBid = Bid.builder().impid("4").mtype(4).build();

final BidderCall<BidRequest> httpCall = givenHttpCall(givenBidRequest(identity()), givenBidResponse(nativeBid));

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

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue()).containsOnly(BidderBid.of(nativeBid, xNative, "USD"));
}

@Test
public void makeBidsShouldReturnErrorWhenImpTypeIsNotSupported() throws JsonProcessingException {
// given
final Bid audioBid = Bid.builder().impid("id").mtype(5).build();
final BidderCall<BidRequest> httpCall = givenHttpCall(
givenBidRequest(identity()),
givenBidResponse(audioBid));

// when
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
// then
assertThat(result.getErrors())
.containsExactly(BidderError.badInput("Unable to fetch mediaType 5 in multi-format: id"));
}

@Test
public void makeBidsShouldReturnNativeBidAndErrorIfNativeAndEmptyImpsArePresent() throws JsonProcessingException {
// given
final BidResponse bidResponse = BidResponse.builder()
.cur("USD")
.seatbid(singletonList(SeatBid.builder()
.bid(Arrays.asList(Bid.builder().impid("123").build(),
Bid.builder().impid("12").build()))
.build()))
.build();
final Bid invalidBid = Bid.builder().impid("id1").mtype(5).build();
final Bid validBid = Bid.builder().impid("id2").mtype(1).build();
final BidderCall<BidRequest> httpCall = givenHttpCall(
BidRequest.builder()
.imp(Arrays.asList(Imp.builder().id("123").xNative(Native.builder().build()).build(),
Imp.builder().id("12").build())).build(),
mapper.writeValueAsString(bidResponse));
final BidRequest bidRequest = givenBidRequest(impBuilder -> impBuilder.xNative(Native.builder().build()));
givenBidRequest(identity()),
givenBidResponse(invalidBid, validBid));

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

// then
assertThat(result.getErrors()).hasSize(1)
.extracting(BidderError::getMessage)
.containsExactly("Failed to find native/banner/video impression 12");
assertThat(result.getValue())
.containsOnly(BidderBid.of(Bid.builder().impid("123").build(), xNative, "USD"));
.containsExactly(BidderError.badInput("Unable to fetch mediaType 5 in multi-format: id1"));
assertThat(result.getValue()).containsOnly(BidderBid.of(validBid, banner, "USD"));
}

@Test
Expand Down Expand Up @@ -213,13 +220,14 @@ private static BidderCall<BidRequest> givenHttpCall(BidRequest bidRequest, Strin
HttpResponse.of(200, null, body), null);
}

private static BidResponse givenBidResponse(Function<Bid.BidBuilder, Bid.BidBuilder> bidCustomizer) {
return BidResponse.builder()
.cur("USD")
.seatbid(singletonList(SeatBid.builder()
.bid(singletonList(bidCustomizer.apply(Bid.builder()).build()))
.build()))
.build();
private static String givenBidResponse(Bid... bids) throws JsonProcessingException {
return mapper.writeValueAsString(
BidResponse.builder()
.cur("USD")
.seatbid(bids.length == 0
? Collections.emptyList()
: List.of(SeatBid.builder().bid(List.of(bids)).build()))
.build());
}

private static BidRequest givenBidRequest(Function<Imp.ImpBuilder, Imp.ImpBuilder> impCustomizer) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
"cid": "cid001",
"adm": "adm001",
"h": 320,
"w": 480
"w": 480,
"mtype": 1
}
]
}
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"crid": "crid001",
"w": 480,
"h": 320,
"mtype": 1,
"ext": {
"prebid": {
"type": "banner"
Expand Down

0 comments on commit e43a2ae

Please sign in to comment.