initial commit
This commit is contained in:
@ -0,0 +1,187 @@
|
||||
package id.iptek.utms.preference.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import id.iptek.utms.auth.security.UserPrincipal;
|
||||
import id.iptek.utms.core.exception.AppException;
|
||||
import id.iptek.utms.core.i18n.MessageResolver;
|
||||
import id.iptek.utms.preference.domain.UserUiPreference;
|
||||
import id.iptek.utms.preference.dto.TablePreferenceProfile;
|
||||
import id.iptek.utms.preference.dto.TablePreferenceRequest;
|
||||
import id.iptek.utms.preference.dto.TablePreferenceSavedProfile;
|
||||
import id.iptek.utms.preference.dto.UserUiPreferencesResponse;
|
||||
import id.iptek.utms.preference.repository.UserUiPreferenceRepository;
|
||||
import id.iptek.utms.tenant.TenantContext;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Service
|
||||
public class UserPreferenceService {
|
||||
|
||||
private static final String VALUE_JSON_FIELD = "visibleColumns";
|
||||
private static final Pattern PREFERENCE_KEY_PATTERN =
|
||||
Pattern.compile("^(users|roles|workflow|audit|modules):[A-Za-z0-9_./-]+$");
|
||||
private static final Map<String, List<String>> DEFAULT_COLUMNS_BY_KEY = Map.of(
|
||||
"users:workflow-requests", List.of("id", "resourceType", "resourceId", "makerUsername", "status", "requiredSteps", "currentStep", "createdAt", "updatedAt", "actions"),
|
||||
"workflow:requests", List.of("id", "resourceType", "resourceId", "makerUsername", "status", "updatedAt")
|
||||
);
|
||||
|
||||
private final UserUiPreferenceRepository repository;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final MessageResolver messageResolver;
|
||||
|
||||
public UserPreferenceService(UserUiPreferenceRepository repository,
|
||||
ObjectMapper objectMapper,
|
||||
MessageResolver messageResolver) {
|
||||
this.repository = repository;
|
||||
this.objectMapper = objectMapper;
|
||||
this.messageResolver = messageResolver;
|
||||
}
|
||||
|
||||
public UserUiPreferencesResponse getAll(Authentication authentication) {
|
||||
UUID userId = getUserId(authentication);
|
||||
TenantContext.getRequiredTenantId();
|
||||
|
||||
List<UserUiPreference> preferences = repository.findByUserId(userId);
|
||||
preferences.sort(Comparator.comparing(UserUiPreference::getUpdatedAt, Comparator.nullsLast(Comparator.reverseOrder())));
|
||||
|
||||
List<TablePreferenceProfile> columns = preferences.stream()
|
||||
.map(this::toProfile)
|
||||
.toList();
|
||||
Instant latestUpdatedAt = preferences.stream()
|
||||
.map(UserUiPreference::getUpdatedAt)
|
||||
.filter(Objects::nonNull)
|
||||
.max(Instant::compareTo)
|
||||
.orElse(null);
|
||||
|
||||
return new UserUiPreferencesResponse(columns, latestUpdatedAt);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public TablePreferenceSavedProfile upsert(Authentication authentication, TablePreferenceRequest request) {
|
||||
UUID userId = getUserId(authentication);
|
||||
String tenantId = TenantContext.getRequiredTenantId();
|
||||
String normalizedKey = normalizePreferenceKey(request.preferenceKey());
|
||||
List<String> normalizedColumns = normalizeVisibleColumns(request.visibleColumns());
|
||||
|
||||
UserUiPreference preference = repository.findByUserIdAndPreferenceKey(userId, normalizedKey)
|
||||
.orElseGet(() -> {
|
||||
UserUiPreference created = new UserUiPreference();
|
||||
created.setUserId(userId);
|
||||
created.setTenantId(tenantId);
|
||||
created.setPreferenceKey(normalizedKey);
|
||||
return created;
|
||||
});
|
||||
preference.setValueJson(serializePreferenceValue(normalizedColumns));
|
||||
UserUiPreference saved = repository.save(preference);
|
||||
return new TablePreferenceSavedProfile(
|
||||
saved.getPreferenceKey(),
|
||||
normalizedColumns,
|
||||
saved.getUpdatedAt()
|
||||
);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public TablePreferenceProfile resetTablePreference(Authentication authentication, String preferenceKey) {
|
||||
UUID userId = getUserId(authentication);
|
||||
String normalizedKey = normalizePreferenceKey(preferenceKey);
|
||||
|
||||
repository.deleteByUserIdAndPreferenceKey(userId, normalizedKey);
|
||||
return new TablePreferenceProfile(normalizedKey, getDefaultColumns(normalizedKey));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void resetAll(Authentication authentication) {
|
||||
UUID userId = getUserId(authentication);
|
||||
repository.deleteByUserId(userId);
|
||||
}
|
||||
|
||||
private TablePreferenceProfile toProfile(UserUiPreference preference) {
|
||||
return new TablePreferenceProfile(
|
||||
preference.getPreferenceKey(),
|
||||
parseVisibleColumns(preference.getValueJson())
|
||||
);
|
||||
}
|
||||
|
||||
private UUID getUserId(Authentication authentication) {
|
||||
Object principal = authentication != null ? authentication.getPrincipal() : null;
|
||||
if (principal instanceof UserPrincipal userPrincipal) {
|
||||
return userPrincipal.getId();
|
||||
}
|
||||
throw new AppException(messageResolver.get("auth.invalid.credentials"));
|
||||
}
|
||||
|
||||
private String normalizePreferenceKey(String preferenceKey) {
|
||||
if (preferenceKey == null || preferenceKey.isBlank()) {
|
||||
throw new AppException(messageResolver.get("user.preferences.invalid.key"));
|
||||
}
|
||||
String normalized = preferenceKey.trim();
|
||||
if (!PREFERENCE_KEY_PATTERN.matcher(normalized).matches()) {
|
||||
throw new AppException(messageResolver.get("user.preferences.invalid.key"));
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private List<String> normalizeVisibleColumns(List<String> visibleColumns) {
|
||||
if (visibleColumns == null || visibleColumns.isEmpty()) {
|
||||
throw new AppException(messageResolver.get("user.preferences.invalid.columns"));
|
||||
}
|
||||
List<String> normalized = visibleColumns.stream()
|
||||
.map(column -> column == null ? null : column.trim())
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
if (normalized.isEmpty() || normalized.stream().anyMatch(String::isBlank)) {
|
||||
throw new AppException(messageResolver.get("user.preferences.invalid.columns"));
|
||||
}
|
||||
return List.copyOf(normalized);
|
||||
}
|
||||
|
||||
private String serializePreferenceValue(List<String> visibleColumns) {
|
||||
try {
|
||||
Map<String, List<String>> value = new LinkedHashMap<>();
|
||||
value.put(VALUE_JSON_FIELD, visibleColumns);
|
||||
return objectMapper.writeValueAsString(value);
|
||||
} catch (Exception ex) {
|
||||
throw new AppException(messageResolver.get("user.preferences.serialize.failed"));
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> parseVisibleColumns(String valueJson) {
|
||||
try {
|
||||
JsonNode root = objectMapper.readTree(valueJson);
|
||||
JsonNode columns = root.get(VALUE_JSON_FIELD);
|
||||
if (columns == null || !columns.isArray()) {
|
||||
throw new AppException(messageResolver.get("user.preferences.invalid.value"));
|
||||
}
|
||||
List<String> visibleColumns = new ArrayList<>();
|
||||
for (JsonNode value : columns) {
|
||||
String column = value != null ? value.asText() : null;
|
||||
if (column == null || column.isBlank()) {
|
||||
throw new AppException(messageResolver.get("user.preferences.invalid.value"));
|
||||
}
|
||||
visibleColumns.add(column);
|
||||
}
|
||||
return visibleColumns;
|
||||
} catch (AppException ex) {
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
throw new AppException(messageResolver.get("user.preferences.invalid.value"));
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getDefaultColumns(String preferenceKey) {
|
||||
return DEFAULT_COLUMNS_BY_KEY.getOrDefault(preferenceKey, List.of("id", "createdAt", "updatedAt", "actions"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user