Multi-Level Metadata | MuleSoft Documentation
# Multi-Level Metadata
Imagine the case where your end user will use an operation that invokes an action provided by a service. To know the metadata that the action requires as arguments or the metadata of what it returns, you need to differentiate the service from the action to be executed. You do this with a metadata key id. For these cases, the metadata key id does not need to be a single `String`. It can instead contain multiple parts bundled within a complex parameter that holds the information needed to resolve the metadata.
## Implementing a Multi-Level Metadata Key
It is necessary to set the order in which parameters of the metadata key are specified, starting with `1`. Returning to the example above, the end user will first specify the service field, then the action to invoke. So, the service parameter needs to set `order = 1` and the action parameter `order = 2`.
All the parameters of the POJO representing the `MetadataKeyId` must be annotated with a `@MetadataKeyPart` annotation that sets the `order` value for it. These annotated fields must be of type `String`.
Here is an example of a metadata key id class:
```
public class ActionIdentifier {
 @Parameter
 @MetadataKeyPart(order = 1) **(1)**
 private String service;
 @Parameter
 @MetadataKeyPart(order = 2) **(1)**
 private String action;
 public String getService() {
  return service;
 }
 public String getAction() {
  return action;
 }
 public void setService(String service) {
  this.service = service;
 }
 public void setAction(String action) {
  this.action = action;
 }
}
```

The next example shows an operation that uses a `MetadataKeyId` that is a complex type:
```
@OutputResolver(output = OutputOperationTypeResolver.class)
public Object invoke( @ParameterGroup(name = "Operation") @MetadataKeyId(OperationTypeKeysResolver.class) ActionIdentifier identifier,
           @TypeResolver(InputOperationTypeResolver.class) Map<String, Object> args){
 return invokeAction(identifier.getService(), identifier.getAction(), args);
}
```

Next is an example of a `TypeKeysResolver` that returns each available `MetadataKey`:
```
public class OperationTypeKeysResolver implements TypeKeysResolver {
 @Override
 public Set<MetadataKey> getKeys(MetadataContext context) throws MetadataResolvingException, ConnectionException {
  Set<MetadataKey> keys = new HashSet<>();
  for(String service : getServices()){
   MetadataKeyBuilder key = MetadataKeyBuilder.newKey(service);
   for(String action : getActions(service)){
    key.withChild(MetadataKeyBuilder.newKey(action).build());
   }
   keys.add(key.build());
  }
  return keys;
 }
}
```

```
public class OutputOperationTypeResolver implements OutputTypeResolver<ActionIdentifier>{
 @Override
 public MetadataType getOutputType(MetadataContext context, ActionIdentifier key)
   throws MetadataResolvingException, ConnectionException {
   // Resolve the output metadata using the ActionIdentifier
 }
}
```

```
public class InputOperationTypeResolver implements InputTypeResolver<ActionIdentifier> {
 @Override
 public MetadataType getInputMetadata(MetadataContext context, ActionIdentifier key)
  throws MetadataResolvingException, ConnectionException {
  // Resolve the input metadata using the ActionIdentifier
 }
}
```

## Partial Fetching
**Available since version 1.1**
The process of getting each `MetadataKey` can be very time consuming. In the example that gets all the possible services and the actions of each service, the information you are fetching might be on another server.
Partial fetching enables you to resolve one level of the `MetadataKey` tree at a time.
The `getKeys` method from `TypeKeysResolver` will only return the first level of each tree, not the complete tree of each `MetadataKey`.
So, the amount of information needed is greatly reduced because you only need to get the possible services. Once a service is selected, you need to provide the next level of the `MetadataKey` tree.
To make this possible, your key resolver must implement `PartialTypeKeysResolver`, for example:
```
public class OperationTypeKeysResolver implements PartialTypeKeysResolver<ActionIdentifier> {
 @Override
 public Set<MetadataKey> getKeys(MetadataContext context) throws MetadataResolvingException, ConnectionException {
  Set<MetadataKey> keys = new HashSet<>();
  for(String service : getServices()){ **(1)**
   MetadataKeyBuilder key = MetadataKeyBuilder.newKey(service);
   keys.add(key.build());
  }
  return keys;
 }
 @Override
 public MetadataKey resolveChilds(MetadataContext metadataContext, ServiceOperation key)
   throws MetadataResolvingException, ConnectionException {
  if(key.getService() == null){
   throw new MetadataResolvingException("Missing Service name. Cannot resolve Actions without a service",
                     FailureCode.INVALID_METADATA_KEY);
  }
  MetadataKeyBuilder key = MetadataKeyBuilder.newKey(key.getService()); **(2)**
  for(String action : getActions(key.getService())){
   key.withChild(MetadataKeyBuilder.newKey(action).build()); **(3)**
  }
  return key;
 }
}
```

Only the services are retrieved. The actions of a service will be retrieved on demand.  
---  
Build a single `MetadataKey` tree with a new, complete level of metadata, in this case, the actions level.  
Add the actions of that service as children.  
## Using User Input As Partial Level
**Available since version 1.1**
In some cases, you might not be able to provide the end user with a hint about part of your MetadataKey. For example, the universe of options might be too big (a dropdown with all the classes in a classpath makes not sense) or when the starting point of the ID is a free input (for example, a query).
Imagine a `MetadataKeyId` that has a part that is a `String` representing a Java class. It can be very time consuming to retrieve all the classes, and it is complicated for the user to have so many options on a dropdown.
So, you can signal that a `MetadataKeyPart` will not be provided by the resolver and must be inserted by the user. You do this by setting the `providedByKeyResolver` to `false` value on the `MetadataKeyPart` annotation.
Here is an example where the POJO representing the `MetadataKeyId` represents a Java method:
```
public class MethodIdentifier{
 @Parameter
 @Alias("class")
 @MetadataKeyPart(order = 1, providedByKeyResolver = false) **(1)**
 private String clazz;
 @Parameter
 @Alias("method")
 @MetadataKeyPart(order = 2)
 private String methodId;
 @Override
 public String getClazz() {
  return clazz;
 }
 @Override
 public String getMethodId() {
  return methodId;
 }
 @Override
 public void setClazz(String clazz) {
  this.clazz = clazz;
 }
 @Override
 public void setMethodId(String methodId) {
  this.methodId = methodId;
 }
}
```

The `clazz` field must be inserted by the end user without hints.  
---  
In this case, it also means that the `getKeys` method cannot return all the possible classes:
```
public class MethodTypeKeysResolver implements PartialTypeKeysResolver<MethodIdentifier> {
 @Override
 public Set<MetadataKey> getKeys(MetadataContext context) throws MetadataResolvingException, ConnectionException {
  return emptySet(); **(1)**
 }
 @Override
 public MetadataKey resolveChilds(MetadataContext metadataContext, MethodIdentifier key)
   throws MetadataResolvingException, ConnectionException {
  if(key.getClazz() == null){
   throw new MetadataResolvingException("Missing Class name. Cannot resolve Methods without a target Class",
                     FailureCode.INVALID_METADATA_KEY);
  }
  MetadataKeyBuilder key = MetadataKeyBuilder.newKey(key.getClazz()); **(2)**
  for(String methodId : getMethodIds(key.getClazz())){
   key.withChild(MetadataKeyBuilder.newKey(methodId).build()); **(3)**
  }
  return key;
 }
}
```

Return an empty set of `MetadataKey` because the end user will provide this information.  
---  
Build a single `MetadataKey` tree with a new, complete level of metadata, in this case, the `methodIds` level.  
Add the `methodIds` of that class as children.