Merge pull request #4 from Start9Labs/bugfix/version-spec-resilience

Bugfix/version spec resilience
This commit is contained in:
Keagan McClelland
2020-02-27 21:05:41 -07:00
committed by GitHub
7 changed files with 41 additions and 27 deletions

View File

@@ -11,17 +11,18 @@ import Startlude
import Control.Monad.Logger import Control.Monad.Logger
import Data.Aeson import Data.Aeson
import qualified Data.ByteString.Lazy as BS import qualified Data.ByteString.Lazy as BS
import Data.Char
import Data.Conduit import Data.Conduit
import qualified Data.Conduit.Binary as CB import qualified Data.Conduit.Binary as CB
import qualified Data.Text as T
import qualified GHC.Show (Show (..)) import qualified GHC.Show (Show (..))
import Network.HTTP.Types
import System.Directory import System.Directory
import Yesod.Core import Yesod.Core
import Foundation import Foundation
import Handler.Types.Status
import Lib.Registry import Lib.Registry
import Lib.Semver import Lib.Semver
import Lib.Types.Semver
import System.FilePath ((<.>)) import System.FilePath ((<.>))
import System.Posix.Files (fileSize, getFileStatus) import System.Posix.Files (fileSize, getFileStatus)
@@ -47,7 +48,10 @@ getAppR = getApp appResourceDir
getApp :: KnownSymbol a => FilePath -> Extension a -> Handler TypedContent getApp :: KnownSymbol a => FilePath -> Extension a -> Handler TypedContent
getApp rootDir ext = do getApp rootDir ext = do
spec <- querySpecD mostRecentVersion <$> lookupGetParam "spec" specString <- T.filter (not . isSpace) . fromMaybe "*" <$> lookupGetParam "spec"
spec <- case readMaybe specString of
Nothing -> sendResponseStatus status400 ("Invalid App Version Specification" :: Text)
Just t -> pure t
appVersions <- liftIO $ getAvailableAppVersions rootDir ext appVersions <- liftIO $ getAvailableAppVersions rootDir ext
putStrLn $ "valid appversion for " <> (show ext :: String) <> ": " <> show appVersions putStrLn $ "valid appversion for " <> (show ext :: String) <> ": " <> show appVersions
case getSpecifiedAppVersion spec appVersions of case getSpecifiedAppVersion spec appVersions of

View File

@@ -29,9 +29,3 @@ instance ToContent (Maybe AppVersionRes) where
toContent = toContent . toJSON toContent = toContent . toJSON
instance ToTypedContent (Maybe AppVersionRes) where instance ToTypedContent (Maybe AppVersionRes) where
toTypedContent = toTypedContent . toJSON toTypedContent = toTypedContent . toJSON
querySpec :: Maybe Text -> Maybe AppVersionSpecification
querySpec = (readMaybe . toS =<<)
querySpecD :: AppVersionSpecification -> Maybe Text -> AppVersionSpecification
querySpecD defaultSpec = fromMaybe defaultSpec . querySpec

View File

@@ -7,8 +7,10 @@ module Handler.Version where
import Startlude import Startlude
import Control.Monad.Trans.Maybe import Control.Monad.Trans.Maybe
import Data.Char
import qualified Data.HashMap.Strict as HM import qualified Data.HashMap.Strict as HM
import Data.String.Interpolate.IsString import Data.String.Interpolate.IsString
import qualified Data.Text as T
import Network.HTTP.Types import Network.HTTP.Types
import Yesod.Core import Yesod.Core
@@ -39,7 +41,10 @@ getVersionSysR sysAppId = runMaybeT $ do
getVersionWSpec :: KnownSymbol a => FilePath -> Extension a -> Handler (Maybe AppVersionRes) getVersionWSpec :: KnownSymbol a => FilePath -> Extension a -> Handler (Maybe AppVersionRes)
getVersionWSpec rootDir ext = do getVersionWSpec rootDir ext = do
spec <- querySpecD mostRecentVersion <$> lookupGetParam "spec" specString <- T.filter (not . isSpace) . fromMaybe "*" <$> lookupGetParam "spec"
spec <- case readMaybe specString of
Nothing -> sendResponseStatus status400 ("Invalid App Version Specification" :: Text)
Just t -> pure t
appVersions <- liftIO $ getAvailableAppVersions rootDir ext appVersions <- liftIO $ getAvailableAppVersions rootDir ext
let av = version <$> getSpecifiedAppVersion spec appVersions let av = version <$> getSpecifiedAppVersion spec appVersions
pure $ liftA2 AppVersionRes av (pure Nothing) pure $ liftA2 AppVersionRes av (pure Nothing)

View File

@@ -42,7 +42,7 @@ instance HasAppVersion RegisteredAppVersion where
-- retrieve all valid semver folder names with queried for file: rootDirectory/appId/[0.0.0 ...]/appId.extension -- retrieve all valid semver folder names with queried for file: rootDirectory/appId/[0.0.0 ...]/appId.extension
getAvailableAppVersions :: KnownSymbol a => FilePath -> Extension a -> IO [RegisteredAppVersion] getAvailableAppVersions :: KnownSymbol a => FilePath -> Extension a -> IO [RegisteredAppVersion]
getAvailableAppVersions rootDirectory ext@(Extension appId) = do getAvailableAppVersions rootDirectory ext@(Extension appId) = do
versions <- mapMaybe readMaybe <$> getSubDirectories (rootDirectory </> appId) versions <- mapMaybe (readMaybe . toS) <$> getSubDirectories (rootDirectory </> appId)
fmap catMaybes . for versions $ \v -> fmap catMaybes . for versions $ \v ->
getVersionedFileFromDir rootDirectory ext v getVersionedFileFromDir rootDirectory ext v
>>= \case >>= \case

