mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-12 00:28:31 -05:00
Compare commits
33 Commits
gitbook
...
swiftyos/e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8cd26bba0e | ||
|
|
95bc7dbfca | ||
|
|
ff6ad3270e | ||
|
|
9ab5dd597b | ||
|
|
324216df31 | ||
|
|
3408277c0e | ||
|
|
83d96eb4b5 | ||
|
|
c26a96fc6d | ||
|
|
1aae4e7474 | ||
|
|
5981c4d70b | ||
|
|
8ecadca8a9 | ||
|
|
a7d7192022 | ||
|
|
95ce2c825f | ||
|
|
b9366c9b28 | ||
|
|
79948263c8 | ||
|
|
57223e6343 | ||
|
|
211e53bf5d | ||
|
|
8aba4a5d48 | ||
|
|
6ba9fd9cb4 | ||
|
|
b16c2eed52 | ||
|
|
5af718c9f5 | ||
|
|
a588cf1dc5 | ||
|
|
98a1adc397 | ||
|
|
19728ebc05 | ||
|
|
3e117aac5d | ||
|
|
8dabe6c70d | ||
|
|
a04919beca | ||
|
|
c6b22842a4 | ||
|
|
614f751a90 | ||
|
|
c458bec9c7 | ||
|
|
040bde3f49 | ||
|
|
71028d57d7 | ||
|
|
dbf014f936 |
@@ -173,6 +173,9 @@ EXA_API_KEY=
|
||||
# E2B
|
||||
E2B_API_KEY=
|
||||
|
||||
# Example API Key
|
||||
EXAMPLE_API_KEY=
|
||||
|
||||
# Mem0
|
||||
MEM0_API_KEY=
|
||||
|
||||
|
||||
137
autogpt_platform/backend/backend/blocks/example/_api.py
Normal file
137
autogpt_platform/backend/backend/blocks/example/_api.py
Normal file
@@ -0,0 +1,137 @@
|
||||
"""
|
||||
API module for Example API integration.
|
||||
|
||||
This module provides a example of how to create a client for an API.
|
||||
"""
|
||||
|
||||
# We also have a Json Wrapper library available in backend.util.json
|
||||
from json import JSONDecodeError
|
||||
from typing import Any, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from backend.data.model import APIKeyCredentials
|
||||
|
||||
# This is a wrapper around the requests library that is used to make API requests.
|
||||
from backend.util.request import Requests
|
||||
|
||||
|
||||
class ExampleAPIException(Exception):
|
||||
def __init__(self, message: str, status_code: int):
|
||||
super().__init__(message)
|
||||
self.status_code = status_code
|
||||
|
||||
|
||||
class CreateResourceResponse(BaseModel):
|
||||
message: str
|
||||
is_funny: bool
|
||||
|
||||
|
||||
class GetResourceResponse(BaseModel):
|
||||
message: str
|
||||
is_funny: bool
|
||||
|
||||
|
||||
class ExampleClient:
|
||||
"""Client for the Example API"""
|
||||
|
||||
API_BASE_URL = "https://api.example.com/v1"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
credentials: Optional[APIKeyCredentials] = None,
|
||||
custom_requests: Optional[Requests] = None,
|
||||
):
|
||||
if custom_requests:
|
||||
self._requests = custom_requests
|
||||
else:
|
||||
headers: dict[str, str] = {
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
if credentials:
|
||||
headers["Authorization"] = credentials.auth_header()
|
||||
|
||||
self._requests = Requests(
|
||||
extra_headers=headers,
|
||||
raise_for_status=False,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _handle_response(response) -> Any:
|
||||
"""
|
||||
Handles API response and checks for errors.
|
||||
|
||||
Args:
|
||||
response: The response object from the request.
|
||||
|
||||
Returns:
|
||||
The parsed JSON response data.
|
||||
|
||||
Raises:
|
||||
ExampleAPIException: If the API request fails.
|
||||
"""
|
||||
if not response.ok:
|
||||
try:
|
||||
error_data = response.json()
|
||||
error_message = error_data.get("error", {}).get("message", "")
|
||||
except JSONDecodeError:
|
||||
error_message = response.text
|
||||
|
||||
raise ExampleAPIException(
|
||||
f"Example API request failed ({response.status_code}): {error_message}",
|
||||
response.status_code,
|
||||
)
|
||||
|
||||
response_data = response.json()
|
||||
if "errors" in response_data:
|
||||
# This is an example error and needs to be
|
||||
# replaced with how the real API returns errors
|
||||
error_messages = [
|
||||
error.get("message", "") for error in response_data["errors"]
|
||||
]
|
||||
raise ExampleAPIException(
|
||||
f"Example API returned errors: {', '.join(error_messages)}",
|
||||
response.status_code,
|
||||
)
|
||||
|
||||
return response_data
|
||||
|
||||
def get_resource(self, resource_id: str) -> GetResourceResponse:
|
||||
"""
|
||||
Fetches a resource from the Example API.
|
||||
|
||||
Args:
|
||||
resource_id: The ID of the resource to fetch.
|
||||
|
||||
Returns:
|
||||
The resource data as a GetResourceResponse object.
|
||||
|
||||
Raises:
|
||||
ExampleAPIException: If the API request fails.
|
||||
"""
|
||||
try:
|
||||
response = self._requests.get(
|
||||
f"{self.API_BASE_URL}/resources/{resource_id}"
|
||||
)
|
||||
return GetResourceResponse(**self._handle_response(response))
|
||||
except Exception as e:
|
||||
raise ExampleAPIException(f"Failed to get resource: {str(e)}", 500)
|
||||
|
||||
def create_resource(self, data: dict) -> CreateResourceResponse:
|
||||
"""
|
||||
Creates a new resource via the Example API.
|
||||
|
||||
Args:
|
||||
data: The resource data to create.
|
||||
|
||||
Returns:
|
||||
The created resource data as a CreateResourceResponse object.
|
||||
|
||||
Raises:
|
||||
ExampleAPIException: If the API request fails.
|
||||
"""
|
||||
try:
|
||||
response = self._requests.post(f"{self.API_BASE_URL}/resources", json=data)
|
||||
return CreateResourceResponse(**self._handle_response(response))
|
||||
except Exception as e:
|
||||
raise ExampleAPIException(f"Failed to create resource: {str(e)}", 500)
|
||||
37
autogpt_platform/backend/backend/blocks/example/_auth.py
Normal file
37
autogpt_platform/backend/backend/blocks/example/_auth.py
Normal file
@@ -0,0 +1,37 @@
|
||||
"""
|
||||
Authentication module for Example API integration.
|
||||
|
||||
This module provides credential types and test credentials for the Example API integration.
|
||||
It defines the structure for API key credentials used to authenticate with the Example API
|
||||
and provides mock credentials for testing purposes.
|
||||
"""
|
||||
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import SecretStr
|
||||
|
||||
from backend.data.model import APIKeyCredentials, CredentialsMetaInput
|
||||
from backend.integrations.providers import ProviderName
|
||||
|
||||
# Define the type of credentials input expected for Example API
|
||||
ExampleCredentialsInput = CredentialsMetaInput[
|
||||
Literal[ProviderName.EXAMPLE_PROVIDER], Literal["api_key"]
|
||||
]
|
||||
|
||||
|
||||
# Mock credentials for testing Example API integration
|
||||
TEST_CREDENTIALS = APIKeyCredentials(
|
||||
id="9191c4f0-498f-4235-a79c-59c0e37454d4",
|
||||
provider="example-provider",
|
||||
api_key=SecretStr("mock-example-api-key"),
|
||||
title="Mock Example API key",
|
||||
expires_at=None,
|
||||
)
|
||||
|
||||
# Dictionary representation of test credentials for input fields
|
||||
TEST_CREDENTIALS_INPUT = {
|
||||
"provider": TEST_CREDENTIALS.provider,
|
||||
"id": TEST_CREDENTIALS.id,
|
||||
"type": TEST_CREDENTIALS.type,
|
||||
"title": TEST_CREDENTIALS.title,
|
||||
}
|
||||
154
autogpt_platform/backend/backend/blocks/example/example.py
Normal file
154
autogpt_platform/backend/backend/blocks/example/example.py
Normal file
@@ -0,0 +1,154 @@
|
||||
import logging
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
|
||||
from backend.data.model import APIKeyCredentials, CredentialsField, SchemaField
|
||||
|
||||
from ._api import ExampleClient
|
||||
from ._auth import TEST_CREDENTIALS, TEST_CREDENTIALS_INPUT, ExampleCredentialsInput
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class GreetingMessage(BaseModel):
|
||||
message: str
|
||||
is_funny: bool
|
||||
|
||||
|
||||
class ExampleBlock(Block):
|
||||
|
||||
class Input(BlockSchema):
|
||||
name: str = SchemaField(
|
||||
description="The name of the example block", placeholder="Enter a name"
|
||||
)
|
||||
greetings: list[str] = SchemaField(
|
||||
description="The greetings to display", default=["Hello", "Hi", "Hey"]
|
||||
)
|
||||
is_funny: bool = SchemaField(
|
||||
description="Whether the block is funny",
|
||||
placeholder="True",
|
||||
default=True,
|
||||
# Advanced fields are moved to the "Advanced" dropdown in the UI
|
||||
advanced=True,
|
||||
)
|
||||
greeting_context: str = SchemaField(
|
||||
description="The context of the greeting",
|
||||
placeholder="Enter a context",
|
||||
default="The user is looking for an inspirational greeting",
|
||||
# Hidden fields are not shown in the UI at all
|
||||
hidden=True,
|
||||
)
|
||||
# Only if the block needs credentials
|
||||
credentials: ExampleCredentialsInput = CredentialsField(
|
||||
description="The credentials for the example block"
|
||||
)
|
||||
|
||||
class Output(BlockSchema):
|
||||
response: GreetingMessage = SchemaField(
|
||||
description="The response object generated by the example block."
|
||||
)
|
||||
all_responses: list[GreetingMessage] = SchemaField(
|
||||
description="All the responses from the example block."
|
||||
)
|
||||
greeting_count: int = SchemaField(
|
||||
description="The number of greetings in the input."
|
||||
)
|
||||
error: str = SchemaField(description="The error from the example block")
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
# The unique identifier for the block, this value will be persisted in the DB.
|
||||
# It should be unique and constant across the application run.
|
||||
# Use the UUID format for the ID.
|
||||
id="380694d5-3b2e-4130-bced-b43752b70de9",
|
||||
# The description of the block, explaining what the block does.
|
||||
description="The example block",
|
||||
# The set of categories that the block belongs to.
|
||||
# Each category is an instance of BlockCategory Enum.
|
||||
categories={BlockCategory.BASIC},
|
||||
# The schema, defined as a Pydantic model, for the input data.
|
||||
input_schema=ExampleBlock.Input,
|
||||
# The schema, defined as a Pydantic model, for the output data.
|
||||
output_schema=ExampleBlock.Output,
|
||||
# The list or single sample input data for the block, for testing.
|
||||
# This is an instance of the Input schema with sample values.
|
||||
test_input={
|
||||
"name": "Craig",
|
||||
"greetings": ["Hello", "Hi", "Hey"],
|
||||
"is_funny": True,
|
||||
"credentials": TEST_CREDENTIALS_INPUT,
|
||||
},
|
||||
# The list or single expected output if the test_input is run.
|
||||
# Each output is a tuple of (output_name, output_data).
|
||||
test_output=[
|
||||
("response", GreetingMessage(message="Hello, world!", is_funny=True)),
|
||||
(
|
||||
"response",
|
||||
GreetingMessage(message="Hello, world!", is_funny=True),
|
||||
), # We mock the function
|
||||
(
|
||||
"response",
|
||||
GreetingMessage(message="Hello, world!", is_funny=True),
|
||||
), # We mock the function
|
||||
(
|
||||
"all_responses",
|
||||
[
|
||||
GreetingMessage(message="Hello, world!", is_funny=True),
|
||||
GreetingMessage(message="Hello, world!", is_funny=True),
|
||||
GreetingMessage(message="Hello, world!", is_funny=True),
|
||||
],
|
||||
),
|
||||
("greeting_count", 3),
|
||||
],
|
||||
# Function names on the block implementation to mock on test run.
|
||||
# Each mock is a dictionary with function names as keys and mock implementations as values.
|
||||
test_mock={
|
||||
"my_function_that_can_be_mocked": lambda *args, **kwargs: GreetingMessage(
|
||||
message="Hello, world!", is_funny=True
|
||||
)
|
||||
},
|
||||
# The credentials required for testing the block.
|
||||
# This is an instance of APIKeyCredentials with sample values.
|
||||
test_credentials=TEST_CREDENTIALS,
|
||||
)
|
||||
|
||||
def my_function_that_can_be_mocked(
|
||||
self, name: str, credentials: APIKeyCredentials
|
||||
) -> GreetingMessage:
|
||||
logger.info("my_function_that_can_be_mocked called with input: %s", name)
|
||||
|
||||
# Use the ExampleClient from _api.py to make an API call
|
||||
client = ExampleClient(credentials=credentials)
|
||||
|
||||
# Create a sample resource using the client
|
||||
resource_data = {"name": name, "type": "greeting"}
|
||||
# If your API response object matches the return type of the function,
|
||||
# there is no need to convert the object. In this case we have a different
|
||||
# object type for the response and the return type of the function.
|
||||
return GreetingMessage(**client.create_resource(resource_data).model_dump())
|
||||
|
||||
def run(
|
||||
self, input_data: Input, *, credentials: APIKeyCredentials, **kwargs
|
||||
) -> BlockOutput:
|
||||
"""
|
||||
The run function implements the block's core logic. It processes the input_data
|
||||
and yields the block's output.
|
||||
|
||||
In addition to credentials, the following parameters can be specified:
|
||||
graph_id: The ID of the graph containing this block.
|
||||
node_id: The ID of this block's node in the graph.
|
||||
graph_exec_id: The ID of the current graph execution.
|
||||
node_exec_id: The ID of the current node execution.
|
||||
user_id: The ID of the user executing the block.
|
||||
"""
|
||||
rtn_all_responses: list[GreetingMessage] = []
|
||||
# Here we deomonstrate best practice for blocks that need to yield multiple items.
|
||||
# We yield each item from the list to allow for operations on each element.
|
||||
# We also yield the complete list for situations when the full list is needed.
|
||||
for greeting in input_data.greetings:
|
||||
message = self.my_function_that_can_be_mocked(greeting, credentials)
|
||||
rtn_all_responses.append(message)
|
||||
yield "response", message
|
||||
yield "all_responses", rtn_all_responses
|
||||
yield "greeting_count", len(input_data.greetings)
|
||||
65
autogpt_platform/backend/backend/blocks/example/triggers.py
Normal file
65
autogpt_platform/backend/backend/blocks/example/triggers.py
Normal file
@@ -0,0 +1,65 @@
|
||||
import logging
|
||||
|
||||
from backend.data.block import (
|
||||
Block,
|
||||
BlockCategory,
|
||||
BlockManualWebhookConfig,
|
||||
BlockOutput,
|
||||
BlockSchema,
|
||||
)
|
||||
from backend.data.model import SchemaField
|
||||
from backend.integrations.webhooks.example import ExampleWebhookEventType
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ExampleTriggerBlock(Block):
|
||||
"""
|
||||
A trigger block that is activated by an external webhook event.
|
||||
|
||||
Unlike standard blocks that are manually executed, trigger blocks are automatically
|
||||
activated when a webhook event is received from the specified provider.
|
||||
"""
|
||||
|
||||
class Input(BlockSchema):
|
||||
# The payload field is hidden because it's automatically populated by the webhook
|
||||
# system rather than being manually entered by the user
|
||||
payload: dict = SchemaField(hidden=True)
|
||||
|
||||
class Output(BlockSchema):
|
||||
event_data: dict = SchemaField(
|
||||
description="The contents of the example webhook event."
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
id="7c5933ce-d60c-42dd-9c4e-db82496474a3",
|
||||
description="This block will output the contents of an example webhook event.",
|
||||
categories={BlockCategory.BASIC},
|
||||
input_schema=ExampleTriggerBlock.Input,
|
||||
output_schema=ExampleTriggerBlock.Output,
|
||||
# The webhook_config is a key difference from standard blocks
|
||||
# It defines which external service can trigger this block and what type of events it responds to
|
||||
webhook_config=BlockManualWebhookConfig(
|
||||
provider="example_provider", # The external service that will send webhook events
|
||||
webhook_type=ExampleWebhookEventType.EXAMPLE_EVENT, # The specific event type this block responds to
|
||||
),
|
||||
# Test input for trigger blocks should mimic the payload structure that would be received from the webhook
|
||||
test_input=[
|
||||
{
|
||||
"payload": {
|
||||
"event_type": "example",
|
||||
"data": "Sample webhook data",
|
||||
}
|
||||
}
|
||||
],
|
||||
test_output=[
|
||||
("event_data", {"event_type": "example", "data": "Sample webhook data"})
|
||||
],
|
||||
)
|
||||
|
||||
def run(self, input_data: Input, **kwargs) -> BlockOutput:
|
||||
# For trigger blocks, the run method is called automatically when a webhook event is received
|
||||
# The payload from the webhook is passed in as input_data.payload
|
||||
logger.info("Example trigger block run with payload: %s", input_data.payload)
|
||||
yield "event_data", input_data.payload
|
||||
@@ -2,6 +2,7 @@ from typing import Type
|
||||
|
||||
from backend.blocks.ai_music_generator import AIMusicGeneratorBlock
|
||||
from backend.blocks.ai_shortform_video_block import AIShortformVideoCreatorBlock
|
||||
from backend.blocks.example.example import ExampleBlock
|
||||
from backend.blocks.ideogram import IdeogramModelBlock
|
||||
from backend.blocks.jina.embeddings import JinaEmbeddingBlock
|
||||
from backend.blocks.jina.search import ExtractWebsiteContentBlock, SearchTheWebBlock
|
||||
@@ -23,6 +24,7 @@ from backend.data.cost import BlockCost, BlockCostType
|
||||
from backend.integrations.credentials_store import (
|
||||
anthropic_credentials,
|
||||
did_credentials,
|
||||
example_credentials,
|
||||
groq_credentials,
|
||||
ideogram_credentials,
|
||||
jina_credentials,
|
||||
@@ -267,4 +269,16 @@ BLOCK_COSTS: dict[Type[Block], list[BlockCost]] = {
|
||||
)
|
||||
],
|
||||
SmartDecisionMakerBlock: LLM_COST,
|
||||
ExampleBlock: [
|
||||
BlockCost(
|
||||
cost_amount=1,
|
||||
cost_filter={
|
||||
"credentials": {
|
||||
"id": example_credentials.id,
|
||||
"provider": example_credentials.provider,
|
||||
"type": example_credentials.type,
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
}
|
||||
|
||||
@@ -169,7 +169,16 @@ zerobounce_credentials = APIKeyCredentials(
|
||||
expires_at=None,
|
||||
)
|
||||
|
||||
example_credentials = APIKeyCredentials(
|
||||
id="a2b7f68f-aa6a-4995-99ec-b45b40d33498",
|
||||
provider="example-provider",
|
||||
api_key=SecretStr(settings.secrets.example_api_key),
|
||||
title="Use Credits for Example",
|
||||
expires_at=None,
|
||||
)
|
||||
|
||||
DEFAULT_CREDENTIALS = [
|
||||
example_credentials,
|
||||
ollama_credentials,
|
||||
revid_credentials,
|
||||
ideogram_credentials,
|
||||
@@ -225,6 +234,8 @@ class IntegrationCredentialsStore:
|
||||
all_credentials.append(ollama_credentials)
|
||||
|
||||
# These will only be added if the API key is set
|
||||
if settings.secrets.example_api_key:
|
||||
all_credentials.append(example_credentials)
|
||||
if settings.secrets.revid_api_key:
|
||||
all_credentials.append(revid_credentials)
|
||||
if settings.secrets.ideogram_api_key:
|
||||
|
||||
@@ -10,6 +10,7 @@ class ProviderName(str, Enum):
|
||||
D_ID = "d_id"
|
||||
E2B = "e2b"
|
||||
EXA = "exa"
|
||||
EXAMPLE_PROVIDER = "example-provider"
|
||||
FAL = "fal"
|
||||
GITHUB = "github"
|
||||
GOOGLE = "google"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from .compass import CompassWebhookManager
|
||||
from .example import ExampleWebhookManager
|
||||
from .github import GithubWebhooksManager
|
||||
from .slant3d import Slant3DWebhooksManager
|
||||
|
||||
@@ -15,6 +16,7 @@ WEBHOOK_MANAGERS_BY_NAME: dict["ProviderName", type["BaseWebhooksManager"]] = {
|
||||
CompassWebhookManager,
|
||||
GithubWebhooksManager,
|
||||
Slant3DWebhooksManager,
|
||||
ExampleWebhookManager,
|
||||
]
|
||||
}
|
||||
# --8<-- [end:WEBHOOK_MANAGERS_BY_NAME]
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
import logging
|
||||
|
||||
import requests
|
||||
from fastapi import Request
|
||||
from strenum import StrEnum
|
||||
|
||||
from backend.data import integrations
|
||||
from backend.data.model import APIKeyCredentials, Credentials
|
||||
from backend.integrations.providers import ProviderName
|
||||
|
||||
from ._manual_base import ManualWebhookManagerBase
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ExampleWebhookEventType(StrEnum):
|
||||
EXAMPLE_EVENT = "example_event"
|
||||
ANOTHER_EXAMPLE_EVENT = "another_example_event"
|
||||
|
||||
|
||||
# ExampleWebhookManager is a class that manages webhooks for a hypothetical provider.
|
||||
# It extends ManualWebhookManagerBase, which provides base functionality for manual webhook management.
|
||||
class ExampleWebhookManager(ManualWebhookManagerBase):
|
||||
# Define the provider name for this webhook manager.
|
||||
PROVIDER_NAME = ProviderName.EXAMPLE_PROVIDER
|
||||
# Define the types of webhooks this manager can handle.
|
||||
WebhookEventType = ExampleWebhookEventType
|
||||
|
||||
BASE_URL = "https://api.example.com"
|
||||
|
||||
@classmethod
|
||||
async def validate_payload(
|
||||
cls, webhook: integrations.Webhook, request: Request
|
||||
) -> tuple[dict, str]:
|
||||
"""
|
||||
Validate the incoming webhook payload.
|
||||
|
||||
Args:
|
||||
webhook (integrations.Webhook): The webhook object.
|
||||
request (Request): The incoming request object.
|
||||
|
||||
Returns:
|
||||
tuple: A tuple containing the payload as a dictionary and the event type as a string.
|
||||
"""
|
||||
# Extract the JSON payload from the request.
|
||||
payload = await request.json()
|
||||
# Set the event type based on the webhook type in the payload.
|
||||
event_type = payload.get("webhook_type", ExampleWebhookEventType.EXAMPLE_EVENT)
|
||||
|
||||
# For the payload its better to return a pydantic model
|
||||
# rather than a weakly typed dict here
|
||||
return payload, event_type
|
||||
|
||||
async def _register_webhook(
|
||||
self,
|
||||
credentials: Credentials,
|
||||
webhook_type: str,
|
||||
resource: str,
|
||||
events: list[str],
|
||||
ingress_url: str,
|
||||
secret: str,
|
||||
) -> tuple[str, dict]:
|
||||
"""
|
||||
Register a new webhook with the provider.
|
||||
|
||||
Args:
|
||||
credentials (Credentials): The credentials required for authentication.
|
||||
webhook_type (str): The type of webhook to register.
|
||||
resource (str): The resource associated with the webhook.
|
||||
events (list[str]): The list of events to subscribe to.
|
||||
ingress_url (str): The URL where the webhook will send data.
|
||||
secret (str): A secret for securing the webhook.
|
||||
|
||||
Returns:
|
||||
tuple: A tuple containing the provider's webhook ID, if any, and the webhook configuration as a dictionary.
|
||||
"""
|
||||
# Ensure the credentials are of the correct type.
|
||||
if not isinstance(credentials, APIKeyCredentials):
|
||||
raise ValueError("API key is required to register a webhook")
|
||||
|
||||
# Prepare the headers for the request, including the API key.
|
||||
headers = {
|
||||
"api-key": credentials.api_key.get_secret_value(),
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
# Prepare the payload for the request. Note that the events list is not used.
|
||||
# This is just a fake example
|
||||
payload = {"endPoint": ingress_url}
|
||||
|
||||
# Send a POST request to register the webhook.
|
||||
response = requests.post(
|
||||
f"{self.BASE_URL}/example/webhookSubscribe", headers=headers, json=payload
|
||||
)
|
||||
|
||||
# Check if the response indicates a failure.
|
||||
if not response.ok:
|
||||
error = response.json().get("error", "Unknown error")
|
||||
raise RuntimeError(f"Failed to register webhook: {error}")
|
||||
|
||||
# Prepare the webhook configuration to return.
|
||||
webhook_config = {
|
||||
"endpoint": ingress_url,
|
||||
"provider": self.PROVIDER_NAME,
|
||||
"events": ["example_event"],
|
||||
"type": webhook_type,
|
||||
}
|
||||
|
||||
return "", webhook_config
|
||||
|
||||
async def _deregister_webhook(
|
||||
self, webhook: integrations.Webhook, credentials: Credentials
|
||||
) -> None:
|
||||
"""
|
||||
Deregister a webhook with the provider.
|
||||
|
||||
Args:
|
||||
webhook (integrations.Webhook): The webhook object to deregister.
|
||||
credentials (Credentials): The credentials associated with the webhook.
|
||||
|
||||
Raises:
|
||||
ValueError: If the webhook doesn't belong to the credentials or if deregistration fails.
|
||||
"""
|
||||
if webhook.credentials_id != credentials.id:
|
||||
raise ValueError(
|
||||
f"Webhook #{webhook.id} does not belong to credentials {credentials.id}"
|
||||
)
|
||||
|
||||
if not isinstance(credentials, APIKeyCredentials):
|
||||
raise ValueError("API key is required to deregister a webhook")
|
||||
|
||||
headers = {
|
||||
"api-key": credentials.api_key.get_secret_value(),
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
# Construct the delete URL based on the webhook information
|
||||
delete_url = f"{self.BASE_URL}/example/webhooks/{webhook.provider_webhook_id}"
|
||||
|
||||
response = requests.delete(delete_url, headers=headers)
|
||||
|
||||
if response.status_code not in [204, 404]:
|
||||
# 204 means successful deletion, 404 means the webhook was already deleted
|
||||
error = response.json().get("error", "Unknown error")
|
||||
raise ValueError(f"Failed to delete webhook: {error}")
|
||||
|
||||
# If we reach here, the webhook was successfully deleted or didn't exist
|
||||
@@ -404,6 +404,7 @@ class Secrets(UpdateTrackingModel["Secrets"], BaseSettings):
|
||||
smartlead_api_key: str = Field(default="", description="SmartLead API Key")
|
||||
zerobounce_api_key: str = Field(default="", description="ZeroBounce API Key")
|
||||
|
||||
example_api_key: str = Field(default="", description="Example API Key")
|
||||
# Add more secret fields as needed
|
||||
|
||||
model_config = SettingsConfigDict(
|
||||
|
||||
@@ -90,6 +90,7 @@ export const providerIcons: Record<
|
||||
smartlead: fallbackIcon,
|
||||
todoist: fallbackIcon,
|
||||
zerobounce: fallbackIcon,
|
||||
example: fallbackIcon,
|
||||
};
|
||||
// --8<-- [end:ProviderIconsEmbed]
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ const providerDisplayNames: Record<CredentialsProviderName, string> = {
|
||||
todoist: "Todoist",
|
||||
unreal_speech: "Unreal Speech",
|
||||
zerobounce: "ZeroBounce",
|
||||
example: "Example",
|
||||
} as const;
|
||||
// --8<-- [end:CredentialsProviderNames]
|
||||
|
||||
|
||||
@@ -148,6 +148,7 @@ export const PROVIDER_NAMES = {
|
||||
UNREAL_SPEECH: "unreal_speech",
|
||||
TODOIST: "todoist",
|
||||
ZEROBOUNCE: "zerobounce",
|
||||
EXAMPLE: "example",
|
||||
} as const;
|
||||
// --8<-- [end:BlockIOCredentialsSubSchema]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user