Skip to content

Commit 72e88ed

Browse files
Centralize extension manager request and metadata handling
Co-authored-by: Shri Sukhani <[email protected]>
1 parent df7f517 commit 72e88ed

14 files changed

Lines changed: 394 additions & 28 deletions

CONTRIBUTING.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ This runs lint, format checks, compile checks, tests, and package build.
101101
- `tests/test_examples_naming_convention.py` (example sync/async prefix naming enforcement),
102102
- `tests/test_examples_syntax.py` (example script syntax guardrail),
103103
- `tests/test_extension_create_helper_usage.py` (extension create-input normalization helper usage enforcement),
104+
- `tests/test_extension_operation_metadata_usage.py` (extension manager operation-metadata usage enforcement),
105+
- `tests/test_extension_request_helper_usage.py` (extension manager request-helper usage enforcement),
106+
- `tests/test_extension_route_constants_usage.py` (extension manager route-constant usage enforcement),
104107
- `tests/test_extract_payload_helper_usage.py` (extract start-payload helper usage enforcement),
105108
- `tests/test_guardrail_ast_utils.py` (shared AST guard utility contract),
106109
- `tests/test_job_fetch_helper_boundary.py` (centralization boundary enforcement for retry/paginated-fetch helper primitives),

hyperbrowser/client/managers/async_manager/extension.py

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,23 @@
22

33
from ...file_utils import open_binary_file
44
from ..extension_create_utils import normalize_extension_create_input
5-
from ..extension_utils import parse_extension_list_response_data
6-
from ..response_utils import parse_response_model
5+
from ..extension_operation_metadata import EXTENSION_OPERATION_METADATA
6+
from ..extension_request_utils import (
7+
create_extension_resource_async,
8+
list_extension_resources_async,
9+
)
10+
from ..extension_route_constants import (
11+
EXTENSION_CREATE_ROUTE_PATH,
12+
EXTENSION_LIST_ROUTE_PATH,
13+
)
714
from hyperbrowser.models.extension import CreateExtensionParams, ExtensionResponse
815

916

1017
class ExtensionManager:
18+
_OPERATION_METADATA = EXTENSION_OPERATION_METADATA
19+
_CREATE_ROUTE_PATH = EXTENSION_CREATE_ROUTE_PATH
20+
_LIST_ROUTE_PATH = EXTENSION_LIST_ROUTE_PATH
21+
1122
def __init__(self, client):
1223
self._client = client
1324

