Skip to content

Commit c6b30de

Browse files
committed
Sparteo: add required query params to adapter endpoint
1 parent 8bc1647 commit c6b30de

File tree

5 files changed

+667
-285
lines changed

5 files changed

+667
-285
lines changed

src/main/java/org/prebid/server/bidder/sparteo/SparteoBidder.java

Lines changed: 185 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.fasterxml.jackson.core.type.TypeReference;
55
import com.fasterxml.jackson.databind.JsonNode;
66
import com.fasterxml.jackson.databind.node.ObjectNode;
7+
import com.iab.openrtb.request.App;
78
import com.iab.openrtb.request.BidRequest;
89
import com.iab.openrtb.request.Imp;
910
import com.iab.openrtb.request.Publisher;
@@ -12,6 +13,7 @@
1213
import com.iab.openrtb.response.BidResponse;
1314
import com.iab.openrtb.response.SeatBid;
1415
import org.apache.commons.collections4.CollectionUtils;
16+
import org.apache.commons.lang3.StringUtils;
1517
import org.prebid.server.bidder.Bidder;
1618
import org.prebid.server.bidder.model.BidderBid;
1719
import org.prebid.server.bidder.model.BidderCall;
@@ -29,6 +31,8 @@
2931
import org.prebid.server.util.BidderUtil;
3032
import org.prebid.server.util.HttpUtil;
3133

34+
import java.net.URI;
35+
import java.net.URISyntaxException;
3236
import java.util.ArrayList;
3337
import java.util.Collection;
3438
import java.util.Collections;
@@ -38,34 +42,39 @@
3842

3943
public class SparteoBidder implements Bidder<BidRequest> {
4044

45+
private static final String NETWORK_ID_MACRO = "{{NetworkId}}";
46+
private static final String SITE_DOMAIN_QUERY_MACRO = "{{SiteDomainQuery}}";
47+
private static final String APP_DOMAIN_QUERY_MACRO = "{{AppDomainQuery}}";
48+
private static final String BUNDLE_QUERY_MACRO = "{{BundleQuery}}";
49+
private static final String UNKNOWN_VALUE = "unknown";
50+
4151
private static final TypeReference<ExtPrebid<?, ExtImpSparteo>> TYPE_REFERENCE =
4252
new TypeReference<>() { };
4353

4454
private final String endpointUrl;
4555
private final JacksonMapper mapper;
4656

4757
public SparteoBidder(String endpointUrl, JacksonMapper mapper) {
48-
this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl));
58+
this.endpointUrl = HttpUtil.validateUrlSyntax(Objects.requireNonNull(endpointUrl));
4959
this.mapper = Objects.requireNonNull(mapper);
5060
}
5161

5262
@Override
5363
public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request) {
5464
final List<BidderError> errors = new ArrayList<>();
5565
final List<Imp> modifiedImps = new ArrayList<>();
56-
String siteNetworkId = null;
66+
String networkId = null;
5767

5868
for (Imp imp : request.getImp()) {
59-
if (siteNetworkId == null) {
69+
if (networkId == null) {
6070
try {
61-
siteNetworkId = parseExtImp(imp).getNetworkId();
71+
networkId = parseExtImp(imp).getNetworkId();
6272
} catch (PreBidException e) {
6373
errors.add(BidderError.badInput(
6474
"ignoring imp id=%s, error processing ext: %s".formatted(
6575
imp.getId(), e.getMessage())));
6676
}
6777
}
68-
6978
final ObjectNode modifiedExt = modifyImpExt(imp);
7079
modifiedImps.add(imp.toBuilder().ext(modifiedExt).build());
7180
}
@@ -74,12 +83,21 @@ public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request
7483
return Result.withErrors(errors);
7584
}
7685

77-
final BidRequest outgoingRequest = request.toBuilder()
78-
.imp(modifiedImps)
79-
.site(modifySite(request.getSite(), siteNetworkId, mapper))
80-
.build();
86+
final BidRequest.BidRequestBuilder builder = request.toBuilder().imp(modifiedImps);
8187

82-
final HttpRequest<BidRequest> call = BidderUtil.defaultRequest(outgoingRequest, endpointUrl, mapper);
88+
final Site site = request.getSite();
89+
final App app = request.getApp();
90+
91+
if (site != null) {
92+
builder.site(modifySite(site, networkId, mapper));
93+
} else if (app != null) {
94+
builder.app(modifyApp(app, networkId, mapper));
95+
}
96+
97+
final BidRequest outgoingRequest = builder.build();
98+
99+
final String finalEndpointUrl = replaceMacros(site, app, networkId, errors);
100+
final HttpRequest<BidRequest> call = BidderUtil.defaultRequest(outgoingRequest, finalEndpointUrl, mapper);
83101

84102
return Result.of(Collections.singletonList(call), errors);
85103
}
@@ -92,23 +110,24 @@ private ExtImpSparteo parseExtImp(Imp imp) {
92110
}
93111
}
94112

