From 8c8584b6bffb35e75a17f126c0c93a664578f096 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sun, 5 May 2024 09:37:33 +0200 Subject: [PATCH 1/5] DB script fix. INT is not the same as INTEGER, apparently --- app/Migrations/create-schema.sql | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/Migrations/create-schema.sql b/app/Migrations/create-schema.sql index 4ff9482..a5c85a1 100644 --- a/app/Migrations/create-schema.sql +++ b/app/Migrations/create-schema.sql @@ -4,20 +4,20 @@ -- Holds the different types of cookies we can make. CREATE TABLE IF NOT EXISTS cookies ( - cookie_id INT PRIMARY KEY, + cookie_id INTEGER PRIMARY KEY, cookie_name VARCHAR(50) NOT NULL UNIQUE ); -- Our known customers, may need more fields CREATE TABLE IF NOT EXISTS customers ( - customer_id INT PRIMARY KEY, + customer_id INTEGER PRIMARY KEY, customer_name VARCHAR(50) NOT NULL, customer_address VARCHAR(50) NOT NULL ); -- Orders from customers. CREATE TABLE IF NOT EXISTS orders ( - order_id INT PRIMARY KEY, + order_id INTEGER PRIMARY KEY, customer_id INT NOT NULL, cookie_id INT NOT NULL, order_date DATE NOT NULL DEFAULT CURRENT_DATE CHECK (order_date >= CURRENT_DATE), @@ -31,7 +31,7 @@ CREATE TABLE IF NOT EXISTS orders ( -- Describes ingredients and stock. CREATE TABLE IF NOT EXISTS raw_materials ( - ingredient_id INT PRIMARY KEY, + ingredient_id INTEGER PRIMARY KEY, ingredient_name VARCHAR(50) NOT NULL UNIQUE, ingredient_quantity INT NOT NULL, unit VARCHAR(50) NOT NULL CHECK (unit IN ('g', 'ml')) @@ -39,7 +39,7 @@ CREATE TABLE IF NOT EXISTS raw_materials ( -- When did we get the ingredients? CREATE TABLE IF NOT EXISTS raw_materials_deliveries ( - delivery_id INT PRIMARY KEY, + delivery_id INTEGER PRIMARY KEY, ingredient_id INT NOT NULL, delivery_date DATE NOT NULL, delivery_quantity INT NOT NULL, @@ -54,7 +54,7 @@ CREATE TABLE IF NOT EXISTS raw_materials_deliveries ( -- Pallets are used to store cookies for delivery -- Order related columns are unused for now. CREATE TABLE IF NOT EXISTS pallets ( - pallet_id INT PRIMARY KEY, + pallet_id INTEGER PRIMARY KEY, cookie_id INT NOT NULL, -- order_id INT NOT NULL, status VARCHAR(50) NOT NULL CHECK (status IN ('freezer', 'delivered', 'blocked')), From 7d21a572af427b41fe885bab04c2749772245c90 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sun, 5 May 2024 10:44:07 +0200 Subject: [PATCH 2/5] Helper tostring in recipe for easier debugging --- app/src/main/java/krusty/Recipe.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/krusty/Recipe.java b/app/src/main/java/krusty/Recipe.java index 864e39a..beb56de 100644 --- a/app/src/main/java/krusty/Recipe.java +++ b/app/src/main/java/krusty/Recipe.java @@ -10,6 +10,12 @@ public class Recipe { } public String toString() { - return name; + StringBuilder sb = new StringBuilder(name + ": "); + + for (Ingredient i : ingredients) { + sb.append(i.toString()); + sb.append(" "); + } + return sb.toString(); } } \ No newline at end of file From 19b6b05b69b27524d2f7c3df4cc63fcf282001f6 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sun, 5 May 2024 10:44:28 +0200 Subject: [PATCH 3/5] Dropping entire database every migration --- app/Migrations/create-schema.sql | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/Migrations/create-schema.sql b/app/Migrations/create-schema.sql index a5c85a1..b0aee90 100644 --- a/app/Migrations/create-schema.sql +++ b/app/Migrations/create-schema.sql @@ -1,3 +1,13 @@ +PRAGMA foreign_keys = OFF; + +-- Drop everything... +DROP TABLE IF EXISTS pallets; +DROP TABLE IF EXISTS raw_materials_deliveries; +DROP TABLE IF EXISTS raw_materials; +DROP TABLE IF EXISTS orders; +DROP TABLE IF EXISTS customers; +DROP TABLE IF EXISTS cookies; + -------------------------------------------- -- Recipe/Cookie related tables -------------------------------------------- @@ -63,3 +73,5 @@ CREATE TABLE IF NOT EXISTS pallets ( FOREIGN KEY (cookie_id) REFERENCES cookies(cookie_id) -- FOREIGN KEY (order_id) REFERENCES orders(order_id) ); + +PRAGMA foreign_keys = ON; \ No newline at end of file From 4aeb738df375f1a7c1d82179c0a64eb3516d516a Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sun, 5 May 2024 10:44:37 +0200 Subject: [PATCH 4/5] Passing all but one test --- app/src/main/java/krusty/Database.java | 87 ++++++++++++++++++-------- 1 file changed, 61 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/krusty/Database.java b/app/src/main/java/krusty/Database.java index 7a8fed9..84df098 100644 --- a/app/src/main/java/krusty/Database.java +++ b/app/src/main/java/krusty/Database.java @@ -27,8 +27,8 @@ public class Database { // "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"; + // 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. @@ -79,9 +79,6 @@ public class Database { 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() @@ -130,28 +127,28 @@ public class Database { try { Statement stmt = conn.createStatement(); StringBuilder query = new StringBuilder( - "SELECT * FROM pallets JOIN cookies ON pallets.cookie_id = cookies.cookie_id"); + "SELECT cookie_name, status 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 "); - } + // If both from and to are present + if (from.isPresent() && to.isPresent()) { + String query_from = new SimpleDateFormat("yyyy-MM-dd").format(from.get()); + String query_to = new SimpleDateFormat("yyyy-MM-dd").format(to.get()); - query.append("production_date BETWEEN '" + from_str + "' AND '" + to_str + "'"); + // Super hacky, low quality code + String clause = query.toString().contains("WHERE") ? " AND " : " WHERE "; + + query.append(clause + "production_date BETWEEN '" + query_from + "' AND '" + query_to + "'"); } - if (blocked.isPresent()) { - // WARNING THIS IS NOT CORRECT WARNING - if (r.isPresent() || from != null) { - query.append(" AND "); - } + if(blocked.isPresent()) { + // This again + String clause = query.toString().contains("WHERE") ? " AND " : " WHERE "; + query.append(clause); // TODO: WARNING This logic is flawed. WARNING // Remember, status can be 'freezer', 'delivered' or 'blocked' @@ -159,8 +156,21 @@ public class Database { } query.append(";"); + + System.out.println(query.toString()); + ResultSet result = stmt.executeQuery(query.toString()); + + // Rename the columns String jsonResult = Jsonizer.toJson(result, "pallets"); + + // Some carmack level code, as usual + jsonResult = jsonResult.replaceAll("cookie_name", "cookie"); + jsonResult = jsonResult.replaceAll("freezer", "no"); + jsonResult = jsonResult.replaceAll("delivered", "no"); + jsonResult = jsonResult.replaceAll("blocked", "yes"); + jsonResult = jsonResult.replaceAll("status", "blocked"); + return jsonResult; } catch (SQLException e) { System.out.printf("Error executing query: \n%s", e); @@ -173,9 +183,14 @@ public class Database { } public String reset(Request req, Response res) { - // 1. Wipe database - // 2. Re-run migrations - // 3. Return success + try { + this.migrateScript("Migrations/create-schema.sql"); + this.migrateScript("Migrations/initial-data.sql"); + } catch (Exception e) { + System.out.printf("Error resetting database: \n%s", e); + res.status(500); + return "{}"; + } return "{}"; } @@ -198,13 +213,20 @@ public class Database { return "{}"; } - try (PreparedStatement getRawMaterials = conn.prepareStatement("SELECT * FROM raw_materials WHERE ingredient_name = ?"); - PreparedStatement decrementRawMaterials = conn.prepareStatement("UPDATE raw_materials SET ingredient_quantity = ingredient_quantity - ? WHERE ingredient_name = ?"); - PreparedStatement insertPallet = conn.prepareStatement("INSERT INTO pallets (cookie_id, production_date, status) VALUES (?, ?, ?)")) { + // System.out.println(r.get()); + + try (PreparedStatement getRawMaterials = conn + .prepareStatement("SELECT * FROM raw_materials WHERE ingredient_name = ?"); + PreparedStatement decrementRawMaterials = conn.prepareStatement( + "UPDATE raw_materials SET ingredient_quantity = ingredient_quantity - ? WHERE ingredient_name = ?"); + PreparedStatement insertPallet = conn.prepareStatement( + "INSERT INTO pallets (cookie_id, production_date, status) VALUES (?, ?, ?)"); + PreparedStatement getCookieId = conn + .prepareStatement("SELECT cookie_id FROM cookies WHERE cookie_name = ?")) { // Start transaction conn.setAutoCommit(false); - for(Ingredient i : r.get().ingredients) { + for (Ingredient i : r.get().ingredients) { getRawMaterials.setString(1, i.name); ResultSet result = getRawMaterials.executeQuery(); if (!result.next()) { @@ -229,7 +251,20 @@ public class Database { decrementRawMaterials.executeUpdate(); } - insertPallet.setInt(1, 1); + // Fish out the cookie id + getCookieId.setString(1, cookie); + ResultSet cookie_rs = getCookieId.executeQuery(); + if (!cookie_rs.next()) { + // Rollback transaction + conn.rollback(); + // Return 500 + res.status(500); + return "{}"; + } + + int cookie_id = cookie_rs.getInt("cookie_id"); + + insertPallet.setInt(1, cookie_id); insertPallet.setString(2, new SimpleDateFormat("yyyy-MM-dd").format(new Date())); insertPallet.setString(3, "freezer"); From 6554cb2b6f9e440734c50a0d0721d64d4ac803df Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sun, 5 May 2024 10:52:13 +0200 Subject: [PATCH 5/5] All tests passing --- app/src/main/java/krusty/Database.java | 52 +++++++++++++++++--------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/krusty/Database.java b/app/src/main/java/krusty/Database.java index 84df098..8ccc97f 100644 --- a/app/src/main/java/krusty/Database.java +++ b/app/src/main/java/krusty/Database.java @@ -27,8 +27,8 @@ public class Database { // "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"; + 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. @@ -94,26 +94,26 @@ public class Database { }; } - // Both of these must be present - if (from_str != null && to_str != null) { + if(from_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)); + } catch (Exception e) { + from = Optional.empty(); + } + } + + if(to_str != null) { + try { 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(); - } + // If the interval is negative, reset the dates + if(from.isPresent() && to.isPresent() && from.get().after(to.get())) { + from = Optional.empty(); + to = Optional.empty(); } // This type of code is unreadable, error prone and hard to maintain. @@ -135,16 +135,34 @@ public class Database { } // If both from and to are present - if (from.isPresent() && to.isPresent()) { + if (from.isPresent()) { String query_from = new SimpleDateFormat("yyyy-MM-dd").format(from.get()); + + // Super hacky, low quality code + String clause = query.toString().contains("WHERE") ? " AND " : " WHERE "; + + query.append(clause + "production_date >= '" + query_from + "'"); + } + + if(to.isPresent()) { String query_to = new SimpleDateFormat("yyyy-MM-dd").format(to.get()); // Super hacky, low quality code String clause = query.toString().contains("WHERE") ? " AND " : " WHERE "; - query.append(clause + "production_date BETWEEN '" + query_from + "' AND '" + query_to + "'"); + query.append(clause + "production_date <= '" + query_to + "'"); } + // if (from.isPresent() && to.isPresent()) { + // String query_from = new SimpleDateFormat("yyyy-MM-dd").format(from.get()); + // String query_to = new SimpleDateFormat("yyyy-MM-dd").format(to.get()); + + // // Super hacky, low quality code + // String clause = query.toString().contains("WHERE") ? " AND " : " WHERE "; + + // query.append(clause + "production_date BETWEEN '" + query_from + "' AND '" + query_to + "'"); + // } + if(blocked.isPresent()) { // This again String clause = query.toString().contains("WHERE") ? " AND " : " WHERE ";