diff --git a/src/main/java/org/prebid/server/bidder/colossus/ColossusBidder.java b/src/main/java/org/prebid/server/bidder/colossus/ColossusBidder.java index 5302825cd36..7b1c4101306 100644 --- a/src/main/java/org/prebid/server/bidder/colossus/ColossusBidder.java +++ b/src/main/java/org/prebid/server/bidder/colossus/ColossusBidder.java @@ -1,20 +1,143 @@ package org.prebid.server.bidder.colossus; +import com.fasterxml.jackson.core.type.TypeReference; +import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Imp; -import org.prebid.server.bidder.OpenrtbBidder; +import com.iab.openrtb.response.Bid; +import com.iab.openrtb.response.BidResponse; +import com.iab.openrtb.response.SeatBid; +import io.vertx.core.http.HttpMethod; +import org.apache.commons.collections4.CollectionUtils; +import org.prebid.server.bidder.Bidder; +import org.prebid.server.bidder.model.BidderBid; +import org.prebid.server.bidder.model.BidderError; +import org.prebid.server.bidder.model.HttpCall; +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; import org.prebid.server.proto.openrtb.ext.request.colossus.ExtImpColossus; +import org.prebid.server.proto.openrtb.ext.response.BidType; +import org.prebid.server.util.HttpUtil; -public class ColossusBidder extends OpenrtbBidder { +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class ColossusBidder implements Bidder { + + private static final TypeReference> COLOSSUS_EXT_TYPE_REFERENCE = + new TypeReference<>() { + }; + + private final String endpointUrl; + private final JacksonMapper mapper; public ColossusBidder(String endpointUrl, JacksonMapper mapper) { - super(endpointUrl, RequestCreationStrategy.REQUEST_PER_IMP, ExtImpColossus.class, mapper); + this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl)); + this.mapper = Objects.requireNonNull(mapper); } @Override - protected Imp modifyImp(Imp imp, ExtImpColossus impExt) { - return imp.toBuilder() - .tagid(impExt.getTagId()) + public Result>> makeHttpRequests(BidRequest bidRequest) { + final List> requests = new ArrayList<>(); + final List errors = new ArrayList<>(); + + for (Imp imp : bidRequest.getImp()) { + try { + final ExtImpColossus extImpColossus = parseImpExt(imp); + final Imp modifiedImp = modifyImp(imp, extImpColossus.getTagId()); + requests.add(makeRequest(bidRequest, modifiedImp)); + } catch (PreBidException e) { + errors.add(BidderError.badInput(e.getMessage())); + } + } + + return Result.of(requests, errors); + } + + private ExtImpColossus parseImpExt(Imp imp) { + try { + return mapper.mapper().convertValue(imp.getExt(), COLOSSUS_EXT_TYPE_REFERENCE).getBidder(); + } catch (IllegalArgumentException e) { + throw new PreBidException(e.getMessage()); + } + } + + private static Imp modifyImp(Imp imp, String tagId) { + return imp.toBuilder().tagid(tagId).build(); + } + + private HttpRequest makeRequest(BidRequest bidRequest, Imp imp) { + final BidRequest outgoingRequest = bidRequest.toBuilder().imp(Collections.singletonList(imp)).build(); + + return HttpRequest.builder() + .method(HttpMethod.POST) + .headers(HttpUtil.headers()) + .uri(endpointUrl) + .body(mapper.encodeToBytes(outgoingRequest)) + .payload(outgoingRequest) .build(); } + + @Override + public Result> makeBids(HttpCall httpCall, BidRequest bidRequest) { + final BidResponse bidResponse; + try { + bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); + } catch (DecodeException e) { + return Result.withError(BidderError.badServerResponse(e.getMessage())); + } + + final List errors = new ArrayList<>(); + final List bids = extractBids(httpCall.getRequest().getPayload(), bidResponse, errors); + + return Result.of(bids, errors); + } + + private static List extractBids(BidRequest bidRequest, + BidResponse bidResponse, + List errors) { + + if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) { + return Collections.emptyList(); + } + + return bidResponse.getSeatbid().stream() + .filter(Objects::nonNull) + .map(SeatBid::getBid) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .filter(Objects::nonNull) + .map(bid -> makeBidderBid(bid, bidRequest.getImp(), bidResponse.getCur(), errors)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + private static BidderBid makeBidderBid(Bid bid, List imps, String currency, List errors) { + try { + return BidderBid.of(bid, resolveBidType(bid.getImpid(), imps), currency); + } catch (PreBidException e) { + errors.add(BidderError.badServerResponse(e.getMessage())); + return null; + } + } + + private static BidType resolveBidType(String impId, List imps) { + for (Imp imp : imps) { + if (Objects.equals(impId, imp.getId())) { + if (imp.getBanner() == null && imp.getVideo() != null) { + return BidType.video; + } + return BidType.banner; + } + } + + throw new PreBidException(String.format("Failed to find impression for ID: %s", impId)); + } }