Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import lombok.Data;

import java.util.List;
import java.util.Set;

@Data
public final class LiveIntentOmniChannelProperties {
Expand All @@ -15,5 +15,5 @@ public final class LiveIntentOmniChannelProperties {

float treatmentRate;

List<String> targetBidders;
Set<String> targetBidders;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import io.vertx.core.MultiMap;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.collections4.SetUtils;
import org.prebid.server.activity.Activity;
import org.prebid.server.activity.ComponentType;
import org.prebid.server.activity.infrastructure.ActivityInfrastructure;
Expand Down Expand Up @@ -40,18 +41,15 @@
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidDataEidPermissions;
import org.prebid.server.util.HttpUtil;
import org.prebid.server.util.ListUtil;
import org.prebid.server.util.StreamUtil;
import org.prebid.server.vertx.httpclient.HttpClient;
import org.prebid.server.vertx.httpclient.model.HttpClientResponse;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class LiveIntentOmniChannelIdentityProcessedAuctionRequestHook implements ProcessedAuctionRequestHook {

Expand All @@ -65,7 +63,7 @@ public class LiveIntentOmniChannelIdentityProcessedAuctionRequestHook implements
private final HttpClient httpClient;
private final UserFpdActivityMask userFpdActivityMask;
private final double logSamplingRate;
private final List<String> targetBidders;
private final Set<String> targetBidders;

public LiveIntentOmniChannelIdentityProcessedAuctionRequestHook(LiveIntentOmniChannelProperties config,
UserFpdActivityMask userFpdActivityMask,
Expand All @@ -79,7 +77,7 @@ public LiveIntentOmniChannelIdentityProcessedAuctionRequestHook(LiveIntentOmniCh
this.httpClient = Objects.requireNonNull(httpClient);
this.logSamplingRate = logSamplingRate;
this.userFpdActivityMask = Objects.requireNonNull(userFpdActivityMask);
this.targetBidders = ListUtils.emptyIfNull(config.getTargetBidders());
this.targetBidders = SetUtils.emptyIfNull(config.getTargetBidders());
}

@Override
Expand Down Expand Up @@ -202,14 +200,22 @@ private AuctionRequestPayload updatedPayload(AuctionRequestPayload requestPayloa
}

private BidRequest updateAllowedBidders(BidRequest bidRequest, List<Eid> resolvedEids) {
if (targetBidders.isEmpty()) {
if (CollectionUtils.isEmpty(targetBidders) || CollectionUtils.isEmpty(resolvedEids)) {
return bidRequest;
}

final ExtRequest ext = bidRequest.getExt();
final ExtRequestPrebid extPrebid = ext != null ? ext.getPrebid() : null;
final ExtRequestPrebidData extPrebidData = extPrebid != null ? extPrebid.getData() : null;

final List<ExtRequestPrebidDataEidPermissions> existingPerms = extPrebidData != null
? extPrebidData.getEidPermissions()
: null;

if (CollectionUtils.isEmpty(existingPerms)) {
return bidRequest;
}

final ExtRequestPrebid updatedExtPrebid = Optional.ofNullable(extPrebid)
.map(ExtRequestPrebid::toBuilder)
.orElseGet(ExtRequestPrebid::builder)
Expand All @@ -225,35 +231,38 @@ private BidRequest updateAllowedBidders(BidRequest bidRequest, List<Eid> resolve
}

private ExtRequestPrebidData updatePrebidData(ExtRequestPrebidData extPrebidData, List<Eid> resolvedEids) {
final List<String> prebidDataBidders = extPrebidData != null ? extPrebidData.getBidders() : null;
final List<String> updatedPrebidDataBidders = prebidDataBidders != null
? (List<String>) CollectionUtils.union(targetBidders, prebidDataBidders)
: targetBidders;

final Set<String> resolvedSources = resolvedEids.stream().map(Eid::getSource).collect(Collectors.toSet());

final List<ExtRequestPrebidDataEidPermissions> initialPermissions = Optional.ofNullable(extPrebidData)
.map(ExtRequestPrebidData::getEidPermissions)
.orElse(Collections.emptyList());
final List<ExtRequestPrebidDataEidPermissions> updatedPermissions = Stream.concat(
initialPermissions.stream()
.map(permission -> updateEidPermission(permission, resolvedSources)),
resolvedSources.stream()
.map(source -> ExtRequestPrebidDataEidPermissions.of(source, targetBidders)))
.filter(StreamUtil.distinctBy(ExtRequestPrebidDataEidPermissions::getSource))
final List<String> originalBidders = extPrebidData != null ? extPrebidData.getBidders() : null;

final Set<String> resolvedSources = resolvedEids.stream()
.map(Eid::getSource)
.collect(Collectors.toSet());

final List<ExtRequestPrebidDataEidPermissions> updatedPermissions = extPrebidData.getEidPermissions().stream()
.map(permission -> restrictEidPermission(permission, resolvedSources))
.filter(Objects::nonNull)
.toList();

return ExtRequestPrebidData.of(updatedPrebidDataBidders, updatedPermissions);
return ExtRequestPrebidData.of(originalBidders, updatedPermissions);
}

private ExtRequestPrebidDataEidPermissions updateEidPermission(ExtRequestPrebidDataEidPermissions permission,
Set<String> resolvedSources) {
private ExtRequestPrebidDataEidPermissions restrictEidPermission(ExtRequestPrebidDataEidPermissions permission,
Set<String> resolvedSources) {

return resolvedSources.contains(permission.getSource())
? ExtRequestPrebidDataEidPermissions.of(
permission.getSource(),
(List<String>) CollectionUtils.union(permission.getBidders(), targetBidders))
: permission;
if (!resolvedSources.contains(permission.getSource())) {
return permission;
}

final List<String> finalBidders = ListUtils.emptyIfNull(permission.getBidders()).stream()
.filter(targetBidders::contains)
.toList();

return CollectionUtils.isEmpty(finalBidders)
? null
: ExtRequestPrebidDataEidPermissions
.builder()
.bidders(finalBidders)
.source(permission.getSource())
.build();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.prebid.server.vertx.httpclient.model.HttpClientResponse;

import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeoutException;

import static java.util.Collections.singletonList;
Expand Down Expand Up @@ -74,11 +75,11 @@ public class LiveIntentOmniChannelIdentityProcessedAuctionRequestHookTest {

private LiveIntentOmniChannelIdentityProcessedAuctionRequestHook target;

private List<String> configuredBidders;
private Set<String> configuredBidders;

@BeforeEach
public void setUp() {
configuredBidders = List.of("bidder1", "bidder2");
configuredBidders = Set.of("bidder1", "bidder2");
given(properties.getRequestTimeoutMs()).willReturn(5L);
given(properties.getIdentityResolutionEndpoint()).willReturn("https://test.com/idres");
given(properties.getAuthToken()).willReturn("auth_token");
Expand Down Expand Up @@ -375,14 +376,38 @@ public void callShouldReturnFailureWhenRequestingEidsIsFailed() {
}

@Test
public void biddersConfiguredRestrictionShouldBeRespected() {
public void shouldRestrictExistingEidPermissionsByIntersectionAndKeepGlobalBiddersUnchanged() {
// given
final Uid givenUid = Uid.builder().id("id1").atype(2).build();
final Eid givenEid = Eid.builder().source("some.source.com").uids(singletonList(givenUid)).build();
final User givenUser = User.builder().eids(singletonList(givenEid)).build();
final BidRequest givenBidRequest = BidRequest.builder().id("request").user(givenUser).build();

final ExtRequestPrebidData expectedData = ExtRequestPrebidData.of(configuredBidders, List.of(
ExtRequestPrebidDataEidPermissions.of("liveintent.com", configuredBidders)));
final ExtRequestPrebidDataEidPermissions otherBidder = ExtRequestPrebidDataEidPermissions.builder()
.source("some.other-source.com")
.bidders(singletonList("bidderY"))
.build();

final ExtRequestPrebidDataEidPermissions liBidder2 = ExtRequestPrebidDataEidPermissions.builder()
.source("liveintent.com")
.bidders(singletonList("bidder2"))
.build();
final ExtRequestPrebidDataEidPermissions liBidder23 = ExtRequestPrebidDataEidPermissions.builder()
.source("liveintent.com")
.bidders(List.of("bidder2", "bidder3"))
.build();

final ExtRequestPrebidData givenData = ExtRequestPrebidData.of(singletonList("bidderX"),
List.of(otherBidder, liBidder23));

final BidRequest givenBidRequest = BidRequest.builder()
.id("request")
.user(givenUser)
.ext(ExtRequest.of(ExtRequestPrebid.builder().data(givenData).build()))
.build();

final ExtRequestPrebidData expectedData = ExtRequestPrebidData.of(
List.of("bidderX"),
List.of(otherBidder, liBidder2));

final Eid expectedEid = Eid.builder().source("liveintent.com").build();

Expand Down Expand Up @@ -418,23 +443,28 @@ public void biddersConfiguredRestrictionShouldBeRespected() {
}

@Test
public void biddersConfiguredRestrictionShouldBeMergedWithProvided() {
public void shouldNotAddNewEidPermissionsOrModifyGlobalBiddersWhenSourceNotPresent() {
// given
final Uid givenUid = Uid.builder().id("id1").atype(2).build();
final Eid givenEid = Eid.builder().source("some.source.com").uids(singletonList(givenUid)).build();
final User givenUser = User.builder().eids(singletonList(givenEid)).build();
final BidRequest givenBidRequest = BidRequest.builder().id("request").user(givenUser).ext(ExtRequest.of(
ExtRequestPrebid.builder().data(ExtRequestPrebidData.of(List.of("bidder3"), List.of(
ExtRequestPrebidDataEidPermissions.of("some.other-source.com", List.of("bidder3")),
ExtRequestPrebidDataEidPermissions.of("some.source.com", List.of("bidder3"))))
).build())).build();
final ExtRequestPrebidDataEidPermissions bidder1 = ExtRequestPrebidDataEidPermissions.builder()
.source("some.other-source.com")
.bidders(singletonList("bidder3"))
.build();
final ExtRequestPrebidDataEidPermissions bidder2 = ExtRequestPrebidDataEidPermissions.builder()
.source("some.source.com")
.bidders(singletonList("bidder3"))
.build();

final List<String> expectedBidders = List.of("bidder3", "bidder2", "bidder1");
final List<ExtRequestPrebidDataEidPermissions> bidders = List.of(bidder1, bidder2);

final ExtRequestPrebidData expectedData = ExtRequestPrebidData.of(expectedBidders, List.of(
ExtRequestPrebidDataEidPermissions.of("some.other-source.com", List.of("bidder3")),
ExtRequestPrebidDataEidPermissions.of("some.source.com", List.of("bidder3")),
ExtRequestPrebidDataEidPermissions.of("liveintent.com", configuredBidders)));
final BidRequest givenBidRequest = BidRequest.builder().id("request").user(givenUser).ext(ExtRequest.of(
ExtRequestPrebid.builder().data(
ExtRequestPrebidData.of(singletonList("bidder3"), bidders)).build()))
.build();

final ExtRequestPrebidData expectedData = ExtRequestPrebidData.of(List.of("bidder3"), bidders);

final Eid expectedEid = Eid.builder().source("liveintent.com").build();

Expand Down Expand Up @@ -468,4 +498,64 @@ public void biddersConfiguredRestrictionShouldBeMergedWithProvided() {
eq(MAPPER.encodeToString(givenBidRequest)),
eq(5L));
}

@Test
public void shouldRemovePermissionWhenIntersectionIsEmpty() {
// given
final Uid givenUid = Uid.builder().id("id1").atype(2).build();
final Eid givenEid = Eid.builder().source("some.source.com").uids(singletonList(givenUid)).build();
final User givenUser = User.builder().eids(singletonList(givenEid)).build();

final ExtRequestPrebidData givenData = ExtRequestPrebidData.of(
List.of("bidderGlobal"),
List.of(
ExtRequestPrebidDataEidPermissions.builder().source("liveintent.com")
.bidders(singletonList("not-allowed")).build(),
ExtRequestPrebidDataEidPermissions.builder().source("keep.com")
.bidders(singletonList("bidderGlobal")).build()
));

final BidRequest givenBidRequest = BidRequest.builder()
.id("request")
.user(givenUser)
.ext(ExtRequest.of(ExtRequestPrebid.builder().data(givenData).build()))
.build();

final Eid expectedEid = Eid.builder().source("liveintent.com").build();
final String responseBody = MAPPER.encodeToString(IdResResponse.of(List.of(expectedEid)));
given(httpClient.post(any(), any(), any(), anyLong()))
.willReturn(Future.succeededFuture(HttpClientResponse.of(200, null, responseBody)));

given(auctionInvocationContext.auctionContext()).willReturn(auctionContext);
given(auctionContext.getActivityInfrastructure()).willReturn(activityInfrastructure);
given(activityInfrastructure.isAllowed(any(), any())).willReturn(true);
given(userFpdActivityMask.maskUser(any(), eq(false), eq(false)))
.willAnswer(invocation -> invocation.getArgument(0));
given(userFpdActivityMask.maskDevice(any(), eq(false), eq(false)))
.willAnswer(invocation -> invocation.getArgument(0));

// when
final InvocationResult<AuctionRequestPayload> result =
target.call(AuctionRequestPayloadImpl.of(givenBidRequest), auctionInvocationContext).result();

// then
final ExtRequestPrebidData expectedData = ExtRequestPrebidData.of(
List.of("bidderGlobal"),
List.of(ExtRequestPrebidDataEidPermissions.builder()
.source("keep.com").bidders(singletonList("bidderGlobal")).build()));

assertThat(result.status()).isEqualTo(InvocationStatus.success);
assertThat(result.payloadUpdate().apply(AuctionRequestPayloadImpl.of(givenBidRequest)))
.extracting(AuctionRequestPayload::bidRequest)
.extracting(BidRequest::getExt)
.extracting(ExtRequest::getPrebid)
.extracting(ExtRequestPrebid::getData)
.isEqualTo(expectedData);

verify(httpClient).post(
eq("https://test.com/idres"),
argThat(headers -> headers.contains("Authorization", "Bearer auth_token", true)),
eq(MAPPER.encodeToString(givenBidRequest)),
eq(5L));
}
}