mirror of
https://github.com/kennethreitz/neon-api-python.git
synced 2026-06-05 22:50:18 +00:00
Refactor NeonClientException class and add new NeonResource classes
This commit is contained in:
+103
-18
@@ -1,8 +1,9 @@
|
||||
import os
|
||||
from collections.abc import Sequence
|
||||
|
||||
# from collections.abc import Sequence
|
||||
|
||||
import requests
|
||||
from pydantic import BaseModel, ValidationError
|
||||
from pydantic import BaseModel
|
||||
|
||||
from .jsonschema import (
|
||||
ApiKeysListResponseItem,
|
||||
@@ -10,6 +11,7 @@ from .jsonschema import (
|
||||
ApiKeyCreateResponse,
|
||||
ApiKeyRevokeResponse,
|
||||
)
|
||||
from .jsonschema import ProjectListItem
|
||||
from .jsonschema import CurrentUserInfoResponse
|
||||
|
||||
|
||||
@@ -23,17 +25,12 @@ class NeonClientException(requests.exceptions.HTTPError):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
class APIKey:
|
||||
"""A Neon API key."""
|
||||
|
||||
class NeonResource:
|
||||
def __init__(
|
||||
self,
|
||||
client,
|
||||
obj,
|
||||
data_model: BaseModel,
|
||||
**kwargs,
|
||||
):
|
||||
"""A Neon API key.
|
||||
|
||||
@@ -52,7 +49,7 @@ class APIKey:
|
||||
"""The API key object."""
|
||||
|
||||
if not self.__cached_obj:
|
||||
self.__cached_obj = self._data_model(**self._data)
|
||||
self.__cached_obj = self._data_model.model_construct(**self._data)
|
||||
|
||||
return self.__cached_obj
|
||||
|
||||
@@ -64,24 +61,28 @@ class APIKey:
|
||||
except AttributeError:
|
||||
return getattr(self.obj, name)
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Get an item from the API key object or the API key data."""
|
||||
|
||||
return getattr(self.obj, key, None) or self.obj[key]
|
||||
|
||||
def __repr__(self):
|
||||
"""Return a string representation of the API key."""
|
||||
|
||||
return repr(self.obj)
|
||||
|
||||
|
||||
class APIKey(NeonResource):
|
||||
"""A Neon API key."""
|
||||
|
||||
@classmethod
|
||||
def create(cls, client, key_name: str):
|
||||
"""Create a new API key."""
|
||||
|
||||
obj = ApiKeyCreateRequest(key_name=key_name)
|
||||
r = client.request("POST", "api_keys", json=obj.model_dump())
|
||||
return cls(
|
||||
client=client,
|
||||
obj=r,
|
||||
data_model=ApiKeyCreateResponse,
|
||||
)
|
||||
|
||||
# ApiKeyCreateResponse(**r)
|
||||
return cls(client=client, obj=r, data_model=ApiKeyCreateResponse)
|
||||
|
||||
@classmethod
|
||||
def list(cls, client):
|
||||
@@ -93,15 +94,86 @@ class APIKey:
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def revoke(cls, client, api_key):
|
||||
def revoke_request(cls, client, api_key):
|
||||
"""Revoke an API key."""
|
||||
|
||||
r = client.request("DELETE", f"api_keys/{ api_key.obj.id }")
|
||||
|
||||
return cls(client=client, obj=r, data_model=ApiKeyRevokeResponse)
|
||||
|
||||
def revoke(self):
|
||||
"""Revoke the API key."""
|
||||
|
||||
return self.revoke_request(self._client, self)
|
||||
|
||||
|
||||
class User(NeonResource):
|
||||
"""A Neon user."""
|
||||
|
||||
@classmethod
|
||||
def get_current_user_info(cls, client):
|
||||
"""Get the current user."""
|
||||
|
||||
r = client.request("GET", "users/me")
|
||||
|
||||
return cls(
|
||||
client=client,
|
||||
obj=r,
|
||||
data_model=CurrentUserInfoResponse,
|
||||
)
|
||||
|
||||
|
||||
class Project(NeonResource):
|
||||
@classmethod
|
||||
def list(
|
||||
cls,
|
||||
client,
|
||||
*,
|
||||
cursor: int | None = None,
|
||||
limit: int | None = None,
|
||||
shared: bool = False,
|
||||
):
|
||||
"""Get a list of projects."""
|
||||
|
||||
r_path = "projects" if not shared else "projects/shared"
|
||||
r_params = {"cursor": cursor, "limit": limit}
|
||||
|
||||
r = client.request("GET", r_path, params=r_params)
|
||||
|
||||
return [
|
||||
cls(client=client, obj=x, data_model=ProjectListItem) for x in r["projects"]
|
||||
]
|
||||
|
||||
|
||||
class Branch(NeonResource):
|
||||
@classmethod
|
||||
def list(
|
||||
cls,
|
||||
client,
|
||||
project_id: str,
|
||||
*,
|
||||
cursor: int | None = None,
|
||||
limit: int | None = None,
|
||||
shared: bool = False,
|
||||
):
|
||||
"""Get a list of projects."""
|
||||
|
||||
r_path = "/".join(["projects", project_id, "branches"])
|
||||
r_params = {"cursor": cursor, "limit": limit}
|
||||
|
||||
r = client.request("GET", r_path, params=r_params)
|
||||
|
||||
return [
|
||||
cls(client=client, obj=x, data_model=ProjectListItem) for x in r["branches"]
|
||||
]
|
||||
|
||||
|
||||
class Operation(NeonResource):
|
||||
pass
|
||||
|
||||
|
||||
class NeonAPI:
|
||||
def __init__(self, api_key, *, base_url=None):
|
||||
def __init__(self, api_key: str, *, base_url=None):
|
||||
if not base_url:
|
||||
base_url = NEON_API_BASE_URL
|
||||
|
||||
@@ -113,8 +185,9 @@ class NeonAPI:
|
||||
self.base_url = base_url
|
||||
self.user_agent = f"neon-client/{__VERSION__}"
|
||||
|
||||
def request(self, method, path, **kwargs):
|
||||
def request(self, method: str, path: str, **kwargs):
|
||||
"""Send an HTTP request to the specified path using the specified method."""
|
||||
|
||||
# Set HTTP headers for outgoing requests.
|
||||
headers = kwargs.pop("headers", {})
|
||||
headers["Authorization"] = f"Bearer {self._api_key}"
|
||||
@@ -122,14 +195,18 @@ class NeonAPI:
|
||||
headers["Content-Type"] = "application/json"
|
||||
headers["User-Agent"] = self.user_agent
|
||||
|
||||
# Send the request.
|
||||
r = self._session.request(
|
||||
method, self.base_url + path, headers=headers, **kwargs
|
||||
)
|
||||
|
||||
# Check the response status code.
|
||||
try:
|
||||
r.raise_for_status()
|
||||
except requests.exceptions.HTTPError:
|
||||
raise NeonClientException(r.text)
|
||||
|
||||
# Deserialize the response.
|
||||
return r.json()
|
||||
|
||||
@classmethod
|
||||
@@ -137,3 +214,11 @@ class NeonAPI:
|
||||
"""Create a new Neon API client from the NEON_API_KEY environment variable."""
|
||||
|
||||
return cls(os.environ[NEON_API_KEY_ENVIRON])
|
||||
|
||||
def me(self):
|
||||
"""Get the current user."""
|
||||
return User.get_current_user_info(client=self)
|
||||
|
||||
def api_keys(self):
|
||||
"""Get a list of API keys."""
|
||||
return APIKey.list(client=self)
|
||||
|
||||
+13
-13
@@ -590,11 +590,11 @@ class Branch1(BaseModel):
|
||||
|
||||
class BranchCreateRequestEndpointOptions(BaseModel):
|
||||
type: EndpointType
|
||||
autoscaling_limit_min_cu: Optional[confloat(ge=0.25)] = Field(
|
||||
autoscaling_limit_min_cu: Optional[confloat()] = Field(
|
||||
None,
|
||||
description="The minimum number of Compute Units. The minimum value is `0.25`.\n See [Compute size and Autoscaling configuration](https://neon.tech/docs/manage/endpoints#compute-size-and-autoscaling-configuration)\n for more information.\n",
|
||||
)
|
||||
autoscaling_limit_max_cu: Optional[confloat(ge=0.25)] = Field(
|
||||
autoscaling_limit_max_cu: Optional[confloat()] = Field(
|
||||
None,
|
||||
description="The maximum number of Compute Units.\n See [Compute size and Autoscaling configuration](https://neon.tech/docs/manage/endpoints#compute-size-and-autoscaling-configuration)\n for more information.\n",
|
||||
)
|
||||
@@ -664,7 +664,7 @@ class CurrentUserInfoResponse(BaseModel):
|
||||
last_name: str
|
||||
projects_limit: int
|
||||
branches_limit: int
|
||||
max_autoscaling_limit: confloat(ge=0.25)
|
||||
max_autoscaling_limit: confloat()
|
||||
compute_seconds_limit: Optional[int] = None
|
||||
plan: str
|
||||
|
||||
@@ -677,11 +677,11 @@ class EndpointSettingsData(BaseModel):
|
||||
class DefaultEndpointSettings(BaseModel):
|
||||
pg_settings: Optional[dict[str, str]] = None
|
||||
pgbouncer_settings: Optional[dict[str, str]] = None
|
||||
autoscaling_limit_min_cu: Optional[confloat(ge=0.25)] = Field(
|
||||
autoscaling_limit_min_cu: Optional[confloat()] = Field(
|
||||
None,
|
||||
description="The minimum number of Compute Units. The minimum value is `0.25`.\nSee [Compute size and Autoscaling configuration](https://neon.tech/docs/manage/endpoints#compute-size-and-autoscaling-configuration)\nfor more information.\n",
|
||||
)
|
||||
autoscaling_limit_max_cu: Optional[confloat(ge=0.25)] = Field(
|
||||
autoscaling_limit_max_cu: Optional[confloat()] = Field(
|
||||
None,
|
||||
description="The maximum number of Compute Units. See [Compute size and Autoscaling configuration](https://neon.tech/docs/manage/endpoints#compute-size-and-autoscaling-configuration)\nfor more information.\n",
|
||||
)
|
||||
@@ -865,11 +865,11 @@ class Project1(BaseModel):
|
||||
settings: Optional[ProjectSettingsData] = None
|
||||
name: Optional[str] = Field(None, description="The project name")
|
||||
branch: Optional[Branch] = None
|
||||
autoscaling_limit_min_cu: Optional[confloat(ge=0.25)] = Field(
|
||||
autoscaling_limit_min_cu: Optional[confloat()] = Field(
|
||||
None,
|
||||
description="DEPRECATED, use default_endpoint_settings.autoscaling_limit_min_cu instead.\n\nThe minimum number of Compute Units. The minimum value is `0.25`.\nSee [Compute size and Autoscaling configuration](https://neon.tech/docs/manage/endpoints#compute-size-and-autoscaling-configuration)\nfor more information.\n",
|
||||
)
|
||||
autoscaling_limit_max_cu: Optional[confloat(ge=0.25)] = Field(
|
||||
autoscaling_limit_max_cu: Optional[confloat()] = Field(
|
||||
None,
|
||||
description="DEPRECATED, use default_endpoint_settings.autoscaling_limit_max_cu instead.\n\nThe maximum number of Compute Units. See [Compute size and Autoscaling configuration](https://neon.tech/docs/manage/endpoints#compute-size-and-autoscaling-configuration)\nfor more information.\n",
|
||||
)
|
||||
@@ -935,10 +935,10 @@ class Endpoint(BaseModel):
|
||||
...,
|
||||
description="The ID of the branch that the compute endpoint is associated with\n",
|
||||
)
|
||||
autoscaling_limit_min_cu: confloat(ge=0.25) = Field(
|
||||
autoscaling_limit_min_cu: confloat() = Field(
|
||||
..., description="The minimum number of Compute Units\n"
|
||||
)
|
||||
autoscaling_limit_max_cu: confloat(ge=0.25) = Field(
|
||||
autoscaling_limit_max_cu: confloat() = Field(
|
||||
..., description="The maximum number of Compute Units\n"
|
||||
)
|
||||
region_id: str = Field(..., description="The region identifier\n")
|
||||
@@ -995,11 +995,11 @@ class Endpoint1(BaseModel):
|
||||
)
|
||||
type: EndpointType
|
||||
settings: Optional[EndpointSettingsData] = None
|
||||
autoscaling_limit_min_cu: Optional[confloat(ge=0.25)] = Field(
|
||||
autoscaling_limit_min_cu: Optional[confloat()] = Field(
|
||||
None,
|
||||
description="The minimum number of Compute Units. The minimum value is `0.25`.\nSee [Compute size and Autoscaling configuration](https://neon.tech/docs/manage/endpoints#compute-size-and-autoscaling-configuration)\nfor more information.\n",
|
||||
)
|
||||
autoscaling_limit_max_cu: Optional[confloat(ge=0.25)] = Field(
|
||||
autoscaling_limit_max_cu: Optional[confloat()] = Field(
|
||||
None,
|
||||
description="The maximum number of Compute Units.\nSee [Compute size and Autoscaling configuration](https://neon.tech/docs/manage/endpoints#compute-size-and-autoscaling-configuration)\nfor more information.\n",
|
||||
)
|
||||
@@ -1032,11 +1032,11 @@ class Endpoint2(BaseModel):
|
||||
None,
|
||||
description="The destination branch ID. The destination branch must not have an exsiting read-write endpoint.\n",
|
||||
)
|
||||
autoscaling_limit_min_cu: Optional[confloat(ge=0.25)] = Field(
|
||||
autoscaling_limit_min_cu: Optional[confloat()] = Field(
|
||||
None,
|
||||
description="The minimum number of Compute Units. The minimum value is `0.25`.\nSee [Compute size and Autoscaling configuration](https://neon.tech/docs/manage/endpoints#compute-size-and-autoscaling-configuration)\nfor more information.\n",
|
||||
)
|
||||
autoscaling_limit_max_cu: Optional[confloat(ge=0.25)] = Field(
|
||||
autoscaling_limit_max_cu: Optional[confloat()] = Field(
|
||||
None,
|
||||
description="The maximum number of Compute Units.\nSee [Compute size and Autoscaling configuration](https://neon.tech/docs/manage/endpoints#compute-size-and-autoscaling-configuration)\nfor more information.\n",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user