Skip to content

Commit f3d37d7

Browse files
authored
Upload
0 parents  commit f3d37d7

File tree

49 files changed

+1003
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1003
-0
lines changed

Documentation.md

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
# rent-information-service
2+
3+
## Authentication
4+
One user is available.
5+
6+
**Username**: `rokas`
7+
8+
**Password**: `rokas1`
9+
10+
## API calls
11+
### List products
12+
Fetches products available to rent.
13+
14+
* **URL**
15+
16+
/api/v1/products
17+
18+
* **Method**
19+
20+
`GET`
21+
22+
* **URL Params**
23+
24+
N/A
25+
26+
* **Data Params**
27+
28+
N/A
29+
30+
* **Success Response**
31+
32+
* **Code**: 200 OK
33+
* **Content**: `[{"id":1,"title":"Skis"},{"id":2,"title":"Snowboard"},...]`
34+
35+
36+
* **Error Response**
37+
38+
* **Code**: 401 UNAUTHORIZED
39+
40+
41+
* **Sample Call**
42+
43+
```
44+
curl --location --request GET 'http://localhost:8080/api/v1/products' \
45+
--header 'Authorization: Basic cm9rYXM6cm9rYXMx' \
46+
--header 'Cookie: JSESSIONID=748E420446F0264E69924105E66B699E'
47+
```
48+
49+
50+
### Show product details
51+
Fetches individual product details.
52+
53+
* **URL**
54+
55+
/api/v1/details
56+
57+
* **Method**
58+
59+
`GET`
60+
61+
* **URL Params**
62+
63+
**Required**:
64+
65+
``id=[integer]``
66+
67+
* **Data Params**
68+
69+
N/A
70+
71+
* **Success Response**
72+
73+
* **Code**: 200 OK
74+
* **Content**: `{"id":1,"title":"Skis","commitmentMonths":[0,3,6]}`
75+
76+
77+
* **Error Response**
78+
79+
* **Code**: 400 BAD_REQUEST
80+
81+
* **Code**: 401 UNAUTHORIZED
82+
83+
* **Code**: 404 NOT_FOUND
84+
85+
86+
* **Sample Call**
87+
88+
```
89+
curl --location --request GET 'http://localhost:8080/api/v1/details?id=1' \
90+
--header 'Authorization: Basic cm9rYXM6cm9rYXMx' \
91+
--header 'Cookie: JSESSIONID=748E420446F0264E69924105E66B699E'
92+
```
93+
94+
95+
### Delete product
96+
Deletes a product.
97+
98+
* **URL**
99+
100+
/api/v1/delete
101+
102+
* **Method**
103+
104+
`GET`
105+
106+
* **URL Params**
107+
108+
**Required**:
109+
110+
``id=[integer]``
111+
112+
* **Data Params**
113+
114+
N/A
115+
116+
* **Success Response**
117+
118+
* **Code**: 200 OK
119+
120+
121+
* **Error Response**
122+
123+
* **Code**: 400 BAD_REQUEST
124+
125+
* **Code**: 401 UNAUTHORIZED
126+
127+
* **Code**: 404 NOT_FOUND
128+
129+
130+
* **Sample Call**
131+
132+
```
133+
curl --location --request GET 'http://localhost:8080/api/v1/delete?id=1' \
134+
--header 'Authorization: Basic cm9rYXM6cm9rYXMx' \
135+
--header 'Cookie: JSESSIONID=A3E8A8255D27A1501BAF8BBCA9157DEA'
136+
```
137+
138+
139+
### Calculate order price
140+
Fetches order price depending on product, commitment, months of return.
141+
142+
* **URL**
143+
144+
/api/v1/price
145+
146+
* **Method**
147+
148+
`POST`
149+
150+
* **URL Params**
151+
152+
N/A
153+
154+
* **Data Params**
155+
156+
JSON format.
157+
158+
**Required**:
159+
160+
`"id":[integer]`
161+
162+
**Optional**:
163+
164+
`"commitment":[integer]`. Must be one of {3, 6}.
165+
166+
`"returnMonths":[integer]`. Must be greater than or equal to 0.
167+
168+
* **Success Response**
169+
170+
* **Code**: 200 OK
171+
* **Content**: `70.0`
172+
173+
174+
* **Error Response**
175+
176+
* **Code**: 400 BAD_REQUEST
177+
178+
* **Code**: 401 UNAUTHORIZED
179+
180+
* **Code**: 404 NOT_FOUND
181+
182+
183+
* **Sample Call**
184+
185+
```
186+
curl --location --request POST 'http://localhost:8080/api/v1/price' \
187+
--header 'Authorization: Basic cm9rYXM6cm9rYXMx' \
188+
--header 'Content-Type: application/json' \
189+
--header 'Cookie: JSESSIONID=748E420446F0264E69924105E66B699E' \
190+
--data-raw '{"id":1,"commitment":3,"returnMonths":1}'
191+
```

