11package org .prebid .server .analytics .reporter .liveintent ;
22
33import com .iab .openrtb .request .BidRequest ;
4+ import com .iab .openrtb .response .Bid ;
45import com .iab .openrtb .response .BidResponse ;
6+ import com .iab .openrtb .response .SeatBid ;
57import io .vertx .core .Future ;
8+ import org .apache .commons .collections4 .CollectionUtils ;
9+ import org .apache .http .client .utils .URIBuilder ;
610import org .prebid .server .analytics .AnalyticsReporter ;
711import org .prebid .server .analytics .model .AuctionEvent ;
812import org .prebid .server .analytics .model .NotificationEvent ;
2428import org .prebid .server .proto .openrtb .ext .request .ExtRequestPrebid ;
2529import org .prebid .server .vertx .httpclient .HttpClient ;
2630
31+ import java .net .URI ;
32+ import java .net .URISyntaxException ;
2733import java .util .Collection ;
2834import java .util .List ;
2935import java .util .Objects ;
3036import java .util .Optional ;
3137
3238public class LiveIntentAnalyticsReporter implements AnalyticsReporter {
3339
40+ private static final Logger logger = LoggerFactory .getLogger (LiveIntentAnalyticsReporter .class );
41+
42+ private static final String LIVEINTENT_HOOK_ID = "liveintent-omni-channel-identity-enrichment-hook" ;
43+
3444 private final HttpClient httpClient ;
3545 private final LiveIntentAnalyticsProperties properties ;
3646 private final JacksonMapper jacksonMapper ;
3747
38- private static final Logger logger = LoggerFactory .getLogger (LiveIntentAnalyticsReporter .class );
3948
4049 public LiveIntentAnalyticsReporter (
4150 LiveIntentAnalyticsProperties properties ,
4251 HttpClient httpClient ,
43- JacksonMapper jacksonMapper
44- ) {
52+ JacksonMapper jacksonMapper ) {
4553 this .httpClient = Objects .requireNonNull (httpClient );
4654 this .properties = Objects .requireNonNull (properties );
4755 this .jacksonMapper = Objects .requireNonNull (jacksonMapper );
@@ -59,44 +67,78 @@ public <T> Future<Void> processEvent(T event) {
5967 }
6068
6169 private Future <Void > processNotificationEvent (NotificationEvent notificationEvent ) {
62- final String url = properties .getAnalyticsEndpoint () + "/analytic-events/pbsj-winning-bid?"
63- + "b=" + notificationEvent .getBidder ()
64- + "&bidId=" + notificationEvent .getBidId ();
65- return httpClient .get (url , properties .getTimeoutMs ()).mapEmpty ();
70+ try {
71+ final String url = new URIBuilder (URI .create (properties .getAnalyticsEndpoint ()))
72+ .setPath ("/analytic-events/pbsj-winning-bid" )
73+ .setParameter ("b=" , notificationEvent .getBidder ())
74+ .setParameter ("bidId=" , notificationEvent .getBidId ())
75+ .build ()
76+ .toString ();
77+ return httpClient .get (url , properties .getTimeoutMs ()).mapEmpty ();
78+ }
79+ catch (URISyntaxException e ) {
80+ logger .error ("Error composing url for notification event: {}" , e .getMessage ());
81+ return Future .failedFuture (e );
82+ }
83+ }
84+
85+ private Optional <PbsjBid > buildPbsjBid (
86+ BidRequest bidRequest ,
87+ BidResponse bidResponse ,
88+ Bid bid ,
89+ boolean isEnriched ,
90+ Float treatmentRate ,
91+ Long timestamp ) {
92+ return bidRequest .getImp ().stream ()
93+ .filter (impItem -> impItem .getId ().equals (bid .getImpid ()))
94+ .map (imp ->
95+ PbsjBid .builder ()
96+ .bidId (bid .getId ())
97+ .price (bid .getPrice ())
98+ .adUnitId (imp .getTagid ())
99+ .enriched (isEnriched )
100+ .currency (bidResponse .getCur ())
101+ .treatmentRate (treatmentRate )
102+ .timestamp (timestamp )
103+ .partnerId (properties .getPartnerId ())
104+ .build ()
105+ )
106+ .findFirst ();
66107 }
67108
68109 private Future <Void > processAuctionEvent (AuctionContext auctionContext ) {
69- try {
70- final BidRequest bidRequest = Optional .ofNullable (auctionContext .getBidRequest ())
71- .orElseThrow (() -> new PreBidException ("Bid request should not be empty" ));
72- final BidResponse bidResponse = Optional .ofNullable (auctionContext .getBidResponse ())
73- .orElseThrow (() -> new PreBidException ("Bid response should not be empty" ));
110+ if (auctionContext .getBidRequest () == null ) {
111+ return Future .failedFuture (new PreBidException ("Bid request should not be empty" ));
112+ }
113+
114+ if (auctionContext .getBidResponse () == null ) {
115+ return Future .failedFuture (new PreBidException ("Bid response should not be empty" ));
116+ }
117+
118+ final BidRequest bidRequest = auctionContext .getBidRequest ();
119+ final BidResponse bidResponse = auctionContext .getBidResponse ();
74120 final Optional <ExtRequestPrebid > requestPrebid = Optional .ofNullable (bidRequest .getExt ())
75121 .flatMap (ext -> Optional .of (ext .getPrebid ()));
76122
77- final List <Activity > activities = getActivities (auctionContext );
78-
79- final List <PbsjBid > pbsjBids = bidResponse .getSeatbid ().stream ()
80- .flatMap (seatBid -> seatBid .getBid ().stream ())
81- .flatMap (bid ->
82- bidRequest .getImp ().stream ()
83- .filter (impItem -> impItem .getId ().equals (bid .getImpid ()))
84- .map (imp ->
85- PbsjBid .builder ()
86- .bidId (bid .getId ())
87- .price (bid .getPrice ())
88- .adUnitId (imp .getTagid ())
89- .enriched (isEnriched (activities ))
90- .currency (bidResponse .getCur ())
91- .treatmentRate (getTreatmentRate (activities ))
92- .timestamp (requestPrebid .map (ExtRequestPrebid ::getAuctiontimestamp ).orElse (0L ))
93- .partnerId (properties .getPartnerId ())
94- .build ()
95- )
96- ).toList ();
123+ final Optional <Activity > activity = getActivities (auctionContext );
124+ final boolean isEnriched = isEnriched (activity );
125+ final Float treatmentRate = getTreatmentRate (activity );
126+ final Long timestamp = requestPrebid .map (ExtRequestPrebid ::getAuctiontimestamp ).orElse (0L );
97127
128+ final List <PbsjBid > pbsjBids = CollectionUtils .emptyIfNull (bidResponse .getSeatbid ()).stream ()
129+ .map (SeatBid ::getBid )
130+ .flatMap (Collection ::stream )
131+ .map (bid -> buildPbsjBid (bidRequest , bidResponse , bid , isEnriched , treatmentRate , timestamp ))
132+ .filter (Optional ::isPresent )
133+ .map (Optional ::get )
134+ .toList ();
135+
136+ try {
98137 return httpClient .post (
99- properties .getAnalyticsEndpoint () + "/analytic-events/pbsj-bids" ,
138+ new URIBuilder (URI .create (properties .getAnalyticsEndpoint ()))
139+ .setPath ("/analytic-events/pbsj-bids" )
140+ .build ()
141+ .toString (),
100142 jacksonMapper .encodeToString (pbsjBids ),
101143 properties .getTimeoutMs ()
102144 ).mapEmpty ();
@@ -106,7 +148,7 @@ private Future<Void> processAuctionEvent(AuctionContext auctionContext) {
106148 }
107149 }
108150
109- private List <Activity > getActivities (AuctionContext auctionContext ) {
151+ private Optional <Activity > getActivities (AuctionContext auctionContext ) {
110152 return Optional .ofNullable (auctionContext )
111153 .map (AuctionContext ::getHookExecutionContext )
112154 .map (HookExecutionContext ::getStageOutcomes )
@@ -118,33 +160,25 @@ private List<Activity> getActivities(AuctionContext auctionContext) {
118160 .map (GroupExecutionOutcome ::getHooks )
119161 .flatMap (Collection ::stream )
120162 .filter (hook ->
121- "liveintent-omni-channel-identity-enrichment-hook" .equals (hook .getHookId ().getModuleCode ())
163+ LIVEINTENT_HOOK_ID .equals (hook .getHookId ().getModuleCode ())
122164 && hook .getStatus () == ExecutionStatus .success
123165 )
124166 .map (HookExecutionOutcome ::getAnalyticsTags )
125167 .map (Tags ::activities )
126168 .flatMap (Collection ::stream )
127- .toList ();
169+ .findFirst ();
128170 }
129171
130- private Float getTreatmentRate (List <Activity > activities ) {
131- return activities
132- .stream ()
133- .filter (activity -> "liveintent-treatment-rate" .equals (activity .name ()))
172+ private Float getTreatmentRate (Optional <Activity > activity ) {
173+ return activity .stream ()
174+ .flatMap (a -> a .results ().stream ())
134175 .findFirst ()
135- .map (activity -> {
136- try {
137- return Float .parseFloat (activity .status ());
138- } catch (NumberFormatException e ) {
139- logger .warn ("Invalid treatment rate value: {}" , activity .status ());
140- throw e ;
141- }
142- })
176+ .map (a -> a .values ().at ("treatmentRate" ).floatValue ())
143177 .orElse (null );
144178 }
145179
146- private boolean isEnriched (List <Activity > activities ) {
147- return activities .stream ().anyMatch (activity -> "liveintent-enriched" .equals (activity .name ()));
180+ private boolean isEnriched (Optional <Activity > activity ) {
181+ return activity .stream ().anyMatch (a -> "liveintent-enriched" .equals (a .name ()));
148182 }
149183
150184 @ Override
0 commit comments