Skip to content

Commit

Permalink
Merge branch 'dev' into ntindle/open-1865-add-docs-on-playwright-test…
Browse files Browse the repository at this point in the history
…ing-and-what-we-care-about
  • Loading branch information
Swiftyos authored Dec 4, 2024
2 parents 64bbcab + d4edb93 commit b30a739
Show file tree
Hide file tree
Showing 13 changed files with 961 additions and 2 deletions.
4 changes: 2 additions & 2 deletions autogpt_platform/backend/backend/blocks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
if f.is_file() and f.name != "__init__.py"
]
for module in modules:
if not re.match("^[a-z_.]+$", module):
if not re.match("^[a-z0-9_.]+$", module):
raise ValueError(
f"Block module {module} error: module name must be lowercase, "
"separated by underscores, and contain only alphabet characters"
"and contain only alphanumeric characters and underscores."
)

importlib.import_module(f".{module}", package=__name__)
Expand Down
71 changes: 71 additions & 0 deletions autogpt_platform/backend/backend/blocks/slant3d/_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from enum import Enum
from typing import Literal

from pydantic import BaseModel, SecretStr

from backend.data.model import APIKeyCredentials, CredentialsField, CredentialsMetaInput

Slant3DCredentialsInput = CredentialsMetaInput[Literal["slant3d"], Literal["api_key"]]


def Slant3DCredentialsField() -> Slant3DCredentialsInput:
return CredentialsField(
provider="slant3d",
supported_credential_types={"api_key"},
description="Slant3D API key for authentication",
)


TEST_CREDENTIALS = APIKeyCredentials(
id="01234567-89ab-cdef-0123-456789abcdef",
provider="slant3d",
api_key=SecretStr("mock-slant3d-api-key"),
title="Mock Slant3D API key",
expires_at=None,
)

TEST_CREDENTIALS_INPUT = {
"provider": TEST_CREDENTIALS.provider,
"id": TEST_CREDENTIALS.id,
"type": TEST_CREDENTIALS.type,
"title": TEST_CREDENTIALS.title,
}


class CustomerDetails(BaseModel):
name: str
email: str
phone: str
address: str
city: str
state: str
zip: str
country_iso: str = "US"
is_residential: bool = True


class Color(Enum):
WHITE = "white"
BLACK = "black"


class Profile(Enum):
PLA = "PLA"
PETG = "PETG"


class OrderItem(BaseModel):
# filename: str
file_url: str
quantity: str # String as per API spec
color: Color = Color.WHITE
profile: Profile = Profile.PLA
# image_url: str = ""
# sku: str = ""


class Filament(BaseModel):
filament: str
hexColor: str
colorTag: str
profile: str
94 changes: 94 additions & 0 deletions autogpt_platform/backend/backend/blocks/slant3d/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from typing import Any, Dict

from backend.data.block import Block
from backend.util.request import requests

from ._api import Color, CustomerDetails, OrderItem, Profile


class Slant3DBlockBase(Block):
"""Base block class for Slant3D API interactions"""

BASE_URL = "https://www.slant3dapi.com/api"

def _get_headers(self, api_key: str) -> Dict[str, str]:
return {"api-key": api_key, "Content-Type": "application/json"}

def _make_request(self, method: str, endpoint: str, api_key: str, **kwargs) -> Dict:
url = f"{self.BASE_URL}/{endpoint}"
response = requests.request(
method=method, url=url, headers=self._get_headers(api_key), **kwargs
)

if not response.ok:
error_msg = response.json().get("error", "Unknown error")
raise RuntimeError(f"API request failed: {error_msg}")

return response.json()

def _check_valid_color(self, profile: Profile, color: Color, api_key: str) -> str:
response = self._make_request(
"GET",
"filament",
api_key,
params={"profile": profile.value, "color": color.value},
)
if profile == Profile.PLA:
color_tag = color.value
else:
color_tag = f"{profile.value.lower()}{color.value.capitalize()}"
valid_tags = [filament["colorTag"] for filament in response["filaments"]]

