-
Notifications
You must be signed in to change notification settings - Fork 2
API Client Maintenance
This page covers the maintenance, operations, and release processes for the TMI auto-generated API client libraries. For end-user installation and usage, see API-Clients.
The TMI project maintains three auto-generated REST client libraries:
| Client | Package Name | Registry | Generator | Models |
|---|---|---|---|---|
| Python | tmi-client |
PyPI | openapi-generator 7.x | Pydantic v2 |
| TypeScript | @tmiclient/client |
npm | openapi-generator 7.x | TypeScript interfaces |
| Go | tmiclient |
Go modules (git) | swagger-codegen 3.0.75 | Go structs |
All clients are generated from the TMI OpenAPI specification hosted at:
https://raw.githubusercontent.com/ericfitz/tmi/refs/heads/{branch}/api-schema/tmi-openapi.json
Repository: github.com/ericfitz/tmi-clients
tmi-clients/
├── regenerate_python.py # Python client regeneration script
├── regenerate_go.py # Go client regeneration script
├── regenerate_ts.py # TypeScript client regeneration script
├── regen_common.py # Shared utilities for all regeneration scripts
├── release_python.sh # Python release automation script
├── .github/workflows/
│ ├── ci.yml # CI for all three clients
│ ├── publish-python.yml # PyPI publishing (tag: python-v*)
│ ├── publish-go.yml # Go validation (tag: go-v*)
│ └── publish-js.yml # npm publishing (tag: ts-v*)
├── python-client-generated/
│ ├── scripts/openapi-generator-config.json # Codegen configuration
│ ├── tmi_client/ # Package source (Pydantic v2 models)
│ │ ├── api/ # API endpoint classes
│ │ └── models/ # Data model classes
│ ├── test/ # Auto-generated tests
│ ├── docs/ # Auto-generated API docs
│ │ └── developer/ # Developer docs (migration guide, changelog)
│ ├── test_diagram_fixes.py # Integration test (custom, preserved across regeneration)
│ ├── pyproject.toml # Python packaging (uv compatible)
│ └── setup.py # Legacy packaging (kept in sync)
├── go-client-generated/
│ ├── scripts/swagger-codegen-config.json # Codegen configuration
│ ├── *.go # Generated Go source files
│ ├── go.mod # Go module definition
│ └── docs/ # Auto-generated docs
└── typescript-client-generated/
├── scripts/openapi-generator-config.json # Codegen configuration
├── src/
│ ├── apis/ # API classes
│ └── models/ # TypeScript interfaces
├── package.json # npm package definition
└── tsconfig.json # TypeScript compiler configuration
Client versions track the info.version field in the OpenAPI specification. The regeneration scripts automatically extract this version from the spec and stamp it into all relevant configuration files.
- The regeneration script downloads the OpenAPI spec (or reads a local copy).
- It extracts the version from
info.version(for example,1.3.0). - It updates the codegen config file (
packageVersionornpmVersionfield). - After code generation, it stamps the version into package files:
-
Python:
pyproject.toml(version = "X.Y.Z") andsetup.py(VERSION = "X.Y.Z") -
TypeScript:
package.json("version": "X.Y.Z") -
Go: codegen config (
packageVersion)
-
Python:
The OpenAPI spec lives on multiple branches of the ericfitz/tmi repository:
-
main-- current stable spec (currently version 1.3.0) -
dev/1.4.0-- next development spec (version 1.4.0)
Each client is tagged and released independently using per-client tag prefixes:
-
Python:
python-vX.Y.Z -
TypeScript:
ts-vX.Y.Z -
Go:
go-vX.Y.Z
This allows you to release one client without affecting the others.
Install the following tools before regenerating any client:
| Tool | Required For | Install Command |
|---|---|---|
openapi-generator |
Python, TypeScript | brew install openapi-generator |
swagger-codegen |
Go | brew install swagger-codegen |
uv |
Python | brew install uv |
go (1.21+) |
Go | brew install go |
node (18+) |
TypeScript | brew install node |
All three scripts follow the same CLI pattern:
# Regenerate from the main branch spec (default)
python3 regenerate_python.py
python3 regenerate_go.py
python3 regenerate_ts.py
# Regenerate from a specific branch
python3 regenerate_python.py --branch dev/1.4.0
python3 regenerate_go.py --branch dev/1.4.0
python3 regenerate_ts.py --branch dev/1.4.0
# Regenerate from a local spec file
python3 regenerate_python.py /path/to/tmi-openapi.json
python3 regenerate_go.py /path/to/tmi-openapi.json
python3 regenerate_ts.py /path/to/tmi-openapi.jsonEvery regeneration script follows the same sequence of steps. Understanding this workflow is essential for debugging regeneration failures.
- Check prerequisites -- verify that the required codegen tool and language runtime are installed and meet minimum version requirements.
-
Download spec -- fetch the OpenAPI spec from GitHub (or copy a local file). Extract the
info.versionfield and update the codegen config file. - Backup custom files -- copy files that must survive regeneration (integration tests, ignore files, developer docs) to a temporary backup directory.
-
Clean generated files -- delete all previously generated source, docs, and config files. Custom directories (such as
docs/developer/) are preserved, either by backup/restore or by excluding them from the clean step. -
Run code generation -- invoke
openapi-generatororswagger-codegenwith the language-specific config file. -
Stamp version -- write the spec version into generated package files (
pyproject.toml,package.json, etc.). - Apply patches -- fix known codegen bugs automatically (see "Codegen Bug Patches" below).
- Restore custom files -- copy backed-up files back into the client directory.
-
Install dependencies -- run the language-specific dependency install (
uv pip install,npm install, orgo mod tidy). - Run tests -- execute the generated test suite and any integration tests.
-
Generate report -- write a
REGENERATION_REPORT.mdwith file counts, build status, and test results. - Clean up -- remove the temporary backup directory.
| Code | Meaning |
|---|---|
0 |
Success -- codegen, patches, and tests all passed |
1 |
Fatal error -- code generation itself failed (prerequisites missing, spec download failed, codegen crashed) |
2 |
Completed with issues -- codegen succeeded but patches had warnings or tests failed |
After regeneration, always review the generated REGENERATION_REPORT.md and check test_output.log (or build_output.log) for details on any issues.
Each regeneration script includes automatic patches for known bugs in the code generators. These patches are applied after every regeneration to ensure the generated code compiles and behaves correctly.
Bug: openapi-generator generates @field_validator functions that call re.match() on UUID and datetime fields. However, Pydantic v2 parses these values into native Python types (uuid.UUID, datetime.datetime) before the validator runs. Calling re.match() on a non-string type raises a TypeError.
Fix: Insert a string conversion line before the re.match() call:
value = value.isoformat() if hasattr(value, 'isoformat') else str(value)Affected files: tmi_client/models/*.py (any model with UUID or datetime fields that have regex patterns in the spec)
Implementation: patch_regex_validators() in regenerate_python.py
Bug: openapi-generator generates test stubs with def make_instance(self, include_optional) -> ModelType: but the method body is entirely commented out, so the function always returns None. This causes type-checker errors (ty, mypy) because the return type annotation does not match the actual return value.
Fix: Change the return type annotation to -> None.
Affected files: test/*.py
Implementation: patch_test_return_types() in regenerate_python.py
Bug: openapi-generator defaults to requiring urllib3 >= 2.1.0, but versions before 2.6.3 have HIGH-severity CVEs (decompression-bomb bypass, unbounded decompression chain).
Fix: Bump the minimum urllib3 version to >= 2.6.3 in all dependency files.
Affected files: pyproject.toml, setup.py, requirements.txt
Implementation: patch_urllib3_minimum_version() in regenerate_python.py
Bug: swagger-codegen generates the RevokeToken function with a JSON body parameter (localVarPostBody = &body), but the OAuth 2.0 token revocation spec (RFC 7009) requires form-encoded parameters (application/x-www-form-urlencoded). Without this patch, token revocation is completely broken.
Fix: Replace the body parameter assignment with individual form parameter additions:
// Before (broken):
localVarPostBody = &body
// After (correct):
localVarFormParams.Add("token", parameterToString(token, ""))
localVarFormParams.Add("token_type_hint", parameterToString(tokenTypeHint, ""))
if clientId != "" {
localVarFormParams.Add("client_id", parameterToString(clientId, ""))
}
if clientSecret != "" {
localVarFormParams.Add("client_secret", parameterToString(clientSecret, ""))
}Affected files: api_authentication.go
Implementation: Exact string replacement in main() of regenerate_go.py
Severity: CRITICAL -- without this patch, token revocation does not work.
Bug: openapi-generator marks inherited required properties as optional in child interfaces when using allOf inheritance. For example, DfdDiagram extends BaseDiagram generates type?: DfdDiagramTypeEnum instead of type: DfdDiagramTypeEnum. This causes TypeScript error TS2430 (interface incorrectly extends interface).
Fix: Remove the ? from affected property declarations to make them required, matching the parent interface.
Affected pairs:
-
DfdDiagramextendsBaseDiagram(property:type) -
DfdDiagramInputextendsBaseDiagramInput(property:type) -
EdgeextendsCell(property:shape) -
NodeextendsCell(property:shape)
Affected files: src/models/DfdDiagram.ts, src/models/DfdDiagramInput.ts, src/models/Edge.ts, src/models/Node.ts
Implementation: patch_optional_extends() in regenerate_ts.py
Bug: openapi-generator generates imports for TokenRequest, TokenRequestFromJSON, and TokenRequestToJSON in AuthenticationApi.ts, but never generates the TokenRequest model file. This happens because the OpenAPI spec uses application/x-www-form-urlencoded for the token endpoint, and the generator creates a reference to a model it never produces.
Fix: Remove the TokenRequest imports and replace TokenRequestToJSON(body) calls with body as any.
Affected files: src/apis/AuthenticationApi.ts
Implementation: patch_missing_token_request() in regenerate_ts.py
When you discover a new codegen bug, follow this procedure to add an automatic patch that survives future regeneration runs.
Regenerate the client and inspect the build or test output:
python3 regenerate_python.py
# Check: REGENERATION_REPORT.md, test_output.log, build_output.logAdd a new function to the appropriate regenerate_*.py script. Follow the existing pattern:
def patch_your_fix(had_issues: bool) -> bool:
"""Describe the bug, why it happens, and what the fix does.
Include affected files and any relevant context.
"""
# Use helpers from regen_common for simple cases:
patch_file_regex(filepath, pattern, replacement, "description")
patch_file_exact(filepath, old_text, new_text, "description")
# Or write custom logic for complex multi-file patches
return had_issuesThe regen_common module provides two general-purpose patching helpers:
-
patch_file_regex(file, pattern, replacement, description)-- apply a regex substitution. ReturnsTrueif any replacements were made. -
patch_file_exact(file, old_text, new_text, description)-- replace an exact string. ReturnsTrueif the replacement was made.
Both helpers print warnings (instead of failing) when the file or pattern is not found, which makes patches resilient to future codegen output changes.
Add the function call in the "Apply patches" section of the script's main() function:
had_issues = patch_your_fix(had_issues)Regenerate the client and confirm the patch applies cleanly:
python3 regenerate_python.py
# Exit code 0 = successUpdate the patch function's docstring with a clear explanation. Then update this wiki page with the new patch details under the appropriate language section.
The Python client uses PyPI trusted publishing (OIDC) -- no API tokens are stored in GitHub secrets.
# Validate the release (build, check, test -- but do not publish)
./release_python.sh --dry-run
# Publish (build, test, tag, push tag, create GitHub Release)
./release_python.sh
# Override the version (instead of reading from pyproject.toml)
./release_python.sh 1.4.0The release_python.sh script performs the following steps:
- Reads the version from
python-client-generated/pyproject.toml(or uses the version you provide). - Checks for uncommitted changes and verifies the tag does not already exist.
- Builds the package with
uv buildand validates it withtwine check. - Runs the test suite.
- Creates an annotated git tag
python-vX.Y.Zand pushes it. - Creates a GitHub Release with the built distribution files attached.
The publish-python.yml workflow triggers automatically when a GitHub Release is published with a python-v* tag. It:
- Verifies the package version matches the tag version.
- Runs the test suite.
- Builds the package.
- Publishes to PyPI using trusted publishing (OIDC, no tokens).
Requirements:
- A
pypiGitHub environment must be configured on the repository. - A PyPI pending publisher must be set up for the
tmi-clientpackage, linked to this repository and thepublish-python.ymlworkflow.
- Ensure the version in
typescript-client-generated/package.jsonis correct (the regeneration script sets this from the spec). - Create an annotated git tag and push it:
git tag -a ts-v1.3.0 -m "Release TypeScript client v1.3.0" git push origin ts-v1.3.0 - Create a GitHub Release targeting the
ts-v1.3.0tag.
The publish-js.yml workflow triggers on the Release event for ts-v* tags. It:
- Installs dependencies (
npm install). - Builds the TypeScript project (
npm run build). - Runs tests (
npm test). - Publishes to npm with provenance (
npm publish --provenance --access public).
Requirements:
- An
npmGitHub environment must be configured on the repository. - An
NPM_TOKENsecret must be set in thenpmenvironment with publish permissions for the@tmiclient/clientpackage.
Go modules are served directly from git tags -- there is no registry upload step.
- Ensure the code builds and tests pass:
cd go-client-generated go build ./... go test -v ./...
- Create an annotated git tag and push it:
git tag -a go-v1.3.0 -m "Release Go client v1.3.0" git push origin go-v1.3.0 - Create a GitHub Release targeting the
go-v1.3.0tag.
The publish-go.yml workflow runs on the Release event for go-v* tags and validates (build + test) but does not upload anything. Users install the Go client directly from the git tag:
go get github.com/ericfitz/tmi-clients/go-client-generated@v1.3.0Edit both pyproject.toml and setup.py in python-client-generated/ -- these two files must stay in sync. After editing, verify the build:
cd python-client-generated
uv build
uv run --with pytest python3 -m pytest test/ -vImportant: The regeneration script overwrites
pyproject.tomlandsetup.pyon every run. If your dependency change must persist across regeneration, add a patch function toregenerate_python.py. The existingpatch_urllib3_minimum_version()function is a good example of how to do this.
The regeneration script writes a fresh package.json from a template string (PACKAGE_JSON) in regenerate_ts.py. If you need to add or update a dependency persistently, edit the PACKAGE_JSON template in the regeneration script, not just the package.json file.
For one-off changes that do not need to survive regeneration, edit typescript-client-generated/package.json directly:
cd typescript-client-generated
npm install
npm run buildEdit go.mod in go-client-generated/, then run:
cd go-client-generated
go mod tidy
go build ./...
go test -v ./...Note: The regeneration script does NOT restore
go.modfrom backup. Instead, it patches the existinggo.modor creates a fresh one if none exists after codegen. Persistent dependency changes should be added to theFRESH_GO_MODtemplate string inregenerate_go.py. Be aware thatgo mod tidymay adjust dependency versions beyond what the template specifies.
The ci.yml workflow runs on every push and pull request to main. It tests all three clients in parallel.
The CI pipeline uses pip directly rather than uv, since uv is not pre-installed on GitHub Actions runners.
- pip install -e ".[test]"
- pytest test/ -v --tb=short- npm install
- npm run build
- npm test- go build ./...
- go test -v ./...Known issue: The CI workflow currently references
javascript-client-generatedfor the TypeScript job. This is a known defect -- the directory was renamed totypescript-client-generatedandci.ymlhas not been updated yet. The TypeScript CI job will fail until the working directory is corrected. Note that the publish workflow (publish-js.yml) already uses the correct directory name.
The publish-python.yml workflow checks that the installed package version matches the git tag. If you see this error, the version in pyproject.toml does not match the python-vX.Y.Z tag you created. Either regenerate the client from the correct spec version or manually update pyproject.toml and setup.py.
This is an informational warning, not an error. It means the current spec has no UUID or datetime fields with regex patterns that trigger the Pydantic v2 validator bug. The patch function runs safely and produces no changes.
The swagger-codegen output structure for api_authentication.go has changed. Inspect the file manually and look for the RevokeToken function. You may need to update the REVOKE_TOKEN_OLD constant in regenerate_go.py to match the new output format.
- Check
REGENERATION_REPORT.mdfor a summary of what happened. - Check
test_output.logfor test failures. - Check
build_output.logfor compilation errors (Go, TypeScript). - If the regeneration script exited with code 2, the codegen succeeded but something downstream failed. The logs will tell you what.
You have already created a release for this version. Options:
- Bump the version in the spec or manually in the package files, then tag again.
- Delete the existing tag if it was created by mistake:
git tag -d TAG && git push origin :refs/tags/TAG
If you see TypeScript errors like Interface 'DfdDiagram' incorrectly extends interface 'BaseDiagram', the optional-extends patch may not have applied. Regenerate the client and check the build output. If the patch reports "no properties needed fixing", the codegen output format may have changed.
If the Go client fails to build with references to undefined types, swagger-codegen may have changed its output format. Compare the current api_authentication.go with the expected patch targets and update regenerate_go.py accordingly.
| Python | TypeScript | Go | |
|---|---|---|---|
| Package | tmi-client |
@tmiclient/client |
tmiclient |
| Registry | PyPI | npm | Go modules (git) |
| Generator | openapi-generator 7.x | openapi-generator 7.x | swagger-codegen 3.0.75 |
| Release tag | python-vX.Y.Z |
ts-vX.Y.Z |
go-vX.Y.Z |
| Config file | scripts/openapi-generator-config.json |
scripts/openapi-generator-config.json |
scripts/swagger-codegen-config.json |
| Runtime | Python 3.9+ | Node 18+ | Go 1.21+ |
| Models | Pydantic v2 | TypeScript interfaces | Go structs |
| Regenerate | python3 regenerate_python.py |
python3 regenerate_ts.py |
python3 regenerate_go.py |
| Release script | ./release_python.sh |
Manual tag + GH Release | Manual tag + GH Release |
| Publish workflow | publish-python.yml |
publish-js.yml |
publish-go.yml |
| Publish method | Trusted publishing (OIDC) | npm token | Git tags (no upload) |
- API-Clients -- End-user installation and usage guide
- API-Overview -- Complete API documentation
- API-Integration -- Integration patterns and examples
- Getting-Started-with-Development -- Development environment setup
- Contributing -- How to contribute to TMI projects
- Using TMI for Threat Modeling
- Accessing TMI
- Authentication
- Creating Your First Threat Model
- Understanding the User Interface
- Working with Data Flow Diagrams
- Managing Threats
- Collaborative Threat Modeling
- Using Notes and Documentation
- Timmy AI Assistant
- Metadata and Extensions
- Planning Your Deployment
- Terraform Deployment (AWS, OCI, GCP, Azure)
- Deploying TMI Server
- OCI Container Deployment
- Certificate Automation
- Deploying TMI Web Application
- Setting Up Authentication
- Database Setup
- Component Integration
- Post-Deployment
- Branding and Customization
- Monitoring and Health
- Cloud Logging
- Database Operations
- Security Operations
- Performance and Scaling
- Maintenance Tasks
- Getting Started with Development
- Architecture and Design
- API Integration
- Testing
- Contributing
- Extending TMI
- Dependency Upgrade Plans
- DFD Graphing Library Reference
- Migration Instructions