Skip to content

Commit

Permalink
Support x-tie-haskell-name to override field names
Browse files Browse the repository at this point in the history
  • Loading branch information
alexbiehl committed Apr 7, 2023
1 parent 4e9764f commit e877363
Show file tree
Hide file tree
Showing 26 changed files with 385 additions and 319 deletions.
28 changes: 14 additions & 14 deletions Request.template.hs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ import Web.HttpApiData
)

pathVariable ::
FromHttpApiData a =>
(FromHttpApiData a) =>
-- | Path variable value
Text ->
(a -> Wai.Application) ->
Expand All @@ -73,27 +73,27 @@ data Style

newtype CommaDelimitedValue a = CommaDelimitedValue {unCommaDelimitedValue :: [a]}

instance FromHttpApiData a => FromHttpApiData (CommaDelimitedValue a) where
instance (FromHttpApiData a) => FromHttpApiData (CommaDelimitedValue a) where
parseUrlPiece input = do
xs <- parseUrlPieces (Text.splitOn "," input)
pure (CommaDelimitedValue xs)

newtype SpaceDelimitedValue a = SpaceDelimitedValue {unSpaceDelimitedValue :: [a]}

instance FromHttpApiData a => FromHttpApiData (SpaceDelimitedValue a) where
instance (FromHttpApiData a) => FromHttpApiData (SpaceDelimitedValue a) where
parseUrlPiece input = do
xs <- parseUrlPieces (Text.splitOn " " input)
pure (SpaceDelimitedValue xs)

newtype PipeDelimitedValue a = PipeDelimitedValue {unPipeDelimitedValue :: [a]}

instance FromHttpApiData a => FromHttpApiData (PipeDelimitedValue a) where
instance (FromHttpApiData a) => FromHttpApiData (PipeDelimitedValue a) where
parseUrlPiece input = do
xs <- parseUrlPieces (Text.splitOn "|" input)
pure (PipeDelimitedValue xs)

requiredQueryParameters ::
FromHttpApiData a =>
(FromHttpApiData a) =>
Style ->
ByteString ->
(NonEmpty.NonEmpty a -> Wai.Application) ->
Expand Down Expand Up @@ -144,7 +144,7 @@ requiredQueryParameters style name withParam =
)

optionalQueryParameters ::
FromHttpApiData a =>
(FromHttpApiData a) =>
Style ->
ByteString ->
(Maybe (NonEmpty.NonEmpty a) -> Wai.Application) ->
Expand Down Expand Up @@ -186,7 +186,7 @@ optionalQueryParameters style name withParam =
)

