diff --git a/extra/bundle/pom.xml b/extra/bundle/pom.xml
index 32c8ef2b187..a9b6fcc0990 100644
--- a/extra/bundle/pom.xml
+++ b/extra/bundle/pom.xml
@@ -65,6 +65,11 @@
wurfl-devicedetection
${project.version}
+
+ org.prebid.server.hooks.modules
+ live-intent-omni-channel-identity
+ ${project.version}
+
diff --git a/extra/modules/live-intent-omni-channel-identity/README.md b/extra/modules/live-intent-omni-channel-identity/README.md
new file mode 100644
index 00000000000..be5ad801ec1
--- /dev/null
+++ b/extra/modules/live-intent-omni-channel-identity/README.md
@@ -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
+```
+
diff --git a/extra/modules/live-intent-omni-channel-identity/pom.xml b/extra/modules/live-intent-omni-channel-identity/pom.xml
new file mode 100644
index 00000000000..8b64db27a7d
--- /dev/null
+++ b/extra/modules/live-intent-omni-channel-identity/pom.xml
@@ -0,0 +1,18 @@
+
+
+ 4.0.0
+
+ org.prebid.server.hooks.modules
+ all-modules
+ 3.31.0-SNAPSHOT
+
+
+ live-intent-omni-channel-identity
+
+ live-intent-omni-channel-identity
+ LiveIntent Omni-Channel Identity
+
+
+
+
diff --git a/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/config/LiveIntentOmniChannelIdentityConfiguration.java b/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/config/LiveIntentOmniChannelIdentityConfiguration.java
new file mode 100644
index 00000000000..e3f286e0120
--- /dev/null
+++ b/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/config/LiveIntentOmniChannelIdentityConfiguration.java
@@ -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));
+ }
+}
diff --git a/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/model/IdResResponse.java b/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/model/IdResResponse.java
new file mode 100644
index 00000000000..35b22adca0d
--- /dev/null
+++ b/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/model/IdResResponse.java
@@ -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 eids;
+}
diff --git a/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/model/config/LiveIntentOmniChannelProperties.java b/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/model/config/LiveIntentOmniChannelProperties.java
new file mode 100644
index 00000000000..b6a61b0ca8f
--- /dev/null
+++ b/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/model/config/LiveIntentOmniChannelProperties.java
@@ -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;
+}
diff --git a/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/LiveIntentOmniChannelIdentityModule.java b/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/LiveIntentOmniChannelIdentityModule.java
new file mode 100644
index 00000000000..0ffdce8b436
--- /dev/null
+++ b/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/LiveIntentOmniChannelIdentityModule.java
@@ -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;
+ }
+}
diff --git a/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/hooks/LiveIntentOmniChannelIdentityProcessedAuctionRequestHook.java b/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/hooks/LiveIntentOmniChannelIdentityProcessedAuctionRequestHook.java
new file mode 100644
index 00000000000..5c3e43f9952
--- /dev/null
+++ b/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/hooks/LiveIntentOmniChannelIdentityProcessedAuctionRequestHook.java
@@ -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> call(AuctionRequestPayload auctionRequestPayload,
+ AuctionInvocationContext invocationContext) {
+
+ return config.getTreatmentRate() > ThreadLocalRandom.current().nextFloat()
+ ? requestIdentities(auctionRequestPayload.bidRequest())
+ .>map(this::update)
+ .onFailure(throwable -> conditionalLogger.error(
+ "Failed enrichment: %s".formatted(throwable.getMessage()), logSamplingRate))
+ : noAction();
+ }
+
+ private Future 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> noAction() {
+ return Future.succeededFuture(InvocationResultImpl.builder()
+ .status(InvocationStatus.success)
+ .action(InvocationAction.no_action)
+ .build());
+ }
+
+ private InvocationResultImpl update(IdResResponse resolutionResult) {
+ return InvocationResultImpl.builder()
+ .status(InvocationStatus.success)
+ .action(InvocationAction.update)
+ .payloadUpdate(payload -> updatedPayload(payload, resolutionResult.getEids()))
+ .build();
+ }
+
+ private AuctionRequestPayload updatedPayload(AuctionRequestPayload requestPayload, List resolvedEids) {
+ final List 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;
+ }
+}
diff --git a/extra/modules/live-intent-omni-channel-identity/src/test/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/LiveIntentOmniChannelIdentityProcessedAuctionRequestHookTest.java b/extra/modules/live-intent-omni-channel-identity/src/test/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/LiveIntentOmniChannelIdentityProcessedAuctionRequestHookTest.java
new file mode 100644
index 00000000000..9dc53916980
--- /dev/null
+++ b/extra/modules/live-intent-omni-channel-identity/src/test/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/LiveIntentOmniChannelIdentityProcessedAuctionRequestHookTest.java
@@ -0,0 +1,202 @@
+package org.prebid.server.hooks.modules.liveintent.omni.channel.identity.v1;
+
+import com.iab.openrtb.request.BidRequest;
+import com.iab.openrtb.request.Eid;
+import com.iab.openrtb.request.Uid;
+import com.iab.openrtb.request.User;
+import io.vertx.core.Future;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.prebid.server.hooks.execution.v1.auction.AuctionInvocationContextImpl;
+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.modules.liveintent.omni.channel.identity.v1.hooks.LiveIntentOmniChannelIdentityProcessedAuctionRequestHook;
+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.json.JacksonMapper;
+import org.prebid.server.json.ObjectMapperProvider;
+import org.prebid.server.vertx.httpclient.HttpClient;
+import org.prebid.server.vertx.httpclient.model.HttpClientResponse;
+
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mock.Strictness.LENIENT;
+import static org.mockito.Mockito.verify;
+
+@ExtendWith(MockitoExtension.class)
+public class LiveIntentOmniChannelIdentityProcessedAuctionRequestHookTest {
+
+ private static final JacksonMapper MAPPER = new JacksonMapper(ObjectMapperProvider.mapper());
+
+ @Mock
+ private HttpClient httpClient;
+
+ @Mock(strictness = LENIENT)
+ private LiveIntentOmniChannelProperties properties;
+
+ private LiveIntentOmniChannelIdentityProcessedAuctionRequestHook target;
+
+ @BeforeEach
+ public void setUp() {
+ 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);
+
+ target = new LiveIntentOmniChannelIdentityProcessedAuctionRequestHook(
+ properties, MAPPER, httpClient, 0.01d);
+ }
+
+ @Test
+ public void creationShouldFailOnInvalidIdentityUrl() {
+ given(properties.getIdentityResolutionEndpoint()).willReturn("invalid_url");
+ assertThatIllegalArgumentException().isThrownBy(() ->
+ new LiveIntentOmniChannelIdentityProcessedAuctionRequestHook(
+ properties, MAPPER, httpClient, 0.01d));
+ }
+
+ @Test
+ public void callShouldEnrichUserEidsWithRequestedEids() {
+ // 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 Eid expectedEid = Eid.builder()
+ .source("liveintent.com")
+ .uids(singletonList(Uid.builder().id("id2").atype(3).build()))
+ .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)));
+
+ final AuctionInvocationContext auctionInvocationContext = AuctionInvocationContextImpl.of(
+ null, null, false, null, null);
+
+ // when
+ final InvocationResult result =
+ target.call(AuctionRequestPayloadImpl.of(givenBidRequest), auctionInvocationContext).result();
+
+ // then
+ assertThat(result.status()).isEqualTo(InvocationStatus.success);
+ assertThat(result.action()).isEqualTo(InvocationAction.update);
+ assertThat(result.payloadUpdate().apply(AuctionRequestPayloadImpl.of(givenBidRequest)))
+ .extracting(AuctionRequestPayload::bidRequest)
+ .extracting(BidRequest::getUser)
+ .extracting(User::getEids)
+ .isEqualTo(List.of(givenEid, expectedEid));
+
+ 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 callShouldCreateUserAndUseRequestedEidsWhenUserIsAbsent() {
+ // given
+ final BidRequest givenBidRequest = BidRequest.builder().id("request").user(null).build();
+
+ final Eid expectedEid = Eid.builder()
+ .source("liveintent.com")
+ .uids(singletonList(Uid.builder().id("id2").atype(3).build()))
+ .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)));
+
+ final AuctionInvocationContext auctionInvocationContext = AuctionInvocationContextImpl.of(
+ null, null, false, null, null);
+
+ // when
+ final InvocationResult result =
+ target.call(AuctionRequestPayloadImpl.of(givenBidRequest), auctionInvocationContext).result();
+
+ // then
+ assertThat(result.status()).isEqualTo(InvocationStatus.success);
+ assertThat(result.action()).isEqualTo(InvocationAction.update);
+ assertThat(result.payloadUpdate().apply(AuctionRequestPayloadImpl.of(givenBidRequest)))
+ .extracting(AuctionRequestPayload::bidRequest)
+ .extracting(BidRequest::getUser)
+ .extracting(User::getEids)
+ .isEqualTo(List.of(expectedEid));
+
+ 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 callShouldReturnNoActionSuccessfullyWhenTreatmentRateIsLowerThanThreshold() {
+ // given
+ final Uid givenUid = Uid.builder().id("id1").atype(2).build();
+ final Eid givebEid = Eid.builder().source("some.source.com").uids(singletonList(givenUid)).build();
+ final User givenUser = User.builder().eids(singletonList(givebEid)).build();
+ final BidRequest givenBidRequest = BidRequest.builder().id("request").user(givenUser).build();
+
+ final AuctionInvocationContext auctionInvocationContext = AuctionInvocationContextImpl.of(
+ null, null, false, null, null);
+
+ given(properties.getTreatmentRate()).willReturn(0.0f);
+
+ // when
+ final InvocationResult result = target.call(
+ AuctionRequestPayloadImpl.of(givenBidRequest),
+ auctionInvocationContext)
+ .result();
+
+ // then
+ assertThat(result.status()).isEqualTo(InvocationStatus.success);
+ assertThat(result.action()).isEqualTo(InvocationAction.no_action);
+ assertThat(result.payloadUpdate()).isNull();
+ }
+
+ @Test
+ public void callShouldReturnFailureWhenRequestingEidsIsFailed() {
+ // given
+ final Uid givenUid = Uid.builder().id("id1").atype(2).build();
+ final Eid givebEid = Eid.builder().source("some.source.com").uids(singletonList(givenUid)).build();
+ final User givenUser = User.builder().eids(singletonList(givebEid)).build();
+ final BidRequest givenBidRequest = BidRequest.builder().id("request").user(givenUser).build();
+
+ final AuctionInvocationContext auctionInvocationContext = AuctionInvocationContextImpl.of(
+ null, null, false, null, null);
+
+ given(httpClient.post(any(), any(), any(), anyLong()))
+ .willReturn(Future.failedFuture(new TimeoutException("Timeout exceeded")));
+
+ // when
+ final Future> result = target.call(
+ AuctionRequestPayloadImpl.of(givenBidRequest),
+ auctionInvocationContext);
+
+ // then
+ assertThat(result.failed()).isTrue();
+ assertThat(result.cause()).isInstanceOf(TimeoutException.class);
+ assertThat(result.cause())
+ .isInstanceOf(TimeoutException.class)
+ .hasMessage("Timeout exceeded");
+ }
+}
diff --git a/extra/modules/pom.xml b/extra/modules/pom.xml
index bb71bcd798b..00c3294b1a9 100644
--- a/extra/modules/pom.xml
+++ b/extra/modules/pom.xml
@@ -26,6 +26,7 @@
pb-request-correction
optable-targeting
wurfl-devicedetection
+ live-intent-omni-channel-identity