Compare commits
	
		
			No commits in common. "9843a078ea38912660552b090a34a11dd920c634" and "0b8a771aa59984503de48e3ea8818caec00a2f1b" have entirely different histories.
		
	
	
		
			9843a078ea
			...
			0b8a771aa5
		
	
		
					 2 changed files with 5 additions and 181 deletions
				
			
		| 
						 | 
					@ -52,14 +52,13 @@ CREATE TABLE IF NOT EXISTS raw_materials_deliveries (
 | 
				
			||||||
--------------------------------------------
 | 
					--------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- Pallets are used to store cookies for delivery
 | 
					-- Pallets are used to store cookies for delivery
 | 
				
			||||||
-- Order related columns are unused for now.
 | 
					 | 
				
			||||||
CREATE TABLE IF NOT EXISTS pallets (
 | 
					CREATE TABLE IF NOT EXISTS pallets (
 | 
				
			||||||
    pallet_id INT PRIMARY KEY,
 | 
					    pallet_id INT PRIMARY KEY,
 | 
				
			||||||
    cookie_id INT NOT NULL,
 | 
					    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')),
 | 
					    status VARCHAR(50) NOT NULL CHECK (status IN ('freezer', 'delivered', 'blocked')),
 | 
				
			||||||
    production_date DATE NOT NULL,
 | 
					    production_date DATE NOT NULL,
 | 
				
			||||||
    delivery_date DATE DEFAULT NULL,
 | 
					    delivery_date DATE DEFAULT NULL,
 | 
				
			||||||
    FOREIGN KEY (cookie_id) REFERENCES cookies(cookie_id)
 | 
					    FOREIGN KEY (cookie_id) REFERENCES cookies(cookie_id)
 | 
				
			||||||
    -- FOREIGN KEY (order_id) REFERENCES orders(order_id)
 | 
					    FOREIGN KEY (order_id) REFERENCES orders(order_id)
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,15 +6,11 @@ import spark.Response;
 | 
				
			||||||
// Likely dependencies for db operations
 | 
					// Likely dependencies for db operations
 | 
				
			||||||
import java.sql.Connection;
 | 
					import java.sql.Connection;
 | 
				
			||||||
import java.sql.DriverManager;
 | 
					import java.sql.DriverManager;
 | 
				
			||||||
import java.sql.PreparedStatement;
 | 
					 | 
				
			||||||
import java.sql.SQLException;
 | 
					import java.sql.SQLException;
 | 
				
			||||||
import java.sql.Statement;
 | 
					import java.sql.Statement;
 | 
				
			||||||
import java.text.SimpleDateFormat;
 | 
					 | 
				
			||||||
// Likely dependencies for general operations
 | 
					// Likely dependencies for general operations
 | 
				
			||||||
import java.io.IOException;
 | 
					import java.io.IOException;
 | 
				
			||||||
import java.sql.ResultSet;
 | 
					import java.sql.ResultSet;
 | 
				
			||||||
import java.util.Date;
 | 
					 | 
				
			||||||
import java.util.Optional;
 | 
					 | 
				
			||||||
import java.util.StringJoiner;
 | 
					import java.util.StringJoiner;
 | 
				
			||||||
import java.util.stream.Collectors;
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
import java.nio.file.Files;
 | 
					import java.nio.file.Files;
 | 
				
			||||||
| 
						 | 
					@ -22,16 +18,12 @@ import java.nio.file.Paths;
 | 
				
			||||||
import java.util.stream.Stream;
 | 
					import java.util.stream.Stream;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class Database {
 | 
					public class Database {
 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Here, we use an in-memory database. This string could be changed to
 | 
						// Here, we use an in-memory database. This string could be changed to
 | 
				
			||||||
	// "jdbc:sqlite:<filename>.sqlite3" to use a file-based database instead.
 | 
						// "jdbc:sqlite:<filename>.sqlite3" to use a file-based database instead.
 | 
				
			||||||
	// Nore that ":memory:" is an **SQLite specific** magic string that tells the
 | 
						// Nore that ":memory:" is an **SQLite specific** magic string that tells the
 | 
				
			||||||
	// underlying SQLite engine to store the database in memory.
 | 
						// underlying SQLite engine to store the database in memory.
 | 
				
			||||||
	private static final String jdbcString = "jdbc:sqlite::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: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;
 | 
						private Connection conn = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public String getCustomers(Request req, Response res) {
 | 
						public String getCustomers(Request req, Response res) {
 | 
				
			||||||
| 
						 | 
					@ -60,115 +52,9 @@ public class Database {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public String getPallets(Request req, Response res) {
 | 
						public String getPallets(Request req, Response res) {
 | 
				
			||||||
		// These queries look like:
 | 
							// 1. Get query param -> validate cookie type if not null
 | 
				
			||||||
		// http://localhost:8888/api/v1/pallets?cookie=Nut+cookie&from=2024-05-23&to=2024-05-30&blocked=yes
 | 
							// 2. Figure out if we should return all pallets or just one type
 | 
				
			||||||
		// They may contain any combination of the parameters, or none at all.
 | 
							// 3. Return pallets as {cookie, blocked}
 | 
				
			||||||
 | 
					 | 
				
			||||||
		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\":[]}";
 | 
							return "{\"pallets\":[]}";
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -180,67 +66,6 @@ public class Database {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public String createPallet(Request req, Response res) {
 | 
						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
 | 
							// 1. Get query param
 | 
				
			||||||
		// 2. Check if cookie exists (is in DefaultRecipes)
 | 
							// 2. Check if cookie exists (is in DefaultRecipes)
 | 
				
			||||||
		// 3. Start transaction
 | 
							// 3. Start transaction
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue