cachix-api 1.0.1 → 1.1
raw patch · 4 files changed
+64/−21 lines, 4 filesdep +safe-exceptionsdep +stm-chansPVP ok
version bump matches the API change (PVP)
Dependencies added: safe-exceptions, stm-chans
API changes (from Hackage documentation)
- Cachix.API.WebSocketSubprotocol: recieveDataConcurrently :: Connection -> (ByteString -> IO ()) -> IO ()
+ Cachix.API.WebSocketSubprotocol: receiveDataConcurrently :: Connection -> (ByteString -> IO ()) -> IO ()
+ Cachix.Types.BinaryCache: XZ :: CompressionMethod
+ Cachix.Types.BinaryCache: ZSTD :: CompressionMethod
+ Cachix.Types.BinaryCache: [preferredCompressionMethod] :: BinaryCache -> CompressionMethod
+ Cachix.Types.BinaryCache: data CompressionMethod
+ Cachix.Types.BinaryCache: instance Control.DeepSeq.NFData Cachix.Types.BinaryCache.CompressionMethod
+ Cachix.Types.BinaryCache: instance Data.Aeson.Types.FromJSON.FromJSON Cachix.Types.BinaryCache.CompressionMethod
+ Cachix.Types.BinaryCache: instance Data.Aeson.Types.ToJSON.ToJSON Cachix.Types.BinaryCache.CompressionMethod
+ Cachix.Types.BinaryCache: instance Data.Swagger.Internal.ParamSchema.ToParamSchema Cachix.Types.BinaryCache.CompressionMethod
+ Cachix.Types.BinaryCache: instance Data.Swagger.Internal.Schema.ToSchema Cachix.Types.BinaryCache.CompressionMethod
+ Cachix.Types.BinaryCache: instance GHC.Generics.Generic Cachix.Types.BinaryCache.CompressionMethod
+ Cachix.Types.BinaryCache: instance GHC.Read.Read Cachix.Types.BinaryCache.CompressionMethod
+ Cachix.Types.BinaryCache: instance GHC.Show.Show Cachix.Types.BinaryCache.CompressionMethod
+ Cachix.Types.BinaryCache: instance Web.Internal.HttpApiData.FromHttpApiData Cachix.Types.BinaryCache.CompressionMethod
+ Cachix.Types.BinaryCache: instance Web.Internal.HttpApiData.ToHttpApiData Cachix.Types.BinaryCache.CompressionMethod
- Cachix.API: BinaryCacheAPI :: (route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> ("nix-cache-info" :> Get '[XNixCacheInfo, JSON] NixCacheInfo))))) -> (route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> (Capture "narinfohash" NarInfoHash :> Get '[XNixNarInfo, JSON] (Headers '[Header "Cache-Control" Text] CachixNarInfo)))))) -> (route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> (Capture "narinfohash" NarInfoHash :> Head))))) -> (route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> ("nar" :> (Capture "nar" NarFileName :> StreamGet NoFraming XNixNar (ConduitT () ByteStringStreaming (ResourceT IO) ()))))))) -> (route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> Get '[JSON] BinaryCache)))) -> (route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> ("narinfo" :> (Summary "Given a list of store hashes, return a list of those that are missing" :> (ReqBody '[JSON] [Text] :> Post '[JSON] [Text]))))))) -> (route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> ("narurl" :> (Capture "nar" NarFileName :> Get '[JSON] Text)))))) -> (route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> (Capture "narinfohash" NarInfoHash :> (ReqBody '[JSON] NarInfoCreate :> Post '[JSON] NoContent)))))) -> (route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> ("nar" :> (StreamBody NoFraming XNixNar (ConduitT () ByteStringStreaming (ResourceT IO) ()) :> Post '[JSON] NoContent)))))) -> (route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> ("serve" :> (Capture "storehash" Text :> (CaptureAll "filepath" Text :> (Summary "Serve a file from a given store path" :> Get '[XNixNar] (Headers '[Header "X-Content-Type-Options" Text, Header "Cache-Control" Text] LazyByteStringStreaming))))))))) -> (route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> ("key" :> (ReqBody '[JSON] SigningKeyCreate :> Post '[JSON] NoContent)))))) -> BinaryCacheAPI route
+ Cachix.API: BinaryCacheAPI :: (route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> ("nix-cache-info" :> Get '[XNixCacheInfo, JSON] NixCacheInfo))))) -> (route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> (Capture "narinfohash" NarInfoHash :> Get '[XNixNarInfo, JSON] (Headers '[Header "Cache-Control" Text] CachixNarInfo)))))) -> (route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> (Capture "narinfohash" NarInfoHash :> Head))))) -> (route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> ("nar" :> (Capture "nar" NarFileName :> StreamGet NoFraming XNixNar (ConduitT () ByteStringStreaming (ResourceT IO) ()))))))) -> (route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> Get '[JSON] BinaryCache)))) -> (route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> ("narinfo" :> (Summary "Given a list of store hashes, return a list of those that are missing" :> (ReqBody '[JSON] [Text] :> Post '[JSON] [Text]))))))) -> (route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> ("narurl" :> (Capture "nar" NarFileName :> Get '[JSON] Text)))))) -> (route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> (Capture "narinfohash" NarInfoHash :> (ReqBody '[JSON] NarInfoCreate :> Post '[JSON] NoContent)))))) -> (route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> ("nar" :> (QueryParam "compression" CompressionMethod :> (StreamBody NoFraming XNixNar (ConduitT () ByteStringStreaming (ResourceT IO) ()) :> Post '[JSON] NoContent))))))) -> (route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> ("serve" :> (Capture "storehash" Text :> (CaptureAll "filepath" Text :> (Summary "Serve a file from a given store path" :> Get '[XNixNar] (Headers '[Header "X-Content-Type-Options" Text, Header "Cache-Control" Text] LazyByteStringStreaming))))))))) -> (route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> ("key" :> (ReqBody '[JSON] SigningKeyCreate :> Post '[JSON] NoContent)))))) -> BinaryCacheAPI route
- Cachix.API: [createNar] :: BinaryCacheAPI route -> route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> ("nar" :> (StreamBody NoFraming XNixNar (ConduitT () ByteStringStreaming (ResourceT IO) ()) :> Post '[JSON] NoContent)))))
+ Cachix.API: [createNar] :: BinaryCacheAPI route -> route :- (CachixAuth :> ("cache" :> (Capture "name" Text :> ("nar" :> (QueryParam "compression" CompressionMethod :> (StreamBody NoFraming XNixNar (ConduitT () ByteStringStreaming (ResourceT IO) ()) :> Post '[JSON] NoContent))))))
- Cachix.Types.BinaryCache: BinaryCache :: Text -> Text -> Bool -> [Text] -> Text -> Permission -> BinaryCache
+ Cachix.Types.BinaryCache: BinaryCache :: Text -> Text -> Bool -> [Text] -> Text -> Permission -> CompressionMethod -> BinaryCache
Files
- cachix-api.cabal +4/−2
- src/Cachix/API.hs +1/−0
- src/Cachix/API/WebSocketSubprotocol.hs +40/−17
- src/Cachix/Types/BinaryCache.hs +19/−2
cachix-api.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: cachix-api-version: 1.0.1+version: 1.1 synopsis: Servant HTTP API specification for https://cachix.org homepage: https://github.com/cachix/cachix#readme bug-reports: https://github.com/cachix/cachix/issues@@ -79,10 +79,12 @@ , nix-narinfo , protolude , resourcet+ , safe-exceptions , servant >=0.14.1 , servant-auth , servant-auth-swagger , servant-client+ , stm-chans , string-conv , swagger2 , text@@ -127,4 +129,4 @@ , transformers , unordered-containers - build-tool-depends: hspec-discover:hspec-discover -any+ build-tool-depends: hspec-discover:hspec-discover
src/Cachix/API.hs view
@@ -102,6 +102,7 @@ :> "cache" :> Capture "name" Text :> "nar"+ :> QueryParam "compression" BinaryCache.CompressionMethod :> StreamBody NoFraming XNixNar (ConduitT () ByteStringStreaming.ByteStringStreaming (ResourceT IO) ()) :> Post '[JSON] NoContent, serveNarContent ::
src/Cachix/API/WebSocketSubprotocol.hs view
@@ -1,10 +1,10 @@-{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DuplicateRecordFields #-} module Cachix.API.WebSocketSubprotocol where import qualified Control.Concurrent.Async as Async-import qualified Control.Concurrent.Chan as Chan+import qualified Control.Concurrent.STM.TMQueue as TMQueue+import qualified Control.Exception.Safe as Safe import qualified Data.Aeson as Aeson import Data.Time (UTCTime) import Data.UUID (UUID)@@ -51,27 +51,50 @@ deriving (Show, Eq, Generic, Aeson.FromJSON, Aeson.ToJSON) parseMessage :: Aeson.FromJSON cmd => ByteString -> Either Text (Message cmd)-parseMessage body =- case Aeson.eitherDecodeStrict' body of- Left err -> Left $ toS err- Right message -> Right message+parseMessage = first toS . Aeson.eitherDecodeStrict' sendMessage :: Aeson.ToJSON cmd => WS.Connection -> Message cmd -> IO () sendMessage connection cmd =- WS.sendTextData connection $ Aeson.encode cmd+ WS.sendTextData connection (Aeson.encode cmd) +-- | Receive and process messages in parallel.+--+-- Note: This will not rethrow the 'CloseRequest' exception!+-- -- TODO: use Async.replicateConcurrently-recieveDataConcurrently :: WS.Connection -> (ByteString -> IO ()) -> IO ()-recieveDataConcurrently connection m = do- channel <- Chan.newChan- Async.race_ (producer channel) (consumer channel)+receiveDataConcurrently :: WS.Connection -> (ByteString -> IO ()) -> IO ()+receiveDataConcurrently connection action =+ do+ queue <- atomically TMQueue.newTMQueue+ Async.withAsync (consumer queue) (producer queue) where- producer channel = forever $ do- payload <- WS.receiveData connection- Chan.writeChan channel payload- consumer channel = forever $ do- payload <- Chan.readChan channel- m payload+ producer queue consumerThread =+ loop+ `finally` closeGracefully queue consumerThread+ `Safe.catch` closeRequest+ where+ loop = do+ payload <- WS.receiveData connection+ atomically $ TMQueue.writeTMQueue queue payload+ loop++ consumer queue = loop+ where+ loop = do+ payload <- atomically $ TMQueue.readTMQueue queue+ case payload of+ Nothing -> return ()+ Just msg -> action msg *> loop++ -- Close the queue and wait for the consumer finish processing messages.+ closeGracefully :: TMQueue.TMQueue a -> Async.Async () -> IO ()+ closeGracefully queue consumerThread = do+ atomically $ TMQueue.closeTMQueue queue+ Async.wait consumerThread++ closeRequest :: WS.ConnectionException -> IO ()+ closeRequest (WS.CloseRequest _ _) = return ()+ closeRequest e = throwIO e data Log = Log { line :: Text,
src/Cachix/Types/BinaryCache.hs view
@@ -2,8 +2,9 @@ import Cachix.Types.Permission (Permission) import Data.Aeson (FromJSON, ToJSON)-import Data.Swagger (ToSchema)+import Data.Swagger (ToParamSchema (..), ToSchema) import Protolude+import Servant.API data BinaryCache = BinaryCache { name :: Text,@@ -11,6 +12,22 @@ isPublic :: Bool, publicSigningKeys :: [Text], githubUsername :: Text,- permission :: Permission+ permission :: Permission,+ preferredCompressionMethod :: CompressionMethod } deriving (Show, Generic, FromJSON, ToJSON, ToSchema, NFData)++data CompressionMethod = XZ | ZSTD+ deriving (Show, Read, Generic, FromJSON, ToJSON, ToSchema, NFData)++instance FromHttpApiData CompressionMethod where+ parseUrlPiece "xz" = Right XZ+ parseUrlPiece "zst" = Right ZSTD+ parseUrlPiece compressionMethod = Left $ "Wrong compression method: " <> compressionMethod++instance ToHttpApiData CompressionMethod where+ toUrlPiece XZ = "xz"+ toUrlPiece ZSTD = "zst"++instance ToParamSchema CompressionMethod where+ toParamSchema _ = toParamSchema (Proxy :: Proxy Text)