Skip to content

Commit 681a8fc

Browse files
feat(api): add GET /extensions/{id_or_name}/metadata
1 parent 4fddd6b commit 681a8fc

6 files changed

Lines changed: 203 additions & 5 deletions

File tree

.stats.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
configured_endpoints: 120
2-
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel/kernel-ab4e7c50c41fefd891648fa84ba0258986f192ba1bde9f42cbdf487f64c4cc27.yml
3-
openapi_spec_hash: b4bcd6557f7045ecff30b01e02d28ac5
4-
config_hash: 03c7e57f268c750e2415831662e95969
1+
configured_endpoints: 121
2+
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel/kernel-bccc42bb10d1afe703f0374d11f33e775522069d69a13bb2345cc566a49ba4f3.yml
3+
openapi_spec_hash: 9f00975c0e741ed84011413674313be0
4+
config_hash: 3a50aee540dce69a53bb8942f5086f5e

api.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ Methods:
342342
Types:
343343

344344
```python
345-
from kernel.types import ExtensionListResponse, ExtensionUploadResponse
345+
from kernel.types import ExtensionListResponse, ExtensionGetResponse, ExtensionUploadResponse
346346
```
347347

348348
Methods:
@@ -351,6 +351,7 @@ Methods:
351351
- <code title="delete /extensions/{id_or_name}">client.extensions.<a href="./src/kernel/resources/extensions.py">delete</a>(id_or_name) -> None</code>
352352
- <code title="get /extensions/{id_or_name}">client.extensions.<a href="./src/kernel/resources/extensions.py">download</a>(id_or_name) -> BinaryAPIResponse</code>
353353
- <code title="get /extensions/from_chrome_store">client.extensions.<a href="./src/kernel/resources/extensions.py">download_from_chrome_store</a>(\*\*<a href="src/kernel/types/extension_download_from_chrome_store_params.py">params</a>) -> BinaryAPIResponse</code>
354+
- <code title="get /extensions/{id_or_name}/metadata">client.extensions.<a href="./src/kernel/resources/extensions.py">get</a>(id_or_name) -> <a href="./src/kernel/types/extension_get_response.py">ExtensionGetResponse</a></code>
354355
- <code title="post /extensions">client.extensions.<a href="./src/kernel/resources/extensions.py">upload</a>(\*\*<a href="src/kernel/types/extension_upload_params.py">params</a>) -> <a href="./src/kernel/types/extension_upload_response.py">ExtensionUploadResponse</a></code>
355356

356357
# BrowserPools

src/kernel/resources/extensions.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
)
3030
from ..pagination import SyncOffsetPagination, AsyncOffsetPagination
3131
from .._base_client import AsyncPaginator, make_request_options
32+
from ..types.extension_get_response import ExtensionGetResponse
3233
from ..types.extension_list_response import ExtensionListResponse
3334
from ..types.extension_upload_response import ExtensionUploadResponse
3435

@@ -224,6 +225,40 @@ def download_from_chrome_store(
224225
cast_to=BinaryAPIResponse,
225226
)
226227

