stratosphere 0.6.0 → 0.7.0
raw patch · 8 files changed
+119/−40 lines, 8 filesdep +hspecdep +hspec-discoverdep −tastydep −tasty-hspec
Dependencies added: hspec, hspec-discover
Dependencies removed: tasty, tasty-hspec
Files
- CHANGELOG.md +11/−0
- library/Stratosphere/ResourceAttributes/AutoScalingRollingUpdatePolicy.hs +2/−2
- library/Stratosphere/Values.hs +37/−14
- stratosphere.cabal +14/−12
- tests/HLint.hs +1/−0
- tests/Main.hs +0/−12
- tests/Spec.hs +1/−0
- tests/Stratosphere/ValuesSpec.hs +53/−0
CHANGELOG.md view
@@ -1,5 +1,16 @@ # Change Log +## 0.7.0++* Made `Val` and `ValList` more type-safe by moving some constructors to+ `ValList` and being more specific with types in functions that only accept+ `Text` parameters. Specific examples include:+ - `Join` and `Select` now require a `ValList` argument+ - `Base64` and `Join` now work only on `Val Text`, not `Val a`+ - `GetAZs` and `Split` are now in `ValList`, not `Val`+* Created `ImportValueList` as a `ValList` alternative to `ImportValue`.+* Added support for `Fn::Sub` intrinsic function.+ ## 0.6.0 * **BREAKING CHANGE**: Added `ValList` type. This new type allows you to
library/Stratosphere/ResourceAttributes/AutoScalingRollingUpdatePolicy.hs view
@@ -23,7 +23,7 @@ , _autoScalingRollingUpdatePolicyMinInstancesInService :: Maybe (Val Integer) , _autoScalingRollingUpdatePolicyMinSuccessfulInstancesPercent :: Maybe (Val Integer) , _autoScalingRollingUpdatePolicyPauseTime :: Maybe (Val Text)- , _autoScalingRollingUpdatePolicySuspendProcess :: Maybe [Val Text]+ , _autoScalingRollingUpdatePolicySuspendProcess :: Maybe (ValList Text) , _autoScalingRollingUpdatePolicyWaitOnResourceSignals :: Maybe (Val Bool) } deriving (Show, Eq) @@ -110,7 +110,7 @@ -- execute scaling policies associated with an alarm. For valid values, see -- the ScalingProcesses.member.N parameter for the SuspendProcesses action in -- the Auto Scaling API Reference.-asrupSuspendProcess :: Lens' AutoScalingRollingUpdatePolicy (Maybe [Val Text])+asrupSuspendProcess :: Lens' AutoScalingRollingUpdatePolicy (Maybe (ValList Text)) asrupSuspendProcess = lens _autoScalingRollingUpdatePolicySuspendProcess (\s a -> s { _autoScalingRollingUpdatePolicySuspendProcess = a }) -- | Specifies whether the Auto Scaling group waits on signals from new
library/Stratosphere/Values.hs view
@@ -8,6 +8,7 @@ module Stratosphere.Values ( Val (..)+ , sub , ValList (..) , Integer' (..) , Bool' (..)@@ -16,6 +17,7 @@ ) where import Data.Aeson+import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HM import Data.String (IsString (..)) import Data.Text (Text)@@ -42,13 +44,12 @@ | Equals (Val Bool) (Val Bool) | Or (Val Bool) (Val Bool) | GetAtt Text Text- | Base64 (Val a)- | Join Text [Val a]- | Split Text Text- | Select Integer (Val a)- | GetAZs (Val a)+ | Base64 (Val Text)+ | Join Text (ValList Text)+ | Select Integer (ValList a) | FindInMap (Val a) (Val a) (Val a) -- ^ Map name, top level key, and second level key | ImportValue Text -- ^ The account-and-region-unique exported name of the value to import+ | Sub Text (Maybe (HashMap Text (Val Text))) -- ^ Substitution string and optional map of values deriving instance (Show a) => Show (Val a) deriving instance (Eq a) => Eq (Val a)@@ -67,16 +68,23 @@ toJSON (GetAtt x y) = mkFunc "Fn::GetAtt" [toJSON x, toJSON y] toJSON (Base64 v) = object [("Fn::Base64", toJSON v)] toJSON (Join d vs) = mkFunc "Fn::Join" [toJSON d, toJSON vs]- toJSON (Split d s) = mkFunc "Fn::Split" [toJSON d, toJSON s] toJSON (Select i vs) = mkFunc "Fn::Select" [toJSON (Integer' i), toJSON vs]- toJSON (GetAZs r) = object [("Fn::GetAZs", toJSON r)] toJSON (FindInMap mapName topKey secondKey) = object [("Fn::FindInMap", toJSON [toJSON mapName, toJSON topKey, toJSON secondKey])]- toJSON (ImportValue t) = object [("Fn::ImportValue", toJSON t)]+ toJSON (ImportValue t) = importValueToJSON t+ toJSON (Sub s Nothing) = object [("Fn::Sub", toJSON s)]+ toJSON (Sub s (Just vals)) = mkFunc "Fn::Sub" [toJSON s, Object (toJSON <$> vals)] +-- | Simple version of 'Sub' without a map of values.+sub :: Text -> Val Text+sub s = Sub s Nothing+ refToJSON :: Text -> Value refToJSON ref = object [("Ref", toJSON ref)] +importValueToJSON :: Text -> Value+importValueToJSON ref = object [("Fn::ImportValue", toJSON ref)]+ mkFunc :: Text -> [Value] -> Value mkFunc name args = object [(name, Array $ fromList args)] @@ -92,27 +100,33 @@ [("Fn::GetAtt", o')] -> uncurry GetAtt <$> parseJSON o' [("Fn::Base64", o')] -> Base64 <$> parseJSON o' [("Fn::Join", o')] -> uncurry Join <$> parseJSON o'- [("Fn::Split", o')] -> uncurry Split <$> parseJSON o' [("Fn::Select", o')] -> (\(x, y) -> Select (unInteger' x) y) <$> parseJSON o'- [("Fn::GetAZs", o')] -> GetAZs <$> parseJSON o' [("Fn::FindInMap", o')] -> do (mapName, topKey, secondKey) <- parseJSON o' return (FindInMap mapName topKey secondKey) [("Fn::ImportValue", o')] -> ImportValue <$> parseJSON o'+ [("Fn::Sub", vals)] ->+ case vals of+ (String s) -> return $ Sub s Nothing+ otherwise' -> uncurry Sub <$> parseJSON otherwise' [(n, o')] -> Literal <$> parseJSON (object [(n, o')]) os -> Literal <$> parseJSON (object os) parseJSON v = Literal <$> parseJSON v --- | There are two ways to construct a list of 'Val's. One is to use a literal--- 'ValList' to construct the list. The other is to reference something that is--- already a list. For example, if you have a parameter called @SubnetIds@ of--- type @List<AWS::EC2::Subnet::Id>@ then, you can use @RefList "SubnetIds"@ to+-- | 'ValList' is like 'Val', except it is used in place of lists of Vals in+-- templates. For example, if you have a parameter called @SubnetIds@ of type+-- @List<AWS::EC2::Subnet::Id>@ then, you can use @RefList "SubnetIds"@ to -- reference it. data ValList a = ValList [Val a] | RefList Text+ | ImportValueList Text+ | Split Text Text+ | GetAZs (Val Text) deriving (Show, Eq) +deriving instance Functor ValList+ instance IsList (ValList a) where type Item (ValList a) = Val a fromList = ValList@@ -121,16 +135,25 @@ -- This is obviously not meaningful, but the IsList instance is so useful -- that I decided to allow it. toList (RefList _) = []+ toList (ImportValueList _) = []+ toList (Split _ _) = []+ toList (GetAZs _) = [] instance (ToJSON a) => ToJSON (ValList a) where toJSON (ValList vals) = toJSON vals toJSON (RefList ref) = refToJSON ref+ toJSON (ImportValueList ref) = importValueToJSON ref+ toJSON (Split d s) = mkFunc "Fn::Split" [toJSON d, toJSON s]+ toJSON (GetAZs r) = object [("Fn::GetAZs", toJSON r)] instance (FromJSON a) => FromJSON (ValList a) where parseJSON a@(Array _) = ValList <$> parseJSON a parseJSON (Object o) = case HM.toList o of [("Ref", o')] -> RefList <$> parseJSON o'+ [("Fn::ImportValue", o')] -> ImportValueList <$> parseJSON o'+ [("Fn::Split", o')] -> uncurry Split <$> parseJSON o'+ [("Fn::GetAZs", o')] -> GetAZs <$> parseJSON o' _ -> fail "Could not parse object into RefList" parseJSON _ = fail "Expected Array or Object for ValList in parseJSON"
stratosphere.cabal view
@@ -3,7 +3,7 @@ -- see: https://github.com/sol/hpack name: stratosphere-version: 0.6.0+version: 0.7.0 synopsis: EDSL for AWS CloudFormation description: EDSL for AWS CloudFormation category: AWS, Cloud@@ -733,9 +733,9 @@ buildable: False default-language: Haskell2010 -test-suite style+test-suite spec type: exitcode-stdio-1.0- main-is: HLint.hs+ main-is: Spec.hs hs-source-dirs: tests build-depends:@@ -749,15 +749,17 @@ , text >= 1.1 , unordered-containers >= 0.2 , base- , directory- , hlint+ , stratosphere+ , hspec+ , hspec-discover other-modules:- Main+ HLint+ Stratosphere.ValuesSpec default-language: Haskell2010 -test-suite tasty+test-suite style type: exitcode-stdio-1.0- main-is: Main.hs+ main-is: HLint.hs hs-source-dirs: tests build-depends:@@ -771,9 +773,9 @@ , text >= 1.1 , unordered-containers >= 0.2 , base- , stratosphere- , tasty- , tasty-hspec+ , directory+ , hlint other-modules:- HLint+ Spec+ Stratosphere.ValuesSpec default-language: Haskell2010
tests/HLint.hs view
@@ -10,6 +10,7 @@ , "tests" , "-i", "Use newtype instead of data" , "-i", "Unused LANGUAGE pragma"+ , "-i", "Redundant do" ] main :: IO ()
− tests/Main.hs
@@ -1,12 +0,0 @@-import qualified Test.Tasty-import Test.Tasty.Hspec--main :: IO ()-main = do- test <- testSpec "stratosphere" spec- Test.Tasty.defaultMain test--spec :: Spec-spec = parallel $- it "is trivially true" $- True `shouldBe` True
+ tests/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ tests/Stratosphere/ValuesSpec.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE OverloadedStrings #-}++module Stratosphere.ValuesSpec+ ( spec+ ) where++import Data.Aeson+import Data.Text (Text)+import Test.Hspec++import Stratosphere.Values++spec :: Spec+spec = do+ describe "Val/ValList JSON instances" $ do+ it "is idempotent for Sub" $ do+ idempotentJSON (sub "MyString")+ idempotentJSON (Sub "${Greeting} ${Name}" (Just []) :: Val Text)+ let+ values =+ [ ("Greeting", "Hello")+ , ("Name", "Bob")+ ]+ idempotentJSON (Sub "${Greeting} ${Name}" (Just values) :: Val Text)++ it "Ref and RefList produce the same JSON" $ do+ toJSON (Ref "MyVal" :: Val Text) `shouldBe` toJSON (RefList "MyVal" :: ValList Text)++ it "ImportValue and ImportValueList produce the same JSON" $ do+ toJSON (ImportValue "MyVal" :: Val Text) `shouldBe` toJSON (ImportValueList "MyVal" :: ValList Text)++ it "Functor conversions in nested Vals work" $ do+ let+ input :: Val Integer+ input =+ Select 1 $+ ValList+ [ Literal 1+ , Ref "Hello"+ , Literal 2+ ]+ expected =+ Select 1 $+ ValList+ [ Literal 2+ , Ref "Hello"+ , Literal 3+ ]+ fmap (+1) input `shouldBe` expected++idempotentJSON :: (ToJSON a, FromJSON a, Show a, Eq a) => a -> Expectation+idempotentJSON x = decode (encode x) `shouldBe` Just x