diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/language/Utils.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/language/Utils.kt index 8f8310387..2317cbab6 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/language/Utils.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/language/Utils.kt @@ -1,6 +1,24 @@ package org.gotson.komga.infrastructure.language +import java.util.Enumeration import java.util.SortedMap fun List.toIndexedMap(): SortedMap = mapIndexed { i, e -> i to e }.toMap().toSortedMap() + +fun List.toEnumeration(): Enumeration { + return object : Enumeration { + var count = 0 + + override fun hasMoreElements(): Boolean { + return this.count < size + } + + override fun nextElement(): T { + if (this.count < size) { + return get(this.count++) + } + throw NoSuchElementException("List enumeration asked for more elements than present") + } + } +} diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/web/BracketParamsFilterConfiguration.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/web/BracketParamsFilterConfiguration.kt new file mode 100644 index 000000000..78e9dd1d8 --- /dev/null +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/web/BracketParamsFilterConfiguration.kt @@ -0,0 +1,33 @@ +package org.gotson.komga.infrastructure.web + +import org.springframework.boot.web.servlet.FilterRegistrationBean +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import javax.servlet.Filter +import javax.servlet.FilterChain +import javax.servlet.ServletRequest +import javax.servlet.ServletResponse +import javax.servlet.http.HttpServletRequest + +@Configuration +class BracketParamsFilterConfiguration { + @Bean + fun bracketParamsFilter(): FilterRegistrationBean = + FilterRegistrationBean(BracketParamsFilter()) + .also { + it.addUrlPatterns( + "/api/*" + ) + it.setName("queryParamsFilter") + } + + inner class BracketParamsFilter : Filter { + override fun doFilter(request: ServletRequest?, response: ServletResponse?, chain: FilterChain?) { + chain?.doFilter(BracketParamsRequestWrapper(request as HttpServletRequest), response) + } + } +} + + + + diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/web/BracketParamsRequestWrapper.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/web/BracketParamsRequestWrapper.kt new file mode 100644 index 000000000..2216f80c2 --- /dev/null +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/web/BracketParamsRequestWrapper.kt @@ -0,0 +1,34 @@ +package org.gotson.komga.infrastructure.web + +import org.gotson.komga.infrastructure.language.toEnumeration +import java.util.Enumeration +import javax.servlet.http.HttpServletRequest +import javax.servlet.http.HttpServletRequestWrapper + +class BracketParamsRequestWrapper(request: HttpServletRequest) : HttpServletRequestWrapper(request) { + override fun getParameter(name: String): String? { + val nameWithoutSuffix = name.removeSuffix("[]") + val values = listOfNotNull(super.getParameter(nameWithoutSuffix), super.getParameter("$nameWithoutSuffix[]")) + return if (values.isEmpty()) null + else values.joinToString(",") + } + + override fun getParameterValues(name: String): Array? { + val nameWithoutSuffix = name.removeSuffix("[]") + val regular = super.getParameterValues(nameWithoutSuffix) + val suffix = super.getParameterValues("$nameWithoutSuffix[]") + val values = listOfNotNull(regular, suffix) + return if (values.isEmpty()) null + else values.reduce { acc, strings -> acc + strings } + } + + override fun getParameterNames(): Enumeration = + super.getParameterNames().toList().map { it.removeSuffix("[]") }.distinct().toEnumeration() + + override fun getParameterMap(): MutableMap> { + return super.getParameterMap().asSequence() + .groupBy({ it.key.removeSuffix("[]") }, { it.value }) + .mapValues { it.value.reduce { acc, strings -> acc + strings } } + .toMutableMap() + } +} diff --git a/komga/src/test/kotlin/org/gotson/komga/infrastructure/web/BracketParamsRequestWrapperTest.kt b/komga/src/test/kotlin/org/gotson/komga/infrastructure/web/BracketParamsRequestWrapperTest.kt new file mode 100644 index 000000000..de9d55af0 --- /dev/null +++ b/komga/src/test/kotlin/org/gotson/komga/infrastructure/web/BracketParamsRequestWrapperTest.kt @@ -0,0 +1,258 @@ +package org.gotson.komga.infrastructure.web + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.springframework.mock.web.MockHttpServletRequest + +class BracketParamsRequestWrapperTest { + @Nested + inner class ParameterNames { + @Test + fun `given parameters with and without brackets when getting parameter names then only the name without bracket is returned`() { + // given + val request = MockHttpServletRequest() + request.setParameter("param") + request.setParameter("param[]") + + // when + val filtered = BracketParamsRequestWrapper(request) + + // then + assertThat(filtered.parameterNames.toList()).containsExactlyInAnyOrder("param") + } + + @Test + fun `given parameters with brackets when getting parameter names then only the name without bracket is returned`() { + // given + val request = MockHttpServletRequest() + request.setParameter("param[]") + + // when + val filtered = BracketParamsRequestWrapper(request) + + // then + assertThat(filtered.parameterNames.toList()).containsExactlyInAnyOrder("param") + } + + @Test + fun `given parameters without brackets when getting parameter names then only the name without bracket is returned`() { + // given + val request = MockHttpServletRequest() + request.setParameter("param") + + // when + val filtered = BracketParamsRequestWrapper(request) + + // then + assertThat(filtered.parameterNames.toList()).containsExactlyInAnyOrder("param") + } + + @Test + fun `given empty parameters when getting parameter names then it is empty`() { + // given + val request = MockHttpServletRequest() + + // when + val filtered = BracketParamsRequestWrapper(request) + + // then + assertThat(filtered.parameterNames.toList()).isEmpty() + } + } + + @Nested + inner class ParameterValue { + @Test + fun `given parameters with and without brackets when getting parameter value then both values are joined to string`() { + // given + val request = MockHttpServletRequest() + request.setParameter("param", "a") + request.setParameter("param[]", "b") + + // when + val filtered = BracketParamsRequestWrapper(request) + val value = filtered.getParameter("param") + val valueBracket = filtered.getParameter("param[]") + + // then + assertThat(value).isEqualTo(valueBracket) + assertThat(value).isEqualTo("a,b") + } + + @Test + fun `given parameters with brackets when getting parameter value single value is returned`() { + // given + val request = MockHttpServletRequest() + request.setParameter("param[]", "b") + + // when + val filtered = BracketParamsRequestWrapper(request) + val value = filtered.getParameter("param") + val valueBracket = filtered.getParameter("param[]") + + // then + assertThat(value).isEqualTo(valueBracket) + assertThat(value).isEqualTo("b") + } + + @Test + fun `given parameters without brackets when getting parameter value single value is returned`() { + // given + val request = MockHttpServletRequest() + request.setParameter("param", "b") + + // when + val filtered = BracketParamsRequestWrapper(request) + val value = filtered.getParameter("param") + val valueBracket = filtered.getParameter("param[]") + + // then + assertThat(value).isEqualTo(valueBracket) + assertThat(value).isEqualTo("b") + } + + @Test + fun `given empty parameters when getting parameter value then return null`() { + // given + val request = MockHttpServletRequest() + + // when + val filtered = BracketParamsRequestWrapper(request) + val value = filtered.getParameter("param") + + // then + assertThat(value).isNull() + } + } + + @Nested + inner class ParameterValues { + @Test + fun `given parameters with and without brackets when getting parameter values then both values are returned`() { + // given + val request = MockHttpServletRequest() + request.setParameter("param[]", "a") + request.setParameter("param", "b") + + // when + val filtered = BracketParamsRequestWrapper(request) + val values = filtered.getParameterValues("param") + val valuesBracket = filtered.getParameterValues("param[]") + + // then + assertThat(values).isEqualTo(valuesBracket) + assertThat(values).containsExactlyInAnyOrder("a", "b") + } + + @Test + fun `given parameters with brackets when getting parameter values then value is returned`() { + // given + val request = MockHttpServletRequest() + request.setParameter("param[]", "a") + + // when + val filtered = BracketParamsRequestWrapper(request) + val values = filtered.getParameterValues("param") + val valuesBracket = filtered.getParameterValues("param[]") + + // then + assertThat(values).isEqualTo(valuesBracket) + assertThat(values).containsExactlyInAnyOrder("a") + } + + @Test + fun `given parameters without brackets when getting parameter values then value is returned`() { + // given + val request = MockHttpServletRequest() + request.setParameter("param", "a") + + // when + val filtered = BracketParamsRequestWrapper(request) + val values = filtered.getParameterValues("param") + val valuesBracket = filtered.getParameterValues("param[]") + + // then + assertThat(values).isEqualTo(valuesBracket) + assertThat(values).containsExactlyInAnyOrder("a") + } + + @Test + fun `given empty parameters when getting parameter values then return null`() { + // given + val request = MockHttpServletRequest() + + // when + val filtered = BracketParamsRequestWrapper(request) + val values = filtered.getParameterValues("param") + + // then + assertThat(values).isNull() + } + } + + @Nested + inner class ParameterMap { + @Test + fun `given parameters with and without brackets when getting parameter map then both values are returned`() { + // given + val request = MockHttpServletRequest() + request.setParameter("param[]", "a") + request.setParameter("param", "b") + + // when + val filtered = BracketParamsRequestWrapper(request) + val map = filtered.parameterMap + + // then + assertThat(map.keys).hasSize(1) + assertThat(map.keys).containsExactly("param") + assertThat(map["param"]).containsExactlyInAnyOrder("a", "b") + } + + @Test + fun `given parameters with brackets when getting parameter map then key without bracket is returned`() { + // given + val request = MockHttpServletRequest() + request.setParameter("param[]", "a") + + // when + val filtered = BracketParamsRequestWrapper(request) + val map = filtered.parameterMap + + // then + assertThat(map.keys).hasSize(1) + assertThat(map.keys).containsExactly("param") + assertThat(map["param"]).containsExactlyInAnyOrder("a") + } + + @Test + fun `given parameters without brackets when getting parameter map then key without bracket is returned`() { + // given + val request = MockHttpServletRequest() + request.setParameter("param", "a") + + // when + val filtered = BracketParamsRequestWrapper(request) + val map = filtered.parameterMap + + // then + assertThat(map.keys).hasSize(1) + assertThat(map.keys).containsExactly("param") + assertThat(map["param"]).containsExactlyInAnyOrder("a") + } + + @Test + fun `given empty parameters when getting parameter map then empty map is returned`() { + // given + val request = MockHttpServletRequest() + + // when + val filtered = BracketParamsRequestWrapper(request) + val map = filtered.parameterMap + + // then + assertThat(map).isEmpty() + } + } +}