Skip to content

Commit 04c68bf

Browse files
feat(openapi): add support for x-fern-parameter-name extension in AsyncAPI (#11409)
Co-authored-by: Niels Swimberghe <[email protected]> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1 parent 7cdca12 commit 04c68bf

File tree

15 files changed

+2842
-12
lines changed

15 files changed

+2842
-12
lines changed

packages/cli/api-importers/openapi/openapi-ir-parser/src/asyncapi/fernExtensions.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,20 @@ export const FernAsyncAPIExtension = {
104104
* /my-channel:
105105
* x-fern-ignore: true
106106
*/
107-
IGNORE: "x-fern-ignore"
107+
IGNORE: "x-fern-ignore",
108+
109+
/**
110+
* Used to customize the name of a channel parameter in generated SDKs.
111+
* The original parameter name is preserved as the wire value.
112+
*
113+
* channels:
114+
* /my-channel/{userId}:
115+
* parameters:
116+
* userId:
117+
* description: The user ID
118+
* x-fern-parameter-name: id
119+
*/
120+
FERN_PARAMETER_NAME: "x-fern-parameter-name"
108121
} as const;
109122

110123
export type FernAsyncAPIExtension = Values<typeof FernAsyncAPIExtension>;

packages/cli/api-importers/openapi/openapi-ir-parser/src/asyncapi/v2/parseAsyncAPIV2.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,14 @@ export function parseAsyncAPIV2({
7373
const pathParameters: PathParameterWithExample[] = [];
7474
if (channel.parameters != null) {
7575
for (const [name, parameter] of Object.entries(channel.parameters ?? {})) {
76+
const parameterNameOverride = getExtension<string>(
77+
parameter,
78+
FernAsyncAPIExtension.FERN_PARAMETER_NAME
79+
);
7680
pathParameters.push({
7781
name,
7882
description: parameter.description,
79-
parameterNameOverride: undefined,
83+
parameterNameOverride,
8084
schema:
8185
parameter.schema != null
8286
? convertSchema(
@@ -340,7 +344,6 @@ export function parseAsyncAPIV2({
340344
pathParameters: pathParameters.map((param) => {
341345
return {
342346
...param,
343-
parameterNameOverride: undefined, // come back
344347
schema: convertSchemaWithExampleToSchema(param.schema)
345348
};
346349
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
{
2+
"servers": [],
3+
"websocketServers": [
4+
{
5+
"name": "production",
6+
"url": "wss://api.example.com"
7+
}
8+
],
9+
"tags": {
10+
"tagsById": {}
11+
},
12+
"hasEndpointsMarkedInternal": false,
13+
"endpoints": [],
14+
"webhooks": [],
15+
"channels": {
16+
"/users/{userId}/messages": {
17+
"audiences": [],
18+
"handshake": {
19+
"headers": [],
20+
"queryParameters": [],
21+
"pathParameters": [
22+
{
23+
"name": "userId",
24+
"description": "The user ID",
25+
"parameterNameOverride": "id",
26+
"schema": {
27+
"schema": {
28+
"type": "string"
29+
},
30+
"generatedName": "Websocket",
31+
"groupName": [],
32+
"type": "primitive"
33+
},
34+
"source": {
35+
"file": "../asyncapi.yml",
36+
"type": "openapi"
37+
}
38+
}
39+
]
40+
},
41+
"groupName": [
42+
"/users/{userId}/messages"
43+
],
44+
"messages": [
45+
{
46+
"origin": "client",
47+
"name": "publish",
48+
"body": {
49+
"allOf": [],
50+
"properties": [
51+
{
52+
"conflict": {},
53+
"generatedName": "usersUserIdMessagesPublishContent",
54+
"key": "content",
55+
"schema": {
56+
"generatedName": "UsersUserIdMessagesPublishContent",
57+
"value": {
58+
"schema": {
59+
"type": "string"
60+
},
61+
"generatedName": "UsersUserIdMessagesPublishContent",
62+
"groupName": [],
63+
"type": "primitive"
64+
},
65+
"groupName": [],
66+
"type": "optional"
67+
},
68+
"audiences": []
69+
}
70+
],
71+
"allOfPropertyConflicts": [],
72+
"generatedName": "UsersUserIdMessagesPublish",
73+
"groupName": [],
74+
"additionalProperties": false,
75+
"source": {
76+
"file": "../asyncapi.yml",
77+
"type": "openapi"
78+
},
79+
"type": "object"
80+
}
81+
},
82+
{
83+
"origin": "server",
84+
"name": "subscribe",
85+
"body": {
86+
"allOf": [],
87+
"properties": [
88+
{
89+
"conflict": {},
90+
"generatedName": "usersUserIdMessagesSubscribeContent",
91+
"key": "content",
92+
"schema": {
93+
"generatedName": "UsersUserIdMessagesSubscribeContent",
94+
"value": {
95+
"schema": {
96+
"type": "string"
97+
},
98+
"generatedName": "UsersUserIdMessagesSubscribeContent",
99+
"groupName": [],
100+
"type": "primitive"
101+
},
102+
"groupName": [],
103+
"type": "optional"
104+
},
105+
"audiences": []
106+
},
107+
{
108+
"conflict": {},
109+
"generatedName": "usersUserIdMessagesSubscribeTimestamp",
110+
"key": "timestamp",
111+
"schema": {
112+
"generatedName": "UsersUserIdMessagesSubscribeTimestamp",
113+
"value": {
114+
"schema": {
115+
"type": "string"
116+
},
117+
"generatedName": "UsersUserIdMessagesSubscribeTimestamp",
118+
"groupName": [],
119+
"type": "primitive"
120+
},
121+
"groupName": [],
122+
"type": "optional"
123+
},
124+
"audiences": []
125+
}
126+
],
127+
"allOfPropertyConflicts": [],
128+
"generatedName": "UsersUserIdMessagesSubscribe",
129+
"groupName": [],
130+
"additionalProperties": false,
131+
"source": {
132+
"file": "../asyncapi.yml",
133+
"type": "openapi"
134+
},
135+
"type": "object"
136+
}
137+
}
138+
],
139+
"servers": [
140+
{
141+
"name": "production",
142+
"url": "wss://api.example.com"
143+
}
144+
],
145+
"path": "/users/{userId}/messages",
146+
"examples": [
147+
{
148+
"queryParameters": [],
149+
"headers": [],
150+
"messages": [
151+
{
152+
"messageType": "publish",
153+
"payload": {
154+
"properties": {},
155+
"type": "object"
156+
}
157+
},
158+
{
159+
"messageType": "subscribe",
160+
"payload": {
161+
"properties": {},
162+
"type": "object"
163+
}
164+
}
165+
]
166+
}
167+
],
168+
"source": {
169+
"file": "../asyncapi.yml",
170+
"type": "openapi"
171+
}
172+
}
173+
},
174+
"groupedSchemas": {
175+
"rootSchemas": {},
176+
"namespacedSchemas": {}
177+
},
178+
"variables": {},
179+
"nonRequestReferencedSchemas": {},
180+
"securitySchemes": {},
181+
"globalHeaders": [],
182+
"idempotencyHeaders": [],
183+
"groups": {}
184+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
{
2+
"absoluteFilePath": "/DUMMY_PATH",
3+
"importedDefinitions": {},
4+
"namedDefinitionFiles": {
5+
"__package__.yml": {
6+
"absoluteFilepath": "/DUMMY_PATH",
7+
"contents": {},
8+
"rawContents": "{}
9+
",
10+
},
11+
"usersUserIdMessages.yml": {
12+
"absoluteFilepath": "/DUMMY_PATH",
13+
"contents": {
14+
"channel": {
15+
"auth": false,
16+
"examples": [
17+
{
18+
"messages": [
19+
{
20+
"body": {},
21+
"type": "publish",
22+
},
23+
{
24+
"body": {},
25+
"type": "subscribe",
26+
},
27+
],
28+
},
29+
],
30+
"messages": {
31+
"publish": {
32+
"body": "UsersUserIdMessagesPublish",
33+
"origin": "client",
34+
},
35+
"subscribe": {
36+
"body": "UsersUserIdMessagesSubscribe",
37+
"origin": "server",
38+
},
39+
},
40+
"path": "/users/{id}/messages",
41+
"path-parameters": {
42+
"id": {
43+
"docs": "The user ID",
44+
"type": "string",
45+
},
46+
},
47+
"url": "production",
48+
},
49+
"types": {
50+
"UsersUserIdMessagesPublish": {
51+
"docs": undefined,
52+
"inline": undefined,
53+
"properties": {
54+
"content": "optional<string>",
55+
},
56+
"source": {
57+
"openapi": "../asyncapi.yml",
58+
},
59+
},
60+
"UsersUserIdMessagesSubscribe": {
61+
"docs": undefined,
62+
"inline": undefined,
63+
"properties": {
64+
"content": "optional<string>",
65+
"timestamp": "optional<string>",
66+
},
67+
"source": {
68+
"openapi": "../asyncapi.yml",
69+
},
70+
},
71+
},
72+
},
73+
"rawContents": "channel:
74+
path: /users/{id}/messages
75+
url: production
76+
auth: false
77+
path-parameters:
78+
id:
79+
type: string
80+
docs: The user ID
81+
messages:
82+
publish:
83+
origin: client
84+
body: UsersUserIdMessagesPublish
85+
subscribe:
86+
origin: server
87+
body: UsersUserIdMessagesSubscribe
88+
examples:
89+
- messages:
90+
- type: publish
91+
body: {}
92+
- type: subscribe
93+
body: {}
94+
types:
95+
UsersUserIdMessagesPublish:
96+
properties:
97+
content: optional<string>
98+
source:
99+
openapi: ../asyncapi.yml
100+
UsersUserIdMessagesSubscribe:
101+
properties:
102+
content: optional<string>
103+
timestamp: optional<string>
104+
source:
105+
openapi: ../asyncapi.yml
106+
",
107+
},
108+
},
109+
"packageMarkers": {},
110+
"rootApiFile": {
111+
"contents": {
112+
"default-environment": "production",
113+
"default-url": "Base",
114+
"environments": {
115+
"production": "wss://api.example.com",
116+
},
117+
"error-discrimination": {
118+
"strategy": "status-code",
119+
},
120+
"name": "api",
121+
},
122+
"defaultUrl": "Base",
123+
"rawContents": "name: api
124+
error-discrimination:
125+
strategy: status-code
126+
environments:
127+
production: wss://api.example.com
128+
default-environment: production
129+
default-url: Base
130+
",
131+
},
132+
}

0 commit comments

Comments
 (0)