Skip to content

Commit 5196394

Browse files
committed
Support path-level parameters in code generation
Merge path item parameters with operation parameters per the OpenAPI spec. Previously, parameters defined at the path level (shared across all operations) were silently ignored, causing path params like {orgId} to appear as literal strings in generated URLs.
1 parent 302c7a1 commit 5196394

4 files changed

Lines changed: 300 additions & 48 deletions

File tree

cli/example/path-level-params.yaml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
openapi: "3.1.0"
2+
info:
3+
title: "Path Level Params Test"
4+
version: "1.0.0"
5+
paths:
6+
/orgs/{orgId}/teams/{teamId}/items:
7+
parameters:
8+
- $ref: '#/components/parameters/orgIdParam'
9+
- in: path
10+
name: teamId
11+
required: true
12+
schema:
13+
type: string
14+
get:
15+
operationId: getItems
16+
parameters:
17+
- in: query
18+
name: status
19+
required: false
20+
schema:
21+
type: string
22+
responses:
23+
"200":
24+
description: OK
25+
content:
26+
application/json:
27+
schema:
28+
type: object
29+
properties:
30+
count:
31+
type: integer
32+
required:
33+
- count
34+
components:
35+
parameters:
36+
orgIdParam:
37+
in: path
38+
name: orgId
39+
required: true
40+
schema:
41+
type: string

cli/src/TestGenScript.elm

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,23 @@ run =
3939
binaryResponse =
4040
OpenApi.Config.inputFrom (OpenApi.Config.File "./example/binary-response.yaml")
4141

42+
bug : Int -> OpenApi.Config.Input
43+
bug n =
44+
OpenApi.Config.inputFrom (OpenApi.Config.File ("./example/openapi-generator-bugs/" ++ String.fromInt n ++ ".yaml"))
45+
4246
cookieAuth : OpenApi.Config.Input
4347
cookieAuth =
4448
OpenApi.Config.inputFrom (OpenApi.Config.File "./example/cookie-auth.yaml")
4549

50+
dbFahrplanApi : OpenApi.Config.Input
51+
dbFahrplanApi =
52+
OpenApi.Config.inputFrom (OpenApi.Config.File "./example/db-fahrplan-api-specification.yaml")
53+
54+
gitHub : OpenApi.Config.Input
55+
gitHub =
56+
OpenApi.Config.inputFrom (OpenApi.Config.File "./example/github-spec.json")
57+
|> OpenApi.Config.withWarnOnMissingEnums False
58+
4659
ifconfigOvh : OpenApi.Config.Input
4760
ifconfigOvh =
4861
OpenApi.Config.inputFrom (OpenApi.Config.File "./example/ifconfig.ovh.json")
@@ -60,6 +73,14 @@ run =
6073
OpenApi.Config.inputFrom (OpenApi.Config.File "./example/overriding-global-security.yaml")
6174
|> OpenApi.Config.withOverrides [ OpenApi.Config.File "./example/overriding-global-security-override.yaml" ]
6275

76+
pathLevelParams : OpenApi.Config.Input
77+
pathLevelParams =
78+
OpenApi.Config.inputFrom (OpenApi.Config.File "./example/path-level-params.yaml")
79+
80+
patreon : OpenApi.Config.Input
81+
patreon =
82+
OpenApi.Config.inputFrom (OpenApi.Config.File "./example/patreon.json")
83+
6384
realworldConduit : OpenApi.Config.Input
6485
realworldConduit =
6586
OpenApi.Config.inputFrom (OpenApi.Config.File "./example/realworld-conduit.yaml")
@@ -76,9 +97,9 @@ run =
7697
singleEnum =
7798
OpenApi.Config.inputFrom (OpenApi.Config.File "./example/single-enum.yaml")
7899

79-
uuidArrayParam : OpenApi.Config.Input
80-
uuidArrayParam =
81-
OpenApi.Config.inputFrom (OpenApi.Config.File "./example/uuid-array-param.yaml")
100+
telegramBot : OpenApi.Config.Input
101+
telegramBot =
102+
OpenApi.Config.inputFrom (OpenApi.Config.File "./example/telegram-bot.json")
82103

83104
trustmark : OpenApi.Config.Input
84105
trustmark =
@@ -93,31 +114,14 @@ run =
93114
|> OpenApi.Config.withOutputModuleName [ "Trustmark", "TradeCheck" ]
94115
|> OpenApi.Config.withEffectTypes [ OpenApi.Config.ElmHttpCmd ]
95116

117+
uuidArrayParam : OpenApi.Config.Input
118+
uuidArrayParam =
119+
OpenApi.Config.inputFrom (OpenApi.Config.File "./example/uuid-array-param.yaml")
120+
96121
viaggiatreno : OpenApi.Config.Input
97122
viaggiatreno =
98123
OpenApi.Config.inputFrom (OpenApi.Config.File "./example/viaggiatreno.yaml")
99124

