Advertisements

Archive

Archive for the ‘Dynamics 365’ Category

[Logic App and HTTP Request Trigger] Create a record in D365

December 20, 2018 Leave a comment

We got an ask from client that they should be able to Insert contacts in D365 from their Java application and requested our help.

To fulfill the ask, one option would be configuring S2S authentication so that Java application can connect to D365 using ADAL library and insert contacts.

Another option is using “Logic App” with “Http Request Trigger”.

High level approach:

  • Create a Logic App with ‘When a HTTP request is received’ trigger
  • Pass Contact record information in Json format as Request
    • Use ObjGen site to generate desired Json data.
    • This site seamlessly generates Json data based on our input. As I am going to create a Contact record, I generated below JSon data.

LogicApp_3

  • From Logic App, read the Json data and create Contact record using D365 connector
  • Finally capture the created Contact GUID in ‘Response’

Lets see the step by step approach to create Logic App and test.

Pre-requisites:

  • Dynamics 365 instance. Subscribe for trail if you have’t already
  • Azure Subscription.Subscribe for trail if you have’t already

Once you got the D365 and Azure Portal subscriptions below are the steps to create LogicApp.

Create a Logic App:

  • Connect to your Azure Portal and create a new ‘Logic Apps’. Refer article for steps to create Logic App.
  • From the list select ‘When a HTTP request is received’ trigger

LogicApp_4

  • In the next screen, click on “Use sample payload to generate schema” to generate the Json schema.

LogicApp_5

  • Paste the Json format prepared in ObjGen site

LogicApp_6

  • Click on ‘Done’ so that LogicApp generates “Request Body jSON Schema”

LogicApp_7

  • Next, we need to connect to D365 to save the Contact

LogicApp_8

  • Click ‘New Step’ and select ‘Dynamics 365’ connector. From the ‘Actions’ select ‘Create a new record’ and provide credentials and connect to your Instance

LogicApp_9

  • From the ‘Entity Name’, select ‘Contacts’
  • Map the Contact entity fields with the fields from ‘Dynamic content’ popup.
    • As ‘Company Name’ is a Look up to ‘Account’ entity, make sure you map
      • ‘Company Name Type’ = ‘accounts’
      • ‘Company Name’ = ‘id’ passed from Json

LogicApp_10

  • Next we need to capture the ‘Response’, choose ‘Response’ from ‘Actions’

LogicApp_11

  • In the ‘Response’ control,
    • In the ‘Headers’, add ‘Content-Type’ as key and ‘application/json’ as Value.
    • In the ‘Body’, form your output string and ‘Contact’ field from ‘Dynamic Content’ to capture the GUID

LogicApp_12

  • Save the Logic App

Test the Logic App:

  • Copy the “HTTP POST URL” from the “When a HTTP request is received” control.

LogicApp_13

Using Post Man:

  • Create a ‘POST’ request and paste the URL copied from Logic APP
  • In the ‘Headers’, add ‘Content-Type’ as key and ‘application/json’ as Value.

LogicApp_14

  • In the ‘Body’, paste the Json (You can copy the Json format prepared in ObjGen site)
  • Click ‘Send’ to call Logic App and capture the Response.

LogicApp_2

Using jScript and HTML:

  •  Below the jScript to call the Logic APP URL by passing JSon and capture the response

<html lang=”en” xmlns=”http://www.w3.org/1999/xhtml”&gt;
<head>
<meta charset=”utf-8″ />
<title>Test Logic App</title>

function TestLogicApp() {
try {
var xhr = new XMLHttpRequest();
var url = “Logic App URL“;
xhr.open(“POST”, url, true);
xhr.setRequestHeader(“Content-Type”, “application/json”);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
alert(xhr.responseText);
}
};
var data = JSON.stringify({
“firstname”: “Rajeev”,
“lastname”: “Pentyala”,
“Account”: {
“id”: “A16B3F4B-1BE7-E611-8101-E0071B6AF231”,
“Name”: “A Datum Corporation”
}
});
xhr.send(data);
} catch (e) {
alert(“Error – ” + e.description);
}
}

</head>
<body>
<input type=”button” value=”Post” onclick=”TestLogicApp()” />
</body>
</html>

  • Open the HTML page and click the ‘Post’ button to capture the Response returned from LogicApp

LogicApp_1

