Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
bf8c1fa
WIP - LiveIntent Module
3link Apr 25, 2025
8429f34
Adjust the hook code
3link Apr 25, 2025
459028d
WIP - polish, add config tests
3link Apr 25, 2025
c846d1c
Add unit tests
3link Apr 28, 2025
8348d46
Add auth token
3link Apr 29, 2025
c0f6566
Add README.md
3link May 6, 2025
4f90c17
Fix stage
3link May 6, 2025
c6dc20e
Add logging
3link May 7, 2025
a863d0a
Add docs
3link May 7, 2025
62a4508
Format
3link May 7, 2025
e45ccbc
Impro docs
3link May 7, 2025
3dbb46a
Add IdResResponse decode test
3link May 7, 2025
69fc413
Improve code style
3link May 7, 2025
c1ad22f
Fix Collections API usage
3link May 7, 2025
26288b7
Clean up/format
3link May 14, 2025
4192fa8
Merge branch 'prebid:master' into cm-1776
SuperIzya May 15, 2025
1f1a7cf
Format
3link May 19, 2025
c9659ad
Add treatment rate
3link May 19, 2025
3ed7ee8
Remove superflous JsonProperty annotation
3link Jun 11, 2025
bedb969
Separate field by line
3link Jun 11, 2025
d94e06f
Use RandomGenerator + ThreadLocalRandom instead of Random
3link Jun 11, 2025
61ceeed
Apply code style
3link Jun 11, 2025
fa66d18
Apply style guide: method order
3link Jun 11, 2025
09450d2
Add empty lines to separate test stages
3link Jun 11, 2025
b588f6a
Use NoArgsConstructor instead of Jacksonized
3link Jun 11, 2025
1ae6da8
Merge branch 'master' into cm-1776
3link Jun 11, 2025
f597339
Bump dependency version
3link Jun 11, 2025
5f15bf1
cm-1776: PR issues fixed
Jul 29, 2025
d6dfcc7
Merge branch 'master' into cm-1776
Aug 5, 2025
53ca621
Merge branch 'master' into cm-1776
Aug 5, 2025
b61986a
cm-1776: PR issues fixed
Aug 5, 2025
e458a4c
cm-1776: PR issues fixed
Aug 5, 2025
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
5 changes: 5 additions & 0 deletions extra/bundle/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@
<artifactId>pb-request-correction</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
17 changes: 17 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,17 @@
<?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.25.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,34 @@
package org.prebid.server.hooks.modules.liveintent.omni.channel.identity.config;

import org.prebid.server.hooks.modules.liveintent.omni.channel.identity.model.config.ModuleConfig;
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.Hook;
import org.prebid.server.hooks.v1.InvocationContext;
import org.prebid.server.hooks.v1.Module;
import org.prebid.server.json.JacksonMapper;
import org.prebid.server.vertx.httpclient.HttpClient;
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.Set;

