Repository navigation
v0.22.0
Pre-releaseFOCA 0.22.0 prepares apps for FOCA's ASGI release, which moves to Connexion 3 AsyncApp and removes Flask and Flask-Cors. Nothing is removed in 0.22.0: FOCA warns about what the ASGI release removes where it can, and adds replacements that already work on Connexion 2: request and claims accessors (foca.context), framework-neutral errors (foca.errors), a test client (foca.testing) and an ast-grep rule that finds Flask imports. The functions that take a Flask app (get_app_config(), set_app_config(), enable_cors() and foca.factories.celery_app.create_celery_app()) emit a DeprecationWarning, and FOCA logs each deprecation once per process; exception maps with raw Werkzeug classes are logged as deprecated and stop working with the ASGI release. Some defaults change, which clients, tests and log checks may notice: the detail of responses to invalid requests and the INFO logs no longer quote values of clients, and routes that are not operations of the specifications are treated differently (see Breaking changes). Move your app now: see Preparing for the ASGI release and the migration guide. When work on the ASGI release starts, a 0.22.x branch is cut from the latest 0.22 release, for dependency and security fixes while apps migrate.
Breaking changes
- Deprecated, removed in the ASGI release:
get_app_config(),set_app_config(),enable_cors()andfoca.factories.celery_app.create_celery_app()emit aDeprecationWarningand are logged once per process; usefoca.get_config()(in scripts, withFoca.use_config()), thesecurity.corsconfiguration andFoca.create_celery_app()instead (#637, #731, #741, #742) - Also deprecated, removed in the ASGI release: reading, setting and deleting
app.config.foca(#716, #721),www_authenticateoffoca.errors.Unauthorized, whose challenges are now inheaders(#710, #714), exception maps with raw Werkzeug classes instead of those offoca.errors(#633, #709), andx-body-name: permissionin specifications that route operations to the controllers of the permissions API (#717, #722) - FOCA finds routes in its own route table of the operations of the specifications instead of Flask's
url_map; for routes that aren't operations (e.g., the Swagger UI,openapi.json, routes added to Flask directly),is_permitted_for()is nowFalse,me_pathis no longer checked, and ownership controllers refuse requests; the log fieldoperation_idis the specification'soperationId, or the one Connexion resolves if the specification has none (see Routes that aren't operations) (#632, #707; #718, #726) - By default, the
detailof responses to requests rejected by request validation names the invalid fields by location and code (e.g.,Invalid fields: #/name (too-long)), or, if none are listed, is the mappeddetailorThe request is invalid., instead of the validator's reason; this also replaces thedetailofBadRequestProblem(RequestValidationProblem) that apps raise, so raisefoca.errors.InvalidFieldsProblemto name the invalid fields of checks of your own. Clients and tests that checkdetailshould checkerrorsinstead, or setexceptions.validation_reasons: trueto keep the reasons (see Validation reasons without request values) (#732, #733, #736, #738) - Logs at
INFO: request logs (log_traffic) and denials of access control name the route (the path template of the operation, e.g.,GET /ga4gh/tes/v1/tasks/{id}, or<unknown route>) instead of the path, and requests rejected by request validation without invalid fields are logged with the mappeddetailor the title instead of the validator's reason; paths and reasons are logged atDEBUG, so log atDEBUG(e.g., for the loggerfoca) to keep them (see Validation reasons without request values) (#748, #750)
Features
- Request and claims accessors that keep their meaning on ASGI:
foca.context.current_request(), a read-only snapshot of the request, andcurrent_token_info()andcurrent_claims()offoca.security.auth; FOCA itself reads the request only through them (#634, #715, #720; #717, #722; #718, #726) foca.errors: framework-neutral error classes and stable names of Connexion's problems, which FOCA raises everywhere; challenges ofUnauthorizedgo in itsheaders(#633, #709; #710, #714)foca.api.routes, a route table of the operations of the registered specifications, withget_routes()andmatch()(#632, #707)foca.testing.Client, a test client with the API of httpx and Starlette'sTestClient,foca.testing.request_context()for unit tests of code that reads the accessors, and the pytest fixturesfoca_appandfoca_client(#635, #729)- FOCA logs each of its deprecations once per process at
WARNING, on the loggerfoca.deprecation, so that they show under Gunicorn, where Python hidesDeprecationWarning; apps can usefoca.deprecation.warn_deprecated()(#727, #728) - The ast-grep rule
flask-importin FOCA's repository finds the imports of Flask, Werkzeug and Connexion's Flask modules that apps still have to move before the ASGI release, and names the replacements (#636, #730) - Responses to invalid requests list the invalid fields (up to 100), of the body and the parameters together, in the new member
errorsof their problem details, with the problem type Invalid request;titleandstatusstay as before, anddetailchanges as described under Breaking changes. Raisefoca.errors.InvalidFieldsProblemto list the invalid fields of checks of your own the same way (#586, #711, #712, #734) Foca.use_config()lets scripts use a configuration and its collections (get_config(),get_collection()) without creating an app, without Flask and without writing to the database; it replacesset_app_config()for scripts (see Preparing for the ASGI release) (#741, #742)
Security
- Responses to invalid requests no longer quote the rejected values (and, for parameters, the schema) in
detail, which now names the invalid fields by location and code, e.g.,Invalid fields: #/name (too-long); they stay listed inerrors. Set the new optionexceptions.validation_reasons: trueto keep the validator's reasons, e.g., if clients depend on them. Names that clients choose are still named indetail: those of unknown query and form parameters and unknown keys of the body (#732, #733, #736, #738) - The plugin gateway's refusals of per-request pipelines and backends no longer quote the client's values; they name fields, settings and the positions of steps, e.g.,
Plugin not available in step 1(see Plugin gateway messages) (#739, #740) - Error messages and logs quote fewer client values:
GET /plugins/{plugin_id}names the parameter instead of the identifier, errors of the JSON parser on clients' pipelines get a fixed message unlessexceptions.validation_reasonsis on, an invalid user acted for in theauth.on_behalf_of_headeris listed as an invalid field of the header, so that clients learn what to fix, and rejected users acted for, invalid identifiers and values of policy rules and the roles and subjects of refused role grants are no longer logged atINFOor above (#743, #745) - Request logs, denials of access control and rejections by request validation no longer log values of clients at
INFO: request logs and denials name the route (the path template of the operation, e.g.,GET /ga4gh/tes/v1/tasks/{id}) instead of the path, and rejections without invalid fields the title or mappeddetailinstead of the validator's reason; paths and reasons are logged atDEBUG; theINFOrecordRequest rejectedstill names unknown parameters and body keys, whose names clients choose (see Migrating to 0.22.0) (#748, #750) - Errors for malformed MongoDB connection strings have a fixed message instead of PyMongo's, which could quote parts of the password (#701, #703)
- The FOCA images install the Debian updates available when they are built, instead of upgrading single packages, and so get the fixes Debian has released, e.g., for zlib1g (CVE-2026-76844, CVE-2026-85091, CVE-2026-27171), which their base images still lack; images built on FOCA's image get the fixes, too (#747, #749)
Fixes
- Apps are released when they are no longer used, also while objects of their APIs live on, e.g., in log records that a handler keeps; before, such objects kept the Flask app alive, which mattered for processes that build many apps, such as test suites (#735, #737)
- The database configurations and MongoDB clients of apps with background jobs are released with their apps; before, Celery's worker signals kept them alive, which mattered for processes that create many apps, such as test suites or scripts (#744, #746)
foca check-config,sync-policiesandexport-specno longer set up the plugin store of gateway apps without adbsection (#705, #706)- Query strings that aren't UTF-8 no longer fail pagination links or the check for unexpected query parameters; such bytes stay percent-encoded (#717, #722; #718, #726)
- Users acted for that can't be stored in policy rules (e.g., with a comma) are rejected as invalid fields of the
auth.on_behalf_of_headerwith400 Bad Requestinstead of failing with500 Internal Server Errorwhen the app makes them owners; grants of roles to groups that don't apply (group_mappingwithoutkeep_group_subjects) are warned about without the role, group and resource, which are logged atDEBUG; the image build also fails if the check for kept-back Debian updates fails itself; and the built-in access control and/meAPIs have the version 1.1.0, as theirErrorschema gainedcode,errorsandFieldError(#752, #755)
Documentation
- New page Preparing for the ASGI release: what the ASGI release removes, what to use now, with examples, and how to find what is left (#637, #731)
- New page Writing controllers: inputs, accessors, return values and the differences between Connexion 2 and 3 (#719, #723)
- The migration guide covers every change that apps, clients and log checks may have to act on, with the deprecations, the new defaults of
detailand the logs and the routes that aren't operations (#633, #709; #751, #754) - New page Invalid request, the problem type of responses to invalid requests, with their invalid fields (#586, #711)
- The decisions not to coalesce concurrent fetches of user info and to keep
check-configon the app are documented (#702, #704)
Maintenance
- FOCA's route checks, security code and helpers no longer use Flask's
url_mapand request objects, but the route table and the accessors, so that they move to ASGI unchanged (#632, #707; #717, #722; #718, #726) - Unused test files and constants removed (#724, #725)
- Dependency updates (#713)
- Release preparation for 0.22.0: citation metadata, release notes and migration guide (#751, #754)
Commits
- fix(db): don't quote URIs in MongoDB errors by @uniqueg in #703
- docs: record userinfo, check-config decisions by @uniqueg in #704
- fix(cli): don't set up plugin store in checks by @uniqueg in #706
- refactor(api): check routes from specs by @uniqueg in #707
- refactor(errors): make errors framework-neutral by @uniqueg in #709
- feat: report invalid fields in 400 responses by @uniqueg in #711
- build(deps): Bump ast-grep-cli from 0.51.0 to 0.51.1 in the python-dependencies group across 1 directory by @dependabot[bot] in #713
- refactor(errors): move the challenge to headers by @uniqueg in #714
- feat: add request and claims accessors by @uniqueg in #720
- refactor(config): deprecate config.foca accessors by @uniqueg in #721
- refactor(security): read requests via accessors by @uniqueg in #722
- docs: add the page Writing controllers by @uniqueg in #723
- test: remove unused test file and constants by @uniqueg in #725
- refactor(utils): read the request via accessors by @uniqueg in #726
- feat: log deprecations once per process by @uniqueg in #728
- feat(testing): add an httpx-style test client by @uniqueg in #729
- ci: flag Flask imports with ast-grep by @uniqueg in #730
- docs: add "Preparing for the ASGI release" by @uniqueg in #731
- fix: stop echoing values in validation errors by @uniqueg in #733
- feat: list body and parameter errors together by @uniqueg in #734
- fix: release apps after installing the route table by @uniqueg in #737
- feat: don't quote rejected values by default by @uniqueg in #738
- fix: quote no client values in gateway errors by @uniqueg in #740
- feat: use a configuration without an app by @uniqueg in #742
- fix: keep client values out of error messages by @uniqueg in #745
- fix: release configs held by Celery signals by @uniqueg in #746
- build: pick up OS security updates in images by @uniqueg in #749
- fix: log route templates instead of paths by @uniqueg in #750
- build: prepare the 0.22.0 release by @uniqueg in #754
- fix: small fixes from the 0.22.0 review by @uniqueg in #755
Full Changelog: v0.21.0...v0.22.0