Troubleshoot and track the history:

  • To troubleshoot and track the Requests, from the ‘Logic App’, click on ‘Overview’ and check under ‘Run history’

LogicApp_15

🙂

Advertisements

[D365 CE] Migrate data to field with “Time Zone” format

December 14, 2018 Leave a comment

Have you ever worked with “Decimal” field with “Time Zone” Format?

Lets assume your Dynamics application has a global presence and you need to capture “Customer” info along with their “Time Zone”, we can create a Decimal field with “Format” set as “Time Zone”.

TZ_2

With “Time Zone” format, even though, you create a field of “Decimal” type, in UI it will render as drop down and in ‘Advanced Find’ you get a string.

TZ_3

Lets see how to migrate data to the “Time Zone” format field.

Whats “Time Zone” format:

As you would have noticed in above screens, “Decimal” field rendered as Drop down with all time zone options. Now the question is where we are getting these option?

The answer is, Dynamics has an internal entity named “Timezonedefinition” which pulls the time zone options from.

“Timezonedefinition” entity will have below 2 fields

  • Standardname – Name of the Time Zone
  • Timezonecode – Time zone code which is a decimal value

When you add a ‘Decimal’ field with ‘Time Zone’ format to an entity, in UI (i.e., Forms, Advanced Find, Views, etc.) you would get the ‘Standardname’ (i.e., Time zone name) however in the back end ‘Timezonecode’ gets saved.

TZ_4

To check that, export the records to Excel and you would find decimal values in “Timezone” field.

TZ_5

As you notice above, “(GMT-10:00) Hawaii” field rendered as 2 in exported excel, which “Timezonecode”

How to migrate data to “Time Zone” format field:

  • First you need to fetch the “Time zone codes” using “RetrieveMultiple” SDK call (Sample code below)

var request = new RetrieveMultipleRequest{
Query = new QueryExpression(“timezonedefinition”){
ColumnSet = new ColumnSet(“timezonecode”, “standardname”),
}
};

var response = (RetrieveMultipleResponse)_service.Execute(request);

foreach (var item in response.EntityCollection.Entities){
var timeZone = (Entity)item;Console.WriteLine(String.Concat(“Time zone name: “, timeZone[“standardname“], “, time zone code: “, timeZone[“timezonecode“]));
}

  • Executing above code would fetch “Timezone Names” along with “Timezonecode”

TZ_1

  • Once you have the codes, you can migrate the records by setting the “Time zone codes” to “Time zone format” field.
  • In the excel file, add new records with “Timezonecode”

TZ_7

  • After the successful excel import you should see new records in UI with “Time Zone Name” replaced with “Time zone code” passed from excel.

TZ_6.PNG

🙂

 

 

[D365 CE Online] Debug Async Plug-in using “Persist to Entity” profiler option

December 1, 2018 4 comments

As you are aware, using Plug-in Profiler option we can debug D365 online plug-ins by throwing exception and capturing the Serilaized profile. Refer my previous article

With the ‘Exception’ profiling option, you first need to trigger the event and capture the exception log. For example, if you need to debug “Post Account Create” plug-in, you first need to trigger the event (i.e., Create Account from D365 application) and capture the exception log. This ‘Exception’ option would be apt, if your plug-in registered in Sync mode.

What if your plug-in is Async? Answer is, using “Persist to Entity” profiler option.

Plugin Profile 1

Let’s see how to debug Async Plug-in step by step.

Configuring Profiler in Plugin Registration Tool:

  • Connect to your Dynamics instance using Plug-in registration tool.
  • Click on ‘Install Profiler’ button
  • Register a new plug-in assembly and add an Async step
    • In this article, I registered an Async step on Post Account Creation

Plugin Profile 9

  • Select the Plug-in step and click on ‘Start Profiling’
  • In the “Profiler Settings” screen, choose “Persist to Entity” option and click “OK”

Plugin Profile 1

Trigger Account Creation from D365 Application:

  • Connect to D365 application and create a new Account

Plugin Profile 8

  • As we opted for “Persist to Entity” profiler option, an entry will get created in “Settings -> Plug-in  Profiles” entity.
  • Go to “Settings -> Plug-in  Profiles”, open the newly created “Plug-in  Profile” record (Note: Async plug-in takes time to create a new “Plug-in Profile” record).

Plugin Profile 2

  • In the “Plug-in Profile” record, you would notice the Serialized content in “Serialized Profile” field. This is same as what you get in Exception download log if you would have opted for “Exception” profile setting.

