stackport 0.2.2__tar.gz → 0.2.4__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.
Files changed (97) hide show
  1. {stackport-0.2.2/stackport.egg-info → stackport-0.2.4}/PKG-INFO +5 -5
  2. {stackport-0.2.2 → stackport-0.2.4}/README.md +4 -4
  3. {stackport-0.2.2 → stackport-0.2.4}/backend/routes/resources.py +30 -0
  4. {stackport-0.2.2 → stackport-0.2.4}/backend/routes/stats.py +4 -1
  5. {stackport-0.2.2 → stackport-0.2.4}/backend/routes/tags.py +52 -0
  6. {stackport-0.2.2 → stackport-0.2.4}/pyproject.toml +1 -1
  7. {stackport-0.2.2 → stackport-0.2.4/stackport.egg-info}/PKG-INFO +5 -5
  8. {stackport-0.2.2 → stackport-0.2.4}/tests/test_tags_routes.py +5 -2
  9. {stackport-0.2.2 → stackport-0.2.4}/LICENSE +0 -0
  10. {stackport-0.2.2 → stackport-0.2.4}/MANIFEST.in +0 -0
  11. {stackport-0.2.2 → stackport-0.2.4}/backend/__init__.py +0 -0
  12. {stackport-0.2.2 → stackport-0.2.4}/backend/aws_client.py +0 -0
  13. {stackport-0.2.2 → stackport-0.2.4}/backend/cache.py +0 -0
  14. {stackport-0.2.2 → stackport-0.2.4}/backend/cli.py +0 -0
  15. {stackport-0.2.2 → stackport-0.2.4}/backend/config.py +0 -0
  16. {stackport-0.2.2 → stackport-0.2.4}/backend/main.py +0 -0
  17. {stackport-0.2.2 → stackport-0.2.4}/backend/routes/__init__.py +0 -0
  18. {stackport-0.2.2 → stackport-0.2.4}/backend/routes/common.py +0 -0
  19. {stackport-0.2.2 → stackport-0.2.4}/backend/routes/dynamodb.py +0 -0
  20. {stackport-0.2.2 → stackport-0.2.4}/backend/routes/ec2.py +0 -0
  21. {stackport-0.2.2 → stackport-0.2.4}/backend/routes/endpoints.py +0 -0
  22. {stackport-0.2.2 → stackport-0.2.4}/backend/routes/iam.py +0 -0
  23. {stackport-0.2.2 → stackport-0.2.4}/backend/routes/lambda_svc.py +0 -0
  24. {stackport-0.2.2 → stackport-0.2.4}/backend/routes/logs.py +0 -0
  25. {stackport-0.2.2 → stackport-0.2.4}/backend/routes/s3.py +0 -0
  26. {stackport-0.2.2 → stackport-0.2.4}/backend/routes/secretsmanager.py +0 -0
  27. {stackport-0.2.2 → stackport-0.2.4}/backend/routes/sqs.py +0 -0
  28. {stackport-0.2.2 → stackport-0.2.4}/backend/schemas/__init__.py +0 -0
  29. {stackport-0.2.2 → stackport-0.2.4}/backend/schemas/dynamodb.py +0 -0
  30. {stackport-0.2.2 → stackport-0.2.4}/backend/schemas/s3.py +0 -0
  31. {stackport-0.2.2 → stackport-0.2.4}/backend/schemas/sqs.py +0 -0
  32. {stackport-0.2.2 → stackport-0.2.4}/backend/schemas/tags.py +0 -0
  33. {stackport-0.2.2 → stackport-0.2.4}/backend/websocket.py +0 -0
  34. {stackport-0.2.2 → stackport-0.2.4}/setup.cfg +0 -0
  35. {stackport-0.2.2 → stackport-0.2.4}/stackport.egg-info/SOURCES.txt +0 -0
  36. {stackport-0.2.2 → stackport-0.2.4}/stackport.egg-info/dependency_links.txt +0 -0
  37. {stackport-0.2.2 → stackport-0.2.4}/stackport.egg-info/entry_points.txt +0 -0
  38. {stackport-0.2.2 → stackport-0.2.4}/stackport.egg-info/requires.txt +0 -0
  39. {stackport-0.2.2 → stackport-0.2.4}/stackport.egg-info/top_level.txt +0 -0
  40. {stackport-0.2.2 → stackport-0.2.4}/tests/test_cache.py +0 -0
  41. {stackport-0.2.2 → stackport-0.2.4}/tests/test_cli.py +0 -0
  42. {stackport-0.2.2 → stackport-0.2.4}/tests/test_client.py +0 -0
  43. {stackport-0.2.2 → stackport-0.2.4}/tests/test_config.py +0 -0
  44. {stackport-0.2.2 → stackport-0.2.4}/tests/test_dynamodb_routes.py +0 -0
  45. {stackport-0.2.2 → stackport-0.2.4}/tests/test_ec2_routes.py +0 -0
  46. {stackport-0.2.2 → stackport-0.2.4}/tests/test_endpoints.py +0 -0
  47. {stackport-0.2.2 → stackport-0.2.4}/tests/test_iam_routes.py +0 -0
  48. {stackport-0.2.2 → stackport-0.2.4}/tests/test_lambda_routes.py +0 -0
  49. {stackport-0.2.2 → stackport-0.2.4}/tests/test_logs_routes.py +0 -0
  50. {stackport-0.2.2 → stackport-0.2.4}/tests/test_multi_endpoint.py +0 -0
  51. {stackport-0.2.2 → stackport-0.2.4}/tests/test_readonly_middleware.py +0 -0
  52. {stackport-0.2.2 → stackport-0.2.4}/tests/test_registries.py +0 -0
  53. {stackport-0.2.2 → stackport-0.2.4}/tests/test_routes.py +0 -0
  54. {stackport-0.2.2 → stackport-0.2.4}/tests/test_s3_routes.py +0 -0
  55. {stackport-0.2.2 → stackport-0.2.4}/tests/test_s3_upload_limit_env.py +0 -0
  56. {stackport-0.2.2 → stackport-0.2.4}/tests/test_secretsmanager_routes.py +0 -0
  57. {stackport-0.2.2 → stackport-0.2.4}/tests/test_sqs_routes.py +0 -0
  58. {stackport-0.2.2 → stackport-0.2.4}/tests/test_websocket.py +0 -0
  59. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/assets/index-B2xjVeE-.js +0 -0
  60. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/assets/index-D_vb_J84.css +0 -0
  61. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/acm.svg +0 -0
  62. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/apigateway.svg +0 -0
  63. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/appsync.svg +0 -0
  64. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/athena.svg +0 -0
  65. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/cloudformation.svg +0 -0
  66. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/cloudfront.svg +0 -0
  67. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/cognito-idp.svg +0 -0
  68. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/dynamodb.svg +0 -0
  69. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/ec2.svg +0 -0
  70. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/ecr.svg +0 -0
  71. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/ecs.svg +0 -0
  72. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/elasticache.svg +0 -0
  73. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/elasticfilesystem.svg +0 -0
  74. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/elasticloadbalancing.svg +0 -0
  75. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/elasticmapreduce.svg +0 -0
  76. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/events.svg +0 -0
  77. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/firehose.svg +0 -0
  78. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/glue.svg +0 -0
  79. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/iam.svg +0 -0
  80. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/kinesis.svg +0 -0
  81. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/kms.svg +0 -0
  82. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/lambda.svg +0 -0
  83. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/logs.svg +0 -0
  84. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/monitoring.svg +0 -0
  85. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/rds.svg +0 -0
  86. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/route53.svg +0 -0
  87. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/s3.svg +0 -0
  88. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/secretsmanager.svg +0 -0
  89. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/ses.svg +0 -0
  90. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/sns.svg +0 -0
  91. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/sqs.svg +0 -0
  92. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/ssm.svg +0 -0
  93. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/stepfunctions.svg +0 -0
  94. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/aws-icons/wafv2.svg +0 -0
  95. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/favicon.png +0 -0
  96. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/favicon.svg +0 -0
  97. {stackport-0.2.2 → stackport-0.2.4}/ui/dist/index.html +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stackport
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Universal AWS resource browser for local emulators
5
5
  Author: Davi Reis Vieira
