diff --git a/buildSrc/src/main/kotlin/Config.kt b/buildSrc/src/main/kotlin/Config.kt index c0b9cd9..7345ec5 100644 --- a/buildSrc/src/main/kotlin/Config.kt +++ b/buildSrc/src/main/kotlin/Config.kt @@ -4,8 +4,8 @@ object Config { const val minSdk = 21 const val major = 0 - const val minor = 8 - const val patch = 3 + const val minor = 9 + const val patch = 0 const val versionName = "$major.$minor.$patch" const val maven_group = "ch.srg.data.provider" diff --git a/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/request/image/DefaultImageUrlDecorator.kt b/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/request/image/DefaultImageUrlDecorator.kt index 7cc83f5..b74e0a3 100644 --- a/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/request/image/DefaultImageUrlDecorator.kt +++ b/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/request/image/DefaultImageUrlDecorator.kt @@ -1,31 +1,54 @@ package ch.srg.dataProvider.integrationlayer.request.image +import android.net.Uri import ch.srg.dataProvider.integrationlayer.data.ImageUrlDecorator import ch.srg.dataProvider.integrationlayer.request.IlHost /** - * Copyright (c) SRG SSR. All rights reserved. + * Il host image url decorator * + * If the image url isn't supported by [DefaultImageUrlDecorator] the same url is returned. * - * License information is available from the LICENSE file. - */ - -/** - * Default image url decorator + * confluence doc : https://srgssr-ch.atlassian.net/wiki/spaces/SRGPLAY/pages/799082429/Project+-+Image+Service) * - * For specific RTS image url, the old [ScaleWidthImageUrlDecorator] is used, but it should be fixed sooner or later. - * - * @param ilHost The [IlHost] to use with [ilHostImageUrlDecorator]. + * @param ilHost The [IlHost] of the integration layer image service. */ -class DefaultImageUrlDecorator(ilHost: IlHost = IlHost.PROD) : ImageUrlDecorator { - private val ilHostImageUrlDecorator = IlHostImageUrlDecorator(ilHost) +class DefaultImageUrlDecorator(ilHost: IlHost) : ImageUrlDecorator { + private val imageServiceUri: Uri + + init { + imageServiceUri = ilHost.hostUri.buildUpon().appendEncodedPath(IMAGES_SEGMENT).build() + } override fun decorate(sourceUrl: String, widthPixels: Int): String { - // FIXME https://github.com/SRGSSR/srgdataprovider-apple/issues/47 once RTS image service is well connected to Il Play image service. - return if (sourceUrl.contains("rts.ch") && sourceUrl.contains(".image")) { - ScaleWidthImageUrlDecorator.decorate(sourceUrl, widthPixels) - } else { - ilHostImageUrlDecorator.decorate(sourceUrl, widthPixels) - } + // Il image service only support some image url hostnames! + if (!isImageUrlHostCompatible(sourceUrl)) return sourceUrl + // Il image service only support a limited image size! + val imageWidth = ImageWidth.getFromPixels(widthPixels) + return imageServiceUri.buildUpon() + .appendQueryParameter(PARAM_IMAGE_URL, sourceUrl) + .appendQueryParameter(PARAM_FORMAT, FORMAT_WEBP) + .appendQueryParameter(PARAM_WIDTH, imageWidth.widthPixels.toString()) + .build() + .toString() + } + + /** + * Check that the host of the [imageUrl] is compatible with the il image service. + * + * @param imageUrl The image url to decorate. + */ + fun isImageUrlHostCompatible(imageUrl: String): Boolean { + return Uri.parse(imageUrl)?.host?.contains(SUPPORTED_HOST_NAME_REGEX) ?: false + } + + companion object { + private const val FORMAT_WEBP = "webp" // webp, jpg, png + private const val IMAGES_SEGMENT = "images/" + private const val PARAM_IMAGE_URL = "imageUrl" + private const val PARAM_FORMAT = "format" + private const val PARAM_WIDTH = "width" + + private val SUPPORTED_HOST_NAME_REGEX = "((rts|srf|rsi|rtr|swissinfo|srgssr)\\.ch)|swi-services-ch".toRegex(RegexOption.IGNORE_CASE) } } diff --git a/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/request/image/IlHostImageUrlDecorator.kt b/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/request/image/IlHostImageUrlDecorator.kt deleted file mode 100644 index 046212c..0000000 --- a/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/request/image/IlHostImageUrlDecorator.kt +++ /dev/null @@ -1,54 +0,0 @@ -package ch.srg.dataProvider.integrationlayer.request.image - -import android.net.Uri -import ch.srg.dataProvider.integrationlayer.data.ImageUrlDecorator -import ch.srg.dataProvider.integrationlayer.request.IlHost - -/** - * Il host image url decorator - * - * If the image url isn't supported by [IlHostImageUrlDecorator] the same url is returned. - * - * confluence doc : https://srgssr-ch.atlassian.net/wiki/spaces/SRGPLAY/pages/799082429/Project+-+Image+Service) - * - * @param ilHost The [IlHost] of the integration layer image service. - */ -class IlHostImageUrlDecorator(ilHost: IlHost) : ImageUrlDecorator { - private val imageServiceUri: Uri - - init { - imageServiceUri = ilHost.hostUri.buildUpon().appendEncodedPath(IMAGES_SEGMENT).build() - } - - override fun decorate(sourceUrl: String, widthPixels: Int): String { - // Il image service only support some image url hostnames! - if (!isImageUrlHostCompatible(sourceUrl)) return sourceUrl - // Il image service only support a limited image size! - val imageWidth = ImageWidth.getFromPixels(widthPixels) - return imageServiceUri.buildUpon() - .appendQueryParameter(PARAM_IMAGE_URL, sourceUrl) - .appendQueryParameter(PARAM_FORMAT, FORMAT_WEBP) - .appendQueryParameter(PARAM_WIDTH, imageWidth.widthPixels.toString()) - .build() - .toString() - } - - /** - * Check that the host of the [imageUrl] is compatible with the il image service. - * - * @param imageUrl The image url to decorate. - */ - fun isImageUrlHostCompatible(imageUrl: String): Boolean { - return Uri.parse(imageUrl)?.host?.contains(SUPPORTED_HOST_NAME_REGEX) ?: false - } - - companion object { - private const val FORMAT_WEBP = "webp" // webp, jpg, png - private const val IMAGES_SEGMENT = "images/" - private const val PARAM_IMAGE_URL = "imageUrl" - private const val PARAM_FORMAT = "format" - private const val PARAM_WIDTH = "width" - - private val SUPPORTED_HOST_NAME_REGEX = "((rts|srf|rsi|rtr|swissinfo|srgssr)\\.ch)|swi-services-ch".toRegex(RegexOption.IGNORE_CASE) - } -} diff --git a/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/request/image/ScaleWidthImageUrlDecorator.kt b/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/request/image/ScaleWidthImageUrlDecorator.kt deleted file mode 100644 index 074c5cb..0000000 --- a/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/request/image/ScaleWidthImageUrlDecorator.kt +++ /dev/null @@ -1,23 +0,0 @@ -package ch.srg.dataProvider.integrationlayer.request.image - -import android.net.Uri -import ch.srg.dataProvider.integrationlayer.data.ImageUrlDecorator - -/** - * Scale width image url decorator - * - * @constructor Create empty Scale width image url decorator - */ -object ScaleWidthImageUrlDecorator : ImageUrlDecorator { - private const val Scale = "scale" - private const val Width = "width" - - override fun decorate(sourceUrl: String, widthPixels: Int): String { - return Uri.parse(sourceUrl).buildUpon() - .appendPath(Scale) - .appendPath(Width) - .appendPath(widthPixels.toString()) - .build() - .toString() - } -} diff --git a/dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/TestDefaultImageUrlDecorator.kt b/dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/TestDefaultImageUrlDecorator.kt index 2720e5b..a3d5912 100644 --- a/dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/TestDefaultImageUrlDecorator.kt +++ b/dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/TestDefaultImageUrlDecorator.kt @@ -5,45 +5,99 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import ch.srg.dataProvider.integrationlayer.data.ImageUrl import ch.srg.dataProvider.integrationlayer.request.IlHost import ch.srg.dataProvider.integrationlayer.request.image.DefaultImageUrlDecorator -import org.junit.Assert +import ch.srg.dataProvider.integrationlayer.request.image.ImageSize +import ch.srg.dataProvider.integrationlayer.request.image.ImageWidth +import ch.srg.dataProvider.integrationlayer.request.image.decorated +import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class TestDefaultImageUrlDecorator { + private val decorator = DefaultImageUrlDecorator(ilHost = IlHost.PROD) @Test - fun testNonRtsUrl() { + fun testPixelValid() { val input = ImageUrl("https://ws.srf.ch/asset/image/audio/123") val encodedInput = Uri.encode("https://ws.srf.ch/asset/image/audio/123") val expected = "https://il.srgssr.ch/images/?imageUrl=${encodedInput}&format=webp&width=480" - Assert.assertEquals(expected, input.decorated(decorator, 480)) + assertEquals(expected, input.decorated(decorator, 480)) } @Test - fun testRtsUrlWithoutImage() { - val input = ImageUrl("https://ws.rts.ch/asset/image/audio/123") - val encodedInput = Uri.encode("https://ws.rts.ch/asset/image/audio/123") + fun testPixelWidthInvalid() { + val input = ImageUrl("https://ws.srf.ch/asset/image/audio/123") + val encodedInput = Uri.encode("https://ws.srf.ch/asset/image/audio/123") val expected = "https://il.srgssr.ch/images/?imageUrl=${encodedInput}&format=webp&width=480" - Assert.assertEquals(expected, input.decorated(decorator, 480)) + assertEquals(expected, input.decorated(decorator, 460)) } @Test - fun testUrlWithImageOnly() { - val input = ImageUrl("https://ws.srf.ch/asset/image/audio/123.image") - val encodedInput = Uri.encode("https://ws.srf.ch/asset/image/audio/123.image") + fun testImageSize() { + val input = ImageUrl("https://ws.srf.ch/asset/image/audio/123") + val encodedInput = Uri.encode("https://ws.srf.ch/asset/image/audio/123") val expected = "https://il.srgssr.ch/images/?imageUrl=${encodedInput}&format=webp&width=480" - Assert.assertEquals(expected, input.decorated(decorator, 480)) + assertEquals(expected, input.decorated(decorator, ImageSize.MEDIUM)) } @Test - fun testRtsUrlWithImage() { - val input = ImageUrl("https://ws.rts.ch/asset/image/audio/123.image") - val expected = "https://ws.rts.ch/asset/image/audio/123.image/scale/width/460" - Assert.assertEquals(expected, input.decorated(decorator, 460)) + fun testImageWidth() { + val input = ImageUrl("https://ws.srf.ch/asset/image/audio/123") + val encodedInput = Uri.encode("https://ws.srf.ch/asset/image/audio/123") + val expected = "https://il.srgssr.ch/images/?imageUrl=${encodedInput}&format=webp&width=1920" + assertEquals(expected, input.decorated(decorator, ImageWidth.W1920)) } + @Test + fun testOtherIlHost() { + val input = ImageUrl("https://ws.srf.ch/asset/image/audio/123") + val encodedInput = Uri.encode("https://ws.srf.ch/asset/image/audio/123") + val expected = "https://il-stage.srgssr.ch/images/?imageUrl=${encodedInput}&format=webp&width=1920" + assertEquals(expected, input.decorated(decorator = DefaultImageUrlDecorator(IlHost.STAGE), width = ImageWidth.W1920)) + } + @Test + fun testExtensionImageWidthWithIlHost() { + val input = ImageUrl("https://ws.srf.ch/asset/image/audio/123") + val encodedInput = Uri.encode("https://ws.srf.ch/asset/image/audio/123") + val expected = "https://il-stage.srgssr.ch/images/?imageUrl=${encodedInput}&format=webp&width=1920" + assertEquals(expected, input.decorated(ilHost = IlHost.STAGE, width = ImageWidth.W1920)) + } + + @Test + fun testExtensionImageSizeWithIlHost() { + val input = ImageUrl("https://ws.srf.ch/asset/image/audio/123") + val encodedInput = Uri.encode("https://ws.srf.ch/asset/image/audio/123") + val expected = "https://il-test.srgssr.ch/images/?imageUrl=${encodedInput}&format=webp&width=480" + assertEquals(expected, input.decorated(ilHost = IlHost.TEST, imageSize = ImageSize.MEDIUM)) + } + + @Test + fun testAllValidatedHostName() { + val tests = listOf( + "https://ws.srf.ch/asset/image/audio/123", + "https://www.srf.ch/asset/image/audio/123", + "https://ws.debug.srf.ch/asset/image/audio/123", + "https://ws.rts.ch/asset/image/audio/123", + "https://ws.rtr.ch/asset/image/audio/123", + "https://ws.rsi.ch/asset/image/audio/123", + "https://ws.swissinfo.ch/asset/image/audio/123", + "https://ws.srgssr.ch/asset/image/audio/123", + "https://swi-services-ch/asset/image/audio/123", + ) + for (url in tests) { + val input = ImageUrl(url) + val encodedInput = Uri.encode(url) + val expected = "https://il-test.srgssr.ch/images/?imageUrl=${encodedInput}&format=webp&width=480" + assertEquals(expected, input.decorated(ilHost = IlHost.TEST, imageSize = ImageSize.MEDIUM)) + } + } + @Test + fun testInvalidHostNameUrlReturnInputUrl() { + val input = ImageUrl("https://akamai.playsuisse.ch/asset/image/audio/123") + val expected = input.rawUrl + assertEquals(expected, input.decorated(ilHost = IlHost.TEST, imageSize = ImageSize.MEDIUM)) + } } diff --git a/dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/TestIlHostImageUrlDecorator.kt b/dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/TestIlHostImageUrlDecorator.kt deleted file mode 100644 index 38a8a83..0000000 --- a/dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/TestIlHostImageUrlDecorator.kt +++ /dev/null @@ -1,103 +0,0 @@ -package ch.srg.dataProvider.integrationlayer - -import android.net.Uri -import androidx.test.ext.junit.runners.AndroidJUnit4 -import ch.srg.dataProvider.integrationlayer.data.ImageUrl -import ch.srg.dataProvider.integrationlayer.request.IlHost -import ch.srg.dataProvider.integrationlayer.request.image.IlHostImageUrlDecorator -import ch.srg.dataProvider.integrationlayer.request.image.ImageSize -import ch.srg.dataProvider.integrationlayer.request.image.ImageWidth -import ch.srg.dataProvider.integrationlayer.request.image.decorated -import org.junit.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -class TestIlHostImageUrlDecorator { - - private val decorator = IlHostImageUrlDecorator(ilHost = IlHost.PROD) - - @Test - fun testPixelValid() { - val input = ImageUrl("https://ws.srf.ch/asset/image/audio/123") - val encodedInput = Uri.encode("https://ws.srf.ch/asset/image/audio/123") - val expected = "https://il.srgssr.ch/images/?imageUrl=${encodedInput}&format=webp&width=480" - assertEquals(expected, input.decorated(decorator, 480)) - } - - @Test - fun testPixelWidthInvalid() { - val input = ImageUrl("https://ws.srf.ch/asset/image/audio/123") - val encodedInput = Uri.encode("https://ws.srf.ch/asset/image/audio/123") - val expected = "https://il.srgssr.ch/images/?imageUrl=${encodedInput}&format=webp&width=480" - assertEquals(expected, input.decorated(decorator, 460)) - } - - @Test - fun testImageSize() { - val input = ImageUrl("https://ws.srf.ch/asset/image/audio/123") - val encodedInput = Uri.encode("https://ws.srf.ch/asset/image/audio/123") - val expected = "https://il.srgssr.ch/images/?imageUrl=${encodedInput}&format=webp&width=480" - assertEquals(expected, input.decorated(decorator, ImageSize.MEDIUM)) - } - - @Test - fun testImageWidth() { - val input = ImageUrl("https://ws.srf.ch/asset/image/audio/123") - val encodedInput = Uri.encode("https://ws.srf.ch/asset/image/audio/123") - val expected = "https://il.srgssr.ch/images/?imageUrl=${encodedInput}&format=webp&width=1920" - assertEquals(expected, input.decorated(decorator, ImageWidth.W1920)) - } - - @Test - fun testOtherIlHost() { - val input = ImageUrl("https://ws.srf.ch/asset/image/audio/123") - val encodedInput = Uri.encode("https://ws.srf.ch/asset/image/audio/123") - val expected = "https://il-stage.srgssr.ch/images/?imageUrl=${encodedInput}&format=webp&width=1920" - assertEquals(expected, input.decorated(decorator = IlHostImageUrlDecorator(IlHost.STAGE), width = ImageWidth.W1920)) - } - - @Test - fun testExtensionImageWidthWithIlHost() { - val input = ImageUrl("https://ws.srf.ch/asset/image/audio/123") - val encodedInput = Uri.encode("https://ws.srf.ch/asset/image/audio/123") - val expected = "https://il-stage.srgssr.ch/images/?imageUrl=${encodedInput}&format=webp&width=1920" - assertEquals(expected, input.decorated(ilHost = IlHost.STAGE, width = ImageWidth.W1920)) - } - - @Test - fun testExtensionImageSizeWithIlHost() { - val input = ImageUrl("https://ws.srf.ch/asset/image/audio/123") - val encodedInput = Uri.encode("https://ws.srf.ch/asset/image/audio/123") - val expected = "https://il-test.srgssr.ch/images/?imageUrl=${encodedInput}&format=webp&width=480" - assertEquals(expected, input.decorated(ilHost = IlHost.TEST, imageSize = ImageSize.MEDIUM)) - } - - @Test - fun testAllValidatedHostName() { - val tests = listOf( - "https://ws.srf.ch/asset/image/audio/123", - "https://www.srf.ch/asset/image/audio/123", - "https://ws.debug.srf.ch/asset/image/audio/123", - "https://ws.rts.ch/asset/image/audio/123", - "https://ws.rtr.ch/asset/image/audio/123", - "https://ws.rsi.ch/asset/image/audio/123", - "https://ws.swissinfo.ch/asset/image/audio/123", - "https://ws.srgssr.ch/asset/image/audio/123", - "https://swi-services-ch/asset/image/audio/123", - ) - for (url in tests) { - val input = ImageUrl(url) - val encodedInput = Uri.encode(url) - val expected = "https://il-test.srgssr.ch/images/?imageUrl=${encodedInput}&format=webp&width=480" - assertEquals(expected, input.decorated(ilHost = IlHost.TEST, imageSize = ImageSize.MEDIUM)) - } - } - - @Test - fun testInvalidHostNameUrlReturnInputUrl() { - val input = ImageUrl("https://akamai.playsuisse.ch/asset/image/audio/123") - val expected = input.rawUrl - assertEquals(expected, input.decorated(ilHost = IlHost.TEST, imageSize = ImageSize.MEDIUM)) - } -} diff --git a/dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/TestScaleWidthImageUrlDecorator.kt b/dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/TestScaleWidthImageUrlDecorator.kt deleted file mode 100644 index 26e0083..0000000 --- a/dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/TestScaleWidthImageUrlDecorator.kt +++ /dev/null @@ -1,39 +0,0 @@ -package ch.srg.dataProvider.integrationlayer - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import ch.srg.dataProvider.integrationlayer.data.ImageUrl -import ch.srg.dataProvider.integrationlayer.request.image.ImageSize -import ch.srg.dataProvider.integrationlayer.request.image.ImageWidth -import ch.srg.dataProvider.integrationlayer.request.image.ScaleWidthImageUrlDecorator -import ch.srg.dataProvider.integrationlayer.request.image.decorated -import org.junit.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -class TestScaleWidthImageUrlDecorator { - - private val decorator = ScaleWidthImageUrlDecorator - - @Test - fun testScaleWidth() { - val input = ImageUrl("https://www.data.com/images/images.png") - val width = 458 - val expected = "https://www.data.com/images/images.png/scale/width/458" - assertEquals(expected, input.decorated(decorator, width)) - } - - @Test - fun testScaleWidthImageSize() { - val input = ImageUrl("https://www.data.com/images/images.png") - val expected = "https://www.data.com/images/images.png/scale/width/480" - assertEquals(expected, input.decorated(decorator, ImageSize.MEDIUM)) - } - - @Test - fun testScaleWidthImageWidth() { - val input = ImageUrl("https://www.data.com/images/images.png") - val expected = "https://www.data.com/images/images.png/scale/width/1920" - assertEquals(expected, input.decorated(decorator, ImageWidth.W1920)) - } -}