packages feed

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 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)