---
schema_version: "1"
id: bc744364cdfa222c075cb7a4dfe866abebde526cb3eed9ccaf1c49e0aada836c
title: Multi-Level Metadata
collection: mulesoft
version: current
locale: en-us
portal_url: https://docs.salesforce.com/mulesoft/current/en-us/mule-sdk/latest/multi-level-metadata.md
canonical_url: https://docs.mulesoft.com/mule-sdk/latest/multi-level-metadata
source_site: docs.mulesoft.com
content_type: documentation
content_hash: aba36382a4549e9fcbd0e262e3513160508e020d4bdcd3b0e769fb1ce262c1e4
description: MuleSoft Documentation Site
release: "1.13"
latest: true
---
---
product: Mule SDK
version: 1.13
is-latest-version: true
---

# Multi-Level Metadata

> For the full documentation index, see: https://docs.mulesoft.com/llms.txt

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:

```java
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:

```java
@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`:

```java
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;
  }
}
```

```java
public class OutputOperationTypeResolver implements OutputTypeResolver<ActionIdentifier>{

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

```java
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:

```java
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;
  }

}
```

<table><tbody><tr><td><i data-value="1"></i><b>1</b></td><td>Only the services are retrieved. The actions of a service will be retrieved on demand.</td></tr><tr><td><i data-value="2"></i><b>2</b></td><td>Build a single <code>MetadataKey</code> tree with a new, complete level of metadata, in this case, the actions level.</td></tr><tr><td><i data-value="3"></i><b>3</b></td><td>Add the actions of that service as children.</td></tr></tbody></table>

## 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:

```java
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;
  }
}
```

<table><tbody><tr><td><i data-value="1"></i><b>1</b></td><td>The <code>clazz</code> field must be inserted by the end user without hints.</td></tr></tbody></table>

In this case, it also means that the `getKeys` method cannot return all the possible classes:

```java
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;
  }

}
```

<table><tbody><tr><td><i data-value="1"></i><b>1</b></td><td>Return an empty set of <code>MetadataKey</code> because the end user will provide this information.</td></tr><tr><td><i data-value="2"></i><b>2</b></td><td>Build a single <code>MetadataKey</code> tree with a new, complete level of metadata, in this case, the <code>methodIds</code> level.</td></tr><tr><td><i data-value="3"></i><b>3</b></td><td>Add the <code>methodIds</code> of that class as children.</td></tr></tbody></table>
