Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add transaction-API scenario / Classic flavor #1017

Merged
merged 1 commit into from
Jan 30, 2023
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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,13 @@ Base application is reduced to two REST resources:
Tests cover the supported functionality of `rest-data-panache`: CRUD operations, `json` and `hal+json` data types,
invalid input, filtering, sorting, pagination.

### `sql-db/narayana-transactions`

Verifies Quarkus transaction programmatic API.
Base application contains REST resource `TransferResource` and three main services: `TransferTransactionService`, `TransferWithdrawalService`
and `TransferTopUpService` which implement various bank transactions. The main scenario is implemented in `TransactionGeneralUsageIT`
and checks whether transactions and rollbacks always done in full.

### `security/basic`

Verifies the simplest way of doing authn/authz.
Expand Down
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,7 @@
<module>sql-db/hibernate-reactive</module>
<module>sql-db/reactive-vanilla</module>
<module>sql-db/hibernate-fulltext-search</module>
<module>sql-db/narayana-transactions</module>
</modules>
</profile>
<profile>
Expand Down
108 changes: 108 additions & 0 deletions sql-db/narayana-transactions/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.quarkus.ts.qe</groupId>
<artifactId>parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<artifactId>narayana-transactions</artifactId>
<packaging>jar</packaging>
<name>Quarkus QE TS: SQL Database: Narayana-transactions</name>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-mariadb</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-mssql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-mysql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-oracle</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-health</artifactId>
</dependency>
<!-- Swagger documentation -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-openapi</artifactId>
</dependency>
<!-- Tracing extensions -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-opentelemetry</artifactId>
</dependency>
<!-- Metrics extensions -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-micrometer-registry-prometheus</artifactId>
</dependency>
<!-- test extensions -->
<dependency>
<groupId>io.quarkus.qe</groupId>
<artifactId>quarkus-test-service-database</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus.qe</groupId>
<artifactId>quarkus-test-service-jaeger</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
</profile>
<!-- Skipped on Windows as does not support Linux Containers / Testcontainers -->
<profile>
<id>skip-tests-on-windows</id>
<activation>
<os>
<family>windows</family>
</os>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package io.quarkus.ts.transactions;

import java.sql.Timestamp;
import java.util.List;
import java.util.Objects;

import javax.persistence.Column;
import javax.persistence.Entity;

import io.quarkus.hibernate.orm.panache.PanacheEntity;
import io.quarkus.panache.common.Parameters;
import io.quarkus.panache.common.Sort;

@Entity(name = "account")
public class AccountEntity extends PanacheEntity {
@Column(nullable = false)
private String name;
@Column(nullable = false)
private String lastName;
@Column(unique = true, nullable = false)
private String accountNumber;
@Column(precision = 10, scale = 2, nullable = false)
private int amount;
private Timestamp updatedAt;
@Column(nullable = false)
private Timestamp createdAt;

public static boolean exist(String accountNumber) {
return Objects.nonNull(findAccount(accountNumber));
}

public static AccountEntity findAccount(String accountNumber) {
return find("accountNumber", accountNumber).firstResult();
}

public static int updateAmount(String accountNumber, int amount) {
Timestamp currentTime = new Timestamp(System.currentTimeMillis());
int updatedRecordsAmount = update("amount = :amount, updatedAt = :updatedAt where accountNumber = :account",
Parameters.with("amount", amount)
.and("updatedAt", currentTime)
.and("account", accountNumber));
flush();
return updatedRecordsAmount;
}

public static List<AccountEntity> getAllAccountsRecords() {
return findAll(Sort.by("createdAt").descending()).list();
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public int getAmount() {
return amount;
}

public void setAmount(int amount) {
this.amount = amount;
}

public Timestamp getUpdatedAt() {
return updatedAt;
}

public void setUpdatedAt(Timestamp updatedAt) {
this.updatedAt = updatedAt;
}

public Timestamp getCreatedAt() {
return createdAt;
}

public void setCreatedAt(Timestamp createdAt) {
this.createdAt = createdAt;
}

public String getAccountNumber() {
return accountNumber;
}

public void setAccountNumber(String accountNumber) {
this.accountNumber = accountNumber;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package io.quarkus.ts.transactions;

import static io.quarkus.ts.transactions.AccountEntity.exist;

import java.util.List;

import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.NotFoundException;

import org.jboss.logging.Logger;

@ApplicationScoped
public class AccountService {

private static final Logger LOG = Logger.getLogger(AccountService.class);

public boolean isPresent(String accountNumber) {
if (!exist(accountNumber)) {
String msg = String.format("Account %s doesn't exist", accountNumber);
LOG.warn(msg);
throw new NotFoundException(msg);
}

return true;
}

public int increaseBalance(String account, int amount) {
AccountEntity accountEntity = AccountEntity.findAccount(account);
int updatedAmount = accountEntity.getAmount() + amount;
AccountEntity.updateAmount(account, updatedAmount);
return AccountEntity.findAccount(account).getAmount();
}

public int decreaseBalance(String account, int amount) {
AccountEntity accountEntity = AccountEntity.findAccount(account);
int updatedAmount = accountEntity.getAmount() - amount;
if (updatedAmount < 0) {
String msg = String.format("Account %s Not enough balance.", account);
LOG.warn(msg);
throw new BadRequestException(msg);
}
AccountEntity.updateAmount(account, updatedAmount);
return updatedAmount;
}

public List<AccountEntity> getAllAccounts() {
return AccountEntity.getAllAccountsRecords();
}

public AccountEntity getAccount(String accountNumber) {
return AccountEntity.findAccount(accountNumber);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package io.quarkus.ts.transactions;

import java.sql.Timestamp;

import javax.persistence.Column;
import javax.persistence.Entity;

import io.quarkus.hibernate.orm.panache.PanacheEntity;
import io.quarkus.panache.common.Parameters;
import io.quarkus.panache.common.Sort;

@Entity(name = "journal")
public class JournalEntity extends PanacheEntity {

@Column(nullable = false)
private String annotation;
@Column(nullable = false)
private String accountTo;
@Column(nullable = false)
private String accountFrom;
@Column(nullable = false)
private int amount;
@Column(nullable = false)
private Timestamp createdAt;

public JournalEntity() {
}

public JournalEntity(String accountFrom, String accountTo, String annotation, int amount) {
this.accountFrom = accountFrom;
this.accountTo = accountTo;
this.annotation = annotation;
this.amount = amount;
this.createdAt = new Timestamp(System.currentTimeMillis());
}

public JournalEntity addLog() {
persistAndFlush();
return this;
}

public static JournalEntity getLatestJournalRecord(String accountNumber) {
return find("accountFrom = :accountFrom",
Sort.by("createdAt").descending(),
Parameters.with("accountFrom", accountNumber))
.firstResult();
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getAnnotation() {
return annotation;
}

public void setAnnotation(String annotation) {
this.annotation = annotation;
}

public String getAccountTo() {
return accountTo;
}

public void setAccountTo(String accountTo) {
this.accountTo = accountTo;
}

public String getAccountFrom() {
return accountFrom;
}

public void setAccountFrom(String accountFrom) {
this.accountFrom = accountFrom;
}

public int getAmount() {
return amount;
}

public void setAmount(int amount) {
this.amount = amount;
}

public Timestamp getCreatedAt() {
return createdAt;
}

public void setCreatedAt(Timestamp createdAt) {
this.createdAt = createdAt;
}
}
Loading