-
-
Notifications
You must be signed in to change notification settings - Fork 0
GH-137 Refactor project #137
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
Conversation
…olderUtil.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…erRepositoryOrmLite.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
Krótki, ale myślę znaczący review wobec głównej logiki architektury wobec mojej opinii. -> UserServiceImpl można rozbić na osobne komponenty, izolując odpowiednie funkcjonalności. Aktualnie klasa łączy cachowanie, logikę biznesową i komunikację z repository. Najlepiej wydzielić operacje cache do osobnej klasy, która będzie odpowiadać wyłącznie za zarządzanie cache, podczas gdy reszta logiki pozostanie w serwisie.
-> Standardowa kolekcja HashMap nie jest thread-safe, co przy operacjach asynchronicznych z użyciem CF (CompletableFuture) może powodować race conditions i potencjalne awarie. Brak synchronizacji przy dostępie do kolekcji Map stanowi poważne zagrożenie. Rozwiązaniem jest użycie ConcurrentHashMap oraz odpowiedniego zarządzania stanem w logice. -> Krytyczny Błąd Logiczny w Metodzie getOrCreate(): Według osobistych i dobrych praktyk, zalecałbym unikanie takich metod lub ich gruntowną refaktoryzację z uwzględnieniem:
Rozważyć implementację wzorca tzw. Identity Map dla zarządzania cache, dzięki temu uzyskujemy:
Czyli tak naprawdę dzięki zmianom uzyskujemy:
[thread-safe]
|
|
Wstępnie wrzuciłbym taką dawkę implementacji walidacji:
public record ValidationResult(
boolean isValid,
String errorMessage
) {
public static ValidationResult valid() {
return new ValidationResult(true, null);
}
public static ValidationResult invalid(String message) {
return new ValidationResult(false, message);
}
}
interface LockerValidationService {
ValidationResult validateCreateParameters(UUID uniqueId, String name, Position position);
ValidationResult validateNoConflicts(UUID uniqueId, Position position,
Optional<Locker> existingByUUID,
Optional<Locker> existingByPosition);
}
interface ConflictDetectionStrategy {
ValidationResult detectConflicts(UUID uniqueId, Position position,
Optional<Locker> existingByUUID,
Optional<Locker> existingByPosition);
}
@FunctionalInterface
interface ValidationRule<T> {
ValidationResult validate(T value);
default ValidationRule<T> and(ValidationRule<T> other) {
return value -> {
ValidationResult result = this.validate(value);
return result.isValid() ? other.validate(value) : result;
};
}
}
// Impl:
public final class LockerValidator implements LockerValidationService {
private final List<ValidationRule<CreateLockerRequest>> creationRules;
private final ConflictDetectionStrategy conflictStrategy;
public LockerValidator() {
this.creationRules = List.of(
this::validateUUID,
this::validateName,
this::validatePosition);
this.conflictStrategy = this::defaultConflictDetection;
}
public LockerValidator(List<ValidationRule<CreateLockerRequest>> creationRules,
ConflictDetectionStrategy conflictStrategy) {
this.creationRules = creationRules;
this.conflictStrategy = conflictStrategy;
}
private record CreateLockerRequest(
UUID uniqueId,
String name,
Position position) {
}
@Override
public ValidationResult validateCreateParameters(UUID uniqueId, String name, Position position) {
CreateLockerRequest request = new CreateLockerRequest(uniqueId, name, position);
return creationRules.stream()
.reduce(ValidationRule::and)
.map(rule -> rule.validate(request))
.orElse(ValidationResult.valid());
}
@Override
public ValidationResult validateNoConflicts(UUID uniqueId, Position position,
Optional<Locker> existingByUUID,
Optional<Locker> existingByPosition) {
return conflictStrategy.detectConflicts(uniqueId, position, existingByUUID, existingByPosition);
}
private ValidationResult validateUUID(CreateLockerRequest request) {
return request.uniqueId() != null ?
ValidationResult.valid() :
ValidationResult.invalid("UUID cannot be null");
}
private ValidationResult validateName(CreateLockerRequest request) {
return request.name() != null && !request.name().trim().isEmpty() ?
ValidationResult.valid() :
ValidationResult.invalid("Name cannot be empty");
}
private ValidationResult validatePosition(CreateLockerRequest request) {
return request.position() != null ?
ValidationResult.valid() :
ValidationResult.invalid("Position cannot be null");
}
private ValidationResult defaultConflictDetection(UUID uniqueId, Position position,
Optional<Locker> existingByUUID,
Optional<Locker> existingByPosition) {
return existingByUUID.flatMap(byUUID -> existingByPosition.map(byPosition -> validateBothExist(uniqueId, position, byUUID, byPosition)))
.orElseGet(() -> existingByUUID
.map(locker -> validateUUIDConflict(uniqueId, position, locker))
.orElseGet(() -> existingByPosition
.map(locker -> validatePositionConflict(uniqueId, position, locker))
.orElse(ValidationResult.valid())));
}
private ValidationResult validateBothExist(UUID uniqueId, Position position,
Locker byUUID, Locker byPosition) {
if (!byUUID.equals(byPosition)) {
return ValidationResult.invalid(String.format("Conflicting lockers found. UUID %s vs Position %s",
uniqueId, position));
}
return ValidationResult.valid();
}
private ValidationResult validateUUIDConflict(UUID uniqueId, Position position, Locker locker) {
if (!locker.position().equals(position)) {
return ValidationResult.invalid(String.format("Locker with UUID %s exists at different position: %s",
uniqueId, locker.position()));
}
return ValidationResult.valid();
}
private ValidationResult validatePositionConflict(UUID uniqueId, Position position, Locker locker) {
if (!locker.uuid().equals(uniqueId)) {
return ValidationResult.invalid(String.format("Locker at position %s has different UUID: %s",
position, locker.uuid()));
}
return ValidationResult.valid();
}
}// Wiadomo referencja ->
private final LockerValidationService lockerValidator
// Pełny use case walidacji
public CompletableFuture<Locker> create(UUID uniqueId, String name, Position position) {
return CompletableFuture.supplyAsync(() -> {
ValidationResult validation = lockerValidator.validateCreateParameters(uniqueId, name, position);
if (!validation.isValid()) {
throw new IllegalArgumentException(validation.errorMessage());
}
Optional<Locker> existingByUUID = lockerCache.getByUUID(uniqueId);
Optional<Locker> existingByPosition = lockerCache.getByPosition(position);
ValidationResult conflictCheck = lockerValidator.validateNoConflicts(
uniqueId, position, existingByUUID, existingByPosition);
if (!conflictCheck.isValid()) {
throw new IllegalStateException(conflictCheck.errorMessage()); // rzucamy wyjątek właściwy z cause
}
Locker locker = new Locker(uniqueId, name, position);
lockerCache.put(locker);
return lockerRepository.save(locker)
.thenApply(savedLocker -> {
// moze byc logowanie że np. stworzono locker czy coś takiego
return savedLocker;
});
}).thenCompose(Function.identity());
} |
|
/gemini review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR introduces significant refactoring and modernization to the ParcelLockers plugin, focusing on service-based architecture improvements, enhanced GUI management, and configuration system modernization. The changes streamline the codebase by removing cache-based implementations in favor of service managers and replace legacy components with modern alternatives.
- Replaced legacy managers with new service-based architecture using ConfigService, NoticeService, and manager classes
- Modernized GUI system with new manager classes and improved placeholder handling
- Updated configuration system from CDN to OkaeriConfig with new ConfigItem serialization
- Removed cache classes and direct cache operations in favor of service-layer caching
Reviewed Changes
Copilot reviewed 112 out of 114 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| Test files | Updated to use new service architecture and removed cache dependencies |
| Configuration | Migrated from CDN to OkaeriConfig with new ConfigItem implementation |
| GUI implementation | Replaced legacy GUI classes with new service-based implementation |
| Repository classes | Renamed methods from find/remove to fetch/delete and removed cache integration |
| Manager classes | Converted concrete classes to interfaces with implementation classes |
| Service classes | Added new service layer with caching using Caffeine |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
src/main/java/com/eternalcode/parcellockers/database/persister/PositionPersister.java
Show resolved
Hide resolved
src/test/java/com/eternalcode/parcellockers/database/ParcelRepositoryIntegrationTest.java
Show resolved
Hide resolved
src/main/java/com/eternalcode/parcellockers/parcel/ParcelServiceImpl.java
Outdated
Show resolved
Hide resolved
src/main/java/com/eternalcode/parcellockers/user/controller/LoadUserController.java
Show resolved
Hide resolved
src/main/java/com/eternalcode/parcellockers/configuration/serializable/ConfigItem.java
Outdated
Show resolved
Hide resolved
src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/ItemStorageGui.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This is an impressive and extensive refactoring of the ParcelLockers plugin. The move to a service-based architecture, the adoption of modern tools like okaeri-configs and Paper's library loader, and the overall code cleanup have significantly improved the codebase's quality, maintainability, and performance. The new structure is much cleaner and more robust.
I have a few minor suggestions for further improvement. I also noticed that Sentry integration for error reporting was removed. Was this intentional? It's a significant change to how errors are tracked, and it might be worth reconsidering or documenting the reason for its removal.
Overall, excellent work on modernizing the plugin!
src/main/java/com/eternalcode/parcellockers/locker/prompt/LockerPlacePrompt.java
Show resolved
Hide resolved
src/main/java/com/eternalcode/parcellockers/user/repository/UserRepositoryOrmLite.java
Show resolved
Hide resolved
…iState, add comment to LoadUserController, improve CollectionGui empty result handling
This pull request introduces significant refactoring and modernization to the ParcelLockers plugin, focusing on codebase simplification, improved configuration and notification handling, and enhanced plugin initialization. The changes streamline service and manager creation, replace legacy handlers with newer implementations, and update build and workflow configurations for better compatibility and efficiency.
Codebase Refactoring & Service Architecture
ConfigurationManager,NotificationAnnouncer, etc.) with new service-based architecture (ConfigService,NoticeService, etc.), and updated command, GUI, and controller classes to use these new services for cleaner dependency management.InvalidUsageHandlerImpl,MissingPermissionsHandlerImpl) and updated message handling to useMessageConfigandNoticeServicethrough the Multification library.Plugin Initialization & Shutdown Improvements
TriumphGuiand newGuiManager, and replaced legacy GUI classes with new ones for lockers and main interfaces.Build & Workflow Modernization
.gitattributesto enforce LF line endings for shell scripts and removed unnecessary rules.New Features & Utilities
ParcelLockersLibraryLoaderclass to support dynamic Maven library loading for Paper plugins, enabling better dependency management.deletePluginConfigs.batfor easier cleanup of plugin configuration files during development.