Skip to content

Commit 47ae9af

Browse files
committed
prepare erabliereapi connection
1 parent 573290b commit 47ae9af

File tree

5 files changed

+195
-5
lines changed

5 files changed

+195
-5
lines changed

erabliereapi/APIWebService.al

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
codeunit 50126 "API Web Service"
2+
{
3+
//Get a valid authentication token from setup. If token-age < expiry, get from blob, otherwise call API
4+
procedure GetAuthenticationToken(ApiSetup: Record "EAPI Setup"; ForceRenewal: Boolean): Text
5+
var
6+
TokenResponseText: Text;
7+
TokenExpiry: DateTime;
8+
TokenOutStream: OutStream;
9+
TokenInStream: InStream;
10+
AuthPayload: Text;
11+
begin
12+
if (ApiSetup."Token valid until" <= CurrentDateTime()) or ForceRenewal then begin
13+
//Get fresh Token
14+
TokenResponseText := GetFreshAuthenticationToken(ApiSetup, TokenExpiry);
15+
16+
//Write Token to Blob
17+
ApiSetup."API Token".CreateOutStream(TokenOutStream);
18+
TokenOutStream.WriteText(TokenResponseText);
19+
20+
//Calculate the expriation date of the token.
21+
//Should be defined by the API or even delivered in the response
22+
if TokenExpiry <> 0DT then
23+
ApiSetup."Token valid until" := TokenExpiry;
24+
ApiSetup.Modify();
25+
end else begin
26+
ApiSetup.CalcFields("API Token");
27+
28+
//Read Token from Blob
29+
ApiSetup."API Token".CreateInStream(TokenInStream);
30+
TokenInStream.ReadText(TokenResponseText);
31+
end;
32+
33+
//Return the token
34+
exit(TokenResponseText);
35+
end;
36+
37+
procedure GetFreshAuthenticationToken(ApiSetup: Record "EAPI Setup"; var TokenExpiry: DateTime): Text
38+
var
39+
AuthPayload: Text;
40+
ResponseText: Text;
41+
TokenResponseText: Text;
42+
JObjectResult: JsonObject;
43+
JObjectRequest: JsonObject;
44+
WebClient: HttpClient;
45+
RequestHeader: HttpHeaders;
46+
ResponseMessage: HttpResponseMessage;
47+
RequestMessage: HttpRequestMessage;
48+
RequestContent: HttpContent;
49+
TokenOutStream: OutStream;
50+
begin
51+
//Create webservice call
52+
RequestMessage.Method := 'POST';
53+
RequestMessage.SetRequestUri(ApiSetup.Audiance);
54+
55+
//Create webservice header
56+
RequestMessage.GetHeaders(RequestHeader);
57+
58+
//Payload needed? This might as well be a different implementation!
59+
//It's just an example where the credentials are stored as a json payload
60+
61+
//Create json payload
62+
JObjectRequest.Add('client_id', ApiSetup."Client Id");
63+
JObjectRequest.Add('client_secret', ApiSetup."Client Secret");
64+
JObjectRequest.WriteTo(AuthPayload);
65+
66+
//Get Request Content
67+
RequestContent.WriteFrom(AuthPayload);
68+
69+
RequestContent.GetHeaders(RequestHeader);
70+
RequestHeader.Remove('Content-Type');
71+
RequestHeader.Add('Content-Type', 'application/json');
72+
73+
RequestMessage.Content := RequestContent;
74+
75+
//Send webservice query
76+
WebClient.Send(RequestMessage, ResponseMessage);
77+
78+
if ResponseMessage.IsSuccessStatusCode() then begin
79+
ResponseMessage.Content().ReadAs(ResponseText);
80+
81+
if not JObjectResult.ReadFrom(ResponseText) then
82+
Error('Error Read JSON');
83+
84+
TokenResponseText := GetJsonToken(JObjectResult, 'access_token').AsValue().AsText();
85+
TokenExpiry := GetJsonToken(JObjectResult, 'expiry_date').AsValue().AsDateTime();
86+
87+
end else
88+
Error('Webservice Error');
89+
90+
exit(TokenResponseText);
91+
end;
92+
93+
local procedure GetJsonToken(JsonObject: JsonObject; TokenKey: Text) JsonToken: JsonToken;
94+
begin
95+
if not JsonObject.Get(TokenKey, JsonToken) then
96+
Error(StrSubstNo('Token %1 not found', TokenKey));
97+
end;
98+
}

erabliereapi/DynDialog.al

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ codeunit 50130 DynDialog
1010
Texts.Add('');
1111
end;
1212
Dialog.Open(dialogTemplate);
13+
IsOpen := true;
1314
end;
1415

1516
procedure Close()
1617
begin
1718
Dialog.Close();
19+
IsOpen := false;
1820
end;
1921

