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
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,7 @@ public class OptableAttributes {

List<String> ips;

String userAgent;

Long timeout;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import lombok.NoArgsConstructor;

import java.util.Map;
import java.util.Set;

@Data
@NoArgsConstructor
Expand All @@ -31,5 +32,14 @@ public final class OptableTargetingProperties {
@JsonProperty("id-prefix-order")
String idPrefixOrder;

@JsonProperty("optable-inserter-eids-merge")
Set<String> optableInserterEidsMerge = Set.of();

@JsonProperty("optable-inserter-eids-replace")
Set<String> optableInserterEidsReplace = Set.of();

@JsonProperty("optable-inserter-eids-ignore")
Set<String> optableInserterEidsIgnore = Set.of();

CacheProperties cache = new CacheProperties();
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public Future<InvocationResult<AuctionRequestPayload>> call(AuctionRequestPayloa
.compose(targetingResult -> {
moduleContext.setOptableTargetingExecutionTime(
System.currentTimeMillis() - callTargetingAPITimestamp);
return enrichedPayload(targetingResult, moduleContext);
return enrichedPayload(targetingResult, moduleContext, properties);
})
.recover(throwable -> {
moduleContext.setOptableTargetingExecutionTime(
Expand Down Expand Up @@ -143,13 +143,14 @@ private Timeout getHookTimeout(AuctionInvocationContext invocationContext) {
}

private Future<InvocationResult<AuctionRequestPayload>> enrichedPayload(TargetingResult targetingResult,
ModuleContext moduleContext) {
ModuleContext moduleContext,
OptableTargetingProperties properties) {

moduleContext.setTargeting(targetingResult.getAudience());
moduleContext.setEnrichRequestStatus(EnrichmentStatus.success());
return update(
BidRequestCleaner.instance()
.andThen(BidRequestEnricher.of(targetingResult))
.andThen(BidRequestEnricher.of(targetingResult, properties))
::apply,
moduleContext);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
import com.iab.openrtb.request.Data;
import com.iab.openrtb.request.Eid;
import com.iab.openrtb.request.Segment;
import com.iab.openrtb.request.Uid;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.prebid.server.hooks.execution.v1.auction.AuctionRequestPayloadImpl;
import org.prebid.server.hooks.modules.optable.targeting.model.config.OptableTargetingProperties;
import org.prebid.server.hooks.modules.optable.targeting.model.openrtb.Ortb2;
import org.prebid.server.hooks.modules.optable.targeting.model.openrtb.TargetingResult;
import org.prebid.server.hooks.modules.optable.targeting.model.openrtb.User;
Expand All @@ -23,14 +26,18 @@

public class BidRequestEnricher implements PayloadUpdate<AuctionRequestPayload> {

private static final String OPTABLE_CO_INSERTER = "optable.co";

private final TargetingResult targetingResult;
private final OptableTargetingProperties targetingProperties;

private BidRequestEnricher(TargetingResult targetingResult) {
private BidRequestEnricher(TargetingResult targetingResult, OptableTargetingProperties targetingProperties) {
this.targetingResult = targetingResult;
this.targetingProperties = targetingProperties;
}

public static BidRequestEnricher of(TargetingResult targetingResult) {
return new BidRequestEnricher(targetingResult);
public static BidRequestEnricher of(TargetingResult targetingResult, OptableTargetingProperties properties) {
return new BidRequestEnricher(targetingResult, properties);
}

@Override
Expand Down Expand Up @@ -60,18 +67,88 @@ private BidRequest enrichBidRequest(BidRequest bidRequest) {
.build();
}

private static com.iab.openrtb.request.User mergeUserData(com.iab.openrtb.request.User user, User optableUser) {
private com.iab.openrtb.request.User mergeUserData(com.iab.openrtb.request.User user, User optableUser) {
return user.toBuilder()
.eids(mergeEids(user.getEids(), optableUser.getEids()))
.eids(filterOptableEids(mergeEids(user.getEids(), optableUser.getEids())))
.data(mergeData(user.getData(), optableUser.getData()))
.build();
}

private static List<Eid> mergeEids(List<Eid> destination, List<Eid> source) {
return merge(
destination,
source,
Eid::getSource);
private List<Eid> mergeEids(List<Eid> destination, List<Eid> source) {
if (CollectionUtils.isEmpty(destination)) {
return source;
}

if (CollectionUtils.isEmpty(source)) {
return destination;
}

final Map<String, Eid> idToSourceEid = source.stream().collect(Collectors.toMap(
BidRequestEnricher::eidIdExtractor,
Function.identity(),
(a, b) -> b,
HashMap::new));

final Set<String> sourceToReplace = targetingProperties.getOptableInserterEidsReplace();
final Set<String> sourceToMerge = targetingProperties.getOptableInserterEidsMerge()
.stream()
.filter(it -> !sourceToReplace.contains(it)).collect(Collectors.toSet());

final List<Eid> mergedEid = destination.stream()
.map(destinationEid -> idToSourceEid.containsKey(eidIdExtractor(destinationEid))
&& OPTABLE_CO_INSERTER.equals(destinationEid.getInserter())
? resolveEidConflict(
destinationEid,
idToSourceEid.get(eidIdExtractor(destinationEid)),
sourceToMerge,
sourceToReplace)
: destinationEid)
.toList();

return merge(mergedEid, source, BidRequestEnricher::eidIdExtractor);
}

private List<Eid> filterOptableEids(List<Eid> eids) {
if (CollectionUtils.isEmpty(eids)) {
return eids;
}

final Set<String> optableIdsToIgnore = targetingProperties.getOptableInserterEidsIgnore();
if (CollectionUtils.isEmpty(optableIdsToIgnore)) {
return eids;
}

return eids.stream()
.filter(eid -> !OPTABLE_CO_INSERTER.equals(eid.getInserter())
|| !optableIdsToIgnore.contains(eid.getSource()))
.toList();
}

private static Eid resolveEidConflict(Eid destinationEid,
Eid sourceEid,
Set<String> sourceToMerge,
Set<String> sourceToReplace) {

final String eidSource = sourceEid.getSource();

if (sourceToReplace.contains(eidSource)) {
return sourceEid;
}
if (sourceToMerge.contains(eidSource)) {
return mergeEid(destinationEid, sourceEid);
}

return destinationEid;
}

private static Eid mergeEid(Eid destinationEid, Eid sourceEid) {
return destinationEid.toBuilder()
.uids(merge(destinationEid.getUids(), sourceEid.getUids(), Uid::getId))
.build();
}

private static String eidIdExtractor(Eid eid) {
return "%s_%s".formatted(StringUtils.defaultString(eid.getInserter()), eid.getSource());
}

private static List<Data> mergeData(List<Data> destination, List<Data> source) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public static OptableAttributes resolveAttributes(AuctionContext auctionContext,

final OptableAttributes.OptableAttributesBuilder builder = OptableAttributes.builder()
.ips(resolveIp(auctionContext))
.userAgent(resolveUserAgent(auctionContext))
.timeout(timeout);

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

public static List<String> resolveIp(AuctionContext auctionContext) {
public static String resolveUserAgent(AuctionContext auctionContext) {
final Device device = auctionContext.getBidRequest().getDevice();
return device != null ? device.getUa() : null;
}

private static List<String> resolveIp(AuctionContext auctionContext) {
final List<String> result = new ArrayList<>();

final Optional<Device> deviceOpt = Optional.ofNullable(auctionContext.getBidRequest().getDevice());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ public Future<TargetingResult> getTargeting(OptableTargetingProperties propertie
return Future.failedFuture("Can't get targeting");
}

return apiClient.getTargeting(properties, query, attributes.getIps(), timeout);
return apiClient.getTargeting(properties, query, attributes.getIps(), attributes.getUserAgent(), timeout);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

public class QueryBuilder {

private static final String REQUEST_SOURCE = "prebid-server";

private QueryBuilder() {
}

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

sb.append("&osdk=").append(REQUEST_SOURCE);

return sb.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ public interface APIClient {
Future<TargetingResult> getTargeting(OptableTargetingProperties properties,
Query query,
List<String> ips,
String userAgent,
Timeout timeout);
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,12 @@ public APIClientImpl(String endpoint,
public Future<TargetingResult> getTargeting(OptableTargetingProperties properties,
Query query,
List<String> ips,
String userAgent,
Timeout timeout) {

final String uri = resolveEndpoint(properties.getTenant(), properties.getOrigin());
final String queryAsString = query.toQueryString();
final MultiMap headers = headers(properties, ips);
final MultiMap headers = headers(properties, ips, userAgent);

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

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

if (userAgent != null) {
headers.add(HttpUtil.USER_AGENT_HEADER, userAgent);
}
final String apiKey = properties.getApiKey();
if (StringUtils.isNotEmpty(apiKey)) {
headers.add(HttpUtil.AUTHORIZATION_HEADER, "Bearer %s".formatted(apiKey));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,19 @@ public CachedAPIClient(APIClient apiClient, Cache cache, boolean isCircuitBreake
public Future<TargetingResult> getTargeting(OptableTargetingProperties properties,
Query query,
List<String> ips,
String userAgent,
Timeout timeout) {

final CacheProperties cacheProperties = properties.getCache();
if (!cacheProperties.isEnabled()) {
return apiClient.getTargeting(properties, query, ips, timeout);
return apiClient.getTargeting(properties, query, ips, userAgent, timeout);
}

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

return cache.get(createCachingKey(tenant, origin, ips, query, true))
.recover(ignore -> apiClient.getTargeting(properties, query, ips, timeout)
.recover(ignore -> apiClient.getTargeting(properties, query, ips, userAgent, timeout)
.recover(throwable -> isCircuitBreakerEnabled
? Future.succeededFuture(new TargetingResult(null, null))
: Future.failedFuture(throwable))
Expand Down
Loading
Loading