cloudwire 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cloudwire-0.1.0/PKG-INFO +186 -0
- cloudwire-0.1.0/README.md +156 -0
- cloudwire-0.1.0/cloudwire/__init__.py +3 -0
- cloudwire-0.1.0/cloudwire/app/__init__.py +1 -0
- cloudwire-0.1.0/cloudwire/app/graph_store.py +88 -0
- cloudwire-0.1.0/cloudwire/app/main.py +453 -0
- cloudwire-0.1.0/cloudwire/app/models.py +83 -0
- cloudwire-0.1.0/cloudwire/app/scan_jobs.py +368 -0
- cloudwire-0.1.0/cloudwire/app/scanner.py +1149 -0
- cloudwire-0.1.0/cloudwire/cli.py +86 -0
- cloudwire-0.1.0/cloudwire/static/assets/index-CByMF4j6.js +40 -0
- cloudwire-0.1.0/cloudwire/static/assets/index-lik1Sxh5.css +1 -0
- cloudwire-0.1.0/cloudwire/static/index.html +13 -0
- cloudwire-0.1.0/cloudwire.egg-info/PKG-INFO +186 -0
- cloudwire-0.1.0/cloudwire.egg-info/SOURCES.txt +19 -0
- cloudwire-0.1.0/cloudwire.egg-info/dependency_links.txt +1 -0
- cloudwire-0.1.0/cloudwire.egg-info/entry_points.txt +2 -0
- cloudwire-0.1.0/cloudwire.egg-info/requires.txt +7 -0
- cloudwire-0.1.0/cloudwire.egg-info/top_level.txt +1 -0
- cloudwire-0.1.0/pyproject.toml +49 -0
- cloudwire-0.1.0/setup.cfg +4 -0
cloudwire-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cloudwire
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Scan and visualize your AWS infrastructure as an interactive graph
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/hisingh_gwre/cloudwire
|
|
7
|
+
Project-URL: Issues, https://github.com/hisingh_gwre/cloudwire/issues
|
|
8
|
+
Keywords: aws,cloud,visualization,graph,infrastructure
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Environment :: Web Environment
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: System Administrators
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
20
|
+
Classifier: Topic :: System :: Systems Administration
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Requires-Dist: fastapi>=0.115
|
|
24
|
+
Requires-Dist: uvicorn[standard]>=0.34
|
|
25
|
+
Requires-Dist: boto3>=1.37
|
|
26
|
+
Requires-Dist: botocore>=1.37
|
|
27
|
+
Requires-Dist: networkx>=3.4
|
|
28
|
+
Requires-Dist: pydantic>=2.11
|
|
29
|
+
Requires-Dist: click>=8.1
|
|
30
|
+
|
|
31
|
+
# cloudwire
|
|
32
|
+
|
|
33
|
+
Scan your AWS account and visualize resource dependencies as an interactive graph — directly in your browser, running entirely on your local machine.
|
|
34
|
+
|
|
35
|
+
No data leaves your system. AWS credentials never leave your terminal. The graph is built locally using your existing credential chain (`~/.aws/credentials`, `aws sso login`, `saml2aws`, `aws-vault` — all work out of the box).
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install cloudwire
|
|
43
|
+
cloudwire
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
That's it. The browser opens automatically at `http://localhost:8080`.
|
|
47
|
+
|
|
48
|
+
> **Requirements:** Python 3.9+ and valid AWS credentials configured locally.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## What it looks like
|
|
53
|
+
|
|
54
|
+
- Dark hacker-aesthetic graph canvas
|
|
55
|
+
- Nodes represent AWS resources — Lambda functions, SQS queues, API Gateways, RDS instances, S3 buckets, and more
|
|
56
|
+
- Edges represent relationships and data flow between resources
|
|
57
|
+
- Click any node to inspect its attributes and connected resources
|
|
58
|
+
- Search, filter by service, highlight upstream/downstream blast radius
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Supported services
|
|
63
|
+
|
|
64
|
+
| Service | Scanner |
|
|
65
|
+
|---------|---------|
|
|
66
|
+
| API Gateway | Dedicated |
|
|
67
|
+
| Lambda | Dedicated (with state) |
|
|
68
|
+
| SQS | Dedicated |
|
|
69
|
+
| SNS | Dedicated |
|
|
70
|
+
| EventBridge | Dedicated |
|
|
71
|
+
| DynamoDB | Dedicated (with state) |
|
|
72
|
+
| EC2 | Dedicated (with state) |
|
|
73
|
+
| ECS | Dedicated |
|
|
74
|
+
| S3 | Dedicated |
|
|
75
|
+
| RDS | Dedicated (with state) |
|
|
76
|
+
| Step Functions | Dedicated |
|
|
77
|
+
| Kinesis | Dedicated |
|
|
78
|
+
| IAM | Dedicated |
|
|
79
|
+
| Cognito | Dedicated |
|
|
80
|
+
| CloudFront | Dedicated (with state) |
|
|
81
|
+
| ElastiCache | Dedicated (with state) |
|
|
82
|
+
| Glue | Dedicated |
|
|
83
|
+
| AppSync | Dedicated |
|
|
84
|
+
| Everything else | Generic (tagged resources only) |
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Project structure
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
cloudwire/ # Python package (the distributable unit)
|
|
92
|
+
├── __init__.py # Package version
|
|
93
|
+
├── cli.py # `cloudwire` CLI entry point (click)
|
|
94
|
+
├── static/ # Built React app (populated by `make build`)
|
|
95
|
+
│ ├── index.html
|
|
96
|
+
│ └── assets/
|
|
97
|
+
└── app/ # FastAPI backend
|
|
98
|
+
├── main.py # App factory, API routes (/api/*), static serving
|
|
99
|
+
├── models.py # Pydantic request/response models
|
|
100
|
+
├── scanner.py # boto3 AWS scanner — one function per service
|
|
101
|
+
├── scan_jobs.py # Async job store with progress tracking
|
|
102
|
+
└── graph_store.py # networkx graph with thread-safe mutations
|
|
103
|
+
|
|
104
|
+
frontend/ # React + Vite source (compiled into cloudwire/static/)
|
|
105
|
+
├── src/
|
|
106
|
+
│ ├── pages/CloudWirePage.jsx # Main page — orchestrates all state
|
|
107
|
+
│ ├── components/
|
|
108
|
+
│ │ ├── graph/ # GraphCanvas, GraphNode, GraphEdge, Minimap, Legend
|
|
109
|
+
│ │ └── layout/ # TopBar, ServiceSidebar, InspectorPanel
|
|
110
|
+
│ ├── hooks/
|
|
111
|
+
│ │ ├── useScanPolling.js # Scan lifecycle, polling, graph data state
|
|
112
|
+
│ │ └── useGraphViewport.js # Pan/zoom viewport state
|
|
113
|
+
│ ├── lib/
|
|
114
|
+
│ │ ├── graphTransforms.js # Layout algorithms (circular, flow, swimlane)
|
|
115
|
+
│ │ ├── serviceVisuals.jsx # Service icon + color map
|
|
116
|
+
│ │ └── awsRegions.js # AWS region list
|
|
117
|
+
│ └── styles/graph.css # All UI styles
|
|
118
|
+
├── vite.config.js # base: "./", outDir: ../cloudwire/static, dev proxy
|
|
119
|
+
└── package.json
|
|
120
|
+
|
|
121
|
+
.github/workflows/publish.yml # CI: build + publish to PyPI on version tag push
|
|
122
|
+
pyproject.toml # Package metadata, dependencies, entry point
|
|
123
|
+
Makefile # make build / make dev / make clean
|
|
124
|
+
.python-version # Pins Python 3.11 for consistent builds
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Contributing
|
|
130
|
+
|
|
131
|
+
### Prerequisites
|
|
132
|
+
|
|
133
|
+
- Python 3.9+ (3.11 recommended)
|
|
134
|
+
- Node.js 18+
|
|
135
|
+
- AWS credentials configured (any method)
|
|
136
|
+
|
|
137
|
+
### Set up the dev environment
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
git clone https://github.com/hisingh_gwre/cloudwire
|
|
141
|
+
cd cloudwire
|
|
142
|
+
|
|
143
|
+
# Python
|
|
144
|
+
python3 -m venv .venv
|
|
145
|
+
source .venv/bin/activate
|
|
146
|
+
pip install -e .
|
|
147
|
+
|
|
148
|
+
# Frontend
|
|
149
|
+
cd frontend && npm install
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Run in development mode
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
make dev
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
This starts the FastAPI backend on `:8000` (with `--reload`) and the Vite dev server on `:5173` concurrently. The Vite dev server proxies all `/api/*` requests to the backend — no CORS config needed.
|
|
159
|
+
|
|
160
|
+
### Making changes
|
|
161
|
+
|
|
162
|
+
| Area | Where to edit |
|
|
163
|
+
|------|--------------|
|
|
164
|
+
| Add a new AWS service scanner | `cloudwire/app/scanner.py` → add a `_scan_<service>` method and register it in `self.service_scanners` |
|
|
165
|
+
| Change graph layout | `frontend/src/lib/graphTransforms.js` |
|
|
166
|
+
| Add a new UI component | `frontend/src/components/` |
|
|
167
|
+
| Change API routes | `cloudwire/app/main.py` — all routes are under the `/api` prefix |
|
|
168
|
+
| Change CLI options | `cloudwire/cli.py` |
|
|
169
|
+
|
|
170
|
+
### Before opening a PR
|
|
171
|
+
|
|
172
|
+
- Run a scan against a real (or mocked) AWS account and confirm the graph renders
|
|
173
|
+
- Make sure `make build` completes without errors
|
|
174
|
+
- Keep PRs focused — one feature or fix per PR
|
|
175
|
+
|
|
176
|
+
### Code style
|
|
177
|
+
|
|
178
|
+
- Python: standard library imports first, then third-party, then local. No formatter enforced yet.
|
|
179
|
+
- JavaScript: no linter enforced yet. Match the style of the surrounding file.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Links
|
|
184
|
+
|
|
185
|
+
- [Usage & setup guide](docs/USAGE.md)
|
|
186
|
+
- [Release guide for maintainers](docs/RELEASING.md)
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# cloudwire
|
|
2
|
+
|
|
3
|
+
Scan your AWS account and visualize resource dependencies as an interactive graph — directly in your browser, running entirely on your local machine.
|
|
4
|
+
|
|
5
|
+
No data leaves your system. AWS credentials never leave your terminal. The graph is built locally using your existing credential chain (`~/.aws/credentials`, `aws sso login`, `saml2aws`, `aws-vault` — all work out of the box).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install cloudwire
|
|
13
|
+
cloudwire
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
That's it. The browser opens automatically at `http://localhost:8080`.
|
|
17
|
+
|
|
18
|
+
> **Requirements:** Python 3.9+ and valid AWS credentials configured locally.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## What it looks like
|
|
23
|
+
|
|
24
|
+
- Dark hacker-aesthetic graph canvas
|
|
25
|
+
- Nodes represent AWS resources — Lambda functions, SQS queues, API Gateways, RDS instances, S3 buckets, and more
|
|
26
|
+
- Edges represent relationships and data flow between resources
|
|
27
|
+
- Click any node to inspect its attributes and connected resources
|
|
28
|
+
- Search, filter by service, highlight upstream/downstream blast radius
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Supported services
|
|
33
|
+
|
|
34
|
+
| Service | Scanner |
|
|
35
|
+
|---------|---------|
|
|
36
|
+
| API Gateway | Dedicated |
|
|
37
|
+
| Lambda | Dedicated (with state) |
|
|
38
|
+
| SQS | Dedicated |
|
|
39
|
+
| SNS | Dedicated |
|
|
40
|
+
| EventBridge | Dedicated |
|
|
41
|
+
| DynamoDB | Dedicated (with state) |
|
|
42
|
+
| EC2 | Dedicated (with state) |
|
|
43
|
+
| ECS | Dedicated |
|
|
44
|
+
| S3 | Dedicated |
|
|
45
|
+
| RDS | Dedicated (with state) |
|
|
46
|
+
| Step Functions | Dedicated |
|
|
47
|
+
| Kinesis | Dedicated |
|
|
48
|
+
| IAM | Dedicated |
|
|
49
|
+
| Cognito | Dedicated |
|
|
50
|
+
| CloudFront | Dedicated (with state) |
|
|
51
|
+
| ElastiCache | Dedicated (with state) |
|
|
52
|
+
| Glue | Dedicated |
|
|
53
|
+
| AppSync | Dedicated |
|
|
54
|
+
| Everything else | Generic (tagged resources only) |
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Project structure
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
cloudwire/ # Python package (the distributable unit)
|
|
62
|
+
├── __init__.py # Package version
|
|
63
|
+
├── cli.py # `cloudwire` CLI entry point (click)
|
|
64
|
+
├── static/ # Built React app (populated by `make build`)
|
|
65
|
+
│ ├── index.html
|
|
66
|
+
│ └── assets/
|
|
67
|
+
└── app/ # FastAPI backend
|
|
68
|
+
├── main.py # App factory, API routes (/api/*), static serving
|
|
69
|
+
├── models.py # Pydantic request/response models
|
|
70
|
+
├── scanner.py # boto3 AWS scanner — one function per service
|
|
71
|
+
├── scan_jobs.py # Async job store with progress tracking
|
|
72
|
+
└── graph_store.py # networkx graph with thread-safe mutations
|
|
73
|
+
|
|
74
|
+
frontend/ # React + Vite source (compiled into cloudwire/static/)
|
|
75
|
+
├── src/
|
|
76
|
+
│ ├── pages/CloudWirePage.jsx # Main page — orchestrates all state
|
|
77
|
+
│ ├── components/
|
|
78
|
+
│ │ ├── graph/ # GraphCanvas, GraphNode, GraphEdge, Minimap, Legend
|
|
79
|
+
│ │ └── layout/ # TopBar, ServiceSidebar, InspectorPanel
|
|
80
|
+
│ ├── hooks/
|
|
81
|
+
│ │ ├── useScanPolling.js # Scan lifecycle, polling, graph data state
|
|
82
|
+
│ │ └── useGraphViewport.js # Pan/zoom viewport state
|
|
83
|
+
│ ├── lib/
|
|
84
|
+
│ │ ├── graphTransforms.js # Layout algorithms (circular, flow, swimlane)
|
|
85
|
+
│ │ ├── serviceVisuals.jsx # Service icon + color map
|
|
86
|
+
│ │ └── awsRegions.js # AWS region list
|
|
87
|
+
│ └── styles/graph.css # All UI styles
|
|
88
|
+
├── vite.config.js # base: "./", outDir: ../cloudwire/static, dev proxy
|
|
89
|
+
└── package.json
|
|
90
|
+
|
|
91
|
+
.github/workflows/publish.yml # CI: build + publish to PyPI on version tag push
|
|
92
|
+
pyproject.toml # Package metadata, dependencies, entry point
|
|
93
|
+
Makefile # make build / make dev / make clean
|
|
94
|
+
.python-version # Pins Python 3.11 for consistent builds
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Contributing
|
|
100
|
+
|
|
101
|
+
### Prerequisites
|
|
102
|
+
|
|
103
|
+
- Python 3.9+ (3.11 recommended)
|
|
104
|
+
- Node.js 18+
|
|
105
|
+
- AWS credentials configured (any method)
|
|
106
|
+
|
|
107
|
+
### Set up the dev environment
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
git clone https://github.com/hisingh_gwre/cloudwire
|
|
111
|
+
cd cloudwire
|
|
112
|
+
|
|
113
|
+
# Python
|
|
114
|
+
python3 -m venv .venv
|
|
115
|
+
source .venv/bin/activate
|
|
116
|
+
pip install -e .
|
|
117
|
+
|
|
118
|
+
# Frontend
|
|
119
|
+
cd frontend && npm install
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Run in development mode
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
make dev
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
This starts the FastAPI backend on `:8000` (with `--reload`) and the Vite dev server on `:5173` concurrently. The Vite dev server proxies all `/api/*` requests to the backend — no CORS config needed.
|
|
129
|
+
|
|
130
|
+
### Making changes
|
|
131
|
+
|
|
132
|
+
| Area | Where to edit |
|
|
133
|
+
|------|--------------|
|
|
134
|
+
| Add a new AWS service scanner | `cloudwire/app/scanner.py` → add a `_scan_<service>` method and register it in `self.service_scanners` |
|
|
135
|
+
| Change graph layout | `frontend/src/lib/graphTransforms.js` |
|
|
136
|
+
| Add a new UI component | `frontend/src/components/` |
|
|
137
|
+
| Change API routes | `cloudwire/app/main.py` — all routes are under the `/api` prefix |
|
|
138
|
+
| Change CLI options | `cloudwire/cli.py` |
|
|
139
|
+
|
|
140
|
+
### Before opening a PR
|
|
141
|
+
|
|
142
|
+
- Run a scan against a real (or mocked) AWS account and confirm the graph renders
|
|
143
|
+
- Make sure `make build` completes without errors
|
|
144
|
+
- Keep PRs focused — one feature or fix per PR
|
|
145
|
+
|
|
146
|
+
### Code style
|
|
147
|
+
|
|
148
|
+
- Python: standard library imports first, then third-party, then local. No formatter enforced yet.
|
|
149
|
+
- JavaScript: no linter enforced yet. Match the style of the surrounding file.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Links
|
|
154
|
+
|
|
155
|
+
- [Usage & setup guide](docs/USAGE.md)
|
|
156
|
+
- [Release guide for maintainers](docs/RELEASING.md)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CloudWire backend package."""
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
from threading import Lock
|
|
5
|
+
from typing import Any, Dict, List
|
|
6
|
+
|
|
7
|
+
import networkx as nx
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class GraphStore:
|
|
11
|
+
def __init__(self) -> None:
|
|
12
|
+
self.graph = nx.DiGraph()
|
|
13
|
+
self.metadata: Dict[str, Any] = {
|
|
14
|
+
"last_scan_at": None,
|
|
15
|
+
"region": None,
|
|
16
|
+
"scanned_services": [],
|
|
17
|
+
"warnings": [],
|
|
18
|
+
}
|
|
19
|
+
self._lock = Lock()
|
|
20
|
+
|
|
21
|
+
def reset(self, *, region: str, services: List[str]) -> None:
|
|
22
|
+
with self._lock:
|
|
23
|
+
self.graph = nx.DiGraph()
|
|
24
|
+
self.metadata = {
|
|
25
|
+
"last_scan_at": datetime.now(timezone.utc).isoformat(),
|
|
26
|
+
"region": region,
|
|
27
|
+
"scanned_services": services,
|
|
28
|
+
"warnings": [],
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
def add_warning(self, warning: str) -> None:
|
|
32
|
+
with self._lock:
|
|
33
|
+
self.metadata.setdefault("warnings", []).append(warning)
|
|
34
|
+
|
|
35
|
+
def update_metadata(self, **kwargs: Any) -> None:
|
|
36
|
+
with self._lock:
|
|
37
|
+
self.metadata.update(kwargs)
|
|
38
|
+
|
|
39
|
+
def add_node(self, node_id: str, **attrs: Any) -> None:
|
|
40
|
+
with self._lock:
|
|
41
|
+
current = self.graph.nodes[node_id] if self.graph.has_node(node_id) else {}
|
|
42
|
+
merged = {**current, **attrs}
|
|
43
|
+
merged["id"] = node_id
|
|
44
|
+
self.graph.add_node(node_id, **merged)
|
|
45
|
+
|
|
46
|
+
def add_edge(self, source: str, target: str, **attrs: Any) -> None:
|
|
47
|
+
with self._lock:
|
|
48
|
+
current = self.graph.get_edge_data(source, target, default={})
|
|
49
|
+
merged = {**current, **attrs}
|
|
50
|
+
self.graph.add_edge(source, target, **merged)
|
|
51
|
+
|
|
52
|
+
def _serialize_node(self, node_id: str, attrs: Dict[str, Any]) -> Dict[str, Any]:
|
|
53
|
+
payload = {"id": node_id}
|
|
54
|
+
payload.update(attrs)
|
|
55
|
+
return payload
|
|
56
|
+
|
|
57
|
+
def _serialize_edge(self, source: str, target: str, attrs: Dict[str, Any]) -> Dict[str, Any]:
|
|
58
|
+
payload = {"id": f"{source}__{target}", "source": source, "target": target}
|
|
59
|
+
payload.update(attrs)
|
|
60
|
+
return payload
|
|
61
|
+
|
|
62
|
+
def get_graph_payload(self) -> Dict[str, Any]:
|
|
63
|
+
with self._lock:
|
|
64
|
+
nodes = [self._serialize_node(node_id, attrs) for node_id, attrs in self.graph.nodes(data=True)]
|
|
65
|
+
edges = [
|
|
66
|
+
self._serialize_edge(source, target, attrs)
|
|
67
|
+
for source, target, attrs in self.graph.edges(data=True)
|
|
68
|
+
]
|
|
69
|
+
metadata = dict(self.metadata)
|
|
70
|
+
metadata["node_count"] = len(nodes)
|
|
71
|
+
metadata["edge_count"] = len(edges)
|
|
72
|
+
return {"nodes": nodes, "edges": edges, "metadata": metadata}
|
|
73
|
+
|
|
74
|
+
def get_resource_payload(self, resource_id: str) -> Dict[str, Any]:
|
|
75
|
+
with self._lock:
|
|
76
|
+
if not self.graph.has_node(resource_id):
|
|
77
|
+
raise KeyError(resource_id)
|
|
78
|
+
|
|
79
|
+
node = self._serialize_node(resource_id, dict(self.graph.nodes[resource_id]))
|
|
80
|
+
incoming = [
|
|
81
|
+
self._serialize_edge(source, resource_id, dict(attrs))
|
|
82
|
+
for source, _, attrs in self.graph.in_edges(resource_id, data=True)
|
|
83
|
+
]
|
|
84
|
+
outgoing = [
|
|
85
|
+
self._serialize_edge(resource_id, target, dict(attrs))
|
|
86
|
+
for _, target, attrs in self.graph.out_edges(resource_id, data=True)
|
|
87
|
+
]
|
|
88
|
+
return {"node": node, "incoming": incoming, "outgoing": outgoing}
|