View File

@@ -5,6 +5,7 @@ import Startlude
import Lib.Types.Semver import Lib.Types.Semver
(<||) :: HasAppVersion a => a -> AppVersionSpecification -> Bool (<||) :: HasAppVersion a => a -> AppVersionSpecification -> Bool
(<||) _ AppVersionAny = True
(<||) a (AppVersionSpecification SVEquals av1) = version a == av1 (<||) a (AppVersionSpecification SVEquals av1) = version a == av1
(<||) a (AppVersionSpecification SVLessThan av1) = version a < av1 (<||) a (AppVersionSpecification SVLessThan av1) = version a < av1
(<||) a (AppVersionSpecification SVGreaterThan av1) = version a > av1 (<||) a (AppVersionSpecification SVGreaterThan av1) = version a > av1

View File

@@ -38,8 +38,8 @@ instance Show AppVersion where
instance IsString AppVersion where instance IsString AppVersion where
fromString s = case traverse (readMaybe . toS) $ splitOn "+" <=< splitOn "." $ (toS s) of fromString s = case traverse (readMaybe . toS) $ splitOn "+" <=< splitOn "." $ (toS s) of
Just [major, minor, patch, build] -> AppVersion (major, minor, patch, build) Just [major, minor, patch, build] -> AppVersion (major, minor, patch, build)
Just [major, minor, patch] -> AppVersion (major, minor, patch, 0) Just [major, minor, patch] -> AppVersion (major, minor, patch, 0)
_ -> panic . toS $ "Invalid App Version: " <> s _ -> panic . toS $ "Invalid App Version: " <> s
instance ToJSON AppVersion where instance ToJSON AppVersion where
toJSON = String . show toJSON = String . show
instance FromJSON AppVersion where instance FromJSON AppVersion where
@@ -47,7 +47,7 @@ instance FromJSON AppVersion where
case traverse (decode . toS) $ splitOn "+" <=< splitOn "." $ t of case traverse (decode . toS) $ splitOn "+" <=< splitOn "." $ t of
Just [a, b, c, d] -> pure $ AppVersion (a, b, c, d) Just [a, b, c, d] -> pure $ AppVersion (a, b, c, d)
Just [a, b, c] -> pure $ AppVersion (a, b, c, 0) Just [a, b, c] -> pure $ AppVersion (a, b, c, 0)
_ -> fail "unknown versioning" _ -> fail "unknown versioning"
instance ToTypedContent AppVersion where instance ToTypedContent AppVersion where
toTypedContent = toTypedContent . toJSON toTypedContent = toTypedContent . toJSON
instance ToContent AppVersion where instance ToContent AppVersion where
@@ -62,16 +62,17 @@ instance FromJSONKey AppVersion where
-- Semver AppVersionSpecification -- Semver AppVersionSpecification
------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------
data AppVersionSpecification = AppVersionSpecification data AppVersionSpecification =
{ requestModifier :: SemverRequestModifier AppVersionAny
, baseVersion :: AppVersion | AppVersionSpecification SemverRequestModifier AppVersion
}
instance Read AppVersionSpecification where instance Read AppVersionSpecification where
readsPrec _ s = readsPrec _ s =
case (readMaybe . toS $ svMod, readMaybe . toS $ version) of if s == "*"
(Just m, Just av) -> [(AppVersionSpecification m av, "")] then [(AppVersionAny, "")]
_ -> [] else case (readMaybe . toS $ svMod, readMaybe . toS $ version) of
(Just m, Just av) -> [(AppVersionSpecification m av, "")]
_ -> []
where where
(svMod, version) = break isDigit . toS $ s (svMod, version) = break isDigit . toS $ s
@@ -80,15 +81,19 @@ instance PathPiece AppVersionSpecification where
toPathPiece = show toPathPiece = show
instance Show AppVersionSpecification where instance Show AppVersionSpecification where
show AppVersionAny = "*"
show (AppVersionSpecification r b) = show r <> show b show (AppVersionSpecification r b) = show r <> show b
instance ToJSON AppVersionSpecification where instance ToJSON AppVersionSpecification where
toJSON = String . show toJSON = String . show
instance FromJSON AppVersionSpecification where instance FromJSON AppVersionSpecification where
parseJSON = withText "app version spec" $ \t -> do parseJSON = withText "app version spec" $ \t ->
let (svMod, version) = break isDigit t if t == "*"
baseVersion <- parseJSON . String $ version then pure AppVersionAny
requestModifier <- parseJSON . String $ svMod else do
pure $ AppVersionSpecification {..} let (svMod, version) = break isDigit t
baseVersion <- parseJSON . String $ version
requestModifier <- parseJSON . String $ svMod
pure $ AppVersionSpecification requestModifier baseVersion
mostRecentVersion :: AppVersionSpecification mostRecentVersion :: AppVersionSpecification
mostRecentVersion = AppVersionSpecification SVGreaterThanEq $ AppVersion (0,0,0,0) mostRecentVersion = AppVersionSpecification SVGreaterThanEq $ AppVersion (0,0,0,0)

View File

@@ -10,7 +10,12 @@ import Control.Error.Util as X
import Data.Coerce as X import Data.Coerce as X
import Data.String as X (String, fromString) import Data.String as X (String, fromString)
import Data.Time.Clock as X import Data.Time.Clock as X
import Protolude as X hiding (bool, hush, isLeft, isRight, note, tryIO, (<.>)) import Protolude as X hiding (bool, hush, isLeft, isRight, note, readMaybe, tryIO, (<.>))
import qualified Protolude as P (readMaybe)
id :: a -> a id :: a -> a
id = identity id = identity
readMaybe :: Read a => Text -> Maybe a
readMaybe = P.readMaybe . toS
{-# INLINE readMaybe #-}