From 08763e5139711d95506335937359d3cb150f818c Mon Sep 17 00:00:00 2001
From: Gaydamakha <mikhail.gaydamakha@etu.unistra.fr>
Date: Wed, 20 Oct 2021 12:50:04 +0200
Subject: [PATCH] :recycle: Use dependency injection for categories controller

---
 build.gradle                                  |  5 ++
 .../fr/unistra/sil/erp/back/WebMvcConfig.java | 72 ++++++++++++-------
 .../api/ApiRetrieveCategoriesController.java  | 42 +++++------
 .../repository/ICategoriesRepository.java     | 15 ++++
 .../erp/back/repository/SqliteRepository.java | 44 ++++++++++++
 .../category/SqliteCategoryRepository.java    | 49 +++++++++++++
 .../sil/erp/back/repository/package-info.java |  4 ++
 ...itional-spring-configuration-metadata.json |  9 +++
 src/main/resources/application.properties     |  2 +
 9 files changed, 193 insertions(+), 49 deletions(-)
 create mode 100644 src/main/java/fr/unistra/sil/erp/back/repository/ICategoriesRepository.java
 create mode 100644 src/main/java/fr/unistra/sil/erp/back/repository/SqliteRepository.java
 create mode 100644 src/main/java/fr/unistra/sil/erp/back/repository/category/SqliteCategoryRepository.java
 create mode 100644 src/main/java/fr/unistra/sil/erp/back/repository/package-info.java
 create mode 100644 src/main/resources/META-INF/additional-spring-configuration-metadata.json

diff --git a/build.gradle b/build.gradle
index a85e402..89164a4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,6 +5,10 @@ plugins {
 	id 'war'
 }
 
+tasks {
+    compileJava.inputs.files(processResources)
+}
+
 group = 'fr.unistra.sil'
 version = '0.0.1-SNAPSHOT'
 sourceCompatibility = '1.8'
