Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/validate-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name: Validate Integration (Tooling)
on:
pull_request:
branches: [master, main]
workflow_dispatch:

jobs:
validate:
Expand All @@ -20,4 +21,4 @@ jobs:
- name: Validate
uses: autohive-ai/autohive-integrations-tooling@1.0.2
with:
base_ref: origin/${{ github.base_ref }}
base_ref: origin/${{ github.base_ref || 'master' }}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Supports basic HTTP authentication and Bearer token authentication via the SDK.

### Coda

[coda](coda): Comprehensive Coda integration for managing documents, pages, tables, and rows. Supports full CRUD operations for docs (list, get, create, update, delete) and pages (list, get, create with HTML/Markdown content, update metadata, delete). Includes table and column discovery (list tables/columns, get table/column details) and complete row management (list with filtering/sorting, get, upsert with keyColumns, update, delete single/multiple). Features Bearer token authentication, pagination support, async processing (HTTP 202 responses), multiple value formats (simple/rich), and comprehensive error handling. Ideal for document automation, content management, and data synchronization workflows.
[coda](coda): Comprehensive Coda integration for managing documents, pages, tables, and rows. Supports full CRUD operations for docs (list, get, create, update, delete) and pages (list, get, create with HTML/Markdown content, update metadata, delete). Includes table and column discovery (list tables/columns, get table/column details) and complete row management (list with filtering/sorting, get, upsert with keyColumns, update, delete single/multiple). Features Bearer token (API token) authentication, pagination support, async processing (HTTP 202 responses), multiple value formats (simple/rich), and comprehensive error handling. Ideal for document automation, content management, and data synchronization workflows.

### ElevenLabs

Expand Down
16 changes: 4 additions & 12 deletions aws/actions/cloudtrail.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,9 @@ async def execute(self, inputs: Dict[str, Any], context: ExecutionContext):
for attr in inputs["lookup_attributes"]
]
if inputs.get("start_time"):
kwargs["StartTime"] = datetime.fromisoformat(
inputs["start_time"].replace("Z", "+00:00")
)
kwargs["StartTime"] = datetime.fromisoformat(inputs["start_time"].replace("Z", "+00:00"))
if inputs.get("end_time"):
kwargs["EndTime"] = datetime.fromisoformat(
inputs["end_time"].replace("Z", "+00:00")
)
kwargs["EndTime"] = datetime.fromisoformat(inputs["end_time"].replace("Z", "+00:00"))
if inputs.get("next_token"):
kwargs["NextToken"] = inputs["next_token"]
response = await run_sync(client.lookup_events, **kwargs)
Expand Down Expand Up @@ -68,9 +64,7 @@ async def execute(self, inputs: Dict[str, Any], context: ExecutionContext):
client = create_boto3_client(context, "cloudtrail")
kwargs = {"Name": inputs["trail_name"]}
response = await run_sync(client.get_trail_status, **kwargs)
trail_status = {
k: v for k, v in response.items() if k != "ResponseMetadata"
}
trail_status = {k: v for k, v in response.items() if k != "ResponseMetadata"}
return success_result({"trail_status": trail_status})
except Exception as e:
return error_result(e)
Expand All @@ -89,9 +83,7 @@ async def execute(self, inputs: Dict[str, Any], context: ExecutionContext):
{
"trail_arn": response.get("TrailARN"),
"event_selectors": response.get("EventSelectors", []),
"advanced_event_selectors": response.get(
"AdvancedEventSelectors", []
),
"advanced_event_selectors": response.get("AdvancedEventSelectors", []),
}
)
except Exception as e:
Expand Down
16 changes: 4 additions & 12 deletions aws/actions/cloudwatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,15 @@ class GetMetricDataAction(ActionHandler):
async def execute(self, inputs: Dict[str, Any], context: ExecutionContext):
try:
client = create_boto3_client(context, "cloudwatch")
start_time = datetime.fromisoformat(
inputs["start_time"].replace("Z", "+00:00")
)
start_time = datetime.fromisoformat(inputs["start_time"].replace("Z", "+00:00"))
end_time = datetime.fromisoformat(inputs["end_time"].replace("Z", "+00:00"))
kwargs = {
"MetricDataQueries": inputs["metric_data_queries"],
"StartTime": start_time,
"EndTime": end_time,
}
response = await run_sync(client.get_metric_data, **kwargs)
return success_result(
{"metric_data_results": response.get("MetricDataResults", [])}
)
return success_result({"metric_data_results": response.get("MetricDataResults", [])})
except Exception as e:
return error_result(e)

