Skip to content

Commit 60ef209

Browse files
author
QuentinGallard
committed
SmileWanted endpoint now supports dynamic zoneId and integrates prebid server technology
1 parent 413779f commit 60ef209

File tree

7 files changed

+241
-8
lines changed

7 files changed

+241
-8
lines changed

src/main/java/org/prebid/server/bidder/smilewanted/SmileWantedBidder.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.prebid.server.bidder.smilewanted;
22

3+
import com.fasterxml.jackson.core.type.TypeReference;
34
import com.iab.openrtb.request.BidRequest;
45
import com.iab.openrtb.request.Imp;
56
import com.iab.openrtb.response.BidResponse;
@@ -13,8 +14,11 @@
1314
import org.prebid.server.bidder.model.BidderError;
1415
import org.prebid.server.bidder.model.HttpRequest;
1516
import org.prebid.server.bidder.model.Result;
17+
import org.prebid.server.exception.PreBidException;
1618
import org.prebid.server.json.DecodeException;
1719
import org.prebid.server.json.JacksonMapper;
20+
import org.prebid.server.proto.openrtb.ext.ExtPrebid;
21+
import org.prebid.server.proto.openrtb.ext.request.smilewanted.ExtImpSmilewanted;
1822
import org.prebid.server.proto.openrtb.ext.response.BidType;
1923
import org.prebid.server.util.HttpUtil;
2024

