With this post I would like to start a series of articles on third-party application integrations through the API for Business Central. An approximate list of articles will look like this:
table 52000 "NYT API Setup"
{
Caption = 'New York Times API Setup';
DataClassification = CustomerContent;
fields
{
field(10; "Primary Key"; Code[10])
{
Caption = 'Primary Key';
DataClassification = CustomerContent;
}
field(20; "Base URL"; Text[100])
{
Caption = 'Base URL';
DataClassification = CustomerContent;
}
}
keys
{
key(PK; "Primary Key")
{
Clustered = true;
}
}
procedure SetAPIKey(NewAPIKey: Text)
var
EncryptionManagement: Codeunit "Cryptography Management";
begin
if IsolatedStorage.Contains(GetStorageKey(), DataScope::Module) then
IsolatedStorage.Delete((GetStorageKey()));
if EncryptionManagement.IsEncryptionEnabled() and EncryptionManagement.IsEncryptionPossible() then
NewAPIKey := EncryptionManagement.Encrypt(NewAPIKey);
IsolatedStorage.set(GetStorageKey(), NewAPIKey, DataScope::Module);
end;
procedure GetAPIKey(): Text
var
EncryptionManagement: Codeunit "Cryptography Management";
APIKey: Text;
begin
if IsolatedStorage.Contains(GetStorageKey(), DataScope::Module) then begin
IsolatedStorage.Get(GetStorageKey(), DataScope::Module, APIKey);
if EncryptionManagement.IsEncryptionEnabled() and EncryptionManagement.IsEncryptionPossible() then
APIKey := EncryptionManagement.Decrypt(APIKey);
exit(APIKey);
end;
end;
local procedure GetStorageKey(): Text
begin
exit(SystemId);
end;
}
page 52000 "NYT API Setup"
{
Caption = 'New York Times API Setup';
PageType = Card;
SourceTable = "NYT API Setup";
UsageCategory = Administration;
ApplicationArea = all;
layout
{
area(content)
{
group(General)
{
field("Base URL"; "Base URL")
{
ApplicationArea = All;
ToolTip = 'Specifies the Base URL';
}
field(APIKey; APIKey)
{
ApplicationArea = All;
Caption = 'API Key';
ToolTip = 'Specifies the API Key';
ExtendedDatatype = Masked;
trigger OnValidate()
begin
SetAPIKey(APIKey);
end;
}
}
}
}
trigger OnOpenPage()
begin
if not Get() then begin
Init();
Insert();
end;
if GetAPIKey() <> '' then
APIKey := '****';
end;
var
APIKey: Text;
}
table 52001 "NYT Best Sellers Theme"
{
Caption = 'Best Sellers List of Themes';
DataClassification = CustomerContent;
fields
{
field(10; "List Name"; Text[250])
{
Caption = 'List Name';
DataClassification = CustomerContent;
}
field(20; "Oldest Published Date"; Date)
{
Caption = 'Oldest Published Date';
DataClassification = CustomerContent;
}
field(30; "Newest Published Date"; Date)
{
Caption = 'Newest Published Date';
DataClassification = CustomerContent;
}
field(40; Updated; Text[30])
{
Caption = 'Updated';
DataClassification = CustomerContent;
}
field(50; "List Name Encoded"; Text[250])
{
Caption = 'List Name Encoded';
DataClassification = CustomerContent;
}
}
keys
{
key(PK; "List Name")
{
Clustered = true;
}
}
trigger OnDelete()
var
NYTBestSellers: Record "NYT Best Sellers";
begin
NYTBestSellers.SetRange("List Name", Rec."List Name");
if not NYTBestSellers.IsEmpty() then
NYTBestSellers.DeleteAll();
end;
}
table 52002 "NYT Best Sellers"
{
Caption = 'Best Sellers Book';
DataClassification = CustomerContent;
fields
{
field(10; "List Name"; Text[250])
{
Caption = 'List Name';
DataClassification = CustomerContent;
}
field(20; "Line No."; BigInteger)
{
Caption = 'Line No.';
DataClassification = CustomerContent;
}
field(30; "Book Title"; Text[250])
{
Caption = 'Book Title';
DataClassification = CustomerContent;
}
field(40; "Book Description"; Text[2048])
{
Caption = 'Book Description';
DataClassification = CustomerContent;
}
field(50; "Book Author"; Text[250])
{
Caption = 'Book Author';
DataClassification = CustomerContent;
}
field(60; "Amazon URL"; Text[250])
{
Caption = 'Amazon URL';
DataClassification = CustomerContent;
ExtendedDatatype = URL;
}
}
keys
{
key(PK; "List Name", "Line No.")
{
Clustered = true;
}
}
trigger OnInsert()
begin
"Line No." := GetNextLineNo();
end;
procedure GetNextLineNo(): BigInteger
var
NYTBestSellers: Record "NYT Best Sellers";
begin
NYTBestSellers.SetRange("List Name", "List Name");
if NYTBestSellers.FindLast() then
exit(NYTBestSellers."Line No." + 1);
exit(1);
end;
}
procedure GetRequest(AdditionalURL: Text; var Data: Text; var httpStatusCode: Integer): Boolean
var
NYAPISetup: Record "NYT API Setup";
httpClient: HttpClient;
httpResponseMessage: HttpResponseMessage;
requestUri: Text;
begin
NYAPISetup.get();
requestUri := NYAPISetup."Base URL" + AdditionalURL + 'api-key=' + NYAPISetup.GetAPIKey();
httpClient.Get(requestUri, httpResponseMessage);
httpResponseMessage.Content().ReadAs(Data);
httpStatusCode := httpResponseMessage.HttpStatusCode();
if not httpResponseMessage.IsSuccessStatusCode() then
Error(RequestErr, httpStatusCode, Data);
exit(true);
end;
codeunit 52001 "NYT JSON Mgt"
{
procedure SelectJsonToken(JObject: JsonObject; Path: Text): Text
var
JToken: JsonToken;
begin
if JObject.SelectToken(Path, JToken) then
if NOT JToken.AsValue().IsNull() then
exit(JToken.AsValue().AsText());
end;
procedure GetValueAsText(JToken: JsonToken; ParamString: Text): Text
var
JObject: JsonObject;
begin
JObject := JToken.AsObject();
exit(SelectJsonToken(JObject, ParamString));
end;
local procedure EvaluateUTCDateTime(DataTimeText: Text) EvaluatedDateTime: DateTime;
var
TypeHelper: Codeunit "Type Helper";
ValueTest: Variant;
begin
ValueTest := EvaluatedDateTime;
IF TypeHelper.Evaluate(ValueTest, DataTimeText, '', TypeHelper.GetCultureName()) THEN
EvaluatedDateTime := ValueTest;
end;
procedure UpdateBestSellersTheme(Data: text)
var
NYTBestSellersTheme: Record "NYT Best Sellers Theme";
JToken: JsonToken;
JObject: JsonObject;
JArray: JsonArray;
begin
if Data = '' then
exit;
JToken.ReadFrom(Data);
JObject := JToken.AsObject();
JObject.SelectToken('results', JToken);
JArray := JToken.AsArray();
foreach JToken in JArray do begin
NYTBestSellersTheme.Init();
NYTBestSellersTheme."List Name" := CopyStr(GetValueAsText(JToken, 'list_name'), 1, MaxStrLen(NYTBestSellersTheme."List Name"));
NYTBestSellersTheme."List Name Encoded" := CopyStr(GetValueAsText(JToken, 'list_name_encoded'), 1, MaxStrLen(NYTBestSellersTheme."List Name Encoded"));
NYTBestSellersTheme.Updated := CopyStr(GetValueAsText(JToken, 'updated'), 1, MaxStrLen(NYTBestSellersTheme.Updated));
NYTBestSellersTheme."Newest Published Date" := DT2Date(EvaluateUTCDateTime(GetValueAsText(JToken, 'newest_published_date')));
NYTBestSellersTheme."Oldest Published Date" := DT2Date(EvaluateUTCDateTime(GetValueAsText(JToken, 'oldest_published_date')));
NYTBestSellersTheme.Insert();
end;
end;
procedure UpdateBestSeller(Data: Text)
var
NYTBestSellers: Record "NYT Best Sellers";
JToken: JsonToken;
JToken2: JsonToken;
JObject: JsonObject;
JObject2: JsonObject;
JArray: JsonArray;
JArray2: JsonArray;
begin
if Data = '' then
exit;
JToken.ReadFrom(Data);
JObject := JToken.AsObject();
JObject.SelectToken('results', JToken);
JArray := JToken.AsArray();
foreach JToken in JArray do begin
NYTBestSellers.Init();
NYTBestSellers."List Name" := CopyStr(GetValueAsText(JToken, 'list_name'), 1, MaxStrLen(NYTBestSellers."List Name"));
NYTBestSellers."Amazon URL" := CopyStr(GetValueAsText(JToken, 'amazon_product_url'), 1, MaxStrLen(NYTBestSellers."Amazon URL"));
JObject2 := JToken.AsObject();
if JObject2.SelectToken('book_details', Jtoken2) then begin
JArray2 := JToken2.AsArray();
foreach JToken2 in JArray2 do begin
NYTBestSellers."Book Title" := CopyStr(GetValueAsText(JToken2, 'title'), 1, MaxStrLen(NYTBestSellers."Book Title"));
NYTBestSellers."Book Description" := CopyStr(GetValueAsText(JToken2, 'description'), 1, MaxStrLen(NYTBestSellers."Book Title"));
NYTBestSellers."Book Author" := CopyStr(GetValueAsText(JToken2, 'author'), 1, MaxStrLen(NYTBestSellers."Book Author"));
end;
end;
NYTBestSellers.Insert(true);
end;
end;
}
procedure SyncBookAPIData()
var
NYTBestSellerTheme: Record "NYT Best Sellers Theme";
NYTJsonMgt: Codeunit "NYT JSON Mgt";
Window: Dialog;
AddUrl: Text;
HttpStatusCode: Integer;
RecCounter: Integer;
Data: Text;
begin
NYTBestSellerTheme.DeleteAll(true);
AddUrl := '/lists/names.json?';
GetRequest(AddUrl, Data, HttpStatusCode);
NYTJsonMgt.UpdateBestSellersTheme(Data);
Window.OPEN('Processing: @1@@@@@@@@@@@@@@@');
if NYTBestSellerTheme.FindSet() then
repeat
RecCounter += 1;
Window.UPDATE(1, ROUND(RecCounter / NYTBestSellerTheme.Count() * 10000, 1));
AddUrl := StrSubstNo('/lists.json?list=%1&', NYTBestSellerTheme."List Name Encoded");
GetRequest(AddUrl, Data, HttpStatusCode);
NYTJsonMgt.UpdateBestSeller(Data);
Commit();
Sleep(7000); // To avoid New York Time API request limit
until NYTBestSellerTheme.Next() = 0;
Window.CLOSE();
end;