100-
bug : Int -> OpenApi.Config.Input
101-
bug n =
102-
OpenApi.Config.inputFrom (OpenApi.Config.File ("./example/openapi-generator-bugs/" ++ String.fromInt n ++ ".yaml"))
103-
104-
dbFahrplanApi : OpenApi.Config.Input
105-
dbFahrplanApi =
106-
OpenApi.Config.inputFrom (OpenApi.Config.File "./example/db-fahrplan-api-specification.yaml")
107-
108-
gitHub : OpenApi.Config.Input
109-
gitHub =
110-
OpenApi.Config.inputFrom (OpenApi.Config.File "./example/github-spec.json")
111-
|> OpenApi.Config.withWarnOnMissingEnums False
112-
113-
patreon : OpenApi.Config.Input
114-
patreon =
115-
OpenApi.Config.inputFrom (OpenApi.Config.File "./example/patreon.json")
116-
117-
telegramBot : OpenApi.Config.Input
118-
telegramBot =
119-
OpenApi.Config.inputFrom (OpenApi.Config.File "./example/telegram-bot.json")
120-
121125
profileConfig : OpenApi.Config.Config
122126
profileConfig =
123127
-- Slimmed config for profiling
@@ -133,6 +137,7 @@ run =
133137
|> OpenApi.Config.withInput marioPartyStats
134138
|> OpenApi.Config.withInput nullableEnum
135139
|> OpenApi.Config.withInput overridingGlobalSecurity
140+
|> OpenApi.Config.withInput pathLevelParams
136141
|> OpenApi.Config.withInput realworldConduit
137142
|> OpenApi.Config.withInput recursiveAllOfRefs
138143
|> OpenApi.Config.withInput simpleRef

src/OpenApi/Generate.elm

Lines changed: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,42 @@ stripTrailingSlash input =
273273
input
274274

275275

276+
{-| Merge path-level parameters with operation-level parameters.
277+
Per the OpenAPI spec, operation-level parameters override path-level
278+
parameters with the same name and location.
279+
-}
280+
mergeParams :
281+
List (OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter)
282+
-> List (OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter)
283+
-> CliMonad (List (OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter))
284+
mergeParams pathParams operationParams =
285+
let
286+
paramKey : OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter -> CliMonad String
287+
paramKey param =
288+
toConcreteParam param
289+
|> CliMonad.map (\concrete -> OpenApi.Parameter.in_ concrete ++ ":" ++ OpenApi.Parameter.name concrete)
290+
in
291+
CliMonad.combineMap paramKey operationParams
292+
|> CliMonad.map FastSet.fromList
293+
|> CliMonad.andThen
294+
(\operationParamKeys ->
295+
pathParams
296+
|> CliMonad.combineMap
297+
(\param ->
298+
paramKey param
299+
|> CliMonad.map
300+
(\key ->
301+
if FastSet.member key operationParamKeys then
302+
Nothing
303+
304+
else
305+
Just param
306+
)
307+
)
308+
|> CliMonad.map (\filtered -> List.filterMap identity filtered ++ operationParams)
309+
)
310+
311+
276312
pathDeclarations : List OpenApi.Config.EffectType -> ServerInfo -> CliMonad (List CliMonad.Declaration)
277313
pathDeclarations effectTypes server =
278314
CliMonad.getApiSpec
@@ -294,7 +330,7 @@ pathDeclarations effectTypes server =
294330
|> List.filterMap (\( method, getter ) -> Maybe.map (Tuple.pair method) (getter path))
295331
|> CliMonad.combineMap
296332
(\( method, operation ) ->
297-
toRequestFunctions server effectTypes method url operation
333+
toRequestFunctions server effectTypes method url (OpenApi.Path.parameters path) operation
298334
|> CliMonad.errorToWarning
299335
)
300336
|> CliMonad.map (List.filterMap identity >> List.concat)
@@ -462,8 +498,17 @@ requestBodyToDeclarations name reference =
462498
|> CliMonad.withPath name
463499

464500

