Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add category mapping enabled toggle #1565

Merged
merged 3 commits into from
Nov 9, 2021
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
1 change: 1 addition & 0 deletions docs/config-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ Removes and downloads file again if depending service cant process probably corr
- `auction.validations.banner-creative-max-size` - enables creative max size validation for banners. Possible values: `skip`, `enforce`, `warn`. Default is `skip`.
- `auction.validations.secure-markup` - enables secure markup validation. Possible values: `skip`, `enforce`, `warn`. Default is `skip`.
- `auction.host-schain-node` - defines global schain node that will be appended to `request.source.ext.schain.nodes` passed to bidders
- `auction.category-mapping-enabled` - if equals to `true` the category mapping feature will be active while auction.

## Event
- `event.default-timeout-ms` - timeout for event notifications
Expand Down
175 changes: 94 additions & 81 deletions src/main/java/org/prebid/server/auction/BidResponseCreator.java

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/main/java/org/prebid/server/auction/PriceGranularity.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ private PriceGranularity(List<ExtGranularityRange> ranges, BigDecimal rangesMax,
/**
* Creates {@link PriceGranularity} from {@link ExtPriceGranularity}.
*/
static PriceGranularity createFromExtPriceGranularity(ExtPriceGranularity extPriceGranularity) {
public static PriceGranularity createFromExtPriceGranularity(ExtPriceGranularity extPriceGranularity) {
return createFromRanges(extPriceGranularity.getPrecision(), extPriceGranularity.getRanges());
}

Expand Down Expand Up @@ -107,7 +107,7 @@ private static void putStringPriceGranularity(PriceGranularityType type, Integer
/**
* Creates {@link PriceGranularity} from list of {@link ExtGranularityRange}s and validates it.
*/
private static PriceGranularity createFromRanges(Integer precision, List<ExtGranularityRange> ranges) {
public static PriceGranularity createFromRanges(Integer precision, List<ExtGranularityRange> ranges) {

final BigDecimal rangeMax = CollectionUtils.emptyIfNull(ranges).stream()
.filter(Objects::nonNull)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public class VideoStoredRequestProcessor {
private VideoStoredRequestProcessor(boolean enforceStoredRequest,
List<String> blacklistedAccounts,
long defaultTimeout,
String adServerCurrency,
String currency,
BidRequest defaultBidRequest,
ApplicationSettings applicationSettings,
VideoRequestValidator validator,
Expand All @@ -87,7 +87,7 @@ private VideoStoredRequestProcessor(boolean enforceStoredRequest,
this.enforceStoredRequest = enforceStoredRequest;
this.blacklistedAccounts = blacklistedAccounts;
this.defaultTimeout = defaultTimeout;
this.currency = StringUtils.isBlank(adServerCurrency) ? DEFAULT_CURRENCY : adServerCurrency;
this.currency = currency;
this.defaultBidRequest = defaultBidRequest;
this.applicationSettings = applicationSettings;
this.validator = validator;
Expand Down Expand Up @@ -116,7 +116,7 @@ public static VideoStoredRequestProcessor create(boolean enforceStoredRequest,
enforceStoredRequest,
Objects.requireNonNull(blacklistedAccounts),
defaultTimeout,
adServerCurrency,
StringUtils.isBlank(adServerCurrency) ? DEFAULT_CURRENCY : adServerCurrency,
readBidRequest(
defaultBidRequestPath, Objects.requireNonNull(fileSystem), Objects.requireNonNull(mapper)),
Objects.requireNonNull(applicationSettings),
Expand All @@ -137,39 +137,44 @@ public Future<WithPodErrors<BidRequest>> processVideoRequest(String accountId,
BidRequestVideo videoRequest) {

final Set<String> storedRequestIds = StringUtils.isNotBlank(storedBidRequestId)
? Collections.singleton(storedBidRequestId)
: Collections.emptySet();
? Collections.singleton(storedBidRequestId)
: Collections.emptySet();

return applicationSettings.getVideoStoredData(accountId, storedRequestIds, podIds,
timeoutFactory.create(defaultTimeout))
.compose(storedDataResult -> updateMetrics(storedDataResult, storedRequestIds, podIds))
.map(storedData -> mergeToBidRequest(storedData, videoRequest, storedBidRequestId))

.map(storedDataResult -> updateMetrics(storedDataResult, storedRequestIds, podIds))

.map(storedData -> toBidRequestWithPodErrors(storedData, videoRequest, storedBidRequestId))

.recover(exception -> Future.failedFuture(new InvalidRequestException(
String.format("Stored request fetching failed: %s", exception.getMessage()))));
}

private static BidRequest readBidRequest(String defaultBidRequestPath, FileSystem fileSystem,
private static BidRequest readBidRequest(String defaultBidRequestPath,
FileSystem fileSystem,
JacksonMapper mapper) {

return StringUtils.isNotBlank(defaultBidRequestPath)
? mapper.decodeValue(fileSystem.readFileBlocking(defaultBidRequestPath), BidRequest.class)
: null;
? mapper.decodeValue(fileSystem.readFileBlocking(defaultBidRequestPath), BidRequest.class)
: null;
}

private Future<StoredDataResult> updateMetrics(StoredDataResult storedDataResult,
Set<String> requestIds,
Set<String> impIds) {
private StoredDataResult updateMetrics(StoredDataResult storedDataResult,
Set<String> requestIds,
Set<String> impIds) {

requestIds.forEach(
id -> metrics.updateStoredRequestMetric(storedDataResult.getStoredIdToRequest().containsKey(id)));
impIds.forEach(
id -> metrics.updateStoredImpsMetric(storedDataResult.getStoredIdToImp().containsKey(id)));

return Future.succeededFuture(storedDataResult);
return storedDataResult;
}

private WithPodErrors<BidRequest> mergeToBidRequest(StoredDataResult storedResult,
BidRequestVideo videoRequest,
String storedBidRequestId) {
private WithPodErrors<BidRequest> toBidRequestWithPodErrors(StoredDataResult storedResult,
BidRequestVideo videoRequest,
String storedBidRequestId) {

final BidRequestVideo mergedStoredRequest = mergeBidRequest(videoRequest, storedBidRequestId, storedResult);
validator.validateStoredBidRequest(mergedStoredRequest, enforceStoredRequest, blacklistedAccounts);
Expand All @@ -194,8 +199,8 @@ private BidRequestVideo mergeBidRequest(BidRequestVideo originalRequest,
}

return StringUtils.isNotBlank(storedRequest)
? jsonMerger.merge(originalRequest, storedRequest, storedRequestId, BidRequestVideo.class)
: originalRequest;
? jsonMerger.merge(originalRequest, storedRequest, storedRequestId, BidRequestVideo.class)
: originalRequest;
}

private WithPodErrors<List<Imp>> mergeStoredImps(Podconfig podconfig,
Expand Down Expand Up @@ -306,28 +311,27 @@ private static Video updateVideo(Video video, Integer minDuration, Integer maxDu

private BidRequest mergeWithDefaultBidRequest(BidRequestVideo videoRequest, List<Imp> imps) {
final BidRequest.BidRequestBuilder bidRequestBuilder = defaultBidRequest != null
? defaultBidRequest.toBuilder()
: BidRequest.builder();
? defaultBidRequest.toBuilder()
: BidRequest.builder();

final Site site = videoRequest.getSite();
if (site != null) {
final Site.SiteBuilder siteBuilder = site.toBuilder();
final Content content = videoRequest.getContent();
if (content != null) {
siteBuilder.content(content);
bidRequestBuilder.site(site.toBuilder()
.content(content)
.build());
}
siteBuilder.content(content);
bidRequestBuilder.site(siteBuilder.build());
}

final App app = videoRequest.getApp();
if (app != null) {
final App.AppBuilder appBuilder = app.toBuilder();
final Content content = videoRequest.getContent();
if (content != null) {
appBuilder.content(content);
bidRequestBuilder.app(app.toBuilder()
.content(content)
.build());
}
bidRequestBuilder.app(appBuilder.build());
}

final Device device = videoRequest.getDevice();
Expand Down Expand Up @@ -355,8 +359,8 @@ private BidRequest mergeWithDefaultBidRequest(BidRequestVideo videoRequest, List
bidRequestBuilder.regs(regs);
}

final Long videoTmax = timeoutResolver.resolve(videoRequest.getTmax());
bidRequestBuilder.tmax(videoTmax);
final long timeout = timeoutResolver.resolve(videoRequest.getTmax());
bidRequestBuilder.tmax(timeout);

addRequiredOpenRtbFields(bidRequestBuilder);

Expand All @@ -373,48 +377,50 @@ private void addRequiredOpenRtbFields(BidRequest.BidRequestBuilder bidRequestBui
}

private ExtRequest createExtRequest(BidRequestVideo videoRequest) {
final ExtIncludeBrandCategory extIncludeBrandCategory;
final IncludeBrandCategory includebrandcategory = videoRequest.getIncludebrandcategory();
if (includebrandcategory != null) {
extIncludeBrandCategory = ExtIncludeBrandCategory.of(
includebrandcategory.getPrimaryAdserver(),
includebrandcategory.getPublisher(),
true,
includebrandcategory.getTranslateCategories());
} else {
extIncludeBrandCategory = ExtIncludeBrandCategory.of(null, null, false, null);
}
final ExtRequestPrebidCache cache = ExtRequestPrebidCache.of(null,
ExtRequestPrebidCacheVastxml.of(null, null), null);

List<Integer> durationRangeSec = null;
if (BooleanUtils.isFalse(videoRequest.getPodconfig().getRequireExactDuration())) {
durationRangeSec = videoRequest.getPodconfig().getDurationRangeSec();
}
final ExtIncludeBrandCategory extIncludeBrandCategory = createExtIncludeBrandCategory(videoRequest);

final Podconfig podconfig = videoRequest.getPodconfig();
final List<Integer> durationRangeSec = BooleanUtils.isFalse(podconfig.getRequireExactDuration())
? podconfig.getDurationRangeSec()
: null;

final PriceGranularity priceGranularity = videoRequest.getPricegranularity();
final Integer precision = priceGranularity != null
? priceGranularity.getPrecision()
: null;

PriceGranularity updatedPriceGranularity = precision != null && precision != 0
? priceGranularity
: PriceGranularity.createFromString("med");
? priceGranularity.getPrecision()
: null;
final PriceGranularity updatedPriceGranularity = precision != null && precision != 0
? priceGranularity
: PriceGranularity.createFromString("med");

final ExtRequestTargeting targeting = ExtRequestTargeting.builder()
.pricegranularity(mapper.mapper().valueToTree(updatedPriceGranularity))
.includebidderkeys(true)
.includebrandcategory(extIncludeBrandCategory)
.durationrangesec(durationRangeSec)
.pricegranularity(mapper.mapper().valueToTree(updatedPriceGranularity))
.appendbiddernames(videoRequest.getAppendbiddernames())
.build();

final ExtRequestPrebidCache extReqPrebidCache = ExtRequestPrebidCache.of(null,
ExtRequestPrebidCacheVastxml.of(null, null), null);

final ExtRequestPrebid extRequestPrebid = ExtRequestPrebid.builder()
.cache(extReqPrebidCache)
.cache(cache)
.targeting(targeting)
.build();

return ExtRequest.of(extRequestPrebid);
}

private static ExtIncludeBrandCategory createExtIncludeBrandCategory(BidRequestVideo videoRequest) {
final IncludeBrandCategory includeBrandCategory = videoRequest.getIncludebrandcategory();
if (includeBrandCategory != null) {
return ExtIncludeBrandCategory.of(
includeBrandCategory.getPrimaryAdserver(),
includeBrandCategory.getPublisher(),
true,
includeBrandCategory.getTranslateCategories());
}

return ExtIncludeBrandCategory.of(null, null, false, null);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.prebid.server.auction;
package org.prebid.server.auction.categorymapping;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
Expand All @@ -19,6 +19,8 @@
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.prebid.server.auction.CpmRange;
import org.prebid.server.auction.PriceGranularity;
import org.prebid.server.auction.model.BidderResponse;
import org.prebid.server.auction.model.CategoryMappingResult;
import org.prebid.server.auction.requestfactory.Ortb2ImplicitParametersResolver;
Expand Down Expand Up @@ -55,7 +57,7 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class CategoryMappingService {
public class BasicCategoryMappingService implements CategoryMappingService {

private static final TypeReference<Map<String, DealTierContainer>> EXT_IMP_DEAL_TIER_REFERENCE =
new TypeReference<>() {
Expand All @@ -67,7 +69,7 @@ public class CategoryMappingService {
private final ApplicationSettings applicationSettings;
private final JacksonMapper jacksonMapper;

public CategoryMappingService(ApplicationSettings applicationSettings, JacksonMapper jacksonMapper) {
public BasicCategoryMappingService(ApplicationSettings applicationSettings, JacksonMapper jacksonMapper) {
this.applicationSettings = Objects.requireNonNull(applicationSettings);
this.jacksonMapper = Objects.requireNonNull(jacksonMapper);
}
Expand All @@ -78,21 +80,18 @@ public CategoryMappingService(ApplicationSettings applicationSettings, JacksonMa
* Removes the bids with duplicated category key, leaving one with the highest price.
* Returns list of errors for each dropped bid.
*/
@Override
public Future<CategoryMappingResult> createCategoryMapping(List<BidderResponse> bidderResponses,
BidRequest bidRequest,
ExtRequestTargeting targeting,
Timeout timeout) {

final ExtRequestTargeting targeting = targeting(bidRequest);

final ExtIncludeBrandCategory includeBrandCategory = ObjectUtil.getIfNotNull(
targeting, ExtRequestTargeting::getIncludebrandcategory);

if (includeBrandCategory == null) {
return Future.succeededFuture(
CategoryMappingResult.of(
Collections.emptyMap(),
Collections.emptyMap(),
bidderResponses,
Collections.emptyList()));
return Future.succeededFuture(CategoryMappingResult.of(bidderResponses));
}

final boolean withCategory = BooleanUtils.toBooleanDefaultIfNull(
Expand All @@ -116,6 +115,12 @@ public Future<CategoryMappingResult> createCategoryMapping(List<BidderResponse>
bidderResponses, categoryBidContexts, bidRequest, targeting, withCategory, rejectedBids));
}

private static ExtRequestTargeting targeting(BidRequest bidRequest) {
final ExtRequest ext = bidRequest.getExt();
final ExtRequestPrebid prebid = ext != null ? ext.getPrebid() : null;
return prebid != null ? prebid.getTargeting() : null;
}

/**
* Converts integer ad server to string representation or throws exception if unknown.
*/
Expand Down Expand Up @@ -592,7 +597,7 @@ private static Set<RejectedBid> collectRejectedDuplicatedBids(

return categoryToDuplicatedCategoryBids.values().stream()
.filter(categoryBids -> categoryBids.size() > 1)
.flatMap(CategoryMappingService::getDuplicatedForCategory)
.flatMap(BasicCategoryMappingService::getDuplicatedForCategory)
.map(categoryBidContext -> RejectedBid.of(
extractBidId(categoryBidContext),
categoryBidContext.getBidder(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.prebid.server.auction.categorymapping;

import com.iab.openrtb.request.BidRequest;
import io.vertx.core.Future;
import org.prebid.server.auction.model.BidderResponse;
import org.prebid.server.auction.model.CategoryMappingResult;
import org.prebid.server.execution.Timeout;

import java.util.List;

public interface CategoryMappingService {

Future<CategoryMappingResult> createCategoryMapping(List<BidderResponse> bidderResponses,
BidRequest bidRequest,
Timeout timeout);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.prebid.server.auction.categorymapping;

import com.iab.openrtb.request.BidRequest;
import io.vertx.core.Future;
import org.prebid.server.auction.model.BidderResponse;
import org.prebid.server.auction.model.CategoryMappingResult;
import org.prebid.server.execution.Timeout;

import java.util.List;

public class NoOpCategoryMappingService implements CategoryMappingService {

@Override
public Future<CategoryMappingResult> createCategoryMapping(List<BidderResponse> bidderResponses,
BidRequest bidRequest,
Timeout timeout) {

return Future.succeededFuture(CategoryMappingResult.of(bidderResponses));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.iab.openrtb.response.Bid;
import lombok.Value;

import java.util.Collections;
import java.util.List;
import java.util.Map;

Expand All @@ -17,6 +18,14 @@ public class CategoryMappingResult {

List<String> errors;

public static CategoryMappingResult of(List<BidderResponse> bidderResponses) {
return CategoryMappingResult.of(
Collections.emptyMap(),
Collections.emptyMap(),
bidderResponses,
Collections.emptyList());
}

public String getCategory(Bid bid) {
return biddersToBidsCategories.get(bid);
}
Expand Down
Loading