requiredQueryParameter ::
FromHttpApiData a =>
(FromHttpApiData a) =>
ByteString ->
(a -> Wai.Application) ->
Wai.Application
Expand All @@ -205,7 +205,7 @@ requiredQueryParameter name withParam = \request respond ->
{-# INLINEABLE requiredQueryParameter #-}

optionalQueryParameter ::
FromHttpApiData a =>
(FromHttpApiData a) =>
ByteString ->
-- | Allow empty, e.g. "x="
Bool ->
Expand All @@ -229,7 +229,7 @@ optionalQueryParameter name allowEmpty withParam = \request respond ->
{-# INLINEABLE optionalQueryParameter #-}

optionalHeader ::
FromHttpApiData a =>
(FromHttpApiData a) =>
HeaderName ->
(Maybe a -> Wai.Application) ->
Wai.Application
Expand All @@ -246,7 +246,7 @@ optionalHeader name withHeader = \request respond ->
{-# INLINEABLE optionalHeader #-}

requiredHeader ::
FromHttpApiData a =>
(FromHttpApiData a) =>
HeaderName ->
(a -> Wai.Application) ->
Wai.Application
Expand All @@ -267,11 +267,11 @@ data BodyParser a
Network.HTTP.Media.MediaType
((a -> Wai.Application) -> Wai.Application)

jsonBodyParser :: FromJSON a => BodyParser a
jsonBodyParser :: (FromJSON a) => BodyParser a
jsonBodyParser = BodyParser "application/json" parseRequestBodyJSON
{-# INLINE jsonBodyParser #-}

formBodyParser :: FromForm a => BodyParser a
formBodyParser :: (FromForm a) => BodyParser a
formBodyParser = BodyParser "application/xxx-form-urlencoded" parseRequestBodyForm
{-# INLINE formBodyParser #-}

Expand All @@ -294,7 +294,7 @@ parseRequestBody parsers withBody = \request respond -> do
respond (Wai.responseBuilder (toEnum 415) [] mempty)
{-# INLINE parseRequestBody #-}

parseRequestBodyJSON :: FromJSON a => (a -> Wai.Application) -> Wai.Application
parseRequestBodyJSON :: (FromJSON a) => (a -> Wai.Application) -> Wai.Application
parseRequestBodyJSON withBody = \request respond -> do
result <- parseWith (Wai.getRequestBodyChunk request) Data.Aeson.Parser.json' mempty
case eitherResult result of
Expand All @@ -308,7 +308,7 @@ parseRequestBodyJSON withBody = \request respond -> do
withBody body request respond
{-# INLINEABLE parseRequestBodyJSON #-}

parseRequestBodyForm :: FromForm a => (a -> Wai.Application) -> Wai.Application
parseRequestBodyForm :: (FromForm a) => (a -> Wai.Application) -> Wai.Application
parseRequestBodyForm withBody = \request respond -> do
-- Reads the body using lazy IO. Not great but it gets us
-- going and is pretty local.
Expand Down
2 changes: 1 addition & 1 deletion Response.template.hs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import qualified Network.Wai

type NDJSON element = ((element -> IO ()) -> IO () -> IO ())

responseNDJSON :: Data.Aeson.ToJSON element => Network.HTTP.Types.Status -> Network.HTTP.Types.ResponseHeaders -> NDJSON element -> Network.Wai.Response
responseNDJSON :: (Data.Aeson.ToJSON element) => Network.HTTP.Types.Status -> Network.HTTP.Types.ResponseHeaders -> NDJSON element -> Network.Wai.Response
responseNDJSON status responseHeaders stream =
Network.Wai.responseStream status responseHeaders $ \emit flush ->
stream
Expand Down
14 changes: 8 additions & 6 deletions bin/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

module Main (main) where

import Data.Version (showVersion)
import Options.Applicative
( Parser,
auto,
Expand All @@ -26,11 +27,10 @@ import Options.Applicative
switch,
value,
)
import Paths_tie (version)
import System.Environment (getArgs)
import Tie (fileWriter, generate)
import Prelude hiding (Option)
import Data.Version (showVersion)
import Paths_tie (version)

data Input = Input
{ outputDirectory :: FilePath,
Expand Down Expand Up @@ -82,10 +82,12 @@ options =
)

versioner :: Parser (a -> a)
versioner = infoOption ("tie " <> showVersion version)
( long "version"
<> help "Print Tie version"
)
versioner =
infoOption
("tie " <> showVersion version)
( long "version"
<> help "Print Tie version"
)

main :: IO ()
main = do
Expand Down
10 changes: 5 additions & 5 deletions src/Tie.hs
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,13 @@ import Tie.Writer (Writer, fileWriter, withTestWriter)
import Prelude hiding (Type)

-- | Our own version of nubOrd that both nubs and sorts
nubOrd :: Ord a => [a] -> [a]
nubOrd :: (Ord a) => [a] -> [a]
nubOrd = Set.toList . Set.fromList

-- | Read an OpenAPI spec. Throws in case it can not
-- be read or deserialized.
readOpenApiSpec ::
MonadIO m =>
(MonadIO m) =>
FilePath ->
m OpenApi.OpenApi
readOpenApiSpec filePath =
Expand All @@ -104,7 +104,7 @@ specComponents =

-- | Normalizes a 'Type' by extracting the contained inline type
-- definitions.
normalize :: Monad m => Name -> Type -> m (Type, [(Name, Type)])
normalize :: (Monad m) => Name -> Type -> m (Type, [(Name, Type)])
normalize =
normalizeType
( \enclosingType fieldName ->
Expand All @@ -125,7 +125,7 @@ normalize =
-- unnamed types left:
-- forall x. normalize x == []
-- where x is an element of the result of normalizedTypes
normalizeTypes :: Monad m => [(Name, Type)] -> m [(Name, Type)]
normalizeTypes :: (Monad m) => [(Name, Type)] -> m [(Name, Type)]
normalizeTypes types =
concat
<$> traverse
Expand All @@ -137,7 +137,7 @@ normalizeTypes types =
types

generate ::
MonadIO m =>
(MonadIO m) =>
Writer m ->
-- | Package name
Text ->
Expand Down
20 changes: 13 additions & 7 deletions src/Tie/Codegen/Operation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import Tie.Operation
import Tie.Resolve (Resolver)
import Tie.Type (isArrayType, namedType)

codegenOperations :: Monad m => Resolver m -> [Operation] -> m (PP.Doc ann)
codegenOperations :: (Monad m) => Resolver m -> [Operation] -> m (PP.Doc ann)
codegenOperations resolver operations = do
let groupedOperations :: Map.Map Path [Operation]
groupedOperations =
Expand Down Expand Up @@ -121,7 +121,7 @@ codegenOperations resolver operations = do

pure (dataApiDecl <> PP.line <> PP.line <> apiDecl <> PP.line <> inlineablePragma)

codegenApiType :: Monad m => Resolver m -> [Operation] -> m (PP.Doc ann)
codegenApiType :: (Monad m) => Resolver m -> [Operation] -> m (PP.Doc ann)
codegenApiType resolver operations = do
operationsFieldsCode <- traverse (codegenApiTypeOperation resolver) operations
let fieldsCode =
Expand All @@ -140,7 +140,7 @@ codegenApiType resolver operations = do
<> "}"
pure dataDecl

codegenApiTypeOperation :: Monad m => Resolver m -> Operation -> m (PP.Doc ann)
codegenApiTypeOperation :: (Monad m) => Resolver m -> Operation -> m (PP.Doc ann)
codegenApiTypeOperation resolver Operation {..} = do
paramsCode <-
sequence $
Expand All @@ -164,7 +164,7 @@ codegenApiTypeOperation resolver Operation {..} = do
(\x y -> x <+> "->" <> PP.line <> y)
( paramsCode
++ [ codegenRequestBodyComment body <> codegenRequestBodyType body
| Just body<- [requestBody]
| Just body <- [requestBody]
]
++ ["m" <+> toApiResponseTypeName name]
)
Expand Down Expand Up @@ -194,7 +194,7 @@ codegenApiTypeOperation resolver Operation {..} = do
code <- codegenParamSchema param
pure (codegenParamComment param <> code)

codegenOperation :: Monad m => Resolver m -> [Operation] -> m (PP.Doc ann)
codegenOperation :: (Monad m) => Resolver m -> [Operation] -> m (PP.Doc ann)
codegenOperation resolver operations@(Operation {path} : _) =
pure $
codegenPathGuard path $
Expand Down Expand Up @@ -297,8 +297,14 @@ codegenRequestBodyGuard requestBody continuation = case requestBody of
Nothing ->
continuation
Just RequestBody {provideRequestBodyAsStream = True} ->
"let" <+> "body" <+> "=" <+> "Network.Wai.getRequestBodyChunk" <+> "request" <+> "in" <> PP.line <>
PP.indent 4 ("(" <> continuation <> ")")
"let"
<+> "body"
<+> "="
<+> "Network.Wai.getRequestBodyChunk"
<+> "request"
<+> "in"
<> PP.line
<> PP.indent 4 ("(" <> continuation <> ")")
Just RequestBody {jsonRequestBodyContent} ->
let parsers =
-- TODO support forms
Expand Down
2 changes: 1 addition & 1 deletion src/Tie/Codegen/Response.hs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import Tie.Resolve (Resolver)

-- | Generate code for the responses of an 'Operation'.
codegenResponses ::
Monad m =>
(Monad m) =>
Resolver m ->
-- | Aux. Response module name TODO make this a proper type
Text ->
Expand Down
38 changes: 23 additions & 15 deletions src/Tie/Codegen/Schema.hs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ import Tie.Type
import Prelude hiding (Type)

-- | Generate code for a parameter type.
codegenParamSchema :: Monad m => Param -> m (Doc ann)
codegenParamSchema :: (Monad m) => Param -> m (Doc ann)
codegenParamSchema Param {schema, required} =
fmap (codegenRequiredOptionalFieldType required) $
case schema of
Expand Down Expand Up @@ -93,7 +93,7 @@ codegenHeaderSchema Header {schema, required} =
error "Header without schema"

-- | Generate code for a schema.
codegenSchema :: Monad m => Name -> Type -> m (Doc ann)
codegenSchema :: (Monad m) => Name -> Type -> m (Doc ann)
codegenSchema typName typ
| Just Enumeration {alternatives, includeNull} <- isEnumType typ =
pure (codegenEnumeration typName alternatives includeNull)
Expand Down Expand Up @@ -126,7 +126,7 @@ codegenArrayType typeName elemType =
"type" <+> toDataTypeName typeName <+> "=" <+> "[" <+> codegenFieldType elemType <+> "]"

codegenOneOfType ::
Monad m =>
(Monad m) =>
-- | Given a variant type name, returns the discrimintor property
-- and value, if any
(Name -> Maybe (Text, Text)) ->
Expand Down Expand Up @@ -263,7 +263,7 @@ codegenOneOfType getDiscriminator typName variants = do

pure (PP.vsep $ intersperse mempty [decl, toJson, fromJson])

codegenObjectType :: Monad m => Name -> ObjectType (Named Type) -> m (Doc ann)
codegenObjectType :: (Monad m) => Name -> ObjectType (Named Type) -> m (Doc ann)
codegenObjectType typName ObjectType {..}
-- for empty, free form objects, just generate a type synonym for Value.
| Just FreeForm <- additionalProperties,
Expand Down Expand Up @@ -366,12 +366,14 @@ codegenObjectType typName ObjectType {..}
4
( PP.concatWith
(\x y -> x <> "," <> PP.line <> y)
[ toFieldName field
[ toFieldName haskellField
<+> "::"
<+> codegenRequiredOptionalFieldType
(HashSet.member field requiredProperties)
(codegenFieldType fieldType)
| (field, fieldType) <- orderedProperties
| (field, fieldType) <- orderedProperties,
let haskellField =
HashMap.lookupDefault field field haskellFieldNames
]
)
<> PP.line
Expand Down Expand Up @@ -403,9 +405,11 @@ codegenObjectType typName ObjectType {..}
<+> PP.align
( PP.concatWith
(\x y -> x <> "," <> PP.line <> y)
[ "\"" <> toJsonFieldName field <> "\"" <+> "Data.Aeson..=" <+> toFieldName field
[ "\"" <> toJsonFieldName field <> "\"" <+> "Data.Aeson..=" <+> toFieldName haskellField
| (field, _) <- orderedProperties,
HashSet.member field requiredProperties
HashSet.member field requiredProperties,
let haskellField =
HashMap.lookupDefault field field haskellFieldNames
]
)
<> PP.line
Expand All @@ -417,16 +421,18 @@ codegenObjectType typName ObjectType {..}
<+> "["
<+> "\"" <> toJsonFieldName field <> "\""
<+> "Data.Aeson..="
<+> toFieldName field
<+> toFieldName haskellField
<+> "|"
<+> "Just"
<+> toFieldName field
<+> toFieldName haskellField
<+> "<-"
<+> "[" <> toFieldName field <> "]"
<+> "[" <> toFieldName haskellField <> "]"
<+> "]"
)
| (field, _) <- orderedProperties,
not (HashSet.member field requiredProperties)
not (HashSet.member field requiredProperties),
let haskellField =
HashMap.lookupDefault field field haskellFieldNames
]
<> ")"
)
Expand All @@ -449,7 +455,7 @@ codegenObjectType typName ObjectType {..}
"Data.Aeson.Encoding.pair"
<+> "\"" <> toJsonFieldName field <> "\""
<+> "(" <> "Data.Aeson.toEncoding"
<+> toFieldName field <> ")"
<+> toFieldName haskellField <> ")"
else
"maybe"
<+> "mempty"
Expand All @@ -458,8 +464,10 @@ codegenObjectType typName ObjectType {..}
<+> "\"" <> toJsonFieldName field <> "\""
<+> "."
<+> "Data.Aeson.toEncoding" <> ")"
<+> toFieldName field
| (field, _) <- orderedProperties
<+> toFieldName haskellField
| (field, _) <- orderedProperties,
let haskellField =
HashMap.lookupDefault field field haskellFieldNames
]
)
<> PP.line
Expand Down
Loading

0 comments on commit e877363

Please sign in to comment.