Skip to content
This repository was archived by the owner on Apr 13, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
39 changes: 29 additions & 10 deletions Frends.Template/.template.config/template.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,29 @@
"sourceName": "Frends.Echo.Execute",
"preferNameDirectory": true,
"symbols": {
"taskName": {
"FullTaskName": {
"type": "parameter",
"datatype": "string",
"defaultValue": "Execute",
"replaces": "Execute"
"defaultValue": "Frends.Echo.Execute",
"replaces": "Frends.Echo.Execute",
"description": "Full name in format: Frends.ClassName.MethodName"
},
"className": {
"type": "parameter",
"datatype": "string",
"defaultValue": "Echo",
"type": "derived",
"valueSource": "FullTaskName",
"valueTransform": "GetClassName",
"replaces": "Echo"
},
"taskName": {
"type": "derived",
"valueSource": "FullTaskName",
"valueTransform": "GetMethodName",
"replaces": "Execute"
},
"workflows": {
"type": "parameter",
"datatype": "string",
"defaultValue": "Execute",
"type": "derived",
"valueSource": "FullTaskName",
"valueTransform": "GetMethodName",
"FileRename": "Execute"
},
"CurrentDate": {
Expand All @@ -49,6 +56,18 @@
"replaces": "GeneratedYear"
}
},
"forms": {
"GetClassName": {
"identifier": "replace",
"pattern": "^.*\\.([^.]+)\\.[^.]+$",
"replacement": "$1"
},
"GetMethodName": {
"identifier": "replace",
"pattern": "^.*\\.([^.]+)$",
"replacement": "$1"
}
Comment thread
MatteoDelOmbra marked this conversation as resolved.
},
"SpecialCustomOperations": {
"**/*.md": {
"operations": [
Expand Down Expand Up @@ -151,4 +170,4 @@
"continueOnError": false
}
]
}
}
2 changes: 1 addition & 1 deletion Frends.Template/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

## [1.0.0] - GeneratedDate

### Changed
### Added

- Initial implementation
54 changes: 54 additions & 0 deletions Frends.Template/Frends.Echo.Execute.Tests/ErrorHandlerTest..cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using System.Threading;
using Frends.Echo.Execute.Definitions;
using NUnit.Framework;

namespace Frends.Echo.Execute.Tests;

// TODO: Adjust the test to use a real invalid Input scenario (e.g., missing or malformed data)
[TestFixture]
public class ErrorHandlerTest
{
private const string CustomErrorMessage = "CustomErrorMesasge";
Comment thread
MatteoDelOmbra marked this conversation as resolved.
Outdated

[Test]
public void Should_Throw_Error_When_ThrowErrorOnFailure_Is_True()
{
var ex = Assert.Throws<Exception>(() =>
Echo.Execute(DefaultInput(), DefaultConnection(), DefaultOptions(), CancellationToken.None));
Assert.That(ex, Is.Not.Null);
}

[Test]
public void Should_Return_Failed_Result_When_ThrowErrorOnFailure_Is_False()
{
var options = DefaultOptions();
options.ThrowErrorOnFailure = false;
var result = Echo.Execute(DefaultInput(), DefaultConnection(), options, CancellationToken.None);
Assert.That(result.Success, Is.False);
}

[Test]
public void Should_Use_Custom_ErrorMessageOnFailure()
{
var options = DefaultOptions();
options.ErrorMessageOnFailure = CustomErrorMessage;
var ex = Assert.Throws<Exception>(() =>
Echo.Execute(DefaultInput(), DefaultConnection(), options, CancellationToken.None));
Assert.That(ex, Is.Not.Null);
Assert.That(ex.Message, Contains.Substring(CustomErrorMessage));
}

private static Input DefaultInput() => new()
{
Repeat = -1, // Invalid value to cause an exception
};

private static Connection DefaultConnection() => new();

private static Options DefaultOptions() => new()
{
ThrowErrorOnFailure = true,
ErrorMessageOnFailure = string.Empty,
};
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
using System.Diagnostics.CodeAnalysis;

[assembly: SuppressMessage("StyleCop.CSharp.SpecialRules", "SA0001::XmlCommentAnalysisDisabled", Justification = "Following Frends documentation guidelines")]
[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1000:KeywordsMustBeSpacedCorrectly", Justification = "Incorrect for .NET 8.0")]
[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1010:OpeningSquareBracketsMustBeSpacedCorrectly", Justification = "Incorrect for .NET 8.0")]
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:PrefixLocalCallsWithThis", Justification = "Following Frends documentation guidelines")]
[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1200:UsingDirectivesMustBePlacedWithinNamespace", Justification = "Following Frends documentation guidelines")]
[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1206:DeclarationKeywordsMustFollowOrder", Justification = "Incorrect for .NET 8.0")]
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:BracesMustNotBeOmitted", Justification = "Following Frends documentation guidelines")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Following Frends documentation guidelines")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:FileMustHaveHeader", Justification = "Following Frends documentation guidelines")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:FileMustHaveHeader", Justification = "Following Frends documentation guidelines")]
4 changes: 2 additions & 2 deletions Frends.Template/Frends.Echo.Execute/Definitions/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ public class Options
/// </summary>
/// <example>false</example>
[DefaultValue(true)]
public bool ThrowErrorOnFailure { get; set; }
public bool ThrowErrorOnFailure { get; set; } = true;

/// <summary>
/// Overrides the error message on failure.
/// </summary>
/// <example>Custom error message</example>
[DisplayFormat(DataFormatString = "Text")]
[DefaultValue("")]
public string ErrorMessageOnFailure { get; set; }
public string ErrorMessageOnFailure { get; set; } = string.Empty;
}
32 changes: 6 additions & 26 deletions Frends.Template/Frends.Echo.Execute/Frends.Echo.Execute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Threading;
using Frends.Echo.Execute.Definitions;
using Frends.Echo.Execute.Helpers;

namespace Frends.Echo.Execute;

Expand Down Expand Up @@ -36,6 +37,9 @@ public static Result Execute(
// and checked during long-running operations, e.g., loops
cancellationToken.ThrowIfCancellationRequested();

if (input.Repeat < 0)
throw new Exception("Repeat count cannot be negative.");

var output = string.Join(options.Delimiter, Enumerable.Repeat(input.Content, input.Repeat));

return new Result
Expand All @@ -45,33 +49,9 @@ public static Result Execute(
Error = null,
};
}
catch (Exception e) when (e is not OperationCanceledException)
catch (Exception ex)
{
if (options.ThrowErrorOnFailure)
{
if (string.IsNullOrEmpty(options.ErrorMessageOnFailure))
throw new Exception(e.Message, e);

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

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

return new Result
{
Success = false,
Output = null,
Error = new Error
{
Message = errorMessage,
AdditionalInfo = new
{
Exception = e,
},
},
};
return ErrorHandler.Handle(ex, options);
}
}
}
16 changes: 14 additions & 2 deletions Frends.Template/Frends.Echo.Execute/Frends.Echo.Execute.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,21 @@
</ItemGroup>

