Please enable JS

Keep your Azure API Management API up to date !

Keep your Azure API Management API up to date !

Keep your Azure API Management API up to date !

JUL 08/David De Baets

This is an addition to the Automated APIM Swagger definition download post where we showed how you can download your Swagger definition from PowerShell. In this post we do it the other way around, uploading your Swagger definition to your APIM instance. With this approach you can keep your APIM API up-to-date in for example a CD pipeline. But in this example we'll update the API manually with a local swagger.json file.

So we assume that you have an APIM API instance running, if not we like to refer to the Microsoft documentation on to set this up:

A developer instance of APIM is enough to be able to test. Now let's get started and look into the script that can be used to update your APIM api:

param(
    [string] $subscriptionName,
    [string] $resourceGroup,
    [string] $apimInstance,
    [string] $apiName,
    [string] $displayName,
    [string] $apiPath,
    [string] $swaggerFilePath
)
az apim api import -g $resourceGroup -n $apimInstance --subscription-required true --api-id $apiName --display-name $displayName --path $apiPath --specification-format "OpenAPIJson" --specification-path $swaggerFilePath --service-url $serviceUrl --debug

Deep dive into the parameters

  • $resourceGroup: This is the resource group where your APIM instance exists.
  • $apimInstance: The name of the APIM instance.
  • $displayName: How the API is known in the portal: enter image description here
  • $apiName: The name of the API you would like to update, this can be found in the API definition in APIM: enter image description here
  • $apiPath: The path under which the API should be published: enter image description here
  • $swaggerFilePath: The path to the Swagger json file on your machine.

After running the script you'll have an updated APIM API, now for our project we wanted to go a bit further and also wanted to filter out apis that we don't to publish in APIM. There are different solutions to this problem but we introduced a custom Swagger property which is used in the update process to filter out endpoints that we don't want to be published in APIM. For this we needed to create: - A custom .net attribute to decorate the endpoints with - An IOperationFilter for SwashBuckle reference

First we create the custom attribute:

[AttributeUsage(AttributeTargets.Method)]
public class SwaggerApimAttribute : Attribute
{
    public SwaggerApimAttribute(string[] tags)
    {
        Tags = tags;
    }

    public string[] Tags { get; }
}

With this we now have a SwaggerApim attribute which we decorate endpoints with that we would like to publish:

[SwaggerApim(new[] {ApimTags.PublishMe})]
public async Task<IActionResult> NotRelevant()
{
    return Ok();
}

The ApimTags is just an enum to make it easy to find all reference, a string would also work here. Now we have to add the IOperationFilter which will be responsible to create the custom attribute in the Swagger definition. This implementation will be called for each endpoint and allows us to extract custom attributes on the controller method, when the custom attribute can be found then we add an operation extension by calling operation.AddExtensions("<ATTRIBUTENAME>"). We prefix this operation withx-` to indicate that this is a custom property!

public class AddApimTagsOperationFilter : IOperationFilter  
{  
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        var swaggerApimAttribute = context.MethodInfo.GetCustomAttribute<SwaggerApimAttribute>();
        if (swaggerApimAttribute != null)
        {
            foreach (var apimAttribute in swaggerApimAttribute.Tags)
            {
                operation.AddExtension($"x-apim-{apimAttribute}", new OpenApiBoolean(true));    
            }
        }
    }  
}

Next, we need to register the IOperationFilter in the Startup.cs where you registered Swagger:

services.AddSwaggerGen(options => { options.OperationFilter<AddApimTagsOperationFilter>(); }

If everything went ok and you run the code then you should see x-apim-publishme: true attributes in the definition. Now we need to change the script to filter out endpoints that are decorated with these attributes. For this we use the openapi-filter NPM package which you can check out here. For this you would need to run have NPM installed and need to run npm install -g openapi-filter to be able to run it. Adjusted script:

param(
    [Parameter(Mandatory = $true)]
    [string] $resourceGroup,
    [Parameter(Mandatory = $true)]
    [string] $apimInstance,
    [Parameter(Mandatory = $true)]
    [string] $apiName,
    [Parameter(Mandatory = $true)]
    [string] $displayName,
    [Parameter(Mandatory = $true)]
    [string] $apiPath,
    [Parameter(Mandatory = $true)]
    [string] $swaggerFilePath
)
$openFilter = "openapi-filter -f $swaggerTag --info --valid -i -- $swaggerfile $filteredSwagger"
Invoke-Expression $openFilter
$utf8 = New-Object System.Text.UTF8Encoding $false
$MyFile = Get-Content $filteredSwagger -Raw
Set-Content -Value $utf8.GetBytes($MyFile) -AsByteStream -Path $filteredSwagger

az apim api import -g $resourceGroup -n $apimInstance --subscription-required true --api-id $apiName --display-name $displayName --path $apiPath --specification-format "OpenAPIJson" --specification-path $filteredSwagger --service-url $serviceUrl --debug

Now after running the script again only the decorated api endpoints will be in the Swagger definition and are published to APIM. Obviously running this from your machine doesn't make much sense, but it works quite well if you integrate this in your CD pipeline to keep your APIM instance up-to-date.

Hope you enjoyed and learned something from this post! Feel free to contact us if you have any questions or remarks!



Categories
Tags