Skip to content

Commit e664dc5

Browse files
author
QuentinGallard
committed
SmileWanted endpoint now supports dynamic zoneId and integrates prebid server technology
1 parent 27d6364 commit e664dc5

File tree

7 files changed

+227
-15
lines changed

7 files changed

+227
-15
lines changed

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

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
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;
67
import com.iab.openrtb.response.SeatBid;
78
import io.vertx.core.MultiMap;
8-
import io.vertx.core.http.HttpMethod;
99
import org.apache.commons.collections4.CollectionUtils;
1010
import org.prebid.server.bidder.Bidder;
1111
import org.prebid.server.bidder.model.BidderBid;
1212
import org.prebid.server.bidder.model.BidderCall;
1313
import org.prebid.server.bidder.model.BidderError;
1414
import org.prebid.server.bidder.model.HttpRequest;
1515
import org.prebid.server.bidder.model.Result;
16+
import org.prebid.server.exception.PreBidException;
1617
import org.prebid.server.json.DecodeException;
1718
import org.prebid.server.json.JacksonMapper;
19+
import org.prebid.server.proto.openrtb.ext.ExtPrebid;
20+
import org.prebid.server.proto.openrtb.ext.request.smilewanted.ExtImpSmilewanted;
1821
import org.prebid.server.proto.openrtb.ext.response.BidType;
22+
import org.prebid.server.util.BidderUtil;
1923
import org.prebid.server.util.HttpUtil;
2024

2125
import java.util.Collections;
@@ -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,15 +46,30 @@ public SmileWantedBidder(String endpointUrl, JacksonMapper mapper) {
3846

3947
@Override
4048
public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request) {
49+
final ExtImpSmilewanted extImpSmilewanted;
50+
51+
try {
52+
extImpSmilewanted = parseImpExt(request.getImp().getFirst());
53+
} catch (PreBidException e) {
54+
return Result.withError(BidderError.badInput(e.getMessage()));
55+
}
56+
4157
final BidRequest outgoingRequest = request.toBuilder().at(DEFAULT_AT).build();
58+
final String url = endpointUrl.replace("{{ZoneId}}", extImpSmilewanted.getZoneId());
4259

43-
return Result.withValue(HttpRequest.<BidRequest>builder()
44-
.method(HttpMethod.POST)
45-
.uri(endpointUrl)
46-
.headers(createHeaders())
47-
.payload(outgoingRequest)
48-
.body(mapper.encodeToBytes(outgoingRequest))
49-
.build());
60+
return Result.withValue(BidderUtil.defaultRequest(
61+
outgoingRequest,
62+
createHeaders(),
63+
url,
64+
mapper));
65+
}
66+
67+
private ExtImpSmilewanted parseImpExt(Imp imp) {
68+
try {
69+
return mapper.mapper().convertValue(imp.getExt(), SMILEWANTED_EXT_TYPE_REFERENCE).getBidder();
70+
} catch (IllegalArgumentException e) {
71+
throw new PreBidException("Missing bidder ext in impression with id: " + imp.getId());
72+
}
5073
}
5174