228+
def get(
229+
self,
230+
id_or_name: str,
231+
*,
232+
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
233+
# The extra values given here take precedence over values defined on the client or passed to this method.
234+
extra_headers: Headers | None = None,
235+
extra_query: Query | None = None,
236+
extra_body: Body | None = None,
237+
timeout: float | httpx.Timeout | None | NotGiven = not_given,
238+
) -> ExtensionGetResponse:
239+
"""
240+
Get an extension's metadata (name, size, timestamps) by ID or name, without
241+
downloading the archive.
242+
243+
Args:
244+
extra_headers: Send extra headers
245+
246+
extra_query: Add additional query parameters to the request
247+
248+
extra_body: Add additional JSON properties to the request
249+
250+
timeout: Override the client-level default timeout for this request, in seconds
251+
"""
252+
if not id_or_name:
253+
raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
254+
return self._get(
255+
path_template("/extensions/{id_or_name}/metadata", id_or_name=id_or_name),
256+
options=make_request_options(
257+
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
258+
),
259+
cast_to=ExtensionGetResponse,
260+
)
261+
227262
def upload(
228263
self,
229264
*,
@@ -466,6 +501,40 @@ async def download_from_chrome_store(
466501
cast_to=AsyncBinaryAPIResponse,
467502
)
468503

504+
async def get(
505+
self,
506+
id_or_name: str,
507+
*,
508+
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
509+
# The extra values given here take precedence over values defined on the client or passed to this method.
510+
extra_headers: Headers | None = None,
511+
extra_query: Query | None = None,
512+
extra_body: Body | None = None,
513+
timeout: float | httpx.Timeout | None | NotGiven = not_given,
514+
) -> ExtensionGetResponse:
515+
"""
516+
Get an extension's metadata (name, size, timestamps) by ID or name, without
517+
downloading the archive.
518+
519+
Args:
520+
extra_headers: Send extra headers
521+
522+
extra_query: Add additional query parameters to the request
523+
524+
extra_body: Add additional JSON properties to the request
525+
526+
timeout: Override the client-level default timeout for this request, in seconds
527+
"""
528+
if not id_or_name:
529+
raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
530+
return await self._get(
531+
path_template("/extensions/{id_or_name}/metadata", id_or_name=id_or_name),
532+
options=make_request_options(
533+
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
534+
),
535+
cast_to=ExtensionGetResponse,
536+
)
537+
469538
async def upload(
470539
self,
471540
*,
@@ -537,6 +606,9 @@ def __init__(self, extensions: ExtensionsResource) -> None:
537606
extensions.download_from_chrome_store,
538607
BinaryAPIResponse,
539608
)
609+
self.get = to_raw_response_wrapper(
610+
extensions.get,
611+
)
540612
self.upload = to_raw_response_wrapper(
541613
extensions.upload,
542614
)
@@ -560,6 +632,9 @@ def __init__(self, extensions: AsyncExtensionsResource) -> None:
560632
extensions.download_from_chrome_store,
561633
AsyncBinaryAPIResponse,
562634
)
635+
self.get = async_to_raw_response_wrapper(
636+
extensions.get,
637+
)
563638
self.upload = async_to_raw_response_wrapper(
564639
extensions.upload,
565640
)
@@ -583,6 +658,9 @@ def __init__(self, extensions: ExtensionsResource) -> None:
583658
extensions.download_from_chrome_store,
584659
StreamedBinaryAPIResponse,
585660
)
661+
self.get = to_streamed_response_wrapper(
662+
extensions.get,
663+
)
586664
self.upload = to_streamed_response_wrapper(
587665
extensions.upload,
588666
)
@@ -606,6 +684,9 @@ def __init__(self, extensions: AsyncExtensionsResource) -> None:
606684
extensions.download_from_chrome_store,
607685
AsyncStreamedBinaryAPIResponse,
608686
)
687+
self.get = async_to_streamed_response_wrapper(
688+
extensions.get,
689+
)
609690
self.upload = async_to_streamed_response_wrapper(
610691
extensions.upload,
611692
)

src/kernel/types/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
from .credential_list_params import CredentialListParams as CredentialListParams
5454
from .deployment_list_params import DeploymentListParams as DeploymentListParams
5555
from .deployment_state_event import DeploymentStateEvent as DeploymentStateEvent
56+
from .extension_get_response import ExtensionGetResponse as ExtensionGetResponse
5657
from .invocation_list_params import InvocationListParams as InvocationListParams
5758
from .invocation_state_event import InvocationStateEvent as InvocationStateEvent
5859
from .api_key_retrieve_params import APIKeyRetrieveParams as APIKeyRetrieveParams
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2+
3+
from typing import Optional
4+
from datetime import datetime
5+
6+
from .._models import BaseModel
7+
8+
__all__ = ["ExtensionGetResponse"]
9+
10+
11+
class ExtensionGetResponse(BaseModel):
12+
"""A browser extension uploaded to Kernel."""
13+
14+
id: str
15+
"""Unique identifier for the extension"""
16+
17+
created_at: datetime
18+
"""Timestamp when the extension was created"""
19+
20+
size_bytes: int
21+
"""Size of the extension archive in bytes"""
22+
23+
last_used_at: Optional[datetime] = None
24+
"""Timestamp when the extension was last used"""
25+
26+
name: Optional[str] = None
27+
"""Optional, easier-to-reference name for the extension.
28+
29+
Must be unique within the project.
30+
"""

