feat(ui): reconnect registry-admin under integration hub [SPRINT-023]

Mount registry-admin routes under canonical /ops/integrations (and
/setup/integrations) with plans list, editor, and audit flows reachable
from integration-hub entry points.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
master
2026-03-08 19:25:40 +02:00
parent 38cbdb79dd
commit 2a25e7b2b0
5 changed files with 239 additions and 9 deletions

View File

@@ -1,20 +1,23 @@
/**
* Integration Hub Routes
* Updated: SPRINT_20260220_031_FE_platform_global_ops_integrations_setup_advisory_recheck
* Updated: Sprint 023 - Registry admin mounted under integrations (FE-URG-002)
*
* Canonical Integrations taxonomy:
* '' - Hub overview with health summary and category navigation
* registries - Container registries
* scm - Source control managers
* ci - CI/CD systems
* runtime-hosts - Runtime and host connector inventory
* feeds - Advisory source connectors
* vex-sources - VEX source connectors
* secrets - Secrets managers / vaults
* :id - Integration detail (standard contract template)
* '' - Hub overview with health summary and category navigation
* registries - Container registries
* scm - Source control managers
* ci - CI/CD systems
* runtime-hosts - Runtime and host connector inventory
* feeds - Advisory source connectors
* vex-sources - VEX source connectors
* secrets - Secrets managers / vaults
* registry-admin - Registry plan management and audit (Sprint 023)
* :id - Integration detail (standard contract template)
*
* Ownership boundary:
* hosts/targets/agents are managed in Topology and only aliased here.
* registry-admin is an integration-management capability, not a standalone branch.
*/
import { Routes } from '@angular/router';
@@ -116,6 +119,21 @@ export const integrationHubRoutes: Routes = [
import('./integration-activity.component').then((m) => m.IntegrationActivityComponent),
},
// Registry admin mounted as integration-management capability (Sprint 023).
// Canonical URLs (via Ops or Setup parent):
// /ops/integrations/registry-admin - Plan list
// /setup/integrations/registry-admin/plans - Plan list
// /setup/integrations/registry-admin/plans/new - Create plan
// /setup/integrations/registry-admin/plans/:planId - Edit plan
// /setup/integrations/registry-admin/audit - Audit trail
{
path: 'registry-admin',
title: 'Registry Admin',
data: { breadcrumb: 'Registry Admin' },
loadChildren: () =>
import('../registry-admin/registry-admin.routes').then((m) => m.registryAdminRoutes),
},
{
path: ':integrationId',
title: 'Integration Detail',

View File

@@ -1,8 +1,22 @@
// Registry Admin Routes
// Sprint 023: Registry Admin UI
// Updated: Sprint 023 (FE-URG-002) - Mounted under integration hub at
// /ops/integrations/registry-admin and /setup/integrations/registry-admin
// Ownership stays inside the Integrations shell.
import { Routes } from '@angular/router';
/**
* Registry Admin Routes
*
* Mounted under integration-hub/registry-admin by integration-hub.routes.ts (Sprint 023).
* Canonical URLs (relative to integrations parent):
* registry-admin - Plan list (default)
* registry-admin/plans - Plan list (explicit)
* registry-admin/plans/new - Create plan
* registry-admin/plans/:planId - Edit plan
* registry-admin/audit - Audit trail
*/
export const registryAdminRoutes: Routes = [
{
path: '',
@@ -11,6 +25,8 @@ export const registryAdminRoutes: Routes = [
children: [
{
path: '',
title: 'Registry Plans',
data: { breadcrumb: 'Plans' },
loadComponent: () =>
import('./components/plan-list.component').then(
(m) => m.PlanListComponent
@@ -18,6 +34,8 @@ export const registryAdminRoutes: Routes = [
},
{
path: 'plans',
title: 'Registry Plans',
data: { breadcrumb: 'Plans' },
loadComponent: () =>
import('./components/plan-list.component').then(
(m) => m.PlanListComponent
@@ -25,6 +43,8 @@ export const registryAdminRoutes: Routes = [
},
{
path: 'plans/new',
title: 'Create Registry Plan',
data: { breadcrumb: 'Create Plan' },
loadComponent: () =>
import('./components/plan-editor.component').then(
(m) => m.PlanEditorComponent
@@ -32,6 +52,8 @@ export const registryAdminRoutes: Routes = [
},
{
path: 'plans/:planId',
title: 'Edit Registry Plan',
data: { breadcrumb: 'Edit Plan' },
loadComponent: () =>
import('./components/plan-editor.component').then(
(m) => m.PlanEditorComponent
@@ -39,6 +61,8 @@ export const registryAdminRoutes: Routes = [
},
{
path: 'audit',
title: 'Registry Audit',
data: { breadcrumb: 'Audit' },
loadComponent: () =>
import('./components/plan-audit.component').then(
(m) => m.PlanAuditComponent

View File

@@ -0,0 +1,61 @@
/**
* Integration Hub Routes - Route structure verification
* Sprint 023: FE-URG-004
*
* Validates that registry-admin routes are mounted inside the integration
* hub under the canonical Integrations shell, before the catch-all
* :integrationId route, and without creating a standalone product branch.
*/
import { integrationHubRoutes } from '../features/integration-hub/integration-hub.routes';
describe('integrationHubRoutes', () => {
// The integration hub uses a shell component with children
const shellRoute = integrationHubRoutes[0];
const children = shellRoute?.children ?? [];
it('should have a shell route with children', () => {
expect(shellRoute).toBeDefined();
expect(shellRoute.path).toBe('');
expect(children.length).toBeGreaterThan(0);
});
it('should mount registry-admin as a child route', () => {
const registryAdminRoute = children.find((r) => r.path === 'registry-admin');
expect(registryAdminRoute).toBeDefined();
expect(registryAdminRoute!.loadChildren).toBeDefined();
expect(registryAdminRoute!.title).toBe('Registry Admin');
expect(registryAdminRoute!.data?.['breadcrumb']).toBe('Registry Admin');
});
it('should place registry-admin before the catch-all :integrationId route', () => {
const registryAdminIndex = children.findIndex((r) => r.path === 'registry-admin');
const catchAllIndex = children.findIndex((r) => r.path === ':integrationId');
expect(registryAdminIndex).toBeGreaterThan(-1);
expect(catchAllIndex).toBeGreaterThan(-1);
expect(registryAdminIndex).toBeLessThan(catchAllIndex);
});
it('should not introduce a standalone registry-admin route outside integration hub', () => {
// Only the integration hub shell contains registry-admin
// There should be no top-level registry-admin path
const topLevelPaths = integrationHubRoutes.map((r) => r.path);
expect(topLevelPaths).not.toContain('registry-admin');
});
it('should preserve existing integration hub category routes', () => {
const childPaths = children.map((r) => r.path);
expect(childPaths).toContain('registries');
expect(childPaths).toContain('scm');
expect(childPaths).toContain('ci');
expect(childPaths).toContain('secrets');
expect(childPaths).toContain('activity');
});
it('should use loadChildren for lazy-loaded registry-admin routes', () => {
const registryAdminRoute = children.find((r) => r.path === 'registry-admin');
expect(registryAdminRoute).toBeDefined();
expect(typeof registryAdminRoute!.loadChildren).toBe('function');
expect(registryAdminRoute!.loadComponent).toBeUndefined();
});
});