Skip to content

Commit 8a86567

Browse files
Adjust Floors for Bidadjustments (#3910)
1 parent 6c495ae commit 8a86567

File tree

56 files changed

+3836
-1080
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+3836
-1080
lines changed

src/main/java/org/prebid/server/auction/BidsAdjuster.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,21 @@ public List<AuctionParticipation> validateAndAdjustBids(List<AuctionParticipatio
4343
AuctionContext auctionContext,
4444
BidderAliases aliases) {
4545

46+
final BidRequest bidRequest = auctionContext.getBidRequest();
4647
return auctionParticipations.stream()
4748
.map(auctionParticipation -> validBidderResponse(auctionParticipation, auctionContext, aliases))
4849
.map(auctionParticipation -> bidAdjustmentsProcessor.enrichWithAdjustedBids(
4950
auctionParticipation,
50-
auctionContext.getBidRequest(),
51-
auctionContext.getBidAdjustments()))
51+
bidRequest))
5252

5353
.map(auctionParticipation -> priceFloorEnforcer.enforce(
54-
auctionContext.getBidRequest(),
54+
bidRequest,
5555
auctionParticipation,
5656
auctionContext.getAccount(),
5757
auctionContext.getBidRejectionTrackers().get(auctionParticipation.getBidder())))
5858

5959
.map(auctionParticipation -> dsaEnforcer.enforce(
60-
auctionContext.getBidRequest(),
60+
bidRequest,
6161
auctionParticipation,
6262
auctionContext.getBidRejectionTrackers().get(auctionParticipation.getBidder())))
6363
.toList();

