Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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