{"id":46688207,"url":"https://github.com/ndsev/zswag","last_synced_at":"2026-03-09T02:35:22.827Z","repository":{"id":40427234,"uuid":"253472682","full_name":"ndsev/zswag","owner":"ndsev","description":"Access/Serve zserio services via REST/OpenAPI 🌍.","archived":false,"fork":false,"pushed_at":"2026-02-08T14:06:46.000Z","size":1027,"stargazers_count":15,"open_issues_count":13,"forks_count":4,"subscribers_count":5,"default_branch":"main","last_synced_at":"2026-02-08T20:43:00.368Z","etag":null,"topics":["connexion","flask","openapi3","services","swagger","wsgi","zserio"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ndsev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-04-06T11:07:06.000Z","updated_at":"2025-12-05T11:20:42.000Z","dependencies_parsed_at":"2023-09-23T04:20:11.443Z","dependency_job_id":"4671c0c1-03e7-4e0e-97f4-e6ba9bee8467","html_url":"https://github.com/ndsev/zswag","commit_stats":null,"previous_names":["klebert-engineering/zswag"],"tags_count":41,"template":false,"template_full_name":null,"purl":"pkg:github/ndsev/zswag","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ndsev%2Fzswag","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ndsev%2Fzswag/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ndsev%2Fzswag/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ndsev%2Fzswag/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ndsev","download_url":"https://codeload.github.com/ndsev/zswag/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ndsev%2Fzswag/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30280956,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-09T02:23:26.802Z","status":"ssl_error","status_checked_at":"2026-03-09T02:22:46.175Z","response_time":61,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["connexion","flask","openapi3","services","swagger","wsgi","zserio"],"created_at":"2026-03-09T02:35:22.153Z","updated_at":"2026-03-09T02:35:22.796Z","avatar_url":"https://github.com/ndsev.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Zswag\n\n[![CI](https://github.com/ndsev/zswag/actions/workflows/build-deploy.yml/badge.svg)](https://github.com/ndsev/zswag/actions/workflows/build-deploy.yml)\n[![codecov](https://codecov.io/github/ndsev/zswag/graph/badge.svg?token=5DTX2M8IDE)](https://codecov.io/github/ndsev/zswag)\n[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ndsev_zswag\u0026metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ndsev_zswag)\n[![Release](https://img.shields.io/github/release/ndsev/zswag)](https://GitHub.com/ndsev/zswag/releases/)\n[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=ndsev_zswag\u0026metric=coverage)](https://sonarcloud.io/summary/new_code?id=ndsev_zswag)\n[![License](https://img.shields.io/github/license/ndsev/zswag)](https://github.com/ndsev/zswag/blob/master/LICENSE)\n\nzswag is a set of libraries for using/hosting zserio services through OpenAPI.\n\n**Table of Contents:**\n\n  * [Components](#components)\n  * [Setup](#setup)\n    + [For Python Users](#for-python-users)\n    + [For C++ Users](#for-c-users)\n      - [Offline/Disconnected Builds](#offlinedisconnected-builds)\n  * [CI/CD and Release Process](#cicd-and-release-process)\n    + [Continuous Integration](#continuous-integration)\n    + [Release Process](#release-process)\n    + [Development Snapshots](#development-snapshots)\n    + [Version Validation](#version-validation)\n  * [OpenAPI Generator CLI](#openapi-generator-cli)\n    + [Generator Usage Example](#generator-usage-example)\n    + [Documentation extraction](#documentation-extraction)\n  * [Server Component (Python)](#server-component)\n  * [Using the Python Client](#using-the-python-client)\n  * [C++ Client](#c-client)\n  * [Client Environment Settings](#client-environment-settings)\n  * [HTTP Proxies and Authentication](#persistent-http-headers-proxy-cookie-and-authentication)\n  * [Swagger User Interface](#swagger-user-interface)\n  * [Client Result Code Handling](#client-result-code-handling)\n  * [OpenAPI Options Interoperability](#openapi-options-interoperability)\n    + [HTTP method](#http-method)\n    + [Request Body](#request-body)\n    + [URL Blob Parameter](#url-blob-parameter)\n    + [URL Scalar Parameter](#url-scalar-parameter)\n    + [URL Array Parameter](#url-array-parameter)\n    + [URL Compound Parameter](#url-compound-parameter)\n    + [Server URL Base Path](#server-url-base-path)\n    + [Authentication Schemes](#authentication-schemes)\n\n## Components\n\nThe zswag repository contains two main libraries which provide\nOpenAPI layers for zserio Python and C++ clients. For Python, there\nis even a generic zserio OpenAPI server layer.\n\nThe following UML diagram provides a more in-depth overview:\n\n![Component Overview](doc/zswag-architecture.png)\n\nHere are some brief descriptions of the main components:\n\n* `zswagcl` is a C++ Library which exposes the zserio OpenAPI service client `OAClient`\n  as well as the more generic `OpenApiClient` and `OpenApiConfig` classes.\n  The latter two are reused for the Python client library.\n* `zswag` is a Python Library which provides both a zserio Python service client\n  (`OAClient`) as well as a zserio-OpenAPI server layer based on Flask/Connexion\n  (`OAServer`). It also contains the command-line tool `zswag.gen`, which can be\n  used to generate an OpenAPI specification from a zserio Python service class.\n* `pyzswagcl` is a binding library which exposes the C++-based OpenApi\n  parsing/request functionality to Python. **Please consider it \"internal\".**\n* `httpcl` is a wrapper around [cpp-httplib](https://github.com/yhirose/cpp-httplib),\n  HTTP request configuration and OS secret storage abilities based on\n  the [keychain](https://github.com/hrantzsch/keychain) library.\n  \n## Setup\n\n### For Python Users\n\nSimply run `pip install zswag`. **Note: This only works with ...**\n\n* 64-bit Python 3.10-3.13, `pip --version` \u003e= 19.3\n* Supported platforms: Linux (x86_64), macOS (x86_64 and arm64), Windows (x64)\n\n**Notes:**\n* On Windows, make sure that you have the *Microsoft Visual C++ Redistributable Binaries* installed. You can find the x64 installer here: https://aka.ms/vs/16/release/vc_redist.x64.exe\n* zswag for Python 3.10 is not supported on Apple Silicon (arm64) because no compatible GitHub Actions runner is available.\n  However, this is typically not an issue, as macOS includes more recent Python versions by default.\n\n### For C++ Users\n\nUsing CMake, you can ...\n\n* 🌟run tests.\n* 🌟build the zswag wheels for a custom Python version.\n* 🌟[integrate the C++ client into a C++ project.](#c-client)\n\nDependencies are managed via CMake's `FetchContent` mechanism. Make sure you have a recent version of CMake (\u003e= 3.22.3) installed.\n\nThe basic setup follows the usual CMake configure/build steps:\n```bash\nmkdir build \u0026\u0026 cd build\ncmake ..\ncmake --build .\n```\n\n**Note:** The Python environment used for configuration will be used\nto build the resulting wheels. After building, you will find the Python\nwheels under `build/bin/wheel`.\n\n**To run tests**, just execute CTest at the top of the build directory:\n```bash\ncd build \u0026\u0026 ctest --verbose\n```\n\n#### Offline/Disconnected Builds\n\nFor environments without internet access or for reproducible builds, zswag supports offline builds using CMake's FetchContent mechanism.\n\n**Offline Build Process**\n\nFor offline builds, you can pre-fetch all dependencies while online and then build without network access:\n\n```bash\n# First, fetch all dependencies while online\nmkdir build \u0026\u0026 cd build\ncmake -DFETCHCONTENT_FULLY_DISCONNECTED=OFF ..\n# This will download all dependencies\n\n# Then build offline\ncmake -DFETCHCONTENT_FULLY_DISCONNECTED=ON ..\ncmake --build .\n```\n\nThe `FETCHCONTENT_FULLY_DISCONNECTED=ON` option tells CMake to use only the pre-fetched dependencies and never attempt network access.\n\n**Local Development with Custom Dependencies**\n\nFor development, you can override specific dependencies with local sources:\n```bash\nmkdir build \u0026\u0026 cd build\ncmake -DFETCHCONTENT_SOURCE_DIR_SPDLOG=/path/to/local/spdlog ..\ncmake --build .\n```\n\nAvailable override variables:\n- `FETCHCONTENT_SOURCE_DIR_ZLIB` - zlib compression library\n- `FETCHCONTENT_SOURCE_DIR_SPDLOG` - spdlog logging library  \n- `FETCHCONTENT_SOURCE_DIR_YAML_CPP` - yaml-cpp parsing library\n- `FETCHCONTENT_SOURCE_DIR_STX` - stx utility library\n- `FETCHCONTENT_SOURCE_DIR_SPEEDYJ` - speedyj JSON library\n- `FETCHCONTENT_SOURCE_DIR_HTTPLIB` - cpp-httplib HTTP library\n- `FETCHCONTENT_SOURCE_DIR_OPENSSL` - OpenSSL cryptography library\n- `FETCHCONTENT_SOURCE_DIR_PYBIND11` - pybind11 (when `ZSWAG_BUILD_WHEELS=ON`)\n- `FETCHCONTENT_SOURCE_DIR_PYTHON_CMAKE_WHEEL` - python-cmake-wheel (when `ZSWAG_BUILD_WHEELS=ON`)\n- `FETCHCONTENT_SOURCE_DIR_ZSERIO_CMAKE_HELPER` - zserio build helpers\n- `FETCHCONTENT_SOURCE_DIR_KEYCHAIN` - keychain library (when `ZSWAG_KEYCHAIN_SUPPORT=ON`)\n- `FETCHCONTENT_SOURCE_DIR_CATCH2` - Catch2 testing framework (when `ZSWAG_ENABLE_TESTING=ON`)\n\n**Build Options**\n\nCommon build configuration options:\n```bash\n# Minimal build (no wheels, no keychain, no tests)\ncmake -DZSWAG_BUILD_WHEELS=OFF -DZSWAG_KEYCHAIN_SUPPORT=OFF -DZSWAG_ENABLE_TESTING=OFF ..\n\n# Offline build with custom spdlog\ncmake -DFETCHCONTENT_FULLY_DISCONNECTED=ON -DFETCHCONTENT_SOURCE_DIR_SPDLOG=/path/to/spdlog ..\n\n# Development build with wheels enabled\ncmake -DZSWAG_BUILD_WHEELS=ON -DZSWAG_ENABLE_TESTING=ON ..\n```\n\n#### Code Coverage\n\n[![codecov](https://codecov.io/gh/ndsev/zswag/branch/master/graph/badge.svg)](https://codecov.io/gh/ndsev/zswag)\n\nzswag includes comprehensive C++ test coverage analysis for the `httpcl` and `zswagcl` libraries. Coverage is automatically collected in CI and reported to [Codecov](https://codecov.io/gh/ndsev/zswag).\n\n**📊 [View HTML Coverage Report](https://ndsev.github.io/zswag/coverage/)** - Browsable coverage report hosted on GitHub Pages\n\n**Local Coverage Analysis**\n\nTo generate coverage reports locally, you'll need:\n- GCC or Clang compiler\n- `lcov` and `genhtml` tools (install with `sudo apt-get install lcov` on Ubuntu/Debian)\n\nBuild with coverage enabled:\n```bash\nmkdir build \u0026\u0026 cd build\ncmake -DCMAKE_BUILD_TYPE=Debug \\\n      -DZSWAG_ENABLE_COVERAGE=ON \\\n      -DZSWAG_ENABLE_TESTING=ON \\\n      -DZSWAG_BUILD_WHEELS=OFF \\\n      -DZSWAG_KEYCHAIN_SUPPORT=OFF ..\ncmake --build .\n```\n\n**Note:** The flags `-DZSWAG_BUILD_WHEELS=OFF` and `-DZSWAG_KEYCHAIN_SUPPORT=OFF` disable features that aren't needed for coverage analysis and avoid requiring Python development headers or system keychain libraries.\n\nGenerate coverage reports:\n```bash\n# Run tests to generate coverage data\nctest --output-on-failure\n\n# Generate HTML coverage report\ncmake --build . --target coverage-report\n```\n\nThe HTML coverage report will be available at `build/coverage/html/index.html`.\n\n**Available Coverage Targets:**\n- `coverage-clean` - Remove all coverage data\n- `coverage-report` - Generate coverage report from existing test runs\n- `coverage` - Clean, run tests, and generate report (all-in-one)\n\n**Coverage Configuration:**\n- **Goal:** 70%+ line coverage (initial), near 100% line and branch coverage (ultimate)\n- **Scope:** Coverage is tracked only for library source files (`libs/httpcl`, `libs/zswagcl`)\n- **Not included:** Test code, dependencies, generated code (zserio)\n\n**Troubleshooting Coverage Builds:**\n\nIf you get \"gcov not found\" warnings:\n```bash\n# Check if versioned gcov exists\nwhich gcov-13 gcov-12 gcov-11\n\n# Create symlink (example for gcov-13)\nsudo ln -s /usr/bin/gcov-13 /usr/bin/gcov\n\n# Or install gcc package\nsudo apt-get install gcc\n```\n\nIf you want to build coverage **with** wheel support (requires Python development headers):\n```bash\ncmake -DCMAKE_BUILD_TYPE=Debug \\\n      -DZSWAG_ENABLE_COVERAGE=ON \\\n      -DZSWAG_ENABLE_TESTING=ON \\\n      -DZSWAG_BUILD_WHEELS=ON \\\n      -DZSWAG_KEYCHAIN_SUPPORT=OFF ..\n```\nNote: This requires `python3-dev` package (Ubuntu/Debian) or equivalent on your system.\n\n## CI/CD and Release Process\n\n### Continuous Integration\n\nThe project uses GitHub Actions for automated building and deployment:\n\n- **Platforms**: Linux (x86_64), macOS (Intel x86_64 and Apple Silicon arm64), Windows (x64)\n- **Python versions**: 3.10, 3.11, 3.12, 3.13\n- **Triggers**: Pull requests, pushes to main branch, and version tags\n\n### Release Process\n\nReleases are automated through the CI/CD pipeline:\n\n1. **Update version**: Modify `ZSWAG_VERSION` in `CMakeLists.txt`\n2. **Create release tag**: Tag the commit with `v{version}` (e.g., `v1.7.2`)\n3. **Automatic deployment**: The CI pipeline will:\n   - Validate that the tag version matches the CMake version\n   - Build wheels for all supported platforms\n   - Deploy to PyPI automatically\n\n### Development Snapshots\n\nPushes to the main branch automatically create development releases:\n- Version format: `{base_version}.dev{commit_count}` (e.g., `1.7.2.dev3`)\n- Automatically deployed to PyPI for testing\n\n### Version Validation\n\nThe build process ensures version consistency:\n- Git tags must match the version in `CMakeLists.txt`\n- Mismatched versions will cause the build to fail\n- This prevents accidental deployment of incorrect versions\n\n## OpenAPI Generator CLI\n\nAfter installing `zswag` via pip as [described above](#for-python-users),\nyou can run `python -m zswag.gen`, a CLI to generate OpenAPI YAML files.\nThe CLI offers the following options\n\n```\nusage: Zserio OpenApi YAML Generator [-h] -s service-identifier -i\n                                     zserio-or-python-path\n                                     [-r zserio-src-root-dir]\n                                     [-p top-level-package] [-c tags [tags ...]]\n                                     [-o output] [-b BASE_CONFIG_YAML]\n\noptional arguments:\n  -h, --help\n        show this help message and exit\n  -s service-identifier, --service service-identifier\n\n        Fully qualified zserio service identifier.\n\n        Example:\n            -s my.package.ServiceClass\n\n  -i zserio-or-python-path, --input zserio-or-python-path\n\n        Can be either ...\n        (A) Path to a zserio .zs file. Must be either a top-\n            level entrypoint (e.g. all.zs), or a subpackage\n            (e.g. services/myservice.zs) in conjunction with\n            a \"--zserio-source-root|-r \u003cdir\u003e\" argument.\n        (B) Path to parent dir of a zserio Python package.\n\n        Examples:\n            -i path/to/schema/main.zs         (A)\n            -i path/to/python/package/parent  (B)\n\n  -r zserio-src-root-dir, --zserio-source-root zserio-src-root-dir\n\n        When -i specifies a zs file (Option A), indicate the\n        directory for the zserio -src directory argument. If\n        not specified, the parent directory of the zs file\n        will be used.\n\n  -p top-level-package, --package top-level-package\n\n        When -i specifies a zs file (Option A), indicate\n        that a specific top-level zserio package name\n        should be used.\n\n        Examples:\n            -p zserio_pkg_name\n\n  -c tags [tags ...], --config tags [tags ...]\n\n        Configuration tags for a specific or all methods.\n        The argument syntax follows this pattern:\n\n           [(service-method-name):](comma-separated-tags)\n\n        Note: The -c argument may be applied multiple times.\n        The `comma-separated-tags` must be a list of tags\n        which indicate OpenApi method generator preferences.\n        The following tags are supported:\n\n        get|put|post|delete : HTTP method tags\n                query|path| : Parameter location tags\n                header|body\n                  flat|blob : Flatten request object,\n                              or pass it as whole blob.\n          (param-specifier) : Specify parameter name, format\n                              and location for a specific\n                              request-part. See below.\n            security=(name) : Set a particular security\n                              scheme to be used. The scheme\n                              details must be provided through\n                              the --base-config-yaml.\n         path=(method-path) : Set a particular method path.\n                              May contain placeholders for\n                              path params.\n\n        A (param-specifier) tag has the following schema:\n\n            (field?name=...\n                  \u0026in=[path|body|query|header]\n                  \u0026format=[binary|base64|hex]\n                  [\u0026style=...]\n                  [\u0026explode=...])\n\n        Examples:\n\n          Expose all methods as POST, but `getLayerByTileId`\n          as GET with flat path-parameters:\n\n            `-c post getLayerByTileId:get,flat,path`\n\n          For myMethod, put the whole request blob into the a\n          query \"data\" parameter as base64:\n\n            `-c myMethod:*?name=data\u0026in=query\u0026format=base64`\n\n          For myMethod, set the \"AwesomeAuth\" auth scheme:\n\n            `-c myMethod:security=AwesomeAuth`\n\n          For myMethod, provide the path and place myField\n          explicitely in a path placeholder:\n\n            `-c 'myMethod:path=/my-method/{param},...\n                 myField?name=param\u0026in=path\u0026format=string'`\n\n        Note:\n            * The HTTP-method defaults to `post`.\n            * The parameter 'in' defaults to `query` for\n              `get`, `body` otherwise.\n            * If a method uses a parameter specifier, the\n              `flat`, `body`, `query`, `path`, `header` and\n              `body`-tags are ignored.\n            * The `flat` tag is only meaningful in conjunction\n              with `query` or `path`.\n            * An unspecific tag list (no service-method-name)\n              affects the defaults only for following, not\n              preceding specialized tag assignments.\n\n  -o output, --output output\n\n        Output file path. If not specified, the output will be\n        written to stdout.\n\n  -b BASE_CONFIG_YAML, --base-config-yaml BASE_CONFIG_YAML\n\n        Base configuration file. Can be used to fully or partially\n        substitute --config arguments, and to provide additional\n        OpenAPI information. The YAML file must look like this:\n\n          method: # Optional method tags dictionary\n            \u003cmethod-name|*\u003e: \u003clist of config tags\u003e\n          securitySchemes: ... # Optional OpenAPI securitySchemes\n          info: ...            # Optional OpenAPI info section\n          servers: ...         # Optional OpenAPI servers section\n          security: ...        # Optional OpenAPI global security\n```\n\n### Generator Usage example\n\nLet's consider the following zserio service saved under `myapp/services.zs`:\n\n```\npackage services;\n\nstruct Request {\n  int32 value;\n};\n\nstruct Response {\n  int32 value;\n};\n\nservice MyService {\n  Response myApi(Request);\n};\n```\n\nAn OpenAPI file `api.yaml` for `MyService` can now be\ncreated with the following `zswag.gen` invocation:\n\n```bash\ncd myapp\npython -m zswag.gen -s services.MyService -i services.zs -o api.yaml\n```\n\nYou can further customize the generation using `-c` configuration\narguments. For example, `-c get,flat,path` will recursively \"flatten\"\nthe zserio request object into it's compound scalar fields using\n[x-zserio-request-part](#url-scalar-parameter) for all methods.\nIf you want to change OpenAPI parameters only for one particular\nmethod, you can prefix the tag config argument with the method\nname (`-c methodName:tags...`).\n\n### Documentation Extraction\n\nWhen invoking `zswag.gen` with `-i zserio-file` an attempt\nwill be made to populate the service/method/request/response\ndescriptions with doc-strings that are extracted from the zserio\nsources.\n\nFor structs and services, the documentation is expected to be\nenclosed by `/*! .... !*/` markers preceding the declaration:\n\n```C\n/*!\n### My Markdown Struct Doc\nI choose to __highlight__ this word.\n!*/\n\nstruct MyStruct {\n    ...\n};\n```\n\nFor service methods, a single-line doc-string is parsed which\nimmediately precedes the declaration:\n\n```C\n/** This method is documented. */\nReturnType myMethod(ArgumentType);\n```\n\n## Server Component\n\nThe `OAServer` component gives you the power to marry a zserio-generated app\nserver class with a user-written app controller and a fitting OpenAPI specification.\nIt is based on [Flask](https://flask.palletsprojects.com/en/1.1.x/) and\n[Connexion](https://connexion.readthedocs.io/en/latest/).\n\n**Implementation choice regarding HTTP response codes:** The server as implemented\nhere will return HTTP code `400` (Bad Request) when the user request could not\nbe parsed, and `500` (Internal Server Error) when a different exception occurred while\ngenerating the response/running the user's controller implementation.\n\n### Integration Example\n\nWe consider the same `myapp` directory with a `services.zs` zserio file\nas already used in the [OpenAPI Generator Example](#generator-usage-example).\n\n**Note:**\n\n* `myapp` must be available as a module (it must be\npossible to `import myapp`). \n* We recommend to run the zserio Python generator invocation\n  inside the `myapp` module's `__init__.py`, like this:\n\n```py\nimport zserio\nfrom os.path import dirname, abspath\n\nworking_dir = dirname(abspath(__file__))\nzserio.generate(\n  zs_dir=working_dir,\n  main_zs_file=\"services.zs\",\n  gen_dir=working_dir)\n```\n\nA server script like `myapp/server.py` might then look as follows:\n\n```py\nimport zswag\nimport myapp.controller as controller\nfrom myapp import working_dir\n\n# This import only works after zserio generation.\nimport services.api as services\n\napp = zswag.OAServer(\n  controller_module=controller,\n  service_type=services.MyService.Service,\n  yaml_path=working_dir+\"/api.yaml\",\n  zs_pkg_path=working_dir)\n\nif __name__ == \"__main__\":\n    app.run()\n```\n\nThe server script above references two important components:\n* An **OpenAPI file** (`myapp/api.yaml`): Upon startup, `OAServer`\n  will output an error message if this file does not exist. The\n  error message already contains the correct command to\n  invoke the [OpenAPI Generator CLI](#openapi-generator-cli)\n  to generate `myapp/api.yaml`.\n* A **controller module** (`myapp/controller.py`): This file provides\n  the actual implementations for your service endpoints.\n  \nFor the current example, `controller.py` might look as follows:\n\n```py\nimport services.api as services\n\n# Written by you\ndef my_api(request: services.Request):\n    return services.Response(request.value * 42)\n```\n\n## Using the Python Client\n\nThe generic Python client talks to any zserio service that is running\nvia HTTP/REST, and provides an OpenAPI specification of it's interface.\n\n### Integration Example\n\nAs an example, consider a Python module called `myapp` which has the\nsame `myapp/__init__.py` and `myapp/services.zs` zserio definition as\n[previously mentioned](#generator-usage-example). We consider\nthat the server is providing its OpenAPI spec under `localhost:5000/openapi.json`.\n\nIn this setting, a client `myapp/client.py` might look as follows:\n\n```python\nfrom zswag import OAClient\nimport services.api as services\n\nopenapi_url = \"http://localhost:5000/openapi.json\"\n\n# The client reads per-method HTTP details from the OpenAPI URL.\n# You can also pass a local file by setting the `is_local_file` argument\n# of the OAClient constructor.\nclient = services.MyService.Client(OAClient(openapi_url))\n\n# This will trigger an HTTP request under the hood.\nclient.my_api(services.Request(1))\n```\n\nAs you can see, an instance of `OAClient` is passed into the constructor\nfor zserio to use as the service client's transport implementation.\n\n**Note:** While connecting, the client will also use ...\n1. [Persistent HTTP configuration](#persistent-http-headers-proxy-cookie-and-authentication).\n2. Additional HTTP query/header/cookie/proxy/basic-auth configs passed\n   into the `OAClient` constructor using an instance of `zswag.HTTPConfig`.\n   For example:\n   \n   ```python\n   from zswag import OAClient, HTTPConfig\n   import services.api as services\n   config = HTTPConfig() \\\n       .header(key=\"X-My-Header\", val=\"value\") \\  # Can be specified \n       .cookie(key=\"MyCookie\", val=\"value\")    \\  # multiple times.\n       .query(key=\"MyCookie\", val=\"value\")     \\  # \n       .proxy(host=\"localhost\", port=5050, user=\"john\", pw=\"doe\") \\\n       .basic_auth(user=\"john\", pw=\"doe\") \\\n       .bearer(\"bearer-token\") \\\n       .api_key(\"token\")\n   \n   client = services.MyService.Client(\n       OAClient(\"http://localhost:8080/openapi.\", config=config))\n   \n   # Alternative when specifying api-key or bearer\n   client = services.MyService.Client(\n       OAClient(\"http://localhost:8080/openapi.\", api_key=\"token\", bearer=\"token\"))\n   ```\n   \n   **Note:** The additional `config` will only enrich, not overwrite the\n   default persistent configuration. If you would like to prevent persistent\n   config from being considered at all, set `HTTP_SETTINGS_FILE` to empty,\n   e.g. via `os.environ['HTTP_SETTINGS_FILE']=''`\n\n## C++ Client\n\nThe generic C++ client talks to any zserio service that is running\nvia HTTP/REST, and provides an OpenAPI specification of its interface.\nWhen using the C++ `OAClient` with your zserio schema, make sure\nthat the flags [`-withTypeInfoCode` and `-withReflectionCode`](http://zserio.org/doc/ZserioUserGuide.html#zserio-command-line-interface) are passed to the zserio C++ emitter.\n\n### Integration Example\n\nAs an example, we consider the `myapp` directory which contains a `services.zs`\nzserio definition as [previously mentioned](#generator-usage-example).\n\nWe assume that zswag is added to `myapp` as a [Git submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules)\nunder `myapp/zswag`.\n\nNext to `myapp/services.zs`, we place a `myapp/CMakeLists.txt` which describes our project:\n\n```cmake\nproject(myapp)\n\n# If you are not interested in building zswag Python\n# wheels, you can set the following option:\n# set(ZSWAG_BUILD_WHEELS OFF)\n\n# If your compilation environment does not provide\n# libsecret, the following switch will disable keychain integration:\n# set(ZSWAG_KEYCHAIN_SUPPORT OFF)\n\n# Optional: For offline/disconnected builds, you can\n# predefine dependency sources using FETCHCONTENT_SOURCE_DIR_*\n# variables (see README offline builds section for details)\n\n# This is how C++ will know about the zswag lib\n# and its dependencies, such as zserio.\nif (NOT TARGET zswag)\n        FetchContent_Declare(zswag\n                GIT_REPOSITORY \"https://github.com/ndsev/zswag.git\"\n                GIT_TAG        \"v1.6.7\"\n                GIT_SHALLOW    ON)\n        FetchContent_MakeAvailable(zswag)\nendif()\n\nfind_package(OpenSSL CONFIG REQUIRED)\ntarget_link_libraries(httplib INTERFACE OpenSSL::SSL)\n\n# This command is provided by zswag to easily create\n# a CMake C++ reflection library from zserio code.\nadd_zserio_library(${PROJECT_NAME}-zserio-cpp\n  WITH_REFLECTION\n  ROOT \"${CMAKE_CURRENT_SOURCE_DIR}\"\n  ENTRY services.zs\n  TOP_LEVEL_PKG myapp_services)\n\n# We create a myapp client executable which links to\n# the generated zserio C++ library and the zswag client\n# library.\nadd_executable(${PROJECT_NAME} client.cpp)\n\n# Make sure to link to the `zswagcl` target\ntarget_link_libraries(${PROJECT_NAME}\n    ${PROJECT_NAME}-zserio-cpp zswagcl)\n```\n\n**Note:** OpenSSL is assumed to be installed or built using the `lib` (not `lib64`) directory name.\n\nThe `add_executable` command above references the file `myapp/client.cpp`,\nwhich contains the code to actually use the zswag C++ client.\n\n```cpp\n#include \"zswagcl/oaclient.hpp\"\n#include \u003ciostream\u003e\n#include \"myapp_services/services/MyService.h\"\n\nusing namespace zswagcl;\nusing namespace httpcl;\nnamespace MyService = myapp_services::services::MyService;\n\nint main (int argc, char* argv[])\n{\n    // Assume that the server provides its OpenAPI definition here\n    auto openApiUrl = \"http://localhost:5000/openapi.json\";\n    \n    // Create an HTTP client to be used by our OpenAPI client\n    auto httpClient = std::make_unique\u003cHttpLibHttpClient\u003e();\n    \n    // Fetch the OpenAPI configuration using the HTTP client\n    auto openApiConfig = fetchOpenAPIConfig(openApiUrl, *httpClient);\n    \n    // Create a Zserio reflection-based OpenAPI client that\n    // uses the OpenAPI configuration we just retrieved.\n    auto openApiClient = OAClient(openApiConfig, std::move(httpClient));\n        \n    // Create a MyService client based on the OpenApi-Client\n    // implementation of the zserio::IServiceClient interface.\n    auto myServiceClient = MyService::Client(openApiClient);\n    \n    // Create the request object\n    auto request = myapp_services::services::Request(2);\n\n    // Invoke the REST endpoint. Mind that your method-\n    // name from the schema is appended with a \"...Method\" suffix.\n    auto response = myServiceClient.myApiMethod(request);\n    \n    // Print the response\n    std::cout \u003c\u003c \"Got \" \u003c\u003c response.getValue() \u003c\u003c std::endl;\n}\n```\n\n**Note:** While connecting, `HttpLibHttpClient` will also use ...\n1. [Persistent HTTP configuration](#persistent-http-headers-proxy-cookie-and-authentication).\n2. Additional HTTP query/header/cookie/proxy/basic-auth configs passed\n   into the `OAClient` constructor using an instance of `httpcl::Config`.\n   You can include this class via `#include \"httpcl/http-settings.hpp\"`.\n   The additional `Config` will only enrich, not overwrite the\n   default persistent configuration. If you would like to prevent persistent\n   config from being considered at all, set `HTTP_SETTINGS_FILE` to empty,\n   e.g. via `setenv`.\n\n## Client Environment Settings\n\nBoth the Python and C++ Clients can be configured using the following\nenvironment variables:\n\n\u003c!-- --8\u003c-- [start:env] --\u003e\n\n| Variable Name | Details   |\n| ------------- | --------- |\n| `HTTP_SETTINGS_FILE` | Path to settings file for HTTP proxies and authentication, see [next section](#persistent-http-headers-proxy-cookie-and-authentication) |\n| `HTTP_LOG_LEVEL` | Verbosity level for console/log output. Set to `debug` for detailed output. |\n| `HTTP_LOG_FILE` | Logfile-path (including filename) to redirect console output. The log will rotate with three files (`HTTP_LOG_FILE`, `HTTP_LOG_FILE-1`, `HTTP_LOG_FILE-2`). |\n| `HTTP_LOG_FILE_MAXSIZE` | Maximum size of the logfile, in bytes. Defaults to 1GB. |\n| `HTTP_TIMEOUT` | Timeout for HTTP requests (connection+transfer) in seconds. Defaults to 60s. |\n| `HTTP_SSL_STRICT` | Set to any nonempty value for strict SSL certificate validation. |\n\n\u003c!-- --8\u003c-- [end:env] --\u003e\n\n## Persistent HTTP Headers, Proxy, Cookie and Authentication\n\nBoth the Python `OAClient` and C++ `HttpLibHttpClient` read a YAML file\nstored under a path which is given by the `HTTP_SETTINGS_FILE` environment\nvariable.\n\n\u003c!-- --8\u003c-- [start:settings] --\u003e\n\n### HTTP Settings File Format \n\nThe YAML file contains a list of HTTP-related configs that are\napplied to HTTP requests based on a regular expression which is matched\nagainst the requested URL.\n\nFor example, the following entry would match all requests due to the `*`\nurl-match-pattern for the `scope` field:\n\n```yaml\nhttp-settings:\n  # Under http-settings, a list of settings is defined for specific URL scopes.\n  - scope: *     # URL scope - e.g. https://*.nds.live/* or *.google.com.\n    basic-auth:  # Basic auth credentials for matching requests.\n      user: johndoe\n      keychain: keychain-service-string\n      password: cleartext-password  # alternative to keychain\n    proxy:      # Proxy settings for matching requests.\n      host: localhost\n      port: 8888\n      user: test\n      keychain: ...\n      password: cleartext-password  # alternative to keychain\n    cookies:    # Additional Cookies for matching requests.\n      key: value\n    headers:    # Additional Headers for matching requests.\n      key: value\n    query:      # Additional Query parameters for matching requests.\n      key: value\n    api-key: value  # API Key as required by OpenAPI config - see description below.\n    oauth2:\n      # REQUIRED fields\n      clientId: my-client-id                               # REQUIRED: OAuth2 client identifier\n\n      # REQUIRED if useForSpecFetch=true (default), OPTIONAL otherwise\n      tokenUrl: https://issuer.example.com/oauth/token     # Token endpoint URL (see precedence rules below)\n\n      # Client secret (choose one method)\n      clientSecretKeychain: keychain-service-string        # RECOMMENDED: Load secret from OS keychain\n      clientSecret: cleartext-secret                       # DISCOURAGED: Cleartext secret (use keychain instead)\n\n      # OPTIONAL fields (with defaults/precedence)\n      refreshUrl: https://issuer.example.com/oauth/token   # Optional override; defaults to refreshUrl from OpenAPI, then tokenUrl\n      audience: https://api.example.com/                   # Optional: audience parameter (required by some providers)\n      scope: [\"orders.read\", ...]                          # Optional: scope override; defaults to OpenAPI spec's per-operation scopes\n      useForSpecFetch: true                                # Optional: acquire token before fetching OpenAPI spec (default: true)\n      tokenEndpointAuth:                                   # Optional: token endpoint authentication method\n        method: rfc6749-client-secret-basic                # Options: rfc6749-client-secret-basic (default), rfc5849-oauth1-signature\n        nonceLength: 16                                    # For rfc5849-oauth1-signature: nonce length (8-64, default: 16)\n```\n\n**Note:** For `proxy` configs, the credentials are optional.\n\n### OAuth2 Configuration: Required vs Optional Fields\n\n**Important:** Zswag only supports the **OAuth2 `clientCredentials` flow**. Other flows (`authorizationCode`, `implicit`, `password`) are not supported.\n\n**Field Requirements:**\n\n| Field | Required? | Notes |\n|-------|-----------|-------|\n| `clientId` | ✅ Always | OAuth2 client identifier |\n| `tokenUrl` | ⚠️ Conditional | **REQUIRED** when `useForSpecFetch: true` (default)\u003cbr\u003e**OPTIONAL** when `useForSpecFetch: false` (defaults to OpenAPI spec) |\n| `clientSecret` / `clientSecretKeychain` | ⚠️ Conditional | **REQUIRED** for confidential clients\u003cbr\u003e**OPTIONAL** for public clients (if omitted, client_id is sent in request body) |\n| `refreshUrl` | ❌ Optional | Defaults to `refreshUrl` from OpenAPI spec, then `tokenUrl` |\n| `scope` | ❌ Optional | Defaults to scopes from OpenAPI spec's per-operation `security` requirements |\n| `audience` | ❌ Optional | Only required by some OAuth2 providers |\n| `useForSpecFetch` | ❌ Optional | Default: `true` (acquire token before fetching OpenAPI spec) |\n| `tokenEndpointAuth` | ❌ Optional | Default: `rfc6749-client-secret-basic` |\n\n**Precedence Rules (http-settings.yaml vs OpenAPI spec):**\n\nWhen both http-settings.yaml and the OpenAPI specification provide values, the following precedence applies:\n\n1. **`tokenUrl`**: http-settings.yaml `tokenUrl` **overrides** OpenAPI spec's `flows.clientCredentials.tokenUrl`\n2. **`refreshUrl`**: http-settings.yaml `refreshUrl` **overrides** OpenAPI spec's `flows.clientCredentials.refreshUrl`\n3. **`scope`**: http-settings.yaml `scope` **overrides** OpenAPI spec's per-operation `security` scopes\n\n**Common Scenarios:**\n\n| Scenario | `useForSpecFetch` | `tokenUrl` in http-settings | `tokenUrl` in OpenAPI spec | Result |\n|----------|-------------------|----------------------------|---------------------------|--------|\n| **Protected OpenAPI spec** | `true` (default) | ✅ Required | Used as fallback | http-settings value used |\n| **Public OpenAPI spec** | `false` | ❌ Optional | ✅ Required in spec | OpenAPI spec value used |\n| **Override spec settings** | `true` or `false` | ✅ Provided | Any | http-settings value **always wins** |\n\n### OAuth2 Token Endpoint Authentication Methods\n\nThe `tokenEndpointAuth` field controls how the client authenticates when requesting OAuth2 tokens. Two methods are supported:\n\n**`rfc6749-client-secret-basic` (default):** HTTP Basic Authentication (RFC 6749 Section 2.3.1)\n- Both `clientId` and `clientSecret` are sent in the `Authorization: Basic` header\n- Works with most OAuth2 providers\n\n**`rfc5849-oauth1-signature`:** OAuth 1.0 HMAC-SHA256 request signing (RFC 5849)\n- `clientId` is sent as `oauth_consumer_key` in both header and body\n- `clientSecret` is used only for HMAC-SHA256 signature computation (never transmitted)\n- Required by some providers that use OAuth 1.0 signature-based token authentication\n- Provides enhanced security through cryptographic request signing\n- **Note:** Only HMAC-SHA256 signature method is supported\n\n### OAuth2-Authenticated OpenAPI Spec Fetching\n\nBy default, when OAuth2 is configured, zswag will acquire an OAuth2 access token **before** fetching the OpenAPI specification. This solves the \"chicken-and-egg\" problem where the OpenAPI spec endpoint itself requires authentication.\n\n**How it works:**\n\n1. **With `useForSpecFetch: true` (default):**\n   - Client acquires OAuth2 token using configured authentication method\n   - Token is included as `Authorization: Bearer \u003ctoken\u003e` header when fetching OpenAPI spec\n   - OpenAPI spec fetch succeeds even if endpoint requires authentication\n   - Same token is cached and reused for subsequent API calls\n\n2. **With `useForSpecFetch: false`:**\n   - OpenAPI spec is fetched without OAuth2 token (plain HTTP GET)\n   - OAuth2 token acquisition is deferred until first API method call\n   - Use this when the OpenAPI spec endpoint is public (doesn't require authentication)\n\n**Configuration:**\n\n```yaml\n- scope: https://api.example.com/*\n  oauth2:\n    clientId: your-client-id\n    clientSecret: your-client-secret\n    tokenUrl: https://api.example.com/oauth/token\n    useForSpecFetch: true  # Default: true. Set to false if spec is public.\n```\n\n**When to use `useForSpecFetch: false`:**\n- OpenAPI spec endpoint is publicly accessible\n- Avoids unnecessary token acquisition if only the spec is needed\n- Improves performance by deferring OAuth2 flow until first API call\n\n**When to keep `useForSpecFetch: true` (default):**\n- OpenAPI spec endpoint requires authentication\n- Service returns 401/403 when fetching spec without credentials\n- Most secure option (ensures token is available from the start)\n\n**Debugging OAuth2 Issues:**\n\nTo troubleshoot OAuth2 authentication problems, enable detailed logging:\n\n```bash\nexport HTTP_LOG_LEVEL=debug   # Shows OAuth2 flow (token acquisition, cache hits/misses)\nexport HTTP_LOG_LEVEL=trace   # Shows additional details (request/response bodies, signatures)\n```\n\nThe logs will show:\n- Token endpoint authentication method being used\n- Token request/response status\n- Token cache hit/miss/expired events\n- OAuth2 configuration status for OpenAPI spec fetch\n- Whether OAuth2 token is being used for spec fetch\n\n### Testing OAuth 1.0 Signature with Your Service\n\nTo verify OAuth 1.0 signature authentication with your service:\n\n**1. Install zswag:**\n\nFor official releases:\n```bash\npip install zswag\n```\n\nFor custom builds or development snapshots:\n```bash\npip install /path/to/pyzswagcl-*.whl /path/to/zswag-*.whl\n```\n\n**2. Create `http-settings.yaml`** with your service details:\n```yaml\n- scope: https://your-api.example.com/*\n  oauth2:\n    clientId: your-client-id\n    clientSecret: your-client-secret\n    tokenUrl: https://your-api.example.com/oauth/token\n    tokenEndpointAuth:\n      method: rfc5849-oauth1-signature\n      nonceLength: 16\n```\n\n**3. Create a test script** (`test_oauth1.py`):\n```python\nimport os\nfrom zswag import OAClient\n\n# Point to your http-settings file\nos.environ['HTTP_SETTINGS_FILE'] = '/path/to/http-settings.yaml'\n\n# Create client with your OpenAPI spec\nclient = OAClient(\"https://your-api.example.com/openapi.json\")\n\n# Import your generated zserio service\nimport your_service.api as api\n\n# Create service client and make a test call\nservice = api.YourService.Client(client)\nrequest = api.YourRequest(...)\nresponse = service.your_method(request)\n\nprint(f\"Success! Response: {response}\")\n```\n\n**4. Run the test:**\n```bash\npython test_oauth1.py\n```\n\n**Verification:** Check your server logs to confirm OAuth 1.0 HMAC-SHA256 signatures are being validated correctly and the token endpoint receives properly signed requests.\n\nThe **`api-key`** setting will be applied under the correct\ncookie/header/query parameter, if the service\nyou are connecting to uses an [OpenAPI `apiKey` auth scheme](#authentication-schemes).\n\nPasswords can be stored in clear text by setting a `password` field instead\nof the `keychain` field. Keychain entries can be made with different tools\non each platform:\n\n* [Linux `secret-tool`](https://www.marian-dan.ro/blog/storing-secrets-using-secret-tool) \n* [macOS `add-generic-password`](https://www.netmeister.org/blog/keychain-passwords.html)\n* [Windows `cmdkey`](https://www.scriptinglibrary.com/languages/powershell/how-to-manage-secrets-and-passwords-with-credentialmanager-and-powershell/)\n\n\u003c!-- --8\u003c-- [end:settings] --\u003e\n\n## Client Result Code Handling\n\nBoth clients (Python and C++) will treat any HTTP response code other than `200` as an error since zserio services are expected to return a parsable response object. The client will throw an exception with a descriptive message if the response code is not `200`.\n\nIn case applications want to utilize for example the `204 (No Content)` response code, they have to catch the exception and handle it accordingly.\n\n## Swagger User Interface \n\nIf you have installed `pip install \"connexion[swagger-ui]\"`, you can view\nAPI docs of your service under `[/prefix]/ui`.\n\n## OpenAPI Options Interoperability\n\nThe Server, Clients and Generator offer various degrees of freedom\nregarding the OpenAPI YAML file. The following sections detail which\ncomponents support which aspects of OpenAPI. The difference in compliance\nis mostly due to limited development scopes. If you are missing a particular\nOpenAPI feature for a particular component, feel free to create an issue!\n\n**Note:** For all options that are not supported by `zswag.gen`, you\nwill need to manually edit the OpenAPI YAML file to achieve the desired\nconfiguration. You will also need to edit the file manually to fill in\nmeta-info (provider name, service version, etc.).\n\n### HTTP method\n\nTo change the **HTTP method**, the desired method name is placed \nas the key under the method path, such as in the following example:\n```yaml\npaths:\n  /methodName:\n    {get|post|put|delete}:\n      ...\n```\n\n#### Component Support\n\n| Feature            | C++ Client | Python Client | OAServer | zswag.gen |\n| ------------------ | ---------- | ------------- | -------- | --------- |\n| `get` `post` `put` `delete` | ✔️ | ✔️ | ✔️ | ✔️ |\n| `patch` | ❌️ | ❌️ | ❌️ | ❌️ |\n\n**Note:** Patch is unsupported, because the required semantics of\na partial object update cannot be realized in the zserio transport\nlayer interface.\n\n### Request Body\n\nA server can instruct clients to transmit their zserio request object in the\nrequest body when using HTTP `post`, `put` or `delete`.\nThis is done by setting the OpenAPI `requestBody/content` to\n`application/x-zserio-object`:\n\n```yaml\nrequestBody:\n  content:\n    application/x-zserio-object:\n      schema:\n        type: string\n```\n\n#### Component Support\n\n| Feature            | C++ Client | Python Client | OAServer | zswag.gen |\n| ------------------ | ---------- | ------------- | -------- | --------- |\n| `application/x-zserio-object` | ✔️ | ✔️ | ✔️ | ✔️ |\n\n### URL Blob Parameter\n\nZswag tools support an additional OpenAPI method parameter\nfield called `x-zserio-request-part`. Through this field,\na service provider can express that a certain request parameter\nonly contains a part of, or the whole zserio request object.\nWhen parameter contains the whole request object, `x-zserio-request-part`\nshould be set to an asterisk (`*`):\n\n```yaml\nparameters:\n- description: ''\n  in: query|path|header\n  name: parameterName\n  required: true\n  x-zserio-request-part: \"*\"\n  schema:\n    format: string|byte|base64|base64url|hex|binary\n```\n\nAbout the `format` specifier value:\n* Both `string` and `binary` result in a raw URL-encoded string buffer.\n* Both `byte` and `base64` result in a standard Base64-encoded value.\n  The `base64url` option indicates URL-safe Base64 format.\n* The `hex` encoding produces a hexadecimal encoding of the request blob.\n\n**Note:** When a parameter is passed with `in=path`, its value\n**must not be empty**. This holds true for strings and bytes,\nbut also for arrays (see below).\n\n#### Component Support\n\n| Feature            | C++ Client | Python Client | OAServer | zswag.gen |\n| ------------------ | ---------- | ------------- | -------- | --------- |\n| `x-zserio-request-part: *` | ✔️ | ✔️ | ✔️ | ✔️ |\n| `format: string` | ✔️ | ✔️ | ✔️ | ✔️ |\n| `format: byte` | ✔️ | ✔️ | ✔️ | ✔️ |\n| `format: hex` | ✔️ | ✔️ | ✔️ | ✔️ |\n\n### URL Scalar Parameter\n\nUsing `x-zserio-request-part`, it is also possible to transfer\nonly a single scalar (nested) member of the request object:\n\n```yaml\nparameters:\n- description: ''\n  in: query|path|header\n  name: parameterName\n  required: true\n  x-zserio-request-part: \"[parent.]*member\"\n  schema:\n    format: string|byte|base64|base64url|hex|binary\n```\n\nIn this case, `x-zserio-request-part` should point to a scalar type,\nsuch as `uint8`, `float32`, `string` etc.\n\nThe `format` value effect remains as explained above. A small\ndifference exists for integer types: Their hexadecimal representation\nwill be the natural numeric one, not binary.\n\n#### Component Support\n\n| Feature            | C++ Client | Python Client | OAServer | zswag.gen |\n| ------------------ | ---------- | ------------- | -------- | --------- |\n| `x-zserio-request-part: \u003c[parent.]*member\u003e` | ✔️ | ✔️ | ✔️ | ✔️ |\n\n### URL Array Parameter\n\nThe `x-zserio-request-part` may also point to an array member of\nthe zserio request struct, like so:\n\n```yaml\nparameters:\n- description: ''\n  in: query|path|header\n  style: form|simple|label|matrix\n  explode: true|false\n  name: parameterName\n  required: true\n  x-zserio-request-part: \"[parent.]*array_member\"\n  schema:\n    format: string|byte|base64|base64url|hex|binary\n```\n\nIn this case, `x-zserio-request-part` should point to an array of\nscalar types. The array will be encoded according\nto the [format, style and explode](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#parameter-object)\nspecifiers.\n\n| Feature            | C++ Client | Python Client | OAServer | zswag.gen |\n| ------------------ | ---------- | ------------- | -------- | --------- |\n| `x-zserio-request-part: \u003c[parent.]*array_member\u003e`  | ✔️ | ✔️ | ✔️ | ✔️ |\n| `style: simple` | ✔️ | ✔️ | ✔️ | ✔️ |\n| `style: form` | ✔️ | ✔️ | ✔️ | ✔️ |\n| `style: label` | ✔️ | ✔️ | ❌ | ✔️ |\n| `style: matrix` | ✔️ | ✔️ | ❌ | ✔️ |\n| `explode: true` | ✔️ | ✔️ | ✔️ | ✔️ |\n| `explode: false` | ✔️ | ✔️ | ✔️ | ✔️ |\n\n### URL Compound Parameter\n\nIn this case, `x-zserio-request-part` points to a zserio compound struct\ninstead of a field with a scalar value. **This is currently not supported.**\n\n#### Component Support\n\n| Feature            | C++ Client | Python Client | OAServer | zswag.gen |\n| ------------------ | ---------- | ------------- | -------- | --------- |\n| `x-zserio-request-part: \u003c[parent.]*compound_member\u003e`  | ❌️ | ❌️ | ❌️ | ❌️ |\n\n### Server URL Base Path\n\nOpenAPI allows for a `servers` field in the spec that lists URL path prefixes\nunder which the specified API may be reached. The OpenAPI clients\nlooks into this list to determine a URL base path from\nthe first entry in this list. A sample entry might look as follows:\n\n```\nservers:\n- http://unused-host-information/path/to/my/api\n``` \n\nThe OpenAPI client will then call methods with your specified host\nand port, but prefix the `/path/to/my/api` string. \n\n#### Component Support\n\n| Feature            | C++ Client | Python Client | OAServer | zswag.gen |\n| ------------------ | ---------- | ------------- | -------- | --------- |\n| `servers`  | ✔️ | ✔️ | ✔️ | ✔️ |\n\n### Authentication Schemes\n\nTo facilitate the communication of authentication needs for the whole or parts\nof a service, OpenAPI allows for `securitySchemes` and `security` fields in the spec.\nPlease refer to the relevant parts of the [OpenAPI 3 specification](https://swagger.io/docs/specification/authentication/) for some\nexamples on how to integrate these fields into your spec.\n\n#### When Security Schemes Are Applied\n\n**Important:** Security schemes (including OAuth2) are **only applied when explicitly declared** in the OpenAPI specification. Zswag clients respect the security requirements defined in the spec according to the [OpenAPI 3.0 Security Requirement specification](https://spec.openapis.org/oas/v3.0.3#security-requirement-object).\n\n**Security Configuration Levels:**\n\n1. **Global Security** - Applied to all endpoints by default (root-level `security` field):\n   ```yaml\n   components:\n     securitySchemes:\n       HeaderAuth:\n         type: apiKey\n         in: header\n         name: X-Generic-Token\n\n   security:\n     - HeaderAuth: []  # Applied to all endpoints by default\n\n   paths:\n     /methodWithGlobalAuth:\n       get:\n         # Uses global HeaderAuth\n   ```\n\n2. **Per-Endpoint Security** - Override global security for specific operations:\n   ```yaml\n   paths:\n     /protected:\n       post:\n         security:\n           - oauth2: [read, write]  # Overrides global security\n     /admin:\n       post:\n         security:\n           - oauth2: [admin]  # Different scopes for admin endpoint\n   ```\n\n3. **No Authentication** - Explicitly disable security for public endpoints:\n   ```yaml\n   paths:\n     /public:\n       get:\n         security: []  # Explicitly no authentication required (overrides global)\n   ```\n\n**Precedence Rule:** Per-operation `security` settings **override** global `security` settings. If an operation specifies its own security requirements (including `security: []`), the global security configuration is ignored for that operation.\n\n**Complete Working Examples:**\n\n- **OAuth2 with per-endpoint security**: See [`libs/zswagcl/test/testdata/oauth2-openapi.yaml`](libs/zswagcl/test/testdata/oauth2-openapi.yaml) which demonstrates different OAuth2 scopes per endpoint and public endpoints without authentication.\n- **Global security with overrides**: See [`libs/zswag/test/calc/api.yaml`](libs/zswag/test/calc/api.yaml) which shows global `HeaderAuth` security with per-endpoint overrides and explicit no-auth declarations.\n\nZswag currently understands the following authentication schemes:\n\n* **HTTP Basic Authorization:** If a called endpoint requires HTTP basic auth,\n  zswag will verify that the HTTP config contains basic-auth credentials.\n  If there are none, zswag will throw a descriptive runtime error.\n* **HTTP Bearer Authorization:** If a called endpoint requires HTTP bearer auth,\n  zswag will verify that the HTTP config contains a header with the\n  key name `Authorization` and the value `Bearer \u003ctoken\u003e`, *case-sensitive*.\n* **API-Key Cookie:** If a called endpoint requires a Cookie API-Key,\n  zswag will either apply [the `api-key` setting](#persistent-http-headers-proxy-cookie-and-authentication), or verify that the\n  HTTP config contains a cookie with the required name, *case-sensitive*.\n* **API-Key Query Parameter:** If a called endpoint requires a Query API-Key,\n  zswag will either apply the `api-key` setting, or verify that the\n  HTTP config contains a query key-value pair with the required name, *case-sensitive*.\n* **API-Key Header:** If a called endpoint requires an API-Key Header,\n  zswag will either apply the `api-key` setting, or verify that the\n  HTTP config contains a header key-value pair with the required name, *case-sensitive*.\n* **OAuth2 Client Credentials:** If a called endpoint requires OAuth2 authentication,\n  zswag will **automatically acquire, cache, and refresh** access tokens from the configured\n  OAuth2 token endpoint. The client handles the entire OAuth2 client credentials flow\n  transparently, including token expiry and refresh. **Note:** Only the `clientCredentials`\n  flow is supported. See the [OAuth2 configuration section](#persistent-http-headers-proxy-cookie-and-authentication)\n  for detailed setup instructions.\n\n**Note**: If you don't want to pass your Basic-Auth/Bearer/Query/Cookie/Header\ncredential through your [persistent config](#persistent-http-headers-proxy-cookie-and-authentication),\nyou can pass a `httpcl::Config`/[`HTTPConfig`](#using-the-python-client) object to the `OAClient`/[`OAClient`](#using-the-python-client).\nconstructor in C++/Python with the relevant detail.\n\n#### Component Support\n\n| Feature                                                                                | C++ Client | Python Client | OAServer | zswag.gen |\n|----------------------------------------------------------------------------------------| ---------- | ------------- | -------- | --------- |\n| `HTTP Basic-Auth` `HTTP Bearer-Auth` `Cookie API-Key` `Header API-Key` `Query API-Key` | ✔️ | ✔️ | ✔️(**) | ✔️ |\n| `OAuth2[clientCredentials]`                                                            | ✔️ | ✔️ | ✔️(**) | ✔️ |\n| `OpenID Connect` `OAuth2[authorizationCode]` `OAuth2[implicit]` `OAuth2[password]`     | ❌️ | ❌️ | ✔️(**) | ❌️ |\n\n**(\\*\\*)**: The server support for all authentication schemes depends on your\nconfiguration of the WSGI server (Apache/Nginx/...) which wraps the zswag Flask app.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fndsev%2Fzswag","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fndsev%2Fzswag","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fndsev%2Fzswag/lists"}