Skip to content

Commit

Permalink
extract Gropius adapter from backend repository
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulBredl committed Jul 26, 2024
1 parent 5850e2e commit a926f74
Show file tree
Hide file tree
Showing 60 changed files with 15,049 additions and 769 deletions.
13 changes: 4 additions & 9 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,12 @@ jobs:
- name: Run chmod to make gradlew executable
run: chmod +x gradlew

- name: Run tests
- name: Execute tests
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
uses: gradle/gradle-build-action@v3
with:
arguments: cleanTest test
# Configuration for SonarCloud, enable this when SonarCloud is configured
# - name: Execute tests
# env:
# SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
# uses: gradle/gradle-build-action@v3
# with:
# arguments: cleanTest test jacocoTestReport sonar
arguments: cleanTest test jacocoTestReport sonar



Expand Down
24 changes: 0 additions & 24 deletions Dockerfile

This file was deleted.

173 changes: 17 additions & 156 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,165 +1,26 @@
# template-microservice
This serves as a template for the microservices
# Gropius Adapter for DinoDev

## Package structure
This repository contains the adapter that connects DinoDev to Gropius.

This package structure is based on multiple sources of best practices in Spring Boot, using roughly the "Package by layer" approach.
- *root*
- *config*
- *controller*
- *dapr*
- *dto*
- *exception*
- *persistence*
- *entity*
- *repository*
- *mapper*
- *service*
- *util* (optional, if needed)
- *validation*
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=MEITREX_dinodev_gropius_adapter&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=MEITREX_dinodev_gropius_adapter)
[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=MEITREX_dinodev_gropius_adapter&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=MEITREX_dinodev_gropius_adapter)

Detailed description of the packages:
## GraphQL Code Generator