5275
private static MultiMap createHeaders() {
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: [email protected]
66
app-media-types:

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

Lines changed: 181 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;
@@ -32,7 +36,7 @@
3236

3337
public class SmileWantedBidderTest extends VertxTest {
3438

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

3741
private final SmileWantedBidder target = new SmileWantedBidder(ENDPOINT_URL, jacksonMapper);
3842

@@ -41,10 +45,76 @@ public void creationShouldFailOnInvalidEndpointUrl() {
4145
assertThatIllegalArgumentException().isThrownBy(() -> new SmileWantedBidder("invalid_url", jacksonMapper));
4246
}
4347

48+
@Test
49+
public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() {
50+
// given
51+
final BidRequest bidRequest = BidRequest.builder()
52+
.imp(singletonList(Imp.builder()
53+
.id("123")
54+
.ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode())))
55+
.build()))
56+
.build();
57+
58+
// when
59+
final Result<List<HttpRequest<BidRequest>>> result = target.makeHttpRequests(bidRequest);
60+
61+
// then
62+
assertThat(result.getValue()).isEmpty();
63+
assertThat(result.getErrors()).hasSize(1)
64+
.allSatisfy(error -> {
65+
assertThat(error.getType()).isEqualTo(BidderError.Type.bad_input);
66+
assertThat(error.getMessage()).startsWith("Missing bidder ext in impression with id: 123");
67+
});
68+
}
69+
70+
@Test
71+
public void makeHttpRequestsShouldReturnSingleRequest() {
72+
// given
73+
final BidRequest bidRequest = BidRequest.builder()
74+
.imp(List.of(
75+
givenImp("zone123"),
76+
Imp.builder()
77+
.id("456")
78+
.ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpSmilewanted.of("zone123"))))
79+
.build()))
80+
.build();
81+
82+
// when
83+
final Result<List<HttpRequest<BidRequest>>> result = target.makeHttpRequests(bidRequest);
84+
85+
// then
86+
assertThat(result.getErrors()).isEmpty();
87+
assertThat(result.getValue()).hasSize(1);
88+
final HttpRequest<BidRequest> httpRequest = result.getValue().get(0);
89+
assertThat(httpRequest.getPayload()).isNotNull();
90+
assertThat(httpRequest.getPayload().getImp()).hasSize(2);
91+
assertThat(httpRequest.getPayload().getAt()).isEqualTo(1);
92+
assertThat(httpRequest.getImpIds()).containsExactlyInAnyOrder("123", "456");
93+
}
94+
95+
@Test
96+
public void makeHttpRequestsShouldBuildCorrectEndpointUrlWithZoneId() {
97+
// given
98+
final BidRequest bidRequest = BidRequest.builder()
99+
.imp(singletonList(givenImp("zone456")))
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())
108+
.extracting(HttpRequest::getUri)
109+
.containsExactly("https://prebid-server.smilewanted.com/java/zone456");
110+
}
111+
44112
@Test
45113
public void makeHttpRequestsShouldCorrectlyAddHeaders() {
46114
// given
47-
final BidRequest bidRequest = BidRequest.builder().build();
115+
final BidRequest bidRequest = BidRequest.builder()
116+
.imp(singletonList(givenImp("zone123")))
117+
.build();
48118

49119
// when
50120
final Result<List<HttpRequest<BidRequest>>> result = target.makeHttpRequests(bidRequest);
@@ -65,7 +135,9 @@ public void makeHttpRequestsShouldCorrectlyAddHeaders() {
65135
@Test
66136
public void makeHttpRequestsShouldSetAtToOne() {
67137
// given
68-
final BidRequest bidRequest = BidRequest.builder().build();
138+
final BidRequest bidRequest = BidRequest.builder()
139+
.imp(singletonList(givenImp("zone123")))
140+
.build();
69141

70142
// when
71143
final Result<List<HttpRequest<BidRequest>>> result = target.makeHttpRequests(bidRequest);
@@ -160,6 +232,105 @@ public void makeBidsShouldReturnBannerBidIfVideoIsAbsentInRequestImp() throws Js
160232
.containsOnly(BidderBid.of(Bid.builder().impid("123").build(), banner, null));
161233
}
162234

235+
@Test
236+
public void makeBidsShouldReturnMultipleBidsFromSingleSeatBid() throws JsonProcessingException {
237+
// given
238+
final BidderCall<BidRequest> httpCall = givenHttpCall(
239+
BidRequest.builder()
240+
.imp(List.of(
241+
Imp.builder().id("123").build(),
242+
Imp.builder().id("456").video(Video.builder().build()).build()))
243+
.build(),
244+
mapper.writeValueAsString(
245+
BidResponse.builder()
246+
.cur("USD")
247+
.seatbid(singletonList(SeatBid.builder()
248+
.bid(List.of(
249+
Bid.builder().impid("123").price(BigDecimal.valueOf(1.0)).build(),
250+
Bid.builder().impid("456").price(BigDecimal.valueOf(2.0)).build()))
251+
.build()))
252+
.build()));
253+
254+
// when
255+
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
256+
257+
// then
258+
assertThat(result.getErrors()).isEmpty();
259+
assertThat(result.getValue()).hasSize(2)
260+
.containsExactlyInAnyOrder(
261+
BidderBid.of(Bid.builder().impid("123").price(BigDecimal.valueOf(1.0)).build(), banner, "USD"),
262+
BidderBid.of(Bid.builder().impid("456").price(BigDecimal.valueOf(2.0)).build(), video, "USD"));
263+
}
264+
265+
@Test
266+
public void makeBidsShouldFilterNullBids() throws JsonProcessingException {
267+
// given
268+
final BidderCall<BidRequest> httpCall = givenHttpCall(
269+
BidRequest.builder()
270+
.imp(singletonList(Imp.builder().id("123").build()))
271+
.build(),
272+
mapper.writeValueAsString(
273+
BidResponse.builder()
274+
.seatbid(singletonList(SeatBid.builder()
275+
.bid(Arrays.asList(
276+
Bid.builder().impid("123").build(),
277+
null,
278+
Bid.builder().impid("456").build()))
279+
.build()))
280+
.build()));
281+
282+
// when
283+
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
284+
285+
// then
286+
assertThat(result.getErrors()).isEmpty();
287+
assertThat(result.getValue()).hasSize(2)
288+
.extracting(BidderBid::getBid)
289+
.extracting(Bid::getImpid)
290+
.containsExactlyInAnyOrder("123", "456");
291+
}
292+
293+
@Test
294+
public void makeBidsShouldReturnBidWithCurrency() throws JsonProcessingException {
295+
// given
296+
final BidderCall<BidRequest> httpCall = givenHttpCall(
297+
BidRequest.builder()
298+
.imp(singletonList(Imp.builder().id("123").build()))
299+
.build(),
300+
mapper.writeValueAsString(
301+
BidResponse.builder()
302+
.cur("EUR")
303+
.seatbid(singletonList(SeatBid.builder()
304+
.bid(singletonList(Bid.builder().impid("123").build()))
305+
.build()))
306+
.build()));
307+
308+
// when
309+
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
310+
311+
// then
312+
assertThat(result.getErrors()).isEmpty();
313+
assertThat(result.getValue())
314+
.extracting(BidderBid::getBidCurrency)
315+
.containsExactly("EUR");
316+
}
317+
318+
@Test
319+
public void makeBidsShouldReturnEmptyListIfSeatBidIsEmpty() throws JsonProcessingException {
320+
// given
321+
final BidderCall<BidRequest> httpCall = givenHttpCall(null,
322+
mapper.writeValueAsString(BidResponse.builder()
323+
.seatbid(singletonList(SeatBid.builder().build()))
324+
.build()));
325+
326+
// when
327+
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
328+
329+
// then
330+
assertThat(result.getErrors()).isEmpty();
331+
assertThat(result.getValue()).isEmpty();
332+
}
333+
163334
private static BidResponse givenBidResponse(Function<Bid.BidBuilder, Bid.BidBuilder> bidCustomizer) {
164335
return BidResponse.builder()
165336
.seatbid(singletonList(SeatBid.builder().bid(singletonList(bidCustomizer.apply(Bid.builder()).build()))
@@ -173,4 +344,11 @@ private static BidderCall<BidRequest> givenHttpCall(BidRequest bidRequest, Strin
173344
HttpResponse.of(200, null, body),
174345
null);
175346
}
347+
348+
private static Imp givenImp(String zoneId) {
349+
return Imp.builder()
350+
.id("123")
351+
.ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpSmilewanted.of(zoneId))))
352+
.build();
353+
}
176354
}

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
@@ -522,7 +522,7 @@ adapters.smarthub.aliases.artechnology.endpoint=http://localhost:8090/artechnolo
522522
adapters.smartyads.enabled=true
523523
adapters.smartyads.endpoint=http://localhost:8090/smartyads-exchange
524524
adapters.smilewanted.enabled=true
525-
adapters.smilewanted.endpoint=http://localhost:8090/smilewanted-exchange
525+
adapters.smilewanted.endpoint=http://localhost:8090/smilewanted-exchange/java/{{ZoneId}}
526526
adapters.smoot.enabled=true
527527
adapters.smoot.endpoint=http://localhost:8090/smoot-exchange
528528
adapters.smrtconnect.enabled=true

0 commit comments

Comments
 (0)