Expand Down Expand Up @@ -99,13 +95,9 @@ async def execute(self, inputs: Dict[str, Any], context: ExecutionContext):
if inputs.get("history_item_type"):
kwargs["HistoryItemType"] = inputs["history_item_type"]
if inputs.get("start_date"):
kwargs["StartDate"] = datetime.fromisoformat(
inputs["start_date"].replace("Z", "+00:00")
)
kwargs["StartDate"] = datetime.fromisoformat(inputs["start_date"].replace("Z", "+00:00"))
if inputs.get("end_date"):
kwargs["EndDate"] = datetime.fromisoformat(
inputs["end_date"].replace("Z", "+00:00")
)
kwargs["EndDate"] = datetime.fromisoformat(inputs["end_date"].replace("Z", "+00:00"))
if inputs.get("next_token"):
kwargs["NextToken"] = inputs["next_token"]
response = await run_sync(client.describe_alarm_history, **kwargs)
Expand Down
20 changes: 5 additions & 15 deletions aws/actions/security_hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,14 @@ async def execute(self, inputs: Dict[str, Any], context: ExecutionContext):
for i in range(0, len(finding_arns), 100):
batch = finding_arns[i : i + 100]
lookup_kwargs = {
"Filters": {
"Id": [{"Value": arn, "Comparison": "EQUALS"} for arn in batch]
},
"Filters": {"Id": [{"Value": arn, "Comparison": "EQUALS"} for arn in batch]},
"MaxResults": len(batch),
}
lookup_response = await run_sync(client.get_findings, **lookup_kwargs)
findings.extend(lookup_response.get("Findings", []))

# Build FindingIdentifiers from the looked-up findings
finding_identifiers = [
{"Id": f["Id"], "ProductArn": f["ProductArn"]} for f in findings
]
finding_identifiers = [{"Id": f["Id"], "ProductArn": f["ProductArn"]} for f in findings]

if not finding_identifiers:
return success_result(
Expand Down Expand Up @@ -164,21 +160,15 @@ async def fetch_insight_result(insight):
"group_by_attribute": insight.get("GroupByAttribute"),
}
try:
result_response = await run_sync(
client.get_insight_results, InsightArn=insight["InsightArn"]
)
result_response = await run_sync(client.get_insight_results, InsightArn=insight["InsightArn"])
insight_data["results"] = result_response.get("InsightResults", {})
except Exception as inner_e:
insight_data["results"] = None
insight_data["error"] = str(inner_e)
return insight_data

enriched_insights = await asyncio.gather(
*[fetch_insight_result(insight) for insight in insights]
)
enriched_insights = await asyncio.gather(*[fetch_insight_result(insight) for insight in insights])

