Skip to content

Commit

Permalink
Imported Tingle.AspNetCore.JsonPatch (#231)
Browse files Browse the repository at this point in the history
  • Loading branch information
mburumaxwell authored Apr 6, 2024
1 parent cf0997d commit 7ff76e5
Show file tree
Hide file tree
Showing 97 changed files with 11,113 additions and 0 deletions.
1 change: 1 addition & 0 deletions .github/workflows/cleanup.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ jobs:
- { name: 'Tingle.AspNetCore.Authentication' }
- { name: 'Tingle.AspNetCore.Authorization' }
- { name: 'Tingle.AspNetCore.DataProtection.MongoDB' }
- { name: 'Tingle.AspNetCore.JsonPatch' }
- { name: 'Tingle.AspNetCore.JsonPatch.NewtonsoftJson' }
- { name: 'Tingle.AspNetCore.Swagger' }
- { name: 'Tingle.AspNetCore.Tokens' }
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ This repository contains projects/libraries for adding useful functionality to .
|[`Tingle.AspNetCore.Authentication`](https://www.nuget.org/packages/Tingle.AspNetCore.Authentication/)|Convenience authentication functionality such as pass through and pre-shared key authentication mechanisms. See [docs](./src/Tingle.AspNetCore.Authentication/README.md) and [sample](./samples/AuthenticationSample)|
|[`Tingle.AspNetCore.Authorization`](https://www.nuget.org/packages/Tingle.AspNetCore.Authorization/)|Additional authorization functionality such as handlers and requirements. See [docs](./src/Tingle.AspNetCore.Authorization/README.md) and [sample](./samples/AuthorizationSample)|
|[`Tingle.AspNetCore.DataProtection.MongoDB`](https://www.nuget.org/packages/Tingle.AspNetCore.DataProtection.MongoDB/)|Data Protection store in [MongoDB](https://mongodb.com) for ASP.NET Core. See [docs](./src/Tingle.AspNetCore.DataProtection.MongoDB/README.md) and [sample](./samples/DataProtectionMongoDBSample).|
|[`Tingle.AspNetCore.JsonPatch`](https://www.nuget.org/packages/Tingle.AspNetCore.JsonPatch/)|JSON Patch support for AspNetCore using System.Text.Json. See [docs](./src/Tingle.AspNetCore.JsonPatch/README.md).|
|[`Tingle.AspNetCore.JsonPatch.NewtonsoftJson`](https://www.nuget.org/packages/Tingle.AspNetCore.JsonPatch.NewtonsoftJson/)|Helpers for validation when working with JsonPatch in ASP.NET Core. See [docs](./src/Tingle.AspNetCore.JsonPatch.NewtonsoftJson/README.md) and [blog](https://maxwellweru.com/blog/2020-11-17-immutable-properties-with-json-patch-in-aspnet-core).|
|[`Tingle.AspNetCore.Swagger`](https://www.nuget.org/packages/Tingle.AspNetCore.Swagger/)|Usability extensions for Swagger middleware including smaller ReDoc support. See [docs](./src/Tingle.AspNetCore.Swagger/README.md).|
|[`Tingle.AspNetCore.Tokens`](https://www.nuget.org/packages/Tingle.AspNetCore.Tokens/)|Support for generation of continuation tokens in ASP.NET Core with optional expiry. Useful for pagination, user invite tokens, expiring operation tokens, etc. This is availed through the `ContinuationToken<T>` and `TimedContinuationToken<T>` types. See [docs](./src/Tingle.AspNetCore.Tokens/README.md) and [sample](./samples/TokensSample).|
Expand Down
14 changes: 14 additions & 0 deletions Tingle.Extensions.sln
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.AspNetCore.Authoriza
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.AspNetCore.DataProtection.MongoDB", "src\Tingle.AspNetCore.DataProtection.MongoDB\Tingle.AspNetCore.DataProtection.MongoDB.csproj", "{6F93ED1D-6475-46F2-A7DB-B2A5F9DB5A83}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.AspNetCore.JsonPatch", "src\Tingle.AspNetCore.JsonPatch\Tingle.AspNetCore.JsonPatch.csproj", "{A8FCE1AF-3844-4D79-A6E2-A8B2BC9C6B82}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.AspNetCore.JsonPatch.NewtonsoftJson", "src\Tingle.AspNetCore.JsonPatch.NewtonsoftJson\Tingle.AspNetCore.JsonPatch.NewtonsoftJson.csproj", "{82B17C91-B96A-4290-A623-6867912A4C8E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.AspNetCore.Swagger", "src\Tingle.AspNetCore.Swagger\Tingle.AspNetCore.Swagger.csproj", "{C8093B92-5322-4B24-B71B-497340E5C5AA}"
Expand Down Expand Up @@ -63,6 +65,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.AspNetCore.DataProte
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.AspNetCore.JsonPatch.NewtonsoftJson.Tests", "tests\Tingle.AspNetCore.JsonPatch.NewtonsoftJson.Tests\Tingle.AspNetCore.JsonPatch.NewtonsoftJson.Tests.csproj", "{55B3650C-36C6-4C05-B6A2-B4CBC3DC3E4C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.AspNetCore.JsonPatch.Tests", "tests\Tingle.AspNetCore.JsonPatch.Tests\Tingle.AspNetCore.JsonPatch.Tests.csproj", "{01998E3A-C61A-44CD-B2DD-B04A5CFAA592}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.AspNetCore.Swagger.Tests", "tests\Tingle.AspNetCore.Swagger.Tests\Tingle.AspNetCore.Swagger.Tests.csproj", "{AAA0315A-779D-4F1E-89CA-EA78A5B6E3ED}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.AspNetCore.Tokens.Tests", "tests\Tingle.AspNetCore.Tokens.Tests\Tingle.AspNetCore.Tokens.Tests.csproj", "{FB2F8961-9F8F-4B35-ACAC-CCBEA2A89684}"
Expand Down Expand Up @@ -142,6 +146,10 @@ Global
{6F93ED1D-6475-46F2-A7DB-B2A5F9DB5A83}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6F93ED1D-6475-46F2-A7DB-B2A5F9DB5A83}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6F93ED1D-6475-46F2-A7DB-B2A5F9DB5A83}.Release|Any CPU.Build.0 = Release|Any CPU
{A8FCE1AF-3844-4D79-A6E2-A8B2BC9C6B82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A8FCE1AF-3844-4D79-A6E2-A8B2BC9C6B82}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A8FCE1AF-3844-4D79-A6E2-A8B2BC9C6B82}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A8FCE1AF-3844-4D79-A6E2-A8B2BC9C6B82}.Release|Any CPU.Build.0 = Release|Any CPU
{82B17C91-B96A-4290-A623-6867912A4C8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{82B17C91-B96A-4290-A623-6867912A4C8E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{82B17C91-B96A-4290-A623-6867912A4C8E}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -226,6 +234,10 @@ Global
{55B3650C-36C6-4C05-B6A2-B4CBC3DC3E4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{55B3650C-36C6-4C05-B6A2-B4CBC3DC3E4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{55B3650C-36C6-4C05-B6A2-B4CBC3DC3E4C}.Release|Any CPU.Build.0 = Release|Any CPU
{01998E3A-C61A-44CD-B2DD-B04A5CFAA592}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{01998E3A-C61A-44CD-B2DD-B04A5CFAA592}.Debug|Any CPU.Build.0 = Debug|Any CPU
{01998E3A-C61A-44CD-B2DD-B04A5CFAA592}.Release|Any CPU.ActiveCfg = Release|Any CPU
{01998E3A-C61A-44CD-B2DD-B04A5CFAA592}.Release|Any CPU.Build.0 = Release|Any CPU
{AAA0315A-779D-4F1E-89CA-EA78A5B6E3ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AAA0315A-779D-4F1E-89CA-EA78A5B6E3ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AAA0315A-779D-4F1E-89CA-EA78A5B6E3ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -335,6 +347,7 @@ Global
{98F3A2B7-5774-4E38-8FA0-FA13B6134454} = {9546186D-D4E1-4EDB-956E-1F81C7F4DB72}
{22690754-DCFB-4CD2-968D-239C1952B52C} = {9546186D-D4E1-4EDB-956E-1F81C7F4DB72}
{6F93ED1D-6475-46F2-A7DB-B2A5F9DB5A83} = {9546186D-D4E1-4EDB-956E-1F81C7F4DB72}
{A8FCE1AF-3844-4D79-A6E2-A8B2BC9C6B82} = {9546186D-D4E1-4EDB-956E-1F81C7F4DB72}
{82B17C91-B96A-4290-A623-6867912A4C8E} = {9546186D-D4E1-4EDB-956E-1F81C7F4DB72}
{C8093B92-5322-4B24-B71B-497340E5C5AA} = {9546186D-D4E1-4EDB-956E-1F81C7F4DB72}
{B545B88C-4BE0-43FB-AE87-47706D479C6B} = {9546186D-D4E1-4EDB-956E-1F81C7F4DB72}
Expand All @@ -356,6 +369,7 @@ Global
{E67CB6B9-6F42-4E63-9603-810B5B9FBF57} = {815F0941-3B70-4705-A583-AF627559595C}
{E28F4E8D-148B-4583-A27D-E1DA2CC08167} = {815F0941-3B70-4705-A583-AF627559595C}
{55B3650C-36C6-4C05-B6A2-B4CBC3DC3E4C} = {815F0941-3B70-4705-A583-AF627559595C}
{01998E3A-C61A-44CD-B2DD-B04A5CFAA592} = {815F0941-3B70-4705-A583-AF627559595C}
{AAA0315A-779D-4F1E-89CA-EA78A5B6E3ED} = {815F0941-3B70-4705-A583-AF627559595C}
{FB2F8961-9F8F-4B35-ACAC-CCBEA2A89684} = {815F0941-3B70-4705-A583-AF627559595C}
{0EA063C6-9A97-4DE8-9344-5D2BDD301134} = {815F0941-3B70-4705-A583-AF627559595C}
Expand Down
47 changes: 47 additions & 0 deletions src/Tingle.AspNetCore.JsonPatch/Adapters/AdapterFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System.Collections;
using System.Dynamic;
using System.Text.Json;
using System.Text.Json.Nodes;
using Tingle.AspNetCore.JsonPatch.Internal;

namespace Tingle.AspNetCore.JsonPatch.Adapters;

/// <summary>
/// The default AdapterFactory to be used for resolving <see cref="IAdapter"/>.
/// </summary>
public class AdapterFactory : IAdapterFactory
{
internal static AdapterFactory Default { get; } = new();

/// <inheritdoc />
public virtual IAdapter Create(object target, JsonSerializerOptions serializerOptions)
{
ArgumentNullException.ThrowIfNull(target);

ArgumentNullException.ThrowIfNull(serializerOptions);

if (target is JsonObject)
{
return new JsonObjectAdapter();
}
if (target is IList)
{
return new ListAdapter();
}
else if (target is IDictionary || target is IDictionary<string, object?>) // ExpandoObject implements IDictionary<string, object?>
{
var intf = target.GetType().GetInterfaces().FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<,>));
if (intf != null)
{
var type = typeof(DictionaryAdapter<,>).MakeGenericType(intf.GetGenericArguments());
return (IAdapter)Activator.CreateInstance(type)!;
}
}
else if (target is DynamicObject)
{
return new DynamicObjectAdapter();
}

return new PocoAdapter();
}
}
18 changes: 18 additions & 0 deletions src/Tingle.AspNetCore.JsonPatch/Adapters/IAdapterFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Text.Json;
using Tingle.AspNetCore.JsonPatch.Internal;

namespace Tingle.AspNetCore.JsonPatch.Adapters;

/// <summary>
/// Defines the operations used for loading an <see cref="IAdapter"/> based on the current object and ContractResolver.
/// </summary>
public interface IAdapterFactory
{
/// <summary>
/// Creates an <see cref="IAdapter"/> for the current object
/// </summary>
/// <param name="target">The target object</param>
/// <param name="serializerOptions">The current <see cref="JsonSerializerOptions"/></param>
/// <returns>The needed <see cref="IAdapter"/></returns>
IAdapter Create(object target, JsonSerializerOptions serializerOptions);
}
108 changes: 108 additions & 0 deletions src/Tingle.AspNetCore.JsonPatch/Adapters/IObjectAdapter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using Tingle.AspNetCore.JsonPatch.Operations;

namespace Tingle.AspNetCore.JsonPatch.Adapters;

/// <summary>
/// Defines the operations that can be performed on a JSON patch document.
/// </summary>
public interface IObjectAdapter
{
/// <summary>
/// Using the "add" operation a new value is inserted into the root of the target
/// document, into the target array at the specified valid index, or to a target object at
/// the specified location.
///
/// When adding to arrays, the specified index MUST NOT be greater than the number of elements in the array.
/// To append the value to the array, the index of "-" character is used (see [RFC6901]).
///
/// When adding to an object, if an object member does not already exist, a new member is added to the object at the
/// specified location or if an object member does exist, that member's value is replaced.
///
/// The operation object MUST contain a "value" member whose content
/// specifies the value to be added.
///
/// For example:
///
/// { "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] }
///
/// See RFC 6902 https://tools.ietf.org/html/rfc6902#page-4
/// </summary>
/// <param name="operation">The add operation.</param>
/// <param name="objectToApplyTo">Object to apply the operation to.</param>
void Add(Operation operation, object objectToApplyTo);

/// <summary>
/// Using the "copy" operation, a value is copied from a specified location to the
/// target location.
///
/// The operation object MUST contain a "from" member, which references the location in the
/// target document to copy the value from.
///
/// The "from" location MUST exist for the operation to be successful.
///
/// For example:
///
/// { "op": "copy", "from": "/a/b/c", "path": "/a/b/e" }
///
/// See RFC 6902 https://tools.ietf.org/html/rfc6902#page-7
/// </summary>
/// <param name="operation">The copy operation.</param>
/// <param name="objectToApplyTo">Object to apply the operation to.</param>
void Copy(Operation operation, object objectToApplyTo);

/// <summary>
/// Using the "move" operation the value at a specified location is removed and
/// added to the target location.
///
/// The operation object MUST contain a "from" member, which references the location in the
/// target document to move the value from.
///
/// The "from" location MUST exist for the operation to be successful.
///
/// For example:
///
/// { "op": "move", "from": "/a/b/c", "path": "/a/b/d" }
///
/// A location cannot be moved into one of its children.
///
/// See RFC 6902 https://tools.ietf.org/html/rfc6902#page-6
/// </summary>
/// <param name="operation">The move operation.</param>
/// <param name="objectToApplyTo">Object to apply the operation to.</param>
void Move(Operation operation, object objectToApplyTo);

/// <summary>
/// Using the "remove" operation the value at the target location is removed.
///
/// The target location MUST exist for the operation to be successful.
///
/// For example:
///
/// { "op": "remove", "path": "/a/b/c" }
///
/// If removing an element from an array, any elements above the
/// specified index are shifted one position to the left.
///
/// See RFC 6902 https://tools.ietf.org/html/rfc6902#page-6
/// </summary>
/// <param name="operation">The remove operation.</param>
/// <param name="objectToApplyTo">Object to apply the operation to.</param>
void Remove(Operation operation, object objectToApplyTo);

/// <summary>
/// Using the "replace" operation the value at the target location is replaced
/// with a new value. The operation object MUST contain a "value" member
/// which specifies the replacement value.
///
/// The target location MUST exist for the operation to be successful.
///
/// For example:
///
/// { "op": "replace", "path": "/a/b/c", "value": 42 }
///
/// See RFC 6902 https://tools.ietf.org/html/rfc6902#page-6
/// </summary>
/// <param name="operation">The replace operation.</param>
/// <param name="objectToApplyTo">Object to apply the operation to.</param>
void Replace(Operation operation, object objectToApplyTo);
}
28 changes: 28 additions & 0 deletions src/Tingle.AspNetCore.JsonPatch/Adapters/IObjectAdapterWithTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Tingle.AspNetCore.JsonPatch.Operations;

namespace Tingle.AspNetCore.JsonPatch.Adapters;

/// <summary>
/// Defines the operations that can be performed on a JSON patch document, including "test".
/// </summary>
public interface IObjectAdapterWithTest : IObjectAdapter
{
/// <summary>
/// Using the "test" operation a value at the target location is compared for
/// equality to a specified value.
///
/// The operation object MUST contain a "value" member that specifies
/// value to be compared to the target location's value.
///
/// The target location MUST be equal to the "value" value for the
/// operation to be considered successful.
///
/// For example:
/// { "op": "test", "path": "/a/b/c", "value": "foo" }
///
/// See RFC 6902 https://tools.ietf.org/html/rfc6902#page-7
/// </summary>
/// <param name="operation">The test operation.</param>
/// <param name="objectToApplyTo">Object to apply the operation to.</param>
void Test(Operation operation, object objectToApplyTo);
}
Loading

0 comments on commit 7ff76e5

Please sign in to comment.