address PR feedback and add os filtering to get package latest version endpoint

This commit is contained in:
Lucy Cifferello
2022-07-09 16:01:46 -06:00
parent 2cde1a2d8e
commit 4e2285f130
11 changed files with 67 additions and 46 deletions

View File

@@ -229,8 +229,8 @@ getDependencyVersions pkgDepRecord = do
pure $ entityVal <$> depVers pure $ entityVal <$> depVers
fetchAllAppVersions :: MonadUnliftIO m => ConnectionPool -> PkgId -> m [VersionRecord] fetchAllPkgVersions :: MonadUnliftIO m => ConnectionPool -> PkgId -> m [VersionRecord]
fetchAllAppVersions appConnPool appId = do fetchAllPkgVersions appConnPool appId = do
entityAppVersions <- runSqlPool (P.selectList [VersionRecordPkgId P.==. PkgRecordKey appId] []) appConnPool entityAppVersions <- runSqlPool (P.selectList [VersionRecordPkgId P.==. PkgRecordKey appId] []) appConnPool
pure $ entityVal <$> entityAppVersions pure $ entityVal <$> entityAppVersions

View File

@@ -12,7 +12,7 @@ import Data.String.Interpolate.IsString (
import Foundation (Handler) import Foundation (Handler)
import Handler.Package.V1.Index (getOsVersionQuery) import Handler.Package.V1.Index (getOsVersionQuery)
import Handler.Util ( import Handler.Util (
filterOsCompat, fetchCompatiblePkgVersions,
getVersionSpecFromQuery, getVersionSpecFromQuery,
orThrow, orThrow,
versionPriorityFromQueryIsMin, versionPriorityFromQueryIsMin,
@@ -25,6 +25,7 @@ import Lib.PkgRepository (
import Lib.Types.Core (PkgId) import Lib.Types.Core (PkgId)
import Network.HTTP.Types (status400) import Network.HTTP.Types (status400)
import Startlude ( import Startlude (
pure,
show, show,
($), ($),
) )
@@ -40,11 +41,11 @@ import Yesod (
getIconsR :: PkgId -> Handler TypedContent getIconsR :: PkgId -> Handler TypedContent
getIconsR pkg = do getIconsR pkg = do
osVersion <- getOsVersionQuery osVersion <- getOsVersionQuery
osCompatibleVersions <- filterOsCompat osVersion pkg osCompatibleVersions <- fetchCompatiblePkgVersions osVersion pkg
spec <- getVersionSpecFromQuery spec <- getVersionSpecFromQuery
preferMin <- versionPriorityFromQueryIsMin preferMin <- versionPriorityFromQueryIsMin
version <- version <-
getBestVersion spec preferMin osCompatibleVersions (pure $ 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

@@ -12,7 +12,7 @@ import Data.String.Interpolate.IsString (
import Foundation (Handler) import Foundation (Handler)
import Handler.Package.V1.Index (getOsVersionQuery) import Handler.Package.V1.Index (getOsVersionQuery)
import Handler.Util ( import Handler.Util (
filterOsCompat, fetchCompatiblePkgVersions,
getVersionSpecFromQuery, getVersionSpecFromQuery,
orThrow, orThrow,
versionPriorityFromQueryIsMin, versionPriorityFromQueryIsMin,
@@ -25,6 +25,7 @@ import Lib.PkgRepository (
import Lib.Types.Core (PkgId) import Lib.Types.Core (PkgId)
import Network.HTTP.Types (status400) import Network.HTTP.Types (status400)
import Startlude ( import Startlude (
pure,
show, show,
($), ($),
) )
@@ -42,10 +43,12 @@ getInstructionsR :: PkgId -> Handler TypedContent
getInstructionsR pkg = do getInstructionsR pkg = do
spec <- getVersionSpecFromQuery spec <- getVersionSpecFromQuery
osVersion <- getOsVersionQuery osVersion <- getOsVersionQuery
osCompatibleVersions <- filterOsCompat osVersion pkg osCompatibleVersions <- fetchCompatiblePkgVersions osVersion pkg
preferMin <- versionPriorityFromQueryIsMin preferMin <- versionPriorityFromQueryIsMin
version <- version <-
getBestVersion spec preferMin osCompatibleVersions ( pure $
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

@@ -1,19 +1,22 @@
module Handler.Package.V0.Latest where module Handler.Package.V0.Latest where
import Conduit (mapC, runConduit, sinkList, (.|))
import Data.Aeson (ToJSON (..), eitherDecode) import Data.Aeson (ToJSON (..), eitherDecode)
import Data.ByteString.Lazy qualified as LBS import Data.ByteString.Lazy qualified as LBS
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 Data.List (lookup) import Data.List (lookup, sortOn)
import Database.Queries (fetchLatestApp) import Data.Tuple.Extra (second)
import Database.Queries (collateVersions, getPkgDataSource)
import Foundation (Handler) import Foundation (Handler)
import Handler.Package.V1.Index (getOsVersionQuery)
import Lib.Error (S9Error (..)) import Lib.Error (S9Error (..))
import Lib.Types.Core (PkgId) import Lib.Types.Core (PkgId)
import Lib.Types.Emver (Version) import Lib.Types.Emver (Version, satisfies)
import Model (Key (..), VersionRecord (..)) import Model (VersionRecord (..))
import Network.HTTP.Types (status400) import Network.HTTP.Types (status400)
import Startlude (Either (..), Generic, Maybe (..), Show, catMaybes, encodeUtf8, fst, pure, snd, traverse, ($), (.), (<$>)) import Startlude (Bool (True), Down (Down), Either (..), Generic, Maybe (..), Show, const, encodeUtf8, filter, flip, headMay, pure, ($), (.), (<$>), (<&>))
import Yesod (Entity (..), ToContent (..), ToTypedContent (..), YesodPersist (runDB), YesodRequest (reqGetParams), getRequest, sendResponseStatus) import Yesod (ToContent (..), ToTypedContent (..), YesodPersist (runDB), YesodRequest (reqGetParams), getRequest, sendResponseStatus)
newtype VersionLatestRes = VersionLatestRes (HashMap PkgId (Maybe Version)) newtype VersionLatestRes = VersionLatestRes (HashMap PkgId (Maybe Version))
@@ -25,24 +28,36 @@ instance ToTypedContent VersionLatestRes where
toTypedContent = toTypedContent . toJSON toTypedContent = toTypedContent . toJSON
-- TODO refactor with conduit
getVersionLatestR :: Handler VersionLatestRes getVersionLatestR :: Handler VersionLatestRes
getVersionLatestR = do getVersionLatestR = do
getParameters <- reqGetParams <$> getRequest getParameters <- reqGetParams <$> getRequest
osPredicate' <-
getOsVersionQuery <&> \case
Nothing -> const True
Just v -> flip satisfies v
case lookup "ids" getParameters of case lookup "ids" getParameters of
Nothing -> sendResponseStatus status400 (InvalidParamsE "get:ids" "<MISSING>") Nothing -> sendResponseStatus status400 (InvalidParamsE "get:ids" "<MISSING>")
Just packages -> case eitherDecode $ LBS.fromStrict $ encodeUtf8 packages of Just packages -> case eitherDecode $ LBS.fromStrict $ encodeUtf8 packages of
Left _ -> sendResponseStatus status400 (InvalidParamsE "get:ids" packages) Left _ -> sendResponseStatus status400 (InvalidParamsE "get:ids" packages)
Right p -> do Right p -> do
let packageList = (,Nothing) <$> p let packageList = (,Nothing) <$> p
found <- runDB $ traverse fetchLatestApp $ fst <$> packageList let source = getPkgDataSource p
filteredPackages <-
runDB $
runConduit $
source
-- group conduit pipeline by pkg id
.| collateVersions
-- filter out versions of apps that are incompatible with the OS predicate
.| mapC (second (filter (osPredicate' . versionRecordOsVersion)))
-- prune empty version sets
-- grab the latest matching version if it exists
.| mapC (\(a, b) -> (a, (selectLatestVersion b)))
.| sinkList
-- if the requested package does not have available versions, return it as a key with a null value
pure $ pure $
VersionLatestRes $ VersionLatestRes $
HM.union HM.union (HM.fromList $ filteredPackages) (HM.fromList packageList)
( HM.fromList $ where
( \v -> selectLatestVersion :: [VersionRecord] -> Maybe Version
(unPkgRecordKey . entityKey $ fst v, Just $ versionRecordNumber $ entityVal $ snd v) selectLatestVersion vs = headMay $ (versionRecordNumber <$>) $ sortOn (Down . versionRecordNumber) $ vs
)
<$> catMaybes found
)
$ HM.fromList packageList

View File

@@ -12,7 +12,7 @@ import Data.String.Interpolate.IsString (
import Foundation (Handler) import Foundation (Handler)
import Handler.Package.V1.Index (getOsVersionQuery) import Handler.Package.V1.Index (getOsVersionQuery)
import Handler.Util ( import Handler.Util (
filterOsCompat, fetchCompatiblePkgVersions,
getVersionSpecFromQuery, getVersionSpecFromQuery,
orThrow, orThrow,
versionPriorityFromQueryIsMin, versionPriorityFromQueryIsMin,
@@ -25,6 +25,7 @@ import Lib.PkgRepository (
import Lib.Types.Core (PkgId) import Lib.Types.Core (PkgId)
import Network.HTTP.Types (status400) import Network.HTTP.Types (status400)
import Startlude ( import Startlude (
pure,
show, show,
($), ($),
) )
@@ -41,11 +42,11 @@ import Yesod (
getLicenseR :: PkgId -> Handler TypedContent getLicenseR :: PkgId -> Handler TypedContent
getLicenseR pkg = do getLicenseR pkg = do
osVersion <- getOsVersionQuery osVersion <- getOsVersionQuery
osCompatibleVersions <- filterOsCompat osVersion pkg osCompatibleVersions <- fetchCompatiblePkgVersions osVersion pkg
spec <- getVersionSpecFromQuery spec <- getVersionSpecFromQuery
preferMin <- versionPriorityFromQueryIsMin preferMin <- versionPriorityFromQueryIsMin
version <- version <-
getBestVersion spec preferMin osCompatibleVersions (pure $ 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

@@ -13,7 +13,7 @@ import Foundation (Handler)
import Handler.Package.V1.Index (getOsVersionQuery) import Handler.Package.V1.Index (getOsVersionQuery)
import Handler.Util ( import Handler.Util (
addPackageHeader, addPackageHeader,
filterOsCompat, fetchCompatiblePkgVersions,
getVersionSpecFromQuery, getVersionSpecFromQuery,
orThrow, orThrow,
versionPriorityFromQueryIsMin, versionPriorityFromQueryIsMin,
@@ -26,6 +26,7 @@ import Lib.PkgRepository (
import Lib.Types.Core (PkgId) import Lib.Types.Core (PkgId)
import Network.HTTP.Types (status404) import Network.HTTP.Types (status404)
import Startlude ( import Startlude (
pure,
show, show,
($), ($),
) )
@@ -42,11 +43,11 @@ import Yesod (
getAppManifestR :: PkgId -> Handler TypedContent getAppManifestR :: PkgId -> Handler TypedContent
getAppManifestR pkg = do getAppManifestR pkg = do
osVersion <- getOsVersionQuery osVersion <- getOsVersionQuery
osCompatibleVersions <- filterOsCompat osVersion pkg osCompatibleVersions <- fetchCompatiblePkgVersions osVersion pkg
versionSpec <- getVersionSpecFromQuery versionSpec <- getVersionSpecFromQuery
preferMin <- versionPriorityFromQueryIsMin preferMin <- versionPriorityFromQueryIsMin
version <- version <-
getBestVersion versionSpec preferMin osCompatibleVersions (pure $ 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

View File

@@ -12,7 +12,7 @@ import Data.HashMap.Strict (HashMap)
import Data.HashMap.Strict qualified as HM import Data.HashMap.Strict qualified as HM
import Foundation (Handler) import Foundation (Handler)
import Handler.Package.V1.Index (getOsVersionQuery) import Handler.Package.V1.Index (getOsVersionQuery)
import Handler.Util (filterOsCompat) import Handler.Util (fetchCompatiblePkgVersions)
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 (..))
@@ -50,7 +50,7 @@ instance ToTypedContent ReleaseNotes where
getReleaseNotesR :: PkgId -> Handler ReleaseNotes getReleaseNotesR :: PkgId -> Handler ReleaseNotes
getReleaseNotesR pkg = do getReleaseNotesR pkg = do
osVersion <- getOsVersionQuery osVersion <- getOsVersionQuery
osCompatibleVersions <- filterOsCompat osVersion pkg osCompatibleVersions <- fetchCompatiblePkgVersions osVersion pkg
pure $ constructReleaseNotesApiRes osCompatibleVersions pure $ constructReleaseNotesApiRes osCompatibleVersions
where where
constructReleaseNotesApiRes :: [VersionRecord] -> ReleaseNotes constructReleaseNotesApiRes :: [VersionRecord] -> ReleaseNotes

View File

@@ -17,7 +17,7 @@ import GHC.Show (show)
import Handler.Package.V1.Index (getOsVersionQuery) import Handler.Package.V1.Index (getOsVersionQuery)
import Handler.Util ( import Handler.Util (
addPackageHeader, addPackageHeader,
filterOsCompat, fetchCompatiblePkgVersions,
getVersionSpecFromQuery, getVersionSpecFromQuery,
orThrow, orThrow,
versionPriorityFromQueryIsMin, versionPriorityFromQueryIsMin,
@@ -58,11 +58,11 @@ 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 osVersion <- getOsVersionQuery
osCompatibleVersions <- filterOsCompat osVersion pkg osCompatibleVersions <- fetchCompatiblePkgVersions osVersion pkg
versionSpec <- getVersionSpecFromQuery versionSpec <- getVersionSpecFromQuery
preferMin <- versionPriorityFromQueryIsMin preferMin <- versionPriorityFromQueryIsMin
version <- version <-
getBestVersion versionSpec preferMin osCompatibleVersions (pure $ 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

View File

@@ -13,7 +13,7 @@ import Data.String.Interpolate.IsString (
import Foundation (Handler) import Foundation (Handler)
import Handler.Package.V1.Index (getOsVersionQuery) import Handler.Package.V1.Index (getOsVersionQuery)
import Handler.Util ( import Handler.Util (
filterOsCompat, fetchCompatiblePkgVersions,
getVersionSpecFromQuery, getVersionSpecFromQuery,
orThrow, orThrow,
versionPriorityFromQueryIsMin, versionPriorityFromQueryIsMin,
@@ -27,6 +27,8 @@ import Startlude (
Eq, Eq,
Maybe, Maybe,
Show, Show,
pure,
($),
(.), (.),
(<$>), (<$>),
) )
@@ -60,10 +62,10 @@ instance ToTypedContent (Maybe AppVersionRes) where
getPkgVersionR :: PkgId -> Handler AppVersionRes getPkgVersionR :: PkgId -> Handler AppVersionRes
getPkgVersionR pkg = do getPkgVersionR pkg = do
osVersion <- getOsVersionQuery osVersion <- getOsVersionQuery
osCompatibleVersions <- filterOsCompat osVersion pkg osCompatibleVersions <- fetchCompatiblePkgVersions osVersion pkg
spec <- getVersionSpecFromQuery spec <- getVersionSpecFromQuery
preferMin <- versionPriorityFromQueryIsMin preferMin <- versionPriorityFromQueryIsMin
AppVersionRes <$> getBestVersion spec preferMin osCompatibleVersions AppVersionRes <$> (pure $ 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

@@ -16,7 +16,7 @@ import Data.String.Interpolate.IsString (
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 Database.Queries (fetchAllPkgVersions)
import Foundation import Foundation
import Lib.PkgRepository ( import Lib.PkgRepository (
PkgRepo, PkgRepo,
@@ -64,7 +64,6 @@ import Startlude (
) )
import UnliftIO (MonadUnliftIO) import UnliftIO (MonadUnliftIO)
import Yesod ( import Yesod (
HandlerFor,
MonadHandler, MonadHandler,
RenderRoute (..), RenderRoute (..),
TypedContent (..), TypedContent (..),
@@ -137,10 +136,10 @@ tickleMAU = do
void $ liftHandler $ runDB $ insertRecord $ UserActivity now sid void $ liftHandler $ runDB $ insertRecord $ UserActivity now sid
filterOsCompat :: Maybe VersionRange -> PkgId -> HandlerFor RegistryCtx [VersionRecord] fetchCompatiblePkgVersions :: Maybe VersionRange -> PkgId -> Handler [VersionRecord]
filterOsCompat osVersion pkg = do fetchCompatiblePkgVersions osVersion pkg = do
appConnPool <- appConnPool <$> getYesod appConnPool <- appConnPool <$> getYesod
versionRecords <- runDB $ fetchAllAppVersions appConnPool pkg versionRecords <- runDB $ fetchAllPkgVersions appConnPool pkg
pure $ filter (osPredicate osVersion . versionRecordOsVersion) versionRecords pure $ filter (osPredicate osVersion . versionRecordOsVersion) versionRecords
where where
osPredicate osV = do osPredicate osV = do

View File

@@ -215,12 +215,11 @@ getViableVersions spec vrs = filter (`satisfies` spec) (versionRecordNumber <$>
getBestVersion :: getBestVersion ::
(MonadIO m, MonadReader r m, Has PkgRepo r, MonadLogger m) =>
VersionRange -> VersionRange ->
Bool -> Bool ->
[VersionRecord] -> [VersionRecord] ->
m (Maybe Version) (Maybe Version)
getBestVersion spec preferMin vrs = headMay . sortBy comparator <$> (pure $ getViableVersions spec vrs) getBestVersion spec preferMin vrs = headMay $ sortBy comparator $ getViableVersions spec vrs
where where
comparator = if preferMin then compare else compare `on` Down comparator = if preferMin then compare else compare `on` Down