diff --git a/sources/api/pom.xml b/sources/api/pom.xml index 74c1e25..3c1b6c8 100644 --- a/sources/api/pom.xml +++ b/sources/api/pom.xml @@ -26,13 +26,13 @@ tools.dynamia.modules tools.dynamia.modules.saas.parent - 3.4.4 + 3.5.0 tools.dynamia.modules.saas.api DynamiaModules - SaaS API https://www.dynamia.tools/modules/saas - 3.4.4 + 3.5.0 diff --git a/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountAPIConfig.java b/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountAPIConfig.java index 22bbd62..1f49df0 100644 --- a/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountAPIConfig.java +++ b/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountAPIConfig.java @@ -21,18 +21,31 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import tools.dynamia.integration.ms.MessageService; /** - * Default Account SaaS configuration + * Default Spring configuration for the SaaS Account API module. + *

+ * This configuration class sets up the essential beans required for multi-tenant + * account management, including the custom account scope and a fallback no-op + * implementation of {@link AccountServiceAPI}. + *

+ * The configuration is automatically loaded when the module is present in the classpath, + * and provides sensible defaults that can be overridden by custom implementations. + * + * @author Mario Serrano Leones */ @Configuration public class AccountAPIConfig { - /** - * This bean is used to register the AccountScope in the application context. - * @return CustomScopeConfigurer + * Registers the custom "account" scope with the Spring container. + *

+ * This bean configures the {@link AccountScope} which enables beans to be scoped + * at the account level, ensuring proper isolation in multi-tenant environments. + * Once registered, beans can use {@code @Scope("account")} to be managed at the account level. + * + * @return a {@link CustomScopeConfigurer} with the account scope registered + * @see AccountScope */ @Bean public CustomScopeConfigurer accountScopeConfigurer() { @@ -42,10 +55,14 @@ public CustomScopeConfigurer accountScopeConfigurer() { } /** - * If no AccountServiceAPI is configured, this bean will be used as a default. - * This is useful for testing. + * Provides a no-op implementation of {@link AccountServiceAPI} as a fallback. + *

+ * This bean is only created if no other {@link AccountServiceAPI} implementation + * is found in the application context. It's primarily useful for testing scenarios + * or when the SaaS features are not fully configured. * - * @return NoOpAccountServiceAPI instance + * @return a {@link NoOpAccountServiceAPI} instance + * @see NoOpAccountServiceAPI */ @Bean @ConditionalOnMissingBean(AccountServiceAPI.class) diff --git a/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountAdminAction.java b/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountAdminAction.java index d93ed79..7f899fd 100644 --- a/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountAdminAction.java +++ b/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountAdminAction.java @@ -22,11 +22,49 @@ import tools.dynamia.actions.ActionSelfFilter; import tools.dynamia.integration.Containers; +/** + * Base class for administrative actions that require authorization in a SaaS environment. + *

+ * This class extends {@link AbstractAction} and provides built-in support for authorization + * checks before executing administrative operations. It integrates with + * {@link AccountAdminActionAuthorizationProvider} to enforce security policies. + *

+ * Actions extending this class can require authorization by setting the + * {@code authorizationRequired} flag. When enabled, the action will delegate to the + * configured authorization provider before executing. + *

+ * Example usage: + *

{@code
+ * public class DeleteAccountAction extends AccountAdminAction {
+ *     public DeleteAccountAction() {
+ *         setName("Delete Account");
+ *         setAuthorizationRequired(true);
+ *     }
+ *
+ *     @Override
+ *     public void actionPerformed(ActionEvent evt) {
+ *         // Delete account logic
+ *     }
+ * }
+ * }
+ * + * @author Mario Serrano Leones + * @see AccountAdminActionAuthorizationProvider + * @see AbstractAction + */ public abstract class AccountAdminAction extends AbstractAction implements ActionSelfFilter { private boolean authorizationRequired; - + /** + * Hook method executed before the action is performed. + *

+ * If authorization is required, this method will delegate to the + * {@link AccountAdminActionAuthorizationProvider} to perform authorization checks. + * The actual action will only execute if authorization is granted. + * + * @param evt the action event + */ @Override public void beforeActionPerformed(ActionEvent evt) { if (isAuthorizationRequired()) { @@ -39,15 +77,33 @@ public void beforeActionPerformed(ActionEvent evt) { } } + /** + * Hook method executed after the action is performed. + *

+ * This implementation does nothing and can be overridden by subclasses + * to add post-execution logic. + * + * @param evt the action event + */ @Override public void afterActionPerformed(ActionEvent evt) { //do nothing } + /** + * Checks if authorization is required before executing this action. + * + * @return true if authorization is required, false otherwise + */ public boolean isAuthorizationRequired() { return authorizationRequired; } + /** + * Sets whether authorization is required before executing this action. + * + * @param authorizationRequired true to require authorization, false otherwise + */ public void setAuthorizationRequired(boolean authorizationRequired) { this.authorizationRequired = authorizationRequired; } diff --git a/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountAdminActionAuthorizationProvider.java b/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountAdminActionAuthorizationProvider.java index 16c5c07..6beb3d6 100644 --- a/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountAdminActionAuthorizationProvider.java +++ b/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountAdminActionAuthorizationProvider.java @@ -4,7 +4,51 @@ import tools.dynamia.actions.ActionEvent; import tools.dynamia.commons.Callback; +/** + * Interface for providing custom authorization logic for administrative actions in a SaaS environment. + *

+ * This interface allows modules to implement custom authorization checks before executing + * administrative actions on accounts. It provides a hook point for adding security layers, + * audit requirements, or business rules that must be validated before an action proceeds. + *

+ * The authorization process is asynchronous, using a callback mechanism to either grant or deny access. + * This design allows for complex authorization workflows, including user confirmations, multi-factor + * authentication, or external authorization services. + *

+ * Example usage: + *

{@code
+ * @Component
+ * public class MFAAuthorizationProvider implements AccountAdminActionAuthorizationProvider {
+ *     @Override
+ *     public void authorize(Action action, ActionEvent evt, Callback onAuthorization) {
+ *         if (requiresMFA(action)) {
+ *             promptForMFA(success -> {
+ *                 if (success) {
+ *                     onAuthorization.doSomething();
+ *                 }
+ *             });
+ *         } else {
+ *             onAuthorization.doSomething();
+ *         }
+ *     }
+ * }
+ * }
+ * + * @author Mario Serrano Leones + */ public interface AccountAdminActionAuthorizationProvider { + /** + * Performs authorization checks for an administrative action. + *

+ * This method is called before executing administrative actions on accounts. + * Implementations should perform any necessary authorization checks and invoke + * the callback if authorization is granted. If authorization is denied, the + * callback should not be invoked. + * + * @param action the action being authorized + * @param evt the event context containing information about the action execution + * @param onAuthorization callback to invoke if authorization is granted + */ void authorize(Action action, ActionEvent evt, Callback onAuthorization); } diff --git a/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountAware.java b/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountAware.java index bd93170..62e4bbe 100644 --- a/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountAware.java +++ b/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountAware.java @@ -19,13 +19,45 @@ package tools.dynamia.modules.saas.api; /** + * Interface for entities or components that belong to a specific account in a multi-tenant SaaS environment. + *

+ * Implementing this interface allows objects to be associated with an account, enabling proper data isolation + * and tenant-specific operations. This is a fundamental interface for multi-tenancy support, ensuring that + * each entity knows which account it belongs to. + *

+ * Example usage: + *

{@code
+ * @Entity
+ * public class Customer implements AccountAware {
+ *     @Column
+ *     private Long accountId;
+ *
+ *     public Long getAccountId() {
+ *         return accountId;
+ *     }
+ *
+ *     public void setAccountId(Long accountId) {
+ *         this.accountId = accountId;
+ *     }
+ * }
+ * }
* * @author Mario Serrano Leones */ public interface AccountAware { + /** + * Returns the unique identifier of the account this entity belongs to. + * + * @return the account ID + */ Long getAccountId(); + /** + * Sets the account this entity belongs to. + * + * @param account the account ID to assign to this entity + */ void setAccountId(Long account); } diff --git a/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountFeatureProvider.java b/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountFeatureProvider.java index 844daa2..1f5e4b4 100644 --- a/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountFeatureProvider.java +++ b/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountFeatureProvider.java @@ -17,10 +17,48 @@ package tools.dynamia.modules.saas.api; +/** + * Provider interface for defining account features in a SaaS multi-tenant environment. + *

+ * This interface allows modules to register features that can be enabled or disabled + * per account. Features can be used to control access to specific functionality, + * allowing different subscription tiers or custom feature sets for different accounts. + *

+ * Implementations should be registered as Spring beans and will be automatically + * discovered by the SaaS module to populate the available features list. + *

+ * Example usage: + *

{@code
+ * @Component
+ * public class AdvancedReportsFeature implements AccountFeatureProvider {
+ *     public String getId() {
+ *         return "advanced-reports";
+ *     }
+ *
+ *     public String getName() {
+ *         return "Advanced Reports";
+ *     }
+ * }
+ * }
+ * + * @author Mario Serrano Leones + */ public interface AccountFeatureProvider { + /** + * Returns the unique identifier for this feature. + * This ID is used to reference the feature programmatically throughout the application. + * + * @return the feature identifier (e.g., "advanced-reports", "api-access") + */ String getId(); + /** + * Returns the human-readable name of this feature. + * This name is typically displayed in the user interface for feature management. + * + * @return the feature display name + */ String getName(); } diff --git a/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountInitializer.java b/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountInitializer.java index fdc34cf..8a2cd43 100644 --- a/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountInitializer.java +++ b/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountInitializer.java @@ -27,22 +27,75 @@ import java.util.Locale; /** + * Interface for components that perform initialization tasks when a new account is created in a SaaS environment. + *

+ * Implementations of this interface are automatically discovered and executed during account creation, + * allowing modules to set up initial data, configurations, or resources for new accounts. + * Multiple initializers can be chained together, with execution order controlled by priority. + *

+ * Common use cases include: + *

+ *

+ * Example usage: + *

{@code
+ * @Component
+ * public class DefaultRolesInitializer implements AccountInitializer {
+ *     @Override
+ *     public void init(AccountDTO accountDTO) {
+ *         // Create default roles for the new account
+ *         createRole(accountDTO, "ADMIN");
+ *         createRole(accountDTO, "USER");
+ *     }
+ *
+ *     @Override
+ *     public int getPriority() {
+ *         return 100; // Execute after basic setup
+ *     }
+ * }
+ * }
+ * * @author Mario Serrano Leones */ public interface AccountInitializer { + /** + * Performs initialization tasks for a newly created account. + *

+ * This method is called automatically after an account is created and persisted. + * Implementations should create any necessary initial data or configurations + * required for the account to function properly. + * + * @param accountDTO the newly created account's data transfer object + */ void init(AccountDTO accountDTO); /** - * Define account initializer priority or order. Lower value is high priority + * Defines the execution priority for this initializer. + *

+ * When multiple initializers exist, they are executed in order from lowest to highest priority value. + * Lower values indicate higher priority and will execute first. * - * @return + * @return the priority value (default is 0, lower values execute first) */ default int getPriority() { return 0; } - + /** + * Retrieves a localized message for the account's configured locale. + *

+ * This helper method simplifies obtaining localized strings during account initialization, + * using the account's locale setting if available, or falling back to the system default. + * + * @param key the message key to retrieve + * @param accountDTO the account containing locale information + * @return the localized message string, or the key itself if no translation is found + */ default String localizedMessage(String key, AccountDTO accountDTO) { try { var provider = Containers.get().findObjects(LocalizedMessagesProvider.class) diff --git a/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountLocalStorage.java b/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountLocalStorage.java index b82e269..b30ffa4 100644 --- a/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountLocalStorage.java +++ b/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountLocalStorage.java @@ -22,35 +22,114 @@ import java.util.Date; +/** + * Interface for managing account-specific temporary storage in a multi-tenant SaaS environment. + *

+ * This interface provides a key-value storage mechanism that is scoped to the current account, + * useful for caching data, storing temporary state, or maintaining session-like information + * at the account level. The storage is typically backed by in-memory caching or similar mechanisms. + *

+ * All stored entries include metadata such as timestamps and optional messages, + * making it suitable for audit trails or debugging purposes. + *

+ * Example usage: + *

{@code
+ * AccountLocalStorage storage = AccountLocalStorage.load();
+ * storage.add("last-sync-time", new Date());
+ * Date lastSync = (Date) storage.get("last-sync-time");
+ * }
+ * + * @author Mario Serrano Leones + */ public interface AccountLocalStorage { + /** + * Loads the current account's local storage instance. + *

+ * This static factory method retrieves the active implementation of AccountLocalStorage + * from the dependency injection container, automatically scoped to the current account context. + * + * @return the AccountLocalStorage instance for the current account + */ static AccountLocalStorage load() { return Containers.get().findObject(AccountLocalStorage.class); } + /** + * Retrieves a value from storage by its key. + * + * @param key the storage key + * @return the stored value, or null if not found + */ Object get(String key); + /** + * Retrieves a complete entry from storage, including metadata. + * + * @param key the storage key + * @return the Entry object containing value and metadata, or null if not found + */ Entry getEntry(String key); + /** + * Stores a value with the specified key. + *

+ * If a value already exists for this key, it will be replaced. + * + * @param key the storage key + * @param value the value to store + */ void add(String key, Object value); + /** + * Stores a complete entry with metadata. + * + * @param entry the entry to store, containing key, value, and optional message + */ void addEntry(Entry entry); + /** + * Removes a value from storage. + * + * @param key the storage key to remove + */ void remove(String key); + /** + * Clears all entries from the current account's storage. + */ void clear(); + /** + * Represents a storage entry with metadata including timestamp and optional message. + *

+ * Each entry captures not only the key-value pair but also when it was created + * and an optional descriptive message, useful for audit trails or debugging. + */ class Entry { - private String key; - private Object value; - private long timestamp; - private String message; - private Date date; - + private final String key; + private final Object value; + private final long timestamp; + private final String message; + private final Date date; + + /** + * Creates a new entry with the specified key and value. + * + * @param key the storage key + * @param value the value to store + */ public Entry(String key, Object value) { this(key, value, null); } + /** + * Creates a new entry with the specified key, value, and descriptive message. + * + * @param key the storage key + * @param value the value to store + * @param message an optional descriptive message for this entry + */ public Entry(String key, Object value, String message) { this.key = key; this.value = value; @@ -59,22 +138,47 @@ public Entry(String key, Object value, String message) { this.date = DateTimeUtils.createDate(timestamp); } + /** + * Returns the storage key for this entry. + * + * @return the key + */ public String getKey() { return key; } + /** + * Returns the stored value. + * + * @return the value + */ public Object getValue() { return value; } + /** + * Returns the timestamp (milliseconds since epoch) when this entry was created. + * + * @return the creation timestamp + */ public long getTimestamp() { return timestamp; } + /** + * Returns the optional descriptive message associated with this entry. + * + * @return the message, or null if none was provided + */ public String getMessage() { return message; } + /** + * Returns the creation date of this entry. + * + * @return the creation date + */ public Date getDate() { return date; } diff --git a/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountScope.java b/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountScope.java index ac69af8..88a1662 100644 --- a/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountScope.java +++ b/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountScope.java @@ -25,10 +25,43 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +/** + * Custom Spring bean scope implementation for account-scoped beans in a multi-tenant SaaS environment. + *

+ * This scope manages beans at the account level, ensuring that each tenant account has its own + * isolated instances of account-scoped beans. This is essential for maintaining proper data + * isolation and state management in multi-tenant applications. + *

+ * Beans defined with this scope will be created once per account and reused for all requests + * within that account's context. The scope automatically handles bean lifecycle and cleanup + * when accounts are switched or removed. + *

+ * Example usage in Spring configuration: + *

{@code
+ * @Component
+ * @Scope("account")
+ * public class AccountSpecificService {
+ *     // This bean will have one instance per account
+ * }
+ * }
+ * + * @author Mario Serrano Leones + * @see org.springframework.beans.factory.config.Scope + */ public class AccountScope implements Scope { - private Map> accountObjects = new ConcurrentHashMap<>(); - + private final Map> accountObjects = new ConcurrentHashMap<>(); + + /** + * Retrieves a bean instance from the account scope, creating it if necessary. + *

+ * This method is called by Spring when resolving account-scoped beans. + * It ensures that each account has its own isolated instance of the bean. + * + * @param name the name of the bean + * @param objectFactory the factory to create the bean if it doesn't exist + * @return the scoped bean instance, or null if no account context is available + */ @Override public Object get(String name, ObjectFactory objectFactory) { Object object = null; @@ -44,7 +77,15 @@ public Object get(String name, ObjectFactory objectFactory) { return object; } - + /** + * Removes a bean instance from the current account's scope. + *

+ * This method is called when explicitly removing a bean from the scope + * or during account cleanup operations. + * + * @param name the name of the bean to remove + * @return the removed bean instance, or null if not found or no account context + */ @Override public Object remove(String name) { Long accountId = getAccountId(); @@ -54,16 +95,40 @@ public Object remove(String name) { return null; } + /** + * Registers a callback to be executed when a bean is destroyed. + *

+ * This implementation currently does not support destruction callbacks. + * + * @param name the name of the bean + * @param callback the callback to execute on destruction + */ @Override public void registerDestructionCallback(String name, Runnable callback) { } + /** + * Resolves a contextual object for the given key. + *

+ * This implementation does not provide contextual objects. + * + * @param key the key to resolve + * @return always returns null + */ @Override public Object resolveContextualObject(String key) { return null; } + /** + * Returns a unique conversation ID for the current account scope. + *

+ * The conversation ID is based on the current account ID and is used + * by Spring to identify the scope context. + * + * @return a conversation ID in the format "account{accountId}", or null if no account context + */ @Override public String getConversationId() { Long accountId = getAccountId(); @@ -74,6 +139,12 @@ public String getConversationId() { } + /** + * Retrieves or creates the bean storage map for the specified account. + * + * @param accountId the account identifier + * @return a map containing all beans for the account + */ private Map getAccountObjects(Long accountId) { Map objectMap = accountObjects.get(accountId); if (objectMap == null) { @@ -83,6 +154,11 @@ private Map getAccountObjects(Long accountId) { return objectMap; } + /** + * Retrieves the current account ID from the AccountServiceAPI. + * + * @return the current account ID, or null if no account context is available + */ private Long getAccountId() { AccountServiceAPI accountServiceAPI = Containers.get().findObject(AccountServiceAPI.class); if (accountServiceAPI != null) { diff --git a/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountServiceAPI.java b/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountServiceAPI.java index b0c4f54..b8352b8 100644 --- a/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountServiceAPI.java +++ b/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountServiceAPI.java @@ -29,81 +29,256 @@ import java.util.List; import java.util.Map; +/** + * Core service API for managing SaaS accounts in a multi-tenant environment. + * This interface provides comprehensive account management capabilities including + * status tracking, payment processing, logging, feature management, and configuration. + *

+ * Implementations of this interface handle the business logic for account operations, + * tenant isolation, and account lifecycle management in a SaaS architecture. + *

+ * Example usage: + *

{@code
+ * AccountServiceAPI accountService = Containers.get().findObject(AccountServiceAPI.class);
+ * AccountDTO account = accountService.getCurrentAccount();
+ * if (accountService.hasFeature(account.getId(), "advanced-reports")) {
+ *     // Enable advanced reporting features
+ * }
+ * }
+ * + * @author Mario Serrano Leones + */ public interface AccountServiceAPI { + /** + * Attribute name used to store the current account ID in the session or context. + */ String CURRENT_ACCOUNT_ID_ATTRIBUTE = "currentAccountId"; + /** + * Retrieves the current status of the specified account. + * + * @param accountId the unique identifier of the account + * @return the current {@link AccountStatus} of the account + */ AccountStatus getAccountStatus(Long accountId); + /** + * Retrieves the complete account information for the specified account ID. + * + * @param accountId the unique identifier of the account + * @return an {@link AccountDTO} containing all account details + */ AccountDTO getAccount(Long accountId); + /** + * Returns the system account ID. The system account represents the main + * administrative account with special privileges. + * + * @return the unique identifier of the system account + */ Long getSystemAccountId(); + /** + * Retrieves the ID of the account currently active in the session or execution context. + * + * @return the unique identifier of the current account, or null if no account is set + */ Long getCurrentAccountId(); + /** + * Retrieves the complete information of the currently active account. + * + * @return an {@link AccountDTO} with the current account details + */ AccountDTO getCurrentAccount(); + /** + * Sets the specified account as the current active account in the session or context. + * This is typically used for account switching in multi-tenant scenarios. + * + * @param accountId the unique identifier of the account to set as current + * @return the {@link AccountDTO} of the newly set current account + */ AccountDTO setCurrentAccount(Long accountId); + /** + * Updates the user count statistics for the specified account. + * + * @param accountId the unique identifier of the account + * @param users the total number of users registered in the account + * @param activedUsers the number of currently active users in the account + */ void updateAccountUsers(Long accountId, long users, long activedUsers); + /** + * Retrieves all payment records associated with the specified account. + * + * @param accountId the unique identifier of the account + * @return a list of {@link AccountPaymentDTO} objects representing payment history + */ List getPayments(Long accountId); + /** + * Retrieves activity logs for the specified account within a date range. + * + * @param accountId the unique identifier of the account + * @param startDate the beginning of the date range for log retrieval + * @param endDate the end of the date range for log retrieval + * @return a list of {@link AccountLogDTO} objects representing account activities + */ List getLogs(Long accountId, Date startDate, Date endDate); + /** + * Retrieves a configuration parameter value by name for the current account. + * + * @param name the name of the parameter to retrieve + * @return the parameter value as a string, or null if not found + */ String getParameterValue(String name); + /** + * Retrieves a configuration parameter value by name, with a default fallback. + * + * @param name the name of the parameter to retrieve + * @param defaultValue the default value to return if the parameter is not found + * @return the parameter value, or the default value if not found + */ String getParameterValue(String name, String defaultValue); + /** + * Sets a configuration parameter for the current account. + * + * @param name the name of the parameter to set + * @param value the value to assign to the parameter + */ void setParameter(String name, String value); + /** + * Checks whether the specified account has access to a particular feature. + * This method is used for feature flagging and permission management in multi-tenant scenarios. + * + * @param accountId the unique identifier of the account + * @param featureId the identifier of the feature to check + * @return true if the account has access to the feature, false otherwise + */ default boolean hasFeature(Long accountId, String featureId) { return false; } + /** + * Determines whether printing functionality is enabled for the specified account. + * This can be used to control access to document generation and printing features. + * + * @param accountId the unique identifier of the account + * @return true if printing is enabled, false otherwise + */ default boolean isPrintingEnabled(Long accountId) { return true; } + /** + * Finds all account IDs that have access to the specified feature. + * + * @param featureId the identifier of the feature to search for + * @return a list of account IDs that have access to the feature + */ List findAccountsIdByFeature(String featureId); + /** + * Records a log message for the specified account. + * This method is used for audit trails and activity tracking. + * + * @param accountId the unique identifier of the account + * @param message the log message to record + */ void log(Long accountId, String message); + /** + * Retrieves detailed status information for the specified account. + * This includes status, messages, and balance information. + * + * @param accountId the unique identifier of the account + * @return an {@link AccountStatusDTO} containing comprehensive status details + */ default AccountStatusDTO getAccountStatusDetails(Long accountId) { AccountDTO dto = getAccount(accountId); return new AccountStatusDTO(dto.getId(), dto.getName(), dto.getStatus(), dto.getStatusDate(), dto.getStatusDescription(), dto.getGlobalMessage(), dto.isShowGlobalMessage(), dto.getGlobalMessageType(), BigDecimal.ZERO); } + /** + * Retrieves the parent account ID for the specified account in a hierarchical account structure. + * + * @param accountId the unique identifier of the account + * @return the parent account ID, or null if the account has no parent + */ default Long getParentAccountId(Long accountId) { return null; } + /** + * Finds the account ID associated with the specified domain name. + * This is used in multi-tenant scenarios where each account has a unique domain or subdomain. + * + * @param domain the domain name to search for + * @return the account ID associated with the domain, or null if not found + */ Long getAccountIdByDomain(String domain); + /** + * Clears all cached account data. + * This method should be called when account information needs to be refreshed globally. + */ default void clearCache() { //do nothing } + /** + * Clears cached data for a specific account and its associated domain. + * + * @param accountId the unique identifier of the account + * @param accountDomain the domain associated with the account + */ default void clearCache(Long accountId, String accountDomain) { //do nothing } + /** + * Finds account IDs matching the specified search criteria. + * The parameters map can contain various filters such as status, type, or other account attributes. + * + * @param params a map of search parameters and their values + * @return a list of account IDs matching the criteria, or an empty list if none found + */ default List findAccountsId(Map params) { return Collections.emptyList(); } + /** + * Finds all active accounts that require payment. + * This is a convenience method that filters accounts by ACTIVE status and payment requirement. + * + * @return a list of account IDs for active accounts requiring payment + */ default List findActivePaymentRequiredAccounts() { return findAccountsId(Map.of("status", AccountStatus.ACTIVE, "type.paymentRequired", true)); } + /** + * Initializes the domain cache for improved performance in domain-to-account lookups. + * This method should be called during application startup or when the domain mapping changes. + */ default void initDomainCache() { //do nothing } /** - * Check if account is ACTIVE or throw a {@link tools.dynamia.domain.ValidationError} - * @param accountId + * Validates that the specified account is in ACTIVE status. + * If the account is not active, this method throws a validation error. + * + * @param accountId the unique identifier of the account to validate + * @throws RuntimeException if the account is not active */ default void validateAccountStatus(Long accountId) { //do nothing diff --git a/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountServiceQuantityCalculator.java b/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountServiceQuantityCalculator.java index e807e20..c8782bd 100644 --- a/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountServiceQuantityCalculator.java +++ b/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountServiceQuantityCalculator.java @@ -1,13 +1,71 @@ package tools.dynamia.modules.saas.api; /** - * Provider of quantity for aditional services + * Interface for calculating quantities of additional services consumed by an account. + *

+ * This interface enables modules to define custom metrics or usage calculators that can be tracked + * per account. These calculations can be used for billing purposes, usage analytics, or quota enforcement. + *

+ * Common use cases include: + *

    + *
  • Calculating storage space used
  • + *
  • Counting API calls or transactions
  • + *
  • Measuring bandwidth consumption
  • + *
  • Tracking feature usage
  • + *
+ *

+ * Implementations should be registered as Spring beans and will be automatically discovered + * by the SaaS module for service quantity tracking. + *

+ * Example usage: + *

{@code
+ * @Component
+ * public class StorageCalculator implements AccountServiceQuantityCalculator {
+ *     @Autowired
+ *     private FileRepository fileRepository;
+ *
+ *     public String getId() {
+ *         return "storage-used";
+ *     }
+ *
+ *     public String getName() {
+ *         return "Storage Used (MB)";
+ *     }
+ *
+ *     public long calculate(Long accountId) {
+ *         return fileRepository.getTotalSizeByAccount(accountId) / (1024 * 1024);
+ *     }
+ * }
+ * }
+ * + * @author Mario Serrano Leones */ public interface AccountServiceQuantityCalculator { + /** + * Returns the unique identifier for this quantity calculator. + * This ID is used to reference the calculator throughout the system. + * + * @return the calculator identifier (e.g., "storage-used", "api-calls") + */ String getId(); + /** + * Returns the human-readable name of this quantity calculator. + * This name is typically displayed in reports and billing statements. + * + * @return the calculator display name + */ String getName(); + /** + * Calculates the current quantity for the specified account. + *

+ * This method should perform any necessary queries or calculations to determine + * the current usage or quantity value for the given account. + * + * @param accountId the unique identifier of the account + * @return the calculated quantity value + */ long calculate(Long accountId); } diff --git a/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountStatsProvider.java b/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountStatsProvider.java index f6e0ef4..f6d0616 100644 --- a/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountStatsProvider.java +++ b/sources/api/src/main/java/tools/dynamia/modules/saas/api/AccountStatsProvider.java @@ -19,7 +19,52 @@ import java.util.List; +/** + * Interface for providing statistical information about an account. + *

+ * This interface allows modules to contribute custom statistics and metrics + * that can be displayed in account dashboards, reports, or administrative interfaces. + * Multiple providers can coexist, each providing different types of statistics. + *

+ * Statistics can include metrics such as: + *

    + *
  • Usage statistics (storage, bandwidth, API calls)
  • + *
  • Business metrics (sales, customers, transactions)
  • + *
  • Activity metrics (login counts, active users)
  • + *
  • Performance metrics (response times, error rates)
  • + *
+ *

+ * Example usage: + *

{@code
+ * @Component
+ * public class UserActivityStatsProvider implements AccountStatsProvider {
+ *     @Autowired
+ *     private UserRepository userRepository;
+ *
+ *     public List getAccountStats(Long accountId) {
+ *         List stats = new ArrayList<>();
+ *         stats.add(new AccountStats("Total Users",
+ *                 userRepository.countByAccountId(accountId)));
+ *         stats.add(new AccountStats("Active Users",
+ *                 userRepository.countActiveByAccountId(accountId)));
+ *         return stats;
+ *     }
+ * }
+ * }
+ * + * @author Mario Serrano Leones + */ public interface AccountStatsProvider { + /** + * Retrieves a list of statistics for the specified account. + *

+ * Implementations should gather and return relevant statistical information + * for the given account. Each statistic should be represented as an {@link AccountStats} + * object containing a label and value. + * + * @param accountId the unique identifier of the account + * @return a list of {@link AccountStats} objects representing various metrics + */ List getAccountStats(Long accountId); } diff --git a/sources/api/src/main/java/tools/dynamia/modules/saas/api/ExternalReferenceAware.java b/sources/api/src/main/java/tools/dynamia/modules/saas/api/ExternalReferenceAware.java index f897698..3ea4032 100644 --- a/sources/api/src/main/java/tools/dynamia/modules/saas/api/ExternalReferenceAware.java +++ b/sources/api/src/main/java/tools/dynamia/modules/saas/api/ExternalReferenceAware.java @@ -17,9 +17,55 @@ package tools.dynamia.modules.saas.api; +/** + * Interface for entities that maintain references to external systems or identifiers. + *

+ * This interface is useful when integrating with third-party services, payment gateways, + * or external APIs where entities need to store external identifiers for synchronization + * or cross-referencing purposes. + *

+ * Common use cases include: + *

    + *
  • Storing Stripe customer IDs
  • + *
  • Maintaining external CRM references
  • + *
  • Tracking third-party service subscription IDs
  • + *
  • Linking to external accounting systems
  • + *
+ *

+ * Example usage: + *

{@code
+ * @Entity
+ * public class Account implements ExternalReferenceAware {
+ *     @Column
+ *     private String externalRef;
+ *
+ *     public String getExternalRef() {
+ *         return externalRef;
+ *     }
+ *
+ *     public void setExternalRef(String externalRef) {
+ *         this.externalRef = externalRef;
+ *     }
+ * }
+ * }
+ * + * @author Mario Serrano Leones + */ public interface ExternalReferenceAware { + /** + * Returns the external reference identifier. + *

+ * This typically contains an ID or reference code from an external system. + * + * @return the external reference, or null if not set + */ String getExternalRef(); + /** + * Sets the external reference identifier. + * + * @param externalRef the external reference to assign to this entity + */ void setExternalRef(String externalRef); } diff --git a/sources/api/src/main/java/tools/dynamia/modules/saas/api/SimpleAccountLocalStorage.java b/sources/api/src/main/java/tools/dynamia/modules/saas/api/SimpleAccountLocalStorage.java index d2b9f1f..eadb858 100644 --- a/sources/api/src/main/java/tools/dynamia/modules/saas/api/SimpleAccountLocalStorage.java +++ b/sources/api/src/main/java/tools/dynamia/modules/saas/api/SimpleAccountLocalStorage.java @@ -32,7 +32,7 @@ class SimpleAccountLocalStorage implements AccountLocalStorage { @Autowired private AccountServiceAPI accountServiceAPI; - private Map> storage = new ConcurrentHashMap<>(); + private final Map> storage = new ConcurrentHashMap<>(); @Override public Object get(String key) { diff --git a/sources/api/src/main/java/tools/dynamia/modules/saas/api/dto/AccountDTO.java b/sources/api/src/main/java/tools/dynamia/modules/saas/api/dto/AccountDTO.java index 5065ce6..7a042fd 100644 --- a/sources/api/src/main/java/tools/dynamia/modules/saas/api/dto/AccountDTO.java +++ b/sources/api/src/main/java/tools/dynamia/modules/saas/api/dto/AccountDTO.java @@ -98,7 +98,7 @@ public class AccountDTO implements Serializable { private String activationCoupon; - private Map attributes = new HashMap<>(); + private final Map attributes = new HashMap<>(); public AccountDTO() { diff --git a/sources/api/src/main/java/tools/dynamia/modules/saas/api/dto/AccountStatusDTO.java b/sources/api/src/main/java/tools/dynamia/modules/saas/api/dto/AccountStatusDTO.java index 86f9e65..64d916b 100644 --- a/sources/api/src/main/java/tools/dynamia/modules/saas/api/dto/AccountStatusDTO.java +++ b/sources/api/src/main/java/tools/dynamia/modules/saas/api/dto/AccountStatusDTO.java @@ -28,14 +28,14 @@ public class AccountStatusDTO implements Serializable { - private Long id; - private String name; - private AccountStatus status; - private Date statusDate; + private final Long id; + private final String name; + private final AccountStatus status; + private final Date statusDate; private String statusDescription; - private String globalMessage; - private boolean showGlobalMessage; - private String globalMessageType; + private final String globalMessage; + private final boolean showGlobalMessage; + private final String globalMessageType; private final BigDecimal balance; diff --git a/sources/api/src/main/java/tools/dynamia/modules/saas/api/enums/AccountPeriodicity.java b/sources/api/src/main/java/tools/dynamia/modules/saas/api/enums/AccountPeriodicity.java index 8f857d6..9773d32 100644 --- a/sources/api/src/main/java/tools/dynamia/modules/saas/api/enums/AccountPeriodicity.java +++ b/sources/api/src/main/java/tools/dynamia/modules/saas/api/enums/AccountPeriodicity.java @@ -25,7 +25,7 @@ public enum AccountPeriodicity { MONTHLY(30), YEARLY(365), UNLIMITED(-1); - private int days; + private final int days; AccountPeriodicity(int days) { this.days = days; diff --git a/sources/api/src/main/java/tools/dynamia/modules/saas/api/enums/AccountStatus.java b/sources/api/src/main/java/tools/dynamia/modules/saas/api/enums/AccountStatus.java index cd6fb0c..220c075 100644 --- a/sources/api/src/main/java/tools/dynamia/modules/saas/api/enums/AccountStatus.java +++ b/sources/api/src/main/java/tools/dynamia/modules/saas/api/enums/AccountStatus.java @@ -23,6 +23,6 @@ */ public enum AccountStatus { - NEW, ACTIVE, CANCELED, SUSPENDED; + NEW, ACTIVE, CANCELED, SUSPENDED } diff --git a/sources/core/pom.xml b/sources/core/pom.xml index 655420d..2e1398b 100644 --- a/sources/core/pom.xml +++ b/sources/core/pom.xml @@ -22,10 +22,10 @@ tools.dynamia.modules tools.dynamia.modules.saas.parent - 3.4.4 + 3.5.0 tools.dynamia.modules.saas - 3.4.4 + 3.5.0 DynamiaModules - SaaS Core https://www.dynamia.tools/modules/saas @@ -49,13 +49,20 @@ tools.dynamia.modules tools.dynamia.modules.saas.api - 3.4.4 + 3.5.0 tools.dynamia.modules tools.dynamia.modules.saas.jpa - 3.4.4 + 3.5.0 + + + tools.dynamia + tools.dynamia.integration + ${dynamiatools.version} + + junit junit diff --git a/sources/core/src/main/java/tools/dynamia/modules/saas/AccountRequestFilter.java b/sources/core/src/main/java/tools/dynamia/modules/saas/AccountRequestFilter.java index ebc53fe..d7967c6 100644 --- a/sources/core/src/main/java/tools/dynamia/modules/saas/AccountRequestFilter.java +++ b/sources/core/src/main/java/tools/dynamia/modules/saas/AccountRequestFilter.java @@ -23,8 +23,7 @@ public AccountRequestFilter() { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - if (request instanceof HttpServletRequest) { - var req = (HttpServletRequest) request; + if (request instanceof HttpServletRequest req) { String subdomain = HttpUtils.getSubdomain(req); if (subdomain != null) { var accountId = (Long) req.getAttribute(AccountServiceAPI.CURRENT_ACCOUNT_ID_ATTRIBUTE); diff --git a/sources/core/src/main/java/tools/dynamia/modules/saas/AccountSessionHolder.java b/sources/core/src/main/java/tools/dynamia/modules/saas/AccountSessionHolder.java index ef4f089..5ae0fa7 100644 --- a/sources/core/src/main/java/tools/dynamia/modules/saas/AccountSessionHolder.java +++ b/sources/core/src/main/java/tools/dynamia/modules/saas/AccountSessionHolder.java @@ -23,7 +23,9 @@ import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import tools.dynamia.domain.util.DomainUtils; +import tools.dynamia.integration.CloneableThreadLocalObject; import tools.dynamia.integration.Containers; +import tools.dynamia.integration.ThreadLocalObjectContainer; import tools.dynamia.integration.sterotypes.Component; import tools.dynamia.modules.saas.api.AccountException; import tools.dynamia.modules.saas.api.dto.AccountDTO; @@ -39,7 +41,7 @@ */ @Component("accountSessionHolder") @SessionScope -public class AccountSessionHolder implements Serializable { +public class AccountSessionHolder implements Serializable, CloneableThreadLocalObject { @JsonIgnore @@ -61,7 +63,7 @@ public AccountSessionHolder(AccountService service) { public static AccountSessionHolder get() { AccountSessionHolder accountSessionHolder = null; try { - accountSessionHolder = Containers.get().findObject(AccountSessionHolder.class); + accountSessionHolder = ThreadLocalObjectContainer.getObject(AccountSessionHolder.class); } catch (Exception e) { RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); if (attributes != null) { @@ -140,4 +142,15 @@ public ZoneId getAccountTimeZone() { } return accountTimeZone; } + + public AccountSessionHolder clone() { + AccountSessionHolder accountSessionHolder = new AccountSessionHolder(service); + accountSessionHolder.currentId = this.currentId; + accountSessionHolder.accountLocale = this.accountLocale; + accountSessionHolder.accountTimeZone = this.accountTimeZone; + accountSessionHolder.currentDTO = this.currentDTO; + return accountSessionHolder; + } + + } diff --git a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/Account.java b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/Account.java index 78e4eac..4040c81 100644 --- a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/Account.java +++ b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/Account.java @@ -534,7 +534,7 @@ public String toString() { @Override public AccountDTO toDTO() { - System.out.println("Loading Account DTO: " + toString()); + System.out.println("Loading Account DTO: " + this); AccountDTO dto = DomainUtils.autoDataTransferObject(this, AccountDTO.class); String logoURL = null; diff --git a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/enums/AccessControl.java b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/enums/AccessControl.java index ccb0783..6776cf0 100644 --- a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/enums/AccessControl.java +++ b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/enums/AccessControl.java @@ -18,5 +18,5 @@ package tools.dynamia.modules.saas.domain.enums; public enum AccessControl { - ALLOWED, DENIED, DELEGATE; + ALLOWED, DENIED, DELEGATE } diff --git a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/enums/ResellerComissionStatus.java b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/enums/ResellerComissionStatus.java index 25c7799..ba385bd 100644 --- a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/enums/ResellerComissionStatus.java +++ b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/enums/ResellerComissionStatus.java @@ -18,5 +18,5 @@ package tools.dynamia.modules.saas.domain.enums; public enum ResellerComissionStatus { - PENDING, PAYED, CANCELED; + PENDING, PAYED, CANCELED } diff --git a/sources/core/src/test/java/tools/dynamia/modules/saas/AccountTest.java b/sources/core/src/test/java/tools/dynamia/modules/saas/AccountTest.java index 42bd0dd..7398236 100644 --- a/sources/core/src/test/java/tools/dynamia/modules/saas/AccountTest.java +++ b/sources/core/src/test/java/tools/dynamia/modules/saas/AccountTest.java @@ -32,7 +32,7 @@ public void testTrialPeriod() { left = account.computeTrialLeft(account.getFreeTrial(), createDate(2022, 1, 15)); System.out.println("Trial Left 3 = " + left); - Assert.assertEquals(0 + 1, left); + Assert.assertEquals(1, left); left = account.computeTrialLeft(account.getFreeTrial(), createDate(2022, 1, 20)); System.out.println("Trial Left 4 = " + left); diff --git a/sources/jpa/pom.xml b/sources/jpa/pom.xml index 26b1426..010c19f 100644 --- a/sources/jpa/pom.xml +++ b/sources/jpa/pom.xml @@ -22,7 +22,7 @@ tools.dynamia.modules.saas.parent tools.dynamia.modules - 3.4.4 + 3.5.0 4.0.0 DynamiaModules - SaaS JPA @@ -33,7 +33,7 @@ tools.dynamia.modules tools.dynamia.modules.saas.api - 3.4.4 + 3.5.0 tools.dynamia diff --git a/sources/pom.xml b/sources/pom.xml index 28f6994..8bd8ed7 100644 --- a/sources/pom.xml +++ b/sources/pom.xml @@ -21,7 +21,7 @@ 4.0.0 tools.dynamia.modules tools.dynamia.modules.saas.parent - 3.4.4 + 3.5.0 pom DynamiaModules - SaaS DynamiaTools extension to create SaaS applications with accounts control and multi tenants in same @@ -59,8 +59,8 @@ - 5.4.1 - 7.4.0 + 5.4.7 + 7.5.0 21 3.14.0 UTF-8 @@ -68,7 +68,7 @@ 6.5.0 5.14.0 - 3.5.5 + 3.5.8 diff --git a/sources/remote/pom.xml b/sources/remote/pom.xml index 265b37b..053f81a 100644 --- a/sources/remote/pom.xml +++ b/sources/remote/pom.xml @@ -22,7 +22,7 @@ tools.dynamia.modules.saas.parent tools.dynamia.modules - 3.4.4 + 3.5.0 4.0.0 @@ -35,7 +35,7 @@ tools.dynamia.modules tools.dynamia.modules.saas.jpa - 3.4.4 + 3.5.0 diff --git a/sources/remote/src/main/java/tools/dynamia/modules/saas/remote/RemoteAccountServiceAPI.java b/sources/remote/src/main/java/tools/dynamia/modules/saas/remote/RemoteAccountServiceAPI.java index 18e5efa..ed61b0d 100644 --- a/sources/remote/src/main/java/tools/dynamia/modules/saas/remote/RemoteAccountServiceAPI.java +++ b/sources/remote/src/main/java/tools/dynamia/modules/saas/remote/RemoteAccountServiceAPI.java @@ -278,12 +278,11 @@ public void beforeQuery(QueryParameters params) { } private String getLocalInfo() { - StringBuilder info = new StringBuilder(); - info.append("User Dir:").append(System.getProperty("user.dir")).append(","); - info.append("OS:").append(System.getProperty("os.name")).append(","); - info.append("Port:").append(env.getProperty("server.port")).append(","); - info.append("Datasource:").append(env.getProperty("spring.datasource.url")); - return info.toString(); + String info = "User Dir:" + System.getProperty("user.dir") + "," + + "OS:" + System.getProperty("os.name") + "," + + "Port:" + env.getProperty("server.port") + "," + + "Datasource:" + env.getProperty("spring.datasource.url"); + return info; } @Scheduled(cron = "0 0 6 * * *") diff --git a/sources/ui/pom.xml b/sources/ui/pom.xml index 08ce125..92cbd1f 100644 --- a/sources/ui/pom.xml +++ b/sources/ui/pom.xml @@ -22,10 +22,10 @@ tools.dynamia.modules tools.dynamia.modules.saas.parent - 3.4.4 + 3.5.0 tools.dynamia.modules.saas.ui - 3.4.4 + 3.5.0 DynamiaModules - SaaS UI https://www.dynamia.tools/modules/saas diff --git a/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/AccountNavigationRestriction.java b/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/AccountNavigationRestriction.java index 3717622..14a1325 100644 --- a/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/AccountNavigationRestriction.java +++ b/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/AccountNavigationRestriction.java @@ -67,9 +67,7 @@ public Boolean allowAccess(NavigationElement element) { return null; } } - } else if (account.getType().isAdmin()) { - return true; - } + } else return account.getType().isAdmin(); return false; } diff --git a/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/action/ViewAccountLogAction.java b/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/action/ViewAccountLogAction.java index 0ced64e..4f28513 100644 --- a/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/action/ViewAccountLogAction.java +++ b/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/action/ViewAccountLogAction.java @@ -56,7 +56,6 @@ public void actionPerformed(CrudActionEvent evt) { if (logs.isEmpty()) { UIMessages.showMessage("No logs found", MessageType.WARNING); - return; } else { Viewer viewer = new Viewer("table", AccountLog.class); viewer.setValue(logs); diff --git a/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/controllers/AccountProfileController.java b/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/controllers/AccountProfileController.java index 508a62d..f7c2a6c 100644 --- a/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/controllers/AccountProfileController.java +++ b/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/controllers/AccountProfileController.java @@ -54,7 +54,7 @@ public class AccountProfileController extends CrudController { private Listbox modules; @Wire private Listbox restrictions; - private List toDelete = new ArrayList<>(); + private final List toDelete = new ArrayList<>(); // autowired by zk composer @Wire @@ -164,7 +164,7 @@ private Module getSelectedModule() { private AccountProfileRestriction getSelectedPermiso() { if (restrictions != null) { - return (AccountProfileRestriction) restrictions.getSelectedItem().getValue(); + return restrictions.getSelectedItem().getValue(); } else { return null; } diff --git a/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/vm/AccountProfileCrudVM.java b/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/vm/AccountProfileCrudVM.java index 1ef3750..4dfafc4 100644 --- a/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/vm/AccountProfileCrudVM.java +++ b/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/vm/AccountProfileCrudVM.java @@ -55,10 +55,10 @@ public class AccountProfileCrudVM extends AbstractService implements FormCrudViewModel { - private ModuleContainer moduleContainer = Containers.get().findObject(ModuleContainer.class); + private final ModuleContainer moduleContainer = Containers.get().findObject(ModuleContainer.class); - private List toDelete = new ArrayList<>(); + private final List toDelete = new ArrayList<>(); private AccountProfile model; private Object selectedItem; private AccountProfileRestriction selectedRestriction; @@ -163,7 +163,7 @@ private void initTreeModel() { if (!module.isEmpty()) { DefaultTreeNode modNode = new DefaultTreeNode(module, new ArrayList()); rootNode.add(modNode); - renderPageGroupNode(Arrays.asList(module.getDefaultPageGroup()), modNode); + renderPageGroupNode(Collections.singletonList(module.getDefaultPageGroup()), modNode); renderPageGroupNode(module.getPageGroups(), modNode); } }