Wednesday, August 28, 2019

Connecting Business Central to Azure Service Bus with REST API Part 2

This blog post is a continuation to my previous post "Connecting Business Central to Azure Service Bus with REST API". I recommend reading the first post first.


Reading messages from the Service Bus queue.

There are two ways to read a message from a queue, a destructive read and a non-destructive read. A destructive read is effectively a read+delete and non-destructive read is a read, where the message gets locked for a period of time but will be available for a read after the locking period has expired. The length of the locking period can be found in the queue properties in the Azure Portal. The simplest case is reading the first available message from the queue. The location of the first available message is "/messages/head"



The http verb is POST, but the resource Uri ending with messages/head will give you the first available message. The message payload is returned in the content of the response and the other information of the message is in the response header. 

If you wish to also delete the message (destructive read), just change verb POST to a DELETE an you effectively pop the first message from the queue. 

Response header

When you read a message from the message queue the API returns a collection of properties, called Broker Properties. These properties include the MessageID and Lock Token of the message, which can be used to identify the message in the queue. It also tells you when the lock, which you just created by reading the message, will expire. This is all very useful information.





Broker Properties are in a JSON array. You can read individual values by creating an array of text and reading the header value "BrokerProperties" into the array.



Then it is pretty easy to initialize JSON object from the array using Codeunit 5459 JSON Management and get the values by property name as in the example above.

Where would you use broker Properties? Imagine you're designing an integration, where you need to either know the message content before you know what to do with it, or, say you need to get the messages in certain order, then it is extremely handy if you can read a message and then either process it or move forward to another message.

Unlocking a read message

The message read from the queue will be locked for a period of time after it was read from the queue. If you wish to re-read it while its still locked, the message needs to be unlocked. The Broker Properties returns the expiry date and time of the message in a LockedUntilUtc variable. There's no need to know what the Lock Duration of the queue is as you can easily check if the lock is still on. The value of LockedUntilUtc is in this example in RFC2616 UTC format. To convert to Business Central datetime, there is an external  function in codeunit 10 Type Helper that does exactly that.



If you need to access the message while its locked, you need to unlock the message before you can read it again. To unlock the message, you need to call a PUT with a messageID and a LockToken.








Unlocking a message makes it available to read immediately.

Controlled delete

A message can be deleted from the queue without unlocking it first as long as you know the MessageID and the Lock Token of the message. 


Binary Payload

Sending or receiving a binary payload differs from earlier examples only by how the payload is inserted or retrieved from the message body. I use a temporary table called tempBlob to handle the blob value.

To send a BLOB content:



To receive a BLOB content:




So the only trick needed is to convert the BLOB to/from Base64String, which implemented in the tempblob table itself, and that's it.

Thank You for reading.

Tero

Monday, August 19, 2019

Connecting Business Central to Azure Service Bus with REST API


Tero Hassinen shares his thoughts on developing extensions for Dynamics 365 Business Central using AL programming language and Azure Functions. Tero is a senior solutions architect at Futuriot where he develops business management solutions in Microsoft Dynamics NAV, AX and Business Central environments

As a Business Central developer I've been lately working solely on the cloud version of Business Central. What I've found is that there's a plethora of new awesomeness to tinker around with and you just need to figure out how these pieces fall into place. The whole Power Platform thing, Microsoft Flows, Power Apps, lot to learn and keep up with sure, but who wouldn't want to learn few new things. Some of it has proved very useful and not that complicated at all. We just created a fully functional purchase invoice approval system using Microsoft Flow and seriously a caveman can do it. 

Bulk of my profession is writing extensions in AL. How would I leverage the new possibilities in extension development? I am developing in the cloud, so I guess the most obvious place to look would be the cloud and whats available in the Cloud. And therefore the answer is Azure Functions. 

How do I access Azure Functions from AL code? As I weed trough the  documentation and walkthroughs of everything Azure Functions has to offer, I constantly run into C# examples that use .NET. The problem is that .NET interop is not available for cloud solutions. Microsoft has this to say: 

For cloud solutions .NET interop is not available due to safety issues in running arbitrary .NET code on cloud servers. 
Ok, seems fair enough. 

I guess one option would be to host the proprietary .NET code in Azure functions to achieve the needed result. No doubt that is sometimes the only avenue, but what I am planning to do here can be done through a REST API interface.

What is REST API and where to use it?

Here is what Microsoft says about REST API:
Basically, REST is an architecture you can use when calling APIs or making APIs available to be called. It's independent of what's happening on either side, and what other software is used when sending or receiving the REST calls. You can write an application that runs on a Mac, Windows, Linux, an Android phone or tablet, iPhone, iPod, or web site, and use the same REST API for all of those platforms. Data can be passed in and/or out when the REST API is called. The REST API doesn't care from what platform it's called – what's important is the information passed in the request and the data provided in the response.
Knowing how to use REST is a useful skill. The Azure product team frequently releases new features. Many times, the new features are accessible through the REST interface. Sometimes, though, the features haven't surfaced through all of the storage client libraries or the UI (such as the Azure portal). If you always want to use the latest and greatest, learning REST is a requirement. Also, if you want to write your own library to interact with Azure Storage, or you want to access Azure Storage with a programming language that does not have an SDK or storage client library, you can use the REST API.
OK, I am sold, lets get to work.
As the headline suggest, I am going to connect to Azure Service Bus from AL code and send and receive some messages. Hopefully I will one day use this code to integrate with another system. It seems at least D365 Sales has an ability to connect to Service Bus out of the box, so this is very promising. There seems to be more than one Service Bus connector available for SAP, even better.

