Module Development
Overview
The Module system is designed to create reusable and extensible plugin components that implement specific business functionalities. A module typically handles inputs, processes data according to its logic, and then produces outputs.
This document provides an overview of:
- Module Input and Output Parameters
- Steps to Develop a New Module
1. Module Input and Output Parameters
Modules generally define their input and output parameters as structured types, which ensure consistency across the application. Let’s break down the input and output concepts:
Input Parameters
Input parameters are encapsulated into a dedicated structure, which ensures type safety and consistency. Each module can define specific arguments it needs to function effectively.
-
Example (Input Type):
ArgsCHxRatestype ArgsCHxRates struct {Currencies []string `json:"currencies"` // List of currency codes to filter (e.g., ["USD", "EUR"]).}- Key Points:
- Inputs are defined as JSON-serializable structs.
- Fields in the struct represent the configurable arguments a module expects.
- Use JSON field annotations (
json:"field") for deserialization.
- Key Points:
-
How it is Passed: During runtime, the input parameters are unmarshalled from a
stringargument (LayerArgs) into the specific struct usingjson.Unmarshal.
Output Parameters
Output parameters are also structured in a formal way to ensure uniformity in how modules return responses.
-
Example (Output Type):
OutCHxRatestype OutCHxRates struct {Rates CHRates // Object containing exchange rates processed by the module.}-
CHRatesfurther encapsulates the output details, such as:type CHRates struct {Date string // Date for which the exchange rates are fetched.Rates []CHRate // List of filtered exchange rates.} -
Key Points:
- Outputs are structured in a nested manner to handle multiple levels of data representation.
- The top-level result (
OutCHxRates) is wrapped and returned, complying with a standard response format.
-
2. Developing New Modules
To create a new module, follow these structured steps:
Step 1: Define Input and Output Structures
-
Input: Create a struct to encapsulate the arguments your module requires.
- Use expressive field names that are consistent with the module’s functionality.
- Add JSON tags for serialization/deserialization.
type ArgsMyModule struct {Param1 string `json:"param1"` // Example: Some input parameterParam2 int `json:"param2"` // Example: Another input (e.g., an integer value)} -
Output: Define a struct to encapsulate the result your module will produce.
- Ensure it is explicit and type-safe.
type OutMyModule struct {ResultDetails Details `json:"result_details"`}type Details struct {ID string `json:"id"`Name string `json:"name"`}
Step 2: Implement the Module Structure
Create your module by embedding shared behaviors from the base class or interfaces (e.g., PluginModule) and defining any module-specific attributes.
- Example:
type MyNewModule struct {PluginModule // Embedding shared functionality}
Use the NewModule function to initialize an instance of your module.
- Example:
func (r *PluginModule) NewMyNewModule() *MyNewModule {return &MyNewModule{}}
Step 3: Implement the Run Function
The Run function is the core of your module. It executes the business logic using input parameters and provides structured output.
-
Parse the incoming
paramsargument. -
Unmarshal JSON data into the input struct type.
-
Perform the module-specific operations.
-
Return the formatted output result.
-
Example Implementation:
func (m *MyNewModule) Run(params interface{}) interface{} {// Parse input parameterspr := reflect.ValueOf(params)p := pr.Interface().(Params)var args ArgsMyModuleerr := json.Unmarshal([]byte(p.LayerArgs.(string)), &args)if err != nil {return m.ErrorResponse(err)}// Business logic (perform the required tasks)result := Details{ID: "123",Name: "Test Result",}// Return the final structured responsereturn Response{State: cnst.STATE_DONE,Out: OutMyModule{ResultDetails: result},}}
Step 4: Add Helper Functions
If your module has operations that require additional logic (e.g., API calls, processing), define helper functions for these operations.
- Example:
func (m *MyNewModule) getDataFromAPI(url string) ([]byte, error) {resp, err := http.Get(url)if err != nil {return nil, fmt.Errorf("GET error: %v", err)}defer resp.Body.Close()if resp.StatusCode != http.StatusOK {return nil, fmt.Errorf("Status error: %v", resp.StatusCode)}body, err := ioutil.ReadAll(resp.Body)if err != nil {return nil, fmt.Errorf("Read error: %v", err)}return body, nil}
Summary
The Module system follows a structured approach:
- Input/Output parameters are defined using dedicated structs to ensure uniformity.
- Modules are initialized via factory functions (e.g.,
NewModuleXYZ). - The core logic resides in the
Runmethod. - Helper functions encapsulate additional operations like API requests, data parsing, etc.
By adhering to this structure, new modules can be easily integrated and reused while ensuring predictable behaviors in the plugin framework.