-
Notifications
You must be signed in to change notification settings - Fork 265
Description
Describe the bug
Wanting to create a SharePoint site using Sites.PostAsync(), and waiting for the site to provision.
According to the Beta docs, this should return a site object, but it's null which makes sense as the site is created asynchronously. But the response contains a Location header which we can use to get the status of the site creation.
Unfortunately, the current iteration of the SDK (5.125.0-preview) doesn't allow access to this Location header.
Is there some funky thing we can do to the GraphServiceClient instance to get the Location header?
Expected behavior
The SDK to either:
- Return a Tuple of the
Site?and theUriof the Location header, so that we can proceed to repeatedly ping this every 5 seconds or so until the site is created or otherwise errors. - Add property to the returned
Site?instance, perhaps extended with a property that has theUriof the Location header.
How to reproduce
var site = await _delegatedGraphService.Sites.PostAsync(new GraphBeta.Models.Site
{
Name = "My Newly Created Site",
DisplayName = "My Newly Created Site",
Description = "My Newly Created Site",
WebUrl = "https://tenant.sharepoint.com/sites/MyNewSite",
Locale = "en-US",
Template = SiteTemplateType.Sitepagepublishing
});
// This object instance is always null, but the site will be available after a few seconds
var siteId = site.Id;
SDK Version
5.125.0-preview
Latest version known to work for scenario above?
No response
Known Workarounds
I managed to get a very clunky workaround in place.
- Create a custom
DelegatingHandler, this will test the response for a Location header and put its value into a public string property. - Instantiate an instance of this custom handler, and have it's instance publicly available in the class that is using
GraphServiceClient - Create an instance of
GraphServiceClientin a way that allows the use of the custom Delegating handler
When I make a request to Sites.PostAsync(), I then inspect the public string property of the custom DelegatingHandler to see if it's non-null - and consume that. I then use regex to extract just the operationId value.
public class CustomGraphDelegatingHandler : DelegatingHandler
{
/// <summary>
/// The value of the "Location" header from this request
/// </summary>
public string LocationHeader { get; set; } = string.Empty;
/// <inheritdoc/>
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
LocationHeader = string.Empty;
var response = await base.SendAsync(request, cancellationToken);
if (response.Headers.TryGetValues("Location", out var values))
{
LocationHeader = values.First();
}
return response;
}
}
And then in the class that is setting up GraphServiceClient
this.customHandler = new CustomGraphDelegatingHandler ();
var handlers = GraphClientFactory.CreateDefaultHandlers();
handlers.Add(this.customHandler);
var http = GraphClientFactory.Create(handlers);
graphServiceClient = new GraphBeta.GraphServiceClient(http, authProvider, graphEndpoint);
Thus, after making the call to Sites.PostAsync, I get the "Location" property out of this.customHandler, if it's there, use regex to pull out operationId, then issue a call to GetOperationStatusWithOperationId to check the status for "succeeded". If it's "Failed" I throw an exception, if it's anything else I sleep with Task.Delay(5000); then try again.
Pretty messy. Would be nice if there was a quicker way to get to the response object from the call, though.
Debug output
No response
Configuration
No response
Other information
No response