Skip to content
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

Add JsonPatchDocument #14618

Merged
merged 5 commits into from
Aug 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions sdk/core/Azure.Core.Experimental/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## 0.1.0-preview.5 (Unreleased)

### Added
- `JsonPatchDocument` type to represent JSON Path document.

## 0.1.0-preview.4 (2020-08-18)

### Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,17 @@ public DynamicJson(System.Text.Json.JsonElement element) { }
public override string ToString() { throw null; }
public void WriteTo(System.Text.Json.Utf8JsonWriter writer) { }
}
public partial class JsonPatchDocument
{
public JsonPatchDocument() { }
public void AppendAdd(string path, string rawJsonValue) { }
public void AppendCopy(string from, string path) { }
public void AppendMove(string from, string path) { }
public void AppendRemove(string path) { }
public void AppendReplace(string path, string rawJsonValue) { }
public void AppendTest(string path, string rawJsonValue) { }
public override string ToString() { throw null; }
}
}
namespace Azure.Core.GeoJson
{
Expand Down
123 changes: 123 additions & 0 deletions sdk/core/Azure.Core.Experimental/src/JsonPatchDocument.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections.ObjectModel;
using System.IO;
using System.Text;
using System.Text.Json;
using Azure.Core.JsonPatch;

namespace Azure.Core
{
/// <summary>
/// Represents a JSON Patch document.
/// </summary>
public class JsonPatchDocument
{
internal Collection<JsonPatchOperation> Operations { get; }

/// <summary>
/// Initializes a new instance of <see cref="JsonPatchDocument"/>
/// </summary>
public JsonPatchDocument()
{
Operations = new Collection<JsonPatchOperation>();
}

/// <summary>
/// Appends an "add" operation to this <see cref="JsonPatchDocument"/>.
/// </summary>
/// <param name="path">The path to apply the addition to.</param>
/// <param name="rawJsonValue">The raw JSON value to add to the path.</param>
public void AppendAdd(string path, string rawJsonValue)
{
Operations.Add(new JsonPatchOperation(JsonPatchOperationKind.Add, path, null, rawJsonValue));
}

/// <summary>
/// Appends a "replace" operation to this <see cref="JsonPatchDocument"/>.
/// </summary>
/// <param name="path">The path to replace.</param>
/// <param name="rawJsonValue">The raw JSON value to replace with.</param>
public void AppendReplace(string path, string rawJsonValue)
{
Operations.Add(new JsonPatchOperation(JsonPatchOperationKind.Replace, path, null, rawJsonValue));
}

/// <summary>
/// Appends a "copy" operation to this <see cref="JsonPatchDocument"/>.
/// </summary>
/// <param name="from">The path to copy from.</param>
/// <param name="path">The path to copy to.</param>
pakrym marked this conversation as resolved.
Show resolved Hide resolved
public void AppendCopy(string from, string path)
{
Operations.Add(new JsonPatchOperation(JsonPatchOperationKind.Copy, path, from, null));
}

/// <summary>
/// Appends a "move" operation to this <see cref="JsonPatchDocument"/>.
/// </summary>
/// <param name="from">The path to move from.</param>
/// <param name="path">The path to move to.</param>
public void AppendMove(string from, string path)
{
Operations.Add(new JsonPatchOperation(JsonPatchOperationKind.Move, path, from, null));
}

/// <summary>
/// Appends a "remove" operation to this <see cref="JsonPatchDocument"/>.
/// </summary>
/// <param name="path">The path to remove.</param>
public void AppendRemove(string path)
{
Operations.Add(new JsonPatchOperation(JsonPatchOperationKind.Remove, path, null, null));
}

/// <summary>
/// Appends a "test" operation to this <see cref="JsonPatchDocument"/>.
/// </summary>
/// <param name="path">The path to test.</param>
/// <param name="rawJsonValue">The raw JSON value to test against.</param>
public void AppendTest(string path, string rawJsonValue)
{
Operations.Add(new JsonPatchOperation(JsonPatchOperationKind.Test, path, null, rawJsonValue));
}

/// <summary>
/// Returns a formatted JSON string representation of this <see cref="JsonPatchDocument"/>.
/// </summary>
/// <returns>A formatted JSON string representation of this <see cref="JsonPatchDocument"/>.</returns>
public override string ToString()
{
using var memoryStream = new MemoryStream();
using (var writer = new Utf8JsonWriter(memoryStream))
{
WriteTo(writer);
}
return Encoding.UTF8.GetString(memoryStream.ToArray());
}

private void WriteTo(Utf8JsonWriter writer)
{
writer.WriteStartArray();
foreach (var operation in Operations)
{
writer.WriteStartObject();
writer.WriteString("op", operation.Kind.ToString());
if (operation.From != null)
{
writer.WriteString("from", operation.From);
}
writer.WriteString("path", operation.Path);
if (operation.RawJsonValue != null)
{
using var parsedValue = JsonDocument.Parse(operation.RawJsonValue);
writer.WritePropertyName("value");
parsedValue.WriteTo(writer);
}
writer.WriteEndObject();
}
writer.WriteEndArray();
}
}
}
21 changes: 21 additions & 0 deletions sdk/core/Azure.Core.Experimental/src/JsonPatchOperation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace Azure.Core.JsonPatch
{
internal class JsonPatchOperation
{
public JsonPatchOperation(JsonPatchOperationKind kind, string path, string? from, string? rawJsonValue)
{
Kind = kind;
Path = path;
From = from;
RawJsonValue = rawJsonValue;
}

public JsonPatchOperationKind Kind { get; }
public string Path { get; }
public string? From { get; }
public string? RawJsonValue { get; }
}
}
27 changes: 27 additions & 0 deletions sdk/core/Azure.Core.Experimental/src/JsonPatchOperationKind.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace Azure.Core.JsonPatch
{
internal readonly struct JsonPatchOperationKind
{
private readonly string _operation;

public JsonPatchOperationKind(string operation)
{
_operation = operation;
}

public static JsonPatchOperationKind Add { get; } = new JsonPatchOperationKind("add");
public static JsonPatchOperationKind Remove { get; } = new JsonPatchOperationKind("remove");
public static JsonPatchOperationKind Replace { get; } = new JsonPatchOperationKind("replace");
public static JsonPatchOperationKind Move { get; } = new JsonPatchOperationKind("move");
public static JsonPatchOperationKind Copy { get; } = new JsonPatchOperationKind("copy");
public static JsonPatchOperationKind Test { get; } = new JsonPatchOperationKind("test");

public override string ToString()
{
return _operation;
}
}
}
81 changes: 81 additions & 0 deletions sdk/core/Azure.Core.Experimental/tests/JsonPatchDocumentTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Azure.Core.JsonPatch;
using NUnit.Framework;

namespace Azure.Core.Tests
{
public class JsonPatchDocumentTests
{
[Test]
public void AddIsSerializedCorrectly()
{
JsonPatchDocument document = new JsonPatchDocument();
document.AppendAdd("/a/b/c","[ \"foo\", \"bar\" ]");
Assert.AreEqual(document.ToString(), "[{\"op\":\"add\",\"path\":\"/a/b/c\",\"value\":[\"foo\",\"bar\"]}]");
}

[Test]
public void ReplaceIsSerializedCorrectly()
{
JsonPatchDocument document = new JsonPatchDocument();
document.AppendReplace("/a/b/c","[ \"foo\", \"bar\" ]");
Assert.AreEqual(document.ToString(), "[{\"op\":\"replace\",\"path\":\"/a/b/c\",\"value\":[\"foo\",\"bar\"]}]");
}

[Test]
public void TestIsSerializedCorrectly()
{
JsonPatchDocument document = new JsonPatchDocument();
document.AppendTest("/a/b/c","[ \"foo\", \"bar\" ]");
Assert.AreEqual(document.ToString(), "[{\"op\":\"test\",\"path\":\"/a/b/c\",\"value\":[\"foo\",\"bar\"]}]");
}

[Test]
public void RemoveIsSerializedCorrectly()
{
JsonPatchDocument document = new JsonPatchDocument();
document.AppendRemove("/a/b/c");
Assert.AreEqual(document.ToString(), "[{\"op\":\"remove\",\"path\":\"/a/b/c\"}]");
}

[Test]
public void MoveIsSerializedCorrectly()
{
JsonPatchDocument document = new JsonPatchDocument();
document.AppendMove("/a/b/c", "/a/b/d");
Assert.AreEqual(document.ToString(), "[{\"op\":\"move\",\"from\":\"/a/b/c\",\"path\":\"/a/b/d\"}]");
}

[Test]
public void CopyIsSerializedCorrectly()
{
JsonPatchDocument document = new JsonPatchDocument();
document.AppendCopy("/a/b/c", "/a/b/d");
Assert.AreEqual(document.ToString(), "[{\"op\":\"copy\",\"from\":\"/a/b/c\",\"path\":\"/a/b/d\"}]");
}

[Test]
public void MultipleOperationsSerializedInOrder()
{
JsonPatchDocument document = new JsonPatchDocument();
document.AppendTest("/a/b/c","\"foo\"");
document.AppendAdd("/a/b/c","42");
document.AppendReplace("/a/b/c","[ \"foo\", \"bar\" ]");
document.AppendRemove("/a/b/c");
document.AppendMove("/a/b/c", "/a/b/d");
document.AppendCopy("/a/b/c", "/a/b/d");

Assert.AreEqual(document.ToString(),
"[" +
"{\"op\":\"test\",\"path\":\"/a/b/c\",\"value\":\"foo\"}," +
"{\"op\":\"add\",\"path\":\"/a/b/c\",\"value\":42}," +
"{\"op\":\"replace\",\"path\":\"/a/b/c\",\"value\":[\"foo\",\"bar\"]}," +
"{\"op\":\"remove\",\"path\":\"/a/b/c\"}," +
"{\"op\":\"move\",\"from\":\"/a/b/c\",\"path\":\"/a/b/d\"}," +
"{\"op\":\"copy\",\"from\":\"/a/b/c\",\"path\":\"/a/b/d\"}" +
"]");
}
}
}