DocSpace-client/thirdparty/Microsoft.Graph.Core/Requests/DeltaResponseHandler.cs
2020-07-10 18:37:02 +03:00

196 lines
7.8 KiB
C#

// ------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
// ------------------------------------------------------------------------------
namespace Microsoft.Graph
{
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
/// <summary>
/// PREVIEW
/// A response handler that exposes the list of changes returned in a response.
/// This supports scenarios where the service expresses changes to 'null'. The
/// deserializer can't express changes to null so you can now discover if a property
/// has been set to null. This is intended for use with a Delta query scenario.
/// </summary>
public class DeltaResponseHandler : IResponseHandler
{
private readonly ISerializer serializer;
/// <summary>
/// Constructs a new <see cref="ResponseHandler"/>.
/// </summary>
public DeltaResponseHandler()
{
this.serializer = new Serializer();
}
/// <summary>
/// Process raw HTTP response into requested domain type.
/// </summary>
/// <typeparam name="T">The type to return</typeparam>
/// <param name="response">The HttpResponseMessage to handle</param>
/// <returns></returns>
public async Task<T> HandleResponse<T>(HttpResponseMessage response)
{
if (response.Content != null)
{
// Gets the response string with response headers and status code
// set on the response body object.
var responseString = await GetResponseString(response);
// Get the response body object with the change list
// set on each response item.
JObject responseBody = await GetResponseBodyWithChangelist(responseString);
return responseBody.ToObject<T>();
}
return default(T);
}
/// <summary>
/// Get the response content string
/// </summary>
/// <param name="hrm">The response object</param>
/// <returns>The full response string to return</returns>
private async Task<string> GetResponseString(HttpResponseMessage hrm)
{
var responseContent = "";
var content = await hrm.Content.ReadAsStringAsync().ConfigureAwait(false);
//Only add headers and the changed list if we are going to return a response body
if (content.Length > 0)
{
// Add headers
var responseHeaders = hrm.Headers;
var statusCode = hrm.StatusCode;
Dictionary<string, string[]> headerDictionary = responseHeaders.ToDictionary(x => x.Key, x => x.Value.ToArray());
var responseHeaderString = serializer.SerializeObject(headerDictionary);
responseContent = content.Substring(0, content.Length - 1) + ", ";
responseContent += "\"responseHeaders\": " + responseHeaderString + ", ";
responseContent += "\"statusCode\": \"" + statusCode + "\"}";
}
return responseContent;
}
/// <summary>
/// Gets the response with change lists set on each item.
/// </summary>
/// <param name="deltaResponseBody">The entire response body as a string.</param>
/// <returns>A task with a JObject represention of the response body. The changes are set on each response item.</returns>
private async Task<JObject> GetResponseBodyWithChangelist(string deltaResponseBody)
{
// This is the JObject that we will replace. We should probably
// return a string instead.
JObject responseJObject = JObject.Parse(deltaResponseBody);
// An array of delta objects. We will need to process
// each one independently of each other.
var pageOfDeltaObjects = responseJObject["value"] as JArray;
if (pageOfDeltaObjects is null)
{
return responseJObject;
}
JArray updatedObjectsWithChangeList = new JArray();
for (int i = 0; i < pageOfDeltaObjects.Count(); i++)
{
// Go inspect all of the properties in the responseItem
var updatedObject = await DiscoverChangedProperties(pageOfDeltaObjects[i] as JObject);
updatedObjectsWithChangeList.Add(updatedObject);
}
// Replace the original page of changed items with a page of items that
// have a self describing change list.
responseJObject["value"].Replace(updatedObjectsWithChangeList);
return responseJObject;
}
/// <summary>
/// Inspects the response item and captures the list of properties on a new property
/// named 'changes'.
/// </summary>
/// <param name="responseItem">The item to inspect for properties.</param>
/// <returns>The item with the 'changes' property set on it.</returns>
private async Task<JObject> DiscoverChangedProperties(JObject responseItem)
{
// List of changed properties.
JArray changes = new JArray();
// Get the list of changed properties on the item.
await GetObjectProperties(responseItem, changes);
// Add the changes object to the response item.
responseItem.Add("changes", changes);
return responseItem;
}
/// <summary>
/// Gets all changes on the object.
/// </summary>
/// <param name="changedObject">The responseItem to inspect for changes.</param>
/// <param name="changes">The list of properties returned in the response.</param>
/// <param name="parentName">The parent object of this changed object.</param>
/// <returns></returns>
private async Task GetObjectProperties(JObject changedObject, JArray changes, string parentName = "")
{
if (parentName != string.Empty)
{
parentName += ".";
}
foreach (JProperty property in changedObject.Children())
{
if (property.Value is JObject)
{
string parent = parentName + property.Name;
await GetObjectProperties(property.Value as JObject, changes, parent);
}
else if (property.Value is JArray)
{
string parent = parentName + property.Name;
JArray collection = (property.Value as JArray);
for (int i = 0; i < collection.Count(); i++)
{
string parentWithIndex = $"{parent}[{i}]";
if (collection[i] is JObject)
{
JObject collectionItem = collection[i] as JObject;
await GetObjectProperties(collectionItem, changes, parentWithIndex);
}
else // Assuming that this is a JValue.
{
changes.Add(parentWithIndex);
}
}
}
else if (property.Value is JValue)
{
var name = parentName + property.Name;
changes.Add(name);
}
else
{
throw new NotImplementedException("Case is not a JObject, JArray, or JProperty");
}
}
}
}
}