{-# LANGUAGE OverloadedStrings #-} -- | CouchDB document attachments. -- -- /Note about attachment paths:/ Attachments may have embedded @\/@ -- characters that are sent unescaped to CouchDB. You can use this to -- provide a subtree of attachments under a document. A DocID must have -- any @\/@ escaped as @%2F@. So if you have document @a\/b\/c@ with an -- attachment @d\/e\/f.txt@, you would be able to access it at -- @http:\/\/couchdb\/db\/a%2fb%2fc\/d\/e\/f.txt@. -- -- @couchdb-conduit@ automaticaly normalizes attachment paths. module Database.CouchDB.Conduit.Attachment ( couchGetAttach, couchPutAttach, couchDeleteAttach ) where import Control.Exception.Lifted (throw) import Data.Maybe (fromMaybe) import Data.ByteString (ByteString) import Data.ByteString.Char8 (split) import qualified Data.Aeson as A import Data.Conduit (ResumableSource, ($$+-)) import qualified Data.Conduit.Attoparsec as CA import Network.HTTP.Conduit (RequestBody(..), Response(..)) import qualified Network.HTTP.Types as HT import Database.CouchDB.Conduit.Internal.Connection (MonadCouch (..), Path, Revision, mkPath) import Database.CouchDB.Conduit.Internal.Parser (extractRev) import Database.CouchDB.Conduit.LowLevel (couch, protect') -- | Get document attachment and @Content-Type@. couchGetAttach :: MonadCouch m => Path -- ^ Database -> Path -- ^ Document -> ByteString -- ^ Attachment path -> m (ResumableSource m ByteString, ByteString) couchGetAttach db doc att = do Response _ _ hs bsrc <- couch HT.methodGet (attachPath db doc att) [] [] (RequestBodyBS "") protect' return (bsrc, fromMaybe "" . lookup "Content-Type" $ hs) -- | Put or update document attachment couchPutAttach :: MonadCouch m => Path -- ^ Database -> Path -- ^ Document -> ByteString -- ^ Attachment path -> Revision -- ^ Document revision -> ByteString -- ^ Attacment @Content-Type@ -> RequestBody m -- ^ Attachment body -> m Revision couchPutAttach db doc att rev contentType body = do Response _ _ _ bsrc <- couch HT.methodPut (attachPath db doc att) [(HT.hContentType, contentType)] [("rev", Just rev)] body protect' j <- bsrc $$+- CA.sinkParser A.json either throw return $ extractRev j -- | Delete document attachment couchDeleteAttach :: MonadCouch m => Path -- ^ Database -> Path -- ^ Document -> ByteString -- ^ Attachment path -> Revision -- ^ Document revision -> m Revision couchDeleteAttach db doc att rev = do Response _ _ _ bsrc <- couch HT.methodDelete (attachPath db doc att) [] [("rev", Just rev)] (RequestBodyBS "") protect' j <- bsrc $$+- CA.sinkParser A.json either throw return $ extractRev j ------------------------------------------------------------------------------ -- Internal ------------------------------------------------------------------------------ -- | Make normalized attachment path attachPath :: Path -> Path -> ByteString -> Path attachPath db doc att = mkPath $ db : doc : attP where attP = split '/' att