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
53 changes: 28 additions & 25 deletions extra/modules/live-intent-omni-channel-identity/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,48 @@

This module enriches bid requests with user EIDs.

The user EIDs to be enriched are configured per partner as part of the LiveIntent HIRO onboarding process. As part of this onboarding process, partners will also be provided with the `identity-resolution-endpoint` URL as well as with the `auth-token`.
The user EIDs to be enriched are configured per partner as part of the LiveIntent HIRO onboarding process. As part of
this onboarding process, partners will also be provided with the `identity-resolution-endpoint` URL as well as with the
`auth-token`.

`treatment-rate` is a value between 0.0 and 1.0 (including 0.0 and 1.0) and defines the percentage of requests for which identity enrichment should be performed. This value can be freely picked. We recommend a value between 0.9 and 0.95
`treatment-rate` is a value between 0.0 and 1.0 (including 0.0 and 1.0) and defines the percentage of requests for which
identity enrichment should be performed. This value can be freely picked. We recommend a value between 0.9 and 0.95

## Configuration

To start using the LiveIntent Omni Channel Identity module you have to enable it and add configuration:

```yaml
hooks:
liveintent-omni-channel-identity:
enabled: true
host-execution-plan: >
{
"endpoints": {
"/openrtb2/auction": {
"stages": {
"processed-auction-request": {
"groups": [
{
"timeout": 100,
"hook-sequence": [
liveintent-omni-channel-identity:
enabled: true
host-execution-plan: >
{
"endpoints": {
"/openrtb2/auction": {
"stages": {
"processed-auction-request": {
"groups": [
{
"module-code": "liveintent-omni-channel-identity",
"hook-impl-code": "liveintent-omni-channel-identity-enrichment-hook"
"timeout": 100,
"hook-sequence": [
{
"module-code": "liveintent-omni-channel-identity",
"hook-impl-code": "liveintent-omni-channel-identity-enrichment-hook"
}
]
}
]
}
]
}
}
}
}
}
}
modules:
liveintent-omni-channel-identity:
request-timeout-ms: 2000
identity-resolution-endpoint: "https://liveintent.com/idx"
auth-token: "secret-token"
treatment-rate: 0.9
modules:
liveintent-omni-channel-identity:
request-timeout-ms: 2000
identity-resolution-endpoint: "https://u.liveintent.com/idx"
auth-token: "secret-token"
treatment-rate: 0.9
```

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.prebid.server.hooks.modules.liveintent.omni.channel.identity.model;

import lombok.Value;

@Value(staticConstructor = "of")
public class FullSource {

String source;

String inserter;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.collections4.SetUtils;
import org.apache.commons.lang3.StringUtils;
import org.prebid.server.activity.Activity;
import org.prebid.server.activity.ComponentType;
import org.prebid.server.activity.infrastructure.ActivityInfrastructure;
Expand Down Expand Up @@ -58,6 +59,8 @@ public class LiveIntentOmniChannelIdentityProcessedAuctionRequestHook implements

private static final String CODE = "liveintent-omni-channel-identity-enrichment-hook";

private static final String INSERTER = "s2s.liveintent.com";

private final LiveIntentOmniChannelProperties config;
private final JacksonMapper mapper;
private final HttpClient httpClient;
Expand Down Expand Up @@ -161,7 +164,18 @@ private MultiMap headers() {
}

private IdResResponse processResponse(HttpClientResponse response) {
return mapper.decodeValue(response.getBody(), IdResResponse.class);
final IdResResponse res = mapper.decodeValue(response.getBody(), IdResResponse.class);
final List<Eid> eids = res.getEids();

if (CollectionUtils.isEmpty(eids)) {
return res;
}

final List<Eid> modifiedEids = eids.stream()
.map(eid -> eid.toBuilder().inserter(INSERTER).build())
.toList();

return IdResResponse.of(modifiedEids);
}

private static Future<InvocationResult<AuctionRequestPayload>> noAction() {
Expand Down Expand Up @@ -189,80 +203,109 @@ private InvocationResultImpl<AuctionRequestPayload> update(IdResResponse resolut
}

private AuctionRequestPayload updatedPayload(AuctionRequestPayload requestPayload, List<Eid> resolvedEids) {
final List<Eid> eids = ListUtils.emptyIfNull(resolvedEids);
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))
.build();
return CollectionUtils.isNotEmpty(resolvedEids)
? AuctionRequestPayloadImpl.of(updateBidRequest(requestPayload.bidRequest(), resolvedEids))
: requestPayload;
}

return AuctionRequestPayloadImpl.of(bidRequest.toBuilder().user(updatedUser).build());
private BidRequest updateBidRequest(BidRequest bidRequest, List<Eid> resolvedEids) {
return bidRequest.toBuilder()
.ext(updateExtRequest(bidRequest.getExt(), resolvedEids))
.user(updateUser(bidRequest.getUser(), resolvedEids))
.build();
}

