From f8efed44c17a2fddd8b54069415fabe3360c0816 Mon Sep 17 00:00:00 2001 From: Mei Chang van der Werff Date: Thu, 27 Nov 2025 15:57:43 +0100 Subject: [PATCH] implemented addRecipe method, and renamed my class to ServerUtils --- client/src/main/java/client/DetailView.java | 118 ----------- client/src/main/java/client/Main.java | 4 +- .../main/java/client/scenes/AddQuoteCtrl.java | 6 +- .../java/client/scenes/QuoteOverviewCtrl.java | 6 +- .../main/java/client/utils/ServerUtils.java | 187 +++++++++++++----- .../java/client/utils/ServerUtilsExample.java | 72 +++++++ ...tailViewTest.java => ServerUtilsTest.java} | 19 +- 7 files changed, 228 insertions(+), 184 deletions(-) delete mode 100644 client/src/main/java/client/DetailView.java create mode 100644 client/src/main/java/client/utils/ServerUtilsExample.java rename client/src/test/java/client/{DetailViewTest.java => ServerUtilsTest.java} (75%) diff --git a/client/src/main/java/client/DetailView.java b/client/src/main/java/client/DetailView.java deleted file mode 100644 index ca71bce..0000000 --- a/client/src/main/java/client/DetailView.java +++ /dev/null @@ -1,118 +0,0 @@ -package client; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import commons.Recipe; - -import java.io.IOException; -import java.io.StringWriter; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.util.List; - -public class DetailView { - private static final String BASE_URL = "http://localhost:8080/api"; - private final HttpClient client; - private final ObjectMapper objectMapper = new ObjectMapper(); - private final int statusOK = 200; - - public DetailView() { - client = HttpClient.newHttpClient(); - } - - /** - * Gets all the recipes from the backend - * @return a JSON string with all the recipes - */ - public List findAll() throws IOException, InterruptedException { - HttpRequest request = HttpRequest.newBuilder() - .GET() - .uri(URI.create(BASE_URL+ "/recipes")) - .build(); - HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); - - return objectMapper.readValue(response.body(), new TypeReference>() { - });// JSON string-> List (Jackson) - } - - /** - * Gets a single recipe based on its id - * @param id every recipe has it's unique id - * @return a singe recipe with the given id - */ - public Recipe findId(long id) throws IOException, InterruptedException { - HttpRequest request = HttpRequest.newBuilder() - .GET() - .uri(URI.create(BASE_URL+"/recipes/" + id)) - .build(); - HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); - - if(response.statusCode() != statusOK){ - System.out.println("No recipe found with this id"); - return null; - } - - return objectMapper.readValue(response.body(),Recipe.class); - } - - /** - * Deletes a recipe - * @param id the recipe that get deleted - */ - public void deleteRecipe(long id) throws IOException, InterruptedException { - HttpRequest request = HttpRequest.newBuilder() - .DELETE() - .uri(URI.create(BASE_URL + "/recipes/" + id)) - .build(); - HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); - - if(response.statusCode() != statusOK){ - System.out.println("No recipe to delete"); - } - - } - - /** - * Clones a recipe - * @param id the id of the recipe to be cloned - * @return a duplicated recipe of the given recipe - */ - public Recipe cloneRecipe(long id) throws IOException, InterruptedException { - //Get the recipe - HttpRequest request = HttpRequest.newBuilder() - .GET() - .uri(URI.create(BASE_URL + "/recipes/" + id)) - .build(); - HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); - - Recipe ogRecipe = objectMapper.readValue(response.body(),Recipe.class); - - //200 is the status code for success, other codes can mean there is no recipe to clone - if(response.statusCode() != statusOK){ - return null; - } - - // recipe exists so you can make a "new" recipe aka the clone - Recipe clone = new Recipe(); - String cloneName = ogRecipe.getName() + "(clone)"; // I suppose we want unique names? - clone.setName(cloneName); - clone.setIngredients(ogRecipe.getIngredients()); - clone.setPreparationSteps(ogRecipe.getPreparationSteps()); - - //From String to Json - StringWriter s = new StringWriter(); - objectMapper.writeValue(s, clone); - - // Post the clone to backend - HttpRequest request2 = HttpRequest.newBuilder() - .POST(HttpRequest.BodyPublishers.ofString(s.toString())) - .uri(URI.create(BASE_URL + "/recipes/")) - .build(); - - HttpResponse cloneResponse = client.send(request2, HttpResponse.BodyHandlers.ofString()); - - return objectMapper.readValue(cloneResponse.body(),Recipe.class); - } -} \ No newline at end of file diff --git a/client/src/main/java/client/Main.java b/client/src/main/java/client/Main.java index f7c5a00..8be1175 100644 --- a/client/src/main/java/client/Main.java +++ b/client/src/main/java/client/Main.java @@ -25,7 +25,7 @@ import com.google.inject.Injector; import client.scenes.AddQuoteCtrl; import client.scenes.MainCtrl; import client.scenes.QuoteOverviewCtrl; -import client.utils.ServerUtils; +import client.utils.ServerUtilsExample; import javafx.application.Application; import javafx.stage.Stage; @@ -41,7 +41,7 @@ public class Main extends Application { @Override public void start(Stage primaryStage) throws Exception { - var serverUtils = INJECTOR.getInstance(ServerUtils.class); + var serverUtils = INJECTOR.getInstance(ServerUtilsExample.class); if (!serverUtils.isServerAvailable()) { var msg = "Server needs to be started before the client, but it does not seem to be available. Shutting down."; System.err.println(msg); diff --git a/client/src/main/java/client/scenes/AddQuoteCtrl.java b/client/src/main/java/client/scenes/AddQuoteCtrl.java index 0dcf39f..1ddf889 100644 --- a/client/src/main/java/client/scenes/AddQuoteCtrl.java +++ b/client/src/main/java/client/scenes/AddQuoteCtrl.java @@ -17,7 +17,7 @@ package client.scenes; import com.google.inject.Inject; -import client.utils.ServerUtils; +import client.utils.ServerUtilsExample; import commons.Person; import commons.Quote; import jakarta.ws.rs.WebApplicationException; @@ -29,7 +29,7 @@ import javafx.stage.Modality; public class AddQuoteCtrl { - private final ServerUtils server; + private final ServerUtilsExample server; private final MainCtrl mainCtrl; @FXML @@ -42,7 +42,7 @@ public class AddQuoteCtrl { private TextField quote; @Inject - public AddQuoteCtrl(ServerUtils server, MainCtrl mainCtrl) { + public AddQuoteCtrl(ServerUtilsExample server, MainCtrl mainCtrl) { this.mainCtrl = mainCtrl; this.server = server; diff --git a/client/src/main/java/client/scenes/QuoteOverviewCtrl.java b/client/src/main/java/client/scenes/QuoteOverviewCtrl.java index d97ebaf..11b64c6 100644 --- a/client/src/main/java/client/scenes/QuoteOverviewCtrl.java +++ b/client/src/main/java/client/scenes/QuoteOverviewCtrl.java @@ -20,7 +20,7 @@ import java.util.ResourceBundle; import com.google.inject.Inject; -import client.utils.ServerUtils; +import client.utils.ServerUtilsExample; import commons.Quote; import javafx.beans.property.SimpleStringProperty; import javafx.collections.FXCollections; @@ -32,7 +32,7 @@ import javafx.scene.control.TableView; public class QuoteOverviewCtrl implements Initializable { - private final ServerUtils server; + private final ServerUtilsExample server; private final MainCtrl mainCtrl; private ObservableList data; @@ -47,7 +47,7 @@ public class QuoteOverviewCtrl implements Initializable { private TableColumn colQuote; @Inject - public QuoteOverviewCtrl(ServerUtils server, MainCtrl mainCtrl) { + public QuoteOverviewCtrl(ServerUtilsExample server, MainCtrl mainCtrl) { this.server = server; this.mainCtrl = mainCtrl; } diff --git a/client/src/main/java/client/utils/ServerUtils.java b/client/src/main/java/client/utils/ServerUtils.java index 483eed1..cf14fd7 100644 --- a/client/src/main/java/client/utils/ServerUtils.java +++ b/client/src/main/java/client/utils/ServerUtils.java @@ -1,64 +1,153 @@ -/* - * Copyright 2021 Delft University of Technology - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package client.utils; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import commons.Recipe; +import jakarta.ws.rs.ProcessingException; +import jakarta.ws.rs.client.ClientBuilder; +import org.glassfish.jersey.client.ClientConfig; + +import java.io.IOException; +import java.io.StringWriter; +import java.net.ConnectException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.List; + import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.ConnectException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.List; - -import org.glassfish.jersey.client.ClientConfig; - -import commons.Quote; -import jakarta.ws.rs.ProcessingException; -import jakarta.ws.rs.client.ClientBuilder; -import jakarta.ws.rs.client.Entity; -import jakarta.ws.rs.core.GenericType; - public class ServerUtils { + private static final String SERVER = "http://localhost:8080/api"; + private final HttpClient client; + private final ObjectMapper objectMapper = new ObjectMapper(); + private final int statusOK = 200; - private static final String SERVER = "http://localhost:8080/"; + public ServerUtils() { + client = HttpClient.newHttpClient(); + } - public void getQuotesTheHardWay() throws IOException, URISyntaxException { - var url = new URI("http://localhost:8080/api/quotes").toURL(); - var is = url.openConnection().getInputStream(); - var br = new BufferedReader(new InputStreamReader(is)); - String line; - while ((line = br.readLine()) != null) { - System.out.println(line); + /** + * Gets all the recipes from the backend + * @return a JSON string with all the recipes + */ + public List getRecipes() throws IOException, InterruptedException { + HttpRequest request = HttpRequest.newBuilder() + .GET() + .uri(URI.create(SERVER + "/recipe")) + .build(); + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + return objectMapper.readValue(response.body(), new TypeReference>() { + });// JSON string-> List (Jackson) + } + + /** + * Gets a single recipe based on its id + * @param id every recipe has it's unique id + * @return a singe recipe with the given id + */ + public Recipe findId(long id) throws IOException, InterruptedException { + HttpRequest request = HttpRequest.newBuilder() + .GET() + .uri(URI.create(SERVER +"/recipes/" + id)) + .build(); + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + if(response.statusCode() != statusOK){ + System.out.println("No recipe found with this id"); + return null; } + + return objectMapper.readValue(response.body(),Recipe.class); } - public List getQuotes() { - return ClientBuilder.newClient(new ClientConfig()) // - .target(SERVER).path("api/quotes") // - .request(APPLICATION_JSON) // - .get(new GenericType>() {}); + // todo : make an add button + public Recipe addRecipe(Recipe newRecipe) throws IOException, InterruptedException { + StringWriter s = new StringWriter(); + objectMapper.writeValue(s, newRecipe); + + HttpRequest request = HttpRequest.newBuilder() + .PUT(HttpRequest.BodyPublishers.ofString(s.toString())) + .uri(URI.create(SERVER + "/recipe/new")) + .build(); + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + if(response.statusCode() != 200){ + System.out.println("No recipe to add"); + return null; + } + + return objectMapper.readValue(response.body(),Recipe.class); + } +// public Quote addR(Quote quote) { +// return ClientBuilder.newClient(new ClientConfig()) // +// .target(SERVER).path("api/quotes") // +// .request(APPLICATION_JSON) // +// .post(Entity.entity(quote, APPLICATION_JSON), Quote.class); +// } + + /** + * Deletes a recipe + * @param id the recipe that get deleted + */ + public void deleteRecipe(long id) throws IOException, InterruptedException { + HttpRequest request = HttpRequest.newBuilder() + .DELETE() + .uri(URI.create(SERVER + "/recipe/" + id)) + .build(); + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + if(response.statusCode() != statusOK){ + System.out.println("No recipe to delete"); + } + } - public Quote addQuote(Quote quote) { - return ClientBuilder.newClient(new ClientConfig()) // - .target(SERVER).path("api/quotes") // - .request(APPLICATION_JSON) // - .post(Entity.entity(quote, APPLICATION_JSON), Quote.class); + /** + * Clones a recipe + * @param id the id of the recipe to be cloned + * @return a duplicated recipe of the given recipe + */ + public Recipe cloneRecipe(long id) throws IOException, InterruptedException { + //Get the recipe + HttpRequest request = HttpRequest.newBuilder() + .GET() + .uri(URI.create(SERVER + "/recipe/" + id)) + .build(); + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + //200 is the status code for success, other codes can mean there is no recipe to clone + if(response.statusCode() != statusOK){ + return null; + } + // recipe exists so you can make a "new" recipe aka the clone + Recipe recipe = objectMapper.readValue(response.body(), Recipe.class); + + // TODO : Make the names unique, this isn't unique. Cause when cloning again we get the same name + int version = 1; + while(version != 1){ // fix the condition!!!!!!! + version++; + } + + String newName = recipe.getName() + "("+ version + ")"; // I suppose we want unique names? + recipe.setName(newName); + + //From String to Json + StringWriter s = new StringWriter(); + objectMapper.writeValue(s, recipe); + + // Post the clone to backend + HttpRequest request2 = HttpRequest.newBuilder() + .PUT(HttpRequest.BodyPublishers.ofString(s.toString())) + .uri(URI.create(SERVER + "/recipe/new")) + .build(); + + HttpResponse cloneResponse = client.send(request2, HttpResponse.BodyHandlers.ofString()); + + return objectMapper.readValue(cloneResponse.body(),Recipe.class); } public boolean isServerAvailable() { diff --git a/client/src/main/java/client/utils/ServerUtilsExample.java b/client/src/main/java/client/utils/ServerUtilsExample.java new file mode 100644 index 0000000..bc4a187 --- /dev/null +++ b/client/src/main/java/client/utils/ServerUtilsExample.java @@ -0,0 +1,72 @@ +/* + * Copyright 2021 Delft University of Technology + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package client.utils; + +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; + +import java.net.ConnectException; +import java.util.List; + +import jakarta.ws.rs.core.GenericType; +import org.glassfish.jersey.client.ClientConfig; + +import commons.Quote; +import jakarta.ws.rs.ProcessingException; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.client.Entity; + +public class ServerUtilsExample { + + private static final String SERVER = "http://localhost:8080/"; + +// public void getQuotesTheHardWay() throws IOException, URISyntaxException { +// var url = new URI("http://localhost:8080/api/quotes").toURL(); +// var is = url.openConnection().getInputStream(); +// var br = new BufferedReader(new InputStreamReader(is)); +// String line; +// while ((line = br.readLine()) != null) { +// System.out.println(line); +// } +// } + + public List getQuotes() { + return ClientBuilder.newClient(new ClientConfig()) // + .target(SERVER).path("api/quotes") // + .request(APPLICATION_JSON) // + .get(new GenericType>() {}); + } + + public Quote addQuote(Quote quote) { + return ClientBuilder.newClient(new ClientConfig()) // + .target(SERVER).path("api/quotes") // + .request(APPLICATION_JSON) // + .post(Entity.entity(quote, APPLICATION_JSON), Quote.class); + } + + public boolean isServerAvailable() { + try { + ClientBuilder.newClient(new ClientConfig()) // + .target(SERVER) // + .request(APPLICATION_JSON) // + .get(); + } catch (ProcessingException e) { + if (e.getCause() instanceof ConnectException) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/client/src/test/java/client/DetailViewTest.java b/client/src/test/java/client/ServerUtilsTest.java similarity index 75% rename from client/src/test/java/client/DetailViewTest.java rename to client/src/test/java/client/ServerUtilsTest.java index d367b74..13960d4 100644 --- a/client/src/test/java/client/DetailViewTest.java +++ b/client/src/test/java/client/ServerUtilsTest.java @@ -1,5 +1,6 @@ package client; +import client.utils.ServerUtils; import commons.Recipe; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -9,26 +10,26 @@ import java.util.List; import static org.junit.jupiter.api.Assertions.*; -class DetailViewTest { - static DetailView dv; +class ServerUtilsTest { + static ServerUtils dv; static Recipe testRecipe; @BeforeAll - static void setup(){ - dv = new DetailView(); + static void setup() throws IOException, InterruptedException { + dv = new ServerUtils(); Recipe r = new Recipe(); r.setName("Tosti"); r.setIngredients(List.of("Bread", "Cheese", "Ham")); r.setPreparationSteps(List.of("Step 1:", "Step 2")); -// testRecipe = dv.create(r); + testRecipe = dv.addRecipe(r); } @Test - void findAll() throws IOException, InterruptedException { - List recipes = dv.findAll(); + void findAllTest() throws IOException, InterruptedException { + List recipes = dv.getRecipes(); assertNotNull(recipes, "The list should not be null"); assertTrue(recipes.size() >= 0, "The list should be 0 (when no recipes), or more"); @@ -45,14 +46,14 @@ class DetailViewTest { } @Test - void deleteTest() throws IOException, InterruptedException { + void deleteRecipeTest() throws IOException, InterruptedException { dv.deleteRecipe(testRecipe.getId()); assertNull(dv.findId(testRecipe.getId())); } @Test - void cloneTest() throws IOException, InterruptedException { + void cloneRecipeTest() throws IOException, InterruptedException { Recipe clone = dv.cloneRecipe(testRecipe.getId()); assertNotEquals(clone.getId(), testRecipe.getId());