117 lines
3.8 KiB
C#
117 lines
3.8 KiB
C#
using Microsoft.AspNetCore.Mvc;
|
|
using w4c_workflows.Filters;
|
|
using w4c_workflows.Models.Nodes;
|
|
using w4c_workflows.Services.Nodes;
|
|
using w4c_workflows.Services.Security;
|
|
|
|
namespace w4c_workflows.Controllers;
|
|
|
|
/// <summary>
|
|
/// Node catalog surface. The authoring UI uses this to render its palette and
|
|
/// its generic parameter forms; it authenticates with the tenant operator key
|
|
/// like the rest of the control plane.
|
|
/// </summary>
|
|
[ApiController]
|
|
[Route("api/nodes")]
|
|
public class NodesController : ControllerBase
|
|
{
|
|
private readonly NodeBlueprintCatalog _catalog;
|
|
private readonly NodeExecutorRegistry _executors;
|
|
private readonly NodePermissionPolicy? _permissions;
|
|
|
|
public NodesController(
|
|
NodeBlueprintCatalog catalog,
|
|
NodeExecutorRegistry executors,
|
|
NodePermissionPolicy? permissions = null)
|
|
{
|
|
_catalog = catalog;
|
|
_executors = executors;
|
|
_permissions = permissions;
|
|
}
|
|
|
|
/// <summary>Lists node types for the palette, with optional search/filter.</summary>
|
|
[HttpGet]
|
|
[RequireScope("read")]
|
|
public IActionResult List(
|
|
[FromQuery] string? search,
|
|
[FromQuery] string? kind,
|
|
[FromQuery] string? category,
|
|
[FromQuery] bool includeHidden = false)
|
|
{
|
|
var results = _catalog.Search(search, kind, category);
|
|
if (!includeHidden)
|
|
results = results.Where(b => !b.Hidden).ToList();
|
|
|
|
// Hide barred node types from the palette so the author is never offered
|
|
// a step that the compiler and run kernel would refuse anyway.
|
|
if (_permissions != null)
|
|
{
|
|
var tenantId = HttpContext?.Items["TenantId"] as string;
|
|
results = results.Where(b => _permissions.IsPermitted(b, tenantId)).ToList();
|
|
}
|
|
|
|
return Ok(results.Select(Summarize));
|
|
}
|
|
|
|
/// <summary>All category labels, for the palette's category rail.</summary>
|
|
[HttpGet("categories")]
|
|
[RequireScope("read")]
|
|
public IActionResult Categories() => Ok(_catalog.Categories);
|
|
|
|
/// <summary>Full blueprint for one node type (optionally a pinned version).</summary>
|
|
[HttpGet("{type}")]
|
|
[RequireScope("read")]
|
|
public IActionResult Get(string type, [FromQuery] double? version)
|
|
{
|
|
var blueprint = _catalog.Resolve(type, version);
|
|
if (blueprint is null)
|
|
return NotFound(new { error = $"unknown node type '{type}'" });
|
|
|
|
return Ok(new NodeDetail(blueprint, _executors.CanRun(blueprint.Type)));
|
|
}
|
|
|
|
private NodeSummary Summarize(NodeBlueprint blueprint) => new(
|
|
blueprint.Type,
|
|
blueprint.Version,
|
|
blueprint.DisplayName,
|
|
blueprint.Description,
|
|
blueprint.Kind,
|
|
blueprint.Categories,
|
|
blueprint.Icon,
|
|
blueprint.IconColor,
|
|
blueprint.RunMode,
|
|
blueprint.Hidden,
|
|
blueprint.Origin,
|
|
blueprint.Inputs.Count,
|
|
blueprint.Outputs.Count,
|
|
blueprint.Inputs,
|
|
blueprint.Outputs,
|
|
blueprint.ProducesErrorBranch,
|
|
blueprint.Credentials.Any(c => c.Required),
|
|
_executors.CanRun(blueprint.Type));
|
|
}
|
|
|
|
/// <summary>Palette entry: blueprint identity plus port shape, without the full form schema.</summary>
|
|
public sealed record NodeSummary(
|
|
string Type,
|
|
double Version,
|
|
string DisplayName,
|
|
string? Description,
|
|
string Kind,
|
|
IReadOnlyList<string> Categories,
|
|
string? Icon,
|
|
string? IconColor,
|
|
string RunMode,
|
|
bool Hidden,
|
|
string Origin,
|
|
int Inputs,
|
|
int Outputs,
|
|
IReadOnlyList<NodePort> InputPorts,
|
|
IReadOnlyList<NodePort> OutputPorts,
|
|
bool HasErrorBranch,
|
|
bool RequiresCredentials,
|
|
bool Runnable);
|
|
|
|
/// <summary>Full blueprint detail plus whether an executor is installed for it.</summary>
|
|
public sealed record NodeDetail(NodeBlueprint Blueprint, bool Runnable);
|