filter packages for os compatibility before fetching metadata

This commit is contained in:
Lucy Cifferello
2022-07-09 08:35:58 -06:00
parent 18b951388b
commit 2cde1a2d8e
9 changed files with 279 additions and 71 deletions

View File

@@ -2,30 +2,49 @@
module Handler.Package.V0.Icon where module Handler.Package.V0.Icon where
import Conduit (awaitForever, (.|)) import Conduit (
awaitForever,
(.|),
)
import Data.String.Interpolate.IsString ( import Data.String.Interpolate.IsString (
i, i,
) )
import Foundation (Handler) import Foundation (Handler)
import Handler.Package.V1.Index (getOsVersionQuery)
import Handler.Util ( import Handler.Util (
filterOsCompat,
getVersionSpecFromQuery, getVersionSpecFromQuery,
orThrow, orThrow,
versionPriorityFromQueryIsMin, versionPriorityFromQueryIsMin,
) )
import Lib.Error (S9Error (..)) import Lib.Error (S9Error (..))
import Lib.PkgRepository (getBestVersion, getIcon) import Lib.PkgRepository (
getBestVersion,
getIcon,
)
import Lib.Types.Core (PkgId) import Lib.Types.Core (PkgId)
import Network.HTTP.Types (status400) import Network.HTTP.Types (status400)
import Startlude (show, ($)) import Startlude (
import Yesod (TypedContent, addHeader, respondSource, sendChunkBS, sendResponseStatus) show,
($),
)
import Yesod (
TypedContent,
addHeader,
respondSource,
sendChunkBS,
sendResponseStatus,
)
getIconsR :: PkgId -> Handler TypedContent getIconsR :: PkgId -> Handler TypedContent
getIconsR pkg = do getIconsR pkg = do
osVersion <- getOsVersionQuery
osCompatibleVersions <- filterOsCompat osVersion pkg
spec <- getVersionSpecFromQuery spec <- getVersionSpecFromQuery
preferMin <- versionPriorityFromQueryIsMin preferMin <- versionPriorityFromQueryIsMin
version <- version <-
getBestVersion pkg spec preferMin getBestVersion spec preferMin osCompatibleVersions
`orThrow` sendResponseStatus status400 (NotFoundE [i|Icon for #{pkg} satisfying #{spec}|]) `orThrow` sendResponseStatus status400 (NotFoundE [i|Icon for #{pkg} satisfying #{spec}|])
(ct, len, src) <- getIcon pkg version (ct, len, src) <- getIcon pkg version
addHeader "Content-Length" (show len) addHeader "Content-Length" (show len)

View File

@@ -2,24 +2,50 @@
module Handler.Package.V0.Instructions where module Handler.Package.V0.Instructions where
import Conduit (awaitForever, (.|)) import Conduit (
import Data.String.Interpolate.IsString (i) awaitForever,
(.|),
)
import Data.String.Interpolate.IsString (
i,
)
import Foundation (Handler) import Foundation (Handler)
import Handler.Util (getVersionSpecFromQuery, orThrow, versionPriorityFromQueryIsMin) import Handler.Package.V1.Index (getOsVersionQuery)
import Handler.Util (
filterOsCompat,
getVersionSpecFromQuery,
orThrow,
versionPriorityFromQueryIsMin,
)
import Lib.Error (S9Error (..)) import Lib.Error (S9Error (..))
import Lib.PkgRepository (getBestVersion, getInstructions) import Lib.PkgRepository (
getBestVersion,
getInstructions,
)
import Lib.Types.Core (PkgId) import Lib.Types.Core (PkgId)
import Network.HTTP.Types (status400) import Network.HTTP.Types (status400)
import Startlude (show, ($)) import Startlude (
import Yesod (TypedContent, addHeader, respondSource, sendChunkBS, sendResponseStatus, typePlain) show,
($),
)
import Yesod (
TypedContent,
addHeader,
respondSource,
sendChunkBS,
sendResponseStatus,
typePlain,
)
getInstructionsR :: PkgId -> Handler TypedContent getInstructionsR :: PkgId -> Handler TypedContent
getInstructionsR pkg = do getInstructionsR pkg = do
spec <- getVersionSpecFromQuery spec <- getVersionSpecFromQuery
osVersion <- getOsVersionQuery
osCompatibleVersions <- filterOsCompat osVersion pkg
preferMin <- versionPriorityFromQueryIsMin preferMin <- versionPriorityFromQueryIsMin
version <- version <-
getBestVersion pkg spec preferMin getBestVersion spec preferMin osCompatibleVersions
`orThrow` sendResponseStatus status400 (NotFoundE [i|Instructions for #{pkg} satisfying #{spec}|]) `orThrow` sendResponseStatus status400 (NotFoundE [i|Instructions for #{pkg} satisfying #{spec}|])
(len, src) <- getInstructions pkg version (len, src) <- getInstructions pkg version
addHeader "Content-Length" (show len) addHeader "Content-Length" (show len)

View File

@@ -2,24 +2,50 @@
module Handler.Package.V0.License where module Handler.Package.V0.License where
import Conduit (awaitForever, (.|)) import Conduit (
import Data.String.Interpolate.IsString (i) awaitForever,
(.|),
)
import Data.String.Interpolate.IsString (
i,
)
import Foundation (Handler) import Foundation (Handler)
import Handler.Util (getVersionSpecFromQuery, orThrow, versionPriorityFromQueryIsMin) import Handler.Package.V1.Index (getOsVersionQuery)
import Handler.Util (
filterOsCompat,
getVersionSpecFromQuery,
orThrow,
versionPriorityFromQueryIsMin,
)
import Lib.Error (S9Error (..)) import Lib.Error (S9Error (..))
import Lib.PkgRepository (getBestVersion, getLicense) import Lib.PkgRepository (
getBestVersion,
getLicense,
)
import Lib.Types.Core (PkgId) import Lib.Types.Core (PkgId)
import Network.HTTP.Types (status400) import Network.HTTP.Types (status400)
import Startlude (show, ($)) import Startlude (
import Yesod (TypedContent, addHeader, respondSource, sendChunkBS, sendResponseStatus, typePlain) show,
($),
)
import Yesod (
TypedContent,
addHeader,
respondSource,
sendChunkBS,
sendResponseStatus,
typePlain,
)
getLicenseR :: PkgId -> Handler TypedContent getLicenseR :: PkgId -> Handler TypedContent
getLicenseR pkg = do getLicenseR pkg = do
osVersion <- getOsVersionQuery
osCompatibleVersions <- filterOsCompat osVersion pkg
spec <- getVersionSpecFromQuery spec <- getVersionSpecFromQuery
preferMin <- versionPriorityFromQueryIsMin preferMin <- versionPriorityFromQueryIsMin
version <- version <-
getBestVersion pkg spec preferMin getBestVersion spec preferMin osCompatibleVersions
`orThrow` sendResponseStatus status400 (NotFoundE [i|License for #{pkg} satisfying #{spec}|]) `orThrow` sendResponseStatus status400 (NotFoundE [i|License for #{pkg} satisfying #{spec}|])
(len, src) <- getLicense pkg version (len, src) <- getLicense pkg version
addHeader "Content-Length" (show len) addHeader "Content-Length" (show len)

View File

@@ -2,26 +2,53 @@
module Handler.Package.V0.Manifest where module Handler.Package.V0.Manifest where
import Conduit (awaitForever, (.|)) import Conduit (
import Data.String.Interpolate.IsString (i) awaitForever,
(.|),
)
import Data.String.Interpolate.IsString (
i,
)
import Foundation (Handler) import Foundation (Handler)
import Handler.Util (addPackageHeader, getVersionSpecFromQuery, orThrow, versionPriorityFromQueryIsMin) import Handler.Package.V1.Index (getOsVersionQuery)
import Handler.Util (
addPackageHeader,
filterOsCompat,
getVersionSpecFromQuery,
orThrow,
versionPriorityFromQueryIsMin,
)
import Lib.Error (S9Error (..)) import Lib.Error (S9Error (..))
import Lib.PkgRepository (getBestVersion, getManifest) import Lib.PkgRepository (
getBestVersion,
getManifest,
)
import Lib.Types.Core (PkgId) import Lib.Types.Core (PkgId)
import Network.HTTP.Types (status404) import Network.HTTP.Types (status404)
import Startlude (show, ($)) import Startlude (
import Yesod (TypedContent, addHeader, respondSource, sendChunkBS, sendResponseStatus, typeJson) show,
($),
)
import Yesod (
TypedContent,
addHeader,
respondSource,
sendChunkBS,
sendResponseStatus,
typeJson,
)
getAppManifestR :: PkgId -> Handler TypedContent getAppManifestR :: PkgId -> Handler TypedContent
getAppManifestR pkg = do getAppManifestR pkg = do
osVersion <- getOsVersionQuery
osCompatibleVersions <- filterOsCompat osVersion pkg
versionSpec <- getVersionSpecFromQuery versionSpec <- getVersionSpecFromQuery
preferMin <- versionPriorityFromQueryIsMin preferMin <- versionPriorityFromQueryIsMin
version <- version <-
getBestVersion pkg versionSpec preferMin getBestVersion versionSpec preferMin osCompatibleVersions
`orThrow` sendResponseStatus status404 (NotFoundE [i|#{pkg} satisfying #{versionSpec}|]) `orThrow` sendResponseStatus status404 (NotFoundE [i|#{pkg} satisfying #{versionSpec}|])
addPackageHeader pkg version addPackageHeader pkg version
(len, src) <- getManifest pkg version (len, src) <- getManifest pkg version
addHeader "Content-Length" (show len) addHeader "Content-Length" (show len)
respondSource typeJson $ src .| awaitForever sendChunkBS respondSource typeJson $ src .| awaitForever sendChunkBS

View File

@@ -2,23 +2,45 @@
module Handler.Package.V0.ReleaseNotes where module Handler.Package.V0.ReleaseNotes where
import Data.Aeson (ToJSON (..), object, KeyValue((.=))) import Data.Aeson (
KeyValue ((.=)),
ToJSON (..),
object,
)
import Data.Aeson.Key (fromText)
import Data.HashMap.Strict (HashMap) import Data.HashMap.Strict (HashMap)
import Data.HashMap.Strict qualified as HM import Data.HashMap.Strict qualified as HM
import Database.Queries (fetchAllAppVersions) import Foundation (Handler)
import Foundation (Handler, RegistryCtx (..)) import Handler.Package.V1.Index (getOsVersionQuery)
import Handler.Util (filterOsCompat)
import Lib.Types.Core (PkgId) import Lib.Types.Core (PkgId)
import Lib.Types.Emver (Version) import Lib.Types.Emver (Version)
import Model (VersionRecord (..)) import Model (VersionRecord (..))
import Startlude (Down (..), Eq, Show, Text, fst, pure, sortOn, ($), (&&&), (.), (<$>), show) import Startlude (
import Yesod (ToContent (..), ToTypedContent (..), YesodPersist (runDB), getYesod) Down (..),
import Data.Aeson.Key (fromText) Eq,
Show,
Text,
fst,
pure,
show,
sortOn,
($),
(&&&),
(.),
(<$>),
)
import Yesod (
ToContent (..),
ToTypedContent (..),
)
newtype ReleaseNotes = ReleaseNotes {unReleaseNotes :: HashMap Version Text} newtype ReleaseNotes = ReleaseNotes {unReleaseNotes :: HashMap Version Text}
deriving (Eq, Show) deriving (Eq, Show)
instance ToJSON ReleaseNotes where instance ToJSON ReleaseNotes where
toJSON ReleaseNotes {..} = object [ version .= value | (key, value) <- HM.toList unReleaseNotes, let version = fromText $ show key] toJSON ReleaseNotes{..} =
object [version .= value | (key, value) <- HM.toList unReleaseNotes, let version = fromText $ show key]
instance ToContent ReleaseNotes where instance ToContent ReleaseNotes where
toContent = toContent . toJSON toContent = toContent . toJSON
instance ToTypedContent ReleaseNotes where instance ToTypedContent ReleaseNotes where
@@ -27,9 +49,9 @@ instance ToTypedContent ReleaseNotes where
getReleaseNotesR :: PkgId -> Handler ReleaseNotes getReleaseNotesR :: PkgId -> Handler ReleaseNotes
getReleaseNotesR pkg = do getReleaseNotesR pkg = do
appConnPool <- appConnPool <$> getYesod osVersion <- getOsVersionQuery
versionRecords <- runDB $ fetchAllAppVersions appConnPool pkg osCompatibleVersions <- filterOsCompat osVersion pkg
pure $ constructReleaseNotesApiRes versionRecords pure $ constructReleaseNotesApiRes osCompatibleVersions
where where
constructReleaseNotesApiRes :: [VersionRecord] -> ReleaseNotes constructReleaseNotesApiRes :: [VersionRecord] -> ReleaseNotes
constructReleaseNotesApiRes vers = do constructReleaseNotesApiRes vers = do

View File

@@ -4,30 +4,65 @@
module Handler.Package.V0.S9PK where module Handler.Package.V0.S9PK where
import Data.String.Interpolate.IsString (i) import Data.String.Interpolate.IsString (
i,
)
import Data.Text qualified as T import Data.Text qualified as T
import Database.Queries (createMetric, fetchAppVersion) import Database.Queries (
createMetric,
fetchAppVersion,
)
import Foundation (Handler) import Foundation (Handler)
import GHC.Show (show) import GHC.Show (show)
import Handler.Util (addPackageHeader, getVersionSpecFromQuery, orThrow, versionPriorityFromQueryIsMin) import Handler.Package.V1.Index (getOsVersionQuery)
import Handler.Util (
addPackageHeader,
filterOsCompat,
getVersionSpecFromQuery,
orThrow,
versionPriorityFromQueryIsMin,
)
import Lib.Error (S9Error (..)) import Lib.Error (S9Error (..))
import Lib.PkgRepository (getBestVersion, getPackage) import Lib.PkgRepository (
import Lib.Types.Core (PkgId (..), S9PK) getBestVersion,
getPackage,
)
import Lib.Types.Core (
PkgId (..),
S9PK,
)
import Lib.Types.Emver (Version (..)) import Lib.Types.Emver (Version (..))
import Network.HTTP.Types (status404) import Network.HTTP.Types (status404)
import Startlude (Maybe (..), pure, void, ($), (.), (>>=)) import Startlude (
Maybe (..),
pure,
void,
($),
(.),
(>>=),
)
import System.FilePath (takeBaseName) import System.FilePath (takeBaseName)
import Yesod (Content (..), TypedContent, YesodPersist (runDB), notFound, respond, sendResponseStatus, typeOctet) import Yesod (
Content (..),
TypedContent,
YesodPersist (runDB),
notFound,
respond,
sendResponseStatus,
typeOctet,
)
import Yesod.Core (logError) import Yesod.Core (logError)
getAppR :: S9PK -> Handler TypedContent getAppR :: S9PK -> Handler TypedContent
getAppR file = do getAppR file = do
let pkg = PkgId . T.pack $ takeBaseName (show file) let pkg = PkgId . T.pack $ takeBaseName (show file)
osVersion <- getOsVersionQuery
osCompatibleVersions <- filterOsCompat osVersion pkg
versionSpec <- getVersionSpecFromQuery versionSpec <- getVersionSpecFromQuery
preferMin <- versionPriorityFromQueryIsMin preferMin <- versionPriorityFromQueryIsMin
version <- version <-
getBestVersion pkg versionSpec preferMin getBestVersion versionSpec preferMin osCompatibleVersions
`orThrow` sendResponseStatus status404 (NotFoundE [i|#{pkg} satisfying #{versionSpec}|]) `orThrow` sendResponseStatus status404 (NotFoundE [i|#{pkg} satisfying #{versionSpec}|])
addPackageHeader pkg version addPackageHeader pkg version
void $ recordMetrics pkg version void $ recordMetrics pkg version
@@ -42,8 +77,7 @@ recordMetrics :: PkgId -> Version -> Handler ()
recordMetrics pkg appVersion = do recordMetrics pkg appVersion = do
existingVersion <- runDB $ fetchAppVersion pkg appVersion existingVersion <- runDB $ fetchAppVersion pkg appVersion
case existingVersion of case existingVersion of
Nothing -> Nothing -> do
do $logError [i|#{pkg}@#{appVersion} not found in database|]
$logError [i|#{pkg}@#{appVersion} not found in database|] notFound
notFound Just _ -> runDB $ createMetric pkg appVersion
Just _ -> runDB $ createMetric pkg appVersion

View File

@@ -2,10 +2,18 @@
module Handler.Package.V0.Version where module Handler.Package.V0.Version where
import Data.Aeson (ToJSON, object, (.=)) import Data.Aeson (
import Data.String.Interpolate.IsString (i) ToJSON,
object,
(.=),
)
import Data.String.Interpolate.IsString (
i,
)
import Foundation (Handler) import Foundation (Handler)
import Handler.Package.V1.Index (getOsVersionQuery)
import Handler.Util ( import Handler.Util (
filterOsCompat,
getVersionSpecFromQuery, getVersionSpecFromQuery,
orThrow, orThrow,
versionPriorityFromQueryIsMin, versionPriorityFromQueryIsMin,
@@ -15,9 +23,22 @@ import Lib.PkgRepository (getBestVersion)
import Lib.Types.Core (PkgId) import Lib.Types.Core (PkgId)
import Lib.Types.Emver (Version (..)) import Lib.Types.Emver (Version (..))
import Network.HTTP.Types (status404) import Network.HTTP.Types (status404)
import Startlude (Eq, Maybe, Show, (.), (<$>)) import Startlude (
import Yesod (ToContent (..), ToTypedContent, sendResponseStatus) Eq,
import Yesod.Core (ToJSON (..), ToTypedContent (..)) Maybe,
Show,
(.),
(<$>),
)
import Yesod (
ToContent (..),
ToTypedContent,
sendResponseStatus,
)
import Yesod.Core (
ToJSON (..),
ToTypedContent (..),
)
newtype AppVersionRes = AppVersionRes newtype AppVersionRes = AppVersionRes
@@ -38,9 +59,11 @@ instance ToTypedContent (Maybe AppVersionRes) where
getPkgVersionR :: PkgId -> Handler AppVersionRes getPkgVersionR :: PkgId -> Handler AppVersionRes
getPkgVersionR pkg = do getPkgVersionR pkg = do
osVersion <- getOsVersionQuery
osCompatibleVersions <- filterOsCompat osVersion pkg
spec <- getVersionSpecFromQuery spec <- getVersionSpecFromQuery
preferMin <- versionPriorityFromQueryIsMin preferMin <- versionPriorityFromQueryIsMin
AppVersionRes <$> getBestVersion pkg spec preferMin AppVersionRes <$> getBestVersion spec preferMin osCompatibleVersions
`orThrow` sendResponseStatus `orThrow` sendResponseStatus
status404 status404
(NotFoundE [i|Version for #{pkg} satisfying #{spec}|]) (NotFoundE [i|Version for #{pkg} satisfying #{spec}|])

View File

@@ -6,19 +6,32 @@ import Control.Monad.Reader.Has (
Has, Has,
MonadReader, MonadReader,
) )
import Data.Attoparsec.Text (Parser, parseOnly) import Data.Attoparsec.Text (
import Data.String.Interpolate.IsString (i) Parser,
parseOnly,
)
import Data.String.Interpolate.IsString (
i,
)
import Data.Text qualified as T import Data.Text qualified as T
import Data.Text.Lazy qualified as TL import Data.Text.Lazy qualified as TL
import Data.Text.Lazy.Builder qualified as TB import Data.Text.Lazy.Builder qualified as TB
import Database.Queries (fetchAllAppVersions)
import Foundation import Foundation
import Lib.PkgRepository (PkgRepo, getHash) import Lib.PkgRepository (
PkgRepo,
getHash,
)
import Lib.Types.Core (PkgId) import Lib.Types.Core (PkgId)
import Lib.Types.Emver ( import Lib.Types.Emver (
Version, Version,
VersionRange, VersionRange,
satisfies,
)
import Model (
UserActivity (..),
VersionRecord (versionRecordOsVersion),
) )
import Model (UserActivity (..))
import Network.HTTP.Types ( import Network.HTTP.Types (
Status, Status,
status400, status400,
@@ -31,7 +44,10 @@ import Startlude (
Monoid (..), Monoid (..),
Semigroup ((<>)), Semigroup ((<>)),
Text, Text,
const,
decodeUtf8, decodeUtf8,
filter,
flip,
fromMaybe, fromMaybe,
fst, fst,
getCurrentTime, getCurrentTime,
@@ -48,10 +64,12 @@ import Startlude (
) )
import UnliftIO (MonadUnliftIO) import UnliftIO (MonadUnliftIO)
import Yesod ( import Yesod (
HandlerFor,
MonadHandler, MonadHandler,
RenderRoute (..), RenderRoute (..),
TypedContent (..), TypedContent (..),
YesodPersist (runDB), YesodPersist (runDB),
getYesod,
insertRecord, insertRecord,
liftHandler, liftHandler,
lookupGetParam, lookupGetParam,
@@ -106,8 +124,7 @@ queryParamAs k p =
lookupGetParam k >>= \case lookupGetParam k >>= \case
Nothing -> pure Nothing Nothing -> pure Nothing
Just x -> case parseOnly p x of Just x -> case parseOnly p x of
Left e -> Left e -> sendResponseText status400 [i|Invalid Request! The query parameter '#{k}' failed to parse: #{e}|]
sendResponseText status400 [i|Invalid Request! The query parameter '#{k}' failed to parse: #{e}|]
Right a -> pure (Just a) Right a -> pure (Just a)
@@ -118,3 +135,15 @@ tickleMAU = do
Just sid -> do Just sid -> do
now <- liftIO getCurrentTime now <- liftIO getCurrentTime
void $ liftHandler $ runDB $ insertRecord $ UserActivity now sid void $ liftHandler $ runDB $ insertRecord $ UserActivity now sid
filterOsCompat :: Maybe VersionRange -> PkgId -> HandlerFor RegistryCtx [VersionRecord]
filterOsCompat osVersion pkg = do
appConnPool <- appConnPool <$> getYesod
versionRecords <- runDB $ fetchAllAppVersions appConnPool pkg
pure $ filter (osPredicate osVersion . versionRecordOsVersion) versionRecords
where
osPredicate osV = do
case osV of
Nothing -> const True
Just v -> flip satisfies v

View File

@@ -68,22 +68,24 @@ import Database.Persist.Sql (
import Database.PostgreSQL.Simple (SqlError (sqlState)) import Database.PostgreSQL.Simple (SqlError (sqlState))
import Lib.Error (S9Error (NotFoundE)) import Lib.Error (S9Error (NotFoundE))
import Lib.External.AppMgr qualified as AppMgr import Lib.External.AppMgr qualified as AppMgr
import Lib.Types.Core ( import Lib.Types.Core (PkgId (..))
PkgId (..),
)
import Lib.Types.Emver ( import Lib.Types.Emver (
Version, Version,
VersionRange, VersionRange,
parseVersion, parseVersion,
satisfies, satisfies,
) )
import Lib.Types.Manifest (PackageDependency (..), PackageManifest (..)) import Lib.Types.Manifest (
PackageDependency (..),
PackageManifest (..),
)
import Model ( import Model (
EntityField (EosHashHash, PkgRecordUpdatedAt), EntityField (EosHashHash, PkgRecordUpdatedAt),
EosHash (EosHash), EosHash (EosHash),
Key (PkgRecordKey), Key (PkgRecordKey),
PkgDependency (PkgDependency), PkgDependency (PkgDependency),
PkgRecord (PkgRecord), PkgRecord (PkgRecord),
VersionRecord (versionRecordNumber),
) )
import Startlude ( import Startlude (
Bool (..), Bool (..),
@@ -208,17 +210,17 @@ getVersionsFor pkg = do
else pure [] else pure []
getViableVersions :: (MonadIO m, MonadReader r m, Has PkgRepo r, MonadLogger m) => PkgId -> VersionRange -> m [Version] getViableVersions :: VersionRange -> [VersionRecord] -> [Version]
getViableVersions pkg spec = filter (`satisfies` spec) <$> getVersionsFor pkg getViableVersions spec vrs = filter (`satisfies` spec) (versionRecordNumber <$> vrs)
getBestVersion :: getBestVersion ::
(MonadIO m, MonadReader r m, Has PkgRepo r, MonadLogger m) => (MonadIO m, MonadReader r m, Has PkgRepo r, MonadLogger m) =>
PkgId ->
VersionRange -> VersionRange ->
Bool -> Bool ->
[VersionRecord] ->
m (Maybe Version) m (Maybe Version)
getBestVersion pkg spec preferMin = headMay . sortBy comparator <$> getViableVersions pkg spec getBestVersion spec preferMin vrs = headMay . sortBy comparator <$> (pure $ getViableVersions spec vrs)
where where
comparator = if preferMin then compare else compare `on` Down comparator = if preferMin then compare else compare `on` Down