Skip to content
Merged
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,6 +2,8 @@

import lombok.Data;

import java.util.List;

@Data
public final class LiveIntentOmniChannelProperties {

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

float treatmentRate;

List<String> targetBidders;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SuperIzya , you might want to have this and other configs to be per-account, instead of being global. Or being overridable by the account. Right now, big host companies that serve multiple accounts won't be able to define different configs for different accounts. Which doesn't sound like something that you actually want.

No need to implement this in the scope of this PR. It's just a thing for you to think about.

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.iab.openrtb.request.User;
import io.vertx.core.Future;
import io.vertx.core.MultiMap;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.prebid.server.activity.Activity;
import org.prebid.server.activity.ComponentType;
Expand All @@ -33,15 +34,24 @@
import org.prebid.server.json.JacksonMapper;
import org.prebid.server.log.ConditionalLogger;
import org.prebid.server.log.LoggerFactory;
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.request.ExtRequestPrebidData;
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 @@ -55,6 +65,7 @@ public class LiveIntentOmniChannelIdentityProcessedAuctionRequestHook implements
private final HttpClient httpClient;
private final UserFpdActivityMask userFpdActivityMask;
private final double logSamplingRate;
private final List<String> targetBidders;

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

@Override
Expand Down Expand Up @@ -170,17 +182,17 @@ private InvocationResultImpl<AuctionRequestPayload> update(IdResResponse resolut
ActivityImpl.of(
"liveintent-enriched", "success",
List.of(
ResultImpl.of(
"",
mapper.mapper().createObjectNode()
.put("treatmentRate", config.getTreatmentRate()),
null))))))
ResultImpl.of(
"",
mapper.mapper().createObjectNode()
.put("treatmentRate", config.getTreatmentRate()),
null))))))
.build();
}

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

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

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

final ExtRequestPrebid updatedExtPrebid = Optional.ofNullable(extPrebid)
.map(ExtRequestPrebid::toBuilder)
.orElseGet(ExtRequestPrebid::builder)
.data(updatePrebidData(extPrebidData, resolvedEids))
.build();

final ExtRequest updatedExtRequest = ExtRequest.of(updatedExtPrebid);
if (ext != null) {
mapper.fillExtension(updatedExtRequest, ext.getProperties());
}

return bidRequest.toBuilder().ext(updatedExtRequest).build();
}

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))
.toList();

return ExtRequestPrebidData.of(updatedPrebidDataBidders, updatedPermissions);
}

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

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

@Override
public String code() {
return CODE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
import org.prebid.server.hooks.v1.auction.AuctionRequestPayload;
import org.prebid.server.json.JacksonMapper;
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 org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidData;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidDataEidPermissions;
import org.prebid.server.vertx.httpclient.HttpClient;
import org.prebid.server.vertx.httpclient.model.HttpClientResponse;

Expand Down Expand Up @@ -70,12 +74,16 @@ public class LiveIntentOmniChannelIdentityProcessedAuctionRequestHookTest {

private LiveIntentOmniChannelIdentityProcessedAuctionRequestHook target;

private List<String> configuredBidders;

@BeforeEach
public void setUp() {
configuredBidders = List.of("bidder1", "bidder2");
given(properties.getRequestTimeoutMs()).willReturn(5L);
given(properties.getIdentityResolutionEndpoint()).willReturn("https://test.com/idres");
given(properties.getAuthToken()).willReturn("auth_token");
given(properties.getTreatmentRate()).willReturn(1.0f);
given(properties.getTargetBidders()).willReturn(configuredBidders);

target = new LiveIntentOmniChannelIdentityProcessedAuctionRequestHook(
properties, userFpdActivityMask, MAPPER, httpClient, 0.01d);
Expand Down Expand Up @@ -365,4 +373,99 @@ public void callShouldReturnFailureWhenRequestingEidsIsFailed() {
.isInstanceOf(TimeoutException.class)
.hasMessage("Timeout exceeded");
}

@Test
public void biddersConfiguredRestrictionShouldBeRespected() {
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 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
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));
}

@Test
public void biddersConfiguredRestrictionShouldBeMergedWithProvided() {
// 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 List<String> expectedBidders = List.of("bidder3", "bidder2", "bidder1");

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 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
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));
}
}
Loading