Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
2 changes: 1 addition & 1 deletion src/System Application/App/MicrosoftGraph/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"idRanges": [
{
"from": 9350,
"to": 9359
"to": 9361
}
],
"target": "OnPrem",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,51 @@ codeunit 9350 "Graph Client"
begin
exit(GraphClientImpl.Delete(RelativeUriToResource, GraphOptionalParameters, HttpResponseMessage));
end;

#region Pagination Support

/// <summary>
/// Get any request to the microsoft graph API with pagination support
/// </summary>
/// <remarks>Does not require UI interaction. This method handles pagination automatically.</remarks>
/// <param name="RelativeUriToResource">A relativ uri including the resource segments</param>
/// <param name="GraphOptionalParameters">A wrapper for optional header and query parameters</param>
/// <param name="GraphPaginationData">The pagination data object to track pagination state</param>
/// <param name="HttpResponseMessage">The response message object.</param>
/// <returns>True if the operation was successful; otherwise - false.</returns>
/// <error>Authentication failed.</error>
procedure GetWithPagination(RelativeUriToResource: Text; GraphOptionalParameters: Codeunit "Graph Optional Parameters"; var GraphPaginationData: Codeunit "Graph Pagination Data"; var HttpResponseMessage: Codeunit "Http Response Message"): Boolean
begin
exit(GraphClientImpl.GetWithPagination(RelativeUriToResource, GraphOptionalParameters, GraphPaginationData, HttpResponseMessage));
end;

/// <summary>
/// Get the next page of results using pagination data
/// </summary>
/// <remarks>Does not require UI interaction.</remarks>
/// <param name="GraphPaginationData">The pagination data object containing the next link</param>
/// <param name="HttpResponseMessage">The response message object.</param>
/// <returns>True if the operation was successful; otherwise - false.</returns>
/// <error>Authentication failed.</error>
procedure GetNextPage(var GraphPaginationData: Codeunit "Graph Pagination Data"; var HttpResponseMessage: Codeunit "Http Response Message"): Boolean
begin
exit(GraphClientImpl.GetNextPage(GraphPaginationData, HttpResponseMessage));
end;

/// <summary>
/// Get all pages of results automatically
/// </summary>
/// <remarks>Does not require UI interaction. This method fetches all pages automatically and returns the combined results.</remarks>
/// <param name="RelativeUriToResource">A relativ uri including the resource segments</param>
/// <param name="GraphOptionalParameters">A wrapper for optional header and query parameters</param>
/// <param name="HttpResponseMessage">The last response message object.</param>
/// <param name="JsonResults">A JSON array containing all results from all pages</param>
/// <returns>True if the operation was successful; otherwise - false.</returns>
/// <error>Authentication failed.</error>
procedure GetAllPages(RelativeUriToResource: Text; GraphOptionalParameters: Codeunit "Graph Optional Parameters"; var HttpResponseMessage: Codeunit "Http Response Message"; var JsonResults: JsonArray): Boolean
begin
exit(GraphClientImpl.GetAllPages(RelativeUriToResource, GraphOptionalParameters, HttpResponseMessage, JsonResults));
end;

#endregion
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,5 +99,63 @@ codeunit 9351 "Graph Client Impl."
exit(HttpResponseMessage.GetIsSuccessStatusCode());
end;

procedure GetWithPagination(RelativeUriToResource: Text; GraphOptionalParameters: Codeunit "Graph Optional Parameters"; var GraphPaginationData: Codeunit "Graph Pagination Data"; var HttpResponseMessage: Codeunit "Http Response Message"): Boolean
var
GraphPaginationHelper: Codeunit "Graph Pagination Helper";
begin
// Apply page size if set
GraphPaginationHelper.ApplyPageSize(GraphOptionalParameters, GraphPaginationData);

// Make the request
if not Get(RelativeUriToResource, GraphOptionalParameters, HttpResponseMessage) then
exit(false);

// Extract pagination data
GraphPaginationHelper.ExtractNextLink(HttpResponseMessage, GraphPaginationData);
exit(HttpResponseMessage.GetIsSuccessStatusCode());
end;

