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 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
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 All @@ -21,6 +22,7 @@
import org.prebid.server.bidder.model.BidderError;
import org.prebid.server.bidder.model.HttpRequest;
import org.prebid.server.bidder.model.Result;
import org.prebid.server.exception.PreBidException;
import org.prebid.server.json.DecodeException;
import org.prebid.server.json.JacksonMapper;
import org.prebid.server.proto.openrtb.ext.ExtPrebid;
Expand All @@ -36,6 +38,7 @@
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

public class NextMillenniumBidder implements Bidder<BidRequest> {

Expand All @@ -57,47 +60,53 @@ public NextMillenniumBidder(String endpointUrl, JacksonMapper mapper, List<Strin
@Override
public final Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest bidRequest) {
final List<BidderError> errors = new ArrayList<>();
final List<ExtImpNextMillennium> impExts = getImpExts(bidRequest, errors);

return errors.isEmpty()
? Result.withValues(makeRequests(bidRequest, impExts))
: Result.withErrors(errors);
}

private List<ExtImpNextMillennium> getImpExts(BidRequest bidRequest, List<BidderError> errors) {
return bidRequest.getImp().stream()
.map(imp -> convertExt(imp, errors))
.toList();
}
final List<HttpRequest<BidRequest>> httpRequests = new ArrayList<>();

for (Imp imp : bidRequest.getImp()) {
final ExtImpNextMillennium extImpNextMillennium;
try {
extImpNextMillennium = convertExt(imp.getExt());
} catch (PreBidException e) {
errors.add(BidderError.badInput(e.getMessage()));
continue;
}
httpRequests.add(makeHttpRequest(updateBidRequest(bidRequest, extImpNextMillennium)));
}

private List<HttpRequest<BidRequest>> makeRequests(BidRequest bidRequest, List<ExtImpNextMillennium> extImps) {
return extImps.stream()
.map(extImp -> makeHttpRequest(updateBidRequest(bidRequest, extImp)))
.toList();
return errors.isEmpty() ? Result.withValues(httpRequests) : Result.withErrors(errors);
}

private ExtImpNextMillennium convertExt(Imp imp, List<BidderError> errors) {
private ExtImpNextMillennium convertExt(ObjectNode impExt) {
try {
return mapper.mapper()
.convertValue(imp.getExt(), NEXTMILLENNIUM_EXT_TYPE_REFERENCE)
.getBidder();
return mapper.mapper().convertValue(impExt, NEXTMILLENNIUM_EXT_TYPE_REFERENCE).getBidder();
} catch (IllegalArgumentException e) {
errors.add(BidderError.badInput(e.getMessage()));
throw new PreBidException(e.getMessage());
}
return null;
}

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.ofNullable(bidRequest)
.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 +173,53 @@ 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) {
final Integer markupType = bid.getMtype();
if (markupType == null) {
bidderErrors.add(BidderError.badServerResponse("Missing MType for bid: " + bid.getId()));
return null;
}

return switch (markupType) {
case 1 -> BidType.banner;
case 2 -> BidType.video;
default -> {
bidderErrors.add(BidderError.badServerResponse(
"Unable to fetch mediaType " + bid.getMtype() + " in multi-format: " + 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,88 @@ 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("Unable to fetch mediaType 999 in multi-format: 123"));
assertThat(result.getValue()).isEmpty();
}

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

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

// then
assertThat(result.getErrors()).contains(BidderError.badServerResponse("Missing MType for bid: bidId"));
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("Unable to fetch mediaType 999 in multi-format: 123"));
assertThat(result.getValue())
.containsExactly(BidderBid.of(Bid.builder().mtype(1).impid("312").build(),
BidType.banner, "USD"));
}

@Test
Expand Down Expand Up @@ -476,12 +546,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