Skip to content

Commit

Permalink
Update Eplanning Bidder with sizes priority by device type (#1143)
Browse files Browse the repository at this point in the history
* Update Eplanning Bidder with sizes priority by device type

* Minor cleaning

* Replace containsOnly with containsExactly
  • Loading branch information
BraslavskiyAndrey authored Feb 17, 2021
1 parent a661eff commit 238bdad
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand All @@ -62,6 +64,13 @@ public class EplanningBidder implements Bidder<Void> {
CleanStepName.of("\\)\\(|\\(|\\)|:", "_"),
CleanStepName.of("^_+|_+$", ""));

private static final Set<Integer> MOBILE_DEVICE_TYPES = new HashSet<>(Arrays.asList(1, 4, 5));
private static final String SIZE_FORMAT = "%sx%s";
private static final List<String> PRIORITY_SIZES_FOR_MOBILE
= new ArrayList<>(Arrays.asList("300x250", "320x50", "300x50", "1x1"));
private static final List<String> PRIORITY_SIZES_FOR_DESKTOP = new ArrayList<>(
Arrays.asList("300x250", "728x90", "300x600", "160x600", "970x250", "970x90", "1x1"));

private static final TypeReference<ExtPrebid<?, ExtImpEplanning>> EPLANNING_EXT_TYPE_REFERENCE =
new TypeReference<ExtPrebid<?, ExtImpEplanning>>() {
};
Expand All @@ -78,6 +87,7 @@ public EplanningBidder(String endpointUrl, JacksonMapper mapper) {
public Result<List<HttpRequest<Void>>> makeHttpRequests(BidRequest request) {
final List<BidderError> errors = new ArrayList<>();
final List<String> requestsStrings = new ArrayList<>();
boolean isMobile = isMobile(request);

String clientId = null;
for (final Imp imp : request.getImp()) {
Expand All @@ -88,7 +98,7 @@ public Result<List<HttpRequest<Void>>> makeHttpRequests(BidRequest request) {
if (clientId == null) {
clientId = extImpEplanning.getClientId();
}
final String sizeString = resolveSizeString(imp);
final String sizeString = resolveSizeString(imp, isMobile);
final String name = getCleanAdUnitCode(extImpEplanning, () -> sizeString);
requestsStrings.add(name + ":" + sizeString);
} catch (PreBidException e) {
Expand Down Expand Up @@ -121,6 +131,12 @@ private static void validateImp(Imp imp) {
}
}

private boolean isMobile(BidRequest bidRequest) {
final Device device = bidRequest.getDevice();
final Integer deviceType = device != null ? device.getDevicetype() : null;
return MOBILE_DEVICE_TYPES.contains(deviceType);
}

private ExtImpEplanning validateAndModifyImpExt(Imp imp) throws PreBidException {
final ExtImpEplanning extImpEplanning;
try {
Expand All @@ -143,26 +159,25 @@ private ExtImpEplanning validateAndModifyImpExt(Imp imp) throws PreBidException
return extImpEplanning;
}

private static String resolveSizeString(Imp imp) {
private static String resolveSizeString(Imp imp, boolean isMobile) {
final Banner banner = imp.getBanner();
final Integer bannerWidth = banner.getW();
final Integer bannerHeight = banner.getH();
if (bannerWidth != null && bannerHeight != null) {
return String.format("%sx%s", bannerWidth, bannerHeight);
return String.format(SIZE_FORMAT, bannerWidth, bannerHeight);
}

final List<Format> bannerFormats = banner.getFormat();
if (CollectionUtils.isNotEmpty(bannerFormats)) {
for (Format format : bannerFormats) {
final Integer formatHeight = format.getH();
final Integer formatWidth = format.getW();
if (formatHeight != null && formatWidth != null) {
return String.format("%sx%s", formatWidth, formatHeight);
}
}
}

return NULL_SIZE;
final Set<String> formattedBannerSizes = CollectionUtils.emptyIfNull(bannerFormats).stream()
.filter(format -> format.getH() != null && format.getW() != null)
.map(format -> String.format(SIZE_FORMAT, format.getW(), format.getH()))
.collect(Collectors.toSet());

final List<String> prioritySizes = isMobile ? PRIORITY_SIZES_FOR_MOBILE : PRIORITY_SIZES_FOR_DESKTOP;
return prioritySizes.stream()
.filter(formattedBannerSizes::contains).findFirst()
.orElse(NULL_SIZE);
}

private static String cleanName(String name) {
Expand Down Expand Up @@ -274,14 +289,15 @@ public Result<List<BidderBid>> makeBids(HttpCall<Void> httpCall, BidRequest bidR

private Result<List<BidderBid>> extractBids(HbResponse hbResponse, BidRequest bidRequest) {
final Map<String, String> nameSpaceToImpId = new HashMap<>();
boolean isMobile = isMobile(bidRequest);
for (Imp imp : bidRequest.getImp()) {
final ExtImpEplanning impExt;
try {
impExt = validateAndModifyImpExt(imp);
} catch (PreBidException e) {
continue;
}
final String name = getCleanAdUnitCode(impExt, () -> resolveSizeString(imp));
final String name = getCleanAdUnitCode(impExt, () -> resolveSizeString(imp, isMobile));
nameSpaceToImpId.put(name, imp.getId());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.iab.openrtb.request.Banner;
import com.iab.openrtb.request.BidRequest;
import com.iab.openrtb.request.Device;
import com.iab.openrtb.request.Format;
import com.iab.openrtb.request.Imp;
import com.iab.openrtb.request.Site;
import com.iab.openrtb.request.User;
Expand Down Expand Up @@ -34,6 +35,7 @@
import java.util.Map;
import java.util.function.Function;

import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static java.util.function.Function.identity;
import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -117,13 +119,13 @@ public void makeHttpRequestsShouldSendSingleGetRequestWithNullBody() {

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue()).hasSize(1)
.extracting(HttpRequest::getBody)
assertThat(result.getValue())
.hasSize(1)
.extracting(HttpRequest::getBody)
.containsNull();
assertThat(result.getValue()).hasSize(1)
assertThat(result.getValue())
.extracting(HttpRequest::getMethod)
.containsOnly(HttpMethod.GET);
.containsExactly(HttpMethod.GET);
}

@Test
Expand All @@ -136,11 +138,11 @@ public void makeHttpRequestsShouldSetCorrectHeaders() {

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue()).hasSize(1)
assertThat(result.getValue())
.extracting(HttpRequest::getHeaders)
.flatExtracting(MultiMap::entries)
.extracting(Map.Entry::getKey, Map.Entry::getValue)
.containsOnly(
.containsExactly(
tuple(HttpUtil.CONTENT_TYPE_HEADER.toString(), HttpUtil.APPLICATION_JSON_CONTENT_TYPE),
tuple(HttpUtil.ACCEPT_HEADER.toString(), HttpHeaderValues.APPLICATION_JSON.toString()));
}
Expand Down Expand Up @@ -184,9 +186,9 @@ public void makeHttpRequestsShouldSetCorrectUriWithDefaults() {

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue()).hasSize(1)
assertThat(result.getValue())
.extracting(HttpRequest::getUri)
.containsOnly(
.containsExactly(
"https://eplanning.com/clientId/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadun_itco_de%3A1x1");
}

Expand All @@ -203,9 +205,9 @@ public void makeHttpRequestsShouldSetCorrectUriWithSitePageAndDomain() {

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue()).hasSize(1)
assertThat(result.getValue())
.extracting(HttpRequest::getUri)
.containsOnly(
.containsExactly(
"https://eplanning.com/clientId/1/DOMAIN/ROS?r=pbs&ncb=1&ur=https%3A%2F%2Fwww.example.com&e="
+ "testadun_itco_de%3A1x1");
}
Expand All @@ -223,14 +225,14 @@ public void makeHttpRequestsShouldSetCorrectUriIfSiteDomainIsBlank() {

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue()).hasSize(1)
assertThat(result.getValue())
.extracting(HttpRequest::getUri)
.containsOnly("https://eplanning.com/clientId/1/www.example.com/ROS?r=pbs&ncb=1"
.containsExactly("https://eplanning.com/clientId/1/www.example.com/ROS?r=pbs&ncb=1"
+ "&ur=https%3A%2F%2Fwww.example.com&e=testadun_itco_de%3A1x1");
}

@Test
public void makeHttpRequestsShouldSetCorrectUriWithSizeString() {
public void makeHttpRequestsShouldSetCorrectUriWithSizeStringFromBannerWAndH() {
// given
final BidRequest bidRequest = givenBidRequest(
impBuilder -> impBuilder
Expand All @@ -244,12 +246,100 @@ public void makeHttpRequestsShouldSetCorrectUriWithSizeString() {

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue()).hasSize(1)
assertThat(result.getValue())
.extracting(HttpRequest::getUri)
.containsOnly("https://eplanning.com/clientId/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadun_itco_de%3A"
.containsExactly("https://eplanning.com/clientId/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadun_itco_de%3A"
+ "300x200");
}

@Test
public void makeHttpRequestsShouldSetCorrectUriWithSizeStringFromFormatForMobile() {
// given
final BidRequest bidRequest = givenBidRequest(
bidRequestBuilder -> bidRequestBuilder.device(Device.builder().devicetype(1).build()),
impBuilder -> impBuilder
.banner(Banner.builder()
.format(asList(Format.builder().w(300).h(50).build(),
Format.builder().w(320).h(50).build()))
.build()));

// when
final Result<List<HttpRequest<Void>>> result = eplanningBidder.makeHttpRequests(bidRequest);

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue())
.extracting(HttpRequest::getUri)
.containsExactly("https://eplanning.com/clientId/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadun_itco_de%3A"
+ "320x50");
}

@Test
public void makeHttpRequestsShouldSetCorrectUriWithSizeStringFromFormatForDesktop() {
// given
final BidRequest bidRequest = givenBidRequest(
bidRequestBuilder -> bidRequestBuilder.device(Device.builder().devicetype(2).build()),
impBuilder -> impBuilder
.banner(Banner.builder()
.format(asList(Format.builder().w(300).h(600).build(),
Format.builder().w(728).h(90).build()))
.build()));

// when
final Result<List<HttpRequest<Void>>> result = eplanningBidder.makeHttpRequests(bidRequest);

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue())
.extracting(HttpRequest::getUri)
.containsExactly("https://eplanning.com/clientId/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadun_itco_de%3A"
+ "728x90");
}

@Test
public void makeHttpRequestsShouldTolerateAndDropInvalidFormats() {
// given
final BidRequest bidRequest = givenBidRequest(
bidRequestBuilder -> bidRequestBuilder.device(Device.builder().devicetype(2).build()),
impBuilder -> impBuilder
.banner(Banner.builder()
.format(asList(Format.builder().w(null).h(600).build(),
Format.builder().w(728).h(90).build()))
.build()));

// when
final Result<List<HttpRequest<Void>>> result = eplanningBidder.makeHttpRequests(bidRequest);

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue())
.extracting(HttpRequest::getUri)
.containsExactly("https://eplanning.com/clientId/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadun_itco_de%3A"
+ "728x90");
}

@Test
public void makeHttpRequestsShouldSetUriWithSize1x1WhenSizeWasNotFoundInPriority() {
// given
final BidRequest bidRequest = givenBidRequest(
bidRequestBuilder -> bidRequestBuilder.device(Device.builder().devicetype(2).build()),
impBuilder -> impBuilder
.banner(Banner.builder()
.format(asList(Format.builder().w(301).h(600).build(),
Format.builder().w(729).h(90).build()))
.build()));

// when
final Result<List<HttpRequest<Void>>> result = eplanningBidder.makeHttpRequests(bidRequest);

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue())
.extracting(HttpRequest::getUri)
.containsExactly("https://eplanning.com/clientId/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadun_itco_de%3A"
+ "1x1");
}

@Test
public void makeHttpRequestsShouldSetCorrectUriWithUserId() {
// given
Expand All @@ -263,9 +353,9 @@ public void makeHttpRequestsShouldSetCorrectUriWithUserId() {

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue()).hasSize(1)
assertThat(result.getValue())
.extracting(HttpRequest::getUri)
.containsOnly("https://eplanning.com/clientId/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadun_itco_de%3A"
.containsExactly("https://eplanning.com/clientId/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadun_itco_de%3A"
+ "1x1&uid=Buyer-ID");
}

Expand All @@ -282,9 +372,9 @@ public void makeHttpRequestsShouldSetCorrectUriWithDeviceIp() {

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue()).hasSize(1)
assertThat(result.getValue())
.extracting(HttpRequest::getUri)
.containsOnly("https://eplanning.com/clientId/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadun_itco_de%3A1x1"
.containsExactly("https://eplanning.com/clientId/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadun_itco_de%3A1x1"
+ "&ip=123.321.321.123");
}

Expand All @@ -302,9 +392,9 @@ public void makeHttpRequestsShouldSetCorrectUriWithApp() {

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue()).hasSize(1)
assertThat(result.getValue())
.extracting(HttpRequest::getUri)
.containsOnly("https://eplanning.com/clientId/1/FILE/ROS?r=pbs&ncb=1&e=testadun_itco_de%3A1x1&"
.containsExactly("https://eplanning.com/clientId/1/FILE/ROS?r=pbs&ncb=1&e=testadun_itco_de%3A1x1&"
+ "appn=appName&appid=id&ifa=ifa&app=1");
}

Expand Down
2 changes: 1 addition & 1 deletion src/test/java/org/prebid/server/it/EplanningTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public void openrtb2AuctionShouldRespondWithBidsFromEplanning() throws IOExcepti
.withQueryParam("r", equalTo("pbs"))
.withQueryParam("ncb", equalTo("1"))
.withQueryParam("ur", equalTo("https://www.example.com"))
.withQueryParam("e", equalTo("testadunitcode:600x300"))
.withQueryParam("e", equalTo("testadunitcode:300x600"))
.withQueryParam("ip", equalTo("193.168.244.1"))
.withHeader("Content-Type", equalToIgnoreCase("application/json;charset=utf-8"))
.withHeader("Accept", equalTo("application/json"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"banner": {
"format": [
{
"w": 600,
"h": 300
"w": 300,
"h": 600
}
]
},
Expand Down

0 comments on commit 238bdad

Please sign in to comment.