Free MuleSoft CONNECT Keynote & Expo Pass Available!

Register now+
Nav

Value Providers

When developing a connector, you can let the end user select a parameter from a set of values. This makes it easier for the user to know what to configure, and you can be sure that the configured values will be correct.

You can provide this functionality for known values by using a Java Enum as a parameter. The SDK automatically takes all the values from the Enum so they can be provided as a combo-selector in the UI.

To include values that are not known, such as custom values, you should use Value Providers, instead.

Java Enum

Assume that you are developing a connector for a Content Publishing Service that can publish content with these roles:

  • ADMIN

  • READER

  • WRITER

To provide these roles, you simply create a UserRole Enum and expose it as a parameter of the publishAs operation, for example:

Example: Java Enum the Defines Known Roles

         
      
1
2
3
public enum UserRole {
    ADMIN, READER, WRITER
}

The operation in the next example lets the user publish content using a certain UserRole.


         
      
1
2
3
public void publishAs(@Content Object content, UserRole roleEnum){
    System.out.println("Publishing " + content + " with Role: " + roleEnum);
}

This solution is easy and effective, but it carries several limitations and problems:

  • Does not support custom values: Because the parameter is based on a Enum, you cannot define a custom value. For example, if the service to which you are connecting can be configured with custom roles, you will not be able to continue using the UserRole Enum because it will limit the connector’s capabilities.

  • The values are defined statically: You cannot change the values depending on a connection or configuration.

  • Java Enums have naming restrictions: You cannot have an Enum value starting with a Number or special characters.

If need custom roles but do not want to lose the drop-down list of known User Roles, you can use Value Providers for dynamic values and known values.

Value Providers

Value Providers is a mechanism for providing values in a dynamic and smart way for any parameter in any component of a connector: Operations, Sources, Configurations, Connection Providers, scopes, and so on.

To Implement Value Providers

This example produces the same behavior as the Enum solution above, but it provides the benefit of an open set of values. It will support a custom value.

  1. Create a Java Class implementing ValueProvider.

    When implementing this interface, you must implement the resolve() method, which returns a set of values.

    
                 
              
    1
    2
    3
    4
    5
    6
    7
    
    public class StaticUserRoleValueProvider implements ValueProvider {
    
        @Override
        public Set<Value> resolve() {
            return ValueBuilder.getValuesFor("ADMIN", "READER", "WRITER");
        }
    }
  2. Mark the parameter that requires dynamic values with the @OfValues() annotation.

    @OfValues requires a ValueProvider implementation that is bound to the parameter, for example:

    
                 
              
    1
    2
    3
    4
    
    public void publishAs(@Content Object content,
    @OfValues(StaticUserRoleValueProvider.class) String userRole){ (1)
        System.out.println("Publishing " + content + " with Role: " + userRole);
    }
  3. Now, you can use it in Studio or Design Center to start retrieving values.

    value provider static

Value Providers with Connections or Configurations

Value Providers can receive connections and configurations. This makes it possible to resolve values based on the current connection and configuration.

You cannot inject connections and configurations into Value Providers that are located in connection and configuration parameters.

Receiving a Connection or Configuration

You use the @Connection and @Config annotations to declare the use of connections and configurations inside a Value Provider. To work, a Value Provider that uses these annotations depends on a valid configuration or connection. The SDK ensures that the resolving logic will not be executed unless the connection and configuration are valid.


           
        
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ConnectedValueProvider implements ValueProvider {

  @Connection
  ServiceConnection connection;

  @Config
  ServiceConfig config;

  @Override
  public Set<Value> resolve() throws ValueResolvingException {
    //Do whatever is required with the connection or config
    List<String> result = connection.retrieveInfo();
    return ValueBuilder.getValuesFor(result);
  }
}
The injected connections and configurations must be of a type that is compatible with those defined in the operation or source where the Value Provider is referenced.

Example 2: Connected Value Provider Use Case

In the Roles example above, the service could define custom roles, but it was not possible for the connector to communicate those roles.

Once the Value Provider is implemented to resolve values through a working connection, it becomes possible to fetch the available roles and communicate them through the Value Provider:


          
       
1
2
3
4
5
6
7
8
9
10
public class UserRoleValueProvider implements ValueProvider {

  @Connection
  ServiceConnection connection;

  @Override
  public Set<Value> resolve() throws ValueResolvingException {
    return ValueBuilder.getValuesFor(connection.getAvailableRoles());
  }
}

Value Providers that Depend on Other Parameters

In addition to injecting connections and configurations, Value Providers can depend on other parameters of the same context. The SDK ensures that the Value Provider resolving logic will not be executed until the required parameters are configured.

The words "same context" mean that if the Value Provider is used in a component, the required parameter must exist in that component. For example, if the configuration FancyConfig with a Value Provider in the parameter dynamicParam requires the value of the parameter aConfigParam, aConfigParam must exist in the FancyConfig configuration.

