Skip to content

Commit 018fc48

Browse files
author
Dmitry Kozlov
committed
adjust clients for updated API of Dynamics and Business Central
1 parent ce30f56 commit 018fc48

File tree

15 files changed

+200
-138
lines changed

15 files changed

+200
-138
lines changed

FunctionApp/DebugRequestHandler.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace Plumsail.DataSource
8+
{
9+
class DebugRequestHandler : DelegatingHandler
10+
{
11+
public DebugRequestHandler(HttpMessageHandler? innerHandler = null)
12+
{
13+
InnerHandler = innerHandler ?? new HttpClientHandler();
14+
}
15+
16+
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
17+
{
18+
Console.WriteLine("");
19+
Console.WriteLine(string.Format("Request: {0} {1}", request.Method, request.RequestUri));
20+
Console.WriteLine("Request headers:");
21+
foreach (var header in request.Headers)
22+
{
23+
Console.WriteLine(string.Format("{0}: {1}", header.Key, string.Join(',', header.Value)));
24+
}
25+
if (request.Content is not null)
26+
{
27+
Console.WriteLine("");
28+
Console.WriteLine("Request body:");
29+
var body = await request.Content.ReadAsStringAsync();
30+
Console.WriteLine(body);
31+
}
32+
33+
return await base.SendAsync(request, cancellationToken);
34+
}
35+
}
36+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace Plumsail.DataSource
8+
{
9+
class DebugResponseHandler : DelegatingHandler
10+
{
11+
public DebugResponseHandler(HttpMessageHandler? innerHandler = null)
12+
{
13+
InnerHandler = innerHandler ?? new HttpClientHandler();
14+
}
15+
16+
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
17+
{
18+
var response = await base.SendAsync(request, cancellationToken);
19+
20+
Console.WriteLine("");
21+
Console.WriteLine("Response headers:");
22+
foreach (var header in response.Headers)
23+
{
24+
Console.WriteLine(string.Format("{0}: {1}", header.Key, string.Join(',', header.Value)));
25+
}
26+
if (response.Content is not null)
27+
{
28+
Console.WriteLine("");
29+
Console.WriteLine("Response body:");
30+
var body = await response.Content.ReadAsStringAsync();
31+
Console.WriteLine(body);
32+
}
33+
34+
return response;
35+
}
36+
}
37+
}

FunctionApp/Dynamics365/BusinessCentral/Authorize.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public Authorize(IOptions<AppSettings> settings, ILogger<Authorize> logger)
2828
[Function("D365-BC-Authorize")]
2929
public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequest req)
3030
{
31-
var scopes = new string[] { "https://graph.microsoft.com/.default", "offline_access" };
31+
var scopes = new string[] { "https://api.businesscentral.dynamics.com/.default", "offline_access" };
3232

3333
if (req.Method == "POST" && req.Form.ContainsKey("code"))
3434
{

FunctionApp/Dynamics365/BusinessCentral/Customers.cs

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,26 @@
55
using Microsoft.Extensions.Logging;
66
using Microsoft.Extensions.Options;
77
using Microsoft.Graph;
8+
using Microsoft.Graph.Beta;
89
using Microsoft.Graph.Beta.Models;
910
using Plumsail.DataSource.Dynamics365.BusinessCentral.Settings;
11+
using Plumsail.DataSource.Dynamics365.CRM;
1012
using System.Collections.Generic;
13+
using System.Text.Json.Nodes;
1114
using System.Threading.Tasks;
1215

1316
namespace Plumsail.DataSource.Dynamics365.BusinessCentral
1417
{
1518
public class Customers
1619
{
1720
private readonly Settings.Customers _settings;
18-
private readonly GraphServiceClientProvider _graphProvider;
21+
private readonly HttpClientProvider _httpClientProvider;
1922
private readonly ILogger<Customers> _logger;
2023

21-
public Customers(IOptions<AppSettings> settings, GraphServiceClientProvider graphProvider, ILogger<Customers> logger)
24+
public Customers(IOptions<AppSettings> settings, HttpClientProvider httpClientProvider, ILogger<Customers> logger)
2225
{
2326
_settings = settings.Value.Customers;
24-
_graphProvider = graphProvider;
27+
_httpClientProvider = httpClientProvider;
2528
_logger = logger;
2629
}
2730

@@ -30,24 +33,16 @@ public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "
3033
{
3134
_logger.LogInformation("Dynamics365-BusinessCentral-Customers is requested.");
3235

33-
var graph = await _graphProvider.Create();
34-
var company = await graph.GetCompanyAsync(_settings.Company);
35-
if (company == null)
36+
var client = _httpClientProvider.Create();
37+
var companyId = await client.GetCompanyIdAsync(_settings.Company);
38+
if (companyId == null)
3639
{
3740
return new NotFoundResult();
3841
}
3942

40-
var customersPage = await graph.Financials.Companies[company.Id.Value].Customers.GetAsync();
41-
var customers = new List<Customer>();
42-
var pageIterator = PageIterator<Customer, CustomerCollectionResponse>
43-
.CreatePageIterator(graph, customersPage, customer =>
44-
{
45-
customers.Add(customer);
46-
return true;
47-
});
48-
49-
await pageIterator.IterateAsync();
50-
return new OkObjectResult(customers);
43+
var customersJson = await client.GetStringAsync($"companies({companyId})/customers");
44+
var customers = JsonValue.Parse(customersJson);
45+
return new OkObjectResult(customers?["value"]);
5146
}
5247
}
5348
}

FunctionApp/Dynamics365/BusinessCentral/GraphServiceClientExtensions.cs

Lines changed: 0 additions & 20 deletions
This file was deleted.

FunctionApp/Dynamics365/BusinessCentral/GraphServiceClientProvider.cs

Lines changed: 0 additions & 49 deletions
This file was deleted.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System.Text.Json.Nodes;
2+
3+
namespace Plumsail.DataSource.Dynamics365.BusinessCentral
4+
{
5+
internal static class HttpClientExtensions
6+
{
7+
internal static async System.Threading.Tasks.Task<string> GetCompanyIdAsync(this HttpClient client, string companyName)
8+
{
9+
var companiesJson = await client.GetStringAsync("companies?$select=id,name");
10+
var contacts = JsonValue.Parse(companiesJson)["value"].AsArray();
11+
12+
return contacts.FirstOrDefault(c => c["name"]?.GetValue<string>() == companyName)?["id"]?.GetValue<string>();
13+
}
14+
}
15+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using Azure.Core;
2+
using Azure.Identity;
3+
using Microsoft.Extensions.Options;
4+
using Microsoft.Graph.Beta;
5+
using Microsoft.Identity.Client;
6+
using Microsoft.Kiota.Abstractions.Authentication;
7+
using Plumsail.DataSource.Dynamics365.BusinessCentral.Settings;
8+
using Plumsail.DataSource.Dynamics365.CRM;
9+
using System;
10+
using System.Net.Http;
11+
using System.Net.Http.Headers;
12+
using System.Threading;
13+
using System.Threading.Tasks;
14+
15+
namespace Plumsail.DataSource.Dynamics365.BusinessCentral
16+
{
17+
public class HttpClientProvider
18+
{
19+
private readonly AzureApp _azureAppSettings;
20+
21+
public HttpClientProvider(IOptions<AppSettings> settings)
22+
{
23+
_azureAppSettings = settings.Value.AzureApp;
24+
}
25+
26+
public HttpClient Create()
27+
{
28+
// for debugging requests
29+
//var debugHandler = new DebugRequestHandler(new DebugResponseHandler());
30+
//var client = new HttpClient(new OAuthMessageHandler(_azureAppSettings, debugHandler));
31+
32+
var client = new HttpClient(new OAuthMessageHandler(_azureAppSettings));
33+
client.BaseAddress = new Uri($"https://api.businesscentral.dynamics.com/v2.0/{_azureAppSettings.InstanceId}/Production/api/v2.0/");
34+
client.Timeout = new TimeSpan(0, 2, 0);
35+
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
36+
37+
return client;
38+
}
39+
}
40+
41+
class OAuthMessageHandler : DelegatingHandler
42+
{
43+
private readonly AzureApp _azureAppSettings;
44+
45+
public OAuthMessageHandler(AzureApp azureAppSettings, HttpMessageHandler? innerHandler = null) : base(innerHandler ?? new HttpClientHandler())
46+
{
47+
_azureAppSettings = azureAppSettings;
48+
}
49+
50+
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
51+
{
52+
var app = ConfidentialClientApplicationBuilder.Create(_azureAppSettings.ClientId)
53+
.WithClientSecret(_azureAppSettings.ClientSecret)
54+
.WithTenantId(_azureAppSettings.Tenant)
55+
.Build();
56+
57+
var cache = new TokenCacheHelper(AzureApp.CacheFileDir);
58+
cache.EnableSerialization(app.UserTokenCache);
59+
60+
var account = await app.GetAccountAsync(cache.GetAccountIdentifier());
61+
var result = await app.AcquireTokenSilent(["https://api.businesscentral.dynamics.com/.default"], account).ExecuteAsync();
62+
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
63+
return await base.SendAsync(request, cancellationToken);
64+
}
65+
}
66+
}

FunctionApp/Dynamics365/BusinessCentral/Items.cs

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,47 +8,40 @@
88
using Microsoft.Graph.Beta.Models;
99
using Plumsail.DataSource.Dynamics365.BusinessCentral.Settings;
1010
using System.Collections.Generic;
11+
using System.Text.Json.Nodes;
1112
using System.Threading.Tasks;
1213

1314
namespace Plumsail.DataSource.Dynamics365.BusinessCentral
1415
{
1516
public class Items
1617
{
1718
private readonly Settings.Items _settings;
18-
private readonly GraphServiceClientProvider _graphProvider;
19+
private readonly HttpClientProvider _httpClientProvider;
1920
private readonly ILogger<Items> _logger;
2021

21-
public Items(IOptions<AppSettings> settings, GraphServiceClientProvider graphProvider, ILogger<Items> logger)
22+
public Items(IOptions<AppSettings> settings, HttpClientProvider httpClientProvider, ILogger<Items> logger)
2223
{
2324
_settings = settings.Value.Items;
24-
_graphProvider = graphProvider;
25+
_httpClientProvider = httpClientProvider;
2526
_logger = logger;
2627
}
2728

2829
[Function("D365-BC-Items")]
2930
public async Task<IActionResult> Run(
3031
[HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req)
3132
{
32-
_logger.LogInformation("Dynamics365-BusinessCentral-Vendors is requested.");
33+
_logger.LogInformation("Dynamics365-BusinessCentral-Items is requested.");
3334

34-
var graph = await _graphProvider.Create();
35-
var company = await graph.GetCompanyAsync(_settings.Company);
36-
if (company == null)
35+
var client = _httpClientProvider.Create();
36+
var companyId = await client.GetCompanyIdAsync(_settings.Company);
37+
if (companyId == null)
3738
{
3839
return new NotFoundResult();
3940
}
4041

41-
var itemsPage = await graph.Financials.Companies[company.Id.Value].Items.GetAsync();
42-
var items = new List<Item>();
43-
var pageIterator = PageIterator<Item, ItemCollectionResponse>
44-
.CreatePageIterator(graph, itemsPage, item =>
45-
{
46-
items.Add(item);
47-
return true;
48-
});
49-
50-
await pageIterator.IterateAsync();
51-
return new OkObjectResult(items);
42+
var itemsJson = await client.GetStringAsync($"companies({companyId})/items");
43+
var items = JsonValue.Parse(itemsJson);
44+
return new OkObjectResult(items?["value"]);
5245
}
5346
}
5447
}

FunctionApp/Dynamics365/BusinessCentral/Settings/AzureApp.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ public class AzureApp
1010
public string ClientId { get; set; }
1111
public string ClientSecret { get; set; }
1212
public string Tenant { get; set; }
13+
public string InstanceId { get; set; }
1314
}
1415
}

0 commit comments

Comments
 (0)