From 96b40a1f96ba553591f28d9c68e08946f320f0da Mon Sep 17 00:00:00 2001 From: chrisdengso <8185683+chrisdengso@users.noreply.github.com> Date: Thu, 22 Oct 2026 12:14:44 +0000 Subject: [PATCH] feat: regenerate from the OpenAPI spec --- .github/release-notes/v2.0.0.md | 33 +++++++++++++++++++++ .github/workflows/publish-pypi.yml | 46 ++++++++++++++++++++++-------- .speakeasy/gen.lock | 2 +- 3 files changed, 68 insertions(+), 13 deletions(-) create mode 100644 .github/release-notes/v2.0.0.md diff --git a/.github/release-notes/v2.0.0.md b/.github/release-notes/v2.0.0.md new file mode 100644 index 0000000..4a81b54 --- /dev/null +++ b/.github/release-notes/v2.0.0.md @@ -0,0 +1,33 @@ +## moderation_api 2.0.0 + +A new major version of the Python SDK. Read the breaking changes before you upgrade. + +```bash +pip install --upgrade "moderation_api>=2,<3" +``` + +### Python-specific changes + +- Requires Python 3.10 or later (was 3.9). +- Use the client as a context manager: `with ModerationAPI() as client:`. Every method has an `_async` version, for example `await client.content.submit_async(...)`. +- Methods take keyword arguments in snake_case, for example `client.content.submit(content={...}, author_id="user-123")`. +- Verify webhooks with `moderation_api.webhook_signature.construct_event(payload, signature)`. +### What's new + +- **Covers the whole API**, including the management API (`projects`, `channels`), wordlists and the review queue. +- **Automatic pagination.** List methods return an iterator that fetches the next page for you. +- **Clear errors.** One error class per HTTP status (`BadRequestError`, `NotFoundError`, `RateLimitError`, ...). Every error has a specific `code` (for example `invalid_api_key`, `quota_exceeded` or `author_not_found`), a `request_id` and a `doc_url`. See [Errors](https://docs.moderationapi.com/api-reference/errors). +- **Retries** with exponential backoff on `429` and `5xx`, and timeouts per client or per request. +- **Webhook verification** that reads the secret from `MODAPI_WEBHOOK_SECRET`. + +### Breaking changes + +The SDK is now generated from our OpenAPI spec with a new generator. Most calls keep their names, but check these: + +- **List responses** are `{ data, pagination }` instead of a plain array or a resource-specific shape, and list methods take `page` and `pageSize`. +- **Errors** have a new body: `code` is the specific error code, not the HTTP category. The old `data` field is gone. +- **Renamed:** `account.list` is now `account.retrieve`, and `actions.execute.execute` is now `actions.execute`. +- **Removed:** `actions.execute.executeByID` (use `actions.execute` with `actionId` or `actionKey`), `auth.*`, and `content.stream` (see [Real-time voice](https://docs.moderationapi.com/content-moderation/real-time-voice)). +- **Organization keys** work only with `projects` and `channels`; project keys work with everything else. Use one client per key. See the README. + +The license is unchanged: Apache-2.0. diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 7ce47e1..56781b4 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -1,29 +1,51 @@ # Maintained in moderation-api/sdk-generator (custom/python). Edits here are overwritten. +# Every push to main whose package version has no release tag yet publishes +# that version and creates the GitHub release (notes: .github/release-notes/vX.Y.Z.md +# if present, otherwise generated from the merged pull requests). name: Publish PyPI on: - release: - types: [published] + push: + branches: [main] workflow_dispatch: +concurrency: release + permissions: - contents: read + contents: write jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Read the version + run: echo "VERSION=$(sed -n 's/^version = \"\(.*\)\"$/\1/p' pyproject.toml | head -n 1)" >> "$GITHUB_ENV" + - name: Check for an unreleased version + id: version + run: | + echo "VERSION=${VERSION}" >> "$GITHUB_ENV" + if git ls-remote --exit-code --tags origin "refs/tags/v${VERSION}" >/dev/null; then + echo "v${VERSION} is already released." + echo "release=false" >> "$GITHUB_OUTPUT" + else + echo "release=true" >> "$GITHUB_OUTPUT" + fi - uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 + if: steps.version.outputs.release == 'true' with: version: "0.9.13" - - name: Check the release tag matches pyproject.toml - if: github.event_name == 'release' - run: | - version=$(sed -n 's/^version = "\(.*\)"$/\1/p' pyproject.toml | head -1) - if [ "${GITHUB_REF_NAME#v}" != "$version" ]; then - echo "::error::Release tag $GITHUB_REF_NAME does not match pyproject.toml version $version" - exit 1 - fi - - run: bash scripts/publish.sh + - if: steps.version.outputs.release == 'true' + run: bash scripts/publish.sh env: PYPI_TOKEN: ${{ secrets.MODERATION_API_PYPI_TOKEN || secrets.PYPI_TOKEN }} + - name: Create the GitHub release + if: steps.version.outputs.release == 'true' + env: + GH_TOKEN: ${{ github.token }} + run: | + notes=".github/release-notes/v${VERSION}.md" + if [ -f "$notes" ]; then + gh release create "v${VERSION}" --target "$GITHUB_SHA" --title "v${VERSION}" --notes-file "$notes" + else + gh release create "v${VERSION}" --target "$GITHUB_SHA" --title "v${VERSION}" --generate-notes + fi diff --git a/.speakeasy/gen.lock b/.speakeasy/gen.lock index ebbc3c7..b59cc30 100644 --- a/.speakeasy/gen.lock +++ b/.speakeasy/gen.lock @@ -1,7 +1,7 @@ lockVersion: 2.0.0 id: 9620bfba-8df9-4d0e-8796-8423b39f97f0 management: - docChecksum: 3320e8867e978fb3c091936a85b4b990 + docChecksum: 9e61a5512adcd4b1e0a74112ed775731 docVersion: 1.1.0 speakeasyVersion: internal generationVersion: internal