Skip to content

Commit 98e8065

Browse files
Price Granularity: Defaults Fix (#3511)
1 parent af321ad commit 98e8065

13 files changed

Lines changed: 406 additions & 22 deletions

File tree

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ public static PriceGranularity createFromString(String stringPriceGranularity) {
7272
}
7373
}
7474

75+
public static PriceGranularity createFromStringOrDefault(String stringPriceGranularity) {
76+
return isValidStringPriceGranularityType(stringPriceGranularity)
77+
? STRING_TO_CUSTOM_PRICE_GRANULARITY.get(PriceGranularityType.valueOf(stringPriceGranularity))
78+
: PriceGranularity.DEFAULT;
79+
}
80+
7581
/**
7682
* Returns list of {@link ExtGranularityRange}s.
7783
*/

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

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import org.prebid.server.proto.openrtb.ext.request.ExtStoredRequest;
5353
import org.prebid.server.proto.openrtb.ext.request.ExtUser;
5454
import org.prebid.server.settings.model.Account;
55+
import org.prebid.server.settings.model.AccountAuctionConfig;
5556
import org.prebid.server.util.HttpUtil;
5657

5758
import java.util.ArrayList;
@@ -60,6 +61,7 @@
6061
import java.util.List;
6162
import java.util.Map;
6263
import java.util.Objects;
64+
import java.util.Optional;
6365
import java.util.stream.Collectors;
6466

