Skip to content
This repository was archived by the owner on Dec 8, 2022. It is now read-only.

Commit 81b7ef5

Browse files
committed
[CAD-1838] Add whitelisting (listing) to return delisted.
1 parent 94e74c7 commit 81b7ef5

File tree

10 files changed

+182
-1083
lines changed

10 files changed

+182
-1083
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
dist-newstyle/
33
*~
44
tags
5+
stack.yaml.lock
56
result*

schema/migration-2-0003-20201001.sql

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
-- Persistent generated migration.
2+
3+
CREATE FUNCTION migrate() RETURNS void AS $$
4+
DECLARE
5+
next_version int ;
6+
BEGIN
7+
SELECT stage_two + 1 INTO next_version FROM schema_version ;
8+
IF next_version = 3 THEN
9+
ALTER TABLE "pool_metadata_reference" ALTER COLUMN "pool_id" TYPE text;
10+
ALTER TABLE "pool_metadata_reference" ALTER COLUMN "url" TYPE text;
11+
ALTER TABLE "pool_metadata_reference" ALTER COLUMN "hash" TYPE text;
12+
ALTER TABLE "pool_metadata" ALTER COLUMN "pool_id" TYPE text;
13+
ALTER TABLE "pool_metadata" ALTER COLUMN "ticker_name" TYPE text;
14+
ALTER TABLE "pool_metadata" ALTER COLUMN "hash" TYPE text;
15+
ALTER TABLE "pool_metadata" ALTER COLUMN "metadata" TYPE text;
16+
ALTER TABLE "pool_metadata_fetch_error" ALTER COLUMN "pool_id" TYPE text;
17+
ALTER TABLE "pool_metadata_fetch_error" ALTER COLUMN "pool_hash" TYPE text;
18+
ALTER TABLE "delisted_pool" ALTER COLUMN "pool_id" TYPE text;
19+
ALTER TABLE "delisted_pool" ADD CONSTRAINT "unique_delisted_pool" UNIQUE("pool_id");
20+
ALTER TABLE "delisted_pool" DROP CONSTRAINT "unique_blacklisted_pool";
21+
ALTER TABLE "reserved_ticker" ALTER COLUMN "name" TYPE text;
22+
ALTER TABLE "reserved_ticker" ALTER COLUMN "pool_hash" TYPE text;
23+
-- Hand written SQL statements can be added here.
24+
UPDATE schema_version SET stage_two = 3 ;
25+
RAISE NOTICE 'DB has been migrated to stage_two version %', next_version ;
26+
END IF ;
27+
END ;
28+
$$ LANGUAGE plpgsql ;
29+
30+
SELECT migrate() ;
31+
32+
DROP FUNCTION migrate() ;

smash.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ library
5050
-- DB operations
5151
, Cardano.Db.Error
5252
, Cardano.Db.Insert
53+
, Cardano.Db.Delete
5354
, Cardano.Db.Query
5455
, Cardano.Db.Types
5556

src/Cardano/Db/Delete.hs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{-# LANGUAGE FlexibleContexts #-}
2+
{-# LANGUAGE TypeFamilies #-}
3+
4+
module Cardano.Db.Delete
5+
( deleteDelistedPool
6+
) where
7+
8+
import Cardano.Prelude hiding (Meta)
9+
10+
import Control.Monad.IO.Class (MonadIO)
11+
import Control.Monad.Trans.Reader (ReaderT)
12+
13+
import Database.Persist.Class (AtLeastOneUniqueKey, Key, PersistEntityBackend,
14+
getByValue, insert, checkUnique)
15+
import Database.Persist.Sql (SqlBackend, (==.), deleteCascade, selectKeysList)
16+
import Database.Persist.Types (entityKey)
17+
18+
import Cardano.Db.Schema
19+
import Cardano.Db.Error
20+
import qualified Cardano.Db.Types as Types
21+
22+
-- | Delete a delisted pool if it exists. Returns 'True' if it did exist and has been
23+
-- deleted and 'False' if it did not exist.
24+
deleteDelistedPool :: MonadIO m => Types.PoolId -> ReaderT SqlBackend m Bool
25+
deleteDelistedPool poolId = do
26+
keys <- selectKeysList [ DelistedPoolPoolId ==. poolId ] []
27+
mapM_ deleteCascade keys
28+
pure $ not (null keys)
29+

src/Cardano/Db/Error.hs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ data DBFail
2626
| UnableToEncodePoolMetadataToJSON !Text
2727
| UnknownError !Text
2828
| ReservedTickerAlreadyInserted !Text
29+
| RecordDoesNotExist
2930
deriving (Eq, Show, Generic)
3031

3132
{-
@@ -86,7 +87,11 @@ instance ToJSON DBFail where
8687
[ "code" .= String "ReservedTickerAlreadyInserted"
8788
, "description" .= String (renderLookupFail failure)
8889
]
89-
90+
toJSON failure@(RecordDoesNotExist) =
91+
object
92+
[ "code" .= String "RecordDoesNotExist"
93+
, "description" .= String (renderLookupFail failure)
94+
]
9095

9196
renderLookupFail :: DBFail -> Text
9297
renderLookupFail lf =
@@ -100,4 +105,5 @@ renderLookupFail lf =
100105
UnableToEncodePoolMetadataToJSON err -> "Unable to encode the content to JSON. " <> err
101106
UnknownError text -> "Unknown error. Context: " <> text
102107
ReservedTickerAlreadyInserted tickerName -> "Ticker '" <> tickerName <> "' has already been inserted."
108+
RecordDoesNotExist -> "The requested record does not exist."
103109

src/Cardano/Db/Schema.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ share
114114
-- A table containing a list of delisted pools.
115115
DelistedPool
116116
poolId Types.PoolId sqltype=text
117-
UniqueBlacklistedPool poolId
117+
UniqueDelistedPool poolId
118118

119119
-- A table containing a managed list of reserved ticker names.
120120
-- For now they are grouped under the specific hash of the pool.

src/DB.hs

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds)
2323

2424
import Types
2525

26+
import Cardano.Db.Delete (deleteDelistedPool)
2627
import Cardano.Db.Insert (insertDelistedPool,
2728
insertPoolMetadata,
2829
insertPoolMetadataFetchError,
@@ -65,6 +66,7 @@ data DataLayer = DataLayer
6566
, dlGetDelistedPools :: IO [PoolId]
6667
, dlCheckDelistedPool :: PoolId -> IO Bool
6768
, dlAddDelistedPool :: PoolId -> IO (Either DBFail PoolId)
69+
, dlRemoveDelistedPool :: PoolId -> IO (Either DBFail PoolId)
6870

6971
, dlGetAdminUsers :: IO (Either DBFail [AdminUser])
7072

@@ -86,28 +88,25 @@ stubbedDataLayer ioDataMap ioDelistedPool = DataLayer
8688
case (Map.lookup (poolId, poolmdHash) ioDataMap') of
8789
Just poolOfflineMetadata' -> return . Right $ ("Test", poolOfflineMetadata')
8890
Nothing -> return $ Left (DbLookupPoolMetadataHash poolId poolmdHash)
89-
9091
, dlAddPoolMetadata = \ _ poolId poolmdHash poolMetadata poolTicker -> do
9192
-- TODO(KS): What if the pool metadata already exists?
9293
_ <- modifyIORef ioDataMap (Map.insert (poolId, poolmdHash) poolMetadata)
9394
return . Right $ poolMetadata
9495

95-
, dlAddReservedTicker = \tickerName poolMetadataHash -> panic "!"
96+
, dlAddMetaDataReference = \poolId poolUrl poolMetadataHash -> panic "!"
9697

98+
, dlAddReservedTicker = \tickerName poolMetadataHash -> panic "!"
9799
, dlCheckReservedTicker = \tickerName -> panic "!"
98100

99-
, dlAddMetaDataReference = \poolId poolUrl poolMetadataHash -> panic "!"
100-
101101
, dlGetDelistedPools = readIORef ioDelistedPool
102-
103102
, dlCheckDelistedPool = \poolId -> do
104103
blacklistedPool' <- readIORef ioDelistedPool
105104
return $ poolId `elem` blacklistedPool'
106-
107105
, dlAddDelistedPool = \poolId -> do
108-
_ <- modifyIORef ioDelistedPool (\pool -> [poolId] ++ pool)
109-
-- TODO(KS): Do I even need to query this?
110-
_blacklistedPool' <- readIORef ioDelistedPool
106+
_ <- modifyIORef ioDelistedPool (\pools -> [poolId] ++ pools)
107+
return $ Right poolId
108+
, dlRemoveDelistedPool = \poolId -> do
109+
_ <- modifyIORef ioDelistedPool (\pools -> filter (/= poolId) pools)
111110
return $ Right poolId
112111

113112
, dlGetAdminUsers = return $ Right []
@@ -132,9 +131,7 @@ postgresqlDataLayer = DataLayer
132131
poolMetadata <- runDbAction Nothing $ queryPoolMetadata poolId poolMetadataHash
133132
let poolTickerName = Types.getTickerName . poolMetadataTickerName <$> poolMetadata
134133
let poolMetadata' = Types.getPoolMetadata . poolMetadataMetadata <$> poolMetadata
135-
-- Ugh. Very sorry about this.
136134
return $ (,) <$> poolTickerName <*> poolMetadata'
137-
138135
, dlAddPoolMetadata = \ mRefId poolId poolHash poolMetadata poolTicker -> do
139136
let poolTickerName = Types.TickerName $ getPoolTicker poolTicker
140137
_ <- runDbAction Nothing $ insertPoolMetadata $ PoolMetadata poolId poolTickerName poolHash (Types.PoolMetadataRaw poolMetadata) mRefId
@@ -151,21 +148,24 @@ postgresqlDataLayer = DataLayer
151148

152149
, dlAddReservedTicker = \tickerName poolMetadataHash ->
153150
runDbAction Nothing $ insertReservedTicker $ ReservedTicker tickerName poolMetadataHash
154-
155151
, dlCheckReservedTicker = \tickerName ->
156152
runDbAction Nothing $ queryReservedTicker tickerName
157153

158154
, dlGetDelistedPools = do
159155
delistedPoolsDB <- runDbAction Nothing queryAllDelistedPools
160156
-- Convert from DB-specific type to the "general" type
161157
return $ map (\delistedPoolDB -> PoolId . getPoolId $ delistedPoolPoolId delistedPoolDB) delistedPoolsDB
162-
163158
, dlCheckDelistedPool = \poolId -> do
164159
runDbAction Nothing $ queryDelistedPool poolId
165-
166160
, dlAddDelistedPool = \poolId -> do
167161
delistedPoolId <- runDbAction Nothing $ insertDelistedPool $ DelistedPool poolId
168162
return $ Right poolId
163+
, dlRemoveDelistedPool = \poolId -> do
164+
isDeleted <- runDbAction Nothing $ deleteDelistedPool poolId
165+
-- Up for a discussion, but this might be more sensible in the lower DB layer.
166+
if isDeleted
167+
then return $ Right poolId
168+
else return $ Left RecordDoesNotExist
169169

170170
, dlGetAdminUsers = do
171171
adminUsers <- runDbAction Nothing $ queryAdminUsers
@@ -174,11 +174,9 @@ postgresqlDataLayer = DataLayer
174174
, dlAddFetchError = \poolMetadataFetchError -> do
175175
poolMetadataFetchErrorId <- runDbAction Nothing $ insertPoolMetadataFetchError poolMetadataFetchError
176176
return $ Right poolMetadataFetchErrorId
177-
178177
, dlGetFetchErrors = \mPoolId -> do
179178
poolMetadataFetchErrors <- runDbAction Nothing (queryPoolMetadataFetchError mPoolId)
180179
pure $ sequence $ Right <$> map convertPoolMetadataFetchError poolMetadataFetchErrors
181-
182180
}
183181

184182
convertPoolMetadataFetchError :: PoolMetadataFetchError -> PoolFetchError

src/Lib.hs

Lines changed: 79 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,26 @@ type DelistedPoolsAPI = "api" :> "v1" :> "delisted" :> ApiRes Get [PoolId]
5656
#ifdef DISABLE_BASIC_AUTH
5757
type DelistPoolAPI = "api" :> "v1" :> "delist" :> ReqBody '[JSON] PoolId :> ApiRes Patch PoolId
5858

59+
type EnlistPoolAPI = "api" :> "v1" :> "enlist" :> ReqBody '[JSON] PoolId :> ApiRes Patch PoolId
60+
5961
type FetchPoolErrorAPI = "api" :> "v1" :> "errors" :> QueryParam "poolId" PoolId :> ApiRes Get [PoolFetchError]
6062
#else
6163
-- The basic auth.
6264
type BasicAuthURL = BasicAuth "smash" User
6365

6466
type DelistPoolAPI = BasicAuthURL :> "api" :> "v1" :> "delist" :> ReqBody '[JSON] PoolId :> ApiRes Patch PoolId
6567

68+
type EnlistPoolAPI = BasicAuthURL :> "api" :> "v1" :> "enlist" :> ReqBody '[JSON] PoolId :> ApiRes Patch PoolId
69+
6670
type FetchPoolErrorAPI = BasicAuthURL :> "api" :> "v1" :> "errors" :> QueryParam "poolId" PoolId :> ApiRes Get [PoolFetchError]
6771
#endif
6872

69-
type SmashAPI = OfflineMetadataAPI :<|> DelistPoolAPI :<|> FetchPoolErrorAPI :<|> DelistedPoolsAPI
73+
-- The full API.
74+
type SmashAPI = OfflineMetadataAPI
75+
:<|> DelistedPoolsAPI
76+
:<|> DelistPoolAPI
77+
:<|> EnlistPoolAPI
78+
:<|> FetchPoolErrorAPI
7079

7180
-- | Swagger spec for Todo API.
7281
todoSwagger :: Swagger
@@ -82,7 +91,7 @@ todoSwagger =
8291
Nothing
8392
Nothing
8493
Nothing
85-
"0.0.1"
94+
"1.1.0"
8695

8796
-- | API for serving @swagger.json@.
8897
type SwaggerAPI = "swagger.json" :> Get '[JSON] Swagger
@@ -97,10 +106,6 @@ fullAPI = Proxy
97106
smashApi :: Proxy SmashAPI
98107
smashApi = Proxy
99108

100-
-- 403 if it is delisted
101-
-- 404 if it is not available (e.g. it could not be downloaded, or was invalid)
102-
-- 200 with the JSON content. Note that this must be the original content with the expected hash, not a re-rendering of the original.
103-
104109
runApp :: Configuration -> IO ()
105110
runApp configuration = do
106111
let port = cPortNumber configuration
@@ -223,48 +228,15 @@ server :: Configuration -> DataLayer -> Server API
223228
server configuration dataLayer
224229
= return todoSwagger
225230
:<|> getPoolOfflineMetadata dataLayer
226-
:<|> postDelistPool dataLayer
227-
:<|> fetchPoolErrorAPI dataLayer
228231
:<|> getDelistedPools dataLayer
229-
230-
#ifdef DISABLE_BASIC_AUTH
231-
fetchPoolErrorAPI :: DataLayer -> Maybe PoolId -> Handler (ApiResult DBFail [PoolFetchError])
232-
fetchPoolErrorAPI dataLayer mPoolId = convertIOToHandler $ do
233-
234-
let getFetchErrors = dlGetFetchErrors dataLayer
235-
fetchErrors <- getFetchErrors mPoolId
236-
237-
return . ApiResult $ fetchErrors
238-
#else
239-
fetchPoolErrorAPI :: DataLayer -> User -> Maybe PoolId -> Handler (ApiResult DBFail [PoolFetchError])
240-
fetchPoolErrorAPI dataLayer _user mPoolId = convertIOToHandler $ do
241-
242-
let getFetchErrors = dlGetFetchErrors dataLayer
243-
fetchErrors <- getFetchErrors mPoolId
244-
245-
return . ApiResult $ fetchErrors
246-
#endif
247-
248-
#ifdef DISABLE_BASIC_AUTH
249-
postDelistPool :: DataLayer -> PoolId -> Handler (ApiResult DBFail PoolId)
250-
postDelistPool dataLayer poolId = convertIOToHandler $ do
251-
252-
let addDelistedPool = dlAddDelistedPool dataLayer
253-
delistedPool' <- addDelistedPool poolId
254-
255-
return . ApiResult $ delistedPool'
256-
#else
257-
postDelistPool :: DataLayer -> User -> PoolId -> Handler (ApiResult DBFail PoolId)
258-
postDelistPool dataLayer user poolId = convertIOToHandler $ do
259-
260-
let addDelistedPool = dlAddDelistedPool dataLayer
261-
delistedPool' <- addDelistedPool poolId
262-
263-
return . ApiResult $ delistedPool'
264-
#endif
232+
:<|> delistPool dataLayer
233+
:<|> enlistPool dataLayer
234+
:<|> fetchPoolErrorAPI dataLayer
265235

266236

267-
-- throwError err404
237+
-- 403 if it is delisted
238+
-- 404 if it is not available (e.g. it could not be downloaded, or was invalid)
239+
-- 200 with the JSON content. Note that this must be the original content with the expected hash, not a re-rendering of the original.
268240
getPoolOfflineMetadata
269241
:: DataLayer
270242
-> PoolId
@@ -306,6 +278,68 @@ getDelistedPools dataLayer = convertIOToHandler $ do
306278
allDelistedPools <- getAllDelisted
307279
return . ApiResult . Right $ allDelistedPools
308280

281+
282+
#ifdef DISABLE_BASIC_AUTH
283+
delistPool :: DataLayer -> PoolId -> Handler (ApiResult DBFail PoolId)
284+
delistPool dataLayer poolId = convertIOToHandler $ do
285+
286+
let addDelistedPool = dlAddDelistedPool dataLayer
287+
delistedPool' <- addDelistedPool poolId
288+
289+
return . ApiResult $ delistedPool'
290+
#else
291+
delistPool :: DataLayer -> User -> PoolId -> Handler (ApiResult DBFail PoolId)
292+
delistPool dataLayer user poolId = convertIOToHandler $ do
293+
294+
let addDelistedPool = dlAddDelistedPool dataLayer
295+
delistedPool' <- addDelistedPool poolId
296+
297+
return . ApiResult $ delistedPool'
298+
#endif
299+
300+
301+
#ifdef DISABLE_BASIC_AUTH
302+
enlistPool :: DataLayer -> PoolId -> Handler (ApiResult DBFail PoolId)
303+
enlistPool dataLayer poolId = convertIOToHandler $ do
304+
305+
let removeDelistedPool = dlRemoveDelistedPool dataLayer
306+
delistedPool' <- removeDelistedPool poolId
307+
308+
case delistedPool' of
309+
Left err -> throwIO err404
310+
Right poolId' -> return . ApiResult . Right $ poolId
311+
#else
312+
enlistPool :: DataLayer -> User -> PoolId -> Handler (ApiResult DBFail PoolId)
313+
enlistPool dataLayer user poolId = convertIOToHandler $ do
314+
315+
let removeDelistedPool = dlRemoveDelistedPool dataLayer
316+
delistedPool' <- removeDelistedPool poolId
317+
318+
case delistedPool' of
319+
Left err -> throwIO err404
320+
Right poolId' -> return . ApiResult . Right $ poolId'
321+
#endif
322+
323+
324+
#ifdef DISABLE_BASIC_AUTH
325+
fetchPoolErrorAPI :: DataLayer -> Maybe PoolId -> Handler (ApiResult DBFail [PoolFetchError])
326+
fetchPoolErrorAPI dataLayer mPoolId = convertIOToHandler $ do
327+
328+
let getFetchErrors = dlGetFetchErrors dataLayer
329+
fetchErrors <- getFetchErrors mPoolId
330+
331+
return . ApiResult $ fetchErrors
332+
#else
333+
fetchPoolErrorAPI :: DataLayer -> User -> Maybe PoolId -> Handler (ApiResult DBFail [PoolFetchError])
334+
fetchPoolErrorAPI dataLayer _user mPoolId = convertIOToHandler $ do
335+
336+
let getFetchErrors = dlGetFetchErrors dataLayer
337+
fetchErrors <- getFetchErrors mPoolId
338+
339+
return . ApiResult $ fetchErrors
340+
#endif
341+
342+
309343
-- For now, we just ignore the @BasicAuth@ definition.
310344
instance (HasSwagger api) => HasSwagger (BasicAuth name typo :> api) where
311345
toSwagger _ = toSwagger (Proxy :: Proxy api)

0 commit comments

Comments
 (0)