Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Frends.JSON.Handlebars/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## [1.3.0] - 2026-08-05
### Changed
- The task now targets .NET 8.
- The task now supports configurable error handling: a new **Options** parameter lets you choose whether failures throw an exception (default) or return a result with `Success = false` and error details in the `Error` property.
- The result object now includes an `Error` property with the error message and exception details when the task fails without throwing.

## [1.2.0] - 2024-11-26
### Changed
- Removed old Handlebars DLL from the libs directory and replaced it with the NuGet package Handlebars.Net version 2.1.6.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using System.Threading;
using Frends.JSON.Handlebars.Definitions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Frends.JSON.Handlebars.UnitTests;

[TestClass]
public class ErrorHandlerTests
{
private const string CustomErrorMessage = "CustomErrorMessage";

private static Input InvalidInput() => new Input
{
Json = "not valid json {{{",
HandlebarTemplate = @"{{title}}",
HandlebarPartials = new HandlebarPartial[0]
};

private static Options DefaultOptions() => new Options { ThrowErrorOnFailure = true };

[TestMethod]
public void Should_Throw_Error_When_ThrowErrorOnFailure_Is_True()
{
var ex = Assert.ThrowsException<Exception>(() =>
JSON.Handlebars(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.Handlebars(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<Exception>(() =>
JSON.Handlebars(InvalidInput(), options, CancellationToken.None));
Assert.IsNotNull(ex);
StringAssert.Contains(ex.Message, CustomErrorMessage);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ namespace Frends.JSON.Handlebars.UnitTests;
[TestClass]
public class UnitTests
{
private static Options DefaultOptions() => new Options { ThrowErrorOnFailure = true };

[TestMethod]
public void HandlebarShouldGenerateTemplate()
{
Expand All @@ -16,7 +18,7 @@ public void HandlebarShouldGenerateTemplate()
HandlebarPartials = new HandlebarPartial[0]
};

var result = JSON.Handlebars(input, default);
var result = JSON.Handlebars(input, DefaultOptions(), default);
Assert.IsTrue(result.Success);
Assert.IsTrue(result.Data.Contains("<span>Mr.</span> <strong>Andersson</strong>"));
}
Expand All @@ -31,7 +33,7 @@ public void HandlebarShouldGeneratePartials()
HandlebarPartials = new[] { new HandlebarPartial { Template = "<strong>{{name}}</strong>", TemplateName = "strongName" } }
};

var result = JSON.Handlebars(input, default);
var result = JSON.Handlebars(input, DefaultOptions(), default);
Assert.IsTrue(result.Success);
Assert.IsTrue(result.Data.Contains("<span>Mr.</span> <strong>Andersson</strong>"));
}
Expand Down
20 changes: 20 additions & 0 deletions Frends.JSON.Handlebars/Frends.JSON.Handlebars/Definitions/Error.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;

namespace Frends.JSON.Handlebars.Definitions;

/// <summary>
/// Error information returned when the task fails and ThrowErrorOnFailure is false.
/// </summary>
public class Error
{
/// <summary>
/// Human-readable error message.
/// </summary>
/// <example>Something went wrong.</example>
public string Message { get; internal set; }

/// <summary>
/// The exception that caused the error, if available.
/// </summary>
public Exception AdditionalInfo { get; internal set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace Frends.JSON.Handlebars.Definitions;

/// <summary>
/// Options parameters.
/// </summary>
public class Options
{
/// <summary>
/// Whether to throw an exception on failure or return a Result with Success = false.
/// </summary>
/// <example>true</example>
[DefaultValue(true)]
public bool ThrowErrorOnFailure { get; set; } = true;

/// <summary>
/// Custom error message used when an error occurs.
/// </summary>
/// <example></example>
[DisplayFormat(DataFormatString = "Text")]
[DefaultValue("")]
public string ErrorMessageOnFailure { get; set; } = string.Empty;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,24 @@ public class Result
/// Operation complete without errors.
/// </summary>
/// <example>true</example>
public bool Success { get; private set; }
public bool Success { get; internal set; }

/// <summary>
/// Result data.
/// </summary>
/// <example>&lt;div&gt;&lt;span&gt;Mr.&lt;/span&gt; &lt;strong&gt;Andersson&lt;/strong&gt;&lt;/div&gt;</example>
public string Data { get; private set; }
public string Data { get; internal set; }

/// <summary>
/// Error information when Success is false.
/// </summary>
public Error Error { get; internal set; }

internal Result(bool success, string data)
{
Success = success;
Data = data;
}
}

internal Result() { }
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net6.0</TargetFrameworks>
<Version>1.2.0</Version>
<TargetFrameworks>net8.0</TargetFrameworks>
<Version>1.3.0</Version>
<Authors>Frends</Authors>
<Copyright>Frends</Copyright>
<Company>Frends</Company>
Expand Down
34 changes: 22 additions & 12 deletions Frends.JSON.Handlebars/Frends.JSON.Handlebars/Handlebars.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using Frends.JSON.Handlebars.Definitions;
using Frends.JSON.Handlebars.Helpers;
using Newtonsoft.Json.Linq;
using System;
using System.ComponentModel;
using System.IO;
using System.Reflection;
Expand All @@ -11,7 +13,7 @@ namespace Frends.JSON.Handlebars;
/// <summary>
/// JSON Task.
/// </summary>
public class JSON
public static class JSON
{
/// Mem cleanup.
static JSON()
Expand All @@ -27,22 +29,30 @@ static JSON()
/// [Documentation](https://tasks.frends.com/tasks/frends-tasks/Frends.JSON.Handlebars)
/// </summary>
/// <param name="input">Input parameters</param>
/// <param name="options">Options parameters</param>
/// <param name="cancellationToken">Token generated by frends to stop this Task.</param>
/// <returns>Object { bool Success, string Data }</returns>
public static Result Handlebars([PropertyTab] Input input, CancellationToken cancellationToken)
/// <returns>Object { bool Success, string Data, Error Error }</returns>
public static Result Handlebars([PropertyTab] Input input, [PropertyTab] Options options, CancellationToken cancellationToken)
{
var template = HandlebarsDotNet.Handlebars.Compile(input.HandlebarTemplate);
try
{
var template = HandlebarsDotNet.Handlebars.Compile(input.HandlebarTemplate);

foreach (var partial in input.HandlebarPartials)
{
cancellationToken.ThrowIfCancellationRequested();
using var reader = new StringReader(partial.Template);
var partialTemplate = HandlebarsDotNet.Handlebars.Compile(reader);
HandlebarsDotNet.Handlebars.RegisterTemplate(partial.TemplateName, partialTemplate);
}

foreach (var partial in input.HandlebarPartials)
JToken jToken = GetJTokenFromInput(input.Json);
return new Result(true, template(jToken));
}
catch (Exception ex)
{
cancellationToken.ThrowIfCancellationRequested();
using var reader = new StringReader(partial.Template);
var partialTemplate = HandlebarsDotNet.Handlebars.Compile(reader);
HandlebarsDotNet.Handlebars.RegisterTemplate(partial.TemplateName, partialTemplate);
return ex.Handle(options);
}

JToken jToken = GetJTokenFromInput(input.Json);
return new Result(true, template(jToken));
}

private static object GetJTokenFromInput(dynamic json)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using Frends.JSON.Handlebars.Definitions;

namespace Frends.JSON.Handlebars.Helpers;

internal static class ErrorHandler
{
internal static Result Handle(this Exception exception, Options options, bool throwCanceled = true)
{
if (throwCanceled && exception is OperationCanceledException) throw exception;

if (options.ThrowErrorOnFailure)
{
if (string.IsNullOrEmpty(options.ErrorMessageOnFailure))
throw new Exception(exception.Message, exception);

throw new Exception(options.ErrorMessageOnFailure, exception);
}

var errorMessage = string.IsNullOrEmpty(options.ErrorMessageOnFailure)
? exception.Message
: $"{options.ErrorMessageOnFailure}: {exception.Message}";

return new Result
{
Success = false,
Error = new Error
{
Message = errorMessage,
AdditionalInfo = exception,
},
};
}
}
Loading