diff --git a/Frends.JSON.ConvertJSONStringToJToken/CHANGELOG.md b/Frends.JSON.ConvertJSONStringToJToken/CHANGELOG.md index d5653be..adc4fab 100644 --- a/Frends.JSON.ConvertJSONStringToJToken/CHANGELOG.md +++ b/Frends.JSON.ConvertJSONStringToJToken/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [1.2.0] - 2026-08-04 +### Changed +- The task now targets .NET 8. +- Added error handling options: you can now choose whether the task throws an error on failure or returns a result with `Success = false` and error details in the `Error` property. +- The result now includes an `Error` property with details when the task fails. + ## [1.1.0] - 2024-08-20 ### Updated - Updated Newtonsoft.Json library to the latest version 13.0.3. diff --git a/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken.UnitTests/ErrorHandlerTests.cs b/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken.UnitTests/ErrorHandlerTests.cs new file mode 100644 index 0000000..1dad410 --- /dev/null +++ b/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken.UnitTests/ErrorHandlerTests.cs @@ -0,0 +1,44 @@ +using Frends.JSON.ConvertJSONStringToJToken.Definitions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Threading; + +namespace Frends.JSON.ConvertJSONStringToJToken.UnitTests; + +[TestClass] +public class ErrorHandlerTests +{ + private const string CustomErrorMessage = "CustomErrorMessage"; + + private static Input InvalidInput() => new Input { Json = "not valid json{{" }; + private static Options DefaultOptions() => new Options { ThrowErrorOnFailure = true }; + + [TestMethod] + public void Should_Throw_Error_When_ThrowErrorOnFailure_Is_True() + { + var ex = Assert.ThrowsException(() => + JSON.ConvertJSONStringToJToken(InvalidInput(), DefaultOptions(), CancellationToken.None)); + Assert.IsNotNull(ex); + } + + [TestMethod] + public void Should_Return_Failed_Result_When_ThrowErrorOnFailure_Is_False() + { + var options = DefaultOptions(); + options.ThrowErrorOnFailure = false; + var result = JSON.ConvertJSONStringToJToken(InvalidInput(), options, CancellationToken.None); + Assert.IsFalse(result.Success); + Assert.IsNotNull(result.Error); + } + + [TestMethod] + public void Should_Use_Custom_ErrorMessageOnFailure() + { + var options = DefaultOptions(); + options.ErrorMessageOnFailure = CustomErrorMessage; + var ex = Assert.ThrowsException(() => + JSON.ConvertJSONStringToJToken(InvalidInput(), options, CancellationToken.None)); + Assert.IsNotNull(ex); + StringAssert.Contains(ex.Message, CustomErrorMessage); + } +} diff --git a/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken.UnitTests/Frends.JSON.ConvertJSONStringToJToken.UnitTests.csproj b/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken.UnitTests/Frends.JSON.ConvertJSONStringToJToken.UnitTests.csproj index 65d219f..d0556aa 100644 --- a/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken.UnitTests/Frends.JSON.ConvertJSONStringToJToken.UnitTests.csproj +++ b/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken.UnitTests/Frends.JSON.ConvertJSONStringToJToken.UnitTests.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 enable enable diff --git a/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken.UnitTests/UnitTests.cs b/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken.UnitTests/UnitTests.cs index 39ffa75..2d9c4bb 100644 --- a/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken.UnitTests/UnitTests.cs +++ b/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken.UnitTests/UnitTests.cs @@ -1,12 +1,15 @@ using Frends.JSON.ConvertJSONStringToJToken.Definitions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json.Linq; +using System.Threading; namespace Frends.JSON.ConvertJSONStringToJToken.UnitTests; [TestClass] public class UnitTests { + private static Options DefaultOptions() => new Options { ThrowErrorOnFailure = true }; + [TestMethod] public void ShouldConvertJsonStringToJToken() { @@ -15,9 +18,9 @@ public void ShouldConvertJsonStringToJToken() Json = @"{ 'foo': 'bar', 'foobar': ['Foo', 'Bar'] }" }; - var result = JSON.ConvertJSONStringToJToken(input); + var result = JSON.ConvertJSONStringToJToken(input, DefaultOptions(), CancellationToken.None); Assert.AreEqual("bar", result.Jtoken.foo.ToString()); Assert.IsTrue(result.Success); Assert.IsInstanceOfType(result.Jtoken, typeof(JObject)); } -} \ No newline at end of file +} diff --git a/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken/ConvertJSONStringToJToken.cs b/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken/ConvertJSONStringToJToken.cs index 9fc69cf..b5030cd 100644 --- a/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken/ConvertJSONStringToJToken.cs +++ b/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken/ConvertJSONStringToJToken.cs @@ -1,22 +1,35 @@ using Frends.JSON.ConvertJSONStringToJToken.Definitions; +using Frends.JSON.ConvertJSONStringToJToken.Helpers; using Newtonsoft.Json.Linq; +using System; using System.ComponentModel; +using System.Threading; namespace Frends.JSON.ConvertJSONStringToJToken; /// /// JSON Task. /// -public class JSON +public static class JSON { /// /// Convert JSON string to JToken. /// [Documentation](https://tasks.frends.com/tasks/frends-tasks/Frends.JSON.ConvertJSONStringToJToken) /// /// Input parameters - /// Object { bool Success, dynamic Jtoken } - public static Result ConvertJSONStringToJToken([PropertyTab] Input input) + /// Options for error handling + /// Token to cancel the operation + /// Object { bool Success, dynamic Jtoken, Error Error } + public static Result ConvertJSONStringToJToken([PropertyTab] Input input, [PropertyTab] Options options, CancellationToken cancellationToken) { - return new Result(true, JToken.Parse(input.Json)); + try + { + cancellationToken.ThrowIfCancellationRequested(); + return new Result { Success = true, Jtoken = JToken.Parse(input.Json) }; + } + catch (Exception ex) + { + return ex.Handle(options); + } } -} \ No newline at end of file +} diff --git a/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken/Definitions/Error.cs b/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken/Definitions/Error.cs new file mode 100644 index 0000000..d9a59ae --- /dev/null +++ b/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken/Definitions/Error.cs @@ -0,0 +1,20 @@ +using System; + +namespace Frends.JSON.ConvertJSONStringToJToken.Definitions; + +/// +/// Error information. +/// +public class Error +{ + /// + /// Error message. + /// + /// An error occurred. + public string Message { get; init; } + + /// + /// Additional error information. + /// + public Exception AdditionalInfo { get; init; } +} diff --git a/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken/Definitions/Options.cs b/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken/Definitions/Options.cs new file mode 100644 index 0000000..1335e6e --- /dev/null +++ b/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken/Definitions/Options.cs @@ -0,0 +1,25 @@ +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace Frends.JSON.ConvertJSONStringToJToken.Definitions; + +/// +/// Options for the task. +/// +public class Options +{ + /// + /// Whether to throw an error on failure or return a result with Success = false. + /// + /// true + [DefaultValue(true)] + public bool ThrowErrorOnFailure { get; set; } = true; + + /// + /// Custom error message to use when ThrowErrorOnFailure is true. + /// + /// + [DisplayFormat(DataFormatString = "Text")] + [DefaultValue("")] + public string ErrorMessageOnFailure { get; set; } = string.Empty; +} diff --git a/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken/Definitions/Result.cs b/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken/Definitions/Result.cs index c6847de..6884799 100644 --- a/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken/Definitions/Result.cs +++ b/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken/Definitions/Result.cs @@ -9,17 +9,16 @@ public class Result /// Operation complete without errors. /// /// true - public bool Success { get; private set; } + public bool Success { get; init; } /// /// JToken. /// /// {{ "foo": "bar", "foobar": [ "Foo", "Bar" ]}} - public dynamic Jtoken { get; private set; } + public dynamic Jtoken { get; init; } - internal Result(bool success, object jtoken) - { - Success = success; - Jtoken = jtoken; - } + /// + /// Error information if the operation failed. + /// + public Error Error { get; init; } } \ No newline at end of file diff --git a/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken.csproj b/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken.csproj index d97bc28..70599e9 100644 --- a/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken.csproj +++ b/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken.csproj @@ -1,8 +1,8 @@  - net6.0 - 1.1.0 + net8.0 + 1.2.0 Frends Frends Frends diff --git a/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken/Helpers/ErrorHandler.cs b/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken/Helpers/ErrorHandler.cs new file mode 100644 index 0000000..851adc2 --- /dev/null +++ b/Frends.JSON.ConvertJSONStringToJToken/Frends.JSON.ConvertJSONStringToJToken/Helpers/ErrorHandler.cs @@ -0,0 +1,45 @@ +using Frends.JSON.ConvertJSONStringToJToken.Definitions; +using System; + +namespace Frends.JSON.ConvertJSONStringToJToken.Helpers; + +internal static class ErrorHandler +{ + internal static Result Handle(this Exception exception, Options options, bool throwCanceled = true) + { + ThrowIfCanceled(exception, throwCanceled); + if (options.ThrowErrorOnFailure) ThrowBaseException(exception, options.ErrorMessageOnFailure); + + return ReturnResult(exception, options.ErrorMessageOnFailure); + } + + private static void ThrowIfCanceled(Exception exception, bool throwCanceled = true) + { + if (throwCanceled && exception is OperationCanceledException) throw exception; + } + + private static void ThrowBaseException(Exception exception, string customMessage = null) + { + if (string.IsNullOrEmpty(customMessage)) + throw new Exception(exception.Message, exception); + + throw new Exception(customMessage, exception); + } + + private static Result ReturnResult(Exception exception, string customMessage = null) + { + var errorMessage = string.IsNullOrEmpty(customMessage) + ? exception.Message + : $"{customMessage}: {exception.Message}"; + + return new Result + { + Success = false, + Error = new Error + { + Message = errorMessage, + AdditionalInfo = exception, + }, + }; + } +}