Skip to content
Closed
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
60 changes: 60 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"permissions": {
"deny": [
"Bash(git push --force*)",
"Bash(git reset --hard*)",
"Bash(git clean -f*)",
"Bash(gh pr merge*)",
"Bash(gh pr close*)",
"Bash(gh pr edit*)",
"Bash(gh pr review*)",
"Bash(gh issue*)",
"Bash(gh release*)",
"Bash(gh repo*)",
"Bash(gh gist*)",
"Bash(gh api --method POST*)",
"Bash(gh api --method PATCH*)",
"Bash(gh api --method DELETE*)",
"Bash(gh api --method PUT*)"
],
"allow": [
"Bash(git:*)",
"Bash(gh auth status*)",
"Bash(gh pr create*)",
"Bash(gh pr list*)",
"Bash(gh pr view*)",
"Bash(gh pr checks*)",
"Bash(gh run list*)",
"Bash(gh run view*)",
"Bash(gh run watch*)",
"Bash(gh api repos/opendatasoft/opendatasoft-automation-sdk*)",
"Bash(uv:*)",
"Bash(python3:*)",
"Bash(.venv/bin/python3:*)",
"Bash(docker:*)",
"Bash(sh:*)",
"Bash(./generators/python/generate.sh*)",
"Bash(chmod:*)",
"Bash(ls:*)",
"Bash(find:*)",
"Bash(grep:*)",
"Bash(cat:*)",
"Bash(echo:*)",
"Bash(mkdir:*)",
"Bash(rm:*)",
"Bash(mv:*)",
"Bash(cp:*)",
"Bash(touch:*)",
"Bash(sed:*)",
"Bash(awk:*)",
"Bash(sort:*)",
"Bash(wc:*)",
"Bash(diff:*)",
"Bash(curl:*)",
"Bash(pre-commit:*)",
"WebSearch",
"WebFetch"
]
},
"plansDirectory": ".claude/plans"
}
33 changes: 33 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: CI

on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]

jobs:
generate-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Generate SDK
run: ./generators/python/generate.sh

- name: Set up uv
uses: astral-sh/setup-uv@v5
with:
python-version: "3.11"

- name: Install dependencies
run: uv --directory tests/python sync --extra dev

- name: Lint
run: uv --directory tests/python run ruff check .

- name: Format check
run: uv --directory tests/python run ruff format --check .

- name: Run tests
run: uv --directory tests/python run pytest
42 changes: 42 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Release

on:
push:
tags:
- 'v*'

jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- uses: actions/checkout@v4

- name: Extract version from tag
id: version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT

- name: Generate SDK with release version
run: ./generators/python/generate.sh ${{ steps.version.outputs.VERSION }}

- name: Set up uv
uses: astral-sh/setup-uv@v5
with:
python-version: "3.11"

- name: Install dependencies
run: uv --directory tests/python sync --extra dev

- name: Run tests
run: uv --directory tests/python run pytest

