refactor: rename AI Integration Hub to Integration Hub with UX improvements

Made-with: Cursor
This commit is contained in:
taylanbakircioglu
2026-03-29 19:56:13 +03:00
parent dd7039afd0
commit bcbef2b896
13 changed files with 991 additions and 863 deletions
+13 -13
View File
@@ -29,7 +29,7 @@ Centralized multi-cluster Kubernetes observability platform — real-time depend
- [Change Detection](#change-detection)
- [Impact Simulation](#impact-simulation)
- [Blast Radius Oracle](#blast-radius-oracle)
- [AI Integration Hub](#ai-integration-hub)
- [Integration Hub](#integration-hub)
- [Pod & Deployment Annotations](#pod--deployment-annotations)
- [Activity Monitor](#activity-monitor)
- [Events Timeline](#events-timeline)
@@ -101,8 +101,8 @@ Unlike traditional APM tools that require agent installation or service meshes t
- **Role-Based Access Control** — User and role management with Admin, Viewer, and custom roles with granular permissions
- **Multi-Tab Dashboard** — Overview, Operations, Security, Network, Changes, and Workloads tabs with real-time metrics
- **CI/CD Pipeline Integration** — Blast radius checks for Azure DevOps, GitHub Actions, Jenkins, and GitLab CI
- **AI Integration Hub** — Three-step wizard (Configure → Preview → Integration Code) for dependency data integrations with ready-to-use code snippets for Azure DevOps, GitHub Actions, Jenkins, GitLab CI, Python, JavaScript, and cURL — includes a Blast Radius tab for pre-deployment risk assessment pipeline integration
- **Pod & Deployment Annotations** — Full annotation support including automatic merge of Deployment/StatefulSet annotations into pods, visible across Map, Network Explorer, Application Inventory, Impact Simulation, and AI Integration Hub
- **Integration Hub** — Four-step wizard (Integration Type → Configure → Preview → Integration Code) for dependency data and blast radius gate integrations with ready-to-use code snippets for Azure DevOps, GitHub Actions, Jenkins, GitLab CI, Python, JavaScript, and cURL
- **Pod & Deployment Annotations** — Full annotation support including automatic merge of Deployment/StatefulSet annotations into pods, visible across Map, Network Explorer, Application Inventory, Impact Simulation, and Integration Hub
- **API Key Management** — Generate, expire, and revoke API keys for secure programmatic access alongside JWT authentication
---
@@ -251,9 +251,9 @@ The analysis wizard guides you through creating a new eBPF data collection sessi
![API Documentation](docs/screenshots/APIs.png)
**AI Integration Hub**Three-step wizard for dependency data integrations. Configure analysis scope and service identification method, preview dependency results with upstream/downstream statistics, and generate ready-to-use code snippets (Pipeline YAML, cURL, Python, JavaScript) with a dedicated Blast Radius tab for pre-deployment risk assessment:
**Integration Hub**Four-step wizard for dependency data and blast radius gate integrations. Select integration type, configure analysis scope and service identification method, preview dependency results with upstream/downstream statistics, and generate ready-to-use code snippets (Pipeline YAML, cURL, Python, JavaScript):
![AI Integration Hub](docs/screenshots/ai-integration-hub.png)
![Integration Hub](docs/screenshots/integration-hub.png)
---
@@ -644,7 +644,7 @@ The Blast Radius Oracle provides a **pre-deployment impact assessment API** desi
```yaml
- script: |
RESULT=$(curl -s -X POST "$(FLOWFISH_URL)/api/v1/blast-radius/assess" \
-H "Authorization: Bearer $(FLOWFISH_TOKEN)" \
-H "X-API-Key: $(FLOWFISH_API_KEY)" \
-H "Content-Type: application/json" \
-d '{"cluster_id": $(CLUSTER_ID), "change": {"type": "image_update", "target": "payment-service", "namespace": "production"}}')
echo "Risk Score: $(echo $RESULT | jq '.risk_score')"
@@ -657,7 +657,7 @@ The Blast Radius Oracle provides a **pre-deployment impact assessment API** desi
- name: Blast Radius Check
run: |
RESULT=$(curl -s -X POST "${{ secrets.FLOWFISH_URL }}/api/v1/blast-radius/assess" \
-H "Authorization: Bearer ${{ secrets.FLOWFISH_TOKEN }}" \
-H "X-API-Key: ${{ secrets.FLOWFISH_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{"cluster_id": ${{ env.CLUSTER_ID }}, "change": {"type": "image_update", "target": "payment-service", "namespace": "production"}}')
echo "Risk: $(echo $RESULT | jq -r '.risk_level')"
@@ -671,7 +671,7 @@ stage('Blast Radius Check') {
def result = httpRequest(
url: "${FLOWFISH_URL}/api/v1/blast-radius/assess",
httpMode: 'POST',
customHeaders: [[name: 'Authorization', value: "Bearer ${FLOWFISH_TOKEN}"]],
customHeaders: [[name: 'X-API-Key', value: "${FLOWFISH_API_KEY}"]],
requestBody: '{"cluster_id": ' + CLUSTER_ID + ', "change": {"type": "image_update", "target": "payment-service", "namespace": "production"}}'
)
def json = readJSON text: result.content
@@ -691,13 +691,13 @@ The UI includes a live test runner for ad-hoc assessments: select cluster, analy
Full history of all assessments with assessment ID, timestamp, target, namespace, change type, risk score, risk level, affected count, and pipeline source. Click any entry for detailed JSON view.
The Overview tab also includes a cross-link to the **AI Integration Hub** for users who need dependency data integrations alongside blast radius assessments.
The Overview tab also includes a cross-link to the **Integration Hub** for users who need dependency data integrations alongside blast radius assessments.
---
### AI Integration Hub
### Integration Hub
The AI Integration Hub provides a **three-step guided wizard** (Configure → Preview → Integration Code) for setting up dependency data integrations with AI code agents and CI/CD pipelines. It enables cross-project impact analysis by exposing Flowfish dependency data through a compact, categorized JSON API. The wizard also includes a Blast Radius tab for generating pre-deployment risk assessment pipeline snippets, with a cross-link to the Blast Radius Oracle page for interactive testing.
The Integration Hub provides a **four-step guided wizard** (Integration Type → Configure → Preview → Integration Code) for setting up CI/CD pipeline integrations. Users choose between two integration types: **Dependency Analysis** for cross-project impact analysis via categorized dependency data, or **Blast Radius Gate** for pre-deployment risk scoring. The wizard generates ready-to-use pipeline snippets for all major CI/CD platforms with consistent `X-API-Key` authentication.
#### Use Case: Cross-Project Impact Analysis
@@ -763,7 +763,7 @@ Annotations are visible across:
- **Application Inventory** — Searchable annotation column with expandable detail view
- **Network Explorer** — Metadata column with annotations included in CSV exports
- **Impact Simulation** — Affected service annotations in results and exports
- **AI Integration Hub** — Annotations exposed in dependency summary API responses
- **Integration Hub** — Annotations exposed in dependency summary API responses
Internal Kubernetes annotations (`kubectl.kubernetes.io/`, `kubernetes.io/`, `openshift.io/` prefixes) are filtered during ingestion to reduce noise, and values exceeding 500 characters are excluded.
@@ -1556,7 +1556,7 @@ Interactive docs at `http://localhost:8000/api/docs` (Swagger) or `http://localh
| **Analyses** | `/api/v1/analyses` | Create, start, stop, delete analyses |
| **Dependencies** | `/api/v1/dependencies` | Graph queries, upstream/downstream |
| **Communications** | `/api/v1/communications` | Network flow data |
| **AI Integration** | `/api/v1/communications/dependencies/*` | Dependency summary, stream, batch, diff, and impact for AI agents and CI/CD pipelines |
| **Integration** | `/api/v1/communications/dependencies/*` | Dependency summary, stream, batch, diff, and impact for CI/CD pipelines |
| **Changes** | `/api/v1/changes` | Change detection events |
| **Blast Radius** | `/api/v1/blast-radius` | Pre-deployment assessment |
| **Simulation** | `/api/v1/simulation` | Impact simulation |
+10 -10
View File
@@ -12,7 +12,7 @@ info:
- **Real-Time Dependency Map** — Live visualization of service dependency graphs
- **Anomaly Detection** — Intelligent anomaly detection powered by statistical analysis
- **Multi-Cluster** — Manage multiple clusters from a single interface
- **AI Integration** — Dependency intelligence APIs for AI agents and CI/CD pipeline integration
- **Integration** — Dependency intelligence APIs for CI/CD pipeline integration
- **Pod & Deployment Annotations** — Rich metadata enrichment including annotations merged from owning Deployments/StatefulSets
- **Import/Export** — Data import and export in multiple formats
@@ -57,8 +57,8 @@ tags:
description: Kubernetes workload inventory (Pod, Deployment, Service, StatefulSet) with labels and annotations
- name: Communications
description: Service-to-service communication records and dependency graphs
- name: AI Integration
description: Dependency intelligence APIs for AI agents and CI/CD pipeline integrations — dependency discovery, impact analysis, and cross-project dependency mapping
- name: Integration
description: Dependency intelligence APIs for CI/CD pipeline integrations — dependency discovery, impact analysis, and cross-project dependency mapping
- name: Dependencies
description: Dependency map and graph queries
- name: Anomalies
@@ -1231,7 +1231,7 @@ paths:
/communications/dependencies/stream:
get:
tags:
- AI Integration
- Integration
- Communications
summary: Get pod dependencies as upstream/downstream
description: |
@@ -1340,7 +1340,7 @@ paths:
/communications/dependencies/batch:
post:
tags:
- AI Integration
- Integration
- Communications
summary: Batch query dependencies for multiple services
requestBody:
@@ -1360,7 +1360,7 @@ paths:
/communications/dependencies/diff:
get:
tags:
- AI Integration
- Integration
- Communications
summary: Get dependency diff between two analyses
parameters:
@@ -1401,7 +1401,7 @@ paths:
/communications/dependencies/impact:
get:
tags:
- AI Integration
- Integration
- Communications
summary: Dependency impact analysis by change type
parameters:
@@ -1469,10 +1469,10 @@ paths:
/communications/dependencies/summary:
get:
tags:
- AI Integration
summary: AI-agent-friendly dependency summary grouped by category
- Integration
summary: Dependency summary grouped by category for CI/CD integration
description: |
Returns a compact, grouped dependency summary for AI agents and CI/CD pipelines.
Returns a compact, grouped dependency summary for CI/CD pipelines and automation.
Dependencies are grouped by service_category. Annotations and labels are prominently
exposed for cross-project impact analysis (e.g. git-repo URLs, team info).
Requires at least one analysis_id and one search parameter.
+1 -1
View File
@@ -200,7 +200,7 @@ openapi_tags = [
{"name": "Analyses", "description": "Traffic analysis creation, lifecycle, and run management"},
{"name": "Workloads", "description": "Kubernetes workload inventory and metadata"},
{"name": "Communications", "description": "Service-to-service communication graph, dependency map, and topology"},
{"name": "AI Integration", "description": "AI agent and CI/CD pipeline integration endpoints for dependency discovery, impact analysis, and cross-project dependency mapping"},
{"name": "Integration", "description": "CI/CD pipeline integration endpoints for dependency discovery, impact analysis, and cross-project dependency mapping"},
{"name": "Impact Analysis", "description": "Blast radius assessment, impact simulation, and pre-deployment risk evaluation"},
{"name": "Events", "description": "eBPF event statistics, queries, and timeline"},
{"name": "Changes", "description": "Change detection and infrastructure drift tracking"},
+5 -5
View File
@@ -1351,7 +1351,7 @@ async def _get_neo4j_error_stats(
)
@router.get("/dependencies/stream", tags=["AI Integration"])
@router.get("/dependencies/stream", tags=["Integration"])
async def find_pod_dependencies(
analysis_id: Optional[int] = Query(None, description="Analysis ID for scope"),
cluster_id: Optional[int] = Query(None, description="Cluster ID for scope"),
@@ -1431,7 +1431,7 @@ async def find_pod_dependencies(
)
@router.post("/dependencies/batch", tags=["AI Integration"])
@router.post("/dependencies/batch", tags=["Integration"])
async def batch_find_dependencies(
request: dict,
current_user: dict = Depends(get_current_user)
@@ -1452,7 +1452,7 @@ async def batch_find_dependencies(
raise HTTPException(status_code=500, detail=str(e))
@router.get("/dependencies/diff", tags=["AI Integration"])
@router.get("/dependencies/diff", tags=["Integration"])
async def diff_dependencies(
analysis_id_before: str = Query(..., description="Analysis ID before"),
analysis_id_after: str = Query(..., description="Analysis ID after"),
@@ -1491,7 +1491,7 @@ async def diff_dependencies(
raise HTTPException(status_code=500, detail=str(e))
@router.get("/dependencies/summary", tags=["AI Integration"])
@router.get("/dependencies/summary", tags=["Integration"])
async def get_dependency_summary(
analysis_ids: List[int] = Query(..., description="Analysis IDs (required, at least one)"),
cluster_id: Optional[int] = Query(None, description="Cluster ID"),
@@ -1549,7 +1549,7 @@ async def get_dependency_summary(
raise HTTPException(status_code=500, detail=str(e))
@router.get("/dependencies/impact", tags=["AI Integration"])
@router.get("/dependencies/impact", tags=["Integration"])
async def get_dependency_impact(
analysis_id: Optional[int] = Query(None, description="Analysis ID"),
cluster_id: Optional[int] = Query(None, description="Cluster ID"),
+3 -3
View File
@@ -1255,8 +1255,8 @@ Range: 0-100
- Security events
- Audit events
### 2.10. AI Integration Hub
- Three-step guided wizard (Configure → Preview → Integration Code) for dependency data integrations
### 2.10. Integration Hub
- Four-step guided wizard (Integration Type → Configure → Preview → Integration Code) with two integration paths: Dependency Analysis and Blast Radius Gate
- Configure step: analysis selection, service identification method (Annotation, Label, Namespace + Deployment, Pod Name, Advanced), search depth, live test query
- Preview step: dependency summary with upstream service metadata, downstream/caller statistics, matched services table
- Integration Code step: tabbed snippet generation (Pipeline YAML, cURL, Python, JavaScript, Blast Radius)
@@ -2105,7 +2105,7 @@ Would you like me to create rollback CR?
| **Authentication** | ✅ OAuth/SSO, K8s SA | ✅ | ✅ |
| **API Key Management** | ✅ | ✅ | ✅ |
| **Pod & Deployment Annotations** | ✅ | ✅ | ✅ |
| **AI Integration Hub** | ❌ | ✅ | ✅ |
| **Integration Hub** | ❌ | ✅ | ✅ |
| **Multi-Cluster** | ❌ | ✅ | ✅ |
| **Change Detection** | ❌ | ✅ | ✅ |
| **Anomaly Detection** | ❌ | ✅ LLM | ✅ LLM + ML models |
+2 -2
View File
@@ -877,7 +877,7 @@ frontend/
│ │ ├── PolicySimulation.tsx
│ │ ├── UserManagement.tsx
│ │ ├── Settings.tsx
│ │ └── Integrations.tsx
│ │ └── IntegrationHub.tsx
│ ├── store/ # Redux store
│ │ ├── index.ts
│ │ ├── slices/
@@ -978,7 +978,7 @@ frontend/
The HTTP API is organized under `/api/v1/` (see the endpoint tree under **Backend API (FastAPI)** above). **Authentication** supports both **JWT** (standard Bearer tokens) and **API keys** supplied via the **`X-API-Key`** header, so automation, agents, and CI/CD can authenticate without an interactive login flow.
**AI Integration** exposes dependency-oriented endpoints for AI agents and CI/CD pipelines (tagged **AI Integration** in the OpenAPI spec), including:
**Integration** exposes dependency-oriented endpoints for CI/CD pipelines (tagged **Integration** in the OpenAPI spec), including:
| Area | Path (relative to `/api/v1`) |
|------|------------------------------|
+4 -3
View File
@@ -24,7 +24,7 @@ import Settings from './pages/Settings';
import UserManagement from './pages/UserManagement';
import BlastRadiusOracle from './pages/BlastRadiusOracle';
import APIDocumentation from './pages/APIDocumentation';
import AIIntegrationHub from './pages/AIIntegrationHub';
import IntegrationHub from './pages/IntegrationHub';
// Build Timestamp forces unique webpack hash on each build
const BUILD_TIMESTAMP = process.env.REACT_APP_BUILD_TIMESTAMP || 'dev';
@@ -76,8 +76,9 @@ const App: React.FC = () => {
<Route path="impact/simulation" element={<ImpactSimulation />} />
<Route path="impact/blast-radius" element={<BlastRadiusOracle />} />
{/* AI Integration routes */}
<Route path="integration/ai-hub" element={<AIIntegrationHub />} />
{/* Integration routes */}
<Route path="integration/hub" element={<IntegrationHub />} />
<Route path="integration/ai-hub" element={<Navigate to="/integration/hub" replace />} />
{/* Observability routes */}
<Route path="observability/activity" element={<ActivityMonitor />} />
+5 -6
View File
@@ -18,7 +18,6 @@ import {
RadarChartOutlined,
AlertOutlined,
RocketOutlined,
RobotOutlined,
} from '@ant-design/icons';
import FlowfishLogo from '../FlowfishLogo';
import type { MenuProps } from 'antd';
@@ -98,13 +97,13 @@ const Sidebar: React.FC<SidebarProps> = ({ collapsed }) => {
},
{
key: 'integration',
icon: <RobotOutlined />,
label: 'AI Integration',
icon: <ApiOutlined />,
label: 'Integration',
children: [
{
key: '/integration/ai-hub',
label: 'AI Integration Hub',
onClick: () => navigate('/integration/ai-hub'),
key: '/integration/hub',
label: 'Integration Hub',
onClick: () => navigate('/integration/hub'),
},
],
},
+1 -1
View File
@@ -55,5 +55,5 @@ export const ROUTES = {
ANALYSIS_WIZARD: '/analysis/wizard',
DEPENDENCY_MAP: '/discovery/map',
CLUSTER_MANAGEMENT: '/management/clusters',
AI_INTEGRATION_HUB: '/integration/ai-hub',
INTEGRATION_HUB: '/integration/hub',
} as const;
+2 -2
View File
@@ -171,7 +171,7 @@ curl -s -f -H "X-API-Key: fk_your_api_key_here" \\
curl -X GET "${baseUrl}/api/v1/clusters" \\
-H "Authorization: Bearer eyJhbGciOiJIUz..."
# Example: Get dependency summary (AI Integration)
# Example: Get dependency summary (Integration)
curl -s -f -H "X-API-Key: fk_your_api_key" \\
"${baseUrl}/api/v1/communications/dependencies/summary?analysis_ids=1&namespace=production"
@@ -194,7 +194,7 @@ curl -X GET "${baseUrl}/api/v1/communications/graph?cluster_id=1" \\
<li><Text strong>Workloads</Text> - Kubernetes workload inventory and metadata</li>
<li><Text strong>Analyses</Text> - Traffic analysis creation, scheduling, and lifecycle</li>
<li><Text strong>Communications</Text> - Service-to-service communication and dependency graphs</li>
<li><Text strong>AI Integration</Text> - Dependency discovery, impact analysis, and CI/CD pipeline integration endpoints</li>
<li><Text strong>Integration</Text> - Dependency discovery, impact analysis, and CI/CD pipeline integration endpoints</li>
<li><Text strong>Events & Event Types</Text> - eBPF event statistics, queries, and type definitions</li>
<li><Text strong>Changes</Text> - Change detection and infrastructure drift tracking</li>
<li><Text strong>Simulation & Blast Radius</Text> - Impact simulation and pre-deployment assessment</li>
File diff suppressed because it is too large Load Diff
@@ -25,7 +25,8 @@ import {
theme,
} from 'antd';
import {
RobotOutlined,
ApiOutlined,
BranchesOutlined,
CodeOutlined,
CheckCircleOutlined,
ArrowLeftOutlined,
@@ -63,9 +64,29 @@ import {
const { Text, Title, Paragraph } = Typography;
const { Option } = Select;
const AIIntegrationHub: React.FC = () => {
type IntegrationType = 'dependency' | 'blast_radius' | null;
const EXAMPLE_BR_RESPONSE = `{
"assessment_id": "br-20260327-abc123",
"risk_score": 42,
"risk_level": "medium",
"blast_radius": {
"total_affected": 8,
"direct_dependencies": 3,
"indirect_dependencies": 5,
"critical_services": ["checkout-service"]
},
"recommendation": "proceed",
"suggested_actions": [
{ "action": "Notify checkout-service team", "priority": "medium" }
],
"advisory_only": true
}`;
const IntegrationHub: React.FC = () => {
const { token } = theme.useToken();
const [currentStep, setCurrentStep] = useState(0);
const [integrationType, setIntegrationType] = useState<IntegrationType>(null);
const [form] = Form.useForm();
const [selectedAnalysisIds, setSelectedAnalysisIds] = useState<number[]>([]);
@@ -73,6 +94,10 @@ const AIIntegrationHub: React.FC = () => {
const [idMethod, setIdMethod] = useState('annotation');
const [depth, setDepth] = useState(1);
// Blast Radius Gate flow state
const [brTargetService, setBrTargetService] = useState('');
const [brTargetNamespace, setBrTargetNamespace] = useState('');
const [triggerSummary, { data: rawSummaryData, isFetching: summaryLoading, error: rawSummaryError }] =
useLazyGetDependencySummaryQuery();
const [summaryParams, setSummaryParams] = useState<DependencySummaryParams | null>(null);
@@ -91,6 +116,8 @@ const AIIntegrationHub: React.FC = () => {
const urlAnnotationKey = searchParams.get('annotation_key');
const urlAnnotationValue = searchParams.get('annotation_value');
if (urlOwner || urlNs || urlAnnotationKey) {
setIntegrationType('dependency');
setCurrentStep(1);
if (urlAnnotationKey) setIdMethod('annotation');
else if (urlOwner || urlNs) setIdMethod('namespace_deployment');
setTimeout(() => {
@@ -132,7 +159,7 @@ const AIIntegrationHub: React.FC = () => {
return raw;
}, [summaryError, summaryData]);
const canProceedStep0 = selectedAnalysisIds.length > 0;
const canProceedConfigure = selectedAnalysisIds.length > 0;
const buildParamsFromForm = useCallback((): DependencySummaryParams | null => {
const values = form.getFieldsValue();
@@ -180,7 +207,7 @@ const AIIntegrationHub: React.FC = () => {
return;
}
setSummaryParams(params);
setCurrentStep(2);
setCurrentStep(3);
}, [selectedAnalysisIds, buildParamsFromForm]);
const responseSize = useMemo(() => {
@@ -191,15 +218,85 @@ const AIIntegrationHub: React.FC = () => {
const contextNamespace = summaryParams?.namespace;
const contextOwnerName = summaryParams?.owner_name;
const handleSelectType = (type: IntegrationType) => {
setIntegrationType(type);
setCurrentStep(1);
};
const handleBackToTypeSelection = () => {
setIntegrationType(null);
setCurrentStep(0);
setBrTargetService('');
setBrTargetNamespace('');
resetSummary();
};
const depSteps = [
{ title: 'Integration Type', icon: <ApiOutlined /> },
{ title: 'Configure', icon: <ExperimentOutlined /> },
{ title: 'Preview', icon: <EyeOutlined /> },
{ title: 'Integration Code', icon: <CodeOutlined /> },
];
const brSteps = [
{ title: 'Integration Type', icon: <ApiOutlined /> },
{ title: 'Configure', icon: <ExperimentOutlined /> },
{ title: 'Integration Code', icon: <CodeOutlined /> },
];
const activeSteps = integrationType === 'blast_radius' ? brSteps : depSteps;
const handleStepClick = (n: number) => {
if (n === 0) {
handleBackToTypeSelection();
return;
}
if (n < currentStep) {
setCurrentStep(n);
return;
}
if (integrationType === 'dependency') {
if (n === 2 && summaryData?.success) setCurrentStep(n);
else if (n === 3 && (summaryData?.success || summaryParams)) setCurrentStep(n);
}
if (integrationType === 'blast_radius') {
if (n === 2) setCurrentStep(n);
}
};
// ─── Shared auth card ───
const authCard = (
<Card
title={<span><KeyOutlined /> Authentication</span>}
size="small"
style={{ marginTop: 16 }}
>
<Paragraph>
All API calls require authentication via <Text strong>API Key</Text>. Include the header <Text code>X-API-Key: fk_your_key</Text> in every request.
</Paragraph>
<ol>
<li>Go to <Link to="/settings"><Text strong>Settings</Text></Link> and open the <Text strong>API Keys</Text> tab</li>
<li>Click <Text strong>Generate New API Key</Text> and give it a descriptive name (e.g. &quot;azure-devops-pipeline&quot;)</li>
<li>Copy the generated key (starts with <Text code>fk_</Text>) and store it securely in your CI/CD platform&apos;s secrets/variables</li>
</ol>
<Alert
type="warning"
showIcon
message="API keys provide full API access. Store them as encrypted secrets in your pipeline platform, never in source code."
style={{ marginTop: 8 }}
/>
</Card>
);
return (
<div style={{ maxWidth: 1100, margin: '0 auto' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
<div>
<Title level={2} style={{ marginBottom: 4 }}>
<RobotOutlined /> AI Integration Hub
<ApiOutlined /> Integration Hub
</Title>
<Paragraph type="secondary" style={{ marginBottom: 0 }}>
Set up CI/CD pipeline and AI agent integrations with Flowfish dependency and impact data.
Set up CI/CD pipeline integrations with Flowfish dependency and impact data.
</Paragraph>
</div>
<Link to="/discovery/map">
@@ -207,24 +304,104 @@ const AIIntegrationHub: React.FC = () => {
</Link>
</div>
<Card style={{ marginBottom: 16 }}>
<Steps
current={currentStep}
onChange={(n) => {
if (n < currentStep) setCurrentStep(n);
else if (n === 1 && summaryData?.success) setCurrentStep(n);
else if (n === 2 && (summaryData?.success || summaryParams)) setCurrentStep(n);
}}
items={[
{ title: 'Configure', icon: <ExperimentOutlined /> },
{ title: 'Preview', icon: <EyeOutlined /> },
{ title: 'Integration Code', icon: <CodeOutlined /> },
]}
/>
</Card>
{integrationType && (
<Card style={{ marginBottom: 16 }}>
<Steps
current={currentStep}
onChange={handleStepClick}
items={activeSteps}
/>
</Card>
)}
{/* ─── Step 0: Configure ─── */}
{/* ═══════════════════════════════════════════════════════════ */}
{/* Step 0: Integration Type Selection */}
{/* ═══════════════════════════════════════════════════════════ */}
{currentStep === 0 && (
<Row gutter={24} style={{ marginTop: 8 }}>
<Col xs={24} md={12}>
<Card
hoverable
onClick={() => handleSelectType('dependency')}
style={{
height: '100%',
borderColor: token.colorPrimary,
cursor: 'pointer',
transition: 'box-shadow 0.2s',
}}
styles={{ body: { padding: 24 } }}
>
<div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 16 }}>
<div style={{
width: 48, height: 48, borderRadius: 12,
background: `${token.colorPrimary}15`,
display: 'flex', alignItems: 'center', justifyContent: 'center',
}}>
<BranchesOutlined style={{ fontSize: 24, color: token.colorPrimary }} />
</div>
<div>
<Text strong style={{ fontSize: 16 }}>Dependency Analysis</Text>
<Tag color="blue" style={{ marginLeft: 8 }}>Most Common</Tag>
</div>
</div>
<Paragraph type="secondary" style={{ marginBottom: 12 }}>
Expose cross-service dependency data to CI/CD pipelines. Identify affected repositories, critical services, and downstream impact chains.
</Paragraph>
<ul style={{ margin: 0, paddingLeft: 20, color: token.colorTextSecondary, fontSize: 13 }}>
<li>Multi-analysis scope with 5 identification methods</li>
<li>Live preview with downstream/caller categorization</li>
<li>Pipeline YAML, curl, Python, and JavaScript snippets</li>
<li>Git-repo annotation extraction for cross-project impact</li>
</ul>
<div style={{ marginTop: 16 }}>
<Button type="primary" icon={<ArrowRightOutlined />}>Get Started</Button>
</div>
</Card>
</Col>
<Col xs={24} md={12}>
<Card
hoverable
onClick={() => handleSelectType('blast_radius')}
style={{
height: '100%',
cursor: 'pointer',
transition: 'box-shadow 0.2s',
}}
styles={{ body: { padding: 24 } }}
>
<div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 16 }}>
<div style={{
width: 48, height: 48, borderRadius: 12,
background: `${token.colorWarning}15`,
display: 'flex', alignItems: 'center', justifyContent: 'center',
}}>
<ThunderboltOutlined style={{ fontSize: 24, color: token.colorWarning }} />
</div>
<Text strong style={{ fontSize: 16 }}>Blast Radius Gate</Text>
</div>
<Paragraph type="secondary" style={{ marginBottom: 12 }}>
Add pre-deployment risk scoring to your CI/CD pipeline. Get automated risk assessments, affected service counts, and actionable recommendations.
</Paragraph>
<ul style={{ margin: 0, paddingLeft: 20, color: token.colorTextSecondary, fontSize: 13 }}>
<li>Risk score (0-100) with level classification</li>
<li>Blast radius: direct, indirect, and critical services</li>
<li>Advisory-only Flowfish never blocks deployments</li>
<li>Pipeline snippets for all major CI/CD platforms</li>
</ul>
<div style={{ marginTop: 16 }}>
<Button icon={<ArrowRightOutlined />}>Get Started</Button>
</div>
</Card>
</Col>
</Row>
)}
{/* ═══════════════════════════════════════════════════════════ */}
{/* DEPENDENCY ANALYSIS FLOW (Steps 1-3) */}
{/* ═══════════════════════════════════════════════════════════ */}
{/* ─── Dep Step 1: Configure ─── */}
{integrationType === 'dependency' && currentStep === 1 && (
<Card title="Analysis Scope & Service Identification">
<Form.Item label="Analysis (required, multi-select)" required>
<Select
@@ -376,7 +553,7 @@ const AIIntegrationHub: React.FC = () => {
icon={<ExperimentOutlined />}
onClick={onTestQuery}
loading={summaryLoading}
disabled={!canProceedStep0}
disabled={!canProceedConfigure}
>
Test Query
</Button>
@@ -407,26 +584,31 @@ const AIIntegrationHub: React.FC = () => {
/>
)}
<div style={{ display: 'flex', justifyContent: 'flex-end', alignItems: 'center', marginTop: 24, gap: 8 }}>
{!summaryData?.success && (
<Button onClick={onSkipToSetup} disabled={!canProceedStep0}>
Skip to Integration Code <ArrowRightOutlined />
</Button>
)}
<Button
type="primary"
icon={<ArrowRightOutlined />}
disabled={!summaryData?.success}
onClick={() => setCurrentStep(1)}
>
Preview Results
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 24 }}>
<Button icon={<ArrowLeftOutlined />} onClick={handleBackToTypeSelection}>
Back
</Button>
<Space>
{!summaryData?.success && (
<Button onClick={onSkipToSetup} disabled={!canProceedConfigure}>
Skip to Integration Code <ArrowRightOutlined />
</Button>
)}
<Button
type="primary"
icon={<ArrowRightOutlined />}
disabled={!summaryData?.success}
onClick={() => setCurrentStep(2)}
>
Preview Results
</Button>
</Space>
</div>
</Card>
)}
{/* ─── Step 1: Preview & Validate ─── */}
{currentStep === 1 && !summaryData?.success && (
{/* ─── Dep Step 2: Preview & Validate ─── */}
{integrationType === 'dependency' && currentStep === 2 && !summaryData?.success && (
<Card>
<Alert
type="warning"
@@ -434,10 +616,10 @@ const AIIntegrationHub: React.FC = () => {
message="Preview data is no longer available. Please run Test Query again."
style={{ marginBottom: 16 }}
/>
<Button type="primary" onClick={() => setCurrentStep(0)}>Back to Configure</Button>
<Button type="primary" onClick={() => setCurrentStep(1)}>Back to Configure</Button>
</Card>
)}
{currentStep === 1 && summaryData?.success && (
{integrationType === 'dependency' && currentStep === 2 && summaryData?.success && (
<div>
<Card size="small" style={{ borderLeft: `3px solid ${token.colorPrimary}`, marginBottom: 16 }}>
<Row gutter={16} align="middle">
@@ -526,25 +708,18 @@ const AIIntegrationHub: React.FC = () => {
/>
<div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 24 }}>
<Button
icon={<ArrowLeftOutlined />}
onClick={() => setCurrentStep(0)}
>
<Button icon={<ArrowLeftOutlined />} onClick={() => setCurrentStep(1)}>
Back
</Button>
<Button
type="primary"
icon={<ArrowRightOutlined />}
onClick={() => setCurrentStep(2)}
>
<Button type="primary" icon={<ArrowRightOutlined />} onClick={() => setCurrentStep(3)}>
Integration Code
</Button>
</div>
</div>
)}
{/* ─── Step 2: Integration Code ─── */}
{currentStep === 2 && !summaryData?.success && !summaryParams && (
{/* ─── Dep Step 3: Integration Code ─── */}
{integrationType === 'dependency' && currentStep === 3 && !summaryData?.success && !summaryParams && (
<Card>
<Alert
type="warning"
@@ -552,10 +727,10 @@ const AIIntegrationHub: React.FC = () => {
message="Configuration required. Please set up your query in the Configure step."
style={{ marginBottom: 16 }}
/>
<Button type="primary" onClick={() => setCurrentStep(0)}>Back to Configure</Button>
<Button type="primary" onClick={() => setCurrentStep(1)}>Back to Configure</Button>
</Card>
)}
{currentStep === 2 && (summaryData?.success || summaryParams) && (
{integrationType === 'dependency' && currentStep === 3 && (summaryData?.success || summaryParams) && (
<div>
{!summaryData?.success && (
<Alert
@@ -572,10 +747,7 @@ const AIIntegrationHub: React.FC = () => {
style={{ marginBottom: 16 }}
/>
<Card
size="small"
style={{ marginBottom: 16 }}
>
<Card size="small" style={{ marginBottom: 16 }}>
<Space align="center">
<Text strong>Pipeline Platform:</Text>
<Select value={platform} onChange={setPlatform} style={{ width: 200 }}>
@@ -617,7 +789,7 @@ const AIIntegrationHub: React.FC = () => {
showIcon
icon={<InfoCircleOutlined />}
message="Pre-deployment risk assessment"
description="Use this endpoint to assess the impact of deploying changes to a service. Returns a risk score (0-100), affected services count, and actionable recommendations."
description={<>Use this endpoint to assess the impact of deploying changes to a service. You can also <Button type="link" style={{ padding: 0 }} onClick={handleBackToTypeSelection}>set up Blast Radius as a dedicated integration type</Button> from Step 1.</>}
style={{ marginBottom: 16 }}
/>
<Tabs
@@ -636,7 +808,7 @@ const AIIntegrationHub: React.FC = () => {
]}
/>
<div style={{ marginTop: 12 }}>
<Link to="/impact/blast-radius">
<Link to="/impact/blast-radius?tab=test">
<Button type="link" style={{ padding: 0 }}>
<ThunderboltOutlined /> Test blast radius assessments interactively
</Button>
@@ -648,26 +820,7 @@ const AIIntegrationHub: React.FC = () => {
]}
/>
<Card
title={<span><KeyOutlined /> Authentication</span>}
size="small"
style={{ marginTop: 16 }}
>
<Paragraph>
All API calls require authentication via <Text strong>API Key</Text>. Include the header <Text code>X-API-Key: fk_your_key</Text> in every request.
</Paragraph>
<ol>
<li>Go to <Link to="/settings"><Text strong>Settings</Text></Link> and open the <Text strong>API Keys</Text> tab</li>
<li>Click <Text strong>Generate New API Key</Text> and give it a descriptive name (e.g. "azure-devops-pipeline")</li>
<li>Copy the generated key (starts with <Text code>fk_</Text>) and store it securely in your CI/CD platform's secrets/variables</li>
</ol>
<Alert
type="warning"
showIcon
message="API keys provide full API access. Store them as encrypted secrets in your pipeline platform, never in source code."
style={{ marginTop: 8 }}
/>
</Card>
{authCard}
<Card title="Understanding the Response" size="small" style={{ marginTop: 16 }}>
<Paragraph>
@@ -675,10 +828,10 @@ const AIIntegrationHub: React.FC = () => {
Each dependency includes its Kubernetes <Text strong>annotations</Text> and <Text strong>labels</Text>.
</Paragraph>
<Paragraph>
Your AI agent or pipeline should:
Your pipeline should:
</Paragraph>
<ol>
<li>Extract <Text code>annotations["git-repo"]</Text> from each downstream service to identify affected repositories</li>
<li>Extract <Text code>annotations[&quot;git-repo&quot;]</Text> from each downstream service to identify affected repositories</li>
<li>Check <Text code>is_critical</Text> flag to prioritize critical dependency changes</li>
<li>Use <Text code>service_category</Text> grouping to understand the type of each dependency (database, cache, API, etc.)</li>
<li>Examine <Text code>callers</Text> to understand which services call the changed service</li>
@@ -686,14 +839,144 @@ const AIIntegrationHub: React.FC = () => {
</Card>
<div style={{ marginTop: 24 }}>
<Button icon={<ArrowLeftOutlined />} onClick={() => setCurrentStep(summaryData?.success ? 1 : 0)}>
<Button icon={<ArrowLeftOutlined />} onClick={() => setCurrentStep(summaryData?.success ? 2 : 1)}>
{summaryData?.success ? 'Back to Preview' : 'Back to Configure'}
</Button>
</div>
</div>
)}
{/* ═══════════════════════════════════════════════════════════ */}
{/* BLAST RADIUS GATE FLOW (Steps 1-2) */}
{/* ═══════════════════════════════════════════════════════════ */}
{/* ─── BR Step 1: Configure ─── */}
{integrationType === 'blast_radius' && currentStep === 1 && (
<Card title="Blast Radius Gate Configuration">
<Paragraph type="secondary">
Configure your pre-deployment risk assessment integration. The Blast Radius API evaluates the impact of changes
and returns a risk score with recommendations your pipeline decides what to do.
</Paragraph>
<Divider />
<Form layout="vertical">
<Form.Item label="Pipeline Platform" required>
<Select value={platform} onChange={setPlatform} style={{ maxWidth: 300 }}>
{PIPELINE_PLATFORMS.map(p => <Option key={p.value} value={p.value}>{p.label}</Option>)}
</Select>
</Form.Item>
<Row gutter={16}>
<Col xs={24} sm={12}>
<Form.Item
label="Target Service Name"
tooltip="Optional — if left empty, snippets will use a placeholder. You can parameterize this in your pipeline."
>
<Input
placeholder="e.g. payment-service"
value={brTargetService}
onChange={e => setBrTargetService(e.target.value)}
/>
</Form.Item>
</Col>
<Col xs={24} sm={12}>
<Form.Item
label="Target Namespace"
tooltip="Optional — defaults to 'default' in generated snippets."
>
<Input
placeholder="e.g. production"
value={brTargetNamespace}
onChange={e => setBrTargetNamespace(e.target.value)}
/>
</Form.Item>
</Col>
</Row>
</Form>
<div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 24 }}>
<Button icon={<ArrowLeftOutlined />} onClick={handleBackToTypeSelection}>
Back
</Button>
<Button type="primary" icon={<ArrowRightOutlined />} onClick={() => setCurrentStep(2)}>
Generate Integration Code
</Button>
</div>
</Card>
)}
{/* ─── BR Step 2: Integration Code ─── */}
{integrationType === 'blast_radius' && currentStep === 2 && (
<div>
<Alert
type="info"
showIcon
message="All snippets below use your configured parameters. Copy and adapt to your environment."
style={{ marginBottom: 16 }}
/>
<Card size="small" style={{ marginBottom: 16 }}>
<Space align="center">
<Text strong>Pipeline Platform:</Text>
<Select value={platform} onChange={setPlatform} style={{ width: 200 }}>
{PIPELINE_PLATFORMS.map(p => <Option key={p.value} value={p.value}>{p.label}</Option>)}
</Select>
</Space>
</Card>
<Tabs
items={[
{
key: 'br-pipeline',
label: <span><RocketOutlined /> {PIPELINE_PLATFORMS.find(p => p.value === platform)?.label || 'Pipeline'}</span>,
children: <CodeBlock code={buildBlastRadiusPipelineSnippet(platform, brTargetNamespace || undefined, brTargetService || undefined)} label="Blast Radius Pipeline" />,
},
{
key: 'br-curl',
label: <span><CodeOutlined /> curl</span>,
children: <CodeBlock code={buildBlastRadiusCurlSnippet(brTargetNamespace || undefined, brTargetService || undefined)} label="Blast Radius curl" />,
},
]}
/>
<Card title="Example Response" size="small" style={{ marginTop: 16 }}>
<Paragraph type="secondary" style={{ marginBottom: 8 }}>
The <Text code>POST /api/v1/blast-radius/assess</Text> endpoint returns a risk assessment:
</Paragraph>
<CodeBlock code={EXAMPLE_BR_RESPONSE} label="Example Response" />
</Card>
<Card title="Response Fields" size="small" style={{ marginTop: 16 }}>
<Descriptions size="small" column={1} bordered>
<Descriptions.Item label={<Text code>risk_score</Text>}>0-100, higher = more risky</Descriptions.Item>
<Descriptions.Item label={<Text code>risk_level</Text>}>low / medium / high / critical</Descriptions.Item>
<Descriptions.Item label={<Text code>blast_radius.total_affected</Text>}>Total services in impact zone</Descriptions.Item>
<Descriptions.Item label={<Text code>blast_radius.critical_services</Text>}>Names of critical downstream services</Descriptions.Item>
<Descriptions.Item label={<Text code>recommendation</Text>}>proceed / review_required / delay_suggested</Descriptions.Item>
<Descriptions.Item label={<Text code>advisory_only</Text>}>Always true Flowfish never blocks deployments</Descriptions.Item>
</Descriptions>
</Card>
{authCard}
<div style={{ marginTop: 16 }}>
<Link to="/impact/blast-radius?tab=test">
<Button type="link" icon={<ThunderboltOutlined />} style={{ padding: 0 }}>
Test blast radius assessments interactively
</Button>
</Link>
</div>
<div style={{ marginTop: 24 }}>
<Button icon={<ArrowLeftOutlined />} onClick={() => setCurrentStep(1)}>
Back to Configure
</Button>
</div>
</div>
)}
</div>
);
};
export default AIIntegrationHub;
export default IntegrationHub;
+7 -8
View File
@@ -85,7 +85,6 @@ import {
BarChartOutlined,
CheckCircleOutlined,
AlertOutlined,
RobotOutlined,
CopyOutlined,
} from '@ant-design/icons';
import { useNavigate } from 'react-router-dom';
@@ -7673,25 +7672,25 @@ const MapInner: React.FC = () => {
})() : null,
},
{
key: 'ai-hub',
label: <span><RobotOutlined /> AI Hub</span>,
children: drawerTab === 'ai-hub' ? (
key: 'integration',
label: <span><ApiOutlined /> Integration</span>,
children: drawerTab === 'integration' ? (
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
<Typography.Text type="secondary">
Analyze this service's dependencies, impact radius, and generate integration snippets for AI Agents and CI/CD pipelines.
Analyze this service's dependencies, impact radius, and generate integration snippets for CI/CD pipelines.
</Typography.Text>
<Button
type="primary"
icon={<RobotOutlined />}
icon={<ApiOutlined />}
onClick={() => {
const node = selectedNode as any;
const params = new URLSearchParams();
if (node?.name) params.set('owner_name', node.owner_name || node.name);
if (node?.namespace && node.namespace !== 'external') params.set('namespace', node.namespace);
navigate(`/integration/ai-hub?${params.toString()}`);
navigate(`/integration/hub?${params.toString()}`);
}}
>
Open AI Integration Hub
Open Integration Hub
</Button>
</div>
) : null,