return success_result(
{"insights": enriched_insights, "next_token": response.get("NextToken")}
)
return success_result({"insights": enriched_insights, "next_token": response.get("NextToken")})
except Exception as e:
return error_result(e)
2 changes: 1 addition & 1 deletion aws/config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "aws",
"name": "AWS - Security",
"display_name": "Amazon Web Services",
"version": "1.0.0",
"description": "AWS security and monitoring integration for Security Hub, GuardDuty, CloudWatch, and CloudTrail",
Expand Down
8 changes: 2 additions & 6 deletions aws/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ def create_boto3_client(context: ExecutionContext, service_name: str):
access_key = credentials.get("aws_access_key_id")
secret_key = credentials.get("aws_secret_access_key")
if not access_key or not secret_key:
raise ValueError(
"AWS credentials are missing: aws_access_key_id and aws_secret_access_key are required"
)
raise ValueError("AWS credentials are missing: aws_access_key_id and aws_secret_access_key are required")
return boto3.client(
service_name,
aws_access_key_id=access_key,
Expand Down Expand Up @@ -53,6 +51,4 @@ def error_result(e: Exception) -> ActionResult:
if hasattr(e, "response"):
error_code = e.response.get("Error", {}).get("Code", "")
error_msg = e.response.get("Error", {}).get("Message", error_msg)
return ActionResult(
data={"result": False, "error": error_msg, "error_code": error_code}
)
return ActionResult(data={"result": False, "error": error_msg, "error_code": error_code})
60 changes: 15 additions & 45 deletions aws/tests/test_aws.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,10 @@ async def test_get_findings():
async def test_get_finding_details():
"""Test retrieving details for a specific Security Hub finding."""
print("\n=== Testing get_finding_details ===")
inputs = {
"finding_arn": "arn:aws:securityhub:us-east-1:123456789012:finding/example"
}
inputs = {"finding_arn": "arn:aws:securityhub:us-east-1:123456789012:finding/example"}
async with ExecutionContext(auth=TEST_AUTH) as context:
try:
result = await integration.execute_action(
"get_finding_details", inputs, context
)
result = await integration.execute_action("get_finding_details", inputs, context)
print(f"Result: {result}")
return result
except Exception as e:
Expand All @@ -69,9 +65,7 @@ async def test_update_finding_workflow():
}
async with ExecutionContext(auth=TEST_AUTH) as context:
try:
result = await integration.execute_action(
"update_finding_workflow", inputs, context
)
result = await integration.execute_action("update_finding_workflow", inputs, context)
print(f"Result: {result}")
return result
except Exception as e:
Expand Down Expand Up @@ -118,9 +112,7 @@ async def test_list_guardduty_findings():
inputs = {"detector_id": "YOUR_DETECTOR_ID", "max_results": 10}
async with ExecutionContext(auth=TEST_AUTH) as context:
try:
result = await integration.execute_action(
"list_guardduty_findings", inputs, context
)
result = await integration.execute_action("list_guardduty_findings", inputs, context)
print(f"Result: {result}")
return result
except Exception as e:
Expand All @@ -134,9 +126,7 @@ async def test_get_guardduty_finding_details():
inputs = {"detector_id": "YOUR_DETECTOR_ID", "finding_ids": ["example-finding-id"]}
async with ExecutionContext(auth=TEST_AUTH) as context:
try:
result = await integration.execute_action(
"get_guardduty_finding_details", inputs, context
)
result = await integration.execute_action("get_guardduty_finding_details", inputs, context)
print(f"Result: {result}")
return result
except Exception as e:
Expand All @@ -150,9 +140,7 @@ async def test_archive_findings():
inputs = {"detector_id": "YOUR_DETECTOR_ID", "finding_ids": ["example-finding-id"]}
async with ExecutionContext(auth=TEST_AUTH) as context:
try:
result = await integration.execute_action(
"archive_findings", inputs, context
)
result = await integration.execute_action("archive_findings", inputs, context)
print(f"Result: {result}")
return result
except Exception as e:
Expand Down Expand Up @@ -198,9 +186,7 @@ async def test_get_metric_data():
}
async with ExecutionContext(auth=TEST_AUTH) as context:
try:
result = await integration.execute_action(
"get_metric_data", inputs, context
)
result = await integration.execute_action("get_metric_data", inputs, context)
print(f"Result: {result}")
return result
except Exception as e:
Expand All @@ -214,9 +200,7 @@ async def test_describe_alarms():
inputs = {"max_records": 10}
async with ExecutionContext(auth=TEST_AUTH) as context:
try:
result = await integration.execute_action(
"describe_alarms", inputs, context
)
result = await integration.execute_action("describe_alarms", inputs, context)
print(f"Result: {result}")
return result
except Exception as e:
Expand All @@ -230,9 +214,7 @@ async def test_get_alarm_history():
inputs = {"max_records": 10}
async with ExecutionContext(auth=TEST_AUTH) as context:
try:
result = await integration.execute_action(
"get_alarm_history", inputs, context
)
result = await integration.execute_action("get_alarm_history", inputs, context)
print(f"Result: {result}")
return result
except Exception as e:
Expand All @@ -250,9 +232,7 @@ async def test_set_alarm_state():
}
async with ExecutionContext(auth=TEST_AUTH) as context:
try:
result = await integration.execute_action(
"set_alarm_state", inputs, context
)
result = await integration.execute_action("set_alarm_state", inputs, context)
print(f"Result: {result}")
return result
except Exception as e:
Expand All @@ -271,9 +251,7 @@ async def test_describe_log_groups():
inputs = {"limit": 10}
async with ExecutionContext(auth=TEST_AUTH) as context:
try:
result = await integration.execute_action(
"describe_log_groups", inputs, context
)
result = await integration.execute_action("describe_log_groups", inputs, context)
print(f"Result: {result}")
return result
except Exception as e:
Expand All @@ -287,9 +265,7 @@ async def test_filter_log_events():
inputs = {"log_group_name": "/aws/lambda/test-function", "limit": 10}
async with ExecutionContext(auth=TEST_AUTH) as context:
try:
result = await integration.execute_action(
"filter_log_events", inputs, context
)
result = await integration.execute_action("filter_log_events", inputs, context)
print(f"Result: {result}")
return result
except Exception as e:
Expand Down Expand Up @@ -340,9 +316,7 @@ async def test_describe_trails():
inputs = {}
async with ExecutionContext(auth=TEST_AUTH) as context:
try:
result = await integration.execute_action(
"describe_trails", inputs, context
)
result = await integration.execute_action("describe_trails", inputs, context)
print(f"Result: {result}")
return result
except Exception as e:
Expand All @@ -356,9 +330,7 @@ async def test_get_trail_status():
inputs = {"trail_name": "management-events"}
async with ExecutionContext(auth=TEST_AUTH) as context:
try:
result = await integration.execute_action(
"get_trail_status", inputs, context
)
result = await integration.execute_action("get_trail_status", inputs, context)
print(f"Result: {result}")
return result
except Exception as e:
Expand All @@ -372,9 +344,7 @@ async def test_get_event_selectors():
inputs = {"trail_name": "management-events"}
async with ExecutionContext(auth=TEST_AUTH) as context:
try:
result = await integration.execute_action(
"get_event_selectors", inputs, context
)
result = await integration.execute_action("get_event_selectors", inputs, context)
print(f"Result: {result}")
return result
except Exception as e:
Expand Down
Loading
Loading