### Root package
Like all other DinoDev repos, the adapter uses the
[GraphQL Code Generator Plugin](https://github.com/kobylynskyi/graphql-java-codegen-gradle-plugin)
to generate Java classes from the GraphQL schema. In this case, the schema is the Gropius schema
and the generated classes are used to interact with the Gropius API.

This should be named after the microservice itself. This is the root package for the microservice. It contains the `Application.java` file (or of similar name), which is the entry point for the microservice. Usually, this is the only class in this package.
The plugin is configured in the `build.gradle` file. The classes are generated on every build, so you don't need to run the generator manually.

### Config package
This package should contain any classes that are used to configure the application. This includes [Sprint Boot configuration classes](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Configuration.html) but might also contain anything else related to configuration the microservice.
The classes that are in this package should not be deleted in the actual microservice as they provide useful functionality.
### Updating the Gropius Schema

### Controller package
To update the Gropius Schema, you can run the gradle task `refreshGropiusSchema`. The URL of the Gropius backend can be adjusted in the `build.gradle` file.

**Location:src/main/java/de/unistuttgart/iste/meitrex/{service_name}/controller**
### Limitations of generated client classes

This package contains the GraphQL controllers (and other types of controllers if needed). The GraphQL controllers are annotated with the `@Controller` annotation. Controllers contain no business logic, but only delegate the requests to the service layer. They handle the "technical stuff" of the request.

In some services, there is also a class called SubscriptionController which handles all dapr event subscriptions.

More information can be found in
the [Controller package](src/main/java/de/unistuttgart/iste/meitrex/template/controller/package-info.java).

### Dapr package

**Location**:src/main/java/de/unistuttgart/iste/meitrex/{service_name}/dapr

This package should contain all classes that are used to communicate with Dapr, e.g. using pub sub.

### DTO package

**This package will not be located in the src/main/java folder, but in the build/generated folder.**

This package contains the generated DTOs (data transfer objects) from the GraphQL schema. The DTOs are generated when building the project with gradle.

If not necessary, no other files should be added manually to this package.

#### Why both DTOs and Entities?

The DTOs are used to transfer data between the GraphQL controller and the service layer. The entities are used to persist data in the database. This is done to separate the data transfer from the data persistence. This is a common approach in Spring Boot applications as it can happen that we want to store more data in the database than we want to transfer to the client or vice versa.

### Exception package

**Location**:src/main/java/de/unistuttgart/iste/meitrex/{service_name}/exception

This package is used for exception handling. Note that with GraphQL, the exceptions are not thrown directly, but are wrapped in a `GraphQLException`, which is different that from the usual Spring Boot approach.

More information can be found in
the [Exception package](src/main/java/de/unistuttgart/iste/meitrex/template/exception/package-info.java).

### Persistence package

**Location**:src/main/java/de/unistuttgart/iste/meitrex/{service_name}/persistence

This package contains all classes that are used to persist data in the database. This includes the entities, the mapping
logic between entities and DTOs, as well as the repositories.

This package handles the calls to the database and defines the database entities. It is structured into three sub-packages:

#### 1. entity
This package contains the database entities.

#### 2. repository
This package contains the interfaces to the database, also known as Data Access Objects (DAOs), used to perform various database operations. Note that these interfaces may sometimes be empty, especially when the default methods provided by the Spring Framework are sufficient for the required operations.

#### 3. mapper
The 'mapper' package is responsible for the mapping logic between the database entities and the data types defined in the GraphQL schema. Specifically, it maps the database entity classes to the corresponding classes generated from the GraphQL schema.

This structure helps organize the database-related components of the project, making it easier to manage and maintain.

More information can be found in
the [Entity package](src/main/java/de/unistuttgart/iste/meitrex/template/persistence/entity/package-info.java) and
the [Repository package](src/main/java/de/unistuttgart/iste/meitrex/template/persistence/repository/package-info.java).

### Service package

**Location**:src/main/java/de/unistuttgart/iste/meitrex/{service_name}/service

This package contains all classes that are used to handle the business logic of the microservice. Services are annotated with the `@Service` annotation. Services contain only business logic and delegate the data access to the persistence layer (repositories).

More information can be found in
the [Service package](src/main/java/de/unistuttgart/iste/meitrex/template/service/package-info.java).

### Validation package

**Location**:src/main/java/de/unistuttgart/iste/meitrex/{service_name}/validation

This package should contain the *class-level* validation logic, i.e. the validation logic that is not directly related to a specific field, e.g. validation if an end date is after a start date.

Field-level validation logic should not be placed in this package, but in the graphql schema, via directives.
If these directives are not sufficient, the validation logic can also be placed in this package.

## Getting Started

### Todos

Follow the guide in the wiki: https://github.com/MEITREX/wiki/blob/main/dev-manuals/backend/new-service.md

Addtionally, after cloning the repository, you need to do the following steps:
- [ ] Setup the gradle files correctly. This means
- [ ] Change the project name in the `settings.gradle` file
- [ ] Change the package name in the `build.gradle` file (there is a TODO comment)
- [ ] Change the sonar project key in the `build.gradle` file (should be MEITREX_repository_name)
- [ ] Add/Remove dependencies in the `build.gradle` file
- [ ] Rename the package in the `src/main/java` folder to a more suitable name (should be the same as the package name in the `build.gradle` file)
- [ ] Remove the package-info.java files in the `src/main/java` folder (or update with the microservice specific information)
- [ ] Update the application.properties file in the `src/main/resources` folder (check the TODOS in the file)
- [ ] Change the ports and name of the database in the docker-compose.yml (see wiki on how to)
- [ ] Define the GraphQL schema in the `src/main/resources/schema.graphqls` file
<!-- TODO there probably more TODOs -->


After creating a new service you need to do the following:
- [ ] Add the repository to sonarcloud, follow the instructions for extra configuration, unselect automatic analysis and choose github actions, only the first step needs to be completed
- [ ] Add SONAR_TOKEN to the service repository secrets on Github (this requires you to have admin permissions on sonarcloud)

### Pull new changes from this template

If this template changes and you want to pull the changes to the actual microservice, you can run the following commands:
```bash
git remote add template https://github.com/MEITREX/template_microservice # only necessary once
git fetch --all
git checkout [branch] # replace [branch] with the branch name you want the changes to be merged into (preferably not main)
git merge template/main --allow-unrelated-histories
# you will probably need to commit afterwars
```

### Guides
The following guides illustrate how to use some features concretely:

* [Building a GraphQL service](https://spring.io/guides/gs/graphql-server/)
* [Accessing Data with JPA](https://spring.io/guides/gs/accessing-data-jpa/)
* [Validation with GraphQL directives](https://github.com/graphql-java/graphql-java-extended-validation/blob/master/README.md)
* [Error handling](https://www.baeldung.com/spring-graphql-error-handling)

### Reference Documentation
For further reference, please consider the following sections:

* [Official Gradle documentation](https://docs.gradle.org)
* [Spring Boot Gradle Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/3.0.6/gradle-plugin/reference/html/)
* [Spring Configuration Processor](https://docs.spring.io/spring-boot/docs/3.0.6/reference/htmlsingle/#appendix.configuration-metadata.annotation-processor)
* [Spring Boot DevTools](https://docs.spring.io/spring-boot/docs/3.0.6/reference/htmlsingle/#using.devtools)
* [Spring for GraphQL](https://docs.spring.io/spring-boot/docs/3.0.6/reference/html/web.html#web.graphql)
* [Spring Data JPA](https://docs.spring.io/spring-boot/docs/3.0.6/reference/htmlsingle/#data.sql.jpa-and-spring-data)
* [Validation](https://docs.spring.io/spring-boot/docs/3.0.6/reference/htmlsingle/#io.validation)
* [Generating Sonarqube Token](https://docs.sonarqube.org/latest/user-guide/user-account/generating-and-using-tokens/)
* [Adding secrets on Github](https://docs.github.com/en/actions/security-guides/encrypted-secrets)
The advantage of using the generated classes is that you can use them to interact with the Gropius API in a type-safe way. However, the generated classes are not perfect and there are some limitations:
- To few fields are generated if the field has parameters. In this case, add the type name to `fieldsWithoutResolvers` in the `build.gradle` file.
- There are serialization issues with `OffsetDateTime` and `LocalDateTime`. The workaround is to use `String` fields instead. This can be achieved using a `customTypeMapping` in the `build.gradle` file.
- Serialization of interfaces is not supported. If interfaces are queried, manually map them to a concrete subclass in the `customTypeMapping`.`
63 changes: 47 additions & 16 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ plugins {
id 'org.springframework.boot' version '3.+'
id 'io.spring.dependency-management' version '1.+'
id "io.github.kobylynskyi.graphql.codegen" version "5.+"
id "org.sonarqube" version "4.+"
id "org.sonarqube" version "5.+"
id "jacoco"

}
Expand All @@ -22,39 +22,63 @@ if (jacocoEnabled.toBoolean()) {

sonarqube {
properties {
property("sonar.projectKey", "MEITREX_template-microservice")
property("sonar.projectKey", "MEITREX_dinodev_gropius_adapter")
property("sonar.organization", "meitrex")
property("sonar.host.url", "https://sonarcloud.io")
}
}

def gropiusUrl = System.properties.getProperty("gropiusUrl") ?: "http://localhost:8080"

// run this task to refresh the Gropius schema
// it will download the schema from the Gropius server and save it to src/main/resources/graphql/gropius.graphqls
// this task is not run automatically, you have to run it manually (so you can control when the schema is updated)
// you can run it from the command line with `./gradlew refreshGropiusSchema`
tasks.register('refreshGropiusSchema', Exec) {
def filePath = 'src/main/resources/graphql/gropius.graphqls'

commandLine 'curl', "${gropiusUrl}/sdl", '-o', filePath
}

// Automatically generate DTOs from GraphQL schema:
// generate gropius client
graphqlCodegen {
// all config options:
// https://github.com/kobylynskyi/graphql-java-codegen/blob/main/docs/codegen-options.md
outputDir = new File("$buildDir/generated")
packageName = "de.unistuttgart.iste.meitrex.generated.dto"
graphqlSchemas {
rootDir = file("src/main/resources/graphql")
includePattern = "gropius.graphqls"
}
outputDir = new File("$buildDir/generated/gropius")
packageName = "de.unistuttgart.iste.gropius.generated.dto"
generatedAnnotation = "jakarta.annotation.Generated"
modelValidationAnnotation = "jakarta.validation.constraints.NotNull"
generateApis = false // set to false as the generator does not support spring boot graphQL
customTypesMapping = [
"DateTime" : "java.time.OffsetDateTime",
"Date" : "java.time.LocalDate",
"Time" : "java.time.OffsetTime",
"LocalTime": "java.time.LocalTime",
"UUID" : "java.util.UUID",
"Url" : "java.net.URL",
"DateTime" : "java.time.OffsetDateTime",
"Date" : "java.time.LocalDate",
"Time" : "java.time.OffsetTime",
"LocalTime" : "java.time.LocalTime",
"UUID" : "java.util.UUID",
"Url" : "java.net.URL",
"JSON" : "java.lang.Object",
"DateTimeFilterInput.gt" : "String", // serialization of OffsetDateTime does not work
"Assignment.user" : "GropiusGropiusUser", // concrete sub type to allow serialization of the user object
"TimelineItemConnection.nodes": "de.unistuttgart.iste.meitrex.scrumgame.ims.gropius.GropiusProjections.TimelineItemResponse",
"TrackableConnection.nodes" : "de.unistuttgart.iste.meitrex.scrumgame.ims.gropius.GropiusProjections.TrackableResponse",
]
modelNamePrefix = "Gropius"
generateEqualsAndHashCode = true
generateToString = true
generateClient = true
// add fields that should be generated in the model classes
fieldsWithoutResolvers = ["Project", "Issue", "Component"]
}

// Automatically generate GraphQL code on project build:
compileJava.dependsOn 'graphqlCodegen'

// Add generated sources to your project source sets:
sourceSets.main.java.srcDir "$buildDir/generated"
sourceSets.main.java.srcDir "$buildDir/generated/gropius"

configurations {
compileOnly {
Expand All @@ -67,20 +91,29 @@ repositories {
}

dependencies {
implementation 'de.unistuttgart.iste.meitrex:meitrex-common:1.0.0'
implementation 'de.unistuttgart.iste.meitrex:meitrex-common:1.2'
implementation('de.unistuttgart.iste.meitrex:dinodev_common') {
version {
branch = 'main'
}
}
implementation 'de.unistuttgart.iste.meitrex:gamification_engine:1.2'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-graphql'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.modelmapper:modelmapper:3.+'
implementation 'com.graphql-java:graphql-java-extended-scalars:20.0'
implementation 'com.graphql-java:graphql-java-extended-validation:20.0'
implementation 'io.github.kobylynskyi:graphql-java-codegen:5.+'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'de.unistuttgart.iste.meitrex:meitrex-common-test:1.0.0'
testImplementation 'de.unistuttgart.iste.meitrex:meitrex-common-test:1.2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework:spring-webflux'
testImplementation 'org.springframework.graphql:spring-graphql-test'
Expand All @@ -93,5 +126,3 @@ dependencies {
tasks.named('test') {
useJUnitPlatform()
}

tasks.withType(Test).configureEach { testLogging.showStandardStreams = true }
10 changes: 0 additions & 10 deletions components/pubsub.yaml

This file was deleted.

Loading

0 comments on commit a926f74

Please sign in to comment.