README.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Rental Store Mock API
2+
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.
3+
4+
## Use cases
5+
* API users can get a list of available equipment.
6+
* API users can choose a single product and get its details and pricing options.
7+
* API users can delete a product.
8+
* API users can calculate the total price for the chosen product, commitment and rental period.
9+
10+
Each use case corresponds to an endpoint. Technical details and specifications of each can be found in the Documentation.md file.
11+
12+
## Features
13+
14+
* REST API endpoints for each use case mentioned above.
15+
* Spring Boot Validation for these endpoints.
16+
* Error codes for invalid, unauthorized or incorrect data.
17+
* Persistent H2 database.
18+
* Flyway database migrations for setting up and filling database tables.
19+
* Basic Spring Security with a single user.
20+
* Basic Spring Caching for data retrieval and cache eviction upon product deletion.
21+
* Docker script for running the generated JAR file.
22+
* JUnit unit tests for price calculations mentioned below. Mockito for mocking the database for these tests.
23+
24+
## Sample data
25+
26+
| Product | Price/month without commitment| Price/month for 3 month commitment | Price/month for 6 month commitment | Initial charge | Available for rent
27+
|---:|---:|---:|---:|---:|---
28+
|Skis|$35|$30|$25|$35|Yes
29+
|Snowboard|$25|$20|$17|$25|Yes
30+
|Bike|$35|$30|$25|$35|No
31+
|Roller-blades|$17|$13|$10|$17|Yes
32+
|Skateboard|$35|$30|$25|$35|Yes
33+
34+
## Pricing formula
35+
36+
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.
37+
38+
Pricing formula is as follows:
39+
40+
*total price = initial charge + (return months * price per month, based on commitment)*
41+
42+
For example:
43+
* User chooses a skateboard with a commitment of 6 months and chooses to return it after 2 months:
44+
* price = $35 initial charge + (2 months * $35 no commitment price) = $105
45+
46+
47+
* User chooses roller-blades without a commitment and chooses to return it after 7 months:
48+
* price = $17 initial charge + (7 months * $17 no commitment price) = $136

dockerfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
FROM openjdk:11-jdk-slim
2+
ARG JAR_FILE=target/*.jar
3+
COPY ${JAR_FILE} app.jar
4+
ENTRYPOINT ["java","-jar","/app.jar"]

pom.xml

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<parent>
6+
<groupId>org.springframework.boot</groupId>
7+
<artifactId>spring-boot-starter-parent</artifactId>
8+
<version>2.3.1.RELEASE</version>
9+
<relativePath/>
10+
</parent>
11+
<groupId>dev.rokas.task.backend</groupId>
12+
<artifactId>rent-information-service</artifactId>
13+
<version>0.0.1-SNAPSHOT</version>
14+
<name>rent-information-service</name>
15+
<description>Boilerplate for backend application</description>
16+
<properties>
17+
<java.version>11</java.version>
18+
</properties>
19+
<dependencies>
20+
<dependency>
21+
<groupId>org.springframework.boot</groupId>
22+
<artifactId>spring-boot-starter-actuator</artifactId>
23+
</dependency>
24+
<dependency>
25+
<groupId>org.springframework.boot</groupId>
26+
<artifactId>spring-boot-starter-cache</artifactId>
27+
</dependency>
28+
<dependency>
29+
<groupId>org.springframework.boot</groupId>
30+
<artifactId>spring-boot-starter-data-jpa</artifactId>
31+
</dependency>
32+
<dependency>
33+
<groupId>org.springframework.boot</groupId>
34+
<artifactId>spring-boot-starter-data-rest</artifactId>
35+
</dependency>
36+
<dependency>
37+
<groupId>org.springframework.boot</groupId>
38+
<artifactId>spring-boot-starter-validation</artifactId>
39+
</dependency>
40+
<dependency>
41+
<groupId>org.springframework.boot</groupId>
42+
<artifactId>spring-boot-starter-web</artifactId>
43+
</dependency>
44+
<dependency>
45+
<groupId>org.springframework.boot</groupId>
46+
<artifactId>spring-boot-starter-security</artifactId>
47+
</dependency>
48+
<dependency>
49+
<groupId>org.springframework.security</groupId>
50+
<artifactId>spring-security-core</artifactId>
51+
</dependency>
52+
<dependency>
53+
<groupId>org.flywaydb</groupId>
54+
<artifactId>flyway-core</artifactId>
55+
</dependency>
56+
<dependency>
57+
<groupId>org.projectlombok</groupId>
58+
<artifactId>lombok</artifactId>
59+
<optional>true</optional>
60+
<scope>provided</scope>
61+
</dependency>
62+
63+
<dependency>
64+
<groupId>com.h2database</groupId>
65+
<artifactId>h2</artifactId>
66+
<scope>runtime</scope>
67+
</dependency>
68+
<dependency>
69+
<groupId>org.springframework.boot</groupId>
70+
<artifactId>spring-boot-starter-test</artifactId>
71+
<scope>test</scope>
72+
</dependency>
73+
<dependency>
74+
<groupId>org.springframework.security</groupId>
75+
<artifactId>spring-security-test</artifactId>
76+
<scope>test</scope>
77+
</dependency>
78+
<dependency>
79+
<groupId>org.mockito</groupId>
80+
<artifactId>mockito-all</artifactId>
81+
<version>1.9.5</version>
82+
<scope>test</scope>
83+
</dependency>
84+
</dependencies>
85+
86+
<build>
87+
<plugins>
88+
<plugin>
89+
<groupId>org.springframework.boot</groupId>
90+
<artifactId>spring-boot-maven-plugin</artifactId>
91+
</plugin>
92+
</plugins>
93+
</build>
94+
95+
</project>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package dev.rokas.task.backend;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
import org.springframework.cache.annotation.EnableCaching;
6+
7+
@EnableCaching
8+
@SpringBootApplication
9+
public class RentInformationApplication {
10+
11+
public static void main(String[] args) {
12+
SpringApplication.run(RentInformationApplication.class, args);
13+
}
14+
15+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package dev.rokas.task.backend.api.v1.auth;
2+
3+
import org.springframework.beans.factory.annotation.Autowired;
4+
import org.springframework.context.annotation.Bean;
5+
import org.springframework.context.annotation.Configuration;
6+
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
7+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
8+
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
9+
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
10+
import org.springframework.security.crypto.password.PasswordEncoder;
11+
12+
@Configuration
13+
public class SecurityConfig extends WebSecurityConfigurerAdapter {
14+
15+
@Override
16+
protected void configure(HttpSecurity http) throws Exception {
17+
http
18+
.csrf().disable()
19+
.authorizeRequests().anyRequest().authenticated()
20+
.and().headers().frameOptions().disable()
21+
.and().httpBasic();
22+
}
23+
24+
@Autowired
25+
public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
26+
authenticationManagerBuilder
27+
.inMemoryAuthentication()
28+
.withUser("rokas")
29+
.password(getPasswordEncoder().encode("rokas1"))
30+
.authorities("user");
31+
}
32+
33+
@Bean
34+
public PasswordEncoder getPasswordEncoder() {
35+
return new BCryptPasswordEncoder();
36+
}
37+
38+
}

0 commit comments

Comments
 (0)