Codeunit 9120 SharePoint Graph Client Impl. in 28
- App
- System Application
- Namespace
- System.Integration.Sharepoint
Versions171819202122232425262728latest
Source2829
Source in 28
src/System Application/App/SharePoint/src/graph/SharePointGraphClientImpl.Codeunit.al2095 lines, Copyright (c) Microsoft Corporation. MIT
// ------------------------------------------------------------------------------------------------
// 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.Sharepoint;
using System.Integration.Graph;
using System.Integration.Graph.Authorization;
using System.RestClient;
using System.Utilities;
/// <summary>
/// Provides functionality for interacting with SharePoint through Microsoft Graph API.
/// This implementation uses native Graph API concepts and models.
/// </summary>
codeunit 9120 "SharePoint Graph Client Impl."
{
Access = Internal;
InherentEntitlements = X;
InherentPermissions = X;
var
SharePointGraphRequestHelper: Codeunit "SharePoint Graph Req. Helper";
SharePointGraphParser: Codeunit "Sharepoint Graph Parser";
SharePointGraphUriBuilder: Codeunit "Sharepoint Graph Uri Builder";
SiteId: Text;
SharePointUrl: Text;
DefaultDriveId: Text;
IsInitialized: Boolean;
NotInitializedErr: Label 'SharePoint Graph Client is not initialized. Call Initialize first.';
InvalidSharePointUrlErr: Label 'Invalid SharePoint URL ''%1''.', Comment = '%1 = URL string';
SharePointUrlPatternLbl: Label '^https:\/\/[a-zA-Z0-9][a-zA-Z0-9\-]*\.sharepoint\.com(\/.*)?$', Locked = true;
RetrieveSiteInfoErr: Label 'Failed to retrieve SharePoint site information from Graph API. %1', Comment = '%1 = Error message';
ContentRangeHeaderLbl: Label 'bytes %1-%2/%3', Locked = true, Comment = '%1 = Start Bytes, %2 = End Bytes, %3 = Total Bytes';
FailedToRetrieveListsErr: Label 'Failed to retrieve lists: %1', Comment = '%1 = Error message';
FailedToParseListsErr: Label 'Failed to parse lists collection from response';
FailedToRetrieveListErr: Label 'Failed to retrieve list: %1', Comment = '%1 = Error message';
FailedToParseListErr: Label 'Failed to parse list details from response';
InvalidListIdErr: Label 'List ID cannot be empty';
InvalidDisplayNameErr: Label 'Display name cannot be empty';
FailedToCreateListErr: Label 'Failed to create list: %1', Comment = '%1 = Error message';
FailedToParseCreatedListErr: Label 'Failed to parse created list details from response';
FailedToRetrieveListItemsErr: Label 'Failed to retrieve list items: %1', Comment = '%1 = Error message';
FailedToParseListItemsErr: Label 'Failed to parse list items collection from response';
FailedToCreateListItemErr: Label 'Failed to create list item: %1', Comment = '%1 = Error message';
FailedToParseCreatedListItemErr: Label 'Failed to parse created list item details from response';
NoDefaultDriveIdErr: Label 'Default drive ID is not available. Please check the SharePoint site.';
FailedToRetrieveDefaultDriveErr: Label 'Failed to retrieve default drive: %1', Comment = '%1 = Error message';
FailedToRetrieveDrivesErr: Label 'Failed to retrieve drives: %1', Comment = '%1 = Error message';
FailedToParseDrivesErr: Label 'Failed to parse drives collection from response';
FailedToRetrieveDriveErr: Label 'Failed to retrieve drive: %1', Comment = '%1 = Error message';
FailedToParseDriveErr: Label 'Failed to parse drive details from response';
InvalidDriveIdErr: Label 'Drive ID cannot be empty';
FailedToRetrieveRootItemsErr: Label 'Failed to retrieve root items: %1', Comment = '%1 = Error message';
FailedToParseRootItemsErr: Label 'Failed to parse root items collection from response';
InvalidFolderIdErr: Label 'Folder ID cannot be empty';
FailedToRetrieveFolderItemsErr: Label 'Failed to retrieve folder items: %1', Comment = '%1 = Error message';
FailedToParseFolderItemsErr: Label 'Failed to parse folder items collection from response';
FailedToRetrieveItemsByPathErr: Label 'Failed to retrieve items by path: %1', Comment = '%1 = Error message';
FailedToParseItemsByPathErr: Label 'Failed to parse items collection from response';
InvalidItemIdErr: Label 'Item ID cannot be empty';
FailedToRetrieveDriveItemErr: Label 'Failed to retrieve drive item: %1', Comment = '%1 = Error message';
FailedToParseDriveItemErr: Label 'Failed to parse drive item details from response';
InvalidItemPathErr: Label 'Item path cannot be empty';
FailedToRetrieveDriveItemByPathErr: Label 'Failed to retrieve drive item by path: %1', Comment = '%1 = Error message';
FailedToParseDriveItemByPathErr: Label 'Failed to parse drive item details from response';
InvalidFolderNameErr: Label 'Folder name cannot be empty';
FailedToCreateFolderErr: Label 'Failed to create folder: %1', Comment = '%1 = Error message';
FailedToParseCreatedFolderErr: Label 'Failed to parse created folder details from response';
InvalidFileNameErr: Label 'File name cannot be empty';
FailedToUploadFileErr: Label 'Failed to upload file: %1', Comment = '%1 = Error message';
FailedToParseUploadedFileErr: Label 'Failed to parse uploaded file details from response';
InvalidFileSizeErr: Label 'File size must be greater than 0';
FailedToCreateUploadSessionErr: Label 'Failed to create upload session: %1', Comment = '%1 = Error message';
FailedToUploadChunkErr: Label 'Failed to upload file chunk: %1', Comment = '%1 = Error message';
NoUploadResponseErr: Label 'No response received from chunked upload';
FailedToParseChunkedUploadErr: Label 'Failed to parse chunked upload response';
FailedToDownloadFileErr: Label 'Failed to download file: %1', Comment = '%1 = Error message';
InvalidFilePathErr: Label 'File path cannot be empty';
FailedToDownloadFileByPathErr: Label 'Failed to download file by path: %1', Comment = '%1 = Error message';
FailedToDownloadChunkErr: Label 'Failed to download file chunk %1-%2: %3', Comment = '%1 = Start byte, %2 = End byte, %3 = Error message';
FailedToGetFileSizeErr: Label 'Failed to get file size for chunked download: %1', Comment = '%1 = Error message';
FailedToDeleteItemErr: Label 'Failed to delete item: %1', Comment = '%1 = Error message';
FailedToDeleteItemByPathErr: Label 'Failed to delete item by path: %1', Comment = '%1 = Error message';
InvalidTargetPathErr: Label 'Target path cannot be empty';
FailedToCopyItemErr: Label 'Failed to copy item: %1', Comment = '%1 = Error message';
FailedToMoveItemErr: Label 'Failed to move item: %1', Comment = '%1 = Error message';
GraphSharePointCategoryLbl: Label 'AL Graph SharePoint', Locked = true;
OperationSuccessTelemetryMsg: Label '%1 completed successfully.', Locked = true, Comment = '%1 = Operation name';
#region Initialization
/// <summary>
/// Initializes SharePoint Graph client.
/// </summary>
/// <param name="NewSharePointUrl">SharePoint site URL.</param>
/// <param name="GraphAuthorization">The Graph API authorization to use.</param>
procedure Initialize(NewSharePointUrl: Text; GraphAuthorization: Interface "Graph Authorization")
begin
SharePointGraphRequestHelper.Initialize(GraphAuthorization);
InitializeCommon(NewSharePointUrl);
end;
/// <summary>
/// Initializes SharePoint Graph client with a specific API version.
/// </summary>
/// <param name="NewSharePointUrl">SharePoint site URL.</param>
/// <param name="ApiVersion">The Graph API version to use.</param>
/// <param name="GraphAuthorization">The Graph API authorization to use.</param>
procedure Initialize(NewSharePointUrl: Text; ApiVersion: Enum "Graph API Version"; GraphAuthorization: Interface "Graph Authorization")
begin
SharePointGraphRequestHelper.Initialize(ApiVersion, GraphAuthorization);
InitializeCommon(NewSharePointUrl);
end;
/// <summary>
/// Initializes SharePoint Graph client with a custom base URL.
/// </summary>
/// <param name="NewSharePointUrl">SharePoint site URL.</param>
/// <param name="BaseUrl">The custom base URL for Graph API.</param>
/// <param name="GraphAuthorization">The Graph API authorization to use.</param>
procedure Initialize(NewSharePointUrl: Text; BaseUrl: Text; GraphAuthorization: Interface "Graph Authorization")
begin
SharePointGraphRequestHelper.Initialize(BaseUrl, GraphAuthorization);
InitializeCommon(NewSharePointUrl);
end;
/// <summary>
/// Initializes SharePoint Graph client with an HTTP client handler for testing.
/// </summary>
/// <param name="NewSharePointUrl">SharePoint site URL.</param>
/// <param name="ApiVersion">The Graph API version to use.</param>
/// <param name="GraphAuthorization">The Graph API authorization to use.</param>
/// <param name="HttpClientHandler">HTTP client handler for intercepting requests.</param>
procedure Initialize(NewSharePointUrl: Text; ApiVersion: Enum "Graph API Version"; GraphAuthorization: Interface "Graph Authorization"; HttpClientHandler: Interface "Http Client Handler")
begin
SharePointGraphRequestHelper.Initialize(ApiVersion, GraphAuthorization, HttpClientHandler);
InitializeCommon(NewSharePointUrl);
end;
/// <summary>
/// Common initialization logic shared by all Initialize overloads.
/// </summary>
/// <param name="NewSharePointUrl">SharePoint site URL.</param>
local procedure InitializeCommon(NewSharePointUrl: Text)
var
Uri: Codeunit Uri;
begin
// Validate that the URL points to a SharePoint domain to prevent sending credentials to external URLs
if not Uri.IsValidURIPattern(NewSharePointUrl, SharePointUrlPatternLbl) then begin
Session.LogMessage('0000RNN', StrSubstNo(InvalidSharePointUrlErr, NewSharePointUrl), Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
Error(InvalidSharePointUrlErr, NewSharePointUrl);
end;
// If we have a new URL, clear the cached IDs so they'll be re-acquired
if SharePointUrl <> NewSharePointUrl then begin
SharePointUrl := NewSharePointUrl;
SiteId := '';
DefaultDriveId := '';
end;
IsInitialized := true;
end;
local procedure GetSiteIdFromUrl(Url: Text)
var
JsonResponse: JsonObject;
JsonToken: JsonToken;
HostName: Text;
RelativePath: Text;
Endpoint: Text;
begin
// Extract hostname and relative path from the URL
HostName := ExtractHostName(Url);
RelativePath := ExtractRelativePath(Url);
if (HostName = '') or (RelativePath = '') then begin
Session.LogMessage('0000RK6', StrSubstNo(InvalidSharePointUrlErr, Url), Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
Error(InvalidSharePointUrlErr, Url);
end;
// Build the Graph endpoint to get site information
Endpoint := SharePointGraphUriBuilder.GetSiteByHostAndPathEndpoint(HostName, RelativePath);
if not SharePointGraphRequestHelper.Get(Endpoint, JsonResponse) then begin
Session.LogMessage('0000RK7', StrSubstNo(RetrieveSiteInfoErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase()), Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
Error(RetrieveSiteInfoErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
end;
if JsonResponse.Get('id', JsonToken) then
SiteId := JsonToken.AsValue().AsText();
end;
local procedure GetDefaultDriveId()
var
JsonResponse: JsonObject;
JsonToken: JsonToken;
begin
if SiteId = '' then
exit;
if not SharePointGraphRequestHelper.Get(SharePointGraphUriBuilder.GetDriveEndpoint(), JsonResponse) then
exit;
if JsonResponse.Get('id', JsonToken) then
DefaultDriveId := JsonToken.AsValue().AsText();
end;
local procedure ExtractHostName(Url: Text): Text
var
UriBuilder: Codeunit "Uri Builder";
begin
UriBuilder.Init(Url);
exit(UriBuilder.GetHost()); // e.g. contoso.sharepoint.com
end;
local procedure ExtractRelativePath(Url: Text): Text
var
UriBuilder: Codeunit "Uri Builder";
Path: Text;
begin
UriBuilder.Init(Url);
// Azure AD / Graph requires the path to start with '/'
Path := UriBuilder.GetPath();
// Guarantee at least '/'
if Path = '' then
Path := '/';
exit(Path);
end;
local procedure EnsureInitialized()
begin
if not IsInitialized then begin
Session.LogMessage('0000RK8', NotInitializedErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
Error(NotInitializedErr);
end;
end;
#endregion
#region Lists
/// <summary>
/// Gets all lists from the SharePoint site.
/// </summary>
/// <param name="GraphLists">Collection of the result (temporary record).</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure GetLists(var GraphLists: Record "SharePoint Graph List" temporary): Codeunit "SharePoint Graph Response"
var
GraphOptionalParameters: Codeunit "Graph Optional Parameters";
begin
exit(GetLists(GraphLists, GraphOptionalParameters));
end;
/// <summary>
/// Gets all lists from the SharePoint site.
/// </summary>
/// <param name="GraphLists">Collection of the result (temporary record).</param>
/// <param name="GraphOptionalParameters">A wrapper for optional header and query parameters</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure GetLists(var GraphLists: Record "SharePoint Graph List" temporary; GraphOptionalParameters: Codeunit "Graph Optional Parameters"): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
JsonArray: JsonArray;
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Use Graph pagination to get all pages automatically
if not SharePointGraphRequestHelper.GetAllPages(SharePointGraphUriBuilder.GetListsEndpoint(), GraphOptionalParameters, JsonArray) then begin
ErrorMessage := StrSubstNo(FailedToRetrieveListsErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RK9', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Parse the combined results from all pages
if not SharePointGraphParser.ParseListCollection(JsonArray, GraphLists) then begin
SharePointGraphResponse.SetError(FailedToParseListsErr);
Session.LogMessage('0000RKA', FailedToParseListsErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RKB', StrSubstNo(OperationSuccessTelemetryMsg, 'GetLists'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Gets a SharePoint list by ID.
/// </summary>
/// <param name="ListId">ID of the list to retrieve.</param>
/// <param name="GraphList">Record to store the result.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure GetList(ListId: Text; var GraphList: Record "SharePoint Graph List" temporary): Codeunit "SharePoint Graph Response"
var
GraphOptionalParameters: Codeunit "Graph Optional Parameters";
begin
exit(GetList(ListId, GraphList, GraphOptionalParameters));
end;
/// <summary>
/// Gets a SharePoint list by ID.
/// </summary>
/// <param name="ListId">ID of the list to retrieve.</param>
/// <param name="GraphList">Record to store the result.</param>
/// <param name="GraphOptionalParameters">A wrapper for optional header and query parameters.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure GetList(ListId: Text; var GraphList: Record "SharePoint Graph List" temporary; GraphOptionalParameters: Codeunit "Graph Optional Parameters"): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
JsonResponse: JsonObject;
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if ListId = '' then begin
SharePointGraphResponse.SetError(InvalidListIdErr);
Session.LogMessage('0000RKC', InvalidListIdErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Make the API request
if not SharePointGraphRequestHelper.Get(SharePointGraphUriBuilder.GetListEndpoint(ListId), JsonResponse, GraphOptionalParameters) then begin
ErrorMessage := StrSubstNo(FailedToRetrieveListErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RKD', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
GraphList.Init();
if not SharePointGraphParser.ParseListItem(JsonResponse, GraphList) then begin
SharePointGraphResponse.SetError(FailedToParseListErr);
Session.LogMessage('0000RKE', FailedToParseListErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
GraphList.Insert();
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RKF', StrSubstNo(OperationSuccessTelemetryMsg, 'GetList'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Creates a new SharePoint list.
/// </summary>
/// <param name="DisplayName">Display name for the list.</param>
/// <param name="Description">Description for the list.</param>
/// <param name="GraphList">Record to store the result.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure CreateList(DisplayName: Text; Description: Text; var GraphList: Record "SharePoint Graph List" temporary): Codeunit "SharePoint Graph Response"
begin
exit(CreateList(DisplayName, 'genericList', Description, GraphList));
end;
/// <summary>
/// Creates a new SharePoint list.
/// </summary>
/// <param name="DisplayName">Display name for the list.</param>
/// <param name="ListTemplate">Template for the list (genericList, documentLibrary, etc.)</param>
/// <param name="Description">Description for the list.</param>
/// <param name="GraphList">Record to store the result.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure CreateList(DisplayName: Text; ListTemplate: Text; Description: Text; var GraphList: Record "SharePoint Graph List" temporary): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
JsonResponse: JsonObject;
RequestJsonObj: JsonObject;
ListJsonObj: JsonObject;
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if DisplayName = '' then begin
SharePointGraphResponse.SetError(InvalidDisplayNameErr);
Session.LogMessage('0000RKG', InvalidDisplayNameErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Create the request body with list properties
RequestJsonObj.Add('displayName', DisplayName);
RequestJsonObj.Add('description', Description);
// Add list template
ListJsonObj.Add('template', ListTemplate);
RequestJsonObj.Add('list', ListJsonObj);
// Post the request to create the list
if not SharePointGraphRequestHelper.Post(SharePointGraphUriBuilder.GetListsEndpoint(), RequestJsonObj, JsonResponse) then begin
ErrorMessage := StrSubstNo(FailedToCreateListErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RKH', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
GraphList.Init();
if not SharePointGraphParser.ParseListItem(JsonResponse, GraphList) then begin
SharePointGraphResponse.SetError(FailedToParseCreatedListErr);
Session.LogMessage('0000RKI', FailedToParseCreatedListErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
GraphList.Insert();
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RKJ', StrSubstNo(OperationSuccessTelemetryMsg, 'CreateList'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
#endregion
#region List Items
/// <summary>
/// Gets items from a SharePoint list.
/// </summary>
/// <param name="ListId">ID of the list.</param>
/// <param name="GraphListItems">Collection of the result (temporary record).</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure GetListItems(ListId: Text; var GraphListItems: Record "SharePoint Graph List Item" temporary): Codeunit "SharePoint Graph Response"
var
GraphOptionalParameters: Codeunit "Graph Optional Parameters";
begin
exit(GetListItems(ListId, GraphListItems, GraphOptionalParameters));
end;
/// <summary>
/// Gets items from a SharePoint list.
/// </summary>
/// <param name="ListId">ID of the list.</param>
/// <param name="GraphListItems">Collection of the result (temporary record).</param>
/// <param name="GraphOptionalParameters">A wrapper for optional header and query parameters.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure GetListItems(ListId: Text; var GraphListItems: Record "SharePoint Graph List Item" temporary; GraphOptionalParameters: Codeunit "Graph Optional Parameters"): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
JsonArray: JsonArray;
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if ListId = '' then begin
SharePointGraphResponse.SetError(InvalidListIdErr);
Session.LogMessage('0000RKK', InvalidListIdErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Use Graph pagination to get all pages automatically
if not SharePointGraphRequestHelper.GetAllPages(SharePointGraphUriBuilder.GetListItemsEndpoint(ListId), GraphOptionalParameters, JsonArray) then begin
ErrorMessage := StrSubstNo(FailedToRetrieveListItemsErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RKL', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Parse the combined results from all pages
if not SharePointGraphParser.ParseListItemCollection(JsonArray, ListId, GraphListItems) then begin
SharePointGraphResponse.SetError(FailedToParseListItemsErr);
Session.LogMessage('0000RKM', FailedToParseListItemsErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RKN', StrSubstNo(OperationSuccessTelemetryMsg, 'GetListItems'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Creates a new item in a SharePoint list.
/// </summary>
/// <param name="ListId">ID of the list.</param>
/// <param name="FieldsJsonObject">JSON object containing the fields for the new item.</param>
/// <param name="GraphListItem">Record to store the result.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure CreateListItem(ListId: Text; FieldsJsonObject: JsonObject; var GraphListItem: Record "SharePoint Graph List Item" temporary): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
JsonResponse: JsonObject;
RequestJsonObj: JsonObject;
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if ListId = '' then begin
SharePointGraphResponse.SetError(InvalidListIdErr);
Session.LogMessage('0000RKO', InvalidListIdErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Create the request body with fields
RequestJsonObj.Add('fields', FieldsJsonObject);
// Post the request to create the item
if not SharePointGraphRequestHelper.Post(SharePointGraphUriBuilder.GetCreateListItemEndpoint(ListId), RequestJsonObj, JsonResponse) then begin
ErrorMessage := StrSubstNo(FailedToCreateListItemErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RKP', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
GraphListItem.Init();
GraphListItem.ListId := CopyStr(ListId, 1, MaxStrLen(GraphListItem.ListId));
if not SharePointGraphParser.ParseListItemDetail(JsonResponse, GraphListItem) then begin
SharePointGraphResponse.SetError(FailedToParseCreatedListItemErr);
Session.LogMessage('0000RKQ', FailedToParseCreatedListItemErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
GraphListItem.Insert();
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RKR', StrSubstNo(OperationSuccessTelemetryMsg, 'CreateListItem'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Creates a new item in a SharePoint list with a simple title.
/// </summary>
/// <param name="ListId">ID of the list.</param>
/// <param name="Title">Title for the new item.</param>
/// <param name="GraphListItem">Record to store the result.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure CreateListItem(ListId: Text; Title: Text; var GraphListItem: Record "SharePoint Graph List Item" temporary): Codeunit "SharePoint Graph Response"
var
FieldsJsonObject: JsonObject;
begin
FieldsJsonObject.Add('Title', Title);
exit(CreateListItem(ListId, FieldsJsonObject, GraphListItem));
end;
#endregion
#region Drive and Items
/// <summary>
/// Gets the default document library (drive) for the site.
/// </summary>
/// <param name="DriveId">ID of the default drive.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure GetDefaultDrive(var DriveId: Text): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
EnsureDefaultDriveId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Get default drive ID if not already cached
if DefaultDriveId = '' then begin
ErrorMessage := StrSubstNo(FailedToRetrieveDefaultDriveErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RKS', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
DriveId := DefaultDriveId;
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RKT', StrSubstNo(OperationSuccessTelemetryMsg, 'GetDefaultDrive'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Gets all drives (document libraries) available on the site with detailed information.
/// </summary>
/// <param name="GraphDrives">Collection of the result (temporary record).</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure GetDrives(var GraphDrives: Record "SharePoint Graph Drive" temporary): Codeunit "SharePoint Graph Response"
var
GraphOptionalParameters: Codeunit "Graph Optional Parameters";
begin
exit(GetDrives(GraphDrives, GraphOptionalParameters));
end;
/// <summary>
/// Gets all drives (document libraries) available on the site with detailed information.
/// </summary>
/// <param name="GraphDrives">Collection of the result (temporary record).</param>
/// <param name="GraphOptionalParameters">A wrapper for optional header and query parameters.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure GetDrives(var GraphDrives: Record "SharePoint Graph Drive" temporary; GraphOptionalParameters: Codeunit "Graph Optional Parameters"): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
JsonArray: JsonArray;
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Use Graph pagination to get all pages automatically
if not SharePointGraphRequestHelper.GetAllPages(SharePointGraphUriBuilder.GetDrivesEndpoint(), GraphOptionalParameters, JsonArray) then begin
ErrorMessage := StrSubstNo(FailedToRetrieveDrivesErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RKU', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Parse the combined results from all pages
if not SharePointGraphParser.ParseDriveCollection(JsonArray, GraphDrives) then begin
SharePointGraphResponse.SetError(FailedToParseDrivesErr);
Session.LogMessage('0000RKV', FailedToParseDrivesErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RKW', StrSubstNo(OperationSuccessTelemetryMsg, 'GetDrives'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Gets a drive (document library) by ID with detailed information.
/// </summary>
/// <param name="DriveId">ID of the drive to retrieve.</param>
/// <param name="GraphDrive">Record to store the result.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure GetDrive(DriveId: Text; var GraphDrive: Record "SharePoint Graph Drive" temporary): Codeunit "SharePoint Graph Response"
var
GraphOptionalParameters: Codeunit "Graph Optional Parameters";
begin
exit(GetDrive(DriveId, GraphDrive, GraphOptionalParameters));
end;
/// <summary>
/// Gets a drive (document library) by ID with detailed information.
/// </summary>
/// <param name="DriveId">ID of the drive to retrieve.</param>
/// <param name="GraphDrive">Record to store the result.</param>
/// <param name="GraphOptionalParameters">A wrapper for optional header and query parameters.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure GetDrive(DriveId: Text; var GraphDrive: Record "SharePoint Graph Drive" temporary; GraphOptionalParameters: Codeunit "Graph Optional Parameters"): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
JsonResponse: JsonObject;
DriveEndpoint: Text;
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if DriveId = '' then begin
SharePointGraphResponse.SetError(InvalidDriveIdErr);
Session.LogMessage('0000RKX', InvalidDriveIdErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Construct drive endpoint for specific drive ID
DriveEndpoint := SharePointGraphUriBuilder.GetSpecificDriveEndpoint(DriveId);
// Make the API request
if not SharePointGraphRequestHelper.Get(DriveEndpoint, JsonResponse, GraphOptionalParameters) then begin
ErrorMessage := StrSubstNo(FailedToRetrieveDriveErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RKY', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
GraphDrive.Init();
if not SharePointGraphParser.ParseDriveDetail(JsonResponse, GraphDrive) then begin
SharePointGraphResponse.SetError(FailedToParseDriveErr);
Session.LogMessage('0000RKZ', FailedToParseDriveErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
GraphDrive.Insert();
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RL0', StrSubstNo(OperationSuccessTelemetryMsg, 'GetDrive'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Gets the default document library (drive) for the site with detailed information.
/// </summary>
/// <param name="GraphDrive">Record to store the result.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure GetDefaultDrive(var GraphDrive: Record "SharePoint Graph Drive" temporary): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
GraphOptionalParameters: Codeunit "Graph Optional Parameters";
JsonResponse: JsonObject;
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Make the API request
if not SharePointGraphRequestHelper.Get(SharePointGraphUriBuilder.GetDriveEndpoint(), JsonResponse, GraphOptionalParameters) then begin
ErrorMessage := StrSubstNo(FailedToRetrieveDefaultDriveErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RL1', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
GraphDrive.Init();
if not SharePointGraphParser.ParseDriveDetail(JsonResponse, GraphDrive) then begin
SharePointGraphResponse.SetError(FailedToParseDriveErr);
Session.LogMessage('0000RL2', FailedToParseDriveErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
GraphDrive.Insert();
// Update DefaultDriveId while we're at it
DefaultDriveId := CopyStr(GraphDrive.Id, 1, MaxStrLen(DefaultDriveId));
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RL3', StrSubstNo(OperationSuccessTelemetryMsg, 'GetDefaultDrive'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Gets items in the root folder of the default drive.
/// </summary>
/// <param name="GraphDriveItems">Collection of the result (temporary record).</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure GetRootItems(var GraphDriveItems: Record "SharePoint Graph Drive Item" temporary): Codeunit "SharePoint Graph Response"
var
GraphOptionalParameters: Codeunit "Graph Optional Parameters";
begin
exit(GetRootItems(GraphDriveItems, GraphOptionalParameters));
end;
/// <summary>
/// Gets items in the root folder of the default drive.
/// </summary>
/// <param name="GraphDriveItems">Collection of the result (temporary record).</param>
/// <param name="GraphOptionalParameters">A wrapper for optional header and query parameters.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure GetRootItems(var GraphDriveItems: Record "SharePoint Graph Drive Item" temporary; GraphOptionalParameters: Codeunit "Graph Optional Parameters"): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
JsonArray: JsonArray;
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Ensure we have Default Drive ID
EnsureDefaultDriveId();
if DefaultDriveId = '' then begin
SharePointGraphResponse.SetError(NoDefaultDriveIdErr);
Session.LogMessage('0000RL4', NoDefaultDriveIdErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Use Graph pagination to get all pages automatically
if not SharePointGraphRequestHelper.GetAllPages(SharePointGraphUriBuilder.GetDriveRootChildrenEndpoint(), GraphOptionalParameters, JsonArray) then begin
ErrorMessage := StrSubstNo(FailedToRetrieveRootItemsErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RL5', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Parse the combined results from all pages
if not SharePointGraphParser.ParseDriveItemCollection(JsonArray, DefaultDriveId, GraphDriveItems) then begin
SharePointGraphResponse.SetError(FailedToParseRootItemsErr);
Session.LogMessage('0000RL6', FailedToParseRootItemsErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RL7', StrSubstNo(OperationSuccessTelemetryMsg, 'GetRootItems'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Gets children of a folder by the folder's ID.
/// </summary>
/// <param name="FolderId">ID of the folder.</param>
/// <param name="GraphDriveItems">Collection of the result (temporary record).</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure GetFolderItems(FolderId: Text; var GraphDriveItems: Record "SharePoint Graph Drive Item" temporary): Codeunit "SharePoint Graph Response"
var
GraphOptionalParameters: Codeunit "Graph Optional Parameters";
begin
exit(GetFolderItems(FolderId, GraphDriveItems, GraphOptionalParameters));
end;
/// <summary>
/// Gets children of a folder by the folder's ID.
/// </summary>
/// <param name="FolderId">ID of the folder.</param>
/// <param name="GraphDriveItems">Collection of the result (temporary record).</param>
/// <param name="GraphOptionalParameters">A wrapper for optional header and query parameters.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure GetFolderItems(FolderId: Text; var GraphDriveItems: Record "SharePoint Graph Drive Item" temporary; GraphOptionalParameters: Codeunit "Graph Optional Parameters"): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
JsonArray: JsonArray;
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if FolderId = '' then begin
SharePointGraphResponse.SetError(InvalidFolderIdErr);
Session.LogMessage('0000RL8', InvalidFolderIdErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Ensure we have Default Drive ID
EnsureDefaultDriveId();
if DefaultDriveId = '' then begin
SharePointGraphResponse.SetError(NoDefaultDriveIdErr);
Session.LogMessage('0000RL9', NoDefaultDriveIdErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Use Graph pagination to get all pages automatically
if not SharePointGraphRequestHelper.GetAllPages(SharePointGraphUriBuilder.GetDriveItemChildrenByIdEndpoint(FolderId), GraphOptionalParameters, JsonArray) then begin
ErrorMessage := StrSubstNo(FailedToRetrieveFolderItemsErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RLA', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Parse the combined results from all pages
if not SharePointGraphParser.ParseDriveItemCollection(JsonArray, DefaultDriveId, GraphDriveItems) then begin
SharePointGraphResponse.SetError(FailedToParseFolderItemsErr);
Session.LogMessage('0000RLB', FailedToParseFolderItemsErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RLC', StrSubstNo(OperationSuccessTelemetryMsg, 'GetFolderItems'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Gets items from a path in the default drive.
/// </summary>
/// <param name="FolderPath">Path to the folder (e.g., 'Documents/Folder1').</param>
/// <param name="GraphDriveItems">Collection of the result (temporary record).</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure GetItemsByPath(FolderPath: Text; var GraphDriveItems: Record "SharePoint Graph Drive Item" temporary): Codeunit "SharePoint Graph Response"
var
GraphOptionalParameters: Codeunit "Graph Optional Parameters";
begin
exit(GetItemsByPath(FolderPath, GraphDriveItems, GraphOptionalParameters));
end;
/// <summary>
/// Gets items from a path in the default drive.
/// </summary>
/// <param name="FolderPath">Path to the folder (e.g., 'Documents/Folder1').</param>
/// <param name="GraphDriveItems">Collection of the result (temporary record).</param>
/// <param name="GraphOptionalParameters">A wrapper for optional header and query parameters.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure GetItemsByPath(FolderPath: Text; var GraphDriveItems: Record "SharePoint Graph Drive Item" temporary; GraphOptionalParameters: Codeunit "Graph Optional Parameters"): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
JsonArray: JsonArray;
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
EnsureDefaultDriveId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Handle empty path as root
if FolderPath = '' then
exit(GetRootItems(GraphDriveItems, GraphOptionalParameters));
// Remove leading slash if present
FolderPath := FolderPath.TrimStart('/');
// Use Graph pagination to get all pages automatically
if not SharePointGraphRequestHelper.GetAllPages(SharePointGraphUriBuilder.GetDriveItemChildrenByPathEndpoint(FolderPath), GraphOptionalParameters, JsonArray) then begin
ErrorMessage := StrSubstNo(FailedToRetrieveItemsByPathErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RLD', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Parse the combined results from all pages
if not SharePointGraphParser.ParseDriveItemCollection(JsonArray, DefaultDriveId, GraphDriveItems) then begin
SharePointGraphResponse.SetError(FailedToParseItemsByPathErr);
Session.LogMessage('0000RLE', FailedToParseItemsByPathErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RLF', StrSubstNo(OperationSuccessTelemetryMsg, 'GetItemsByPath'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Uploads a file to a folder in a specific drive (document library) with specified conflict behavior.
/// </summary>
/// <param name="DriveId">ID of the drive (document library).</param>
/// <param name="FolderPath">Path to the folder (e.g., 'Documents').</param>
/// <param name="FileName">Name of the file to upload.</param>
/// <param name="FileInStream">Content of the file.</param>
/// <param name="GraphDriveItem">Record to store the result.</param>
/// <param name="ConflictBehavior">How to handle conflicts if a file with the same name exists</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure UploadFile(DriveId: Text; FolderPath: Text; FileName: Text; FileInStream: InStream; var GraphDriveItem: Record "SharePoint Graph Drive Item" temporary; ConflictBehavior: Enum "Graph ConflictBehavior"): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
GraphOptionalParameters: Codeunit "Graph Optional Parameters";
JsonResponse: JsonObject;
EffectiveDriveId: Text;
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if FileName = '' then begin
SharePointGraphResponse.SetError(InvalidFileNameErr);
Session.LogMessage('0000RLG', InvalidFileNameErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Use default drive ID if none specified
if DriveId = '' then begin
EnsureDefaultDriveId();
EffectiveDriveId := DefaultDriveId;
end else
EffectiveDriveId := DriveId;
// Remove leading slash if present
FolderPath := FolderPath.TrimStart('/');
// Configure conflict behavior
SharePointGraphRequestHelper.ConfigureConflictBehavior(GraphOptionalParameters, ConflictBehavior);
// Put the file content in the specific drive
if not SharePointGraphRequestHelper.UploadFile(SharePointGraphUriBuilder.GetSpecificDriveUploadEndpoint(EffectiveDriveId, FolderPath, FileName), FileInStream, GraphOptionalParameters, JsonResponse) then begin
ErrorMessage := StrSubstNo(FailedToUploadFileErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RLH', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
GraphDriveItem.Init();
GraphDriveItem.DriveId := CopyStr(EffectiveDriveId, 1, MaxStrLen(GraphDriveItem.DriveId));
if not SharePointGraphParser.ParseDriveItemDetail(JsonResponse, GraphDriveItem) then begin
SharePointGraphResponse.SetError(FailedToParseUploadedFileErr);
Session.LogMessage('0000RLI', FailedToParseUploadedFileErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
GraphDriveItem.Insert();
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RLJ', StrSubstNo(OperationSuccessTelemetryMsg, 'UploadFile'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Uploads a file to a folder in a specific drive (document library).
/// </summary>
/// <param name="DriveId">ID of the drive (document library).</param>
/// <param name="FolderPath">Path to the folder (e.g., 'Documents').</param>
/// <param name="FileName">Name of the file to upload.</param>
/// <param name="FileInStream">Content of the file.</param>
/// <param name="GraphDriveItem">Record to store the result.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure UploadFile(DriveId: Text; FolderPath: Text; FileName: Text; FileInStream: InStream; var GraphDriveItem: Record "SharePoint Graph Drive Item" temporary): Codeunit "SharePoint Graph Response"
begin
exit(UploadFile(DriveId, FolderPath, FileName, FileInStream, GraphDriveItem, Enum::"Graph ConflictBehavior"::Replace));
end;
/// <summary>
/// Uploads a large file to a folder on SharePoint using chunked upload for better performance and reliability.
/// </summary>
/// <param name="DriveId">ID of the drive (document library), or empty for default drive.</param>
/// <param name="FolderPath">Path to the folder (e.g., 'Documents').</param>
/// <param name="FileName">Name of the file to upload.</param>
/// <param name="FileInStream">Content of the file.</param>
/// <param name="GraphDriveItem">Record to store the result.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure UploadLargeFile(DriveId: Text; FolderPath: Text; FileName: Text; FileInStream: InStream; var GraphDriveItem: Record "SharePoint Graph Drive Item" temporary): Codeunit "SharePoint Graph Response"
var
GraphConflictBehavior: Enum "Graph ConflictBehavior";
begin
exit(UploadLargeFile(DriveId, FolderPath, FileName, FileInStream, GraphDriveItem, GraphConflictBehavior::Replace));
end;
/// <summary>
/// Uploads a large file to a folder on SharePoint using chunked upload with specified conflict behavior.
/// </summary>
/// <param name="DriveId">ID of the drive (document library), or empty for default drive.</param>
/// <param name="FolderPath">Path to the folder (e.g., 'Documents').</param>
/// <param name="FileName">Name of the file to upload.</param>
/// <param name="FileInStream">Content of the file.</param>
/// <param name="GraphDriveItem">Record to store the result.</param>
/// <param name="ConflictBehavior">How to handle conflicts if a file with the same name exists</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure UploadLargeFile(DriveId: Text; FolderPath: Text; FileName: Text; FileInStream: InStream; var GraphDriveItem: Record "SharePoint Graph Drive Item" temporary; ConflictBehavior: Enum "Graph ConflictBehavior"): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
GraphOptionalParameters: Codeunit "Graph Optional Parameters";
TempBlob: Codeunit "Temp Blob";
ChunkInStream: InStream;
ChunkOutStream: OutStream;
JsonResponse: JsonObject;
CompleteResponseJson: JsonObject;
Endpoint: Text;
UploadUrl: Text;
ContentRange: Text;
EffectiveDriveId: Text;
ErrorMessage: Text;
FileSize: Integer;
ChunkSize: Integer;
BytesInChunk: Integer;
TotalBytesRead: Integer;
MinChunkSize: Integer;
ChunkMultiple: Integer;
begin
EnsureInitialized();
EnsureSiteId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if FileName = '' then begin
SharePointGraphResponse.SetError(InvalidFileNameErr);
Session.LogMessage('0000RLK', InvalidFileNameErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Use default drive ID if none specified
if DriveId = '' then begin
EnsureDefaultDriveId();
if DefaultDriveId = '' then begin
SharePointGraphResponse.SetError(NoDefaultDriveIdErr);
Session.LogMessage('0000RLL', NoDefaultDriveIdErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
EffectiveDriveId := DefaultDriveId;
end else
EffectiveDriveId := DriveId;
// Remove leading slash if present
FolderPath := FolderPath.TrimStart('/');
// Configure conflict behavior
SharePointGraphRequestHelper.ConfigureConflictBehavior(GraphOptionalParameters, ConflictBehavior);
// Prepare the upload session endpoint
if EffectiveDriveId = DefaultDriveId then
Endpoint := SharePointGraphUriBuilder.GetDriveItemByPathEndpoint(FolderPath + '/' + FileName)
else
Endpoint := SharePointGraphUriBuilder.GetSpecificDriveUploadEndpoint(EffectiveDriveId, FolderPath, FileName);
FileSize := FileInStream.Length();
if FileSize <= 0 then begin
SharePointGraphResponse.SetError(InvalidFileSizeErr);
Session.LogMessage('0000RLM', InvalidFileSizeErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Create upload session
if not SharePointGraphRequestHelper.CreateUploadSession(Endpoint, FileName, FileSize, GraphOptionalParameters, ConflictBehavior, UploadUrl) then begin
ErrorMessage := StrSubstNo(FailedToCreateUploadSessionErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RLN', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Microsoft requires chunks to be multiples of 320 KiB (327,680 bytes)
ChunkMultiple := 320 * 1024; // 320 KiB
MinChunkSize := ChunkMultiple; // Minimum allowed size
// Use 4 MB chunks as recommended by Microsoft for optimum performance
ChunkSize := 4 * 1024 * 1024; // 4 MB
// Ensure chunk size is a multiple of 320 KiB
ChunkSize := Round(ChunkSize / ChunkMultiple, 1, '<') * ChunkMultiple;
// For small files, use at least the minimum size but ensure it doesn't exceed file size
if FileSize < ChunkSize then
ChunkSize := MinChunkSize;
// Reset the stream position to beginning
FileInStream.ResetPosition();
TotalBytesRead := 0;
// Read and upload chunks until the entire file is uploaded
while TotalBytesRead < FileSize do begin
// Clear temp blob for new chunk
Clear(TempBlob);
TempBlob.CreateOutStream(ChunkOutStream);
// Determine bytes to copy in this chunk
BytesInChunk := ChunkSize;
if (FileSize - TotalBytesRead) < ChunkSize then
// For the last chunk, ensure it's still a multiple of 320 KiB unless it's the final remainder
if (FileSize - TotalBytesRead) > MinChunkSize then
BytesInChunk := Round((FileSize - TotalBytesRead) / ChunkMultiple, 1, '<') * ChunkMultiple
else
BytesInChunk := FileSize - TotalBytesRead;
// Copy directly from source stream into the chunk stream
CopyStream(ChunkOutStream, FileInStream, BytesInChunk);
// Prepare content range header - must follow format "bytes startPosition-endPosition/totalSize"
ContentRange := StrSubstNo(ContentRangeHeaderLbl,
TotalBytesRead,
TotalBytesRead + BytesInChunk - 1,
FileSize);
// Get the input stream for the chunk
TempBlob.CreateInStream(ChunkInStream);
// Upload the chunk - use the exact URL returned from the upload session without modification
if not SharePointGraphRequestHelper.UploadChunk(UploadUrl, ChunkInStream, ContentRange, JsonResponse) then begin
ErrorMessage := StrSubstNo(FailedToUploadChunkErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RLO', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Check if upload is complete (last chunk response will contain the item details)
if JsonResponse.Contains('id') then
CompleteResponseJson := JsonResponse;
// Update total bytes read
TotalBytesRead += BytesInChunk;
end;
if not CompleteResponseJson.Contains('id') then begin
SharePointGraphResponse.SetError(NoUploadResponseErr);
Session.LogMessage('0000RLP', NoUploadResponseErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
GraphDriveItem.Init();
GraphDriveItem.DriveId := CopyStr(EffectiveDriveId, 1, MaxStrLen(GraphDriveItem.DriveId));
if not SharePointGraphParser.ParseDriveItemDetail(CompleteResponseJson, GraphDriveItem) then begin
SharePointGraphResponse.SetError(FailedToParseChunkedUploadErr);
Session.LogMessage('0000RLQ', FailedToParseChunkedUploadErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
GraphDriveItem.Insert();
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RLR', StrSubstNo(OperationSuccessTelemetryMsg, 'UploadLargeFile'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Creates a new folder in a specific drive (document library) with specified conflict behavior.
/// </summary>
/// <param name="DriveId">ID of the drive (document library).</param>
/// <param name="FolderPath">Path where to create the folder (e.g., 'Documents').</param>
/// <param name="FolderName">Name of the new folder.</param>
/// <param name="GraphDriveItem">Record to store the result.</param>
/// <param name="ConflictBehavior">How to handle conflicts if a folder with the same name exists</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure CreateFolder(DriveId: Text; FolderPath: Text; FolderName: Text; var GraphDriveItem: Record "SharePoint Graph Drive Item" temporary; ConflictBehavior: Enum "Graph ConflictBehavior"): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
GraphOptionalParameters: Codeunit "Graph Optional Parameters";
JsonResponse: JsonObject;
RequestJsonObj: JsonObject;
FolderJsonObj: JsonObject;
Endpoint: Text;
EffectiveDriveId: Text;
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if FolderName = '' then begin
SharePointGraphResponse.SetError(InvalidFolderNameErr);
Session.LogMessage('0000RLS', InvalidFolderNameErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Use default drive ID if none specified
if DriveId = '' then begin
EnsureDefaultDriveId();
EffectiveDriveId := DefaultDriveId;
end else
EffectiveDriveId := DriveId;
// Remove leading slash if present
FolderPath := FolderPath.TrimStart('/');
// Create the request body with folder properties
RequestJsonObj.Add('name', FolderName);
RequestJsonObj.Add('folder', FolderJsonObj);
// Configure conflict behavior
SharePointGraphRequestHelper.ConfigureConflictBehavior(GraphOptionalParameters, ConflictBehavior);
// Set endpoint for creating folder in specific drive
if FolderPath = '' then
Endpoint := SharePointGraphUriBuilder.GetSpecificDriveRootChildrenEndpoint(EffectiveDriveId)
else
Endpoint := SharePointGraphUriBuilder.GetSpecificDriveItemChildrenByPathEndpoint(EffectiveDriveId, FolderPath);
// Post the request to create the folder
if not SharePointGraphRequestHelper.Post(Endpoint, RequestJsonObj, GraphOptionalParameters, JsonResponse) then begin
ErrorMessage := StrSubstNo(FailedToCreateFolderErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RLT', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
GraphDriveItem.Init();
GraphDriveItem.DriveId := CopyStr(EffectiveDriveId, 1, MaxStrLen(GraphDriveItem.DriveId));
if not SharePointGraphParser.ParseDriveItemDetail(JsonResponse, GraphDriveItem) then begin
SharePointGraphResponse.SetError(FailedToParseCreatedFolderErr);
Session.LogMessage('0000RLU', FailedToParseCreatedFolderErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
GraphDriveItem.Insert();
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RLV', StrSubstNo(OperationSuccessTelemetryMsg, 'CreateFolder'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Creates a new folder in a specific drive (document library).
/// </summary>
/// <param name="DriveId">ID of the drive (document library).</param>
/// <param name="FolderPath">Path where to create the folder (e.g., 'Documents').</param>
/// <param name="FolderName">Name of the new folder.</param>
/// <param name="GraphDriveItem">Record to store the result.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure CreateFolder(DriveId: Text; FolderPath: Text; FolderName: Text; var GraphDriveItem: Record "SharePoint Graph Drive Item" temporary): Codeunit "SharePoint Graph Response"
begin
exit(CreateFolder(DriveId, FolderPath, FolderName, GraphDriveItem, Enum::"Graph ConflictBehavior"::Fail));
end;
/// <summary>
/// Downloads a file.
/// </summary>
/// <param name="ItemId">ID of the file to download.</param>
/// <param name="TempBlob">TempBlob to receive the file content.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure DownloadFile(ItemId: Text; var TempBlob: Codeunit "Temp Blob"): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if ItemId = '' then begin
SharePointGraphResponse.SetError(InvalidItemIdErr);
Session.LogMessage('0000RLW', InvalidItemIdErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Make the API request
if not SharePointGraphRequestHelper.DownloadFile(SharePointGraphUriBuilder.GetDriveItemContentByIdEndpoint(ItemId), TempBlob) then begin
ErrorMessage := StrSubstNo(FailedToDownloadFileErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RLX', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RLY', StrSubstNo(OperationSuccessTelemetryMsg, 'DownloadFile'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Downloads a file by path.
/// </summary>
/// <param name="FilePath">Path to the file (e.g., 'Documents/file.docx').</param>
/// <param name="TempBlob">TempBlob to receive the file content.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure DownloadFileByPath(FilePath: Text; var TempBlob: Codeunit "Temp Blob"): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if FilePath = '' then begin
SharePointGraphResponse.SetError(InvalidFilePathErr);
Session.LogMessage('0000RLZ', InvalidFilePathErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Remove leading slash if present
FilePath := FilePath.TrimStart('/');
// Make the API request
if not SharePointGraphRequestHelper.DownloadFile(SharePointGraphUriBuilder.GetDriveItemContentByPathEndpoint(FilePath), TempBlob) then begin
ErrorMessage := StrSubstNo(FailedToDownloadFileByPathErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RM0', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RM1', StrSubstNo(OperationSuccessTelemetryMsg, 'DownloadFileByPath'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Downloads a large file using chunked download to overcome Business Central's 150MB HTTP response limit.
/// </summary>
/// <param name="ItemId">ID of the file to download.</param>
/// <param name="FileOutStream">OutStream to receive the file content.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure DownloadLargeFile(ItemId: Text; var TempBlob: Codeunit "Temp Blob"): Codeunit "SharePoint Graph Response"
var
GraphDriveItem: Record "SharePoint Graph Drive Item";
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
ChunkTempBlob: Codeunit "Temp Blob";
ChunkInStream: InStream;
FileOutStream: OutStream;
FileSize: BigInteger;
ChunkSize: BigInteger;
RangeStart: BigInteger;
RangeEnd: BigInteger;
Endpoint: Text;
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
EnsureDefaultDriveId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if ItemId = '' then begin
SharePointGraphResponse.SetError(InvalidItemIdErr);
Session.LogMessage('0000RM2', InvalidItemIdErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// First, get the file size
if not GetDriveItem(ItemId, GraphDriveItem).IsSuccessful() then begin
ErrorMessage := StrSubstNo(FailedToGetFileSizeErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RM3', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
FileSize := GraphDriveItem.Size;
if FileSize <= 0 then begin
SharePointGraphResponse.SetError(InvalidFileSizeErr);
Session.LogMessage('0000RM4', InvalidFileSizeErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// 100MB chunk size (104,857,600 bytes) - safely under 150MB Business Central limit
ChunkSize := 100 * 1024 * 1024;
RangeStart := 0;
Endpoint := SharePointGraphUriBuilder.GetDriveItemContentByIdEndpoint(ItemId);
TempBlob.CreateOutStream(FileOutStream);
// Download file in chunks
while RangeStart < FileSize do begin
Clear(ChunkTempBlob);
Clear(ChunkInStream);
RangeEnd := RangeStart + ChunkSize - 1;
if RangeEnd >= FileSize then
RangeEnd := FileSize - 1;
if not SharePointGraphRequestHelper.DownloadChunk(Endpoint, RangeStart, RangeEnd, ChunkTempBlob) then begin
ErrorMessage := StrSubstNo(FailedToDownloadChunkErr, RangeStart, RangeEnd, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RM5', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Write chunk to output Blob
ChunkTempBlob.CreateInStream(ChunkInStream);
CopyStream(FileOutStream, ChunkInStream);
RangeStart := RangeEnd + 1;
end;
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RM6', StrSubstNo(OperationSuccessTelemetryMsg, 'DownloadLargeFile'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Downloads a large file by path using chunked download to overcome Business Central's 150MB HTTP response limit.
/// </summary>
/// <param name="FilePath">Path to the file (e.g., 'Documents/file.docx').</param>
/// <param name="FileOutStream">OutStream to receive the file content.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure DownloadLargeFileByPath(FilePath: Text; var TempBlob: Codeunit "Temp Blob"): Codeunit "SharePoint Graph Response"
var
GraphDriveItem: Record "SharePoint Graph Drive Item";
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
ChunkTempBlob: Codeunit "Temp Blob";
ChunkInStream: InStream;
FileOutStream: OutStream;
FileSize: BigInteger;
ChunkSize: BigInteger;
RangeStart: BigInteger;
RangeEnd: BigInteger;
Endpoint: Text;
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
EnsureDefaultDriveId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if FilePath = '' then begin
SharePointGraphResponse.SetError(InvalidFilePathErr);
Session.LogMessage('0000RM7', InvalidFilePathErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Remove leading slash if present
FilePath := FilePath.TrimStart('/');
// First, get the file size
if not GetDriveItemByPath(FilePath, GraphDriveItem).IsSuccessful() then begin
ErrorMessage := StrSubstNo(FailedToGetFileSizeErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RM8', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
FileSize := GraphDriveItem.Size;
if FileSize <= 0 then begin
SharePointGraphResponse.SetError(InvalidFileSizeErr);
Session.LogMessage('0000RM9', InvalidFileSizeErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// 100MB chunk size (104,857,600 bytes) - safely under 150MB Business Central limit
ChunkSize := 100 * 1024 * 1024;
RangeStart := 0;
Endpoint := SharePointGraphUriBuilder.GetDriveItemContentByPathEndpoint(FilePath);
TempBlob.CreateOutStream(FileOutStream);
// Download file in chunks
while RangeStart < FileSize do begin
Clear(ChunkTempBlob);
RangeEnd := RangeStart + ChunkSize - 1;
if RangeEnd >= FileSize then
RangeEnd := FileSize - 1;
if not SharePointGraphRequestHelper.DownloadChunk(Endpoint, RangeStart, RangeEnd, ChunkTempBlob) then begin
ErrorMessage := StrSubstNo(FailedToDownloadChunkErr, RangeStart, RangeEnd, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RMA', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Write chunk to output Blob
ChunkTempBlob.CreateInStream(ChunkInStream);
CopyStream(FileOutStream, ChunkInStream);
RangeStart := RangeEnd + 1;
end;
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RMB', StrSubstNo(OperationSuccessTelemetryMsg, 'DownloadLargeFileByPath'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Gets a file or folder by ID.
/// </summary>
/// <param name="ItemId">ID of the item to retrieve.</param>
/// <param name="GraphDriveItem">Record to store the result.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure GetDriveItem(ItemId: Text; var GraphDriveItem: Record "SharePoint Graph Drive Item" temporary): Codeunit "SharePoint Graph Response"
var
GraphOptionalParameters: Codeunit "Graph Optional Parameters";
begin
exit(GetDriveItem(ItemId, GraphDriveItem, GraphOptionalParameters));
end;
/// <summary>
/// Gets a file or folder by path.
/// </summary>
/// <param name="ItemPath">Path to the item (e.g., 'Documents/file.docx').</param>
/// <param name="GraphDriveItem">Record to store the result.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure GetDriveItemByPath(ItemPath: Text; var GraphDriveItem: Record "SharePoint Graph Drive Item" temporary): Codeunit "SharePoint Graph Response"
var
GraphOptionalParameters: Codeunit "Graph Optional Parameters";
begin
exit(GetDriveItemByPath(ItemPath, GraphDriveItem, GraphOptionalParameters));
end;
/// <summary>
/// Gets a file or folder by path.
/// </summary>
/// <param name="ItemPath">Path to the item (e.g., 'Documents/file.docx').</param>
/// <param name="GraphDriveItem">Record to store the result.</param>
/// <param name="GraphOptionalParameters">A wrapper for optional header and query parameters.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure GetDriveItemByPath(ItemPath: Text; var GraphDriveItem: Record "SharePoint Graph Drive Item" temporary; GraphOptionalParameters: Codeunit "Graph Optional Parameters"): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
JsonResponse: JsonObject;
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
EnsureDefaultDriveId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if ItemPath = '' then begin
SharePointGraphResponse.SetError(InvalidItemPathErr);
Session.LogMessage('0000RMC', InvalidItemPathErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Remove leading slash if present
ItemPath := ItemPath.TrimStart('/');
// Make the API request
if not SharePointGraphRequestHelper.Get(SharePointGraphUriBuilder.GetDriveItemByPathEndpoint(ItemPath), JsonResponse, GraphOptionalParameters) then begin
ErrorMessage := StrSubstNo(FailedToRetrieveDriveItemByPathErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RMD', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
GraphDriveItem.Init();
GraphDriveItem.DriveId := CopyStr(DefaultDriveId, 1, MaxStrLen(GraphDriveItem.DriveId));
if not SharePointGraphParser.ParseDriveItemDetail(JsonResponse, GraphDriveItem) then begin
SharePointGraphResponse.SetError(FailedToParseDriveItemByPathErr);
Session.LogMessage('0000RME', FailedToParseDriveItemByPathErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
GraphDriveItem.Insert();
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RMF', StrSubstNo(OperationSuccessTelemetryMsg, 'GetDriveItemByPath'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Gets a file or folder by ID.
/// </summary>
/// <param name="ItemId">ID of the item to retrieve.</param>
/// <param name="GraphDriveItem">Record to store the result.</param>
/// <param name="GraphOptionalParameters">A wrapper for optional header and query parameters.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure GetDriveItem(ItemId: Text; var GraphDriveItem: Record "SharePoint Graph Drive Item" temporary; GraphOptionalParameters: Codeunit "Graph Optional Parameters"): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
JsonResponse: JsonObject;
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
EnsureDefaultDriveId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if ItemId = '' then begin
SharePointGraphResponse.SetError(InvalidItemIdErr);
Session.LogMessage('0000RMG', InvalidItemIdErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Make the API request
if not SharePointGraphRequestHelper.Get(SharePointGraphUriBuilder.GetDriveItemByIdEndpoint(ItemId), JsonResponse, GraphOptionalParameters) then begin
ErrorMessage := StrSubstNo(FailedToRetrieveDriveItemErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RMH', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
GraphDriveItem.Init();
GraphDriveItem.DriveId := CopyStr(DefaultDriveId, 1, MaxStrLen(GraphDriveItem.DriveId));
if not SharePointGraphParser.ParseDriveItemDetail(JsonResponse, GraphDriveItem) then begin
SharePointGraphResponse.SetError(FailedToParseDriveItemErr);
Session.LogMessage('0000RMI', FailedToParseDriveItemErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
GraphDriveItem.Insert();
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RMJ', StrSubstNo(OperationSuccessTelemetryMsg, 'GetDriveItem'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Deletes a drive item (file or folder) by ID.
/// </summary>
/// <param name="ItemId">ID of the item to delete.</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure DeleteItem(ItemId: Text): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
EnsureDefaultDriveId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if ItemId = '' then begin
SharePointGraphResponse.SetError(InvalidItemIdErr);
Session.LogMessage('0000RMK', InvalidItemIdErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Make the DELETE request
if not SharePointGraphRequestHelper.Delete(SharePointGraphUriBuilder.GetDriveItemByIdEndpoint(ItemId)) then begin
// 404 is success - item already doesn't exist
if SharePointGraphRequestHelper.GetDiagnostics().GetHttpStatusCode() = 404 then begin
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RML', StrSubstNo(OperationSuccessTelemetryMsg, 'DeleteItem'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
ErrorMessage := StrSubstNo(FailedToDeleteItemErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RMM', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RMN', StrSubstNo(OperationSuccessTelemetryMsg, 'DeleteItem'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Deletes a drive item (file or folder) by path.
/// </summary>
/// <param name="ItemPath">Path to the item (e.g., 'Documents/file.docx').</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure DeleteItemByPath(ItemPath: Text): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
EnsureDefaultDriveId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if ItemPath = '' then begin
SharePointGraphResponse.SetError(InvalidFilePathErr);
Session.LogMessage('0000RMO', InvalidFilePathErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Remove leading slash if present
ItemPath := ItemPath.TrimStart('/');
// Make the DELETE request
if not SharePointGraphRequestHelper.Delete(SharePointGraphUriBuilder.GetDriveItemByPathEndpoint(ItemPath)) then begin
// 404 is success - item already doesn't exist
if SharePointGraphRequestHelper.GetDiagnostics().GetHttpStatusCode() = 404 then begin
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RMP', StrSubstNo(OperationSuccessTelemetryMsg, 'DeleteItemByPath'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
ErrorMessage := StrSubstNo(FailedToDeleteItemByPathErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RMQ', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RMR', StrSubstNo(OperationSuccessTelemetryMsg, 'DeleteItemByPath'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Checks if a drive item (file or folder) exists by ID.
/// </summary>
/// <param name="ItemId">ID of the item to check.</param>
/// <param name="Exists">True if the item exists, false if it doesn't exist (404).</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure ItemExists(ItemId: Text; var Exists: Boolean): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
JsonResponse: JsonObject;
HttpStatusCode: Integer;
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
EnsureDefaultDriveId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if ItemId = '' then begin
SharePointGraphResponse.SetError(InvalidItemIdErr);
Session.LogMessage('0000RMS', InvalidItemIdErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
Exists := false;
exit(SharePointGraphResponse);
end;
// Try to get the item
if not SharePointGraphRequestHelper.Get(SharePointGraphUriBuilder.GetDriveItemByIdEndpoint(ItemId), JsonResponse) then begin
HttpStatusCode := SharePointGraphRequestHelper.GetDiagnostics().GetHttpStatusCode();
// 404 means item doesn't exist - this is a successful check, just with a negative result
if HttpStatusCode = 404 then begin
Exists := false;
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RMT', StrSubstNo(OperationSuccessTelemetryMsg, 'ItemExists'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// For other errors (401, 403, 429, 500, etc.), return error response
ErrorMessage := StrSubstNo(FailedToRetrieveDriveItemErr, SharePointGraphRequestHelper.GetDiagnostics().GetErrorMessage());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RMU', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
Exists := false;
exit(SharePointGraphResponse);
end;
// Item exists
Exists := true;
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RMV', StrSubstNo(OperationSuccessTelemetryMsg, 'ItemExists'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Checks if a drive item (file or folder) exists by path.
/// </summary>
/// <param name="ItemPath">Path to the item (e.g., 'Documents/file.docx').</param>
/// <param name="Exists">True if the item exists, false if it doesn't exist (404).</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure ItemExistsByPath(ItemPath: Text; var Exists: Boolean): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
JsonResponse: JsonObject;
HttpStatusCode: Integer;
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
EnsureDefaultDriveId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if ItemPath = '' then begin
SharePointGraphResponse.SetError(InvalidItemPathErr);
Session.LogMessage('0000RMW', InvalidItemPathErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
Exists := false;
exit(SharePointGraphResponse);
end;
// Remove leading slash if present
ItemPath := ItemPath.TrimStart('/');
// Try to get the item
if not SharePointGraphRequestHelper.Get(SharePointGraphUriBuilder.GetDriveItemByPathEndpoint(ItemPath), JsonResponse) then begin
HttpStatusCode := SharePointGraphRequestHelper.GetDiagnostics().GetHttpStatusCode();
// 404 means item doesn't exist - this is a successful check, just with a negative result
if HttpStatusCode = 404 then begin
Exists := false;
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RMX', StrSubstNo(OperationSuccessTelemetryMsg, 'ItemExistsByPath'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// For other errors (401, 403, 429, 500, etc.), return error response
ErrorMessage := StrSubstNo(FailedToRetrieveDriveItemByPathErr, SharePointGraphRequestHelper.GetDiagnostics().GetErrorMessage());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RMY', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
Exists := false;
exit(SharePointGraphResponse);
end;
// Item exists
Exists := true;
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RMZ', StrSubstNo(OperationSuccessTelemetryMsg, 'ItemExistsByPath'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Copies a drive item (file or folder) to a new location by ID.
/// </summary>
/// <param name="ItemId">ID of the item to copy.</param>
/// <param name="TargetFolderId">ID of the target folder.</param>
/// <param name="NewName">New name for the copied item (optional).</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure CopyItem(ItemId: Text; TargetFolderId: Text; NewName: Text): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
RequestBody: JsonObject;
ParentReference: JsonObject;
ResponseJson: JsonObject;
CopyEndpoint: Text;
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
EnsureDefaultDriveId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if ItemId = '' then begin
SharePointGraphResponse.SetError(InvalidItemIdErr);
Session.LogMessage('0000RN0', InvalidItemIdErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
if TargetFolderId = '' then begin
SharePointGraphResponse.SetError(InvalidTargetPathErr);
Session.LogMessage('0000RN1', InvalidTargetPathErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Build the copy endpoint
CopyEndpoint := SharePointGraphUriBuilder.GetCopyItemEndpoint(ItemId);
// Build request body
ParentReference.Add('driveId', DefaultDriveId);
ParentReference.Add('id', TargetFolderId);
RequestBody.Add('parentReference', ParentReference);
if NewName <> '' then
RequestBody.Add('name', NewName);
// Make the POST request (copy is asynchronous - returns 202 Accepted)
if not SharePointGraphRequestHelper.Post(CopyEndpoint, RequestBody, ResponseJson) then begin
ErrorMessage := StrSubstNo(FailedToCopyItemErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RN2', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RN3', StrSubstNo(OperationSuccessTelemetryMsg, 'CopyItem'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Copies a drive item (file or folder) to a new location by path.
/// </summary>
/// <param name="ItemPath">Path to the item (e.g., 'Documents/file.docx').</param>
/// <param name="TargetFolderPath">Path to the target folder (e.g., 'Documents/Archive').</param>
/// <param name="NewName">New name for the copied item (optional).</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure CopyItemByPath(ItemPath: Text; TargetFolderPath: Text; NewName: Text): Codeunit "SharePoint Graph Response"
var
GraphDriveItem: Record "SharePoint Graph Drive Item";
TargetFolderItem: Record "SharePoint Graph Drive Item";
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
begin
EnsureInitialized();
EnsureSiteId();
EnsureDefaultDriveId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if ItemPath = '' then begin
SharePointGraphResponse.SetError(InvalidFilePathErr);
Session.LogMessage('0000RN4', InvalidFilePathErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
if TargetFolderPath = '' then begin
SharePointGraphResponse.SetError(InvalidTargetPathErr);
Session.LogMessage('0000RN5', InvalidTargetPathErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Get the target folder ID first
SharePointGraphResponse := GetDriveItemByPath(TargetFolderPath, TargetFolderItem);
if not SharePointGraphResponse.IsSuccessful() then
exit(SharePointGraphResponse);
// Get the source item ID
SharePointGraphResponse := GetDriveItemByPath(ItemPath, GraphDriveItem);
if not SharePointGraphResponse.IsSuccessful() then
exit(SharePointGraphResponse);
// Now call CopyItem with IDs
exit(CopyItem(GraphDriveItem.Id, TargetFolderItem.Id, NewName));
end;
/// <summary>
/// Moves a drive item (file or folder) to a new location by ID.
/// </summary>
/// <param name="ItemId">ID of the item to move.</param>
/// <param name="TargetFolderId">ID of the target folder (optional).</param>
/// <param name="NewName">New name for the moved item (optional).</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure MoveItem(ItemId: Text; TargetFolderId: Text; NewName: Text): Codeunit "SharePoint Graph Response"
var
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
RequestBody: JsonObject;
ParentReference: JsonObject;
ResponseJson: JsonObject;
ErrorMessage: Text;
begin
EnsureInitialized();
EnsureSiteId();
EnsureDefaultDriveId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if ItemId = '' then begin
SharePointGraphResponse.SetError(InvalidItemIdErr);
Session.LogMessage('0000RN6', InvalidItemIdErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// At least one of target folder or new name must be provided
if (TargetFolderId = '') and (NewName = '') then begin
SharePointGraphResponse.SetError(InvalidTargetPathErr);
Session.LogMessage('0000RN7', InvalidTargetPathErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Build request body for PATCH
// Move is done by updating the parentReference and/or name
if TargetFolderId <> '' then begin
ParentReference.Add('id', TargetFolderId);
RequestBody.Add('parentReference', ParentReference);
end;
if NewName <> '' then
RequestBody.Add('name', NewName);
// Make the PATCH request
if not SharePointGraphRequestHelper.Patch(SharePointGraphUriBuilder.GetDriveItemByIdEndpoint(ItemId), RequestBody, ResponseJson) then begin
ErrorMessage := StrSubstNo(FailedToMoveItemErr, SharePointGraphRequestHelper.GetDiagnostics().GetResponseReasonPhrase());
SharePointGraphResponse.SetError(ErrorMessage);
Session.LogMessage('0000RN8', ErrorMessage, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
SharePointGraphResponse.SetSuccess();
Session.LogMessage('0000RN9', StrSubstNo(OperationSuccessTelemetryMsg, 'MoveItem'), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
/// <summary>
/// Moves a drive item (file or folder) to a new location by path.
/// </summary>
/// <param name="ItemPath">Path to the item (e.g., 'Documents/file.docx').</param>
/// <param name="TargetFolderPath">Path to the target folder (optional).</param>
/// <param name="NewName">New name for the moved item (optional).</param>
/// <returns>An operation response object containing the result of the operation.</returns>
procedure MoveItemByPath(ItemPath: Text; TargetFolderPath: Text; NewName: Text): Codeunit "SharePoint Graph Response"
var
GraphDriveItem: Record "SharePoint Graph Drive Item";
TargetFolderItem: Record "SharePoint Graph Drive Item";
SharePointGraphResponse: Codeunit "SharePoint Graph Response";
TargetFolderId: Text;
begin
EnsureInitialized();
EnsureSiteId();
EnsureDefaultDriveId();
SharePointGraphResponse.SetRequestHelper(SharePointGraphRequestHelper);
// Validate input
if ItemPath = '' then begin
SharePointGraphResponse.SetError(InvalidFilePathErr);
Session.LogMessage('0000RNA', InvalidFilePathErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// At least one of target folder or new name must be provided
if (TargetFolderPath = '') and (NewName = '') then begin
SharePointGraphResponse.SetError(InvalidTargetPathErr);
Session.LogMessage('0000RNB', InvalidTargetPathErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GraphSharePointCategoryLbl);
exit(SharePointGraphResponse);
end;
// Get the source item ID
SharePointGraphResponse := GetDriveItemByPath(ItemPath, GraphDriveItem);
if not SharePointGraphResponse.IsSuccessful() then
exit(SharePointGraphResponse);
// Get the target folder ID if provided
if TargetFolderPath <> '' then begin
SharePointGraphResponse := GetDriveItemByPath(TargetFolderPath, TargetFolderItem);
if not SharePointGraphResponse.IsSuccessful() then
exit(SharePointGraphResponse);
TargetFolderId := TargetFolderItem.Id;
end;
// Now call MoveItem with IDs
exit(MoveItem(GraphDriveItem.Id, TargetFolderId, NewName));
end;
#endregion
/// <summary>
/// Returns detailed information on last API call.
/// </summary>
/// <returns>Codeunit holding http response status, reason phrase, headers and possible error information for the last API call</returns>
procedure GetDiagnostics(): Interface "HTTP Diagnostics"
begin
exit(SharePointGraphRequestHelper.GetDiagnostics());
end;
/// <summary>
/// Sets the site ID directly for testing purposes.
/// </summary>
/// <param name="NewSiteId">The site ID to set.</param>
internal procedure SetSiteIdForTesting(NewSiteId: Text)
begin
SiteId := NewSiteId;
SharePointGraphUriBuilder.Initialize(SiteId, SharePointGraphRequestHelper);
end;
/// <summary>
/// Sets the default drive ID directly for testing purposes.
/// </summary>
/// <param name="NewDefaultDriveId">The default drive ID to set.</param>
internal procedure SetDefaultDriveIdForTesting(NewDefaultDriveId: Text)
begin
DefaultDriveId := NewDefaultDriveId;
end;
// Add this method to lazily load the default drive ID
local procedure EnsureDefaultDriveId()
begin
if DefaultDriveId = '' then
GetDefaultDriveId();
end;
// Add method to lazily load the site ID
local procedure EnsureSiteId()
begin
if SiteId = '' then begin
GetSiteIdFromUrl(SharePointUrl);
SharePointGraphUriBuilder.Initialize(SiteId, SharePointGraphRequestHelper);
end;
end;
}Procedures, 51
| Name | Parameters | Returns | Access | Obsolete |
|---|---|---|---|---|
| Initialize | (Text, Interface Graph Authorization) | public | - | |
| Initialize | (Text, Enum Graph API Version, Interface Graph Authorization) | public | - | |
| Initialize | (Text, Text, Interface Graph Authorization) | public | - | |
| Initialize | (Text, Enum Graph API Version, Interface Graph Authorization, Interface Http Client Handler) | public | - | |
| GetLists | (var Record SharePoint Graph List) | Codeunit SharePoint Graph Response | public | - |
| GetLists | (var Record SharePoint Graph List, Codeunit Graph Optional Parameters) | Codeunit SharePoint Graph Response | public | - |
| GetList | (Text, var Record SharePoint Graph List) | Codeunit SharePoint Graph Response | public | - |
| GetList | (Text, var Record SharePoint Graph List, Codeunit Graph Optional Parameters) | Codeunit SharePoint Graph Response | public | - |
| CreateList | (Text, Text, var Record SharePoint Graph List) | Codeunit SharePoint Graph Response | public | - |
| CreateList | (Text, Text, Text, var Record SharePoint Graph List) | Codeunit SharePoint Graph Response | public | - |
| GetListItems | (Text, var Record SharePoint Graph List Item) | Codeunit SharePoint Graph Response | public | - |
| GetListItems | (Text, var Record SharePoint Graph List Item, Codeunit Graph Optional Parameters) | Codeunit SharePoint Graph Response | public | - |
| CreateListItem | (Text, JsonObject, var Record SharePoint Graph List Item) | Codeunit SharePoint Graph Response | public | - |
| CreateListItem | (Text, Text, var Record SharePoint Graph List Item) | Codeunit SharePoint Graph Response | public | - |
| GetDefaultDrive | (var Text) | Codeunit SharePoint Graph Response | public | - |
| GetDrives | (var Record SharePoint Graph Drive) | Codeunit SharePoint Graph Response | public | - |
| GetDrives | (var Record SharePoint Graph Drive, Codeunit Graph Optional Parameters) | Codeunit SharePoint Graph Response | public | - |
| GetDrive | (Text, var Record SharePoint Graph Drive) | Codeunit SharePoint Graph Response | public | - |
| GetDrive | (Text, var Record SharePoint Graph Drive, Codeunit Graph Optional Parameters) | Codeunit SharePoint Graph Response | public | - |
| GetDefaultDrive | (var Record SharePoint Graph Drive) | Codeunit SharePoint Graph Response | public | - |
| GetRootItems | (var Record SharePoint Graph Drive Item) | Codeunit SharePoint Graph Response | public | - |
| GetRootItems | (var Record SharePoint Graph Drive Item, Codeunit Graph Optional Parameters) | Codeunit SharePoint Graph Response | public | - |
| GetFolderItems | (Text, var Record SharePoint Graph Drive Item) | Codeunit SharePoint Graph Response | public | - |
| GetFolderItems | (Text, var Record SharePoint Graph Drive Item, Codeunit Graph Optional Parameters) | Codeunit SharePoint Graph Response | public | - |
| GetItemsByPath | (Text, var Record SharePoint Graph Drive Item) | Codeunit SharePoint Graph Response | public | - |
| GetItemsByPath | (Text, var Record SharePoint Graph Drive Item, Codeunit Graph Optional Parameters) | Codeunit SharePoint Graph Response | public | - |
| UploadFile | (Text, Text, Text, InStream, var Record SharePoint Graph Drive Item, Enum Graph ConflictBehavior) | Codeunit SharePoint Graph Response | public | - |
| UploadFile | (Text, Text, Text, InStream, var Record SharePoint Graph Drive Item) | Codeunit SharePoint Graph Response | public | - |
| UploadLargeFile | (Text, Text, Text, InStream, var Record SharePoint Graph Drive Item) | Codeunit SharePoint Graph Response | public | - |
| UploadLargeFile | (Text, Text, Text, InStream, var Record SharePoint Graph Drive Item, Enum Graph ConflictBehavior) | Codeunit SharePoint Graph Response | public | - |
| CreateFolder | (Text, Text, Text, var Record SharePoint Graph Drive Item, Enum Graph ConflictBehavior) | Codeunit SharePoint Graph Response | public | - |
| CreateFolder | (Text, Text, Text, var Record SharePoint Graph Drive Item) | Codeunit SharePoint Graph Response | public | - |
| DownloadFile | (Text, var Codeunit Temp Blob) | Codeunit SharePoint Graph Response | public | - |
| DownloadFileByPath | (Text, var Codeunit Temp Blob) | Codeunit SharePoint Graph Response | public | - |
| DownloadLargeFile | (Text, var Codeunit Temp Blob) | Codeunit SharePoint Graph Response | public | - |
| DownloadLargeFileByPath | (Text, var Codeunit Temp Blob) | Codeunit SharePoint Graph Response | public | - |
| GetDriveItem | (Text, var Record SharePoint Graph Drive Item) | Codeunit SharePoint Graph Response | public | - |
| GetDriveItemByPath | (Text, var Record SharePoint Graph Drive Item) | Codeunit SharePoint Graph Response | public | - |
| GetDriveItemByPath | (Text, var Record SharePoint Graph Drive Item, Codeunit Graph Optional Parameters) | Codeunit SharePoint Graph Response | public | - |
| GetDriveItem | (Text, var Record SharePoint Graph Drive Item, Codeunit Graph Optional Parameters) | Codeunit SharePoint Graph Response | public | - |
| DeleteItem | (Text) | Codeunit SharePoint Graph Response | public | - |
| DeleteItemByPath | (Text) | Codeunit SharePoint Graph Response | public | - |
| ItemExists | (Text, var Boolean) | Codeunit SharePoint Graph Response | public | - |
| ItemExistsByPath | (Text, var Boolean) | Codeunit SharePoint Graph Response | public | - |
| CopyItem | (Text, Text, Text) | Codeunit SharePoint Graph Response | public | - |
| CopyItemByPath | (Text, Text, Text) | Codeunit SharePoint Graph Response | public | - |
| MoveItem | (Text, Text, Text) | Codeunit SharePoint Graph Response | public | - |
| MoveItemByPath | (Text, Text, Text) | Codeunit SharePoint Graph Response | public | - |
| GetDiagnostics | () | Interface HTTP Diagnostics | public | - |
| SetSiteIdForTesting | (Text) | internal | - | |
| SetDefaultDriveIdForTesting | (Text) | internal | - |