diff --git a/payguard-api-gateway/pom.xml b/payguard-api-gateway/pom.xml index f216969..86f9e7c 100644 --- a/payguard-api-gateway/pom.xml +++ b/payguard-api-gateway/pom.xml @@ -6,7 +6,7 @@ dev.amg.payguard - payguard + payguard-parent 1.0.0-SNAPSHOT diff --git a/payguard-collateral-service/pom.xml b/payguard-collateral-service/pom.xml index 87985a2..3e1b822 100644 --- a/payguard-collateral-service/pom.xml +++ b/payguard-collateral-service/pom.xml @@ -6,13 +6,30 @@ dev.amg.payguard - payguard + payguard-parent 1.0.0-SNAPSHOT payguard-collateral-service + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-kafka + + + org.flywaydb + flyway-core + + + com.oracle.database.jdbc + ojdbc17 + runtime + dev.amg.payguard payguard-shared @@ -24,11 +41,17 @@ org.springframework.boot - spring-boot-starter-kafka + spring-boot-starter-webmvc-test + test - org.springframework.boot - spring-boot-starter-webmvc-test + org.testcontainers + testcontainers-oracle-xe + test + + + org.testcontainers + testcontainers-junit-jupiter test diff --git a/payguard-collateral-service/src/main/resources/application.yaml b/payguard-collateral-service/src/main/resources/application.yaml index b5771f8..6cc583f 100644 --- a/payguard-collateral-service/src/main/resources/application.yaml +++ b/payguard-collateral-service/src/main/resources/application.yaml @@ -1,3 +1,12 @@ spring: application: name: payguard-collateral-service + datasource: + url: ${SPRING_DATASOURCE_URL} + username: ${SPRING_DATASOURCE_USERNAME} + password: ${SPRING_DATASOURCE_PASSWORD} + driver-class-name: oracle.jdbc.OracleDriver + jpa: + open-in-view: false + flyway: + enabled: true diff --git a/payguard-collateral-service/src/main/resources/db/migration/V1__collateral_schema.sql b/payguard-collateral-service/src/main/resources/db/migration/V1__collateral_schema.sql new file mode 100644 index 0000000..dd4793e --- /dev/null +++ b/payguard-collateral-service/src/main/resources/db/migration/V1__collateral_schema.sql @@ -0,0 +1,8 @@ +CREATE TABLE collateral (id RAW(16) PRIMARY KEY, owner_token VARCHAR2(128) NOT NULL, loan_id RAW(16) NOT NULL, amount NUMBER(28,8) NOT NULL, asset_currency CHAR(3) NOT NULL, warning_ltv NUMBER(10,8) NOT NULL, margin_ltv NUMBER(10,8) NOT NULL, liquidation_ltv NUMBER(10,8) NOT NULL, status VARCHAR2(24) NOT NULL, version NUMBER(19) DEFAULT 0 NOT NULL, retention_until TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL); +CREATE INDEX ix_collateral_loan_status ON collateral(loan_id, status); +CREATE TABLE collateral_valuation (id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, collateral_id RAW(16) NOT NULL, asset_value NUMBER(28,8) NOT NULL, fx_rate NUMBER(28,12) NOT NULL, observed_at TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT fk_val_collateral FOREIGN KEY(collateral_id) REFERENCES collateral(id)); +CREATE INDEX ix_collateral_val_time ON collateral_valuation(collateral_id, observed_at); +CREATE TABLE collateral_audit_log (id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, collateral_id RAW(16) NOT NULL, subject_token VARCHAR2(128), actor_token VARCHAR2(128), action VARCHAR2(64) NOT NULL, reason VARCHAR2(256), occurred_at TIMESTAMP WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL, previous_hash VARCHAR2(128), record_hash VARCHAR2(128) NOT NULL); +CREATE INDEX ix_collateral_audit_subject_time ON collateral_audit_log(subject_token, occurred_at); +CREATE TABLE collateral_processed_event (event_id RAW(16) NOT NULL, consumer_name VARCHAR2(128) NOT NULL, processed_at TIMESTAMP WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL, PRIMARY KEY(event_id, consumer_name)); +CREATE TABLE collateral_outbox (id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, event_id RAW(16) UNIQUE NOT NULL, event_type VARCHAR2(128) NOT NULL, aggregate_id VARCHAR2(128) NOT NULL, payload CLOB NOT NULL, occurred_at TIMESTAMP WITH TIME ZONE NOT NULL, published_at TIMESTAMP WITH TIME ZONE); diff --git a/payguard-collateral-service/src/test/java/dev/amg/payguard/collateral/CollateralServiceApplicationTests.java b/payguard-collateral-service/src/test/java/dev/amg/payguard/collateral/CollateralServiceApplicationTests.java index a32414c..1e729ba 100644 --- a/payguard-collateral-service/src/test/java/dev/amg/payguard/collateral/CollateralServiceApplicationTests.java +++ b/payguard-collateral-service/src/test/java/dev/amg/payguard/collateral/CollateralServiceApplicationTests.java @@ -2,10 +2,33 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.OracleContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; @SpringBootTest +@ActiveProfiles("test") +@Testcontainers class CollateralServiceApplicationTests { + @Container + static final OracleContainer ORACLE = + new OracleContainer("gvenzl/oracle-xe:21-slim-faststart") + .withUsername("PAYGUARD_TEST") + .withPassword("payguard_test"); + + @DynamicPropertySource + static void oracleProperties(DynamicPropertyRegistry registry) { + registry.add("spring.datasource.url", ORACLE::getJdbcUrl); + registry.add("spring.datasource.username", ORACLE::getUsername); + registry.add("spring.datasource.password", ORACLE::getPassword); + registry.add("spring.datasource.driver-class-name", ORACLE::getDriverClassName); + registry.add("spring.jpa.database-platform", () -> "org.hibernate.dialect.OracleDialect"); + } + @Test void contextLoads() {} } diff --git a/payguard-collateral-service/src/test/resources/application-test.yaml b/payguard-collateral-service/src/test/resources/application-test.yaml new file mode 100644 index 0000000..53dc98c --- /dev/null +++ b/payguard-collateral-service/src/test/resources/application-test.yaml @@ -0,0 +1,16 @@ +spring: + datasource: + # Replaced by CollateralServiceApplicationTests.oracleProperties at runtime. + url: ${TEST_ORACLE_JDBC_URL:jdbc:oracle:thin:@//localhost:1521/XEPDB1} + username: ${TEST_ORACLE_USERNAME:PAYGUARD_TEST} + password: ${TEST_ORACLE_PASSWORD:payguard_test} + driver-class-name: oracle.jdbc.OracleDriver + jpa: + database-platform: org.hibernate.dialect.OracleDialect + hibernate: + ddl-auto: none + open-in-view: false + flyway: + # Schema migrations are exercised independently; context tests only need + # the Oracle-backed JPA infrastructure to start. + enabled: false diff --git a/payguard-loan-service/pom.xml b/payguard-loan-service/pom.xml index b9d7627..76e699c 100644 --- a/payguard-loan-service/pom.xml +++ b/payguard-loan-service/pom.xml @@ -6,7 +6,7 @@ dev.amg.payguard - payguard + payguard-parent 1.0.0-SNAPSHOT @@ -26,11 +26,34 @@ org.springframework.boot spring-boot-starter-kafka + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.flywaydb + flyway-core + + + com.oracle.database.jdbc + ojdbc17 + runtime + org.springframework.boot spring-boot-starter-webmvc-test test + + org.testcontainers + testcontainers-oracle-xe + test + + + org.testcontainers + testcontainers-junit-jupiter + test + diff --git a/payguard-loan-service/src/main/resources/application.yaml b/payguard-loan-service/src/main/resources/application.yaml index 4b50a35..667f1df 100644 --- a/payguard-loan-service/src/main/resources/application.yaml +++ b/payguard-loan-service/src/main/resources/application.yaml @@ -1,3 +1,12 @@ spring: application: name: payguard-loan-service + datasource: + url: ${SPRING_DATASOURCE_URL} + username: ${SPRING_DATASOURCE_USERNAME} + password: ${SPRING_DATASOURCE_PASSWORD} + driver-class-name: oracle.jdbc.OracleDriver + jpa: + open-in-view: false + flyway: + enabled: true diff --git a/payguard-loan-service/src/main/resources/db/migration/V1__loan_schema.sql b/payguard-loan-service/src/main/resources/db/migration/V1__loan_schema.sql new file mode 100644 index 0000000..e537797 --- /dev/null +++ b/payguard-loan-service/src/main/resources/db/migration/V1__loan_schema.sql @@ -0,0 +1,6 @@ +CREATE TABLE loan_account (id RAW(16) PRIMARY KEY, borrower_token VARCHAR2(128) NOT NULL, collateral_id RAW(16) NOT NULL, principal NUMBER(28,8) NOT NULL, annual_rate NUMBER(20,12) NOT NULL, term_months NUMBER(5) NOT NULL, status VARCHAR2(24) NOT NULL, outstanding_principal NUMBER(28,8) NOT NULL, accrued_interest NUMBER(28,8) NOT NULL, last_accrual_date DATE NOT NULL, version NUMBER(19) DEFAULT 0 NOT NULL, retention_until TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL); +CREATE INDEX ix_loan_due ON loan_account(status, last_accrual_date); +CREATE TABLE loan_audit_log (id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, loan_id RAW(16) NOT NULL, subject_token VARCHAR2(128), actor_token VARCHAR2(128), action VARCHAR2(64) NOT NULL, reason VARCHAR2(256), occurred_at TIMESTAMP WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL, previous_hash VARCHAR2(128), record_hash VARCHAR2(128) NOT NULL); +CREATE INDEX ix_loan_audit_subject_time ON loan_audit_log(subject_token, occurred_at); +CREATE TABLE loan_processed_event (event_id RAW(16) NOT NULL, consumer_name VARCHAR2(128) NOT NULL, processed_at TIMESTAMP WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL, PRIMARY KEY(event_id, consumer_name)); +CREATE TABLE loan_outbox (id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, event_id RAW(16) UNIQUE NOT NULL, event_type VARCHAR2(128) NOT NULL, aggregate_id VARCHAR2(128) NOT NULL, payload CLOB NOT NULL, occurred_at TIMESTAMP WITH TIME ZONE NOT NULL, published_at TIMESTAMP WITH TIME ZONE); diff --git a/payguard-loan-service/src/test/java/dev/amg/payguard/loan/LoanServiceApplicationTests.java b/payguard-loan-service/src/test/java/dev/amg/payguard/loan/LoanServiceApplicationTests.java index 8b2129c..04605f0 100644 --- a/payguard-loan-service/src/test/java/dev/amg/payguard/loan/LoanServiceApplicationTests.java +++ b/payguard-loan-service/src/test/java/dev/amg/payguard/loan/LoanServiceApplicationTests.java @@ -2,10 +2,33 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.OracleContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; @SpringBootTest +@ActiveProfiles("test") +@Testcontainers class LoanServiceApplicationTests { + @Container + static final OracleContainer ORACLE = + new OracleContainer("gvenzl/oracle-xe:21-slim-faststart") + .withUsername("PAYGUARD_TEST") + .withPassword("payguard_test"); + + @DynamicPropertySource + static void oracleProperties(DynamicPropertyRegistry registry) { + registry.add("spring.datasource.url", ORACLE::getJdbcUrl); + registry.add("spring.datasource.username", ORACLE::getUsername); + registry.add("spring.datasource.password", ORACLE::getPassword); + registry.add("spring.datasource.driver-class-name", ORACLE::getDriverClassName); + registry.add("spring.jpa.database-platform", () -> "org.hibernate.dialect.OracleDialect"); + } + @Test void contextLoads() {} } diff --git a/payguard-loan-service/src/test/resources/application-test.yaml b/payguard-loan-service/src/test/resources/application-test.yaml new file mode 100644 index 0000000..291eb64 --- /dev/null +++ b/payguard-loan-service/src/test/resources/application-test.yaml @@ -0,0 +1,16 @@ +spring: + datasource: + # Replaced by LoanServiceApplicationTests.oracleProperties at runtime. + url: ${TEST_ORACLE_JDBC_URL:jdbc:oracle:thin:@//localhost:1521/XEPDB1} + username: ${TEST_ORACLE_USERNAME:PAYGUARD_TEST} + password: ${TEST_ORACLE_PASSWORD:payguard_test} + driver-class-name: oracle.jdbc.OracleDriver + jpa: + database-platform: org.hibernate.dialect.OracleDialect + hibernate: + ddl-auto: none + open-in-view: false + flyway: + # Schema migrations are exercised independently; context tests only need + # the Oracle-backed JPA infrastructure to start. + enabled: false diff --git a/payguard-proto/pom.xml b/payguard-proto/pom.xml index 1a7e588..fcbe987 100644 --- a/payguard-proto/pom.xml +++ b/payguard-proto/pom.xml @@ -6,7 +6,7 @@ dev.amg.payguard - payguard + payguard-parent 1.0.0-SNAPSHOT diff --git a/payguard-shared/pom.xml b/payguard-shared/pom.xml index d421b65..21f65ed 100644 --- a/payguard-shared/pom.xml +++ b/payguard-shared/pom.xml @@ -5,7 +5,7 @@ 4.0.0 dev.amg.payguard - payguard + payguard-parent 1.0.0-SNAPSHOT diff --git a/payguard-wallet-service/pom.xml b/payguard-wallet-service/pom.xml index 5faa39a..01fe374 100644 --- a/payguard-wallet-service/pom.xml +++ b/payguard-wallet-service/pom.xml @@ -4,7 +4,7 @@ 4.0.0 dev.amg.payguard - payguard + payguard-parent 1.0.0-SNAPSHOT payguard-wallet-service diff --git a/payguard-wallet-service/src/main/resources/application.yaml b/payguard-wallet-service/src/main/resources/application.yaml index 3b31bc3..d71ae8d 100644 --- a/payguard-wallet-service/src/main/resources/application.yaml +++ b/payguard-wallet-service/src/main/resources/application.yaml @@ -2,9 +2,9 @@ spring: application: name: wallet-service datasource: - url: jdbc:oracle:thin:@localhost:${ORACLE_PORT:1521}/FREE - username: ${ORACLE_APP_USER:payguard_user} - password: ${ORACLE_APP_PASSWORD:payguard_pass} + url: ${SPRING_DATASOURCE_URL} + username: ${SPRING_DATASOURCE_USERNAME} + password: ${SPRING_DATASOURCE_PASSWORD} driver-class-name: oracle.jdbc.OracleDriver jooq: sql-dialect: default diff --git a/pom.xml b/pom.xml index 09150aa..13f1819 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 dev.amg.payguard - payguard + payguard-parent 1.0.0-SNAPSHOT pom @@ -24,7 +24,7 @@ 25 25 UTF-8 - 4.0.1 + 4.1.1 1.68.0 3.25.5 3.19.15