diff --git a/openapi3-code-generator/src/OpenAPI/Generate/Internal/Operation.hs b/openapi3-code-generator/src/OpenAPI/Generate/Internal/Operation.hs index 055179d..d2f485a 100644 --- a/openapi3-code-generator/src/OpenAPI/Generate/Internal/Operation.hs +++ b/openapi3-code-generator/src/OpenAPI/Generate/Internal/Operation.hs @@ -90,8 +90,8 @@ getInFromParameterObject :: OAT.ParameterObject -> OAT.ParameterObjectLocation getInFromParameterObject = OAT.in' -- | Generates the parameter type for an operation. See 'ParameterCardinality' for further information. -generateParameterTypeFromOperation :: Text -> OAT.OperationObject -> OAM.Generator ParameterCardinality -generateParameterTypeFromOperation operationName = getParametersFromOperationConcrete >=> generateParameterType operationName +generateParameterTypeFromOperation :: [OAT.Referencable OAT.ParameterObject] -> Text -> OAT.OperationObject -> OAM.Generator ParameterCardinality +generateParameterTypeFromOperation pathParams operationName = getParametersFromOperationConcrete pathParams >=> generateParameterType operationName generateParameterType :: Text -> [(OAT.ParameterObject, [Text])] -> OAM.Generator ParameterCardinality generateParameterType operationName parameters = OAM.nested "parameters" $ do @@ -159,12 +159,20 @@ getParameterLocationPrefix = ) . getInFromParameterObject --- | Extracts all parameters of an operation --- --- Concrete objects are always added. References try to get resolved to a concrete object. --- If this fails, the parameter is skipped and a warning gets produced. -getParametersFromOperationConcrete :: OAT.OperationObject -> OAM.Generator [(OAT.ParameterObject, [Text])] -getParametersFromOperationConcrete = +-- | Override parameters defined at path level with parameters which are defined at operation level +overrideParameters :: [(OAT.ParameterObject, [Text])] -> [(OAT.ParameterObject, [Text])] -> [(OAT.ParameterObject, [Text])] +overrideParameters pathParams operationParams = + -- According to OpenAPI specification, the unique parameter is identified by a combination of name and location. + -- So, we are using (name, in') as key in these maps. + let mkParamsMap parameters = Map.fromList [((getNameFromParameter (fst p), getInFromParameterObject (fst p)), p) | p <- parameters] + pathParamsMap = mkParamsMap pathParams + operationParamsMap = mkParamsMap operationParams + -- prefer operation parameters + allParamsMap = Map.union operationParamsMap pathParamsMap + in Map.elems allParamsMap + +getConcreteParameters :: [OAT.Referencable OAT.ParameterObject] -> OAM.Generator [(OAT.ParameterObject, [Text])] +getConcreteParameters = OAM.nested "parameters" . fmap Maybe.catMaybes . mapM @@ -179,7 +187,16 @@ getParametersFromOperationConcrete = pure $ (,["components", "parameters", name]) <$> p ) . zip ([0 ..] :: [Int]) - . getParametersFromOperationReference + +-- | Extracts all parameters of an operation +-- +-- Concrete objects are always added. References try to get resolved to a concrete object. +-- If this fails, the parameter is skipped and a warning gets produced. +getParametersFromOperationConcrete :: [OAT.Referencable OAT.ParameterObject] -> OAT.OperationObject -> OAM.Generator [(OAT.ParameterObject, [Text])] +getParametersFromOperationConcrete pathParameters operation = do + operationParameters <- getConcreteParameters $ getParametersFromOperationReference operation + concretePathParameters <- getConcreteParameters pathParameters + pure $ overrideParameters concretePathParameters operationParameters getSchemaFromParameterObjectSchema :: OAT.ParameterObjectSchema -> OAM.Generator (Maybe OAS.Schema) getSchemaFromParameterObjectSchema (OAT.SimpleParameterObjectSchema OAT.SimpleParameterSchema {..}) = pure $ Just schema @@ -282,9 +299,9 @@ defineOperationFunction useExplicitConfiguration fnName parameterCardinality req if getInFromParameterObject parameter == OAT.PathParameterObjectLocation then ([paramExpr], []) else ([], [paramExpr]) - MultipleParameters paramDefinition -> + MultipleParameters paramDefinition -> do let toParamExpr f = BF.first (\name -> [|$(varE name) $(varE paramName)|]) <$> f paramDefinition - in pure (toParamExpr parameterTypeDefinitionPathParams, toParamExpr parameterTypeDefinitionQueryParams) + pure (toParamExpr parameterTypeDefinitionPathParams, toParamExpr parameterTypeDefinitionQueryParams) let queryParameters' = generateQueryParams queryParameters request = generateParameterizedRequestPath pathParameters requestPath methodLit = stringE $ T.unpack method diff --git a/openapi3-code-generator/src/OpenAPI/Generate/Operation.hs b/openapi3-code-generator/src/OpenAPI/Generate/Operation.hs index dc662bb..eebb3eb 100644 --- a/openapi3-code-generator/src/OpenAPI/Generate/Operation.hs +++ b/openapi3-code-generator/src/OpenAPI/Generate/Operation.hs @@ -31,13 +31,16 @@ import qualified OpenAPI.Generate.OptParse as OAO import qualified OpenAPI.Generate.Response as OAR import qualified OpenAPI.Generate.Types as OAT +getParametersFromPath :: OAT.PathItemObject -> [OAT.Referencable OAT.ParameterObject] +getParametersFromPath = OAT.parameters + -- | Defines the operations for all paths and their methods defineOperationsForPath :: String -> Text -> OAT.PathItemObject -> OAM.Generator (Q [Dep.ModuleDefinition], Dep.Models) defineOperationsForPath mainModuleName requestPath pathItemObject = OAM.nested requestPath $ do operationsToGenerate <- OAM.getSetting OAO.settingOperationsToGenerate fmap (BF.bimap sequence Set.unions) . mapAndUnzipM - (uncurry (defineModuleForOperation mainModuleName requestPath)) + (uncurry (defineModuleForOperation mainModuleName requestPath (getParametersFromPath pathItemObject))) $ filterEmptyAndOmittedOperations operationsToGenerate [ ("GET", OAT.get pathItemObject), @@ -68,13 +71,15 @@ defineModuleForOperation :: -- | The path to the request (This is the key from the map of Operations) -- It may contain placeholder variables in the form of /my/{var}/path/ Text -> + -- | Path parameter definition + [OAT.Referencable OAT.ParameterObject] -> -- | HTTP Method (GET,POST,etc) Text -> -- | The Operation Object OAT.OperationObject -> -- | commented function definition and implementation OAM.Generator (Q Dep.ModuleDefinition, Dep.Models) -defineModuleForOperation mainModuleName requestPath method operation = OAM.nested method $ do +defineModuleForOperation mainModuleName requestPath pathParams method operation = OAM.nested method $ do operationIdName <- getOperationName requestPath method operation convertToCamelCase <- OAM.getSetting OAO.settingConvertToCamelCase let operationIdAsText = T.pack $ show operationIdName @@ -84,7 +89,7 @@ defineModuleForOperation mainModuleName requestPath method operation = OAM.neste (bodySchema, bodyPath) <- getBodySchemaFromOperation operation (responseTypeName, responseTransformerExp, responseBodyDefinitions, responseBodyDependencies) <- OAR.getResponseDefinitions operation appendToOperationName (bodyType, (bodyDefinition, bodyDependencies)) <- OAM.resetPath bodyPath $ getBodyType bodySchema appendToOperationName - parameterCardinality <- generateParameterTypeFromOperation operationIdAsText operation + parameterCardinality <- generateParameterTypeFromOperation pathParams operationIdAsText operation paramDescriptions <- (<> ["The request body to send" | not $ null bodyType]) <$> ( case parameterCardinality of diff --git a/openapi3-code-generator/src/OpenAPI/Generate/Types.hs b/openapi3-code-generator/src/OpenAPI/Generate/Types.hs index db583b0..ae372b3 100644 --- a/openapi3-code-generator/src/OpenAPI/Generate/Types.hs +++ b/openapi3-code-generator/src/OpenAPI/Generate/Types.hs @@ -319,7 +319,7 @@ data ParameterObjectLocation | HeaderParameterObjectLocation | PathParameterObjectLocation | CookieParameterObjectLocation - deriving (Show, Eq, Generic) + deriving (Show, Eq, Ord, Generic) instance FromJSON ParameterObjectLocation where parseJSON = withText "ParameterObjectLocation" $ \case