From 01a18e9770c62b441658110cef8febea069d2dd3 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Wed, 17 Apr 2024 10:29:27 +0100 Subject: [PATCH 01/28] Remove JmesPath external dependency. Update Idempotency to use new internal JmesPath implementation. Add internal JmesPath implementation and tests. --- libraries/AWS.Lambda.Powertools.sln | 30 + .../AWS.Lambda.Powertools.Idempotency.csproj | 2 +- .../Persistence/BasePersistenceStore.cs | 20 +- .../Serialization/JsonFunction.cs | 42 - .../AWS.Lambda.Powertools.JMESPath.csproj | 7 + .../BinaryOperator.cs | 295 +++ .../Expression.cs | 748 +++++++ .../Function.cs | 1492 ++++++++++++++ .../InternalsVisibleTo.cs | 18 + .../JmesPathParser.cs | 1749 +++++++++++++++++ .../JsonTransformer.cs | 145 ++ .../Operator.cs | 74 + .../AWS.Lambda.Powertools.JMESPath/README.md | 1 + .../AWS.Lambda.Powertools.JMESPath/Slice.cs | 53 + .../AWS.Lambda.Powertools.JMESPath/Token.cs | 245 +++ .../UnaryOperator.cs | 75 + .../Utilities/JsonDocumentBuilder.cs | 305 +++ .../Utilities/JsonElementComparer.cs | 159 ++ .../Utilities/JsonElementEqualityComparer.cs | 183 ++ .../Utilities/JsonFlattener.cs | 503 +++++ .../Utilities/JsonMergePatch.cs | 213 ++ .../Utilities/JsonPatch.cs | 413 ++++ .../Utilities/JsonPointer.cs | 576 ++++++ .../Utilities/JsonPointerExtensions.cs | 343 ++++ .../AWS.Lambda.Powertools.JMESPath/Value.cs | 816 ++++++++ .../ValueComparer.cs | 154 ++ .../ValueEqualityComparer.cs | 129 ++ libraries/src/Directory.Packages.props | 1 - .../Persistence/BasePersistenceStoreTests.cs | 12 +- ...WS.Lambda.Powertools.JMESPath.Tests.csproj | 94 + .../GlobalUsings.cs | 1 + .../JmesPathTests.cs | 113 ++ .../test_files/apigw_event.json | 76 + .../test_files/apigw_event_2.json | 54 + .../test_files/basic.json | 96 + .../test_files/benchmarks.json | 138 ++ .../test_files/boolean.json | 275 +++ .../test_files/current.json | 25 + .../test_files/escape.json | 46 + .../test_files/example.json | 50 + .../test_files/filters.json | 468 +++++ .../test_files/functions.json | 829 ++++++++ .../test_files/identifiers.json | 1377 +++++++++++++ .../test_files/indices.json | 346 ++++ .../test_files/literal.json | 200 ++ .../test_files/multiselect.json | 398 ++++ .../test_files/pipe.json | 131 ++ .../test_files/slice.json | 187 ++ .../test_files/syntax.json | 678 +++++++ .../test_files/test.json | 19 + .../test_files/unicode.json | 38 + .../test_files/wildcard.json | 460 +++++ 52 files changed, 14843 insertions(+), 59 deletions(-) delete mode 100644 libraries/src/AWS.Lambda.Powertools.Idempotency/Serialization/JsonFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/AWS.Lambda.Powertools.JMESPath.csproj create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Function.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/InternalsVisibleTo.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Operator.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/README.md create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Slice.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonDocumentBuilder.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementComparer.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementEqualityComparer.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonFlattener.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonMergePatch.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPatch.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointer.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointerExtensions.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Value.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/ValueComparer.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/ValueEqualityComparer.cs create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/AWS.Lambda.Powertools.JMESPath.Tests.csproj create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/GlobalUsings.cs create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/JmesPathTests.cs create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/apigw_event.json create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/apigw_event_2.json create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/basic.json create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/benchmarks.json create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/boolean.json create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/current.json create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/escape.json create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/example.json create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/filters.json create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/functions.json create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/identifiers.json create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/indices.json create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/literal.json create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/multiselect.json create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/pipe.json create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/slice.json create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/syntax.json create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/test.json create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/unicode.json create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/wildcard.json diff --git a/libraries/AWS.Lambda.Powertools.sln b/libraries/AWS.Lambda.Powertools.sln index 62c1cec1..27ad89f2 100644 --- a/libraries/AWS.Lambda.Powertools.sln +++ b/libraries/AWS.Lambda.Powertools.sln @@ -35,6 +35,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Param EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Parameters.Tests", "tests\AWS.Lambda.Powertools.Parameters.Tests\AWS.Lambda.Powertools.Parameters.Tests.csproj", "{386A9769-59BF-4BE3-99D4-A9603E300729}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.JMESPath", "src\AWS.Lambda.Powertools.JMESPath\AWS.Lambda.Powertools.JMESPath.csproj", "{4F5020DB-9856-4A6F-B2CB-2C213FD749BC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.JMESPath.Tests", "tests\AWS.Lambda.Powertools.JMESPath.Tests\AWS.Lambda.Powertools.JMESPath.Tests.csproj", "{B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -216,6 +220,30 @@ Global {386A9769-59BF-4BE3-99D4-A9603E300729}.Release|x64.Build.0 = Release|Any CPU {386A9769-59BF-4BE3-99D4-A9603E300729}.Release|x86.ActiveCfg = Release|Any CPU {386A9769-59BF-4BE3-99D4-A9603E300729}.Release|x86.Build.0 = Release|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Debug|x64.ActiveCfg = Debug|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Debug|x64.Build.0 = Debug|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Debug|x86.ActiveCfg = Debug|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Debug|x86.Build.0 = Debug|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Release|Any CPU.Build.0 = Release|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Release|x64.ActiveCfg = Release|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Release|x64.Build.0 = Release|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Release|x86.ActiveCfg = Release|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Release|x86.Build.0 = Release|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Debug|x64.ActiveCfg = Debug|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Debug|x64.Build.0 = Debug|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Debug|x86.ActiveCfg = Debug|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Debug|x86.Build.0 = Debug|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Release|Any CPU.Build.0 = Release|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Release|x64.ActiveCfg = Release|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Release|x64.Build.0 = Release|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Release|x86.ActiveCfg = Release|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution @@ -233,5 +261,7 @@ Global {F8B4100F-4014-4A1E-8130-D281453B79ED} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} {12B940EF-A5D3-459D-BD36-A603834D1F7D} = {1CFF5568-8486-475F-81F6-06105C437528} {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3} = {1CFF5568-8486-475F-81F6-06105C437528} + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1} = {1CFF5568-8486-475F-81F6-06105C437528} EndGlobalSection EndGlobal diff --git a/libraries/src/AWS.Lambda.Powertools.Idempotency/AWS.Lambda.Powertools.Idempotency.csproj b/libraries/src/AWS.Lambda.Powertools.Idempotency/AWS.Lambda.Powertools.Idempotency.csproj index d12fe902..50a99381 100644 --- a/libraries/src/AWS.Lambda.Powertools.Idempotency/AWS.Lambda.Powertools.Idempotency.csproj +++ b/libraries/src/AWS.Lambda.Powertools.Idempotency/AWS.Lambda.Powertools.Idempotency.csproj @@ -14,8 +14,8 @@ - + diff --git a/libraries/src/AWS.Lambda.Powertools.Idempotency/Persistence/BasePersistenceStore.cs b/libraries/src/AWS.Lambda.Powertools.Idempotency/Persistence/BasePersistenceStore.cs index 27ae02f6..75191c5a 100644 --- a/libraries/src/AWS.Lambda.Powertools.Idempotency/Persistence/BasePersistenceStore.cs +++ b/libraries/src/AWS.Lambda.Powertools.Idempotency/Persistence/BasePersistenceStore.cs @@ -21,8 +21,7 @@ using AWS.Lambda.Powertools.Common; using AWS.Lambda.Powertools.Idempotency.Exceptions; using AWS.Lambda.Powertools.Idempotency.Internal; -using AWS.Lambda.Powertools.Idempotency.Serialization; -using DevLab.JmesPath; +using AWS.Lambda.Powertools.JMESPath; namespace AWS.Lambda.Powertools.Idempotency.Persistence; @@ -262,15 +261,11 @@ private string GetHashedPayload(JsonDocument data) return ""; } - var jmes = new JmesPath(); - jmes.FunctionRepository.Register(); - var result = jmes.Transform(data.RootElement.ToString(), _idempotencyOptions.PayloadValidationJmesPath); - var node = JsonDocument.Parse(result); - return GenerateHash(node.RootElement); + var transformer = JsonTransformer.Parse(_idempotencyOptions.PayloadValidationJmesPath); + JsonDocument result = transformer.Transform(data.RootElement); + return GenerateHash(result.RootElement); } - - /// /// Calculate unix timestamp of expiry date for idempotency record /// @@ -293,10 +288,9 @@ private string GetHashedIdempotencyKey(JsonDocument data) var eventKeyJmesPath = _idempotencyOptions.EventKeyJmesPath; if (eventKeyJmesPath != null) { - var jmes = new JmesPath(); - jmes.FunctionRepository.Register(); - var result = jmes.Transform(node.ToString(), eventKeyJmesPath); - node = JsonDocument.Parse(result).RootElement; + var transformer = JsonTransformer.Parse(eventKeyJmesPath); + JsonDocument result = transformer.Transform(node); + node = result.RootElement; } if (IsMissingIdempotencyKey(node)) diff --git a/libraries/src/AWS.Lambda.Powertools.Idempotency/Serialization/JsonFunction.cs b/libraries/src/AWS.Lambda.Powertools.Idempotency/Serialization/JsonFunction.cs deleted file mode 100644 index 94ed3f25..00000000 --- a/libraries/src/AWS.Lambda.Powertools.Idempotency/Serialization/JsonFunction.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -using System.Diagnostics; -using DevLab.JmesPath.Functions; -using Newtonsoft.Json.Linq; - -namespace AWS.Lambda.Powertools.Idempotency.Serialization; - -/// -/// Creates JMESPath function powertools_json() to treat the payload as a JSON object rather than a string. -/// -public class JsonFunction : JmesPathFunction -{ - /// - public JsonFunction() - : base("powertools_json", 1) - { - } - - /// - public override JToken Execute(params JmesPathFunctionArgument[] args) - { - Debug.Assert(args.Length == 1); - Debug.Assert(args[0].IsToken); - var argument = args[0]; - var token = argument.Token; - return JToken.Parse(token.ToString()); - } -} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/AWS.Lambda.Powertools.JMESPath.csproj b/libraries/src/AWS.Lambda.Powertools.JMESPath/AWS.Lambda.Powertools.JMESPath.csproj new file mode 100644 index 00000000..891af5d4 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/AWS.Lambda.Powertools.JMESPath.csproj @@ -0,0 +1,7 @@ + + + + + + + diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs new file mode 100644 index 00000000..f2c911b0 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs @@ -0,0 +1,295 @@ +using System; + +namespace AWS.Lambda.Powertools.JMESPath +{ + internal interface IBinaryOperator + { + int PrecedenceLevel {get;} + bool IsRightAssociative {get;} + bool TryEvaluate(IValue lhs, IValue rhs, out IValue result); + }; + + internal abstract class BinaryOperator : IBinaryOperator + { + internal BinaryOperator(Operator oper) + { + PrecedenceLevel = OperatorTable.PrecedenceLevel(oper); + } + + public int PrecedenceLevel {get;} + + public bool IsRightAssociative => false; + + public abstract bool TryEvaluate(IValue lhs, IValue rhs, out IValue result); + }; + + internal sealed class OrOperator : BinaryOperator + { + internal static OrOperator Instance { get; } = new(); + + private OrOperator() + : base(Operator.Or) + { + } + + public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) + { + if (lhs.Type == JmesPathType.Null && rhs.Type == JmesPathType.Null) + { + result = lhs; + return true; + } + result = Expression.IsTrue(lhs) ? lhs : rhs; + return true; + } + + public override string ToString() + { + return "OrOperator"; + } + }; + + internal sealed class AndOperator : BinaryOperator + { + internal static AndOperator Instance { get; } = new(); + + private AndOperator() + : base(Operator.And) + { + } + + public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) + { + result = Expression.IsTrue(lhs) ? rhs : lhs; + return true; + } + + public override string ToString() + { + return "AndOperator"; + } + }; + + internal sealed class EqOperator : BinaryOperator + { + internal static EqOperator Instance { get; } = new(); + + private EqOperator() + : base(Operator.Eq) + { + } + + public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) + { + var comparer = ValueEqualityComparer.Instance; + result = comparer.Equals(lhs, rhs) ? JsonConstants.True : JsonConstants.False; + return true; + } + + public override string ToString() + { + return "EqOperator"; + } + }; + + internal sealed class NeOperator : BinaryOperator + { + internal static NeOperator Instance { get; } = new(); + + private NeOperator() + : base(Operator.Ne) + { + } + + public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) + { + if (!EqOperator.Instance.TryEvaluate(lhs, rhs, out var value)) + { + result = JsonConstants.Null; + return false; + } + + result = Expression.IsFalse(value) ? JsonConstants.True : JsonConstants.False; + return true; + } + + public override string ToString() + { + return "NeOperator"; + } + }; + + internal sealed class LtOperator : BinaryOperator + { + internal static LtOperator Instance { get; } = new(); + + private LtOperator() + : base(Operator.Lt) + { + } + + public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) + { + if (lhs.Type == JmesPathType.Number && rhs.Type == JmesPathType.Number) + { + if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) + { + result = dec1 < dec2 ? JsonConstants.True : JsonConstants.False; + } + else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) + { + result = val1 < val2 ? JsonConstants.True : JsonConstants.False; + } + else + { + result = JsonConstants.Null; + } + } + else if (lhs.Type == JmesPathType.String && rhs.Type == JmesPathType.String) + { + result = string.CompareOrdinal(lhs.GetString(), rhs.GetString()) < 0 ? JsonConstants.True : JsonConstants.False; + } + else + { + result = JsonConstants.Null; + } + return true; + } + + public override string ToString() + { + return "LtOperator"; + } + }; + + internal sealed class LteOperator : BinaryOperator + { + internal static LteOperator Instance { get; } = new(); + + private LteOperator() + : base(Operator.Lte) + { + } + + public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) + { + if (lhs.Type == JmesPathType.Number && rhs.Type == JmesPathType.Number) + { + if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) + { + result = dec1 <= dec2 ? JsonConstants.True : JsonConstants.False; + } + else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) + { + result = val1 <= val2 ? JsonConstants.True : JsonConstants.False; + } + else + { + result = JsonConstants.Null; + } + } + else if (lhs.Type == JmesPathType.String && rhs.Type == JmesPathType.String) + { + result = string.CompareOrdinal(lhs.GetString(), rhs.GetString()) <= 0 ? JsonConstants.True : JsonConstants.False; + } + else + { + result = JsonConstants.Null; + } + return true; + } + + + public override string ToString() + { + return "LteOperator"; + } + }; + + internal sealed class GtOperator : BinaryOperator + { + internal static GtOperator Instance { get; } = new(); + + private GtOperator() + : base(Operator.Gt) + { + } + + public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) + { + if (lhs.Type == JmesPathType.Number && rhs.Type == JmesPathType.Number) + { + if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) + { + result = dec1 > dec2 ? JsonConstants.True : JsonConstants.False; + } + else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) + { + result = val1 > val2 ? JsonConstants.True : JsonConstants.False; + } + else + { + result = JsonConstants.Null; + } + } + else if (lhs.Type == JmesPathType.String && rhs.Type == JmesPathType.String) + { + result = string.CompareOrdinal(lhs.GetString(), rhs.GetString()) > 0 ? JsonConstants.True : JsonConstants.False; + } + else + { + result = JsonConstants.Null; + } + return true; + } + + public override string ToString() + { + return "GtOperator"; + } + }; + + internal sealed class GteOperator : BinaryOperator + { + internal static GteOperator Instance { get; } = new(); + + private GteOperator() + : base(Operator.Gte) + { + } + + public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) + { + if (lhs.Type == JmesPathType.Number && rhs.Type == JmesPathType.Number) + { + if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) + { + result = dec1 >= dec2 ? JsonConstants.True : JsonConstants.False; + } + else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) + { + result = val1 >= val2 ? JsonConstants.True : JsonConstants.False; + } + else + { + result = JsonConstants.Null; + } + } + else if (lhs.Type == JmesPathType.String && rhs.Type == JmesPathType.String) + { + result = string.CompareOrdinal(lhs.GetString(), rhs.GetString()) >= 0 ? JsonConstants.True : JsonConstants.False; + } + else + { + result = JsonConstants.Null; + } + return true; + } + + public override string ToString() + { + return "GteOperator"; + } + }; +} + diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs new file mode 100644 index 00000000..7d993671 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs @@ -0,0 +1,748 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace AWS.Lambda.Powertools.JMESPath +{ + internal static class JsonConstants + { + static JsonConstants() + { + True = new TrueValue(); + False = new FalseValue(); + Null = new NullValue(); + } + + internal static IValue True {get;} + internal static IValue False {get;} + internal static IValue Null {get;} + } + + internal interface IExpression + { + bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value); + + int PrecedenceLevel {get;} + + bool IsProjection {get;} + + bool IsRightAssociative {get;} + + void AddExpression(IExpression expr); + } + + // BaseExpression + internal abstract class BaseExpression : IExpression + { + public int PrecedenceLevel {get;} + + public bool IsRightAssociative {get;} + + public bool IsProjection {get;} + + internal BaseExpression(Operator oper, bool isProjection) + { + PrecedenceLevel = OperatorTable.PrecedenceLevel(oper); + IsRightAssociative = OperatorTable.IsRightAssociative(oper); + IsProjection = isProjection; + } + + public abstract bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value); + + public virtual void AddExpression(IExpression expressions) + { + } + + public override string ToString() + { + return "ToString not implemented"; + } + } + + internal sealed class IdentifierSelector : BaseExpression + { + private readonly string _identifier; + + internal IdentifierSelector(string name) + : base(Operator.Default, false) + { + _identifier = name; + } + + public override bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value) + { + if (current.Type == JmesPathType.Object && current.TryGetProperty(_identifier, out value)) + { + return true; + } + + value = JsonConstants.Null; + return true; + } + + public override string ToString() + { + return $"IdentifierSelector {_identifier}"; + } + }; + + internal sealed class CurrentNode : BaseExpression + { + internal CurrentNode() + : base(Operator.Default, false) + { + } + + public override bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value) + { + value = current; + return true; + } + + public override string ToString() + { + return "CurrentNode"; + } + }; + + internal sealed class IndexSelector : BaseExpression + { + private readonly int _index; + internal IndexSelector(int index) + : base(Operator.Default, false) + { + _index = index; + } + + public override bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value) + { + if (current.Type != JmesPathType.Array) + { + value = JsonConstants.Null; + return true; + } + var slen = current.GetArrayLength(); + if (_index >= 0 && _index < slen) + { + value = current[_index]; + } + else if ((slen + _index) >= 0 && (slen+_index) < slen) + { + var index = slen + _index; + value = current[index]; + } + else + { + value = JsonConstants.Null; + } + return true; + } + + public override string ToString() + { + return $"Index Selector {_index}"; + } + }; + + internal abstract class Projection : BaseExpression + { + private readonly List _expressions; + + internal Projection(Operator oper) + : base(oper, true) + { + _expressions = new List(); + } + + public override void AddExpression(IExpression expr) + { + if (_expressions.Count != 0 && _expressions[_expressions.Count-1].IsProjection && + (expr.PrecedenceLevel > _expressions[_expressions.Count-1].PrecedenceLevel || + (expr.PrecedenceLevel == _expressions[_expressions.Count-1].PrecedenceLevel && expr.IsRightAssociative))) + { + _expressions[_expressions.Count-1].AddExpression(expr); + } + else + { + _expressions.Add(expr); + } + } + internal bool TryApplyExpressions(DynamicResources resources, IValue current, out IValue value) + { + value = current; + foreach (var expression in _expressions) + { + if (!expression.TryEvaluate(resources, value, out value)) + { + return false; + } + } + return true; + } + }; + + internal sealed class ObjectProjection : Projection + { + internal ObjectProjection() + : base(Operator.Projection) + { + } + + public override bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value) + { + if (current.Type != JmesPathType.Object) + { + value = JsonConstants.Null; + return true; + } + + var result = new List(); + value = new ArrayValue(result); + foreach (var item in current.EnumerateObject()) + { + if (item.Value.Type == JmesPathType.Null) continue; + if (!TryApplyExpressions(resources, item.Value, out var val)) + { + return false; + } + if (val.Type != JmesPathType.Null) + { + result.Add(val); + } + } + return true; + } + + public override string ToString() + { + return "ObjectProjection"; + } + }; + + internal sealed class ListProjection : Projection + { + internal ListProjection() + : base(Operator.Projection) + { + } + + public override bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value) + { + if (current.Type != JmesPathType.Array) + { + value = JsonConstants.Null; + return true; + } + + var result = new List(); + foreach (var item in current.EnumerateArray()) + { + if (item.Type != JmesPathType.Null) + { + if (!TryApplyExpressions(resources, item, out var val)) + { + value = JsonConstants.Null; + return false; + } + if (val.Type != JmesPathType.Null) + { + result.Add(val); + } + } + } + value = new ArrayValue(result); + return true; + } + + public override string ToString() + { + return "ListProjection"; + } + }; + + internal sealed class FlattenProjection : Projection + { + internal FlattenProjection() + : base(Operator.FlattenProjection) + { + } + + public override bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value) + { + if (current.Type != JmesPathType.Array) + { + value = JsonConstants.Null; + return true; + } + + var result = new List(); + foreach (var item in current.EnumerateArray()) + { + if (item.Type == JmesPathType.Array) + { + foreach (var elem in item.EnumerateArray()) + { + if (elem.Type != JmesPathType.Null) + { + if (!TryApplyExpressions(resources, elem, out var val)) + { + value = JsonConstants.Null; + return false; + } + if (val.Type != JmesPathType.Null) + { + result.Add(val); + } + } + } + } + else + { + if (item.Type != JmesPathType.Null) + { + if (!TryApplyExpressions(resources, item, out var val)) + { + value = JsonConstants.Null; + return false; + } + if (val.Type != JmesPathType.Null) + { + result.Add(val); + } + } + } + } + + value = new ArrayValue(result); + return true; + } + + public override string ToString() + { + return "FlattenProjection"; + } + }; + + internal sealed class SliceProjection : Projection + { + private readonly Slice _slice; + + internal SliceProjection(Slice s) + : base(Operator.Projection) + { + _slice = s; + } + + public override bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value) + { + if (current.Type != JmesPathType.Array) + { + value = JsonConstants.Null; + return true; + } + + var start = _slice.GetStart(current.GetArrayLength()); + var end = _slice.GetStop(current.GetArrayLength()); + var step = _slice.Step; + + if (step == 0) + { + value = JsonConstants.Null; + return false; + } + + var result = new List(); + if (step > 0) + { + if (start < 0) + { + start = 0; + } + if (end > current.GetArrayLength()) + { + end = current.GetArrayLength(); + } + for (var i = start; i < end; i += step) + { + if (!TryApplyExpressions(resources, current[i], out var val)) + { + value = JsonConstants.Null; + return false; + } + if (val.Type != JmesPathType.Null) + { + result.Add(val); + } + } + } + else + { + if (start >= current.GetArrayLength()) + { + start = current.GetArrayLength() - 1; + } + if (end < -1) + { + end = -1; + } + for (var i = start; i > end; i += step) + { + if (!TryApplyExpressions(resources, current[i], out var val)) + { + value = JsonConstants.Null; + return false; + } + if (val.Type != JmesPathType.Null) + { + result.Add(val); + } + } + } + + value = new ArrayValue(result); + return true; + } + + public override string ToString() + { + return "SliceProjection"; + } + }; + + internal sealed class FilterExpression : Projection + { + private readonly Expression _expr; + + internal FilterExpression(Expression expr) + : base(Operator.Projection) + { + _expr = expr; + } + + public override bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value) + { + if (current.Type != JmesPathType.Array) + { + value = JsonConstants.Null; + return true; + } + var result = new List(); + + foreach (var item in current.EnumerateArray()) + { + if (!_expr.TryEvaluate(resources, item, out var test)) + { + value = JsonConstants.Null; + return false; + } + if (Expression.IsTrue(test)) + { + if (!TryApplyExpressions(resources, item, out var val)) + { + value = JsonConstants.Null; + return false; + } + if (val.Type != JmesPathType.Null) + { + result.Add(val); + } + } + } + value = new ArrayValue(result); + return true; + } + + public override string ToString() + { + return "FilterExpression"; + } + }; + + internal sealed class MultiSelectList : BaseExpression + { + private readonly IList _expressions; + + internal MultiSelectList(IList expressions) + : base(Operator.Default, false) + { + _expressions = expressions; + } + + public override bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value) + { + if (current.Type == JmesPathType.Null) + { + value = JsonConstants.Null; + return true; + } + var result = new List(); + + foreach (var expr in _expressions) + { + if (!expr.TryEvaluate(resources, current, out var val)) + { + value = JsonConstants.Null; + return false; + } + result.Add(val); + } + value = new ArrayValue(result); + return true; + } + + public override string ToString() + { + return "MultiSelectList"; + } + }; + + internal struct KeyExpressionPair + { + internal string Key {get;} + internal Expression Expression {get;} + + internal KeyExpressionPair(string key, Expression expression) + { + Key = key; + Expression = expression; + } + }; + + internal sealed class MultiSelectHash : BaseExpression + { + private readonly IList _keyExprPairs; + + internal MultiSelectHash(IList keyExprPairs) + : base(Operator.Default, false) + { + _keyExprPairs = keyExprPairs; + } + + public override bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value) + { + if (current.Type == JmesPathType.Null) + { + value = JsonConstants.Null; + return true; + } + var result = new Dictionary(); + foreach (var item in _keyExprPairs) + { + if (!item.Expression.TryEvaluate(resources, current, out var val)) + { + value = JsonConstants.Null; + return false; + } + result.Add(item.Key, val); + } + + value = new ObjectValue(result); + return true; + } + + public override string ToString() + { + return "MultiSelectHash"; + } + } + + internal sealed class FunctionExpression : BaseExpression + { + private readonly Expression _expr; + + internal FunctionExpression(Expression expr) + : base(Operator.Default, false) + { + _expr = expr; + } + + public override bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value) + { + if (!_expr.TryEvaluate(resources, current, out var val)) + { + value = JsonConstants.Null; + return true; + } + value = val; + return true; + } + + public override string ToString() + { + return "FunctionExpression"; + } + } + + internal class Expression + { + private readonly Token[] _tokens; + + internal Expression(Token[] tokens) + { + _tokens = tokens; + } + + public bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue result) + { + var stack = new Stack(); + IList argStack = new List(); + + var rootPtr = current; + + for (var i = _tokens.Length-1; i >= 0; --i) + { + var token = _tokens[i]; + switch (token.Type) + { + case TokenType.Literal: + { + stack.Push(token.GetValue()); + break; + } + case TokenType.BeginExpressionType: + { + Debug.Assert(i>0); + token = _tokens[--i]; + Debug.Assert(token.Type == TokenType.Expression); + Debug.Assert(stack.Count != 0); + stack.Pop(); + stack.Push(new ExpressionValue(token.GetExpression())); + break; + } + case TokenType.Pipe: + { + Debug.Assert(stack.Count != 0); + rootPtr = stack.Peek(); + break; + } + case TokenType.CurrentNode: + stack.Push(rootPtr); + break; + case TokenType.Expression: + { + Debug.Assert(stack.Count != 0); + var ptr = stack.Pop(); + if (!token.GetExpression().TryEvaluate(resources, ptr, out var val)) + { + result = JsonConstants.Null; + return false; + } + stack.Push(val); + break; + } + case TokenType.UnaryOperator: + { + Debug.Assert(stack.Count >= 1); + var rhs = stack.Pop(); + if (!token.GetUnaryOperator().TryEvaluate(rhs, out var val)) + { + result = JsonConstants.Null; + return false; + } + stack.Push(val); + break; + } + case TokenType.BinaryOperator: + { + Debug.Assert(stack.Count >= 2); + var rhs = stack.Pop(); + var lhs = stack.Pop(); + if (!token.GetBinaryOperator().TryEvaluate(lhs, rhs, out var val)) + { + result = JsonConstants.Null; + return false; + } + stack.Push(val); + break; + } + case TokenType.Argument: + { + Debug.Assert(stack.Count != 0); + argStack.Add(stack.Pop()); + break; + } + case TokenType.Function: + { + if (token.GetFunction().Arity != null && token.GetFunction().Arity != argStack.Count()) + { + // airty error should never happen here + result = JsonConstants.Null; + return false; + } + + if (!token.GetFunction().TryEvaluate(resources, argStack, out var val)) + { + result = JsonConstants.Null; + return false; + } + argStack.Clear(); + stack.Push(val); + break; + } + default: + break; + } + } + Debug.Assert(stack.Count == 1); + result = stack.Peek(); + return true; + } + + internal static bool IsFalse(IValue val) + { + var comparer = ValueEqualityComparer.Instance; + switch (val.Type) + { + case JmesPathType.False: + return true; + case JmesPathType.Null: + return true; + case JmesPathType.Array: + return val.GetArrayLength() == 0; + case JmesPathType.Object: + return val.EnumerateObject().MoveNext() == false; + case JmesPathType.String: + return val.GetString().Length == 0; + case JmesPathType.Number: + return false; + default: + return false; + } + } + + internal static bool IsTrue(IValue val) + { + return !IsFalse(val); + } + } +} + diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Function.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Function.cs new file mode 100644 index 00000000..2ee2d28b --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Function.cs @@ -0,0 +1,1492 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Text; +using System.Text.Json; + +namespace AWS.Lambda.Powertools.JMESPath +{ + internal sealed class SortByComparer : IComparer, System.Collections.IComparer + { + private readonly DynamicResources _resources; + private readonly IExpression _expr; + + internal bool IsValid { get; set; } = true; + + internal SortByComparer(DynamicResources resources, + IExpression expr) + { + _resources = resources; + _expr = expr; + } + + public int Compare(IValue lhs, IValue rhs) + { + var comparer = ValueComparer.Instance; + + if (!IsValid) + { + return 0; + } + + if (!_expr.TryEvaluate(_resources, lhs, out var key1)) + { + IsValid = false; + return 0; + } + + var isNumber1 = key1.Type == JmesPathType.Number; + var isString1 = key1.Type == JmesPathType.String; + if (!(isNumber1 || isString1)) + { + IsValid = false; + return 0; + } + + if (!_expr.TryEvaluate(_resources, rhs, out var key2)) + { + IsValid = false; + return 0; + } + + var isNumber2 = key2.Type == JmesPathType.Number; + var isString2 = key2.Type == JmesPathType.String; + if (isNumber2 == isNumber1 && isString2 == isString1) return comparer.Compare(key1, key2); + IsValid = false; + return 0; + } + + int System.Collections.IComparer.Compare(object x, object y) + { + return Compare((IValue)x, (IValue)y); + } + } + + internal interface IFunction + { + int? Arity { get; } + bool TryEvaluate(DynamicResources resources, IList args, out IValue element); + }; + + internal abstract class BaseFunction : IFunction + { + internal BaseFunction(int? argCount) + { + Arity = argCount; + } + + public int? Arity { get; } + + public abstract bool TryEvaluate(DynamicResources resources, IList args, out IValue element); + }; + + internal sealed class AbsFunction : BaseFunction + { + internal AbsFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg = args[0]; + + if (arg.TryGetDecimal(out var decVal)) + { + result = new DecimalValue(decVal >= 0 ? decVal : -decVal); + return true; + } + + if (arg.TryGetDouble(out var dblVal)) + { + result = new DecimalValue(dblVal >= 0 ? decVal : new decimal(-dblVal)); + return true; + } + + result = JsonConstants.Null; + return false; + } + + public override string ToString() + { + return "abs"; + } + }; + + internal sealed class AvgFunction : BaseFunction + { + internal AvgFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + if (arg0.Type != JmesPathType.Array || arg0.GetArrayLength() == 0) + { + result = JsonConstants.Null; + return false; + } + + if (!SumFunction.Instance.TryEvaluate(resources, args, out var sum)) + { + result = JsonConstants.Null; + return false; + } + + if (sum.TryGetDecimal(out var decVal)) + { + result = new DecimalValue(decVal / arg0.GetArrayLength()); + return true; + } + + if (sum.TryGetDouble(out var dblVal)) + { + result = new DoubleValue(dblVal / arg0.GetArrayLength()); + return true; + } + result = JsonConstants.Null; + return false; + } + + public override string ToString() + { + return "avg"; + } + }; + + internal sealed class CeilFunction : BaseFunction + { + internal CeilFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, + out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var val = args[0]; + if (val.Type != JmesPathType.Number) + { + result = JsonConstants.Null; + return false; + } + + if (val.TryGetDecimal(out var decVal)) + { + result = new DecimalValue(decimal.Ceiling(decVal)); + return true; + } + + if (val.TryGetDouble(out var dblVal)) + { + result = new DoubleValue(Math.Ceiling(dblVal)); + return true; + } + result = JsonConstants.Null; + return false; + } + + public override string ToString() + { + return "ceil"; + } + }; + + internal sealed class ContainsFunction : BaseFunction + { + internal ContainsFunction() + : base(2) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, + out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + var arg1 = args[1]; + + var comparer = ValueEqualityComparer.Instance; + + switch (arg0.Type) + { + case JmesPathType.Array: + foreach (var item in arg0.EnumerateArray()) + { + if (comparer.Equals(item, arg1)) + { + result = JsonConstants.True; + return true; + } + } + + result = JsonConstants.False; + return true; + case JmesPathType.String: + { + if (arg1.Type != JmesPathType.String) + { + result = JsonConstants.Null; + return false; + } + + var s0 = arg0.GetString(); + var s1 = arg1.GetString(); + if (s0.Contains(s1)) + { + result = JsonConstants.True; + return true; + } + + result = JsonConstants.False; + return true; + } + default: + { + result = JsonConstants.Null; + return false; + } + } + } + + public override string ToString() + { + return "contains"; + } + }; + + internal sealed class EndsWithFunction : BaseFunction + { + internal EndsWithFunction() + : base(2) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, + out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + var arg1 = args[1]; + if (arg0.Type != JmesPathType.String + || arg1.Type != JmesPathType.String) + { + result = JsonConstants.Null; + return false; + } + + var s0 = arg0.GetString(); + var s1 = arg1.GetString(); + + if (s0.EndsWith(s1)) + { + result = JsonConstants.True; + } + else + { + result = JsonConstants.False; + } + + return true; + } + + public override string ToString() + { + return "ends_with"; + } + }; + + internal sealed class FloorFunction : BaseFunction + { + internal FloorFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, + out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var val = args[0]; + if (val.Type != JmesPathType.Number) + { + result = JsonConstants.Null; + return false; + } + + if (val.TryGetDecimal(out var decVal)) + { + result = new DecimalValue(decimal.Floor(decVal)); + return true; + } + + if (val.TryGetDouble(out var dblVal)) + { + result = new DoubleValue(Math.Floor(dblVal)); + return true; + } + result = JsonConstants.Null; + return false; + } + + public override string ToString() + { + return "floor"; + } + }; + + internal sealed class JoinFunction : BaseFunction + { + internal JoinFunction() + : base(2) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + var arg1 = args[1]; + + if (!(arg0.Type == JmesPathType.String && args[1].Type == JmesPathType.Array)) + { + result = JsonConstants.Null; + return false; + } + + var sep = arg0.GetString(); + var buf = new StringBuilder(); + foreach (var j in arg1.EnumerateArray()) + { + if (j.Type != JmesPathType.String) + { + result = JsonConstants.Null; + return false; + } + + if (buf.Length != 0) + { + buf.Append(sep); + } + + var sv = j.GetString(); + buf.Append(sv); + } + + result = new StringValue(buf.ToString()); + return true; + } + + public override string ToString() + { + return "join"; + } + } + + internal sealed class KeysFunction : BaseFunction + { + internal KeysFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, + out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + if (arg0.Type != JmesPathType.Object) + { + result = JsonConstants.Null; + return false; + } + + var values = new List(); + + foreach (var property in arg0.EnumerateObject()) + { + values.Add(new StringValue(property.Name)); + } + + result = new ArrayValue(values); + return true; + } + + public override string ToString() + { + return "keys"; + } + } + + internal sealed class LengthFunction : BaseFunction + { + internal LengthFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, + out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + + switch (arg0.Type) + { + case JmesPathType.Object: + { + var count = 0; + foreach (var unused in arg0.EnumerateObject()) + { + ++count; + } + + result = new DecimalValue(new decimal(count)); + return true; + } + case JmesPathType.Array: + result = new DecimalValue(new decimal(arg0.GetArrayLength())); + return true; + case JmesPathType.String: + { + var bytes = Encoding.UTF32.GetBytes(arg0.GetString().ToCharArray()); + result = new DecimalValue(new decimal(bytes.Length / 4)); + return true; + } + default: + { + result = JsonConstants.Null; + return false; + } + } + } + + public override string ToString() + { + return "length"; + } + }; + + internal sealed class MaxFunction : BaseFunction + { + internal MaxFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, + out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + if (arg0.Type != JmesPathType.Array) + { + result = JsonConstants.Null; + return false; + } + + if (arg0.GetArrayLength() == 0) + { + result = JsonConstants.Null; + return false; + } + + var isNumber = arg0[0].Type == JmesPathType.Number; + var isString = arg0[0].Type == JmesPathType.String; + if (!isNumber && !isString) + { + result = JsonConstants.Null; + return false; + } + + var greater = GtOperator.Instance; + var index = 0; + for (var i = 1; i < arg0.GetArrayLength(); ++i) + { + if (!(((arg0[i].Type == JmesPathType.Number) == isNumber) && + (arg0[i].Type == JmesPathType.String) == isString)) + { + result = JsonConstants.Null; + return false; + } + + if (!greater.TryEvaluate(arg0[i], arg0[index], out var value)) + { + result = JsonConstants.Null; + return false; + } + + if (Expression.IsTrue(value)) + { + index = i; + } + } + + result = arg0[index]; + return true; + } + + public override string ToString() + { + return "max"; + } + } + + internal sealed class MaxByFunction : BaseFunction + { + internal MaxByFunction() + : base(2) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + if (!(args[0].Type == JmesPathType.Array && args[1].Type == JmesPathType.Expression)) + { + result = JsonConstants.Null; + return false; + } + + var arg0 = args[0]; + if (arg0.GetArrayLength() == 0) + { + result = JsonConstants.Null; + return true; + } + + var expr = args[1].GetExpression(); + + if (!expr.TryEvaluate(resources, arg0[0], out var key1)) + { + result = JsonConstants.Null; + return false; + } + + var isNumber1 = key1.Type == JmesPathType.Number; + var isString1 = key1.Type == JmesPathType.String; + if (!(isNumber1 || isString1)) + { + result = JsonConstants.Null; + return false; + } + + var greater = GtOperator.Instance; + var index = 0; + for (var i = 1; i < arg0.GetArrayLength(); ++i) + { + if (!expr.TryEvaluate(resources, arg0[i], out var key2)) + { + result = JsonConstants.Null; + return false; + } + + var isNumber2 = key2.Type == JmesPathType.Number; + var isString2 = key2.Type == JmesPathType.String; + if (!(isNumber2 == isNumber1 && isString2 == isString1)) + { + result = JsonConstants.Null; + return false; + } + + if (!greater.TryEvaluate(key2, key1, out var value)) + { + result = JsonConstants.Null; + return false; + } + + if (value.Type == JmesPathType.True) + { + key1 = key2; + index = i; + } + } + + result = arg0[index]; + return true; + } + + public override string ToString() + { + return "max_by"; + } + } + + internal sealed class MinFunction : BaseFunction + { + internal MinFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, + out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + if (arg0.Type != JmesPathType.Array) + { + result = JsonConstants.Null; + return false; + } + + if (arg0.GetArrayLength() == 0) + { + result = JsonConstants.Null; + return false; + } + + var isNumber = arg0[0].Type == JmesPathType.Number; + var isString = arg0[0].Type == JmesPathType.String; + if (!isNumber && !isString) + { + result = JsonConstants.Null; + return false; + } + + var less = LtOperator.Instance; + var index = 0; + for (var i = 1; i < arg0.GetArrayLength(); ++i) + { + if (!(((arg0[i].Type == JmesPathType.Number) == isNumber) && + (arg0[i].Type == JmesPathType.String) == isString)) + { + result = JsonConstants.Null; + return false; + } + + if (!less.TryEvaluate(arg0[i], arg0[index], out var value)) + { + result = JsonConstants.Null; + return false; + } + + if (value.Type == JmesPathType.True) + { + index = i; + } + } + + result = arg0[index]; + return true; + } + + public override string ToString() + { + return "min"; + } + } + + internal sealed class MergeFunction : BaseFunction + { + internal MergeFunction() + : base(null) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + { + if (!args.Any()) + { + result = JsonConstants.Null; + return false; + } + + var arg0 = args[0]; + if (arg0.Type != JmesPathType.Object) + { + result = JsonConstants.Null; + return false; + } + + if (args.Count == 1) + { + result = arg0; + return true; + } + + var dict = new Dictionary(); + for (var i = 0; i < args.Count; ++i) + { + var argi = args[i]; + if (argi.Type != JmesPathType.Object) + { + result = JsonConstants.Null; + return false; + } + + foreach (var item in argi.EnumerateObject()) + { + if (!dict.TryAdd(item.Name, item.Value)) + { + dict.Remove(item.Name); + dict.Add(item.Name, item.Value); + } + } + } + + result = new ObjectValue(dict); + return true; + } + + public override string ToString() + { + return "merge"; + } + } + + internal sealed class NotNullFunction : BaseFunction + { + internal NotNullFunction() + : base(null) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + { + foreach (var arg in args) + { + if (arg.Type == JmesPathType.Null) continue; + result = arg; + return true; + } + + result = JsonConstants.Null; + return true; + } + + public override string ToString() + { + return "not_null"; + } + } + + internal sealed class ReverseFunction : BaseFunction + { + internal ReverseFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + switch (arg0.Type) + { + case JmesPathType.String: + { + result = new StringValue(string.Join("", GraphemeClusters(arg0.GetString()).Reverse().ToArray())); + return true; + } + case JmesPathType.Array: + { + var list = new List(); + for (var i = arg0.GetArrayLength() - 1; i >= 0; --i) + { + list.Add(arg0[i]); + } + + result = new ArrayValue(list); + return true; + } + default: + result = JsonConstants.Null; + return false; + } + } + + private static IEnumerable GraphemeClusters(string s) + { + var enumerator = StringInfo.GetTextElementEnumerator(s); + while (enumerator.MoveNext()) + { + yield return (string)enumerator.Current; + } + } + + public override string ToString() + { + return "reverse"; + } + } + + internal sealed class MapFunction : BaseFunction + { + internal MapFunction() + : base(2) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + if (!(args[0].Type == JmesPathType.Expression && args[1].Type == JmesPathType.Array)) + { + result = JsonConstants.Null; + return false; + } + + var expr = args[0].GetExpression(); + var arg0 = args[1]; + + var list = new List(); + + foreach (var item in arg0.EnumerateArray()) + { + if (!expr.TryEvaluate(resources, item, out var val)) + { + result = JsonConstants.Null; + return false; + } + + list.Add(val); + } + + result = new ArrayValue(list); + return true; + } + + public override string ToString() + { + return "map"; + } + } + + internal sealed class MinByFunction : BaseFunction + { + internal MinByFunction() + : base(2) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + if (!(args[0].Type == JmesPathType.Array && args[1].Type == JmesPathType.Expression)) + { + result = JsonConstants.Null; + return false; + } + + var arg0 = args[0]; + if (arg0.GetArrayLength() == 0) + { + result = JsonConstants.Null; + return true; + } + + var expr = args[1].GetExpression(); + + if (!expr.TryEvaluate(resources, arg0[0], out var key1)) + { + result = JsonConstants.Null; + return false; + } + + var isNumber1 = key1.Type == JmesPathType.Number; + var isString1 = key1.Type == JmesPathType.String; + if (!(isNumber1 || isString1)) + { + result = JsonConstants.Null; + return false; + } + + var lessor = LtOperator.Instance; + var index = 0; + for (var i = 1; i < arg0.GetArrayLength(); ++i) + { + if (!expr.TryEvaluate(resources, arg0[i], out var key2)) + { + result = JsonConstants.Null; + return false; + } + + var isNumber2 = key2.Type == JmesPathType.Number; + var isString2 = key2.Type == JmesPathType.String; + if (!(isNumber2 == isNumber1 && isString2 == isString1)) + { + result = JsonConstants.Null; + return false; + } + + if (!lessor.TryEvaluate(key2, key1, out var value)) + { + result = JsonConstants.Null; + return false; + } + + if (value.Type == JmesPathType.True) + { + key1 = key2; + index = i; + } + } + + result = arg0[index]; + return true; + } + + public override string ToString() + { + return "min_by"; + } + } + + internal sealed class SortFunction : BaseFunction + { + internal SortFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + if (arg0.Type != JmesPathType.Array) + { + result = JsonConstants.Null; + return false; + } + + if (arg0.GetArrayLength() <= 1) + { + result = arg0; + return true; + } + + var isNumber1 = arg0[0].Type == JmesPathType.Number; + var isString1 = arg0[0].Type == JmesPathType.String; + if (!isNumber1 && !isString1) + { + result = JsonConstants.Null; + return false; + } + + var comparer = ValueComparer.Instance; + + var list = new List(); + foreach (var item in arg0.EnumerateArray()) + { + var isNumber2 = item.Type == JmesPathType.Number; + var isString2 = item.Type == JmesPathType.String; + if (!(isNumber2 == isNumber1 && isString2 == isString1)) + { + result = JsonConstants.Null; + return false; + } + + list.Add(item); + } + + list.Sort(comparer); + result = new ArrayValue(list); + return true; + } + + public override string ToString() + { + return "sort"; + } + } + + internal sealed class SortByFunction : BaseFunction + { + internal SortByFunction() + : base(2) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + if (!(args[0].Type == JmesPathType.Array && args[1].Type == JmesPathType.Expression)) + { + result = JsonConstants.Null; + return false; + } + + var arg0 = args[0]; + if (arg0.GetArrayLength() <= 1) + { + result = arg0; + return true; + } + + var expr = args[1].GetExpression(); + + var list = new List(); + foreach (var item in arg0.EnumerateArray()) + { + list.Add(item); + } + + var comparer = new SortByComparer(resources, expr); + list.Sort(comparer); + if (comparer.IsValid) + { + result = new ArrayValue(list); + return true; + } + + result = JsonConstants.Null; + return false; + } + + public override string ToString() + { + return "sort_by"; + } + } + + internal sealed class StartsWithFunction : BaseFunction + { + internal StartsWithFunction() + : base(2) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, + out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + var arg1 = args[1]; + if (arg0.Type != JmesPathType.String + || arg1.Type != JmesPathType.String) + { + result = JsonConstants.Null; + return false; + } + + var s0 = arg0.GetString(); + var s1 = arg1.GetString(); + result = s0.StartsWith(s1) ? JsonConstants.True : JsonConstants.False; + + return true; + } + + public override string ToString() + { + return "starts_with"; + } + } + + internal sealed class SumFunction : BaseFunction + { + internal static SumFunction Instance { get; } = new(); + + internal SumFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, + out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + if (arg0.Type != JmesPathType.Array) + { + result = JsonConstants.Null; + return false; + } + + foreach (var item in arg0.EnumerateArray()) + { + if (item.Type != JmesPathType.Number) + { + result = JsonConstants.Null; + return false; + } + } + + var success = true; + decimal decSum = 0; + foreach (var item in arg0.EnumerateArray()) + { + if (!item.TryGetDecimal(out var dec)) + { + success = false; + break; + } + + decSum += dec; + } + + if (success) + { + result = new DecimalValue(decSum); + return true; + } + + double dblSum = 0; + foreach (var item in arg0.EnumerateArray()) + { + if (!item.TryGetDouble(out var dbl)) + { + result = JsonConstants.Null; + return false; + } + + dblSum += dbl; + } + + result = new DoubleValue(dblSum); + return true; + } + + public override string ToString() + { + return "sum"; + } + } + + internal sealed class ToArrayFunction : BaseFunction + { + internal ToArrayFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + if (arg0.Type == JmesPathType.Array) + { + result = arg0; + return true; + } + + var list = new List { arg0 }; + result = new ArrayValue(list); + return true; + } + + public override string ToString() + { + return "to_array"; + } + } + + internal sealed class ToNumberFunction : BaseFunction + { + internal ToNumberFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, + out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + switch (arg0.Type) + { + case JmesPathType.Number: + result = arg0; + return true; + case JmesPathType.String: + { + var s = arg0.GetString(); + if (decimal.TryParse(s, out var dec)) + { + result = new DecimalValue(dec); + return true; + } + + if (double.TryParse(s, out var dbl)) + { + result = new DoubleValue(dbl); + return true; + } + result = JsonConstants.Null; + return false; + } + default: + result = JsonConstants.Null; + return false; + } + } + + public override string ToString() + { + return "to_number"; + } + } + + internal sealed class ToStringFunction : BaseFunction + { + internal ToStringFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + if (args[0].Type == JmesPathType.Expression) + { + result = JsonConstants.Null; + return false; + } + + var arg0 = args[0]; + switch (arg0.Type) + { + case JmesPathType.String: + result = arg0; + return true; + case JmesPathType.Expression: + result = JsonConstants.Null; + return false; + default: + result = new StringValue(arg0.ToString()); + return true; + } + } + + public override string ToString() + { + return "to_string"; + } + } + + internal sealed class ValuesFunction : BaseFunction + { + internal ValuesFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + if (arg0.Type != JmesPathType.Object) + { + result = JsonConstants.Null; + return false; + } + + var list = new List(); + + foreach (var item in arg0.EnumerateObject()) + { + list.Add(item.Value); + } + + result = new ArrayValue(list); + return true; + } + + public override string ToString() + { + return "values"; + } + } + + internal sealed class TypeFunction : BaseFunction + { + internal TypeFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + + switch (arg0.Type) + { + case JmesPathType.Number: + result = new StringValue("number"); + return true; + case JmesPathType.True: + case JmesPathType.False: + result = new StringValue("boolean"); + return true; + case JmesPathType.String: + result = new StringValue("string"); + return true; + case JmesPathType.Object: + result = new StringValue("object"); + return true; + case JmesPathType.Array: + result = new StringValue("array"); + return true; + case JmesPathType.Null: + result = new StringValue("null"); + return true; + default: + result = JsonConstants.Null; + return false; + } + } + + public override string ToString() + { + return "type"; + } + } + + internal sealed class JsonFunction : BaseFunction + { + /// + public JsonFunction() + : base(1) + { + } + + public override string ToString() + { + return "powertools_json"; + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + result = args[0]; + + //result = new JsonElementValue(JsonNode.Parse(args[0].GetString()).Deserialize()); + return true; + } + } + + internal sealed class Base64Function : BaseFunction + { + /// + public Base64Function() + : base(1) + { + } + + public override string ToString() + { + return "powertools_base64"; + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + var base64StringBytes = Convert.FromBase64String(args[0].GetString()); + var doc = JsonDocument.Parse(base64StringBytes); + result = new JsonElementValue(doc.RootElement); + return true; + } + } + + internal sealed class Base64GzipFunction : BaseFunction + { + /// + public Base64GzipFunction() + : base(1) + { + } + + public override string ToString() + { + return "powertools_base64_gzip"; + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var compressedBytes = Convert.FromBase64String(args[0].GetString()); + + using var compressedStream = new MemoryStream(compressedBytes); + using var decompressedStream = new MemoryStream(); + using (var gzipStream = new GZipStream(compressedStream, CompressionMode.Decompress)) + { + gzipStream.CopyTo(decompressedStream); + } + + var doc = JsonDocument.Parse(Encoding.UTF8.GetString(decompressedStream.ToArray())); + result = new JsonElementValue(doc.RootElement); + + return true; + } + } + + internal sealed class BuiltInFunctions + { + internal static BuiltInFunctions Instance { get; } = new(); + + private readonly Dictionary _functions = new(); + + private BuiltInFunctions() + { + _functions.Add("abs", new AbsFunction()); + _functions.Add("avg", new AvgFunction()); + _functions.Add("ceil", new CeilFunction()); + _functions.Add("contains", new ContainsFunction()); + _functions.Add("ends_with", new EndsWithFunction()); + _functions.Add("floor", new FloorFunction()); + _functions.Add("join", new JoinFunction()); + _functions.Add("keys", new KeysFunction()); + _functions.Add("length", new LengthFunction()); + _functions.Add("map", new MapFunction()); + _functions.Add("max", new MaxFunction()); + _functions.Add("max_by", new MaxByFunction()); + _functions.Add("merge", new MergeFunction()); + _functions.Add("min", new MinFunction()); + _functions.Add("min_by", new MinByFunction()); + _functions.Add("not_null", new NotNullFunction()); + _functions.Add("reverse", new ReverseFunction()); + _functions.Add("sort", new SortFunction()); + _functions.Add("sort_by", new SortByFunction()); + _functions.Add("starts_with", new StartsWithFunction()); + _functions.Add("sum", new SumFunction()); + _functions.Add("to_array", new ToArrayFunction()); + _functions.Add("to_number", new ToNumberFunction()); + _functions.Add("to_string", new ToStringFunction()); + _functions.Add("type", new TypeFunction()); + _functions.Add("values", new ValuesFunction()); + _functions.Add("powertools_json", new JsonFunction()); + _functions.Add("powertools_base64", new Base64Function()); + _functions.Add("powertools_base64_gzip", new Base64GzipFunction()); + } + + internal bool TryGetFunction(string name, out IFunction func) + { + return _functions.TryGetValue(name, out func); + } + }; +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/InternalsVisibleTo.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/InternalsVisibleTo.cs new file mode 100644 index 00000000..652795a2 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/InternalsVisibleTo.cs @@ -0,0 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("AWS.Lambda.Powertools.Idempotency")] \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs new file mode 100644 index 00000000..d7e0efbc --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs @@ -0,0 +1,1749 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Text.Json; + +namespace AWS.Lambda.Powertools.JMESPath +{ + /// + /// Defines a custom exception object that is thrown when JMESPath parsing fails. + /// + + public sealed class JmesPathParseException : Exception + { + /// + /// The line in the JMESPath string where a parse error was detected. + /// + public int LineNumber {get;} + + /// + /// The column in the JMESPath string where a parse error was detected. + /// + public int ColumnNumber {get;} + + internal JmesPathParseException(string message, int line, int column) + : base(message) + { + LineNumber = line; + ColumnNumber = column; + } + + /// + /// Returns an error message that describes the current exception. + /// + /// A string representation of the current exception. + public override string ToString () + { + return $"{Message} at line {LineNumber} and column {ColumnNumber}"; + } + }; + + internal enum JmesPathState + { + Start, + LhsExpression, + RhsExpression, + SubExpression, + ExpressionType, + ComparatorExpression, + FunctionExpression, + Argument, + ExpressionOrExpressionType, + QuotedString, + RawString, + RawStringEscapeChar, + QuotedStringEscapeChar, + EscapeU1, + EscapeU2, + EscapeU3, + EscapeU4, + EscapeExpectSurrogatePair1, + EscapeExpectSurrogatePair2, + EscapeU5, + EscapeU6, + EscapeU7, + EscapeU8, + Literal, + KeyExpr, + ValExpr, + IdentifierOrFunctionExpr, + UnquotedString, + KeyValExpr, + Number, + Digit, + IndexOrSliceExpression, + BracketSpecifier, + BracketSpecifierOrMultiSelectList, + Filter, + MultiSelectList, + MultiSelectHash, + RhsSliceExpressionStop, + RhsSliceExpressionStep, + ExpectRightBracket, + ExpectRightParen, + ExpectDot, + ExpectRightBrace, + ExpectColon, + ExpectMultiSelectList, + CmpLtOrLte, + CmpEq, + CmpGtOrGte, + CmpNe, + ExpectPipeOrOr, + ExpectAnd + } + + internal ref struct JmesPathParser + { + private ReadOnlyMemory _source; + private ReadOnlySpan _span; + private int _index; + private int _column; + private int _line; + private Stack _stateStack; + private Stack_outputStack; + private Stack_operatorStack; + + internal JmesPathParser(string input) + { + _source = input.AsMemory(); + _span = input.AsSpan(); + _index = 0; + _column = 1; + _line = 1; + _stateStack = new Stack(); + _outputStack = new Stack(); + _operatorStack = new Stack(); + } + + internal JsonTransformer Parse() + { + _stateStack.Clear(); + _outputStack.Clear(); + _operatorStack.Clear(); + _index = 0; + _line = 1; + _column = 1; + + var buffer = new StringBuilder(); + int? sliceStart = null; + int? sliceStop = null; + var sliceStep = 1; + uint cp = 0; + uint cp2 = 0; + + PushToken(new Token(TokenType.CurrentNode)); + _stateStack.Push(JmesPathState.Start); + + while (_index < _span.Length) + { + switch (_stateStack.Peek()) + { + default: + break; + case JmesPathState.Start: + { + _stateStack.Pop(); + _stateStack.Push(JmesPathState.RhsExpression); + _stateStack.Push(JmesPathState.LhsExpression); + break; + } + case JmesPathState.RhsExpression: + switch(_span[_index]) + { + case ' ':case '\t':case '\r':case '\n': + SkipWhiteSpace(); + break; + case '.': + ++_index; + ++_column; + _stateStack.Push(JmesPathState.SubExpression); + break; + case '|': + ++_index; + ++_column; + _stateStack.Push(JmesPathState.LhsExpression); + _stateStack.Push(JmesPathState.ExpectPipeOrOr); + break; + case '&': + ++_index; + ++_column; + _stateStack.Push(JmesPathState.LhsExpression); + _stateStack.Push(JmesPathState.ExpectAnd); + break; + case '<': + case '>': + case '=': + { + _stateStack.Push(JmesPathState.ComparatorExpression); + break; + } + case '!': + { + ++_index; + ++_column; + _stateStack.Push(JmesPathState.LhsExpression); + _stateStack.Push(JmesPathState.CmpNe); + break; + } + case ')': + { + _stateStack.Pop(); + break; + } + case '[': + _stateStack.Push(JmesPathState.BracketSpecifier); + ++_index; + ++_column; + break; + default: + if (_stateStack.Count > 1) + { + _stateStack.Pop(); + } + else + { + throw new JmesPathParseException("Syntax error", _line, _column); + } + break; + } + break; + case JmesPathState.ComparatorExpression: + switch(_span[_index]) + { + case ' ':case '\t':case '\r':case '\n': + SkipWhiteSpace(); + break; + case '<': + ++_index; + ++_column; + _stateStack.Pop(); + _stateStack.Push(JmesPathState.LhsExpression); + _stateStack.Push(JmesPathState.CmpLtOrLte); + break; + case '>': + ++_index; + ++_column; + _stateStack.Pop(); + _stateStack.Push(JmesPathState.LhsExpression); + _stateStack.Push(JmesPathState.CmpGtOrGte); + break; + case '=': + { + ++_index; + ++_column; + _stateStack.Pop(); + _stateStack.Push(JmesPathState.LhsExpression); + _stateStack.Push(JmesPathState.CmpEq); + break; + } + default: + if (_stateStack.Count > 1) + { + _stateStack.Pop(); + } + else + { + throw new JmesPathParseException("Syntax error", _line, _column); + } + break; + } + break; + case JmesPathState.LhsExpression: + { + switch (_span[_index]) + { + case ' ':case '\t':case '\r':case '\n': + SkipWhiteSpace(); + break; + case '\"': + _stateStack.Pop(); + _stateStack.Push(JmesPathState.ValExpr); + _stateStack.Push(JmesPathState.QuotedString); + ++_index; + ++_column; + break; + case '\'': + _stateStack.Pop(); + _stateStack.Push(JmesPathState.RawString); + ++_index; + ++_column; + break; + case '`': + _stateStack.Pop(); + _stateStack.Push(JmesPathState.Literal); + ++_index; + ++_column; + break; + case '{': + PushToken(new Token(TokenType.BeginMultiSelectHash)); + _stateStack.Pop(); + _stateStack.Push(JmesPathState.MultiSelectHash); + ++_index; + ++_column; + break; + case '*': // wildcard + PushToken(new Token(new ObjectProjection())); + _stateStack.Pop(); + ++_index; + ++_column; + break; + case '(': + { + ++_index; + ++_column; + PushToken(new Token(TokenType.LeftParen)); + _stateStack.Pop(); + _stateStack.Push(JmesPathState.ExpectRightParen); + _stateStack.Push(JmesPathState.RhsExpression); + _stateStack.Push(JmesPathState.LhsExpression); + break; + } + case '!': + { + ++_index; + ++_column; + PushToken(new Token(NotOperator.Instance)); + break; + } + case '@': + ++_index; + ++_column; + PushToken(new Token(new CurrentNode())); + _stateStack.Pop(); + break; + case '[': + _stateStack.Pop(); + _stateStack.Push(JmesPathState.BracketSpecifierOrMultiSelectList); + ++_index; + ++_column; + break; + default: + if ((_span[_index] >= 'A' && _span[_index] <= 'Z') || (_span[_index] >= 'a' && _span[_index] <= 'z') || (_span[_index] == '_')) + { + _stateStack.Pop(); + _stateStack.Push(JmesPathState.IdentifierOrFunctionExpr); + _stateStack.Push(JmesPathState.UnquotedString); + buffer.Append(_span[_index]); + ++_index; + ++_column; + } + else + { + throw new JmesPathParseException("Expected identifier", _line, _column); + } + break; + }; + break; + } + + case JmesPathState.SubExpression: + { + switch (_span[_index]) + { + case ' ':case '\t':case '\r':case '\n': + SkipWhiteSpace(); + break; + case '\"': + _stateStack.Pop(); + _stateStack.Push(JmesPathState.ValExpr); + _stateStack.Push(JmesPathState.QuotedString); + ++_index; + ++_column; + break; + case '{': + PushToken(new Token(TokenType.BeginMultiSelectHash)); + _stateStack.Pop(); + _stateStack.Push(JmesPathState.MultiSelectHash); + ++_index; + ++_column; + break; + case '*': + PushToken(new Token(new ObjectProjection())); + _stateStack.Pop(); + ++_index; + ++_column; + break; + case '[': + _stateStack.Pop(); + _stateStack.Push(JmesPathState.ExpectMultiSelectList); + ++_index; + ++_column; + break; + default: + if ((_span[_index] >= 'A' && _span[_index] <= 'Z') || (_span[_index] >= 'a' && _span[_index] <= 'z') || (_span[_index] == '_')) + { + _stateStack.Pop(); + _stateStack.Push(JmesPathState.IdentifierOrFunctionExpr); + _stateStack.Push(JmesPathState.UnquotedString); + buffer.Append(_span[_index]); + ++_index; + ++_column; + } + else + { + throw new JmesPathParseException("Expected identifier", _line, _column); + } + break; + }; + break; + } + case JmesPathState.KeyExpr: + PushToken(new Token(TokenType.Key, buffer.ToString())); + buffer.Clear(); + _stateStack.Pop(); + break; + case JmesPathState.ValExpr: + PushToken(new Token(new IdentifierSelector(buffer.ToString()))); + buffer.Clear(); + _stateStack.Pop(); + break; + case JmesPathState.ExpressionOrExpressionType: + switch (_span[_index]) + { + case ' ':case '\t':case '\r':case '\n': + SkipWhiteSpace(); + break; + case '&': + PushToken(new Token(TokenType.BeginExpressionType)); + _stateStack.Pop(); + _stateStack.Push(JmesPathState.ExpressionType); + _stateStack.Push(JmesPathState.RhsExpression); + _stateStack.Push(JmesPathState.LhsExpression); + ++_index; + ++_column; + break; + default: + _stateStack.Pop(); + _stateStack.Push(JmesPathState.Argument); + _stateStack.Push(JmesPathState.RhsExpression); + _stateStack.Push(JmesPathState.LhsExpression); + break; + } + break; + + case JmesPathState.IdentifierOrFunctionExpr: + switch(_span[_index]) + { + case '(': + { + var functionName = buffer.ToString(); + if (!BuiltInFunctions.Instance.TryGetFunction(functionName, out var func)) + { + throw new JmesPathParseException($"Function '{functionName}' not found", _line, _column); + } + buffer.Clear(); + PushToken(new Token(func)); + _stateStack.Pop(); + _stateStack.Push(JmesPathState.FunctionExpression); + _stateStack.Push(JmesPathState.ExpressionOrExpressionType); + ++_index; + ++_column; + break; + } + default: + { + PushToken(new Token(new IdentifierSelector(buffer.ToString()))); + buffer.Clear(); + _stateStack.Pop(); + break; + } + } + break; + + case JmesPathState.FunctionExpression: + switch (_span[_index]) + { + case ' ':case '\t':case '\r':case '\n': + SkipWhiteSpace(); + break; + case ',': + PushToken(new Token(TokenType.CurrentNode)); + _stateStack.Push(JmesPathState.ExpressionOrExpressionType); + ++_index; + ++_column; + break; + case ')': + { + PushToken(new Token(TokenType.EndArguments)); + _stateStack.Pop(); + ++_index; + ++_column; + break; + } + default: + break; + } + break; + + case JmesPathState.Argument: + PushToken(new Token(TokenType.Argument)); + _stateStack.Pop(); + break; + + case JmesPathState.ExpressionType: + PushToken(new Token(TokenType.EndExpressionType)); + PushToken(new Token(TokenType.Argument)); + _stateStack.Pop(); + break; + + case JmesPathState.QuotedString: + switch (_span[_index]) + { + case '\"': + _stateStack.Pop(); // quotedString + ++_index; + ++_column; + break; + case '\\': + _stateStack.Push(JmesPathState.QuotedStringEscapeChar); + ++_index; + ++_column; + break; + default: + buffer.Append(_span[_index]); + ++_index; + ++_column; + break; + }; + break; + + case JmesPathState.UnquotedString: + switch (_span[_index]) + { + case ' ':case '\t':case '\r':case '\n': + _stateStack.Pop(); // unquotedString + SkipWhiteSpace(); + break; + default: + if ((_span[_index] >= '0' && _span[_index] <= '9') || (_span[_index] >= 'A' && _span[_index] <= 'Z') || (_span[_index] >= 'a' && _span[_index] <= 'z') || (_span[_index] == '_')) + { + buffer.Append(_span[_index]); + ++_index; + ++_column; + } + else + { + _stateStack.Pop(); // unquotedString + } + break; + }; + break; + + case JmesPathState.RawStringEscapeChar: + switch (_span[_index]) + { + case '\'': + buffer.Append(_span[_index]); + _stateStack.Pop(); + ++_index; + ++_column; + break; + default: + buffer.Append('\\'); + buffer.Append(_span[_index]); + _stateStack.Pop(); + ++_index; + ++_column; + break; + } + break; + + case JmesPathState.QuotedStringEscapeChar: + switch (_span[_index]) + { + case '\"': + buffer.Append('\"'); + ++_index; + ++_column; + _stateStack.Pop(); + break; + case '\\': + buffer.Append('\\'); + ++_index; + ++_column; + _stateStack.Pop(); + break; + case '/': + buffer.Append('/'); + ++_index; + ++_column; + _stateStack.Pop(); + break; + case 'b': + buffer.Append('\b'); + ++_index; + ++_column; + _stateStack.Pop(); + break; + case 'f': + buffer.Append('\f'); + ++_index; + ++_column; + _stateStack.Pop(); + break; + case 'n': + buffer.Append('\n'); + ++_index; + ++_column; + _stateStack.Pop(); + break; + case 'r': + buffer.Append('\r'); + ++_index; + ++_column; + _stateStack.Pop(); + break; + case 't': + buffer.Append('\t'); + ++_index; + ++_column; + _stateStack.Pop(); + break; + case 'u': + ++_index; + ++_column; + _stateStack.Pop(); + _stateStack.Push(JmesPathState.EscapeU1); + break; + default: + throw new JmesPathParseException("Illegal escaped character", _line, _column); + } + break; + + case JmesPathState.EscapeU1: + cp = AppendToCodepoint(0, _span[_index]); + ++_index; + ++_column; + _stateStack.Pop(); + _stateStack.Push(JmesPathState.EscapeU2); + break; + case JmesPathState.EscapeU2: + cp = AppendToCodepoint(cp, _span[_index]); + ++_index; + ++_column; + _stateStack.Pop(); + _stateStack.Push(JmesPathState.EscapeU3); + break; + case JmesPathState.EscapeU3: + cp = AppendToCodepoint(cp, _span[_index]); + ++_index; + ++_column; + _stateStack.Pop(); + _stateStack.Push(JmesPathState.EscapeU4); + break; + case JmesPathState.EscapeU4: + cp = AppendToCodepoint(cp, _span[_index]); + if (char.IsHighSurrogate((char)cp)) + { + ++_index; + ++_column; + _stateStack.Pop(); + _stateStack.Push(JmesPathState.EscapeExpectSurrogatePair1); + } + else + { + buffer.Append(char.ConvertFromUtf32((int)cp)); + ++_index; + ++_column; + _stateStack.Pop(); + } + break; + case JmesPathState.EscapeExpectSurrogatePair1: + switch (_span[_index]) + { + case '\\': + ++_index; + ++_column; + _stateStack.Pop(); + _stateStack.Push(JmesPathState.EscapeExpectSurrogatePair2); + break; + default: + throw new JmesPathParseException("Invalid codepoint", _line, _column); + } + break; + case JmesPathState.EscapeExpectSurrogatePair2: + switch (_span[_index]) + { + case 'u': + ++_index; + ++_column; + _stateStack.Pop(); + _stateStack.Push(JmesPathState.EscapeU5); + break; + default: + throw new JmesPathParseException("Invalid codepoint", _line, _column); + } + break; + case JmesPathState.EscapeU5: + cp2 = AppendToCodepoint(0, _span[_index]); + ++_index; + ++_column; + _stateStack.Pop(); + _stateStack.Push(JmesPathState.EscapeU6); + break; + case JmesPathState.EscapeU6: + cp2 = AppendToCodepoint(cp2, _span[_index]); + ++_index; + ++_column; + _stateStack.Pop(); + _stateStack.Push(JmesPathState.EscapeU7); + break; + case JmesPathState.EscapeU7: + cp2 = AppendToCodepoint(cp2, _span[_index]); + ++_index; + ++_column; + _stateStack.Pop(); + _stateStack.Push(JmesPathState.EscapeU8); + break; + case JmesPathState.EscapeU8: + { + cp2 = AppendToCodepoint(cp2, _span[_index]); + var codepoint = 0x10000 + ((cp & 0x3FF) << 10) + (cp2 & 0x3FF); + buffer.Append(char.ConvertFromUtf32((int)codepoint)); + _stateStack.Pop(); + ++_index; + ++_column; + break; + } + + case JmesPathState.RawString: + switch (_span[_index]) + { + case '\'': + { + PushToken(new Token(new StringValue(buffer.ToString()))); + buffer.Clear(); + _stateStack.Pop(); // rawString + ++_index; + ++_column; + break; + } + case '\\': + _stateStack.Push(JmesPathState.RawStringEscapeChar); + ++_index; + ++_column; + break; + default: + buffer.Append(_span[_index]); + ++_index; + ++_column; + break; + }; + break; + + case JmesPathState.Literal: + switch (_span[_index]) + { + case '`': + { + try + { + using (var doc = JsonDocument.Parse(buffer.ToString())) + { + PushToken(new Token(new JsonElementValue(doc.RootElement.Clone()))); + buffer.Clear(); + _stateStack.Pop(); + ++_index; + } + } + catch (JsonException) + { + throw new JmesPathParseException("Invalid JSON literal", _line, _column); + } + break; + } + case '\\': + if (_index + 1 < _span.Length) + { + ++_index; + ++_column; + if (_span[_index] != '`') + { + buffer.Append('\\'); + } + buffer.Append(_span[_index]); + } + else + { + throw new JmesPathParseException("Unexpected end of input", _line, _column); + } + ++_index; + ++_column; + break; + default: + buffer.Append(_span[_index]); + ++_index; + ++_column; + break; + }; + break; + + case JmesPathState.Number: + switch(_span[_index]) + { + case '-': + buffer.Append(_span[_index]); + _stateStack.Pop(); + _stateStack.Push(JmesPathState.Digit); + ++_index; + ++_column; + break; + default: + _stateStack.Pop(); + _stateStack.Push(JmesPathState.Digit); + break; + } + break; + case JmesPathState.Digit: + switch(_span[_index]) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + buffer.Append(_span[_index]); + ++_index; + ++_column; + break; + default: + _stateStack.Pop(); // digit + break; + } + break; + + case JmesPathState.BracketSpecifier: + switch(_span[_index]) + { + case '*': + PushToken(new Token(new ListProjection())); + _stateStack.Pop(); + _stateStack.Push(JmesPathState.ExpectRightBracket); + ++_index; + ++_column; + break; + case ']': // [] + PushToken(new Token(new FlattenProjection())); + _stateStack.Pop(); // bracketSpecifier + ++_index; + ++_column; + break; + case '?': + PushToken(new Token(TokenType.BeginFilter)); + _stateStack.Pop(); + _stateStack.Push(JmesPathState.Filter); + _stateStack.Push(JmesPathState.RhsExpression); + _stateStack.Push(JmesPathState.LhsExpression); + ++_index; + ++_column; + break; + case ':': // sliceExpression + _stateStack.Pop(); + _stateStack.Push(JmesPathState.RhsSliceExpressionStop); + _stateStack.Push(JmesPathState.Number); + ++_index; + ++_column; + break; + // number + case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + _stateStack.Pop(); + _stateStack.Push(JmesPathState.IndexOrSliceExpression); + _stateStack.Push(JmesPathState.Number); + break; + default: + throw new JmesPathParseException("Expected index expression", _line, _column); + } + break; + case JmesPathState.BracketSpecifierOrMultiSelectList: + switch(_span[_index]) + { + case '*': + if (_index+1 >= _span.Length) + { + throw new JmesPathParseException("Unexpected end of input", _line, _column); + } + if (_span[_index+1] == ']') + { + _stateStack.Pop(); + _stateStack.Push(JmesPathState.BracketSpecifier); + } + else + { + PushToken(new Token(TokenType.BeginMultiSelectList)); + _stateStack.Pop(); + _stateStack.Push(JmesPathState.MultiSelectList); + _stateStack.Push(JmesPathState.LhsExpression); + } + break; + case ']': // [] + case '?': + case ':': // sliceExpression + case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + _stateStack.Pop(); + _stateStack.Push(JmesPathState.BracketSpecifier); + break; + default: + PushToken(new Token(TokenType.BeginMultiSelectList)); + _stateStack.Pop(); + _stateStack.Push(JmesPathState.MultiSelectList); + _stateStack.Push(JmesPathState.LhsExpression); + break; + } + break; + + case JmesPathState.ExpectMultiSelectList: + switch(_span[_index]) + { + case ']': + case '?': + case ':': + case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + throw new JmesPathParseException("Expected MultiSelectList", _line, _column); + case '*': + PushToken(new Token(new ListProjection())); + _stateStack.Pop(); + _stateStack.Push(JmesPathState.ExpectRightBracket); + ++_index; + ++_column; + break; + default: + PushToken(new Token(TokenType.BeginMultiSelectList)); + _stateStack.Pop(); + _stateStack.Push(JmesPathState.MultiSelectList); + _stateStack.Push(JmesPathState.LhsExpression); + break; + } + break; + + case JmesPathState.MultiSelectHash: + switch(_span[_index]) + { + case '*': + case ']': + case '?': + case ':': + case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + break; + default: + _stateStack.Pop(); + _stateStack.Push(JmesPathState.KeyValExpr); + break; + } + break; + + case JmesPathState.IndexOrSliceExpression: + switch(_span[_index]) + { + case ']': + { + if (buffer.Length == 0) + { + PushToken(new Token(new FlattenProjection())); + } + else + { + if (!int.TryParse(buffer.ToString(), out var n)) + { + throw new JmesPathParseException("Invalid number", _line, _column); + } + PushToken(new Token(new IndexSelector(n))); + buffer.Clear(); + } + _stateStack.Pop(); // bracketSpecifier + ++_index; + ++_column; + break; + } + case ':': + { + var s = buffer.ToString(); + if (!int.TryParse(s, out var n)) + { + n = s.StartsWith("-") ? int.MinValue : int.MaxValue; + } + sliceStart = n; + buffer.Clear(); + _stateStack.Pop(); + _stateStack.Push(JmesPathState.RhsSliceExpressionStop); + _stateStack.Push(JmesPathState.Number); + ++_index; + ++_column; + break; + } + default: + throw new JmesPathParseException("Expected right bracket", _line, _column); + } + break; + case JmesPathState.RhsSliceExpressionStop : + { + if (buffer.Length != 0) + { + var s = buffer.ToString(); + if (!int.TryParse(s, out var n)) + { + n = s.StartsWith("-") ? int.MinValue : int.MaxValue; + } + sliceStop = n; + buffer.Clear(); + } + switch(_span[_index]) + { + case ']': + PushToken(new Token(new SliceProjection(new Slice(sliceStart,sliceStop,sliceStep)))); + sliceStart = null; + sliceStop = null; + sliceStep = 1; + _stateStack.Pop(); // bracketSpecifier2 + ++_index; + ++_column; + break; + case ':': + _stateStack.Pop(); + _stateStack.Push(JmesPathState.RhsSliceExpressionStep); + _stateStack.Push(JmesPathState.Number); + ++_index; + ++_column; + break; + default: + throw new JmesPathParseException("Expected right bracket", _line, _column); + } + break; + } + case JmesPathState.RhsSliceExpressionStep: + { + if (buffer.Length != 0) + { + if (!int.TryParse(buffer.ToString(), out var n)) + { + throw new JmesPathParseException("Invalid slice stop", _line, _column); + } + buffer.Clear(); + if (n == 0) + { + throw new JmesPathParseException("Slice step cannot be zero", _line, _column); + } + sliceStep = n; + buffer.Clear(); + } + switch(_span[_index]) + { + case ']': + PushToken(new Token(new SliceProjection(new Slice(sliceStart,sliceStop,sliceStep)))); + sliceStart = null; + sliceStop = null; + sliceStep = 1; + buffer.Clear(); + _stateStack.Pop(); // rhsSliceExpressionStep + ++_index; + ++_column; + break; + default: + throw new JmesPathParseException("Expected right bracket", _line, _column); + } + break; + } + case JmesPathState.ExpectRightBracket: + { + switch(_span[_index]) + { + case ']': + _stateStack.Pop(); // expectRightBracket + ++_index; + ++_column; + break; + default: + throw new JmesPathParseException("Expected right bracket", _line, _column); + } + break; + } + case JmesPathState.ExpectRightParen: + switch (_span[_index]) + { + case ' ':case '\t':case '\r':case '\n': + SkipWhiteSpace(); + break; + case ')': + ++_index; + ++_column; + PushToken(new Token(TokenType.RightParen)); + _stateStack.Pop(); + _stateStack.Push(JmesPathState.RhsExpression); + break; + default: + throw new JmesPathParseException("Expected right parenthesis", _line, _column); + } + break; + case JmesPathState.KeyValExpr: + { + switch (_span[_index]) + { + case ' ':case '\t':case '\r':case '\n': + SkipWhiteSpace(); + break; + case '\"': + _stateStack.Pop(); + _stateStack.Push(JmesPathState.ExpectColon); + _stateStack.Push(JmesPathState.KeyExpr); + _stateStack.Push(JmesPathState.QuotedString); + ++_index; + ++_column; + break; + case '\'': + _stateStack.Pop(); + _stateStack.Push(JmesPathState.ExpectColon); + _stateStack.Push(JmesPathState.RawString); + ++_index; + ++_column; + break; + default: + if ((_span[_index] >= 'A' && _span[_index] <= 'Z') || (_span[_index] >= 'a' && _span[_index] <= 'z') || (_span[_index] == '_')) + { + _stateStack.Pop(); + _stateStack.Push(JmesPathState.ExpectColon); + _stateStack.Push(JmesPathState.KeyExpr); + _stateStack.Push(JmesPathState.UnquotedString); + buffer.Append(_span[_index]); + ++_index; + ++_column; + } + else + { + throw new JmesPathParseException("Expected key", _line, _column); + } + break; + }; + break; + } + case JmesPathState.CmpLtOrLte: + { + switch(_span[_index]) + { + case '=': + PushToken(new Token(LteOperator.Instance)); + PushToken(new Token(TokenType.CurrentNode)); + _stateStack.Pop(); + ++_index; + ++_column; + break; + default: + PushToken(new Token(LtOperator.Instance)); + PushToken(new Token(TokenType.CurrentNode)); + _stateStack.Pop(); + break; + } + break; + } + case JmesPathState.CmpGtOrGte: + { + switch(_span[_index]) + { + case '=': + PushToken(new Token(GteOperator.Instance)); + PushToken(new Token(TokenType.CurrentNode)); + _stateStack.Pop(); + ++_index; + ++_column; + break; + default: + PushToken(new Token(GtOperator.Instance)); + PushToken(new Token(TokenType.CurrentNode)); + _stateStack.Pop(); + break; + } + break; + } + case JmesPathState.CmpEq: + { + switch(_span[_index]) + { + case '=': + PushToken(new Token(EqOperator.Instance)); + PushToken(new Token(TokenType.CurrentNode)); + _stateStack.Pop(); + ++_index; + ++_column; + break; + default: + throw new JmesPathParseException("Expected comparator", _line, _column); + } + break; + } + case JmesPathState.CmpNe: + { + switch(_span[_index]) + { + case '=': + PushToken(new Token(NeOperator.Instance)); + PushToken(new Token(TokenType.CurrentNode)); + _stateStack.Pop(); + ++_index; + ++_column; + break; + default: + throw new JmesPathParseException("Expected comparator", _line, _column); + } + break; + } + case JmesPathState.ExpectDot: + { + switch(_span[_index]) + { + case ' ':case '\t':case '\r':case '\n': + SkipWhiteSpace(); + break; + case '.': + _stateStack.Pop(); // expect_dot + ++_index; + ++_column; + break; + default: + throw new JmesPathParseException("Expected dot", _line, _column); + } + break; + } + case JmesPathState.ExpectPipeOrOr: + { + switch(_span[_index]) + { + case '|': + PushToken(new Token(OrOperator.Instance)); + PushToken(new Token(TokenType.CurrentNode)); + _stateStack.Pop(); + ++_index; + ++_column; + break; + default: + PushToken(new Token(TokenType.Pipe)); + _stateStack.Pop(); + break; + } + break; + } + case JmesPathState.ExpectAnd: + { + switch(_span[_index]) + { + case '&': + PushToken(new Token(AndOperator.Instance)); + PushToken(new Token(TokenType.CurrentNode)); + _stateStack.Pop(); // expectAnd + ++_index; + ++_column; + break; + default: + throw new JmesPathParseException("Expected &&", _line, _column); + } + break; + } + case JmesPathState.MultiSelectList: + { + switch(_span[_index]) + { + case ' ':case '\t':case '\r':case '\n': + SkipWhiteSpace(); + break; + case ',': + PushToken(new Token(TokenType.Separator)); + _stateStack.Push(JmesPathState.LhsExpression); + ++_index; + ++_column; + break; + case '[': + _stateStack.Push(JmesPathState.LhsExpression); + break; + case '.': + _stateStack.Push(JmesPathState.SubExpression); + ++_index; + ++_column; + break; + case '|': + { + ++_index; + ++_column; + _stateStack.Push(JmesPathState.LhsExpression); + _stateStack.Push(JmesPathState.ExpectPipeOrOr); + break; + } + case ']': + { + PushToken(new Token(TokenType.EndMultiSelectList)); + _stateStack.Pop(); + + ++_index; + ++_column; + break; + } + default: + throw new JmesPathParseException("Expected right bracket", _line, _column); + } + break; + } + case JmesPathState.Filter: + { + switch(_span[_index]) + { + case ' ':case '\t':case '\r':case '\n': + SkipWhiteSpace(); + break; + case ']': + { + PushToken(new Token(TokenType.EndFilter)); + _stateStack.Pop(); + ++_index; + ++_column; + break; + } + default: + throw new JmesPathParseException("Expected right bracket", _line, _column); + } + break; + } + case JmesPathState.ExpectRightBrace: + { + switch(_span[_index]) + { + case ' ':case '\t':case '\r':case '\n': + SkipWhiteSpace(); + break; + case ',': + PushToken(new Token(TokenType.Separator)); + _stateStack.Pop(); + _stateStack.Push(JmesPathState.KeyValExpr); + ++_index; + ++_column; + break; + case '[': + case '{': + _stateStack.Push(JmesPathState.LhsExpression); + break; + case '.': + _stateStack.Push(JmesPathState.SubExpression); + ++_index; + ++_column; + break; + case '}': + { + _stateStack.Pop(); + PushToken(new Token(TokenType.EndMultiSelectHash)); + ++_index; + ++_column; + break; + } + default: + throw new JmesPathParseException("Expected right brace", _line, _column); + } + break; + } + case JmesPathState.ExpectColon: + { + switch(_span[_index]) + { + case ' ':case '\t':case '\r':case '\n': + SkipWhiteSpace(); + break; + case ':': + _stateStack.Pop(); + _stateStack.Push(JmesPathState.ExpectRightBrace); + _stateStack.Push(JmesPathState.LhsExpression); + ++_index; + ++_column; + break; + default: + throw new JmesPathParseException("Expected colon", _line, _column); + } + break; + } + } + } + + if (_stateStack.Count == 0) + { + throw new JmesPathParseException("Syntax error", _line, _column); + } + while (_stateStack.Count > 1) + { + switch (_stateStack.Peek()) + { + case JmesPathState.RhsExpression: + if (_stateStack.Count > 1) + { + _stateStack.Pop(); + } + else + { + throw new JmesPathParseException("Syntax error", _line, _column); + } + break; + case JmesPathState.ValExpr: + PushToken(new Token(new IdentifierSelector(buffer.ToString()))); + _stateStack.Pop(); + break; + case JmesPathState.IdentifierOrFunctionExpr: + PushToken(new Token(new IdentifierSelector(buffer.ToString()))); + _stateStack.Pop(); + break; + case JmesPathState.UnquotedString: + _stateStack.Pop(); + break; + default: + throw new JmesPathParseException("Syntax error", _line, _column); + } + } + + if (!(_stateStack.Count == 1 && _stateStack.Peek() == JmesPathState.RhsExpression)) + { + throw new JmesPathParseException("Unexpected end of input", _line, _column); + } + + _stateStack.Pop(); + + PushToken(new Token(TokenType.EndOfExpression)); + + var a = _outputStack.ToArray(); + + return new JsonTransformer(new Expression(a)); + } + + private void SkipWhiteSpace() + { + switch (_span[_index]) + { + case ' ':case '\t': + ++_index; + ++_column; + break; + case '\r': + if (_index+1 < _span.Length && _span[_index+1] == '\n') + ++_index; + ++_line; + _column = 1; + ++_index; + break; + case '\n': + ++_line; + _column = 1; + ++_index; + break; + default: + break; + } + } + + private void UnwindRightParen() + { + while (_operatorStack.Count > 1 && _operatorStack.Peek().Type != TokenType.LeftParen) + { + _outputStack.Push(_operatorStack.Pop()); + } + if (_operatorStack.Count == 0) + { + throw new JmesPathParseException("Unbalanced parentheses", _line, _column); + } + _operatorStack.Pop(); // TokenType.LeftParen + } + + private void PushToken(Token token) + { + switch (token.Type) + { + case TokenType.EndFilter: + { + UnwindRightParen(); + var tokens = new List(); + while (_outputStack.Count > 1 && _outputStack.Peek().Type != TokenType.BeginFilter) + { + tokens.Add(_outputStack.Pop()); + } + if (_outputStack.Count == 0) + { + throw new JmesPathParseException("Unbalanced parentheses", _line, _column); + } + if (tokens[tokens.Count-1].Type != TokenType.Literal) + { + tokens.Add(new Token(TokenType.CurrentNode)); + } + _outputStack.Pop(); + + if (_outputStack.Count != 0 && _outputStack.Peek().IsProjection && + (token.PrecedenceLevel > _outputStack.Peek().PrecedenceLevel || + (token.PrecedenceLevel == _outputStack.Peek().PrecedenceLevel && token.IsRightAssociative))) + { + _outputStack.Peek().GetExpression().AddExpression(new FilterExpression(new Expression(tokens.ToArray()))); + } + else + { + _outputStack.Push(new Token(new FilterExpression(new Expression(tokens.ToArray())))); + } + break; + } + case TokenType.EndMultiSelectList: + { + UnwindRightParen(); + var expressions = new List(); + while (_outputStack.Count > 0 && _outputStack.Peek().Type != TokenType.BeginMultiSelectList) + { + var tokens = new List(); + do + { + tokens.Add(_outputStack.Pop()); + } + while (_outputStack.Count > 0 && _outputStack.Peek().Type != TokenType.BeginMultiSelectList && _outputStack.Peek().Type != TokenType.Separator); + if (_outputStack.Peek().Type == TokenType.Separator) + { + _outputStack.Pop(); + } + if (tokens[tokens.Count-1].Type != TokenType.Literal) + { + tokens.Add(new Token(TokenType.CurrentNode)); + } + expressions.Add(new Expression(tokens.ToArray())); + } + if (_outputStack.Count == 0) + { + throw new JmesPathParseException("Unbalanced braces", _line, _column); + } + _outputStack.Pop(); // TokenType.BeginMultiSelectList + expressions.Reverse(); + + if (_outputStack.Count != 0 && _outputStack.Peek().IsProjection && + (token.PrecedenceLevel > _outputStack.Peek().PrecedenceLevel || + (token.PrecedenceLevel == _outputStack.Peek().PrecedenceLevel && token.IsRightAssociative))) + { + _outputStack.Peek().GetExpression().AddExpression(new MultiSelectList(expressions)); + } + else + { + _outputStack.Push(new Token(new MultiSelectList(expressions))); + } + break; + } + + case TokenType.EndMultiSelectHash: + { + UnwindRightParen(); + var keyExprPairs = new List(); + while (_outputStack.Count > 1 && _outputStack.Peek().Type != TokenType.BeginMultiSelectHash) + { + var tokens = new List(); + do + { + tokens.Add(_outputStack.Pop()); + } + while (_outputStack.Peek().Type != TokenType.Key); + if (_outputStack.Peek().Type != TokenType.Key) + { + throw new JmesPathParseException("Syntax error", _line, _column); + } + var key = _outputStack.Pop().GetKey(); + if (_outputStack.Peek().Type == TokenType.Separator) + { + _outputStack.Pop(); + } + if (tokens[tokens.Count-1].Type != TokenType.Literal) + { + tokens.Add(new Token(TokenType.CurrentNode)); + } + keyExprPairs.Add(new KeyExpressionPair(key, new Expression(tokens.ToArray()))); + } + if (_outputStack.Count == 0) + { + throw new JmesPathParseException("Syntax error", _line, _column); + } + keyExprPairs.Reverse(); + _outputStack.Pop(); // TokenType.BeginMultiSelectHash + + if (_outputStack.Count != 0 && _outputStack.Peek().IsProjection && + (token.PrecedenceLevel > _outputStack.Peek().PrecedenceLevel || + (token.PrecedenceLevel == _outputStack.Peek().PrecedenceLevel && token.IsRightAssociative))) + { + _outputStack.Peek().GetExpression().AddExpression(new MultiSelectHash(keyExprPairs)); + } + else + { + _outputStack.Push(new Token(new MultiSelectHash(keyExprPairs))); + } + break; + } + case TokenType.EndExpressionType: + { + var tokens = new List(); + while (_outputStack.Count > 1 && _outputStack.Peek().Type != TokenType.BeginExpressionType) + { + tokens.Add(_outputStack.Pop()); + } + if (_outputStack.Count == 0) + { + throw new JmesPathParseException("Unbalanced braces", _line, _column); + } + if (tokens[tokens.Count-1].Type != TokenType.Literal) + { + tokens.Add(new Token(TokenType.CurrentNode)); + } + _outputStack.Push(new Token(new FunctionExpression(new Expression(tokens.ToArray())))); + break; + } + case TokenType.Literal: + if (_outputStack.Count != 0 && _outputStack.Peek().Type == TokenType.CurrentNode) + { + _outputStack.Pop(); + _outputStack.Push(token); + } + else + { + _outputStack.Push(token); + } + break; + case TokenType.Expression: + if (_outputStack.Count != 0 && _outputStack.Peek().IsProjection && + (token.PrecedenceLevel > _outputStack.Peek().PrecedenceLevel || + (token.PrecedenceLevel == _outputStack.Peek().PrecedenceLevel && token.IsRightAssociative))) + { + _outputStack.Peek().GetExpression().AddExpression(token.GetExpression()); + } + else + { + _outputStack.Push(token); + } + break; + case TokenType.RightParen: + { + UnwindRightParen(); + break; + } + case TokenType.EndArguments: + { + UnwindRightParen(); + var argCount = 0; + var tokens = new List(); + Debug.Assert(_operatorStack.Count > 0 && _operatorStack.Peek().Type == TokenType.Function); + tokens.Add(_operatorStack.Pop()); // Function + while (_outputStack.Count > 1 && _outputStack.Peek().Type != TokenType.BeginArguments) + { + if (_outputStack.Peek().Type == TokenType.Argument) + { + ++argCount; + } + tokens.Add(_outputStack.Pop()); + } + if (_outputStack.Count == 0) + { + throw new JmesPathParseException("Expected parentheses", _line, _column); + } + _outputStack.Pop(); // TokenType.BeginArguments + if (tokens[tokens.Count-1].Type != TokenType.Literal) + { + tokens.Add(new Token(TokenType.CurrentNode)); + } + if (tokens[0].GetFunction().Arity != null && argCount != tokens[0].GetFunction().Arity) + { + throw new JmesPathParseException($"Invalid arity (The number of arguments or operands a function or operation takes) calling function '{tokens[0].GetFunction()}', expected {tokens[0].GetFunction().Arity}, found {argCount}", _line, _column); + } + + if (_outputStack.Count != 0 && _outputStack.Peek().IsProjection && + (token.PrecedenceLevel > _outputStack.Peek().PrecedenceLevel || + (token.PrecedenceLevel == _outputStack.Peek().PrecedenceLevel && token.IsRightAssociative))) + { + _outputStack.Peek().GetExpression().AddExpression(new FunctionExpression(new Expression(tokens.ToArray()))); + } + else + { + _outputStack.Push(new Token(new FunctionExpression(new Expression(tokens.ToArray())))); + } + break; + } + case TokenType.EndOfExpression: + { + while (_operatorStack.Count != 0) + { + _outputStack.Push(_operatorStack.Pop()); + } + break; + } + case TokenType.UnaryOperator: + case TokenType.BinaryOperator: + { + if (_operatorStack.Count == 0 || _operatorStack.Peek().Type == TokenType.LeftParen) + { + _operatorStack.Push(token); + } + else if (token.PrecedenceLevel > _operatorStack.Peek().PrecedenceLevel + || (token.PrecedenceLevel == _operatorStack.Peek().PrecedenceLevel && token.IsRightAssociative)) + { + _operatorStack.Push(token); + } + else + { + while (_operatorStack.Count > 0 && _operatorStack.Peek().IsOperator + && (_operatorStack.Peek().PrecedenceLevel > token.PrecedenceLevel + || (token.PrecedenceLevel == _operatorStack.Peek().PrecedenceLevel && token.IsRightAssociative))) + { + _outputStack.Push(_operatorStack.Pop()); + } + + _operatorStack.Push(token); + } + break; + } + case TokenType.Separator: + { + UnwindRightParen(); + _outputStack.Push(token); + _operatorStack.Push(new Token(TokenType.LeftParen)); + break; + } + case TokenType.BeginFilter: + _outputStack.Push(token); + _operatorStack.Push(new Token(TokenType.LeftParen)); + break; + case TokenType.BeginMultiSelectList: + _outputStack.Push(token); + _operatorStack.Push(new Token(TokenType.LeftParen)); + break; + case TokenType.BeginMultiSelectHash: + _outputStack.Push(token); + _operatorStack.Push(new Token(TokenType.LeftParen)); + break; + case TokenType.Function: + _outputStack.Push(new Token(TokenType.BeginArguments)); + _operatorStack.Push(token); + _operatorStack.Push(new Token(TokenType.LeftParen)); + break; + case TokenType.CurrentNode: + _outputStack.Push(token); + break; + case TokenType.Key: + case TokenType.Pipe: + case TokenType.Argument: + case TokenType.BeginExpressionType: + _outputStack.Push(token); + break; + case TokenType.LeftParen: + _operatorStack.Push(token); + break; + default: + break; + } + } + + + private uint AppendToCodepoint(uint cp, uint c) + { + cp *= 16; + if (c >= '0' && c <= '9') + { + cp += c - '0'; + } + else if (c >= 'a' && c <= 'f') + { + cp += c - 'a' + 10; + } + else if (c >= 'A' && c <= 'F') + { + cp += c - 'A' + 10; + } + else + { + throw new JmesPathParseException("Invalid codepoint", _line, _column); + } + return cp; + } + } +} diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs new file mode 100644 index 00000000..4fc29685 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs @@ -0,0 +1,145 @@ +using System; +using System.Text.Json; + +namespace AWS.Lambda.Powertools.JMESPath +{ + internal sealed class DynamicResources + { + } + /// + /// Provides functionality for applying a JMESPath expression to transform a JSON document into + /// another JSON document + /// + /// + /// The following example shows how to apply a JMESPath expression to transform a JSON document into + /// another JSON document. + /// + /// using System; + /// using System.Text.Json; + /// + /// public class Example + /// { + /// public static void Main() + /// { + /// string jsonString = @" + /// { + /// ""people"": [ + /// { + /// ""age"": 20, + /// ""other"": ""foo"", + /// ""name"": ""Bob"" + /// }, + /// { + /// ""age"": 25, + /// ""other"": ""bar"", + /// ""name"": ""Fred"" + /// }, + /// { + /// ""age"": 30, + /// ""other"": ""baz"", + /// ""name"": ""George"" + /// } + /// ] + /// } + /// "; + /// + /// using JsonDocument doc = JsonDocument.Parse(jsonString); + /// + /// var transformer = JsonTransformer.Parse("people[?age > `20`].[name, age]"); + /// + /// using JsonDocument result = transformer.Transform(doc.RootElement); + /// + /// var serializerOptions = new JsonSerializerOptions() {WriteIndented = true}; + /// Console.WriteLine(JsonSerializer.Serialize(result.RootElement, serializerOptions)); + /// } + /// + /// Output: + /// + /// + /// [ + /// [ + /// "Fred", + /// 25 + /// ], + /// [ + /// "George", + /// 30 + /// ] + /// ] + /// + /// + + public sealed class JsonTransformer + { + /// + /// Parses a JMESPath string into a , for "parse once, use many times". + /// A instance is thread safe and has no mutable state. + /// + /// A JMESPath string. + /// A . + /// + /// The parameter is not a valid JMESPath expression. + /// + /// + /// The is . + /// + public static JsonTransformer Parse(string jmesPath) + { + if (jmesPath == null) + { + throw new ArgumentNullException(nameof(jmesPath)); + } + var compiler = new JmesPathParser(jmesPath); + return compiler.Parse(); + } + + private Expression _expr; + + internal JsonTransformer(Expression expr) + { + _expr = expr; + } + + /// + /// Applies a JMESPath expression to a JSON document to transform it + /// into another Json document. + /// + /// + /// It is the users responsibilty to properly Dispose the returned value + /// + /// The provided JSON document. + /// The transformed JSON document. If a type error is detected in a function call, + /// a JSON null value is returned. + + public JsonDocument Transform(JsonElement doc) + { + var resources = new DynamicResources(); + _expr.TryEvaluate(resources, new JsonElementValue(doc), out var temp); + return JsonDocument.Parse(temp.ToString()); + } + + /// + /// Applies a JMESPath expression to a JSON document to transform it + /// into another Json document. + /// This method parses and applies the expression in one operation. + /// + /// + /// It is the users responsibilty to properly Dispose the returned value + /// + /// The provided JSON document. + /// A JMESPath string. + /// The transformed JSON document. + /// + /// The parameter is not a valid JMESPath expression. + /// + /// + /// The is . + /// + + public static JsonDocument Transform(JsonElement doc, string jmesPath) + { + var searcher = Parse(jmesPath); + return searcher.Transform(doc); + } + } +} diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operator.cs new file mode 100644 index 00000000..c0816e53 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operator.cs @@ -0,0 +1,74 @@ +namespace AWS.Lambda.Powertools.JMESPath +{ + internal enum Operator + { + Default, // Identifier, CurrentNode, Index, MultiSelectList, MultiSelectHash, FunctionExpression + Projection, + FlattenProjection, // FlattenProjection + Or, + And, + Eq, + Ne, + Lt, + Lte, + Gt, + Gte, + Not + } + + internal static class OperatorTable + { + static internal int PrecedenceLevel(Operator oper) + { + switch (oper) + { + case Operator.Projection: + return 1; + case Operator.FlattenProjection: + return 1; + case Operator.Or: + return 2; + case Operator.And: + return 3; + case Operator.Eq: + case Operator.Ne: + return 4; + case Operator.Lt: + case Operator.Lte: + case Operator.Gt: + case Operator.Gte: + return 5; + case Operator.Not: + return 6; + default: + return 6; + } + } + + static internal bool IsRightAssociative(Operator oper) + { + switch (oper) + { + case Operator.Not: + return true; + case Operator.Projection: + return true; + case Operator.FlattenProjection: + return false; + case Operator.Or: + case Operator.And: + case Operator.Eq: + case Operator.Ne: + case Operator.Lt: + case Operator.Lte: + case Operator.Gt: + case Operator.Gte: + return false; + default: + return false; + } + } + } + +} + diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/README.md b/libraries/src/AWS.Lambda.Powertools.JMESPath/README.md new file mode 100644 index 00000000..bb9ca308 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/README.md @@ -0,0 +1 @@ +JMESPath \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Slice.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Slice.cs new file mode 100644 index 00000000..0d6fb21b --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Slice.cs @@ -0,0 +1,53 @@ +using System; + +namespace AWS.Lambda.Powertools.JMESPath +{ + internal readonly struct Slice + { + private readonly int? _start; + private readonly int? _stop; + + public int Step {get;} + + public Slice(int? start, int? stop, int step) + { + _start = start; + _stop = stop; + Step = step; + } + + public int GetStart(int size) + { + if (_start.HasValue) + { + var len = _start.Value >= 0 ? _start.Value : size + _start.Value; + return len <= size ? len : size; + } + else + { + if (Step >= 0) + { + return 0; + } + else + { + return size; + } + } + } + + public int GetStop(int size) + { + if (_stop.HasValue) + { + var len = _stop.Value >= 0 ? _stop.Value : size + _stop.Value; + return len <= size ? len : size; + } + else + { + return Step >= 0 ? size : -1; + } + } + }; + +} diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs new file mode 100644 index 00000000..ab5d1b76 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs @@ -0,0 +1,245 @@ +using System; +using System.Diagnostics; + +namespace AWS.Lambda.Powertools.JMESPath +{ + internal enum TokenType + { + CurrentNode, + LeftParen, + RightParen, + BeginMultiSelectHash, + EndMultiSelectHash, + BeginMultiSelectList, + EndMultiSelectList, + BeginFilter, + EndFilter, + Pipe, + Separator, + Key, + Literal, + Expression, + BinaryOperator, + UnaryOperator, + Function, + BeginArguments, + EndArguments, + Argument, + BeginExpressionType, + EndExpressionType, + EndOfExpression + } + + internal readonly struct Token : IEquatable + { + private readonly object? _expr; + + internal Token(TokenType type) + { + Type = type; + _expr = null; + } + + internal Token(TokenType type, string s) + { + Type = type; + _expr = s; + } + + internal Token(IExpression expr) + { + Type = TokenType.Expression; + _expr = expr; + } + + internal Token(IUnaryOperator expr) + { + Type = TokenType.UnaryOperator; + _expr = expr; + } + + internal Token(IBinaryOperator expr) + { + Type = TokenType.BinaryOperator; + _expr = expr; + } + + internal Token(IFunction expr) + { + Type = TokenType.Function; + _expr = expr; + } + + internal Token(IValue expr) + { + Type = TokenType.Literal; + _expr = expr; + } + + internal TokenType Type{get;} + + internal bool IsOperator + { + get + { + switch(Type) + { + case TokenType.UnaryOperator: + return true; + case TokenType.BinaryOperator: + return true; + default: + return false; + } + } + } + + internal bool IsProjection + { + get + { + switch(Type) + { + case TokenType.Expression: + return GetExpression().IsProjection; + default: + return false; + } + } + } + + internal bool IsRightAssociative + { + get + { + switch(Type) + { + case TokenType.Expression: + return GetExpression().IsRightAssociative; + case TokenType.UnaryOperator: + return GetUnaryOperator().IsRightAssociative; + case TokenType.BinaryOperator: + return GetBinaryOperator().IsRightAssociative; + default: + return false; + } + } + } + + internal int PrecedenceLevel + { + get + { + switch(Type) + { + case TokenType.Expression: + return GetExpression().PrecedenceLevel; + case TokenType.UnaryOperator: + return GetUnaryOperator().PrecedenceLevel; + case TokenType.BinaryOperator: + return GetBinaryOperator().PrecedenceLevel; + default: + return 100; + } + } + } + + internal string GetKey() + { + Debug.Assert(Type == TokenType.Key); + return _expr as string ?? throw new InvalidOperationException("Key cannot be null"); + } + + internal IUnaryOperator GetUnaryOperator() + { + Debug.Assert(Type == TokenType.UnaryOperator); + return _expr as IUnaryOperator ?? throw new InvalidOperationException("Unary operator cannot be null"); + } + + internal IBinaryOperator GetBinaryOperator() + { + Debug.Assert(Type == TokenType.BinaryOperator); + return _expr as IBinaryOperator ?? throw new InvalidOperationException("Binary operator cannot be null"); + } + + internal IValue GetValue() + { + Debug.Assert(Type == TokenType.Literal); + return _expr as IValue ?? throw new InvalidOperationException("Value cannot be null"); + } + + internal IFunction GetFunction() + { + Debug.Assert(Type == TokenType.Function); + return _expr as IFunction ?? throw new InvalidOperationException("Function cannot be null"); + } + + internal IExpression GetExpression() + { + Debug.Assert(Type == TokenType.Expression); + return _expr as IExpression ?? throw new InvalidOperationException("Expression cannot be null"); + } + public bool Equals(Token other) + { + if (Type == other.Type) + return true; + else + return false; + } + + public override string ToString() + { + switch(Type) + { + case TokenType.BeginArguments: + return "BeginArguments"; + case TokenType.CurrentNode: + return "CurrentNode"; + case TokenType.LeftParen: + return "LeftParen"; + case TokenType.RightParen: + return "RightParen"; + case TokenType.BeginMultiSelectHash: + return "BeginMultiSelectHash"; + case TokenType.EndMultiSelectHash: + return "EndMultiSelectHash"; + case TokenType.BeginMultiSelectList: + return "BeginMultiSelectList"; + case TokenType.EndMultiSelectList: + return "EndMultiSelectList"; + case TokenType.BeginFilter: + return "BeginFilter"; + case TokenType.EndFilter: + return "EndFilter"; + case TokenType.Pipe: + return $"Pipe"; + case TokenType.Separator: + return "Separator"; + case TokenType.Key: + return $"Key {_expr}"; + case TokenType.Literal: + return $"Literal {_expr}"; + case TokenType.Expression: + return "Expression"; + case TokenType.BinaryOperator: + return $"BinaryOperator {_expr}"; + case TokenType.UnaryOperator: + return $"UnaryOperator {_expr}"; + case TokenType.Function: + return $"Function {_expr}"; + case TokenType.EndArguments: + return "EndArguments"; + case TokenType.Argument: + return "Argument"; + case TokenType.BeginExpressionType: + return "BeginExpressionType"; + case TokenType.EndExpressionType: + return "EndExpressionType"; + case TokenType.EndOfExpression: + return "EndOfExpression"; + default: + return "Other"; + } + } + } +} diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs new file mode 100644 index 00000000..c39df7eb --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs @@ -0,0 +1,75 @@ +using System.Text.RegularExpressions; + +namespace AWS.Lambda.Powertools.JMESPath +{ + internal interface IUnaryOperator + { + int PrecedenceLevel {get;} + bool IsRightAssociative {get;} + bool TryEvaluate(IValue elem, out IValue result); + }; + + internal abstract class UnaryOperator : IUnaryOperator + { + internal UnaryOperator(Operator oper) + { + PrecedenceLevel = OperatorTable.PrecedenceLevel(oper); + IsRightAssociative = OperatorTable.IsRightAssociative(oper); + } + + public int PrecedenceLevel {get;} + + public bool IsRightAssociative {get;} + + public abstract bool TryEvaluate(IValue elem, out IValue result); + }; + + internal sealed class NotOperator : UnaryOperator + { + internal static NotOperator Instance { get; } = new(); + + internal NotOperator() + : base(Operator.Not) + {} + + public override bool TryEvaluate(IValue val, out IValue result) + { + result = Expression.IsFalse(val) ? JsonConstants.True : JsonConstants.False; + return true; + } + + public override string ToString() + { + return "Not"; + } + }; + + internal sealed class RegexOperator : UnaryOperator + { + private Regex _regex; + + internal RegexOperator(Regex regex) + : base(Operator.Not) + { + _regex = regex; + } + + public override bool TryEvaluate(IValue val, out IValue result) + { + if (!(val.Type == JmesPathType.String)) + { + result = JsonConstants.Null; + return false; // type error + } + result = _regex.IsMatch(val.GetString()) ? JsonConstants.True : JsonConstants.False; + return true; + } + + public override string ToString() + { + return "Regex"; + } + }; + +} + diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonDocumentBuilder.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonDocumentBuilder.cs new file mode 100644 index 00000000..cabfd2be --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonDocumentBuilder.cs @@ -0,0 +1,305 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json; + +namespace AWS.Lambda.Powertools.JMESPath.Utilities +{ + internal class JsonDocumentBuilder + { + internal static JsonDocumentBuilder Default { get; } = new(); + + internal JsonValueKind ValueKind {get;} + + private object? _item; + + private IList GetList() + { + return _item as IList ?? throw new InvalidOperationException("Item is null"); + } + + private IDictionary GetDictionary() + { + return _item as IDictionary ?? throw new InvalidOperationException("Item is null"); + } + internal JsonDocumentBuilder() + : this(JsonValueKind.Null) + { + } + + internal JsonDocumentBuilder(JsonValueKind valueKind) + { + ValueKind = valueKind; + switch (valueKind) + { + case JsonValueKind.Array: + _item = new List(); + break; + case JsonValueKind.Object: + _item = new Dictionary(); + break; + case JsonValueKind.True: + _item = true; + break; + case JsonValueKind.False: + _item = false; + break; + case JsonValueKind.Null: + _item = null; + break; + case JsonValueKind.String: + _item = ""; + break; + case JsonValueKind.Number: + _item = 0; + break; + default: + _item = null; + break; + } + } + + internal JsonDocumentBuilder(IList list) + { + ValueKind = JsonValueKind.Array; + _item = list; + } + + internal JsonDocumentBuilder(IDictionary dict) + { + ValueKind = JsonValueKind.Object; + _item = dict; + } + + internal JsonDocumentBuilder(string str) + { + ValueKind = JsonValueKind.String; + _item = str; + } + + internal JsonDocumentBuilder(JsonElement element) + { + ValueKind = element.ValueKind; + switch (element.ValueKind) + { + case JsonValueKind.Array: + var list = new List(); + foreach (var item in element.EnumerateArray()) + { + list.Add(new JsonDocumentBuilder(item)); + } + _item = list; + break; + case JsonValueKind.Object: + var dict = new Dictionary(); + foreach (var property in element.EnumerateObject()) + { + dict.Add(property.Name, new JsonDocumentBuilder(property.Value)); + } + _item = dict; + break; + default: + _item = element; + break; + } + } + + internal IEnumerable EnumerateArray() + { + if (ValueKind != JsonValueKind.Array) + { + throw new InvalidOperationException("This value's ValueKind is not Array."); + } + return GetList(); + } + + internal IEnumerable> EnumerateObject() + { + if (ValueKind != JsonValueKind.Object) + { + throw new InvalidOperationException("This value's ValueKind is not Object."); + } + return GetDictionary(); + } + + internal JsonDocumentBuilder this[int i] + { + get { + if (ValueKind != JsonValueKind.Array) + { + throw new InvalidOperationException("This value's ValueKind is not Array."); + } + return GetList() [i]; + } + set { + if (ValueKind != JsonValueKind.Array) + { + throw new InvalidOperationException("This value's ValueKind is not Array."); + } + GetList()[i] = value; + } + } + + internal void AddArrayItem(JsonDocumentBuilder value) + { + if (ValueKind != JsonValueKind.Array) + { + throw new InvalidOperationException("This value's ValueKind is not Array."); + } + GetList().Add(value); + } + + internal void InsertArrayItem(int index, JsonDocumentBuilder value) + { + if (ValueKind != JsonValueKind.Array) + { + throw new InvalidOperationException("This value's ValueKind is not Array."); + } + GetList().Insert(index, value); + } + + internal void RemoveArrayItemAt(int index) + { + if (ValueKind != JsonValueKind.Array) + { + throw new InvalidOperationException("This value's ValueKind is not Array."); + } + GetList().RemoveAt(index); + } + + internal void AddProperty(string name, JsonDocumentBuilder value) + { + if (ValueKind != JsonValueKind.Object) + { + throw new InvalidOperationException("This value's ValueKind is not Object."); + } + GetDictionary().Add(name, value); + } + + internal bool TryAddProperty(string name, JsonDocumentBuilder value) + { + if (ValueKind != JsonValueKind.Object) + { + throw new InvalidOperationException("This value's ValueKind is not Object."); + } + return GetDictionary().TryAdd(name, value); + } + + internal bool ContainsPropertyName(string name) + { + if (ValueKind != JsonValueKind.Object) + { + throw new InvalidOperationException("This value's ValueKind is not Object."); + } + return GetDictionary().ContainsKey(name); + } + + internal void RemoveProperty(string name) + { + if (ValueKind != JsonValueKind.Object) + { + throw new InvalidOperationException("This value's ValueKind is not Object."); + } + GetDictionary().Remove(name); + } + + internal int GetArrayLength() + { + if (ValueKind != JsonValueKind.Array) + { + throw new InvalidOperationException("This value's ValueKind is not Array."); + } + return GetList().Count(); + } + + internal int GetObjectLength() + { + if (ValueKind != JsonValueKind.Object) + { + throw new InvalidOperationException("This value's ValueKind is not Object."); + } + return GetDictionary().Count(); + } + + internal bool TryGetProperty(string name, out JsonDocumentBuilder value) + { + if (ValueKind != JsonValueKind.Object) + { + throw new InvalidOperationException("This value's ValueKind is not Object."); + } + if (ValueKind != JsonValueKind.Object) + { + value = Default; + return false; + } + return GetDictionary().TryGetValue(name, out value); + } + + public override string ToString() + { + var buffer = new StringBuilder(); + ToString(buffer); + return buffer.ToString(); + } + + private void ToString(StringBuilder buffer) + { + switch (ValueKind) + { + case JsonValueKind.Array: + { + buffer.Append("["); + var first = true; + foreach (var item in EnumerateArray()) + { + if (!first) + { + buffer.Append(","); + } + else + { + first = false; + } + item.ToString(buffer); + } + buffer.Append("]"); + break; + } + case JsonValueKind.Object: + { + buffer.Append("{"); + var first = true; + foreach (var property in EnumerateObject()) + { + if (!first) + { + buffer.Append(","); + } + else + { + first = false; + } + buffer.Append(JsonSerializer.Serialize(property.Key)); + buffer.Append(":"); + property.Value.ToString(buffer); + } + buffer.Append("}"); + break; + } + default: + { + buffer.Append(JsonSerializer.Serialize(_item, (JsonSerializerOptions)null)); + break; + } + } + } + + internal JsonDocument ToJsonDocument() + { + var json = ToString(); + return JsonDocument.Parse(json); + } + } + +} // namespace JsonCons.Utilities diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementComparer.cs new file mode 100644 index 00000000..b40ab7c6 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementComparer.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; + +namespace AWS.Lambda.Powertools.JMESPath.Utilities +{ + /// + /// Compares two instances. + /// + + public sealed class JsonElementComparer : IComparer, System.Collections.IComparer + { + /// Gets a singleton instance of . This property is read-only. + public static JsonElementComparer Instance { get; } = new(); + + /// + /// Constructs a + /// + public JsonElementComparer() {} + + /// + /// Compares two instances. + /// + /// If the two instances have different data types, they are + /// compared according to their ValueKind property, which gives this ordering: + /// + /// Undefined + /// Object + /// Array + /// String + /// Number + /// True + /// False + /// Null + /// + /// + /// If both instances are null, true, or false, they are equal. + /// + /// If both are strings, they are compared with the String.CompareTo method. + /// + /// If both are numbers, and both can be represented by a , + /// they are compared with the Decimal.CompareTo method, otherwise they are + /// compared as doubles. + /// + /// If both are objects, they are compared accoring to the following rules: + /// + ///
    + ///
  • Order each object's properties by name and compare sequentially. + /// The properties are compared first by name with the String.CompareTo method, then by value with
  • + ///
  • The first mismatching property defines which instance is less or greater than the other.
  • + ///
  • If the two sequences have no mismatching properties until one of them ends, and the other is longer, the shorter sequence is less than the other.
  • + ///
  • If the two sequences have no mismatching properties and have the same length, they are equal.
  • + ///
+ /// + /// If both are arrays, they are compared element wise with . + /// The first mismatching element defines which instance is less or greater than the other. + /// If the two arrays have no mismatching elements until one of them ends, and the other is longer, the shorter array is less than the other. + /// If the two arrays have no mismatching elements and have the same length, they are equal. + /// + ///
+ /// The first object of type cref="JsonElement"/> to compare. + /// The second object of type cref="JsonElement"/> to compare. + /// + /// + /// Unable to compare numbers as either or double (shouldn't happen.) + /// + public int Compare(JsonElement lhs, JsonElement rhs) + { + if (lhs.ValueKind != rhs.ValueKind) + return (int)lhs.ValueKind - (int)rhs.ValueKind; + + switch (lhs.ValueKind) + { + case JsonValueKind.Null: + case JsonValueKind.True: + case JsonValueKind.False: + case JsonValueKind.Undefined: + return 0; + + case JsonValueKind.Number: + { + if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) + { + return dec1.CompareTo(dec2); + } + else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) + { + return val1.CompareTo(val2); + } + else + { + throw new InvalidOperationException("Unable to compare numbers"); + } + } + + case JsonValueKind.String: + { + return string.Compare(lhs.GetString(), rhs.GetString()); + } + + case JsonValueKind.Array: + { + var enumerator1 = lhs.EnumerateArray(); + var enumerator2 = rhs.EnumerateArray(); + var result1 = enumerator1.MoveNext(); + var result2 = enumerator2.MoveNext(); + while (result1 && result2) + { + var diff = Compare(enumerator1.Current, enumerator2.Current); + if (diff != 0) + { + return diff; + } + result1 = enumerator1.MoveNext(); + result2 = enumerator2.MoveNext(); + } + return result1 ? 1 : result2 ? -1 : 0; + } + + case JsonValueKind.Object: + { + // OrderBy performs a stable sort (Note that supports duplicate property names) + var enumerator1 = lhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); + var enumerator2 = rhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); + + var result1 = enumerator1.MoveNext(); + var result2 = enumerator2.MoveNext(); + while (result1 && result2) + { + if (enumerator1.Current.Name != enumerator2.Current.Name) + { + return enumerator1.Current.Name.CompareTo(enumerator2.Current.Name); + } + var diff = Compare(enumerator1.Current.Value, enumerator2.Current.Value); + if (diff != 0) + { + return diff; + } + result1 = enumerator1.MoveNext(); + result2 = enumerator2.MoveNext(); + } + + return result1 ? 1 : result2 ? -1 : 0; + } + + default: + throw new InvalidOperationException(string.Format("Unknown JsonValueKind {0}", lhs.ValueKind)); + } + } + + int System.Collections.IComparer.Compare(object x, object y) + { + return Compare((JsonElement)x, (JsonElement)y); + } + } + + +} // namespace JsonCons.JsonPath diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementEqualityComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementEqualityComparer.cs new file mode 100644 index 00000000..f8e7bfd7 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementEqualityComparer.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; + +namespace AWS.Lambda.Powertools.JMESPath.Utilities +{ + /// + /// Compares two instances for equality by using value-based comparison. + /// + + public sealed class JsonElementEqualityComparer : IEqualityComparer + { + /// Gets a singleton instance of . This property is read-only. + public static JsonElementEqualityComparer Instance { get; } = new(); + + private int MaxHashDepth { get; } = 64; + + private JsonElementEqualityComparer() {} + + /// + /// Determines whether the provided objects are equal. + /// + /// If the two instances have different data types, they are different. + /// + /// If both instances are null, true, or false, they are equal. + /// + /// If both are strings, they are compared with the String.Equals method. + /// + /// If both are numbers, and both can be represented by a , + /// they are compared with the Decimal.Equals method, otherwise they are + /// compared as doubles. + /// + /// If both are objects, they are compared accoring to the following rules: + /// + ///
    + ///
  • If the two objects have a different number of properties, they are different.
  • + ///
  • Otherwise, order each object's properties by name and compare sequentially. + /// The properties are compared first by name with the String.Equals method, then by value with
  • + ///
  • A mismatching property means the two instance are different.
  • + ///
+ /// + /// If both are arrays, and both have the same length and compare equal element wise with , + /// they are equal, otherwise they are different. + ///
+ /// The first object of type cref="JsonElement"/> to compare. + /// The second object of type cref="JsonElement"/> to compare. + /// + /// + /// Unable to compare numbers as either or double (shouldn't happen.) + /// + public bool Equals(JsonElement lhs, JsonElement rhs) + { + if (lhs.ValueKind != rhs.ValueKind) + return false; + + switch (lhs.ValueKind) + { + case JsonValueKind.Null: + case JsonValueKind.True: + case JsonValueKind.False: + case JsonValueKind.Undefined: + return true; + + case JsonValueKind.Number: + { + if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) + { + return dec1.Equals(dec2); + } + else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) + { + return val1 == val2; + } + else + { + return false; + } + } + + case JsonValueKind.String: + { + var str = lhs.GetString() ?? throw new InvalidOperationException("string cannot be null"); + return str.Equals(rhs.GetString()); + } + + case JsonValueKind.Array: + return lhs.EnumerateArray().SequenceEqual(rhs.EnumerateArray(), this); + + case JsonValueKind.Object: + { + // OrderBy performs a stable sort (Note that supports duplicate property names) + var baseEnumerator1 = lhs.EnumerateObject(); + var baseEnumerator2 = rhs.EnumerateObject(); + if (baseEnumerator1.Count() != baseEnumerator2.Count()) + { + return false; + } + + var enumerator1 = baseEnumerator1.OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); + var enumerator2 = baseEnumerator2.OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); + + var result1 = enumerator1.MoveNext(); + var result2 = enumerator2.MoveNext(); + while (result1 && result2) + { + if (enumerator1.Current.Name != enumerator2.Current.Name) + { + return false; + } + if (!(Equals(enumerator1.Current.Value,enumerator2.Current.Value))) + { + return false; + } + result1 = enumerator1.MoveNext(); + result2 = enumerator2.MoveNext(); + } + + return result1 == false && result2 == false; + } + + default: + throw new InvalidOperationException(string.Format("Unknown JsonValueKind {0}", lhs.ValueKind)); + } + } + + /// + /// Returns a hash code for the specified value. + /// + /// + /// An Int32 value representing the hash code of the value. + public int GetHashCode(JsonElement value) + { + return ComputeHashCode(value, 0); + } + + private int ComputeHashCode(JsonElement element, int depth) + { + var hashCode = element.ValueKind.GetHashCode(); + + switch (element.ValueKind) + { + case JsonValueKind.Null: + case JsonValueKind.True: + case JsonValueKind.False: + case JsonValueKind.Undefined: + break; + + case JsonValueKind.Number: + hashCode += 17*element.GetDouble().GetHashCode(); + break; + + case JsonValueKind.String: + { + var str = element.GetString() ?? throw new InvalidOperationException("string cannot be null"); + hashCode += 17 * str.GetHashCode(); + break; + } + + case JsonValueKind.Array: + if (depth < MaxHashDepth) + foreach (var item in element.EnumerateArray()) + hashCode += 17*ComputeHashCode(item, depth+1); + break; + + case JsonValueKind.Object: + foreach (var property in element.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal)) + { + hashCode += 17*property.Name.GetHashCode(); + if (depth < MaxHashDepth) + hashCode += 17*ComputeHashCode(property.Value, depth+1); + } + break; + + default: + throw new InvalidOperationException(string.Format("Unknown JsonValueKind {0}", element.ValueKind)); + } + return hashCode; + } + } + + +} // namespace JsonCons.JsonPath diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonFlattener.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonFlattener.cs new file mode 100644 index 00000000..b5b8510b --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonFlattener.cs @@ -0,0 +1,503 @@ +using System; +using System.Linq; +using System.Text; +using System.Text.Json; + +namespace AWS.Lambda.Powertools.JMESPath.Utilities +{ + /// + /// Defines how the unflatten operation handles integer tokens in a JSON Pointer + /// + /// + /// This example illustrates the use of + /// + /// using System; + /// using System.Diagnostics; + /// using System.Text.Json; + /// using JsonCons.Utilities; + /// + /// public class Example + /// { + /// public static void Main() + /// { + /// using var doc = JsonDocument.Parse(@" + /// { + /// ""discards"": { + /// ""1000"": ""Record does not exist"", + /// ""1004"": ""Queue limit exceeded"", + /// ""1010"": ""Discarding timed-out partial msg"" + /// }, + /// ""warnings"": { + /// ""0"": ""Phone number missing country code"", + /// ""1"": ""State code missing"", + /// ""2"": ""Zip code missing"" + /// } + /// } + /// "); + /// + /// var options = new JsonSerializerOptions() { WriteIndented = true }; + /// + /// using JsonDocument flattened = JsonFlattener.Flatten(doc.RootElement); + /// Console.WriteLine("The flattened document:\n"); + /// Console.WriteLine($"{JsonSerializer.Serialize(flattened, options)}\n"); + /// + /// Console.WriteLine("Unflatten integer tokens as array indices if possible:\n"); + /// using JsonDocument unflattened1 = JsonFlattener.Unflatten(flattened.RootElement, + /// IntegerTokenUnflattening.TryIndex); + /// Console.WriteLine($"{JsonSerializer.Serialize(unflattened1, options)}\n"); + /// + /// Console.WriteLine("Always unflatten integer tokens as object names:\n"); + /// using JsonDocument unflattened2 = JsonFlattener.Unflatten(flattened.RootElement, + /// IntegerTokenUnflattening.AssumeName); + /// Console.WriteLine($"{JsonSerializer.Serialize(unflattened2, options)}\n"); + /// } + /// } + /// + /// Output: + /// + /// The flattened document: + /// + /// { + /// "/discards/1000": "Record does not exist", + /// "/discards/1004": "Queue limit exceeded", + /// "/discards/1010": "Discarding timed-out partial msg", + /// "/warnings/0": "Phone number missing country code", + /// "/warnings/1": "State code missing", + /// "/warnings/2": "Zip code missing" + /// } + /// + /// Unflatten integer tokens as array indices if possible: + /// + /// { + /// "discards": { + /// "1000": "Record does not exist", + /// "1004": "Queue limit exceeded", + /// "1010": "Discarding timed-out partial msg" + /// }, + /// "warnings": [ + /// "Phone number missing country code", + /// "State code missing", + /// "Zip code missing" + /// ] + /// } + /// + /// Always unflatten integer tokens as object names: + /// + /// { + /// "discards": { + /// "1000": "Record does not exist", + /// "1004": "Queue limit exceeded", + /// "1010": "Discarding timed-out partial msg" + /// }, + /// "warnings": { + /// "0": "Phone number missing country code", + /// "1": "State code missing", + /// "2": "Zip code missing" + /// } + /// } + /// + /// + public enum IntegerTokenUnflattening { + /// + /// The unflatten operation first tries to unflatten into a JSON array + /// using the integer tokens as sequential indices, and if that fails, unflattens into + /// a JSON object using the integer tokens as names. + /// + TryIndex, + /// + /// The unflatten operation always unflattens into a JSON object + /// using the integer tokens as names. + /// + AssumeName + } + + /// + /// Provides functionality to flatten a JSON object or array to a single depth JSON object of JSON Pointer-value pairs, + /// and to unflatten a flattened JSON object. + /// + /// + /// This example shows how to flatten and unflatten a JSON value + /// + /// using System; + /// using System.Diagnostics; + /// using System.Text.Json; + /// using JsonCons.Utilities; + /// + /// public class Example + /// { + /// public static void Main() + /// { + /// using var doc = JsonDocument.Parse(@" + /// { + /// ""application"": ""hiking"", + /// ""reputons"": [ + /// { + /// ""rater"": ""HikingAsylum"", + /// ""assertion"": ""advanced"", + /// ""rated"": ""Marilyn C"", + /// ""rating"": 0.90 + /// }, + /// { + /// ""rater"": ""HikingAsylum"", + /// ""assertion"": ""intermediate"", + /// ""rated"": ""Hongmin"", + /// ""rating"": 0.75 + /// } + /// ] + /// } + /// "); + /// + /// using JsonDocument flattened = JsonFlattener.Flatten(doc.RootElement); + /// + /// var options = new JsonSerializerOptions() { WriteIndented = true }; + /// + /// Console.WriteLine($"{JsonSerializer.Serialize(flattened, options)}\n"); + /// + /// using JsonDocument unflattened = JsonFlattener.Unflatten(flattened.RootElement); + /// + /// var comparer = JsonElementEqualityComparer.Instance; + /// Debug.Assert(comparer.Equals(unflattened.RootElement,doc.RootElement)); + /// } + /// } + /// + /// Output: + /// + /// { + /// "/application": "hiking", + /// "/reputons/0/rater": "HikingAsylum", + /// "/reputons/0/assertion": "advanced", + /// "/reputons/0/rated": "Marilyn C", + /// "/reputons/0/rating": 0.90, + /// "/reputons/1/rater": "HikingAsylum", + /// "/reputons/1/assertion": "intermediate", + /// "/reputons/1/rated": "Hongmin", + /// "/reputons/1/rating": 0.75 + /// } + /// + /// + + public static class JsonFlattener + { + /// + /// Converts a JSON object or array into a single depth JSON object of name-value pairs, + /// such that the names are JSON Pointer strings, and the values are either string, + /// number, true, false, null, empty object, or empty array. + /// + /// + /// It is the users responsibilty to properly Dispose the returned value + /// + /// The value to be flattened. + /// The flattened value + public static JsonDocument Flatten(JsonElement value) + { + var result = new JsonDocumentBuilder(JsonValueKind.Object); + var parentKey = ""; + _Flatten(parentKey, value, result); + return result.ToJsonDocument(); + } + + /// + /// Recovers the orginal JSON value from a JSON object in flattened form, to the extent possible. + /// There may not be a unique solution, an integer token in a JSON Pointer could be an array index or + /// it could be an object name. The default behavior is to attempt to recover arrays. The + /// parameter can be used to recover objects with integer names instead. + /// + /// + /// It is the users responsibilty to properly Dispose the returned value + /// + /// The flattened value, which must be a JSON object of name-value pairs, such that + /// the names are JSON Pointer strings, and the values are either string, + /// number, true, false, null, empty object, or empty array. + /// Options for handling integer tokens in the JSON Pointer. + /// The unflattened value + /// + /// The is not a JSON object, or has a name that contains an invalid JSON pointer. + /// + public static JsonDocument Unflatten(JsonElement flattenedValue, + IntegerTokenUnflattening options = IntegerTokenUnflattening.TryIndex) + { + if (options == IntegerTokenUnflattening.TryIndex) + { + if (TryUnflattenArray(flattenedValue, out var val)) + { + return val.ToJsonDocument(); + } + else + { + return UnflattenToObject(flattenedValue, options).ToJsonDocument(); + } + } + else + { + return UnflattenToObject(flattenedValue, options).ToJsonDocument(); + } + } + + private static void _Flatten(string parentKey, + JsonElement parentValue, + JsonDocumentBuilder result) + { + switch (parentValue.ValueKind) + { + case JsonValueKind.Array: + { + if (parentValue.GetArrayLength() == 0) + { + result.AddProperty(parentKey, new JsonDocumentBuilder(parentValue)); + } + else + { + for (var i = 0; i < parentValue.GetArrayLength(); ++i) + { + var buffer = new StringBuilder(parentKey); + buffer.Append('/'); + buffer.Append(i.ToString()); + _Flatten(buffer.ToString(), parentValue[i], result); + } + } + break; + } + + case JsonValueKind.Object: + { + if (parentValue.EnumerateObject().Count() == 0) + { + result.AddProperty(parentKey, new JsonDocumentBuilder(parentValue)); + } + else + { + foreach (var item in parentValue.EnumerateObject()) + { + var buffer = new StringBuilder(parentKey); + buffer.Append('/'); + buffer.Append(JsonPointer.Escape(item.Name)); + _Flatten(buffer.ToString(), item.Value, result); + } + } + break; + } + + default: + { + result.AddProperty(parentKey, new JsonDocumentBuilder(parentValue)); + break; + } + } + } + + // unflatten + + private static JsonDocumentBuilder SafeUnflatten(JsonDocumentBuilder value) + { + if (value.ValueKind != JsonValueKind.Object || value.GetObjectLength() == 0) + { + return value; + } + var safe = true; + var index = 0; + foreach (var item in value.EnumerateObject()) + { + if (!int.TryParse(item.Key, out var n) || index++ != n) + { + safe = false; + break; + } + } + + if (safe) + { + var j = new JsonDocumentBuilder(JsonValueKind.Array); + foreach (var item in value.EnumerateObject()) + { + j.AddArrayItem(item.Value); + } + var a = new JsonDocumentBuilder(JsonValueKind.Array); + foreach (var item in j.EnumerateArray()) + { + a.AddArrayItem(SafeUnflatten(item)); + } + return a; + } + else + { + var o = new JsonDocumentBuilder(JsonValueKind.Object); + foreach (var item in value.EnumerateObject()) + { + //if (!o.ContainsPropertyName(item.Key)) + //{ + // o.AddProperty(item.Key, SafeUnflatten (item.Value)); + //} + o.TryAddProperty(item.Key, SafeUnflatten (item.Value)); + } + return o; + } + } + + private static bool TryUnflattenArray(JsonElement value, out JsonDocumentBuilder result) + { + if (value.ValueKind != JsonValueKind.Object) + { + throw new ArgumentException("The value to unflatten is not a JSON object"); + } + + result = new JsonDocumentBuilder(JsonValueKind.Object); + + foreach (var item in value.EnumerateObject()) + { + JsonDocumentBuilder? parent = null; + var part = result; + var parentIndex = 0; + var parentName = ""; + + if (!JsonPointer.TryParse(item.Name, out var ptr)) + { + throw new ArgumentException("Name contains invalid JSON Pointer"); + } + var index = 0; + + var it = ptr.GetEnumerator(); + var more = it.MoveNext(); + while (more) + { + var token = it.Current; + + if (int.TryParse(token, out var n) && index++ == n) + { + if (part.ValueKind != JsonValueKind.Array) + { + if (parent != null && parent.ValueKind == JsonValueKind.Object) + { + parent.RemoveProperty(parentName); + var val = new JsonDocumentBuilder(JsonValueKind.Array); + parent.AddProperty(parentName, val); + part = val; + } + else if (parent != null && parent.ValueKind == JsonValueKind.Array) + { + var val = new JsonDocumentBuilder(JsonValueKind.Array); + parent[parentIndex] = val; + part = val; + } + else + { + return false; + } + } + parent = part; + parentIndex = n; + parentName = token; + more = it.MoveNext(); + if (more) + { + if (n >= part.GetArrayLength()) + { + part.AddArrayItem(new JsonDocumentBuilder(JsonValueKind.Object)); + part = part[part.GetArrayLength() - 1]; + } + else + { + part = part[n]; + } + } + else + { + part.AddArrayItem(new JsonDocumentBuilder(item.Value)); + part = part[part.GetArrayLength() - 1]; + } + } + else if (part.ValueKind == JsonValueKind.Object) + { + more = it.MoveNext(); + if (more) + { + if (part.TryGetProperty(token, out var val)) + { + part = val; + } + else + { + val = new JsonDocumentBuilder(JsonValueKind.Object); + part.AddProperty(token,val); + part = val; + } + } + else + { + if (part.TryGetProperty(token, out var val)) + { + part = val; + } + else + { + val = new JsonDocumentBuilder(item.Value); + part.AddProperty(token,val); + part = val; + } + } + } + else + { + return false; + } + } + } + + return true; + } + + private static JsonDocumentBuilder UnflattenToObject(JsonElement value, IntegerTokenUnflattening options = IntegerTokenUnflattening.TryIndex) + { + if (value.ValueKind != JsonValueKind.Object) + { + throw new ArgumentException("The value to unflatten is not a JSON object"); + } + + var result = new JsonDocumentBuilder(JsonValueKind.Object); + + foreach (var item in value.EnumerateObject()) + { + var part = result; + if (!JsonPointer.TryParse(item.Name, out var ptr)) + { + throw new ArgumentException("Name contains invalid JSON Pointer"); + } + var it = ptr.GetEnumerator(); + var more = it.MoveNext(); + while (more) + { + var s = it.Current; + more = it.MoveNext(); + if (more) + { + if (part.TryGetProperty(s, out var val)) + { + part = val; + } + else + { + val = new JsonDocumentBuilder(JsonValueKind.Object); + part.AddProperty(s,val); + part = val; + } + } + else + { + if (part.TryGetProperty(s, out var val)) + { + part = val; + } + else + { + val = new JsonDocumentBuilder(item.Value); + part.AddProperty(s,val); + part = val; + } + } + } + } + + return options == IntegerTokenUnflattening.TryIndex ? SafeUnflatten (result) : result; + } + } + +} // namespace JsonCons.Utilities diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonMergePatch.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonMergePatch.cs new file mode 100644 index 00000000..0f2d3a3e --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonMergePatch.cs @@ -0,0 +1,213 @@ +using System.Text.Json; + +namespace AWS.Lambda.Powertools.JMESPath.Utilities +{ + /// + /// Provides functionality for applying a JSON Merge Patch as + /// defined in RFC 7396 + /// to a JSON value. + /// + /// + /// The following example borrowed from [RFC 7396](https://datatracker.ietf.org/doc/html/rfc7396) shows how to apply a JSON Merge Patch to a JSON value + /// + /// using System; + /// using System.Diagnostics; + /// using System.Text.Json; + /// using JsonCons.Utilities; + /// + /// public class Example + /// { + /// public static void Main() + /// { + /// using var doc = JsonDocument.Parse(@" + /// { + /// ""title"": ""Goodbye!"", + /// ""author"" : { + /// ""givenName"" : ""John"", + /// ""familyName"" : ""Doe"" + /// }, + /// ""tags"":[ ""example"", ""sample"" ], + /// ""content"": ""This will be unchanged"" + /// } + /// "); + /// + /// using var patch = JsonDocument.Parse(@" + /// { + /// ""title"": ""Hello!"", + /// ""phoneNumber"": ""+01-123-456-7890"", + /// ""author"": { + /// ""familyName"": null + /// }, + /// ""tags"": [ ""example"" ] + /// } + /// "); + /// + /// using JsonDocument result = JsonMergePatch.ApplyMergePatch(doc.RootElement, patch.RootElement); + /// + /// var options = new JsonSerializerOptions() { WriteIndented = true }; + /// + /// Console.WriteLine("The original document:\n"); + /// Console.WriteLine($"{JsonSerializer.Serialize(doc, options)}\n"); + /// Console.WriteLine("The patch:\n"); + /// Console.WriteLine($"{JsonSerializer.Serialize(patch, options)}\n"); + /// Console.WriteLine("The result:\n"); + /// Console.WriteLine($"{JsonSerializer.Serialize(result, options)}\n"); + /// "); + /// } + /// } + /// + /// The original document: + /// + /// + /// { + /// "title": "Goodbye!", + /// "author": { + /// "givenName": "John", + /// "familyName": "Doe" + /// }, + /// "tags": [ + /// "example", + /// "sample" + /// ], + /// "content": "This will be unchanged" + /// } + /// + /// + /// The patch: + /// + /// + /// { + /// "title": "Hello!", + /// "phoneNumber": "\u002B01-123-456-7890", + /// "author": { + /// "familyName": null + /// }, + /// "tags": [ + /// "example" + /// ] + /// } + /// + /// + /// The result: + /// + /// + /// { + /// "title": "Hello!", + /// "author": { + /// "givenName": "John" + /// }, + /// "tags": [ + /// "example" + /// ], + /// "content": "This will be unchanged", + /// "phoneNumber": "\u002B01-123-456-7890" + /// } + /// + /// + + public static class JsonMergePatch + { + /// + /// Applies a JSON Merge Patch as defined in RFC 7396 + /// to a source JSON value. + /// + /// + /// It is the users responsibilty to properly Dispose the returned value + /// + /// The source JSON value. + /// The JSON merge patch to be applied to the source JSON value. + /// The patched JSON value + public static JsonDocument ApplyMergePatch(JsonElement source, JsonElement patch) + { + var documentBuilder = new JsonDocumentBuilder(source); + var builder = ApplyMergePatch(ref documentBuilder, patch); + return builder.ToJsonDocument(); + } + + private static JsonDocumentBuilder ApplyMergePatch(ref JsonDocumentBuilder target, JsonElement patch) + { + if (patch.ValueKind == JsonValueKind.Object) + { + if (target.ValueKind != JsonValueKind.Object) + { + target = new JsonDocumentBuilder(JsonValueKind.Object); + } + foreach (var property in patch.EnumerateObject()) + { + if (target.TryGetProperty(property.Name, out var item)) + { + target.RemoveProperty(property.Name); + if (property.Value.ValueKind != JsonValueKind.Null) + { + target.AddProperty(property.Name, ApplyMergePatch(ref item, property.Value)); + } + } + else if (property.Value.ValueKind != JsonValueKind.Null) + { + item = new JsonDocumentBuilder(JsonValueKind.Object); + target.AddProperty(property.Name, ApplyMergePatch(ref item, property.Value)); + } + } + return target; + } + else + { + return new JsonDocumentBuilder(patch); + } + } + + /// + /// Builds a JSON Merge Patch as defined in RFC 7396 + /// given two JSON values, a source and a target. + /// + /// + /// It is the users responsibilty to properly Dispose the returned value + /// + /// The source JSON value. + /// The target JSON value. + /// A JSON Merge Patch to convert the source JSON value to the target JSON value + public static JsonDocument FromDiff(JsonElement source, JsonElement target) + { + return _FromDiff(source, target).ToJsonDocument(); + } + + private static JsonDocumentBuilder _FromDiff(JsonElement source, JsonElement target) + { + var comparer = JsonElementEqualityComparer.Instance; + + if (source.ValueKind != JsonValueKind.Object || target.ValueKind != JsonValueKind.Object) + { + return new JsonDocumentBuilder(target); + } + var builder = new JsonDocumentBuilder(JsonValueKind.Object); + + foreach (var property in source.EnumerateObject()) + { + if (target.TryGetProperty(property.Name, out var value)) + { + if (!comparer.Equals(property.Value,value)) + { + builder.AddProperty(property.Name, _FromDiff(property.Value, value)); + } + } + else + { + builder.AddProperty(property.Name, new JsonDocumentBuilder(JsonValueKind.Null)); + } + } + + foreach (var property in target.EnumerateObject()) + { + JsonElement value; + if (!source.TryGetProperty(property.Name, out value)) + { + builder.AddProperty(property.Name, new JsonDocumentBuilder(property.Value)); + } + } + + return builder; + } + } + + +} // namespace JsonCons.Utilities diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPatch.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPatch.cs new file mode 100644 index 00000000..bf1fc449 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPatch.cs @@ -0,0 +1,413 @@ +using System; +using System.Diagnostics; +using System.Text; +using System.Text.Json; + +namespace AWS.Lambda.Powertools.JMESPath.Utilities +{ + /// + /// Captures error message and the operation that caused it. + /// + public sealed class JsonPatchException : Exception + { + /// + /// Constructs a . + /// + /// The operation that caused the error. + /// The error message. + public JsonPatchException( + string operation, + string message) : base(message) + { + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } + + Operation = operation; + } + + /// + /// Gets the that caused the error. + /// + public string Operation { get; } + } + + /// + /// Provides functionality for applying a JSON Patch as + /// defined in RFC 6902 + /// to a JSON value. + /// + /// + /// The following example borrowed from [jsonpatch.com](http://jsonpatch.com/) shows how to apply a JSON Patch to a JSON value + /// + /// using System; + /// using System.Diagnostics; + /// using System.Text.Json; + /// using JsonCons.Utilities; + /// + /// public class Example + /// { + /// public static void Main() + /// { + /// using var doc = JsonDocument.Parse(@" + /// { + /// ""baz"": ""qux"", + /// ""foo"": ""bar"" + /// } + /// "); + /// + /// using var patch = JsonDocument.Parse(@" + /// [ + /// { ""op"": ""replace"", ""path"": ""/baz"", ""value"": ""boo"" }, + /// { ""op"": ""add"", ""path"": ""/hello"", ""value"": [""world""] }, + /// { ""op"": ""remove"", ""path"": ""/foo"" } + /// ] + /// "); + /// + /// using JsonDocument result = JsonPatch.ApplyPatch(doc.RootElement, patch.RootElement); + /// + /// var options = new JsonSerializerOptions() { WriteIndented = true }; + /// + /// Console.WriteLine("The original document:\n"); + /// Console.WriteLine($"{JsonSerializer.Serialize(doc, options)}\n"); + /// Console.WriteLine("The patch:\n"); + /// Console.WriteLine($"{JsonSerializer.Serialize(patch, options)}\n"); + /// Console.WriteLine("The result:\n"); + /// Console.WriteLine($"{JsonSerializer.Serialize(result, options)}\n"); + /// "); + /// } + /// } + /// + /// The original document: + /// + /// + /// { + /// "baz": "qux", + /// "foo": "bar" + /// } + /// + /// + /// The patch: + /// + /// + /// [ + /// { + /// "op": "replace", + /// "path": "/baz", + /// "value": "boo" + /// }, + /// { + /// "op": "add", + /// "path": "/hello", + /// "value": [ + /// "world" + /// ] + /// }, + /// { + /// "op": "remove", + /// "path": "/foo" + /// } + /// ] + /// + /// + /// The result: + /// + /// { + /// "baz": "boo", + /// "hello": [ + /// "world" + /// ] + /// } + /// + /// + public static class JsonPatch + { + /// + /// Applies a JSON Patch as defined in RFC 6902 + /// to a source JSON value. + /// + /// + /// It is the users responsibilty to properly Dispose the returned value + /// + /// The source JSON value. + /// The patch to be applied to the source JSON value. + /// The patched JSON value + /// + /// The provided is invalid + /// + /// + /// A JSON Patch operation failed + /// + public static JsonDocument ApplyPatch(JsonElement source, + JsonElement patch) + { + var documentBuilder = new JsonDocumentBuilder(source); + ApplyPatch(ref documentBuilder, patch); + return documentBuilder.ToJsonDocument(); + } + + private static void ApplyPatch(ref JsonDocumentBuilder target, + JsonElement patch) + { + var comparer = JsonElementEqualityComparer.Instance; + + Debug.Assert(target != null); + + if (patch.ValueKind != JsonValueKind.Array) + { + throw new ArgumentException("Patch must be an array"); + } + + foreach (var operation in patch.EnumerateArray()) + { + if (!operation.TryGetProperty("op", out var opElement)) + { + throw new ArgumentException("Invalid patch"); + } + var op = opElement.GetString() ?? throw new InvalidOperationException("Operation cannot be null"); + + if (!operation.TryGetProperty("path", out var pathElement)) + { + throw new ArgumentException(op, "Invalid patch"); + } + var path = pathElement.GetString() ?? throw new InvalidOperationException("Operation cannot be null"); ; + + if (!JsonPointer.TryParse(path, out var location)) + { + throw new ArgumentException(op, "Invalid patch"); + } + + if (op =="test") + { + if (!operation.TryGetProperty("value", out var value)) + { + throw new ArgumentException(op, "Invalid patch"); + } + + if (!location.TryGetValue(target, out var tested)) + { + throw new ArgumentException(op, "Invalid patch"); + } + + using (var doc = tested.ToJsonDocument()) + { + if (!comparer.Equals(doc.RootElement, value)) + { + throw new JsonPatchException(op, "Test failed"); + } + } + } + else if (op =="add") + { + if (!operation.TryGetProperty("value", out var value)) + { + throw new ArgumentException(op, "Invalid patch"); + } + var valueBuilder = new JsonDocumentBuilder(value); + if (!location.TryAddIfAbsent(ref target, valueBuilder)) // try insert without replace + { + if (!location.TryReplace(ref target, valueBuilder)) // try insert without replace + { + throw new JsonPatchException(op, "Add failed"); + } + } + } + else if (op =="remove") + { + if (!location.TryRemove(ref target)) + { + throw new JsonPatchException(op, "Add failed"); + } + } + else if (op =="replace") + { + if (!operation.TryGetProperty("value", out var value)) + { + throw new ArgumentException(op, "Invalid patch"); + } + var valueBuilder = new JsonDocumentBuilder(value); + if (!location.TryReplace(ref target, valueBuilder)) + { + throw new JsonPatchException(op, "Replace failed"); + } + } + else if (op =="move") + { + if (!operation.TryGetProperty("from", out var fromElement)) + { + throw new ArgumentException(op, "Invalid patch"); + } + var from = fromElement.GetString() ?? throw new InvalidOperationException("From element cannot be null"); ; + + if (!JsonPointer.TryParse(from, out var fromPointer)) + { + throw new ArgumentException(op, "Invalid patch"); + } + + if (!fromPointer.TryGetValue(target, out var value)) + { + throw new JsonPatchException(op, "Move failed"); + } + + if (!fromPointer.TryRemove(ref target)) + { + throw new JsonPatchException(op, "Move failed"); + } + if (!location.TryAddIfAbsent(ref target, value)) + { + if (!location.TryReplace(ref target, value)) // try insert without replace + { + throw new JsonPatchException(op, "Move failed"); + } + } + } + else if (op =="copy") + { + if (!operation.TryGetProperty("from", out var fromElement)) + { + throw new ArgumentException(op, "Invalid patch"); + } + var from = fromElement.GetString() ?? throw new InvalidOperationException("from cannot be null"); + if (!JsonPointer.TryParse(from, out var fromPointer)) + { + throw new ArgumentException(op, "Invalid patch"); + } + + if (!fromPointer.TryGetValue(target, out var value)) + { + throw new JsonPatchException(op, "Copy failed"); + } + if (!location.TryAddIfAbsent(ref target, value)) + { + if (!location.TryReplace(ref target, value)) // try insert without replace + { + throw new JsonPatchException(op, "Move failed"); + } + } + } + } + } + + /// + /// Builds a JSON Patch as defined in RFC 6902 + /// given two JSON values, a source and a target. + /// + /// + /// It is the users responsibilty to properly Dispose the returned value + /// + /// The source JSON value. + /// The target JSON value. + /// A JSON Merge Patch to convert the source JSON value to the target JSON value + public static JsonDocument FromDiff(JsonElement source, + JsonElement target) + { + return _FromDiff(source, target, "").ToJsonDocument(); + } + + private static JsonDocumentBuilder _FromDiff(JsonElement source, + JsonElement target, + string path) + { + var builder = new JsonDocumentBuilder(JsonValueKind.Array); + + var comparer = JsonElementEqualityComparer.Instance; + + if (comparer.Equals(source,target)) + { + return builder; + } + + if (source.ValueKind == JsonValueKind.Array && target.ValueKind == JsonValueKind.Array) + { + var common = Math.Min(source.GetArrayLength(),target.GetArrayLength()); + for (var i = 0; i < common; ++i) + { + var buffer = new StringBuilder(path); + buffer.Append("/"); + buffer.Append(i.ToString()); + var temp_diff = _FromDiff(source[i], target[i], buffer.ToString()); + foreach (var item in temp_diff.EnumerateArray()) + { + builder.AddArrayItem(item); + } + } + // Element in source, not in target - remove + for (var i = source.GetArrayLength(); i-- > target.GetArrayLength();) + { + var buffer = new StringBuilder(path); + buffer.Append("/"); + buffer.Append(i.ToString()); + var valBuilder = new JsonDocumentBuilder(JsonValueKind.Object); + valBuilder.AddProperty("op", new JsonDocumentBuilder("remove")); + valBuilder.AddProperty("path", new JsonDocumentBuilder(buffer.ToString())); + builder.AddArrayItem(valBuilder); + } + // Element in target, not in source - add, + for (var i = source.GetArrayLength(); i < target.GetArrayLength(); ++i) + { + var a = target[i]; + var buffer = new StringBuilder(path); + buffer.Append("/"); + buffer.Append(i.ToString()); + var valBuilder = new JsonDocumentBuilder(JsonValueKind.Object); + valBuilder.AddProperty("op", new JsonDocumentBuilder("add")); + valBuilder.AddProperty("path", new JsonDocumentBuilder(buffer.ToString())); + valBuilder.AddProperty("value", new JsonDocumentBuilder(a)); + builder.AddArrayItem(valBuilder); + } + } + else if (source.ValueKind == JsonValueKind.Object && target.ValueKind == JsonValueKind.Object) + { + foreach (var a in source.EnumerateObject()) + { + var buffer = new StringBuilder(path); + buffer.Append("/"); + buffer.Append(JsonPointer.Escape(a.Name)); + + if (target.TryGetProperty(a.Name, out var element)) + { + var temp_diff = _FromDiff(a.Value, element, buffer.ToString()); + foreach (var item in temp_diff.EnumerateArray()) + { + builder.AddArrayItem(item); + } + } + else + { + var valBuilder = new JsonDocumentBuilder(JsonValueKind.Object); + valBuilder.AddProperty("op", new JsonDocumentBuilder("remove")); + valBuilder.AddProperty("path", new JsonDocumentBuilder(buffer.ToString())); + builder.AddArrayItem(valBuilder); + } + } + foreach (var a in target.EnumerateObject()) + { + JsonElement element; + if (!source.TryGetProperty(a.Name, out element)) + { + var buffer = new StringBuilder(path); + buffer.Append("/"); + buffer.Append(JsonPointer.Escape(a.Name)); + var valBuilder = new JsonDocumentBuilder(JsonValueKind.Object); + valBuilder.AddProperty("op", new JsonDocumentBuilder("add")); + valBuilder.AddProperty("path", new JsonDocumentBuilder(buffer.ToString())); + valBuilder.AddProperty("value", new JsonDocumentBuilder(a.Value)); + builder.AddArrayItem(valBuilder); + } + } + } + else + { + var valBuilder = new JsonDocumentBuilder(JsonValueKind.Object); + valBuilder.AddProperty("op", new JsonDocumentBuilder("replace")); + valBuilder.AddProperty("path", new JsonDocumentBuilder(path)); + valBuilder.AddProperty("value", new JsonDocumentBuilder(target)); + builder.AddArrayItem(valBuilder); + } + + return builder; + } + } + +} // namespace JsonCons.Utilities diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointer.cs new file mode 100644 index 00000000..059dc613 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointer.cs @@ -0,0 +1,576 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json; + +namespace AWS.Lambda.Powertools.JMESPath.Utilities +{ + /// + /// Represents a JSON Pointer as defined by RFC 6901 + /// + /// + /// The following example shows how to get a value at a referenced location in a JSON document. + /// + /// using System; + /// using System.Diagnostics; + /// using System.Text.Json; + /// using JsonCons.Utilities; + /// + /// public class Example + /// { + /// public static void Main() + /// { + /// using var doc = JsonDocument.Parse(@" + /// [ + /// { ""category"": ""reference"", + /// ""author"": ""Nigel Rees"", + /// ""title"": ""Sayings of the Century"", + /// ""price"": 8.95 + /// }, + /// { ""category"": ""fiction"", + /// ""author"": ""Evelyn Waugh"", + /// ""title"": ""Sword of Honour"", + /// ""price"": 12.99 + /// } + /// ] + /// "); + /// + /// var options = new JsonSerializerOptions() { WriteIndented = true }; + /// + /// JsonPointer pointer = JsonPointer.Parse("/1/author"); + /// + /// JsonElement element; + /// + /// if (pointer.TryGetValue(doc.RootElement, out element)) + /// { + /// Console.WriteLine($"{JsonSerializer.Serialize(element, options)}\n"); + /// } + /// } + /// } + /// + /// Output: + /// + /// + /// "Evelyn Waugh" + /// + /// + + public sealed class JsonPointer : IEnumerable, IEquatable + { + /// Gets a singleton instance of a to the root value of a JSON document. + public static JsonPointer Default {get;} = new(); + + private enum JsonPointerState {Start, Escaped, Delim} + + /// + /// Returns a list of (unescaped) reference tokens + /// + public IReadOnlyList Tokens {get;} + + /// + /// Constructs a JSON Pointer to the root value of a JSON document + /// + + public JsonPointer() + { + Tokens = new List(); + } + + /// + /// Constructs a JSON Pointer from a list of (unescaped) reference tokens + /// + /// A list of (unescaped) reference tokens. + + public JsonPointer(IReadOnlyList tokens) + { + Tokens = tokens; + } + + /// + /// Parses a JSON Pointer represented as a string value or a + /// fragment identifier (starts with #) into a . + /// + /// A JSON Pointer represented as a string or a fragment identifier. + /// A . + /// + /// The is . + /// + /// + /// The is invalid. + /// + public static JsonPointer Parse(string input) + { + if (!TryParse(input, out var pointer)) + { + throw new ArgumentException("The provided JSON Pointer is invalid."); + } + return pointer; + } + + /// + /// Parses a JSON Pointer represented as a string value or a + /// fragment identifier (starts with #) into a . + /// + /// A JSON Pointer represented as a string or a fragment identifier. + /// The JsonPointer. + /// true if the input string can be parsed into a list of reference tokens, false otherwise. + /// + /// The is . + /// + public static bool TryParse(string input, out JsonPointer pointer) + { + if (input == null) + { + throw new ArgumentNullException(nameof(input)); + } + var tokens = new List(); + + if (input.Length == 0 || input.Equals("#")) + { + pointer = new JsonPointer(tokens); + return true; + } + + var state = JsonPointerState.Start; + var index = 0; + var buffer = new StringBuilder(); + + if (input[0] == '#') + { + input = Uri.UnescapeDataString(input); + index = 1; + } + + while (index < input.Length) + { + var done = false; + while (index < input.Length && !done) + { + switch (state) + { + case JsonPointerState.Start: + switch (input[index]) + { + case '/': + state = JsonPointerState.Delim; + break; + default: + pointer = Default; + return false; + }; + break; + case JsonPointerState.Delim: + switch (input[index]) + { + case '/': + done = true; + break; + case '~': + state = JsonPointerState.Escaped; + break; + default: + buffer.Append(input[index]); + break; + }; + break; + case JsonPointerState.Escaped: + switch (input[index]) + { + case '0': + buffer.Append('~'); + state = JsonPointerState.Delim; + break; + case '1': + buffer.Append('/'); + state = JsonPointerState.Delim; + break; + default: + pointer = Default; + return false; + }; + break; + default: + pointer = Default; + return false; + } + ++index; + } + tokens.Add(buffer.ToString()); + buffer.Clear(); + } + if (buffer.Length > 0) + { + tokens.Add(buffer.ToString()); + } + pointer = new JsonPointer(tokens); + return true; + } + + /// + /// Returns an enumerator that iterates through a list of reference tokens. + /// + /// An IEnumerator<string> for a list of reference tokens. + public IEnumerator GetEnumerator() + { + return Tokens.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return (System.Collections.IEnumerator) GetEnumerator(); + } + + /// + /// Returns a JSON Pointer represented as a string value. + /// + /// A JSON Pointer represented as a string value. + + public override string ToString() + { + var buffer = new StringBuilder(); + foreach (var token in Tokens) + { + buffer.Append("/"); + Escape(token, buffer); + } + return buffer.ToString(); + } + + /// + /// Returns a string representing the JSON Pointer as a URI fragment identifier + /// + /// A JSON Pointer represented as a fragment identifier. + + public string ToUriFragment() + { + var buffer = new StringBuilder(); + + buffer.Append("#"); + foreach (var token in Tokens) + { + buffer.Append("/"); + var s = Uri.EscapeUriString(token); + var span = s.AsSpan(); + for (var i = 0; i < span.Length; ++i) + { + var c = span[i]; + switch (c) + { + case '~': + buffer.Append('~'); + buffer.Append('0'); + break; + case '/': + buffer.Append('~'); + buffer.Append('1'); + break; + default: + buffer.Append(c); + break; + } + } + } + return buffer.ToString(); + } + + /// + /// Determines whether two JSONPointer objects have the same value. + /// + /// + /// true if other is a and has exactly the same reference tokens as this instance; otherwise, false. + /// If other is null, the method returns false. + public bool Equals(JsonPointer other) + { + if (other == null) + { + return false; + } + if (Tokens.Count != other.Tokens.Count) + { + return false; + } + for (var i = 0; i < Tokens.Count; ++i) + { + if (!Tokens[i].Equals(other.Tokens[i])) + { + return false; + } + } + return true; + } + /// + /// Determines whether this instance and a specified object, which must also be a JSONPointer object, have the same value. + /// + /// + /// + public override bool Equals(object other) + { + if (other == null) + { + return false; + } + + return Equals((JsonPointer)other); + } + + /// + /// Returns the hash code for this + /// + /// A 32-bit signed integer hash code. + /// + public override int GetHashCode() + { + var hashCode = Tokens.GetHashCode(); + foreach (var token in Tokens) + { + hashCode += 17*token.GetHashCode(); + } + return hashCode; + } + + /// + /// Returns true if the provided contains a value at the referenced location. + /// + /// The root that is to be queried. + /// true if the provided contains a value at the referenced location, otherwise false. + public bool ContainsValue(JsonElement root) + { + var value = root; + + foreach (var token in Tokens) + { + if (value.ValueKind == JsonValueKind.Array) + { + if (token == "-") + { + return false; + } + + if (!int.TryParse(token, out var index)) + { + return false; + } + if (index >= value.GetArrayLength()) + { + return false; + } + value = value[index]; + } + else if (value.ValueKind == JsonValueKind.Object) + { + if (!value.TryGetProperty(token, out value)) + { + return false; + } + } + else + { + return false; + } + } + + return true; + } + + /// + /// Returns true if the provided contains a value at the referenced location. + /// + /// The root that is to be queried. + /// The JSON string or URI Fragment representation of the JSON pointer. + /// true if the provided contains a value at the referenced location, otherwise false. + /// + /// The is . + /// + public static bool ContainsValue(JsonElement root, string pointer) + { + if (pointer == null) + { + throw new ArgumentNullException(nameof(pointer)); + } + + if (!TryParse(pointer, out var location)) + { + return false; + } + var value = root; + + foreach (var token in location.Tokens) + { + if (value.ValueKind == JsonValueKind.Array) + { + if (token == "-") + { + return false; + } + + if (!int.TryParse(token, out var index)) + { + return false; + } + if (index >= value.GetArrayLength()) + { + return false; + } + value = value[index]; + } + else if (value.ValueKind == JsonValueKind.Object) + { + if (!value.TryGetProperty(token, out value)) + { + return false; + } + } + else + { + return false; + } + } + + return true; + } + + /// + /// Gets the value at the referenced location in the provided . + /// + /// The root that is to be queried. + /// Contains the value at the referenced location, if found. + /// true if the value was found at the referenced location, otherwise false. + public bool TryGetValue(JsonElement root, out JsonElement value) + { + value = root; + + foreach (var token in Tokens) + { + if (value.ValueKind == JsonValueKind.Array) + { + if (token == "-") + { + return false; + } + + if (!int.TryParse(token, out var index)) + { + return false; + } + if (index >= value.GetArrayLength()) + { + return false; + } + value = value[index]; + } + else if (value.ValueKind == JsonValueKind.Object) + { + if (!value.TryGetProperty(token, out value)) + { + return false; + } + } + else + { + return false; + } + } + + return true; + } + + /// + /// Creates a new JsonPointer by appending a name token to the provided JsonPointer. + /// + /// The provided JsonPointer + /// A name token + /// A new JsonPointer + public static JsonPointer Append(JsonPointer pointer, string token) + { + var tokens = new List(pointer.Tokens); + tokens.Add(token); + return new JsonPointer(tokens); + } + + /// + /// Creates a new JsonPointer by appending an index token to the provided JsonPointer. + /// + /// The provided JsonPointer + /// An index token + /// A new JsonPointer + public static JsonPointer Append(JsonPointer pointer, int token) + { + var tokens = new List(pointer.Tokens); + tokens.Add(token.ToString()); + return new JsonPointer(tokens); + } + + /// + /// Gets the value at the referenced location in the provided . + /// + /// The root that is to be queried. + /// The JSON string or URI Fragment representation of the JSON pointer. + /// Contains the value at the referenced location, if found. + /// true if the value was found at the referenced location, otherwise false. + /// + /// The is . + /// + public static bool TryGetValue(JsonElement root, string pointer, out JsonElement value) + { + if (pointer == null) + { + throw new ArgumentNullException(nameof(pointer)); + } + + if (!TryParse(pointer, out var location)) + { + value = root; + return false; + } + + return location.TryGetValue(root, out value); + } + + /// + /// Escapes a JSON Pointer token + /// + /// + /// + /// + /// The is . + /// + public static string Escape(string token) + { + if (token == null) + { + throw new ArgumentNullException(nameof(token)); + } + + var buffer = new StringBuilder(); + Escape(token, buffer); + return buffer.ToString(); + } + + private static void Escape(string token, StringBuilder buffer) + { + if (token == null) + { + throw new ArgumentNullException(nameof(token)); + } + + foreach (var c in token) + { + switch (c) + { + case '~': + buffer.Append('~'); + buffer.Append('0'); + break; + case '/': + buffer.Append('~'); + buffer.Append('1'); + break; + default: + buffer.Append(c); + break; + } + } + } + } + +} // namespace JsonCons.Utilities diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointerExtensions.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointerExtensions.cs new file mode 100644 index 00000000..a729c44e --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointerExtensions.cs @@ -0,0 +1,343 @@ +using System.Collections.Generic; +using System.Text.Json; + +namespace AWS.Lambda.Powertools.JMESPath.Utilities +{ + internal static class JsonPointerExtensions + { + public static bool TryResolve(string token, JsonDocumentBuilder current, out JsonDocumentBuilder result) + { + result = current; + + if (result.ValueKind == JsonValueKind.Array) + { + if (token == "-") + { + return false; + } + + if (!int.TryParse(token, out var index)) + { + return false; + } + if (index >= result.GetArrayLength()) + { + return false; + } + result = result[index]; + } + else if (result.ValueKind == JsonValueKind.Object) + { + if (!result.TryGetProperty(token, out result)) + { + return false; + } + } + else + { + return false; + } + + return true; + } + + public static JsonPointer ToDefinitePath(this JsonPointer pointer, JsonDocumentBuilder value) + { + if (value.ValueKind == JsonValueKind.Array && pointer.Tokens.Count > 0 && pointer.Tokens[pointer.Tokens.Count-1] == "-") + { + var tokens = new List(); + for (var i = 0; i < pointer.Tokens.Count-1; ++i) + { + tokens.Add(pointer.Tokens[i]); + } + tokens.Add(value.GetArrayLength().ToString()); + return new JsonPointer(tokens); + } + else + { + return pointer; + } + } + + public static bool TryGetValue(this JsonPointer pointer, JsonDocumentBuilder root, out JsonDocumentBuilder value) + { + value = root; + + foreach (var token in pointer) + { + if (!TryResolve(token,value,out value)) + { + return false; + } + } + + return true; + } + + public static bool TryAdd(this JsonPointer location, + ref JsonDocumentBuilder root, + JsonDocumentBuilder value) + { + var current = root; + var token = ""; + + var enumerator = location.GetEnumerator(); + var more = enumerator.MoveNext(); + if (!more) + { + return false; + } + while (more) + { + token = enumerator.Current; + more = enumerator.MoveNext(); + if (more) + { + if (!TryResolve(token, current, out current)) + { + return false; + } + } + } + + if (current.ValueKind == JsonValueKind.Array) + { + if (token.Length == 1 && token[0] == '-') + { + current.AddArrayItem(value); + current = current[current.GetArrayLength()-1]; + } + else + { + if (!int.TryParse(token, out var index)) + { + return false; + } + if (index > current.GetArrayLength()) + { + return false; + } + if (index == current.GetArrayLength()) + { + current.AddArrayItem(value); + current = value; + } + else + { + current.InsertArrayItem(index,value); + current = value; + } + } + } + else if (current.ValueKind == JsonValueKind.Object) + { + if (current.ContainsPropertyName(token)) + { + current.RemoveProperty(token); + } + current.AddProperty(token, value); + current = value; + } + else + { + return false; + } + return true; + } + + public static bool TryAddIfAbsent(this JsonPointer location, + ref JsonDocumentBuilder root, + JsonDocumentBuilder value) + { + var current = root; + var token = ""; + + var enumerator = location.GetEnumerator(); + var more = enumerator.MoveNext(); + if (!more) + { + return false; + } + while (more) + { + token = enumerator.Current; + more = enumerator.MoveNext(); + if (more) + { + if (!TryResolve(token, current, out current)) + { + return false; + } + } + } + + if (current.ValueKind == JsonValueKind.Array) + { + if (token.Length == 1 && token[0] == '-') + { + current.AddArrayItem(value); + current = current[current.GetArrayLength()-1]; + } + else + { + if (!int.TryParse(token, out var index)) + { + return false; + } + if (index > current.GetArrayLength()) + { + return false; + } + if (index == current.GetArrayLength()) + { + current.AddArrayItem(value); + current = value; + } + else + { + current.InsertArrayItem(index,value); + current = value; + } + } + } + else if (current.ValueKind == JsonValueKind.Object) + { + if (current.ContainsPropertyName(token)) + { + return false; + } + current.AddProperty(token, value); + current = value; + } + else + { + return false; + } + return true; + } + + public static bool TryRemove(this JsonPointer location, + ref JsonDocumentBuilder root) + { + var current = root; + var token = ""; + + var enumerator = location.GetEnumerator(); + var more = enumerator.MoveNext(); + if (!more) + { + return false; + } + while (more) + { + token = enumerator.Current; + more = enumerator.MoveNext(); + if (more) + { + if (!TryResolve(token, current, out current)) + { + return false; + } + } + } + + if (current.ValueKind == JsonValueKind.Array) + { + if (token.Length == 1 && token[0] == '-') + { + return false; + } + else + { + if (!int.TryParse(token, out var index)) + { + return false; + } + if (index >= current.GetArrayLength()) + { + return false; + } + current.RemoveArrayItemAt(index); + } + } + else if (current.ValueKind == JsonValueKind.Object) + { + if (current.ContainsPropertyName(token)) + { + current.RemoveProperty(token); + } + } + else + { + return false; + } + return true; + } + + public static bool TryReplace(this JsonPointer location, + ref JsonDocumentBuilder root, + JsonDocumentBuilder value) + { + var current = root; + var token = ""; + + var enumerator = location.GetEnumerator(); + var more = enumerator.MoveNext(); + if (!more) + { + return false; + } + while (more) + { + token = enumerator.Current; + more = enumerator.MoveNext(); + if (more) + { + if (!TryResolve(token, current, out current)) + { + return false; + } + } + } + + if (current.ValueKind == JsonValueKind.Array) + { + if (token.Length == 1 && token[0] == '-') + { + return false; + } + else + { + if (!int.TryParse(token, out var index)) + { + return false; + } + if (index >= current.GetArrayLength()) + { + return false; + } + current[index] = value; + } + } + else if (current.ValueKind == JsonValueKind.Object) + { + if (current.ContainsPropertyName(token)) + { + current.RemoveProperty(token); + } + else + { + return false; + } + current.AddProperty(token, value); + } + else + { + return false; + } + return true; + } + + } + +} // namespace JsonCons.Utilities diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Value.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Value.cs new file mode 100644 index 00000000..8ec005d6 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Value.cs @@ -0,0 +1,816 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json; +using System.Text.Json.Nodes; + +namespace AWS.Lambda.Powertools.JMESPath +{ + internal readonly struct NameValuePair + { + public string Name { get; } + public IValue Value { get; } + + public NameValuePair(string name, IValue value) + { + Name = name; + Value = value; + } + } + + internal interface IArrayValueEnumerator : IEnumerator, IEnumerable + { + } + + internal interface IObjectValueEnumerator : IEnumerator, IEnumerable + { + } + + internal enum JmesPathType + { + Null, + Array, + False, + Number, + Object, + String, + True, + Expression + } + + internal interface IValue + { + JmesPathType Type {get;} + IValue this[int index] {get;} + int GetArrayLength(); + string GetString(); + bool TryGetDecimal(out decimal value); + bool TryGetDouble(out double value); + bool TryGetProperty(string propertyName, out IValue property); + IArrayValueEnumerator EnumerateArray(); + IObjectValueEnumerator EnumerateObject(); + IExpression GetExpression(); + }; + + internal readonly struct JsonElementValue : IValue + { + private class ArrayEnumerator : IArrayValueEnumerator + { + private JsonElement.ArrayEnumerator _enumerator; + + public ArrayEnumerator(JsonElement.ArrayEnumerator enumerator) + { + _enumerator = enumerator; + } + + public bool MoveNext() + { + return _enumerator.MoveNext(); + } + + public void Reset() { _enumerator.Reset(); } + + void IDisposable.Dispose() { _enumerator.Dispose();} + + public IValue Current => new JsonElementValue(_enumerator.Current); + + object System.Collections.IEnumerator.Current => Current; + + public IEnumerator GetEnumerator() + { + return new ArrayEnumerator(_enumerator.GetEnumerator()); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + private class ObjectEnumerator : IObjectValueEnumerator + { + private JsonElement.ObjectEnumerator _enumerator; + + public ObjectEnumerator(JsonElement.ObjectEnumerator enumerator) + { + _enumerator = enumerator; + } + + public bool MoveNext() + { + return _enumerator.MoveNext(); + } + + public void Reset() { _enumerator.Reset(); } + + void IDisposable.Dispose() { _enumerator.Dispose();} + + public NameValuePair Current => new(_enumerator.Current.Name, new JsonElementValue(_enumerator.Current.Value)); + + object System.Collections.IEnumerator.Current => Current; + + public IEnumerator GetEnumerator() + { + return new ObjectEnumerator(_enumerator); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + private readonly JsonElement _element; + + internal JsonElementValue(JsonElement element) + { + _element = element; + } + + public JmesPathType Type + { + get + { + switch (_element.ValueKind) + { + case JsonValueKind.Array: + return JmesPathType.Array; + case JsonValueKind.False: + return JmesPathType.False; + case JsonValueKind.Number: + return JmesPathType.Number; + case JsonValueKind.Object: + return JmesPathType.Object; + case JsonValueKind.String: + return JmesPathType.String; + case JsonValueKind.True: + return JmesPathType.True; + default: + return JmesPathType.Null; + } + } + } + + public IValue this[int index] => new JsonElementValue(_element[index]); + + public int GetArrayLength() {return _element.GetArrayLength();} + + public string GetString() + { + return _element.GetString() ?? throw new InvalidOperationException("String cannot be null"); + } + + public bool TryGetDecimal(out decimal value) + { + return _element.TryGetDecimal(out value); + } + + public bool TryGetDouble(out double value) + { + return _element.TryGetDouble(out value); + } + + public bool TryGetProperty(string propertyName, out IValue property) + { + var r = _element.TryGetProperty(propertyName, out var prop); + + property = prop.ValueKind == JsonValueKind.String && IsJsonValid(prop.GetString()) ? + new JsonElementValue(JsonNode.Parse(prop.GetString() ?? string.Empty).Deserialize()) : + new JsonElementValue(prop); + + return r; + } + + private static bool IsJsonValid(string json) + { + if (string.IsNullOrWhiteSpace(json)) + return false; + + try + { + using var jsonDoc = JsonDocument.Parse(json); + return true; + } + catch (JsonException) + { + return false; + } + } + + public IArrayValueEnumerator EnumerateArray() + { + return new ArrayEnumerator(_element.EnumerateArray()); + } + + public IObjectValueEnumerator EnumerateObject() + { + return new ObjectEnumerator(_element.EnumerateObject()); + } + + public IExpression GetExpression() + { + throw new InvalidOperationException("Not an expression"); + } + + public override string ToString() + { + var s = JsonSerializer.Serialize(_element); + return s; + } + }; + + internal readonly struct DoubleValue : IValue + { + private readonly double _value; + + internal DoubleValue(double value) + { + _value = value; + } + + public JmesPathType Type => JmesPathType.Number; + + public IValue this[int index] => throw new InvalidOperationException(); + + public int GetArrayLength() { throw new InvalidOperationException(); } + + public string GetString() + { + throw new InvalidOperationException(); + } + + public bool TryGetDecimal(out decimal value) + { + if (!(double.IsNaN(_value) || double.IsInfinity(_value)) && _value is >= (double)decimal.MinValue and <= (double)decimal.MaxValue) + { + value = decimal.MinValue; + return false; + } + + value = new decimal(_value); + return true; + } + + public bool TryGetDouble(out double value) + { + value = _value; + return true; + } + + public bool TryGetProperty(string propertyName, out IValue property) + { + throw new InvalidOperationException(); + } + + public IArrayValueEnumerator EnumerateArray() + { + throw new InvalidOperationException(); + } + + public IObjectValueEnumerator EnumerateObject() + { + throw new InvalidOperationException(); + } + + public IExpression GetExpression() + { + throw new InvalidOperationException("Not an expression"); + } + + public override string ToString() + { + var s = JsonSerializer.Serialize(_value); + return s; + } + } + + internal readonly struct DecimalValue : IValue + { + private readonly decimal _value; + + internal DecimalValue(decimal value) + { + _value = value; + } + + public JmesPathType Type => JmesPathType.Number; + + public IValue this[int index] => throw new InvalidOperationException(); + + public int GetArrayLength() { throw new InvalidOperationException(); } + + public string GetString() + { + throw new InvalidOperationException(); + } + + public bool TryGetDecimal(out decimal value) + { + value = _value; + return true; + } + + public bool TryGetDouble(out double value) + { + value = (double)_value; + return true; + } + + public bool TryGetProperty(string propertyName, out IValue property) + { + throw new InvalidOperationException(); + } + + public IArrayValueEnumerator EnumerateArray() + { + throw new InvalidOperationException(); + } + + public IObjectValueEnumerator EnumerateObject() + { + throw new InvalidOperationException(); + } + + public IExpression GetExpression() + { + throw new InvalidOperationException("Not an expression"); + } + + public override string ToString() + { + var s = JsonSerializer.Serialize(_value); + return s; + } + } + + internal readonly struct StringValue : IValue + { + private readonly string _value; + + internal StringValue(string value) + { + _value = value; + } + + public JmesPathType Type => JmesPathType.String; + + public IValue this[int index] => throw new InvalidOperationException(); + + public int GetArrayLength() { throw new InvalidOperationException(); } + + public string GetString() + { + return _value; + } + + public bool TryGetDecimal(out decimal value) + { + throw new InvalidOperationException(); + } + + public bool TryGetDouble(out double value) + { + throw new InvalidOperationException(); + } + + public bool TryGetProperty(string propertyName, out IValue property) + { + throw new InvalidOperationException(); + } + + public IArrayValueEnumerator EnumerateArray() + { + throw new InvalidOperationException(); + } + + public IObjectValueEnumerator EnumerateObject() + { + throw new InvalidOperationException(); + } + + public IExpression GetExpression() + { + throw new InvalidOperationException("Not an expression"); + } + + public override string ToString() + { + var s = JsonSerializer.Serialize(_value); + return s; + } + } + + internal readonly struct TrueValue : IValue + { + public JmesPathType Type => JmesPathType.True; + + public IValue this[int index] => throw new InvalidOperationException(); + + public int GetArrayLength() { throw new InvalidOperationException(); } + + public string GetString() { throw new InvalidOperationException(); } + + public bool TryGetDecimal(out decimal value) + { + throw new InvalidOperationException(); + } + + public bool TryGetDouble(out double value) + { + throw new InvalidOperationException(); + } + + public bool TryGetProperty(string propertyName, out IValue property) + { + throw new InvalidOperationException(); + } + + public IArrayValueEnumerator EnumerateArray() + { + throw new InvalidOperationException(); + } + + public IObjectValueEnumerator EnumerateObject() + { + throw new InvalidOperationException(); + } + + public IExpression GetExpression() + { + throw new InvalidOperationException("Not an expression"); + } + + public override string ToString() + { + return "true"; + } + } + + internal readonly struct FalseValue : IValue + { + public JmesPathType Type => JmesPathType.False; + + public IValue this[int index] => throw new InvalidOperationException(); + + public int GetArrayLength() { throw new InvalidOperationException(); } + + public string GetString() { throw new InvalidOperationException(); } + + public bool TryGetDecimal(out decimal value) + { + throw new InvalidOperationException(); + } + + public bool TryGetDouble(out double value) + { + throw new InvalidOperationException(); + } + + public bool TryGetProperty(string propertyName, out IValue property) + { + throw new InvalidOperationException(); + } + + public IArrayValueEnumerator EnumerateArray() + { + throw new InvalidOperationException(); + } + + public IObjectValueEnumerator EnumerateObject() + { + throw new InvalidOperationException(); + } + + public IExpression GetExpression() + { + throw new InvalidOperationException("Not an expression"); + } + + public override string ToString() + { + return "false"; + } + } + + internal readonly struct NullValue : IValue + { + public JmesPathType Type => JmesPathType.Null; + + public IValue this[int index] => throw new InvalidOperationException(); + + public int GetArrayLength() { throw new InvalidOperationException(); } + + public string GetString() { throw new InvalidOperationException(); } + + public bool TryGetDecimal(out decimal value) + { + throw new InvalidOperationException(); + } + + public bool TryGetDouble(out double value) + { + throw new InvalidOperationException(); + } + + public bool TryGetProperty(string propertyName, out IValue property) + { + throw new InvalidOperationException(); + } + + public IArrayValueEnumerator EnumerateArray() + { + throw new InvalidOperationException(); + } + + public IObjectValueEnumerator EnumerateObject() + { + throw new InvalidOperationException(); + } + + public IExpression GetExpression() + { + throw new InvalidOperationException("Not an expression"); + } + + public override string ToString() + { + return "null"; + } + } + + internal readonly struct ArrayValue : IValue + { + private class ArrayEnumerator : IArrayValueEnumerator + { + private readonly IList _value; + private readonly System.Collections.IEnumerator _enumerator; + + public ArrayEnumerator(IList value) + { + _value = value; + _enumerator = value.GetEnumerator(); + } + + public bool MoveNext() + { + return _enumerator.MoveNext(); + } + + public void Reset() { _enumerator.Reset(); } + + void IDisposable.Dispose() {} + + public IValue Current => _enumerator.Current as IValue ?? throw new InvalidOperationException("Current cannot be null"); + + object System.Collections.IEnumerator.Current => Current; + + public IEnumerator GetEnumerator() + { + return _value.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + private readonly IList _value; + + internal ArrayValue(IList value) + { + _value = value; + } + + public JmesPathType Type => JmesPathType.Array; + + public IValue this[int index] => _value[index]; + + public int GetArrayLength() { return _value.Count; } + + public string GetString() + { + throw new InvalidOperationException(); + } + + public bool TryGetDecimal(out decimal value) + { + throw new InvalidOperationException(); + } + + public bool TryGetDouble(out double value) + { + throw new InvalidOperationException(); + } + + public bool TryGetProperty(string propertyName, out IValue property) + { + throw new InvalidOperationException(); + } + + public IArrayValueEnumerator EnumerateArray() + { + return new ArrayEnumerator(_value); + } + + public IObjectValueEnumerator EnumerateObject() + { + throw new InvalidOperationException(); + } + + public IExpression GetExpression() + { + throw new InvalidOperationException("Not an expression"); + } + + public override string ToString() + { + var buffer = new StringBuilder(); + buffer.Append('['); + var first = true; + foreach (var item in _value) + { + if (!first) + { + buffer.Append(','); + } + else + { + first = false; + } + buffer.Append(item.ToString()); + } + buffer.Append(']'); + return buffer.ToString(); + } + } + + internal readonly struct ObjectValue : IValue + { + private class ObjectEnumerator : IObjectValueEnumerator + { + private readonly IDictionary _value; + private readonly System.Collections.IEnumerator _enumerator; + + public ObjectEnumerator(IDictionary value) + { + _value = value; + _enumerator = value.GetEnumerator(); + } + + public bool MoveNext() + { + return _enumerator.MoveNext(); + } + + public void Reset() { _enumerator.Reset(); } + + void IDisposable.Dispose() {} + + public NameValuePair Current + { + get {var pair = (KeyValuePair)_enumerator.Current!; + return new NameValuePair(pair.Key, pair.Value); } + } + + object System.Collections.IEnumerator.Current => Current; + + public IEnumerator GetEnumerator() + { + return new ObjectEnumerator(_value); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + private readonly IDictionary _value; + + internal ObjectValue(IDictionary value) + { + _value = value; + } + + public JmesPathType Type => JmesPathType.Object; + + public IValue this[int index] => throw new InvalidOperationException(); + + public int GetArrayLength() + { + throw new InvalidOperationException(); + } + + public string GetString() + { + throw new InvalidOperationException(); + } + + public bool TryGetDecimal(out decimal value) + { + throw new InvalidOperationException(); + } + + public bool TryGetDouble(out double value) + { + throw new InvalidOperationException(); + } + + public bool TryGetProperty(string propertyName, out IValue property) + { + return _value.TryGetValue(propertyName, out property); + } + + public IArrayValueEnumerator EnumerateArray() + { + throw new InvalidOperationException(); + } + + public IObjectValueEnumerator EnumerateObject() + { + return new ObjectEnumerator(_value); + } + + public IExpression GetExpression() + { + throw new InvalidOperationException("Not an expression"); + } + + public override string ToString() + { + var buffer = new StringBuilder(); + buffer.Append('{'); + var first = true; + foreach (var property in _value) + { + if (!first) + { + buffer.Append(','); + } + else + { + first = false; + } + buffer.Append(JsonSerializer.Serialize(property.Key)); + buffer.Append(':'); + buffer.Append(property.Value.ToString()); + } + buffer.Append('}'); + return buffer.ToString(); + } + } + + internal readonly struct ExpressionValue : IValue + { + private readonly IExpression _expr; + + internal ExpressionValue(IExpression expr) + { + _expr = expr; + } + + public JmesPathType Type => JmesPathType.Expression; + + public IValue this[int index] => throw new InvalidOperationException(); + + public int GetArrayLength() { throw new InvalidOperationException(); } + + public string GetString() { throw new InvalidOperationException(); } + + public bool TryGetDecimal(out decimal value) + { + throw new InvalidOperationException(); + } + + public bool TryGetDouble(out double value) + { + throw new InvalidOperationException(); + } + + public bool TryGetProperty(string propertyName, out IValue property) + { + throw new InvalidOperationException(); + } + + public IArrayValueEnumerator EnumerateArray() + { + throw new InvalidOperationException(); + } + + public IObjectValueEnumerator EnumerateObject() + { + throw new InvalidOperationException(); + } + + public IExpression GetExpression() + { + return _expr; + } + + public override string ToString() + { + return "expression"; + } + }; +} diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/ValueComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/ValueComparer.cs new file mode 100644 index 00000000..bfd785a3 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/ValueComparer.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AWS.Lambda.Powertools.JMESPath +{ + /// + /// Compares two instances. + /// + internal sealed class ValueComparer : IComparer, System.Collections.IComparer + { + /// Gets a singleton instance of . This property is read-only. + public static ValueComparer Instance { get; } = new(); + + /// + /// Constructs a + /// + public ValueComparer() {} + + /// + /// Compares two instances. + /// + /// If the two instances have different data types, they are + /// compared according to their Type property, which gives this ordering: + /// + /// Undefined + /// Object + /// Array + /// String + /// Number + /// True + /// False + /// Null + /// + /// + /// If both instances are null, true, or false, they are equal. + /// + /// If both are strings, they are compared with the String.CompareTo method. + /// + /// If both are numbers, and both can be represented by a , + /// they are compared with the Decimal.CompareTo method, otherwise they are + /// compared as doubles. + /// + /// If both are objects, they are compared accoring to the following rules: + /// + ///
    + ///
  • Order each object's properties by name and compare sequentially. + /// The properties are compared first by name with the String.CompareTo method, then by value with
  • + ///
  • The first mismatching property defines which instance is less or greater than the other.
  • + ///
  • If the two sequences have no mismatching properties until one of them ends, and the other is longer, the shorter sequence is less than the other.
  • + ///
  • If the two sequences have no mismatching properties and have the same length, they are equal.
  • + ///
+ /// + /// If both are arrays, they are compared element wise with . + /// The first mismatching element defines which instance is less or greater than the other. + /// If the two arrays have no mismatching elements until one of them ends, and the other is longer, the shorter array is less than the other. + /// If the two arrays have no mismatching elements and have the same length, they are equal. + /// + ///
+ /// The first object of type cref="IValue"/> to compare. + /// The second object of type cref="IValue"/> to compare. + /// + /// + /// Unable to compare numbers as either or double (shouldn't happen.) + /// + public int Compare(IValue lhs, IValue rhs) + { + if (lhs.Type != rhs.Type) + return (int)lhs.Type - (int)rhs.Type; + + switch (lhs.Type) + { + case JmesPathType.Null: + case JmesPathType.True: + case JmesPathType.False: + return 0; + + case JmesPathType.Number: + { + if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) + { + return dec1.CompareTo(dec2); + } + else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) + { + return val1.CompareTo(val2); + } + else + { + throw new InvalidOperationException("Unable to compare numbers"); + } + } + + case JmesPathType.String: + return lhs.GetString().CompareTo(rhs.GetString()); + + case JmesPathType.Array: + { + var enumerator1 = lhs.EnumerateArray(); + var enumerator2 = rhs.EnumerateArray(); + var result1 = enumerator1.MoveNext(); + var result2 = enumerator2.MoveNext(); + while (result1 && result2) + { + var diff = Compare(enumerator1.Current, enumerator2.Current); + if (diff != 0) + { + return diff; + } + result1 = enumerator1.MoveNext(); + result2 = enumerator2.MoveNext(); + } + return result1 ? 1 : result2 ? -1 : 0; + } + + case JmesPathType.Object: + { + // OrderBy performs a stable sort (Note that supports duplicate property names) + var enumerator1 = lhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); + var enumerator2 = rhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); + + var result1 = enumerator1.MoveNext(); + var result2 = enumerator2.MoveNext(); + while (result1 && result2) + { + if (enumerator1.Current.Name != enumerator2.Current.Name) + { + return enumerator1.Current.Name.CompareTo(enumerator2.Current.Name); + } + var diff = Compare(enumerator1.Current.Value, enumerator2.Current.Value); + if (diff != 0) + { + return diff; + } + result1 = enumerator1.MoveNext(); + result2 = enumerator2.MoveNext(); + } + + return result1 ? 1 : result2 ? -1 : 0; + } + + default: + throw new InvalidOperationException(string.Format("Unknown JmesPathType {0}", lhs.Type)); + } + } + + int System.Collections.IComparer.Compare(object x, object y) + { + return Compare((IValue)x, (IValue)y); + } + } + + +} // namespace JsonCons.JsonPath diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/ValueEqualityComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/ValueEqualityComparer.cs new file mode 100644 index 00000000..78679579 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/ValueEqualityComparer.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AWS.Lambda.Powertools.JMESPath +{ + internal sealed class ValueEqualityComparer : IEqualityComparer + { + internal static ValueEqualityComparer Instance { get; } = new(); + + private int _maxHashDepth = 100; + + private ValueEqualityComparer() {} + + public bool Equals(IValue lhs, IValue rhs) + { + if (lhs.Type != rhs.Type) + return false; + + switch (lhs.Type) + { + case JmesPathType.Null: + case JmesPathType.True: + case JmesPathType.False: + return true; + + case JmesPathType.Number: + { + if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) + { + return dec1 == dec2; + } + else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) + { + return val1 == val2; + } + else + { + return false; + } + } + + case JmesPathType.String: + return lhs.GetString().Equals(rhs.GetString()); + + case JmesPathType.Array: + return lhs.EnumerateArray().SequenceEqual(rhs.EnumerateArray(), this); + + case JmesPathType.Object: + { + // OrderBy performs a stable sort (Note that IValue supports duplicate property names) + var enumerator1 = lhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); + var enumerator2 = rhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); + + var result1 = enumerator1.MoveNext(); + var result2 = enumerator2.MoveNext(); + while (result1 && result2) + { + if (enumerator1.Current.Name != enumerator2.Current.Name) + { + return false; + } + if (!(Equals(enumerator1.Current.Value,enumerator2.Current.Value))) + { + return false; + } + result1 = enumerator1.MoveNext(); + result2 = enumerator2.MoveNext(); + } + + return result1 == false && result2 == false; + } + + default: + throw new InvalidOperationException(string.Format("Unknown JmesPathType {0}", lhs.Type)); + } + } + + public int GetHashCode(IValue obj) + { + return ComputeHashCode(obj, 0); + } + + private int ComputeHashCode(IValue element, int depth) + { + var hashCode = element.Type.GetHashCode(); + + switch (element.Type) + { + case JmesPathType.Null: + case JmesPathType.True: + case JmesPathType.False: + break; + + case JmesPathType.Number: + { + element.TryGetDouble(out var dbl); + hashCode += 17 * dbl.GetHashCode(); + break; + } + + case JmesPathType.String: + hashCode += 17 * element.GetString().GetHashCode(); + break; + + case JmesPathType.Array: + if (depth < _maxHashDepth) + foreach (var item in element.EnumerateArray()) + hashCode += 17*ComputeHashCode(item, depth+1); + break; + + case JmesPathType.Object: + foreach (var property in element.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal)) + { + hashCode += 17*property.Name.GetHashCode(); + if (depth < _maxHashDepth) + hashCode += 17*ComputeHashCode(property.Value, depth+1); + } + break; + + default: + throw new InvalidOperationException(string.Format("Unknown JmesPathType {0}", element.Type)); + } + return hashCode; + } + } + + +} diff --git a/libraries/src/Directory.Packages.props b/libraries/src/Directory.Packages.props index e73f3288..d9c404bd 100644 --- a/libraries/src/Directory.Packages.props +++ b/libraries/src/Directory.Packages.props @@ -10,7 +10,6 @@ - diff --git a/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/Persistence/BasePersistenceStoreTests.cs b/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/Persistence/BasePersistenceStoreTests.cs index 0b0ff4ad..366aac77 100644 --- a/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/Persistence/BasePersistenceStoreTests.cs +++ b/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/Persistence/BasePersistenceStoreTests.cs @@ -545,7 +545,15 @@ private static APIGatewayProxyRequest LoadApiGatewayProxyRequest() }; var eventJson = File.ReadAllText("./resources/apigw_event.json"); - var request = JsonSerializer.Deserialize(eventJson, options); - return request!; + try + { + var request = JsonSerializer.Deserialize(eventJson, options); + return request!; + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } } } \ No newline at end of file diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/AWS.Lambda.Powertools.JMESPath.Tests.csproj b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/AWS.Lambda.Powertools.JMESPath.Tests.csproj new file mode 100644 index 00000000..2f385c45 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/AWS.Lambda.Powertools.JMESPath.Tests.csproj @@ -0,0 +1,94 @@ + + + + default + AWS.Lambda.Powertools.JMESpath.Tests + AWS.Lambda.Powertools.JMESpath.Tests + enable + enable + + false + true + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/GlobalUsings.cs b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/GlobalUsings.cs new file mode 100644 index 00000000..8c927eb7 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/GlobalUsings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/JmesPathTests.cs b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/JmesPathTests.cs new file mode 100644 index 00000000..52f5db99 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/JmesPathTests.cs @@ -0,0 +1,113 @@ +using System.Text.Json; +using AWS.Lambda.Powertools.JMESPath.Utilities; +using Xunit.Abstractions; + +namespace AWS.Lambda.Powertools.JMESPath.Tests; + +public class JmesPathTests +{ + private readonly ITestOutputHelper _output; + + public JmesPathTests(ITestOutputHelper output) + { + _output = output; + } + + [Theory] + [InlineData("test_files/basic.json")] + [InlineData("test_files/benchmarks.json")] + [InlineData("test_files/boolean.json")] + [InlineData("test_files/current.json")] + [InlineData("test_files/escape.json")] + [InlineData("test_files/filters.json")] + [InlineData("test_files/identifiers.json")] + [InlineData("test_files/indices.json")] + [InlineData("test_files/literal.json")] + [InlineData("test_files/multiselect.json")] + [InlineData("test_files/pipe.json")] + [InlineData("test_files/slice.json")] + [InlineData("test_files/unicode.json")] + [InlineData("test_files/syntax.json")] + [InlineData("test_files/wildcard.json")] + [InlineData("test_files/example.json")] + [InlineData("test_files/functions.json")] + [InlineData("test_files/test.json")] + [InlineData("test_files/apigw_event.json")] + [InlineData("test_files/apigw_event_2.json")] + public void RunJmesPathTests(string path) + { + _output.WriteLine($"Test {path}"); + + var text = File.ReadAllText(path); + var jsonOptions = new JsonDocumentOptions + { + CommentHandling = JsonCommentHandling.Skip + }; + using var doc = JsonDocument.Parse(text, jsonOptions); + + var testsEnumerable = doc.RootElement.EnumerateArray(); + var comparer = JsonElementEqualityComparer.Instance; + + foreach (var testGroup in testsEnumerable) + { + var given = testGroup.GetProperty("given"); + var testCases = testGroup.GetProperty("cases"); + var testCasesEnumerable = testCases.EnumerateArray(); + foreach (var testCase in testCasesEnumerable) + { + var exprElement = testCase.GetProperty("expression"); + + try + { + if (testCase.TryGetProperty("error", out var expected)) + { + var msg = expected.GetString(); + //Debug.WriteLine($"message: {msg}"); + if (msg != null && (msg.Equals("syntax") || msg.Equals("invalid-arity") || msg.Equals("unknown-function") || msg.Equals("invalid-value"))) + { + Assert.Throws(() => JsonTransformer.Parse(exprElement.ToString())); + } + else + { + var expr = JsonTransformer.Parse(exprElement.ToString()); + try + { + var result = expr.Transform(given); + using var nullValue = JsonDocument.Parse("null"); + var success = comparer.Equals(result.RootElement, nullValue.RootElement); + Assert.True(success); + } + catch (InvalidOperationException) + { } + } + } + else if (testCase.TryGetProperty("result", out expected)) + { + var expr = JsonTransformer.Parse(exprElement.ToString()); + var result = expr.Transform(given); + var success = comparer.Equals(result.RootElement, expected); + if (!success) + { + _output.WriteLine("File: {0}", path); + + _output.WriteLine($"Document: {given}"); + _output.WriteLine($"Path: {exprElement}"); + _output.WriteLine($"Expected: {JsonSerializer.Serialize(expected)}"); + _output.WriteLine($"Result: {JsonSerializer.Serialize(result)}"); + } + Assert.True(comparer.Equals(result.RootElement,expected)); + + } + } + catch (Exception e) + { + _output.WriteLine("File: {0}", path); + _output.WriteLine($"Document: {given}"); + _output.WriteLine($"Path: {exprElement}"); + _output.WriteLine("Error: {0}", e.Message); + throw; + } + } + } + } +} \ No newline at end of file diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/apigw_event.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/apigw_event.json new file mode 100644 index 00000000..159bc270 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/apigw_event.json @@ -0,0 +1,76 @@ +[ + { + "given": { + "body": "{\"message\": \"Lambda rocks\", \"id\": 43876123454654}", + "resource": "/{proxy+}", + "path": "/path/to/resource", + "httpMethod": "POST", + "isBase64Encoded": false, + "queryStringParameters": { + "foo": "bar" + }, + "pathParameters": { + "proxy": "/path/to/resource" + }, + "stageVariables": { + "baz": "qux" + }, + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String", + "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/path/to/resource", + "resourcePath": "/{proxy+}", + "httpMethod": "POST", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } + }, + "cases": [ + { + "expression": "body.id", + "result": 43876123454654 + }, + { + "expression": "powertools_json(body).id", + "result": 43876123454654 + } + ] + } +] \ No newline at end of file diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/apigw_event_2.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/apigw_event_2.json new file mode 100644 index 00000000..432697d9 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/apigw_event_2.json @@ -0,0 +1,54 @@ +[ + { + "given": { + "version": "2.0", + "routeKey": "ANY /createpayment", + "rawPath": "/createpayment", + "rawQueryString": "", + "headers": { + "Header1": "value1", + "Header2": "value2" + }, + "requestContext": { + "accountId": "123456789012", + "apiId": "api-id", + "domainName": "id.execute-api.us-east-1.amazonaws.com", + "domainPrefix": "id", + "http": { + "method": "POST", + "path": "/createpayment", + "protocol": "HTTP/1.1", + "sourceIp": "ip", + "userAgent": "agent" + }, + "requestId": "id", + "routeKey": "ANY /createpayment", + "stage": "$default", + "time": "10/Feb/2021:13:40:43 +0000", + "timeEpoch": 1612964443723 + }, + "body": {"user_id":"xyz","product_id":"123456789"}, + "body64": "eyJ1c2VyX2lkIjoieHl6IiwicHJvZHVjdF9pZCI6IjEyMzQ1Njc4OSJ9", + "bodygzip": "H4sIAAAAAAAAA6tWKi1OLYrPTFGyUqqorFLSUSooyk8pTS6BCBkaGZuYmplbWCrVAgApzA/LKgAAAA==", + "isBase64Encoded": false + }, + "cases": [ + { + "expression": "body.[user_id,product_id]", + "result": ["xyz",123456789] + }, + { + "expression": "powertools_json(body).[user_id,product_id]", + "result": ["xyz",123456789] + }, + { + "expression": "powertools_base64(body64).[user_id,product_id]", + "result": ["xyz",123456789] + }, + { + "expression": "powertools_base64_gzip(bodygzip).[user_id,product_id]", + "result": ["xyz",123456789] + } + ] + } +] \ No newline at end of file diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/basic.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/basic.json new file mode 100644 index 00000000..d550e969 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/basic.json @@ -0,0 +1,96 @@ +[{ + "given": + {"foo": {"bar": {"baz": "correct"}}}, + "cases": [ + { + "expression": "foo", + "result": {"bar": {"baz": "correct"}} + }, + { + "expression": "foo.bar", + "result": {"baz": "correct"} + }, + { + "expression": "foo.bar.baz", + "result": "correct" + }, + { + "expression": "foo\n.\nbar\n.baz", + "result": "correct" + }, + { + "expression": "foo.bar.baz.bad", + "result": null + }, + { + "expression": "foo.bar.bad", + "result": null + }, + { + "expression": "foo.bad", + "result": null + }, + { + "expression": "bad", + "result": null + }, + { + "expression": "bad.morebad.morebad", + "result": null + } + ] +}, +{ + "given": + {"foo": {"bar": ["one", "two", "three"]}}, + "cases": [ + { + "expression": "foo", + "result": {"bar": ["one", "two", "three"]} + }, + { + "expression": "foo.bar", + "result": ["one", "two", "three"] + } + ] +}, +{ + "given": ["one", "two", "three"], + "cases": [ + { + "expression": "one", + "result": null + }, + { + "expression": "two", + "result": null + }, + { + "expression": "three", + "result": null + }, + { + "expression": "one.two", + "result": null + } + ] +}, +{ + "given": + {"foo": {"1": ["one", "two", "three"], "-1": "bar"}}, + "cases": [ + { + "expression": "foo.\"1\"", + "result": ["one", "two", "three"] + }, + { + "expression": "foo.\"1\"[0]", + "result": "one" + }, + { + "expression": "foo.\"-1\"", + "result": "bar" + } + ] +} +] diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/benchmarks.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/benchmarks.json new file mode 100644 index 00000000..024a5904 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/benchmarks.json @@ -0,0 +1,138 @@ +[ + { + "given": { + "long_name_for_a_field": true, + "a": { + "b": { + "c": { + "d": { + "e": { + "f": { + "g": { + "h": { + "i": { + "j": { + "k": { + "l": { + "m": { + "n": { + "o": { + "p": true + } + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "b": true, + "c": { + "d": true + } + }, + "cases": [ + { + "comment": "simple field", + "expression": "b", + "bench": "full" + }, + { + "comment": "simple subexpression", + "expression": "c.d", + "bench": "full" + }, + { + "comment": "deep field selection no match", + "expression": "a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s", + "bench": "full" + }, + { + "comment": "deep field selection", + "expression": "a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p", + "bench": "full" + }, + { + "comment": "simple or", + "expression": "not_there || b", + "bench": "full" + } + ] + }, + { + "given": { + "a":0,"b":1,"c":2,"d":3,"e":4,"f":5,"g":6,"h":7,"i":8,"j":9,"k":10, + "l":11,"m":12,"n":13,"o":14,"p":15,"q":16,"r":17,"s":18,"t":19,"u":20, + "v":21,"w":22,"x":23,"y":24,"z":25 + }, + "cases": [ + { + "comment": "deep ands", + "expression": "a && b && c && d && e && f && g && h && i && j && k && l && m && n && o && p && q && r && s && t && u && v && w && x && y && z", + "bench": "full" + }, + { + "comment": "deep ors", + "expression": "z || y || x || w || v || u || t || s || r || q || p || o || n || m || l || k || j || i || h || g || f || e || d || c || b || a", + "bench": "full" + }, + { + "comment": "lots of summing", + "expression": "sum([z, y, x, w, v, u, t, s, r, q, p, o, n, m, l, k, j, i, h, g, f, e, d, c, b, a])", + "bench": "full" + }, + { + "comment": "lots of function application", + "expression": "sum([z, sum([y, sum([x, sum([w, sum([v, sum([u, sum([t, sum([s, sum([r, sum([q, sum([p, sum([o, sum([n, sum([m, sum([l, sum([k, sum([j, sum([i, sum([h, sum([g, sum([f, sum([e, sum([d, sum([c, sum([b, a])])])])])])])])])])])])])])])])])])])])])])])])])", + "bench": "full" + }, + { + "comment": "lots of multi list", + "expression": "[z, y, x, w, v, u, t, s, r, q, p, o, n, m, l, k, j, i, h, g, f, e, d, c, b, a]", + "bench": "full" + } + ] + }, + { + "given": {}, + "cases": [ + { + "comment": "field 50", + "expression": "j49.j48.j47.j46.j45.j44.j43.j42.j41.j40.j39.j38.j37.j36.j35.j34.j33.j32.j31.j30.j29.j28.j27.j26.j25.j24.j23.j22.j21.j20.j19.j18.j17.j16.j15.j14.j13.j12.j11.j10.j9.j8.j7.j6.j5.j4.j3.j2.j1.j0", + "bench": "parse" + }, + { + "comment": "pipe 50", + "expression": "j49|j48|j47|j46|j45|j44|j43|j42|j41|j40|j39|j38|j37|j36|j35|j34|j33|j32|j31|j30|j29|j28|j27|j26|j25|j24|j23|j22|j21|j20|j19|j18|j17|j16|j15|j14|j13|j12|j11|j10|j9|j8|j7|j6|j5|j4|j3|j2|j1|j0", + "bench": "parse" + }, + { + "comment": "index 50", + "expression": "[49][48][47][46][45][44][43][42][41][40][39][38][37][36][35][34][33][32][31][30][29][28][27][26][25][24][23][22][21][20][19][18][17][16][15][14][13][12][11][10][9][8][7][6][5][4][3][2][1][0]", + "bench": "parse" + }, + { + "comment": "long raw string literal", + "expression": "'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz'", + "bench": "parse" + }, + { + "comment": "deep projection 104", + "expression": "a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*].a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*].a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*].a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*]", + "bench": "parse" + }, + { + "comment": "filter projection", + "expression": "foo[?bar > baz][?qux > baz]", + "bench": "parse" + } + ] + } +] diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/boolean.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/boolean.json new file mode 100644 index 00000000..60635acb --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/boolean.json @@ -0,0 +1,275 @@ +[ + { + "given": { + "outer": { + "foo": "foo", + "bar": "bar", + "baz": "baz" + } + }, + "cases": [ + { + "expression": "outer.foo || outer.bar", + "result": "foo" + }, + { + "expression": "outer.foo||outer.bar", + "result": "foo" + }, + { + "expression": "outer.bar || outer.baz", + "result": "bar" + }, + { + "expression": "outer.bar||outer.baz", + "result": "bar" + }, + { + "expression": "outer.bad || outer.foo", + "result": "foo" + }, + { + "expression": "outer.bad||outer.foo", + "result": "foo" + }, + { + "expression": "outer.foo || outer.bad", + "result": "foo" + }, + { + "expression": "outer.foo||outer.bad", + "result": "foo" + }, + { + "expression": "outer.bad || outer.alsobad", + "result": null + }, + { + "expression": "outer.bad||outer.alsobad", + "result": null + } + ] + }, + { + "given": { + "outer": { + "foo": "foo", + "bool": false, + "empty_list": [], + "empty_string": "" + } + }, + "cases": [ + { + "expression": "outer.empty_string || outer.foo", + "result": "foo" + }, + { + "expression": "outer.nokey || outer.bool || outer.empty_list || outer.empty_string || outer.foo", + "result": "foo" + } + ] + }, + { + "given": { + "True": true, + "False": false, + "Number": 5, + "EmptyList": [], + "Zero": 0 + }, + "cases": [ + { + "expression": "True && False", + "result": false + }, + { + "expression": "False && True", + "result": false + }, + { + "expression": "True && True", + "result": true + }, + { + "expression": "False && False", + "result": false + }, + { + "expression": "True && Number", + "result": 5 + }, + { + "expression": "Number && True", + "result": true + }, + { + "expression": "Number && False", + "result": false + }, + { + "expression": "Number && EmptyList", + "result": [] + }, + { + "expression": "Number && True", + "result": true + }, + { + "expression": "EmptyList && True", + "result": [] + }, + { + "expression": "EmptyList && False", + "result": [] + }, + { + "expression": "True || False", + "result": true + }, + { + "expression": "True || True", + "result": true + }, + { + "expression": "False || True", + "result": true + }, + { + "expression": "False || False", + "result": false + }, + { + "expression": "Number || EmptyList", + "result": 5 + }, + { + "expression": "Number || True", + "result": 5 + }, + { + "expression": "Number || True && False", + "result": 5 + }, + { + "expression": "(Number || True) && False", + "result": false + }, + { + "expression": "Number || (True && False)", + "result": 5 + }, + { + "expression": "!True", + "result": false + }, + { + "expression": "!False", + "result": true + }, + { + "expression": "!Number", + "result": false + }, + { + "expression": "!EmptyList", + "result": true + }, + { + "expression": "True && !False", + "result": true + }, + { + "expression": "True && !EmptyList", + "result": true + }, + { + "expression": "!False && !EmptyList", + "result": true + }, + { + "expression": "!(True && False)", + "result": true + }, + { + "expression": "!Zero", + "result": false + }, + { + "expression": "!!Zero", + "result": true + } + ] + }, + { + "given": { + "one": 1, + "two": 2, + "three": 3, + "emptylist": [], + "boolvalue": false + }, + "cases": [ + { + "expression": "one < two", + "result": true + }, + { + "expression": "one <= two", + "result": true + }, + { + "expression": "one == one", + "result": true + }, + { + "expression": "one == two", + "result": false + }, + { + "expression": "one > two", + "result": false + }, + { + "expression": "one >= two", + "result": false + }, + { + "expression": "one != two", + "result": true + }, + { + "expression": "emptylist < one", + "result": null + }, + { + "expression": "emptylist < nullvalue", + "result": null + }, + { + "expression": "emptylist < boolvalue", + "result": null + }, + { + "expression": "one < boolvalue", + "result": null + }, + { + "expression": "one < two && three > one", + "result": true + }, + { + "expression": "one < two || three > one", + "result": true + }, + { + "expression": "one < two || three < one", + "result": true + }, + { + "expression": "two < one || three < one", + "result": false + } + ] + } +] diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/current.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/current.json new file mode 100644 index 00000000..0c26248d --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/current.json @@ -0,0 +1,25 @@ +[ + { + "given": { + "foo": [{"name": "a"}, {"name": "b"}], + "bar": {"baz": "qux"} + }, + "cases": [ + { + "expression": "@", + "result": { + "foo": [{"name": "a"}, {"name": "b"}], + "bar": {"baz": "qux"} + } + }, + { + "expression": "@.bar", + "result": {"baz": "qux"} + }, + { + "expression": "@.foo[0]", + "result": {"name": "a"} + } + ] + } +] diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/escape.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/escape.json new file mode 100644 index 00000000..4a62d951 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/escape.json @@ -0,0 +1,46 @@ +[{ + "given": { + "foo.bar": "dot", + "foo bar": "space", + "foo\nbar": "newline", + "foo\"bar": "doublequote", + "c:\\\\windows\\path": "windows", + "/unix/path": "unix", + "\"\"\"": "threequotes", + "bar": {"baz": "qux"} + }, + "cases": [ + { + "expression": "\"foo.bar\"", + "result": "dot" + }, + { + "expression": "\"foo bar\"", + "result": "space" + }, + { + "expression": "\"foo\\nbar\"", + "result": "newline" + }, + { + "expression": "\"foo\\\"bar\"", + "result": "doublequote" + }, + { + "expression": "\"c:\\\\\\\\windows\\\\path\"", + "result": "windows" + }, + { + "expression": "\"/unix/path\"", + "result": "unix" + }, + { + "expression": "\"\\\"\\\"\\\"\"", + "result": "threequotes" + }, + { + "expression": "\"bar\".\"baz\"", + "result": "qux" + } + ] +}] diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/example.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/example.json new file mode 100644 index 00000000..871aa8dc --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/example.json @@ -0,0 +1,50 @@ +[ + { + "given": { + "_id": "63ba60670fe420f2fb346866", + "isActive": true, + "balance": "$2,285.51", + "age": 20, + "eyeColor": "blue", + "name": "Eva Sharpe", + "email": "evasharpe@zaggles.com", + "phone": "+1 (950) 479-2130", + "registered": "2023-01-08T08:07:44.1787922+00:00", + "latitude": 46.325291, + "longitude": 5.211461, + "friends": [ + { + "id": 0, + "name": "Nielsen Casey", + "age": 19 + }, + { + "id": 1, + "name": "Carlene Long", + "age": 38 + } + ] + }, + "cases": [ + { + "expression": "balance", + "result": "$2,285.51" + }, + { + "expression": "to_string(latitude)", + "result": "46.325291" + }, + { + "expression": "friends[*].name", + "result": [ + "Nielsen Casey", + "Carlene Long" + ] + }, + { + "expression": "{email: email, name: name}", + "result": {"email": "evasharpe@zaggles.com", "name": "Eva Sharpe"} + } + ] + } +] \ No newline at end of file diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/filters.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/filters.json new file mode 100644 index 00000000..b2141a4e --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/filters.json @@ -0,0 +1,468 @@ +[ + { + "given": {"foo": [{"name": "a"}, {"name": "b"}]}, + "cases": [ + { + "comment": "Matching a literal `a`", + "expression": "foo[?name == 'a']", + "result": [{"name": "a"}] + } + ] + }, + { + "given": {"foo": [0, 1], "bar": [2, 3]}, + "cases": [ + { + "comment": "Matching a literal `0`", + "expression": "*[?[0] == `0`]", + "result": [[], []] + } + ] + }, + { + "given": {"foo": [{"first": "foo", "last": "bar"}, + {"first": "foo", "last": "foo"}, + {"first": "foo", "last": "baz"}]}, + "cases": [ + { + "comment": "Matching an expression", + "expression": "foo[?first == last]", + "result": [{"first": "foo", "last": "foo"}] + }, + { + "comment": "Verify projection created from filter", + "expression": "foo[?first == last].first", + "result": ["foo"] + } + ] + }, + { + "given": {"foo": [{"age": 20}, + {"age": 25}, + {"age": 30}]}, + "cases": [ + { + "comment": "Greater than with a number", + "expression": "foo[?age > `25`]", + "result": [{"age": 30}] + }, + { + "expression": "foo[?age >= `25`]", + "result": [{"age": 25}, {"age": 30}] + }, + { + "comment": "Greater than with a number", + "expression": "foo[?age > `30`]", + "result": [] + }, + { + "comment": "Greater than with a number", + "expression": "foo[?age < `25`]", + "result": [{"age": 20}] + }, + { + "comment": "Greater than with a number", + "expression": "foo[?age <= `25`]", + "result": [{"age": 20}, {"age": 25}] + }, + { + "comment": "Greater than with a number", + "expression": "foo[?age < `20`]", + "result": [] + }, + { + "expression": "foo[?age == `20`]", + "result": [{"age": 20}] + }, + { + "expression": "foo[?age != `20`]", + "result": [{"age": 25}, {"age": 30}] + } + ] + }, + { + "given": {"foo": [{"top": {"name": "a"}}, + {"top": {"name": "b"}}]}, + "cases": [ + { + "comment": "Filter with subexpression", + "expression": "foo[?top.name == 'a']", + "result": [{"top": {"name": "a"}}] + } + ] + }, + { + "given": {"foo": [{"top": {"first": "foo", "last": "bar"}}, + {"top": {"first": "foo", "last": "foo"}}, + {"top": {"first": "foo", "last": "baz"}}]}, + "cases": [ + { + "comment": "Matching an expression", + "expression": "foo[?top.first == top.last]", + "result": [{"top": {"first": "foo", "last": "foo"}}] + }, + { + "comment": "Matching a JSON array", + "expression": "foo[?top == `{\"first\": \"foo\", \"last\": \"bar\"}`]", + "result": [{"top": {"first": "foo", "last": "bar"}}] + } + ] + }, + { + "given": {"foo": [ + {"key": true}, + {"key": false}, + {"key": 0}, + {"key": 1}, + {"key": [0]}, + {"key": {"bar": [0]}}, + {"key": null}, + {"key": [1]}, + {"key": {"a":2}} + ]}, + "cases": [ + { + "expression": "foo[?key == `true`]", + "result": [{"key": true}] + }, + { + "expression": "foo[?key == `false`]", + "result": [{"key": false}] + }, + { + "expression": "foo[?key == `0`]", + "result": [{"key": 0}] + }, + { + "expression": "foo[?key == `1`]", + "result": [{"key": 1}] + }, + { + "expression": "foo[?key == `[0]`]", + "result": [{"key": [0]}] + }, + { + "expression": "foo[?key == `{\"bar\": [0]}`]", + "result": [{"key": {"bar": [0]}}] + }, + { + "expression": "foo[?key == `null`]", + "result": [{"key": null}] + }, + { + "expression": "foo[?key == `[1]`]", + "result": [{"key": [1]}] + }, + { + "expression": "foo[?key == `{\"a\":2}`]", + "result": [{"key": {"a":2}}] + }, + { + "expression": "foo[?`true` == key]", + "result": [{"key": true}] + }, + { + "expression": "foo[?`false` == key]", + "result": [{"key": false}] + }, + { + "expression": "foo[?`0` == key]", + "result": [{"key": 0}] + }, + { + "expression": "foo[?`1` == key]", + "result": [{"key": 1}] + }, + { + "expression": "foo[?`[0]` == key]", + "result": [{"key": [0]}] + }, + { + "expression": "foo[?`{\"bar\": [0]}` == key]", + "result": [{"key": {"bar": [0]}}] + }, + { + "expression": "foo[?`null` == key]", + "result": [{"key": null}] + }, + { + "expression": "foo[?`[1]` == key]", + "result": [{"key": [1]}] + }, + { + "expression": "foo[?`{\"a\":2}` == key]", + "result": [{"key": {"a":2}}] + }, + { + "expression": "foo[?key != `true`]", + "result": [{"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, + {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] + }, + { + "expression": "foo[?key != `false`]", + "result": [{"key": true}, {"key": 0}, {"key": 1}, {"key": [0]}, + {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] + }, + { + "expression": "foo[?key != `0`]", + "result": [{"key": true}, {"key": false}, {"key": 1}, {"key": [0]}, + {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] + }, + { + "expression": "foo[?key != `1`]", + "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": [0]}, + {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] + }, + { + "expression": "foo[?key != `null`]", + "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, + {"key": {"bar": [0]}}, {"key": [1]}, {"key": {"a":2}}] + }, + { + "expression": "foo[?key != `[1]`]", + "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, + {"key": {"bar": [0]}}, {"key": null}, {"key": {"a":2}}] + }, + { + "expression": "foo[?key != `{\"a\":2}`]", + "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, + {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}] + }, + { + "expression": "foo[?`true` != key]", + "result": [{"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, + {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] + }, + { + "expression": "foo[?`false` != key]", + "result": [{"key": true}, {"key": 0}, {"key": 1}, {"key": [0]}, + {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] + }, + { + "expression": "foo[?`0` != key]", + "result": [{"key": true}, {"key": false}, {"key": 1}, {"key": [0]}, + {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] + }, + { + "expression": "foo[?`1` != key]", + "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": [0]}, + {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] + }, + { + "expression": "foo[?`null` != key]", + "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, + {"key": {"bar": [0]}}, {"key": [1]}, {"key": {"a":2}}] + }, + { + "expression": "foo[?`[1]` != key]", + "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, + {"key": {"bar": [0]}}, {"key": null}, {"key": {"a":2}}] + }, + { + "expression": "foo[?`{\"a\":2}` != key]", + "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, + {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}] + } + ] + }, + { + "given": {"reservations": [ + {"instances": [ + {"foo": 1, "bar": 2}, {"foo": 1, "bar": 3}, + {"foo": 1, "bar": 2}, {"foo": 2, "bar": 1}]}]}, + "cases": [ + { + "expression": "reservations[].instances[?bar==`1`]", + "result": [[{"foo": 2, "bar": 1}]] + }, + { + "expression": "reservations[*].instances[?bar==`1`]", + "result": [[{"foo": 2, "bar": 1}]] + }, + { + "expression": "reservations[].instances[?bar==`1`][]", + "result": [{"foo": 2, "bar": 1}] + } + ] + }, + { + "given": { + "baz": "other", + "foo": [ + {"bar": 1}, {"bar": 2}, {"bar": 3}, {"bar": 4}, {"bar": 1, "baz": 2} + ] + }, + "cases": [ + { + "expression": "foo[?bar==`1`].bar[0]", + "result": [] + } + ] + }, + { + "given": { + "foo": [ + {"a": 1, "b": {"c": "x"}}, + {"a": 1, "b": {"c": "y"}}, + {"a": 1, "b": {"c": "z"}}, + {"a": 2, "b": {"c": "z"}}, + {"a": 1, "baz": 2} + ] + }, + "cases": [ + { + "expression": "foo[?a==`1`].b.c", + "result": ["x", "y", "z"] + } + ] + }, + { + "given": {"foo": [{"name": "a"}, {"name": "b"}, {"name": "c"}]}, + "cases": [ + { + "comment": "Filter with or expression", + "expression": "foo[?name == 'a' || name == 'b']", + "result": [{"name": "a"}, {"name": "b"}] + }, + { + "expression": "foo[?name == 'a' || name == 'e']", + "result": [{"name": "a"}] + }, + { + "expression": "foo[?name == 'a' || name == 'b' || name == 'c']", + "result": [{"name": "a"}, {"name": "b"}, {"name": "c"}] + } + ] + }, + { + "given": {"foo": [{"a": 1, "b": 2}, {"a": 1, "b": 3}]}, + "cases": [ + { + "comment": "Filter with and expression", + "expression": "foo[?a == `1` && b == `2`]", + "result": [{"a": 1, "b": 2}] + }, + { + "expression": "foo[?a == `1` && b == `4`]", + "result": [] + } + ] + }, + { + "given": {"foo": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]}, + "cases": [ + { + "comment": "Filter with Or and And expressions", + "expression": "foo[?c == `3` || a == `1` && b == `4`]", + "result": [{"a": 1, "b": 2, "c": 3}] + }, + { + "expression": "foo[?b == `2` || a == `3` && b == `4`]", + "result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}] + }, + { + "expression": "foo[?a == `3` && b == `4` || b == `2`]", + "result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}] + }, + { + "expression": "foo[?(a == `3` && b == `4`) || b == `2`]", + "result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}] + }, + { + "expression": "foo[?((a == `3` && b == `4`)) || b == `2`]", + "result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}] + }, + { + "expression": "foo[?a == `3` && (b == `4` || b == `2`)]", + "result": [{"a": 3, "b": 4}] + }, + { + "expression": "foo[?a == `3` && ((b == `4` || b == `2`))]", + "result": [{"a": 3, "b": 4}] + } + ] + }, + { + "given": {"foo": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]}, + "cases": [ + { + "comment": "Verify precedence of or/and expressions", + "expression": "foo[?a == `1` || b ==`2` && c == `5`]", + "result": [{"a": 1, "b": 2, "c": 3}] + }, + { + "comment": "Parentheses can alter precedence", + "expression": "foo[?(a == `1` || b ==`2`) && c == `5`]", + "result": [] + }, + { + "comment": "Not expressions combined with and/or", + "expression": "foo[?!(a == `1` || b ==`2`)]", + "result": [{"a": 3, "b": 4}] + } + ] + }, + { + "given": { + "foo": [ + {"key": true}, + {"key": false}, + {"key": []}, + {"key": {}}, + {"key": [0]}, + {"key": {"a": "b"}}, + {"key": 0}, + {"key": 1}, + {"key": null}, + {"notkey": true} + ] + }, + "cases": [ + { + "comment": "Unary filter expression", + "expression": "foo[?key]", + "result": [ + {"key": true}, {"key": [0]}, {"key": {"a": "b"}}, + {"key": 0}, {"key": 1} + ] + }, + { + "comment": "Unary not filter expression", + "expression": "foo[?!key]", + "result": [ + {"key": false}, {"key": []}, {"key": {}}, + {"key": null}, {"notkey": true} + ] + }, + { + "comment": "Equality with null RHS", + "expression": "foo[?key == `null`]", + "result": [ + {"key": null}, {"notkey": true} + ] + } + ] + }, + { + "given": { + "foo": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + }, + "cases": [ + { + "comment": "Using @ in a filter expression", + "expression": "foo[?@ < `5`]", + "result": [0, 1, 2, 3, 4] + }, + { + "comment": "Using @ in a filter expression", + "expression": "foo[?`5` > @]", + "result": [0, 1, 2, 3, 4] + }, + { + "comment": "Using @ in a filter expression", + "expression": "foo[?@ == @]", + "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + } + ] + } +] diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/functions.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/functions.json new file mode 100644 index 00000000..d2ec9369 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/functions.json @@ -0,0 +1,829 @@ +[{ + "given": + { + "foo": -1, + "zero": 0, + "numbers": [-1, 3, 4, 5], + "array": [-1, 3, 4, 5, "a", "100"], + "strings": ["a", "b", "c"], + "decimals": [1.01, 1.2, -1.5], + "str": "Str", + "false": false, + "empty_list": [], + "empty_hash": {}, + "objects": {"foo": "bar", "bar": "baz"}, + "null_key": null + }, + "cases": [ + { + "expression": "abs(foo)", + "result": 1 + }, + { + "expression": "abs(foo)", + "result": 1 + }, + { + "expression": "abs(str)", + "error": "invalid-type" + }, + { + "expression": "abs(array[1])", + "result": 3 + }, + { + "expression": "abs(array[1])", + "result": 3 + }, + { + "expression": "abs(`false`)", + "error": "invalid-type" + }, + { + "expression": "abs(`-24`)", + "result": 24 + }, + { + "expression": "abs(`-24`)", + "result": 24 + }, + { + "expression": "abs(`1`, `2`)", + "error": "invalid-arity" + }, + { + "expression": "abs()", + "error": "invalid-arity" + }, + { + "expression": "unknown_function(`1`, `2`)", + "error": "unknown-function" + }, + { + "expression": "avg(numbers)", + "result": 2.75 + }, + { + "expression": "avg(array)", + "error": "invalid-type" + }, + { + "expression": "avg('abc')", + "error": "invalid-type" + }, + { + "expression": "avg(foo)", + "error": "invalid-type" + }, + { + "expression": "avg(@)", + "error": "invalid-type" + }, + { + "expression": "avg(strings)", + "error": "invalid-type" + }, + { + "expression": "avg(empty_list)", + "result": null + }, + { + "expression": "ceil(`1.2`)", + "result": 2 + }, + { + "expression": "ceil(decimals[0])", + "result": 2 + }, + { + "expression": "ceil(decimals[1])", + "result": 2 + }, + { + "expression": "ceil(decimals[2])", + "result": -1 + }, + { + "expression": "ceil('string')", + "error": "invalid-type" + }, + { + "expression": "contains('abc', 'a')", + "result": true + }, + { + "expression": "contains('abc', 'd')", + "result": false + }, + { + "expression": "contains(`false`, 'd')", + "error": "invalid-type" + }, + { + "expression": "contains(strings, 'a')", + "result": true + }, + { + "expression": "contains(decimals, `1.2`)", + "result": true + }, + { + "expression": "contains(decimals, `false`)", + "result": false + }, + { + "expression": "ends_with(str, 'r')", + "result": true + }, + { + "expression": "ends_with(str, 'tr')", + "result": true + }, + { + "expression": "ends_with(str, 'Str')", + "result": true + }, + { + "expression": "ends_with(str, 'SStr')", + "result": false + }, + { + "expression": "ends_with(str, 'foo')", + "result": false + }, + { + "expression": "ends_with(str, `0`)", + "error": "invalid-type" + }, + { + "expression": "floor(`1.2`)", + "result": 1 + }, + { + "expression": "floor('string')", + "error": "invalid-type" + }, + { + "expression": "floor(decimals[0])", + "result": 1 + }, + { + "expression": "floor(foo)", + "result": -1 + }, + { + "expression": "floor(str)", + "error": "invalid-type" + }, + { + "expression": "length('abc')", + "result": 3 + }, + { + "expression": "length('✓foo')", + "result": 4 + }, + { + "expression": "length('')", + "result": 0 + }, + { + "expression": "length(@)", + "result": 12 + }, + { + "expression": "length(strings[0])", + "result": 1 + }, + { + "expression": "length(str)", + "result": 3 + }, + { + "expression": "length(array)", + "result": 6 + }, + { + "expression": "length(objects)", + "result": 2 + }, + { + "expression": "length(`false`)", + "error": "invalid-type" + }, + { + "expression": "length(foo)", + "error": "invalid-type" + }, + { + "expression": "length(strings[0])", + "result": 1 + }, + { + "expression": "max(numbers)", + "result": 5 + }, + { + "expression": "max(decimals)", + "result": 1.2 + }, + { + "expression": "max(strings)", + "result": "c" + }, + { + "expression": "max(abc)", + "error": "invalid-type" + }, + { + "expression": "max(array)", + "error": "invalid-type" + }, + { + "expression": "max(decimals)", + "result": 1.2 + }, + { + "expression": "max(empty_list)", + "result": null + }, + { + "expression": "merge(`{}`)", + "result": {} + }, + { + "expression": "merge(`{}`, `{}`)", + "result": {} + }, + { + "expression": "merge(`{\"a\": 1}`, `{\"b\": 2}`)", + "result": {"a": 1, "b": 2} + }, + { + "expression": "merge(`{\"a\": 1}`, `{\"a\": 2}`)", + "result": {"a": 2} + }, + { + "expression": "merge(`{\"a\": 1, \"b\": 2}`, `{\"a\": 2, \"c\": 3}`, `{\"d\": 4}`)", + "result": {"a": 2, "b": 2, "c": 3, "d": 4} + }, + { + "expression": "min(numbers)", + "result": -1 + }, + { + "expression": "min(decimals)", + "result": -1.5 + }, + { + "expression": "min(abc)", + "error": "invalid-type" + }, + { + "expression": "min(array)", + "error": "invalid-type" + }, + { + "expression": "min(empty_list)", + "result": null + }, + { + "expression": "min(decimals)", + "result": -1.5 + }, + { + "expression": "min(strings)", + "result": "a" + }, + { + "expression": "type('abc')", + "result": "string" + }, + { + "expression": "type(`1.0`)", + "result": "number" + }, + { + "expression": "type(`2`)", + "result": "number" + }, + { + "expression": "type(`true`)", + "result": "boolean" + }, + { + "expression": "type(`false`)", + "result": "boolean" + }, + { + "expression": "type(`null`)", + "result": "null" + }, + { + "expression": "type(`[0]`)", + "result": "array" + }, + { + "expression": "type(`{\"a\": \"b\"}`)", + "result": "object" + }, + { + "expression": "type(@)", + "result": "object" + }, + { + "expression": "sort(keys(objects))", + "result": ["bar", "foo"] + }, + { + "expression": "keys(foo)", + "error": "invalid-type" + }, + { + "expression": "keys(strings)", + "error": "invalid-type" + }, + { + "expression": "keys(`false`)", + "error": "invalid-type" + }, + { + "expression": "sort(values(objects))", + "result": ["bar", "baz"] + }, + { + "expression": "keys(empty_hash)", + "result": [] + }, + { + "expression": "values(foo)", + "error": "invalid-type" + }, + { + "expression": "join(', ', strings)", + "result": "a, b, c" + }, + { + "expression": "join(', ', strings)", + "result": "a, b, c" + }, + { + "expression": "join(',', `[\"a\", \"b\"]`)", + "result": "a,b" + }, + { + "expression": "join(',', `[\"a\", 0]`)", + "error": "invalid-type" + }, + { + "expression": "join(', ', str)", + "error": "invalid-type" + }, + { + "expression": "join('|', strings)", + "result": "a|b|c" + }, + { + "expression": "join(`2`, strings)", + "error": "invalid-type" + }, + { + "expression": "join('|', decimals)", + "error": "invalid-type" + }, + { + "expression": "join('|', decimals[].to_string(@))", + "result": "1.01|1.2|-1.5" + }, + { + "expression": "join('|', empty_list)", + "result": "" + }, + { + "expression": "reverse(numbers)", + "result": [5, 4, 3, -1] + }, + { + "expression": "reverse(array)", + "result": ["100", "a", 5, 4, 3, -1] + }, + { + "expression": "reverse(`[]`)", + "result": [] + }, + { + "expression": "reverse('')", + "result": "" + }, + { + "expression": "reverse('hello world')", + "result": "dlrow olleh" + }, + { + "expression": "starts_with(str, 'S')", + "result": true + }, + { + "expression": "starts_with(str, 'St')", + "result": true + }, + { + "expression": "starts_with(str, 'Str')", + "result": true + }, + { + "expression": "starts_with(str, 'String')", + "result": false + }, + { + "expression": "starts_with(str, `0`)", + "error": "invalid-type" + }, + { + "expression": "sum(numbers)", + "result": 11 + }, + { + "expression": "sum(decimals)", + "result": 0.71 + }, + { + "expression": "sum(array)", + "error": "invalid-type" + }, + { + "expression": "sum(array[].to_number(@))", + "result": 111 + }, + { + "expression": "sum(`[]`)", + "result": 0 + }, + { + "expression": "to_array('foo')", + "result": ["foo"] + }, + { + "expression": "to_array(`0`)", + "result": [0] + }, + { + "expression": "to_array(objects)", + "result": [{"foo": "bar", "bar": "baz"}] + }, + { + "expression": "to_array(`[1, 2, 3]`)", + "result": [1, 2, 3] + }, + { + "expression": "to_array(false)", + "result": [false] + }, + { + "expression": "to_string('foo')", + "result": "foo" + }, + { + "expression": "to_string(`1.2`)", + "result": "1.2" + }, + { + "expression": "to_string(`[0, 1]`)", + "result": "[0,1]" + }, + { + "expression": "to_number('1.0')", + "result": 1.0 + }, + { + "expression": "to_number('1.1')", + "result": 1.1 + }, + { + "expression": "to_number('4')", + "result": 4 + }, + { + "expression": "to_number('notanumber')", + "result": null + }, + { + "expression": "to_number(`false`)", + "result": null + }, + { + "expression": "to_number(`null`)", + "result": null + }, + { + "expression": "to_number(`[0]`)", + "result": null + }, + { + "expression": "to_number(`{\"foo\": 0}`)", + "result": null + }, + { + "expression": "\"to_string\"(`1.0`)", + "error": "syntax" + }, + { + "expression": "sort(numbers)", + "result": [-1, 3, 4, 5] + }, + { + "expression": "sort(strings)", + "result": ["a", "b", "c"] + }, + { + "expression": "sort(decimals)", + "result": [-1.5, 1.01, 1.2] + }, + { + "expression": "sort(array)", + "error": "invalid-type" + }, + { + "expression": "sort(abc)", + "error": "invalid-type" + }, + { + "expression": "sort(empty_list)", + "result": [] + }, + { + "expression": "sort(@)", + "error": "invalid-type" + }, + { + "expression": "not_null(unknown_key, str)", + "result": "Str" + }, + { + "expression": "not_null(unknown_key, foo.bar, empty_list, str)", + "result": [] + }, + { + "expression": "not_null(unknown_key, null_key, empty_list, str)", + "result": [] + }, + { + "expression": "not_null(all, expressions, are_null)", + "result": null + }, + { + "expression": "not_null()", + "error": "invalid-arity" + }, + { + "comment": "function projection on single arg function", + "expression": "numbers[].to_string(@)", + "result": ["-1", "3", "4", "5"] + }, + { + "comment": "function projection on single arg function", + "expression": "array[].to_number(@)", + "result": [-1, 3, 4, 5, 100] + } + ] +}, { + "given": + { + "foo": [ + {"b": "b", "a": "a"}, + {"c": "c", "b": "b"}, + {"d": "d", "c": "c"}, + {"e": "e", "d": "d"}, + {"f": "f", "e": "e"} + ] + }, + "cases": [ + { + "comment": "function projection on variadic function", + "expression": "foo[].not_null(f, e, d, c, b, a)", + "result": ["b", "c", "d", "e", "f"] + } + ] +}, { + "given": + { + "people": [ + {"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"}, + {"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"}, + {"age": 30, "age_str": "30", "bool": true, "name": "c"}, + {"age": 50, "age_str": "50", "bool": false, "name": "d"}, + {"age": 10, "age_str": "10", "bool": true, "name": 3} + ] + }, + "cases": [ + { + "comment": "sort by field expression", + "expression": "sort_by(people, &age)", + "result": [ + {"age": 10, "age_str": "10", "bool": true, "name": 3}, + {"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"}, + {"age": 30, "age_str": "30", "bool": true, "name": "c"}, + {"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"}, + {"age": 50, "age_str": "50", "bool": false, "name": "d"} + ] + }, + { + "expression": "sort_by(people, &age_str)", + "result": [ + {"age": 10, "age_str": "10", "bool": true, "name": 3}, + {"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"}, + {"age": 30, "age_str": "30", "bool": true, "name": "c"}, + {"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"}, + {"age": 50, "age_str": "50", "bool": false, "name": "d"} + ] + }, + { + "comment": "sort by function expression", + "expression": "sort_by(people, &to_number(age_str))", + "result": [ + {"age": 10, "age_str": "10", "bool": true, "name": 3}, + {"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"}, + {"age": 30, "age_str": "30", "bool": true, "name": "c"}, + {"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"}, + {"age": 50, "age_str": "50", "bool": false, "name": "d"} + ] + }, + { + "comment": "function projection on sort_by function", + "expression": "sort_by(people, &age)[].name", + "result": [3, "a", "c", "b", "d"] + }, + { + "expression": "sort_by(people, &extra)", + "error": "invalid-type" + }, + { + "expression": "sort_by(people, &bool)", + "error": "invalid-type" + }, + { + "expression": "sort_by(people, &name)", + "error": "invalid-type" + }, + { + "expression": "sort_by(people, name)", + "error": "invalid-type" + }, + { + "expression": "sort_by(people, &age)[].extra", + "result": ["foo", "bar"] + }, + { + "expression": "sort_by(`[]`, &age)", + "result": [] + }, + { + "expression": "max_by(people, &age)", + "result": {"age": 50, "age_str": "50", "bool": false, "name": "d"} + }, + { + "expression": "max_by(people, &age_str)", + "result": {"age": 50, "age_str": "50", "bool": false, "name": "d"} + }, + { + "expression": "max_by(people, &bool)", + "error": "invalid-type" + }, + { + "expression": "max_by(people, &extra)", + "error": "invalid-type" + }, + { + "expression": "max_by(people, &to_number(age_str))", + "result": {"age": 50, "age_str": "50", "bool": false, "name": "d"} + }, + { + "expression": "min_by(people, &age)", + "result": {"age": 10, "age_str": "10", "bool": true, "name": 3} + }, + { + "expression": "min_by(people, &age_str)", + "result": {"age": 10, "age_str": "10", "bool": true, "name": 3} + }, + { + "expression": "min_by(people, &bool)", + "error": "invalid-type" + }, + { + "expression": "min_by(people, &extra)", + "error": "invalid-type" + }, + { + "expression": "min_by(people, &to_number(age_str))", + "result": {"age": 10, "age_str": "10", "bool": true, "name": 3} + } + ] +}, { + "given": + { + "people": [ + {"age": 10, "order": "1"}, + {"age": 10, "order": "2"}, + {"age": 10, "order": "3"}, + {"age": 10, "order": "4"}, + {"age": 10, "order": "5"}, + {"age": 10, "order": "6"}, + {"age": 10, "order": "7"}, + {"age": 10, "order": "8"}, + {"age": 10, "order": "9"}, + {"age": 10, "order": "10"}, + {"age": 10, "order": "11"} + ] + }, + "cases": [ + { + "comment": "stable sort order", + "expression": "sort_by(people, &age)", + "result": [ + {"age": 10, "order": "1"}, + {"age": 10, "order": "2"}, + {"age": 10, "order": "3"}, + {"age": 10, "order": "4"}, + {"age": 10, "order": "5"}, + {"age": 10, "order": "6"}, + {"age": 10, "order": "7"}, + {"age": 10, "order": "8"}, + {"age": 10, "order": "9"}, + {"age": 10, "order": "10"}, + {"age": 10, "order": "11"} + ] + } + ] +}, { + "given": + { + "people": [ + {"a": 10, "b": 1, "c": "z"}, + {"a": 10, "b": 2, "c": null}, + {"a": 10, "b": 3}, + {"a": 10, "b": 4, "c": "z"}, + {"a": 10, "b": 5, "c": null}, + {"a": 10, "b": 6}, + {"a": 10, "b": 7, "c": "z"}, + {"a": 10, "b": 8, "c": null}, + {"a": 10, "b": 9} + ], + "empty": [] + }, + "cases": [ + { + "expression": "map(&a, people)", + "result": [10, 10, 10, 10, 10, 10, 10, 10, 10] + }, + { + "expression": "map(&c, people)", + "result": ["z", null, null, "z", null, null, "z", null, null] + }, + { + "expression": "map(&a, badkey)", + "error": "invalid-type" + }, + { + "expression": "map(&foo, empty)", + "result": [] + } + ] +}, { + "given": { + "array": [ + { + "foo": {"bar": "yes1"} + }, + { + "foo": {"bar": "yes2"} + }, + { + "foo1": {"bar": "no"} + } + ]}, + "cases": [ + { + "expression": "map(&foo.bar, array)", + "result": ["yes1", "yes2", null] + }, + { + "expression": "map(&foo1.bar, array)", + "result": [null, null, "no"] + }, + { + "expression": "map(&foo.bar.baz, array)", + "result": [null, null, null] + } + ] +}, { + "given": { + "array": [[1, 2, 3, [4]], [5, 6, 7, [8, 9]]] + }, + "cases": [ + { + "expression": "map(&[], array)", + "result": [[1, 2, 3, 4], [5, 6, 7, 8, 9]] + } + ] +} +] diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/identifiers.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/identifiers.json new file mode 100644 index 00000000..7998a41a --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/identifiers.json @@ -0,0 +1,1377 @@ +[ + { + "given": { + "__L": true + }, + "cases": [ + { + "expression": "__L", + "result": true + } + ] + }, + { + "given": { + "!\r": true + }, + "cases": [ + { + "expression": "\"!\\r\"", + "result": true + } + ] + }, + { + "given": { + "Y_1623": true + }, + "cases": [ + { + "expression": "Y_1623", + "result": true + } + ] + }, + { + "given": { + "x": true + }, + "cases": [ + { + "expression": "x", + "result": true + } + ] + }, + { + "given": { + "\tF\uCebb": true + }, + "cases": [ + { + "expression": "\"\\tF\\uCebb\"", + "result": true + } + ] + }, + { + "given": { + " \t": true + }, + "cases": [ + { + "expression": "\" \\t\"", + "result": true + } + ] + }, + { + "given": { + " ": true + }, + "cases": [ + { + "expression": "\" \"", + "result": true + } + ] + }, + { + "given": { + "v2": true + }, + "cases": [ + { + "expression": "v2", + "result": true + } + ] + }, + { + "given": { + "\t": true + }, + "cases": [ + { + "expression": "\"\\t\"", + "result": true + } + ] + }, + { + "given": { + "_X": true + }, + "cases": [ + { + "expression": "_X", + "result": true + } + ] + }, + { + "given": { + "\t4\ud9da\udd15": true + }, + "cases": [ + { + "expression": "\"\\t4\\ud9da\\udd15\"", + "result": true + } + ] + }, + { + "given": { + "v24_W": true + }, + "cases": [ + { + "expression": "v24_W", + "result": true + } + ] + }, + { + "given": { + "H": true + }, + "cases": [ + { + "expression": "\"H\"", + "result": true + } + ] + }, + { + "given": { + "\f": true + }, + "cases": [ + { + "expression": "\"\\f\"", + "result": true + } + ] + }, + { + "given": { + "E4": true + }, + "cases": [ + { + "expression": "\"E4\"", + "result": true + } + ] + }, + { + "given": { + "!": true + }, + "cases": [ + { + "expression": "\"!\"", + "result": true + } + ] + }, + { + "given": { + "tM": true + }, + "cases": [ + { + "expression": "tM", + "result": true + } + ] + }, + { + "given": { + " [": true + }, + "cases": [ + { + "expression": "\" [\"", + "result": true + } + ] + }, + { + "given": { + "R!": true + }, + "cases": [ + { + "expression": "\"R!\"", + "result": true + } + ] + }, + { + "given": { + "_6W": true + }, + "cases": [ + { + "expression": "_6W", + "result": true + } + ] + }, + { + "given": { + "\uaBA1\r": true + }, + "cases": [ + { + "expression": "\"\\uaBA1\\r\"", + "result": true + } + ] + }, + { + "given": { + "tL7": true + }, + "cases": [ + { + "expression": "tL7", + "result": true + } + ] + }, + { + "given": { + "<": true + }, + "cases": [ + { + "expression": "\">\"", + "result": true + } + ] + }, + { + "given": { + "hvu": true + }, + "cases": [ + { + "expression": "hvu", + "result": true + } + ] + }, + { + "given": { + "; !": true + }, + "cases": [ + { + "expression": "\"; !\"", + "result": true + } + ] + }, + { + "given": { + "hU": true + }, + "cases": [ + { + "expression": "hU", + "result": true + } + ] + }, + { + "given": { + "!I\n\/": true + }, + "cases": [ + { + "expression": "\"!I\\n\\/\"", + "result": true + } + ] + }, + { + "given": { + "\uEEbF": true + }, + "cases": [ + { + "expression": "\"\\uEEbF\"", + "result": true + } + ] + }, + { + "given": { + "U)\t": true + }, + "cases": [ + { + "expression": "\"U)\\t\"", + "result": true + } + ] + }, + { + "given": { + "fa0_9": true + }, + "cases": [ + { + "expression": "fa0_9", + "result": true + } + ] + }, + { + "given": { + "/": true + }, + "cases": [ + { + "expression": "\"/\"", + "result": true + } + ] + }, + { + "given": { + "Gy": true + }, + "cases": [ + { + "expression": "Gy", + "result": true + } + ] + }, + { + "given": { + "\b": true + }, + "cases": [ + { + "expression": "\"\\b\"", + "result": true + } + ] + }, + { + "given": { + "<": true + }, + "cases": [ + { + "expression": "\"<\"", + "result": true + } + ] + }, + { + "given": { + "\t": true + }, + "cases": [ + { + "expression": "\"\\t\"", + "result": true + } + ] + }, + { + "given": { + "\t&\\\r": true + }, + "cases": [ + { + "expression": "\"\\t&\\\\\\r\"", + "result": true + } + ] + }, + { + "given": { + "#": true + }, + "cases": [ + { + "expression": "\"#\"", + "result": true + } + ] + }, + { + "given": { + "B__": true + }, + "cases": [ + { + "expression": "B__", + "result": true + } + ] + }, + { + "given": { + "\nS \n": true + }, + "cases": [ + { + "expression": "\"\\nS \\n\"", + "result": true + } + ] + }, + { + "given": { + "Bp": true + }, + "cases": [ + { + "expression": "Bp", + "result": true + } + ] + }, + { + "given": { + ",\t;": true + }, + "cases": [ + { + "expression": "\",\\t;\"", + "result": true + } + ] + }, + { + "given": { + "B_q": true + }, + "cases": [ + { + "expression": "B_q", + "result": true + } + ] + }, + { + "given": { + "\/+\t\n\b!Z": true + }, + "cases": [ + { + "expression": "\"\\/+\\t\\n\\b!Z\"", + "result": true + } + ] + }, + { + "given": { + "\udadd\udfc7\\ueFAc": true + }, + "cases": [ + { + "expression": "\"\udadd\udfc7\\\\ueFAc\"", + "result": true + } + ] + }, + { + "given": { + ":\f": true + }, + "cases": [ + { + "expression": "\":\\f\"", + "result": true + } + ] + }, + { + "given": { + "\/": true + }, + "cases": [ + { + "expression": "\"\\/\"", + "result": true + } + ] + }, + { + "given": { + "_BW_6Hg_Gl": true + }, + "cases": [ + { + "expression": "_BW_6Hg_Gl", + "result": true + } + ] + }, + { + "given": { + "\udbcf\udc02": true + }, + "cases": [ + { + "expression": "\"\udbcf\udc02\"", + "result": true + } + ] + }, + { + "given": { + "zs1DC": true + }, + "cases": [ + { + "expression": "zs1DC", + "result": true + } + ] + }, + { + "given": { + "__434": true + }, + "cases": [ + { + "expression": "__434", + "result": true + } + ] + }, + { + "given": { + "\udb94\udd41": true + }, + "cases": [ + { + "expression": "\"\udb94\udd41\"", + "result": true + } + ] + }, + { + "given": { + "Z_5": true + }, + "cases": [ + { + "expression": "Z_5", + "result": true + } + ] + }, + { + "given": { + "z_M_": true + }, + "cases": [ + { + "expression": "z_M_", + "result": true + } + ] + }, + { + "given": { + "YU_2": true + }, + "cases": [ + { + "expression": "YU_2", + "result": true + } + ] + }, + { + "given": { + "_0": true + }, + "cases": [ + { + "expression": "_0", + "result": true + } + ] + }, + { + "given": { + "\b+": true + }, + "cases": [ + { + "expression": "\"\\b+\"", + "result": true + } + ] + }, + { + "given": { + "\"": true + }, + "cases": [ + { + "expression": "\"\\\"\"", + "result": true + } + ] + }, + { + "given": { + "D7": true + }, + "cases": [ + { + "expression": "D7", + "result": true + } + ] + }, + { + "given": { + "_62L": true + }, + "cases": [ + { + "expression": "_62L", + "result": true + } + ] + }, + { + "given": { + "\tK\t": true + }, + "cases": [ + { + "expression": "\"\\tK\\t\"", + "result": true + } + ] + }, + { + "given": { + "\n\\\f": true + }, + "cases": [ + { + "expression": "\"\\n\\\\\\f\"", + "result": true + } + ] + }, + { + "given": { + "I_": true + }, + "cases": [ + { + "expression": "I_", + "result": true + } + ] + }, + { + "given": { + "W_a0_": true + }, + "cases": [ + { + "expression": "W_a0_", + "result": true + } + ] + }, + { + "given": { + "BQ": true + }, + "cases": [ + { + "expression": "BQ", + "result": true + } + ] + }, + { + "given": { + "\tX$\uABBb": true + }, + "cases": [ + { + "expression": "\"\\tX$\\uABBb\"", + "result": true + } + ] + }, + { + "given": { + "Z9": true + }, + "cases": [ + { + "expression": "Z9", + "result": true + } + ] + }, + { + "given": { + "\b%\"\uda38\udd0f": true + }, + "cases": [ + { + "expression": "\"\\b%\\\"\uda38\udd0f\"", + "result": true + } + ] + }, + { + "given": { + "_F": true + }, + "cases": [ + { + "expression": "_F", + "result": true + } + ] + }, + { + "given": { + "!,": true + }, + "cases": [ + { + "expression": "\"!,\"", + "result": true + } + ] + }, + { + "given": { + "\"!": true + }, + "cases": [ + { + "expression": "\"\\\"!\"", + "result": true + } + ] + }, + { + "given": { + "Hh": true + }, + "cases": [ + { + "expression": "Hh", + "result": true + } + ] + }, + { + "given": { + "&": true + }, + "cases": [ + { + "expression": "\"&\"", + "result": true + } + ] + }, + { + "given": { + "9\r\\R": true + }, + "cases": [ + { + "expression": "\"9\\r\\\\R\"", + "result": true + } + ] + }, + { + "given": { + "M_k": true + }, + "cases": [ + { + "expression": "M_k", + "result": true + } + ] + }, + { + "given": { + "!\b\n\udb06\ude52\"\"": true + }, + "cases": [ + { + "expression": "\"!\\b\\n\udb06\ude52\\\"\\\"\"", + "result": true + } + ] + }, + { + "given": { + "6": true + }, + "cases": [ + { + "expression": "\"6\"", + "result": true + } + ] + }, + { + "given": { + "_7": true + }, + "cases": [ + { + "expression": "_7", + "result": true + } + ] + }, + { + "given": { + "0": true + }, + "cases": [ + { + "expression": "\"0\"", + "result": true + } + ] + }, + { + "given": { + "\\8\\": true + }, + "cases": [ + { + "expression": "\"\\\\8\\\\\"", + "result": true + } + ] + }, + { + "given": { + "b7eo": true + }, + "cases": [ + { + "expression": "b7eo", + "result": true + } + ] + }, + { + "given": { + "xIUo9": true + }, + "cases": [ + { + "expression": "xIUo9", + "result": true + } + ] + }, + { + "given": { + "5": true + }, + "cases": [ + { + "expression": "\"5\"", + "result": true + } + ] + }, + { + "given": { + "?": true + }, + "cases": [ + { + "expression": "\"?\"", + "result": true + } + ] + }, + { + "given": { + "sU": true + }, + "cases": [ + { + "expression": "sU", + "result": true + } + ] + }, + { + "given": { + "VH2&H\\\/": true + }, + "cases": [ + { + "expression": "\"VH2&H\\\\\\/\"", + "result": true + } + ] + }, + { + "given": { + "_C": true + }, + "cases": [ + { + "expression": "_C", + "result": true + } + ] + }, + { + "given": { + "_": true + }, + "cases": [ + { + "expression": "_", + "result": true + } + ] + }, + { + "given": { + "<\t": true + }, + "cases": [ + { + "expression": "\"<\\t\"", + "result": true + } + ] + }, + { + "given": { + "\uD834\uDD1E": true + }, + "cases": [ + { + "expression": "\"\\uD834\\uDD1E\"", + "result": true + } + ] + } +] diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/indices.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/indices.json new file mode 100644 index 00000000..aa03b35d --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/indices.json @@ -0,0 +1,346 @@ +[{ + "given": + {"foo": {"bar": ["zero", "one", "two"]}}, + "cases": [ + { + "expression": "foo.bar[0]", + "result": "zero" + }, + { + "expression": "foo.bar[1]", + "result": "one" + }, + { + "expression": "foo.bar[2]", + "result": "two" + }, + { + "expression": "foo.bar[3]", + "result": null + }, + { + "expression": "foo.bar[-1]", + "result": "two" + }, + { + "expression": "foo.bar[-2]", + "result": "one" + }, + { + "expression": "foo.bar[-3]", + "result": "zero" + }, + { + "expression": "foo.bar[-4]", + "result": null + } + ] +}, +{ + "given": + {"foo": [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}]}, + "cases": [ + { + "expression": "foo.bar", + "result": null + }, + { + "expression": "foo[0].bar", + "result": "one" + }, + { + "expression": "foo[1].bar", + "result": "two" + }, + { + "expression": "foo[2].bar", + "result": "three" + }, + { + "expression": "foo[3].notbar", + "result": "four" + }, + { + "expression": "foo[3].bar", + "result": null + }, + { + "expression": "foo[0]", + "result": {"bar": "one"} + }, + { + "expression": "foo[1]", + "result": {"bar": "two"} + }, + { + "expression": "foo[2]", + "result": {"bar": "three"} + }, + { + "expression": "foo[3]", + "result": {"notbar": "four"} + }, + { + "expression": "foo[4]", + "result": null + } + ] +}, +{ + "given": [ + "one", "two", "three" + ], + "cases": [ + { + "expression": "[0]", + "result": "one" + }, + { + "expression": "[1]", + "result": "two" + }, + { + "expression": "[2]", + "result": "three" + }, + { + "expression": "[-1]", + "result": "three" + }, + { + "expression": "[-2]", + "result": "two" + }, + { + "expression": "[-3]", + "result": "one" + } + ] +}, +{ + "given": {"reservations": [ + {"instances": [{"foo": 1}, {"foo": 2}]} + ]}, + "cases": [ + { + "expression": "reservations[].instances[].foo", + "result": [1, 2] + }, + { + "expression": "reservations[].instances[].bar", + "result": [] + }, + { + "expression": "reservations[].notinstances[].foo", + "result": [] + }, + { + "expression": "reservations[].notinstances[].foo", + "result": [] + } + ] +}, +{ + "given": {"reservations": [{ + "instances": [ + {"foo": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]}, + {"foo": [{"bar": 5}, {"bar": 6}, {"notbar": [7]}, {"bar": 8}]}, + {"foo": "bar"}, + {"notfoo": [{"bar": 20}, {"bar": 21}, {"notbar": [7]}, {"bar": 22}]}, + {"bar": [{"baz": [1]}, {"baz": [2]}, {"baz": [3]}, {"baz": [4]}]}, + {"baz": [{"baz": [1, 2]}, {"baz": []}, {"baz": []}, {"baz": [3, 4]}]}, + {"qux": [{"baz": []}, {"baz": [1, 2, 3]}, {"baz": [4]}, {"baz": []}]} + ], + "otherkey": {"foo": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]} + }, { + "instances": [ + {"a": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]}, + {"b": [{"bar": 5}, {"bar": 6}, {"notbar": [7]}, {"bar": 8}]}, + {"c": "bar"}, + {"notfoo": [{"bar": 23}, {"bar": 24}, {"notbar": [7]}, {"bar": 25}]}, + {"qux": [{"baz": []}, {"baz": [1, 2, 3]}, {"baz": [4]}, {"baz": []}]} + ], + "otherkey": {"foo": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]} + } + ]}, + "cases": [ + { + "expression": "reservations[].instances[].foo[].bar", + "result": [1, 2, 4, 5, 6, 8] + }, + { + "expression": "reservations[].instances[].foo[].baz", + "result": [] + }, + { + "expression": "reservations[].instances[].notfoo[].bar", + "result": [20, 21, 22, 23, 24, 25] + }, + { + "expression": "reservations[].instances[].notfoo[].notbar", + "result": [[7], [7]] + }, + { + "expression": "reservations[].notinstances[].foo", + "result": [] + }, + { + "expression": "reservations[].instances[].foo[].notbar", + "result": [3, [7]] + }, + { + "expression": "reservations[].instances[].bar[].baz", + "result": [[1], [2], [3], [4]] + }, + { + "expression": "reservations[].instances[].baz[].baz", + "result": [[1, 2], [], [], [3, 4]] + }, + { + "expression": "reservations[].instances[].qux[].baz", + "result": [[], [1, 2, 3], [4], [], [], [1, 2, 3], [4], []] + }, + { + "expression": "reservations[].instances[].qux[].baz[]", + "result": [1, 2, 3, 4, 1, 2, 3, 4] + } + ] +}, +{ + "given": { + "foo": [ + [["one", "two"], ["three", "four"]], + [["five", "six"], ["seven", "eight"]], + [["nine"], ["ten"]] + ] + }, + "cases": [ + { + "expression": "foo[]", + "result": [["one", "two"], ["three", "four"], ["five", "six"], + ["seven", "eight"], ["nine"], ["ten"]] + }, + { + "expression": "foo[][0]", + "result": ["one", "three", "five", "seven", "nine", "ten"] + }, + { + "expression": "foo[][1]", + "result": ["two", "four", "six", "eight"] + }, + { + "expression": "foo[][0][0]", + "result": [] + }, + { + "expression": "foo[][2][2]", + "result": [] + }, + { + "expression": "foo[][0][0][100]", + "result": [] + } + ] +}, +{ + "given": { + "foo": [{ + "bar": [ + { + "qux": 2, + "baz": 1 + }, + { + "qux": 4, + "baz": 3 + } + ] + }, + { + "bar": [ + { + "qux": 6, + "baz": 5 + }, + { + "qux": 8, + "baz": 7 + } + ] + } + ] + }, + "cases": [ + { + "expression": "foo", + "result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]}, + {"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}] + }, + { + "expression": "foo[]", + "result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]}, + {"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}] + }, + { + "expression": "foo[].bar", + "result": [[{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}], + [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]] + }, + { + "expression": "foo[].bar[]", + "result": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}, + {"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}] + }, + { + "expression": "foo[].bar[].baz", + "result": [1, 3, 5, 7] + } + ] +}, +{ + "given": { + "string": "string", + "hash": {"foo": "bar", "bar": "baz"}, + "number": 23, + "nullvalue": null + }, + "cases": [ + { + "expression": "string[]", + "result": null + }, + { + "expression": "hash[]", + "result": null + }, + { + "expression": "number[]", + "result": null + }, + { + "expression": "nullvalue[]", + "result": null + }, + { + "expression": "string[].foo", + "result": null + }, + { + "expression": "hash[].foo", + "result": null + }, + { + "expression": "number[].foo", + "result": null + }, + { + "expression": "nullvalue[].foo", + "result": null + }, + { + "expression": "nullvalue[].foo[].bar", + "result": null + } + ] +} +] diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/literal.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/literal.json new file mode 100644 index 00000000..b5ddbeda --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/literal.json @@ -0,0 +1,200 @@ +[ + { + "given": { + "foo": [{"name": "a"}, {"name": "b"}], + "bar": {"baz": "qux"} + }, + "cases": [ + { + "expression": "`\"foo\"`", + "result": "foo" + }, + { + "comment": "Interpret escaped unicode.", + "expression": "`\"\\u03a6\"`", + "result": "Φ" + }, + { + "expression": "`\"✓\"`", + "result": "✓" + }, + { + "expression": "`[1, 2, 3]`", + "result": [1, 2, 3] + }, + { + "expression": "`{\"a\": \"b\"}`", + "result": {"a": "b"} + }, + { + "expression": "`true`", + "result": true + }, + { + "expression": "`false`", + "result": false + }, + { + "expression": "`null`", + "result": null + }, + { + "expression": "`0`", + "result": 0 + }, + { + "expression": "`1`", + "result": 1 + }, + { + "expression": "`2`", + "result": 2 + }, + { + "expression": "`3`", + "result": 3 + }, + { + "expression": "`4`", + "result": 4 + }, + { + "expression": "`5`", + "result": 5 + }, + { + "expression": "`6`", + "result": 6 + }, + { + "expression": "`7`", + "result": 7 + }, + { + "expression": "`8`", + "result": 8 + }, + { + "expression": "`9`", + "result": 9 + }, + { + "comment": "Escaping a backtick in quotes", + "expression": "`\"foo\\`bar\"`", + "result": "foo`bar" + }, + { + "comment": "Double quote in literal", + "expression": "`\"foo\\\"bar\"`", + "result": "foo\"bar" + }, + { + "expression": "`\"1\\`\"`", + "result": "1`" + }, + { + "comment": "Multiple literal expressions with escapes", + "expression": "`\"\\\\\"`.{a:`\"b\"`}", + "result": {"a": "b"} + }, + { + "comment": "literal . identifier", + "expression": "`{\"a\": \"b\"}`.a", + "result": "b" + }, + { + "comment": "literal . identifier . identifier", + "expression": "`{\"a\": {\"b\": \"c\"}}`.a.b", + "result": "c" + }, + { + "comment": "literal . identifier bracket-expr", + "expression": "`[0, 1, 2]`[1]", + "result": 1 + } + ] + }, + { + "comment": "Literals", + "given": {"type": "object"}, + "cases": [ + { + "comment": "Literal with leading whitespace", + "expression": "` {\"foo\": true}`", + "result": {"foo": true} + }, + { + "comment": "Literal with trailing whitespace", + "expression": "`{\"foo\": true} `", + "result": {"foo": true} + }, + { + "comment": "Literal on RHS of subexpr not allowed", + "expression": "foo.`\"bar\"`", + "error": "syntax" + } + ] + }, + { + "comment": "Raw String Literals", + "given": {}, + "cases": [ + { + "expression": "'foo'", + "result": "foo" + }, + { + "expression": "' foo '", + "result": " foo " + }, + { + "expression": "'0'", + "result": "0" + }, + { + "expression": "'newline\n'", + "result": "newline\n" + }, + { + "expression": "'\n'", + "result": "\n" + }, + { + "expression": "'✓'", + "result": "✓" + }, + { + "expression": "'𝄞'", + "result": "𝄞" + }, + { + "expression": "' [foo] '", + "result": " [foo] " + }, + { + "expression": "'[foo]'", + "result": "[foo]" + }, + { + "comment": "Do not interpret escaped unicode.", + "expression": "'\\u03a6'", + "result": "\\u03a6" + }, + { + "comment": "Can escape the single quote", + "expression": "'foo\\'bar'", + "result": "foo'bar" + }, + { + "comment": "Backslash not followed by single quote is treated as any other character", + "expression": "'\\z'", + "result": "\\z" + }, + { + "comment": "Backslash not followed by single quote is treated as any other character", + "expression": "'\\\\'", + "result": "\\\\" + } + ] + } +] diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/multiselect.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/multiselect.json new file mode 100644 index 00000000..4f464822 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/multiselect.json @@ -0,0 +1,398 @@ +[{ + "given": { + "foo": { + "bar": "bar", + "baz": "baz", + "qux": "qux", + "nested": { + "one": { + "a": "first", + "b": "second", + "c": "third" + }, + "two": { + "a": "first", + "b": "second", + "c": "third" + }, + "three": { + "a": "first", + "b": "second", + "c": {"inner": "third"} + } + } + }, + "bar": 1, + "baz": 2, + "qux\"": 3 + }, + "cases": [ + { + "expression": "foo.{bar: bar}", + "result": {"bar": "bar"} + }, + { + "expression": "foo.{\"bar\": bar}", + "result": {"bar": "bar"} + }, + { + "expression": "foo.{\"foo.bar\": bar}", + "result": {"foo.bar": "bar"} + }, + { + "expression": "foo.{bar: bar, baz: baz}", + "result": {"bar": "bar", "baz": "baz"} + }, + { + "expression": "foo.{\"bar\": bar, \"baz\": baz}", + "result": {"bar": "bar", "baz": "baz"} + }, + { + "expression": "{\"baz\": baz, \"qux\\\"\": \"qux\\\"\"}", + "result": {"baz": 2, "qux\"": 3} + }, + { + "expression": "foo.{bar:bar,baz:baz}", + "result": {"bar": "bar", "baz": "baz"} + }, + { + "expression": "foo.{bar: bar,qux: qux}", + "result": {"bar": "bar", "qux": "qux"} + }, + { + "expression": "foo.{bar: bar, noexist: noexist}", + "result": {"bar": "bar", "noexist": null} + }, + { + "expression": "foo.{noexist: noexist, alsonoexist: alsonoexist}", + "result": {"noexist": null, "alsonoexist": null} + }, + { + "expression": "foo.badkey.{nokey: nokey, alsonokey: alsonokey}", + "result": null + }, + { + "expression": "foo.nested.*.{a: a,b: b}", + "result": [{"a": "first", "b": "second"}, + {"a": "first", "b": "second"}, + {"a": "first", "b": "second"}] + }, + { + "expression": "foo.nested.three.{a: a, cinner: c.inner}", + "result": {"a": "first", "cinner": "third"} + }, + { + "expression": "foo.nested.three.{a: a, c: c.inner.bad.key}", + "result": {"a": "first", "c": null} + }, + { + "expression": "foo.{a: nested.one.a, b: nested.two.b}", + "result": {"a": "first", "b": "second"} + }, + { + "expression": "{bar: bar, baz: baz}", + "result": {"bar": 1, "baz": 2} + }, + { + "expression": "{bar: bar}", + "result": {"bar": 1} + }, + { + "expression": "{otherkey: bar}", + "result": {"otherkey": 1} + }, + { + "expression": "{no: no, exist: exist}", + "result": {"no": null, "exist": null} + }, + { + "expression": "foo.[bar]", + "result": ["bar"] + }, + { + "expression": "foo.[bar,baz]", + "result": ["bar", "baz"] + }, + { + "expression": "foo.[bar,qux]", + "result": ["bar", "qux"] + }, + { + "expression": "foo.[bar,noexist]", + "result": ["bar", null] + }, + { + "expression": "foo.[noexist,alsonoexist]", + "result": [null, null] + } + ] +}, { + "given": { + "foo": {"bar": 1, "baz": [2, 3, 4]} + }, + "cases": [ + { + "expression": "foo.{bar:bar,baz:baz}", + "result": {"bar": 1, "baz": [2, 3, 4]} + }, + { + "expression": "foo.[bar,baz[0]]", + "result": [1, 2] + }, + { + "expression": "foo.[bar,baz[1]]", + "result": [1, 3] + }, + { + "expression": "foo.[bar,baz[2]]", + "result": [1, 4] + }, + { + "expression": "foo.[bar,baz[3]]", + "result": [1, null] + }, + { + "expression": "foo.[bar[0],baz[3]]", + "result": [null, null] + } + ] +}, { + "given": { + "foo": {"bar": 1, "baz": 2} + }, + "cases": [ + { + "expression": "foo.{bar: bar, baz: baz}", + "result": {"bar": 1, "baz": 2} + }, + { + "expression": "foo.[bar,baz]", + "result": [1, 2] + } + ] +}, { + "given": { + "foo": { + "bar": {"baz": [{"common": "first", "one": 1}, + {"common": "second", "two": 2}]}, + "ignoreme": 1, + "includeme": true + } + }, + "cases": [ + { + "expression": "foo.{bar: bar.baz[1],includeme: includeme}", + "result": {"bar": {"common": "second", "two": 2}, "includeme": true} + }, + { + "expression": "foo.{\"bar.baz.two\": bar.baz[1].two, includeme: includeme}", + "result": {"bar.baz.two": 2, "includeme": true} + }, + { + "expression": "foo.[includeme, bar.baz[*].common]", + "result": [true, ["first", "second"]] + }, + { + "expression": "foo.[includeme, bar.baz[*].none]", + "result": [true, []] + }, + { + "expression": "foo.[includeme, bar.baz[].common]", + "result": [true, ["first", "second"]] + } + ] +}, { + "given": { + "reservations": [{ + "instances": [ + {"id": "id1", + "name": "first"}, + {"id": "id2", + "name": "second"} + ]}, { + "instances": [ + {"id": "id3", + "name": "third"}, + {"id": "id4", + "name": "fourth"} + ]} + ]}, + "cases": [ + { + "expression": "reservations[*].instances[*].{id: id, name: name}", + "result": [[{"id": "id1", "name": "first"}, {"id": "id2", "name": "second"}], + [{"id": "id3", "name": "third"}, {"id": "id4", "name": "fourth"}]] + }, + { + "expression": "reservations[].instances[].{id: id, name: name}", + "result": [{"id": "id1", "name": "first"}, + {"id": "id2", "name": "second"}, + {"id": "id3", "name": "third"}, + {"id": "id4", "name": "fourth"}] + }, + { + "expression": "reservations[].instances[].[id, name]", + "result": [["id1", "first"], + ["id2", "second"], + ["id3", "third"], + ["id4", "fourth"]] + } + ] +}, +{ + "given": { + "foo": [{ + "bar": [ + { + "qux": 2, + "baz": 1 + }, + { + "qux": 4, + "baz": 3 + } + ] + }, + { + "bar": [ + { + "qux": 6, + "baz": 5 + }, + { + "qux": 8, + "baz": 7 + } + ] + } + ] + }, + "cases": [ + { + "expression": "foo", + "result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]}, + {"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}] + }, + { + "expression": "foo[]", + "result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]}, + {"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}] + }, + { + "expression": "foo[].bar", + "result": [[{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}], + [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]] + }, + { + "expression": "foo[].bar[]", + "result": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}, + {"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}] + }, + { + "expression": "foo[].bar[].[baz, qux]", + "result": [[1, 2], [3, 4], [5, 6], [7, 8]] + }, + { + "expression": "foo[].bar[].[baz]", + "result": [[1], [3], [5], [7]] + }, + { + "expression": "foo[].bar[].[baz, qux][]", + "result": [1, 2, 3, 4, 5, 6, 7, 8] + } + ] +}, +{ + "given": { + "foo": { + "baz": [ + { + "bar": "abc" + }, { + "bar": "def" + } + ], + "qux": ["zero"] + } + }, + "cases": [ + { + "expression": "foo.[baz[*].bar, qux[0]]", + "result": [["abc", "def"], "zero"] + } + ] +}, +{ + "given": { + "foo": { + "baz": [ + { + "bar": "a", + "bam": "b", + "boo": "c" + }, { + "bar": "d", + "bam": "e", + "boo": "f" + } + ], + "qux": ["zero"] + } + }, + "cases": [ + { + "expression": "foo.[baz[*].[bar, boo], qux[0]]", + "result": [[["a", "c" ], ["d", "f" ]], "zero"] + } + ] +}, +{ + "given": { + "foo": { + "baz": [ + { + "bar": "a", + "bam": "b", + "boo": "c" + }, { + "bar": "d", + "bam": "e", + "boo": "f" + } + ], + "qux": ["zero"] + } + }, + "cases": [ + { + "expression": "foo.[baz[*].not_there || baz[*].bar, qux[0]]", + "result": [["a", "d"], "zero"] + } + ] +}, +{ + "given": {"type": "object"}, + "cases": [ + { + "comment": "Nested multiselect", + "expression": "[[*],*]", + "result": [null, ["object"]] + } + ] +}, +{ + "given": [], + "cases": [ + { + "comment": "Nested multiselect", + "expression": "[[*]]", + "result": [[]] + }, + { + "comment": "Select on null", + "expression": "missing.{foo: bar}", + "result": null + } + ] +} +] diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/pipe.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/pipe.json new file mode 100644 index 00000000..b10c0a49 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/pipe.json @@ -0,0 +1,131 @@ +[{ + "given": { + "foo": { + "bar": { + "baz": "subkey" + }, + "other": { + "baz": "subkey" + }, + "other2": { + "baz": "subkey" + }, + "other3": { + "notbaz": ["a", "b", "c"] + }, + "other4": { + "notbaz": ["a", "b", "c"] + } + } + }, + "cases": [ + { + "expression": "foo.*.baz | [0]", + "result": "subkey" + }, + { + "expression": "foo.*.baz | [1]", + "result": "subkey" + }, + { + "expression": "foo.*.baz | [2]", + "result": "subkey" + }, + { + "expression": "foo.bar.* | [0]", + "result": "subkey" + }, + { + "expression": "foo.*.notbaz | [*]", + "result": [["a", "b", "c"], ["a", "b", "c"]] + }, + { + "expression": "{\"a\": foo.bar, \"b\": foo.other} | *.baz", + "result": ["subkey", "subkey"] + } + ] +}, { + "given": { + "foo": { + "bar": { + "baz": "one" + }, + "other": { + "baz": "two" + }, + "other2": { + "baz": "three" + }, + "other3": { + "notbaz": ["a", "b", "c"] + }, + "other4": { + "notbaz": ["d", "e", "f"] + } + } + }, + "cases": [ + { + "expression": "foo | bar", + "result": {"baz": "one"} + }, + { + "expression": "foo | bar | baz", + "result": "one" + }, + { + "expression": "foo|bar| baz", + "result": "one" + }, + { + "expression": "not_there | [0]", + "result": null + }, + { + "expression": "not_there | [0]", + "result": null + }, + { + "expression": "[foo.bar, foo.other] | [0]", + "result": {"baz": "one"} + }, + { + "expression": "{\"a\": foo.bar, \"b\": foo.other} | a", + "result": {"baz": "one"} + }, + { + "expression": "{\"a\": foo.bar, \"b\": foo.other} | b", + "result": {"baz": "two"} + }, + { + "expression": "foo.bam || foo.bar | baz", + "result": "one" + }, + { + "expression": "foo | not_there || bar", + "result": {"baz": "one"} + } + ] +}, { + "given": { + "foo": [{ + "bar": [{ + "baz": "one" + }, { + "baz": "two" + }] + }, { + "bar": [{ + "baz": "three" + }, { + "baz": "four" + }] + }] + }, + "cases": [ + { + "expression": "foo[*].bar[*] | [0][0]", + "result": {"baz": "one"} + } + ] +}] diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/slice.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/slice.json new file mode 100644 index 00000000..35947727 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/slice.json @@ -0,0 +1,187 @@ +[{ + "given": { + "foo": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + "bar": { + "baz": 1 + } + }, + "cases": [ + { + "expression": "bar[0:10]", + "result": null + }, + { + "expression": "foo[0:10:1]", + "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + }, + { + "expression": "foo[0:10]", + "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + }, + { + "expression": "foo[0:10:]", + "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + }, + { + "expression": "foo[0::1]", + "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + }, + { + "expression": "foo[0::]", + "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + }, + { + "expression": "foo[0:]", + "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + }, + { + "expression": "foo[:10:1]", + "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + }, + { + "expression": "foo[::1]", + "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + }, + { + "expression": "foo[:10:]", + "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + }, + { + "expression": "foo[::]", + "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + }, + { + "expression": "foo[:]", + "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + }, + { + "expression": "foo[1:9]", + "result": [1, 2, 3, 4, 5, 6, 7, 8] + }, + { + "expression": "foo[0:10:2]", + "result": [0, 2, 4, 6, 8] + }, + { + "expression": "foo[5:]", + "result": [5, 6, 7, 8, 9] + }, + { + "expression": "foo[5::2]", + "result": [5, 7, 9] + }, + { + "expression": "foo[::2]", + "result": [0, 2, 4, 6, 8] + }, + { + "expression": "foo[::-1]", + "result": [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] + }, + { + "expression": "foo[1::2]", + "result": [1, 3, 5, 7, 9] + }, + { + "expression": "foo[10:0:-1]", + "result": [9, 8, 7, 6, 5, 4, 3, 2, 1] + }, + { + "expression": "foo[10:5:-1]", + "result": [9, 8, 7, 6] + }, + { + "expression": "foo[8:2:-2]", + "result": [8, 6, 4] + }, + { + "expression": "foo[0:20]", + "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + }, + { + "expression": "foo[10:-20:-1]", + "result": [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] + }, + { + "expression": "foo[10:-20]", + "result": [] + }, + { + "expression": "foo[-4:-1]", + "result": [6, 7, 8] + }, + { + "expression": "foo[:-5:-1]", + "result": [9, 8, 7, 6] + }, + { + "expression": "foo[8:2:0]", + "error": "invalid-value" + }, + { + "expression": "foo[8:2:0:1]", + "error": "syntax" + }, + { + "expression": "foo[8:2&]", + "error": "syntax" + }, + { + "expression": "foo[2:a:3]", + "error": "syntax" + } + ] +}, { + "given": { + "foo": [{"a": 1}, {"a": 2}, {"a": 3}], + "bar": [{"a": {"b": 1}}, {"a": {"b": 2}}, + {"a": {"b": 3}}], + "baz": 50 + }, + "cases": [ + { + "expression": "foo[:2].a", + "result": [1, 2] + }, + { + "expression": "foo[:2].b", + "result": [] + }, + { + "expression": "foo[:2].a.b", + "result": [] + }, + { + "expression": "bar[::-1].a.b", + "result": [3, 2, 1] + }, + { + "expression": "bar[:2].a.b", + "result": [1, 2] + }, + { + "expression": "baz[:2].a", + "result": null + } + ] +}, { + "given": [{"a": 1}, {"a": 2}, {"a": 3}], + "cases": [ + { + "expression": "[:]", + "result": [{"a": 1}, {"a": 2}, {"a": 3}] + }, + { + "expression": "[:2].a", + "result": [1, 2] + }, + { + "expression": "[::-1].a", + "result": [3, 2, 1] + }, + { + "expression": "[:2].b", + "result": [] + } + ] +}] diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/syntax.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/syntax.json new file mode 100644 index 00000000..9318901f --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/syntax.json @@ -0,0 +1,678 @@ +[{ + "comment": "Dot syntax", + "given": {"type": "object"}, + "cases": [ + { + "expression": "foo.bar", + "result": null + }, + { + "expression": "foo.1", + "error": "syntax" + }, + { + "expression": "foo.-11", + "error": "syntax" + }, + { + "expression": "foo.", + "error": "syntax" + }, + { + "expression": ".foo", + "error": "syntax" + }, + { + "expression": "foo..bar", + "error": "syntax" + }, + { + "expression": "foo.bar.", + "error": "syntax" + }, + { + "expression": "foo[.]", + "error": "syntax" + } + ] +}, + { + "comment": "Simple token errors", + "given": {"type": "object"}, + "cases": [ + { + "expression": ".", + "error": "syntax" + }, + { + "expression": ":", + "error": "syntax" + }, + { + "expression": ",", + "error": "syntax" + }, + { + "expression": "]", + "error": "syntax" + }, + { + "expression": "[", + "error": "syntax" + }, + { + "expression": "}", + "error": "syntax" + }, + { + "expression": "{", + "error": "syntax" + }, + { + "expression": ")", + "error": "syntax" + }, + { + "expression": "(", + "error": "syntax" + }, + { + "expression": "((&", + "error": "syntax" + }, + { + "expression": "a[", + "error": "syntax" + }, + { + "expression": "a]", + "error": "syntax" + }, + { + "expression": "a][", + "error": "syntax" + }, + { + "expression": "!", + "error": "syntax" + }, + { + "expression": "@=", + "error": "syntax" + }, + { + "expression": "@``", + "error": "syntax" + } + ] + }, + { + "comment": "Boolean syntax errors", + "given": {"type": "object"}, + "cases": [ + { + "expression": "![!(!", + "error": "syntax" + } + ] + }, + { + "comment": "Paren syntax errors", + "given": {}, + "cases": [ + { + "comment": "missing closing paren", + "expression": "(@", + "error": "syntax" + } + ] + }, + { + "comment": "Function syntax errors", + "given": {}, + "cases": [ + { + "comment": "invalid start of function", + "expression": "@(foo)", + "error": "syntax" + }, + { + "comment": "function names cannot be quoted", + "expression": "\"foo\"(bar)", + "error": "syntax" + } + ] + }, + { + "comment": "Wildcard syntax", + "given": {"type": "object"}, + "cases": [ + { + "expression": "*", + "result": ["object"] + }, + { + "expression": "*.*", + "result": [] + }, + { + "expression": "*.foo", + "result": [] + }, + { + "expression": "*[0]", + "result": [] + }, + { + "expression": ".*", + "error": "syntax" + }, + { + "expression": "*foo", + "error": "syntax" + }, + { + "expression": "*0", + "error": "syntax" + }, + { + "expression": "foo[*]bar", + "error": "syntax" + }, + { + "expression": "foo[*]*", + "error": "syntax" + } + ] + }, + { + "comment": "Flatten syntax", + "given": {"type": "object"}, + "cases": [ + { + "expression": "[]", + "result": null + } + ] + }, + { + "comment": "Simple bracket syntax", + "given": {"type": "object"}, + "cases": [ + { + "expression": "[0]", + "result": null + }, + { + "expression": "[*]", + "result": null + }, + { + "expression": "*.[0]", + "error": "syntax" + }, + { + "expression": "*.[\"0\"]", + "result": [[null]] + }, + { + "expression": "[*].bar", + "result": null + }, + { + "expression": "[*][0]", + "result": null + }, + { + "expression": "foo[#]", + "error": "syntax" + }, + { + "comment": "missing rbracket for led wildcard index", + "expression": "led[*", + "error": "syntax" + } + ] + }, + { + "comment": "slice syntax", + "given": {}, + "cases": [ + { + "comment": "slice expected colon or rbracket", + "expression": "[:@]", + "error": "syntax" + }, + { + "comment": "slice has too many colons", + "expression": "[:::]", + "error": "syntax" + }, + { + "comment": "slice expected number", + "expression": "[:@:]", + "error": "syntax" + }, + { + "comment": "slice expected number of colon", + "expression": "[:1@]", + "error": "syntax" + } + ] + }, + { + "comment": "Multi-select list syntax", + "given": {"type": "object"}, + "cases": [ + { + "expression": "foo[0]", + "result": null + }, + { + "comment": "Valid multi-select of a list", + "expression": "foo[0, 1]", + "error": "syntax" + }, + { + "expression": "foo.[0]", + "error": "syntax" + }, + { + "expression": "foo.[*]", + "result": null + }, + { + "comment": "Multi-select of a list with trailing comma", + "expression": "foo[0, ]", + "error": "syntax" + }, + { + "comment": "Multi-select of a list with trailing comma and no close", + "expression": "foo[0,", + "error": "syntax" + }, + { + "comment": "Multi-select of a list with trailing comma and no close", + "expression": "foo.[a", + "error": "syntax" + }, + { + "comment": "Multi-select of a list with extra comma", + "expression": "foo[0,, 1]", + "error": "syntax" + }, + { + "comment": "Multi-select of a list using an identifier index", + "expression": "foo[abc]", + "error": "syntax" + }, + { + "comment": "Multi-select of a list using identifier indices", + "expression": "foo[abc, def]", + "error": "syntax" + }, + { + "comment": "Multi-select of a list using an identifier index", + "expression": "foo[abc, 1]", + "error": "syntax" + }, + { + "comment": "Multi-select of a list using an identifier index with trailing comma", + "expression": "foo[abc, ]", + "error": "syntax" + }, + { + "comment": "Valid multi-select of a hash using an identifier index", + "expression": "foo.[abc]", + "result": null + }, + { + "comment": "Valid multi-select of a hash", + "expression": "foo.[abc, def]", + "result": null + }, + { + "comment": "Multi-select of a hash using a numeric index", + "expression": "foo.[abc, 1]", + "error": "syntax" + }, + { + "comment": "Multi-select of a hash with a trailing comma", + "expression": "foo.[abc, ]", + "error": "syntax" + }, + { + "comment": "Multi-select of a hash with extra commas", + "expression": "foo.[abc,, def]", + "error": "syntax" + }, + { + "comment": "Multi-select of a hash using number indices", + "expression": "foo.[0, 1]", + "error": "syntax" + } + ] + }, + { + "comment": "Multi-select hash syntax", + "given": {"type": "object"}, + "cases": [ + { + "comment": "No key or value", + "expression": "a{}", + "error": "syntax" + }, + { + "comment": "No closing token", + "expression": "a{", + "error": "syntax" + }, + { + "comment": "Not a key value pair", + "expression": "a{foo}", + "error": "syntax" + }, + { + "comment": "Missing value and closing character", + "expression": "a{foo:", + "error": "syntax" + }, + { + "comment": "Missing closing character", + "expression": "a{foo: 0", + "error": "syntax" + }, + { + "comment": "Missing value", + "expression": "a{foo:}", + "error": "syntax" + }, + { + "comment": "Trailing comma and no closing character", + "expression": "a{foo: 0, ", + "error": "syntax" + }, + { + "comment": "Missing value with trailing comma", + "expression": "a{foo: ,}", + "error": "syntax" + }, + { + "comment": "Accessing Array using an identifier", + "expression": "a{foo: bar}", + "error": "syntax" + }, + { + "expression": "a{foo: 0}", + "error": "syntax" + }, + { + "comment": "Missing key-value pair", + "expression": "a.{}", + "error": "syntax" + }, + { + "comment": "Not a key-value pair", + "expression": "a.{foo}", + "error": "syntax" + }, + { + "comment": "Valid multi-select hash extraction", + "expression": "a.{foo: bar}", + "result": null + }, + { + "comment": "Valid multi-select hash extraction", + "expression": "a.{foo: bar, baz: bam}", + "result": null + }, + { + "comment": "Trailing comma", + "expression": "a.{foo: bar, }", + "error": "syntax" + }, + { + "comment": "Missing key in second key-value pair", + "expression": "a.{foo: bar, baz}", + "error": "syntax" + }, + { + "comment": "Missing value in second key-value pair", + "expression": "a.{foo: bar, baz:}", + "error": "syntax" + }, + { + "comment": "Trailing comma", + "expression": "a.{foo: bar, baz: bam, }", + "error": "syntax" + }, + { + "comment": "Nested multi select", + "expression": "{\"\\\\\":{\" \":*}}", + "result": {"\\": {" ": ["object"]}} + }, + { + "comment": "Missing closing } after a valid nud", + "expression": "{a: @", + "error": "syntax" + } + ] + }, + { + "comment": "Or expressions", + "given": {"type": "object"}, + "cases": [ + { + "expression": "foo || bar", + "result": null + }, + { + "expression": "foo ||", + "error": "syntax" + }, + { + "expression": "foo.|| bar", + "error": "syntax" + }, + { + "expression": " || foo", + "error": "syntax" + }, + { + "expression": "foo || || foo", + "error": "syntax" + }, + { + "expression": "foo.[a || b]", + "result": null + }, + { + "expression": "foo.[a ||]", + "error": "syntax" + }, + { + "expression": "\"foo", + "error": "syntax" + } + ] + }, + { + "comment": "Filter expressions", + "given": {"type": "object"}, + "cases": [ + { + "expression": "foo[?bar==`\"baz\"`]", + "result": null + }, + { + "expression": "foo[? bar == `\"baz\"` ]", + "result": null + }, + { + "expression": "foo[ ?bar==`\"baz\"`]", + "error": "syntax" + }, + { + "expression": "foo[?bar==]", + "error": "syntax" + }, + { + "expression": "foo[?==]", + "error": "syntax" + }, + { + "expression": "foo[?==bar]", + "error": "syntax" + }, + { + "expression": "foo[?bar==baz?]", + "error": "syntax" + }, + { + "expression": "foo[?a.b.c==d.e.f]", + "result": null + }, + { + "expression": "foo[?bar==`[0, 1, 2]`]", + "result": null + }, + { + "expression": "foo[?bar==`[\"a\", \"b\", \"c\"]`]", + "result": null + }, + { + "comment": "Literal char not escaped", + "expression": "foo[?bar==`[\"foo`bar\"]`]", + "error": "syntax" + }, + { + "comment": "Literal char escaped", + "expression": "foo[?bar==`[\"foo\\`bar\"]`]", + "result": null + }, + { + "comment": "Unknown comparator", + "expression": "foo[?bar<>baz]", + "error": "syntax" + }, + { + "comment": "Unknown comparator", + "expression": "foo[?bar^baz]", + "error": "syntax" + }, + { + "expression": "foo[bar==baz]", + "error": "syntax" + }, + { + "comment": "Quoted identifier in filter expression no spaces", + "expression": "[?\"\\\\\">`\"foo\"`]", + "result": null + }, + { + "comment": "Quoted identifier in filter expression with spaces", + "expression": "[?\"\\\\\" > `\"foo\"`]", + "result": null + } + ] + }, + { + "comment": "Filter expression errors", + "given": {"type": "object"}, + "cases": [ + { + "expression": "bar.`\"anything\"`", + "error": "syntax" + }, + { + "expression": "bar.baz.noexists.`\"literal\"`", + "error": "syntax" + }, + { + "comment": "Literal wildcard projection", + "expression": "foo[*].`\"literal\"`", + "error": "syntax" + }, + { + "expression": "foo[*].name.`\"literal\"`", + "error": "syntax" + }, + { + "expression": "foo[].name.`\"literal\"`", + "error": "syntax" + }, + { + "expression": "foo[].name.`\"literal\"`.`\"subliteral\"`", + "error": "syntax" + }, + { + "comment": "Projecting a literal onto an empty list", + "expression": "foo[*].name.noexist.`\"literal\"`", + "error": "syntax" + }, + { + "expression": "foo[].name.noexist.`\"literal\"`", + "error": "syntax" + }, + { + "expression": "twolen[*].`\"foo\"`", + "error": "syntax" + }, + { + "comment": "Two level projection of a literal", + "expression": "twolen[*].threelen[*].`\"bar\"`", + "error": "syntax" + }, + { + "comment": "Two level flattened projection of a literal", + "expression": "twolen[].threelen[].`\"bar\"`", + "error": "syntax" + }, + { + "comment": "expects closing ]", + "expression": "foo[? @ | @", + "error": "syntax" + } + ] + }, + { + "comment": "Identifiers", + "given": {"type": "object"}, + "cases": [ + { + "expression": "foo", + "result": null + }, + { + "expression": "\"foo\"", + "result": null + }, + { + "expression": "\"\\\\\"", + "result": null + }, + { + "expression": "\"\\u\"", + "error": "syntax" + } + ] + }, + { + "comment": "Combined syntax", + "given": [], + "cases": [ + { + "expression": "*||*|*|*", + "result": null + }, + { + "expression": "*[]||[*]", + "result": [] + }, + { + "expression": "[*.*]", + "result": [null] + } + ] + } +] diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/test.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/test.json new file mode 100644 index 00000000..aa8953c8 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/test.json @@ -0,0 +1,19 @@ +[ + {"given": + { + "people": [ + {"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"}, + {"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"}, + {"age": 30, "age_str": "30", "bool": true, "name": "c"}, + {"age": 50, "age_str": "50", "bool": false, "name": "d"}, + {"age": 10, "age_str": "10", "bool": true, "name": 3} + ] + }, + "cases": [ + { + "expression": "max_by(people, &age)", + "result": {"age": 50, "age_str": "50", "bool": false, "name": "d"} + } + ] + } +] diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/unicode.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/unicode.json new file mode 100644 index 00000000..6b07b0b6 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/unicode.json @@ -0,0 +1,38 @@ +[ + { + "given": {"foo": [{"✓": "✓"}, {"✓": "✗"}]}, + "cases": [ + { + "expression": "foo[].\"✓\"", + "result": ["✓", "✗"] + } + ] + }, + { + "given": {"☯": true}, + "cases": [ + { + "expression": "\"☯\"", + "result": true + } + ] + }, + { + "given": {"♪♫•*¨*•.¸¸❤¸¸.•*¨*•♫♪": true}, + "cases": [ + { + "expression": "\"♪♫•*¨*•.¸¸❤¸¸.•*¨*•♫♪\"", + "result": true + } + ] + }, + { + "given": {"☃": true}, + "cases": [ + { + "expression": "\"☃\"", + "result": true + } + ] + } +] diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/wildcard.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/wildcard.json new file mode 100644 index 00000000..3bcec302 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/wildcard.json @@ -0,0 +1,460 @@ +[{ + "given": { + "foo": { + "bar": { + "baz": "val" + }, + "other": { + "baz": "val" + }, + "other2": { + "baz": "val" + }, + "other3": { + "notbaz": ["a", "b", "c"] + }, + "other4": { + "notbaz": ["a", "b", "c"] + }, + "other5": { + "other": { + "a": 1, + "b": 1, + "c": 1 + } + } + } + }, + "cases": [ + { + "expression": "foo.*.baz", + "result": ["val", "val", "val"] + }, + { + "expression": "foo.bar.*", + "result": ["val"] + }, + { + "expression": "foo.*.notbaz", + "result": [["a", "b", "c"], ["a", "b", "c"]] + }, + { + "expression": "foo.*.notbaz[0]", + "result": ["a", "a"] + }, + { + "expression": "foo.*.notbaz[-1]", + "result": ["c", "c"] + } + ] +}, { + "given": { + "foo": { + "first-1": { + "second-1": "val" + }, + "first-2": { + "second-1": "val" + }, + "first-3": { + "second-1": "val" + } + } + }, + "cases": [ + { + "expression": "foo.*", + "result": [{"second-1": "val"}, {"second-1": "val"}, + {"second-1": "val"}] + }, + { + "expression": "foo.*.*", + "result": [["val"], ["val"], ["val"]] + }, + { + "expression": "foo.*.*.*", + "result": [[], [], []] + }, + { + "expression": "foo.*.*.*.*", + "result": [[], [], []] + } + ] +}, { + "given": { + "foo": { + "bar": "one" + }, + "other": { + "bar": "one" + }, + "nomatch": { + "notbar": "three" + } + }, + "cases": [ + { + "expression": "*.bar", + "result": ["one", "one"] + } + ] +}, { + "given": { + "top1": { + "sub1": {"foo": "one"} + }, + "top2": { + "sub1": {"foo": "one"} + } + }, + "cases": [ + { + "expression": "*", + "result": [{"sub1": {"foo": "one"}}, + {"sub1": {"foo": "one"}}] + }, + { + "expression": "*.sub1", + "result": [{"foo": "one"}, + {"foo": "one"}] + }, + { + "expression": "*.*", + "result": [[{"foo": "one"}], + [{"foo": "one"}]] + }, + { + "expression": "*.*.foo[]", + "result": ["one", "one"] + }, + { + "expression": "*.sub1.foo", + "result": ["one", "one"] + } + ] +}, +{ + "given": + {"foo": [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}]}, + "cases": [ + { + "expression": "foo[*].bar", + "result": ["one", "two", "three"] + }, + { + "expression": "foo[*].notbar", + "result": ["four"] + } + ] +}, +{ + "given": + [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}], + "cases": [ + { + "expression": "[*]", + "result": [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}] + }, + { + "expression": "[*].bar", + "result": ["one", "two", "three"] + }, + { + "expression": "[*].notbar", + "result": ["four"] + } + ] +}, +{ + "given": { + "foo": { + "bar": [ + {"baz": ["one", "two", "three"]}, + {"baz": ["four", "five", "six"]}, + {"baz": ["seven", "eight", "nine"]} + ] + } + }, + "cases": [ + { + "expression": "foo.bar[*].baz", + "result": [["one", "two", "three"], ["four", "five", "six"], ["seven", "eight", "nine"]] + }, + { + "expression": "foo.bar[*].baz[0]", + "result": ["one", "four", "seven"] + }, + { + "expression": "foo.bar[*].baz[1]", + "result": ["two", "five", "eight"] + }, + { + "expression": "foo.bar[*].baz[2]", + "result": ["three", "six", "nine"] + }, + { + "expression": "foo.bar[*].baz[3]", + "result": [] + } + ] +}, +{ + "given": { + "foo": { + "bar": [["one", "two"], ["three", "four"]] + } + }, + "cases": [ + { + "expression": "foo.bar[*]", + "result": [["one", "two"], ["three", "four"]] + }, + { + "expression": "foo.bar[0]", + "result": ["one", "two"] + }, + { + "expression": "foo.bar[0][0]", + "result": "one" + }, + { + "expression": "foo.bar[0][0][0]", + "result": null + }, + { + "expression": "foo.bar[0][0][0][0]", + "result": null + }, + { + "expression": "foo[0][0]", + "result": null + } + ] +}, +{ + "given": { + "foo": [ + {"bar": [{"kind": "basic"}, {"kind": "intermediate"}]}, + {"bar": [{"kind": "advanced"}, {"kind": "expert"}]}, + {"bar": "string"} + ] + + }, + "cases": [ + { + "expression": "foo[*].bar[*].kind", + "result": [["basic", "intermediate"], ["advanced", "expert"]] + }, + { + "expression": "foo[*].bar[0].kind", + "result": ["basic", "advanced"] + } + ] +}, +{ + "given": { + "foo": [ + {"bar": {"kind": "basic"}}, + {"bar": {"kind": "intermediate"}}, + {"bar": {"kind": "advanced"}}, + {"bar": {"kind": "expert"}}, + {"bar": "string"} + ] + }, + "cases": [ + { + "expression": "foo[*].bar.kind", + "result": ["basic", "intermediate", "advanced", "expert"] + } + ] +}, +{ + "given": { + "foo": [{"bar": ["one", "two"]}, {"bar": ["three", "four"]}, {"bar": ["five"]}] + }, + "cases": [ + { + "expression": "foo[*].bar[0]", + "result": ["one", "three", "five"] + }, + { + "expression": "foo[*].bar[1]", + "result": ["two", "four"] + }, + { + "expression": "foo[*].bar[2]", + "result": [] + } + ] +}, +{ + "given": { + "foo": [{"bar": []}, {"bar": []}, {"bar": []}] + }, + "cases": [ + { + "expression": "foo[*].bar[0]", + "result": [] + } + ] +}, +{ + "given": { + "foo": [["one", "two"], ["three", "four"], ["five"]] + }, + "cases": [ + { + "expression": "foo[*][0]", + "result": ["one", "three", "five"] + }, + { + "expression": "foo[*][1]", + "result": ["two", "four"] + } + ] +}, +{ + "given": { + "foo": [ + [ + ["one", "two"], ["three", "four"] + ], [ + ["five", "six"], ["seven", "eight"] + ], [ + ["nine"], ["ten"] + ] + ] + }, + "cases": [ + { + "expression": "foo[*][0]", + "result": [["one", "two"], ["five", "six"], ["nine"]] + }, + { + "expression": "foo[*][1]", + "result": [["three", "four"], ["seven", "eight"], ["ten"]] + }, + { + "expression": "foo[*][0][0]", + "result": ["one", "five", "nine"] + }, + { + "expression": "foo[*][1][0]", + "result": ["three", "seven", "ten"] + }, + { + "expression": "foo[*][0][1]", + "result": ["two", "six"] + }, + { + "expression": "foo[*][1][1]", + "result": ["four", "eight"] + }, + { + "expression": "foo[*][2]", + "result": [] + }, + { + "expression": "foo[*][2][2]", + "result": [] + }, + { + "expression": "bar[*]", + "result": null + }, + { + "expression": "bar[*].baz[*]", + "result": null + } + ] +}, +{ + "given": { + "string": "string", + "hash": {"foo": "bar", "bar": "baz"}, + "number": 23, + "nullvalue": null + }, + "cases": [ + { + "expression": "string[*]", + "result": null + }, + { + "expression": "hash[*]", + "result": null + }, + { + "expression": "number[*]", + "result": null + }, + { + "expression": "nullvalue[*]", + "result": null + }, + { + "expression": "string[*].foo", + "result": null + }, + { + "expression": "hash[*].foo", + "result": null + }, + { + "expression": "number[*].foo", + "result": null + }, + { + "expression": "nullvalue[*].foo", + "result": null + }, + { + "expression": "nullvalue[*].foo[*].bar", + "result": null + } + ] +}, +{ + "given": { + "string": "string", + "hash": {"foo": "val", "bar": "val"}, + "number": 23, + "array": [1, 2, 3], + "nullvalue": null + }, + "cases": [ + { + "expression": "string.*", + "result": null + }, + { + "expression": "hash.*", + "result": ["val", "val"] + }, + { + "expression": "number.*", + "result": null + }, + { + "expression": "array.*", + "result": null + }, + { + "expression": "nullvalue.*", + "result": null + } + ] +}, +{ + "given": { + "a": [0, 1, 2], + "b": [0, 1, 2] + }, + "cases": [ + { + "expression": "*[0]", + "result": [0, 0] + } + ] +} +] From 93ede40544891f57c9bd6086ab8982e048d91419 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Wed, 17 Apr 2024 16:31:53 +0100 Subject: [PATCH 02/28] cleanup, refactoring and tests --- .../BinaryOperator.cs | 35 ++- .../Expression.cs | 38 ++- .../Function.cs | 49 +++- .../InternalsVisibleTo.cs | 3 +- .../JmesPathParser.cs | 63 +++-- .../JsonTransformer.cs | 21 +- .../Operator.cs | 21 +- .../AWS.Lambda.Powertools.JMESPath/README.md | 44 ++- .../AWS.Lambda.Powertools.JMESPath/Slice.cs | 48 ++-- .../AWS.Lambda.Powertools.JMESPath/Token.cs | 22 +- .../UnaryOperator.cs | 32 ++- .../Utilities/JsonDocumentBuilder.cs | 55 ++-- .../Utilities/JsonElementComparer.cs | 40 ++- .../Utilities/JsonElementEqualityComparer.cs | 42 ++- .../Utilities/JsonFlattener.cs | 80 +++--- .../Utilities/JsonMergePatch.cs | 27 +- .../Utilities/JsonPatch.cs | 266 ++++++++++-------- .../Utilities/JsonPointer.cs | 47 ++-- .../Utilities/JsonPointerExtensions.cs | 135 +++++---- .../AWS.Lambda.Powertools.JMESPath/Value.cs | 27 +- .../ValueComparer.cs | 40 ++- .../ValueEqualityComparer.cs | 50 +++- .../GlobalUsings.cs | 15 + .../JmesPathTests.cs | 15 + 24 files changed, 782 insertions(+), 433 deletions(-) diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs index f2c911b0..86c04554 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs @@ -1,4 +1,17 @@ -using System; +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ namespace AWS.Lambda.Powertools.JMESPath { @@ -7,7 +20,7 @@ internal interface IBinaryOperator int PrecedenceLevel {get;} bool IsRightAssociative {get;} bool TryEvaluate(IValue lhs, IValue rhs, out IValue result); - }; + } internal abstract class BinaryOperator : IBinaryOperator { @@ -21,7 +34,7 @@ internal BinaryOperator(Operator oper) public bool IsRightAssociative => false; public abstract bool TryEvaluate(IValue lhs, IValue rhs, out IValue result); - }; + } internal sealed class OrOperator : BinaryOperator { @@ -47,7 +60,7 @@ public override string ToString() { return "OrOperator"; } - }; + } internal sealed class AndOperator : BinaryOperator { @@ -68,7 +81,7 @@ public override string ToString() { return "AndOperator"; } - }; + } internal sealed class EqOperator : BinaryOperator { @@ -90,7 +103,7 @@ public override string ToString() { return "EqOperator"; } - }; + } internal sealed class NeOperator : BinaryOperator { @@ -117,7 +130,7 @@ public override string ToString() { return "NeOperator"; } - }; + } internal sealed class LtOperator : BinaryOperator { @@ -160,7 +173,7 @@ public override string ToString() { return "LtOperator"; } - }; + } internal sealed class LteOperator : BinaryOperator { @@ -204,7 +217,7 @@ public override string ToString() { return "LteOperator"; } - }; + } internal sealed class GtOperator : BinaryOperator { @@ -247,7 +260,7 @@ public override string ToString() { return "GtOperator"; } - }; + } internal sealed class GteOperator : BinaryOperator { @@ -290,6 +303,6 @@ public override string ToString() { return "GteOperator"; } - }; + } } diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs index 7d993671..83ad6c1c 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs @@ -1,4 +1,18 @@ -using System; +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -91,7 +105,7 @@ public override string ToString() { return $"IdentifierSelector {_identifier}"; } - }; + } internal sealed class CurrentNode : BaseExpression { @@ -112,7 +126,7 @@ public override string ToString() { return "CurrentNode"; } - }; + } internal sealed class IndexSelector : BaseExpression { @@ -153,7 +167,7 @@ public override string ToString() { return $"Index Selector {_index}"; } - }; + } internal abstract class Projection : BaseExpression { @@ -190,7 +204,7 @@ internal bool TryApplyExpressions(DynamicResources resources, IValue current, ou } return true; } - }; + } internal sealed class ObjectProjection : Projection { @@ -230,7 +244,7 @@ public override string ToString() { return "ObjectProjection"; } - }; + } internal sealed class ListProjection : Projection { @@ -273,7 +287,7 @@ public override string ToString() { return "ListProjection"; } - }; + } internal sealed class FlattenProjection : Projection { @@ -338,7 +352,7 @@ public override string ToString() { return "FlattenProjection"; } - }; + } internal sealed class SliceProjection : Projection { @@ -426,7 +440,7 @@ public override string ToString() { return "SliceProjection"; } - }; + } internal sealed class FilterExpression : Projection { @@ -477,7 +491,7 @@ public override string ToString() { return "FilterExpression"; } - }; + } internal sealed class MultiSelectList : BaseExpression { @@ -517,7 +531,7 @@ public override string ToString() { return "MultiSelectList"; } - }; + } internal struct KeyExpressionPair { @@ -529,7 +543,7 @@ internal KeyExpressionPair(string key, Expression expression) Key = key; Expression = expression; } - }; + } internal sealed class MultiSelectHash : BaseExpression { diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Function.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Function.cs index 2ee2d28b..4eddb7dc 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Function.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Function.cs @@ -1,4 +1,19 @@ -using System; +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; @@ -70,7 +85,7 @@ internal interface IFunction { int? Arity { get; } bool TryEvaluate(DynamicResources resources, IList args, out IValue element); - }; + } internal abstract class BaseFunction : IFunction { @@ -82,7 +97,7 @@ internal BaseFunction(int? argCount) public int? Arity { get; } public abstract bool TryEvaluate(DynamicResources resources, IList args, out IValue element); - }; + } internal sealed class AbsFunction : BaseFunction { @@ -117,7 +132,7 @@ public override string ToString() { return "abs"; } - }; + } internal sealed class AvgFunction : BaseFunction { @@ -154,6 +169,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, result = new DoubleValue(dblVal / arg0.GetArrayLength()); return true; } + result = JsonConstants.Null; return false; } @@ -162,7 +178,7 @@ public override string ToString() { return "avg"; } - }; + } internal sealed class CeilFunction : BaseFunction { @@ -194,6 +210,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, result = new DoubleValue(Math.Ceiling(dblVal)); return true; } + result = JsonConstants.Null; return false; } @@ -202,7 +219,7 @@ public override string ToString() { return "ceil"; } - }; + } internal sealed class ContainsFunction : BaseFunction { @@ -266,7 +283,7 @@ public override string ToString() { return "contains"; } - }; + } internal sealed class EndsWithFunction : BaseFunction { @@ -308,7 +325,7 @@ public override string ToString() { return "ends_with"; } - }; + } internal sealed class FloorFunction : BaseFunction { @@ -340,6 +357,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, result = new DoubleValue(Math.Floor(dblVal)); return true; } + result = JsonConstants.Null; return false; } @@ -348,7 +366,7 @@ public override string ToString() { return "floor"; } - }; + } internal sealed class JoinFunction : BaseFunction { @@ -483,7 +501,7 @@ public override string ToString() { return "length"; } - }; + } internal sealed class MaxFunction : BaseFunction { @@ -1221,17 +1239,18 @@ public override bool TryEvaluate(DynamicResources resources, IList args, case JmesPathType.String: { var s = arg0.GetString(); - if (decimal.TryParse(s, out var dec)) + if (decimal.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out var dec)) { result = new DecimalValue(dec); return true; } - if (double.TryParse(s, out var dbl)) + if (double.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out var dbl)) { result = new DoubleValue(dbl); return true; } + result = JsonConstants.Null; return false; } @@ -1383,7 +1402,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); result = args[0]; - + //result = new JsonElementValue(JsonNode.Parse(args[0].GetString()).Deserialize()); return true; } @@ -1428,7 +1447,7 @@ public override string ToString() public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - + var compressedBytes = Convert.FromBase64String(args[0].GetString()); using var compressedStream = new MemoryStream(compressedBytes); @@ -1488,5 +1507,5 @@ internal bool TryGetFunction(string name, out IFunction func) { return _functions.TryGetValue(name, out func); } - }; + } } \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/InternalsVisibleTo.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/InternalsVisibleTo.cs index 652795a2..62746e87 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/InternalsVisibleTo.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/InternalsVisibleTo.cs @@ -15,4 +15,5 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("AWS.Lambda.Powertools.Idempotency")] \ No newline at end of file +[assembly: InternalsVisibleTo("AWS.Lambda.Powertools.Idempotency")] +[assembly: InternalsVisibleTo("AWS.Lambda.Powertools.JMESPath.Tests")] \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs index d7e0efbc..e8bde3c6 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs @@ -1,4 +1,19 @@ -using System; +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; using System.Collections.Generic; using System.Diagnostics; using System.Text; @@ -15,12 +30,12 @@ public sealed class JmesPathParseException : Exception /// /// The line in the JMESPath string where a parse error was detected. /// - public int LineNumber {get;} + private int LineNumber {get;} /// /// The column in the JMESPath string where a parse error was detected. /// - public int ColumnNumber {get;} + private int ColumnNumber {get;} internal JmesPathParseException(string message, int line, int column) : base(message) @@ -37,7 +52,7 @@ public override string ToString () { return $"{Message} at line {LineNumber} and column {ColumnNumber}"; } - }; + } internal enum JmesPathState { @@ -334,7 +349,7 @@ internal JsonTransformer Parse() throw new JmesPathParseException("Expected identifier", _line, _column); } break; - }; + } break; } @@ -386,7 +401,7 @@ internal JsonTransformer Parse() throw new JmesPathParseException("Expected identifier", _line, _column); } break; - }; + } break; } case JmesPathState.KeyExpr: @@ -506,7 +521,7 @@ internal JsonTransformer Parse() ++_index; ++_column; break; - }; + } break; case JmesPathState.UnquotedString: @@ -528,7 +543,7 @@ internal JsonTransformer Parse() _stateStack.Pop(); // unquotedString } break; - }; + } break; case JmesPathState.RawStringEscapeChar: @@ -730,7 +745,7 @@ internal JsonTransformer Parse() ++_index; ++_column; break; - }; + } break; case JmesPathState.Literal: @@ -777,7 +792,7 @@ internal JsonTransformer Parse() ++_index; ++_column; break; - }; + } break; case JmesPathState.Number: @@ -1109,7 +1124,7 @@ internal JsonTransformer Parse() throw new JmesPathParseException("Expected key", _line, _column); } break; - }; + } break; } case JmesPathState.CmpLtOrLte: @@ -1727,21 +1742,19 @@ private void PushToken(Token token) private uint AppendToCodepoint(uint cp, uint c) { cp *= 16; - if (c >= '0' && c <= '9') + switch (c) { - cp += c - '0'; - } - else if (c >= 'a' && c <= 'f') - { - cp += c - 'a' + 10; - } - else if (c >= 'A' && c <= 'F') - { - cp += c - 'A' + 10; - } - else - { - throw new JmesPathParseException("Invalid codepoint", _line, _column); + case >= '0' and <= '9': + cp += c - '0'; + break; + case >= 'a' and <= 'f': + cp += c - 'a' + 10; + break; + case >= 'A' and <= 'F': + cp += c - 'A' + 10; + break; + default: + throw new JmesPathParseException("Invalid codepoint", _line, _column); } return cp; } diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs index 4fc29685..56aaa176 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs @@ -1,4 +1,19 @@ -using System; +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; using System.Text.Json; namespace AWS.Lambda.Powertools.JMESPath @@ -93,7 +108,7 @@ public static JsonTransformer Parse(string jmesPath) return compiler.Parse(); } - private Expression _expr; + private readonly Expression _expr; internal JsonTransformer(Expression expr) { @@ -115,7 +130,7 @@ public JsonDocument Transform(JsonElement doc) { var resources = new DynamicResources(); _expr.TryEvaluate(resources, new JsonElementValue(doc), out var temp); - return JsonDocument.Parse(temp.ToString()); + return JsonDocument.Parse(temp.ToString() ?? string.Empty); } /// diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operator.cs index c0816e53..bff60479 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operator.cs @@ -1,4 +1,19 @@ -namespace AWS.Lambda.Powertools.JMESPath +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +namespace AWS.Lambda.Powertools.JMESPath { internal enum Operator { @@ -18,7 +33,7 @@ internal enum Operator internal static class OperatorTable { - static internal int PrecedenceLevel(Operator oper) + internal static int PrecedenceLevel(Operator oper) { switch (oper) { @@ -45,7 +60,7 @@ static internal int PrecedenceLevel(Operator oper) } } - static internal bool IsRightAssociative(Operator oper) + internal static bool IsRightAssociative(Operator oper) { switch (oper) { diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/README.md b/libraries/src/AWS.Lambda.Powertools.JMESPath/README.md index bb9ca308..66d2c222 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/README.md +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/README.md @@ -1 +1,43 @@ -JMESPath \ No newline at end of file +# Powertools JMESPath support + +JMESPath is a query language for JSON used by AWS CLI, AWS Python SDK, and Powertools for AWS Lambda. + With built-in JMESPath functions to easily deserialize common encoded JSON payloads in Lambda functions. + +## Key features + +- Deserialize JSON from JSON strings, base64, and compressed data +- Use JMESPath to extract and combine data recursively +- Provides commonly used JMESPath expression with popular event sources + +JMESPath allows you to transform a JsonDocument into another JsonDocument. + +For example, consider the JSON data + +```csharp + +string jsonString = """ +{ + "body": "{\"customerId\":\"dd4649e6-2484-4993-acb8-0f9123103394\"}", + "deeply_nested": [ + { + "some_data": [ + 1, + 2, + 3 + ] + } + ] +} +"""; + +using JsonDocument doc = JsonDocument.Parse(jsonString); + +string expr = "powertools_json(body).customerId"; +//also works for fetching and flattening deeply nested data +// string expr = "deeply_nested[*].some_data[]"; + +JsonDocument result = JsonTransformer.Transform(doc.RootElement, expr); + + + +``` \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Slice.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Slice.cs index 0d6fb21b..6d5dca86 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Slice.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Slice.cs @@ -1,4 +1,17 @@ -using System; +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ namespace AWS.Lambda.Powertools.JMESPath { @@ -18,36 +31,17 @@ public Slice(int? start, int? stop, int step) public int GetStart(int size) { - if (_start.HasValue) - { - var len = _start.Value >= 0 ? _start.Value : size + _start.Value; - return len <= size ? len : size; - } - else - { - if (Step >= 0) - { - return 0; - } - else - { - return size; - } - } + if (!_start.HasValue) return Step >= 0 ? 0 : size; + var len = _start.Value >= 0 ? _start.Value : size + _start.Value; + return len <= size ? len : size; } public int GetStop(int size) { - if (_stop.HasValue) - { - var len = _stop.Value >= 0 ? _stop.Value : size + _stop.Value; - return len <= size ? len : size; - } - else - { - return Step >= 0 ? size : -1; - } + if (!_stop.HasValue) return Step >= 0 ? size : -1; + var len = _stop.Value >= 0 ? _stop.Value : size + _stop.Value; + return len <= size ? len : size; } - }; + } } diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs index ab5d1b76..553f9140 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs @@ -1,4 +1,19 @@ -using System; +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; using System.Diagnostics; namespace AWS.Lambda.Powertools.JMESPath @@ -181,10 +196,7 @@ internal IExpression GetExpression() } public bool Equals(Token other) { - if (Type == other.Type) - return true; - else - return false; + return Type == other.Type; } public override string ToString() diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs index c39df7eb..c28c2246 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs @@ -1,4 +1,19 @@ -using System.Text.RegularExpressions; +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Text.RegularExpressions; namespace AWS.Lambda.Powertools.JMESPath { @@ -7,7 +22,7 @@ internal interface IUnaryOperator int PrecedenceLevel {get;} bool IsRightAssociative {get;} bool TryEvaluate(IValue elem, out IValue result); - }; + } internal abstract class UnaryOperator : IUnaryOperator { @@ -22,13 +37,13 @@ internal UnaryOperator(Operator oper) public bool IsRightAssociative {get;} public abstract bool TryEvaluate(IValue elem, out IValue result); - }; + } internal sealed class NotOperator : UnaryOperator { internal static NotOperator Instance { get; } = new(); - internal NotOperator() + private NotOperator() : base(Operator.Not) {} @@ -42,11 +57,11 @@ public override string ToString() { return "Not"; } - }; + } internal sealed class RegexOperator : UnaryOperator { - private Regex _regex; + private readonly Regex _regex; internal RegexOperator(Regex regex) : base(Operator.Not) @@ -56,7 +71,7 @@ internal RegexOperator(Regex regex) public override bool TryEvaluate(IValue val, out IValue result) { - if (!(val.Type == JmesPathType.String)) + if (val.Type != JmesPathType.String) { result = JsonConstants.Null; return false; // type error @@ -69,7 +84,6 @@ public override string ToString() { return "Regex"; } - }; - + } } diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonDocumentBuilder.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonDocumentBuilder.cs index cabfd2be..609f17e9 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonDocumentBuilder.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonDocumentBuilder.cs @@ -1,4 +1,19 @@ -using System; +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -8,11 +23,11 @@ namespace AWS.Lambda.Powertools.JMESPath.Utilities { internal class JsonDocumentBuilder { - internal static JsonDocumentBuilder Default { get; } = new(); + private static JsonDocumentBuilder Default { get; } = new(); internal JsonValueKind ValueKind {get;} - private object? _item; + private readonly object? _item; private IList GetList() { @@ -23,7 +38,8 @@ private IDictionary GetDictionary() { return _item as IDictionary ?? throw new InvalidOperationException("Item is null"); } - internal JsonDocumentBuilder() + + private JsonDocumentBuilder() : this(JsonValueKind.Null) { } @@ -210,7 +226,7 @@ internal int GetArrayLength() { throw new InvalidOperationException("This value's ValueKind is not Array."); } - return GetList().Count(); + return GetList().Count; } internal int GetObjectLength() @@ -219,7 +235,7 @@ internal int GetObjectLength() { throw new InvalidOperationException("This value's ValueKind is not Object."); } - return GetDictionary().Count(); + return GetDictionary().Count; } internal bool TryGetProperty(string name, out JsonDocumentBuilder value) @@ -228,12 +244,10 @@ internal bool TryGetProperty(string name, out JsonDocumentBuilder value) { throw new InvalidOperationException("This value's ValueKind is not Object."); } - if (ValueKind != JsonValueKind.Object) - { - value = Default; - return false; - } - return GetDictionary().TryGetValue(name, out value); + + if (ValueKind == JsonValueKind.Object) return GetDictionary().TryGetValue(name, out value); + value = Default; + return false; } public override string ToString() @@ -249,13 +263,13 @@ private void ToString(StringBuilder buffer) { case JsonValueKind.Array: { - buffer.Append("["); + buffer.Append('['); var first = true; foreach (var item in EnumerateArray()) { if (!first) { - buffer.Append(","); + buffer.Append(','); } else { @@ -263,28 +277,28 @@ private void ToString(StringBuilder buffer) } item.ToString(buffer); } - buffer.Append("]"); + buffer.Append(']'); break; } case JsonValueKind.Object: { - buffer.Append("{"); + buffer.Append('{'); var first = true; foreach (var property in EnumerateObject()) { if (!first) { - buffer.Append(","); + buffer.Append(','); } else { first = false; } buffer.Append(JsonSerializer.Serialize(property.Key)); - buffer.Append(":"); + buffer.Append(':'); property.Value.ToString(buffer); } - buffer.Append("}"); + buffer.Append('}'); break; } default: @@ -301,5 +315,4 @@ internal JsonDocument ToJsonDocument() return JsonDocument.Parse(json); } } - -} // namespace JsonCons.Utilities +} diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementComparer.cs index b40ab7c6..22d44c6c 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementComparer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementComparer.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System; using System.Collections.Generic; using System.Linq; @@ -43,7 +58,7 @@ public JsonElementComparer() {} /// they are compared with the Decimal.CompareTo method, otherwise they are /// compared as doubles. /// - /// If both are objects, they are compared accoring to the following rules: + /// If both are objects, they are compared according to the following rules: /// ///
    ///
  • Order each object's properties by name and compare sequentially. @@ -84,19 +99,18 @@ public int Compare(JsonElement lhs, JsonElement rhs) { return dec1.CompareTo(dec2); } - else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) + + if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) { return val1.CompareTo(val2); } - else - { - throw new InvalidOperationException("Unable to compare numbers"); - } + + throw new InvalidOperationException("Unable to compare numbers"); } case JsonValueKind.String: { - return string.Compare(lhs.GetString(), rhs.GetString()); + return string.CompareOrdinal(lhs.GetString(), rhs.GetString()); } case JsonValueKind.Array: @@ -121,8 +135,8 @@ public int Compare(JsonElement lhs, JsonElement rhs) case JsonValueKind.Object: { // OrderBy performs a stable sort (Note that supports duplicate property names) - var enumerator1 = lhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); - var enumerator2 = rhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); + using var enumerator1 = lhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); + using var enumerator2 = rhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); var result1 = enumerator1.MoveNext(); var result2 = enumerator2.MoveNext(); @@ -130,7 +144,7 @@ public int Compare(JsonElement lhs, JsonElement rhs) { if (enumerator1.Current.Name != enumerator2.Current.Name) { - return enumerator1.Current.Name.CompareTo(enumerator2.Current.Name); + return string.Compare(enumerator1.Current.Name, enumerator2.Current.Name, StringComparison.Ordinal); } var diff = Compare(enumerator1.Current.Value, enumerator2.Current.Value); if (diff != 0) @@ -145,7 +159,7 @@ public int Compare(JsonElement lhs, JsonElement rhs) } default: - throw new InvalidOperationException(string.Format("Unknown JsonValueKind {0}", lhs.ValueKind)); + throw new InvalidOperationException($"Unknown JsonValueKind {lhs.ValueKind}"); } } @@ -154,6 +168,4 @@ int System.Collections.IComparer.Compare(object x, object y) return Compare((JsonElement)x, (JsonElement)y); } } - - -} // namespace JsonCons.JsonPath +} diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementEqualityComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementEqualityComparer.cs index f8e7bfd7..6141fb58 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementEqualityComparer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementEqualityComparer.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System; using System.Collections.Generic; using System.Linq; @@ -14,7 +29,7 @@ public sealed class JsonElementEqualityComparer : IEqualityComparer /// Gets a singleton instance of . This property is read-only. public static JsonElementEqualityComparer Instance { get; } = new(); - private int MaxHashDepth { get; } = 64; + private static int MaxHashDepth => 64; private JsonElementEqualityComparer() {} @@ -31,7 +46,7 @@ private JsonElementEqualityComparer() {} /// they are compared with the Decimal.Equals method, otherwise they are /// compared as doubles. /// - /// If both are objects, they are compared accoring to the following rules: + /// If both are objects, they are compared according to the following rules: /// ///
      ///
    • If the two objects have a different number of properties, they are different.
    • @@ -68,14 +83,13 @@ public bool Equals(JsonElement lhs, JsonElement rhs) { return dec1.Equals(dec2); } - else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) - { - return val1 == val2; - } - else + + if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) { - return false; + return Math.Abs(val1 - val2) < 0.000000001; } + + return false; } case JsonValueKind.String: @@ -97,8 +111,8 @@ public bool Equals(JsonElement lhs, JsonElement rhs) return false; } - var enumerator1 = baseEnumerator1.OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); - var enumerator2 = baseEnumerator2.OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); + using var enumerator1 = baseEnumerator1.OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); + using var enumerator2 = baseEnumerator2.OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); var result1 = enumerator1.MoveNext(); var result2 = enumerator2.MoveNext(); @@ -120,7 +134,7 @@ public bool Equals(JsonElement lhs, JsonElement rhs) } default: - throw new InvalidOperationException(string.Format("Unknown JsonValueKind {0}", lhs.ValueKind)); + throw new InvalidOperationException($"Unknown JsonValueKind {lhs.ValueKind}"); } } @@ -173,11 +187,9 @@ private int ComputeHashCode(JsonElement element, int depth) break; default: - throw new InvalidOperationException(string.Format("Unknown JsonValueKind {0}", element.ValueKind)); + throw new InvalidOperationException($"Unknown JsonValueKind {element.ValueKind}"); } return hashCode; } } - - -} // namespace JsonCons.JsonPath +} diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonFlattener.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonFlattener.cs index b5b8510b..719f6a1a 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonFlattener.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonFlattener.cs @@ -1,4 +1,19 @@ -using System; +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; using System.Linq; using System.Text; using System.Text.Json; @@ -14,7 +29,6 @@ namespace AWS.Lambda.Powertools.JMESPath.Utilities /// using System; /// using System.Diagnostics; /// using System.Text.Json; - /// using JsonCons.Utilities; /// /// public class Example /// { @@ -121,7 +135,6 @@ public enum IntegerTokenUnflattening { /// using System; /// using System.Diagnostics; /// using System.Text.Json; - /// using JsonCons.Utilities; /// /// public class Example /// { @@ -184,26 +197,26 @@ public static class JsonFlattener /// number, true, false, null, empty object, or empty array. ///
/// - /// It is the users responsibilty to properly Dispose the returned value + /// It is the users responsibility to properly Dispose the returned value /// /// The value to be flattened. /// The flattened value public static JsonDocument Flatten(JsonElement value) { var result = new JsonDocumentBuilder(JsonValueKind.Object); - var parentKey = ""; + const string parentKey = ""; _Flatten(parentKey, value, result); return result.ToJsonDocument(); } /// - /// Recovers the orginal JSON value from a JSON object in flattened form, to the extent possible. + /// Recovers the original JSON value from a JSON object in flattened form, to the extent possible. /// There may not be a unique solution, an integer token in a JSON Pointer could be an array index or /// it could be an object name. The default behavior is to attempt to recover arrays. The /// parameter can be used to recover objects with integer names instead. /// /// - /// It is the users responsibilty to properly Dispose the returned value + /// It is the users responsibility to properly Dispose the returned value /// /// The flattened value, which must be a JSON object of name-value pairs, such that /// the names are JSON Pointer strings, and the values are either string, @@ -218,19 +231,10 @@ public static JsonDocument Unflatten(JsonElement flattenedValue, { if (options == IntegerTokenUnflattening.TryIndex) { - if (TryUnflattenArray(flattenedValue, out var val)) - { - return val.ToJsonDocument(); - } - else - { - return UnflattenToObject(flattenedValue, options).ToJsonDocument(); - } - } - else - { - return UnflattenToObject(flattenedValue, options).ToJsonDocument(); + return TryUnflattenArray(flattenedValue, out var val) ? val.ToJsonDocument() : UnflattenToObject(flattenedValue, options).ToJsonDocument(); } + + return UnflattenToObject(flattenedValue, options).ToJsonDocument(); } private static void _Flatten(string parentKey, @@ -260,7 +264,7 @@ private static void _Flatten(string parentKey, case JsonValueKind.Object: { - if (parentValue.EnumerateObject().Count() == 0) + if (!parentValue.EnumerateObject().Any()) { result.AddProperty(parentKey, new JsonDocumentBuilder(parentValue)); } @@ -297,11 +301,9 @@ private static JsonDocumentBuilder SafeUnflatten(JsonDocumentBuilder value) var index = 0; foreach (var item in value.EnumerateObject()) { - if (!int.TryParse(item.Key, out var n) || index++ != n) - { - safe = false; - break; - } + if (int.TryParse(item.Key, out var n) && index++ == n) continue; + safe = false; + break; } if (safe) @@ -318,19 +320,17 @@ private static JsonDocumentBuilder SafeUnflatten(JsonDocumentBuilder value) } return a; } - else + + var o = new JsonDocumentBuilder(JsonValueKind.Object); + foreach (var item in value.EnumerateObject()) { - var o = new JsonDocumentBuilder(JsonValueKind.Object); - foreach (var item in value.EnumerateObject()) - { - //if (!o.ContainsPropertyName(item.Key)) - //{ - // o.AddProperty(item.Key, SafeUnflatten (item.Value)); - //} - o.TryAddProperty(item.Key, SafeUnflatten (item.Value)); - } - return o; + //if (!o.ContainsPropertyName(item.Key)) + //{ + // o.AddProperty(item.Key, SafeUnflatten (item.Value)); + //} + o.TryAddProperty(item.Key, SafeUnflatten (item.Value)); } + return o; } private static bool TryUnflattenArray(JsonElement value, out JsonDocumentBuilder result) @@ -355,7 +355,7 @@ private static bool TryUnflattenArray(JsonElement value, out JsonDocumentBuilder } var index = 0; - var it = ptr.GetEnumerator(); + using var it = ptr.GetEnumerator(); var more = it.MoveNext(); while (more) { @@ -461,7 +461,8 @@ private static JsonDocumentBuilder UnflattenToObject(JsonElement value, IntegerT { throw new ArgumentException("Name contains invalid JSON Pointer"); } - var it = ptr.GetEnumerator(); + + using var it = ptr.GetEnumerator(); var more = it.MoveNext(); while (more) { @@ -499,5 +500,4 @@ private static JsonDocumentBuilder UnflattenToObject(JsonElement value, IntegerT return options == IntegerTokenUnflattening.TryIndex ? SafeUnflatten (result) : result; } } - -} // namespace JsonCons.Utilities +} diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonMergePatch.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonMergePatch.cs index 0f2d3a3e..350128ca 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonMergePatch.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonMergePatch.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Text.Json; namespace AWS.Lambda.Powertools.JMESPath.Utilities @@ -13,7 +28,6 @@ namespace AWS.Lambda.Powertools.JMESPath.Utilities /// using System; /// using System.Diagnostics; /// using System.Text.Json; - /// using JsonCons.Utilities; /// /// public class Example /// { @@ -112,7 +126,7 @@ public static class JsonMergePatch /// to a source JSON value. /// /// - /// It is the users responsibilty to properly Dispose the returned value + /// It is the users responsibility to properly Dispose the returned value /// /// The source JSON value. /// The JSON merge patch to be applied to the source JSON value. @@ -161,7 +175,7 @@ private static JsonDocumentBuilder ApplyMergePatch(ref JsonDocumentBuilder targe /// given two JSON values, a source and a target. /// /// - /// It is the users responsibilty to properly Dispose the returned value + /// It is the users responsibility to properly Dispose the returned value /// /// The source JSON value. /// The target JSON value. @@ -198,8 +212,7 @@ private static JsonDocumentBuilder _FromDiff(JsonElement source, JsonElement tar foreach (var property in target.EnumerateObject()) { - JsonElement value; - if (!source.TryGetProperty(property.Name, out value)) + if (!source.TryGetProperty(property.Name, out _)) { builder.AddProperty(property.Name, new JsonDocumentBuilder(property.Value)); } @@ -208,6 +221,4 @@ private static JsonDocumentBuilder _FromDiff(JsonElement source, JsonElement tar return builder; } } - - -} // namespace JsonCons.Utilities +} diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPatch.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPatch.cs index bf1fc449..5164bb2b 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPatch.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPatch.cs @@ -1,4 +1,19 @@ -using System; +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; using System.Diagnostics; using System.Text; using System.Text.Json; @@ -44,7 +59,6 @@ public JsonPatchException( /// using System; /// using System.Diagnostics; /// using System.Text.Json; - /// using JsonCons.Utilities; /// /// public class Example /// { @@ -128,7 +142,7 @@ public static class JsonPatch /// to a source JSON value. /// /// - /// It is the users responsibilty to properly Dispose the returned value + /// It is the users responsibility to properly Dispose the returned value /// /// The source JSON value. /// The patch to be applied to the source JSON value. @@ -139,16 +153,16 @@ public static class JsonPatch /// /// A JSON Patch operation failed /// - public static JsonDocument ApplyPatch(JsonElement source, - JsonElement patch) + public static JsonDocument ApplyPatch(JsonElement source, + JsonElement patch) { var documentBuilder = new JsonDocumentBuilder(source); ApplyPatch(ref documentBuilder, patch); return documentBuilder.ToJsonDocument(); } - private static void ApplyPatch(ref JsonDocumentBuilder target, - JsonElement patch) + private static void ApplyPatch(ref JsonDocumentBuilder target, + JsonElement patch) { var comparer = JsonElementEqualityComparer.Instance; @@ -158,132 +172,146 @@ private static void ApplyPatch(ref JsonDocumentBuilder target, { throw new ArgumentException("Patch must be an array"); } - + foreach (var operation in patch.EnumerateArray()) { if (!operation.TryGetProperty("op", out var opElement)) { throw new ArgumentException("Invalid patch"); } + var op = opElement.GetString() ?? throw new InvalidOperationException("Operation cannot be null"); if (!operation.TryGetProperty("path", out var pathElement)) { - throw new ArgumentException(op, "Invalid patch"); + throw new ArgumentException(op, nameof(patch)); } - var path = pathElement.GetString() ?? throw new InvalidOperationException("Operation cannot be null"); ; + + var path = pathElement.GetString() ?? throw new InvalidOperationException("Operation cannot be null"); if (!JsonPointer.TryParse(path, out var location)) { - throw new ArgumentException(op, "Invalid patch"); + throw new ArgumentException(op, nameof(patch)); } - if (op =="test") + switch (op) { - if (!operation.TryGetProperty("value", out var value)) + case "test": { - throw new ArgumentException(op, "Invalid patch"); - } + if (!operation.TryGetProperty("value", out var value)) + { + throw new ArgumentException(op, nameof(patch)); + } - if (!location.TryGetValue(target, out var tested)) - { - throw new ArgumentException(op, "Invalid patch"); - } + if (!location.TryGetValue(target, out var tested)) + { + throw new ArgumentException(op, nameof(patch)); + } - using (var doc = tested.ToJsonDocument()) - { + using var doc = tested.ToJsonDocument(); if (!comparer.Equals(doc.RootElement, value)) { throw new JsonPatchException(op, "Test failed"); } + + break; } - } - else if (op =="add") - { - if (!operation.TryGetProperty("value", out var value)) - { - throw new ArgumentException(op, "Invalid patch"); - } - var valueBuilder = new JsonDocumentBuilder(value); - if (!location.TryAddIfAbsent(ref target, valueBuilder)) // try insert without replace + case "add": { + if (!operation.TryGetProperty("value", out var value)) + { + throw new ArgumentException(op, nameof(patch)); + } + + var valueBuilder = new JsonDocumentBuilder(value); + if (location.TryAddIfAbsent(ref target, valueBuilder)) continue; // try insert without replace if (!location.TryReplace(ref target, valueBuilder)) // try insert without replace { throw new JsonPatchException(op, "Add failed"); } + + break; } - } - else if (op =="remove") - { - if (!location.TryRemove(ref target)) - { + case "remove" when !location.TryRemove(ref target): throw new JsonPatchException(op, "Add failed"); - } - } - else if (op =="replace") - { - if (!operation.TryGetProperty("value", out var value)) - { - throw new ArgumentException(op, "Invalid patch"); - } - var valueBuilder = new JsonDocumentBuilder(value); - if (!location.TryReplace(ref target, valueBuilder)) + case "replace": { - throw new JsonPatchException(op, "Replace failed"); + if (!operation.TryGetProperty("value", out var value)) + { + throw new ArgumentException(op, nameof(patch)); + } + + var valueBuilder = new JsonDocumentBuilder(value); + if (!location.TryReplace(ref target, valueBuilder)) + { + throw new JsonPatchException(op, "Replace failed"); + } + + break; } - } - else if (op =="move") - { - if (!operation.TryGetProperty("from", out var fromElement)) + case "move": { - throw new ArgumentException(op, "Invalid patch"); - } - var from = fromElement.GetString() ?? throw new InvalidOperationException("From element cannot be null"); ; + if (!operation.TryGetProperty("from", out var fromElement)) + { + throw new ArgumentException(op, nameof(patch)); + } - if (!JsonPointer.TryParse(from, out var fromPointer)) - { - throw new ArgumentException(op, "Invalid patch"); - } + var from = fromElement.GetString() ?? + throw new InvalidOperationException("From element cannot be null"); + + if (!JsonPointer.TryParse(from, out var fromPointer)) + { + throw new ArgumentException(op, nameof(patch)); + } - if (!fromPointer.TryGetValue(target, out var value)) - { - throw new JsonPatchException(op, "Move failed"); - } + if (!fromPointer.TryGetValue(target, out var value)) + { + throw new JsonPatchException(op, "Move failed"); + } - if (!fromPointer.TryRemove(ref target)) - { - throw new JsonPatchException(op, "Move failed"); - } - if (!location.TryAddIfAbsent(ref target, value)) - { - if (!location.TryReplace(ref target, value)) // try insert without replace + if (!fromPointer.TryRemove(ref target)) { throw new JsonPatchException(op, "Move failed"); } - } - } - else if (op =="copy") - { - if (!operation.TryGetProperty("from", out var fromElement)) - { - throw new ArgumentException(op, "Invalid patch"); - } - var from = fromElement.GetString() ?? throw new InvalidOperationException("from cannot be null"); - if (!JsonPointer.TryParse(from, out var fromPointer)) - { - throw new ArgumentException(op, "Invalid patch"); - } - if (!fromPointer.TryGetValue(target, out var value)) - { - throw new JsonPatchException(op, "Copy failed"); + if (!location.TryAddIfAbsent(ref target, value)) + { + if (!location.TryReplace(ref target, value)) // try insert without replace + { + throw new JsonPatchException(op, "Move failed"); + } + } + + break; } - if (!location.TryAddIfAbsent(ref target, value)) + case "copy": { - if (!location.TryReplace(ref target, value)) // try insert without replace + if (!operation.TryGetProperty("from", out var fromElement)) { - throw new JsonPatchException(op, "Move failed"); + throw new ArgumentException(op, nameof(patch)); + } + + var from = fromElement.GetString() ?? + throw new InvalidOperationException("from cannot be null"); + if (!JsonPointer.TryParse(from, out var fromPointer)) + { + throw new ArgumentException(op, nameof(patch)); + } + + if (!fromPointer.TryGetValue(target, out var value)) + { + throw new JsonPatchException(op, "Copy failed"); + } + + if (!location.TryAddIfAbsent(ref target, value)) + { + if (!location.TryReplace(ref target, value)) // try insert without replace + { + throw new JsonPatchException(op, "Move failed"); + } } + + break; } } } @@ -294,60 +322,62 @@ private static void ApplyPatch(ref JsonDocumentBuilder target, /// given two JSON values, a source and a target. /// /// - /// It is the users responsibilty to properly Dispose the returned value + /// It is the users responsibility to properly Dispose the returned value /// /// The source JSON value. /// The target JSON value. /// A JSON Merge Patch to convert the source JSON value to the target JSON value - public static JsonDocument FromDiff(JsonElement source, - JsonElement target) + public static JsonDocument FromDiff(JsonElement source, + JsonElement target) { return _FromDiff(source, target, "").ToJsonDocument(); } - private static JsonDocumentBuilder _FromDiff(JsonElement source, - JsonElement target, - string path) + private static JsonDocumentBuilder _FromDiff(JsonElement source, + JsonElement target, + string path) { var builder = new JsonDocumentBuilder(JsonValueKind.Array); var comparer = JsonElementEqualityComparer.Instance; - if (comparer.Equals(source,target)) + if (comparer.Equals(source, target)) { return builder; } if (source.ValueKind == JsonValueKind.Array && target.ValueKind == JsonValueKind.Array) { - var common = Math.Min(source.GetArrayLength(),target.GetArrayLength()); + var common = Math.Min(source.GetArrayLength(), target.GetArrayLength()); for (var i = 0; i < common; ++i) { - var buffer = new StringBuilder(path); + var buffer = new StringBuilder(path); buffer.Append("/"); buffer.Append(i.ToString()); - var temp_diff = _FromDiff(source[i], target[i], buffer.ToString()); - foreach (var item in temp_diff.EnumerateArray()) + var tempDiff = _FromDiff(source[i], target[i], buffer.ToString()); + foreach (var item in tempDiff.EnumerateArray()) { builder.AddArrayItem(item); } } + // Element in source, not in target - remove for (var i = source.GetArrayLength(); i-- > target.GetArrayLength();) { - var buffer = new StringBuilder(path); - buffer.Append("/"); + var buffer = new StringBuilder(path); + buffer.Append('/'); buffer.Append(i.ToString()); var valBuilder = new JsonDocumentBuilder(JsonValueKind.Object); valBuilder.AddProperty("op", new JsonDocumentBuilder("remove")); valBuilder.AddProperty("path", new JsonDocumentBuilder(buffer.ToString())); builder.AddArrayItem(valBuilder); } + // Element in target, not in source - add, for (var i = source.GetArrayLength(); i < target.GetArrayLength(); ++i) { var a = target[i]; - var buffer = new StringBuilder(path); + var buffer = new StringBuilder(path); buffer.Append("/"); buffer.Append(i.ToString()); var valBuilder = new JsonDocumentBuilder(JsonValueKind.Object); @@ -362,13 +392,13 @@ private static JsonDocumentBuilder _FromDiff(JsonElement source, foreach (var a in source.EnumerateObject()) { var buffer = new StringBuilder(path); - buffer.Append("/"); + buffer.Append("/"); buffer.Append(JsonPointer.Escape(a.Name)); if (target.TryGetProperty(a.Name, out var element)) - { - var temp_diff = _FromDiff(a.Value, element, buffer.ToString()); - foreach (var item in temp_diff.EnumerateArray()) + { + var tempDiff = _FromDiff(a.Value, element, buffer.ToString()); + foreach (var item in tempDiff.EnumerateArray()) { builder.AddArrayItem(item); } @@ -381,20 +411,19 @@ private static JsonDocumentBuilder _FromDiff(JsonElement source, builder.AddArrayItem(valBuilder); } } + foreach (var a in target.EnumerateObject()) { - JsonElement element; - if (!source.TryGetProperty(a.Name, out element)) - { - var buffer = new StringBuilder(path); - buffer.Append("/"); - buffer.Append(JsonPointer.Escape(a.Name)); - var valBuilder = new JsonDocumentBuilder(JsonValueKind.Object); - valBuilder.AddProperty("op", new JsonDocumentBuilder("add")); - valBuilder.AddProperty("path", new JsonDocumentBuilder(buffer.ToString())); - valBuilder.AddProperty("value", new JsonDocumentBuilder(a.Value)); - builder.AddArrayItem(valBuilder); - } + if (source.TryGetProperty(a.Name, out _)) continue; + + var buffer = new StringBuilder(path); + buffer.Append("/"); + buffer.Append(JsonPointer.Escape(a.Name)); + var valBuilder = new JsonDocumentBuilder(JsonValueKind.Object); + valBuilder.AddProperty("op", new JsonDocumentBuilder("add")); + valBuilder.AddProperty("path", new JsonDocumentBuilder(buffer.ToString())); + valBuilder.AddProperty("value", new JsonDocumentBuilder(a.Value)); + builder.AddArrayItem(valBuilder); } } else @@ -409,5 +438,4 @@ private static JsonDocumentBuilder _FromDiff(JsonElement source, return builder; } } - -} // namespace JsonCons.Utilities +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointer.cs index 059dc613..1b933a50 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointer.cs @@ -1,4 +1,19 @@ -using System; +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; using System.Collections.Generic; using System.Text; using System.Text.Json; @@ -14,7 +29,6 @@ namespace AWS.Lambda.Powertools.JMESPath.Utilities /// using System; /// using System.Diagnostics; /// using System.Text.Json; - /// using JsonCons.Utilities; /// /// public class Example /// { @@ -58,7 +72,7 @@ namespace AWS.Lambda.Powertools.JMESPath.Utilities public sealed class JsonPointer : IEnumerable, IEquatable { /// Gets a singleton instance of a to the root value of a JSON document. - public static JsonPointer Default {get;} = new(); + private static JsonPointer Default {get;} = new(); private enum JsonPointerState {Start, Escaped, Delim} @@ -70,8 +84,7 @@ private enum JsonPointerState {Start, Escaped, Delim} /// /// Constructs a JSON Pointer to the root value of a JSON document /// - - public JsonPointer() + private JsonPointer() { Tokens = new List(); } @@ -157,7 +170,7 @@ public static bool TryParse(string input, out JsonPointer pointer) default: pointer = Default; return false; - }; + } break; case JsonPointerState.Delim: switch (input[index]) @@ -171,7 +184,7 @@ public static bool TryParse(string input, out JsonPointer pointer) default: buffer.Append(input[index]); break; - }; + } break; case JsonPointerState.Escaped: switch (input[index]) @@ -187,11 +200,13 @@ public static bool TryParse(string input, out JsonPointer pointer) default: pointer = Default; return false; - }; + } break; default: + { pointer = Default; return false; + } } ++index; } @@ -217,7 +232,7 @@ public IEnumerator GetEnumerator() System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return (System.Collections.IEnumerator) GetEnumerator(); + return GetEnumerator(); } /// @@ -230,7 +245,7 @@ public override string ToString() var buffer = new StringBuilder(); foreach (var token in Tokens) { - buffer.Append("/"); + buffer.Append('/'); Escape(token, buffer); } return buffer.ToString(); @@ -240,16 +255,15 @@ public override string ToString() /// Returns a string representing the JSON Pointer as a URI fragment identifier /// /// A JSON Pointer represented as a fragment identifier. - public string ToUriFragment() { var buffer = new StringBuilder(); - buffer.Append("#"); + buffer.Append('#'); foreach (var token in Tokens) { - buffer.Append("/"); - var s = Uri.EscapeUriString(token); + buffer.Append('/'); + var s = Uri.EscapeDataString(token); var span = s.AsSpan(); for (var i = 0; i < span.Length; ++i) { @@ -435,7 +449,7 @@ public static bool ContainsValue(JsonElement root, string pointer) /// The root that is to be queried. /// Contains the value at the referenced location, if found. /// true if the value was found at the referenced location, otherwise false. - public bool TryGetValue(JsonElement root, out JsonElement value) + private bool TryGetValue(JsonElement root, out JsonElement value) { value = root; @@ -572,5 +586,4 @@ private static void Escape(string token, StringBuilder buffer) } } } - -} // namespace JsonCons.Utilities +} diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointerExtensions.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointerExtensions.cs index a729c44e..0f7fa997 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointerExtensions.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointerExtensions.cs @@ -1,11 +1,26 @@ -using System.Collections.Generic; +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Collections.Generic; using System.Text.Json; namespace AWS.Lambda.Powertools.JMESPath.Utilities { internal static class JsonPointerExtensions { - public static bool TryResolve(string token, JsonDocumentBuilder current, out JsonDocumentBuilder result) + private static bool TryResolve(string token, JsonDocumentBuilder current, out JsonDocumentBuilder result) { result = current; @@ -20,10 +35,12 @@ public static bool TryResolve(string token, JsonDocumentBuilder current, out Jso { return false; } + if (index >= result.GetArrayLength()) { return false; } + result = result[index]; } else if (result.ValueKind == JsonValueKind.Object) @@ -43,29 +60,30 @@ public static bool TryResolve(string token, JsonDocumentBuilder current, out Jso public static JsonPointer ToDefinitePath(this JsonPointer pointer, JsonDocumentBuilder value) { - if (value.ValueKind == JsonValueKind.Array && pointer.Tokens.Count > 0 && pointer.Tokens[pointer.Tokens.Count-1] == "-") + if (value.ValueKind == JsonValueKind.Array && pointer.Tokens.Count > 0 && + pointer.Tokens[pointer.Tokens.Count - 1] == "-") { var tokens = new List(); - for (var i = 0; i < pointer.Tokens.Count-1; ++i) + for (var i = 0; i < pointer.Tokens.Count - 1; ++i) { tokens.Add(pointer.Tokens[i]); } + tokens.Add(value.GetArrayLength().ToString()); return new JsonPointer(tokens); } - else - { - return pointer; - } + + return pointer; } - public static bool TryGetValue(this JsonPointer pointer, JsonDocumentBuilder root, out JsonDocumentBuilder value) + public static bool TryGetValue(this JsonPointer pointer, JsonDocumentBuilder root, + out JsonDocumentBuilder value) { value = root; foreach (var token in pointer) { - if (!TryResolve(token,value,out value)) + if (!TryResolve(token, value, out value)) { return false; } @@ -74,19 +92,20 @@ public static bool TryGetValue(this JsonPointer pointer, JsonDocumentBuilder roo return true; } - public static bool TryAdd(this JsonPointer location, - ref JsonDocumentBuilder root, - JsonDocumentBuilder value) + public static bool TryAdd(this JsonPointer location, + ref JsonDocumentBuilder root, + JsonDocumentBuilder value) { var current = root; var token = ""; - var enumerator = location.GetEnumerator(); + using var enumerator = location.GetEnumerator(); var more = enumerator.MoveNext(); if (!more) { return false; } + while (more) { token = enumerator.Current; @@ -105,7 +124,7 @@ public static bool TryAdd(this JsonPointer location, if (token.Length == 1 && token[0] == '-') { current.AddArrayItem(value); - current = current[current.GetArrayLength()-1]; + current = current[current.GetArrayLength() - 1]; } else { @@ -113,10 +132,12 @@ public static bool TryAdd(this JsonPointer location, { return false; } + if (index > current.GetArrayLength()) { return false; } + if (index == current.GetArrayLength()) { current.AddArrayItem(value); @@ -124,7 +145,7 @@ public static bool TryAdd(this JsonPointer location, } else { - current.InsertArrayItem(index,value); + current.InsertArrayItem(index, value); current = value; } } @@ -135,6 +156,7 @@ public static bool TryAdd(this JsonPointer location, { current.RemoveProperty(token); } + current.AddProperty(token, value); current = value; } @@ -142,22 +164,24 @@ public static bool TryAdd(this JsonPointer location, { return false; } + return true; } - public static bool TryAddIfAbsent(this JsonPointer location, - ref JsonDocumentBuilder root, - JsonDocumentBuilder value) + public static bool TryAddIfAbsent(this JsonPointer location, + ref JsonDocumentBuilder root, + JsonDocumentBuilder value) { var current = root; var token = ""; - var enumerator = location.GetEnumerator(); + using var enumerator = location.GetEnumerator(); var more = enumerator.MoveNext(); if (!more) { return false; } + while (more) { token = enumerator.Current; @@ -176,7 +200,7 @@ public static bool TryAddIfAbsent(this JsonPointer location, if (token.Length == 1 && token[0] == '-') { current.AddArrayItem(value); - current = current[current.GetArrayLength()-1]; + current = current[current.GetArrayLength() - 1]; } else { @@ -184,10 +208,12 @@ public static bool TryAddIfAbsent(this JsonPointer location, { return false; } + if (index > current.GetArrayLength()) { return false; } + if (index == current.GetArrayLength()) { current.AddArrayItem(value); @@ -195,7 +221,7 @@ public static bool TryAddIfAbsent(this JsonPointer location, } else { - current.InsertArrayItem(index,value); + current.InsertArrayItem(index, value); current = value; } } @@ -206,6 +232,7 @@ public static bool TryAddIfAbsent(this JsonPointer location, { return false; } + current.AddProperty(token, value); current = value; } @@ -213,21 +240,23 @@ public static bool TryAddIfAbsent(this JsonPointer location, { return false; } + return true; } - public static bool TryRemove(this JsonPointer location, - ref JsonDocumentBuilder root) + public static bool TryRemove(this JsonPointer location, + ref JsonDocumentBuilder root) { var current = root; var token = ""; - var enumerator = location.GetEnumerator(); + using var enumerator = location.GetEnumerator(); var more = enumerator.MoveNext(); if (!more) { return false; } + while (more) { token = enumerator.Current; @@ -247,18 +276,18 @@ public static bool TryRemove(this JsonPointer location, { return false; } - else + + if (!int.TryParse(token, out var index)) { - if (!int.TryParse(token, out var index)) - { - return false; - } - if (index >= current.GetArrayLength()) - { - return false; - } - current.RemoveArrayItemAt(index); + return false; } + + if (index >= current.GetArrayLength()) + { + return false; + } + + current.RemoveArrayItemAt(index); } else if (current.ValueKind == JsonValueKind.Object) { @@ -271,22 +300,24 @@ public static bool TryRemove(this JsonPointer location, { return false; } + return true; } - public static bool TryReplace(this JsonPointer location, - ref JsonDocumentBuilder root, - JsonDocumentBuilder value) + public static bool TryReplace(this JsonPointer location, + ref JsonDocumentBuilder root, + JsonDocumentBuilder value) { var current = root; var token = ""; - var enumerator = location.GetEnumerator(); + using var enumerator = location.GetEnumerator(); var more = enumerator.MoveNext(); if (!more) { return false; } + while (more) { token = enumerator.Current; @@ -306,18 +337,18 @@ public static bool TryReplace(this JsonPointer location, { return false; } - else + + if (!int.TryParse(token, out var index)) { - if (!int.TryParse(token, out var index)) - { - return false; - } - if (index >= current.GetArrayLength()) - { - return false; - } - current[index] = value; + return false; + } + + if (index >= current.GetArrayLength()) + { + return false; } + + current[index] = value; } else if (current.ValueKind == JsonValueKind.Object) { @@ -329,15 +360,15 @@ public static bool TryReplace(this JsonPointer location, { return false; } + current.AddProperty(token, value); } else { return false; } + return true; } - } - -} // namespace JsonCons.Utilities +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Value.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Value.cs index 8ec005d6..e0fae410 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Value.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Value.cs @@ -1,4 +1,19 @@ -using System; +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; using System.Collections.Generic; using System.Text; using System.Text.Json; @@ -50,7 +65,7 @@ internal interface IValue IArrayValueEnumerator EnumerateArray(); IObjectValueEnumerator EnumerateObject(); IExpression GetExpression(); - }; + } internal readonly struct JsonElementValue : IValue { @@ -217,7 +232,7 @@ public override string ToString() var s = JsonSerializer.Serialize(_element); return s; } - }; + } internal readonly struct DoubleValue : IValue { @@ -638,7 +653,7 @@ public override string ToString() { first = false; } - buffer.Append(item.ToString()); + buffer.Append(item); } buffer.Append(']'); return buffer.ToString(); @@ -754,7 +769,7 @@ public override string ToString() } buffer.Append(JsonSerializer.Serialize(property.Key)); buffer.Append(':'); - buffer.Append(property.Value.ToString()); + buffer.Append(property.Value); } buffer.Append('}'); return buffer.ToString(); @@ -812,5 +827,5 @@ public override string ToString() { return "expression"; } - }; + } } diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/ValueComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/ValueComparer.cs index bfd785a3..a09d6bff 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/ValueComparer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/ValueComparer.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System; using System.Collections.Generic; using System.Linq; @@ -15,7 +30,7 @@ internal sealed class ValueComparer : IComparer, System.Collections.ICom /// /// Constructs a /// - public ValueComparer() {} + private ValueComparer() {} /// /// Compares two instances. @@ -81,18 +96,17 @@ public int Compare(IValue lhs, IValue rhs) { return dec1.CompareTo(dec2); } - else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) + + if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) { return val1.CompareTo(val2); } - else - { - throw new InvalidOperationException("Unable to compare numbers"); - } + + throw new InvalidOperationException("Unable to compare numbers"); } case JmesPathType.String: - return lhs.GetString().CompareTo(rhs.GetString()); + return string.Compare(lhs.GetString(), rhs.GetString(), StringComparison.Ordinal); case JmesPathType.Array: { @@ -116,8 +130,8 @@ public int Compare(IValue lhs, IValue rhs) case JmesPathType.Object: { // OrderBy performs a stable sort (Note that supports duplicate property names) - var enumerator1 = lhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); - var enumerator2 = rhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); + using var enumerator1 = lhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); + using var enumerator2 = rhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); var result1 = enumerator1.MoveNext(); var result2 = enumerator2.MoveNext(); @@ -125,7 +139,7 @@ public int Compare(IValue lhs, IValue rhs) { if (enumerator1.Current.Name != enumerator2.Current.Name) { - return enumerator1.Current.Name.CompareTo(enumerator2.Current.Name); + return string.Compare(enumerator1.Current.Name, enumerator2.Current.Name, StringComparison.Ordinal); } var diff = Compare(enumerator1.Current.Value, enumerator2.Current.Value); if (diff != 0) @@ -140,7 +154,7 @@ public int Compare(IValue lhs, IValue rhs) } default: - throw new InvalidOperationException(string.Format("Unknown JmesPathType {0}", lhs.Type)); + throw new InvalidOperationException($"Unknown JmesPathType {lhs.Type}"); } } @@ -149,6 +163,4 @@ int System.Collections.IComparer.Compare(object x, object y) return Compare((IValue)x, (IValue)y); } } - - -} // namespace JsonCons.JsonPath +} diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/ValueEqualityComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/ValueEqualityComparer.cs index 78679579..ef04d261 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/ValueEqualityComparer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/ValueEqualityComparer.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System; using System.Collections.Generic; using System.Linq; @@ -8,15 +23,17 @@ internal sealed class ValueEqualityComparer : IEqualityComparer { internal static ValueEqualityComparer Instance { get; } = new(); - private int _maxHashDepth = 100; + private readonly int _maxHashDepth = 100; private ValueEqualityComparer() {} public bool Equals(IValue lhs, IValue rhs) { - if (lhs.Type != rhs.Type) + if (lhs != null && rhs != null && lhs.Type != rhs.Type) return false; + if (rhs == null || lhs == null) return false; + switch (lhs.Type) { case JmesPathType.Null: @@ -30,18 +47,17 @@ public bool Equals(IValue lhs, IValue rhs) { return dec1 == dec2; } - else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) - { - return val1 == val2; - } - else + + if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) { - return false; + return Math.Abs(val1 - val2) < 0.000000001; } + + return false; } case JmesPathType.String: - return lhs.GetString().Equals(rhs.GetString()); + return lhs.GetString().Equals(rhs.GetString()); case JmesPathType.Array: return lhs.EnumerateArray().SequenceEqual(rhs.EnumerateArray(), this); @@ -49,8 +65,10 @@ public bool Equals(IValue lhs, IValue rhs) case JmesPathType.Object: { // OrderBy performs a stable sort (Note that IValue supports duplicate property names) - var enumerator1 = lhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); - var enumerator2 = rhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); + using var enumerator1 = lhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal) + .GetEnumerator(); + using var enumerator2 = rhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal) + .GetEnumerator(); var result1 = enumerator1.MoveNext(); var result2 = enumerator2.MoveNext(); @@ -60,19 +78,21 @@ public bool Equals(IValue lhs, IValue rhs) { return false; } - if (!(Equals(enumerator1.Current.Value,enumerator2.Current.Value))) + + if (!(Equals(enumerator1.Current.Value, enumerator2.Current.Value))) { return false; } + result1 = enumerator1.MoveNext(); result2 = enumerator2.MoveNext(); - } + } return result1 == false && result2 == false; } default: - throw new InvalidOperationException(string.Format("Unknown JmesPathType {0}", lhs.Type)); + throw new InvalidOperationException($"Unknown JmesPathType {lhs.Type}"); } } @@ -119,7 +139,7 @@ private int ComputeHashCode(IValue element, int depth) break; default: - throw new InvalidOperationException(string.Format("Unknown JmesPathType {0}", element.Type)); + throw new InvalidOperationException($"Unknown JmesPathType {element.Type}"); } return hashCode; } diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/GlobalUsings.cs b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/GlobalUsings.cs index 8c927eb7..2cdb71da 100644 --- a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/GlobalUsings.cs +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/GlobalUsings.cs @@ -1 +1,16 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + global using Xunit; \ No newline at end of file diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/JmesPathTests.cs b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/JmesPathTests.cs index 52f5db99..4da08962 100644 --- a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/JmesPathTests.cs +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/JmesPathTests.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Text.Json; using AWS.Lambda.Powertools.JMESPath.Utilities; using Xunit.Abstractions; From 4170aba52dfa98edaa18253f8b50e5d3455c190d Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Thu, 18 Apr 2024 11:54:52 +0100 Subject: [PATCH 03/28] Add README.md. More tests, examples and events. --- .../AWS.Lambda.Powertools.JMESPath/README.md | 35 +- ...WS.Lambda.Powertools.JMESPath.Tests.csproj | 12 + .../JmesPathExamples.cs | 489 ++++++++++++++++++ .../JmesPathTests.cs | 20 +- .../test_files/cloud_watch_logs.json | 15 + .../test_files/kinesis_data_stream.json | 74 +++ .../test_files/sns.json | 41 ++ .../test_files/sqs.json | 78 +++ 8 files changed, 754 insertions(+), 10 deletions(-) create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/JmesPathExamples.cs create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/cloud_watch_logs.json create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/kinesis_data_stream.json create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/sns.json create mode 100644 libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/sqs.json diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/README.md b/libraries/src/AWS.Lambda.Powertools.JMESPath/README.md index 66d2c222..3ad4bca4 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/README.md +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/README.md @@ -40,4 +40,37 @@ JsonDocument result = JsonTransformer.Transform(doc.RootElement, expr); -``` \ No newline at end of file +``` + +It produces the result +```json +"dd4649e6-2484-4993-acb8-0f9123103394" +``` + You can find more examples [here](../../tests/AWS.Lambda.Powertools.JMESPath.Tests/JmesPathExamples.cs) + +## Built-in envelopes + +We provide built-in envelopes for popular AWS Lambda event sources to easily decode and/or deserialize JSON objects. + +| Envelop | JMESPath expression | +|---------------------|-----------------------------------------------------------------------------| +| API_GATEWAY_HTTP | powertools_json(body) | +| API_GATEWAY_REST | powertools_json(body) | +| CLOUDWATCH_LOGS | awslogs.powertools_base64_gzip(data) | powertools_json(@).logEvents[*] | +| KINESIS_DATA_STREAM | Records[*].kinesis.powertools_json(powertools_base64(data)) | +| SNS | Records[*].Sns.Message | powertools_json(@) | +| SQS | Records[*].powertools_json(body) | + +More examples of events can be found [here](../../tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files) + +## Built-in JMESPath functions +You can use our built-in JMESPath functions within your envelope expression. They handle deserialization for common data formats found in AWS Lambda event sources such as JSON strings, base64, and uncompress gzip data. + +### powertools_json function +Use powertools_json function to decode any JSON string anywhere a JMESPath expression is allowed. + +### powertools_base64 function +Use powertools_base64 function to decode any base64 data. + +### powertools_base64_gzip function +Use powertools_base64_gzip function to decompress and decode base64 data. \ No newline at end of file diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/AWS.Lambda.Powertools.JMESPath.Tests.csproj b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/AWS.Lambda.Powertools.JMESPath.Tests.csproj index 2f385c45..bbea95c1 100644 --- a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/AWS.Lambda.Powertools.JMESPath.Tests.csproj +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/AWS.Lambda.Powertools.JMESPath.Tests.csproj @@ -89,6 +89,18 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/JmesPathExamples.cs b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/JmesPathExamples.cs new file mode 100644 index 00000000..a1386ea6 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/JmesPathExamples.cs @@ -0,0 +1,489 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Text.Json; +using Xunit.Abstractions; + +namespace AWS.Lambda.Powertools.JMESPath.Tests; + +public class JmesPathExamples +{ + private readonly ITestOutputHelper _output; + private readonly JsonSerializerOptions _serializerOptions = new() { WriteIndented = false }; + + public JmesPathExamples(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void Select_With_Powertools_Json_Function() + { + var jsonString = """ + { + "body": "{\"customerId\":\"dd4649e6-2484-4993-acb8-0f9123103394\"}", + "deeply_nested": [ + { + "some_data": [ + 1, + 2, + 3 + ] + } + ] + } + """; + + using var doc = JsonDocument.Parse(jsonString); + + var transformer = JsonTransformer.Parse("powertools_json(body).customerId"); + using var result = transformer.Transform(doc.RootElement); + + _output.WriteLine(result.RootElement.GetRawText()); + + Assert.Equal("dd4649e6-2484-4993-acb8-0f9123103394", result.RootElement.GetString()); + } + + [Fact] + public void Select_With_Powertools_Base64_Function() + { + var jsonString = """ + { + "body": "eyJjdXN0b21lcklkIjoiZGQ0NjQ5ZTYtMjQ4NC00OTkzLWFjYjgtMGY5MTIzMTAzMzk0In0=", + "deeply_nested": [ + { + "some_data": [ + 1, + 2, + 3 + ] + } + ] + } + """; + + using var doc = JsonDocument.Parse(jsonString); + + var transformer = JsonTransformer.Parse("powertools_base64(body).customerId"); + using var result = transformer.Transform(doc.RootElement); + + _output.WriteLine(result.RootElement.GetRawText()); + + Assert.Equal("dd4649e6-2484-4993-acb8-0f9123103394", result.RootElement.GetString()); + } + + [Fact] + public void Select_With_Powertools_Base64_Gzip_Function() + { + var jsonString = """ + { + "body": "H4sIAAAAAAAAA6tWSi4tLsnPTS3yTFGyUkpJMTEzsUw10zUysTDRNbG0NNZNTE6y0DVIszQ0MjY0MDa2NFGqBQCMzDWgNQAAAA==", + "deeply_nested": [ + { + "some_data": [ + 1, + 2, + 3 + ] + } + ] + } + """; + + using var doc = JsonDocument.Parse(jsonString); + + var transformer = JsonTransformer.Parse("powertools_base64_gzip(body).customerId"); + using var result = transformer.Transform(doc.RootElement); + + _output.WriteLine(result.RootElement.GetRawText()); + + Assert.Equal("dd4649e6-2484-4993-acb8-0f9123103394", result.RootElement.GetString()); + } + + [Fact] + public void FiltersAndMultiselectLists() + { + //Arrange + + var jsonString = """ + { + "people": [ + { + "age": 20, + "other": "foo", + "name": "Bob" + }, + { + "age": 25, + "other": "bar", + "name": "Fred" + }, + { + "age": 30, + "other": "baz", + "name": "George" + } + ] + } + """; + + using var doc = JsonDocument.Parse(jsonString); + + var expectedJson = """[["Fred",25],["George",30]]"""; + + //Act + + var transformer = JsonTransformer.Parse("people[?age > `20`].[name, age]"); + + using var result = transformer.Transform(doc.RootElement); + + var actualJson = JsonSerializer.Serialize(result.RootElement, _serializerOptions); + + //Assert + + _output.WriteLine(actualJson); + Assert.Equal(expectedJson, actualJson); + } + + // Source: https://jmespath.org/examples.html#filters-and-multiselect-hashes + [Fact] + public void FiltersAndMultiselectHashes() + { + //Arrange + + var jsonString = """ + + { + "people": [ + { + "age": 20, + "other": "foo", + "name": "Bob" + }, + { + "age": 25, + "other": "bar", + "name": "Fred" + }, + { + "age": 30, + "other": "baz", + "name": "George" + } + ] + } + + """; + + using var doc = JsonDocument.Parse(jsonString); + var expectedJson = """[{"name":"Fred","age":25},{"name":"George","age":30}]"""; + + // Act + + var transformer = JsonTransformer.Parse("people[?age > `20`].{name: name, age: age}"); + + using var result = transformer.Transform(doc.RootElement); + var actualJson = JsonSerializer.Serialize(result.RootElement, _serializerOptions); + + //Assert + + _output.WriteLine(actualJson); + Assert.Equal(expectedJson, actualJson); + } + + // Source: https://jmespath.org/examples.html#working-with-nested-data + [Fact] + public void WorkingWithNestedData() + { + // Arrange + + var jsonString = """ + + { + "reservations": [ + { + "instances": [ + {"type": "small", + "state": {"name": "running"}, + "tags": [{"Key": "Name", + "Values": ["Web"]}, + {"Key": "version", + "Values": ["1"]}]}, + {"type": "large", + "state": {"name": "stopped"}, + "tags": [{"Key": "Name", + "Values": ["Web"]}, + {"Key": "version", + "Values": ["1"]}]} + ] + }, { + "instances": [ + {"type": "medium", + "state": {"name": "terminated"}, + "tags": [{"Key": "Name", + "Values": ["Web"]}, + {"Key": "version", + "Values": ["1"]}]}, + {"type": "xlarge", + "state": {"name": "running"}, + "tags": [{"Key": "Name", + "Values": ["DB"]}, + {"Key": "version", + "Values": ["1"]}]} + ] + } + ] + } + + """; + + using var doc = JsonDocument.Parse(jsonString); + var expectedJson = """[["Web","small","running"],["Web","large","stopped"],["Web","medium","terminated"],["DB","xlarge","running"]]"""; + + // Act + + var transformer = + JsonTransformer.Parse("reservations[].instances[].[tags[?Key=='Name'].Values[] | [0], type, state.name]"); + + using var result = transformer.Transform(doc.RootElement); + var actualJson = JsonSerializer.Serialize(result.RootElement, _serializerOptions); + + //Assert + + _output.WriteLine(actualJson); + Assert.Equal(expectedJson, actualJson); + } + + // Source: https://jmespath.org/examples.html#filtering-and-selecting-nested-data + [Fact] + public void FilteringAndSelectingNestedData() + { + //Arrange + + var jsonString = """ + + { + "people": [ + { + "general": { + "id": 100, + "age": 20, + "other": "foo", + "name": "Bob" + }, + "history": { + "first_login": "2014-01-01", + "last_login": "2014-01-02" + } + }, + { + "general": { + "id": 101, + "age": 30, + "other": "bar", + "name": "Bill" + }, + "history": { + "first_login": "2014-05-01", + "last_login": "2014-05-02" + } + } + ] + } + + """; + + using var doc = JsonDocument.Parse(jsonString); + var expectedJson = """{"id":100,"age":20,"other":"foo","name":"Bob"}"""; + + // Act + + var transformer = JsonTransformer.Parse("people[?general.id==`100`].general | [0]"); + using var result = transformer.Transform(doc.RootElement); + var actualJson = JsonSerializer.Serialize(result.RootElement, _serializerOptions); + + //Assert + + _output.WriteLine(actualJson); + Assert.Equal(expectedJson, actualJson); + } + + // Source: https://jmespath.org/examples.html#using-functions + [Fact] + public void UsingFunctions() + { + // Arrange + + var jsonString = """ + + { + "Contents": [ + { + "Date": "2014-12-21T05:18:08.000Z", + "Key": "logs/bb", + "Size": 303 + }, + { + "Date": "2014-12-20T05:19:10.000Z", + "Key": "logs/aa", + "Size": 308 + }, + { + "Date": "2014-12-20T05:19:12.000Z", + "Key": "logs/qux", + "Size": 297 + }, + { + "Date": "2014-11-20T05:22:23.000Z", + "Key": "logs/baz", + "Size": 329 + }, + { + "Date": "2014-12-20T05:25:24.000Z", + "Key": "logs/bar", + "Size": 604 + }, + { + "Date": "2014-12-20T05:27:12.000Z", + "Key": "logs/foo", + "Size": 647 + } + ] + } + + """; + + using var doc = JsonDocument.Parse(jsonString); + var expectedJson = """[{"Key":"logs/baz","Size":329},{"Key":"logs/aa","Size":308},{"Key":"logs/qux","Size":297},{"Key":"logs/bar","Size":604},{"Key":"logs/foo","Size":647},{"Key":"logs/bb","Size":303}]"""; + + // Act + + var transformer = JsonTransformer.Parse("sort_by(Contents, &Date)[*].{Key: Key, Size: Size}"); + using var result = transformer.Transform(doc.RootElement); + var actualJson = JsonSerializer.Serialize(result.RootElement, _serializerOptions); + + //Assert + + _output.WriteLine(actualJson); + Assert.Equal(expectedJson, actualJson); + } + + [Fact] + public void SortBySize() + { + // Arrange + + var jsonString = """ + + { + "Contents": [ + { + "Date": "2014-12-21T05:18:08.000Z", + "Key": "logs/bb", + "Size": 303 + }, + { + "Date": "2014-12-20T05:19:10.000Z", + "Key": "logs/aa", + "Size": 308 + }, + { + "Date": "2014-12-20T05:19:12.000Z", + "Key": "logs/qux", + "Size": 297 + }, + { + "Date": "2014-11-20T05:22:23.000Z", + "Key": "logs/baz", + "Size": 329 + }, + { + "Date": "2014-12-20T05:25:24.000Z", + "Key": "logs/bar", + "Size": 604 + }, + { + "Date": "2014-12-20T05:27:12.000Z", + "Key": "logs/foo", + "Size": 647 + } + ] + } + + """; + + using var doc = JsonDocument.Parse(jsonString); + var expectedJson = """[{"Size":297},{"Size":303},{"Size":308},{"Size":329},{"Size":604},{"Size":647}]"""; + + // Act + + var transformer = JsonTransformer.Parse("sort_by(Contents, &Size)[*].{Size: Size}"); + using var result = transformer.Transform(doc.RootElement); + var actualJson = JsonSerializer.Serialize(result.RootElement, _serializerOptions); + + //Assert + + _output.WriteLine(actualJson); + Assert.Equal(expectedJson, actualJson); + } + + [Fact] + public void KeyOfInterest() + { + var jsonString = """ + + { + "Data":[ + { + "KeyOfInterest":true, + "AnotherKey":true + }, + { + "KeyOfInterest":false, + "AnotherKey":true + }, + { + "KeyOfInterest":true, + "AnotherKey":true + } + ] + } + + """; + + using var doc = JsonDocument.Parse(jsonString); + + var expectedJson1 = "[true,false,true]"; + var expectedJson2 = """[{"Key of Interest":true,"Another Key":true},{"Key of Interest":false,"Another Key":true},{"Key of Interest":true,"Another Key":true}]"""; + + // Act + + var result1 = JsonTransformer.Transform(doc.RootElement, + "Data[*].KeyOfInterest"); + var result2 = JsonTransformer.Transform(doc.RootElement, + "Data[*].{\"Key of Interest\" : KeyOfInterest, \"Another Key\": AnotherKey}"); + + var actualJson1 = JsonSerializer.Serialize(result1); + var actualJson2 = JsonSerializer.Serialize(result2, _serializerOptions); + + // Assert + + _output.WriteLine(JsonSerializer.Serialize(result1)); + _output.WriteLine(JsonSerializer.Serialize(result2, _serializerOptions)); + + Assert.Equal(expectedJson1, actualJson1); + Assert.Equal(expectedJson2, actualJson2); + } +} \ No newline at end of file diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/JmesPathTests.cs b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/JmesPathTests.cs index 4da08962..7a82c697 100644 --- a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/JmesPathTests.cs +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/JmesPathTests.cs @@ -49,6 +49,10 @@ public JmesPathTests(ITestOutputHelper output) [InlineData("test_files/test.json")] [InlineData("test_files/apigw_event.json")] [InlineData("test_files/apigw_event_2.json")] + [InlineData("test_files/sns.json")] + [InlineData("test_files/sqs.json")] + [InlineData("test_files/cloud_watch_logs.json")] + [InlineData("test_files/kinesis_data_stream.json")] public void RunJmesPathTests(string path) { _output.WriteLine($"Test {path}"); @@ -100,16 +104,14 @@ public void RunJmesPathTests(string path) { var expr = JsonTransformer.Parse(exprElement.ToString()); var result = expr.Transform(given); - var success = comparer.Equals(result.RootElement, expected); - if (!success) - { - _output.WriteLine("File: {0}", path); - _output.WriteLine($"Document: {given}"); - _output.WriteLine($"Path: {exprElement}"); - _output.WriteLine($"Expected: {JsonSerializer.Serialize(expected)}"); - _output.WriteLine($"Result: {JsonSerializer.Serialize(result)}"); - } + _output.WriteLine("File: {0}", path); + + // _output.WriteLine($"Document: {given}"); + _output.WriteLine($"Path: {exprElement}"); + _output.WriteLine($"Expected: {JsonSerializer.Serialize(expected)}"); + _output.WriteLine($"Result: {JsonSerializer.Serialize(result)}"); + Assert.True(comparer.Equals(result.RootElement,expected)); } diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/cloud_watch_logs.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/cloud_watch_logs.json new file mode 100644 index 00000000..f779f00d --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/cloud_watch_logs.json @@ -0,0 +1,15 @@ +[ + { + "given": { + "awslogs": { + "data": "H4sIAAAAAAAAAHWPwQqCQBCGX0Xm7EFtK+smZBEUgXoLCdMhFtKV3akI8d0bLYmibvPPN3wz00CJxmQnTO41whwWQRIctmEcB6sQbFC3CjW3XW8kxpOpP+OC22d1Wml1qZkQGtoMsScxaczKN3plG8zlaHIta5KqWsozoTYw3/djzwhpLwivWFGHGpAFe7DL68JlBUk+l7KSN7tCOEJ4M3/qOI49vMHj+zCKdlFqLaU2ZHV2a4Ct/an0/ivdX8oYc1UVX860fQDQiMdxRQEAAA==" + } + }, + "cases": [ + { + "expression": "awslogs.powertools_base64_gzip(data) | powertools_json(@).logEvents[*]", + "result": [{"id":"eventId1","timestamp":1440442987000,"message":"[ERROR] First test message"},{"id":"eventId2","timestamp":1440442987001,"message":"[ERROR] Second test message"}] + } + ] + } +] \ No newline at end of file diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/kinesis_data_stream.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/kinesis_data_stream.json new file mode 100644 index 00000000..a9e7f236 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/kinesis_data_stream.json @@ -0,0 +1,74 @@ +[ + { + "given": { + "Records": [ + { + "kinesis": { + "partitionKey": "partitionKey-03", + "kinesisSchemaVersion": "1.0", + "data": "IlRlc3QgZnJvbSBLaW5lc2lzIg==", + "sequenceNumber": "49545115243490985018280067714973144582180062593244200961", + "approximateArrivalTimestamp": 1428537600 + }, + "eventSource": "aws:kinesis", + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200961", + "invokeIdentityArn": "arn:aws:iam::EXAMPLE", + "eventVersion": "1.0", + "eventName": "aws:kinesis:record", + "eventSourceARN": "arn:aws:kinesis:EXAMPLE", + "awsRegion": "us-east-1" + } + ] + }, + "cases": [ + { + "expression": "Records[0].kinesis.powertools_json(powertools_base64(data))", + "result": "Test from Kinesis" + } + ] + }, + { + "given": { + "Records": [ + { + "kinesis": { + "partitionKey": "partitionKey-03", + "kinesisSchemaVersion": "1.0", + "data": "IlRlc3QgZnJvbSBLaW5lc2lzIg==", + "sequenceNumber": "49545115243490985018280067714973144582180062593244200961", + "approximateArrivalTimestamp": 1428537600 + }, + "eventSource": "aws:kinesis", + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200961", + "invokeIdentityArn": "arn:aws:iam::EXAMPLE", + "eventVersion": "1.0", + "eventName": "aws:kinesis:record", + "eventSourceARN": "arn:aws:kinesis:EXAMPLE", + "awsRegion": "us-east-1" + }, + { + "kinesis": { + "partitionKey": "partitionKey-03", + "kinesisSchemaVersion": "1.0", + "data": "IlNlY29uZCBSZWNvcmQgU3RyZWFtIg==", + "sequenceNumber": "49545115243490985018280067714973144582180062593244200961", + "approximateArrivalTimestamp": 1428537600 + }, + "eventSource": "aws:kinesis", + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200961", + "invokeIdentityArn": "arn:aws:iam::EXAMPLE", + "eventVersion": "1.0", + "eventName": "aws:kinesis:record", + "eventSourceARN": "arn:aws:kinesis:EXAMPLE", + "awsRegion": "us-east-1" + } + ] + }, + "cases": [ + { + "expression": "Records[*].kinesis.powertools_json(powertools_base64(data))", + "result": ["Test from Kinesis", "Second Record Stream"] + } + ] + } +] \ No newline at end of file diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/sns.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/sns.json new file mode 100644 index 00000000..45a9c346 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/sns.json @@ -0,0 +1,41 @@ +[ + { + "given": { + "Records": [ + { + "EventSource": "aws:sns", + "EventVersion": "1.0", + "EventSubscriptionArn": "arn:aws:sns:us-east-1:{{{accountId}}}:ExampleTopic", + "Sns": { + "Type": "Notification", + "MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e", + "TopicArn": "arn:aws:sns:us-east-1:123456789012:ExampleTopic", + "Subject": "example subject", + "Message": "example message", + "Timestamp": "1970-01-01T00:00:00.000Z", + "SignatureVersion": "1", + "Signature": "EXAMPLE", + "SigningCertUrl": "EXAMPLE", + "UnsubscribeUrl": "EXAMPLE", + "MessageAttributes": { + "Test": { + "Type": "String", + "Value": "TestString" + }, + "TestBinary": { + "Type": "Binary", + "Value": "TestBinary" + } + } + } + } + ] + }, + "cases": [ + { + "expression": "Records[0].Sns.Message | powertools_json(@)", + "result": "example message" + } + ] + } +] \ No newline at end of file diff --git a/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/sqs.json b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/sqs.json new file mode 100644 index 00000000..d0f45c69 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/test_files/sqs.json @@ -0,0 +1,78 @@ +[ + { + "given": { + "Records": [ + { + "messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78", + "receiptHandle": "MessageReceiptHandle", + "body": "Hello from SQS!", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1523232000000", + "SenderId": "123456789012", + "ApproximateFirstReceiveTimestamp": "1523232000001" + }, + "messageAttributes": {}, + "md5OfBody": "{{{md5_of_body}}}", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:us-east-1:123456789012:MyQueue", + "awsRegion": "us-east-1" + } + ] + }, + "cases": [ + { + "expression": "Records[0].powertools_json(body)", + "result": "Hello from SQS!" + } + ] + }, + { + "given": { + "Records": [ + { + "messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78", + "receiptHandle": "MessageReceiptHandle", + "body": "Hello from SQS!", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1523232000000", + "SenderId": "123456789012", + "ApproximateFirstReceiveTimestamp": "1523232000001" + }, + "messageAttributes": {}, + "md5OfBody": "{{{md5_of_body}}}", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:us-east-1:123456789012:MyQueue", + "awsRegion": "us-east-1" + }, + { + "messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78", + "receiptHandle": "MessageReceiptHandle", + "body": "2nd Message", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1523232000000", + "SenderId": "123456789012", + "ApproximateFirstReceiveTimestamp": "1523232000001" + }, + "messageAttributes": {}, + "md5OfBody": "{{{md5_of_body}}}", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:us-east-1:123456789012:MyQueue", + "awsRegion": "us-east-1" + } + ] + }, + "cases": [ + { + "expression": "Records[0].powertools_json(body)", + "result": "Hello from SQS!" + }, + { + "expression": "Records[*].powertools_json(body)", + "result": ["Hello from SQS!","2nd Message"] + } + ] + } +] \ No newline at end of file From 0055589be2c2970f0140ec80655d5791562a7f20 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Thu, 18 Apr 2024 13:00:28 +0100 Subject: [PATCH 04/28] mkdocs update to move sidebar to the right and year update --- mkdocs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 4862dbf5..42177854 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -19,6 +19,7 @@ nav: - utilities/parameters.md - utilities/idempotency.md - utilities/batch-processing.md + - utilities/jmespath-functions.md theme: name: material @@ -47,7 +48,6 @@ theme: - navigation.tracking - content.code.annotate - toc.follow - - toc.integrate - announce.dismiss icon: repo: fontawesome/brands/github @@ -81,7 +81,7 @@ markdown_extensions: format: !!python/name:pymdownx.superfences.fence_code_format - md_in_html -copyright: Copyright © 2023 Amazon Web Services +copyright: Copyright © 2024 Amazon Web Services plugins: - git-revision-date From 5e8ba98339cd8ea18d534ba37d5f89e21c1c62cb Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Thu, 18 Apr 2024 13:01:28 +0100 Subject: [PATCH 05/28] update examples nuget packages --- examples/Idempotency/src/HelloWorld/HelloWorld.csproj | 4 ++-- examples/Logging/src/HelloWorld/HelloWorld.csproj | 2 +- examples/Metrics/src/HelloWorld/HelloWorld.csproj | 4 ++-- .../src/LambdaPowertoolsAPI/LambdaPowertoolsAPI.csproj | 6 +++--- examples/Tracing/src/HelloWorld/HelloWorld.csproj | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/Idempotency/src/HelloWorld/HelloWorld.csproj b/examples/Idempotency/src/HelloWorld/HelloWorld.csproj index 9f776ce0..4a3f8ed3 100644 --- a/examples/Idempotency/src/HelloWorld/HelloWorld.csproj +++ b/examples/Idempotency/src/HelloWorld/HelloWorld.csproj @@ -8,7 +8,7 @@ - - + + diff --git a/examples/Logging/src/HelloWorld/HelloWorld.csproj b/examples/Logging/src/HelloWorld/HelloWorld.csproj index a970a2f0..be745788 100644 --- a/examples/Logging/src/HelloWorld/HelloWorld.csproj +++ b/examples/Logging/src/HelloWorld/HelloWorld.csproj @@ -8,7 +8,7 @@ - + diff --git a/examples/Metrics/src/HelloWorld/HelloWorld.csproj b/examples/Metrics/src/HelloWorld/HelloWorld.csproj index d6eee6b5..9116bbec 100644 --- a/examples/Metrics/src/HelloWorld/HelloWorld.csproj +++ b/examples/Metrics/src/HelloWorld/HelloWorld.csproj @@ -8,8 +8,8 @@ - - + + diff --git a/examples/ServerlessApi/src/LambdaPowertoolsAPI/LambdaPowertoolsAPI.csproj b/examples/ServerlessApi/src/LambdaPowertoolsAPI/LambdaPowertoolsAPI.csproj index ce9c7cb4..100f2d2c 100644 --- a/examples/ServerlessApi/src/LambdaPowertoolsAPI/LambdaPowertoolsAPI.csproj +++ b/examples/ServerlessApi/src/LambdaPowertoolsAPI/LambdaPowertoolsAPI.csproj @@ -13,8 +13,8 @@ - - - + + + diff --git a/examples/Tracing/src/HelloWorld/HelloWorld.csproj b/examples/Tracing/src/HelloWorld/HelloWorld.csproj index e6bf4310..f63df00c 100644 --- a/examples/Tracing/src/HelloWorld/HelloWorld.csproj +++ b/examples/Tracing/src/HelloWorld/HelloWorld.csproj @@ -8,8 +8,8 @@ - - + + From 568caa4d1f9b0882b7def0fcaccc2aa8e63acf13 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Thu, 18 Apr 2024 13:01:49 +0100 Subject: [PATCH 06/28] update jmespath readme --- libraries/src/AWS.Lambda.Powertools.JMESPath/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/README.md b/libraries/src/AWS.Lambda.Powertools.JMESPath/README.md index 3ad4bca4..7f63b79f 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/README.md +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/README.md @@ -73,4 +73,7 @@ Use powertools_json function to decode any JSON string anywhere a JMESPath expre Use powertools_base64 function to decode any base64 data. ### powertools_base64_gzip function -Use powertools_base64_gzip function to decompress and decode base64 data. \ No newline at end of file +Use powertools_base64_gzip function to decompress and decode base64 data. + +## Credit +We took heavy inspiration in the https://github.com/danielaparker/JsonCons.Net repository. \ No newline at end of file From 6953ee1af8148380eb6dd1289a2e501948b32816 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:56:39 +0100 Subject: [PATCH 07/28] Add documentation and update README.md --- docs/utilities/jmespath-functions.md | 197 ++++++++++++++++++ .../AWS.Lambda.Powertools.JMESPath/README.md | 3 +- 2 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 docs/utilities/jmespath-functions.md diff --git a/docs/utilities/jmespath-functions.md b/docs/utilities/jmespath-functions.md new file mode 100644 index 00000000..411b7fec --- /dev/null +++ b/docs/utilities/jmespath-functions.md @@ -0,0 +1,197 @@ +--- +title: JMESPath Functions +description: Utility +--- + + + +???+ tip + JMESPath is a query language for JSON used by AWS CLI, AWS Python SDK, and Powertools for AWS Lambda. + +Built-in JMESPath functions to easily deserialize common encoded JSON payloads in Lambda functions. + +## Key features + +* Deserialize JSON from JSON strings, base64, and compressed data +* Use JMESPath to extract and combine data recursively +* Provides commonly used JMESPath expression with popular event sources + +## Getting started + +???+ tip + All examples shared in this documentation are available within the [project repository](https://github.com/aws-powertools/powertools-lambda-dotnet/tree/develop/libraries/tests/AWS.Lambda.Powertools.JMESPath.Tests/JmesPathExamples.cs){target="_blank"}. + +You might have events that contains encoded JSON payloads as string, base64, or even in compressed format. It is a common use case to decode and extract them partially or fully as part of your Lambda function invocation. + +???+ info "Terminology" + **Envelope** is the terminology we use for the **JMESPath expression** to extract your JSON object from your data input. We might use those two terms interchangeably. + +### Extracting data + +You can use the `JsonTransformer.Transform` function with any [JMESPath expression](https://jmespath.org/tutorial.html){target="_blank" rel="nofollow"}. + +???+ tip + Another common use case is to fetch deeply nested data, filter, flatten, and more. + +=== "Transform" + ```csharp hl_lines="1 2" + var transformer = JsonTransformer.Parse("powertools_json(body).customerId"); + using var result = transformer.Transform(doc.RootElement); + + Logger.LogInformation(result.RootElement.GetRawText()); // "dd4649e6-2484-4993-acb8-0f9123103394" + ``` + +=== "Payload" + ```json + { + "body": "{\"customerId\":\"dd4649e6-2484-4993-acb8-0f9123103394\"}", + "deeply_nested": [ + { + "some_data": [ + 1, + 2, + 3 + ] + } + ] + } + ``` + +### Built-in envelopes + +We provide built-in envelopes for popular AWS Lambda event sources to easily decode and/or deserialize JSON objects. + +| Envelop | JMESPath expression | +|---------------------|-----------------------------------------------------------------------------| +| API_GATEWAY_HTTP | powertools_json(body) | +| API_GATEWAY_REST | powertools_json(body) | +| CLOUDWATCH_LOGS | awslogs.powertools_base64_gzip(data) | powertools_json(@).logEvents[*] | +| KINESIS_DATA_STREAM | Records[*].kinesis.powertools_json(powertools_base64(data)) | +| SNS | Records[*].Sns.Message | powertools_json(@) | +| SQS | Records[*].powertools_json(body) | + +???+ tip "Using SNS?" + If you don't require SNS metadata, enable [raw message delivery](https://docs.aws.amazon.com/sns/latest/dg/sns-large-payload-raw-message-delivery.html). It will reduce multiple payload layers and size, when using SNS in combination with other services (_e.g., SQS, S3, etc_). + +## Advanced + +### Built-in JMESPath functions + +You can use our built-in JMESPath functions within your envelope expression. They handle deserialization for common data formats found in AWS Lambda event sources such as JSON strings, base64, and uncompress gzip data. + +#### powertools_json function + +Use `powertools_json` function to decode any JSON string anywhere a JMESPath expression is allowed. + +> **Idempotency scenario** + +This sample will deserialize the JSON string within the `body` key before [Idempotency](./idempotency.md){target="_blank"} processes it. + +=== "Idempotency utility: WithEventKeyJmesPath" + + ```csharp hl_lines="4" + Idempotency.Configure(builder => + builder + .WithOptions(optionsBuilder => + optionsBuilder.WithEventKeyJmesPath("powertools_json(Body).[\"user_id\", \"product_id\"]")) + .UseDynamoDb("idempotency_table")); + ``` + +=== "Payload" + + ```json hl_lines="28" + { + "version": "2.0", + "routeKey": "ANY /createpayment", + "rawPath": "/createpayment", + "rawQueryString": "", + "headers": { + "Header1": "value1", + "Header2": "value2" + }, + "requestContext": { + "accountId": "123456789012", + "apiId": "api-id", + "domainName": "id.execute-api.us-east-1.amazonaws.com", + "domainPrefix": "id", + "http": { + "method": "POST", + "path": "/createpayment", + "protocol": "HTTP/1.1", + "sourceIp": "ip", + "userAgent": "agent" + }, + "requestId": "id", + "routeKey": "ANY /createpayment", + "stage": "$default", + "time": "10/Feb/2021:13:40:43 +0000", + "timeEpoch": 1612964443723 + }, + "body": "{\"user_id\":\"xyz\",\"product_id\":\"123456789\"}", + "isBase64Encoded": false + } + ``` + +#### powertools_base64 function + +Use `powertools_base64` function to decode any base64 data. + +This sample will decode the base64 value within the `data` key, and deserialize the JSON string before validation. + +=== "Function" + + ```csharp + var transformer = JsonTransformer.Parse("powertools_base64(body).customerId"); + using var result = transformer.Transform(doc.RootElement); + + Logger.LogInformation(result.RootElement.GetRawText()); // "dd4649e6-2484-4993-acb8-0f9123103394" + ``` + +=== "Payload" + + ```json + { + "body": "eyJjdXN0b21lcklkIjoiZGQ0NjQ5ZTYtMjQ4NC00OTkzLWFjYjgtMGY5MTIzMTAzMzk0In0=", + "deeply_nested": [ + { + "some_data": [ + 1, + 2, + 3 + ] + } + ] + } + ``` + +#### powertools_base64_gzip function + +Use `powertools_base64_gzip` function to decompress and decode base64 data. + +This sample will decompress and decode base64 data from Cloudwatch Logs, then use JMESPath pipeline expression to pass the result for decoding its JSON string. + +=== "Function" + + ```csharp + var transformer = JsonTransformer.Parse("powertools_base64_gzip(body).customerId"); + using var result = transformer.Transform(doc.RootElement); + + Logger.LogInformation(result.RootElement.GetRawText()); // "dd4649e6-2484-4993-acb8-0f9123103394" + ``` + +=== "Payload" + + ```json + { + "body": "H4sIAAAAAAAAA6tWSi4tLsnPTS3yTFGyUkpJMTEzsUw10zUysTDRNbG0NNZNTE6y0DVIszQ0MjY0MDa2NFGqBQCMzDWgNQAAAA==", + "deeply_nested": [ + { + "some_data": [ + 1, + 2, + 3 + ] + } + ] + } + ``` \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/README.md b/libraries/src/AWS.Lambda.Powertools.JMESPath/README.md index 7f63b79f..2ec00c16 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/README.md +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/README.md @@ -1,7 +1,8 @@ # Powertools JMESPath support JMESPath is a query language for JSON used by AWS CLI, AWS Python SDK, and Powertools for AWS Lambda. - With built-in JMESPath functions to easily deserialize common encoded JSON payloads in Lambda functions. + +With built-in JMESPath functions to easily deserialize common encoded JSON payloads in Lambda functions. ## Key features From 1e9808c79c83378714e5767b3652e478e4971476 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Thu, 18 Apr 2024 15:35:49 +0100 Subject: [PATCH 08/28] Add project properties. Prevent JMESPath project to add Common project. --- .../AWS.Lambda.Powertools.JMESPath.csproj | 6 +++++- .../AWS.Lambda.Powertools.JMESPath/InternalsVisibleTo.cs | 1 - libraries/src/Directory.Build.targets | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/AWS.Lambda.Powertools.JMESPath.csproj b/libraries/src/AWS.Lambda.Powertools.JMESPath/AWS.Lambda.Powertools.JMESPath.csproj index 891af5d4..f4ce628b 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/AWS.Lambda.Powertools.JMESPath.csproj +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/AWS.Lambda.Powertools.JMESPath.csproj @@ -1,7 +1,11 @@  - + + AWS.Lambda.Powertools.JMESPath + Powertools for AWS Lambda (.NET) - JMESPath package. + AWS.Lambda.Powertools.JMESPath + AWS.Lambda.Powertools.JMESPath diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/InternalsVisibleTo.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/InternalsVisibleTo.cs index 62746e87..4a4695be 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/InternalsVisibleTo.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/InternalsVisibleTo.cs @@ -15,5 +15,4 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("AWS.Lambda.Powertools.Idempotency")] [assembly: InternalsVisibleTo("AWS.Lambda.Powertools.JMESPath.Tests")] \ No newline at end of file diff --git a/libraries/src/Directory.Build.targets b/libraries/src/Directory.Build.targets index 20593976..5844e458 100644 --- a/libraries/src/Directory.Build.targets +++ b/libraries/src/Directory.Build.targets @@ -1,6 +1,6 @@ - + From bdc5cbfb021a76975b910e4f6a60b3314b3833bc Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Thu, 18 Apr 2024 16:18:08 +0100 Subject: [PATCH 09/28] Tackle SonarCloud raised issues --- .../BinaryOperator.cs | 164 +++++----- .../Expression.cs | 55 ++-- .../Function.cs | 296 +++++++++--------- 3 files changed, 261 insertions(+), 254 deletions(-) diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs index 86c04554..66102648 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs @@ -141,31 +141,35 @@ private LtOperator() { } - public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) + public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) { - if (lhs.Type == JmesPathType.Number && rhs.Type == JmesPathType.Number) + switch (lhs.Type) { - if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) - { - result = dec1 < dec2 ? JsonConstants.True : JsonConstants.False; - } - else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) + case JmesPathType.Number when rhs.Type == JmesPathType.Number: { - result = val1 < val2 ? JsonConstants.True : JsonConstants.False; + if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) + { + result = dec1 < dec2 ? JsonConstants.True : JsonConstants.False; + } + else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) + { + result = val1 < val2 ? JsonConstants.True : JsonConstants.False; + } + else + { + result = JsonConstants.Null; + } + + break; } - else - { + case JmesPathType.String when rhs.Type == JmesPathType.String: + result = string.CompareOrdinal(lhs.GetString(), rhs.GetString()) < 0 ? JsonConstants.True : JsonConstants.False; + break; + default: result = JsonConstants.Null; - } - } - else if (lhs.Type == JmesPathType.String && rhs.Type == JmesPathType.String) - { - result = string.CompareOrdinal(lhs.GetString(), rhs.GetString()) < 0 ? JsonConstants.True : JsonConstants.False; - } - else - { - result = JsonConstants.Null; + break; } + return true; } @@ -184,31 +188,35 @@ private LteOperator() { } - public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) + public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) { - if (lhs.Type == JmesPathType.Number && rhs.Type == JmesPathType.Number) + switch (lhs.Type) { - if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) + case JmesPathType.Number when rhs.Type == JmesPathType.Number: { - result = dec1 <= dec2 ? JsonConstants.True : JsonConstants.False; + if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) + { + result = dec1 <= dec2 ? JsonConstants.True : JsonConstants.False; + } + else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) + { + result = val1 <= val2 ? JsonConstants.True : JsonConstants.False; + } + else + { + result = JsonConstants.Null; + } + + break; } - else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) - { - result = val1 <= val2 ? JsonConstants.True : JsonConstants.False; - } - else - { + case JmesPathType.String when rhs.Type == JmesPathType.String: + result = string.CompareOrdinal(lhs.GetString(), rhs.GetString()) <= 0 ? JsonConstants.True : JsonConstants.False; + break; + default: result = JsonConstants.Null; - } - } - else if (lhs.Type == JmesPathType.String && rhs.Type == JmesPathType.String) - { - result = string.CompareOrdinal(lhs.GetString(), rhs.GetString()) <= 0 ? JsonConstants.True : JsonConstants.False; - } - else - { - result = JsonConstants.Null; + break; } + return true; } @@ -230,29 +238,33 @@ private GtOperator() public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) { - if (lhs.Type == JmesPathType.Number && rhs.Type == JmesPathType.Number) + switch (lhs.Type) { - if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) - { - result = dec1 > dec2 ? JsonConstants.True : JsonConstants.False; - } - else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) + case JmesPathType.Number when rhs.Type == JmesPathType.Number: { - result = val1 > val2 ? JsonConstants.True : JsonConstants.False; + if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) + { + result = dec1 > dec2 ? JsonConstants.True : JsonConstants.False; + } + else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) + { + result = val1 > val2 ? JsonConstants.True : JsonConstants.False; + } + else + { + result = JsonConstants.Null; + } + + break; } - else - { + case JmesPathType.String when rhs.Type == JmesPathType.String: + result = string.CompareOrdinal(lhs.GetString(), rhs.GetString()) > 0 ? JsonConstants.True : JsonConstants.False; + break; + default: result = JsonConstants.Null; - } - } - else if (lhs.Type == JmesPathType.String && rhs.Type == JmesPathType.String) - { - result = string.CompareOrdinal(lhs.GetString(), rhs.GetString()) > 0 ? JsonConstants.True : JsonConstants.False; - } - else - { - result = JsonConstants.Null; + break; } + return true; } @@ -273,29 +285,33 @@ private GteOperator() public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) { - if (lhs.Type == JmesPathType.Number && rhs.Type == JmesPathType.Number) + switch (lhs.Type) { - if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) - { - result = dec1 >= dec2 ? JsonConstants.True : JsonConstants.False; - } - else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) + case JmesPathType.Number when rhs.Type == JmesPathType.Number: { - result = val1 >= val2 ? JsonConstants.True : JsonConstants.False; + if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) + { + result = dec1 >= dec2 ? JsonConstants.True : JsonConstants.False; + } + else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) + { + result = val1 >= val2 ? JsonConstants.True : JsonConstants.False; + } + else + { + result = JsonConstants.Null; + } + + break; } - else - { + case JmesPathType.String when rhs.Type == JmesPathType.String: + result = string.CompareOrdinal(lhs.GetString(), rhs.GetString()) >= 0 ? JsonConstants.True : JsonConstants.False; + break; + default: result = JsonConstants.Null; - } - } - else if (lhs.Type == JmesPathType.String && rhs.Type == JmesPathType.String) - { - result = string.CompareOrdinal(lhs.GetString(), rhs.GetString()) >= 0 ? JsonConstants.True : JsonConstants.False; - } - else - { - result = JsonConstants.Null; + break; } + return true; } diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs index 83ad6c1c..08ce6234 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs @@ -68,7 +68,7 @@ public abstract bool TryEvaluate(DynamicResources resources, IValue current, out IValue value); - public virtual void AddExpression(IExpression expressions) + public virtual void AddExpression(IExpression expr) { } @@ -313,25 +313,8 @@ public override bool TryEvaluate(DynamicResources resources, { foreach (var elem in item.EnumerateArray()) { - if (elem.Type != JmesPathType.Null) - { - if (!TryApplyExpressions(resources, elem, out var val)) - { - value = JsonConstants.Null; - return false; - } - if (val.Type != JmesPathType.Null) - { - result.Add(val); - } - } - } - } - else - { - if (item.Type != JmesPathType.Null) - { - if (!TryApplyExpressions(resources, item, out var val)) + if (elem.Type == JmesPathType.Null) continue; + if (!TryApplyExpressions(resources, elem, out var val)) { value = JsonConstants.Null; return false; @@ -342,6 +325,19 @@ public override bool TryEvaluate(DynamicResources resources, } } } + else + { + if (item.Type == JmesPathType.Null) continue; + if (!TryApplyExpressions(resources, item, out var val)) + { + value = JsonConstants.Null; + return false; + } + if (val.Type != JmesPathType.Null) + { + result.Add(val); + } + } } value = new ArrayValue(result); @@ -470,17 +466,16 @@ public override bool TryEvaluate(DynamicResources resources, value = JsonConstants.Null; return false; } - if (Expression.IsTrue(test)) + + if (!Expression.IsTrue(test)) continue; + if (!TryApplyExpressions(resources, item, out var val)) { - if (!TryApplyExpressions(resources, item, out var val)) - { - value = JsonConstants.Null; - return false; - } - if (val.Type != JmesPathType.Null) - { - result.Add(val); - } + value = JsonConstants.Null; + return false; + } + if (val.Type != JmesPathType.Null) + { + result.Add(val); } } value = new ArrayValue(result); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Function.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Function.cs index 4eddb7dc..5c62ef18 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Function.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Function.cs @@ -106,7 +106,7 @@ internal AbsFunction() { } - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); @@ -114,17 +114,17 @@ public override bool TryEvaluate(DynamicResources resources, IList args, if (arg.TryGetDecimal(out var decVal)) { - result = new DecimalValue(decVal >= 0 ? decVal : -decVal); + element = new DecimalValue(decVal >= 0 ? decVal : -decVal); return true; } if (arg.TryGetDouble(out var dblVal)) { - result = new DecimalValue(dblVal >= 0 ? decVal : new decimal(-dblVal)); + element = new DecimalValue(dblVal >= 0 ? decVal : new decimal(-dblVal)); return true; } - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -141,36 +141,36 @@ internal AvgFunction() { } - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); var arg0 = args[0]; if (arg0.Type != JmesPathType.Array || arg0.GetArrayLength() == 0) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } if (!SumFunction.Instance.TryEvaluate(resources, args, out var sum)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } if (sum.TryGetDecimal(out var decVal)) { - result = new DecimalValue(decVal / arg0.GetArrayLength()); + element = new DecimalValue(decVal / arg0.GetArrayLength()); return true; } if (sum.TryGetDouble(out var dblVal)) { - result = new DoubleValue(dblVal / arg0.GetArrayLength()); + element = new DoubleValue(dblVal / arg0.GetArrayLength()); return true; } - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -188,30 +188,30 @@ internal CeilFunction() } public override bool TryEvaluate(DynamicResources resources, IList args, - out IValue result) + out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); var val = args[0]; if (val.Type != JmesPathType.Number) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } if (val.TryGetDecimal(out var decVal)) { - result = new DecimalValue(decimal.Ceiling(decVal)); + element = new DecimalValue(decimal.Ceiling(decVal)); return true; } if (val.TryGetDouble(out var dblVal)) { - result = new DoubleValue(Math.Ceiling(dblVal)); + element = new DoubleValue(Math.Ceiling(dblVal)); return true; } - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -229,7 +229,7 @@ internal ContainsFunction() } public override bool TryEvaluate(DynamicResources resources, IList args, - out IValue result) + out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); @@ -245,18 +245,18 @@ public override bool TryEvaluate(DynamicResources resources, IList args, { if (comparer.Equals(item, arg1)) { - result = JsonConstants.True; + element = JsonConstants.True; return true; } } - result = JsonConstants.False; + element = JsonConstants.False; return true; case JmesPathType.String: { if (arg1.Type != JmesPathType.String) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -264,16 +264,16 @@ public override bool TryEvaluate(DynamicResources resources, IList args, var s1 = arg1.GetString(); if (s0.Contains(s1)) { - result = JsonConstants.True; + element = JsonConstants.True; return true; } - result = JsonConstants.False; + element = JsonConstants.False; return true; } default: { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } } @@ -293,7 +293,7 @@ internal EndsWithFunction() } public override bool TryEvaluate(DynamicResources resources, IList args, - out IValue result) + out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); @@ -302,7 +302,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, if (arg0.Type != JmesPathType.String || arg1.Type != JmesPathType.String) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -311,11 +311,11 @@ public override bool TryEvaluate(DynamicResources resources, IList args, if (s0.EndsWith(s1)) { - result = JsonConstants.True; + element = JsonConstants.True; } else { - result = JsonConstants.False; + element = JsonConstants.False; } return true; @@ -335,30 +335,30 @@ internal FloorFunction() } public override bool TryEvaluate(DynamicResources resources, IList args, - out IValue result) + out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); var val = args[0]; if (val.Type != JmesPathType.Number) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } if (val.TryGetDecimal(out var decVal)) { - result = new DecimalValue(decimal.Floor(decVal)); + element = new DecimalValue(decimal.Floor(decVal)); return true; } if (val.TryGetDouble(out var dblVal)) { - result = new DoubleValue(Math.Floor(dblVal)); + element = new DoubleValue(Math.Floor(dblVal)); return true; } - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -375,7 +375,7 @@ internal JoinFunction() { } - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); @@ -384,7 +384,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, if (!(arg0.Type == JmesPathType.String && args[1].Type == JmesPathType.Array)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -394,7 +394,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, { if (j.Type != JmesPathType.String) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -407,7 +407,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, buf.Append(sv); } - result = new StringValue(buf.ToString()); + element = new StringValue(buf.ToString()); return true; } @@ -425,14 +425,14 @@ internal KeysFunction() } public override bool TryEvaluate(DynamicResources resources, IList args, - out IValue result) + out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); var arg0 = args[0]; if (arg0.Type != JmesPathType.Object) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -443,7 +443,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, values.Add(new StringValue(property.Name)); } - result = new ArrayValue(values); + element = new ArrayValue(values); return true; } @@ -461,7 +461,7 @@ internal LengthFunction() } public override bool TryEvaluate(DynamicResources resources, IList args, - out IValue result) + out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); @@ -477,21 +477,21 @@ public override bool TryEvaluate(DynamicResources resources, IList args, ++count; } - result = new DecimalValue(new decimal(count)); + element = new DecimalValue(new decimal(count)); return true; } case JmesPathType.Array: - result = new DecimalValue(new decimal(arg0.GetArrayLength())); + element = new DecimalValue(new decimal(arg0.GetArrayLength())); return true; case JmesPathType.String: { var bytes = Encoding.UTF32.GetBytes(arg0.GetString().ToCharArray()); - result = new DecimalValue(new decimal(bytes.Length / 4)); + element = new DecimalValue(new decimal(bytes.Length / 4)); return true; } default: { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } } @@ -511,20 +511,20 @@ internal MaxFunction() } public override bool TryEvaluate(DynamicResources resources, IList args, - out IValue result) + out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); var arg0 = args[0]; if (arg0.Type != JmesPathType.Array) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } if (arg0.GetArrayLength() == 0) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -532,7 +532,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, var isString = arg0[0].Type == JmesPathType.String; if (!isNumber && !isString) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -543,13 +543,13 @@ public override bool TryEvaluate(DynamicResources resources, IList args, if (!(((arg0[i].Type == JmesPathType.Number) == isNumber) && (arg0[i].Type == JmesPathType.String) == isString)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } if (!greater.TryEvaluate(arg0[i], arg0[index], out var value)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -559,7 +559,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, } } - result = arg0[index]; + element = arg0[index]; return true; } @@ -576,20 +576,20 @@ internal MaxByFunction() { } - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); if (!(args[0].Type == JmesPathType.Array && args[1].Type == JmesPathType.Expression)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } var arg0 = args[0]; if (arg0.GetArrayLength() == 0) { - result = JsonConstants.Null; + element = JsonConstants.Null; return true; } @@ -597,7 +597,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, if (!expr.TryEvaluate(resources, arg0[0], out var key1)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -605,7 +605,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, var isString1 = key1.Type == JmesPathType.String; if (!(isNumber1 || isString1)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -615,7 +615,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, { if (!expr.TryEvaluate(resources, arg0[i], out var key2)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -623,24 +623,22 @@ public override bool TryEvaluate(DynamicResources resources, IList args, var isString2 = key2.Type == JmesPathType.String; if (!(isNumber2 == isNumber1 && isString2 == isString1)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } if (!greater.TryEvaluate(key2, key1, out var value)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } - if (value.Type == JmesPathType.True) - { - key1 = key2; - index = i; - } + if (value.Type != JmesPathType.True) continue; + key1 = key2; + index = i; } - result = arg0[index]; + element = arg0[index]; return true; } @@ -658,20 +656,20 @@ internal MinFunction() } public override bool TryEvaluate(DynamicResources resources, IList args, - out IValue result) + out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); var arg0 = args[0]; if (arg0.Type != JmesPathType.Array) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } if (arg0.GetArrayLength() == 0) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -679,7 +677,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, var isString = arg0[0].Type == JmesPathType.String; if (!isNumber && !isString) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -690,13 +688,13 @@ public override bool TryEvaluate(DynamicResources resources, IList args, if (!(((arg0[i].Type == JmesPathType.Number) == isNumber) && (arg0[i].Type == JmesPathType.String) == isString)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } if (!less.TryEvaluate(arg0[i], arg0[index], out var value)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -706,7 +704,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, } } - result = arg0[index]; + element = arg0[index]; return true; } @@ -723,24 +721,24 @@ internal MergeFunction() { } - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { if (!args.Any()) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } var arg0 = args[0]; if (arg0.Type != JmesPathType.Object) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } if (args.Count == 1) { - result = arg0; + element = arg0; return true; } @@ -750,7 +748,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, var argi = args[i]; if (argi.Type != JmesPathType.Object) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -764,7 +762,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, } } - result = new ObjectValue(dict); + element = new ObjectValue(dict); return true; } @@ -781,16 +779,16 @@ internal NotNullFunction() { } - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { foreach (var arg in args) { if (arg.Type == JmesPathType.Null) continue; - result = arg; + element = arg; return true; } - result = JsonConstants.Null; + element = JsonConstants.Null; return true; } @@ -807,7 +805,7 @@ internal ReverseFunction() { } - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); @@ -816,7 +814,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, { case JmesPathType.String: { - result = new StringValue(string.Join("", GraphemeClusters(arg0.GetString()).Reverse().ToArray())); + element = new StringValue(string.Join("", GraphemeClusters(arg0.GetString()).Reverse().ToArray())); return true; } case JmesPathType.Array: @@ -827,11 +825,11 @@ public override bool TryEvaluate(DynamicResources resources, IList args, list.Add(arg0[i]); } - result = new ArrayValue(list); + element = new ArrayValue(list); return true; } default: - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } } @@ -858,13 +856,13 @@ internal MapFunction() { } - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); if (!(args[0].Type == JmesPathType.Expression && args[1].Type == JmesPathType.Array)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -877,14 +875,14 @@ public override bool TryEvaluate(DynamicResources resources, IList args, { if (!expr.TryEvaluate(resources, item, out var val)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } list.Add(val); } - result = new ArrayValue(list); + element = new ArrayValue(list); return true; } @@ -901,20 +899,20 @@ internal MinByFunction() { } - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); if (!(args[0].Type == JmesPathType.Array && args[1].Type == JmesPathType.Expression)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } var arg0 = args[0]; if (arg0.GetArrayLength() == 0) { - result = JsonConstants.Null; + element = JsonConstants.Null; return true; } @@ -922,7 +920,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, if (!expr.TryEvaluate(resources, arg0[0], out var key1)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -930,7 +928,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, var isString1 = key1.Type == JmesPathType.String; if (!(isNumber1 || isString1)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -940,7 +938,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, { if (!expr.TryEvaluate(resources, arg0[i], out var key2)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -948,13 +946,13 @@ public override bool TryEvaluate(DynamicResources resources, IList args, var isString2 = key2.Type == JmesPathType.String; if (!(isNumber2 == isNumber1 && isString2 == isString1)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } if (!lessor.TryEvaluate(key2, key1, out var value)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -965,7 +963,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, } } - result = arg0[index]; + element = arg0[index]; return true; } @@ -982,20 +980,20 @@ internal SortFunction() { } - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); var arg0 = args[0]; if (arg0.Type != JmesPathType.Array) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } if (arg0.GetArrayLength() <= 1) { - result = arg0; + element = arg0; return true; } @@ -1003,7 +1001,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, var isString1 = arg0[0].Type == JmesPathType.String; if (!isNumber1 && !isString1) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -1016,7 +1014,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, var isString2 = item.Type == JmesPathType.String; if (!(isNumber2 == isNumber1 && isString2 == isString1)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -1024,7 +1022,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, } list.Sort(comparer); - result = new ArrayValue(list); + element = new ArrayValue(list); return true; } @@ -1041,20 +1039,20 @@ internal SortByFunction() { } - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); if (!(args[0].Type == JmesPathType.Array && args[1].Type == JmesPathType.Expression)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } var arg0 = args[0]; if (arg0.GetArrayLength() <= 1) { - result = arg0; + element = arg0; return true; } @@ -1070,11 +1068,11 @@ public override bool TryEvaluate(DynamicResources resources, IList args, list.Sort(comparer); if (comparer.IsValid) { - result = new ArrayValue(list); + element = new ArrayValue(list); return true; } - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -1092,7 +1090,7 @@ internal StartsWithFunction() } public override bool TryEvaluate(DynamicResources resources, IList args, - out IValue result) + out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); @@ -1101,13 +1099,13 @@ public override bool TryEvaluate(DynamicResources resources, IList args, if (arg0.Type != JmesPathType.String || arg1.Type != JmesPathType.String) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } var s0 = arg0.GetString(); var s1 = arg1.GetString(); - result = s0.StartsWith(s1) ? JsonConstants.True : JsonConstants.False; + element = s0.StartsWith(s1) ? JsonConstants.True : JsonConstants.False; return true; } @@ -1128,14 +1126,14 @@ internal SumFunction() } public override bool TryEvaluate(DynamicResources resources, IList args, - out IValue result) + out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); var arg0 = args[0]; if (arg0.Type != JmesPathType.Array) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -1143,7 +1141,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, { if (item.Type != JmesPathType.Number) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } } @@ -1163,7 +1161,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, if (success) { - result = new DecimalValue(decSum); + element = new DecimalValue(decSum); return true; } @@ -1172,14 +1170,14 @@ public override bool TryEvaluate(DynamicResources resources, IList args, { if (!item.TryGetDouble(out var dbl)) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } dblSum += dbl; } - result = new DoubleValue(dblSum); + element = new DoubleValue(dblSum); return true; } @@ -1196,19 +1194,19 @@ internal ToArrayFunction() { } - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); var arg0 = args[0]; if (arg0.Type == JmesPathType.Array) { - result = arg0; + element = arg0; return true; } var list = new List { arg0 }; - result = new ArrayValue(list); + element = new ArrayValue(list); return true; } @@ -1226,7 +1224,7 @@ internal ToNumberFunction() } public override bool TryEvaluate(DynamicResources resources, IList args, - out IValue result) + out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); @@ -1234,28 +1232,28 @@ public override bool TryEvaluate(DynamicResources resources, IList args, switch (arg0.Type) { case JmesPathType.Number: - result = arg0; + element = arg0; return true; case JmesPathType.String: { var s = arg0.GetString(); if (decimal.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out var dec)) { - result = new DecimalValue(dec); + element = new DecimalValue(dec); return true; } if (double.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out var dbl)) { - result = new DoubleValue(dbl); + element = new DoubleValue(dbl); return true; } - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } default: - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } } @@ -1273,13 +1271,13 @@ internal ToStringFunction() { } - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); if (args[0].Type == JmesPathType.Expression) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -1287,13 +1285,13 @@ public override bool TryEvaluate(DynamicResources resources, IList args, switch (arg0.Type) { case JmesPathType.String: - result = arg0; + element = arg0; return true; case JmesPathType.Expression: - result = JsonConstants.Null; + element = JsonConstants.Null; return false; default: - result = new StringValue(arg0.ToString()); + element = new StringValue(arg0.ToString()); return true; } } @@ -1311,14 +1309,14 @@ internal ValuesFunction() { } - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); var arg0 = args[0]; if (arg0.Type != JmesPathType.Object) { - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } @@ -1329,7 +1327,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, list.Add(item.Value); } - result = new ArrayValue(list); + element = new ArrayValue(list); return true; } @@ -1346,7 +1344,7 @@ internal TypeFunction() { } - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); @@ -1355,26 +1353,26 @@ public override bool TryEvaluate(DynamicResources resources, IList args, switch (arg0.Type) { case JmesPathType.Number: - result = new StringValue("number"); + element = new StringValue("number"); return true; case JmesPathType.True: case JmesPathType.False: - result = new StringValue("boolean"); + element = new StringValue("boolean"); return true; case JmesPathType.String: - result = new StringValue("string"); + element = new StringValue("string"); return true; case JmesPathType.Object: - result = new StringValue("object"); + element = new StringValue("object"); return true; case JmesPathType.Array: - result = new StringValue("array"); + element = new StringValue("array"); return true; case JmesPathType.Null: - result = new StringValue("null"); + element = new StringValue("null"); return true; default: - result = JsonConstants.Null; + element = JsonConstants.Null; return false; } } @@ -1398,12 +1396,10 @@ public override string ToString() return "powertools_json"; } - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - result = args[0]; - - //result = new JsonElementValue(JsonNode.Parse(args[0].GetString()).Deserialize()); + element = args[0]; return true; } } @@ -1421,12 +1417,12 @@ public override string ToString() return "powertools_base64"; } - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); var base64StringBytes = Convert.FromBase64String(args[0].GetString()); var doc = JsonDocument.Parse(base64StringBytes); - result = new JsonElementValue(doc.RootElement); + element = new JsonElementValue(doc.RootElement); return true; } } @@ -1444,7 +1440,7 @@ public override string ToString() return "powertools_base64_gzip"; } - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue result) + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); @@ -1458,7 +1454,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, } var doc = JsonDocument.Parse(Encoding.UTF8.GetString(decompressedStream.ToArray())); - result = new JsonElementValue(doc.RootElement); + element = new JsonElementValue(doc.RootElement); return true; } From 4b5c6fdd4c3ff4020d175f7efae9996f4d196797 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Thu, 18 Apr 2024 16:58:54 +0100 Subject: [PATCH 10/28] more Sonar fixes --- .../Utilities/JsonPointerExtensions.cs | 355 +++++++----------- 1 file changed, 137 insertions(+), 218 deletions(-) diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointerExtensions.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointerExtensions.cs index 0f7fa997..8926f510 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointerExtensions.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointerExtensions.cs @@ -24,35 +24,36 @@ private static bool TryResolve(string token, JsonDocumentBuilder current, out Js { result = current; - if (result.ValueKind == JsonValueKind.Array) + switch (result.ValueKind) { - if (token == "-") - { + case JsonValueKind.Array when token == "-": return false; - } - - if (!int.TryParse(token, out var index)) + case JsonValueKind.Array: { - return false; - } + if (!int.TryParse(token, out var index)) + { + return false; + } - if (index >= result.GetArrayLength()) - { - return false; - } + if (index >= result.GetArrayLength()) + { + return false; + } - result = result[index]; - } - else if (result.ValueKind == JsonValueKind.Object) - { - if (!result.TryGetProperty(token, out result)) + result = result[index]; + break; + } + case JsonValueKind.Object: { - return false; + if (!result.TryGetProperty(token, out result)) + { + return false; + } + + break; } - } - else - { - return false; + default: + return false; } return true; @@ -60,20 +61,17 @@ private static bool TryResolve(string token, JsonDocumentBuilder current, out Js public static JsonPointer ToDefinitePath(this JsonPointer pointer, JsonDocumentBuilder value) { - if (value.ValueKind == JsonValueKind.Array && pointer.Tokens.Count > 0 && - pointer.Tokens[pointer.Tokens.Count - 1] == "-") + if (value.ValueKind != JsonValueKind.Array || pointer.Tokens.Count <= 0 || + pointer.Tokens[pointer.Tokens.Count - 1] != "-") return pointer; + var tokens = new List(); + for (var i = 0; i < pointer.Tokens.Count - 1; ++i) { - var tokens = new List(); - for (var i = 0; i < pointer.Tokens.Count - 1; ++i) - { - tokens.Add(pointer.Tokens[i]); - } - - tokens.Add(value.GetArrayLength().ToString()); - return new JsonPointer(tokens); + tokens.Add(pointer.Tokens[i]); } - return pointer; + tokens.Add(value.GetArrayLength().ToString()); + return new JsonPointer(tokens); + } public static bool TryGetValue(this JsonPointer pointer, JsonDocumentBuilder root, @@ -96,84 +94,41 @@ public static bool TryAdd(this JsonPointer location, ref JsonDocumentBuilder root, JsonDocumentBuilder value) { - var current = root; - var token = ""; + if (!TryGetToken(location, root, out var current, out var token)) return false; - using var enumerator = location.GetEnumerator(); - var more = enumerator.MoveNext(); - if (!more) + switch (current.ValueKind) { - return false; - } - - while (more) - { - token = enumerator.Current; - more = enumerator.MoveNext(); - if (more) + case JsonValueKind.Array when token.Length == 1 && token[0] == '-': + current.AddArrayItem(value); + break; + case JsonValueKind.Array: { - if (!TryResolve(token, current, out current)) - { - return false; - } - } - } + if (!TryGetArray(value, token, current)) return false; - if (current.ValueKind == JsonValueKind.Array) - { - if (token.Length == 1 && token[0] == '-') - { - current.AddArrayItem(value); - current = current[current.GetArrayLength() - 1]; + break; } - else + case JsonValueKind.Object: { - if (!int.TryParse(token, out var index)) + if (current.ContainsPropertyName(token)) { - return false; - } - - if (index > current.GetArrayLength()) - { - return false; + current.RemoveProperty(token); } - if (index == current.GetArrayLength()) - { - current.AddArrayItem(value); - current = value; - } - else - { - current.InsertArrayItem(index, value); - current = value; - } + current.AddProperty(token, value); + break; } - } - else if (current.ValueKind == JsonValueKind.Object) - { - if (current.ContainsPropertyName(token)) - { - current.RemoveProperty(token); - } - - current.AddProperty(token, value); - current = value; - } - else - { - return false; + default: + return false; } return true; } - public static bool TryAddIfAbsent(this JsonPointer location, - ref JsonDocumentBuilder root, - JsonDocumentBuilder value) + private static bool TryGetToken(JsonPointer location, JsonDocumentBuilder root, out JsonDocumentBuilder current, + out string token) { - var current = root; - var token = ""; + current = root; + token = ""; using var enumerator = location.GetEnumerator(); var more = enumerator.MoveNext(); @@ -186,59 +141,64 @@ public static bool TryAddIfAbsent(this JsonPointer location, { token = enumerator.Current; more = enumerator.MoveNext(); - if (more) + if (!more) continue; + if (!TryResolve(token, current, out current)) { - if (!TryResolve(token, current, out current)) - { - return false; - } + return false; } } - if (current.ValueKind == JsonValueKind.Array) + return true; + } + + public static bool TryAddIfAbsent(this JsonPointer location, + ref JsonDocumentBuilder root, + JsonDocumentBuilder value) + { + if (!TryGetToken(location, root, out var current, out var token)) return false; + + switch (current.ValueKind) { - if (token.Length == 1 && token[0] == '-') - { + case JsonValueKind.Array when token.Length == 1 && token[0] == '-': current.AddArrayItem(value); - current = current[current.GetArrayLength() - 1]; - } - else + break; + case JsonValueKind.Array: { - if (!int.TryParse(token, out var index)) - { - return false; - } + if (!TryGetArray(value, token, current)) return false; - if (index > current.GetArrayLength()) - { - return false; - } - - if (index == current.GetArrayLength()) - { - current.AddArrayItem(value); - current = value; - } - else - { - current.InsertArrayItem(index, value); - current = value; - } + break; } + case JsonValueKind.Object when current.ContainsPropertyName(token): + return false; + case JsonValueKind.Object: + current.AddProperty(token, value); + break; + default: + return false; } - else if (current.ValueKind == JsonValueKind.Object) + + return true; + } + + private static bool TryGetArray(JsonDocumentBuilder value, string token, JsonDocumentBuilder current) + { + if (!int.TryParse(token, out var index)) { - if (current.ContainsPropertyName(token)) - { - return false; - } + return false; + } + + if (index > current.GetArrayLength()) + { + return false; + } - current.AddProperty(token, value); - current = value; + if (index == current.GetArrayLength()) + { + current.AddArrayItem(value); } else { - return false; + current.InsertArrayItem(index, value); } return true; @@ -247,58 +207,38 @@ public static bool TryAddIfAbsent(this JsonPointer location, public static bool TryRemove(this JsonPointer location, ref JsonDocumentBuilder root) { - var current = root; - var token = ""; - - using var enumerator = location.GetEnumerator(); - var more = enumerator.MoveNext(); - if (!more) - { - return false; - } + if (!TryGetToken(location, root, out var current, out var token)) return false; - while (more) + switch (current.ValueKind) { - token = enumerator.Current; - more = enumerator.MoveNext(); - if (more) + case JsonValueKind.Array when token.Length == 1 && token[0] == '-': + return false; + case JsonValueKind.Array: { - if (!TryResolve(token, current, out current)) + if (!int.TryParse(token, out var index)) { return false; } - } - } - if (current.ValueKind == JsonValueKind.Array) - { - if (token.Length == 1 && token[0] == '-') - { - return false; - } + if (index >= current.GetArrayLength()) + { + return false; + } - if (!int.TryParse(token, out var index)) - { - return false; + current.RemoveArrayItemAt(index); + break; } - - if (index >= current.GetArrayLength()) + case JsonValueKind.Object: { - return false; - } + if (current.ContainsPropertyName(token)) + { + current.RemoveProperty(token); + } - current.RemoveArrayItemAt(index); - } - else if (current.ValueKind == JsonValueKind.Object) - { - if (current.ContainsPropertyName(token)) - { - current.RemoveProperty(token); + break; } - } - else - { - return false; + default: + return false; } return true; @@ -308,64 +248,43 @@ public static bool TryReplace(this JsonPointer location, ref JsonDocumentBuilder root, JsonDocumentBuilder value) { - var current = root; - var token = ""; + if (!TryGetToken(location, root, out var current, out var token)) return false; - using var enumerator = location.GetEnumerator(); - var more = enumerator.MoveNext(); - if (!more) + switch (current.ValueKind) { - return false; - } - - while (more) - { - token = enumerator.Current; - more = enumerator.MoveNext(); - if (more) + case JsonValueKind.Array when token.Length == 1 && token[0] == '-': + return false; + case JsonValueKind.Array: { - if (!TryResolve(token, current, out current)) + if (!int.TryParse(token, out var index)) { return false; } - } - } - if (current.ValueKind == JsonValueKind.Array) - { - if (token.Length == 1 && token[0] == '-') - { - return false; - } + if (index >= current.GetArrayLength()) + { + return false; + } - if (!int.TryParse(token, out var index)) - { - return false; + current[index] = value; + break; } - - if (index >= current.GetArrayLength()) + case JsonValueKind.Object: { - return false; - } + if (current.ContainsPropertyName(token)) + { + current.RemoveProperty(token); + } + else + { + return false; + } - current[index] = value; - } - else if (current.ValueKind == JsonValueKind.Object) - { - if (current.ContainsPropertyName(token)) - { - current.RemoveProperty(token); + current.AddProperty(token, value); + break; } - else - { + default: return false; - } - - current.AddProperty(token, value); - } - else - { - return false; } return true; From 5927c52d731b01b6f4dc6a2b0ba452a4b8e463dd Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Thu, 18 Apr 2024 18:37:29 +0100 Subject: [PATCH 11/28] more sonarcloud --- .../Function.cs | 11 ++---- .../JmesPathParser.cs | 37 +++++-------------- 2 files changed, 13 insertions(+), 35 deletions(-) diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Function.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Function.cs index 5c62ef18..294633a1 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Function.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Function.cs @@ -89,7 +89,7 @@ internal interface IFunction internal abstract class BaseFunction : IFunction { - internal BaseFunction(int? argCount) + private protected BaseFunction(int? argCount) { Arity = argCount; } @@ -241,13 +241,10 @@ public override bool TryEvaluate(DynamicResources resources, IList args, switch (arg0.Type) { case JmesPathType.Array: - foreach (var item in arg0.EnumerateArray()) + if (arg0.EnumerateArray().Any(item => comparer.Equals(item, arg1))) { - if (comparer.Equals(item, arg1)) - { - element = JsonConstants.True; - return true; - } + element = JsonConstants.True; + return true; } element = JsonConstants.False; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs index e8bde3c6..43de7f04 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs @@ -111,18 +111,16 @@ internal enum JmesPathState internal ref struct JmesPathParser { - private ReadOnlyMemory _source; - private ReadOnlySpan _span; + private readonly ReadOnlySpan _span; private int _index; private int _column; private int _line; - private Stack _stateStack; - private Stack_outputStack; - private Stack_operatorStack; + private readonly Stack _stateStack; + private readonly Stack_outputStack; + private readonly Stack_operatorStack; internal JmesPathParser(string input) { - _source = input.AsMemory(); _span = input.AsSpan(); _index = 0; _column = 1; @@ -155,8 +153,6 @@ internal JsonTransformer Parse() { switch (_stateStack.Peek()) { - default: - break; case JmesPathState.Start: { _stateStack.Pop(); @@ -487,8 +483,6 @@ internal JsonTransformer Parse() ++_column; break; } - default: - break; } break; @@ -972,7 +966,7 @@ internal JsonTransformer Parse() var s = buffer.ToString(); if (!int.TryParse(s, out var n)) { - n = s.StartsWith("-") ? int.MinValue : int.MaxValue; + n = s.StartsWith('-') ? int.MinValue : int.MaxValue; } sliceStart = n; buffer.Clear(); @@ -994,7 +988,7 @@ internal JsonTransformer Parse() var s = buffer.ToString(); if (!int.TryParse(s, out var n)) { - n = s.StartsWith("-") ? int.MinValue : int.MaxValue; + n = s.StartsWith('-') ? int.MinValue : int.MaxValue; } sliceStop = n; buffer.Clear(); @@ -1437,8 +1431,6 @@ private void SkipWhiteSpace() _column = 1; ++_index; break; - default: - break; } } @@ -1704,15 +1696,9 @@ private void PushToken(Token token) _operatorStack.Push(new Token(TokenType.LeftParen)); break; } - case TokenType.BeginFilter: - _outputStack.Push(token); - _operatorStack.Push(new Token(TokenType.LeftParen)); - break; - case TokenType.BeginMultiSelectList: - _outputStack.Push(token); - _operatorStack.Push(new Token(TokenType.LeftParen)); - break; case TokenType.BeginMultiSelectHash: + case TokenType.BeginMultiSelectList: + case TokenType.BeginFilter: _outputStack.Push(token); _operatorStack.Push(new Token(TokenType.LeftParen)); break; @@ -1722,8 +1708,6 @@ private void PushToken(Token token) _operatorStack.Push(new Token(TokenType.LeftParen)); break; case TokenType.CurrentNode: - _outputStack.Push(token); - break; case TokenType.Key: case TokenType.Pipe: case TokenType.Argument: @@ -1733,12 +1717,9 @@ private void PushToken(Token token) case TokenType.LeftParen: _operatorStack.Push(token); break; - default: - break; } } - - + private uint AppendToCodepoint(uint cp, uint c) { cp *= 16; From 11202d4bc67b2bb87abe288a3626d2419a7ee660 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Thu, 18 Apr 2024 19:13:52 +0100 Subject: [PATCH 12/28] sonarcloud fixes --- .../AWS.Lambda.Powertools.JMESPath/Value.cs | 198 +++++++++++++----- 1 file changed, 140 insertions(+), 58 deletions(-) diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Value.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Value.cs index e0fae410..b6e17a8d 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Value.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Value.cs @@ -43,20 +43,20 @@ internal interface IObjectValueEnumerator : IEnumerator, IEnumera internal enum JmesPathType { - Null, + Null, Array, - False, - Number, - Object, - String, - True, + False, + Number, + Object, + String, + True, Expression } - internal interface IValue + internal interface IValue { - JmesPathType Type {get;} - IValue this[int index] {get;} + JmesPathType Type { get; } + IValue this[int index] { get; } int GetArrayLength(); string GetString(); bool TryGetDecimal(out decimal value); @@ -83,9 +83,25 @@ public bool MoveNext() return _enumerator.MoveNext(); } - public void Reset() { _enumerator.Reset(); } + public void Reset() + { + _enumerator.Reset(); + } - void IDisposable.Dispose() { _enumerator.Dispose();} + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + // Cleanup + if (disposing) + { + _enumerator.Dispose(); + } + } public IValue Current => new JsonElementValue(_enumerator.Current); @@ -98,7 +114,7 @@ public IEnumerator GetEnumerator() System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return GetEnumerator(); + return GetEnumerator(); } } @@ -116,11 +132,28 @@ public bool MoveNext() return _enumerator.MoveNext(); } - public void Reset() { _enumerator.Reset(); } + public void Reset() + { + _enumerator.Reset(); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - void IDisposable.Dispose() { _enumerator.Dispose();} + protected virtual void Dispose(bool disposing) + { + // Cleanup + if (disposing) + { + _enumerator.Dispose(); + } + } - public NameValuePair Current => new(_enumerator.Current.Name, new JsonElementValue(_enumerator.Current.Value)); + public NameValuePair Current => + new(_enumerator.Current.Name, new JsonElementValue(_enumerator.Current.Value)); object System.Collections.IEnumerator.Current => Current; @@ -131,7 +164,7 @@ public IEnumerator GetEnumerator() System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return GetEnumerator(); + return GetEnumerator(); } } @@ -142,7 +175,7 @@ internal JsonElementValue(JsonElement element) _element = element; } - public JmesPathType Type + public JmesPathType Type { get { @@ -168,7 +201,10 @@ public JmesPathType Type public IValue this[int index] => new JsonElementValue(_element[index]); - public int GetArrayLength() {return _element.GetArrayLength();} + public int GetArrayLength() + { + return _element.GetArrayLength(); + } public string GetString() { @@ -188,11 +224,11 @@ public bool TryGetDouble(out double value) public bool TryGetProperty(string propertyName, out IValue property) { var r = _element.TryGetProperty(propertyName, out var prop); - - property = prop.ValueKind == JsonValueKind.String && IsJsonValid(prop.GetString()) ? - new JsonElementValue(JsonNode.Parse(prop.GetString() ?? string.Empty).Deserialize()) : - new JsonElementValue(prop); - + + property = prop.ValueKind == JsonValueKind.String && IsJsonValid(prop.GetString()) + ? new JsonElementValue(JsonNode.Parse(prop.GetString() ?? string.Empty).Deserialize()) + : new JsonElementValue(prop); + return r; } @@ -225,7 +261,7 @@ public IObjectValueEnumerator EnumerateObject() public IExpression GetExpression() { throw new InvalidOperationException("Not an expression"); - } + } public override string ToString() { @@ -247,7 +283,10 @@ internal DoubleValue(double value) public IValue this[int index] => throw new InvalidOperationException(); - public int GetArrayLength() { throw new InvalidOperationException(); } + public int GetArrayLength() + { + throw new InvalidOperationException(); + } public string GetString() { @@ -256,7 +295,8 @@ public string GetString() public bool TryGetDecimal(out decimal value) { - if (!(double.IsNaN(_value) || double.IsInfinity(_value)) && _value is >= (double)decimal.MinValue and <= (double)decimal.MaxValue) + if (!(double.IsNaN(_value) || double.IsInfinity(_value)) && + _value is >= (double)decimal.MinValue and <= (double)decimal.MaxValue) { value = decimal.MinValue; return false; @@ -290,7 +330,7 @@ public IObjectValueEnumerator EnumerateObject() public IExpression GetExpression() { throw new InvalidOperationException("Not an expression"); - } + } public override string ToString() { @@ -312,7 +352,10 @@ internal DecimalValue(decimal value) public IValue this[int index] => throw new InvalidOperationException(); - public int GetArrayLength() { throw new InvalidOperationException(); } + public int GetArrayLength() + { + throw new InvalidOperationException(); + } public string GetString() { @@ -349,7 +392,7 @@ public IObjectValueEnumerator EnumerateObject() public IExpression GetExpression() { throw new InvalidOperationException("Not an expression"); - } + } public override string ToString() { @@ -371,7 +414,10 @@ internal StringValue(string value) public IValue this[int index] => throw new InvalidOperationException(); - public int GetArrayLength() { throw new InvalidOperationException(); } + public int GetArrayLength() + { + throw new InvalidOperationException(); + } public string GetString() { @@ -406,7 +452,7 @@ public IObjectValueEnumerator EnumerateObject() public IExpression GetExpression() { throw new InvalidOperationException("Not an expression"); - } + } public override string ToString() { @@ -421,9 +467,15 @@ public override string ToString() public IValue this[int index] => throw new InvalidOperationException(); - public int GetArrayLength() { throw new InvalidOperationException(); } + public int GetArrayLength() + { + throw new InvalidOperationException(); + } - public string GetString() { throw new InvalidOperationException(); } + public string GetString() + { + throw new InvalidOperationException(); + } public bool TryGetDecimal(out decimal value) { @@ -453,7 +505,7 @@ public IObjectValueEnumerator EnumerateObject() public IExpression GetExpression() { throw new InvalidOperationException("Not an expression"); - } + } public override string ToString() { @@ -467,9 +519,15 @@ public override string ToString() public IValue this[int index] => throw new InvalidOperationException(); - public int GetArrayLength() { throw new InvalidOperationException(); } + public int GetArrayLength() + { + throw new InvalidOperationException(); + } - public string GetString() { throw new InvalidOperationException(); } + public string GetString() + { + throw new InvalidOperationException(); + } public bool TryGetDecimal(out decimal value) { @@ -499,7 +557,7 @@ public IObjectValueEnumerator EnumerateObject() public IExpression GetExpression() { throw new InvalidOperationException("Not an expression"); - } + } public override string ToString() { @@ -513,9 +571,15 @@ public override string ToString() public IValue this[int index] => throw new InvalidOperationException(); - public int GetArrayLength() { throw new InvalidOperationException(); } + public int GetArrayLength() + { + throw new InvalidOperationException(); + } - public string GetString() { throw new InvalidOperationException(); } + public string GetString() + { + throw new InvalidOperationException(); + } public bool TryGetDecimal(out decimal value) { @@ -545,7 +609,7 @@ public IObjectValueEnumerator EnumerateObject() public IExpression GetExpression() { throw new InvalidOperationException("Not an expression"); - } + } public override string ToString() { @@ -586,7 +650,7 @@ public IEnumerator GetEnumerator() System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return GetEnumerator(); + return GetEnumerator(); } } @@ -636,7 +700,7 @@ public IObjectValueEnumerator EnumerateObject() public IExpression GetExpression() { throw new InvalidOperationException("Not an expression"); - } + } public override string ToString() { @@ -653,8 +717,10 @@ public override string ToString() { first = false; } + buffer.Append(item); } + buffer.Append(']'); return buffer.ToString(); } @@ -664,10 +730,10 @@ public override string ToString() { private class ObjectEnumerator : IObjectValueEnumerator { - private readonly IDictionary _value; + private readonly IDictionary _value; private readonly System.Collections.IEnumerator _enumerator; - public ObjectEnumerator(IDictionary value) + public ObjectEnumerator(IDictionary value) { _value = value; _enumerator = value.GetEnumerator(); @@ -678,14 +744,22 @@ public bool MoveNext() return _enumerator.MoveNext(); } - public void Reset() { _enumerator.Reset(); } + public void Reset() + { + _enumerator.Reset(); + } - void IDisposable.Dispose() {} + void IDisposable.Dispose() + { + } public NameValuePair Current { - get {var pair = (KeyValuePair)_enumerator.Current!; - return new NameValuePair(pair.Key, pair.Value); } + get + { + var pair = (KeyValuePair)_enumerator.Current!; + return new NameValuePair(pair.Key, pair.Value); + } } object System.Collections.IEnumerator.Current => Current; @@ -697,13 +771,13 @@ public IEnumerator GetEnumerator() System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return GetEnumerator(); + return GetEnumerator(); } } - private readonly IDictionary _value; + private readonly IDictionary _value; - internal ObjectValue(IDictionary value) + internal ObjectValue(IDictionary value) { _value = value; } @@ -712,8 +786,8 @@ internal ObjectValue(IDictionary value) public IValue this[int index] => throw new InvalidOperationException(); - public int GetArrayLength() - { + public int GetArrayLength() + { throw new InvalidOperationException(); } @@ -750,7 +824,7 @@ public IObjectValueEnumerator EnumerateObject() public IExpression GetExpression() { throw new InvalidOperationException("Not an expression"); - } + } public override string ToString() { @@ -767,10 +841,12 @@ public override string ToString() { first = false; } + buffer.Append(JsonSerializer.Serialize(property.Key)); buffer.Append(':'); buffer.Append(property.Value); } + buffer.Append('}'); return buffer.ToString(); } @@ -789,9 +865,15 @@ internal ExpressionValue(IExpression expr) public IValue this[int index] => throw new InvalidOperationException(); - public int GetArrayLength() { throw new InvalidOperationException(); } + public int GetArrayLength() + { + throw new InvalidOperationException(); + } - public string GetString() { throw new InvalidOperationException(); } + public string GetString() + { + throw new InvalidOperationException(); + } public bool TryGetDecimal(out decimal value) { @@ -821,11 +903,11 @@ public IObjectValueEnumerator EnumerateObject() public IExpression GetExpression() { return _expr; - } + } public override string ToString() { return "expression"; } } -} +} \ No newline at end of file From ce881d4abb11d1bd8310c29561b11e244ad22794 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Fri, 19 Apr 2024 10:23:21 +0100 Subject: [PATCH 13/28] Add continue-on-error to build workflow. very flaky service --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e831d366..4d96df9c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,6 +30,7 @@ jobs: run: dotnet test --collect:"XPlat Code Coverage" --results-directory ./codecov --verbosity normal - name: Codecov uses: codecov/codecov-action@81cd2dc8148241f03f5839d295e000b8f761e378 # 3.1.0 + continue-on-error: true # prevent build fail when codecov service is down (very frequent) with: token: ${{ secrets.CODECOV_TOKEN }} flags: unittests From 333d4b1f0e039043293ca871246b8c094a7e8818 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Fri, 19 Apr 2024 10:30:09 +0100 Subject: [PATCH 14/28] more sonar fixes --- libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs index 08ce6234..9e3265ba 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs @@ -55,9 +55,9 @@ internal abstract class BaseExpression : IExpression public bool IsRightAssociative {get;} - public bool IsProjection {get;} + public bool IsProjection {get;} - internal BaseExpression(Operator oper, bool isProjection) + private protected BaseExpression(Operator oper, bool isProjection) { PrecedenceLevel = OperatorTable.PrecedenceLevel(oper); IsRightAssociative = OperatorTable.IsRightAssociative(oper); @@ -173,7 +173,7 @@ internal abstract class Projection : BaseExpression { private readonly List _expressions; - internal Projection(Operator oper) + private protected Projection(Operator oper) : base(oper, true) { _expressions = new List(); From 418386ef8c7ffae8dcd4618c1be3751e1573c845 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Fri, 19 Apr 2024 10:38:26 +0100 Subject: [PATCH 15/28] sonar fixes --- libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs | 2 +- libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs index 66102648..5e93629a 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs @@ -24,7 +24,7 @@ internal interface IBinaryOperator internal abstract class BinaryOperator : IBinaryOperator { - internal BinaryOperator(Operator oper) + private protected BinaryOperator(Operator oper) { PrecedenceLevel = OperatorTable.PrecedenceLevel(oper); } diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs index c28c2246..d7b02b7d 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs @@ -26,7 +26,7 @@ internal interface IUnaryOperator internal abstract class UnaryOperator : IUnaryOperator { - internal UnaryOperator(Operator oper) + private protected UnaryOperator(Operator oper) { PrecedenceLevel = OperatorTable.PrecedenceLevel(oper); IsRightAssociative = OperatorTable.IsRightAssociative(oper); From b39a1c4ce73c7771fdb9927f905e34b07157da68 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Fri, 19 Apr 2024 11:03:05 +0100 Subject: [PATCH 16/28] sonar fixes --- .../Utilities/JsonPointer.cs | 91 ++++++++++--------- 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointer.cs index 1b933a50..af42a36d 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointer.cs @@ -159,55 +159,56 @@ public static bool TryParse(string input, out JsonPointer pointer) var done = false; while (index < input.Length && !done) { - switch (state) + if (state == JsonPointerState.Start) { - case JsonPointerState.Start: - switch (input[index]) - { - case '/': - state = JsonPointerState.Delim; - break; - default: - pointer = Default; - return false; - } - break; - case JsonPointerState.Delim: - switch (input[index]) - { - case '/': - done = true; - break; - case '~': - state = JsonPointerState.Escaped; - break; - default: - buffer.Append(input[index]); - break; - } - break; - case JsonPointerState.Escaped: - switch (input[index]) - { - case '0': - buffer.Append('~'); - state = JsonPointerState.Delim; - break; - case '1': - buffer.Append('/'); - state = JsonPointerState.Delim; - break; - default: - pointer = Default; - return false; - } - break; - default: + switch (input[index]) + { + case '/': + state = JsonPointerState.Delim; + break; + default: + pointer = Default; + return false; + } + } + else if (state == JsonPointerState.Delim) + { + switch (input[index]) { - pointer = Default; - return false; + case '/': + done = true; + break; + case '~': + state = JsonPointerState.Escaped; + break; + default: + buffer.Append(input[index]); + break; } } + else if (state == JsonPointerState.Escaped) + { + switch (input[index]) + { + case '0': + buffer.Append('~'); + state = JsonPointerState.Delim; + break; + case '1': + buffer.Append('/'); + state = JsonPointerState.Delim; + break; + default: + pointer = Default; + return false; + } + } + else + { + pointer = Default; + return false; + } + ++index; } tokens.Add(buffer.ToString()); From e353d7d6ea364ba2e9bd0977dc67b8a6edfff233 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Fri, 19 Apr 2024 11:14:30 +0100 Subject: [PATCH 17/28] remove unused files --- .../Utilities/JsonDocumentBuilder.cs | 318 ---------- .../Utilities/JsonElementComparer.cs | 171 ----- .../Utilities/JsonFlattener.cs | 503 --------------- .../Utilities/JsonMergePatch.cs | 224 ------- .../Utilities/JsonPatch.cs | 441 ------------- .../Utilities/JsonPointer.cs | 590 ------------------ .../Utilities/JsonPointerExtensions.cs | 293 --------- 7 files changed, 2540 deletions(-) delete mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonDocumentBuilder.cs delete mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementComparer.cs delete mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonFlattener.cs delete mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonMergePatch.cs delete mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPatch.cs delete mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointer.cs delete mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointerExtensions.cs diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonDocumentBuilder.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonDocumentBuilder.cs deleted file mode 100644 index 609f17e9..00000000 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonDocumentBuilder.cs +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.Json; - -namespace AWS.Lambda.Powertools.JMESPath.Utilities -{ - internal class JsonDocumentBuilder - { - private static JsonDocumentBuilder Default { get; } = new(); - - internal JsonValueKind ValueKind {get;} - - private readonly object? _item; - - private IList GetList() - { - return _item as IList ?? throw new InvalidOperationException("Item is null"); - } - - private IDictionary GetDictionary() - { - return _item as IDictionary ?? throw new InvalidOperationException("Item is null"); - } - - private JsonDocumentBuilder() - : this(JsonValueKind.Null) - { - } - - internal JsonDocumentBuilder(JsonValueKind valueKind) - { - ValueKind = valueKind; - switch (valueKind) - { - case JsonValueKind.Array: - _item = new List(); - break; - case JsonValueKind.Object: - _item = new Dictionary(); - break; - case JsonValueKind.True: - _item = true; - break; - case JsonValueKind.False: - _item = false; - break; - case JsonValueKind.Null: - _item = null; - break; - case JsonValueKind.String: - _item = ""; - break; - case JsonValueKind.Number: - _item = 0; - break; - default: - _item = null; - break; - } - } - - internal JsonDocumentBuilder(IList list) - { - ValueKind = JsonValueKind.Array; - _item = list; - } - - internal JsonDocumentBuilder(IDictionary dict) - { - ValueKind = JsonValueKind.Object; - _item = dict; - } - - internal JsonDocumentBuilder(string str) - { - ValueKind = JsonValueKind.String; - _item = str; - } - - internal JsonDocumentBuilder(JsonElement element) - { - ValueKind = element.ValueKind; - switch (element.ValueKind) - { - case JsonValueKind.Array: - var list = new List(); - foreach (var item in element.EnumerateArray()) - { - list.Add(new JsonDocumentBuilder(item)); - } - _item = list; - break; - case JsonValueKind.Object: - var dict = new Dictionary(); - foreach (var property in element.EnumerateObject()) - { - dict.Add(property.Name, new JsonDocumentBuilder(property.Value)); - } - _item = dict; - break; - default: - _item = element; - break; - } - } - - internal IEnumerable EnumerateArray() - { - if (ValueKind != JsonValueKind.Array) - { - throw new InvalidOperationException("This value's ValueKind is not Array."); - } - return GetList(); - } - - internal IEnumerable> EnumerateObject() - { - if (ValueKind != JsonValueKind.Object) - { - throw new InvalidOperationException("This value's ValueKind is not Object."); - } - return GetDictionary(); - } - - internal JsonDocumentBuilder this[int i] - { - get { - if (ValueKind != JsonValueKind.Array) - { - throw new InvalidOperationException("This value's ValueKind is not Array."); - } - return GetList() [i]; - } - set { - if (ValueKind != JsonValueKind.Array) - { - throw new InvalidOperationException("This value's ValueKind is not Array."); - } - GetList()[i] = value; - } - } - - internal void AddArrayItem(JsonDocumentBuilder value) - { - if (ValueKind != JsonValueKind.Array) - { - throw new InvalidOperationException("This value's ValueKind is not Array."); - } - GetList().Add(value); - } - - internal void InsertArrayItem(int index, JsonDocumentBuilder value) - { - if (ValueKind != JsonValueKind.Array) - { - throw new InvalidOperationException("This value's ValueKind is not Array."); - } - GetList().Insert(index, value); - } - - internal void RemoveArrayItemAt(int index) - { - if (ValueKind != JsonValueKind.Array) - { - throw new InvalidOperationException("This value's ValueKind is not Array."); - } - GetList().RemoveAt(index); - } - - internal void AddProperty(string name, JsonDocumentBuilder value) - { - if (ValueKind != JsonValueKind.Object) - { - throw new InvalidOperationException("This value's ValueKind is not Object."); - } - GetDictionary().Add(name, value); - } - - internal bool TryAddProperty(string name, JsonDocumentBuilder value) - { - if (ValueKind != JsonValueKind.Object) - { - throw new InvalidOperationException("This value's ValueKind is not Object."); - } - return GetDictionary().TryAdd(name, value); - } - - internal bool ContainsPropertyName(string name) - { - if (ValueKind != JsonValueKind.Object) - { - throw new InvalidOperationException("This value's ValueKind is not Object."); - } - return GetDictionary().ContainsKey(name); - } - - internal void RemoveProperty(string name) - { - if (ValueKind != JsonValueKind.Object) - { - throw new InvalidOperationException("This value's ValueKind is not Object."); - } - GetDictionary().Remove(name); - } - - internal int GetArrayLength() - { - if (ValueKind != JsonValueKind.Array) - { - throw new InvalidOperationException("This value's ValueKind is not Array."); - } - return GetList().Count; - } - - internal int GetObjectLength() - { - if (ValueKind != JsonValueKind.Object) - { - throw new InvalidOperationException("This value's ValueKind is not Object."); - } - return GetDictionary().Count; - } - - internal bool TryGetProperty(string name, out JsonDocumentBuilder value) - { - if (ValueKind != JsonValueKind.Object) - { - throw new InvalidOperationException("This value's ValueKind is not Object."); - } - - if (ValueKind == JsonValueKind.Object) return GetDictionary().TryGetValue(name, out value); - value = Default; - return false; - } - - public override string ToString() - { - var buffer = new StringBuilder(); - ToString(buffer); - return buffer.ToString(); - } - - private void ToString(StringBuilder buffer) - { - switch (ValueKind) - { - case JsonValueKind.Array: - { - buffer.Append('['); - var first = true; - foreach (var item in EnumerateArray()) - { - if (!first) - { - buffer.Append(','); - } - else - { - first = false; - } - item.ToString(buffer); - } - buffer.Append(']'); - break; - } - case JsonValueKind.Object: - { - buffer.Append('{'); - var first = true; - foreach (var property in EnumerateObject()) - { - if (!first) - { - buffer.Append(','); - } - else - { - first = false; - } - buffer.Append(JsonSerializer.Serialize(property.Key)); - buffer.Append(':'); - property.Value.ToString(buffer); - } - buffer.Append('}'); - break; - } - default: - { - buffer.Append(JsonSerializer.Serialize(_item, (JsonSerializerOptions)null)); - break; - } - } - } - - internal JsonDocument ToJsonDocument() - { - var json = ToString(); - return JsonDocument.Parse(json); - } - } -} diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementComparer.cs deleted file mode 100644 index 22d44c6c..00000000 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementComparer.cs +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.Json; - -namespace AWS.Lambda.Powertools.JMESPath.Utilities -{ - /// - /// Compares two instances. - /// - - public sealed class JsonElementComparer : IComparer, System.Collections.IComparer - { - /// Gets a singleton instance of . This property is read-only. - public static JsonElementComparer Instance { get; } = new(); - - /// - /// Constructs a - /// - public JsonElementComparer() {} - - /// - /// Compares two instances. - /// - /// If the two instances have different data types, they are - /// compared according to their ValueKind property, which gives this ordering: - /// - /// Undefined - /// Object - /// Array - /// String - /// Number - /// True - /// False - /// Null - /// - /// - /// If both instances are null, true, or false, they are equal. - /// - /// If both are strings, they are compared with the String.CompareTo method. - /// - /// If both are numbers, and both can be represented by a , - /// they are compared with the Decimal.CompareTo method, otherwise they are - /// compared as doubles. - /// - /// If both are objects, they are compared according to the following rules: - /// - ///
    - ///
  • Order each object's properties by name and compare sequentially. - /// The properties are compared first by name with the String.CompareTo method, then by value with
  • - ///
  • The first mismatching property defines which instance is less or greater than the other.
  • - ///
  • If the two sequences have no mismatching properties until one of them ends, and the other is longer, the shorter sequence is less than the other.
  • - ///
  • If the two sequences have no mismatching properties and have the same length, they are equal.
  • - ///
- /// - /// If both are arrays, they are compared element wise with . - /// The first mismatching element defines which instance is less or greater than the other. - /// If the two arrays have no mismatching elements until one of them ends, and the other is longer, the shorter array is less than the other. - /// If the two arrays have no mismatching elements and have the same length, they are equal. - /// - ///
- /// The first object of type cref="JsonElement"/> to compare. - /// The second object of type cref="JsonElement"/> to compare. - /// - /// - /// Unable to compare numbers as either or double (shouldn't happen.) - /// - public int Compare(JsonElement lhs, JsonElement rhs) - { - if (lhs.ValueKind != rhs.ValueKind) - return (int)lhs.ValueKind - (int)rhs.ValueKind; - - switch (lhs.ValueKind) - { - case JsonValueKind.Null: - case JsonValueKind.True: - case JsonValueKind.False: - case JsonValueKind.Undefined: - return 0; - - case JsonValueKind.Number: - { - if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) - { - return dec1.CompareTo(dec2); - } - - if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) - { - return val1.CompareTo(val2); - } - - throw new InvalidOperationException("Unable to compare numbers"); - } - - case JsonValueKind.String: - { - return string.CompareOrdinal(lhs.GetString(), rhs.GetString()); - } - - case JsonValueKind.Array: - { - var enumerator1 = lhs.EnumerateArray(); - var enumerator2 = rhs.EnumerateArray(); - var result1 = enumerator1.MoveNext(); - var result2 = enumerator2.MoveNext(); - while (result1 && result2) - { - var diff = Compare(enumerator1.Current, enumerator2.Current); - if (diff != 0) - { - return diff; - } - result1 = enumerator1.MoveNext(); - result2 = enumerator2.MoveNext(); - } - return result1 ? 1 : result2 ? -1 : 0; - } - - case JsonValueKind.Object: - { - // OrderBy performs a stable sort (Note that supports duplicate property names) - using var enumerator1 = lhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); - using var enumerator2 = rhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); - - var result1 = enumerator1.MoveNext(); - var result2 = enumerator2.MoveNext(); - while (result1 && result2) - { - if (enumerator1.Current.Name != enumerator2.Current.Name) - { - return string.Compare(enumerator1.Current.Name, enumerator2.Current.Name, StringComparison.Ordinal); - } - var diff = Compare(enumerator1.Current.Value, enumerator2.Current.Value); - if (diff != 0) - { - return diff; - } - result1 = enumerator1.MoveNext(); - result2 = enumerator2.MoveNext(); - } - - return result1 ? 1 : result2 ? -1 : 0; - } - - default: - throw new InvalidOperationException($"Unknown JsonValueKind {lhs.ValueKind}"); - } - } - - int System.Collections.IComparer.Compare(object x, object y) - { - return Compare((JsonElement)x, (JsonElement)y); - } - } -} diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonFlattener.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonFlattener.cs deleted file mode 100644 index 719f6a1a..00000000 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonFlattener.cs +++ /dev/null @@ -1,503 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -using System; -using System.Linq; -using System.Text; -using System.Text.Json; - -namespace AWS.Lambda.Powertools.JMESPath.Utilities -{ - /// - /// Defines how the unflatten operation handles integer tokens in a JSON Pointer - /// - /// - /// This example illustrates the use of - /// - /// using System; - /// using System.Diagnostics; - /// using System.Text.Json; - /// - /// public class Example - /// { - /// public static void Main() - /// { - /// using var doc = JsonDocument.Parse(@" - /// { - /// ""discards"": { - /// ""1000"": ""Record does not exist"", - /// ""1004"": ""Queue limit exceeded"", - /// ""1010"": ""Discarding timed-out partial msg"" - /// }, - /// ""warnings"": { - /// ""0"": ""Phone number missing country code"", - /// ""1"": ""State code missing"", - /// ""2"": ""Zip code missing"" - /// } - /// } - /// "); - /// - /// var options = new JsonSerializerOptions() { WriteIndented = true }; - /// - /// using JsonDocument flattened = JsonFlattener.Flatten(doc.RootElement); - /// Console.WriteLine("The flattened document:\n"); - /// Console.WriteLine($"{JsonSerializer.Serialize(flattened, options)}\n"); - /// - /// Console.WriteLine("Unflatten integer tokens as array indices if possible:\n"); - /// using JsonDocument unflattened1 = JsonFlattener.Unflatten(flattened.RootElement, - /// IntegerTokenUnflattening.TryIndex); - /// Console.WriteLine($"{JsonSerializer.Serialize(unflattened1, options)}\n"); - /// - /// Console.WriteLine("Always unflatten integer tokens as object names:\n"); - /// using JsonDocument unflattened2 = JsonFlattener.Unflatten(flattened.RootElement, - /// IntegerTokenUnflattening.AssumeName); - /// Console.WriteLine($"{JsonSerializer.Serialize(unflattened2, options)}\n"); - /// } - /// } - /// - /// Output: - /// - /// The flattened document: - /// - /// { - /// "/discards/1000": "Record does not exist", - /// "/discards/1004": "Queue limit exceeded", - /// "/discards/1010": "Discarding timed-out partial msg", - /// "/warnings/0": "Phone number missing country code", - /// "/warnings/1": "State code missing", - /// "/warnings/2": "Zip code missing" - /// } - /// - /// Unflatten integer tokens as array indices if possible: - /// - /// { - /// "discards": { - /// "1000": "Record does not exist", - /// "1004": "Queue limit exceeded", - /// "1010": "Discarding timed-out partial msg" - /// }, - /// "warnings": [ - /// "Phone number missing country code", - /// "State code missing", - /// "Zip code missing" - /// ] - /// } - /// - /// Always unflatten integer tokens as object names: - /// - /// { - /// "discards": { - /// "1000": "Record does not exist", - /// "1004": "Queue limit exceeded", - /// "1010": "Discarding timed-out partial msg" - /// }, - /// "warnings": { - /// "0": "Phone number missing country code", - /// "1": "State code missing", - /// "2": "Zip code missing" - /// } - /// } - /// - /// - public enum IntegerTokenUnflattening { - /// - /// The unflatten operation first tries to unflatten into a JSON array - /// using the integer tokens as sequential indices, and if that fails, unflattens into - /// a JSON object using the integer tokens as names. - /// - TryIndex, - /// - /// The unflatten operation always unflattens into a JSON object - /// using the integer tokens as names. - /// - AssumeName - } - - /// - /// Provides functionality to flatten a JSON object or array to a single depth JSON object of JSON Pointer-value pairs, - /// and to unflatten a flattened JSON object. - /// - /// - /// This example shows how to flatten and unflatten a JSON value - /// - /// using System; - /// using System.Diagnostics; - /// using System.Text.Json; - /// - /// public class Example - /// { - /// public static void Main() - /// { - /// using var doc = JsonDocument.Parse(@" - /// { - /// ""application"": ""hiking"", - /// ""reputons"": [ - /// { - /// ""rater"": ""HikingAsylum"", - /// ""assertion"": ""advanced"", - /// ""rated"": ""Marilyn C"", - /// ""rating"": 0.90 - /// }, - /// { - /// ""rater"": ""HikingAsylum"", - /// ""assertion"": ""intermediate"", - /// ""rated"": ""Hongmin"", - /// ""rating"": 0.75 - /// } - /// ] - /// } - /// "); - /// - /// using JsonDocument flattened = JsonFlattener.Flatten(doc.RootElement); - /// - /// var options = new JsonSerializerOptions() { WriteIndented = true }; - /// - /// Console.WriteLine($"{JsonSerializer.Serialize(flattened, options)}\n"); - /// - /// using JsonDocument unflattened = JsonFlattener.Unflatten(flattened.RootElement); - /// - /// var comparer = JsonElementEqualityComparer.Instance; - /// Debug.Assert(comparer.Equals(unflattened.RootElement,doc.RootElement)); - /// } - /// } - /// - /// Output: - /// - /// { - /// "/application": "hiking", - /// "/reputons/0/rater": "HikingAsylum", - /// "/reputons/0/assertion": "advanced", - /// "/reputons/0/rated": "Marilyn C", - /// "/reputons/0/rating": 0.90, - /// "/reputons/1/rater": "HikingAsylum", - /// "/reputons/1/assertion": "intermediate", - /// "/reputons/1/rated": "Hongmin", - /// "/reputons/1/rating": 0.75 - /// } - /// - /// - - public static class JsonFlattener - { - /// - /// Converts a JSON object or array into a single depth JSON object of name-value pairs, - /// such that the names are JSON Pointer strings, and the values are either string, - /// number, true, false, null, empty object, or empty array. - /// - /// - /// It is the users responsibility to properly Dispose the returned value - /// - /// The value to be flattened. - /// The flattened value - public static JsonDocument Flatten(JsonElement value) - { - var result = new JsonDocumentBuilder(JsonValueKind.Object); - const string parentKey = ""; - _Flatten(parentKey, value, result); - return result.ToJsonDocument(); - } - - /// - /// Recovers the original JSON value from a JSON object in flattened form, to the extent possible. - /// There may not be a unique solution, an integer token in a JSON Pointer could be an array index or - /// it could be an object name. The default behavior is to attempt to recover arrays. The - /// parameter can be used to recover objects with integer names instead. - /// - /// - /// It is the users responsibility to properly Dispose the returned value - /// - /// The flattened value, which must be a JSON object of name-value pairs, such that - /// the names are JSON Pointer strings, and the values are either string, - /// number, true, false, null, empty object, or empty array. - /// Options for handling integer tokens in the JSON Pointer. - /// The unflattened value - /// - /// The is not a JSON object, or has a name that contains an invalid JSON pointer. - /// - public static JsonDocument Unflatten(JsonElement flattenedValue, - IntegerTokenUnflattening options = IntegerTokenUnflattening.TryIndex) - { - if (options == IntegerTokenUnflattening.TryIndex) - { - return TryUnflattenArray(flattenedValue, out var val) ? val.ToJsonDocument() : UnflattenToObject(flattenedValue, options).ToJsonDocument(); - } - - return UnflattenToObject(flattenedValue, options).ToJsonDocument(); - } - - private static void _Flatten(string parentKey, - JsonElement parentValue, - JsonDocumentBuilder result) - { - switch (parentValue.ValueKind) - { - case JsonValueKind.Array: - { - if (parentValue.GetArrayLength() == 0) - { - result.AddProperty(parentKey, new JsonDocumentBuilder(parentValue)); - } - else - { - for (var i = 0; i < parentValue.GetArrayLength(); ++i) - { - var buffer = new StringBuilder(parentKey); - buffer.Append('/'); - buffer.Append(i.ToString()); - _Flatten(buffer.ToString(), parentValue[i], result); - } - } - break; - } - - case JsonValueKind.Object: - { - if (!parentValue.EnumerateObject().Any()) - { - result.AddProperty(parentKey, new JsonDocumentBuilder(parentValue)); - } - else - { - foreach (var item in parentValue.EnumerateObject()) - { - var buffer = new StringBuilder(parentKey); - buffer.Append('/'); - buffer.Append(JsonPointer.Escape(item.Name)); - _Flatten(buffer.ToString(), item.Value, result); - } - } - break; - } - - default: - { - result.AddProperty(parentKey, new JsonDocumentBuilder(parentValue)); - break; - } - } - } - - // unflatten - - private static JsonDocumentBuilder SafeUnflatten(JsonDocumentBuilder value) - { - if (value.ValueKind != JsonValueKind.Object || value.GetObjectLength() == 0) - { - return value; - } - var safe = true; - var index = 0; - foreach (var item in value.EnumerateObject()) - { - if (int.TryParse(item.Key, out var n) && index++ == n) continue; - safe = false; - break; - } - - if (safe) - { - var j = new JsonDocumentBuilder(JsonValueKind.Array); - foreach (var item in value.EnumerateObject()) - { - j.AddArrayItem(item.Value); - } - var a = new JsonDocumentBuilder(JsonValueKind.Array); - foreach (var item in j.EnumerateArray()) - { - a.AddArrayItem(SafeUnflatten(item)); - } - return a; - } - - var o = new JsonDocumentBuilder(JsonValueKind.Object); - foreach (var item in value.EnumerateObject()) - { - //if (!o.ContainsPropertyName(item.Key)) - //{ - // o.AddProperty(item.Key, SafeUnflatten (item.Value)); - //} - o.TryAddProperty(item.Key, SafeUnflatten (item.Value)); - } - return o; - } - - private static bool TryUnflattenArray(JsonElement value, out JsonDocumentBuilder result) - { - if (value.ValueKind != JsonValueKind.Object) - { - throw new ArgumentException("The value to unflatten is not a JSON object"); - } - - result = new JsonDocumentBuilder(JsonValueKind.Object); - - foreach (var item in value.EnumerateObject()) - { - JsonDocumentBuilder? parent = null; - var part = result; - var parentIndex = 0; - var parentName = ""; - - if (!JsonPointer.TryParse(item.Name, out var ptr)) - { - throw new ArgumentException("Name contains invalid JSON Pointer"); - } - var index = 0; - - using var it = ptr.GetEnumerator(); - var more = it.MoveNext(); - while (more) - { - var token = it.Current; - - if (int.TryParse(token, out var n) && index++ == n) - { - if (part.ValueKind != JsonValueKind.Array) - { - if (parent != null && parent.ValueKind == JsonValueKind.Object) - { - parent.RemoveProperty(parentName); - var val = new JsonDocumentBuilder(JsonValueKind.Array); - parent.AddProperty(parentName, val); - part = val; - } - else if (parent != null && parent.ValueKind == JsonValueKind.Array) - { - var val = new JsonDocumentBuilder(JsonValueKind.Array); - parent[parentIndex] = val; - part = val; - } - else - { - return false; - } - } - parent = part; - parentIndex = n; - parentName = token; - more = it.MoveNext(); - if (more) - { - if (n >= part.GetArrayLength()) - { - part.AddArrayItem(new JsonDocumentBuilder(JsonValueKind.Object)); - part = part[part.GetArrayLength() - 1]; - } - else - { - part = part[n]; - } - } - else - { - part.AddArrayItem(new JsonDocumentBuilder(item.Value)); - part = part[part.GetArrayLength() - 1]; - } - } - else if (part.ValueKind == JsonValueKind.Object) - { - more = it.MoveNext(); - if (more) - { - if (part.TryGetProperty(token, out var val)) - { - part = val; - } - else - { - val = new JsonDocumentBuilder(JsonValueKind.Object); - part.AddProperty(token,val); - part = val; - } - } - else - { - if (part.TryGetProperty(token, out var val)) - { - part = val; - } - else - { - val = new JsonDocumentBuilder(item.Value); - part.AddProperty(token,val); - part = val; - } - } - } - else - { - return false; - } - } - } - - return true; - } - - private static JsonDocumentBuilder UnflattenToObject(JsonElement value, IntegerTokenUnflattening options = IntegerTokenUnflattening.TryIndex) - { - if (value.ValueKind != JsonValueKind.Object) - { - throw new ArgumentException("The value to unflatten is not a JSON object"); - } - - var result = new JsonDocumentBuilder(JsonValueKind.Object); - - foreach (var item in value.EnumerateObject()) - { - var part = result; - if (!JsonPointer.TryParse(item.Name, out var ptr)) - { - throw new ArgumentException("Name contains invalid JSON Pointer"); - } - - using var it = ptr.GetEnumerator(); - var more = it.MoveNext(); - while (more) - { - var s = it.Current; - more = it.MoveNext(); - if (more) - { - if (part.TryGetProperty(s, out var val)) - { - part = val; - } - else - { - val = new JsonDocumentBuilder(JsonValueKind.Object); - part.AddProperty(s,val); - part = val; - } - } - else - { - if (part.TryGetProperty(s, out var val)) - { - part = val; - } - else - { - val = new JsonDocumentBuilder(item.Value); - part.AddProperty(s,val); - part = val; - } - } - } - } - - return options == IntegerTokenUnflattening.TryIndex ? SafeUnflatten (result) : result; - } - } -} diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonMergePatch.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonMergePatch.cs deleted file mode 100644 index 350128ca..00000000 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonMergePatch.cs +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -using System.Text.Json; - -namespace AWS.Lambda.Powertools.JMESPath.Utilities -{ - /// - /// Provides functionality for applying a JSON Merge Patch as - /// defined in RFC 7396 - /// to a JSON value. - /// - /// - /// The following example borrowed from [RFC 7396](https://datatracker.ietf.org/doc/html/rfc7396) shows how to apply a JSON Merge Patch to a JSON value - /// - /// using System; - /// using System.Diagnostics; - /// using System.Text.Json; - /// - /// public class Example - /// { - /// public static void Main() - /// { - /// using var doc = JsonDocument.Parse(@" - /// { - /// ""title"": ""Goodbye!"", - /// ""author"" : { - /// ""givenName"" : ""John"", - /// ""familyName"" : ""Doe"" - /// }, - /// ""tags"":[ ""example"", ""sample"" ], - /// ""content"": ""This will be unchanged"" - /// } - /// "); - /// - /// using var patch = JsonDocument.Parse(@" - /// { - /// ""title"": ""Hello!"", - /// ""phoneNumber"": ""+01-123-456-7890"", - /// ""author"": { - /// ""familyName"": null - /// }, - /// ""tags"": [ ""example"" ] - /// } - /// "); - /// - /// using JsonDocument result = JsonMergePatch.ApplyMergePatch(doc.RootElement, patch.RootElement); - /// - /// var options = new JsonSerializerOptions() { WriteIndented = true }; - /// - /// Console.WriteLine("The original document:\n"); - /// Console.WriteLine($"{JsonSerializer.Serialize(doc, options)}\n"); - /// Console.WriteLine("The patch:\n"); - /// Console.WriteLine($"{JsonSerializer.Serialize(patch, options)}\n"); - /// Console.WriteLine("The result:\n"); - /// Console.WriteLine($"{JsonSerializer.Serialize(result, options)}\n"); - /// "); - /// } - /// } - /// - /// The original document: - /// - /// - /// { - /// "title": "Goodbye!", - /// "author": { - /// "givenName": "John", - /// "familyName": "Doe" - /// }, - /// "tags": [ - /// "example", - /// "sample" - /// ], - /// "content": "This will be unchanged" - /// } - /// - /// - /// The patch: - /// - /// - /// { - /// "title": "Hello!", - /// "phoneNumber": "\u002B01-123-456-7890", - /// "author": { - /// "familyName": null - /// }, - /// "tags": [ - /// "example" - /// ] - /// } - /// - /// - /// The result: - /// - /// - /// { - /// "title": "Hello!", - /// "author": { - /// "givenName": "John" - /// }, - /// "tags": [ - /// "example" - /// ], - /// "content": "This will be unchanged", - /// "phoneNumber": "\u002B01-123-456-7890" - /// } - /// - /// - - public static class JsonMergePatch - { - /// - /// Applies a JSON Merge Patch as defined in RFC 7396 - /// to a source JSON value. - /// - /// - /// It is the users responsibility to properly Dispose the returned value - /// - /// The source JSON value. - /// The JSON merge patch to be applied to the source JSON value. - /// The patched JSON value - public static JsonDocument ApplyMergePatch(JsonElement source, JsonElement patch) - { - var documentBuilder = new JsonDocumentBuilder(source); - var builder = ApplyMergePatch(ref documentBuilder, patch); - return builder.ToJsonDocument(); - } - - private static JsonDocumentBuilder ApplyMergePatch(ref JsonDocumentBuilder target, JsonElement patch) - { - if (patch.ValueKind == JsonValueKind.Object) - { - if (target.ValueKind != JsonValueKind.Object) - { - target = new JsonDocumentBuilder(JsonValueKind.Object); - } - foreach (var property in patch.EnumerateObject()) - { - if (target.TryGetProperty(property.Name, out var item)) - { - target.RemoveProperty(property.Name); - if (property.Value.ValueKind != JsonValueKind.Null) - { - target.AddProperty(property.Name, ApplyMergePatch(ref item, property.Value)); - } - } - else if (property.Value.ValueKind != JsonValueKind.Null) - { - item = new JsonDocumentBuilder(JsonValueKind.Object); - target.AddProperty(property.Name, ApplyMergePatch(ref item, property.Value)); - } - } - return target; - } - else - { - return new JsonDocumentBuilder(patch); - } - } - - /// - /// Builds a JSON Merge Patch as defined in RFC 7396 - /// given two JSON values, a source and a target. - /// - /// - /// It is the users responsibility to properly Dispose the returned value - /// - /// The source JSON value. - /// The target JSON value. - /// A JSON Merge Patch to convert the source JSON value to the target JSON value - public static JsonDocument FromDiff(JsonElement source, JsonElement target) - { - return _FromDiff(source, target).ToJsonDocument(); - } - - private static JsonDocumentBuilder _FromDiff(JsonElement source, JsonElement target) - { - var comparer = JsonElementEqualityComparer.Instance; - - if (source.ValueKind != JsonValueKind.Object || target.ValueKind != JsonValueKind.Object) - { - return new JsonDocumentBuilder(target); - } - var builder = new JsonDocumentBuilder(JsonValueKind.Object); - - foreach (var property in source.EnumerateObject()) - { - if (target.TryGetProperty(property.Name, out var value)) - { - if (!comparer.Equals(property.Value,value)) - { - builder.AddProperty(property.Name, _FromDiff(property.Value, value)); - } - } - else - { - builder.AddProperty(property.Name, new JsonDocumentBuilder(JsonValueKind.Null)); - } - } - - foreach (var property in target.EnumerateObject()) - { - if (!source.TryGetProperty(property.Name, out _)) - { - builder.AddProperty(property.Name, new JsonDocumentBuilder(property.Value)); - } - } - - return builder; - } - } -} diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPatch.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPatch.cs deleted file mode 100644 index 5164bb2b..00000000 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPatch.cs +++ /dev/null @@ -1,441 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -using System; -using System.Diagnostics; -using System.Text; -using System.Text.Json; - -namespace AWS.Lambda.Powertools.JMESPath.Utilities -{ - /// - /// Captures error message and the operation that caused it. - /// - public sealed class JsonPatchException : Exception - { - /// - /// Constructs a . - /// - /// The operation that caused the error. - /// The error message. - public JsonPatchException( - string operation, - string message) : base(message) - { - if (message == null) - { - throw new ArgumentNullException(nameof(message)); - } - - Operation = operation; - } - - /// - /// Gets the that caused the error. - /// - public string Operation { get; } - } - - /// - /// Provides functionality for applying a JSON Patch as - /// defined in RFC 6902 - /// to a JSON value. - /// - /// - /// The following example borrowed from [jsonpatch.com](http://jsonpatch.com/) shows how to apply a JSON Patch to a JSON value - /// - /// using System; - /// using System.Diagnostics; - /// using System.Text.Json; - /// - /// public class Example - /// { - /// public static void Main() - /// { - /// using var doc = JsonDocument.Parse(@" - /// { - /// ""baz"": ""qux"", - /// ""foo"": ""bar"" - /// } - /// "); - /// - /// using var patch = JsonDocument.Parse(@" - /// [ - /// { ""op"": ""replace"", ""path"": ""/baz"", ""value"": ""boo"" }, - /// { ""op"": ""add"", ""path"": ""/hello"", ""value"": [""world""] }, - /// { ""op"": ""remove"", ""path"": ""/foo"" } - /// ] - /// "); - /// - /// using JsonDocument result = JsonPatch.ApplyPatch(doc.RootElement, patch.RootElement); - /// - /// var options = new JsonSerializerOptions() { WriteIndented = true }; - /// - /// Console.WriteLine("The original document:\n"); - /// Console.WriteLine($"{JsonSerializer.Serialize(doc, options)}\n"); - /// Console.WriteLine("The patch:\n"); - /// Console.WriteLine($"{JsonSerializer.Serialize(patch, options)}\n"); - /// Console.WriteLine("The result:\n"); - /// Console.WriteLine($"{JsonSerializer.Serialize(result, options)}\n"); - /// "); - /// } - /// } - /// - /// The original document: - /// - /// - /// { - /// "baz": "qux", - /// "foo": "bar" - /// } - /// - /// - /// The patch: - /// - /// - /// [ - /// { - /// "op": "replace", - /// "path": "/baz", - /// "value": "boo" - /// }, - /// { - /// "op": "add", - /// "path": "/hello", - /// "value": [ - /// "world" - /// ] - /// }, - /// { - /// "op": "remove", - /// "path": "/foo" - /// } - /// ] - /// - /// - /// The result: - /// - /// { - /// "baz": "boo", - /// "hello": [ - /// "world" - /// ] - /// } - /// - /// - public static class JsonPatch - { - /// - /// Applies a JSON Patch as defined in RFC 6902 - /// to a source JSON value. - /// - /// - /// It is the users responsibility to properly Dispose the returned value - /// - /// The source JSON value. - /// The patch to be applied to the source JSON value. - /// The patched JSON value - /// - /// The provided is invalid - /// - /// - /// A JSON Patch operation failed - /// - public static JsonDocument ApplyPatch(JsonElement source, - JsonElement patch) - { - var documentBuilder = new JsonDocumentBuilder(source); - ApplyPatch(ref documentBuilder, patch); - return documentBuilder.ToJsonDocument(); - } - - private static void ApplyPatch(ref JsonDocumentBuilder target, - JsonElement patch) - { - var comparer = JsonElementEqualityComparer.Instance; - - Debug.Assert(target != null); - - if (patch.ValueKind != JsonValueKind.Array) - { - throw new ArgumentException("Patch must be an array"); - } - - foreach (var operation in patch.EnumerateArray()) - { - if (!operation.TryGetProperty("op", out var opElement)) - { - throw new ArgumentException("Invalid patch"); - } - - var op = opElement.GetString() ?? throw new InvalidOperationException("Operation cannot be null"); - - if (!operation.TryGetProperty("path", out var pathElement)) - { - throw new ArgumentException(op, nameof(patch)); - } - - var path = pathElement.GetString() ?? throw new InvalidOperationException("Operation cannot be null"); - - if (!JsonPointer.TryParse(path, out var location)) - { - throw new ArgumentException(op, nameof(patch)); - } - - switch (op) - { - case "test": - { - if (!operation.TryGetProperty("value", out var value)) - { - throw new ArgumentException(op, nameof(patch)); - } - - if (!location.TryGetValue(target, out var tested)) - { - throw new ArgumentException(op, nameof(patch)); - } - - using var doc = tested.ToJsonDocument(); - if (!comparer.Equals(doc.RootElement, value)) - { - throw new JsonPatchException(op, "Test failed"); - } - - break; - } - case "add": - { - if (!operation.TryGetProperty("value", out var value)) - { - throw new ArgumentException(op, nameof(patch)); - } - - var valueBuilder = new JsonDocumentBuilder(value); - if (location.TryAddIfAbsent(ref target, valueBuilder)) continue; // try insert without replace - if (!location.TryReplace(ref target, valueBuilder)) // try insert without replace - { - throw new JsonPatchException(op, "Add failed"); - } - - break; - } - case "remove" when !location.TryRemove(ref target): - throw new JsonPatchException(op, "Add failed"); - case "replace": - { - if (!operation.TryGetProperty("value", out var value)) - { - throw new ArgumentException(op, nameof(patch)); - } - - var valueBuilder = new JsonDocumentBuilder(value); - if (!location.TryReplace(ref target, valueBuilder)) - { - throw new JsonPatchException(op, "Replace failed"); - } - - break; - } - case "move": - { - if (!operation.TryGetProperty("from", out var fromElement)) - { - throw new ArgumentException(op, nameof(patch)); - } - - var from = fromElement.GetString() ?? - throw new InvalidOperationException("From element cannot be null"); - - if (!JsonPointer.TryParse(from, out var fromPointer)) - { - throw new ArgumentException(op, nameof(patch)); - } - - if (!fromPointer.TryGetValue(target, out var value)) - { - throw new JsonPatchException(op, "Move failed"); - } - - if (!fromPointer.TryRemove(ref target)) - { - throw new JsonPatchException(op, "Move failed"); - } - - if (!location.TryAddIfAbsent(ref target, value)) - { - if (!location.TryReplace(ref target, value)) // try insert without replace - { - throw new JsonPatchException(op, "Move failed"); - } - } - - break; - } - case "copy": - { - if (!operation.TryGetProperty("from", out var fromElement)) - { - throw new ArgumentException(op, nameof(patch)); - } - - var from = fromElement.GetString() ?? - throw new InvalidOperationException("from cannot be null"); - if (!JsonPointer.TryParse(from, out var fromPointer)) - { - throw new ArgumentException(op, nameof(patch)); - } - - if (!fromPointer.TryGetValue(target, out var value)) - { - throw new JsonPatchException(op, "Copy failed"); - } - - if (!location.TryAddIfAbsent(ref target, value)) - { - if (!location.TryReplace(ref target, value)) // try insert without replace - { - throw new JsonPatchException(op, "Move failed"); - } - } - - break; - } - } - } - } - - /// - /// Builds a JSON Patch as defined in RFC 6902 - /// given two JSON values, a source and a target. - /// - /// - /// It is the users responsibility to properly Dispose the returned value - /// - /// The source JSON value. - /// The target JSON value. - /// A JSON Merge Patch to convert the source JSON value to the target JSON value - public static JsonDocument FromDiff(JsonElement source, - JsonElement target) - { - return _FromDiff(source, target, "").ToJsonDocument(); - } - - private static JsonDocumentBuilder _FromDiff(JsonElement source, - JsonElement target, - string path) - { - var builder = new JsonDocumentBuilder(JsonValueKind.Array); - - var comparer = JsonElementEqualityComparer.Instance; - - if (comparer.Equals(source, target)) - { - return builder; - } - - if (source.ValueKind == JsonValueKind.Array && target.ValueKind == JsonValueKind.Array) - { - var common = Math.Min(source.GetArrayLength(), target.GetArrayLength()); - for (var i = 0; i < common; ++i) - { - var buffer = new StringBuilder(path); - buffer.Append("/"); - buffer.Append(i.ToString()); - var tempDiff = _FromDiff(source[i], target[i], buffer.ToString()); - foreach (var item in tempDiff.EnumerateArray()) - { - builder.AddArrayItem(item); - } - } - - // Element in source, not in target - remove - for (var i = source.GetArrayLength(); i-- > target.GetArrayLength();) - { - var buffer = new StringBuilder(path); - buffer.Append('/'); - buffer.Append(i.ToString()); - var valBuilder = new JsonDocumentBuilder(JsonValueKind.Object); - valBuilder.AddProperty("op", new JsonDocumentBuilder("remove")); - valBuilder.AddProperty("path", new JsonDocumentBuilder(buffer.ToString())); - builder.AddArrayItem(valBuilder); - } - - // Element in target, not in source - add, - for (var i = source.GetArrayLength(); i < target.GetArrayLength(); ++i) - { - var a = target[i]; - var buffer = new StringBuilder(path); - buffer.Append("/"); - buffer.Append(i.ToString()); - var valBuilder = new JsonDocumentBuilder(JsonValueKind.Object); - valBuilder.AddProperty("op", new JsonDocumentBuilder("add")); - valBuilder.AddProperty("path", new JsonDocumentBuilder(buffer.ToString())); - valBuilder.AddProperty("value", new JsonDocumentBuilder(a)); - builder.AddArrayItem(valBuilder); - } - } - else if (source.ValueKind == JsonValueKind.Object && target.ValueKind == JsonValueKind.Object) - { - foreach (var a in source.EnumerateObject()) - { - var buffer = new StringBuilder(path); - buffer.Append("/"); - buffer.Append(JsonPointer.Escape(a.Name)); - - if (target.TryGetProperty(a.Name, out var element)) - { - var tempDiff = _FromDiff(a.Value, element, buffer.ToString()); - foreach (var item in tempDiff.EnumerateArray()) - { - builder.AddArrayItem(item); - } - } - else - { - var valBuilder = new JsonDocumentBuilder(JsonValueKind.Object); - valBuilder.AddProperty("op", new JsonDocumentBuilder("remove")); - valBuilder.AddProperty("path", new JsonDocumentBuilder(buffer.ToString())); - builder.AddArrayItem(valBuilder); - } - } - - foreach (var a in target.EnumerateObject()) - { - if (source.TryGetProperty(a.Name, out _)) continue; - - var buffer = new StringBuilder(path); - buffer.Append("/"); - buffer.Append(JsonPointer.Escape(a.Name)); - var valBuilder = new JsonDocumentBuilder(JsonValueKind.Object); - valBuilder.AddProperty("op", new JsonDocumentBuilder("add")); - valBuilder.AddProperty("path", new JsonDocumentBuilder(buffer.ToString())); - valBuilder.AddProperty("value", new JsonDocumentBuilder(a.Value)); - builder.AddArrayItem(valBuilder); - } - } - else - { - var valBuilder = new JsonDocumentBuilder(JsonValueKind.Object); - valBuilder.AddProperty("op", new JsonDocumentBuilder("replace")); - valBuilder.AddProperty("path", new JsonDocumentBuilder(path)); - valBuilder.AddProperty("value", new JsonDocumentBuilder(target)); - builder.AddArrayItem(valBuilder); - } - - return builder; - } - } -} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointer.cs deleted file mode 100644 index af42a36d..00000000 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointer.cs +++ /dev/null @@ -1,590 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.Json; - -namespace AWS.Lambda.Powertools.JMESPath.Utilities -{ - /// - /// Represents a JSON Pointer as defined by RFC 6901 - /// - /// - /// The following example shows how to get a value at a referenced location in a JSON document. - /// - /// using System; - /// using System.Diagnostics; - /// using System.Text.Json; - /// - /// public class Example - /// { - /// public static void Main() - /// { - /// using var doc = JsonDocument.Parse(@" - /// [ - /// { ""category"": ""reference"", - /// ""author"": ""Nigel Rees"", - /// ""title"": ""Sayings of the Century"", - /// ""price"": 8.95 - /// }, - /// { ""category"": ""fiction"", - /// ""author"": ""Evelyn Waugh"", - /// ""title"": ""Sword of Honour"", - /// ""price"": 12.99 - /// } - /// ] - /// "); - /// - /// var options = new JsonSerializerOptions() { WriteIndented = true }; - /// - /// JsonPointer pointer = JsonPointer.Parse("/1/author"); - /// - /// JsonElement element; - /// - /// if (pointer.TryGetValue(doc.RootElement, out element)) - /// { - /// Console.WriteLine($"{JsonSerializer.Serialize(element, options)}\n"); - /// } - /// } - /// } - /// - /// Output: - /// - /// - /// "Evelyn Waugh" - /// - /// - - public sealed class JsonPointer : IEnumerable, IEquatable - { - /// Gets a singleton instance of a to the root value of a JSON document. - private static JsonPointer Default {get;} = new(); - - private enum JsonPointerState {Start, Escaped, Delim} - - /// - /// Returns a list of (unescaped) reference tokens - /// - public IReadOnlyList Tokens {get;} - - /// - /// Constructs a JSON Pointer to the root value of a JSON document - /// - private JsonPointer() - { - Tokens = new List(); - } - - /// - /// Constructs a JSON Pointer from a list of (unescaped) reference tokens - /// - /// A list of (unescaped) reference tokens. - - public JsonPointer(IReadOnlyList tokens) - { - Tokens = tokens; - } - - /// - /// Parses a JSON Pointer represented as a string value or a - /// fragment identifier (starts with #) into a . - /// - /// A JSON Pointer represented as a string or a fragment identifier. - /// A . - /// - /// The is . - /// - /// - /// The is invalid. - /// - public static JsonPointer Parse(string input) - { - if (!TryParse(input, out var pointer)) - { - throw new ArgumentException("The provided JSON Pointer is invalid."); - } - return pointer; - } - - /// - /// Parses a JSON Pointer represented as a string value or a - /// fragment identifier (starts with #) into a . - /// - /// A JSON Pointer represented as a string or a fragment identifier. - /// The JsonPointer. - /// true if the input string can be parsed into a list of reference tokens, false otherwise. - /// - /// The is . - /// - public static bool TryParse(string input, out JsonPointer pointer) - { - if (input == null) - { - throw new ArgumentNullException(nameof(input)); - } - var tokens = new List(); - - if (input.Length == 0 || input.Equals("#")) - { - pointer = new JsonPointer(tokens); - return true; - } - - var state = JsonPointerState.Start; - var index = 0; - var buffer = new StringBuilder(); - - if (input[0] == '#') - { - input = Uri.UnescapeDataString(input); - index = 1; - } - - while (index < input.Length) - { - var done = false; - while (index < input.Length && !done) - { - if (state == JsonPointerState.Start) - { - switch (input[index]) - { - case '/': - state = JsonPointerState.Delim; - break; - default: - pointer = Default; - return false; - } - } - else if (state == JsonPointerState.Delim) - { - switch (input[index]) - { - case '/': - done = true; - break; - case '~': - state = JsonPointerState.Escaped; - break; - default: - buffer.Append(input[index]); - break; - } - } - else if (state == JsonPointerState.Escaped) - { - switch (input[index]) - { - case '0': - buffer.Append('~'); - state = JsonPointerState.Delim; - break; - case '1': - buffer.Append('/'); - state = JsonPointerState.Delim; - break; - default: - pointer = Default; - return false; - } - } - else - { - pointer = Default; - return false; - } - - ++index; - } - tokens.Add(buffer.ToString()); - buffer.Clear(); - } - if (buffer.Length > 0) - { - tokens.Add(buffer.ToString()); - } - pointer = new JsonPointer(tokens); - return true; - } - - /// - /// Returns an enumerator that iterates through a list of reference tokens. - /// - /// An IEnumerator<string> for a list of reference tokens. - public IEnumerator GetEnumerator() - { - return Tokens.GetEnumerator(); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - /// - /// Returns a JSON Pointer represented as a string value. - /// - /// A JSON Pointer represented as a string value. - - public override string ToString() - { - var buffer = new StringBuilder(); - foreach (var token in Tokens) - { - buffer.Append('/'); - Escape(token, buffer); - } - return buffer.ToString(); - } - - /// - /// Returns a string representing the JSON Pointer as a URI fragment identifier - /// - /// A JSON Pointer represented as a fragment identifier. - public string ToUriFragment() - { - var buffer = new StringBuilder(); - - buffer.Append('#'); - foreach (var token in Tokens) - { - buffer.Append('/'); - var s = Uri.EscapeDataString(token); - var span = s.AsSpan(); - for (var i = 0; i < span.Length; ++i) - { - var c = span[i]; - switch (c) - { - case '~': - buffer.Append('~'); - buffer.Append('0'); - break; - case '/': - buffer.Append('~'); - buffer.Append('1'); - break; - default: - buffer.Append(c); - break; - } - } - } - return buffer.ToString(); - } - - /// - /// Determines whether two JSONPointer objects have the same value. - /// - /// - /// true if other is a and has exactly the same reference tokens as this instance; otherwise, false. - /// If other is null, the method returns false. - public bool Equals(JsonPointer other) - { - if (other == null) - { - return false; - } - if (Tokens.Count != other.Tokens.Count) - { - return false; - } - for (var i = 0; i < Tokens.Count; ++i) - { - if (!Tokens[i].Equals(other.Tokens[i])) - { - return false; - } - } - return true; - } - /// - /// Determines whether this instance and a specified object, which must also be a JSONPointer object, have the same value. - /// - /// - /// - public override bool Equals(object other) - { - if (other == null) - { - return false; - } - - return Equals((JsonPointer)other); - } - - /// - /// Returns the hash code for this - /// - /// A 32-bit signed integer hash code. - /// - public override int GetHashCode() - { - var hashCode = Tokens.GetHashCode(); - foreach (var token in Tokens) - { - hashCode += 17*token.GetHashCode(); - } - return hashCode; - } - - /// - /// Returns true if the provided contains a value at the referenced location. - /// - /// The root that is to be queried. - /// true if the provided contains a value at the referenced location, otherwise false. - public bool ContainsValue(JsonElement root) - { - var value = root; - - foreach (var token in Tokens) - { - if (value.ValueKind == JsonValueKind.Array) - { - if (token == "-") - { - return false; - } - - if (!int.TryParse(token, out var index)) - { - return false; - } - if (index >= value.GetArrayLength()) - { - return false; - } - value = value[index]; - } - else if (value.ValueKind == JsonValueKind.Object) - { - if (!value.TryGetProperty(token, out value)) - { - return false; - } - } - else - { - return false; - } - } - - return true; - } - - /// - /// Returns true if the provided contains a value at the referenced location. - /// - /// The root that is to be queried. - /// The JSON string or URI Fragment representation of the JSON pointer. - /// true if the provided contains a value at the referenced location, otherwise false. - /// - /// The is . - /// - public static bool ContainsValue(JsonElement root, string pointer) - { - if (pointer == null) - { - throw new ArgumentNullException(nameof(pointer)); - } - - if (!TryParse(pointer, out var location)) - { - return false; - } - var value = root; - - foreach (var token in location.Tokens) - { - if (value.ValueKind == JsonValueKind.Array) - { - if (token == "-") - { - return false; - } - - if (!int.TryParse(token, out var index)) - { - return false; - } - if (index >= value.GetArrayLength()) - { - return false; - } - value = value[index]; - } - else if (value.ValueKind == JsonValueKind.Object) - { - if (!value.TryGetProperty(token, out value)) - { - return false; - } - } - else - { - return false; - } - } - - return true; - } - - /// - /// Gets the value at the referenced location in the provided . - /// - /// The root that is to be queried. - /// Contains the value at the referenced location, if found. - /// true if the value was found at the referenced location, otherwise false. - private bool TryGetValue(JsonElement root, out JsonElement value) - { - value = root; - - foreach (var token in Tokens) - { - if (value.ValueKind == JsonValueKind.Array) - { - if (token == "-") - { - return false; - } - - if (!int.TryParse(token, out var index)) - { - return false; - } - if (index >= value.GetArrayLength()) - { - return false; - } - value = value[index]; - } - else if (value.ValueKind == JsonValueKind.Object) - { - if (!value.TryGetProperty(token, out value)) - { - return false; - } - } - else - { - return false; - } - } - - return true; - } - - /// - /// Creates a new JsonPointer by appending a name token to the provided JsonPointer. - /// - /// The provided JsonPointer - /// A name token - /// A new JsonPointer - public static JsonPointer Append(JsonPointer pointer, string token) - { - var tokens = new List(pointer.Tokens); - tokens.Add(token); - return new JsonPointer(tokens); - } - - /// - /// Creates a new JsonPointer by appending an index token to the provided JsonPointer. - /// - /// The provided JsonPointer - /// An index token - /// A new JsonPointer - public static JsonPointer Append(JsonPointer pointer, int token) - { - var tokens = new List(pointer.Tokens); - tokens.Add(token.ToString()); - return new JsonPointer(tokens); - } - - /// - /// Gets the value at the referenced location in the provided . - /// - /// The root that is to be queried. - /// The JSON string or URI Fragment representation of the JSON pointer. - /// Contains the value at the referenced location, if found. - /// true if the value was found at the referenced location, otherwise false. - /// - /// The is . - /// - public static bool TryGetValue(JsonElement root, string pointer, out JsonElement value) - { - if (pointer == null) - { - throw new ArgumentNullException(nameof(pointer)); - } - - if (!TryParse(pointer, out var location)) - { - value = root; - return false; - } - - return location.TryGetValue(root, out value); - } - - /// - /// Escapes a JSON Pointer token - /// - /// - /// - /// - /// The is . - /// - public static string Escape(string token) - { - if (token == null) - { - throw new ArgumentNullException(nameof(token)); - } - - var buffer = new StringBuilder(); - Escape(token, buffer); - return buffer.ToString(); - } - - private static void Escape(string token, StringBuilder buffer) - { - if (token == null) - { - throw new ArgumentNullException(nameof(token)); - } - - foreach (var c in token) - { - switch (c) - { - case '~': - buffer.Append('~'); - buffer.Append('0'); - break; - case '/': - buffer.Append('~'); - buffer.Append('1'); - break; - default: - buffer.Append(c); - break; - } - } - } - } -} diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointerExtensions.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointerExtensions.cs deleted file mode 100644 index 8926f510..00000000 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonPointerExtensions.cs +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -using System.Collections.Generic; -using System.Text.Json; - -namespace AWS.Lambda.Powertools.JMESPath.Utilities -{ - internal static class JsonPointerExtensions - { - private static bool TryResolve(string token, JsonDocumentBuilder current, out JsonDocumentBuilder result) - { - result = current; - - switch (result.ValueKind) - { - case JsonValueKind.Array when token == "-": - return false; - case JsonValueKind.Array: - { - if (!int.TryParse(token, out var index)) - { - return false; - } - - if (index >= result.GetArrayLength()) - { - return false; - } - - result = result[index]; - break; - } - case JsonValueKind.Object: - { - if (!result.TryGetProperty(token, out result)) - { - return false; - } - - break; - } - default: - return false; - } - - return true; - } - - public static JsonPointer ToDefinitePath(this JsonPointer pointer, JsonDocumentBuilder value) - { - if (value.ValueKind != JsonValueKind.Array || pointer.Tokens.Count <= 0 || - pointer.Tokens[pointer.Tokens.Count - 1] != "-") return pointer; - var tokens = new List(); - for (var i = 0; i < pointer.Tokens.Count - 1; ++i) - { - tokens.Add(pointer.Tokens[i]); - } - - tokens.Add(value.GetArrayLength().ToString()); - return new JsonPointer(tokens); - - } - - public static bool TryGetValue(this JsonPointer pointer, JsonDocumentBuilder root, - out JsonDocumentBuilder value) - { - value = root; - - foreach (var token in pointer) - { - if (!TryResolve(token, value, out value)) - { - return false; - } - } - - return true; - } - - public static bool TryAdd(this JsonPointer location, - ref JsonDocumentBuilder root, - JsonDocumentBuilder value) - { - if (!TryGetToken(location, root, out var current, out var token)) return false; - - switch (current.ValueKind) - { - case JsonValueKind.Array when token.Length == 1 && token[0] == '-': - current.AddArrayItem(value); - break; - case JsonValueKind.Array: - { - if (!TryGetArray(value, token, current)) return false; - - break; - } - case JsonValueKind.Object: - { - if (current.ContainsPropertyName(token)) - { - current.RemoveProperty(token); - } - - current.AddProperty(token, value); - break; - } - default: - return false; - } - - return true; - } - - private static bool TryGetToken(JsonPointer location, JsonDocumentBuilder root, out JsonDocumentBuilder current, - out string token) - { - current = root; - token = ""; - - using var enumerator = location.GetEnumerator(); - var more = enumerator.MoveNext(); - if (!more) - { - return false; - } - - while (more) - { - token = enumerator.Current; - more = enumerator.MoveNext(); - if (!more) continue; - if (!TryResolve(token, current, out current)) - { - return false; - } - } - - return true; - } - - public static bool TryAddIfAbsent(this JsonPointer location, - ref JsonDocumentBuilder root, - JsonDocumentBuilder value) - { - if (!TryGetToken(location, root, out var current, out var token)) return false; - - switch (current.ValueKind) - { - case JsonValueKind.Array when token.Length == 1 && token[0] == '-': - current.AddArrayItem(value); - break; - case JsonValueKind.Array: - { - if (!TryGetArray(value, token, current)) return false; - - break; - } - case JsonValueKind.Object when current.ContainsPropertyName(token): - return false; - case JsonValueKind.Object: - current.AddProperty(token, value); - break; - default: - return false; - } - - return true; - } - - private static bool TryGetArray(JsonDocumentBuilder value, string token, JsonDocumentBuilder current) - { - if (!int.TryParse(token, out var index)) - { - return false; - } - - if (index > current.GetArrayLength()) - { - return false; - } - - if (index == current.GetArrayLength()) - { - current.AddArrayItem(value); - } - else - { - current.InsertArrayItem(index, value); - } - - return true; - } - - public static bool TryRemove(this JsonPointer location, - ref JsonDocumentBuilder root) - { - if (!TryGetToken(location, root, out var current, out var token)) return false; - - switch (current.ValueKind) - { - case JsonValueKind.Array when token.Length == 1 && token[0] == '-': - return false; - case JsonValueKind.Array: - { - if (!int.TryParse(token, out var index)) - { - return false; - } - - if (index >= current.GetArrayLength()) - { - return false; - } - - current.RemoveArrayItemAt(index); - break; - } - case JsonValueKind.Object: - { - if (current.ContainsPropertyName(token)) - { - current.RemoveProperty(token); - } - - break; - } - default: - return false; - } - - return true; - } - - public static bool TryReplace(this JsonPointer location, - ref JsonDocumentBuilder root, - JsonDocumentBuilder value) - { - if (!TryGetToken(location, root, out var current, out var token)) return false; - - switch (current.ValueKind) - { - case JsonValueKind.Array when token.Length == 1 && token[0] == '-': - return false; - case JsonValueKind.Array: - { - if (!int.TryParse(token, out var index)) - { - return false; - } - - if (index >= current.GetArrayLength()) - { - return false; - } - - current[index] = value; - break; - } - case JsonValueKind.Object: - { - if (current.ContainsPropertyName(token)) - { - current.RemoveProperty(token); - } - else - { - return false; - } - - current.AddProperty(token, value); - break; - } - default: - return false; - } - - return true; - } - } -} \ No newline at end of file From 4678224e6c310de8fdccd47c3662ff99fe1b0c1b Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Fri, 19 Apr 2024 11:44:53 +0100 Subject: [PATCH 18/28] sonar fixes --- .../JmesPathParser.cs | 3 - .../UnaryOperator.cs | 10 +-- .../ValueComparer.cs | 84 +++++++++++-------- 3 files changed, 52 insertions(+), 45 deletions(-) diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs index 43de7f04..7ca72351 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs @@ -1382,9 +1382,6 @@ internal JsonTransformer Parse() } break; case JmesPathState.ValExpr: - PushToken(new Token(new IdentifierSelector(buffer.ToString()))); - _stateStack.Pop(); - break; case JmesPathState.IdentifierOrFunctionExpr: PushToken(new Token(new IdentifierSelector(buffer.ToString()))); _stateStack.Pop(); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs index d7b02b7d..6e21c0fd 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs @@ -47,9 +47,9 @@ private NotOperator() : base(Operator.Not) {} - public override bool TryEvaluate(IValue val, out IValue result) + public override bool TryEvaluate(IValue elem, out IValue result) { - result = Expression.IsFalse(val) ? JsonConstants.True : JsonConstants.False; + result = Expression.IsFalse(elem) ? JsonConstants.True : JsonConstants.False; return true; } @@ -69,14 +69,14 @@ internal RegexOperator(Regex regex) _regex = regex; } - public override bool TryEvaluate(IValue val, out IValue result) + public override bool TryEvaluate(IValue elem, out IValue result) { - if (val.Type != JmesPathType.String) + if (elem.Type != JmesPathType.String) { result = JsonConstants.Null; return false; // type error } - result = _regex.IsMatch(val.GetString()) ? JsonConstants.True : JsonConstants.False; + result = _regex.IsMatch(elem.GetString()) ? JsonConstants.True : JsonConstants.False; return true; } diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/ValueComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/ValueComparer.cs index a09d6bff..fb1b8c48 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/ValueComparer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/ValueComparer.cs @@ -110,47 +110,12 @@ public int Compare(IValue lhs, IValue rhs) case JmesPathType.Array: { - var enumerator1 = lhs.EnumerateArray(); - var enumerator2 = rhs.EnumerateArray(); - var result1 = enumerator1.MoveNext(); - var result2 = enumerator2.MoveNext(); - while (result1 && result2) - { - var diff = Compare(enumerator1.Current, enumerator2.Current); - if (diff != 0) - { - return diff; - } - result1 = enumerator1.MoveNext(); - result2 = enumerator2.MoveNext(); - } - return result1 ? 1 : result2 ? -1 : 0; + return ArrayComparer(lhs, rhs); } case JmesPathType.Object: { - // OrderBy performs a stable sort (Note that supports duplicate property names) - using var enumerator1 = lhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); - using var enumerator2 = rhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); - - var result1 = enumerator1.MoveNext(); - var result2 = enumerator2.MoveNext(); - while (result1 && result2) - { - if (enumerator1.Current.Name != enumerator2.Current.Name) - { - return string.Compare(enumerator1.Current.Name, enumerator2.Current.Name, StringComparison.Ordinal); - } - var diff = Compare(enumerator1.Current.Value, enumerator2.Current.Value); - if (diff != 0) - { - return diff; - } - result1 = enumerator1.MoveNext(); - result2 = enumerator2.MoveNext(); - } - - return result1 ? 1 : result2 ? -1 : 0; + return ObjectComparer(lhs, rhs); } default: @@ -158,6 +123,51 @@ public int Compare(IValue lhs, IValue rhs) } } + private int ArrayComparer(IValue lhs, IValue rhs) + { + var enumerator1 = lhs.EnumerateArray(); + var enumerator2 = rhs.EnumerateArray(); + var result1 = enumerator1.MoveNext(); + var result2 = enumerator2.MoveNext(); + while (result1 && result2) + { + var diff = Compare(enumerator1.Current, enumerator2.Current); + if (diff != 0) + { + return diff; + } + result1 = enumerator1.MoveNext(); + result2 = enumerator2.MoveNext(); + } + return result1 ? 1 : result2 ? -1 : 0; + } + + private int ObjectComparer(IValue lhs, IValue rhs) + { + // OrderBy performs a stable sort (Note that supports duplicate property names) + using var enumerator1 = lhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); + using var enumerator2 = rhs.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal).GetEnumerator(); + + var result1 = enumerator1.MoveNext(); + var result2 = enumerator2.MoveNext(); + while (result1 && result2) + { + if (enumerator1.Current.Name != enumerator2.Current.Name) + { + return string.Compare(enumerator1.Current.Name, enumerator2.Current.Name, StringComparison.Ordinal); + } + var diff = Compare(enumerator1.Current.Value, enumerator2.Current.Value); + if (diff != 0) + { + return diff; + } + result1 = enumerator1.MoveNext(); + result2 = enumerator2.MoveNext(); + } + + return result1 ? 1 : result2 ? -1 : 0; + } + int System.Collections.IComparer.Compare(object x, object y) { return Compare((IValue)x, (IValue)y); From 910e8592fe7ba2445931bf108934d5daf0b0b499 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Fri, 19 Apr 2024 12:04:14 +0100 Subject: [PATCH 19/28] sonar fixes --- .../Expression.cs | 1 - .../JmesPathParser.cs | 24 ++++++++++--------- .../JsonTransformer.cs | 4 +--- .../AWS.Lambda.Powertools.JMESPath/Value.cs | 4 ++-- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs index 9e3265ba..426278ea 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs @@ -728,7 +728,6 @@ public bool TryEvaluate(DynamicResources resources, internal static bool IsFalse(IValue val) { - var comparer = ValueEqualityComparer.Instance; switch (val.Type) { case JmesPathType.False: diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs index 7ca72351..4b7a37f7 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs @@ -149,8 +149,10 @@ internal JsonTransformer Parse() PushToken(new Token(TokenType.CurrentNode)); _stateStack.Push(JmesPathState.Start); + var syntaxErrorMsg = "Syntax error"; while (_index < _span.Length) { + var expectedRightBracket = "Expected right bracket"; switch (_stateStack.Peek()) { case JmesPathState.Start: @@ -215,7 +217,7 @@ internal JsonTransformer Parse() } else { - throw new JmesPathParseException("Syntax error", _line, _column); + throw new JmesPathParseException(syntaxErrorMsg, _line, _column); } break; } @@ -256,7 +258,7 @@ internal JsonTransformer Parse() } else { - throw new JmesPathParseException("Syntax error", _line, _column); + throw new JmesPathParseException(syntaxErrorMsg, _line, _column); } break; } @@ -978,7 +980,7 @@ internal JsonTransformer Parse() break; } default: - throw new JmesPathParseException("Expected right bracket", _line, _column); + throw new JmesPathParseException(expectedRightBracket, _line, _column); } break; case JmesPathState.RhsSliceExpressionStop : @@ -1012,7 +1014,7 @@ internal JsonTransformer Parse() ++_column; break; default: - throw new JmesPathParseException("Expected right bracket", _line, _column); + throw new JmesPathParseException(expectedRightBracket, _line, _column); } break; } @@ -1045,7 +1047,7 @@ internal JsonTransformer Parse() ++_column; break; default: - throw new JmesPathParseException("Expected right bracket", _line, _column); + throw new JmesPathParseException(expectedRightBracket, _line, _column); } break; } @@ -1059,7 +1061,7 @@ internal JsonTransformer Parse() ++_column; break; default: - throw new JmesPathParseException("Expected right bracket", _line, _column); + throw new JmesPathParseException(expectedRightBracket, _line, _column); } break; } @@ -1281,7 +1283,7 @@ internal JsonTransformer Parse() break; } default: - throw new JmesPathParseException("Expected right bracket", _line, _column); + throw new JmesPathParseException(expectedRightBracket, _line, _column); } break; } @@ -1301,7 +1303,7 @@ internal JsonTransformer Parse() break; } default: - throw new JmesPathParseException("Expected right bracket", _line, _column); + throw new JmesPathParseException(expectedRightBracket, _line, _column); } break; } @@ -1365,7 +1367,7 @@ internal JsonTransformer Parse() if (_stateStack.Count == 0) { - throw new JmesPathParseException("Syntax error", _line, _column); + throw new JmesPathParseException(syntaxErrorMsg, _line, _column); } while (_stateStack.Count > 1) { @@ -1378,7 +1380,7 @@ internal JsonTransformer Parse() } else { - throw new JmesPathParseException("Syntax error", _line, _column); + throw new JmesPathParseException(syntaxErrorMsg, _line, _column); } break; case JmesPathState.ValExpr: @@ -1390,7 +1392,7 @@ internal JsonTransformer Parse() _stateStack.Pop(); break; default: - throw new JmesPathParseException("Syntax error", _line, _column); + throw new JmesPathParseException(syntaxErrorMsg, _line, _column); } } diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs index 56aaa176..f7146737 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs @@ -18,9 +18,7 @@ namespace AWS.Lambda.Powertools.JMESPath { - internal sealed class DynamicResources - { - } + internal sealed class DynamicResources; /// /// Provides functionality for applying a JMESPath expression to transform a JSON document into /// another JSON document diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Value.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Value.cs index b6e17a8d..4b157cbe 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Value.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Value.cs @@ -619,7 +619,7 @@ public override string ToString() internal readonly struct ArrayValue : IValue { - private class ArrayEnumerator : IArrayValueEnumerator + private sealed class ArrayEnumerator : IArrayValueEnumerator { private readonly IList _value; private readonly System.Collections.IEnumerator _enumerator; @@ -728,7 +728,7 @@ public override string ToString() internal readonly struct ObjectValue : IValue { - private class ObjectEnumerator : IObjectValueEnumerator + private sealed class ObjectEnumerator : IObjectValueEnumerator { private readonly IDictionary _value; private readonly System.Collections.IEnumerator _enumerator; From 0c92aae470a16a62b5235e09756c4cd9e95bbf20 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Fri, 19 Apr 2024 14:53:05 +0100 Subject: [PATCH 20/28] sonar fixes and refactor --- .../Function.cs | 1504 ----------------- .../Functions/AbsFunction.cs | 39 + .../Functions/AvgFunction.cs | 50 + .../Functions/Base64Function.cs | 29 + .../Functions/Base64GzipFunction.cs | 42 + .../Functions/BaseFunction.cs | 15 + .../Functions/BuiltInFunctions.cs | 48 + .../Functions/CeilFunction.cs | 46 + .../Functions/ContainsFunction.cs | 66 + .../Functions/EndsWithFunction.cs | 39 + .../Functions/FloorFunction.cs | 46 + .../Functions/IFunction.cs | 9 + .../Functions/JoinFunction.cs | 54 + .../Functions/JsonFunction.cs | 25 + .../Functions/KeysFunction.cs | 40 + .../Functions/LengthFunction.cs | 55 + .../Functions/MapFunction.cs | 47 + .../Functions/MaxByFunction.cs | 83 + .../Functions/MaxFunction.cs | 70 + .../Functions/MergeFunction.cs | 59 + .../Functions/MinByFunction.cs | 83 + .../Functions/MinFunction.cs | 70 + .../Functions/NotNullFunction.cs | 29 + .../Functions/ReverseFunction.cs | 57 + .../Functions/SortByComparer.cs | 75 + .../Functions/SortByFunction.cs | 54 + .../Functions/SortFunction.cs | 63 + .../Functions/StartsWithFunction.cs | 38 + .../Functions/SumFunction.cs | 73 + .../Functions/ToArrayFunction.cs | 33 + .../Functions/ToNumberFunction.cs | 53 + .../Functions/ToStringFunction.cs | 42 + .../Functions/TypeFunction.cs | 50 + .../Functions/ValuesFunction.cs | 39 + .../JmesPathParser.cs | 1 + .../AWS.Lambda.Powertools.JMESPath/Token.cs | 1 + 36 files changed, 1623 insertions(+), 1504 deletions(-) delete mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Function.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64Function.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64GzipFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BuiltInFunctions.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JsonFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByComparer.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Function.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Function.cs deleted file mode 100644 index 294633a1..00000000 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Function.cs +++ /dev/null @@ -1,1504 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Text; -using System.Text.Json; - -namespace AWS.Lambda.Powertools.JMESPath -{ - internal sealed class SortByComparer : IComparer, System.Collections.IComparer - { - private readonly DynamicResources _resources; - private readonly IExpression _expr; - - internal bool IsValid { get; set; } = true; - - internal SortByComparer(DynamicResources resources, - IExpression expr) - { - _resources = resources; - _expr = expr; - } - - public int Compare(IValue lhs, IValue rhs) - { - var comparer = ValueComparer.Instance; - - if (!IsValid) - { - return 0; - } - - if (!_expr.TryEvaluate(_resources, lhs, out var key1)) - { - IsValid = false; - return 0; - } - - var isNumber1 = key1.Type == JmesPathType.Number; - var isString1 = key1.Type == JmesPathType.String; - if (!(isNumber1 || isString1)) - { - IsValid = false; - return 0; - } - - if (!_expr.TryEvaluate(_resources, rhs, out var key2)) - { - IsValid = false; - return 0; - } - - var isNumber2 = key2.Type == JmesPathType.Number; - var isString2 = key2.Type == JmesPathType.String; - if (isNumber2 == isNumber1 && isString2 == isString1) return comparer.Compare(key1, key2); - IsValid = false; - return 0; - } - - int System.Collections.IComparer.Compare(object x, object y) - { - return Compare((IValue)x, (IValue)y); - } - } - - internal interface IFunction - { - int? Arity { get; } - bool TryEvaluate(DynamicResources resources, IList args, out IValue element); - } - - internal abstract class BaseFunction : IFunction - { - private protected BaseFunction(int? argCount) - { - Arity = argCount; - } - - public int? Arity { get; } - - public abstract bool TryEvaluate(DynamicResources resources, IList args, out IValue element); - } - - internal sealed class AbsFunction : BaseFunction - { - internal AbsFunction() - : base(1) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - var arg = args[0]; - - if (arg.TryGetDecimal(out var decVal)) - { - element = new DecimalValue(decVal >= 0 ? decVal : -decVal); - return true; - } - - if (arg.TryGetDouble(out var dblVal)) - { - element = new DecimalValue(dblVal >= 0 ? decVal : new decimal(-dblVal)); - return true; - } - - element = JsonConstants.Null; - return false; - } - - public override string ToString() - { - return "abs"; - } - } - - internal sealed class AvgFunction : BaseFunction - { - internal AvgFunction() - : base(1) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - var arg0 = args[0]; - if (arg0.Type != JmesPathType.Array || arg0.GetArrayLength() == 0) - { - element = JsonConstants.Null; - return false; - } - - if (!SumFunction.Instance.TryEvaluate(resources, args, out var sum)) - { - element = JsonConstants.Null; - return false; - } - - if (sum.TryGetDecimal(out var decVal)) - { - element = new DecimalValue(decVal / arg0.GetArrayLength()); - return true; - } - - if (sum.TryGetDouble(out var dblVal)) - { - element = new DoubleValue(dblVal / arg0.GetArrayLength()); - return true; - } - - element = JsonConstants.Null; - return false; - } - - public override string ToString() - { - return "avg"; - } - } - - internal sealed class CeilFunction : BaseFunction - { - internal CeilFunction() - : base(1) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, - out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - var val = args[0]; - if (val.Type != JmesPathType.Number) - { - element = JsonConstants.Null; - return false; - } - - if (val.TryGetDecimal(out var decVal)) - { - element = new DecimalValue(decimal.Ceiling(decVal)); - return true; - } - - if (val.TryGetDouble(out var dblVal)) - { - element = new DoubleValue(Math.Ceiling(dblVal)); - return true; - } - - element = JsonConstants.Null; - return false; - } - - public override string ToString() - { - return "ceil"; - } - } - - internal sealed class ContainsFunction : BaseFunction - { - internal ContainsFunction() - : base(2) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, - out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - var arg0 = args[0]; - var arg1 = args[1]; - - var comparer = ValueEqualityComparer.Instance; - - switch (arg0.Type) - { - case JmesPathType.Array: - if (arg0.EnumerateArray().Any(item => comparer.Equals(item, arg1))) - { - element = JsonConstants.True; - return true; - } - - element = JsonConstants.False; - return true; - case JmesPathType.String: - { - if (arg1.Type != JmesPathType.String) - { - element = JsonConstants.Null; - return false; - } - - var s0 = arg0.GetString(); - var s1 = arg1.GetString(); - if (s0.Contains(s1)) - { - element = JsonConstants.True; - return true; - } - - element = JsonConstants.False; - return true; - } - default: - { - element = JsonConstants.Null; - return false; - } - } - } - - public override string ToString() - { - return "contains"; - } - } - - internal sealed class EndsWithFunction : BaseFunction - { - internal EndsWithFunction() - : base(2) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, - out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - var arg0 = args[0]; - var arg1 = args[1]; - if (arg0.Type != JmesPathType.String - || arg1.Type != JmesPathType.String) - { - element = JsonConstants.Null; - return false; - } - - var s0 = arg0.GetString(); - var s1 = arg1.GetString(); - - if (s0.EndsWith(s1)) - { - element = JsonConstants.True; - } - else - { - element = JsonConstants.False; - } - - return true; - } - - public override string ToString() - { - return "ends_with"; - } - } - - internal sealed class FloorFunction : BaseFunction - { - internal FloorFunction() - : base(1) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, - out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - var val = args[0]; - if (val.Type != JmesPathType.Number) - { - element = JsonConstants.Null; - return false; - } - - if (val.TryGetDecimal(out var decVal)) - { - element = new DecimalValue(decimal.Floor(decVal)); - return true; - } - - if (val.TryGetDouble(out var dblVal)) - { - element = new DoubleValue(Math.Floor(dblVal)); - return true; - } - - element = JsonConstants.Null; - return false; - } - - public override string ToString() - { - return "floor"; - } - } - - internal sealed class JoinFunction : BaseFunction - { - internal JoinFunction() - : base(2) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - var arg0 = args[0]; - var arg1 = args[1]; - - if (!(arg0.Type == JmesPathType.String && args[1].Type == JmesPathType.Array)) - { - element = JsonConstants.Null; - return false; - } - - var sep = arg0.GetString(); - var buf = new StringBuilder(); - foreach (var j in arg1.EnumerateArray()) - { - if (j.Type != JmesPathType.String) - { - element = JsonConstants.Null; - return false; - } - - if (buf.Length != 0) - { - buf.Append(sep); - } - - var sv = j.GetString(); - buf.Append(sv); - } - - element = new StringValue(buf.ToString()); - return true; - } - - public override string ToString() - { - return "join"; - } - } - - internal sealed class KeysFunction : BaseFunction - { - internal KeysFunction() - : base(1) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, - out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - var arg0 = args[0]; - if (arg0.Type != JmesPathType.Object) - { - element = JsonConstants.Null; - return false; - } - - var values = new List(); - - foreach (var property in arg0.EnumerateObject()) - { - values.Add(new StringValue(property.Name)); - } - - element = new ArrayValue(values); - return true; - } - - public override string ToString() - { - return "keys"; - } - } - - internal sealed class LengthFunction : BaseFunction - { - internal LengthFunction() - : base(1) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, - out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - var arg0 = args[0]; - - switch (arg0.Type) - { - case JmesPathType.Object: - { - var count = 0; - foreach (var unused in arg0.EnumerateObject()) - { - ++count; - } - - element = new DecimalValue(new decimal(count)); - return true; - } - case JmesPathType.Array: - element = new DecimalValue(new decimal(arg0.GetArrayLength())); - return true; - case JmesPathType.String: - { - var bytes = Encoding.UTF32.GetBytes(arg0.GetString().ToCharArray()); - element = new DecimalValue(new decimal(bytes.Length / 4)); - return true; - } - default: - { - element = JsonConstants.Null; - return false; - } - } - } - - public override string ToString() - { - return "length"; - } - } - - internal sealed class MaxFunction : BaseFunction - { - internal MaxFunction() - : base(1) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, - out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - var arg0 = args[0]; - if (arg0.Type != JmesPathType.Array) - { - element = JsonConstants.Null; - return false; - } - - if (arg0.GetArrayLength() == 0) - { - element = JsonConstants.Null; - return false; - } - - var isNumber = arg0[0].Type == JmesPathType.Number; - var isString = arg0[0].Type == JmesPathType.String; - if (!isNumber && !isString) - { - element = JsonConstants.Null; - return false; - } - - var greater = GtOperator.Instance; - var index = 0; - for (var i = 1; i < arg0.GetArrayLength(); ++i) - { - if (!(((arg0[i].Type == JmesPathType.Number) == isNumber) && - (arg0[i].Type == JmesPathType.String) == isString)) - { - element = JsonConstants.Null; - return false; - } - - if (!greater.TryEvaluate(arg0[i], arg0[index], out var value)) - { - element = JsonConstants.Null; - return false; - } - - if (Expression.IsTrue(value)) - { - index = i; - } - } - - element = arg0[index]; - return true; - } - - public override string ToString() - { - return "max"; - } - } - - internal sealed class MaxByFunction : BaseFunction - { - internal MaxByFunction() - : base(2) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - if (!(args[0].Type == JmesPathType.Array && args[1].Type == JmesPathType.Expression)) - { - element = JsonConstants.Null; - return false; - } - - var arg0 = args[0]; - if (arg0.GetArrayLength() == 0) - { - element = JsonConstants.Null; - return true; - } - - var expr = args[1].GetExpression(); - - if (!expr.TryEvaluate(resources, arg0[0], out var key1)) - { - element = JsonConstants.Null; - return false; - } - - var isNumber1 = key1.Type == JmesPathType.Number; - var isString1 = key1.Type == JmesPathType.String; - if (!(isNumber1 || isString1)) - { - element = JsonConstants.Null; - return false; - } - - var greater = GtOperator.Instance; - var index = 0; - for (var i = 1; i < arg0.GetArrayLength(); ++i) - { - if (!expr.TryEvaluate(resources, arg0[i], out var key2)) - { - element = JsonConstants.Null; - return false; - } - - var isNumber2 = key2.Type == JmesPathType.Number; - var isString2 = key2.Type == JmesPathType.String; - if (!(isNumber2 == isNumber1 && isString2 == isString1)) - { - element = JsonConstants.Null; - return false; - } - - if (!greater.TryEvaluate(key2, key1, out var value)) - { - element = JsonConstants.Null; - return false; - } - - if (value.Type != JmesPathType.True) continue; - key1 = key2; - index = i; - } - - element = arg0[index]; - return true; - } - - public override string ToString() - { - return "max_by"; - } - } - - internal sealed class MinFunction : BaseFunction - { - internal MinFunction() - : base(1) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, - out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - var arg0 = args[0]; - if (arg0.Type != JmesPathType.Array) - { - element = JsonConstants.Null; - return false; - } - - if (arg0.GetArrayLength() == 0) - { - element = JsonConstants.Null; - return false; - } - - var isNumber = arg0[0].Type == JmesPathType.Number; - var isString = arg0[0].Type == JmesPathType.String; - if (!isNumber && !isString) - { - element = JsonConstants.Null; - return false; - } - - var less = LtOperator.Instance; - var index = 0; - for (var i = 1; i < arg0.GetArrayLength(); ++i) - { - if (!(((arg0[i].Type == JmesPathType.Number) == isNumber) && - (arg0[i].Type == JmesPathType.String) == isString)) - { - element = JsonConstants.Null; - return false; - } - - if (!less.TryEvaluate(arg0[i], arg0[index], out var value)) - { - element = JsonConstants.Null; - return false; - } - - if (value.Type == JmesPathType.True) - { - index = i; - } - } - - element = arg0[index]; - return true; - } - - public override string ToString() - { - return "min"; - } - } - - internal sealed class MergeFunction : BaseFunction - { - internal MergeFunction() - : base(null) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) - { - if (!args.Any()) - { - element = JsonConstants.Null; - return false; - } - - var arg0 = args[0]; - if (arg0.Type != JmesPathType.Object) - { - element = JsonConstants.Null; - return false; - } - - if (args.Count == 1) - { - element = arg0; - return true; - } - - var dict = new Dictionary(); - for (var i = 0; i < args.Count; ++i) - { - var argi = args[i]; - if (argi.Type != JmesPathType.Object) - { - element = JsonConstants.Null; - return false; - } - - foreach (var item in argi.EnumerateObject()) - { - if (!dict.TryAdd(item.Name, item.Value)) - { - dict.Remove(item.Name); - dict.Add(item.Name, item.Value); - } - } - } - - element = new ObjectValue(dict); - return true; - } - - public override string ToString() - { - return "merge"; - } - } - - internal sealed class NotNullFunction : BaseFunction - { - internal NotNullFunction() - : base(null) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) - { - foreach (var arg in args) - { - if (arg.Type == JmesPathType.Null) continue; - element = arg; - return true; - } - - element = JsonConstants.Null; - return true; - } - - public override string ToString() - { - return "not_null"; - } - } - - internal sealed class ReverseFunction : BaseFunction - { - internal ReverseFunction() - : base(1) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - var arg0 = args[0]; - switch (arg0.Type) - { - case JmesPathType.String: - { - element = new StringValue(string.Join("", GraphemeClusters(arg0.GetString()).Reverse().ToArray())); - return true; - } - case JmesPathType.Array: - { - var list = new List(); - for (var i = arg0.GetArrayLength() - 1; i >= 0; --i) - { - list.Add(arg0[i]); - } - - element = new ArrayValue(list); - return true; - } - default: - element = JsonConstants.Null; - return false; - } - } - - private static IEnumerable GraphemeClusters(string s) - { - var enumerator = StringInfo.GetTextElementEnumerator(s); - while (enumerator.MoveNext()) - { - yield return (string)enumerator.Current; - } - } - - public override string ToString() - { - return "reverse"; - } - } - - internal sealed class MapFunction : BaseFunction - { - internal MapFunction() - : base(2) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - if (!(args[0].Type == JmesPathType.Expression && args[1].Type == JmesPathType.Array)) - { - element = JsonConstants.Null; - return false; - } - - var expr = args[0].GetExpression(); - var arg0 = args[1]; - - var list = new List(); - - foreach (var item in arg0.EnumerateArray()) - { - if (!expr.TryEvaluate(resources, item, out var val)) - { - element = JsonConstants.Null; - return false; - } - - list.Add(val); - } - - element = new ArrayValue(list); - return true; - } - - public override string ToString() - { - return "map"; - } - } - - internal sealed class MinByFunction : BaseFunction - { - internal MinByFunction() - : base(2) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - if (!(args[0].Type == JmesPathType.Array && args[1].Type == JmesPathType.Expression)) - { - element = JsonConstants.Null; - return false; - } - - var arg0 = args[0]; - if (arg0.GetArrayLength() == 0) - { - element = JsonConstants.Null; - return true; - } - - var expr = args[1].GetExpression(); - - if (!expr.TryEvaluate(resources, arg0[0], out var key1)) - { - element = JsonConstants.Null; - return false; - } - - var isNumber1 = key1.Type == JmesPathType.Number; - var isString1 = key1.Type == JmesPathType.String; - if (!(isNumber1 || isString1)) - { - element = JsonConstants.Null; - return false; - } - - var lessor = LtOperator.Instance; - var index = 0; - for (var i = 1; i < arg0.GetArrayLength(); ++i) - { - if (!expr.TryEvaluate(resources, arg0[i], out var key2)) - { - element = JsonConstants.Null; - return false; - } - - var isNumber2 = key2.Type == JmesPathType.Number; - var isString2 = key2.Type == JmesPathType.String; - if (!(isNumber2 == isNumber1 && isString2 == isString1)) - { - element = JsonConstants.Null; - return false; - } - - if (!lessor.TryEvaluate(key2, key1, out var value)) - { - element = JsonConstants.Null; - return false; - } - - if (value.Type == JmesPathType.True) - { - key1 = key2; - index = i; - } - } - - element = arg0[index]; - return true; - } - - public override string ToString() - { - return "min_by"; - } - } - - internal sealed class SortFunction : BaseFunction - { - internal SortFunction() - : base(1) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - var arg0 = args[0]; - if (arg0.Type != JmesPathType.Array) - { - element = JsonConstants.Null; - return false; - } - - if (arg0.GetArrayLength() <= 1) - { - element = arg0; - return true; - } - - var isNumber1 = arg0[0].Type == JmesPathType.Number; - var isString1 = arg0[0].Type == JmesPathType.String; - if (!isNumber1 && !isString1) - { - element = JsonConstants.Null; - return false; - } - - var comparer = ValueComparer.Instance; - - var list = new List(); - foreach (var item in arg0.EnumerateArray()) - { - var isNumber2 = item.Type == JmesPathType.Number; - var isString2 = item.Type == JmesPathType.String; - if (!(isNumber2 == isNumber1 && isString2 == isString1)) - { - element = JsonConstants.Null; - return false; - } - - list.Add(item); - } - - list.Sort(comparer); - element = new ArrayValue(list); - return true; - } - - public override string ToString() - { - return "sort"; - } - } - - internal sealed class SortByFunction : BaseFunction - { - internal SortByFunction() - : base(2) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - if (!(args[0].Type == JmesPathType.Array && args[1].Type == JmesPathType.Expression)) - { - element = JsonConstants.Null; - return false; - } - - var arg0 = args[0]; - if (arg0.GetArrayLength() <= 1) - { - element = arg0; - return true; - } - - var expr = args[1].GetExpression(); - - var list = new List(); - foreach (var item in arg0.EnumerateArray()) - { - list.Add(item); - } - - var comparer = new SortByComparer(resources, expr); - list.Sort(comparer); - if (comparer.IsValid) - { - element = new ArrayValue(list); - return true; - } - - element = JsonConstants.Null; - return false; - } - - public override string ToString() - { - return "sort_by"; - } - } - - internal sealed class StartsWithFunction : BaseFunction - { - internal StartsWithFunction() - : base(2) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, - out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - var arg0 = args[0]; - var arg1 = args[1]; - if (arg0.Type != JmesPathType.String - || arg1.Type != JmesPathType.String) - { - element = JsonConstants.Null; - return false; - } - - var s0 = arg0.GetString(); - var s1 = arg1.GetString(); - element = s0.StartsWith(s1) ? JsonConstants.True : JsonConstants.False; - - return true; - } - - public override string ToString() - { - return "starts_with"; - } - } - - internal sealed class SumFunction : BaseFunction - { - internal static SumFunction Instance { get; } = new(); - - internal SumFunction() - : base(1) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, - out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - var arg0 = args[0]; - if (arg0.Type != JmesPathType.Array) - { - element = JsonConstants.Null; - return false; - } - - foreach (var item in arg0.EnumerateArray()) - { - if (item.Type != JmesPathType.Number) - { - element = JsonConstants.Null; - return false; - } - } - - var success = true; - decimal decSum = 0; - foreach (var item in arg0.EnumerateArray()) - { - if (!item.TryGetDecimal(out var dec)) - { - success = false; - break; - } - - decSum += dec; - } - - if (success) - { - element = new DecimalValue(decSum); - return true; - } - - double dblSum = 0; - foreach (var item in arg0.EnumerateArray()) - { - if (!item.TryGetDouble(out var dbl)) - { - element = JsonConstants.Null; - return false; - } - - dblSum += dbl; - } - - element = new DoubleValue(dblSum); - return true; - } - - public override string ToString() - { - return "sum"; - } - } - - internal sealed class ToArrayFunction : BaseFunction - { - internal ToArrayFunction() - : base(1) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - var arg0 = args[0]; - if (arg0.Type == JmesPathType.Array) - { - element = arg0; - return true; - } - - var list = new List { arg0 }; - element = new ArrayValue(list); - return true; - } - - public override string ToString() - { - return "to_array"; - } - } - - internal sealed class ToNumberFunction : BaseFunction - { - internal ToNumberFunction() - : base(1) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, - out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - var arg0 = args[0]; - switch (arg0.Type) - { - case JmesPathType.Number: - element = arg0; - return true; - case JmesPathType.String: - { - var s = arg0.GetString(); - if (decimal.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out var dec)) - { - element = new DecimalValue(dec); - return true; - } - - if (double.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out var dbl)) - { - element = new DoubleValue(dbl); - return true; - } - - element = JsonConstants.Null; - return false; - } - default: - element = JsonConstants.Null; - return false; - } - } - - public override string ToString() - { - return "to_number"; - } - } - - internal sealed class ToStringFunction : BaseFunction - { - internal ToStringFunction() - : base(1) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - if (args[0].Type == JmesPathType.Expression) - { - element = JsonConstants.Null; - return false; - } - - var arg0 = args[0]; - switch (arg0.Type) - { - case JmesPathType.String: - element = arg0; - return true; - case JmesPathType.Expression: - element = JsonConstants.Null; - return false; - default: - element = new StringValue(arg0.ToString()); - return true; - } - } - - public override string ToString() - { - return "to_string"; - } - } - - internal sealed class ValuesFunction : BaseFunction - { - internal ValuesFunction() - : base(1) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - var arg0 = args[0]; - if (arg0.Type != JmesPathType.Object) - { - element = JsonConstants.Null; - return false; - } - - var list = new List(); - - foreach (var item in arg0.EnumerateObject()) - { - list.Add(item.Value); - } - - element = new ArrayValue(list); - return true; - } - - public override string ToString() - { - return "values"; - } - } - - internal sealed class TypeFunction : BaseFunction - { - internal TypeFunction() - : base(1) - { - } - - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - var arg0 = args[0]; - - switch (arg0.Type) - { - case JmesPathType.Number: - element = new StringValue("number"); - return true; - case JmesPathType.True: - case JmesPathType.False: - element = new StringValue("boolean"); - return true; - case JmesPathType.String: - element = new StringValue("string"); - return true; - case JmesPathType.Object: - element = new StringValue("object"); - return true; - case JmesPathType.Array: - element = new StringValue("array"); - return true; - case JmesPathType.Null: - element = new StringValue("null"); - return true; - default: - element = JsonConstants.Null; - return false; - } - } - - public override string ToString() - { - return "type"; - } - } - - internal sealed class JsonFunction : BaseFunction - { - /// - public JsonFunction() - : base(1) - { - } - - public override string ToString() - { - return "powertools_json"; - } - - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - element = args[0]; - return true; - } - } - - internal sealed class Base64Function : BaseFunction - { - /// - public Base64Function() - : base(1) - { - } - - public override string ToString() - { - return "powertools_base64"; - } - - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - var base64StringBytes = Convert.FromBase64String(args[0].GetString()); - var doc = JsonDocument.Parse(base64StringBytes); - element = new JsonElementValue(doc.RootElement); - return true; - } - } - - internal sealed class Base64GzipFunction : BaseFunction - { - /// - public Base64GzipFunction() - : base(1) - { - } - - public override string ToString() - { - return "powertools_base64_gzip"; - } - - public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) - { - Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - var compressedBytes = Convert.FromBase64String(args[0].GetString()); - - using var compressedStream = new MemoryStream(compressedBytes); - using var decompressedStream = new MemoryStream(); - using (var gzipStream = new GZipStream(compressedStream, CompressionMode.Decompress)) - { - gzipStream.CopyTo(decompressedStream); - } - - var doc = JsonDocument.Parse(Encoding.UTF8.GetString(decompressedStream.ToArray())); - element = new JsonElementValue(doc.RootElement); - - return true; - } - } - - internal sealed class BuiltInFunctions - { - internal static BuiltInFunctions Instance { get; } = new(); - - private readonly Dictionary _functions = new(); - - private BuiltInFunctions() - { - _functions.Add("abs", new AbsFunction()); - _functions.Add("avg", new AvgFunction()); - _functions.Add("ceil", new CeilFunction()); - _functions.Add("contains", new ContainsFunction()); - _functions.Add("ends_with", new EndsWithFunction()); - _functions.Add("floor", new FloorFunction()); - _functions.Add("join", new JoinFunction()); - _functions.Add("keys", new KeysFunction()); - _functions.Add("length", new LengthFunction()); - _functions.Add("map", new MapFunction()); - _functions.Add("max", new MaxFunction()); - _functions.Add("max_by", new MaxByFunction()); - _functions.Add("merge", new MergeFunction()); - _functions.Add("min", new MinFunction()); - _functions.Add("min_by", new MinByFunction()); - _functions.Add("not_null", new NotNullFunction()); - _functions.Add("reverse", new ReverseFunction()); - _functions.Add("sort", new SortFunction()); - _functions.Add("sort_by", new SortByFunction()); - _functions.Add("starts_with", new StartsWithFunction()); - _functions.Add("sum", new SumFunction()); - _functions.Add("to_array", new ToArrayFunction()); - _functions.Add("to_number", new ToNumberFunction()); - _functions.Add("to_string", new ToStringFunction()); - _functions.Add("type", new TypeFunction()); - _functions.Add("values", new ValuesFunction()); - _functions.Add("powertools_json", new JsonFunction()); - _functions.Add("powertools_base64", new Base64Function()); - _functions.Add("powertools_base64_gzip", new Base64GzipFunction()); - } - - internal bool TryGetFunction(string name, out IFunction func) - { - return _functions.TryGetValue(name, out func); - } - } -} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs new file mode 100644 index 00000000..e1f42165 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class AbsFunction : BaseFunction +{ + internal AbsFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg = args[0]; + + if (arg.TryGetDecimal(out var decVal)) + { + element = new DecimalValue(decVal >= 0 ? decVal : -decVal); + return true; + } + + if (arg.TryGetDouble(out var dblVal)) + { + element = new DecimalValue(dblVal >= 0 ? decVal : new decimal(-dblVal)); + return true; + } + + element = JsonConstants.Null; + return false; + } + + public override string ToString() + { + return "abs"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs new file mode 100644 index 00000000..a34be4a6 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class AvgFunction : BaseFunction +{ + internal AvgFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + if (arg0.Type != JmesPathType.Array || arg0.GetArrayLength() == 0) + { + element = JsonConstants.Null; + return false; + } + + if (!SumFunction.Instance.TryEvaluate(resources, args, out var sum)) + { + element = JsonConstants.Null; + return false; + } + + if (sum.TryGetDecimal(out var decVal)) + { + element = new DecimalValue(decVal / arg0.GetArrayLength()); + return true; + } + + if (sum.TryGetDouble(out var dblVal)) + { + element = new DoubleValue(dblVal / arg0.GetArrayLength()); + return true; + } + + element = JsonConstants.Null; + return false; + } + + public override string ToString() + { + return "avg"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64Function.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64Function.cs new file mode 100644 index 00000000..45b7b696 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64Function.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text.Json; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class Base64Function : BaseFunction +{ + /// + public Base64Function() + : base(1) + { + } + + public override string ToString() + { + return "powertools_base64"; + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + var base64StringBytes = Convert.FromBase64String(args[0].GetString()); + var doc = JsonDocument.Parse(base64StringBytes); + element = new JsonElementValue(doc.RootElement); + return true; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64GzipFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64GzipFunction.cs new file mode 100644 index 00000000..1f6a8b6d --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64GzipFunction.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Text; +using System.Text.Json; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class Base64GzipFunction : BaseFunction +{ + /// + public Base64GzipFunction() + : base(1) + { + } + + public override string ToString() + { + return "powertools_base64_gzip"; + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var compressedBytes = Convert.FromBase64String(args[0].GetString()); + + using var compressedStream = new MemoryStream(compressedBytes); + using var decompressedStream = new MemoryStream(); + using (var gzipStream = new GZipStream(compressedStream, CompressionMode.Decompress)) + { + gzipStream.CopyTo(decompressedStream); + } + + var doc = JsonDocument.Parse(Encoding.UTF8.GetString(decompressedStream.ToArray())); + element = new JsonElementValue(doc.RootElement); + + return true; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs new file mode 100644 index 00000000..33008545 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal abstract class BaseFunction : IFunction +{ + private protected BaseFunction(int? argCount) + { + Arity = argCount; + } + + public int? Arity { get; } + + public abstract bool TryEvaluate(DynamicResources resources, IList args, out IValue element); +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BuiltInFunctions.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BuiltInFunctions.cs new file mode 100644 index 00000000..b19a2be3 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BuiltInFunctions.cs @@ -0,0 +1,48 @@ +using System.Collections.Generic; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class BuiltInFunctions +{ + internal static BuiltInFunctions Instance { get; } = new(); + + private readonly Dictionary _functions = new(); + + private BuiltInFunctions() + { + _functions.Add("abs", new AbsFunction()); + _functions.Add("avg", new AvgFunction()); + _functions.Add("ceil", new CeilFunction()); + _functions.Add("contains", new ContainsFunction()); + _functions.Add("ends_with", new EndsWithFunction()); + _functions.Add("floor", new FloorFunction()); + _functions.Add("join", new JoinFunction()); + _functions.Add("keys", new KeysFunction()); + _functions.Add("length", new LengthFunction()); + _functions.Add("map", new MapFunction()); + _functions.Add("max", new MaxFunction()); + _functions.Add("max_by", new MaxByFunction()); + _functions.Add("merge", new MergeFunction()); + _functions.Add("min", new MinFunction()); + _functions.Add("min_by", new MinByFunction()); + _functions.Add("not_null", new NotNullFunction()); + _functions.Add("reverse", new ReverseFunction()); + _functions.Add("sort", new SortFunction()); + _functions.Add("sort_by", new SortByFunction()); + _functions.Add("starts_with", new StartsWithFunction()); + _functions.Add("sum", new SumFunction()); + _functions.Add("to_array", new ToArrayFunction()); + _functions.Add("to_number", new ToNumberFunction()); + _functions.Add("to_string", new ToStringFunction()); + _functions.Add("type", new TypeFunction()); + _functions.Add("values", new ValuesFunction()); + _functions.Add("powertools_json", new JsonFunction()); + _functions.Add("powertools_base64", new Base64Function()); + _functions.Add("powertools_base64_gzip", new Base64GzipFunction()); + } + + internal bool TryGetFunction(string name, out IFunction func) + { + return _functions.TryGetValue(name, out func); + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs new file mode 100644 index 00000000..164e80d4 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class CeilFunction : BaseFunction +{ + internal CeilFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, + out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var val = args[0]; + if (val.Type != JmesPathType.Number) + { + element = JsonConstants.Null; + return false; + } + + if (val.TryGetDecimal(out var decVal)) + { + element = new DecimalValue(decimal.Ceiling(decVal)); + return true; + } + + if (val.TryGetDouble(out var dblVal)) + { + element = new DoubleValue(Math.Ceiling(dblVal)); + return true; + } + + element = JsonConstants.Null; + return false; + } + + public override string ToString() + { + return "ceil"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs new file mode 100644 index 00000000..2eb9080d --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs @@ -0,0 +1,66 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class ContainsFunction : BaseFunction +{ + internal ContainsFunction() + : base(2) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, + out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + var arg1 = args[1]; + + var comparer = ValueEqualityComparer.Instance; + + switch (arg0.Type) + { + case JmesPathType.Array: + if (arg0.EnumerateArray().Any(item => comparer.Equals(item, arg1))) + { + element = JsonConstants.True; + return true; + } + + element = JsonConstants.False; + return true; + case JmesPathType.String: + { + if (arg1.Type != JmesPathType.String) + { + element = JsonConstants.Null; + return false; + } + + var s0 = arg0.GetString(); + var s1 = arg1.GetString(); + if (s0.Contains(s1)) + { + element = JsonConstants.True; + return true; + } + + element = JsonConstants.False; + return true; + } + default: + { + element = JsonConstants.Null; + return false; + } + } + } + + public override string ToString() + { + return "contains"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs new file mode 100644 index 00000000..9d3cc4b2 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class EndsWithFunction : BaseFunction +{ + internal EndsWithFunction() + : base(2) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, + out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + var arg1 = args[1]; + if (arg0.Type != JmesPathType.String + || arg1.Type != JmesPathType.String) + { + element = JsonConstants.Null; + return false; + } + + var s0 = arg0.GetString(); + var s1 = arg1.GetString(); + + element = s0.EndsWith(s1) ? JsonConstants.True : JsonConstants.False; + + return true; + } + + public override string ToString() + { + return "ends_with"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs new file mode 100644 index 00000000..c65e72a2 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class FloorFunction : BaseFunction +{ + internal FloorFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, + out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var val = args[0]; + if (val.Type != JmesPathType.Number) + { + element = JsonConstants.Null; + return false; + } + + if (val.TryGetDecimal(out var decVal)) + { + element = new DecimalValue(decimal.Floor(decVal)); + return true; + } + + if (val.TryGetDouble(out var dblVal)) + { + element = new DoubleValue(Math.Floor(dblVal)); + return true; + } + + element = JsonConstants.Null; + return false; + } + + public override string ToString() + { + return "floor"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs new file mode 100644 index 00000000..ddbd8634 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal interface IFunction +{ + int? Arity { get; } + bool TryEvaluate(DynamicResources resources, IList args, out IValue element); +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs new file mode 100644 index 00000000..827e2316 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class JoinFunction : BaseFunction +{ + internal JoinFunction() + : base(2) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + var arg1 = args[1]; + + if (!(arg0.Type == JmesPathType.String && args[1].Type == JmesPathType.Array)) + { + element = JsonConstants.Null; + return false; + } + + var sep = arg0.GetString(); + var buf = new StringBuilder(); + foreach (var j in arg1.EnumerateArray()) + { + if (j.Type != JmesPathType.String) + { + element = JsonConstants.Null; + return false; + } + + if (buf.Length != 0) + { + buf.Append(sep); + } + + var sv = j.GetString(); + buf.Append(sv); + } + + element = new StringValue(buf.ToString()); + return true; + } + + public override string ToString() + { + return "join"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JsonFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JsonFunction.cs new file mode 100644 index 00000000..560947c1 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JsonFunction.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class JsonFunction : BaseFunction +{ + /// + public JsonFunction() + : base(1) + { + } + + public override string ToString() + { + return "powertools_json"; + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + element = args[0]; + return true; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs new file mode 100644 index 00000000..915013df --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class KeysFunction : BaseFunction +{ + internal KeysFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, + out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + if (arg0.Type != JmesPathType.Object) + { + element = JsonConstants.Null; + return false; + } + + var values = new List(); + + foreach (var property in arg0.EnumerateObject()) + { + values.Add(new StringValue(property.Name)); + } + + element = new ArrayValue(values); + return true; + } + + public override string ToString() + { + return "keys"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs new file mode 100644 index 00000000..078621f2 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class LengthFunction : BaseFunction +{ + internal LengthFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, + out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + + switch (arg0.Type) + { + case JmesPathType.Object: + { + var count = 0; + foreach (var unused in arg0.EnumerateObject()) + { + ++count; + } + + element = new DecimalValue(new decimal(count)); + return true; + } + case JmesPathType.Array: + element = new DecimalValue(new decimal(arg0.GetArrayLength())); + return true; + case JmesPathType.String: + { + var bytes = Encoding.UTF32.GetBytes(arg0.GetString().ToCharArray()); + element = new DecimalValue(new decimal(bytes.Length / 4)); + return true; + } + default: + { + element = JsonConstants.Null; + return false; + } + } + } + + public override string ToString() + { + return "length"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs new file mode 100644 index 00000000..10ca0c96 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class MapFunction : BaseFunction +{ + internal MapFunction() + : base(2) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + if (!(args[0].Type == JmesPathType.Expression && args[1].Type == JmesPathType.Array)) + { + element = JsonConstants.Null; + return false; + } + + var expr = args[0].GetExpression(); + var arg0 = args[1]; + + var list = new List(); + + foreach (var item in arg0.EnumerateArray()) + { + if (!expr.TryEvaluate(resources, item, out var val)) + { + element = JsonConstants.Null; + return false; + } + + list.Add(val); + } + + element = new ArrayValue(list); + return true; + } + + public override string ToString() + { + return "map"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs new file mode 100644 index 00000000..cdca7214 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class MaxByFunction : BaseFunction +{ + internal MaxByFunction() + : base(2) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + if (!(args[0].Type == JmesPathType.Array && args[1].Type == JmesPathType.Expression)) + { + element = JsonConstants.Null; + return false; + } + + var arg0 = args[0]; + if (arg0.GetArrayLength() == 0) + { + element = JsonConstants.Null; + return true; + } + + var expr = args[1].GetExpression(); + + if (!expr.TryEvaluate(resources, arg0[0], out var key1)) + { + element = JsonConstants.Null; + return false; + } + + var isNumber1 = key1.Type == JmesPathType.Number; + var isString1 = key1.Type == JmesPathType.String; + if (!(isNumber1 || isString1)) + { + element = JsonConstants.Null; + return false; + } + + var greater = GtOperator.Instance; + var index = 0; + for (var i = 1; i < arg0.GetArrayLength(); ++i) + { + if (!expr.TryEvaluate(resources, arg0[i], out var key2)) + { + element = JsonConstants.Null; + return false; + } + + var isNumber2 = key2.Type == JmesPathType.Number; + var isString2 = key2.Type == JmesPathType.String; + if (!(isNumber2 == isNumber1 && isString2 == isString1)) + { + element = JsonConstants.Null; + return false; + } + + if (!greater.TryEvaluate(key2, key1, out var value)) + { + element = JsonConstants.Null; + return false; + } + + if (value.Type != JmesPathType.True) continue; + key1 = key2; + index = i; + } + + element = arg0[index]; + return true; + } + + public override string ToString() + { + return "max_by"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs new file mode 100644 index 00000000..551ccf2c --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class MaxFunction : BaseFunction +{ + internal MaxFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, + out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + if (arg0.Type != JmesPathType.Array) + { + element = JsonConstants.Null; + return false; + } + + if (arg0.GetArrayLength() == 0) + { + element = JsonConstants.Null; + return false; + } + + var isNumber = arg0[0].Type == JmesPathType.Number; + var isString = arg0[0].Type == JmesPathType.String; + if (!isNumber && !isString) + { + element = JsonConstants.Null; + return false; + } + + var greater = GtOperator.Instance; + var index = 0; + for (var i = 1; i < arg0.GetArrayLength(); ++i) + { + if (!(arg0[i].Type == JmesPathType.Number == isNumber && + arg0[i].Type == JmesPathType.String == isString)) + { + element = JsonConstants.Null; + return false; + } + + if (!greater.TryEvaluate(arg0[i], arg0[index], out var value)) + { + element = JsonConstants.Null; + return false; + } + + if (Expression.IsTrue(value)) + { + index = i; + } + } + + element = arg0[index]; + return true; + } + + public override string ToString() + { + return "max"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs new file mode 100644 index 00000000..60b488ec --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using System.Linq; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class MergeFunction : BaseFunction +{ + internal MergeFunction() + : base(null) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) + { + if (!args.Any()) + { + element = JsonConstants.Null; + return false; + } + + var arg0 = args[0]; + if (arg0.Type != JmesPathType.Object) + { + element = JsonConstants.Null; + return false; + } + + if (args.Count == 1) + { + element = arg0; + return true; + } + + var dict = new Dictionary(); + foreach (var argi in args) + { + if (argi.Type != JmesPathType.Object) + { + element = JsonConstants.Null; + return false; + } + + foreach (var item in argi.EnumerateObject()) + { + if (dict.TryAdd(item.Name, item.Value)) continue; + dict.Remove(item.Name); + dict.Add(item.Name, item.Value); + } + } + + element = new ObjectValue(dict); + return true; + } + + public override string ToString() + { + return "merge"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs new file mode 100644 index 00000000..9196c03b --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class MinByFunction : BaseFunction +{ + internal MinByFunction() + : base(2) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + if (!(args[0].Type == JmesPathType.Array && args[1].Type == JmesPathType.Expression)) + { + element = JsonConstants.Null; + return false; + } + + var arg0 = args[0]; + if (arg0.GetArrayLength() == 0) + { + element = JsonConstants.Null; + return true; + } + + var expr = args[1].GetExpression(); + + if (!expr.TryEvaluate(resources, arg0[0], out var key1)) + { + element = JsonConstants.Null; + return false; + } + + var isNumber1 = key1.Type == JmesPathType.Number; + var isString1 = key1.Type == JmesPathType.String; + if (!(isNumber1 || isString1)) + { + element = JsonConstants.Null; + return false; + } + + var lessor = LtOperator.Instance; + var index = 0; + for (var i = 1; i < arg0.GetArrayLength(); ++i) + { + if (!expr.TryEvaluate(resources, arg0[i], out var key2)) + { + element = JsonConstants.Null; + return false; + } + + var isNumber2 = key2.Type == JmesPathType.Number; + var isString2 = key2.Type == JmesPathType.String; + if (!(isNumber2 == isNumber1 && isString2 == isString1)) + { + element = JsonConstants.Null; + return false; + } + + if (!lessor.TryEvaluate(key2, key1, out var value)) + { + element = JsonConstants.Null; + return false; + } + + if (value.Type != JmesPathType.True) continue; + key1 = key2; + index = i; + } + + element = arg0[index]; + return true; + } + + public override string ToString() + { + return "min_by"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs new file mode 100644 index 00000000..6a8612dc --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class MinFunction : BaseFunction +{ + internal MinFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, + out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + if (arg0.Type != JmesPathType.Array) + { + element = JsonConstants.Null; + return false; + } + + if (arg0.GetArrayLength() == 0) + { + element = JsonConstants.Null; + return false; + } + + var isNumber = arg0[0].Type == JmesPathType.Number; + var isString = arg0[0].Type == JmesPathType.String; + if (!isNumber && !isString) + { + element = JsonConstants.Null; + return false; + } + + var less = LtOperator.Instance; + var index = 0; + for (var i = 1; i < arg0.GetArrayLength(); ++i) + { + if (!(arg0[i].Type == JmesPathType.Number == isNumber && + arg0[i].Type == JmesPathType.String == isString)) + { + element = JsonConstants.Null; + return false; + } + + if (!less.TryEvaluate(arg0[i], arg0[index], out var value)) + { + element = JsonConstants.Null; + return false; + } + + if (value.Type == JmesPathType.True) + { + index = i; + } + } + + element = arg0[index]; + return true; + } + + public override string ToString() + { + return "min"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs new file mode 100644 index 00000000..fc8afcfb --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class NotNullFunction : BaseFunction +{ + internal NotNullFunction() + : base(null) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) + { + foreach (var arg in args) + { + if (arg.Type == JmesPathType.Null) continue; + element = arg; + return true; + } + + element = JsonConstants.Null; + return true; + } + + public override string ToString() + { + return "not_null"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs new file mode 100644 index 00000000..0c6837d8 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class ReverseFunction : BaseFunction +{ + internal ReverseFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + switch (arg0.Type) + { + case JmesPathType.String: + { + element = new StringValue(string.Join("", GraphemeClusters(arg0.GetString()).Reverse().ToArray())); + return true; + } + case JmesPathType.Array: + { + var list = new List(); + for (var i = arg0.GetArrayLength() - 1; i >= 0; --i) + { + list.Add(arg0[i]); + } + + element = new ArrayValue(list); + return true; + } + default: + element = JsonConstants.Null; + return false; + } + } + + private static IEnumerable GraphemeClusters(string s) + { + var enumerator = StringInfo.GetTextElementEnumerator(s); + while (enumerator.MoveNext()) + { + yield return (string)enumerator.Current; + } + } + + public override string ToString() + { + return "reverse"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByComparer.cs new file mode 100644 index 00000000..70c5167e --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByComparer.cs @@ -0,0 +1,75 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Collections.Generic; + +namespace AWS.Lambda.Powertools.JMESPath.Functions +{ + internal sealed class SortByComparer : IComparer, System.Collections.IComparer + { + private readonly DynamicResources _resources; + private readonly IExpression _expr; + + internal bool IsValid { get; private set; } = true; + + internal SortByComparer(DynamicResources resources, + IExpression expr) + { + _resources = resources; + _expr = expr; + } + + public int Compare(IValue lhs, IValue rhs) + { + var comparer = ValueComparer.Instance; + + if (!IsValid) + { + return 0; + } + + if (!_expr.TryEvaluate(_resources, lhs, out var key1)) + { + IsValid = false; + return 0; + } + + var isNumber1 = key1.Type == JmesPathType.Number; + var isString1 = key1.Type == JmesPathType.String; + if (!(isNumber1 || isString1)) + { + IsValid = false; + return 0; + } + + if (!_expr.TryEvaluate(_resources, rhs, out var key2)) + { + IsValid = false; + return 0; + } + + var isNumber2 = key2.Type == JmesPathType.Number; + var isString2 = key2.Type == JmesPathType.String; + if (isNumber2 == isNumber1 && isString2 == isString1) return comparer.Compare(key1, key2); + IsValid = false; + return 0; + } + + int System.Collections.IComparer.Compare(object x, object y) + { + return Compare((IValue)x, (IValue)y); + } + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs new file mode 100644 index 00000000..7af9b922 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class SortByFunction : BaseFunction +{ + internal SortByFunction() + : base(2) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + if (!(args[0].Type == JmesPathType.Array && args[1].Type == JmesPathType.Expression)) + { + element = JsonConstants.Null; + return false; + } + + var arg0 = args[0]; + if (arg0.GetArrayLength() <= 1) + { + element = arg0; + return true; + } + + var expr = args[1].GetExpression(); + + var list = new List(); + foreach (var item in arg0.EnumerateArray()) + { + list.Add(item); + } + + var comparer = new SortByComparer(resources, expr); + list.Sort(comparer); + if (comparer.IsValid) + { + element = new ArrayValue(list); + return true; + } + + element = JsonConstants.Null; + return false; + } + + public override string ToString() + { + return "sort_by"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs new file mode 100644 index 00000000..b697a0a7 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class SortFunction : BaseFunction +{ + internal SortFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + if (arg0.Type != JmesPathType.Array) + { + element = JsonConstants.Null; + return false; + } + + if (arg0.GetArrayLength() <= 1) + { + element = arg0; + return true; + } + + var isNumber1 = arg0[0].Type == JmesPathType.Number; + var isString1 = arg0[0].Type == JmesPathType.String; + if (!isNumber1 && !isString1) + { + element = JsonConstants.Null; + return false; + } + + var comparer = ValueComparer.Instance; + + var list = new List(); + foreach (var item in arg0.EnumerateArray()) + { + var isNumber2 = item.Type == JmesPathType.Number; + var isString2 = item.Type == JmesPathType.String; + if (!(isNumber2 == isNumber1 && isString2 == isString1)) + { + element = JsonConstants.Null; + return false; + } + + list.Add(item); + } + + list.Sort(comparer); + element = new ArrayValue(list); + return true; + } + + public override string ToString() + { + return "sort"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs new file mode 100644 index 00000000..02b7d86d --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class StartsWithFunction : BaseFunction +{ + internal StartsWithFunction() + : base(2) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, + out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + var arg1 = args[1]; + if (arg0.Type != JmesPathType.String + || arg1.Type != JmesPathType.String) + { + element = JsonConstants.Null; + return false; + } + + var s0 = arg0.GetString(); + var s1 = arg1.GetString(); + element = s0.StartsWith(s1) ? JsonConstants.True : JsonConstants.False; + + return true; + } + + public override string ToString() + { + return "starts_with"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs new file mode 100644 index 00000000..8c3956f4 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs @@ -0,0 +1,73 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class SumFunction : BaseFunction +{ + internal static SumFunction Instance { get; } = new(); + + internal SumFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, + out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + if (arg0.Type != JmesPathType.Array) + { + element = JsonConstants.Null; + return false; + } + + foreach (var item in arg0.EnumerateArray()) + { + if (item.Type == JmesPathType.Number) continue; + element = JsonConstants.Null; + return false; + } + + var success = true; + decimal decSum = 0; + foreach (var item in arg0.EnumerateArray()) + { + if (!item.TryGetDecimal(out var dec)) + { + success = false; + break; + } + + decSum += dec; + } + + if (success) + { + element = new DecimalValue(decSum); + return true; + } + + double dblSum = 0; + foreach (var item in arg0.EnumerateArray()) + { + if (!item.TryGetDouble(out var dbl)) + { + element = JsonConstants.Null; + return false; + } + + dblSum += dbl; + } + + element = new DoubleValue(dblSum); + return true; + } + + public override string ToString() + { + return "sum"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs new file mode 100644 index 00000000..4cad1f76 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class ToArrayFunction : BaseFunction +{ + internal ToArrayFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + if (arg0.Type == JmesPathType.Array) + { + element = arg0; + return true; + } + + var list = new List { arg0 }; + element = new ArrayValue(list); + return true; + } + + public override string ToString() + { + return "to_array"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs new file mode 100644 index 00000000..49194fb5 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class ToNumberFunction : BaseFunction +{ + internal ToNumberFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, + out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + switch (arg0.Type) + { + case JmesPathType.Number: + element = arg0; + return true; + case JmesPathType.String: + { + var s = arg0.GetString(); + if (decimal.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out var dec)) + { + element = new DecimalValue(dec); + return true; + } + + if (double.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out var dbl)) + { + element = new DoubleValue(dbl); + return true; + } + + element = JsonConstants.Null; + return false; + } + default: + element = JsonConstants.Null; + return false; + } + } + + public override string ToString() + { + return "to_number"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs new file mode 100644 index 00000000..caaff747 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class ToStringFunction : BaseFunction +{ + internal ToStringFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + if (args[0].Type == JmesPathType.Expression) + { + element = JsonConstants.Null; + return false; + } + + var arg0 = args[0]; + switch (arg0.Type) + { + case JmesPathType.String: + element = arg0; + return true; + case JmesPathType.Expression: + element = JsonConstants.Null; + return false; + default: + element = new StringValue(arg0.ToString()); + return true; + } + } + + public override string ToString() + { + return "to_string"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs new file mode 100644 index 00000000..c8d61538 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class TypeFunction : BaseFunction +{ + internal TypeFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + + switch (arg0.Type) + { + case JmesPathType.Number: + element = new StringValue("number"); + return true; + case JmesPathType.True: + case JmesPathType.False: + element = new StringValue("boolean"); + return true; + case JmesPathType.String: + element = new StringValue("string"); + return true; + case JmesPathType.Object: + element = new StringValue("object"); + return true; + case JmesPathType.Array: + element = new StringValue("array"); + return true; + case JmesPathType.Null: + element = new StringValue("null"); + return true; + default: + element = JsonConstants.Null; + return false; + } + } + + public override string ToString() + { + return "type"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs new file mode 100644 index 00000000..e46c05b9 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal sealed class ValuesFunction : BaseFunction +{ + internal ValuesFunction() + : base(1) + { + } + + public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) + { + Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + + var arg0 = args[0]; + if (arg0.Type != JmesPathType.Object) + { + element = JsonConstants.Null; + return false; + } + + var list = new List(); + + foreach (var item in arg0.EnumerateObject()) + { + list.Add(item.Value); + } + + element = new ArrayValue(list); + return true; + } + + public override string ToString() + { + return "values"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs index 4b7a37f7..a7641fe3 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs @@ -18,6 +18,7 @@ using System.Diagnostics; using System.Text; using System.Text.Json; +using AWS.Lambda.Powertools.JMESPath.Functions; namespace AWS.Lambda.Powertools.JMESPath { diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs index 553f9140..7ac5433b 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs @@ -15,6 +15,7 @@ using System; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Functions; namespace AWS.Lambda.Powertools.JMESPath { From 942aaa97f6139ec04a0be15a3781d2eaf4bdf3ce Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Fri, 19 Apr 2024 16:11:52 +0100 Subject: [PATCH 21/28] refactor. include license text, remove duplication to make sonar happy --- .../Functions/AbsFunction.cs | 15 ++++ .../Functions/AvgFunction.cs | 15 ++++ .../Functions/Base64Function.cs | 15 ++++ .../Functions/Base64GzipFunction.cs | 15 ++++ .../Functions/BaseFunction.cs | 15 ++++ .../Functions/BuiltInFunctions.cs | 15 ++++ .../Functions/CeilFunction.cs | 15 ++++ .../Functions/ContainsFunction.cs | 15 ++++ .../Functions/EndsWithFunction.cs | 31 +++---- .../Functions/EvaluateMinMax.cs | 70 ++++++++++++++++ .../Functions/EvaluateMinMaxBy.cs | 84 +++++++++++++++++++ .../Functions/EvaluateStartEndWith.cs | 40 +++++++++ .../Functions/FloorFunction.cs | 15 ++++ .../Functions/IFunction.cs | 15 ++++ .../Functions/JoinFunction.cs | 15 ++++ .../Functions/JsonFunction.cs | 15 ++++ .../Functions/KeysFunction.cs | 15 ++++ .../Functions/LengthFunction.cs | 15 ++++ .../Functions/MapFunction.cs | 15 ++++ .../Functions/MaxByFunction.cs | 78 ++++------------- .../Functions/MaxFunction.cs | 62 ++++---------- .../Functions/MergeFunction.cs | 15 ++++ .../Functions/MinByFunction.cs | 76 ++++------------- .../Functions/MinFunction.cs | 62 ++++---------- .../Functions/NotNullFunction.cs | 15 ++++ .../Functions/ReverseFunction.cs | 15 ++++ .../Functions/SortByFunction.cs | 15 ++++ .../Functions/SortFunction.cs | 15 ++++ .../Functions/StartsWithFunction.cs | 30 +++---- .../Functions/SumFunction.cs | 15 ++++ .../Functions/ToArrayFunction.cs | 15 ++++ .../Functions/ToNumberFunction.cs | 15 ++++ .../Functions/ToStringFunction.cs | 15 ++++ .../Functions/TypeFunction.cs | 15 ++++ .../Functions/ValuesFunction.cs | 15 ++++ 35 files changed, 681 insertions(+), 242 deletions(-) create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMax.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMaxBy.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateStartEndWith.cs diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs index e1f42165..e1733344 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs index a34be4a6..9dc83294 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64Function.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64Function.cs index 45b7b696..6db33b66 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64Function.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64Function.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System; using System.Collections.Generic; using System.Diagnostics; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64GzipFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64GzipFunction.cs index 1f6a8b6d..a68cd5c8 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64GzipFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64GzipFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System; using System.Collections.Generic; using System.Diagnostics; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs index 33008545..b4162cea 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BuiltInFunctions.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BuiltInFunctions.cs index b19a2be3..b953e136 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BuiltInFunctions.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BuiltInFunctions.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs index 164e80d4..2f0e773c 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System; using System.Collections.Generic; using System.Diagnostics; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs index 2eb9080d..2953eb98 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; using System.Linq; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs index 9d3cc4b2..c0af8786 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; @@ -15,21 +30,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - var arg0 = args[0]; - var arg1 = args[1]; - if (arg0.Type != JmesPathType.String - || arg1.Type != JmesPathType.String) - { - element = JsonConstants.Null; - return false; - } - - var s0 = arg0.GetString(); - var s1 = arg1.GetString(); - - element = s0.EndsWith(s1) ? JsonConstants.True : JsonConstants.False; - - return true; + return EvaluateStartEndWith.TryEvaluate(args, out element, s0 => s0.EndsWith); } public override string ToString() diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMax.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMax.cs new file mode 100644 index 00000000..fd8de403 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMax.cs @@ -0,0 +1,70 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Collections.Generic; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal static class EvaluateMinMax +{ + internal static bool TryEvaluate(IList args, IBinaryOperator binaryOperator, out IValue element) + { + var arg0 = args[0]; + if (arg0.Type != JmesPathType.Array) + { + element = JsonConstants.Null; + return false; + } + + if (arg0.GetArrayLength() == 0) + { + element = JsonConstants.Null; + return false; + } + + var isNumber = arg0[0].Type == JmesPathType.Number; + var isString = arg0[0].Type == JmesPathType.String; + if (!isNumber && !isString) + { + element = JsonConstants.Null; + return false; + } + + var index = 0; + for (var i = 1; i < arg0.GetArrayLength(); ++i) + { + if (!(arg0[i].Type == JmesPathType.Number == isNumber && + arg0[i].Type == JmesPathType.String == isString)) + { + element = JsonConstants.Null; + return false; + } + + if (!binaryOperator.TryEvaluate(arg0[i], arg0[index], out var value)) + { + element = JsonConstants.Null; + return false; + } + + if (Expression.IsTrue(value)) + { + index = i; + } + } + + element = arg0[index]; + return true; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMaxBy.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMaxBy.cs new file mode 100644 index 00000000..a177a5ac --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMaxBy.cs @@ -0,0 +1,84 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Collections.Generic; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal static class EvaluateMinMaxBy +{ + internal static bool TryEvaluate(DynamicResources resources, IList args, IBinaryOperator binaryOperator, out IValue element) + { + if (!(args[0].Type == JmesPathType.Array && args[1].Type == JmesPathType.Expression)) + { + element = JsonConstants.Null; + return false; + } + + var arg0 = args[0]; + if (arg0.GetArrayLength() == 0) + { + element = JsonConstants.Null; + return true; + } + + var expr = args[1].GetExpression(); + + if (!expr.TryEvaluate(resources, arg0[0], out var key1)) + { + element = JsonConstants.Null; + return false; + } + + var isNumber1 = key1.Type == JmesPathType.Number; + var isString1 = key1.Type == JmesPathType.String; + if (!(isNumber1 || isString1)) + { + element = JsonConstants.Null; + return false; + } + + var index = 0; + for (var i = 1; i < arg0.GetArrayLength(); ++i) + { + if (!expr.TryEvaluate(resources, arg0[i], out var key2)) + { + element = JsonConstants.Null; + return false; + } + + var isNumber2 = key2.Type == JmesPathType.Number; + var isString2 = key2.Type == JmesPathType.String; + if (!(isNumber2 == isNumber1 && isString2 == isString1)) + { + element = JsonConstants.Null; + return false; + } + + if (!binaryOperator.TryEvaluate(key2, key1, out var value)) + { + element = JsonConstants.Null; + return false; + } + + if (value.Type != JmesPathType.True) continue; + key1 = key2; + index = i; + } + + element = arg0[index]; + return true; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateStartEndWith.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateStartEndWith.cs new file mode 100644 index 00000000..11ae9ee2 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateStartEndWith.cs @@ -0,0 +1,40 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; +using System.Collections.Generic; + +namespace AWS.Lambda.Powertools.JMESPath.Functions; + +internal static class EvaluateStartEndWith +{ + internal static bool TryEvaluate(IList args, out IValue element, Func> method) + { + var arg0 = args[0]; + var arg1 = args[1]; + if (arg0.Type != JmesPathType.String + || arg1.Type != JmesPathType.String) + { + element = JsonConstants.Null; + return false; + } + + var s0 = arg0.GetString(); + var s1 = arg1.GetString(); + element = method(s0)(s1) ? JsonConstants.True : JsonConstants.False; + + return true; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs index c65e72a2..4eef9848 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System; using System.Collections.Generic; using System.Diagnostics; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs index ddbd8634..476838e8 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs index 827e2316..b32b6001 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; using System.Text; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JsonFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JsonFunction.cs index 560947c1..aba2b558 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JsonFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JsonFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs index 915013df..257812cd 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs index 078621f2..e1779c3b 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; using System.Text; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs index 10ca0c96..100c4790 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs index cdca7214..0cfd4464 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; @@ -13,67 +28,8 @@ internal MaxByFunction() public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - - if (!(args[0].Type == JmesPathType.Array && args[1].Type == JmesPathType.Expression)) - { - element = JsonConstants.Null; - return false; - } - - var arg0 = args[0]; - if (arg0.GetArrayLength() == 0) - { - element = JsonConstants.Null; - return true; - } - - var expr = args[1].GetExpression(); - - if (!expr.TryEvaluate(resources, arg0[0], out var key1)) - { - element = JsonConstants.Null; - return false; - } - - var isNumber1 = key1.Type == JmesPathType.Number; - var isString1 = key1.Type == JmesPathType.String; - if (!(isNumber1 || isString1)) - { - element = JsonConstants.Null; - return false; - } - - var greater = GtOperator.Instance; - var index = 0; - for (var i = 1; i < arg0.GetArrayLength(); ++i) - { - if (!expr.TryEvaluate(resources, arg0[i], out var key2)) - { - element = JsonConstants.Null; - return false; - } - - var isNumber2 = key2.Type == JmesPathType.Number; - var isString2 = key2.Type == JmesPathType.String; - if (!(isNumber2 == isNumber1 && isString2 == isString1)) - { - element = JsonConstants.Null; - return false; - } - - if (!greater.TryEvaluate(key2, key1, out var value)) - { - element = JsonConstants.Null; - return false; - } - - if (value.Type != JmesPathType.True) continue; - key1 = key2; - index = i; - } - - element = arg0[index]; - return true; + + return EvaluateMinMaxBy.TryEvaluate(resources, args, GtOperator.Instance, out element); } public override string ToString() diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs index 551ccf2c..ffa99756 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; @@ -15,52 +30,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - var arg0 = args[0]; - if (arg0.Type != JmesPathType.Array) - { - element = JsonConstants.Null; - return false; - } - - if (arg0.GetArrayLength() == 0) - { - element = JsonConstants.Null; - return false; - } - - var isNumber = arg0[0].Type == JmesPathType.Number; - var isString = arg0[0].Type == JmesPathType.String; - if (!isNumber && !isString) - { - element = JsonConstants.Null; - return false; - } - - var greater = GtOperator.Instance; - var index = 0; - for (var i = 1; i < arg0.GetArrayLength(); ++i) - { - if (!(arg0[i].Type == JmesPathType.Number == isNumber && - arg0[i].Type == JmesPathType.String == isString)) - { - element = JsonConstants.Null; - return false; - } - - if (!greater.TryEvaluate(arg0[i], arg0[index], out var value)) - { - element = JsonConstants.Null; - return false; - } - - if (Expression.IsTrue(value)) - { - index = i; - } - } - - element = arg0[index]; - return true; + return EvaluateMinMax.TryEvaluate(args, GtOperator.Instance, out element); } public override string ToString() diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs index 60b488ec..e0dde1ac 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Linq; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs index 9196c03b..82f7c38f 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; @@ -14,66 +29,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - if (!(args[0].Type == JmesPathType.Array && args[1].Type == JmesPathType.Expression)) - { - element = JsonConstants.Null; - return false; - } - - var arg0 = args[0]; - if (arg0.GetArrayLength() == 0) - { - element = JsonConstants.Null; - return true; - } - - var expr = args[1].GetExpression(); - - if (!expr.TryEvaluate(resources, arg0[0], out var key1)) - { - element = JsonConstants.Null; - return false; - } - - var isNumber1 = key1.Type == JmesPathType.Number; - var isString1 = key1.Type == JmesPathType.String; - if (!(isNumber1 || isString1)) - { - element = JsonConstants.Null; - return false; - } - - var lessor = LtOperator.Instance; - var index = 0; - for (var i = 1; i < arg0.GetArrayLength(); ++i) - { - if (!expr.TryEvaluate(resources, arg0[i], out var key2)) - { - element = JsonConstants.Null; - return false; - } - - var isNumber2 = key2.Type == JmesPathType.Number; - var isString2 = key2.Type == JmesPathType.String; - if (!(isNumber2 == isNumber1 && isString2 == isString1)) - { - element = JsonConstants.Null; - return false; - } - - if (!lessor.TryEvaluate(key2, key1, out var value)) - { - element = JsonConstants.Null; - return false; - } - - if (value.Type != JmesPathType.True) continue; - key1 = key2; - index = i; - } - - element = arg0[index]; - return true; + return EvaluateMinMaxBy.TryEvaluate(resources, args, LtOperator.Instance, out element); } public override string ToString() diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs index 6a8612dc..2fd25e97 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; @@ -15,52 +30,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - var arg0 = args[0]; - if (arg0.Type != JmesPathType.Array) - { - element = JsonConstants.Null; - return false; - } - - if (arg0.GetArrayLength() == 0) - { - element = JsonConstants.Null; - return false; - } - - var isNumber = arg0[0].Type == JmesPathType.Number; - var isString = arg0[0].Type == JmesPathType.String; - if (!isNumber && !isString) - { - element = JsonConstants.Null; - return false; - } - - var less = LtOperator.Instance; - var index = 0; - for (var i = 1; i < arg0.GetArrayLength(); ++i) - { - if (!(arg0[i].Type == JmesPathType.Number == isNumber && - arg0[i].Type == JmesPathType.String == isString)) - { - element = JsonConstants.Null; - return false; - } - - if (!less.TryEvaluate(arg0[i], arg0[index], out var value)) - { - element = JsonConstants.Null; - return false; - } - - if (value.Type == JmesPathType.True) - { - index = i; - } - } - - element = arg0[index]; - return true; + return EvaluateMinMax.TryEvaluate(args, LtOperator.Instance, out element); } public override string ToString() diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs index fc8afcfb..d2eee194 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs index 0c6837d8..cba14dc6 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; using System.Globalization; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs index 7af9b922..4b54a400 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs index b697a0a7..009bf66d 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs index 02b7d86d..a59261a3 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; @@ -15,20 +30,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); - var arg0 = args[0]; - var arg1 = args[1]; - if (arg0.Type != JmesPathType.String - || arg1.Type != JmesPathType.String) - { - element = JsonConstants.Null; - return false; - } - - var s0 = arg0.GetString(); - var s1 = arg1.GetString(); - element = s0.StartsWith(s1) ? JsonConstants.True : JsonConstants.False; - - return true; + return EvaluateStartEndWith.TryEvaluate(args, out element, s0 => s0.StartsWith); } public override string ToString() diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs index 8c3956f4..554e91db 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs index 4cad1f76..9c79e35f 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs index 49194fb5..d00f8eb0 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; using System.Globalization; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs index caaff747..bea47423 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs index c8d61538..aac891ba 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs index e46c05b9..1942c9aa 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Collections.Generic; using System.Diagnostics; From ea6b795b7fb8f6e8b3646c9787ecda230d220900 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Fri, 19 Apr 2024 16:23:05 +0100 Subject: [PATCH 22/28] refactor values --- .../BinaryOperator.cs | 2 + .../Expression.cs | 1 + .../Functions/AbsFunction.cs | 1 + .../Functions/AvgFunction.cs | 1 + .../Functions/Base64Function.cs | 1 + .../Functions/Base64GzipFunction.cs | 1 + .../Functions/BaseFunction.cs | 1 + .../Functions/CeilFunction.cs | 1 + .../Functions/ContainsFunction.cs | 1 + .../Functions/EndsWithFunction.cs | 1 + .../Functions/EvaluateMinMax.cs | 1 + .../Functions/EvaluateMinMaxBy.cs | 1 + .../Functions/EvaluateStartEndWith.cs | 1 + .../Functions/FloorFunction.cs | 1 + .../Functions/IFunction.cs | 1 + .../Functions/JoinFunction.cs | 1 + .../Functions/JsonFunction.cs | 1 + .../Functions/KeysFunction.cs | 1 + .../Functions/LengthFunction.cs | 1 + .../Functions/MapFunction.cs | 1 + .../Functions/MaxByFunction.cs | 1 + .../Functions/MaxFunction.cs | 1 + .../Functions/MergeFunction.cs | 1 + .../Functions/MinByFunction.cs | 1 + .../Functions/MinFunction.cs | 1 + .../Functions/NotNullFunction.cs | 1 + .../Functions/ReverseFunction.cs | 1 + .../Functions/SortByComparer.cs | 1 + .../Functions/SortByFunction.cs | 1 + .../Functions/SortFunction.cs | 1 + .../Functions/StartsWithFunction.cs | 1 + .../Functions/SumFunction.cs | 1 + .../Functions/ToArrayFunction.cs | 1 + .../Functions/ToNumberFunction.cs | 1 + .../Functions/ToStringFunction.cs | 1 + .../Functions/TypeFunction.cs | 1 + .../Functions/ValuesFunction.cs | 1 + .../JmesPathParser.cs | 1 + .../JsonTransformer.cs | 1 + .../AWS.Lambda.Powertools.JMESPath/Token.cs | 1 + .../UnaryOperator.cs | 1 + .../AWS.Lambda.Powertools.JMESPath/Value.cs | 913 ------------------ .../Values/ArrayValue.cs | 129 +++ .../Values/DecimalValue.cs | 81 ++ .../Values/DoubleValue.cs | 88 ++ .../Values/ExpressionValue.cs | 77 ++ .../Values/FalseValue.cs | 70 ++ .../Values/IArrayValueEnumerator.cs | 22 + .../Values/IObjectValueEnumerator.cs | 22 + .../Values/IValue.cs | 30 + .../Values/JmesPathType.cs | 28 + .../Values/JsonElementValue.cs | 224 +++++ .../Values/NameValuePair.cs | 29 + .../Values/NullValue.cs | 70 ++ .../Values/ObjectValue.cs | 147 +++ .../Values/StringValue.cs | 79 ++ .../Values/TrueValue.cs | 70 ++ .../{ => Values}/ValueComparer.cs | 2 +- .../{ => Values}/ValueEqualityComparer.cs | 2 +- 59 files changed, 1210 insertions(+), 915 deletions(-) delete mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Value.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ArrayValue.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DecimalValue.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DoubleValue.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ExpressionValue.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Values/FalseValue.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IArrayValueEnumerator.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IObjectValueEnumerator.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IValue.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JmesPathType.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JsonElementValue.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NameValuePair.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NullValue.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ObjectValue.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Values/StringValue.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Values/TrueValue.cs rename libraries/src/AWS.Lambda.Powertools.JMESPath/{ => Values}/ValueComparer.cs (99%) rename libraries/src/AWS.Lambda.Powertools.JMESPath/{ => Values}/ValueEqualityComparer.cs (99%) diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs index 5e93629a..9e01cffa 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs @@ -13,6 +13,8 @@ * permissions and limitations under the License. */ +using AWS.Lambda.Powertools.JMESPath.Values; + namespace AWS.Lambda.Powertools.JMESPath { internal interface IBinaryOperator diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs index 426278ea..41b2cd23 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs @@ -16,6 +16,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath { diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs index e1733344..08dafa02 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs index 9dc83294..0ee6e5b3 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64Function.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64Function.cs index 6db33b66..8902aadf 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64Function.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64Function.cs @@ -17,6 +17,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text.Json; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64GzipFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64GzipFunction.cs index a68cd5c8..eacec5a3 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64GzipFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64GzipFunction.cs @@ -20,6 +20,7 @@ using System.IO.Compression; using System.Text; using System.Text.Json; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs index b4162cea..f3925a2c 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs @@ -14,6 +14,7 @@ */ using System.Collections.Generic; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs index 2f0e773c..81f45289 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs @@ -16,6 +16,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs index 2953eb98..01b5e051 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs @@ -16,6 +16,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs index c0af8786..25873f32 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMax.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMax.cs index fd8de403..233b7cde 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMax.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMax.cs @@ -14,6 +14,7 @@ */ using System.Collections.Generic; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMaxBy.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMaxBy.cs index a177a5ac..423b6ab4 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMaxBy.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMaxBy.cs @@ -14,6 +14,7 @@ */ using System.Collections.Generic; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateStartEndWith.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateStartEndWith.cs index 11ae9ee2..52e14913 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateStartEndWith.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateStartEndWith.cs @@ -15,6 +15,7 @@ using System; using System.Collections.Generic; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs index 4eef9848..2c559328 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs @@ -16,6 +16,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs index 476838e8..610b02bd 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs @@ -14,6 +14,7 @@ */ using System.Collections.Generic; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs index b32b6001..4e62ab84 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs @@ -16,6 +16,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JsonFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JsonFunction.cs index aba2b558..b2d63595 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JsonFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JsonFunction.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs index 257812cd..0dd72074 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs index e1779c3b..a3104b64 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs @@ -16,6 +16,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs index 100c4790..e08680af 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs index 0cfd4464..ffe3dcc3 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs index ffa99756..16cde15c 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs index e0dde1ac..17b60d45 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Linq; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs index 82f7c38f..ccc8a9b8 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs index 2fd25e97..005bb8bd 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs index d2eee194..8d2cdcf4 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs @@ -14,6 +14,7 @@ */ using System.Collections.Generic; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs index cba14dc6..94af36a0 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs @@ -17,6 +17,7 @@ using System.Diagnostics; using System.Globalization; using System.Linq; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByComparer.cs index 70c5167e..af54b9fd 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByComparer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByComparer.cs @@ -14,6 +14,7 @@ */ using System.Collections.Generic; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions { diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs index 4b54a400..5d9c1481 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs index 009bf66d..af8f092c 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs index a59261a3..4ba5f87e 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs index 554e91db..e634a7d5 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs index 9c79e35f..02e6e22b 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs index d00f8eb0..83e5f98f 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs @@ -16,6 +16,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs index bea47423..d6745944 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs index aac891ba..16ab2b46 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs index 1942c9aa..6ddf44dc 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs index a7641fe3..9b609368 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs @@ -19,6 +19,7 @@ using System.Text; using System.Text.Json; using AWS.Lambda.Powertools.JMESPath.Functions; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath { diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs index f7146737..80530cc2 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs @@ -15,6 +15,7 @@ using System; using System.Text.Json; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath { diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs index 7ac5433b..8a27fb20 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs @@ -16,6 +16,7 @@ using System; using System.Diagnostics; using AWS.Lambda.Powertools.JMESPath.Functions; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath { diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs index 6e21c0fd..b769a1e1 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs @@ -14,6 +14,7 @@ */ using System.Text.RegularExpressions; +using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath { diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Value.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Value.cs deleted file mode 100644 index 4b157cbe..00000000 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Value.cs +++ /dev/null @@ -1,913 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.Json; -using System.Text.Json.Nodes; - -namespace AWS.Lambda.Powertools.JMESPath -{ - internal readonly struct NameValuePair - { - public string Name { get; } - public IValue Value { get; } - - public NameValuePair(string name, IValue value) - { - Name = name; - Value = value; - } - } - - internal interface IArrayValueEnumerator : IEnumerator, IEnumerable - { - } - - internal interface IObjectValueEnumerator : IEnumerator, IEnumerable - { - } - - internal enum JmesPathType - { - Null, - Array, - False, - Number, - Object, - String, - True, - Expression - } - - internal interface IValue - { - JmesPathType Type { get; } - IValue this[int index] { get; } - int GetArrayLength(); - string GetString(); - bool TryGetDecimal(out decimal value); - bool TryGetDouble(out double value); - bool TryGetProperty(string propertyName, out IValue property); - IArrayValueEnumerator EnumerateArray(); - IObjectValueEnumerator EnumerateObject(); - IExpression GetExpression(); - } - - internal readonly struct JsonElementValue : IValue - { - private class ArrayEnumerator : IArrayValueEnumerator - { - private JsonElement.ArrayEnumerator _enumerator; - - public ArrayEnumerator(JsonElement.ArrayEnumerator enumerator) - { - _enumerator = enumerator; - } - - public bool MoveNext() - { - return _enumerator.MoveNext(); - } - - public void Reset() - { - _enumerator.Reset(); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - // Cleanup - if (disposing) - { - _enumerator.Dispose(); - } - } - - public IValue Current => new JsonElementValue(_enumerator.Current); - - object System.Collections.IEnumerator.Current => Current; - - public IEnumerator GetEnumerator() - { - return new ArrayEnumerator(_enumerator.GetEnumerator()); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } - - private class ObjectEnumerator : IObjectValueEnumerator - { - private JsonElement.ObjectEnumerator _enumerator; - - public ObjectEnumerator(JsonElement.ObjectEnumerator enumerator) - { - _enumerator = enumerator; - } - - public bool MoveNext() - { - return _enumerator.MoveNext(); - } - - public void Reset() - { - _enumerator.Reset(); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - // Cleanup - if (disposing) - { - _enumerator.Dispose(); - } - } - - public NameValuePair Current => - new(_enumerator.Current.Name, new JsonElementValue(_enumerator.Current.Value)); - - object System.Collections.IEnumerator.Current => Current; - - public IEnumerator GetEnumerator() - { - return new ObjectEnumerator(_enumerator); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } - - private readonly JsonElement _element; - - internal JsonElementValue(JsonElement element) - { - _element = element; - } - - public JmesPathType Type - { - get - { - switch (_element.ValueKind) - { - case JsonValueKind.Array: - return JmesPathType.Array; - case JsonValueKind.False: - return JmesPathType.False; - case JsonValueKind.Number: - return JmesPathType.Number; - case JsonValueKind.Object: - return JmesPathType.Object; - case JsonValueKind.String: - return JmesPathType.String; - case JsonValueKind.True: - return JmesPathType.True; - default: - return JmesPathType.Null; - } - } - } - - public IValue this[int index] => new JsonElementValue(_element[index]); - - public int GetArrayLength() - { - return _element.GetArrayLength(); - } - - public string GetString() - { - return _element.GetString() ?? throw new InvalidOperationException("String cannot be null"); - } - - public bool TryGetDecimal(out decimal value) - { - return _element.TryGetDecimal(out value); - } - - public bool TryGetDouble(out double value) - { - return _element.TryGetDouble(out value); - } - - public bool TryGetProperty(string propertyName, out IValue property) - { - var r = _element.TryGetProperty(propertyName, out var prop); - - property = prop.ValueKind == JsonValueKind.String && IsJsonValid(prop.GetString()) - ? new JsonElementValue(JsonNode.Parse(prop.GetString() ?? string.Empty).Deserialize()) - : new JsonElementValue(prop); - - return r; - } - - private static bool IsJsonValid(string json) - { - if (string.IsNullOrWhiteSpace(json)) - return false; - - try - { - using var jsonDoc = JsonDocument.Parse(json); - return true; - } - catch (JsonException) - { - return false; - } - } - - public IArrayValueEnumerator EnumerateArray() - { - return new ArrayEnumerator(_element.EnumerateArray()); - } - - public IObjectValueEnumerator EnumerateObject() - { - return new ObjectEnumerator(_element.EnumerateObject()); - } - - public IExpression GetExpression() - { - throw new InvalidOperationException("Not an expression"); - } - - public override string ToString() - { - var s = JsonSerializer.Serialize(_element); - return s; - } - } - - internal readonly struct DoubleValue : IValue - { - private readonly double _value; - - internal DoubleValue(double value) - { - _value = value; - } - - public JmesPathType Type => JmesPathType.Number; - - public IValue this[int index] => throw new InvalidOperationException(); - - public int GetArrayLength() - { - throw new InvalidOperationException(); - } - - public string GetString() - { - throw new InvalidOperationException(); - } - - public bool TryGetDecimal(out decimal value) - { - if (!(double.IsNaN(_value) || double.IsInfinity(_value)) && - _value is >= (double)decimal.MinValue and <= (double)decimal.MaxValue) - { - value = decimal.MinValue; - return false; - } - - value = new decimal(_value); - return true; - } - - public bool TryGetDouble(out double value) - { - value = _value; - return true; - } - - public bool TryGetProperty(string propertyName, out IValue property) - { - throw new InvalidOperationException(); - } - - public IArrayValueEnumerator EnumerateArray() - { - throw new InvalidOperationException(); - } - - public IObjectValueEnumerator EnumerateObject() - { - throw new InvalidOperationException(); - } - - public IExpression GetExpression() - { - throw new InvalidOperationException("Not an expression"); - } - - public override string ToString() - { - var s = JsonSerializer.Serialize(_value); - return s; - } - } - - internal readonly struct DecimalValue : IValue - { - private readonly decimal _value; - - internal DecimalValue(decimal value) - { - _value = value; - } - - public JmesPathType Type => JmesPathType.Number; - - public IValue this[int index] => throw new InvalidOperationException(); - - public int GetArrayLength() - { - throw new InvalidOperationException(); - } - - public string GetString() - { - throw new InvalidOperationException(); - } - - public bool TryGetDecimal(out decimal value) - { - value = _value; - return true; - } - - public bool TryGetDouble(out double value) - { - value = (double)_value; - return true; - } - - public bool TryGetProperty(string propertyName, out IValue property) - { - throw new InvalidOperationException(); - } - - public IArrayValueEnumerator EnumerateArray() - { - throw new InvalidOperationException(); - } - - public IObjectValueEnumerator EnumerateObject() - { - throw new InvalidOperationException(); - } - - public IExpression GetExpression() - { - throw new InvalidOperationException("Not an expression"); - } - - public override string ToString() - { - var s = JsonSerializer.Serialize(_value); - return s; - } - } - - internal readonly struct StringValue : IValue - { - private readonly string _value; - - internal StringValue(string value) - { - _value = value; - } - - public JmesPathType Type => JmesPathType.String; - - public IValue this[int index] => throw new InvalidOperationException(); - - public int GetArrayLength() - { - throw new InvalidOperationException(); - } - - public string GetString() - { - return _value; - } - - public bool TryGetDecimal(out decimal value) - { - throw new InvalidOperationException(); - } - - public bool TryGetDouble(out double value) - { - throw new InvalidOperationException(); - } - - public bool TryGetProperty(string propertyName, out IValue property) - { - throw new InvalidOperationException(); - } - - public IArrayValueEnumerator EnumerateArray() - { - throw new InvalidOperationException(); - } - - public IObjectValueEnumerator EnumerateObject() - { - throw new InvalidOperationException(); - } - - public IExpression GetExpression() - { - throw new InvalidOperationException("Not an expression"); - } - - public override string ToString() - { - var s = JsonSerializer.Serialize(_value); - return s; - } - } - - internal readonly struct TrueValue : IValue - { - public JmesPathType Type => JmesPathType.True; - - public IValue this[int index] => throw new InvalidOperationException(); - - public int GetArrayLength() - { - throw new InvalidOperationException(); - } - - public string GetString() - { - throw new InvalidOperationException(); - } - - public bool TryGetDecimal(out decimal value) - { - throw new InvalidOperationException(); - } - - public bool TryGetDouble(out double value) - { - throw new InvalidOperationException(); - } - - public bool TryGetProperty(string propertyName, out IValue property) - { - throw new InvalidOperationException(); - } - - public IArrayValueEnumerator EnumerateArray() - { - throw new InvalidOperationException(); - } - - public IObjectValueEnumerator EnumerateObject() - { - throw new InvalidOperationException(); - } - - public IExpression GetExpression() - { - throw new InvalidOperationException("Not an expression"); - } - - public override string ToString() - { - return "true"; - } - } - - internal readonly struct FalseValue : IValue - { - public JmesPathType Type => JmesPathType.False; - - public IValue this[int index] => throw new InvalidOperationException(); - - public int GetArrayLength() - { - throw new InvalidOperationException(); - } - - public string GetString() - { - throw new InvalidOperationException(); - } - - public bool TryGetDecimal(out decimal value) - { - throw new InvalidOperationException(); - } - - public bool TryGetDouble(out double value) - { - throw new InvalidOperationException(); - } - - public bool TryGetProperty(string propertyName, out IValue property) - { - throw new InvalidOperationException(); - } - - public IArrayValueEnumerator EnumerateArray() - { - throw new InvalidOperationException(); - } - - public IObjectValueEnumerator EnumerateObject() - { - throw new InvalidOperationException(); - } - - public IExpression GetExpression() - { - throw new InvalidOperationException("Not an expression"); - } - - public override string ToString() - { - return "false"; - } - } - - internal readonly struct NullValue : IValue - { - public JmesPathType Type => JmesPathType.Null; - - public IValue this[int index] => throw new InvalidOperationException(); - - public int GetArrayLength() - { - throw new InvalidOperationException(); - } - - public string GetString() - { - throw new InvalidOperationException(); - } - - public bool TryGetDecimal(out decimal value) - { - throw new InvalidOperationException(); - } - - public bool TryGetDouble(out double value) - { - throw new InvalidOperationException(); - } - - public bool TryGetProperty(string propertyName, out IValue property) - { - throw new InvalidOperationException(); - } - - public IArrayValueEnumerator EnumerateArray() - { - throw new InvalidOperationException(); - } - - public IObjectValueEnumerator EnumerateObject() - { - throw new InvalidOperationException(); - } - - public IExpression GetExpression() - { - throw new InvalidOperationException("Not an expression"); - } - - public override string ToString() - { - return "null"; - } - } - - internal readonly struct ArrayValue : IValue - { - private sealed class ArrayEnumerator : IArrayValueEnumerator - { - private readonly IList _value; - private readonly System.Collections.IEnumerator _enumerator; - - public ArrayEnumerator(IList value) - { - _value = value; - _enumerator = value.GetEnumerator(); - } - - public bool MoveNext() - { - return _enumerator.MoveNext(); - } - - public void Reset() { _enumerator.Reset(); } - - void IDisposable.Dispose() {} - - public IValue Current => _enumerator.Current as IValue ?? throw new InvalidOperationException("Current cannot be null"); - - object System.Collections.IEnumerator.Current => Current; - - public IEnumerator GetEnumerator() - { - return _value.GetEnumerator(); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } - - private readonly IList _value; - - internal ArrayValue(IList value) - { - _value = value; - } - - public JmesPathType Type => JmesPathType.Array; - - public IValue this[int index] => _value[index]; - - public int GetArrayLength() { return _value.Count; } - - public string GetString() - { - throw new InvalidOperationException(); - } - - public bool TryGetDecimal(out decimal value) - { - throw new InvalidOperationException(); - } - - public bool TryGetDouble(out double value) - { - throw new InvalidOperationException(); - } - - public bool TryGetProperty(string propertyName, out IValue property) - { - throw new InvalidOperationException(); - } - - public IArrayValueEnumerator EnumerateArray() - { - return new ArrayEnumerator(_value); - } - - public IObjectValueEnumerator EnumerateObject() - { - throw new InvalidOperationException(); - } - - public IExpression GetExpression() - { - throw new InvalidOperationException("Not an expression"); - } - - public override string ToString() - { - var buffer = new StringBuilder(); - buffer.Append('['); - var first = true; - foreach (var item in _value) - { - if (!first) - { - buffer.Append(','); - } - else - { - first = false; - } - - buffer.Append(item); - } - - buffer.Append(']'); - return buffer.ToString(); - } - } - - internal readonly struct ObjectValue : IValue - { - private sealed class ObjectEnumerator : IObjectValueEnumerator - { - private readonly IDictionary _value; - private readonly System.Collections.IEnumerator _enumerator; - - public ObjectEnumerator(IDictionary value) - { - _value = value; - _enumerator = value.GetEnumerator(); - } - - public bool MoveNext() - { - return _enumerator.MoveNext(); - } - - public void Reset() - { - _enumerator.Reset(); - } - - void IDisposable.Dispose() - { - } - - public NameValuePair Current - { - get - { - var pair = (KeyValuePair)_enumerator.Current!; - return new NameValuePair(pair.Key, pair.Value); - } - } - - object System.Collections.IEnumerator.Current => Current; - - public IEnumerator GetEnumerator() - { - return new ObjectEnumerator(_value); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } - - private readonly IDictionary _value; - - internal ObjectValue(IDictionary value) - { - _value = value; - } - - public JmesPathType Type => JmesPathType.Object; - - public IValue this[int index] => throw new InvalidOperationException(); - - public int GetArrayLength() - { - throw new InvalidOperationException(); - } - - public string GetString() - { - throw new InvalidOperationException(); - } - - public bool TryGetDecimal(out decimal value) - { - throw new InvalidOperationException(); - } - - public bool TryGetDouble(out double value) - { - throw new InvalidOperationException(); - } - - public bool TryGetProperty(string propertyName, out IValue property) - { - return _value.TryGetValue(propertyName, out property); - } - - public IArrayValueEnumerator EnumerateArray() - { - throw new InvalidOperationException(); - } - - public IObjectValueEnumerator EnumerateObject() - { - return new ObjectEnumerator(_value); - } - - public IExpression GetExpression() - { - throw new InvalidOperationException("Not an expression"); - } - - public override string ToString() - { - var buffer = new StringBuilder(); - buffer.Append('{'); - var first = true; - foreach (var property in _value) - { - if (!first) - { - buffer.Append(','); - } - else - { - first = false; - } - - buffer.Append(JsonSerializer.Serialize(property.Key)); - buffer.Append(':'); - buffer.Append(property.Value); - } - - buffer.Append('}'); - return buffer.ToString(); - } - } - - internal readonly struct ExpressionValue : IValue - { - private readonly IExpression _expr; - - internal ExpressionValue(IExpression expr) - { - _expr = expr; - } - - public JmesPathType Type => JmesPathType.Expression; - - public IValue this[int index] => throw new InvalidOperationException(); - - public int GetArrayLength() - { - throw new InvalidOperationException(); - } - - public string GetString() - { - throw new InvalidOperationException(); - } - - public bool TryGetDecimal(out decimal value) - { - throw new InvalidOperationException(); - } - - public bool TryGetDouble(out double value) - { - throw new InvalidOperationException(); - } - - public bool TryGetProperty(string propertyName, out IValue property) - { - throw new InvalidOperationException(); - } - - public IArrayValueEnumerator EnumerateArray() - { - throw new InvalidOperationException(); - } - - public IObjectValueEnumerator EnumerateObject() - { - throw new InvalidOperationException(); - } - - public IExpression GetExpression() - { - return _expr; - } - - public override string ToString() - { - return "expression"; - } - } -} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ArrayValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ArrayValue.cs new file mode 100644 index 00000000..43008b80 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ArrayValue.cs @@ -0,0 +1,129 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace AWS.Lambda.Powertools.JMESPath.Values; + +internal readonly struct ArrayValue : IValue +{ + private sealed class ArrayEnumerator : IArrayValueEnumerator + { + private readonly IList _value; + private readonly System.Collections.IEnumerator _enumerator; + + public ArrayEnumerator(IList value) + { + _value = value; + _enumerator = value.GetEnumerator(); + } + + public bool MoveNext() + { + return _enumerator.MoveNext(); + } + + public void Reset() { _enumerator.Reset(); } + + void IDisposable.Dispose() {} + + public IValue Current => _enumerator.Current as IValue ?? throw new InvalidOperationException("Current cannot be null"); + + object System.Collections.IEnumerator.Current => Current; + + public IEnumerator GetEnumerator() + { + return _value.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + private readonly IList _value; + + internal ArrayValue(IList value) + { + _value = value; + } + + public JmesPathType Type => JmesPathType.Array; + + public IValue this[int index] => _value[index]; + + public int GetArrayLength() { return _value.Count; } + + public string GetString() + { + throw new InvalidOperationException(); + } + + public bool TryGetDecimal(out decimal value) + { + throw new InvalidOperationException(); + } + + public bool TryGetDouble(out double value) + { + throw new InvalidOperationException(); + } + + public bool TryGetProperty(string propertyName, out IValue property) + { + throw new InvalidOperationException(); + } + + public IArrayValueEnumerator EnumerateArray() + { + return new ArrayEnumerator(_value); + } + + public IObjectValueEnumerator EnumerateObject() + { + throw new InvalidOperationException(); + } + + public IExpression GetExpression() + { + throw new InvalidOperationException("Not an expression"); + } + + public override string ToString() + { + var buffer = new StringBuilder(); + buffer.Append('['); + var first = true; + foreach (var item in _value) + { + if (!first) + { + buffer.Append(','); + } + else + { + first = false; + } + + buffer.Append(item); + } + + buffer.Append(']'); + return buffer.ToString(); + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DecimalValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DecimalValue.cs new file mode 100644 index 00000000..3cdf685d --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DecimalValue.cs @@ -0,0 +1,81 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; +using System.Text.Json; + +namespace AWS.Lambda.Powertools.JMESPath.Values; + +internal readonly struct DecimalValue : IValue +{ + private readonly decimal _value; + + internal DecimalValue(decimal value) + { + _value = value; + } + + public JmesPathType Type => JmesPathType.Number; + + public IValue this[int index] => throw new InvalidOperationException(); + + public int GetArrayLength() + { + throw new InvalidOperationException(); + } + + public string GetString() + { + throw new InvalidOperationException(); + } + + public bool TryGetDecimal(out decimal value) + { + value = _value; + return true; + } + + public bool TryGetDouble(out double value) + { + value = (double)_value; + return true; + } + + public bool TryGetProperty(string propertyName, out IValue property) + { + throw new InvalidOperationException(); + } + + public IArrayValueEnumerator EnumerateArray() + { + throw new InvalidOperationException(); + } + + public IObjectValueEnumerator EnumerateObject() + { + throw new InvalidOperationException(); + } + + public IExpression GetExpression() + { + throw new InvalidOperationException("Not an expression"); + } + + public override string ToString() + { + var s = JsonSerializer.Serialize(_value); + return s; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DoubleValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DoubleValue.cs new file mode 100644 index 00000000..56649d55 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DoubleValue.cs @@ -0,0 +1,88 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; +using System.Text.Json; + +namespace AWS.Lambda.Powertools.JMESPath.Values; + +internal readonly struct DoubleValue : IValue +{ + private readonly double _value; + + internal DoubleValue(double value) + { + _value = value; + } + + public JmesPathType Type => JmesPathType.Number; + + public IValue this[int index] => throw new InvalidOperationException(); + + public int GetArrayLength() + { + throw new InvalidOperationException(); + } + + public string GetString() + { + throw new InvalidOperationException(); + } + + public bool TryGetDecimal(out decimal value) + { + if (!(double.IsNaN(_value) || double.IsInfinity(_value)) && + _value is >= (double)decimal.MinValue and <= (double)decimal.MaxValue) + { + value = decimal.MinValue; + return false; + } + + value = new decimal(_value); + return true; + } + + public bool TryGetDouble(out double value) + { + value = _value; + return true; + } + + public bool TryGetProperty(string propertyName, out IValue property) + { + throw new InvalidOperationException(); + } + + public IArrayValueEnumerator EnumerateArray() + { + throw new InvalidOperationException(); + } + + public IObjectValueEnumerator EnumerateObject() + { + throw new InvalidOperationException(); + } + + public IExpression GetExpression() + { + throw new InvalidOperationException("Not an expression"); + } + + public override string ToString() + { + var s = JsonSerializer.Serialize(_value); + return s; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ExpressionValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ExpressionValue.cs new file mode 100644 index 00000000..b9a1e425 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ExpressionValue.cs @@ -0,0 +1,77 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; + +namespace AWS.Lambda.Powertools.JMESPath.Values; + +internal readonly struct ExpressionValue : IValue +{ + private readonly IExpression _expr; + + internal ExpressionValue(IExpression expr) + { + _expr = expr; + } + + public JmesPathType Type => JmesPathType.Expression; + + public IValue this[int index] => throw new InvalidOperationException(); + + public int GetArrayLength() + { + throw new InvalidOperationException(); + } + + public string GetString() + { + throw new InvalidOperationException(); + } + + public bool TryGetDecimal(out decimal value) + { + throw new InvalidOperationException(); + } + + public bool TryGetDouble(out double value) + { + throw new InvalidOperationException(); + } + + public bool TryGetProperty(string propertyName, out IValue property) + { + throw new InvalidOperationException(); + } + + public IArrayValueEnumerator EnumerateArray() + { + throw new InvalidOperationException(); + } + + public IObjectValueEnumerator EnumerateObject() + { + throw new InvalidOperationException(); + } + + public IExpression GetExpression() + { + return _expr; + } + + public override string ToString() + { + return "expression"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/FalseValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/FalseValue.cs new file mode 100644 index 00000000..6b06928c --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/FalseValue.cs @@ -0,0 +1,70 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; + +namespace AWS.Lambda.Powertools.JMESPath.Values; + +internal readonly struct FalseValue : IValue +{ + public JmesPathType Type => JmesPathType.False; + + public IValue this[int index] => throw new InvalidOperationException(); + + public int GetArrayLength() + { + throw new InvalidOperationException(); + } + + public string GetString() + { + throw new InvalidOperationException(); + } + + public bool TryGetDecimal(out decimal value) + { + throw new InvalidOperationException(); + } + + public bool TryGetDouble(out double value) + { + throw new InvalidOperationException(); + } + + public bool TryGetProperty(string propertyName, out IValue property) + { + throw new InvalidOperationException(); + } + + public IArrayValueEnumerator EnumerateArray() + { + throw new InvalidOperationException(); + } + + public IObjectValueEnumerator EnumerateObject() + { + throw new InvalidOperationException(); + } + + public IExpression GetExpression() + { + throw new InvalidOperationException("Not an expression"); + } + + public override string ToString() + { + return "false"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IArrayValueEnumerator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IArrayValueEnumerator.cs new file mode 100644 index 00000000..a96a311c --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IArrayValueEnumerator.cs @@ -0,0 +1,22 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Collections.Generic; + +namespace AWS.Lambda.Powertools.JMESPath.Values; + +internal interface IArrayValueEnumerator : IEnumerator, IEnumerable +{ +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IObjectValueEnumerator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IObjectValueEnumerator.cs new file mode 100644 index 00000000..05f6b075 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IObjectValueEnumerator.cs @@ -0,0 +1,22 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Collections.Generic; + +namespace AWS.Lambda.Powertools.JMESPath.Values; + +internal interface IObjectValueEnumerator : IEnumerator, IEnumerable +{ +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IValue.cs new file mode 100644 index 00000000..a0295f62 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IValue.cs @@ -0,0 +1,30 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +namespace AWS.Lambda.Powertools.JMESPath.Values; + +internal interface IValue +{ + JmesPathType Type { get; } + IValue this[int index] { get; } + int GetArrayLength(); + string GetString(); + bool TryGetDecimal(out decimal value); + bool TryGetDouble(out double value); + bool TryGetProperty(string propertyName, out IValue property); + IArrayValueEnumerator EnumerateArray(); + IObjectValueEnumerator EnumerateObject(); + IExpression GetExpression(); +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JmesPathType.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JmesPathType.cs new file mode 100644 index 00000000..20f154f3 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JmesPathType.cs @@ -0,0 +1,28 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +namespace AWS.Lambda.Powertools.JMESPath.Values; + +internal enum JmesPathType +{ + Null, + Array, + False, + Number, + Object, + String, + True, + Expression +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JsonElementValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JsonElementValue.cs new file mode 100644 index 00000000..61028276 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JsonElementValue.cs @@ -0,0 +1,224 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Nodes; + +namespace AWS.Lambda.Powertools.JMESPath.Values; + +internal readonly struct JsonElementValue : IValue +{ + private class ArrayEnumerator : IArrayValueEnumerator + { + private JsonElement.ArrayEnumerator _enumerator; + + public ArrayEnumerator(JsonElement.ArrayEnumerator enumerator) + { + _enumerator = enumerator; + } + + public bool MoveNext() + { + return _enumerator.MoveNext(); + } + + public void Reset() + { + _enumerator.Reset(); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + // Cleanup + if (disposing) + { + _enumerator.Dispose(); + } + } + + public IValue Current => new JsonElementValue(_enumerator.Current); + + object System.Collections.IEnumerator.Current => Current; + + public IEnumerator GetEnumerator() + { + return new ArrayEnumerator(_enumerator.GetEnumerator()); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + private class ObjectEnumerator : IObjectValueEnumerator + { + private JsonElement.ObjectEnumerator _enumerator; + + public ObjectEnumerator(JsonElement.ObjectEnumerator enumerator) + { + _enumerator = enumerator; + } + + public bool MoveNext() + { + return _enumerator.MoveNext(); + } + + public void Reset() + { + _enumerator.Reset(); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + // Cleanup + if (disposing) + { + _enumerator.Dispose(); + } + } + + public NameValuePair Current => + new(_enumerator.Current.Name, new JsonElementValue(_enumerator.Current.Value)); + + object System.Collections.IEnumerator.Current => Current; + + public IEnumerator GetEnumerator() + { + return new ObjectEnumerator(_enumerator); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + private readonly JsonElement _element; + + internal JsonElementValue(JsonElement element) + { + _element = element; + } + + public JmesPathType Type + { + get + { + switch (_element.ValueKind) + { + case JsonValueKind.Array: + return JmesPathType.Array; + case JsonValueKind.False: + return JmesPathType.False; + case JsonValueKind.Number: + return JmesPathType.Number; + case JsonValueKind.Object: + return JmesPathType.Object; + case JsonValueKind.String: + return JmesPathType.String; + case JsonValueKind.True: + return JmesPathType.True; + default: + return JmesPathType.Null; + } + } + } + + public IValue this[int index] => new JsonElementValue(_element[index]); + + public int GetArrayLength() + { + return _element.GetArrayLength(); + } + + public string GetString() + { + return _element.GetString() ?? throw new InvalidOperationException("String cannot be null"); + } + + public bool TryGetDecimal(out decimal value) + { + return _element.TryGetDecimal(out value); + } + + public bool TryGetDouble(out double value) + { + return _element.TryGetDouble(out value); + } + + public bool TryGetProperty(string propertyName, out IValue property) + { + var r = _element.TryGetProperty(propertyName, out var prop); + + property = prop.ValueKind == JsonValueKind.String && IsJsonValid(prop.GetString()) + ? new JsonElementValue(JsonNode.Parse(prop.GetString() ?? string.Empty).Deserialize()) + : new JsonElementValue(prop); + + return r; + } + + private static bool IsJsonValid(string json) + { + if (string.IsNullOrWhiteSpace(json)) + return false; + + try + { + using var jsonDoc = JsonDocument.Parse(json); + return true; + } + catch (JsonException) + { + return false; + } + } + + public IArrayValueEnumerator EnumerateArray() + { + return new ArrayEnumerator(_element.EnumerateArray()); + } + + public IObjectValueEnumerator EnumerateObject() + { + return new ObjectEnumerator(_element.EnumerateObject()); + } + + public IExpression GetExpression() + { + throw new InvalidOperationException("Not an expression"); + } + + public override string ToString() + { + var s = JsonSerializer.Serialize(_element); + return s; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NameValuePair.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NameValuePair.cs new file mode 100644 index 00000000..2ffe2717 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NameValuePair.cs @@ -0,0 +1,29 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +namespace AWS.Lambda.Powertools.JMESPath.Values +{ + internal readonly struct NameValuePair + { + public string Name { get; } + public IValue Value { get; } + + public NameValuePair(string name, IValue value) + { + Name = name; + Value = value; + } + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NullValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NullValue.cs new file mode 100644 index 00000000..fc509571 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NullValue.cs @@ -0,0 +1,70 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; + +namespace AWS.Lambda.Powertools.JMESPath.Values; + +internal readonly struct NullValue : IValue +{ + public JmesPathType Type => JmesPathType.Null; + + public IValue this[int index] => throw new InvalidOperationException(); + + public int GetArrayLength() + { + throw new InvalidOperationException(); + } + + public string GetString() + { + throw new InvalidOperationException(); + } + + public bool TryGetDecimal(out decimal value) + { + throw new InvalidOperationException(); + } + + public bool TryGetDouble(out double value) + { + throw new InvalidOperationException(); + } + + public bool TryGetProperty(string propertyName, out IValue property) + { + throw new InvalidOperationException(); + } + + public IArrayValueEnumerator EnumerateArray() + { + throw new InvalidOperationException(); + } + + public IObjectValueEnumerator EnumerateObject() + { + throw new InvalidOperationException(); + } + + public IExpression GetExpression() + { + throw new InvalidOperationException("Not an expression"); + } + + public override string ToString() + { + return "null"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ObjectValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ObjectValue.cs new file mode 100644 index 00000000..c1ff0c9e --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ObjectValue.cs @@ -0,0 +1,147 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json; + +namespace AWS.Lambda.Powertools.JMESPath.Values; + +internal readonly struct ObjectValue : IValue +{ + private sealed class ObjectEnumerator : IObjectValueEnumerator + { + private readonly IDictionary _value; + private readonly System.Collections.IEnumerator _enumerator; + + public ObjectEnumerator(IDictionary value) + { + _value = value; + _enumerator = value.GetEnumerator(); + } + + public bool MoveNext() + { + return _enumerator.MoveNext(); + } + + public void Reset() + { + _enumerator.Reset(); + } + + void IDisposable.Dispose() + { + } + + public NameValuePair Current + { + get + { + var pair = (KeyValuePair)_enumerator.Current!; + return new NameValuePair(pair.Key, pair.Value); + } + } + + object System.Collections.IEnumerator.Current => Current; + + public IEnumerator GetEnumerator() + { + return new ObjectEnumerator(_value); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + private readonly IDictionary _value; + + internal ObjectValue(IDictionary value) + { + _value = value; + } + + public JmesPathType Type => JmesPathType.Object; + + public IValue this[int index] => throw new InvalidOperationException(); + + public int GetArrayLength() + { + throw new InvalidOperationException(); + } + + public string GetString() + { + throw new InvalidOperationException(); + } + + public bool TryGetDecimal(out decimal value) + { + throw new InvalidOperationException(); + } + + public bool TryGetDouble(out double value) + { + throw new InvalidOperationException(); + } + + public bool TryGetProperty(string propertyName, out IValue property) + { + return _value.TryGetValue(propertyName, out property); + } + + public IArrayValueEnumerator EnumerateArray() + { + throw new InvalidOperationException(); + } + + public IObjectValueEnumerator EnumerateObject() + { + return new ObjectEnumerator(_value); + } + + public IExpression GetExpression() + { + throw new InvalidOperationException("Not an expression"); + } + + public override string ToString() + { + var buffer = new StringBuilder(); + buffer.Append('{'); + var first = true; + foreach (var property in _value) + { + if (!first) + { + buffer.Append(','); + } + else + { + first = false; + } + + buffer.Append(JsonSerializer.Serialize(property.Key)); + buffer.Append(':'); + buffer.Append(property.Value); + } + + buffer.Append('}'); + return buffer.ToString(); + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/StringValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/StringValue.cs new file mode 100644 index 00000000..1146cc21 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/StringValue.cs @@ -0,0 +1,79 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; +using System.Text.Json; + +namespace AWS.Lambda.Powertools.JMESPath.Values; + +internal readonly struct StringValue : IValue +{ + private readonly string _value; + + internal StringValue(string value) + { + _value = value; + } + + public JmesPathType Type => JmesPathType.String; + + public IValue this[int index] => throw new InvalidOperationException(); + + public int GetArrayLength() + { + throw new InvalidOperationException(); + } + + public string GetString() + { + return _value; + } + + public bool TryGetDecimal(out decimal value) + { + throw new InvalidOperationException(); + } + + public bool TryGetDouble(out double value) + { + throw new InvalidOperationException(); + } + + public bool TryGetProperty(string propertyName, out IValue property) + { + throw new InvalidOperationException(); + } + + public IArrayValueEnumerator EnumerateArray() + { + throw new InvalidOperationException(); + } + + public IObjectValueEnumerator EnumerateObject() + { + throw new InvalidOperationException(); + } + + public IExpression GetExpression() + { + throw new InvalidOperationException("Not an expression"); + } + + public override string ToString() + { + var s = JsonSerializer.Serialize(_value); + return s; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/TrueValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/TrueValue.cs new file mode 100644 index 00000000..60309692 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/TrueValue.cs @@ -0,0 +1,70 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; + +namespace AWS.Lambda.Powertools.JMESPath.Values; + +internal readonly struct TrueValue : IValue +{ + public JmesPathType Type => JmesPathType.True; + + public IValue this[int index] => throw new InvalidOperationException(); + + public int GetArrayLength() + { + throw new InvalidOperationException(); + } + + public string GetString() + { + throw new InvalidOperationException(); + } + + public bool TryGetDecimal(out decimal value) + { + throw new InvalidOperationException(); + } + + public bool TryGetDouble(out double value) + { + throw new InvalidOperationException(); + } + + public bool TryGetProperty(string propertyName, out IValue property) + { + throw new InvalidOperationException(); + } + + public IArrayValueEnumerator EnumerateArray() + { + throw new InvalidOperationException(); + } + + public IObjectValueEnumerator EnumerateObject() + { + throw new InvalidOperationException(); + } + + public IExpression GetExpression() + { + throw new InvalidOperationException("Not an expression"); + } + + public override string ToString() + { + return "true"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/ValueComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueComparer.cs similarity index 99% rename from libraries/src/AWS.Lambda.Powertools.JMESPath/ValueComparer.cs rename to libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueComparer.cs index fb1b8c48..20738000 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/ValueComparer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueComparer.cs @@ -17,7 +17,7 @@ using System.Collections.Generic; using System.Linq; -namespace AWS.Lambda.Powertools.JMESPath +namespace AWS.Lambda.Powertools.JMESPath.Values { /// /// Compares two instances. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/ValueEqualityComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueEqualityComparer.cs similarity index 99% rename from libraries/src/AWS.Lambda.Powertools.JMESPath/ValueEqualityComparer.cs rename to libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueEqualityComparer.cs index ef04d261..bdc83197 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/ValueEqualityComparer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueEqualityComparer.cs @@ -17,7 +17,7 @@ using System.Collections.Generic; using System.Linq; -namespace AWS.Lambda.Powertools.JMESPath +namespace AWS.Lambda.Powertools.JMESPath.Values { internal sealed class ValueEqualityComparer : IEqualityComparer { From 83560afa4501894ca52be66bbac948c7cb5dbbbb Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Mon, 22 Apr 2024 13:15:38 +0100 Subject: [PATCH 23/28] Add comments to code. Refactor Expressions and Operators --- .../BinaryOperator.cs | 326 -------- .../Expression.cs | 757 ------------------ .../Expressions/BaseExpression.cs | 55 ++ .../Expressions/CurrentNode.cs | 43 + .../Expressions/Expression.cs | 178 ++++ .../Expressions/FilterExpression.cs | 76 ++ .../Expressions/FlattenProjection.cs | 84 ++ .../Expressions/FunctionExpression.cs | 54 ++ .../Expressions/IExpression.cs | 55 ++ .../Expressions/IdentifierSelector.cs | 54 ++ .../Expressions/IndexSelector.cs | 66 ++ .../Expressions/JsonConstants.cs | 35 + .../Expressions/KeyExpressionPair.cs | 31 + .../Expressions/ListProjection.cs | 66 ++ .../Expressions/MultiSelectHash.cs | 66 ++ .../Expressions/MultiSelectList.cs | 66 ++ .../Expressions/ObjectProjection.cs | 63 ++ .../Expressions/Projection.cs | 74 ++ .../Expressions/SliceProjection.cs | 114 +++ .../Functions/AbsFunction.cs | 5 + .../Functions/AvgFunction.cs | 5 + .../Functions/Base64Function.cs | 4 + .../Functions/Base64GzipFunction.cs | 4 + .../Functions/BaseFunction.cs | 5 + .../Functions/BuiltInFunctions.cs | 3 + .../Functions/CeilFunction.cs | 5 + .../Functions/ContainsFunction.cs | 5 + .../Functions/EndsWithFunction.cs | 4 + .../Functions/EvaluateMinMax.cs | 12 + .../Functions/EvaluateMinMaxBy.cs | 13 + .../Functions/EvaluateStartEndWith.cs | 13 + .../Functions/FloorFunction.cs | 5 + .../Functions/IFunction.cs | 10 + .../Functions/JoinFunction.cs | 5 + .../Functions/JsonFunction.cs | 4 + .../Functions/KeysFunction.cs | 5 + .../Functions/LengthFunction.cs | 5 + .../Functions/MapFunction.cs | 5 + .../Functions/MaxByFunction.cs | 5 + .../Functions/MaxFunction.cs | 5 + .../Functions/MergeFunction.cs | 5 + .../Functions/MinByFunction.cs | 5 + .../Functions/MinFunction.cs | 5 + .../Functions/NotNullFunction.cs | 5 + .../Functions/ReverseFunction.cs | 5 + .../Functions/SortByComparer.cs | 4 + .../Functions/SortByFunction.cs | 5 + .../Functions/SortFunction.cs | 5 + .../Functions/StartsWithFunction.cs | 4 + .../Functions/SumFunction.cs | 5 + .../Functions/ToArrayFunction.cs | 4 + .../Functions/ToNumberFunction.cs | 5 + .../Functions/ToStringFunction.cs | 5 + .../Functions/TypeFunction.cs | 5 + .../Functions/ValuesFunction.cs | 5 + .../JmesPathParseException.cs | 51 ++ .../JmesPathParser.cs | 108 +-- .../JmesPathState.cs | 74 ++ .../JsonTransformer.cs | 1 + .../Operator.cs | 2 +- .../Operators/AndOperator.cs | 47 ++ .../Operators/BinaryOperator.cs | 40 + .../Operators/EqOperator.cs | 48 ++ .../Operators/GtOperator.cs | 73 ++ .../Operators/GteOperator.cs | 73 ++ .../Operators/IBinaryOperator.cs | 39 + .../Operators/IUnaryOperator.cs | 42 + .../Operators/LtOperator.cs | 73 ++ .../Operators/LteOperator.cs | 74 ++ .../Operators/NeOperator.cs | 53 ++ .../Operators/NotOperator.cs | 46 ++ .../Operators/OrOperator.cs | 52 ++ .../Operators/RegexOperator.cs | 32 + .../Operators/UnaryOperator.cs | 41 + .../AWS.Lambda.Powertools.JMESPath/Slice.cs | 22 + .../AWS.Lambda.Powertools.JMESPath/Token.cs | 48 ++ .../UnaryOperator.cs | 90 --- .../Values/ArrayValue.cs | 50 ++ .../Values/DecimalValue.cs | 23 + .../Values/DoubleValue.cs | 23 +- .../Values/ExpressionValue.cs | 17 + .../Values/FalseValue.cs | 14 + .../Values/IArrayValueEnumerator.cs | 3 + .../Values/IObjectValueEnumerator.cs | 3 + .../Values/IValue.cs | 54 ++ .../Values/JmesPathType.cs | 3 + .../Values/JsonElementValue.cs | 26 + .../Values/NameValuePair.cs | 3 + .../Values/NullValue.cs | 14 + .../Values/ObjectValue.cs | 28 +- .../Values/StringValue.cs | 17 + .../Values/TrueValue.cs | 14 + .../Values/ValueEqualityComparer.cs | 12 +- 93 files changed, 2629 insertions(+), 1266 deletions(-) delete mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs delete mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/BaseExpression.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/CurrentNode.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Expression.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FilterExpression.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FlattenProjection.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FunctionExpression.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IExpression.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IdentifierSelector.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IndexSelector.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/JsonConstants.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/KeyExpressionPair.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ListProjection.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectHash.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectList.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ObjectProjection.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Projection.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/SliceProjection.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParseException.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathState.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/AndOperator.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/BinaryOperator.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/EqOperator.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GtOperator.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GteOperator.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IBinaryOperator.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IUnaryOperator.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LtOperator.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LteOperator.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NeOperator.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NotOperator.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/OrOperator.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/RegexOperator.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/UnaryOperator.cs delete mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs deleted file mode 100644 index 9e01cffa..00000000 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/BinaryOperator.cs +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -using AWS.Lambda.Powertools.JMESPath.Values; - -namespace AWS.Lambda.Powertools.JMESPath -{ - internal interface IBinaryOperator - { - int PrecedenceLevel {get;} - bool IsRightAssociative {get;} - bool TryEvaluate(IValue lhs, IValue rhs, out IValue result); - } - - internal abstract class BinaryOperator : IBinaryOperator - { - private protected BinaryOperator(Operator oper) - { - PrecedenceLevel = OperatorTable.PrecedenceLevel(oper); - } - - public int PrecedenceLevel {get;} - - public bool IsRightAssociative => false; - - public abstract bool TryEvaluate(IValue lhs, IValue rhs, out IValue result); - } - - internal sealed class OrOperator : BinaryOperator - { - internal static OrOperator Instance { get; } = new(); - - private OrOperator() - : base(Operator.Or) - { - } - - public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) - { - if (lhs.Type == JmesPathType.Null && rhs.Type == JmesPathType.Null) - { - result = lhs; - return true; - } - result = Expression.IsTrue(lhs) ? lhs : rhs; - return true; - } - - public override string ToString() - { - return "OrOperator"; - } - } - - internal sealed class AndOperator : BinaryOperator - { - internal static AndOperator Instance { get; } = new(); - - private AndOperator() - : base(Operator.And) - { - } - - public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) - { - result = Expression.IsTrue(lhs) ? rhs : lhs; - return true; - } - - public override string ToString() - { - return "AndOperator"; - } - } - - internal sealed class EqOperator : BinaryOperator - { - internal static EqOperator Instance { get; } = new(); - - private EqOperator() - : base(Operator.Eq) - { - } - - public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) - { - var comparer = ValueEqualityComparer.Instance; - result = comparer.Equals(lhs, rhs) ? JsonConstants.True : JsonConstants.False; - return true; - } - - public override string ToString() - { - return "EqOperator"; - } - } - - internal sealed class NeOperator : BinaryOperator - { - internal static NeOperator Instance { get; } = new(); - - private NeOperator() - : base(Operator.Ne) - { - } - - public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) - { - if (!EqOperator.Instance.TryEvaluate(lhs, rhs, out var value)) - { - result = JsonConstants.Null; - return false; - } - - result = Expression.IsFalse(value) ? JsonConstants.True : JsonConstants.False; - return true; - } - - public override string ToString() - { - return "NeOperator"; - } - } - - internal sealed class LtOperator : BinaryOperator - { - internal static LtOperator Instance { get; } = new(); - - private LtOperator() - : base(Operator.Lt) - { - } - - public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) - { - switch (lhs.Type) - { - case JmesPathType.Number when rhs.Type == JmesPathType.Number: - { - if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) - { - result = dec1 < dec2 ? JsonConstants.True : JsonConstants.False; - } - else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) - { - result = val1 < val2 ? JsonConstants.True : JsonConstants.False; - } - else - { - result = JsonConstants.Null; - } - - break; - } - case JmesPathType.String when rhs.Type == JmesPathType.String: - result = string.CompareOrdinal(lhs.GetString(), rhs.GetString()) < 0 ? JsonConstants.True : JsonConstants.False; - break; - default: - result = JsonConstants.Null; - break; - } - - return true; - } - - public override string ToString() - { - return "LtOperator"; - } - } - - internal sealed class LteOperator : BinaryOperator - { - internal static LteOperator Instance { get; } = new(); - - private LteOperator() - : base(Operator.Lte) - { - } - - public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) - { - switch (lhs.Type) - { - case JmesPathType.Number when rhs.Type == JmesPathType.Number: - { - if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) - { - result = dec1 <= dec2 ? JsonConstants.True : JsonConstants.False; - } - else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) - { - result = val1 <= val2 ? JsonConstants.True : JsonConstants.False; - } - else - { - result = JsonConstants.Null; - } - - break; - } - case JmesPathType.String when rhs.Type == JmesPathType.String: - result = string.CompareOrdinal(lhs.GetString(), rhs.GetString()) <= 0 ? JsonConstants.True : JsonConstants.False; - break; - default: - result = JsonConstants.Null; - break; - } - - return true; - } - - - public override string ToString() - { - return "LteOperator"; - } - } - - internal sealed class GtOperator : BinaryOperator - { - internal static GtOperator Instance { get; } = new(); - - private GtOperator() - : base(Operator.Gt) - { - } - - public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) - { - switch (lhs.Type) - { - case JmesPathType.Number when rhs.Type == JmesPathType.Number: - { - if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) - { - result = dec1 > dec2 ? JsonConstants.True : JsonConstants.False; - } - else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) - { - result = val1 > val2 ? JsonConstants.True : JsonConstants.False; - } - else - { - result = JsonConstants.Null; - } - - break; - } - case JmesPathType.String when rhs.Type == JmesPathType.String: - result = string.CompareOrdinal(lhs.GetString(), rhs.GetString()) > 0 ? JsonConstants.True : JsonConstants.False; - break; - default: - result = JsonConstants.Null; - break; - } - - return true; - } - - public override string ToString() - { - return "GtOperator"; - } - } - - internal sealed class GteOperator : BinaryOperator - { - internal static GteOperator Instance { get; } = new(); - - private GteOperator() - : base(Operator.Gte) - { - } - - public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) - { - switch (lhs.Type) - { - case JmesPathType.Number when rhs.Type == JmesPathType.Number: - { - if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) - { - result = dec1 >= dec2 ? JsonConstants.True : JsonConstants.False; - } - else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) - { - result = val1 >= val2 ? JsonConstants.True : JsonConstants.False; - } - else - { - result = JsonConstants.Null; - } - - break; - } - case JmesPathType.String when rhs.Type == JmesPathType.String: - result = string.CompareOrdinal(lhs.GetString(), rhs.GetString()) >= 0 ? JsonConstants.True : JsonConstants.False; - break; - default: - result = JsonConstants.Null; - break; - } - - return true; - } - - public override string ToString() - { - return "GteOperator"; - } - } -} - diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs deleted file mode 100644 index 41b2cd23..00000000 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expression.cs +++ /dev/null @@ -1,757 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using AWS.Lambda.Powertools.JMESPath.Values; - -namespace AWS.Lambda.Powertools.JMESPath -{ - internal static class JsonConstants - { - static JsonConstants() - { - True = new TrueValue(); - False = new FalseValue(); - Null = new NullValue(); - } - - internal static IValue True {get;} - internal static IValue False {get;} - internal static IValue Null {get;} - } - - internal interface IExpression - { - bool TryEvaluate(DynamicResources resources, - IValue current, - out IValue value); - - int PrecedenceLevel {get;} - - bool IsProjection {get;} - - bool IsRightAssociative {get;} - - void AddExpression(IExpression expr); - } - - // BaseExpression - internal abstract class BaseExpression : IExpression - { - public int PrecedenceLevel {get;} - - public bool IsRightAssociative {get;} - - public bool IsProjection {get;} - - private protected BaseExpression(Operator oper, bool isProjection) - { - PrecedenceLevel = OperatorTable.PrecedenceLevel(oper); - IsRightAssociative = OperatorTable.IsRightAssociative(oper); - IsProjection = isProjection; - } - - public abstract bool TryEvaluate(DynamicResources resources, - IValue current, - out IValue value); - - public virtual void AddExpression(IExpression expr) - { - } - - public override string ToString() - { - return "ToString not implemented"; - } - } - - internal sealed class IdentifierSelector : BaseExpression - { - private readonly string _identifier; - - internal IdentifierSelector(string name) - : base(Operator.Default, false) - { - _identifier = name; - } - - public override bool TryEvaluate(DynamicResources resources, - IValue current, - out IValue value) - { - if (current.Type == JmesPathType.Object && current.TryGetProperty(_identifier, out value)) - { - return true; - } - - value = JsonConstants.Null; - return true; - } - - public override string ToString() - { - return $"IdentifierSelector {_identifier}"; - } - } - - internal sealed class CurrentNode : BaseExpression - { - internal CurrentNode() - : base(Operator.Default, false) - { - } - - public override bool TryEvaluate(DynamicResources resources, - IValue current, - out IValue value) - { - value = current; - return true; - } - - public override string ToString() - { - return "CurrentNode"; - } - } - - internal sealed class IndexSelector : BaseExpression - { - private readonly int _index; - internal IndexSelector(int index) - : base(Operator.Default, false) - { - _index = index; - } - - public override bool TryEvaluate(DynamicResources resources, - IValue current, - out IValue value) - { - if (current.Type != JmesPathType.Array) - { - value = JsonConstants.Null; - return true; - } - var slen = current.GetArrayLength(); - if (_index >= 0 && _index < slen) - { - value = current[_index]; - } - else if ((slen + _index) >= 0 && (slen+_index) < slen) - { - var index = slen + _index; - value = current[index]; - } - else - { - value = JsonConstants.Null; - } - return true; - } - - public override string ToString() - { - return $"Index Selector {_index}"; - } - } - - internal abstract class Projection : BaseExpression - { - private readonly List _expressions; - - private protected Projection(Operator oper) - : base(oper, true) - { - _expressions = new List(); - } - - public override void AddExpression(IExpression expr) - { - if (_expressions.Count != 0 && _expressions[_expressions.Count-1].IsProjection && - (expr.PrecedenceLevel > _expressions[_expressions.Count-1].PrecedenceLevel || - (expr.PrecedenceLevel == _expressions[_expressions.Count-1].PrecedenceLevel && expr.IsRightAssociative))) - { - _expressions[_expressions.Count-1].AddExpression(expr); - } - else - { - _expressions.Add(expr); - } - } - internal bool TryApplyExpressions(DynamicResources resources, IValue current, out IValue value) - { - value = current; - foreach (var expression in _expressions) - { - if (!expression.TryEvaluate(resources, value, out value)) - { - return false; - } - } - return true; - } - } - - internal sealed class ObjectProjection : Projection - { - internal ObjectProjection() - : base(Operator.Projection) - { - } - - public override bool TryEvaluate(DynamicResources resources, - IValue current, - out IValue value) - { - if (current.Type != JmesPathType.Object) - { - value = JsonConstants.Null; - return true; - } - - var result = new List(); - value = new ArrayValue(result); - foreach (var item in current.EnumerateObject()) - { - if (item.Value.Type == JmesPathType.Null) continue; - if (!TryApplyExpressions(resources, item.Value, out var val)) - { - return false; - } - if (val.Type != JmesPathType.Null) - { - result.Add(val); - } - } - return true; - } - - public override string ToString() - { - return "ObjectProjection"; - } - } - - internal sealed class ListProjection : Projection - { - internal ListProjection() - : base(Operator.Projection) - { - } - - public override bool TryEvaluate(DynamicResources resources, - IValue current, - out IValue value) - { - if (current.Type != JmesPathType.Array) - { - value = JsonConstants.Null; - return true; - } - - var result = new List(); - foreach (var item in current.EnumerateArray()) - { - if (item.Type != JmesPathType.Null) - { - if (!TryApplyExpressions(resources, item, out var val)) - { - value = JsonConstants.Null; - return false; - } - if (val.Type != JmesPathType.Null) - { - result.Add(val); - } - } - } - value = new ArrayValue(result); - return true; - } - - public override string ToString() - { - return "ListProjection"; - } - } - - internal sealed class FlattenProjection : Projection - { - internal FlattenProjection() - : base(Operator.FlattenProjection) - { - } - - public override bool TryEvaluate(DynamicResources resources, - IValue current, - out IValue value) - { - if (current.Type != JmesPathType.Array) - { - value = JsonConstants.Null; - return true; - } - - var result = new List(); - foreach (var item in current.EnumerateArray()) - { - if (item.Type == JmesPathType.Array) - { - foreach (var elem in item.EnumerateArray()) - { - if (elem.Type == JmesPathType.Null) continue; - if (!TryApplyExpressions(resources, elem, out var val)) - { - value = JsonConstants.Null; - return false; - } - if (val.Type != JmesPathType.Null) - { - result.Add(val); - } - } - } - else - { - if (item.Type == JmesPathType.Null) continue; - if (!TryApplyExpressions(resources, item, out var val)) - { - value = JsonConstants.Null; - return false; - } - if (val.Type != JmesPathType.Null) - { - result.Add(val); - } - } - } - - value = new ArrayValue(result); - return true; - } - - public override string ToString() - { - return "FlattenProjection"; - } - } - - internal sealed class SliceProjection : Projection - { - private readonly Slice _slice; - - internal SliceProjection(Slice s) - : base(Operator.Projection) - { - _slice = s; - } - - public override bool TryEvaluate(DynamicResources resources, - IValue current, - out IValue value) - { - if (current.Type != JmesPathType.Array) - { - value = JsonConstants.Null; - return true; - } - - var start = _slice.GetStart(current.GetArrayLength()); - var end = _slice.GetStop(current.GetArrayLength()); - var step = _slice.Step; - - if (step == 0) - { - value = JsonConstants.Null; - return false; - } - - var result = new List(); - if (step > 0) - { - if (start < 0) - { - start = 0; - } - if (end > current.GetArrayLength()) - { - end = current.GetArrayLength(); - } - for (var i = start; i < end; i += step) - { - if (!TryApplyExpressions(resources, current[i], out var val)) - { - value = JsonConstants.Null; - return false; - } - if (val.Type != JmesPathType.Null) - { - result.Add(val); - } - } - } - else - { - if (start >= current.GetArrayLength()) - { - start = current.GetArrayLength() - 1; - } - if (end < -1) - { - end = -1; - } - for (var i = start; i > end; i += step) - { - if (!TryApplyExpressions(resources, current[i], out var val)) - { - value = JsonConstants.Null; - return false; - } - if (val.Type != JmesPathType.Null) - { - result.Add(val); - } - } - } - - value = new ArrayValue(result); - return true; - } - - public override string ToString() - { - return "SliceProjection"; - } - } - - internal sealed class FilterExpression : Projection - { - private readonly Expression _expr; - - internal FilterExpression(Expression expr) - : base(Operator.Projection) - { - _expr = expr; - } - - public override bool TryEvaluate(DynamicResources resources, - IValue current, - out IValue value) - { - if (current.Type != JmesPathType.Array) - { - value = JsonConstants.Null; - return true; - } - var result = new List(); - - foreach (var item in current.EnumerateArray()) - { - if (!_expr.TryEvaluate(resources, item, out var test)) - { - value = JsonConstants.Null; - return false; - } - - if (!Expression.IsTrue(test)) continue; - if (!TryApplyExpressions(resources, item, out var val)) - { - value = JsonConstants.Null; - return false; - } - if (val.Type != JmesPathType.Null) - { - result.Add(val); - } - } - value = new ArrayValue(result); - return true; - } - - public override string ToString() - { - return "FilterExpression"; - } - } - - internal sealed class MultiSelectList : BaseExpression - { - private readonly IList _expressions; - - internal MultiSelectList(IList expressions) - : base(Operator.Default, false) - { - _expressions = expressions; - } - - public override bool TryEvaluate(DynamicResources resources, - IValue current, - out IValue value) - { - if (current.Type == JmesPathType.Null) - { - value = JsonConstants.Null; - return true; - } - var result = new List(); - - foreach (var expr in _expressions) - { - if (!expr.TryEvaluate(resources, current, out var val)) - { - value = JsonConstants.Null; - return false; - } - result.Add(val); - } - value = new ArrayValue(result); - return true; - } - - public override string ToString() - { - return "MultiSelectList"; - } - } - - internal struct KeyExpressionPair - { - internal string Key {get;} - internal Expression Expression {get;} - - internal KeyExpressionPair(string key, Expression expression) - { - Key = key; - Expression = expression; - } - } - - internal sealed class MultiSelectHash : BaseExpression - { - private readonly IList _keyExprPairs; - - internal MultiSelectHash(IList keyExprPairs) - : base(Operator.Default, false) - { - _keyExprPairs = keyExprPairs; - } - - public override bool TryEvaluate(DynamicResources resources, - IValue current, - out IValue value) - { - if (current.Type == JmesPathType.Null) - { - value = JsonConstants.Null; - return true; - } - var result = new Dictionary(); - foreach (var item in _keyExprPairs) - { - if (!item.Expression.TryEvaluate(resources, current, out var val)) - { - value = JsonConstants.Null; - return false; - } - result.Add(item.Key, val); - } - - value = new ObjectValue(result); - return true; - } - - public override string ToString() - { - return "MultiSelectHash"; - } - } - - internal sealed class FunctionExpression : BaseExpression - { - private readonly Expression _expr; - - internal FunctionExpression(Expression expr) - : base(Operator.Default, false) - { - _expr = expr; - } - - public override bool TryEvaluate(DynamicResources resources, - IValue current, - out IValue value) - { - if (!_expr.TryEvaluate(resources, current, out var val)) - { - value = JsonConstants.Null; - return true; - } - value = val; - return true; - } - - public override string ToString() - { - return "FunctionExpression"; - } - } - - internal class Expression - { - private readonly Token[] _tokens; - - internal Expression(Token[] tokens) - { - _tokens = tokens; - } - - public bool TryEvaluate(DynamicResources resources, - IValue current, - out IValue result) - { - var stack = new Stack(); - IList argStack = new List(); - - var rootPtr = current; - - for (var i = _tokens.Length-1; i >= 0; --i) - { - var token = _tokens[i]; - switch (token.Type) - { - case TokenType.Literal: - { - stack.Push(token.GetValue()); - break; - } - case TokenType.BeginExpressionType: - { - Debug.Assert(i>0); - token = _tokens[--i]; - Debug.Assert(token.Type == TokenType.Expression); - Debug.Assert(stack.Count != 0); - stack.Pop(); - stack.Push(new ExpressionValue(token.GetExpression())); - break; - } - case TokenType.Pipe: - { - Debug.Assert(stack.Count != 0); - rootPtr = stack.Peek(); - break; - } - case TokenType.CurrentNode: - stack.Push(rootPtr); - break; - case TokenType.Expression: - { - Debug.Assert(stack.Count != 0); - var ptr = stack.Pop(); - if (!token.GetExpression().TryEvaluate(resources, ptr, out var val)) - { - result = JsonConstants.Null; - return false; - } - stack.Push(val); - break; - } - case TokenType.UnaryOperator: - { - Debug.Assert(stack.Count >= 1); - var rhs = stack.Pop(); - if (!token.GetUnaryOperator().TryEvaluate(rhs, out var val)) - { - result = JsonConstants.Null; - return false; - } - stack.Push(val); - break; - } - case TokenType.BinaryOperator: - { - Debug.Assert(stack.Count >= 2); - var rhs = stack.Pop(); - var lhs = stack.Pop(); - if (!token.GetBinaryOperator().TryEvaluate(lhs, rhs, out var val)) - { - result = JsonConstants.Null; - return false; - } - stack.Push(val); - break; - } - case TokenType.Argument: - { - Debug.Assert(stack.Count != 0); - argStack.Add(stack.Pop()); - break; - } - case TokenType.Function: - { - if (token.GetFunction().Arity != null && token.GetFunction().Arity != argStack.Count()) - { - // airty error should never happen here - result = JsonConstants.Null; - return false; - } - - if (!token.GetFunction().TryEvaluate(resources, argStack, out var val)) - { - result = JsonConstants.Null; - return false; - } - argStack.Clear(); - stack.Push(val); - break; - } - default: - break; - } - } - Debug.Assert(stack.Count == 1); - result = stack.Peek(); - return true; - } - - internal static bool IsFalse(IValue val) - { - switch (val.Type) - { - case JmesPathType.False: - return true; - case JmesPathType.Null: - return true; - case JmesPathType.Array: - return val.GetArrayLength() == 0; - case JmesPathType.Object: - return val.EnumerateObject().MoveNext() == false; - case JmesPathType.String: - return val.GetString().Length == 0; - case JmesPathType.Number: - return false; - default: - return false; - } - } - - internal static bool IsTrue(IValue val) - { - return !IsFalse(val); - } - } -} - diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/BaseExpression.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/BaseExpression.cs new file mode 100644 index 00000000..925f8c97 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/BaseExpression.cs @@ -0,0 +1,55 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Expressions; + +/// +/// Base class for all expressions. +/// +internal abstract class BaseExpression : IExpression +{ + /// + public int PrecedenceLevel {get;} + + /// + public bool IsRightAssociative {get;} + + /// + public bool IsProjection {get;} + + private protected BaseExpression(Operator oper, bool isProjection) + { + PrecedenceLevel = OperatorTable.PrecedenceLevel(oper); + IsRightAssociative = OperatorTable.IsRightAssociative(oper); + IsProjection = isProjection; + } + + /// + public abstract bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value); + + /// + public virtual void AddExpression(IExpression expr) + { + } + + public override string ToString() + { + return "ToString not implemented"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/CurrentNode.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/CurrentNode.cs new file mode 100644 index 00000000..1794d8b4 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/CurrentNode.cs @@ -0,0 +1,43 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Expressions; + +/// +/// Represents the current node. +/// +internal sealed class CurrentNode : BaseExpression +{ + internal CurrentNode() + : base(Operator.Default, false) + { + } + + /// + public override bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value) + { + value = current; + return true; + } + + public override string ToString() + { + return "CurrentNode"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Expression.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Expression.cs new file mode 100644 index 00000000..8ae2d52a --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Expression.cs @@ -0,0 +1,178 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Expressions +{ + // BaseExpression + + /// + /// Represents a JMESPath expression. + /// + internal class Expression + { + /// + /// The tokens in the expression. + /// + private readonly Token[] _tokens; + + internal Expression(Token[] tokens) + { + _tokens = tokens; + } + + /// + /// Evaluates the expression against the given resources. + /// + public bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue result) + { + var stack = new Stack(); + IList argStack = new List(); + + var rootPtr = current; + + for (var i = _tokens.Length-1; i >= 0; --i) + { + var token = _tokens[i]; + switch (token.Type) + { + case TokenType.Literal: + { + stack.Push(token.GetValue()); + break; + } + case TokenType.BeginExpressionType: + { + Debug.Assert(i>0); + token = _tokens[--i]; + Debug.Assert(token.Type == TokenType.Expression); + Debug.Assert(stack.Count != 0); + stack.Pop(); + stack.Push(new ExpressionValue(token.GetExpression())); + break; + } + case TokenType.Pipe: + { + Debug.Assert(stack.Count != 0); + rootPtr = stack.Peek(); + break; + } + case TokenType.CurrentNode: + stack.Push(rootPtr); + break; + case TokenType.Expression: + { + Debug.Assert(stack.Count != 0); + var ptr = stack.Pop(); + if (!token.GetExpression().TryEvaluate(resources, ptr, out var val)) + { + result = JsonConstants.Null; + return false; + } + stack.Push(val); + break; + } + case TokenType.UnaryOperator: + { + Debug.Assert(stack.Count >= 1); + var rhs = stack.Pop(); + if (!token.GetUnaryOperator().TryEvaluate(rhs, out var val)) + { + result = JsonConstants.Null; + return false; + } + stack.Push(val); + break; + } + case TokenType.BinaryOperator: + { + Debug.Assert(stack.Count >= 2); + var rhs = stack.Pop(); + var lhs = stack.Pop(); + if (!token.GetBinaryOperator().TryEvaluate(lhs, rhs, out var val)) + { + result = JsonConstants.Null; + return false; + } + stack.Push(val); + break; + } + case TokenType.Argument: + { + Debug.Assert(stack.Count != 0); + argStack.Add(stack.Pop()); + break; + } + case TokenType.Function: + { + if (token.GetFunction().Arity != null && token.GetFunction().Arity != argStack.Count()) + { + // airty error should never happen here + result = JsonConstants.Null; + return false; + } + + if (!token.GetFunction().TryEvaluate(resources, argStack, out var val)) + { + result = JsonConstants.Null; + return false; + } + argStack.Clear(); + stack.Push(val); + break; + } + default: + break; + } + } + Debug.Assert(stack.Count == 1); + result = stack.Peek(); + return true; + } + + internal static bool IsFalse(IValue val) + { + switch (val.Type) + { + case JmesPathType.False: + return true; + case JmesPathType.Null: + return true; + case JmesPathType.Array: + return val.GetArrayLength() == 0; + case JmesPathType.Object: + return val.EnumerateObject().MoveNext() == false; + case JmesPathType.String: + return val.GetString().Length == 0; + case JmesPathType.Number: + return false; + default: + return false; + } + } + + internal static bool IsTrue(IValue val) + { + return !IsFalse(val); + } + } +} + diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FilterExpression.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FilterExpression.cs new file mode 100644 index 00000000..38d0e4ed --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FilterExpression.cs @@ -0,0 +1,76 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Collections.Generic; +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Expressions; + +/// +/// Represents a filter expression. +/// +internal sealed class FilterExpression : Projection +{ + /// + /// The expression to evaluate. + /// + private readonly Expression _expr; + + internal FilterExpression(Expression expr) + : base(Operator.Projection) + { + _expr = expr; + } + + /// + public override bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value) + { + if (current.Type != JmesPathType.Array) + { + value = JsonConstants.Null; + return true; + } + var result = new List(); + + foreach (var item in current.EnumerateArray()) + { + if (!_expr.TryEvaluate(resources, item, out var test)) + { + value = JsonConstants.Null; + return false; + } + + if (!Expression.IsTrue(test)) continue; + if (!TryApplyExpressions(resources, item, out var val)) + { + value = JsonConstants.Null; + return false; + } + if (val.Type != JmesPathType.Null) + { + result.Add(val); + } + } + value = new ArrayValue(result); + return true; + } + + public override string ToString() + { + return "FilterExpression"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FlattenProjection.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FlattenProjection.cs new file mode 100644 index 00000000..d3946321 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FlattenProjection.cs @@ -0,0 +1,84 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Collections.Generic; +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Expressions; + +/// +/// Represents the flatten projection operator. +/// +internal sealed class FlattenProjection : Projection +{ + internal FlattenProjection() + : base(Operator.FlattenProjection) + { + } + + /// + public override bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value) + { + if (current.Type != JmesPathType.Array) + { + value = JsonConstants.Null; + return true; + } + + var result = new List(); + foreach (var item in current.EnumerateArray()) + { + if (item.Type == JmesPathType.Array) + { + foreach (var elem in item.EnumerateArray()) + { + if (elem.Type == JmesPathType.Null) continue; + if (!TryApplyExpressions(resources, elem, out var val)) + { + value = JsonConstants.Null; + return false; + } + if (val.Type != JmesPathType.Null) + { + result.Add(val); + } + } + } + else + { + if (item.Type == JmesPathType.Null) continue; + if (!TryApplyExpressions(resources, item, out var val)) + { + value = JsonConstants.Null; + return false; + } + if (val.Type != JmesPathType.Null) + { + result.Add(val); + } + } + } + + value = new ArrayValue(result); + return true; + } + + public override string ToString() + { + return "FlattenProjection"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FunctionExpression.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FunctionExpression.cs new file mode 100644 index 00000000..e5630582 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FunctionExpression.cs @@ -0,0 +1,54 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Expressions; + +/// +/// Represents a function expression. +/// +internal sealed class FunctionExpression : BaseExpression +{ + /// + /// The expression to evaluate. + /// + private readonly Expression _expr; + + internal FunctionExpression(Expression expr) + : base(Operator.Default, false) + { + _expr = expr; + } + + /// + public override bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value) + { + if (!_expr.TryEvaluate(resources, current, out var val)) + { + value = JsonConstants.Null; + return true; + } + value = val; + return true; + } + + public override string ToString() + { + return "FunctionExpression"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IExpression.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IExpression.cs new file mode 100644 index 00000000..1e083366 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IExpression.cs @@ -0,0 +1,55 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Expressions; + +/// +/// Represents a JMESPath expression. +/// +internal interface IExpression +{ + /// + /// Evaluates the expression against the provided resources. + /// + /// + /// + /// + /// + bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value); + + /// + /// The precedence level of the expression. + /// + int PrecedenceLevel {get;} + + /// + /// True if the expression is a projection, false otherwise. + /// + bool IsProjection {get;} + + /// + /// True if the expression is right-associative, false otherwise. + /// + bool IsRightAssociative {get;} + + /// + /// Adds an expression to the expression. + /// + void AddExpression(IExpression expr); +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IdentifierSelector.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IdentifierSelector.cs new file mode 100644 index 00000000..1f64ee00 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IdentifierSelector.cs @@ -0,0 +1,54 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Expressions; + +/// +/// Represents a JMESPath identifier expression. +/// +internal sealed class IdentifierSelector : BaseExpression +{ + /// + /// The identifier to select. + /// + private readonly string _identifier; + + internal IdentifierSelector(string name) + : base(Operator.Default, false) + { + _identifier = name; + } + + /// + public override bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value) + { + if (current.Type == JmesPathType.Object && current.TryGetProperty(_identifier, out value)) + { + return true; + } + + value = JsonConstants.Null; + return true; + } + + public override string ToString() + { + return $"IdentifierSelector {_identifier}"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IndexSelector.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IndexSelector.cs new file mode 100644 index 00000000..0a21b4f6 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IndexSelector.cs @@ -0,0 +1,66 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Expressions; + +/// +/// Represents a single index selector. +/// +internal sealed class IndexSelector : BaseExpression +{ + /// + /// The index of the selector. + /// + private readonly int _index; + internal IndexSelector(int index) + : base(Operator.Default, false) + { + _index = index; + } + + /// + public override bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value) + { + if (current.Type != JmesPathType.Array) + { + value = JsonConstants.Null; + return true; + } + var slen = current.GetArrayLength(); + if (_index >= 0 && _index < slen) + { + value = current[_index]; + } + else if ((slen + _index) >= 0 && (slen+_index) < slen) + { + var index = slen + _index; + value = current[index]; + } + else + { + value = JsonConstants.Null; + } + return true; + } + + public override string ToString() + { + return $"Index Selector {_index}"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/JsonConstants.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/JsonConstants.cs new file mode 100644 index 00000000..71f6a394 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/JsonConstants.cs @@ -0,0 +1,35 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Expressions; + +/// +/// Constants used by the JMESPath parser. +/// +internal static class JsonConstants +{ + static JsonConstants() + { + True = new TrueValue(); + False = new FalseValue(); + Null = new NullValue(); + } + + internal static IValue True {get;} + internal static IValue False {get;} + internal static IValue Null {get;} +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/KeyExpressionPair.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/KeyExpressionPair.cs new file mode 100644 index 00000000..32d21edc --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/KeyExpressionPair.cs @@ -0,0 +1,31 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +namespace AWS.Lambda.Powertools.JMESPath.Expressions; + +/// +/// A pair of a JMESPath key and an expression. +/// +internal struct KeyExpressionPair +{ + internal string Key {get;} + internal Expression Expression {get;} + + internal KeyExpressionPair(string key, Expression expression) + { + Key = key; + Expression = expression; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ListProjection.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ListProjection.cs new file mode 100644 index 00000000..a36e6292 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ListProjection.cs @@ -0,0 +1,66 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Collections.Generic; +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Expressions; + +/// +/// Represents a projection that returns a list of values. +/// +internal sealed class ListProjection : Projection +{ + internal ListProjection() + : base(Operator.Projection) + { + } + + /// + public override bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value) + { + if (current.Type != JmesPathType.Array) + { + value = JsonConstants.Null; + return true; + } + + var result = new List(); + foreach (var item in current.EnumerateArray()) + { + if (item.Type != JmesPathType.Null) + { + if (!TryApplyExpressions(resources, item, out var val)) + { + value = JsonConstants.Null; + return false; + } + if (val.Type != JmesPathType.Null) + { + result.Add(val); + } + } + } + value = new ArrayValue(result); + return true; + } + + public override string ToString() + { + return "ListProjection"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectHash.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectHash.cs new file mode 100644 index 00000000..21404a1c --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectHash.cs @@ -0,0 +1,66 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Collections.Generic; +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Expressions; + +/// +/// Represents a multi-select hash expression. +/// +internal sealed class MultiSelectHash : BaseExpression +{ + /// + /// The list of key expression pairs. + /// + private readonly IList _keyExprPairs; + + internal MultiSelectHash(IList keyExprPairs) + : base(Operator.Default, false) + { + _keyExprPairs = keyExprPairs; + } + + /// + public override bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value) + { + if (current.Type == JmesPathType.Null) + { + value = JsonConstants.Null; + return true; + } + var result = new Dictionary(); + foreach (var item in _keyExprPairs) + { + if (!item.Expression.TryEvaluate(resources, current, out var val)) + { + value = JsonConstants.Null; + return false; + } + result.Add(item.Key, val); + } + + value = new ObjectValue(result); + return true; + } + + public override string ToString() + { + return "MultiSelectHash"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectList.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectList.cs new file mode 100644 index 00000000..0b7cf183 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectList.cs @@ -0,0 +1,66 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Collections.Generic; +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Expressions; + +/// +/// Represents a multi-select list expression. +/// +internal sealed class MultiSelectList : BaseExpression +{ + /// + /// The list of expressions to evaluate. + /// + private readonly IList _expressions; + + internal MultiSelectList(IList expressions) + : base(Operator.Default, false) + { + _expressions = expressions; + } + + /// + public override bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value) + { + if (current.Type == JmesPathType.Null) + { + value = JsonConstants.Null; + return true; + } + var result = new List(); + + foreach (var expr in _expressions) + { + if (!expr.TryEvaluate(resources, current, out var val)) + { + value = JsonConstants.Null; + return false; + } + result.Add(val); + } + value = new ArrayValue(result); + return true; + } + + public override string ToString() + { + return "MultiSelectList"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ObjectProjection.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ObjectProjection.cs new file mode 100644 index 00000000..a6ec0035 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ObjectProjection.cs @@ -0,0 +1,63 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Collections.Generic; +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Expressions; + +/// +/// Represents the projection of an object. +/// +internal sealed class ObjectProjection : Projection +{ + internal ObjectProjection() + : base(Operator.Projection) + { + } + + /// + public override bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value) + { + if (current.Type != JmesPathType.Object) + { + value = JsonConstants.Null; + return true; + } + + var result = new List(); + value = new ArrayValue(result); + foreach (var item in current.EnumerateObject()) + { + if (item.Value.Type == JmesPathType.Null) continue; + if (!TryApplyExpressions(resources, item.Value, out var val)) + { + return false; + } + if (val.Type != JmesPathType.Null) + { + result.Add(val); + } + } + return true; + } + + public override string ToString() + { + return "ObjectProjection"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Projection.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Projection.cs new file mode 100644 index 00000000..28d1ea4e --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Projection.cs @@ -0,0 +1,74 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Collections.Generic; +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Expressions; + +/// +/// Base class for projection expressions. +/// +internal abstract class Projection : BaseExpression +{ + /// + /// List of expressions to be applied to the current value. + /// + private readonly List _expressions; + + private protected Projection(Operator oper) + : base(oper, true) + { + _expressions = new List(); + } + + /// + /// Adds an expression to the list of expressions to be applied to the current value. + /// + /// + public override void AddExpression(IExpression expr) + { + if (_expressions.Count != 0 && _expressions[_expressions.Count-1].IsProjection && + (expr.PrecedenceLevel > _expressions[_expressions.Count-1].PrecedenceLevel || + (expr.PrecedenceLevel == _expressions[_expressions.Count-1].PrecedenceLevel && expr.IsRightAssociative))) + { + _expressions[_expressions.Count-1].AddExpression(expr); + } + else + { + _expressions.Add(expr); + } + } + + /// + /// Tries to apply the list of expressions to the current value. + /// + /// + /// + /// + /// + internal bool TryApplyExpressions(DynamicResources resources, IValue current, out IValue value) + { + value = current; + foreach (var expression in _expressions) + { + if (!expression.TryEvaluate(resources, value, out value)) + { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/SliceProjection.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/SliceProjection.cs new file mode 100644 index 00000000..5c895033 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/SliceProjection.cs @@ -0,0 +1,114 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Collections.Generic; +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Expressions; + +/// +/// Represents a projection of a slice of an array. +/// +internal sealed class SliceProjection : Projection +{ + /// + /// The slice to project. + /// + private readonly Slice _slice; + + internal SliceProjection(Slice s) + : base(Operator.Projection) + { + _slice = s; + } + + /// + public override bool TryEvaluate(DynamicResources resources, + IValue current, + out IValue value) + { + if (current.Type != JmesPathType.Array) + { + value = JsonConstants.Null; + return true; + } + + var start = _slice.GetStart(current.GetArrayLength()); + var end = _slice.GetStop(current.GetArrayLength()); + var step = _slice.Step; + + if (step == 0) + { + value = JsonConstants.Null; + return false; + } + + var result = new List(); + if (step > 0) + { + if (start < 0) + { + start = 0; + } + if (end > current.GetArrayLength()) + { + end = current.GetArrayLength(); + } + for (var i = start; i < end; i += step) + { + if (!TryApplyExpressions(resources, current[i], out var val)) + { + value = JsonConstants.Null; + return false; + } + if (val.Type != JmesPathType.Null) + { + result.Add(val); + } + } + } + else + { + if (start >= current.GetArrayLength()) + { + start = current.GetArrayLength() - 1; + } + if (end < -1) + { + end = -1; + } + for (var i = start; i > end; i += step) + { + if (!TryApplyExpressions(resources, current[i], out var val)) + { + value = JsonConstants.Null; + return false; + } + if (val.Type != JmesPathType.Null) + { + result.Add(val); + } + } + } + + value = new ArrayValue(result); + return true; + } + + public override string ToString() + { + return "SliceProjection"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs index 08dafa02..ef58d85b 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs @@ -15,10 +15,14 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns the absolute value of a number. +/// internal sealed class AbsFunction : BaseFunction { internal AbsFunction() @@ -26,6 +30,7 @@ internal AbsFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs index 0ee6e5b3..70c5b3ee 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs @@ -15,10 +15,14 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns the average of the values +/// internal sealed class AvgFunction : BaseFunction { internal AvgFunction() @@ -26,6 +30,7 @@ internal AvgFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64Function.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64Function.cs index 8902aadf..9eab21ff 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64Function.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64Function.cs @@ -21,6 +21,9 @@ namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns the Base64 encoded value of a string. powertools_base64 +/// internal sealed class Base64Function : BaseFunction { /// @@ -34,6 +37,7 @@ public override string ToString() return "powertools_base64"; } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64GzipFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64GzipFunction.cs index eacec5a3..e3f27b96 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64GzipFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64GzipFunction.cs @@ -24,6 +24,9 @@ namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Decodes a base64-encoded, gzip-compressed string into a JSON document. +/// internal sealed class Base64GzipFunction : BaseFunction { /// @@ -37,6 +40,7 @@ public override string ToString() return "powertools_base64_gzip"; } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs index f3925a2c..987c88ce 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs @@ -18,6 +18,9 @@ namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Base class for JMESPath functions. +/// internal abstract class BaseFunction : IFunction { private protected BaseFunction(int? argCount) @@ -25,7 +28,9 @@ private protected BaseFunction(int? argCount) Arity = argCount; } + /// public int? Arity { get; } + /// public abstract bool TryEvaluate(DynamicResources resources, IList args, out IValue element); } \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BuiltInFunctions.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BuiltInFunctions.cs index b953e136..5555e0b5 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BuiltInFunctions.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BuiltInFunctions.cs @@ -17,6 +17,9 @@ namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// A registry of built-in functions. +/// internal sealed class BuiltInFunctions { internal static BuiltInFunctions Instance { get; } = new(); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs index 81f45289..cfd55eba 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs @@ -16,10 +16,14 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns the smallest integer greater than or equal to the argument. +/// internal sealed class CeilFunction : BaseFunction { internal CeilFunction() @@ -27,6 +31,7 @@ internal CeilFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs index 01b5e051..8bd8b80c 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs @@ -16,10 +16,14 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns true if the first argument contains the second argument. +/// internal sealed class ContainsFunction : BaseFunction { internal ContainsFunction() @@ -27,6 +31,7 @@ internal ContainsFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs index 25873f32..92ccfdfd 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs @@ -19,6 +19,9 @@ namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns true if the first argument ends with the second argument. +/// internal sealed class EndsWithFunction : BaseFunction { internal EndsWithFunction() @@ -26,6 +29,7 @@ internal EndsWithFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMax.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMax.cs index 233b7cde..d1c9b0f3 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMax.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMax.cs @@ -14,12 +14,24 @@ */ using System.Collections.Generic; +using AWS.Lambda.Powertools.JMESPath.Expressions; +using AWS.Lambda.Powertools.JMESPath.Operators; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Evaluates the min and max functions. +/// internal static class EvaluateMinMax { + /// + /// Evaluates the min and max functions. + /// + /// + /// + /// + /// internal static bool TryEvaluate(IList args, IBinaryOperator binaryOperator, out IValue element) { var arg0 = args[0]; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMaxBy.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMaxBy.cs index 423b6ab4..064521f9 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMaxBy.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMaxBy.cs @@ -14,12 +14,25 @@ */ using System.Collections.Generic; +using AWS.Lambda.Powertools.JMESPath.Expressions; +using AWS.Lambda.Powertools.JMESPath.Operators; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Evaluates the min and max functions by resources. +/// internal static class EvaluateMinMaxBy { + /// + /// Evaluates the min and max function by resources. + /// + /// + /// + /// + /// + /// internal static bool TryEvaluate(DynamicResources resources, IList args, IBinaryOperator binaryOperator, out IValue element) { if (!(args[0].Type == JmesPathType.Array && args[1].Type == JmesPathType.Expression)) diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateStartEndWith.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateStartEndWith.cs index 52e14913..c64b4545 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateStartEndWith.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateStartEndWith.cs @@ -15,12 +15,25 @@ using System; using System.Collections.Generic; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Evaluates true if the first string starts with the second string. +/// Evaluates true if the first string ends with the second string. +/// internal static class EvaluateStartEndWith { + /// + /// Evaluates true if the first string starts with the second string. + /// Evaluates true if the first string ends with the second string. + /// + /// + /// + /// + /// internal static bool TryEvaluate(IList args, out IValue element, Func> method) { var arg0 = args[0]; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs index 2c559328..763a7100 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs @@ -16,10 +16,14 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns the largest integer less than or equal to the argument. +/// internal sealed class FloorFunction : BaseFunction { internal FloorFunction() @@ -27,6 +31,7 @@ internal FloorFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs index 610b02bd..aa7198fc 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs @@ -18,8 +18,18 @@ namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Represents a JMESPath function. +/// internal interface IFunction { + /// + /// The number of arguments the function takes. + /// int? Arity { get; } + + /// + /// Evaluates the function. + /// bool TryEvaluate(DynamicResources resources, IList args, out IValue element); } \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs index 4e62ab84..52df606b 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs @@ -16,10 +16,14 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns a string consisting of a list of strings joined by a separator. +/// internal sealed class JoinFunction : BaseFunction { internal JoinFunction() @@ -27,6 +31,7 @@ internal JoinFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JsonFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JsonFunction.cs index b2d63595..80e4daf9 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JsonFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JsonFunction.cs @@ -19,6 +19,9 @@ namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns the JSON representation of a value. +/// internal sealed class JsonFunction : BaseFunction { /// @@ -32,6 +35,7 @@ public override string ToString() return "powertools_json"; } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs index 0dd72074..75c773ac 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs @@ -15,10 +15,14 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns the keys of the object as an array. +/// internal sealed class KeysFunction : BaseFunction { internal KeysFunction() @@ -26,6 +30,7 @@ internal KeysFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs index a3104b64..7be3c5f9 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs @@ -16,10 +16,14 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns the number of elements in a value. +/// internal sealed class LengthFunction : BaseFunction { internal LengthFunction() @@ -27,6 +31,7 @@ internal LengthFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs index e08680af..5ce795a9 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs @@ -15,10 +15,14 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns a new array with the results of calling a provided function on every element in the calling array. +/// internal sealed class MapFunction : BaseFunction { internal MapFunction() @@ -26,6 +30,7 @@ internal MapFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs index ffe3dcc3..4804bce6 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs @@ -15,10 +15,14 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Operators; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns the maximum value of the expression by resource. +/// internal sealed class MaxByFunction : BaseFunction { internal MaxByFunction() @@ -26,6 +30,7 @@ internal MaxByFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs index 16cde15c..e37e85ef 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs @@ -15,10 +15,14 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Operators; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns the maximum value in a list. +/// internal sealed class MaxFunction : BaseFunction { internal MaxFunction() @@ -26,6 +30,7 @@ internal MaxFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs index 17b60d45..5af18d99 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs @@ -15,10 +15,14 @@ using System.Collections.Generic; using System.Linq; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Merges multiple objects into a single object. +/// internal sealed class MergeFunction : BaseFunction { internal MergeFunction() @@ -26,6 +30,7 @@ internal MergeFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { if (!args.Any()) diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs index ccc8a9b8..08fffc2a 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs @@ -15,10 +15,14 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Operators; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns the element with the minimum value by resource +/// internal sealed class MinByFunction : BaseFunction { internal MinByFunction() @@ -26,6 +30,7 @@ internal MinByFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs index 005bb8bd..c8ae1c20 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs @@ -15,10 +15,14 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Operators; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns the minimum value in a element. +/// internal sealed class MinFunction : BaseFunction { internal MinFunction() @@ -26,6 +30,7 @@ internal MinFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs index 8d2cdcf4..b362da73 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs @@ -14,10 +14,14 @@ */ using System.Collections.Generic; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns the first non-null argument. +/// internal sealed class NotNullFunction : BaseFunction { internal NotNullFunction() @@ -25,6 +29,7 @@ internal NotNullFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { foreach (var arg in args) diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs index 94af36a0..f66f1e90 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs @@ -17,10 +17,14 @@ using System.Diagnostics; using System.Globalization; using System.Linq; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns the elements of the input array in reverse order. +/// internal sealed class ReverseFunction : BaseFunction { internal ReverseFunction() @@ -28,6 +32,7 @@ internal ReverseFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByComparer.cs index af54b9fd..eea78e56 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByComparer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByComparer.cs @@ -14,10 +14,14 @@ */ using System.Collections.Generic; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions { + /// + /// Implements the sort_by function. + /// internal sealed class SortByComparer : IComparer, System.Collections.IComparer { private readonly DynamicResources _resources; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs index 5d9c1481..a4ae4fcf 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs @@ -15,10 +15,14 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns the input array sorted by the value of the expression by resources. +/// internal sealed class SortByFunction : BaseFunction { internal SortByFunction() @@ -26,6 +30,7 @@ internal SortByFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs index af8f092c..2bb5fffd 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs @@ -15,10 +15,14 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns the sorted elements of the input array. +/// internal sealed class SortFunction : BaseFunction { internal SortFunction() @@ -26,6 +30,7 @@ internal SortFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs index 4ba5f87e..0b7d7c74 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs @@ -19,6 +19,9 @@ namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns true if the first argument starts with the second argument. +/// internal sealed class StartsWithFunction : BaseFunction { internal StartsWithFunction() @@ -26,6 +29,7 @@ internal StartsWithFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs index e634a7d5..15d59853 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs @@ -15,10 +15,14 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns the sum of the values in a list. +/// internal sealed class SumFunction : BaseFunction { internal static SumFunction Instance { get; } = new(); @@ -28,6 +32,7 @@ internal SumFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs index 02e6e22b..fa7d7f6a 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs @@ -19,6 +19,9 @@ namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns the input as an array if it is not already an array. +/// internal sealed class ToArrayFunction : BaseFunction { internal ToArrayFunction() @@ -26,6 +29,7 @@ internal ToArrayFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs index 83e5f98f..fb68800d 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs @@ -16,10 +16,14 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Converts a string to a number. +/// internal sealed class ToNumberFunction : BaseFunction { internal ToNumberFunction() @@ -27,6 +31,7 @@ internal ToNumberFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs index d6745944..41c18ddc 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs @@ -15,10 +15,14 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns the string representation of a value. +/// internal sealed class ToStringFunction : BaseFunction { internal ToStringFunction() @@ -26,6 +30,7 @@ internal ToStringFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs index 16ab2b46..24529604 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs @@ -15,10 +15,14 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns the type of the value as a string. +/// internal sealed class TypeFunction : BaseFunction { internal TypeFunction() @@ -26,6 +30,7 @@ internal TypeFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs index 6ddf44dc..60e472c4 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs @@ -15,10 +15,14 @@ using System.Collections.Generic; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath.Functions; +/// +/// Returns the values of an object. +/// internal sealed class ValuesFunction : BaseFunction { internal ValuesFunction() @@ -26,6 +30,7 @@ internal ValuesFunction() { } + /// public override bool TryEvaluate(DynamicResources resources, IList args, out IValue element) { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParseException.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParseException.cs new file mode 100644 index 00000000..b1d5b8d4 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParseException.cs @@ -0,0 +1,51 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; + +namespace AWS.Lambda.Powertools.JMESPath; + +/// +/// Defines a custom exception object that is thrown when JMESPath parsing fails. +/// + +public sealed class JmesPathParseException : Exception +{ + /// + /// The line in the JMESPath string where a parse error was detected. + /// + private int LineNumber {get;} + + /// + /// The column in the JMESPath string where a parse error was detected. + /// + private int ColumnNumber {get;} + + internal JmesPathParseException(string message, int line, int column) + : base(message) + { + LineNumber = line; + ColumnNumber = column; + } + + /// + /// Returns an error message that describes the current exception. + /// + /// A string representation of the current exception. + public override string ToString () + { + return $"{Message} at line {LineNumber} and column {ColumnNumber}"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs index 9b609368..a73a5a02 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs @@ -18,99 +18,16 @@ using System.Diagnostics; using System.Text; using System.Text.Json; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Functions; +using AWS.Lambda.Powertools.JMESPath.Operators; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath { /// - /// Defines a custom exception object that is thrown when JMESPath parsing fails. - /// - - public sealed class JmesPathParseException : Exception - { - /// - /// The line in the JMESPath string where a parse error was detected. - /// - private int LineNumber {get;} - - /// - /// The column in the JMESPath string where a parse error was detected. - /// - private int ColumnNumber {get;} - - internal JmesPathParseException(string message, int line, int column) - : base(message) - { - LineNumber = line; - ColumnNumber = column; - } - - /// - /// Returns an error message that describes the current exception. - /// - /// A string representation of the current exception. - public override string ToString () - { - return $"{Message} at line {LineNumber} and column {ColumnNumber}"; - } - } - - internal enum JmesPathState - { - Start, - LhsExpression, - RhsExpression, - SubExpression, - ExpressionType, - ComparatorExpression, - FunctionExpression, - Argument, - ExpressionOrExpressionType, - QuotedString, - RawString, - RawStringEscapeChar, - QuotedStringEscapeChar, - EscapeU1, - EscapeU2, - EscapeU3, - EscapeU4, - EscapeExpectSurrogatePair1, - EscapeExpectSurrogatePair2, - EscapeU5, - EscapeU6, - EscapeU7, - EscapeU8, - Literal, - KeyExpr, - ValExpr, - IdentifierOrFunctionExpr, - UnquotedString, - KeyValExpr, - Number, - Digit, - IndexOrSliceExpression, - BracketSpecifier, - BracketSpecifierOrMultiSelectList, - Filter, - MultiSelectList, - MultiSelectHash, - RhsSliceExpressionStop, - RhsSliceExpressionStep, - ExpectRightBracket, - ExpectRightParen, - ExpectDot, - ExpectRightBrace, - ExpectColon, - ExpectMultiSelectList, - CmpLtOrLte, - CmpEq, - CmpGtOrGte, - CmpNe, - ExpectPipeOrOr, - ExpectAnd - } - + /// Parses a JMESPath expression and returns a . + /// internal ref struct JmesPathParser { private readonly ReadOnlySpan _span; @@ -132,6 +49,9 @@ internal JmesPathParser(string input) _operatorStack = new Stack(); } + /// + /// Parses a JMESPath expression and returns a . + /// internal JsonTransformer Parse() { _stateStack.Clear(); @@ -1411,7 +1331,7 @@ internal JsonTransformer Parse() return new JsonTransformer(new Expression(a)); } - + private void SkipWhiteSpace() { switch (_span[_index]) @@ -1448,6 +1368,11 @@ private void UnwindRightParen() _operatorStack.Pop(); // TokenType.LeftParen } + /// + /// Pushes a token onto the output stack. + /// + /// + /// private void PushToken(Token token) { switch (token.Type) @@ -1721,6 +1646,13 @@ private void PushToken(Token token) } } + /// + /// Appends the given codepoint to the current codepoint. + /// + /// + /// + /// + /// private uint AppendToCodepoint(uint cp, uint c) { cp *= 16; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathState.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathState.cs new file mode 100644 index 00000000..a8bb4fb3 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathState.cs @@ -0,0 +1,74 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +namespace AWS.Lambda.Powertools.JMESPath; + +/// +/// The state of the JMESPath parser. +/// +internal enum JmesPathState +{ + Start, + LhsExpression, + RhsExpression, + SubExpression, + ExpressionType, + ComparatorExpression, + FunctionExpression, + Argument, + ExpressionOrExpressionType, + QuotedString, + RawString, + RawStringEscapeChar, + QuotedStringEscapeChar, + EscapeU1, + EscapeU2, + EscapeU3, + EscapeU4, + EscapeExpectSurrogatePair1, + EscapeExpectSurrogatePair2, + EscapeU5, + EscapeU6, + EscapeU7, + EscapeU8, + Literal, + KeyExpr, + ValExpr, + IdentifierOrFunctionExpr, + UnquotedString, + KeyValExpr, + Number, + Digit, + IndexOrSliceExpression, + BracketSpecifier, + BracketSpecifierOrMultiSelectList, + Filter, + MultiSelectList, + MultiSelectHash, + RhsSliceExpressionStop, + RhsSliceExpressionStep, + ExpectRightBracket, + ExpectRightParen, + ExpectDot, + ExpectRightBrace, + ExpectColon, + ExpectMultiSelectList, + CmpLtOrLte, + CmpEq, + CmpGtOrGte, + CmpNe, + ExpectPipeOrOr, + ExpectAnd +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs index 80530cc2..79409d3d 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs @@ -15,6 +15,7 @@ using System; using System.Text.Json; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operator.cs index bff60479..4f245603 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operator.cs @@ -30,7 +30,7 @@ internal enum Operator Gte, Not } - + internal static class OperatorTable { internal static int PrecedenceLevel(Operator oper) diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/AndOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/AndOperator.cs new file mode 100644 index 00000000..7535e40b --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/AndOperator.cs @@ -0,0 +1,47 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.JMESPath.Expressions; +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Operators; + +/// +/// Base class for all binary operators that are and. +/// +internal sealed class AndOperator : BinaryOperator +{ + /// + /// Singleton instance of the class. + /// + internal static AndOperator Instance { get; } = new(); + + private AndOperator() + : base(Operator.And) + { + } + + /// + public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) + { + result = Expression.IsTrue(lhs) ? rhs : lhs; + return true; + } + + public override string ToString() + { + return "AndOperator"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/BinaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/BinaryOperator.cs new file mode 100644 index 00000000..3dbd70a4 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/BinaryOperator.cs @@ -0,0 +1,40 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Operators +{ + /// + /// Base class for all binary operators. + /// + internal abstract class BinaryOperator : IBinaryOperator + { + private protected BinaryOperator(Operator oper) + { + PrecedenceLevel = OperatorTable.PrecedenceLevel(oper); + } + + /// + public int PrecedenceLevel {get;} + + /// + public bool IsRightAssociative => false; + + /// + public abstract bool TryEvaluate(IValue lhs, IValue rhs, out IValue result); + } +} + diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/EqOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/EqOperator.cs new file mode 100644 index 00000000..730408f3 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/EqOperator.cs @@ -0,0 +1,48 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.JMESPath.Expressions; +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Operators; + +/// +/// Base class for all binary operators that are equality +/// +internal sealed class EqOperator : BinaryOperator +{ + /// + /// Singleton instance of the + /// + internal static EqOperator Instance { get; } = new(); + + private EqOperator() + : base(Operator.Eq) + { + } + + /// + public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) + { + var comparer = ValueEqualityComparer.Instance; + result = comparer.Equals(lhs, rhs) ? JsonConstants.True : JsonConstants.False; + return true; + } + + public override string ToString() + { + return "EqOperator"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GtOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GtOperator.cs new file mode 100644 index 00000000..86071d41 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GtOperator.cs @@ -0,0 +1,73 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.JMESPath.Expressions; +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Operators; + +/// +/// Base class for all binary operators that are comparison +/// +internal sealed class GtOperator : BinaryOperator +{ + /// + /// Singleton instance of the class + /// + internal static GtOperator Instance { get; } = new(); + + private GtOperator() + : base(Operator.Gt) + { + } + + /// + public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) + { + switch (lhs.Type) + { + case JmesPathType.Number when rhs.Type == JmesPathType.Number: + { + if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) + { + result = dec1 > dec2 ? JsonConstants.True : JsonConstants.False; + } + else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) + { + result = val1 > val2 ? JsonConstants.True : JsonConstants.False; + } + else + { + result = JsonConstants.Null; + } + + break; + } + case JmesPathType.String when rhs.Type == JmesPathType.String: + result = string.CompareOrdinal(lhs.GetString(), rhs.GetString()) > 0 ? JsonConstants.True : JsonConstants.False; + break; + default: + result = JsonConstants.Null; + break; + } + + return true; + } + + public override string ToString() + { + return "GtOperator"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GteOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GteOperator.cs new file mode 100644 index 00000000..e17e2f7e --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GteOperator.cs @@ -0,0 +1,73 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.JMESPath.Expressions; +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Operators; + +/// +/// Base class for all binary operators that are comparison +/// +internal sealed class GteOperator : BinaryOperator +{ + /// + /// Singleton instance of the + /// + internal static GteOperator Instance { get; } = new(); + + private GteOperator() + : base(Operator.Gte) + { + } + + /// + public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) + { + switch (lhs.Type) + { + case JmesPathType.Number when rhs.Type == JmesPathType.Number: + { + if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) + { + result = dec1 >= dec2 ? JsonConstants.True : JsonConstants.False; + } + else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) + { + result = val1 >= val2 ? JsonConstants.True : JsonConstants.False; + } + else + { + result = JsonConstants.Null; + } + + break; + } + case JmesPathType.String when rhs.Type == JmesPathType.String: + result = string.CompareOrdinal(lhs.GetString(), rhs.GetString()) >= 0 ? JsonConstants.True : JsonConstants.False; + break; + default: + result = JsonConstants.Null; + break; + } + + return true; + } + + public override string ToString() + { + return "GteOperator"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IBinaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IBinaryOperator.cs new file mode 100644 index 00000000..f9a08433 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IBinaryOperator.cs @@ -0,0 +1,39 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Operators; + +/// +/// Interface for all binary operators. +/// +internal interface IBinaryOperator +{ + /// + /// The precedence level of the operator. + /// + int PrecedenceLevel {get;} + + /// + /// Whether the operator is right-associative. + /// + bool IsRightAssociative {get;} + + /// + /// Evaluates the expression. + /// + bool TryEvaluate(IValue lhs, IValue rhs, out IValue result); +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IUnaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IUnaryOperator.cs new file mode 100644 index 00000000..ab6da0d4 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IUnaryOperator.cs @@ -0,0 +1,42 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Operators; + +/// +/// Interface for unary operators. +/// +internal interface IUnaryOperator +{ + /// + /// The precedence level of the operator. + /// + int PrecedenceLevel {get;} + + /// + /// Whether the operator is right-associative or not. + /// + bool IsRightAssociative {get;} + + /// + /// Evaluates the expression. + /// + /// + /// + /// + bool TryEvaluate(IValue elem, out IValue result); +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LtOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LtOperator.cs new file mode 100644 index 00000000..64d5f465 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LtOperator.cs @@ -0,0 +1,73 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.JMESPath.Expressions; +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Operators; + +/// +/// Base class for all binary operators that are comparison +/// +internal sealed class LtOperator : BinaryOperator +{ + /// + /// Singleton instance of the + /// + internal static LtOperator Instance { get; } = new(); + + private LtOperator() + : base(Operator.Lt) + { + } + + /// + public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) + { + switch (lhs.Type) + { + case JmesPathType.Number when rhs.Type == JmesPathType.Number: + { + if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) + { + result = dec1 < dec2 ? JsonConstants.True : JsonConstants.False; + } + else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) + { + result = val1 < val2 ? JsonConstants.True : JsonConstants.False; + } + else + { + result = JsonConstants.Null; + } + + break; + } + case JmesPathType.String when rhs.Type == JmesPathType.String: + result = string.CompareOrdinal(lhs.GetString(), rhs.GetString()) < 0 ? JsonConstants.True : JsonConstants.False; + break; + default: + result = JsonConstants.Null; + break; + } + + return true; + } + + public override string ToString() + { + return "LtOperator"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LteOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LteOperator.cs new file mode 100644 index 00000000..72ba49ce --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LteOperator.cs @@ -0,0 +1,74 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.JMESPath.Expressions; +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Operators; + +/// +/// Base class for all binary operators that are comparison +/// +internal sealed class LteOperator : BinaryOperator +{ + /// + /// Singleton instance of the class + /// + internal static LteOperator Instance { get; } = new(); + + private LteOperator() + : base(Operator.Lte) + { + } + + /// + public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) + { + switch (lhs.Type) + { + case JmesPathType.Number when rhs.Type == JmesPathType.Number: + { + if (lhs.TryGetDecimal(out var dec1) && rhs.TryGetDecimal(out var dec2)) + { + result = dec1 <= dec2 ? JsonConstants.True : JsonConstants.False; + } + else if (lhs.TryGetDouble(out var val1) && rhs.TryGetDouble(out var val2)) + { + result = val1 <= val2 ? JsonConstants.True : JsonConstants.False; + } + else + { + result = JsonConstants.Null; + } + + break; + } + case JmesPathType.String when rhs.Type == JmesPathType.String: + result = string.CompareOrdinal(lhs.GetString(), rhs.GetString()) <= 0 ? JsonConstants.True : JsonConstants.False; + break; + default: + result = JsonConstants.Null; + break; + } + + return true; + } + + + public override string ToString() + { + return "LteOperator"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NeOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NeOperator.cs new file mode 100644 index 00000000..7a880047 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NeOperator.cs @@ -0,0 +1,53 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.JMESPath.Expressions; +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Operators; + +/// +/// Base class for all binary operators that are inequality +/// +internal sealed class NeOperator : BinaryOperator +{ + /// + /// Singleton instance of the class + /// + internal static NeOperator Instance { get; } = new(); + + private NeOperator() + : base(Operator.Ne) + { + } + + /// + public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) + { + if (!EqOperator.Instance.TryEvaluate(lhs, rhs, out var value)) + { + result = JsonConstants.Null; + return false; + } + + result = Expression.IsFalse(value) ? JsonConstants.True : JsonConstants.False; + return true; + } + + public override string ToString() + { + return "NeOperator"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NotOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NotOperator.cs new file mode 100644 index 00000000..095c653b --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NotOperator.cs @@ -0,0 +1,46 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.JMESPath.Expressions; +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Operators; + +/// +/// Represents the not operator. +/// +internal sealed class NotOperator : UnaryOperator +{ + /// + /// The singleton instance of the class. + /// + internal static NotOperator Instance { get; } = new(); + + private NotOperator() + : base(Operator.Not) + {} + + /// + public override bool TryEvaluate(IValue elem, out IValue result) + { + result = Expression.IsFalse(elem) ? JsonConstants.True : JsonConstants.False; + return true; + } + + public override string ToString() + { + return "Not"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/OrOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/OrOperator.cs new file mode 100644 index 00000000..0ead0bba --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/OrOperator.cs @@ -0,0 +1,52 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.JMESPath.Expressions; +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Operators; + +/// +/// Base class for all binary operators that are left associative. +/// +internal sealed class OrOperator : BinaryOperator +{ + /// + /// Singleton instance of the class. + /// + internal static OrOperator Instance { get; } = new(); + + private OrOperator() + : base(Operator.Or) + { + } + + /// + public override bool TryEvaluate(IValue lhs, IValue rhs, out IValue result) + { + if (lhs.Type == JmesPathType.Null && rhs.Type == JmesPathType.Null) + { + result = lhs; + return true; + } + result = Expression.IsTrue(lhs) ? lhs : rhs; + return true; + } + + public override string ToString() + { + return "OrOperator"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/RegexOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/RegexOperator.cs new file mode 100644 index 00000000..c6d1251c --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/RegexOperator.cs @@ -0,0 +1,32 @@ +using System.Text.RegularExpressions; +using AWS.Lambda.Powertools.JMESPath.Expressions; +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Operators; + +internal sealed class RegexOperator : UnaryOperator +{ + private readonly Regex _regex; + + internal RegexOperator(Regex regex) + : base(Operator.Not) + { + _regex = regex; + } + + public override bool TryEvaluate(IValue elem, out IValue result) + { + if (elem.Type != JmesPathType.String) + { + result = JsonConstants.Null; + return false; // type error + } + result = _regex.IsMatch(elem.GetString()) ? JsonConstants.True : JsonConstants.False; + return true; + } + + public override string ToString() + { + return "Regex"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/UnaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/UnaryOperator.cs new file mode 100644 index 00000000..daf88f17 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/UnaryOperator.cs @@ -0,0 +1,41 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.JMESPath.Values; + +namespace AWS.Lambda.Powertools.JMESPath.Operators +{ + /// + /// Base class for unary operators. + /// + internal abstract class UnaryOperator : IUnaryOperator + { + private protected UnaryOperator(Operator oper) + { + PrecedenceLevel = OperatorTable.PrecedenceLevel(oper); + IsRightAssociative = OperatorTable.IsRightAssociative(oper); + } + + /// + public int PrecedenceLevel {get;} + + /// + public bool IsRightAssociative {get;} + + /// + public abstract bool TryEvaluate(IValue elem, out IValue result); + } +} + diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Slice.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Slice.cs index 6d5dca86..938f2c01 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Slice.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Slice.cs @@ -15,11 +15,23 @@ namespace AWS.Lambda.Powertools.JMESPath { + /// + /// A slice of a list or string. + /// internal readonly struct Slice { + /// + /// The start of the slice. + /// private readonly int? _start; + /// + /// The stop of the slice. + /// private readonly int? _stop; + /// + /// The step of the slice. + /// public int Step {get;} public Slice(int? start, int? stop, int step) @@ -29,6 +41,11 @@ public Slice(int? start, int? stop, int step) Step = step; } + /// + /// Gets the start of the slice. + /// + /// + /// public int GetStart(int size) { if (!_start.HasValue) return Step >= 0 ? 0 : size; @@ -36,6 +53,11 @@ public int GetStart(int size) return len <= size ? len : size; } + /// + /// Gets the stop of the slice. + /// + /// + /// public int GetStop(int size) { if (!_stop.HasValue) return Step >= 0 ? size : -1; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs index 8a27fb20..d333c69e 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs @@ -15,7 +15,9 @@ using System; using System.Diagnostics; +using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Functions; +using AWS.Lambda.Powertools.JMESPath.Operators; using AWS.Lambda.Powertools.JMESPath.Values; namespace AWS.Lambda.Powertools.JMESPath @@ -47,8 +49,14 @@ internal enum TokenType EndOfExpression } + /// + /// Represents a token in the JMESPath expression. + /// internal readonly struct Token : IEquatable { + /// + /// The expression associated with this token. + /// private readonly object? _expr; internal Token(TokenType type) @@ -95,6 +103,9 @@ internal Token(IValue expr) internal TokenType Type{get;} + /// + /// Return if it is an Operator + /// internal bool IsOperator { get @@ -111,6 +122,9 @@ internal bool IsOperator } } + /// + /// True if the expression is a projection, false otherwise. + /// internal bool IsProjection { get @@ -125,6 +139,9 @@ internal bool IsProjection } } + /// + /// True if the expression is right-associative, false otherwise. + /// internal bool IsRightAssociative { get @@ -143,6 +160,9 @@ internal bool IsRightAssociative } } + /// + /// The precedence level of the operator. + /// internal int PrecedenceLevel { get @@ -161,36 +181,64 @@ internal int PrecedenceLevel } } + /// + /// Returns the token expression key if Type is Key + /// + /// + /// internal string GetKey() { Debug.Assert(Type == TokenType.Key); return _expr as string ?? throw new InvalidOperationException("Key cannot be null"); } + /// + /// Returns the token expression key if Type is UnaryOperator + /// + /// + /// internal IUnaryOperator GetUnaryOperator() { Debug.Assert(Type == TokenType.UnaryOperator); return _expr as IUnaryOperator ?? throw new InvalidOperationException("Unary operator cannot be null"); } + /// + /// Returns the token expression key if Type is BinaryOperator + /// + /// + /// internal IBinaryOperator GetBinaryOperator() { Debug.Assert(Type == TokenType.BinaryOperator); return _expr as IBinaryOperator ?? throw new InvalidOperationException("Binary operator cannot be null"); } + /// + /// Returns the token expression key if Type is Literal + /// + /// + /// internal IValue GetValue() { Debug.Assert(Type == TokenType.Literal); return _expr as IValue ?? throw new InvalidOperationException("Value cannot be null"); } + /// + /// Returns the token expression key if Type is Function + /// + /// + /// internal IFunction GetFunction() { Debug.Assert(Type == TokenType.Function); return _expr as IFunction ?? throw new InvalidOperationException("Function cannot be null"); } + /// + /// Returns the token expression key if Type is Expression + /// internal IExpression GetExpression() { Debug.Assert(Type == TokenType.Expression); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs deleted file mode 100644 index b769a1e1..00000000 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/UnaryOperator.cs +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -using System.Text.RegularExpressions; -using AWS.Lambda.Powertools.JMESPath.Values; - -namespace AWS.Lambda.Powertools.JMESPath -{ - internal interface IUnaryOperator - { - int PrecedenceLevel {get;} - bool IsRightAssociative {get;} - bool TryEvaluate(IValue elem, out IValue result); - } - - internal abstract class UnaryOperator : IUnaryOperator - { - private protected UnaryOperator(Operator oper) - { - PrecedenceLevel = OperatorTable.PrecedenceLevel(oper); - IsRightAssociative = OperatorTable.IsRightAssociative(oper); - } - - public int PrecedenceLevel {get;} - - public bool IsRightAssociative {get;} - - public abstract bool TryEvaluate(IValue elem, out IValue result); - } - - internal sealed class NotOperator : UnaryOperator - { - internal static NotOperator Instance { get; } = new(); - - private NotOperator() - : base(Operator.Not) - {} - - public override bool TryEvaluate(IValue elem, out IValue result) - { - result = Expression.IsFalse(elem) ? JsonConstants.True : JsonConstants.False; - return true; - } - - public override string ToString() - { - return "Not"; - } - } - - internal sealed class RegexOperator : UnaryOperator - { - private readonly Regex _regex; - - internal RegexOperator(Regex regex) - : base(Operator.Not) - { - _regex = regex; - } - - public override bool TryEvaluate(IValue elem, out IValue result) - { - if (elem.Type != JmesPathType.String) - { - result = JsonConstants.Null; - return false; // type error - } - result = _regex.IsMatch(elem.GetString()) ? JsonConstants.True : JsonConstants.False; - return true; - } - - public override string ToString() - { - return "Regex"; - } - } -} - diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ArrayValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ArrayValue.cs index 43008b80..504d86e7 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ArrayValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ArrayValue.cs @@ -16,14 +16,27 @@ using System; using System.Collections.Generic; using System.Text; +using AWS.Lambda.Powertools.JMESPath.Expressions; namespace AWS.Lambda.Powertools.JMESPath.Values; +/// +/// Represents a JMESPath array value. +/// internal readonly struct ArrayValue : IValue { + /// + /// An enumerator for a JMESPath array value. + /// private sealed class ArrayEnumerator : IArrayValueEnumerator { + /// + /// The list of values in the array. + /// private readonly IList _value; + /// + /// The enumerator for the list of values in the array. + /// private readonly System.Collections.IEnumerator _enumerator; public ArrayEnumerator(IList value) @@ -32,41 +45,78 @@ public ArrayEnumerator(IList value) _enumerator = value.GetEnumerator(); } + /// + /// Gets the current value in the array. + /// public bool MoveNext() { return _enumerator.MoveNext(); } + /// + /// Resets the enumerator to the beginning of the array. + /// public void Reset() { _enumerator.Reset(); } void IDisposable.Dispose() {} + /// + /// Gets the current value in the array. + /// + /// public IValue Current => _enumerator.Current as IValue ?? throw new InvalidOperationException("Current cannot be null"); + /// + /// Gets the current value in the array. + /// object System.Collections.IEnumerator.Current => Current; + /// + /// Gets an enumerator for the array. + /// + /// public IEnumerator GetEnumerator() { return _value.GetEnumerator(); } + /// + /// Gets an enumerator for the array. + /// + /// System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } } + /// + /// The list of values in the array. + /// private readonly IList _value; + /// + /// Creates a new array value. + /// internal ArrayValue(IList value) { _value = value; } + /// + /// Gets the type of the value. + /// public JmesPathType Type => JmesPathType.Array; + /// + /// Gets the value at the specified index. + /// public IValue this[int index] => _value[index]; + /// + /// Gets the length of the array. + /// + /// public int GetArrayLength() { return _value.Count; } public string GetString() diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DecimalValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DecimalValue.cs index 3cdf685d..7612befe 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DecimalValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DecimalValue.cs @@ -15,11 +15,18 @@ using System; using System.Text.Json; +using AWS.Lambda.Powertools.JMESPath.Expressions; namespace AWS.Lambda.Powertools.JMESPath.Values; +/// +/// Represents a JMESPath number value. +/// internal readonly struct DecimalValue : IValue { + /// + /// The value of the JMESPath number. + /// private readonly decimal _value; internal DecimalValue(decimal value) @@ -27,47 +34,63 @@ internal DecimalValue(decimal value) _value = value; } + /// + /// The type of the JMESPath value. + /// public JmesPathType Type => JmesPathType.Number; + /// + /// Gets the value at the specified index. + /// + /// + /// public IValue this[int index] => throw new InvalidOperationException(); + /// public int GetArrayLength() { throw new InvalidOperationException(); } + /// public string GetString() { throw new InvalidOperationException(); } + /// public bool TryGetDecimal(out decimal value) { value = _value; return true; } + /// public bool TryGetDouble(out double value) { value = (double)_value; return true; } + /// public bool TryGetProperty(string propertyName, out IValue property) { throw new InvalidOperationException(); } + /// public IArrayValueEnumerator EnumerateArray() { throw new InvalidOperationException(); } + /// public IObjectValueEnumerator EnumerateObject() { throw new InvalidOperationException(); } + /// public IExpression GetExpression() { throw new InvalidOperationException("Not an expression"); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DoubleValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DoubleValue.cs index 56649d55..15966a58 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DoubleValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DoubleValue.cs @@ -15,11 +15,18 @@ using System; using System.Text.Json; +using AWS.Lambda.Powertools.JMESPath.Expressions; namespace AWS.Lambda.Powertools.JMESPath.Values; +/// +/// Represents a double value. +/// internal readonly struct DoubleValue : IValue { + /// + /// The value of this . + /// private readonly double _value; internal DoubleValue(double value) @@ -27,20 +34,29 @@ internal DoubleValue(double value) _value = value; } + /// public JmesPathType Type => JmesPathType.Number; + /// + /// Gets the value at the specified index. + /// + /// + /// public IValue this[int index] => throw new InvalidOperationException(); + /// public int GetArrayLength() { throw new InvalidOperationException(); } - + + /// public string GetString() { throw new InvalidOperationException(); } + /// public bool TryGetDecimal(out decimal value) { if (!(double.IsNaN(_value) || double.IsInfinity(_value)) && @@ -54,27 +70,32 @@ public bool TryGetDecimal(out decimal value) return true; } + /// public bool TryGetDouble(out double value) { value = _value; return true; } + /// public bool TryGetProperty(string propertyName, out IValue property) { throw new InvalidOperationException(); } + /// public IArrayValueEnumerator EnumerateArray() { throw new InvalidOperationException(); } + /// public IObjectValueEnumerator EnumerateObject() { throw new InvalidOperationException(); } + /// public IExpression GetExpression() { throw new InvalidOperationException("Not an expression"); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ExpressionValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ExpressionValue.cs index b9a1e425..141c59b3 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ExpressionValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ExpressionValue.cs @@ -14,11 +14,18 @@ */ using System; +using AWS.Lambda.Powertools.JMESPath.Expressions; namespace AWS.Lambda.Powertools.JMESPath.Values; +/// +/// Represents a JMESPath expression. +/// internal readonly struct ExpressionValue : IValue { + /// + /// The expression to evaluate. + /// private readonly IExpression _expr; internal ExpressionValue(IExpression expr) @@ -26,45 +33,55 @@ internal ExpressionValue(IExpression expr) _expr = expr; } + /// public JmesPathType Type => JmesPathType.Expression; + /// public IValue this[int index] => throw new InvalidOperationException(); + /// public int GetArrayLength() { throw new InvalidOperationException(); } + /// public string GetString() { throw new InvalidOperationException(); } + /// public bool TryGetDecimal(out decimal value) { throw new InvalidOperationException(); } + /// public bool TryGetDouble(out double value) { throw new InvalidOperationException(); } + /// public bool TryGetProperty(string propertyName, out IValue property) { throw new InvalidOperationException(); } + /// public IArrayValueEnumerator EnumerateArray() { throw new InvalidOperationException(); } + /// public IObjectValueEnumerator EnumerateObject() { throw new InvalidOperationException(); } + /// public IExpression GetExpression() { return _expr; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/FalseValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/FalseValue.cs index 6b06928c..cc1f2409 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/FalseValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/FalseValue.cs @@ -14,50 +14,64 @@ */ using System; +using AWS.Lambda.Powertools.JMESPath.Expressions; namespace AWS.Lambda.Powertools.JMESPath.Values; +/// +/// Represents a JMESPath false value. +/// internal readonly struct FalseValue : IValue { + /// public JmesPathType Type => JmesPathType.False; + /// public IValue this[int index] => throw new InvalidOperationException(); + /// public int GetArrayLength() { throw new InvalidOperationException(); } + /// public string GetString() { throw new InvalidOperationException(); } + /// public bool TryGetDecimal(out decimal value) { throw new InvalidOperationException(); } + /// public bool TryGetDouble(out double value) { throw new InvalidOperationException(); } + /// public bool TryGetProperty(string propertyName, out IValue property) { throw new InvalidOperationException(); } + /// public IArrayValueEnumerator EnumerateArray() { throw new InvalidOperationException(); } + /// public IObjectValueEnumerator EnumerateObject() { throw new InvalidOperationException(); } + /// public IExpression GetExpression() { throw new InvalidOperationException("Not an expression"); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IArrayValueEnumerator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IArrayValueEnumerator.cs index a96a311c..02d16d1c 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IArrayValueEnumerator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IArrayValueEnumerator.cs @@ -17,6 +17,9 @@ namespace AWS.Lambda.Powertools.JMESPath.Values; +/// +/// Represents an enumerator for an array value. +/// internal interface IArrayValueEnumerator : IEnumerator, IEnumerable { } \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IObjectValueEnumerator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IObjectValueEnumerator.cs index 05f6b075..61d88868 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IObjectValueEnumerator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IObjectValueEnumerator.cs @@ -17,6 +17,9 @@ namespace AWS.Lambda.Powertools.JMESPath.Values; +/// +/// Interface for object value enumerator. +/// internal interface IObjectValueEnumerator : IEnumerator, IEnumerable { } \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IValue.cs index a0295f62..052e184b 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IValue.cs @@ -13,18 +13,72 @@ * permissions and limitations under the License. */ +using AWS.Lambda.Powertools.JMESPath.Expressions; + namespace AWS.Lambda.Powertools.JMESPath.Values; internal interface IValue { + /// + /// The type of the JMESPath value + /// JmesPathType Type { get; } + + /// + /// The value of the JMESPath value + /// + /// IValue this[int index] { get; } + + /// + /// The length of the array + /// + /// int GetArrayLength(); + + /// + /// Get the value as a string + /// + /// string GetString(); + + /// + /// Try to get the value as a decimal + /// + /// + /// bool TryGetDecimal(out decimal value); + + /// + /// Try to get the value as a double + /// + /// + /// bool TryGetDouble(out double value); + + /// + /// Try to get the property value + /// + /// + /// + /// bool TryGetProperty(string propertyName, out IValue property); + + /// + /// Enumerate the array values + /// + /// IArrayValueEnumerator EnumerateArray(); + + + /// + /// Enumerate the object values + /// IObjectValueEnumerator EnumerateObject(); + + + /// + /// Get the expression for this value + /// IExpression GetExpression(); } \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JmesPathType.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JmesPathType.cs index 20f154f3..0af98387 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JmesPathType.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JmesPathType.cs @@ -15,6 +15,9 @@ namespace AWS.Lambda.Powertools.JMESPath.Values; +/// +/// Represents the type of a JMESPath value. +/// internal enum JmesPathType { Null, diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JsonElementValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JsonElementValue.cs index 61028276..b28382b4 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JsonElementValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JsonElementValue.cs @@ -17,13 +17,23 @@ using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Nodes; +using AWS.Lambda.Powertools.JMESPath.Expressions; namespace AWS.Lambda.Powertools.JMESPath.Values; +/// +/// Represents a value. +/// internal readonly struct JsonElementValue : IValue { + /// + /// The underlying value. + /// private class ArrayEnumerator : IArrayValueEnumerator { + /// + /// The underlying value. + /// private JsonElement.ArrayEnumerator _enumerator; public ArrayEnumerator(JsonElement.ArrayEnumerator enumerator) @@ -56,8 +66,14 @@ protected virtual void Dispose(bool disposing) } } + /// + /// The current in the . + /// public IValue Current => new JsonElementValue(_enumerator.Current); + /// + /// The current in the . + /// object System.Collections.IEnumerator.Current => Current; public IEnumerator GetEnumerator() @@ -73,6 +89,9 @@ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() private class ObjectEnumerator : IObjectValueEnumerator { + /// + /// The underlying value. + /// private JsonElement.ObjectEnumerator _enumerator; public ObjectEnumerator(JsonElement.ObjectEnumerator enumerator) @@ -105,9 +124,15 @@ protected virtual void Dispose(bool disposing) } } + /// + /// The current in the . + /// public NameValuePair Current => new(_enumerator.Current.Name, new JsonElementValue(_enumerator.Current.Value)); + /// + /// The current in the . + /// object System.Collections.IEnumerator.Current => Current; public IEnumerator GetEnumerator() @@ -152,6 +177,7 @@ public JmesPathType Type } } + /// public IValue this[int index] => new JsonElementValue(_element[index]); public int GetArrayLength() diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NameValuePair.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NameValuePair.cs index 2ffe2717..0d7712ce 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NameValuePair.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NameValuePair.cs @@ -15,6 +15,9 @@ namespace AWS.Lambda.Powertools.JMESPath.Values { + /// + /// Represents a name-value pair. + /// internal readonly struct NameValuePair { public string Name { get; } diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NullValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NullValue.cs index fc509571..88d2bcab 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NullValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NullValue.cs @@ -14,50 +14,64 @@ */ using System; +using AWS.Lambda.Powertools.JMESPath.Expressions; namespace AWS.Lambda.Powertools.JMESPath.Values; +/// +/// Represents a null value. +/// internal readonly struct NullValue : IValue { + /// public JmesPathType Type => JmesPathType.Null; + /// public IValue this[int index] => throw new InvalidOperationException(); + /// public int GetArrayLength() { throw new InvalidOperationException(); } + /// public string GetString() { throw new InvalidOperationException(); } + /// public bool TryGetDecimal(out decimal value) { throw new InvalidOperationException(); } + /// public bool TryGetDouble(out double value) { throw new InvalidOperationException(); } + /// public bool TryGetProperty(string propertyName, out IValue property) { throw new InvalidOperationException(); } + /// public IArrayValueEnumerator EnumerateArray() { throw new InvalidOperationException(); } + /// public IObjectValueEnumerator EnumerateObject() { throw new InvalidOperationException(); } + /// public IExpression GetExpression() { throw new InvalidOperationException("Not an expression"); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ObjectValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ObjectValue.cs index c1ff0c9e..809e53d8 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ObjectValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ObjectValue.cs @@ -17,14 +17,28 @@ using System.Collections.Generic; using System.Text; using System.Text.Json; +using AWS.Lambda.Powertools.JMESPath.Expressions; namespace AWS.Lambda.Powertools.JMESPath.Values; +/// +/// Represents an object value. +/// internal readonly struct ObjectValue : IValue { + /// + /// An that can be used to iterate over the + /// private sealed class ObjectEnumerator : IObjectValueEnumerator { + /// + /// The underlying that is being enumerated. + /// private readonly IDictionary _value; + + /// + /// The underlying that is being enumerated. + /// private readonly System.Collections.IEnumerator _enumerator; public ObjectEnumerator(IDictionary value) @@ -47,6 +61,7 @@ void IDisposable.Dispose() { } + /// public NameValuePair Current { get @@ -56,6 +71,7 @@ public NameValuePair Current } } + /// object System.Collections.IEnumerator.Current => Current; public IEnumerator GetEnumerator() @@ -76,45 +92,55 @@ internal ObjectValue(IDictionary value) _value = value; } + /// public JmesPathType Type => JmesPathType.Object; + /// public IValue this[int index] => throw new InvalidOperationException(); + /// public int GetArrayLength() { throw new InvalidOperationException(); } + /// public string GetString() { throw new InvalidOperationException(); } - + + /// public bool TryGetDecimal(out decimal value) { throw new InvalidOperationException(); } + /// public bool TryGetDouble(out double value) { throw new InvalidOperationException(); } + /// public bool TryGetProperty(string propertyName, out IValue property) { return _value.TryGetValue(propertyName, out property); } + /// public IArrayValueEnumerator EnumerateArray() { throw new InvalidOperationException(); } + /// public IObjectValueEnumerator EnumerateObject() { return new ObjectEnumerator(_value); } + /// public IExpression GetExpression() { throw new InvalidOperationException("Not an expression"); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/StringValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/StringValue.cs index 1146cc21..135e0715 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/StringValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/StringValue.cs @@ -15,11 +15,18 @@ using System; using System.Text.Json; +using AWS.Lambda.Powertools.JMESPath.Expressions; namespace AWS.Lambda.Powertools.JMESPath.Values; +/// +/// Represents a string value. +/// internal readonly struct StringValue : IValue { + /// + /// The string value. + /// private readonly string _value; internal StringValue(string value) @@ -27,45 +34,55 @@ internal StringValue(string value) _value = value; } + /// public JmesPathType Type => JmesPathType.String; + /// public IValue this[int index] => throw new InvalidOperationException(); + /// public int GetArrayLength() { throw new InvalidOperationException(); } + /// public string GetString() { return _value; } + /// public bool TryGetDecimal(out decimal value) { throw new InvalidOperationException(); } + /// public bool TryGetDouble(out double value) { throw new InvalidOperationException(); } + /// public bool TryGetProperty(string propertyName, out IValue property) { throw new InvalidOperationException(); } + /// public IArrayValueEnumerator EnumerateArray() { throw new InvalidOperationException(); } + /// public IObjectValueEnumerator EnumerateObject() { throw new InvalidOperationException(); } + /// public IExpression GetExpression() { throw new InvalidOperationException("Not an expression"); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/TrueValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/TrueValue.cs index 60309692..14d46b45 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/TrueValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/TrueValue.cs @@ -14,50 +14,64 @@ */ using System; +using AWS.Lambda.Powertools.JMESPath.Expressions; namespace AWS.Lambda.Powertools.JMESPath.Values; +/// +/// True value +/// internal readonly struct TrueValue : IValue { + /// public JmesPathType Type => JmesPathType.True; + /// public IValue this[int index] => throw new InvalidOperationException(); + /// public int GetArrayLength() { throw new InvalidOperationException(); } + /// public string GetString() { throw new InvalidOperationException(); } + /// public bool TryGetDecimal(out decimal value) { throw new InvalidOperationException(); } + /// public bool TryGetDouble(out double value) { throw new InvalidOperationException(); } + /// public bool TryGetProperty(string propertyName, out IValue property) { throw new InvalidOperationException(); } + /// public IArrayValueEnumerator EnumerateArray() { throw new InvalidOperationException(); } + /// public IObjectValueEnumerator EnumerateObject() { throw new InvalidOperationException(); } + /// public IExpression GetExpression() { throw new InvalidOperationException("Not an expression"); diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueEqualityComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueEqualityComparer.cs index bdc83197..b0ce0a2c 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueEqualityComparer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueEqualityComparer.cs @@ -19,14 +19,24 @@ namespace AWS.Lambda.Powertools.JMESPath.Values { + /// + /// Compares two instances for equality. + /// internal sealed class ValueEqualityComparer : IEqualityComparer { + /// + /// Singleton instance of . + /// internal static ValueEqualityComparer Instance { get; } = new(); + /// + /// Max Hash depth + /// private readonly int _maxHashDepth = 100; private ValueEqualityComparer() {} + /// public bool Equals(IValue lhs, IValue rhs) { if (lhs != null && rhs != null && lhs.Type != rhs.Type) @@ -144,6 +154,4 @@ private int ComputeHashCode(IValue element, int depth) return hashCode; } } - - } From 6af56d99d8be22156aae6f302e49d1c625c29a70 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Thu, 2 May 2024 16:23:37 +0100 Subject: [PATCH 24/28] Update Attribution - update LICENSE-THIRD-PARTY --- LICENSE-THIRD-PARTY | 201 +++++++++++++++++- .../Expressions/BaseExpression.cs | 2 +- .../Expressions/CurrentNode.cs | 2 +- .../Expressions/Expression.cs | 2 +- .../Expressions/FilterExpression.cs | 2 +- .../Expressions/FlattenProjection.cs | 2 +- .../Expressions/FunctionExpression.cs | 2 +- .../Expressions/IExpression.cs | 2 +- .../Expressions/IdentifierSelector.cs | 2 +- .../Expressions/IndexSelector.cs | 2 +- .../Expressions/JsonConstants.cs | 2 +- .../Expressions/KeyExpressionPair.cs | 2 +- .../Expressions/ListProjection.cs | 2 +- .../Expressions/MultiSelectHash.cs | 2 +- .../Expressions/MultiSelectList.cs | 2 +- .../Expressions/ObjectProjection.cs | 2 +- .../Expressions/Projection.cs | 2 +- .../Expressions/SliceProjection.cs | 2 +- .../Functions/AbsFunction.cs | 2 +- .../Functions/AvgFunction.cs | 2 +- .../Functions/Base64Function.cs | 6 +- .../Functions/Base64GzipFunction.cs | 6 +- .../Functions/BaseFunction.cs | 2 +- .../Functions/BuiltInFunctions.cs | 6 +- .../Functions/CeilFunction.cs | 2 +- .../Functions/ContainsFunction.cs | 2 +- .../Functions/EndsWithFunction.cs | 3 +- .../Functions/EvaluateMinMax.cs | 2 +- .../Functions/EvaluateMinMaxBy.cs | 2 +- .../Functions/EvaluateStartEndWith.cs | 2 +- .../Functions/FloorFunction.cs | 2 +- .../Functions/IFunction.cs | 2 +- .../Functions/JoinFunction.cs | 2 +- .../Functions/JsonFunction.cs | 6 +- .../Functions/KeysFunction.cs | 2 +- .../Functions/LengthFunction.cs | 2 +- .../Functions/MapFunction.cs | 2 +- .../Functions/MaxByFunction.cs | 3 +- .../Functions/MaxFunction.cs | 3 +- .../Functions/MergeFunction.cs | 2 +- .../Functions/MinByFunction.cs | 3 +- .../Functions/MinFunction.cs | 3 +- .../Functions/NotNullFunction.cs | 2 +- .../Functions/ReverseFunction.cs | 2 +- .../Functions/SortByComparer.cs | 2 +- .../Functions/SortByFunction.cs | 2 +- .../Functions/SortFunction.cs | 2 +- .../Functions/StartsWithFunction.cs | 3 +- .../Functions/SumFunction.cs | 2 +- .../Functions/ToArrayFunction.cs | 2 +- .../Functions/ToNumberFunction.cs | 2 +- .../Functions/ToStringFunction.cs | 2 +- .../Functions/TypeFunction.cs | 2 +- .../Functions/ValuesFunction.cs | 2 +- .../InternalsVisibleTo.cs | 2 +- .../JmesPathParseException.cs | 2 +- .../JmesPathParser.cs | 5 +- .../JmesPathState.cs | 2 +- .../JsonTransformer.cs | 7 +- .../Operator.cs | 2 +- .../Operators/AndOperator.cs | 2 +- .../Operators/BinaryOperator.cs | 2 +- .../Operators/EqOperator.cs | 2 +- .../Operators/GtOperator.cs | 2 +- .../Operators/GteOperator.cs | 2 +- .../Operators/IBinaryOperator.cs | 2 +- .../Operators/IUnaryOperator.cs | 2 +- .../Operators/LtOperator.cs | 2 +- .../Operators/LteOperator.cs | 2 +- .../Operators/NeOperator.cs | 2 +- .../Operators/NotOperator.cs | 2 +- .../Operators/OrOperator.cs | 2 +- .../Operators/RegexOperator.cs | 15 ++ .../Operators/UnaryOperator.cs | 2 +- .../AWS.Lambda.Powertools.JMESPath/Slice.cs | 5 +- .../AWS.Lambda.Powertools.JMESPath/Token.cs | 2 +- .../Utilities/JsonElementEqualityComparer.cs | 2 +- .../Values/ArrayValue.cs | 2 +- .../Values/DecimalValue.cs | 2 +- .../Values/DoubleValue.cs | 2 +- .../Values/ExpressionValue.cs | 2 +- .../Values/FalseValue.cs | 2 +- .../Values/IArrayValueEnumerator.cs | 2 +- .../Values/IObjectValueEnumerator.cs | 2 +- .../Values/IValue.cs | 2 +- .../Values/JmesPathType.cs | 2 +- .../Values/JsonElementValue.cs | 2 +- .../Values/NameValuePair.cs | 2 +- .../Values/NullValue.cs | 2 +- .../Values/ObjectValue.cs | 2 +- .../Values/StringValue.cs | 2 +- .../Values/TrueValue.cs | 2 +- .../Values/ValueComparer.cs | 6 +- .../Values/ValueEqualityComparer.cs | 2 +- 94 files changed, 333 insertions(+), 104 deletions(-) diff --git a/LICENSE-THIRD-PARTY b/LICENSE-THIRD-PARTY index ece76399..23f6acaf 100644 --- a/LICENSE-THIRD-PARTY +++ b/LICENSE-THIRD-PARTY @@ -202,4 +202,203 @@ Apache License distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file + limitations under the License. + + +-------------------------------------------------------------------------------- + +danielaparker/JsonCons.Net +v1.1.0 + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2013-2018 Docker, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/BaseExpression.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/BaseExpression.cs index 925f8c97..a88705dc 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/BaseExpression.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/BaseExpression.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/CurrentNode.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/CurrentNode.cs index 1794d8b4..f741224a 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/CurrentNode.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/CurrentNode.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Expression.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Expression.cs index 8ae2d52a..701d1522 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Expression.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Expression.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FilterExpression.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FilterExpression.cs index 38d0e4ed..2f4f77c0 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FilterExpression.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FilterExpression.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FlattenProjection.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FlattenProjection.cs index d3946321..058785b3 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FlattenProjection.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FlattenProjection.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FunctionExpression.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FunctionExpression.cs index e5630582..1fb69ffe 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FunctionExpression.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FunctionExpression.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IExpression.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IExpression.cs index 1e083366..c02dd13b 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IExpression.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IExpression.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IdentifierSelector.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IdentifierSelector.cs index 1f64ee00..958346b6 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IdentifierSelector.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IdentifierSelector.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IndexSelector.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IndexSelector.cs index 0a21b4f6..360e9452 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IndexSelector.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IndexSelector.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/JsonConstants.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/JsonConstants.cs index 71f6a394..3c12a488 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/JsonConstants.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/JsonConstants.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/KeyExpressionPair.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/KeyExpressionPair.cs index 32d21edc..a186f95a 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/KeyExpressionPair.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/KeyExpressionPair.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ListProjection.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ListProjection.cs index a36e6292..3311a265 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ListProjection.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ListProjection.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectHash.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectHash.cs index 21404a1c..c0941002 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectHash.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectHash.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectList.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectList.cs index 0b7cf183..5d99c4f4 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectList.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectList.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ObjectProjection.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ObjectProjection.cs index a6ec0035..95675502 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ObjectProjection.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ObjectProjection.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Projection.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Projection.cs index 28d1ea4e..2f75b14b 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Projection.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Projection.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/SliceProjection.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/SliceProjection.cs index 5c895033..18e1f386 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/SliceProjection.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/SliceProjection.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs index ef58d85b..0b72576a 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs index 70c5b3ee..8740255a 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64Function.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64Function.cs index 9eab21ff..589600a4 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64Function.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64Function.cs @@ -1,12 +1,12 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * + * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at - * + * * http://aws.amazon.com/apache2.0 - * + * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64GzipFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64GzipFunction.cs index e3f27b96..18617695 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64GzipFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/Base64GzipFunction.cs @@ -1,12 +1,12 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * + * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at - * + * * http://aws.amazon.com/apache2.0 - * + * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs index 987c88ce..d2890cfb 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BuiltInFunctions.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BuiltInFunctions.cs index 5555e0b5..c0035054 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BuiltInFunctions.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BuiltInFunctions.cs @@ -1,12 +1,12 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * + * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at - * + * * http://aws.amazon.com/apache2.0 - * + * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs index cfd55eba..e4ce63fc 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs index 8bd8b80c..c9cc1cfd 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs index 92ccfdfd..caf3c166 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + // 2024-04-19: Powertools addition. return EvaluateStartEndWith.TryEvaluate(args, out element, s0 => s0.EndsWith); } diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMax.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMax.cs index d1c9b0f3..1a2bada5 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMax.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMax.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMaxBy.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMaxBy.cs index 064521f9..fe3b3249 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMaxBy.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMaxBy.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateStartEndWith.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateStartEndWith.cs index c64b4545..8adad2e0 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateStartEndWith.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateStartEndWith.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs index 763a7100..cced2d05 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs index aa7198fc..49d9b886 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs index 52df606b..ae9199cc 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JsonFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JsonFunction.cs index 80e4daf9..a1381cc6 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JsonFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JsonFunction.cs @@ -1,12 +1,12 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * + * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at - * + * * http://aws.amazon.com/apache2.0 - * + * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs index 75c773ac..edb9dcfd 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs index 7be3c5f9..e54493e7 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs index 5ce795a9..3e0095f5 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs index 4804bce6..8fd68e7e 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + // 2024-04-19: Powertools addition. return EvaluateMinMaxBy.TryEvaluate(resources, args, GtOperator.Instance, out element); } diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs index e37e85ef..24820895 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -36,6 +36,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + // 2024-04-19: Powertools addition. return EvaluateMinMax.TryEvaluate(args, GtOperator.Instance, out element); } diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs index 5af18d99..f91990b8 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs index 08fffc2a..71f1fb3c 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + // 2024-04-19: Powertools addition. return EvaluateMinMaxBy.TryEvaluate(resources, args, LtOperator.Instance, out element); } diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs index c8ae1c20..2d377327 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -36,6 +36,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + // 2024-04-19: Powertools addition. return EvaluateMinMax.TryEvaluate(args, LtOperator.Instance, out element); } diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs index b362da73..0d835e35 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs index f66f1e90..8570e0c9 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByComparer.cs index eea78e56..81508620 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByComparer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByComparer.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs index a4ae4fcf..f42293de 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs index 2bb5fffd..3c5e461a 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs index 0b7d7c74..7ed8e1e2 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ public override bool TryEvaluate(DynamicResources resources, IList args, { Debug.Assert(Arity.HasValue && args.Count == Arity!.Value); + // 2024-04-19: Powertools addition. return EvaluateStartEndWith.TryEvaluate(args, out element, s0 => s0.StartsWith); } diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs index 15d59853..a3d74105 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs index fa7d7f6a..54ef92c5 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs index fb68800d..70fe7736 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs index 41c18ddc..5df71056 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs index 24529604..375adaa4 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs index 60e472c4..ce518f62 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/InternalsVisibleTo.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/InternalsVisibleTo.cs index 4a4695be..a1510e77 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/InternalsVisibleTo.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/InternalsVisibleTo.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParseException.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParseException.cs index b1d5b8d4..7ae21860 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParseException.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParseException.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs index a73a5a02..c1657f0f 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -71,9 +71,11 @@ internal JsonTransformer Parse() PushToken(new Token(TokenType.CurrentNode)); _stateStack.Push(JmesPathState.Start); + // 2024-04-19: Powertools addition. var syntaxErrorMsg = "Syntax error"; while (_index < _span.Length) { + // 2024-04-19: Powertools addition. var expectedRightBracket = "Expected right bracket"; switch (_stateStack.Peek()) { @@ -1655,6 +1657,7 @@ private void PushToken(Token token) /// private uint AppendToCodepoint(uint cp, uint c) { + // 2024-04-19: Powertools addition. cp *= 16; switch (c) { diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathState.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathState.cs index a8bb4fb3..b01b06cc 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathState.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathState.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs index 79409d3d..229b9d10 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ namespace AWS.Lambda.Powertools.JMESPath { + // 2024-04-19: Powertools addition. internal sealed class DynamicResources; /// /// Provides functionality for applying a JMESPath expression to transform a JSON document into @@ -125,9 +126,9 @@ internal JsonTransformer(Expression expr) /// The provided JSON document. /// The transformed JSON document. If a type error is detected in a function call, /// a JSON null value is returned. - public JsonDocument Transform(JsonElement doc) { + // 2024-04-19: Powertools addition. var resources = new DynamicResources(); _expr.TryEvaluate(resources, new JsonElementValue(doc), out var temp); return JsonDocument.Parse(temp.ToString() ?? string.Empty); @@ -150,9 +151,9 @@ public JsonDocument Transform(JsonElement doc) /// /// The is . /// - public static JsonDocument Transform(JsonElement doc, string jmesPath) { + // 2024-04-19: Powertools addition. var searcher = Parse(jmesPath); return searcher.Transform(doc); } diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operator.cs index 4f245603..b56ec6d3 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operator.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/AndOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/AndOperator.cs index 7535e40b..290a5016 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/AndOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/AndOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/BinaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/BinaryOperator.cs index 3dbd70a4..c5902520 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/BinaryOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/BinaryOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/EqOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/EqOperator.cs index 730408f3..508d2309 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/EqOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/EqOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GtOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GtOperator.cs index 86071d41..4ae65e2a 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GtOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GtOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GteOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GteOperator.cs index e17e2f7e..c53a173a 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GteOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GteOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IBinaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IBinaryOperator.cs index f9a08433..779179be 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IBinaryOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IBinaryOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IUnaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IUnaryOperator.cs index ab6da0d4..dde47419 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IUnaryOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IUnaryOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LtOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LtOperator.cs index 64d5f465..ee5feedf 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LtOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LtOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LteOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LteOperator.cs index 72ba49ce..2e4c7b5e 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LteOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LteOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NeOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NeOperator.cs index 7a880047..137fd939 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NeOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NeOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NotOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NotOperator.cs index 095c653b..c15d2dc0 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NotOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NotOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/OrOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/OrOperator.cs index 0ead0bba..06fa4596 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/OrOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/OrOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/RegexOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/RegexOperator.cs index c6d1251c..805c32cc 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/RegexOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/RegexOperator.cs @@ -1,3 +1,18 @@ +/* + * Copyright JsonCons.Net authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + using System.Text.RegularExpressions; using AWS.Lambda.Powertools.JMESPath.Expressions; using AWS.Lambda.Powertools.JMESPath.Values; diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/UnaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/UnaryOperator.cs index daf88f17..ecd77016 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/UnaryOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/UnaryOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Slice.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Slice.cs index 938f2c01..b76afb33 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Slice.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Slice.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -48,6 +48,7 @@ public Slice(int? start, int? stop, int step) /// public int GetStart(int size) { + // 2024-04-19: Powertools addition. if (!_start.HasValue) return Step >= 0 ? 0 : size; var len = _start.Value >= 0 ? _start.Value : size + _start.Value; return len <= size ? len : size; @@ -60,10 +61,10 @@ public int GetStart(int size) /// public int GetStop(int size) { + // 2024-04-19: Powertools addition. if (!_stop.HasValue) return Step >= 0 ? size : -1; var len = _stop.Value >= 0 ? _stop.Value : size + _stop.Value; return len <= size ? len : size; } } - } diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs index d333c69e..a37df5cd 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementEqualityComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementEqualityComparer.cs index 6141fb58..418d13c0 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementEqualityComparer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementEqualityComparer.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ArrayValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ArrayValue.cs index 504d86e7..dc6b2460 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ArrayValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ArrayValue.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DecimalValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DecimalValue.cs index 7612befe..64d67f34 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DecimalValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DecimalValue.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DoubleValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DoubleValue.cs index 15966a58..fd147fe6 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DoubleValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DoubleValue.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ExpressionValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ExpressionValue.cs index 141c59b3..bb8c7e97 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ExpressionValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ExpressionValue.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/FalseValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/FalseValue.cs index cc1f2409..7f9d7171 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/FalseValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/FalseValue.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IArrayValueEnumerator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IArrayValueEnumerator.cs index 02d16d1c..7a6d54dd 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IArrayValueEnumerator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IArrayValueEnumerator.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IObjectValueEnumerator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IObjectValueEnumerator.cs index 61d88868..018f197e 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IObjectValueEnumerator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IObjectValueEnumerator.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IValue.cs index 052e184b..fafa6b95 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IValue.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JmesPathType.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JmesPathType.cs index 0af98387..6e0a512b 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JmesPathType.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JmesPathType.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JsonElementValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JsonElementValue.cs index b28382b4..4ef0b228 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JsonElementValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JsonElementValue.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NameValuePair.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NameValuePair.cs index 0d7712ce..7086a759 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NameValuePair.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NameValuePair.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NullValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NullValue.cs index 88d2bcab..e1cdade0 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NullValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NullValue.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ObjectValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ObjectValue.cs index 809e53d8..aabbdd4d 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ObjectValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ObjectValue.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/StringValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/StringValue.cs index 135e0715..734c0cfb 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/StringValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/StringValue.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/TrueValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/TrueValue.cs index 14d46b45..56b2098d 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/TrueValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/TrueValue.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueComparer.cs index 20738000..b7cf20b9 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueComparer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueComparer.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -110,11 +110,13 @@ public int Compare(IValue lhs, IValue rhs) case JmesPathType.Array: { + // 2024-04-19: Powertools addition. return ArrayComparer(lhs, rhs); } case JmesPathType.Object: { + // 2024-04-19: Powertools addition. return ObjectComparer(lhs, rhs); } @@ -123,6 +125,7 @@ public int Compare(IValue lhs, IValue rhs) } } + // 2024-04-19: Powertools addition. private int ArrayComparer(IValue lhs, IValue rhs) { var enumerator1 = lhs.EnumerateArray(); @@ -142,6 +145,7 @@ private int ArrayComparer(IValue lhs, IValue rhs) return result1 ? 1 : result2 ? -1 : 0; } + // 2024-04-19: Powertools addition. private int ObjectComparer(IValue lhs, IValue rhs) { // OrderBy performs a stable sort (Note that supports duplicate property names) diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueEqualityComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueEqualityComparer.cs index b0ce0a2c..226accad 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueEqualityComparer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueEqualityComparer.cs @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. From 930c32605defd16b00d53d11c1ffd7cdd0e312b4 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Thu, 2 May 2024 16:28:25 +0100 Subject: [PATCH 25/28] rename THIRD-PARTY-LICENSES --- LICENSE-THIRD-PARTY => THIRD-PARTY-LICENSES | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename LICENSE-THIRD-PARTY => THIRD-PARTY-LICENSES (100%) diff --git a/LICENSE-THIRD-PARTY b/THIRD-PARTY-LICENSES similarity index 100% rename from LICENSE-THIRD-PARTY rename to THIRD-PARTY-LICENSES From 11827dca2cbcba46b84917d2202af6be96d22be5 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Mon, 6 May 2024 15:15:30 +0100 Subject: [PATCH 26/28] remove unused operator --- .../Operators/RegexOperator.cs | 47 ------------------- 1 file changed, 47 deletions(-) delete mode 100644 libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/RegexOperator.cs diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/RegexOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/RegexOperator.cs deleted file mode 100644 index 805c32cc..00000000 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/RegexOperator.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright JsonCons.Net authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -using System.Text.RegularExpressions; -using AWS.Lambda.Powertools.JMESPath.Expressions; -using AWS.Lambda.Powertools.JMESPath.Values; - -namespace AWS.Lambda.Powertools.JMESPath.Operators; - -internal sealed class RegexOperator : UnaryOperator -{ - private readonly Regex _regex; - - internal RegexOperator(Regex regex) - : base(Operator.Not) - { - _regex = regex; - } - - public override bool TryEvaluate(IValue elem, out IValue result) - { - if (elem.Type != JmesPathType.String) - { - result = JsonConstants.Null; - return false; // type error - } - result = _regex.IsMatch(elem.GetString()) ? JsonConstants.True : JsonConstants.False; - return true; - } - - public override string ToString() - { - return "Regex"; - } -} \ No newline at end of file From 4a15617227edd76b42dc44b03824310b12544dfc Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Mon, 6 May 2024 15:17:46 +0100 Subject: [PATCH 27/28] move slice to expressions --- .../AWS.Lambda.Powertools.JMESPath/{ => Expressions}/Slice.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename libraries/src/AWS.Lambda.Powertools.JMESPath/{ => Expressions}/Slice.cs (97%) diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Slice.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Slice.cs similarity index 97% rename from libraries/src/AWS.Lambda.Powertools.JMESPath/Slice.cs rename to libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Slice.cs index b76afb33..ea2902b0 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Slice.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Slice.cs @@ -13,7 +13,7 @@ * permissions and limitations under the License. */ -namespace AWS.Lambda.Powertools.JMESPath +namespace AWS.Lambda.Powertools.JMESPath.Expressions { /// /// A slice of a list or string. From c7448709ffe3a88c96298a7c176316eabfef9e01 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Thu, 9 May 2024 10:22:41 +0100 Subject: [PATCH 28/28] adressing pr comments --- .../Persistence/BasePersistenceStore.cs | 16 +++++++--------- .../Expressions/BaseExpression.cs | 2 +- .../Expressions/CurrentNode.cs | 2 +- .../Expressions/Expression.cs | 2 +- .../Expressions/FilterExpression.cs | 2 +- .../Expressions/FlattenProjection.cs | 2 +- .../Expressions/FunctionExpression.cs | 2 +- .../Expressions/IExpression.cs | 2 +- .../Expressions/IdentifierSelector.cs | 2 +- .../Expressions/IndexSelector.cs | 2 +- .../Expressions/JsonConstants.cs | 2 +- .../Expressions/KeyExpressionPair.cs | 2 +- .../Expressions/ListProjection.cs | 2 +- .../Expressions/MultiSelectHash.cs | 2 +- .../Expressions/MultiSelectList.cs | 2 +- .../Expressions/ObjectProjection.cs | 2 +- .../Expressions/Projection.cs | 2 +- .../Expressions/Slice.cs | 2 +- .../Expressions/SliceProjection.cs | 2 +- .../Functions/AbsFunction.cs | 2 +- .../Functions/AvgFunction.cs | 2 +- .../Functions/BaseFunction.cs | 2 +- .../Functions/CeilFunction.cs | 2 +- .../Functions/ContainsFunction.cs | 2 +- .../Functions/EndsWithFunction.cs | 2 +- .../Functions/EvaluateMinMax.cs | 2 +- .../Functions/EvaluateMinMaxBy.cs | 2 +- .../Functions/EvaluateStartEndWith.cs | 2 +- .../Functions/FloorFunction.cs | 2 +- .../Functions/IFunction.cs | 2 +- .../Functions/JoinFunction.cs | 2 +- .../Functions/KeysFunction.cs | 2 +- .../Functions/LengthFunction.cs | 2 +- .../Functions/MapFunction.cs | 2 +- .../Functions/MaxByFunction.cs | 2 +- .../Functions/MaxFunction.cs | 2 +- .../Functions/MergeFunction.cs | 2 +- .../Functions/MinByFunction.cs | 2 +- .../Functions/MinFunction.cs | 2 +- .../Functions/NotNullFunction.cs | 2 +- .../Functions/ReverseFunction.cs | 2 +- .../Functions/SortByComparer.cs | 2 +- .../Functions/SortByFunction.cs | 2 +- .../Functions/SortFunction.cs | 2 +- .../Functions/StartsWithFunction.cs | 2 +- .../Functions/SumFunction.cs | 2 +- .../Functions/ToArrayFunction.cs | 2 +- .../Functions/ToNumberFunction.cs | 2 +- .../Functions/ToStringFunction.cs | 2 +- .../Functions/TypeFunction.cs | 2 +- .../Functions/ValuesFunction.cs | 2 +- .../InternalsVisibleTo.cs | 2 +- .../JmesPathParseException.cs | 2 +- .../JmesPathParser.cs | 2 +- .../JmesPathState.cs | 2 +- .../JsonTransformer.cs | 8 +++----- .../AWS.Lambda.Powertools.JMESPath/Operator.cs | 2 +- .../Operators/AndOperator.cs | 2 +- .../Operators/BinaryOperator.cs | 2 +- .../Operators/EqOperator.cs | 2 +- .../Operators/GtOperator.cs | 2 +- .../Operators/GteOperator.cs | 2 +- .../Operators/IBinaryOperator.cs | 2 +- .../Operators/IUnaryOperator.cs | 2 +- .../Operators/LtOperator.cs | 2 +- .../Operators/LteOperator.cs | 2 +- .../Operators/NeOperator.cs | 2 +- .../Operators/NotOperator.cs | 2 +- .../Operators/OrOperator.cs | 2 +- .../Operators/UnaryOperator.cs | 2 +- .../src/AWS.Lambda.Powertools.JMESPath/Token.cs | 4 ++-- .../Utilities/JsonElementEqualityComparer.cs | 2 +- .../Values/ArrayValue.cs | 2 +- .../Values/DecimalValue.cs | 2 +- .../Values/DoubleValue.cs | 2 +- .../Values/ExpressionValue.cs | 2 +- .../Values/FalseValue.cs | 2 +- .../Values/IArrayValueEnumerator.cs | 2 +- .../Values/IObjectValueEnumerator.cs | 2 +- .../Values/IValue.cs | 2 +- .../Values/JmesPathType.cs | 2 +- .../Values/JsonElementValue.cs | 2 +- .../Values/NameValuePair.cs | 2 +- .../Values/NullValue.cs | 2 +- .../Values/ObjectValue.cs | 2 +- .../Values/StringValue.cs | 2 +- .../Values/TrueValue.cs | 2 +- .../Values/ValueComparer.cs | 2 +- .../Values/ValueEqualityComparer.cs | 2 +- 89 files changed, 98 insertions(+), 102 deletions(-) diff --git a/libraries/src/AWS.Lambda.Powertools.Idempotency/Persistence/BasePersistenceStore.cs b/libraries/src/AWS.Lambda.Powertools.Idempotency/Persistence/BasePersistenceStore.cs index 75191c5a..c71dc9d9 100644 --- a/libraries/src/AWS.Lambda.Powertools.Idempotency/Persistence/BasePersistenceStore.cs +++ b/libraries/src/AWS.Lambda.Powertools.Idempotency/Persistence/BasePersistenceStore.cs @@ -225,15 +225,13 @@ private DataRecord RetrieveFromCache(string idempotencyKey, DateTimeOffset now) { if (!_idempotencyOptions.UseLocalCache) return null; - - if (_cache.TryGet(idempotencyKey, out var record) && record!=null) + + if (!_cache.TryGet(idempotencyKey, out var record) || record == null) return null; + if (!record.IsExpired(now)) { - if (!record.IsExpired(now)) - { - return record; - } - DeleteFromCache(idempotencyKey); + return record; } + DeleteFromCache(idempotencyKey); return null; } @@ -262,7 +260,7 @@ private string GetHashedPayload(JsonDocument data) } var transformer = JsonTransformer.Parse(_idempotencyOptions.PayloadValidationJmesPath); - JsonDocument result = transformer.Transform(data.RootElement); + var result = transformer.Transform(data.RootElement); return GenerateHash(result.RootElement); } @@ -289,7 +287,7 @@ private string GetHashedIdempotencyKey(JsonDocument data) if (eventKeyJmesPath != null) { var transformer = JsonTransformer.Parse(eventKeyJmesPath); - JsonDocument result = transformer.Transform(node); + var result = transformer.Transform(node); node = result.RootElement; } diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/BaseExpression.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/BaseExpression.cs index a88705dc..843d6f8c 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/BaseExpression.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/BaseExpression.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/CurrentNode.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/CurrentNode.cs index f741224a..2566e00c 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/CurrentNode.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/CurrentNode.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Expression.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Expression.cs index 701d1522..7e0163dd 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Expression.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Expression.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FilterExpression.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FilterExpression.cs index 2f4f77c0..4389b588 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FilterExpression.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FilterExpression.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FlattenProjection.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FlattenProjection.cs index 058785b3..4c0746d1 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FlattenProjection.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FlattenProjection.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FunctionExpression.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FunctionExpression.cs index 1fb69ffe..2627df88 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FunctionExpression.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/FunctionExpression.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IExpression.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IExpression.cs index c02dd13b..011a6f4a 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IExpression.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IExpression.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IdentifierSelector.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IdentifierSelector.cs index 958346b6..4648c54d 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IdentifierSelector.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IdentifierSelector.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IndexSelector.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IndexSelector.cs index 360e9452..d6233ce9 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IndexSelector.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/IndexSelector.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/JsonConstants.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/JsonConstants.cs index 3c12a488..1d8e7006 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/JsonConstants.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/JsonConstants.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/KeyExpressionPair.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/KeyExpressionPair.cs index a186f95a..2cc65cdb 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/KeyExpressionPair.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/KeyExpressionPair.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ListProjection.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ListProjection.cs index 3311a265..a8f9bfc6 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ListProjection.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ListProjection.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectHash.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectHash.cs index c0941002..454550c4 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectHash.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectHash.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectList.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectList.cs index 5d99c4f4..4b5a577c 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectList.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/MultiSelectList.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ObjectProjection.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ObjectProjection.cs index 95675502..64704b3a 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ObjectProjection.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/ObjectProjection.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Projection.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Projection.cs index 2f75b14b..d6a7e62c 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Projection.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Projection.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Slice.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Slice.cs index ea2902b0..c534c34c 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Slice.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/Slice.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/SliceProjection.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/SliceProjection.cs index 18e1f386..5250a2fd 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/SliceProjection.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Expressions/SliceProjection.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs index 0b72576a..d1b9da51 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AbsFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs index 8740255a..3820b089 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/AvgFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs index d2890cfb..5168da76 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/BaseFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs index e4ce63fc..5d3cbb3c 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/CeilFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs index c9cc1cfd..94f41326 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ContainsFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs index caf3c166..738a29f0 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EndsWithFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMax.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMax.cs index 1a2bada5..a7fa04e5 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMax.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMax.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMaxBy.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMaxBy.cs index fe3b3249..dc04356a 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMaxBy.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateMinMaxBy.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateStartEndWith.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateStartEndWith.cs index 8adad2e0..c2a2d50c 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateStartEndWith.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/EvaluateStartEndWith.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs index cced2d05..22c59813 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/FloorFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs index 49d9b886..16e1b852 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/IFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs index ae9199cc..a30671db 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/JoinFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs index edb9dcfd..f3136ef6 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/KeysFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs index e54493e7..fb6ef5e4 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/LengthFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs index 3e0095f5..13c6ce67 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MapFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs index 8fd68e7e..7cd67189 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxByFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs index 24820895..a5325d97 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MaxFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs index f91990b8..29afd5d6 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MergeFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs index 71f1fb3c..f98def98 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinByFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs index 2d377327..e686fb5a 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/MinFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs index 0d835e35..bc8b86cf 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/NotNullFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs index 8570e0c9..70a33ff8 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ReverseFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByComparer.cs index 81508620..a307ae06 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByComparer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByComparer.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs index f42293de..5ca2e64a 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortByFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs index 3c5e461a..5c489eac 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SortFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs index 7ed8e1e2..d2b15033 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/StartsWithFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs index a3d74105..f88327ba 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/SumFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs index 54ef92c5..5832a208 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToArrayFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs index 70fe7736..b5389cf4 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToNumberFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs index 5df71056..bd179c3d 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ToStringFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs index 375adaa4..fbf3f399 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/TypeFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs index ce518f62..263201f6 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Functions/ValuesFunction.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/InternalsVisibleTo.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/InternalsVisibleTo.cs index a1510e77..89436184 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/InternalsVisibleTo.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/InternalsVisibleTo.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParseException.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParseException.cs index 7ae21860..1cd7bfa4 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParseException.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParseException.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs index c1657f0f..dd6e004f 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathParser.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathState.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathState.cs index b01b06cc..eb595e93 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathState.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JmesPathState.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs index 229b9d10..46059aea 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/JsonTransformer.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -101,10 +101,8 @@ public sealed class JsonTransformer /// public static JsonTransformer Parse(string jmesPath) { - if (jmesPath == null) - { - throw new ArgumentNullException(nameof(jmesPath)); - } + ArgumentNullException.ThrowIfNull(jmesPath); + var compiler = new JmesPathParser(jmesPath); return compiler.Parse(); } diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operator.cs index b56ec6d3..1e24bbf0 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operator.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/AndOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/AndOperator.cs index 290a5016..901aece8 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/AndOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/AndOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/BinaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/BinaryOperator.cs index c5902520..f7aa3f07 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/BinaryOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/BinaryOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/EqOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/EqOperator.cs index 508d2309..d1fc3d4b 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/EqOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/EqOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GtOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GtOperator.cs index 4ae65e2a..07b8ff0a 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GtOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GtOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GteOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GteOperator.cs index c53a173a..7c25c6eb 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GteOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/GteOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IBinaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IBinaryOperator.cs index 779179be..cff04a8e 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IBinaryOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IBinaryOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IUnaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IUnaryOperator.cs index dde47419..a44b1392 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IUnaryOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/IUnaryOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LtOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LtOperator.cs index ee5feedf..6405e593 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LtOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LtOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LteOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LteOperator.cs index 2e4c7b5e..f0f3ba33 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LteOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/LteOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NeOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NeOperator.cs index 137fd939..410f3b66 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NeOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NeOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NotOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NotOperator.cs index c15d2dc0..429b1a5d 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NotOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/NotOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/OrOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/OrOperator.cs index 06fa4596..3db2145f 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/OrOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/OrOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/UnaryOperator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/UnaryOperator.cs index ecd77016..2a156989 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/UnaryOperator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Operators/UnaryOperator.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs index a37df5cd..46465f84 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Token.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -57,7 +57,7 @@ internal enum TokenType /// /// The expression associated with this token. /// - private readonly object? _expr; + private readonly object _expr; internal Token(TokenType type) { diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementEqualityComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementEqualityComparer.cs index 418d13c0..d96c61f2 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementEqualityComparer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Utilities/JsonElementEqualityComparer.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ArrayValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ArrayValue.cs index dc6b2460..e07d8b8d 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ArrayValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ArrayValue.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DecimalValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DecimalValue.cs index 64d67f34..b03ff5db 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DecimalValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DecimalValue.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DoubleValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DoubleValue.cs index fd147fe6..2962f28d 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DoubleValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/DoubleValue.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ExpressionValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ExpressionValue.cs index bb8c7e97..53dc3a7c 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ExpressionValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ExpressionValue.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/FalseValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/FalseValue.cs index 7f9d7171..5a06320c 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/FalseValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/FalseValue.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IArrayValueEnumerator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IArrayValueEnumerator.cs index 7a6d54dd..778e3403 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IArrayValueEnumerator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IArrayValueEnumerator.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IObjectValueEnumerator.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IObjectValueEnumerator.cs index 018f197e..407f04d0 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IObjectValueEnumerator.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IObjectValueEnumerator.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IValue.cs index fafa6b95..49950ed6 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/IValue.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JmesPathType.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JmesPathType.cs index 6e0a512b..86a71209 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JmesPathType.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JmesPathType.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JsonElementValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JsonElementValue.cs index 4ef0b228..b215d339 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JsonElementValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/JsonElementValue.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NameValuePair.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NameValuePair.cs index 7086a759..e0666fe2 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NameValuePair.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NameValuePair.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NullValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NullValue.cs index e1cdade0..458bf892 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NullValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/NullValue.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ObjectValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ObjectValue.cs index aabbdd4d..86153435 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ObjectValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ObjectValue.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/StringValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/StringValue.cs index 734c0cfb..02fb2890 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/StringValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/StringValue.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/TrueValue.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/TrueValue.cs index 56b2098d..457012ee 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/TrueValue.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/TrueValue.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueComparer.cs index b7cf20b9..0996d6c7 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueComparer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueComparer.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueEqualityComparer.cs b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueEqualityComparer.cs index 226accad..73363874 100644 --- a/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueEqualityComparer.cs +++ b/libraries/src/AWS.Lambda.Powertools.JMESPath/Values/ValueEqualityComparer.cs @@ -1,5 +1,5 @@ /* - * Copyright JsonCons.Net authors. All Rights Reserved. + * Copyright JsonCons.Net authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License.