private BidRequest updateAllowedBidders(BidRequest bidRequest, List<Eid> resolvedEids) {
if (CollectionUtils.isEmpty(targetBidders) || CollectionUtils.isEmpty(resolvedEids)) {
return bidRequest;
}
private ExtRequest updateExtRequest(ExtRequest ext, List<Eid> resolvedEids) {
final Set<String> uniqueSources = CollectionUtils.emptyIfNull(resolvedEids).stream()
.map(Eid::getSource)
.filter(StringUtils::isNotEmpty)
.collect(Collectors.toSet());

final ExtRequest ext = bidRequest.getExt();
final ExtRequestPrebid extPrebid = ext != null ? ext.getPrebid() : null;
final ExtRequestPrebidData extPrebidData = extPrebid != null ? extPrebid.getData() : null;
final List<ExtRequestPrebidDataEidPermissions> eidPermissions =
extPrebidData != null ? extPrebidData.getEidPermissions() : null;

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

if (CollectionUtils.isEmpty(existingPerms)) {
return bidRequest;
}
final List<ExtRequestPrebidDataEidPermissions> modifiedEidPermissions = CollectionUtils.isEmpty(eidPermissions)
? createEidPermissions(uniqueSources)
: modifyEidPermissions(eidPermissions, uniqueSources);

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

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

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

private ExtRequestPrebidData updatePrebidData(ExtRequestPrebidData extPrebidData, List<Eid> resolvedEids) {
final List<String> originalBidders = extPrebidData != null ? extPrebidData.getBidders() : null;
private static User updateUser(User user, List<Eid> resolvedEids) {
final List<Eid> updatedEids = Optional.ofNullable(user)
.map(User::getEids)
.map(eids -> ListUtil.union(eids, resolvedEids))
.orElse(resolvedEids);

final Set<String> resolvedSources = resolvedEids.stream()
.map(Eid::getSource)
.collect(Collectors.toSet());
return Optional.ofNullable(user)
.map(User::toBuilder)
.orElseGet(User::builder)
.eids(updatedEids)
.build();
}

final List<ExtRequestPrebidDataEidPermissions> updatedPermissions = extPrebidData.getEidPermissions().stream()
.map(permission -> restrictEidPermission(permission, resolvedSources))
.filter(Objects::nonNull)
private List<ExtRequestPrebidDataEidPermissions> createEidPermissions(Set<String> sources) {
return sources.stream()
.map(source -> ExtRequestPrebidDataEidPermissions.builder()
.source(source)
.inserter(INSERTER)
.bidders(targetBidders.stream().toList())
.build())
.toList();
}

return ExtRequestPrebidData.of(originalBidders, updatedPermissions);
private List<ExtRequestPrebidDataEidPermissions> modifyEidPermissions(
List<ExtRequestPrebidDataEidPermissions> eidPermissions,
Set<String> sources) {
final List<ExtRequestPrebidDataEidPermissions> modifiedEidPermissions = eidPermissions.stream()
.map(it -> updateEidPermission(it, sources))
.filter(Objects::nonNull)
.toList();
final List<ExtRequestPrebidDataEidPermissions> defaultEidPermissions = createEidPermissions(sources);
return ListUtils.union(modifiedEidPermissions, defaultEidPermissions);
}

private ExtRequestPrebidDataEidPermissions restrictEidPermission(ExtRequestPrebidDataEidPermissions permission,
Set<String> resolvedSources) {
private ExtRequestPrebidData updatePrebidData(ExtRequestPrebidData extPrebidData,
List<ExtRequestPrebidDataEidPermissions> eidPermissions) {

final List<String> originalBidders = extPrebidData != null ? extPrebidData.getBidders() : null;

return ExtRequestPrebidData.of(originalBidders, eidPermissions);
}

if (!resolvedSources.contains(permission.getSource())) {
return permission;
private ExtRequestPrebidDataEidPermissions updateEidPermission(ExtRequestPrebidDataEidPermissions eidPermission,
Set<String> sources) {
if (!sources.contains(eidPermission.getSource()) || !INSERTER.equals(eidPermission.getInserter())) {
return eidPermission;
}

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

return CollectionUtils.isEmpty(finalBidders)
? null
: ExtRequestPrebidDataEidPermissions
.builder()
.bidders(finalBidders)
.source(permission.getSource())
.build();
if (CollectionUtils.isEmpty(allowedBidders) || allowedBidders.contains("*")) {
return eidPermission.toBuilder().bidders(targetBidders.stream().toList()).build();
}

if (CollectionUtils.isEmpty(finalBidders)) {
return null;
}

return eidPermission.toBuilder().bidders(finalBidders).build();
}

@Override
Expand Down
Loading