Skip to content

Commit

Permalink
[Core.Experimental] Add DynamicContent/DynamicRequest/DynamicResponse…
Browse files Browse the repository at this point in the history
  • Loading branch information
chamons authored and jongio committed Feb 9, 2021
1 parent 44f43bc commit 0faeb4f
Show file tree
Hide file tree
Showing 4 changed files with 344 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
namespace Azure.Core
{
[System.Diagnostics.DebuggerDisplayAttribute("Content: {_body}")]
public partial class DynamicContent : Azure.Core.RequestContent
{
internal DynamicContent() { }
public override void Dispose() { }
public override bool TryComputeLength(out long length) { throw null; }
public override void WriteTo(System.IO.Stream stream, System.Threading.CancellationToken cancellation) { }
public override System.Threading.Tasks.Task WriteToAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellation) { throw null; }
}
public partial class DynamicJson : System.Dynamic.IDynamicMetaObjectProvider
{
public DynamicJson(string json) { }
Expand Down Expand Up @@ -56,6 +65,40 @@ public DynamicJson(System.Text.Json.JsonElement element) { }
public override string ToString() { throw null; }
public void WriteTo(System.Text.Json.Utf8JsonWriter writer) { }
}
[System.Diagnostics.DebuggerDisplayAttribute("Body: {Body}")]
public partial class DynamicRequest : Azure.Core.Request
{
public DynamicRequest(Azure.Core.Request request, Azure.Core.Pipeline.HttpPipeline pipeline) { }
public Azure.Core.DynamicJson Body { get { throw null; } set { } }
public override string ClientRequestId { get { throw null; } set { } }
public override Azure.Core.RequestContent? Content { get { throw null; } set { } }
protected override void AddHeader(string name, string value) { }
protected override bool ContainsHeader(string name) { throw null; }
public override void Dispose() { }
protected virtual void Dispose(bool disposing) { }
protected override System.Collections.Generic.IEnumerable<Azure.Core.HttpHeader> EnumerateHeaders() { throw null; }
protected override bool RemoveHeader(string name) { throw null; }
public Azure.Core.DynamicResponse Send(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public System.Threading.Tasks.Task<Azure.Core.DynamicResponse> SendAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
protected override bool TryGetHeader(string name, out string? value) { throw null; }
protected override bool TryGetHeaderValues(string name, out System.Collections.Generic.IEnumerable<string>? values) { throw null; }
}
[System.Diagnostics.DebuggerDisplayAttribute("Status: {Response.Status}, Value: {Value}")]
public partial class DynamicResponse : Azure.Response
{
public DynamicResponse(Azure.Response response, Azure.Core.DynamicJson? body) { }
public Azure.Core.DynamicJson? Body { get { throw null; } }
public override string ClientRequestId { get { throw null; } set { } }
public override System.IO.Stream? ContentStream { get { throw null; } set { } }
public override string ReasonPhrase { get { throw null; } }
public override int Status { get { throw null; } }
protected override bool ContainsHeader(string name) { throw null; }
public override void Dispose() { }
protected virtual void Dispose(bool disposing) { }
protected override System.Collections.Generic.IEnumerable<Azure.Core.HttpHeader> EnumerateHeaders() { throw null; }
protected override bool TryGetHeader(string name, out string? value) { throw null; }
protected override bool TryGetHeaderValues(string name, out System.Collections.Generic.IEnumerable<string>? values) { throw null; }
}
}
namespace Azure.Core.GeoJson
{
Expand Down
60 changes: 60 additions & 0 deletions sdk/core/Azure.Core.Experimental/src/DynamicContent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

namespace Azure.Core
{
/// <summary>
/// Represents the <see cref="DynamicJson"/> sent as part of the Azure.Core.Request.
/// </summary>
[DebuggerDisplay("Content: {_body}")]
public class DynamicContent : RequestContent
{
private readonly DynamicJson _body;

internal DynamicContent(DynamicJson body)
{
_body = body;
}

internal static RequestContent Create(DynamicJson body) => new DynamicContent(body);

/// <inheritdoc />
public override async Task WriteToAsync(Stream stream, CancellationToken cancellation)
{
using Utf8JsonWriter writer = new Utf8JsonWriter(stream);
_body.WriteTo(writer);
await writer.FlushAsync(cancellation).ConfigureAwait(false);
}

/// <inheritdoc />
public override void WriteTo(Stream stream, CancellationToken cancellation)
{
using Utf8JsonWriter writer = new Utf8JsonWriter(stream);
_body.WriteTo(writer);
writer.Flush();
}

/// <inheritdoc />
public override bool TryComputeLength(out long length)
{
using MemoryStream stream = new MemoryStream();
WriteTo(stream, CancellationToken.None);
length = Encoding.UTF8.GetString(stream.ToArray()).Length;
return true;
}

/// <inheritdoc />
public override void Dispose()
{
GC.SuppressFinalize(this);
}
}
}
159 changes: 159 additions & 0 deletions sdk/core/Azure.Core.Experimental/src/DynamicRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Azure.Core;
using Azure.Core.Pipeline;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

namespace Azure.Core
{
/// <summary>
/// Represents an HTTP request with <see cref="DynamicJson"/> content.
/// </summary>
[DebuggerDisplay("Body: {Body}")]
public class DynamicRequest : Request
{
private Request Request { get; }
private HttpPipeline HttpPipeline { get; }
private bool _disposed;

private static readonly Encoding Utf8NoBom = new UTF8Encoding(false, true);

/// <inheritdoc />
public override RequestContent? Content
{
get => DynamicContent.Create(Body);

set {
MemoryStream ms = new MemoryStream();
if (value != null)
{
value.WriteTo(ms, default);
ms.Seek(0, SeekOrigin.Begin);
}
using (StreamReader sr = new StreamReader(ms, Utf8NoBom))
{
Body = new DynamicJson(sr.ReadToEnd());
}
Request.Content = value;
}
}

// TODO(matell): How does the initialization here play into the ability to send a request with an "empty" body?
/// <summary>
/// The JSON body of request.
/// </summary>
public DynamicJson Body { get; set; } = DynamicJson.Object();

// TODO(matell): In Krzysztof's prototype we also took DiagnosticScope as a parameter, do we still need that?
/// <summary>
/// Creates an instance of <see cref="RequestContent"/> that wraps <see cref="DynamicJson"/> content.
/// </summary>
/// <param name="request">The <see cref="Request"/> to send.</param>
/// <param name="pipeline">The HTTP pipeline for sending and receiving REST requests and responses.</param>
public DynamicRequest(Request request, HttpPipeline pipeline)
{
Request = request;
HttpPipeline = pipeline;
}

/// <summary>
/// Send the request asynchronously.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The response dynamically typed in a <see cref="DynamicResponse"/>.</returns>
public async Task<DynamicResponse> SendAsync(CancellationToken cancellationToken = default)
{
// Since we are sending the underlying request, we need to copy the Content on to it, or we'll lose the body.
Request.Content = Content;

Response res = await HttpPipeline.SendRequestAsync(Request, cancellationToken).ConfigureAwait(false);
DynamicJson? dynamicContent = null;

if (res.ContentStream != null)
{
JsonDocument doc = await JsonDocument.ParseAsync(res.ContentStream, new JsonDocumentOptions(), cancellationToken).ConfigureAwait(false);
dynamicContent = new DynamicJson(doc.RootElement);
}

return new DynamicResponse(res, dynamicContent);
}

/// <summary>
/// Send the request synchronously.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The response dynamically typed in a <see cref="DynamicResponse"/>.</returns>
public DynamicResponse Send(CancellationToken cancellationToken = default)
{
// Since we are sending the underlying request, we need to copy the Content on to it, or we'll lose the body.
Request.Content = Content;

Response res = HttpPipeline.SendRequest(Request, cancellationToken);
DynamicJson? dynamicContent = null;

if (res.ContentStream != null)
{
JsonDocument doc = JsonDocument.Parse(res.ContentStream);
dynamicContent = new DynamicJson(doc.RootElement);
}

return new DynamicResponse(res, dynamicContent);
}

/// <inheritdoc />
public override string ClientRequestId { get => Request.ClientRequestId; set => Request.ClientRequestId = value; }

/// <summary>
/// Frees resources held by the <see cref="DynamicRequest"/> object.
/// </summary>
public override void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

/// <summary>
/// Frees resources held by the <see cref="DynamicRequest"/> object.
/// </summary>
/// <param name="disposing">true if we should dispose, otherwise false</param>
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
Request.Dispose();
}
_disposed = true;
}

/// <inheritdoc />
protected override void AddHeader(string name, string value) => Request.Headers.Add(name, value);

/// <inheritdoc />
protected override bool ContainsHeader(string name) => Request.Headers.Contains(name);

/// <inheritdoc />
protected override IEnumerable<HttpHeader> EnumerateHeaders() => Request.Headers;

/// <inheritdoc />
protected override bool RemoveHeader(string name) => Request.Headers.Remove(name);

/// <inheritdoc />
protected override bool TryGetHeader(string name, [NotNullWhen(true)] out string? value) => Request.Headers.TryGetValue(name, out value);

/// <inheritdoc />
protected override bool TryGetHeaderValues(string name, [NotNullWhen(true)] out IEnumerable<string>? values) => Request.Headers.TryGetValues(name, out values);
}
}
82 changes: 82 additions & 0 deletions sdk/core/Azure.Core.Experimental/src/DynamicResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text;

