Repository navigation
feat(massimo-cli): add --path-map to declare a path map of the client operations - #245
Open
mirko-pira wants to merge 1 commit into
Open
mirko-pira wants to merge 1 commit into
mirko-pira wants to merge 1 commit into
Conversation
… operations The generated types name each operation's request and responses types after its operationId, so code that only knows the path template and the HTTP method (a generic fetch wrapper, a router, a mock server) has no way to reach them, and an operation without operationId or with a repeated one gets a name that is hard to predict. With `--path-map` the generated types also declare `<Name>Paths`, an interface keyed by path template, then by method, whose entries name the request and responses types the client method uses. `writeOperations` now returns one entry per operation with the names it declared, so the map follows the ids generated from a path and the renames of repeated ids instead of recomputing them. Two aliases, `<Name>PathRequest<P, M>` and `<Name>PathResponses<P, M>`, resolve an entry for a generic path and method through `infer`: indexing `<Name>Paths[P][M]['request']` does not compile there (TS2536). The map names only operation types, which every mode declares, so the option does not depend on `--named-schemas` and is a flag of its own. An alias whose name an operation type already has (an operationId of `<name>Path`) gets a numeric suffix; for that the operation type names are computed whenever either flag is set. With both flags a component schema named like the map or an alias is renamed with the `Schema` suffix, like any other taken name. The flag is off by default: without it the output, `--named-schemas` included, is unchanged. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Claude <noreply@anthropic.com> Signed-off-by: Mirko Pira <mirkodev@outlook.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Code that knows a URL template and an HTTP method (a generic fetch wrapper, a router, a mock server) cannot reach the generated types of that operation. The types are named after the operationId, and Massimo derives that id from the path when it is missing and renames it when it repeats (
generateOperationId, called at openapi-generator.js:106). A consumer that wants to go from a path to its types has to reimplement that naming, and drifts the moment it changes.Change
A new opt-in flag:
With it the types file also declares
<Name>Paths, keyed by path template and then method. From the new test fixture (excerpt):GET /movies/{id}has no operationId, and the secondcreateFolderbecamepostCreateFolder: the map carries the names the client actually uses. The two aliases look up an entry when the path and the method are type parameters. The obviousMoviesPaths[P][M]['request']does not compile (TS2536), because the per-path types share no constraint that proves the member exists; henceinfer.writeOperationsnow returns, for every operation, the request and responses type names it puts in the client method signature (built at openapi-common.js:33 and responses-writer.js:56), andwritePathMapprints exactly those, so the map cannot disagree with the client. The Node types and the--frontendtypes both get the map.Names:
<Name>Pathsrather than a fixedPaths, so two clients in one project do not clash. If an operationIdmoviesPathalready declaresMoviesPathRequest, the aliases becomeMoviesPathRequest2andMoviesPathResponses2;MoviesPathsnever gets a suffix. With--named-schemasas well, a component schema called like one of the three names gets theSchemasuffix, like any other taken name.Why a separate flag
#218 added
--named-schemasand shipped in 1.6.0. The map names only operation types, which every mode declares and which--named-schemasnever renames (openapi-generator.js:143). So the map is the same text with or without named schemas, and is useful without them. Putting it under--named-schemaswould change the output of a released flag: three new exports, a schema rename in the collision case, and a different warning. As its own flag it changes nothing for anyone who does not pass it.Compatibility
The flag is off by default. I generated every OpenAPI fixture under
packages/massimo-cli/test/fixtureswith main 8c10018 and with this branch: the 47 specs on main (one YAML, and four without anopenapikey) plus the new fixture, in four modes (--types-only, full client,--frontend,--frontend --language ts), each plain and with--named-schemas. That is 768 runs (384 pairs) and 1,048 files, with identical bytes and identical exit codes; the only non-zero exit,common-parameters/openapi.jsonwith--frontend, is the same on both sides. The 1.6.0 warning for--named-schemason a spec withoutcomponents.schemasis unchanged.Size: on the Stripe spec,
--types-only --path-mapadds 7.0% to the declaration file. Petstore 3 and Stripe already failtscon main because adefaultresponse prints asFullResponse<..., default>(responses-writer.js:51, #16); the error count is the same with and without--path-map, and with that status patched tonumberthe map check compiles with 0 errors.Tests
test/cli-openapi-path-map.test.js, with a fixture that has an operation without an operationId, one operationId on two paths, a 204-only response, and schemas named like the map and an alias.--types-onlyand--frontend --language ts. A stricttsccheck (skipLibCheck: false) asserts each entry is the request and responses type of the matching client method, the aliases resolve every entry for a generic path and method, and an unknown path or method is an error.--named-schemas, a schema named like the map is renamed with theSchemasuffix.<name>Pathsuffixes the aliases, plain and with--named-schemas.components.schemasgives no warning with--path-map.--named-schemas, once the two expected schema renames (and their comments) are undone as well.Revert proofs: computing the operation type names only under
--named-schemasmakes the plain suffix test fail itsMoviesPathRequest2assertion, beforetscruns; compiling that output on its own gives TS2300 (duplicate identifierMoviesPathRequestandMoviesPathResponses). Indexing the map directly instead ofinfermakes the four map tests fail their alias text assertions, and the two suffix tests fail their stricttsccheck with TS2536; a separate compile of a two-path spec gives the same TS2536.Suites on this commit:
pnpm testinpackages/massimo-clipasses 174 of 174 tests over 24 files, with a cleantsdposttest;pnpm run lintpasses; rootpnpm run lint:markdownpasses and rewrites nothing. I did not run the Playwright e2e.Docs: one help line for
--path-map, and a### Path Mapsection in the README after Named Schemas.This PR was prepared with AI assistance (Claude). I reviewed the change, and both contributors signed off per CONTRIBUTING.md.