diff --git a/README.md b/README.md index 8fcc6af..34609f8 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,45 @@ Please use this readme as your projects readme. You can find instructions for the assignment in the [`INSTRUCTIONS.md`](INSTRUCTIONS.md) file. + +Overview +======== + +This project is a simple command-line application that communicates with a PostgreSQL database to retrieve data and generate reports based on that data. It's composed of several classes and packages that work together to accomplish this task. + +Classes and packages +-------------------- + +The application's main class is `Main` class, it runs the entire application. It's composed of three services: `AppConfigService`, `DatabaseService`, and `ReportGenerationService`. + +### AppConfigService + +`AppConfigService` is a service class that prompts and stores the configuration that the app needs to be run with. It prompts the user for input for the country name and date, and stores them as class members. + +### DatabaseService + +`DatabaseService` class is responsible for maintaining the active connection between the app and the SQL Server. It also communicates with the `DatabaseApi` class to perform final CRUD operations and return any results. + +### ReportGenerationService + +`ReportGenerationService` generates a report from results. Since only a std-out is the only strategy, composition is preferred. It provides a single method `reportBaseResults(QueryResultsRepository queryResultsRepository)` that takes a `QueryResultsRepository` object as input and prints the results to the console. The method prints the `toString()` representation of the `QueryResultsRepository` object. It provides a basic printer and more members can be added in the future to use different strategies for printing out the received results. + +### data.dtos + +`data.dtos` package contains the `QueryDTO` class. This class serves as a container for storing SQL statements to be executed, along with their arguments. + +### data.enums + +`data.enums` package contains the `CustomPreparedStatementsRead` enum. This enum contains all the read-only prepared statements that are used throughout the application. + +### data.repos + +`data.repos` package contains the `QueryResultsRepository` class. This class stores the results of the executed queries, and can be used to generate a report. + +### data.models + +`data.models` package contains the `PersistentResultSetModel` class. This class adapts a given `ResultSet` to a `PersistentResultModel`. + +### apis + +`apis` package contains the `DatabaseApi` class. This class communicates with the database to perform final CRUD operations, returning any results. This class is responsible for executing SQL statements, which are passed to it in the form of a QueryDTO class. It receives a QueryDTO object and a JDBC connection, then it processes the query and returns a PreparedStatement object. This class is designed to handle any exception that may occur during the execution of the query and it can be extended in the future to handle other types of queries if needed. diff --git a/docs/ClassUML.png b/docs/ClassUML.png new file mode 100644 index 0000000..3535f4b Binary files /dev/null and b/docs/ClassUML.png differ diff --git a/src/Main.java b/src/Main.java index 630989a..5cda283 100644 --- a/src/Main.java +++ b/src/Main.java @@ -7,6 +7,9 @@ import services.ReportGenerationService; import java.sql.SQLException; +/** + * Entry point of the application + */ public class Main { public static void main(String[] args) { final App app = new App(); @@ -14,6 +17,9 @@ public class Main { } } +/** + * Responsible for the overall flow of the application. It creates and manages instances Services. + */ class App { public App() { databaseService = new DatabaseService(); @@ -25,28 +31,39 @@ class App { final private AppConfigService appConfigService; final private ReportGenerationService reportGenerationService; + /** + * Endpoints mentioned under the `Report` section + */ private static final CustomPreparedStatementsRead[] reportEndpoints = { CustomPreparedStatementsRead.HighestAndLowest10Vaccination, CustomPreparedStatementsRead.HighestInfectionsPer100K, CustomPreparedStatementsRead.CountriesLaggingBehind, }; + /** + * Endpoints mentioned under the `Query` section + */ private static final CustomPreparedStatementsRead[] queryEndpoints = { CustomPreparedStatementsRead.DailyInfectionsAndDeathAggregate, }; + /** + * Responsible for the overall flow of the application. + * It retrieves data from the database using the `DatabaseService` class, + * generates reports using the `ReportGenerationService` class, and prompts the user for input using the `AppConfigService` class. + */ public void run() { try { QueryResultsRepository resultsRepository; for (final CustomPreparedStatementsRead query : reportEndpoints) { resultsRepository = databaseService.executeReadReportsEndpoint(new QueryDTO(query)); - reportGenerationService.reportResults(resultsRepository); + reportGenerationService.reportBaseResults(resultsRepository); } for(final CustomPreparedStatementsRead query: queryEndpoints) { appConfigService.promptAndSetUserArguments(); resultsRepository = databaseService.executeReadReportsEndpoint(new QueryDTO(query, new Object[]{appConfigService.countryName, appConfigService.date,})); - reportGenerationService.reportResults(resultsRepository); + reportGenerationService.reportBaseResults(resultsRepository); } diff --git a/src/apis/DatabaseApi.java b/src/apis/DatabaseApi.java index 570c421..cf59287 100644 --- a/src/apis/DatabaseApi.java +++ b/src/apis/DatabaseApi.java @@ -8,10 +8,22 @@ import java.util.Date; import java.util.MissingFormatArgumentException; /** - * Communicates with the database to perform final CRUD operations, returning any results. - * When there is a need for more than a single READ action, the Api can be abstracted, and implement into constituent action-specific APIs. + * The `DatabaseApi` class provides a way to communicate with the database to perform read operations and return the results. + * It contains a single method `performReadQuery()` that takes a `QueryDTO` and a `Connection` object as parameters and + * returns a `PreparedStatement` object. It throws `SQLException` in case of any SQL error or `MissingFormatArgumentException` + * in case of any missing argument or extra argument. + * + * The class can be abstracted and implemented into constituent action-specific APIs when there is a need for more than a single READ action. */ public class DatabaseApi { + /** + * Creates and executes a PreparedStatement, given the QueryDTO. + * @param queryDTO + * @param connection + * @return + * @throws MissingFormatArgumentException in case of any missing argument or extra argument. + * @throws SQLException + */ public PreparedStatement performReadQuery(QueryDTO queryDTO, Connection connection) throws SQLException { // Set template PreparedStatement statement = connection.prepareStatement(queryDTO.statement().statementTemplate); diff --git a/src/data/dtos/QueryDTO.java b/src/data/dtos/QueryDTO.java index e605d9e..547a9f2 100644 --- a/src/data/dtos/QueryDTO.java +++ b/src/data/dtos/QueryDTO.java @@ -4,10 +4,14 @@ import data.enums.CustomPreparedStatementsRead; /** * Container for storing SQL statements to be executed, along with their aruments. - * @param statement - * @param templateArgs + * @param statement an instance of `CustomPreparedStatementsRead` enum which contains the statement template to be executed. + * @param templateArgsn an array of `Object` representing the arguments to be used in the statement template. */ public record QueryDTO(CustomPreparedStatementsRead statement, Object[] templateArgs) { + /** + * overloaded constructor that takes only a `CustomPreparedStatementsRead` object as parameter. + * @param customPreparedStatementsRead + */ public QueryDTO(CustomPreparedStatementsRead customPreparedStatementsRead) { this(customPreparedStatementsRead, new Object[]{}); } diff --git a/src/data/enums/CustomPreparedStatementsRead.java b/src/data/enums/CustomPreparedStatementsRead.java index 5af4977..57fc483 100644 --- a/src/data/enums/CustomPreparedStatementsRead.java +++ b/src/data/enums/CustomPreparedStatementsRead.java @@ -2,7 +2,6 @@ package data.enums; /** * Holds prepared data for Read-based SQL queries. - * */ public enum CustomPreparedStatementsRead { DailyInfectionsAndDeathAggregate(""" diff --git a/src/data/models/PersistentResultSetModel.java b/src/data/models/PersistentResultSetModel.java index b11df46..6e050ac 100644 --- a/src/data/models/PersistentResultSetModel.java +++ b/src/data/models/PersistentResultSetModel.java @@ -6,6 +6,13 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +/** + * The class `PersistentResultSetModel` adapts a given `ResultSet` to a + * PersistentResultModel. The class provides two main fields, an array of `ResultRowModel` called + * `resultRowEntries` and an array of `String` called `columnNames`. + * The factory constructor takes a `ResultSet` as a parameter, and adapts it to a `PersistentResultModel`. + * The class also provides a toString method that returns the string representation of the result set. + */ public class PersistentResultSetModel { /** * Factory constructor, that adapts a given ResultSet to PersistentResultModel. @@ -36,8 +43,25 @@ public class PersistentResultSetModel { } + /** + * Represents all the rows of the result set. + */ final public ResultRowModel[] resultRowEntries; + /** + * Represents column names that appear in the result set. + */ + + /** + * It returns the string representation of the result set in tabular format, showing the column names and rowElements. + */ final public String[] columnNames; + + /** + * The `ResultRowModel` class represents a single row in the result set. It contains an array of + * `Object` called `rowElements`. The class provides an implementation of the `toString()` method + * that returns the string representation of the row. + * @param rowElements The elements of the row + */ public record ResultRowModel(Object[] rowElements){ @Override public String toString() { diff --git a/src/data/repos/QueryResultsRepository.java b/src/data/repos/QueryResultsRepository.java index d22a64b..900a92a 100644 --- a/src/data/repos/QueryResultsRepository.java +++ b/src/data/repos/QueryResultsRepository.java @@ -8,7 +8,11 @@ import java.sql.SQLException; import java.util.ArrayList; /** - * Stores the results obtained from executing an SQL query. + * Provides a way to store and persist the results of read queries. + * It contains an `ArrayList` of `PersistentResultSetModel` called `persistentResults` and a `String` called `queryDescription`. + * The class provides a constructor that takes a `queryDescription` as a parameter and an `addResult()` method + * that takes a `ResultSet` as a parameter and adds it to the `persistentResults` list. + * The class also provides a `toString()` method that returns the string representation of the stored results and their description. */ public class QueryResultsRepository { public QueryResultsRepository(String queryDescription) { @@ -20,10 +24,13 @@ public class QueryResultsRepository { */ final private ArrayList persistentResults = new ArrayList<>(); + /** + * Represents a description of the query that was executed. + */ final private String queryDescription; /** - * Adds a ResultSet to the store to persist. + * Creates a PersistentResultSetModel instance from given ResultSet, and adds it to the persistentResults list * @param resultSet * @throws SQLException */ diff --git a/src/services/AppConfigService.java b/src/services/AppConfigService.java index 083620b..a344334 100644 --- a/src/services/AppConfigService.java +++ b/src/services/AppConfigService.java @@ -1,7 +1,7 @@ package services; -import data.constants.ConstFormatters; -import data.constants.ConstValues; +import utils.ConstFormatters; +import utils.ConstValues; import java.text.ParseException; @@ -15,6 +15,10 @@ public class AppConfigService { public String countryName; public Date date; + /** + * Prompts the user to input the country name and date, and stores the input. + * It will keep prompting the user until the date is in the correct format. + */ public void promptAndSetUserArguments() { final Scanner scanner = new Scanner(System.in); System.out.println(ConstValues.inputCountryName); diff --git a/src/services/DatabaseService.java b/src/services/DatabaseService.java index 6c3e698..0d35d10 100644 --- a/src/services/DatabaseService.java +++ b/src/services/DatabaseService.java @@ -7,7 +7,9 @@ import data.repos.QueryResultsRepository; import java.sql.*; /** - * Stores the active connection between the app and the SQL Server. + * Responsible for storing the active connection between the application and the SQL server. + * It creates the connection to the PostgreSQL server and provides a way to execute read queries and persist their results. + * It uses DatabaseApi class for performing read queries. */ public class DatabaseService { private static final String url = "jdbc:postgresql://localhost:5432/lunatech_covid"; @@ -23,7 +25,7 @@ public class DatabaseService { } /** - * Fetches read-query results and packages them to persist them (and so free the connection). + * Fetches read-query results and packages them to persist them (and so frees the connection). * * @param queryDTO The query with its arguments. * @return Persisting (non-lazy) results from executing the query. diff --git a/src/services/ReportGenerationService.java b/src/services/ReportGenerationService.java index 3f08c34..91d47c2 100644 --- a/src/services/ReportGenerationService.java +++ b/src/services/ReportGenerationService.java @@ -2,8 +2,20 @@ package services; import data.repos.QueryResultsRepository; +/** + * Generates a report from results + * + * Since only a std-out is the only strategy, composition is preferred. + * If more strategies are needed in the future, just add in a ReporterApi that delegates the task + * to any (strategy) class that implements it. + * A basic printer is provided, more members can be added to use print out the received results differently. + */ public class ReportGenerationService { - public void reportResults(QueryResultsRepository queryResultsRepository) { + /** + * Prints results to the console. + * @param queryResultsRepository + */ + public void reportBaseResults(QueryResultsRepository queryResultsRepository) { System.out.println(queryResultsRepository); } } diff --git a/src/data/constants/ConstFormatters.java b/src/utils/ConstFormatters.java similarity index 87% rename from src/data/constants/ConstFormatters.java rename to src/utils/ConstFormatters.java index 529f736..c455f19 100644 --- a/src/data/constants/ConstFormatters.java +++ b/src/utils/ConstFormatters.java @@ -1,4 +1,4 @@ -package data.constants; +package utils; import java.text.SimpleDateFormat; diff --git a/src/data/constants/ConstValues.java b/src/utils/ConstValues.java similarity index 87% rename from src/data/constants/ConstValues.java rename to src/utils/ConstValues.java index 74294bb..30c1d82 100644 --- a/src/data/constants/ConstValues.java +++ b/src/utils/ConstValues.java @@ -1,4 +1,4 @@ -package data.constants; +package utils; public class ConstValues { //I