Commonly Occurring Errors in Microsoft Graph Integrations and How to Troubleshoot Them (Part 3)
This third article explains common integration errors that may be seen in the transition from EWS to Microsoft Graph as to the resource type To Do Tasks.
Join the DZone community and get the full member experience.
Join For FreeUnlike the first and second articles of the series, this article not only covers case studies related to To Do Tasks, but also discusses what happened to To Do Tasks in the last months of 2022 regarding the accessibility via the MS Graph API v1.0.
Types of Accessibility
Source: Microsoft Documentation
There are two types to access a protected resource via the MS Graph API’s. The delegated access and the app-only access. What does it mean?
Assume there is an application A that uses delegated access, and the resource owner must consent to or deny your application request. Application A acts on behalf of a signed-in user.
Assume there is an application B that uses app-only access. No authorization from the resource owner is required. Application B acts only as the application’s own identity.
Let’s take a closer look at these two types.
Type 1—Delegated Access
The client application A accesses the resource on behalf of the user. This type of access requires delegated permissions. To make the request, the client and user must be authorized interdependently. The client application A must have the correct delegated permissions (and must also be granted). Regarding user permission, the authorization relies on the privileges the user has been granted for them to access the resource.
Type 2—App-Only Access
The client application B acts on its own with no user signed in. There are many scenarios to use application access, such as automation. Most of these applications run as background services or daemons. Unlike application A, apps like B use app roles instead of delegated scopes to define permissions. For app-only access, the client application B must have the correct app roles of the resource it’s calling to access the data.
Recent Changes of the Accessibility of To Do Tasks via the MS Graph API v1.0
The resource type To Do Tasks was introduced in August 2020 for MS Graph Beta and in October 2020 for Microsoft Graph v1.0 (production environment). At that time, app-only access to To Do Tasks via the MS Graph API v1.0 was not possible.
At the last Microsoft Build conference in 2022, the MS Graph developers announced this feature will be available by September 2022. According to the change notes of the MS Graph Java SDK 5.25.0, To Do Tasks was now available within the Java SDK. But, still, at that time, app-only access was not possible (according to the Microsoft documentation and my own tests in May.)
It should take more than six months until accessibility of To Do Tasks via the MS Graph v1.0 at the application level was possible. Finally, with the changes in December 2022, Microsoft has also released the long-requested feature. Unfortunately, the Microsoft documentation is not updated yet or there is no other remark in the change notes about this very important feature.
Attach Files to To Do Tasks
Assume there is an application B that uses app-only access. That means no authorization from the resource owner is required. Further, a To Do task T was already created and has the ID taskID
.
According to Microsoft documentation, only attachments up to 25MB can be uploaded and attached to To Do tasks. Attachments up to 3MB in size can be uploaded with a single POST
call. The following example shows how to create such a call within the Java SDK:
final String primaryUser = "office365userEmail";
final String taskID = "taskID";
final GraphServiceClient<Request> graphClient = GraphServiceClient
.builder()
.authenticationProvider(getAuthenticationProvider())
.buildClient();
String defaultTodoTaskListId = getDefaultTodoTaskListId();
final AttachmentBase attachmentBase = graphService
.getGraphServiceClient()
.users(primaryUser)
.todo()
.lists(defaultTodoTaskListId)
.tasks(taskID)
.attachments()
.buildRequest()
.post(getAttachment());
After creating the client graphClient
, the application must create a call to get the ID number of the To Do Task List that contains the To Do task T. In this case, it is the default To Do task list. The following code shows how to create such a call (getDefaultTodoTaskListId()
):
Objects.requireNonNull(service
.getGraphServiceClient()
.users(primeryID)
.todo()
.lists()
.buildRequest()
.get())
.getCurrentPage()
.stream()
.filter(e -> e.wellknownListName == WellknownListName.DEFAULT_LIST)
.findFirst()
.ifPresent(todoTaskList -> defaultTodoTaskListId = todoTaskList.id);
As parameter of the POST request (getAttachment()
), we paste an object of the type TaskFileAttachment
.
TaskFileAttachment attachmentBase = new TaskFileAttachment();
attachmentBase.name = attachment.getFilename();
attachmentBase.contentBytes = attachment.getContent();
attachmentBase.contentType = attachment.getMimeType();
For attachments between 3MB and 25MB, similar to the way how to upload large attachments for events (check my first article), we alson create an upload session and upload these attachments piece by piece via multiple PUT
calls. Let us consider the following example:
final String primaryUser = "office365userEmail";
final String taskID = "taskID";
String defaultTodoTaskListId = getDefaultTodoTaskListId();
final GraphServiceClient<Request> graphClient = GraphServiceClient
.builder()
.authenticationProvider(getAuthenticationProvider())
.buildClient();
final File file = File.createTempFile("testFile", "txt");
FileUtils.writeByteArrayToFile(file, "testContent".getBytes());
InputStream fileStream = new FileInputStream(file);
final AttachmentInfo attachmentInfo = new AttachmentInfo();
attachmentInfo.attachmentType = AttachmentType.FILE;
attachmentInfo.name = file.getName();
attachmentInfo.size = file.getTotalSpace();
final AttachmentBaseCreateUploadSessionParameterSet attachmentBaseCreateUploadSessionParameterSet =
AttachmentBaseCreateUploadSessionParameterSet
.newBuilder()
.withAttachmentInfo(attachmentInfo)
.build();
final UploadSession uploadSession = graphService.getGraphServiceClient()
.users(primaryUser)
.todo()
.lists(defaultTodoTaskListId)
.tasks(taskID)
.attachments()
.createUploadSession(attachmentBaseCreateUploadSessionParameterSet)
.buildRequest()
.post();
uploadSession.uploadUrl = uploadSession.uploadUrl + "/content";
// Called after each slice of the file is uploaded
final IProgressCallback callback =
(current, max) -> log.info("Uploaded {} bytes of {} total bytes", current, max);
final LargeFileUploadTask<AttachmentInfo> uploadTask =
new LargeFileUploadTask<>(uploadSession, graphService.getGraphServiceClient(),
fileStream, file.length(), AttachmentInfo.class);
// upload (default: chunkSize is 5 MB)
uploadTask.upload(0, null, callback);
The example is very simple and self-explanatory and is not really different from the way large attachments are uploaded for events. First, we create a upload sesssion uploadSession
via a POST
request. A possible response to the POST
request could look as follows:
HTTP/1.1 200 OK
Content-Type: application/json
{
"@odata.context": "https://graph.microsoft.com/beta/$metadata#microsoft.graph.uploadSession",
"uploadUrl": "https://graph.microsoft.com/beta/users/6f9a2a92-8527-4d64-837e-b5312852f36d/todo/lists/AAMDiFkfh=/tasks/AAMkADliMm=/attachmentSessions/AAMkADliMm=",
"expirationDateTime": "2022-06-09T10:45:27.4324526Z",
"nextExpectedRanges": [
"0-"
]
}
As a response to the POST
request, you get an upload link uploadURL
, which can be used to upload the attachment. Be aware, you have to extend the upload URL uploadURL
with the keyword “content.” If not, you will receive a return code 404, the attachment session can’t be found. In the final step, we use multiple PUT
calls to upload the attachment.
In both cases, MS Events and MS To Do Tasks, we create an upload session and use multiple PUT
calls to upload large attachments. But, there are still differences.
- Size of file: For events, you can upload attachments with a maximum size of 150 MB. For tasks, you only can upload files of a maximum size of 25 MB.
- Upload URL: For events, you upload the attachment via a Outlook API. For tasks, you upload the attachment via an MS Graph API and you must extend the upload URL with “content.”
Unlike events, you won’t see an issue, such as if you upload an attachment to a task since the upload is carried out via an MS Graph API:
401 : Unauthorized upon request. com.microsoft.graph.core.ClientException:
Error code: InvalidAudienceForResource Error message:
The audience claim value is invalid for current resource.
Audience claim is 'https://graph.microsoft.com/', request url is
'https://outlook.office.com/api/v2.0/Users...'.
Conclusion
From the perspective of a developer, in the future, I hope to see Microsoft invest more attention and a better way to upload large attachements.
By now, you should have a better understanding of the common integration errors that may be seen in the transition from EWS to Microsoft Graph as to the resource type To Do Tasks. I hope this article was informative and useful.
Opinions expressed by DZone contributors are their own.
Comments