Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • xkollar3/online-banking-service
1 result
Show changes
Commits on Source (8)
Showing
with 298 additions and 63 deletions
FROM eclipse-temurin:21-jdk-alpine
FROM maven:3.9.6-eclipse-temurin-21 AS build
WORKDIR /build
COPY target/*.jar analytics-service.jar
COPY src/main/resources/application.yml /usr/src/application.yml
COPY pom.xml .
COPY analytics-service/pom.xml analytics-service/pom.xml
COPY analytics-service/src analytics-service/src
ENTRYPOINT ["java", "-jar", "analytics-service.jar"]
\ No newline at end of file
RUN mvn -f analytics-service/pom.xml clean package -DskipTests
FROM eclipse-temurin:21-jdk@sha256:dd90127baca82663c56ff903156d229a8287fe94a7b0145a31b45fecebdb7be6
WORKDIR /app
COPY --from=build /build/analytics-service/target/*.jar ./app.jar
CMD ["java", "-jar", "app.jar"]
......@@ -52,6 +52,15 @@
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
......
......@@ -2,6 +2,13 @@ server:
servlet:
context-path: '/api/analytics-service'
port: 8080
management:
endpoints:
web:
exposure:
include: [ 'prometheus', 'health', 'info' ]
spring:
application:
name: analytics-service
......
FROM eclipse-temurin:21-jdk-alpine
FROM maven:3.9.6-eclipse-temurin-21 AS build
WORKDIR /build
COPY target/*.jar currency-service.jar
COPY src/main/resources/application.yml /usr/src/application.yml
COPY pom.xml .
COPY currency-service/pom.xml currency-service/pom.xml
COPY currency-service/src currency-service/src
ENTRYPOINT ["java", "-jar", "currency-service.jar"]
\ No newline at end of file
RUN mvn -f currency-service/pom.xml clean package -DskipTests
FROM eclipse-temurin:21-jdk@sha256:dd90127baca82663c56ff903156d229a8287fe94a7b0145a31b45fecebdb7be6
WORKDIR /app
COPY --from=build /build/currency-service/target/*.jar ./app.jar
CMD ["java", "-jar", "app.jar"]
......@@ -52,6 +52,15 @@
<artifactId>opencsv</artifactId>
<version>${opencsv.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
......
......@@ -3,6 +3,12 @@ server:
context-path: '/api/currency-service'
port: 8081
management:
endpoints:
web:
exposure:
include: [ 'prometheus', 'health', 'info' ]
data:
initialize: true
......
......@@ -2,100 +2,117 @@ services:
analytics-service:
container_name: analytics-service
build:
context: analytics-service
dockerfile: Dockerfile
context: .
dockerfile: ./analytics-service/Dockerfile
depends_on:
- analytics-db
- analytics-db
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://analytics-db:5432/analytics_db
- SPRING_DATASOURCE_URL=jdbc:postgresql://analytics-db:5432/analytics_db
ports:
- 8080:8080
- 8080:8080
analytics-db:
container_name: analytics-db
image: postgres:latest
ports:
- 5432:5432
- 5432:5432
volumes:
- analytics-db-data:/var/lib/postgresql/data
- analytics-db-data:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=changemelater
- POSTGRES_USER=analytics_service
- POSTGRES_DB=analytics_db
- POSTGRES_PASSWORD=changemelater
- POSTGRES_USER=analytics_service
- POSTGRES_DB=analytics_db
currency-service:
container_name: currency-service
build:
context: currency-service
dockerfile: Dockerfile
context: .
dockerfile: ./currency-service/Dockerfile
depends_on:
- currency-db
- currency-db
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://currency-db:5432/currency_db
- SPRING_DATASOURCE_URL=jdbc:postgresql://currency-db:5432/currency_db
ports:
- 8081:8081
- 8081:8081
currency-db:
container_name: currency-db
image: postgres:latest
ports:
- 5433:5432
- 5433:5432
volumes:
- currency-db-data:/var/lib/postgresql/data
- currency-db-data:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=changemelater
- POSTGRES_USER=currency_service
- POSTGRES_DB=currency_db
- POSTGRES_PASSWORD=changemelater
- POSTGRES_USER=currency_service
- POSTGRES_DB=currency_db
transaction-service:
container_name: transaction-service
build:
context: transaction-service
dockerfile: Dockerfile
context: .
dockerfile: ./transaction-service/Dockerfile
depends_on:
- transaction-db
- transaction-db
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://transaction-db:5432/transaction_db
- SPRING_DATASOURCE_URL=jdbc:postgresql://transaction-db:5432/transaction_db
ports:
- 8082:8082
- 8082:8082
transaction-db:
container_name: transaction-db
image: postgres:latest
ports:
- 5434:5432
- 5434:5432
volumes:
- transaction-db-data:/var/lib/postgresql/data
- transaction-db-data:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=changemelater
- POSTGRES_USER=transaction_service
- POSTGRES_DB=transaction_db
- POSTGRES_PASSWORD=changemelater
- POSTGRES_USER=transaction_service
- POSTGRES_DB=transaction_db
user-service:
container_name: user-service
build:
context: user-service
dockerfile: Dockerfile
context: .
dockerfile: ./user-service/Dockerfile
depends_on:
- user-db
- user-db
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://user-db:5432/user_db
- SPRING_DATASOURCE_URL=jdbc:postgresql://user-db:5432/user_db
ports:
- 8083:8083
- 8083:8083
user-db:
container_name: user-db
image: postgres:latest
ports:
- 5435:5432
- 5435:5432
volumes:
- user-db-data:/var/lib/postgresql/data
- user-db-data:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=changemelater
- POSTGRES_USER=user_service
- POSTGRES_DB=user_db
- POSTGRES_PASSWORD=changemelater
- POSTGRES_USER=user_service
- POSTGRES_DB=user_db
database-fe:
image: adminer:standalone
restart: always
ports:
- "8084:8080"
- "8084:8080"
environment:
ADMINER_DESIGN: pepa-linha
prometheus:
image: prom/prometheus:v2.51.2
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ./monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
grafana:
image: grafana/grafana:10.4.2
container_name: grafana
ports:
- "3000:3000"
restart: unless-stopped
volumes:
- ./monitoring/grafana/datasources:/etc/grafana/provisioning/datasources
- grafana_storage:/var/lib/grafana
volumes:
analytics-db-data: { }
user-db-data: { }
currency-db-data: { }
transaction-db-data: { }
\ No newline at end of file
transaction-db-data: { }
grafana_storage: { }
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
scrape_configs:
- job_name: 'analytics-service'
metrics_path: '/api/analytics-service/actuator/prometheus'
scrape_interval: 5s
static_configs:
- targets: ['host.docker.internal:8080']
labels:
application: 'analytics-service'
- job_name: 'currency-service'
metrics_path: '/api/currency-service/actuator/prometheus'
scrape_interval: 5s
static_configs:
- targets: ['host.docker.internal:8081']
labels:
application: 'currency-service'
- job_name: 'transaction-service'
metrics_path: '/api/transaction-service/actuator/prometheus'
scrape_interval: 5s
static_configs:
- targets: ['host.docker.internal:8082']
labels:
application: 'transaction-service'
- job_name: 'user-service'
metrics_path: '/api/user-service/actuator/prometheus'
scrape_interval: 5s
static_configs:
- targets: ['host.docker.internal:8083']
labels:
application: 'user-service'
File mode changed from 100644 to 100755
......@@ -4,7 +4,7 @@ How to run the project:
1. Run `mvn clean install` in the root directory
NOTE: if this step fails you might not be running docker on your machine, it is neccessary for test containers
2. (OPTIONAL) Add `data.initialize: true` to application.yml of a service if you want to initialize some data in the tables, unfortunately this does nothing in transaction-service yet.
3. Run `docker compose up` in the root directory
3. Run `docker compose up --build` in the root directory
Now all the services and the databases are running, and you can access them on the following ports:
- User-service: `localhost:8081/api/user-service`
- Transaction-service: `localhost:8082/api/transaction-service`
......@@ -66,6 +66,17 @@ Password: `changemelater`
- [Transaction-service](http://localhost:8084/?pgsql=transaction-db&username=transaction_service&db=transaction_db&)
- [Currency-service](http://localhost:8084/?pgsql=currency-db&username=currency_service&db=currency_db&)
### Grafana
http://localhost:3000/
Username: `admin`
Password: `admin`
### Prometheus
http://localhost:9090/
### Entity relationship Diagram
![img_3.png](img_3.png)
FROM eclipse-temurin:21-jdk-alpine
FROM maven:3.9.6-eclipse-temurin-21 AS build
WORKDIR /build
COPY target/*.jar transaction-service.jar
COPY src/main/resources/application.yml /usr/src/application.yml
COPY pom.xml .
COPY transaction-service/pom.xml transaction-service/pom.xml
COPY transaction-service/src transaction-service/src
ENTRYPOINT ["java", "-jar", "transaction-service.jar"]
\ No newline at end of file
RUN mvn -f transaction-service/pom.xml clean package -DskipTests
FROM eclipse-temurin:21-jdk@sha256:dd90127baca82663c56ff903156d229a8287fe94a7b0145a31b45fecebdb7be6
WORKDIR /app
COPY --from=build /build/transaction-service/target/*.jar ./app.jar
CMD ["java", "-jar", "app.jar"]
......@@ -74,6 +74,15 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
......
package cz.muni.fi.obs;
import static io.restassured.RestAssured.get;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.actuate.observability.AutoConfigureObservability;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalManagementPort;
import org.springframework.test.context.ActiveProfiles;
@ActiveProfiles("test")
@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
)
@AutoConfigureObservability
public class TransactionServiceTest {
@LocalManagementPort
private int managementPort;
@Test
void healthCheckWorks() {
String healthUrl = "http://localhost:" + managementPort + "/api/transaction-service/actuator/health";
String status = get(healthUrl)
.then()
.extract()
.path("status").toString();
assertThat(status).isEqualTo("UP");
}
}
......@@ -4,6 +4,7 @@ import cz.muni.fi.obs.exceptions.NotFoundResponse;
import cz.muni.fi.obs.exceptions.ResourceNotFoundException;
import cz.muni.fi.obs.exceptions.ValidationErrors;
import cz.muni.fi.obs.exceptions.ValidationFailedResponse;
import org.postgresql.util.PSQLException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
......@@ -50,6 +51,30 @@ public class ControllerAdvice {
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(PSQLException.class)
public ResponseEntity<ValidationFailedResponse> handlePSQLException(PSQLException ex) {
String fieldName = extractFieldName(ex.getMessage());
if (fieldName != null) {
ValidationFailedResponse response = ValidationFailedResponse.builder()
.message("Validation failed")
.validationErrors(
ValidationErrors.builder()
.fieldErrors(Map.of(
fieldName,
"Already used" +
" by " +
"another user - must be unique"
))
.build()
)
.build();
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
} else {
throw new InternalError("Unexpected error occurred.");
}
}
private ValidationErrors getValidationErrors(BindException ex) {
Map<String, String> fieldErrors = new HashMap<>();
for (FieldError fieldError : ex.getFieldErrors()) {
......@@ -63,4 +88,25 @@ public class ControllerAdvice {
return ValidationErrors.builder().fieldErrors(fieldErrors).globalErrors(globalErrors).build();
}
private String extractFieldName(String message) {
String fieldName = null;
if (message.contains("Key (")) {
int start = message.indexOf("Key (") + 5;
int end = message.indexOf(")");
fieldName = message.substring(start, end);
}
// change snake_case to camelCase
if (fieldName != null) {
String[] parts = fieldName.split("_");
StringBuilder camelCase = new StringBuilder(parts[0]);
for (int i = 1; i < parts.length; i++) {
camelCase.append(parts[i].substring(0, 1).toUpperCase()).append(parts[i].substring(1));
}
fieldName = camelCase.toString();
}
return fieldName;
}
}
......@@ -3,6 +3,12 @@ server:
context-path: '/api/transaction-service'
port: 8082
management:
endpoints:
web:
exposure:
include: [ 'prometheus', 'health', 'info' ]
spring:
application:
name: transaction-service
......
......@@ -8,6 +8,3 @@ spring:
hibernate:
ddl-auto: create-drop
database-platform: org.hibernate.dialect.H2Dialect
FROM eclipse-temurin:21-jdk-alpine
FROM maven:3.9.6-eclipse-temurin-21 AS build
WORKDIR /build
COPY target/*.jar user-service.jar
COPY src/main/resources/application.yml /usr/src/application.yml
COPY pom.xml .
COPY user-service/pom.xml user-service/pom.xml
COPY user-service/src user-service/src
ENTRYPOINT ["java", "-jar", "user-service.jar"]
\ No newline at end of file
RUN mvn -f user-service/pom.xml clean package -DskipTests
FROM eclipse-temurin:21-jdk@sha256:dd90127baca82663c56ff903156d229a8287fe94a7b0145a31b45fecebdb7be6
WORKDIR /app
COPY --from=build /build/user-service/target/*.jar ./app.jar
CMD ["java", "-jar", "app.jar"]
......@@ -52,6 +52,15 @@
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
......
......@@ -5,7 +5,7 @@ import jakarta.validation.constraints.NotBlank;
public record AccountCreateDto(
@Schema(description = "Account number", example = "19-2000145399/0800")
@Schema(description = "Account number", example = "19-2000145399/0810")
@NotBlank(message = "Account number is required")
String accountNumber,
......