From 5c25e7c9f8cd98345bcda33bfa97d92f9a5a3480 Mon Sep 17 00:00:00 2001 From: Greg Gauthier Date: Wed, 24 May 2023 21:01:54 +0100 Subject: [PATCH] initial commit --- .gitignore | 41 +++++ pom.xml | 116 ++++++++++++++ src/main/java/com/gmgauthier/Application.java | 15 ++ .../controllers/CalculatorController.java | 94 +++++++++++ src/main/resources/application.properties | 8 + .../requests/CalculatorControllerTest.java | 148 ++++++++++++++++++ 6 files changed, 422 insertions(+) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/java/com/gmgauthier/Application.java create mode 100644 src/main/java/com/gmgauthier/controllers/CalculatorController.java create mode 100644 src/main/resources/application.properties create mode 100644 src/test/java/com/gmgauthier/requests/CalculatorControllerTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8252660 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +#https://github.com/spring-projects/spring-boot/blob/master/.gitignore +.idea/ +*# +*.iml +*.ipr +*.iws +*.jar +*.sw? +*~ +.#* +.*.md.html +.DS_Store +.classpath +.factorypath +.gradle +.idea +.metadata +.project +.recommenders +.settings +.springBeans +/build +/code +MANIFEST.MF +_site/ +activemq-data +bin +build +build.log +dependency-reduced-pom.xml +dump.rdb +interpolated*.xml +lib/ +manifest.yml +overridedb.* +target +transaction-logs +.flattened-pom.xml +secrets.yml +.gradletasknamecache +.sts4-cache diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..c4b3bd3 --- /dev/null +++ b/pom.xml @@ -0,0 +1,116 @@ + + + 4.0.0 + + org.example + calculator-project2 + 1.0-SNAPSHOT + jar + + + 1.8 + 1.8 + 1.8 + UTF-8 + + + + + + org.springframework.boot + spring-boot-dependencies + 2.1.1.RELEASE + pom + import + + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-devtools + true + + + + com.h2database + h2 + runtime + + + + org.springframework + spring-test + test + + + + org.springframework.boot + spring-boot-test + test + + + + org.springframework.boot + spring-boot-test-autoconfigure + test + + + + junit + junit + test + + + + org.json + json + 20220924 + + + + com.hackerrank.applications + junit-ordered-test-runner + 1.0.1 + test + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.1.1.RELEASE + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + repackage + + + + + + + diff --git a/src/main/java/com/gmgauthier/Application.java b/src/main/java/com/gmgauthier/Application.java new file mode 100644 index 0000000..1f16e50 --- /dev/null +++ b/src/main/java/com/gmgauthier/Application.java @@ -0,0 +1,15 @@ +package com.gmgauthier; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * + * Spring Boot application starter class + */ +@SpringBootApplication +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/src/main/java/com/gmgauthier/controllers/CalculatorController.java b/src/main/java/com/gmgauthier/controllers/CalculatorController.java new file mode 100644 index 0000000..3cd6e90 --- /dev/null +++ b/src/main/java/com/gmgauthier/controllers/CalculatorController.java @@ -0,0 +1,94 @@ +package com.gmgauthier.controllers; + +import org.json.JSONObject; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; + +import org.springframework.web.bind.annotation.*; +import org.springframework.web.client.HttpClientErrorException; + +import java.util.*; + +@RestController +public class CalculatorController { + + @RequestMapping(value = "/sum", + method = RequestMethod.POST, + consumes = MediaType.APPLICATION_JSON_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE) + public String sums(@RequestBody HashMap> payload) { + List values = validateValues(payload); + Integer sum = values.get(0) + values.get(1); + + HashMap results = new HashMap<>(); + results.put("sum", sum); + JSONObject jsonResults = new JSONObject(results); + + return jsonResults.toString(); + } + + @RequestMapping(value = "/difference", + method = RequestMethod.POST, + consumes = MediaType.APPLICATION_JSON_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE) + public String differences(@RequestBody HashMap> payload) { + List values = validateValues(payload); + Integer remainder = values.get(0) - values.get(1); + + HashMap results = new HashMap<>(); + results.put("difference", remainder); + JSONObject jsonResults = new JSONObject(results); + + return jsonResults.toString(); + } + + @RequestMapping(value = "/product", + method = RequestMethod.POST, + consumes = MediaType.APPLICATION_JSON_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE) + public String products(@RequestBody HashMap> payload) { + List values = validateValues(payload); + Integer product = values.get(0) * values.get(1); + + HashMap results = new HashMap<>(); + results.put("product", product); + JSONObject jsonResults = new JSONObject(results); + + return jsonResults.toString(); + } + + @RequestMapping(value = "/quotient", + method = RequestMethod.POST, + consumes = MediaType.APPLICATION_JSON_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE) + public String quotients(@RequestBody HashMap> payload) { + List values = validateValues(payload); + Float quotient = values.get(0).floatValue() / values.get(1).floatValue(); + + HashMap results = new HashMap<>(); + results.put("quotient", quotient); + JSONObject jsonResults = new JSONObject(results); + + return jsonResults.toString(); + } + + + private List validateValues(HashMap> payload) { + List values; + + // Make sure the payload is extractable + try { + values = payload.get("values"); + } catch (Exception e){ + throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, + "operand values could not be extracted from payload"); + } + + if (values.size() != 2) { + throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, + "incorrect number of operands"); + } + + return values; + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..059b55a --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,8 @@ +#Spring Boot server configuration +server.address=0.0.0.0 +server.port=8000 + +#H2 console web access configuration +#Open "http://0.0.0.0:8000/h2-console" and hit "Connect" button +spring.h2.console.enabled=true +spring.h2.console.path=/h2-console diff --git a/src/test/java/com/gmgauthier/requests/CalculatorControllerTest.java b/src/test/java/com/gmgauthier/requests/CalculatorControllerTest.java new file mode 100644 index 0000000..76d839e --- /dev/null +++ b/src/test/java/com/gmgauthier/requests/CalculatorControllerTest.java @@ -0,0 +1,148 @@ +package com.gmgauthier.requests; + +import com.hackerrank.test.utility.OrderedTestRunner; +import com.hackerrank.test.utility.TestWatchman; +import org.junit.*; +import org.junit.rules.TestWatcher; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.rules.SpringClassRule; +import org.springframework.test.context.junit4.rules.SpringMethodRule; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import org.springframework.web.util.NestedServletException; + +import static org.junit.Assert.assertEquals; + +@RunWith(OrderedTestRunner.class) +@SpringBootTest +@AutoConfigureMockMvc +public class CalculatorControllerTest { + @ClassRule + public static final SpringClassRule springClassRule = new SpringClassRule(); + + @Rule + public final SpringMethodRule springMethodRule = new SpringMethodRule(); + + @Rule + public TestWatcher watchman = TestWatchman.watchman; + + @Autowired + private MockMvc mockMvc; + + @BeforeClass + public static void setUpClass() { + TestWatchman.watchman.registerClass(CalculatorControllerTest.class); + } + + @AfterClass + public static void tearDownClass() { + TestWatchman.watchman.createReport(CalculatorControllerTest.class); + } + + @Test + public void testValidSums() throws Exception { + String response = mockMvc.perform( + MockMvcRequestBuilders.post("/sum") + .content("{\"values\":[2,2]}") + .contentType("application/json") + ) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andReturn() + .getResponse() + .getContentAsString(); + + Assert.assertEquals("{\"sum\":4}", response); + } + + @Test(expected = NestedServletException.class) + public void testTooManyOperands() throws Exception { + String response = mockMvc.perform( + MockMvcRequestBuilders.post("/sum") + .content("{\"values\":[2,2,3]}") + .contentType("application/json") + ) + .andExpect(MockMvcResultMatchers.status().is(400)) + .andReturn() + .getResponse() + .getContentAsString(); + + Assert.assertEquals("", response); + } + + @Test + public void testNoContentBody() throws Exception { + String response = mockMvc.perform( + MockMvcRequestBuilders.post("/sum") + ) + .andExpect(MockMvcResultMatchers.status().is(415)) //mocker does our status check + .andReturn() + .getResponse() + .getContentAsString(); + + assertEquals("", response); + } + + @Test + public void testStringOperands() throws Exception { + String response = mockMvc.perform( + MockMvcRequestBuilders.post("/sum") + .content("{\"values\":[\"apple\",\"orange\",\"banana\"]}") + .contentType("application/json") + ) + .andExpect(MockMvcResultMatchers.status().is(400)) + .andReturn() + .getResponse() + .getContentAsString(); + + Assert.assertEquals("", response); + } + + @Test + public void testValidDifferences() throws Exception { + String response = mockMvc.perform( + MockMvcRequestBuilders.post("/difference") + .content("{\"values\":[4,2]}") + .contentType("application/json") + ) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andReturn() + .getResponse() + .getContentAsString(); + + Assert.assertEquals("{\"difference\":2}", response); + } + + @Test + public void testValidProducts() throws Exception { + String response = mockMvc.perform( + MockMvcRequestBuilders.post("/product") + .content("{\"values\":[4,2]}") + .contentType("application/json") + ) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andReturn() + .getResponse() + .getContentAsString(); + + Assert.assertEquals("{\"product\":8}", response); + } + + @Test + public void testValidQuotients() throws Exception { + String response = mockMvc.perform( + MockMvcRequestBuilders.post("/quotient") + .content("{\"values\":[4,2]}") + .contentType("application/json") + ) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andReturn() + .getResponse() + .getContentAsString(); + + Assert.assertEquals("{\"quotient\":2}", response); + } +}