procedure GetNextPage(var GraphPaginationData: Codeunit "Graph Pagination Data"; var HttpResponseMessage: Codeunit "Http Response Message"): Boolean
var
GraphPaginationHelper: Codeunit "Graph Pagination Helper";
NextLink: Text;
begin
NextLink := GraphPaginationData.GetNextLink();

if NextLink = '' then
exit(false);

GraphRequestHelper.SetRestClient(RestClient);
HttpResponseMessage := GraphRequestHelper.GetByFullUrl(NextLink);

// Update pagination data
GraphPaginationHelper.ExtractNextLink(HttpResponseMessage, GraphPaginationData);
exit(HttpResponseMessage.GetIsSuccessStatusCode());
end;

procedure GetAllPages(RelativeUriToResource: Text; GraphOptionalParameters: Codeunit "Graph Optional Parameters"; var HttpResponseMessage: Codeunit "Http Response Message"; var JsonResults: JsonArray): Boolean
var
GraphPaginationHelper: Codeunit "Graph Pagination Helper";
GraphPaginationData: Codeunit "Graph Pagination Data";
IterationCount: Integer;
begin
// First request with pagination
if not GetWithPagination(RelativeUriToResource, GraphOptionalParameters, GraphPaginationData, HttpResponseMessage) then
exit(false);

// Process first page
GraphPaginationHelper.CombineValueArrays(HttpResponseMessage, JsonResults);

// Fetch remaining pages
while GraphPaginationData.HasMorePages() and GraphPaginationHelper.IsWithinIterationLimit(IterationCount, GraphPaginationHelper.GetMaxIterations()) do begin
if not GetNextPage(GraphPaginationData, HttpResponseMessage) then
exit(false);

GraphPaginationHelper.CombineValueArrays(HttpResponseMessage, JsonResults);
end;

exit(true);
end;

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// ------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// ------------------------------------------------------------------------------------------------
namespace System.Integration.Graph;

