Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[E-Document Connector] Logiq E-Document Connector #27562

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions Apps/W1/EDocumentConnectors/Logiq/app/app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{

Check failure on line 1 in Apps/W1/EDocumentConnectors/Logiq/app/app.json

View workflow job for this annotation

GitHub Actions / Build 1st Party Apps (W1) (Translated) / 1st Party Apps (W1) (Translated)

AS0051 The manifest property 'contextSensitiveHelpUrl' must be specified and contain a meaningful value.
"id": "f4a198ad-cd8c-44bb-aff1-814e0e28ab79",
"name": "E-Document Connector - Logiq",
"publisher": "Microsoft",
"version": "26.0.0.0",
"brief": "E-Document Connector - Logiq",
"description": "E-Document Connector - Logiq",
"privacyStatement": "https://go.microsoft.com/fwlink/?LinkId=724009",
"EULA": "https://go.microsoft.com/fwlink/?linkid=2009120",
"help": "https://go.microsoft.com/fwlink/?linkid=2204541",
"url": "https://go.microsoft.com/fwlink/?LinkId=724011",
"logo": "",

Check failure on line 12 in Apps/W1/EDocumentConnectors/Logiq/app/app.json

View workflow job for this annotation

GitHub Actions / Build 1st Party Apps (W1) (Translated) / 1st Party Apps (W1) (Translated)

AS0051 The manifest property 'logo' must be specified and contain a meaningful value.
"dependencies": [
{
"id": "e1d97edc-c239-46b4-8d84-6368bdf67c8b",
"name": "E-Document Core",
"publisher": "Microsoft",
"version": "24.0.0.0"
}
],
"internalsVisibleTo": [
{
"id": "d57ff58b-78cc-488f-81d0-c42079507a2d",
"name": "E-Document Connector - Logiq Tests",
"publisher": "Microsoft"
}
],
"screenshots": [],
"platform": "26.0.0.0",
"application": "26.0.0.0",
"idRanges": [
{
"from": 6380,
"to": 6389
}
],
"resourceExposurePolicy": {
"allowDebugging": true,
"allowDownloadingSource": true,
"includeSourceInSymbolFile": true
},
"features": [
"TranslationFile"
],
"target": "OnPrem"
}
20 changes: 20 additions & 0 deletions Apps/W1/EDocumentConnectors/Logiq/app/src/APIEngine.Enum.al
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace Microsoft.EServices.EDocumentConnector.Logiq;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All files should contain the license comments as the first thing.


