Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Core: Update NextMillennium bidder #3103

Merged
merged 5 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.iab.openrtb.request.Format;
import com.iab.openrtb.request.Imp;
import com.iab.openrtb.request.Site;
import com.iab.openrtb.response.Bid;
import com.iab.openrtb.response.BidResponse;
import com.iab.openrtb.response.SeatBid;
import io.vertx.core.MultiMap;
Expand Down Expand Up @@ -36,6 +37,7 @@
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

public class NextMillenniumBidder implements Bidder<BidRequest> {

Expand Down Expand Up @@ -88,16 +90,28 @@ private ExtImpNextMillennium convertExt(Imp imp, List<BidderError> errors) {
}

private BidRequest updateBidRequest(BidRequest bidRequest, ExtImpNextMillennium ext) {
final ExtRequestPrebid prebid = ExtRequestPrebid.builder()
.storedrequest(ExtStoredRequest.of(resolveStoredRequestId(bidRequest, ext)))
final ExtStoredRequest storedRequest = ExtStoredRequest.of(resolveStoredRequestId(bidRequest, ext));

final ExtRequestPrebid createdExtRequestPrebid = ExtRequestPrebid.builder()
.storedrequest(storedRequest)
.build();
final ExtRequest extRequest = ExtRequest.of(prebid);

final List<Imp> imps = bidRequest.getImp().stream()
.map(imp -> imp.toBuilder().ext(createImpExt(prebid)).build())
.toList();
final ExtRequestPrebid extRequestPrebid = Optional.of(bidRequest)
AntoxaAntoxic marked this conversation as resolved.
Show resolved Hide resolved
.map(BidRequest::getExt)
.map(ExtRequest::getPrebid)
.map(prebid -> prebid.toBuilder().storedrequest(storedRequest).build())
.orElse(createdExtRequestPrebid);

return bidRequest.toBuilder()
.imp(updateImps(bidRequest, createdExtRequestPrebid))
.ext(ExtRequest.of(extRequestPrebid))
.build();
}

return bidRequest.toBuilder().imp(imps).ext(extRequest).build();
private List<Imp> updateImps(BidRequest bidRequest, ExtRequestPrebid extRequestPrebid) {
return bidRequest.getImp().stream()
.map(imp -> imp.toBuilder().ext(createImpExt(extRequestPrebid)).build())
.toList();
}

private static String resolveStoredRequestId(BidRequest bidRequest, ExtImpNextMillennium extImpNextMillennium) {
Expand Down Expand Up @@ -164,24 +178,47 @@ private static MultiMap headers() {

@Override
public final Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequest bidRequest) {
final List<BidderError> bidderErrors = new ArrayList<>();
try {
final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class);
if (CollectionUtils.isEmpty(bidResponse.getSeatbid())) {
return Result.empty();
}
return Result.withValues(bidsFromResponse(bidResponse));
return Result.of(bidsFromResponse(bidResponse, bidderErrors), bidderErrors);
} catch (DecodeException e) {
return Result.withError(BidderError.badServerResponse(e.getMessage()));
}
}

private static List<BidderBid> bidsFromResponse(BidResponse bidResponse) {
private static List<BidderBid> bidsFromResponse(BidResponse bidResponse, List<BidderError> bidderErrors) {
return bidResponse.getSeatbid().stream()
.filter(Objects::nonNull)
.map(SeatBid::getBid)
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.map(bid -> BidderBid.of(bid, BidType.banner, bidResponse.getCur()))
.map(bid -> resolveBidderBid(bidResponse, bidderErrors, bid))
.filter(Objects::nonNull)
.toList();
}

private static BidderBid resolveBidderBid(BidResponse bidResponse, List<BidderError> bidderErrors, Bid bid) {
final BidType bidType = getBidType(bid, bidderErrors);
if (bidType == null) {
return null;
}

return BidderBid.of(bid, bidType, bidResponse.getCur());
}

private static BidType getBidType(Bid bid, List<BidderError> bidderErrors) {
return switch (ObjectUtils.defaultIfNull(bid.getMtype(), 0)) {
AntoxaAntoxic marked this conversation as resolved.
Show resolved Hide resolved
case 1 -> BidType.banner;
case 2 -> BidType.video;
default -> {
bidderErrors.add(BidderError.badServerResponse("Failed to parse bid mtype for impression id \"%s\""
.formatted(bid.getImpid())));
yield null;
}
};
}
}
2 changes: 2 additions & 0 deletions src/main/resources/bidder-config/nextmillennium.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ adapters:
maintainer-email: [email protected]
app-media-types:
- banner
- video
site-media-types:
- banner
- video
supported-vendors:
vendor-id: 1060
usersync:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.prebid.server.proto.openrtb.ext.request.nextmillennium.ExtImpNextMillennium;
import org.prebid.server.proto.openrtb.ext.response.BidType;

import java.util.Arrays;
import java.util.List;
import java.util.function.UnaryOperator;

Expand Down Expand Up @@ -275,19 +276,73 @@ public void makeHttpRequestsShouldReturnImpExtNextMillenniumWhenNmmFlagsConfigur
}