95-
private static ObjectNode modifyImpExt(Imp imp) {
113+
private ObjectNode modifyImpExt(Imp imp) {
96114
final ObjectNode modifiedImpExt = imp.getExt().deepCopy();
97115
final ObjectNode sparteoNode = modifiedImpExt.putObject("sparteo");
98116
final JsonNode bidderJsonNode = modifiedImpExt.remove("bidder");
99117
sparteoNode.set("params", bidderJsonNode);
100-
101118
return modifiedImpExt;
102119
}
103120

104-
private Site modifySite(Site site, String siteNetworkId, JacksonMapper mapper) {
105-
if (site == null || site.getPublisher() == null || siteNetworkId == null) {
121+
private Site modifySite(Site site, String networkId, JacksonMapper mapper) {
122+
if (site == null) {
106123
return site;
107124
}
108125

109-
final Publisher originalPublisher = site.getPublisher();
110-
final ExtPublisher originalExt = originalPublisher.getExt();
126+
final Publisher originalPublisher = site.getPublisher() != null
127+
? site.getPublisher()
128+
: Publisher.builder().build();
111129

130+
final ExtPublisher originalExt = originalPublisher.getExt();
112131
final ExtPublisher modifiedExt = originalExt != null
113132
? ExtPublisher.of(originalExt.getPrebid())
114133
: ExtPublisher.empty();
@@ -117,25 +136,164 @@ private Site modifySite(Site site, String siteNetworkId, JacksonMapper mapper) {
117136
mapper.fillExtension(modifiedExt, originalExt);
118137
}
119138

120-
final JsonNode paramsProperty = modifiedExt.getProperty("params");
121-
final ObjectNode paramsNode;
139+
final ObjectNode paramsNode = ensureParamsNode(modifiedExt);
140+
paramsNode.put("networkId", networkId);
122141

123-
if (paramsProperty != null && paramsProperty.isObject()) {
124-
paramsNode = (ObjectNode) paramsProperty;
125-
} else {
126-
paramsNode = mapper.mapper().createObjectNode();
127-
modifiedExt.addProperty("params", paramsNode);
142+
final Publisher modifiedPublisher = originalPublisher.toBuilder()
143+
.ext(modifiedExt)
144+
.build();
145+
146+
return site.toBuilder().publisher(modifiedPublisher).build();
147+
}
148+
149+
private App modifyApp(App app, String networkId, JacksonMapper mapper) {
150+
if (app == null) {
151+
return app;
128152
}
129153

130-
paramsNode.put("networkId", siteNetworkId);
154+
final Publisher originalPublisher = app.getPublisher() != null
155+
? app.getPublisher()
156+
: Publisher.builder().build();
157+
158+
final ExtPublisher originalExt = originalPublisher.getExt();
159+
final ExtPublisher modifiedExt = originalExt != null
160+
? ExtPublisher.of(originalExt.getPrebid())
161+
: ExtPublisher.empty();
162+
163+
if (originalExt != null) {
164+
mapper.fillExtension(modifiedExt, originalExt);
165+
}
166+
167+
final ObjectNode paramsNode = ensureParamsNode(modifiedExt);
168+
paramsNode.put("networkId", networkId);
131169

132170
final Publisher modifiedPublisher = originalPublisher.toBuilder()
133171
.ext(modifiedExt)
134172
.build();
135173

136-
return site.toBuilder()
137-
.publisher(modifiedPublisher)
138-
.build();
174+
return app.toBuilder().publisher(modifiedPublisher).build();
175+
}
176+
177+
private ObjectNode ensureParamsNode(ExtPublisher extPublisher) {
178+
final JsonNode paramsProperty = extPublisher.getProperty("params");
179+
if (paramsProperty != null && paramsProperty.isObject()) {
180+
return (ObjectNode) paramsProperty;
181+
}
182+
final ObjectNode paramsNode = mapper.mapper().createObjectNode();
183+
extPublisher.addProperty("params", paramsNode);
184+
return paramsNode;
185+
}
186+
187+
private String replaceMacros(Site site, App app, String networkId, List<BidderError> errors) {
188+
final String siteDomain = resolveSiteDomain(site);
189+
final String appDomain = resolveAppDomain(app);
190+
final String bundle = resolveBundle(app);
191+
192+
if (site != null) {
193+
if (UNKNOWN_VALUE.equals(siteDomain)) {
194+
errors.add(BidderError.badInput(
195+
"Domain not found. Missing the site.domain or the site.page field"));
196+
}
197+
} else if (app != null) {
198+
if (UNKNOWN_VALUE.equals(appDomain)) {
199+
errors.add(BidderError.badInput("Domain not found. Missing the app.domain field."));
200+
}
201+
}
202+
203+
if (UNKNOWN_VALUE.equals(bundle)) {
204+
errors.add(BidderError.badInput(
205+
"Bundle not found. Missing the app.bundle field."));
206+
}
207+
return resolveEndpoint(siteDomain, appDomain, networkId, bundle);
208+
}
209+
210+
private static String normalizeHostname(String host) {
211+
String h = StringUtils.trimToEmpty(host);
212+
if (h.isEmpty()) {
213+
return "";
214+
}
215+
216+
String hostname = null;
217+
try {
218+
hostname = new URI(h).getHost();
219+
} catch (URISyntaxException e) {
220+
}
221+
222+
if (StringUtils.isNotEmpty(hostname)) {
223+
h = hostname;
224+
} else {
225+
if (h.contains(":")) {
226+
h = StringUtils.substringBefore(h, ":");
227+
} else {
228+
h = StringUtils.substringBefore(h, "/");
229+
}
230+
}
231+
232+
h = h.toLowerCase();
233+
h = StringUtils.removeStart(h, "www.");
234+
h = StringUtils.removeEnd(h, ".");
235+
236+
return "null".equals(h) ? "" : h;
237+
}
238+
239+
private String resolveSiteDomain(Site site) {
240+
if (site != null) {
241+
final String siteDomain = normalizeHostname(site.getDomain());
242+
if (siteDomain != null && !siteDomain.isEmpty()) {
243+
return siteDomain;
244+
} else {
245+
final String fromPage = normalizeHostname(site.getPage());
246+
if (fromPage != null && !fromPage.isEmpty()) {
247+
return fromPage;
248+
}
249+
}
250+
return UNKNOWN_VALUE;
251+
}
252+
253+
return null;
254+
}
255+
256+
private String resolveAppDomain(App app) {
257+
if (app != null) {
258+
final String appDomain = normalizeHostname(app.getDomain());
259+
if (appDomain != null && !appDomain.isEmpty()) {
260+
return appDomain;
261+
}
262+
return UNKNOWN_VALUE;
263+
}
264+
265+
return null;
266+
}
267+
268+
private String resolveBundle(App app) {
269+
if (app != null) {
270+
final String appBundle = normalizeHostname(app.getBundle());
271+
if (appBundle != null && !appBundle.isEmpty()) {
272+
return appBundle;
273+
}
274+
return UNKNOWN_VALUE;
275+
}
276+
277+
return null;
278+
}
279+
280+
private String resolveEndpoint(String siteDomain, String appDomain, String networkId, String bundle) {
281+
282+
final String siteDomainQuery = StringUtils.isNotBlank(siteDomain)
283+
? "&site_domain=" + HttpUtil.encodeUrl(siteDomain)
284+
: "";
285+
final String appDomainQuery = StringUtils.isNotBlank(appDomain)
286+
? "&app_domain=" + HttpUtil.encodeUrl(appDomain)
287+
: "";
288+
final String bundleQuery = StringUtils.isNotBlank(bundle)
289+
? "&bundle=" + HttpUtil.encodeUrl(bundle)
290+
: "";
291+
292+
return endpointUrl
293+
.replace(NETWORK_ID_MACRO, StringUtils.defaultString(networkId))
294+
.replace(BUNDLE_QUERY_MACRO, bundleQuery)
295+
.replace(SITE_DOMAIN_QUERY_MACRO, siteDomainQuery)
296+
.replace(APP_DOMAIN_QUERY_MACRO, appDomainQuery);
139297
}
140298

141299
@Override

src/main/resources/bidder-config/sparteo.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
adapters:
22
sparteo:
3-
endpoint: https://bid.sparteo.com/s2s-auction
3+
endpoint: https://bid.sparteo.com/s2s-auction?network_id={{NetworkId}}{{SiteDomainQuery}}{{AppDomainQuery}}{{BundleQuery}}
44
meta-info:
55
maintainer-email: [email protected]
66
app-media-types:

0 commit comments

Comments
 (0)