if color_tag not in valid_tags:
raise ValueError(
f"""Invalid color profile combination {color_tag}.
Valid colors for {profile.value} are:
{','.join([filament['colorTag'].replace(profile.value.lower(), '') for filament in response['filaments'] if filament['profile'] == profile.value])}
"""
)
return color_tag

def _convert_to_color(self, profile: Profile, color: Color, api_key: str) -> str:
return self._check_valid_color(profile, color, api_key)

def _format_order_data(
self,
customer: CustomerDetails,
order_number: str,
items: list[OrderItem],
api_key: str,
) -> list[dict[str, Any]]:
"""Helper function to format order data for API requests"""
orders = []
for item in items:
order_data = {
"email": customer.email,
"phone": customer.phone,
"name": customer.name,
"orderNumber": order_number,
"filename": item.file_url,
"fileURL": item.file_url,
"bill_to_street_1": customer.address,
"bill_to_city": customer.city,
"bill_to_state": customer.state,
"bill_to_zip": customer.zip,
"bill_to_country_as_iso": customer.country_iso,
"bill_to_is_US_residential": str(customer.is_residential).lower(),
"ship_to_name": customer.name,
"ship_to_street_1": customer.address,
"ship_to_city": customer.city,
"ship_to_state": customer.state,
"ship_to_zip": customer.zip,
"ship_to_country_as_iso": customer.country_iso,
"ship_to_is_US_residential": str(customer.is_residential).lower(),
"order_item_name": item.file_url,
"order_quantity": item.quantity,
"order_image_url": "",
"order_sku": "NOT_USED",
"order_item_color": self._convert_to_color(
item.profile, item.color, api_key
),
"profile": item.profile.value,
}
orders.append(order_data)
return orders
85 changes: 85 additions & 0 deletions autogpt_platform/backend/backend/blocks/slant3d/filament.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
from typing import List

from backend.data.block import BlockOutput, BlockSchema
from backend.data.model import APIKeyCredentials, SchemaField

from ._api import (
TEST_CREDENTIALS,
TEST_CREDENTIALS_INPUT,
Filament,
Slant3DCredentialsField,
Slant3DCredentialsInput,
)
from .base import Slant3DBlockBase


class Slant3DFilamentBlock(Slant3DBlockBase):
"""Block for retrieving available filaments"""

class Input(BlockSchema):
credentials: Slant3DCredentialsInput = Slant3DCredentialsField()

class Output(BlockSchema):
filaments: List[Filament] = SchemaField(
description="List of available filaments"
)
error: str = SchemaField(description="Error message if request failed")

def __init__(self):
super().__init__(
id="7cc416f4-f305-4606-9b3b-452b8a81031c",
description="Get list of available filaments",
input_schema=self.Input,
output_schema=self.Output,
test_input={"credentials": TEST_CREDENTIALS_INPUT},
test_credentials=TEST_CREDENTIALS,
test_output=[
(
"filaments",
[
{
"filament": "PLA BLACK",
"hexColor": "000000",
"colorTag": "black",
"profile": "PLA",
},
{
"filament": "PLA WHITE",
"hexColor": "ffffff",
"colorTag": "white",
"profile": "PLA",
},
],
)
],
test_mock={
"_make_request": lambda *args, **kwargs: {
"filaments": [
{
"filament": "PLA BLACK",
"hexColor": "000000",
"colorTag": "black",
"profile": "PLA",
},
{
"filament": "PLA WHITE",
"hexColor": "ffffff",
"colorTag": "white",
"profile": "PLA",
},
]
}
},
)

def run(
self, input_data: Input, *, credentials: APIKeyCredentials, **kwargs
) -> BlockOutput:
try:
result = self._make_request(
"GET", "filament", credentials.api_key.get_secret_value()
)
yield "filaments", result["filaments"]
except Exception as e:
yield "error", str(e)
raise
Loading

0 comments on commit b30a739

Please sign in to comment.