Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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 @@ -10,6 +10,8 @@
@Builder(toBuilder = true)
public class OptableAttributes {

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

String gpp;

Set<Integer> gppSid;
Expand All @@ -20,5 +22,11 @@ public class OptableAttributes {

List<String> ips;

String userAgent;

Long timeout;

public String getRequestSource() {
return REQUEST_SOURCE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;
import java.util.Map;

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

@JsonProperty("no-skip")
List<String> noSkip;

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

@JsonProperty("optable-inserter-eids-replace")
List<String> optableInserterEidsReplace = List.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,13 +4,16 @@
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.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;
import org.prebid.server.hooks.v1.PayloadUpdate;
import org.prebid.server.hooks.v1.auction.AuctionRequestPayload;
import org.prebid.server.util.ObjectUtil;

import java.util.HashMap;
import java.util.List;
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 All @@ -56,22 +63,82 @@ private BidRequest enrichBidRequest(BidRequest bidRequest) {
.orElseGet(() -> com.iab.openrtb.request.User.builder().build());

return bidRequest.toBuilder()
.user(mergeUserData(bidRequestUser, optableUser))
.user(mergeUserData(bidRequestUser, optableUser, targetingProperties))
.build();
}

private static com.iab.openrtb.request.User mergeUserData(com.iab.openrtb.request.User user, User optableUser) {
private static com.iab.openrtb.request.User mergeUserData(com.iab.openrtb.request.User user,
User optableUser,
OptableTargetingProperties targetingProperties) {

return user.toBuilder()
.eids(mergeEids(user.getEids(), optableUser.getEids()))
.eids(mergeEids(user.getEids(), optableUser.getEids(), targetingProperties))
.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 static List<Eid> mergeEids(List<Eid> destination,
List<Eid> source,
OptableTargetingProperties targetingProperties) {

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 List<String> sourceToReplace = targetingProperties.getOptableInserterEidsReplace();
final List<String> sourceToMerge = targetingProperties.getOptableInserterEidsMerge()
.stream()
.filter(it -> !sourceToReplace.contains(it)).toList();

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

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

private static Eid resolveEidConflict(Eid destinationEid, Eid sourceEid, List<String> sourceToMerge,
List<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(
ObjectUtil.getIfNotNullOrDefault(eid, 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 @@ -81,6 +81,8 @@ private static String buildAttributesString(OptableAttributes optableAttributes)
Optional.ofNullable(optableAttributes.getTimeout())
.ifPresent(timeout -> sb.append("&timeout=").append(timeout).append("ms"));

sb.append("&osdk=").append(optableAttributes.getRequestSource());

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