src/main/java/org/prebid/server/auction/ExchangeService.java

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
import org.prebid.server.settings.model.Account;
9797
import org.prebid.server.settings.model.AccountAuctionConfig;
9898
import org.prebid.server.settings.model.AccountCacheConfig;
99+
import org.prebid.server.util.BidderUtil;
99100
import org.prebid.server.util.HttpUtil;
100101
import org.prebid.server.util.ListUtil;
101102
import org.prebid.server.util.PbsUtil;
@@ -739,20 +740,34 @@ private AuctionParticipation createAuctionParticipation(
739740
final String storedBidResponse = impBidderToStoredBidResponse.size() == 1
740741
? impBidderToStoredBidResponse.get(imps.getFirst().getId()).get(bidder)
741742
: null;
743+
744+
final BidRequest enrichedWithPriceFloors = priceFloorProcessor.enrichWithPriceFloors(
745+
context.getBidRequest().toBuilder().imp(imps).build(),
746+
context.getAccount(),
747+
bidder,
748+
context.getPrebidErrors(),
749+
context.getDebugWarnings());
750+
742751
final BidRequest preparedBidRequest = prepareBidRequest(
743752
bidderPrivacyResult,
744-
imps,
753+
enrichedWithPriceFloors,
745754
bidderToMultiBid,
746755
biddersToConfigs,
747756
bidderToPrebidBidders,
748757
bidderAliases,
749758
context);
750759

760+
final Map<String, Price> originalPriceFloors = enrichedWithPriceFloors.getImp().stream()
761+
.filter(imp -> BidderUtil.isValidPrice(imp.getBidfloor())
762+
&& StringUtils.isNotBlank(imp.getBidfloorcur()))
763+
.collect(Collectors.toMap(Imp::getId, imp -> Price.of(imp.getBidfloorcur(), imp.getBidfloor())));
764+
751765
final BidderRequest bidderRequest = BidderRequest.builder()
752766
.bidder(bidder)
753767
.ortbVersion(ortbVersion)
754768
.storedResponse(storedBidResponse)
755769
.bidRequest(preparedBidRequest)
770+
.originalPriceFloors(originalPriceFloors)
756771
.build();
757772

758773
return AuctionParticipation.builder()
@@ -768,20 +783,14 @@ private OrtbVersion bidderSupportedOrtbVersion(String bidder, BidderAliases alia
768783
}
769784

770785
private BidRequest prepareBidRequest(BidderPrivacyResult bidderPrivacyResult,
771-
List<Imp> imps,
786+
BidRequest bidRequest,
772787
Map<String, MultiBidConfig> bidderToMultiBid,
773788
Map<String, ExtBidderConfigOrtb> biddersToConfigs,
774789
Map<String, JsonNode> bidderToPrebidBidders,
775790
BidderAliases bidderAliases,
776791
AuctionContext context) {
777792

778793
final String bidder = bidderPrivacyResult.getRequestBidder();
779-
final BidRequest bidRequest = priceFloorProcessor.enrichWithPriceFloors(
780-
context.getBidRequest().toBuilder().imp(imps).build(),
781-
context.getAccount(),
782-
bidder,
783-
context.getPrebidErrors(),
784-
context.getDebugWarnings());
785794
final boolean transmitTid = transmitTransactionId(bidder, context);
786795
final List<String> firstPartyDataBidders = firstPartyDataBidders(bidRequest.getExt());
787796
final boolean useFirstPartyData = firstPartyDataBidders == null || firstPartyDataBidders.stream()

src/main/java/org/prebid/server/auction/model/AuctionContext.java

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import org.prebid.server.activity.infrastructure.ActivityInfrastructure;
99
import org.prebid.server.auction.gpp.model.GppContext;
1010
import org.prebid.server.auction.model.debug.DebugContext;
11-
import org.prebid.server.bidadjustments.model.BidAdjustments;
1211
import org.prebid.server.cache.model.DebugHttpCall;
1312
import org.prebid.server.cookie.UidsCookie;
1413
import org.prebid.server.geolocation.model.GeoInfo;
@@ -18,7 +17,6 @@
1817
import org.prebid.server.privacy.model.PrivacyContext;
1918
import org.prebid.server.settings.model.Account;
2019

21-
import java.util.Collections;
2220
import java.util.List;
2321
import java.util.Map;
2422

@@ -73,10 +71,6 @@ public class AuctionContext {
7371

7472
CachedDebugLog cachedDebugLog;
7573

76-
@JsonIgnore
77-
@Builder.Default
78-
BidAdjustments bidAdjustments = BidAdjustments.of(Collections.emptyMap());
79-
8074
public AuctionContext with(Account account) {
8175
return this.toBuilder().account(account).build();
8276
}
@@ -130,12 +124,6 @@ public AuctionContext with(GeoInfo geoInfo) {
130124
.build();
131125
}
132126

133-
public AuctionContext with(BidAdjustments bidAdjustments) {
134-
return this.toBuilder()
135-
.bidAdjustments(bidAdjustments)
136-
.build();
137-
}
138-
139127
public AuctionContext withRequestRejected() {
140128
return this.toBuilder()
141129
.requestRejected(true)

src/main/java/org/prebid/server/auction/model/BidderRequest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
import lombok.Builder;
55
import lombok.Value;
66
import org.prebid.server.auction.versionconverter.OrtbVersion;
7+
import org.prebid.server.bidder.model.Price;
8+
9+
import java.util.Map;
710

811
@Builder(toBuilder = true)
912
@Value
@@ -17,6 +20,8 @@ public class BidderRequest {
1720

1821
BidRequest bidRequest;
1922

23+
Map<String, Price> originalPriceFloors;
24+
2025
public BidderRequest with(BidRequest bidRequest) {
2126
return toBuilder().bidRequest(bidRequest).build();
2227
}

src/main/java/org/prebid/server/auction/requestfactory/AuctionRequestFactory.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import org.prebid.server.auction.model.AuctionStoredResult;
1818
import org.prebid.server.auction.privacy.contextfactory.AuctionPrivacyContextFactory;
1919
import org.prebid.server.auction.versionconverter.BidRequestOrtbVersionConversionManager;
20-
import org.prebid.server.bidadjustments.BidAdjustmentsRetriever;
20+
import org.prebid.server.bidadjustments.BidAdjustmentsEnricher;
2121
import org.prebid.server.cookie.CookieDeprecationService;
2222
import org.prebid.server.exception.InvalidRequestException;
2323
import org.prebid.server.json.JacksonMapper;
@@ -51,7 +51,7 @@ public class AuctionRequestFactory {
5151
private final JacksonMapper mapper;
5252
private final OrtbTypesResolver ortbTypesResolver;
5353
private final GeoLocationServiceWrapper geoLocationServiceWrapper;
54-
private final BidAdjustmentsRetriever bidAdjustmentsRetriever;
54+
private final BidAdjustmentsEnricher bidAdjustmentsEnricher;
5555

5656
private static final String ENDPOINT = Endpoint.openrtb2_auction.value();
5757

@@ -69,7 +69,7 @@ public AuctionRequestFactory(long maxRequestSize,
6969
DebugResolver debugResolver,
7070
JacksonMapper mapper,
7171
GeoLocationServiceWrapper geoLocationServiceWrapper,
72-
BidAdjustmentsRetriever bidAdjustmentsRetriever) {
72+
BidAdjustmentsEnricher bidAdjustmentsEnricher) {
7373

7474
this.maxRequestSize = maxRequestSize;
7575
this.ortb2RequestFactory = Objects.requireNonNull(ortb2RequestFactory);
@@ -85,7 +85,7 @@ public AuctionRequestFactory(long maxRequestSize,
8585
this.debugResolver = Objects.requireNonNull(debugResolver);
8686
this.mapper = Objects.requireNonNull(mapper);
8787
this.geoLocationServiceWrapper = Objects.requireNonNull(geoLocationServiceWrapper);
88-
this.bidAdjustmentsRetriever = Objects.requireNonNull(bidAdjustmentsRetriever);
88+
this.bidAdjustmentsEnricher = Objects.requireNonNull(bidAdjustmentsEnricher);
8989
}
9090

9191
/**
@@ -146,7 +146,7 @@ public Future<AuctionContext> enrichAuctionContext(AuctionContext initialContext
146146
.compose(auctionContext -> ortb2RequestFactory.enrichBidRequestWithAccountAndPrivacyData(auctionContext)
147147
.map(auctionContext::with))
148148

149-
.map(auctionContext -> auctionContext.with(bidAdjustmentsRetriever.retrieve(auctionContext)))
149+
.map(auctionContext -> auctionContext.with(bidAdjustmentsEnricher.enrichBidRequest(auctionContext)))
150150

151151
.compose(auctionContext -> ortb2RequestFactory.executeProcessedAuctionRequestHooks(auctionContext)
152152
.map(auctionContext::with))

src/main/java/org/prebid/server/auction/adjustment/BidAdjustmentFactorResolver.java renamed to src/main/java/org/prebid/server/bidadjustments/BidAdjustmentFactorResolver.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.prebid.server.auction.adjustment;
1+
package org.prebid.server.bidadjustments;
22

33
import org.apache.commons.collections4.MapUtils;
44
import org.apache.commons.lang3.StringUtils;
@@ -38,6 +38,7 @@ private static Optional<BigDecimal> resolveFromMediaTypes(
3838
}
3939

4040
return Optional.ofNullable(mediaType)
41+
.map(type -> type == ImpMediaType.video_instream ? ImpMediaType.video : type)
4142
.map(adjustmentFactors::get)
4243
.flatMap(factors -> factors.entrySet().stream()
4344
.filter(entry -> StringUtils.equalsIgnoreCase(entry.getKey(), bidderCode))

src/main/java/org/prebid/server/bidadjustments/BidAdjustmentRulesValidator.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import org.apache.commons.collections4.MapUtils;
44
import org.apache.commons.lang3.StringUtils;
55
import org.prebid.server.bidadjustments.model.BidAdjustmentType;
6-
import org.prebid.server.proto.openrtb.ext.request.ExtRequestBidAdjustments;
7-
import org.prebid.server.proto.openrtb.ext.request.ExtRequestBidAdjustmentsRule;
6+
import org.prebid.server.bidadjustments.model.BidAdjustments;
7+
import org.prebid.server.bidadjustments.model.BidAdjustmentsRule;
88
import org.prebid.server.proto.openrtb.ext.request.ImpMediaType;
99
import org.prebid.server.validation.ValidationException;
1010

@@ -16,7 +16,7 @@
1616
public class BidAdjustmentRulesValidator {
1717

1818
public static final Set<String> SUPPORTED_MEDIA_TYPES = Set.of(
19-
BidAdjustmentsResolver.WILDCARD,
19+
BidAdjustmentsRulesResolver.WILDCARD,
2020
ImpMediaType.banner.toString(),
2121
ImpMediaType.audio.toString(),
2222
ImpMediaType.video_instream.toString(),
@@ -27,26 +27,26 @@ private BidAdjustmentRulesValidator() {
2727

2828
}
2929

30-
public static void validate(ExtRequestBidAdjustments bidAdjustments) throws ValidationException {
30+
public static void validate(BidAdjustments bidAdjustments) throws ValidationException {
3131
if (bidAdjustments == null) {
3232
return;
3333
}
3434

35-
final Map<String, Map<String, Map<String, List<ExtRequestBidAdjustmentsRule>>>> mediatypes =
36-
bidAdjustments.getMediatype();
35+
final Map<String, Map<String, Map<String, List<BidAdjustmentsRule>>>> mediatypes =
36+
bidAdjustments.getRules();
3737

3838
if (MapUtils.isEmpty(mediatypes)) {
3939
return;
4040
}
4141

4242
for (String mediatype : mediatypes.keySet()) {
4343
if (SUPPORTED_MEDIA_TYPES.contains(mediatype)) {
44-
final Map<String, Map<String, List<ExtRequestBidAdjustmentsRule>>> bidders = mediatypes.get(mediatype);
44+
final Map<String, Map<String, List<BidAdjustmentsRule>>> bidders = mediatypes.get(mediatype);
4545
if (MapUtils.isEmpty(bidders)) {
4646
throw new ValidationException("no bidders found in %s".formatted(mediatype));
4747
}
4848
for (String bidder : bidders.keySet()) {
49-
final Map<String, List<ExtRequestBidAdjustmentsRule>> deals = bidders.get(bidder);
49+
final Map<String, List<BidAdjustmentsRule>> deals = bidders.get(bidder);
5050

5151
if (MapUtils.isEmpty(deals)) {
5252
throw new ValidationException("no deals found in %s.%s".formatted(mediatype, bidder));
@@ -61,14 +61,14 @@ public static void validate(ExtRequestBidAdjustments bidAdjustments) throws Vali
6161
}
6262
}
6363

64-
private static void validateRules(List<ExtRequestBidAdjustmentsRule> rules,
64+
private static void validateRules(List<BidAdjustmentsRule> rules,
6565
String path) throws ValidationException {
6666

6767
if (rules == null) {
6868
throw new ValidationException("no bid adjustment rules found in %s".formatted(path));
6969
}
7070

71-
for (ExtRequestBidAdjustmentsRule rule : rules) {
71+
for (BidAdjustmentsRule rule : rules) {
7272
final BidAdjustmentType type = rule.getAdjType();
7373
final String currency = rule.getCurrency();
7474
final BigDecimal value = rule.getValue();
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package org.prebid.server.bidadjustments;
2+
3+
import com.fasterxml.jackson.databind.JsonNode;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import com.fasterxml.jackson.databind.node.ObjectNode;
6+
import com.iab.openrtb.request.BidRequest;
7+
import org.prebid.server.auction.model.AuctionContext;
8+
import org.prebid.server.json.JacksonMapper;
9+
import org.prebid.server.json.JsonMerger;
10+
import org.prebid.server.log.ConditionalLogger;
11+
import org.prebid.server.log.Logger;
12+
import org.prebid.server.log.LoggerFactory;
13+
import org.prebid.server.proto.openrtb.ext.request.ExtRequest;
14+
import org.prebid.server.bidadjustments.model.BidAdjustments;
15+
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid;
16+
import org.prebid.server.settings.model.Account;
17+
import org.prebid.server.settings.model.AccountAuctionConfig;
18+
import org.prebid.server.validation.ValidationException;
19+
20+
import java.util.List;
21+
import java.util.Objects;
22+
import java.util.Optional;
23+
24+
public class BidAdjustmentsEnricher {
25+
26+
private static final Logger logger = LoggerFactory.getLogger(BidAdjustmentsEnricher.class);
27+
private static final ConditionalLogger conditionalLogger = new ConditionalLogger(logger);
28+
29+
private final ObjectMapper mapper;
30+
private final JacksonMapper jacksonMapper;
31+
private final JsonMerger jsonMerger;
32+
private final double samplingRate;
33+
34+
public BidAdjustmentsEnricher(JacksonMapper mapper, JsonMerger jsonMerger, double samplingRate) {
35+
this.jacksonMapper = Objects.requireNonNull(mapper);
36+
this.mapper = mapper.mapper();
37+
this.jsonMerger = Objects.requireNonNull(jsonMerger);
38+
this.samplingRate = samplingRate;
39+
}
40+
41+
public BidRequest enrichBidRequest(AuctionContext auctionContext) {
42+
final BidRequest bidRequest = auctionContext.getBidRequest();
43+
final List<String> debugWarnings = auctionContext.getDebugWarnings();
44+
final boolean debugEnabled = auctionContext.getDebugContext().isDebugEnabled();
45+
46+
final JsonNode requestNode = Optional.ofNullable(bidRequest.getExt())
47+
.map(ExtRequest::getPrebid)
48+
.map(ExtRequestPrebid::getBidadjustments)
49+
.orElseGet(mapper::createObjectNode);
50+
51+
final JsonNode accountNode = Optional.ofNullable(auctionContext.getAccount())
52+
.map(Account::getAuction)
53+
.map(AccountAuctionConfig::getBidAdjustments)
54+
.orElseGet(mapper::createObjectNode);
55+
56+
final JsonNode mergedNode = jsonMerger.merge(requestNode, accountNode);
57+
58+
final List<String> resolvedWarnings = debugEnabled ? debugWarnings : null;
59+
final JsonNode resolvedBidAdjustments = convertAndValidate(mergedNode, resolvedWarnings, "request")
60+
.or(() -> convertAndValidate(accountNode, resolvedWarnings, "account"))
61+
.orElse(null);
62+
63+
return bidRequest.toBuilder()
64+
.ext(updateExtRequestWithBidAdjustments(bidRequest, resolvedBidAdjustments))
65+
.build();
66+
}
67+
68+
private Optional<JsonNode> convertAndValidate(JsonNode bidAdjustmentsNode,
69+
List<String> debugWarnings,
70+
String errorLocation) {
71+
72+
if (bidAdjustmentsNode.isEmpty()) {
73+
return Optional.empty();
74+
}
75+
76+
try {
77+
final BidAdjustments bidAdjustments = mapper.convertValue(bidAdjustmentsNode, BidAdjustments.class);
78+
79+
BidAdjustmentRulesValidator.validate(bidAdjustments);
80+
return Optional.of(bidAdjustmentsNode);
81+
} catch (IllegalArgumentException | ValidationException e) {
82+
final String message = "bid adjustment from " + errorLocation + " was invalid: " + e.getMessage();
83+
if (debugWarnings != null) {
84+
debugWarnings.add(message);
85+
}
86+
conditionalLogger.error(message, samplingRate);
87+
return Optional.empty();
88+
}
89+
}
90+
91+
private ExtRequest updateExtRequestWithBidAdjustments(BidRequest bidRequest, JsonNode bidAdjustments) {
92+
final ExtRequest extRequest = bidRequest.getExt();
93+
final ExtRequestPrebid updatedPrebid = Optional.ofNullable(extRequest)
94+
.map(ExtRequest::getPrebid)
95+
.map(ExtRequestPrebid::toBuilder)
96+
.orElse(ExtRequestPrebid.builder())
97+
.bidadjustments((ObjectNode) bidAdjustments)
98+
.build();
99+
100+
final ExtRequest updatedExtRequest = ExtRequest.of(updatedPrebid);
101+
return extRequest == null
102+
? updatedExtRequest
103+
: jacksonMapper.fillExtension(updatedExtRequest, extRequest.getProperties());
104+
}
105+
}

0 commit comments

Comments
 (0)