@@ -18,19 +29,17 @@ async def create(self, params: CreateExtensionParams) -> ExtensionResponse:
1829
file_path,
1930
open_error_message=f"Failed to open extension file at path: {file_path}",
2031
) as extension_file:
21-
response = await self._client.transport.post(
22-
self._client._build_url("/extensions/add"),
23-
data=payload,
24-
files={"file": extension_file},
32+
return await create_extension_resource_async(
33+
client=self._client,
34+
route_path=self._CREATE_ROUTE_PATH,
35+
payload=payload,
36+
file_stream=extension_file,
37+
model=ExtensionResponse,
38+
operation_name=self._OPERATION_METADATA.create_operation_name,
2539
)
26-
return parse_response_model(
27-
response.data,
28-
model=ExtensionResponse,
29-
operation_name="create extension",
30-
)
3140

3241
async def list(self) -> List[ExtensionResponse]:
33-
response = await self._client.transport.get(
34-
self._client._build_url("/extensions/list"),
42+
return await list_extension_resources_async(
43+
client=self._client,
44+
route_path=self._LIST_ROUTE_PATH,
3545
)
36-
return parse_extension_list_response_data(response.data)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from dataclasses import dataclass
2+
3+
4+
@dataclass(frozen=True)
5+
class ExtensionOperationMetadata:
6+
create_operation_name: str
7+
8+
9+
EXTENSION_OPERATION_METADATA = ExtensionOperationMetadata(
10+
create_operation_name="create extension",
11+
)
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
from typing import Any, IO, List, Type, TypeVar
2+
3+
from .extension_utils import parse_extension_list_response_data
4+
from .response_utils import parse_response_model
5+
from hyperbrowser.models.extension import ExtensionResponse
6+
7+
T = TypeVar("T")
8+
9+
10+
def create_extension_resource(
11+
*,
12+
client: Any,
13+
route_path: str,
14+
payload: Any,
15+
file_stream: IO,
16+
model: Type[T],
17+
operation_name: str,
18+
) -> T:
19+
response = client.transport.post(
20+
client._build_url(route_path),
21+
data=payload,
22+
files={"file": file_stream},
23+
)
24+
return parse_response_model(
25+
response.data,
26+
model=model,
27+
operation_name=operation_name,
28+
)
29+
30+
31+
def list_extension_resources(
32+
*,
33+
client: Any,
34+
route_path: str,
35+
) -> List[ExtensionResponse]:
36+
response = client.transport.get(
37+
client._build_url(route_path),
38+
)
39+
return parse_extension_list_response_data(response.data)
40+
41+
42+
async def create_extension_resource_async(
43+
*,
44+
client: Any,
45+
route_path: str,
46+
payload: Any,
47+
file_stream: IO,
48+
model: Type[T],
49+
operation_name: str,
50+
) -> T:
51+
response = await client.transport.post(
52+
client._build_url(route_path),
53+
data=payload,
54+
files={"file": file_stream},
55+
)
56+
return parse_response_model(
57+
response.data,
58+
model=model,
59+
operation_name=operation_name,
60+
)
61+
62+
63+
async def list_extension_resources_async(
64+
*,
65+
client: Any,
66+
route_path: str,
67+
) -> List[ExtensionResponse]:
68+
response = await client.transport.get(
69+
client._build_url(route_path),
70+
)
71+
return parse_extension_list_response_data(response.data)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
EXTENSION_CREATE_ROUTE_PATH = "/extensions/add"
2+
EXTENSION_LIST_ROUTE_PATH = "/extensions/list"

hyperbrowser/client/managers/sync_manager/extension.py

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,23 @@
22

33
from ...file_utils import open_binary_file
44
from ..extension_create_utils import normalize_extension_create_input
5-
from ..extension_utils import parse_extension_list_response_data
6-
from ..response_utils import parse_response_model
5+
from ..extension_operation_metadata import EXTENSION_OPERATION_METADATA
6+
from ..extension_request_utils import (
7+
create_extension_resource,
8+
list_extension_resources,
9+
)
10+
from ..extension_route_constants import (
11+
EXTENSION_CREATE_ROUTE_PATH,
12+
EXTENSION_LIST_ROUTE_PATH,
13+
)
714
from hyperbrowser.models.extension import CreateExtensionParams, ExtensionResponse
815

916

1017
class ExtensionManager:
18+
_OPERATION_METADATA = EXTENSION_OPERATION_METADATA
19+
_CREATE_ROUTE_PATH = EXTENSION_CREATE_ROUTE_PATH
20+
_LIST_ROUTE_PATH = EXTENSION_LIST_ROUTE_PATH
21+
1122
def __init__(self, client):
1223
self._client = client
1324

@@ -18,19 +29,17 @@ def create(self, params: CreateExtensionParams) -> ExtensionResponse:
1829
file_path,
1930
open_error_message=f"Failed to open extension file at path: {file_path}",
2031
) as extension_file:
21-
response = self._client.transport.post(
22-
self._client._build_url("/extensions/add"),
23-
data=payload,
24-
files={"file": extension_file},
32+
return create_extension_resource(
33+
client=self._client,
34+
route_path=self._CREATE_ROUTE_PATH,
35+
payload=payload,
36+
file_stream=extension_file,
37+
model=ExtensionResponse,
38+
operation_name=self._OPERATION_METADATA.create_operation_name,
2539
)
26-
return parse_response_model(
27-
response.data,
28-
model=ExtensionResponse,
29-
operation_name="create extension",
30-
)
3140

3241
def list(self) -> List[ExtensionResponse]:
33-
response = self._client.transport.get(
34-
self._client._build_url("/extensions/list"),
42+
return list_extension_resources(
43+
client=self._client,
44+
route_path=self._LIST_ROUTE_PATH,
3545
)
36-
return parse_extension_list_response_data(response.data)

tests/test_architecture_marker_usage.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@
4747
"tests/test_extension_create_helper_usage.py",
4848
"tests/test_extract_payload_helper_usage.py",
4949
"tests/test_examples_naming_convention.py",
50+
"tests/test_extension_operation_metadata_usage.py",
51+
"tests/test_extension_request_helper_usage.py",
52+
"tests/test_extension_route_constants_usage.py",
5053
"tests/test_job_pagination_helper_usage.py",
5154
"tests/test_job_fetch_helper_boundary.py",
5255
"tests/test_job_fetch_helper_usage.py",

tests/test_core_type_helper_usage.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@
3737
"hyperbrowser/client/managers/computer_action_utils.py",
3838
"hyperbrowser/client/managers/computer_action_payload_utils.py",
3939
"hyperbrowser/client/managers/extension_create_utils.py",
40+
"hyperbrowser/client/managers/extension_operation_metadata.py",
41+
"hyperbrowser/client/managers/extension_request_utils.py",
42+
"hyperbrowser/client/managers/extension_route_constants.py",
4043
"hyperbrowser/client/managers/extract_payload_utils.py",
4144
"hyperbrowser/client/managers/job_fetch_utils.py",
4245
"hyperbrowser/client/managers/job_operation_metadata.py",
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from hyperbrowser.client.managers.extension_operation_metadata import (
2+
EXTENSION_OPERATION_METADATA,
3+
)
4+
5+
6+
def test_extension_operation_metadata_values():
7+
assert EXTENSION_OPERATION_METADATA.create_operation_name == "create extension"
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from pathlib import Path
2+
3+
import pytest
4+
5+
pytestmark = pytest.mark.architecture
6+
7+
8+
MODULES = (
9+
"hyperbrowser/client/managers/sync_manager/extension.py",
10+
"hyperbrowser/client/managers/async_manager/extension.py",
11+
)
12+
13+
14+
def test_extension_managers_use_shared_operation_metadata():
15+
for module_path in MODULES:
16+
module_text = Path(module_path).read_text(encoding="utf-8")
17+
assert "extension_operation_metadata import" in module_text
18+
assert "_OPERATION_METADATA = " in module_text
19+
assert "operation_name=self._OPERATION_METADATA." in module_text
20+
assert 'operation_name="' not in module_text

0 commit comments

Comments
 (0)