Skip to content

Commit 3cb039b

Browse files
authored
LiveIntent: Limiting LI ids to configured bidders (#4281)
1 parent 0c6585e commit 3cb039b

File tree

3 files changed

+180
-6
lines changed

3 files changed

+180
-6
lines changed

extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/model/config/LiveIntentOmniChannelProperties.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import lombok.Data;
44

5+
import java.util.List;
6+
57
@Data
68
public final class LiveIntentOmniChannelProperties {
79

@@ -12,4 +14,6 @@ public final class LiveIntentOmniChannelProperties {
1214
String authToken;
1315

1416
float treatmentRate;
17+
18+
List<String> targetBidders;
1519
}

extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/hooks/LiveIntentOmniChannelIdentityProcessedAuctionRequestHook.java

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.iab.openrtb.request.User;
88
import io.vertx.core.Future;
99
import io.vertx.core.MultiMap;
10+
import org.apache.commons.collections4.CollectionUtils;
1011
import org.apache.commons.collections4.ListUtils;
1112
import org.prebid.server.activity.Activity;
1213
import org.prebid.server.activity.ComponentType;
@@ -33,15 +34,24 @@
3334
import org.prebid.server.json.JacksonMapper;
3435
import org.prebid.server.log.ConditionalLogger;
3536
import org.prebid.server.log.LoggerFactory;
37+
import org.prebid.server.proto.openrtb.ext.request.ExtRequest;
38+
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid;
39+
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidData;
40+
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidDataEidPermissions;
3641
import org.prebid.server.util.HttpUtil;
3742
import org.prebid.server.util.ListUtil;
43+
import org.prebid.server.util.StreamUtil;
3844
import org.prebid.server.vertx.httpclient.HttpClient;
3945
import org.prebid.server.vertx.httpclient.model.HttpClientResponse;
4046

47+
import java.util.Collections;
4148
import java.util.List;
4249
import java.util.Objects;
4350
import java.util.Optional;
51+
import java.util.Set;
4452
import java.util.concurrent.ThreadLocalRandom;
53+
import java.util.stream.Collectors;
54+
import java.util.stream.Stream;
4555

4656
public class LiveIntentOmniChannelIdentityProcessedAuctionRequestHook implements ProcessedAuctionRequestHook {
4757

@@ -55,6 +65,7 @@ public class LiveIntentOmniChannelIdentityProcessedAuctionRequestHook implements
5565
private final HttpClient httpClient;
5666
private final UserFpdActivityMask userFpdActivityMask;
5767
private final double logSamplingRate;
68+
private final List<String> targetBidders;
5869

5970
public LiveIntentOmniChannelIdentityProcessedAuctionRequestHook(LiveIntentOmniChannelProperties config,
6071
UserFpdActivityMask userFpdActivityMask,
@@ -68,6 +79,7 @@ public LiveIntentOmniChannelIdentityProcessedAuctionRequestHook(LiveIntentOmniCh
6879
this.httpClient = Objects.requireNonNull(httpClient);
6980
this.logSamplingRate = logSamplingRate;
7081
this.userFpdActivityMask = Objects.requireNonNull(userFpdActivityMask);
82+
this.targetBidders = ListUtils.emptyIfNull(config.getTargetBidders());
7183
}
7284

7385
@Override
@@ -170,17 +182,17 @@ private InvocationResultImpl<AuctionRequestPayload> update(IdResResponse resolut
170182
ActivityImpl.of(
171183
"liveintent-enriched", "success",
172184
List.of(
173-
ResultImpl.of(
174-
"",
175-
mapper.mapper().createObjectNode()
176-
.put("treatmentRate", config.getTreatmentRate()),
177-
null))))))
185+
ResultImpl.of(
186+
"",
187+
mapper.mapper().createObjectNode()
188+
.put("treatmentRate", config.getTreatmentRate()),
189+
null))))))
178190
.build();
179191
}
180192

