packages feed

conferer-provider-json (empty) → 0.1.0.1

raw patch · 7 files changed

+270/−0 lines, 7 filesdep +aesondep +basedep +bytestringsetup-changed

Dependencies added: aeson, base, bytestring, conferer, conferer-provider-json, directory, hspec, text, unordered-containers, vector

Files

+ 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-provider-json
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ conferer-provider-json.cabal view
@@ -0,0 +1,65 @@+cabal-version: 1.18++-- This file has been generated from package.yaml by hpack version 0.31.2.+--+-- see: https://github.com/sol/hpack+--+-- hash: d31257113b709deb8fcf8f95e8c61e556cf1ee4f70ead71346016f39c9b7e7ab++name:           conferer-provider-json+version:        0.1.0.1+synopsis:       Configuration 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.Provider.JSON+  other-modules:+      Paths_conferer_provider_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.1.0.1+    , directory >=1.2 && <2.0+    , text >=1.1 && <1.3+    , unordered-containers+    , vector+  default-language: Haskell2010++test-suite conferer-provider-json-test+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      Conferer.Provider.JSONSpec+      Paths_conferer_provider_json+  hs-source-dirs:+      test+  default-extensions: OverloadedStrings LambdaCase QuasiQuotes ScopedTypeVariables+  build-depends:+      aeson >=0.10 && <2.0+    , base >=4.3 && <5+    , bytestring >=0.10 && <0.11+    , conferer+    , conferer-provider-json+    , directory >=1.2 && <2.0+    , hspec+    , text >=1.1 && <1.3+    , unordered-containers+    , vector+  default-language: Haskell2010
+ src/Conferer/Provider/JSON.hs view
@@ -0,0 +1,121 @@+module Conferer.Provider.JSON+  (+  -- * How to use this provider+  -- | As any other provider you can add it to a config using the 'addProvider'+  -- function. There are a couple of oddities that come from supporting many+  -- different providers which do not support numbers, arrays or objects+  -- nativelly+  --+  -- @+  -- import 'Conferer'+  -- import Conferer.Provider.JSON ('mkJsonProvider')+  --+  -- main = do+  --   config <-+  --     'defaultConfig' \"awesomeapp\"+  --     & 'addProvider' 'mkJsonProvider'+  --   warpSettings <- 'getFromConfig' \"warp\" config+  --   runSettings warpSettings application+  -- @+  --+  -- This will result on a provider 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+  mkJsonProvider+  , mkJsonProvider'++  -- * 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.Provider.Files+import Conferer.Provider.Null+import Conferer.Types++-- | Default 'ProviderCreator' which usese files with @config/{env}.json@+-- template, if the file is not present it will behave like the null provider+-- (it has no keys) but if the file doesn't have valid json it will throw an+-- error+mkJsonProvider :: ProviderCreator+mkJsonProvider 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+          mkJsonProvider' v config+    else do+      mkNullProvider config++-- | Just like 'mkJsonProvider' but accepts the json value as a parameter+mkJsonProvider' :: Value -> ProviderCreator+mkJsonProvider' v = \_config ->+  return $ Provider+  { getKeyInProvider = \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/Provider/JSONSpec.hs view
@@ -0,0 +1,59 @@+module Conferer.Provider.JSONSpec where++import Data.Aeson.QQ.Simple+import Test.Hspec++import Conferer+import Conferer.Provider.JSON++spec :: Spec+spec = do+  describe "json provider" $ do+    it "getting an existing path returns the right value" $ do+      c <- emptyConfig+           & addProvider (mkJsonProvider' [aesonQQ| {"postgres": {"url": "some url", "ssl": true}} |])+      res <- getKey "postgres.url" c+      res `shouldBe` Right "some url"++    it "getting an non existing path returns nothing" $ do+      c <- emptyConfig+           & addProvider (mkJsonProvider' [aesonQQ| {"postgres": {"url": "some url", "ssl": true}} |])+      res <- getKey "some.path" c+      res `shouldBe` Left "Key 'some.path' was not found"++    describe "with an array" $ do+      it "getting a path with number gets the right value" $ do+        c <- emptyConfig+             & addProvider (mkJsonProvider'+                          [aesonQQ| {"key": ["value"]} |])+        res <- getKey "key.0" c+        res `shouldBe` Right "value"++    describe "with an object" $ do+      it "getting an existing path returns nothing" $ do+        c <- emptyConfig+           & addProvider (mkJsonProvider'+            [aesonQQ| {"key": { "path": "value"}} |])+        res <- getKey "key" c+        res `shouldBe` Left "Key 'key' was not found"++    describe "with an int" $ do+      it "getting an existing path returns the right value" $ do+        c <- emptyConfig+           & addProvider (mkJsonProvider' [aesonQQ| {"key": 1} |])+        res <- getKey "key" c+        res `shouldBe` Right "1"++    describe "with a float" $ do+      it "getting an existing path returns the right value" $ do+        c <- emptyConfig+           & addProvider (mkJsonProvider' [aesonQQ| {"key": 1.2} |])+        res <- getKey "key" c+        res `shouldBe` Right "1.2"++    describe "with a boolean" $ do+      it "getting an existing path returns the right value" $ do+        c <- emptyConfig+           & addProvider (mkJsonProvider' [aesonQQ| {"key": false} |])+        res <- getKey "key" c+        res `shouldBe` Right "false"
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}