Skip to content
Snippets Groups Projects
Commit affb7c8d authored by Deane, Alistair (UG - Comp Sci & Elec Eng)'s avatar Deane, Alistair (UG - Comp Sci & Elec Eng)
Browse files

finished

parent 01305b24
Branches master
No related tags found
No related merge requests found
Showing
with 1557 additions and 70 deletions
# Fortommy ## COM1028 Software Engineering - FlickFinder
This is the starting project for the COM1028 Software Engineering module. This project is a simple application that provides a RESTful API for accessing a movie database. The database is a simple SQLite database that contains information about movies, people, and their relationships.
## Dependencies
## Getting started Overall, we have the following dependencies in our project:
To make it easy for you to get started with GitLab, here's a list of recommended next steps. - [sqllite-jdbc](https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc) - SQLite JDBC Driver
- [javalin](https://javalin.io/) - A simple web framework for Java
- [junit](https://junit.org/junit5/) - JUnit for unit testing
- [mockito](https://site.mockito.org/) - Mockito for mocking objects in unit tests
- [rest-assured](https://rest-assured.io/) - Testing and validating our APIs
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! These dependencies are managed by Maven, and you can find them in the [pom.xml](pom.xml) file.
## Add your files ## Database
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files ### Development Database
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
``` The database is a simple SQLite database that contains information about movies, people, and their relationships. The database is structured as follows:
cd existing_repo
git remote add origin https://gitlab.surrey.ac.uk/ad02184/fortommy.git
git branch -M main
git push -uf origin main
```
## Integrate with your tools ![Database](https://csee.pages.surrey.ac.uk/com1028/res/ERD.png)
- [ ] [Set up project integrations](https://gitlab.surrey.ac.uk/ad02184/fortommy/-/settings/integrations) You've been given the IMDB movies database to work with. It is a simple database and you will only need to interact with it in a read only manner; you will not be adding data or modifying the database.
## Collaborate with your team You can find the database in the [src/main/resources](src/main/resources) folder. The database is called `movies.db`. You should not modify this database in any way. However, it won't be there until you run the project for the first time. I am using the com.googlecode.maven-download-plugin to pull the database in from a remote location. This is defined in the [pom.xml](pom.xml) file. It should be pulled when you run the project for the first time.
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) ### Testing Database
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
## Test and Deploy Although the development database is simple, it has a lot of data. This can make testing difficult. For testing we use a in-memory database. This database is created and populated with data before each test and destroyed after each test. This code can be found in [src/test/java/com/flickfinder/util/Seeder.java](src/test/java/com/flickfinder/util/Seeder.java).
Use the built-in continuous integration in GitLab. ## Getting Started
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) 1. Clone the repository: `git clone <repository address>`
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) 2. Open the project in your favourite IDE (IntelliJ, Eclipse, etc.). While you can use any IDE, these instructions will assume you are using Eclipse. The project is a Maven project; as such, the process should be similar in other IDEs.
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) 3. In Eclipse, got to File -> Open Projects from File System... and select the root directory of the project.
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
*** ![Open Project](https://csee.pages.surrey.ac.uk/com1028/res/open_project.png)
# Editing this README 4. In the package explorer, right click on the project and select Run As -> Maven Install. This will download all the dependencies and build the project, including the database.
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template. ![Maven Install](https://csee.pages.surrey.ac.uk/com1028/res/maven_install.png)
## Suggestions for a good README 5. If all has gone well, you should see a message in the console saying "BUILD SUCCESS". If you see this, you are ready to run the project. Ignore, any warnings or errors about JRE System Library.
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. ![Build Success](https://csee.pages.surrey.ac.uk/com1028/res/build_success.png)
## Name 6. To run the project, right click on the project in the package explorer and locate `src/main/java/com/flickfinder/Main.java`, right click on this file and select Run As -> Java Application.
Choose a self-explaining name for your project.
## Description 7. You'll see a warning in the console: "SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". This is not a problem and can be ignored.
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
## Badges 8. Open a browser. Ideally one with JSON rendering capabilities (e.g.Firefox or Chrome with a JSON extension). In the address bar, type `http://localhost:8000`. If all has gone well, you should see the API documentation.
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
## Visuals
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
## Installation
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
## Usage
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
## Support
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
## Roadmap
If you have ideas for releases in the future, it is a good idea to list them in the README.
## Contributing
State if you are open to contributions and what your requirements are for accepting them.
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
## Authors and acknowledgment
Show your appreciation to those who have contributed to the project.
## License
For open source projects, say how it is licensed.
## Project status
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
pom.xml 0 → 100644
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.flickfinder</groupId>
<artifactId>COM1028-ad02184</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<!-- sql light drivers -->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.45.1.0</version>
</dependency>
<!-- javalin for the api -->
<dependency>
<groupId>io.javalin</groupId>
<artifactId>javalin</artifactId>
<version>6.0.0</version>
</dependency>
<!-- this is a dependancy for javalin -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.1</version>
</dependency>
<!-- j unit -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<!-- mockito -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.10.0</version>
</dependency>
<!-- rest assured -->
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>5.4.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.googlecode.maven-download-plugin</groupId>
<artifactId>download-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<id>download-file</id>
<phase>generate-resources</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>
https://csee.pages.surrey.ac.uk/com1028/res/movies.db</url>
<outputDirectory>${project.basedir}/src/main/resources</outputDirectory>
<outputFileName>movies.db</outputFileName>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
package com.flickfinder;
import com.flickfinder.controller.MovieController;
import com.flickfinder.controller.PersonController;
import com.flickfinder.dao.MovieDAO;
import com.flickfinder.dao.PersonDAO;
import io.javalin.Javalin;
import io.javalin.http.staticfiles.Location;
/**
* This class is used to configure the Javalin web server.
* It sets up the routes and the static files location.
* As you implement the remaining functionality, you will need to update this
* class.
*
*/
public class AppConfig {
/**
* Sets up the routes and the static files location.
* As you can see, the routes are currently incomplete. Depending on how far you
* get in the assessment, you will update some or all of the routes in this
* method.
*
* @param port The port that the server should run on.
* @return The Javalin object that represents the running server.
*/
public static Javalin startServer(int port) {
Javalin app = Javalin.create(config -> {
config.staticFiles.add("/public", Location.CLASSPATH);
}).start(port);
// Set up controllers
MovieDAO movieDao = new MovieDAO();
MovieController movieController = new MovieController(movieDao);
PersonDAO personDao = new PersonDAO();
PersonController personController = new PersonController(personDao);
// Uncomment the following lines as you progress through the assessment.
// PersonController personController = new PersonController();
// PersonDAO personDao = new PersonDAO();
// PersonController personController = new PersonController(personDao);
/**
* Below are the routes for the application.
* You will need uncomment these as you progress through the assessment.
* Do not:
* - change the strings and methods passed to the get() method.
* - change the order of the routes. Order matters in Javalin, as the routes are
* pattern matched in the order they are defined.
* Only uncomment the routes for the functionality you have implemented.
*/
app.get("/movies/ratings/{year}", movieController::getRatingsByYear);
app.get("/movies", movieController::getAllMovies);
app.get("/movies/{id}", movieController::getMovieById);
app.get("/movies/{id}/stars", movieController::getPeopleByMovieId);
app.get("/people", personController::getAllPeople);
app.get("/people/{id}", personController::getPersonById);
app.get("/people/{id}/movies", personController::getMoviesStarringPerson);
return app;
}
}
\ No newline at end of file
package com.flickfinder;
import com.flickfinder.util.Database;
/**
* Entry point of the application.
*
*/
public class Main {
/**
* The port that the server should run on.
*/
static int port = 8000;
/**
* Set up a Javalin server and the database.
*
* @param args The command line arguments.
*/
public static void main(String[] args) {
// This gives us a path to the database file, which is in the resources folder.
final String dbPath = "src/main/resources/movies.db";
/**
* This sets up the database connection and starts the server.
* In this case, we are using a connection string to connect to the database.
* For testing, we are using an in-memory database.
*/
Database.getInstance("jdbc:sqlite:" + dbPath);
// start the server
AppConfig.startServer(port);
}
}
\ No newline at end of file
package com.flickfinder.controller;
import java.sql.SQLException;
import java.util.List;
import com.flickfinder.dao.MovieDAO;
import com.flickfinder.model.Movie;
import io.javalin.http.Context;
/**
* The controller for the movie endpoints.
*
* The controller acts as an intermediary between the HTTP routes and the DAO.
*
* As you can see each method in the controller class is responsible for
* handling a specific HTTP request.
*
* Methods a Javalin Context object as a parameter and uses it to send a
* response back to the client.
* We also handle business logic in the controller, such as validating input and
* handling errors.
*
* Notice that the methods don't return anything. Instead, they use the Javalin
* Context object to send a response back to the client.
*/
public class MovieController {
/**
* The movie data access object.
*/
private final MovieDAO movieDAO;
/**
* Constructs a MovieController object and initializes the movieDAO.
*/
public MovieController(MovieDAO movieDAO) {
this.movieDAO = movieDAO;
}
/**
* Returns a list of all movies in the database.
*
* @param ctx the Javalin context
*/
public void getAllMovies(Context ctx) {
String limitparam = ctx.queryParam("limit");
try {
ctx.json(movieDAO.getAllMovies(limitparam));
} catch (SQLException e) {
ctx.status(500);
ctx.result("Database error"); if(limitparam == null) {
limitparam = "50";
}
e.printStackTrace();
}
}
/**
* Returns the movie with the specified id.
*
* @param ctx the Javalin context
*/
public void getMovieById(Context ctx) {
int id = Integer.parseInt(ctx.pathParam("id"));
try {
Movie movie = movieDAO.getMovieById(id);
if (movie == null) {
ctx.status(404);
ctx.result("Movie not found");
return;
}
ctx.json(movieDAO.getMovieById(id));
} catch (SQLException e) {
ctx.status(500);
ctx.result("Database error");
e.printStackTrace();
}
}
public void getRatingsByYear(Context ctx) {
int year = Integer.parseInt(ctx.pathParam("year"));
String limitparam = ctx.queryParam("limit");
String voteparam = ctx.queryParam("votes");
if(limitparam == null) {
limitparam = "50";
}
if (voteparam == null){
voteparam = "0";
}
try {
List<Movie> movie = movieDAO.getRatingsByYear(year, limitparam, voteparam);
if (movie == null) {
ctx.status(404);
ctx.result("Movie not found");
return;
}
ctx.json(movieDAO.getRatingsByYear(year, limitparam, voteparam));
} catch (SQLException e) {
ctx.status(500);
ctx.result("Database error");
e.printStackTrace();
}
}
public void getPeopleByMovieId(Context ctx) {
int id = Integer.parseInt(ctx.pathParam("id"));
try {
ctx.json(movieDAO.getPeopleByMovieId(id));
} catch (SQLException e) {
ctx.status(500);
ctx.result("Database error");
e.printStackTrace();
}
}
}
\ No newline at end of file
package com.flickfinder.controller;
import java.sql.SQLException;
import java.util.List;
import com.flickfinder.dao.PersonDAO;
import com.flickfinder.model.Movie;
import com.flickfinder.model.Person;
import io.javalin.http.Context;
public class PersonController {
private final PersonDAO personDAO;
// to complete the must-have requirements you need to add the following methods:
// getAllPeople
// getPersonById
// you will add further methods for the more advanced tasks; however, ensure your have completed
// the must have requirements before you start these.
public PersonController(PersonDAO personDAO) {
this.personDAO = personDAO;
}
public void getAllPeople(Context ctx) {
String limitparam = ctx.queryParam("limit");
try {
ctx.json(personDAO.getAllPeople(limitparam));
} catch (SQLException e) {
ctx.status(500);
ctx.result("Database error");
e.printStackTrace();
}
}
public void getPersonById(Context ctx) {
int id = Integer.parseInt(ctx.pathParam("id"));
try {
Person person = personDAO.getPersonById(id);
System.out.println(person);
if (person == null){
ctx.status(404);
ctx.result("Person not found");
return;
}
ctx.json(personDAO.getPersonById(id));
} catch (SQLException e) {
ctx.status(500);
ctx.result("Database error");
e.printStackTrace();
}
}
public void getMoviesStarringPerson(Context ctx) {
int id = Integer.parseInt(ctx.pathParam("id"));
try {
List<Movie> movie = personDAO.getMoviesStarringPerson(id);
if (movie == null) {
ctx.status(404);
ctx.result("Movie not found");
return;
}
ctx.json(movie);
} catch (SQLException e) {
ctx.status(500);
ctx.result("Database error");
e.printStackTrace();
}
}
}
\ No newline at end of file
package com.flickfinder.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import com.flickfinder.model.Movie;
import com.flickfinder.model.MovieRating;
import com.flickfinder.model.Person;
import com.flickfinder.util.Database;
/**
* The Data Access Object for the Movie table.
*
* This class is responsible for getting data from the Movies table in the
* database.
*
*/
public class MovieDAO {
/**
* The connection to the database.
*/
private final Connection connection;
/**
* Constructs a SQLiteMovieDAO object and gets the database connection.
*
*/
public MovieDAO() {
Database database = Database.getInstance();
connection = database.getConnection();
}
/**
* Returns a list of all movies in the database.
*
* @return a list of all movies in the database
* @throws SQLException if a database error occurs
*/
public List<Movie> getAllMovies(String params) throws SQLException {
List<Movie> movies = new ArrayList<>();
Statement statement = connection.createStatement();
String temp = params;
// I've set the limit to 10 for development purposes - you should do the same.
if (temp == null) {
temp = "50";
}
ResultSet rs = statement.executeQuery("select * from movies LIMIT " + temp);
while (rs.next()) {
movies.add(new Movie(rs.getInt("id"), rs.getString("title"), rs.getInt("year")));
}
return movies;
}
public List<Movie> getAllMovies() throws SQLException {
return getAllMovies(null);
}
/**
* Returns the movie with the specified id.
*
* @param id the id of the movie
* @return the movie with the specified id
* @throws SQLException if a database error occurs
*/
public Movie getMovieById(int id) throws SQLException {
String statement = "select * from movies where id = ?";
PreparedStatement ps = connection.prepareStatement(statement);
ps.setInt(1, id);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
return new Movie(rs.getInt("id"), rs.getString("title"), rs.getInt("year"));
}
// return null if the id does not return a movie.
return null;
}
public List<Movie> getRatingsByYear(int year, String Limitparam, String Votesparam) throws SQLException {
// Is this finished?
List<Movie> movies = new ArrayList<>();
//String statement = "select * from movies where year = ? order by (select rating from ratings where movie_id = (select id from movies where year = ?)) asc";
String statement = "select * from movies inner join ratings on movies.id = ratings.movie_id where movies.year = ? AND ratings.votes > " + Votesparam + " ORDER BY ratings.rating DESC LIMIT " + Limitparam;
PreparedStatement ps = connection.prepareStatement(statement);
ps.setInt(1, year);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
movies.add(new MovieRating(rs.getInt("id"), rs.getString("title"), rs.getInt("year"), rs.getDouble("rating"),rs.getInt("votes")));
}
return movies;
}
public List<Person> getPeopleByMovieId(int id) throws SQLException{
List<Person> people = new ArrayList<>();
String statement = "SELECT * FROM people INNER JOIN stars ON people.id = stars.person_id WHERE stars.movie_id = ?";
PreparedStatement ps = connection.prepareStatement(statement);
ps.setInt(1, id);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
people.add(new Person(rs.getInt("id"), rs.getString("name"), rs.getString("birth")));
}
return people;
}
}
package com.flickfinder.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import com.flickfinder.util.Database;
import com.flickfinder.model.Movie;
import com.flickfinder.model.Person;
/**
* TODO: Implement this class
*
*/
public class PersonDAO {
private final Connection connection;
// for the must have requirements, you will need to implement the following
// methods:
// - getAllPeople()
// - getPersonById(int id)
// you will add further methods for the more advanced tasks; however, ensure your have completed
// the must have requirements before you start these.
public PersonDAO() {
Database database = Database.getInstance();
connection = database.getConnection();
}
public List<Person> getAllPeople(String params) throws SQLException {
String temp = params;
if (temp == null) {
temp = "50";
}
List<Person> people = new ArrayList<>();
Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery("select * from people LIMIT "+ temp);
while (rs.next()) {
people.add(new Person(rs.getInt("id"), rs.getString("name"), rs.getString("birth")));
}
return people;
}
public List<Person> getAllPeople() throws SQLException {
return this.getAllPeople(null);
}
public Person getPersonById(int id) throws SQLException{
String statement = "select * from people where id = ?";
PreparedStatement ps = connection.prepareStatement(statement);
ps.setInt(1, id);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
return new Person(rs.getInt("id"), rs.getString("name"), rs.getString("birth"));
}
return null;
}
public List<Movie> getMoviesStarringPerson(int id) throws SQLException {
// Is this finished?
List<Movie> movies = new ArrayList<>();
//String statement = "select * from movies where id = (select movie_id from stars where person_id = ?)";
String statement = "select * from movies inner join stars on movies.id = stars.movie_id where stars.person_id = ?";
PreparedStatement ps = connection.prepareStatement(statement);
ps.setInt(1, id);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
movies.add(new Movie(rs.getInt("id"), rs.getString("title"), rs.getInt("year")));
}
return movies;
}
}
package com.flickfinder.model;
/**
* Represents a movie with its unique identifier, title, and release year.
*/
public class Movie {
private int id;
private String title;
private int year;
/**
* Constructs a Movie object with the specified id, title, and year.
*
* @param id the unique identifier of the movie
* @param title the title of the movie
* @param year the release year of the movie
*/
public Movie(int id, String title, int year) {
this.id = id;
this.title = title;
this.year = year;
}
/**
* Returns the unique identifier of the movie.
*
* @return the id of the movie
*/
public int getId() {
return id;
}
/**
* Sets the unique identifier of the movie.
*
* @param id the id to set
*/
public void setId(int id) {
this.id = id;
}
/**
* Returns the title of the movie.
*
* @return the title of the movie
*/
public String getTitle() {
return title;
}
/**
* Sets the title of the movie.
*
* @param title the title to set
*/
public void setTitle(String title) {
this.title = title;
}
/**
* Returns the release year of the movie.
*
* @return the release year of the movie
*/
public int getYear() {
return year;
}
/**
* Sets the release year of the movie.
*
* @param year the release year to set
*/
public void setYear(int year) {
this.year = year;
}
@Override
public String toString() {
return "Movie [id=" + id + ", title=" + title + ", year=" + year+ "]";
}
}
\ No newline at end of file
package com.flickfinder.model;
public class MovieRating extends Movie{
double rating;
int votes;
public MovieRating(int id, String title, int year, double rating, int votes) {
super(id, title, year);
this.rating = rating;
this.votes = votes;
}
public Double getRating() {
return this.rating;
}
public void setRating(double rating) {
this.rating = rating;
}
public Integer getVotes() {
return this.votes;
}
public void setVotes(int votes) {
this.votes = votes;
}
}
package com.flickfinder.model;
/**
* A person in the movie database.
*
* @TODO: Implement this class
*/
public class Person {
// - Add your code here: use the MovieDAO.java as an example
// - Check the ERD and database schema in the docs folder
// (./docs/database_schema.md) to ensure each column in the People table
// has an attribute in the model. (DELETE THIS COMMENT WHEN DONE)
private int id;
private String name;
private String DOB;
public Person(int id, String name, String DOB) {
this.id = id;
this.name = name;
this.DOB = DOB;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDOB() {
return DOB;
}
public void setDOB(String dOB) {
DOB = dOB;
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", DOB=" + DOB + "]";
}
}
package com.flickfinder.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* A class to handle the database connection.
* You should not need to modify this class.
*
*/
public class Database {
/**
* The instance of the database.
*/
private static Database instance;
/**
* The connection to the database.
* This is optional as we can also
* pass in a connection to the database.
*/
Connection connection;
private Database(String path) {
try {
this.connection = DriverManager.getConnection(path);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* This allows use to pass in an existing database connection.
* This is useful for testing.
*
* @param connection
*/
private Database(Connection connection) {
this.connection = connection;
}
/**
* Returns the instance of the database.
* We pass in the path to the database.
* This is the path to the database file.
*
* @param path
* @return
*/
public static Database getInstance(String path) {
if (instance == null) {
instance = new Database(path);
}
return instance;
}
/**
* Returns the instance of the database. However, this methods allows us
* to pass in the connection to an instance of an in-memory database.
* We use this for testing. Notice how we are using the same method name, overloading the above
* method.
*
* @param conn
* @return
*/
public static Database getInstance(Connection conn) {
instance = new Database(conn);
return instance;
}
/**
* Returns the instance of the database.
* notice how this method is static and does not take any parameters.
* This is because if we have already set the instance of the database, we
* can just return the instance. There is no need to pass in the path or connection to the database.
*
*
* @return
*/
public static Database getInstance() {
/**
* If the instance is null, we throw an IllegalStateException.
* This is because we need to set the instance of the database before we can use it.
* As we are using a singleton pattern, we only have to do this once.
*/
if (instance == null) {
throw new IllegalStateException("Database instance not set");
}
return instance;
}
/**
* Returns the connection to the database.
*
* @return
*/
public Connection getConnection() {
return this.connection;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API Documentation</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
.route {
margin-bottom: 10px;
}
.route h3 {
margin: 0;
color: #333;
}
.route p {
margin: 5px 0;
color: #666;
}
</style>
</head>
<body>
<h1>API Documentation</h1>
<div class="route">
<h3><a href="/movies"> GET /movies </a></h3>
<p>Returns all movies.</p>
</div>
<div class="route">
<h3><a href="/movies/63018"> GET /movies/{id} </a></h3>
<p>Returns a movie by its ID.</p>
</div>
<div class="route">
<h3><a href="/movies/407887/stars"> GET /movies/{id}/stars </a></h3>
<p>Returns all stars of a movie</p>
</div>
<div class="route">
<h3><a href="/movies/ratings/1999"> GET /movies/ratings/{year} </a></h3>
<p>Retrieves a list of movies ordered by rating in descending order, for a given year</p>
</div>
<div class="route">
<h3> <a href="/people">GET /people </a></h3>
<p>Returns a list of people</p>
</div>
<div class="route">
<h3><a href="/people/138"> GET /people/{id} </a></h3>
<p>Returns a person by their ID.</p>
</div>
<div class="route">
<h3><a href="/people/138/movies">GET /people/{id}/movies </a></h3>
<p>Returns all movies of a person</p>
</div>
</body>
</html>
\ No newline at end of file
package com.flickfinder;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItems;
import org.hamcrest.collection.HasItemInArray;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.flickfinder.util.Database;
import com.flickfinder.util.Seeder;
import io.javalin.Javalin;
/**
* These are our integration tests.
* We are testing the application as a whole, including the database.
*/
class IntegrationTests {
/**
* The Javalin app.*
*/
Javalin app;
/**
* The seeder object.
*/
Seeder seeder;
/**
* The port number. Try and use a different port number from your main
* application.
*/
int port = 6000;
/**
* The base URL for our test application.
*/
String baseURL = "http://localhost:" + port;
/**
* Bootstraps the application before each test.
*/
@BeforeEach
void setUp() {
var url = "jdbc:sqlite::memory:";
seeder = new Seeder(url);
Database.getInstance(seeder.getConnection());
app = AppConfig.startServer(port);
}
/**
* Test that the application retrieves a list of all movies.
* Notice how we are checking the actual content of the list.
* At this higher level, we are not concerned with the implementation details.
*/
@Test
void retrieves_a_list_of_all_movies() {
given().when().get(baseURL + "/movies").then().assertThat().statusCode(200). // Assuming a successful
// response returns HTTP
// 200
body("id", hasItems(1, 2, 3, 4, 5))
.body("title", hasItems("The Shawshank Redemption", "The Godfather",
"The Godfather: Part II", "The Dark Knight", "12 Angry Men"))
.body("year", hasItems(1994, 1972, 1974, 2008, 1957));
}
@Test
void retrieves_a_single_movie_by_id() {
given().when().get(baseURL + "/movies/1").then().assertThat().statusCode(200). // Assuming a successful
// response returns HTTP
// 200
body("id", equalTo(1))
.body("title", equalTo("The Shawshank Redemption"))
.body("year", equalTo(1994));
}
@Test
void retrieves_movies_by_person_id() {
given().when().get(baseURL + "/people/4/movies").then().assertThat().statusCode(200). // Assuming a successful
body("id", hasItems(2,3))
.body("title", hasItems("The Godfather", "The Godfather: Part II"))
.body("year", hasItems(1972, 1974));
}
@Test
void retrieves_movies_by_year_sorted() {
given().when().get(baseURL + "/movies/ratings/2008").then().assertThat().statusCode(200). // Assuming a successful
body("id", hasItems(4))
.body("title", hasItems("The Dark Knight"))
.body("year", hasItems(2008));
}
@Test
void retrieves_stars_by_movieid() {
given().when().get(baseURL + "/movies/1/stars").then().assertThat().statusCode(200). // Assuming a successful
// response returns HTTP
// 200
body("id", hasItems(1, 2))
.body("name", hasItems("Tim Robbins", "Morgan Freeman"))
.body("dob", hasItems("1958-10-16", "1937-06-01"));
}
@Test
void retrieves_a_list_of_all_people() {
given().when().get(baseURL + "/people").then().assertThat().statusCode(200). // Assuming a successful
// response returns HTTP
// 200
body("id", hasItems(1, 2, 3, 4, 5))
.body("name", hasItems("Tim Robbins", "Morgan Freeman",
"Christopher Nolan", "Al Pacino", "Henry Fonda"))
.body("dob", hasItems("1958-10-16", "1937-06-01", "1970-07-30", "1940-04-25", "1905-05-16"));
}
@Test
void retrieves_a_single_person_by_id() {
given().when().get(baseURL + "/people/1").then().assertThat().statusCode(200). // Assuming a successful
body("id", equalTo(1))
.body("name", equalTo("Tim Robbins"))
.body("dob", equalTo("1958-10-16"));
}
@Test
void listofmovieswithlimit(){
given().when().get(baseURL + "/movies?limit=2").then().assertThat().statusCode(200). // Assuming a successful
body("id", hasItems(1, 2))
.body("title", hasItems("The Shawshank Redemption", "The Godfather"))
.body("year", hasItems(1994, 1972));
}
@Test
void listofallpeoplewithlimit(){
given().when().get(baseURL + "/people?limit=2").then().assertThat().statusCode(200). // Assuming a successful
body("id", hasItems(1, 2))
.body("name", hasItems("Tim Robbins", "Morgan Freeman"))
.body("dob", hasItems("1958-10-16", "1937-06-01"));
}
@Test
void yearwithlimit(){
given().when().get(baseURL + "/movies/ratings/1972?limit=1").then().assertThat().statusCode(200). // Assuming a successful
body("id", hasItems(2))
.body("title", hasItems("The Godfather"))
.body("year", hasItems(1972));
}
//Should be empty
@Test
void yearwithvote(){
given().when().get(baseURL + "/movies/ratings/1994?votes=2200001").then().assertThat().statusCode(200). // Assuming a successful
body("id", hasItems())
.body("title", hasItems())
.body("year", hasItems());
}
/**
* Tears down the application after each test.
* We want to make sure that each test runs in isolation.
*/
@AfterEach
void tearDown() {
seeder.closeConnection();
app.stop();
}
}
package com.flickfinder.controller;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.sql.SQLException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.flickfinder.dao.MovieDAO;
import io.javalin.http.Context;
/**
* Test for the Movie Controller.
*/
class MovieControllerTest {
/**
*
* The context object, later we will mock it.
*/
private Context ctx;
/**
* The movie data access object.
*/
private MovieDAO movieDAO;
/**
* The movie controller.
*/
private MovieController movieController;
@BeforeEach
void setUp() {
// We create a mock of the MovieDAO class.
movieDAO = mock(MovieDAO.class);
// We create a mock of the Context class.
ctx = mock(Context.class);
// We create an instance of the MovieController class and pass the mock object
movieController = new MovieController(movieDAO);
}
/**
* Tests the getAllMovies method.
* We expect to get a list of all movies in the database.
*/
@Test
void testGetAllMovies() {
movieController.getAllMovies(ctx);
try {
verify(movieDAO).getAllMovies(null);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* Test that the controller returns a 500 status code when a database error
* occurs
*
* @throws SQLException
*/
@Test
void testThrows500ExceptionWhenGetAllDatabaseError() throws SQLException {
when(movieDAO.getAllMovies(null)).thenThrow(new SQLException());
movieController.getAllMovies(ctx);
verify(ctx).status(500);
}
/**
* Tests the getMovieById method.
* We expect to get the movie with the specified id.
*/
@Test
void testGetMovieById() {
when(ctx.pathParam("id")).thenReturn("1");
movieController.getMovieById(ctx);
try {
verify(movieDAO).getMovieById(1);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* Test a 500 status code is returned when a database error occurs.
*
* @throws SQLException
*/
@Test
void testThrows500ExceptionWhenGetByIdDatabaseError() throws SQLException {
when(ctx.pathParam("id")).thenReturn("1");
when(movieDAO.getMovieById(1)).thenThrow(new SQLException());
movieController.getMovieById(ctx);
verify(ctx).status(500);
}
/**
* Test that the controller returns a 404 status code when a movie is not found
* or
* database error.
*
* @throws SQLException
*/
@Test
void testThrows404ExceptionWhenNoMovieFound() throws SQLException {
when(ctx.pathParam("id")).thenReturn("1");
when(movieDAO.getMovieById(1)).thenReturn(null);
movieController.getMovieById(ctx);
verify(ctx).status(404);
}
}
\ No newline at end of file
package com.flickfinder.controller;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.sql.SQLException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.flickfinder.dao.PersonDAO;
import io.javalin.http.Context;
class PersonControllerTest {
private Context ctx;
private PersonDAO personDAO;
private PersonController personController;
@BeforeEach
void setUp() {
personDAO = mock(PersonDAO.class);
ctx = mock(Context.class);
personController = new PersonController(personDAO);
}
@Test
void testGetAllPeople() {
personController.getAllPeople(ctx);
try {
verify(personDAO).getAllPeople(null);
} catch (SQLException e) {
e.printStackTrace();
}
}
@Test
void testThrows500ExceptionWhenGetAllDatabaseError() throws SQLException {
when(personDAO.getAllPeople(null)).thenThrow(new SQLException());
personController.getAllPeople(ctx);
verify(ctx).status(500);
}
@Test
void testGetMovieById() {
when(ctx.pathParam("id")).thenReturn("1");
personController.getPersonById(ctx);
try {
verify(personDAO).getPersonById(1);
} catch (SQLException e) {
e.printStackTrace();
}
}
@Test
void testThrows500ExceptionWhenGetByIdDatabaseError() throws SQLException {
when(ctx.pathParam("id")).thenReturn("1");
when(personDAO.getPersonById(1)).thenThrow(new SQLException());
personController.getPersonById(ctx);
verify(ctx).status(500);
}
@Test
void testThrows404ExceptionWhenNoPersonFound() throws SQLException {
when(ctx.pathParam("id")).thenReturn("3");
when(personDAO.getPersonById(3)).thenReturn(null);
personController.getPersonById(ctx);
verify(ctx).status(404);
}
}
\ No newline at end of file
package com.flickfinder.dao;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import java.sql.SQLException;
import java.util.List;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.flickfinder.model.Movie;
import com.flickfinder.util.Database;
import com.flickfinder.util.Seeder;
/**
* Test for the Movie Data Access Object.
* This uses an in-memory database for testing purposes.
*/
class MovieDAOTest {
/**
* The movie data access object.
*/
private MovieDAO movieDAO;
/**
* Seeder
*/
Seeder seeder;
/**
* Sets up the database connection and creates the tables.
* We are using an in-memory database for testing purposes.
* This gets passed to the Database class to get a connection to the database.
* As it's a singleton class, the entire application will use the same
* connection.
*/
@BeforeEach
void setUp() {
var url = "jdbc:sqlite::memory:";
seeder = new Seeder(url);
Database.getInstance(seeder.getConnection());
movieDAO = new MovieDAO();
}
/**
* Tests the getAllMovies method.
* We expect to get a list of all movies in the database.
* We have seeded the database with 5 movies, so we expect to get 5 movies back.
* At this point, we avoid checking the actual content of the list.
*/
@Test
void testGetAllMovies() {
try {
List<Movie> movies = movieDAO.getAllMovies();
assertEquals(5, movies.size());
} catch (SQLException e) {
fail("SQLException thrown");
e.printStackTrace();
}
}
/**
* Tests the getMovieById method.
* We expect to get the movie with the specified id.
*/
@Test
void testGetMovieById() {
Movie movie;
try {
movie = movieDAO.getMovieById(1);
assertEquals("The Shawshank Redemption", movie.getTitle());
} catch (SQLException e) {
fail("SQLException thrown");
e.printStackTrace();
}
}
/**
* Tests the getMovieById method with an invalid id. Null should be returned.
*/
@Test
void testGetMovieByIdInvalidId() {
// write an assertThrows for a SQLException
try {
Movie movie = movieDAO.getMovieById(1000);
assertEquals(null, movie);
} catch (SQLException e) {
fail("SQLException thrown");
e.printStackTrace();
}
}
@AfterEach
void tearDown() {
seeder.closeConnection();
}
}
\ No newline at end of file
package com.flickfinder.dao;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import java.sql.SQLException;
import java.util.List;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.flickfinder.model.Movie;
import com.flickfinder.model.Person;
import com.flickfinder.util.Database;
import com.flickfinder.util.Seeder;
/**
* TODO: Implement this class
*/
class PersonDAOTest {
private PersonDAO personDAO;
Seeder seeder;
@BeforeEach
void setUp() {
var url = "jdbc:sqlite::memory:";
seeder = new Seeder(url);
Database.getInstance(seeder.getConnection());
personDAO = new PersonDAO();
}
@Test
void testGetAllPeople() {
try {
List<Person> people = personDAO.getAllPeople();
assertEquals(5, people.size());
} catch (SQLException e) {
fail("SQLException thrown");
e.printStackTrace();
}
}
@Test
void testGetPersonById() {
Person person;
try {
person = personDAO.getPersonById(2);
assertEquals("Morgan Freeman", person.getName());
} catch (SQLException e) {
fail("SQLException thrown");
e.printStackTrace();
}
}
@Test
void testGetPersonByIdInvalidId() {
// write an assertThrows for a SQLException
try {
Person person = personDAO.getPersonById(10000);
assertEquals(null, person);
} catch (SQLException e) {
fail("SQLException thrown");
e.printStackTrace();
}
}
@AfterEach
void tearDown() {
seeder.closeConnection();
}
}
\ No newline at end of file
package com.flickfinder.model;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* Test for the Movie Model.
*
*
*/
public class MovieTest {
/**
* The movie object to be tested.
*/
private Movie movie;
/**
* Set up the movie object before each test.
*
*/
@BeforeEach
public void setUp() {
movie = new Movie(1, "The Matrix", 1999);
}
/**
* Test the movie object is created with the correct values.
*/
@Test
public void testMovieCreated() {
assertEquals(1, movie.getId());
assertEquals("The Matrix", movie.getTitle());
assertEquals(1999, movie.getYear());
}
/**
* Test the movie object is created with the correct values.
*/
@Test
public void testMovieSetters() {
movie.setId(2);
movie.setTitle("The Matrix Reloaded");
movie.setYear(2003);
assertEquals(2, movie.getId());
assertEquals("The Matrix Reloaded", movie.getTitle());
assertEquals(2003, movie.getYear());
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment