Skip to content

Commit 5fd4836

Browse files
authored
Merge pull request #70 from EMResearch/sut-mongo
reservation API
2 parents 2a32aad + 7443b9e commit 5fd4836

File tree

121 files changed

+7020
-10
lines changed

Some content is hidden

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

121 files changed

+7020
-10
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,3 +252,7 @@ dotnet_3/em/embedded/rest/ScsDriver/generated-tests/
252252
/jdk_17_maven/cs/web/spring-petclinic/target/
253253
/jdk_17_maven/em/embedded/web/spring-petclinic/target/
254254
/statistics/table_suts.tex
255+
/jdk_11_gradle/cs/rest/reservations-api/build/
256+
/jdk_11_gradle/em/embedded/rest/reservations-api/build/
257+
/jdk_11_gradle/em/external/rest/reservations-api/build/
258+
/jdk_17_gradle/.gradle/

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ More details (e.g., #LOCs and used databases) on these APIs can be found [in thi
7272

7373
### REST: Java/Kotlin
7474

75+
* Bibliothek (MIT), [jdk_17_gradle/cs/rest/bibliothek](jdk_17_gradle/cs/rest/bibliothek), from [https://github.com/PaperMC/bibliothek](https://github.com/PaperMC/bibliothek)
76+
77+
* Reservations API (not-known license), [jdk_11_gradle/cs/rest/reservations-api](jdk_11_gradle/cs/rest/reservations-api), from [https://github.com/cyrilgavala/reservations-api](https://github.com/cyrilgavala/reservations-api)
78+
7579
* Genome Nexus (MIT), [jdk_8_maven/cs/rest-gui/genome-nexus](jdk_8_maven/cs/rest-gui/genome-nexus), from [https://github.com/genome-nexus/genome-nexus](https://github.com/genome-nexus/genome-nexus)
7680

7781
* Market (MIT), [jdk_11_maven/cs/rest-gui/market](jdk_11_maven/cs/rest-gui/market), from [https://github.com/aleksey-lukyanets/market](https://github.com/aleksey-lukyanets/market)

jdk_11_gradle/build.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
allprojects {
3+
ext {
4+
EVOMASTER_VERSION = "1.6.2-SNAPSHOT"
5+
}
6+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
web: java -Dserver.port=$PORT $JAVA_OPTS -jar build/libs/reservations-api.jar
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Reservations API
2+
3+
Simple API built with SpringBoot and MongoDB database.
4+
5+
## Documentation
6+
7+
Documentation can be found [here](https://cg-reservations-api.herokuapp.com/documentation)
8+
9+
## How to run
10+
11+
### Application
12+
13+
1. Clone the repository by executing commands:
14+
15+
```
16+
cd <yourRepoDirectory>
17+
git clone https://github.com/cyrilgavala/reservations-api.git .
18+
```
19+
20+
2. Open the project with your preferable IDE.
21+
If you use IntelliJ IDEA, it will offer you a **SpringBoot** runner configuration.
22+
3. Update the runner by adding environment variable ```DATABASE_URL``` containing
23+
URL to your MongoDB database and ```JWT_SECRET``` with 512-bit secret.
24+
4. Run the runner configuration.
25+
26+
### Tests
27+
28+
1. To run tests you need to pass step 2. from previous instructions and run command:
29+
30+
```./gradlew test```
31+
32+
It will also execute ```jacocoTestReport``` gradle task, which will generate
33+
test report on path ```reservation-api/build/reports/jacoco/test/html/index.html```.
34+
2. To run whether you pass 95% test coverage check, simply run command:
35+
36+
```./gradlew jacocoTestCoverageVerification```
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
plugins {
2+
id 'org.springframework.boot' version '2.7.0'
3+
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
4+
id 'java'
5+
id 'jacoco'
6+
}
7+
8+
group = 'sk.cyrilgavala'
9+
sourceCompatibility = '11'
10+
11+
repositories {
12+
mavenCentral()
13+
}
14+
15+
dependencies {
16+
/* Annotation processors */
17+
annotationProcessor group: 'org.projectlombok', name: 'lombok-mapstruct-binding', version: '0.1.0'
18+
annotationProcessor group: 'org.mapstruct', name: 'mapstruct-processor', version: mapStructVersion
19+
annotationProcessor group: 'org.projectlombok', name: 'lombok', version: lombokVersion
20+
21+
/* Implementation dependencies */
22+
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-mongodb', version: springBootVersion
23+
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: springBootVersion
24+
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-validation', version: springBootVersion
25+
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: springBootVersion
26+
implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: jjwtVersion
27+
implementation group: 'io.jsonwebtoken', name: 'jjwt-impl', version: jjwtVersion
28+
implementation group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: jjwtVersion
29+
implementation group: 'org.mapstruct', name: 'mapstruct', version: mapStructVersion
30+
implementation group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.2'
31+
implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.8'
32+
33+
/* Compile only dependencies */
34+
compileOnly group: 'org.projectlombok', name: 'lombok', version: lombokVersion
35+
36+
/* Test annotation processors */
37+
testAnnotationProcessor group: 'org.projectlombok', name: 'lombok', version: lombokVersion
38+
testAnnotationProcessor group: 'org.mapstruct', name: 'mapstruct-processor', version: mapStructVersion
39+
40+
/* Test implementation dependencies */
41+
testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: springBootVersion
42+
testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: springBootVersion
43+
testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: springBootVersion
44+
testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-mongodb', version: springBootVersion
45+
testImplementation group: 'org.springframework.security', name: 'spring-security-test', version: springSecurityVersion
46+
47+
/* Test compile only dependencies */
48+
testCompileOnly group: 'org.projectlombok', name: 'lombok', version: lombokVersion
49+
50+
}
51+
52+
def jacocoExcludePackages = ["**/reservationsApi/ReservationsApi.class",
53+
"**/reservationsApi/config/**",
54+
"**/reservationsApi/exception/*",
55+
"**/reservationsApi/model/*",
56+
"**/reservationsApi/security/*",
57+
"**/reservationsApi/web/advise/*",
58+
"**/reservationsApi/web/interceptor/*",
59+
"**/reservationsApi/web/request/*",
60+
"**/reservationsApi/web/response/*"]
61+
62+
test {
63+
useJUnitPlatform()
64+
finalizedBy jacocoTestReport
65+
}
66+
67+
jacoco {
68+
toolVersion "0.8.8"
69+
}
70+
71+
jacocoTestReport {
72+
dependsOn test
73+
reports {
74+
xml.required = false
75+
csv.required = false
76+
}
77+
afterEvaluate {
78+
classDirectories.setFrom(files(classDirectories.files.collect {
79+
fileTree(dir: it, exclude: jacocoExcludePackages)
80+
}))
81+
}
82+
}
83+
84+
jacocoTestCoverageVerification {
85+
violationRules {
86+
rule {
87+
limit {
88+
minimum = 0.95
89+
}
90+
}
91+
}
92+
afterEvaluate {
93+
classDirectories.setFrom(files(classDirectories.files.collect {
94+
fileTree(dir: it, exclude: jacocoExcludePackages)
95+
}))
96+
}
97+
}
98+
99+
check.dependsOn jacocoTestCoverageVerification
100+
101+
102+
tasks.named("bootJar") {
103+
archiveClassifier = 'sut'
104+
}
105+
106+
tasks.named("jar") {
107+
archiveClassifier = 'plain'
108+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
springBootVersion=2.7.0
2+
springSecurityVersion=5.6.3
3+
lombokVersion=1.18.24
4+
mapStructVersion=1.4.2.Final
5+
swaggerVersion=3.0.0
6+
jjwtVersion=0.11.5
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rootProject.name = 'reservations-api'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package sk.cyrilgavala.reservationsApi;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
import org.springframework.context.annotation.ComponentScan;
6+
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
7+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
8+
import org.springframework.transaction.annotation.EnableTransactionManagement;
9+
10+
import java.util.TimeZone;
11+
12+
@SpringBootApplication
13+
@EnableWebSecurity
14+
@EnableMongoRepositories(basePackages = "sk.cyrilgavala.reservationsApi.repository")
15+
@EnableTransactionManagement
16+
@ComponentScan({"sk.cyrilgavala.reservationsApi.config",
17+
"sk.cyrilgavala.reservationsApi.mapper",
18+
"sk.cyrilgavala.reservationsApi.security",
19+
"sk.cyrilgavala.reservationsApi.service",
20+
"sk.cyrilgavala.reservationsApi.web"})
21+
public class ReservationsApi {
22+
23+
public static void main(String[] args) {
24+
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
25+
SpringApplication.run(ReservationsApi.class, args);
26+
}
27+
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package sk.cyrilgavala.reservationsApi.config;
2+
3+
import com.mongodb.ConnectionString;
4+
import com.mongodb.MongoClientSettings;
5+
import com.mongodb.client.MongoClient;
6+
import com.mongodb.client.MongoClients;
7+
import org.springframework.beans.factory.annotation.Value;
8+
import org.springframework.context.annotation.Bean;
9+
import org.springframework.context.annotation.Configuration;
10+
import org.springframework.data.mongodb.MongoDatabaseFactory;
11+
import org.springframework.data.mongodb.MongoTransactionManager;
12+
import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration;
13+
14+
@Configuration
15+
public class DatabaseConfiguration extends AbstractMongoClientConfiguration {
16+
17+
@Value("${databaseUrl}")
18+
private String databaseUrl;
19+
20+
@Bean
21+
MongoTransactionManager transactionManager(MongoDatabaseFactory dbFactory) {
22+
return new MongoTransactionManager(dbFactory);
23+
}
24+
25+
@Override
26+
protected String getDatabaseName() {
27+
return "reservations-api";
28+
}
29+
30+
@Override
31+
public MongoClient mongoClient() {
32+
ConnectionString connectionString = new ConnectionString(databaseUrl);
33+
MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
34+
.applyConnectionString(connectionString)
35+
.build();
36+
return MongoClients.create(mongoClientSettings);
37+
}
38+
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package sk.cyrilgavala.reservationsApi.config;
2+
3+
import io.swagger.v3.oas.models.Components;
4+
import io.swagger.v3.oas.models.OpenAPI;
5+
import io.swagger.v3.oas.models.info.Info;
6+
import io.swagger.v3.oas.models.security.SecurityScheme;
7+
import org.springframework.context.annotation.Bean;
8+
import org.springframework.context.annotation.Configuration;
9+
10+
@Configuration
11+
public class OpenApiConfiguration {
12+
13+
@Bean
14+
public OpenAPI springShopOpenAPI() {
15+
return new OpenAPI()
16+
.components(
17+
new Components().addSecuritySchemes("bearer-key",
18+
new SecurityScheme().type(SecurityScheme.Type.HTTP).scheme("bearer").bearerFormat("JWT")))
19+
.info(new Info().title("Reservations API")
20+
.description("Simple API for implementing basic reservation system.")
21+
.version("v1.0.0"));
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package sk.cyrilgavala.reservationsApi.config;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.springframework.context.annotation.Bean;
5+
import org.springframework.context.annotation.Configuration;
6+
import org.springframework.http.HttpMethod;
7+
import org.springframework.http.HttpStatus;
8+
import org.springframework.security.authentication.AuthenticationManager;
9+
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
10+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
11+
import org.springframework.security.config.http.SessionCreationPolicy;
12+
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
13+
import org.springframework.security.crypto.password.PasswordEncoder;
14+
import org.springframework.security.web.SecurityFilterChain;
15+
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
16+
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
17+
import sk.cyrilgavala.reservationsApi.security.TokenAuthenticationFilter;
18+
19+
@RequiredArgsConstructor
20+
@Configuration
21+
public class SecurityConfiguration {
22+
23+
public static final String ADMIN = "ADMIN";
24+
public static final String USER = "USER";
25+
private final TokenAuthenticationFilter tokenAuthenticationFilter;
26+
27+
@Bean
28+
AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
29+
return authenticationConfiguration.getAuthenticationManager();
30+
}
31+
32+
@Bean
33+
public PasswordEncoder passwordEncoder() {
34+
return new BCryptPasswordEncoder(10);
35+
}
36+
37+
@Bean
38+
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
39+
http.authorizeRequests()
40+
.antMatchers(HttpMethod.GET, "/api/reservation/**").hasAnyAuthority(ADMIN, USER)
41+
.antMatchers(HttpMethod.POST, "/api/reservation").hasAnyAuthority(ADMIN, USER)
42+
.antMatchers(HttpMethod.PUT, "/api/reservation").hasAnyAuthority(ADMIN, USER)
43+
.antMatchers(HttpMethod.DELETE, "/api/reservation/**").hasAnyAuthority(ADMIN, USER)
44+
.antMatchers(HttpMethod.GET, "/api/reservation").hasAuthority(ADMIN)
45+
.antMatchers("/api/user/**").permitAll()
46+
.antMatchers("/", "/error", "/documentation", "/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs", "/v3/api-docs/**").permitAll()
47+
.anyRequest().authenticated();
48+
http.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
49+
http.exceptionHandling(e -> e.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)));
50+
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
51+
http.cors().and().csrf().disable();
52+
return http.build();
53+
}
54+
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package sk.cyrilgavala.reservationsApi.config;
2+
3+
import com.fasterxml.jackson.databind.SerializationFeature;
4+
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
5+
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
6+
import org.springframework.context.annotation.Configuration;
7+
import org.springframework.http.converter.HttpMessageConverter;
8+
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
9+
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
10+
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
11+
12+
import java.util.List;
13+
import java.util.TimeZone;
14+
15+
@Configuration
16+
public class WebConfiguration implements WebMvcConfigurer {
17+
18+
@Override
19+
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
20+
WebMvcConfigurer.super.configureMessageConverters(converters);
21+
22+
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
23+
builder.modules(new JavaTimeModule(), new Jdk8Module());
24+
builder.timeZone(TimeZone.getTimeZone("UTC"));
25+
builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
26+
27+
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
28+
converter.setObjectMapper(builder.build());
29+
}
30+
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package sk.cyrilgavala.reservationsApi.exception;
2+
3+
public class DuplicateUserException extends RuntimeException {
4+
5+
private static final long serialVersionUID = 9197342664218222132L;
6+
7+
public DuplicateUserException(String message) {
8+
super(message);
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package sk.cyrilgavala.reservationsApi.exception;
2+
3+
public class ReservationException extends RuntimeException {
4+
5+
private static final long serialVersionUID = 4033799885256608552L;
6+
7+
public ReservationException(String message) {
8+
super(message);
9+
}
10+
}

0 commit comments

Comments
 (0)