module Ecluse.Core.Server.Metadata (
ManifestCaching (..),
newMetadataClient,
) where
import Data.Map.Strict qualified as Map
import Ecluse.Core.Package (InvalidEntry, PackageDetails, PackageInfo (infoInvalidEntries, infoVersions), PackageName)
import Ecluse.Core.Registry.Metadata (
Manifest (Manifest, manifestDigest, manifestInfo, manifestRaw),
MetadataClient (..),
MetadataError (MetadataBoundExceeded, MetadataNameMismatch, MetadataUndecodable, MetadataUnreachable, MetadataUrlUnformable),
)
import Ecluse.Core.Server.Cache (
CacheEntry (CacheEntry, entryDigest, entryInfo, entryRaw),
MetadataCache,
Source,
cachedMetadata,
cachedVersion,
resolveMetadata,
resolveVersion,
)
import Ecluse.Core.Telemetry.Metrics qualified as Metric
import Ecluse.Core.Telemetry.Record (MetricsPort (..), timedSeconds)
import Ecluse.Core.Version (Version, renderVersion)
data ManifestCaching
=
Uncached
|
Cached MetadataCache Source
newMetadataClient ::
MetricsPort ->
Metric.Upstream ->
ManifestCaching ->
(PackageName -> MetadataError -> IO ()) ->
(PackageName -> [InvalidEntry] -> IO ()) ->
(PackageName -> IO ()) ->
(PackageName -> IO (Either MetadataError Manifest)) ->
(PackageName -> Version -> IO (Either MetadataError (Maybe PackageDetails))) ->
MetadataClient
newMetadataClient :: MetricsPort
-> Upstream
-> ManifestCaching
-> (PackageName -> MetadataError -> IO ())
-> (PackageName -> [InvalidEntry] -> IO ())
-> (PackageName -> IO ())
-> (PackageName -> IO (Either MetadataError Manifest))
-> (PackageName
-> Version -> IO (Either MetadataError (Maybe PackageDetails)))
-> MetadataClient
newMetadataClient MetricsPort
metrics Upstream
upstream ManifestCaching
caching PackageName -> MetadataError -> IO ()
logFailure PackageName -> [InvalidEntry] -> IO ()
logInvalid PackageName -> IO ()
logFetch PackageName -> IO (Either MetadataError Manifest)
rawFetch PackageName
-> Version -> IO (Either MetadataError (Maybe PackageDetails))
rawFetchVersion =
MetadataClient
{ fetchFullManifest :: PackageName -> IO (Either MetadataError Manifest)
fetchFullManifest = (Either MetadataError CacheEntry -> Either MetadataError Manifest)
-> IO (Either MetadataError CacheEntry)
-> IO (Either MetadataError Manifest)
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((CacheEntry -> Manifest)
-> Either MetadataError CacheEntry -> Either MetadataError Manifest
forall a b.
(a -> b) -> Either MetadataError a -> Either MetadataError b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap CacheEntry -> Manifest
entryToManifest) (IO (Either MetadataError CacheEntry)
-> IO (Either MetadataError Manifest))
-> (PackageName -> IO (Either MetadataError CacheEntry))
-> PackageName
-> IO (Either MetadataError Manifest)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PackageName -> IO (Either MetadataError CacheEntry)
resolveEntry
, fetchVersionMetadata :: PackageName
-> Version -> IO (Either MetadataError (Maybe PackageDetails))
fetchVersionMetadata = PackageName
-> Version -> IO (Either MetadataError (Maybe PackageDetails))
resolveVersionHybrid
}
where
resolveEntry :: PackageName -> IO (Either MetadataError CacheEntry)
resolveEntry :: PackageName -> IO (Either MetadataError CacheEntry)
resolveEntry PackageName
name = case ManifestCaching
caching of
ManifestCaching
Uncached -> PackageName -> IO (Either MetadataError CacheEntry)
manifestLeader PackageName
name
Cached MetadataCache
cache Source
source -> MetricsPort
-> MetadataCache
-> Source
-> PackageName
-> IO (Either MetadataError CacheEntry)
-> IO (Either MetadataError CacheEntry)
resolveMetadata MetricsPort
metrics MetadataCache
cache Source
source PackageName
name (PackageName -> IO (Either MetadataError CacheEntry)
manifestLeader PackageName
name)
manifestLeader :: PackageName -> IO (Either MetadataError CacheEntry)
manifestLeader :: PackageName -> IO (Either MetadataError CacheEntry)
manifestLeader PackageName
name = do
PackageName -> IO ()
logFetch PackageName
name
MetricsPort
-> Upstream
-> IO (Either MetadataError CacheEntry)
-> IO (Either MetadataError CacheEntry)
forall a.
MetricsPort
-> Upstream
-> IO (Either MetadataError a)
-> IO (Either MetadataError a)
recordedFetch MetricsPort
metrics Upstream
upstream (IO (Either MetadataError CacheEntry)
-> IO (Either MetadataError CacheEntry))
-> IO (Either MetadataError CacheEntry)
-> IO (Either MetadataError CacheEntry)
forall a b. (a -> b) -> a -> b
$
PackageName -> IO (Either MetadataError Manifest)
rawFetch PackageName
name IO (Either MetadataError Manifest)
-> (Either MetadataError Manifest
-> IO (Either MetadataError CacheEntry))
-> IO (Either MetadataError CacheEntry)
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Right Manifest
manifest -> do
let invalid :: [InvalidEntry]
invalid = PackageInfo -> [InvalidEntry]
infoInvalidEntries (Manifest -> PackageInfo
manifestInfo Manifest
manifest)
Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless ([InvalidEntry] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [InvalidEntry]
invalid) (PackageName -> [InvalidEntry] -> IO ()
logInvalid PackageName
name [InvalidEntry]
invalid)
Either MetadataError CacheEntry
-> IO (Either MetadataError CacheEntry)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (CacheEntry -> Either MetadataError CacheEntry
forall a b. b -> Either a b
Right (PackageInfo -> CachedDoc -> ContentDigest -> CacheEntry
CacheEntry (Manifest -> PackageInfo
manifestInfo Manifest
manifest) (Manifest -> CachedDoc
manifestRaw Manifest
manifest) (Manifest -> ContentDigest
manifestDigest Manifest
manifest)))
Left MetadataError
err -> PackageName -> MetadataError -> IO ()
logFailure PackageName
name MetadataError
err IO ()
-> IO (Either MetadataError CacheEntry)
-> IO (Either MetadataError CacheEntry)
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Either MetadataError CacheEntry
-> IO (Either MetadataError CacheEntry)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (MetadataError -> Either MetadataError CacheEntry
forall a b. a -> Either a b
Left MetadataError
err)
resolveVersionHybrid :: PackageName -> Version -> IO (Either MetadataError (Maybe PackageDetails))
resolveVersionHybrid :: PackageName
-> Version -> IO (Either MetadataError (Maybe PackageDetails))
resolveVersionHybrid PackageName
name Version
version = case ManifestCaching
caching of
ManifestCaching
Uncached -> PackageName
-> Version -> IO (Either MetadataError (Maybe PackageDetails))
versionLeader PackageName
name Version
version
Cached MetadataCache
cache Source
source -> do
cached <- MetadataCache
-> Source
-> PackageName
-> Version
-> IO (Maybe (Maybe PackageDetails))
cachedVersion MetadataCache
cache Source
source PackageName
name Version
version
case cached of
Just Maybe PackageDetails
details -> Either MetadataError (Maybe PackageDetails)
-> IO (Either MetadataError (Maybe PackageDetails))
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe PackageDetails -> Either MetadataError (Maybe PackageDetails)
forall a b. b -> Either a b
Right Maybe PackageDetails
details)
Maybe (Maybe PackageDetails)
Nothing -> do
warm <- MetadataCache -> Source -> PackageName -> IO (Maybe CacheEntry)
cachedMetadata MetadataCache
cache Source
source PackageName
name
case warm of
Just CacheEntry
entry -> Either MetadataError (Maybe PackageDetails)
-> IO (Either MetadataError (Maybe PackageDetails))
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe PackageDetails -> Either MetadataError (Maybe PackageDetails)
forall a b. b -> Either a b
Right (Version -> PackageInfo -> Maybe PackageDetails
selectVersion Version
version (CacheEntry -> PackageInfo
entryInfo CacheEntry
entry)))
Maybe CacheEntry
Nothing -> MetricsPort
-> MetadataCache
-> Source
-> PackageName
-> Version
-> IO (Either MetadataError (Maybe PackageDetails))
-> IO (Either MetadataError (Maybe PackageDetails))
resolveVersion MetricsPort
metrics MetadataCache
cache Source
source PackageName
name Version
version (PackageName
-> Version -> IO (Either MetadataError (Maybe PackageDetails))
versionLeader PackageName
name Version
version)
versionLeader :: PackageName -> Version -> IO (Either MetadataError (Maybe PackageDetails))
versionLeader :: PackageName
-> Version -> IO (Either MetadataError (Maybe PackageDetails))
versionLeader PackageName
name Version
version = do
PackageName -> IO ()
logFetch PackageName
name
MetricsPort
-> Upstream
-> IO (Either MetadataError (Maybe PackageDetails))
-> IO (Either MetadataError (Maybe PackageDetails))
forall a.
MetricsPort
-> Upstream
-> IO (Either MetadataError a)
-> IO (Either MetadataError a)
recordedFetch MetricsPort
metrics Upstream
upstream (IO (Either MetadataError (Maybe PackageDetails))
-> IO (Either MetadataError (Maybe PackageDetails)))
-> IO (Either MetadataError (Maybe PackageDetails))
-> IO (Either MetadataError (Maybe PackageDetails))
forall a b. (a -> b) -> a -> b
$
PackageName
-> Version -> IO (Either MetadataError (Maybe PackageDetails))
rawFetchVersion PackageName
name Version
version IO (Either MetadataError (Maybe PackageDetails))
-> (Either MetadataError (Maybe PackageDetails)
-> IO (Either MetadataError (Maybe PackageDetails)))
-> IO (Either MetadataError (Maybe PackageDetails))
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Right Maybe PackageDetails
details -> Either MetadataError (Maybe PackageDetails)
-> IO (Either MetadataError (Maybe PackageDetails))
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe PackageDetails -> Either MetadataError (Maybe PackageDetails)
forall a b. b -> Either a b
Right Maybe PackageDetails
details)
Left MetadataError
err -> PackageName -> MetadataError -> IO ()
logFailure PackageName
name MetadataError
err IO ()
-> IO (Either MetadataError (Maybe PackageDetails))
-> IO (Either MetadataError (Maybe PackageDetails))
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Either MetadataError (Maybe PackageDetails)
-> IO (Either MetadataError (Maybe PackageDetails))
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (MetadataError -> Either MetadataError (Maybe PackageDetails)
forall a b. a -> Either a b
Left MetadataError
err)
selectVersion :: Version -> PackageInfo -> Maybe PackageDetails
selectVersion :: Version -> PackageInfo -> Maybe PackageDetails
selectVersion Version
version PackageInfo
info = Text -> Map Text PackageDetails -> Maybe PackageDetails
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup (Version -> Text
renderVersion Version
version) (PackageInfo -> Map Text PackageDetails
infoVersions PackageInfo
info)
entryToManifest :: CacheEntry -> Manifest
entryToManifest :: CacheEntry -> Manifest
entryToManifest CacheEntry
entry =
Manifest
{ manifestInfo :: PackageInfo
manifestInfo = CacheEntry -> PackageInfo
entryInfo CacheEntry
entry
, manifestRaw :: CachedDoc
manifestRaw = CacheEntry -> CachedDoc
entryRaw CacheEntry
entry
, manifestDigest :: ContentDigest
manifestDigest = CacheEntry -> ContentDigest
entryDigest CacheEntry
entry
}
recordedFetch :: MetricsPort -> Metric.Upstream -> IO (Either MetadataError a) -> IO (Either MetadataError a)
recordedFetch :: forall a.
MetricsPort
-> Upstream
-> IO (Either MetadataError a)
-> IO (Either MetadataError a)
recordedFetch MetricsPort
metrics Upstream
upstream IO (Either MetadataError a)
action = do
(result, seconds) <- IO (Either MetadataError a) -> IO (Either MetadataError a, Double)
forall (m :: * -> *) a. MonadIO m => m a -> m (a, Double)
timedSeconds IO (Either MetadataError a)
action
case result of
Right a
_ -> MetricsPort -> Upstream -> StatusClass -> Double -> IO ()
mpUpstreamFetch MetricsPort
metrics Upstream
upstream StatusClass
Metric.Status2xx Double
seconds
Left MetadataError
err -> MetricsPort -> Upstream -> Cause -> IO ()
mpUpstreamFetchError MetricsPort
metrics Upstream
upstream (MetadataError -> Cause
metadataErrorCause MetadataError
err)
pure result
metadataErrorCause :: MetadataError -> Metric.Cause
metadataErrorCause :: MetadataError -> Cause
metadataErrorCause = \case
MetadataError
MetadataUndecodable -> Cause
Metric.Decode
MetadataNameMismatch Text
_ -> Cause
Metric.Decode
MetadataBoundExceeded LimitError
_ -> Cause
Metric.OtherCause
MetadataUrlUnformable UrlFormationError
_ -> Cause
Metric.OtherCause
MetadataUnreachable TransportFault
_ -> Cause
Metric.Connection