Compare commits

...

3 commits

Author SHA1 Message Date
Imbus
9843a078ea More tests passing 2024-05-05 07:25:19 +02:00
Imbus
71687c8bf7 Uncomment orders 2024-05-05 07:25:10 +02:00
Imbus
35607ce341 getPallets seemingly working 2024-05-05 06:47:56 +02:00
2 changed files with 181 additions and 5 deletions

View file

@ -52,13 +52,14 @@ 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,
cookie_id INT NOT NULL,
order_id INT NOT NULL,
-- order_id INT NOT NULL,
status VARCHAR(50) NOT NULL CHECK (status IN ('freezer', 'delivered', 'blocked')),
production_date DATE NOT NULL,
delivery_date DATE DEFAULT NULL,
FOREIGN KEY (cookie_id) REFERENCES cookies(cookie_id)
FOREIGN KEY (order_id) REFERENCES orders(order_id)
-- FOREIGN KEY (order_id) REFERENCES orders(order_id)
);

View file

@ -6,11 +6,15 @@ import spark.Response;
// Likely dependencies for db operations
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
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 +22,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:<filename>.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 +60,115 @@ 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<Recipe> r = Optional.empty(); // Holds the ecipe if we have a cookie
Optional<Boolean> blocked = Optional.empty(); // Holds the blocked status
Optional<Date> from = Optional.empty(); // Holds the from date
Optional<Date> 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\":[]}";
}
@ -66,6 +180,67 @@ public class Database {
}
public String createPallet(Request req, Response res) {
// This on only has one query param and looks like:
// http://localhost:8888/api/v1/pallets?cookie=Amneris
Optional<Recipe> r = Optional.empty();
String cookie = req.queryParams("cookie");
if (cookie != null) {
r = Optional.ofNullable(DefaultRecipes.recipes.stream()
.filter(recipe -> recipe.name.equals(cookie))
.findFirst().orElse(null));
}
if (r.isEmpty()) {
// Return 404
res.status(404);
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 (?, ?, ?)")) {
// Start transaction
conn.setAutoCommit(false);
for(Ingredient i : r.get().ingredients) {
getRawMaterials.setString(1, i.name);
ResultSet result = getRawMaterials.executeQuery();
if (!result.next()) {
// Rollback transaction
conn.rollback();
// Return 500
res.status(500);
return "{}";
}
// Check if we have enough raw materials
if (result.getInt("ingredient_quantity") < i.amount) {
// Rollback transaction
conn.rollback();
// Return 500
res.status(500);
return "{}";
}
decrementRawMaterials.setInt(1, i.amount * 54); // 5400 / 100
decrementRawMaterials.setString(2, i.name);
decrementRawMaterials.executeUpdate();
}
insertPallet.setInt(1, 1);
insertPallet.setString(2, new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
insertPallet.setString(3, "freezer");
insertPallet.executeUpdate();
conn.commit();
} catch (SQLException e) {
System.out.printf("Error starting transaction: \n%s", e);
}
// TODO: NOT DONE
// 1. Get query param
// 2. Check if cookie exists (is in DefaultRecipes)
// 3. Start transaction