Skip to content

Commit bc8cd58

Browse files
committed
Implementation of AntoxaAntoxic review
1 parent 70b88dd commit bc8cd58

5 files changed

Lines changed: 146 additions & 129 deletions

File tree

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

Lines changed: 124 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import com.iab.openrtb.response.Bid;
1111
import com.iab.openrtb.response.BidResponse;
1212
import com.iab.openrtb.response.SeatBid;
13-
import io.vertx.core.http.HttpMethod;
1413
import org.apache.commons.collections4.CollectionUtils;
1514
import org.prebid.server.bidder.Bidder;
1615
import org.prebid.server.bidder.model.BidderBid;
@@ -21,14 +20,15 @@
2120
import org.prebid.server.json.DecodeException;
2221
import org.prebid.server.json.JacksonMapper;
2322
import org.prebid.server.proto.openrtb.ext.request.ExtPublisher;
23+
import org.prebid.server.proto.openrtb.ext.request.sparteo.ExtImpSparteo;
2424
import org.prebid.server.proto.openrtb.ext.response.BidType;
2525
import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebid;
2626
import org.prebid.server.util.BidderUtil;
2727
import org.prebid.server.util.HttpUtil;
2828

2929
import java.util.ArrayList;
30+
import java.util.Collection;
3031
import java.util.Collections;
31-
import java.util.Iterator;
3232
import java.util.List;
3333
import java.util.Objects;
3434

@@ -42,89 +42,158 @@ public SparteoBidder(String endpointUrl, JacksonMapper mapper) {
4242
this.mapper = Objects.requireNonNull(mapper);
4343
}
4444

45-
private static <T> Iterable<T> iterable(Iterator<T> it) {
46-
return () -> it;
47-
}
48-
4945
@Override
5046
public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request) {
5147
final List<BidderError> errors = new ArrayList<>();
52-
53-
String siteNetworkId = null;
5448
final List<Imp> modifiedImps = new ArrayList<>();
49+
String siteNetworkId = null;
5550

5651
for (Imp imp : request.getImp()) {
5752
try {
58-
final ObjectNode extMap = mapper.mapper()
59-
.convertValue(imp.getExt(), ObjectNode.class);
60-
61-
final ObjectNode bidderNode = (ObjectNode) extMap.remove("bidder");
53+
final JsonNode bidderNode = imp.getExt().get("bidder");
54+
final ExtImpSparteo bidderParams = mapper.mapper().treeToValue(bidderNode, ExtImpSparteo.class);
6255

63-
if (bidderNode != null) {
64-
if (siteNetworkId == null && bidderNode.hasNonNull("networkId")) {
65-
siteNetworkId = bidderNode.get("networkId").asText();
66-
}
67-
68-
final ObjectNode sparteoNode = extMap.has("sparteo") && extMap.get("sparteo").isObject()
69-
? (ObjectNode) extMap.get("sparteo")
70-
: extMap.putObject("sparteo");
71-
final ObjectNode paramsNode = sparteoNode.has("params") && sparteoNode.get("params").isObject()
72-
? (ObjectNode) sparteoNode.get("params")
73-
: sparteoNode.putObject("params");
74-
75-
for (String field : iterable(bidderNode.fieldNames())) {
76-
paramsNode.set(field, bidderNode.get(field));
77-
}
56+
if (siteNetworkId == null && bidderParams.getNetworkId() != null) {
57+
siteNetworkId = bidderParams.getNetworkId();
7858
}
7959

80-
modifiedImps.add(imp.toBuilder().ext(extMap).build());
81-
} catch (Exception e) {
60+
final ObjectNode modifiedExt = buildImpExt(imp, bidderParams, mapper);
61+
62+
modifiedImps.add(imp.toBuilder().ext(modifiedExt).build());
63+
} catch (NullPointerException | JsonProcessingException e) {
8264
errors.add(BidderError.badInput(
8365
String.format("ignoring imp id=%s, error processing ext: %s",
84-
imp.getId(), e.getMessage())));
66+
imp.getId(), e.getMessage())));
8567
}
8668
}
8769

