From 143b3224d55189a8682f0a79e0dc8dd14d293ec1 Mon Sep 17 00:00:00 2001 From: antonbabak Date: Mon, 12 May 2025 15:08:54 +0200 Subject: [PATCH 1/3] Add Account auction.cache.enabled Flag --- docs/application-settings.md | 1 + .../server/auction/ExchangeService.java | 15 +++++-- .../settings/model/AccountAuctionConfig.java | 2 + .../settings/model/AccountCacheConfig.java | 11 +++++ .../server/auction/ExchangeServiceTest.java | 42 +++++++++++++++++++ 5 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/prebid/server/settings/model/AccountCacheConfig.java diff --git a/docs/application-settings.md b/docs/application-settings.md index d99607a1477..9fa2bff4917 100644 --- a/docs/application-settings.md +++ b/docs/application-settings.md @@ -50,6 +50,7 @@ Keep in mind following restrictions: - `auction.preferredmediatype..` - that will be left for 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 diff --git a/src/main/java/org/prebid/server/auction/ExchangeService.java b/src/main/java/org/prebid/server/auction/ExchangeService.java index 4ae191bcaad..13419db10c3 100644 --- a/src/main/java/org/prebid/server/auction/ExchangeService.java +++ b/src/main/java/org/prebid/server/auction/ExchangeService.java @@ -93,6 +93,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; @@ -239,7 +241,7 @@ private Future runAuction(AuctionContext receivedContext) { final List storedAuctionResponses = new ArrayList<>(); final BidderAliases aliases = aliases(bidRequest, account); - final BidRequestCacheInfo cacheInfo = bidRequestCacheInfo(bidRequest); + final BidRequestCacheInfo cacheInfo = bidRequestCacheInfo(bidRequest, account); final Map bidderToMultiBid = bidderToMultiBids(bidRequest, debugWarnings); receivedContext.getBidRejectionTrackers().putAll(makeBidRejectionTrackers(bidRequest, aliases)); @@ -309,12 +311,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() @@ -343,6 +351,7 @@ private static BidRequestCacheInfo bidRequestCacheInfo(BidRequest bidRequest) { .build(); } } + return BidRequestCacheInfo.noCache(); } diff --git a/src/main/java/org/prebid/server/settings/model/AccountAuctionConfig.java b/src/main/java/org/prebid/server/settings/model/AccountAuctionConfig.java index ac7da04dd31..96e12b0e927 100644 --- a/src/main/java/org/prebid/server/settings/model/AccountAuctionConfig.java +++ b/src/main/java/org/prebid/server/settings/model/AccountAuctionConfig.java @@ -53,4 +53,6 @@ public class AccountAuctionConfig { @JsonProperty("paaformat") PaaFormat paaFormat; + + AccountCacheConfig cache; } diff --git a/src/main/java/org/prebid/server/settings/model/AccountCacheConfig.java b/src/main/java/org/prebid/server/settings/model/AccountCacheConfig.java new file mode 100644 index 00000000000..c34e3b1b333 --- /dev/null +++ b/src/main/java/org/prebid/server/settings/model/AccountCacheConfig.java @@ -0,0 +1,11 @@ +package org.prebid.server.settings.model; + +import lombok.Builder; +import lombok.Value; + +@Builder(toBuilder = true) +@Value +public class AccountCacheConfig { + + Boolean enabled; +} diff --git a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java index 25ee4657df9..fb28cd00ca0 100644 --- a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java +++ b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java @@ -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; @@ -1633,6 +1634,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.builder().enabled(false).build()) + .build()) + .build())); + + // then + final ArgumentCaptor auctionContextArgumentCaptor = + ArgumentCaptor.forClass(AuctionContext.class); + verify(bidResponseCreator).create( + auctionContextArgumentCaptor.capture(), + eq(BidRequestCacheInfo.noCache()), + eq(emptyMap())); + } + @Test public void shouldCallBidResponseCreatorWithWinningOnlyFalseWhenWinningOnlyIsNull() { // given From 15de7823ed2d49b5af491b33169e17e35fe0cd9f Mon Sep 17 00:00:00 2001 From: antonbabak Date: Tue, 27 May 2025 10:01:53 +0200 Subject: [PATCH 2/3] Fix comments --- src/main/java/org/prebid/server/auction/ExchangeService.java | 2 +- .../org/prebid/server/settings/model/AccountCacheConfig.java | 4 +--- .../java/org/prebid/server/auction/ExchangeServiceTest.java | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/prebid/server/auction/ExchangeService.java b/src/main/java/org/prebid/server/auction/ExchangeService.java index ab622b3e62a..1db72fcc649 100644 --- a/src/main/java/org/prebid/server/auction/ExchangeService.java +++ b/src/main/java/org/prebid/server/auction/ExchangeService.java @@ -314,7 +314,7 @@ private static ExtRequestTargeting targeting(BidRequest bidRequest) { } private static BidRequestCacheInfo bidRequestCacheInfo(BidRequest bidRequest, Account account) { - final Boolean cachingEnabled = Optional.ofNullable(account) + final boolean cachingEnabled = Optional.ofNullable(account) .map(Account::getAuction) .map(AccountAuctionConfig::getCache) .map(AccountCacheConfig::getEnabled) diff --git a/src/main/java/org/prebid/server/settings/model/AccountCacheConfig.java b/src/main/java/org/prebid/server/settings/model/AccountCacheConfig.java index c34e3b1b333..72f205865b8 100644 --- a/src/main/java/org/prebid/server/settings/model/AccountCacheConfig.java +++ b/src/main/java/org/prebid/server/settings/model/AccountCacheConfig.java @@ -1,10 +1,8 @@ package org.prebid.server.settings.model; -import lombok.Builder; import lombok.Value; -@Builder(toBuilder = true) -@Value +@Value(staticConstructor = "of") public class AccountCacheConfig { Boolean enabled; diff --git a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java index 2654052c12b..584825fa273 100644 --- a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java +++ b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java @@ -1664,7 +1664,7 @@ public void shouldCallBidResponseCreatorWithCachingDisabledWhenCachingIsNotEnabl .id("accountId") .auction(AccountAuctionConfig.builder() .events(AccountEventsConfig.of(true)) - .cache(AccountCacheConfig.builder().enabled(false).build()) + .cache(AccountCacheConfig.of(false)) .build()) .build())); From 1e6daa80a6007bd63b2b3aa9bb5abadb02ed373a Mon Sep 17 00:00:00 2001 From: osulzhenko <125548596+osulzhenko@users.noreply.github.com> Date: Tue, 3 Jun 2025 17:19:22 +0300 Subject: [PATCH 3/3] Tests: Account auction.cache.enabled Flag (#3973) --- .../model/config/AccountAuctionConfig.groovy | 1 + .../model/config/AccountCacheConfig.groovy | 9 ++ .../server/functional/tests/CacheSpec.groovy | 135 ++++++++++++++++++ .../ResponseCorrectionSpec.groovy | 57 ++++++++ 4 files changed, 202 insertions(+) create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/AccountCacheConfig.groovy diff --git a/src/test/groovy/org/prebid/server/functional/model/config/AccountAuctionConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/AccountAuctionConfig.groovy index 8dc9831e3fa..984ef6409b2 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/AccountAuctionConfig.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/AccountAuctionConfig.groovy @@ -22,6 +22,7 @@ class AccountAuctionConfig { Boolean debugAllow AccountBidValidationConfig bidValidations AccountEventsConfig events + AccountCacheConfig cache AccountPriceFloorsConfig priceFloors Targeting targeting PaaFormat paaformat diff --git a/src/test/groovy/org/prebid/server/functional/model/config/AccountCacheConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/AccountCacheConfig.groovy new file mode 100644 index 00000000000..121e32f03cd --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/AccountCacheConfig.groovy @@ -0,0 +1,9 @@ +package org.prebid.server.functional.model.config + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class AccountCacheConfig { + + Boolean enabled +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/CacheSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/CacheSpec.groovy index e145e5a972f..2aec80a7e0a 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/CacheSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/CacheSpec.groovy @@ -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 @@ -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 @@ -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) @@ -468,6 +474,9 @@ class CacheSpec extends BaseSpec { "<${impression}> <![CDATA[ ]]> " def request = VtrackRequest.getDefaultVtrackRequest(creative) + and: "Flush metrics" + flushMetrics(defaultPbsService) + when: "PBS processes vtrack request" defaultPbsService.sendVtrackRequest(request, accountId) @@ -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..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] + } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/responsecorrenction/ResponseCorrectionSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/responsecorrenction/ResponseCorrectionSpec.groovy index b4cbdd3fb88..2426a3fd316 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/responsecorrenction/ResponseCorrectionSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/responsecorrenction/ResponseCorrectionSpec.groovy @@ -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 @@ -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)))