Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
7c75de0
refactor(entity): replace lombok required args constructor
eggy03 Feb 28, 2026
9e729cf
build(test): set up test environment and add assertj
eggy03 Feb 28, 2026
fe0d2be
test(audit-log-registration): add integration tests for audit log reg…
eggy03 Feb 28, 2026
8e0f84e
test(message-log-registration): add integration tests for message log…
eggy03 Mar 1, 2026
2bbff27
refactor(dto): add lombok constructor annotations
eggy03 Mar 1, 2026
6b9cade
refactor(api): use smallrye NotNull annotation
eggy03 Mar 1, 2026
d39713f
refactor(message-locks): introduce configurable message operations
eggy03 Mar 1, 2026
3b2b5d8
test(message-log-content): add integration tests for `MessageLogConte…
eggy03 Mar 1, 2026
3e8ca42
refactor(locks): revert logging level for save lock operations
eggy03 Mar 1, 2026
af8ae9e
refactor(test): introduce constants for message log content test data
eggy03 Mar 1, 2026
2323d64
test(integration-tests): refactor log registration validation tests
eggy03 Mar 1, 2026
2b293cb
test(integration): remove 404 tests for invalid long parameters
eggy03 Mar 1, 2026
85e4f8a
config: set test logging level to `INFO`
eggy03 Mar 1, 2026
2d63093
build: skip tests during build
eggy03 Mar 1, 2026
0154e2f
chore: update quarkus platform version
eggy03 Mar 2, 2026
0fee155
chore: remove h2 dependency
eggy03 Mar 2, 2026
dc29b4f
chore(dev-config): activate quarkus devservices
eggy03 Mar 2, 2026
a33ebcb
refactor(service): standardize delete operation error handling
eggy03 Mar 2, 2026
21a5b48
refactor(integration-tests): rename integration test files and classes
eggy03 Mar 2, 2026
acca633
build: add quarkus junit mockito test dependency
eggy03 Mar 2, 2026
0901051
test(audit-log-registration-service): add unit tests
eggy03 Mar 2, 2026
9dcbf09
fix: apply missed out optimizations in a33ebcb
eggy03 Mar 2, 2026
d9f4a2d
test(message-log-registration-service): add unit tests
eggy03 Mar 2, 2026
c9f4044
Merge branch 'main' into develop-tests
eggy03 Mar 2, 2026
57a3922
refactor(exceptions): rename exception and mapper classes for clarity
eggy03 Mar 2, 2026
08539a3
test(message-log-content-service): add unit tests
eggy03 Mar 2, 2026
bd7c3ba
docs: relocate migration guide
eggy03 Mar 2, 2026
1b19cbc
build(deps): update quarkus platform and project version
eggy03 Mar 2, 2026
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
2 changes: 1 addition & 1 deletion Dockerfile.native
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ COPY src ./src

# Build native binary
# quarkus defaults to prod
RUN ./mvnw package -Dnative "-Dquarkus.native.additional-build-args=-J-Djava.net.preferIPv4Stack=true"
RUN ./mvnw clean package -DskipTests -Dnative "-Dquarkus.native.additional-build-args=-J-Djava.net.preferIPv4Stack=true"


# ---------- Stage 2: Minimal Runtime ----------
Expand Down
49 changes: 26 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ API service for the PaperTrail Bot, built with Quarkus 3 and optimized for nativ

### Services Required

| Service | Version |
|-----------------------|----------------|
| `Relational Database` | Postgres |
| `Distributed Cache` | Redis / Valkey |
| Service Type | Supported Variants |
|-----------------------|--------------------|
| `Relational Database` | Postgres |
| `Distributed Cache` | Redis / Valkey |

It is recommended that you deploy the latest or the officially supported versions of Postgres and Redis or Valkey.
At the time of development, Postgres 17 and 18 and Valkey 8 were fully supported.
Expand All @@ -35,25 +35,6 @@ Below are the links to the official docs stating the support status of each of t
- [Redis Supported Versions](https://redis.io/docs/latest/operate/rs/references/supported-platforms/)
- [Valkey Supported Versions](https://valkey.io/topics/releases/)

> [!IMPORTANT]
> This section applies only to users migrating from the Spring-based API to this API.
>
> Depending on your existing database setup, you may encounter up to **two** breaking changes:
>
> **Case-1**: Using a database other than PostgreSQL
>
> You need to migrate your existing data to a newly created Postgres DB.
> This API exclusively supports Postgres. Support for other DBs have been dropped to ease maintainability.
>
> **Case-2**: Already using Postgres
>
> There is only **one** breaking change:
> - Previously, tables were created in the default schema.
> - The new API uses flyway to check and create tables in a custom schema named `papertrailbot` on startup.
>
> The table structures and relationships remain unchanged.
> You only need to migrate your existing data from the default schema to the `papertrailbot` schema.

### Environment Variables Required

| Variable | Description |
Expand Down Expand Up @@ -156,6 +137,28 @@ Some cloud platforms may require these endpoints to periodically determine the h

> /q/health - Accumulates all health check procedures in the application.

# Migration Guide

> [!NOTE]
> This section applies only to users migrating from the Spring-based API.

Depending on your existing database setup, you may encounter up to **two** breaking changes:

**Case-1**: Using a database other than PostgreSQL

You need to migrate your existing data to a newly created Postgres DB.
This API exclusively supports Postgres. Support for other DBs have been dropped to ease maintainability.

**Case-2**: Already using Postgres

There is only **one** breaking change:

- Previously, tables were created in the default schema.
- The new API uses flyway to check and create tables in a custom schema named `papertrailbot` on startup.

The table structures and relationships remain unchanged.
You only need to migrate your existing data from the default schema to the `papertrailbot` schema.

# License

This API is licensed under the [AGPLv3](/LICENSE) license.
Expand Down
21 changes: 14 additions & 7 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,17 @@

<groupId>io.github.eggy03</groupId>
<artifactId>papertrail-api-quarkus</artifactId>
<version>1.0.0-SNAPSHOT</version>
<version>1.0.0</version>
<packaging>quarkus</packaging>

<properties>

<maven.compiler.release>25</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>3.31.3</quarkus.platform.version>
<quarkus.platform.version>3.32.1</quarkus.platform.version>
<skipITs>true</skipITs>

<compiler-plugin.version>3.14.1</compiler-plugin.version>
Expand All @@ -25,6 +24,7 @@
<commons.lang3.version>3.20.0</commons.lang3.version>
<lombok.version>1.18.42</lombok.version>
<redisson.quarkus.version>4.2.0</redisson.quarkus.version>
<assertj.core.version>3.27.7</assertj.core.version>
</properties>

<dependencyManagement>
Expand Down Expand Up @@ -68,10 +68,6 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-h2</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
Expand Down Expand Up @@ -141,11 +137,22 @@
<artifactId>quarkus-junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit-mockito</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj.core.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.github.eggy03.papertrail.api.controller;

import io.github.eggy03.papertrail.api.dto.MessageLogContentDTO;
import io.github.eggy03.papertrail.api.service.locks.MessageLogContentLockingService;
import io.github.eggy03.papertrail.api.service.locks.MessageLogContentOperation;
import io.smallrye.common.annotation.RunOnVirtualThread;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
Expand All @@ -25,7 +25,7 @@
@RequiredArgsConstructor
public class MessageLogContentController {

private final MessageLogContentLockingService service; // accessing the MessageLogContentService behind Redisson locks
private final MessageLogContentOperation service;

@POST
public Response saveMessage(@Valid MessageLogContentDTO dto) {
Expand All @@ -46,7 +46,7 @@ public Response getMessage(@PathParam("messageId") @Positive @NotNull Long messa
@PUT
public Response updateMessage(@Valid MessageLogContentDTO dto) {
return Response
.ok(service.updateMessage(dto))
.ok(service.updateMessage(dto.getMessageId(), dto))
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class AuditLogRegistrationDTO {

@NotNull(message = "GuildID cannot be null")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data

@NoArgsConstructor
@AllArgsConstructor
public class MessageLogContentDTO {

@NotNull(message = "MessageID cannot be null")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class MessageLogRegistrationDTO {

@NotNull(message = "GuildID cannot be null")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@Entity
@Getter
@Setter
@ToString
@RequiredArgsConstructor
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "audit_log_table")
public class AuditLogRegistration {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.CreationTimestamp;
Expand All @@ -16,7 +17,8 @@
@Getter
@Setter
@ToString
@RequiredArgsConstructor
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "message_log_content_table")
public class MessageLogContent {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@Entity
@Getter
@Setter
@ToString
@RequiredArgsConstructor
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "message_log_registration_table")
public class MessageLogRegistration {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
import lombok.experimental.StandardException;

@StandardException
public class MessageContentException extends RuntimeException {
public class GuildRegistrationFailureException extends RuntimeException {
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
import lombok.experimental.StandardException;

@StandardException
public class GuildRegistrationException extends RuntimeException {
public class MessageSaveFailureException extends RuntimeException {
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.github.eggy03.papertrail.api.exceptions.mapper;

import io.github.eggy03.papertrail.api.exceptions.GuildRegistrationException;
import io.github.eggy03.papertrail.api.exceptions.GuildRegistrationFailureException;
import io.github.eggy03.papertrail.api.exceptions.entity.ErrorResponse;
import io.github.eggy03.papertrail.api.util.AnsiColor;
import jakarta.ws.rs.core.Context;
Expand All @@ -14,13 +14,13 @@

@Provider
@Slf4j
public class GuildRegistrationExceptionMapper implements ExceptionMapper<GuildRegistrationException> {
public class GuildRegistrationFailureExceptionMapper implements ExceptionMapper<GuildRegistrationFailureException> {

@Context
UriInfo uriInfo;

@Override
public Response toResponse(GuildRegistrationException e) {
public Response toResponse(GuildRegistrationFailureException e) {

log.debug(AnsiColor.MAGENTA + "{}" + AnsiColor.RESET, e.getMessage(), e);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.github.eggy03.papertrail.api.exceptions.mapper;

import io.github.eggy03.papertrail.api.exceptions.MessageContentException;
import io.github.eggy03.papertrail.api.exceptions.MessageSaveFailureException;
import io.github.eggy03.papertrail.api.exceptions.entity.ErrorResponse;
import io.github.eggy03.papertrail.api.util.AnsiColor;
import jakarta.ws.rs.core.Context;
Expand All @@ -14,13 +14,13 @@

@Provider
@Slf4j
public class MessageContentExceptionMapper implements ExceptionMapper<MessageContentException> {
public class MessageSaveFailureExceptionMapper implements ExceptionMapper<MessageSaveFailureException> {

@Context
UriInfo uriInfo;

@Override
public Response toResponse(MessageContentException e) {
public Response toResponse(MessageSaveFailureException e) {

log.debug(AnsiColor.MAGENTA + "{}" + AnsiColor.RESET, e.getMessage(), e);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,21 @@
import io.github.eggy03.papertrail.api.dto.AuditLogRegistrationDTO;
import io.github.eggy03.papertrail.api.entity.AuditLogRegistration;
import io.github.eggy03.papertrail.api.exceptions.GuildNotFoundException;
import io.github.eggy03.papertrail.api.exceptions.GuildRegistrationException;
import io.github.eggy03.papertrail.api.exceptions.GuildRegistrationFailureException;
import io.github.eggy03.papertrail.api.mapper.AuditLogRegistrationMapper;
import io.github.eggy03.papertrail.api.repository.AuditLogRegistrationRepository;
import io.github.eggy03.papertrail.api.util.AnsiColor;
import io.quarkus.cache.CacheInvalidate;
import io.quarkus.cache.CacheKey;
import io.quarkus.cache.CacheResult;
import io.smallrye.common.constraint.NotNull;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.transaction.Transactional;
import jakarta.validation.constraints.NotNull;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.exception.ConstraintViolationException;


@ApplicationScoped
@RequiredArgsConstructor
@Slf4j
Expand All @@ -35,7 +34,7 @@ public class AuditLogRegistrationService {
log.debug("{}Saved audit log guild with ID={}{}", AnsiColor.GREEN, dto.getGuildId(), AnsiColor.RESET);
return dto;
} catch (ConstraintViolationException e) { // from hibernate
throw new GuildRegistrationException(e);
throw new GuildRegistrationFailureException(e);
}
}

Expand Down Expand Up @@ -69,13 +68,9 @@ public class AuditLogRegistrationService {
@CacheInvalidate(cacheName = "auditLog")
public void deleteRegisteredGuild(@NonNull @CacheKey Long guildId) {

repository
.findByIdOptional(guildId)
.orElseThrow(() -> new GuildNotFoundException("Guild is not registered for audit logging"));

if (repository.deleteById(guildId))
log.debug("{}Deleted audit log guild with ID={}{}", AnsiColor.GREEN, guildId, AnsiColor.RESET);
else
log.warn("{}Failed to delete audit log guild with ID={}{}", AnsiColor.YELLOW, guildId, AnsiColor.RESET);
throw new GuildNotFoundException("Guild is not registered for audit logging");
}
}
Loading
Loading