Skip to content
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
160 changes: 105 additions & 55 deletions src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ public class RubiconBidder implements Bidder<BidRequest> {
private final String xapiUsername;
private final Set<String> supportedVendors;
private final boolean generateBidId;
private final String apexRendererUrl;
private final CurrencyConversionService currencyConversionService;
private final PriceFloorResolver floorResolver;
private final PrebidVersionProvider versionProvider;
Expand All @@ -200,6 +201,7 @@ public RubiconBidder(String bidderName,
String xapiPassword,
List<String> supportedVendors,
boolean generateBidId,
String apexRendererUrl,
CurrencyConversionService currencyConversionService,
PriceFloorResolver floorResolver,
PrebidVersionProvider versionProvider,
Expand All @@ -211,6 +213,7 @@ public RubiconBidder(String bidderName,
this.xapiUsername = Objects.requireNonNull(xapiUsername);
this.supportedVendors = Set.copyOf(Objects.requireNonNull(supportedVendors));
this.generateBidId = generateBidId;
this.apexRendererUrl = apexRendererUrl;
this.currencyConversionService = Objects.requireNonNull(currencyConversionService);
this.floorResolver = Objects.requireNonNull(floorResolver);
this.versionProvider = Objects.requireNonNull(versionProvider);
Expand Down Expand Up @@ -1565,58 +1568,104 @@ private List<BidderBid> bidsFromResponse(BidRequest prebidRequest,
.collect(Collectors.toMap(Imp::getId, Function.identity()));
final Map<String, Imp> idToRubiconImp = bidRequest.getImp().stream()
.collect(Collectors.toMap(Imp::getId, Function.identity()));
final Float cpmOverrideFromRequest = cpmOverrideFromRequest(prebidRequest);
final RubiconExtPrebidBiddersBidder extPrebidBiddersBidder = extPrebidBiddersRubicon(prebidRequest.getExt());
final Float cpmOverrideFromRequest = cpmOverrideFromRequest(extPrebidBiddersBidder);
final boolean hasApexRenderer = hasApexRenderer(extPrebidBiddersBidder);
final BidType bidType = bidType(bidRequest);

return bidResponse.getSeatbid().stream()
.filter(Objects::nonNull)
.map(seatBid -> updateSeatBids(seatBid, errors))
.map(RubiconSeatBid::getBid)
.filter(Objects::nonNull)
.map(seatBid -> seatBid.getBid().stream()
.filter(Objects::nonNull)
.map(bid -> updateBid(
bid,
seatBid,
idToImp.get(bid.getImpid()),
bidType,
cpmOverrideFromRequest,
hasApexRenderer,
bidResponse,
errors))
.filter(Objects::nonNull)
.map(bid -> createBidderBid(
bid,
idToRubiconImp.get(bid.getImpid()),
bidType,
bidResponse.getCur()))
.toList())
.flatMap(Collection::stream)
.map(bid -> updateBid(bid, idToImp.get(bid.getImpid()), cpmOverrideFromRequest, bidResponse))
.map(bid -> createBidderBid(bid, idToRubiconImp.get(bid.getImpid()), bidType, bidResponse.getCur()))
.toList();
}

private RubiconSeatBid updateSeatBids(RubiconSeatBid seatBid, List<BidderError> errors) {
final Integer networkId = resolveNetworkId(seatBid);
final String seat = seatBid.getSeat();
private Bid updateBid(RubiconBid bid,
RubiconSeatBid seatBid,
Imp imp,
BidType bidType,
Float cpmOverrideFromRequest,
boolean hasApexRenderer,
RubiconBidResponse bidResponse,
List<BidderError> errors) {

if (networkId == null && seat == null) {
return seatBid;
final ObjectNode updateBidExt;
try {
updateBidExt = prepareBidExt(bid, seatBid, imp, bidType, hasApexRenderer);
} catch (PreBidException e) {
errors.add(BidderError.badServerResponse(e.getMessage()));
return null;
}

final List<RubiconBid> updatedBids = seatBid.getBid().stream()
.map(bid -> prepareBidMeta(bid, seat, networkId, errors))
.filter(Objects::nonNull)
.toList();
return seatBid.toBuilder().bid(updatedBids).build();
}
String bidId = bid.getId();
if (generateBidId) {
// Since Rubicon XAPI returns openrtb_response.seatbid.bid.id not unique enough
// generate new value for it
bidId = UUID.randomUUID().toString();
} else if (Objects.equals(bid.getId(), "0")) {
// Since Rubicon XAPI returns only one bid per response
// copy bidResponse.bidid to openrtb_response.seatbid.bid.id
bidId = bidResponse.getBidid();
}

private static Integer resolveNetworkId(RubiconSeatBid seatBid) {
final String buyer = seatBid.getBuyer();
final int networkId = NumberUtils.toInt(buyer, 0);
return networkId <= 0 ? null : networkId;
// Unconditionally set price if coming from CPM override
final Float cpmOverride = ObjectUtils.defaultIfNull(cpmOverrideFromImp(imp), cpmOverrideFromRequest);
final BigDecimal bidPrice = cpmOverride != null
? new BigDecimal(String.valueOf(cpmOverride))
: bid.getPrice();

final RubiconBid updatedRubiconBid = bid.toBuilder()
.id(bidId)
.adm(resolveAdm(bid.getAdm(), bid.getAdmNative()))
.price(bidPrice)
.ext(updateBidExt)
.build();

return bidFromRubiconBid(updatedRubiconBid);
}

private RubiconBid prepareBidMeta(RubiconBid bid, String seat, Integer networkId, List<BidderError> errors) {
private ObjectNode prepareBidExt(RubiconBid bid,
RubiconSeatBid seatBid,
Imp imp,
BidType bidType,
boolean hasApexRenderer) {

final ObjectNode bidExt = bid.getExt();
final ExtPrebid<ExtBidPrebid, ObjectNode> extPrebid;
try {
extPrebid = getExtPrebid(bidExt, bid.getId());
} catch (PreBidException e) {
errors.add(BidderError.badServerResponse(e.getMessage()));
return null;
}
final ExtPrebid<ExtBidPrebid, ObjectNode> extPrebid = getExtPrebid(bidExt, bid.getId());
final ExtBidPrebid extBidPrebid = extPrebid != null ? extPrebid.getPrebid() : null;
final ExtBidPrebidMeta meta = extBidPrebid != null ? extBidPrebid.getMeta() : null;

final Integer networkId = resolveNetworkId(seatBid);
final String seat = seatBid.getSeat();
final String rendererUrl = resolveRendererUrl(imp, meta, bidType, hasApexRenderer);

if (ObjectUtils.allNull(networkId, rendererUrl, seat)) {
return bidExt;
}

final ExtBidPrebidMeta updatedMeta = Optional.ofNullable(meta)
.map(ExtBidPrebidMeta::toBuilder)
.orElseGet(ExtBidPrebidMeta::builder)
.networkId(networkId)
.seat(seat)
.rendererUrl(rendererUrl)
.build();

final ExtBidPrebid modifiedExtBidPrebid = extBidPrebid != null
Expand All @@ -1626,7 +1675,7 @@ private RubiconBid prepareBidMeta(RubiconBid bid, String seat, Integer networkId
final ObjectNode updatedBidExt = bidExt != null ? bidExt : mapper.mapper().createObjectNode();
updatedBidExt.set(PREBID_EXT, mapper.mapper().valueToTree(modifiedExtBidPrebid));

return bid.toBuilder().ext(updatedBidExt).build();
return updatedBidExt;
}

private ExtPrebid<ExtBidPrebid, ObjectNode> getExtPrebid(ObjectNode bidExt, String bidId) {
Expand All @@ -1637,31 +1686,30 @@ private ExtPrebid<ExtBidPrebid, ObjectNode> getExtPrebid(ObjectNode bidExt, Stri
}
}

private Bid updateBid(RubiconBid bid, Imp imp, Float cpmOverrideFromRequest, RubiconBidResponse bidResponse) {
String bidId = bid.getId();
if (generateBidId) {
// Since Rubicon XAPI returns openrtb_response.seatbid.bid.id not unique enough
// generate new value for it
bidId = UUID.randomUUID().toString();
} else if (Objects.equals(bid.getId(), "0")) {
// Since Rubicon XAPI returns only one bid per response
// copy bidResponse.bidid to openrtb_response.seatbid.bid.id
bidId = bidResponse.getBidid();
}
private static Integer resolveNetworkId(RubiconSeatBid seatBid) {
final String buyer = seatBid.getBuyer();
final int networkId = NumberUtils.toInt(buyer, 0);
return networkId <= 0 ? null : networkId;
}

// Unconditionally set price if coming from CPM override
final Float cpmOverride = ObjectUtils.defaultIfNull(cpmOverrideFromImp(imp), cpmOverrideFromRequest);
final BigDecimal bidPrice = cpmOverride != null
? new BigDecimal(String.valueOf(cpmOverride))
: bid.getPrice();
private String resolveRendererUrl(Imp imp, ExtBidPrebidMeta meta, BidType bidType, boolean hasApexRenderer) {
if (imp == null) {
return null;
}

final RubiconBid updatedRubiconBid = bid.toBuilder()
.id(bidId)
.adm(resolveAdm(bid.getAdm(), bid.getAdmNative()))
.price(bidPrice)
.build();
final Video video = imp.getVideo();
return hasApexRenderer
&& (bidType == BidType.video || isVideoMetaMediaType(meta))
&& (video != null && !Objects.equals(video.getPlacement(), 1) && !Objects.equals(video.getPlcmt(), 1))
? apexRendererUrl
: null;
}

return bidFromRubiconBid(updatedRubiconBid);
private static Boolean isVideoMetaMediaType(ExtBidPrebidMeta meta) {
return Optional.ofNullable(meta)
.map(ExtBidPrebidMeta::getMediaType)
.map("video"::equalsIgnoreCase)
.orElse(false);
}

private String resolveAdm(String bidAdm, ObjectNode admobject) {
Expand All @@ -1681,7 +1729,6 @@ private Bid bidFromRubiconBid(RubiconBid rubiconBid) {
}

private static BidderBid createBidderBid(Bid bid, Imp imp, BidType bidType, String currency) {

return BidderBid.builder()
.bid(bid)
.type(bidType)
Expand All @@ -1690,8 +1737,7 @@ private static BidderBid createBidderBid(Bid bid, Imp imp, BidType bidType, Stri
.build();
}

private Float cpmOverrideFromRequest(BidRequest bidRequest) {
final RubiconExtPrebidBiddersBidder bidder = extPrebidBiddersRubicon(bidRequest.getExt());
private static Float cpmOverrideFromRequest(RubiconExtPrebidBiddersBidder bidder) {
final RubiconExtPrebidBiddersBidderDebug debug = bidder != null ? bidder.getDebug() : null;
return debug != null ? debug.getCpmoverride() : null;
}
Expand All @@ -1704,6 +1750,10 @@ private Float cpmOverrideFromImp(Imp imp) {
.orElse(null);
}

private static boolean hasApexRenderer(RubiconExtPrebidBiddersBidder bidder) {
return Optional.ofNullable(bidder).map(RubiconExtPrebidBiddersBidder::getApexRenderer).orElse(false);
}

private static BidType bidType(BidRequest bidRequest) {
final ImpMediaType impMediaType = impType(bidRequest.getImp().getFirst());
return switch (impMediaType) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.prebid.server.bidder.rubicon.proto.request;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Value;

Expand All @@ -10,4 +11,7 @@ public class RubiconExtPrebidBiddersBidder {
String integration;

RubiconExtPrebidBiddersBidderDebug debug;

@JsonProperty("apexRenderer")
Boolean apexRenderer;
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ BidderDeps rubiconBidderDeps(RubiconConfigurationProperties rubiconConfiguration
config.getXapi().getPassword(),
config.getMetaInfo().getSupportedVendors(),
config.getGenerateBidId(),
config.getApexRendererUrl(),
currencyConversionService,
floorResolver,
versionProvider,
Expand All @@ -75,6 +76,9 @@ private static class RubiconConfigurationProperties extends BidderConfigurationP

@NotNull
private Boolean generateBidId;

@NotNull
private String apexRendererUrl;
}

@Data
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/bidder-config/rubicon.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ adapters:
url: GET_FROM_globalsupport@magnite.com
support-cors: false
generate-bid-id: false
apex-renderer-url: "https://video-outstream.rubiconproject.com/apex-2.2.1.js"
XAPI:
Username: GET_FROM_globalsupport@magnite.com
Password: GET_FROM_globalsupport@magnite.com
Loading
Loading