Skip to content
Merged
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
8 changes: 8 additions & 0 deletions src/gradient_labs/_secret_revoke.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from ._http_client import HttpClient


def revoke_secret(*, client: HttpClient, name: str) -> None:
"""revoke_secret permanently deletes a secret. This action cannot be undone.

Note: requires a `Management` API key."""
_ = client.delete(path=f"secrets/{name}", body={})
44 changes: 44 additions & 0 deletions src/gradient_labs/_secret_write.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from typing import Optional
from datetime import datetime
from dataclasses import dataclass
from dataclasses_json import dataclass_json

from ._http_client import HttpClient
from .secret import Secret, RefreshMechanismHTTP


@dataclass_json
@dataclass(frozen=True)
class WriteSecretParams:
"""WriteSecretParams contains the parameters for writing (creating or updating) a secret."""

# name is the unique identifier for the secret.
name: str

# value is the secret value to store.
value: str

# expiry is the optional expiration time for the secret.
expiry: Optional[datetime] = None

# refresh_mechanism_http is the optional configuration for automatically refreshing
# the secret value using an HTTP request (e.g., OAuth token refresh).
refresh_mechanism_http: Optional[RefreshMechanismHTTP] = None


def write_secret(*, client: HttpClient, params: WriteSecretParams) -> Secret:
"""write_secret creates or updates a secret. If a secret with the given name already exists,
it will be updated with the new value and configuration.

Note: requires a `Management` API key."""
# Build body manually to handle datetime serialization and exclude name from body
body = {
"value": params.value,
}
if params.expiry is not None:
body["expiry"] = HttpClient.localize(params.expiry)
if params.refresh_mechanism_http is not None:
body["refresh_mechanism_http"] = params.refresh_mechanism_http.to_dict()

rsp = client.put(path=f"secrets/{params.name}", body=body)
return Secret.from_dict(rsp)
25 changes: 25 additions & 0 deletions src/gradient_labs/_secrets_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from typing import List
from dataclasses import dataclass
from dataclasses_json import dataclass_json

from .secret import Secret
from ._http_client import HttpClient


@dataclass_json
@dataclass(frozen=True)
class SecretsList:
"""SecretsList contains the list of all secrets."""

secrets: List[Secret]


def list_secrets(*, client: HttpClient) -> SecretsList:
"""list_secrets returns all of your secrets.

Note: requires a `Management` API key."""
rsp = client.get(
path="secrets",
body={},
)
return SecretsList.from_dict(rsp)
32 changes: 32 additions & 0 deletions src/gradient_labs/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,14 @@
from ._note_update import update_note, UpdateNoteParams
from ._note_set_status import set_note_status, SetNoteStatusParams

from ._secret_write import write_secret, WriteSecretParams
from ._secrets_list import list_secrets, SecretsList
from ._secret_revoke import revoke_secret

from ._http_client import HttpClient, API_BASE_URL
from .tool import *
from .note import Note
from .secret import Secret
from .webhook import Webhook, WebhookEvent


Expand Down Expand Up @@ -551,3 +556,30 @@ def set_note_status(self, *, note_id: str, params: SetNoteStatusParams) -> None:
note_id=note_id,
params=params,
)

# Secret Operations

def write_secret(self, *, params: WriteSecretParams) -> Secret:
"""write_secret creates or updates a secret. If a secret with the given name already exists,
it will be updated with the new value and configuration.

Note: requires a `Management` API key."""
return write_secret(
client=self.http_client,
params=params,
)

def list_secrets(self) -> SecretsList:
"""list_secrets returns all of your secrets.

Note: requires a `Management` API key."""
return list_secrets(client=self.http_client)

def revoke_secret(self, *, name: str) -> None:
"""revoke_secret permanently deletes a secret. This action cannot be undone.

Note: requires a `Management` API key."""
revoke_secret(
client=self.http_client,
name=name,
)
43 changes: 43 additions & 0 deletions src/gradient_labs/secret.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from typing import Optional
from dataclasses import dataclass
from dataclasses_json import dataclass_json
from datetime import datetime

from .tool import HTTPDefinition


@dataclass_json
@dataclass(frozen=True)
class RefreshMechanismHTTP:
"""RefreshMechanismHTTP defines how to automatically refresh a secret's value
using an HTTP request. This is commonly used for OAuth access tokens that
need periodic renewal."""

# request_definition specifies the HTTP request to make to refresh the secret.
request_definition: HTTPDefinition

# response_param_name is the JSON field name in the response that contains
# the new secret value.
response_param_name: str


@dataclass_json
@dataclass
class Secret:
"""Secret represents a stored secret with optional expiration and refresh configuration."""

# name is the unique identifier for the secret.
name: str

# created is when the secret was first created.
created: datetime

# updated is when the secret was last updated.
updated: datetime

# expiry is the optional expiration time for the secret.
expiry: Optional[datetime] = None

# refresh_mechanism_http is the optional configuration for automatically refreshing
# the secret value using an HTTP request (e.g., OAuth token refresh).
refresh_mechanism_http: Optional[RefreshMechanismHTTP] = None