221 lines
8.0 KiB
Haskell
221 lines
8.0 KiB
Haskell
module Network.Minio.S3API
|
|
(
|
|
getLocation
|
|
|
|
-- * Listing buckets
|
|
--------------------
|
|
, getService
|
|
|
|
-- * Listing objects
|
|
--------------------
|
|
, listObjects
|
|
|
|
-- * Retrieving objects
|
|
-----------------------
|
|
, getObject
|
|
|
|
-- * Creating buckets and objects
|
|
---------------------------------
|
|
, putBucket
|
|
, putObject
|
|
|
|
-- * Deletion APIs
|
|
--------------------------
|
|
, deleteBucket
|
|
, deleteObject
|
|
|
|
-- * Multipart Upload APIs
|
|
--------------------------
|
|
, newMultipartUpload
|
|
, putObjectPart
|
|
, completeMultipartUpload
|
|
, abortMultipartUpload
|
|
, listIncompleteUploads
|
|
) where
|
|
|
|
import qualified Data.Conduit as C
|
|
import Data.Default (def)
|
|
import qualified Network.HTTP.Conduit as NC
|
|
import qualified Network.HTTP.Types as HT
|
|
|
|
import Lib.Prelude
|
|
|
|
import Network.Minio.Data
|
|
import Network.Minio.API
|
|
import Network.Minio.Utils
|
|
import Network.Minio.XmlParser
|
|
import Network.Minio.XmlGenerator
|
|
|
|
|
|
-- | Fetch all buckets from the service.
|
|
getService :: Minio [BucketInfo]
|
|
getService = do
|
|
resp <- executeRequest $ def
|
|
parseListBuckets $ NC.responseBody resp
|
|
|
|
-- | Fetch bucket location (region)
|
|
getLocation :: Bucket -> Minio Text
|
|
getLocation bucket = do
|
|
resp <- executeRequest $ def { riBucket = Just bucket
|
|
, riQueryParams = [("location", Nothing)]
|
|
}
|
|
parseLocation $ NC.responseBody resp
|
|
|
|
-- | GET an object from the service and return the response headers
|
|
-- and a conduit source for the object content
|
|
getObject :: Bucket -> Object -> HT.Query -> [HT.Header]
|
|
-> Minio ([HT.Header], C.ResumableSource Minio ByteString)
|
|
getObject bucket object queryParams headers = do
|
|
resp <- mkStreamRequest reqInfo
|
|
return $ (NC.responseHeaders resp, NC.responseBody resp)
|
|
where
|
|
reqInfo = def { riBucket = Just bucket
|
|
, riObject = Just object
|
|
, riQueryParams = queryParams
|
|
, riHeaders = headers}
|
|
|
|
-- | Creates a bucket via a PUT bucket call.
|
|
putBucket :: Bucket -> Region -> Minio ()
|
|
putBucket bucket location = do
|
|
void $ executeRequest $
|
|
def { riMethod = HT.methodPut
|
|
, riBucket = Just bucket
|
|
, riPayload = PayloadBS $ mkCreateBucketConfig location
|
|
}
|
|
|
|
-- | Single PUT object size.
|
|
maxSinglePutObjectSizeBytes :: Int64
|
|
maxSinglePutObjectSizeBytes = 5 * 1024 * 1024 * 1024
|
|
|
|
-- | PUT an object into the service. This function performs a single
|
|
-- PUT object call, and so can only transfer objects upto 5GiB.
|
|
putObject :: Bucket -> Object -> [HT.Header] -> Handle -> Int64
|
|
-> Int64 -> Minio ()
|
|
putObject bucket object headers h offset size = do
|
|
-- check length is within single PUT object size.
|
|
when (size > maxSinglePutObjectSizeBytes) $
|
|
throwError $ MErrValidation $ MErrVSinglePUTSizeExceeded size
|
|
|
|
-- content-length header is automatically set by library.
|
|
void $ executeRequest $
|
|
def { riMethod = HT.methodPut
|
|
, riBucket = Just bucket
|
|
, riObject = Just object
|
|
, riHeaders = headers
|
|
, riPayload = PayloadH h offset size
|
|
}
|
|
|
|
|
|
-- | List objects in a bucket matching prefix up to delimiter,
|
|
-- starting from nextToken.
|
|
listObjects :: Bucket -> Maybe Text -> Maybe Text -> Maybe Text
|
|
-> Minio ListObjectsResult
|
|
listObjects bucket prefix nextToken delimiter = do
|
|
resp <- executeRequest $ def { riMethod = HT.methodGet
|
|
, riBucket = Just bucket
|
|
, riQueryParams = ("list-type", Just "2") : qp
|
|
}
|
|
parseListObjectsResponse $ NC.responseBody resp
|
|
where
|
|
-- build optional query params
|
|
ctokList = map ((\k -> ("continuation_token", k)) . Just . encodeUtf8) $
|
|
maybeToList nextToken
|
|
prefixList = map ((\k -> ("prefix", k)) . Just . encodeUtf8) $
|
|
maybeToList prefix
|
|
delimList = map ((\k -> ("delimiter", k)) . Just . encodeUtf8) $
|
|
maybeToList delimiter
|
|
qp = concat [ctokList, prefixList, delimList]
|
|
|
|
-- | DELETE a bucket from the service.
|
|
deleteBucket :: Bucket -> Minio ()
|
|
deleteBucket bucket = do
|
|
void $ executeRequest $
|
|
def { riMethod = HT.methodDelete
|
|
, riBucket = Just bucket
|
|
}
|
|
|
|
-- | DELETE an object from the service.
|
|
deleteObject :: Bucket -> Object -> Minio ()
|
|
deleteObject bucket object = do
|
|
void $ executeRequest $
|
|
def { riMethod = HT.methodDelete
|
|
, riBucket = Just bucket
|
|
, riObject = Just object
|
|
}
|
|
|
|
-- | Create a new multipart upload.
|
|
newMultipartUpload :: Bucket -> Object -> [HT.Header] -> Minio UploadId
|
|
newMultipartUpload bucket object headers = do
|
|
resp <- executeRequest $ def { riMethod = HT.methodPost
|
|
, riBucket = Just bucket
|
|
, riObject = Just object
|
|
, riQueryParams = [("uploads", Nothing)]
|
|
, riHeaders = headers
|
|
}
|
|
parseNewMultipartUpload $ NC.responseBody resp
|
|
|
|
-- | PUT a part of an object as part of a multipart upload.
|
|
putObjectPart :: Bucket -> Object -> UploadId -> PartNumber -> [HT.Header]
|
|
-> Handle -> Int64 -> Int64 -> Minio PartInfo
|
|
putObjectPart bucket object uploadId partNumber headers h offset size = do
|
|
resp <- executeRequest $
|
|
def { riMethod = HT.methodPut
|
|
, riBucket = Just bucket
|
|
, riObject = Just object
|
|
, riQueryParams = [("partNumber", Just $ encodeUtf8 $
|
|
show partNumber),
|
|
("uploadId", Just $ encodeUtf8 uploadId)]
|
|
, riHeaders = headers
|
|
, riPayload = PayloadH h offset size
|
|
}
|
|
let rheaders = NC.responseHeaders resp
|
|
etag = getETagHeader rheaders
|
|
maybe
|
|
(throwError $ MErrValidation MErrVETagHeaderNotFound)
|
|
(return . PartInfo partNumber) etag
|
|
|
|
-- | Complete a multipart upload.
|
|
completeMultipartUpload :: Bucket -> Object -> UploadId -> [PartInfo]
|
|
-> Minio ETag
|
|
completeMultipartUpload bucket object uploadId partInfo = do
|
|
resp <- executeRequest $
|
|
def { riMethod = HT.methodPost
|
|
, riBucket = Just bucket
|
|
, riObject = Just object
|
|
, riQueryParams = [("uploadId", Just $ encodeUtf8 uploadId)]
|
|
, riPayload = PayloadBS $
|
|
mkCompleteMultipartUploadRequest partInfo
|
|
}
|
|
parseCompleteMultipartUploadResponse $ NC.responseBody resp
|
|
|
|
-- | Abort a multipart upload.
|
|
abortMultipartUpload :: Bucket -> Object -> UploadId -> Minio ()
|
|
abortMultipartUpload bucket object uploadId = do
|
|
void $ executeRequest $ def { riMethod = HT.methodDelete
|
|
, riBucket = Just bucket
|
|
, riObject = Just object
|
|
, riQueryParams = [("uploadId",
|
|
Just $ encodeUtf8 uploadId)]
|
|
}
|
|
|
|
-- | List incomplete multipart uploads.
|
|
listIncompleteUploads :: Bucket -> Maybe Text -> Maybe Text -> Maybe Text
|
|
-> Maybe Text -> Minio ListUploadsResult
|
|
listIncompleteUploads bucket prefix delimiter keyMarker uploadIdMarker = do
|
|
resp <- executeRequest $ def { riMethod = HT.methodGet
|
|
, riBucket = Just bucket
|
|
, riQueryParams = ("uploads", Nothing) : qp
|
|
}
|
|
parseListUploadsResponse $ NC.responseBody resp
|
|
where
|
|
-- build optional query params
|
|
prefixList = map ((\k -> ("prefix", k)) . Just . encodeUtf8) $
|
|
maybeToList prefix
|
|
delimList = map ((\k -> ("delimiter", k)) . Just . encodeUtf8) $
|
|
maybeToList delimiter
|
|
keyMarkerList = map ((\k -> ("key-marker", k)) . Just . encodeUtf8) $
|
|
maybeToList keyMarker
|
|
uploadIdMarkerList = map ((\k -> ("upload-id-marker", k)) . Just . encodeUtf8) $
|
|
maybeToList uploadIdMarker
|
|
qp = concat [prefixList, delimList, keyMarkerList, uploadIdMarkerList]
|