-
Notifications
You must be signed in to change notification settings - Fork 224
New Module: LiveIntent Omni-channel Identity #3938
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
bf8c1fa
8429f34
459028d
c846d1c
8348d46
c0f6566
4f90c17
c6dc20e
a863d0a
62a4508
e45ccbc
3dbb46a
69fc413
c1ad22f
26288b7
4192fa8
1f1a7cf
c9659ad
3ed7ee8
bedb969
d94e06f
61ceeed
fa66d18
09450d2
b588f6a
1ae6da8
f597339
5f15bf1
d6dfcc7
53ca621
b61986a
e458a4c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 | ||
| public class IdResResponse { | ||
| @JsonProperty("eids") | ||
|
||
| 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; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please, add separation lines between all fields.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
||
| @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() { | ||
|
||
| 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(); | ||
|
||
| 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)); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
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.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to using
NoArgsConstructorin b588f6a