diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000000..a86f0b144a5 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,48 @@ +name: "CodeQL" + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + language: [ 'java' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up JDK + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: 21 + + - name: Cache Maven packages + uses: actions/cache@v3 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + + - name: Build with Maven + run: mvn -B package --file extra/pom.xml + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 + with: + category: "/language:${{ matrix.language }}" diff --git a/extra/bundle/pom.xml b/extra/bundle/pom.xml index eb64da6bd91..5f17d237be8 100644 --- a/extra/bundle/pom.xml +++ b/extra/bundle/pom.xml @@ -5,7 +5,7 @@ org.prebid prebid-server-aggregator - 3.14.0-SNAPSHOT + 3.15.0-SNAPSHOT ../../extra/pom.xml diff --git a/extra/modules/confiant-ad-quality/pom.xml b/extra/modules/confiant-ad-quality/pom.xml index 263056107ba..a4b77048c76 100644 --- a/extra/modules/confiant-ad-quality/pom.xml +++ b/extra/modules/confiant-ad-quality/pom.xml @@ -5,7 +5,7 @@ org.prebid.server.hooks.modules all-modules - 3.14.0-SNAPSHOT + 3.15.0-SNAPSHOT confiant-ad-quality diff --git a/extra/modules/fiftyone-devicedetection/pom.xml b/extra/modules/fiftyone-devicedetection/pom.xml index 3d14e3e6b51..963b239763e 100644 --- a/extra/modules/fiftyone-devicedetection/pom.xml +++ b/extra/modules/fiftyone-devicedetection/pom.xml @@ -5,7 +5,7 @@ org.prebid.server.hooks.modules all-modules - 3.14.0-SNAPSHOT + 3.15.0-SNAPSHOT fiftyone-devicedetection diff --git a/extra/modules/ortb2-blocking/pom.xml b/extra/modules/ortb2-blocking/pom.xml index d997a0dc4bd..90fe75bac96 100644 --- a/extra/modules/ortb2-blocking/pom.xml +++ b/extra/modules/ortb2-blocking/pom.xml @@ -5,7 +5,7 @@ org.prebid.server.hooks.modules all-modules - 3.14.0-SNAPSHOT + 3.15.0-SNAPSHOT ortb2-blocking diff --git a/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/AccountConfigReader.java b/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/AccountConfigReader.java index 599be6e981c..a7ee0425135 100644 --- a/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/AccountConfigReader.java +++ b/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/AccountConfigReader.java @@ -18,6 +18,7 @@ import org.prebid.server.hooks.modules.ortb2.blocking.core.model.ResponseBlockingConfig; import org.prebid.server.hooks.modules.ortb2.blocking.core.model.Result; import org.prebid.server.hooks.modules.ortb2.blocking.core.util.MergeUtils; +import org.prebid.server.spring.config.bidder.model.MediaType; import org.prebid.server.util.ObjectUtil; import org.prebid.server.util.StreamUtil; @@ -51,7 +52,11 @@ public class AccountConfigReader { private static final String ALLOWED_APP_FOR_DEALS_FIELD = "allowed-app-for-deals"; private static final String BLOCKED_BANNER_TYPE_FIELD = "blocked-banner-type"; private static final String BLOCKED_BANNER_ATTR_FIELD = "blocked-banner-attr"; + private static final String BLOCKED_VIDEO_ATTR_FIELD = "blocked-video-attr"; + private static final String BLOCKED_AUDIO_ATTR_FIELD = "blocked-audio-attr"; private static final String ALLOWED_BANNER_ATTR_FOR_DEALS = "allowed-banner-attr-for-deals"; + private static final String ALLOWED_VIDEO_ATTR_FOR_DEALS = "allowed-video-attr-for-deals"; + private static final String ALLOWED_AUDIO_ATTR_FOR_DEALS = "allowed-audio-attr-for-deals"; private static final String ACTION_OVERRIDES_FIELD = "action-overrides"; private static final String OVERRIDE_FIELD = "override"; private static final String CONDITIONS_FIELD = "conditions"; @@ -100,8 +105,14 @@ public Result blockedAttributesFor(BidRequest bidRequest) { blockedAttribute(BAPP_FIELD, String.class, BLOCKED_APP_FIELD, requestMediaTypes); final Result>> btype = blockedAttributesForImps(BTYPE_FIELD, Integer.class, BLOCKED_BANNER_TYPE_FIELD, bidRequest); - final Result>> battr = + final Result>> bannerBattr = blockedAttributesForImps(BATTR_FIELD, Integer.class, BLOCKED_BANNER_ATTR_FIELD, bidRequest); + final Result>> videoBattr = + blockedAttributesForImps(BATTR_FIELD, Integer.class, BLOCKED_VIDEO_ATTR_FIELD, bidRequest); + final Result>> audioBattr = + blockedAttributesForImps(BATTR_FIELD, Integer.class, BLOCKED_AUDIO_ATTR_FIELD, bidRequest); + final Result>>> battr = + mergeBlockedAttributes(bannerBattr, videoBattr, audioBattr); return Result.of( toBlockedAttributes(badv, bcat, cattaxComplement, bapp, btype, battr), @@ -133,22 +144,39 @@ public Result responseBlockingConfigFor(BidderBid bidder ALLOWED_APP_FOR_DEALS_FIELD, bidMediaTypes, dealid); - final Result> battr = blockingConfigForAttribute( + final Result> bannerBattr = blockingConfigForAttribute( BATTR_FIELD, Integer.class, ALLOWED_BANNER_ATTR_FOR_DEALS, bidMediaTypes, dealid); + final Result> videoBattr = blockingConfigForAttribute( + BATTR_FIELD, + Integer.class, + ALLOWED_VIDEO_ATTR_FOR_DEALS, + bidMediaTypes, + dealid); + final Result> audioBattr = blockingConfigForAttribute( + BATTR_FIELD, + Integer.class, + ALLOWED_AUDIO_ATTR_FOR_DEALS, + bidMediaTypes, + dealid); + final Map> battr = new HashMap<>(); + battr.put(MediaType.BANNER, bannerBattr.getValue()); + battr.put(MediaType.VIDEO, videoBattr.getValue()); + battr.put(MediaType.AUDIO, audioBattr.getValue()); final ResponseBlockingConfig response = ResponseBlockingConfig.builder() .badv(badv.getValue()) .bcat(bcat.getValue()) .cattax(cattax.getValue()) .bapp(bapp.getValue()) - .battr(battr.getValue()) + .battr(battr) .build(); - final List warnings = MergeUtils.mergeMessages(badv, bcat, cattax, bapp, battr); + final List warnings = MergeUtils.mergeMessages( + badv, bcat, cattax, bapp, bannerBattr, videoBattr, audioBattr); return Result.of(response, warnings); } @@ -218,6 +246,28 @@ private Result>> blockedAttributesForImps(String attribu MergeUtils.mergeMessages(results)); } + private static Result>>> mergeBlockedAttributes( + Result>> bannerBattr, + Result>> videoBattr, + Result>> audioBattr) { + + final Map>> battr = new HashMap<>(); + + if (bannerBattr.hasValue()) { + battr.put(MediaType.BANNER, bannerBattr.getValue()); + } + if (videoBattr.hasValue()) { + battr.put(MediaType.VIDEO, videoBattr.getValue()); + } + if (audioBattr.hasValue()) { + battr.put(MediaType.AUDIO, audioBattr.getValue()); + } + + return Result.of( + !battr.isEmpty() ? battr : null, + MergeUtils.mergeMessages(bannerBattr, videoBattr, audioBattr)); + } + private Result> blockingConfigForAttribute(String attribute, Class attributeType, String blockUnknownField, @@ -360,8 +410,8 @@ private Result toResult(List specificBidderResults, Set actualMediaTypes) { final JsonNode value = ObjectUtils.firstNonNull( - specificBidderResults.size() > 0 ? specificBidderResults.get(0) : null, - catchAllBidderResults.size() > 0 ? catchAllBidderResults.get(0) : null); + !specificBidderResults.isEmpty() ? specificBidderResults.getFirst() : null, + !catchAllBidderResults.isEmpty() ? catchAllBidderResults.getFirst() : null); final List warnings = debugEnabled && specificBidderResults.size() + catchAllBidderResults.size() > 1 ? Collections.singletonList( "More than one conditions matches request. Bidder: %s, request media types: %s" @@ -376,7 +426,7 @@ private static BlockedAttributes toBlockedAttributes(Result> badv, Result cattaxComplement, Result> bapp, Result>> btype, - Result>> battr) { + Result>>> battr) { return badv.hasValue() || bcat.hasValue() diff --git a/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/BidsBlocker.java b/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/BidsBlocker.java index 6e86d4fce0b..2c9b40b6079 100644 --- a/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/BidsBlocker.java +++ b/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/BidsBlocker.java @@ -18,6 +18,8 @@ import org.prebid.server.hooks.modules.ortb2.blocking.core.model.ResponseBlockingConfig; import org.prebid.server.hooks.modules.ortb2.blocking.core.model.Result; import org.prebid.server.hooks.modules.ortb2.blocking.core.util.MergeUtils; +import org.prebid.server.proto.openrtb.ext.response.BidType; +import org.prebid.server.spring.config.bidder.model.MediaType; import java.util.ArrayList; import java.util.Collections; @@ -91,7 +93,6 @@ public ExecutionResult block() { try { final List> blockedBidResults = bids.stream() - .sequential() .map(bid -> isBlocked(bid, accountConfigReader)) .toList(); @@ -170,11 +171,30 @@ private AttributeCheckResult checkBapp(BidderBid bidderBid, ResponseBloc } private AttributeCheckResult checkBattr(BidderBid bidderBid, ResponseBlockingConfig blockingConfig) { - + final MediaType mediaType = mapBidTypeToMediaType(bidderBid.getType()); return checkAttribute( bidderBid.getBid().getAttr(), - blockingConfig.getBattr(), - blockedAttributeValues(BlockedAttributes::getBattr, bidderBid.getBid().getImpid())); + blockingConfig.getBattr().get(mediaType), + blockedAttributeValues( + blockedAttributes -> extractBattrForMediaType(blockedAttributes, mediaType), + bidderBid.getBid().getImpid())); + } + + private static MediaType mapBidTypeToMediaType(BidType bidType) { + return switch (bidType) { + case banner -> MediaType.BANNER; + case video -> MediaType.VIDEO; + case audio -> MediaType.AUDIO; + case xNative -> MediaType.NATIVE; + case null -> null; + }; + } + + private static Map> extractBattrForMediaType(BlockedAttributes blockedAttributes, + MediaType mediaType) { + + final Map>> battr = blockedAttributes.getBattr(); + return battr != null ? battr.get(mediaType) : null; } private AttributeCheckResult checkAttribute(List attribute, diff --git a/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/RequestUpdater.java b/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/RequestUpdater.java index f83d8554a2c..ac963b94857 100644 --- a/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/RequestUpdater.java +++ b/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/RequestUpdater.java @@ -1,15 +1,19 @@ package org.prebid.server.hooks.modules.ortb2.blocking.core; +import com.iab.openrtb.request.Audio; import com.iab.openrtb.request.Banner; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Imp; +import com.iab.openrtb.request.Video; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.MapUtils; import org.prebid.server.hooks.modules.ortb2.blocking.core.model.BlockedAttributes; +import org.prebid.server.spring.config.bidder.model.MediaType; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; public class RequestUpdater { @@ -40,39 +44,93 @@ public BidRequest update(BidRequest bidRequest) { private List updateImps(List imps) { final Map> blockedBannerType = blockedAttributes.getBtype(); - final Map> blockedBannerAttr = blockedAttributes.getBattr(); + final Map>> blockedAttr = blockedAttributes.getBattr(); - if (MapUtils.isEmpty(blockedBannerType) && MapUtils.isEmpty(blockedBannerAttr)) { + if (MapUtils.isEmpty(blockedBannerType) && MapUtils.isEmpty(blockedAttr)) { return imps; } return imps.stream() - .map(imp -> updateImp(imp, blockedBannerType, blockedBannerAttr)) + .map(imp -> updateImp(imp, blockedBannerType, blockedAttr)) .toList(); } private Imp updateImp(Imp imp, Map> blockedBannerType, - Map> blockedBannerAttr) { + Map>> blockedAttr) { final String impId = imp.getId(); final List btypeForImp = blockedBannerType != null ? blockedBannerType.get(impId) : null; - final List battrForImp = blockedBannerAttr != null ? blockedBannerAttr.get(impId) : null; + final List bannerBattrForImp = extractBattr(blockedAttr, MediaType.BANNER, impId); + final List videoBattrForImp = extractBattr(blockedAttr, MediaType.VIDEO, impId); + final List audioBattrForImp = extractBattr(blockedAttr, MediaType.AUDIO, impId); + + if (CollectionUtils.isEmpty(btypeForImp) + && CollectionUtils.isEmpty(bannerBattrForImp) + && CollectionUtils.isEmpty(videoBattrForImp) + && CollectionUtils.isEmpty(audioBattrForImp)) { - if (CollectionUtils.isEmpty(btypeForImp) && CollectionUtils.isEmpty(battrForImp)) { return imp; } final Banner banner = imp.getBanner(); - final List existingBtype = banner != null ? banner.getBtype() : null; - final List existingBattr = banner != null ? banner.getBattr() : null; - final Banner.BannerBuilder bannerBuilder = banner != null ? banner.toBuilder() : Banner.builder(); + final Video video = imp.getVideo(); + final Audio audio = imp.getAudio(); return imp.toBuilder() - .banner(bannerBuilder - .btype(CollectionUtils.isNotEmpty(existingBtype) ? existingBtype : btypeForImp) - .battr(CollectionUtils.isNotEmpty(existingBattr) ? existingBattr : battrForImp) - .build()) + .banner(CollectionUtils.isNotEmpty(btypeForImp) || CollectionUtils.isNotEmpty(bannerBattrForImp) + ? updateBanner(banner, btypeForImp, bannerBattrForImp) + : banner) + .video(CollectionUtils.isNotEmpty(videoBattrForImp) + ? updateVideo(imp.getVideo(), videoBattrForImp) + : video) + .audio(CollectionUtils.isNotEmpty(audioBattrForImp) + ? updateAudio(imp.getAudio(), audioBattrForImp) + : audio) .build(); } + + private static List extractBattr(Map>> blockedAttr, + MediaType mediaType, + String impId) { + + final Map> impIdToBattr = blockedAttr != null ? blockedAttr.get(mediaType) : null; + return impIdToBattr != null ? impIdToBattr.get(impId) : null; + } + + private static Banner updateBanner(Banner banner, List btype, List battr) { + final List existingBtype = banner != null ? banner.getBtype() : null; + final List existingBattr = banner != null ? banner.getBattr() : null; + + return CollectionUtils.isEmpty(existingBtype) || CollectionUtils.isEmpty(existingBattr) + ? Optional.ofNullable(banner) + .map(Banner::toBuilder) + .orElseGet(Banner::builder) + .btype(CollectionUtils.isNotEmpty(existingBtype) ? existingBtype : btype) + .battr(CollectionUtils.isNotEmpty(existingBattr) ? existingBattr : battr) + .build() + : banner; + } + + private static Video updateVideo(Video video, List battr) { + final List existingBattr = video != null ? video.getBattr() : null; + return CollectionUtils.isEmpty(existingBattr) + ? Optional.ofNullable(video) + .map(Video::toBuilder) + .orElseGet(Video::builder) + .battr(battr) + .build() + : video; + } + + private static Audio updateAudio(Audio audio, List battr) { + final List existingBattr = audio != null ? audio.getBattr() : null; + return CollectionUtils.isEmpty(existingBattr) + ? Optional.ofNullable(audio) + .map(Audio::toBuilder) + .orElseGet(Audio::builder) + .battr(battr) + .build() + : audio; + } } diff --git a/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/model/BlockedAttributes.java b/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/model/BlockedAttributes.java index d3d3049b57c..aad04ba8db6 100644 --- a/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/model/BlockedAttributes.java +++ b/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/model/BlockedAttributes.java @@ -2,6 +2,7 @@ import lombok.Builder; import lombok.Value; +import org.prebid.server.spring.config.bidder.model.MediaType; import java.util.List; import java.util.Map; @@ -20,5 +21,5 @@ public class BlockedAttributes { Map> btype; - Map> battr; + Map>> battr; } diff --git a/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/model/ResponseBlockingConfig.java b/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/model/ResponseBlockingConfig.java index c2108eb8a8f..8c34561079e 100644 --- a/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/model/ResponseBlockingConfig.java +++ b/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/model/ResponseBlockingConfig.java @@ -2,6 +2,9 @@ import lombok.Builder; import lombok.Value; +import org.prebid.server.spring.config.bidder.model.MediaType; + +import java.util.Map; @Builder @Value @@ -15,5 +18,5 @@ public class ResponseBlockingConfig { BidAttributeBlockingConfig bapp; - BidAttributeBlockingConfig battr; + Map> battr; } diff --git a/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/core/AccountConfigReaderTest.java b/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/core/AccountConfigReaderTest.java index 456ac49939a..f6568663807 100644 --- a/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/core/AccountConfigReaderTest.java +++ b/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/core/AccountConfigReaderTest.java @@ -30,6 +30,7 @@ import org.prebid.server.hooks.modules.ortb2.blocking.core.model.ResponseBlockingConfig; import org.prebid.server.hooks.modules.ortb2.blocking.core.model.Result; import org.prebid.server.proto.openrtb.ext.response.BidType; +import org.prebid.server.spring.config.bidder.model.MediaType; import java.util.HashMap; import java.util.HashSet; @@ -734,7 +735,7 @@ public void blockedAttributesForShouldReturnResultWithBtypeAndWarningsFromOverri } @Test - public void blockedAttributesForShouldReturnResultWithAllAttributes() { + public void blockedAttributesForShouldReturnResultWithAllAttributesForBanner() { // given final ObjectNode accountConfig = toObjectNode(ModuleConfig.of(Attributes.builder() .badv(Attribute.badvBuilder() @@ -766,7 +767,7 @@ public void blockedAttributesForShouldReturnResultWithAllAttributes() { Conditions.of(singletonList("bidder1"), null), singletonList(3))))) .build()) - .battr(Attribute.battrBuilder() + .battr(Attribute.bannerBattrBuilder() .blocked(asList(1, 2)) .actionOverrides(AttributeActionOverrides.blocked(singletonList( ArrayOverride.of( @@ -783,7 +784,115 @@ public void blockedAttributesForShouldReturnResultWithAllAttributes() { .bcat(singletonList("cat3")) .bapp(singletonList("app3")) .btype(singletonMap("impId1", singletonList(3))) - .battr(singletonMap("impId1", singletonList(3))) + .battr(singletonMap(MediaType.BANNER, singletonMap("impId1", singletonList(3)))) + .build())); + } + + @Test + public void blockedAttributesForShouldReturnResultWithAllAttributesForVideo() { + // given + final ObjectNode accountConfig = toObjectNode(ModuleConfig.of(Attributes.builder() + .badv(Attribute.badvBuilder() + .blocked(asList("domain1.com", "domain2.com")) + .actionOverrides(AttributeActionOverrides.blocked( + singletonList( + ArrayOverride.of( + Conditions.of(singletonList("bidder1"), null), + singletonList("domain3.com"))))) + .build()) + .bcat(Attribute.bcatBuilder() + .blocked(asList("cat1", "cat2")) + .actionOverrides(AttributeActionOverrides.blocked(singletonList( + ArrayOverride.of( + Conditions.of(singletonList("bidder1"), null), + singletonList("cat3"))))) + .build()) + .bapp(Attribute.bappBuilder() + .blocked(asList("app1", "app2")) + .actionOverrides(AttributeActionOverrides.blocked(singletonList( + ArrayOverride.of( + Conditions.of(singletonList("bidder1"), null), + singletonList("app3"))))) + .build()) + .btype(Attribute.btypeBuilder() + .blocked(asList(1, 2)) + .actionOverrides(AttributeActionOverrides.blocked(singletonList( + ArrayOverride.of( + Conditions.of(singletonList("bidder1"), null), + singletonList(3))))) + .build()) + .battr(Attribute.videoBattrBuilder() + .blocked(asList(1, 2)) + .actionOverrides(AttributeActionOverrides.blocked(singletonList( + ArrayOverride.of( + Conditions.of(singletonList("bidder1"), null), + singletonList(3))))) + .build()) + .build())); + final AccountConfigReader reader = AccountConfigReader.create(accountConfig, "bidder1", ORTB_VERSION, true); + + // when and then + assertThat(reader.blockedAttributesFor(request(imp -> imp.id("impId1")))).isEqualTo( + Result.withValue(BlockedAttributes.builder() + .badv(singletonList("domain3.com")) + .bcat(singletonList("cat3")) + .bapp(singletonList("app3")) + .btype(singletonMap("impId1", singletonList(3))) + .battr(singletonMap(MediaType.VIDEO, singletonMap("impId1", singletonList(3)))) + .build())); + } + + @Test + public void blockedAttributesForShouldReturnResultWithAllAttributesForAudio() { + // given + final ObjectNode accountConfig = toObjectNode(ModuleConfig.of(Attributes.builder() + .badv(Attribute.badvBuilder() + .blocked(asList("domain1.com", "domain2.com")) + .actionOverrides(AttributeActionOverrides.blocked( + singletonList( + ArrayOverride.of( + Conditions.of(singletonList("bidder1"), null), + singletonList("domain3.com"))))) + .build()) + .bcat(Attribute.bcatBuilder() + .blocked(asList("cat1", "cat2")) + .actionOverrides(AttributeActionOverrides.blocked(singletonList( + ArrayOverride.of( + Conditions.of(singletonList("bidder1"), null), + singletonList("cat3"))))) + .build()) + .bapp(Attribute.bappBuilder() + .blocked(asList("app1", "app2")) + .actionOverrides(AttributeActionOverrides.blocked(singletonList( + ArrayOverride.of( + Conditions.of(singletonList("bidder1"), null), + singletonList("app3"))))) + .build()) + .btype(Attribute.btypeBuilder() + .blocked(asList(1, 2)) + .actionOverrides(AttributeActionOverrides.blocked(singletonList( + ArrayOverride.of( + Conditions.of(singletonList("bidder1"), null), + singletonList(3))))) + .build()) + .battr(Attribute.audioBattrBuilder() + .blocked(asList(1, 2)) + .actionOverrides(AttributeActionOverrides.blocked(singletonList( + ArrayOverride.of( + Conditions.of(singletonList("bidder1"), null), + singletonList(3))))) + .build()) + .build())); + final AccountConfigReader reader = AccountConfigReader.create(accountConfig, "bidder1", ORTB_VERSION, true); + + // when and then + assertThat(reader.blockedAttributesFor(request(imp -> imp.id("impId1")))).isEqualTo( + Result.withValue(BlockedAttributes.builder() + .badv(singletonList("domain3.com")) + .bcat(singletonList("cat3")) + .bapp(singletonList("app3")) + .btype(singletonMap("impId1", singletonList(3))) + .battr(singletonMap(MediaType.AUDIO, singletonMap("impId1", singletonList(3)))) .build())); } @@ -1143,7 +1252,163 @@ public void responseBlockingConfigForShouldReturnResultWithMergedDealExceptionsW } @Test - public void responseBlockingConfigForShouldReturnAllAttributes() { + public void responseBlockingConfigForShouldReturnAllAttributesForBanner() { + // given + final ObjectNode accountConfig = toObjectNode(ModuleConfig.of(Attributes.builder() + .badv(Attribute.badvBuilder() + .enforceBlocks(true) + .blockUnknown(true) + .allowedForDeals(asList("domain1.com", "domain2.com")) + .actionOverrides(AttributeActionOverrides.response( + singletonList(BooleanOverride.of( + Conditions.of(singletonList("bidder1"), null), + false)), + singletonList(BooleanOverride.of( + Conditions.of(singletonList("bidder1"), null), + false)), + singletonList(AllowedForDealsOverride.of( + DealsConditions.of(singletonList("dealid1")), + singletonList("domain3.com"))))) + .build()) + .bcat(Attribute.bcatBuilder() + .enforceBlocks(true) + .blockUnknown(true) + .allowedForDeals(asList("cat1", "cat2")) + .actionOverrides(AttributeActionOverrides.response( + singletonList(BooleanOverride.of( + Conditions.of(singletonList("bidder1"), null), + false)), + singletonList(BooleanOverride.of( + Conditions.of(singletonList("bidder1"), null), + false)), + singletonList(AllowedForDealsOverride.of( + DealsConditions.of(singletonList("dealid1")), + singletonList("cat3"))))) + .build()) + .bapp(Attribute.bappBuilder() + .enforceBlocks(true) + .allowedForDeals(asList("app1", "app2")) + .actionOverrides(AttributeActionOverrides.response( + singletonList(BooleanOverride.of( + Conditions.of(singletonList("bidder1"), null), + false)), + null, + singletonList(AllowedForDealsOverride.of( + DealsConditions.of(singletonList("dealid1")), + singletonList("app3"))))) + .build()) + .battr(Attribute.bannerBattrBuilder() + .enforceBlocks(true) + .allowedForDeals(asList(1, 2)) + .actionOverrides(AttributeActionOverrides.response( + singletonList(BooleanOverride.of( + Conditions.of(singletonList("bidder1"), null), + false)), + null, + singletonList(AllowedForDealsOverride.of( + DealsConditions.of(singletonList("dealid1")), + singletonList(3))))) + .build()) + .build())); + final AccountConfigReader reader = AccountConfigReader.create(accountConfig, "bidder1", ORTB_VERSION, true); + + // when and then + assertThat(reader.responseBlockingConfigFor(bid())).satisfies(result -> { + assertThat(result.getValue()).isEqualTo(ResponseBlockingConfig.builder() + .badv(BidAttributeBlockingConfig.of( + false, false, Set.of("domain1.com", "domain2.com", "domain3.com"))) + .bcat(BidAttributeBlockingConfig.of(false, false, Set.of("cat1", "cat2", "cat3"))) + .cattax(BidAttributeBlockingConfig.of(false, true, emptySet())) + .bapp(BidAttributeBlockingConfig.of(false, false, Set.of("app1", "app2", "app3"))) + .battr(Map.of( + MediaType.BANNER, BidAttributeBlockingConfig.of(false, false, Set.of(1, 2, 3)), + MediaType.VIDEO, BidAttributeBlockingConfig.of(false, false, emptySet()), + MediaType.AUDIO, BidAttributeBlockingConfig.of(false, false, emptySet()))) + .build()); + assertThat(result.getMessages()).isNull(); + }); + } + + @Test + public void responseBlockingConfigForShouldReturnAllAttributesForVideo() { + // given + final ObjectNode accountConfig = toObjectNode(ModuleConfig.of(Attributes.builder() + .badv(Attribute.badvBuilder() + .enforceBlocks(true) + .blockUnknown(true) + .allowedForDeals(asList("domain1.com", "domain2.com")) + .actionOverrides(AttributeActionOverrides.response( + singletonList(BooleanOverride.of( + Conditions.of(singletonList("bidder1"), null), + false)), + singletonList(BooleanOverride.of( + Conditions.of(singletonList("bidder1"), null), + false)), + singletonList(AllowedForDealsOverride.of( + DealsConditions.of(singletonList("dealid1")), + singletonList("domain3.com"))))) + .build()) + .bcat(Attribute.bcatBuilder() + .enforceBlocks(true) + .blockUnknown(true) + .allowedForDeals(asList("cat1", "cat2")) + .actionOverrides(AttributeActionOverrides.response( + singletonList(BooleanOverride.of( + Conditions.of(singletonList("bidder1"), null), + false)), + singletonList(BooleanOverride.of( + Conditions.of(singletonList("bidder1"), null), + false)), + singletonList(AllowedForDealsOverride.of( + DealsConditions.of(singletonList("dealid1")), + singletonList("cat3"))))) + .build()) + .bapp(Attribute.bappBuilder() + .enforceBlocks(true) + .allowedForDeals(asList("app1", "app2")) + .actionOverrides(AttributeActionOverrides.response( + singletonList(BooleanOverride.of( + Conditions.of(singletonList("bidder1"), null), + false)), + null, + singletonList(AllowedForDealsOverride.of( + DealsConditions.of(singletonList("dealid1")), + singletonList("app3"))))) + .build()) + .battr(Attribute.videoBattrBuilder() + .enforceBlocks(true) + .allowedForDeals(asList(1, 2)) + .actionOverrides(AttributeActionOverrides.response( + singletonList(BooleanOverride.of( + Conditions.of(singletonList("bidder1"), null), + false)), + null, + singletonList(AllowedForDealsOverride.of( + DealsConditions.of(singletonList("dealid1")), + singletonList(3))))) + .build()) + .build())); + final AccountConfigReader reader = AccountConfigReader.create(accountConfig, "bidder1", ORTB_VERSION, true); + + // when and then + assertThat(reader.responseBlockingConfigFor(bid())).satisfies(result -> { + assertThat(result.getValue()).isEqualTo(ResponseBlockingConfig.builder() + .badv(BidAttributeBlockingConfig.of( + false, false, Set.of("domain1.com", "domain2.com", "domain3.com"))) + .bcat(BidAttributeBlockingConfig.of(false, false, Set.of("cat1", "cat2", "cat3"))) + .cattax(BidAttributeBlockingConfig.of(false, true, emptySet())) + .bapp(BidAttributeBlockingConfig.of(false, false, Set.of("app1", "app2", "app3"))) + .battr(Map.of( + MediaType.BANNER, BidAttributeBlockingConfig.of(false, false, emptySet()), + MediaType.VIDEO, BidAttributeBlockingConfig.of(false, false, Set.of(1, 2, 3)), + MediaType.AUDIO, BidAttributeBlockingConfig.of(false, false, emptySet()))) + .build()); + assertThat(result.getMessages()).isNull(); + }); + } + + @Test + public void responseBlockingConfigForShouldReturnAllAttributesForAudio() { // given final ObjectNode accountConfig = toObjectNode(ModuleConfig.of(Attributes.builder() .badv(Attribute.badvBuilder() @@ -1188,7 +1453,7 @@ public void responseBlockingConfigForShouldReturnAllAttributes() { DealsConditions.of(singletonList("dealid1")), singletonList("app3"))))) .build()) - .battr(Attribute.battrBuilder() + .battr(Attribute.audioBattrBuilder() .enforceBlocks(true) .allowedForDeals(asList(1, 2)) .actionOverrides(AttributeActionOverrides.response( @@ -1211,7 +1476,10 @@ public void responseBlockingConfigForShouldReturnAllAttributes() { .bcat(BidAttributeBlockingConfig.of(false, false, Set.of("cat1", "cat2", "cat3"))) .cattax(BidAttributeBlockingConfig.of(false, true, emptySet())) .bapp(BidAttributeBlockingConfig.of(false, false, Set.of("app1", "app2", "app3"))) - .battr(BidAttributeBlockingConfig.of(false, false, Set.of(1, 2, 3))) + .battr(Map.of( + MediaType.BANNER, BidAttributeBlockingConfig.of(false, false, emptySet()), + MediaType.VIDEO, BidAttributeBlockingConfig.of(false, false, emptySet()), + MediaType.AUDIO, BidAttributeBlockingConfig.of(false, false, Set.of(1, 2, 3)))) .build()); assertThat(result.getMessages()).isNull(); }); @@ -1240,10 +1508,15 @@ public void responseBlockingConfigForShouldReturnCattaxConfigDependsOnBcatConfig final AccountConfigReader reader = AccountConfigReader.create(accountConfig, "bidder1", ORTB_VERSION, true); // when and then + final Map> expectedBattr = new HashMap<>(); + expectedBattr.put(MediaType.BANNER, null); + expectedBattr.put(MediaType.VIDEO, null); + expectedBattr.put(MediaType.AUDIO, null); assertThat(reader.responseBlockingConfigFor(bid())).satisfies(result -> { assertThat(result.getValue()).isEqualTo(ResponseBlockingConfig.builder() .bcat(BidAttributeBlockingConfig.of(true, false, Set.of("cat1", "cat2", "cat3"))) .cattax(BidAttributeBlockingConfig.of(true, true, emptySet())) + .battr(expectedBattr) .build()); assertThat(result.getMessages()).isNull(); }); diff --git a/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/core/BidsBlockerTest.java b/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/core/BidsBlockerTest.java index a995b43c90a..79b1309a2ba 100644 --- a/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/core/BidsBlockerTest.java +++ b/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/core/BidsBlockerTest.java @@ -21,6 +21,7 @@ import org.prebid.server.hooks.modules.ortb2.blocking.core.model.BlockedBids; import org.prebid.server.hooks.modules.ortb2.blocking.core.model.ExecutionResult; import org.prebid.server.proto.openrtb.ext.response.BidType; +import org.prebid.server.spring.config.bidder.model.MediaType; import java.util.HashMap; import java.util.List; @@ -227,7 +228,7 @@ public void shouldReturnEmptyResultWhenBidWithAdomainAndNoBlockedAttributes() { public void shouldReturnEmptyResultWhenBidWithAttrAndNoBlockedBannerAttrForImp() { // given final ObjectNode accountConfig = toObjectNode(ModuleConfig.of(Attributes.builder() - .battr(Attribute.battrBuilder() + .battr(Attribute.bannerBattrBuilder() .enforceBlocks(true) .build()) .build())); @@ -237,7 +238,53 @@ public void shouldReturnEmptyResultWhenBidWithAttrAndNoBlockedBannerAttrForImp() .impid("impId2") .attr(singletonList(1)))); final BlockedAttributes blockedAttributes = BlockedAttributes.builder() - .battr(singletonMap("impId1", asList(1, 2))) + .battr(singletonMap(MediaType.BANNER, singletonMap("impId1", asList(1, 2)))) + .build(); + final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, bidRejectionTracker, true); + + // when and then + assertThat(blocker.block()).satisfies(BidsBlockerTest::isEmpty); + verifyNoInteractions(bidRejectionTracker); + } + + @Test + public void shouldReturnEmptyResultWhenBidWithAttrAndNoBlockedVideoAttrForImp() { + // given + final ObjectNode accountConfig = toObjectNode(ModuleConfig.of(Attributes.builder() + .battr(Attribute.videoBattrBuilder() + .enforceBlocks(true) + .build()) + .build())); + + // when + final List bids = singletonList(bid(bid -> bid + .impid("impId2") + .attr(singletonList(1)))); + final BlockedAttributes blockedAttributes = BlockedAttributes.builder() + .battr(singletonMap(MediaType.VIDEO, singletonMap("impId1", asList(1, 2)))) + .build(); + final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, bidRejectionTracker, true); + + // when and then + assertThat(blocker.block()).satisfies(BidsBlockerTest::isEmpty); + verifyNoInteractions(bidRejectionTracker); + } + + @Test + public void shouldReturnEmptyResultWhenBidWithAttrAndNoBlockedAudioAttrForImp() { + // given + final ObjectNode accountConfig = toObjectNode(ModuleConfig.of(Attributes.builder() + .battr(Attribute.audioBattrBuilder() + .enforceBlocks(true) + .build()) + .build())); + + // when + final List bids = singletonList(bid(bid -> bid + .impid("impId2") + .attr(singletonList(1)))); + final BlockedAttributes blockedAttributes = BlockedAttributes.builder() + .battr(singletonMap(MediaType.AUDIO, singletonMap("impId1", asList(1, 2)))) .build(); final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, bidRejectionTracker, true); @@ -341,7 +388,7 @@ public void shouldReturnResultWithAnalyticsResults() { .bapp(Attribute.bappBuilder() .enforceBlocks(true) .build()) - .battr(Attribute.battrBuilder() + .battr(Attribute.bannerBattrBuilder() .enforceBlocks(true) .build()) .build())); @@ -366,7 +413,7 @@ public void shouldReturnResultWithAnalyticsResults() { .badv(asList("domain1.com", "domain2.com", "domain3.com")) .bcat(asList("cat1", "cat2", "cat3")) .bapp(asList("app1", "app2", "app3")) - .battr(singletonMap("impId2", asList(1, 2, 3))) + .battr(singletonMap(MediaType.BANNER, singletonMap("impId2", asList(1, 2, 3)))) .build(); final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, bidRejectionTracker, true); @@ -413,7 +460,7 @@ public void shouldReturnResultWithoutSomeBidsWhenAllAttributesInConfig() { .enforceBlocks(true) .allowedForDeals(singletonList("app2")) .build()) - .battr(Attribute.battrBuilder() + .battr(Attribute.bannerBattrBuilder() .enforceBlocks(true) .allowedForDeals(singletonList(2)) .build()) @@ -441,7 +488,7 @@ public void shouldReturnResultWithoutSomeBidsWhenAllAttributesInConfig() { .badv(asList("domain1.com", "domain2.com")) .bcat(asList("cat1", "cat2")) .bapp(asList("app1", "app2")) - .battr(singletonMap("impId1", asList(1, 2))) + .battr(singletonMap(MediaType.BANNER, singletonMap("impId1", asList(1, 2)))) .build(); final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, bidRejectionTracker, true); diff --git a/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/core/RequestUpdaterTest.java b/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/core/RequestUpdaterTest.java index 39b5807ab12..630c09e96d1 100644 --- a/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/core/RequestUpdaterTest.java +++ b/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/core/RequestUpdaterTest.java @@ -1,12 +1,16 @@ package org.prebid.server.hooks.modules.ortb2.blocking.core; +import com.iab.openrtb.request.Audio; import com.iab.openrtb.request.Banner; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Imp; +import com.iab.openrtb.request.Video; import org.junit.jupiter.api.Test; import org.prebid.server.hooks.modules.ortb2.blocking.core.model.BlockedAttributes; +import org.prebid.server.spring.config.bidder.model.MediaType; import java.util.List; +import java.util.Map; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; @@ -138,10 +142,12 @@ public void shouldReplaceImpBtypeWhenAbsent() { } @Test - public void shouldReplaceImpBattrWhenAbsent() { + public void shouldReplaceImpBannerBattrWhenAbsent() { // given final RequestUpdater updater = RequestUpdater.create( - BlockedAttributes.builder().battr(singletonMap("impId1", asList(1, 2))).build()); + BlockedAttributes.builder() + .battr(singletonMap(MediaType.BANNER, singletonMap("impId1", asList(1, 2)))) + .build()); final BidRequest request = BidRequest.builder() .imp(singletonList(Imp.builder() .id("impId1") @@ -160,6 +166,56 @@ public void shouldReplaceImpBattrWhenAbsent() { .build()); } + @Test + public void shouldReplaceImpVideoBattrWhenAbsent() { + // given + final RequestUpdater updater = RequestUpdater.create( + BlockedAttributes.builder() + .battr(singletonMap(MediaType.VIDEO, singletonMap("impId1", asList(1, 2)))) + .build()); + final BidRequest request = BidRequest.builder() + .imp(singletonList(Imp.builder() + .id("impId1") + .video(Video.builder().build()) + .build())) + .build(); + + // when and then + assertThat(updater.update(request)).isEqualTo(BidRequest.builder() + .imp(singletonList(Imp.builder() + .id("impId1") + .video(Video.builder() + .battr(asList(1, 2)) + .build()) + .build())) + .build()); + } + + @Test + public void shouldReplaceImpAudioBattrWhenAbsent() { + // given + final RequestUpdater updater = RequestUpdater.create( + BlockedAttributes.builder() + .battr(singletonMap(MediaType.AUDIO, singletonMap("impId1", asList(1, 2)))) + .build()); + final BidRequest request = BidRequest.builder() + .imp(singletonList(Imp.builder() + .id("impId1") + .audio(Audio.builder().build()) + .build())) + .build(); + + // when and then + assertThat(updater.update(request)).isEqualTo(BidRequest.builder() + .imp(singletonList(Imp.builder() + .id("impId1") + .audio(Audio.builder() + .battr(asList(1, 2)) + .build()) + .build())) + .build()); + } + @Test public void shouldNotChangeImpsWhenNoBlockedBannerTypeAndBlockedBannerAttr() { // given @@ -180,7 +236,43 @@ public void shouldNotChangeImpWhenNoBlockedBannerTypeAndBlockedBannerAttrForImp( final RequestUpdater updater = RequestUpdater.create( BlockedAttributes.builder() .btype(singletonMap("impId2", singletonList(1))) - .battr(singletonMap("impId2", singletonList(1))) + .battr(singletonMap(MediaType.BANNER, singletonMap("impId2", singletonList(1)))) + .build()); + final Imp imp = Imp.builder().build(); + final BidRequest request = BidRequest.builder() + .imp(singletonList(imp)) + .build(); + + // when and then + final BidRequest updatedRequest = updater.update(request); + assertThat(updatedRequest.getImp()).hasSize(1); + assertThat(updatedRequest.getImp().get(0)).isSameAs(imp); + } + + @Test + public void shouldNotChangeImpWhenNoBlockedVideoAttrForImp() { + // given + final RequestUpdater updater = RequestUpdater.create( + BlockedAttributes.builder() + .battr(singletonMap(MediaType.VIDEO, singletonMap("impId2", singletonList(1)))) + .build()); + final Imp imp = Imp.builder().build(); + final BidRequest request = BidRequest.builder() + .imp(singletonList(imp)) + .build(); + + // when and then + final BidRequest updatedRequest = updater.update(request); + assertThat(updatedRequest.getImp()).hasSize(1); + assertThat(updatedRequest.getImp().get(0)).isSameAs(imp); + } + + @Test + public void shouldNotChangeImpWhenNoBlockedAudioAttrForImp() { + // given + final RequestUpdater updater = RequestUpdater.create( + BlockedAttributes.builder() + .battr(singletonMap(MediaType.AUDIO, singletonMap("impId2", singletonList(1)))) .build()); final Imp imp = Imp.builder().build(); final BidRequest request = BidRequest.builder() @@ -198,7 +290,7 @@ public void shouldKeepImpBtypeWhenNoBlockedBannerTypeAndPresentBlockedBannerAttr // given final RequestUpdater updater = RequestUpdater.create( BlockedAttributes.builder() - .battr(singletonMap("impId1", singletonList(1))) + .battr(singletonMap(MediaType.BANNER, singletonMap("impId1", singletonList(1)))) .build()); final Imp imp = Imp.builder() .id("impId1") @@ -256,7 +348,10 @@ public void shouldUpdateAllAttributes() { .bcat(asList("cat1", "cat2")) .bapp(asList("app1", "app2")) .btype(singletonMap("impId1", asList(1, 2))) - .battr(singletonMap("impId1", asList(1, 2))) + .battr(Map.of( + MediaType.BANNER, singletonMap("impId1", asList(1, 2)), + MediaType.VIDEO, singletonMap("impId1", asList(3, 4)), + MediaType.AUDIO, singletonMap("impId1", asList(5, 6)))) .build()); final BidRequest request = BidRequest.builder() .imp(singletonList(Imp.builder().id("impId1").build())) @@ -273,6 +368,8 @@ public void shouldUpdateAllAttributes() { .btype(asList(1, 2)) .battr(asList(1, 2)) .build()) + .video(Video.builder().battr(asList(3, 4)).build()) + .audio(Audio.builder().battr(asList(5, 6)).build()) .build())) .build()); } diff --git a/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/core/config/Attribute.java b/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/core/config/Attribute.java index 6e7d3257a33..e60354670e9 100644 --- a/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/core/config/Attribute.java +++ b/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/core/config/Attribute.java @@ -63,8 +63,18 @@ public static AttributeBuilder btypeBuilder() { .field("banner-type"); } - public static AttributeBuilder battrBuilder() { + public static AttributeBuilder bannerBattrBuilder() { return Attribute.builder() .field("banner-attr"); } + + public static AttributeBuilder videoBattrBuilder() { + return Attribute.builder() + .field("video-attr"); + } + + public static AttributeBuilder audioBattrBuilder() { + return Attribute.builder() + .field("audio-attr"); + } } diff --git a/extra/modules/pb-response-correction/pom.xml b/extra/modules/pb-response-correction/pom.xml index 81fa2877d71..802bed7fe04 100644 --- a/extra/modules/pb-response-correction/pom.xml +++ b/extra/modules/pb-response-correction/pom.xml @@ -5,7 +5,7 @@ org.prebid.server.hooks.modules all-modules - 3.14.0-SNAPSHOT + 3.15.0-SNAPSHOT pb-response-correction diff --git a/extra/modules/pb-richmedia-filter/pom.xml b/extra/modules/pb-richmedia-filter/pom.xml index f3d73d09347..fc852b520f2 100644 --- a/extra/modules/pb-richmedia-filter/pom.xml +++ b/extra/modules/pb-richmedia-filter/pom.xml @@ -5,7 +5,7 @@ org.prebid.server.hooks.modules all-modules - 3.14.0-SNAPSHOT + 3.15.0-SNAPSHOT pb-richmedia-filter diff --git a/extra/modules/pom.xml b/extra/modules/pom.xml index 438a800851c..700902c8834 100644 --- a/extra/modules/pom.xml +++ b/extra/modules/pom.xml @@ -5,7 +5,7 @@ org.prebid prebid-server-aggregator - 3.14.0-SNAPSHOT + 3.15.0-SNAPSHOT ../../extra/pom.xml diff --git a/extra/pom.xml b/extra/pom.xml index 123c70cbb3c..6fe748f904a 100644 --- a/extra/pom.xml +++ b/extra/pom.xml @@ -4,7 +4,7 @@ org.prebid prebid-server-aggregator - 3.14.0-SNAPSHOT + 3.15.0-SNAPSHOT pom diff --git a/pom.xml b/pom.xml index 75a749fab00..8c5d08b1dc6 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.prebid prebid-server-aggregator - 3.14.0-SNAPSHOT + 3.15.0-SNAPSHOT extra/pom.xml diff --git a/src/test/groovy/org/prebid/server/functional/model/bidder/BidderName.groovy b/src/test/groovy/org/prebid/server/functional/model/bidder/BidderName.groovy index 1502df5ed70..f91f209395c 100644 --- a/src/test/groovy/org/prebid/server/functional/model/bidder/BidderName.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/bidder/BidderName.groovy @@ -21,6 +21,7 @@ enum BidderName { ACUITYADS("acuityads"), AAX("aax"), ADKERNEL("adkernel"), + IX("ix"), GRID("grid"), MEDIANET("medianet") diff --git a/src/test/groovy/org/prebid/server/functional/model/bidder/GeneralBidderAdapter.groovy b/src/test/groovy/org/prebid/server/functional/model/bidder/GeneralBidderAdapter.groovy new file mode 100644 index 00000000000..dbd47dcda4d --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/bidder/GeneralBidderAdapter.groovy @@ -0,0 +1,17 @@ +package org.prebid.server.functional.model.bidder + +import com.fasterxml.jackson.annotation.JsonProperty + +class GeneralBidderAdapter implements BidderAdapter { + + Object exampleProperty + Integer firstParam + Integer secondParam + @JsonProperty("dealsonly") + Boolean dealsOnly + @JsonProperty("pgdealsonly") + Boolean pgDealsOnly + String siteId + List size + String sid +} diff --git a/src/test/groovy/org/prebid/server/functional/model/bidderspecific/BidderImpExt.groovy b/src/test/groovy/org/prebid/server/functional/model/bidderspecific/BidderImpExt.groovy index e0e3b26d02a..d07ca31ad9d 100644 --- a/src/test/groovy/org/prebid/server/functional/model/bidderspecific/BidderImpExt.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/bidderspecific/BidderImpExt.groovy @@ -1,12 +1,12 @@ package org.prebid.server.functional.model.bidderspecific import groovy.transform.ToString -import org.prebid.server.functional.model.bidder.Generic +import org.prebid.server.functional.model.bidder.GeneralBidderAdapter import org.prebid.server.functional.model.request.auction.ImpExt @ToString(includeNames = true, ignoreNulls = true) class BidderImpExt extends ImpExt { - Generic bidder + GeneralBidderAdapter bidder Rp rp } diff --git a/src/test/groovy/org/prebid/server/functional/model/config/Ortb2BlockingActionOverride.groovy b/src/test/groovy/org/prebid/server/functional/model/config/Ortb2BlockingActionOverride.groovy index 6e09273bef7..de8eae06d04 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/Ortb2BlockingActionOverride.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/Ortb2BlockingActionOverride.groovy @@ -4,11 +4,13 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategies import com.fasterxml.jackson.databind.annotation.JsonNaming import groovy.transform.ToString +import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.AUDIO_BATTR import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.BADV import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.BAPP -import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.BATTR +import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.BANNER_BATTR import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.BCAT import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.BTYPE +import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.VIDEO_BATTR @ToString(includeNames = true, ignoreNulls = true) @JsonNaming(PropertyNamingStrategies.KebabCaseStrategy) @@ -18,6 +20,8 @@ class Ortb2BlockingActionOverride { List blockedAdomain List blockedApp List blockedBannerAttr + List blockedVideoAttr + List blockedAudioAttr List blockedAdvCat List blockedBannerType @@ -27,6 +31,8 @@ class Ortb2BlockingActionOverride { List allowedAdomainForDeals List allowedAppForDeals List allowedBannerAttrForDeals + List allowedVideoAttrForDeals + List allowedAudioAttrForDeals List allowedAdvCatForDeals static Ortb2BlockingActionOverride getDefaultOverride(Ortb2BlockingAttribute attribute, @@ -43,10 +49,18 @@ class Ortb2BlockingActionOverride { blockedApp = blocked allowedAppForDeals = allowedForDeals break - case BATTR: + case BANNER_BATTR: blockedBannerAttr = blocked allowedBannerAttrForDeals = allowedForDeals break + case VIDEO_BATTR: + blockedVideoAttr = blocked + allowedVideoAttrForDeals = allowedForDeals + break + case AUDIO_BATTR: + blockedAudioAttr = blocked + allowedAudioAttrForDeals = allowedForDeals + break case BCAT: blockedAdvCat = blocked allowedAdvCatForDeals = allowedForDeals diff --git a/src/test/groovy/org/prebid/server/functional/model/config/Ortb2BlockingAttribute.groovy b/src/test/groovy/org/prebid/server/functional/model/config/Ortb2BlockingAttribute.groovy index e1688e2d2b3..15c54c2c021 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/Ortb2BlockingAttribute.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/Ortb2BlockingAttribute.groovy @@ -6,10 +6,18 @@ import groovy.transform.ToString @ToString(includeNames = true, ignoreNulls = true) enum Ortb2BlockingAttribute { - BADV, BAPP, BATTR, BCAT, BTYPE + BADV('badv'), + BAPP('bapp'), + BANNER_BATTR('battr'), + VIDEO_BATTR('battr'), + AUDIO_BATTR('battr'), + BCAT('bcat'), + BTYPE('btype') @JsonValue - String getValue() { - name().toLowerCase() + final String value + + Ortb2BlockingAttribute(String value) { + this.value = value } } diff --git a/src/test/groovy/org/prebid/server/functional/model/config/Ortb2BlockingAttributeConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/Ortb2BlockingAttributeConfig.groovy index 5c405269466..9e622472024 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/Ortb2BlockingAttributeConfig.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/Ortb2BlockingAttributeConfig.groovy @@ -4,11 +4,13 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategies import com.fasterxml.jackson.databind.annotation.JsonNaming import groovy.transform.ToString +import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.AUDIO_BATTR import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.BADV import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.BAPP -import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.BATTR +import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.BANNER_BATTR import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.BCAT import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.BTYPE +import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.VIDEO_BATTR @ToString(includeNames = true, ignoreNulls = true) @JsonNaming(PropertyNamingStrategies.KebabCaseStrategy) @@ -19,6 +21,8 @@ class Ortb2BlockingAttributeConfig { Object blockedAdomain Object blockedApp Object blockedBannerAttr + Object blockedVideoAttr + Object blockedAudioAttr Object blockedAdvCat Object blockedBannerType @@ -28,6 +32,8 @@ class Ortb2BlockingAttributeConfig { Object allowedAdomainForDeals Object allowedAppForDeals Object allowedBannerAttrForDeals + Object allowedVideoAttrForDeals + Object allowedAudioAttrForDeals Object allowedAdvCatForDeals Ortb2BlockingActionOverride actionOverrides @@ -44,10 +50,18 @@ class Ortb2BlockingAttributeConfig { blockedApp = ortb2Attributes allowedAppForDeals = ortb2AttributesForDeals break - case BATTR: + case BANNER_BATTR: blockedBannerAttr = ortb2Attributes allowedBannerAttrForDeals = ortb2AttributesForDeals break + case VIDEO_BATTR: + blockedVideoAttr = ortb2Attributes + allowedVideoAttrForDeals = ortb2AttributesForDeals + break + case AUDIO_BATTR: + blockedAudioAttr = ortb2Attributes + allowedAudioAttrForDeals = ortb2AttributesForDeals + break case BCAT: blockedAdvCat = ortb2Attributes allowedAdvCatForDeals = ortb2AttributesForDeals diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/BidRequestExt.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/BidRequestExt.groovy index f7aee45fb75..c253291dbf8 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/BidRequestExt.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/BidRequestExt.groovy @@ -11,4 +11,5 @@ class BidRequestExt { AppNexus appnexus String bc String platform + IxDiag ixdiag } diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Bidder.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Bidder.groovy index 9b5c78f5d97..a1078731f44 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/Bidder.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Bidder.groovy @@ -19,6 +19,7 @@ class Bidder { @JsonProperty("appnexus") AppNexus appNexus Openx openx + Ix ix static Bidder getDefaultBidder() { new Bidder().tap { diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Imp.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Imp.groovy index 8ea26570de9..dbea9b32624 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/Imp.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Imp.groovy @@ -90,7 +90,8 @@ class Imp { (bidder.genericCamelCase): BidderName.GENERIC_CAMEL_CASE, (bidder.rubicon) : BidderName.RUBICON, (bidder.appNexus) : BidderName.APPNEXUS, - (bidder.openx) : BidderName.OPENX + (bidder.openx) : BidderName.OPENX, + (bidder.ix) : BidderName.IX ].findAll { it.key } if (bidderNames.size() != 1) { @@ -99,4 +100,14 @@ class Imp { bidderNames.values().first() } + + @JsonIgnore + List getMediaTypes() { + return [ + (banner ? BANNER : null), + (video ? VIDEO : null), + (nativeObj ? NATIVE : null), + (audio ? AUDIO : null) + ].findAll { it } + } } diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpExt.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpExt.groovy index fe8cd0f089c..e817a4540a0 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpExt.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpExt.groovy @@ -22,6 +22,7 @@ class ImpExt { ImpExtContextData data String tid String gpid + String sid Integer ae String all String skadn diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Ix.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Ix.groovy new file mode 100644 index 00000000000..a620c7646b1 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Ix.groovy @@ -0,0 +1,20 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.EqualsAndHashCode +import org.prebid.server.functional.util.PBSUtils + +@EqualsAndHashCode +class Ix { + + String siteId + List size + String sid + + static Ix getDefault() { + new Ix().tap { + siteId = PBSUtils.randomString + size = [PBSUtils.randomNumber, PBSUtils.randomNumber] + sid = PBSUtils.randomString + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/IxDiag.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/IxDiag.groovy new file mode 100644 index 00000000000..7bc0adc1054 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/IxDiag.groovy @@ -0,0 +1,11 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class IxDiag { + + String pbsv + String pbjsv + String multipleSiteIds +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/Bid.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/Bid.groovy index e4fb04de375..22e29b76908 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/Bid.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/Bid.groovy @@ -47,7 +47,8 @@ class Bid implements ObjectMapperWrapper { Integer heightRatio Integer exp Integer dur - Integer mtype + @JsonProperty("mtype") + BidMediaType mediaType Integer slotinpod BidExt ext diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/BidMediaType.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/BidMediaType.groovy new file mode 100644 index 00000000000..76aa2a558f9 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/BidMediaType.groovy @@ -0,0 +1,18 @@ +package org.prebid.server.functional.model.response.auction + +import com.fasterxml.jackson.annotation.JsonValue + +enum BidMediaType { + + BANNER(1), + VIDEO(2), + AUDIO(3), + NATIVE(4) + + @JsonValue + final Integer value + + BidMediaType(Integer value) { + this.value = value + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/ErrorType.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/ErrorType.groovy index e62b548fbe6..267a23cc067 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/ErrorType.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/ErrorType.groovy @@ -13,6 +13,7 @@ enum ErrorType { CACHE("cache"), ALIAS("alias"), TARGETING("targeting"), + IX("ix"), OPENX("openx") @JsonValue diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/ortb2blocking/Ortb2BlockingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/ortb2blocking/Ortb2BlockingSpec.groovy index 4df794b3ead..b37cae6a067 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/ortb2blocking/Ortb2BlockingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/ortb2blocking/Ortb2BlockingSpec.groovy @@ -2,7 +2,6 @@ package org.prebid.server.functional.tests.module.ortb2blocking import org.prebid.server.functional.model.bidder.BidderName import org.prebid.server.functional.model.bidder.Generic -import org.prebid.server.functional.model.bidder.Openx import org.prebid.server.functional.model.config.AccountConfig import org.prebid.server.functional.model.config.AccountHooksConfiguration import org.prebid.server.functional.model.config.ExecutionPlan @@ -14,9 +13,16 @@ import org.prebid.server.functional.model.config.Ortb2BlockingConfig import org.prebid.server.functional.model.config.Ortb2BlockingOverride import org.prebid.server.functional.model.config.PbsModulesConfig import org.prebid.server.functional.model.db.Account +import org.prebid.server.functional.model.request.auction.Asset +import org.prebid.server.functional.model.request.auction.Audio +import org.prebid.server.functional.model.request.auction.Banner +import org.prebid.server.functional.model.request.auction.Ix import org.prebid.server.functional.model.request.auction.BidRequest import org.prebid.server.functional.model.request.auction.Imp +import org.prebid.server.functional.model.request.auction.Video +import org.prebid.server.functional.model.response.auction.Adm import org.prebid.server.functional.model.response.auction.Bid +import org.prebid.server.functional.model.response.auction.BidMediaType import org.prebid.server.functional.model.response.auction.BidResponse import org.prebid.server.functional.model.response.auction.ErrorType import org.prebid.server.functional.model.response.auction.MediaType @@ -24,36 +30,39 @@ import org.prebid.server.functional.model.response.auction.SeatBid import org.prebid.server.functional.service.PrebidServerService import org.prebid.server.functional.tests.module.ModuleBaseSpec import org.prebid.server.functional.util.PBSUtils -import spock.lang.PendingFeature import static org.prebid.server.functional.model.ModuleName.ORTB2_BLOCKING import static org.prebid.server.functional.model.bidder.BidderName.ALIAS import static org.prebid.server.functional.model.bidder.BidderName.GENERIC -import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.bidder.BidderName.IX import static org.prebid.server.functional.model.config.Endpoint.OPENRTB2_AUCTION +import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.AUDIO_BATTR import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.BADV import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.BAPP -import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.BATTR +import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.BANNER_BATTR import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.BCAT import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.BTYPE +import static org.prebid.server.functional.model.config.Ortb2BlockingAttribute.VIDEO_BATTR import static org.prebid.server.functional.model.config.Stage.BIDDER_REQUEST import static org.prebid.server.functional.model.config.Stage.RAW_BIDDER_RESPONSE import static org.prebid.server.functional.model.response.auction.BidRejectionReason.RESPONSE_REJECTED_ADVERTISER_BLOCKED +import static org.prebid.server.functional.model.response.auction.MediaType.AUDIO import static org.prebid.server.functional.model.response.auction.MediaType.BANNER import static org.prebid.server.functional.model.response.auction.MediaType.VIDEO import static org.prebid.server.functional.testcontainers.Dependencies.getNetworkServiceContainer class Ortb2BlockingSpec extends ModuleBaseSpec { - private static final Map OPENX_CONFIG = ["adapters.openx.enabled" : "true", - "adapters.openx.endpoint": "$networkServiceContainer.rootUri/auction".toString()] + private static final Map IX_CONFIG = ["adapters.ix.enabled" : "true", + "adapters.ix.endpoint": "$networkServiceContainer.rootUri/auction".toString()] private static final String WILDCARD = '*' - private final PrebidServerService pbsServiceWithEnabledOrtb2Blocking = pbsServiceFactory.getService(ortb2BlockingSettings + OPENX_CONFIG) + private final PrebidServerService pbsServiceWithEnabledOrtb2Blocking = pbsServiceFactory.getService(ortb2BlockingSettings + IX_CONFIG + + ["adapters.generic.ortb.multiformat-supported": "true"]) def "PBS should send original array ortb2 attribute to bidder when enforce blocking is disabled"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(attributeName) and: "Account in the DB with blocking configuration" def account = getAccountWithOrtb2BlockingConfig(bidRequest.accountId, [ortb2Attributes], attributeName) @@ -83,13 +92,15 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { PBSUtils.randomString | BADV PBSUtils.randomString | BAPP PBSUtils.randomString | BCAT - PBSUtils.randomNumber | BATTR + PBSUtils.randomNumber | BANNER_BATTR + PBSUtils.randomNumber | VIDEO_BATTR + PBSUtils.randomNumber | AUDIO_BATTR PBSUtils.randomNumber | BTYPE } def "PBS should be able to send original array ortb2 attribute to bidder alias"() { given: "Default bid request with alias" - def bidRequest = BidRequest.defaultBidRequest.tap { + def bidRequest = getBidRequestForOrtbAttribute(attributeName).tap { ext.prebid.aliases = [(ALIAS.value): GENERIC] imp[0].ext.prebid.bidder.generic = null imp[0].ext.prebid.bidder.alias = new Generic() @@ -114,13 +125,15 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { PBSUtils.randomString | BADV PBSUtils.randomString | BAPP PBSUtils.randomString | BCAT - PBSUtils.randomNumber | BATTR + PBSUtils.randomNumber | BANNER_BATTR + PBSUtils.randomNumber | VIDEO_BATTR + PBSUtils.randomNumber | AUDIO_BATTR PBSUtils.randomNumber | BTYPE } def "PBS shouldn't send original single ortb2 attribute to bidder when enforce blocking is disabled"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(attributeName) and: "Account in the DB with blocking configuration" def account = getAccountWithOrtb2BlockingConfig(bidRequest.accountId, ortb2Attributes, attributeName) @@ -151,13 +164,15 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { PBSUtils.randomString | BADV PBSUtils.randomString | BAPP PBSUtils.randomString | BCAT - PBSUtils.randomNumber | BATTR + PBSUtils.randomNumber | BANNER_BATTR + PBSUtils.randomNumber | VIDEO_BATTR + PBSUtils.randomNumber | AUDIO_BATTR PBSUtils.randomNumber | BTYPE } def "PBS shouldn't send original inappropriate ortb2 attribute to bidder when blocking is disabled"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(attributeName) and: "Account in the DB with blocking configuration" def account = getAccountWithOrtb2BlockingConfig(bidRequest.accountId, [ortb2Attributes], attributeName) @@ -182,13 +197,15 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { PBSUtils.randomNumber | BADV PBSUtils.randomNumber | BAPP PBSUtils.randomNumber | BCAT - PBSUtils.randomString | BATTR + PBSUtils.randomString | BANNER_BATTR + PBSUtils.randomString | VIDEO_BATTR + PBSUtils.randomString | AUDIO_BATTR PBSUtils.randomString | BTYPE } def "PBS shouldn't send original inappropriate ortb2 attribute to bidder when blocking is enabled"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(attributeName) and: "Account in the DB with blocking configuration" def ortb2BlockingAttributeConfig = Ortb2BlockingAttributeConfig.getDefaultConfig([ortb2Attributes], attributeName).tap { @@ -220,12 +237,14 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { PBSUtils.randomString | BADV PBSUtils.randomString | BAPP PBSUtils.randomString | BCAT - PBSUtils.randomNumber | BATTR + PBSUtils.randomNumber | BANNER_BATTR + PBSUtils.randomNumber | VIDEO_BATTR + PBSUtils.randomNumber | AUDIO_BATTR } def "PBS should send only not matched ortb2 attribute to bidder when blocking is enabled"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(attributeName) and: "Account in the DB with blocking configuration" def ortb2BlockingAttributeConfig = Ortb2BlockingAttributeConfig.getDefaultConfig([disallowedOrtb2Attributes], attributeName).tap { @@ -255,16 +274,82 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { assert !response?.ext?.prebid?.modules?.warnings where: - allowedOrtb2Attributes | disallowedOrtb2Attributes | attributeName - PBSUtils.randomString | PBSUtils.randomString | BADV - PBSUtils.randomString | PBSUtils.randomString | BAPP - PBSUtils.randomString | PBSUtils.randomString | BCAT - PBSUtils.randomNumber | PBSUtils.randomNumber | BATTR + allowedOrtb2Attributes | disallowedOrtb2Attributes | attributeName + PBSUtils.randomString | PBSUtils.randomString | BADV + PBSUtils.randomString | PBSUtils.randomString | BAPP + PBSUtils.randomString | PBSUtils.randomString | BCAT + PBSUtils.randomNumber | PBSUtils.randomNumber | BANNER_BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | VIDEO_BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | AUDIO_BATTR + PBSUtils.randomNegativeNumber | PBSUtils.randomNegativeNumber | BANNER_BATTR + PBSUtils.randomNegativeNumber | PBSUtils.randomNegativeNumber | VIDEO_BATTR + PBSUtils.randomNegativeNumber | PBSUtils.randomNegativeNumber | AUDIO_BATTR + } + + def "PBS should left only not matched ortb2 attribute to bidder with multiply type imp when blocking is enabled"() { + given: "Default bid request with proper ortb attribute" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp.first.tap { + banner = Banner.getDefaultBanner().tap { + battr = [PBSUtils.randomNumber] + } + video = Video.getDefaultVideo().tap { + battr = [PBSUtils.randomNumber] + } + audio = Audio.getDefaultAudio().tap { + battr = [PBSUtils.randomNumber] + } + ext.prebid.bidder.generic = null + ext.prebid.bidder.ix = Ix.default + } + imp[0].ext.prebid.bidder.generic = null + imp[0].ext.prebid.bidder.ix = Ix.default + } + + and: "Account in the DB with blocking configuration" + def disallowedOrtb2Attributes = PBSUtils.randomNumber + def ortb2BlockingAttributeConfig = Ortb2BlockingAttributeConfig.getDefaultConfig([disallowedOrtb2Attributes], attributeName).tap { + enforceBlocks = true + } + def account = getAccountWithOrtb2BlockingConfig(bidRequest.accountId, [(attributeName): ortb2BlockingAttributeConfig]) + accountDao.save(account) + + and: "Default bidder response with ortb2 attributes" + def removeBid = getBidWithOrtb2Attribute(bidRequest.imp.first, disallowedOrtb2Attributes, attributeName).tap { + it.mediaType = enforceType + } + def presentBid = getBidWithOrtb2Attribute(bidRequest.imp.first, disallowedOrtb2Attributes, attributeName).tap { + it.mediaType = presentType + } + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { + it.seatbid.first.bid = [removeBid, presentBid] + } + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes the auction request" + def response = pbsServiceWithEnabledOrtb2Blocking.sendAuctionRequest(bidRequest) + + then: "PBS response should contain only allowed seatbid" + assert response.seatbid.bid.flatten().size() == 1 + assert response.seatbid.first.bid.first.mediaType == presentType + assert getOrtb2Attributes(response.seatbid.first.bid.first, attributeName) == [disallowedOrtb2Attributes]*.toString() + + and: "PBS response shouldn't contain any module errors" + assert !response?.ext?.prebid?.modules?.errors + + and: "PBS response shouldn't contain any module warning" + assert !response?.ext?.prebid?.modules?.warnings + + where: + attributeName | enforceType | presentType + BANNER_BATTR | BidMediaType.BANNER | BidMediaType.AUDIO + VIDEO_BATTR | BidMediaType.VIDEO | BidMediaType.BANNER + AUDIO_BATTR | BidMediaType.AUDIO | BidMediaType.VIDEO } def "PBS should send original inappropriate ortb2 attribute to bidder when blocking is disabled"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(attributeName) and: "Account in the DB with blocking configuration" def ortb2BlockingAttributeConfig = Ortb2BlockingAttributeConfig.getDefaultConfig([ortb2Attributes], attributeName).tap { @@ -296,12 +381,14 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { PBSUtils.randomString | BADV PBSUtils.randomString | BAPP PBSUtils.randomString | BCAT - PBSUtils.randomNumber | BATTR + PBSUtils.randomNumber | BANNER_BATTR + PBSUtils.randomNumber | VIDEO_BATTR + PBSUtils.randomNumber | AUDIO_BATTR } def "PBS should discard unknown adomain bids when enforcement is enabled"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(BADV) and: "Account in the DB with blocking configuration" def ortb2BlockingAttributeConfig = new Ortb2BlockingAttributeConfig(enforceBlocks: true, blockUnknownAdomain: true) @@ -339,8 +426,8 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { } def "PBS should not discard unknown adomain bids when enforcement is disabled"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(BADV) and: "Account in the DB with blocking configuration" def account = getAccountWithOrtb2BlockingConfig(bidRequest.accountId, [(BADV): ortb2BlockingAttributeConfig]) @@ -374,8 +461,8 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { } def "PBS should discard unknown adv cat bids when enforcement is enabled"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(BCAT) and: "Account in the DB with blocking configuration" def ortb2BlockingAttributeConfig = new Ortb2BlockingAttributeConfig(enforceBlocks: true, blockUnknownAdvCat: true) @@ -413,8 +500,8 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { } def "PBS should not discard unknown adv cat bids when enforcement is disabled"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(BCAT) and: "Account in the DB with blocking configuration" def account = getAccountWithOrtb2BlockingConfig(bidRequest.accountId, [(BCAT): ortb2BlockingAttributeConfig]) @@ -448,8 +535,8 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { } def "PBS should not discard bids with deals when allowed ortb2 attribute for deals is matched"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(attributeName) and: "Account in the DB with blocking configuration" def attributes = [(attributeName): Ortb2BlockingAttributeConfig.getDefaultConfig([ortb2Attributes], attributeName, [ortb2Attributes]).tap { @@ -483,12 +570,14 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { PBSUtils.randomString | BADV PBSUtils.randomString | BAPP PBSUtils.randomString | BCAT - PBSUtils.randomNumber | BATTR + PBSUtils.randomNumber | BANNER_BATTR + PBSUtils.randomNumber | VIDEO_BATTR + PBSUtils.randomNumber | AUDIO_BATTR } def "PBS should discard bids with deals when allowed ortb2 attribute for deals is not matched"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(attributeName) and: "Account in the DB with blocking configuration" def attributeConfig = Ortb2BlockingAttributeConfig.getDefaultConfig([allowedOrtb2Attributes, dielsOrtb2Attributes], attributeName, [allowedOrtb2Attributes]).tap { @@ -521,17 +610,19 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { PBSUtils.randomString | PBSUtils.randomString | BADV PBSUtils.randomString | PBSUtils.randomString | BAPP PBSUtils.randomString | PBSUtils.randomString | BCAT - PBSUtils.randomNumber | PBSUtils.randomNumber | BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | BANNER_BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | VIDEO_BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | AUDIO_BATTR } def "PBS should be able to override enforcement by bidder"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest.tap { - imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(attributeName).tap { + imp[0].ext.prebid.bidder.ix = Ix.default } and: "Account in the DB with blocking configuration" - def blockingCondition = new Ortb2BlockingConditions(bidders: [OPENX]) + def blockingCondition = new Ortb2BlockingConditions(bidders: [IX]) def ortb2BlockingAttributeConfig = Ortb2BlockingAttributeConfig.getDefaultConfig([ortb2Attributes], attributeName).tap { enforceBlocks = true actionOverrides = new Ortb2BlockingActionOverride(enforceBlocks: [new Ortb2BlockingOverride(override: false, conditions: blockingCondition)]) @@ -542,7 +633,7 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { and: "Default bidder response with ortb2 attributes" def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { it.seatbid = [new SeatBid(bid: [getBidWithOrtb2Attribute(bidRequest.imp.first, ortb2Attributes, attributeName)], seat: GENERIC), - new SeatBid(bid: [getBidWithOrtb2Attribute(bidRequest.imp.first, ortb2Attributes, attributeName)], seat: OPENX)] + new SeatBid(bid: [getBidWithOrtb2Attribute(bidRequest.imp.first, ortb2Attributes, attributeName)], seat: IX)] } bidder.setResponse(bidRequest.id, bidResponse) @@ -551,7 +642,7 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { then: "PBS response should contain only openx seatbid" assert response.seatbid.size() == 1 - assert response.seatbid.first.seat == OPENX + assert response.seatbid.first.seat == IX assert getOrtb2Attributes(response.seatbid.first.bid.first, attributeName) == [ortb2Attributes]*.toString() and: "PBS response shouldn't contain any module errors" @@ -565,14 +656,16 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { PBSUtils.randomString | BADV PBSUtils.randomString | BAPP PBSUtils.randomString | BCAT - PBSUtils.randomNumber | BATTR + PBSUtils.randomNumber | BANNER_BATTR + PBSUtils.randomNumber | VIDEO_BATTR + PBSUtils.randomNumber | AUDIO_BATTR } def "PBS should be able to override enforcement by media type"() { - given: "Default bidRequest" + given: "Bid request with multy type imp" def bannerImp = Imp.getDefaultImpression(BANNER) def videoImp = Imp.getDefaultImpression(VIDEO) - def bidRequest = BidRequest.defaultBidRequest.tap { + def bidRequest = getBidRequestForOrtbAttribute(attributeName).tap { imp = [bannerImp, videoImp] } @@ -614,25 +707,31 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { } def "PBS should be able to override enforcement by media type for battr attribute"() { - given: "Default bidRequest" - def bannerImp = Imp.getDefaultImpression(BANNER) - def bidRequest = BidRequest.defaultBidRequest.tap { - imp = [bannerImp] + given: "Default bid request with proper ortb attribute" + BidRequest bidRequest = getBidRequestForOrtbAttribute(attributeName, [PBSUtils.randomNumber]).tap { +// default resolve for bids always prefer type from request, ix from response and only then from request if null + imp[0].ext.prebid.bidder.generic = null + imp[0].ext.prebid.bidder.ix = Ix.default } and: "Account in the DB with blocking configuration" - def blockingCondition = new Ortb2BlockingConditions(mediaType: [BANNER]) + def blockingCondition = new Ortb2BlockingConditions(mediaType: [mediaType]) def ortb2Attribute = PBSUtils.randomNumber - def ortb2BlockingAttributeConfig = Ortb2BlockingAttributeConfig.getDefaultConfig([ortb2Attribute], BATTR).tap { + def ortb2BlockingAttributeConfig = Ortb2BlockingAttributeConfig.getDefaultConfig([ortb2Attribute], attributeName).tap { enforceBlocks = true actionOverrides = new Ortb2BlockingActionOverride(enforceBlocks: [new Ortb2BlockingOverride(override: false, conditions: blockingCondition)]) } - def account = getAccountWithOrtb2BlockingConfig(bidRequest.accountId, [(BATTR): ortb2BlockingAttributeConfig]) + def account = getAccountWithOrtb2BlockingConfig(bidRequest.accountId, [(attributeName): ortb2BlockingAttributeConfig]) accountDao.save(account) and: "Default bidder response with ortb2 attributes" + def bid = getBidWithOrtb2Attribute(bidRequest.imp.first, ortb2Attribute, attributeName).tap { + it.mediaType = bidMediaType + it.adm = new Adm(assets: [Asset.defaultAsset]) // required for video type + } def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { - it.seatbid = [new SeatBid(bid: [getBidWithOrtb2Attribute(bannerImp, ortb2Attribute, BATTR)])] + it.seatbid = [new SeatBid(bid: [bid])] + } bidder.setResponse(bidRequest.id, bidResponse) @@ -641,19 +740,81 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { then: "PBS response should contain banner seatbid" assert response.seatbid.bid.flatten().size() == 1 - assert response.seatbid.first.bid.first.impid == bannerImp.id - assert getOrtb2Attributes(response.seatbid.first.bid.first, BATTR) == [ortb2Attribute]*.toString() + assert response.seatbid.first.bid.first.impid == bidRequest.imp.first.id + assert getOrtb2Attributes(response.seatbid.first.bid.first, attributeName) == [ortb2Attribute]*.toString() and: "PBS response shouldn't contain any module errors" assert !response?.ext?.prebid?.modules?.errors and: "PBS response shouldn't contain any module warning" assert !response?.ext?.prebid?.modules?.warnings + + where: + attributeName | mediaType | bidMediaType + BANNER_BATTR | BANNER | null + VIDEO_BATTR | VIDEO | null + AUDIO_BATTR | AUDIO | null + BANNER_BATTR | BANNER | BidMediaType.BANNER + VIDEO_BATTR | VIDEO | BidMediaType.VIDEO + AUDIO_BATTR | AUDIO | BidMediaType.AUDIO + BANNER_BATTR | BANNER | BidMediaType.AUDIO + VIDEO_BATTR | VIDEO | BidMediaType.BANNER + AUDIO_BATTR | AUDIO | BidMediaType.VIDEO + } + + def "PBS shouldn't be able to override enforcement by incorrect media type for battr attribute"() { + given: "Default bid request with proper ortb attribute" + BidRequest bidRequest = getBidRequestForOrtbAttribute(attributeName, [PBSUtils.randomNumber]).tap { + // default resolve for bids always prefer type from request, ix from response and only then from request if null + imp[0].ext.prebid.bidder.generic = null + imp[0].ext.prebid.bidder.ix = Ix.default + } + + and: "Account in the DB with blocking configuration" + def blockingCondition = new Ortb2BlockingConditions(mediaType: [mediaType]) + def ortb2Attribute = PBSUtils.randomNumber + def ortb2BlockingAttributeConfig = Ortb2BlockingAttributeConfig.getDefaultConfig([ortb2Attribute], attributeName).tap { + enforceBlocks = true + actionOverrides = new Ortb2BlockingActionOverride(enforceBlocks: [new Ortb2BlockingOverride(override: false, conditions: blockingCondition)]) + } + def account = getAccountWithOrtb2BlockingConfig(bidRequest.accountId, [(attributeName): ortb2BlockingAttributeConfig]) + accountDao.save(account) + + and: "Default bidder response with ortb2 attributes" + def bid = getBidWithOrtb2Attribute(bidRequest.imp.first, ortb2Attribute, attributeName).tap { + it.mediaType = bidMediaType + } + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { + it.seatbid = [new SeatBid(bid: [bid])] + } + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes the auction request" + def response = pbsServiceWithEnabledOrtb2Blocking.sendAuctionRequest(bidRequest) + + then: "PBS response shouldn't contain any seatbid" + assert !response.seatbid.bid.flatten().size() + + and: "PBS response shouldn't contain any module errors" + assert !response?.ext?.prebid?.modules?.errors + + and: "PBS response shouldn't contain any module warning" + assert !response?.ext?.prebid?.modules?.warnings + + and: "PBS request should contain original ortb2 attribute" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert getOrtb2Attributes(bidderRequest, attributeName) == getOrtb2Attributes(bidRequest, attributeName) + + where: + attributeName | mediaType | bidMediaType + BANNER_BATTR | AUDIO | null + VIDEO_BATTR | BANNER | null + AUDIO_BATTR | VIDEO | null } def "PBS should be able to override enforcement by deal id"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(attributeName) and: "Account in the DB with blocking configuration" def blockingCondition = new Ortb2BlockingOverride(override: [ortb2Attributes], conditions: new Ortb2BlockingConditions(dealIds: [dealId.toString()])) @@ -689,16 +850,20 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { PBSUtils.randomNumber | PBSUtils.randomString | PBSUtils.randomString | BADV PBSUtils.randomNumber | PBSUtils.randomString | PBSUtils.randomString | BAPP PBSUtils.randomNumber | PBSUtils.randomString | PBSUtils.randomString | BCAT - PBSUtils.randomNumber | PBSUtils.randomNumber | PBSUtils.randomNumber | BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | PBSUtils.randomNumber | BANNER_BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | PBSUtils.randomNumber | VIDEO_BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | PBSUtils.randomNumber | AUDIO_BATTR WILDCARD | PBSUtils.randomString | PBSUtils.randomString | BADV WILDCARD | PBSUtils.randomString | PBSUtils.randomString | BAPP WILDCARD | PBSUtils.randomString | PBSUtils.randomString | BCAT - WILDCARD | PBSUtils.randomNumber | PBSUtils.randomNumber | BATTR + WILDCARD | PBSUtils.randomNumber | PBSUtils.randomNumber | BANNER_BATTR + WILDCARD | PBSUtils.randomNumber | PBSUtils.randomNumber | VIDEO_BATTR + WILDCARD | PBSUtils.randomNumber | PBSUtils.randomNumber | AUDIO_BATTR } def "PBS should be able to override blocked ortb2 attribute by bidder"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(attributeName) and: "Account in the DB with blocking configuration" def blockingCondition = new Ortb2BlockingConditions(bidders: [GENERIC]) @@ -734,12 +899,14 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { PBSUtils.randomString | PBSUtils.randomString | BADV PBSUtils.randomString | PBSUtils.randomString | BAPP PBSUtils.randomString | PBSUtils.randomString | BCAT - PBSUtils.randomNumber | PBSUtils.randomNumber | BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | BANNER_BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | VIDEO_BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | AUDIO_BATTR } def "PBS should be able to override blocked ortb2 attribute by media type"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(attributeName) and: "Account in the DB with blocking configuration" def blockingCondition = new Ortb2BlockingConditions(mediaType: [BANNER]) @@ -775,17 +942,19 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { PBSUtils.randomString | PBSUtils.randomString | BADV PBSUtils.randomString | PBSUtils.randomString | BAPP PBSUtils.randomString | PBSUtils.randomString | BCAT - PBSUtils.randomNumber | PBSUtils.randomNumber | BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | BANNER_BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | BANNER_BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | BANNER_BATTR } def "PBS should be able to override block unknown adomain by bidder"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest.tap { - imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(BADV).tap { + imp[0].ext.prebid.bidder.ix = Ix.default } and: "Account in the DB with blocking configuration" - def blockingCondition = new Ortb2BlockingConditions(bidders: [OPENX]) + def blockingCondition = new Ortb2BlockingConditions(bidders: [IX]) and: "Account in the DB with blocking configuration" def ortb2BlockingAttributeConfig = new Ortb2BlockingAttributeConfig(enforceBlocks: true, blockUnknownAdomain: true).tap { @@ -800,16 +969,16 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { } def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { it.seatbid = [new SeatBid(bid: [bidWithOutAdomain], seat: GENERIC), - new SeatBid(bid: [bidWithOutAdomain], seat: OPENX)] + new SeatBid(bid: [bidWithOutAdomain], seat: IX)] } bidder.setResponse(bidRequest.id, bidResponse) when: "PBS processes the auction request" def response = pbsServiceWithEnabledOrtb2Blocking.sendAuctionRequest(bidRequest) - then: "PBS response should contain only openx seatbid" + then: "PBS response should contain only ix seatbid" assert response.seatbid.bid.flatten().size() == 1 - assert response.seatbid.first.seat == OPENX + assert response.seatbid.first.seat == IX and: "PBS response shouldn't contain any module errors" assert !response?.ext?.prebid?.modules?.errors @@ -819,8 +988,8 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { } def "PBS should be able to override block unknown adomain by media type"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(BADV) and: "Account in the DB with blocking configuration" def blockingCondition = new Ortb2BlockingConditions(mediaType: [BANNER]) @@ -855,13 +1024,13 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { } def "PBS should be able to override block unknown adv-cat by bidder"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest.tap { - imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(BCAT).tap { + imp[0].ext.prebid.bidder.ix = Ix.default } and: "Account in the DB with blocking configuration" - def blockingCondition = new Ortb2BlockingConditions(bidders: [OPENX]) + def blockingCondition = new Ortb2BlockingConditions(bidders: [IX]) and: "Account in the DB with blocking configuration" def ortb2BlockingAttributeConfig = new Ortb2BlockingAttributeConfig(enforceBlocks: true, blockUnknownAdvCat: true).tap { @@ -876,16 +1045,16 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { } def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { it.seatbid = [new SeatBid(bid: [bidWithOutCat], seat: GENERIC), - new SeatBid(bid: [bidWithOutCat], seat: OPENX)] + new SeatBid(bid: [bidWithOutCat], seat: IX)] } bidder.setResponse(bidRequest.id, bidResponse) when: "PBS processes the auction request" def response = pbsServiceWithEnabledOrtb2Blocking.sendAuctionRequest(bidRequest) - then: "PBS response should contain only openx seatbid" + then: "PBS response should contain only ix seatbid" assert response.seatbid.bid.flatten().size() == 1 - assert response.seatbid.first.seat == OPENX + assert response.seatbid.first.seat == IX and: "PBS response shouldn't contain any module errors" assert !response?.ext?.prebid?.modules?.errors @@ -895,8 +1064,8 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { } def "PBS should be able to override block unknown adv-cat by media type"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(BCAT) and: "Account in the DB with blocking configuration" def blockingCondition = new Ortb2BlockingConditions(mediaType: [BANNER]) @@ -929,8 +1098,8 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { } def "PBS should be able to override allowed ortb2 attribute for deals by deal ids"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(attributeName) and: "Account in the DB with blocking configuration" def dealId = PBSUtils.randomNumber @@ -968,12 +1137,14 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { PBSUtils.randomString | PBSUtils.randomString | BADV PBSUtils.randomString | PBSUtils.randomString | BAPP PBSUtils.randomString | PBSUtils.randomString | BCAT - PBSUtils.randomNumber | PBSUtils.randomNumber | BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | BANNER_BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | VIDEO_BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | AUDIO_BATTR } def "PBS should use first override when multiple match same condition"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(attributeName) and: "Account in the DB with blocking configuration" def firstOrtb2BlockingOverride = new Ortb2BlockingOverride(override: [firstOverrideAttributes], conditions: blockingCondition) @@ -1003,23 +1174,27 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { and: "PBS response should contain proper warning" assert response?.ext?.prebid?.modules?.warnings?.ortb2Blocking["ortb2-blocking-bidder-request"] == - ["More than one conditions matches request. Bidder: generic, request media types: [banner]"] + ["More than one conditions matches request. Bidder: generic, request media types: [${bidRequest.imp[0].mediaTypes[0].value}]"] where: blockingCondition | ortb2Attributes | firstOverrideAttributes | secondOverrideAttributes | attributeName new Ortb2BlockingConditions(bidders: [GENERIC]) | PBSUtils.randomString | PBSUtils.randomString | PBSUtils.randomString | BADV new Ortb2BlockingConditions(bidders: [GENERIC]) | PBSUtils.randomString | PBSUtils.randomString | PBSUtils.randomString | BAPP new Ortb2BlockingConditions(bidders: [GENERIC]) | PBSUtils.randomString | PBSUtils.randomString | PBSUtils.randomString | BCAT - new Ortb2BlockingConditions(bidders: [GENERIC]) | PBSUtils.randomNumber | PBSUtils.randomNumber | PBSUtils.randomNumber | BATTR + new Ortb2BlockingConditions(bidders: [GENERIC]) | PBSUtils.randomNumber | PBSUtils.randomNumber | PBSUtils.randomNumber | BANNER_BATTR + new Ortb2BlockingConditions(bidders: [GENERIC]) | PBSUtils.randomNumber | PBSUtils.randomNumber | PBSUtils.randomNumber | VIDEO_BATTR + new Ortb2BlockingConditions(bidders: [GENERIC]) | PBSUtils.randomNumber | PBSUtils.randomNumber | PBSUtils.randomNumber | AUDIO_BATTR new Ortb2BlockingConditions(mediaType: [BANNER]) | PBSUtils.randomString | PBSUtils.randomString | PBSUtils.randomString | BADV new Ortb2BlockingConditions(mediaType: [BANNER]) | PBSUtils.randomString | PBSUtils.randomString | PBSUtils.randomString | BAPP new Ortb2BlockingConditions(mediaType: [BANNER]) | PBSUtils.randomString | PBSUtils.randomString | PBSUtils.randomString | BCAT - new Ortb2BlockingConditions(mediaType: [BANNER]) | PBSUtils.randomNumber | PBSUtils.randomNumber | PBSUtils.randomNumber | BATTR + new Ortb2BlockingConditions(mediaType: [BANNER]) | PBSUtils.randomNumber | PBSUtils.randomNumber | PBSUtils.randomNumber | BANNER_BATTR + new Ortb2BlockingConditions(mediaType: [VIDEO]) | PBSUtils.randomNumber | PBSUtils.randomNumber | PBSUtils.randomNumber | VIDEO_BATTR + new Ortb2BlockingConditions(mediaType: [AUDIO]) | PBSUtils.randomNumber | PBSUtils.randomNumber | PBSUtils.randomNumber | AUDIO_BATTR } def "PBS should prefer non wildcard override when multiple match same condition by bidder"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(attributeName) and: "Account in the DB with blocking configuration" def firstOrtb2BlockingOverride = new Ortb2BlockingOverride(override: [firstOverrideAttributes], conditions: new Ortb2BlockingConditions(bidders: [BidderName.WILDCARD])) @@ -1055,16 +1230,18 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { PBSUtils.randomString | PBSUtils.randomString | PBSUtils.randomString | BADV PBSUtils.randomString | PBSUtils.randomString | PBSUtils.randomString | BAPP PBSUtils.randomString | PBSUtils.randomString | PBSUtils.randomString | BCAT - PBSUtils.randomNumber | PBSUtils.randomNumber | PBSUtils.randomNumber | BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | PBSUtils.randomNumber | BANNER_BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | PBSUtils.randomNumber | VIDEO_BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | PBSUtils.randomNumber | AUDIO_BATTR } def "PBS should prefer non wildcard override when multiple match same condition by media type"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(attributeName) and: "Account in the DB with blocking configuration" def firstOrtb2BlockingOverride = new Ortb2BlockingOverride(override: [firstOverrideAttributes], conditions: new Ortb2BlockingConditions(mediaType: [MediaType.WILDCARD])) - def secondOrtb2BlockingOverride = new Ortb2BlockingOverride(override: [secondOverrideAttributes], conditions: new Ortb2BlockingConditions(mediaType: [BANNER])) + def secondOrtb2BlockingOverride = new Ortb2BlockingOverride(override: [secondOverrideAttributes], conditions: new Ortb2BlockingConditions(mediaType: [bidRequest.imp[0].mediaTypes[0]])) def ortb2BlockingAttributeConfig = Ortb2BlockingAttributeConfig.getDefaultConfig([ortb2Attributes], attributeName).tap { enforceBlocks = true actionOverrides = Ortb2BlockingActionOverride.getDefaultOverride(attributeName, [firstOrtb2BlockingOverride, secondOrtb2BlockingOverride], null) @@ -1096,12 +1273,14 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { PBSUtils.randomString | PBSUtils.randomString | PBSUtils.randomString | BADV PBSUtils.randomString | PBSUtils.randomString | PBSUtils.randomString | BAPP PBSUtils.randomString | PBSUtils.randomString | PBSUtils.randomString | BCAT - PBSUtils.randomNumber | PBSUtils.randomNumber | PBSUtils.randomNumber | BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | PBSUtils.randomNumber | BANNER_BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | PBSUtils.randomNumber | VIDEO_BATTR + PBSUtils.randomNumber | PBSUtils.randomNumber | PBSUtils.randomNumber | AUDIO_BATTR } def "PBS should merge allowed bundle for deals overrides together"() { - given: "Default bidRequest" - def bidRequest = BidRequest.defaultBidRequest + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(attributeName) and: "Account in the DB with blocking configuration" def dealId = PBSUtils.randomNumber @@ -1138,11 +1317,16 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { ortb2Attributes | attributeName [PBSUtils.randomString, PBSUtils.randomString] | BADV [PBSUtils.randomString, PBSUtils.randomString] | BCAT - [PBSUtils.randomNumber, PBSUtils.randomNumber] | BATTR + [PBSUtils.randomNumber, PBSUtils.randomNumber] | BANNER_BATTR + [PBSUtils.randomNumber, PBSUtils.randomNumber] | VIDEO_BATTR + [PBSUtils.randomNumber, PBSUtils.randomNumber] | AUDIO_BATTR } def "PBS should not be override from config when ortb2 attribute present in incoming request"() { - given: "Account in the DB with blocking configuration" + given: "Default bid request with proper ortb attribute" + def bidRequest = getBidRequestForOrtbAttribute(attributeName, bidRequestAttribute) + + and: "Account in the DB with blocking configuration" def account = getAccountWithOrtb2BlockingConfig(bidRequest.accountId, [ortb2Attributes], attributeName) accountDao.save(account) @@ -1166,17 +1350,19 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { assert !response?.ext?.prebid?.modules?.warnings where: - bidRequest | ortb2Attributes | attributeName - BidRequest.defaultBidRequest.tap { badv = [PBSUtils.randomString] } | PBSUtils.randomString | BADV - BidRequest.defaultBidRequest.tap { bapp = [PBSUtils.randomString] } | PBSUtils.randomString | BAPP - BidRequest.defaultBidRequest.tap { bcat = [PBSUtils.randomString] } | PBSUtils.randomString | BCAT - BidRequest.defaultBidRequest.tap { imp[0].banner.battr = [PBSUtils.randomNumber] } | PBSUtils.randomNumber | BATTR - BidRequest.defaultBidRequest.tap { imp[0].banner.btype = [PBSUtils.randomNumber] } | PBSUtils.randomNumber | BTYPE + bidRequestAttribute | ortb2Attributes | attributeName + [PBSUtils.randomString] | PBSUtils.randomString | BADV + [PBSUtils.randomString] | PBSUtils.randomString | BAPP + [PBSUtils.randomString] | PBSUtils.randomString | BCAT + [PBSUtils.randomNumber] | PBSUtils.randomNumber | BANNER_BATTR + [PBSUtils.randomNumber] | PBSUtils.randomNumber | VIDEO_BATTR + [PBSUtils.randomNumber] | PBSUtils.randomNumber | AUDIO_BATTR + [PBSUtils.randomNumber] | PBSUtils.randomNumber | BTYPE } def "PBS should populate seatNonBid when returnAllBidStatus=true and requested bidder responded with rejected advertiser blocked status code"() { given: "Default bidRequest with returnAllBidStatus attribute" - def bidRequest = BidRequest.defaultBidRequest.tap { + def bidRequest = getBidRequestForOrtbAttribute(BADV).tap { it.ext.prebid.returnAllBidStatus = true } @@ -1217,6 +1403,41 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { new Account(uuid: accountId, config: accountConfig) } + private static BidRequest getBidRequestForOrtbAttribute(Ortb2BlockingAttribute attribute, List attributeValue = null) { + switch (attribute) { + case BADV: + return BidRequest.defaultBidRequest.tap { + badv = attributeValue as List + } + case BAPP: + return BidRequest.defaultBidRequest.tap { + bapp = attributeValue as List + } + case BANNER_BATTR: + return BidRequest.defaultBidRequest.tap { + imp[0].banner.battr = attributeValue as List + } + case VIDEO_BATTR: + return BidRequest.defaultVideoRequest.tap { + imp[0].video.battr = attributeValue as List + } + case AUDIO_BATTR: + return BidRequest.defaultAudioRequest.tap { + imp[0].audio.battr = attributeValue as List + } + case BCAT: + return BidRequest.defaultBidRequest.tap { + bcat = attributeValue as List + } + case BTYPE: + return BidRequest.defaultBidRequest.tap { + imp[0].banner.btype = attributeValue as List + } + default: + throw new IllegalArgumentException("Unknown ortb2 attribute: $attribute") + } + } + private static Bid getBidWithOrtb2Attribute(Imp imp, Object ortb2Attributes, Ortb2BlockingAttribute attributeName) { Bid.getDefaultBid(imp).tap { switch (attributeName) { @@ -1226,7 +1447,13 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { case BAPP: bundle = (ortb2Attributes instanceof List) ? ortb2Attributes.first : ortb2Attributes break - case BATTR: + case BANNER_BATTR: + attr = (ortb2Attributes instanceof List) ? ortb2Attributes : [ortb2Attributes] + break + case VIDEO_BATTR: + attr = (ortb2Attributes instanceof List) ? ortb2Attributes : [ortb2Attributes] + break + case AUDIO_BATTR: attr = (ortb2Attributes instanceof List) ? ortb2Attributes : [ortb2Attributes] break case BCAT: @@ -1246,8 +1473,12 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { return bidRequest.badv case BAPP: return bidRequest.bapp - case BATTR: + case BANNER_BATTR: return bidRequest.imp[0].banner.battr*.toString() + case VIDEO_BATTR: + return bidRequest.imp[0].video.battr*.toString() + case AUDIO_BATTR: + return bidRequest.imp[0].audio.battr*.toString() case BCAT: return bidRequest.bcat case BTYPE: @@ -1263,7 +1494,11 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { return bid.adomain case BAPP: return [bid.bundle] - case BATTR: + case BANNER_BATTR: + return bid.attr*.toString() + case VIDEO_BATTR: + return bid.attr*.toString() + case AUDIO_BATTR: return bid.attr*.toString() case BCAT: return bid.cat diff --git a/src/test/java/org/prebid/server/privacy/gdpr/vendorlist/VersionedVendorListServiceTest.java b/src/test/java/org/prebid/server/privacy/gdpr/vendorlist/VersionedVendorListServiceTest.java index c6ae182fabc..8437698b9f0 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/vendorlist/VersionedVendorListServiceTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/vendorlist/VersionedVendorListServiceTest.java @@ -47,7 +47,7 @@ public void versionedVendorListServiceShouldTreatTcfPolicyLessThanFourAsVendorLi @Test public void versionedVendorListServiceShouldTreatTcfPolicyGreaterOrEqualFourAsVendorListSpecificationThree() { // given - final int tcfPolicyVersion = ThreadLocalRandom.current().nextInt(4, 100); + final int tcfPolicyVersion = ThreadLocalRandom.current().nextInt(4, 64); final TCString consent = TCStringEncoder.newBuilder() .version(2) .tcfPolicyVersion(tcfPolicyVersion)