Skip to content

Commit

Permalink
Merge pull request #70 from EMResearch/sut-mongo
Browse files Browse the repository at this point in the history
reservation API
  • Loading branch information
arcuri82 authored Sep 24, 2023
2 parents 2a32aad + 7443b9e commit 5fd4836
Show file tree
Hide file tree
Showing 121 changed files with 7,020 additions and 10 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -252,3 +252,7 @@ dotnet_3/em/embedded/rest/ScsDriver/generated-tests/
/jdk_17_maven/cs/web/spring-petclinic/target/
/jdk_17_maven/em/embedded/web/spring-petclinic/target/
/statistics/table_suts.tex
/jdk_11_gradle/cs/rest/reservations-api/build/
/jdk_11_gradle/em/embedded/rest/reservations-api/build/
/jdk_11_gradle/em/external/rest/reservations-api/build/
/jdk_17_gradle/.gradle/
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ More details (e.g., #LOCs and used databases) on these APIs can be found [in thi

### REST: Java/Kotlin

* 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)

* 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)

* 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)

* 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)
Expand Down
6 changes: 6 additions & 0 deletions jdk_11_gradle/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

allprojects {
ext {
EVOMASTER_VERSION = "1.6.2-SNAPSHOT"
}
}
1 change: 1 addition & 0 deletions jdk_11_gradle/cs/rest/reservations-api/Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: java -Dserver.port=$PORT $JAVA_OPTS -jar build/libs/reservations-api.jar
36 changes: 36 additions & 0 deletions jdk_11_gradle/cs/rest/reservations-api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Reservations API

Simple API built with SpringBoot and MongoDB database.

## Documentation

