Skip to content

Commit aa4f82a

Browse files
authored
optable-targeting: Optimize Optable requests for more precise ranging and performance insights (#4222)
1 parent 0359db8 commit aa4f82a

File tree

15 files changed

+364
-68
lines changed

15 files changed

+364
-68
lines changed

extra/modules/optable-targeting/src/main/java/org/prebid/server/hooks/modules/optable/targeting/model/OptableAttributes.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,7 @@ public class OptableAttributes {
2020

2121
List<String> ips;
2222

23+
String userAgent;
24+
2325
Long timeout;
2426
}

extra/modules/optable-targeting/src/main/java/org/prebid/server/hooks/modules/optable/targeting/model/config/OptableTargetingProperties.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import lombok.NoArgsConstructor;
66

77
import java.util.Map;
8+
import java.util.Set;
89

910
@Data
1011
@NoArgsConstructor
@@ -31,5 +32,14 @@ public final class OptableTargetingProperties {
3132
@JsonProperty("id-prefix-order")
3233
String idPrefixOrder;
3334

35+
@JsonProperty("optable-inserter-eids-merge")
36+
Set<String> optableInserterEidsMerge = Set.of();
37+
38+
@JsonProperty("optable-inserter-eids-replace")
39+
Set<String> optableInserterEidsReplace = Set.of();
40+
41+
@JsonProperty("optable-inserter-eids-ignore")
42+
Set<String> optableInserterEidsIgnore = Set.of();
43+
3444
CacheProperties cache = new CacheProperties();
3545
}

extra/modules/optable-targeting/src/main/java/org/prebid/server/hooks/modules/optable/targeting/v1/OptableTargetingProcessedAuctionRequestHook.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public Future<InvocationResult<AuctionRequestPayload>> call(AuctionRequestPayloa
8989
.compose(targetingResult -> {
9090
moduleContext.setOptableTargetingExecutionTime(
9191
System.currentTimeMillis() - callTargetingAPITimestamp);
92-
return enrichedPayload(targetingResult, moduleContext);
92+
return enrichedPayload(targetingResult, moduleContext, properties);
9393
})
9494
.recover(throwable -> {
9595
moduleContext.setOptableTargetingExecutionTime(
@@ -143,13 +143,14 @@ private Timeout getHookTimeout(AuctionInvocationContext invocationContext) {
143143
}
144144

145145
private Future<InvocationResult<AuctionRequestPayload>> enrichedPayload(TargetingResult targetingResult,
146-
ModuleContext moduleContext) {
146+
ModuleContext moduleContext,
147+
OptableTargetingProperties properties) {
147148

148149
moduleContext.setTargeting(targetingResult.getAudience());
149150
moduleContext.setEnrichRequestStatus(EnrichmentStatus.success());
150151
return update(
151152
BidRequestCleaner.instance()
152-
.andThen(BidRequestEnricher.of(targetingResult))
153+
.andThen(BidRequestEnricher.of(targetingResult, properties))
153154
::apply,
154155
moduleContext);
155156
}

extra/modules/optable-targeting/src/main/java/org/prebid/server/hooks/modules/optable/targeting/v1/core/BidRequestEnricher.java

Lines changed: 87 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
import com.iab.openrtb.request.Data;
55
import com.iab.openrtb.request.Eid;
66
import com.iab.openrtb.request.Segment;
7+
import com.iab.openrtb.request.Uid;
78
import org.apache.commons.collections4.CollectionUtils;
9+
import org.apache.commons.lang3.StringUtils;
810
import org.prebid.server.hooks.execution.v1.auction.AuctionRequestPayloadImpl;
11+
import org.prebid.server.hooks.modules.optable.targeting.model.config.OptableTargetingProperties;
912
import org.prebid.server.hooks.modules.optable.targeting.model.openrtb.Ortb2;
1013
import org.prebid.server.hooks.modules.optable.targeting.model.openrtb.TargetingResult;
1114
import org.prebid.server.hooks.modules.optable.targeting.model.openrtb.User;
@@ -23,14 +26,18 @@
2326

2427
public class BidRequestEnricher implements PayloadUpdate<AuctionRequestPayload> {
2528

29+
private static final String OPTABLE_CO_INSERTER = "optable.co";
30+
2631
private final TargetingResult targetingResult;
32+
private final OptableTargetingProperties targetingProperties;
2733

28-
private BidRequestEnricher(TargetingResult targetingResult) {
34+
private BidRequestEnricher(TargetingResult targetingResult, OptableTargetingProperties targetingProperties) {
2935
this.targetingResult = targetingResult;
36+
this.targetingProperties = targetingProperties;
3037
}
3138

32-
public static BidRequestEnricher of(TargetingResult targetingResult) {
33-
return new BidRequestEnricher(targetingResult);
39+
public static BidRequestEnricher of(TargetingResult targetingResult, OptableTargetingProperties properties) {
40+
return new BidRequestEnricher(targetingResult, properties);
3441
}
3542

3643
@Override
@@ -60,18 +67,88 @@ private BidRequest enrichBidRequest(BidRequest bidRequest) {
6067
.build();
6168
}
6269

63-
private static com.iab.openrtb.request.User mergeUserData(com.iab.openrtb.request.User user, User optableUser) {
70+
private com.iab.openrtb.request.User mergeUserData(com.iab.openrtb.request.User user, User optableUser) {
6471
return user.toBuilder()
65-
.eids(mergeEids(user.getEids(), optableUser.getEids()))
72+
.eids(filterOptableEids(mergeEids(user.getEids(), optableUser.getEids())))
6673
.data(mergeData(user.getData(), optableUser.getData()))
6774
.build();
6875
}
6976

70-
private static List<Eid> mergeEids(List<Eid> destination, List<Eid> source) {
71-
return merge(
72-
destination,
73-
source,
74-
Eid::getSource);
77+
private List<Eid> mergeEids(List<Eid> destination, List<Eid> source) {
78+
if (CollectionUtils.isEmpty(destination)) {
79+
return source;
80+
}
81+
82+
if (CollectionUtils.isEmpty(source)) {
83+
return destination;
84+
}
85+
86+
final Map<String, Eid> idToSourceEid = source.stream().collect(Collectors.toMap(
87+
BidRequestEnricher::eidIdExtractor,
88+
Function.identity(),
89+
(a, b) -> b,
90+
HashMap::new));
91+
92+
final Set<String> sourceToReplace = targetingProperties.getOptableInserterEidsReplace();
93+
final Set<String> sourceToMerge = targetingProperties.getOptableInserterEidsMerge()
94+
.stream()
95+
.filter(it -> !sourceToReplace.contains(it)).collect(Collectors.toSet());
96+
97+
final List<Eid> mergedEid = destination.stream()
98+
.map(destinationEid -> idToSourceEid.containsKey(eidIdExtractor(destinationEid))
99+
&& OPTABLE_CO_INSERTER.equals(destinationEid.getInserter())
100+
? resolveEidConflict(
101+
destinationEid,
102+
idToSourceEid.get(eidIdExtractor(destinationEid)),
103+
sourceToMerge,
104+
sourceToReplace)
105+
: destinationEid)
106+
.toList();
107+
108+
return merge(mergedEid, source, BidRequestEnricher::eidIdExtractor);
109+
}
110+
111+
private List<Eid> filterOptableEids(List<Eid> eids) {
112+
if (CollectionUtils.isEmpty(eids)) {
113+
return eids;
114+
}
115+
116+
final Set<String> optableIdsToIgnore = targetingProperties.getOptableInserterEidsIgnore();
117+
if (CollectionUtils.isEmpty(optableIdsToIgnore)) {
118+
return eids;
119+
}
120+
121+
return eids.stream()
122+
.filter(eid -> !OPTABLE_CO_INSERTER.equals(eid.getInserter())
123+
|| !optableIdsToIgnore.contains(eid.getSource()))
124+
.toList();
125+
}
126+
127+
private static Eid resolveEidConflict(Eid destinationEid,
128+
Eid sourceEid,
129+
Set<String> sourceToMerge,
130+
Set<String> sourceToReplace) {
131+
132+
final String eidSource = sourceEid.getSource();
133+
134+
if (sourceToReplace.contains(eidSource)) {
135+
return sourceEid;
136+
}
137+
if (sourceToMerge.contains(eidSource)) {
138+
return mergeEid(destinationEid, sourceEid);
139+
}
140+
141+
return destinationEid;
142+
}
143+
144+
private static Eid mergeEid(Eid destinationEid, Eid sourceEid) {
145+
return destinationEid.toBuilder()
146+
.uids(merge(destinationEid.getUids(), sourceEid.getUids(), Uid::getId))
147+
.build();
148+
}
149+
150+
private static String eidIdExtractor(Eid eid) {
151+
return "%s_%s".formatted(StringUtils.defaultString(eid.getInserter()), eid.getSource());
75152
}
76153

77154
private static List<Data> mergeData(List<Data> destination, List<Data> source) {

extra/modules/optable-targeting/src/main/java/org/prebid/server/hooks/modules/optable/targeting/v1/core/OptableAttributesResolver.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public static OptableAttributes resolveAttributes(AuctionContext auctionContext,
2222

2323
final OptableAttributes.OptableAttributesBuilder builder = OptableAttributes.builder()
2424
.ips(resolveIp(auctionContext))
25+
.userAgent(resolveUserAgent(auctionContext))
2526
.timeout(timeout);
2627

2728
if (tcfContext.isConsentValid()) {
@@ -39,7 +40,12 @@ public static OptableAttributes resolveAttributes(AuctionContext auctionContext,
3940
return builder.build();
4041
}
4142

42-
public static List<String> resolveIp(AuctionContext auctionContext) {
43+
public static String resolveUserAgent(AuctionContext auctionContext) {
44+
final Device device = auctionContext.getBidRequest().getDevice();
45+
return device != null ? device.getUa() : null;
46+
}
47+
48+
private static List<String> resolveIp(AuctionContext auctionContext) {
4349
final List<String> result = new ArrayList<>();
4450

4551
final Optional<Device> deviceOpt = Optional.ofNullable(auctionContext.getBidRequest().getDevice());

extra/modules/optable-targeting/src/main/java/org/prebid/server/hooks/modules/optable/targeting/v1/core/OptableTargeting.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,6 @@ public Future<TargetingResult> getTargeting(OptableTargetingProperties propertie
3434
return Future.failedFuture("Can't get targeting");
3535
}
3636

37-
return apiClient.getTargeting(properties, query, attributes.getIps(), timeout);
37+
return apiClient.getTargeting(properties, query, attributes.getIps(), attributes.getUserAgent(), timeout);
3838
}
3939
}

extra/modules/optable-targeting/src/main/java/org/prebid/server/hooks/modules/optable/targeting/v1/core/QueryBuilder.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
public class QueryBuilder {
2323

24+
private static final String REQUEST_SOURCE = "prebid-server";
25+
2426
private QueryBuilder() {
2527
}
2628

@@ -81,6 +83,8 @@ private static String buildAttributesString(OptableAttributes optableAttributes)
8183
Optional.ofNullable(optableAttributes.getTimeout())
8284
.ifPresent(timeout -> sb.append("&timeout=").append(timeout).append("ms"));
8385

86+
sb.append("&osdk=").append(REQUEST_SOURCE);
87+
8488
return sb.toString();
8589
}
8690
}

extra/modules/optable-targeting/src/main/java/org/prebid/server/hooks/modules/optable/targeting/v1/net/APIClient.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ public interface APIClient {
1313
Future<TargetingResult> getTargeting(OptableTargetingProperties properties,
1414
Query query,
1515
List<String> ips,
16+
String userAgent,
1617
Timeout timeout);
1718
}

extra/modules/optable-targeting/src/main/java/org/prebid/server/hooks/modules/optable/targeting/v1/net/APIClientImpl.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,12 @@ public APIClientImpl(String endpoint,
4949
public Future<TargetingResult> getTargeting(OptableTargetingProperties properties,
5050
Query query,
5151
List<String> ips,
52+
String userAgent,
5253
Timeout timeout) {
5354

5455
final String uri = resolveEndpoint(properties.getTenant(), properties.getOrigin());
5556
final String queryAsString = query.toQueryString();
56-
final MultiMap headers = headers(properties, ips);
57+
final MultiMap headers = headers(properties, ips, userAgent);
5758

5859
return httpClient.get(uri + queryAsString, headers, timeout.remaining())
5960
.compose(this::validateResponse)
@@ -67,10 +68,13 @@ private String resolveEndpoint(String tenant, String origin) {
6768
.replace(ORIGIN, origin);
6869
}
6970

70-
private static MultiMap headers(OptableTargetingProperties properties, List<String> ips) {
71+
private static MultiMap headers(OptableTargetingProperties properties, List<String> ips, String userAgent) {
7172
final MultiMap headers = HeadersMultiMap.headers()
7273
.add(HttpUtil.ACCEPT_HEADER, "application/json");
7374

75+
if (userAgent != null) {
76+
headers.add(HttpUtil.USER_AGENT_HEADER, userAgent);
77+
}
7478
final String apiKey = properties.getApiKey();
7579
if (StringUtils.isNotEmpty(apiKey)) {
7680
headers.add(HttpUtil.AUTHORIZATION_HEADER, "Bearer %s".formatted(apiKey));

extra/modules/optable-targeting/src/main/java/org/prebid/server/hooks/modules/optable/targeting/v1/net/CachedAPIClient.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,19 @@ public CachedAPIClient(APIClient apiClient, Cache cache, boolean isCircuitBreake
2828
public Future<TargetingResult> getTargeting(OptableTargetingProperties properties,
2929
Query query,
3030
List<String> ips,
31+
String userAgent,
3132
Timeout timeout) {
3233

3334
final CacheProperties cacheProperties = properties.getCache();
3435
if (!cacheProperties.isEnabled()) {
35-
return apiClient.getTargeting(properties, query, ips, timeout);
36+
return apiClient.getTargeting(properties, query, ips, userAgent, timeout);
3637
}
3738

3839
final String tenant = properties.getTenant();
3940
final String origin = properties.getOrigin();
4041

4142
return cache.get(createCachingKey(tenant, origin, ips, query, true))
42-
.recover(ignore -> apiClient.getTargeting(properties, query, ips, timeout)
43+
.recover(ignore -> apiClient.getTargeting(properties, query, ips, userAgent, timeout)
4344
.recover(throwable -> isCircuitBreakerEnabled
4445
? Future.succeededFuture(new TargetingResult(null, null))
4546
: Future.failedFuture(throwable))

0 commit comments

Comments
 (0)