/// <summary>
/// Holder for pagination data when working with Microsoft Graph API responses.
/// </summary>
codeunit 9360 "Graph Pagination Data"
{
Access = Public;
InherentEntitlements = X;
InherentPermissions = X;

var
GraphPaginationDataImpl: Codeunit "Graph Pagination Data Impl.";

/// <summary>
/// Sets the next link URL for retrieving the next page of results.
/// </summary>
/// <param name="NewNextLink">The @odata.nextLink value from the Graph response.</param>
procedure SetNextLink(NewNextLink: Text)
begin
GraphPaginationDataImpl.SetNextLink(NewNextLink);
end;

/// <summary>
/// Gets the current next link URL.
/// </summary>
/// <returns>The URL to retrieve the next page of results.</returns>
procedure GetNextLink(): Text
begin
exit(GraphPaginationDataImpl.GetNextLink());
end;

/// <summary>
/// Checks if there are more pages available.
/// </summary>
/// <returns>True if more pages are available; otherwise false.</returns>
procedure HasMorePages(): Boolean
begin
exit(GraphPaginationDataImpl.HasMorePages());
end;

/// <summary>
/// Sets the page size for pagination requests.
/// </summary>
/// <param name="NewPageSize">The number of items to retrieve per page (max 999).</param>
procedure SetPageSize(NewPageSize: Integer)
begin
GraphPaginationDataImpl.SetPageSize(NewPageSize);
end;

/// <summary>
/// Gets the current page size.
/// </summary>
/// <returns>The number of items per page.</returns>
procedure GetPageSize(): Integer
begin
exit(GraphPaginationDataImpl.GetPageSize());
end;

/// <summary>
/// Gets the default page size.
/// </summary>
/// <returns>The default number of items per page.</returns>
procedure GetDefaultPageSize(): Integer
begin
exit(GraphPaginationDataImpl.GetDefaultPageSize());
end;

/// <summary>
/// Resets the pagination data to initial state.
/// </summary>
procedure Reset()
begin
GraphPaginationDataImpl.Reset();
end;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// ------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// ------------------------------------------------------------------------------------------------
namespace System.Integration.Graph;

codeunit 9361 "Graph Pagination Data Impl."
{
Access = Internal;
InherentEntitlements = X;
InherentPermissions = X;

var
NextLink: Text;
PageSize: Integer;
DefaultPageSizeErr: Label 'Page size must be between 1 and 999.';

procedure SetNextLink(NewNextLink: Text)
begin
NextLink := NewNextLink;
end;

procedure GetNextLink(): Text
begin
exit(NextLink);
end;

procedure HasMorePages(): Boolean
begin
exit(NextLink <> '');
end;

procedure SetPageSize(NewPageSize: Integer)
begin
if not (NewPageSize in [1 .. 999]) then
Error(DefaultPageSizeErr);

PageSize := NewPageSize;
end;

procedure GetPageSize(): Integer
begin
if PageSize = 0 then
exit(GetDefaultPageSize());

exit(PageSize);
end;

procedure Reset()
begin
Clear(NextLink);
Clear(PageSize);
end;

procedure GetDefaultPageSize(): Integer
begin
exit(100);
end;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// ------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// ------------------------------------------------------------------------------------------------
namespace System.Integration.Graph;

using System.RestClient;

codeunit 9359 "Graph Pagination Helper"
{
Access = Internal;
InherentEntitlements = X;
InherentPermissions = X;

procedure ExtractNextLink(HttpResponseMessage: Codeunit "Http Response Message"; var GraphPaginationData: Codeunit "Graph Pagination Data")
var
ResponseJson: JsonObject;
JsonToken: JsonToken;
NextLink: Text;
ResponseText: Text;
begin
if not HttpResponseMessage.GetIsSuccessStatusCode() then begin
GraphPaginationData.SetNextLink('');
exit;
end;

ResponseText := HttpResponseMessage.GetContent().AsText();

// Parse JSON response
if not ResponseJson.ReadFrom(ResponseText) then begin
GraphPaginationData.SetNextLink('');
exit;
end;

// Extract nextLink
if ResponseJson.Get('@odata.nextLink', JsonToken) then
NextLink := JsonToken.AsValue().AsText();

GraphPaginationData.SetNextLink(NextLink);
end;

procedure ExtractValueArray(HttpResponseMessage: Codeunit "Http Response Message"; var ValueArray: JsonArray): Boolean
var
ResponseJson: JsonObject;
JsonToken: JsonToken;
ResponseText: Text;
begin
Clear(ValueArray);

if not HttpResponseMessage.GetIsSuccessStatusCode() then
exit(false);

ResponseText := HttpResponseMessage.GetContent().AsText();

// Parse JSON response
if not ResponseJson.ReadFrom(ResponseText) then
exit(false);

// Extract value array
if not ResponseJson.Get('value', JsonToken) then
exit(false);

ValueArray := JsonToken.AsArray();
exit(true);
end;

procedure ApplyPageSize(var GraphOptionalParameters: Codeunit "Graph Optional Parameters"; GraphPaginationData: Codeunit "Graph Pagination Data")
begin
if GraphPaginationData.GetPageSize() > 0 then
GraphOptionalParameters.SetODataQueryParameter(Enum::"Graph OData Query Parameter"::top, Format(GraphPaginationData.GetPageSize()));
end;

procedure CombineValueArrays(HttpResponseMessage: Codeunit "Http Response Message"; var JsonResults: JsonArray): Boolean
var
ValueArray: JsonArray;
JsonItem: JsonToken;
begin
if not ExtractValueArray(HttpResponseMessage, ValueArray) then
exit(false);

foreach JsonItem in ValueArray do
JsonResults.Add(JsonItem);

exit(true);
end;

procedure IsWithinIterationLimit(var IterationCount: Integer; MaxIterations: Integer): Boolean
begin
if IterationCount >= MaxIterations then
exit(false);

IterationCount += 1;

exit(true);
end;

procedure GetMaxIterations(): Integer
begin
exit(1000); // Safety limit to prevent infinite loops
end;
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,9 @@ codeunit 9354 "Graph Request Helper"
PrepareRestClient(GraphOptionalParameters);
HttpResponseMessage := RestClient.Send(HttpMethod, GraphUriBuilder.GetUri(), HttpContent);
end;

procedure GetByFullUrl(FullUrl: Text) HttpResponseMessage: Codeunit "Http Response Message"
begin
HttpResponseMessage := RestClient.Get(FullUrl);
end;
}
Loading
Loading