enum 6380 "API Engine"
{
Extensible = false;
Access = Internal;

value(0; " ")
{
Caption = '', Locked = true;
}
value(1; Engine1)
{
Caption = 'Engine 1';
}
value(2; Engine3)
{
Caption = 'Engine 3';
}
}
181 changes: 181 additions & 0 deletions Apps/W1/EDocumentConnectors/Logiq/app/src/Auth.Codeunit.al
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
namespace Microsoft.EServices.EDocumentConnector.Logiq;
codeunit 6383 Auth
{
internal procedure SetIsolatedStorageValue(var ValueKey: Guid; Value: SecretText; TokenDataScope: DataScope)
begin
if IsNullGuid(ValueKey) then
ValueKey := CreateGuid();

IsolatedStorage.Set(ValueKey, Value, TokenDataScope);
end;

internal procedure SetIsolatedStorageValue(var ValueKey: Guid; Value: SecretText)
begin
this.SetIsolatedStorageValue(ValueKey, Value, DataScope::Company);
end;

internal procedure GetIsolatedStorageValue(var ValueKey: Guid; var Value: SecretText; TokenDataScope: DataScope)
begin
if IsNullGuid(ValueKey) then
exit;
IsolatedStorage.Get(ValueKey, TokenDataScope, Value);
end;

internal procedure GetIsolatedStorageValue(var ValueKey: Guid; var Value: SecretText)
begin
this.GetIsolatedStorageValue(ValueKey, Value, DataScope::Company);
end;

[NonDebuggable]
internal procedure GetTokens()
var
LogiqConnectionSetup: Record "Connection Setup";
LogiqConnectionUserSetup: Record "Connection User Setup";
Client: HttpClient;
Headers: HttpHeaders;
Content: HttpContent;
RequestMessage: HttpRequestMessage;
ResponseMessage: HttpResponseMessage;
AccessToken, RefreshToken : SecretText;
AccessTokExpires, RefreshTokExpires : DateTime;
begin
this.CheckSetup(LogiqConnectionSetup);
this.CheckUserCredentials(LogiqConnectionUserSetup);

RequestMessage.Method('POST');
RequestMessage.SetRequestUri(LogiqConnectionSetup."Authentication URL");

this.BuildTokenRequestBody(Content);

Content.GetHeaders(Headers);
Headers.Clear();
Headers.Add('Content-Type', 'application/x-www-form-urlencoded');

RequestMessage.Content(Content);

Client.Send(RequestMessage, ResponseMessage);

if not ResponseMessage.IsSuccessStatusCode() then begin
if GuiAllowed then
Message(this.AuthenticationFailedErr);
exit;
end;

this.ParseTokens(ResponseMessage, AccessToken, RefreshToken, AccessTokExpires, RefreshTokExpires);

this.SaveTokens(AccessToken, RefreshToken, AccessTokExpires, RefreshTokExpires);
end;

local procedure BuildTokenRequestBody(var Content: HttpContent)
var
LogiqConnectionSetup: Record "Connection Setup";
LogiqConnectionUserSetup: Record "Connection User Setup";
BodyText: SecretText;
begin
LogiqConnectionSetup.Get();
LogiqConnectionUserSetup.Get(UserId());

if (not IsNullGuid(LogiqConnectionUserSetup."Refresh Token")) and (LogiqConnectionUserSetup."Refresh Token Expiration" > (CurrentDateTime + 60 * 1000)) then
BodyText := SecretText.SecretStrSubstNo(this.RefreshTokenBodyTok, LogiqConnectionSetup."Client ID", LogiqConnectionSetup.GetClientSecret(), LogiqConnectionUserSetup.GetRefreshToken())
else
BodyText := SecretText.SecretStrSubstNo(this.CredentialsBodyTok, LogiqConnectionSetup."Client ID", LogiqConnectionSetup.GetClientSecret(), LogiqConnectionUserSetup.Username, LogiqConnectionUserSetup.GetPassword());

Content.WriteFrom(BodyText);
end;

internal procedure CheckUserCredentials(var LogiqConnectionUserSetup: Record "Connection User Setup")
begin
if not LogiqConnectionUserSetup.Get(UserId()) then
Error(this.NoUserSetupErr);

if (LogiqConnectionUserSetup.Username = '') or (IsNullGuid(LogiqConnectionUserSetup."Password")) then
Error(this.MissingCredentialsErr);
end;

internal procedure CheckUserSetup(var LogiqConnectionUserSetup: Record "Connection User Setup")
var
begin
this.CheckUserCredentials(LogiqConnectionUserSetup);

if (LogiqConnectionUserSetup."API Engine" = LogiqConnectionUserSetup."API Engine"::" ") then
Error(this.MissingAPIEngineErr);

if (LogiqConnectionUserSetup."Document Transfer Endpoint" = '') or (LogiqConnectionUserSetup."Document Status Endpoint" = '') then
Error(this.MissingEndpointsErr);
end;

internal procedure CheckSetup(var LogiqConnectionSetup: Record "Connection Setup")
begin
if not LogiqConnectionSetup.Get() then
Error(this.NoSetupErr);

if (LogiqConnectionSetup."Client ID" = '') or (IsNullGuid(LogiqConnectionSetup."Client Secret")) then
Error(this.MissingClientInfoErr);

if LogiqConnectionSetup."Authentication URL" = '' then
Error(this.MissingAuthUrlErr);

if LogiqConnectionSetup."Base URL" = '' then
Error(this.MissingBaseUrlErr);
end;

[NonDebuggable]
local procedure ParseTokens(ResponseMessage: HttpResponseMessage; var AccessToken: SecretText; var RefreshToken: SecretText; var AccessTokExpires: DateTime; var RefreshTokExpires: DateTime)
var
ContentJson: JsonObject;
JsonTok: JsonToken;
ResponseTxt: Text;
begin
ResponseMessage.Content.ReadAs(ResponseTxt);
ContentJson.ReadFrom(ResponseTxt);
if ContentJson.Get('access_token', JsonTok) then
AccessToken := JsonTok.AsValue().AsText();
if ContentJson.Get('refresh_token', JsonTok) then
RefreshToken := JsonTok.AsValue().AsText();
if ContentJson.Get('expires_in', JsonTok) then
AccessTokExpires := CurrentDateTime + JsonTok.AsValue().AsInteger() * 1000;
if ContentJson.Get('refresh_expires_in', JsonTok) then
RefreshTokExpires := CurrentDateTime + JsonTok.AsValue().AsInteger() * 1000;
end;

local procedure SaveTokens(AccessToken: SecretText; RefreshToken: SecretText; AccessTokExpires: DateTime; RefreshTokExpires: DateTime)
var
LogiqConnectionUserSetup: Record "Connection User Setup";
begin
LogiqConnectionUserSetup.Get(UserId());
this.SetIsolatedStorageValue(LogiqConnectionUserSetup."Access Token", AccessToken, DataScope::User);
this.SetIsolatedStorageValue(LogiqConnectionUserSetup."Refresh Token", RefreshToken, DataScope::User);
LogiqConnectionUserSetup."Access Token Expiration" := AccessTokExpires;
LogiqConnectionUserSetup."Refresh Token Expiration" := RefreshTokExpires;
LogiqConnectionUserSetup.Modify(false);
end;

internal procedure HasToken(ValueKey: Guid; DataScope: DataScope): Boolean
begin
exit(IsolatedStorage.Contains(ValueKey, DataScope));
end;

internal procedure CheckUpdateTokens()
var
LogiqConnectionUserSetup: Record "Connection User Setup";
begin
if not LogiqConnectionUserSetup.Get(UserId()) then
Error(this.NoUserSetupErr);
if IsNullGuid(LogiqConnectionUserSetup."Access Token") or (LogiqConnectionUserSetup."Access Token Expiration" < (CurrentDateTime + 5 * 60 * 1000)) then
this.GetTokens();
end;

var
AuthenticationFailedErr: Label 'Logiq authentication failed. Please check the user credentials.';
CredentialsBodyTok: Label 'grant_type=password&scope=openid&client_id=%1&client_secret=%2&username=%3&password=%4', Locked = true;
MissingAPIEngineErr: Label 'API Engine is missing. Please select the API Engine in the Logiq Connection User Setup page.';
MissingAuthUrlErr: Label 'Authentication URL is missing. Please fill the Authentication URL in the Logiq Connection Setup page.';
MissingBaseUrlErr: Label 'Base URL is missing. Please fill the API Base URL in the Logiq Connection Setup page.';
MissingClientInfoErr: Label 'Client ID or Client Secret is missing. Please fill the Client ID and Client Secret in the Logiq Connection Setup page.';
MissingCredentialsErr: Label 'User credentials are missing. Please enter username and password in the Logiq Connection User Setup page.';
MissingEndpointsErr: Label 'Endpoints are missing. Please fill the Document Transfer Endpoint and Document Status Endpoint in the Logiq Connection User Setup page.';
NoSetupErr: Label 'No setup found. Please fill the setup in the Logiq Connection Setup page.';
NoUserSetupErr: Label 'No user setup found. Please fill the user setup in the Logiq Connection User Setup page.';
RefreshTokenBodyTok: Label 'grant_type=refresh_token&client_id=%1&client_secret=%2&refresh_token=%3', Locked = true;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
namespace Microsoft.EServices.EDocumentConnector.Logiq;

page 6380 "Connection Setup"
{
Caption = 'Logiq Connection Setup';
PageType = Card;
ApplicationArea = Basic, Suite;
UsageCategory = None;
SourceTable = "Connection Setup";

layout
{
area(Content)
{
group(General)
{
Caption = 'General';

field(ClientID; Rec."Client ID")
{
ShowMandatory = true;
}
field(ClientSecret; this.ClientSecret)
{
Caption = 'Client Secret';
ToolTip = 'Specifies the client secret token.';
ExtendedDatatype = Masked;
ShowMandatory = true;

trigger OnValidate()
begin
this.LogiqAuth.SetIsolatedStorageValue(Rec."Client Secret", this.ClientSecret);
end;
}
field("Authentication URL"; Rec."Authentication URL")
{
}
field("Base URL"; Rec."Base URL")
{
}
field("File List Endpoint"; Rec."File List Endpoint")
{
}
}
}
}

actions
{
area(Processing)
{
action(Connect)
{
ApplicationArea = All;
Caption = 'User Setup';
Image = Setup;
ToolTip = 'Open page for User Setup.';

trigger OnAction()
var
LogiqConnectionUserSetup: Record "Connection User Setup";
LoqiqConnectionUserSetupPage: Page "Connection User Setup";
begin
LogiqConnectionUserSetup.FindUserSetup(CopyStr(UserId(), 1, 50));
LoqiqConnectionUserSetupPage.SetRecord(LogiqConnectionUserSetup);
LoqiqConnectionUserSetupPage.Run();
end;
}
}
area(Promoted)
{
actionref(Connect_Promoted; Connect)
{
}
}
}

var
LogiqAuth: Codeunit Auth;
[NonDebuggable]
ClientSecret: Text;

trigger OnOpenPage()
begin
if not Rec.Get() then begin
Rec.Init();
Rec.Insert(true);
end;

if this.LogiqAuth.HasToken(Rec."Client Secret", DataScope::Company) then
this.ClientSecret := '*';
end;
}
Loading
Loading