Plugin Profile 3

Debug the Plug-in:

Once an entry got created in “Plug-in Profile” record

  • Open the Plug-in class file in Visual Studio and attach “Plug-in Registration Tool” process

Plugin Profile 5

  • Add a break point in your Plug-in class file to debug.
  • Open the “Plugin Registration Tool”
  • Select the Plug-in step and click on “Debug/REPLAY PLUG-IN EXECUTION” button
  • In the “Replay Plug-in Execution” screen, set
    • Profile = Click on ‘Down Arrow’ button to pick your “Plug-in Profile” record from D365 (Refer screen)
    • Assembly Location = Plug-in dll
    • Plugin = Class Name

Plugin Profile 10

  • Click on “Start Execution”

Plugin Profile 11

  • You should see a break point hit in Visual Studio.

Plugin Profile 7

  • Press F10 to proceed with debugging.

🙂

 

 

Dynamics 365 – New Admin Center (Preview)

October 22, 2018 Leave a comment

Have you noticed the Try the new Admin center link when you connected to your Dynamics 365 Admin Center?

Admin Center_2

When you click on the link, you will be redirected to ‘Admin Center (Preview)’ built on Power platform.

Admin Center_1

Click on the Instance name (i.e.,MS) in above screen, will take you Instance Details page.

Admin Center_5

Click on ‘See all‘ to get the ‘Details’ pane.

Admin Center_6

Click on ‘Edit‘ to get ‘Settings’ edit pane where you can change the Instance Type (i.e., Production/Sandbox)

Admin Center_7

I started exploring and one standout feature so far is ‘Settings’ page where you can manage all your Admin related configurations (i.e., Short cuts) in one page.

Admin Center_3

To go to ‘Settings’ page, you first need to select the ‘Instance’ and click on ‘Settings’ button.

Admin Center_4

🙂

Web API Helper Code Compilation Error

September 18, 2018 Leave a comment

I was creating a console application to connect to Dynamics 365 Web API, and downloaded “Microsoft.CrmSdk.WebApi.Samples.HelperCode” NuGet package.

Compilation Error_1

I got “AcquireToken method is no longer available” compilation error, when I build the project

Compilation Error

Reason & Fix:

  • We have to use UserPasswordCredential class in ADAL v3.
  • Below is the code snippet

var credentials = new UserPasswordCredential(userName, password);
var context = new AuthenticationContext(authorityUri);
authResult = context.AcquireTokenAsync(serviceUrl, applicationId, credentials).Result;

Refer my previous article for step by step to connect to Dynamics 365 Web API.

🙂

[Fix] AADSTS65001: The user or administrator has not consented to use the application

September 18, 2018 Leave a comment

I was getting below exception when I was trying to connect to D365 Web API from console.

AAD_Access_2

Reason:

  • Issue was the Application registered on ‘Azure Active Directory’ was not granted the Permission

Fix:

  • Connect to the Azure Active Directory admin center,  using O365 credentials.
  • Select the ‘Application’ from the ‘Azure Active Directory -> App registrations’
  • Click on ‘Settings’ -> Required Permissions -> Click on ‘Grant permissions‘ button

AAD_Access_1

Refer my previous article on how to register application and connect to D365 Web API

🙂

[Code Snippet] Authenticate and Perform Operations using D365 Web API and C#

September 18, 2018 3 comments

As a continuation to my last article Different ways to connect authenticate dynamics 365 , lets see how to Authenticate Dynamics Web API using C#.

Note: Be mindful that this approach is different than connecting to Dynamics 365 using Microsoft.XRM.Tooling.Connector dll approach. This article explains how to connect to D365 using Web API which is no SDK .dll approach.

Pre-requisites:

  • Dynamics 365 subscription. Go for 30 days trail if not already have one.
  • Register an App in “Azure Active Directory” and get ‘Application ID’ and other parameters.
  • Visual Studio Console application

Steps to Register App in “Azure Active Directory”:

We need to register an application with Microsoft Azure Active Directory so that it can connect to the Microsoft Dynamics 365 server, authenticate using OAuth, and access the web services.

WebAPI_Snippet2

  • From the “Azure Active Directory admin center’, select ‘App registrations’ -> New application registration

