acacode / acacode/swagger-typescript-api
Colon suffix in path segment (AIP-136 custom method, e.g. /v1/items:search) becomes a phantom required path param
- 主要語言
- TypeScript
- 星號
- 4.1k
- 分支
- 436
- PR 合併指標
- 30 天內沒有已合併 PR
描述
### Description
A colon inside a path segment is treated as an Express-style path parameter. Routes that use [AIP-136 custom methods](https://google.aip.dev/136), such as `/v1/items:search` or `/v1/items/{itemId}:archive`, come out with a required `string` argument that the spec does not declare. The colon suffix is also removed from the URL template.
This looks like the same problem as #532, which was filed as a question and is still open.
Version: 13.12.6. `parseRouteName` on `main` is unchanged.
### Reproduction
```json
{
"openapi": "3.0.3",
"info": { "title": "repro", "version": "1.0.0" },
"paths": {
"/v1/items:search": {
"post": {
"operationId": "searchItems",
"responses": { "200": { "description": "ok" } }
}
},
"/v1/items/{itemId}:archive": {
"post": {
"operationId": "archiveItem",
"parameters": [
{ "name": "itemId", "in": "path", "required": true, "schema": { "type": "string" } }
],
"responses": { "200": { "description": "ok" } }
}
}
}
}
```
```js
await generateApi({ input: "spec.json", output: false, httpClientType: "fetch" });
```
### Actual
```ts
searchItems: (search: string, params: RequestParams = {}) =>
this.request({
path: `/v1/items${search}`,
method: "POST",
...params,
}),
archiveItem: (itemId: string, archive: string, params: RequestParams = {}) =>
this.request({
path: `/v1/items/${itemId}${archive}`,
method: "POST",
...params,
}),
```
`searchItems()` requires an argument that has no meaning in the spec. If the caller passes a value, it is appended to the URL as is. The only argument that gives the correct URL is the literal `":search"`.
### Expected
```ts
searchItems: (params: RequestParams = {}) =>
this.request({ path: `/v1/items:search`, method: "POST", ...params }),
archiveItem: (itemId: string, params: RequestParams = {}) =>
this.request({ path: `/v1/items/${itemId}:archive`, method: "POST", ...params }),
```
### Cause and a possible fix
In `src/schema-routes/schema-routes.ts`, `parseRouteName` matches a colon parameter at any position:
```js
/({[\w[\\\]^`][-_.\w]*})|(:[\w[\\\]^`][-_.\w]*:?)/g
```
An Express-style parameter always starts a path segment. Requiring a preceding `/` keeps `/users/:userId` working and stops matching custom-method suffixes:
```js
/({[\w[\\\]^`][-_.\w]*})|((?<=\/):[\w[\\\]^`][-_.\w]*:?)/g
```
| route | current | with lookbehind |
|---|---|---|
| `/v1/items:search` | `[":search"]` | none |
| `/v1/items/{itemId}:archive` | `["{itemId}", ":archive"]` | `["{itemId}"]` |
| `/users/:userId` | `[":userId"]` | `[":userId"]` |
| `/users/:userId/posts/:postId` | `[":userId", ":postId"]` | `[":userId", ":postId"]` |
### Workaround
We use an `onCreateRoute` hook. It finds path params whose `$match` starts with `:`, puts the literal back into `request.path`, and removes the param from `request.parameters` and from `requestParams.typeData.content`.
貢獻指南
這個儲存庫沒有索引到貢獻指南
評估
這個 Issue 還沒有評估資料。