8870
if (modifiedImps.isEmpty()) {
8971
return Result.withErrors(errors);
9072
}
9173

92-
final BidRequest.BidRequestBuilder rb = request.toBuilder().imp(modifiedImps);
74+
final BidRequest outgoingRequest = request.toBuilder()
75+
.imp(modifiedImps)
76+
.site(modifySite(request.getSite(), siteNetworkId, mapper))
77+
.build();
78+
79+
final HttpRequest<BidRequest> call = BidderUtil.defaultRequest(outgoingRequest, endpointUrl, mapper);
80+
81+
return Result.of(Collections.singletonList(call), errors);
82+
}
83+
84+
@Override
85+
public Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequest bidRequest) {
86+
final List<BidderError> errors = new ArrayList<>();
87+
final int statusCode = httpCall.getResponse().getStatusCode();
9388

94-
final Site site = request.getSite();
95-
if (site != null && site.getPublisher() != null && siteNetworkId != null) {
96-
final Publisher pub = site.getPublisher();
89+
if (statusCode == 204) {
90+
return Result.of(Collections.emptyList(), errors);
91+
}
92+
93+
if (statusCode != 200) {
94+
return Result.withError(BidderError.badServerResponse(
95+
String.format("HTTP status %d returned from Sparteo", statusCode)));
96+
}
97+
98+
try {
99+
final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class);
100+
return Result.of(extractBids(bidResponse, errors), errors);
101+
} catch (DecodeException e) {
102+
return Result.withError(BidderError.badServerResponse(
103+
String.format("Failed to decode Sparteo response: %s", e.getMessage())));
104+
}
105+
}
106+
107+
private ObjectNode buildImpExt(Imp imp, ExtImpSparteo bidderParams, JacksonMapper mapper)
108+
throws JsonProcessingException {
109+
110+
final ObjectNode extMap = mapper.mapper().convertValue(imp.getExt(), ObjectNode.class);
111+
112+
extMap.remove("bidder");
113+
114+
final JsonNode sparteoNode = extMap.get("sparteo");
115+
final ObjectNode outgoingParamsNode;
116+
117+
if (sparteoNode != null && sparteoNode.isObject() && sparteoNode.has("params")
118+
&& sparteoNode.get("params").isObject()) {
119+
outgoingParamsNode = (ObjectNode) sparteoNode.get("params");
120+
} else {
121+
outgoingParamsNode = extMap.putObject("sparteo").putObject("params");
122+
}
123+
124+
final ObjectNode bidderParamsAsNode = mapper.mapper().convertValue(bidderParams, ObjectNode.class);
125+
outgoingParamsNode.setAll(bidderParamsAsNode);
126+
127+
final JsonNode prebidNode = extMap.get("prebid");
128+
if (prebidNode != null && prebidNode.has("adunitcode")) {
129+
outgoingParamsNode.set("adUnitCode", prebidNode.get("adunitcode"));
130+
}
131+
132+
return extMap;
133+
}
134+
135+
private Site modifySite(Site site, String siteNetworkId, JacksonMapper mapper) {
136+
if (site == null || site.getPublisher() == null || siteNetworkId == null) {
137+
return site;
138+
}
97139

98-
final ObjectNode pubExtRaw = pub.getExt() != null
99-
? mapper.mapper().convertValue(pub.getExt(), ObjectNode.class)
100-
: mapper.mapper().createObjectNode();
140+
final Publisher publisher = site.getPublisher();
141+
final ExtPublisher extPublisher;
101142

102-
pubExtRaw.withObjectProperty("params").put("networkId", siteNetworkId);
143+
extPublisher = publisher.getExt() != null
144+
? publisher.getExt()
145+
: ExtPublisher.empty();
103146

104-
final ExtPublisher extPub = mapper.mapper()
105-
.convertValue(pubExtRaw, ExtPublisher.class);
147+
final JsonNode paramsProperty = extPublisher.getProperty("params");
148+
final ObjectNode paramsNode;
106149

107-
final Publisher newPub = pub.toBuilder().ext(extPub).build();
108-
final Site newSite = site.toBuilder().publisher(newPub).build();
109-
rb.site(newSite);
150+
if (paramsProperty != null && paramsProperty.isObject()) {
151+
paramsNode = (ObjectNode) paramsProperty;
152+
} else {
153+
paramsNode = mapper.mapper().createObjectNode();
154+
extPublisher.addProperty("params", paramsNode);
110155
}
111156

112-
final BidRequest outgoing = rb.build();
157+
paramsNode.put("networkId", siteNetworkId);
113158

114-
final HttpRequest<BidRequest> call = HttpRequest.<BidRequest>builder()
115-
.method(HttpMethod.POST)
116-
.uri(endpointUrl)
117-
.headers(HttpUtil.headers())
118-
.impIds(BidderUtil.impIds(outgoing))
119-
.body(mapper.encodeToBytes(outgoing))
120-
.payload(outgoing)
159+
final Publisher modifiedPublisher = publisher.toBuilder()
160+
.ext(extPublisher)
121161
.build();
122162

123-
final List<HttpRequest<BidRequest>> calls = Collections.singletonList(call);
163+
return site.toBuilder()
164+
.publisher(modifiedPublisher)
165+
.build();
166+
}
167+
168+
private List<BidderBid> extractBids(BidResponse bidResponse, List<BidderError> errors) {
169+
if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) {
170+
return Collections.emptyList();
171+
}
124172