181193
private AuctionRequestPayload updatedPayload(AuctionRequestPayload requestPayload, List<Eid> resolvedEids) {
182194
final List<Eid> eids = ListUtils.emptyIfNull(resolvedEids);
183-
final BidRequest bidRequest = requestPayload.bidRequest();
195+
final BidRequest bidRequest = updateAllowedBidders(requestPayload.bidRequest(), resolvedEids);
184196
final User updatedUser = Optional.ofNullable(bidRequest.getUser())
185197
.map(user -> user.toBuilder().eids(ListUtil.union(ListUtils.emptyIfNull(user.getEids()), eids)))
186198
.orElseGet(() -> User.builder().eids(eids))
@@ -189,6 +201,61 @@ private AuctionRequestPayload updatedPayload(AuctionRequestPayload requestPayloa
189201
return AuctionRequestPayloadImpl.of(bidRequest.toBuilder().user(updatedUser).build());
190202
}
191203

204+
private BidRequest updateAllowedBidders(BidRequest bidRequest, List<Eid> resolvedEids) {
205+
if (targetBidders.isEmpty()) {
206+
return bidRequest;
207+
}
208+
209+
final ExtRequest ext = bidRequest.getExt();
210+
final ExtRequestPrebid extPrebid = ext != null ? ext.getPrebid() : null;
211+
final ExtRequestPrebidData extPrebidData = extPrebid != null ? extPrebid.getData() : null;
212+
213+
final ExtRequestPrebid updatedExtPrebid = Optional.ofNullable(extPrebid)
214+
.map(ExtRequestPrebid::toBuilder)
215+
.orElseGet(ExtRequestPrebid::builder)
216+
.data(updatePrebidData(extPrebidData, resolvedEids))
217+
.build();
218+
219+
final ExtRequest updatedExtRequest = ExtRequest.of(updatedExtPrebid);
220+
if (ext != null) {
221+
mapper.fillExtension(updatedExtRequest, ext.getProperties());
222+
}
223+
224+
return bidRequest.toBuilder().ext(updatedExtRequest).build();
225+
}
226+
227+
private ExtRequestPrebidData updatePrebidData(ExtRequestPrebidData extPrebidData, List<Eid> resolvedEids) {
228+
final List<String> prebidDataBidders = extPrebidData != null ? extPrebidData.getBidders() : null;
229+
final List<String> updatedPrebidDataBidders = prebidDataBidders != null
230+
? (List<String>) CollectionUtils.union(targetBidders, prebidDataBidders)
231+
: targetBidders;
232+
233+
final Set<String> resolvedSources = resolvedEids.stream().map(Eid::getSource).collect(Collectors.toSet());
234+
235+
final List<ExtRequestPrebidDataEidPermissions> initialPermissions = Optional.ofNullable(extPrebidData)
236+
.map(ExtRequestPrebidData::getEidPermissions)
237+
.orElse(Collections.emptyList());
238+
final List<ExtRequestPrebidDataEidPermissions> updatedPermissions = Stream.concat(
239+
initialPermissions.stream()
240+
.map(permission -> updateEidPermission(permission, resolvedSources)),
241+
resolvedSources.stream()
242+
.map(source -> ExtRequestPrebidDataEidPermissions.of(source, targetBidders)))
243+
.filter(StreamUtil.distinctBy(ExtRequestPrebidDataEidPermissions::getSource))
244+
.toList();
245+
246+
return ExtRequestPrebidData.of(updatedPrebidDataBidders, updatedPermissions);
247+
}
248+
249+
private ExtRequestPrebidDataEidPermissions updateEidPermission(ExtRequestPrebidDataEidPermissions permission,
250+
Set<String> resolvedSources) {
251+
252+
return resolvedSources.contains(permission.getSource())
253+
? ExtRequestPrebidDataEidPermissions.of(
254+
permission.getSource(),
255+
(List<String>) CollectionUtils.union(permission.getBidders(), targetBidders))
256+
: permission;
257+
}
258+
192259
@Override
193260
public String code() {
194261
return CODE;

extra/modules/live-intent-omni-channel-identity/src/test/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/LiveIntentOmniChannelIdentityProcessedAuctionRequestHookTest.java

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@
2828
import org.prebid.server.hooks.v1.auction.AuctionRequestPayload;
2929
import org.prebid.server.json.JacksonMapper;
3030
import org.prebid.server.json.ObjectMapperProvider;
31+
import org.prebid.server.proto.openrtb.ext.request.ExtRequest;
32+
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid;
33+
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidData;
34+
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidDataEidPermissions;
3135
import org.prebid.server.vertx.httpclient.HttpClient;
3236
import org.prebid.server.vertx.httpclient.model.HttpClientResponse;
3337

@@ -70,12 +74,16 @@ public class LiveIntentOmniChannelIdentityProcessedAuctionRequestHookTest {
7074

7175
private LiveIntentOmniChannelIdentityProcessedAuctionRequestHook target;
7276

77+
private List<String> configuredBidders;
78+
7379
@BeforeEach
7480
public void setUp() {
81+
configuredBidders = List.of("bidder1", "bidder2");
7582
given(properties.getRequestTimeoutMs()).willReturn(5L);
7683
given(properties.getIdentityResolutionEndpoint()).willReturn("https://test.com/idres");
7784
given(properties.getAuthToken()).willReturn("auth_token");
7885
given(properties.getTreatmentRate()).willReturn(1.0f);
86+
given(properties.getTargetBidders()).willReturn(configuredBidders);
7987

8088
target = new LiveIntentOmniChannelIdentityProcessedAuctionRequestHook(
8189
properties, userFpdActivityMask, MAPPER, httpClient, 0.01d);
@@ -365,4 +373,99 @@ public void callShouldReturnFailureWhenRequestingEidsIsFailed() {
365373
.isInstanceOf(TimeoutException.class)
366374
.hasMessage("Timeout exceeded");
367375
}
376+
377+
@Test
378+
public void biddersConfiguredRestrictionShouldBeRespected() {
379+
final Uid givenUid = Uid.builder().id("id1").atype(2).build();
380+
final Eid givenEid = Eid.builder().source("some.source.com").uids(singletonList(givenUid)).build();
381+
final User givenUser = User.builder().eids(singletonList(givenEid)).build();
382+
final BidRequest givenBidRequest = BidRequest.builder().id("request").user(givenUser).build();
383+
384+
final ExtRequestPrebidData expectedData = ExtRequestPrebidData.of(configuredBidders, List.of(
385+
ExtRequestPrebidDataEidPermissions.of("liveintent.com", configuredBidders)));
386+
387+
final Eid expectedEid = Eid.builder().source("liveintent.com").build();
388+
389+
final String responseBody = MAPPER.encodeToString(IdResResponse.of(List.of(expectedEid)));
390+
given(httpClient.post(any(), any(), any(), anyLong()))
391+
.willReturn(Future.succeededFuture(HttpClientResponse.of(200, null, responseBody)));
392+
393+
given(auctionInvocationContext.auctionContext()).willReturn(auctionContext);
394+
given(auctionContext.getActivityInfrastructure()).willReturn(activityInfrastructure);
395+
given(activityInfrastructure.isAllowed(any(), any())).willReturn(true);
396+
given(userFpdActivityMask.maskUser(any(), eq(false), eq(false)))
397+
.willAnswer(invocation -> invocation.getArgument(0));
398+
given(userFpdActivityMask.maskDevice(any(), eq(false), eq(false)))
399+
.willAnswer(invocation -> invocation.getArgument(0));
400+
401+
// when
402+
final InvocationResult<AuctionRequestPayload> result =
403+
target.call(AuctionRequestPayloadImpl.of(givenBidRequest), auctionInvocationContext).result();
404+
// then
405+
assertThat(result.status()).isEqualTo(InvocationStatus.success);
406+
assertThat(result.payloadUpdate().apply(AuctionRequestPayloadImpl.of(givenBidRequest)))
407+
.extracting(AuctionRequestPayload::bidRequest)
408+
.extracting(BidRequest::getExt)
409+
.extracting(ExtRequest::getPrebid)
410+
.extracting(ExtRequestPrebid::getData)
411+
.isEqualTo(expectedData);
412+
413+
verify(httpClient).post(
414+
eq("https://test.com/idres"),
415+
argThat(headers -> headers.contains("Authorization", "Bearer auth_token", true)),
416+
eq(MAPPER.encodeToString(givenBidRequest)),
417+
eq(5L));
418+
}
419+
420+
@Test
421+
public void biddersConfiguredRestrictionShouldBeMergedWithProvided() {
422+
// given
423+
final Uid givenUid = Uid.builder().id("id1").atype(2).build();
424+
final Eid givenEid = Eid.builder().source("some.source.com").uids(singletonList(givenUid)).build();
425+
final User givenUser = User.builder().eids(singletonList(givenEid)).build();
426+
final BidRequest givenBidRequest = BidRequest.builder().id("request").user(givenUser).ext(ExtRequest.of(
427+
ExtRequestPrebid.builder().data(ExtRequestPrebidData.of(List.of("bidder3"), List.of(
428+
ExtRequestPrebidDataEidPermissions.of("some.other-source.com", List.of("bidder3")),
429+
ExtRequestPrebidDataEidPermissions.of("some.source.com", List.of("bidder3"))))
430+
).build())).build();
431+
432+
final List<String> expectedBidders = List.of("bidder3", "bidder2", "bidder1");
433+
434+
final ExtRequestPrebidData expectedData = ExtRequestPrebidData.of(expectedBidders, List.of(
435+
ExtRequestPrebidDataEidPermissions.of("some.other-source.com", List.of("bidder3")),
436+
ExtRequestPrebidDataEidPermissions.of("some.source.com", List.of("bidder3")),
437+
ExtRequestPrebidDataEidPermissions.of("liveintent.com", configuredBidders)));
438+
439+
final Eid expectedEid = Eid.builder().source("liveintent.com").build();
440+
441+
final String responseBody = MAPPER.encodeToString(IdResResponse.of(List.of(expectedEid)));
442+
given(httpClient.post(any(), any(), any(), anyLong()))
443+
.willReturn(Future.succeededFuture(HttpClientResponse.of(200, null, responseBody)));
444+
445+
given(auctionInvocationContext.auctionContext()).willReturn(auctionContext);
446+
given(auctionContext.getActivityInfrastructure()).willReturn(activityInfrastructure);
447+
given(activityInfrastructure.isAllowed(any(), any())).willReturn(true);
448+
given(userFpdActivityMask.maskUser(any(), eq(false), eq(false)))
449+
.willAnswer(invocation -> invocation.getArgument(0));
450+
given(userFpdActivityMask.maskDevice(any(), eq(false), eq(false)))
451+
.willAnswer(invocation -> invocation.getArgument(0));
452+
453+
// when
454+
final InvocationResult<AuctionRequestPayload> result =
455+
target.call(AuctionRequestPayloadImpl.of(givenBidRequest), auctionInvocationContext).result();
456+
// then
457+
assertThat(result.status()).isEqualTo(InvocationStatus.success);
458+
assertThat(result.payloadUpdate().apply(AuctionRequestPayloadImpl.of(givenBidRequest)))
459+
.extracting(AuctionRequestPayload::bidRequest)
460+
.extracting(BidRequest::getExt)
461+
.extracting(ExtRequest::getPrebid)
462+
.extracting(ExtRequestPrebid::getData)
463+
.isEqualTo(expectedData);
464+
465+
verify(httpClient).post(
466+
eq("https://test.com/idres"),
467+
argThat(headers -> headers.contains("Authorization", "Bearer auth_token", true)),
468+
eq(MAPPER.encodeToString(givenBidRequest)),
469+
eq(5L));
470+
}
368471
}

0 commit comments

Comments
 (0)