mirror of
https://github.com/Start9Labs/registry.git
synced 2026-03-30 11:51:57 +00:00
fix handling of using index endpoint to fetch package at specific version
This commit is contained in:
committed by
Keagan McClelland
parent
455e1d9e77
commit
0cf13f46e6
@@ -38,7 +38,9 @@ import Database.Esqueleto.Experimental
|
|||||||
)
|
)
|
||||||
import Lib.Types.AppIndex ( PkgId )
|
import Lib.Types.AppIndex ( PkgId )
|
||||||
import Lib.Types.Category
|
import Lib.Types.Category
|
||||||
import Lib.Types.Emver ( Version )
|
import Lib.Types.Emver ( Version
|
||||||
|
, VersionRange
|
||||||
|
)
|
||||||
import Model
|
import Model
|
||||||
import Startlude hiding ( (%)
|
import Startlude hiding ( (%)
|
||||||
, from
|
, from
|
||||||
@@ -103,10 +105,10 @@ zipVersions = awaitForever $ \i -> do
|
|||||||
filterOsCompatible :: Monad m
|
filterOsCompatible :: Monad m
|
||||||
=> (Version -> Bool)
|
=> (Version -> Bool)
|
||||||
-> ConduitT
|
-> ConduitT
|
||||||
(Entity PkgRecord, [Entity VersionRecord])
|
(Entity PkgRecord, [Entity VersionRecord], VersionRange)
|
||||||
(Entity PkgRecord, [Entity VersionRecord])
|
(Entity PkgRecord, [Entity VersionRecord], VersionRange)
|
||||||
m
|
m
|
||||||
()
|
()
|
||||||
filterOsCompatible p = awaitForever $ \(app, versions) -> do
|
filterOsCompatible p = awaitForever $ \(app, versions, requestedVersion) -> do
|
||||||
let compatible = filter (p . versionRecordOsVersion . entityVal) versions
|
let compatible = filter (p . versionRecordOsVersion . entityVal) versions
|
||||||
when (not $ null compatible) $ yield (app, compatible)
|
when (not $ null compatible) $ yield (app, compatible, requestedVersion)
|
||||||
|
|||||||
@@ -147,6 +147,10 @@ import Yesod.Core ( MonadResource
|
|||||||
)
|
)
|
||||||
import Yesod.Persist ( YesodDB )
|
import Yesod.Persist ( YesodDB )
|
||||||
import Yesod.Persist.Core ( YesodPersist(runDB) )
|
import Yesod.Persist.Core ( YesodPersist(runDB) )
|
||||||
|
import Data.Tuple.Extra hiding ( second
|
||||||
|
, first
|
||||||
|
, (&&&)
|
||||||
|
)
|
||||||
|
|
||||||
type URL = Text
|
type URL = Text
|
||||||
newtype CategoryRes = CategoryRes {
|
newtype CategoryRes = CategoryRes {
|
||||||
@@ -166,8 +170,7 @@ data ServiceRes = ServiceRes
|
|||||||
, serviceResVersions :: [Version]
|
, serviceResVersions :: [Version]
|
||||||
, serviceResDependencyInfo :: HM.HashMap PkgId DependencyInfo
|
, serviceResDependencyInfo :: HM.HashMap PkgId DependencyInfo
|
||||||
}
|
}
|
||||||
deriving Generic
|
deriving (Show, Generic)
|
||||||
|
|
||||||
newtype ReleaseNotes = ReleaseNotes { unReleaseNotes :: HM.HashMap Version Text }
|
newtype ReleaseNotes = ReleaseNotes { unReleaseNotes :: HM.HashMap Version Text }
|
||||||
deriving (Eq, Show)
|
deriving (Eq, Show)
|
||||||
instance ToJSON ReleaseNotes where
|
instance ToJSON ReleaseNotes where
|
||||||
@@ -186,6 +189,16 @@ instance ToJSON ServiceRes where
|
|||||||
, "versions" .= serviceResVersions
|
, "versions" .= serviceResVersions
|
||||||
, "dependency-metadata" .= serviceResDependencyInfo
|
, "dependency-metadata" .= serviceResDependencyInfo
|
||||||
]
|
]
|
||||||
|
instance FromJSON ServiceRes where
|
||||||
|
parseJSON = withObject "ServiceRes" $ \o -> do
|
||||||
|
serviceResIcon <- o .: "icon"
|
||||||
|
serviceResLicense <- o .: "license"
|
||||||
|
serviceResInstructions <- o .: "instructions"
|
||||||
|
serviceResManifest <- o .: "manifest"
|
||||||
|
serviceResCategories <- o .: "categories"
|
||||||
|
serviceResVersions <- o .: "versions"
|
||||||
|
serviceResDependencyInfo <- o .: "dependency-metadata"
|
||||||
|
pure ServiceRes { .. }
|
||||||
data DependencyInfo = DependencyInfo
|
data DependencyInfo = DependencyInfo
|
||||||
{ dependencyInfoTitle :: PkgId
|
{ dependencyInfoTitle :: PkgId
|
||||||
, dependencyInfoIcon :: URL
|
, dependencyInfoIcon :: URL
|
||||||
@@ -193,7 +206,11 @@ data DependencyInfo = DependencyInfo
|
|||||||
deriving (Eq, Show)
|
deriving (Eq, Show)
|
||||||
instance ToJSON DependencyInfo where
|
instance ToJSON DependencyInfo where
|
||||||
toJSON DependencyInfo {..} = object ["icon" .= dependencyInfoIcon, "title" .= dependencyInfoTitle]
|
toJSON DependencyInfo {..} = object ["icon" .= dependencyInfoIcon, "title" .= dependencyInfoTitle]
|
||||||
|
instance FromJSON DependencyInfo where
|
||||||
|
parseJSON = withObject "DependencyInfo" $ \o -> do
|
||||||
|
dependencyInfoIcon <- o .: "icon"
|
||||||
|
dependencyInfoTitle <- o .: "title"
|
||||||
|
pure DependencyInfo { .. }
|
||||||
newtype ServiceAvailableRes = ServiceAvailableRes [ServiceRes]
|
newtype ServiceAvailableRes = ServiceAvailableRes [ServiceRes]
|
||||||
deriving (Generic)
|
deriving (Generic)
|
||||||
instance ToJSON ServiceAvailableRes
|
instance ToJSON ServiceAvailableRes
|
||||||
@@ -346,39 +363,42 @@ getPackageListR = do
|
|||||||
$ runConduit
|
$ runConduit
|
||||||
$ searchServices category query
|
$ searchServices category query
|
||||||
.| zipVersions
|
.| zipVersions
|
||||||
|
.| mapC (\(a, vs) -> (,,) a vs Any)
|
||||||
.| filterOsCompatible osPredicate
|
.| filterOsCompatible osPredicate
|
||||||
-- pages start at 1 for some reason. TODO: make pages start at 0
|
-- pages start at 1 for some reason. TODO: make pages start at 0
|
||||||
.| (dropC (limit' * (page - 1)) *> takeC limit')
|
.| (dropC (limit' * (page - 1)) *> takeC limit')
|
||||||
.| sinkList
|
.| sinkList
|
||||||
Just packages -> do
|
Just packages' -> do
|
||||||
-- for each item in list get best available from version range
|
-- for each item in list get best available from version range
|
||||||
let vMap = (packageVersionId &&& packageVersionVersion) <$> packages
|
let vMap = (packageVersionId &&& packageVersionVersion) <$> packages'
|
||||||
runDB
|
runDB
|
||||||
. runConduit
|
. runConduit
|
||||||
$ getPkgData (packageVersionId <$> packages)
|
$ getPkgData (packageVersionId <$> packages')
|
||||||
.| zipVersions
|
.| zipVersions
|
||||||
.| mapC
|
.| mapC
|
||||||
(\(a, vs) ->
|
(\(a, vs) ->
|
||||||
let spec = fromMaybe Any $ lookup (unPkgRecordKey $ entityKey a) vMap
|
let spec = fromMaybe Any $ lookup (unPkgRecordKey $ entityKey a) vMap
|
||||||
in (a, filter ((<|| spec) . versionRecordNumber . entityVal) vs)
|
in (a, filter ((<|| spec) . versionRecordNumber . entityVal) vs, spec)
|
||||||
)
|
)
|
||||||
.| filterOsCompatible osPredicate
|
.| filterOsCompatible osPredicate
|
||||||
.| sinkList
|
.| sinkList
|
||||||
let keys = unPkgRecordKey . entityKey . fst <$> filteredServices
|
let keys = unPkgRecordKey . entityKey . fst3 <$> filteredServices
|
||||||
cats <- runDB $ fetchAppCategories keys
|
cats <- runDB $ fetchAppCategories keys
|
||||||
let vers =
|
let vers =
|
||||||
filteredServices
|
filteredServices
|
||||||
<&> first (unPkgRecordKey . entityKey)
|
<&> first3 (unPkgRecordKey . entityKey)
|
||||||
<&> second (sortOn Down . fmap (versionRecordNumber . entityVal))
|
<&> second3 (sortOn Down . fmap (versionRecordNumber . entityVal))
|
||||||
& HM.fromListWith (++)
|
<&> (\(a, vs, vr) -> (,) a $ (,) vs vr)
|
||||||
|
& HM.fromListWith mergeDupes
|
||||||
let packageMetadata = HM.intersectionWith (,) vers (categoryName <<$>> cats)
|
let packageMetadata = HM.intersectionWith (,) vers (categoryName <<$>> cats)
|
||||||
serviceDetailResult <- mapConcurrently (flip (getServiceDetails packageMetadata) Nothing)
|
serviceDetailResult <- mapConcurrently (getServiceDetails packageMetadata)
|
||||||
(unPkgRecordKey . entityKey . fst <$> filteredServices)
|
(unPkgRecordKey . entityKey . fst3 <$> filteredServices)
|
||||||
let services = snd $ partitionEithers serviceDetailResult
|
let services = snd $ partitionEithers serviceDetailResult
|
||||||
pure $ ServiceAvailableRes services
|
pure $ ServiceAvailableRes services
|
||||||
|
|
||||||
|
|
||||||
where
|
where
|
||||||
|
mergeDupes :: ([Version], VersionRange) -> ([Version], VersionRange) -> ([Version], VersionRange)
|
||||||
|
mergeDupes (vs, vr) (vs', _) = (,) ((++) vs vs') vr
|
||||||
defaults = ServiceListDefaults { serviceListOrder = DESC
|
defaults = ServiceListDefaults { serviceListOrder = DESC
|
||||||
, serviceListPageLimit = 20
|
, serviceListPageLimit = 20
|
||||||
, serviceListPageNumber = 1
|
, serviceListPageNumber = 1
|
||||||
@@ -434,23 +454,25 @@ getPackageListR = do
|
|||||||
Right v -> pure $ Just v
|
Right v -> pure $ Just v
|
||||||
|
|
||||||
getServiceDetails :: (MonadIO m, MonadResource m, MonadReader r m, Has AppSettings r)
|
getServiceDetails :: (MonadIO m, MonadResource m, MonadReader r m, Has AppSettings r)
|
||||||
=> (HM.HashMap PkgId ([Version], [CategoryTitle]))
|
=> (HM.HashMap PkgId (([Version], VersionRange), [CategoryTitle]))
|
||||||
-> PkgId
|
-> PkgId
|
||||||
-> Maybe Version
|
|
||||||
-> m (Either S9Error ServiceRes)
|
-> m (Either S9Error ServiceRes)
|
||||||
getServiceDetails metadata pkg maybeVersion = runExceptT $ do
|
getServiceDetails metadata pkg = runExceptT $ do
|
||||||
settings <- ask
|
settings <- ask
|
||||||
packageMetadata <- case HM.lookup pkg metadata of
|
packageMetadata <- case HM.lookup pkg metadata of
|
||||||
Nothing -> liftEither . Left $ NotFoundE [i|#{pkg} not found.|]
|
Nothing -> liftEither . Left $ NotFoundE [i|#{pkg} not found.|]
|
||||||
Just m -> pure m
|
Just m -> pure m
|
||||||
let domain = registryHostname settings
|
let domain = registryHostname settings
|
||||||
version <- case maybeVersion of
|
let versionInfo = fst $ (HM.!) metadata pkg
|
||||||
Nothing -> do
|
version <- case snd versionInfo of
|
||||||
|
Any -> do
|
||||||
-- grab first value, which will be the latest version
|
-- grab first value, which will be the latest version
|
||||||
case fst packageMetadata of
|
case fst versionInfo of
|
||||||
[] -> liftEither . Left $ NotFoundE $ [i|No latest version found for #{pkg}|]
|
[] -> liftEither . Left $ NotFoundE $ [i|No latest version found for #{pkg}|]
|
||||||
x : _ -> pure x
|
x : _ -> pure x
|
||||||
Just v -> pure v
|
spec -> case headMay . sortOn Down $ filter (`satisfies` spec) $ fst versionInfo of
|
||||||
|
Nothing -> liftEither . Left $ NotFoundE [i|No version for #{pkg} satisfying #{spec}|]
|
||||||
|
Just v -> pure v
|
||||||
manifest <- flip runReaderT settings $ (snd <$> getManifest pkg version) >>= \bs ->
|
manifest <- flip runReaderT settings $ (snd <$> getManifest pkg version) >>= \bs ->
|
||||||
runConduit $ bs .| CL.foldMap BS.fromStrict
|
runConduit $ bs .| CL.foldMap BS.fromStrict
|
||||||
case eitherDecode manifest of
|
case eitherDecode manifest of
|
||||||
@@ -463,12 +485,12 @@ getServiceDetails metadata pkg maybeVersion = runExceptT $ do
|
|||||||
, serviceResCategories = snd packageMetadata
|
, serviceResCategories = snd packageMetadata
|
||||||
, serviceResInstructions = [i|https://#{domain}/package/instructions/#{pkg}|]
|
, serviceResInstructions = [i|https://#{domain}/package/instructions/#{pkg}|]
|
||||||
, serviceResLicense = [i|https://#{domain}/package/license/#{pkg}|]
|
, serviceResLicense = [i|https://#{domain}/package/license/#{pkg}|]
|
||||||
, serviceResVersions = fst packageMetadata
|
, serviceResVersions = fst . fst $ packageMetadata
|
||||||
, serviceResDependencyInfo = HM.fromList $ snd $ partitionEithers d
|
, serviceResDependencyInfo = HM.fromList $ snd $ partitionEithers d
|
||||||
}
|
}
|
||||||
|
|
||||||
mapDependencyMetadata :: Text
|
mapDependencyMetadata :: Text
|
||||||
-> HM.HashMap PkgId ([Version], [CategoryTitle])
|
-> HM.HashMap PkgId (([Version], VersionRange), [CategoryTitle])
|
||||||
-> (PkgId, ServiceDependencyInfo)
|
-> (PkgId, ServiceDependencyInfo)
|
||||||
-> Either S9Error (PkgId, DependencyInfo)
|
-> Either S9Error (PkgId, DependencyInfo)
|
||||||
mapDependencyMetadata domain metadata (appId, depInfo) = do
|
mapDependencyMetadata domain metadata (appId, depInfo) = do
|
||||||
@@ -476,7 +498,7 @@ mapDependencyMetadata domain metadata (appId, depInfo) = do
|
|||||||
Nothing -> Left $ NotFoundE [i|dependency metadata for #{appId} not found.|]
|
Nothing -> Left $ NotFoundE [i|dependency metadata for #{appId} not found.|]
|
||||||
Just m -> pure m
|
Just m -> pure m
|
||||||
-- get best version from VersionRange of dependency
|
-- get best version from VersionRange of dependency
|
||||||
let satisfactory = filter (<|| serviceDependencyInfoVersion depInfo) (fst depMetadata)
|
let satisfactory = filter (<|| serviceDependencyInfoVersion depInfo) (fst . fst $ depMetadata)
|
||||||
let best = getMax <$> foldMap (Just . Max) satisfactory
|
let best = getMax <$> foldMap (Just . Max) satisfactory
|
||||||
version <- case best of
|
version <- case best of
|
||||||
Nothing -> Left $ NotFoundE $ [i|No satisfactory version for dependent package #{appId}|]
|
Nothing -> Left $ NotFoundE $ [i|No satisfactory version for dependent package #{appId}|]
|
||||||
|
|||||||
Reference in New Issue
Block a user