Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Eplanning Bidder with sizes priority by device type #1143

Merged
merged 4 commits into from
Feb 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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