From e32f457a3c725d8d4fdecf329d532229b515b81e Mon Sep 17 00:00:00 2001 From: BEAUVAIS ANTOINE <antoine.beauvais@etu.unistra.fr> Date: Thu, 23 Sep 2021 23:01:40 +0200 Subject: [PATCH] Added simple stock manipulation through transaction. --- .../unistra/sil/erp/back/DatabaseSystem.java | 2 +- .../back/controller/api/ApiErrorHandler.java | 15 +++ .../api/ApiResourceNotFoundException.java | 28 ++++++ .../api/ApiSubmitTransactionController.java | 47 +++++++-- .../back/db/DatabaseConnectionException.java | 9 ++ .../sil/erp/back/db/DatabaseInterface.java | 18 ++++ .../db/DatabaseResourceNotFoundException.java | 26 +++++ .../sil/erp/back/db/DatabaseSQLiteImpl.java | 98 +++++++++++++++++++ .../erp/back/db/DatabaseUpdateException.java | 27 +++++ 9 files changed, 263 insertions(+), 7 deletions(-) create mode 100644 src/main/java/fr/unistra/sil/erp/back/controller/api/ApiResourceNotFoundException.java create mode 100644 src/main/java/fr/unistra/sil/erp/back/db/DatabaseResourceNotFoundException.java create mode 100644 src/main/java/fr/unistra/sil/erp/back/db/DatabaseUpdateException.java diff --git a/src/main/java/fr/unistra/sil/erp/back/DatabaseSystem.java b/src/main/java/fr/unistra/sil/erp/back/DatabaseSystem.java index 4cdb1f3..e32e6a8 100644 --- a/src/main/java/fr/unistra/sil/erp/back/DatabaseSystem.java +++ b/src/main/java/fr/unistra/sil/erp/back/DatabaseSystem.java @@ -33,7 +33,7 @@ public class DatabaseSystem { DatabaseSystem.instance = new DatabaseSQLiteImpl(); if(DatabaseSystem.instance == null) - throw new DatabaseConnectionException(); + throw new DatabaseConnectionException("Failed to create DB."); return DatabaseSystem.instance; } diff --git a/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiErrorHandler.java b/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiErrorHandler.java index fbd17bf..e3114b4 100644 --- a/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiErrorHandler.java +++ b/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiErrorHandler.java @@ -53,4 +53,19 @@ public class ApiErrorHandler { HttpStatus.BAD_REQUEST); } + /** + * Handler for HTTP 404 Not Found. + * @param ex the thrown Exception. + * @param request the HTTP Servlet request. + * @param response the HTTP Servlet response. + * @return the response to the client. + */ + @ExceptionHandler(ApiResourceNotFoundException.class) + public ResponseEntity<Object> handleResourceNotFound(Exception ex, + HttpServletRequest request, HttpServletResponse response) + { + return new ResponseEntity<>(new ErrorMessage(ex.getMessage()), + HttpStatus.NOT_FOUND); + } + } diff --git a/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiResourceNotFoundException.java b/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiResourceNotFoundException.java new file mode 100644 index 0000000..0676cef --- /dev/null +++ b/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiResourceNotFoundException.java @@ -0,0 +1,28 @@ +/* + * CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL-B + * https://cecill.info/licences/Licence_CeCILL-B_V1-fr.html + */ +package fr.unistra.sil.erp.back.controller.api; + +/** + * API Exception for HTTP 404 Not Found. + * + * If a resource requested by the user does not exist, this exception + * is thrown and handled through ApiErrorHandler.class. + * + * @see ApiErrorHandler + * + * @author BEAUVAIS ANTOINE + */ +public class ApiResourceNotFoundException extends Exception { + + /** + * Class constructor. + * @param errMsg the error message. + */ + public ApiResourceNotFoundException(String errMsg) + { + super(errMsg); + } + +} diff --git a/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiSubmitTransactionController.java b/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiSubmitTransactionController.java index 33f6b5c..b39c303 100644 --- a/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiSubmitTransactionController.java +++ b/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiSubmitTransactionController.java @@ -1,13 +1,19 @@ /* - * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license - * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + * CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL-B + * https://cecill.info/licences/Licence_CeCILL-B_V1-fr.html */ package fr.unistra.sil.erp.back.controller.api; import com.google.gson.Gson; import com.google.gson.JsonParseException; import static fr.unistra.sil.erp.back.Config.MAPPING_SUBTRANSAC; +import fr.unistra.sil.erp.back.DatabaseSystem; import fr.unistra.sil.erp.back.controller.ISubmitTransactionController; +import fr.unistra.sil.erp.back.db.DatabaseConnectionException; +import fr.unistra.sil.erp.back.db.DatabaseInterface; +import fr.unistra.sil.erp.back.db.DatabaseResourceNotFoundException; +import fr.unistra.sil.erp.back.db.DatabaseUpdateException; +import fr.unistra.sil.erp.back.model.Stock; import fr.unistra.sil.erp.back.model.Transaction; import java.io.IOException; import java.util.logging.Level; @@ -35,11 +41,15 @@ public class ApiSubmitTransactionController * @param response the HTTP Servlet Response provided by Spring. * @return the response for the user. * @throws ApiBadRequestException if the query failed. + * @throws ApiServerErrorException Server error. + * @throws ApiResourceNotFoundException Stock not found. */ @RequestMapping(value=MAPPING_SUBTRANSAC, method = RequestMethod.POST) @Override public ResponseEntity<Object> submitTransaction(HttpServletRequest request, - HttpServletResponse response) throws ApiBadRequestException + HttpServletResponse response) + throws ApiBadRequestException, ApiServerErrorException, + ApiResourceNotFoundException { Gson gson = new Gson(); String body; @@ -70,9 +80,34 @@ public class ApiSubmitTransactionController if(!t.checkIfValid()) throw new ApiBadRequestException("Invalid JSON schema."); - System.out.println("Transaction : " + t.getItem() + - t.getType() + " " + t.getAmount()); - + DatabaseInterface db; + Stock s; + try { + db = DatabaseSystem.getInstance(); + s = db.getStockForItem(t.getItem()); + if(s == null) + throw new ApiServerErrorException("Database failure."); + + int newQuantity = s.getQuantity() + t.getQuantity(); + if(newQuantity < 0) + newQuantity = 0; + + db.updateStock(s.getId(), newQuantity); + } catch (DatabaseConnectionException ex) { + Logger.getLogger(ApiSubmitTransactionController.class.getName()) + .log(Level.SEVERE, ex.getMessage(), ex); + throw new ApiServerErrorException("Database failure."); + } catch (DatabaseResourceNotFoundException ex) { + //Logger.getLogger(ApiSubmitTransactionController.class.getName()) + // .log(Level.SEVERE, null, ex); + throw new ApiResourceNotFoundException("No stock found for item " + + t.getItem()); + } catch (DatabaseUpdateException ex) { + Logger.getLogger(ApiSubmitTransactionController.class.getName()) + .log(Level.SEVERE, ex.getMessage(), ex); + throw new ApiServerErrorException("Database update failure."); + } + return new ResponseEntity<>(t, HttpStatus.CREATED); } diff --git a/src/main/java/fr/unistra/sil/erp/back/db/DatabaseConnectionException.java b/src/main/java/fr/unistra/sil/erp/back/db/DatabaseConnectionException.java index fcff9c1..0ace8fd 100644 --- a/src/main/java/fr/unistra/sil/erp/back/db/DatabaseConnectionException.java +++ b/src/main/java/fr/unistra/sil/erp/back/db/DatabaseConnectionException.java @@ -16,4 +16,13 @@ package fr.unistra.sil.erp.back.db; */ public class DatabaseConnectionException extends Exception { + /** + * Class constructor. + * @param errMsg the error message. + */ + public DatabaseConnectionException(String errMsg) + { + super(errMsg); + } + } diff --git a/src/main/java/fr/unistra/sil/erp/back/db/DatabaseInterface.java b/src/main/java/fr/unistra/sil/erp/back/db/DatabaseInterface.java index 988daf0..c71e083 100644 --- a/src/main/java/fr/unistra/sil/erp/back/db/DatabaseInterface.java +++ b/src/main/java/fr/unistra/sil/erp/back/db/DatabaseInterface.java @@ -46,4 +46,22 @@ public interface DatabaseInterface { */ public List<Stock> getStocks(); + /** + * Returns the stock entry of an item. + * @param id the item's ID. + * @return the stock line. + * @throws DatabaseResourceNotFoundException when the query fails. + */ + public Stock getStockForItem(int itemId) + throws DatabaseResourceNotFoundException; + + /** + * Updates the specified stock's quantity. + * @param id the stock entry's ID. + * @param quantity the new quantity. + * @throws DatabaseUpdateException when the query fails. + */ + public void updateStock(int id, int quantity) + throws DatabaseConnectionException, DatabaseUpdateException; + } diff --git a/src/main/java/fr/unistra/sil/erp/back/db/DatabaseResourceNotFoundException.java b/src/main/java/fr/unistra/sil/erp/back/db/DatabaseResourceNotFoundException.java new file mode 100644 index 0000000..9a34919 --- /dev/null +++ b/src/main/java/fr/unistra/sil/erp/back/db/DatabaseResourceNotFoundException.java @@ -0,0 +1,26 @@ +/* + * CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL-B + * https://cecill.info/licences/Licence_CeCILL-B_V1-fr.html + */ +package fr.unistra.sil.erp.back.db; + +/** + * Exception for non-existent database resources. + * + * If the information requested in a database doesn't exist, this Exception + * is thrown. + * + * @author BEAUVAIS ANTOINE + */ +public class DatabaseResourceNotFoundException extends Exception { + + /** + * Class constructor. + * @param errMsg the error message. + */ + public DatabaseResourceNotFoundException(String errMsg) + { + super(errMsg); + } + +} diff --git a/src/main/java/fr/unistra/sil/erp/back/db/DatabaseSQLiteImpl.java b/src/main/java/fr/unistra/sil/erp/back/db/DatabaseSQLiteImpl.java index dd162f3..5d94b89 100644 --- a/src/main/java/fr/unistra/sil/erp/back/db/DatabaseSQLiteImpl.java +++ b/src/main/java/fr/unistra/sil/erp/back/db/DatabaseSQLiteImpl.java @@ -60,6 +60,12 @@ public class DatabaseSQLiteImpl implements DatabaseInterface { "i.name AS name, s.quantity AS quantity FROM stocks s " + "INNER JOIN items i ON s.item = i.ref"; + private static final String SQL_GETSTOCKFORITEM = SQL_GETSTOCKS + + " WHERE item = ?"; + + private static final String SQL_UPDATESTOCK = + "UPDATE stocks SET quantity = ? WHERE item = ?"; + /** * SQL Connection object. */ @@ -177,6 +183,7 @@ public class DatabaseSQLiteImpl implements DatabaseInterface { } catch (SQLException ex) { Logger.getLogger(DatabaseSQLiteImpl.class.getName()).log( Level.SEVERE, "Failed to parse results.", ex); + return null; } return res; @@ -235,5 +242,96 @@ public class DatabaseSQLiteImpl implements DatabaseInterface { return res; } + + /** + * Returns the stock entry for the specified item. + * @param itemId the item's ID. + * @return the Stock entry. + * @throws DatabaseResourceNotFoundException if the stock doesn't exist. + */ + @Override + public Stock getStockForItem(int itemId) + throws DatabaseResourceNotFoundException { + PreparedStatement ps; + try { + ps = this.conn.prepareStatement(SQL_GETSTOCKFORITEM); + } catch (SQLException ex) { + Logger.getLogger(DatabaseSQLiteImpl.class.getName()).log( + Level.SEVERE, "Failed to connect to database.", ex); + return null; + } + + try { + ps.setInt(1, itemId); + } catch (SQLException ex) { + Logger.getLogger(DatabaseSQLiteImpl.class.getName()).log( + Level.SEVERE, "Failed to set query parameter.", ex); + return null; + } + + ResultSet rs; + try { + rs = ps.executeQuery(); + } catch (SQLException ex) { + Logger.getLogger(DatabaseSQLiteImpl.class.getName()).log( + Level.SEVERE, "Failed to fetch stock for item " + + itemId, ex); + return null; + } + + Stock s; + try { + if(rs.next()) + s = new Stock(rs.getInt("id"), rs.getInt("item"), + rs.getString("name"), + rs.getInt("quantity")); + else + throw new DatabaseResourceNotFoundException("Stock not found."); + } catch (SQLException ex) { + Logger.getLogger(DatabaseSQLiteImpl.class.getName()).log( + Level.SEVERE, "Failed to parse results.", ex); + return null; + } + + return s; + } + + /** + * Updates the specified stock entry with the new quantity. + * @param id the stock's ID. + * @param quantity the associated quantity. + * @throws DatabaseConnectionException when the connection to the DB fails. + * @throws DatabaseUpdateException when the update fails. + */ + @Override + public void updateStock(int id, int quantity) + throws DatabaseConnectionException, DatabaseUpdateException { + + PreparedStatement ps; + try { + ps = this.conn.prepareStatement(SQL_UPDATESTOCK); + } catch (SQLException ex) { + Logger.getLogger(DatabaseSQLiteImpl.class.getName()).log( + Level.SEVERE, "Failed to connect to database.", ex); + throw new DatabaseConnectionException("Failed to connect to DB."); + } + + try { + ps.setInt(1, quantity); + ps.setInt(2, id); + } catch (SQLException ex) { + Logger.getLogger(DatabaseSQLiteImpl.class.getName()).log( + Level.SEVERE, "Failed to set query parameter.", ex); + throw new DatabaseUpdateException("Failed to set query params."); + } + + try { + ps.execute(); + } catch (SQLException ex) { + Logger.getLogger(DatabaseSQLiteImpl.class.getName()) + .log(Level.SEVERE, "Failed to execute query.", ex); + throw new DatabaseUpdateException("Failed to execute query."); + } + } } diff --git a/src/main/java/fr/unistra/sil/erp/back/db/DatabaseUpdateException.java b/src/main/java/fr/unistra/sil/erp/back/db/DatabaseUpdateException.java new file mode 100644 index 0000000..465912e --- /dev/null +++ b/src/main/java/fr/unistra/sil/erp/back/db/DatabaseUpdateException.java @@ -0,0 +1,27 @@ +/* + * CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL-B + * https://cecill.info/licences/Licence_CeCILL-B_V1-fr.html + */ +package fr.unistra.sil.erp.back.db; + +/** + * Exception for database update failures. + * + * When a database system fails to update specific rows + * from the database, then it throws this Exception so + * the application can handle it. + * + * @author BEAUVAIS ANTOINE + */ +public class DatabaseUpdateException extends Exception { + + /** + * Class constructor. + * @param errMsg the error message. + */ + public DatabaseUpdateException(String errMsg) + { + super(errMsg); + } + +} -- GitLab