diff --git a/src/main/java/org/prebid/server/bidder/eplanning/EplanningBidder.java b/src/main/java/org/prebid/server/bidder/eplanning/EplanningBidder.java index 245495c6a58..f71948129c6 100644 --- a/src/main/java/org/prebid/server/bidder/eplanning/EplanningBidder.java +++ b/src/main/java/org/prebid/server/bidder/eplanning/EplanningBidder.java @@ -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; @@ -62,6 +64,13 @@ public class EplanningBidder implements Bidder { CleanStepName.of("\\)\\(|\\(|\\)|:", "_"), CleanStepName.of("^_+|_+$", "")); + private static final Set MOBILE_DEVICE_TYPES = new HashSet<>(Arrays.asList(1, 4, 5)); + private static final String SIZE_FORMAT = "%sx%s"; + private static final List PRIORITY_SIZES_FOR_MOBILE + = new ArrayList<>(Arrays.asList("300x250", "320x50", "300x50", "1x1")); + private static final List PRIORITY_SIZES_FOR_DESKTOP = new ArrayList<>( + Arrays.asList("300x250", "728x90", "300x600", "160x600", "970x250", "970x90", "1x1")); + private static final TypeReference> EPLANNING_EXT_TYPE_REFERENCE = new TypeReference>() { }; @@ -78,6 +87,7 @@ public EplanningBidder(String endpointUrl, JacksonMapper mapper) { public Result>> makeHttpRequests(BidRequest request) { final List errors = new ArrayList<>(); final List requestsStrings = new ArrayList<>(); + boolean isMobile = isMobile(request); String clientId = null; for (final Imp imp : request.getImp()) { @@ -88,7 +98,7 @@ public Result>> 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) { @@ -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 { @@ -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 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 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 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) { @@ -274,6 +289,7 @@ public Result> makeBids(HttpCall httpCall, BidRequest bidR private Result> extractBids(HbResponse hbResponse, BidRequest bidRequest) { final Map nameSpaceToImpId = new HashMap<>(); + boolean isMobile = isMobile(bidRequest); for (Imp imp : bidRequest.getImp()) { final ExtImpEplanning impExt; try { @@ -281,7 +297,7 @@ private Result> extractBids(HbResponse hbResponse, BidRequest bi } catch (PreBidException e) { continue; } - final String name = getCleanAdUnitCode(impExt, () -> resolveSizeString(imp)); + final String name = getCleanAdUnitCode(impExt, () -> resolveSizeString(imp, isMobile)); nameSpaceToImpId.put(name, imp.getId()); } diff --git a/src/test/java/org/prebid/server/bidder/eplanning/EplanningBidderTest.java b/src/test/java/org/prebid/server/bidder/eplanning/EplanningBidderTest.java index a20075c653d..66fd6703287 100644 --- a/src/test/java/org/prebid/server/bidder/eplanning/EplanningBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/eplanning/EplanningBidderTest.java @@ -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; @@ -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; @@ -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 @@ -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())); } @@ -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"); } @@ -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"); } @@ -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 @@ -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>> 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>> 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>> 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>> 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 @@ -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"); } @@ -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"); } @@ -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"); } diff --git a/src/test/java/org/prebid/server/it/EplanningTest.java b/src/test/java/org/prebid/server/it/EplanningTest.java index df239b9d3d2..172f51f3cab 100644 --- a/src/test/java/org/prebid/server/it/EplanningTest.java +++ b/src/test/java/org/prebid/server/it/EplanningTest.java @@ -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")) diff --git a/src/test/resources/org/prebid/server/it/openrtb2/eplanning/test-auction-eplanning-request.json b/src/test/resources/org/prebid/server/it/openrtb2/eplanning/test-auction-eplanning-request.json index f9217cad610..b5f0696bfc6 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/eplanning/test-auction-eplanning-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/eplanning/test-auction-eplanning-request.json @@ -6,8 +6,8 @@ "banner": { "format": [ { - "w": 600, - "h": 300 + "w": 300, + "h": 600 } ] },