module Ecluse.Core.Registry.Npm.Filter (
rewriteTarballUrls,
assembleMergedPackument,
) where
import Data.Aeson (Value (Object, String))
import Data.Aeson.Key qualified as Key
import Data.Aeson.KeyMap (KeyMap)
import Data.Aeson.KeyMap qualified as KeyMap
import Data.Map.Strict qualified as Map
import Data.Text qualified as T
import Data.Time (UTCTime)
import Ecluse.Core.Package.Merge (MergePlan (mpDistTags, mpSurvivors, mpTime), SourceId)
import Ecluse.Core.Server.Route (isSafeComponent)
import Ecluse.Core.Text (renderIso8601Utc)
import Ecluse.Core.Version (renderVersion)
rewriteTarballUrls :: Text -> Value -> Value
rewriteTarballUrls :: Text -> Value -> Value
rewriteTarballUrls Text
base = \case
Object KeyMap Value
o
| Just Text
pkg <- Key -> KeyMap Value -> Maybe Text
stringField Key
"name" KeyMap Value
o
, Text -> Bool
safeName Text
pkg ->
KeyMap Value -> Value
Object (Key -> (Value -> Value) -> KeyMap Value -> KeyMap Value
adjustObject Key
"versions" ((Value -> Value) -> Value -> Value
mapValues (Text -> Value -> Value
rewriteVersion (Text -> Text -> Text
joinUrl Text
base Text
pkg))) KeyMap Value
o)
Value
other -> Value
other
safeName :: Text -> Bool
safeName :: Text -> Bool
safeName Text
name = (Text -> Bool) -> [Text] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Text -> Bool
isSafeComponent [Text]
components
where
components :: [Text]
components = case Text -> Text -> Maybe Text
T.stripPrefix Text
"@" Text
name of
Just Text
scopeAndBase ->
let (Text
scope, Text
base) = HasCallStack => Text -> Text -> (Text, Text)
Text -> Text -> (Text, Text)
T.breakOn Text
"/" Text
scopeAndBase
in if Text -> Bool
T.null Text
base then [Text
name] else [Text
scope, SourceId -> Text -> Text
T.drop SourceId
1 Text
base]
Maybe Text
Nothing -> [Text
name]
rewriteVersion :: Text -> Value -> Value
rewriteVersion :: Text -> Value -> Value
rewriteVersion Text
prefix = \case
Object KeyMap Value
vo -> KeyMap Value -> Value
Object (Key -> (Value -> Value) -> KeyMap Value -> KeyMap Value
adjustObject Key
"dist" (Text -> Value -> Value
rewriteDist Text
prefix) KeyMap Value
vo)
Value
other -> Value
other
rewriteDist :: Text -> Value -> Value
rewriteDist :: Text -> Value -> Value
rewriteDist Text
prefix = \case
Object KeyMap Value
dist
| Just Text
url <- Key -> KeyMap Value -> Maybe Text
stringField Key
"tarball" KeyMap Value
dist
, Just Text
file <- Text -> Maybe Text
tarballFile Text
url ->
KeyMap Value -> Value
Object (Key -> Value -> KeyMap Value -> KeyMap Value
forall v. Key -> v -> KeyMap v -> KeyMap v
KeyMap.insert Key
"tarball" (Text -> Value
String (Text
prefix Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"/-/" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
file)) KeyMap Value
dist)
Value
other -> Value
other
tarballFile :: Text -> Maybe Text
tarballFile :: Text -> Maybe Text
tarballFile Text
url =
let afterLastSlash :: Text
afterLastSlash = (Text, Text) -> Text
forall a b. (a, b) -> b
snd (HasCallStack => Text -> Text -> (Text, Text)
Text -> Text -> (Text, Text)
T.breakOnEnd Text
"/" Text
url)
in if Text -> Bool
T.null Text
afterLastSlash then Maybe Text
forall a. Maybe a
Nothing else Text -> Maybe Text
forall a. a -> Maybe a
Just Text
afterLastSlash
joinUrl :: Text -> Text -> Text
joinUrl :: Text -> Text -> Text
joinUrl Text
base Text
seg = (Char -> Bool) -> Text -> Text
T.dropWhileEnd (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'/') Text
base Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"/" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
seg
assembleMergedPackument :: Text -> Map SourceId Value -> MergePlan -> Value -> Value
assembleMergedPackument :: Text -> Map SourceId Value -> MergePlan -> Value -> Value
assembleMergedPackument Text
mountBase Map SourceId Value
bySource MergePlan
plan Value
base =
KeyMap Value -> Value
Object KeyMap Value
rebuilt
where
rebuilt :: KeyMap Value
rebuilt :: KeyMap Value
rebuilt =
KeyMap Value
baseObject
KeyMap Value -> (KeyMap Value -> KeyMap Value) -> KeyMap Value
forall a b. a -> (a -> b) -> b
& Key -> Value -> KeyMap Value -> KeyMap Value
forall v. Key -> v -> KeyMap v -> KeyMap v
KeyMap.insert Key
"versions" (KeyMap Value -> Value
Object KeyMap Value
survivingVersions)
KeyMap Value -> (KeyMap Value -> KeyMap Value) -> KeyMap Value
forall a b. a -> (a -> b) -> b
& Key -> Value -> KeyMap Value -> KeyMap Value
forall v. Key -> v -> KeyMap v -> KeyMap v
KeyMap.insert Key
"dist-tags" (KeyMap Value -> Value
Object KeyMap Value
distTags)
KeyMap Value -> (KeyMap Value -> KeyMap Value) -> KeyMap Value
forall a b. a -> (a -> b) -> b
& Key -> Value -> KeyMap Value -> KeyMap Value
forall v. Key -> v -> KeyMap v -> KeyMap v
KeyMap.insert Key
"time" (KeyMap Value -> Value
Object KeyMap Value
reconciledTime)
baseObject :: KeyMap Value
baseObject :: KeyMap Value
baseObject = case Value
base of
Object KeyMap Value
o -> KeyMap Value
o
Value
_ -> KeyMap Value
forall a. Monoid a => a
mempty
rewriteSurvivor :: Value -> Value
rewriteSurvivor :: Value -> Value
rewriteSurvivor = case Key -> KeyMap Value -> Maybe Text
stringField Key
"name" KeyMap Value
baseObject of
Just Text
pkg | Text -> Bool
safeName Text
pkg -> Text -> Value -> Value
rewriteVersion (Text -> Text -> Text
joinUrl Text
mountBase Text
pkg)
Maybe Text
_ -> Value -> Value
forall a. a -> a
id
survivingVersions :: KeyMap Value
survivingVersions :: KeyMap Value
survivingVersions =
[(Key, Value)] -> KeyMap Value
forall v. [(Key, v)] -> KeyMap v
KeyMap.fromList
[ (Text -> Key
Key.fromText Text
version, Value -> Value
rewriteSurvivor Value
object)
| (Text
version, SourceId
sid) <- Map Text SourceId -> [(Text, SourceId)]
forall k a. Map k a -> [(k, a)]
Map.toList (MergePlan -> Map Text SourceId
mpSurvivors MergePlan
plan)
, Just Value
object <- [SourceId -> Text -> Maybe Value
versionObjectFrom SourceId
sid Text
version]
]
versionsBySource :: Map SourceId (KeyMap Value)
versionsBySource :: Map SourceId (KeyMap Value)
versionsBySource = (Value -> Maybe (KeyMap Value))
-> Map SourceId Value -> Map SourceId (KeyMap Value)
forall a b k. (a -> Maybe b) -> Map k a -> Map k b
Map.mapMaybe Value -> Maybe (KeyMap Value)
versionsObjectOf Map SourceId Value
bySource
versionObjectFrom :: SourceId -> Text -> Maybe Value
versionObjectFrom :: SourceId -> Text -> Maybe Value
versionObjectFrom SourceId
sid Text
version =
SourceId -> Map SourceId (KeyMap Value) -> Maybe (KeyMap Value)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup SourceId
sid Map SourceId (KeyMap Value)
versionsBySource Maybe (KeyMap Value)
-> (KeyMap Value -> Maybe Value) -> Maybe Value
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Key -> KeyMap Value -> Maybe Value
forall v. Key -> KeyMap v -> Maybe v
KeyMap.lookup (Text -> Key
Key.fromText Text
version)
distTags :: KeyMap Value
distTags :: KeyMap Value
distTags =
[(Key, Value)] -> KeyMap Value
forall v. [(Key, v)] -> KeyMap v
KeyMap.fromList
[ (Text -> Key
Key.fromText Text
tag, Text -> Value
String (Version -> Text
renderVersion Version
v))
| (Text
tag, Version
v) <- Map Text Version -> [(Text, Version)]
forall k a. Map k a -> [(k, a)]
Map.toList (MergePlan -> Map Text Version
mpDistTags MergePlan
plan)
]
reconciledTime :: KeyMap Value
reconciledTime :: KeyMap Value
reconciledTime =
KeyMap Value
bookkeepingTime
KeyMap Value -> KeyMap Value -> KeyMap Value
forall a. Semigroup a => a -> a -> a
<> [(Key, Value)] -> KeyMap Value
forall v. [(Key, v)] -> KeyMap v
KeyMap.fromList
[ (Text -> Key
Key.fromText Text
version, Text -> Value
String (UTCTime -> Text
renderTime UTCTime
t))
| (Text
version, UTCTime
t) <- Map Text UTCTime -> [(Text, UTCTime)]
forall k a. Map k a -> [(k, a)]
Map.toList (MergePlan -> Map Text UTCTime
mpTime MergePlan
plan)
]
bookkeepingTime :: KeyMap Value
bookkeepingTime :: KeyMap Value
bookkeepingTime = case Key -> KeyMap Value -> Maybe Value
forall v. Key -> KeyMap v -> Maybe v
KeyMap.lookup Key
"time" KeyMap Value
baseObject of
Just (Object KeyMap Value
timeObject) ->
[(Key, Value)] -> KeyMap Value
forall v. [(Key, v)] -> KeyMap v
KeyMap.fromList
[ (Key
k, Value
value)
| Text
name <- [Text]
timeBookkeepingKeys
, let k :: Key
k = Text -> Key
Key.fromText Text
name
, Just Value
value <- [Key -> KeyMap Value -> Maybe Value
forall v. Key -> KeyMap v -> Maybe v
KeyMap.lookup Key
k KeyMap Value
timeObject]
]
Maybe Value
_ -> KeyMap Value
forall a. Monoid a => a
mempty
versionsObjectOf :: Value -> Maybe (KeyMap Value)
versionsObjectOf :: Value -> Maybe (KeyMap Value)
versionsObjectOf = \case
Object KeyMap Value
o | Just (Object KeyMap Value
vs) <- Key -> KeyMap Value -> Maybe Value
forall v. Key -> KeyMap v -> Maybe v
KeyMap.lookup Key
"versions" KeyMap Value
o -> KeyMap Value -> Maybe (KeyMap Value)
forall a. a -> Maybe a
Just KeyMap Value
vs
Value
_ -> Maybe (KeyMap Value)
forall a. Maybe a
Nothing
timeBookkeepingKeys :: [Text]
timeBookkeepingKeys :: [Text]
timeBookkeepingKeys = [Text
"created", Text
"modified"]
renderTime :: UTCTime -> Text
renderTime :: UTCTime -> Text
renderTime = UTCTime -> Text
renderIso8601Utc
mapValues :: (Value -> Value) -> Value -> Value
mapValues :: (Value -> Value) -> Value -> Value
mapValues Value -> Value
f = \case
Object KeyMap Value
o -> KeyMap Value -> Value
Object ((Value -> Value) -> KeyMap Value -> KeyMap Value
forall a b. (a -> b) -> KeyMap a -> KeyMap b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Value -> Value
f KeyMap Value
o)
Value
other -> Value
other
adjustObject :: Key.Key -> (Value -> Value) -> KeyMap Value -> KeyMap Value
adjustObject :: Key -> (Value -> Value) -> KeyMap Value -> KeyMap Value
adjustObject Key
key Value -> Value
f KeyMap Value
o = case Key -> KeyMap Value -> Maybe Value
forall v. Key -> KeyMap v -> Maybe v
KeyMap.lookup Key
key KeyMap Value
o of
Just Value
v -> Key -> Value -> KeyMap Value -> KeyMap Value
forall v. Key -> v -> KeyMap v -> KeyMap v
KeyMap.insert Key
key (Value -> Value
f Value
v) KeyMap Value
o
Maybe Value
Nothing -> KeyMap Value
o
stringField :: Key.Key -> KeyMap Value -> Maybe Text
stringField :: Key -> KeyMap Value -> Maybe Text
stringField Key
key KeyMap Value
o = case Key -> KeyMap Value -> Maybe Value
forall v. Key -> KeyMap v -> Maybe v
KeyMap.lookup Key
key KeyMap Value
o of
Just (String Text
s) -> Text -> Maybe Text
forall a. a -> Maybe a
Just Text
s
Maybe Value
_ -> Maybe Text
forall a. Maybe a
Nothing