-
Notifications
You must be signed in to change notification settings - Fork 0
feat(api): v4.0.1 OpenAPI contract, contract-first source, and specs #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
17957a5
75ff3e0
67dbe85
87786ff
93055aa
2f9d9b0
9f859db
8dcea32
2115930
062ad88
f70225f
39cc34e
0447ab5
b049c56
1622775
908e958
c7eb7a5
e021aa7
7b0cbc7
3da0d56
6a05396
a3d2724
2924d97
844aa49
5cce02d
71791c4
16aa992
fedfd9b
a8b5c55
f358aeb
4d9dea3
c083f6a
ae4c841
aba221d
9bed783
922eff1
637f1e8
4f3991c
c998885
1ccfc60
e9417b5
6d636f9
3bb75da
528b10a
f93b6b2
8702c33
09fdd80
541976a
ad03e54
032fd23
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| root = true | ||
|
|
||
| [*] | ||
| charset = utf-8 | ||
| end_of_line = lf | ||
| indent_style = space | ||
| indent_size = 4 | ||
| insert_final_newline = true | ||
| trim_trailing_whitespace = true | ||
|
|
||
| [*.{json,yml,yaml}] | ||
| indent_size = 2 | ||
|
|
||
| [{*.markdown,*.md}] | ||
| ij_markdown_wrap_text_if_long = false | ||
| ij_markdown_keep_line_breaks_inside_text_blocks = true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| openapi.json linguist-generated=true | ||
| uv.lock linguist-generated=true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| version: 2 | ||
| updates: | ||
| - package-ecosystem: github-actions | ||
| directory: / | ||
| schedule: | ||
| interval: weekly | ||
|
|
||
| - package-ecosystem: uv | ||
| directory: / | ||
| schedule: | ||
| interval: weekly |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| name: CI | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main] | ||
| pull_request: | ||
| branches: [main] | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| contract: | ||
| name: OpenAPI contract | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - name: Set up uv | ||
| uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 | ||
| with: | ||
| enable-cache: true | ||
|
|
||
| - name: Install dependencies | ||
| run: uv sync --locked | ||
|
|
||
| - name: Lint | ||
| run: uv run --locked ruff check . | ||
|
|
||
| - name: Type check | ||
| run: uv run --locked pyright | ||
|
|
||
| - name: Test | ||
| run: uv run --locked pytest |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| 3.13 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| .PHONY: dev export-openapi lint test | ||
|
|
||
| dev: | ||
| uv run fastapi dev src/nc3_testing_platform/main.py | ||
|
|
||
| export-openapi: | ||
| uv run export-openapi | ||
|
|
||
| lint: | ||
| uv run ruff check . | ||
|
|
||
| test: | ||
| uv run pytest | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -8,7 +8,7 @@ Project repository for the NC3 Testing Platform backend (v4). | |||||||||||||
|
|
||||||||||||||
| - Python 3.13, [uv](https://docs.astral.sh/uv/) (packaging + virtualenv) | ||||||||||||||
| - FastAPI + Pydantic — the app and its request/response models | ||||||||||||||
| - openapi-spec-validator (dev) — validates the generated 3.1 spec | ||||||||||||||
| - pytest + openapi-spec-validator (dev) — the contract test suite | ||||||||||||||
|
|
||||||||||||||
| **Projected** — planned: | ||||||||||||||
|
|
||||||||||||||
|
|
@@ -28,7 +28,7 @@ No environment variables or config are required yet. | |||||||||||||
| # Running the mock server | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| uv run fastapi dev app/main.py | ||||||||||||||
| make dev | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| - API base: http://localhost:8000/api/v1 | ||||||||||||||
|
|
@@ -40,36 +40,43 @@ Handlers return static stub data, so the running server doubles as a mock the fr | |||||||||||||
|
|
||||||||||||||
| # Generating the OpenAPI contract | ||||||||||||||
|
|
||||||||||||||
| The OpenAPI 3.1 spec is generated from the FastAPI app (`app.main:app`) and written to `docs/openapi.json`. | ||||||||||||||
| The OpenAPI 3.1 spec is generated from the FastAPI app (`nc3_testing_platform.main:app`) and written to `api/openapi.json`. | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| uv run python -m app.tools.export_openapi # write docs/openapi.json | ||||||||||||||
| uv run openapi-spec-validator --schema 3.1 docs/openapi.json # validate (exits 0 if valid) | ||||||||||||||
| make export-openapi # write api/openapi.json | ||||||||||||||
| make lint # ruff over the source | ||||||||||||||
| make test # validate it, and check the committed file is current | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| Regenerate and re-validate after any change to a router or Pydantic schema. `docs/openapi.json` is the contract the | ||||||||||||||
| frontend interfaces with; commit it alongside the change that alters it. | ||||||||||||||
| The development routine after any change to a router or Pydantic schema is `make export-openapi && make lint`. `api/openapi.json` is the contract the frontend interfaces with; commit it alongside the change that alters it. | ||||||||||||||
|
|
||||||||||||||
| `make test` validates the generated document against OpenAPI 3.1 and fails if the committed file differs from it. CI runs the same command. | ||||||||||||||
|
Comment on lines
+51
to
+53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Include Line 51 instructs contributors to run only Proposed documentation fix-The development routine after any change to a router or Pydantic schema is `make export-openapi && make lint`.
+The development routine after any change to a router or Pydantic schema is `make export-openapi && make lint && make test`.📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
|
|
||||||||||||||
| # Project structure | ||||||||||||||
|
|
||||||||||||||
| > **Current scope:** the only working functionality is the Pydantic schemas and OpenAPI spec generation. Route handlers | ||||||||||||||
| > return stub data so the app runs as a live mock; there is no persistence, auth backend, or scan logic yet. | ||||||||||||||
|
|
||||||||||||||
| ``` | ||||||||||||||
| app/ | ||||||||||||||
| src/nc3_testing_platform/ | ||||||||||||||
| main.py # FastAPI app; mounts every domain router under /api/v1 | ||||||||||||||
| core/ # shared, cross-cutting building blocks | ||||||||||||||
| enums.py # canonical enums | ||||||||||||||
| schemas.py # base model config + shared field types | ||||||||||||||
| errors.py # RFC 9457 problem+json errors + handlers | ||||||||||||||
| pagination.py # cursor pagination | ||||||||||||||
| security.py # OpenAPI security schemes + rate-limit contract | ||||||||||||||
| domains/ # one vertical slice per domain (router + schemas together) | ||||||||||||||
| guest/ auth/ org/ assets/ scans/ | ||||||||||||||
| schedules/ findings/ reports/ notifications/ health/ | ||||||||||||||
| domains/ # one vertical slice per domain | ||||||||||||||
| scans/ # every slice follows this layout | ||||||||||||||
| models.py # SQLAlchemy models | ||||||||||||||
| schemas.py # Pydantic request and response models | ||||||||||||||
| repository.py # queries; session is the first argument | ||||||||||||||
| service.py # business logic and transaction boundaries | ||||||||||||||
| router.py # path operations | ||||||||||||||
| tools/ | ||||||||||||||
| export_openapi.py # dumps app.openapi() -> docs/openapi.json | ||||||||||||||
| export_openapi.py # dumps app.openapi() -> api/openapi.json | ||||||||||||||
| api/ | ||||||||||||||
| openapi.json # generated API contract (see "Generating the OpenAPI contract") | ||||||||||||||
| docs/ | ||||||||||||||
| openapi.json # generated contract (see "Generating the OpenAPI contract") | ||||||||||||||
| reference/ # source design docs (data-model, ADRs) | ||||||||||||||
| ``` | ||||||||||||||
| reference/ # reference documentation | ||||||||||||||
| ``` | ||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| # API contract | ||
|
|
||
| `openapi.json` is the generated OpenAPI 3.1 contract for the v4 backend. | ||
| It is committed so the frontend and client generators can consume it without a Python toolchain, and so contract changes | ||
| show up as reviewable PR diffs. | ||
|
|
||
| Do not edit it by hand. | ||
| The source of truth is the FastAPI app; regenerate after any schema or route change: | ||
|
|
||
| ```bash | ||
| make export-openapi | ||
| ``` | ||
|
|
||
| Commit the regenerated file together with the code change that caused it. | ||
| CI validates the committed spec and fails if it drifts from what the app generates. |
Uh oh!
There was an error while loading. Please reload this page.