diff --git a/app/src/main/java/krusty/Database.java b/app/src/main/java/krusty/Database.java index 96ea689..609843c 100644 --- a/app/src/main/java/krusty/Database.java +++ b/app/src/main/java/krusty/Database.java @@ -8,9 +8,12 @@ import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; +import java.text.SimpleDateFormat; // Likely dependencies for general operations import java.io.IOException; import java.sql.ResultSet; +import java.util.Date; +import java.util.Optional; import java.util.StringJoiner; import java.util.stream.Collectors; import java.nio.file.Files; @@ -18,12 +21,16 @@ import java.nio.file.Paths; import java.util.stream.Stream; public class Database { + // Here, we use an in-memory database. This string could be changed to // "jdbc:sqlite:.sqlite3" to use a file-based database instead. // Nore that ":memory:" is an **SQLite specific** magic string that tells the // underlying SQLite engine to store the database in memory. private static final String jdbcString = "jdbc:sqlite::memory:"; // private static final String jdbcString = "jdbc:sqlite:krusty.db"; + + // Hold a single connection to the database. Note that this is + // not a pool, so this is not thread-safe nor efficient. private Connection conn = null; public String getCustomers(Request req, Response res) { @@ -52,9 +59,114 @@ public class Database { } public String getPallets(Request req, Response res) { - // 1. Get query param -> validate cookie type if not null - // 2. Figure out if we should return all pallets or just one type - // 3. Return pallets as {cookie, blocked} + // These queries look like: + // http://localhost:8888/api/v1/pallets?cookie=Nut+cookie&from=2024-05-23&to=2024-05-30&blocked=yes + // They may contain any combination of the parameters, or none at all. + + Optional r = Optional.empty(); // Holds the ecipe if we have a cookie + Optional blocked = Optional.empty(); // Holds the blocked status + Optional from = Optional.empty(); // Holds the from date + Optional to = Optional.empty(); // Holds the to date + + // First we need the cookie parameter + String cookie = req.queryParams("cookie"); + + // And the blocked parameter + String blocked_str = req.queryParams("blocked"); + + // Then we need the date parameters + String from_str = req.queryParams("from"); + String to_str = req.queryParams("to"); + + // Date from = null; + // Date to = null; + + // Fancy functional one-liner to get the recipe if the cookie is present + if (cookie != null) { + r = Optional.ofNullable(DefaultRecipes.recipes.stream() + .filter(recipe -> recipe.name.equals(cookie)) + .findFirst().orElse(null)); + } + + if (blocked_str != null) { + blocked = switch (blocked_str) { + case "yes" -> Optional.of(true); + case "no" -> Optional.of(false); + default -> Optional.empty(); + }; + } + + // Both of these must be present + if (from_str != null && to_str != null) { + try { + // Parse both in the format (2024-05-23), also called ISO 8601 + from = Optional.of(new SimpleDateFormat("yyyy-MM-dd").parse(from_str)); + to = Optional.of(new SimpleDateFormat("yyyy-MM-dd").parse(to_str)); + } catch (Exception e) { + // Reset the dates to empty + from = Optional.empty(); + to = Optional.empty(); + // We have a bad date, maybe log this somewhere + } + + // Check so that the dates are in the correct order + if (from.isPresent() && to.isPresent() && from.get().after(to.get())) { + // We have a bad interval, perhaps set dates to empty agian? + // TODO: This obviously need louder error handling + from = Optional.empty(); + to = Optional.empty(); + } + } + + // This type of code is unreadable, error prone and hard to maintain. + // The fact that im responsible for this code makes my soul hurt. + // This part almost made me write a simple query factory to handle this. + // + // SqlBuilder exists to 'take the pain out of generating SQL queries', + // but it's not in the standard library. + // + // Helmets, seatbelts and safety goggles on; we need to execute a query. + try { + Statement stmt = conn.createStatement(); + StringBuilder query = new StringBuilder("SELECT * FROM pallets JOIN cookies ON pallets.cookie_id = cookies.cookie_id"); + + // r is validated here + if (r.isPresent()) { + query.append(" WHERE cookie_name = '" + r.get().name + "'"); + } + + if (from != null && to != null) { + if (r.isPresent()) { + query.append(" AND "); + } else { + query.append(" WHERE "); + } + + query.append("production_date BETWEEN '" + from_str + "' AND '" + to_str + "'"); + } + + if (blocked.isPresent()) { + // WARNING THIS IS NOT CORRECT WARNING + if (r.isPresent() || from != null) { + query.append(" AND "); + } + + // TODO: WARNING This logic is flawed. WARNING + // Remember, status can be 'freezer', 'delivered' or 'blocked' + query.append("status = " + (blocked.get() ? "'blocked'" : "'freezer'")); + } + + query.append(";"); + ResultSet result = stmt.executeQuery(query.toString()); + String jsonResult = Jsonizer.toJson(result, "pallets"); + return jsonResult; + } catch (SQLException e) { + System.out.printf("Error executing query: \n%s", e); + } + + // Statue 500, to give the client a + // chance to figure out that something went wrong + res.status(500); return "{\"pallets\":[]}"; }