Skip to content

Commit 535c2bb

Browse files
authored
Implement JSON endpoint for the top downloaded packages (#1189)
1 parent 03739ed commit 535c2bb

File tree

1 file changed

+31
-2
lines changed

1 file changed

+31
-2
lines changed

src/Distribution/Server/Features/DownloadCount.hs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
{-# LANGUAGE RankNTypes, NamedFieldPuns, RecordWildCards #-}
2+
{-# LANGUAGE DerivingStrategies #-}
3+
{-# LANGUAGE DeriveGeneric #-}
4+
{-# LANGUAGE DeriveAnyClass #-}
5+
{-# OPTIONS_GHC -Wno-orphans #-}
26
-- | Download counts
37
--
48
-- We maintain
@@ -33,12 +37,17 @@ import Distribution.Server.Features.Core
3337
import Distribution.Server.Features.Users
3438

3539
import Distribution.Package
36-
import Distribution.Server.Util.CountingMap (cmFromCSV)
40+
import Distribution.Server.Util.CountingMap (cmFromCSV, cmToList)
3741

3842
import Data.Time.Calendar (Day, addDays)
3943
import Data.Time.Clock (getCurrentTime, utctDay)
4044
import Control.Concurrent.Chan
4145
import Control.Concurrent (forkIO)
46+
import GHC.Generics (Generic)
47+
import Data.Aeson (ToJSON)
48+
import qualified Data.Aeson as Aeson
49+
import Data.List (sortBy)
50+
import Data.Function (on)
4251

4352
data DownloadFeature = DownloadFeature {
4453
downloadFeatureInterface :: HackageFeature
@@ -54,6 +63,14 @@ data DownloadResource = DownloadResource {
5463
topDownloads :: Resource
5564
}
5665

66+
data PackageDownloads = PackageDownloads {
67+
packageName :: !String
68+
, downloads :: !Int
69+
}
70+
deriving stock (Eq, Ord, Generic)
71+
deriving anyclass (ToJSON)
72+
73+
5774
initDownloadFeature :: ServerEnv
5875
-> IO (CoreFeature -> UserFeature -> IO DownloadFeature)
5976
initDownloadFeature serverEnv@ServerEnv{serverStateDir} = do
@@ -176,10 +193,22 @@ downloadFeature CoreFeature{}
176193

177194
updateState inMemState $ RegisterDownload pkg
178195

196+
179197
downloadResource = DownloadResource {
180-
topDownloads = resourceAt "/packages/top.:format"
198+
topDownloads = (resourceAt "/packages/top.:format")
199+
{ resourceDesc = [ (GET, "Get top downloaded packages for the last 30 days")]
200+
, resourceGet = [ ("json", serveDownloadTopJSON) ]
201+
}
181202
}
182203

204+
serveDownloadTopJSON :: DynamicPath -> ServerPartE Response
205+
serveDownloadTopJSON _ = do
206+
pkgList <- sortedPackages <$> recentPackageDownloads
207+
pure $ toResponse $ Aeson.toJSON pkgList
208+
209+
sortedPackages :: RecentDownloads -> [PackageDownloads]
210+
sortedPackages = fmap (\(p, c) -> PackageDownloads (unPackageName p) c) . sortBy (flip compare `on` snd) . cmToList
211+
183212
downloadCSV = (resourceAt "/packages/downloads.:format") {
184213
resourceDesc = [ (GET, "Get download counts")
185214
, (PUT, "Upload download counts (for import)")

0 commit comments

Comments
 (0)