|
1 | | -# Copilot Instructions for Codesphere Python SDK |
| 1 | +# Codesphere SDK - AI Instructions |
2 | 2 |
|
3 | | -## Project Context |
4 | | - |
5 | | -This repository contains the **Codesphere Python SDK**, an official asynchronous Python client for the [Codesphere Public API](https://codesphere.com/api/swagger-ui/). It provides a high-level, type-safe interface for managing Codesphere resources including Teams, Workspaces, Domains, Environment Variables, and Metadata. |
6 | | - |
7 | | -The SDK follows a resource-based architecture where API operations are defined declaratively and executed through a centralized handler system. |
| 3 | +## Architecture & Core |
| 4 | +- **Async-First:** All I/O operations MUST be `async/await`. |
| 5 | +- **Base Classes:** |
| 6 | + - Models: Use `core.base.CamelModel` (handles camelCase API conversion). |
| 7 | + - Resources: Inherit from `ResourceBase` + `_APIOperationExecutor`. |
| 8 | +- **HTTP:** Use `APIHttpClient` (wraps httpx). Raise `httpx.HTTPStatusError` for API errors. |
8 | 9 |
|
9 | 10 | ## Project Structure |
10 | | - |
11 | | -``` |
12 | | -src/codesphere/ |
13 | | -├── __init__.py # Public API exports |
14 | | -├── client.py # Main SDK entry point (CodesphereSDK) |
15 | | -├── config.py # Settings via pydantic-settings |
16 | | -├── exceptions.py # Custom exception classes |
17 | | -├── http_client.py # Async HTTP client wrapper (APIHttpClient) |
18 | | -├── utils.py # Utility functions |
19 | | -├── core/ # Core SDK infrastructure |
20 | | -│ ├── base.py # Base classes (ResourceBase, CamelModel, ResourceList) |
21 | | -│ ├── handler.py # API operation executor (_APIOperationExecutor, APIRequestHandler) |
22 | | -│ └── operations.py # APIOperation definition and AsyncCallable type |
23 | | -└── resources/ # API resource implementations |
24 | | - ├── metadata/ # Datacenters, Plans, Images |
25 | | - ├── team/ # Teams and nested Domains |
26 | | - │ └── domain/ # Domain management (schemas, operations, manager) |
27 | | - └── workspace/ # Workspaces and nested resources |
28 | | - ├── envVars/ # Environment variables management |
29 | | - ├── landscape/ # (Placeholder) |
30 | | - └── pipeline/ # (Placeholder) |
31 | | -
|
32 | | -tests/ # Test files mirroring src structure |
33 | | -├── conftest.py # Shared unit test fixtures |
34 | | -├── core/ # Core infrastructure tests |
35 | | -├── resources/ # Resource unit tests |
36 | | -└── integration/ # Integration tests (real API) |
37 | | - ├── conftest.py # Integration fixtures & workspace setup |
38 | | - ├── test_domains.py |
39 | | - ├── test_env_vars.py |
40 | | - ├── test_metadata.py |
41 | | - ├── test_teams.py |
42 | | - └── test_workspaces.py |
43 | | -
|
44 | | -examples/ # Usage examples organized by resource type |
45 | | -``` |
46 | | - |
47 | | -## Coding Guidelines |
48 | | - |
49 | | -### General Principles |
50 | | - |
51 | | -- **Async-First**: All API operations MUST be async. Use `async/await` syntax consistently. |
52 | | -- **Type Hints**: Always provide complete type annotations for function parameters, return values, and class attributes. |
53 | | -- **Pydantic Models**: Use Pydantic `BaseModel` for all data structures. Prefer `CamelModel` for API payloads to handle camelCase conversion. |
54 | | - |
55 | | -### Naming Conventions |
56 | | - |
57 | | -- **Variables/Functions**: Use `snake_case` (e.g., `workspace_id`, `list_datacenters`) |
58 | | -- **Classes**: Use `PascalCase` (e.g., `WorkspaceCreate`, `APIHttpClient`) |
59 | | -- **Constants/Operations**: Use `UPPER_SNAKE_CASE` with leading underscore for internal operations (e.g., `_LIST_BY_TEAM_OP`) |
60 | | -- **Private Attributes**: Prefix with underscore (e.g., `_http_client`, `_token`) |
61 | | - |
62 | | -### Resource Pattern |
63 | | - |
64 | | -When adding new API resources, follow this structure: |
65 | | - |
66 | | -1. **schemas.py**: Define Pydantic models for Create, Base, Update, and the main resource class |
67 | | -2. **operations.py**: Define `APIOperation` instances for each endpoint |
68 | | -3. **resources.py**: Create the resource class extending `ResourceBase` with operation fields |
69 | | -4. **__init__.py**: Export public classes |
70 | | - |
71 | | -```python |
72 | | -# Example operation definition |
73 | | -_GET_OP = APIOperation( |
74 | | - method="GET", |
75 | | - endpoint_template="/resources/{resource_id}", |
76 | | - response_model=ResourceModel, |
77 | | -) |
78 | | - |
79 | | -# Example resource method |
80 | | -async def get(self, resource_id: int) -> ResourceModel: |
81 | | - return await self.get_op(resource_id=resource_id) |
82 | | -``` |
83 | | - |
84 | | -### Model Guidelines |
85 | | - |
86 | | -- Extend `CamelModel` for API request/response models (automatic camelCase aliasing) |
87 | | -- Extend `_APIOperationExecutor` for models that can perform operations on themselves |
88 | | -- Use `Field(default=..., exclude=True)` for operation callables |
89 | | -- Use `@cached_property` for lazy-loaded sub-managers (e.g., `workspace.env_vars`) |
90 | | - |
91 | | -```python |
92 | | -class Workspace(WorkspaceBase, _APIOperationExecutor): |
93 | | - delete_op: AsyncCallable[None] = Field(default=_DELETE_OP, exclude=True) |
94 | | - |
95 | | - async def delete(self) -> None: |
96 | | - await self.delete_op() |
97 | | -``` |
98 | | - |
99 | | -### Error Handling |
100 | | - |
101 | | -- Raise `httpx.HTTPStatusError` for HTTP errors (handled by `APIHttpClient`) |
102 | | -- Raise `RuntimeError` for SDK misuse (e.g., accessing resources without context manager) |
103 | | -- Use custom exceptions from `exceptions.py` for SDK-specific errors |
104 | | - |
105 | | -### Code Style |
106 | | - |
107 | | -- Line length: 88 characters (Ruff/Black standard) |
108 | | -- Indentation: 4 spaces |
109 | | -- Quotes: Double quotes for strings |
110 | | -- Imports: Group stdlib, third-party, and local imports |
111 | | - |
112 | | ---- |
113 | | - |
114 | | -## Testing Guidelines |
115 | | - |
116 | | -When adding features or making changes, appropriate tests are **required**. The SDK uses two types of tests: |
117 | | - |
118 | | -### Unit Tests |
119 | | - |
120 | | -Located in `tests/` (excluding `tests/integration/`). These mock HTTP responses and test SDK logic in isolation. |
121 | | - |
122 | | -**When to write unit tests:** |
123 | | -- New Pydantic models or schemas |
124 | | -- New API operations |
125 | | -- Core handler or utility logic changes |
126 | | - |
127 | | -**Unit test patterns:** |
128 | | - |
129 | | -```python |
130 | | -import pytest |
131 | | -from dataclasses import dataclass |
132 | | -from unittest.mock import AsyncMock, MagicMock |
133 | | - |
134 | | -# Use @dataclass for parameterized test cases |
135 | | -@dataclass |
136 | | -class WorkspaceTestCase: |
137 | | - name: str |
138 | | - workspace_id: int |
139 | | - expected_name: str |
140 | | - |
141 | | -@pytest.mark.asyncio |
142 | | -@pytest.mark.parametrize("case", [ |
143 | | - WorkspaceTestCase(name="basic", workspace_id=123, expected_name="test-ws"), |
144 | | -]) |
145 | | -async def test_workspace_get(case: WorkspaceTestCase): |
146 | | - """Should fetch a workspace by ID.""" |
147 | | - mock_response = MagicMock() |
148 | | - mock_response.json.return_value = {"id": case.workspace_id, "name": case.expected_name} |
149 | | - |
150 | | - # Test implementation... |
151 | | -``` |
152 | | - |
153 | | -### Integration Tests |
154 | | - |
155 | | -Located in `tests/integration/`. These run against the real Codesphere API. |
156 | | - |
157 | | -**When to write integration tests:** |
158 | | -- New API endpoints (CRUD operations) |
159 | | -- Changes to request/response serialization |
160 | | -- Schema field changes (detect API contract changes early) |
161 | | - |
162 | | -**Integration test patterns:** |
163 | | - |
164 | | -```python |
165 | | -import pytest |
166 | | -from codesphere import CodesphereSDK |
167 | | - |
168 | | -pytestmark = [pytest.mark.integration, pytest.mark.asyncio] |
169 | | - |
170 | | -class TestMyResourceIntegration: |
171 | | - """Integration tests for MyResource endpoints.""" |
172 | | - |
173 | | - async def test_list_resources(self, sdk_client: CodesphereSDK): |
174 | | - """Should retrieve a list of resources.""" |
175 | | - resources = await sdk_client.my_resource.list() |
176 | | - |
177 | | - assert isinstance(resources, list) |
178 | | - |
179 | | - async def test_create_and_delete( |
180 | | - self, |
181 | | - sdk_client: CodesphereSDK, |
182 | | - test_team_id: int, |
183 | | - ): |
184 | | - """Should create and delete a resource.""" |
185 | | - resource = await sdk_client.my_resource.create(name="test") |
186 | | - |
187 | | - try: |
188 | | - assert resource.name == "test" |
189 | | - finally: |
190 | | - # Always cleanup created resources |
191 | | - await resource.delete() |
192 | | -``` |
193 | | - |
194 | | -**Available integration test fixtures** (from `tests/integration/conftest.py`): |
195 | | - |
| 11 | +- `src/codesphere/core/`: Base classes & handlers. |
| 12 | +- `src/codesphere/resources/`: Resource implementations (follow `schemas.py`, `operations.py`, `resources.py` pattern). |
| 13 | +- `tests/integration/`: Real API tests (require `CS_TOKEN`). |
| 14 | +- `tests/unit/`: Mocked logic tests. |
| 15 | + |
| 16 | +## Resource Implementation Pattern |
| 17 | +When adding resources, strictly follow this pattern: |
| 18 | + |
| 19 | +1. **`schemas.py`**: Define Pydantic models (inherit `CamelModel`). |
| 20 | +2. **`operations.py`**: Define `APIOperation` constants. |
| 21 | + ```python |
| 22 | + _GET_OP = APIOperation(method="GET", endpoint_template="/res/{id}", response_model=ResModel) |
| 23 | + ``` |
| 24 | +3. **`resources.py`**: Implementation logic. |
| 25 | + ```python |
| 26 | + class MyResource(ResourceBase, _APIOperationExecutor): |
| 27 | + # Operation callable (exclude from model dump) |
| 28 | + delete_op: AsyncCallable[None] = Field(default=_DELETE_OP, exclude=True) |
| 29 | + |
| 30 | + async def delete(self) -> None: |
| 31 | + await self.delete_op() |
| 32 | + ``` |
| 33 | + |
| 34 | +## Testing Rules |
| 35 | +- **Unit Tests (`tests/`):** MUST mock all HTTP calls (`unittest.mock.AsyncMock`). |
| 36 | +- **Integration Tests (`tests/integration/`):** |
| 37 | + - Use `pytest.mark.integration`. |
| 38 | + - Use provided fixtures: `sdk_client` (fresh client), `test_team_id`, `test_workspace`. |
| 39 | + - **Cleanup:** Always delete created resources in a `try...finally` block. |
| 40 | + |
| 41 | +## Key Fixtures & Env Vars |
196 | 42 | | Fixture | Scope | Description | |
197 | | -|---------|-------|-------------| |
198 | | -| `sdk_client` | function | Fresh SDK client for each test | |
199 | | -| `session_sdk_client` | session | Shared SDK client for setup/teardown | |
200 | | -| `test_team_id` | session | Team ID for testing | |
201 | | -| `test_workspace` | session | Single pre-created workspace | |
202 | | -| `test_workspaces` | session | List of 2 test workspaces | |
203 | | -| `integration_token` | session | The API token (from `CS_TOKEN`) | |
204 | | - |
205 | | -**Environment variables:** |
206 | | - |
207 | | -| Variable | Required | Description | |
208 | | -|----------|----------|-------------| |
209 | | -| `CS_TOKEN` | Yes | Codesphere API token | |
210 | | -| `CS_TEST_TEAM_ID` | No | Specific team ID (defaults to first team) | |
211 | | -| `CS_TEST_DC_ID` | No | Datacenter ID (defaults to 1) | |
212 | | - |
213 | | -### Running Tests |
214 | | - |
215 | | -```bash |
216 | | -make test # Run unit tests only |
217 | | -make test-unit # Run unit tests only (explicit) |
218 | | -make test-integration # Run integration tests (requires CS_TOKEN) |
219 | | -``` |
220 | | - |
221 | | -### Test Requirements Checklist |
222 | | - |
223 | | -When submitting a PR, ensure: |
224 | | - |
225 | | -- [ ] **New endpoints** have integration tests covering all operations |
226 | | -- [ ] **New models** have unit tests for serialization/deserialization |
227 | | -- [ ] **Bug fixes** include a test that reproduces the issue |
228 | | -- [ ] **All tests pass** locally before pushing |
229 | | - |
230 | | ---- |
231 | | - |
232 | | -## Development Commands |
233 | | - |
234 | | -```bash |
235 | | -make install # Set up development environment |
236 | | -make lint # Run Ruff linter |
237 | | -make format # Format code with Ruff |
238 | | -make test # Run unit tests |
239 | | -make test-unit # Run unit tests (excludes integration) |
240 | | -make test-integration # Run integration tests |
241 | | -make commit # Guided commit with Commitizen |
242 | | -``` |
| 43 | +|---|---|---| |
| 44 | +| `sdk_client` | func | Fresh `CodesphereSDK` instance. | |
| 45 | +| `test_team_id` | session | ID from `CS_TEST_TEAM_ID` (or default). | |
| 46 | +| `integration_token` | session | Token from `CS_TOKEN`. | |
| 47 | + |
| 48 | +## Style & Naming |
| 49 | +- **Classes:** PascalCase (`WorkspaceCreate`). |
| 50 | +- **Vars:** snake_case. |
| 51 | +- **Internal Ops:** UPPER_SNAKE (`_LIST_OP`). |
| 52 | +- **Private:** Leading underscore (`_http_client`). |
| 53 | +- **Typing:** Strict type hints required. |
0 commit comments