Skip to content
Closed
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
@@ -0,0 +1,64 @@
package org.prebid.server.auction;

import com.iab.openrtb.request.BidRequest;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestAlternateBidderCodes;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestAlternateBidderCodesBidder;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid;
import org.prebid.server.util.ObjectUtil;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* Helper class for applying the allow-alternate-bidder-codes configuration
*/
public class AllowedAlternateBidderCodes {

private static final String WILDCARD = "*";

private AllowedAlternateBidderCodes() { }

public static Set<String> allowedCodesForBidder(String bidder, BidRequest bidRequest) {
final ExtRequestAlternateBidderCodes alternateBidderCodes = ObjectUtil.getIfNotNull(
bidRequest.getExt().getPrebid(), ExtRequestPrebid::getAlternatebiddercodes);

if (alternateBidderCodes == null) {
return null;
}

if (!alternateBidderCodes.getEnabled()) {
return null;
}

final Map<String, ExtRequestAlternateBidderCodesBidder> alternateBidderCodesBidderMap =
alternateBidderCodes.getBidders();

if (alternateBidderCodesBidderMap == null) {
return null;
}

final ExtRequestAlternateBidderCodesBidder alternateBidderCodesBidder =
alternateBidderCodesBidderMap.get(bidder);

if (alternateBidderCodesBidder == null || !alternateBidderCodesBidder.getEnabled()) {
return null;
}

final List<String> allowedAlternates = alternateBidderCodesBidder.getAllowedBidderCodes();
return allowedAlternates != null ? new HashSet<>(allowedAlternates) : null;
}

public static String applySeatForBid(Set<String> allowedAlternateBidderCodes, String bidder, String wantedSeat) {
if (allowedAlternateBidderCodes == null || wantedSeat == null) {
return bidder;
}

if (allowedAlternateBidderCodes.contains(wantedSeat) || allowedAlternateBidderCodes.contains(WILDCARD)) {
return wantedSeat;
}

return bidder;
}
}
103 changes: 62 additions & 41 deletions src/main/java/org/prebid/server/auction/BidResponseCreator.java
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ private Future<List<BidderResponse>> updateBids(List<BidderResponse> bidderRespo

for (final BidderResponse bidderResponse : bidderResponses) {
final String bidder = bidderResponse.getBidder();
final Set<String> allowedAlternateBidderCodes = AllowedAlternateBidderCodes.allowedCodesForBidder(
bidder, auctionContext.getBidRequest());

final List<BidderBid> modifiedBidderBids = new ArrayList<>();
final BidderSeatBid seatBid = bidderResponse.getSeatBid();
Expand All @@ -283,10 +285,15 @@ private Future<List<BidderResponse>> updateBids(List<BidderResponse> bidderRespo

final Bid updatedBid = updateBid(
receivedBid, bidType, bidder, videoStoredDataResult, auctionContext, eventsContext);
modifiedBidderBids.add(bidderBid.toBuilder().bid(updatedBid).build());

modifiedBidderBids.add(bidderBid.toBuilder().bid(updatedBid)
.seat(AllowedAlternateBidderCodes.applySeatForBid(
allowedAlternateBidderCodes, bidder, bidderBid.getSeat()))
.build());
}

final BidderSeatBid modifiedSeatBid = seatBid.with(modifiedBidderBids);

result.add(bidderResponse.with(modifiedSeatBid));
}

Expand Down Expand Up @@ -425,15 +432,14 @@ private List<BidderResponseInfo> toBidderResponseInfos(CategoryMappingResult cat

final List<BidderResponse> bidderResponses = categoryMappingResult.getBidderResponses();
for (final BidderResponse bidderResponse : bidderResponses) {
final String bidder = bidderResponse.getBidder();

final List<BidInfo> bidInfos = new ArrayList<>();
final BidderSeatBid seatBid = bidderResponse.getSeatBid();

for (final BidderBid bidderBid : seatBid.getBids()) {
final Bid bid = bidderBid.getBid();
final BidType type = bidderBid.getType();
final BidInfo bidInfo = toBidInfo(bid, type, imps, bidder, categoryMappingResult, cacheInfo, account);
final BidInfo bidInfo = toBidInfo(
bid, type, imps, bidderBid.getSeat(), categoryMappingResult, cacheInfo, account);
bidInfos.add(bidInfo);
}

Expand All @@ -445,6 +451,7 @@ private List<BidderResponseInfo> toBidderResponseInfos(CategoryMappingResult cat
seatBid.getFledgeAuctionConfigs(),
seatBid.getIgi());

final String bidder = bidderResponse.getBidder();
result.add(BidderResponseInfo.of(bidder, bidderSeatBidInfo, bidderResponse.getResponseTime()));
}

Expand Down Expand Up @@ -1339,7 +1346,7 @@ private BidResponse toBidResponse(List<BidderResponseInfo> bidderResponseInfos,
.map(BidderResponseInfo::getSeatBid)
.map(BidderSeatBidInfo::getBidsInfos)
.filter(CollectionUtils::isNotEmpty)
.map(bidInfos -> toSeatBid(
.flatMap(bidInfos -> toSeatBids(
bidInfos,
targeting,
bidRequest,
Expand Down Expand Up @@ -1417,45 +1424,59 @@ private boolean checkEchoVideoAttrs(Imp imp) {
}

/**
* Creates an OpenRTB {@link SeatBid} for a bidder. It will contain all the bids supplied by a bidder and a "bidder"
* extension field populated.
* Creates a OpenRTB {@link SeatBid}s for a bidder. It will contain all the bids supplied by a bidder and a "bidder"
* extension field populated. Will return a list of a single SeatBid unless the bids have overriden the "bidder".
*/
private SeatBid toSeatBid(List<BidInfo> bidInfos,
ExtRequestTargeting targeting,
BidRequest bidRequest,
BidRequestCacheInfo requestCacheInfo,
Map<Bid, CacheInfo> bidToCacheInfo,
Account account,
Map<String, List<ExtBidderError>> bidErrors,
Map<String, List<ExtBidderError>> bidWarnings) {

final String bidder = bidInfos.stream()
.map(BidInfo::getBidder)
.findFirst()
// Should never occur
.orElseThrow(() -> new IllegalArgumentException("Bidder was not defined for bidInfo"));
private Stream<SeatBid> toSeatBids(List<BidInfo> bidInfos,
ExtRequestTargeting targeting,
BidRequest bidRequest,
BidRequestCacheInfo requestCacheInfo,
Map<Bid, CacheInfo> bidToCacheInfo,
Account account,
Map<String, List<ExtBidderError>> bidErrors,
Map<String, List<ExtBidderError>> bidWarnings) {

final Map<String, List<Bid>> bidsByBidder = new HashMap<>(1);

for (BidInfo bidInfo : bidInfos) {
bidInfo = injectAdmWithCacheInfo(
bidInfo,
requestCacheInfo,
bidToCacheInfo,
bidErrors);

if (bidInfo == null) {
continue;
}

final List<Bid> bids = bidInfos.stream()
.map(bidInfo -> injectAdmWithCacheInfo(
bidInfo,
requestCacheInfo,
bidToCacheInfo,
bidErrors))
.filter(Objects::nonNull)
.map(bidInfo -> toBid(
bidInfo,
targeting,
bidRequest,
account,
bidWarnings))
.filter(Objects::nonNull)
.toList();
final String bidder = bidInfo.getBidder();
if (bidder == null) {
logger.warn("BidInfo missing bidder, skipping bid", bidInfo);
continue;
}

return SeatBid.builder()
.seat(bidder)
.bid(bids)
.group(0) // prebid cannot support roadblocking
.build();
final Bid bid = toBid(
bidInfo,
targeting,
bidRequest,
account,
bidWarnings);

if (bid == null) {
continue;
}

bidsByBidder.putIfAbsent(bidder, new ArrayList<>(1));
bidsByBidder.get(bidder).add(bid);
}

return bidsByBidder.entrySet().stream()
.map(kvp ->
SeatBid.builder()
.seat(kvp.getKey())
.bid(kvp.getValue())
.group(0) // prebid cannot support roadblocking
.build());
}

private BidInfo injectAdmWithCacheInfo(BidInfo bidInfo,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1201,6 +1201,7 @@ private Future<BidderResponse> requestBids(BidderRequest bidderRequest,

final CaseInsensitiveMultiMap requestHeaders = auctionContext.getHttpRequest().getHeaders();
final String bidderName = bidderRequest.getBidder();

final String resolvedBidderName = aliases.resolveBidder(bidderName);
final Bidder<?> bidder = bidderCatalog.bidderByName(resolvedBidderName);
final long bidderTmaxDeductionMs = bidderCatalog.bidderInfoByName(resolvedBidderName).getTmaxDeductionMs();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import org.prebid.server.proto.openrtb.ext.request.ExtMediaTypePriceGranularity;
import org.prebid.server.proto.openrtb.ext.request.ExtPriceGranularity;
import org.prebid.server.proto.openrtb.ext.request.ExtRequest;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestAlternateBidderCodes;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidCache;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidChannel;
Expand Down Expand Up @@ -726,6 +727,8 @@ private ExtRequest populateRequestExt(ExtRequest ext,
Account account) {

final ExtRequestPrebid prebid = ObjectUtil.getIfNotNull(ext, ExtRequest::getPrebid);
final ExtRequestAlternateBidderCodes accountAlternateBidderCodes = ObjectUtil.getIfNotNull(
ObjectUtil.getIfNotNull(account, Account::getAuction), AccountAuctionConfig::getAlternateBidderCodes);

final ExtRequestTargeting updatedTargeting = targetingOrNull(prebid, imps, account);
final ExtRequestPrebidCache updatedCache = cacheOrNull(prebid);
Expand All @@ -742,6 +745,10 @@ private ExtRequest populateRequestExt(ExtRequest ext,
ObjectUtil.getIfNotNull(prebid, ExtRequestPrebid::getCache)))
.channel(ObjectUtils.defaultIfNull(updatedChannel,
ObjectUtil.getIfNotNull(prebid, ExtRequestPrebid::getChannel)))
.alternatebiddercodes(ObjectUtils.defaultIfNull(
ObjectUtil.getIfNotNull(prebid, ExtRequestPrebid::getAlternatebiddercodes),
accountAlternateBidderCodes
))
.server(serverInfo.with(endpoint))
.build());

Expand Down
6 changes: 4 additions & 2 deletions src/main/java/org/prebid/server/bidder/amx/AmxBidder.java
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,10 @@ private BidderBid createBidderBid(Bid bid, String cur, List<BidderError> errors)
errors.add(BidderError.badInput(e.getMessage()));
return null;
}
// TODO: After adding support to change seat data, add bid.ext bidderCode processing
return BidderBid.of(resolveBid(bid, amxBidExt.getDemandSource()), getBidType(amxBidExt), cur);

return BidderBid.of(
resolveBid(bid, amxBidExt.getDemandSource()),
getBidType(amxBidExt), cur, amxBidExt.getBidderCode());
}

private AmxBidExt parseBidderExt(ObjectNode ext) {
Expand Down
13 changes: 12 additions & 1 deletion src/main/java/org/prebid/server/bidder/model/BidderBid.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,22 @@ public class BidderBid {
*/
PriceFloorInfo priceFloorInfo;

public static BidderBid of(Bid bid, BidType bidType, String bidCurrency) {
/**
* The seat, which will override the default seat (e.g. the bidder name)
* if alternate-bidder-codes (ext.prebid.alternatebiddercodes) are allowed
*/
String seat;

public static BidderBid of(Bid bid, BidType bidType, String bidCurrency, String seat) {
return BidderBid.builder()
.bid(bid)
.type(bidType)
.bidCurrency(bidCurrency)
.seat(seat)
.build();
}

public static BidderBid of(Bid bid, BidType bidType, String bidCurrency) {
return of(bid, bidType, bidCurrency, null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.prebid.server.proto.openrtb.ext.request;

import lombok.Builder;
import lombok.Value;

import java.util.Map;


/**
* Defines the contract for bidrequest.ext.prebid.alternatebiddercodes
*/
@Builder(toBuilder = true)
@Value
public class ExtRequestAlternateBidderCodes {

/**
* Is this feature enabled
*/
Boolean enabled;

Map<String, ExtRequestAlternateBidderCodesBidder> bidders;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.prebid.server.proto.openrtb.ext.request;

import lombok.Builder;
import lombok.Value;

import java.util.List;

@Builder(toBuilder = true)
@Value
public class ExtRequestAlternateBidderCodesBidder {

Boolean enabled;

List<String> allowedBidderCodes;
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ public class ExtRequestPrebid {
*/
Map<String, String> aliases;

/**
* Defines the contract for bidrequest.ext.prebid.alternatebiddercodes
*/
ExtRequestAlternateBidderCodes alternatebiddercodes;

/**
* Defines the contract for bidrequest.ext.prebid.aliasgvlids
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import lombok.Builder;
import lombok.Value;
import org.prebid.server.auction.model.PaaFormat;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestAlternateBidderCodes;
import org.prebid.server.spring.config.bidder.model.MediaType;

import java.util.Map;
Expand All @@ -32,6 +33,9 @@ public class AccountAuctionConfig {
@JsonAlias("debug-allow")
Boolean debugAllow;

@JsonAlias("alternate-bidder-codes")
ExtRequestAlternateBidderCodes alternateBidderCodes;

@JsonAlias("bid-validations")
AccountBidValidationConfig bidValidations;

Expand Down
1 change: 1 addition & 0 deletions src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ adapter-defaults:
allow: true
auction:
ad-server-currency: USD
alternate-bidder-codes:
blocklisted-accounts:
blocklisted-apps:
biddertmax:
Expand Down
6 changes: 6 additions & 0 deletions src/test/java/org/prebid/server/bidder/amx/AmxBidderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,8 @@ public void makeBidsShouldSetDemandSourceFromBidExtDsField() throws JsonProcessi
// given
final ObjectNode givenBidExt = mapper.createObjectNode();
givenBidExt.set("ds", new TextNode("someDs"));
givenBidExt.set("bc", new TextNode("someBc"));

final BidderCall<BidRequest> httpCall = givenHttpCall(
BidRequest.builder()
.imp(singletonList(Imp.builder().video(Video.builder().build()).id("123").build()))
Expand All @@ -328,6 +330,10 @@ public void makeBidsShouldSetDemandSourceFromBidExtDsField() throws JsonProcessi

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue())
.extracting(BidderBid::getSeat)
.containsExactly("someBc");

assertThat(result.getValue())
.extracting(BidderBid::getBid)
.extracting(Bid::getExt)
Expand Down
Loading