stackport 0.1.6__tar.gz → 0.1.7__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.
- {stackport-0.1.6/stackport.egg-info → stackport-0.1.7}/PKG-INFO +1 -1
- {stackport-0.1.6 → stackport-0.1.7}/backend/routes/resources.py +36 -4
- {stackport-0.1.6 → stackport-0.1.7}/pyproject.toml +1 -1
- {stackport-0.1.6 → stackport-0.1.7/stackport.egg-info}/PKG-INFO +1 -1
- {stackport-0.1.6 → stackport-0.1.7}/LICENSE +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/MANIFEST.in +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/README.md +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/backend/__init__.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/backend/aws_client.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/backend/cache.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/backend/config.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/backend/main.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/backend/routes/__init__.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/backend/routes/dynamodb.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/backend/routes/ec2.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/backend/routes/iam.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/backend/routes/lambda_svc.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/backend/routes/logs.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/backend/routes/s3.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/backend/routes/sqs.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/backend/routes/stats.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/setup.cfg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/stackport.egg-info/SOURCES.txt +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/stackport.egg-info/dependency_links.txt +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/stackport.egg-info/entry_points.txt +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/stackport.egg-info/requires.txt +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/stackport.egg-info/top_level.txt +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/tests/test_cache.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/tests/test_client.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/tests/test_config.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/tests/test_dynamodb_routes.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/tests/test_ec2_routes.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/tests/test_iam_routes.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/tests/test_lambda_routes.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/tests/test_logs_routes.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/tests/test_registries.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/tests/test_routes.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/tests/test_sqs_routes.py +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/assets/index-DI-V3ZCb.js +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/assets/index-DM3oKaVN.css +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/acm.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/apigateway.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/appsync.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/athena.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/cloudformation.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/cloudfront.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/cognito-idp.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/dynamodb.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/ec2.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/ecr.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/ecs.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/elasticache.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/elasticfilesystem.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/elasticloadbalancing.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/elasticmapreduce.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/events.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/firehose.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/glue.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/iam.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/kinesis.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/kms.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/lambda.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/logs.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/monitoring.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/rds.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/route53.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/s3.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/secretsmanager.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/ses.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/sns.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/sqs.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/ssm.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/stepfunctions.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/aws-icons/wafv2.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/favicon.png +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/favicon.svg +0 -0
- {stackport-0.1.6 → stackport-0.1.7}/ui/dist/index.html +0 -0
|
@@ -71,6 +71,15 @@ DESCRIBE_REGISTRY: dict[tuple[str, str], tuple[str, str, str, str | None]] = {
|
|
|
71
71
|
("elasticmapreduce", "clusters"): ("emr", "describe_cluster", "ClusterId", "Cluster"),
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
# Override the generic _ID_FIELDS for services where the default order picks
|
|
75
|
+
# the wrong field (e.g. Arn before Name, or Name before Id).
|
|
76
|
+
_PREFERRED_ID_FIELD: dict[tuple[str, str], str] = {
|
|
77
|
+
("route53", "hosted_zones"): "Id",
|
|
78
|
+
("events", "rules"): "Name",
|
|
79
|
+
("events", "event_buses"): "Name",
|
|
80
|
+
("wafv2", "web_acls"): "Name",
|
|
81
|
+
}
|
|
82
|
+
|
|
74
83
|
# Known ID field names for extracting a resource identifier from list results
|
|
75
84
|
_ID_FIELDS = [
|
|
76
85
|
"BucketName",
|
|
@@ -119,11 +128,13 @@ _ID_FIELDS = [
|
|
|
119
128
|
]
|
|
120
129
|
|
|
121
130
|
|
|
122
|
-
def _extract_id(item) -> str:
|
|
131
|
+
def _extract_id(item, preferred_field: str | None = None) -> str:
|
|
123
132
|
"""Extract a usable ID from a list API result item."""
|
|
124
133
|
if isinstance(item, str):
|
|
125
134
|
return item
|
|
126
135
|
if isinstance(item, dict):
|
|
136
|
+
if preferred_field and preferred_field in item:
|
|
137
|
+
return str(item[preferred_field])
|
|
127
138
|
for field in _ID_FIELDS:
|
|
128
139
|
if field in item:
|
|
129
140
|
return str(item[field])
|
|
@@ -134,12 +145,12 @@ def _extract_id(item) -> str:
|
|
|
134
145
|
return str(item)
|
|
135
146
|
|
|
136
147
|
|
|
137
|
-
def _summarize_item(item) -> dict:
|
|
148
|
+
def _summarize_item(item, preferred_field: str | None = None) -> dict:
|
|
138
149
|
"""Create a summary dict from a list API result item."""
|
|
139
150
|
if isinstance(item, str):
|
|
140
151
|
return {"id": item}
|
|
141
152
|
if isinstance(item, dict):
|
|
142
|
-
summary = {"id": _extract_id(item)}
|
|
153
|
+
summary = {"id": _extract_id(item, preferred_field)}
|
|
143
154
|
for key, value in item.items():
|
|
144
155
|
if isinstance(value, (str, int, float, bool)) or value is None:
|
|
145
156
|
summary[key] = value
|
|
@@ -171,7 +182,8 @@ def list_resources(service: str):
|
|
|
171
182
|
# Handle nested structures (e.g., cloudfront DistributionList.Items)
|
|
172
183
|
if isinstance(items, dict) and "Items" in items:
|
|
173
184
|
items = items.get("Items", []) or []
|
|
174
|
-
|
|
185
|
+
preferred = _PREFERRED_ID_FIELD.get((service, resource_type))
|
|
186
|
+
resources[resource_type] = [_summarize_item(item, preferred) for item in items]
|
|
175
187
|
except Exception:
|
|
176
188
|
logger.debug("Failed to list %s/%s", service, resource_type, exc_info=True)
|
|
177
189
|
resources[resource_type] = []
|
|
@@ -188,6 +200,26 @@ def get_resource_detail(service: str, res_type: str, res_id: str):
|
|
|
188
200
|
if cached is not None:
|
|
189
201
|
return cached
|
|
190
202
|
|
|
203
|
+
# WAFv2 get_web_acl requires Name, Scope, AND Id — resolve Id from list first
|
|
204
|
+
if (service, res_type) == ("wafv2", "web_acls"):
|
|
205
|
+
try:
|
|
206
|
+
client = get_client("wafv2")
|
|
207
|
+
acls = client.list_web_acls(Scope="REGIONAL").get("WebACLs", [])
|
|
208
|
+
match = next((a for a in acls if a.get("Name") == res_id), None)
|
|
209
|
+
if not match:
|
|
210
|
+
raise HTTPException(404, f"Web ACL '{res_id}' not found")
|
|
211
|
+
resp = client.get_web_acl(Name=res_id, Scope="REGIONAL", Id=match["Id"])
|
|
212
|
+
resp.pop("ResponseMetadata", None)
|
|
213
|
+
detail = _serialize(resp.get("WebACL", resp))
|
|
214
|
+
result = {"service": service, "type": res_type, "id": res_id, "detail": detail}
|
|
215
|
+
cache.set(cache_key, result, ttl=5)
|
|
216
|
+
return result
|
|
217
|
+
except HTTPException:
|
|
218
|
+
raise
|
|
219
|
+
except Exception as exc:
|
|
220
|
+
logger.warning("Failed to get detail for %s/%s/%s", service, res_type, res_id, exc_info=True)
|
|
221
|
+
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
|
222
|
+
|
|
191
223
|
lookup = DESCRIBE_REGISTRY.get((service, res_type))
|
|
192
224
|
if not lookup:
|
|
193
225
|
raise HTTPException(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|