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 index 67d42d47bc2..47da698c627 100644 --- 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 @@ -7,7 +7,7 @@ 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.Partner; +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; @@ -22,13 +22,15 @@ 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( - Partner partner, + GreenbidsConfig greenbidsConfig, BidRequest bidRequest, Map> impsBiddersFilterMap) { final String greenbidsId = UUID.randomUUID().toString(); - final boolean isExploration = isExploration(partner, greenbidsId); + final boolean isExploration = isExploration(greenbidsConfig, greenbidsId); final BidRequest updatedBidRequest = isExploration ? bidRequest @@ -49,10 +51,12 @@ public GreenbidsInvocationResult createGreenbidsInvocationResult( return GreenbidsInvocationResult.of(updatedBidRequest, invocationAction, analyticsResult); } - private Boolean isExploration(Partner partner, String greenbidsId) { + 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 < partner.getExplorationRate() * RANGE_16_BIT_INTEGER_DIVISION_BASIS; + return hashInt < explorationRate * RANGE_16_BIT_INTEGER_DIVISION_BASIS; } private List updateImps(BidRequest bidRequest, Map> impsBiddersFilterMap) { diff --git a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/OnnxModelRunnerWithThresholds.java b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/OnnxModelRunnerWithThresholds.java index adbc1e17b2c..eaa184f7574 100644 --- a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/OnnxModelRunnerWithThresholds.java +++ b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/core/OnnxModelRunnerWithThresholds.java @@ -1,7 +1,7 @@ package org.prebid.server.hooks.modules.greenbids.real.time.data.core; import io.vertx.core.Future; -import org.prebid.server.hooks.modules.greenbids.real.time.data.model.data.Partner; +import org.prebid.server.hooks.modules.greenbids.real.time.data.model.data.GreenbidsConfig; import java.util.Objects; @@ -18,14 +18,14 @@ public OnnxModelRunnerWithThresholds( this.thresholdCache = Objects.requireNonNull(thresholdCache); } - public Future retrieveOnnxModelRunner(Partner partner) { - final String onnxModelPath = "models_pbuid=" + partner.getPbuid() + ".onnx"; - return modelCache.get(onnxModelPath, partner.getPbuid()); + public Future retrieveOnnxModelRunner(GreenbidsConfig greenbidsConfig) { + final String onnxModelPath = "models_pbuid=" + greenbidsConfig.getPbuid() + ".onnx"; + return modelCache.get(onnxModelPath, greenbidsConfig.getPbuid()); } - public Future retrieveThreshold(Partner partner) { - final String thresholdJsonPath = "thresholds_pbuid=" + partner.getPbuid() + ".json"; - return thresholdCache.get(thresholdJsonPath, partner.getPbuid()) - .map(partner::getThreshold); + public Future retrieveThreshold(GreenbidsConfig greenbidsConfig) { + final String thresholdJsonPath = "thresholds_pbuid=" + greenbidsConfig.getPbuid() + ".json"; + return thresholdCache.get(thresholdJsonPath, greenbidsConfig.getPbuid()) + .map(greenbidsConfig::getThreshold); } } diff --git a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/model/data/Partner.java b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/model/data/GreenbidsConfig.java similarity index 73% rename from extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/model/data/Partner.java rename to extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/model/data/GreenbidsConfig.java index 2be7c1887e8..c857dd37e63 100644 --- a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/model/data/Partner.java +++ b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/model/data/GreenbidsConfig.java @@ -6,27 +6,31 @@ import java.util.Comparator; import java.util.List; +import java.util.Optional; import java.util.stream.IntStream; @Value(staticConstructor = "of") -public class Partner { +public class GreenbidsConfig { + + private static final double DEFAULT_TPR = 1.0; String pbuid; - @JsonProperty("targetTpr") + @JsonProperty("target-tpr") Double targetTpr; - @JsonProperty("explorationRate") + @JsonProperty("exploration-rate") Double explorationRate; public Double getThreshold(ThrottlingThresholds throttlingThresholds) { + final double safeTargetTpr = targetTpr != null ? targetTpr : DEFAULT_TPR; final List truePositiveRates = throttlingThresholds.getTpr(); final List thresholds = throttlingThresholds.getThresholds(); final int minSize = Math.min(truePositiveRates.size(), thresholds.size()); return IntStream.range(0, minSize) - .filter(i -> truePositiveRates.get(i) >= targetTpr) + .filter(i -> truePositiveRates.get(i) >= safeTargetTpr) .mapToObj(thresholds::get) .max(Comparator.naturalOrder()) .orElse(0.0); 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 3b677a78a18..5188b756ddc 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 @@ -21,7 +21,7 @@ import org.prebid.server.hooks.modules.greenbids.real.time.data.core.GreenbidsInvocationService; 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.Partner; +import org.prebid.server.hooks.modules.greenbids.real.time.data.model.data.GreenbidsConfig; import org.prebid.server.hooks.modules.greenbids.real.time.data.model.data.ThrottlingMessage; 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; @@ -35,6 +35,8 @@ 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.Collections; import java.util.List; @@ -44,10 +46,10 @@ public class GreenbidsRealTimeDataProcessedAuctionRequestHook implements ProcessedAuctionRequestHook { + private static final String BID_REQUEST_ANALYTICS_EXTENSION_NAME = "greenbids-rtd"; private static final String CODE = "greenbids-real-time-data-processed-auction-request"; private static final String ACTIVITY = "greenbids-filter"; private static final String SUCCESS_STATUS = "success"; - private static final String BID_REQUEST_ANALYTICS_EXTENSION_NAME = "greenbids-rtd"; private final ObjectMapper mapper; private final FilterService filterService; @@ -75,33 +77,35 @@ public Future> call( final AuctionContext auctionContext = invocationContext.auctionContext(); final BidRequest bidRequest = auctionContext.getBidRequest(); - final Partner partner = parseBidRequestExt(bidRequest); + final GreenbidsConfig greenbidsConfig = Optional.ofNullable(parseBidRequestExt(auctionContext)) + .orElseGet(() -> toGreenbidsConfig(invocationContext.accountConfig())); - if (partner == null) { - return Future.succeededFuture(toInvocationResult( - bidRequest, null, InvocationAction.no_action)); + if (greenbidsConfig == null) { + return Future.failedFuture( + new PreBidException("Greenbids config is null; cannot proceed.")); } return Future.all( - onnxModelRunnerWithThresholds.retrieveOnnxModelRunner(partner), - onnxModelRunnerWithThresholds.retrieveThreshold(partner)) + onnxModelRunnerWithThresholds.retrieveOnnxModelRunner(greenbidsConfig), + onnxModelRunnerWithThresholds.retrieveThreshold(greenbidsConfig)) .compose(compositeFuture -> toInvocationResult( bidRequest, - partner, + greenbidsConfig, compositeFuture.resultAt(0), compositeFuture.resultAt(1))) .recover(throwable -> Future.succeededFuture(toInvocationResult( bidRequest, null, InvocationAction.no_action))); } - private Partner parseBidRequestExt(BidRequest bidRequest) { - return Optional.ofNullable(bidRequest) + private GreenbidsConfig parseBidRequestExt(AuctionContext auctionContext) { + return Optional.ofNullable(auctionContext) + .map(AuctionContext::getBidRequest) .map(BidRequest::getExt) .map(ExtRequest::getPrebid) .map(ExtRequestPrebid::getAnalytics) .filter(this::isNotEmptyObjectNode) .map(analytics -> (ObjectNode) analytics.get(BID_REQUEST_ANALYTICS_EXTENSION_NAME)) - .map(this::toPartner) + .map(this::toGreenbidsConfig) .orElse(null); } @@ -109,9 +113,9 @@ private boolean isNotEmptyObjectNode(JsonNode analytics) { return analytics != null && analytics.isObject() && !analytics.isEmpty(); } - private Partner toPartner(ObjectNode adapterNode) { + private GreenbidsConfig toGreenbidsConfig(ObjectNode greenbidsConfigNode) { try { - return mapper.treeToValue(adapterNode, Partner.class); + return mapper.treeToValue(greenbidsConfigNode, GreenbidsConfig.class); } catch (JsonProcessingException e) { return null; } @@ -119,7 +123,7 @@ private Partner toPartner(ObjectNode adapterNode) { private Future> toInvocationResult( BidRequest bidRequest, - Partner partner, + GreenbidsConfig greenbidsConfig, OnnxModelRunner onnxModelRunner, Double threshold) { @@ -138,7 +142,7 @@ private Future> toInvocationResult( } final GreenbidsInvocationResult greenbidsInvocationResult = greenbidsInvocationService - .createGreenbidsInvocationResult(partner, bidRequest, impsBiddersFilterMap); + .createGreenbidsInvocationResult(greenbidsConfig, bidRequest, impsBiddersFilterMap); return Future.succeededFuture(toInvocationResult( greenbidsInvocationResult.getUpdatedBidRequest(), 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 fafc759ca11..c56b19e14c2 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 @@ -74,7 +74,7 @@ public void extractThrottlingMessagesFromBidRequestShouldReturnValidThrottlingMe .banner(banner) .build(); final Device device = givenDevice(identity()); - final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device, null); + final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device); final CountryResponse countryResponse = mock(CountryResponse.class); @@ -115,7 +115,7 @@ public void extractThrottlingMessagesFromBidRequestShouldReturnValidThrottlingMe .banner(banner) .build(); final Device device = givenDevice(identity(), "FRA"); - final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device, null); + final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device); final ZonedDateTime timestamp = ZonedDateTime.now(ZoneId.of("UTC")); final Integer expectedHourBucket = timestamp.getHour(); @@ -152,7 +152,7 @@ public void extractThrottlingMessagesFromBidRequestShouldHandleMissingIp() { .banner(banner) .build(); final Device device = givenDeviceWithoutIp(identity()); - final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device, null); + final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device); final ZonedDateTime timestamp = ZonedDateTime.now(ZoneId.of("UTC")); final Integer expectedHourBucket = timestamp.getHour(); @@ -188,7 +188,7 @@ public void extractThrottlingMessagesFromBidRequestShouldThrowPreBidExceptionWhe .banner(banner) .build(); final Device device = givenDevice(identity()); - final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device, null); + final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device); when(databaseReader.country(any(InetAddress.class))).thenThrow(new GeoIp2Exception("GeoIP failure")); 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 index 1bf0f5409e4..8af8b6e2a03 100644 --- 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 @@ -10,7 +10,7 @@ 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.Partner; +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; @@ -45,13 +45,13 @@ public void createGreenbidsInvocationResultShouldReturnUpdateBidRequestWhenNotEx .banner(banner) .build(); final Device device = givenDevice(identity()); - final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device, null); + final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device); final Map> impsBiddersFilterMap = givenImpsBiddersFilterMap(); - final Partner partner = givenPartner(0.0); + final GreenbidsConfig greenbidsConfig = givenPartner(0.0); // when final GreenbidsInvocationResult result = target.createGreenbidsInvocationResult( - partner, bidRequest, impsBiddersFilterMap); + greenbidsConfig, bidRequest, impsBiddersFilterMap); // then final JsonNode updatedBidRequestExtPrebidBidders = result.getUpdatedBidRequest().getImp().getFirst().getExt() @@ -82,13 +82,13 @@ public void createGreenbidsInvocationResultShouldReturnNoActionWhenExploration() .banner(banner) .build(); final Device device = givenDevice(identity()); - final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device, null); + final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device); final Map> impsBiddersFilterMap = givenImpsBiddersFilterMap(); - final Partner partner = givenPartner(1.0); + final GreenbidsConfig greenbidsConfig = givenPartner(1.0); // when final GreenbidsInvocationResult result = target.createGreenbidsInvocationResult( - partner, bidRequest, impsBiddersFilterMap); + greenbidsConfig, bidRequest, impsBiddersFilterMap); // then final JsonNode updatedBidRequestExtPrebidBidders = result.getUpdatedBidRequest().getImp().getFirst().getExt() @@ -120,7 +120,7 @@ private Map> givenImpsBiddersFilterMap() { return impsBiddersFilterMap; } - private Partner givenPartner(Double explorationRate) { - return Partner.of("test-pbuid", 0.60, explorationRate); + 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/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 c66ba26d953..7cb8e902dff 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 @@ -12,6 +12,7 @@ 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; @@ -26,15 +27,41 @@ private TestBidRequestProvider() { } public static BidRequest givenBidRequest( UnaryOperator bidRequestCustomizer, List imps, - Device device, - ExtRequest extRequest) { + Device device) { return bidRequestCustomizer.apply(BidRequest.builder() .id("request") .imp(imps) .site(givenSite(site -> site)) - .device(device) - .ext(extRequest)).build(); + .device(device)).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 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) { 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 284741a1b68..07f13d519f4 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 @@ -52,8 +52,8 @@ import org.prebid.server.hooks.v1.auction.AuctionInvocationContext; import org.prebid.server.hooks.v1.auction.AuctionRequestPayload; import org.prebid.server.model.HttpRequestContext; -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.io.IOException; import java.net.InetAddress; @@ -72,8 +72,10 @@ import static org.mockito.Mockito.when; 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; 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; @@ -149,7 +151,8 @@ public void setUp() throws IOException, GeoIp2Exception { } @Test - public void callShouldExitEarlyWhenPartnerNotActivatedInBidRequest() { + public void callShouldFilterBiddersWhenPartnerActivatedInBidRequest() + throws IOException, OrtException { // given final Banner banner = givenBanner(); @@ -159,24 +162,57 @@ public void callShouldExitEarlyWhenPartnerNotActivatedInBidRequest() { .banner(banner) .build(); + final Double explorationRate = 0.0001; final Device device = givenDevice(identity()); - final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device, null); - final AuctionContext auctionContext = givenAuctionContext(bidRequest, context -> context); - final AuctionInvocationContext invocationContext = givenAuctionInvocationContext(auctionContext); + 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, false); // 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.no_action); - assertThat(result.analyticsTags()).isNull(); + 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 @@ -192,10 +228,10 @@ public void callShouldNotFilterBiddersAndReturnAnalyticsTagWhenExploration() thr final Double explorationRate = 1.0; final Device device = givenDevice(identity()); - final ExtRequest extRequest = givenExtRequest(explorationRate); - final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device, extRequest); + final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device); final AuctionContext auctionContext = givenAuctionContext(bidRequest, context -> context); - final AuctionInvocationContext invocationContext = givenAuctionInvocationContext(auctionContext); + final AuctionInvocationContext invocationContext = givenAuctionInvocationContext( + auctionContext, explorationRate); when(invocationContext.auctionContext()).thenReturn(auctionContext); when(modelCacheWithExpiration.getIfPresent("onnxModelRunner_test-pbuid")) .thenReturn(givenOnnxModelRunner()); @@ -246,17 +282,18 @@ public void callShouldFilterBiddersBasedOnModelWhenAnyFeatureNotAvailable() thro final Double explorationRate = 0.0001; final Device device = givenDeviceWithoutUserAgent(identity()); - final ExtRequest extRequest = givenExtRequest(explorationRate); - final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device, extRequest); + final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device); final AuctionContext auctionContext = givenAuctionContext(bidRequest, context -> context); - final AuctionInvocationContext invocationContext = givenAuctionInvocationContext(auctionContext); + 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, explorationRate, device); + final BidRequest expectedBidRequest = expectedUpdatedBidRequest( + request -> request, device, false); final AnalyticsResult expectedAnalyticsResult = expectedAnalyticsResult(false, false); // when @@ -306,10 +343,10 @@ public void callShouldFilterBiddersBasedOnModelResults() throws OrtException, IO final Double explorationRate = 0.0001; final Device device = givenDevice(identity()); - final ExtRequest extRequest = givenExtRequest(explorationRate); - final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device, extRequest); + final BidRequest bidRequest = givenBidRequest(request -> request, List.of(imp), device); final AuctionContext auctionContext = givenAuctionContext(bidRequest, context -> context); - final AuctionInvocationContext invocationContext = givenAuctionInvocationContext(auctionContext); + final AuctionInvocationContext invocationContext = givenAuctionInvocationContext( + auctionContext, explorationRate); when(invocationContext.auctionContext()).thenReturn(auctionContext); when(modelCacheWithExpiration.getIfPresent("onnxModelRunner_test-pbuid")) .thenReturn(givenOnnxModelRunner()); @@ -317,7 +354,7 @@ public void callShouldFilterBiddersBasedOnModelResults() throws OrtException, IO .thenReturn(givenThrottlingThresholds()); final BidRequest expectedBidRequest = expectedUpdatedBidRequest( - request -> request, explorationRate, device); + request -> request, device, false); final AnalyticsResult expectedAnalyticsResult = expectedAnalyticsResult(false, false); // when @@ -355,21 +392,6 @@ public void callShouldFilterBiddersBasedOnModelResults() throws OrtException, IO .isEqualTo(expectedBidRequest); } - static ExtRequest givenExtRequest(Double explorationRate) { - final ObjectNode greenbidsNode = TestBidRequestProvider.MAPPER.createObjectNode(); - greenbidsNode.put("pbuid", "test-pbuid"); - greenbidsNode.put("targetTpr", 0.60); - greenbidsNode.put("explorationRate", explorationRate); - - final ObjectNode analyticsNode = TestBidRequestProvider.MAPPER.createObjectNode(); - analyticsNode.set("greenbids-rtd", greenbidsNode); - - return ExtRequest.of(ExtRequestPrebid - .builder() - .analytics(analyticsNode) - .build()); - } - private AuctionContext givenAuctionContext( BidRequest bidRequest, UnaryOperator auctionContextCustomizer) { @@ -381,12 +403,23 @@ private AuctionContext givenAuctionContext( return auctionContextCustomizer.apply(auctionContextBuilder).build(); } - private AuctionInvocationContext givenAuctionInvocationContext(AuctionContext auctionContext) { + 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 ObjectNode givenAccountConfig(Double explorationRate) { + final ObjectNode greenbidsNode = TestBidRequestProvider.MAPPER.createObjectNode(); + greenbidsNode.put("enabled", true); + greenbidsNode.put("pbuid", "test-pbuid"); + greenbidsNode.put("target-tpr", 0.60); + 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")); @@ -403,8 +436,8 @@ private ThrottlingThresholds givenThrottlingThresholds() throws IOException { private BidRequest expectedUpdatedBidRequest( UnaryOperator bidRequestCustomizer, - Double explorationRate, - Device device) { + Device device, + Boolean isExtRequest) { final Banner banner = givenBanner(); @@ -422,12 +455,17 @@ private BidRequest expectedUpdatedBidRequest( .banner(banner) .build(); - return bidRequestCustomizer.apply(BidRequest.builder() + final BidRequest.BidRequestBuilder bidRequestBuilder = BidRequest.builder() .id("request") .imp(List.of(imp)) .site(givenSite(site -> site)) - .device(device) - .ext(givenExtRequest(explorationRate))).build(); + .device(device); + + if (isExtRequest) { + bidRequestBuilder.ext(givenExtRequest()); + } + + return bidRequestCustomizer.apply(bidRequestBuilder).build(); } private AnalyticsResult expectedAnalyticsResult(Boolean isExploration, Boolean isKeptInAuction) { 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 daf51c9d74f..d4b0a4f8711 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 @@ -17,6 +17,7 @@ import io.vertx.core.Future; import io.vertx.core.MultiMap; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.prebid.server.analytics.AnalyticsReporter; import org.prebid.server.analytics.model.AmpEvent; import org.prebid.server.analytics.model.AuctionEvent; @@ -26,7 +27,7 @@ import org.prebid.server.analytics.reporter.greenbids.model.GreenbidsAdUnit; import org.prebid.server.analytics.reporter.greenbids.model.GreenbidsAnalyticsProperties; import org.prebid.server.analytics.reporter.greenbids.model.GreenbidsBid; -import org.prebid.server.analytics.reporter.greenbids.model.GreenbidsPrebidExt; +import org.prebid.server.analytics.reporter.greenbids.model.GreenbidsConfig; import org.prebid.server.analytics.reporter.greenbids.model.GreenbidsSource; import org.prebid.server.analytics.reporter.greenbids.model.GreenbidsUnifiedCode; import org.prebid.server.analytics.reporter.greenbids.model.MediaTypes; @@ -54,6 +55,8 @@ import org.prebid.server.proto.openrtb.ext.request.ExtStoredRequest; import org.prebid.server.proto.openrtb.ext.response.seatnonbid.NonBid; import org.prebid.server.proto.openrtb.ext.response.seatnonbid.SeatNonBid; +import org.prebid.server.settings.model.Account; +import org.prebid.server.settings.model.AccountAnalyticsConfig; import org.prebid.server.util.HttpUtil; import org.prebid.server.version.PrebidVersionProvider; import org.prebid.server.vertx.httpclient.HttpClient; @@ -118,9 +121,10 @@ public Future processEvent(T event) { return Future.failedFuture(new PreBidException("Bid response or auction context cannot be null")); } - final GreenbidsPrebidExt greenbidsBidRequestExt = parseBidRequestExt(auctionContext.getBidRequest()); + final GreenbidsConfig greenbidsConfig = Optional.ofNullable(parseBidRequestExt(auctionContext)) + .orElseGet(() -> parseAccountConfig(auctionContext.getAccount())); - if (greenbidsBidRequestExt == null) { + if (greenbidsConfig == null) { return Future.succeededFuture(); } @@ -131,7 +135,9 @@ public Future processEvent(T event) { final String greenbidsId = greenbidsId(analyticsResultFromAnalyticsTag); - if (!isSampled(greenbidsBidRequestExt.getGreenbidsSampling(), greenbidsId)) { + final double samplingRate = resolveSamplingRate(greenbidsConfig); + + if (!isSampled(samplingRate, greenbidsId)) { return Future.succeededFuture(); } @@ -142,8 +148,9 @@ public Future processEvent(T event) { bidResponse, greenbidsId, billingId, - greenbidsBidRequestExt, - analyticsResultFromAnalyticsTag); + greenbidsConfig, + analyticsResultFromAnalyticsTag, + samplingRate); commonMessageJson = jacksonMapper.encodeToString(commonMessage); } catch (PreBidException e) { return Future.failedFuture(e); @@ -170,14 +177,15 @@ public Future processEvent(T event) { return responseFuture.compose(this::processAnalyticServerResponse); } - private GreenbidsPrebidExt parseBidRequestExt(BidRequest bidRequest) { - return Optional.ofNullable(bidRequest) + private GreenbidsConfig parseBidRequestExt(AuctionContext auctionContext) { + return Optional.ofNullable(auctionContext) + .map(AuctionContext::getBidRequest) .map(BidRequest::getExt) .map(ExtRequest::getPrebid) .map(ExtRequestPrebid::getAnalytics) .filter(this::isNotEmptyObjectNode) .map(analytics -> (ObjectNode) analytics.get(BID_REQUEST_ANALYTICS_EXTENSION_NAME)) - .map(this::toGreenbidsPrebidExt) + .map(this::toGreenbidsConfig) .orElse(null); } @@ -185,9 +193,18 @@ private boolean isNotEmptyObjectNode(JsonNode analytics) { return analytics != null && analytics.isObject() && !analytics.isEmpty(); } - private GreenbidsPrebidExt toGreenbidsPrebidExt(ObjectNode adapterNode) { + private GreenbidsConfig parseAccountConfig(Account account) { + return Optional.ofNullable(account) + .map(Account::getAnalytics) + .map(AccountAnalyticsConfig::getModules) + .map(analyticsModules -> analyticsModules.get(name())) + .map(this::toGreenbidsConfig) + .orElse(null); + } + + private GreenbidsConfig toGreenbidsConfig(ObjectNode adapterNode) { try { - return jacksonMapper.mapper().treeToValue(adapterNode, GreenbidsPrebidExt.class); + return jacksonMapper.mapper().treeToValue(adapterNode, GreenbidsConfig.class); } catch (JsonProcessingException e) { throw new PreBidException("Error decoding bid request analytics extension: " + e.getMessage(), e); } @@ -252,6 +269,16 @@ private String greenbidsId(Map analyticsResultFromAna .orElseGet(() -> UUID.randomUUID().toString()); } + private double resolveSamplingRate(GreenbidsConfig greenbidsConfig) { + final Double sampling = greenbidsConfig.getGreenbidsSampling(); + if (sampling == null) { + logger.warn("Warning: Sampling rate is not defined in request. Set sampling at {}", + greenbidsAnalyticsProperties.getDefaultSamplingRate()); + return greenbidsAnalyticsProperties.getDefaultSamplingRate(); + } + return sampling; + } + private Future processAnalyticServerResponse(HttpClientResponse response) { final int responseStatusCode = response.getStatusCode(); if (responseStatusCode >= 200 && responseStatusCode < 300) { @@ -261,12 +288,6 @@ private Future processAnalyticServerResponse(HttpClientResponse response) } private boolean isSampled(Double samplingRate, String greenbidsId) { - if (samplingRate == null) { - logger.warn("Warning: Sampling rate is not defined in request. Set sampling at " - + greenbidsAnalyticsProperties.getDefaultSamplingRate()); - return true; - } - if (samplingRate < 0 || samplingRate > 1) { logger.warn("Warning: Sampling rate must be between 0 and 1"); return true; @@ -291,8 +312,9 @@ private CommonMessage createBidMessage( BidResponse bidResponse, String greenbidsId, String billingId, - GreenbidsPrebidExt greenbidsImpExt, - Map analyticsResultFromAnalyticsTag) { + GreenbidsConfig greenbidsConfig, + Map analyticsResultFromAnalyticsTag, + Double samplingRate) { final Optional bidRequest = Optional.ofNullable(auctionContext.getBidRequest()); final List imps = bidRequest @@ -324,17 +346,16 @@ private CommonMessage createBidMessage( .map(Site::getPage) .orElse(null); - final Double greenbidsSamplingRate = Optional.ofNullable(greenbidsImpExt.getGreenbidsSampling()) - .orElse(greenbidsAnalyticsProperties.getDefaultSamplingRate()); + final String pbuid = Optional.ofNullable(greenbidsConfig.getPbuid()).orElse(StringUtils.EMPTY); return CommonMessage.builder() .version(greenbidsAnalyticsProperties.getAnalyticsServerVersion()) .auctionId(auctionId) .referrer(referrer) - .sampling(greenbidsSamplingRate) + .sampling(samplingRate) .prebidServer(prebidVersionProvider.getNameVersionRecord()) .greenbidsId(greenbidsId) - .pbuid(greenbidsImpExt.getPbuid()) + .pbuid(pbuid) .billingId(billingId) .adUnits(adUnitsWithBidResponses) .auctionElapsed(auctionElapsed) diff --git a/src/main/java/org/prebid/server/analytics/reporter/greenbids/model/GreenbidsPrebidExt.java b/src/main/java/org/prebid/server/analytics/reporter/greenbids/model/GreenbidsConfig.java similarity index 75% rename from src/main/java/org/prebid/server/analytics/reporter/greenbids/model/GreenbidsPrebidExt.java rename to src/main/java/org/prebid/server/analytics/reporter/greenbids/model/GreenbidsConfig.java index d5983205898..2820daea092 100644 --- a/src/main/java/org/prebid/server/analytics/reporter/greenbids/model/GreenbidsPrebidExt.java +++ b/src/main/java/org/prebid/server/analytics/reporter/greenbids/model/GreenbidsConfig.java @@ -4,10 +4,10 @@ import lombok.Value; @Value(staticConstructor = "of") -public class GreenbidsPrebidExt { +public class GreenbidsConfig { String pbuid; - @JsonProperty("greenbidsSampling") + @JsonProperty("greenbids-sampling") Double greenbidsSampling; } 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 3e5e8678c2c..82cae87406e 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 @@ -54,17 +54,8 @@ import org.prebid.server.model.HttpRequestContext; import org.prebid.server.proto.openrtb.ext.request.ExtRequest; import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid; -import org.prebid.server.proto.openrtb.ext.response.ExtBidResponse; -import org.prebid.server.proto.openrtb.ext.response.ExtBidResponsePrebid; -import org.prebid.server.proto.openrtb.ext.response.ExtModules; -import org.prebid.server.proto.openrtb.ext.response.ExtModulesTrace; -import org.prebid.server.proto.openrtb.ext.response.ExtModulesTraceAnalyticsActivity; -import org.prebid.server.proto.openrtb.ext.response.ExtModulesTraceAnalyticsResult; -import org.prebid.server.proto.openrtb.ext.response.ExtModulesTraceAnalyticsTags; -import org.prebid.server.proto.openrtb.ext.response.ExtModulesTraceGroup; -import org.prebid.server.proto.openrtb.ext.response.ExtModulesTraceInvocationResult; -import org.prebid.server.proto.openrtb.ext.response.ExtModulesTraceStage; -import org.prebid.server.proto.openrtb.ext.response.ExtModulesTraceStageOutcome; +import org.prebid.server.settings.model.Account; +import org.prebid.server.settings.model.AccountAnalyticsConfig; import org.prebid.server.util.HttpUtil; import org.prebid.server.version.PrebidVersionProvider; import org.prebid.server.vertx.httpclient.HttpClient; @@ -139,6 +130,58 @@ public void setUp() { prebidVersionProvider); } + @Test + public void shouldReceiveValidResponseOnAuctionContextForBannerWhenPartnerActivatedInBidRequest() + throws IOException { + // given + final Banner banner = givenBanner(); + + final ObjectNode impExtNode = mapper.createObjectNode(); + impExtNode.set("gpid", TextNode.valueOf("gpidvalue")); + impExtNode.set("prebid", givenPrebidBidderParamsNode()); + + final Imp imp = Imp.builder() + .id("adunitcodevalue") + .ext(impExtNode) + .banner(banner) + .build(); + + final AuctionContext auctionContext = givenAuctionContext( + builder -> builder.bidRequest(givenBidRequestWithExtension(identity(), List.of(imp))), + List.of(imp), + true); + final AuctionEvent event = AuctionEvent.builder() + .auctionContext(auctionContext) + .bidResponse(auctionContext.getBidResponse()) + .build(); + + final HttpClientResponse mockResponse = mock(HttpClientResponse.class); + when(mockResponse.getStatusCode()).thenReturn(202); + when(httpClient.post(anyString(), any(MultiMap.class), anyString(), anyLong())) + .thenReturn(Future.succeededFuture(mockResponse)); + final CommonMessage expectedCommonMessage = expectedCommonMessageForBanner(); + + // when + target.processEvent(event); + + // then + verify(httpClient).post( + eq(greenbidsAnalyticsProperties.getAnalyticsServerUrl()), + any(MultiMap.class), + jsonCaptor.capture(), + eq(greenbidsAnalyticsProperties.getTimeoutMs())); + + final String capturedJson = jsonCaptor.getValue(); + final CommonMessage capturedCommonMessage = jacksonMapper.mapper() + .readValue(capturedJson, CommonMessage.class); + + assertThat(capturedCommonMessage).usingRecursiveComparison() + .ignoringFields("billingId", "greenbidsId") + .isEqualTo(expectedCommonMessage); + assertThat(capturedCommonMessage.getGreenbidsId()).isNotNull(); + assertThat(capturedCommonMessage.getBillingId()).isNotNull(); + } + @Test public void shouldReceiveValidResponseOnAuctionContextWithAnalyticsTagForBanner() throws IOException { // given @@ -155,7 +198,7 @@ public void shouldReceiveValidResponseOnAuctionContextWithAnalyticsTagForBanner( .build(); final AuctionContext auctionContext = givenAuctionContextWithAnalyticsTag( - context -> context, List.of(imp), true, true); + context -> context, List.of(imp), true); final AuctionEvent event = AuctionEvent.builder() .auctionContext(auctionContext) .bidResponse(auctionContext.getBidResponse()) @@ -521,8 +564,9 @@ public void shouldFailWhenAdUnitsListIsEmpty() { when(auctionContext.getBidRequest()) .thenReturn(BidRequest.builder() .id("request1") - .ext(givenExtRequest()) + .imp(Collections.emptyList()) .build()); + when(auctionContext.getAccount()).thenReturn(givenAccount()); final AuctionEvent event = mock(AuctionEvent.class); when(event.getAuctionContext()).thenReturn(auctionContext); @@ -584,7 +628,8 @@ private static AuctionContext givenAuctionContext( final AuctionContext.AuctionContextBuilder auctionContextBuilder = AuctionContext.builder() .httpRequest(HttpRequestContext.builder().build()) .bidRequest(givenBidRequest(request -> request, imps)) - .bidRejectionTrackers(Map.of("seat3", givenBidRejectionTracker())); + .bidRejectionTrackers(Map.of("seat3", givenBidRejectionTracker())) + .account(givenAccount()); if (includeBidResponse) { auctionContextBuilder.bidResponse(givenBidResponse(response -> response)); @@ -596,17 +641,15 @@ private static AuctionContext givenAuctionContext( private static AuctionContext givenAuctionContextWithAnalyticsTag( UnaryOperator auctionContextCustomizer, List imps, - boolean includeBidResponse, - boolean includeHookExecutionContextWithAnalyticsTag) { + boolean includeBidResponse) { final AuctionContext.AuctionContextBuilder auctionContextBuilder = AuctionContext.builder() .httpRequest(HttpRequestContext.builder().build()) .bidRequest(givenBidRequest(request -> request, imps)) - .bidRejectionTrackers(Map.of("seat3", givenBidRejectionTracker())); + .bidRejectionTrackers(Map.of("seat3", givenBidRejectionTracker())) + .account(givenAccount()); - if (includeHookExecutionContextWithAnalyticsTag) { - final HookExecutionContext hookExecutionContext = givenHookExecutionContextWithAnalyticsTag(); - auctionContextBuilder.hookExecutionContext(hookExecutionContext); - } + final HookExecutionContext hookExecutionContext = givenHookExecutionContextWithAnalyticsTag(); + auctionContextBuilder.hookExecutionContext(hookExecutionContext); if (includeBidResponse) { auctionContextBuilder.bidResponse(givenBidResponse(response -> response)); @@ -615,15 +658,59 @@ private static AuctionContext givenAuctionContextWithAnalyticsTag( return auctionContextCustomizer.apply(auctionContextBuilder).build(); } + private static Account givenAccount() { + return Account.builder() + .id("test-account") + .analytics(givenAccountHooksConfiguration()) + .build(); + } + + private static AccountAnalyticsConfig givenAccountHooksConfiguration() { + final ObjectNode greenbidsNode = mapper.createObjectNode(); + greenbidsNode.put("pbuid", "leparisien"); + greenbidsNode.put("greenbids-sampling", 1.0); + final Map modules = Map.of("greenbids", greenbidsNode); + return AccountAnalyticsConfig.of(true, null, modules); + } + private static BidRequest givenBidRequest( UnaryOperator bidRequestCustomizer, List imps) { - return bidRequestCustomizer.apply(BidRequest.builder() + final BidRequest.BidRequestBuilder bidRequestBuilder = BidRequest.builder() + .id("request1") + .imp(imps) + .site(givenSite(site -> site)) + .device(givenDevice(device -> device)); + + return bidRequestCustomizer.apply(bidRequestBuilder).build(); + } + + private static BidRequest givenBidRequestWithExtension( + UnaryOperator bidRequestCustomizer, + List imps) { + final BidRequest.BidRequestBuilder bidRequestBuilder = BidRequest.builder() .id("request1") .imp(imps) .site(givenSite(site -> site)) .device(givenDevice(device -> device)) - .ext(givenExtRequest())).build(); + .ext(givenExtRequest()); + + return bidRequestCustomizer.apply(bidRequestBuilder).build(); + } + + private static ExtRequest givenExtRequest() { + final ObjectNode greenbidsNode = new ObjectMapper().createObjectNode(); + greenbidsNode.put("pbuid", "leparisien"); + greenbidsNode.put("greenbids-sampling", 1.0); + + final ObjectNode analyticsNode = new ObjectMapper().createObjectNode(); + analyticsNode.set("greenbids", greenbidsNode); + + return ExtRequest.of( + ExtRequestPrebid + .builder() + .analytics(analyticsNode) + .build()); } private static Site givenSite(UnaryOperator siteCustomizer) { @@ -685,55 +772,6 @@ private static BidResponse givenBidResponse(UnaryOperator bidResponseCustomizer) { - final ObjectNode analyticsResultNode = mapper.valueToTree( - singletonMap( - "adunitcodevalue", - createAnalyticsResultNode())); - - final ExtModulesTraceAnalyticsTags analyticsTags = ExtModulesTraceAnalyticsTags.of( - Collections.singletonList( - ExtModulesTraceAnalyticsActivity.of( - null, null, Collections.singletonList( - ExtModulesTraceAnalyticsResult.of( - null, analyticsResultNode, null))))); - - final ExtModulesTraceInvocationResult invocationResult = ExtModulesTraceInvocationResult.builder() - .hookId(HookId.of("greenbids-real-time-data", null)) - .analyticsTags(analyticsTags) - .build(); - - final ExtModulesTraceStageOutcome outcome = ExtModulesTraceStageOutcome.of( - "auction-request", null, - Collections.singletonList(ExtModulesTraceGroup.of( - null, Collections.singletonList(invocationResult)))); - - final ExtModulesTraceStage stage = ExtModulesTraceStage.of( - Stage.processed_auction_request, null, - Collections.singletonList(outcome)); - - final ExtModulesTrace modulesTrace = ExtModulesTrace.of(null, Collections.singletonList(stage)); - - final ExtModules modules = ExtModules.of(null, null, modulesTrace); - - final ExtBidResponsePrebid prebid = ExtBidResponsePrebid.builder().modules(modules).build(); - - final ExtBidResponse extBidResponse = ExtBidResponse.builder().prebid(prebid).build(); - - return bidResponseCustomizer.apply(BidResponse.builder() - .id("response2") - .seatbid(List.of( - givenSeatBid( - seatBid -> seatBid.seat("seat1"), - bid -> bid.id("bid1").price(BigDecimal.valueOf(1.5))), - givenSeatBid( - seatBid -> seatBid.seat("seat2"), - bid -> bid.id("bid2").price(BigDecimal.valueOf(0.5))))) - .cur("USD") - .ext(extBidResponse)).build(); - } - private static ObjectNode createAnalyticsResultNode() { final ObjectNode keptInAuctionNode = new ObjectNode(JsonNodeFactory.instance); keptInAuctionNode.put("seat1", true); @@ -803,21 +841,6 @@ private static ObjectNode givenPrebidBidderParamsNode() { return prebidNode; } - private 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()); - } - private static CommonMessage expectedCommonMessageForBanner() { return expectedCommonMessage( adUnit -> adUnit