11module Seabug.Metadata
2- ( FullSeabugMetadata
2+ ( BlockfrostFetchError (..)
3+ , FullSeabugMetadata
34 , Hash
45 , getFullSeabugMetadata
56 ) where
67
78import Contract.Prelude
89
910import Aeson as Aeson
11+ import Affjax (printError )
1012import Affjax as Affjax
1113import Affjax.RequestHeader as Affjax.RequestHeader
1214import Affjax.ResponseFormat as Affjax.ResponseFormat
1315import Cardano.Types.Value as Cardano.Types.Value
1416import Contract.Prim.ByteArray (byteArrayToHex )
15- import Contract.Transaction
16- ( ClientError (ClientHttpError, ClientDecodeJsonError)
17- )
1817import Contract.Value
1918 ( CurrencySymbol
2019 , TokenName
@@ -23,12 +22,13 @@ import Contract.Value
2322 , mkCurrencySymbol
2423 )
2524import Control.Alternative (guard )
25+ import Control.Monad.Error.Class (throwError )
2626import Control.Monad.Except.Trans (ExceptT (ExceptT), except , runExceptT )
2727import Control.Monad.Reader (ReaderT , runReaderT )
2828import Control.Monad.Reader.Trans (asks )
2929import Control.Monad.Trans.Class (lift )
3030import Data.Argonaut as Argonaut
31- import Data.Bifunctor (bimap , lmap )
31+ import Data.Bifunctor (lmap )
3232import Data.Function (on )
3333import Data.HTTP.Method (Method (GET))
3434import Metadata.Seabug (SeabugMetadata (SeabugMetadata))
@@ -42,27 +42,35 @@ type FullSeabugMetadata =
4242 , ipfsHash :: Hash
4343 }
4444
45- type BlockfrostFetch a = ExceptT ClientError
45+ data BlockfrostFetchError
46+ = BlockfrostRateLimit
47+ | BlockfrostOtherError String
48+
49+ derive instance Generic BlockfrostFetchError _
50+
51+ instance Show BlockfrostFetchError where
52+ show = genericShow
53+
54+ type BlockfrostFetch a = ExceptT BlockfrostFetchError
4655 (ReaderT { projectId :: String } Aff )
4756 a
4857
4958getFullSeabugMetadata
5059 :: CurrencySymbol /\ TokenName
5160 -> String
52- -> Aff (Either ClientError FullSeabugMetadata )
61+ -> Aff (Either BlockfrostFetchError FullSeabugMetadata )
5362getFullSeabugMetadata a@(currSym /\ _) projectId =
5463 flip runReaderT { projectId } <<< runExceptT $ do
55- seabugMetadata <- getMintingTxSeabugMetadata currSym =<<
56- getMintingTxHash
57- a
64+ seabugMetadata <-
65+ getMintingTxSeabugMetadata currSym =<< getMintingTxHash a
5866 ipfsHash <- getIpfsHash seabugMetadata
5967 pure { seabugMetadata, ipfsHash }
6068
6169getIpfsHash
6270 :: SeabugMetadata
6371 -> BlockfrostFetch Hash
6472getIpfsHash (SeabugMetadata { collectionNftCS, collectionNftTN }) = do
65- except <<< (decodeField " image" <=< decodeFieldJson " onchain_metadata" )
73+ except <<< (decodeField " image" <=< decodeField " onchain_metadata" )
6674 =<< mkGetRequest (" assets/" <> mkAsset curr collectionNftTN)
6775 where
6876 curr :: CurrencySymbol
@@ -77,13 +85,13 @@ getMintingTxSeabugMetadata
7785getMintingTxSeabugMetadata currSym txHash = do
7886 res <- mkGetRequest $ " txs/" <> txHash <> " /metadata"
7987 ms <- except
80- $ lmap ClientDecodeJsonError
88+ $ lmap ( BlockfrostOtherError <<< show)
8189 $ Aeson .caseAesonArray
8290 (Left (Argonaut.TypeMismatch " Expected array of objects" ))
8391 Right
84- ( Aeson .jsonToAeson res)
92+ res
8593 except
86- $ note (ClientDecodeJsonError ( Argonaut.UnexpectedValue res))
94+ $ note (BlockfrostOtherError ( " Unexpected JSON: " <> show res))
8795 $ findSeabugMetadata ms
8896 where
8997 findSeabugMetadata :: Array Aeson.Aeson -> Maybe SeabugMetadata
@@ -102,7 +110,7 @@ getMintingTxHash
102110 . CurrencySymbol /\ TokenName
103111 -> BlockfrostFetch Hash
104112getMintingTxHash a =
105- except <<< decodeFieldJson " initial_mint_tx_hash"
113+ except <<< decodeField " initial_mint_tx_hash"
106114 =<< mkGetRequest (" assets/" <> uncurry mkAsset a)
107115
108116mkAsset :: CurrencySymbol -> TokenName -> String
@@ -115,40 +123,41 @@ decodeField
115123 . Aeson.DecodeAeson a
116124 => String
117125 -> Aeson.Aeson
118- -> Either ClientError a
126+ -> Either BlockfrostFetchError a
119127decodeField field = do
120- lmap ClientDecodeJsonError <<<
128+ lmap ( BlockfrostOtherError <<< show) <<<
121129 ( Aeson .decodeAeson
122130 <=< Aeson .caseAesonObject
123131 (Left (Argonaut.TypeMismatch " Expected Object" ))
124132 (flip Aeson .getField field)
125133 )
126134
127- decodeFieldJson
128- :: forall (a :: Type )
129- . Aeson.DecodeAeson a
130- => String
131- -> Argonaut.Json
132- -> Either ClientError a
133- decodeFieldJson field = decodeField field <<< Aeson .jsonToAeson
134-
135135mkGetRequest
136136 :: forall (r :: Row Type )
137137 . String
138- -> BlockfrostFetch Argonaut.Json
138+ -> BlockfrostFetch Aeson.Aeson
139139mkGetRequest path = do
140140 projectId <- lift $ asks $ _.projectId
141141 let
142- req :: Affjax.Request Argonaut.Json
142+ req :: Affjax.Request String
143143 req = Affjax .defaultRequest
144144 { url = mkUrl
145- , responseFormat = Affjax.ResponseFormat .json
145+ , responseFormat = Affjax.ResponseFormat .string
146146 , method = Left GET
147147 , headers =
148148 [ Affjax.RequestHeader.RequestHeader " project_id" projectId
149149 ]
150150 }
151- ExceptT $ liftAff $ Affjax .request req <#> bimap ClientHttpError _.body
151+ res <- ExceptT $ liftAff $ do
152+ r <- Affjax .request req <#> lmap (BlockfrostOtherError <<< printError)
153+ case r of
154+ Left e -> log $ show e
155+ Right _ -> pure unit
156+ pure r
157+ when (unwrap res.status == 429 ) $ throwError $ BlockfrostRateLimit
158+ except $
159+ lmap (BlockfrostOtherError <<< ((" Error parsing JSON: " <> _) <<< show))
160+ (Aeson .parseJsonStringToAeson res.body)
152161 where
153162 mkUrl :: String
154163 mkUrl = " https://cardano-testnet.blockfrost.io/api/v0/" <> path
0 commit comments