6
6
  License: MIT
@@ -54,13 +54,13 @@ Dynamic: license-file
54
54
  ## Screenshots
55
55
 
56
56
  **Dashboard** — Service overview with resource counts and health status
57
- ![StackPort Dashboard](https://raw.githubusercontent.com/DaviReisVieira/stackport/main/docs/images/dashboard.jpeg)
57
+ ![StackPort Dashboard](https://raw.githubusercontent.com/DaviReisVieira/stackport/main/docs/images/dashboard.jpeg?v=1.0)
58
58
 
59
- **DynamoDB Browser** — Query and scan tables with schema visualization
60
- ![DynamoDB Resources](https://raw.githubusercontent.com/DaviReisVieira/stackport/main/docs/images/dynamo.jpeg)
59
+ **Lambda Browser** — Function list with config, aliases, versions, event sources, and invocation panel
60
+ ![Lambda Resources](https://raw.githubusercontent.com/DaviReisVieira/stackport/main/docs/images/lambda.jpeg?v=1.0)
61
61
 
62
62
  **S3 Browser** — File browser with upload, download, folder navigation, and tagging
63
- ![S3 Browser](https://raw.githubusercontent.com/DaviReisVieira/stackport/main/docs/images/s3.jpeg)
63
+ ![S3 Browser](https://raw.githubusercontent.com/DaviReisVieira/stackport/main/docs/images/s3.jpeg?v=1.0)
64
64
 
65
65
  ## Features
66
66
 
@@ -19,13 +19,13 @@
19
19
  ## Screenshots
20
20
 
21
21
  **Dashboard** — Service overview with resource counts and health status
22
- ![StackPort Dashboard](https://raw.githubusercontent.com/DaviReisVieira/stackport/main/docs/images/dashboard.jpeg)
22
+ ![StackPort Dashboard](https://raw.githubusercontent.com/DaviReisVieira/stackport/main/docs/images/dashboard.jpeg?v=1.0)
23
23
 
24
- **DynamoDB Browser** — Query and scan tables with schema visualization
25
- ![DynamoDB Resources](https://raw.githubusercontent.com/DaviReisVieira/stackport/main/docs/images/dynamo.jpeg)
24
+ **Lambda Browser** — Function list with config, aliases, versions, event sources, and invocation panel
25
+ ![Lambda Resources](https://raw.githubusercontent.com/DaviReisVieira/stackport/main/docs/images/lambda.jpeg?v=1.0)
26
26
 
27
27
  **S3 Browser** — File browser with upload, download, folder navigation, and tagging
28
- ![S3 Browser](https://raw.githubusercontent.com/DaviReisVieira/stackport/main/docs/images/s3.jpeg)
28
+ ![S3 Browser](https://raw.githubusercontent.com/DaviReisVieira/stackport/main/docs/images/s3.jpeg?v=1.0)
29
29
 
30
30
  ## Features
31
31
 
@@ -66,6 +66,7 @@ DESCRIBE_REGISTRY: dict[tuple[str, str], tuple[str, str, str, str | None]] = {
66
66
  ("glue", "crawlers"): ("glue", "get_crawler", "Name", "Crawler"),
67
67
  ("athena", "workgroups"): ("athena", "get_work_group", "WorkGroup", "WorkGroup"),
68
68
  # API
69
+ ("apigateway", "rest_apis"): ("apigateway", "get_rest_api", "restApiId", None),
69
70
  ("apigateway", "apis"): ("apigatewayv2", "get_api", "ApiId", None),
70
71
  ("appsync", "graphql_apis"): ("appsync", "get_graphql_api", "apiId", "graphqlApi"),
71
72
  # EMR
@@ -79,6 +80,10 @@ _PREFERRED_ID_FIELD: dict[tuple[str, str], str] = {
79
80
  ("events", "rules"): "Name",
80
81
  ("events", "event_buses"): "Name",
81
82
  ("wafv2", "web_acls"): "Name",
83
+ ("appsync", "graphql_apis"): "apiId",
84
+ ("elasticmapreduce", "clusters"): "Id",
85
+ ("cognito-idp", "user_pools"): "Id",
86
+ ("apigateway", "rest_apis"): "id",
82
87
  }
83
88
 
84
89
  # Known ID field names for extracting a resource identifier from list results
@@ -126,6 +131,8 @@ _ID_FIELDS = [
126
131
  "CrawlerName",
127
132
  "DatabaseName",
128
133
  "DistributionId",
134
+ "apiId",
135
+ "ClusterId",
129
136
  ]
130
137
 
131
138
 
@@ -201,6 +208,29 @@ def get_resource_detail(service: str, res_type: str, res_id: str, endpoint_url:
201
208
  if cached is not None:
202
209
  return cached
203
210
 
211
+ # SES identities are plain strings — aggregate detail from multiple APIs
212
+ if (service, res_type) == ("ses", "identities"):
213
+ try:
214
+ client = get_client("ses", endpoint_url)
215
+ verif = client.get_identity_verification_attributes(Identities=[res_id])
216
+ attrs = verif.get("VerificationAttributes", {}).get(res_id, {})
217
+ dkim = client.get_identity_dkim_attributes(Identities=[res_id])
218
+ dkim_attrs = dkim.get("DkimAttributes", {}).get(res_id, {})
219
+ detail = _serialize({
220
+ "Identity": res_id,
221
+ "VerificationStatus": attrs.get("VerificationStatus", "Unknown"),
222
+ "VerificationToken": attrs.get("VerificationToken"),
223
+ "DkimEnabled": dkim_attrs.get("DkimEnabled", False),
224
+ "DkimVerificationStatus": dkim_attrs.get("DkimVerificationStatus", "Unknown"),
225
+ "DkimTokens": dkim_attrs.get("DkimTokens", []),
226
+ })
227
+ result = {"service": service, "type": res_type, "id": res_id, "detail": detail}
228
+ cache.set(cache_key, result, ttl=5)
229
+ return result
230
+ except Exception as exc:
231
+ logger.warning("Failed to get detail for %s/%s/%s", service, res_type, res_id, exc_info=True)
232
+ raise HTTPException(status_code=500, detail=str(exc)) from exc
233
+
204
234
  # WAFv2 get_web_acl requires Name, Scope, AND Id — resolve Id from list first
205
235
  if (service, res_type) == ("wafv2", "web_acls"):
206
236
  try:
@@ -75,7 +75,10 @@ SERVICE_REGISTRY: dict[str, list[tuple[str, str, str, str]]] = {
75
75
  ("crawlers", "glue", "get_crawlers", "Crawlers"),
76
76
  ],
77
77
  "athena": [("workgroups", "athena", "list_work_groups", "WorkGroups")],
78
- "apigateway": [("apis", "apigatewayv2", "get_apis", "Items")],
78
+ "apigateway": [
79
+ ("rest_apis", "apigateway", "get_rest_apis", "items"),
80
+ ("apis", "apigatewayv2", "get_apis", "Items"),
81
+ ],
79
82
  "firehose": [("delivery_streams", "firehose", "list_delivery_streams", "DeliveryStreamNames")],
80
83
  "cognito-idp": [("user_pools", "cognito-idp", "list_user_pools", "UserPools")],
81
84
  "cognito-identity": [("identity_pools", "cognito-identity", "list_identity_pools", "IdentityPools")],
@@ -356,6 +356,52 @@ def _set_tags_elasticache_cluster(client: Any, resource_id: str, tags: dict[str,
356
356
  client.add_tags_to_resource(ResourceName=arn, Tags=tag_list)
357
357
 
358
358
 
359
+ def _get_tags_cognito_idp_user_pool(client: Any, resource_id: str) -> dict[str, str]:
360
+ pool = client.describe_user_pool(UserPoolId=resource_id)
361
+ arn = pool["UserPool"]["Arn"]
362
+ resp = client.list_tags_for_resource(ResourceArn=arn)
363
+ return resp.get("Tags", {})
364
+
365
+
366
+ def _get_tags_emr_cluster(client: Any, resource_id: str) -> dict[str, str]:
367
+ resp = client.describe_cluster(ClusterId=resource_id)
368
+ return {t["Key"]: t["Value"] for t in resp["Cluster"].get("Tags", [])}
369
+
370
+
371
+ def _get_tags_apigateway_rest_api(client: Any, resource_id: str) -> dict[str, str]:
372
+ arn = f"arn:aws:apigateway:{client.meta.region_name}::/restapis/{resource_id}"
373
+ resp = client.get_tags(resourceArn=arn)
374
+ return resp.get("tags", {})
375
+
376
+
377
+ def _set_tags_cognito_idp_user_pool(client: Any, resource_id: str, tags: dict[str, str]) -> None:
378
+ pool = client.describe_user_pool(UserPoolId=resource_id)
379
+ arn = pool["UserPool"]["Arn"]
380
+ existing = client.list_tags_for_resource(ResourceArn=arn).get("Tags", {})
381
+ if existing:
382
+ client.untag_resource(ResourceArn=arn, TagKeys=list(existing.keys()))
383
+ if tags:
384
+ client.tag_resource(ResourceArn=arn, Tags=tags)
385
+
386
+
387
+ def _set_tags_emr_cluster(client: Any, resource_id: str, tags: dict[str, str]) -> None:
388
+ existing = _get_tags_emr_cluster(client, resource_id)
389
+ if existing:
390
+ client.remove_tags(ResourceId=resource_id, TagKeys=list(existing.keys()))
391
+ if tags:
392
+ tag_list = [{"Key": k, "Value": v} for k, v in tags.items()]
393
+ client.add_tags(ResourceId=resource_id, Tags=tag_list)
394
+
395
+
396
+ def _set_tags_apigateway_rest_api(client: Any, resource_id: str, tags: dict[str, str]) -> None:
397
+ arn = f"arn:aws:apigateway:{client.meta.region_name}::/restapis/{resource_id}"
398
+ existing = client.get_tags(resourceArn=arn).get("tags", {})
399
+ if existing:
400
+ client.untag_resource(resourceArn=arn, tagKeys=list(existing.keys()))
401
+ if tags:
402
+ client.tag_resource(resourceArn=arn, tags=tags)
403
+
404
+
359
405
  # --- Registries ---
360
406
 
361
407
  TAG_GETTER_REGISTRY: dict[tuple[str, str], tuple[str, Any]] = {
@@ -380,6 +426,9 @@ TAG_GETTER_REGISTRY: dict[tuple[str, str], tuple[str, Any]] = {
380
426
  ("ssm", "parameters"): ("ssm", _get_tags_ssm_parameter),
381
427
  ("elasticloadbalancing", "load_balancers"): ("elbv2", _get_tags_elbv2_load_balancer),
382
428
  ("elasticache", "cache_clusters"): ("elasticache", _get_tags_elasticache_cluster),
429
+ ("cognito-idp", "user_pools"): ("cognito-idp", _get_tags_cognito_idp_user_pool),
430
+ ("elasticmapreduce", "clusters"): ("emr", _get_tags_emr_cluster),
431
+ ("apigateway", "rest_apis"): ("apigateway", _get_tags_apigateway_rest_api),
383
432
  }
384
433
 
385
434
  TAG_SETTER_REGISTRY: dict[tuple[str, str], tuple[str, Any]] = {
@@ -403,6 +452,9 @@ TAG_SETTER_REGISTRY: dict[tuple[str, str], tuple[str, Any]] = {
403
452
  ("ssm", "parameters"): ("ssm", _set_tags_ssm_parameter),
404
453
  ("elasticloadbalancing", "load_balancers"): ("elbv2", _set_tags_elbv2_load_balancer),
405
454
  ("elasticache", "cache_clusters"): ("elasticache", _set_tags_elasticache_cluster),
455
+ ("cognito-idp", "user_pools"): ("cognito-idp", _set_tags_cognito_idp_user_pool),
456
+ ("elasticmapreduce", "clusters"): ("emr", _set_tags_emr_cluster),
457
+ ("apigateway", "rest_apis"): ("apigateway", _set_tags_apigateway_rest_api),
406
458
  }
407
459
 
408
460
  # Delete registry: (service, type) -> (boto3_service, callable(client, resource_id))
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "stackport"
3
- version = "0.2.2"
3
+ version = "0.2.4"
4
4
  description = "Universal AWS resource browser for local emulators"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stackport
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Universal AWS resource browser for local emulators
5
5
  Author: Davi Reis Vieira
6
6
  License: MIT
@@ -54,13 +54,13 @@ Dynamic: license-file
54
54
  ## Screenshots
55
55
 
56
56
  **Dashboard** — Service overview with resource counts and health status
57
- ![StackPort Dashboard](https://raw.githubusercontent.com/DaviReisVieira/stackport/main/docs/images/dashboard.jpeg)
57
+ ![StackPort Dashboard](https://raw.githubusercontent.com/DaviReisVieira/stackport/main/docs/images/dashboard.jpeg?v=1.0)
58
58
 
59
- **DynamoDB Browser** — Query and scan tables with schema visualization
60
- ![DynamoDB Resources](https://raw.githubusercontent.com/DaviReisVieira/stackport/main/docs/images/dynamo.jpeg)
59
+ **Lambda Browser** — Function list with config, aliases, versions, event sources, and invocation panel
60
+ ![Lambda Resources](https://raw.githubusercontent.com/DaviReisVieira/stackport/main/docs/images/lambda.jpeg?v=1.0)
61
61
 
62
62
  **S3 Browser** — File browser with upload, download, folder navigation, and tagging
63
- ![S3 Browser](https://raw.githubusercontent.com/DaviReisVieira/stackport/main/docs/images/s3.jpeg)
63
+ ![S3 Browser](https://raw.githubusercontent.com/DaviReisVieira/stackport/main/docs/images/s3.jpeg?v=1.0)
64
64
 
65
65
  ## Features
66
66
 
@@ -336,7 +336,7 @@ class TestTagsSupported:
336
336
  assert resp.status_code == 200
337
337
  data = resp.json()
338
338
  supported = data["supported"]
339
- assert len(supported) == 21
339
+ assert len(supported) == 24
340
340
 
341
341
  keys = {(s["service"], s["type"]) for s in supported}
342
342
  assert ("s3", "buckets") in keys
@@ -360,6 +360,9 @@ class TestTagsSupported:
360
360
  assert ("ssm", "parameters") in keys
361
361
  assert ("elasticloadbalancing", "load_balancers") in keys
362
362
  assert ("elasticache", "cache_clusters") in keys
363
+ assert ("cognito-idp", "user_pools") in keys
364
+ assert ("elasticmapreduce", "clusters") in keys
365
+ assert ("apigateway", "rest_apis") in keys
363
366
 
364
367
  def test_writable_flag(self):
365
368
  resp = client.get("/api/tags/supported")
@@ -367,7 +370,7 @@ class TestTagsSupported:
367
370
  writable_lookup = {(e["service"], e["type"]): e["writable"] for e in supported}
368
371
  assert writable_lookup[("cloudformation", "stacks")] is False
369
372
  writable_count = sum(1 for e in supported if e["writable"])
370
- assert writable_count == 20
373
+ assert writable_count == 23
371
374
 
372
375
 
373
376
  # --- PUT tags ---
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