<ItemGroup>
<None Include="FrendsTaskMetadata.json" Pack="true" PackagePath="/">
<AdditionalFiles Include="FrendsTaskMetadata.json" Pack="true" PackagePath="/">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</AdditionalFiles>
</ItemGroup>

<ItemGroup>
<Content Include="migration.json" PackagePath="/" Pack="true" />
<Content Include="../CHANGELOG.md" PackagePath="/" Pack="true" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="FrendsTaskAnalyzers" Version="*">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Comment thread
MatteoDelOmbra marked this conversation as resolved.
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions Frends.Template/Frends.Echo.Execute/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1200:UsingDirectivesMustBePlacedWithinNamespace", Justification = "Following Frends documentation guidelines")]
[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1206:DeclarationKeywordsMustFollowOrder", Justification = "Incorrect for .NET 8.0")]
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:BracesMustNotBeOmitted", Justification = "Following Frends documentation guidelines")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Documentation checked by custom analyzers")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1623:PropertySummaryDocumentationMustMatchAccessors", Justification = "Following Frends documentation guidelines")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1629:DocumentationTextMustEndWithAPeriod", Justification = "Following Frends Tasks guidelines")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:FileMustHaveHeader", Justification = "Following Frends documentation guidelines")]
41 changes: 41 additions & 0 deletions Frends.Template/Frends.Echo.Execute/Helpers/ErrorHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using Frends.Echo.Execute.Definitions;

namespace Frends.Echo.Execute.Helpers;

/// <summary>
/// Provides centralized error handling for task operations.
/// </summary>
public static class ErrorHandler
{
/// <summary>
/// Handles an exception that occurs during task execution.
/// </summary>
/// <param name="ex">The exception that occurred during task execution.</param>
/// <param name="options">Configuration options that determine how errors should be handled.</param>
/// <returns>A Result object containing error information when ThrowErrorOnFailure is false.</returns>
public static Result Handle(Exception ex, Options options)
Comment thread
MatteoDelOmbra marked this conversation as resolved.
Outdated
{
if (options.ThrowErrorOnFailure)
{
if (!string.IsNullOrEmpty(options.ErrorMessageOnFailure))
{
throw new Exception(options.ErrorMessageOnFailure, ex);
}

throw ex;
}

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

var error = new Error
{
Message = errorMessage,
AdditionalInfo = ex,
};

return new Result { Success = false, Error = error };
}
}
12 changes: 12 additions & 0 deletions Frends.Template/Frends.Echo.Execute/migration.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[
{
"Task": "Frends.Echo.Execute",
"Migrations": [
{
"Version": "1.0.0",
"Description": "",
"Migration": []
}
]
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ jobs:
with:
workdir: Frends.Echo.Execute
dotnet_version: 8.0.x
strict_analyzers: true
secrets:
badge_service_api_key: ${{ secrets.BADGE_SERVICE_API_KEY }}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ jobs:
with:
workdir: Frends.Echo.Execute
dotnet_version: 8.0.x
strict_analyzers: true
secrets:
badge_service_api_key: ${{ secrets.BADGE_SERVICE_API_KEY }}
test_feed_api_key: ${{ secrets.TASKS_TEST_FEED_API_KEY }}
1 change: 1 addition & 0 deletions Frends.Template/workflows/Execute_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ jobs:
with:
workdir: Frends.Echo.Execute
dotnet_version: 8.0.x
strict_analyzers: true
secrets:
feed_api_key: ${{ secrets.TASKS_FEED_API_KEY }}
11 changes: 4 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ You need to clone the repository for the new Task. After cloning, move to that f

You can create a new Task by running the following command in the Task's repository folder.

`dotnet new frendstasktemplate --name namespaceForTask --className classNameForTask --taskName TaskName --workflows TaskName --allow-scripts yes`
`dotnet new frendstasktemplate --FullTaskName Frends.ClassName.TaskName --allow-scripts yes`

## Get help using the template

Expand Down Expand Up @@ -63,12 +63,9 @@ Options:
--allow-scripts <No|Prompt|Yes> Specifies if post action scripts should run. [default: Prompt]

Template options:
-ta, --taskName <taskName> Type: string
Default: Execute
-c, --className <className> Type: string
Default: Echo
-w, --workflows <workflows> Type: string
Default: Execute
--FullTaskName <FullTaskName> Full name in format: Frends.ClassName.TaskName
Type: string
Default: Frends.Echo.Execute
```

## Update the template
Expand Down