Skip to content

Commit ce96d4c

Browse files
committed
feat(cli): Allow for parameter fields to be overridden
1 parent 509be4a commit ce96d4c

File tree

4 files changed

+112
-6
lines changed

4 files changed

+112
-6
lines changed

README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,51 @@ operations:
313313

314314
The goal of the builder is to be a lightweight abstraction on top of OpenAPI, injecting in defaults where it doesn't really matter for services vending an SDK. That being said, the builder does allow you to provide any OpenAPI 3.1 operation definitions you want.
315315

316+
### Adding more properties to parameters
317+
318+
Sometimes you may want to add additional properties to a parameter, such as an example or a deprecation message, or to override its location to be in the `path` or `header`. To do this, you can provide an additional `parameters` key on the input that is keyed by the field names.
319+
320+
For example, you can add a deprecation message to a field:
321+
322+
```yaml
323+
operations:
324+
queries:
325+
listWidgets:
326+
input:
327+
schema: { $ref: '#/components/schemas/ListWidgetsInput' }
328+
parameters:
329+
status:
330+
description: 'Use "statuses" instead'
331+
deprecated: true
332+
output:
333+
schema: { $ref: '#/components/schemas/ListWidgetsOutput' }
334+
335+
errors: []
336+
```
337+
338+
<details>
339+
<summary>Resulting OpenAPI path specification</summary>
340+
341+
```yaml
342+
paths:
343+
/queries/listWidgets:
344+
operationId: listWidgets
345+
parameters:
346+
- name: status
347+
in: query
348+
schema: { $ref: '#/components/ListWidgetsInput/properties/status' }
349+
description: 'Use "statuses" instead'
350+
deprecated: true
351+
- name: userId
352+
in: query
353+
...
354+
```
355+
356+
</details>
357+
358+
> **Note**
359+
> Any fields added will take precedence over the defaults for `in` and `schema`
360+
316361
### (TODO) Changing the HTTP method and URL
317362

318363
> **Warning**

packages/cli/src/operation-transformer.test.ts

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
operations,
99
} from "./__fixtures__/widgets.fixtures.js";
1010
import { OperationTransformer } from "./operation-transformer.js";
11-
import { ReferenceObject, SchemaObject } from "./types/oas.js";
11+
import { ParameterObject, ReferenceObject, SchemaObject } from "./types/oas.js";
1212

1313
const resolver = {
1414
resolve: (ref: ReferenceObject) => {
@@ -43,4 +43,66 @@ test("OperationTransformer#transformQueryOperation", async (t) => {
4343
);
4444
assert.deepStrictEqual(actual, listUserWidgetsOAS);
4545
});
46+
47+
await t.test(
48+
"adds specified overrides to the operation parameter",
49+
async () => {
50+
const actual = await transformer.transformQueryOperation(
51+
"listUserWidgets",
52+
{
53+
...operations.listUserWidgets,
54+
input: {
55+
...operations.listUserWidgets.input,
56+
parameters: {
57+
userId: {
58+
in: "path",
59+
},
60+
status: {
61+
deprecated: true,
62+
description: 'Use "statuses" instead',
63+
},
64+
nonExistent: {
65+
description: "This parameter does not exist in the OAS",
66+
},
67+
},
68+
},
69+
}
70+
);
71+
72+
const expectedByName: Record<string, ParameterObject> =
73+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
74+
listUserWidgetsOAS.parameters!.reduce(
75+
(acc, param) => ({
76+
...acc,
77+
[(param as ParameterObject).name]: param,
78+
}),
79+
{}
80+
);
81+
expectedByName.status = {
82+
name: "status",
83+
in: "query",
84+
description: 'Use "statuses" instead',
85+
deprecated: true,
86+
schema: {
87+
type: "array",
88+
items: {
89+
$ref: "#/components/schemas/WidgetStatus",
90+
},
91+
},
92+
};
93+
94+
expectedByName.userId = {
95+
...expectedByName.userId,
96+
in: "path",
97+
};
98+
99+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
100+
const actualByName = actual.parameters!.reduce(
101+
(acc, param) => ({ ...acc, [(param as ParameterObject).name]: param }),
102+
{}
103+
);
104+
105+
assert.deepStrictEqual(actualByName, expectedByName);
106+
}
107+
);
46108
});

packages/cli/src/operation-transformer.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ export class OperationTransformer {
115115
async transformQueryInput(
116116
input: QueryInputObject
117117
): Promise<ParameterObject[]> {
118-
const { schema: schemaOrRef } = input;
118+
const { schema: schemaOrRef, parameters: parameterOverrides = {} } = input;
119119
assert(
120120
typeof schemaOrRef === "object",
121121
"Query input schema must be an object"
@@ -136,6 +136,7 @@ export class OperationTransformer {
136136
in: "query",
137137
schema,
138138
...(inputSchema?.required?.includes(name) ? { required: true } : {}),
139+
...parameterOverrides[name],
139140
})) as ParameterObject[];
140141
}
141142

packages/cli/src/types/operations.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
ResponsesObject,
66
} from "./oas.js";
77

8-
export type RPCParameterObject = Omit<OASParameterObject, "schema">;
8+
export type RPCParameterObject = Partial<OASParameterObject>;
99
// eslint-disable-next-line @typescript-eslint/no-empty-interface
1010
export interface RPCOutputObject extends MediaTypeObject {}
1111

@@ -66,10 +66,8 @@ export interface QueryOperationObject extends RPCOperationObject {
6666
export interface QueryInputObject extends MediaTypeObject {
6767
/**
6868
* Allows for optional overriding of the location of specific parameters
69-
*
70-
* @todo support for non-query locations
7169
*/
72-
// parameters?: Record<string, RPCParameterObject>;
70+
parameters?: Record<string, RPCParameterObject>;
7371
}
7472

7573
// eslint-disable-next-line @typescript-eslint/no-empty-interface

0 commit comments

Comments
 (0)