Skip to content

Commit 5bbb155

Browse files
author
ilya
committed
DATA-22937: LI Analytic module.
1 parent 587d6fb commit 5bbb155

2 files changed

Lines changed: 207 additions & 19 deletions

File tree

src/main/java/org/prebid/server/analytics/reporter/liveintent/LiveIntentAnalyticsReporter.java

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import java.util.List;
2929
import java.util.Objects;
3030
import java.util.Optional;
31-
import java.util.stream.Stream;
3231

3332
public class LiveIntentAnalyticsReporter implements AnalyticsReporter {
3433

@@ -69,21 +68,19 @@ private Future<Void> processNotificationEvent(NotificationEvent notificationEven
6968
private Future<Void> processAuctionEvent(AuctionContext auctionContext) {
7069
try {
7170
final BidRequest bidRequest = Optional.ofNullable(auctionContext.getBidRequest())
72-
.orElseThrow(() -> new PreBidException("Bid response should not be empty"));
71+
.orElseThrow(() -> new PreBidException("Bid request should not be empty"));
7372
final BidResponse bidResponse = Optional.ofNullable(auctionContext.getBidResponse())
7473
.orElseThrow(() -> new PreBidException("Bid response should not be empty"));
7574
final Optional<ExtRequestPrebid> requestPrebid = Optional.ofNullable(bidRequest.getExt())
7675
.flatMap(ext -> Optional.of(ext.getPrebid()));
7776

78-
final Stream<Activity> activities = getActivities(auctionContext);
77+
final List<Activity> activities = getActivities(auctionContext);
7978

8079
final List<PbsjBid> pbsjBids = bidResponse.getSeatbid().stream()
8180
.flatMap(seatBid -> seatBid.getBid().stream())
8281
.flatMap(bid ->
8382
bidRequest.getImp().stream()
8483
.filter(impItem -> impItem.getId().equals(bid.getImpid()))
85-
.findFirst()
86-
.stream()
8784
.map(imp ->
8885
PbsjBid.builder()
8986
.bidId(bid.getId())
@@ -109,7 +106,7 @@ private Future<Void> processAuctionEvent(AuctionContext auctionContext) {
109106
}
110107
}
111108

112-
private Stream<Activity> getActivities(AuctionContext auctionContext) {
109+
private List<Activity> getActivities(AuctionContext auctionContext) {
113110
return Optional.ofNullable(auctionContext)
114111
.map(AuctionContext::getHookExecutionContext)
115112
.map(HookExecutionContext::getStageOutcomes)
@@ -126,11 +123,13 @@ private Stream<Activity> getActivities(AuctionContext auctionContext) {
126123
)
127124
.map(HookExecutionOutcome::getAnalyticsTags)
128125
.map(Tags::activities)
129-
.flatMap(Collection::stream);
126+
.flatMap(Collection::stream)
127+
.toList();
130128
}
131129

132-
private Float getTreatmentRate(Stream<Activity> activities) {
130+
private Float getTreatmentRate(List<Activity> activities) {
133131
return activities
132+
.stream()
134133
.filter(activity -> "liveintent-treatment-rate".equals(activity.name()))
135134
.findFirst()
136135
.map(activity -> {
@@ -141,11 +140,11 @@ private Float getTreatmentRate(Stream<Activity> activities) {
141140
throw e;
142141
}
143142
})
144-
.get();
143+
.orElse(-1.0f);
145144
}
146145

147-
private boolean isEnriched(Stream<Activity> activities) {
148-
return activities.anyMatch(activity -> "liveintent-enriched".equals(activity.name()));
146+
private boolean isEnriched(List<Activity> activities) {
147+
return activities.stream().anyMatch(activity -> "liveintent-enriched".equals(activity.name()));
149148
}
150149

151150
@Override

src/test/java/org/prebid/server/analytics/reporter/liveintent/LiveintentAnalyticsReporterTest.java

Lines changed: 197 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package org.prebid.server.analytics.reporter.liveintent;
22

33
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import com.iab.openrtb.request.BidRequest;
5+
import com.iab.openrtb.request.Imp;
6+
import com.iab.openrtb.response.Bid;
7+
import com.iab.openrtb.response.BidResponse;
8+
import com.iab.openrtb.response.SeatBid;
49
import io.vertx.core.Future;
5-
import io.vertx.core.MultiMap;
610
import org.junit.jupiter.api.BeforeEach;
711
import org.junit.jupiter.api.extension.ExtendWith;
812
import org.mockito.ArgumentCaptor;
@@ -11,12 +15,32 @@
1115
import org.junit.jupiter.api.Test;
1216
import org.mockito.junit.jupiter.MockitoExtension;
1317
import org.prebid.server.VertxTest;
18+
import org.prebid.server.analytics.model.AuctionEvent;
1419
import org.prebid.server.analytics.model.NotificationEvent;
1520
import org.prebid.server.analytics.reporter.liveintent.model.LiveIntentAnalyticsProperties;
21+
import org.prebid.server.analytics.reporter.liveintent.model.PbsjBid;
22+
import org.prebid.server.auction.model.AuctionContext;
23+
import org.prebid.server.hooks.execution.model.ExecutionStatus;
24+
import org.prebid.server.hooks.execution.model.GroupExecutionOutcome;
25+
import org.prebid.server.hooks.execution.model.HookExecutionContext;
26+
import org.prebid.server.hooks.execution.model.HookExecutionOutcome;
27+
import org.prebid.server.hooks.execution.model.HookId;
28+
import org.prebid.server.hooks.execution.model.Stage;
29+
import org.prebid.server.hooks.execution.model.StageExecutionOutcome;
30+
import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl;
31+
import org.prebid.server.hooks.execution.v1.analytics.TagsImpl;
1632
import org.prebid.server.json.JacksonMapper;
33+
import org.prebid.server.model.Endpoint;
34+
import org.prebid.server.util.ListUtil;
1735
import org.prebid.server.vertx.httpclient.HttpClient;
1836
import org.prebid.server.vertx.httpclient.model.HttpClientResponse;
1937

38+
import java.math.BigDecimal;
39+
import java.util.Arrays;
40+
import java.util.EnumMap;
41+
import java.util.List;
42+
43+
import static org.assertj.core.api.Assertions.assertThat;
2044
import static org.mockito.ArgumentMatchers.anyLong;
2145
import static org.mockito.ArgumentMatchers.anyString;
2246
import static org.mockito.ArgumentMatchers.eq;
@@ -27,12 +51,12 @@
2751
@ExtendWith(MockitoExtension.class)
2852
public class LiveintentAnalyticsReporterTest extends VertxTest {
2953

30-
private static final HttpClientResponse SUCCESS_RESPONSE = HttpClientResponse.of(
31-
200, MultiMap.caseInsensitiveMultiMap(), "OK");
32-
3354
@Mock
3455
private HttpClient httpClient;
3556

57+
@Captor
58+
private ArgumentCaptor<String> jsonCaptor;
59+
3660
private LiveIntentAnalyticsReporter target;
3761

3862
private LiveIntentAnalyticsProperties properties;
@@ -58,15 +82,180 @@ public void setUp() {
5882

5983
@Test
6084
public void shouldProcessNotificationEvent() {
85+
// given
86+
final HttpClientResponse mockResponse = mock(HttpClientResponse.class);
87+
when(httpClient.get(anyString(), anyLong())).thenReturn(Future.succeededFuture(mockResponse));
6188

6289
// when
6390
target.processEvent(NotificationEvent.builder().bidId("123").bidder("foo").build());
64-
final HttpClientResponse mockResponse = mock(HttpClientResponse.class);
6591

66-
when(httpClient.get(anyString(), anyLong())).thenReturn(Future.succeededFuture(mockResponse));
6792
// then
6893
// Verify that the HTTP client was called with the expected parameters
69-
verify(httpClient).get(eq(properties.getAnalyticsEndpoint() + "/analytic-events/pbsj-winning-bid")
70-
+ "?b=foo&bidId=123", eq(properties.getTimeoutMs()));
94+
verify(httpClient).get(eq(properties.getAnalyticsEndpoint() + "/analytic-events/pbsj-winning-bid"
95+
+ "?b=foo&bidId=123"), eq(properties.getTimeoutMs()));
96+
}
97+
98+
@Test
99+
public void shouldSendAllBidsToLiveIntent() {
100+
// given
101+
final HttpClientResponse mockResponse = mock(HttpClientResponse.class);
102+
when(httpClient.post(anyString(), anyString(), anyLong()))
103+
.thenReturn(Future.succeededFuture(mockResponse));
104+
105+
// when
106+
target.processEvent(buildEvent(true));
107+
108+
// then
109+
verify(httpClient).post(
110+
eq(properties.getAnalyticsEndpoint() + "/analytic-events/pbsj-bids"),
111+
jsonCaptor.capture(),
112+
eq(properties.getTimeoutMs()));
113+
114+
final String capturedJson = jsonCaptor.getValue();
115+
final List<PbsjBid> pbsjBids = Arrays.stream(jacksonMapper.decodeValue(capturedJson, PbsjBid[].class)).toList();
116+
assertThat(pbsjBids).isEqualTo(List.of(
117+
PbsjBid.builder()
118+
.bidId("bid-id")
119+
.price(BigDecimal.ONE)
120+
.adUnitId("ad-unit-id")
121+
.enriched(true)
122+
.currency("USD")
123+
.treatmentRate(0.5f)
124+
.timestamp(0L)
125+
.partnerId("pbsj")
126+
.build()
127+
));
128+
}
129+
130+
@Test
131+
public void shouldSendAllBidsToLiveIntentNotEnriched() {
132+
// given
133+
final HttpClientResponse mockResponse = mock(HttpClientResponse.class);
134+
when(httpClient.post(anyString(), anyString(), anyLong()))
135+
.thenReturn(Future.succeededFuture(mockResponse));
136+
137+
// when
138+
target.processEvent(buildEvent(false));
139+
140+
// then
141+
verify(httpClient).post(
142+
eq(properties.getAnalyticsEndpoint() + "/analytic-events/pbsj-bids"),
143+
jsonCaptor.capture(),
144+
eq(properties.getTimeoutMs()));
145+
146+
final String capturedJson = jsonCaptor.getValue();
147+
final List<PbsjBid> pbsjBids = Arrays.stream(jacksonMapper.decodeValue(capturedJson, PbsjBid[].class)).toList();
148+
assertThat(pbsjBids).isEqualTo(List.of(
149+
PbsjBid.builder()
150+
.bidId("bid-id")
151+
.price(BigDecimal.ONE)
152+
.adUnitId("ad-unit-id")
153+
.enriched(false)
154+
.currency("USD")
155+
.treatmentRate(0.5f)
156+
.timestamp(0L)
157+
.partnerId("pbsj")
158+
.build()
159+
));
160+
}
161+
162+
@Test
163+
public void shouldSendAllBidsToLiveIntentNoTreatmentRate() {
164+
// given
165+
final HttpClientResponse mockResponse = mock(HttpClientResponse.class);
166+
when(httpClient.post(anyString(), anyString(), anyLong()))
167+
.thenReturn(Future.succeededFuture(mockResponse));
168+
169+
// when
170+
target.processEvent(buildEvent(false, false));
171+
172+
// then
173+
verify(httpClient).post(
174+
eq(properties.getAnalyticsEndpoint() + "/analytic-events/pbsj-bids"),
175+
jsonCaptor.capture(),
176+
eq(properties.getTimeoutMs()));
177+
178+
final String capturedJson = jsonCaptor.getValue();
179+
final List<PbsjBid> pbsjBids = Arrays.stream(jacksonMapper.decodeValue(capturedJson, PbsjBid[].class)).toList();
180+
assertThat(pbsjBids).isEqualTo(List.of(
181+
PbsjBid.builder()
182+
.bidId("bid-id")
183+
.price(BigDecimal.ONE)
184+
.adUnitId("ad-unit-id")
185+
.enriched(false)
186+
.currency("USD")
187+
.treatmentRate(-1.0f)
188+
.timestamp(0L)
189+
.partnerId("pbsj")
190+
.build()
191+
));
192+
}
193+
194+
private AuctionEvent buildEvent(Boolean isEnriched) {
195+
return buildEvent(isEnriched, true);
196+
}
197+
198+
private AuctionEvent buildEvent(Boolean isEnriched, Boolean withTags) {
199+
final HookId hookId = HookId.of(
200+
"liveintent-omni-channel-identity-enrichment-hook",
201+
"liveintent-omni-channel-identity-enrichment-hook");
202+
203+
final ActivityImpl enrichmentRate = ActivityImpl.of("liveintent-treatment-rate", "0.5", List.of());
204+
205+
final List<ActivityImpl> enriched = isEnriched
206+
? List.of(ActivityImpl.of("liveintent-enriched", "success", List.of()))
207+
: List.of();
208+
209+
final HookExecutionOutcome hookExecutionOutcome = HookExecutionOutcome.builder()
210+
.hookId(hookId)
211+
.executionTime(100L)
212+
.status(ExecutionStatus.success)
213+
.analyticsTags(TagsImpl.of(
214+
withTags
215+
? ListUtil.union(
216+
List.of(enrichmentRate),
217+
enriched
218+
)
219+
: List.of()
220+
))
221+
.action(null)
222+
.build();
223+
224+
final StageExecutionOutcome stageExecutionOutcome = StageExecutionOutcome.of(
225+
"auction-request",
226+
List.of(GroupExecutionOutcome.of(List.of(hookExecutionOutcome)))
227+
);
228+
229+
final EnumMap<Stage, List<StageExecutionOutcome>> stageOutcomes = new EnumMap<>(Stage.class);
230+
stageOutcomes.put(Stage.processed_auction_request, List.of(stageExecutionOutcome));
231+
return AuctionEvent.builder()
232+
.auctionContext(
233+
AuctionContext.builder()
234+
.bidRequest(BidRequest.builder()
235+
.id("request-id")
236+
.imp(List.of(
237+
Imp.builder()
238+
.id("imp-id")
239+
.tagid("ad-unit-id")
240+
.build()))
241+
.build())
242+
.bidResponse(BidResponse.builder()
243+
.bidid("bid-id")
244+
.cur("USD")
245+
.seatbid(List.of(
246+
SeatBid.builder()
247+
.bid(List.of(
248+
Bid.builder()
249+
.id("bid-id")
250+
.impid("imp-id")
251+
.price(BigDecimal.ONE)
252+
.build()))
253+
.build()))
254+
.build())
255+
.hookExecutionContext(HookExecutionContext.of(
256+
Endpoint.openrtb2_auction,
257+
stageOutcomes))
258+
.build())
259+
.build();
71260
}
72261
}

0 commit comments

Comments
 (0)