Skip to content

Commit ba568a9

Browse files
SmileWanted endpoint now supports dynamic zoneId and integrates prebid server technology (#4109)
1 parent ad37434 commit ba568a9

File tree

7 files changed

+234
-22
lines changed

7 files changed

+234
-22
lines changed

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

Lines changed: 32 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;
@@ -27,6 +31,11 @@ public class SmileWantedBidder implements Bidder<BidRequest> {
2731
private static final String SW_INTEGRATION_TYPE = "prebid_server";
2832
private static final String X_OPENRTB_VERSION = "2.5";
2933
private static final int DEFAULT_AT = 1;
34+
private static final String ZONE_ID_MACRO = "{{ZoneId}}";
35+
36+
private static final TypeReference<ExtPrebid<?, ExtImpSmilewanted>> SMILEWANTED_EXT_TYPE_REFERENCE =
37+
new TypeReference<>() {
38+
};
3039

3140
private final String endpointUrl;
3241
private final JacksonMapper mapper;
@@ -38,15 +47,30 @@ public SmileWantedBidder(String endpointUrl, JacksonMapper mapper) {
3847

3948
@Override
4049
public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request) {
50+
final ExtImpSmilewanted extImpSmilewanted;
51+
52+
try {
53+
extImpSmilewanted = parseImpExt(request.getImp().getFirst());
54+
} catch (PreBidException e) {
55+
return Result.withError(BidderError.badInput(e.getMessage()));
56+
}
57+
4158
final BidRequest outgoingRequest = request.toBuilder().at(DEFAULT_AT).build();
59+
final String url = endpointUrl.replace(ZONE_ID_MACRO, HttpUtil.encodeUrl(extImpSmilewanted.getZoneId()));
4260

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());
61+
return Result.withValue(BidderUtil.defaultRequest(
62+
outgoingRequest,
63+
createHeaders(),
64+
url,
65+
mapper));
66+
}
67+
68+
private ExtImpSmilewanted parseImpExt(Imp imp) {
69+
try {
70+
return mapper.mapper().convertValue(imp.getExt(), SMILEWANTED_EXT_TYPE_REFERENCE).getBidder();
71+
} catch (IllegalArgumentException e) {
72+
throw new PreBidException("Missing bidder ext in impression with id: " + imp.getId());
73+
}
5074
}
5175

5276
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/{{ZoneId}}
44
meta-info:
55
maintainer-email: [email protected]
66
app-media-types:

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

Lines changed: 187 additions & 10 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);
@@ -98,7 +170,7 @@ public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() {
98170
@Test
99171
public void makeBidsShouldReturnEmptyListIfBidResponseIsNull() throws JsonProcessingException {
100172
// given
101-
final BidderCall<BidRequest> httpCall = givenHttpCall(null, mapper.writeValueAsString(null));
173+
final BidderCall<BidRequest> httpCall = givenHttpCall(null, (BidResponse) null);
102174

103175
// when
104176
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
@@ -111,8 +183,7 @@ public void makeBidsShouldReturnEmptyListIfBidResponseIsNull() throws JsonProces
111183
@Test
112184
public void makeBidsShouldReturnEmptyListIfBidResponseSeatBidIsNull() throws JsonProcessingException {
113185
// given
114-
final BidderCall<BidRequest> httpCall = givenHttpCall(null,
115-
mapper.writeValueAsString(BidResponse.builder().build()));
186+
final BidderCall<BidRequest> httpCall = givenHttpCall(null, BidResponse.builder().build());
116187

117188
// when
118189
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
@@ -130,8 +201,7 @@ public void makeBidsShouldReturnVideoBidIfVideoIsPresentInRequestImpAndCorrespon
130201
BidRequest.builder()
131202
.imp(singletonList(Imp.builder().id("123").video(Video.builder().build()).build()))
132203
.build(),
133-
mapper.writeValueAsString(
134-
givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
204+
givenBidResponse(bidBuilder -> bidBuilder.impid("123")));
135205

136206
// when
137207
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
@@ -148,8 +218,7 @@ public void makeBidsShouldReturnBannerBidIfVideoIsAbsentInRequestImp() throws Js
148218
final BidderCall<BidRequest> httpCall = givenHttpCall(BidRequest.builder()
149219
.imp(singletonList(Imp.builder().id("123").build()))
150220
.build(),
151-
mapper.writeValueAsString(
152-
givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
221+
givenBidResponse(bidBuilder -> bidBuilder.impid("123")));
153222

154223
// when
155224
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
@@ -160,6 +229,102 @@ public void makeBidsShouldReturnBannerBidIfVideoIsAbsentInRequestImp() throws Js
160229
.containsOnly(BidderBid.of(Bid.builder().impid("123").build(), banner, null));
161230
}
162231

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

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
@@ -530,7 +530,7 @@ adapters.smarthub.aliases.artechnology.endpoint=http://localhost:8090/artechnolo
530530
adapters.smartyads.enabled=true
531531
adapters.smartyads.endpoint=http://localhost:8090/smartyads-exchange
532532
adapters.smilewanted.enabled=true
533-
adapters.smilewanted.endpoint=http://localhost:8090/smilewanted-exchange
533+
adapters.smilewanted.endpoint=http://localhost:8090/smilewanted-exchange/java/{{ZoneId}}
534534
adapters.smoot.enabled=true
535535
adapters.smoot.endpoint=http://localhost:8090/smoot-exchange
536536
adapters.smrtconnect.enabled=true

0 commit comments

Comments
 (0)