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 pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ dependencies = [
"pydantic-settings>=2.11.0",
"python-dateutil>=2.9.0.post0",
"python-dotenv>=1.2.1",
"pyyaml>=6.0.2",
"typing-extensions>=4.14.0",
"urllib3>=2.4.0",
]
Expand Down
122 changes: 120 additions & 2 deletions src/codesphere/core/base.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from typing import Generic, List, TypeVar
from typing import Any, Generic, List, Literal, TypeVar

import yaml
from pydantic import BaseModel, ConfigDict, RootModel
from pydantic.alias_generators import to_camel

from ..http_client import APIHttpClient
from .handler import _APIOperationExecutor

ModelT = TypeVar("ModelT")
ModelT = TypeVar("ModelT", bound=BaseModel)


class ResourceBase(_APIOperationExecutor):
Expand All @@ -20,6 +22,58 @@ class CamelModel(BaseModel):
serialize_by_alias=True,
)

def to_dict(
self, *, by_alias: bool = True, exclude_none: bool = False
) -> dict[str, Any]:
"""Export model as a Python dictionary.

Args:
by_alias: Use camelCase keys (API format) if True, snake_case if False.
exclude_none: Exclude fields with None values if True.

Returns:
Dictionary representation of the model.
"""
return self.model_dump(by_alias=by_alias, exclude_none=exclude_none)

def to_json(
self,
*,
by_alias: bool = True,
exclude_none: bool = False,
indent: int | None = None,
) -> str:
"""Export model as a JSON string.

Args:
by_alias: Use camelCase keys (API format) if True, snake_case if False.
exclude_none: Exclude fields with None values if True.
indent: Number of spaces for indentation. None for compact output.

Returns:
JSON string representation of the model.
"""
return self.model_dump_json(
by_alias=by_alias, exclude_none=exclude_none, indent=indent
)

def to_yaml(self, *, by_alias: bool = True, exclude_none: bool = False) -> str:
"""Export model as a YAML string.

Args:
by_alias: Use camelCase keys (API format) if True, snake_case if False.
exclude_none: Exclude fields with None values if True.

Returns:
YAML string representation of the model.
"""
data = self.model_dump(
by_alias=by_alias, exclude_none=exclude_none, mode="json"
)
return yaml.safe_dump(
data, default_flow_style=False, allow_unicode=True, sort_keys=False
)


class ResourceList(RootModel[List[ModelT]], Generic[ModelT]):
root: List[ModelT]
Expand All @@ -32,3 +86,67 @@ def __getitem__(self, item):

def __len__(self):
return len(self.root)

def to_list(
self,
*,
by_alias: bool = True,
exclude_none: bool = False,
mode: Literal["python", "json"] = "python",
) -> list[dict[str, Any]]:
"""Export all items as a list of dictionaries.

Args:
by_alias: Use camelCase keys (API format) if True, snake_case if False.
exclude_none: Exclude fields with None values if True.
mode: Serialization mode. "python" returns native Python objects,
"json" returns JSON-compatible types (e.g., datetime as ISO string).

Returns:
List of dictionary representations.
"""
return [
item.model_dump(by_alias=by_alias, exclude_none=exclude_none, mode=mode)
for item in self.root
]

def to_json(
self,
*,
by_alias: bool = True,
exclude_none: bool = False,
indent: int | None = None,
) -> str:
"""Export all items as a JSON array string.

Args:
by_alias: Use camelCase keys (API format) if True, snake_case if False.
exclude_none: Exclude fields with None values if True.
indent: Number of spaces for indentation. None for compact output.

Returns:
JSON array string representation.
"""
import json

return json.dumps(
self.to_list(by_alias=by_alias, exclude_none=exclude_none, mode="json"),
indent=indent,
)

def to_yaml(self, *, by_alias: bool = True, exclude_none: bool = False) -> str:
"""Export all items as a YAML string.

Args:
by_alias: Use camelCase keys (API format) if True, snake_case if False.
exclude_none: Exclude fields with None values if True.

Returns:
YAML string representation.
"""
return yaml.safe_dump(
self.to_list(by_alias=by_alias, exclude_none=exclude_none, mode="json"),
default_flow_style=False,
allow_unicode=True,
sort_keys=False,
)
10 changes: 3 additions & 7 deletions src/codesphere/resources/team/domain/manager.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
from typing import List, Union

from pydantic import Field

from ....core.base import ResourceList
from ....core.handler import _APIOperationExecutor
from ....core.operations import AsyncCallable
from ....http_client import APIHttpClient
from .schemas import CustomDomainConfig, DomainRouting, RoutingMap
from .resources import Domain
from .operations import _CREATE_OP, _GET_OP, _LIST_OP, _UPDATE_OP, _UPDATE_WS_OP
from .resources import Domain
from .schemas import CustomDomainConfig, DomainRouting, RoutingMap


class TeamDomainManager(_APIOperationExecutor):
"""
Verwaltet Domains im Kontext eines spezifischen Teams.
Zugriff typischerweise über 'team.domains'.
"""

def __init__(self, http_client: APIHttpClient, team_id: int):
self._http_client = http_client
self.team_id = team_id
Expand Down
6 changes: 2 additions & 4 deletions src/codesphere/resources/team/domain/schemas.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

from typing import Dict, List, Optional, TypeAlias

from pydantic import Field, RootModel

from ....core.base import CamelModel
Expand Down Expand Up @@ -31,10 +33,6 @@ class CustomDomainConfig(CamelModel):


class DomainRouting(RootModel):
"""
Helper class to build the routing configuration.
"""

root: RoutingMap = Field(default_factory=dict)

def add(self, path: str, workspace_ids: List[int]) -> DomainRouting:
Expand Down
Loading
Loading