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
1 change: 1 addition & 0 deletions docs/application-settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Keep in mind following restrictions:
- `auction.preferredmediatype.<bidder>.<media-type>` - <media-type> that will be left for <bidder> that doesn't support multi-format. Other media types will be removed. Acceptable values: `banner`, `video`, `audio`, `native`.
- `auction.privacysandbox.cookiedeprecation.enabled` - boolean that turns on setting and reading of the Chrome Privacy Sandbox testing label header. Defaults to false.
- `auction.privacysandbox.cookiedeprecation.ttlsec` - if the above setting is true, how long to set the receive-cookie-deprecation cookie's expiration
- `auction.cache.enabled` - enables bids caching for account if true. Defaults to true.
- `privacy.gdpr.enabled` - enables gdpr verifications if true. Has higher priority than configuration in
application.yaml.
- `privacy.gdpr.eea-countries` - overrides the host-level list of 2-letter country codes where TCF processing is applied
Expand Down
15 changes: 12 additions & 3 deletions src/main/java/org/prebid/server/auction/ExchangeService.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@
import org.prebid.server.proto.openrtb.ext.request.ExtUser;
import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebidMeta;
import org.prebid.server.settings.model.Account;
import org.prebid.server.settings.model.AccountAuctionConfig;
import org.prebid.server.settings.model.AccountCacheConfig;
import org.prebid.server.util.HttpUtil;
import org.prebid.server.util.ListUtil;
import org.prebid.server.util.PbsUtil;
Expand Down Expand Up @@ -241,7 +243,7 @@ private Future<AuctionContext> runAuction(AuctionContext receivedContext) {

final List<SeatBid> storedAuctionResponses = new ArrayList<>();
final BidderAliases aliases = aliases(bidRequest, account);
final BidRequestCacheInfo cacheInfo = bidRequestCacheInfo(bidRequest);
final BidRequestCacheInfo cacheInfo = bidRequestCacheInfo(bidRequest, account);
final Map<String, MultiBidConfig> bidderToMultiBid = bidderToMultiBids(bidRequest, debugWarnings);
receivedContext.getBidRejectionTrackers().putAll(makeBidRejectionTrackers(bidRequest, aliases));

Expand Down Expand Up @@ -311,12 +313,18 @@ private static ExtRequestTargeting targeting(BidRequest bidRequest) {
return prebid != null ? prebid.getTargeting() : null;
}

private static BidRequestCacheInfo bidRequestCacheInfo(BidRequest bidRequest) {
private static BidRequestCacheInfo bidRequestCacheInfo(BidRequest bidRequest, Account account) {
final boolean cachingEnabled = Optional.ofNullable(account)
.map(Account::getAuction)
.map(AccountAuctionConfig::getCache)
.map(AccountCacheConfig::getEnabled)
.orElse(true);

final ExtRequestTargeting targeting = targeting(bidRequest);
final ExtRequestPrebid prebid = PbsUtil.extRequestPrebid(bidRequest);
final ExtRequestPrebidCache cache = prebid != null ? prebid.getCache() : null;

if (targeting != null && cache != null) {
if (cachingEnabled && targeting != null && cache != null) {
final boolean shouldCacheBids = cache.getBids() != null;
final boolean shouldCacheVideoBids = cache.getVastxml() != null;
final boolean shouldCacheWinningBidsOnly = !targeting.getIncludebidderkeys()
Expand Down Expand Up @@ -345,6 +353,7 @@ private static BidRequestCacheInfo bidRequestCacheInfo(BidRequest bidRequest) {
.build();
}
}

return BidRequestCacheInfo.noCache();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,6 @@ public class AccountAuctionConfig {

@JsonProperty("paaformat")
PaaFormat paaFormat;

AccountCacheConfig cache;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.prebid.server.settings.model;

import lombok.Value;

@Value(staticConstructor = "of")
public class AccountCacheConfig {

Boolean enabled;
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class AccountAuctionConfig {
Boolean debugAllow
AccountBidValidationConfig bidValidations
AccountEventsConfig events
AccountCacheConfig cache
AccountPriceFloorsConfig priceFloors
Targeting targeting
PaaFormat paaformat
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.prebid.server.functional.model.config

import groovy.transform.ToString

@ToString(includeNames = true, ignoreNulls = true)
class AccountCacheConfig {

Boolean enabled
}
135 changes: 135 additions & 0 deletions src/test/groovy/org/prebid/server/functional/tests/CacheSpec.groovy
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.prebid.server.functional.tests

import org.prebid.server.functional.model.config.AccountAuctionConfig
import org.prebid.server.functional.model.config.AccountCacheConfig
import org.prebid.server.functional.model.config.AccountConfig
import org.prebid.server.functional.model.config.AccountEventsConfig
import org.prebid.server.functional.model.db.Account
Expand All @@ -14,6 +15,8 @@ import org.prebid.server.functional.model.response.auction.Adm
import org.prebid.server.functional.model.response.auction.BidResponse
import org.prebid.server.functional.util.PBSUtils

import static org.prebid.server.functional.model.AccountStatus.ACTIVE
import static org.prebid.server.functional.model.bidder.BidderName.GENERIC
import static org.prebid.server.functional.model.response.auction.MediaType.BANNER
import static org.prebid.server.functional.model.response.auction.MediaType.VIDEO

Expand Down Expand Up @@ -42,6 +45,9 @@ class CacheSpec extends BaseSpec {
def creative = encodeXml(Vast.getDefaultVastModel(PBSUtils.randomString))
def request = VtrackRequest.getDefaultVtrackRequest(creative)

and: "Flush metrics"
flushMetrics(defaultPbsService)

when: "PBS processes vtrack request"
defaultPbsService.sendVtrackRequest(request, accountId)

Expand Down Expand Up @@ -468,6 +474,9 @@ class CacheSpec extends BaseSpec {
"<${impression}> &lt;![CDATA[ ]]&gt; </${impression}><Creatives></Creatives></${wrapper}></Ad></VAST>"
def request = VtrackRequest.getDefaultVtrackRequest(creative)

and: "Flush metrics"
flushMetrics(defaultPbsService)

when: "PBS processes vtrack request"
defaultPbsService.sendVtrackRequest(request, accountId)

Expand All @@ -492,4 +501,130 @@ class CacheSpec extends BaseSpec {
PBSUtils.getRandomCase(" inline ") | " ${PBSUtils.getRandomCase(" impression ")} $PBSUtils.randomNumber "
" inline ${PBSUtils.getRandomString()} " | " ImpreSSion "
}

def "PBS should cache bids and add targeting values when account cache config #accountAuctionConfig"() {
given: "Current value of metric prebid_cache.requests.ok"
def initialValue = getCurrentMetricValue(defaultPbsService, CACHE_REQUEST_OK_GLOBAL_METRIC)

and: "Default BidRequest with cache, targeting"
def bidRequest = BidRequest.getDefaultVideoRequest().tap {
it.enableCache()
}

and: "Account in the DB"
def accountConfig = new AccountConfig(status: ACTIVE, auction: accountAuctionConfig)
def account = new Account(uuid: bidRequest.accountId, config: accountConfig)
accountDao.save(account)

and: "Default bid response"
def presetBidResponse = BidResponse.getDefaultBidResponse(bidRequest)
bidder.setResponse(bidRequest.id, presetBidResponse)

and: "Flush metrics"
flushMetrics(defaultPbsService)

when: "PBS processes auction request"
def response = defaultPbsService.sendAuctionRequest(bidRequest)

then: "PBS should call PBC"
assert prebidCache.getRequestCount(bidRequest.imp[0].id) == 1

and: "PBS response targeting contains bidder specific keys"
def targetingKeyMap = response.seatbid?.first()?.bid?.first()?.ext?.prebid?.targeting
assert targetingKeyMap.containsKey('hb_cache_id')
assert targetingKeyMap.containsKey("hb_cache_id_${GENERIC}".toString())
assert targetingKeyMap.containsKey('hb_uuid')
assert targetingKeyMap.containsKey("hb_uuid_${GENERIC}".toString())

and: "Metrics should be updated"
def metrics = defaultPbsService.sendCollectedMetricsRequest()
assert metrics[CACHE_REQUEST_OK_GLOBAL_METRIC] == initialValue + 1
assert metrics[CACHE_REQUEST_OK_ACCOUNT_METRIC.formatted(bidRequest.accountId)] == 1

where:
accountAuctionConfig << [
new AccountAuctionConfig(),
new AccountAuctionConfig(cache: new AccountCacheConfig()),
new AccountAuctionConfig(cache: new AccountCacheConfig(enabled: null)),
new AccountAuctionConfig(cache: new AccountCacheConfig(enabled: true))
]
}

def "PBS shouldn't cache bids and add targeting values when account cache config disabled"() {
given: "Current value of metric prebid_cache.requests.ok"
def initialValue = getCurrentMetricValue(defaultPbsService, CACHE_REQUEST_OK_GLOBAL_METRIC)

and: "Default BidRequest with cache, targeting"
def bidRequest = BidRequest.getDefaultVideoRequest().tap {
it.enableCache()
}

and: "Account with cache config"
def accountAuctionConfig = new AccountAuctionConfig(cache: new AccountCacheConfig(enabled: false))
def accountConfig = new AccountConfig(status: ACTIVE, auction: accountAuctionConfig)
def account = new Account(uuid: bidRequest.accountId, config: accountConfig)
accountDao.save(account)

and: "Default bid response"
def presetBidResponse = BidResponse.getDefaultBidResponse(bidRequest)
bidder.setResponse(bidRequest.id, presetBidResponse)

and: "Flush metrics"
flushMetrics(defaultPbsService)

when: "PBS processes auction request"
def response = defaultPbsService.sendAuctionRequest(bidRequest)

then: "PBS shouldn't call PBC"
assert !prebidCache.getRequestCount(bidRequest.imp[0].id)

and: "PBS response targeting shouldn't contains bidder specific keys"
def targetingKeyMap = response.seatbid?.first()?.bid?.first()?.ext?.prebid?.targeting
assert !targetingKeyMap.containsKey('hb_cache_id')
assert !targetingKeyMap.containsKey("hb_cache_id_${GENERIC}".toString())
assert !targetingKeyMap.containsKey('hb_uuid')
assert !targetingKeyMap.containsKey("hb_uuid_${GENERIC}".toString())

and: "Metrics shouldn't be updated"
def metrics = defaultPbsService.sendCollectedMetricsRequest()
assert metrics[CACHE_REQUEST_OK_GLOBAL_METRIC] == initialValue
assert !metrics[CACHE_REQUEST_OK_ACCOUNT_METRIC.formatted(bidRequest.accountId)]
}

def "PBS should update prebid_cache.creative_size.xml metric when account cache config #enabledCacheConcfig"() {
given: "Current value of metric prebid_cache.requests.ok"
def okInitialValue = getCurrentMetricValue(defaultPbsService, CACHE_REQUEST_OK_GLOBAL_METRIC)

and: "Default VtrackRequest"
def accountId = PBSUtils.randomNumber.toString()
def creative = encodeXml(Vast.getDefaultVastModel(PBSUtils.randomString))
def request = VtrackRequest.getDefaultVtrackRequest(creative)

and: "Create and save enabled events config in account"
def account = new Account().tap {
it.uuid = accountId
it.config = new AccountConfig().tap {
it.auction = new AccountAuctionConfig(cache: new AccountCacheConfig(enabled: enabledCacheConcfig))
}
}
accountDao.save(account)

and: "Flush metrics"
flushMetrics(defaultPbsService)

when: "PBS processes vtrack request"
defaultPbsService.sendVtrackRequest(request, accountId)

then: "prebid_cache.creative_size.xml metric should be updated"
def metrics = defaultPbsService.sendCollectedMetricsRequest()
def creativeSize = creative.bytes.length
assert metrics[CACHE_REQUEST_OK_GLOBAL_METRIC] == okInitialValue + 1

and: "account.<account-id>.prebid_cache.creative_size.xml should be updated"
assert metrics[CACHE_REQUEST_OK_ACCOUNT_METRIC.formatted(accountId)] == 1
assert metrics[XML_CREATIVE_SIZE_ACCOUNT_METRIC.formatted(accountId)] == creativeSize

where:
enabledCacheConcfig << [null, false, true]
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.prebid.server.functional.tests.module.responsecorrenction

import org.prebid.server.functional.model.config.AccountAuctionConfig
import org.prebid.server.functional.model.config.AccountCacheConfig
import org.prebid.server.functional.model.config.AccountConfig
import org.prebid.server.functional.model.config.AccountHooksConfiguration
import org.prebid.server.functional.model.config.AppVideoHtml
Expand Down Expand Up @@ -576,6 +578,61 @@ class ResponseCorrectionSpec extends ModuleBaseSpec {
assert !response.ext.warnings
}

def "PBS should modify response when requested video impression respond with invalid adm VAST keyword and disabled cache config"() {
given: "Start up time"
def start = Instant.now()

and: "Default bid request with APP and Video imp"
def bidRequest = getDefaultVideoRequest(APP)

and: "Set bidder response"
def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap {
seatbid[0].bid[0].setAdm(PBSUtils.getRandomCase(admValue))
}
bidder.setResponse(bidRequest.id, bidResponse)

and: "Save account with enabled response correction module"
def accountWithResponseCorrectionModule = accountConfigWithResponseCorrectionModule(bidRequest).tap {
config.auction = new AccountAuctionConfig(cache: new AccountCacheConfig(enabled: false))
}
accountDao.save(accountWithResponseCorrectionModule)

when: "PBS processes auction request"
def response = pbsServiceWithResponseCorrectionModule.sendAuctionRequest(bidRequest)

then: "PBS should emit log"
def logsByTime = pbsServiceWithResponseCorrectionModule.getLogsByTime(start)
def bidId = bidResponse.seatbid[0].bid[0].id
def responseCorrection = getLogsByText(logsByTime, bidId)
assert responseCorrection.size() == 1
assert responseCorrection.any {
it.contains("Bid $bidId of bidder generic: changing media type to banner" as String)
}

and: "Response should contain seatBid"
assert response.seatbid.size() == 1

and: "Response should contain single seatBid with proper media type"
assert response.seatbid.bid.ext.prebid.type.flatten() == [BANNER]

and: "Response should contain single seatBid with proper meta media type"
assert response.seatbid.bid.ext.prebid.meta.mediaType.flatten() == [VIDEO.value]

and: "Response shouldn't contain errors"
assert !response.ext.errors

and: "Response shouldn't contain warnings"
assert !response.ext.warnings

where:
admValue << [
"<${' ' * PBSUtils.getRandomNumber(0, OPTIMAL_MAX_LENGTH)}VAST${PBSUtils.randomString}",
"<${' ' * PBSUtils.getRandomNumber(0, OPTIMAL_MAX_LENGTH)}VAST",
"<${' ' * PBSUtils.getRandomNumber(0, OPTIMAL_MAX_LENGTH)}VAST>",
"<${PBSUtils.randomString}VAST${' ' * PBSUtils.getRandomNumber(1, OPTIMAL_MAX_LENGTH)}"
]
}

private static Account accountConfigWithResponseCorrectionModule(BidRequest bidRequest, Boolean enabledResponseCorrection = true, Boolean enabledAppVideoHtml = true) {
def modulesConfig = new PbsModulesConfig(pbResponseCorrection: new PbResponseCorrection(
enabled: enabledResponseCorrection, appVideoHtml: new AppVideoHtml(enabled: enabledAppVideoHtml)))
Expand Down
42 changes: 42 additions & 0 deletions src/test/java/org/prebid/server/auction/ExchangeServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@
import org.prebid.server.settings.model.AccountAlternateBidderCodesBidder;
import org.prebid.server.settings.model.AccountAnalyticsConfig;
import org.prebid.server.settings.model.AccountAuctionConfig;
import org.prebid.server.settings.model.AccountCacheConfig;
import org.prebid.server.settings.model.AccountEventsConfig;
import org.prebid.server.spring.config.bidder.model.CompressionType;
import org.prebid.server.spring.config.bidder.model.Ortb;
Expand Down Expand Up @@ -1635,6 +1636,47 @@ public void shouldCallBidResponseCreatorWithWinningOnlyTrueWhenIncludeBidderKeys
.containsOnly(true);
}

@Test
public void shouldCallBidResponseCreatorWithCachingDisabledWhenCachingIsNotEnabledOnAccountLevel() {
// given
givenBidder("bidder1", mock(Bidder.class), givenEmptySeatBid());

final Bid thirdBid = Bid.builder().id("bidId3").impid("impId3").price(BigDecimal.valueOf(7.89)).build();
givenBidder("bidder2", mock(Bidder.class), givenSeatBid(singletonList(givenBidderBid(thirdBid))));

final ExtRequestTargeting targeting = givenTargeting(false);

final BidRequest bidRequest = givenBidRequest(asList(
// imp ids are not really used for matching, included them here for clarity
givenImp(singletonMap("bidder1", 1), builder -> builder.id("impId1")),
givenImp(Map.of("bidder1", 1, "bidder2", 2), builder -> builder.id("impId2"))),
builder -> builder.ext(ExtRequest.of(ExtRequestPrebid.builder()
.targeting(targeting)
.cache(ExtRequestPrebidCache.of(ExtRequestPrebidCacheBids.of(53, true),
ExtRequestPrebidCacheVastxml.of(34, true), true))
.auctiontimestamp(1000L)
.build())));

// when
target.holdAuction(givenRequestContext(
bidRequest,
Account.builder()
.id("accountId")
.auction(AccountAuctionConfig.builder()
.events(AccountEventsConfig.of(true))
.cache(AccountCacheConfig.of(false))
.build())
.build()));

// then
final ArgumentCaptor<AuctionContext> auctionContextArgumentCaptor =
ArgumentCaptor.forClass(AuctionContext.class);
verify(bidResponseCreator).create(
auctionContextArgumentCaptor.capture(),
eq(BidRequestCacheInfo.noCache()),
eq(emptyMap()));
}

@Test
public void shouldCallBidResponseCreatorWithWinningOnlyFalseWhenWinningOnlyIsNull() {
// given
Expand Down
Loading