The use of expressions in the required parameters might disable the execution of the Value Provider due to the impossibility of resolving the expression without an active event.

Declaring Required Parameters

As with connectors and configurations, you use the @Parameter annotation to declare a parameter that is required for executing the resolving logic. You use the annotation in a field of the Value Provider with the same type and name as the required parameter, such as String requiredParam in these examples:

Example External parameters: Operation declaring two params, one with a value provider

           
        
1
2
3
public void operationWithValueProvider(String requiredParam, @OfValues(ValueProviderWithRequiredParams.class) String dynamicParam){

}
Example External parameters: Value provider requiring the requiredParam parameter.

           
        
1
2
3
4
5
6
7
8
9
10
public class ValueProviderWithRequiredParams implements ValueProvider {

    @Parameter
    String requiredParam;

    @Override
    public Set<Value> resolve() {
      return ValuesBuilder.getValuesFor(param);
    }
}
What Happens If The Required Parameter Is Not Configured?

If the parameter is defined as required in the component but is not configured by the end user, the Value Provider will not execute. However, if the parameter is defined as optional, the Value Provider will be executed with a Null value, so you will need to handle the nullability.

Example 3: Value Provider with Context Parameters

Consider the case where you want to have a Date Picker for the day and month. You can easily represent them with two Enums, but not all months have the same number of days. So the representation might allow the user to configure invalid dates.

To fix this issue:

  1. Define an operation that exposes a Date Picker.

    The operation receives two parameters, an monthEnum that statically communicates all the available months and a day that is used to communicate the day of the month.

    Publish On Date Operation
    
                   
                
    1
    2
    
    public void publishOnDate(Month monthEnum, @OfValues(DayValueProvider.class) String day) {
    }
  2. Define the Month Enum.

    The Month contains all the available months and identifies the number of days in each month.

    MonthEnum Enum
    
                   
                
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    public enum Month {
    
        JANUARY(31), FEBRUARY(28), MARCH(31), APRIL(30), MAY(31), JUNE(30),
        JULY(31), AUGUST(31), SEPTEMBER(30), OCTOBER(31), NOVEMBER(30), DECEMBER(31);
    
        private int dayCount;
    
        MonthEnum(int i) {
            dayCount = i;
        }
    
        public int getDayCount() {
            return dayCount;
        }
    }
  3. Create a Value Provider that consumes the selected Month.

    Depending on the selected month, the Value Provider will dynamically provide all the available days in that month. The DayValueProvider indicates that it requires the parameter monthEnum to work.

    
                   
                
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    public class DayValueProvider implements ValueProvider {
    
        @Parameter
        Month monthEnum; (1)
    
        @Override
        public Set<Value> resolve() {
          return ValueBuilder.getValuesFor(getNumbersFrom(1, monthEnum.getDayCount())
                  .stream()
                  .map(num -> String.format("%02d", num)));
        }
    
        List<Integer> getNumbersFrom(int init, int end){
            List<Integer> numbers = new ArrayList<>(end - init);
            for (int i = init; i <= end; i++) {
                numbers.add(i);
            }
            return numbers;
        }
    }
  4. Result!

    As shown in the animation below, the Day selector gets populated dynamically based on the Month enum parameter value.

    value provider months

Value Structure

Value Providers return a set of Values. A Value is a simple structure composed of these properties:

  • id : A unique identifier for this value. This is required.

  • displayName: A name that will be displayed in the UI. This is optional. By default, the ID will be used as the Display Name.

How to create a Value

There is a unique way to create values by using ValueBuilder.


           
        
1
2
3
4
ValueBuilder adminValueBuilder = ValueBuilder.newValue("ADMIN_USR_ROLE"); (1)
adminValueBuilder.withDisplayName("Admin"); (2)
Value adminValue = newValue.build(); (3)
1 You must create the ValueBuilder with the ID of the Value.
2 Optionally, you can enrich the value with a Display Name.
3 Build the builder to return a Value instance.

ValueBuilder Utils

ValueBuilder provides utilities to make it easier to create values for certain cases.

If you have a List<String>, String[], Stream<String>, or Map<String, String> with values that need to be transformed to Values, the easiest way to make this transformation is to use getValuesFor().


         
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Array Case
Set<Value> arrayCase = ValueBuilder.getValuesFor("Admin", "Writer");

// List Case
List<String> valueList = new ArrayList<>();
valueList.add("Admin");
valueList.add("Writer");
Set<Value> listCase = ValueBuilder.getValuesFor(valueList);

// Stream Case
Set<Value> streamCase = ValueBuilder.getValuesFor(valueList.stream());

// Map Case
// The Key will be considered as ID and the Value as Display Name
Map<String, String> valueMap = new HashMap<>();
valueMap.put("ADMIN_USR_ROLE", "Admin");
valueMap.put("WRITER_USR_ROLE") "Writer");
Set<Value> mapCase = ValueBuilder.getValuesFor(futureValues);