Skip to content
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

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.exence.finance.common.aspect;

import com.exence.finance.modules.workspace.context.WorkspaceContextHolder;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.hibernate.Session;
import org.springframework.stereotype.Component;

@Aspect
@Component
@Slf4j
public class WorkspaceFilterAspect {

@PersistenceContext
private EntityManager entityManager;

@SneakyThrows
@Around("execution(* com.exence.finance.modules.*.repository.*Repository.*(..))")
public Object enableWorkspaceFilter(ProceedingJoinPoint joinPoint) {
if (WorkspaceContextHolder.isSet()) {
Session hibernateSession = entityManager.unwrap(Session.class);
hibernateSession
.enableFilter("workspaceFilter")
.setParameter("workspaceId", WorkspaceContextHolder.getWorkspaceId());
}
return joinPoint.proceed();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.exence.finance.common.entity;

import com.exence.finance.modules.workspace.entity.Workspace;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.MappedSuperclass;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;

@Getter
@Setter
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@MappedSuperclass
public abstract class BaseWorkspaceEntity extends BaseAuditableEntity {

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "workspace_id", nullable = false)
private Workspace workspace;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ public enum ErrorCode {
ACCESS_DENIED(HttpStatus.FORBIDDEN, "access-denied"),
INVALID_TOKEN(HttpStatus.FORBIDDEN, "invalid-token"),
EMAIL_VERIFICATION_REQUIRED(HttpStatus.FORBIDDEN, "email-verification-required"),
WORKSPACE_NOT_FOUND(HttpStatus.FORBIDDEN, "workspace-not-found"),
WORKSPACE_CANNOT_DELETE_LAST(HttpStatus.FORBIDDEN, "workspace-cannot-delete-last"),
WORKSPACE_OWNER_CANNOT_LEAVE(HttpStatus.FORBIDDEN, "workspace-owner-cannot-leave"),
WORKSPACE_OWNER_CANNOT_BE_REMOVED(HttpStatus.FORBIDDEN, "workspace-owner-cannot-be-removed"),
WORKSPACE_OWNER_REQUIRED(HttpStatus.FORBIDDEN, "workspace-owner-required"),

// 404 - Not Found
USER_NOT_FOUND(HttpStatus.NOT_FOUND, "user-not-found"),
Expand All @@ -32,11 +37,13 @@ public enum ErrorCode {

// 409 - Conflict
EMAIL_ALREADY_IN_USE(HttpStatus.CONFLICT, "email-already-in-use"),
WORKSPACE_MEMBER_ALREADY_EXISTS(HttpStatus.CONFLICT, "workspace-member-already-exists"),
CATEGORY_ALREADY_EXISTS(HttpStatus.CONFLICT, "category-already-exists"),
CATEGORY_IN_USE(HttpStatus.CONFLICT, "category-in-use"),
DATA_INTEGRITY_VIOLATION(HttpStatus.CONFLICT, "data-integrity-violation"),

// 400 - Bad Request
WORKSPACE_HEADER_MISSING(HttpStatus.BAD_REQUEST, "workspace-header-missing"),
INVALID_PASSWORD(HttpStatus.BAD_REQUEST, "invalid-password"),
WIDGET_TYPE_MISMATCH(HttpStatus.BAD_REQUEST, "widget-type-mismatch"),
INVALID_WIDGET_SETTING(HttpStatus.BAD_REQUEST, "invalid-widget-setting"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ public final class ValidationConstants {

// User settings validations
public static final int LANGUAGE_CODE_LENGTH = 2;
public static final int CURRENCY_CODE_LENGTH = 3;

// Workspace validations
public static final int WORKSPACE_NAME_MAX_LENGTH = 100;

// Category validations
public static final int CATEGORY_NAME_MIN_LENGTH = 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class CacheConfig {
public CacheManager cacheManager() {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();

cacheManager.setCacheNames(List.of("currentUser", "currentUserId", "systemSettings"));
cacheManager.setCacheNames(List.of("currentUser", "currentUserId", "systemSettings", "workspaceMembership"));

return cacheManager;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.exence.finance.common.converter.StringToTimeframeConverter;
import com.exence.finance.common.converter.StringToTransactionTypeConverter;
import com.exence.finance.security.EmailVerificationInterceptor;
import com.exence.finance.security.WorkspaceInterceptor;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
Expand All @@ -17,6 +18,7 @@ public class WebConfig implements WebMvcConfigurer {
private final StringToCategoryTypeConverter stringToCategoryTypeConverter;
private final StringToTimeframeConverter stringToTimeframeConverter;
private final EmailVerificationInterceptor emailVerificationInterceptor;
private final WorkspaceInterceptor workspaceInterceptor;

@Override
public void addFormatters(FormatterRegistry registry) {
Expand All @@ -30,5 +32,9 @@ public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(emailVerificationInterceptor)
.addPathPatterns("/api/**")
.excludePathPatterns("/api/auth/**");

registry.addInterceptor(workspaceInterceptor)
.addPathPatterns("/api/**")
.excludePathPatterns("/api/auth/**", "/api/admin/**", "/api/exchange-rates/**");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ public interface AchievementChecker {

AchievementType getSupportedType();

long computeCurrentValue(Long userId);
long computeCurrentValue(Long workspaceId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.exence.finance.modules.achievement.checker.AchievementChecker;
import com.exence.finance.modules.achievement.enums.AchievementType;
import com.exence.finance.modules.transaction.repository.TransactionRepository;
import java.sql.Date;
import java.time.LocalDate;
import java.util.List;
import lombok.RequiredArgsConstructor;
Expand All @@ -22,9 +23,11 @@ public AchievementType getSupportedType() {

@Override
@ReadTransactional
public long computeCurrentValue(Long userId) {
public long computeCurrentValue(Long workspaceId) {
// TODO: make it weekly streak
List<LocalDate> dates = transactionRepository.findDistinctCreatedAtByUserId(userId);
List<LocalDate> dates = transactionRepository.findDistinctCreatedAtByWorkspaceId(workspaceId).stream()
.map(Date::toLocalDate)
.toList();
if (dates.isEmpty()) {
return 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public AchievementType getSupportedType() {

@Override
@ReadTransactional
public long computeCurrentValue(Long userId) {
return debtRepository.countAllByUserId(userId);
public long computeCurrentValue(Long workspaceId) {
return debtRepository.countAllByWorkspaceId(workspaceId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public AchievementType getSupportedType() {

@Override
@ReadTransactional
public long computeCurrentValue(Long userId) {
return debtRepository.countByUserIdAndStatus(userId, DebtStatus.SETTLED);
public long computeCurrentValue(Long workspaceId) {
return debtRepository.countByWorkspaceIdAndStatus(workspaceId, DebtStatus.SETTLED);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public AchievementType getSupportedType() {

@Override
@ReadTransactional
public long computeCurrentValue(Long userId) {
return goalRepository.countByUserIdAndStatus(userId, GoalStatus.COMPLETED);
public long computeCurrentValue(Long workspaceId) {
return goalRepository.countByWorkspaceIdAndStatus(workspaceId, GoalStatus.COMPLETED);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public AchievementType getSupportedType() {

@Override
@ReadTransactional
public long computeCurrentValue(Long userId) {
return goalRepository.countAllByUserId(userId);
public long computeCurrentValue(Long workspaceId) {
return goalRepository.countAllByWorkspaceId(workspaceId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public AchievementType getSupportedType() {

@Override
@ReadTransactional
public long computeCurrentValue(Long userId) {
return transactionRepository.countByUserId(userId);
public long computeCurrentValue(Long workspaceId) {
return transactionRepository.countByWorkspaceId(workspaceId);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.exence.finance.modules.achievement.controller;

import com.exence.finance.modules.achievement.dto.AchievementGetDTO;
import com.exence.finance.modules.achievement.dto.UserAchievementGetDTO;
import com.exence.finance.modules.achievement.dto.WorkspaceAchievementGetDTO;
import java.util.List;
import org.springframework.http.ResponseEntity;

public interface AchievementController {

ResponseEntity<List<AchievementGetDTO>> getAllAchievements();

ResponseEntity<List<UserAchievementGetDTO>> getUnlockedAchievements();
ResponseEntity<List<WorkspaceAchievementGetDTO>> getUnlockedAchievements();
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import com.exence.finance.common.util.ResponseFactory;
import com.exence.finance.modules.achievement.controller.AchievementController;
import com.exence.finance.modules.achievement.dto.AchievementGetDTO;
import com.exence.finance.modules.achievement.dto.UserAchievementGetDTO;
import com.exence.finance.modules.achievement.dto.WorkspaceAchievementGetDTO;
import com.exence.finance.modules.achievement.service.AchievementService;
import java.util.List;
import lombok.RequiredArgsConstructor;
Expand All @@ -27,7 +27,7 @@ public ResponseEntity<List<AchievementGetDTO>> getAllAchievements() {
}

@GetMapping("/unlocked")
public ResponseEntity<List<UserAchievementGetDTO>> getUnlockedAchievements() {
public ResponseEntity<List<WorkspaceAchievementGetDTO>> getUnlockedAchievements() {
return ResponseFactory.ok(achievementService.getUnlockedAchievements());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import com.exence.finance.modules.achievement.enums.AchievementType;
import java.time.Instant;

public record UserAchievementGetDTO(
public record WorkspaceAchievementGetDTO(
Long id,
Long achievementId,
String name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import static com.exence.finance.common.util.ValidationConstants.ACHIEVEMENT_TYPE_MAX_LENGTH;

import com.exence.finance.modules.achievement.enums.AchievementType;
import com.exence.finance.modules.auth.entity.User;
import com.exence.finance.modules.workspace.entity.Workspace;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
Expand All @@ -28,8 +28,8 @@
name = "achievement_progress",
uniqueConstraints =
@UniqueConstraint(
columnNames = {"user_id", "achievement_type"},
name = "uq_achievement_progress_user_type"))
columnNames = {"workspace_id", "achievement_type"},
name = "uq_achievement_progress_workspace_type"))
@Getter
@Setter
@Builder
Expand All @@ -47,8 +47,8 @@ public class AchievementProgress {
private Long id;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id", nullable = false)
private User user;
@JoinColumn(name = "workspace_id", nullable = false)
private Workspace workspace;

@Enumerated(EnumType.STRING)
@Column(name = "achievement_type", nullable = false, length = ACHIEVEMENT_TYPE_MAX_LENGTH)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.exence.finance.modules.achievement.entity;

import com.exence.finance.modules.auth.entity.User;
import com.exence.finance.modules.workspace.entity.Workspace;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
Expand All @@ -22,27 +22,30 @@

@Entity
@Table(
name = "user_achievement",
name = "workspace_achievement",
uniqueConstraints =
@UniqueConstraint(
columnNames = {"user_id", "achievement_id"},
name = "uq_user_achievement"))
columnNames = {"workspace_id", "achievement_id"},
name = "uq_workspace_achievement"))
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserAchievement {
public class WorkspaceAchievement {

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_achievement_id_seq")
@SequenceGenerator(name = "user_achievement_id_seq", sequenceName = "user_achievement_id_seq", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "workspace_achievement_id_seq")
@SequenceGenerator(
name = "workspace_achievement_id_seq",
sequenceName = "workspace_achievement_id_seq",
allocationSize = 1)
@Column(name = "id")
private Long id;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id", nullable = false)
private User user;
@JoinColumn(name = "workspace_id", nullable = false)
private Workspace workspace;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "achievement_id", nullable = false)
Expand Down
Loading
Loading