Skip to content

Commit 57d1bc5

Browse files
leliaclaude
andauthored
Make every API-sourced enum forward-compatible, and detect drift proactively (#107)
* feat(enums): make every API-sourced enum forward-compatible Five of the six enums populated from API responses coerced strictly, so a value the backend added would raise ValueError inside from_dict and empty an entire response rather than degrade one field. That is the mechanism behind issue #78 and the unknown `generic` purl type; each was fixed on the single enum that fired, leaving the rest holding the same landmine. All five now fall back to a documented member and log the unrecognized value. The fallbacks are chosen rather than convenient: SocketIssueSeverity and DiffType gain an explicit UNKNOWN, since guessing an existing level would either hide a real finding or invent one, and SecurityAction defers. A generalized test discovers every enum in the package, including ones added later, and fails if any raises. A drift check compares the enums against the public OpenAPI spec; it found 10 purl types the SDK was missing, which are added here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: drop Linear IDs from test docstrings and comments Ticket identifiers belong in the pull request description, not in code that outlives the ticket. The GitHub issue reference in the purl test docstring stays, since that resolves for anyone reading the repository. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: add .gitattributes and normalize line endings to LF Eleven Python files were committed with CRLF, socketdev/__init__.py among them. Any tooling that reads and rewrites one of those files converts it to LF on the way out, so a two-line edit arrives as a whole-file diff with the real change buried in it. That happened while writing the enum change in this same branch. This normalizes all of them once and pins the setting so it cannot recur. Reviewable with `git diff -w`, which shows only .gitattributes: no file content changed, and the unit suite is unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(enums): keep ScanType strict, it only ever builds requests ScanType never parses an API response. FullScanParams.to_dict() is urlencoded onto the create-scan query string, so giving it a _missing_ fallback meant a caller typo silently shipped scan_type=unknown to the API instead of failing at construction. The same from_dict already passes integration_type through uncoerced for that reason. It is now recorded in REQUEST_ONLY_ENUMS, the opt-out the invariant test always had and this branch had left empty, and a new test asserts request-only enums keep raising so the exemption cannot quietly become a skip. Also bumps actions/setup-python in the new workflow to v7.0.0, matching the pin already used by .github/actions/setup-sfw. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent de850cc commit 57d1bc5

22 files changed

Lines changed: 2731 additions & 2278 deletions

.gitattributes

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Normalize line endings to LF in the repository.
2+
#
3+
# Eleven Python files were committed with CRLF, socketdev/__init__.py among
4+
# them. Any tooling that reads and rewrites one of those files converts it to
5+
# LF on the way out, which turns a two-line change into a whole-file diff and
6+
# buries the actual edit. Normalizing once and pinning the setting here stops
7+
# that from recurring.
8+
* text=auto
9+
10+
*.py text eol=lf
11+
*.pyi text eol=lf
12+
*.md text eol=lf
13+
*.rst text eol=lf
14+
*.toml text eol=lf
15+
*.cfg text eol=lf
16+
*.yml text eol=lf
17+
*.yaml text eol=lf
18+
*.json text eol=lf
19+
*.sh text eol=lf
20+
*.lock text eol=lf
21+
22+
# Byte-exact, never line-ending-normalized.
23+
*.png binary
24+
*.jpg binary
25+
*.jpeg binary
26+
*.gif binary
27+
*.ico binary
28+
*.whl binary
29+
*.gz binary
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: API Drift Check
2+
3+
# Compares this SDK's hand-maintained enums against the live Socket OpenAPI
4+
# spec. The spec is public and unauthenticated, so this job needs no secrets,
5+
# no org and no fixture data.
6+
#
7+
# Deliberately NOT a pull_request trigger: it tests the API, not the diff, and a
8+
# backend change must never block an unrelated SDK pull request.
9+
#
10+
# TODO: once this has run green manually a few times, uncomment the schedule
11+
# below to turn it into an early-warning signal instead of a manual check.
12+
on:
13+
workflow_dispatch:
14+
# schedule:
15+
# - cron: '0 14 * * 1' # Mondays, 14:00 UTC
16+
17+
permissions:
18+
contents: read
19+
20+
jobs:
21+
enum-drift:
22+
runs-on: ubuntu-latest
23+
steps:
24+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
25+
with:
26+
fetch-depth: 1
27+
persist-credentials: false
28+
29+
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
30+
with:
31+
python-version: "3.12"
32+
33+
- name: Install the SDK
34+
run: python -m pip install .
35+
36+
- name: Compare SDK enums against the live OpenAPI spec
37+
run: python scripts/check_api_enum_drift.py

CHANGELOG.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,42 @@
11
# Changelog
22

3+
## 3.6.0
4+
5+
### Changed: every API-sourced enum now tolerates unknown values
6+
7+
- `SocketIssueSeverity`, `SocketCategory`, `DiffType` and `SecurityAction`
8+
now fall back to a documented member instead of raising
9+
`ValueError` when the API sends a value this release does not know about.
10+
`SocketPURL_Type` already behaved this way; the others did not, so each was
11+
one backend addition away from emptying a response the same way issue #78 and
12+
the unknown `generic` purl type did. `ScanType` is deliberately left strict:
13+
it is only ever urlencoded onto the create-scan request, so an unrecognized
14+
value is a caller typo rather than API drift.
15+
- Fallbacks are deliberate rather than convenient. `SocketIssueSeverity` and
16+
`DiffType` gained an explicit `UNKNOWN` member because guessing an existing
17+
level would either hide a real finding or invent one, and `SecurityAction`
18+
falls back to `DEFER` because that already means "use the configured
19+
default". Every fallback logs a warning naming the unrecognized value.
20+
- Added the 10 purl types the API defines that this SDK was missing: `alpm`,
21+
`chrome`, `clawhub`, `edge-extension`, `firefox-extension`, `qpkg`, `socket`,
22+
`swid`, `vscode` and `vscode-extension`. Artifacts with those types were
23+
being flattened to `unknown`.
24+
25+
### Added: enum forward-compatibility is now an enforced invariant
26+
27+
- `tests/unit/test_enum_forward_compat.py` discovers every enum in the package,
28+
including ones added later, and fails if any raises on an unrecognized value.
29+
The two prior incidents were each fixed with a bespoke test on the single
30+
enum that happened to fire; this replaces that pattern.
31+
32+
### Added: scheduled check for enum drift against the live API
33+
34+
- `scripts/check_api_enum_drift.py` compares the SDK's enums against
35+
`https://api.socket.dev/v0/openapi`. The spec is public, so the check needs no
36+
token, org or fixture data. Run by `.github/workflows/api-drift-check.yml`,
37+
which is manual-dispatch only for now and is not a pull request check --- it
38+
tests the API rather than the diff.
39+
340
## 3.5.0
441

542
### Changed: bound runtime dependency ranges and pin build backend

0 commit comments

Comments
 (0)