You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add return-type annotations to config/graphql/ resolvers and mutations
Issue #1332 (follow-up to #1331) targeted the largest, least-typed subtree
in the backend (459 function definitions, 4.4% return-annotation coverage at
baseline). All resolver and mutation entry points are now annotated, taking
that subtree to 100% return-annotation coverage with zero behavior changes
and zero new # type: ignore lines.
What changed:
- mutate(...) on graphene.Mutation subclasses → forward-ref to enclosing
class (-> "ClassName"); discovered and fixed a latent missing-return in
DeleteAnalysisMutation.mutate (analysis_mutations.py:179) where the
success path silently returned None — annotation is now
"DeleteAnalysisMutation | None" with an explicit `return None` to
preserve original behavior.
- resolve_* methods → -> Any by default, refined where the GraphQL field
type makes the runtime return obvious (-> bool, -> int, etc.).
- AnnotatePermissionsForReadMixin (per issue guidance):
resolve_my_permissions → list[str]
resolve_is_published → bool
resolve_object_shared_with → list[dict[str, Any]]
The pre-existing wrong annotation list[PermissionTypes] (an Enum, but the
implementation returns plain strings) was corrected; the now-unused
PermissionTypes import was dropped.
- Filter / queryset helpers (filter_by_*, text_search_method, get_node,
get_queryset, _get_*, etc.) → -> Any (conservative; tightening to
QuerySet[Model] is a follow-up).
- Fixed a broken import in
permissioning/permission_annotator/utils.py
(config.graphql.permission_annotator.middleware →
config.graphql.permissioning.permission_annotator.middleware) and
replaced base64.binascii.Error with a direct binascii.Error import in
conversation_types.py.
- Added var-annotated declarations: id_to_children,
read_only_fields, this_model_permission_id_map.
Mypy graduation:
- Removed five modules from the mypy baseline (mypy.ini and
docs/typing/mypy_baseline.txt):
config.graphql.base_types
config.graphql.conversation_types
config.graphql.permissioning.permission_annotator.middleware
config.graphql.permissioning.permission_annotator.utils
config.graphql.serializers
- Verified: `python -m mypy --config-file mypy.ini opencontractserver
config` reports "Success: no issues found in 958 source files".
- Verified: black, isort, flake8 clean across config/graphql/.
The remaining baselined files in config.graphql.* surface structural type
issues (django-stubs missing the custom .visible_to_user() manager method,
set_permissions_for_obj_to_user signature accepting type[Model] instead of
Model, mixin _meta access) that need follow-up PRs to fix at the source
(opencontractserver/utils/permissioning.py, the manager classes, etc.)
rather than in this annotations-only PR.
Closes#1332
Copy file name to clipboardExpand all lines: CHANGELOG.md
+13Lines changed: 13 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
8
8
## [Unreleased]
9
9
10
+
### Added
11
+
12
+
-**Return-type annotations across `config/graphql/` resolvers and mutations** (Issue #1332, follow-up to #1331): The largest, least-typed subtree in the backend (459 function definitions, ~4.4% return-annotation coverage at baseline) is now at 100% return-annotation coverage. Touched files include every `*_mutations.py`, every `*_queries.py`, every `*_types.py`, plus `filters.py`, `base.py`, `base_types.py`, `security.py`, `optimized_file_resolvers.py`, `permissioning/permission_annotator/{middleware,mixins,utils}.py`, and the small utility modules. No behavioral changes — annotations only.
13
+
-**`mutate(...)` on `graphene.Mutation` subclasses**: typed as forward references to the enclosing class (`-> "ClassName"`). Discovered and fixed the latent bug in `config/graphql/analysis_mutations.py:179` (`DeleteAnalysisMutation.mutate`) where the success path had no `return` statement; annotation is `-> "DeleteAnalysisMutation | None"` and an explicit `return None` was added to preserve the original implicit-None behavior on success.
14
+
-**`resolve_*` methods**: typed as `-> Any` by default, refined where the GraphQL field type makes the runtime return obvious (e.g. `resolve_in_use -> bool`, `resolve_datacell_count -> int`).
15
+
-**`AnnotatePermissionsForReadMixin`** (`config/graphql/permissioning/permission_annotator/mixins.py`): per the issue's specific guidance, `resolve_my_permissions -> list[str]`, `resolve_is_published -> bool`, `resolve_object_shared_with -> list[dict[str, Any]]`. The pre-existing wrong annotation `list[PermissionTypes]` (an Enum, while the implementation returns plain strings) was corrected to `list[str]`. The now-unused `PermissionTypes` import was removed.
16
+
-**Filter / queryset helpers** (`filter_by_*`, `text_search_method`, `get_node`, `get_queryset`, `_get_*`, etc.) typed as `-> Any` to keep the change conservative; tightening to `QuerySet[Model]` is a follow-up.
17
+
-**`config/graphql/permissioning/permission_annotator/utils.py`** had a broken import (`config.graphql.permission_annotator.middleware` instead of `config.graphql.permissioning.permission_annotator.middleware`) — fixed in passing.
18
+
-**`config/graphql/conversation_types.py`**: replaced `base64.binascii.Error` with a direct `binascii.Error` import (pre-existing — `base64` re-exports `binascii` at runtime but `mypy` doesn't see the re-export).
19
+
-**Var-annotated additions**: `id_to_children: dict[Any, list[Any]]` in `base_types.py`, `read_only_fields: list[str]` in `serializers.py`, `this_model_permission_id_map: dict[int, str]` etc. in middleware.
20
+
-**Five modules graduated from the mypy baseline** (`mypy.ini` → no longer `ignore_errors = True`): `config.graphql.base_types`, `config.graphql.conversation_types`, `config.graphql.permissioning.permission_annotator.middleware`, `config.graphql.permissioning.permission_annotator.utils`, `config.graphql.serializers`. Their entries in `docs/typing/mypy_baseline.txt` (11 lines) were also pruned. Future PRs can graduate the remaining baselined files as the structural issues they expose (custom `visible_to_user` manager method not seen by `django-stubs`, `set_permissions_for_obj_to_user` signature mismatch, mixin `_meta` access) are addressed.
21
+
-**Tooling**: zero new `# type: ignore` markers; black & isort applied; `flake8 config/graphql/` clean. `mypy --config-file mypy.ini opencontractserver config` passes with the updated baseline.
22
+
10
23
### Fixed
11
24
12
25
- **Merged `frontend` Codecov flag drops to ~33% on every commit where Frontend CI's CT job fails** (`frontend/package.json` `test:coverage:ct`): the script chained `playwright test ... && mkdir -p ... && nyc report ...`, so a failing CT run short-circuited before `nyc report` could turn the per-test JSON files in `.nyc_output` into an `lcov.info`. The downstream `Upload CT Coverage to Codecov` step (`if: success() || failure()`) then errored with "No coverage reports found" and `frontend-component` did not upload for that SHA. Codecov's server-side aggregation of the `frontend` flag was left with only `frontend-unit` (~23%) and `frontend-e2e` (~24%), pulling the merged number down to ~33% even though the previous commit was at ~67% — observed on six consecutive main commits 2026-04-26T01:02..02:58Z (`2d7033f8`..`be5bcfc8`) before recovering on `30298391`. Mirrored the existing `test:e2e:coverage` pattern (`; CT_EXIT=$?; nyc report ... || echo "No coverage data to report"; exit $CT_EXIT`) so `nyc report` runs regardless of test outcome and the lcov ships even on red CT runs. `frontend-component` will still report a slightly lower number when tests fail (failed tests register fewer hits), but it will report — keeping the merged `frontend` flag's denominator stable.
0 commit comments