- name: Build package
run: uv --directory python build

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: python/dist/*
generate_release_notes: true
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
# Generated SDK output (generated by CI / generate.sh, not committed)
/python/

# Temporary directory used by generate-tests.sh
.tmp-generated-tests/

# Virtual environments (root dev venv + tests/python venv)
.venv/
tests/python/.venv/
tests/python/htmlcov/
tests/python/.pytest_cache/

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
150 changes: 150 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Claude Code Guide — automation-sdk

## Project Overview

This repo produces `huwise_automation`, a Python SDK for the Automation API.
The SDK is **generated** (not committed) — the source of truth is `automation.yaml` (OpenAPI 3.1.0).

```
automation.yaml # OpenAPI 3.1.0 spec — edit this, not the generated code
generators/python/
config.json # Generator config (packageName, library=httpx)
generate.sh # Regenerates python/ (requires Docker)
generate-tests.sh # Adds stubs for new operations/models to tests/
templates/
pyproject.mustache # Overrides generated pyproject.toml
configuration.mustache # Adds Configuration.from_api_key() classmethod
api_client.mustache # Adds ApiClient(host=, api_key=) shorthand
tests/ # Hand-written tests — committed, lives here permanently
python/ # Generated SDK — NOT in source control (gitignored)
pyproject.toml # Root dev environment (path dep to python/, pytest config)
.github/workflows/
ci.yml # Generate + lint + test on push / PR
release.yml # Generate + build + publish on git tag push
```

## Versioning

- Spec `info.version` (e.g. `1.0`) → SDK major version = `spec_major * 10` = `10`
- Full version comes from the git tag at release time: tag `v10.0.1` → package `10.0.1`
- Local / CI builds without a tag use `10.0.0.dev0`

## Local Development

```sh
# 1. Generate the SDK (requires Docker)
./generators/python/generate.sh

# 2. Install all dependencies (SDK + dev tools)
uv --directory tests/python sync --extra dev

# 3. Run the full test suite
uv --directory tests/python run pytest

# 4. Lint / format hand-written tests
uv --directory tests/python run ruff check .
uv --directory tests/python run ruff format .
```

## Test Workflow

All tests live in `tests/python/` — committed, pytest style, no unittest.
`python/` contains no test files (generation suppressed via `--global-property apiTests=false,modelTests=false`).

### When you update `automation.yaml`

After editing the spec, run both generation commands:

```sh
# Regenerate the SDK
./generators/python/generate.sh

# Add test stubs for any NEW operations or models
./generators/python/generate-tests.sh

uv --directory tests/python sync --extra dev
uv --directory tests/python run pytest
```

`generate-tests.sh` only creates files that don't yet exist in `tests/` — it **never
overwrites** a file a developer has already written. Newly created stubs appear as
empty `unittest.TestCase` skeletons; fill them in before merging.

## Release Workflow

```sh
git tag v10.0.1
git push origin v10.0.1
# CI generates with version 10.0.1, runs tests, builds .whl + .tar.gz,
# and creates a GitHub Release with those artifacts.
```

The major version in the tag must match the spec-derived major (spec `1.x` → major `10`).

## Authentication

Pass `host` and `api_key` directly to `ApiClient`:

```python
from huwise_automation import ApiClient
from huwise_automation.api import DatasetsApi

with ApiClient(host="https://my-domain.huwise.com", api_key="my-secret-key") as client:
api = DatasetsApi(client)
datasets = api.list_datasets()
```

For advanced configuration (retries, proxies, etc.), use `Configuration.from_api_key()` explicitly:

```python
from huwise_automation import ApiClient, Configuration

conf = Configuration.from_api_key(host="https://my-domain.huwise.com", api_key="my-secret-key")
conf.retries = 3
with ApiClient(conf) as client:
...
```

## Writing Tests

Use the fixtures in `tests/conftest.py`:

```python
import httpx
from huwise_automation.api import DatasetsApi

def test_list_datasets(api_client, respx_mock):
respx_mock.get("https://test.huwise.com/api/automation/v1.0/datasets/").mock(
return_value=httpx.Response(200, json={
"total_count": 1,
"results": [{"dataset_id": "my-dataset", "uid": "abc123"}],
"next": None,
"previous": None,
})
)
with api_client:
page = DatasetsApi(api_client).list_datasets()
assert page.total_count == 1
```

## Package Naming

| Concern | Value |
|---------|-------|
| Python package (import) | `huwise_automation` |
| PyPI / dist name | `huwise-automation` |
| Generated class names | Neutral (`DatasetsApi`, `Dataset`, etc.) — derived from spec tags/schemas |

## Key Files

| File | Purpose |
|------|---------|
| `automation.yaml` | OpenAPI 3.1.0 spec — the only source to edit |
| `generators/python/config.json` | Generator config (no `packageVersion` — injected at runtime) |
| `generators/python/generate.sh` | Regenerate `python/` |
| `generators/python/generate-tests.sh` | Add stubs for new operations/models to `tests/` |
| `generators/python/templates/pyproject.mustache` | Generated pyproject.toml (hatchling, ruff) |
| `generators/python/templates/configuration.mustache` | `from_api_key()` classmethod |
| `generators/python/templates/api_client.mustache` | `ApiClient(host=, api_key=)` shorthand |
| `tests/python/conftest.py` | Shared pytest fixtures (`api_client`, `respx_mock`) |
| `pyproject.toml` | Root dev environment — pytest, ruff, path dep to `python/` |
Loading