6567
public class AmpRequestFactory {
@@ -407,7 +409,7 @@ private Future<BidRequest> updateBidRequest(AuctionContext auctionContext) {
407409
.map(ortbVersionConversionManager::convertToAuctionSupportedVersion)
408410
.map(bidRequest -> gppService.updateBidRequest(bidRequest, auctionContext))
409411
.map(bidRequest -> validateStoredBidRequest(storedRequestId, bidRequest))
410-
.map(this::fillExplicitParameters)
412+
.map(bidRequest -> fillExplicitParameters(bidRequest, account))
411413
.map(bidRequest -> overrideParameters(bidRequest, httpRequest, auctionContext.getPrebidErrors()))
412414
.map(bidRequest -> paramsResolver.resolve(bidRequest, auctionContext, ENDPOINT, true))
413415
.map(bidRequest -> ortb2RequestFactory.removeEmptyEids(bidRequest, auctionContext.getDebugWarnings()))
@@ -459,7 +461,7 @@ private static BidRequest validateStoredBidRequest(String tagId, BidRequest bidR
459461
* - Sets {@link BidRequest}.test = 1 if it was passed in {@link RoutingContext}
460462
* - Updates {@link BidRequest}.ext.prebid.amp.data with all query parameters
461463
*/
462-
private BidRequest fillExplicitParameters(BidRequest bidRequest) {
464+
private BidRequest fillExplicitParameters(BidRequest bidRequest, Account account) {
463465
final List<Imp> imps = bidRequest.getImp();
464466
// Force HTTPS as AMP requires it, but pubs can forget to set it.
465467
final Imp imp = imps.getFirst();
@@ -496,6 +498,7 @@ private BidRequest fillExplicitParameters(BidRequest bidRequest) {
496498
.imp(setSecure ? Collections.singletonList(imps.getFirst().toBuilder().secure(1).build()) : imps)
497499
.ext(extRequest(
498500
bidRequest,
501+
account,
499502
setDefaultTargeting,
500503
setDefaultCache))
501504
.build();
@@ -692,6 +695,7 @@ private static List<Format> parseMultiSizeParam(String ms) {
692695
* Creates updated bidrequest.ext {@link ObjectNode}.
693696
*/
694697
private ExtRequest extRequest(BidRequest bidRequest,
698+
Account account,
695699
boolean setDefaultTargeting,
696700
boolean setDefaultCache) {
697701

@@ -704,7 +708,7 @@ private ExtRequest extRequest(BidRequest bidRequest,
704708
: ExtRequestPrebid.builder();
705709

706710
if (setDefaultTargeting) {
707-
prebidBuilder.targeting(createTargetingWithDefaults(prebid));
711+
prebidBuilder.targeting(createTargetingWithDefaults(prebid, account));
708712
}
709713
if (setDefaultCache) {
710714
prebidBuilder.cache(ExtRequestPrebidCache.of(ExtRequestPrebidCacheBids.of(null, null),
@@ -727,15 +731,14 @@ private ExtRequest extRequest(BidRequest bidRequest,
727731
* Creates updated with default values bidrequest.ext.targeting {@link ExtRequestTargeting} if at least one of it's
728732
* child properties is missed or entire targeting does not exist.
729733
*/
730-
private ExtRequestTargeting createTargetingWithDefaults(ExtRequestPrebid prebid) {
734+
private ExtRequestTargeting createTargetingWithDefaults(ExtRequestPrebid prebid, Account account) {
731735
final ExtRequestTargeting targeting = prebid != null ? prebid.getTargeting() : null;
732736
final boolean isTargetingNull = targeting == null;
733737

734738
final JsonNode priceGranularityNode = isTargetingNull ? null : targeting.getPricegranularity();
735739
final boolean isPriceGranularityNull = priceGranularityNode == null || priceGranularityNode.isNull();
736-
final JsonNode outgoingPriceGranularityNode
737-
= isPriceGranularityNull
738-
? mapper.mapper().valueToTree(ExtPriceGranularity.from(PriceGranularity.DEFAULT))
740+
final JsonNode outgoingPriceGranularityNode = isPriceGranularityNull
741+
? mapper.mapper().valueToTree(ExtPriceGranularity.from(getDefaultPriceGranularity(account)))
739742
: priceGranularityNode;
740743

741744
final ExtMediaTypePriceGranularity mediaTypePriceGranularity = isTargetingNull
@@ -759,6 +762,14 @@ private ExtRequestTargeting createTargetingWithDefaults(ExtRequestPrebid prebid)
759762
.build();
760763
}
761764

765+
private static PriceGranularity getDefaultPriceGranularity(Account account) {
766+
return Optional.ofNullable(account)
767+
.map(Account::getAuction)
768+
.map(AccountAuctionConfig::getPriceGranularity)
769+
.map(PriceGranularity::createFromStringOrDefault)
770+
.orElse(PriceGranularity.DEFAULT);
771+
}
772+
762773
@Value(staticConstructor = "of")
763774
private static class GppSidExtraction {
764775

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

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
import org.prebid.server.proto.openrtb.ext.request.ExtRequestTargeting;
5757
import org.prebid.server.proto.openrtb.ext.request.ExtSite;
5858
import org.prebid.server.proto.openrtb.ext.response.BidType;
59+
import org.prebid.server.settings.model.Account;
60+
import org.prebid.server.settings.model.AccountAuctionConfig;
5961
import org.prebid.server.util.HttpUtil;
6062
import org.prebid.server.util.ObjectUtil;
6163
import org.prebid.server.util.StreamUtil;
@@ -187,7 +189,11 @@ public BidRequest resolve(BidRequest bidRequest,
187189
final ExtRequest ext = bidRequest.getExt();
188190
final List<Imp> imps = bidRequest.getImp();
189191
final ExtRequest populatedExt = populateRequestExt(
190-
ext, bidRequest, ObjectUtils.defaultIfNull(populatedImps, imps), endpoint);
192+
ext,
193+
bidRequest,
194+
ObjectUtils.defaultIfNull(populatedImps, imps),
195+
endpoint,
196+
auctionContext.getAccount());
191197

192198
final Source source = bidRequest.getSource();
193199
final Source populatedSource = populateSource(source, populatedExt, hasStoredBidRequest);
@@ -713,10 +719,15 @@ private static boolean isUniqueIds(List<Imp> imps) {
713719
return impIdsSet.size() == impIdsList.size();
714720
}
715721

716-
private ExtRequest populateRequestExt(ExtRequest ext, BidRequest bidRequest, List<Imp> imps, String endpoint) {
722+
private ExtRequest populateRequestExt(ExtRequest ext,
723+
BidRequest bidRequest,
724+
List<Imp> imps,
725+
String endpoint,
726+
Account account) {
727+
717728
final ExtRequestPrebid prebid = ObjectUtil.getIfNotNull(ext, ExtRequest::getPrebid);
718729

719-
final ExtRequestTargeting updatedTargeting = targetingOrNull(prebid, imps);
730+
final ExtRequestTargeting updatedTargeting = targetingOrNull(prebid, imps, account);
720731
final ExtRequestPrebidCache updatedCache = cacheOrNull(prebid);
721732
final ExtRequestPrebidChannel updatedChannel = channelOrNull(prebid, bidRequest, endpoint);
722733

@@ -783,7 +794,7 @@ private static void resolveImpMediaTypes(Imp imp, Set<BidType> impsMediaTypes) {
783794
/**
784795
* Returns populated {@link ExtRequestTargeting} or null if no changes were applied.
785796
*/
786-
private ExtRequestTargeting targetingOrNull(ExtRequestPrebid prebid, List<Imp> imps) {
797+
private ExtRequestTargeting targetingOrNull(ExtRequestPrebid prebid, List<Imp> imps, Account account) {
787798
final ExtRequestTargeting targeting = prebid != null ? prebid.getTargeting() : null;
788799

789800
final boolean isTargetingNotNull = targeting != null;
@@ -796,8 +807,12 @@ private ExtRequestTargeting targetingOrNull(ExtRequestPrebid prebid, List<Imp> i
796807

797808
if (isPriceGranularityNull || isPriceGranularityTextual || isIncludeWinnersNull || isIncludeBidderKeysNull) {
798809
return targeting.toBuilder()
799-
.pricegranularity(resolvePriceGranularity(targeting, isPriceGranularityNull,
800-
isPriceGranularityTextual, imps))
810+
.pricegranularity(resolvePriceGranularity(
811+
targeting,
812+
isPriceGranularityNull,
813+
isPriceGranularityTextual,
814+
imps,
815+
account))
801816
.includewinners(isIncludeWinnersNull || targeting.getIncludewinners())
802817
.includebidderkeys(isIncludeBidderKeysNull
803818
? !isWinningOnly(prebid.getCache())
@@ -822,14 +837,22 @@ private boolean isWinningOnly(ExtRequestPrebidCache cache) {
822837
* In case of valid string price granularity replaced it with appropriate custom view.
823838
* In case of invalid string value throws {@link InvalidRequestException}.
824839
*/
825-
private JsonNode resolvePriceGranularity(ExtRequestTargeting targeting, boolean isPriceGranularityNull,
826-
boolean isPriceGranularityTextual, List<Imp> imps) {
840+
private JsonNode resolvePriceGranularity(ExtRequestTargeting targeting,
841+
boolean isPriceGranularityNull,
842+
boolean isPriceGranularityTextual,
843+
List<Imp> imps,
844+
Account account) {
827845

828846
final boolean hasAllMediaTypes = checkExistingMediaTypes(targeting.getMediatypepricegranularity())
829847
.containsAll(getImpMediaTypes(imps));
830848

831849
if (isPriceGranularityNull && !hasAllMediaTypes) {
832-
return mapper.mapper().valueToTree(ExtPriceGranularity.from(PriceGranularity.DEFAULT));
850+
final PriceGranularity defaultPriceGranularity = Optional.ofNullable(account)
851+
.map(Account::getAuction)
852+
.map(AccountAuctionConfig::getPriceGranularity)
853+
.map(PriceGranularity::createFromStringOrDefault)
854+
.orElse(PriceGranularity.DEFAULT);
855+
return mapper.mapper().valueToTree(ExtPriceGranularity.from(defaultPriceGranularity));
833856
}
834857

835858
final JsonNode priceGranularityNode = targeting.getPricegranularity();

src/test/groovy/org/prebid/server/functional/model/config/AccountAuctionConfig.groovy

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import org.prebid.server.functional.model.response.auction.MediaType
1212
@JsonNaming(PropertyNamingStrategies.KebabCaseStrategy)
1313
class AccountAuctionConfig {
1414

15-
String priceGranularity
15+
PriceGranularityType priceGranularity
1616
Integer bannerCacheTtl
1717
Integer videoCacheTtl
1818
Integer truncateTargetAttr
@@ -28,7 +28,7 @@ class AccountAuctionConfig {
2828
PrivacySandbox privacySandbox
2929

3030
@JsonProperty("price_granularity")
31-
String priceGranularitySnakeCase
31+
PriceGranularityType priceGranularitySnakeCase
3232
@JsonProperty("banner_cache_ttl")
3333
Integer bannerCacheTtlSnakeCase
3434
@JsonProperty("video_cache_ttl")
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.prebid.server.functional.model.config
2+
3+
import com.fasterxml.jackson.annotation.JsonValue
4+
import org.prebid.server.functional.model.request.auction.Range
5+
6+
enum PriceGranularityType {
7+
8+
LOW(2, [Range.getDefault(5, 0.5)]),
9+
MEDIUM(2, [Range.getDefault(20, 0.1)]),
10+
MED(2, [Range.getDefault(20, 0.1)]),
11+
HIGH(2, [Range.getDefault(20, 0.01)]),
12+
AUTO(2, [Range.getDefault(5, 0.05), Range.getDefault(10, 0.1), Range.getDefault(20, 0.5)]),
13+
DENSE(2, [Range.getDefault(3, 0.01), Range.getDefault(8, 0.05), Range.getDefault(20, 0.5)]),
14+
UNKNOWN(null, [])
15+
16+
final Integer precision
17+
final List<Range> ranges
18+
19+
PriceGranularityType(Integer precision, List<Range> ranges) {
20+
this.precision = precision
21+
this.ranges = ranges
22+
}
23+
24+
@JsonValue
25+
String toLowerCase() {
26+
return name().toLowerCase()
27+
}
28+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
11
package org.prebid.server.functional.model.request.auction
22

3+
import groovy.transform.EqualsAndHashCode
34
import groovy.transform.ToString
5+
import org.prebid.server.functional.model.config.PriceGranularityType
46

57
@ToString(includeNames = true, ignoreNulls = true)
8+
@EqualsAndHashCode
69
class PriceGranularity {
710

811
Integer precision
912
List<Range> ranges
13+
14+
static PriceGranularity getDefault(PriceGranularityType granularity) {
15+
new PriceGranularity(precision: granularity.precision, ranges: granularity.ranges)
16+
}
17+
18+
static PriceGranularity getDefault() {
19+
getDefault(PriceGranularityType.MED)
20+
}
1021
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
package org.prebid.server.functional.model.request.auction
22

3+
import groovy.transform.EqualsAndHashCode
34
import groovy.transform.ToString
45

56
@ToString(includeNames = true, ignoreNulls = true)
7+
@EqualsAndHashCode
68
class Range {
79

810
BigDecimal max
911
BigDecimal increment
12+
13+
static Range getDefault(Integer max, BigDecimal increment) {
14+
new Range(max: max, increment: increment)
15+
}
1016
}

0 commit comments

Comments
 (0)