Skip to content
Snippets Groups Projects
Commit 93a46629 authored by Pavel Tetauer's avatar Pavel Tetauer
Browse files

Add persistance layer

parent d90bc5ed
No related branches found
No related tags found
1 merge request!30Analytics persistance
Showing
with 219 additions and 102 deletions
......@@ -57,6 +57,35 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-database-postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.19.7</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.19.6</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
......
......@@ -2,8 +2,10 @@ package cz.muni.fi.obs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@SpringBootApplication
@EnableJpaRepositories
public class AnalyticsManagement {
public static void main(String[] args) {
......
package cz.muni.fi.obs.data;
import cz.muni.fi.obs.data.dbo.DailyTransaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Repository
public class AnalyticsRepository {
private final DataStore dataStore;
@Autowired
public AnalyticsRepository(DataStore dataStore) {
this.dataStore = dataStore;
}
public List<DailyTransaction> getDailyTransactions(String accountNumber, int year, int month) {
Stream<DailyTransaction> transactionStream = dataStore.transactions.stream()
.filter(transaction -> transaction.getAccount().getAccountNumber().equals(accountNumber))
.filter(transaction -> transaction.getDate().getYear() == year)
.filter(transaction -> transaction.getDate().getMonth() == month);
return transactionStream.toList();
public interface AnalyticsRepository extends JpaRepository<DailyTransaction, String>{
@Query("SELECT dt FROM DailyTransaction dt WHERE dt.account.accountNumber = ?1 AND dt.date.year = ?2 AND dt.date.month = ?3")
default List<DailyTransaction> getDailyTransactions(String accountNumber, int year, int month){
return findAll();
}
}
package cz.muni.fi.obs.data;
import cz.muni.fi.obs.data.dbo.Account;
import cz.muni.fi.obs.data.dbo.DailyTransaction;
import cz.muni.fi.obs.data.dbo.Date;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@Component
public class DataStore {
public List<DailyTransaction> transactions = new ArrayList<>();
private static final Account account = new Account("1234567890");
public DataStore(){
DailyTransaction transaction1 = new DailyTransaction(
5,
5,
new BigDecimal(30000),
new BigDecimal(20000),
new BigDecimal(10000),
new BigDecimal(4000),
new BigDecimal(2000),
account,
new Date(2021, 1, 1)
);
DailyTransaction transaction2 = new DailyTransaction(
10,
10,
new BigDecimal(40000),
new BigDecimal(20000),
new BigDecimal(20000),
new BigDecimal(2000),
new BigDecimal(2000),
account,
new Date(2021, 1, 2)
);
DailyTransaction transaction3 = new DailyTransaction(
4,
2,
new BigDecimal(10000),
new BigDecimal(8000),
new BigDecimal(2000),
new BigDecimal(2000),
new BigDecimal(1000),
account,
new Date(2021, 1, 3)
);
transactions.add(transaction1);
transactions.add(transaction2);
transactions.add(transaction3);
}
}
package cz.muni.fi.obs.data.dbo;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import jakarta.persistence.*;
@Getter
@Setter
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "account")
public class Account extends Dbo{
@Column(nullable = false, unique = true)
String accountNumber;
public Account(String accountNumber) {
this.accountNumber = accountNumber;
}
}
package cz.muni.fi.obs.data.dbo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "daily_transaction")
public class DailyTransaction extends Dbo {
@Column(nullable = false)
Integer totalWithdrawalTransactions;
@Column(nullable = false)
Integer totalDepositTransactions;
@Column(nullable = false)
BigDecimal totalTransactionAmount;
@Column(nullable = false)
BigDecimal totalWithdrawalAmount;
@Column(nullable = false)
BigDecimal totalDepositAmount;
@Column(nullable = false)
BigDecimal averageWithdrawalAmount;
@Column(nullable = false)
BigDecimal averageDepositAmount;
Account account;
Date date;
@ManyToOne
Account account;
public DailyTransaction(Integer totalWithdrawalTransactions, Integer totalDepositTransactions, BigDecimal totalTransactionAmount, BigDecimal totalWithdrawalAmount, BigDecimal totalDepositAmount, BigDecimal averageWithdrawalAmount, BigDecimal averageDepositAmount, Account account, Date date) {
this.totalWithdrawalTransactions = totalWithdrawalTransactions;
this.totalDepositTransactions = totalDepositTransactions;
this.totalTransactionAmount = totalTransactionAmount;
this.totalWithdrawalAmount = totalWithdrawalAmount;
this.totalDepositAmount = totalDepositAmount;
this.averageWithdrawalAmount = averageWithdrawalAmount;
this.averageDepositAmount = averageDepositAmount;
this.account = account;
this.date = date;
}
@ManyToOne
Date date;
}
package cz.muni.fi.obs.data.dbo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.LocalDate;
@Getter
@Setter
public class Date {
@Entity
@NoArgsConstructor
@Table(name = "date")
public class Date extends Dbo{
@Column(nullable = false)
int year;
@Column(nullable = false)
int month;
@Column(nullable = false)
int day;
@Column(nullable = false)
LocalDate date;
public Date(int year, int month, int day) {
......
package cz.muni.fi.obs.data.dbo;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import java.util.UUID;
@Getter
@MappedSuperclass
@EqualsAndHashCode(of = "id")
public abstract class Dbo {
@Id
private final String id = UUID.randomUUID().toString();
}
server:
servlet:
context-path: '/api/analytics-service'
\ No newline at end of file
context-path: '/api/analytics-service'
spring:
application:
name: analytics-service
datasource:
url: jdbc:postgresql://localhost:5432/analytics_db
username: analytics_service
password: changemelater
driver-class-name: org.postgresql.Driver
jpa:
hibernate:
ddl-auto: validate
flyway:
locations: db/migration
\ No newline at end of file
DROP TABLE IF EXISTS daily_transaction;
DROP TABLE IF EXISTS account;
DROP TABLE IF EXISTS date;
CREATE TABLE account (
id varchar(40) PRIMARY KEY,
account_number varchar(40) NOT NULL
);
CREATE TABLE date (
id varchar(40) PRIMARY KEY,
year INTEGER NOT NULL,
month INTEGER NOT NULL,
day INTEGER NOT NULL,
date DATE NOT NULL
);
CREATE TABLE daily_transaction (
id varchar(40) PRIMARY KEY,
total_withdrawal_transactions INTEGER NOT NULL,
total_deposit_transactions INTEGER NOT NULL,
total_transaction_amount numeric (38, 2) NOT NULL,
total_withdrawal_amount numeric (38, 2) NOT NULL,
total_deposit_amount numeric (38, 2) NOT NULL,
average_withdrawal_amount numeric (38, 2) NOT NULL,
average_deposit_amount numeric (38, 2) NOT NULL,
account_id varchar(40) NOT NULL,
date_id varchar(40) NOT NULL,
constraint fk_account_id foreign key (account_id) references account(id),
constraint fk_date_id foreign key (date_id) references date(id)
);
INSERT INTO account (id, account_number) VALUES ('1', '1234567890');
INSERT INTO date (id, year, month, day, date) VALUES ('1', 2021, 1, 1, '2021-01-01');
INSERT INTO date (id, year, month, day, date) VALUES ('2', 2021, 1, 2, '2021-01-02');
INSERT INTO daily_transaction (id, total_withdrawal_transactions, total_deposit_transactions, total_transaction_amount, total_withdrawal_amount, total_deposit_amount, average_withdrawal_amount, average_deposit_amount, account_id, date_id) VALUES ('1', 1, 1, 100.00, 50.00, 50.00, 50.00, 50.00, '1', '1');
INSERT INTO daily_transaction (id, total_withdrawal_transactions, total_deposit_transactions, total_transaction_amount, total_withdrawal_amount, total_deposit_amount, average_withdrawal_amount, average_deposit_amount, account_id, date_id) VALUES ('2', 2, 2, 200.00, 100.00, 100.00, 50.00, 50.00, '1', '2');
package cz.muni.fi.obs.common;
import cz.muni.fi.obs.AnalyticsManagement;
import cz.muni.fi.obs.config.JpaRepositoryConfig;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
@SpringBootTest(classes = AnalyticsManagement.class)
@ContextConfiguration(initializers = {PostgresqlTest.Initializer.class}, classes = {AnalyticsManagement.class, JpaRepositoryConfig.class})
@Testcontainers
@DirtiesContext
public abstract class PostgresqlTest {
@Container
protected static PostgreSQLContainer<?> postgreSQLContainer = new PostgreSQLContainer<>("postgres:16.2")
.withDatabaseName("analytics_db")
.withUsername("analytics_service")
.withPassword("changemelater");
static class Initializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
TestPropertyValues.of(
"spring.datasource.url=" + postgreSQLContainer.getJdbcUrl(),
"spring.datasource.username=" + postgreSQLContainer.getUsername(),
"spring.datasource.password=" + postgreSQLContainer.getPassword()
).applyTo(configurableApplicationContext.getEnvironment());
}
}
}
\ No newline at end of file
package cz.muni.fi.obs.common;
public class RepositoryDataProvider {
}
package cz.muni.fi.obs.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableJpaRepositories
@EnableTransactionManagement
public class JpaRepositoryConfig {
}
\ No newline at end of file
package cz.muni.fi.obs.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import cz.muni.fi.obs.AnalyticsManagement;
import cz.muni.fi.obs.api.DailySummaryRequest;
import cz.muni.fi.obs.api.DailySummaryResult;
import cz.muni.fi.obs.api.MonthlySummaryRequest;
import cz.muni.fi.obs.api.MonthlySummaryResult;
import cz.muni.fi.obs.facade.AnalyticsFacade;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import java.time.LocalDate;
......@@ -23,6 +27,8 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(controllers = AnalyticsController.class)
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {AnalyticsManagement.class})
class AnalyticsControllerTest {
@MockBean
......
......@@ -2,13 +2,15 @@ package cz.muni.fi.obs.service;
import cz.muni.fi.obs.api.DailySummaryResult;
import cz.muni.fi.obs.api.MonthlySummaryResult;
import cz.muni.fi.obs.common.PostgresqlTest;
import cz.muni.fi.obs.common.RepositoryDataProvider;
import cz.muni.fi.obs.data.AnalyticsRepository;
import cz.muni.fi.obs.data.DataStore;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.test.context.SpringBootTest;
import java.math.BigDecimal;
import java.time.Month;
......@@ -20,9 +22,8 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class AnalyticsServiceTest {
private final DataStore dataStore = new DataStore();
@SpringBootTest(classes = RepositoryDataProvider.class)
class AnalyticsServiceTest extends PostgresqlTest {
@Mock
private AnalyticsRepository analyticsRepository;
......@@ -44,7 +45,7 @@ class AnalyticsServiceTest {
@Test
void getDailySummary_withFactsPresent_createsCorrectSummary() {
when(analyticsRepository.getDailyTransactions(any(String.class), any(Integer.class), any(Integer.class)))
.thenReturn(dataStore.transactions);
.thenReturn(null);
DailySummaryResult dailySummaryResult = analyticsService.getDailySummary("1234567890", 2023, 10);
......@@ -74,7 +75,7 @@ class AnalyticsServiceTest {
@Test
void getMonthlySummary_withFactsPresent_createsCorrectSummary() {
when(analyticsRepository.getDailyTransactions(any(String.class), any(Integer.class), any(Integer.class)))
.thenReturn(dataStore.transactions);
.thenReturn(null);
MonthlySummaryResult monthlySummary = analyticsService.getMonthlySummary("1234567890", 2021, 1);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment