Skip to content

Commit 7711f14

Browse files
committed
remove packageinfojson state entirely
1 parent 9483db2 commit 7711f14

File tree

2 files changed

+85
-366
lines changed

2 files changed

+85
-366
lines changed

src/Distribution/Server/Features/PackageInfoJSON.hs

Lines changed: 85 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -17,39 +17,31 @@ import Prelude ()
1717
import Distribution.Server.Prelude
1818

1919
import qualified Data.Aeson as Aeson
20+
import Data.Aeson ((.=))
21+
import qualified Data.Aeson.Key as Key
2022
import qualified Data.ByteString.Lazy.Char8 as BS (toStrict)
23+
import qualified Data.Map.Strict as Map
2124
import qualified Data.Text as T
2225
import qualified Data.Vector as Vector
2326

2427
import Distribution.License (licenseToSPDX)
2528
import Distribution.Package (PackageIdentifier(..),
26-
PackageName, packageName,
2729
packageVersion)
2830
import qualified Distribution.Parsec as Parsec
2931
import qualified Distribution.PackageDescription.Parsec as PkgDescr
32+
import Distribution.Text (display)
3033
import qualified Distribution.Types.GenericPackageDescription as PkgDescr
3134
import qualified Distribution.Types.PackageDescription as PkgDescr
32-
import Distribution.Version (nullVersion)
35+
import qualified Distribution.Pretty as Pretty
36+
import Distribution.SPDX.License (License)
37+
import Distribution.Version (nullVersion, Version)
3338

34-
import Distribution.Server.Framework ((</>))
35-
import qualified Distribution.Server.Framework as Framework
36-
import Distribution.Server.Features.Core (CoreFeature(..),
37-
CoreResource(..),
38-
isPackageChangeAny)
39+
import qualified Distribution.Server.Framework as Framework
40+
import Distribution.Server.Features.Core (CoreFeature(..),
41+
CoreResource(..))
3942
import qualified Distribution.Server.Features.PreferredVersions as Preferred
40-
import Distribution.Server.Packages.Types (CabalFileText(..), pkgMetadataRevisions)
41-
import Distribution.Server.Framework.BackupRestore (RestoreBackup(..))
42-
43-
import Distribution.Server.Features.PackageInfoJSON.State (PackageBasicDescription(..),
44-
PackageBasicDescriptionDTO(..),
45-
PackageVersions(..),
46-
PackageInfoState(..),
47-
GetPackageInfo(..),
48-
ReplacePackageInfo(..),
49-
GetVersionsFor(..),
50-
SetVersionsFor(..),
51-
initialPackageInfoState
52-
)
43+
import Distribution.Server.Packages.Types (CabalFileText(..), pkgMetadataRevisions)
44+
5345
import Distribution.Utils.ShortText (fromShortText)
5446
import Data.Foldable (toList)
5547
import Data.Traversable (for)
@@ -58,6 +50,69 @@ import Data.Time (UTCTime)
5850
import Distribution.Server.Users.Types (UserName (..), UserInfo(..))
5951
import Distribution.Server.Features.Users (UserFeature(lookupUserInfo))
6052

53+
data PackageBasicDescription = PackageBasicDescription
54+
{ pbd_license :: !License
55+
, pbd_copyright :: !T.Text
56+
, pbd_synopsis :: !T.Text
57+
, pbd_description :: !T.Text
58+
, pbd_author :: !T.Text
59+
, pbd_homepage :: !T.Text
60+
, pbd_metadata_revision :: !Int
61+
, pbd_uploaded_at :: !UTCTime
62+
} deriving (Eq, Show)
63+
64+
65+
66+
-- | Data type used in the `/package/:packagename` JSON endpoint
67+
data PackageBasicDescriptionDTO = PackageBasicDescriptionDTO
68+
{ license :: !License
69+
, copyright :: !T.Text
70+
, synopsis :: !T.Text
71+
, description :: !T.Text
72+
, author :: !T.Text
73+
, homepage :: !T.Text
74+
, metadata_revision :: !Int
75+
, uploaded_at :: !UTCTime
76+
, uploader :: !UserName
77+
} deriving (Eq, Show)
78+
79+
instance Aeson.ToJSON PackageBasicDescriptionDTO where
80+
toJSON PackageBasicDescriptionDTO {..} =
81+
Aeson.object
82+
[ Key.fromString "license" .= Pretty.prettyShow license
83+
, Key.fromString "copyright" .= copyright
84+
, Key.fromString "synopsis" .= synopsis
85+
, Key.fromString "description" .= description
86+
, Key.fromString "author" .= author
87+
, Key.fromString "homepage" .= homepage
88+
, Key.fromString "metadata_revision" .= metadata_revision
89+
, Key.fromString "uploaded_at" .= uploaded_at
90+
, Key.fromString "uploader" .= uploader
91+
]
92+
93+
94+
-- | An index of versions for one Hackage package
95+
-- and their preferred/deprecated status
96+
newtype PackageVersions = PackageVersions {
97+
unPackageVersions :: [(Version, Preferred.VersionStatus)]
98+
} deriving (Eq, Show)
99+
100+
-- | This encoding of @PackageVersions@ is used in the
101+
-- `/package/$package` endpoint (when the URI doesn't specify)
102+
-- a version. Any change here is an API change.
103+
instance Aeson.ToJSON PackageVersions where
104+
toJSON (PackageVersions p) =
105+
Aeson.toJSON
106+
$ Map.mapKeys display
107+
$ fmap encodeStatus
108+
$ Map.fromList p
109+
where
110+
encodeStatus = \case
111+
Preferred.NormalVersion -> "normal"
112+
Preferred.DeprecatedVersion -> "deprecated"
113+
Preferred.UnpreferredVersion -> "unpreferred"
114+
115+
61116

62117
data PackageInfoJSONFeature = PackageInfoJSONFeature {
63118
packageInfoJSONFeatureInterface :: Framework.HackageFeature
@@ -77,14 +132,10 @@ data PackageInfoJSONResource = PackageInfoJSONResource {
77132
-- | Initializing our feature involves adding JSON variants to the
78133
-- endpoints that serve basic information about a package-version,
79134
-- and a packages version deprecation status.
80-
-- Additionally we set up caching for these endpoints,
81-
-- and attach a package change hook that invalidates the cache
82-
-- line for a package when it changes
83135
initPackageInfoJSONFeature
84136
:: Framework.ServerEnv
85137
-> IO (CoreFeature -> Preferred.VersionsFeature -> UserFeature -> IO PackageInfoJSONFeature)
86-
initPackageInfoJSONFeature env = do
87-
packageInfoState <- packageInfoStateComponent False (Framework.serverStateDir env)
138+
initPackageInfoJSONFeature _env = do
88139
return $ \core preferred userFeature -> do
89140

90141
let coreR = coreResource core
@@ -98,36 +149,24 @@ initPackageInfoJSONFeature env = do
98149
Framework.resourceDesc = [(Framework.GET, info)]
99150
, Framework.resourceGet =
100151
[("json", servePackageBasicDescription coreR userFeature
101-
preferred packageInfoState)]
152+
preferred)]
102153
}
103154
, (Framework.extendResource (coreCabalFileRev coreR)) {
104155
Framework.resourceDesc = [(Framework.GET, vInfo)]
105156
, Framework.resourceGet =
106157
[("json", servePackageBasicDescription coreR userFeature
107-
preferred packageInfoState)]
158+
preferred)]
108159
}
109160
]
110161

111-
-- When a package is modified in any way, delet all its
112-
-- PackageInfoState cache lines.
113-
-- They will be recalculated next time the endpoint
114-
-- is hit
115-
postInit = Framework.registerHookJust
116-
(packageChangeHook core)
117-
isPackageChangeAny $ \(pkgid, _) -> do
118-
119-
Framework.updateState packageInfoState $
120-
SetVersionsFor (packageName pkgid) Nothing
121-
122162
return $ PackageInfoJSONFeature {
123163
packageInfoJSONFeatureInterface =
124164
(Framework.emptyHackageFeature "package-info-json")
125165
{ Framework.featureDesc = "Provide JSON endpoints for basic package descriptions"
126166
, Framework.featureResources = jsonResources
127167
, Framework.featureCaches = []
128-
, Framework.featurePostInit = postInit
129-
, Framework.featureState =
130-
[Framework.abstractAcidStateComponent packageInfoState]
168+
, Framework.featurePostInit = pure ()
169+
, Framework.featureState = []
131170
}
132171
}
133172

@@ -184,28 +223,27 @@ servePackageBasicDescription
184223
:: CoreResource
185224
-> UserFeature
186225
-> Preferred.VersionsFeature
187-
-> Framework.StateComponent Framework.AcidState PackageInfoState
188226
-> Framework.DynamicPath
189227
-- ^ URI specifying a package and version `e.g. lens or lens-4.11`
190228
-> Framework.ServerPartE Framework.Response
191-
servePackageBasicDescription resource userFeature preferred packageInfoState dpath = do
229+
servePackageBasicDescription resource userFeature preferred dpath = do
192230

193231
let metadataRev :: Maybe Int = lookup "revision" dpath >>= Framework.fromReqURI
194232

195233
pkgid@(PackageIdentifier name version) <- packageInPath resource dpath
196234
guardValidPackageName resource name
197235

198236
if version /= nullVersion
199-
then lookupOrInsertDescr pkgid metadataRev
200-
else lookupOrInsertVersions name
237+
then fetchDescr pkgid metadataRev
238+
else Framework.toResponse . Aeson.toJSON <$> getVersionListing name
201239

202240
where
203241

204-
lookupOrInsertDescr
242+
fetchDescr
205243
:: PackageIdentifier
206244
-> Maybe Int
207245
-> Framework.ServerPartE Framework.Response
208-
lookupOrInsertDescr pkgid metadataRev = do
246+
fetchDescr pkgid metadataRev = do
209247
guardValidPackageId resource pkgid
210248
pkg <- lookupPackageId resource pkgid
211249

@@ -235,55 +273,10 @@ servePackageBasicDescription resource userFeature preferred packageInfoState dpa
235273
let packageInfoDTO = basicDescriptionToDTO uploader d
236274
return packageInfoDTO
237275

238-
lookupOrInsertVersions
239-
:: PackageName
240-
-> Framework.ServerPartE Framework.Response
241-
lookupOrInsertVersions pkgname = do
242-
cachedVersions <- Framework.queryState packageInfoState $
243-
GetVersionsFor pkgname
244-
vers :: PackageVersions <- case cachedVersions of
245-
Just vs -> return vs
246-
Nothing -> do
247-
vs <- getVersionListing pkgname
248-
Framework.updateState packageInfoState $
249-
SetVersionsFor pkgname (Just vs)
250-
return vs
251-
return $ Framework.toResponse $ Aeson.toJSON vers
252-
253276
getVersionListing name = do
254277
pkgs <- lookupPackageName resource name
255278
prefInfo <- Preferred.queryGetPreferredInfo preferred name
256279
return
257280
. PackageVersions
258281
. Preferred.classifyVersions prefInfo
259282
$ fmap packageVersion pkgs
260-
261-
-- | Our backup doesn't produce any entries, and backup restore
262-
-- returns an empty state. Our responses are cheap enough to
263-
-- compute that we would rather regenerate them by need than
264-
-- deal with the complexity persisting backups in
265-
-- yet-another-format
266-
packageInfoStateComponent
267-
:: Bool
268-
-> FilePath
269-
-> IO (Framework.StateComponent Framework.AcidState PackageInfoState)
270-
packageInfoStateComponent freshDB stateDir = do
271-
st <- Framework.openLocalStateFrom
272-
(stateDir </> "db" </> "PackageInfoJSON")
273-
(initialPackageInfoState freshDB)
274-
return Framework.StateComponent {
275-
stateDesc = "Preferred package versions"
276-
, stateHandle = st
277-
, getState = Framework.query st GetPackageInfo
278-
, putState = Framework.update st . ReplacePackageInfo
279-
, resetState = packageInfoStateComponent True
280-
, backupState = \_ -> return []
281-
, restoreState = nullRestore (initialPackageInfoState True)
282-
}
283-
where
284-
285-
nullRestore :: PackageInfoState -> RestoreBackup PackageInfoState
286-
nullRestore st = RestoreBackup {
287-
restoreEntry = \_ -> nullRestore <$> pure (initialPackageInfoState True)
288-
, restoreFinalize = return st
289-
}

0 commit comments

Comments
 (0)