Skip to content

Commit cb4d270

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 65d3e54 commit cb4d270

4 files changed

Lines changed: 275 additions & 24 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: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ run =
6464
OpenApi.Config.inputFrom (OpenApi.Config.File "./example/overriding-global-security.yaml")
6565
|> OpenApi.Config.withOverrides [ OpenApi.Config.File "./example/overriding-global-security-override.yaml" ]
6666

67+
pathLevelParams : OpenApi.Config.Input
68+
pathLevelParams =
69+
OpenApi.Config.inputFrom (OpenApi.Config.File "./example/path-level-params.yaml")
70+
6771
realworldConduit : OpenApi.Config.Input
6872
realworldConduit =
6973
OpenApi.Config.inputFrom (OpenApi.Config.File "./example/realworld-conduit.yaml")
@@ -143,6 +147,7 @@ run =
143147
|> OpenApi.Config.withInput multipartFormData
144148
|> OpenApi.Config.withInput nullableEnum
145149
|> OpenApi.Config.withInput overridingGlobalSecurity
150+
|> OpenApi.Config.withInput pathLevelParams
146151
|> OpenApi.Config.withInput realworldConduit
147152
|> OpenApi.Config.withInput recursiveAllOfRefs
148153
|> OpenApi.Config.withInput simpleRef

src/OpenApi/Generate.elm

Lines changed: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,42 @@ stripTrailingSlash input =
280280
input
281281

282282

283+
{-| Merge path-level parameters with operation-level parameters.
284+
Per the OpenAPI spec, operation-level parameters override path-level
285+
parameters with the same name and location.
286+
-}
287+
mergeParams :
288+
List (OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter)
289+
-> List (OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter)
290+
-> CliMonad (List (OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter))
291+
mergeParams pathParams operationParams =
292+
let
293+
paramKey : OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter -> CliMonad String
294+
paramKey param =
295+
toConcreteParam param
296+
|> CliMonad.map (\concrete -> OpenApi.Parameter.in_ concrete ++ ":" ++ OpenApi.Parameter.name concrete)
297+
in
298+
CliMonad.combineMap paramKey operationParams
299+
|> CliMonad.map FastSet.fromList
300+
|> CliMonad.andThen
301+
(\operationParamKeys ->
302+
pathParams
303+
|> CliMonad.combineMap
304+
(\param ->
305+
paramKey param
306+
|> CliMonad.map
307+
(\key ->
308+
if FastSet.member key operationParamKeys then
309+
Nothing
310+
311+
else
312+
Just param
313+
)
314+
)
315+
|> CliMonad.map (\filtered -> List.filterMap identity filtered ++ operationParams)
316+
)
317+
318+
283319
pathDeclarations : List OpenApi.Config.EffectType -> ServerInfo -> CliMonad (List CliMonad.Declaration)
284320
pathDeclarations effectTypes server =
285321
CliMonad.getApiSpec
@@ -301,7 +337,7 @@ pathDeclarations effectTypes server =
301337
|> List.filterMap (\( method, getter ) -> Maybe.map (Tuple.pair method) (getter path))
302338
|> CliMonad.combineMap
303339
(\( method, operation ) ->
304-
toRequestFunctions server effectTypes method url operation
340+
toRequestFunctions server effectTypes method url (OpenApi.Path.parameters path) operation
305341
|> CliMonad.errorToWarning
306342
)
307343
|> CliMonad.map (List.filterMap identity >> List.concat)
@@ -488,8 +524,17 @@ requestBodyToDeclarations name reference =
488524
|> CliMonad.withPath name
489525

490526