Documentation can be found [here](https://cg-reservations-api.herokuapp.com/documentation)

## How to run

### Application

1. Clone the repository by executing commands:

```
cd <yourRepoDirectory>
git clone https://github.com/cyrilgavala/reservations-api.git .
```

2. Open the project with your preferable IDE.
If you use IntelliJ IDEA, it will offer you a **SpringBoot** runner configuration.
3. Update the runner by adding environment variable ```DATABASE_URL``` containing
URL to your MongoDB database and ```JWT_SECRET``` with 512-bit secret.
4. Run the runner configuration.

### Tests

1. To run tests you need to pass step 2. from previous instructions and run command:

```./gradlew test```

It will also execute ```jacocoTestReport``` gradle task, which will generate
test report on path ```reservation-api/build/reports/jacoco/test/html/index.html```.
2. To run whether you pass 95% test coverage check, simply run command:

```./gradlew jacocoTestCoverageVerification```
108 changes: 108 additions & 0 deletions jdk_11_gradle/cs/rest/reservations-api/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
plugins {
id 'org.springframework.boot' version '2.7.0'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
id 'jacoco'
}

group = 'sk.cyrilgavala'
sourceCompatibility = '11'

repositories {
mavenCentral()
}

dependencies {
/* Annotation processors */
annotationProcessor group: 'org.projectlombok', name: 'lombok-mapstruct-binding', version: '0.1.0'
annotationProcessor group: 'org.mapstruct', name: 'mapstruct-processor', version: mapStructVersion
annotationProcessor group: 'org.projectlombok', name: 'lombok', version: lombokVersion

/* Implementation dependencies */
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-mongodb', version: springBootVersion
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: springBootVersion
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-validation', version: springBootVersion
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: springBootVersion
implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: jjwtVersion
implementation group: 'io.jsonwebtoken', name: 'jjwt-impl', version: jjwtVersion
implementation group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: jjwtVersion
implementation group: 'org.mapstruct', name: 'mapstruct', version: mapStructVersion
implementation group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.2'
implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.8'

/* Compile only dependencies */
compileOnly group: 'org.projectlombok', name: 'lombok', version: lombokVersion

/* Test annotation processors */
testAnnotationProcessor group: 'org.projectlombok', name: 'lombok', version: lombokVersion
testAnnotationProcessor group: 'org.mapstruct', name: 'mapstruct-processor', version: mapStructVersion

/* Test implementation dependencies */
testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: springBootVersion
testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: springBootVersion
testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: springBootVersion
testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-mongodb', version: springBootVersion
testImplementation group: 'org.springframework.security', name: 'spring-security-test', version: springSecurityVersion

/* Test compile only dependencies */
testCompileOnly group: 'org.projectlombok', name: 'lombok', version: lombokVersion

}

def jacocoExcludePackages = ["**/reservationsApi/ReservationsApi.class",
"**/reservationsApi/config/**",
"**/reservationsApi/exception/*",
"**/reservationsApi/model/*",
"**/reservationsApi/security/*",
"**/reservationsApi/web/advise/*",
"**/reservationsApi/web/interceptor/*",
"**/reservationsApi/web/request/*",
"**/reservationsApi/web/response/*"]

test {
useJUnitPlatform()
finalizedBy jacocoTestReport
}

jacoco {
toolVersion "0.8.8"
}

jacocoTestReport {
dependsOn test
reports {
xml.required = false
csv.required = false
}
afterEvaluate {
classDirectories.setFrom(files(classDirectories.files.collect {
fileTree(dir: it, exclude: jacocoExcludePackages)
}))
}
}

jacocoTestCoverageVerification {
violationRules {
rule {
limit {
minimum = 0.95
}
}
}
afterEvaluate {
classDirectories.setFrom(files(classDirectories.files.collect {
fileTree(dir: it, exclude: jacocoExcludePackages)
}))
}
}

check.dependsOn jacocoTestCoverageVerification


tasks.named("bootJar") {
archiveClassifier = 'sut'
}

tasks.named("jar") {
archiveClassifier = 'plain'
}
6 changes: 6 additions & 0 deletions jdk_11_gradle/cs/rest/reservations-api/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
springBootVersion=2.7.0
springSecurityVersion=5.6.3
lombokVersion=1.18.24
mapStructVersion=1.4.2.Final
swaggerVersion=3.0.0
jjwtVersion=0.11.5
1 change: 1 addition & 0 deletions jdk_11_gradle/cs/rest/reservations-api/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'reservations-api'
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package sk.cyrilgavala.reservationsApi;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import java.util.TimeZone;

@SpringBootApplication
@EnableWebSecurity
@EnableMongoRepositories(basePackages = "sk.cyrilgavala.reservationsApi.repository")
@EnableTransactionManagement
@ComponentScan({"sk.cyrilgavala.reservationsApi.config",
"sk.cyrilgavala.reservationsApi.mapper",
"sk.cyrilgavala.reservationsApi.security",
"sk.cyrilgavala.reservationsApi.service",
"sk.cyrilgavala.reservationsApi.web"})
public class ReservationsApi {

public static void main(String[] args) {
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
SpringApplication.run(ReservationsApi.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package sk.cyrilgavala.reservationsApi.config;

import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.MongoTransactionManager;
import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration;

@Configuration
public class DatabaseConfiguration extends AbstractMongoClientConfiguration {

@Value("${databaseUrl}")
private String databaseUrl;

@Bean
MongoTransactionManager transactionManager(MongoDatabaseFactory dbFactory) {
return new MongoTransactionManager(dbFactory);
}

@Override
protected String getDatabaseName() {
return "reservations-api";
}

@Override
public MongoClient mongoClient() {
ConnectionString connectionString = new ConnectionString(databaseUrl);
MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
.applyConnectionString(connectionString)
.build();
return MongoClients.create(mongoClientSettings);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package sk.cyrilgavala.reservationsApi.config;

import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class OpenApiConfiguration {

@Bean
public OpenAPI springShopOpenAPI() {
return new OpenAPI()
.components(
new Components().addSecuritySchemes("bearer-key",
new SecurityScheme().type(SecurityScheme.Type.HTTP).scheme("bearer").bearerFormat("JWT")))
.info(new Info().title("Reservations API")
.description("Simple API for implementing basic reservation system.")
.version("v1.0.0"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package sk.cyrilgavala.reservationsApi.config;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import sk.cyrilgavala.reservationsApi.security.TokenAuthenticationFilter;

@RequiredArgsConstructor
@Configuration
public class SecurityConfiguration {

public static final String ADMIN = "ADMIN";
public static final String USER = "USER";
private final TokenAuthenticationFilter tokenAuthenticationFilter;

@Bean
AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}

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

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/reservation/**").hasAnyAuthority(ADMIN, USER)
.antMatchers(HttpMethod.POST, "/api/reservation").hasAnyAuthority(ADMIN, USER)
.antMatchers(HttpMethod.PUT, "/api/reservation").hasAnyAuthority(ADMIN, USER)
.antMatchers(HttpMethod.DELETE, "/api/reservation/**").hasAnyAuthority(ADMIN, USER)
.antMatchers(HttpMethod.GET, "/api/reservation").hasAuthority(ADMIN)
.antMatchers("/api/user/**").permitAll()
.antMatchers("/", "/error", "/documentation", "/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs", "/v3/api-docs/**").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
http.exceptionHandling(e -> e.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)));
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.cors().and().csrf().disable();
return http.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package sk.cyrilgavala.reservationsApi.config;

import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;
import java.util.TimeZone;

@Configuration
public class WebConfiguration implements WebMvcConfigurer {

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
WebMvcConfigurer.super.configureMessageConverters(converters);

Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.modules(new JavaTimeModule(), new Jdk8Module());
builder.timeZone(TimeZone.getTimeZone("UTC"));
builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(builder.build());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package sk.cyrilgavala.reservationsApi.exception;

public class DuplicateUserException extends RuntimeException {

private static final long serialVersionUID = 9197342664218222132L;

public DuplicateUserException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package sk.cyrilgavala.reservationsApi.exception;

public class ReservationException extends RuntimeException {

private static final long serialVersionUID = 4033799885256608552L;

public ReservationException(String message) {
super(message);
}
}
Loading

0 comments on commit 5fd4836

Please sign in to comment.