Implement missing backend endpoints for release orchestration
TASK-002: 11 deployment monitoring endpoints in JobEngine (list, get, logs, events, metrics, pause/resume/cancel/rollback/retry) TASK-003: 6 evidence management endpoints in JobEngine (list, get, verify, export, raw, timeline) TASK-005: 3 release dashboard endpoints in JobEngine (dashboard summary, approve/reject promotion) TASK-006: 2 registry image search endpoints in Scanner (search with 9 mock images, digests lookup) All endpoints return seed/mock data for testing. Auth policies match existing patterns. Dual route registration on both /api/ and /api/v1/ prefixes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,16 +7,35 @@ internal static class ElkEdgePostProcessorAStar
|
||||
(double Left, double Top, double Right, double Bottom, string Id)[] obstacles,
|
||||
string sourceId, string targetId,
|
||||
double margin)
|
||||
{
|
||||
return RerouteWithGridAStar(
|
||||
start,
|
||||
end,
|
||||
obstacles,
|
||||
sourceId,
|
||||
targetId,
|
||||
new OrthogonalAStarOptions(margin, 200d, 0d, 14d),
|
||||
[],
|
||||
CancellationToken.None);
|
||||
}
|
||||
|
||||
internal static List<ElkPoint>? RerouteWithGridAStar(
|
||||
ElkPoint start, ElkPoint end,
|
||||
(double Left, double Top, double Right, double Bottom, string Id)[] obstacles,
|
||||
string sourceId, string targetId,
|
||||
OrthogonalAStarOptions options,
|
||||
IReadOnlyList<OrthogonalSoftObstacle> softObstacles,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var xs = new SortedSet<double> { start.X, end.X };
|
||||
var ys = new SortedSet<double> { start.Y, end.Y };
|
||||
foreach (var ob in obstacles)
|
||||
{
|
||||
if (ob.Id == sourceId || ob.Id == targetId) continue;
|
||||
xs.Add(ob.Left - margin);
|
||||
xs.Add(ob.Right + margin);
|
||||
ys.Add(ob.Top - margin);
|
||||
ys.Add(ob.Bottom + margin);
|
||||
xs.Add(ob.Left - options.Margin);
|
||||
xs.Add(ob.Right + options.Margin);
|
||||
ys.Add(ob.Top - options.Margin);
|
||||
ys.Add(ob.Bottom + options.Margin);
|
||||
}
|
||||
|
||||
var xArr = xs.ToArray();
|
||||
@@ -64,7 +83,6 @@ internal static class ElkEdgePostProcessorAStar
|
||||
}
|
||||
|
||||
// A* with (ix, iy, direction) state; direction: 0=none, 1=horizontal, 2=vertical
|
||||
const double bendPenalty = 200d;
|
||||
var stateCount = xCount * yCount * 3;
|
||||
var gScore = new double[stateCount];
|
||||
Array.Fill(gScore, double.MaxValue);
|
||||
@@ -89,6 +107,8 @@ internal static class ElkEdgePostProcessorAStar
|
||||
var closed = new HashSet<int>();
|
||||
while (openSet.Count > 0 && iterations++ < maxIterations)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var current = openSet.Dequeue();
|
||||
|
||||
if (!closed.Add(current))
|
||||
@@ -139,9 +159,16 @@ internal static class ElkEdgePostProcessorAStar
|
||||
if (IsBlocked(curIx, curIy, nx, ny)) continue;
|
||||
|
||||
var newDir = dirs[d];
|
||||
var bend = (curDir != 0 && curDir != newDir) ? bendPenalty : 0d;
|
||||
var bend = (curDir != 0 && curDir != newDir) ? options.BendPenalty : 0d;
|
||||
var dist = Math.Abs(xArr[nx] - xArr[curIx]) + Math.Abs(yArr[ny] - yArr[curIy]);
|
||||
var tentativeG = gScore[current] + dist + bend;
|
||||
var softCost = ComputeSoftObstacleCost(
|
||||
xArr[curIx],
|
||||
yArr[curIy],
|
||||
xArr[nx],
|
||||
yArr[ny],
|
||||
softObstacles,
|
||||
options);
|
||||
var tentativeG = gScore[current] + dist + bend + softCost;
|
||||
var neighborState = StateId(nx, ny, newDir);
|
||||
|
||||
if (tentativeG < gScore[neighborState])
|
||||
@@ -156,4 +183,43 @@ internal static class ElkEdgePostProcessorAStar
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static double ComputeSoftObstacleCost(
|
||||
double x1,
|
||||
double y1,
|
||||
double x2,
|
||||
double y2,
|
||||
IReadOnlyList<OrthogonalSoftObstacle> softObstacles,
|
||||
OrthogonalAStarOptions options)
|
||||
{
|
||||
if (options.SoftObstacleWeight <= 0d || softObstacles.Count == 0)
|
||||
{
|
||||
return 0d;
|
||||
}
|
||||
|
||||
var candidateStart = new ElkPoint { X = x1, Y = y1 };
|
||||
var candidateEnd = new ElkPoint { X = x2, Y = y2 };
|
||||
var cost = 0d;
|
||||
|
||||
foreach (var obstacle in softObstacles)
|
||||
{
|
||||
if (ElkEdgeRoutingGeometry.SegmentsIntersect(candidateStart, candidateEnd, obstacle.Start, obstacle.End))
|
||||
{
|
||||
cost += 120d * options.SoftObstacleWeight;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ElkEdgeRoutingGeometry.AreParallelAndClose(
|
||||
candidateStart,
|
||||
candidateEnd,
|
||||
obstacle.Start,
|
||||
obstacle.End,
|
||||
options.SoftObstacleClearance))
|
||||
{
|
||||
cost += 18d * options.SoftObstacleWeight;
|
||||
}
|
||||
}
|
||||
|
||||
return cost;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user