@@ -24,6 +28,7 @@ dependencies {
     testImplementation 'org.springframework.boot:spring-boot-starter-test'
 
     implementation 'org.thymeleaf:thymeleaf-spring5'
+    annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
 }
 
 javadoc {
diff --git a/src/main/java/fr/unistra/sil/erp/back/WebMvcConfig.java b/src/main/java/fr/unistra/sil/erp/back/WebMvcConfig.java
index 86f0a21..2842710 100644
--- a/src/main/java/fr/unistra/sil/erp/back/WebMvcConfig.java
+++ b/src/main/java/fr/unistra/sil/erp/back/WebMvcConfig.java
@@ -5,45 +5,47 @@
 package fr.unistra.sil.erp.back;
 
 import fr.unistra.sil.erp.back.interceptor.api.ApiAuthenticationInterceptor;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.PropertySource;
-import org.springframework.context.annotation.PropertySources;
+import org.springframework.context.annotation.*;
 import org.springframework.core.env.Environment;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
 /**
  * Main configuration file for the application.
+ *
  * @author BEAUVAIS ANTOINE
  */
 @Configuration
-@PropertySources(
-        @PropertySource("/apikey.properties")
-)
+@EnableWebMvc
 public class WebMvcConfig implements WebMvcConfigurer {
-    
+
     /**
      * Autowired environment for retrieving properties.
      */
-    @Autowired
-    private Environment env;
-    
+    private final Environment env;
+
     /**
      * API version.
      */
     public static final String API_VERSION = "v1";
-    
+
     /**
      * API prefix.
      */
     public static final String API_PREFIX = "/api/";
-    
+
     /**
      * Prefix for API calls.
      */
     public static final String API_FULL_PREFIX = API_PREFIX + API_VERSION;
-    
+
     /**
      * API Mapping for retrieving all items.
      */
@@ -72,29 +74,47 @@ public class WebMvcConfig implements WebMvcConfigurer {
      * Web mapping for the login page.
      */
     public static final String MAPPING_LOGIN = "/login";
-    
+
     /**
      * Web mapping for the items list.
      */
     public static final String WEB_MAPPING_ITEMS = "/products";
-    
+
+    public WebMvcConfig(Environment env) {
+        this.env = env;
+    }
+
     /**
      * Adds interceptors to the application.
-     * 
-     * Interceptors process HTTP requests before they reach their 
+     * <p>
+     * Interceptors process HTTP requests before they reach their
      * attributed methods.
-     * 
-     * Currently, interceptors are only used for authentication in this 
+     * <p>
+     * Currently, interceptors are only used for authentication in this
      * application.
-     * 
+     *
      * @param registry the Interceptor Registry to use for additions.
      */
     @Override
     public void addInterceptors(InterceptorRegistry registry) {
-        
-        registry.addInterceptor(new ApiAuthenticationInterceptor(
-                this.env.getProperty("api.key")))
-                .addPathPatterns(API_PREFIX + "**");
-        
+        registry.addInterceptor(new ApiAuthenticationInterceptor(this.env.getRequiredProperty("api.key"))).addPathPatterns(API_PREFIX + "**");
+    }
+
+    /**
+     * SQL Connection object.
+     */
+    private Connection conn;
+
+    @Bean
+    public Connection connection() {
+        if (this.conn == null) {
+            try {
+                conn = DriverManager.getConnection(env.getRequiredProperty("spring.datasource.url"));
+            } catch (SQLException ex) {
+                Logger.getLogger(this.getClass().getName()).log(
+                        Level.SEVERE, "Failed to connect to SQLite file.", ex);
+            }
+        }
+        return conn;
     }
 }
diff --git a/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiRetrieveCategoriesController.java b/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiRetrieveCategoriesController.java
index a2c01ba..d7e4d45 100644
--- a/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiRetrieveCategoriesController.java
+++ b/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiRetrieveCategoriesController.java
@@ -5,50 +5,46 @@
 package fr.unistra.sil.erp.back.controller.api;
 
 import static fr.unistra.sil.erp.back.WebMvcConfig.MAPPING_GETCATEGORIES;
-import fr.unistra.sil.erp.back.DatabaseSystem;
+
 import fr.unistra.sil.erp.back.controller.IRetrieveCategoriesController;
 import fr.unistra.sil.erp.back.model.Category;
-import fr.unistra.sil.erp.back.db.DatabaseConnectionException;
+
 import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
+
+import fr.unistra.sil.erp.back.repository.ICategoriesRepository;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RestController;
-import fr.unistra.sil.erp.back.db.IDatabase;
 
 /**
  * REST controller for the category list.
+ *
  * @author BEAUVAIS ANTOINE
  */
 @RestController
-public class ApiRetrieveCategoriesController
-        implements IRetrieveCategoriesController {
-    
+public class ApiRetrieveCategoriesController implements IRetrieveCategoriesController {
+
+    private final ICategoriesRepository repository;
+
+    public ApiRetrieveCategoriesController(ICategoriesRepository repository) {
+        this.repository = repository;
+    }
+
     /**
      * Returns the list of categories in JSON format.
+     *
      * @return the HTTP response.
      * @throws ApiServerErrorException Database failure.
      */
     @GetMapping(MAPPING_GETCATEGORIES)
     @Override
-    public ResponseEntity<Object> getCategories() throws ApiServerErrorException
-    {
-        IDatabase db;
-        try {
-            db = DatabaseSystem.getInstance();
-        } catch (DatabaseConnectionException ex) {
-            Logger.getLogger(ApiRetrieveCategoriesController.class.getName())
-                    .log(Level.SEVERE, "Failed to connect to database.", ex);
-            throw new ApiServerErrorException("Database failure.");
-        }
-        
-        List<Category> res = db.getCategories();
-        if(res == null)
+    public ResponseEntity<Object> getCategories() throws ApiServerErrorException {
+        List<Category> res = repository.getCategories();
+        if (res == null)
             throw new ApiServerErrorException("Database failure.");
-        
+
         return new ResponseEntity<>(res, HttpStatus.OK);
     }
-    
+
 }
diff --git a/src/main/java/fr/unistra/sil/erp/back/repository/ICategoriesRepository.java b/src/main/java/fr/unistra/sil/erp/back/repository/ICategoriesRepository.java
new file mode 100644
index 0000000..f9287e0
--- /dev/null
+++ b/src/main/java/fr/unistra/sil/erp/back/repository/ICategoriesRepository.java
@@ -0,0 +1,15 @@
+package fr.unistra.sil.erp.back.repository;
+
+import fr.unistra.sil.erp.back.model.Category;
+
+import java.util.List;
+
+public interface ICategoriesRepository {
+
+    /**
+     * Returns the list of all categories.
+     *
+     * @return the list of categories, or null if an error occurred.
+     */
+    List<Category> getCategories();
+}
diff --git a/src/main/java/fr/unistra/sil/erp/back/repository/SqliteRepository.java b/src/main/java/fr/unistra/sil/erp/back/repository/SqliteRepository.java
new file mode 100644
index 0000000..11e239b
--- /dev/null
+++ b/src/main/java/fr/unistra/sil/erp/back/repository/SqliteRepository.java
@@ -0,0 +1,44 @@
+package fr.unistra.sil.erp.back.repository;
+
+import org.springframework.stereotype.Repository;
+
+import java.sql.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+@Repository
+public class SqliteRepository {
+
+    /**
+     * SQL Connection object.
+     */
+    private final Connection conn;
+
+    /**
+     * Class constructor.
+     */
+    public SqliteRepository(Connection conn) {
+        this.conn = conn;
+    }
+
+    /**
+     * Executes a standard query, without parameters.
+     *
+     * @param query the string's query.
+     * @return the result set for this query.
+     */
+    protected ResultSet query(String query) {
+        Statement stmt;
+        ResultSet rs;
+        try {
+            stmt = this.conn.createStatement();
+            rs = stmt.executeQuery(query);
+        } catch (SQLException ex) {
+            Logger.getLogger(this.getClass().getName()).log(
+                    Level.SEVERE, "Failed to run query: " + query, ex);
+            return null;
+        }
+
+        return rs;
+    }
+}
diff --git a/src/main/java/fr/unistra/sil/erp/back/repository/category/SqliteCategoryRepository.java b/src/main/java/fr/unistra/sil/erp/back/repository/category/SqliteCategoryRepository.java
new file mode 100644
index 0000000..aec5070
--- /dev/null
+++ b/src/main/java/fr/unistra/sil/erp/back/repository/category/SqliteCategoryRepository.java
@@ -0,0 +1,49 @@
+package fr.unistra.sil.erp.back.repository.category;
+
+import fr.unistra.sil.erp.back.model.Category;
+import fr.unistra.sil.erp.back.repository.ICategoriesRepository;
+import fr.unistra.sil.erp.back.repository.SqliteRepository;
+import org.springframework.stereotype.Repository;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+@Repository
+public class SqliteCategoryRepository extends SqliteRepository implements ICategoriesRepository {
+
+    /**
+     * Query used to retrieve all categories stored in the database.
+     */
+    private static final String SQL_GETCATEGORIES =
+            "SELECT id, name FROM categories";
+
+    public SqliteCategoryRepository(Connection conn) {
+        super(conn);
+    }
+
+    @Override
+    public List<Category> getCategories() {
+        ResultSet rs = this.query(SQL_GETCATEGORIES);
+        if (rs == null)
+            return null;
+
+        List<Category> res = new ArrayList<>();
+        try {
+            while (rs.next()) {
+                Category c = new Category(rs.getInt("id"), rs.getString("name"));
+                res.add(c);
+            }
+        } catch (SQLException ex) {
+            Logger.getLogger(this.getClass().getName()).log(
+                    Level.SEVERE, "Failed to fetch results.", ex);
+            return null;
+        }
+
+        return res;
+    }
+}
diff --git a/src/main/java/fr/unistra/sil/erp/back/repository/package-info.java b/src/main/java/fr/unistra/sil/erp/back/repository/package-info.java
new file mode 100644
index 0000000..a73a187
--- /dev/null
+++ b/src/main/java/fr/unistra/sil/erp/back/repository/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Database interfaces and implementations.
+ */
+package fr.unistra.sil.erp.back.repository;
\ No newline at end of file
diff --git a/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/src/main/resources/META-INF/additional-spring-configuration-metadata.json
new file mode 100644
index 0000000..aa23278
--- /dev/null
+++ b/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -0,0 +1,9 @@
+{
+  "properties": [
+    {
+      "name": "api.key",
+      "type": "java.lang.String",
+      "description": "Api key to access the server. Should be added to the HTTP-header ''."
+    }
+  ]
+}
\ No newline at end of file
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 6e04b84..9131178 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1 +1,3 @@
 spring.mvc.static-path-pattern=/static/**
+api.key=someKey
+spring.datasource.url=jdbc:sqlite:dev.db
-- 
GitLab