491-
toRequestFunctions : ServerInfo -> List OpenApi.Config.EffectType -> String -> String -> OpenApi.Operation.Operation -> CliMonad (List CliMonad.Declaration)
492-
toRequestFunctions server effectTypes method pathUrl operation =
527+
toRequestFunctions : ServerInfo -> List OpenApi.Config.EffectType -> String -> String -> List (OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter) -> OpenApi.Operation.Operation -> CliMonad (List CliMonad.Declaration)
528+
toRequestFunctions server effectTypes method pathUrl pathLevelParams operation =
529+
mergeParams pathLevelParams (OpenApi.Operation.parameters operation)
530+
|> CliMonad.andThen
531+
(\allParams ->
532+
toRequestFunctionsHelp server effectTypes method pathUrl operation allParams
533+
)
534+
535+
536+
toRequestFunctionsHelp : ServerInfo -> List OpenApi.Config.EffectType -> String -> String -> OpenApi.Operation.Operation -> List (OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter) -> CliMonad (List CliMonad.Declaration)
537+
toRequestFunctionsHelp server effectTypes method pathUrl operation allParams =
493538
let
494539
step : OperationUtils -> CliMonad (List CliMonad.Declaration)
495540
step ({ successType, bodyTypeAnnotation, errorTypeDeclaration, errorTypeAnnotation } as operationUtils) =
@@ -523,7 +568,7 @@ toRequestFunctions server effectTypes method pathUrl operation =
523568
|> CliMonad.andThen
524569
(\params ->
525570
toConfigParamAnnotation
526-
{ operation = operation
571+
{ allParams = allParams
527572
, successAnnotation = successAnnotation
528573
, errorBodyAnnotation = bodyTypeAnnotation
529574
, errorTypeAnnotation = errorTypeAnnotation
@@ -533,11 +578,11 @@ toRequestFunctions server effectTypes method pathUrl operation =
533578
}
534579
)
535580
)
536-
(replacedUrl server auth pathUrl operation)
581+
(replacedUrl server auth pathUrl allParams)
537582
)
538583
(operationToContentSchema operation)
539584
(operationToAuthorizationInfo operation)
540-
(operationToHeaderParams operation)
585+
(operationToHeaderParams allParams)
541586
(case successType of
542587
SuccessType t ->
543588
SchemaUtils.typeToAnnotationWithNullable t
@@ -1323,10 +1368,9 @@ operationToGroup operation =
13231368
"Operations"
13241369

13251370

1326-
operationToHeaderParams : OpenApi.Operation.Operation -> CliMonad (List (Elm.Expression -> ( Elm.Expression, Elm.Expression, Bool )))
1327-
operationToHeaderParams operation =
1328-
operation
1329-
|> OpenApi.Operation.parameters
1371+
operationToHeaderParams : List (OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter) -> CliMonad (List (Elm.Expression -> ( Elm.Expression, Elm.Expression, Bool )))
1372+
operationToHeaderParams params =
1373+
params
13301374
|> CliMonad.combineMap
13311375
(\param ->
13321376
toConcreteParam param
@@ -1372,8 +1416,8 @@ operationToHeaderParams operation =
13721416
|> CliMonad.map (List.filterMap identity)
13731417

13741418

1375-
replacedUrl : ServerInfo -> AuthorizationInfo -> String -> OpenApi.Operation.Operation -> CliMonad (Elm.Expression -> Elm.Expression)
1376-
replacedUrl server authInfo pathUrl operation =
1419+
replacedUrl : ServerInfo -> AuthorizationInfo -> String -> List (OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter) -> CliMonad (Elm.Expression -> Elm.Expression)
1420+
replacedUrl server authInfo pathUrl params =
13771421
let
13781422
pathSegments : List String
13791423
pathSegments =
@@ -1451,8 +1495,7 @@ replacedUrl server authInfo pathUrl operation =
14511495
MultipleServers _ ->
14521496
Gen.Url.Builder.call_.crossOrigin (Elm.get "server" config) (Elm.list replacedSegments) allQueryParams
14531497
in
1454-
operation
1455-
|> OpenApi.Operation.parameters
1498+
params
14561499
|> CliMonad.combineMap
14571500
(\param ->
14581501
toConcreteParam param
@@ -1965,7 +2008,7 @@ multipartContent mediaType =
19652008

19662009

19672010
toConfigParamAnnotation :
1968-
{ operation : OpenApi.Operation.Operation
2011+
{ allParams : List (OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter)
19692012
, successAnnotation : Elm.Annotation.Annotation
19702013
, errorBodyAnnotation : Elm.Annotation.Annotation
19712014
, errorTypeAnnotation : Elm.Annotation.Annotation
@@ -2025,7 +2068,7 @@ toConfigParamAnnotation options =
20252068
, lamderaProgramTest = toMsgLamderaProgramTest
20262069
}
20272070
)
2028-
(operationToUrlParams options.operation)
2071+
(operationToUrlParams options.allParams)
20292072

20302073

20312074
type ServerInfo
@@ -2091,13 +2134,8 @@ serverInfo server =
20912134
|> CliMonad.succeed
20922135

20932136

2094-
operationToUrlParams : OpenApi.Operation.Operation -> CliMonad (List ( Common.UnsafeName, Elm.Annotation.Annotation ))
2095-
operationToUrlParams operation =
2096-
let
2097-
params : List (OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter)
2098-
params =
2099-
OpenApi.Operation.parameters operation
2100-
in
2137+
operationToUrlParams : List (OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter) -> CliMonad (List ( Common.UnsafeName, Elm.Annotation.Annotation ))
2138+
operationToUrlParams params =
21012139
if List.isEmpty params then
21022140
CliMonad.succeed []
21032141

0 commit comments

Comments
 (0)