diff --git a/extra/modules/wurfl-devicedetection/src/main/java/org/prebid/server/hooks/modules/com/scientiamobile/wurfl/devicedetection/v1/OrtbDeviceUpdater.java b/extra/modules/wurfl-devicedetection/src/main/java/org/prebid/server/hooks/modules/com/scientiamobile/wurfl/devicedetection/v1/OrtbDeviceUpdater.java index b9b624b4f5b..ec79b79049c 100644 --- a/extra/modules/wurfl-devicedetection/src/main/java/org/prebid/server/hooks/modules/com/scientiamobile/wurfl/devicedetection/v1/OrtbDeviceUpdater.java +++ b/extra/modules/wurfl-devicedetection/src/main/java/org/prebid/server/hooks/modules/com/scientiamobile/wurfl/devicedetection/v1/OrtbDeviceUpdater.java @@ -55,6 +55,7 @@ public AuctionRequestPayload apply(AuctionRequestPayload auctionRequestPayload) private Device update(Device ortbDevice) { final String make = tryUpdateField(ortbDevice.getMake(), this::getWurflMake); final String model = tryUpdateField(ortbDevice.getModel(), this::getWurflModel); + final String hwv = tryUpdateField(ortbDevice.getHwv(), this::getWurflModel); final Integer deviceType = tryUpdateField( Optional.ofNullable(ortbDevice.getDevicetype()) .filter(it -> it > 0) @@ -72,6 +73,7 @@ private Device update(Device ortbDevice) { .make(make) .model(model) .devicetype(deviceType) + .hwv(hwv) .os(os) .osv(osv) .h(h) @@ -103,49 +105,72 @@ private String getWurflModel() { } private Integer getWurflDeviceType() { - try { - if (wurflDevice.getVirtualCapabilityAsBool("is_mobile")) { - // if at least one of these capabilities is not defined, the mobile device type is undefined - final boolean isPhone = wurflDevice.getVirtualCapabilityAsBool("is_phone"); - final boolean isTablet = wurflDevice.getCapabilityAsBool("is_tablet"); - return isPhone || isTablet ? 1 : 6; - } - if (wurflDevice.getVirtualCapabilityAsBool("is_full_desktop")) { - return 2; - } + if (getWurflIsOtt()) { + return 7; + } - if (wurflDevice.getCapabilityAsBool("is_connected_tv")) { - return 3; - } + if (getWurflIsConsole()) { + return 6; + } - if (wurflDevice.getCapabilityAsBool("is_phone")) { - return 4; - } + if ("out_of_home_device".equals(getWurflPhysicalFormFactor())) { + return 8; + } - if (wurflDevice.getCapabilityAsBool("is_tablet")) { - return 5; - } + final String formFactor = getWurflFormFactor(); + return switch (formFactor) { + case "Desktop" -> 2; + case "Smartphone", "Feature Phone" -> 4; + case "Tablet" -> 5; + case "Smart-TV" -> 3; + case "Other Non-Mobile" -> 6; + case "Other Mobile" -> 1; + default -> null; + }; + } - if (wurflDevice.getCapabilityAsBool("is_ott")) { - return 7; - } + private Boolean getWurflIsOtt() { + try { + return wurflDevice.getCapabilityAsBool("is_ott"); + } catch (CapabilityNotDefinedException e) { + logger.warn("Failed to get is_ott from WURFL device capabilities"); + return Boolean.FALSE; + } + } - final String physicalFormFactor = wurflDevice.getCapability("physical_form_factor"); - if (physicalFormFactor != null && physicalFormFactor.equals("out_of_home_device")) { - return 8; - } - } catch (CapabilityNotDefinedException | VirtualCapabilityNotDefinedException | NumberFormatException e) { - logger.warn("Failed to determine device type from WURFL device capabilities", e); + private String getWurflFormFactor() { + try { + return wurflDevice.getVirtualCapability("form_factor"); + } catch (VirtualCapabilityNotDefinedException e) { + logger.warn("Failed to get form_factor from WURFL device capabilities"); + return ""; + } + } + + private String getWurflPhysicalFormFactor() { + try { + return wurflDevice.getCapability("physical_form_factor"); + } catch (CapabilityNotDefinedException e) { + logger.warn("Failed to get physical_form_factor from WURFL device capabilities"); + return ""; + } + } + + private Boolean getWurflIsConsole() { + try { + return wurflDevice.getCapabilityAsBool("is_console"); + } catch (CapabilityNotDefinedException e) { + logger.warn("Failed to get is_console from WURFL device capabilities"); + return Boolean.FALSE; } - return null; } private String getWurflOs() { try { return wurflDevice.getVirtualCapability("advertised_device_os"); } catch (VirtualCapabilityNotDefinedException e) { - logger.warn("Failed to evaluate advertised device OS", e); + logger.warn("Failed to evaluate advertised device OS"); return null; } } @@ -154,7 +179,7 @@ private String getWurflOsv() { try { return wurflDevice.getVirtualCapability("advertised_device_os_version"); } catch (VirtualCapabilityNotDefinedException e) { - logger.warn("Failed to evaluate advertised device OS version", e); + logger.warn("Failed to evaluate advertised device OS version"); } return null; } @@ -163,7 +188,7 @@ private Integer getWurflH() { try { return wurflDevice.getCapabilityAsInt("resolution_height"); } catch (NumberFormatException e) { - logger.warn("Failed to get resolution height from WURFL device capabilities", e); + logger.warn("Failed to get resolution height from WURFL device capabilities"); return null; } } @@ -172,7 +197,7 @@ private Integer getWurflW() { try { return wurflDevice.getCapabilityAsInt("resolution_width"); } catch (NumberFormatException e) { - logger.warn("Failed to get resolution width from WURFL device capabilities", e); + logger.warn("Failed to get resolution width from WURFL device capabilities"); return null; } } @@ -181,7 +206,7 @@ private Integer getWurflPpi() { try { return wurflDevice.getVirtualCapabilityAsInt("pixel_density"); } catch (VirtualCapabilityNotDefinedException e) { - logger.warn("Failed to get pixel density from WURFL device capabilities", e); + logger.warn("Failed to get pixel density from WURFL device capabilities"); return null; } } @@ -193,7 +218,7 @@ private BigDecimal getWurflPxRatio() { ? new BigDecimal(densityAsString) : null; } catch (CapabilityNotDefinedException | NumberFormatException e) { - logger.warn("Failed to get pixel ratio from WURFL device capabilities", e); + logger.warn("Failed to get pixel ratio from WURFL device capabilities"); return null; } } @@ -202,7 +227,7 @@ private Integer getWurflJs() { try { return wurflDevice.getCapabilityAsBool("ajax_support_javascript") ? 1 : 0; } catch (CapabilityNotDefinedException | NumberFormatException e) { - logger.warn("Failed to get JS support from WURFL device capabilities", e); + logger.warn("Failed to get JS support from WURFL device capabilities"); return null; } } diff --git a/extra/modules/wurfl-devicedetection/src/main/java/org/prebid/server/hooks/modules/com/scientiamobile/wurfl/devicedetection/v1/WURFLDeviceDetectionRawAuctionRequestHook.java b/extra/modules/wurfl-devicedetection/src/main/java/org/prebid/server/hooks/modules/com/scientiamobile/wurfl/devicedetection/v1/WURFLDeviceDetectionRawAuctionRequestHook.java index 337343f15d4..5a92e051f0c 100644 --- a/extra/modules/wurfl-devicedetection/src/main/java/org/prebid/server/hooks/modules/com/scientiamobile/wurfl/devicedetection/v1/WURFLDeviceDetectionRawAuctionRequestHook.java +++ b/extra/modules/wurfl-devicedetection/src/main/java/org/prebid/server/hooks/modules/com/scientiamobile/wurfl/devicedetection/v1/WURFLDeviceDetectionRawAuctionRequestHook.java @@ -1,6 +1,7 @@ package org.prebid.server.hooks.modules.com.scientiamobile.wurfl.devicedetection.v1; import com.iab.openrtb.request.Device; +import org.prebid.server.proto.openrtb.ext.request.ExtDevice; import com.iab.openrtb.request.BidRequest; import org.prebid.server.log.Logger; import org.prebid.server.log.LoggerFactory; @@ -31,6 +32,7 @@ public class WURFLDeviceDetectionRawAuctionRequestHook implements RawAuctionRequ private static final Logger logger = LoggerFactory.getLogger(WURFLDeviceDetectionRawAuctionRequestHook.class); public static final String CODE = "wurfl-devicedetection-raw-auction-request"; + private static final String WURFL_PROPERTY = "wurfl"; private final WURFLService wurflService; private final Set allowedPublisherIDs; @@ -62,6 +64,11 @@ public Future> call(AuctionRequestPayloa return noActionResult(); } + if (isDeviceAlreadyEnriched(device)) { + logger.info("Device is already enriched, returning original bid request"); + return noActionResult(); + } + final Map requestHeaders = invocationContext.moduleContext() instanceof AuctionRequestHeadersContext moduleContext ? moduleContext.getHeaders() @@ -87,6 +94,18 @@ public Future> call(AuctionRequestPayloa .build()); } + private boolean isDeviceAlreadyEnriched(Device device) { + final ExtDevice extDevice = device.getExt(); + if (extDevice != null && extDevice.containsProperty(WURFL_PROPERTY)) { + return true; + } + + // Check if other some of the other Device data are already set + final Integer deviceType = device.getDevicetype(); + final String hwv = device.getHwv(); + return deviceType != null && deviceType > 0 && StringUtils.isNotEmpty(hwv); + } + private boolean shouldEnrichDevice(AuctionInvocationContext invocationContext) { return CollectionUtils.isEmpty(allowedPublisherIDs) || isAccountValid(invocationContext.auctionContext()); } diff --git a/extra/modules/wurfl-devicedetection/src/test/java/org/prebid/server/hooks/modules/com/scientiamobile/wurfl/devicedetection/v1/OrtbDeviceUpdaterTest.java b/extra/modules/wurfl-devicedetection/src/test/java/org/prebid/server/hooks/modules/com/scientiamobile/wurfl/devicedetection/v1/OrtbDeviceUpdaterTest.java index 54e7464619d..43300b0ff92 100644 --- a/extra/modules/wurfl-devicedetection/src/test/java/org/prebid/server/hooks/modules/com/scientiamobile/wurfl/devicedetection/v1/OrtbDeviceUpdaterTest.java +++ b/extra/modules/wurfl-devicedetection/src/test/java/org/prebid/server/hooks/modules/com/scientiamobile/wurfl/devicedetection/v1/OrtbDeviceUpdaterTest.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.node.TextNode; import com.iab.openrtb.request.Device; +import com.scientiamobile.wurfl.core.exc.VirtualCapabilityNotDefinedException; import org.mockito.Mock; import org.prebid.server.proto.openrtb.ext.request.ExtDevice; import org.junit.jupiter.api.BeforeEach; @@ -19,6 +20,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.Mock.Strictness.LENIENT; +import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class OrtbDeviceUpdaterTest { @@ -46,13 +48,10 @@ void setUp() { @Test public void updateShouldUpdateDeviceMakeWhenOriginalIsEmpty() { // given - given(wurflDevice.getCapability("brand_name")).willReturn("Apple"); - given(wurflDevice.getCapability("model_name")).willReturn("iPhone"); - given(wurflDevice.getCapabilityAsBool("ajax_support_javascript")).willReturn(true); - given(wurflDevice.getVirtualCapabilityAsBool("is_mobile")).willReturn(true); - given(wurflDevice.getVirtualCapabilityAsBool("is_phone")).willReturn(true); + given(wurflDevice.getCapability("brand_name")).willReturn("TestPhone"); given(wurflDevice.getCapabilityAsBool("is_tablet")).willReturn(false); given(wurflDevice.getVirtualCapabilityAsBool("is_full_desktop")).willReturn(false); + given(wurflDevice.getVirtualCapability("form_factor")).willReturn("Smartphone"); final OrtbDeviceUpdater target = new OrtbDeviceUpdater(wurflDevice, staticCaps, virtualCaps, true, mapper); final Device device = Device.builder().build(); final BidRequest bidRequest = BidRequest.builder().device(device).build(); @@ -62,22 +61,18 @@ public void updateShouldUpdateDeviceMakeWhenOriginalIsEmpty() { // then final Device resultDevice = result.bidRequest().getDevice(); - assertThat(resultDevice.getMake()).isEqualTo("Apple"); - assertThat(resultDevice.getDevicetype()).isEqualTo(1); + assertThat(resultDevice.getMake()).isEqualTo("TestPhone"); + assertThat(resultDevice.getDevicetype()).isEqualTo(4); } @Test public void updateShouldNotUpdateDeviceMakeWhenOriginalExists() { // given - final Device device = Device.builder().make("Samsung").build(); + final Device device = Device.builder().make("Samphone").model("youPhone").build(); final BidRequest bidRequest = BidRequest.builder().device(device).build(); - given(wurflDevice.getCapability("brand_name")).willReturn("Apple"); - given(wurflDevice.getCapability("model_name")).willReturn("iPhone"); - given(wurflDevice.getCapability("ajax_support_javascript")).willReturn("true"); - given(wurflDevice.getVirtualCapability("is_mobile")).willReturn("true"); - given(wurflDevice.getVirtualCapability("is_phone")).willReturn("true"); - given(wurflDevice.getCapability("is_tablet")).willReturn("false"); - given(wurflDevice.getVirtualCapability("is_full_desktop")).willReturn("false"); + given(wurflDevice.getCapability("brand_name")).willReturn("TestPhone"); + given(wurflDevice.getCapability("model_name")).willReturn("youPhone"); + given(wurflDevice.getVirtualCapability("form_factor")).willReturn("Smartphone"); final OrtbDeviceUpdater target = new OrtbDeviceUpdater(wurflDevice, staticCaps, virtualCaps, true, mapper); given(payload.bidRequest()).willReturn(bidRequest); @@ -86,21 +81,18 @@ public void updateShouldNotUpdateDeviceMakeWhenOriginalExists() { // then final Device resultDevice = result.bidRequest().getDevice(); - assertThat(resultDevice.getMake()).isEqualTo("Samsung"); + assertThat(resultDevice.getMake()).isEqualTo("Samphone"); + assertThat(resultDevice.getModel()).isEqualTo("youPhone"); } @Test public void updateShouldNotUpdateDeviceMakeWhenOriginalBigIntegerExists() { // given - final Device device = Device.builder().make("Apple").pxratio(new BigDecimal("1.0")).build(); + final Device device = Device.builder().make("TestPhone").pxratio(new BigDecimal("1.0")).build(); final BidRequest bidRequest = BidRequest.builder().device(device).build(); - given(wurflDevice.getCapability("brand_name")).willReturn("Apple"); - given(wurflDevice.getCapability("model_name")).willReturn("iPhone"); - given(wurflDevice.getCapability("ajax_support_javascript")).willReturn("true"); - given(wurflDevice.getVirtualCapability("is_mobile")).willReturn("true"); - given(wurflDevice.getVirtualCapability("is_phone")).willReturn("true"); - given(wurflDevice.getCapability("is_tablet")).willReturn("false"); - given(wurflDevice.getVirtualCapability("is_full_desktop")).willReturn("false"); + given(wurflDevice.getCapability("brand_name")).willReturn("TestPhone"); + given(wurflDevice.getCapability("density_class")).willReturn("2.2"); + given(wurflDevice.getVirtualCapability("form_factor")).willReturn("Smartphone"); final OrtbDeviceUpdater target = new OrtbDeviceUpdater(wurflDevice, staticCaps, virtualCaps, true, mapper); given(payload.bidRequest()).willReturn(bidRequest); @@ -109,7 +101,7 @@ public void updateShouldNotUpdateDeviceMakeWhenOriginalBigIntegerExists() { // then final Device resultDevice = result.bidRequest().getDevice(); - assertThat(resultDevice.getMake()).isEqualTo("Apple"); + assertThat(resultDevice.getMake()).isEqualTo("TestPhone"); assertThat(resultDevice.getPxratio()).isEqualTo("1.0"); } @@ -118,13 +110,8 @@ public void updateShouldUpdateDeviceModelWhenOriginalIsEmpty() { // given final Device device = Device.builder().build(); final BidRequest bidRequest = BidRequest.builder().device(device).build(); - given(wurflDevice.getCapability("brand_name")).willReturn("Apple"); - given(wurflDevice.getCapability("model_name")).willReturn("iPhone"); - given(wurflDevice.getCapability("ajax_support_javascript")).willReturn("true"); - given(wurflDevice.getVirtualCapability("is_mobile")).willReturn("true"); - given(wurflDevice.getVirtualCapability("is_phone")).willReturn("true"); - given(wurflDevice.getCapability("is_tablet")).willReturn("false"); - given(wurflDevice.getVirtualCapability("is_full_desktop")).willReturn("false"); + given(wurflDevice.getCapability("model_name")).willReturn("youPhone"); + given(wurflDevice.getVirtualCapability("form_factor")).willReturn("Smartphone"); final OrtbDeviceUpdater target = new OrtbDeviceUpdater(wurflDevice, staticCaps, virtualCaps, true, mapper); given(payload.bidRequest()).willReturn(bidRequest); @@ -133,7 +120,7 @@ public void updateShouldUpdateDeviceModelWhenOriginalIsEmpty() { // then final Device resultDevice = result.bidRequest().getDevice(); - assertThat(resultDevice.getModel()).isEqualTo("iPhone"); + assertThat(resultDevice.getModel()).isEqualTo("youPhone"); } @Test @@ -141,10 +128,8 @@ public void updateShouldUpdateDeviceOsWhenOriginalIsEmpty() { // given final Device device = Device.builder().build(); final BidRequest bidRequest = BidRequest.builder().device(device).build(); - given(wurflDevice.getCapability("brand_name")).willReturn("Apple"); - given(wurflDevice.getCapability("model_name")).willReturn("iPhone"); - given(wurflDevice.getVirtualCapability("advertised_device_os")).willReturn("iOS"); - given(wurflDevice.getVirtualCapabilityAsBool("is_full_desktop")).willReturn(false); + given(wurflDevice.getVirtualCapability("advertised_device_os")).willReturn("testOS"); + given(wurflDevice.getVirtualCapability("form_factor")).willReturn("Smartphone"); final OrtbDeviceUpdater target = new OrtbDeviceUpdater(wurflDevice, staticCaps, virtualCaps, true, mapper); given(payload.bidRequest()).willReturn(bidRequest); @@ -153,7 +138,7 @@ public void updateShouldUpdateDeviceOsWhenOriginalIsEmpty() { // then final Device resultDevice = result.bidRequest().getDevice(); - assertThat(resultDevice.getOs()).isEqualTo("iOS"); + assertThat(resultDevice.getOs()).isEqualTo("testOS"); } @Test @@ -161,13 +146,7 @@ public void updateShouldUpdateResolutionWhenOriginalIsEmpty() { // given final Device device = Device.builder().build(); final BidRequest bidRequest = BidRequest.builder().device(device).build(); - given(wurflDevice.getCapability("brand_name")).willReturn("Apple"); - given(wurflDevice.getCapability("model_name")).willReturn("iPhone"); - given(wurflDevice.getCapabilityAsBool("ajax_support_javascript")).willReturn(true); - given(wurflDevice.getVirtualCapabilityAsBool("is_mobile")).willReturn(true); - given(wurflDevice.getVirtualCapabilityAsBool("is_phone")).willReturn(true); - given(wurflDevice.getVirtualCapabilityAsBool("is_tablet")).willReturn(false); - given(wurflDevice.getVirtualCapabilityAsBool("is_full_desktop")).willReturn(false); + given(wurflDevice.getVirtualCapability("form_factor")).willReturn("Smartphone"); given(wurflDevice.getCapabilityAsInt("resolution_width")).willReturn(3200); given(wurflDevice.getCapabilityAsInt("resolution_height")).willReturn(1440); final OrtbDeviceUpdater target = new OrtbDeviceUpdater(wurflDevice, staticCaps, virtualCaps, true, mapper); @@ -188,6 +167,7 @@ public void updateShouldHandleJavascriptSupport() { final BidRequest bidRequest = BidRequest.builder().device(device).build(); given(wurflDevice.getCapabilityAsBool("ajax_support_javascript")).willReturn(true); given(wurflDevice.getVirtualCapabilityAsBool("is_mobile")).willReturn(true); + given(wurflDevice.getVirtualCapability("form_factor")).willReturn("Smartphone"); final OrtbDeviceUpdater target = new OrtbDeviceUpdater(wurflDevice, staticCaps, virtualCaps, true, mapper); given(payload.bidRequest()).willReturn(bidRequest); @@ -205,10 +185,6 @@ public void updateShouldHandleOttDeviceType() { final Device device = Device.builder().build(); final BidRequest bidRequest = BidRequest.builder().device(device).build(); given(wurflDevice.getCapabilityAsBool("is_ott")).willReturn(true); - given(wurflDevice.getVirtualCapabilityAsBool("is_full_desktop")).willReturn(false); - given(wurflDevice.getVirtualCapabilityAsBool("is_mobile")).willReturn(false); - given(wurflDevice.getVirtualCapabilityAsBool("is_phone")).willReturn(false); - given(wurflDevice.getCapabilityAsBool("is_tablet")).willReturn(false); final OrtbDeviceUpdater target = new OrtbDeviceUpdater(wurflDevice, staticCaps, virtualCaps, true, mapper); given(payload.bidRequest()).willReturn(bidRequest); // when @@ -225,8 +201,6 @@ public void updateShouldHandleOutOfHomeDeviceType() { final Device device = Device.builder().build(); final BidRequest bidRequest = BidRequest.builder().device(device).build(); given(wurflDevice.getCapability("physical_form_factor")).willReturn("out_of_home_device"); - given(wurflDevice.getCapabilityAsBool("is_tablet")).willReturn(false); - given(wurflDevice.getCapabilityAsBool("is_wireless_device")).willReturn(false); final OrtbDeviceUpdater target = new OrtbDeviceUpdater(wurflDevice, staticCaps, virtualCaps, true, mapper); given(payload.bidRequest()).willReturn(bidRequest); // when @@ -242,12 +216,7 @@ public void updateShouldReturnDeviceOtherMobileWhenMobileIsNotPhoneOrTablet() { // given final Device device = Device.builder().build(); final BidRequest bidRequest = BidRequest.builder().device(device).build(); - given(wurflDevice.getVirtualCapabilityAsBool("is_mobile")).willReturn(true); - given(wurflDevice.getVirtualCapabilityAsBool("is_phone")).willReturn(false); - given(wurflDevice.getCapabilityAsBool("is_tablet")).willReturn(false); - given(wurflDevice.getVirtualCapabilityAsBool("is_full_desktop")).willReturn(false); - given(wurflDevice.getVirtualCapability("advertised_device_os")).willReturn("TestOs"); - given(wurflDevice.getVirtualCapability("advertised_device_os_version")).willReturn("1.0"); + given(wurflDevice.getVirtualCapability("form_factor")).willReturn("Other Non-Mobile"); final OrtbDeviceUpdater target = new OrtbDeviceUpdater(wurflDevice, staticCaps, virtualCaps, true, mapper); given(payload.bidRequest()).willReturn(bidRequest); @@ -259,12 +228,28 @@ public void updateShouldReturnDeviceOtherMobileWhenMobileIsNotPhoneOrTablet() { } @Test - public void updateShouldReturnNullWhenMobileTypeIsUnknown() { + public void updateShouldReturnNullDeviceTypeWhenMobileTypeIsUnknown() { + // given + given(wurflDevice.getVirtualCapability("form_factor")).willReturn("Other non-Mobile"); + final Device device = Device.builder().build(); + final BidRequest bidRequest = BidRequest.builder().device(device).build(); + final OrtbDeviceUpdater target = new OrtbDeviceUpdater(wurflDevice, staticCaps, virtualCaps, true, mapper); + // when + given(payload.bidRequest()).willReturn(bidRequest); + final AuctionRequestPayload result = target.apply(payload); + // then + final Device resultDevice = result.bidRequest().getDevice(); + assertThat(resultDevice.getDevicetype()).isNull(); + } + + @Test + public void updateShouldReturnNullDeviceTypeWhenFormFactorIsMissing() { // given - given(wurflDevice.getVirtualCapability("is_mobile")).willReturn("false"); + given(wurflDevice.getVirtualCapability("is_mobile")).willReturn("true"); given(wurflDevice.getVirtualCapability("is_phone")).willReturn("false"); given(wurflDevice.getCapability("is_tablet")).willReturn("false"); - given(wurflDevice.getVirtualCapability("is_full_desktop")).willReturn("false"); + when(wurflDevice.getVirtualCapability("form_factor")) + .thenThrow(new VirtualCapabilityNotDefinedException("form_factor")); final Device device = Device.builder().build(); final BidRequest bidRequest = BidRequest.builder().device(device).build(); final OrtbDeviceUpdater target = new OrtbDeviceUpdater(wurflDevice, staticCaps, virtualCaps, true, mapper); @@ -276,17 +261,27 @@ public void updateShouldReturnNullWhenMobileTypeIsUnknown() { assertThat(resultDevice.getDevicetype()).isNull(); } + @Test + public void updateShouldReturnGenericMobileTabletDeviceTypeWhenFormFactorIsOtherMobile() { + // given + given(wurflDevice.getCapability("is_tablet")).willReturn("false"); + given(wurflDevice.getVirtualCapability("form_factor")).willReturn("Other Mobile"); + final Device device = Device.builder().build(); + final BidRequest bidRequest = BidRequest.builder().device(device).build(); + final OrtbDeviceUpdater target = new OrtbDeviceUpdater(wurflDevice, staticCaps, virtualCaps, true, mapper); + // when + given(payload.bidRequest()).willReturn(bidRequest); + final AuctionRequestPayload result = target.apply(payload); + // then + final Device resultDevice = result.bidRequest().getDevice(); + assertThat(resultDevice.getDevicetype()).isEqualTo(1); + } + @Test public void updateShouldHandlePersonalComputerDeviceType() { // given final Device device = Device.builder().build(); final BidRequest bidRequest = BidRequest.builder().device(device).build(); - given(wurflDevice.getVirtualCapabilityAsBool("is_mobile")).willReturn(false); - given(wurflDevice.getVirtualCapabilityAsBool("is_phone")).willReturn(false); - given(wurflDevice.getCapabilityAsBool("is_tablet")).willReturn(false); - given(wurflDevice.getVirtualCapabilityAsBool("is_full_desktop")).willReturn(true); - given(wurflDevice.getVirtualCapability("advertised_device_os")).willReturn("Windows"); - given(wurflDevice.getVirtualCapability("advertised_device_os_version")).willReturn("10"); given(wurflDevice.getVirtualCapability("form_factor")).willReturn("Desktop"); final OrtbDeviceUpdater target = new OrtbDeviceUpdater(wurflDevice, staticCaps, virtualCaps, true, mapper); // when @@ -303,14 +298,7 @@ public void updateShouldHandleConnectedTvDeviceType() { // given final Device device = Device.builder().build(); final BidRequest bidRequest = BidRequest.builder().device(device).build(); - given(wurflDevice.getCapabilityAsBool("is_connected_tv")).willReturn(true); - given(wurflDevice.getVirtualCapabilityAsBool("is_full_desktop")).willReturn(false); - given(wurflDevice.getVirtualCapabilityAsBool("is_mobile")).willReturn(false); - given(wurflDevice.getVirtualCapabilityAsBool("is_phone")).willReturn(false); - given(wurflDevice.getVirtualCapabilityAsBool("is_tablet")).willReturn(false); - given(wurflDevice.getVirtualCapability("advertised_device_os")).willReturn("WebOS"); - given(wurflDevice.getVirtualCapability("advertised_device_os_version")).willReturn("4"); - given(wurflDevice.getCapabilityAsBool("is_connected_tv")).willReturn(true); + given(wurflDevice.getVirtualCapability("form_factor")).willReturn("Smart-TV"); final OrtbDeviceUpdater target = new OrtbDeviceUpdater(wurflDevice, staticCaps, virtualCaps, true, mapper); // when given(payload.bidRequest()).willReturn(bidRequest); @@ -326,10 +314,6 @@ public void updateShouldNotUpdateDeviceTypeWhenSet() { final Device device = Device.builder() .devicetype(3) .build(); - given(wurflDevice.getVirtualCapabilityAsBool("is_full_desktop")).willReturn(true); - given(wurflDevice.getVirtualCapabilityAsBool("is_mobile")).willReturn(false); - given(wurflDevice.getVirtualCapabilityAsBool("is_phone")).willReturn(false); - given(wurflDevice.getVirtualCapabilityAsBool("is_tablet")).willReturn(false); // device type 2 final BidRequest bidRequest = BidRequest.builder().device(device).build(); final OrtbDeviceUpdater target = new OrtbDeviceUpdater(wurflDevice, staticCaps, virtualCaps, true, mapper); // when @@ -343,12 +327,7 @@ public void updateShouldNotUpdateDeviceTypeWhenSet() { @Test public void updateShouldHandleTabletDeviceType() { // given - given(wurflDevice.getCapabilityAsBool("is_tablet")).willReturn(true); - given(wurflDevice.getVirtualCapabilityAsBool("is_full_desktop")).willReturn(false); - given(wurflDevice.getVirtualCapabilityAsBool("is_mobile")).willReturn(false); - given(wurflDevice.getVirtualCapabilityAsBool("is_phone")).willReturn(false); - given(wurflDevice.getCapability("brand_name")).willReturn("Samsung"); - given(wurflDevice.getCapability("model_name")).willReturn("Galaxy Tab S9+"); + given(wurflDevice.getVirtualCapability("form_factor")).willReturn("Tablet"); final Device device = Device.builder().build(); final BidRequest bidRequest = BidRequest.builder().device(device).build(); @@ -362,6 +341,23 @@ public void updateShouldHandleTabletDeviceType() { assertThat(resultDevice.getDevicetype()).isEqualTo(5); } + @Test + public void updateShouldHandlePhoneDeviceType() { + // given + given(wurflDevice.getVirtualCapability("form_factor")).willReturn("Smartphone"); + + final Device device = Device.builder().build(); + final BidRequest bidRequest = BidRequest.builder().device(device).build(); + final OrtbDeviceUpdater target = new OrtbDeviceUpdater(wurflDevice, staticCaps, virtualCaps, true, mapper); + given(payload.bidRequest()).willReturn(bidRequest); + // when + final AuctionRequestPayload result = target.apply(payload); + + // then + final Device resultDevice = result.bidRequest().getDevice(); + assertThat(resultDevice.getDevicetype()).isEqualTo(4); + } + @Test public void updateShouldAddWurflPropertyToExtIfMissingAndPreserveExistingProperties() { // given @@ -371,13 +367,14 @@ public void updateShouldAddWurflPropertyToExtIfMissingAndPreserveExistingPropert .ext(existingExt) .build(); final BidRequest bidRequest = BidRequest.builder().device(device).build(); - given(wurflDevice.getCapability("brand_name")).willReturn("Apple"); - given(wurflDevice.getCapability("model_name")).willReturn("iPhone"); + given(wurflDevice.getCapability("brand_name")).willReturn("TestPhone"); + given(wurflDevice.getCapability("model_name")).willReturn("youPhone"); given(wurflDevice.getCapability("ajax_support_javascript")).willReturn("true"); given(wurflDevice.getVirtualCapability("is_mobile")).willReturn("true"); given(wurflDevice.getVirtualCapability("is_phone")).willReturn("true"); given(wurflDevice.getCapability("is_tablet")).willReturn("false"); given(wurflDevice.getVirtualCapability("is_full_desktop")).willReturn("false"); + given(wurflDevice.getVirtualCapability("form_factor")).willReturn("Smartphone"); final Set staticCaps = Set.of("brand_name"); final Set virtualCaps = Set.of("advertised_device_os"); final OrtbDeviceUpdater target = new OrtbDeviceUpdater(wurflDevice, staticCaps, virtualCaps, true, mapper); diff --git a/extra/modules/wurfl-devicedetection/src/test/java/org/prebid/server/hooks/modules/com/scientiamobile/wurfl/devicedetection/v1/WURFLDeviceDetectionRawAuctionRequestHookTest.java b/extra/modules/wurfl-devicedetection/src/test/java/org/prebid/server/hooks/modules/com/scientiamobile/wurfl/devicedetection/v1/WURFLDeviceDetectionRawAuctionRequestHookTest.java index 8d82ebb0209..f981f43ccaa 100644 --- a/extra/modules/wurfl-devicedetection/src/test/java/org/prebid/server/hooks/modules/com/scientiamobile/wurfl/devicedetection/v1/WURFLDeviceDetectionRawAuctionRequestHookTest.java +++ b/extra/modules/wurfl-devicedetection/src/test/java/org/prebid/server/hooks/modules/com/scientiamobile/wurfl/devicedetection/v1/WURFLDeviceDetectionRawAuctionRequestHookTest.java @@ -8,6 +8,9 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.proto.openrtb.ext.request.ExtDevicePrebid; +import org.prebid.server.proto.openrtb.ext.request.ExtDevice; +import org.prebid.server.proto.openrtb.ext.request.ExtDeviceInt; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.settings.model.Account; import org.prebid.server.json.JacksonMapper; @@ -20,6 +23,7 @@ import org.prebid.server.hooks.v1.auction.AuctionInvocationContext; import org.prebid.server.hooks.v1.auction.AuctionRequestPayload; import org.prebid.server.model.CaseInsensitiveMultiMap; +import com.fasterxml.jackson.databind.node.ObjectNode; import java.util.Collections; import java.util.Map; @@ -87,6 +91,87 @@ public void callShouldReturnNoActionWhenDeviceIsNull() { assertThat(result.action()).isEqualTo(InvocationAction.no_action); } + @Test + public void callShouldReturnNoActionWhenDeviceHasWurflProperty() { + // given + final String ua = "Mozilla/5.0 (testPhone; CPU testPhone OS 1_0_2) Version/17.4.1 Mobile/12E GrandTest/604.1"; + final ExtDevicePrebid extDevicePrebid = ExtDevicePrebid.of(ExtDeviceInt.of(80, 80)); + final ExtDevice extDevice = ExtDevice.of(0, extDevicePrebid); + final ObjectNode wurfl = mapper.mapper().createObjectNode(); + wurfl.put("wurfl_id", "test_phone_ver1"); + extDevice.addProperty("wurfl", wurfl); + final Device device = Device.builder() + .ua(ua) + .ext(extDevice) + .build(); + final BidRequest bidRequest = BidRequest.builder().device(device).build(); + given(payload.bidRequest()).willReturn(bidRequest); + given(configProperties.getAllowedPublisherIds()).willReturn(Collections.emptySet()); + final WURFLService wurflService = new WURFLService(wurflEngine, configProperties); + target = new WURFLDeviceDetectionRawAuctionRequestHook(wurflService, configProperties, mapper); + + // when + final InvocationResult result = target.call(payload, context).result(); + + // then + assertThat(result.status()).isEqualTo(InvocationStatus.success); + assertThat(result.action()).isEqualTo(InvocationAction.no_action); + } + + @Test + public void callShouldReturnNoActionWhenDeviceHasDeviceTypeAndHwv() { + // given + final String ua = "Mozilla/5.0 (testPhone; CPU testPhone OS 1_0_2) Version/17.4.1 Mobile/12E GrandTest/604.1"; + final Device device = Device.builder() + .ua(ua) + .hwv("test_phone_ver1") + .devicetype(1) + .build(); + final BidRequest bidRequest = BidRequest.builder().device(device).build(); + given(payload.bidRequest()).willReturn(bidRequest); + given(configProperties.getAllowedPublisherIds()).willReturn(Collections.emptySet()); + final WURFLService wurflService = new WURFLService(wurflEngine, configProperties); + given(wurflDevice.getId()).willReturn("test_phone_ver1"); + target = new WURFLDeviceDetectionRawAuctionRequestHook(wurflService, configProperties, mapper); + + // when + final InvocationResult result = target.call(payload, context).result(); + + // then + assertThat(result.status()).isEqualTo(InvocationStatus.success); + assertThat(result.action()).isEqualTo(InvocationAction.no_action); + } + + @Test + public void callShouldReturnActionUpdateWhenDeviceHasJustDeviceType() { + // given + final String ua = "Mozilla/5.0 (testPhone; CPU testPhone OS 1_0_2) Version/17.4.1 Mobile/12E GrandTest/604.1"; + given(configProperties.getAllowedPublisherIds()).willReturn(Collections.emptySet()); + final WURFLService wurflService = new WURFLService(wurflEngine, configProperties); + target = new WURFLDeviceDetectionRawAuctionRequestHook(wurflService, configProperties, mapper); + final Device device = Device.builder() + .ua(ua) + .devicetype(1) + .build(); + final BidRequest bidRequest = BidRequest.builder().device(device).build(); + given(payload.bidRequest()).willReturn(bidRequest); + final CaseInsensitiveMultiMap headers = CaseInsensitiveMultiMap.builder() + .add("User-Agent", ua) + .build(); + final AuctionRequestHeadersContext headersContext = AuctionRequestHeadersContext.from(headers); + + given(context.moduleContext()).willReturn(headersContext); + given(wurflEngine.getDeviceForRequest(any(Map.class))).willReturn(wurflDevice); + given(wurflDevice.getId()).willReturn("test_phone_ver1"); + + // when + final InvocationResult result = target.call(payload, context).result(); + + // then + assertThat(result.status()).isEqualTo(InvocationStatus.success); + assertThat(result.action()).isEqualTo(InvocationAction.update); + } + @Test public void callShouldUpdateDeviceWhenWurflDeviceIsDetected() throws Exception { // given diff --git a/sample/configs/prebid-config-with-wurfl.yaml b/sample/configs/prebid-config-with-wurfl.yaml index 5dff90201ee..0ce8abebe17 100644 --- a/sample/configs/prebid-config-with-wurfl.yaml +++ b/sample/configs/prebid-config-with-wurfl.yaml @@ -23,6 +23,7 @@ settings: filesystem: settings-filename: sample/configs/sample-app-settings.yaml stored-requests-dir: sample + profiles-dir: sample stored-imps-dir: sample stored-responses-dir: sample categories-dir: