Sunday, August 27, 2017

Maybe the most useful Azure function ever! – Introducing a proper Swagger definition generator

This post relates to Azure functions written in C#, hosted as a Function App – and maybe the title is a tiny bit clickbaity ;)

I’m in a project where writing small Azure functions to accomplish pieces of functionality is a very good fit, and the tasks will be connected in a workflow – Microsoft Flow or Logic Apps. With the latest update for Visual Studio 2017, creating an Azure Function project and publish the function to Azure is super easy.

The tricky part comes when you want to consume those functions somewhere else using a Swagger definition file to describe your API.

The people over at the Azure team has been kind enough to add functionality to automatically generate a Swagger definition. The problem is that the output of this preview functionality is, to put it in nice terms, the equivalent to a table of contents, where the book was left out.

image

There are some blog posts out there on how you can write that book to get a working definition, but manual work when you have already defined the functions pretty well in code is not my cup of tea.

Digression

If an Azure function project had been a WebAPI or similar, you could have installed Swashbuckle, and you would have gotten a nice Swagger definition just like that. The fact that Azure functions are compiled to a class DLL, and that Swashbuckle does not work against a DLL in any easy fashion, I saw two options. A shadow API with Swashbuckle, or roll my own.

I started out with the first option where I created a shadow WebAPI project, copying all my Azure Function signatures, and then manually copying out parts to generate a proper Swagger definition. But I quickly discovered this was still too much manual work for my taste.

Option two it is – generate the mofo myself!

That left me with option two, write my own Swagger generator. The Swagger spec itself is not too complicated, and as Azure uses v2, that’s what I set to use as well. I’m no stranger to reflection on .Net DLL’s, having worked with SharePoint for many years, as well as writing other types of generators, so that’s what I did.

I created an Azure function in my project which at run time reflects on the current assembly, finds all methods marked to be Azure functions, then inspect their ins and outs, and construct a full fledged Swagger definition.

It’s not complicated, but a bit tedious to support all the scenarios I wanted to support. I wanted to support input via the path, as query parameters and JSON objects in the body – which is the most useful one in my opinion. It took me one working day, 7.5h to have this up and ready to go, with ~400 lines of code. Imagine if the Azure team could have spend the same? But then again, I wouldn’t have this blog post :)

The code and things to be aware of

You can get the code at https://github.com/wobba/AzureFunction-SwaggerDefinition
  • Copy the Swagger.cs file into your project to get going.
  • You need to add a reference to System.ComponentModel.DataAnnotations, as I use the Display attribute to reflect out the summary and description of the functions – needed for Microsoft flow.
  • See the sample functions in Templates.cs to get an idea on how you can craft and annotate your functions in regards to input and output values.
  • Nested complex types works fine, but not sure if Microsoft Flow support them.
  • The default function to get the Swagger definition will run on /api/Swagger.
  • Microsoft Flow works best with defined input and output objects, not using simple types.

How to use with Microsoft Flow

Once you have published your functions to Azure, navigate to the Azure management portal and your function app. To ensure easy import of Swagger from Azure Functions to Flow, disable CORS on the function app by allowing all domains with a *.



Next get the default API key and make a note of this as you will need it later. The API key can be retrieved from the Function App Settings page.

imageimage

Get the Swagger definition URL by clicking the Swagger function and Get function URL.

image

Go to the Platform features tab and click API definition. On this page you could click the X’ed out Function button and get an auto generated swagger table of contents, but as we want the full book, hit Set external definition URL instead, and point it to your custom Swagger definition function URL from above.

Note: Another option is to create a custom connector directly from Flow, using the same Swagger URL.

image

Once the URL is pasted into the API definition location input box, click Export to PowerApps + Microsoft Flow. Fill in the appropriate information, and paste in the Azure function API key (which you made a note of earlier) in the API Key Name field.

image

When you click OK, and everything went according to plan, you should now see your Azure functions show up as a custom connector in Microsoft Flow.

image

Next up edit the custom connector and add a label for the API Key parameter as seen below. You can also change the icon, name, function names and descriptions etc. if you want.

image

Now it’s time to test the API. At the Test tab you can create a new connection (if you try to consume the API in Flow before adding a connection here it fails the last time I tried).

image

Use the same API Key you used when exporting from Azure to PowerApps + Flow. In theory I would have thought the connection was auto-made on the export – so maybe something went wrong and this step might not be needed. Easy to tell if you see a connection already on the Test tab.

image

Test it all in Microsoft Flow

Once you have tested your functions, head over to Microsoft Flow, find your functions and hopefully it’s all working with green lights on all steps. You should also be able to see defined inputs and outputs for each action.
image

Fill in data, save and run the flow.

image

If all went according to plan, you should see an ok checkmark for both steps.

image

14 comments:

  1. Hi Mikael,

    I'm not sure if my last post went through, so I'm going to send it again.

    This is a great post, and I first want to say thank you.

    I'm having a problem.

    After I follow the instructions where you state:

    "Once the URL is pasted into the API definition location input box, click Export to PowerApps + Microsoft Flow"

    I get the following error:

    Unable to download Swagger 2.0 metadata. Please verify that the URL is publicly accessible. You may need to enable CORS for the App Service portal extension located at https://web1.appsvcux.ext.azure.com.

    Can you let me know if you got the same error and how you resolve it?

    Thanks

    Carlton

    ReplyDelete
    Replies
    1. Try this instead. Call the swagger function in a browser and save the output to a file. Then manually create a custom connector in Flow by uploading the saved swagger file.

      Delete
    2. Have you enabled CORS? You may need to replace the CORS settings (found in the portal under the Functions Platform Features) with a single entry of "*" instead of the specific sites listed. (To be be more secure you'd had to find what sites to add to the list).

      Delete
    3. Jason, that did the trick! Thank you!

      Delete
  2. Hi Mikael, is this still working for you?
    We create a Function App using your Swagger.cs code as one of the functions and it correctly generates the definition.
    We also set the Function Apps's API definition the URL of the Swagger Function and CORS to "*".
    However when we try to use the Function in a Logic App, the Logic Apps designer just shows "loading..." next to the Function name after selecting it from the list of available functions.
    It just hangs and doesn't return.

    If we take the output of the Swagger Function and paste it into the Function Apps API definition (rather than using an External URL) it works just fine.

    Also if we add an HTTP + Swagger Action to the Logic App and use the URL of the Swagger Function as the swagger location, that also works.

    But these are workarounds, is anybody else able to add a Function that uses the generated Swagger?

    ReplyDelete
    Replies
    1. I used the URL directly 1h ago actually in the Flow custom connector. Not tried in a logic app. But going via a file is an easy workaround I guess, one I used until discovering the cors settings.

      Delete
  3. I tried here, but Portal says:

    Function API definition (Swagger) feature is not supported for beta runtime currently.

    ReplyDelete
    Replies
    1. Which is why I created this Swagger generator.

      Delete
  4. Hi Mikael,

    Awesome post mate. I was working on a few Functions and spent hours writing the API definition manually.
    Finally google thought of taking me to your post :) Read the post and I know it's gonna save me a lot of time. Thanks heaps.

    ReplyDelete
  5. Hi Mikael,
    This is awesome. Like you, I was going through the YAML that Azure functions generated and was going to add all the missing information. I'm glad that google took me here, it will save countless hours of tedious and boring work.

    I haven't been able to figure out whether it is possible to add attributes to functions that are dependent on the verb. Ie. a POST may return nothing, and a GET may return an object.

    ReplyDelete
    Replies
    1. In my code that is not supported, then you would need two functions, one for each verb. But in theory it would be possible. My thinking is if you use tooling to consume the swagger, then the number of routes doesn't really matter.

      Delete