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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM amazoncorretto:21.0.6-al2023
FROM amazoncorretto:21.0.8-al2023

WORKDIR /app/prebid-server

Expand Down
2 changes: 1 addition & 1 deletion Dockerfile-modules
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM amazoncorretto:21.0.6-al2023
FROM amazoncorretto:21.0.8-al2023

WORKDIR /app/prebid-server

Expand Down
5 changes: 5 additions & 0 deletions extra/bundle/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@
<artifactId>wurfl-devicedetection</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.prebid.server.hooks.modules</groupId>
<artifactId>live-intent-omni-channel-identity</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>

<build>
Expand Down
46 changes: 46 additions & 0 deletions extra/modules/live-intent-omni-channel-identity/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Overview

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`.

`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": [
{
"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
```

18 changes: 18 additions & 0 deletions extra/modules/live-intent-omni-channel-identity/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.prebid.server.hooks.modules</groupId>
<artifactId>all-modules</artifactId>
<version>3.31.0-SNAPSHOT</version>
</parent>

<artifactId>live-intent-omni-channel-identity</artifactId>

<name>live-intent-omni-channel-identity</name>
<description>LiveIntent Omni-Channel Identity</description>

<dependencies>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.prebid.server.hooks.modules.liveintent.omni.channel.identity.config;

import org.prebid.server.hooks.modules.liveintent.omni.channel.identity.model.config.LiveIntentOmniChannelProperties;
import org.prebid.server.hooks.modules.liveintent.omni.channel.identity.v1.LiveIntentOmniChannelIdentityModule;
import org.prebid.server.hooks.modules.liveintent.omni.channel.identity.v1.hooks.LiveIntentOmniChannelIdentityProcessedAuctionRequestHook;
import org.prebid.server.hooks.v1.Module;
import org.prebid.server.json.JacksonMapper;
import org.prebid.server.vertx.httpclient.HttpClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Collections;

@Configuration
@ConditionalOnProperty(
prefix = "hooks." + LiveIntentOmniChannelIdentityModule.CODE,
name = "enabled",
havingValue = "true")
public class LiveIntentOmniChannelIdentityConfiguration {

@Bean
@ConfigurationProperties(prefix = "hooks.modules." + LiveIntentOmniChannelIdentityModule.CODE)
LiveIntentOmniChannelProperties properties() {
return new LiveIntentOmniChannelProperties();
}

@Bean
Module liveIntentOmniChannelIdentityModule(LiveIntentOmniChannelProperties properties,
JacksonMapper mapper,
HttpClient httpClient,
@Value("${logging.sampling-rate:0.01}") double logSamplingRate) {

final LiveIntentOmniChannelIdentityProcessedAuctionRequestHook hook =
new LiveIntentOmniChannelIdentityProcessedAuctionRequestHook(
properties, mapper, httpClient, logSamplingRate);

return new LiveIntentOmniChannelIdentityModule(Collections.singleton(hook));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.prebid.server.hooks.modules.liveintent.omni.channel.identity.model;

import com.iab.openrtb.request.Eid;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor(staticName = "of")
public class IdResResponse {

List<Eid> eids;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.prebid.server.hooks.modules.liveintent.omni.channel.identity.model.config;

import lombok.Data;

@Data
public final class LiveIntentOmniChannelProperties {

long requestTimeoutMs;

String identityResolutionEndpoint;

String authToken;

float treatmentRate;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.prebid.server.hooks.modules.liveintent.omni.channel.identity.v1;

import org.prebid.server.hooks.v1.Hook;
import org.prebid.server.hooks.v1.InvocationContext;
import org.prebid.server.hooks.v1.Module;

import java.util.Collection;

public record LiveIntentOmniChannelIdentityModule(
Collection<? extends Hook<?, ? extends InvocationContext>> hooks) implements Module {

public static final String CODE = "liveintent-omni-channel-identity";

@Override
public String code() {
return CODE;
}

@Override
public Collection<? extends Hook<?, ? extends InvocationContext>> hooks() {
return hooks;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package org.prebid.server.hooks.modules.liveintent.omni.channel.identity.v1.hooks;

import com.iab.openrtb.request.BidRequest;
import com.iab.openrtb.request.Eid;
import com.iab.openrtb.request.User;
import io.vertx.core.Future;
import io.vertx.core.MultiMap;
import org.apache.commons.collections4.ListUtils;
import org.prebid.server.hooks.execution.v1.InvocationResultImpl;
import org.prebid.server.hooks.execution.v1.auction.AuctionRequestPayloadImpl;
import org.prebid.server.hooks.modules.liveintent.omni.channel.identity.model.IdResResponse;
import org.prebid.server.hooks.modules.liveintent.omni.channel.identity.model.config.LiveIntentOmniChannelProperties;
import org.prebid.server.hooks.v1.InvocationAction;
import org.prebid.server.hooks.v1.InvocationResult;
import org.prebid.server.hooks.v1.InvocationStatus;
import org.prebid.server.hooks.v1.auction.AuctionInvocationContext;
import org.prebid.server.hooks.v1.auction.AuctionRequestPayload;
import org.prebid.server.hooks.v1.auction.ProcessedAuctionRequestHook;
import org.prebid.server.json.JacksonMapper;
import org.prebid.server.log.ConditionalLogger;
import org.prebid.server.log.LoggerFactory;
import org.prebid.server.util.HttpUtil;
import org.prebid.server.util.ListUtil;
import org.prebid.server.vertx.httpclient.HttpClient;
import org.prebid.server.vertx.httpclient.model.HttpClientResponse;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;

public class LiveIntentOmniChannelIdentityProcessedAuctionRequestHook implements ProcessedAuctionRequestHook {

private static final ConditionalLogger conditionalLogger = new ConditionalLogger(LoggerFactory.getLogger(
LiveIntentOmniChannelIdentityProcessedAuctionRequestHook.class));

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

private final LiveIntentOmniChannelProperties config;
private final JacksonMapper mapper;
private final HttpClient httpClient;
private final double logSamplingRate;

public LiveIntentOmniChannelIdentityProcessedAuctionRequestHook(LiveIntentOmniChannelProperties config,
JacksonMapper mapper,
HttpClient httpClient,
double logSamplingRate) {

this.config = Objects.requireNonNull(config);
HttpUtil.validateUrlSyntax(config.getIdentityResolutionEndpoint());
this.mapper = Objects.requireNonNull(mapper);
this.httpClient = Objects.requireNonNull(httpClient);
this.logSamplingRate = logSamplingRate;
}

@Override
public Future<InvocationResult<AuctionRequestPayload>> call(AuctionRequestPayload auctionRequestPayload,
AuctionInvocationContext invocationContext) {

return config.getTreatmentRate() > ThreadLocalRandom.current().nextFloat()
? requestIdentities(auctionRequestPayload.bidRequest())
.<InvocationResult<AuctionRequestPayload>>map(this::update)
.onFailure(throwable -> conditionalLogger.error(
"Failed enrichment: %s".formatted(throwable.getMessage()), logSamplingRate))
: noAction();
}

private Future<IdResResponse> requestIdentities(BidRequest bidRequest) {
return httpClient.post(
config.getIdentityResolutionEndpoint(),
headers(),
mapper.encodeToString(bidRequest),
config.getRequestTimeoutMs())
.map(this::processResponse);
}

private MultiMap headers() {
return MultiMap.caseInsensitiveMultiMap()
.add(HttpUtil.AUTHORIZATION_HEADER, "Bearer " + config.getAuthToken());
}

private IdResResponse processResponse(HttpClientResponse response) {
return mapper.decodeValue(response.getBody(), IdResResponse.class);
}

private static Future<InvocationResult<AuctionRequestPayload>> noAction() {
return Future.succeededFuture(InvocationResultImpl.<AuctionRequestPayload>builder()
.status(InvocationStatus.success)
.action(InvocationAction.no_action)
.build());
}

private InvocationResultImpl<AuctionRequestPayload> update(IdResResponse resolutionResult) {
return InvocationResultImpl.<AuctionRequestPayload>builder()
.status(InvocationStatus.success)
.action(InvocationAction.update)
.payloadUpdate(payload -> updatedPayload(payload, resolutionResult.getEids()))
.build();
}

private AuctionRequestPayload updatedPayload(AuctionRequestPayload requestPayload, List<Eid> resolvedEids) {
final List<Eid> eids = ListUtils.emptyIfNull(resolvedEids);
final BidRequest bidRequest = requestPayload.bidRequest();
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 AuctionRequestPayloadImpl.of(bidRequest.toBuilder().user(updatedUser).build());
}

@Override
public String code() {
return CODE;
}
}
Loading
Loading