WebAPI_Snippet3

  • Provide below details
    • Name – Provide name of the App. Can be any name minimum of 4 characters.
    • Application Type – Choose ‘Native’ as we are going to call Web API from Console application
    • Sign-on URL – Can be a valid URL. This you need to pass in the Console application.

WebAPI_Snippet4

  • Click ‘Create’ to complete the App creation
  • Post creation, open the App and copy the ‘Application ID’ which you need in Console application.

WebAPI_Snippet5

  • Click on ‘Settings’ -> Required Permissions -> Add ‘Dynamics CRM Online’ -> Enable the permission as below

WebAPI_Snippet6

  • Finally, select the App, click on ‘Endpoints’ and copy ‘OAuth 2.0 Authorization Endpoint‘ which you would need in Console Application.

WebAPI_Snippet7

Steps to connect to D365 WebAPI from Console Application:

After registering App in ‘Azure Active Directory’ now its time to connect to D365 Web API from Console Application.

  • Create a new C# Console Application project
  • Add below 2 Nuget packages to the project
    • Newtonsoft.Json
    • Microsoft.IdentityModel.Clients.ActiveDirectory

WebAPI_Snippet8

Code Snippet:

In the ‘Program.cs’ file add below

  • Add Using Namespaces:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Net;

  • Add Global Variables:

// O365 User Name and Password
private const string userName = “rajeevpentyala@exploreSept18.onmicrosoft.com”;
private const string password = “YourPasswordHere”;
// D365 Application Url
private const string serviceUrl = “https://exploresept18.crm.dynamics.com&#8221;;
// Azure APP Application Id
private const string applicationId = “1549b5b3-XXXX-XXXX-94be-7a8eeaf3e081”;
// Redirct Uri specified during registration of application
private const string RedirectUri = “https://localhost&#8221;;
// OAuth 2.0 Authorization Endpoint copied from Azure APP
private const string authorityUri = “https://login.microsoftonline.com/9e3039aa-XXXX-XXXX-80e1-f67d40bd01cf/oauth2/authorize&#8221;;

private static AuthenticationResult authResult = null;

  • Main Method:

private static void Main(string[] args){

// Code to connect to D365
var credentials = new UserPasswordCredential(userName, password);
var context = new AuthenticationContext(authorityUri);
authResult = context.AcquireTokenAsync(serviceUrl, applicationId, credentials).Result;

// Call CRUD operations

// Task.WaitAll(Task.Run(async () => await ExecuteWhoAmI()));
// Task.WaitAll(Task.Run(async () => await CreateRecord()));
// Task.WaitAll(Task.Run(async () => await RetrieveContacts()));

}

  • Code to call WhoAmIRequest:

private static async Task ExecuteWhoAmI(){
var httpClient = new HttpClient{
BaseAddress = new Uri(serviceUrl),
Timeout = new TimeSpan(0, 2, 0)
};
httpClient.DefaultRequestHeaders.Add(“OData-MaxVersion”, “4.0”);
httpClient.DefaultRequestHeaders.Add(“OData-Version”, “4.0”);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(“application/json”));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(“Bearer”, authResult.AccessToken);

// Add this line for TLS complaience
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;

// Call WhoAmI
var retrieveResponse = await httpClient.GetAsync(“api/data/v9.0/WhoAmI”);
if (retrieveResponse.IsSuccessStatusCode){
var jRetrieveResponse = JObject.Parse(retrieveResponse.Content.ReadAsStringAsync().Result);

var currUserId = (Guid)jRetrieveResponse[“UserId”];
var businessId = (Guid)jRetrieveResponse[“BusinessUnitId”];

Console.WriteLine(“My User Id – ” + currUserId);
Console.WriteLine(“My User Id – ” + businessId);
Console.ReadLine();
}
}

  • Code to Retrieve Records:

private static async Task RetrieveContacts(){
var httpClient = new HttpClient{
BaseAddress = new Uri(serviceUrl),
Timeout = new TimeSpan(0, 2, 0)
};
httpClient.DefaultRequestHeaders.Add(“OData-MaxVersion”, “4.0”);
httpClient.DefaultRequestHeaders.Add(“OData-Version”, “4.0”);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(“application/json”));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(“Bearer”, authResult.AccessToken);

// Add this line for TLS complaience
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;