125-
return errors.isEmpty()
126-
? Result.withValues(calls)
127-
: Result.of(calls, errors);
173+
return bidResponse.getSeatbid().stream()
174+
.filter(Objects::nonNull)
175+
.map(SeatBid::getBid)
176+
.filter(Objects::nonNull)
177+
.flatMap(Collection::stream)
178+
.map(bid -> {
179+
if (bid == null) {
180+
errors.add(BidderError.badServerResponse("Received null bid object within a seatbid."));
181+
return null;
182+
}
183+
return toBidderBid(bid, bidResponse.getCur(), errors);
184+
})
185+
.filter(Objects::nonNull)
186+
.toList();
187+
}
188+
189+
private BidderBid toBidderBid(Bid bid, String currency, List<BidderError> errors) {
190+
try {
191+
final BidType bidType = getBidTypeFromBidExtension(bid);
192+
return BidderBid.of(bid, bidType, currency);
193+
} catch (Exception e) {
194+
errors.add(BidderError.badServerResponse(e.getMessage()));
195+
return null;
196+
}
128197
}
129198

130199
private BidType getBidTypeFromBidExtension(Bid bid) throws Exception {
@@ -168,58 +237,4 @@ private BidType getBidTypeFromBidExtension(Bid bid) throws Exception {
168237

169238
return bidType;
170239
}
171-
172-
@Override
173-
public Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequest bidRequest) {
174-
final List<BidderError> errors = new ArrayList<>();
175-
176-
final int status = httpCall.getResponse().getStatusCode();
177-
if (status == 204) {
178-
return Result.of(Collections.emptyList(), errors);
179-
}
180-
if (status != 200) {
181-
errors.add(BidderError.badServerResponse(
182-
String.format("HTTP status %d returned from Sparteo", status))
183-
);
184-
return Result.of(Collections.emptyList(), errors);
185-
}
186-
187-
final BidResponse bidResponse;
188-
try {
189-
bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class);
190-
} catch (DecodeException e) {
191-
errors.add(BidderError.badServerResponse(
192-
String.format("Failed to decode Sparteo response: %s", e.getMessage()))
193-
);
194-
return Result.of(Collections.emptyList(), errors);
195-
}
196-
197-
if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) {
198-
return Result.of(Collections.emptyList(), errors);
199-
}
200-
201-
final List<BidderBid> bidderBids = new ArrayList<>();
202-
final String currency = bidResponse.getCur();
203-
204-
for (SeatBid seatBid : bidResponse.getSeatbid()) {
205-
if (seatBid != null && CollectionUtils.isNotEmpty(seatBid.getBid())) {
206-
for (Bid bid : seatBid.getBid()) {
207-
if (bid == null) {
208-
errors.add(BidderError.badServerResponse(
209-
"Received null bid object within a seatbid.")
210-
);
211-
continue;
212-
}
213-
try {
214-
final BidType type = getBidTypeFromBidExtension(bid);
215-
bidderBids.add(BidderBid.of(bid, type, currency));
216-
} catch (Exception e) {
217-
errors.add(BidderError.badServerResponse(e.getMessage()));
218-
}
219-
}
220-
}
221-
}
222-
223-
return Result.of(bidderBids, errors);
224-
}
225240
}