tests/api_resources/test_extensions.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from kernel import Kernel, AsyncKernel
1313
from tests.utils import assert_matches_type
1414
from kernel.types import (
15+
ExtensionGetResponse,
1516
ExtensionListResponse,
1617
ExtensionUploadResponse,
1718
)
@@ -214,6 +215,48 @@ def test_streaming_response_download_from_chrome_store(self, client: Kernel, res
214215

215216
assert cast(Any, extension.is_closed) is True
216217

218+
@pytest.mark.skip(reason="Mock server tests are disabled")
219+
@parametrize
220+
def test_method_get(self, client: Kernel) -> None:
221+
extension = client.extensions.get(
222+
"id_or_name",
223+
)
224+
assert_matches_type(ExtensionGetResponse, extension, path=["response"])
225+
226+
@pytest.mark.skip(reason="Mock server tests are disabled")
227+
@parametrize
228+
def test_raw_response_get(self, client: Kernel) -> None:
229+
response = client.extensions.with_raw_response.get(
230+
"id_or_name",
231+
)
232+
233+
assert response.is_closed is True
234+
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
235+
extension = response.parse()
236+
assert_matches_type(ExtensionGetResponse, extension, path=["response"])
237+
238+
@pytest.mark.skip(reason="Mock server tests are disabled")
239+
@parametrize
240+
def test_streaming_response_get(self, client: Kernel) -> None:
241+
with client.extensions.with_streaming_response.get(
242+
"id_or_name",
243+
) as response:
244+
assert not response.is_closed
245+
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
246+
247+
extension = response.parse()
248+
assert_matches_type(ExtensionGetResponse, extension, path=["response"])
249+
250+
assert cast(Any, response.is_closed) is True
251+
252+
@pytest.mark.skip(reason="Mock server tests are disabled")
253+
@parametrize
254+
def test_path_params_get(self, client: Kernel) -> None:
255+
with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
256+
client.extensions.with_raw_response.get(
257+
"",
258+
)
259+
217260
@pytest.mark.skip(reason="Mock server tests are disabled")
218261
@parametrize
219262
def test_method_upload(self, client: Kernel) -> None:
@@ -454,6 +497,48 @@ async def test_streaming_response_download_from_chrome_store(
454497

455498
assert cast(Any, extension.is_closed) is True
456499

500+
@pytest.mark.skip(reason="Mock server tests are disabled")
501+
@parametrize
502+
async def test_method_get(self, async_client: AsyncKernel) -> None:
503+
extension = await async_client.extensions.get(
504+
"id_or_name",
505+
)
506+
assert_matches_type(ExtensionGetResponse, extension, path=["response"])
507+
508+
@pytest.mark.skip(reason="Mock server tests are disabled")
509+
@parametrize
510+
async def test_raw_response_get(self, async_client: AsyncKernel) -> None:
511+
response = await async_client.extensions.with_raw_response.get(
512+
"id_or_name",
513+
)
514+
515+
assert response.is_closed is True
516+
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
517+
extension = await response.parse()
518+
assert_matches_type(ExtensionGetResponse, extension, path=["response"])
519+
520+
@pytest.mark.skip(reason="Mock server tests are disabled")
521+
@parametrize
522+
async def test_streaming_response_get(self, async_client: AsyncKernel) -> None:
523+
async with async_client.extensions.with_streaming_response.get(
524+
"id_or_name",
525+
) as response:
526+
assert not response.is_closed
527+
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
528+
529+
extension = await response.parse()
530+
assert_matches_type(ExtensionGetResponse, extension, path=["response"])
531+
532+
assert cast(Any, response.is_closed) is True
533+
534+
@pytest.mark.skip(reason="Mock server tests are disabled")
535+
@parametrize
536+
async def test_path_params_get(self, async_client: AsyncKernel) -> None:
537+
with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
538+
await async_client.extensions.with_raw_response.get(
539+
"",
540+
)
541+
457542
@pytest.mark.skip(reason="Mock server tests are disabled")
458543
@parametrize
459544
async def test_method_upload(self, async_client: AsyncKernel) -> None:

0 commit comments

Comments
 (0)