What is Azure Service Bus.

Azure Service Bus is a messaging service, where you write into and read from queues, much like MS message queue. Azure Service Bus is however quite more sophisticated than MSMQ. It has a nondestructive read, which is excellent. The messages are locked for a period of time after read, but you can unlock the messages at will with a messageID and token, very handy. And the best part is that the message queue payload is binary, so you can send and receive pictures, pdf's, i guess virtually anything. Brilliant!

Setting up a Azure Service Bus and Queues.

There are plenty of instructions on setting up a Azure Service Bus online, I recommend this one https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-create-namespace-portal
After you have your Service Bus and Queues setup, there are four pieces of information needed for this integration:
1. Your service bus base address, e.g. https://mymessages.servicebus.windows.net/ where 
mymessages itemizes the Service Bus Namespace.
2. Name of the Queue(s) to be used.

3. The  name of your Shared Access Signature (SAS) policy. The default, which is created when you create the service, is called RootManageSharedAccessKey. This policy is an admin policy and its recommended to create a new one with less permissions. I named mine ever so cleverly MyAccessPolicy.


4. Shared Access Signature (SAS) key, which is used to sing portion of the Authorization header. There are two keys and you can use either one. All references to a key later in this post always refers to this key. The SAS Policy can be setup at a Namespace level or Queue level but in this example we use a namespace SAS policy. 

I will be iterating some items in this post that some readers will probably consider self evident, but I personally find it really frustrating when a post, or a walk-through is missing a vital piece of information, simply because it was deemed common knowledge. This being my first blog post ever, I probably do plenty of errors, but I try to say everything I would've wanted to find somewhere in plain terms. Most recently, I followed these instructions for creation of the Authorization string https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-sas
and it wasn't exactly easy as in one example it leads you to believe the signature is a SHA256 hash, but then where would the key go? Its not quite clear which part need to be Url-encoded and which not. After vigorous trial-and error and with a little help from Powershell, I was able to figure it out, but the instructions could be better, especially to us who do not benefit from the included C# examples. Anyway, rant over...


How to create a REST API request with an Business Central HttpClient

The request itself is quite straightforward; a request header and a body. The Request header can be as simple as a Content Type and an Authorization header.  The request body carries the payload of the Service Bus Queue message (the message itself, text or binary). However, the Authorization may easily become a tough one to get right. The authorization requires a computation of a HMACSHA256 Hash from a String To Sign, which needs to be absolutely correct. Any extra character in the string and the hash changes completely. 

How to Create HMACSHA256 Hash

First order of business, how to create a HMACSHA256 Hash without .NET? The answer is rather simple. Codeunit 1266 Encryption Management. Encryption Management has external function that you call with your encryption key and your string to sign and as return you get a Base64 encoded HMACSHA256 Hash. Thank You Microsoft!


The third paremeter in the function call is the Algorithm type. Two stands for HMACSHA256. That's it, now to the string to sign.

The elements of the String to Sign 

This is where the issues may rise, as any minor flaw in the string to sign will result in a completely different hash than expected. The following code creates an authorization token.


The first parameter, ResourceUri, is the address of the resource your request will try to access e.g. https://mymessages.servicebus.windows.net/myqueue. 
AccessPolicyName is simply the access policy name from the Azure 
portal, and SigningKey is the key under the Access Policy.

In order to UrlEncode the parts that require Url encoding(ResourceUri and the Signature) there is a 

function UrlEncode in codeunit 10 Type Helper.

se = Token expiry instant. Integer reflecting seconds since the epoch 00:00:00 UTC on 1 January 1970 (UNIX epoch) when the token expires. The easiest way in AL to calculate the seconds is to calculate a duration, which is in milliseconds and divide the duration by 1000.




LF= linefeed, which in Microsoft documentation is '\n' but in AL is ASCII char 10. So char LF := 10.

StringToSign = your ResourceUri and Epoch seconds separated with a linefeed, and would in debugger look like this:

Finally the Token would look very close to this:
Now we can start building the request, which is not that complicated at all. First the request header:

And finally a request:

This Request sends a message. The message content is passed in a httpContent variable. Since we are only sending a message, the http response is pretty much void of any useful information. Makes perfect sense. The response header tells you the message was created in the queue and in most cases that's all one needs to know. The SBHttpClient is a variable of type HttpClient.

However, when reading messages, the response header is full of valuable information. It will contain a messageID, a lock token and will tell you when the message will be readable again and so forth. I will cover reading, deleting and unlocking a message in the 2nd part of this blog: https://dynamics365bc.blogspot.com/2019/08/connecting-business-central-to-azure_28.html