namespace Azure.Core
{
/// <summary>
/// Represents a result of Azure operation with a <see cref="DynamicJson"/> response.
/// </summary>
[DebuggerDisplay("Status: {Response.Status}, Value: {Value}")]
public class DynamicResponse : Response
{
private bool _disposed;

private Response Response { get; }

/// <summary>
/// The JSON body of the response.
/// </summary>
public DynamicJson? Body { get; }

/// <inheritdoc />
public override int Status => Response.Status;
/// <inheritdoc />
public override string ReasonPhrase => Response.ReasonPhrase;
/// <inheritdoc />
public override Stream? ContentStream { get => Response.ContentStream; set => Response.ContentStream = value; }
/// <inheritdoc />
public override string ClientRequestId { get => Response.ClientRequestId; set => Response.ClientRequestId = value; }
/// <inheritdoc />
protected override bool TryGetHeader(string name, [NotNullWhen(true)] out string? value) => Response.Headers.TryGetValue(name, out value);
/// <inheritdoc />
protected override bool TryGetHeaderValues(string name, [NotNullWhen(true)] out IEnumerable<string>? values) => Response.Headers.TryGetValues(name, out values);
/// <inheritdoc />
protected override bool ContainsHeader(string name) => Response.Headers.Contains(name);
/// <inheritdoc />
protected override IEnumerable<HttpHeader> EnumerateHeaders() => Response.Headers;

/// <summary>
/// Represents a result of Azure operation with a <see cref="DynamicJson"/> response.
/// </summary>
/// <param name="response">The response returned by the service.</param>
/// <param name="body">The body returned by the service.</param>
public DynamicResponse(Response response, DynamicJson? body)
{
Response = response;
Body = body;
}

/// <summary>
/// Frees resources held by the <see cref="DynamicResponse"/> object.
/// </summary>
public override void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

/// <summary>
/// Frees resources held by the <see cref="DynamicResponse"/> object.
/// </summary>
/// <param name="disposing">true if we should dispose, otherwise false</param>
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
Response.Dispose();
}
_disposed = true;
}
}
}

0 comments on commit 0faeb4f

Please sign in to comment.