Skip to content

Commit

Permalink
Upload
Browse files Browse the repository at this point in the history
  • Loading branch information
LayoutXML authored Apr 30, 2021
0 parents commit f3d37d7
Show file tree
Hide file tree
Showing 49 changed files with 1,003 additions and 0 deletions.
191 changes: 191 additions & 0 deletions Documentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
# rent-information-service

## Authentication
One user is available.

**Username**: `rokas`

**Password**: `rokas1`

## API calls
### List products
Fetches products available to rent.

* **URL**

/api/v1/products

* **Method**

`GET`

* **URL Params**

N/A

* **Data Params**

N/A

* **Success Response**

* **Code**: 200 OK
* **Content**: `[{"id":1,"title":"Skis"},{"id":2,"title":"Snowboard"},...]`


* **Error Response**

* **Code**: 401 UNAUTHORIZED


* **Sample Call**

```
curl --location --request GET 'http://localhost:8080/api/v1/products' \
--header 'Authorization: Basic cm9rYXM6cm9rYXMx' \
--header 'Cookie: JSESSIONID=748E420446F0264E69924105E66B699E'
```


### Show product details
Fetches individual product details.

* **URL**

/api/v1/details

* **Method**

`GET`

* **URL Params**

**Required**:

``id=[integer]``

* **Data Params**

N/A

* **Success Response**

* **Code**: 200 OK
* **Content**: `{"id":1,"title":"Skis","commitmentMonths":[0,3,6]}`


* **Error Response**

* **Code**: 400 BAD_REQUEST

* **Code**: 401 UNAUTHORIZED

* **Code**: 404 NOT_FOUND


* **Sample Call**

```
curl --location --request GET 'http://localhost:8080/api/v1/details?id=1' \
--header 'Authorization: Basic cm9rYXM6cm9rYXMx' \
--header 'Cookie: JSESSIONID=748E420446F0264E69924105E66B699E'
```


### Delete product
Deletes a product.

* **URL**

/api/v1/delete

* **Method**

`GET`

* **URL Params**

**Required**:

``id=[integer]``

* **Data Params**

N/A

* **Success Response**

* **Code**: 200 OK


* **Error Response**

* **Code**: 400 BAD_REQUEST

* **Code**: 401 UNAUTHORIZED

* **Code**: 404 NOT_FOUND


* **Sample Call**

```
curl --location --request GET 'http://localhost:8080/api/v1/delete?id=1' \
--header 'Authorization: Basic cm9rYXM6cm9rYXMx' \
--header 'Cookie: JSESSIONID=A3E8A8255D27A1501BAF8BBCA9157DEA'
```


### Calculate order price
Fetches order price depending on product, commitment, months of return.

* **URL**

/api/v1/price

* **Method**

`POST`

* **URL Params**

N/A

* **Data Params**

JSON format.

**Required**:

`"id":[integer]`

**Optional**:

`"commitment":[integer]`. Must be one of {3, 6}.

`"returnMonths":[integer]`. Must be greater than or equal to 0.

* **Success Response**

* **Code**: 200 OK
* **Content**: `70.0`


* **Error Response**

* **Code**: 400 BAD_REQUEST

* **Code**: 401 UNAUTHORIZED

* **Code**: 404 NOT_FOUND


* **Sample Call**

```
curl --location --request POST 'http://localhost:8080/api/v1/price' \
--header 'Authorization: Basic cm9rYXM6cm9rYXMx' \
--header 'Content-Type: application/json' \
--header 'Cookie: JSESSIONID=748E420446F0264E69924105E66B699E' \
--data-raw '{"id":1,"commitment":3,"returnMonths":1}'
```
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Rental Store Mock API
REST API made with Spring Boot (web, JPA, validation, cache, security), H2 database, Flyway migration, Docker, JUnit and Mockito tests, Lombok. Built on Java 11.

## Use cases
* API users can get a list of available equipment.
* API users can choose a single product and get its details and pricing options.
* API users can delete a product.
* API users can calculate the total price for the chosen product, commitment and rental period.

Each use case corresponds to an endpoint. Technical details and specifications of each can be found in the Documentation.md file.

## Features

* REST API endpoints for each use case mentioned above.
* Spring Boot Validation for these endpoints.
* Error codes for invalid, unauthorized or incorrect data.
* Persistent H2 database.
* Flyway database migrations for setting up and filling database tables.
* Basic Spring Security with a single user.
* Basic Spring Caching for data retrieval and cache eviction upon product deletion.
* Docker script for running the generated JAR file.
* JUnit unit tests for price calculations mentioned below. Mockito for mocking the database for these tests.

## Sample data

| Product | Price/month without commitment| Price/month for 3 month commitment | Price/month for 6 month commitment | Initial charge | Available for rent
|---:|---:|---:|---:|---:|---
|Skis|$35|$30|$25|$35|Yes
|Snowboard|$25|$20|$17|$25|Yes
|Bike|$35|$30|$25|$35|No
|Roller-blades|$17|$13|$10|$17|Yes
|Skateboard|$35|$30|$25|$35|Yes

## Pricing formula

Commitment is a number of months that the customer chooses to rent the equipment for. It can either be 3, 6 months or no commitment. Regardless of commitment, a customer can choose to return the product at a different time in which case they would pay no commitment price.

Pricing formula is as follows:

*total price = initial charge + (return months * price per month, based on commitment)*

For example:
* User chooses a skateboard with a commitment of 6 months and chooses to return it after 2 months:
* price = $35 initial charge + (2 months * $35 no commitment price) = $105


* User chooses roller-blades without a commitment and chooses to return it after 7 months:
* price = $17 initial charge + (7 months * $17 no commitment price) = $136
4 changes: 4 additions & 0 deletions dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM openjdk:11-jdk-slim
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
95 changes: 95 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/>
</parent>
<groupId>dev.rokas.task.backend</groupId>
<artifactId>rent-information-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>rent-information-service</name>
<description>Boilerplate for backend application</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package dev.rokas.task.backend;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@EnableCaching
@SpringBootApplication
public class RentInformationApplication {

public static void main(String[] args) {
SpringApplication.run(RentInformationApplication.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package dev.rokas.task.backend.api.v1.auth;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests().anyRequest().authenticated()
.and().headers().frameOptions().disable()
.and().httpBasic();
}

@Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.inMemoryAuthentication()
.withUser("rokas")
.password(getPasswordEncoder().encode("rokas1"))
.authorities("user");
}

@Bean
public PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}

}
Loading

0 comments on commit f3d37d7

Please sign in to comment.