// Retrieve Contacts
var retrieveResponse = await httpClient.GetAsync(“api/data/v9.0/contacts”);
if (retrieveResponse.IsSuccessStatusCode){
var jRetrieveResponse = JObject.Parse(retrieveResponse.Content.ReadAsStringAsync().Result);

dynamic collContacts = JsonConvert.DeserializeObject(jRetrieveResponse.ToString());

foreach (var data in collContacts.value){
Console.WriteLine(“Contact Name – ” + data.fullname.Value);
}

Console.ReadLine();
}
}

WebAPI_Snippet1

  • Code to Create Record:

private static async Task CreateRecord(){
JObject contact1 = new JObject{
{ “firstname”, “Peter” },
{ “lastname”, “Cambel” },
{ “annualincome”, 80000 }
};

contact1[“jobtitle”] = “Junior Developer”;

var httpClient = new HttpClient{
BaseAddress = new Uri(serviceUrl + “/api/data/v9.0/”),
Timeout = new TimeSpan(0, 2, 0)
};
httpClient.DefaultRequestHeaders.Add(“OData-MaxVersion”, “4.0”);
httpClient.DefaultRequestHeaders.Add(“OData-Version”, “4.0”);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(“application/json”));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(“Bearer”, authResult.AccessToken);

// Add this line for TLS complaience
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, “contacts”){
Content = new StringContent(contact1.ToString(), Encoding.UTF8, “application/json”)
};

HttpResponseMessage response = await httpClient.SendAsync(request);if (response.StatusCode == HttpStatusCode.NoContent) //204 {
Console.WriteLine(“POST succeeded, entity created!”);
//optionally process response message headers or body here, for example:
var entityUri = response.Headers.GetValues(“OData-EntityId”).FirstOrDefault();

// Update the Contact record
Task.WaitAll(Task.Run(async () => await UpdateRecord(entityUri)));

// Delete the contact record
Task.WaitAll(Task.Run(async () => await DeleteRecord(entityUri)));
}
else{
Console.WriteLine(“Operation failed: {0}”, response.ReasonPhrase);
throw new CrmHttpResponseException(response.Content);
}
}

  • Code to Update Record:

private static async Task UpdateRecord(string contactUri){
JObject contact1Add = new JObject{
{ “annualincome”, 80000 },
{ “jobtitle”, “Junior Developer” }
};

var httpClient = new HttpClient{
BaseAddress = new Uri(serviceUrl + “/api/data/v9.0/”),
Timeout = new TimeSpan(0, 2, 0)
};

httpClient.DefaultRequestHeaders.Add(“OData-MaxVersion”, “4.0”);
httpClient.DefaultRequestHeaders.Add(“OData-Version”, “4.0”);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(“application/json”));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(“Bearer”, authResult.AccessToken);

HttpRequestMessage updateRequest1 = new HttpRequestMessage(new HttpMethod(“PATCH“), contactUri){
Content = new StringContent(contact1Add.ToString(), Encoding.UTF8, “application/json”)
};
HttpResponseMessage updateResponse1 = await httpClient.SendAsync(updateRequest1);

if (updateResponse1.StatusCode == HttpStatusCode.NoContent) //204 {
//Console.WriteLine(“Contact ‘{0} {1}’ updated with job title” +
// ” and annual income.”, contactUri.GetValue(“firstname”),
// contactUri.GetValue(“lastname”));
}
else{
Console.WriteLine(“Failed to update contact for reason: {0}”, updateResponse1.ReasonPhrase);
throw new CrmHttpResponseException(updateResponse1.Content);
}

}

  • Code to Delete Record:

private static async Task DeleteRecord(string contactUri){
var httpClient = new HttpClient{
BaseAddress = new Uri(serviceUrl + “/api/data/v9.0/”),
Timeout = new TimeSpan(0, 2, 0)
};

httpClient.DefaultRequestHeaders.Add(“OData-MaxVersion”, “4.0”);
httpClient.DefaultRequestHeaders.Add(“OData-Version”, “4.0”);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(“application/json”));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(“Bearer”, authResult.AccessToken);

var response = httpClient.DeleteAsync(contactUri).Result;
if (response.IsSuccessStatusCode) //200-299 {
Console.WriteLine(“Contact has been deleted!!!”);
}
else if (response.StatusCode == HttpStatusCode.NotFound) //404 {
//Entity may have been deleted by another user or via cascade delete.
}
else //Failed to delete {
Console.WriteLine(“Error while deletion; Message: ” + response.Content);
}
}

🙂