refactor: separate reading file from parsing it

This commit is contained in:
steve-chavez
2020-07-13 11:30:16 -05:00
committed by Steve Chavez
parent 343e41c51d
commit 896b79f05b
3 changed files with 95 additions and 83 deletions
+6 -3
View File
@@ -31,7 +31,7 @@ import System.IO (BufferMode (..), hSetBuffering)
import PostgREST.App (postgrest) import PostgREST.App (postgrest)
import PostgREST.Config (AppConfig (..), configPoolTimeout', import PostgREST.Config (AppConfig (..), configPoolTimeout',
prettyVersion, readOptions) prettyVersion, readAppConfig, readPath)
import PostgREST.DbStructure (getDbStructure, getPgVersion) import PostgREST.DbStructure (getDbStructure, getPgVersion)
import PostgREST.Error (PgError (PgError), checkIsFatal, import PostgREST.Error (PgError (PgError), checkIsFatal,
errorPayload) errorPayload)
@@ -200,10 +200,13 @@ main = do
hSetBuffering stdout LineBuffering hSetBuffering stdout LineBuffering
hSetBuffering stdin LineBuffering hSetBuffering stdin LineBuffering
hSetBuffering stderr NoBuffering hSetBuffering stderr NoBuffering
--
path <- readPath
-- readOptions builds the 'AppConfig' from the config file specified on the -- readOptions builds the 'AppConfig' from the config file specified on the
-- command line -- command line
conf <- loadDbUriFile =<< loadSecretFile =<< readOptions conf <- loadDbUriFile =<< loadSecretFile =<< readAppConfig path
let schemas = toList $ configSchemas conf let schemas = toList $ configSchemas conf
host = configHost conf host = configHost conf
port = configPort conf port = configPort conf
+87 -80
View File
@@ -19,7 +19,8 @@ Other hardcoded options such as the minimum version number also belong here.
module PostgREST.Config ( prettyVersion module PostgREST.Config ( prettyVersion
, docsVersion , docsVersion
, readOptions , readPath
, readAppConfig
, corsPolicy , corsPolicy
, AppConfig (..) , AppConfig (..)
, configPoolTimeout' , configPoolTimeout'
@@ -93,6 +94,8 @@ data AppConfig = AppConfig {
, configRootSpec :: Maybe Text , configRootSpec :: Maybe Text
, configRawMediaTypes :: [B.ByteString] , configRawMediaTypes :: [B.ByteString]
, configPath :: Maybe FilePath
} }
configPoolTimeout' :: (Fractional a) => AppConfig -> a configPoolTimeout' :: (Fractional a) => AppConfig -> a
@@ -137,25 +140,100 @@ prettyVersion =
docsVersion :: Text docsVersion :: Text
docsVersion = "v" <> dropEnd 1 (dropWhileEnd (/= '.') prettyVersion) docsVersion = "v" <> dropEnd 1 (dropWhileEnd (/= '.') prettyVersion)
-- | Function to read and parse options from the command line -- | Read config the file path from the command line. Also print helpful messages.
readOptions :: IO AppConfig readPath :: IO FilePath
readOptions = do readPath = customExecParser parserPrefs opts
-- First read the config file path from command line where
cfgPath <- customExecParser parserPrefs opts parserPrefs = prefs showHelpOnError
opts = info (helper <*> pathParser) $
fullDesc
<> progDesc (
"PostgREST "
<> toS prettyVersion
<> " / create a REST API to an existing Postgres database"
)
<> footerDoc (Just $
text "Example Config File:"
L.<> nest 2 (hardline L.<> exampleCfg)
)
pathParser :: Parser FilePath
pathParser =
strArgument $
metavar "FILENAME" <>
help "Path to configuration file"
exampleCfg :: Doc
exampleCfg = vsep . map (text . toS) . lines $
[str|db-uri = "postgres://user:pass@localhost:5432/dbname"
|db-schema = "public" # this schema gets added to the search_path of every request
|db-anon-role = "postgres"
|db-pool = 10
|db-pool-timeout = 10
|
|server-host = "!4"
|server-port = 3000
|
|## unix socket location
|## if specified it takes precedence over server-port
|# server-unix-socket = "/tmp/pgrst.sock"
|## unix socket file mode
|## when none is provided, 660 is applied by default
|# server-unix-socket-mode = "660"
|
|## Notification channel for reloading the schema cache
|# db-channel = "pgrst"
|## Enable or disable the notification channel
|# db-channel-enabled = false
|
|## base url for swagger output
|# openapi-server-proxy-uri = ""
|
|## choose a secret, JSON Web Key (or set) to enable JWT auth
|## (use "@filename" to load from separate file)
|# jwt-secret = "secret_with_at_least_32_characters"
|# secret-is-base64 = false
|# jwt-aud = "your_audience_claim"
|
|## limit rows in response
|# max-rows = 1000
|
|## stored proc to exec immediately after auth
|# pre-request = "stored_proc_name"
|
|## jspath to the role claim key
|# role-claim-key = ".role"
|
|## extra schemas to add to the search_path of every request
|# db-extra-search-path = "extensions, util"
|
|## stored proc that overrides the root "/" spec
|## it must be inside the db-schema
|# root-spec = "stored_proc_name"
|
|## content types to produce raw output
|# raw-media-types="image/png, image/jpg"
|]
-- | Parse the config file
readAppConfig :: FilePath -> IO AppConfig
readAppConfig cfgPath = do
-- Now read the actual config file -- Now read the actual config file
conf <- catches (C.load cfgPath) conf <- catches (C.load cfgPath)
[ Handler (\(ex :: IOError) -> exitErr $ "Cannot open config file:\n\t" <> show ex) [ Handler (\(ex :: IOError) -> exitErr $ "Cannot open config file:\n\t" <> show ex)
, Handler (\(C.ParseError err) -> exitErr $ "Error parsing config file:\n" <> err) , Handler (\(C.ParseError err) -> exitErr $ "Error parsing config file:\n" <> err)
] ]
case C.runParser parseConfig conf of case C.runParser (parseConfig cfgPath) conf of
Left err -> Left err ->
exitErr $ "Error parsing config file:\n\t" <> err exitErr $ "Error parsing config file:\n\t" <> err
Right appConf -> Right appConf ->
return appConf return appConf
where where
parseConfig = parseConfig path =
AppConfig AppConfig
<$> reqString "db-uri" <$> reqString "db-uri"
<*> reqString "db-anon-role" <*> reqString "db-anon-role"
@@ -180,6 +258,7 @@ readOptions = do
<*> (maybe ["public"] splitOnCommas <$> optValue "db-extra-search-path") <*> (maybe ["public"] splitOnCommas <$> optValue "db-extra-search-path")
<*> optString "root-spec" <*> optString "root-spec"
<*> (maybe [] (fmap encodeUtf8 . splitOnCommas) <$> optValue "raw-media-types") <*> (maybe [] (fmap encodeUtf8 . splitOnCommas) <$> optValue "raw-media-types")
<*> pure (Just path)
parseSocketFileMode :: C.Key -> C.Parser C.Config (Either Text FileMode) parseSocketFileMode :: C.Key -> C.Parser C.Config (Either Text FileMode)
parseSocketFileMode k = parseSocketFileMode k =
@@ -243,79 +322,7 @@ readOptions = do
splitOnCommas (C.String s) = strip <$> splitOn "," s splitOnCommas (C.String s) = strip <$> splitOn "," s
splitOnCommas _ = [] splitOnCommas _ = []
opts = info (helper <*> pathParser) $
fullDesc
<> progDesc (
"PostgREST "
<> toS prettyVersion
<> " / create a REST API to an existing Postgres database"
)
<> footerDoc (Just $
text "Example Config File:"
L.<> nest 2 (hardline L.<> exampleCfg)
)
parserPrefs = prefs showHelpOnError
exitErr :: Text -> IO a exitErr :: Text -> IO a
exitErr err = do exitErr err = do
hPutStrLn stderr err hPutStrLn stderr err
exitFailure exitFailure
exampleCfg :: Doc
exampleCfg = vsep . map (text . toS) . lines $
[str|db-uri = "postgres://user:pass@localhost:5432/dbname"
|db-schema = "public" # this schema gets added to the search_path of every request
|db-anon-role = "postgres"
|db-pool = 10
|db-pool-timeout = 10
|
|server-host = "!4"
|server-port = 3000
|
|## unix socket location
|## if specified it takes precedence over server-port
|# server-unix-socket = "/tmp/pgrst.sock"
|## unix socket file mode
|## when none is provided, 660 is applied by default
|# server-unix-socket-mode = "660"
|
|## Notification channel for reloading the schema cache
|# db-channel = "pgrst"
|## Enable or disable the notification channel
|# db-channel-enabled = false
|
|## base url for swagger output
|# openapi-server-proxy-uri = ""
|
|## choose a secret, JSON Web Key (or set) to enable JWT auth
|## (use "@filename" to load from separate file)
|# jwt-secret = "secret_with_at_least_32_characters"
|# secret-is-base64 = false
|# jwt-aud = "your_audience_claim"
|
|## limit rows in response
|# max-rows = 1000
|
|## stored proc to exec immediately after auth
|# pre-request = "stored_proc_name"
|
|## jspath to the role claim key
|# role-claim-key = ".role"
|
|## extra schemas to add to the search_path of every request
|# db-extra-search-path = "extensions, util"
|
|## stored proc that overrides the root "/" spec
|## it must be inside the db-schema
|# root-spec = "stored_proc_name"
|
|## content types to produce raw output
|# raw-media-types="image/png, image/jpg"
|]
pathParser :: Parser FilePath
pathParser =
strArgument $
metavar "FILENAME" <>
help "Path to configuration file"
+2
View File
@@ -90,6 +90,8 @@ _baseCfg = -- Connection Settings
Nothing Nothing
-- Raw output media types -- Raw output media types
[] []
-- Config path
Nothing
testCfg :: Text -> AppConfig testCfg :: Text -> AppConfig
testCfg testDbConn = _baseCfg { configDbUri = testDbConn } testCfg testDbConn = _baseCfg { configDbUri = testDbConn }