The curious tale of the result() function in Flow and LogicApps

Photo by Andrew Neel on Unsplash

Photo by Andrew Neel on Unsplash

I owe this blog post to several persons, so I will list their names first:


I woke up to Kevin’s reply and something jolted me awake. See, I didn’t know there was a result() function, and it really isn’t documented at all in LogicApps Workflow Expressions. I have most of the functions memorized and I have never seen it.

Before I saw Kevin’s answer, I actually assumed it was an old article and the function doesn’t exist anymore.

I tried searching for it - I still don’t find it.

The RESULT() YOU ARE LOOKING FOR IS NOT here

And it looks darn useful.

How often do you find a completely undocumented function in a language and the product manager doesn’t say it’s an incorrect function?

And that’s how I woke up early on Thursday morning.

Go read the example article and see what you think, before you continue. You only get one chance to ‘feel’ that excitement I had when I read the article, that expression, if you continue reading, I will spoil that for you. Go read it and wonder, may be even stop now, and go explore it yourself and then come back :)

We have work to do. If you like, I want to compare notes, my notes are below.

Plan

  • Experiment

  • Understand

  • Apply

Experiment

So we create a Flow and immediately add two Compose to test the result function.

TALK about a ERROR message wow

This isn’t a completely crazy error - it tells me a few things

  • result function exists

  • it expects “actions of type ‘Enumerable < actions > ’

  • that looks like it expects actions that contains actions

Try the example in the docs, we do get it to work and can see its outputs.

Wrapping our test action inside a Scope, result(‘Scope’) returns actual real data.

It is a treasure trove of runtime information. Nested, grouped, runtime information.

Understand

Scope isn’t the only block in Flow that contains other actions, let’s try a few more of them

The Do Until block returns the final block and the repetitions.

The Apply to each block is extremely interesting. Because it returns outputs from every iteration. It is very very long.


Applications

The first application is to use this as a generic catch-all error.

Here’s a fancy Flow that tries to sometimes run a divide by zero.

This one is similar to the Logic Apps blog post - I use a fancy HTML table to show all the rows at once.

The second application is to use this as a very fast array append

This is a very common scenario where we want to group up individual apply-to-each results into an array for use after the apply-to-each block.

On one side - we have the traditional method of appending a line of result to a shared global array variable.

On the other side - we have apply-to-each and then we just output the result we want in a compose action. We use a Select after the apply-to-each to map the output from the result() function.

Apologies - the left/right of these screenshots toggled when I save and reload the Flow. So embarrassingly I have some of the pictures where result method is on the left, and other picture where the method is on the right.

Please refer to the two branches as "(result method)” and “(append method)”
If confused - please check the name of the apply-to-each block in the picture.

Change the apply-to-each to run in parallel


In parallel, the result() function is so amazing.

The 3rd Application is to run complex Select, using Apply-To-Each

and then use result() to gather the results, all without defining an array variable. This means you can run fairly complex apply-to-each block within a scope.

Bug

Because result() is a function that the Flow editor does not understand, it will save and run correctly, but the editor may mistakenly translate the expression to a string.

The main problem is that you may have a working Flow but when re-opening the Flow the editor turns an expression into a string and breaks the Flow, because it doesn’t understand that result() is a valid function.

I hope this bug is fixed soon.

Summary

result() is a cool function, and has some very important applications.

Microsoft Graph Community Call September 2018 - $Batch and Flow

I wanted to write a quick blog post letting everyone know I've put my hands up to present a 10 minute demo on using a Custom Connection for Microsoft Graph $Batch in Microsoft Flow.

Custom Connection = Swagger = WSDL

In Microsoft Flow, Power Apps or Logic Apps, a Custom Connection is a Swagger/Open API definition that allows these low-code solutions access to a well defined API, so it knows what to call and how to call them.

For old API guys that knew what a SOAP Webservice looked like, this is the WSDL that tells other systems how to use this service.

In general, calling Microsoft Graph with App-Only Permissions requires only a client-id and client-secret pair, and we can do it directly via the HTTP action without needing a custom connection.

When we do need to create a custom connection, that's usually because we need to call something with Delegate permissions

Why Flow - Connectors Framework

One of the hidden supper power of Flow and Logic Apps is the connectors framework, which cache, renew and manages connections across various different APIs and auth methods.  

This means - you actually don't need to write any of this code.  Repeat - as a developer, you don't have to write code to manage auth.  Auth is encapsulated away and auth code is no longer your concern.

Why $Batch

You'll have to see the demo (I'll update this later).  Hey, I can't spill all the beans in one blog post.  This is a tease.

The result

If you want to automate any delegate-permission API calls on Microsoft Graph, then Microsoft Flow is the easiest way to do it - far easier than writing code, and this is considered a low-code solution, it's crazy nuts.

Resource

Link to the recording will be updated here once presentation is done and uploaded.

A Thesis on the Parse JSON action in Microsoft Flow

Parse JSON can be both intelligent and dumb.  It can help you a lot and it can hinder you just as much.

This blog post, we go deep and study the behaviour of the Parse JSON action, it's various problems and solutions.

Here we tackle the question - How do you use Parse JSON, actually?

Plan

  • Getting Started with Parse JSON
  • Problem - way too many properties
  • Problem - the array - item object and auto-wrapping
  • Problem - duplicate properties names?
  • Problem - null value properties
  • Problem - missing properties
  • How to quickly fix the schema
  • Result is Parse JSON will make the Dynamic Content Panel actually intelligent

Getting Started with Parse JSON

In general, the reason we use Parse JSON is because Flow doesn't know the format of the data we are receiving in our actions.  So we always start by running the actual method once - even if the Flow has only been partially completed.  We want to get a sample of the data we'll be seeing.

Look at all these properties in the Dynamic Content Panel.  Looks amazing.  Our problems are about to happen.  Don't worry we'll fix all of them.

 

Problem 1 - way too many properties

The schema Parse JSON generates is very very verbose.  So there are a lot of properties that we probably just don't care.

So copy the generated JSON Schema from the Parse JSON action, and use a text editor to have a look at it.  Here I'm using VS Code - switch the editor mode to JSON.

We see it has generated type information for various _links properties, or in this example, the halfTime results or odds of a football game.  We don't need this, so we can delete it from the schema.

 

Problem 2 - the array - item object and auto apply-each wrapping

Notice under the fixtures (array) we have an items property with each object.  This appears under Dynamic Content Panel as "fixtures - Item"

Becareful when clicking on this property, it will immediately unfold with an For-Each block.

It depends whether this is actually what we want - typically, when we aren't in a For-Each block, this is helpful.  But if we are already inside a For-Each block this may create a second nested for-each block and be quite unnecessary and confusing.

 

Problem 3 - duplicate properties names?

Hey why do I see duplicate names?  How can we tell which one is which?

The simplest way to ease this problem is to delete the properties in the schema that you don't want (same as problem 1).  This will reduce the number of duplicate properties that appears.  But sometimes, we still get some duplicate names - often I see the top level object has a 'name' and the child object has another 'name'

In this case, I check with the hover over to make sure I've selected the right choice.

 

Problem 4 - null value properties

(This problem category also applies to all type mismatch errors)

In general, Parse JSON generates the schema based on the first object it sees from the sample.  This is not always correct - since sometimes the properties may be null in the later objects.  For example in the football games data - earlier records has scores as integers, but later records haven't been played yet so scores are null.

This error usually fails the Flow.  Another mutation of this problem is when the number is a decimal, but the schema thinks it's integer.

The easiest way to fix nullable-values is to delete the type information for this property.

Using no type information is better than changing to something like type: "object" because the Dynamic Content Panel will always show these properties.

If goalsAwayTeam was set to object, then it wouldn't be available here.

 

Problem 5 - missing properties

Sometimes, a later object in the array doesn't have a property at all.

The fix is simple - remove the "y" from "required"

But sometimes this is half the problem - because the expression used after this may expect the property to exist and always available.

 

Result

Parse JSON will make the Dynamic Content Panel actually quite intelligent.  But it requires some clean up maintenance on the JSON schema.

It is up to each scenario to decide if there's value to fix the JSON Schema, or to skip using Parse JSON and just use item()?['property-name'] expressions directly.