Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
2 changes: 2 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@
<PackageVersion Include="NSubstitute" Version="5.3.0"/>
<PackageVersion Include="NuGet.Packaging" Version="7.0.1"/>
<PackageVersion Include="NuGet.Protocol" Version="7.0.1"/>
<PackageVersion Include="Microsoft.TeamFoundationServer.Client" Version="19.225.2"/>
<PackageVersion Include="Microsoft.VisualStudio.Services.Client" Version="19.225.2"/>
Comment on lines +194 to +195
<PackageVersion Include="Octokit" Version="14.0.0"/>
<PackageVersion Include="Open.Linq.AsyncExtensions" Version="1.2.0"/>
<PackageVersion Include="Polly" Version="8.6.5"/>
Expand Down
86 changes: 30 additions & 56 deletions Elsa.Extensions.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.12.35527.113
# Visual Studio Version 18
VisualStudioVersion = 18.1.11312.151 d18.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{527248D6-B851-4C8D-8667-E2FB0A91DABF}"
EndProject
Expand All @@ -24,9 +23,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "github", "github", "{D02123
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{3948BAF0-023F-4B43-8E77-56C3B00C6EFD}"
ProjectSection(SolutionItems) = preProject
.github\workflows\copilot-setup-steps.yml.yml = .github\workflows\copilot-setup-steps.yml.yml
.github\workflows\packages.yml = .github\workflows\packages.yml
.github\workflows\pr.yml = .github\workflows\pr.yml
.github\workflows\copilot-setup-steps.yml.yml = .github\workflows\copilot-setup-steps.yml.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "_build", "build\_build.csproj", "{4D16DD17-0BC9-476D-9B38-0A8644DD92FE}"
Expand Down Expand Up @@ -204,6 +203,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "devops", "devops", "{9B6DFC
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.DevOps.GitHub", "src\modules\devops\Elsa.DevOps.GitHub\Elsa.DevOps.GitHub.csproj", "{4E8D5DEA-6FB5-8878-0029-584C8F0D2D68}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.DevOps.AzureDevOps", "src\modules\devops\Elsa.DevOps.AzureDevOps\Elsa.DevOps.AzureDevOps.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workbench", "workbench", "{F4CD4668-A0D0-4CD7-91C1-09EE80A03C26}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Server.Web", "src\workbench\Elsa.Server.Web\Elsa.Server.Web.csproj", "{76E6A59C-4F67-40D7-9354-1370893F16A9}"
Expand Down Expand Up @@ -1091,6 +1092,18 @@ Global
{4E8D5DEA-6FB5-8878-0029-584C8F0D2D68}.Release|x64.Build.0 = Release|Any CPU
{4E8D5DEA-6FB5-8878-0029-584C8F0D2D68}.Release|x86.ActiveCfg = Release|Any CPU
{4E8D5DEA-6FB5-8878-0029-584C8F0D2D68}.Release|x86.Build.0 = Release|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x64.ActiveCfg = Debug|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x64.Build.0 = Debug|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x86.ActiveCfg = Debug|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x86.Build.0 = Debug|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.Build.0 = Release|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x64.ActiveCfg = Release|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x64.Build.0 = Release|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.ActiveCfg = Release|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.Build.0 = Release|Any CPU
{76E6A59C-4F67-40D7-9354-1370893F16A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{76E6A59C-4F67-40D7-9354-1370893F16A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{76E6A59C-4F67-40D7-9354-1370893F16A9}.Debug|x64.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -1375,62 +1388,22 @@ Global
{7055576E-1144-4726-BA88-DF89F2A13FA8}.Debug|x86.Build.0 = Debug|Any CPU
{7055576E-1144-4726-BA88-DF89F2A13FA8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7055576E-1144-4726-BA88-DF89F2A13FA8}.Release|Any CPU.Build.0 = Release|Any CPU
{E9388ADF-B222-44CC-83AF-64BF6F07C808}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E9388ADF-B222-44CC-83AF-64BF6F07C808}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E9388ADF-B222-44CC-83AF-64BF6F07C808}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E9388ADF-B222-44CC-83AF-64BF6F07C808}.Release|Any CPU.Build.0 = Release|Any CPU
{7055576E-1144-4726-BA88-DF89F2A13FA8}.Release|x64.ActiveCfg = Release|Any CPU
{7055576E-1144-4726-BA88-DF89F2A13FA8}.Release|x64.Build.0 = Release|Any CPU
{7055576E-1144-4726-BA88-DF89F2A13FA8}.Release|x86.ActiveCfg = Release|Any CPU
{7055576E-1144-4726-BA88-DF89F2A13FA8}.Release|x86.Build.0 = Release|Any CPU
{A904A0FC-9D73-4349-90A3-498053B43CD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A904A0FC-9D73-4349-90A3-498053B43CD6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A904A0FC-9D73-4349-90A3-498053B43CD6}.Debug|x64.ActiveCfg = Debug|Any CPU
{A904A0FC-9D73-4349-90A3-498053B43CD6}.Debug|x64.Build.0 = Debug|Any CPU
{A904A0FC-9D73-4349-90A3-498053B43CD6}.Debug|x86.ActiveCfg = Debug|Any CPU
{A904A0FC-9D73-4349-90A3-498053B43CD6}.Debug|x86.Build.0 = Debug|Any CPU
{A904A0FC-9D73-4349-90A3-498053B43CD6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A904A0FC-9D73-4349-90A3-498053B43CD6}.Release|Any CPU.Build.0 = Release|Any CPU
{A904A0FC-9D73-4349-90A3-498053B43CD6}.Release|x64.ActiveCfg = Release|Any CPU
{A904A0FC-9D73-4349-90A3-498053B43CD6}.Release|x64.Build.0 = Release|Any CPU
{A904A0FC-9D73-4349-90A3-498053B43CD6}.Release|x86.ActiveCfg = Release|Any CPU
{A904A0FC-9D73-4349-90A3-498053B43CD6}.Release|x86.Build.0 = Release|Any CPU
{E87F26AD-1F6B-4723-A3B8-4EC761B08382}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E87F26AD-1F6B-4723-A3B8-4EC761B08382}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E87F26AD-1F6B-4723-A3B8-4EC761B08382}.Debug|x64.ActiveCfg = Debug|Any CPU
{E87F26AD-1F6B-4723-A3B8-4EC761B08382}.Debug|x64.Build.0 = Debug|Any CPU
{E87F26AD-1F6B-4723-A3B8-4EC761B08382}.Debug|x86.ActiveCfg = Debug|Any CPU
{E87F26AD-1F6B-4723-A3B8-4EC761B08382}.Debug|x86.Build.0 = Debug|Any CPU
{E87F26AD-1F6B-4723-A3B8-4EC761B08382}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E87F26AD-1F6B-4723-A3B8-4EC761B08382}.Release|Any CPU.Build.0 = Release|Any CPU
{E87F26AD-1F6B-4723-A3B8-4EC761B08382}.Release|x64.ActiveCfg = Release|Any CPU
{E87F26AD-1F6B-4723-A3B8-4EC761B08382}.Release|x64.Build.0 = Release|Any CPU
{E87F26AD-1F6B-4723-A3B8-4EC761B08382}.Release|x86.ActiveCfg = Release|Any CPU
{E87F26AD-1F6B-4723-A3B8-4EC761B08382}.Release|x86.Build.0 = Release|Any CPU
{C3D4E5F6-7890-1234-5678-90ABCDEF1234}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C3D4E5F6-7890-1234-5678-90ABCDEF1234}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C3D4E5F6-7890-1234-5678-90ABCDEF1234}.Debug|x64.ActiveCfg = Debug|Any CPU
{C3D4E5F6-7890-1234-5678-90ABCDEF1234}.Debug|x64.Build.0 = Debug|Any CPU
{C3D4E5F6-7890-1234-5678-90ABCDEF1234}.Debug|x86.ActiveCfg = Debug|Any CPU
{C3D4E5F6-7890-1234-5678-90ABCDEF1234}.Debug|x86.Build.0 = Debug|Any CPU
{C3D4E5F6-7890-1234-5678-90ABCDEF1234}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C3D4E5F6-7890-1234-5678-90ABCDEF1234}.Release|Any CPU.Build.0 = Release|Any CPU
{C3D4E5F6-7890-1234-5678-90ABCDEF1234}.Release|x64.ActiveCfg = Release|Any CPU
{C3D4E5F6-7890-1234-5678-90ABCDEF1234}.Release|x64.Build.0 = Release|Any CPU
{C3D4E5F6-7890-1234-5678-90ABCDEF1234}.Release|x86.ActiveCfg = Release|Any CPU
{C3D4E5F6-7890-1234-5678-90ABCDEF1234}.Release|x86.Build.0 = Release|Any CPU
{AF9D6B51-19A5-451F-8225-2F1348835A3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AF9D6B51-19A5-451F-8225-2F1348835A3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AF9D6B51-19A5-451F-8225-2F1348835A3A}.Debug|x64.ActiveCfg = Debug|Any CPU
{AF9D6B51-19A5-451F-8225-2F1348835A3A}.Debug|x64.Build.0 = Debug|Any CPU
{AF9D6B51-19A5-451F-8225-2F1348835A3A}.Debug|x86.ActiveCfg = Debug|Any CPU
{AF9D6B51-19A5-451F-8225-2F1348835A3A}.Debug|x86.Build.0 = Debug|Any CPU
{AF9D6B51-19A5-451F-8225-2F1348835A3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AF9D6B51-19A5-451F-8225-2F1348835A3A}.Release|Any CPU.Build.0 = Release|Any CPU
{AF9D6B51-19A5-451F-8225-2F1348835A3A}.Release|x64.ActiveCfg = Release|Any CPU
{AF9D6B51-19A5-451F-8225-2F1348835A3A}.Release|x64.Build.0 = Release|Any CPU
{AF9D6B51-19A5-451F-8225-2F1348835A3A}.Release|x86.ActiveCfg = Release|Any CPU
{AF9D6B51-19A5-451F-8225-2F1348835A3A}.Release|x86.Build.0 = Release|Any CPU
{E9388ADF-B222-44CC-83AF-64BF6F07C808}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E9388ADF-B222-44CC-83AF-64BF6F07C808}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E9388ADF-B222-44CC-83AF-64BF6F07C808}.Debug|x64.ActiveCfg = Debug|Any CPU
{E9388ADF-B222-44CC-83AF-64BF6F07C808}.Debug|x64.Build.0 = Debug|Any CPU
{E9388ADF-B222-44CC-83AF-64BF6F07C808}.Debug|x86.ActiveCfg = Debug|Any CPU
{E9388ADF-B222-44CC-83AF-64BF6F07C808}.Debug|x86.Build.0 = Debug|Any CPU
{E9388ADF-B222-44CC-83AF-64BF6F07C808}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E9388ADF-B222-44CC-83AF-64BF6F07C808}.Release|Any CPU.Build.0 = Release|Any CPU
{E9388ADF-B222-44CC-83AF-64BF6F07C808}.Release|x64.ActiveCfg = Release|Any CPU
{E9388ADF-B222-44CC-83AF-64BF6F07C808}.Release|x64.Build.0 = Release|Any CPU
{E9388ADF-B222-44CC-83AF-64BF6F07C808}.Release|x86.ActiveCfg = Release|Any CPU
{E9388ADF-B222-44CC-83AF-64BF6F07C808}.Release|x86.Build.0 = Release|Any CPU
{65B9F528-568B-4E0E-894E-F22E0B7C64BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{65B9F528-568B-4E0E-894E-F22E0B7C64BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{65B9F528-568B-4E0E-894E-F22E0B7C64BB}.Debug|x64.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -1546,6 +1519,7 @@ Global
{159E73C1-60F9-0D39-9CA1-79EC99187FDC} = {7D32914F-1D75-4E01-A0D1-BBF67789A0E5}
{9B6DFC6C-2514-4784-9952-9B0B913434F9} = {30CF0330-4B09-4784-B499-46BED303810B}
{4E8D5DEA-6FB5-8878-0029-584C8F0D2D68} = {9B6DFC6C-2514-4784-9952-9B0B913434F9}
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890} = {9B6DFC6C-2514-4784-9952-9B0B913434F9}
{F4CD4668-A0D0-4CD7-91C1-09EE80A03C26} = {527248D6-B851-4C8D-8667-E2FB0A91DABF}
{76E6A59C-4F67-40D7-9354-1370893F16A9} = {F4CD4668-A0D0-4CD7-91C1-09EE80A03C26}
{37F3C251-9ADC-4BE8-81A5-C86C563901FF} = {F4CD4668-A0D0-4CD7-91C1-09EE80A03C26}
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Below is the current status of each extension. Icons indicate implementation pro
### 🛠 DevOps & Monitoring
| Status | Extension | Description | Module Name | Issue |
|--------|------------|-------------|-------------|-------|
| 🔲 | **Azure DevOps** | Pipelines, repositories, work items | `Elsa.AzureDevOps` | [Open Issue](https://github.com/elsa-workflows/elsa-extensions/issues/new) |
| [🚧](https://github.com/elsa-workflows/elsa-extensions/tree/main/src/modules/devops/Elsa.DevOps.AzureDevOps/README.md) | **Azure DevOps** | Pipelines, repositories, work items | `Elsa.DevOps.AzureDevOps` | [View Issue](https://github.com/elsa-workflows/elsa-extensions/issues/124) |
| [✅](https://github.com/elsa-workflows/elsa-extensions/tree/main/src/devops/Elsa.DevOps.GitHub/README.md) | **GitHub** | PR automation, repo events | `Elsa.DevOps.GitHub` | [Fixed Issue](https://github.com/elsa-workflows/elsa-extensions/issues/6) |
| 🔲 | **GitLab** | CI/CD triggers and repo management | `Elsa.GitLab` | [Open Issue](https://github.com/elsa-workflows/elsa-extensions/issues/new) |
| 🔲 | **Jenkins** | Pipeline automation and job execution | `Elsa.Jenkins` | [Open Issue](https://github.com/elsa-workflows/elsa-extensions/issues/new) |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
namespace Elsa.DevOps.AzureDevOps.Activities;

/// <summary>
/// Helpers for validating activity inputs. Use Try* in CanExecuteAsync; use Throw* in ExecuteAsync/GetConnection as a safety net.
/// </summary>
internal static class ActivityInputValidation
Comment thread
RalfvandenBurg marked this conversation as resolved.
{
public static (bool Valid, string? Error) TryValidateRequired(string? value, string parameterName)
{
if (string.IsNullOrWhiteSpace(value))
return (false, $"'{parameterName}' must be specified and non-empty.");
return (true, null);
}

public static (bool Valid, string? Error) TryValidateUri(string? value, string parameterName)
{
var (requiredOk, requiredErr) = TryValidateRequired(value, parameterName);
if (!requiredOk) return (false, requiredErr);
if (!Uri.TryCreate(value!.Trim(), UriKind.Absolute, out var uri) || !uri.IsAbsoluteUri || (uri.Scheme != "http" && uri.Scheme != "https"))
return (false, $"'{parameterName}' must be a valid HTTP or HTTPS URL.");
return (true, null);
}

public static (bool Valid, string? Error) TryValidateNonNegative(int value, string parameterName)
{
if (value < 0)
return (false, $"'{parameterName}' must be non-negative.");
return (true, null);
}

public static (bool Valid, string? Error) TryValidatePositive(int value, string parameterName)
{
if (value <= 0)
return (false, $"'{parameterName}' must be greater than zero.");
return (true, null);
}

public static void ThrowIfNullOrEmpty(string? value, string parameterName)
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException($"'{parameterName}' must be specified and non-empty.", parameterName);
}

public static void ThrowIfInvalidUri(string? value, string parameterName)
{
ThrowIfNullOrEmpty(value, parameterName);
if (!Uri.TryCreate(value!.Trim(), UriKind.Absolute, out var uri) || !uri.IsAbsoluteUri || (uri.Scheme != "http" && uri.Scheme != "https"))
throw new ArgumentException($"'{parameterName}' must be a valid HTTP or HTTPS URL.", parameterName);
}

public static void ThrowIfNegative(int value, string parameterName)
{
if (value < 0)
throw new ArgumentOutOfRangeException(parameterName, value, $"'{parameterName}' must be non-negative.");
}

public static void ThrowIfNegativeOrZero(int value, string parameterName)
{
if (value <= 0)
throw new ArgumentOutOfRangeException(parameterName, value, $"'{parameterName}' must be greater than zero.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using Elsa.DevOps.AzureDevOps.Services;
using Elsa.Workflows;
using Elsa.Workflows.Attributes;
using Elsa.Workflows.Models;
using Microsoft.VisualStudio.Services.WebApi;

namespace Elsa.DevOps.AzureDevOps.Activities;

/// <summary>
/// Generic base class inherited by all Azure DevOps activities.
/// </summary>
public abstract class AzureDevOpsActivity : Workflows.Activity
{
/// <summary>
/// The Azure DevOps organization URL (e.g. https://dev.azure.com/myorg).
/// </summary>
[Input(Description = "The Azure DevOps organization URL (e.g. https://dev.azure.com/myorg).")]
public Input<string> OrganizationUrl { get; set; } = null!;

/// <summary>
/// The personal access token (PAT) for authentication.
/// </summary>
[Input(Description = "The personal access token (PAT) for authentication.")]
public Input<string> Token { get; set; } = null!;

/// <inheritdoc />
protected override ValueTask<bool> CanExecuteAsync(ActivityExecutionContext context)
{
var organizationUrl = context.Get(OrganizationUrl);
var token = context.Get(Token);
var (urlOk, urlError) = ActivityInputValidation.TryValidateUri(organizationUrl, nameof(OrganizationUrl));
if (!urlOk)
{
context.AddExecutionLogEntry("Precondition Failed", urlError);
return new ValueTask<bool>(false);
}
var (tokenOk, tokenError) = ActivityInputValidation.TryValidateRequired(token, nameof(Token));
if (!tokenOk)
{
context.AddExecutionLogEntry("Precondition Failed", tokenError);
return new ValueTask<bool>(false);
}
return base.CanExecuteAsync(context);
}

/// <summary>
/// Gets the Azure DevOps connection.
/// </summary>
protected VssConnection GetConnection(ActivityExecutionContext context)
{
var organizationUrl = context.Get(OrganizationUrl);
var token = context.Get(Token);
ActivityInputValidation.ThrowIfInvalidUri(organizationUrl, nameof(OrganizationUrl));
ActivityInputValidation.ThrowIfNullOrEmpty(token, nameof(Token));
var factory = context.GetRequiredService<AzureDevOpsConnectionFactory>();
return factory.GetConnection(organizationUrl!.Trim(), token!);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Elsa.Workflows;
using Elsa.Workflows.Models;

namespace Elsa.DevOps.AzureDevOps.Activities;

/// <summary>
/// Generic base class inherited by all Azure DevOps trigger activities.
/// </summary>
public abstract class AzureDevOpsTriggerActivity : AzureDevOpsActivity
{
/// <summary>
/// Executes the activity.
/// </summary>
protected override ValueTask ExecuteAsync(ActivityExecutionContext context)
{
// Implementation depends on Azure DevOps Service Hooks / WebHook support
throw new NotImplementedException("Event subscription requires WebHook implementation.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using Elsa.DevOps.AzureDevOps.Activities;
using Elsa.Workflows;
using Elsa.Workflows.Attributes;
using Elsa.Workflows.Models;
using JetBrains.Annotations;
using Microsoft.TeamFoundation.Build.WebApi;

namespace Elsa.DevOps.AzureDevOps.Activities.Builds;

/// <summary>
/// Retrieves a build from Azure DevOps.
/// </summary>
[Activity(
"Elsa.AzureDevOps.Builds",
"Azure DevOps Builds",
"Retrieves a build from Azure DevOps.",
DisplayName = "Get Build")]
[UsedImplicitly]
public class GetBuild : AzureDevOpsActivity
{
/// <summary>
/// The project name or ID.
/// </summary>
[Input(Description = "The project name or ID.")]
public Input<string> Project { get; set; } = null!;

/// <summary>
/// The build ID.
/// </summary>
[Input(Description = "The build ID.")]
public Input<int> BuildId { get; set; } = null!;

/// <summary>
/// The retrieved build.
/// </summary>
[Output(Description = "The retrieved build.")]
public Output<Build> RetrievedBuild { get; set; } = null!;

/// <inheritdoc />
protected override ValueTask<bool> CanExecuteAsync(ActivityExecutionContext context)
{
var project = context.Get(Project);
var buildId = context.Get(BuildId);
var (projectOk, projectErr) = ActivityInputValidation.TryValidateRequired(project, nameof(Project));
if (!projectOk) { context.AddExecutionLogEntry("Precondition Failed", projectErr); return new ValueTask<bool>(false); }
var (idOk, idErr) = ActivityInputValidation.TryValidatePositive(buildId, nameof(BuildId));
if (!idOk) { context.AddExecutionLogEntry("Precondition Failed", idErr); return new ValueTask<bool>(false); }
return base.CanExecuteAsync(context);
}

/// <inheritdoc />
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
var project = context.Get(Project)!;
var buildId = context.Get(BuildId);
var connection = GetConnection(context);
var buildClient = connection.GetClient<BuildHttpClient>();
var build = await buildClient.GetBuildAsync(project, buildId, null, null, context.CancellationToken);
context.Set(RetrievedBuild, build);
}
}
Loading