Wednesday, December 6, 2017

Create and run Custom Service in D365FO (AX7) Part 1

NOTE: Whenever I write a Google query to find information about the new AX system, I mention the bad word of those who create the name for the Microsoft products. Therefore, in order not to write many letters, I will use the name of AX7 instead of Dynamics 365 for Finance and Operation.

In this topic we will create a some two servises, which communicate in async mode.


In our case External System it is our AX services which starting in async mode some operations and each of it operation invoke another AX service which create new file locally.

In the new version of AX, the creation of custom services is a very simple operation. All you need to do is create a service group that will automatically be deployed, create a service and add it to the service group. And finally, link your service with the class which will be responsible for the logic of the service.

Let's create two services, one of which will generate a unique text string asynchronously within itself. In addition, the first service accepts two parameters: the number of threads and the size of the delay for each thread, by this we can emulate some complex task.

1 Step:
Create a service group

And set Auto Deploy properties to Yes
2 Step:
Then create some service

Then add services to Service Group and setup properties



I create Service Group and add two services: 


Ok. Next is to create a class and mapping our service operation with a method on a class. I have seen a situation when we can mapping only instance method and class, so our class and method do not be static.

3 Step:
Create a class


class GenerateUniqueString
{
    public str getString(int sleepMS)
    {
        str data = "Test_Async_data_" + "_" + datetime2Str(DateTimeUtil::getSystemDateTime());
        
        System.Threading.Thread::Sleep(sleepMS);

        return data;
    }

}

and then mapping a class with our service
setting service properties

and also setting service operation properties


Final result for the first service

Now you need to create a second service that will accept a text string and write it to a file and save it to disk. I will give only a piece of code, all other operations on class linking with the service are similar to the first service


class SaveStringToDIsc
{
    public str downloadFile(str parameters)
    {
        TextIo file;
        FileName filename = @"C:\Temp\Downloads\file.txt";
        FileIoPermission permission;
        #File
        str result = "";
        try
        {
            permission = new FileIoPermission(filename, #io_write);
            permission.assert();

            file = new TextIo(filename, #io_write);

            if (!file)
            {
                throw Exception::Error;
            }
            file.write(parameters);
            result = "File was successfully saved";
        }
        catch(Exception::Error)
        {
            result = "You do not have access to write the file to the selected folder";
        }
        CodeAccessPermission::revertAssert();

        return result;
    }

}

Then build your solution for deploy services.

Now let's talk about how to start the services.

NOTE: In this example, I will use services that return and accept data in format JSON. In more detail on how to work with custom services in a different format can be read in the documentation from the Microsoft https://docs.microsoft.com/en-us/dynamics365/unified-operations/dev-itpro/data-entities/custom-services

If you call the GET request, then in response we will receive the signature of our service, but if you run the POST service request, then it will work according to the logic that is written in it. Also, do not forget that the services deployed in the AX instance, so the request must contain a header with an authorization key.

To run the services, I will use the Fiedler program (https://www.telerik.com/download/fiddler)
Let's try to call our services:

Open the program / Composer / Scratchpad


Then we must generate access token with next request:

------------------------- OAUTH

POST https://login.windows.net/Tenant_Id/oauth2/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: login.windows.net


resource=https%3A%2F%2FYour_Instance_Address&client_id=Your_Client_Id&client_secret=Your_Client_Secret&grant_type=client_credentials

Where
Your_Instance_Address - AX7 url
Your_Client_Id - generated client id
Your_Client_Secret - generated secret key for client id
Tenant_Id - generated tenant id

How generate tenant, client and secret ids see there
https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-code

and there
https://www.netiq.com/communities/cool-solutions/creating-application-client-id-client-secret-microsoft-azure-new-portal/

Also, we have to setting client id in AX7, go to System Administration / Setup / Azure Active Directory applications and add new record with your client id.



I recommending use user which has system administrator role
Ok. Lets run our OAuth request, paste your script to scratchpad, select them and click "Execute" button:


Then, found your request and seen response. Copy your access token.



At last, everything is ready to call our service. Let's get started
POST https://YouInstanceUrl/api/services/TestServiceGroup/TestFirstService/getString HTTP/1.1
OData-Version: 4.0
OData-MaxVersion: 4.0
Content-Type: application/json;odata.metadata=minimal
Accept: application/json;odata.metadata=minimal
Accept-Charset: UTF-8
Authorization: Bearer yourtoken
Host: YouInstanceUrl

{
    "sleepMS":1000
}

Where
yourtoken - token wich we generated and copy previously
Execute this request and see result:


Perfectly. Our service is working.
Now let's do GET request adn see result

GET https://YouInstanceUrl/api/services/TestServiceGroup/TestFirstService/getString HTTP/1.1
OData-Version: 4.0
OData-MaxVersion: 4.0
Accept: application/json;odata.metadata=minimal
Accept-Charset: UTF-8
Authorization: Bearer yourtoken
Host: YouInstanceUrl




GET requests we can do from the browser, for this you just need to enter the address of our service address line (Example: https://YouInstanceUrl/api/services/TestServiceGroup/TestFirstService/getString)

This will complete the first part. In the second part, we will improve our services so that they can call each other automatically

2 comments:

  1. Thanks Dimitriy
    I planning develop external web apps to access some entity Dynamics 365 Finance and Operations.
    Does this tutorial can cover my planning or there are another ways to achieve my integration planning

    ReplyDelete
  2. We are really grateful for your blog post. You will find a lot of approaches after visiting your post. I was exactly searching for. Thanks for such post and please keep it up. Great work. custom research papers

    ReplyDelete