@Test
public void makeBidsShouldReturnBannerBidByDefault() throws JsonProcessingException {
public void makeBidsShouldReturnBannerBidWhenMTypeIsOne() throws JsonProcessingException {
// given
final BidRequest bidRequest = givenBidRequest(identity());
final BidderCall<BidRequest> httpCall = givenHttpCall(bidRequest,
mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.mtype(1).impid("123"))));

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

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue())
.containsExactly(BidderBid.of(Bid.builder().impid("123").build(), BidType.banner, "USD"));
.containsExactly(BidderBid.of(Bid.builder().mtype(1).impid("123").build(),
BidType.banner, "USD"));
}

@Test
public void makeBidsShouldReturnVideoBidWhenMTypeIsTwo() throws JsonProcessingException {
// given
final BidRequest bidRequest = givenBidRequest(identity());
final BidderCall<BidRequest> httpCall = givenHttpCall(bidRequest,
mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.mtype(2).impid("123"))));

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

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue())
.containsExactly(BidderBid.of(Bid.builder().mtype(2).impid("123").build(),
BidType.video, "USD"));
}

@Test
public void makeBidsShouldReturnErrorWhenMTypeIsUnknown() throws JsonProcessingException {
// given
final BidRequest bidRequest = givenBidRequest(identity());
final BidderCall<BidRequest> httpCall = givenHttpCall(bidRequest,
mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.mtype(999).impid("123"))));

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

// then
assertThat(result.getErrors())
.contains(BidderError.badServerResponse("Failed to parse bid mtype for impression id \"123\""));
assertThat(result.getValue()).isEmpty();
}

@Test
public void makeBidsShouldReturnBothValidAndInvalidBidderBidAtTheSameTime() throws JsonProcessingException {
// given
final BidRequest bidRequest = givenBidRequest(identity());
final BidderCall<BidRequest> httpCall = givenHttpCall(givenBidRequest(identity()),
mapper.writeValueAsString(givenBidResponse(
bidBuilder -> bidBuilder.mtype(999).impid("123"),
bidBuilder -> bidBuilder.mtype(1).impid("312"))));

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

// then
assertThat(result.getErrors())
.contains(BidderError.badServerResponse("Failed to parse bid mtype for impression id \"123\""));
assertThat(result.getValue())
.containsExactly(BidderBid.of(Bid.builder().mtype(1).impid("312").build(),
BidType.banner, "USD"));
}

@Test
Expand Down Expand Up @@ -476,12 +531,15 @@ private static Imp givenImp(UnaryOperator<Imp.ImpBuilder> impCustomizer) {
return impCustomizer.apply(Imp.builder()).build();
}

private static BidResponse givenBidResponse(UnaryOperator<Bid.BidBuilder> bidCustomizer) {
@SafeVarargs
private static BidResponse givenBidResponse(UnaryOperator<Bid.BidBuilder>... bidCustomizers) {
return BidResponse.builder()
.cur("USD")
.seatbid(singletonList(SeatBid.builder()
.bid(singletonList(bidCustomizer.apply(Bid.builder()).build()))
.bid(Arrays.stream(bidCustomizers)
.map(bidCustomizer -> bidCustomizer.apply(Bid.builder()).build())
.toList())
.build()))
.cur("USD")
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"id": "bid_id",
"impid": "imp_id",
"price": 0.01,
"mtype": 1,
"adid": "adid001",
"cid": "cid001",
"crid": "crid001",
Expand All @@ -25,7 +26,7 @@
"cur": "USD",
"ext": {
"responsetimemillis": {
"nextmillennium" : "{{ nextmillennium.response_time_ms }}"
"nextmillennium": "{{ nextmillennium.response_time_ms }}"
},
"prebid": {
"auctiontimestamp": 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@
"prebid": {
"storedrequest": {
"id": "placement_id"
},
"server": {
"externalurl": "http://localhost:8080",
"gvlid": 1,
"datacenter": "local",
"endpoint": "/openrtb2/auction"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
{
"crid": "crid001",
"adid": "adid001",
"mtype": 1,
"price": 0.01,
"id": "bid_id",
"impid": "imp_id",
Expand Down
Loading