Skip to content

Commit

Permalink
Merge pull request #9 from iplantemn/feature/rest-api
Browse files Browse the repository at this point in the history
Feature/rest api
  • Loading branch information
iplantemn authored Aug 28, 2020
2 parents ce252d7 + ab8dcca commit e576b7c
Show file tree
Hide file tree
Showing 12 changed files with 439 additions and 34 deletions.
120 changes: 90 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,98 @@
# imdb-movies-api
Movies API for the IMDB SmartThings project
Movies API for the IMDB SmartThings project.

## Building
Movies API is a Spring Boot application backed by a MySQL database.

| Operating System | Command |
| :--------------- | :---------------- |
| macOS | `./gradlew build` |
| Windows | `gradlew build` |
This API leverages a few packages on top of Spring Boot for ease of development:
* **Project Lombok**: provides annotations to replace boilerplate code in POJOs, such as constructors, getters & setters, etc.
* **Spring Data REST**: automagically implements a fully-fledged RESTful API based on a data `Repository` with simple annotations.
This REST API supports sorting & pagination out of the box.

## Deploying to Docker
## Build

Once you've built the application, navigate to the root of the project and run one of these commands:
Movies API uses the Gradle build tool. It is recommended to use the Gradle Wrapper to ensure cross-platform compatibility and enforce
a validated version.

* For macOS: `./gradlew <task>`
* For Windows: `gradlew <task>`

Here are the various tasks and how to use them:

| Gradle task | Description |
| :-------------- | :------------------------------------------------------------------------------------------------------------------------------------------- |
| `build` | Complete build task - compiles `main` and `test`, runs the test suite and validate coverage, analyzes code with `PMD`, and builds the `.jar` |
| `test` | Compiles the test code, and runs the test suite, validates coverage, and performs `PMD` analysis. |
| `pmdMain` | Analyzes `main` code with `PMD`. |
| `pmdTest` | Analyzes `test` code with `PMD`. |

You can additionally exclude some parts of a task with the `-x` flag. For example, `gradlew build -x test` will exclude all parts of the `test` task.

## Deployments

**Warning**: The first deployment of the application will be slower than the subsequent deployments, because the application will seed the database.

### Prerequisites

Before you can deploy the application:
1. the executable `.jar` must be generated by running the `build` Gradle task
1. the database must be deployed to Docker

### Database

The database is only meant to be deployed to Docker. The `Dockerfile` and a SQL script to generate the schema are located in `.docker/`.
To deploy the MySQL database to Docker:
1. Ensure that Docker is running on your system
1. From the root of the imdb-movies-api project, run the following command:
```shell script
docker-compose up --build # build image & launch a containers for database and service
docker-compose up movies-db --build # build image & launch container for database only
docker-compose up movies-db --build
```

## API versioning

| Version | Description |
| -------- | :---------- |
| `api/v1` | Completely implemented with Spring Data REST. |


## TODO
1. ~~Dockerize app and database~~
1. ~~Implement Spring Data REST~~
1. Insert seed data into database
1. ~~Entity - fetch genres & studio~~
1. Set to api/v1/xyz
1. Implement Swagger & document
1. Create Postman collection
1. Integration tests
1. Implement linter
1. Jacoco coverage
1. ~~Use Lombok for entity~~
1. ArchUnit tests
### Local API deployment

Once you have executed the Gradle `build` task and deployed the MySQL database to Docker, you can deploy the Spring Boot application as such:

```shell script
java -Dspring.profiles.active=local -jar ./build/libs/movies-<version>-.jar
```

### Docker API deployment

Once you have executed the Gradle `build` task, you can deploy the Spring Boot application as such:

```shell script

docker-compose up movies-api --build # if the database is already deployed
```

## Usage

The application is accessible at the following base URLs:

| Environment | Base URL |
| :---------- | :-------------------- |
| Local | https://localhost:8080 |
| Docker | https://localhost:5012 |

### API Docs

#### Postman

For easy interaction with the API, you can import the Postman collection in `./postman`.

#### HATEOAS

Spring Data REST publishes its data using HATEOAS, which uses the API responses and hypermedia to deliver a self-
documenting API. You can start with:

```shell script
curl -X GET https://localhost:8080/api/v1/movies # Local
curl -X GET https://localhost:5012/api/v1/movies # Docker
```

#### Spring Boot Actuator

You can also use the Spring Boot Actuator mappings endpoint to get a list of endpoints:

