using Microsoft.AspNetCore.Mvc;
using StellaOps.Auth.ServerIntegration.Tenancy;
using StellaOps.JobEngine.Infrastructure.Repositories;
using StellaOps.JobEngine.WebService.Contracts;
using StellaOps.JobEngine.WebService.Services;
namespace StellaOps.JobEngine.WebService.Endpoints;
///
/// REST API endpoints for job sources.
///
public static class SourceEndpoints
{
///
/// Maps source endpoints to the route builder.
///
public static RouteGroupBuilder MapSourceEndpoints(this IEndpointRouteBuilder app)
{
var group = app.MapGroup("/api/v1/jobengine/sources")
.WithTags("Orchestrator Sources")
.RequireAuthorization(JobEnginePolicies.Read)
.RequireTenant();
group.MapGet(string.Empty, ListSources)
.WithName("Orchestrator_ListSources")
.WithDescription("Return a cursor-paginated list of job sources registered for the calling tenant, optionally filtered by source type and enabled state. Sources represent the external integrations or internal triggers that produce jobs for the orchestrator.");
group.MapGet("{sourceId:guid}", GetSource)
.WithName("Orchestrator_GetSource")
.WithDescription("Return the configuration and status record for a single job source identified by its GUID. Returns 404 when no source with that ID exists in the tenant.");
return group;
}
private static async Task ListSources(
HttpContext context,
[FromServices] TenantResolver tenantResolver,
[FromServices] ISourceRepository repository,
[FromQuery] string? sourceType = null,
[FromQuery] bool? enabled = null,
[FromQuery] int? limit = null,
[FromQuery] string? cursor = null,
CancellationToken cancellationToken = default)
{
try
{
var tenantId = tenantResolver.Resolve(context);
var effectiveLimit = EndpointHelpers.GetLimit(limit);
var offset = EndpointHelpers.ParseCursorOffset(cursor);
var sources = await repository.ListAsync(
tenantId,
sourceType,
enabled,
effectiveLimit,
offset,
cancellationToken).ConfigureAwait(false);
var responses = sources.Select(SourceResponse.FromDomain).ToList();
var nextCursor = EndpointHelpers.CreateNextCursor(offset, effectiveLimit, responses.Count);
return Results.Ok(new SourceListResponse(responses, nextCursor));
}
catch (InvalidOperationException ex)
{
return Results.BadRequest(new { error = ex.Message });
}
}
private static async Task GetSource(
HttpContext context,
[FromRoute] Guid sourceId,
[FromServices] TenantResolver tenantResolver,
[FromServices] ISourceRepository repository,
CancellationToken cancellationToken = default)
{
try
{
var tenantId = tenantResolver.Resolve(context);
var source = await repository.GetByIdAsync(tenantId, sourceId, cancellationToken).ConfigureAwait(false);
if (source is null)
{
return Results.NotFound();
}
return Results.Ok(SourceResponse.FromDomain(source));
}
catch (InvalidOperationException ex)
{
return Results.BadRequest(new { error = ex.Message });
}
}
}