2022
procedure Update(number: Integer; text: Text)
@@ -23,11 +25,19 @@ codeunit 50130 DynDialog
2325
Dialog.Update(number, text);
2426
end;
2527

28+
procedure UpdateAppend(number: Integer; text: Text)
29+
begin
30+
Texts.Set(number, Texts.Get(number) + text);
31+
Dialog.Update(number, Texts.Get(number));
32+
end;
33+
2634
procedure PrettyMessage(text: Text)
2735
var
2836
i: Integer;
2937
template: Text;
3038
begin
39+
if IsOpen then
40+
Close();
3141
for i := 1 to Texts.Count do
3242
template += Texts.Get(i) + '\';
3343
Message(text + '\' + template, Texts);
@@ -36,4 +46,5 @@ codeunit 50130 DynDialog
3646
var
3747
Dialog: Dialog;
3848
Texts: List of [Text];
49+
IsOpen: Boolean;
3950
}

erabliereapi/EAPISetup.al

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
table 50125 "EAPI Setup"
2+
{
3+
DataClassification = ToBeClassified;
4+
5+
fields
6+
{
7+
field(1; Code; Code[10])
8+
{
9+
DataClassification = CustomerContent;
10+
}
11+
field(5; "API URL"; Text[200])
12+
{
13+
DataClassification = CustomerContent;
14+
}
15+
field(10; "Client Id"; Text[150])
16+
{
17+
DataClassification = CustomerContent;
18+
}
19+
field(11; "Audiance"; Text[150])
20+
{
21+
DataClassification = CustomerContent;
22+
}
23+
field(12; "Scope"; Text[150])
24+
{
25+
DataClassification = CustomerContent;
26+
}
27+
field(15; "Client Secret"; Text[150])
28+
{
29+
DataClassification = CustomerContent;
30+
}
31+
field(16; "Client Certificate"; Text[150])
32+
{
33+
DataClassification = CustomerContent;
34+
}
35+
field(20; "API Token"; Blob)
36+
{
37+
DataClassification = SystemMetadata;
38+
}
39+
field(25; "Token valid until"; DateTime)
40+
{
41+
DataClassification = SystemMetadata;
42+
}
43+
}
44+
45+
keys
46+
{
47+
key(PK; Code)
48+
{
49+
Clustered = true;
50+
}
51+
}
52+
53+
var
54+
ApiWebservice: Codeunit "Api Web Service";
55+
56+
procedure GetAuthenticationToken(ForceRenewal: Boolean): Text
57+
begin
58+
exit(ApiWebservice.GetFreshAuthenticationToken(Rec, Rec."Token valid until"));
59+
end;
60+
}

erabliereapi/ErablieresTable.al

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,8 @@ table 50100 Erablieres
1111
DataClassification = ToBeClassified;
1212
}
1313

14-
// The "Description" field can contain a string
15-
// with up to 250 characters.
1614
field(2; Description; Text[250])
1715
{
18-
// This property specified that
19-
// this field cannot be left empty.
2016
NotBlank = true;
2117
}
2218

@@ -36,6 +32,21 @@ table 50100 Erablieres
3632
Duration := Utils.ParseISODuration(DurationText);
3733
end;
3834
}
35+
36+
field(5; "Invoice Contact"; Code[20])
37+
{
38+
DataClassification = ToBeClassified;
39+
}
40+
41+
field(6; "Invoice Customer"; Code[20])
42+
{
43+
DataClassification = ToBeClassified;
44+
}
45+
46+
field(7; "Last Invoice Date"; Date)
47+
{
48+
DataClassification = ToBeClassified;
49+
}
3950
}
4051

4152
keys

erabliereapi/README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
11
# ErabliereAPI-Connector
22

3-
Connector for the ErabliereAPI project.
3+
Connector for the ErabliereAPI project.
4+
5+
## Links
6+
7+
1. https://www.j3ns.de/d365-business-central/oauth-2-0-in-al/
8+
2. https://learn.microsoft.com/en-us/entra/msal/dotnet/acquiring-tokens/web-apps-apis/confidential-client-assertions
9+
3. https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/administration/automation-apis-using-s2s-authentication
10+
4. https://sookocheff.com/post/api/generating-jwt-assertions/
11+
5. https://learn.microsoft.com/en-us/dynamics365/business-central/application/system-application/codeunit/system.security.encryption.cryptography-management
12+
6. https://community.dynamics.com/blogs/post/?postid=e288a516-2d2b-44b4-bac2-b290a7a8e7f5
13+
7. https://goodworkaround.com/2020/07/07/authenticating-to-azure-ad-as-an-application-using-certificate-based-client-credential-grant/

0 commit comments

Comments
 (0)