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
162 changes: 133 additions & 29 deletions src/main/java/org/prebid/server/bidder/sparteo/SparteoBidder.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.iab.openrtb.request.App;
import com.iab.openrtb.request.BidRequest;
import com.iab.openrtb.request.Imp;
import com.iab.openrtb.request.Publisher;
Expand All @@ -12,12 +13,14 @@
import com.iab.openrtb.response.BidResponse;
import com.iab.openrtb.response.SeatBid;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.prebid.server.bidder.Bidder;
import org.prebid.server.bidder.model.BidderBid;
import org.prebid.server.bidder.model.BidderCall;
import org.prebid.server.bidder.model.BidderError;
import org.prebid.server.bidder.model.HttpRequest;
import org.prebid.server.bidder.model.Result;
import org.prebid.server.bidder.sparteo.util.SparteoUtil;
import org.prebid.server.exception.PreBidException;
import org.prebid.server.json.DecodeException;
import org.prebid.server.json.JacksonMapper;
Expand All @@ -28,7 +31,9 @@
import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebid;
import org.prebid.server.util.BidderUtil;
import org.prebid.server.util.HttpUtil;
import org.apache.http.client.utils.URIBuilder;

import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
Expand All @@ -38,6 +43,8 @@

public class SparteoBidder implements Bidder<BidRequest> {

private static final String UNKNOWN_VALUE = "unknown";

private static final TypeReference<ExtPrebid<?, ExtImpSparteo>> TYPE_REFERENCE =
new TypeReference<>() { };

Expand All @@ -53,19 +60,18 @@ public SparteoBidder(String endpointUrl, JacksonMapper mapper) {
public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request) {
final List<BidderError> errors = new ArrayList<>();
final List<Imp> modifiedImps = new ArrayList<>();
String siteNetworkId = null;
String networkId = null;

for (Imp imp : request.getImp()) {
if (siteNetworkId == null) {
if (networkId == null) {
try {
siteNetworkId = parseExtImp(imp).getNetworkId();
networkId = parseExtImp(imp).getNetworkId();
} catch (PreBidException e) {
errors.add(BidderError.badInput(
"ignoring imp id=%s, error processing ext: %s".formatted(
imp.getId(), e.getMessage())));
}
}

final ObjectNode modifiedExt = modifyImpExt(imp);
modifiedImps.add(imp.toBuilder().ext(modifiedExt).build());
}
Expand All @@ -74,12 +80,17 @@ public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request
return Result.withErrors(errors);
}

final Site site = request.getSite();
final App app = request.getApp();

final BidRequest outgoingRequest = request.toBuilder()
.imp(modifiedImps)
.site(modifySite(request.getSite(), siteNetworkId, mapper))
.site(modifySite(site, networkId))
.app(modifyApp(app, networkId))
.build();

final HttpRequest<BidRequest> call = BidderUtil.defaultRequest(outgoingRequest, endpointUrl, mapper);
final String finalEndpointUrl = replaceMacros(site, app, networkId, errors);
final HttpRequest<BidRequest> call = BidderUtil.defaultRequest(outgoingRequest, finalEndpointUrl, mapper);

return Result.of(Collections.singletonList(call), errors);
}
Expand All @@ -97,45 +108,138 @@ private static ObjectNode modifyImpExt(Imp imp) {
final ObjectNode sparteoNode = modifiedImpExt.putObject("sparteo");
final JsonNode bidderJsonNode = modifiedImpExt.remove("bidder");
sparteoNode.set("params", bidderJsonNode);

return modifiedImpExt;
}

private Site modifySite(Site site, String siteNetworkId, JacksonMapper mapper) {
if (site == null || site.getPublisher() == null || siteNetworkId == null) {
private Site modifySite(Site site, String networkId) {
if (site == null) {
return site;
}

final Publisher originalPublisher = site.getPublisher();
final ExtPublisher originalExt = originalPublisher.getExt();
final Publisher originalPublisher = site.getPublisher() != null
? site.getPublisher()
: Publisher.builder().build();

final Publisher modifiedPublisher = modifyPublisher(originalPublisher, networkId);

final ExtPublisher modifiedExt = originalExt != null
? ExtPublisher.of(originalExt.getPrebid())
: ExtPublisher.empty();
return site.toBuilder().publisher(modifiedPublisher).build();
}

if (originalExt != null) {
mapper.fillExtension(modifiedExt, originalExt);
private App modifyApp(App app, String networkId) {
if (app == null) {
return app;
}

final JsonNode paramsProperty = modifiedExt.getProperty("params");
final ObjectNode paramsNode;
final Publisher originalPublisher = app.getPublisher() != null
? app.getPublisher()
: Publisher.builder().build();

if (paramsProperty != null && paramsProperty.isObject()) {
paramsNode = (ObjectNode) paramsProperty;
} else {
paramsNode = mapper.mapper().createObjectNode();
modifiedExt.addProperty("params", paramsNode);
}
final Publisher modifiedPublisher = modifyPublisher(originalPublisher, networkId);

return app.toBuilder().publisher(modifiedPublisher).build();
}

private Publisher modifyPublisher(Publisher originalPublisher, String networkId) {
final ExtPublisher originalExt = originalPublisher.getExt();
final ExtPublisher modifiedExt = originalExt == null
? ExtPublisher.empty()
: mapper.mapper().convertValue(originalExt, ExtPublisher.class);

paramsNode.put("networkId", siteNetworkId);
final ObjectNode paramsNode = ensureParamsNode(modifiedExt);
paramsNode.put("networkId", networkId);

final Publisher modifiedPublisher = originalPublisher.toBuilder()
return originalPublisher.toBuilder()
.ext(modifiedExt)
.build();
}

return site.toBuilder()
.publisher(modifiedPublisher)
.build();
private ObjectNode ensureParamsNode(ExtPublisher extPublisher) {
final JsonNode paramsProperty = extPublisher.getProperty("params");
if (paramsProperty != null && paramsProperty.isObject()) {
return (ObjectNode) paramsProperty;
}
final ObjectNode paramsNode = mapper.mapper().createObjectNode();
extPublisher.addProperty("params", paramsNode);

return paramsNode;
}

private String replaceMacros(Site site, App app, String networkId, List<BidderError> errors) {
final String siteDomain = resolveSiteDomain(site);
final String appDomain = resolveAppDomain(app);
final String bundle = resolveBundle(app);

if (UNKNOWN_VALUE.equals(siteDomain)) {
errors.add(BidderError.badInput(
"Domain not found. Missing the site.domain or the site.page field"));
}

if (UNKNOWN_VALUE.equals(bundle)) {
errors.add(BidderError.badInput(
"Bundle not found. Missing the app.bundle field."));
}

return resolveEndpoint(siteDomain, appDomain, networkId, bundle);
}

private String resolveSiteDomain(Site site) {
if (site == null) {
return null;
}

return Optional.of(site)
.map(Site::getDomain)
.map(SparteoUtil::normalizeHostname)
.filter(StringUtils::isNotEmpty)
.or(() -> Optional.ofNullable(site.getPage())
.map(SparteoUtil::normalizeHostname)
.filter(StringUtils::isNotEmpty))
.orElse(UNKNOWN_VALUE);
}

private String resolveAppDomain(App app) {
if (app == null) {
return null;
}

return Optional.of(app)
.map(App::getDomain)
.map(SparteoUtil::normalizeHostname)
.filter(StringUtils::isNotEmpty)
.orElse(UNKNOWN_VALUE);
}

private String resolveBundle(App app) {
if (app == null) {
return null;
}

return Optional.ofNullable(app.getBundle())
.filter(StringUtils::isNotBlank)
.map(String::trim)
.filter(bundle -> !"null".equalsIgnoreCase(bundle))
.orElse(UNKNOWN_VALUE);
}

private String resolveEndpoint(String siteDomain, String appDomain, String networkId, String bundle) {
try {
final URIBuilder uriBuilder = new URIBuilder(endpointUrl);
if (StringUtils.isNotBlank(networkId)) {
uriBuilder.addParameter("network_id", networkId);
}
if (StringUtils.isNotBlank(siteDomain)) {
uriBuilder.addParameter("site_domain", siteDomain);
}
if (StringUtils.isNotBlank(appDomain)) {
uriBuilder.addParameter("app_domain", appDomain);
}
if (StringUtils.isNotBlank(bundle)) {
uriBuilder.addParameter("bundle", bundle);
}
return uriBuilder.build().toString();
} catch (URISyntaxException e) {
throw new PreBidException("Failed to build endpoint URL", e);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.prebid.server.bidder.sparteo.util;

import org.apache.commons.lang3.StringUtils;

import java.net.URI;
import java.net.URISyntaxException;

public final class SparteoUtil {

private SparteoUtil() {
}

public static String normalizeHostname(String host) {
String h = StringUtils.trimToEmpty(host);
if (h.isEmpty()) {
return "";
}

String hostname = null;
try {
hostname = new URI(h).getHost();
} catch (URISyntaxException e) {
}

if (StringUtils.isNotEmpty(hostname)) {
h = hostname;
} else {
if (h.contains(":")) {
h = StringUtils.substringBefore(h, ":");
} else {
h = StringUtils.substringBefore(h, "/");
}
}

h = h.toLowerCase();
h = StringUtils.removeStart(h, "www.");
h = StringUtils.removeEnd(h, ".");

return "null".equals(h) ? "" : h;
}
}
2 changes: 1 addition & 1 deletion src/main/resources/bidder-config/sparteo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ adapters:
usersync:
cookie-family-name: sparteo
iframe:
url: "https://sync.sparteo.com/s2s_sync?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&redirect_url={{redirect_url}}"
url: "https://sync.sparteo.com/s2s_sync?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&gpp={{gpp}}&&gpp_sid={{gpp_sid}}&redirect_url={{redirect_url}}"
support-cors: true
Loading
Loading