# FAQ (Frequently Asked Questions)

# Target Audience

The target audience for this document is: Developers and implementers of this system

# Why is an enum field recognized as an object field by the system?

Please ensure that the package name of the enum field contains the 'enums' part, for example:

package tech.muyan.dynamic.task.enums  // --> Package name contains 'enums' part, can be correctly recognized

Enum DynamicTaskType {
  RUN_AT_STARTUP('RUN_AT_STARTUP'),
  CRON_TASK('CRON_TASK'),
  SCHEDULE_TASK('SCHEDULE_TASK'),

  ;

  String value
}
1
2
3
4
5
6
7
8
9
10
11

This can be recognized normally, while the following enum will be recognized as an object type:

package tech.muyan.dynamic.task // --> Package name does not contain 'enums' part, cannot be correctly recognized

enum DynamicTaskType {
  RUN_AT_STARTUP('RUN_AT_STARTUP'),
  CRON_TASK('CRON_TASK'),
  SCHEDULE_TASK('SCHEDULE_TASK'),

  ;

  String value
}
1
2
3
4
5
6
7
8
9
10
11

# How to manually inject service definitions in customized code

During project implementation, for code reuse, some common logic may be defined in the system in the form of services. In customized Groovy code, you can conveniently find the service definition and call its related methods.

Refer to the following code to manually find the service definition from the bean registry by name, where authorityService is the name of the service, corresponding to the class name of the Service definition, with the first letter lowercase.

  import tech.muyan.BeanHelper
  import grails.core.GrailsApplication

  GrailsApplication grailsApplication = (application as GrailsApplication)
  AuthorityService authorityService = BeanHelper.getBean(grailsapplication, "authorityService")  
1
2
3
4
5

# Default filter defined in the form is not working

Please confirm that the field used as a filter condition is included in the form fields. If the field used as a filter condition is not returned to the frontend, it cannot be used as a filter condition.

# Unable to successfully import extInfo field via CSV

Please confirm that for the extInfo field (stored in the database as JSON type) in the imported CSV, if the content contains quotation marks ("), use two double quotes ("") to escape, rather than using a backslash (\) to escape.

# Sharing global configurations, connections, or caching data between Dynamic Logic executions

In some scenarios, you may need to share some data across multiple Dynamic Logic executions, typical scenarios include:

  1. Saving and sharing some computationally expensive cached data
  2. Some configurations or resources that need to be shared globally, such as stateful connections to third-party systems, management of connections to Kafka, Message Queue, etc.
  3. There may be a business logic order between different Dynamic Logic executions, and unavoidably, the results of previous steps can be cached.

The current system provides the tech.muyan.helper.RegistryHelper class for managing globally shared data, which can be accessed through:

  • RegistryHelper.memoryGet(key) to retrieve cached data,
  • RegistryHelper.memoryPut(key, value) to set cached data and return the currently saved data,
  • RegistryHelper.memoryRemove(key) to remove cached data.
Note The global data shared by the above methods is stored in memory, so it only supports data sharing within a single server instance and does not support data sharing between multiple server instances.

# User opens Dashboard, Widget reports an error

Error screenshot as follows:

Error screenshot

First confirm:

  1. Please confirm that the jar file was not packaged on a Windows system. Currently, Grails has a bug that causes the class file names generated by compiling the gson file in the backend deployment package generated on Windows to be incorrect, resulting in all enum fields being passed to the frontend with incorrect values.

Solution:

  1. Find the action run corresponding to the commit SHA to be deployed from the Github action page, and directly download the deployment package generated by the action run.
  2. Package the backend application on MacOS or Linux.

# Backend starts normally but frontend connection shows 401 and cannot connect

Please check if the application.yml file is correct. Common problems include indentation or other format issues caused by modifications, which lead to related configurations not being correctly read by the system. You can try restoring the application.yml file to the original version in the template repository or from someone else.

# After the system frontend and backend go live, frontend requests to backend APIs report errors or users cannot log in, blank screen, etc.

Please try the following operations:

  1. On the web server side, delete or force refresh the nginx cache.
  2. On the browser side, clear the entries in the frontend localStorage.
  3. On the browser side, delete or force refresh the browser cache.

# Domain contains nullable primitive type fields, object fails to save when the value is null

In the Domain definition, please define nullable primitive type fields as their corresponding wrapper types, such as changing int to Integer, double to Double, boolean to Boolean, etc.

# Fields contained in the Domain are not displayed on the frontend

Please check if the field is defined in the constraints of the Domain definition

static constraints = {
  attachments nullable: true
}
1
2
3

Here's an example configuration:

class Feedback implements Serializable, MultiTenant<Feedback>,
  Auditable, Stampable<Feedback>, HasComment<Feedback> {
  List<StorageFieldValue> attachments
  static hasMany = [attachments: StorageFieldValue]
}
1
2
3
4
5

# Unable to save associations in One-to-many relationships

A usable definition can be found in DynamicMenu.groovy, which defines a parent field pointing to itself and a reversely referenced children field

For specific GORM documentation, refer to GORM Associations (opens new window)

class DynamicMenu implements MultiTenant<DynamicMenu>,
  Auditable, Stampable<DynamicMenu>, Serializable {
  // Other fields omitted
  // Field in Many side pointing to One side
  DynamicMenu parent
  
  // Field in One side pointing to Many side
  static hasMany = [children: DynamicMenu]
  // Please note that you cannot explicitly define the children field using List<DynamicMenu> children
  // Otherwise, GORM will not be able to save the parent field

  static constraints = {
    // Other constraints omitted
    parent nullable: true
    children nullable: true
  }
  
  static mapping = {
    // No need to define mapping for the children field, it's only used as a reverse reference
    parent index: 'dynamic_menu_parent_idx'
  }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# How to define a new Websocket interface

  1. Implement the tech.muyan.websocket.WebSocketService interface
  2. Implement the abstract String getWebSocketPath() method to return the websocket path
  3. Implement the void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception method to handle websocket messages

# How to define default values for fields in the first step of a Wizard

Last Updated: 12/4/2024, 1:00:56 PM