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
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CS_TOKEN="secret_token"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ wheels/
.venv/
venv/
env/
*.env

.pytest_cache
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ install: ## Sets up the development environment
@echo "2. Installing all dependencies (including 'dev')..."
uv pip install -e '.[dev]'
@echo "3. Installing git hooks with pre-commit..."
pre-commit install --hook-type commit-msg --hook-type pre-commit --hook-type pre-push
uv run pre-commit install --hook-type commit-msg --hook-type pre-commit --hook-type pre-push
@echo "\n\033[0;32mSetup complete! Please activate the virtual environment with 'source .venv/bin/activate'.\033[0m"

commit: ## Starts Commitizen for a guided commit message
Expand Down
8 changes: 2 additions & 6 deletions examples/metadata/get_datacenters.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import asyncio
import pprint
from codesphere import CodesphereSDK


async def main():
"""Fetches datacenters."""
async with CodesphereSDK() as sdk:
datacenters = await sdk.metadata.datacenters()

for datacenter in datacenters:
pprint.pprint(datacenter.name)
pprint.pprint(datacenter.city)
pprint.pprint(datacenter.countryCode)
pprint.pprint(datacenter.id)
print(datacenter.model_dump_json(indent=2))


if __name__ == "__main__":
asyncio.run(main())
7 changes: 2 additions & 5 deletions examples/metadata/get_workspace_base_image.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import asyncio
import pprint
from codesphere import CodesphereSDK


async def main():
"""Fetches base images."""
async with CodesphereSDK() as sdk:
images = await sdk.metadata.images()

for image in images:
pprint.pprint(image.id)
pprint.pprint(image.name)
pprint.pprint(image.supportedUntil)
print(image.model_dump_json(indent=2))


if __name__ == "__main__":
asyncio.run(main())
15 changes: 2 additions & 13 deletions examples/metadata/get_workspace_plans.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import asyncio
import pprint
from codesphere import CodesphereSDK


Expand All @@ -9,18 +8,8 @@ async def main():
plans = await sdk.metadata.plans()

for plan in plans:
pprint.pprint(plan.id)
pprint.pprint(plan.title)
pprint.pprint(plan.priceUsd)
pprint.pprint(plan.deprecated)
pprint.pprint(plan.characteristics.id)
pprint.pprint(plan.characteristics.CPU)
pprint.pprint(plan.characteristics.GPU)
pprint.pprint(plan.characteristics.RAM)
pprint.pprint(plan.characteristics.SSD)
pprint.pprint(plan.characteristics.TempStorage)
pprint.pprint(plan.characteristics.onDemand)
pprint.pprint(plan.maxReplicas)
print(plan.model_dump_json(indent=2))


if __name__ == "__main__":
asyncio.run(main())
12 changes: 4 additions & 8 deletions examples/teams/create_team.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import asyncio
import pprint

from codesphere import CodesphereSDK
from codesphere import CodesphereSDK, TeamCreate


async def main():
try:
async with CodesphereSDK() as sdk:
created_team = await sdk.teams.create(name="hello", dc=2)
newTeam = TeamCreate(name="test", dc=2)
created_team = await sdk.teams.create(data=newTeam)
print("\n--- Details for the created team ---")
pprint.pprint(created_team.model_dump())

print(f"Team ID: {created_team.id}, Name: {created_team.name}")

print(created_team.model_dump_json(indent=2))
except Exception as e:
print(f"An error occurred: {e}")

Expand Down
4 changes: 1 addition & 3 deletions examples/teams/delete_team.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import asyncio
import pprint

from codesphere import CodesphereSDK


Expand All @@ -9,7 +7,7 @@ async def main():
async with CodesphereSDK() as sdk:
team_to_delete = await sdk.teams.get(team_id="<id>")
print("\n--- Details of the team to be deleted ---")
pprint.pprint(team_to_delete.model_dump())
print(team_to_delete.model_dump_json(indent=2))
await team_to_delete.delete()
print(f"Team with ID {team_to_delete.id} was successfully deleted.")

Expand Down
4 changes: 2 additions & 2 deletions examples/teams/get_team.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import asyncio
import pprint

from codesphere import CodesphereSDK

Expand All @@ -8,9 +7,10 @@ async def main():
try:
async with CodesphereSDK() as sdk:
teams = await sdk.teams.list()
print(teams[0].model_dump_json(indent=2))
first_team = await sdk.teams.get(team_id=teams[0].id)
print("\n--- Details for the first team ---")
pprint.pprint(first_team.model_dump())
print(first_team.model_dump_json(indent=2))

except Exception as e:
print(f"An error occurred: {e}")
Expand Down
3 changes: 1 addition & 2 deletions examples/teams/list_teams.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ async def main():
try:
async with CodesphereSDK() as sdk:
teams = await sdk.teams.list()
print(f"Found {len(teams)} teams:")
for team in teams:
print(f"Team ID: {team.id}, Name: {team.name}")
print(team.model_dump_json(indent=2))

except Exception as e:
print(f"An error occurred: {e}")
Expand Down
5 changes: 4 additions & 1 deletion examples/workspaces/env-vars/delete_envvars.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ async def main():
print("Current Environment Variables:")
pprint.pprint(envs[0].name)

