diff --git a/http-service/src/main/java/net/runelite/http/service/SpringWebMvcConfigurer.java b/http-service/src/main/java/net/runelite/http/service/SpringWebMvcConfigurer.java index f0fd4ed99c..704e4f9cb2 100644 --- a/http-service/src/main/java/net/runelite/http/service/SpringWebMvcConfigurer.java +++ b/http-service/src/main/java/net/runelite/http/service/SpringWebMvcConfigurer.java @@ -30,6 +30,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.GsonHttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; @@ -52,8 +53,11 @@ public class SpringWebMvcConfigurer extends WebMvcConfigurerAdapter * @param converters */ @Override - public void configureMessageConverters(List> converters) + public void extendMessageConverters(List> converters) { + // Could not figure out a better way to force GSON + converters.removeIf(MappingJackson2HttpMessageConverter.class::isInstance); + GsonHttpMessageConverter gsonHttpMessageConverter = new GsonHttpMessageConverter(); gsonHttpMessageConverter.setGson(RuneLiteAPI.GSON); converters.add(gsonHttpMessageConverter); diff --git a/http-service/src/main/java/net/runelite/http/service/config/ConfigController.java b/http-service/src/main/java/net/runelite/http/service/config/ConfigController.java new file mode 100644 index 0000000000..966d49cda2 --- /dev/null +++ b/http-service/src/main/java/net/runelite/http/service/config/ConfigController.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2019, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.http.service.config; + +import java.io.IOException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import net.runelite.http.api.config.Configuration; +import net.runelite.http.service.account.AuthFilter; +import net.runelite.http.service.account.beans.SessionEntry; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import static org.springframework.web.bind.annotation.RequestMethod.DELETE; +import static org.springframework.web.bind.annotation.RequestMethod.PUT; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/config") +public class ConfigController +{ + private final ConfigService configService; + private final AuthFilter authFilter; + + @Autowired + public ConfigController(ConfigService configService, AuthFilter authFilter) + { + this.configService = configService; + this.authFilter = authFilter; + } + + @RequestMapping + public Configuration get(HttpServletRequest request, HttpServletResponse response) throws IOException + { + SessionEntry session = authFilter.handle(request, response); + + if (session == null) + { + return null; + } + + return configService.get(session.getUser()); + } + + @RequestMapping(path = "/{key:.+}", method = PUT) + public void setKey( + HttpServletRequest request, + HttpServletResponse response, + @PathVariable String key, + @RequestBody(required = false) String value + ) throws IOException + { + SessionEntry session = authFilter.handle(request, response); + + if (session == null) + { + return; + } + + configService.setKey(session.getUser(), key, value); + } + + @RequestMapping(path = "/{key:.+}", method = DELETE) + public void unsetKey( + HttpServletRequest request, + HttpServletResponse response, + @PathVariable String key + ) throws IOException + { + SessionEntry session = authFilter.handle(request, response); + + if (session == null) + { + return; + } + + configService.unsetKey(session.getUser(), key); + } +} diff --git a/http-service/src/main/java/net/runelite/http/service/config/ConfigService.java b/http-service/src/main/java/net/runelite/http/service/config/ConfigService.java index 102e2f9c7a..ed96f36cf4 100644 --- a/http-service/src/main/java/net/runelite/http/service/config/ConfigService.java +++ b/http-service/src/main/java/net/runelite/http/service/config/ConfigService.java @@ -24,28 +24,18 @@ */ package net.runelite.http.service.config; -import java.io.IOException; import java.util.List; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import javax.annotation.Nullable; import net.runelite.http.api.config.ConfigEntry; import net.runelite.http.api.config.Configuration; -import net.runelite.http.service.account.AuthFilter; -import net.runelite.http.service.account.beans.SessionEntry; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import static org.springframework.web.bind.annotation.RequestMethod.DELETE; -import static org.springframework.web.bind.annotation.RequestMethod.PUT; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.stereotype.Service; import org.sql2o.Connection; import org.sql2o.Sql2o; import org.sql2o.Sql2oException; -@RestController -@RequestMapping("/config") +@Service public class ConfigService { private static final String CREATE_CONFIG = "CREATE TABLE IF NOT EXISTS `config` (\n" @@ -59,16 +49,13 @@ public class ConfigService + " ADD CONSTRAINT `user_fk` FOREIGN KEY (`user`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;"; private final Sql2o sql2o; - private final AuthFilter auth; @Autowired public ConfigService( - @Qualifier("Runelite SQL2O") Sql2o sql2o, - AuthFilter auth + @Qualifier("Runelite SQL2O") Sql2o sql2o ) { this.sql2o = sql2o; - this.auth = auth; try (Connection con = sql2o.open()) { @@ -87,71 +74,45 @@ public class ConfigService } } - @RequestMapping - public Configuration get(HttpServletRequest request, HttpServletResponse response) throws IOException + public Configuration get(int userId) { - SessionEntry session = auth.handle(request, response); - - if (session == null) - { - return null; - } - List config; try (Connection con = sql2o.open()) { config = con.createQuery("select `key`, value from config where user = :user") - .addParameter("user", session.getUser()) + .addParameter("user", userId) .executeAndFetch(ConfigEntry.class); } return new Configuration(config); } - @RequestMapping(path = "/{key:.+}", method = PUT) public void setKey( - HttpServletRequest request, - HttpServletResponse response, - @PathVariable String key, - @RequestBody(required = false) String value - ) throws IOException + int userId, + String key, + @Nullable String value + ) { - SessionEntry session = auth.handle(request, response); - - if (session == null) - { - return; - } - try (Connection con = sql2o.open()) { con.createQuery("insert into config (user, `key`, value) values (:user, :key, :value) on duplicate key update `key` = :key, value = :value") - .addParameter("user", session.getUser()) + .addParameter("user", userId) .addParameter("key", key) .addParameter("value", value != null ? value : "") .executeUpdate(); } } - @RequestMapping(path = "/{key:.+}", method = DELETE) public void unsetKey( - HttpServletRequest request, - HttpServletResponse response, - @PathVariable String key - ) throws IOException + int userId, + String key + ) { - SessionEntry session = auth.handle(request, response); - - if (session == null) - { - return; - } - try (Connection con = sql2o.open()) { con.createQuery("delete from config where user = :user and `key` = :key") - .addParameter("user", session.getUser()) + .addParameter("user", userId) .addParameter("key", key) .executeUpdate(); } diff --git a/http-service/src/test/java/net/runelite/http/service/config/ConfigControllerTest.java b/http-service/src/test/java/net/runelite/http/service/config/ConfigControllerTest.java new file mode 100644 index 0000000000..5ba857ef0a --- /dev/null +++ b/http-service/src/test/java/net/runelite/http/service/config/ConfigControllerTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2019, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.http.service.config; + +import java.io.IOException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import net.runelite.http.service.account.AuthFilter; +import net.runelite.http.service.account.beans.SessionEntry; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@RunWith(SpringRunner.class) +@WebMvcTest(ConfigController.class) +@Slf4j +public class ConfigControllerTest +{ + @Autowired + private MockMvc mockMvc; + + @MockBean + private ConfigService configService; + + @MockBean + private AuthFilter authFilter; + + @Before + public void before() throws IOException + { + when(authFilter.handle(any(HttpServletRequest.class), any(HttpServletResponse.class))) + .thenReturn(mock(SessionEntry.class)); + } + + @Test + public void testSetKey() throws Exception + { + mockMvc.perform(put("/config/key") + .content("value") + .contentType(MediaType.TEXT_PLAIN)) + .andExpect(status().isOk()); + + verify(configService).setKey(anyInt(), eq("key"), eq("value")); + } +} \ No newline at end of file