src/main/java/org/prebid/server/proto/openrtb/ext/request/sparteo/ExtImpSparteo.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
package org.prebid.server.proto.openrtb.ext.request.sparteo;
22

3+
import com.fasterxml.jackson.annotation.JsonAnyGetter;
4+
import com.fasterxml.jackson.annotation.JsonAnySetter;
35
import com.fasterxml.jackson.annotation.JsonInclude;
46
import com.fasterxml.jackson.annotation.JsonProperty;
5-
import lombok.NonNull;
7+
import com.fasterxml.jackson.databind.JsonNode;
68
import lombok.Value;
79

10+
import java.util.HashMap;
11+
import java.util.Map;
12+
813
@Value(staticConstructor = "of")
914
@JsonInclude(JsonInclude.Include.NON_NULL)
1015
public class ExtImpSparteo {
1116

12-
@NonNull
1317
@JsonProperty("networkId")
1418
String networkId;
1519

@@ -27,4 +31,16 @@ public class ExtImpSparteo {
2731

2832
@JsonProperty("custom5")
2933
String custom5;
34+
35+
Map<String, JsonNode> additionalProperties = new HashMap<>();
36+
37+
@JsonAnySetter
38+
public void addAdditionalProperty(String key, JsonNode value) {
39+
additionalProperties.put(key, value);
40+
}
41+
42+
@JsonAnyGetter
43+
public Map<String, JsonNode> getAdditionalProperties() {
44+
return additionalProperties;
45+
}
3046
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ adapters:
1313
- native
1414
supported-vendors:
1515
vendor-id: 1028
16-
userSync:
16+
usersync:
1717
cookie-family-name: sparteo
1818
iframe:
1919
url: "https://sync.sparteo.com/s2s_sync?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&redirect_url={{redirect_url}}"
20-
supportCors: true
20+
support-cors: true

src/main/resources/static/bidder-params/sparteo.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,5 @@
3030
},
3131
"required": [
3232
"networkId"
33-
],
34-
"additionalProperties": true
33+
]
3534
}

src/test/java/org/prebid/server/bidder/sparteo/SparteoBidderTest.java

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -90,21 +90,8 @@ public void makeHttpRequestsShouldReturnErrorIfImpExtCannotBeParsed() {
9090
assertThat(result.getErrors())
9191
.hasSize(1)
9292
.allSatisfy(error -> {
93-
assertThat(error.getType())
94-
.isEqualTo(BidderError.Type.bad_input);
95-
final String actualExceptionMessageContent =
96-
"class com.fasterxml.jackson.databind.node.TextNode cannot be cast to "
97-
+ "class com.fasterxml.jackson.databind.node.ObjectNode"
98-
+ " (com.fasterxml.jackson.databind.node.TextNode and "
99-
+ "com.fasterxml.jackson.databind.node.ObjectNode are in unnamed module "
100-
+ "of loader 'app')";
101-
final String expectedFullErrorMessage = String.format(
102-
"ignoring imp id=null, error processing ext: %s",
103-
actualExceptionMessageContent
104-
);
105-
10693
assertThat(error.getMessage())
107-
.isEqualTo(expectedFullErrorMessage);
94+
.startsWith("ignoring imp id=null, error processing ext: Cannot construct instance");
10895
});
10996
assertThat(result.getValue())
11097
.isEmpty();

0 commit comments

Comments
 (0)