Not-So-Regular Expression Support in Power Automate, via Custom Code Connector (Part Deaux)

One thing you may have noticed in some of the Power Automate actions is the ability to select a predefined value from a list of choices when setting the value of an action’s parameter. (Think of any one of the SharePoint actions allowing you to choose a site URL and a list name, the Terminate action with the Status parameter Failed, Cancelled, Succeeded, or the Regex Options parameter in the Regular Expression custom code connector we built in the previous post, etc.)

Terminate Action with List of Status Choices

This comes in really handy when you want to prevent the user from incorrectly configuring an action or guide them to a specific action input.

I’ve updated the Regular Expression custom code connector action to provide the user a new IsNumeric choice for the Regular Expression parameter.

This image has an empty alt attribute; its file name is image-5-1.png
Regular Expression Custom Code Connector with a Choice List

The purpose of this new parameter choice is to perform a match against the supplied text and evaluate whether or not it is strictly a numeric value. For example: 1234567 is numeric, whereas THX1138 is not.

Let’s see how we can enhance our custom action.

Action Definition

As we saw previously, what your custom action can do is defined by the YAML swagger file.

This definition is the same as the previous post except for one new attribute: enum. This attribute requires an array of one or more literal values (enumerations), in our example [IsNumeric] .

swagger: '2.0'
info: {title: SteelCut Bytes Actions, description: SteelCut Bytes Power Automate Actions,
  version: '1.0'}
host: flow.steelcutbytes.com
basePath: /
schemes: [https]
consumes: []
produces: []
paths:
  /RegularExpression:
    post:
      responses:
        default:
          description: default
          schema:
            type: object
            properties:
              input: {type: string, description: The supplied string to search for
                  a match., title: Text to Match}
              pattern: {type: string, description: The supplied .NET regular expression
                  pattern to match., title: Regular Expression}
              matches:
                type: array
                items:
                  type: object
                  properties:
                    Groups: {type: object}
                    Success: {type: boolean}
                    Name: {type: string}
                    Captures:
                      type: array
                      items: {type: object}
                    Index: {type: integer}
                    Length: {type: integer}
                    Value: {type: string}
                description: An array of the Match objects found by the search. If
                  no matches are found, the method returns an empty array.
                title: Matches
              isMatch: {type: boolean, description: Indicates whether the .NET regular
                  expression finds a match in the input string., title: IsMatch}
      summary: Regular Expression
      description: Searches the specified input string for all occurrences of a specified
        .NET regular expression, using the specified matching options.
      operationId: RegularExpression
      parameters:
      - name: value
        in: body
        required: true
        schema:
          type: object
          properties:
            input: {type: string, description: The string to search for a match, title: Text
                to Match}
            pattern:
              type: string
              description: The .NET regular expression pattern to match
              enum: [IsNumeric]
              title: Regular Expression
            options:
              title: Regex Options
              description: Value that specifies options for matching
              type: string
              enum: [None, IgnoreCase, Singleline, Multiline]
              default: IgnoreCase
          required: [options, pattern, input]
definitions: {}
parameters: {}
responses: {}
securityDefinitions: {}
security: []
tags: []

When an enumeration is chosen, the value of that parameter will be a literal string equal to that enumeration. So in our example, the Regular Expression parameter will now be equal to the literal string IsNumeric when selected.

The new enumeration value needs to be evaluated and handled by our custom code.

Code

To use this new value in our code, we simply need to check for that value and then do something with it or because of it.

  • If the value matches IsNumeric then use a hard-coded regular expression (regex pattern ^\d+$) instead.
  • If the value is not IsNumeric then we treat it as an actual regular expression and use it as we did before.
public class Script : ScriptBase
{
    public override async Task<HttpResponseMessage> ExecuteAsync()
    {
        switch (this.Context.OperationId)
        {
            case "RegularExpression":
                return await this.HandleRegexOperation().ConfigureAwait(false);
        }

        var response = new HttpResponseMessage(HttpStatusCode.BadRequest);
        response.Content = CreateJsonContent($"Unknown operation ID '{this.Context.OperationId}'");
        return response;
    }

    private async Task<HttpResponseMessage> HandleRegexOperation()
    {
        var contentAsString = await this.Context.Request.Content.ReadAsStringAsync().ConfigureAwait(false);
        var contentAsJson = JObject.Parse(contentAsString);

        var textToMatch = (string)contentAsJson["input"];
        var regexInput = (string)contentAsJson["pattern"];
        var regexOptions = (string)contentAsJson["options"];

        var regex = new Regex(
            (regexInput == "IsNumeric") ? @"^\d+$" :
            (regexInput),

            (regexOptions == "IgnoreCase") ? (RegexOptions.IgnoreCase | RegexOptions.Compiled) :
            (regexOptions == "Singleline") ? (RegexOptions.Singleline | RegexOptions.Compiled) :
            (regexOptions == "Multiline") ? (RegexOptions.Multiline | RegexOptions.Compiled) :
            (RegexOptions.None | RegexOptions.Compiled),

            TimeSpan.FromSeconds(1)
        );

        var jSerializer = JsonSerializer.Create(new JsonSerializerSettings
        {
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
        });

        var result = new JObject
        {
            ["input"] = textToMatch,
            ["pattern"] = regex.ToString(),
            ["isMatch"] = regex.IsMatch(textToMatch),
            ["matches"] = JArray.FromObject(regex.Matches(textToMatch), jSerializer),
        };

        var response = new HttpResponseMessage(HttpStatusCode.OK);
        response.Content = CreateJsonContent(result.ToString());
        return response;
    }
}

As you can see, this adds tremendous value to the end user by providing them a simpler configuration of the action and amplifies the intent of its use. In this case, these choices could reduce or even eliminate the need for them to know regular expression syntax.

What about my custom value? Why not both.

One of the nice things about the Power Automate GUI is that you can also supply a custom enumeration as a choice. You do this by simply choosing the Enter custom value choice at the bottom of the choice list. This turns the choice list field back into a text box.

Custom Regular Expression Using Dynamic Panel

So now, you can apply a custom Regular Expression pattern (as we did in the original version) like this:

  1. Simply choose the Enter custom value choice to display the Regular Expression text box,
  2. Enter your regular expression pattern in the Expression editor of the flow’s Dynamic content panel, by wrapping it in the string(...) expression.

For example:

@string('(?i)<a id="token" href="(?<token>.*[^\W]=*)"></a>')

Here’s the result:

Custom Regular Expression Usage and Output

Wow, wow, wow.

But wait … there’s more!

You can use this approach to provide whatever built-in regular expressions or checks you’d like. The possibilities are endless.

Here are some example expressions:

Have fun.


Follow My Blog

Get new content delivered directly to your inbox.

Author: Joey

I develop modern solutions leveraging the Microsoft cloud technologies: Power Platform and SharePoint Online, with a focus on process automation. I got my start writing code professionally beginning with Microsoft VB6, but really fell in love with code (and the art of it) with Microsoft .NET Framework version 1.0. I'm also a fan of Superman, so don't be surprised if references creep in. Up, Up, and Away!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: