conferer-source-json (empty) → 0.4.0.0
raw patch · 7 files changed
+271/−0 lines, 7 filesdep +aesondep +aeson-qqdep +basesetup-changed
Dependencies added: aeson, aeson-qq, base, bytestring, conferer, conferer-source-json, directory, hspec, text, unordered-containers, vector
Files
- LICENSE +21/−0
- README.md +1/−0
- Setup.hs +2/−0
- conferer-source-json.cabal +66/−0
- src/Conferer/Source/JSON.hs +121/−0
- test/Conferer/Source/JSONSpec.hs +59/−0
- test/Spec.hs +1/−0
+ LICENSE view
@@ -0,0 +1,21 @@+MIT License++Copyright (c) 2019 Lucas David Traverso++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ README.md view
@@ -0,0 +1,1 @@+# conferer-source-json
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ conferer-source-json.cabal view
@@ -0,0 +1,66 @@+cabal-version: 1.18++-- This file has been generated from package.yaml by hpack version 0.33.0.+--+-- see: https://github.com/sol/hpack+--+-- hash: 75e351874fa593dd3d42bba9fbbe98f40ceb07b84a807ac0f020472cc07901d2++name: conferer-source-json+version: 0.4.0.0+synopsis: conferer's source for reading json files++description: Library to abstract the parsing of many haskell config values from different config sources+category: Configuration+homepage: https://github.com/ludat/conferer#readme+author: Lucas David Traverso+maintainer: lucas6246@gmail.com+copyright: MIT+license: BSD3+license-file: LICENSE+build-type: Simple+extra-doc-files:+ README.md+ LICENSE++library+ exposed-modules:+ Conferer.Source.JSON+ other-modules:+ Paths_conferer_source_json+ hs-source-dirs:+ src+ default-extensions: OverloadedStrings LambdaCase QuasiQuotes ScopedTypeVariables+ build-depends:+ aeson >=0.10 && <2.0+ , base >=4.3 && <5+ , bytestring >=0.10 && <0.11+ , conferer >=0.4.0.0 && <0.5.0.0+ , directory >=1.2 && <2.0+ , text >=1.1 && <1.3+ , unordered-containers+ , vector+ default-language: Haskell2010++test-suite specs+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ other-modules:+ Conferer.Source.JSONSpec+ Paths_conferer_source_json+ hs-source-dirs:+ test+ default-extensions: OverloadedStrings LambdaCase QuasiQuotes ScopedTypeVariables+ build-depends:+ aeson >=0.10 && <2.0+ , aeson-qq+ , base >=4.3 && <5+ , bytestring >=0.10 && <0.11+ , conferer+ , conferer-source-json+ , directory >=1.2 && <2.0+ , hspec+ , text >=1.1 && <1.3+ , unordered-containers+ , vector+ default-language: Haskell2010
+ src/Conferer/Source/JSON.hs view
@@ -0,0 +1,121 @@+module Conferer.Source.JSON+ (+ -- * How to use this source+ -- | As any other source you can add it to a config using the 'addSource'+ -- function. There are a couple of oddities that come from supporting many+ -- different sources which do not support numbers, arrays or objects+ -- nativelly+ --+ -- @+ -- import 'Conferer'+ -- import Conferer.Source.JSON ('mkJsonSource')+ --+ -- main = do+ -- config <-+ -- 'defaultConfig' \"awesomeapp\"+ -- & 'addSource' 'mkJsonSource'+ -- warpSettings <- 'getFromConfig' \"warp\" config+ -- runSettings warpSettings application+ -- @+ --+ -- This will result on a source that upon starting looks the file+ -- @config/{.env}.json@ in the current directory and uses it to provide config+ -- keys.+ --+ -- TODO Describe how we transform json into key value strings+ mkJsonSource+ , mkJsonSource'++ -- * Internal utility functions+ -- | These may be useful for someone but are subject to change at any point so+ -- use with care+ , traverseJSON+ , resultToMaybe+ , valueToText+ , boolToString+ )+where++import Data.Aeson+import qualified Data.HashMap.Strict as HashMap+import Data.Text (Text)+import qualified Data.Text as Text+import qualified Data.Text.Encoding as Text+import Data.Vector+import Text.Read (readMaybe)+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as L+import System.Directory (doesFileExist)+import Data.Monoid+++import Conferer.Source.Files+import Conferer.Source.Null+import Conferer.Types++-- | Default 'SourceCreator' which usese files with @config/{env}.json@+-- template, if the file is not present it will behave like the null source+-- (it has no keys) but if the file doesn't have valid json it will throw an+-- error+mkJsonSource :: SourceCreator+mkJsonSource config = do+ fileToParse <- getFilePathFromEnv config "json"+ fileExists <- doesFileExist fileToParse+ if fileExists+ then do+ value <- decodeStrict' <$> B.readFile fileToParse+ case value of+ Nothing ->+ error $ "Failed to decode file '" <> fileToParse <> "'"+ Just v -> do+ mkJsonSource' v config+ else do+ mkNullSource config++-- | Just like 'mkJsonSource' but accepts the json value as a parameter+mkJsonSource' :: Value -> SourceCreator+mkJsonSource' v = \_config ->+ return $ Source+ { getKeyInSource = \k -> do+ return $ traverseJSON k v+ }++-- | Traverse a 'Value' using a 'Key' to get a value for conferer ('Text').+--+-- This function can nest objects and arrays when keys are nested+--+-- @+-- 'traverseJSON' "a.b" {a: {b: 12}} == Just "12"+-- 'traverseJSON' "a.b" {a: {b: false}} == Just "false"+-- 'traverseJSON' "a" {a: {b: false}} == Nothing+-- 'traverseJSON' "1" [false, true] == Just "true"+-- 'traverseJSON' "0.a" [{a: "hi"}] == Just "hi"+-- 'traverseJSON' "0" [] == Nothing+-- @+traverseJSON :: Key -> Value -> Maybe Text+traverseJSON (Path []) v = valueToText v+traverseJSON (Path (k:ks)) (Object o) =+ HashMap.lookup k o >>= traverseJSON (Path ks)+traverseJSON (Path (k:ks)) (Array vs) = do+ n :: Int <- readMaybe $ Text.unpack k+ value <- vs !? n+ traverseJSON (Path ks) value+traverseJSON (Path _) _ = Nothing++valueToText :: Value -> Maybe Text+valueToText (String t) = Just t+valueToText (Object _o) = Nothing+valueToText (Array _as) = Nothing+valueToText (Number n) = Just $ Text.decodeUtf8 $ L.toStrict $ encode $ Number n+valueToText (Bool b) = Just $ boolToString b+valueToText (Null) = Nothing++boolToString :: Bool -> Text+boolToString True = "true"+boolToString False = "false"++-- | Because we use an old version of 'aeson'+resultToMaybe :: Result a -> Maybe a+resultToMaybe (Error _) = Nothing+resultToMaybe (Success a) = Just a+
+ test/Conferer/Source/JSONSpec.hs view
@@ -0,0 +1,59 @@+module Conferer.Source.JSONSpec where++import Data.Aeson.QQ+import Test.Hspec++import Conferer+import Conferer.Source.JSON++spec :: Spec+spec = do+ describe "json source" $ do+ it "getting an existing path returns the right value" $ do+ c <- emptyConfig+ & addSource (mkJsonSource' [aesonQQ| {"postgres": {"url": "some url", "ssl": true}} |])+ res <- getKey "postgres.url" c+ res `shouldBe` Just "some url"++ it "getting an non existing path returns nothing" $ do+ c <- emptyConfig+ & addSource (mkJsonSource' [aesonQQ| {"postgres": {"url": "some url", "ssl": true}} |])+ res <- getKey "some.path" c+ res `shouldBe` Nothing++ describe "with an array" $ do+ it "getting a path with number gets the right value" $ do+ c <- emptyConfig+ & addSource (mkJsonSource'+ [aesonQQ| {"key": ["value"]} |])+ res <- getKey "key.0" c+ res `shouldBe` Just "value"++ describe "with an object" $ do+ it "getting an existing path returns nothing" $ do+ c <- emptyConfig+ & addSource (mkJsonSource'+ [aesonQQ| {"key": { "path": "value"}} |])+ res <- getKey "key" c+ res `shouldBe` Nothing++ describe "with an int" $ do+ it "getting an existing path returns the right value" $ do+ c <- emptyConfig+ & addSource (mkJsonSource' [aesonQQ| {"key": 1} |])+ res <- getKey "key" c+ res `shouldBe` Just "1"++ describe "with a float" $ do+ it "getting an existing path returns the right value" $ do+ c <- emptyConfig+ & addSource (mkJsonSource' [aesonQQ| {"key": 1.2} |])+ res <- getKey "key" c+ res `shouldBe` Just "1.2"++ describe "with a boolean" $ do+ it "getting an existing path returns the right value" $ do+ c <- emptyConfig+ & addSource (mkJsonSource' [aesonQQ| {"key": false} |])+ res <- getKey "key" c+ res `shouldBe` Just "false"
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}