From 653c1c34d098d0bab9106c9df8ae05b2daec61c5 Mon Sep 17 00:00:00 2001 From: antonbabak Date: Mon, 17 Feb 2025 15:02:53 +0100 Subject: [PATCH 1/5] Support seatnonbid Codes in Modules --- ...alTimeDataProcessedAuctionRequestHook.java | 31 ++ ...meDataProcessedAuctionRequestHookTest.java | 8 + .../ortb2/blocking/core/BidsBlocker.java | 21 +- .../blocking/core/model/ExecutionResult.java | 3 + .../Ortb2BlockingRawBidderResponseHook.java | 6 +- .../ortb2/blocking/core/BidsBlockerTest.java | 135 +++---- .../Ortb2BlockingBidderRequestHookTest.java | 14 +- ...rtb2BlockingRawBidderResponseHookTest.java | 49 +-- .../v1/model/BidderInvocationContextImpl.java | 4 - .../filter/core/BidResponsesMraidFilter.java | 9 +- .../filter/model/AnalyticsResult.java | 3 + ...diaFilterAllProcessedBidResponsesHook.java | 22 +- .../core/BidResponsesMraidFilterTest.java | 33 +- ...ilterAllProcessedBidResponsesHookTest.java | 34 +- .../greenbids/GreenbidsAnalyticsReporter.java | 4 +- .../server/auction/BidResponseCreator.java | 4 +- .../prebid/server/auction/DsaEnforcer.java | 3 +- .../server/auction/ExchangeService.java | 43 +- .../auction/model/BidRejectionReason.java | 5 + .../auction/model/BidRejectionTracker.java | 63 ++- .../prebid/server/auction/model/Rejected.java | 9 + .../server/auction/model/RejectedBid.java | 26 ++ .../server/auction/model/RejectedImp.java | 14 + .../requestfactory/Ortb2RequestFactory.java | 2 +- .../server/bidder/HttpBidderRequester.java | 10 +- .../floors/BasicPriceFloorEnforcer.java | 3 +- .../server/hooks/execution/GroupResult.java | 8 + .../hooks/execution/HookStageExecutor.java | 69 +++- .../server/hooks/execution/StageExecutor.java | 2 +- .../server/hooks/execution/StageResult.java | 11 + .../model/HookStageExecutionResult.java | 16 +- .../execution/provider/abtest/ABTestHook.java | 7 + .../execution/v1/InvocationResultImpl.java | 4 + .../server/hooks/v1/InvocationResult.java | 4 + .../spring/config/HooksConfiguration.java | 6 +- .../java/org/prebid/server/util/MapUtil.java | 14 + .../validation/ResponseBidValidator.java | 3 +- .../GreenbidsAnalyticsReporterTest.java | 15 +- .../auction/BidResponseCreatorTest.java | 18 +- .../server/auction/DsaEnforcerTest.java | 17 +- .../server/auction/ExchangeServiceTest.java | 68 +++- .../model/BidRejectionTrackerTest.java | 143 ++++--- .../Ortb2RequestFactoryTest.java | 26 +- .../bidder/HttpBidderRequesterTest.java | 29 +- .../floors/BasicPriceFloorEnforcerTest.java | 3 +- .../handler/openrtb2/AmpHandlerTest.java | 3 +- .../handler/openrtb2/AuctionHandlerTest.java | 3 +- .../handler/openrtb2/VideoHandlerTest.java | 3 +- .../execution/HookStageExecutorTest.java | 378 ++++++++++++++++-- .../hooks/v1/InvocationResultUtils.java | 14 + .../validation/ResponseBidValidatorTest.java | 20 +- 51 files changed, 1018 insertions(+), 424 deletions(-) create mode 100644 src/main/java/org/prebid/server/auction/model/Rejected.java create mode 100644 src/main/java/org/prebid/server/auction/model/RejectedBid.java create mode 100644 src/main/java/org/prebid/server/auction/model/RejectedImp.java diff --git a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHook.java b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHook.java index 5188b756ddc..82760d4c8d6 100644 --- a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHook.java +++ b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHook.java @@ -7,8 +7,14 @@ import com.iab.openrtb.request.BidRequest; import io.vertx.core.Future; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.prebid.server.analytics.reporter.greenbids.model.ExplorationResult; import org.prebid.server.analytics.reporter.greenbids.model.Ortb2ImpExtResult; import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.auction.model.BidRejectionReason; +import org.prebid.server.auction.model.Rejected; +import org.prebid.server.auction.model.RejectedImp; import org.prebid.server.exception.PreBidException; import org.prebid.server.hooks.execution.v1.InvocationResultImpl; import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; @@ -38,11 +44,15 @@ import org.prebid.server.settings.model.Account; import org.prebid.server.settings.model.AccountHooksConfiguration; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class GreenbidsRealTimeDataProcessedAuctionRequestHook implements ProcessedAuctionRequestHook { @@ -166,6 +176,7 @@ private InvocationResult toInvocationResult( .action(action) .payloadUpdate(payload -> AuctionRequestPayloadImpl.of(bidRequest)) .analyticsTags(toAnalyticsTags(analyticsResults)) + .rejections(toRejections(analyticsResult)) .build(); default -> InvocationResultImpl .builder() @@ -207,6 +218,26 @@ private ObjectNode toObjectNode(Map values) { return values != null ? mapper.valueToTree(values) : null; } + private Map> toRejections(AnalyticsResult analyticsResult) { + if (analyticsResult == null) { + return null; + } + + return analyticsResult.getValues().entrySet().stream() + .flatMap(entry -> + Stream.ofNullable(entry.getValue()) + .map(Ortb2ImpExtResult::getGreenbids) + .map(ExplorationResult::getKeptInAuction) + .map(Map::entrySet) + .flatMap(Collection::stream) + .filter(e -> BooleanUtils.isFalse(e.getValue())) + .map(Map.Entry::getKey) + .map(bidder -> Pair.of( + bidder, + RejectedImp.of(entry.getKey(), BidRejectionReason.REQUEST_BLOCKED_OPTIMIZED)))) + .collect(Collectors.groupingBy(Pair::getKey, Collectors.mapping(Pair::getValue, Collectors.toList()))); + } + @Override public String code() { return CODE; diff --git a/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHookTest.java b/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHookTest.java index 07f13d519f4..57b277937d0 100644 --- a/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHookTest.java +++ b/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHookTest.java @@ -25,6 +25,8 @@ import org.prebid.server.analytics.reporter.greenbids.model.ExplorationResult; import org.prebid.server.analytics.reporter.greenbids.model.Ortb2ImpExtResult; import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.auction.model.BidRejectionReason; +import org.prebid.server.auction.model.RejectedImp; import org.prebid.server.geolocation.CountryCodeMapper; import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; import org.prebid.server.hooks.execution.v1.analytics.AppliedToImpl; @@ -66,10 +68,12 @@ import static java.util.function.UnaryOperator.identity; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mock.Strictness.LENIENT; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.prebid.server.auction.model.BidRejectionReason.*; import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.givenBanner; import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.givenBidRequest; import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.givenBidRequestWithExtension; @@ -390,6 +394,10 @@ public void callShouldFilterBiddersBasedOnModelResults() throws OrtException, IO assertThat(fingerprint).isNotNull(); assertThat(resultBidRequest).usingRecursiveComparison() .isEqualTo(expectedBidRequest); + assertThat(result.rejections()).containsOnly( + entry("appnexus", List.of(RejectedImp.of("adunitcodevalue", REQUEST_BLOCKED_OPTIMIZED))), + entry("pubmatic", List.of(RejectedImp.of("adunitcodevalue", REQUEST_BLOCKED_OPTIMIZED))), + entry("rubicon", List.of(RejectedImp.of("adunitcodevalue", REQUEST_BLOCKED_OPTIMIZED)))); } private AuctionContext givenAuctionContext( 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 5435544e2cb..dd6c61043fb 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 @@ -7,6 +7,8 @@ import org.apache.commons.lang3.StringUtils; import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.auction.model.BidRejectionTracker; +import org.prebid.server.auction.model.Rejected; +import org.prebid.server.auction.model.RejectedBid; import org.prebid.server.auction.versionconverter.OrtbVersion; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.hooks.modules.ortb2.blocking.core.exception.InvalidAccountConfigurationException; @@ -49,7 +51,6 @@ public class BidsBlocker { private final OrtbVersion ortbVersion; private final ObjectNode accountConfig; private final BlockedAttributes blockedAttributes; - private final BidRejectionTracker bidRejectionTracker; private final boolean debugEnabled; private BidsBlocker(List bids, @@ -57,7 +58,6 @@ private BidsBlocker(List bids, OrtbVersion ortbVersion, ObjectNode accountConfig, BlockedAttributes blockedAttributes, - BidRejectionTracker bidRejectionTracker, boolean debugEnabled) { this.bids = bids; @@ -65,7 +65,6 @@ private BidsBlocker(List bids, this.ortbVersion = ortbVersion; this.accountConfig = accountConfig; this.blockedAttributes = blockedAttributes; - this.bidRejectionTracker = bidRejectionTracker; this.debugEnabled = debugEnabled; } @@ -74,7 +73,6 @@ public static BidsBlocker create(List bids, OrtbVersion ortbVersion, ObjectNode accountConfig, BlockedAttributes blockedAttributes, - BidRejectionTracker bidRejectionTracker, boolean debugEnabled) { return new BidsBlocker( @@ -83,7 +81,6 @@ public static BidsBlocker create(List bids, Objects.requireNonNull(ortbVersion), accountConfig, blockedAttributes, - bidRejectionTracker, debugEnabled); } @@ -104,9 +101,10 @@ public ExecutionResult block() { final BlockedBids blockedBids = !blockedBidIndexes.isEmpty() ? BlockedBids.of(blockedBidIndexes) : null; final List warnings = MergeUtils.mergeMessages(blockedBidResults); + final List rejectedBids = new ArrayList<>(); if (blockedBids != null) { blockedBidIndexes.forEach(index -> - rejectBlockedBid(blockedBidResults.get(index).getValue(), bids.get(index))); + rejectBlockedBid(rejectedBids, blockedBidResults.get(index).getValue(), bids.get(index))); } return ExecutionResult.builder() @@ -114,6 +112,7 @@ public ExecutionResult block() { .debugMessages(blockedBids != null ? debugMessages(blockedBidIndexes, blockedBidResults) : null) .warnings(warnings) .analyticsResults(toAnalyticsResults(blockedBidResults)) + .rejections(rejectedBids) .build(); } catch (InvalidAccountConfigurationException e) { return debugEnabled ? ExecutionResult.withError(e.getMessage()) : ExecutionResult.empty(); @@ -288,20 +287,20 @@ private String debugEntryFor(int index, BlockingResult blockingResult) { blockingResult.getFailedChecks()); } - private void rejectBlockedBid(BlockingResult blockingResult, BidderBid blockedBid) { + private void rejectBlockedBid(List rejections, BlockingResult blockingResult, BidderBid blockedBid) { if (blockingResult.getBattrCheckResult().isFailed() || blockingResult.getBappCheckResult().isFailed() || blockingResult.getBcatCheckResult().isFailed()) { - bidRejectionTracker.rejectBid( + rejections.add(RejectedBid.of( blockedBid, - BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE); + BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE)); } if (blockingResult.getBadvCheckResult().isFailed()) { - bidRejectionTracker.rejectBid( + rejections.add(RejectedBid.of( blockedBid, - BidRejectionReason.RESPONSE_REJECTED_ADVERTISER_BLOCKED); + BidRejectionReason.RESPONSE_REJECTED_ADVERTISER_BLOCKED)); } } diff --git a/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/model/ExecutionResult.java b/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/model/ExecutionResult.java index 5765dfb5452..2eff89aefde 100644 --- a/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/model/ExecutionResult.java +++ b/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/model/ExecutionResult.java @@ -2,6 +2,7 @@ import lombok.Builder; import lombok.Value; +import org.prebid.server.auction.model.Rejected; import java.util.Collections; import java.util.List; @@ -22,6 +23,8 @@ public class ExecutionResult { List analyticsResults; + List rejections; + public boolean hasValue() { return value != null; } diff --git a/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingRawBidderResponseHook.java b/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingRawBidderResponseHook.java index 720823f4513..94271ff7ca5 100644 --- a/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingRawBidderResponseHook.java +++ b/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingRawBidderResponseHook.java @@ -59,7 +59,6 @@ public Future> call(BidderResponsePayloa ObjectUtils.defaultIfNull(moduleContext.ortbVersionOf(bidder), OrtbVersion.ORTB_2_5), invocationContext.accountConfig(), moduleContext.blockedAttributesFor(bidder), - invocationContext.auctionContext().getBidRejectionTrackers().get(bidder), invocationContext.debugEnabled()) .block(); @@ -73,7 +72,10 @@ public Future> call(BidderResponsePayloa .errors(blockedBidsResult.getErrors()) .warnings(blockedBidsResult.getWarnings()) .debugMessages(blockedBidsResult.getDebugMessages()) - .analyticsTags(toAnalyticsTags(blockedBidsResult.getAnalyticsResults())); + .analyticsTags(toAnalyticsTags(blockedBidsResult.getAnalyticsResults())) + .rejections(CollectionUtils.isEmpty(blockedBidsResult.getRejections()) + ? null + : Map.of(bidder, blockedBidsResult.getRejections())); if (blockedBidsResult.hasValue()) { final ResponseUpdater responseUpdater = ResponseUpdater.create(blockedBidsResult.getValue()); 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 b595b9c5fdb..48a8ec77651 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 @@ -7,10 +7,9 @@ import com.iab.openrtb.response.Bid; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.prebid.server.auction.model.BidRejectionReason; -import org.prebid.server.auction.model.BidRejectionTracker; +import org.prebid.server.auction.model.RejectedBid; import org.prebid.server.auction.versionconverter.OrtbVersion; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.hooks.modules.ortb2.blocking.core.config.Attribute; @@ -35,10 +34,10 @@ import static java.util.Collections.singletonMap; import static java.util.function.UnaryOperator.identity; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.prebid.server.auction.model.BidRejectionReason.*; +import static org.prebid.server.auction.model.BidRejectionReason.RESPONSE_REJECTED_ADVERTISER_BLOCKED; +import static org.prebid.server.auction.model.BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE; @ExtendWith(MockitoExtension.class) public class BidsBlockerTest { @@ -49,18 +48,14 @@ public class BidsBlockerTest { private static final OrtbVersion ORTB_VERSION = OrtbVersion.ORTB_2_5; - @Mock - private BidRejectionTracker bidRejectionTracker; - @Test public void shouldReturnEmptyResultWhenNoBlockingResponseConfig() { // given final List bids = singletonList(bid()); - final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, null, null, bidRejectionTracker, true); + final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, null, null, true); // when and then assertThat(blocker.block()).satisfies(BidsBlockerTest::isEmpty); - verifyNoInteractions(bidRejectionTracker); } @Test @@ -70,13 +65,12 @@ public void shouldReturnEmptyResultWithErrorWhenInvalidAccountConfig() { .put("attributes", 1); final List bids = singletonList(bid()); - final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, null, bidRejectionTracker, true); + final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, null, true); // when and then assertThat(blocker.block()).isEqualTo(ExecutionResult.builder() .errors(singletonList("attributes field in account configuration is not an object")) .build()); - verifyNoInteractions(bidRejectionTracker); } @Test @@ -86,11 +80,11 @@ public void shouldReturnEmptyResultWithoutErrorWhenInvalidAccountConfigAndDebugD .put("attributes", 1); final List bids = singletonList(bid()); - final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, null, bidRejectionTracker, false); + final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, null, false); // when and then assertThat(blocker.block()).isEqualTo(ExecutionResult.empty()); - verifyNoInteractions(bidRejectionTracker); + } @Test @@ -105,11 +99,11 @@ public void shouldReturnEmptyResultWhenBidWithoutAdomainAndBlockUnknownFalse() { // when final List bids = singletonList(bid()); - final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, null, bidRejectionTracker, true); + final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, null, true); // when and then assertThat(blocker.block()).satisfies(BidsBlockerTest::isEmpty); - verifyNoInteractions(bidRejectionTracker); + } @Test @@ -124,11 +118,11 @@ public void shouldReturnEmptyResultWhenBidWithoutAdomainAndEnforceBlocksFalseAnd // when final List bids = singletonList(bid()); - final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, null, bidRejectionTracker, true); + final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, null, true); // when and then assertThat(blocker.block()).satisfies(BidsBlockerTest::isEmpty); - verifyNoInteractions(bidRejectionTracker); + } @Test @@ -143,7 +137,7 @@ public void shouldReturnResultWithBidWhenBidWithoutAdomainAndBlockUnknownTrue() // when final List bids = singletonList(bid()); - final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, null, bidRejectionTracker, false); + final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, null, false); // when and then assertThat(blocker.block()).satisfies(result -> hasValue(result, 0)); @@ -161,11 +155,11 @@ public void shouldReturnEmptyResultWhenBidWithBlockedAdomainAndEnforceBlocksFals // when final List bids = singletonList(bid(bid -> bid.adomain(singletonList("domain1.com")))); final BlockedAttributes blockedAttributes = attributesWithBadv(singletonList("domain1.com")); - final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, bidRejectionTracker, true); + final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, true); // when and then assertThat(blocker.block()).satisfies(BidsBlockerTest::isEmpty); - verifyNoInteractions(bidRejectionTracker); + } @Test @@ -180,11 +174,11 @@ public void shouldReturnEmptyResultWhenBidWithNotBlockedAdomain() { // when final List bids = singletonList(bid(bid -> bid.adomain(singletonList("domain1.com")))); final BlockedAttributes blockedAttributes = attributesWithBadv(singletonList("domain2.com")); - final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, bidRejectionTracker, true); + final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, true); // when and then assertThat(blocker.block()).satisfies(BidsBlockerTest::isEmpty); - verifyNoInteractions(bidRejectionTracker); + } @Test @@ -199,11 +193,13 @@ public void shouldReturnResultWithBidWhenBidWithBlockedAdomainAndEnforceBlocksTr // when final BidderBid bid = bid(bidBuilder -> bidBuilder.adomain(singletonList("domain1.com"))); final BlockedAttributes blockedAttributes = attributesWithBadv(singletonList("domain1.com")); - final BidsBlocker blocker = BidsBlocker.create(singletonList(bid), "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, bidRejectionTracker, false); + final BidsBlocker blocker = BidsBlocker.create(singletonList(bid), "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, false); // when and then - assertThat(blocker.block()).satisfies(result -> hasValue(result, 0)); - verify(bidRejectionTracker).rejectBid(bid, BidRejectionReason.RESPONSE_REJECTED_ADVERTISER_BLOCKED); + assertThat(blocker.block()).satisfies(result -> { + hasValue(result, 0); + assertThat(result.getRejections()).containsOnly(RejectedBid.of(bid, RESPONSE_REJECTED_ADVERTISER_BLOCKED)); + }); } @Test @@ -217,11 +213,11 @@ public void shouldReturnEmptyResultWhenBidWithAdomainAndNoBlockedAttributes() { // when final List bids = singletonList(bid(bid -> bid.adomain(singletonList("domain1.com")))); - final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, null, bidRejectionTracker, true); + final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, null, true); // when and then assertThat(blocker.block()).satisfies(BidsBlockerTest::isEmpty); - verifyNoInteractions(bidRejectionTracker); + } @Test @@ -240,11 +236,11 @@ public void shouldReturnEmptyResultWhenBidWithAttrAndNoBlockedBannerAttrForImp() final BlockedAttributes blockedAttributes = BlockedAttributes.builder() .battr(singletonMap(MediaType.BANNER, singletonMap("impId1", asList(1, 2)))) .build(); - final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, bidRejectionTracker, true); + final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, true); // when and then assertThat(blocker.block()).satisfies(BidsBlockerTest::isEmpty); - verifyNoInteractions(bidRejectionTracker); + } @Test @@ -263,11 +259,11 @@ public void shouldReturnEmptyResultWhenBidWithAttrAndNoBlockedVideoAttrForImp() 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); + final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, true); // when and then assertThat(blocker.block()).satisfies(BidsBlockerTest::isEmpty); - verifyNoInteractions(bidRejectionTracker); + } @Test @@ -286,11 +282,11 @@ public void shouldReturnEmptyResultWhenBidWithAttrAndNoBlockedAudioAttrForImp() 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); + final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, true); // when and then assertThat(blocker.block()).satisfies(BidsBlockerTest::isEmpty); - verifyNoInteractions(bidRejectionTracker); + } @Test @@ -306,11 +302,11 @@ public void shouldReturnEmptyResultWhenBidWithBlockedAdomainAndInDealsExceptions // when final BidderBid bid = bid(bidBuilder -> bidBuilder.adomain(singletonList("domain1.com"))); final BlockedAttributes blockedAttributes = attributesWithBadv(singletonList("domain1.com")); - final BidsBlocker blocker = BidsBlocker.create(singletonList(bid), "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, bidRejectionTracker, true); + final BidsBlocker blocker = BidsBlocker.create(singletonList(bid), "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, true); // when and then assertThat(blocker.block()).satisfies(BidsBlockerTest::isEmpty); - verifyNoInteractions(bidRejectionTracker); + } @Test @@ -326,11 +322,13 @@ public void shouldReturnResultWithBidWhenBidWithBlockedAdomainAndNotInDealsExcep // when final BidderBid bid = bid(bidBuilder -> bidBuilder.adomain(singletonList("domain1.com"))); final BlockedAttributes blockedAttributes = attributesWithBadv(singletonList("domain1.com")); - final BidsBlocker blocker = BidsBlocker.create(singletonList(bid), "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, bidRejectionTracker, false); + final BidsBlocker blocker = BidsBlocker.create(singletonList(bid), "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, false); // when and then - assertThat(blocker.block()).satisfies(result -> hasValue(result, 0)); - verify(bidRejectionTracker).rejectBid(bid, BidRejectionReason.RESPONSE_REJECTED_ADVERTISER_BLOCKED); + assertThat(blocker.block()).satisfies(result -> { + hasValue(result, 0); + assertThat(result.getRejections()).containsOnly(RejectedBid.of(bid, RESPONSE_REJECTED_ADVERTISER_BLOCKED)); + }); } @Test @@ -345,15 +343,15 @@ public void shouldReturnResultWithBidAndDebugMessageWhenBidIsBlocked() { // when final BidderBid bid = bid(); - final BidsBlocker blocker = BidsBlocker.create(singletonList(bid), "bidder1", ORTB_VERSION, accountConfig, null, bidRejectionTracker, true); + final BidsBlocker blocker = BidsBlocker.create(singletonList(bid), "bidder1", ORTB_VERSION, accountConfig, null, true); // when and then assertThat(blocker.block()).satisfies(result -> { assertThat(result.getValue()).isEqualTo(BlockedBids.of(singleton(0))); assertThat(result.getDebugMessages()).containsOnly( "Bid 0 from bidder bidder1 has been rejected, failed checks: [bcat]"); + assertThat(result.getRejections()).containsOnly(RejectedBid.of(bid, RESPONSE_REJECTED_INVALID_CREATIVE)); }); - verify(bidRejectionTracker).rejectBid(bid, BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE); } @Test @@ -368,11 +366,13 @@ public void shouldReturnResultWithBidWithoutDebugMessageWhenBidIsBlockedAndDebug // when final BidderBid bid = bid(); - final BidsBlocker blocker = BidsBlocker.create(singletonList(bid), "bidder1", ORTB_VERSION, accountConfig, null, bidRejectionTracker, false); + final BidsBlocker blocker = BidsBlocker.create(singletonList(bid), "bidder1", ORTB_VERSION, accountConfig, null, false); // when and then - assertThat(blocker.block()).satisfies(result -> hasValue(result, 0)); - verify(bidRejectionTracker).rejectBid(bid, BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE); + assertThat(blocker.block()).satisfies(result -> { + hasValue(result, 0); + assertThat(result.getRejections()).containsOnly(RejectedBid.of(bid, RESPONSE_REJECTED_INVALID_CREATIVE)); + }); } @Test @@ -416,7 +416,7 @@ public void shouldReturnResultWithAnalyticsResults() { .bapp(asList("app1", "app2", "app3")) .battr(singletonMap(MediaType.BANNER, singletonMap("impId2", asList(1, 2, 3)))) .build(); - final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, bidRejectionTracker, true); + final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, true); // when and then assertThat(blocker.block()).satisfies(result -> { @@ -435,12 +435,12 @@ public void shouldReturnResultWithAnalyticsResults() { AnalyticsResult.of("success-blocked", analyticsResultValues1, "bidder1", "impId1"), AnalyticsResult.of("success-blocked", analyticsResultValues2, "bidder1", "impId2"), AnalyticsResult.of("success-allow", null, "bidder1", "impId1")); - }); - verify(bidRejectionTracker).rejectBid(bid1, BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE); - verify(bidRejectionTracker).rejectBid(bid2, BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE); - verify(bidRejectionTracker).rejectBid(bid1, BidRejectionReason.RESPONSE_REJECTED_ADVERTISER_BLOCKED); - verifyNoMoreInteractions(bidRejectionTracker); + assertThat(result.getRejections()).containsOnly( + RejectedBid.of(bid1, RESPONSE_REJECTED_INVALID_CREATIVE), + RejectedBid.of(bid2, RESPONSE_REJECTED_INVALID_CREATIVE), + RejectedBid.of(bid1, RESPONSE_REJECTED_ADVERTISER_BLOCKED)); + }); } @Test @@ -498,7 +498,7 @@ public void shouldReturnResultWithoutSomeBidsWhenAllAttributesInConfig() { .bapp(asList("app1", "app2")) .battr(singletonMap(MediaType.BANNER, singletonMap("impId1", asList(1, 2)))) .build(); - final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, bidRejectionTracker, true); + final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, true); // when and then assertThat(blocker.block()).satisfies(result -> { @@ -509,16 +509,15 @@ public void shouldReturnResultWithoutSomeBidsWhenAllAttributesInConfig() { "Bid 3 from bidder bidder1 has been rejected, failed checks: [bapp]", "Bid 5 from bidder bidder1 has been rejected, failed checks: [battr]", "Bid 7 from bidder bidder1 has been rejected, failed checks: [badv, bcat]"); + assertThat(result.getRejections()).containsOnly( + RejectedBid.of(bid1, RESPONSE_REJECTED_INVALID_CREATIVE), + RejectedBid.of(bid1, RESPONSE_REJECTED_ADVERTISER_BLOCKED), + RejectedBid.of(bid2, RESPONSE_REJECTED_INVALID_CREATIVE), + RejectedBid.of(bid4, RESPONSE_REJECTED_INVALID_CREATIVE), + RejectedBid.of(bid6, RESPONSE_REJECTED_INVALID_CREATIVE), + RejectedBid.of(bid8, RESPONSE_REJECTED_INVALID_CREATIVE), + RejectedBid.of(bid8, RESPONSE_REJECTED_ADVERTISER_BLOCKED)); }); - - verify(bidRejectionTracker).rejectBid(bid1, BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE); - verify(bidRejectionTracker).rejectBid(bid1, BidRejectionReason.RESPONSE_REJECTED_ADVERTISER_BLOCKED); - verify(bidRejectionTracker).rejectBid(bid2, BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE); - verify(bidRejectionTracker).rejectBid(bid4, BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE); - verify(bidRejectionTracker).rejectBid(bid6, BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE); - verify(bidRejectionTracker).rejectBid(bid8, BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE); - verify(bidRejectionTracker).rejectBid(bid8, BidRejectionReason.RESPONSE_REJECTED_ADVERTISER_BLOCKED); - verifyNoMoreInteractions(bidRejectionTracker); } @Test @@ -539,14 +538,12 @@ public void shouldReturnEmptyResultForCattaxIfBidderSupportsLowerThan26() { bid(bid -> bid.cattax(3)), bid()); final BlockedAttributes blockedAttributes = BlockedAttributes.builder().build(); - final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, bidRejectionTracker, true); + final BidsBlocker blocker = BidsBlocker.create(bids, "bidder1", ORTB_VERSION, accountConfig, blockedAttributes, true); // when and then assertThat(blocker.block()) .extracting(ExecutionResult::getValue) .isNull(); - - verifyNoInteractions(bidRejectionTracker); } @Test @@ -563,14 +560,12 @@ public void shouldPassBidIfCattaxIsNull() { final List bids = singletonList(bid()); final BlockedAttributes blockedAttributes = BlockedAttributes.builder().build(); final BidsBlocker blocker = BidsBlocker.create( - bids, "bidder1", OrtbVersion.ORTB_2_6, accountConfig, blockedAttributes, bidRejectionTracker, true); + bids, "bidder1", OrtbVersion.ORTB_2_6, accountConfig, blockedAttributes, true); // when and then assertThat(blocker.block()) .extracting(ExecutionResult::getValue) .isNull(); - - verifyNoInteractions(bidRejectionTracker); } @Test @@ -589,7 +584,7 @@ public void shouldBlockBidIfCattaxNotEqualsAllowedCattax() { bid(bid -> bid.cattax(2))); final BlockedAttributes blockedAttributes = BlockedAttributes.builder().cattaxComplement(2).build(); final BidsBlocker blocker = BidsBlocker.create( - bids, "bidder1", OrtbVersion.ORTB_2_6, accountConfig, blockedAttributes, bidRejectionTracker, true); + bids, "bidder1", OrtbVersion.ORTB_2_6, accountConfig, blockedAttributes, true); // when and then assertThat(blocker.block()).satisfies(result -> { @@ -597,8 +592,6 @@ public void shouldBlockBidIfCattaxNotEqualsAllowedCattax() { assertThat(result.getDebugMessages()).containsExactly( "Bid 0 from bidder bidder1 has been rejected, failed checks: [cattax]"); }); - - verifyNoInteractions(bidRejectionTracker); } @Test @@ -617,7 +610,7 @@ public void shouldBlockBidIfCattaxNotEquals1IfBlockedAttributesCattaxAbsent() { bid(bid -> bid.cattax(2))); final BlockedAttributes blockedAttributes = BlockedAttributes.builder().build(); final BidsBlocker blocker = BidsBlocker.create( - bids, "bidder1", OrtbVersion.ORTB_2_6, accountConfig, blockedAttributes, bidRejectionTracker, true); + bids, "bidder1", OrtbVersion.ORTB_2_6, accountConfig, blockedAttributes, true); // when and then assertThat(blocker.block()).satisfies(result -> { @@ -625,8 +618,6 @@ public void shouldBlockBidIfCattaxNotEquals1IfBlockedAttributesCattaxAbsent() { assertThat(result.getDebugMessages()).containsExactly( "Bid 1 from bidder bidder1 has been rejected, failed checks: [cattax]"); }); - - verifyNoInteractions(bidRejectionTracker); } private static BidderBid bid() { diff --git a/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingBidderRequestHookTest.java b/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingBidderRequestHookTest.java index b2182a92f6e..e26e829e899 100644 --- a/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingBidderRequestHookTest.java +++ b/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingBidderRequestHookTest.java @@ -56,9 +56,6 @@ public class Ortb2BlockingBidderRequestHookTest { @Mock(strictness = Mock.Strictness.LENIENT) private BidderCatalog bidderCatalog; - @Mock(strictness = Mock.Strictness.LENIENT) - private BidRejectionTracker bidRejectionTracker; - private Ortb2BlockingBidderRequestHook hook; @BeforeEach @@ -83,7 +80,6 @@ public void shouldReturnResultWithNoActionWhenNoBlockingAttributes() { BidderInvocationContextImpl.of( "bidder1", Map.of("bidder1", "bidder1Base"), - bidRejectionTracker, null, true)); @@ -105,7 +101,7 @@ public void shouldReturnResultWithNoActionAndErrorWhenInvalidAccountConfig() { // when final Future> result = hook.call( BidderRequestPayloadImpl.of(emptyRequest()), - BidderInvocationContextImpl.of("bidder1", bidRejectionTracker, accountConfig, true)); + BidderInvocationContextImpl.of("bidder1", accountConfig, true)); // then assertThat(result.succeeded()).isTrue(); @@ -126,7 +122,7 @@ public void shouldReturnResultWithNoActionAndNoErrorWhenInvalidAccountConfigAndD // when final Future> result = hook.call( BidderRequestPayloadImpl.of(emptyRequest()), - BidderInvocationContextImpl.of("bidder1", bidRejectionTracker, accountConfig, false)); + BidderInvocationContextImpl.of("bidder1", accountConfig, false)); // then assertThat(result.succeeded()).isTrue(); @@ -152,7 +148,7 @@ public void shouldReturnResultWithModuleContextAndPayloadUpdate() { // when final Future> result = hook.call( BidderRequestPayloadImpl.of(emptyRequest()), - BidderInvocationContextImpl.of("bidder1", bidRejectionTracker, accountConfig, true)); + BidderInvocationContextImpl.of("bidder1", accountConfig, true)); // then assertThat(result.succeeded()).isTrue(); @@ -204,7 +200,7 @@ public void shouldReturnResultWithUpdateActionAndWarning() { // when final Future> result = hook.call( BidderRequestPayloadImpl.of(emptyRequest()), - BidderInvocationContextImpl.of("bidder1", bidRejectionTracker, accountConfig, true)); + BidderInvocationContextImpl.of("bidder1", accountConfig, true)); // then assertThat(result.succeeded()).isTrue(); @@ -241,7 +237,7 @@ public void shouldReturnResultWithUpdateActionAndNoWarningWhenDebugDisabled() { // when final Future> result = hook.call( BidderRequestPayloadImpl.of(emptyRequest()), - BidderInvocationContextImpl.of("bidder1", bidRejectionTracker, accountConfig, false)); + BidderInvocationContextImpl.of("bidder1", accountConfig, false)); // then assertThat(result.succeeded()).isTrue(); diff --git a/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingRawBidderResponseHookTest.java b/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingRawBidderResponseHookTest.java index 351bfbb9d33..fc5e1bbccad 100644 --- a/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingRawBidderResponseHookTest.java +++ b/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingRawBidderResponseHookTest.java @@ -12,6 +12,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.BidRejectionTracker; +import org.prebid.server.auction.model.RejectedBid; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.hooks.execution.v1.InvocationResultImpl; import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; @@ -36,6 +37,7 @@ import org.prebid.server.json.ObjectMapperProvider; import org.prebid.server.proto.openrtb.ext.response.BidType; +import java.util.List; import java.util.Map; import java.util.function.UnaryOperator; @@ -43,7 +45,9 @@ import static java.util.Collections.singletonList; import static java.util.function.UnaryOperator.identity; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.prebid.server.auction.model.BidRejectionReason.RESPONSE_REJECTED_ADVERTISER_BLOCKED; @ExtendWith(MockitoExtension.class) public class Ortb2BlockingRawBidderResponseHookTest { @@ -55,15 +59,12 @@ public class Ortb2BlockingRawBidderResponseHookTest { private final Ortb2BlockingRawBidderResponseHook hook = new Ortb2BlockingRawBidderResponseHook( ObjectMapperProvider.mapper()); - @Mock - private BidRejectionTracker bidRejectionTracker; - @Test public void shouldReturnResultWithNoActionWhenNoBidsBlocked() { // when final Future> result = hook.call( BidderResponsePayloadImpl.of(singletonList(bid())), - BidderInvocationContextImpl.of("bidder1", bidRejectionTracker, null, true)); + BidderInvocationContextImpl.of("bidder1", null, true)); // then assertThat(result.succeeded()).isTrue(); @@ -93,7 +94,7 @@ public void shouldReturnResultWithNoActionAndErrorWhenInvalidAccountConfig() { // when final Future> result = hook.call( BidderResponsePayloadImpl.of(singletonList(bid())), - BidderInvocationContextImpl.of("bidder1", bidRejectionTracker, accountConfig, true)); + BidderInvocationContextImpl.of("bidder1", accountConfig, true)); // then assertThat(result.succeeded()).isTrue(); @@ -114,7 +115,7 @@ public void shouldReturnResultWithNoActionAndNoErrorWhenInvalidAccountConfigAndD // when final Future> result = hook.call( BidderResponsePayloadImpl.of(singletonList(bid())), - BidderInvocationContextImpl.of("bidder1", bidRejectionTracker, accountConfig, false)); + BidderInvocationContextImpl.of("bidder1", accountConfig, false)); // then assertThat(result.succeeded()).isTrue(); @@ -135,18 +136,17 @@ public void shouldReturnResultWithPayloadUpdateAndAnalyticsTags() { .build()) .build())); + final BidderBid bid1 = bid(bid -> bid.id("bidId1")); + final BidderBid bid2 = bid(bid -> bid.id("bidId2").adomain(singletonList("domain1.com"))); + final BidderBid bid3 = bid(bid -> bid.id("bidId2").adomain(singletonList("domain2.com"))); + // when final Future> result = hook.call( - BidderResponsePayloadImpl.of(asList( - bid(), - bid(bid -> bid.adomain(singletonList("domain1.com"))), - bid(bid -> bid.adomain(singletonList("domain2.com"))))), + BidderResponsePayloadImpl.of(asList(bid1, bid2, bid3)), BidderInvocationContextImpl.builder() .bidder("bidder1") .accountConfig(accountConfig) - .auctionContext(AuctionContext.builder() - .bidRejectionTrackers(Map.of("bidder1", bidRejectionTracker)) - .build()) + .auctionContext(AuctionContext.builder().build()) .moduleContext(ModuleContext.create().with( "bidder1", BlockedAttributes.builder().badv(singletonList("domain2.com")).build())) .debugEnabled(true) @@ -163,12 +163,9 @@ public void shouldReturnResultWithPayloadUpdateAndAnalyticsTags() { }); final PayloadUpdate payloadUpdate = invocationResult.payloadUpdate(); - final BidderResponsePayloadImpl payloadToUpdate = BidderResponsePayloadImpl.of(asList( - bid(), - bid(bid -> bid.adomain(singletonList("domain1.com"))), - bid(bid -> bid.adomain(singletonList("domain2.com"))))); + final BidderResponsePayloadImpl payloadToUpdate = BidderResponsePayloadImpl.of(asList(bid1, bid2, bid3)); assertThat(payloadUpdate.apply(payloadToUpdate)).isEqualTo(BidderResponsePayloadImpl.of( - singletonList(bid(bid -> bid.adomain(singletonList("domain1.com")))))); + singletonList(bid2))); assertThat(invocationResult.analyticsTags()).isEqualTo(TagsImpl.of(singletonList(ActivityImpl.of( "enforce-blocking", @@ -202,6 +199,12 @@ public void shouldReturnResultWithPayloadUpdateAndAnalyticsTags() { .bidders(singletonList("bidder1")) .impIds(singletonList("impId1")) .build())))))); + + assertThat(invocationResult.rejections()).containsOnly(entry("bidder1", List.of( + RejectedBid.of(bid1, + RESPONSE_REJECTED_ADVERTISER_BLOCKED), + RejectedBid.of(bid(bid -> bid.id("bidId2").adomain(singletonList("domain2.com"))), + RESPONSE_REJECTED_ADVERTISER_BLOCKED)))); } @Test @@ -224,7 +227,7 @@ public void shouldReturnResultWithUpdateActionAndWarning() { // when final Future> result = hook.call( BidderResponsePayloadImpl.of(singletonList(bid())), - BidderInvocationContextImpl.of("bidder1", bidRejectionTracker, accountConfig, true)); + BidderInvocationContextImpl.of("bidder1", accountConfig, true)); // then assertThat(result.succeeded()).isTrue(); @@ -258,7 +261,7 @@ public void shouldReturnResultWithUpdateActionAndNoWarningWhenDebugDisabled() { // when final Future> result = hook.call( BidderResponsePayloadImpl.of(singletonList(bid())), - BidderInvocationContextImpl.of("bidder1", bidRejectionTracker, accountConfig, false)); + BidderInvocationContextImpl.of("bidder1", accountConfig, false)); // then assertThat(result.succeeded()).isTrue(); @@ -284,7 +287,7 @@ public void shouldReturnResultWithUpdateActionAndDebugMessage() { // when final Future> result = hook.call( BidderResponsePayloadImpl.of(singletonList(bid())), - BidderInvocationContextImpl.of("bidder1", bidRejectionTracker, accountConfig, true)); + BidderInvocationContextImpl.of("bidder1", accountConfig, true)); // then assertThat(result.succeeded()).isTrue(); @@ -310,7 +313,7 @@ public void shouldReturnResultWithUpdateActionAndNoDebugMessageWhenDebugDisabled // when final Future> result = hook.call( BidderResponsePayloadImpl.of(singletonList(bid())), - BidderInvocationContextImpl.of("bidder1", bidRejectionTracker, accountConfig, false)); + BidderInvocationContextImpl.of("bidder1", accountConfig, false)); // then assertThat(result.succeeded()).isTrue(); @@ -323,7 +326,7 @@ public void shouldReturnResultWithUpdateActionAndNoDebugMessageWhenDebugDisabled } private static BidderBid bid() { - return bid(identity()); + return bid(bid -> bid.id("bidId")); } private static BidderBid bid(UnaryOperator bidCustomizer) { diff --git a/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/model/BidderInvocationContextImpl.java b/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/model/BidderInvocationContextImpl.java index d39d0a4ca5f..72defaba54a 100644 --- a/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/model/BidderInvocationContextImpl.java +++ b/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/model/BidderInvocationContextImpl.java @@ -35,7 +35,6 @@ public class BidderInvocationContextImpl implements BidderInvocationContext { String bidder; public static BidderInvocationContext of(String bidder, - BidRejectionTracker bidRejectionTracker, ObjectNode accountConfig, boolean debugEnabled) { @@ -43,7 +42,6 @@ public static BidderInvocationContext of(String bidder, .bidder(bidder) .auctionContext(AuctionContext.builder() .bidRequest(BidRequest.builder().build()) - .bidRejectionTrackers(Map.of(bidder, bidRejectionTracker)) .build()) .accountConfig(accountConfig) .debugEnabled(debugEnabled) @@ -52,7 +50,6 @@ public static BidderInvocationContext of(String bidder, public static BidderInvocationContext of(String bidder, Map aliases, - BidRejectionTracker bidRejectionTracker, ObjectNode accountConfig, boolean debugEnabled) { @@ -64,7 +61,6 @@ public static BidderInvocationContext of(String bidder, .aliases(aliases) .build())) .build()) - .bidRejectionTrackers(Map.of(bidder, bidRejectionTracker)) .build()) .accountConfig(accountConfig) .debugEnabled(debugEnabled) diff --git a/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/core/BidResponsesMraidFilter.java b/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/core/BidResponsesMraidFilter.java index 499de57b1d8..e6a413ab408 100644 --- a/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/core/BidResponsesMraidFilter.java +++ b/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/core/BidResponsesMraidFilter.java @@ -24,8 +24,7 @@ public class BidResponsesMraidFilter { private static final Map TAG_VALUES = Map.of("richmedia-format", "mraid"); public MraidFilterResult filterByPattern(String mraidScriptPattern, - List responses, - Map bidRejectionTrackers) { + List responses) { List filteredResponses = new ArrayList<>(); List analyticsResults = new ArrayList<>(); @@ -53,12 +52,10 @@ public MraidFilterResult filterByPattern(String mraidScriptPattern, TAG_STATUS, TAG_VALUES, bidder, - rejectedImps); + rejectedImps, + BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE); analyticsResults.add(analyticsResult); - bidRejectionTrackers.get(bidder) - .rejectBids(invalidBids, BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE); - final List errors = new ArrayList<>(seatBid.getErrors()); errors.add(BidderError.of("Invalid bid", BidderError.Type.invalid_bid, new HashSet<>(rejectedImps))); filteredResponses.add(bidderResponse.with(seatBid.with(validBids, errors))); diff --git a/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/model/AnalyticsResult.java b/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/model/AnalyticsResult.java index 79eb7760d36..247af4c7ee7 100644 --- a/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/model/AnalyticsResult.java +++ b/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/model/AnalyticsResult.java @@ -1,6 +1,7 @@ package org.prebid.server.hooks.modules.pb.richmedia.filter.model; import lombok.Value; +import org.prebid.server.auction.model.BidRejectionReason; import java.util.List; import java.util.Map; @@ -15,4 +16,6 @@ public class AnalyticsResult { String bidder; List impId; + + BidRejectionReason rejectionReason; } diff --git a/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHook.java b/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHook.java index c63baeda58c..b58be3a64e1 100644 --- a/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHook.java +++ b/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHook.java @@ -6,6 +6,8 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; import org.prebid.server.auction.model.BidderResponse; +import org.prebid.server.auction.model.Rejected; +import org.prebid.server.auction.model.RejectedImp; import org.prebid.server.hooks.execution.v1.InvocationResultImpl; import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; import org.prebid.server.hooks.execution.v1.analytics.AppliedToImpl; @@ -26,10 +28,13 @@ import org.prebid.server.hooks.v1.bidder.AllProcessedBidResponsesHook; import org.prebid.server.hooks.v1.bidder.AllProcessedBidResponsesPayload; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class PbRichmediaFilterAllProcessedBidResponsesHook implements AllProcessedBidResponsesHook { @@ -60,26 +65,38 @@ public Future> call( if (BooleanUtils.isTrue(properties.getFilterMraid())) { final MraidFilterResult filterResult = mraidFilter.filterByPattern( properties.getMraidScriptPattern(), - responses, - auctionInvocationContext.auctionContext().getBidRejectionTrackers()); + responses); final InvocationAction action = filterResult.hasRejectedBids() ? InvocationAction.update : InvocationAction.no_action; return Future.succeededFuture(toInvocationResult( filterResult.getFilterResult(), toAnalyticsTags(filterResult.getAnalyticsResult()), + toRejections(filterResult.getAnalyticsResult()), action)); } return Future.succeededFuture(toInvocationResult( responses, toAnalyticsTags(Collections.emptyList()), + null, InvocationAction.no_action)); } + private Map> toRejections(List analyticsResults) { + return analyticsResults.stream().collect(Collectors.toMap( + AnalyticsResult::getBidder, + result -> result.getImpId().stream() + .map(impId -> RejectedImp.of(impId, result.getRejectionReason())) + .map(Rejected.class::cast) + .toList(), + (list1, list2) -> Stream.concat(list1.stream(), list2.stream()).collect(Collectors.toList()))); + } + private static InvocationResult toInvocationResult( List bidderResponses, Tags analyticsTags, + Map> rejections, InvocationAction action) { return InvocationResultImpl.builder() @@ -87,6 +104,7 @@ private static InvocationResult toInvocationRes .action(action) .analyticsTags(analyticsTags) .payloadUpdate(payload -> AllProcessedBidResponsesPayloadImpl.of(bidderResponses)) + .rejections(rejections) .build(); } diff --git a/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/core/BidResponsesMraidFilterTest.java b/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/core/BidResponsesMraidFilterTest.java index 0198210d1ca..bebeeeca2e9 100644 --- a/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/core/BidResponsesMraidFilterTest.java +++ b/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/core/BidResponsesMraidFilterTest.java @@ -30,21 +30,14 @@ public void filterShouldReturnOriginalBidsWhenNoBidsHaveMraidScriptInAdm() { // given final BidderResponse responseA = givenBidderResponse("bidderA", List.of(givenBid("imp_id", "adm1"))); final BidderResponse responseB = givenBidderResponse("bidderB", List.of(givenBid("imp_id", "adm2"))); - final BidRejectionTracker bidRejectionTrackerA = mock(BidRejectionTracker.class); - final BidRejectionTracker bidRejectionTrackerB = mock(BidRejectionTracker.class); - final Map givenTrackers = Map.of( - "bidderA", bidRejectionTrackerA, - "bidderB", bidRejectionTrackerB); // when - final MraidFilterResult filterResult = target.filterByPattern("mraid.js", List.of(responseA, responseB), givenTrackers); + final MraidFilterResult filterResult = target.filterByPattern("mraid.js", List.of(responseA, responseB)); // then assertThat(filterResult.getFilterResult()).containsExactly(responseA, responseB); assertThat(filterResult.getAnalyticsResult()).isEmpty(); assertThat(filterResult.hasRejectedBids()).isFalse(); - - verifyNoInteractions(bidRejectionTrackerA, bidRejectionTrackerB); } @Test @@ -59,19 +52,10 @@ public void filterShouldReturnFilteredBidsWhenBidsWithMraidScriptIsFilteredOut() final BidderResponse responseB = givenBidderResponse("bidderB", List.of(givenBid1, givenInvalidBid2)); final BidderResponse responseC = givenBidderResponse("bidderC", List.of(givenInvalidBid1, givenInvalidBid2)); - final BidRejectionTracker bidRejectionTrackerA = mock(BidRejectionTracker.class); - final BidRejectionTracker bidRejectionTrackerB = mock(BidRejectionTracker.class); - final BidRejectionTracker bidRejectionTrackerC = mock(BidRejectionTracker.class); - final Map givenTrackers = Map.of( - "bidderA", bidRejectionTrackerA, - "bidderB", bidRejectionTrackerB, - "bidderC", bidRejectionTrackerC); - // when final MraidFilterResult filterResult = target.filterByPattern( "mraid.js", - List.of(responseA, responseB, responseC), - givenTrackers); + List.of(responseA, responseB, responseC)); // then final BidderResponse expectedResponseA = givenBidderResponse( @@ -90,25 +74,20 @@ public void filterShouldReturnFilteredBidsWhenBidsWithMraidScriptIsFilteredOut() "success-block", Map.of("richmedia-format", "mraid"), "bidderB", - List.of("imp_id2")); + List.of("imp_id2"), + BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE); final AnalyticsResult expectedAnalyticsResultC = AnalyticsResult.of( "success-block", Map.of("richmedia-format", "mraid"), "bidderC", - List.of("imp_id1", "imp_id2")); + List.of("imp_id1", "imp_id2"), + BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE); assertThat(filterResult.getFilterResult()) .containsExactly(expectedResponseA, expectedResponseB, expectedResponseC); assertThat(filterResult.getAnalyticsResult()) .containsExactlyInAnyOrder(expectedAnalyticsResultB, expectedAnalyticsResultC); assertThat(filterResult.hasRejectedBids()).isTrue(); - - verifyNoInteractions(bidRejectionTrackerA); - verify(bidRejectionTrackerB) - .rejectBids(List.of(givenInvalidBid2), BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE); - verify(bidRejectionTrackerC) - .rejectBids(List.of(givenInvalidBid1, givenInvalidBid2), BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE); - verifyNoMoreInteractions(bidRejectionTrackerB, bidRejectionTrackerC); } private static BidderResponse givenBidderResponse(String bidder, List bids) { diff --git a/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHookTest.java b/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHookTest.java index 2a87faec776..2859ac9cc32 100644 --- a/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHookTest.java +++ b/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHookTest.java @@ -8,8 +8,10 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.model.BidderResponse; +import org.prebid.server.auction.model.RejectedImp; import org.prebid.server.bidder.model.BidderSeatBid; import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; import org.prebid.server.hooks.execution.v1.analytics.AppliedToImpl; @@ -35,12 +37,14 @@ import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.Mock.Strictness.LENIENT; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; +import static org.prebid.server.auction.model.BidRejectionReason.*; @ExtendWith(MockitoExtension.class) public class PbRichmediaFilterAllProcessedBidResponsesHookTest { @@ -61,15 +65,11 @@ public class PbRichmediaFilterAllProcessedBidResponsesHookTest { private PbRichmediaFilterAllProcessedBidResponsesHook target; - private static final Map BID_REJECTION_TRACKERS = Map.of( - "bidder", new BidRejectionTracker("bidder", Collections.emptySet(), 0.1)); @BeforeEach public void setUp() { target = new PbRichmediaFilterAllProcessedBidResponsesHook(ObjectMapperProvider.mapper(), mraidFilter, configResolver); when(configResolver.resolve(any())).thenReturn(PbRichMediaFilterProperties.of(true, "pattern")); - when(auctionInvocationContext.auctionContext()) - .thenReturn(AuctionContext.builder().bidRejectionTrackers(BID_REJECTION_TRACKERS).build()); } @Test @@ -99,6 +99,7 @@ public void callShouldReturnResultWithNoActionWhenFilterMraidIsFalse() { assertThat(result.status()).isEqualTo(InvocationStatus.success); assertThat(result.action()).isEqualTo(InvocationAction.no_action); assertThat(result.analyticsTags()).isNull(); + assertThat(result.rejections()).isNull(); assertThat(result.payloadUpdate().apply(AllProcessedBidResponsesPayloadImpl.of(List.of())).bidResponses()) .isEqualTo(givenResponses); @@ -110,7 +111,7 @@ public void callShouldReturnResultWithUpdateActionWhenSomeResponsesWereFilteredO // given final List givenResponses = givenBidderResponses(2); doReturn(givenResponses).when(allProcessedBidResponsesPayload).bidResponses(); - given(mraidFilter.filterByPattern("pattern", givenResponses, BID_REJECTION_TRACKERS)) + given(mraidFilter.filterByPattern("pattern", givenResponses)) .willReturn(MraidFilterResult.of(givenResponses, List.of(givenAnalyticsResult("bidder", "imp_id")))); // when @@ -133,7 +134,7 @@ public void callShouldReturnResultWithNoActionWhenNothingWereFilteredOut() { // given final List givenResponses = givenBidderResponses(2); doReturn(givenResponses).when(allProcessedBidResponsesPayload).bidResponses(); - given(mraidFilter.filterByPattern("pattern", givenResponses, BID_REJECTION_TRACKERS)) + given(mraidFilter.filterByPattern("pattern", givenResponses)) .willReturn(MraidFilterResult.of(givenResponses, Collections.emptyList())); // when @@ -157,7 +158,7 @@ public void callShouldReturnResultOfFilteredResponses() { final List givenResponses = givenBidderResponses(3); doReturn(givenResponses).when(allProcessedBidResponsesPayload).bidResponses(); final List expectedResponses = givenBidderResponses(2); - given(mraidFilter.filterByPattern("pattern", givenResponses, BID_REJECTION_TRACKERS)) + given(mraidFilter.filterByPattern("pattern", givenResponses)) .willReturn(MraidFilterResult.of(expectedResponses, Collections.emptyList())); // when @@ -181,7 +182,7 @@ public void callShouldReturnAnalyticsResultsOfRejectedBids() { // given final List givenResponses = givenBidderResponses(3); doReturn(givenResponses).when(allProcessedBidResponsesPayload).bidResponses(); - given(mraidFilter.filterByPattern("pattern", givenResponses, BID_REJECTION_TRACKERS)) + given(mraidFilter.filterByPattern("pattern", givenResponses)) .willReturn(MraidFilterResult.of( givenResponses, List.of( @@ -219,6 +220,13 @@ public void callShouldReturnAnalyticsResultsOfRejectedBids() { "reject-richmedia", "success", List.of(expectedResult1, expectedResult2))))); + + assertThat(result.rejections()).containsOnly( + entry("bidderA", List.of( + RejectedImp.of("imp_id1", RESPONSE_REJECTED_INVALID_CREATIVE), + RejectedImp.of("imp_id2", RESPONSE_REJECTED_INVALID_CREATIVE))), + entry("bidderB", List.of( + RejectedImp.of("imp_id3", RESPONSE_REJECTED_INVALID_CREATIVE)))); } @Test @@ -226,7 +234,7 @@ public void callShouldReturnEmptyAnalyticsResultsWhenThereAreNoRejectedBids() { // given final List givenResponses = givenBidderResponses(3); doReturn(givenResponses).when(allProcessedBidResponsesPayload).bidResponses(); - given(mraidFilter.filterByPattern("pattern", givenResponses, BID_REJECTION_TRACKERS)) + given(mraidFilter.filterByPattern("pattern", givenResponses)) .willReturn(MraidFilterResult.of(givenResponses, Collections.emptyList())); // when @@ -242,6 +250,7 @@ public void callShouldReturnEmptyAnalyticsResultsWhenThereAreNoRejectedBids() { assertThat(result).isNotNull(); assertThat(result.status()).isEqualTo(InvocationStatus.success); assertThat(result.analyticsTags()).isNull(); + assertThat(result.rejections()).isEmpty(); } private static List givenBidderResponses(int number) { @@ -251,7 +260,12 @@ private static List givenBidderResponses(int number) { } private static AnalyticsResult givenAnalyticsResult(String bidder, String... rejectedImpIds) { - return AnalyticsResult.of("status", Map.of("key", "value"), bidder, List.of(rejectedImpIds)); + return AnalyticsResult.of( + "status", + Map.of("key", "value"), + bidder, + List.of(rejectedImpIds), + RESPONSE_REJECTED_INVALID_CREATIVE); } } diff --git a/src/main/java/org/prebid/server/analytics/reporter/greenbids/GreenbidsAnalyticsReporter.java b/src/main/java/org/prebid/server/analytics/reporter/greenbids/GreenbidsAnalyticsReporter.java index d4b0a4f8711..81cf79f2213 100644 --- a/src/main/java/org/prebid/server/analytics/reporter/greenbids/GreenbidsAnalyticsReporter.java +++ b/src/main/java/org/prebid/server/analytics/reporter/greenbids/GreenbidsAnalyticsReporter.java @@ -385,8 +385,8 @@ private static Map getSeatsWithNonBids(AuctionContext auctionCon } private static SeatNonBid toSeatNonBid(String bidder, BidRejectionTracker bidRejectionTracker) { - final List nonBids = bidRejectionTracker.getRejectedImps().entrySet().stream() - .map(entry -> NonBid.of(entry.getKey(), entry.getValue())) + final List nonBids = bidRejectionTracker.getRejectedImps().stream() + .map(rejectedImp -> NonBid.of(rejectedImp.impId(), rejectedImp.reason())) .toList(); return SeatNonBid.of(bidder, nonBids); diff --git a/src/main/java/org/prebid/server/auction/BidResponseCreator.java b/src/main/java/org/prebid/server/auction/BidResponseCreator.java index 4242281a5dd..589d24188be 100644 --- a/src/main/java/org/prebid/server/auction/BidResponseCreator.java +++ b/src/main/java/org/prebid/server/auction/BidResponseCreator.java @@ -1924,8 +1924,8 @@ private static BidResponse populateSeatNonBid(AuctionContext auctionContext, Bid } private static SeatNonBid toSeatNonBid(String bidder, BidRejectionTracker bidRejectionTracker) { - final List nonBid = bidRejectionTracker.getRejectedImps().entrySet().stream() - .map(entry -> NonBid.of(entry.getKey(), entry.getValue())) + final List nonBid = bidRejectionTracker.getRejectedImps().stream() + .map(rejectedImp -> NonBid.of(rejectedImp.impId(), rejectedImp.reason())) .toList(); return SeatNonBid.of(bidder, nonBid); diff --git a/src/main/java/org/prebid/server/auction/DsaEnforcer.java b/src/main/java/org/prebid/server/auction/DsaEnforcer.java index 369842b36c6..03ad3cfa33b 100644 --- a/src/main/java/org/prebid/server/auction/DsaEnforcer.java +++ b/src/main/java/org/prebid/server/auction/DsaEnforcer.java @@ -9,6 +9,7 @@ import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.model.BidderResponse; +import org.prebid.server.auction.model.RejectedBid; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderError; import org.prebid.server.bidder.model.BidderSeatBid; @@ -72,7 +73,7 @@ public AuctionParticipation enforce(BidRequest bidRequest, } } catch (PreBidException e) { warnings.add(BidderError.invalidBid("Bid \"%s\": %s".formatted(bid.getId(), e.getMessage()))); - rejectionTracker.rejectBid(bidderBid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY); + rejectionTracker.reject(RejectedBid.of(bidderBid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); updatedBidderBids.remove(bidderBid); } } diff --git a/src/main/java/org/prebid/server/auction/ExchangeService.java b/src/main/java/org/prebid/server/auction/ExchangeService.java index 56cd8192501..01989f13b5b 100644 --- a/src/main/java/org/prebid/server/auction/ExchangeService.java +++ b/src/main/java/org/prebid/server/auction/ExchangeService.java @@ -236,7 +236,8 @@ private Future runAuction(AuctionContext receivedContext) { final BidderAliases aliases = aliases(bidRequest); final BidRequestCacheInfo cacheInfo = bidRequestCacheInfo(bidRequest); final Map bidderToMultiBid = bidderToMultiBids(bidRequest, debugWarnings); - receivedContext.getBidRejectionTrackers().putAll(makeBidRejectionTrackers(bidRequest, aliases)); + + populateBidRejectionTrackers(receivedContext, aliases); final boolean debugEnabled = receivedContext.getDebugContext().isDebugEnabled(); metrics.updateDebugRequestMetrics(debugEnabled); @@ -398,7 +399,29 @@ private static MultiBidConfig toMultiBid(String bidder, Integer maxBids, String return MultiBidConfig.of(bidder, bidLimit, codePrefix); } - private Map makeBidRejectionTrackers(BidRequest bidRequest, BidderAliases aliases) { + private void populateBidRejectionTrackers(AuctionContext auctionContext, BidderAliases aliases) { + final Map bidRejectionTrackers = auctionContext.getBidRejectionTrackers(); + removeInvalidBidRejectionTrackers(bidRejectionTrackers, aliases); + makeBidRejectionTrackers(bidRejectionTrackers, auctionContext.getBidRequest(), aliases); + } + + private void removeInvalidBidRejectionTrackers(Map bidRejectionTrackers, + BidderAliases aliases) { + + final Set bidderNames = new HashSet<>(bidRejectionTrackers.keySet()); + for (String bidder: bidderNames) { + if (!isValidBidder(bidder, aliases)) { + bidRejectionTrackers.remove(bidder); + logger.warn("Pre-rejected impressions of the bidder {} have been removed. " + + "Reason: the bidder is invalid"); + } + } + } + + private void makeBidRejectionTrackers(Map bidRejectionTrackers, + BidRequest bidRequest, + BidderAliases aliases) { + final Map> impIdToBidders = bidRequest.getImp().stream() .filter(Objects::nonNull) .filter(imp -> StringUtils.isNotEmpty(imp.getId())) @@ -412,9 +435,13 @@ private Map makeBidRejectionTrackers(BidRequest bid bidderToImpIds.computeIfAbsent(bidder, bidderName -> new HashSet<>()).add(impId)); } - return bidderToImpIds.entrySet().stream().collect(Collectors.toMap( - Map.Entry::getKey, - entry -> new BidRejectionTracker(entry.getKey(), entry.getValue(), logSamplingRate))); + bidderToImpIds.forEach((bidder, impIds) -> { + if (bidRejectionTrackers.containsKey(bidder)) { + bidRejectionTrackers.put(bidder, new BidRejectionTracker(bidRejectionTrackers.get(bidder), impIds)); + } else { + bidRejectionTrackers.put(bidder, new BidRejectionTracker(bidder, impIds, logSamplingRate)); + } + }); } private static StoredResponseResult populateStoredResponse(StoredResponseResult storedResponseResult, @@ -699,7 +726,7 @@ private AuctionParticipation createAuctionParticipation( if (blockedRequestByTcf) { context.getBidRejectionTrackers() .get(bidder) - .rejectAllImps(BidRejectionReason.REQUEST_BLOCKED_PRIVACY); + .rejectAll(BidRejectionReason.REQUEST_BLOCKED_PRIVACY); return AuctionParticipation.builder() .bidder(bidder) @@ -1140,7 +1167,7 @@ private static Future processReject(AuctionContext auctionContex auctionContext.getBidRejectionTrackers() .get(bidderName) - .rejectAllImps(bidRejectionReason); + .rejectAll(bidRejectionReason); final BidderSeatBid bidderSeatBid = BidderSeatBid.builder() .warnings(warnings) .build(); @@ -1179,7 +1206,7 @@ private Future requestBidsOrRejectBidder( if (hookStageResult.isShouldReject()) { auctionContext.getBidRejectionTrackers() .get(bidderRequest.getBidder()) - .rejectAllImps(BidRejectionReason.REQUEST_BLOCKED_GENERAL); + .rejectAll(BidRejectionReason.REQUEST_BLOCKED_GENERAL); return Future.succeededFuture(BidderResponse.of(bidderRequest.getBidder(), BidderSeatBid.empty(), 0)); } diff --git a/src/main/java/org/prebid/server/auction/model/BidRejectionReason.java b/src/main/java/org/prebid/server/auction/model/BidRejectionReason.java index fc3ee36bd2a..8aed823f6d1 100644 --- a/src/main/java/org/prebid/server/auction/model/BidRejectionReason.java +++ b/src/main/java/org/prebid/server/auction/model/BidRejectionReason.java @@ -53,6 +53,11 @@ public enum BidRejectionReason { */ REQUEST_BLOCKED_UNSUPPORTED_MEDIA_TYPE(202), + /** + * This impression not sent to the bid adapter because the impression or the bidder was removed from the request. + */ + REQUEST_BLOCKED_OPTIMIZED(203), + /** * If the bidder was not called due to GDPR purpose 2 */ diff --git a/src/main/java/org/prebid/server/auction/model/BidRejectionTracker.java b/src/main/java/org/prebid/server/auction/model/BidRejectionTracker.java index 476e61fac09..eb85fdf7aac 100644 --- a/src/main/java/org/prebid/server/auction/model/BidRejectionTracker.java +++ b/src/main/java/org/prebid/server/auction/model/BidRejectionTracker.java @@ -1,7 +1,6 @@ package org.prebid.server.auction.model; import com.iab.openrtb.response.Bid; -import org.apache.commons.lang3.tuple.Pair; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.log.ConditionalLogger; import org.prebid.server.log.Logger; @@ -16,7 +15,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.Set; public class BidRejectionTracker { @@ -36,7 +34,7 @@ public class BidRejectionTracker { private final String bidder; private final Set involvedImpIds; private final Map> succeededBidsIds; - private final Map>> rejectedBids; + private final Map> rejectedBids; public BidRejectionTracker(String bidder, Set involvedImpIds, double logSamplingRate) { this.bidder = bidder; @@ -47,6 +45,16 @@ public BidRejectionTracker(String bidder, Set involvedImpIds, double log rejectedBids = new HashMap<>(); } + public BidRejectionTracker(BidRejectionTracker anotherTracker, Set additionalImpIds) { + this.bidder = anotherTracker.bidder; + this.logSamplingRate = anotherTracker.logSamplingRate; + this.involvedImpIds = new HashSet<>(anotherTracker.involvedImpIds); + this.involvedImpIds.addAll(additionalImpIds); + + this.succeededBidsIds = new HashMap<>(anotherTracker.succeededBidsIds); + this.rejectedBids = new HashMap<>(anotherTracker.rejectedBids); + } + public void succeed(Collection bids) { bids.stream() .map(BidderBid::getBid) @@ -71,28 +79,27 @@ public void restoreFromRejection(Collection bids) { succeed(bids); } - public void rejectBids(Collection bidderBids, BidRejectionReason reason) { - bidderBids.forEach(bidderBid -> rejectBid(bidderBid, reason)); + public void reject(Collection rejections) { + rejections.forEach(this::reject); } - public void rejectBid(BidderBid bidderBid, BidRejectionReason reason) { - final Bid bid = bidderBid.getBid(); - final String impId = bid.getImpid(); - - reject(impId, bidderBid, reason); - } + public void reject(Rejected rejected) { + if (rejected instanceof RejectedImp && rejected.reason().getValue() >= 300) { + logger.warn("The rejected imp {} with the code {} equal to or higher than 300 assumes " + + "that there is a rejected bid that shouldn't be lost"); + } - private void reject(String impId, BidderBid bid, BidRejectionReason reason) { + final String impId = rejected.impId(); if (involvedImpIds.contains(impId)) { if (rejectedBids.containsKey(impId)) { BID_REJECTIONS_LOGGER.warn( MULTIPLE_REJECTIONS_WARNING_TEMPLATE.formatted(bidder, impId), logSamplingRate); } - rejectedBids.computeIfAbsent(impId, key -> new ArrayList<>()).add(Pair.of(bid, reason)); + rejectedBids.computeIfAbsent(impId, key -> new ArrayList<>()).add(rejected); if (succeededBidsIds.containsKey(impId)) { - final String bidId = Optional.ofNullable(bid).map(BidderBid::getBid).map(Bid::getId).orElse(null); + final String bidId = rejected instanceof RejectedBid ? ((RejectedBid) rejected).bidId() : null; final Set succeededBids = succeededBidsIds.get(impId); final boolean removed = bidId == null || succeededBids.remove(bidId); if (removed && !succeededBids.isEmpty()) { @@ -105,30 +112,22 @@ private void reject(String impId, BidderBid bid, BidRejectionReason reason) { } public void rejectImps(Collection impIds, BidRejectionReason reason) { - impIds.forEach(impId -> rejectImp(impId, reason)); - } - - public void rejectImp(String impId, BidRejectionReason reason) { - if (reason.getValue() >= 300) { - throw new IllegalArgumentException("The non-bid code 300 and higher assumes " - + "that there is a rejected bid that shouldn't be lost"); - } - reject(impId, null, reason); + impIds.forEach(impId -> reject(RejectedImp.of(impId, reason))); } - public void rejectAllImps(BidRejectionReason reason) { - involvedImpIds.forEach(impId -> rejectImp(impId, reason)); + public void rejectAll(BidRejectionReason reason) { + involvedImpIds.forEach(impId -> reject(RejectedImp.of(impId, reason))); } - public Map getRejectedImps() { - final Map rejectedImpIds = new HashMap<>(); + public Set getRejectedImps() { + final Set rejectedImpIds = new HashSet<>(); for (String impId : involvedImpIds) { final Set succeededBids = succeededBidsIds.getOrDefault(impId, Collections.emptySet()); if (succeededBids.isEmpty()) { if (rejectedBids.containsKey(impId)) { - rejectedImpIds.put(impId, rejectedBids.get(impId).getFirst().getRight()); + rejectedImpIds.add(RejectedImp.of(impId, rejectedBids.get(impId).getFirst().reason())); } else { - rejectedImpIds.put(impId, BidRejectionReason.NO_BID); + rejectedImpIds.add(RejectedImp.of(impId, BidRejectionReason.NO_BID)); } } } @@ -136,13 +135,13 @@ public Map getRejectedImps() { return rejectedImpIds; } - public Map>> getRejectedBids() { - final Map>> missingImpIds = new HashMap<>(); + public Map> getAllRejected() { + final Map> missingImpIds = new HashMap<>(); for (String impId : involvedImpIds) { final Set succeededBids = succeededBidsIds.getOrDefault(impId, Collections.emptySet()); if (succeededBids.isEmpty() && !rejectedBids.containsKey(impId)) { missingImpIds.computeIfAbsent(impId, key -> new ArrayList<>()) - .add(Pair.of(null, BidRejectionReason.NO_BID)); + .add(RejectedImp.of(impId, BidRejectionReason.NO_BID)); } } diff --git a/src/main/java/org/prebid/server/auction/model/Rejected.java b/src/main/java/org/prebid/server/auction/model/Rejected.java new file mode 100644 index 00000000000..f18fc890d86 --- /dev/null +++ b/src/main/java/org/prebid/server/auction/model/Rejected.java @@ -0,0 +1,9 @@ +package org.prebid.server.auction.model; + +public interface Rejected { + + String impId(); + + BidRejectionReason reason(); + +} diff --git a/src/main/java/org/prebid/server/auction/model/RejectedBid.java b/src/main/java/org/prebid/server/auction/model/RejectedBid.java new file mode 100644 index 00000000000..1a0ae1ff53f --- /dev/null +++ b/src/main/java/org/prebid/server/auction/model/RejectedBid.java @@ -0,0 +1,26 @@ +package org.prebid.server.auction.model; + +import lombok.Value; +import org.prebid.server.bidder.model.BidderBid; + +@Value(staticConstructor = "of") +public class RejectedBid implements Rejected { + + BidderBid bid; + + BidRejectionReason reason; + + @Override + public String impId() { + return bid.getBid().getImpid(); + } + + @Override + public BidRejectionReason reason() { + return reason; + } + + public String bidId() { + return bid.getBid().getId(); + } +} diff --git a/src/main/java/org/prebid/server/auction/model/RejectedImp.java b/src/main/java/org/prebid/server/auction/model/RejectedImp.java new file mode 100644 index 00000000000..186221de0ac --- /dev/null +++ b/src/main/java/org/prebid/server/auction/model/RejectedImp.java @@ -0,0 +1,14 @@ +package org.prebid.server.auction.model; + +import lombok.Value; +import lombok.experimental.Accessors; + +@Value(staticConstructor = "of") +@Accessors(fluent = true) +public class RejectedImp implements Rejected { + + String impId; + + BidRejectionReason reason; + +} diff --git a/src/main/java/org/prebid/server/auction/requestfactory/Ortb2RequestFactory.java b/src/main/java/org/prebid/server/auction/requestfactory/Ortb2RequestFactory.java index c9ec70e8116..7efecf9dab4 100644 --- a/src/main/java/org/prebid/server/auction/requestfactory/Ortb2RequestFactory.java +++ b/src/main/java/org/prebid/server/auction/requestfactory/Ortb2RequestFactory.java @@ -355,7 +355,7 @@ public Future executeEntrypointHooks(RoutingContext routingC toCaseInsensitiveMultiMap(routingContext.queryParams()), toCaseInsensitiveMultiMap(routingContext.request().headers()), body, - auctionContext.getHookExecutionContext()) + auctionContext) .map(stageResult -> toHttpRequest(stageResult, routingContext, auctionContext)); } diff --git a/src/main/java/org/prebid/server/bidder/HttpBidderRequester.java b/src/main/java/org/prebid/server/bidder/HttpBidderRequester.java index 3a24683c774..f7696e1307a 100644 --- a/src/main/java/org/prebid/server/bidder/HttpBidderRequester.java +++ b/src/main/java/org/prebid/server/bidder/HttpBidderRequester.java @@ -419,11 +419,11 @@ private void handleBidderCallError(BidderCall bidderCall) { return; } - if (callErrorType == BidderError.Type.timeout) { - bidRejectionTracker.rejectImps(requestedImpIds, BidRejectionReason.ERROR_TIMED_OUT); - } else { - bidRejectionTracker.rejectImps(requestedImpIds, BidRejectionReason.ERROR_GENERAL); - } + final BidRejectionReason reason = callErrorType == BidderError.Type.timeout + ? BidRejectionReason.ERROR_TIMED_OUT + : BidRejectionReason.ERROR_GENERAL; + + bidRejectionTracker.rejectImps(requestedImpIds, reason); } private void handleFledgeAuctionConfigs(CompositeBidderResponse bidderResponse) { diff --git a/src/main/java/org/prebid/server/floors/BasicPriceFloorEnforcer.java b/src/main/java/org/prebid/server/floors/BasicPriceFloorEnforcer.java index 132bf86e782..9cf1bc05ab2 100644 --- a/src/main/java/org/prebid/server/floors/BasicPriceFloorEnforcer.java +++ b/src/main/java/org/prebid/server/floors/BasicPriceFloorEnforcer.java @@ -13,6 +13,7 @@ import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.model.BidderRequest; import org.prebid.server.auction.model.BidderResponse; +import org.prebid.server.auction.model.RejectedBid; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderError; import org.prebid.server.bidder.model.BidderSeatBid; @@ -179,7 +180,7 @@ private AuctionParticipation applyEnforcement(BidRequest bidRequest, "Bid with id '%s' was rejected by floor enforcement: price %s is below the floor %s" .formatted(bid.getId(), price, floor), impId)); - rejectionTracker.rejectBid(bidderBid, BidRejectionReason.RESPONSE_REJECTED_BELOW_FLOOR); + rejectionTracker.reject(RejectedBid.of(bidderBid, BidRejectionReason.RESPONSE_REJECTED_BELOW_FLOOR)); updatedBidderBids.remove(bidderBid); } } diff --git a/src/main/java/org/prebid/server/hooks/execution/GroupResult.java b/src/main/java/org/prebid/server/hooks/execution/GroupResult.java index 8bc7c5c0723..7b3c79aef8b 100644 --- a/src/main/java/org/prebid/server/hooks/execution/GroupResult.java +++ b/src/main/java/org/prebid/server/hooks/execution/GroupResult.java @@ -2,6 +2,8 @@ import lombok.Getter; import lombok.experimental.Accessors; +import org.apache.commons.collections4.MapUtils; +import org.prebid.server.auction.model.Rejected; import org.prebid.server.hooks.execution.model.ExecutionAction; import org.prebid.server.hooks.execution.model.ExecutionStatus; import org.prebid.server.hooks.execution.model.GroupExecutionOutcome; @@ -15,7 +17,9 @@ import org.prebid.server.log.LoggerFactory; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.TimeoutException; @Accessors(fluent = true) @@ -33,6 +37,8 @@ class GroupResult { private final boolean rejectAllowed; + private final Map> rejections = new HashMap<>(); + private final List hookExecutionOutcomes = new ArrayList<>(); private GroupResult(T payload, boolean rejectAllowed) { @@ -52,6 +58,8 @@ public GroupResult applyInvocationResult(InvocationResult invocationResult if (invocationResult.status() == InvocationStatus.success && invocationResult.action() != null) { try { applyAction(hookId, invocationResult.action(), invocationResult.payloadUpdate()); + MapUtils.emptyIfNull(invocationResult.rejections()).forEach((bidder, rejectionList) -> + rejections.computeIfAbsent(bidder, key -> new ArrayList<>()).addAll(rejectionList)); } catch (Exception e) { hookExecutionOutcomes.add(toExecutionOutcome(e, hookId, executionTime)); diff --git a/src/main/java/org/prebid/server/hooks/execution/HookStageExecutor.java b/src/main/java/org/prebid/server/hooks/execution/HookStageExecutor.java index cdb946f8d37..ff12d07e13a 100644 --- a/src/main/java/org/prebid/server/hooks/execution/HookStageExecutor.java +++ b/src/main/java/org/prebid/server/hooks/execution/HookStageExecutor.java @@ -13,8 +13,10 @@ import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.model.BidderRequest; import org.prebid.server.auction.model.BidderResponse; +import org.prebid.server.auction.model.Rejected; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.execution.timeout.Timeout; import org.prebid.server.execution.timeout.TimeoutFactory; @@ -68,6 +70,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.Stream; public class HookStageExecutor { @@ -88,6 +91,7 @@ public class HookStageExecutor { private final Clock clock; private final ObjectMapper mapper; private final boolean isConfigToInvokeRequired; + private final double logSamplingRate; private HookStageExecutor(ExecutionPlan hostExecutionPlan, ExecutionPlan defaultAccountExecutionPlan, @@ -97,7 +101,8 @@ private HookStageExecutor(ExecutionPlan hostExecutionPlan, Vertx vertx, Clock clock, ObjectMapper mapper, - boolean isConfigToInvokeRequired) { + boolean isConfigToInvokeRequired, + double logSamplingRate) { this.hostExecutionPlan = hostExecutionPlan; this.defaultAccountExecutionPlan = defaultAccountExecutionPlan; @@ -108,6 +113,7 @@ private HookStageExecutor(ExecutionPlan hostExecutionPlan, this.mapper = mapper; this.isConfigToInvokeRequired = isConfigToInvokeRequired; this.hostModuleExecution = hostModuleExecution; + this.logSamplingRate = logSamplingRate; } public static HookStageExecutor create(String hostExecutionPlan, @@ -118,7 +124,8 @@ public static HookStageExecutor create(String hostExecutionPlan, Vertx vertx, Clock clock, JacksonMapper mapper, - boolean isConfigToInvokeRequired) { + boolean isConfigToInvokeRequired, + double logSamplingRate) { Objects.requireNonNull(hookCatalog); Objects.requireNonNull(mapper); @@ -132,7 +139,8 @@ public static HookStageExecutor create(String hostExecutionPlan, Objects.requireNonNull(vertx), Objects.requireNonNull(clock), mapper.mapper(), - isConfigToInvokeRequired); + isConfigToInvokeRequired, + logSamplingRate); } private static ExecutionPlan parseAndValidateExecutionPlan(String executionPlan, @@ -181,8 +189,9 @@ public Future> executeEntrypointStag CaseInsensitiveMultiMap queryParams, CaseInsensitiveMultiMap headers, String body, - HookExecutionContext context) { + AuctionContext auctionContext) { + final HookExecutionContext context = auctionContext.getHookExecutionContext(); final Endpoint endpoint = context.getEndpoint(); return stageExecutor(StageWithHookType.ENTRYPOINT, ENTITY_HTTP_REQUEST, context) @@ -192,7 +201,8 @@ public Future> executeEntrypointStag .withInvocationContextProvider(invocationContextProvider(endpoint)) .withModulesExecution(DefaultedMap.defaultedMap(hostModuleExecution, true)) .withRejectAllowed(true) - .execute(); + .execute() + .map(result -> rejectAll(auctionContext, result)); } public Future> executeRawAuctionRequestStage( @@ -210,7 +220,8 @@ public Future> executeRawAuction .withInitialPayload(AuctionRequestPayloadImpl.of(bidRequest)) .withInvocationContextProvider(auctionInvocationContextProvider(endpoint, auctionContext)) .withRejectAllowed(true) - .execute(); + .execute() + .map(result -> rejectAll(auctionContext, result)); } public Future> executeProcessedAuctionRequestStage( @@ -228,7 +239,8 @@ public Future> executeProcessedA .withInitialPayload(AuctionRequestPayloadImpl.of(bidRequest)) .withInvocationContextProvider(auctionInvocationContextProvider(endpoint, auctionContext)) .withRejectAllowed(true) - .execute(); + .execute() + .map(result -> rejectAll(auctionContext, result)); } public Future> executeBidderRequestStage( @@ -246,7 +258,8 @@ public Future> executeBidderReque .withInitialPayload(BidderRequestPayloadImpl.of(bidderRequest.getBidRequest())) .withInvocationContextProvider(bidderInvocationContextProvider(endpoint, auctionContext, bidder)) .withRejectAllowed(true) - .execute(); + .execute() + .map(result -> rejectAllIgnoringUnknowns(auctionContext, result)); } public Future> executeRawBidderResponseStage( @@ -266,7 +279,8 @@ public Future> executeRawBidderR .withInitialPayload(BidderResponsePayloadImpl.of(bids)) .withInvocationContextProvider(bidderInvocationContextProvider(endpoint, auctionContext, bidder)) .withRejectAllowed(true) - .execute(); + .execute() + .map(result -> rejectAllIgnoringUnknowns(auctionContext, result)); } public Future> executeProcessedBidderResponseStage( @@ -285,7 +299,8 @@ public Future> executeProcessedB .withInitialPayload(BidderResponsePayloadImpl.of(bids)) .withInvocationContextProvider(bidderInvocationContextProvider(endpoint, auctionContext, bidder)) .withRejectAllowed(true) - .execute(); + .execute() + .map(result -> rejectAllIgnoringUnknowns(auctionContext, result)); } public Future> executeAllProcessedBidResponsesStage( @@ -303,7 +318,8 @@ public Future> execute .withInitialPayload(AllProcessedBidResponsesPayloadImpl.of(bidderResponses)) .withInvocationContextProvider(auctionInvocationContextProvider(endpoint, auctionContext)) .withRejectAllowed(false) - .execute(); + .execute() + .map(result -> rejectAllIgnoringUnknowns(auctionContext, result)); } public Future> executeAuctionResponseStage( @@ -543,4 +559,35 @@ private static boolean isABTestApplicable(ABTest abTest, String account) { final Set accounts = abTest.getAccounts(); return CollectionUtils.isEmpty(accounts) || accounts.contains(account); } + + //todo: should it be more strict? e.g. allowing rejecting only imps/bids on the particular stages + + private HookStageExecutionResult rejectAll(AuctionContext auctionContext, + HookStageExecutionResult result) { + + result.getRejections() + .forEach((bidder, rejectedList) -> auctionContext.getBidRejectionTrackers().computeIfAbsent( + bidder, + key -> new BidRejectionTracker( + key, + rejectedList.stream().map(Rejected::impId).collect(Collectors.toSet()), + logSamplingRate)) + .reject(rejectedList)); + + return result; + } + + private HookStageExecutionResult rejectAllIgnoringUnknowns(AuctionContext auctionContext, + HookStageExecutionResult result) { + + result.getRejections() + .forEach((bidder, rejectedList) -> auctionContext.getBidRejectionTrackers().computeIfPresent( + bidder, + (key, value) -> { + value.reject(rejectedList); + return value; + })); + + return result; + } } diff --git a/src/main/java/org/prebid/server/hooks/execution/StageExecutor.java b/src/main/java/org/prebid/server/hooks/execution/StageExecutor.java index 8dfa03e9a7f..2db76e7c657 100644 --- a/src/main/java/org/prebid/server/hooks/execution/StageExecutor.java +++ b/src/main/java/org/prebid/server/hooks/execution/StageExecutor.java @@ -136,6 +136,6 @@ private HookStageExecutionResult toHookStageExecutionResult(StageResult return stageResult.shouldReject() ? HookStageExecutionResult.reject() - : HookStageExecutionResult.success(stageResult.payload()); + : HookStageExecutionResult.success(stageResult.payload(), stageResult.rejections()); } } diff --git a/src/main/java/org/prebid/server/hooks/execution/StageResult.java b/src/main/java/org/prebid/server/hooks/execution/StageResult.java index 695747e73ec..9999319d7be 100644 --- a/src/main/java/org/prebid/server/hooks/execution/StageResult.java +++ b/src/main/java/org/prebid/server/hooks/execution/StageResult.java @@ -2,11 +2,15 @@ import lombok.Getter; import lombok.experimental.Accessors; +import org.prebid.server.auction.model.Rejected; import org.prebid.server.hooks.execution.model.GroupExecutionOutcome; import org.prebid.server.hooks.execution.model.StageExecutionOutcome; +import org.prebid.server.util.MapUtil; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Map; @Accessors(fluent = true) @Getter @@ -47,4 +51,11 @@ private List groupExecutionOutcomes() { .map(GroupResult::toGroupExecutionOutcome) .toList(); } + + public Map> rejections() { + return groupResults.stream() + .map(GroupResult::rejections) + .reduce(MapUtil::collectionMerge) + .orElse(Collections.emptyMap()); + } } diff --git a/src/main/java/org/prebid/server/hooks/execution/model/HookStageExecutionResult.java b/src/main/java/org/prebid/server/hooks/execution/model/HookStageExecutionResult.java index 5e867add4ef..1050dc6c0e4 100644 --- a/src/main/java/org/prebid/server/hooks/execution/model/HookStageExecutionResult.java +++ b/src/main/java/org/prebid/server/hooks/execution/model/HookStageExecutionResult.java @@ -1,6 +1,11 @@ package org.prebid.server.hooks.execution.model; import lombok.Value; +import org.prebid.server.auction.model.Rejected; + +import java.util.Collections; +import java.util.List; +import java.util.Map; @Value(staticConstructor = "of") public class HookStageExecutionResult { @@ -9,11 +14,18 @@ public class HookStageExecutionResult { PAYLOAD payload; + Map> rejections; + + public static HookStageExecutionResult success(PAYLOAD payload, + Map> rejections) { + return of(false, payload, rejections); + } + public static HookStageExecutionResult success(PAYLOAD payload) { - return of(false, payload); + return of(false, payload, Collections.emptyMap()); } public static HookStageExecutionResult reject() { - return of(true, null); + return of(true, null, Collections.emptyMap()); } } diff --git a/src/main/java/org/prebid/server/hooks/execution/provider/abtest/ABTestHook.java b/src/main/java/org/prebid/server/hooks/execution/provider/abtest/ABTestHook.java index e7b82803f9a..44524707786 100644 --- a/src/main/java/org/prebid/server/hooks/execution/provider/abtest/ABTestHook.java +++ b/src/main/java/org/prebid/server/hooks/execution/provider/abtest/ABTestHook.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import io.vertx.core.Future; +import org.prebid.server.auction.model.Rejected; import org.prebid.server.hooks.execution.v1.InvocationResultImpl; import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; import org.prebid.server.hooks.execution.v1.analytics.ResultImpl; @@ -19,6 +20,7 @@ import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Objects; public class ABTestHook implements Hook { @@ -120,6 +122,11 @@ public List warnings() { return invocationResult.warnings(); } + @Override + public Map> rejections() { + return invocationResult.rejections(); + } + @Override public List debugMessages() { return invocationResult.debugMessages(); diff --git a/src/main/java/org/prebid/server/hooks/execution/v1/InvocationResultImpl.java b/src/main/java/org/prebid/server/hooks/execution/v1/InvocationResultImpl.java index 761aef951ec..df675f21b46 100644 --- a/src/main/java/org/prebid/server/hooks/execution/v1/InvocationResultImpl.java +++ b/src/main/java/org/prebid/server/hooks/execution/v1/InvocationResultImpl.java @@ -3,6 +3,7 @@ import lombok.Builder; import lombok.Value; import lombok.experimental.Accessors; +import org.prebid.server.auction.model.Rejected; import org.prebid.server.hooks.v1.InvocationAction; import org.prebid.server.hooks.v1.InvocationResult; import org.prebid.server.hooks.v1.InvocationStatus; @@ -10,6 +11,7 @@ import org.prebid.server.hooks.v1.analytics.Tags; import java.util.List; +import java.util.Map; @Accessors(fluent = true) @Builder @@ -30,6 +32,8 @@ public class InvocationResultImpl implements InvocationResult List debugMessages; + Map> rejections; + Object moduleContext; Tags analyticsTags; diff --git a/src/main/java/org/prebid/server/hooks/v1/InvocationResult.java b/src/main/java/org/prebid/server/hooks/v1/InvocationResult.java index 979ff697316..58f73f24987 100644 --- a/src/main/java/org/prebid/server/hooks/v1/InvocationResult.java +++ b/src/main/java/org/prebid/server/hooks/v1/InvocationResult.java @@ -1,8 +1,10 @@ package org.prebid.server.hooks.v1; +import org.prebid.server.auction.model.Rejected; import org.prebid.server.hooks.v1.analytics.Tags; import java.util.List; +import java.util.Map; public interface InvocationResult { @@ -18,6 +20,8 @@ public interface InvocationResult { List warnings(); + Map> rejections(); + List debugMessages(); Object moduleContext(); diff --git a/src/main/java/org/prebid/server/spring/config/HooksConfiguration.java b/src/main/java/org/prebid/server/spring/config/HooksConfiguration.java index 5a05ccb8c8e..4cbfde1ffde 100644 --- a/src/main/java/org/prebid/server/spring/config/HooksConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/HooksConfiguration.java @@ -36,7 +36,8 @@ HookStageExecutor hookStageExecutor(HooksConfigurationProperties hooksConfigurat Clock clock, JacksonMapper mapper, @Value("${settings.modules.require-config-to-invoke:false}") - boolean isConfigToInvokeRequired) { + boolean isConfigToInvokeRequired, + @Value("${logging.sampling-rate:0.01}") double logSamplingRate) { return HookStageExecutor.create( hooksConfiguration.getHostExecutionPlan(), @@ -49,7 +50,8 @@ HookStageExecutor hookStageExecutor(HooksConfigurationProperties hooksConfigurat vertx, clock, mapper, - isConfigToInvokeRequired); + isConfigToInvokeRequired, + logSamplingRate); } @Bean diff --git a/src/main/java/org/prebid/server/util/MapUtil.java b/src/main/java/org/prebid/server/util/MapUtil.java index 282d2b40795..ae7185f9290 100644 --- a/src/main/java/org/prebid/server/util/MapUtil.java +++ b/src/main/java/org/prebid/server/util/MapUtil.java @@ -1,5 +1,6 @@ package org.prebid.server.util; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -15,4 +16,17 @@ public static Map merge(Map left, Map right) { return Collections.unmodifiableMap(merged); } + + public static > Map collectionMerge(Map left, Map right) { + final Map merged = new HashMap<>(left); + right.forEach((key, value) -> { + if (merged.containsKey(key)) { + merged.get(key).addAll(value); + } else { + merged.put(key, value); + } + }); + + return Collections.unmodifiableMap(merged); + } } diff --git a/src/main/java/org/prebid/server/validation/ResponseBidValidator.java b/src/main/java/org/prebid/server/validation/ResponseBidValidator.java index 4e2f062218b..1609b63b1eb 100644 --- a/src/main/java/org/prebid/server/validation/ResponseBidValidator.java +++ b/src/main/java/org/prebid/server/validation/ResponseBidValidator.java @@ -13,6 +13,7 @@ import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.auction.model.BidRejectionTracker; +import org.prebid.server.auction.model.RejectedBid; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.log.ConditionalLogger; import org.prebid.server.log.Logger; @@ -293,7 +294,7 @@ private List singleWarningOrValidationException( return switch (enforcement) { case enforce -> { - bidRejectionTracker.rejectBid(bidderBid, bidRejectionReason); + bidRejectionTracker.reject(RejectedBid.of(bidderBid, bidRejectionReason)); metricsRecorder.accept(MetricName.err); conditionalLogger.warn(message, logSamplingRate); throw new ValidationException(message); diff --git a/src/test/java/org/prebid/server/analytics/reporter/greenbids/GreenbidsAnalyticsReporterTest.java b/src/test/java/org/prebid/server/analytics/reporter/greenbids/GreenbidsAnalyticsReporterTest.java index 82cae87406e..b3e4896f2f0 100644 --- a/src/test/java/org/prebid/server/analytics/reporter/greenbids/GreenbidsAnalyticsReporterTest.java +++ b/src/test/java/org/prebid/server/analytics/reporter/greenbids/GreenbidsAnalyticsReporterTest.java @@ -39,6 +39,7 @@ import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.auction.model.BidRejectionTracker; +import org.prebid.server.auction.model.RejectedImp; import org.prebid.server.hooks.execution.model.ExecutionStatus; import org.prebid.server.hooks.execution.model.GroupExecutionOutcome; import org.prebid.server.hooks.execution.model.HookExecutionContext; @@ -198,7 +199,7 @@ public void shouldReceiveValidResponseOnAuctionContextWithAnalyticsTagForBanner( .build(); final AuctionContext auctionContext = givenAuctionContextWithAnalyticsTag( - context -> context, List.of(imp), true); + context -> context, List.of(imp)); final AuctionEvent event = AuctionEvent.builder() .auctionContext(auctionContext) .bidResponse(auctionContext.getBidResponse()) @@ -640,8 +641,8 @@ private static AuctionContext givenAuctionContext( private static AuctionContext givenAuctionContextWithAnalyticsTag( UnaryOperator auctionContextCustomizer, - List imps, - boolean includeBidResponse) { + List imps) { + final AuctionContext.AuctionContextBuilder auctionContextBuilder = AuctionContext.builder() .httpRequest(HttpRequestContext.builder().build()) .bidRequest(givenBidRequest(request -> request, imps)) @@ -650,11 +651,7 @@ private static AuctionContext givenAuctionContextWithAnalyticsTag( final HookExecutionContext hookExecutionContext = givenHookExecutionContextWithAnalyticsTag(); auctionContextBuilder.hookExecutionContext(hookExecutionContext); - - if (includeBidResponse) { - auctionContextBuilder.bidResponse(givenBidResponse(response -> response)); - } - + auctionContextBuilder.bidResponse(givenBidResponse(response -> response)); return auctionContextCustomizer.apply(auctionContextBuilder).build(); } @@ -813,7 +810,7 @@ private static BidRejectionTracker givenBidRejectionTracker() { "seat3", Set.of("adunitcodevalue"), 1.0); - bidRejectionTracker.rejectImp("imp1", BidRejectionReason.NO_BID); + bidRejectionTracker.reject(RejectedImp.of("imp1", BidRejectionReason.NO_BID)); return bidRejectionTracker; } diff --git a/src/test/java/org/prebid/server/auction/BidResponseCreatorTest.java b/src/test/java/org/prebid/server/auction/BidResponseCreatorTest.java index 04b7d279956..70870d2bf6c 100644 --- a/src/test/java/org/prebid/server/auction/BidResponseCreatorTest.java +++ b/src/test/java/org/prebid/server/auction/BidResponseCreatorTest.java @@ -37,7 +37,6 @@ import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.AuctionParticipation; import org.prebid.server.auction.model.BidInfo; -import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.model.BidRequestCacheInfo; import org.prebid.server.auction.model.BidderResponse; @@ -45,6 +44,7 @@ import org.prebid.server.auction.model.CategoryMappingResult; import org.prebid.server.auction.model.MultiBidConfig; import org.prebid.server.auction.model.PaaFormat; +import org.prebid.server.auction.model.RejectedImp; import org.prebid.server.auction.model.TargetingInfo; import org.prebid.server.auction.model.TimeoutContext; import org.prebid.server.auction.model.debug.DebugContext; @@ -140,6 +140,7 @@ import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; +import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; import static java.util.function.UnaryOperator.identity; @@ -162,6 +163,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.prebid.server.auction.model.BidRejectionReason.NO_BID; import static org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidAdservertargetingRule.Source.xStatic; import static org.prebid.server.proto.openrtb.ext.response.BidType.audio; import static org.prebid.server.proto.openrtb.ext.response.BidType.banner; @@ -245,11 +247,12 @@ public void setUp() { false, BidderResponsePayloadImpl.of(((BidderResponse) invocation.getArgument(0)) .getSeatBid() - .getBids())))); + .getBids()), + null))); given(hookStageExecutor.executeAllProcessedBidResponsesStage(any(), any())) .willAnswer(invocation -> Future.succeededFuture( HookStageExecutionResult.of( - false, AllProcessedBidResponsesPayloadImpl.of(invocation.getArgument(0))))); + false, AllProcessedBidResponsesPayloadImpl.of(invocation.getArgument(0)), null))); clock = Clock.fixed(Instant.ofEpochMilli(1000L), ZoneOffset.UTC); @@ -313,7 +316,7 @@ public void shouldPassBidWithGeneratedIdAndPreserveExtFieldsWhenIdGeneratorTypeU @Test public void shouldSkipBidderWhenRejectedByProcessedBidderResponseHooks() { // given - doAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of(true, null))) + doAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of(true, null, null))) .when(hookStageExecutor).executeProcessedBidderResponseStage(any(), any()); final Bid bid = Bid.builder() @@ -339,8 +342,7 @@ public void shouldSkipBidderWhenRejectedByProcessedBidderResponseHooks() { @Test public void shouldPassRequestModifiedByBidderRequestHooks() { - doAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of( - false, + doAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.success( BidderResponsePayloadImpl.of(singletonList(BidderBid.of( Bid.builder() .id("bidIdModifiedByHook") @@ -4018,7 +4020,7 @@ public void shouldDropFledgeResponsesReferencingUnknownImps() { public void shouldPopulateExtPrebidSeatNonBidWhenReturnAllBidStatusFlagIsTrue() { // given final BidRejectionTracker bidRejectionTracker = mock(BidRejectionTracker.class); - given(bidRejectionTracker.getRejectedImps()).willReturn(singletonMap("impId2", BidRejectionReason.NO_BID)); + given(bidRejectionTracker.getRejectedImps()).willReturn(singleton(RejectedImp.of("impId2", NO_BID))); final Bid bid = Bid.builder().id("bidId").price(BigDecimal.valueOf(3.67)).impid("impId").build(); final List bidderResponses = singletonList( @@ -4043,7 +4045,7 @@ public void shouldPopulateExtPrebidSeatNonBidWhenReturnAllBidStatusFlagIsTrue() // then final SeatNonBid expectedSeatNonBid = SeatNonBid.of( - "someBidder", singletonList(NonBid.of("impId2", BidRejectionReason.NO_BID))); + "someBidder", singletonList(NonBid.of("impId2", NO_BID))); assertThat(bidResponse.getExt()) .extracting(ExtBidResponse::getSeatnonbid) diff --git a/src/test/java/org/prebid/server/auction/DsaEnforcerTest.java b/src/test/java/org/prebid/server/auction/DsaEnforcerTest.java index 5254ea4edda..3394ab04340 100644 --- a/src/test/java/org/prebid/server/auction/DsaEnforcerTest.java +++ b/src/test/java/org/prebid/server/auction/DsaEnforcerTest.java @@ -14,6 +14,7 @@ import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.model.BidderResponse; +import org.prebid.server.auction.model.RejectedBid; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderError; import org.prebid.server.bidder.model.BidderSeatBid; @@ -104,7 +105,7 @@ public void enforceShouldRejectBidAndAddWarningWhenDsaIsNotRequiredAndDsaRespons .bidderResponse(BidderResponse.of("bidder", expectedSeatBid, 100)) .build(); assertThat(actual).isEqualTo(expectedParticipation); - verify(bidRejectionTracker).rejectBid(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY); + verify(bidRejectionTracker).reject(RejectedBid.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); } @Test @@ -137,7 +138,7 @@ public void enforceShouldRejectBidAndAddWarningWhenDsaIsNotRequiredAndDsaRespons .bidderResponse(BidderResponse.of("bidder", expectedSeatBid, 100)) .build(); assertThat(actual).isEqualTo(expectedParticipation); - verify(bidRejectionTracker).rejectBid(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY); + verify(bidRejectionTracker).reject(RejectedBid.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); } @Test @@ -333,7 +334,7 @@ public void enforceShouldRejectBidAndAddWarningWhenBidExtHasEmptyDsaAndDsaIsRequ .bidderResponse(BidderResponse.of("bidder", expectedSeatBid, 100)) .build(); assertThat(actual).isEqualTo(expectedParticipation); - verify(bidRejectionTracker).rejectBid(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY); + verify(bidRejectionTracker).reject(RejectedBid.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); } @Test @@ -367,7 +368,7 @@ public void enforceShouldRejectBidAndAddWarningWhenDsaIsRequiredAndDsaResponseHa .bidderResponse(BidderResponse.of("bidder", expectedSeatBid, 100)) .build(); assertThat(actual).isEqualTo(expectedParticipation); - verify(bidRejectionTracker).rejectBid(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY); + verify(bidRejectionTracker).reject(RejectedBid.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); } @Test @@ -401,7 +402,7 @@ public void enforceShouldRejectBidAndAddWarningWhenDsaIsRequiredAndDsaResponseHa .bidderResponse(BidderResponse.of("bidder", expectedSeatBid, 100)) .build(); assertThat(actual).isEqualTo(expectedParticipation); - verify(bidRejectionTracker).rejectBid(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY); + verify(bidRejectionTracker).reject(RejectedBid.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); } @Test @@ -436,7 +437,7 @@ public void enforceShouldRejectBidAndAddWarningWhenDsaIsRequiredAndPublisherAndA .bidderResponse(BidderResponse.of("bidder", expectedSeatBid, 100)) .build(); assertThat(actual).isEqualTo(expectedParticipation); - verify(bidRejectionTracker).rejectBid(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY); + verify(bidRejectionTracker).reject(RejectedBid.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); } @Test @@ -471,7 +472,7 @@ public void enforceShouldRejectBidAndAddWarningWhenDsaIsRequiredAndPublisherAndA .bidderResponse(BidderResponse.of("bidder", expectedSeatBid, 100)) .build(); assertThat(actual).isEqualTo(expectedParticipation); - verify(bidRejectionTracker).rejectBid(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY); + verify(bidRejectionTracker).reject(RejectedBid.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); } @Test @@ -505,7 +506,7 @@ public void enforceShouldRejectBidAndAddWarningWhenDsaIsRequiredAndPublisherNotR .bidderResponse(BidderResponse.of("bidder", expectedSeatBid, 100)) .build(); assertThat(actual).isEqualTo(expectedParticipation); - verify(bidRejectionTracker).rejectBid(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY); + verify(bidRejectionTracker).reject(RejectedBid.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); } private static ExtRegs givenExtRegs(DsaRequired dsaRequired, DsaPublisherRender pubRender) { diff --git a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java index 2e8c9a5e2c1..d1a7b7fb2c2 100644 --- a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java +++ b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java @@ -46,13 +46,13 @@ import org.prebid.server.auction.mediatypeprocessor.MediaTypeProcessor; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.AuctionParticipation; -import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.model.BidRequestCacheInfo; import org.prebid.server.auction.model.BidderPrivacyResult; import org.prebid.server.auction.model.BidderRequest; import org.prebid.server.auction.model.BidderResponse; import org.prebid.server.auction.model.MultiBidConfig; +import org.prebid.server.auction.model.RejectedImp; import org.prebid.server.auction.model.StoredResponseResult; import org.prebid.server.auction.model.TimeoutContext; import org.prebid.server.auction.model.debug.DebugContext; @@ -164,6 +164,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.function.Function; import java.util.function.UnaryOperator; @@ -199,6 +200,8 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; +import static org.prebid.server.auction.model.BidRejectionReason.NO_BID; +import static org.prebid.server.auction.model.BidRejectionReason.REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY; import static org.prebid.server.proto.openrtb.ext.response.BidType.banner; import static org.prebid.server.proto.openrtb.ext.response.BidType.video; @@ -337,17 +340,14 @@ public void setUp() { given(supplyChainResolver.resolveForBidder(anyString(), any())).willReturn(null); given(hookStageExecutor.executeBidderRequestStage(any(), any())) - .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of( - false, + .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.success( BidderRequestPayloadImpl.of(invocation.getArgument(0).getBidRequest())))); given(hookStageExecutor.executeRawBidderResponseStage(any(), any())) - .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of( - false, + .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.success( BidderResponsePayloadImpl.of(invocation.getArgument(0).getSeatBid() .getBids())))); given(hookStageExecutor.executeAuctionResponseStage(any(), any())) - .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of( - false, + .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.success( AuctionResponsePayloadImpl.of(invocation.getArgument(0))))); given(bidsAdjuster.validateAndAdjustBids(any(), any(), any())) @@ -597,7 +597,7 @@ public void shouldExtractMultipleRequests() { @Test public void shouldSkipBidderWhenRejectedByBidderRequestHooks() { // given - doAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of(true, null))) + doAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.reject())) .when(hookStageExecutor).executeBidderRequestStage(any(), any()); final BidRequest bidRequest = givenBidRequest(givenSingleImp(singletonMap("someBidder", 1)), identity()); @@ -614,8 +614,7 @@ public void shouldPassRequestModifiedByBidderRequestHooks() { // given givenBidder(givenEmptySeatBid()); - doAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of( - false, + doAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.success( BidderRequestPayloadImpl.of(BidRequest.builder().id("bidderRequestId").build())))) .when(hookStageExecutor).executeBidderRequestStage(any(), any()); @@ -637,7 +636,7 @@ public void shouldSkipBidderWhenRejectedByRawBidderResponseHooks() { givenBidder(bidder, mock(Bidder.class), givenSeatBid(singletonList( givenBidderBid(Bid.builder().price(BigDecimal.ONE).build())))); - doAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of(true, null))) + doAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.reject())) .when(hookStageExecutor).executeRawBidderResponseStage(any(), any()); final BidRequest bidRequest = givenBidRequest(givenSingleImp(singletonMap(bidder, 1)), identity()); @@ -666,8 +665,7 @@ public void shouldPassRequestModifiedByRawBidderResponseHooks() { givenBidderBid(Bid.builder().build())))); final BidderBid hookChangedBid = BidderBid.of(Bid.builder().id("newId").build(), video, "USD"); - doAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of( - false, + doAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.success( BidderResponsePayloadImpl.of(singletonList(hookChangedBid))))) .when(hookStageExecutor).executeRawBidderResponseStage(any(), any()); @@ -3158,8 +3156,7 @@ public void shouldReturnBidResponseModifiedByAuctionResponseHooks() { given(httpBidderRequester.requestBids(any(), any(), any(), any(), any(), any(), anyBoolean())) .willReturn(Future.succeededFuture(givenSeatBid(emptyList()))); - doAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of( - false, + doAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.success( AuctionResponsePayloadImpl.of(BidResponse.builder().id("bidResponseId").build())))) .when(hookStageExecutor).executeAuctionResponseStage(any(), any()); @@ -3853,8 +3850,45 @@ public void shouldResponseWithEmptySeatBidIfBidderNotSupportRequestCurrency() { .extracting(AuctionContext::getBidRejectionTrackers) .extracting(rejectionTrackers -> rejectionTrackers.get("bidder1")) .extracting(BidRejectionTracker::getRejectedImps) - .isEqualTo(Map.of("impId1", BidRejectionReason.REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY)); + .isEqualTo(Set.of(RejectedImp.of("impId1", REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY))); + } + + @Test + public void shouldMakeBidRejectionTrackers() { + // given + final Bidder bidder1 = mock(Bidder.class); + final Bidder bidder2 = mock(Bidder.class); + givenBidder("bidder1", bidder1, givenEmptySeatBid()); + givenBidder("bidder2", bidder2, givenEmptySeatBid()); + + final Imp imp1 = givenImp(Map.of("bidder1", 1, "bidder2", 2), builder -> builder.id("impId1")); + final Imp imp2 = givenImp(Map.of("bidder1", 1, "bidder2", 2), builder -> builder.id("impId2")); + final BidRequest bidRequest = givenBidRequest(List.of(imp1, imp2)); + + final Map bidRejectionTrackers = new HashMap<>(); + bidRejectionTrackers.put("invalidBidder", new BidRejectionTracker("invalidBidder", Set.of("impId1"), 0.0)); + bidRejectionTrackers.put("bidder1", new BidRejectionTracker("bidder1", Set.of("impId1"), 0.0)); + + final AuctionContext auctionContext = givenRequestContext(bidRequest) + .toBuilder() + .bidRejectionTrackers(bidRejectionTrackers) + .build(); + given(bidderCatalog.isValidName("invalidBidder")).willReturn(false); + + // when + final Future result = target.holdAuction(auctionContext); + + // then + assertThat(result.succeeded()).isTrue(); + final Map actualTrackers = result.result().getBidRejectionTrackers(); + assertThat(actualTrackers.keySet()).containsOnly("bidder1", "bidder2"); + assertThat(actualTrackers.get("bidder1").getRejectedImps()).containsOnly( + RejectedImp.of("impId1", NO_BID), + RejectedImp.of("impId2", NO_BID)); + assertThat(actualTrackers.get("bidder2").getRejectedImps()).containsOnly( + RejectedImp.of("impId1", NO_BID), + RejectedImp.of("impId2", NO_BID)); } @Test @@ -4076,7 +4110,7 @@ private static BidRequest givenBidRequest(List imp) { return givenBidRequest(imp, identity()); } - private static Imp givenImp(T ext, Function impBuilderCustomizer) { + private static Imp givenImp(T ext, UnaryOperator impBuilderCustomizer) { return impBuilderCustomizer.apply(Imp.builder() .id(UUID.randomUUID().toString()) .ext(mapper.valueToTree(singletonMap( diff --git a/src/test/java/org/prebid/server/auction/model/BidRejectionTrackerTest.java b/src/test/java/org/prebid/server/auction/model/BidRejectionTrackerTest.java index 502b09108b5..d80327ab390 100644 --- a/src/test/java/org/prebid/server/auction/model/BidRejectionTrackerTest.java +++ b/src/test/java/org/prebid/server/auction/model/BidRejectionTrackerTest.java @@ -1,19 +1,23 @@ package org.prebid.server.auction.model; import com.iab.openrtb.response.Bid; -import org.apache.commons.lang3.tuple.Pair; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.prebid.server.bidder.model.BidderBid; import java.util.List; -import java.util.Map; import java.util.Set; import static java.util.Collections.singleton; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.entry; +import static org.prebid.server.auction.model.BidRejectionReason.ERROR_GENERAL; +import static org.prebid.server.auction.model.BidRejectionReason.ERROR_INVALID_BID_RESPONSE; +import static org.prebid.server.auction.model.BidRejectionReason.ERROR_TIMED_OUT; +import static org.prebid.server.auction.model.BidRejectionReason.NO_BID; +import static org.prebid.server.auction.model.BidRejectionReason.RESPONSE_REJECTED_BELOW_FLOOR; +import static org.prebid.server.auction.model.BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY; +import static org.prebid.server.auction.model.BidRejectionReason.RESPONSE_REJECTED_GENERAL; public class BidRejectionTrackerTest { @@ -27,7 +31,7 @@ public void setUp() { @Test public void succeedShouldRestoreImpFromImpRejection() { // given - target.rejectImp("impId1", BidRejectionReason.ERROR_GENERAL); + target.reject(RejectedImp.of("impId1", ERROR_GENERAL)); // when final BidderBid bid = BidderBid.builder().bid(Bid.builder().id("bidId1").impid("impId1").build()).build(); @@ -35,77 +39,77 @@ public void succeedShouldRestoreImpFromImpRejection() { // then assertThat(target.getRejectedImps()).isEmpty(); - assertThat(target.getRejectedBids()) - .containsOnly(entry("impId1", List.of(Pair.of(null, BidRejectionReason.ERROR_GENERAL)))); + assertThat(target.getAllRejected()) + .containsOnly(entry("impId1", List.of(RejectedImp.of("impId1", ERROR_GENERAL)))); } @Test public void succeedShouldRestoreImpFromBidRejection() { // given final BidderBid bid = BidderBid.builder().bid(Bid.builder().id("bidId1").impid("impId1").build()).build(); - target.rejectBid(bid, BidRejectionReason.ERROR_GENERAL); + target.reject(RejectedBid.of(bid, ERROR_GENERAL)); // when target.succeed(singleton(bid)); // then assertThat(target.getRejectedImps()).isEmpty(); - assertThat(target.getRejectedBids()) - .containsOnly(entry("impId1", List.of(Pair.of(bid, BidRejectionReason.ERROR_GENERAL)))); + assertThat(target.getAllRejected()) + .containsOnly(entry("impId1", List.of(RejectedBid.of(bid, ERROR_GENERAL)))); } @Test public void succeedShouldIgnoreUninvolvedImpIdsOnImpRejection() { // given - target.rejectImp("impId1", BidRejectionReason.ERROR_GENERAL); + target.reject(RejectedImp.of("impId1", ERROR_GENERAL)); // when final BidderBid bid = BidderBid.builder().bid(Bid.builder().id("bidId2").impid("impId2").build()).build(); target.succeed(singleton(bid)); // then - assertThat(target.getRejectedImps()).containsOnly(entry("impId1", BidRejectionReason.ERROR_GENERAL)); - assertThat(target.getRejectedBids()) - .containsOnly(entry("impId1", List.of(Pair.of(null, BidRejectionReason.ERROR_GENERAL)))); + assertThat(target.getRejectedImps()).containsOnly(RejectedImp.of("impId1", ERROR_GENERAL)); + assertThat(target.getAllRejected()) + .containsOnly(entry("impId1", List.of(RejectedImp.of("impId1", ERROR_GENERAL)))); } @Test public void succeedShouldIgnoreUninvolvedImpIdsOnBidRejection() { // given final BidderBid bid1 = BidderBid.builder().bid(Bid.builder().id("bidId1").impid("impId1").build()).build(); - target.rejectBid(bid1, BidRejectionReason.ERROR_GENERAL); + target.reject(RejectedBid.of(bid1, ERROR_GENERAL)); // when final BidderBid bid2 = BidderBid.builder().bid(Bid.builder().id("bidId2").impid("impId2").build()).build(); target.succeed(singleton(bid2)); // then - assertThat(target.getRejectedImps()).containsOnly(entry("impId1", BidRejectionReason.ERROR_GENERAL)); - assertThat(target.getRejectedBids()) - .containsOnly(entry("impId1", List.of(Pair.of(bid1, BidRejectionReason.ERROR_GENERAL)))); + assertThat(target.getRejectedImps()).containsOnly(RejectedImp.of("impId1", ERROR_GENERAL)); + assertThat(target.getAllRejected()) + .containsOnly(entry("impId1", List.of(RejectedBid.of(bid1, ERROR_GENERAL)))); } @Test public void rejectImpShouldRecordImpRejectionFirstTimeIfImpIdIsInvolved() { // when - target.rejectImp("impId1", BidRejectionReason.ERROR_GENERAL); + target.reject(RejectedImp.of("impId1", ERROR_GENERAL)); // then - assertThat(target.getRejectedImps()).containsOnly(entry("impId1", BidRejectionReason.ERROR_GENERAL)); - assertThat(target.getRejectedBids()) - .containsOnly(entry("impId1", List.of(Pair.of(null, BidRejectionReason.ERROR_GENERAL)))); + assertThat(target.getRejectedImps()).containsOnly(RejectedImp.of("impId1", ERROR_GENERAL)); + assertThat(target.getAllRejected()) + .containsOnly(entry("impId1", List.of(RejectedImp.of("impId1", ERROR_GENERAL)))); } @Test public void rejectBidShouldRecordBidRejectionFirstTimeIfImpIdIsInvolved() { // when final BidderBid bid = BidderBid.builder().bid(Bid.builder().id("bidId1").impid("impId1").build()).build(); - target.rejectBid(bid, BidRejectionReason.ERROR_GENERAL); + target.reject(RejectedBid.of(bid, ERROR_GENERAL)); // then - assertThat(target.getRejectedImps()).containsOnly(entry("impId1", BidRejectionReason.ERROR_GENERAL)); - assertThat(target.getRejectedBids()) - .containsOnly(entry("impId1", List.of(Pair.of(bid, BidRejectionReason.ERROR_GENERAL)))); + assertThat(target.getRejectedImps()).containsOnly(RejectedImp.of("impId1", ERROR_GENERAL)); + assertThat(target.getAllRejected()) + .containsOnly(entry("impId1", List.of(RejectedBid.of(bid, ERROR_GENERAL)))); } @Test @@ -116,72 +120,72 @@ public void rejectBidShouldRecordBidRejectionAfterPreviouslySucceededBid() { target.succeed(Set.of(bid1, bid2)); // when - target.rejectBid(bid1, BidRejectionReason.ERROR_GENERAL); + target.reject(RejectedBid.of(bid1, ERROR_GENERAL)); // then assertThat(target.getRejectedImps()).isEmpty(); - assertThat(target.getRejectedBids()) - .containsOnly(entry("impId1", List.of(Pair.of(bid1, BidRejectionReason.ERROR_GENERAL)))); + assertThat(target.getAllRejected()) + .containsOnly(entry("impId1", List.of(RejectedBid.of(bid1, ERROR_GENERAL)))); } @Test public void rejectImpShouldNotRecordImpRejectionIfImpIdIsAlreadyRejected() { // given - target.rejectImp("impId1", BidRejectionReason.ERROR_GENERAL); + target.reject(RejectedImp.of("impId1", ERROR_GENERAL)); // when - target.rejectImp("impId1", BidRejectionReason.ERROR_INVALID_BID_RESPONSE); + target.reject(RejectedImp.of("impId1", ERROR_INVALID_BID_RESPONSE)); // then - assertThat(target.getRejectedImps()).containsOnly(entry("impId1", BidRejectionReason.ERROR_GENERAL)); - assertThat(target.getRejectedBids()) + assertThat(target.getRejectedImps()).containsOnly(RejectedImp.of("impId1", ERROR_GENERAL)); + assertThat(target.getAllRejected()) .containsOnly(entry("impId1", List.of( - Pair.of(null, BidRejectionReason.ERROR_GENERAL), - Pair.of(null, BidRejectionReason.ERROR_INVALID_BID_RESPONSE)))); + RejectedImp.of("impId1", ERROR_GENERAL), + RejectedImp.of("impId1", ERROR_INVALID_BID_RESPONSE)))); } @Test public void rejectBidShouldNotRecordImpRejectionButRecordBidRejectionEvenIfImpIsAlreadyRejected() { // given final BidderBid bid1 = BidderBid.builder().bid(Bid.builder().id("bidId1").impid("impId1").build()).build(); - target.rejectBid(bid1, BidRejectionReason.RESPONSE_REJECTED_GENERAL); + target.reject(RejectedBid.of(bid1, RESPONSE_REJECTED_GENERAL)); // when final BidderBid bid2 = BidderBid.builder().bid(Bid.builder().id("bidId2").impid("impId1").build()).build(); - target.rejectBid(bid2, BidRejectionReason.RESPONSE_REJECTED_BELOW_FLOOR); + target.reject(RejectedBid.of(bid2, RESPONSE_REJECTED_BELOW_FLOOR)); // then assertThat(target.getRejectedImps()) - .containsOnly(entry("impId1", BidRejectionReason.RESPONSE_REJECTED_GENERAL)); - assertThat(target.getRejectedBids()) + .containsOnly(RejectedImp.of("impId1", RESPONSE_REJECTED_GENERAL)); + assertThat(target.getAllRejected()) .containsOnly(entry("impId1", List.of( - Pair.of(bid1, BidRejectionReason.RESPONSE_REJECTED_GENERAL), - Pair.of(bid2, BidRejectionReason.RESPONSE_REJECTED_BELOW_FLOOR)))); + RejectedBid.of(bid1, RESPONSE_REJECTED_GENERAL), + RejectedBid.of(bid2, RESPONSE_REJECTED_BELOW_FLOOR)))); } @Test - public void rejectAllImpsShouldTryRejectingEachImpId() { + public void rejectAllShouldTryRejectingEachImpId() { // given target = new BidRejectionTracker("bidder", Set.of("impId1", "impId2", "impId3"), 0); - target.rejectImp("impId1", BidRejectionReason.NO_BID); + target.reject(RejectedImp.of("impId1", NO_BID)); // when - target.rejectAllImps(BidRejectionReason.ERROR_TIMED_OUT); + target.rejectAll(ERROR_TIMED_OUT); // then assertThat(target.getRejectedImps()) - .isEqualTo(Map.of( - "impId1", BidRejectionReason.NO_BID, - "impId2", BidRejectionReason.ERROR_TIMED_OUT, - "impId3", BidRejectionReason.ERROR_TIMED_OUT)); + .containsOnly( + RejectedImp.of("impId1", NO_BID), + RejectedImp.of("impId2", ERROR_TIMED_OUT), + RejectedImp.of("impId3", ERROR_TIMED_OUT)); - assertThat(target.getRejectedBids()) + assertThat(target.getAllRejected()) .containsOnly( entry("impId1", List.of( - Pair.of(null, BidRejectionReason.NO_BID), - Pair.of(null, BidRejectionReason.ERROR_TIMED_OUT))), - entry("impId2", List.of(Pair.of(null, BidRejectionReason.ERROR_TIMED_OUT))), - entry("impId3", List.of(Pair.of(null, BidRejectionReason.ERROR_TIMED_OUT)))); + RejectedImp.of("impId1", NO_BID), + RejectedImp.of("impId1", ERROR_TIMED_OUT))), + entry("impId2", List.of(RejectedImp.of("impId2", ERROR_TIMED_OUT))), + entry("impId3", List.of(RejectedImp.of("impId3", ERROR_TIMED_OUT)))); } @Test @@ -189,28 +193,31 @@ public void rejectBidsShouldTryRejectingEachBid() { // given target = new BidRejectionTracker("bidder", Set.of("impId1", "impId2", "impId3"), 0); final BidderBid bid0 = BidderBid.builder().bid(Bid.builder().id("bidId0").impid("impId1").build()).build(); - target.rejectBid(bid0, BidRejectionReason.RESPONSE_REJECTED_GENERAL); + target.reject(RejectedBid.of(bid0, RESPONSE_REJECTED_GENERAL)); // when final BidderBid bid1 = BidderBid.builder().bid(Bid.builder().id("bidId1").impid("impId1").build()).build(); final BidderBid bid2 = BidderBid.builder().bid(Bid.builder().id("bidId2").impid("impId2").build()).build(); final BidderBid bid3 = BidderBid.builder().bid(Bid.builder().id("bidId3").impid("impId3").build()).build(); - target.rejectBids(Set.of(bid1, bid2, bid3), BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY); + target.reject(Set.of( + RejectedBid.of(bid1, RESPONSE_REJECTED_DSA_PRIVACY), + RejectedBid.of(bid2, RESPONSE_REJECTED_DSA_PRIVACY), + RejectedBid.of(bid3, RESPONSE_REJECTED_DSA_PRIVACY))); // then assertThat(target.getRejectedImps()) - .isEqualTo(Map.of( - "impId1", BidRejectionReason.RESPONSE_REJECTED_GENERAL, - "impId2", BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY, - "impId3", BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); + .containsOnly( + RejectedImp.of("impId1", RESPONSE_REJECTED_GENERAL), + RejectedImp.of("impId2", RESPONSE_REJECTED_DSA_PRIVACY), + RejectedImp.of("impId3", RESPONSE_REJECTED_DSA_PRIVACY)); - assertThat(target.getRejectedBids()) + assertThat(target.getAllRejected()) .containsOnly( entry("impId1", List.of( - Pair.of(bid0, BidRejectionReason.RESPONSE_REJECTED_GENERAL), - Pair.of(bid1, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY))), - entry("impId2", List.of(Pair.of(bid2, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY))), - entry("impId3", List.of(Pair.of(bid3, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)))); + RejectedBid.of(bid0, RESPONSE_REJECTED_GENERAL), + RejectedBid.of(bid1, RESPONSE_REJECTED_DSA_PRIVACY))), + entry("impId2", List.of(RejectedBid.of(bid2, RESPONSE_REJECTED_DSA_PRIVACY))), + entry("impId3", List.of(RejectedBid.of(bid3, RESPONSE_REJECTED_DSA_PRIVACY)))); } @Test @@ -221,14 +228,6 @@ public void getRejectedImpsShouldTreatUnsuccessfulImpsAsNoBidRejection() { target.succeed(singleton(bid)); // then - assertThat(target.getRejectedImps()).containsOnly(entry("impId1", BidRejectionReason.NO_BID)); - } - - @Test - public void rejectImpShouldFailRejectingWithReasonThatImpliesExistingBidToReject() { - assertThatThrownBy(() -> target.rejectImp("impId1", BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("The non-bid code 300 and higher assumes " - + "that there is a rejected bid that shouldn't be lost"); + assertThat(target.getRejectedImps()).containsOnly(RejectedImp.of("impId1", NO_BID)); } } diff --git a/src/test/java/org/prebid/server/auction/requestfactory/Ortb2RequestFactoryTest.java b/src/test/java/org/prebid/server/auction/requestfactory/Ortb2RequestFactoryTest.java index c6bad5c5e94..3d5b5a9cd8d 100644 --- a/src/test/java/org/prebid/server/auction/requestfactory/Ortb2RequestFactoryTest.java +++ b/src/test/java/org/prebid/server/auction/requestfactory/Ortb2RequestFactoryTest.java @@ -147,21 +147,18 @@ public void setUp() { given(timeoutResolver.limitToMax(any())).willReturn(2000L); given(hookStageExecutor.executeEntrypointStage(any(), any(), any(), any())) - .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of( - false, + .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.success( EntrypointPayloadImpl.of( invocation.getArgument(0), invocation.getArgument(1), invocation.getArgument(2))))); given(hookStageExecutor.executeRawAuctionRequestStage(any())) - .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of( - false, + .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.success( AuctionRequestPayloadImpl.of(invocation.getArgument(0))))); given(hookStageExecutor.executeProcessedAuctionRequestStage(any())) - .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of( - false, + .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.success( AuctionRequestPayloadImpl.of(invocation.getArgument(0))))); givenTarget(90); @@ -1096,8 +1093,7 @@ public void executeEntrypointHooksShouldReturnExpectedHttpRequest() { .add("DHT", "1") .build(); given(hookStageExecutor.executeEntrypointStage(any(), any(), any(), any())) - .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of( - false, + .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.success( EntrypointPayloadImpl.of( updatedQueryParam, headerParams, @@ -1133,7 +1129,7 @@ public void shouldReturnFailedFutureIfEntrypointHooksRejectedRequest() { given(httpServerRequest.headers()).willReturn(MultiMap.caseInsensitiveMultiMap()); given(hookStageExecutor.executeEntrypointStage(any(), any(), any(), any())) - .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of(true, null))); + .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.reject())); final AuctionContext auctionContext = AuctionContext.builder().hookExecutionContext(hookExecutionContext).build(); @@ -1155,8 +1151,8 @@ public void shouldUseBidRequestModifiedByRawAuctionRequestHooks() { .app(App.builder().bundle("org.company.application").build()) .build(); given(hookStageExecutor.executeRawAuctionRequestStage(any())) - .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of( - false, AuctionRequestPayloadImpl.of(modifiedBidRequest)))); + .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.success( + AuctionRequestPayloadImpl.of(modifiedBidRequest)))); final AuctionContext auctionContext = AuctionContext.builder() .bidRequest(BidRequest.builder().site(Site.builder().build()).build()) @@ -1174,7 +1170,7 @@ public void shouldUseBidRequestModifiedByRawAuctionRequestHooks() { public void shouldReturnFailedFutureIfRawAuctionRequestHookRejectedRequest() { // given given(hookStageExecutor.executeRawAuctionRequestStage(any())) - .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of(true, null))); + .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.reject())); final AuctionContext auctionContext = AuctionContext.builder() .hookExecutionContext(hookExecutionContext) @@ -1197,8 +1193,8 @@ public void shouldUseBidRequestModifiedByProcessedAuctionRequestHooks() { .app(App.builder().bundle("org.company.application").build()) .build(); given(hookStageExecutor.executeProcessedAuctionRequestStage(any())) - .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of( - false, AuctionRequestPayloadImpl.of(modifiedBidRequest)))); + .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.success( + AuctionRequestPayloadImpl.of(modifiedBidRequest)))); final AuctionContext auctionContext = AuctionContext.builder() .bidRequest(BidRequest.builder().site(Site.builder().build()).build()) @@ -1216,7 +1212,7 @@ public void shouldUseBidRequestModifiedByProcessedAuctionRequestHooks() { public void shouldReturnFailedFutureIfProcessedAuctionRequestHookRejectedRequest() { // given given(hookStageExecutor.executeProcessedAuctionRequestStage(any())) - .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of(true, null))); + .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.reject())); final AuctionContext auctionContext = AuctionContext.builder() .hookExecutionContext(hookExecutionContext) diff --git a/src/test/java/org/prebid/server/bidder/HttpBidderRequesterTest.java b/src/test/java/org/prebid/server/bidder/HttpBidderRequesterTest.java index 2b067ab06d8..9431e163bf5 100644 --- a/src/test/java/org/prebid/server/bidder/HttpBidderRequesterTest.java +++ b/src/test/java/org/prebid/server/bidder/HttpBidderRequesterTest.java @@ -25,6 +25,7 @@ import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.model.BidderRequest; +import org.prebid.server.auction.model.Rejected; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderCall; import org.prebid.server.bidder.model.BidderError; @@ -228,7 +229,7 @@ public void shouldPassStoredResponseToBidderMakeBidsMethodAndReturnSeatBids() { .isEqualTo("storedResponse"); assertThat(bidderSeatBid.getBids()).hasSameElementsAs(bids); - verify(bidRejectionTracker, never()).rejectImp(anyString(), any()); + verify(bidRejectionTracker, never()).reject(any(Rejected.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -267,7 +268,7 @@ public void shouldMakeRequestToBidderWhenStoredResponseDefinedButBidderCreatesMo // then verify(httpClient, times(2)).request(any(), anyString(), any(), any(byte[].class), anyLong()); - verify(bidRejectionTracker, never()).rejectImp(anyString(), any()); + verify(bidRejectionTracker, never()).reject(any(Rejected.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -302,7 +303,7 @@ public void shouldSendPopulatedGetRequestWithoutBody() { // then verify(httpClient).request(any(), anyString(), any(), (byte[]) isNull(), anyLong()); - verify(bidRejectionTracker, never()).rejectImp(anyString(), any()); + verify(bidRejectionTracker, never()).reject(any(Rejected.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -338,7 +339,7 @@ public void shouldSendMultipleRequests() throws JsonProcessingException { // then verify(httpClient, times(2)).request(any(), anyString(), any(), any(byte[].class), anyLong()); - verify(bidRejectionTracker, never()).rejectImp(anyString(), any()); + verify(bidRejectionTracker, never()).reject(any(Rejected.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -371,7 +372,7 @@ public void shouldReturnBidsCreatedByBidder() { // then assertThat(bidderSeatBid.getBids()).hasSameElementsAs(bids); - verify(bidRejectionTracker, never()).rejectImp(anyString(), any()); + verify(bidRejectionTracker, never()).reject(any(Rejected.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -404,7 +405,7 @@ public void shouldReturnBidsCreatedByMakeBids() { // then assertThat(bidderSeatBid.getBids()).hasSameElementsAs(bids); - verify(bidRejectionTracker, never()).rejectImp(anyString(), any()); + verify(bidRejectionTracker, never()).reject(any(Rejected.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -446,7 +447,7 @@ public void shouldReturnFledgeCreatedByBidder() { assertThat(bidderSeatBid.getBids()).hasSameElementsAs(bids); assertThat(bidderSeatBid.getFledgeAuctionConfigs()).hasSameElementsAs(fledgeAuctionConfigs); - verify(bidRejectionTracker, never()).rejectImp(anyString(), any()); + verify(bidRejectionTracker, never()).reject(any(Rejected.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -493,7 +494,7 @@ public void shouldReturnExtIgiCreatedByBidder() { assertThat(bidderSeatBid.getBids()).hasSameElementsAs(bids); assertThat(bidderSeatBid.getIgi()).containsExactlyElementsOf(igi); - verify(bidRejectionTracker, never()).rejectImp(anyString(), any()); + verify(bidRejectionTracker, never()).reject(any(Rejected.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -529,7 +530,7 @@ public void shouldCompressRequestBodyIfContentEncodingHeaderIsGzip() { verify(httpClient).request(any(), anyString(), any(), actualRequestBody.capture(), anyLong()); assertThat(actualRequestBody.getValue()).isNotSameAs(EMPTY_BYTE_BODY); - verify(bidRejectionTracker, never()).rejectImp(anyString(), any()); + verify(bidRejectionTracker, never()).reject(any(Rejected.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -627,7 +628,7 @@ public void processBids(List bids) { assertThat(bidderSeatBid.getBids()).containsOnly(bidderBidDeal1, bidderBidDeal2); - verify(bidRejectionTracker, never()).rejectImp(anyString(), any()); + verify(bidRejectionTracker, never()).reject(any(Rejected.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -676,7 +677,7 @@ public void shouldFinishWhenAllDealRequestsAreFinishedAndNoDealsProvided() { assertThat(bidderSeatBid.getBids()).contains(bidderBid, bidderBid, bidderBid, bidderBid); - verify(bidRejectionTracker, never()).rejectImp(anyString(), any()); + verify(bidRejectionTracker, never()).reject(any(Rejected.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -743,7 +744,7 @@ public void shouldReturnFullDebugInfoIfDebugEnabled() throws JsonProcessingExcep .status(200) .build()); - verify(bidRejectionTracker, never()).rejectImp(anyString(), any()); + verify(bidRejectionTracker, never()).reject(any(Rejected.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -853,7 +854,7 @@ public void shouldNotReturnSensitiveHeadersInFullDebugInfo() .extracting(ExtHttpCall::getRequestheaders) .containsExactly(singletonMap("headerKey", singletonList("headerValue"))); - verify(bidRejectionTracker, never()).rejectImp(anyString(), any()); + verify(bidRejectionTracker, never()).reject(any(Rejected.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -1238,7 +1239,7 @@ public void shouldNotMakeBidsIfResponseStatusIs204() { verify(bidder, never()).makeBidderResponse(any(), any()); verify(bidder, never()).makeBids(any(), any()); - verify(bidRejectionTracker, never()).rejectImp(anyString(), any()); + verify(bidRejectionTracker, never()).reject(any(Rejected.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } diff --git a/src/test/java/org/prebid/server/floors/BasicPriceFloorEnforcerTest.java b/src/test/java/org/prebid/server/floors/BasicPriceFloorEnforcerTest.java index 724aef1f391..d810d8a0bcd 100644 --- a/src/test/java/org/prebid/server/floors/BasicPriceFloorEnforcerTest.java +++ b/src/test/java/org/prebid/server/floors/BasicPriceFloorEnforcerTest.java @@ -13,6 +13,7 @@ import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.model.BidderRequest; import org.prebid.server.auction.model.BidderResponse; +import org.prebid.server.auction.model.RejectedBid; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderError; import org.prebid.server.bidder.model.BidderSeatBid; @@ -349,7 +350,7 @@ public void shouldRejectBidsHavingPriceBelowFloor() { final BidderBid rejectedBid = BidderBid.of( Bid.builder().id("bidId1").impid("impId1").price(BigDecimal.ONE).build(), null, null); - verify(rejectionTracker).rejectBid(rejectedBid, BidRejectionReason.RESPONSE_REJECTED_BELOW_FLOOR); + verify(rejectionTracker).reject(RejectedBid.of(rejectedBid, BidRejectionReason.RESPONSE_REJECTED_BELOW_FLOOR)); assertThat(singleton(result)) .extracting(AuctionParticipation::getBidderResponse) .extracting(BidderResponse::getSeatBid) diff --git a/src/test/java/org/prebid/server/handler/openrtb2/AmpHandlerTest.java b/src/test/java/org/prebid/server/handler/openrtb2/AmpHandlerTest.java index aee1397e4c9..72ea07c12da 100644 --- a/src/test/java/org/prebid/server/handler/openrtb2/AmpHandlerTest.java +++ b/src/test/java/org/prebid/server/handler/openrtb2/AmpHandlerTest.java @@ -179,8 +179,7 @@ public void setUp() { given(prebidVersionProvider.getNameVersionRecord()).willReturn("pbs-java/1.00"); given(hookStageExecutor.executeExitpointStage(any(), any(), any())) - .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of( - false, + .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.success( ExitpointPayloadImpl.of(invocation.getArgument(0), invocation.getArgument(1))))); given(hooksMetricsService.updateHooksMetrics(any())).willAnswer(invocation -> invocation.getArgument(0)); diff --git a/src/test/java/org/prebid/server/handler/openrtb2/AuctionHandlerTest.java b/src/test/java/org/prebid/server/handler/openrtb2/AuctionHandlerTest.java index 1618caea8d2..0210095affd 100644 --- a/src/test/java/org/prebid/server/handler/openrtb2/AuctionHandlerTest.java +++ b/src/test/java/org/prebid/server/handler/openrtb2/AuctionHandlerTest.java @@ -167,8 +167,7 @@ public void setUp() { given(prebidVersionProvider.getNameVersionRecord()).willReturn("pbs-java/1.00"); given(hookStageExecutor.executeExitpointStage(any(), any(), any())) - .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of( - false, + .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.success( ExitpointPayloadImpl.of(invocation.getArgument(0), invocation.getArgument(1))))); given(hooksMetricsService.updateHooksMetrics(any())).willAnswer(invocation -> invocation.getArgument(0)); diff --git a/src/test/java/org/prebid/server/handler/openrtb2/VideoHandlerTest.java b/src/test/java/org/prebid/server/handler/openrtb2/VideoHandlerTest.java index 64efc34c093..2e89435181b 100644 --- a/src/test/java/org/prebid/server/handler/openrtb2/VideoHandlerTest.java +++ b/src/test/java/org/prebid/server/handler/openrtb2/VideoHandlerTest.java @@ -121,8 +121,7 @@ public void setUp() { given(prebidVersionProvider.getNameVersionRecord()).willReturn("pbs-java/1.00"); given(hookStageExecutor.executeExitpointStage(any(), any(), any())) - .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.of( - false, + .willAnswer(invocation -> Future.succeededFuture(HookStageExecutionResult.success( ExitpointPayloadImpl.of(invocation.getArgument(0), invocation.getArgument(1))))); given(hooksMetricsService.updateHooksMetrics(any())).willAnswer(invocation -> invocation.getArgument(0)); diff --git a/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java b/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java index 7f1d29925c1..6e58fe52f7b 100644 --- a/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java +++ b/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java @@ -26,8 +26,11 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.prebid.server.VertxTest; import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.model.BidderRequest; import org.prebid.server.auction.model.BidderResponse; +import org.prebid.server.auction.model.RejectedBid; +import org.prebid.server.auction.model.RejectedImp; import org.prebid.server.auction.model.debug.DebugContext; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderSeatBid; @@ -84,7 +87,6 @@ import org.prebid.server.hooks.v1.exitpoint.ExitpointPayload; import org.prebid.server.model.CaseInsensitiveMultiMap; import org.prebid.server.model.Endpoint; -import org.prebid.server.proto.openrtb.ext.response.BidType; import org.prebid.server.settings.model.Account; import org.prebid.server.settings.model.AccountHooksConfiguration; import org.prebid.server.settings.model.HooksAdminConfig; @@ -95,8 +97,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.BiFunction; import java.util.function.Function; +import java.util.function.Predicate; import java.util.function.UnaryOperator; import static java.util.Arrays.asList; @@ -117,7 +121,15 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.prebid.server.assertion.FutureAssertion.assertThat; +import static org.prebid.server.auction.model.BidRejectionReason.REQUEST_BLOCKED_GENERAL; +import static org.prebid.server.auction.model.BidRejectionReason.REQUEST_BLOCKED_OPTIMIZED; +import static org.prebid.server.auction.model.BidRejectionReason.REQUEST_BLOCKED_PRIVACY; +import static org.prebid.server.auction.model.BidRejectionReason.REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY; +import static org.prebid.server.auction.model.BidRejectionReason.REQUEST_BLOCKED_UNSUPPORTED_MEDIA_TYPE; import static org.prebid.server.hooks.v1.PayloadUpdate.identity; +import static org.prebid.server.proto.openrtb.ext.response.BidType.banner; +import static org.prebid.server.proto.openrtb.ext.response.BidType.video; +import static org.prebid.server.proto.openrtb.ext.response.BidType.xNative; @ExtendWith(MockitoExtension.class) @ExtendWith(VertxExtension.class) @@ -203,9 +215,13 @@ public void shouldTolerateMissingHostAndDefaultAccountExecutionPlans() { final CaseInsensitiveMultiMap headers = CaseInsensitiveMultiMap.empty(); final String body = "body"; + final AuctionContext givenAuctionContext = AuctionContext.builder() + .hookExecutionContext(HookExecutionContext.of(Endpoint.openrtb2_auction)) + .build(); + // when final Future> future = executor.executeEntrypointStage( - queryParams, headers, body, HookExecutionContext.of(Endpoint.openrtb2_auction)); + queryParams, headers, body, givenAuctionContext); // then assertThat(future).isSucceeded(); @@ -275,13 +291,16 @@ public void shouldExecuteEntrypointHooksHappyPath(VertxTestContext context) { EndpointExecutionPlan.of(singletonMap(Stage.entrypoint, execPlanTwoGroupsTwoHooksEach()))))); final HookExecutionContext hookExecutionContext = HookExecutionContext.of(Endpoint.openrtb2_auction); + final AuctionContext givenAuctionContext = AuctionContext.builder() + .hookExecutionContext(hookExecutionContext) + .build(); // when final Future> future = executor.executeEntrypointStage( CaseInsensitiveMultiMap.empty(), CaseInsensitiveMultiMap.empty(), "body", - hookExecutionContext); + givenAuctionContext); // then future.onComplete(context.succeeding(result -> { @@ -357,13 +376,16 @@ public void shouldBypassEntrypointHooksWhenNoPlanForEndpoint(VertxTestContext co executionPlan(emptyMap())); final HookExecutionContext hookExecutionContext = HookExecutionContext.of(Endpoint.openrtb2_amp); + final AuctionContext givenAuctionContext = AuctionContext.builder() + .hookExecutionContext(hookExecutionContext) + .build(); // when final Future> future = executor.executeEntrypointStage( CaseInsensitiveMultiMap.empty(), CaseInsensitiveMultiMap.empty(), "body", - hookExecutionContext); + givenAuctionContext); // then future.onComplete(context.succeeding(result -> { @@ -389,13 +411,16 @@ public void shouldBypassEntrypointHooksWhenNoPlanForStage(VertxTestContext conte EndpointExecutionPlan.of(emptyMap())))); final HookExecutionContext hookExecutionContext = HookExecutionContext.of(Endpoint.openrtb2_auction); + final AuctionContext givenAuctionContext = AuctionContext.builder() + .hookExecutionContext(hookExecutionContext) + .build(); // when final Future> future = executor.executeEntrypointStage( CaseInsensitiveMultiMap.empty(), CaseInsensitiveMultiMap.empty(), "body", - hookExecutionContext); + givenAuctionContext); // then future.onComplete(context.succeeding(result -> { @@ -454,16 +479,20 @@ public void shouldBypassEntrypointHooksThatAreDisabled(VertxTestContext context) vertx, clock, jacksonMapper, - false); + false, + 0.0); final HookExecutionContext hookExecutionContext = HookExecutionContext.of(Endpoint.openrtb2_auction); + final AuctionContext givenAuctionContext = AuctionContext.builder() + .hookExecutionContext(hookExecutionContext) + .build(); // when final Future> future = executor.executeEntrypointStage( CaseInsensitiveMultiMap.empty(), CaseInsensitiveMultiMap.empty(), "body", - hookExecutionContext); + givenAuctionContext); // then future.onComplete(context.succeeding(result -> { @@ -511,13 +540,16 @@ public void shouldExecuteEntrypointHooksToleratingMisbehavingHooks(VertxTestCont EndpointExecutionPlan.of(singletonMap(Stage.entrypoint, execPlanTwoGroupsTwoHooksEach()))))); final HookExecutionContext hookExecutionContext = HookExecutionContext.of(Endpoint.openrtb2_auction); + final AuctionContext givenAuctionContext = AuctionContext.builder() + .hookExecutionContext(hookExecutionContext) + .build(); // when final Future> future = executor.executeEntrypointStage( CaseInsensitiveMultiMap.empty(), CaseInsensitiveMultiMap.empty(), "body", - hookExecutionContext); + givenAuctionContext); // then future.onComplete(context.succeeding(result -> { @@ -618,6 +650,9 @@ public void shouldExecuteEntrypointHooksToleratingTimeoutAndFailedFuture(VertxTe payload.queryParams(), payload.headers(), payload.body() + "-jkl")))); final HookExecutionContext hookExecutionContext = HookExecutionContext.of(Endpoint.openrtb2_auction); + final AuctionContext givenAuctionContext = AuctionContext.builder() + .hookExecutionContext(hookExecutionContext) + .build(); final HookStageExecutor executor = createExecutor( executionPlan(singletonMap( @@ -629,7 +664,7 @@ public void shouldExecuteEntrypointHooksToleratingTimeoutAndFailedFuture(VertxTe CaseInsensitiveMultiMap.empty(), CaseInsensitiveMultiMap.empty(), "body", - hookExecutionContext); + givenAuctionContext); // then future.onComplete(context.succeeding(result -> { @@ -722,13 +757,16 @@ public void shouldExecuteEntrypointHooksHonoringStatusAndAction(VertxTestContext EndpointExecutionPlan.of(singletonMap(Stage.entrypoint, execPlanTwoGroupsTwoHooksEach()))))); final HookExecutionContext hookExecutionContext = HookExecutionContext.of(Endpoint.openrtb2_auction); + final AuctionContext givenAuctionContext = AuctionContext.builder() + .hookExecutionContext(hookExecutionContext) + .build(); // when final Future> future = executor.executeEntrypointStage( CaseInsensitiveMultiMap.empty(), CaseInsensitiveMultiMap.empty(), "body", - hookExecutionContext); + givenAuctionContext); // then future.onComplete(context.succeeding(result -> { @@ -810,13 +848,16 @@ public void shouldExecuteEntrypointHooksWhenRequestIsRejectedByFirstGroup(VertxT HookId.of("module-beta", "hook-a")))))))))); final HookExecutionContext hookExecutionContext = HookExecutionContext.of(Endpoint.openrtb2_auction); + final AuctionContext givenAuctionContext = AuctionContext.builder() + .hookExecutionContext(hookExecutionContext) + .build(); // when final Future> future = executor.executeEntrypointStage( CaseInsensitiveMultiMap.empty(), CaseInsensitiveMultiMap.empty(), "body", - hookExecutionContext); + givenAuctionContext); // then future.onComplete(context.succeeding(result -> { @@ -887,13 +928,16 @@ public void shouldExecuteEntrypointHooksWhenRequestIsRejectedBySecondGroup(Vertx EndpointExecutionPlan.of(singletonMap(Stage.entrypoint, execPlanTwoGroupsTwoHooksEach()))))); final HookExecutionContext hookExecutionContext = HookExecutionContext.of(Endpoint.openrtb2_auction); + final AuctionContext givenAuctionContext = AuctionContext.builder() + .hookExecutionContext(hookExecutionContext) + .build(); // when final Future> future = executor.executeEntrypointStage( CaseInsensitiveMultiMap.empty(), CaseInsensitiveMultiMap.empty(), "body", - hookExecutionContext); + givenAuctionContext); // then future.onComplete(context.succeeding(result -> { @@ -987,13 +1031,16 @@ public void shouldExecuteEntrypointHooksToleratingMisbehavingInvocationResult(Ve EndpointExecutionPlan.of(singletonMap(Stage.entrypoint, execPlanTwoGroupsTwoHooksEach()))))); final HookExecutionContext hookExecutionContext = HookExecutionContext.of(Endpoint.openrtb2_auction); + final AuctionContext givenAuctionContext = AuctionContext.builder() + .hookExecutionContext(hookExecutionContext) + .build(); // when final Future> future = executor.executeEntrypointStage( CaseInsensitiveMultiMap.empty(), CaseInsensitiveMultiMap.empty(), "body", - hookExecutionContext); + givenAuctionContext); // then future.onComplete(context.succeeding(result -> { @@ -1081,13 +1128,16 @@ public void shouldExecuteEntrypointHooksAndStoreResultInExecutionContext(VertxTe Stage.entrypoint, execPlanOneGroupOneHook("module-alpha", "hook-a")))))); final HookExecutionContext hookExecutionContext = HookExecutionContext.of(Endpoint.openrtb2_auction); + final AuctionContext givenAuctionContext = AuctionContext.builder() + .hookExecutionContext(hookExecutionContext) + .build(); // when final Future> future = executor.executeEntrypointStage( CaseInsensitiveMultiMap.empty(), CaseInsensitiveMultiMap.empty(), "body", - hookExecutionContext); + givenAuctionContext); // then future.onComplete(context.succeeding(result -> { @@ -1135,13 +1185,16 @@ public void shouldExecuteEntrypointHooksAndPassInvocationContext(VertxTestContex EndpointExecutionPlan.of(singletonMap(Stage.entrypoint, execPlanTwoGroupsTwoHooksEach()))))); final HookExecutionContext hookExecutionContext = HookExecutionContext.of(Endpoint.openrtb2_auction); + final AuctionContext givenAuctionContext = AuctionContext.builder() + .hookExecutionContext(hookExecutionContext) + .build(); // when final Future> future = executor.executeEntrypointStage( CaseInsensitiveMultiMap.empty(), CaseInsensitiveMultiMap.empty(), "body", - hookExecutionContext); + givenAuctionContext); // then future.onComplete(context.succeeding(result -> { @@ -1428,7 +1481,8 @@ public void shouldNotExecuteRawAuctionRequestHooksWhenAccountConfigIsNotRequired vertx, clock, jacksonMapper, - false); + false, + 0.0); final HookExecutionContext hookExecutionContext = HookExecutionContext.of(Endpoint.openrtb2_auction); @@ -1545,7 +1599,8 @@ public void shouldExecuteRawAuctionRequestHooksWhenAccountConfigIsRequired(Vertx vertx, clock, jacksonMapper, - true); + true, + 0.0); final HookExecutionContext hookExecutionContext = HookExecutionContext.of(Endpoint.openrtb2_auction); @@ -1663,6 +1718,119 @@ public void shouldExecuteRawAuctionRequestHooksHappyPath(VertxTestContext contex })); } + @Test + public void shouldExecuteRawAuctionRequestHooksWithAllRejectionsPopulated(VertxTestContext context) { + // given + givenRawAuctionRequestHook( + "module-alpha", + "hook-a", + immediateHook(InvocationResultUtils.succeeded( + payload -> AuctionRequestPayloadImpl.of(payload.bidRequest().toBuilder().at(1).build()), + Map.of("bidderA", List.of( + RejectedImp.of("impId1", REQUEST_BLOCKED_OPTIMIZED), + RejectedImp.of("impId3", REQUEST_BLOCKED_GENERAL)), + "bidderC", List.of( + RejectedImp.of("impId4", REQUEST_BLOCKED_GENERAL)))))); + + givenRawAuctionRequestHook( + "module-alpha", + "hook-b", + immediateHook(InvocationResultUtils.succeeded( + payload -> AuctionRequestPayloadImpl.of(payload.bidRequest().toBuilder().id("id").build()), + Map.of("bidderB", List.of( + RejectedImp.of("impId2", REQUEST_BLOCKED_PRIVACY), + RejectedImp.of("impId3", REQUEST_BLOCKED_GENERAL)), + "bidderC", List.of( + RejectedImp.of("impId4", REQUEST_BLOCKED_GENERAL)))))); + + givenRawAuctionRequestHook( + "module-beta", + "hook-a", + immediateHook(InvocationResultUtils.succeeded( + payload -> AuctionRequestPayloadImpl.of(payload.bidRequest().toBuilder().test(1).build()), + Map.of("bidderA", List.of( + RejectedImp.of("impId1", REQUEST_BLOCKED_UNSUPPORTED_MEDIA_TYPE), + RejectedImp.of("impId3", REQUEST_BLOCKED_GENERAL)), + "bidderC", List.of( + RejectedImp.of("impId4", REQUEST_BLOCKED_GENERAL)))))); + + givenRawAuctionRequestHook( + "module-beta", + "hook-b", + immediateHook(InvocationResultUtils.succeeded( + payload -> AuctionRequestPayloadImpl.of(payload.bidRequest().toBuilder().tmax(1000L).build()), + Map.of("bidderB", List.of( + RejectedImp.of("impId2", REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY), + RejectedImp.of("impId3", REQUEST_BLOCKED_GENERAL)), + "bidderC", List.of( + RejectedImp.of("impId4", REQUEST_BLOCKED_GENERAL)))))); + + final HookStageExecutor executor = createExecutor( + executionPlan(singletonMap( + Endpoint.openrtb2_auction, + EndpointExecutionPlan.of(singletonMap( + Stage.raw_auction_request, + execPlanTwoGroupsTwoHooksEach()))))); + + final HookExecutionContext hookExecutionContext = HookExecutionContext.of(Endpoint.openrtb2_auction); + + // when + final AuctionContext givenAuctionContext = AuctionContext.builder() + .bidRequest(BidRequest.builder().build()) + .account(Account.empty("accountId")) + .hookExecutionContext(hookExecutionContext) + .debugContext(DebugContext.empty()) + .bidRejectionTrackers(new HashMap<>()) + .build(); + final Future> future = executor.executeRawAuctionRequestStage( + givenAuctionContext); + + // then + future.onComplete(context.succeeding(result -> { + assertThat(result).isNotNull(); + assertThat(result.getPayload()).isNotNull().satisfies(payload -> + assertThat(payload.bidRequest()).isEqualTo(BidRequest.builder() + .at(1) + .id("id") + .test(1) + .tmax(1000L) + .build())); + + assertThat(hookExecutionContext.getStageOutcomes()) + .hasEntrySatisfying( + Stage.raw_auction_request, + stageOutcomes -> assertThat(stageOutcomes) + .hasSize(1) + .extracting(StageExecutionOutcome::getEntity) + .containsOnly("auction-request")); + + context.completeNow(); + })); + + final Map bidRejectionTrackers = givenAuctionContext.getBidRejectionTrackers(); + assertThat(bidRejectionTrackers.keySet()).containsOnly("bidderA", "bidderB", "bidderC"); + assertThat(bidRejectionTrackers.get("bidderA").getAllRejected()).containsOnly( + entry("impId1", List.of( + RejectedImp.of("impId1", REQUEST_BLOCKED_OPTIMIZED), + RejectedImp.of("impId1", REQUEST_BLOCKED_UNSUPPORTED_MEDIA_TYPE))), + entry("impId3", List.of( + RejectedImp.of("impId3", REQUEST_BLOCKED_GENERAL), + RejectedImp.of("impId3", REQUEST_BLOCKED_GENERAL)))); + assertThat(bidRejectionTrackers.get("bidderB").getAllRejected()).containsOnly( + entry("impId2", List.of( + RejectedImp.of("impId2", REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY), + RejectedImp.of("impId2", REQUEST_BLOCKED_PRIVACY))), + entry("impId3", List.of( + RejectedImp.of("impId3", REQUEST_BLOCKED_GENERAL), + RejectedImp.of("impId3", REQUEST_BLOCKED_GENERAL)))); + assertThat(bidRejectionTrackers.get("bidderC").getAllRejected()).containsOnly( + entry("impId4", List.of( + RejectedImp.of("impId4", REQUEST_BLOCKED_GENERAL), + RejectedImp.of("impId4", REQUEST_BLOCKED_GENERAL), + RejectedImp.of("impId4", REQUEST_BLOCKED_GENERAL), + RejectedImp.of("impId4", REQUEST_BLOCKED_GENERAL)))); + } + @Test public void shouldExecuteRawAuctionRequestHooksAndPassAuctionInvocationContext(VertxTestContext context) { // given @@ -2347,7 +2515,7 @@ public void shouldExecuteRawBidderResponseHooksHappyPath(VertxTestContext contex final Future> future1 = executor.executeRawBidderResponseStage( BidderResponse.of( "bidder1", - BidderSeatBid.of(singletonList(BidderBid.of(Bid.builder().build(), BidType.banner, "USD"))), + BidderSeatBid.of(singletonList(BidderBid.of(Bid.builder().build(), banner, "USD"))), 0), auctionContext); final Future> future2 = executor.executeRawBidderResponseStage( @@ -2368,7 +2536,7 @@ public void shouldExecuteRawBidderResponseHooksHappyPath(VertxTestContext contex .cid("cid") .adm("adm") .build(), - BidType.banner, + banner, "USD"))); checkpoint1.flag(); @@ -2407,7 +2575,7 @@ public void shouldExecuteRawBidderResponseHooksAndPassBidderInvocationContext(Ve final Future> future = executor.executeRawBidderResponseStage( BidderResponse.of( "bidder1", - BidderSeatBid.of(singletonList(BidderBid.of(Bid.builder().build(), BidType.banner, "USD"))), + BidderSeatBid.of(singletonList(BidderBid.of(Bid.builder().build(), banner, "USD"))), 0), AuctionContext.builder() .bidRequest(BidRequest.builder().build()) @@ -2504,7 +2672,7 @@ public void shouldExecuteProcessedBidderResponseHooksHappyPath(VertxTestContext BidderResponse.of( "bidder1", BidderSeatBid.of(singletonList( - BidderBid.of(Bid.builder().build(), BidType.banner, "USD"))), + BidderBid.of(Bid.builder().build(), banner, "USD"))), 0), auctionContext); final Future> future2 = @@ -2526,7 +2694,7 @@ public void shouldExecuteProcessedBidderResponseHooksHappyPath(VertxTestContext .cid("cid") .adm("adm") .build(), - BidType.banner, + banner, "USD"))); checkpoint1.flag(); @@ -2568,7 +2736,7 @@ public void shouldExecuteProcessedBidderResponseHooksAndPassBidderInvocationCont BidderResponse.of( "bidder1", BidderSeatBid.of(singletonList( - BidderBid.of(Bid.builder().build(), BidType.banner, "USD"))), + BidderBid.of(Bid.builder().build(), banner, "USD"))), 0), AuctionContext.builder() .bidRequest(BidRequest.builder().build()) @@ -2680,10 +2848,10 @@ public void shouldExecuteAllProcessedBidResponsesHooksHappyPath() { BidderResponse.of( "bidder1", BidderSeatBid.of(singletonList( - BidderBid.of(Bid.builder().build(), BidType.banner, "USD"))), 0), + BidderBid.of(Bid.builder().build(), banner, "USD"))), 0), BidderResponse.of("bidder2", BidderSeatBid.of(singletonList( - BidderBid.of(Bid.builder().build(), BidType.video, "UAH"))), 0)), + BidderBid.of(Bid.builder().build(), video, "UAH"))), 0)), auctionContext); // then @@ -2702,12 +2870,161 @@ public void shouldExecuteAllProcessedBidResponsesHooksHappyPath() { final List expectedBidderResponses = List.of( BidderResponse.of("bidder1", BidderSeatBid.of(singletonList( - BidderBid.of(expectedBid1, BidType.banner, "USD"))), 0), + BidderBid.of(expectedBid1, banner, "USD"))), 0), BidderResponse.of("bidder2", BidderSeatBid.of(singletonList( - BidderBid.of(expectedBid2, BidType.video, "UAH"))), 0)); + BidderBid.of(expectedBid2, video, "UAH"))), 0)); assertThat(result).succeededWith( - HookStageExecutionResult.of(false, AllProcessedBidResponsesPayloadImpl.of(expectedBidderResponses))); + HookStageExecutionResult.success(AllProcessedBidResponsesPayloadImpl.of(expectedBidderResponses))); + } + + @Test + public void shouldExecuteAllProcessedBidResponsesHooksRejectionAllIgnoringUnknowns(VertxTestContext context) { + final Function, UnaryOperator> bidFilterForResponse = + (Predicate bidPredicate) -> (BidderResponse response) -> { + final BidderSeatBid seatBid = response.getSeatBid(); + final List filteredBids = seatBid.getBids().stream() + .filter(bidPredicate) + .toList(); + return response.with(seatBid.with(filteredBids)); + }; + + // given + final BidderBid bidderABid1 = BidderBid.of(Bid.builder().id("bidId1").impid("impId").build(), banner, "USD"); + final BidderBid bidderABid2 = BidderBid.of(Bid.builder().id("bidId2").impid("impId").build(), banner, "USD"); + final BidderBid bidderBBid3 = BidderBid.of(Bid.builder().id("bidId3").impid("impId").build(), video, "UAH"); + final BidderBid bidderBBid4 = BidderBid.of(Bid.builder().id("bidId4").impid("impId").build(), video, "UAH"); + final BidderBid bidderCBid5 = BidderBid.of(Bid.builder().id("bidId5").impid("impId").build(), xNative, "EUR"); + final BidderBid bidderDBid6 = BidderBid.of(Bid.builder().id("bidId6").impid("impId").build(), xNative, "EUR"); + + givenAllProcessedBidderResponsesHook( + "module-alpha", + "hook-a", + immediateHook(InvocationResultUtils.succeeded(payload -> AllProcessedBidResponsesPayloadImpl.of( + payload.bidResponses().stream() + .map(bidFilterForResponse.apply( + bidderBid -> bidderBid.equals(bidderBBid3) + || bidderBid.equals(bidderBBid4))) + .toList()), + Map.of("bidderA", List.of( + RejectedBid.of(bidderABid1, REQUEST_BLOCKED_OPTIMIZED), + RejectedBid.of(bidderABid2, REQUEST_BLOCKED_GENERAL)), + "bidderC", List.of( + RejectedBid.of(bidderCBid5, REQUEST_BLOCKED_GENERAL)), + "bidderD", List.of( + RejectedBid.of(bidderDBid6, REQUEST_BLOCKED_GENERAL)))))); + + givenAllProcessedBidderResponsesHook( + "module-alpha", + "hook-b", + immediateHook(InvocationResultUtils.succeeded(payload -> AllProcessedBidResponsesPayloadImpl.of( + payload.bidResponses().stream() + .map(bidFilterForResponse.apply( + bidderBid -> bidderBid.equals(bidderABid1) + || bidderBid.equals(bidderABid2))) + .toList()), + Map.of("bidderB", List.of( + RejectedBid.of(bidderBBid3, REQUEST_BLOCKED_PRIVACY), + RejectedBid.of(bidderBBid4, REQUEST_BLOCKED_GENERAL)), + "bidderC", List.of( + RejectedBid.of(bidderCBid5, REQUEST_BLOCKED_GENERAL)), + "bidderD", List.of( + RejectedBid.of(bidderDBid6, REQUEST_BLOCKED_GENERAL)))))); + + givenAllProcessedBidderResponsesHook( + "module-beta", + "hook-a", + immediateHook(InvocationResultUtils.succeeded(payload -> AllProcessedBidResponsesPayloadImpl.of( + payload.bidResponses().stream() + .map(bidFilterForResponse.apply( + bidderBid -> bidderBid.equals(bidderBBid3) + || bidderBid.equals(bidderBBid4))) + .toList()), + Map.of("bidderA", List.of( + RejectedBid.of(bidderABid1, REQUEST_BLOCKED_UNSUPPORTED_MEDIA_TYPE), + RejectedBid.of(bidderABid2, REQUEST_BLOCKED_GENERAL)), + "bidderC", List.of( + RejectedBid.of(bidderCBid5, REQUEST_BLOCKED_GENERAL)), + "bidderD", List.of( + RejectedBid.of(bidderDBid6, REQUEST_BLOCKED_GENERAL)))))); + + givenAllProcessedBidderResponsesHook( + "module-beta", + "hook-b", + immediateHook(InvocationResultUtils.succeeded(payload -> AllProcessedBidResponsesPayloadImpl.of( + payload.bidResponses().stream() + .map(bidFilterForResponse.apply( + bidderBid -> bidderBid.equals(bidderABid1) + || bidderBid.equals(bidderABid2))) + .toList()), + Map.of("bidderB", List.of( + RejectedBid.of(bidderBBid3, REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY), + RejectedBid.of(bidderBBid4, REQUEST_BLOCKED_GENERAL)), + "bidderC", List.of( + RejectedBid.of(bidderCBid5, REQUEST_BLOCKED_GENERAL)), + "bidderD", List.of( + RejectedBid.of(bidderDBid6, REQUEST_BLOCKED_GENERAL)))))); + + final HookStageExecutor executor = createExecutor( + executionPlan(singletonMap( + Endpoint.openrtb2_auction, + EndpointExecutionPlan.of(singletonMap( + Stage.all_processed_bid_responses, + execPlanTwoGroupsTwoHooksEach()))))); + + final Map bidRejectionTrackers = new HashMap<>(); + bidRejectionTrackers.put("bidderA", new BidRejectionTracker("bidderA", Set.of("impId"), 0.0)); + bidRejectionTrackers.put("bidderB", new BidRejectionTracker("bidderB", Set.of("impId"), 0.0)); + bidRejectionTrackers.put("bidderC", new BidRejectionTracker("bidderC", Set.of("impId"), 0.0)); + + final HookExecutionContext hookExecutionContext = HookExecutionContext.of(Endpoint.openrtb2_auction); + final AuctionContext auctionContext = AuctionContext.builder() + .bidRequest(BidRequest.builder().build()) + .account(Account.empty("accountId")) + .hookExecutionContext(hookExecutionContext) + .debugContext(DebugContext.empty()) + .bidRejectionTrackers(bidRejectionTrackers) + .build(); + + // when + final Future> future = + executor.executeAllProcessedBidResponsesStage( + List.of( + BidderResponse.of("bidderA", BidderSeatBid.of(List.of(bidderABid1, bidderABid2)), 0), + BidderResponse.of("bidderB", BidderSeatBid.of(List.of(bidderBBid3, bidderBBid4)), 0), + BidderResponse.of("bidderC", BidderSeatBid.of(List.of(bidderCBid5)), 0)), + auctionContext); + + // then + final List expectedBidderResponses = List.of( + BidderResponse.of("bidderA", BidderSeatBid.of(emptyList()), 0), + BidderResponse.of("bidderB", BidderSeatBid.of(emptyList()), 0), + BidderResponse.of("bidderC", BidderSeatBid.of(emptyList()), 0)); + + future.onComplete(context.succeeding(result -> { + assertThat(result).isNotNull(); + assertThat(result.getPayload()).isNotNull().satisfies(payload -> + assertThat(payload.bidResponses()).isEqualTo(expectedBidderResponses)); + + context.completeNow(); + })); + + assertThat(bidRejectionTrackers.keySet()).containsOnly("bidderA", "bidderB", "bidderC"); + assertThat(bidRejectionTrackers.get("bidderA").getAllRejected().get("impId")).containsOnly( + RejectedBid.of(bidderABid1, REQUEST_BLOCKED_OPTIMIZED), + RejectedBid.of(bidderABid1, REQUEST_BLOCKED_UNSUPPORTED_MEDIA_TYPE), + RejectedBid.of(bidderABid2, REQUEST_BLOCKED_GENERAL), + RejectedBid.of(bidderABid2, REQUEST_BLOCKED_GENERAL)); + assertThat(bidRejectionTrackers.get("bidderB").getAllRejected().get("impId")).containsOnly( + RejectedBid.of(bidderBBid3, REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY), + RejectedBid.of(bidderBBid3, REQUEST_BLOCKED_PRIVACY), + RejectedBid.of(bidderBBid4, REQUEST_BLOCKED_GENERAL), + RejectedBid.of(bidderBBid4, REQUEST_BLOCKED_GENERAL)); + assertThat(bidRejectionTrackers.get("bidderC").getAllRejected().get("impId")).containsOnly( + RejectedBid.of(bidderCBid5, REQUEST_BLOCKED_GENERAL), + RejectedBid.of(bidderCBid5, REQUEST_BLOCKED_GENERAL), + RejectedBid.of(bidderCBid5, REQUEST_BLOCKED_GENERAL), + RejectedBid.of(bidderCBid5, REQUEST_BLOCKED_GENERAL)); } @Test @@ -2733,7 +3050,7 @@ public void shouldExecuteAllProcessedBidResponsesHooksAndPassAuctionInvocationCo singletonList(BidderResponse.of( "bidder1", BidderSeatBid.of(singletonList( - BidderBid.of(Bid.builder().build(), BidType.banner, "USD"))), + BidderBid.of(Bid.builder().build(), banner, "USD"))), 0)), AuctionContext.builder() .bidRequest(BidRequest.builder().build()) @@ -3421,7 +3738,8 @@ private HookStageExecutor createExecutor(String hostExecutionPlan, String defaul vertx, clock, jacksonMapper, - false); + false, + 0.0); } @Value(staticConstructor = "of") diff --git a/src/test/java/org/prebid/server/hooks/v1/InvocationResultUtils.java b/src/test/java/org/prebid/server/hooks/v1/InvocationResultUtils.java index 71d21ce9596..c6690328830 100644 --- a/src/test/java/org/prebid/server/hooks/v1/InvocationResultUtils.java +++ b/src/test/java/org/prebid/server/hooks/v1/InvocationResultUtils.java @@ -1,7 +1,11 @@ package org.prebid.server.hooks.v1; +import org.prebid.server.auction.model.Rejected; import org.prebid.server.hooks.execution.v1.InvocationResultImpl; +import java.util.List; +import java.util.Map; + public class InvocationResultUtils { private InvocationResultUtils() { @@ -16,6 +20,16 @@ public static InvocationResult succeeded(PayloadUpdate InvocationResult succeeded(PayloadUpdate payloadUpdate, + Map> rejections) { + return InvocationResultImpl.builder() + .status(InvocationStatus.success) + .action(InvocationAction.update) + .payloadUpdate(payloadUpdate) + .rejections(rejections) + .build(); + } + public static InvocationResult succeeded(PayloadUpdate payloadUpdate, Object moduleContext) { diff --git a/src/test/java/org/prebid/server/validation/ResponseBidValidatorTest.java b/src/test/java/org/prebid/server/validation/ResponseBidValidatorTest.java index ffb6c9e6804..48faf364637 100644 --- a/src/test/java/org/prebid/server/validation/ResponseBidValidatorTest.java +++ b/src/test/java/org/prebid/server/validation/ResponseBidValidatorTest.java @@ -14,8 +14,8 @@ import org.prebid.server.VertxTest; import org.prebid.server.auction.BidderAliases; import org.prebid.server.auction.model.AuctionContext; -import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.auction.model.BidRejectionTracker; +import org.prebid.server.auction.model.RejectedBid; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.metric.MetricName; import org.prebid.server.metric.Metrics; @@ -38,6 +38,8 @@ import static org.mockito.Mock.Strictness.LENIENT; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; +import static org.prebid.server.auction.model.BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE_NOT_SECURE; +import static org.prebid.server.auction.model.BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE_SIZE_NOT_ALLOWED; import static org.prebid.server.settings.model.BidValidationEnforcement.enforce; import static org.prebid.server.settings.model.BidValidationEnforcement.skip; import static org.prebid.server.settings.model.BidValidationEnforcement.warn; @@ -151,7 +153,7 @@ public void validateShouldFailIfBannerBidHasNoWidthAndHeight() { creative size validation for bid bidId1, account=account, referrer=unknown, \ max imp size='100x200', bid response size='nullxnull'"""); verify(bidRejectionTracker) - .rejectBid(givenBid, BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE_SIZE_NOT_ALLOWED); + .reject(RejectedBid.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_SIZE_NOT_ALLOWED)); } @Test @@ -167,7 +169,7 @@ public void validateShouldFailIfBannerBidWidthIsGreaterThanImposedByImp() { creative size validation for bid bidId1, account=account, referrer=unknown, \ max imp size='100x200', bid response size='150x150'"""); verify(bidRejectionTracker) - .rejectBid(givenBid, BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE_SIZE_NOT_ALLOWED); + .reject(RejectedBid.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_SIZE_NOT_ALLOWED)); } @Test @@ -183,7 +185,7 @@ public void validateShouldFailIfBannerBidHeightIsGreaterThanImposedByImp() { creative size validation for bid bidId1, account=account, referrer=unknown, \ max imp size='100x200', bid response size='50x250'"""); verify(bidRejectionTracker) - .rejectBid(givenBid, BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE_SIZE_NOT_ALLOWED); + .reject(RejectedBid.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_SIZE_NOT_ALLOWED)); } @Test @@ -267,7 +269,7 @@ public void validateShouldFailIfBidHasInsecureMarkerInCreativeInSecureContext() secure creative validation for bid bidId1, account=account, referrer=unknown, \ adm=http://site.com/creative.jpg"""); verify(bidRejectionTracker) - .rejectBid(givenBid, BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE_NOT_SECURE); + .reject(RejectedBid.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_NOT_SECURE)); } @Test @@ -287,7 +289,7 @@ public void validateShouldFailIfBidHasInsecureEncodedMarkerInCreativeInSecureCon secure creative validation for bid bidId1, account=account, referrer=unknown, \ adm=http%3A//site.com/creative.jpg"""); verify(bidRejectionTracker) - .rejectBid(givenBid, BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE_NOT_SECURE); + .reject(RejectedBid.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_NOT_SECURE)); } @Test @@ -307,7 +309,7 @@ public void validateShouldFailIfBidHasNoSecureMarkersInCreativeInSecureContext() secure creative validation for bid bidId1, account=account, referrer=unknown, \ adm=//site.com/creative.jpg"""); verify(bidRejectionTracker) - .rejectBid(givenBid, BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE_NOT_SECURE); + .reject(RejectedBid.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_NOT_SECURE)); } @Test @@ -471,7 +473,7 @@ public void validateShouldIncrementSizeValidationErrMetrics() { // then verify(metrics).updateSizeValidationMetrics(BIDDER_NAME, ACCOUNT_ID, MetricName.err); verify(bidRejectionTracker) - .rejectBid(givenBid, BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE_SIZE_NOT_ALLOWED); + .reject(RejectedBid.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_SIZE_NOT_ALLOWED)); } @Test @@ -501,7 +503,7 @@ public void validateShouldIncrementSecureValidationErrMetrics() { // then verify(metrics).updateSecureValidationMetrics(BIDDER_NAME, ACCOUNT_ID, MetricName.err); verify(bidRejectionTracker) - .rejectBid(givenBid, BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE_NOT_SECURE); + .reject(RejectedBid.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_NOT_SECURE)); } @Test From fdbae73e08a7015ec634370fa45f621e57a3d86f Mon Sep 17 00:00:00 2001 From: antonbabak Date: Mon, 17 Feb 2025 16:16:36 +0100 Subject: [PATCH 2/5] Fix MraidFilter --- ...alTimeDataProcessedAuctionRequestHook.java | 3 -- .../ortb2/blocking/core/BidsBlocker.java | 1 - .../ortb2/blocking/core/BidsBlockerTest.java | 3 -- .../Ortb2BlockingBidderRequestHookTest.java | 1 - .../filter/core/BidResponsesMraidFilter.java | 2 +- .../filter/model/AnalyticsResult.java | 3 ++ ...diaFilterAllProcessedBidResponsesHook.java | 7 ++--- .../core/BidResponsesMraidFilterTest.java | 7 ++--- ...ilterAllProcessedBidResponsesHookTest.java | 29 ++++++++++++++----- 9 files changed, 30 insertions(+), 26 deletions(-) diff --git a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHook.java b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHook.java index 82760d4c8d6..8e7a42c64f9 100644 --- a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHook.java +++ b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHook.java @@ -41,8 +41,6 @@ import org.prebid.server.hooks.v1.auction.ProcessedAuctionRequestHook; import org.prebid.server.proto.openrtb.ext.request.ExtRequest; import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid; -import org.prebid.server.settings.model.Account; -import org.prebid.server.settings.model.AccountHooksConfiguration; import java.util.Collection; import java.util.Collections; @@ -50,7 +48,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; 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 dd6c61043fb..6bc46271d5a 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 @@ -6,7 +6,6 @@ import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.prebid.server.auction.model.BidRejectionReason; -import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.model.Rejected; import org.prebid.server.auction.model.RejectedBid; import org.prebid.server.auction.versionconverter.OrtbVersion; 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 48a8ec77651..7f3dffbc6f4 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 @@ -8,7 +8,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; -import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.auction.model.RejectedBid; import org.prebid.server.auction.versionconverter.OrtbVersion; import org.prebid.server.bidder.model.BidderBid; @@ -34,8 +33,6 @@ import static java.util.Collections.singletonMap; import static java.util.function.UnaryOperator.identity; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.verify; -import static org.prebid.server.auction.model.BidRejectionReason.*; import static org.prebid.server.auction.model.BidRejectionReason.RESPONSE_REJECTED_ADVERTISER_BLOCKED; import static org.prebid.server.auction.model.BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE; diff --git a/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingBidderRequestHookTest.java b/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingBidderRequestHookTest.java index e26e829e899..ce4f5503c25 100644 --- a/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingBidderRequestHookTest.java +++ b/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingBidderRequestHookTest.java @@ -14,7 +14,6 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.versionconverter.OrtbVersion; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.bidder.BidderInfo; diff --git a/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/core/BidResponsesMraidFilter.java b/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/core/BidResponsesMraidFilter.java index e6a413ab408..614e172adb9 100644 --- a/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/core/BidResponsesMraidFilter.java +++ b/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/core/BidResponsesMraidFilter.java @@ -3,7 +3,6 @@ import com.iab.openrtb.response.Bid; import org.apache.commons.lang3.StringUtils; import org.prebid.server.auction.model.BidRejectionReason; -import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.model.BidderResponse; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderError; @@ -53,6 +52,7 @@ public MraidFilterResult filterByPattern(String mraidScriptPattern, TAG_VALUES, bidder, rejectedImps, + invalidBids, BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE); analyticsResults.add(analyticsResult); diff --git a/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/model/AnalyticsResult.java b/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/model/AnalyticsResult.java index 247af4c7ee7..7a4c390a639 100644 --- a/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/model/AnalyticsResult.java +++ b/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/model/AnalyticsResult.java @@ -2,6 +2,7 @@ import lombok.Value; import org.prebid.server.auction.model.BidRejectionReason; +import org.prebid.server.bidder.model.BidderBid; import java.util.List; import java.util.Map; @@ -17,5 +18,7 @@ public class AnalyticsResult { List impId; + List rejectedBids; + BidRejectionReason rejectionReason; } diff --git a/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHook.java b/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHook.java index b58be3a64e1..aad918e0916 100644 --- a/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHook.java +++ b/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHook.java @@ -7,7 +7,7 @@ import org.apache.commons.lang3.BooleanUtils; import org.prebid.server.auction.model.BidderResponse; import org.prebid.server.auction.model.Rejected; -import org.prebid.server.auction.model.RejectedImp; +import org.prebid.server.auction.model.RejectedBid; import org.prebid.server.hooks.execution.v1.InvocationResultImpl; import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; import org.prebid.server.hooks.execution.v1.analytics.AppliedToImpl; @@ -28,7 +28,6 @@ import org.prebid.server.hooks.v1.bidder.AllProcessedBidResponsesHook; import org.prebid.server.hooks.v1.bidder.AllProcessedBidResponsesPayload; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -86,8 +85,8 @@ public Future> call( private Map> toRejections(List analyticsResults) { return analyticsResults.stream().collect(Collectors.toMap( AnalyticsResult::getBidder, - result -> result.getImpId().stream() - .map(impId -> RejectedImp.of(impId, result.getRejectionReason())) + result -> result.getRejectedBids().stream() + .map(bid -> RejectedBid.of(bid, result.getRejectionReason())) .map(Rejected.class::cast) .toList(), (list1, list2) -> Stream.concat(list1.stream(), list2.stream()).collect(Collectors.toList()))); diff --git a/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/core/BidResponsesMraidFilterTest.java b/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/core/BidResponsesMraidFilterTest.java index bebeeeca2e9..961079fa01d 100644 --- a/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/core/BidResponsesMraidFilterTest.java +++ b/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/core/BidResponsesMraidFilterTest.java @@ -3,7 +3,6 @@ import com.iab.openrtb.response.Bid; import org.junit.jupiter.api.Test; import org.prebid.server.auction.model.BidRejectionReason; -import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.model.BidderResponse; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderError; @@ -16,10 +15,6 @@ import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.verifyNoMoreInteractions; public class BidResponsesMraidFilterTest { @@ -75,12 +70,14 @@ public void filterShouldReturnFilteredBidsWhenBidsWithMraidScriptIsFilteredOut() Map.of("richmedia-format", "mraid"), "bidderB", List.of("imp_id2"), + List.of(givenInvalidBid2), BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE); final AnalyticsResult expectedAnalyticsResultC = AnalyticsResult.of( "success-block", Map.of("richmedia-format", "mraid"), "bidderC", List.of("imp_id1", "imp_id2"), + List.of(givenInvalidBid1, givenInvalidBid2), BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE); assertThat(filterResult.getFilterResult()) diff --git a/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHookTest.java b/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHookTest.java index 2859ac9cc32..e1281bc1a62 100644 --- a/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHookTest.java +++ b/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHookTest.java @@ -1,17 +1,16 @@ package org.prebid.server.hooks.modules.pb.richmedia.filter.v1; import com.fasterxml.jackson.databind.ObjectMapper; +import com.iab.openrtb.response.Bid; import io.vertx.core.Future; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.prebid.server.auction.model.AuctionContext; -import org.prebid.server.auction.model.BidRejectionReason; -import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.model.BidderResponse; -import org.prebid.server.auction.model.RejectedImp; +import org.prebid.server.auction.model.RejectedBid; +import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderSeatBid; import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; import org.prebid.server.hooks.execution.v1.analytics.AppliedToImpl; @@ -34,6 +33,7 @@ import java.util.List; import java.util.Map; import java.util.stream.IntStream; +import java.util.stream.Stream; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; @@ -44,7 +44,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; -import static org.prebid.server.auction.model.BidRejectionReason.*; +import static org.prebid.server.auction.model.BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE; @ExtendWith(MockitoExtension.class) public class PbRichmediaFilterAllProcessedBidResponsesHookTest { @@ -223,10 +223,21 @@ public void callShouldReturnAnalyticsResultsOfRejectedBids() { assertThat(result.rejections()).containsOnly( entry("bidderA", List.of( - RejectedImp.of("imp_id1", RESPONSE_REJECTED_INVALID_CREATIVE), - RejectedImp.of("imp_id2", RESPONSE_REJECTED_INVALID_CREATIVE))), + RejectedBid.of( + BidderBid.builder() + .bid(Bid.builder().id("bid-imp_id1").impid("imp_id1").build()) + .build(), + RESPONSE_REJECTED_INVALID_CREATIVE), + RejectedBid.of( + BidderBid.builder() + .bid(Bid.builder().id("bid-imp_id2").impid("imp_id2").build()) + .build(), + RESPONSE_REJECTED_INVALID_CREATIVE))), entry("bidderB", List.of( - RejectedImp.of("imp_id3", RESPONSE_REJECTED_INVALID_CREATIVE)))); + RejectedBid.of(BidderBid.builder() + .bid(Bid.builder().id("bid-imp_id3").impid("imp_id3").build()) + .build(), + RESPONSE_REJECTED_INVALID_CREATIVE)))); } @Test @@ -265,6 +276,8 @@ private static AnalyticsResult givenAnalyticsResult(String bidder, String... rej Map.of("key", "value"), bidder, List.of(rejectedImpIds), + Stream.of(rejectedImpIds).map(impId -> BidderBid.builder().bid( + Bid.builder().id("bid-" + impId).impid(impId).build()).build()).toList(), RESPONSE_REJECTED_INVALID_CREATIVE); } From ddff00c5a4ce38f9594464a67cb98ae9595a8aaf Mon Sep 17 00:00:00 2001 From: antonbabak Date: Mon, 24 Feb 2025 17:03:56 +0100 Subject: [PATCH 3/5] Fixing Greenbids Module --- .../GreenbidsRealTimeDataConfiguration.java | 23 +- .../GreenbidsInvocationResultCreator.java | 84 +++ .../data/core/GreenbidsInvocationService.java | 122 ----- .../data/core/GreenbidsPayloadUpdater.java | 51 ++ .../result/GreenbidsInvocationResult.java | 3 - ...alTimeDataProcessedAuctionRequestHook.java | 121 ++--- .../GreenbidsInferenceDataServiceTest.java | 18 +- .../GreenbidsInvocationResultCreatorTest.java | 143 +++++ .../core/GreenbidsInvocationServiceTest.java | 233 -------- .../core/GreenbidsPayloadUpdaterTest.java | 60 +++ .../data/util/TestBidRequestProvider.java | 124 ++--- ...meDataProcessedAuctionRequestHookTest.java | 498 +++--------------- 12 files changed, 520 insertions(+), 960 deletions(-) create mode 100644 extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationResultCreator.java delete mode 100644 extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationService.java create mode 100644 extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsPayloadUpdater.java create mode 100644 extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationResultCreatorTest.java delete mode 100644 extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationServiceTest.java create mode 100644 extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsPayloadUpdaterTest.java diff --git a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/config/GreenbidsRealTimeDataConfiguration.java b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/config/GreenbidsRealTimeDataConfiguration.java index a6b47f7d6ad..ec8cb4c10b1 100644 --- a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/config/GreenbidsRealTimeDataConfiguration.java +++ b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/config/GreenbidsRealTimeDataConfiguration.java @@ -6,16 +6,15 @@ import com.google.cloud.storage.StorageOptions; import io.vertx.core.Vertx; import org.prebid.server.geolocation.CountryCodeMapper; -import org.prebid.server.hooks.modules.greenbids.real.time.data.model.filter.ThrottlingThresholds; -import org.prebid.server.hooks.modules.greenbids.real.time.data.core.ThrottlingThresholdsFactory; -import org.prebid.server.hooks.modules.greenbids.real.time.data.core.GreenbidsInferenceDataService; import org.prebid.server.hooks.modules.greenbids.real.time.data.core.FilterService; +import org.prebid.server.hooks.modules.greenbids.real.time.data.core.GreenbidsInferenceDataService; import org.prebid.server.hooks.modules.greenbids.real.time.data.core.ModelCache; import org.prebid.server.hooks.modules.greenbids.real.time.data.core.OnnxModelRunner; import org.prebid.server.hooks.modules.greenbids.real.time.data.core.OnnxModelRunnerFactory; import org.prebid.server.hooks.modules.greenbids.real.time.data.core.OnnxModelRunnerWithThresholds; import org.prebid.server.hooks.modules.greenbids.real.time.data.core.ThresholdCache; -import org.prebid.server.hooks.modules.greenbids.real.time.data.core.GreenbidsInvocationService; +import org.prebid.server.hooks.modules.greenbids.real.time.data.core.ThrottlingThresholdsFactory; +import org.prebid.server.hooks.modules.greenbids.real.time.data.model.filter.ThrottlingThresholds; import org.prebid.server.hooks.modules.greenbids.real.time.data.v1.GreenbidsRealTimeDataProcessedAuctionRequestHook; import org.prebid.server.json.ObjectMapperProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -48,16 +47,14 @@ GreenbidsInferenceDataService greenbidsInferenceDataService(DatabaseReaderFactor GreenbidsRealTimeDataModule greenbidsRealTimeDataModule( FilterService filterService, OnnxModelRunnerWithThresholds onnxModelRunnerWithThresholds, - GreenbidsInferenceDataService greenbidsInferenceDataService, - GreenbidsInvocationService greenbidsInvocationService) { + GreenbidsInferenceDataService greenbidsInferenceDataService) { return new GreenbidsRealTimeDataModule(List.of( new GreenbidsRealTimeDataProcessedAuctionRequestHook( ObjectMapperProvider.mapper(), filterService, onnxModelRunnerWithThresholds, - greenbidsInferenceDataService, - greenbidsInvocationService))); + greenbidsInferenceDataService))); } @Bean @@ -123,15 +120,7 @@ ThresholdCache thresholdCache( } @Bean - OnnxModelRunnerWithThresholds onnxModelRunnerWithThresholds( - ModelCache modelCache, - ThresholdCache thresholdCache) { - + OnnxModelRunnerWithThresholds onnxModelRunnerWithThresholds(ModelCache modelCache, ThresholdCache thresholdCache) { return new OnnxModelRunnerWithThresholds(modelCache, thresholdCache); } - - @Bean - GreenbidsInvocationService greenbidsInvocationService() { - return new GreenbidsInvocationService(); - } } diff --git a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationResultCreator.java b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationResultCreator.java new file mode 100644 index 00000000000..649c23e79fb --- /dev/null +++ b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationResultCreator.java @@ -0,0 +1,84 @@ +package org.prebid.server.hooks.modules.greenbids.real.time.data.core; + +import com.fasterxml.jackson.databind.JsonNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.prebid.server.analytics.reporter.greenbids.model.ExplorationResult; +import org.prebid.server.analytics.reporter.greenbids.model.Ortb2ImpExtResult; +import org.prebid.server.hooks.modules.greenbids.real.time.data.model.data.GreenbidsConfig; +import org.prebid.server.hooks.modules.greenbids.real.time.data.model.result.AnalyticsResult; +import org.prebid.server.hooks.modules.greenbids.real.time.data.model.result.GreenbidsInvocationResult; +import org.prebid.server.hooks.v1.InvocationAction; + +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +public class GreenbidsInvocationResultCreator { + + private static final int RANGE_16_BIT_INTEGER_DIVISION_BASIS = 0x10000; + private static final double DEFAULT_EXPLORATION_RATE = 1.0; + + private GreenbidsInvocationResultCreator() { + + } + + public static GreenbidsInvocationResult create(GreenbidsConfig greenbidsConfig, + BidRequest bidRequest, + Map> impsBiddersFilterMap) { + + final String greenbidsId = UUID.randomUUID().toString(); + final boolean isExploration = isExploration(greenbidsConfig, greenbidsId); + + final boolean allRejected = bidRequest.getImp().stream() + .noneMatch(imp -> impsBiddersFilterMap.get(imp.getId()).values().stream().anyMatch(isKept -> isKept)); + + final InvocationAction invocationAction = isExploration + ? InvocationAction.no_action + : allRejected + ? InvocationAction.reject + : InvocationAction.update; + + final Map ort2ImpExtResultMap = createOrtb2ImpExtForImps( + bidRequest, impsBiddersFilterMap, greenbidsId, isExploration); + final AnalyticsResult analyticsResult = AnalyticsResult.of("success", ort2ImpExtResultMap); + return GreenbidsInvocationResult.of(invocationAction, analyticsResult); + } + + private static boolean isExploration(GreenbidsConfig greenbidsConfig, String greenbidsId) { + final double explorationRate = ObjectUtils.defaultIfNull(greenbidsConfig.getExplorationRate(), DEFAULT_EXPLORATION_RATE); + final int hashInt = Integer.parseInt(greenbidsId.substring(greenbidsId.length() - 4), 16); + return hashInt < explorationRate * RANGE_16_BIT_INTEGER_DIVISION_BASIS; + } + + private static Map createOrtb2ImpExtForImps( + BidRequest bidRequest, + Map> impsBiddersFilterMap, + String greenbidsId, + boolean isExploration) { + + return bidRequest.getImp().stream() + .collect(Collectors.toMap( + Imp::getId, + imp -> createOrtb2ImpExt(imp, impsBiddersFilterMap, greenbidsId, isExploration))); + } + + private static Ortb2ImpExtResult createOrtb2ImpExt(Imp imp, + Map> impsBiddersFilterMap, + String greenbidsId, + boolean isExploration) { + + final String tid = Optional.ofNullable(imp) + .map(Imp::getExt) + .map(impExt -> impExt.get("tid")) + .map(JsonNode::asText) + .orElse(StringUtils.EMPTY); + final Map impBiddersFilterMap = impsBiddersFilterMap.get(imp.getId()); + final ExplorationResult explorationResult = ExplorationResult.of( + greenbidsId, impBiddersFilterMap, isExploration); + return Ortb2ImpExtResult.of(explorationResult, tid); + } +} diff --git a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationService.java b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationService.java deleted file mode 100644 index 4a2b5962871..00000000000 --- a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationService.java +++ /dev/null @@ -1,122 +0,0 @@ -package org.prebid.server.hooks.modules.greenbids.real.time.data.core; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.iab.openrtb.request.BidRequest; -import com.iab.openrtb.request.Imp; -import org.apache.commons.lang3.StringUtils; -import org.prebid.server.analytics.reporter.greenbids.model.ExplorationResult; -import org.prebid.server.analytics.reporter.greenbids.model.Ortb2ImpExtResult; -import org.prebid.server.hooks.modules.greenbids.real.time.data.model.data.GreenbidsConfig; -import org.prebid.server.hooks.modules.greenbids.real.time.data.model.result.AnalyticsResult; -import org.prebid.server.hooks.modules.greenbids.real.time.data.model.result.GreenbidsInvocationResult; -import org.prebid.server.hooks.v1.InvocationAction; - -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; -import java.util.stream.Collectors; - -public class GreenbidsInvocationService { - - private static final int RANGE_16_BIT_INTEGER_DIVISION_BASIS = 0x10000; - - private static final double DEFAULT_EXPLORATION_RATE = 1.0; - - public GreenbidsInvocationResult createGreenbidsInvocationResult( - GreenbidsConfig greenbidsConfig, - BidRequest bidRequest, - Map> impsBiddersFilterMap) { - - final String greenbidsId = UUID.randomUUID().toString(); - final boolean isExploration = isExploration(greenbidsConfig, greenbidsId); - - final List updatedImps = updateImps(bidRequest, impsBiddersFilterMap); - - final BidRequest updatedBidRequest = isExploration || updatedImps.isEmpty() - ? bidRequest - : bidRequest.toBuilder() - .imp(updatedImps) - .build(); - - final InvocationAction invocationAction = updatedImps.isEmpty() - ? InvocationAction.reject - : isExploration - ? InvocationAction.no_action - : InvocationAction.update; - - final Map ort2ImpExtResultMap = createOrtb2ImpExtForImps( - bidRequest, impsBiddersFilterMap, greenbidsId, isExploration); - final AnalyticsResult analyticsResult = AnalyticsResult.of( - "success", ort2ImpExtResultMap); - - return GreenbidsInvocationResult.of(updatedBidRequest, invocationAction, analyticsResult); - } - - private Boolean isExploration(GreenbidsConfig greenbidsConfig, String greenbidsId) { - final double explorationRate = Optional.ofNullable(greenbidsConfig.getExplorationRate()) - .orElse(DEFAULT_EXPLORATION_RATE); - final int hashInt = Integer.parseInt( - greenbidsId.substring(greenbidsId.length() - 4), 16); - return hashInt < explorationRate * RANGE_16_BIT_INTEGER_DIVISION_BASIS; - } - - private List updateImps(BidRequest bidRequest, Map> impsBiddersFilterMap) { - return bidRequest.getImp().stream() - .filter(imp -> isImpKept(imp, impsBiddersFilterMap)) - .map(imp -> updateImp(imp, impsBiddersFilterMap.get(imp.getId()))) - .toList(); - } - - private boolean isImpKept(Imp imp, Map> impsBiddersFilterMap) { - return impsBiddersFilterMap.get(imp.getId()).values().stream().anyMatch(isKept -> isKept); - } - - private Imp updateImp(Imp imp, Map bidderFilterMap) { - return imp.toBuilder() - .ext(updateImpExt(imp.getExt(), bidderFilterMap)) - .build(); - } - - private ObjectNode updateImpExt(ObjectNode impExt, Map bidderFilterMap) { - final ObjectNode updatedExt = impExt.deepCopy(); - Optional.ofNullable((ObjectNode) updatedExt.get("prebid")) - .map(prebidNode -> (ObjectNode) prebidNode.get("bidder")) - .ifPresent(bidderNode -> - bidderFilterMap.entrySet().stream() - .filter(entry -> !entry.getValue()) - .map(Map.Entry::getKey) - .forEach(bidderNode::remove)); - return updatedExt; - } - - private Map createOrtb2ImpExtForImps( - BidRequest bidRequest, - Map> impsBiddersFilterMap, - String greenbidsId, - Boolean isExploration) { - - return bidRequest.getImp().stream() - .collect(Collectors.toMap( - Imp::getId, - imp -> createOrtb2ImpExt(imp, impsBiddersFilterMap, greenbidsId, isExploration))); - } - - private Ortb2ImpExtResult createOrtb2ImpExt( - Imp imp, - Map> impsBiddersFilterMap, - String greenbidsId, - Boolean isExploration) { - - final String tid = Optional.ofNullable(imp) - .map(Imp::getExt) - .map(impExt -> impExt.get("tid")) - .map(JsonNode::asText) - .orElse(StringUtils.EMPTY); - final Map impBiddersFilterMap = impsBiddersFilterMap.get(imp.getId()); - final ExplorationResult explorationResult = ExplorationResult.of( - greenbidsId, impBiddersFilterMap, isExploration); - return Ortb2ImpExtResult.of(explorationResult, tid); - } -} diff --git a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsPayloadUpdater.java b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsPayloadUpdater.java new file mode 100644 index 00000000000..3b1c6aa57ed --- /dev/null +++ b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsPayloadUpdater.java @@ -0,0 +1,51 @@ +package org.prebid.server.hooks.modules.greenbids.real.time.data.core; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class GreenbidsPayloadUpdater { + + private GreenbidsPayloadUpdater() { + + } + + public static BidRequest update(BidRequest bidRequest, Map> impsBiddersFilterMap) { + return bidRequest.toBuilder() + .imp(updateImps(bidRequest, impsBiddersFilterMap)) + .build(); + } + + private static List updateImps(BidRequest bidRequest, Map> impsBiddersFilterMap) { + return bidRequest.getImp().stream() + .filter(imp -> isImpKept(impsBiddersFilterMap.get(imp.getId()))) + .map(imp -> updateImp(imp, impsBiddersFilterMap.get(imp.getId()))) + .toList(); + } + + private static boolean isImpKept(Map bidderFilterMap) { + return bidderFilterMap.values().stream().anyMatch(isKept -> isKept); + } + + private static Imp updateImp(Imp imp, Map bidderFilterMap) { + return imp.toBuilder() + .ext(updateImpExt(imp.getExt(), bidderFilterMap)) + .build(); + } + + private static ObjectNode updateImpExt(ObjectNode impExt, Map bidderFilterMap) { + final ObjectNode updatedExt = impExt.deepCopy(); + Optional.ofNullable((ObjectNode) updatedExt.get("prebid")) + .map(prebidNode -> (ObjectNode) prebidNode.get("bidder")) + .ifPresent(bidderNode -> + bidderFilterMap.entrySet().stream() + .filter(entry -> !entry.getValue()) + .map(Map.Entry::getKey) + .forEach(bidderNode::remove)); + return updatedExt; + } +} diff --git a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/model/result/GreenbidsInvocationResult.java b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/model/result/GreenbidsInvocationResult.java index 0aff44ceaec..39e56fe5dcd 100644 --- a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/model/result/GreenbidsInvocationResult.java +++ b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/model/result/GreenbidsInvocationResult.java @@ -1,14 +1,11 @@ package org.prebid.server.hooks.modules.greenbids.real.time.data.model.result; -import com.iab.openrtb.request.BidRequest; import lombok.Value; import org.prebid.server.hooks.v1.InvocationAction; @Value(staticConstructor = "of") public class GreenbidsInvocationResult { - BidRequest updatedBidRequest; - InvocationAction invocationAction; AnalyticsResult analyticsResult; diff --git a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHook.java b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHook.java index 8000cedc8de..6151f50d5ca 100644 --- a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHook.java +++ b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHook.java @@ -6,14 +6,10 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; import io.vertx.core.Future; -import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.tuple.Pair; import org.prebid.server.analytics.reporter.greenbids.model.ExplorationResult; -import org.apache.commons.lang3.BooleanUtils; -import org.prebid.server.analytics.reporter.greenbids.model.ExplorationResult; import org.prebid.server.analytics.reporter.greenbids.model.Ortb2ImpExtResult; -import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.auction.model.Rejected; import org.prebid.server.auction.model.RejectedImp; @@ -26,7 +22,8 @@ import org.prebid.server.hooks.execution.v1.auction.AuctionRequestPayloadImpl; import org.prebid.server.hooks.modules.greenbids.real.time.data.core.FilterService; import org.prebid.server.hooks.modules.greenbids.real.time.data.core.GreenbidsInferenceDataService; -import org.prebid.server.hooks.modules.greenbids.real.time.data.core.GreenbidsInvocationService; +import org.prebid.server.hooks.modules.greenbids.real.time.data.core.GreenbidsInvocationResultCreator; +import org.prebid.server.hooks.modules.greenbids.real.time.data.core.GreenbidsPayloadUpdater; import org.prebid.server.hooks.modules.greenbids.real.time.data.core.OnnxModelRunner; import org.prebid.server.hooks.modules.greenbids.real.time.data.core.OnnxModelRunnerWithThresholds; import org.prebid.server.hooks.modules.greenbids.real.time.data.model.data.GreenbidsConfig; @@ -64,34 +61,29 @@ public class GreenbidsRealTimeDataProcessedAuctionRequestHook implements Process private final FilterService filterService; private final OnnxModelRunnerWithThresholds onnxModelRunnerWithThresholds; private final GreenbidsInferenceDataService greenbidsInferenceDataService; - private final GreenbidsInvocationService greenbidsInvocationService; public GreenbidsRealTimeDataProcessedAuctionRequestHook( ObjectMapper mapper, FilterService filterService, OnnxModelRunnerWithThresholds onnxModelRunnerWithThresholds, - GreenbidsInferenceDataService greenbidsInferenceDataService, - GreenbidsInvocationService greenbidsInvocationService) { + GreenbidsInferenceDataService greenbidsInferenceDataService) { + this.mapper = Objects.requireNonNull(mapper); this.filterService = Objects.requireNonNull(filterService); this.onnxModelRunnerWithThresholds = Objects.requireNonNull(onnxModelRunnerWithThresholds); this.greenbidsInferenceDataService = Objects.requireNonNull(greenbidsInferenceDataService); - this.greenbidsInvocationService = Objects.requireNonNull(greenbidsInvocationService); } @Override - public Future> call( - AuctionRequestPayload auctionRequestPayload, - AuctionInvocationContext invocationContext) { + public Future> call(AuctionRequestPayload auctionRequestPayload, + AuctionInvocationContext invocationContext) { - final AuctionContext auctionContext = invocationContext.auctionContext(); - final BidRequest bidRequest = auctionContext.getBidRequest(); - final GreenbidsConfig greenbidsConfig = Optional.ofNullable(parseBidRequestExt(auctionContext)) + final BidRequest bidRequest = auctionRequestPayload.bidRequest(); + final GreenbidsConfig greenbidsConfig = Optional.ofNullable(parseBidRequestExt(bidRequest.getExt())) .orElseGet(() -> toGreenbidsConfig(invocationContext.accountConfig())); if (greenbidsConfig == null) { - return Future.failedFuture( - new PreBidException("Greenbids config is null; cannot proceed.")); + return Future.failedFuture(new PreBidException("Greenbids config is null; cannot proceed.")); } return Future.all( @@ -102,14 +94,11 @@ public Future> call( greenbidsConfig, compositeFuture.resultAt(0), compositeFuture.resultAt(1))) - .recover(throwable -> Future.succeededFuture(toInvocationResult( - bidRequest, null, InvocationAction.no_action))); + .recover(throwable -> noActionInvocationResult()); } - private GreenbidsConfig parseBidRequestExt(AuctionContext auctionContext) { - return Optional.ofNullable(auctionContext) - .map(AuctionContext::getBidRequest) - .map(BidRequest::getExt) + private GreenbidsConfig parseBidRequestExt(ExtRequest extRequest) { + return Optional.ofNullable(extRequest) .map(ExtRequest::getPrebid) .map(ExtRequestPrebid::getAnalytics) .filter(this::isNotEmptyObjectNode) @@ -146,40 +135,43 @@ private Future> toInvocationResult( throttlingMessages, threshold); } catch (PreBidException e) { - return Future.succeededFuture(toInvocationResult( - bidRequest, null, InvocationAction.no_action)); + return noActionInvocationResult(); } - final GreenbidsInvocationResult greenbidsInvocationResult = greenbidsInvocationService - .createGreenbidsInvocationResult(greenbidsConfig, bidRequest, impsBiddersFilterMap); + final GreenbidsInvocationResult invocationResult = GreenbidsInvocationResultCreator.create( + greenbidsConfig, + bidRequest, + impsBiddersFilterMap); - return Future.succeededFuture(toInvocationResult( - greenbidsInvocationResult.getUpdatedBidRequest(), - greenbidsInvocationResult.getAnalyticsResult(), - greenbidsInvocationResult.getInvocationAction())); + return invocationResult.getInvocationAction() == InvocationAction.no_action + ? noActionInvocationResult(invocationResult.getAnalyticsResult()) + : toInvocationResult(bidRequest, impsBiddersFilterMap, invocationResult); } - private InvocationResult toInvocationResult( + private Future> toInvocationResult( BidRequest bidRequest, - AnalyticsResult analyticsResult, - InvocationAction action) { - - return switch (action) { - case InvocationAction.update -> InvocationResultImpl - .builder() - .status(InvocationStatus.success) - .action(action) - .payloadUpdate(payload -> AuctionRequestPayloadImpl.of(bidRequest)) - .analyticsTags(toAnalyticsTags(analyticsResult)) - .rejections(toRejections(analyticsResult)) - .build(); - default -> InvocationResultImpl - .builder() - .status(InvocationStatus.success) - .action(action) - .analyticsTags(toAnalyticsTags(analyticsResult)) - .build(); - }; + Map> impsBiddersFilterMap, + GreenbidsInvocationResult invocationResult) { + + return Future.succeededFuture(InvocationResultImpl.builder() + .status(InvocationStatus.success) + .action(invocationResult.getInvocationAction()) + .payloadUpdate(payload -> AuctionRequestPayloadImpl.of(GreenbidsPayloadUpdater.update(bidRequest, impsBiddersFilterMap))) + .analyticsTags(toAnalyticsTags(invocationResult.getAnalyticsResult())) + .rejections(toRejections(impsBiddersFilterMap)) + .build()); + } + + private Future> noActionInvocationResult(AnalyticsResult analyticsResult) { + return Future.succeededFuture(InvocationResultImpl.builder() + .status(InvocationStatus.success) + .action(InvocationAction.no_action) + .analyticsTags(toAnalyticsTags(analyticsResult)) + .build()); + } + + private Future> noActionInvocationResult() { + return noActionInvocationResult(null); } private Tags toAnalyticsTags(AnalyticsResult analyticsResult) { @@ -225,23 +217,16 @@ private ObjectNode toObjectNode(Map.Entry values) { return values != null ? mapper.valueToTree(values) : null; } - private Map> toRejections(AnalyticsResult analyticsResult) { - if (analyticsResult == null) { - return null; - } - - return analyticsResult.getValues().entrySet().stream() - .flatMap(entry -> - Stream.ofNullable(entry.getValue()) - .map(Ortb2ImpExtResult::getGreenbids) - .map(ExplorationResult::getKeptInAuction) - .map(Map::entrySet) - .flatMap(Collection::stream) - .filter(e -> BooleanUtils.isFalse(e.getValue())) - .map(Map.Entry::getKey) - .map(bidder -> Pair.of( - bidder, - RejectedImp.of(entry.getKey(), BidRejectionReason.REQUEST_BLOCKED_OPTIMIZED)))) + private Map> toRejections(Map> impsBiddersFilterMap) { + return impsBiddersFilterMap.entrySet().stream() + .flatMap(entry -> Stream.ofNullable(entry.getValue()) + .map(Map::entrySet) + .flatMap(Collection::stream) + .filter(e -> BooleanUtils.isFalse(e.getValue())) + .map(Map.Entry::getKey) + .map(bidder -> Pair.of( + bidder, + RejectedImp.of(entry.getKey(), BidRejectionReason.REQUEST_BLOCKED_OPTIMIZED)))) .collect(Collectors.groupingBy(Pair::getKey, Collectors.mapping(Pair::getValue, Collectors.toList()))); } diff --git a/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInferenceDataServiceTest.java b/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInferenceDataServiceTest.java index c56b19e14c2..a3090b24aee 100644 --- a/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInferenceDataServiceTest.java +++ b/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInferenceDataServiceTest.java @@ -73,8 +73,7 @@ public void extractThrottlingMessagesFromBidRequestShouldReturnValidThrottlingMe .ext(givenImpExt()) .banner(banner) .build(); - final Device device = givenDevice(identity()); - final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device); + final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp)); final CountryResponse countryResponse = mock(CountryResponse.class); @@ -114,8 +113,9 @@ public void extractThrottlingMessagesFromBidRequestShouldReturnValidThrottlingMe .ext(givenImpExt()) .banner(banner) .build(); - final Device device = givenDevice(identity(), "FRA"); - final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device); + final BidRequest bidRequest = givenBidRequest( + request -> request.device(givenDevice("FRA")), + List.of(imp)); final ZonedDateTime timestamp = ZonedDateTime.now(ZoneId.of("UTC")); final Integer expectedHourBucket = timestamp.getHour(); @@ -151,8 +151,7 @@ public void extractThrottlingMessagesFromBidRequestShouldHandleMissingIp() { .ext(givenImpExt()) .banner(banner) .build(); - final Device device = givenDeviceWithoutIp(identity()); - final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device); + final BidRequest bidRequest = givenBidRequest(request -> request.device(givenDeviceWithoutIp()), List.of(imp)); final ZonedDateTime timestamp = ZonedDateTime.now(ZoneId.of("UTC")); final Integer expectedHourBucket = timestamp.getHour(); @@ -187,8 +186,7 @@ public void extractThrottlingMessagesFromBidRequestShouldThrowPreBidExceptionWhe .ext(givenImpExt()) .banner(banner) .build(); - final Device device = givenDevice(identity()); - final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device); + final BidRequest bidRequest = givenBidRequest(request -> request.device(givenDevice()), List.of(imp)); when(databaseReader.country(any(InetAddress.class))).thenThrow(new GeoIp2Exception("GeoIP failure")); @@ -198,9 +196,9 @@ public void extractThrottlingMessagesFromBidRequestShouldThrowPreBidExceptionWhe .hasMessageContaining("Failed to fetch country from geoLite DB"); } - private Device givenDeviceWithoutIp(UnaryOperator deviceCustomizer) { + private Device givenDeviceWithoutIp() { final String userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36" + " (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36"; - return deviceCustomizer.apply(Device.builder().ua(userAgent)).build(); + return Device.builder().ua(userAgent).build(); } } diff --git a/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationResultCreatorTest.java b/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationResultCreatorTest.java new file mode 100644 index 00000000000..fdd263527ad --- /dev/null +++ b/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationResultCreatorTest.java @@ -0,0 +1,143 @@ +package org.prebid.server.hooks.modules.greenbids.real.time.data.core; + +import com.iab.openrtb.request.Banner; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.analytics.reporter.greenbids.model.Ortb2ImpExtResult; +import org.prebid.server.hooks.modules.greenbids.real.time.data.model.data.GreenbidsConfig; +import org.prebid.server.hooks.modules.greenbids.real.time.data.model.result.GreenbidsInvocationResult; +import org.prebid.server.hooks.v1.InvocationAction; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static java.util.function.UnaryOperator.identity; +import static org.assertj.core.api.Assertions.assertThat; +import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.givenBanner; +import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.givenBidRequest; +import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.givenImpExt; + +@ExtendWith(MockitoExtension.class) +public class GreenbidsInvocationResultCreatorTest { + + @Test + public void createGreenbidsInvocationResultWhenNotExploration() { + // given + final Banner banner = givenBanner(); + final Imp imp = Imp.builder() + .id("adunitcodevalue") + .ext(givenImpExt()) + .banner(banner) + .build(); + + final BidRequest bidRequest = givenBidRequest(identity(), List.of(imp)); + final Map> impsBiddersFilterMap = givenImpsBiddersFilterMap(); + final GreenbidsConfig greenbidsConfig = givenConfig(0.0); + + // when + final GreenbidsInvocationResult result = GreenbidsInvocationResultCreator.create( + greenbidsConfig, bidRequest, impsBiddersFilterMap); + + // then + final Ortb2ImpExtResult ortb2ImpExtResult = result.getAnalyticsResult().getValues().get("adunitcodevalue"); + final Map keptInAuction = ortb2ImpExtResult.getGreenbids().getKeptInAuction(); + + assertThat(result.getInvocationAction()).isEqualTo(InvocationAction.update); + assertThat(ortb2ImpExtResult).isNotNull(); + assertThat(ortb2ImpExtResult.getGreenbids().getIsExploration()).isFalse(); + assertThat(ortb2ImpExtResult.getGreenbids().getFingerprint()).isNotNull(); + assertThat(keptInAuction.get("rubicon")).isTrue(); + assertThat(keptInAuction.get("appnexus")).isFalse(); + assertThat(keptInAuction.get("pubmatic")).isFalse(); + } + + @Test + public void createShouldReturnNoActionWhenExploration() { + // given + final Imp imp = Imp.builder() + .id("adunitcodevalue") + .ext(givenImpExt()) + .build(); + + final BidRequest bidRequest = givenBidRequest(identity(), List.of(imp)); + final Map> impsBiddersFilterMap = givenFilterMapWithAllFilteredImps(); + final GreenbidsConfig greenbidsConfig = givenConfig(1.0); + + // when + final GreenbidsInvocationResult result = GreenbidsInvocationResultCreator.create( + greenbidsConfig, bidRequest, impsBiddersFilterMap); + + // then + final Ortb2ImpExtResult ortb2ImpExtResult = result.getAnalyticsResult().getValues().get("adunitcodevalue"); + final Map keptInAuction = ortb2ImpExtResult.getGreenbids().getKeptInAuction(); + + assertThat(result.getInvocationAction()).isEqualTo(InvocationAction.no_action); + assertThat(ortb2ImpExtResult).isNotNull(); + assertThat(ortb2ImpExtResult.getGreenbids().getIsExploration()).isTrue(); + assertThat(ortb2ImpExtResult.getGreenbids().getFingerprint()).isNotNull(); + assertThat(keptInAuction.get("rubicon")).isFalse(); + assertThat(keptInAuction.get("appnexus")).isFalse(); + assertThat(keptInAuction.get("pubmatic")).isFalse(); + } + + @Test + public void createShouldReturnRejectWhenAllImpsAreFilteredOutAndNoExploration() { + // given + final Imp imp = Imp.builder() + .id("adunitcodevalue") + .ext(givenImpExt()) + .build(); + + final BidRequest bidRequest = givenBidRequest(identity(), List.of(imp)); + final Map> impsBiddersFilterMap = givenFilterMapWithAllFilteredImps(); + final GreenbidsConfig greenbidsConfig = givenConfig(0.001); + + // when + final GreenbidsInvocationResult result = GreenbidsInvocationResultCreator.create( + greenbidsConfig, bidRequest, impsBiddersFilterMap); + + // then + final Ortb2ImpExtResult ortb2ImpExtResult = result.getAnalyticsResult().getValues().get("adunitcodevalue"); + final Map keptInAuction = ortb2ImpExtResult.getGreenbids().getKeptInAuction(); + + assertThat(result.getInvocationAction()).isEqualTo(InvocationAction.reject); + assertThat(ortb2ImpExtResult).isNotNull(); + assertThat(ortb2ImpExtResult.getGreenbids().getIsExploration()).isFalse(); + assertThat(ortb2ImpExtResult.getGreenbids().getFingerprint()).isNotNull(); + assertThat(keptInAuction.get("rubicon")).isFalse(); + assertThat(keptInAuction.get("appnexus")).isFalse(); + assertThat(keptInAuction.get("pubmatic")).isFalse(); + } + + private Map> givenImpsBiddersFilterMap() { + final Map biddersFitlerMap = new HashMap<>(); + biddersFitlerMap.put("rubicon", true); + biddersFitlerMap.put("appnexus", false); + biddersFitlerMap.put("pubmatic", false); + + final Map> impsBiddersFilterMap = new HashMap<>(); + impsBiddersFilterMap.put("adunitcodevalue", biddersFitlerMap); + + return impsBiddersFilterMap; + } + + private Map> givenFilterMapWithAllFilteredImps() { + final Map biddersFitlerMap = new HashMap<>(); + biddersFitlerMap.put("rubicon", false); + biddersFitlerMap.put("appnexus", false); + biddersFitlerMap.put("pubmatic", false); + + final Map> impsBiddersFilterMap = new HashMap<>(); + impsBiddersFilterMap.put("adunitcodevalue", biddersFitlerMap); + + return impsBiddersFilterMap; + } + + private GreenbidsConfig givenConfig(Double explorationRate) { + return GreenbidsConfig.of("test-pbuid", 0.60, explorationRate); + } +} diff --git a/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationServiceTest.java b/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationServiceTest.java deleted file mode 100644 index 0c137ffa403..00000000000 --- a/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationServiceTest.java +++ /dev/null @@ -1,233 +0,0 @@ -package org.prebid.server.hooks.modules.greenbids.real.time.data.core; - -import com.fasterxml.jackson.databind.JsonNode; -import com.iab.openrtb.request.Banner; -import com.iab.openrtb.request.BidRequest; -import com.iab.openrtb.request.Device; -import com.iab.openrtb.request.Imp; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; -import org.prebid.server.analytics.reporter.greenbids.model.Ortb2ImpExtResult; -import org.prebid.server.hooks.modules.greenbids.real.time.data.model.data.GreenbidsConfig; -import org.prebid.server.hooks.modules.greenbids.real.time.data.model.result.GreenbidsInvocationResult; -import org.prebid.server.hooks.v1.InvocationAction; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static java.util.function.UnaryOperator.identity; -import static org.assertj.core.api.Assertions.assertThat; -import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.givenBanner; -import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.givenBidRequest; -import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.givenDevice; -import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.givenImpExt; -import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.givenImpExtToFilterAllBidders; - -@ExtendWith(MockitoExtension.class) -public class GreenbidsInvocationServiceTest { - - private GreenbidsInvocationService target; - - @BeforeEach - public void setUp() { - target = new GreenbidsInvocationService(); - } - - @Test - public void createGreenbidsInvocationResultShouldReturnUpdateBidRequestWhenNotExploration() { - // given - final Banner banner = givenBanner(); - final Imp imp = Imp.builder() - .id("adunitcodevalue") - .ext(givenImpExt()) - .banner(banner) - .build(); - final Device device = givenDevice(identity()); - final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device); - final Map> impsBiddersFilterMap = givenImpsBiddersFilterMap(); - final GreenbidsConfig greenbidsConfig = givenPartner(0.0); - - // when - final GreenbidsInvocationResult result = target.createGreenbidsInvocationResult( - greenbidsConfig, bidRequest, impsBiddersFilterMap); - - // then - final JsonNode updatedBidRequestExtPrebidBidders = result.getUpdatedBidRequest().getImp().getFirst().getExt() - .get("prebid").get("bidder"); - final Ortb2ImpExtResult ortb2ImpExtResult = result.getAnalyticsResult().getValues().get("adunitcodevalue"); - final Map keptInAuction = ortb2ImpExtResult.getGreenbids().getKeptInAuction(); - - assertThat(result.getInvocationAction()).isEqualTo(InvocationAction.update); - assertThat(updatedBidRequestExtPrebidBidders.has("rubicon")).isTrue(); - assertThat(updatedBidRequestExtPrebidBidders.has("appnexus")).isFalse(); - assertThat(updatedBidRequestExtPrebidBidders.has("pubmatic")).isFalse(); - assertThat(ortb2ImpExtResult).isNotNull(); - assertThat(ortb2ImpExtResult.getGreenbids().getIsExploration()).isFalse(); - assertThat(ortb2ImpExtResult.getGreenbids().getFingerprint()).isNotNull(); - assertThat(keptInAuction.get("rubicon")).isTrue(); - assertThat(keptInAuction.get("appnexus")).isFalse(); - assertThat(keptInAuction.get("pubmatic")).isFalse(); - } - - @Test - public void createGreenbidsInvocationResultShouldReturnNoActionWhenExploration() { - // given - final Banner banner = givenBanner(); - final Imp imp = Imp.builder() - .id("adunitcodevalue") - .ext(givenImpExt()) - .banner(banner) - .build(); - final Device device = givenDevice(identity()); - final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device); - final Map> impsBiddersFilterMap = givenImpsBiddersFilterMap(); - final GreenbidsConfig greenbidsConfig = givenPartner(1.0); - - // when - final GreenbidsInvocationResult result = target.createGreenbidsInvocationResult( - greenbidsConfig, bidRequest, impsBiddersFilterMap); - - // then - final JsonNode updatedBidRequestExtPrebidBidders = result.getUpdatedBidRequest().getImp().getFirst().getExt() - .get("prebid").get("bidder"); - final Ortb2ImpExtResult ortb2ImpExtResult = result.getAnalyticsResult().getValues().get("adunitcodevalue"); - final Map keptInAuction = ortb2ImpExtResult.getGreenbids().getKeptInAuction(); - - assertThat(result.getInvocationAction()).isEqualTo(InvocationAction.no_action); - assertThat(updatedBidRequestExtPrebidBidders.has("rubicon")).isTrue(); - assertThat(updatedBidRequestExtPrebidBidders.has("appnexus")).isTrue(); - assertThat(updatedBidRequestExtPrebidBidders.has("pubmatic")).isTrue(); - assertThat(ortb2ImpExtResult).isNotNull(); - assertThat(ortb2ImpExtResult.getGreenbids().getIsExploration()).isTrue(); - assertThat(ortb2ImpExtResult.getGreenbids().getFingerprint()).isNotNull(); - assertThat(keptInAuction.get("rubicon")).isTrue(); - assertThat(keptInAuction.get("appnexus")).isFalse(); - assertThat(keptInAuction.get("pubmatic")).isFalse(); - } - - @Test - public void createGreenbidsInvocationResultShouldReturnRejectWhenAllImpsFiltered() { - // given - final Banner banner = givenBanner(); - final Imp imp = Imp.builder() - .id("adunitcodevalue") - .ext(givenImpExt()) - .banner(banner) - .build(); - final Device device = givenDevice(identity()); - final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device); - final Map> impsBiddersFilterMap = givenFilterMapWithAllFilteredImps(); - final GreenbidsConfig greenbidsConfig = givenPartner(1.0); - - // when - final GreenbidsInvocationResult result = target.createGreenbidsInvocationResult( - greenbidsConfig, bidRequest, impsBiddersFilterMap); - - // then - final JsonNode updatedBidRequestExtPrebidBidders = result.getUpdatedBidRequest().getImp().getFirst().getExt() - .get("prebid").get("bidder"); - final Ortb2ImpExtResult ortb2ImpExtResult = result.getAnalyticsResult().getValues().get("adunitcodevalue"); - final Map keptInAuction = ortb2ImpExtResult.getGreenbids().getKeptInAuction(); - - assertThat(result.getInvocationAction()).isEqualTo(InvocationAction.reject); - assertThat(updatedBidRequestExtPrebidBidders.has("rubicon")).isTrue(); - assertThat(updatedBidRequestExtPrebidBidders.has("appnexus")).isTrue(); - assertThat(updatedBidRequestExtPrebidBidders.has("pubmatic")).isTrue(); - assertThat(ortb2ImpExtResult).isNotNull(); - assertThat(ortb2ImpExtResult.getGreenbids().getIsExploration()).isTrue(); - assertThat(ortb2ImpExtResult.getGreenbids().getFingerprint()).isNotNull(); - assertThat(keptInAuction.get("rubicon")).isFalse(); - assertThat(keptInAuction.get("appnexus")).isFalse(); - assertThat(keptInAuction.get("pubmatic")).isFalse(); - } - - @Test - public void createGreenbidsInvocationResultShouldRemoveImpFromUpdateBidRequestWhenAllBiddersFiltered() { - // given - final Banner banner = givenBanner(); - final Imp imp1 = Imp.builder() - .id("adunitcodevalue1") - .ext(givenImpExt()) - .banner(banner) - .build(); - final Imp imp2 = Imp.builder() - .id("adunitcodevalue2") - .ext(givenImpExtToFilterAllBidders()) - .banner(banner) - .build(); - final Device device = givenDevice(identity()); - final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp1, imp2), device); - final Map> impsBiddersFilterMap = givenFilterMapWithAllFilteredBiddersInImp(); - final GreenbidsConfig greenbidsConfig = givenPartner(0.0); - - // when - final GreenbidsInvocationResult result = target.createGreenbidsInvocationResult( - greenbidsConfig, bidRequest, impsBiddersFilterMap); - - // then - final JsonNode updatedBidRequestExtPrebidBidders = result.getUpdatedBidRequest().getImp().getFirst().getExt() - .get("prebid").get("bidder"); - final Ortb2ImpExtResult ortb2ImpExtResult = result.getAnalyticsResult().getValues().get("adunitcodevalue1"); - final Map keptInAuction = ortb2ImpExtResult.getGreenbids().getKeptInAuction(); - - assertThat(result.getInvocationAction()).isEqualTo(InvocationAction.update); - assertThat(result.getUpdatedBidRequest().getImp()).hasSize(1); - assertThat(updatedBidRequestExtPrebidBidders.has("rubicon")).isTrue(); - assertThat(updatedBidRequestExtPrebidBidders.has("appnexus")).isFalse(); - assertThat(updatedBidRequestExtPrebidBidders.has("pubmatic")).isFalse(); - assertThat(ortb2ImpExtResult).isNotNull(); - assertThat(ortb2ImpExtResult.getGreenbids().getIsExploration()).isFalse(); - assertThat(ortb2ImpExtResult.getGreenbids().getFingerprint()).isNotNull(); - assertThat(keptInAuction.get("rubicon")).isTrue(); - assertThat(keptInAuction.get("appnexus")).isFalse(); - assertThat(keptInAuction.get("pubmatic")).isFalse(); - - } - - private Map> givenImpsBiddersFilterMap() { - final Map biddersFitlerMap = new HashMap<>(); - biddersFitlerMap.put("rubicon", true); - biddersFitlerMap.put("appnexus", false); - biddersFitlerMap.put("pubmatic", false); - - final Map> impsBiddersFilterMap = new HashMap<>(); - impsBiddersFilterMap.put("adunitcodevalue", biddersFitlerMap); - - return impsBiddersFilterMap; - } - - private Map> givenFilterMapWithAllFilteredImps() { - final Map biddersFitlerMap = new HashMap<>(); - biddersFitlerMap.put("rubicon", false); - biddersFitlerMap.put("appnexus", false); - biddersFitlerMap.put("pubmatic", false); - - final Map> impsBiddersFilterMap = new HashMap<>(); - impsBiddersFilterMap.put("adunitcodevalue", biddersFitlerMap); - - return impsBiddersFilterMap; - } - - private Map> givenFilterMapWithAllFilteredBiddersInImp() { - final Map biddersFitlerMapForKeptImp = new HashMap<>(); - biddersFitlerMapForKeptImp.put("rubicon", true); - biddersFitlerMapForKeptImp.put("appnexus", false); - biddersFitlerMapForKeptImp.put("pubmatic", false); - - final Map biddersFitlerMapForRemovedImp = new HashMap<>(); - biddersFitlerMapForRemovedImp.put("appnexus", false); - - final Map> impsBiddersFilterMap = new HashMap<>(); - impsBiddersFilterMap.put("adunitcodevalue1", biddersFitlerMapForKeptImp); - impsBiddersFilterMap.put("adunitcodevalue2", biddersFitlerMapForRemovedImp); - - return impsBiddersFilterMap; - } - - private GreenbidsConfig givenPartner(Double explorationRate) { - return GreenbidsConfig.of("test-pbuid", 0.60, explorationRate); - } -} diff --git a/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsPayloadUpdaterTest.java b/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsPayloadUpdaterTest.java new file mode 100644 index 00000000000..36ed3f8e980 --- /dev/null +++ b/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsPayloadUpdaterTest.java @@ -0,0 +1,60 @@ +package org.prebid.server.hooks.modules.greenbids.real.time.data.core; + +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static java.util.function.UnaryOperator.identity; +import static org.assertj.core.api.Assertions.assertThat; +import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.getAppnexusNode; +import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.getPubmaticNode; +import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.getRubiconNode; +import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.givenBidRequest; +import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.givenImpExt; + +public class GreenbidsPayloadUpdaterTest { + + @Test + public void updateShouldReturnUpdatedBidRequest() { + // given + final Imp givenImp = Imp.builder() + .id("adunitcodevalue") + .ext(givenImpExt(getRubiconNode(), getAppnexusNode(), getPubmaticNode())) + .build(); + + // when + final BidRequest result = GreenbidsPayloadUpdater.update( + givenBidRequest(identity(), List.of(givenImp)), + Map.of("adunitcodevalue", Map.of("rubicon", true, "appnexus", false, "pubmatic", false))); + + // then + final Imp expectedImp = Imp.builder() + .id("adunitcodevalue") + .ext(givenImpExt(getRubiconNode(), null, null)) + .build(); + + assertThat(result.getImp()).containsOnly(expectedImp); + } + + @Test + public void updateShouldRemoveImpFromUpdateBidRequestWhenAllBiddersFiltered() { + // given + final Imp givenImp = Imp.builder() + .id("adunitcodevalue") + .ext(givenImpExt(getRubiconNode(), null, null)) + .build(); + + // when + final BidRequest result = GreenbidsPayloadUpdater.update( + givenBidRequest(identity(), List.of(givenImp)), + Map.of("adunitcodevalue", Map.of("rubicon", false, "appnexus", false, "pubmatic", false))); + + // then + assertThat(result.getImp()).isEmpty(); + + } + +} diff --git a/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/util/TestBidRequestProvider.java b/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/util/TestBidRequestProvider.java index 5e07f350869..7876bbda05e 100644 --- a/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/util/TestBidRequestProvider.java +++ b/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/util/TestBidRequestProvider.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.databind.node.TextNode; import com.iab.openrtb.request.Banner; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Device; @@ -11,8 +10,6 @@ import com.iab.openrtb.request.Imp; import com.iab.openrtb.request.Site; import org.prebid.server.json.ObjectMapperProvider; -import org.prebid.server.proto.openrtb.ext.request.ExtRequest; -import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid; import java.util.Collections; import java.util.List; @@ -24,114 +21,73 @@ public class TestBidRequestProvider { private TestBidRequestProvider() { } - public static BidRequest givenBidRequest( - UnaryOperator bidRequestCustomizer, - List imps, - Device device) { + public static BidRequest givenBidRequest(UnaryOperator bidRequestCustomizer, + List imps) { return bidRequestCustomizer.apply(BidRequest.builder() .id("request") .imp(imps) - .site(givenSite(site -> site)) - .device(device)).build(); + .site(givenSite()) + .device(givenDevice())) + .build(); } - public static BidRequest givenBidRequestWithExtension( - UnaryOperator bidRequestCustomizer, - List imps) { - final BidRequest.BidRequestBuilder bidRequestBuilder = BidRequest.builder() - .id("request") - .imp(imps) - .site(givenSite(site -> site)) - .device(givenDevice(device -> device)) - .ext(givenExtRequest()); - - return bidRequestCustomizer.apply(bidRequestBuilder).build(); + public static Site givenSite() { + return Site.builder().domain("www.leparisien.fr").build(); } - public static ExtRequest givenExtRequest() { - final ObjectNode greenbidsNode = new ObjectMapper().createObjectNode(); - greenbidsNode.put("pbuid", "leparisien"); - greenbidsNode.put("greenbidsSampling", 1.0); - - final ObjectNode analyticsNode = new ObjectMapper().createObjectNode(); - analyticsNode.set("greenbids", greenbidsNode); - - return ExtRequest.of( - ExtRequestPrebid - .builder() - .analytics(analyticsNode) - .build()); - } - - public static Site givenSite(UnaryOperator siteCustomizer) { - return siteCustomizer.apply(Site.builder().domain("www.leparisien.fr")).build(); + public static ObjectNode givenImpExt() { + return givenImpExt(getRubiconNode(), getAppnexusNode(), getPubmaticNode()); } - public static ObjectNode givenImpExt() { + public static ObjectNode givenImpExt(ObjectNode rubiconNode, ObjectNode appnexusNode, ObjectNode pubmaticNode) { final ObjectNode bidderNode = MAPPER.createObjectNode(); - final ObjectNode rubiconNode = MAPPER.createObjectNode(); - rubiconNode.put("accountId", 1001); - rubiconNode.put("siteId", 267318); - rubiconNode.put("zoneId", 1861698); - bidderNode.set("rubicon", rubiconNode); - - final ObjectNode appnexusNode = MAPPER.createObjectNode(); - appnexusNode.put("placementId", 123456); - bidderNode.set("appnexus", appnexusNode); + if (rubiconNode != null) { + bidderNode.set("rubicon", rubiconNode); + } - final ObjectNode pubmaticNode = MAPPER.createObjectNode(); - pubmaticNode.put("publisherId", "156209"); - pubmaticNode.put("adSlot", "slot1@300x250"); - bidderNode.set("pubmatic", pubmaticNode); + if (appnexusNode != null) { + bidderNode.set("appnexus", appnexusNode); + } - final ObjectNode prebidNode = MAPPER.createObjectNode(); - prebidNode.set("bidder", bidderNode); + if (pubmaticNode != null) { + bidderNode.set("pubmatic", pubmaticNode); + } - final ObjectNode extNode = MAPPER.createObjectNode(); - extNode.set("prebid", prebidNode); - extNode.set("tid", TextNode.valueOf("67eaab5f-27a6-4689-93f7-bd8f024576e3")); - - return extNode; + return MAPPER.createObjectNode() + .put("tid", "67eaab5f-27a6-4689-93f7-bd8f024576e3") + .set("prebid", MAPPER.createObjectNode().set("bidder", bidderNode)); } - public static ObjectNode givenImpExtToFilterAllBidders() { - final ObjectNode bidderNode = MAPPER.createObjectNode(); - - final ObjectNode appnexusNode = MAPPER.createObjectNode(); - appnexusNode.put("placementId", 789); - bidderNode.set("appnexus", appnexusNode); - - final ObjectNode prebidNode = MAPPER.createObjectNode(); - prebidNode.set("bidder", bidderNode); + public static ObjectNode getPubmaticNode() { + return MAPPER.createObjectNode() + .put("publisherId", "156209") + .put("adSlot", "slot1@300x250"); + } - final ObjectNode extNode = MAPPER.createObjectNode(); - extNode.set("prebid", prebidNode); - extNode.set("tid", TextNode.valueOf("af65045c-2774-44c2-a949-4f42d5c9e179")); + public static ObjectNode getAppnexusNode() { + return MAPPER.createObjectNode().put("placementId", 123456); + } - return extNode; + public static ObjectNode getRubiconNode() { + return MAPPER.createObjectNode() + .put("accountId", 1001) + .put("siteId", 267318) + .put("zoneId", 1861698); } - public static Device givenDevice(UnaryOperator deviceCustomizer, String countryAlpha3) { + public static Device givenDevice(String countryAlpha3) { final String userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36" + " (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36"; - final Geo geo = givenGeoWithCountry(countryAlpha3); - return deviceCustomizer.apply(Device.builder().ua(userAgent).ip("151.101.194.216").geo(geo)).build(); + final Geo geo = Geo.builder().country(countryAlpha3).build(); + return Device.builder().ua(userAgent).ip("151.101.194.216").geo(geo).build(); } - public static Device givenDevice(UnaryOperator deviceCustomizer) { + public static Device givenDevice() { final String userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36" + " (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36"; - return deviceCustomizer.apply(Device.builder().ua(userAgent).ip("151.101.194.216")).build(); - } - - public static Geo givenGeoWithCountry(String countryAlpha3) { - return Geo.builder().country(countryAlpha3).build(); - } - - public static Device givenDeviceWithoutUserAgent(UnaryOperator deviceCustomizer) { - return deviceCustomizer.apply(Device.builder().ip("151.101.194.216")).build(); + return Device.builder().ua(userAgent).ip("151.101.194.216").build(); } public static Banner givenBanner() { diff --git a/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHookTest.java b/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHookTest.java index fa68d5d42f1..5e9780e7e2f 100644 --- a/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHookTest.java +++ b/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHookTest.java @@ -1,306 +1,137 @@ package org.prebid.server.hooks.modules.greenbids.real.time.data.v1; -import ai.onnxruntime.OrtException; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.databind.node.TextNode; -import com.github.benmanes.caffeine.cache.Cache; -import com.google.cloud.storage.Storage; -import com.google.cloud.storage.StorageOptions; -import com.iab.openrtb.request.Banner; import com.iab.openrtb.request.BidRequest; -import com.iab.openrtb.request.Device; import com.iab.openrtb.request.Imp; -import com.maxmind.geoip2.DatabaseReader; -import com.maxmind.geoip2.exception.GeoIp2Exception; -import com.maxmind.geoip2.model.CountryResponse; -import com.maxmind.geoip2.record.Country; import io.vertx.core.Future; -import io.vertx.core.Vertx; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.prebid.server.analytics.reporter.greenbids.model.ExplorationResult; -import org.prebid.server.analytics.reporter.greenbids.model.Ortb2ImpExtResult; -import org.prebid.server.auction.model.AuctionContext; -import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.auction.model.RejectedImp; -import org.prebid.server.geolocation.CountryCodeMapper; import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; -import org.prebid.server.hooks.execution.v1.analytics.AppliedToImpl; import org.prebid.server.hooks.execution.v1.analytics.ResultImpl; -import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; +import org.prebid.server.hooks.execution.v1.auction.AuctionInvocationContextImpl; import org.prebid.server.hooks.execution.v1.auction.AuctionRequestPayloadImpl; -import org.prebid.server.hooks.modules.greenbids.real.time.data.config.DatabaseReaderFactory; import org.prebid.server.hooks.modules.greenbids.real.time.data.core.FilterService; import org.prebid.server.hooks.modules.greenbids.real.time.data.core.GreenbidsInferenceDataService; -import org.prebid.server.hooks.modules.greenbids.real.time.data.core.GreenbidsInvocationService; -import org.prebid.server.hooks.modules.greenbids.real.time.data.core.ModelCache; import org.prebid.server.hooks.modules.greenbids.real.time.data.core.OnnxModelRunner; -import org.prebid.server.hooks.modules.greenbids.real.time.data.core.OnnxModelRunnerFactory; import org.prebid.server.hooks.modules.greenbids.real.time.data.core.OnnxModelRunnerWithThresholds; -import org.prebid.server.hooks.modules.greenbids.real.time.data.core.ThresholdCache; -import org.prebid.server.hooks.modules.greenbids.real.time.data.core.ThrottlingThresholdsFactory; -import org.prebid.server.hooks.modules.greenbids.real.time.data.model.filter.ThrottlingThresholds; -import org.prebid.server.hooks.modules.greenbids.real.time.data.model.result.AnalyticsResult; -import org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider; import org.prebid.server.hooks.v1.InvocationAction; import org.prebid.server.hooks.v1.InvocationResult; import org.prebid.server.hooks.v1.InvocationStatus; -import org.prebid.server.hooks.v1.analytics.Result; -import org.prebid.server.hooks.v1.analytics.Tags; +import org.prebid.server.hooks.v1.analytics.AppliedTo; import org.prebid.server.hooks.v1.auction.AuctionInvocationContext; import org.prebid.server.hooks.v1.auction.AuctionRequestPayload; -import org.prebid.server.model.HttpRequestContext; -import java.io.IOException; -import java.net.InetAddress; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.function.UnaryOperator; import static java.util.function.UnaryOperator.identity; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mock.Strictness.LENIENT; +import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.prebid.server.auction.model.BidRejectionReason.*; -import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.givenBanner; +import static org.prebid.server.auction.model.BidRejectionReason.REQUEST_BLOCKED_OPTIMIZED; +import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.MAPPER; +import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.getRubiconNode; import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.givenBidRequest; -import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.givenBidRequestWithExtension; -import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.givenDevice; -import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.givenDeviceWithoutUserAgent; -import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.givenExtRequest; import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.givenImpExt; -import static org.prebid.server.hooks.modules.greenbids.real.time.data.util.TestBidRequestProvider.givenSite; @ExtendWith(MockitoExtension.class) public class GreenbidsRealTimeDataProcessedAuctionRequestHookTest { @Mock - private Cache modelCacheWithExpiration; + private FilterService filterService; @Mock - private Cache thresholdsCacheWithExpiration; - - @Mock(strictness = LENIENT) - private DatabaseReaderFactory databaseReaderFactory; - - @Mock(strictness = LENIENT) - private DatabaseReader databaseReader; - - @Mock(strictness = LENIENT) - private CountryResponse countryResponse; - - @Mock(strictness = LENIENT) - private Country country; + private OnnxModelRunnerWithThresholds onnxModelRunnerWithThresholds; @Mock - private CountryCodeMapper countryCodeMapper; + private GreenbidsInferenceDataService greenbidsInferenceDataService; private GreenbidsRealTimeDataProcessedAuctionRequestHook target; @BeforeEach - public void setUp() throws IOException, GeoIp2Exception { - final Storage storage = StorageOptions.newBuilder() - .setProjectId("test_project").build().getService(); + public void setUp() { + given(onnxModelRunnerWithThresholds.retrieveOnnxModelRunner(any())) + .willReturn(Future.succeededFuture(mock(OnnxModelRunner.class))); + given(onnxModelRunnerWithThresholds.retrieveThreshold(any())) + .willReturn(Future.succeededFuture(18.2d)); + given(greenbidsInferenceDataService.extractThrottlingMessagesFromBidRequest(any())) + .willReturn(Collections.emptyList()); - when(country.getName()).thenReturn("United States"); - when(countryResponse.getCountry()).thenReturn(country); - when(databaseReader.country(any(InetAddress.class))).thenReturn(countryResponse); - when(databaseReaderFactory.getDatabaseReader()).thenReturn(databaseReader); - - final FilterService filterService = new FilterService(); - final OnnxModelRunnerFactory onnxModelRunnerFactory = new OnnxModelRunnerFactory(); - final ThrottlingThresholdsFactory throttlingThresholdsFactory = new ThrottlingThresholdsFactory(); - final ModelCache modelCache = new ModelCache( - storage, - "test_bucket", - modelCacheWithExpiration, - "onnxModelRunner_", - Vertx.vertx(), - onnxModelRunnerFactory); - final ThresholdCache thresholdCache = new ThresholdCache( - storage, - "test_bucket", - TestBidRequestProvider.MAPPER, - thresholdsCacheWithExpiration, - "throttlingThresholds_", - Vertx.vertx(), - throttlingThresholdsFactory); - final OnnxModelRunnerWithThresholds onnxModelRunnerWithThresholds = new OnnxModelRunnerWithThresholds( - modelCache, - thresholdCache); - - final GreenbidsInferenceDataService greenbidsInferenceDataService = new GreenbidsInferenceDataService( - databaseReaderFactory, - TestBidRequestProvider.MAPPER, - countryCodeMapper); - final GreenbidsInvocationService greenbidsInvocationService = new GreenbidsInvocationService(); target = new GreenbidsRealTimeDataProcessedAuctionRequestHook( - TestBidRequestProvider.MAPPER, + MAPPER, filterService, onnxModelRunnerWithThresholds, - greenbidsInferenceDataService, - greenbidsInvocationService); - } - - @Test - public void callShouldFilterBiddersWhenPartnerActivatedInBidRequest() - throws IOException, OrtException { - // given - final Banner banner = givenBanner(); - - final Imp imp = Imp.builder() - .id("adunitcodevalue") - .ext(givenImpExt()) - .banner(banner) - .build(); - - final Double explorationRate = 0.0001; - final Device device = givenDevice(identity()); - final BidRequest bidRequest = givenBidRequestWithExtension(identity(), List.of(imp)); - final AuctionContext auctionContext = givenAuctionContext( - bidRequest, - context -> context); - final AuctionInvocationContext invocationContext = givenAuctionInvocationContext( - auctionContext, explorationRate); - when(invocationContext.auctionContext()).thenReturn(auctionContext); - when(modelCacheWithExpiration.getIfPresent("onnxModelRunner_test-pbuid")) - .thenReturn(givenOnnxModelRunner()); - when(thresholdsCacheWithExpiration.getIfPresent("throttlingThresholds_test-pbuid")) - .thenReturn(givenThrottlingThresholds()); - - final BidRequest expectedBidRequest = expectedUpdatedBidRequest( - request -> request, device, true); - final AnalyticsResult expectedAnalyticsResult = expectedAnalyticsResult(false, true); - - // when - final Future> future = target - .call(null, invocationContext); - final InvocationResult result = future.result(); - final BidRequest resultBidRequest = result - .payloadUpdate() - .apply(AuctionRequestPayloadImpl.of(bidRequest)) - .bidRequest(); - - // then - final ActivityImpl activityImpl = (ActivityImpl) result.analyticsTags().activities().getFirst(); - final ResultImpl resultImpl = (ResultImpl) activityImpl.results().getFirst(); - final String fingerprint = resultImpl.values() - .get("adunitcodevalue") - .get("greenbids") - .get("fingerprint").asText(); - - assertThat(future).isNotNull(); - assertThat(future.succeeded()).isTrue(); - assertThat(result).isNotNull(); - assertThat(result.status()).isEqualTo(InvocationStatus.success); - assertThat(result.action()).isEqualTo(InvocationAction.update); - assertThat(result.analyticsTags()).isNotNull(); - assertThat(result.analyticsTags()).usingRecursiveComparison() - .ignoringFields( - "activities.results" - + ".values._children" - + ".adunitcodevalue._children" - + ".greenbids._children.fingerprint") - .isEqualTo(toAnalyticsTags(List.of(expectedAnalyticsResult))); - assertThat(fingerprint).isNotNull(); - assertThat(resultBidRequest).usingRecursiveComparison() - .isEqualTo(expectedBidRequest); + greenbidsInferenceDataService); } @Test - public void callShouldFilterBiddersAndReturnAnalyticsTagWhenExploration() throws OrtException, IOException { + public void callShouldReturnAnalyticTagsWithoutFilteringOutBiddersWhenExplorationIsTrue() { // given - final Banner banner = givenBanner(); - final Imp imp = Imp.builder() .id("adunitcodevalue") .ext(givenImpExt()) - .banner(banner) .build(); final Double explorationRate = 1.0; - final Device device = givenDevice(identity()); - final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device); - final AuctionContext auctionContext = givenAuctionContext(bidRequest, context -> context); - final AuctionInvocationContext invocationContext = givenAuctionInvocationContext( - auctionContext, explorationRate); - when(invocationContext.auctionContext()).thenReturn(auctionContext); - when(modelCacheWithExpiration.getIfPresent("onnxModelRunner_test-pbuid")) - .thenReturn(givenOnnxModelRunner()); - when(thresholdsCacheWithExpiration.getIfPresent("throttlingThresholds_test-pbuid")) - .thenReturn(givenThrottlingThresholds()); + final BidRequest bidRequest = givenBidRequest(identity(), List.of(imp)); + final AuctionInvocationContext invocationContext = givenAuctionInvocationContext(explorationRate); - final AnalyticsResult expectedAnalyticsResult = expectedAnalyticsResult(true, true); + given(filterService.filterBidders(any(), any(), any())).willReturn(Map.of("adunitcodevalue", + Map.of("rubicon", false, "appnexus", false, "pubmatic", false))); // when final Future> future = target - .call(null, invocationContext); + .call(AuctionRequestPayloadImpl.of(bidRequest), invocationContext); final InvocationResult result = future.result(); // then - final ActivityImpl activity = (ActivityImpl) result.analyticsTags().activities().getFirst(); - final ResultImpl resultImpl = (ResultImpl) activity.results().getFirst(); - final String fingerprint = resultImpl.values() - .get("adunitcodevalue") - .get("greenbids") - .get("fingerprint").asText(); - - assertThat(future).isNotNull(); assertThat(future.succeeded()).isTrue(); - assertThat(result).isNotNull(); assertThat(result.status()).isEqualTo(InvocationStatus.success); assertThat(result.action()).isEqualTo(InvocationAction.no_action); - assertThat(result.analyticsTags()).isNotNull(); - assertThat(result.analyticsTags()).usingRecursiveComparison() - .ignoringFields( - "activities.results" - + ".values._children" - + ".adunitcodevalue._children" - + ".greenbids._children.fingerprint") - .isEqualTo(toAnalyticsTags(List.of(expectedAnalyticsResult))); - assertThat(fingerprint).isNotNull(); + + final ActivityImpl actualActivity = (ActivityImpl) result.analyticsTags().activities().getFirst(); + final ResultImpl actualResult = (ResultImpl) actualActivity.results().getFirst(); + final AppliedTo acctualAppliedTo = actualResult.appliedTo(); + + assertThat(acctualAppliedTo.bidders()).containsOnly("appnexus", "pubmatic", "rubicon"); + assertThat(acctualAppliedTo.impIds()).containsOnly("adunitcodevalue"); + assertThat(actualResult.values().get("adunitcodevalue").get("greenbids").get("keptInAuction")) + .isEqualTo(MAPPER.createObjectNode() + .put("rubicon", false) + .put("appnexus", false) + .put("pubmatic", false)); + assertThat(actualResult.values().get("adunitcodevalue").get("greenbids").get("fingerprint").asText()) + .isNotNull(); + assertThat(actualResult.values().get("adunitcodevalue").get("tid").asText()) + .isEqualTo("67eaab5f-27a6-4689-93f7-bd8f024576e3"); + assertThat(result.rejections()).isNull(); } @Test - public void callShouldFilterBiddersBasedOnModelWhenAnyFeatureNotAvailable() throws OrtException, IOException { + public void callShouldFilterBiddersBasedOnModelResultsWhenExplorationIsFalse() { // given - final Banner banner = givenBanner(); - final Imp imp = Imp.builder() .id("adunitcodevalue") .ext(givenImpExt()) - .banner(banner) .build(); final Double explorationRate = 0.0001; - final Device device = givenDeviceWithoutUserAgent(identity()); - final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device); - final AuctionContext auctionContext = givenAuctionContext(bidRequest, context -> context); - final AuctionInvocationContext invocationContext = givenAuctionInvocationContext( - auctionContext, explorationRate); - when(invocationContext.auctionContext()).thenReturn(auctionContext); - when(modelCacheWithExpiration.getIfPresent("onnxModelRunner_test-pbuid")) - .thenReturn(givenOnnxModelRunner()); - when(thresholdsCacheWithExpiration.getIfPresent("throttlingThresholds_test-pbuid")) - .thenReturn(givenThrottlingThresholds()); + final BidRequest bidRequest = givenBidRequest(identity(), List.of(imp)); + final AuctionInvocationContext invocationContext = givenAuctionInvocationContext(explorationRate); - final BidRequest expectedBidRequest = expectedUpdatedBidRequest( - request -> request, device, false); - final AnalyticsResult expectedAnalyticsResult = expectedAnalyticsResult(false, true); + given(filterService.filterBidders(any(), any(), any())).willReturn(Map.of("adunitcodevalue", + Map.of("rubicon", true, "appnexus", false, "pubmatic", false))); // when final Future> future = target - .call(null, invocationContext); + .call(AuctionRequestPayloadImpl.of(bidRequest), invocationContext); final InvocationResult result = future.result(); final BidRequest resultBidRequest = result .payloadUpdate() @@ -308,230 +139,51 @@ public void callShouldFilterBiddersBasedOnModelWhenAnyFeatureNotAvailable() thro .bidRequest(); // then - final ActivityImpl activity = (ActivityImpl) result.analyticsTags().activities().getFirst(); - final ResultImpl resultImpl = (ResultImpl) activity.results().getFirst(); - final String fingerprint = resultImpl.values() - .get("adunitcodevalue") - .get("greenbids") - .get("fingerprint").asText(); - - assertThat(future).isNotNull(); assertThat(future.succeeded()).isTrue(); - assertThat(result).isNotNull(); assertThat(result.status()).isEqualTo(InvocationStatus.success); assertThat(result.action()).isEqualTo(InvocationAction.update); - assertThat(result.analyticsTags()).isNotNull(); - assertThat(result.analyticsTags()).usingRecursiveComparison() - .ignoringFields( - "activities.results" - + ".values._children" - + ".adunitcodevalue._children" - + ".greenbids._children.fingerprint") - .isEqualTo(toAnalyticsTags(List.of(expectedAnalyticsResult))); - assertThat(fingerprint).isNotNull(); - assertThat(resultBidRequest).usingRecursiveComparison().isEqualTo(expectedBidRequest); - } - - @Test - public void callShouldFilterBiddersBasedOnModelResults() throws OrtException, IOException { - // given - final Banner banner = givenBanner(); - final Imp imp = Imp.builder() + final Imp expectedImp = Imp.builder() .id("adunitcodevalue") - .ext(givenImpExt()) - .banner(banner) + .ext(givenImpExt(getRubiconNode(), null, null)) .build(); - - final Double explorationRate = 0.0001; - final Device device = givenDevice(identity()); - final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device); - final AuctionContext auctionContext = givenAuctionContext(bidRequest, context -> context); - final AuctionInvocationContext invocationContext = givenAuctionInvocationContext( - auctionContext, explorationRate); - when(invocationContext.auctionContext()).thenReturn(auctionContext); - when(modelCacheWithExpiration.getIfPresent("onnxModelRunner_test-pbuid")) - .thenReturn(givenOnnxModelRunner()); - when(thresholdsCacheWithExpiration.getIfPresent("throttlingThresholds_test-pbuid")) - .thenReturn(givenThrottlingThresholds()); - - final BidRequest expectedBidRequest = expectedUpdatedBidRequest( - request -> request, device, false); - final AnalyticsResult expectedAnalyticsResult = expectedAnalyticsResult(false, true); - - // when - final Future> future = target - .call(null, invocationContext); - final InvocationResult result = future.result(); - final BidRequest resultBidRequest = result - .payloadUpdate() - .apply(AuctionRequestPayloadImpl.of(bidRequest)) - .bidRequest(); - - // then - final ActivityImpl activityImpl = (ActivityImpl) result.analyticsTags().activities().getFirst(); - final ResultImpl resultImpl = (ResultImpl) activityImpl.results().getFirst(); - final String fingerprint = resultImpl.values() - .get("adunitcodevalue") - .get("greenbids") - .get("fingerprint").asText(); - - assertThat(future).isNotNull(); - assertThat(future.succeeded()).isTrue(); - assertThat(result).isNotNull(); - assertThat(result.status()).isEqualTo(InvocationStatus.success); - assertThat(result.action()).isEqualTo(InvocationAction.update); - assertThat(result.analyticsTags()).isNotNull(); - assertThat(result.analyticsTags()).usingRecursiveComparison() - .ignoringFields( - "activities.results" - + ".values._children" - + ".adunitcodevalue._children" - + ".greenbids._children.fingerprint") - .isEqualTo(toAnalyticsTags(List.of(expectedAnalyticsResult))); - assertThat(fingerprint).isNotNull(); - assertThat(resultBidRequest).usingRecursiveComparison() - .isEqualTo(expectedBidRequest); + assertThat(resultBidRequest).isEqualTo(givenBidRequest(identity(), List.of(expectedImp))); + + final ActivityImpl actualActivity = (ActivityImpl) result.analyticsTags().activities().getFirst(); + final ResultImpl actualResult = (ResultImpl) actualActivity.results().getFirst(); + final AppliedTo acctualAppliedTo = actualResult.appliedTo(); + + assertThat(acctualAppliedTo.bidders()).containsOnly("appnexus", "pubmatic"); + assertThat(acctualAppliedTo.impIds()).containsOnly("adunitcodevalue"); + assertThat(actualResult.values().get("adunitcodevalue").get("greenbids").get("keptInAuction")) + .isEqualTo(MAPPER.createObjectNode() + .put("rubicon", true) + .put("appnexus", false) + .put("pubmatic", false)); + assertThat(actualResult.values().get("adunitcodevalue").get("greenbids").get("fingerprint").asText()) + .isNotNull(); + assertThat(actualResult.values().get("adunitcodevalue").get("tid").asText()) + .isEqualTo("67eaab5f-27a6-4689-93f7-bd8f024576e3"); assertThat(result.rejections()).containsOnly( entry("appnexus", List.of(RejectedImp.of("adunitcodevalue", REQUEST_BLOCKED_OPTIMIZED))), - entry("pubmatic", List.of(RejectedImp.of("adunitcodevalue", REQUEST_BLOCKED_OPTIMIZED))), - entry("rubicon", List.of(RejectedImp.of("adunitcodevalue", REQUEST_BLOCKED_OPTIMIZED)))); + entry("pubmatic", List.of(RejectedImp.of("adunitcodevalue", REQUEST_BLOCKED_OPTIMIZED)))); } - private AuctionContext givenAuctionContext( - BidRequest bidRequest, - UnaryOperator auctionContextCustomizer) { - - final AuctionContext.AuctionContextBuilder auctionContextBuilder = AuctionContext.builder() - .httpRequest(HttpRequestContext.builder().build()) - .bidRequest(bidRequest); - - return auctionContextCustomizer.apply(auctionContextBuilder).build(); - } - - private AuctionInvocationContext givenAuctionInvocationContext( - AuctionContext auctionContext, Double explorationRate) { - final AuctionInvocationContext invocationContext = mock(AuctionInvocationContext.class); - when(invocationContext.auctionContext()).thenReturn(auctionContext); - when(invocationContext.accountConfig()).thenReturn(givenAccountConfig(explorationRate)); - return invocationContext; + private AuctionInvocationContext givenAuctionInvocationContext(Double explorationRate) { + return AuctionInvocationContextImpl.of( + null, + null, + false, + givenAccountConfig(explorationRate), + null); } private ObjectNode givenAccountConfig(Double explorationRate) { - final ObjectNode greenbidsNode = TestBidRequestProvider.MAPPER.createObjectNode(); + final ObjectNode greenbidsNode = MAPPER.createObjectNode(); greenbidsNode.put("enabled", true); greenbidsNode.put("pbuid", "test-pbuid"); greenbidsNode.put("target-tpr", 0.99); greenbidsNode.put("exploration-rate", explorationRate); return greenbidsNode; } - - private OnnxModelRunner givenOnnxModelRunner() throws OrtException, IOException { - final byte[] onnxModelBytes = Files.readAllBytes(Paths.get( - "src/test/resources/models_pbuid=test-pbuid.onnx")); - return new OnnxModelRunner(onnxModelBytes); - } - - private ThrottlingThresholds givenThrottlingThresholds() throws IOException { - final JsonNode thresholdsJsonNode = TestBidRequestProvider.MAPPER.readTree( - Files.newInputStream(Paths.get( - "src/test/resources/thresholds_pbuid=test-pbuid.json"))); - return TestBidRequestProvider.MAPPER - .treeToValue(thresholdsJsonNode, ThrottlingThresholds.class); - } - - private BidRequest expectedUpdatedBidRequest( - UnaryOperator bidRequestCustomizer, - Device device, - Boolean isExtRequest) { - - final Banner banner = givenBanner(); - - final ObjectNode bidderNode = TestBidRequestProvider.MAPPER.createObjectNode(); - - final ObjectNode rubiconNode = TestBidRequestProvider.MAPPER.createObjectNode(); - rubiconNode.put("accountId", 1001); - rubiconNode.put("siteId", 267318); - rubiconNode.put("zoneId", 1861698); - bidderNode.set("rubicon", rubiconNode); - - final ObjectNode appnexusNode = TestBidRequestProvider.MAPPER.createObjectNode(); - appnexusNode.put("placementId", 123456); - bidderNode.set("appnexus", appnexusNode); - - final ObjectNode pubmaticNode = TestBidRequestProvider.MAPPER.createObjectNode(); - pubmaticNode.put("publisherId", "156209"); - pubmaticNode.put("adSlot", "slot1@300x250"); - bidderNode.set("pubmatic", pubmaticNode); - - final ObjectNode prebidNode = TestBidRequestProvider.MAPPER.createObjectNode(); - prebidNode.set("bidder", bidderNode); - - final ObjectNode extNode = TestBidRequestProvider.MAPPER.createObjectNode(); - extNode.set("prebid", prebidNode); - extNode.set("tid", TextNode.valueOf("67eaab5f-27a6-4689-93f7-bd8f024576e3")); - - final Imp imp = Imp.builder() - .id("adunitcodevalue") - .ext(extNode) - .banner(banner) - .build(); - - final BidRequest.BidRequestBuilder bidRequestBuilder = BidRequest.builder() - .id("request") - .imp(List.of(imp)) - .site(givenSite(site -> site)) - .device(device); - - if (isExtRequest) { - bidRequestBuilder.ext(givenExtRequest()); - } - - return bidRequestCustomizer.apply(bidRequestBuilder).build(); - } - - private AnalyticsResult expectedAnalyticsResult(Boolean isExploration, Boolean isKeptInAuction) { - return AnalyticsResult.of( - "success", - Map.of("adunitcodevalue", expectedOrtb2ImpExtResult(isExploration, isKeptInAuction))); - } - - private Ortb2ImpExtResult expectedOrtb2ImpExtResult(Boolean isExploration, Boolean isKeptInAuction) { - return Ortb2ImpExtResult.of( - expectedExplorationResult(isExploration, isKeptInAuction), "67eaab5f-27a6-4689-93f7-bd8f024576e3"); - } - - private ExplorationResult expectedExplorationResult(Boolean isExploration, Boolean isKeptInAuction) { - final Map keptInAuction = Map.of( - "appnexus", isKeptInAuction, - "pubmatic", isKeptInAuction, - "rubicon", isKeptInAuction); - return ExplorationResult.of("60a7c66c-c542-48c6-a319-ea7b9f97947f", keptInAuction, isExploration); - } - - private Tags toAnalyticsTags(List analyticsResults) { - return TagsImpl.of(Collections.singletonList(ActivityImpl.of( - "greenbids-filter", - "success", - toResults(analyticsResults)))); - } - - private List toResults(List analyticsResults) { - return analyticsResults.stream() - .map(this::toResult) - .toList(); - } - - private Result toResult(AnalyticsResult analyticsResult) { - return ResultImpl.of( - analyticsResult.getStatus(), - toObjectNode(analyticsResult.getValues()), - AppliedToImpl.builder() - .impIds(Collections.singletonList("adunitcodevalue")) - .build()); - } - - private ObjectNode toObjectNode(Map values) { - return values != null ? TestBidRequestProvider.MAPPER.valueToTree(values) : null; - } } From a53098198d544f70d3e749a314e81de85ff957be Mon Sep 17 00:00:00 2001 From: antonbabak Date: Thu, 26 Jun 2025 15:25:09 +0200 Subject: [PATCH 4/5] Fix conflicts --- .../GreenbidsRealTimeDataConfiguration.java | 2 - .../GreenbidsInvocationResultCreator.java | 4 +- .../data/core/GreenbidsInvocationService.java | 122 ------------------ ...alTimeDataProcessedAuctionRequestHook.java | 3 +- .../GreenbidsInferenceDataServiceTest.java | 2 - .../ortb2/blocking/core/BidsBlockerTest.java | 6 +- ...rtb2BlockingRawBidderResponseHookTest.java | 4 - .../v1/model/BidderInvocationContextImpl.java | 1 - ...ilterAllProcessedBidResponsesHookTest.java | 1 - .../greenbids/GreenbidsAnalyticsReporter.java | 4 +- .../server/auction/BidResponseCreator.java | 9 +- .../auction/model/BidRejectionTracker.java | 18 ++- .../prebid/server/auction/model/Rejected.java | 2 + .../server/auction/model/RejectedBid.java | 5 + .../server/auction/model/RejectedImp.java | 6 + .../validation/ResponseBidValidator.java | 2 +- .../auction/BidResponseCreatorTest.java | 3 +- .../server/auction/ExchangeServiceTest.java | 17 ++- .../model/BidRejectionTrackerTest.java | 63 +++++---- .../execution/HookStageExecutorTest.java | 24 ++-- .../validation/ResponseBidValidatorTest.java | 3 +- 21 files changed, 95 insertions(+), 206 deletions(-) delete mode 100644 extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationService.java diff --git a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/config/GreenbidsRealTimeDataConfiguration.java b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/config/GreenbidsRealTimeDataConfiguration.java index 021258fae39..ec8cb4c10b1 100644 --- a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/config/GreenbidsRealTimeDataConfiguration.java +++ b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/config/GreenbidsRealTimeDataConfiguration.java @@ -8,8 +8,6 @@ import org.prebid.server.geolocation.CountryCodeMapper; import org.prebid.server.hooks.modules.greenbids.real.time.data.core.FilterService; import org.prebid.server.hooks.modules.greenbids.real.time.data.core.GreenbidsInferenceDataService; -import org.prebid.server.hooks.modules.greenbids.real.time.data.core.GreenbidsInvocationService; -import org.prebid.server.hooks.modules.greenbids.real.time.data.core.GreenbidsInferenceDataService; import org.prebid.server.hooks.modules.greenbids.real.time.data.core.ModelCache; import org.prebid.server.hooks.modules.greenbids.real.time.data.core.OnnxModelRunner; import org.prebid.server.hooks.modules.greenbids.real.time.data.core.OnnxModelRunnerFactory; diff --git a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationResultCreator.java b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationResultCreator.java index 649c23e79fb..d93f8343f22 100644 --- a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationResultCreator.java +++ b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationResultCreator.java @@ -49,7 +49,9 @@ public static GreenbidsInvocationResult create(GreenbidsConfig greenbidsConfig, } private static boolean isExploration(GreenbidsConfig greenbidsConfig, String greenbidsId) { - final double explorationRate = ObjectUtils.defaultIfNull(greenbidsConfig.getExplorationRate(), DEFAULT_EXPLORATION_RATE); + final double explorationRate = ObjectUtils.defaultIfNull( + greenbidsConfig.getExplorationRate(), + DEFAULT_EXPLORATION_RATE); final int hashInt = Integer.parseInt(greenbidsId.substring(greenbidsId.length() - 4), 16); return hashInt < explorationRate * RANGE_16_BIT_INTEGER_DIVISION_BASIS; } diff --git a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationService.java b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationService.java deleted file mode 100644 index 77f45d0e6b2..00000000000 --- a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationService.java +++ /dev/null @@ -1,122 +0,0 @@ -package org.prebid.server.hooks.modules.greenbids.real.time.data.core; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.iab.openrtb.request.BidRequest; -import com.iab.openrtb.request.Imp; -import org.apache.commons.lang3.StringUtils; -import org.prebid.server.analytics.reporter.greenbids.model.ExplorationResult; -import org.prebid.server.analytics.reporter.greenbids.model.Ortb2ImpExtResult; -import org.prebid.server.hooks.modules.greenbids.real.time.data.model.data.GreenbidsConfig; -import org.prebid.server.hooks.modules.greenbids.real.time.data.model.result.AnalyticsResult; -import org.prebid.server.hooks.modules.greenbids.real.time.data.model.result.GreenbidsInvocationResult; -import org.prebid.server.hooks.v1.InvocationAction; - -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; -import java.util.stream.Collectors; - -public class GreenbidsInvocationService { - - private static final int RANGE_16_BIT_INTEGER_DIVISION_BASIS = 0x10000; - - private static final double DEFAULT_EXPLORATION_RATE = 1.0; - - public GreenbidsInvocationResult createGreenbidsInvocationResult( - GreenbidsConfig greenbidsConfig, - BidRequest bidRequest, - Map> impsBiddersFilterMap) { - - final String greenbidsId = UUID.randomUUID().toString(); - final boolean isExploration = isExploration(greenbidsConfig, greenbidsId); - - final List updatedImps = updateImps(bidRequest, impsBiddersFilterMap); - - final BidRequest updatedBidRequest = isExploration || updatedImps.isEmpty() - ? bidRequest - : bidRequest.toBuilder() - .imp(updatedImps) - .build(); - - final InvocationAction invocationAction = updatedImps.isEmpty() - ? InvocationAction.reject - : isExploration - ? InvocationAction.no_action - : InvocationAction.update; - - final Map ort2ImpExtResultMap = createOrtb2ImpExtForImps( - bidRequest, impsBiddersFilterMap, greenbidsId, isExploration); - final AnalyticsResult analyticsResult = AnalyticsResult.of( - "success", ort2ImpExtResultMap); - - return GreenbidsInvocationResult.of(updatedBidRequest, invocationAction, analyticsResult); - } - - private Boolean isExploration(GreenbidsConfig greenbidsConfig, String greenbidsId) { - final double explorationRate = Optional.ofNullable(greenbidsConfig.getExplorationRate()) - .orElse(DEFAULT_EXPLORATION_RATE); - final int hashInt = Integer.parseInt( - greenbidsId.substring(greenbidsId.length() - 4), 16); - return hashInt < explorationRate * RANGE_16_BIT_INTEGER_DIVISION_BASIS; - } - - private List updateImps(BidRequest bidRequest, Map> impsBiddersFilterMap) { - return bidRequest.getImp().stream() - .filter(imp -> isImpKept(imp, impsBiddersFilterMap)) - .map(imp -> updateImp(imp, impsBiddersFilterMap.get(imp.getId()))) - .toList(); - } - - private boolean isImpKept(Imp imp, Map> impsBiddersFilterMap) { - return impsBiddersFilterMap.get(imp.getId()).values().stream().anyMatch(isKept -> isKept); - } - - private Imp updateImp(Imp imp, Map bidderFilterMap) { - return imp.toBuilder() - .ext(updateImpExt(imp.getExt(), bidderFilterMap)) - .build(); - } - - private ObjectNode updateImpExt(ObjectNode impExt, Map bidderFilterMap) { - final ObjectNode updatedExt = impExt.deepCopy(); - Optional.ofNullable((ObjectNode) updatedExt.get("prebid")) - .map(prebidNode -> (ObjectNode) prebidNode.get("bidder")) - .ifPresent(bidderNode -> - bidderFilterMap.entrySet().stream() - .filter(entry -> !entry.getValue()) - .map(Map.Entry::getKey) - .forEach(bidderNode::remove)); - return updatedExt; - } - - private Map createOrtb2ImpExtForImps( - BidRequest bidRequest, - Map> impsBiddersFilterMap, - String greenbidsId, - Boolean isExploration) { - - return bidRequest.getImp().stream() - .collect(Collectors.toMap( - Imp::getId, - imp -> createOrtb2ImpExt(imp, impsBiddersFilterMap, greenbidsId, isExploration))); - } - - private Ortb2ImpExtResult createOrtb2ImpExt( - Imp imp, - Map> impsBiddersFilterMap, - String greenbidsId, - Boolean isExploration) { - - final String tid = Optional.of(imp) - .map(Imp::getExt) - .map(impExt -> impExt.get("tid")) - .map(JsonNode::asText) - .orElse(StringUtils.EMPTY); - final Map impBiddersFilterMap = impsBiddersFilterMap.get(imp.getId()); - final ExplorationResult explorationResult = ExplorationResult.of( - greenbidsId, impBiddersFilterMap, isExploration); - return Ortb2ImpExtResult.of(explorationResult, tid); - } -} diff --git a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHook.java b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHook.java index 1b7eac1901a..18aeec75a2a 100644 --- a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHook.java +++ b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHook.java @@ -156,7 +156,8 @@ private Future> toInvocationResult( return Future.succeededFuture(InvocationResultImpl.builder() .status(InvocationStatus.success) .action(invocationResult.getInvocationAction()) - .payloadUpdate(payload -> AuctionRequestPayloadImpl.of(GreenbidsPayloadUpdater.update(bidRequest, impsBiddersFilterMap))) + .payloadUpdate(payload -> AuctionRequestPayloadImpl.of( + GreenbidsPayloadUpdater.update(bidRequest, impsBiddersFilterMap))) .analyticsTags(toAnalyticsTags(invocationResult.getAnalyticsResult())) .rejections(toRejections(impsBiddersFilterMap)) .build()); diff --git a/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInferenceDataServiceTest.java b/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInferenceDataServiceTest.java index a3090b24aee..1a93b22a502 100644 --- a/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInferenceDataServiceTest.java +++ b/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInferenceDataServiceTest.java @@ -25,9 +25,7 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.List; -import java.util.function.UnaryOperator; -import static java.util.function.UnaryOperator.identity; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; 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 8f2be7179e4..dd0293af354 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 @@ -33,9 +33,6 @@ import static java.util.Collections.singletonMap; import static java.util.function.UnaryOperator.identity; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.prebid.server.auction.model.BidRejectionReason.RESPONSE_REJECTED_ADVERTISER_BLOCKED; import static org.prebid.server.auction.model.BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE; @@ -665,7 +662,6 @@ private BidsBlocker bidsBlocker(List bids, BlockedAttributes blockedAttributes, boolean debugEnabled) { - return BidsBlocker.create( - bids, "bidder1", ortbVersion, accountConfig, blockedAttributes, bidRejectionTracker, debugEnabled); + return BidsBlocker.create(bids, "bidder1", ortbVersion, accountConfig, blockedAttributes, debugEnabled); } } diff --git a/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingRawBidderResponseHookTest.java b/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingRawBidderResponseHookTest.java index a020628c2a5..7b574a3312c 100644 --- a/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingRawBidderResponseHookTest.java +++ b/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingRawBidderResponseHookTest.java @@ -8,10 +8,8 @@ import io.vertx.core.Future; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.prebid.server.auction.model.AuctionContext; -import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.model.RejectedBid; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.hooks.execution.v1.InvocationResultImpl; @@ -38,12 +36,10 @@ import org.prebid.server.proto.openrtb.ext.response.BidType; import java.util.List; -import java.util.Map; import java.util.function.UnaryOperator; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; -import static java.util.function.UnaryOperator.identity; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.assertj.core.api.SoftAssertions.assertSoftly; diff --git a/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/model/BidderInvocationContextImpl.java b/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/model/BidderInvocationContextImpl.java index 72defaba54a..5812885d88d 100644 --- a/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/model/BidderInvocationContextImpl.java +++ b/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/model/BidderInvocationContextImpl.java @@ -6,7 +6,6 @@ import lombok.Value; import lombok.experimental.Accessors; import org.prebid.server.auction.model.AuctionContext; -import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.execution.timeout.Timeout; import org.prebid.server.hooks.v1.bidder.BidderInvocationContext; import org.prebid.server.model.Endpoint; diff --git a/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHookTest.java b/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHookTest.java index 03b1516bbb2..2fe927119af 100644 --- a/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHookTest.java +++ b/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHookTest.java @@ -65,7 +65,6 @@ public class PbRichmediaFilterAllProcessedBidResponsesHookTest { private PbRichmediaFilterAllProcessedBidResponsesHook target; - @BeforeEach public void setUp() { target = new PbRichmediaFilterAllProcessedBidResponsesHook( diff --git a/src/main/java/org/prebid/server/analytics/reporter/greenbids/GreenbidsAnalyticsReporter.java b/src/main/java/org/prebid/server/analytics/reporter/greenbids/GreenbidsAnalyticsReporter.java index 7a6dd70e57f..c37ac7aeff2 100644 --- a/src/main/java/org/prebid/server/analytics/reporter/greenbids/GreenbidsAnalyticsReporter.java +++ b/src/main/java/org/prebid/server/analytics/reporter/greenbids/GreenbidsAnalyticsReporter.java @@ -382,8 +382,8 @@ private static Map getSeatsWithNonBids(AuctionContext auctionCon } private static SeatNonBid toSeatNonBid(String bidder, BidRejectionTracker bidRejectionTracker) { - final List nonBids = bidRejectionTracker.getRejectedImps().stream() - .map(rejectedImp -> NonBid.of(rejectedImp.impId(), rejectedImp.reason().getRight())) + final List nonBids = bidRejectionTracker.getRejected().stream() + .map(rejectedImp -> NonBid.of(rejectedImp.impId(), rejectedImp.reason())) .toList(); return SeatNonBid.of(bidder, nonBids); diff --git a/src/main/java/org/prebid/server/auction/BidResponseCreator.java b/src/main/java/org/prebid/server/auction/BidResponseCreator.java index dd8f79c1920..5c1f0f3c555 100644 --- a/src/main/java/org/prebid/server/auction/BidResponseCreator.java +++ b/src/main/java/org/prebid/server/auction/BidResponseCreator.java @@ -35,6 +35,7 @@ import org.prebid.server.auction.model.CategoryMappingResult; import org.prebid.server.auction.model.MultiBidConfig; import org.prebid.server.auction.model.PaaFormat; +import org.prebid.server.auction.model.Rejected; import org.prebid.server.auction.model.TargetingInfo; import org.prebid.server.auction.model.debug.DebugContext; import org.prebid.server.auction.requestfactory.Ortb2ImplicitParametersResolver; @@ -1949,12 +1950,10 @@ private static BidResponse populateSeatNonBid(AuctionContext auctionContext, Bid } final List seatNonBids = auctionContext.getBidRejectionTrackers().values().stream() - .flatMap(bidRejectionTracker -> bidRejectionTracker.getRejectedImps().entrySet().stream()) + .flatMap(bidRejectionTracker -> bidRejectionTracker.getRejected().stream()) .collect(Collectors.groupingBy( - entry -> entry.getValue().getLeft(), - Collectors.mapping( - entry -> NonBid.of(entry.getKey(), entry.getValue().getRight()), - Collectors.toList()))) + Rejected::seat, + Collectors.mapping(entry -> NonBid.of(entry.impId(), entry.reason()), Collectors.toList()))) .entrySet().stream() .filter(entry -> !entry.getValue().isEmpty()) .map(entry -> SeatNonBid.of(entry.getKey(), entry.getValue())) diff --git a/src/main/java/org/prebid/server/auction/model/BidRejectionTracker.java b/src/main/java/org/prebid/server/auction/model/BidRejectionTracker.java index 68406712a00..0de2dc72bbb 100644 --- a/src/main/java/org/prebid/server/auction/model/BidRejectionTracker.java +++ b/src/main/java/org/prebid/server/auction/model/BidRejectionTracker.java @@ -87,6 +87,7 @@ public void reject(Rejected rejected) { if (rejected instanceof RejectedImp && rejected.reason().getValue() >= 300) { logger.warn("The rejected imp {} with the code {} equal to or higher than 300 assumes " + "that there is a rejected bid that shouldn't be lost"); + return; } final String impId = rejected.impId(); @@ -96,7 +97,10 @@ public void reject(Rejected rejected) { MULTIPLE_REJECTIONS_WARNING_TEMPLATE.formatted(bidder, impId), logSamplingRate); } - rejectedBids.computeIfAbsent(impId, key -> new ArrayList<>()).add(rejected); + rejectedBids.computeIfAbsent(impId, key -> new ArrayList<>()) + .add(rejected instanceof RejectedImp + ? RejectedImp.of(bidder, rejected.impId(), rejected.reason()) + : rejected); if (succeededBidsIds.containsKey(impId)) { final String bidId = rejected instanceof RejectedBid ? ((RejectedBid) rejected).bidId() : null; @@ -119,20 +123,20 @@ public void rejectAll(BidRejectionReason reason) { involvedImpIds.forEach(impId -> reject(RejectedImp.of(impId, reason))); } - public Set getRejectedImps() { - final Set rejectedImpIds = new HashSet<>(); + public Set getRejected() { + final Set rejectedResult = new HashSet<>(); for (String impId : involvedImpIds) { final Set succeededBids = succeededBidsIds.getOrDefault(impId, Collections.emptySet()); if (succeededBids.isEmpty()) { if (rejectedBids.containsKey(impId)) { - rejectedImpIds.add(RejectedImp.of(impId, rejectedBids.get(impId).getFirst().reason())); + rejectedResult.add(rejectedBids.get(impId).getFirst()); } else { - rejectedImpIds.add(RejectedImp.of(impId, BidRejectionReason.NO_BID)); + rejectedResult.add(RejectedImp.of(bidder, impId, BidRejectionReason.NO_BID)); } } } - return rejectedImpIds; + return rejectedResult; } public Map> getAllRejected() { @@ -141,7 +145,7 @@ public Map> getAllRejected() { final Set succeededBids = succeededBidsIds.getOrDefault(impId, Collections.emptySet()); if (succeededBids.isEmpty() && !rejectedBids.containsKey(impId)) { missingImpIds.computeIfAbsent(impId, key -> new ArrayList<>()) - .add(RejectedImp.of(impId, BidRejectionReason.NO_BID)); + .add(RejectedImp.of(bidder, impId, BidRejectionReason.NO_BID)); } } diff --git a/src/main/java/org/prebid/server/auction/model/Rejected.java b/src/main/java/org/prebid/server/auction/model/Rejected.java index f18fc890d86..0f1685ee51f 100644 --- a/src/main/java/org/prebid/server/auction/model/Rejected.java +++ b/src/main/java/org/prebid/server/auction/model/Rejected.java @@ -2,6 +2,8 @@ public interface Rejected { + String seat(); + String impId(); BidRejectionReason reason(); diff --git a/src/main/java/org/prebid/server/auction/model/RejectedBid.java b/src/main/java/org/prebid/server/auction/model/RejectedBid.java index 1a0ae1ff53f..f7901999de2 100644 --- a/src/main/java/org/prebid/server/auction/model/RejectedBid.java +++ b/src/main/java/org/prebid/server/auction/model/RejectedBid.java @@ -20,6 +20,11 @@ public BidRejectionReason reason() { return reason; } + @Override + public String seat() { + return bid.getSeat(); + } + public String bidId() { return bid.getBid().getId(); } diff --git a/src/main/java/org/prebid/server/auction/model/RejectedImp.java b/src/main/java/org/prebid/server/auction/model/RejectedImp.java index 186221de0ac..451688c265a 100644 --- a/src/main/java/org/prebid/server/auction/model/RejectedImp.java +++ b/src/main/java/org/prebid/server/auction/model/RejectedImp.java @@ -7,8 +7,14 @@ @Accessors(fluent = true) public class RejectedImp implements Rejected { + String seat; + String impId; BidRejectionReason reason; + public static RejectedImp of(String impId, BidRejectionReason reason) { + return RejectedImp.of(null, impId, reason); + } + } diff --git a/src/main/java/org/prebid/server/validation/ResponseBidValidator.java b/src/main/java/org/prebid/server/validation/ResponseBidValidator.java index 0633592b751..e0503fab0d9 100644 --- a/src/main/java/org/prebid/server/validation/ResponseBidValidator.java +++ b/src/main/java/org/prebid/server/validation/ResponseBidValidator.java @@ -166,7 +166,7 @@ private void validateSeat(BidderBid bid, final String message = "invalid bidder code %s was set by the adapter %s for the account %s" .formatted(bid.getSeat(), bidder, account.getId()); - bidRejectionTracker.rejectBid(bid, BidRejectionReason.RESPONSE_REJECTED_GENERAL); + bidRejectionTracker.reject(RejectedBid.of(bid, BidRejectionReason.RESPONSE_REJECTED_GENERAL)); metrics.updateSeatValidationMetrics(bidder); alternateBidderCodeLogger.warn(message, logSamplingRate); throw new ValidationException(message); diff --git a/src/test/java/org/prebid/server/auction/BidResponseCreatorTest.java b/src/test/java/org/prebid/server/auction/BidResponseCreatorTest.java index 5c8c8aeaee1..c7798b62705 100644 --- a/src/test/java/org/prebid/server/auction/BidResponseCreatorTest.java +++ b/src/test/java/org/prebid/server/auction/BidResponseCreatorTest.java @@ -21,7 +21,6 @@ import com.iab.openrtb.response.SeatBid; import io.vertx.core.Future; import org.apache.commons.collections4.MapUtils; -import org.apache.commons.lang3.tuple.Pair; import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -4393,7 +4392,7 @@ public void shouldDropFledgeResponsesReferencingUnknownImps() { public void shouldPopulateExtPrebidSeatNonBidWhenReturnAllBidStatusFlagIsTrue() { // given final BidRejectionTracker bidRejectionTracker = mock(BidRejectionTracker.class); - given(bidRejectionTracker.getRejectedImps()).willReturn(singleton(RejectedImp.of("impId2", NO_BID))); + given(bidRejectionTracker.getRejected()).willReturn(singleton(RejectedImp.of("someBidder", "impId2", NO_BID))); final Bid bid = Bid.builder().id("bidId").price(BigDecimal.valueOf(3.67)).impid("impId").build(); final List bidderResponses = singletonList( diff --git a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java index abf3ed7efe2..b577bc03470 100644 --- a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java +++ b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java @@ -206,7 +206,6 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; -import static org.prebid.server.auction.model.BidRejectionReason.REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY; import static org.prebid.server.auction.model.BidRejectionReason.NO_BID; import static org.prebid.server.auction.model.BidRejectionReason.REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY; import static org.prebid.server.proto.openrtb.ext.response.BidType.banner; @@ -4061,8 +4060,8 @@ public void shouldResponseWithEmptySeatBidIfBidderNotSupportRequestCurrency() { assertThat(result.result()) .extracting(AuctionContext::getBidRejectionTrackers) .extracting(rejectionTrackers -> rejectionTrackers.get("bidder1")) - .extracting(BidRejectionTracker::getRejectedImps) - .isEqualTo(Set.of(RejectedImp.of("impId1", REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY))); + .extracting(BidRejectionTracker::getRejected) + .isEqualTo(Set.of(RejectedImp.of("bidder1", "impId1", REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY))); } @Test @@ -4095,12 +4094,12 @@ public void shouldMakeBidRejectionTrackers() { assertThat(result.succeeded()).isTrue(); final Map actualTrackers = result.result().getBidRejectionTrackers(); assertThat(actualTrackers.keySet()).containsOnly("bidder1", "bidder2"); - assertThat(actualTrackers.get("bidder1").getRejectedImps()).containsOnly( - RejectedImp.of("impId1", NO_BID), - RejectedImp.of("impId2", NO_BID)); - assertThat(actualTrackers.get("bidder2").getRejectedImps()).containsOnly( - RejectedImp.of("impId1", NO_BID), - RejectedImp.of("impId2", NO_BID)); + assertThat(actualTrackers.get("bidder1").getRejected()).containsOnly( + RejectedImp.of("bidder1", "impId1", NO_BID), + RejectedImp.of("bidder1", "impId2", NO_BID)); + assertThat(actualTrackers.get("bidder2").getRejected()).containsOnly( + RejectedImp.of("bidder2", "impId1", NO_BID), + RejectedImp.of("bidder2", "impId2", NO_BID)); } @Test diff --git a/src/test/java/org/prebid/server/auction/model/BidRejectionTrackerTest.java b/src/test/java/org/prebid/server/auction/model/BidRejectionTrackerTest.java index a8ed1627d6d..d169534c200 100644 --- a/src/test/java/org/prebid/server/auction/model/BidRejectionTrackerTest.java +++ b/src/test/java/org/prebid/server/auction/model/BidRejectionTrackerTest.java @@ -11,6 +11,7 @@ import static java.util.Collections.singleton; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; +import static org.assertj.core.api.Assertions.tuple; import static org.prebid.server.auction.model.BidRejectionReason.ERROR_GENERAL; import static org.prebid.server.auction.model.BidRejectionReason.ERROR_INVALID_BID_RESPONSE; import static org.prebid.server.auction.model.BidRejectionReason.ERROR_TIMED_OUT; @@ -38,9 +39,9 @@ public void succeedShouldRestoreImpFromImpRejection() { target.succeed(singleton(bid)); // then - assertThat(target.getRejectedImps()).isEmpty(); + assertThat(target.getRejected()).isEmpty(); assertThat(target.getAllRejected()) - .containsOnly(entry("impId1", List.of(RejectedImp.of("impId1", ERROR_GENERAL)))); + .containsOnly(entry("impId1", List.of(RejectedImp.of("bidder", "impId1", ERROR_GENERAL)))); } @Test @@ -53,7 +54,7 @@ public void succeedShouldRestoreImpFromBidRejection() { target.succeed(singleton(bid)); // then - assertThat(target.getRejectedImps()).isEmpty(); + assertThat(target.getRejected()).isEmpty(); assertThat(target.getAllRejected()) .containsOnly(entry("impId1", List.of(RejectedBid.of(bid, ERROR_GENERAL)))); } @@ -68,9 +69,10 @@ public void succeedShouldIgnoreUninvolvedImpIdsOnImpRejection() { target.succeed(singleton(bid)); // then - assertThat(target.getRejectedImps()).containsOnly(RejectedImp.of("impId1", ERROR_GENERAL)); + assertThat(target.getRejected()).extracting(Rejected::seat, Rejected::impId, Rejected::reason) + .containsOnly(tuple("bidder", "impId1", ERROR_GENERAL)); assertThat(target.getAllRejected()) - .containsOnly(entry("impId1", List.of(RejectedImp.of("impId1", ERROR_GENERAL)))); + .containsOnly(entry("impId1", List.of(RejectedImp.of("bidder", "impId1", ERROR_GENERAL)))); } @Test @@ -84,7 +86,8 @@ public void succeedShouldIgnoreUninvolvedImpIdsOnBidRejection() { target.succeed(singleton(bid2)); // then - assertThat(target.getRejectedImps()).containsOnly(RejectedImp.of("impId1", ERROR_GENERAL)); + assertThat(target.getRejected()).extracting(Rejected::seat, Rejected::impId, Rejected::reason) + .containsOnly(tuple("seat", "impId1", ERROR_GENERAL)); assertThat(target.getAllRejected()) .containsOnly(entry("impId1", List.of(RejectedBid.of(bid1, ERROR_GENERAL)))); } @@ -95,9 +98,10 @@ public void rejectImpShouldRecordImpRejectionFirstTimeIfImpIdIsInvolved() { target.reject(RejectedImp.of("impId1", ERROR_GENERAL)); // then - assertThat(target.getRejectedImps()).containsOnly(RejectedImp.of("impId1", ERROR_GENERAL)); + assertThat(target.getRejected()).extracting(Rejected::seat, Rejected::impId, Rejected::reason) + .containsOnly(tuple("bidder", "impId1", ERROR_GENERAL)); assertThat(target.getAllRejected()) - .containsOnly(entry("impId1", List.of(RejectedImp.of("impId1", ERROR_GENERAL)))); + .containsOnly(entry("impId1", List.of(RejectedImp.of("bidder", "impId1", ERROR_GENERAL)))); } @Test @@ -107,7 +111,8 @@ public void rejectBidShouldRecordBidRejectionFirstTimeIfImpIdIsInvolved() { target.reject(RejectedBid.of(bid, ERROR_GENERAL)); // then - assertThat(target.getRejectedImps()).containsOnly(RejectedImp.of("impId1", ERROR_GENERAL)); + assertThat(target.getRejected()).extracting(Rejected::seat, Rejected::impId, Rejected::reason) + .containsOnly(tuple("seat", "impId1", ERROR_GENERAL)); assertThat(target.getAllRejected()) .containsOnly(entry("impId1", List.of(RejectedBid.of(bid, ERROR_GENERAL)))); } @@ -123,7 +128,7 @@ public void rejectBidShouldRecordBidRejectionAfterPreviouslySucceededBid() { target.reject(RejectedBid.of(bid1, ERROR_GENERAL)); // then - assertThat(target.getRejectedImps()).isEmpty(); + assertThat(target.getRejected()).isEmpty(); assertThat(target.getAllRejected()) .containsOnly(entry("impId1", List.of(RejectedBid.of(bid1, ERROR_GENERAL)))); } @@ -137,11 +142,12 @@ public void rejectImpShouldNotRecordImpRejectionIfImpIdIsAlreadyRejected() { target.reject(RejectedImp.of("impId1", ERROR_INVALID_BID_RESPONSE)); // then - assertThat(target.getRejectedImps()).containsOnly(RejectedImp.of("impId1", ERROR_GENERAL)); + assertThat(target.getRejected()).extracting(Rejected::seat, Rejected::impId, Rejected::reason) + .containsOnly(tuple("bidder", "impId1", ERROR_GENERAL)); assertThat(target.getAllRejected()) .containsOnly(entry("impId1", List.of( - RejectedImp.of("impId1", ERROR_GENERAL), - RejectedImp.of("impId1", ERROR_INVALID_BID_RESPONSE)))); + RejectedImp.of("bidder", "impId1", ERROR_GENERAL), + RejectedImp.of("bidder", "impId1", ERROR_INVALID_BID_RESPONSE)))); } @Test @@ -155,8 +161,8 @@ public void rejectBidShouldNotRecordImpRejectionButRecordBidRejectionEvenIfImpIs target.reject(RejectedBid.of(bid2, RESPONSE_REJECTED_BELOW_FLOOR)); // then - assertThat(target.getRejectedImps()) - .containsOnly(RejectedImp.of("impId1", RESPONSE_REJECTED_GENERAL)); + assertThat(target.getRejected()).extracting(Rejected::seat, Rejected::impId, Rejected::reason) + .containsOnly(tuple("seat", "impId1", RESPONSE_REJECTED_GENERAL)); assertThat(target.getAllRejected()) .containsOnly(entry("impId1", List.of( RejectedBid.of(bid1, RESPONSE_REJECTED_GENERAL), @@ -173,19 +179,19 @@ public void rejectAllShouldTryRejectingEachImpId() { target.rejectAll(ERROR_TIMED_OUT); // then - assertThat(target.getRejectedImps()) + assertThat(target.getRejected()).extracting(Rejected::seat, Rejected::impId, Rejected::reason) .containsOnly( - RejectedImp.of("impId1", NO_BID), - RejectedImp.of("impId2", ERROR_TIMED_OUT), - RejectedImp.of("impId3", ERROR_TIMED_OUT)); + tuple("bidder", "impId1", NO_BID), + tuple("bidder", "impId2", ERROR_TIMED_OUT), + tuple("bidder", "impId3", ERROR_TIMED_OUT)); assertThat(target.getAllRejected()) .containsOnly( entry("impId1", List.of( - RejectedImp.of("impId1", NO_BID), - RejectedImp.of("impId1", ERROR_TIMED_OUT))), - entry("impId2", List.of(RejectedImp.of("impId2", ERROR_TIMED_OUT))), - entry("impId3", List.of(RejectedImp.of("impId3", ERROR_TIMED_OUT)))); + RejectedImp.of("bidder", "impId1", NO_BID), + RejectedImp.of("bidder", "impId1", ERROR_TIMED_OUT))), + entry("impId2", List.of(RejectedImp.of("bidder", "impId2", ERROR_TIMED_OUT))), + entry("impId3", List.of(RejectedImp.of("bidder", "impId3", ERROR_TIMED_OUT)))); } @Test @@ -205,11 +211,11 @@ public void rejectBidsShouldTryRejectingEachBid() { RejectedBid.of(bid3, RESPONSE_REJECTED_DSA_PRIVACY))); // then - assertThat(target.getRejectedImps()) + assertThat(target.getRejected()).extracting(Rejected::seat, Rejected::impId, Rejected::reason) .containsOnly( - RejectedImp.of("impId1", RESPONSE_REJECTED_GENERAL), - RejectedImp.of("impId2", RESPONSE_REJECTED_DSA_PRIVACY), - RejectedImp.of("impId3", RESPONSE_REJECTED_DSA_PRIVACY)); + tuple("seat", "impId1", RESPONSE_REJECTED_GENERAL), + tuple("seat", "impId2", RESPONSE_REJECTED_DSA_PRIVACY), + tuple("seat", "impId3", RESPONSE_REJECTED_DSA_PRIVACY)); assertThat(target.getAllRejected()) .containsOnly( @@ -228,7 +234,8 @@ public void getRejectedImpsShouldTreatUnsuccessfulImpsAsNoBidRejection() { target.succeed(singleton(bid)); // then - assertThat(target.getRejectedImps()).containsOnly(RejectedImp.of("impId1", NO_BID)); + assertThat(target.getRejected()).extracting(Rejected::seat, Rejected::impId, Rejected::reason) + .containsOnly(tuple("bidder", "impId1", NO_BID)); } private BidderBid givenBid(String bidId, String impId) { diff --git a/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java b/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java index 94bce519dc6..aaca20e7dd2 100644 --- a/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java +++ b/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java @@ -1811,24 +1811,24 @@ public void shouldExecuteRawAuctionRequestHooksWithAllRejectionsPopulated(VertxT assertThat(bidRejectionTrackers.keySet()).containsOnly("bidderA", "bidderB", "bidderC"); assertThat(bidRejectionTrackers.get("bidderA").getAllRejected()).containsOnly( entry("impId1", List.of( - RejectedImp.of("impId1", REQUEST_BLOCKED_OPTIMIZED), - RejectedImp.of("impId1", REQUEST_BLOCKED_UNSUPPORTED_MEDIA_TYPE))), + RejectedImp.of("bidderA", "impId1", REQUEST_BLOCKED_OPTIMIZED), + RejectedImp.of("bidderA", "impId1", REQUEST_BLOCKED_UNSUPPORTED_MEDIA_TYPE))), entry("impId3", List.of( - RejectedImp.of("impId3", REQUEST_BLOCKED_GENERAL), - RejectedImp.of("impId3", REQUEST_BLOCKED_GENERAL)))); + RejectedImp.of("bidderA", "impId3", REQUEST_BLOCKED_GENERAL), + RejectedImp.of("bidderA", "impId3", REQUEST_BLOCKED_GENERAL)))); assertThat(bidRejectionTrackers.get("bidderB").getAllRejected()).containsOnly( entry("impId2", List.of( - RejectedImp.of("impId2", REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY), - RejectedImp.of("impId2", REQUEST_BLOCKED_PRIVACY))), + RejectedImp.of("bidderB", "impId2", REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY), + RejectedImp.of("bidderB", "impId2", REQUEST_BLOCKED_PRIVACY))), entry("impId3", List.of( - RejectedImp.of("impId3", REQUEST_BLOCKED_GENERAL), - RejectedImp.of("impId3", REQUEST_BLOCKED_GENERAL)))); + RejectedImp.of("bidderB", "impId3", REQUEST_BLOCKED_GENERAL), + RejectedImp.of("bidderB", "impId3", REQUEST_BLOCKED_GENERAL)))); assertThat(bidRejectionTrackers.get("bidderC").getAllRejected()).containsOnly( entry("impId4", List.of( - RejectedImp.of("impId4", REQUEST_BLOCKED_GENERAL), - RejectedImp.of("impId4", REQUEST_BLOCKED_GENERAL), - RejectedImp.of("impId4", REQUEST_BLOCKED_GENERAL), - RejectedImp.of("impId4", REQUEST_BLOCKED_GENERAL)))); + RejectedImp.of("bidderC", "impId4", REQUEST_BLOCKED_GENERAL), + RejectedImp.of("bidderC", "impId4", REQUEST_BLOCKED_GENERAL), + RejectedImp.of("bidderC", "impId4", REQUEST_BLOCKED_GENERAL), + RejectedImp.of("bidderC", "impId4", REQUEST_BLOCKED_GENERAL)))); } @Test diff --git a/src/test/java/org/prebid/server/validation/ResponseBidValidatorTest.java b/src/test/java/org/prebid/server/validation/ResponseBidValidatorTest.java index 1782ca4a449..6863552d9ab 100644 --- a/src/test/java/org/prebid/server/validation/ResponseBidValidatorTest.java +++ b/src/test/java/org/prebid/server/validation/ResponseBidValidatorTest.java @@ -39,6 +39,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; +import static org.prebid.server.auction.model.BidRejectionReason.RESPONSE_REJECTED_GENERAL; import static org.prebid.server.auction.model.BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE_NOT_SECURE; import static org.prebid.server.auction.model.BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE_SIZE_NOT_ALLOWED; import static org.prebid.server.settings.model.BidValidationEnforcement.enforce; @@ -565,7 +566,7 @@ public void validateShouldFailOnSeatValidationWhenSeatIsNotAllowed() { assertThat(result.getErrors()) .containsOnly("invalid bidder code seat was set by the adapter bidder for the account account"); verify(metrics).updateSeatValidationMetrics(BIDDER_NAME); - verify(bidRejectionTracker).rejectBid(givenBid, BidRejectionReason.RESPONSE_REJECTED_GENERAL); + verify(bidRejectionTracker).reject(RejectedBid.of(givenBid, RESPONSE_REJECTED_GENERAL)); } @Test From dda4c4160a9f6e8dc1c283114597070741f1c909 Mon Sep 17 00:00:00 2001 From: antonbabak Date: Wed, 10 Sep 2025 10:57:27 +0200 Subject: [PATCH 5/5] Fix comments --- docs/config-app.md | 2 +- docs/metrics.md | 4 +- ...alTimeDataProcessedAuctionRequestHook.java | 8 +- .../GreenbidsInvocationResultCreatorTest.java | 27 ++--- .../data/util/TestBidRequestProvider.java | 4 +- ...meDataProcessedAuctionRequestHookTest.java | 6 +- .../ortb2/blocking/core/BidsBlocker.java | 12 +- .../blocking/core/model/ExecutionResult.java | 4 +- .../ortb2/blocking/core/BidsBlockerTest.java | 30 ++--- ...rtb2BlockingRawBidderResponseHookTest.java | 6 +- ...diaFilterAllProcessedBidResponsesHook.java | 16 +-- ...ilterAllProcessedBidResponsesHookTest.java | 8 +- .../server/auction/BidResponseCreator.java | 4 +- .../prebid/server/auction/DsaEnforcer.java | 4 +- .../{RejectedBid.java => BidRejection.java} | 2 +- .../auction/model/BidRejectionTracker.java | 52 ++++----- .../{RejectedImp.java => ImpRejection.java} | 6 +- .../model/{Rejected.java => Rejection.java} | 2 +- .../floors/BasicPriceFloorEnforcer.java | 4 +- .../server/hooks/execution/GroupResult.java | 4 +- .../hooks/execution/HookStageExecutor.java | 4 +- .../server/hooks/execution/StageResult.java | 17 ++- .../model/HookStageExecutionResult.java | 6 +- .../execution/provider/abtest/ABTestHook.java | 4 +- .../execution/v1/InvocationResultImpl.java | 4 +- .../server/hooks/v1/InvocationResult.java | 4 +- .../java/org/prebid/server/util/MapUtil.java | 14 --- .../validation/ResponseBidValidator.java | 6 +- .../GreenbidsAnalyticsReporterTest.java | 4 +- .../auction/BidResponseCreatorTest.java | 4 +- .../server/auction/DsaEnforcerTest.java | 18 +-- .../server/auction/ExchangeServiceTest.java | 12 +- .../model/BidRejectionTrackerTest.java | 88 +++++++------- .../bidder/HttpBidderRequesterTest.java | 30 ++--- .../floors/BasicPriceFloorEnforcerTest.java | 4 +- .../execution/HookStageExecutorTest.java | 108 +++++++++--------- .../hooks/v1/InvocationResultUtils.java | 4 +- .../validation/ResponseBidValidatorTest.java | 20 ++-- 38 files changed, 272 insertions(+), 284 deletions(-) rename src/main/java/org/prebid/server/auction/model/{RejectedBid.java => BidRejection.java} (91%) rename src/main/java/org/prebid/server/auction/model/{RejectedImp.java => ImpRejection.java} (57%) rename src/main/java/org/prebid/server/auction/model/{Rejected.java => Rejection.java} (80%) diff --git a/docs/config-app.md b/docs/config-app.md index 238a6511d37..a661f5a74a2 100644 --- a/docs/config-app.md +++ b/docs/config-app.md @@ -349,7 +349,7 @@ For HTTP data source available next options: - `settings.http.rfc3986-compatible` - if equals to `true` the url will be build according to RFC 3986, `false` by default For account processing rules available next options: -- `settings.enforce-valid-account` - if equals to `true` then request without account id will be rejected with 401. +- `settings.enforce-valid-account` - if equals to `true` then request without account id will be rejection with 401. - `settings.generate-storedrequest-bidrequest-id` - overrides `bidrequest.id` in amp or app stored request with generated UUID if true. Default value is false. This flag can be overridden by setting `bidrequest.id` as `{{UUID}}` placeholder directly in stored request. It is possible to specify default account configuration values that will be assumed if account config have them diff --git a/docs/metrics.md b/docs/metrics.md index 823c41465c8..c07e0660598 100644 --- a/docs/metrics.md +++ b/docs/metrics.md @@ -93,7 +93,7 @@ Following metrics are collected and submitted if account is configured with `bas Following metrics are collected and submitted if account is configured with `detailed` verbosity: - `account..requests.type.(openrtb2-web,openrtb-app,amp,legacy)` - number of requests received from account with `` broken down by type of incoming request - `account..debug_requests` - number of requests received from account with `` broken down by type of incoming request (when debug mode is enabled) -- `account..requests.rejected` - number of rejected requests caused by incorrect `accountId` +- `account..requests.rejection` - number of rejection requests caused by incorrect `accountId` - `account..requests.disabled_bidder` - number of disabled bidders received within requests from account with `` - `account..requests.unknown_bidder` - number of unknown bidder names received within requests from account with `` - `account..adapter..request_time` - timer tracking how long did it take to make a request to `` when incoming request was from `` @@ -140,7 +140,7 @@ Following metrics are collected and submitted if account is configured with `det - `analytics..(auction|amp|video|cookie_sync|event|setuid).ok` - number of succeeded processed event requests - `analytics..(auction|amp|video|cookie_sync|event|setuid).timeout` - number of event requests, failed with timeout cause - `analytics..(auction|amp|video|cookie_sync|event|setuid).err` - number of event requests, failed with errors -- `analytics..(auction|amp|video|cookie_sync|event|setuid).badinput` - number of event requests, rejected with bad input cause +- `analytics..(auction|amp|video|cookie_sync|event|setuid).badinput` - number of event requests, rejection with bad input cause ## Modules metrics - `modules.module..stage..hook..call` - number of times the hook is called diff --git a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHook.java b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHook.java index 18aeec75a2a..f0bd5467d0e 100644 --- a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHook.java +++ b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHook.java @@ -11,8 +11,8 @@ import org.prebid.server.analytics.reporter.greenbids.model.ExplorationResult; import org.prebid.server.analytics.reporter.greenbids.model.Ortb2ImpExtResult; import org.prebid.server.auction.model.BidRejectionReason; -import org.prebid.server.auction.model.Rejected; -import org.prebid.server.auction.model.RejectedImp; +import org.prebid.server.auction.model.Rejection; +import org.prebid.server.auction.model.ImpRejection; import org.prebid.server.exception.PreBidException; import org.prebid.server.hooks.execution.v1.InvocationResultImpl; import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; @@ -218,7 +218,7 @@ private ObjectNode toObjectNode(Map.Entry values) { return values != null ? mapper.valueToTree(values) : null; } - private Map> toRejections(Map> impsBiddersFilterMap) { + private Map> toRejections(Map> impsBiddersFilterMap) { return impsBiddersFilterMap.entrySet().stream() .flatMap(entry -> Stream.ofNullable(entry.getValue()) .map(Map::entrySet) @@ -227,7 +227,7 @@ private Map> toRejections(Map Pair.of( bidder, - RejectedImp.of(entry.getKey(), BidRejectionReason.REQUEST_BLOCKED_OPTIMIZED)))) + ImpRejection.of(entry.getKey(), BidRejectionReason.REQUEST_BLOCKED_OPTIMIZED)))) .collect(Collectors.groupingBy(Pair::getKey, Collectors.mapping(Pair::getValue, Collectors.toList()))); } diff --git a/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationResultCreatorTest.java b/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationResultCreatorTest.java index fdd263527ad..1aa9e5022b4 100644 --- a/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationResultCreatorTest.java +++ b/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/GreenbidsInvocationResultCreatorTest.java @@ -11,7 +11,6 @@ import org.prebid.server.hooks.modules.greenbids.real.time.data.model.result.GreenbidsInvocationResult; import org.prebid.server.hooks.v1.InvocationAction; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -114,27 +113,21 @@ public void createShouldReturnRejectWhenAllImpsAreFilteredOutAndNoExploration() } private Map> givenImpsBiddersFilterMap() { - final Map biddersFitlerMap = new HashMap<>(); - biddersFitlerMap.put("rubicon", true); - biddersFitlerMap.put("appnexus", false); - biddersFitlerMap.put("pubmatic", false); + final Map biddersFitlerMap = Map.of( + "rubicon", true, + "appnexus", false, + "pubmatic", false); - final Map> impsBiddersFilterMap = new HashMap<>(); - impsBiddersFilterMap.put("adunitcodevalue", biddersFitlerMap); - - return impsBiddersFilterMap; + return Map.of("adunitcodevalue", biddersFitlerMap); } private Map> givenFilterMapWithAllFilteredImps() { - final Map biddersFitlerMap = new HashMap<>(); - biddersFitlerMap.put("rubicon", false); - biddersFitlerMap.put("appnexus", false); - biddersFitlerMap.put("pubmatic", false); - - final Map> impsBiddersFilterMap = new HashMap<>(); - impsBiddersFilterMap.put("adunitcodevalue", biddersFitlerMap); + final Map biddersFitlerMap = Map.of( + "rubicon", false, + "appnexus", false, + "pubmatic", false); - return impsBiddersFilterMap; + return Map.of("adunitcodevalue", biddersFitlerMap); } private GreenbidsConfig givenConfig(Double explorationRate) { diff --git a/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/util/TestBidRequestProvider.java b/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/util/TestBidRequestProvider.java index 7876bbda05e..9a33620c087 100644 --- a/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/util/TestBidRequestProvider.java +++ b/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/util/TestBidRequestProvider.java @@ -25,8 +25,8 @@ public static BidRequest givenBidRequest(UnaryOperator imps) { return bidRequestCustomizer.apply(BidRequest.builder() - .id("request") - .imp(imps) + .id("request") + .imp(imps) .site(givenSite()) .device(givenDevice())) .build(); diff --git a/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHookTest.java b/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHookTest.java index 5e9780e7e2f..42e16253445 100644 --- a/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHookTest.java +++ b/extra/modules/greenbids-real-time-data/src/test/java/org/prebid/server/hooks/modules/greenbids/real/time/data/v1/GreenbidsRealTimeDataProcessedAuctionRequestHookTest.java @@ -9,7 +9,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.prebid.server.auction.model.RejectedImp; +import org.prebid.server.auction.model.ImpRejection; import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; import org.prebid.server.hooks.execution.v1.analytics.ResultImpl; import org.prebid.server.hooks.execution.v1.auction.AuctionInvocationContextImpl; @@ -165,8 +165,8 @@ public void callShouldFilterBiddersBasedOnModelResultsWhenExplorationIsFalse() { assertThat(actualResult.values().get("adunitcodevalue").get("tid").asText()) .isEqualTo("67eaab5f-27a6-4689-93f7-bd8f024576e3"); assertThat(result.rejections()).containsOnly( - entry("appnexus", List.of(RejectedImp.of("adunitcodevalue", REQUEST_BLOCKED_OPTIMIZED))), - entry("pubmatic", List.of(RejectedImp.of("adunitcodevalue", REQUEST_BLOCKED_OPTIMIZED)))); + entry("appnexus", List.of(ImpRejection.of("adunitcodevalue", REQUEST_BLOCKED_OPTIMIZED))), + entry("pubmatic", List.of(ImpRejection.of("adunitcodevalue", REQUEST_BLOCKED_OPTIMIZED)))); } private AuctionInvocationContext givenAuctionInvocationContext(Double explorationRate) { 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 2c196d30c1d..f34355dc9e4 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 @@ -6,8 +6,8 @@ import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.prebid.server.auction.model.BidRejectionReason; -import org.prebid.server.auction.model.Rejected; -import org.prebid.server.auction.model.RejectedBid; +import org.prebid.server.auction.model.Rejection; +import org.prebid.server.auction.model.BidRejection; import org.prebid.server.auction.versionconverter.OrtbVersion; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.hooks.modules.ortb2.blocking.core.exception.InvalidAccountConfigurationException; @@ -100,7 +100,7 @@ public ExecutionResult block() { final BlockedBids blockedBids = !blockedBidIndexes.isEmpty() ? BlockedBids.of(blockedBidIndexes) : null; final List warnings = MergeUtils.mergeMessages(blockedBidResults); - final List rejectedBids = new ArrayList<>(); + final List rejectedBids = new ArrayList<>(); if (blockedBids != null) { blockedBidIndexes.forEach(index -> rejectBlockedBid(rejectedBids, blockedBidResults.get(index).getValue(), bids.get(index))); @@ -286,18 +286,18 @@ private String debugEntryFor(int index, BlockingResult blockingResult) { blockingResult.getFailedChecks()); } - private void rejectBlockedBid(List rejections, BlockingResult blockingResult, BidderBid blockedBid) { + private void rejectBlockedBid(List rejections, BlockingResult blockingResult, BidderBid blockedBid) { if (blockingResult.getBattrCheckResult().isFailed() || blockingResult.getBappCheckResult().isFailed() || blockingResult.getBcatCheckResult().isFailed()) { - rejections.add(RejectedBid.of( + rejections.add(BidRejection.of( blockedBid, BidRejectionReason.RESPONSE_REJECTED_INVALID_CREATIVE)); } if (blockingResult.getBadvCheckResult().isFailed()) { - rejections.add(RejectedBid.of( + rejections.add(BidRejection.of( blockedBid, BidRejectionReason.RESPONSE_REJECTED_ADVERTISER_BLOCKED)); } diff --git a/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/model/ExecutionResult.java b/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/model/ExecutionResult.java index 2eff89aefde..9b767cdf264 100644 --- a/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/model/ExecutionResult.java +++ b/extra/modules/ortb2-blocking/src/main/java/org/prebid/server/hooks/modules/ortb2/blocking/core/model/ExecutionResult.java @@ -2,7 +2,7 @@ import lombok.Builder; import lombok.Value; -import org.prebid.server.auction.model.Rejected; +import org.prebid.server.auction.model.Rejection; import java.util.Collections; import java.util.List; @@ -23,7 +23,7 @@ public class ExecutionResult { List analyticsResults; - List rejections; + List rejections; public boolean hasValue() { return value != null; 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 dd0293af354..549d48e5ae4 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 @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; -import org.prebid.server.auction.model.RejectedBid; +import org.prebid.server.auction.model.BidRejection; import org.prebid.server.auction.versionconverter.OrtbVersion; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.hooks.modules.ortb2.blocking.core.config.Attribute; @@ -196,7 +196,7 @@ public void shouldReturnResultWithBidWhenBidWithBlockedAdomainAndEnforceBlocksTr // when and then assertThat(blocker.block()).satisfies(result -> { hasValue(result, 0); - assertThat(result.getRejections()).containsOnly(RejectedBid.of(bid, RESPONSE_REJECTED_ADVERTISER_BLOCKED)); + assertThat(result.getRejections()).containsOnly(BidRejection.of(bid, RESPONSE_REJECTED_ADVERTISER_BLOCKED)); }); } @@ -327,7 +327,7 @@ public void shouldReturnResultWithBidWhenBidWithBlockedAdomainAndNotInDealsExcep // when and then assertThat(blocker.block()).satisfies(result -> { hasValue(result, 0); - assertThat(result.getRejections()).containsOnly(RejectedBid.of(bid, RESPONSE_REJECTED_ADVERTISER_BLOCKED)); + assertThat(result.getRejections()).containsOnly(BidRejection.of(bid, RESPONSE_REJECTED_ADVERTISER_BLOCKED)); }); } @@ -350,7 +350,7 @@ public void shouldReturnResultWithBidAndDebugMessageWhenBidIsBlocked() { assertThat(result.getValue()).isEqualTo(BlockedBids.of(singleton(0))); assertThat(result.getDebugMessages()).containsOnly( "Bid 0 from bidder bidder1 has been rejected, failed checks: [bcat]"); - assertThat(result.getRejections()).containsOnly(RejectedBid.of(bid, RESPONSE_REJECTED_INVALID_CREATIVE)); + assertThat(result.getRejections()).containsOnly(BidRejection.of(bid, RESPONSE_REJECTED_INVALID_CREATIVE)); }); } @@ -371,7 +371,7 @@ public void shouldReturnResultWithBidWithoutDebugMessageWhenBidIsBlockedAndDebug // when and then assertThat(blocker.block()).satisfies(result -> { hasValue(result, 0); - assertThat(result.getRejections()).containsOnly(RejectedBid.of(bid, RESPONSE_REJECTED_INVALID_CREATIVE)); + assertThat(result.getRejections()).containsOnly(BidRejection.of(bid, RESPONSE_REJECTED_INVALID_CREATIVE)); }); } @@ -437,9 +437,9 @@ public void shouldReturnResultWithAnalyticsResults() { AnalyticsResult.of("success-allow", null, "bidder1", "impId1")); assertThat(result.getRejections()).containsOnly( - RejectedBid.of(bid1, RESPONSE_REJECTED_INVALID_CREATIVE), - RejectedBid.of(bid2, RESPONSE_REJECTED_INVALID_CREATIVE), - RejectedBid.of(bid1, RESPONSE_REJECTED_ADVERTISER_BLOCKED)); + BidRejection.of(bid1, RESPONSE_REJECTED_INVALID_CREATIVE), + BidRejection.of(bid2, RESPONSE_REJECTED_INVALID_CREATIVE), + BidRejection.of(bid1, RESPONSE_REJECTED_ADVERTISER_BLOCKED)); }); } @@ -510,13 +510,13 @@ public void shouldReturnResultWithoutSomeBidsWhenAllAttributesInConfig() { "Bid 5 from bidder bidder1 has been rejected, failed checks: [battr]", "Bid 7 from bidder bidder1 has been rejected, failed checks: [badv, bcat]"); assertThat(result.getRejections()).containsOnly( - RejectedBid.of(bid1, RESPONSE_REJECTED_INVALID_CREATIVE), - RejectedBid.of(bid1, RESPONSE_REJECTED_ADVERTISER_BLOCKED), - RejectedBid.of(bid2, RESPONSE_REJECTED_INVALID_CREATIVE), - RejectedBid.of(bid4, RESPONSE_REJECTED_INVALID_CREATIVE), - RejectedBid.of(bid6, RESPONSE_REJECTED_INVALID_CREATIVE), - RejectedBid.of(bid8, RESPONSE_REJECTED_INVALID_CREATIVE), - RejectedBid.of(bid8, RESPONSE_REJECTED_ADVERTISER_BLOCKED)); + BidRejection.of(bid1, RESPONSE_REJECTED_INVALID_CREATIVE), + BidRejection.of(bid1, RESPONSE_REJECTED_ADVERTISER_BLOCKED), + BidRejection.of(bid2, RESPONSE_REJECTED_INVALID_CREATIVE), + BidRejection.of(bid4, RESPONSE_REJECTED_INVALID_CREATIVE), + BidRejection.of(bid6, RESPONSE_REJECTED_INVALID_CREATIVE), + BidRejection.of(bid8, RESPONSE_REJECTED_INVALID_CREATIVE), + BidRejection.of(bid8, RESPONSE_REJECTED_ADVERTISER_BLOCKED)); }); } diff --git a/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingRawBidderResponseHookTest.java b/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingRawBidderResponseHookTest.java index 7b574a3312c..9c4d20693a6 100644 --- a/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingRawBidderResponseHookTest.java +++ b/extra/modules/ortb2-blocking/src/test/java/org/prebid/server/hooks/modules/ortb2/blocking/v1/Ortb2BlockingRawBidderResponseHookTest.java @@ -10,7 +10,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; import org.prebid.server.auction.model.AuctionContext; -import org.prebid.server.auction.model.RejectedBid; +import org.prebid.server.auction.model.BidRejection; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.hooks.execution.v1.InvocationResultImpl; import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; @@ -197,9 +197,9 @@ public void shouldReturnResultWithPayloadUpdateAndAnalyticsTags() { .build())))))); assertThat(invocationResult.rejections()).containsOnly(entry("bidder1", List.of( - RejectedBid.of(bid1, + BidRejection.of(bid1, RESPONSE_REJECTED_ADVERTISER_BLOCKED), - RejectedBid.of(bid(bid -> bid.id("bidId2").adomain(singletonList("domain2.com"))), + BidRejection.of(bid(bid -> bid.id("bidId2").adomain(singletonList("domain2.com"))), RESPONSE_REJECTED_ADVERTISER_BLOCKED)))); } diff --git a/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHook.java b/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHook.java index aad918e0916..dcec0dfa3ff 100644 --- a/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHook.java +++ b/extra/modules/pb-richmedia-filter/src/main/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHook.java @@ -6,8 +6,8 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; import org.prebid.server.auction.model.BidderResponse; -import org.prebid.server.auction.model.Rejected; -import org.prebid.server.auction.model.RejectedBid; +import org.prebid.server.auction.model.Rejection; +import org.prebid.server.auction.model.BidRejection; import org.prebid.server.hooks.execution.v1.InvocationResultImpl; import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; import org.prebid.server.hooks.execution.v1.analytics.AppliedToImpl; @@ -27,13 +27,13 @@ import org.prebid.server.hooks.v1.auction.AuctionInvocationContext; import org.prebid.server.hooks.v1.bidder.AllProcessedBidResponsesHook; import org.prebid.server.hooks.v1.bidder.AllProcessedBidResponsesPayload; +import org.prebid.server.util.ListUtil; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; -import java.util.stream.Stream; public class PbRichmediaFilterAllProcessedBidResponsesHook implements AllProcessedBidResponsesHook { @@ -82,20 +82,20 @@ public Future> call( InvocationAction.no_action)); } - private Map> toRejections(List analyticsResults) { + private Map> toRejections(List analyticsResults) { return analyticsResults.stream().collect(Collectors.toMap( AnalyticsResult::getBidder, result -> result.getRejectedBids().stream() - .map(bid -> RejectedBid.of(bid, result.getRejectionReason())) - .map(Rejected.class::cast) + .map(bid -> BidRejection.of(bid, result.getRejectionReason())) + .map(Rejection.class::cast) .toList(), - (list1, list2) -> Stream.concat(list1.stream(), list2.stream()).collect(Collectors.toList()))); + ListUtil::union)); } private static InvocationResult toInvocationResult( List bidderResponses, Tags analyticsTags, - Map> rejections, + Map> rejections, InvocationAction action) { return InvocationResultImpl.builder() diff --git a/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHookTest.java b/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHookTest.java index 2fe927119af..b5707a9fe22 100644 --- a/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHookTest.java +++ b/extra/modules/pb-richmedia-filter/src/test/java/org/prebid/server/hooks/modules/pb/richmedia/filter/v1/PbRichmediaFilterAllProcessedBidResponsesHookTest.java @@ -9,7 +9,7 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.prebid.server.auction.model.BidderResponse; -import org.prebid.server.auction.model.RejectedBid; +import org.prebid.server.auction.model.BidRejection; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderSeatBid; import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; @@ -223,18 +223,18 @@ public void callShouldReturnAnalyticsResultsOfRejectedBids() { assertThat(result.rejections()).containsOnly( entry("bidderA", List.of( - RejectedBid.of( + BidRejection.of( BidderBid.builder() .bid(Bid.builder().id("bid-imp_id1").impid("imp_id1").build()) .build(), RESPONSE_REJECTED_INVALID_CREATIVE), - RejectedBid.of( + BidRejection.of( BidderBid.builder() .bid(Bid.builder().id("bid-imp_id2").impid("imp_id2").build()) .build(), RESPONSE_REJECTED_INVALID_CREATIVE))), entry("bidderB", List.of( - RejectedBid.of(BidderBid.builder() + BidRejection.of(BidderBid.builder() .bid(Bid.builder().id("bid-imp_id3").impid("imp_id3").build()) .build(), RESPONSE_REJECTED_INVALID_CREATIVE)))); diff --git a/src/main/java/org/prebid/server/auction/BidResponseCreator.java b/src/main/java/org/prebid/server/auction/BidResponseCreator.java index af77691b132..f983830e37f 100644 --- a/src/main/java/org/prebid/server/auction/BidResponseCreator.java +++ b/src/main/java/org/prebid/server/auction/BidResponseCreator.java @@ -36,7 +36,7 @@ import org.prebid.server.auction.model.CategoryMappingResult; import org.prebid.server.auction.model.MultiBidConfig; import org.prebid.server.auction.model.PaaFormat; -import org.prebid.server.auction.model.Rejected; +import org.prebid.server.auction.model.Rejection; import org.prebid.server.auction.model.TargetingInfo; import org.prebid.server.auction.model.debug.DebugContext; import org.prebid.server.auction.requestfactory.Ortb2ImplicitParametersResolver; @@ -1953,7 +1953,7 @@ private static BidResponse populateSeatNonBid(AuctionContext auctionContext, Bid final List seatNonBids = auctionContext.getBidRejectionTrackers().values().stream() .flatMap(bidRejectionTracker -> bidRejectionTracker.getRejected().stream()) .collect(Collectors.groupingBy( - Rejected::seat, + Rejection::seat, Collectors.mapping(entry -> NonBid.of(entry.impId(), entry.reason()), Collectors.toList()))) .entrySet().stream() .filter(entry -> !entry.getValue().isEmpty()) diff --git a/src/main/java/org/prebid/server/auction/DsaEnforcer.java b/src/main/java/org/prebid/server/auction/DsaEnforcer.java index 03ad3cfa33b..e123603b9a0 100644 --- a/src/main/java/org/prebid/server/auction/DsaEnforcer.java +++ b/src/main/java/org/prebid/server/auction/DsaEnforcer.java @@ -9,7 +9,7 @@ import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.model.BidderResponse; -import org.prebid.server.auction.model.RejectedBid; +import org.prebid.server.auction.model.BidRejection; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderError; import org.prebid.server.bidder.model.BidderSeatBid; @@ -73,7 +73,7 @@ public AuctionParticipation enforce(BidRequest bidRequest, } } catch (PreBidException e) { warnings.add(BidderError.invalidBid("Bid \"%s\": %s".formatted(bid.getId(), e.getMessage()))); - rejectionTracker.reject(RejectedBid.of(bidderBid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); + rejectionTracker.reject(BidRejection.of(bidderBid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); updatedBidderBids.remove(bidderBid); } } diff --git a/src/main/java/org/prebid/server/auction/model/RejectedBid.java b/src/main/java/org/prebid/server/auction/model/BidRejection.java similarity index 91% rename from src/main/java/org/prebid/server/auction/model/RejectedBid.java rename to src/main/java/org/prebid/server/auction/model/BidRejection.java index f7901999de2..17f8924447b 100644 --- a/src/main/java/org/prebid/server/auction/model/RejectedBid.java +++ b/src/main/java/org/prebid/server/auction/model/BidRejection.java @@ -4,7 +4,7 @@ import org.prebid.server.bidder.model.BidderBid; @Value(staticConstructor = "of") -public class RejectedBid implements Rejected { +public class BidRejection implements Rejection { BidderBid bid; diff --git a/src/main/java/org/prebid/server/auction/model/BidRejectionTracker.java b/src/main/java/org/prebid/server/auction/model/BidRejectionTracker.java index 0de2dc72bbb..c9fd9adf00b 100644 --- a/src/main/java/org/prebid/server/auction/model/BidRejectionTracker.java +++ b/src/main/java/org/prebid/server/auction/model/BidRejectionTracker.java @@ -34,7 +34,7 @@ public class BidRejectionTracker { private final String bidder; private final Set involvedImpIds; private final Map> succeededBidsIds; - private final Map> rejectedBids; + private final Map> rejections; public BidRejectionTracker(String bidder, Set involvedImpIds, double logSamplingRate) { this.bidder = bidder; @@ -42,7 +42,7 @@ public BidRejectionTracker(String bidder, Set involvedImpIds, double log this.logSamplingRate = logSamplingRate; succeededBidsIds = new HashMap<>(); - rejectedBids = new HashMap<>(); + rejections = new HashMap<>(); } public BidRejectionTracker(BidRejectionTracker anotherTracker, Set additionalImpIds) { @@ -52,7 +52,7 @@ public BidRejectionTracker(BidRejectionTracker anotherTracker, Set addit this.involvedImpIds.addAll(additionalImpIds); this.succeededBidsIds = new HashMap<>(anotherTracker.succeededBidsIds); - this.rejectedBids = new HashMap<>(anotherTracker.rejectedBids); + this.rejections = new HashMap<>(anotherTracker.rejections); } public void succeed(Collection bids) { @@ -67,7 +67,7 @@ private void succeed(Bid bid) { final String impId = bid.getImpid(); if (involvedImpIds.contains(impId)) { succeededBidsIds.computeIfAbsent(impId, key -> new HashSet<>()).add(bidId); - if (rejectedBids.containsKey(impId)) { + if (rejections.containsKey(impId)) { bidRejectionsLogger.warn( INCONSISTENT_RESPONSES_WARNING_TEMPLATE.formatted(bidder, impId), logSamplingRate); @@ -79,31 +79,31 @@ public void restoreFromRejection(Collection bids) { succeed(bids); } - public void reject(Collection rejections) { + public void reject(Collection rejections) { rejections.forEach(this::reject); } - public void reject(Rejected rejected) { - if (rejected instanceof RejectedImp && rejected.reason().getValue() >= 300) { + public void reject(Rejection rejection) { + if (rejection instanceof ImpRejection && rejection.reason().getValue() >= 300) { logger.warn("The rejected imp {} with the code {} equal to or higher than 300 assumes " + "that there is a rejected bid that shouldn't be lost"); return; } - final String impId = rejected.impId(); + final String impId = rejection.impId(); if (involvedImpIds.contains(impId)) { - if (rejectedBids.containsKey(impId)) { + if (rejections.containsKey(impId)) { bidRejectionsLogger.warn( MULTIPLE_REJECTIONS_WARNING_TEMPLATE.formatted(bidder, impId), logSamplingRate); } - rejectedBids.computeIfAbsent(impId, key -> new ArrayList<>()) - .add(rejected instanceof RejectedImp - ? RejectedImp.of(bidder, rejected.impId(), rejected.reason()) - : rejected); + rejections.computeIfAbsent(impId, key -> new ArrayList<>()) + .add(rejection instanceof ImpRejection + ? ImpRejection.of(bidder, rejection.impId(), rejection.reason()) + : rejection); if (succeededBidsIds.containsKey(impId)) { - final String bidId = rejected instanceof RejectedBid ? ((RejectedBid) rejected).bidId() : null; + final String bidId = rejection instanceof BidRejection ? ((BidRejection) rejection).bidId() : null; final Set succeededBids = succeededBidsIds.get(impId); final boolean removed = bidId == null || succeededBids.remove(bidId); if (removed && !succeededBids.isEmpty()) { @@ -116,22 +116,22 @@ public void reject(Rejected rejected) { } public void rejectImps(Collection impIds, BidRejectionReason reason) { - impIds.forEach(impId -> reject(RejectedImp.of(impId, reason))); + impIds.forEach(impId -> reject(ImpRejection.of(impId, reason))); } public void rejectAll(BidRejectionReason reason) { - involvedImpIds.forEach(impId -> reject(RejectedImp.of(impId, reason))); + involvedImpIds.forEach(impId -> reject(ImpRejection.of(impId, reason))); } - public Set getRejected() { - final Set rejectedResult = new HashSet<>(); + public Set getRejected() { + final Set rejectedResult = new HashSet<>(); for (String impId : involvedImpIds) { final Set succeededBids = succeededBidsIds.getOrDefault(impId, Collections.emptySet()); if (succeededBids.isEmpty()) { - if (rejectedBids.containsKey(impId)) { - rejectedResult.add(rejectedBids.get(impId).getFirst()); + if (rejections.containsKey(impId)) { + rejectedResult.add(rejections.get(impId).getFirst()); } else { - rejectedResult.add(RejectedImp.of(bidder, impId, BidRejectionReason.NO_BID)); + rejectedResult.add(ImpRejection.of(bidder, impId, BidRejectionReason.NO_BID)); } } } @@ -139,16 +139,16 @@ public Set getRejected() { return rejectedResult; } - public Map> getAllRejected() { - final Map> missingImpIds = new HashMap<>(); + public Map> getAllRejected() { + final Map> missingImpIds = new HashMap<>(); for (String impId : involvedImpIds) { final Set succeededBids = succeededBidsIds.getOrDefault(impId, Collections.emptySet()); - if (succeededBids.isEmpty() && !rejectedBids.containsKey(impId)) { + if (succeededBids.isEmpty() && !rejections.containsKey(impId)) { missingImpIds.computeIfAbsent(impId, key -> new ArrayList<>()) - .add(RejectedImp.of(bidder, impId, BidRejectionReason.NO_BID)); + .add(ImpRejection.of(bidder, impId, BidRejectionReason.NO_BID)); } } - return MapUtil.merge(missingImpIds, rejectedBids); + return MapUtil.merge(missingImpIds, rejections); } } diff --git a/src/main/java/org/prebid/server/auction/model/RejectedImp.java b/src/main/java/org/prebid/server/auction/model/ImpRejection.java similarity index 57% rename from src/main/java/org/prebid/server/auction/model/RejectedImp.java rename to src/main/java/org/prebid/server/auction/model/ImpRejection.java index 451688c265a..578a9f0ccfc 100644 --- a/src/main/java/org/prebid/server/auction/model/RejectedImp.java +++ b/src/main/java/org/prebid/server/auction/model/ImpRejection.java @@ -5,7 +5,7 @@ @Value(staticConstructor = "of") @Accessors(fluent = true) -public class RejectedImp implements Rejected { +public class ImpRejection implements Rejection { String seat; @@ -13,8 +13,8 @@ public class RejectedImp implements Rejected { BidRejectionReason reason; - public static RejectedImp of(String impId, BidRejectionReason reason) { - return RejectedImp.of(null, impId, reason); + public static ImpRejection of(String impId, BidRejectionReason reason) { + return ImpRejection.of(null, impId, reason); } } diff --git a/src/main/java/org/prebid/server/auction/model/Rejected.java b/src/main/java/org/prebid/server/auction/model/Rejection.java similarity index 80% rename from src/main/java/org/prebid/server/auction/model/Rejected.java rename to src/main/java/org/prebid/server/auction/model/Rejection.java index 0f1685ee51f..9604cffcea2 100644 --- a/src/main/java/org/prebid/server/auction/model/Rejected.java +++ b/src/main/java/org/prebid/server/auction/model/Rejection.java @@ -1,6 +1,6 @@ package org.prebid.server.auction.model; -public interface Rejected { +public interface Rejection { String seat(); diff --git a/src/main/java/org/prebid/server/floors/BasicPriceFloorEnforcer.java b/src/main/java/org/prebid/server/floors/BasicPriceFloorEnforcer.java index d2e7ef905e8..2252a8fad54 100644 --- a/src/main/java/org/prebid/server/floors/BasicPriceFloorEnforcer.java +++ b/src/main/java/org/prebid/server/floors/BasicPriceFloorEnforcer.java @@ -11,7 +11,7 @@ import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.model.BidderRequest; import org.prebid.server.auction.model.BidderResponse; -import org.prebid.server.auction.model.RejectedBid; +import org.prebid.server.auction.model.BidRejection; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderError; import org.prebid.server.bidder.model.BidderSeatBid; @@ -180,7 +180,7 @@ private AuctionParticipation applyEnforcement(BidRequest bidRequest, "Bid with id '%s' was rejected by floor enforcement: price %s is below the floor %s" .formatted(bid.getId(), price, floor), impId)); - rejectionTracker.reject(RejectedBid.of(bidderBid, BidRejectionReason.RESPONSE_REJECTED_BELOW_FLOOR)); + rejectionTracker.reject(BidRejection.of(bidderBid, BidRejectionReason.RESPONSE_REJECTED_BELOW_FLOOR)); updatedBidderBids.remove(bidderBid); } } diff --git a/src/main/java/org/prebid/server/hooks/execution/GroupResult.java b/src/main/java/org/prebid/server/hooks/execution/GroupResult.java index 7b3c79aef8b..718548ae15b 100644 --- a/src/main/java/org/prebid/server/hooks/execution/GroupResult.java +++ b/src/main/java/org/prebid/server/hooks/execution/GroupResult.java @@ -3,7 +3,7 @@ import lombok.Getter; import lombok.experimental.Accessors; import org.apache.commons.collections4.MapUtils; -import org.prebid.server.auction.model.Rejected; +import org.prebid.server.auction.model.Rejection; import org.prebid.server.hooks.execution.model.ExecutionAction; import org.prebid.server.hooks.execution.model.ExecutionStatus; import org.prebid.server.hooks.execution.model.GroupExecutionOutcome; @@ -37,7 +37,7 @@ class GroupResult { private final boolean rejectAllowed; - private final Map> rejections = new HashMap<>(); + private final Map> rejections = new HashMap<>(); private final List hookExecutionOutcomes = new ArrayList<>(); diff --git a/src/main/java/org/prebid/server/hooks/execution/HookStageExecutor.java b/src/main/java/org/prebid/server/hooks/execution/HookStageExecutor.java index cb48c76c2a9..4f8a1f3f209 100644 --- a/src/main/java/org/prebid/server/hooks/execution/HookStageExecutor.java +++ b/src/main/java/org/prebid/server/hooks/execution/HookStageExecutor.java @@ -17,7 +17,7 @@ import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.model.BidderRequest; import org.prebid.server.auction.model.BidderResponse; -import org.prebid.server.auction.model.Rejected; +import org.prebid.server.auction.model.Rejection; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.execution.timeout.Timeout; import org.prebid.server.execution.timeout.TimeoutFactory; @@ -570,7 +570,7 @@ private HookStageExecutionResult rejectAll(AuctionContext auctionContext, bidder, key -> new BidRejectionTracker( key, - rejectedList.stream().map(Rejected::impId).collect(Collectors.toSet()), + rejectedList.stream().map(Rejection::impId).collect(Collectors.toSet()), logSamplingRate)) .reject(rejectedList)); diff --git a/src/main/java/org/prebid/server/hooks/execution/StageResult.java b/src/main/java/org/prebid/server/hooks/execution/StageResult.java index 9999319d7be..5183a1571ad 100644 --- a/src/main/java/org/prebid/server/hooks/execution/StageResult.java +++ b/src/main/java/org/prebid/server/hooks/execution/StageResult.java @@ -2,13 +2,13 @@ import lombok.Getter; import lombok.experimental.Accessors; -import org.prebid.server.auction.model.Rejected; +import org.prebid.server.auction.model.Rejection; import org.prebid.server.hooks.execution.model.GroupExecutionOutcome; import org.prebid.server.hooks.execution.model.StageExecutionOutcome; -import org.prebid.server.util.MapUtil; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -52,10 +52,19 @@ private List groupExecutionOutcomes() { .toList(); } - public Map> rejections() { + public Map> rejections() { return groupResults.stream() .map(GroupResult::rejections) - .reduce(MapUtil::collectionMerge) + .reduce(StageResult::collectionMerge) .orElse(Collections.emptyMap()); } + + private static Map> collectionMerge(Map> left, + Map> right) { + + final Map> merged = new HashMap<>(); + left.forEach((key, value) -> merged.put(key, new ArrayList<>(value))); + right.forEach((key, value) -> merged.computeIfAbsent(key, k -> new ArrayList<>()).addAll(value)); + return Collections.unmodifiableMap(merged); + } } diff --git a/src/main/java/org/prebid/server/hooks/execution/model/HookStageExecutionResult.java b/src/main/java/org/prebid/server/hooks/execution/model/HookStageExecutionResult.java index 1050dc6c0e4..d88d2599bf8 100644 --- a/src/main/java/org/prebid/server/hooks/execution/model/HookStageExecutionResult.java +++ b/src/main/java/org/prebid/server/hooks/execution/model/HookStageExecutionResult.java @@ -1,7 +1,7 @@ package org.prebid.server.hooks.execution.model; import lombok.Value; -import org.prebid.server.auction.model.Rejected; +import org.prebid.server.auction.model.Rejection; import java.util.Collections; import java.util.List; @@ -14,10 +14,10 @@ public class HookStageExecutionResult { PAYLOAD payload; - Map> rejections; + Map> rejections; public static HookStageExecutionResult success(PAYLOAD payload, - Map> rejections) { + Map> rejections) { return of(false, payload, rejections); } diff --git a/src/main/java/org/prebid/server/hooks/execution/provider/abtest/ABTestHook.java b/src/main/java/org/prebid/server/hooks/execution/provider/abtest/ABTestHook.java index 44524707786..fc7865d1211 100644 --- a/src/main/java/org/prebid/server/hooks/execution/provider/abtest/ABTestHook.java +++ b/src/main/java/org/prebid/server/hooks/execution/provider/abtest/ABTestHook.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import io.vertx.core.Future; -import org.prebid.server.auction.model.Rejected; +import org.prebid.server.auction.model.Rejection; import org.prebid.server.hooks.execution.v1.InvocationResultImpl; import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; import org.prebid.server.hooks.execution.v1.analytics.ResultImpl; @@ -123,7 +123,7 @@ public List warnings() { } @Override - public Map> rejections() { + public Map> rejections() { return invocationResult.rejections(); } diff --git a/src/main/java/org/prebid/server/hooks/execution/v1/InvocationResultImpl.java b/src/main/java/org/prebid/server/hooks/execution/v1/InvocationResultImpl.java index df675f21b46..694c50e8104 100644 --- a/src/main/java/org/prebid/server/hooks/execution/v1/InvocationResultImpl.java +++ b/src/main/java/org/prebid/server/hooks/execution/v1/InvocationResultImpl.java @@ -3,7 +3,7 @@ import lombok.Builder; import lombok.Value; import lombok.experimental.Accessors; -import org.prebid.server.auction.model.Rejected; +import org.prebid.server.auction.model.Rejection; import org.prebid.server.hooks.v1.InvocationAction; import org.prebid.server.hooks.v1.InvocationResult; import org.prebid.server.hooks.v1.InvocationStatus; @@ -32,7 +32,7 @@ public class InvocationResultImpl implements InvocationResult List debugMessages; - Map> rejections; + Map> rejections; Object moduleContext; diff --git a/src/main/java/org/prebid/server/hooks/v1/InvocationResult.java b/src/main/java/org/prebid/server/hooks/v1/InvocationResult.java index 58f73f24987..efdb308d34c 100644 --- a/src/main/java/org/prebid/server/hooks/v1/InvocationResult.java +++ b/src/main/java/org/prebid/server/hooks/v1/InvocationResult.java @@ -1,6 +1,6 @@ package org.prebid.server.hooks.v1; -import org.prebid.server.auction.model.Rejected; +import org.prebid.server.auction.model.Rejection; import org.prebid.server.hooks.v1.analytics.Tags; import java.util.List; @@ -20,7 +20,7 @@ public interface InvocationResult { List warnings(); - Map> rejections(); + Map> rejections(); List debugMessages(); diff --git a/src/main/java/org/prebid/server/util/MapUtil.java b/src/main/java/org/prebid/server/util/MapUtil.java index ae7185f9290..282d2b40795 100644 --- a/src/main/java/org/prebid/server/util/MapUtil.java +++ b/src/main/java/org/prebid/server/util/MapUtil.java @@ -1,6 +1,5 @@ package org.prebid.server.util; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -16,17 +15,4 @@ public static Map merge(Map left, Map right) { return Collections.unmodifiableMap(merged); } - - public static > Map collectionMerge(Map left, Map right) { - final Map merged = new HashMap<>(left); - right.forEach((key, value) -> { - if (merged.containsKey(key)) { - merged.get(key).addAll(value); - } else { - merged.put(key, value); - } - }); - - return Collections.unmodifiableMap(merged); - } } diff --git a/src/main/java/org/prebid/server/validation/ResponseBidValidator.java b/src/main/java/org/prebid/server/validation/ResponseBidValidator.java index e0503fab0d9..660d5785395 100644 --- a/src/main/java/org/prebid/server/validation/ResponseBidValidator.java +++ b/src/main/java/org/prebid/server/validation/ResponseBidValidator.java @@ -13,7 +13,7 @@ import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.auction.model.BidRejectionTracker; -import org.prebid.server.auction.model.RejectedBid; +import org.prebid.server.auction.model.BidRejection; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.log.ConditionalLogger; import org.prebid.server.log.Logger; @@ -166,7 +166,7 @@ private void validateSeat(BidderBid bid, final String message = "invalid bidder code %s was set by the adapter %s for the account %s" .formatted(bid.getSeat(), bidder, account.getId()); - bidRejectionTracker.reject(RejectedBid.of(bid, BidRejectionReason.RESPONSE_REJECTED_GENERAL)); + bidRejectionTracker.reject(BidRejection.of(bid, BidRejectionReason.RESPONSE_REJECTED_GENERAL)); metrics.updateSeatValidationMetrics(bidder); alternateBidderCodeLogger.warn(message, logSamplingRate); throw new ValidationException(message); @@ -318,7 +318,7 @@ private List singleWarningOrValidationException( return switch (enforcement) { case enforce -> { - bidRejectionTracker.reject(RejectedBid.of(bidderBid, bidRejectionReason)); + bidRejectionTracker.reject(BidRejection.of(bidderBid, bidRejectionReason)); metricsRecorder.accept(MetricName.err); conditionalLogger.warn(message, logSamplingRate); throw new ValidationException(message); diff --git a/src/test/java/org/prebid/server/analytics/reporter/greenbids/GreenbidsAnalyticsReporterTest.java b/src/test/java/org/prebid/server/analytics/reporter/greenbids/GreenbidsAnalyticsReporterTest.java index 801000bc7d8..000feebbd5d 100644 --- a/src/test/java/org/prebid/server/analytics/reporter/greenbids/GreenbidsAnalyticsReporterTest.java +++ b/src/test/java/org/prebid/server/analytics/reporter/greenbids/GreenbidsAnalyticsReporterTest.java @@ -39,7 +39,7 @@ import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.auction.model.BidRejectionTracker; -import org.prebid.server.auction.model.RejectedImp; +import org.prebid.server.auction.model.ImpRejection; import org.prebid.server.hooks.execution.model.ExecutionStatus; import org.prebid.server.hooks.execution.model.GroupExecutionOutcome; import org.prebid.server.hooks.execution.model.HookExecutionContext; @@ -817,7 +817,7 @@ private static BidRejectionTracker givenBidRejectionTracker() { "seat3", Set.of("adunitcodevalue"), 1.0); - bidRejectionTracker.reject(RejectedImp.of("imp1", BidRejectionReason.NO_BID)); + bidRejectionTracker.reject(ImpRejection.of("imp1", BidRejectionReason.NO_BID)); return bidRejectionTracker; } diff --git a/src/test/java/org/prebid/server/auction/BidResponseCreatorTest.java b/src/test/java/org/prebid/server/auction/BidResponseCreatorTest.java index ed34c53c8ee..33c9747d70d 100644 --- a/src/test/java/org/prebid/server/auction/BidResponseCreatorTest.java +++ b/src/test/java/org/prebid/server/auction/BidResponseCreatorTest.java @@ -45,7 +45,7 @@ import org.prebid.server.auction.model.CategoryMappingResult; import org.prebid.server.auction.model.MultiBidConfig; import org.prebid.server.auction.model.PaaFormat; -import org.prebid.server.auction.model.RejectedImp; +import org.prebid.server.auction.model.ImpRejection; import org.prebid.server.auction.model.TargetingInfo; import org.prebid.server.auction.model.TimeoutContext; import org.prebid.server.auction.model.debug.DebugContext; @@ -4394,7 +4394,7 @@ public void shouldDropFledgeResponsesReferencingUnknownImps() { public void shouldPopulateExtPrebidSeatNonBidWhenReturnAllBidStatusFlagIsTrue() { // given final BidRejectionTracker bidRejectionTracker = mock(BidRejectionTracker.class); - given(bidRejectionTracker.getRejected()).willReturn(singleton(RejectedImp.of("someBidder", "impId2", NO_BID))); + given(bidRejectionTracker.getRejected()).willReturn(singleton(ImpRejection.of("someBidder", "impId2", NO_BID))); final Bid bid = Bid.builder().id("bidId").price(BigDecimal.valueOf(3.67)).impid("impId").build(); final List bidderResponses = singletonList( diff --git a/src/test/java/org/prebid/server/auction/DsaEnforcerTest.java b/src/test/java/org/prebid/server/auction/DsaEnforcerTest.java index 3394ab04340..916cb934482 100644 --- a/src/test/java/org/prebid/server/auction/DsaEnforcerTest.java +++ b/src/test/java/org/prebid/server/auction/DsaEnforcerTest.java @@ -14,7 +14,7 @@ import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.model.BidderResponse; -import org.prebid.server.auction.model.RejectedBid; +import org.prebid.server.auction.model.BidRejection; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderError; import org.prebid.server.bidder.model.BidderSeatBid; @@ -105,7 +105,7 @@ public void enforceShouldRejectBidAndAddWarningWhenDsaIsNotRequiredAndDsaRespons .bidderResponse(BidderResponse.of("bidder", expectedSeatBid, 100)) .build(); assertThat(actual).isEqualTo(expectedParticipation); - verify(bidRejectionTracker).reject(RejectedBid.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); + verify(bidRejectionTracker).reject(BidRejection.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); } @Test @@ -138,7 +138,7 @@ public void enforceShouldRejectBidAndAddWarningWhenDsaIsNotRequiredAndDsaRespons .bidderResponse(BidderResponse.of("bidder", expectedSeatBid, 100)) .build(); assertThat(actual).isEqualTo(expectedParticipation); - verify(bidRejectionTracker).reject(RejectedBid.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); + verify(bidRejectionTracker).reject(BidRejection.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); } @Test @@ -334,7 +334,7 @@ public void enforceShouldRejectBidAndAddWarningWhenBidExtHasEmptyDsaAndDsaIsRequ .bidderResponse(BidderResponse.of("bidder", expectedSeatBid, 100)) .build(); assertThat(actual).isEqualTo(expectedParticipation); - verify(bidRejectionTracker).reject(RejectedBid.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); + verify(bidRejectionTracker).reject(BidRejection.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); } @Test @@ -368,7 +368,7 @@ public void enforceShouldRejectBidAndAddWarningWhenDsaIsRequiredAndDsaResponseHa .bidderResponse(BidderResponse.of("bidder", expectedSeatBid, 100)) .build(); assertThat(actual).isEqualTo(expectedParticipation); - verify(bidRejectionTracker).reject(RejectedBid.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); + verify(bidRejectionTracker).reject(BidRejection.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); } @Test @@ -402,7 +402,7 @@ public void enforceShouldRejectBidAndAddWarningWhenDsaIsRequiredAndDsaResponseHa .bidderResponse(BidderResponse.of("bidder", expectedSeatBid, 100)) .build(); assertThat(actual).isEqualTo(expectedParticipation); - verify(bidRejectionTracker).reject(RejectedBid.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); + verify(bidRejectionTracker).reject(BidRejection.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); } @Test @@ -437,7 +437,7 @@ public void enforceShouldRejectBidAndAddWarningWhenDsaIsRequiredAndPublisherAndA .bidderResponse(BidderResponse.of("bidder", expectedSeatBid, 100)) .build(); assertThat(actual).isEqualTo(expectedParticipation); - verify(bidRejectionTracker).reject(RejectedBid.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); + verify(bidRejectionTracker).reject(BidRejection.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); } @Test @@ -472,7 +472,7 @@ public void enforceShouldRejectBidAndAddWarningWhenDsaIsRequiredAndPublisherAndA .bidderResponse(BidderResponse.of("bidder", expectedSeatBid, 100)) .build(); assertThat(actual).isEqualTo(expectedParticipation); - verify(bidRejectionTracker).reject(RejectedBid.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); + verify(bidRejectionTracker).reject(BidRejection.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); } @Test @@ -506,7 +506,7 @@ public void enforceShouldRejectBidAndAddWarningWhenDsaIsRequiredAndPublisherNotR .bidderResponse(BidderResponse.of("bidder", expectedSeatBid, 100)) .build(); assertThat(actual).isEqualTo(expectedParticipation); - verify(bidRejectionTracker).reject(RejectedBid.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); + verify(bidRejectionTracker).reject(BidRejection.of(bid, BidRejectionReason.RESPONSE_REJECTED_DSA_PRIVACY)); } private static ExtRegs givenExtRegs(DsaRequired dsaRequired, DsaPublisherRender pubRender) { diff --git a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java index aa5c821ed35..748ce616e5c 100644 --- a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java +++ b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java @@ -54,7 +54,7 @@ import org.prebid.server.auction.model.BidderRequest; import org.prebid.server.auction.model.BidderResponse; import org.prebid.server.auction.model.MultiBidConfig; -import org.prebid.server.auction.model.RejectedImp; +import org.prebid.server.auction.model.ImpRejection; import org.prebid.server.auction.model.StoredResponseResult; import org.prebid.server.auction.model.TimeoutContext; import org.prebid.server.auction.model.debug.DebugContext; @@ -4062,7 +4062,7 @@ public void shouldResponseWithEmptySeatBidIfBidderNotSupportRequestCurrency() { .extracting(AuctionContext::getBidRejectionTrackers) .extracting(rejectionTrackers -> rejectionTrackers.get("bidder1")) .extracting(BidRejectionTracker::getRejected) - .isEqualTo(Set.of(RejectedImp.of("bidder1", "impId1", REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY))); + .isEqualTo(Set.of(ImpRejection.of("bidder1", "impId1", REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY))); } @Test @@ -4096,11 +4096,11 @@ public void shouldMakeBidRejectionTrackers() { final Map actualTrackers = result.result().getBidRejectionTrackers(); assertThat(actualTrackers.keySet()).containsOnly("bidder1", "bidder2"); assertThat(actualTrackers.get("bidder1").getRejected()).containsOnly( - RejectedImp.of("bidder1", "impId1", NO_BID), - RejectedImp.of("bidder1", "impId2", NO_BID)); + ImpRejection.of("bidder1", "impId1", NO_BID), + ImpRejection.of("bidder1", "impId2", NO_BID)); assertThat(actualTrackers.get("bidder2").getRejected()).containsOnly( - RejectedImp.of("bidder2", "impId1", NO_BID), - RejectedImp.of("bidder2", "impId2", NO_BID)); + ImpRejection.of("bidder2", "impId1", NO_BID), + ImpRejection.of("bidder2", "impId2", NO_BID)); } @Test diff --git a/src/test/java/org/prebid/server/auction/model/BidRejectionTrackerTest.java b/src/test/java/org/prebid/server/auction/model/BidRejectionTrackerTest.java index d169534c200..6e3bd66af7a 100644 --- a/src/test/java/org/prebid/server/auction/model/BidRejectionTrackerTest.java +++ b/src/test/java/org/prebid/server/auction/model/BidRejectionTrackerTest.java @@ -32,7 +32,7 @@ public void setUp() { @Test public void succeedShouldRestoreImpFromImpRejection() { // given - target.reject(RejectedImp.of("impId1", ERROR_GENERAL)); + target.reject(ImpRejection.of("impId1", ERROR_GENERAL)); // when final BidderBid bid = givenBid("bidId1", "impId1"); @@ -41,14 +41,14 @@ public void succeedShouldRestoreImpFromImpRejection() { // then assertThat(target.getRejected()).isEmpty(); assertThat(target.getAllRejected()) - .containsOnly(entry("impId1", List.of(RejectedImp.of("bidder", "impId1", ERROR_GENERAL)))); + .containsOnly(entry("impId1", List.of(ImpRejection.of("bidder", "impId1", ERROR_GENERAL)))); } @Test public void succeedShouldRestoreImpFromBidRejection() { // given final BidderBid bid = givenBid("bidId1", "impId1"); - target.reject(RejectedBid.of(bid, ERROR_GENERAL)); + target.reject(BidRejection.of(bid, ERROR_GENERAL)); // when target.succeed(singleton(bid)); @@ -56,65 +56,65 @@ public void succeedShouldRestoreImpFromBidRejection() { // then assertThat(target.getRejected()).isEmpty(); assertThat(target.getAllRejected()) - .containsOnly(entry("impId1", List.of(RejectedBid.of(bid, ERROR_GENERAL)))); + .containsOnly(entry("impId1", List.of(BidRejection.of(bid, ERROR_GENERAL)))); } @Test public void succeedShouldIgnoreUninvolvedImpIdsOnImpRejection() { // given - target.reject(RejectedImp.of("impId1", ERROR_GENERAL)); + target.reject(ImpRejection.of("impId1", ERROR_GENERAL)); // when final BidderBid bid = givenBid("bidId2", "impId2"); target.succeed(singleton(bid)); // then - assertThat(target.getRejected()).extracting(Rejected::seat, Rejected::impId, Rejected::reason) + assertThat(target.getRejected()).extracting(Rejection::seat, Rejection::impId, Rejection::reason) .containsOnly(tuple("bidder", "impId1", ERROR_GENERAL)); assertThat(target.getAllRejected()) - .containsOnly(entry("impId1", List.of(RejectedImp.of("bidder", "impId1", ERROR_GENERAL)))); + .containsOnly(entry("impId1", List.of(ImpRejection.of("bidder", "impId1", ERROR_GENERAL)))); } @Test public void succeedShouldIgnoreUninvolvedImpIdsOnBidRejection() { // given final BidderBid bid1 = givenBid("bidId1", "impId1"); - target.reject(RejectedBid.of(bid1, ERROR_GENERAL)); + target.reject(BidRejection.of(bid1, ERROR_GENERAL)); // when final BidderBid bid2 = givenBid("bidId2", "impId2"); target.succeed(singleton(bid2)); // then - assertThat(target.getRejected()).extracting(Rejected::seat, Rejected::impId, Rejected::reason) + assertThat(target.getRejected()).extracting(Rejection::seat, Rejection::impId, Rejection::reason) .containsOnly(tuple("seat", "impId1", ERROR_GENERAL)); assertThat(target.getAllRejected()) - .containsOnly(entry("impId1", List.of(RejectedBid.of(bid1, ERROR_GENERAL)))); + .containsOnly(entry("impId1", List.of(BidRejection.of(bid1, ERROR_GENERAL)))); } @Test public void rejectImpShouldRecordImpRejectionFirstTimeIfImpIdIsInvolved() { // when - target.reject(RejectedImp.of("impId1", ERROR_GENERAL)); + target.reject(ImpRejection.of("impId1", ERROR_GENERAL)); // then - assertThat(target.getRejected()).extracting(Rejected::seat, Rejected::impId, Rejected::reason) + assertThat(target.getRejected()).extracting(Rejection::seat, Rejection::impId, Rejection::reason) .containsOnly(tuple("bidder", "impId1", ERROR_GENERAL)); assertThat(target.getAllRejected()) - .containsOnly(entry("impId1", List.of(RejectedImp.of("bidder", "impId1", ERROR_GENERAL)))); + .containsOnly(entry("impId1", List.of(ImpRejection.of("bidder", "impId1", ERROR_GENERAL)))); } @Test public void rejectBidShouldRecordBidRejectionFirstTimeIfImpIdIsInvolved() { // when final BidderBid bid = givenBid("bidId1", "impId1"); - target.reject(RejectedBid.of(bid, ERROR_GENERAL)); + target.reject(BidRejection.of(bid, ERROR_GENERAL)); // then - assertThat(target.getRejected()).extracting(Rejected::seat, Rejected::impId, Rejected::reason) + assertThat(target.getRejected()).extracting(Rejection::seat, Rejection::impId, Rejection::reason) .containsOnly(tuple("seat", "impId1", ERROR_GENERAL)); assertThat(target.getAllRejected()) - .containsOnly(entry("impId1", List.of(RejectedBid.of(bid, ERROR_GENERAL)))); + .containsOnly(entry("impId1", List.of(BidRejection.of(bid, ERROR_GENERAL)))); } @Test @@ -125,61 +125,61 @@ public void rejectBidShouldRecordBidRejectionAfterPreviouslySucceededBid() { target.succeed(Set.of(bid1, bid2)); // when - target.reject(RejectedBid.of(bid1, ERROR_GENERAL)); + target.reject(BidRejection.of(bid1, ERROR_GENERAL)); // then assertThat(target.getRejected()).isEmpty(); assertThat(target.getAllRejected()) - .containsOnly(entry("impId1", List.of(RejectedBid.of(bid1, ERROR_GENERAL)))); + .containsOnly(entry("impId1", List.of(BidRejection.of(bid1, ERROR_GENERAL)))); } @Test public void rejectImpShouldNotRecordImpRejectionIfImpIdIsAlreadyRejected() { // given - target.reject(RejectedImp.of("impId1", ERROR_GENERAL)); + target.reject(ImpRejection.of("impId1", ERROR_GENERAL)); // when - target.reject(RejectedImp.of("impId1", ERROR_INVALID_BID_RESPONSE)); + target.reject(ImpRejection.of("impId1", ERROR_INVALID_BID_RESPONSE)); // then - assertThat(target.getRejected()).extracting(Rejected::seat, Rejected::impId, Rejected::reason) + assertThat(target.getRejected()).extracting(Rejection::seat, Rejection::impId, Rejection::reason) .containsOnly(tuple("bidder", "impId1", ERROR_GENERAL)); assertThat(target.getAllRejected()) .containsOnly(entry("impId1", List.of( - RejectedImp.of("bidder", "impId1", ERROR_GENERAL), - RejectedImp.of("bidder", "impId1", ERROR_INVALID_BID_RESPONSE)))); + ImpRejection.of("bidder", "impId1", ERROR_GENERAL), + ImpRejection.of("bidder", "impId1", ERROR_INVALID_BID_RESPONSE)))); } @Test public void rejectBidShouldNotRecordImpRejectionButRecordBidRejectionEvenIfImpIsAlreadyRejected() { // given final BidderBid bid1 = givenBid("bidId1", "impId1"); - target.reject(RejectedBid.of(bid1, RESPONSE_REJECTED_GENERAL)); + target.reject(BidRejection.of(bid1, RESPONSE_REJECTED_GENERAL)); // when final BidderBid bid2 = givenBid("bidId2", "impId1"); - target.reject(RejectedBid.of(bid2, RESPONSE_REJECTED_BELOW_FLOOR)); + target.reject(BidRejection.of(bid2, RESPONSE_REJECTED_BELOW_FLOOR)); // then - assertThat(target.getRejected()).extracting(Rejected::seat, Rejected::impId, Rejected::reason) + assertThat(target.getRejected()).extracting(Rejection::seat, Rejection::impId, Rejection::reason) .containsOnly(tuple("seat", "impId1", RESPONSE_REJECTED_GENERAL)); assertThat(target.getAllRejected()) .containsOnly(entry("impId1", List.of( - RejectedBid.of(bid1, RESPONSE_REJECTED_GENERAL), - RejectedBid.of(bid2, RESPONSE_REJECTED_BELOW_FLOOR)))); + BidRejection.of(bid1, RESPONSE_REJECTED_GENERAL), + BidRejection.of(bid2, RESPONSE_REJECTED_BELOW_FLOOR)))); } @Test public void rejectAllShouldTryRejectingEachImpId() { // given target = new BidRejectionTracker("bidder", Set.of("impId1", "impId2", "impId3"), 0); - target.reject(RejectedImp.of("impId1", NO_BID)); + target.reject(ImpRejection.of("impId1", NO_BID)); // when target.rejectAll(ERROR_TIMED_OUT); // then - assertThat(target.getRejected()).extracting(Rejected::seat, Rejected::impId, Rejected::reason) + assertThat(target.getRejected()).extracting(Rejection::seat, Rejection::impId, Rejection::reason) .containsOnly( tuple("bidder", "impId1", NO_BID), tuple("bidder", "impId2", ERROR_TIMED_OUT), @@ -188,10 +188,10 @@ public void rejectAllShouldTryRejectingEachImpId() { assertThat(target.getAllRejected()) .containsOnly( entry("impId1", List.of( - RejectedImp.of("bidder", "impId1", NO_BID), - RejectedImp.of("bidder", "impId1", ERROR_TIMED_OUT))), - entry("impId2", List.of(RejectedImp.of("bidder", "impId2", ERROR_TIMED_OUT))), - entry("impId3", List.of(RejectedImp.of("bidder", "impId3", ERROR_TIMED_OUT)))); + ImpRejection.of("bidder", "impId1", NO_BID), + ImpRejection.of("bidder", "impId1", ERROR_TIMED_OUT))), + entry("impId2", List.of(ImpRejection.of("bidder", "impId2", ERROR_TIMED_OUT))), + entry("impId3", List.of(ImpRejection.of("bidder", "impId3", ERROR_TIMED_OUT)))); } @Test @@ -199,19 +199,19 @@ public void rejectBidsShouldTryRejectingEachBid() { // given target = new BidRejectionTracker("bidder", Set.of("impId1", "impId2", "impId3"), 0); final BidderBid bid0 = givenBid("bidId0", "impId1"); - target.reject(RejectedBid.of(bid0, RESPONSE_REJECTED_GENERAL)); + target.reject(BidRejection.of(bid0, RESPONSE_REJECTED_GENERAL)); // when final BidderBid bid1 = givenBid("bidId1", "impId1"); final BidderBid bid2 = givenBid("bidId2", "impId2"); final BidderBid bid3 = givenBid("bidId3", "impId3"); target.reject(Set.of( - RejectedBid.of(bid1, RESPONSE_REJECTED_DSA_PRIVACY), - RejectedBid.of(bid2, RESPONSE_REJECTED_DSA_PRIVACY), - RejectedBid.of(bid3, RESPONSE_REJECTED_DSA_PRIVACY))); + BidRejection.of(bid1, RESPONSE_REJECTED_DSA_PRIVACY), + BidRejection.of(bid2, RESPONSE_REJECTED_DSA_PRIVACY), + BidRejection.of(bid3, RESPONSE_REJECTED_DSA_PRIVACY))); // then - assertThat(target.getRejected()).extracting(Rejected::seat, Rejected::impId, Rejected::reason) + assertThat(target.getRejected()).extracting(Rejection::seat, Rejection::impId, Rejection::reason) .containsOnly( tuple("seat", "impId1", RESPONSE_REJECTED_GENERAL), tuple("seat", "impId2", RESPONSE_REJECTED_DSA_PRIVACY), @@ -220,10 +220,10 @@ public void rejectBidsShouldTryRejectingEachBid() { assertThat(target.getAllRejected()) .containsOnly( entry("impId1", List.of( - RejectedBid.of(bid0, RESPONSE_REJECTED_GENERAL), - RejectedBid.of(bid1, RESPONSE_REJECTED_DSA_PRIVACY))), - entry("impId2", List.of(RejectedBid.of(bid2, RESPONSE_REJECTED_DSA_PRIVACY))), - entry("impId3", List.of(RejectedBid.of(bid3, RESPONSE_REJECTED_DSA_PRIVACY)))); + BidRejection.of(bid0, RESPONSE_REJECTED_GENERAL), + BidRejection.of(bid1, RESPONSE_REJECTED_DSA_PRIVACY))), + entry("impId2", List.of(BidRejection.of(bid2, RESPONSE_REJECTED_DSA_PRIVACY))), + entry("impId3", List.of(BidRejection.of(bid3, RESPONSE_REJECTED_DSA_PRIVACY)))); } @Test @@ -234,7 +234,7 @@ public void getRejectedImpsShouldTreatUnsuccessfulImpsAsNoBidRejection() { target.succeed(singleton(bid)); // then - assertThat(target.getRejected()).extracting(Rejected::seat, Rejected::impId, Rejected::reason) + assertThat(target.getRejected()).extracting(Rejection::seat, Rejection::impId, Rejection::reason) .containsOnly(tuple("bidder", "impId1", NO_BID)); } diff --git a/src/test/java/org/prebid/server/bidder/HttpBidderRequesterTest.java b/src/test/java/org/prebid/server/bidder/HttpBidderRequesterTest.java index ddd46680541..dab0028c791 100644 --- a/src/test/java/org/prebid/server/bidder/HttpBidderRequesterTest.java +++ b/src/test/java/org/prebid/server/bidder/HttpBidderRequesterTest.java @@ -25,7 +25,7 @@ import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.model.BidderRequest; -import org.prebid.server.auction.model.Rejected; +import org.prebid.server.auction.model.Rejection; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderCall; import org.prebid.server.bidder.model.BidderError; @@ -229,7 +229,7 @@ public void shouldPassStoredResponseToBidderMakeBidsMethodAndReturnSeatBids() { .isEqualTo("storedResponse"); assertThat(bidderSeatBid.getBids()).hasSameElementsAs(bids); - verify(bidRejectionTracker, never()).reject(any(Rejected.class)); + verify(bidRejectionTracker, never()).reject(any(Rejection.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -268,7 +268,7 @@ public void shouldMakeRequestToBidderWhenStoredResponseDefinedButBidderCreatesMo // then verify(httpClient, times(2)).request(any(), anyString(), any(), any(byte[].class), anyLong()); - verify(bidRejectionTracker, never()).reject(any(Rejected.class)); + verify(bidRejectionTracker, never()).reject(any(Rejection.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -303,7 +303,7 @@ public void shouldSendPopulatedGetRequestWithoutBody() { // then verify(httpClient).request(any(), anyString(), any(), (byte[]) isNull(), anyLong()); - verify(bidRejectionTracker, never()).reject(any(Rejected.class)); + verify(bidRejectionTracker, never()).reject(any(Rejection.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -339,7 +339,7 @@ public void shouldSendMultipleRequests() throws JsonProcessingException { // then verify(httpClient, times(2)).request(any(), anyString(), any(), any(byte[].class), anyLong()); - verify(bidRejectionTracker, never()).reject(any(Rejected.class)); + verify(bidRejectionTracker, never()).reject(any(Rejection.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -372,7 +372,7 @@ public void shouldReturnBidsCreatedByBidder() { // then assertThat(bidderSeatBid.getBids()).hasSameElementsAs(bids); - verify(bidRejectionTracker, never()).reject(any(Rejected.class)); + verify(bidRejectionTracker, never()).reject(any(Rejection.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -405,7 +405,7 @@ public void shouldReturnBidsCreatedByMakeBids() { // then assertThat(bidderSeatBid.getBids()).hasSameElementsAs(bids); - verify(bidRejectionTracker, never()).reject(any(Rejected.class)); + verify(bidRejectionTracker, never()).reject(any(Rejection.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -447,7 +447,7 @@ public void shouldReturnFledgeCreatedByBidder() { assertThat(bidderSeatBid.getBids()).hasSameElementsAs(bids); assertThat(bidderSeatBid.getFledgeAuctionConfigs()).hasSameElementsAs(fledgeAuctionConfigs); - verify(bidRejectionTracker, never()).reject(any(Rejected.class)); + verify(bidRejectionTracker, never()).reject(any(Rejection.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -494,7 +494,7 @@ public void shouldReturnExtIgiCreatedByBidder() { assertThat(bidderSeatBid.getBids()).hasSameElementsAs(bids); assertThat(bidderSeatBid.getIgi()).containsExactlyElementsOf(igi); - verify(bidRejectionTracker, never()).reject(any(Rejected.class)); + verify(bidRejectionTracker, never()).reject(any(Rejection.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -530,7 +530,7 @@ public void shouldCompressRequestBodyIfContentEncodingHeaderIsGzip() { verify(httpClient).request(any(), anyString(), any(), actualRequestBody.capture(), anyLong()); assertThat(actualRequestBody.getValue()).isNotSameAs(EMPTY_BYTE_BODY); - verify(bidRejectionTracker, never()).reject(any(Rejected.class)); + verify(bidRejectionTracker, never()).reject(any(Rejection.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -628,7 +628,7 @@ public void processBids(List bids) { assertThat(bidderSeatBid.getBids()).containsOnly(bidderBidDeal1, bidderBidDeal2); - verify(bidRejectionTracker, never()).reject(any(Rejected.class)); + verify(bidRejectionTracker, never()).reject(any(Rejection.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -677,7 +677,7 @@ public void shouldFinishWhenAllDealRequestsAreFinishedAndNoDealsProvided() { assertThat(bidderSeatBid.getBids()).contains(bidderBid, bidderBid, bidderBid, bidderBid); - verify(bidRejectionTracker, never()).reject(any(Rejected.class)); + verify(bidRejectionTracker, never()).reject(any(Rejection.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -744,7 +744,7 @@ public void shouldReturnFullDebugInfoIfDebugEnabled() throws JsonProcessingExcep .status(200) .build()); - verify(bidRejectionTracker, never()).reject(any(Rejected.class)); + verify(bidRejectionTracker, never()).reject(any(Rejection.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -854,7 +854,7 @@ public void shouldNotReturnSensitiveHeadersInFullDebugInfo() .extracting(ExtHttpCall::getRequestheaders) .containsExactly(singletonMap("headerKey", singletonList("headerValue"))); - verify(bidRejectionTracker, never()).reject(any(Rejected.class)); + verify(bidRejectionTracker, never()).reject(any(Rejection.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } @@ -1239,7 +1239,7 @@ public void shouldNotMakeBidsIfResponseStatusIs204() { verify(bidder, never()).makeBidderResponse(any(), any()); verify(bidder, never()).makeBids(any(), any()); - verify(bidRejectionTracker, never()).reject(any(Rejected.class)); + verify(bidRejectionTracker, never()).reject(any(Rejection.class)); verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } diff --git a/src/test/java/org/prebid/server/floors/BasicPriceFloorEnforcerTest.java b/src/test/java/org/prebid/server/floors/BasicPriceFloorEnforcerTest.java index b102c10d855..9b5a08bc41b 100644 --- a/src/test/java/org/prebid/server/floors/BasicPriceFloorEnforcerTest.java +++ b/src/test/java/org/prebid/server/floors/BasicPriceFloorEnforcerTest.java @@ -13,7 +13,7 @@ import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.model.BidderRequest; import org.prebid.server.auction.model.BidderResponse; -import org.prebid.server.auction.model.RejectedBid; +import org.prebid.server.auction.model.BidRejection; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderError; import org.prebid.server.bidder.model.BidderSeatBid; @@ -338,7 +338,7 @@ public void shouldRejectBidsHavingPriceBelowFloor() { // then final BidderBid rejectedBid = BidderBid.of( Bid.builder().id("bidId1").impid("impId1").price(BigDecimal.ONE).build(), null, null); - verify(rejectionTracker).reject(RejectedBid.of(rejectedBid, BidRejectionReason.RESPONSE_REJECTED_BELOW_FLOOR)); + verify(rejectionTracker).reject(BidRejection.of(rejectedBid, BidRejectionReason.RESPONSE_REJECTED_BELOW_FLOOR)); assertThat(singleton(result)) .extracting(AuctionParticipation::getBidderResponse) .extracting(BidderResponse::getSeatBid) diff --git a/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java b/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java index aaca20e7dd2..24210ae6b72 100644 --- a/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java +++ b/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java @@ -29,8 +29,8 @@ import org.prebid.server.auction.model.BidRejectionTracker; import org.prebid.server.auction.model.BidderRequest; import org.prebid.server.auction.model.BidderResponse; -import org.prebid.server.auction.model.RejectedBid; -import org.prebid.server.auction.model.RejectedImp; +import org.prebid.server.auction.model.BidRejection; +import org.prebid.server.auction.model.ImpRejection; import org.prebid.server.auction.model.debug.DebugContext; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderSeatBid; @@ -1727,10 +1727,10 @@ public void shouldExecuteRawAuctionRequestHooksWithAllRejectionsPopulated(VertxT immediateHook(InvocationResultUtils.succeeded( payload -> AuctionRequestPayloadImpl.of(payload.bidRequest().toBuilder().at(1).build()), Map.of("bidderA", List.of( - RejectedImp.of("impId1", REQUEST_BLOCKED_OPTIMIZED), - RejectedImp.of("impId3", REQUEST_BLOCKED_GENERAL)), + ImpRejection.of("impId1", REQUEST_BLOCKED_OPTIMIZED), + ImpRejection.of("impId3", REQUEST_BLOCKED_GENERAL)), "bidderC", List.of( - RejectedImp.of("impId4", REQUEST_BLOCKED_GENERAL)))))); + ImpRejection.of("impId4", REQUEST_BLOCKED_GENERAL)))))); givenRawAuctionRequestHook( "module-alpha", @@ -1738,10 +1738,10 @@ public void shouldExecuteRawAuctionRequestHooksWithAllRejectionsPopulated(VertxT immediateHook(InvocationResultUtils.succeeded( payload -> AuctionRequestPayloadImpl.of(payload.bidRequest().toBuilder().id("id").build()), Map.of("bidderB", List.of( - RejectedImp.of("impId2", REQUEST_BLOCKED_PRIVACY), - RejectedImp.of("impId3", REQUEST_BLOCKED_GENERAL)), + ImpRejection.of("impId2", REQUEST_BLOCKED_PRIVACY), + ImpRejection.of("impId3", REQUEST_BLOCKED_GENERAL)), "bidderC", List.of( - RejectedImp.of("impId4", REQUEST_BLOCKED_GENERAL)))))); + ImpRejection.of("impId4", REQUEST_BLOCKED_GENERAL)))))); givenRawAuctionRequestHook( "module-beta", @@ -1749,10 +1749,10 @@ public void shouldExecuteRawAuctionRequestHooksWithAllRejectionsPopulated(VertxT immediateHook(InvocationResultUtils.succeeded( payload -> AuctionRequestPayloadImpl.of(payload.bidRequest().toBuilder().test(1).build()), Map.of("bidderA", List.of( - RejectedImp.of("impId1", REQUEST_BLOCKED_UNSUPPORTED_MEDIA_TYPE), - RejectedImp.of("impId3", REQUEST_BLOCKED_GENERAL)), + ImpRejection.of("impId1", REQUEST_BLOCKED_UNSUPPORTED_MEDIA_TYPE), + ImpRejection.of("impId3", REQUEST_BLOCKED_GENERAL)), "bidderC", List.of( - RejectedImp.of("impId4", REQUEST_BLOCKED_GENERAL)))))); + ImpRejection.of("impId4", REQUEST_BLOCKED_GENERAL)))))); givenRawAuctionRequestHook( "module-beta", @@ -1760,10 +1760,10 @@ public void shouldExecuteRawAuctionRequestHooksWithAllRejectionsPopulated(VertxT immediateHook(InvocationResultUtils.succeeded( payload -> AuctionRequestPayloadImpl.of(payload.bidRequest().toBuilder().tmax(1000L).build()), Map.of("bidderB", List.of( - RejectedImp.of("impId2", REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY), - RejectedImp.of("impId3", REQUEST_BLOCKED_GENERAL)), + ImpRejection.of("impId2", REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY), + ImpRejection.of("impId3", REQUEST_BLOCKED_GENERAL)), "bidderC", List.of( - RejectedImp.of("impId4", REQUEST_BLOCKED_GENERAL)))))); + ImpRejection.of("impId4", REQUEST_BLOCKED_GENERAL)))))); final HookStageExecutor executor = createExecutor( executionPlan(singletonMap( @@ -1811,24 +1811,24 @@ public void shouldExecuteRawAuctionRequestHooksWithAllRejectionsPopulated(VertxT assertThat(bidRejectionTrackers.keySet()).containsOnly("bidderA", "bidderB", "bidderC"); assertThat(bidRejectionTrackers.get("bidderA").getAllRejected()).containsOnly( entry("impId1", List.of( - RejectedImp.of("bidderA", "impId1", REQUEST_BLOCKED_OPTIMIZED), - RejectedImp.of("bidderA", "impId1", REQUEST_BLOCKED_UNSUPPORTED_MEDIA_TYPE))), + ImpRejection.of("bidderA", "impId1", REQUEST_BLOCKED_OPTIMIZED), + ImpRejection.of("bidderA", "impId1", REQUEST_BLOCKED_UNSUPPORTED_MEDIA_TYPE))), entry("impId3", List.of( - RejectedImp.of("bidderA", "impId3", REQUEST_BLOCKED_GENERAL), - RejectedImp.of("bidderA", "impId3", REQUEST_BLOCKED_GENERAL)))); + ImpRejection.of("bidderA", "impId3", REQUEST_BLOCKED_GENERAL), + ImpRejection.of("bidderA", "impId3", REQUEST_BLOCKED_GENERAL)))); assertThat(bidRejectionTrackers.get("bidderB").getAllRejected()).containsOnly( entry("impId2", List.of( - RejectedImp.of("bidderB", "impId2", REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY), - RejectedImp.of("bidderB", "impId2", REQUEST_BLOCKED_PRIVACY))), + ImpRejection.of("bidderB", "impId2", REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY), + ImpRejection.of("bidderB", "impId2", REQUEST_BLOCKED_PRIVACY))), entry("impId3", List.of( - RejectedImp.of("bidderB", "impId3", REQUEST_BLOCKED_GENERAL), - RejectedImp.of("bidderB", "impId3", REQUEST_BLOCKED_GENERAL)))); + ImpRejection.of("bidderB", "impId3", REQUEST_BLOCKED_GENERAL), + ImpRejection.of("bidderB", "impId3", REQUEST_BLOCKED_GENERAL)))); assertThat(bidRejectionTrackers.get("bidderC").getAllRejected()).containsOnly( entry("impId4", List.of( - RejectedImp.of("bidderC", "impId4", REQUEST_BLOCKED_GENERAL), - RejectedImp.of("bidderC", "impId4", REQUEST_BLOCKED_GENERAL), - RejectedImp.of("bidderC", "impId4", REQUEST_BLOCKED_GENERAL), - RejectedImp.of("bidderC", "impId4", REQUEST_BLOCKED_GENERAL)))); + ImpRejection.of("bidderC", "impId4", REQUEST_BLOCKED_GENERAL), + ImpRejection.of("bidderC", "impId4", REQUEST_BLOCKED_GENERAL), + ImpRejection.of("bidderC", "impId4", REQUEST_BLOCKED_GENERAL), + ImpRejection.of("bidderC", "impId4", REQUEST_BLOCKED_GENERAL)))); } @Test @@ -2907,12 +2907,12 @@ public void shouldExecuteAllProcessedBidResponsesHooksRejectionAllIgnoringUnknow || bidderBid.equals(bidderBBid4))) .toList()), Map.of("bidderA", List.of( - RejectedBid.of(bidderABid1, REQUEST_BLOCKED_OPTIMIZED), - RejectedBid.of(bidderABid2, REQUEST_BLOCKED_GENERAL)), + BidRejection.of(bidderABid1, REQUEST_BLOCKED_OPTIMIZED), + BidRejection.of(bidderABid2, REQUEST_BLOCKED_GENERAL)), "bidderC", List.of( - RejectedBid.of(bidderCBid5, REQUEST_BLOCKED_GENERAL)), + BidRejection.of(bidderCBid5, REQUEST_BLOCKED_GENERAL)), "bidderD", List.of( - RejectedBid.of(bidderDBid6, REQUEST_BLOCKED_GENERAL)))))); + BidRejection.of(bidderDBid6, REQUEST_BLOCKED_GENERAL)))))); givenAllProcessedBidderResponsesHook( "module-alpha", @@ -2924,12 +2924,12 @@ public void shouldExecuteAllProcessedBidResponsesHooksRejectionAllIgnoringUnknow || bidderBid.equals(bidderABid2))) .toList()), Map.of("bidderB", List.of( - RejectedBid.of(bidderBBid3, REQUEST_BLOCKED_PRIVACY), - RejectedBid.of(bidderBBid4, REQUEST_BLOCKED_GENERAL)), + BidRejection.of(bidderBBid3, REQUEST_BLOCKED_PRIVACY), + BidRejection.of(bidderBBid4, REQUEST_BLOCKED_GENERAL)), "bidderC", List.of( - RejectedBid.of(bidderCBid5, REQUEST_BLOCKED_GENERAL)), + BidRejection.of(bidderCBid5, REQUEST_BLOCKED_GENERAL)), "bidderD", List.of( - RejectedBid.of(bidderDBid6, REQUEST_BLOCKED_GENERAL)))))); + BidRejection.of(bidderDBid6, REQUEST_BLOCKED_GENERAL)))))); givenAllProcessedBidderResponsesHook( "module-beta", @@ -2941,12 +2941,12 @@ public void shouldExecuteAllProcessedBidResponsesHooksRejectionAllIgnoringUnknow || bidderBid.equals(bidderBBid4))) .toList()), Map.of("bidderA", List.of( - RejectedBid.of(bidderABid1, REQUEST_BLOCKED_UNSUPPORTED_MEDIA_TYPE), - RejectedBid.of(bidderABid2, REQUEST_BLOCKED_GENERAL)), + BidRejection.of(bidderABid1, REQUEST_BLOCKED_UNSUPPORTED_MEDIA_TYPE), + BidRejection.of(bidderABid2, REQUEST_BLOCKED_GENERAL)), "bidderC", List.of( - RejectedBid.of(bidderCBid5, REQUEST_BLOCKED_GENERAL)), + BidRejection.of(bidderCBid5, REQUEST_BLOCKED_GENERAL)), "bidderD", List.of( - RejectedBid.of(bidderDBid6, REQUEST_BLOCKED_GENERAL)))))); + BidRejection.of(bidderDBid6, REQUEST_BLOCKED_GENERAL)))))); givenAllProcessedBidderResponsesHook( "module-beta", @@ -2958,12 +2958,12 @@ public void shouldExecuteAllProcessedBidResponsesHooksRejectionAllIgnoringUnknow || bidderBid.equals(bidderABid2))) .toList()), Map.of("bidderB", List.of( - RejectedBid.of(bidderBBid3, REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY), - RejectedBid.of(bidderBBid4, REQUEST_BLOCKED_GENERAL)), + BidRejection.of(bidderBBid3, REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY), + BidRejection.of(bidderBBid4, REQUEST_BLOCKED_GENERAL)), "bidderC", List.of( - RejectedBid.of(bidderCBid5, REQUEST_BLOCKED_GENERAL)), + BidRejection.of(bidderCBid5, REQUEST_BLOCKED_GENERAL)), "bidderD", List.of( - RejectedBid.of(bidderDBid6, REQUEST_BLOCKED_GENERAL)))))); + BidRejection.of(bidderDBid6, REQUEST_BLOCKED_GENERAL)))))); final HookStageExecutor executor = createExecutor( executionPlan(singletonMap( @@ -3011,20 +3011,20 @@ public void shouldExecuteAllProcessedBidResponsesHooksRejectionAllIgnoringUnknow assertThat(bidRejectionTrackers.keySet()).containsOnly("bidderA", "bidderB", "bidderC"); assertThat(bidRejectionTrackers.get("bidderA").getAllRejected().get("impId")).containsOnly( - RejectedBid.of(bidderABid1, REQUEST_BLOCKED_OPTIMIZED), - RejectedBid.of(bidderABid1, REQUEST_BLOCKED_UNSUPPORTED_MEDIA_TYPE), - RejectedBid.of(bidderABid2, REQUEST_BLOCKED_GENERAL), - RejectedBid.of(bidderABid2, REQUEST_BLOCKED_GENERAL)); + BidRejection.of(bidderABid1, REQUEST_BLOCKED_OPTIMIZED), + BidRejection.of(bidderABid1, REQUEST_BLOCKED_UNSUPPORTED_MEDIA_TYPE), + BidRejection.of(bidderABid2, REQUEST_BLOCKED_GENERAL), + BidRejection.of(bidderABid2, REQUEST_BLOCKED_GENERAL)); assertThat(bidRejectionTrackers.get("bidderB").getAllRejected().get("impId")).containsOnly( - RejectedBid.of(bidderBBid3, REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY), - RejectedBid.of(bidderBBid3, REQUEST_BLOCKED_PRIVACY), - RejectedBid.of(bidderBBid4, REQUEST_BLOCKED_GENERAL), - RejectedBid.of(bidderBBid4, REQUEST_BLOCKED_GENERAL)); + BidRejection.of(bidderBBid3, REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY), + BidRejection.of(bidderBBid3, REQUEST_BLOCKED_PRIVACY), + BidRejection.of(bidderBBid4, REQUEST_BLOCKED_GENERAL), + BidRejection.of(bidderBBid4, REQUEST_BLOCKED_GENERAL)); assertThat(bidRejectionTrackers.get("bidderC").getAllRejected().get("impId")).containsOnly( - RejectedBid.of(bidderCBid5, REQUEST_BLOCKED_GENERAL), - RejectedBid.of(bidderCBid5, REQUEST_BLOCKED_GENERAL), - RejectedBid.of(bidderCBid5, REQUEST_BLOCKED_GENERAL), - RejectedBid.of(bidderCBid5, REQUEST_BLOCKED_GENERAL)); + BidRejection.of(bidderCBid5, REQUEST_BLOCKED_GENERAL), + BidRejection.of(bidderCBid5, REQUEST_BLOCKED_GENERAL), + BidRejection.of(bidderCBid5, REQUEST_BLOCKED_GENERAL), + BidRejection.of(bidderCBid5, REQUEST_BLOCKED_GENERAL)); } @Test diff --git a/src/test/java/org/prebid/server/hooks/v1/InvocationResultUtils.java b/src/test/java/org/prebid/server/hooks/v1/InvocationResultUtils.java index c6690328830..6518d1dced1 100644 --- a/src/test/java/org/prebid/server/hooks/v1/InvocationResultUtils.java +++ b/src/test/java/org/prebid/server/hooks/v1/InvocationResultUtils.java @@ -1,6 +1,6 @@ package org.prebid.server.hooks.v1; -import org.prebid.server.auction.model.Rejected; +import org.prebid.server.auction.model.Rejection; import org.prebid.server.hooks.execution.v1.InvocationResultImpl; import java.util.List; @@ -21,7 +21,7 @@ public static InvocationResult succeeded(PayloadUpdate InvocationResult succeeded(PayloadUpdate payloadUpdate, - Map> rejections) { + Map> rejections) { return InvocationResultImpl.builder() .status(InvocationStatus.success) .action(InvocationAction.update) diff --git a/src/test/java/org/prebid/server/validation/ResponseBidValidatorTest.java b/src/test/java/org/prebid/server/validation/ResponseBidValidatorTest.java index 6863552d9ab..df429612cdc 100644 --- a/src/test/java/org/prebid/server/validation/ResponseBidValidatorTest.java +++ b/src/test/java/org/prebid/server/validation/ResponseBidValidatorTest.java @@ -15,7 +15,7 @@ import org.prebid.server.auction.aliases.BidderAliases; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.BidRejectionTracker; -import org.prebid.server.auction.model.RejectedBid; +import org.prebid.server.auction.model.BidRejection; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.metric.MetricName; import org.prebid.server.metric.Metrics; @@ -160,7 +160,7 @@ public void validateShouldFailIfBannerBidHasNoWidthAndHeight() { creative size validation for bid bidId1, account=account, referrer=unknown, \ max imp size='100x200', bid response size='nullxnull'"""); verify(bidRejectionTracker) - .reject(RejectedBid.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_SIZE_NOT_ALLOWED)); + .reject(BidRejection.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_SIZE_NOT_ALLOWED)); } @Test @@ -177,7 +177,7 @@ public void validateShouldFailIfBannerBidWidthIsGreaterThanImposedByImp() { creative size validation for bid bidId1, account=account, referrer=unknown, \ max imp size='100x200', bid response size='150x150'"""); verify(bidRejectionTracker) - .reject(RejectedBid.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_SIZE_NOT_ALLOWED)); + .reject(BidRejection.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_SIZE_NOT_ALLOWED)); } @Test @@ -194,7 +194,7 @@ public void validateShouldFailIfBannerBidHeightIsGreaterThanImposedByImp() { creative size validation for bid bidId1, account=account, referrer=unknown, \ max imp size='100x200', bid response size='50x250'"""); verify(bidRejectionTracker) - .reject(RejectedBid.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_SIZE_NOT_ALLOWED)); + .reject(BidRejection.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_SIZE_NOT_ALLOWED)); } @Test @@ -278,7 +278,7 @@ public void validateShouldFailIfBidHasInsecureMarkerInCreativeInSecureContext() secure creative validation for bid bidId1, account=account, referrer=unknown, \ adm=http://site.com/creative.jpg"""); verify(bidRejectionTracker) - .reject(RejectedBid.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_NOT_SECURE)); + .reject(BidRejection.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_NOT_SECURE)); } @Test @@ -298,7 +298,7 @@ public void validateShouldFailIfBidHasInsecureEncodedMarkerInCreativeInSecureCon secure creative validation for bid bidId1, account=account, referrer=unknown, \ adm=http%3A//site.com/creative.jpg"""); verify(bidRejectionTracker) - .reject(RejectedBid.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_NOT_SECURE)); + .reject(BidRejection.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_NOT_SECURE)); } @Test @@ -318,7 +318,7 @@ public void validateShouldFailIfBidHasNoSecureMarkersInCreativeInSecureContext() secure creative validation for bid bidId1, account=account, referrer=unknown, \ adm=//site.com/creative.jpg"""); verify(bidRejectionTracker) - .reject(RejectedBid.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_NOT_SECURE)); + .reject(BidRejection.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_NOT_SECURE)); } @Test @@ -482,7 +482,7 @@ public void validateShouldIncrementSizeValidationErrMetrics() { // then verify(metrics).updateSizeValidationMetrics(BIDDER_NAME, ACCOUNT_ID, MetricName.err); verify(bidRejectionTracker) - .reject(RejectedBid.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_SIZE_NOT_ALLOWED)); + .reject(BidRejection.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_SIZE_NOT_ALLOWED)); } @Test @@ -512,7 +512,7 @@ public void validateShouldIncrementSecureValidationErrMetrics() { // then verify(metrics).updateSecureValidationMetrics(BIDDER_NAME, ACCOUNT_ID, MetricName.err); verify(bidRejectionTracker) - .reject(RejectedBid.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_NOT_SECURE)); + .reject(BidRejection.of(givenBid, RESPONSE_REJECTED_INVALID_CREATIVE_NOT_SECURE)); } @Test @@ -566,7 +566,7 @@ public void validateShouldFailOnSeatValidationWhenSeatIsNotAllowed() { assertThat(result.getErrors()) .containsOnly("invalid bidder code seat was set by the adapter bidder for the account account"); verify(metrics).updateSeatValidationMetrics(BIDDER_NAME); - verify(bidRejectionTracker).reject(RejectedBid.of(givenBid, RESPONSE_REJECTED_GENERAL)); + verify(bidRejectionTracker).reject(BidRejection.of(givenBid, RESPONSE_REJECTED_GENERAL)); } @Test