Using Dynamic Values in Microsoft Flow

Microsoft Flow provides a nice, low-friction environment for power users to automate tasks. When integrating custom web services into flow, we should be mindful of this ease-of-use capability and make our actions simple to call.

Recently, at the Live360! Conference in Orlando, I heard Matthew McDermott remark that he had to create a process for users to request new terms for a term set. As you may not know, the enterprise term store requires administrative privileges to update. It struck me as an ideal problem to solve with a Custom Connector in Flow.

I've written before about custom connectors and how they can provide capabilities that are un-achievable by most users in an organization. Using this framework, adding an action to add a term to a term set is quite straightforward. (The actual implementation is the topic for another post. Stay tuned!)

Gathering parameters for creating a term

When looking at the SharePoint CSOM code required to add a term, you will notice a few pieces of data that are required:

TaxonomySession txSes = TaxonomySession.GetTaxonomySession(ctx);
TermStore termStore = txSes.GetDefaultSiteCollectionTermStore();
TermGroup group = termStore.GetGroup(TermGroupId);
TermSet set = group.TermSets.GetById(TermSetId);

var newTerm = set.CreateTerm(addTermRequest.Term, 1033, Guid.NewGuid());
  • A SharePoint context (which requires URL and credentials)
  • Term Group Id
  • Term Set Id
  • Label for the new Term.

In our scenario, we will be establishing a context using stored credentials, so we don't need to collect those. (I promise, the creds are secure. Again, stay tuned.) But we do need the Id or Name of the term group and the term set. This information is likely not known by most users, since they are not readily visible when adding an item to a list.

In keeping with our goal of ease-of-use, we should provide a dropdown list of Term Groups, and once a Term Group is selected, show a list of Term Sets. For reference, this is very similar to the experience when referencing a SharePoint list. We provide a URL and can then select the list.

Supporting methods

To support the dropdown controls, we will need methods to read all Term Groups from the Term Store and to read all Term Sets from a specified Term Group. We will also need a method to add the term. Example calls to these methods are listed below -- we will refer to them in a bit. Notice that each method requires a Site Url, but the property name for each is prefixed to distinguish them.

GetTermGroups

POST https://<host>/api/GetTermGroups

{
  "tgSiteUrl":"https://<tenant>.sharepoint.com"
}

HTTP/1.1 200 OK
Content-type: application/json

{
  "termGroups": [
    {
      "termGroupId": "7caf3397-ZZa6-4d0d-8c00-aa8fdadfcXX3",
      "termGroupName": "Contoso"
    },
    {
      "termGroupId": "8e9c5770-ZZ4d-4324-b782-75a6fd95dXXb",
      "termGroupName": "Flow"
    }
  ]
}

GetTermSets

POST https://<host>/api/GetTermSets

{
  "tsSiteUrl":"https://<tenant>.sharepoint.com",
  "tsTermGroupId":"8e9c5770-ZZ4d-4324-b782-75a6fd95dXXb"
}

HTTP/1.1 200 OK
Content-type: application/json

{
  "termSets": [
    {
      "termSetId": "8e4a2c7a-ZZ18-4839-88b9-14a38be1XXXc",
      "termSetName": "Matt"
    }
  ]
}

AddTerm

POST https://<host>/api/AddTerm

{
  "atSiteUrl":"https://scmvp.sharepoint.com",
  "atTermGroupId":"8e9c5770-ZZ4d-4324-b782-75a6fd95dXXb",
  "atTermSetId":"8e4a2c7a-ZZ18-4839-88b9-14a38be1XXXc",
  "atTerm":"spfn"
}

Configuring Dynamic Values

With the methods to get the required parameters, we can update the OpenAPI document (f.k.a. Swagger), instructing it to use those methods. The definition of the AddTerm method starts out with a description of the parameters:

"definitions": {
  "AddTermRequest": {
    "type": "object",
    "properties": {
      "atSiteUrl": {
        "description": "Site Collection Url",
        "type": "string"
      },
      "atTermGroupId": {
        "description": "TermGroup Id",
        "type": "string"
      },
      "atTermSetId": {
        "description": "TermSet Id",
        "type": "string",
      },
      "atTerm": {
        "description": "Term to add",
        "type": "string"
      }
    },
    "required": [
      "atSiteUrl",
      "atTermGroupId",
      "atTermSetId",
      "atTerm"
    ]
  },
}