@@ -28,6 +32,10 @@ public class SmileWantedBidder implements Bidder<BidRequest> {
2832
private static final String X_OPENRTB_VERSION = "2.5";
2933
private static final int DEFAULT_AT = 1;
3034

35+
private static final TypeReference<ExtPrebid<?, ExtImpSmilewanted>> SMILEWANTED_EXT_TYPE_REFERENCE =
36+
new TypeReference<>() {
37+
};
38+
3139
private final String endpointUrl;
3240
private final JacksonMapper mapper;
3341

@@ -38,17 +46,38 @@ public SmileWantedBidder(String endpointUrl, JacksonMapper mapper) {
3846

3947
@Override
4048
public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request) {
49+
if (CollectionUtils.isEmpty(request.getImp())) {
50+
return Result.withError(BidderError.badInput("No impressions in request"));
51+
}
52+
53+
final ExtImpSmilewanted extImpSmilewanted;
54+
55+
try {
56+
extImpSmilewanted = parseImpExt(request.getImp().getFirst());
57+
} catch (PreBidException e) {
58+
return Result.withError(BidderError.badInput(e.getMessage()));
59+
}
60+
4161
final BidRequest outgoingRequest = request.toBuilder().at(DEFAULT_AT).build();
62+
final String url = endpointUrl + extImpSmilewanted.getZoneId();
4263

4364
return Result.withValue(HttpRequest.<BidRequest>builder()
4465
.method(HttpMethod.POST)
45-
.uri(endpointUrl)
66+
.uri(url)
4667
.headers(createHeaders())
4768
.payload(outgoingRequest)
4869
.body(mapper.encodeToBytes(outgoingRequest))
4970
.build());
5071
}
5172

73+
private ExtImpSmilewanted parseImpExt(Imp imp) {
74+
try {
75+
return mapper.mapper().convertValue(imp.getExt(), SMILEWANTED_EXT_TYPE_REFERENCE).getBidder();
76+
} catch (IllegalArgumentException e) {
77+
throw new PreBidException("Missing bidder ext in impression with id: " + imp.getId());
78+
}
79+
}
80+
5281
private static MultiMap createHeaders() {
5382
return HttpUtil.headers()
5483
.add(HttpUtil.X_OPENRTB_VERSION_HEADER, X_OPENRTB_VERSION)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.prebid.server.proto.openrtb.ext.request.smilewanted;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import lombok.Value;
5+
6+
@Value(staticConstructor = "of")
7+
public class ExtImpSmilewanted {
8+
9+
@JsonProperty("zoneId")
10+
String zoneId;
11+
}

src/main/java/org/prebid/server/spring/config/bidder/SimpleWantedConfiguration.java renamed to src/main/java/org/prebid/server/spring/config/bidder/SmileWantedConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
@Configuration
1919
@PropertySource(value = "classpath:/bidder-config/smilewanted.yaml", factory = YamlPropertySourceFactory.class)
20-
public class SimpleWantedConfiguration {
20+
public class SmileWantedConfiguration {
2121

2222
private static final String BIDDER_NAME = "smilewanted";
2323

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
adapters:
22
smilewanted:
3-
endpoint: https://prebid-server.smilewanted.com
3+
endpoint: https://prebid-server.smilewanted.com/java/
44
meta-info:
55
maintainer-email: tech@smilewanted.com
66
app-media-types:

src/test/java/org/prebid/server/bidder/smilewanted/SmileWantedBidderTest.java

Lines changed: 196 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@
1717
import org.prebid.server.bidder.model.HttpRequest;
1818
import org.prebid.server.bidder.model.HttpResponse;
1919
import org.prebid.server.bidder.model.Result;
20+
import org.prebid.server.proto.openrtb.ext.ExtPrebid;
21+
import org.prebid.server.proto.openrtb.ext.request.smilewanted.ExtImpSmilewanted;
2022
import org.prebid.server.util.HttpUtil;
2123

24+
import java.math.BigDecimal;
25+
import java.util.Arrays;
2226
import java.util.List;
2327
import java.util.Map;
2428
import java.util.function.Function;
@@ -28,11 +32,12 @@
2832
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
2933
import static org.assertj.core.api.Assertions.tuple;
3034
import static org.prebid.server.proto.openrtb.ext.response.BidType.banner;
35+
import static org.assertj.core.api.Assertions.assertThatNullPointerException;
3136
import static org.prebid.server.proto.openrtb.ext.response.BidType.video;
3237

3338
public class SmileWantedBidderTest extends VertxTest {
3439

35-
private static final String ENDPOINT_URL = "https://{{Host}}/test?param={{PublisherId}}";
40+
private static final String ENDPOINT_URL = "https://prebid-server.smilewanted.com/java/";
3641

3742
private final SmileWantedBidder target = new SmileWantedBidder(ENDPOINT_URL, jacksonMapper);
3843

@@ -42,13 +47,93 @@ public void creationShouldFailOnInvalidEndpointUrl() {
4247
}
4348

4449
@Test
45-
public void makeHttpRequestsShouldCorrectlyAddHeaders() {
50+
public void creationShouldFailOnNullEndpointUrl() {
51+
assertThatNullPointerException().isThrownBy(() -> new SmileWantedBidder(null, jacksonMapper));
52+
}
53+
54+
@Test
55+
public void creationShouldFailOnNullMapper() {
56+
assertThatNullPointerException().isThrownBy(() -> new SmileWantedBidder(ENDPOINT_URL, null));
57+
}
58+
59+
@Test
60+
public void makeHttpRequestsShouldReturnErrorIfNoImpressions() {
4661
// given
4762
final BidRequest bidRequest = BidRequest.builder().build();
4863

4964
// when
5065
final Result<List<HttpRequest<BidRequest>>> result = target.makeHttpRequests(bidRequest);
5166

67+
// then
68+
assertThat(result.getValue()).isEmpty();
69+
assertThat(result.getErrors())
70+
.containsExactly(BidderError.badInput("No impressions in request"));
71+
}
72+
73+
@Test
74+
public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() {
75+
// given
76+
final BidRequest bidRequest = BidRequest.builder()
77+
.imp(singletonList(Imp.builder()
78+
.id("123")
79+
.ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode())))
80+
.build()))
81+
.build();
82+
83+
// when
84+
final Result<List<HttpRequest<BidRequest>>> result = target.makeHttpRequests(bidRequest);
85+
86+
// then
87+
assertThat(result.getValue()).isEmpty();
88+
assertThat(result.getErrors()).hasSize(1)
89+
.allSatisfy(error -> {
90+
assertThat(error.getType()).isEqualTo(BidderError.Type.bad_input);
91+
assertThat(error.getMessage()).startsWith("Missing bidder ext in impression with id: 123");
92+
});
93+
}
94+
95+
@Test
96+
public void makeHttpRequestsShouldReturnSingleRequest() {
97+
// given
98+
final BidRequest bidRequest = BidRequest.builder()
99+
.imp(singletonList(givenImp("zone123")))
100+
.build();
101+
102+
// when
103+
final Result<List<HttpRequest<BidRequest>>> result = target.makeHttpRequests(bidRequest);
104+
105+
// then
106+
assertThat(result.getErrors()).isEmpty();
107+
assertThat(result.getValue()).hasSize(1);
108+
}
109+
110+
@Test
111+
public void makeHttpRequestsShouldBuildCorrectEndpointUrlWithZoneId() {
112+
// given
113+
final BidRequest bidRequest = BidRequest.builder()
114+
.imp(singletonList(givenImp("zone456")))
115+
.build();
116+
117+
// when
118+
final Result<List<HttpRequest<BidRequest>>> result = target.makeHttpRequests(bidRequest);
119+
120+
// then
121+
assertThat(result.getErrors()).isEmpty();
122+
assertThat(result.getValue())
123+
.extracting(HttpRequest::getUri)
124+
.containsExactly("https://prebid-server.smilewanted.com/java/zone456");
125+
}
126+
127+
@Test
128+
public void makeHttpRequestsShouldCorrectlyAddHeaders() {
129+
// given
130+
final BidRequest bidRequest = BidRequest.builder()
131+
.imp(singletonList(givenImp("zone123")))
132+
.build();
133+
134+
// when
135+
final Result<List<HttpRequest<BidRequest>>> result = target.makeHttpRequests(bidRequest);
136+
52137
// then
53138
assertThat(result.getErrors()).isEmpty();
54139
assertThat(result.getValue())
@@ -65,7 +150,9 @@ public void makeHttpRequestsShouldCorrectlyAddHeaders() {
65150
@Test
66151
public void makeHttpRequestsShouldSetAtToOne() {
67152
// given
68-
final BidRequest bidRequest = BidRequest.builder().build();
153+
final BidRequest bidRequest = BidRequest.builder()
154+
.imp(singletonList(givenImp("zone123")))
155+
.build();
69156

70157
// when
71158
final Result<List<HttpRequest<BidRequest>>> result = target.makeHttpRequests(bidRequest);
@@ -160,6 +247,105 @@ public void makeBidsShouldReturnBannerBidIfVideoIsAbsentInRequestImp() throws Js
160247
.containsOnly(BidderBid.of(Bid.builder().impid("123").build(), banner, null));
161248
}
162249

250+
@Test
251+
public void makeBidsShouldReturnMultipleBidsFromSingleSeatBid() throws JsonProcessingException {
252+
// given
253+
final BidderCall<BidRequest> httpCall = givenHttpCall(
254+
BidRequest.builder()
255+
.imp(List.of(
256+
Imp.builder().id("123").build(),
257+
Imp.builder().id("456").video(Video.builder().build()).build()))
258+
.build(),
259+
mapper.writeValueAsString(
260+
BidResponse.builder()
261+
.cur("USD")
262+
.seatbid(singletonList(SeatBid.builder()
263+
.bid(List.of(
264+
Bid.builder().impid("123").price(BigDecimal.valueOf(1.0)).build(),
265+
Bid.builder().impid("456").price(BigDecimal.valueOf(2.0)).build()))
266+
.build()))
267+
.build()));
268+
269+
// when
270+
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
271+
272+
// then
273+
assertThat(result.getErrors()).isEmpty();
274+
assertThat(result.getValue()).hasSize(2)
275+
.containsExactlyInAnyOrder(
276+
BidderBid.of(Bid.builder().impid("123").price(BigDecimal.valueOf(1.0)).build(), banner, "USD"),
277+
BidderBid.of(Bid.builder().impid("456").price(BigDecimal.valueOf(2.0)).build(), video, "USD"));
278+
}
279+
280+
@Test
281+
public void makeBidsShouldFilterNullBids() throws JsonProcessingException {
282+
// given
283+
final BidderCall<BidRequest> httpCall = givenHttpCall(
284+
BidRequest.builder()
285+
.imp(singletonList(Imp.builder().id("123").build()))
286+
.build(),
287+
mapper.writeValueAsString(
288+
BidResponse.builder()
289+
.seatbid(singletonList(SeatBid.builder()
290+
.bid(Arrays.asList(
291+
Bid.builder().impid("123").build(),
292+
null,
293+
Bid.builder().impid("456").build()))
294+
.build()))
295+
.build()));
296+
297+
// when
298+
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
299+
300+
// then
301+
assertThat(result.getErrors()).isEmpty();
302+
assertThat(result.getValue()).hasSize(2)
303+
.extracting(BidderBid::getBid)
304+
.extracting(Bid::getImpid)
305+
.containsExactlyInAnyOrder("123", "456");
306+
}
307+
308+
@Test
309+
public void makeBidsShouldReturnBidWithCurrency() throws JsonProcessingException {
310+
// given
311+
final BidderCall<BidRequest> httpCall = givenHttpCall(
312+
BidRequest.builder()
313+
.imp(singletonList(Imp.builder().id("123").build()))
314+
.build(),
315+
mapper.writeValueAsString(
316+
BidResponse.builder()
317+
.cur("EUR")
318+
.seatbid(singletonList(SeatBid.builder()
319+
.bid(singletonList(Bid.builder().impid("123").build()))
320+
.build()))
321+
.build()));
322+
323+
// when
324+
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
325+
326+
// then
327+
assertThat(result.getErrors()).isEmpty();
328+
assertThat(result.getValue())
329+
.extracting(BidderBid::getBidCurrency)
330+
.containsExactly("EUR");
331+
}
332+
333+
@Test
334+
public void makeBidsShouldReturnEmptyListIfSeatBidIsEmpty() throws JsonProcessingException {
335+
// given
336+
final BidderCall<BidRequest> httpCall = givenHttpCall(null,
337+
mapper.writeValueAsString(BidResponse.builder()
338+
.seatbid(singletonList(SeatBid.builder().build()))
339+
.build()));
340+
341+
// when
342+
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
343+
344+
// then
345+
assertThat(result.getErrors()).isEmpty();
346+
assertThat(result.getValue()).isEmpty();
347+
}
348+
163349
private static BidResponse givenBidResponse(Function<Bid.BidBuilder, Bid.BidBuilder> bidCustomizer) {
164350
return BidResponse.builder()
165351
.seatbid(singletonList(SeatBid.builder().bid(singletonList(bidCustomizer.apply(Bid.builder()).build()))
@@ -173,4 +359,11 @@ private static BidderCall<BidRequest> givenHttpCall(BidRequest bidRequest, Strin
173359
HttpResponse.of(200, null, body),
174360
null);
175361
}
362+
363+
private static Imp givenImp(String zoneId) {
364+
return Imp.builder()
365+
.id("123")
366+
.ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpSmilewanted.of(zoneId))))
367+
.build();
368+
}
176369
}

src/test/java/org/prebid/server/it/SmileWantedTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public class SmileWantedTest extends IntegrationTest {
1818
@Test
1919
public void openrtb2AuctionShouldRespondWithBidsFromSmileWanted() throws IOException, JSONException {
2020
// given
21-
WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/smilewanted-exchange"))
21+
WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/smilewanted-exchange/java/someZoneId"))
2222
.withRequestBody(equalToJson(jsonFrom("openrtb2/smilewanted/test-smilewanted-bid-request.json")))
2323
.willReturn(aResponse().withBody(jsonFrom(
2424
"openrtb2/smilewanted/test-smilewanted-bid-response.json"))));

src/test/resources/org/prebid/server/it/test-application.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ adapters.smarthub.aliases.artechnology.endpoint=http://localhost:8090/artechnolo
515515
adapters.smartyads.enabled=true
516516
adapters.smartyads.endpoint=http://localhost:8090/smartyads-exchange
517517
adapters.smilewanted.enabled=true
518-
adapters.smilewanted.endpoint=http://localhost:8090/smilewanted-exchange
518+
adapters.smilewanted.endpoint=http://localhost:8090/smilewanted-exchange/java/
519519
adapters.smoot.enabled=true
520520
adapters.smoot.endpoint=http://localhost:8090/smoot-exchange
521521
adapters.smrtconnect.enabled=true

0 commit comments

Comments
 (0)