Skip to content

[DRAFT] SharePoint Graph API support #3655

New issue

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

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

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

Drakonian
Copy link
Contributor

@Drakonian Drakonian commented May 7, 2025

Summary

This is the first version of the SharePoint Graph API module that leverages the Microsoft Graph module.

After analyzing the current REST implementation, I concluded that it cannot be changed without breaking backward compatibility. In addition, the REST and Graph APIs have completely different data models and sets of fields, which makes it impossible to reuse the same tables for entities that REST uses.

Therefore, adding a separate SharePoint Graph Client seemed the simplest and most natural solution. I also considered factory/builder patterns and dependency injection, but for the reasons mentioned above these approaches did not seem optimal, they don’t fit well and would greatly complicate the overall use of the SharePoint module.

I have already tested this code locally in various scenarios, and it behaves well. My plans are:

  • Detail review of all the code with improvements where necessary
  • Gathering feedback from the community and Microsoft
  • Test pagination
  • Test large file uploads
  • Restructuring the folders so that REST and Graph reside in separate, peer-level folders
  • Writing tests

Examples of how to use:

local procedure GetDrivesFromSite()
var
    TempGraphDrive: Record "SharePoint Graph Drive" temporary;
    SharePointGraphClient: Codeunit "SharePoint Graph Client";
    GraphAuthorization: Codeunit "Graph Authorization";
    GraphAuthInterface: Interface "Graph Authorization";
begin
    GraphAuthInterface := GraphAuthorization.CreateAuthorizationWithClientCredentials(
        TenantIdValue,
        ClientIdValue,
        ClientSecretValue,
        GraphScopeLbl);

    SharePointGraphClient.Initialize(SharePointUrlValue, GraphAuthInterface);

    if not SharePointGraphClient.GetDrives(TempGraphDrive) then
        Message('No drives found.');

    if TempGraphDrive.FindSet() then
        repeat
            Message('Drive Name: %1\Type: %2\Owner: %3\Quota Used: %4 of %5\Created: %6',
                TempGraphDrive.Name,
                TempGraphDrive.DriveType,
                TempGraphDrive.OwnerName,
                TempGraphDrive.QuotaUsed,
                TempGraphDrive.QuotaTotal,
                TempGraphDrive.CreatedDateTime);
        until TempGraphDrive.Next() = 0;
end;
local procedure UploadFileToRootDriveFolder()
var
    TempGraphDriveItem: Record "SharePoint Graph Drive Item" temporary;
    SharePointGraphClient: Codeunit "SharePoint Graph Client";
    GraphAuthorization: Codeunit "Graph Authorization";
    GraphAuthInterface: Interface "Graph Authorization";
    Diagnostics: Interface "HTTP Diagnostics";
    FileInStream: InStream;
    FileName: Text;
    FolderPath: Text;
begin
    GraphAuthInterface := GraphAuthorization.CreateAuthorizationWithClientCredentials(
        TenantIdValue,
        ClientIdValue,
        ClientSecretValue,
        GraphScopeLbl);

    SharePointGraphClient.Initialize(SharePointUrlValue, GraphAuthInterface);

    FolderPath := 'TestFolder'; //To root drive folder as we not specified DriveId
    if not UploadIntoStream('Select a file to upload', '', '', FileName, FileInStream) then
        exit;

    if SharePointGraphClient.UploadFile(FolderPath, FileName, FileInStream, TempGraphDriveItem) then
        Message('File uploaded successfully: %1', TempGraphDriveItem.Name)
    else begin
        Diagnostics := SharePointGraphClient.GetDiagnostics();
        Error('Error: %1 - %2', Diagnostics.GetHttpStatusCode(), Diagnostics.GetResponseReasonPhrase());
    end;
end;

Work Item(s)

Fixes #3198

Fixes AB#568746

@github-actions github-actions bot added AL: System Application From Fork Pull request is coming from a fork labels May 7, 2025
@Drakonian
Copy link
Contributor Author

@pri-kise

@github-actions github-actions bot added the Linked Issue is linked to a Azure Boards work item label May 7, 2025
@github-actions github-actions bot added this to the Version 27.0 milestone May 7, 2025
Drakonian added 2 commits May 15, 2025 13:31
…ific requests

Bound SharePointDiagnostics with response information
Add ability to upload large files in 4mb chunks
@JesperSchulz
Copy link
Contributor

Very interesting contribution! Please allow me to take a much closer look at your code in the coming days. Unfortunately I will be on the road next week, so this might take me a short while to get to - but I'll be on it soon!

@JesperSchulz JesperSchulz self-assigned this May 22, 2025
@JesperSchulz JesperSchulz added the Integration GitHub request for Integration area label May 22, 2025
@Drakonian
Copy link
Contributor Author

Very interesting contribution! Please allow me to take a much closer look at your code in the coming days. Unfortunately I will be on the road next week, so this might take me a short while to get to - but I'll be on it soon!

Thank you, I’ll look forward to your feedback -)

Copy link
Contributor

@pri-kise pri-kise left a comment

Choose a reason for hiding this comment

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

First of all: I really appreciate the work you have done for this PR.

I'm unsure if maybe some of the functionalities in the graph helper codeunits should maybe better be placed into the graph client module.
I only took a short look into the new code so far. I will need some more time to look into the changes in more detail.

Update:
I'm not sure if it's really helpful to provide a boolean value as return value on all those functions.
How should anyone know why the got false.

I think that it would be more helpful to return errors when something didn't went well.

Furthermore on your planned improvements for pagination:
I'm not sure if this is possible with the GraphOptionalParameters, but I think that it would be helpful if we could specify a range from outside, too.
What I mean is that sometimes we might want to control the pagination steps from outside.

Return repsonse object instead of unclear boolean
Additional inetrnal methods for testing
@Drakonian
Copy link
Contributor Author

@pri-kise

Thank you for such a helpful review of the PR. Your remarks are very useful.

  1. I added an HTTP client handler, of course, it is absolutely essential here.
  2. I completely agree with your point about the boolean, it's a relic from an older code version where I was experimenting with building a SharePoint Graph module (trying to merge with the SharePoint REST module). I prefer the approach used in the Azure Blob Storage API, where a method returns an object containing detailed information about the operation, so I’ve applied a similar pattern here.

Regarding managing pagination externally, that’s an excellent observation, but I’m still considering the best way to implement it. I’ll think through an approach that is convenient.

I believe pagination would be best handled at the Microsoft Graph module level, this needs some further thought.

My plans: at the moment I intend to keep improving the code and finish writing the tests.

@JesperSchulz JesperSchulz changed the title SharePoint Graph API support [DRAFT] SharePoint Graph API support Jun 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
AL: System Application From Fork Pull request is coming from a fork Integration GitHub request for Integration area Linked Issue is linked to a Azure Boards work item
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[BC Idea]: SharePoint - add graph API support
3 participants