To get the dropdown control for a parameter, we need to provide the extension property x-ms-dynamic-values. I found the documentation of this property to be a bit confusing, so let me try to explain with examples.

In my scenario, I want the atTermGroupId to have a dynamic list of values. So I add the x-ms-dynamic-values property:

"atTermGroupId": {
  "description": "TermGroup Id",
  "type": "string",
  "x-ms-dynamic-values": {
  }
}

The values I want to show are returned by the GetTermGroups method. That becomes the operationId:

"atTermGroupId": {
  "description": "TermGroup Id",
  "type": "string",
  "x-ms-dynamic-values": {
    "operationId": "GetTermGroups"
  }
}

Next, the x-ms-dynamic-values properties needs to specify how to extract the dropdown items (and Id and Text) from the response. Referring to the sample output shown above, I see that method returns a collection with the name of "termGroups". Each item in the collection has a "termGroupId" and termGroupName". These are added as the value-* properties (not sure why it is called value-path instead of value-id):

"atTermGroupId": {
  "description": "TermGroup Id",
  "type": "string",
  "x-ms-dynamic-values": {
    "operationId": "GetTermGroups",
    "value-collection":"termGroups",
    "value-path":"termGroupId",
    "value-title":"termGroupName"
  }
}

It appears that we are done, but there is one more property that is required. The GetTermGroups operation has an input parameter - it requires the SiteUrl parameter:

"atTermGroupId": {
  "description": "TermGroup Id",
  "type": "string",
  "x-ms-dynamic-values": {
    "operationId": "GetTermGroups",
    "value-collection":"termGroups",
    "value-path":"termGroupId",
    "value-title":"termGroupName",
    "parameters": {
      "tgSiteUrl": <parameter-definition-here>
    }
  }
}

To fill in that <parameter-definition-here> token, we have two possible choices. If we want to hard-code a value, meaning the same value is always used, then we can specify that value:

   "tgSiteUrl":"https://example.com"

However, in our case, we want to pass the value entered by the user as the atSiteUrl parameter for the AddTerm operation. This is defined as a dynamic parameter. So, the parameter definition contains a "link" from the AddTerm parameter to the GetTermGroups parameter.

The full definition of the atTermGroupId parameter of the AddTerm operation:

"atTermGroupId": {
  "description": "TermGroup Id",
  "type": "string",
  "x-ms-dynamic-values": {
    "operationId": "GetTermGroups",
    "value-collection":"termGroups",
    "value-path":"termGroupId",
    "value-title":"termGroupName",
    "parameters": {
      "tgSiteUrl": {
        "parameter":"atSiteUrl"
      }
    }
  }
}

When the above definition is provided as a custom connector, the Flow designer will then render the dropdown control, connected to the GetTermGroups operation.


In the AddTerm operation, the atTermSet parameter must be configured similarly:

"atTermSetId": {
  "description": "TermSet Id",
  "type": "string",
  "x-ms-dynamic-values":{
    "operationId":"GetTermSets",
    "value-collection":"termSets"
    "value-path":"termSetId",
    "value-title":"termSetName",
    "parameters":{
      "tsSiteUrl": {
        "parameter":"atSiteUrl"
      },
      "tsTermGroupId": {
        "parameter": "atTermGroupId"
      }
    }
  }
}

Hide the supporting operations

In keeping with our ease-of-use theme, we should hide the supporting operations from the users. When using the Terms, it is very unlikely that getting a list of Term Groups or Term Sets is ever required. They are only needed to add or select a Term. Using the x-ms-visibility property, an operation (or parameter or schema) can be hidden from the Flow designer.

On the GetTermGroups and GetTermSets operations, add the property with the value internal;

"paths": {
  "/api/GetTermGroups": {
    "post": {
      "operationId": "GetTermGroups",
      "x-ms-visibility":"internal",

    <snipped>

}

With the x-ms-visibility setting, the support operations do not show as actions that the Flow creator can choose:

Conclusion

I hope that you found this post helpful in creating Dynamic Values for flow Operations. Please post questions/comments below, or tag me in the Flow Community Boards.