@Configuration
@ConditionalOnProperty(prefix = "hooks." + LiveIntentOmniChannelIdentityModule.CODE, name = "enabled", havingValue = "true")
public class LiveIntentOmniChannelIdentityConfiguration {
@Bean
@ConfigurationProperties(prefix = "hooks.modules." + LiveIntentOmniChannelIdentityModule.CODE)
ModuleConfig moduleConfig() {
return new ModuleConfig();
}

@Bean
Module liveIntentOmniChannelIdentityModule(ModuleConfig config, JacksonMapper mapper, HttpClient httpClient) {
final Set<? extends Hook<?, ? extends InvocationContext>> hooks = Set.of(
new LiveIntentOmniChannelIdentityProcessedAuctionRequestHook(config, mapper, httpClient)
);
return new LiveIntentOmniChannelIdentityModule(hooks);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.prebid.server.hooks.modules.liveintent.omni.channel.identity.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.iab.openrtb.request.Eid;
import lombok.Builder;
import lombok.Data;
import lombok.extern.jackson.Jacksonized;

import java.util.List;

@Builder
@Data
@Jacksonized
Copy link
Collaborator

Choose a reason for hiding this comment

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

We don't use this annotation. We try to avoid experimental features of Lombok as muck as possible. I also believe that it will work without that annotation.

Copy link
Author

@3link 3link Jun 11, 2025

Choose a reason for hiding this comment

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

Changed to using NoArgsConstructor in b588f6a

public class IdResResponse {
@JsonProperty("eids")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Useless annotation, please remove. Since you are using default mapper - it will deserialize by name of the field.

Copy link
Author

Choose a reason for hiding this comment

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

Done in 3ed7ee8

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

import lombok.Data;

@Data
public final class ModuleConfig {
long requestTimeoutMs;
String identityResolutionEndpoint;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please, add separation lines between all fields.

Copy link
Author

Choose a reason for hiding this comment

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

Done in bedb969

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
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,87 @@
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 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.ModuleConfig;
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.vertx.httpclient.HttpClient;
import org.prebid.server.vertx.httpclient.model.HttpClientResponse;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

public class LiveIntentOmniChannelIdentityProcessedAuctionRequestHook implements ProcessedAuctionRequestHook {

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

private final ModuleConfig config;
private final JacksonMapper mapper;
private final HttpClient httpClient;

public LiveIntentOmniChannelIdentityProcessedAuctionRequestHook(
ModuleConfig config,
JacksonMapper mapper,
HttpClient httpClient) {
this.config = Objects.requireNonNull(config);
this.mapper = Objects.requireNonNull(mapper);
this.httpClient = Objects.requireNonNull(httpClient);
}

// TODO: Caching
Copy link
Author

Choose a reason for hiding this comment

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

How and whether or not to cache is being decided now.

@Override
public Future<InvocationResult<AuctionRequestPayload>> call(AuctionRequestPayload auctionRequestPayload, AuctionInvocationContext invocationContext) {
return requestEnrichment(auctionRequestPayload)
.map(resolutionResult ->
InvocationResultImpl.<AuctionRequestPayload>builder()
.status(InvocationStatus.success)
.action(InvocationAction.update)
.payloadUpdate(requestPayload -> updatedPayload(requestPayload, resolutionResult))
.build()
);
}

@Override
public String code() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same here about method order.

Copy link
Author

Choose a reason for hiding this comment

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

Done in fa66d18

return CODE;
}

private AuctionRequestPayload updatedPayload(AuctionRequestPayload requestPayload, IdResResponse idResResponse) {
BidRequest bidRequest = Optional.ofNullable(requestPayload.bidRequest()).orElse(BidRequest.builder().build());
User user = Optional.ofNullable(bidRequest.getUser()).orElse(User.builder().build());

List<Eid> allEids = new ArrayList<>();
allEids.addAll(Optional.ofNullable(user.getEids()).orElse(List.of()));
allEids.addAll(idResResponse.getEids());

User updatedUser = user.toBuilder().eids(allEids).build();
BidRequest updatedBidRequest = requestPayload.bidRequest().toBuilder().user(updatedUser).build();

return AuctionRequestPayloadImpl.of(updatedBidRequest);
}

private Future<IdResResponse> requestEnrichment(AuctionRequestPayload auctionRequestPayload) {
String bidRequestJson = mapper.encodeToString(auctionRequestPayload.bidRequest());
return httpClient.post(
config.getIdentityResolutionEndpoint(),
bidRequestJson,
config.getRequestTimeoutMs())
.map(this::processResponse);
}

private IdResResponse processResponse(HttpClientResponse response) {
return mapper.decodeValue(response.getBody(), IdResResponse.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.prebid.server.hooks.modules.liveintent.omni.channel.identity.model.config;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class ModuleConfigTest {
@Test
public void shouldReturnRequestTimeoutMs() {
ModuleConfig moduleConfig = new ModuleConfig();
moduleConfig.setRequestTimeoutMs(5);
assertThat(moduleConfig.getRequestTimeoutMs()).isEqualTo(5);
}

@Test
public void shouldReturnIdentityResolutionEndpoint() {
ModuleConfig moduleConfig = new ModuleConfig();
moduleConfig.setIdentityResolutionEndpoint("https://test.com/idres");
assertThat(moduleConfig.getIdentityResolutionEndpoint()).isEqualTo("https://test.com/idres");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package org.prebid.server.hooks.modules.liveintent.omni.channel.identity.v1;

import com.fasterxml.jackson.databind.ObjectMapper;
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.config.ModuleConfig;
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.vertx.httpclient.HttpClient;
import org.prebid.server.vertx.httpclient.model.HttpClientResponse;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
public class LiveIntentOmniChannelIdentityProcessedAuctionRequestHookTest {

private ModuleConfig moduleConfig;
private LiveIntentOmniChannelIdentityProcessedAuctionRequestHook target;
private JacksonMapper jacksonMapper;


@BeforeEach
public void setUp() {
ObjectMapper mapper = new ObjectMapper();
jacksonMapper = new JacksonMapper(mapper);

moduleConfig = new ModuleConfig();
moduleConfig.setRequestTimeoutMs(5);
moduleConfig.setIdentityResolutionEndpoint("https://test.com/idres");

target = new LiveIntentOmniChannelIdentityProcessedAuctionRequestHook(moduleConfig, jacksonMapper, httpClient);
}

@Mock
private HttpClient httpClient;

@Test
public void shouldAddResolvedEids() {
Uid providedUid = Uid.builder().id("id1").atype(2).build();
Eid providedEid = Eid.builder().source("some.source.com").uids(List.of(providedUid)).build();

Uid enrichedUid = Uid.builder().id("id2").atype(3).build();
Eid enrichedEid = Eid.builder().source("liveintent.com").uids(List.of(enrichedUid)).build();

User user = User.builder().eids(List.of(providedEid)).build();
BidRequest bidRequest = BidRequest.builder().id("request").user(user).build();

AuctionInvocationContext auctionInvocationContext = AuctionInvocationContextImpl.of(null, null, false, null, null);

HttpClientResponse mockResponse = mock(HttpClientResponse.class);
when(mockResponse.getBody()).thenReturn("{\"eids\": [ { \"source\": \""+ enrichedEid.getSource() + "\", \"uids\": [ { \"atype\": " + enrichedUid.getAtype() + ", \"id\" : \"" + enrichedUid.getId() + "\" } ] } ] }");

when(httpClient.post(moduleConfig.getIdentityResolutionEndpoint(), jacksonMapper.encodeToString(bidRequest), moduleConfig.getRequestTimeoutMs()))
.thenReturn(Future.succeededFuture(mockResponse));

Future<InvocationResult<AuctionRequestPayload>> future = target.call(AuctionRequestPayloadImpl.of(bidRequest), auctionInvocationContext);
InvocationResult<AuctionRequestPayload> result = future.result();

assertThat(future).isNotNull();
assertThat(future.succeeded()).isTrue();
Copy link
Collaborator

Choose a reason for hiding this comment

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

I mean, why bother checking for null? This is unit tests. It will crash anyways.

Copy link
Contributor

Choose a reason for hiding this comment

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

Done in 5f15bf1

assertThat(result).isNotNull();
assertThat(result.status()).isEqualTo(InvocationStatus.success);
assertThat(result.action()).isEqualTo(InvocationAction.update);
assertThat(result.payloadUpdate().apply(AuctionRequestPayloadImpl.of(bidRequest)).bidRequest().getUser().getEids()).isEqualTo(List.of(providedEid, enrichedEid));
}

@Test
public void shouldCreateUserWhenNotPresent() {
Uid enrichedUid = Uid.builder().id("id2").atype(3).build();
Eid enrichedEid = Eid.builder().source("liveintent.com").uids(List.of(enrichedUid)).build();

BidRequest bidRequest = BidRequest.builder().id("request").build();

AuctionInvocationContext auctionInvocationContext = AuctionInvocationContextImpl.of(null, null, false, null, null);

HttpClientResponse mockResponse = mock(HttpClientResponse.class);
when(mockResponse.getBody()).thenReturn("{\"eids\": [{ \"source\": \""+ enrichedEid.getSource() + "\", \"uids\": [{ \"atype\": " + enrichedUid.getAtype() + ", \"id\" : \"" + enrichedUid.getId() + "\" }]}]}");

when(httpClient.post(moduleConfig.getIdentityResolutionEndpoint(), jacksonMapper.encodeToString(bidRequest), moduleConfig.getRequestTimeoutMs()))
.thenReturn(Future.succeededFuture(mockResponse));

Future<InvocationResult<AuctionRequestPayload>> future = target.call(AuctionRequestPayloadImpl.of(bidRequest), auctionInvocationContext);
InvocationResult<AuctionRequestPayload> result = future.result();

assertThat(future).isNotNull();
assertThat(future.succeeded()).isTrue();
assertThat(result).isNotNull();
assertThat(result.status()).isEqualTo(InvocationStatus.success);
assertThat(result.action()).isEqualTo(InvocationAction.update);
assertThat(result.payloadUpdate().apply(AuctionRequestPayloadImpl.of(bidRequest)).bidRequest().getUser().getEids()).isEqualTo(List.of(enrichedEid));
}
}
1 change: 1 addition & 0 deletions extra/modules/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<module>pb-response-correction</module>
<module>greenbids-real-time-data</module>
<module>pb-request-correction</module>
<module>live-intent-omni-channel-identity</module>
</modules>

<dependencyManagement>
Expand Down