await workspace.delete_env_vars([envs[0].name]) # you can pass a list of strings to delete multiple env vars
await workspace.delete_env_vars(
[envs[0].name]
) # you can pass a list of strings to delete multiple env vars

print("Environment Variables after deletion:")
updated_envs = await workspace.get_env_vars()
pprint.pprint(updated_envs)


if __name__ == "__main__":
asyncio.run(main())
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ dependencies = [
"aiohttp-retry>=2.9.1",
"httpx>=0.28.1",
"pydantic>=2.11.7",
"pydantic-settings>=2.11.0",
"python-dateutil>=2.9.0.post0",
"python-dotenv>=1.2.1",
"typing-extensions>=4.14.0",
"urllib3>=2.4.0",
]
Expand Down
3 changes: 1 addition & 2 deletions src/codesphere/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# z.B. in src/codesphere/__init__.py oder einer eigenen client.py
from .client import APIHttpClient
from .resources.team.resources import TeamsResource
from .resources.team.resources import TeamsResource, TeamCreate
from .resources.workspace.resources import WorkspacesResource
from .resources.metadata.resources import MetadataResource
from .resources.workspace.models import (
Expand Down
16 changes: 6 additions & 10 deletions src/codesphere/client.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,25 @@
import os
import httpx
from pydantic import BaseModel
from typing import Optional, Any
from functools import partial

from .resources.exceptions.exceptions import AuthenticationError
from .config import settings


class APIHttpClient:
def __init__(self, base_url: str = "https://codesphere.com/api"):
auth_token = os.environ.get("CS_TOKEN")

if not auth_token:
raise AuthenticationError()

self._token = auth_token
self._base_url = base_url
self._token = settings.token.get_secret_value()
self._base_url = base_url or str(settings.base_url)
self.client: Optional[httpx.AsyncClient] = None

# Dynamically create get, post, put, patch, delete methods
for method in ["get", "post", "put", "patch", "delete"]:
setattr(self, method, partial(self.request, method.upper()))

async def __aenter__(self):
timeout_config = httpx.Timeout(10.0, read=30.0)
timeout_config = httpx.Timeout(
settings.client_timeout_connect, read=settings.client_timeout_read
)
self.client = httpx.AsyncClient(
base_url=self._base_url,
headers={"Authorization": f"Bearer {self._token}"},
Expand Down
23 changes: 23 additions & 0 deletions src/codesphere/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from pydantic import HttpUrl, SecretStr
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
"""
API Client Settings

TODO: add List of available env vars
"""

model_config = SettingsConfigDict(
env_file=".env", env_file_encoding="utf-8", env_prefix="CS_"
)

token: SecretStr
base_url: HttpUrl = "https://codesphere.com/api"

client_timeout_connect: float = 10.0
client_timeout_read: float = 30.0


settings = Settings()
5 changes: 4 additions & 1 deletion src/codesphere/resources/metadata/resources.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
from typing import List
from typing import Awaitable, Callable, List
from ..base import ResourceBase, APIOperation
from .models import Datacenters, WsPlans, Images


class MetadataResource(ResourceBase):
"""Contains all API operations for team ressources."""

datacenters: Callable[[], Awaitable[List[Datacenters]]]
datacenters = APIOperation(
method="GET",
endpoint_template="/metadata/datacenters",
input_model=None,
response_model=List[Datacenters],
)

plans: Callable[[], Awaitable[List[WsPlans]]]
plans = APIOperation(
method="GET",
endpoint_template="/metadata/workspace-plans",
input_model=None,
response_model=List[WsPlans],
)

images: Callable[[], Awaitable[List[Images]]]
images = APIOperation(
method="GET",
endpoint_template="/metadata/workspace-base-images",
Expand Down
6 changes: 0 additions & 6 deletions src/codesphere/resources/team/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,6 @@ class TeamBase(BaseModel):
role: Optional[int] = None


class TeamInList(TeamBase):
"""Represents a team as it appears in the list response."""

role: int


class Team(TeamBase):
"""
Represents a complete, single team object (detail view).
Expand Down
19 changes: 18 additions & 1 deletion src/codesphere/resources/team/resources.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,49 @@
from typing import List
from asyncio import Protocol
from typing import Awaitable, Callable, List
from ..base import ResourceBase, APIOperation
from .models import Team, TeamCreate


class GetTeamCallable(Protocol):
async def __call__(self, *, team_id: int) -> Team: ...


class CreateTeamCallable(Protocol):
async def __call__(self, *, data: TeamCreate) -> Team: ...


class DeleteTeamCallable(Protocol):
async def __call__(self, *, team_id: int) -> None: ...


class TeamsResource(ResourceBase):
"""Contains all API operations for team ressources."""

list: Callable[[], Awaitable[List[Team]]]
list = APIOperation(
method="GET",
endpoint_template="/teams",
input_model=None,
response_model=List[Team],
)

get: GetTeamCallable
get = APIOperation(
method="GET",
endpoint_template="/teams/{team_id}",
input_model=None,
response_model=Team,
)

create: CreateTeamCallable
create = APIOperation(
method="POST",
endpoint_template="/teams",
input_model=TeamCreate,
response_model=Team,
)

delete: DeleteTeamCallable
delete = APIOperation(
method="DELETE",
endpoint_template="/teams/{team_id}",
Expand Down
Loading
Loading