```shell script
curl -X GET 'https://localhost:8080/actuator/mappings' # Local
curl -X GET 'https://localhost:5012/actuator/mappings' # Docker
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ repositories {
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-rest'
implementation 'org.springframework.boot:spring-boot-starter-hateoas'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.liquibase:liquibase-core'
implementation 'org.apache.commons:commons-lang3:3.0'
Expand Down
11 changes: 9 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,19 @@ services:
- "5012:8080"
expose:
- "5012"
networks:
- imdb
movies-db:
build: ./.docker/movies-db
command: ['mysqld']
command: [ 'mysqld' ]
environment:
- MYSQL_ROOT_PASSWORD=NBLNKpr7FYbdRgkXmmP8
ports:
- "3308:3306"
expose:
- "3308"
- "3308"
networks:
- imdb
networks:
imdb:
external: true
107 changes: 107 additions & 0 deletions postman/Movies%20API.postman_collection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
{
"variables": [],
"info": {
"name": "Movies API",
"_postman_id": "3ba62dc1-2ca5-5012-75be-3b5dc4d29831",
"description": "",
"schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json"
},
"item": [
{
"name": "GET /movies",
"request": {
"url": "https://localhost:5012/api/v1/movies?size=20&page=0&sort=title=desc",
"method": "GET",
"header": [],
"body": {
"mode": "formdata",
"formdata": []
},
"description": ""
},
"response": []
},
{
"name": "GET /movies by ID",
"request": {
"url": "https://localhost:5012/api/v1/movies/{{movieId}}",
"method": "GET",
"header": [],
"body": {
"mode": "formdata",
"formdata": []
},
"description": "Replace {{movieId}} with an actual ID"
},
"response": []
},
{
"name": "GET /movies/ID/cast",
"request": {
"url": "https://localhost:5012/api/v1/movies/{{movieId}}/cast?size=20&page=0&sort=title=desc",
"method": "GET",
"header": [],
"body": {
"mode": "formdata",
"formdata": []
},
"description": "Replace {{movieId}} with an actual ID"
},
"response": []
},
{
"name": "GET /movies/search/cast",
"request": {
"url": "https://localhost:5013/api/v1/movies/search/castId?castId={{castId}}",
"method": "GET",
"header": [],
"body": {
"mode": "formdata",
"formdata": []
},
"description": "Replace {{castId}} with an actual ID"
},
"response": []
},
{
"name": "POST /movies",
"request": {
"url": "https://localhost:5012/api/v1/movies",
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"description": ""
}
],
"body": {
"mode": "raw",
"raw": "{\n\t\"title\": \"The Hitchhiker's Guide to the Galaxy\",\n\t\"lengthMinutes\": 109,\n\t\"releaseDate\": \"2005-04-29\",\n\t\"genres\": [\"Adventure\", \"Comedy\", \"Sci Fi\"],\n\t\"studios\": [\"Touchstone Pictures\", \"Spyglass Entertainment\"],\n\t\"synopsis\": \"Mere seconds before the Earth is to be demolished by an alien construction crew, journeyman Arthur Dent is swept off the planet by his friend Ford Prefect, a researcher penning a new edition of The Hitchhiker's Guide to the Galaxy.\"\n}"
},
"description": ""
},
"response": []
},
{
"name": "PATCH /movies by ID",
"request": {
"url": "https://localhost:5012/api/v1/movies/{{movieId}}",
"method": "PATCH",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"description": ""
}
],
"body": {
"mode": "raw",
"raw": "{\n\t\"synopsis\": \"42\"\n}"
},
"description": "Replace {{movieId}} with an actual ID"
},
"response": []
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

@SpringBootApplication
@EnableConfigurationProperties
public class MovieApiApplication {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.iplante.imdb.movies.configuration;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
* Configuration object for the Cast API.
*
* @author Isabelle Plante
* @version 1
* @since 8/27/20
*/
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "cast-api")
public class CastApiConfiguration {

/**
* The base URL of the Cast API.
*/
private String baseUrl;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.iplante.imdb.movies.configuration;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import java.util.Collections;

/**
* Configure {@link RestTemplate} to deserialize HAL JSON automatically. This is necessary because
* Spring Data REST uses HATEOAS delivers data through hypermedia.
* <p>
* See: <a href="https://gist.github.com/ripla/6f1516e3d0c28f4d591303d4060342d4">Source</a>
*
* @author Isabelle Plante
* @version 1
* @since 8/27/20
*/
@Configuration
public class RestConfiguration {

/**
* Configure {@link RestTemplate} to support HAL JSON.
*
* @param restTemplateBuilder the builder.
* @return configured {@link RestTemplate}.
*/
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
final var objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.registerModule(new Jackson2HalModule());

final var messageConverter = new MappingJackson2HttpMessageConverter();
messageConverter.setSupportedMediaTypes(Collections.singletonList(MediaTypes.HAL_JSON));
messageConverter.setObjectMapper(objectMapper);

return restTemplateBuilder.messageConverters(messageConverter).build();
}
}
Loading

0 comments on commit e576b7c

Please sign in to comment.