minio-hs/src/Network/Minio/Data.hs
Aditya Manthramurthy 153c5a67cd Add high-level makeBucket API (#2)
* Add high-level makeBucket API

* Add build badge

* Bring back live server tests.
2017-02-13 16:06:01 +05:30

250 lines
6.8 KiB
Haskell

{-# LANGUAGE GeneralizedNewtypeDeriving, TypeFamilies #-}
module Network.Minio.Data where
import Control.Monad.Base
import qualified Control.Monad.Catch as MC
import Control.Monad.Trans.Control
import Control.Monad.Trans.Resource
import qualified Data.ByteString as B
import Data.Default (Default(..))
import Network.HTTP.Client (defaultManagerSettings, HttpException)
import qualified Network.HTTP.Conduit as NC
import Network.HTTP.Types (Method, Header, Query)
import qualified Network.HTTP.Types as HT
import Text.XML
import Lib.Prelude
-- | Connection Info data type. Use the Default instance to create
-- connection info for your service.
data ConnectInfo = ConnectInfo {
connectHost :: Text
, connectPort :: Int
, connectAccessKey :: Text
, connectSecretKey :: Text
, connectIsSecure :: Bool
, connectRegion :: Region
} deriving (Eq, Show)
instance Default ConnectInfo where
def = ConnectInfo "localhost" 9000 "minio" "minio123" False "us-east-1"
-- |
-- Default aws ConnectInfo. Credentials should be supplied before use.
awsCI :: ConnectInfo
awsCI = def {
connectHost = "s3.amazonaws.com"
, connectPort = 443
, connectAccessKey = ""
, connectSecretKey = ""
, connectIsSecure = True
}
-- |
-- Default minio play server ConnectInfo. Credentials are already filled.
minioPlayCI :: ConnectInfo
minioPlayCI = def {
connectHost = "play.minio.io"
, connectPort = 9000
, connectAccessKey = "Q3AM3UQ867SPQQA43P2F"
, connectSecretKey = "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG"
, connectIsSecure = True
}
-- |
-- Represents a bucket in the object store
type Bucket = Text
-- |
-- Represents an object name
type Object = Text
-- |
-- Represents a region
-- TODO: This could be a Sum Type with all defined regions for AWS.
type Region = Text
-- | A type alias to represent an Entity-Tag returned by S3-compatible
-- APIs.
type ETag = Text
-- |
-- BucketInfo returned for list buckets call
data BucketInfo = BucketInfo {
biName :: Bucket
, biCreationDate :: UTCTime
} deriving (Show, Eq)
-- | A type alias to represent a part-number for multipart upload
type PartNumber = Int16
-- | A type alias to represent an upload-id for multipart upload
type UploadId = Text
-- | A data-type to represent info about a part
data PartInfo = PartInfo PartNumber ETag
deriving (Show, Eq)
instance Ord PartInfo where
(PartInfo a _) `compare` (PartInfo b _) = a `compare` b
-- | Represents result from a listing of object parts of an ongoing
-- multipart upload.
data ListPartsResult = ListPartsResult {
lprHasMore :: Bool
, lprNextPart :: Maybe Int
, lprParts :: [ListPartInfo]
} deriving (Show, Eq)
-- | Represents information about an object part in an ongoing
-- multipart upload.
data ListPartInfo = ListPartInfo {
piNumber :: PartNumber
, piETag :: ETag
, piSize :: Int64
, piModTime :: UTCTime
} deriving (Show, Eq)
-- | Represents result from a listing of incomplete uploads to a
-- bucket.
data ListUploadsResult = ListUploadsResult {
lurHasMore :: Bool
, lurNextKey :: Maybe Text
, lurNextUpload :: Maybe Text
, lurUploads :: [UploadInfo]
, lurCPrefixes :: [Text]
} deriving (Show, Eq)
-- | Represents information about a multipart upload.
data UploadInfo = UploadInfo {
uiKey :: Object
, uiUploadId :: UploadId
, uiInitTime :: UTCTime
} deriving (Show, Eq)
-- | Represents result from a listing of objects in a bucket.
data ListObjectsResult = ListObjectsResult {
lorHasMore :: Bool
, lorNextToken :: Maybe Text
, lorObjects :: [ObjectInfo]
, lorCPrefixes :: [Text]
} deriving (Show, Eq)
-- | Represents information about an object.
data ObjectInfo = ObjectInfo {
oiObject :: Object
, oiModTime :: UTCTime
, oiETag :: ETag
, oiSize :: Int64
} deriving (Show, Eq)
-- | Represents different kinds of payload that are used with S3 API
-- requests.
data Payload = PayloadBS ByteString
| PayloadH Handle
Int64 -- offset
Int64 -- size
instance Default Payload where
def = PayloadBS ""
data RequestInfo = RequestInfo {
riMethod :: Method
, riBucket :: Maybe Bucket
, riObject :: Maybe Object
, riQueryParams :: Query
, riHeaders :: [Header]
, riPayload :: Payload
, riPayloadHash :: ByteString
, riRegion :: Maybe Region
}
instance Default RequestInfo where
def = RequestInfo HT.methodGet def def def def def "" def
getPathFromRI :: RequestInfo -> ByteString
getPathFromRI ri = B.concat $ parts
where
objPart = maybe [] (\o -> ["/", encodeUtf8 o]) $ riObject ri
parts = maybe ["/"] (\b -> "/" : encodeUtf8 b : objPart) $ riBucket ri
getRegionFromRI :: RequestInfo -> Text
getRegionFromRI ri = maybe "us-east-1" identity (riRegion ri)
newtype Minio a = Minio {
unMinio :: ReaderT MinioConn (ResourceT IO) a
}
deriving (
Functor
, Applicative
, Monad
, MonadIO
, MonadReader MinioConn
, MonadThrow
, MonadCatch
, MonadBase IO
, MonadResource
)
instance MonadBaseControl IO Minio where
type StM Minio a = a
liftBaseWith f = Minio $ liftBaseWith $ \q -> f (q . unMinio)
restoreM = Minio . restoreM
-- | MinioConn holds connection info and a connection pool
data MinioConn = MinioConn {
mcConnInfo :: ConnectInfo
, mcConnManager :: NC.Manager
}
-- | Takes connection information and returns a connection object to
-- be passed to 'runMinio'
connect :: ConnectInfo -> IO MinioConn
connect ci = do
let settings = bool defaultManagerSettings NC.tlsManagerSettings $
connectIsSecure ci
mgr <- NC.newManager settings
return $ MinioConn ci mgr
-- | Run the Minio action and return the result or an error.
runMinio :: ConnectInfo -> Minio a -> ResourceT IO (Either MinioErr a)
runMinio ci m = do
conn <- liftIO $ connect ci
flip runReaderT conn . unMinio $
(m >>= (return . Right)) `MC.catches`
[MC.Handler handlerME, MC.Handler handlerHE, MC.Handler handlerFE]
where
handlerME = return . Left . ME
handlerHE = return . Left . MEHttp
handlerFE = return . Left . MEFile
s3Name :: Text -> Name
s3Name s = Name s (Just "http://s3.amazonaws.com/doc/2006-03-01/") Nothing
---------------------------------
-- Errors
---------------------------------
-- | Various validation errors
data MErrV = MErrVSinglePUTSizeExceeded Int64
| MErrVPutSizeExceeded Int64
| MErrVETagHeaderNotFound
deriving (Show, Eq)
-- | Errors thrown by the library
data MinioErr = ME MError
| MEHttp HttpException
| MEFile IOException
deriving (Show)
instance Exception MinioErr
-- | Library internal errors
data MError = XMLParseError Text
| ResponseError (NC.Response LByteString)
| ValidationError MErrV
deriving (Show, Eq)
instance Exception MError