465-
toRequestFunctions : ServerInfo -> List OpenApi.Config.EffectType -> String -> String -> OpenApi.Operation.Operation -> CliMonad (List CliMonad.Declaration)
466-
toRequestFunctions server effectTypes method pathUrl operation =
501+
toRequestFunctions : ServerInfo -> List OpenApi.Config.EffectType -> String -> String -> List (OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter) -> OpenApi.Operation.Operation -> CliMonad (List CliMonad.Declaration)
502+
toRequestFunctions server effectTypes method pathUrl pathLevelParams operation =
503+
mergeParams pathLevelParams (OpenApi.Operation.parameters operation)
504+
|> CliMonad.andThen
505+
(\allParams ->
506+
toRequestFunctionsHelp server effectTypes method pathUrl operation allParams
507+
)
508+
509+
510+
toRequestFunctionsHelp : ServerInfo -> List OpenApi.Config.EffectType -> String -> String -> OpenApi.Operation.Operation -> List (OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter) -> CliMonad (List CliMonad.Declaration)
511+
toRequestFunctionsHelp server effectTypes method pathUrl operation allParams =
467512
let
468513
functionName : String
469514
functionName =
@@ -1142,7 +1187,7 @@ toRequestFunctions server effectTypes method pathUrl operation =
11421187
|> CliMonad.andThen
11431188
(\params ->
11441189
toConfigParamAnnotation
1145-
{ operation = operation
1190+
{ allParams = allParams
11461191
, successAnnotation = successAnnotation
11471192
, errorBodyAnnotation = bodyTypeAnnotation
11481193
, errorTypeAnnotation = errorTypeAnnotation
@@ -1152,8 +1197,8 @@ toRequestFunctions server effectTypes method pathUrl operation =
11521197
}
11531198
)
11541199
)
1155-
(replacedUrl server auth pathUrl operation)
1156-
(operationToHeaderParams operation)
1200+
(replacedUrl server auth pathUrl allParams)
1201+
(operationToHeaderParams allParams)
11571202
)
11581203
(operationToContentSchema operation)
11591204
(operationToAuthorizationInfo operation)
@@ -1181,10 +1226,9 @@ operationToGroup operation =
11811226
"Operations"
11821227

11831228

1184-
operationToHeaderParams : OpenApi.Operation.Operation -> CliMonad (List (Elm.Expression -> ( Elm.Expression, Elm.Expression, Bool )))
1185-
operationToHeaderParams operation =
1186-
operation
1187-
|> OpenApi.Operation.parameters
1229+
operationToHeaderParams : List (OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter) -> CliMonad (List (Elm.Expression -> ( Elm.Expression, Elm.Expression, Bool )))
1230+
operationToHeaderParams params =
1231+
params
11881232
|> CliMonad.combineMap
11891233
(\param ->
11901234
toConcreteParam param
@@ -1230,8 +1274,8 @@ operationToHeaderParams operation =
12301274
|> CliMonad.map (List.filterMap identity)
12311275

12321276

1233-
replacedUrl : ServerInfo -> AuthorizationInfo -> String -> OpenApi.Operation.Operation -> CliMonad (Elm.Expression -> Elm.Expression)
1234-
replacedUrl server authInfo pathUrl operation =
1277+
replacedUrl : ServerInfo -> AuthorizationInfo -> String -> List (OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter) -> CliMonad (Elm.Expression -> Elm.Expression)
1278+
replacedUrl server authInfo pathUrl params =
12351279
let
12361280
pathSegments : List String
12371281
pathSegments =
@@ -1309,8 +1353,7 @@ replacedUrl server authInfo pathUrl operation =
13091353
MultipleServers _ ->
13101354
Gen.Url.Builder.call_.crossOrigin (Elm.get "server" config) (Elm.list replacedSegments) allQueryParams
13111355
in
1312-
operation
1313-
|> OpenApi.Operation.parameters
1356+
params
13141357
|> CliMonad.combineMap
13151358
(\param ->
13161359
toConcreteParam param
@@ -1761,7 +1804,7 @@ contentToContentSchema content =
17611804

17621805

17631806
toConfigParamAnnotation :
1764-
{ operation : OpenApi.Operation.Operation
1807+
{ allParams : List (OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter)
17651808
, successAnnotation : Elm.Annotation.Annotation
17661809
, errorBodyAnnotation : Elm.Annotation.Annotation
17671810
, errorTypeAnnotation : Elm.Annotation.Annotation
@@ -1820,7 +1863,7 @@ toConfigParamAnnotation options =
18201863
, lamderaProgramTest = toAnnotation toMsgLamderaProgramTest
18211864
}
18221865
)
1823-
(operationToUrlParams options.operation)
1866+
(operationToUrlParams options.allParams)
18241867

18251868

18261869
type ServerInfo
@@ -1886,13 +1929,8 @@ serverInfo server =
18861929
|> CliMonad.succeed
18871930

18881931

1889-
operationToUrlParams : OpenApi.Operation.Operation -> CliMonad (List ( Common.UnsafeName, Elm.Annotation.Annotation ))
1890-
operationToUrlParams operation =
1891-
let
1892-
params : List (OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter)
1893-
params =
1894-
OpenApi.Operation.parameters operation
1895-
in
1932+
operationToUrlParams : List (OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter) -> CliMonad (List ( Common.UnsafeName, Elm.Annotation.Annotation ))
1933+
operationToUrlParams params =
18961934
if List.isEmpty params then
18971935
CliMonad.succeed []
18981936

0 commit comments

Comments
 (0)