Skip to content

Commit

Permalink
Add JsonPatchDocument (Azure#14618)
Browse files Browse the repository at this point in the history
  • Loading branch information
pakrym authored Aug 27, 2020
1 parent 8bcc441 commit c34f0d4
Show file tree
Hide file tree
Showing 6 changed files with 266 additions and 0 deletions.
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>
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\"}" +
"]");
}
}
}

0 comments on commit c34f0d4

Please sign in to comment.