Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
84 changes: 50 additions & 34 deletions src/Layers/W1/BaseApp/System/API/APIOverview.Page.al
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Microsoft.API;

using System.Reflection;
using System.Integration;

page 812 "API Overview"
{
Expand Down Expand Up @@ -33,7 +33,7 @@ page 812 "API Overview"
field("Object Type"; Rec."Object Type")
{
Caption = 'Type';
ToolTip = 'Specifies whether the API is implemented as an API page (supports read and write operations) or as an API query (read-only set of data).';
ToolTip = 'Specifies whether the API is implemented as an API page (read and write operations), an API query (read-only set of data), or an API codeunit (operations exposed as unbound actions).';
}
field("Object ID"; Rec."Object ID")
{
Expand Down Expand Up @@ -83,6 +83,11 @@ page 812 "API Overview"
Caption = 'API Queries';
Filters = where("Object Type" = const(Query));
}
view("API Codeunits")
{
Caption = 'API Codeunits';
Filters = where("Object Type" = const(Codeunit));
}
view(APIv2)
{
Caption = 'API v2.0';
Expand All @@ -108,46 +113,25 @@ page 812 "API Overview"
local procedure LoadAPIs()
var
TempAPILine: Record "API Overview Buffer" temporary;
PageMetadata: Record "Page Metadata";
QueryMetadata: Record "Query Metadata";
ApiWebService: Record "Api Web Service";
LineNo: Integer;
EntryNo: Integer;
begin
Rec.Reset();
Rec.DeleteAll();

PageMetadata.SetRange(PageType, PageMetadata.PageType::API);
if PageMetadata.FindSet() then
repeat
LineNo += 1;
TempAPILine.Init();
TempAPILine."Entry No." := LineNo;
TempAPILine."Object Type" := TempAPILine."Object Type"::Page;
TempAPILine."Object ID" := PageMetadata.ID;
TempAPILine.Description := PageMetadata.Name;
TempAPILine."Entity Name" := PageMetadata.EntityName;
TempAPILine."API Publisher" := PageMetadata.APIPublisher;
TempAPILine."API Group" := PageMetadata.APIGroup;
TempAPILine."API Version" := PageMetadata.APIVersion;
TempAPILine.Insert();
until PageMetadata.Next() = 0;
ApiWebService.SetRange(Published, true);

QueryMetadata.SetFilter(EntityName, '<>%1', '');
if QueryMetadata.FindSet() then
repeat
LineNo += 1;
TempAPILine.Init();
TempAPILine."Entry No." := LineNo;
TempAPILine."Object Type" := TempAPILine."Object Type"::Query;
TempAPILine."Object ID" := QueryMetadata.ID;
TempAPILine.Description := QueryMetadata.Name;
TempAPILine."Entity Name" := QueryMetadata.EntityName;
TempAPILine."API Publisher" := QueryMetadata.APIPublisher;
TempAPILine."API Group" := QueryMetadata.APIGroup;
TempAPILine."API Version" := QueryMetadata.APIVersion;
TempAPILine.Insert();
until QueryMetadata.Next() = 0;
ApiWebService.SetRange("Object Type", ApiWebService."Object Type"::Page);
AddAPIObjects(ApiWebService, TempAPILine, TempAPILine."Object Type"::Page, LineNo);

ApiWebService.SetRange("Object Type", ApiWebService."Object Type"::Query);
AddAPIObjects(ApiWebService, TempAPILine, TempAPILine."Object Type"::Query, LineNo);

ApiWebService.SetRange("Object Type", ApiWebService."Object Type"::Codeunit);
AddAPIObjects(ApiWebService, TempAPILine, TempAPILine."Object Type"::Codeunit, LineNo);

TempAPILine.Reset();
TempAPILine.SetCurrentKey("API Publisher", "API Group", Description);
if TempAPILine.FindSet() then
repeat
Expand All @@ -160,13 +144,45 @@ page 812 "API Overview"
if Rec.FindFirst() then;
end;

local procedure AddAPIObjects(var ApiWebService: Record "Api Web Service"; var TempAPILine: Record "API Overview Buffer" temporary; BufferObjectType: Option; var LineNo: Integer)
begin
if not ApiWebService.FindSet() then
Comment thread
onbuyuka marked this conversation as resolved.
exit;

repeat
TempAPILine.Reset();
TempAPILine.SetRange("Object Type", BufferObjectType);
TempAPILine.SetRange("Object ID", ApiWebService."Object ID");
if TempAPILine.FindFirst() then begin
TempAPILine."API Version" := CopyStr(TempAPILine."API Version" + ',' + ApiWebService.Version, 1, MaxStrLen(TempAPILine."API Version"));
TempAPILine.Modify();
end else begin
LineNo += 1;
TempAPILine.Init();
TempAPILine."Entry No." := LineNo;
TempAPILine."Object Type" := BufferObjectType;
TempAPILine."Object ID" := ApiWebService."Object ID";
TempAPILine.Description := CopyStr(ApiWebService."Object Name", 1, MaxStrLen(TempAPILine.Description));
TempAPILine."Entity Name" := CopyStr(ApiWebService."Service Name", 1, MaxStrLen(TempAPILine."Entity Name"));
TempAPILine."API Publisher" := CopyStr(ApiWebService.Publisher, 1, MaxStrLen(TempAPILine."API Publisher"));
TempAPILine."API Group" := CopyStr(ApiWebService.Group, 1, MaxStrLen(TempAPILine."API Group"));
TempAPILine."API Version" := CopyStr(ApiWebService.Version, 1, MaxStrLen(TempAPILine."API Version"));
TempAPILine.Insert();
end;
until ApiWebService.Next() = 0;
end;

local procedure GetApiUrl(APIBuffer: Record "API Overview Buffer"): Text
begin
case APIBuffer."Object Type" of
APIBuffer."Object Type"::Page:
exit(GetUrl(ClientType::Api, CompanyName(), ObjectType::Page, APIBuffer."Object ID"));
APIBuffer."Object Type"::Query:
exit(GetUrl(ClientType::Api, CompanyName(), ObjectType::Query, APIBuffer."Object ID"));
APIBuffer."Object Type"::Codeunit:
// API codeunits expose one endpoint per procedure (unbound action), so there is no single
// URL at the codeunit level; the URL column is left blank for codeunit rows.
exit('');
end;
end;
}
4 changes: 2 additions & 2 deletions src/Layers/W1/BaseApp/System/API/APIOverviewBuffer.Table.al
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ table 812 "API Overview Buffer"
field(3; "Object Type"; Option)
{
Caption = 'Type';
OptionMembers = ,Page,Query;
OptionCaption = ' ,Page,Query';
OptionMembers = ,Page,Query,Codeunit;
OptionCaption = ' ,Page,Query,Codeunit';
}
field(4; "Object ID"; Integer)
{
Expand Down
22 changes: 15 additions & 7 deletions src/Layers/W1/Tests/Integration/APIOverviewTest.Codeunit.al
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ codeunit 139103 "API Overview Test"
Assert.AreEqual('microsoft', APIOverview."API Publisher".Value(), 'Unexpected API publisher for Posted Sales Invoice API');
Assert.AreEqual('automate', APIOverview."API Group".Value(), 'Unexpected API group for Posted Sales Invoice API');
Assert.AreEqual('v1.0', APIOverview."API Version".Value(), 'Unexpected API version for Posted Sales Invoice API');
Assert.AreEqual('postedSalesInvoice', APIOverview."Entity Name".Value(), 'Unexpected entity name for Posted Sales Invoice API');
Assert.AreEqual('postedSalesInvoices', APIOverview."Entity Name".Value(), 'Unexpected entity name for Posted Sales Invoice API');
Assert.IsTrue(APIOverview."API URL".Value().Contains('/api/microsoft/automate/v1.0/'), 'Unexpected API URL for Posted Sales Invoice API');
APIOverview.Close();
end;
Expand Down Expand Up @@ -121,12 +121,13 @@ codeunit 139103 "API Overview Test"
[Scope('OnPrem')]
procedure TestAPIOverviewListsEveryAPIPage()
var
ApiWebService: Record "Api Web Service";
APIOverview: TestPage "API Overview";
PageMetadata: Record "Page Metadata";
ObjectIds: List of [Integer];
APIPageCount: Integer;
DetailPageCount: Integer;
begin
// [SCENARIO] Every API page in metadata appears as a row in the flat list (no APIs are lost)
// [SCENARIO] Every published API page appears as exactly one row in the flat list (no APIs are lost)
// [GIVEN] The API Overview page is opened
APIOverview.OpenView();

Expand All @@ -139,9 +140,16 @@ codeunit 139103 "API Overview Test"

APIOverview.Close();

// [THEN] Count matches the underlying Page Metadata where PageType = API
PageMetadata.SetRange(PageType, PageMetadata.PageType::API);
APIPageCount := PageMetadata.Count();
Assert.AreEqual(APIPageCount, DetailPageCount, 'API Overview did not list every API page from Page Metadata');
// [THEN] Count matches the distinct published API pages in the Api Web Service table
// (a page published under several versions is a single row, so distinct object IDs are counted)
ApiWebService.SetRange("Object Type", ApiWebService."Object Type"::Page);
ApiWebService.SetRange(Published, true);
if ApiWebService.FindSet() then
repeat
if not ObjectIds.Contains(ApiWebService."Object ID") then
ObjectIds.Add(ApiWebService."Object ID");
until ApiWebService.Next() = 0;
APIPageCount := ObjectIds.Count();
Assert.AreEqual(APIPageCount, DetailPageCount, 'API Overview did not list every published API page from Api Web Service');
end;
}
Original file line number Diff line number Diff line change
Expand Up @@ -191,14 +191,25 @@ codeunit 8350 "MCP Config"
exit(MCPConfigImplementation.CreateAPIQueryTool(ConfigId, QueryId));
end;

/// <summary>
/// Creates a new API tool for the specified configuration and codeunit.
/// </summary>
/// <param name="ConfigId">The SystemId (GUID) of the configuration.</param>
/// <param name="CodeunitId">The ID of the codeunit.</param>
/// <returns>The SystemId (GUID) of the created tool.</returns>
procedure CreateCodeunitAPITool(ConfigId: Guid; CodeunitId: Integer): Guid
begin
exit(MCPConfigImplementation.CreateAPICodeunitTool(ConfigId, CodeunitId));
end;

/// <summary>
/// Retrieves the SystemId (GUID) of a tool by its configuration ID, object ID and object type.
/// </summary>
/// <param name="ConfigId">The SystemId (GUID) of the configuration.</param>
/// <param name="ObjectId">The ID of the API page or query.</param>
/// <param name="ObjectType">The object type (Page or Query).</param>
/// <param name="ObjectType">The object type (Page, Query or Codeunit).</param>
/// <returns>The SystemId (GUID) of the tool if found; otherwise, an empty GUID.</returns>
procedure GetAPIToolId(ConfigId: Guid; ObjectId: Integer; ObjectType: Option Page,Query): Guid
procedure GetAPIToolId(ConfigId: Guid; ObjectId: Integer; ObjectType: Option Page,Query,Codeunit): Guid
Comment thread
onbuyuka marked this conversation as resolved.
begin
exit(MCPConfigImplementation.GetAPIToolId(ConfigId, ObjectId, ObjectType));
end;
Expand Down Expand Up @@ -268,15 +279,29 @@ codeunit 8350 "MCP Config"
MCPConfigImplementation.AllowDelete(ToolSystemId, Allow);
end;

/// <summary>
/// Sets the actions permission for the specified tool. For API pages this controls bound actions;
/// for codeunit tools this controls whether the codeunit action can be invoked.
/// </summary>
/// <param name="ToolSystemId">The SystemId (GUID) of the tool.</param>
/// <param name="Allow">True to allow actions, false to disallow.</param>
procedure AllowActions(ToolSystemId: Guid; Allow: Boolean)
begin
MCPConfigImplementation.AllowActions(ToolSystemId, Allow);
end;

#if not CLEAN29
/// <summary>
/// Sets the bound actions permission for the specified tool.
/// </summary>
/// <param name="ToolSystemId">The SystemId (GUID) of the tool.</param>
/// <param name="Allow">True to allow bound actions, false to disallow.</param>
[Obsolete('Renamed to AllowActions.', '29.0')]
procedure AllowBoundActions(ToolSystemId: Guid; Allow: Boolean)
begin
MCPConfigImplementation.AllowBoundActions(ToolSystemId, Allow);
MCPConfigImplementation.AllowActions(ToolSystemId, Allow);
end;
#endif

/// <summary>
/// Creates a new MCP Entra Application with the specified name, description, and client ID.
Expand Down
Loading
Loading