90 lines
2.9 KiB
C#
90 lines
2.9 KiB
C#
using System.Diagnostics;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Nexus.Api.Models;
|
|
|
|
namespace Nexus.Api.Services;
|
|
|
|
/// <summary>
|
|
/// Builds the transport-safe result metadata shared by browser, bridge and
|
|
/// OpenClaw mutation responses. URLs deliberately remain a frontend concern.
|
|
/// </summary>
|
|
public static class OperationResultFactory
|
|
{
|
|
private const int MaxOperationIdLength = 128;
|
|
|
|
public static OperationResultDto FromHttpContext(
|
|
HttpContext context,
|
|
string status,
|
|
EntityRefDto? primaryRef,
|
|
int revision = 0,
|
|
IEnumerable<EntityRefDto>? affectedRefs = null)
|
|
{
|
|
var requestedCorrelation = context.Request.Headers["X-Correlation-ID"]
|
|
.FirstOrDefault();
|
|
var operationId = IsSafeIdentifier(requestedCorrelation)
|
|
? requestedCorrelation!.Trim()
|
|
: IsSafeIdentifier(context.TraceIdentifier)
|
|
? context.TraceIdentifier.Trim()
|
|
: Guid.NewGuid().ToString("N");
|
|
var traceId = Activity.Current?.Id;
|
|
if (string.IsNullOrWhiteSpace(traceId))
|
|
{
|
|
var traceParent = context.Request.Headers["traceparent"].FirstOrDefault();
|
|
traceId = IsSafeIdentifier(traceParent) ? traceParent!.Trim() : null;
|
|
}
|
|
|
|
context.Response.Headers["X-Correlation-ID"] = operationId;
|
|
return Create(
|
|
operationId,
|
|
status,
|
|
revision,
|
|
primaryRef,
|
|
affectedRefs,
|
|
traceId);
|
|
}
|
|
|
|
public static OperationResultDto FromInvocation(
|
|
OpenClawInvocationContext context,
|
|
string status,
|
|
EntityRefDto? primaryRef,
|
|
int revision = 0,
|
|
IEnumerable<EntityRefDto>? affectedRefs = null)
|
|
=> Create(
|
|
context.CorrelationId,
|
|
status,
|
|
revision,
|
|
primaryRef,
|
|
affectedRefs,
|
|
Activity.Current?.Id ?? context.TraceParent);
|
|
|
|
public static OperationResultDto Create(
|
|
string operationId,
|
|
string status,
|
|
int revision,
|
|
EntityRefDto? primaryRef,
|
|
IEnumerable<EntityRefDto>? affectedRefs = null,
|
|
string? traceId = null)
|
|
{
|
|
var uniqueAffected = (affectedRefs ?? [])
|
|
.Where(reference =>
|
|
primaryRef is null ||
|
|
!string.Equals(reference.Type, primaryRef.Type, StringComparison.Ordinal) ||
|
|
!string.Equals(reference.Id, primaryRef.Id, StringComparison.Ordinal))
|
|
.DistinctBy(reference => (reference.Type, reference.Id))
|
|
.ToArray();
|
|
|
|
return new OperationResultDto(
|
|
operationId,
|
|
status,
|
|
Math.Max(0, revision),
|
|
primaryRef,
|
|
uniqueAffected,
|
|
traceId);
|
|
}
|
|
|
|
private static bool IsSafeIdentifier(string? value)
|
|
=> !string.IsNullOrWhiteSpace(value)
|
|
&& value.Length <= MaxOperationIdLength
|
|
&& !value.Any(char.IsControl);
|
|
}
|