minio-hs 1.4.0 → 1.5.0
raw patch · 29 files changed
+417/−289 lines, 29 filesdep −containersPVP ok
version bump matches the API change (PVP)
Dependencies removed: containers
API changes (from Hackage documentation)
+ Network.Minio: data GetObjectResponse
+ Network.Minio: gorObjectInfo :: GetObjectResponse -> ObjectInfo
+ Network.Minio: gorObjectStream :: GetObjectResponse -> ConduitM () ByteString Minio ()
+ Network.Minio: oiUserMetadata :: ObjectInfo -> HashMap Text Text
- Network.Minio: getObject :: Bucket -> Object -> GetObjectOptions -> Minio (ConduitM () ByteString Minio ())
+ Network.Minio: getObject :: Bucket -> Object -> GetObjectOptions -> Minio GetObjectResponse
- Network.Minio: oiMetadata :: ObjectInfo -> Map Text Text
+ Network.Minio: oiMetadata :: ObjectInfo -> HashMap Text Text
- Network.Minio: presignedPostPolicy :: PostPolicy -> Minio (ByteString, Map Text ByteString)
+ Network.Minio: presignedPostPolicy :: PostPolicy -> Minio (ByteString, HashMap Text ByteString)
- Network.Minio.S3API: getObject' :: Bucket -> Object -> Query -> [Header] -> Minio ([Header], ConduitM () ByteString Minio ())
+ Network.Minio.S3API: getObject' :: Bucket -> Object -> Query -> [Header] -> Minio GetObjectResponse
- Network.Minio.S3API: presignedPostPolicy :: PostPolicy -> Minio (ByteString, Map Text ByteString)
+ Network.Minio.S3API: presignedPostPolicy :: PostPolicy -> Minio (ByteString, HashMap Text ByteString)
Files
- CHANGELOG.md +11/−0
- README.md +3/−3
- docs/API.md +183/−182
- examples/BucketExists.hs +1/−1
- examples/CopyObject.hs +1/−1
- examples/FileUploader.hs +1/−1
- examples/GetObject.hs +1/−1
- examples/HeadObject.hs +1/−1
- examples/ListBuckets.hs +1/−1
- examples/ListIncompleteUploads.hs +1/−1
- examples/ListObjects.hs +1/−1
- examples/MakeBucket.hs +1/−1
- examples/PresignedGetObject.hs +1/−1
- examples/PresignedPostPolicy.hs +3/−3
- examples/PresignedPutObject.hs +1/−1
- examples/PutObject.hs +1/−1
- examples/RemoveBucket.hs +1/−1
- examples/RemoveIncompleteUpload.hs +1/−1
- minio-hs.cabal +1/−4
- src/Network/Minio.hs +9/−5
- src/Network/Minio/API.hs +2/−2
- src/Network/Minio/Data.hs +41/−19
- src/Network/Minio/PresignedOperations.hs +5/−5
- src/Network/Minio/S3API.hs +32/−22
- src/Network/Minio/Sign/V4.hs +5/−5
- src/Network/Minio/Utils.hs +22/−7
- src/Network/Minio/XmlParser.hs +8/−6
- test/LiveServer.hs +75/−9
- test/Network/Minio/XmlParser/Test.hs +3/−3
CHANGELOG.md view
@@ -1,6 +1,17 @@ Changelog ========== +## Version 1.5.0++* Switch to faster map data type - all previous usage of+ Data.Map.Strict and Data.Set is replaced with Data.HashMap.Strict+ and Data.HashSet.+* Add `oiUserMetadata` to parse and return user metadata stored with+ an object.+* Add `GetObjectResponse` data type for the value returned by+ `getObject`. It now contains parsed ObjectInfo along with the+ conduit of object bytes.+ ## Version 1.4.0 * Expose runMinioRes and runMinioResWith (#129)
README.md view
@@ -25,7 +25,7 @@ ``` A section of the tests use the remote MinIO Play server at-`https://play.min.io:9000` by default. For library development,+`https://play.min.io` by default. For library development, using this remote server maybe slow. To run the tests against a locally running MinIO live server at `http://localhost:9000`, just set the environment `MINIO_LOCAL` to any value (and unset it to switch@@ -47,7 +47,7 @@ -- stack --resolver lts-11.1 runghc --package minio-hs --package optparse-applicative --package filepath ----- MinIO Haskell SDK, (C) 2017, 2018 MinIO, Inc.+-- MinIO Haskell SDK, (C) 2017-2019 MinIO, Inc. -- -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License.@@ -76,7 +76,7 @@ import Prelude -- | The following example uses minio's play server at--- https://play.min.io:9000. The endpoint and associated+-- https://play.min.io. The endpoint and associated -- credentials are provided via the libary constant, -- -- > minioPlayCI :: ConnectInfo
docs/API.md view
@@ -20,15 +20,15 @@ ``` -|Bucket operations|Object Operations|Presigned Operations|-|:---|:---|:---|-|[`listBuckets`](#listBuckets) |[`getObject`](#getObject)|[`presignedGetObjectUrl`](#presignedGetObjectUrl)|-|[`makeBucket`](#makeBucket)|[`putObject`](#putObject)|[`presignedPutObjectUrl`](#presignedPutObjectUrl)|-|[`removeBucket`](#removeBucket)|[`fGetObject`](#fGetObject)|[`presignedPostPolicy`](#presignedPostPolicy)|-|[`listObjects`](#listObjects)|[`fPutObject`](#fPutObject)||-|[`listObjectsV1`](#listObjectsV1)|[`copyObject`](#copyObject)||-|[`listIncompleteUploads`](#listIncompleteUploads)|[`removeObject`](#removeObject)||-|[`bucketExists`](#bucketExists)|[`selectObjectContent`](#selectObjectContent)||+| Bucket operations | Object Operations | Presigned Operations |+|:--------------------------------------------------|:----------------------------------------------|:--------------------------------------------------|+| [`listBuckets`](#listBuckets) | [`getObject`](#getObject) | [`presignedGetObjectUrl`](#presignedGetObjectUrl) |+| [`makeBucket`](#makeBucket) | [`putObject`](#putObject) | [`presignedPutObjectUrl`](#presignedPutObjectUrl) |+| [`removeBucket`](#removeBucket) | [`fGetObject`](#fGetObject) | [`presignedPostPolicy`](#presignedPostPolicy) |+| [`listObjects`](#listObjects) | [`fPutObject`](#fPutObject) | |+| [`listObjectsV1`](#listObjectsV1) | [`copyObject`](#copyObject) | |+| [`listIncompleteUploads`](#listIncompleteUploads) | [`removeObject`](#removeObject) | |+| [`bucketExists`](#bucketExists) | [`selectObjectContent`](#selectObjectContent) | | ## 1. Connecting and running operations on the storage service @@ -69,16 +69,16 @@ The parameters in the expression `awsWithRegion region autoDiscover` are: -|Parameter|Type|Description|-|:---|:---|:---|-| `region` | _Region_ (alias for `Text`) | The region to connect to by default for all requests. |-| `autoDiscover` | _Bool_ | If `True`, region discovery will be enabled. If `False`, discovery is disabled, and all requests go the given region only.|+| Parameter | Type | Description |+|:---------------|:----------------------------|:---------------------------------------------------------------------------------------------------------------------------|+| `region` | _Region_ (alias for `Text`) | The region to connect to by default for all requests. |+| `autoDiscover` | _Bool_ | If `True`, region discovery will be enabled. If `False`, discovery is disabled, and all requests go the given region only. | #### minioPlayCI :: ConnectInfo This constructor provides connection and authentication information to connect to the public MinIO Play server at-`https://play.min.io:9000/`.+`https://play.min.io/`. #### minioCI :: Text -> Int -> Bool -> ConnectInfo @@ -86,25 +86,25 @@ The parameters in the expression `minioCI host port isSecure` are: -|Parameter|Type|Description|-|:---|:---|:---|-| `host` | _Text_ | Hostname of the MinIO or other S3-API compatible server |-| `port` | _Int_ | Port number to connect to|-| `isSecure` | _Bool_ | Does the server use HTTPS? |+| Parameter | Type | Description |+|:-----------|:-------|:--------------------------------------------------------|+| `host` | _Text_ | Hostname of the MinIO or other S3-API compatible server |+| `port` | _Int_ | Port number to connect to |+| `isSecure` | _Bool_ | Does the server use HTTPS? | #### The ConnectInfo fields and Default instance The following table shows the fields in the `ConnectInfo` record-type: -| Field | Type | Description |-|:---|:---|:---|-| `connectHost` | _Text_ | Host name of the server. Defaults to `localhost`. |-| `connectPort` | _Int_ | Port number on which the server listens. Defaults to `9000`. |-| `connectAccessKey` | _Text_ | Access key to use in authentication. Defaults to `minio`. |-| `connectSecretkey` | _Text_ | Secret key to use in authentication. Defaults to `minio123`. |-| `connectIsSecure` | _Bool_ | Specifies if the server used TLS. Defaults to `False` |-| `connectRegion` | _Region_ (alias for `Text`) | Specifies the region to use. Defaults to 'us-east-1' |-| `connectAutoDiscoverRegion` | _Bool_ | Specifies if the library should automatically discover the region of a bucket. Defaults to `True`|+| Field | Type | Description |+|:----------------------------|:----------------------------|:--------------------------------------------------------------------------------------------------|+| `connectHost` | _Text_ | Host name of the server. Defaults to `localhost`. |+| `connectPort` | _Int_ | Port number on which the server listens. Defaults to `9000`. |+| `connectAccessKey` | _Text_ | Access key to use in authentication. Defaults to `minio`. |+| `connectSecretkey` | _Text_ | Secret key to use in authentication. Defaults to `minio123`. |+| `connectIsSecure` | _Bool_ | Specifies if the server used TLS. Defaults to `False` |+| `connectRegion` | _Region_ (alias for `Text`) | Specifies the region to use. Defaults to 'us-east-1' |+| `connectAutoDiscoverRegion` | _Bool_ | Specifies if the library should automatically discover the region of a bucket. Defaults to `True` | The `def` value of type `ConnectInfo` has all the above default values.@@ -148,17 +148,17 @@ __Return Value__ -|Return type |Description |-|:---|:---|-| _Minio [BucketInfo]_| List of buckets |+| Return type | Description |+|:---------------------|:----------------|+| _Minio [BucketInfo]_ | List of buckets | __BucketInfo record type__ -|Field |Type |Description |-|:---|:---| :---|-| `biName` | _Bucket_ (alias of `Text`) | Name of the bucket |-| `biCreationDate` | _UTCTime_ | Creation time of the bucket |+| Field | Type | Description |+|:-----------------|:---------------------------|:----------------------------|+| `biName` | _Bucket_ (alias of `Text`) | Name of the bucket |+| `biCreationDate` | _UTCTime_ | Creation time of the bucket | <a name="makeBucket"></a>@@ -171,10 +171,10 @@ In the expression `makeBucket bucketName region` the arguments are: -| Param | Type | Description |-|---|---|---|-|`bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |-| `region` | _Maybe Region_ | Region where the bucket is to be created. If not specified, default to the region in `ConnectInfo`.|+| Param | Type | Description |+|--------------|-----------------------------|-----------------------------------------------------------------------------------------------------|+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |+| `region` | _Maybe Region_ | Region where the bucket is to be created. If not specified, default to the region in `ConnectInfo`. | __Example__ @@ -200,9 +200,9 @@ In the expression `removeBucket bucketName` the arguments are: -| Param | Type | Description |-|---|---|---|-|`bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |+| Param | Type | Description |+|--------------|-----------------------------|--------------------|+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket | __Example__@@ -233,26 +233,27 @@ In the expression `listObjects bucketName prefix recursive` the arguments are: -|Param |Type |Description |-|:---|:---| :---|-| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |-| `prefix` | _Maybe Text_ | Optional prefix that listed objects should have |-| `recursive` | _Bool_ |`True` indicates recursive style listing and `False` indicates directory style listing delimited by '/'. |+| Param | Type | Description |+|:-------------|:----------------------------|:---------------------------------------------------------------------------------------------------------|+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |+| `prefix` | _Maybe Text_ | Optional prefix that listed objects should have |+| `recursive` | _Bool_ | `True` indicates recursive style listing and `False` indicates directory style listing delimited by '/'. | __Return Value__ -|Return type |Description |-|:---|:---|-| _C.ConduitM () ObjectInfo Minio ()_ | A Conduit Producer of `ObjectInfo` values corresponding to each object. |+| Return type | Description |+|:------------------------------------|:------------------------------------------------------------------------|+| _C.ConduitM () ObjectInfo Minio ()_ | A Conduit Producer of `ObjectInfo` values corresponding to each object. | __ObjectInfo record type__ -|Field |Type |Description |-|:---|:---| :---|-|`oiObject` | _Object_ (alias for `Text`) | Name of object |-|`oiModTime` | _UTCTime_ | Last modified time of the object |-|`oiETag` | _ETag_ (alias for `Text`) | ETag of the object |-|`oiSize` | _Int64_ | Size of the object in bytes |+| Field | Type | Description |+|:-------------|:----------------------------|:-------------------------------------|+| `oiObject` | _Object_ (alias for `Text`) | Name of object |+| `oiModTime` | _UTCTime_ | Last modified time of the object |+| `oiETag` | _ETag_ (alias for `Text`) | ETag of the object |+| `oiSize` | _Int64_ | Size of the object in bytes |+| `oiMetadata` | _HashMap Text Text_ | Map of key-value user-metadata pairs | __Example__ @@ -265,7 +266,7 @@ -- | The following example uses MinIO play server at--- https://play.min.io:9000. The endpoint and associated+-- https://play.min.io. The endpoint and associated -- credentials are provided via the libary constant, -- -- > minioPlayCI :: ConnectInfo@@ -294,26 +295,26 @@ In the expression `listObjectsV1 bucketName prefix recursive` the arguments are: -|Param |Type |Description |-|:---|:---| :---|-| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |-| `prefix` | _Maybe Text_ | Optional prefix that listed objects should have |-| `recursive` | _Bool_ |`True` indicates recursive style listing and `False` indicates directory style listing delimited by '/'. |+| Param | Type | Description |+|:-------------|:----------------------------|:---------------------------------------------------------------------------------------------------------|+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |+| `prefix` | _Maybe Text_ | Optional prefix that listed objects should have |+| `recursive` | _Bool_ | `True` indicates recursive style listing and `False` indicates directory style listing delimited by '/'. | __Return Value__ -|Return type |Description |-|:---|:---|-| _C.ConduitM () ObjectInfo Minio ()_ | A Conduit Producer of `ObjectInfo` values corresponding to each object. |+| Return type | Description |+|:------------------------------------|:------------------------------------------------------------------------|+| _C.ConduitM () ObjectInfo Minio ()_ | A Conduit Producer of `ObjectInfo` values corresponding to each object. | __ObjectInfo record type__ -|Field |Type |Description |-|:---|:---| :---|-|`oiObject` | _Object_ (alias for `Text`) | Name of object |-|`oiModTime` | _UTCTime_ | Last modified time of the object |-|`oiETag` | _ETag_ (alias for `Text`) | ETag of the object |-|`oiSize` | _Int64_ | Size of the object in bytes |+| Field | Type | Description |+|:------------|:----------------------------|:---------------------------------|+| `oiObject` | _Object_ (alias for `Text`) | Name of object |+| `oiModTime` | _UTCTime_ | Last modified time of the object |+| `oiETag` | _ETag_ (alias for `Text`) | ETag of the object |+| `oiSize` | _Int64_ | Size of the object in bytes | __Example__ @@ -326,7 +327,7 @@ -- | The following example uses MinIO play server at--- https://play.min.io:9000. The endpoint and associated+-- https://play.min.io. The endpoint and associated -- credentials are provided via the libary constant, -- -- > minioPlayCI :: ConnectInfo@@ -354,11 +355,11 @@ In the expression `listIncompleteUploads bucketName prefix recursive` the parameters are: -|Param |Type |Description |-|:---|:---| :---|-| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |-| `prefix` | _Maybe Text_ | Optional prefix that listed objects should have. |-| `recursive` | _Bool_ |`True` indicates recursive style listing and `Talse` indicates directory style listing delimited by '/'. |+| Param | Type | Description |+|:-------------|:----------------------------|:---------------------------------------------------------------------------------------------------------|+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |+| `prefix` | _Maybe Text_ | Optional prefix that listed objects should have. |+| `recursive` | _Bool_ | `True` indicates recursive style listing and `Talse` indicates directory style listing delimited by '/'. | __Return Value__ @@ -368,11 +369,11 @@ __UploadInfo record type__ -|Field |Type |Description |-|:---|:---| :---|-|`uiKey` | _Object_ |Name of incompletely uploaded object |-|`uiUploadId` | _String_ |Upload ID of incompletely uploaded object |-|`uiSize` | _Int64_ |Size of incompletely uploaded object |+| Field | Type | Description |+|:-------------|:---------|:------------------------------------------|+| `uiKey` | _Object_ | Name of incompletely uploaded object |+| `uiUploadId` | _String_ | Upload ID of incompletely uploaded object |+| `uiSize` | _Int64_ | Size of incompletely uploaded object | __Example__ @@ -384,7 +385,7 @@ import Prelude -- | The following example uses MinIO play server at--- https://play.min.io:9000. The endpoint and associated+-- https://play.min.io. The endpoint and associated -- credentials are provided via the libary constant, -- -- > minioPlayCI :: ConnectInfo@@ -415,29 +416,29 @@ In the expression `getObject bucketName objectName opts` the parameters are: -|Param |Type |Description |-|:---|:---| :---|-| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |-| `objectName` | _Object_ (alias for `Text`) | Name of the object |-| `opts` | _GetObjectOptions_ | Options for GET requests specifying additional options like If-Match, Range |+| Param | Type | Description |+|:-------------|:----------------------------|:----------------------------------------------------------------------------|+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |+| `objectName` | _Object_ (alias for `Text`) | Name of the object |+| `opts` | _GetObjectOptions_ | Options for GET requests specifying additional options like If-Match, Range | __GetObjectOptions record type__ -|Field |Type |Description |-|:---|:---| :---|-| `gooRange` | `Maybe ByteRanges` | Represents the byte range of object. E.g ByteRangeFromTo 0 9 represents first ten bytes of the object|-| `gooIfMatch` | `Maybe ETag` (alias for `Text`) | (Optional) ETag of object should match |-| `gooIfNoneMatch` | `Maybe ETag` (alias for `Text`) | (Optional) ETag of object shouldn't match |-| `gooIfUnmodifiedSince` | `Maybe UTCTime` | (Optional) Time since object wasn't modified |-| `gooIfModifiedSince` | `Maybe UTCTime` | (Optional) Time since object was modified |+| Field | Type | Description |+|:-----------------------|:--------------------------------|:------------------------------------------------------------------------------------------------------|+| `gooRange` | `Maybe ByteRanges` | Represents the byte range of object. E.g ByteRangeFromTo 0 9 represents first ten bytes of the object |+| `gooIfMatch` | `Maybe ETag` (alias for `Text`) | (Optional) ETag of object should match |+| `gooIfNoneMatch` | `Maybe ETag` (alias for `Text`) | (Optional) ETag of object shouldn't match |+| `gooIfUnmodifiedSince` | `Maybe UTCTime` | (Optional) Time since object wasn't modified |+| `gooIfModifiedSince` | `Maybe UTCTime` | (Optional) Time since object was modified | __Return Value__ The return value can be incrementally read to process the contents of the object.-|Return type |Description |-|:---|:---|-| _Minio (C.ConduitM () ByteString Minio ())_ | A Conduit source of `ByteString` values. |+| Return type | Description |+|:--------------------------------------------|:-----------------------------------------|+| _Minio (C.ConduitM () ByteString Minio ())_ | A Conduit source of `ByteString` values. | __Example__ @@ -451,7 +452,7 @@ import Prelude -- | The following example uses MinIO play server at--- https://play.min.io:9000. The endpoint and associated+-- https://play.min.io. The endpoint and associated -- credentials are provided via the libary constant, -- -- > minioPlayCI :: ConnectInfo@@ -482,13 +483,13 @@ In the expression `putObject bucketName objectName inputSrc` the parameters are: -|Param |Type |Description |-|:---|:---| :---|-| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |-| `objectName` | _Object_ (alias for `Text`) | Name of the object |-| `inputSrc` | _C.ConduitM () ByteString Minio ()_ | A Conduit producer of `ByteString` values |-| `size` | _Int64_ | Provide stream size (optional) |-| `opts` | _PutObjectOptions_ | Optional parameters to provide additional metadata for the object |+| Param | Type | Description |+|:-------------|:------------------------------------|:------------------------------------------------------------------|+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |+| `objectName` | _Object_ (alias for `Text`) | Name of the object |+| `inputSrc` | _C.ConduitM () ByteString Minio ()_ | A Conduit producer of `ByteString` values |+| `size` | _Int64_ | Provide stream size (optional) |+| `opts` | _PutObjectOptions_ | Optional parameters to provide additional metadata for the object | __Example__ @@ -501,7 +502,7 @@ import Prelude -- | The following example uses MinIO play server at--- https://play.min.io:9000. The endpoint and associated+-- https://play.min.io. The endpoint and associated -- credentials are provided via the libary constant, -- -- > minioPlayCI :: ConnectInfo@@ -533,23 +534,23 @@ In the expression `fGetObject bucketName objectName inputFile` the parameters are: -|Param |Type |Description |-|:---|:---| :---|-| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |-| `objectName` | _Object_ (alias for `Text`) | Name of the object |-| `inputFile` | _FilePath_ | Path to the file to be uploaded |-| `opts` | _GetObjectOptions_ | Options for GET requests specifying additional options like If-Match, Range |+| Param | Type | Description |+|:-------------|:----------------------------|:----------------------------------------------------------------------------|+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |+| `objectName` | _Object_ (alias for `Text`) | Name of the object |+| `inputFile` | _FilePath_ | Path to the file to be uploaded |+| `opts` | _GetObjectOptions_ | Options for GET requests specifying additional options like If-Match, Range | __GetObjectOptions record type__ -|Field |Type |Description |-|:---|:---| :---|-| `gooRange` | `Maybe ByteRanges` | Represents the byte range of object. E.g ByteRangeFromTo 0 9 represents first ten bytes of the object|-| `gooIfMatch` | `Maybe ETag` (alias for `Text`) | (Optional) ETag of object should match |-| `gooIfNoneMatch` | `Maybe ETag` (alias for `Text`) | (Optional) ETag of object shouldn't match |-| `gooIfUnmodifiedSince` | `Maybe UTCTime` | (Optional) Time since object wasn't modified |-| `gooIfModifiedSince` | `Maybe UTCTime` | (Optional) Time since object was modified |+| Field | Type | Description |+|:-----------------------|:--------------------------------|:------------------------------------------------------------------------------------------------------|+| `gooRange` | `Maybe ByteRanges` | Represents the byte range of object. E.g ByteRangeFromTo 0 9 represents first ten bytes of the object |+| `gooIfMatch` | `Maybe ETag` (alias for `Text`) | (Optional) ETag of object should match |+| `gooIfNoneMatch` | `Maybe ETag` (alias for `Text`) | (Optional) ETag of object shouldn't match |+| `gooIfUnmodifiedSince` | `Maybe UTCTime` | (Optional) Time since object wasn't modified |+| `gooIfModifiedSince` | `Maybe UTCTime` | (Optional) Time since object was modified | ``` haskell @@ -561,7 +562,7 @@ import Prelude -- | The following example uses MinIO play server at--- https://play.min.io:9000. The endpoint and associated+-- https://play.min.io. The endpoint and associated -- credentials are provided via the libary constant, -- -- > minioPlayCI :: ConnectInfo@@ -592,11 +593,11 @@ In the expression `fPutObject bucketName objectName inputFile` the parameters are: -|Param |Type |Description |-|:---|:---| :---|-| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |-| `objectName` | _Object_ (alias for `Text`) | Name of the object |-| `inputFile` | _FilePath_ | Path to the file to be uploaded |+| Param | Type | Description |+|:-------------|:----------------------------|:--------------------------------|+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |+| `objectName` | _Object_ (alias for `Text`) | Name of the object |+| `inputFile` | _FilePath_ | Path to the file to be uploaded | __Example__ @@ -629,28 +630,28 @@ In the expression `copyObject dstInfo srcInfo` the parameters are: -|Param |Type |Description |-|:---|:---| :---|+| Param | Type | Description |+|:----------|:------------------|:----------------------------------------------------------| | `dstInfo` | _DestinationInfo_ | A value representing properties of the destination object |-| `srcInfo` | _SourceInfo_ | A value representing properties of the source object |+| `srcInfo` | _SourceInfo_ | A value representing properties of the source object | __SourceInfo record type__ -|Field |Type |Description |-|:---|:---| :---|-| `srcBucket` | `Bucket` | Name of source bucket |-| `srcObject` | `Object` | Name of source object |-| `srcRange` | `Maybe (Int64, Int64)` | (Optional) Represents the byte range of source object. (0, 9) represents first ten bytes of source object|-| `srcIfMatch` | `Maybe Text` | (Optional) ETag source object should match |-| `srcIfNoneMatch` | `Maybe Text` | (Optional) ETag source object shouldn't match |-| `srcIfUnmodifiedSince` | `Maybe UTCTime` | (Optional) Time since source object wasn't modified |-| `srcIfModifiedSince` | `Maybe UTCTime` | (Optional) Time since source object was modified |+| Field | Type | Description |+|:-----------------------|:-----------------------|:----------------------------------------------------------------------------------------------------------|+| `srcBucket` | `Bucket` | Name of source bucket |+| `srcObject` | `Object` | Name of source object |+| `srcRange` | `Maybe (Int64, Int64)` | (Optional) Represents the byte range of source object. (0, 9) represents first ten bytes of source object |+| `srcIfMatch` | `Maybe Text` | (Optional) ETag source object should match |+| `srcIfNoneMatch` | `Maybe Text` | (Optional) ETag source object shouldn't match |+| `srcIfUnmodifiedSince` | `Maybe UTCTime` | (Optional) Time since source object wasn't modified |+| `srcIfModifiedSince` | `Maybe UTCTime` | (Optional) Time since source object was modified | __Destination record type__ -|Field |Type |Description |-|:---|:---| :---|+| Field | Type | Description |+|:------------|:---------|:-----------------------------------------------------| | `dstBucket` | `Bucket` | Name of destination bucket in server-side copyObject | | `dstObject` | `Object` | Name of destination object in server-side copyObject | @@ -684,10 +685,10 @@ In the expression `removeObject bucketName objectName` the parameters are: -|Param |Type |Description |-|:---|:---| :---|-| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |-| `objectName` | _Object_ (alias for `Text`) | Name of the object |+| Param | Type | Description |+|:-------------|:----------------------------|:-------------------|+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |+| `objectName` | _Object_ (alias for `Text`) | Name of the object | __Example__ @@ -718,10 +719,10 @@ In the expression `removeIncompleteUpload bucketName objectName` the parameters are: -|Param |Type |Description |-|:---|:---| :---|-| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |-| `objectName` | _Object_ (alias for `Text`) | Name of the object |+| Param | Type | Description |+|:-------------|:----------------------------|:-------------------|+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |+| `objectName` | _Object_ (alias for `Text`) | Name of the object | __Example__ @@ -752,11 +753,11 @@ In the expression `selectObjectContent bucketName objectName selReq` the parameters are: -|Param |Type |Description |-|:---|:---| :---|-| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |-| `objectName` | _Object_ (alias for `Text`) | Name of the object |-| `selReq` | _SelectRequest_ | Select request parameters |+| Param | Type | Description |+|:-------------|:----------------------------|:--------------------------|+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |+| `objectName` | _Object_ (alias for `Text`) | Name of the object |+| `selReq` | _SelectRequest_ | Select request parameters | __SelectRequest record__ @@ -804,9 +805,9 @@ In the expression `bucketExists bucketName` the parameters are: -|Param |Type |Description |-|:---|:---| :---|-| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |+| Param | Type | Description |+|:-------------|:----------------------------|:-------------------|+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket | ## 4. Presigned operations@@ -828,22 +829,22 @@ In the expression `presignedGetObjectUrl bucketName objectName expiry queryParams headers` the parameters are: -|Param |Type |Description |-|:---|:---| :---|-| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |-| `objectName` | _Object_ (alias for `Text`) | Name of the object |-| `expiry` | _UrlExpiry_ (alias for `Int`) | Url expiry time in seconds |-| `queryParams` | _Query_ (from package `http-types:Network.HTTP.Types`) | Query parameters to add to the URL |-| `headers` | _RequestHeaders_ (from package `http-types:Network.HTTP.Types` | Request headers that would be used with the URL |+| Param | Type | Description |+|:--------------|:---------------------------------------------------------------|:------------------------------------------------|+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |+| `objectName` | _Object_ (alias for `Text`) | Name of the object |+| `expiry` | _UrlExpiry_ (alias for `Int`) | Url expiry time in seconds |+| `queryParams` | _Query_ (from package `http-types:Network.HTTP.Types`) | Query parameters to add to the URL |+| `headers` | _RequestHeaders_ (from package `http-types:Network.HTTP.Types` | Request headers that would be used with the URL | __Return Value__ Returns the generated URL - it will include authentication information. -|Return type |Description |-|:---|:---|-| _ByteString_ | Generated presigned URL |+| Return type | Description |+|:-------------|:------------------------|+| _ByteString_ | Generated presigned URL | __Example__ @@ -886,21 +887,21 @@ In the expression `presignedPutObjectUrl bucketName objectName expiry headers` the parameters are: -|Param |Type |Description |-|:---|:---| :---|-| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |-| `objectName` | _Object_ (alias for `Text`) | Name of the object |-| `expiry` | _UrlExpiry_ (alias for `Int`) | Url expiry time in seconds |-| `headers` | _RequestHeaders_ (from package `http-types:Network.HTTP.Types` | Request headers that would be used with the URL |+| Param | Type | Description |+|:-------------|:---------------------------------------------------------------|:------------------------------------------------|+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |+| `objectName` | _Object_ (alias for `Text`) | Name of the object |+| `expiry` | _UrlExpiry_ (alias for `Int`) | Url expiry time in seconds |+| `headers` | _RequestHeaders_ (from package `http-types:Network.HTTP.Types` | Request headers that would be used with the URL | __Return Value__ Returns the generated URL - it will include authentication information. -|Return type |Description |-|:---|:---|-| _ByteString_ | Generated presigned URL |+| Return type | Description |+|:-------------|:------------------------|+| _ByteString_ | Generated presigned URL | __Example__ @@ -928,7 +929,7 @@ ``` <a name="presignedPostPolicy"></a>-### presignedPostPolicy :: PostPolicy -> Minio (ByteString, Map.Map Text ByteString)+### presignedPostPolicy :: PostPolicy -> Minio (ByteString, HashMap Text ByteString) Generate a presigned URL and POST policy to upload files via a POST request. This is intended for browser uploads and generates form data@@ -940,10 +941,10 @@ In the expression `newPostPolicy expirationTime conditions` the parameters are: -|Param | Type| Description |-|:---|:---|:---|-| `expirationTime` | _UTCTime_ (from package `time:Data.Time.UTCTime`) | The expiration time for the policy |-| `conditions` | _[PostPolicyConditions]_ | List of conditions to be added to the policy |+| Param | Type | Description |+|:-----------------|:--------------------------------------------------|:---------------------------------------------|+| `expirationTime` | _UTCTime_ (from package `time:Data.Time.UTCTime`) | The expiration time for the policy |+| `conditions` | _[PostPolicyConditions]_ | List of conditions to be added to the policy | The policy conditions are created using various helper functions - please refer to the Haddocks for details.@@ -965,7 +966,7 @@ import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as Char8-import qualified Data.Map.Strict as Map+import qualified Data.HashMap.Strict as H import qualified Data.Text.Encoding as Enc import qualified Data.Time as Time @@ -1005,7 +1006,7 @@ let formFn (k, v) = B.concat ["-F ", Enc.encodeUtf8 k, "=", "'", v, "'"]- formOptions = B.intercalate " " $ map formFn $ Map.toList formData+ formOptions = B.intercalate " " $ map formFn $ H.toList formData return $ B.intercalate " " $
examples/BucketExists.hs view
@@ -24,7 +24,7 @@ import Prelude -- | The following example uses minio's play server at--- https://play.min.io:9000. The endpoint and associated+-- https://play.min.io. The endpoint and associated -- credentials are provided via the libary constant, -- -- > minioPlayCI :: ConnectInfo
examples/CopyObject.hs view
@@ -24,7 +24,7 @@ import Prelude -- | The following example uses minio's play server at--- https://play.min.io:9000. The endpoint and associated+-- https://play.min.io. The endpoint and associated -- credentials are provided via the libary constant, -- -- > minioPlayCI :: ConnectInfo
examples/FileUploader.hs view
@@ -31,7 +31,7 @@ import Prelude -- | The following example uses minio's play server at--- https://play.min.io:9000. The endpoint and associated+-- https://play.min.io. The endpoint and associated -- credentials are provided via the libary constant, -- -- > minioPlayCI :: ConnectInfo
examples/GetObject.hs view
@@ -26,7 +26,7 @@ import Prelude -- | The following example uses minio's play server at--- https://play.min.io:9000. The endpoint and associated+-- https://play.min.io. The endpoint and associated -- credentials are provided via the libary constant, -- -- > minioPlayCI :: ConnectInfo
examples/HeadObject.hs view
@@ -24,7 +24,7 @@ import Prelude -- | The following example uses minio's play server at--- https://play.min.io:9000. The endpoint and associated+-- https://play.min.io. The endpoint and associated -- credentials are provided via the libary constant, -- -- > minioPlayCI :: ConnectInfo
examples/ListBuckets.hs view
@@ -24,7 +24,7 @@ import Prelude -- | The following example uses minio's play server at--- https://play.min.io:9000. The endpoint and associated+-- https://play.min.io. The endpoint and associated -- credentials are provided via the libary constant, -- -- > minioPlayCI :: ConnectInfo
examples/ListIncompleteUploads.hs view
@@ -24,7 +24,7 @@ import Prelude -- | The following example uses minio's play server at--- https://play.min.io:9000. The endpoint and associated+-- https://play.min.io. The endpoint and associated -- credentials are provided via the libary constant, -- -- > minioPlayCI :: ConnectInfo
examples/ListObjects.hs view
@@ -25,7 +25,7 @@ -- | The following example uses minio's play server at--- https://play.min.io:9000. The endpoint and associated+-- https://play.min.io. The endpoint and associated -- credentials are provided via the libary constant, -- -- > minioPlayCI :: ConnectInfo
examples/MakeBucket.hs view
@@ -24,7 +24,7 @@ import Prelude -- | The following example uses minio's play server at--- https://play.min.io:9000. The endpoint and associated+-- https://play.min.io. The endpoint and associated -- credentials are provided via the libary constant, -- -- > minioPlayCI :: ConnectInfo
examples/PresignedGetObject.hs view
@@ -27,7 +27,7 @@ import qualified Data.Text.Encoding as E -- | The following example uses minio's play server at--- https://play.min.io:9000. The endpoint and associated+-- https://play.min.io. The endpoint and associated -- credentials are provided via the libary constant, -- -- > minioPlayCI :: ConnectInfo
examples/PresignedPostPolicy.hs view
@@ -22,12 +22,12 @@ import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as Char8-import qualified Data.Map.Strict as Map+import qualified Data.HashMap.Strict as H import qualified Data.Text.Encoding as Enc import qualified Data.Time as Time -- | The following example uses minio's play server at--- https://play.min.io:9000. The endpoint and associated+-- https://play.min.io. The endpoint and associated -- credentials are provided via the libary constant, -- -- > minioPlayCI :: ConnectInfo@@ -69,7 +69,7 @@ let formFn (k, v) = B.concat ["-F ", Enc.encodeUtf8 k, "=", "'", v, "'"]- formOptions = B.intercalate " " $ map formFn $ Map.toList formData+ formOptions = B.intercalate " " $ map formFn $ H.toList formData return $ B.intercalate " " $
examples/PresignedPutObject.hs view
@@ -24,7 +24,7 @@ import Data.CaseInsensitive (original) -- | The following example uses minio's play server at--- https://play.min.io:9000. The endpoint and associated+-- https://play.min.io. The endpoint and associated -- credentials are provided via the libary constant, -- -- > minioPlayCI :: ConnectInfo
examples/PutObject.hs view
@@ -25,7 +25,7 @@ import Prelude -- | The following example uses minio's play server at--- https://play.min.io:9000. The endpoint and associated+-- https://play.min.io. The endpoint and associated -- credentials are provided via the libary constant, -- -- > minioPlayCI :: ConnectInfo
examples/RemoveBucket.hs view
@@ -24,7 +24,7 @@ import Prelude -- | The following example uses minio's play server at--- https://play.min.io:9000. The endpoint and associated+-- https://play.min.io. The endpoint and associated -- credentials are provided via the libary constant, -- -- > minioPlayCI :: ConnectInfo
examples/RemoveIncompleteUpload.hs view
@@ -23,7 +23,7 @@ import Prelude -- | The following example uses minio's play server at--- https://play.min.io:9000. The endpoint and associated+-- https://play.min.io. The endpoint and associated -- credentials are provided via the libary constant, -- -- > minioPlayCI :: ConnectInfo
minio-hs.cabal view
@@ -1,5 +1,5 @@ name: minio-hs-version: 1.4.0+version: 1.5.0 synopsis: A MinIO Haskell Library for Amazon S3 compatible cloud storage. description: The MinIO Haskell client library provides simple APIs to@@ -57,7 +57,6 @@ , conduit >= 1.3 , conduit-extra >= 1.3 , connection- , containers >= 0.5 , cryptonite >= 0.25 , cryptonite-conduit >= 0.2 , digest >= 0.0.1@@ -153,7 +152,6 @@ , conduit , conduit-extra , connection- , containers , cryptonite , cryptonite-conduit , digest@@ -200,7 +198,6 @@ , conduit , conduit-extra , connection- , containers , cryptonite , cryptonite-conduit , digest
src/Network/Minio.hs view
@@ -90,6 +90,7 @@ , oiModTime , oiETag , oiSize+ , oiUserMetadata , oiMetadata -- ** Listing incomplete uploads@@ -146,6 +147,9 @@ , gooIfModifiedSince , gooIfUnmodifiedSince , gooSSECKey+ , GetObjectResponse+ , gorObjectInfo+ , gorObjectStream -- ** Server-side object copying , copyObject@@ -241,7 +245,7 @@ fGetObject :: Bucket -> Object -> FilePath -> GetObjectOptions -> Minio () fGetObject bucket object fp opts = do src <- getObject bucket object opts- C.connect src $ CB.sinkFileCautious fp+ C.connect (gorObjectStream src) $ CB.sinkFileCautious fp -- | Upload the given file to the given object. fPutObject :: Bucket -> Object -> FilePath@@ -271,11 +275,11 @@ removeObject :: Bucket -> Object -> Minio () removeObject = deleteObject --- | Get an object from the object store as a resumable source (conduit).+-- | Get an object from the object store. getObject :: Bucket -> Object -> GetObjectOptions- -> Minio (C.ConduitM () ByteString Minio ())-getObject bucket object opts = snd <$> getObject' bucket object []- (gooToHeaders opts)+ -> Minio GetObjectResponse+getObject bucket object opts =+ getObject' bucket object [] $ gooToHeaders opts -- | Get an object's metadata from the object store. It accepts the -- same options as GetObject.
src/Network/Minio/API.hs view
@@ -34,7 +34,7 @@ import qualified Data.ByteString as B import qualified Data.Char as C import qualified Data.Conduit as C-import qualified Data.Map as Map+import qualified Data.HashMap.Strict as H import qualified Data.Text as T import qualified Data.Time.Clock as Time import Network.HTTP.Conduit (Response)@@ -95,7 +95,7 @@ if "amazonaws.com" `T.isSuffixOf` connectHost ci then maybe (throwIO $ MErrVRegionNotSupported r)- return (Map.lookup r awsRegionMap)+ return (H.lookup r awsRegionMap) else return $ connectHost ci buildRequest :: S3ReqInfo -> Minio NC.Request
src/Network/Minio/Data.hs view
@@ -32,7 +32,6 @@ import Data.CaseInsensitive (mk) import qualified Data.HashMap.Strict as H import qualified Data.Ini as Ini-import qualified Data.Map as Map import Data.String (IsString (..)) import qualified Data.Text as T import qualified Data.Text.Encoding as TE@@ -74,8 +73,8 @@ -- type should have a IsString instance to infer the appropriate -- constant. -- | awsRegionMap - library constant-awsRegionMap :: Map.Map Text Text-awsRegionMap = Map.fromList [+awsRegionMap :: H.HashMap Text Text+awsRegionMap = H.fromList [ ("us-east-1", "s3.amazonaws.com") , ("us-east-2", "s3-us-east-2.amazonaws.com") , ("us-west-1", "s3-us-west-1.amazonaws.com")@@ -96,7 +95,7 @@ -- enable the @OverloadedStrings@ language extension and use the -- `IsString` instance to provide a URL, for example: ----- > let c :: ConnectInfo = "https://play.min.io:9000"+-- > let c :: ConnectInfo = "https://play.min.io" data ConnectInfo = ConnectInfo { connectHost :: Text , connectPort :: Int@@ -231,13 +230,13 @@ awsCI = "https://s3.amazonaws.com" --- | <https://play.min.io:9000 MinIO Play Server>+-- | <https://play.min.io MinIO Play Server> -- ConnectInfo. Credentials are already filled in. minioPlayCI :: ConnectInfo minioPlayCI = let playCreds = Credentials "Q3AM3UQ867SPQQA43P2F" "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG" in setCreds playCreds $ setRegion "us-east-1"- "https://play.min.io:9000"+ "https://play.min.io" -- | -- Represents a bucket in the object store@@ -336,14 +335,22 @@ defaultPutObjectOptions :: PutObjectOptions defaultPutObjectOptions = PutObjectOptions Nothing Nothing Nothing Nothing Nothing Nothing [] Nothing Nothing +-- | If the given header name has the @X-Amz-Meta-@ prefix, it is+-- stripped and a Just is returned.+userMetadataHeaderNameMaybe :: Text -> Maybe Text+userMetadataHeaderNameMaybe k =+ let prefix = T.toCaseFold "X-Amz-Meta-"+ n = T.length prefix+ in if T.toCaseFold (T.take n k) == prefix+ then Just (T.drop n k)+ else Nothing+ addXAmzMetaPrefix :: Text -> Text-addXAmzMetaPrefix s = do- if (T.isPrefixOf "x-amz-meta-" s)- then s- else T.concat ["x-amz-meta-", s]+addXAmzMetaPrefix s | isJust (userMetadataHeaderNameMaybe s) = s+ | otherwise = "X-Amz-Meta-" <> s mkHeaderFromMetadata :: [(Text, Text)] -> [HT.Header]-mkHeaderFromMetadata = map (\(x, y) -> (mk $ encodeUtf8 $ addXAmzMetaPrefix $ T.toLower x, encodeUtf8 y))+mkHeaderFromMetadata = map (\(x, y) -> (mk $ encodeUtf8 $ addXAmzMetaPrefix $ x, encodeUtf8 y)) pooToHeaders :: PutObjectOptions -> [HT.Header] pooToHeaders poo = userMetadata@@ -436,12 +443,19 @@ -- | Represents information about an object. data ObjectInfo = ObjectInfo- { oiObject :: Object -- ^ Object key- , oiModTime :: UTCTime -- ^ Mdification time of the object- , oiETag :: ETag -- ^ ETag of the object- , oiSize :: Int64 -- ^ Size of the object in bytes- , oiMetadata :: Map.Map Text Text -- ^ A map of the metadata- -- key-value pairs+ { oiObject :: Object -- ^ Object key+ , oiModTime :: UTCTime -- ^ Modification time of the object+ , oiETag :: ETag -- ^ ETag of the object+ , oiSize :: Int64 -- ^ Size of the object in bytes+ , oiUserMetadata :: H.HashMap Text Text -- ^ A map of user-metadata+ -- pairs stored with an+ -- object (keys will not+ -- have the @X-Amz-Meta-@+ -- prefix).+ , oiMetadata :: H.HashMap Text Text -- ^ A map of metadata+ -- key-value pairs (not+ -- including the+ -- user-metadata pairs) } deriving (Show, Eq) -- | Represents source object in server-side copy object@@ -523,6 +537,14 @@ rangeHdr = maybe [] (\a -> [(hRange, HT.renderByteRanges [a])]) $ gooRange goo +-- | Data type returned by 'getObject' representing the object being+-- retrieved. Use the @gor*@ functions to access its contents.+data GetObjectResponse = GetObjectResponse {+ -- | ObjectInfo of the object being retrieved.+ gorObjectInfo :: ObjectInfo+ -- | A conduit of the bytes of the object.+ , gorObjectStream :: C.ConduitM () ByteString Minio ()+ } -- | A data-type for events that can occur in the object storage -- server. Reference:@@ -928,7 +950,7 @@ -- seconds. The maximum duration that can be specified is 7 days. type UrlExpiry = Int -type RegionMap = Map.Map Bucket Region+type RegionMap = H.HashMap Bucket Region -- | The Minio Monad - all computations accessing object storage -- happens in it.@@ -991,7 +1013,7 @@ -- `MinioConn`. mkMinioConn :: ConnectInfo -> NC.Manager -> IO MinioConn mkMinioConn ci mgr = do- rMapMVar <- M.newMVar Map.empty+ rMapMVar <- M.newMVar H.empty return $ MinioConn ci mgr rMapMVar -- | Run the Minio action and return the result or an error.
src/Network/Minio/PresignedOperations.hs view
@@ -39,7 +39,7 @@ import Data.Aeson ((.=)) import qualified Data.Aeson as Json import Data.ByteString.Builder (byteString, toLazyByteString)-import qualified Data.Map.Strict as Map+import qualified Data.HashMap.Strict as H import qualified Data.Text as T import qualified Data.Time as Time import qualified Network.HTTP.Conduit as NC@@ -252,7 +252,7 @@ -- browser. On success, this function returns a URL and POST -- form-data. presignedPostPolicy :: PostPolicy- -> Minio (ByteString, Map.Map Text ByteString)+ -> Minio (ByteString, H.HashMap Text ByteString) presignedPostPolicy p = do ci <- asks mcConnInfo signTime <- liftIO $ Time.getCurrentTime@@ -277,12 +277,12 @@ mkPair (PPCStartsWith k v) = Just (k, v) mkPair (PPCEquals k v) = Just (k, v) mkPair _ = Nothing- formFromPolicy = Map.map toS $ Map.fromList $ catMaybes $+ formFromPolicy = H.map toS $ H.fromList $ catMaybes $ mkPair <$> conditions ppWithCreds- formData = formFromPolicy `Map.union` signData+ formData = formFromPolicy `H.union` signData -- compute POST upload URL- bucket = Map.findWithDefault "" "bucket" formData+ bucket = H.lookupDefault "" "bucket" formData scheme = byteString $ bool "http://" "https://" $ connectIsSecure ci region = connectRegion ci
src/Network/Minio/S3API.hs view
@@ -91,7 +91,6 @@ , removeAllBucketNotification ) where -import qualified Conduit as C import qualified Data.ByteString as BS import qualified Data.Text as T import qualified Network.HTTP.Conduit as NC@@ -118,19 +117,37 @@ } parseListBuckets $ NC.responseBody resp --- | GET an object from the service and return the response headers--- and a conduit source for the object content+-- Parse headers from getObject and headObject calls.+parseGetObjectHeaders :: Object -> [HT.Header] -> Maybe ObjectInfo+parseGetObjectHeaders object headers =+ let metadataPairs = getMetadata headers+ userMetadata = getUserMetadataMap metadataPairs+ metadata = getNonUserMetadataMap metadataPairs+ in ObjectInfo <$> Just object+ <*> getLastModifiedHeader headers+ <*> getETagHeader headers+ <*> getContentLength headers+ <*> Just userMetadata+ <*> Just metadata++-- | GET an object from the service and return parsed ObjectInfo and a+-- conduit source for the object content getObject' :: Bucket -> Object -> HT.Query -> [HT.Header]- -> Minio ([HT.Header], C.ConduitM () ByteString Minio ())+ -> Minio GetObjectResponse getObject' bucket object queryParams headers = do- resp <- mkStreamRequest reqInfo- return (NC.responseHeaders resp, NC.responseBody resp)+ resp <- mkStreamRequest reqInfo+ let objInfoMaybe = parseGetObjectHeaders object $ NC.responseHeaders resp+ objInfo <- maybe (throwIO MErrVInvalidObjectInfoResponse) return+ objInfoMaybe+ return $ GetObjectResponse { gorObjectInfo = objInfo+ , gorObjectStream = NC.responseBody resp+ } where reqInfo = defaultS3ReqInfo { riBucket = Just bucket- , riObject = Just object- , riQueryParams = queryParams- , riHeaders = headers- }+ , riObject = Just object+ , riQueryParams = queryParams+ , riHeaders = headers+ } -- | Creates a bucket via a PUT bucket call. putBucket :: Bucket -> Region -> Minio ()@@ -412,20 +429,13 @@ headObject :: Bucket -> Object -> [HT.Header] -> Minio ObjectInfo headObject bucket object reqHeaders = do resp <- executeRequest $ defaultS3ReqInfo { riMethod = HT.methodHead- , riBucket = Just bucket- , riObject = Just object- , riHeaders = reqHeaders- }-- let- headers = NC.responseHeaders resp- modTime = getLastModifiedHeader headers- etag = getETagHeader headers- size = getContentLength headers- metadata = getMetadataMap headers+ , riBucket = Just bucket+ , riObject = Just object+ , riHeaders = reqHeaders+ } maybe (throwIO MErrVInvalidObjectInfoResponse) return $- ObjectInfo <$> Just object <*> modTime <*> etag <*> size <*> Just metadata+ parseGetObjectHeaders object $ NC.responseHeaders resp -- | Query the object store if a given bucket exists.
src/Network/Minio/Sign/V4.hs view
@@ -22,8 +22,8 @@ import qualified Data.ByteString.Char8 as B8 import Data.CaseInsensitive (mk) import qualified Data.CaseInsensitive as CI-import qualified Data.Map.Strict as Map-import qualified Data.Set as Set+import qualified Data.HashMap.Strict as Map+import qualified Data.HashSet as Set import qualified Data.Time as Time import qualified Network.HTTP.Conduit as NC import Network.HTTP.Types (Header, parseQuery)@@ -39,7 +39,7 @@ -- these headers are not included in the string to sign when signing a -- request-ignoredHeaders :: Set ByteString+ignoredHeaders :: Set.HashSet ByteString ignoredHeaders = Set.fromList $ map CI.foldedCase [ H.hAuthorization , H.hContentType@@ -178,7 +178,7 @@ getHeadersToSign :: [Header] -> [(ByteString, ByteString)] getHeadersToSign !h =- filter (flip Set.notMember ignoredHeaders . fst) $+ filter ((\hdr -> not $ Set.member hdr ignoredHeaders) . fst) $ map (\(x, y) -> (CI.foldedCase x, stripBS y)) h mkCanonicalRequest :: Bool -> SignParams -> NC.Request -> [(ByteString, ByteString)]@@ -234,7 +234,7 @@ -- and ConnInfo and returns form-data for the POST upload containing -- just the signature and the encoded post-policy. signV4PostPolicy :: ByteString -> SignParams- -> Map.Map Text ByteString+ -> Map.HashMap Text ByteString signV4PostPolicy !postPolicyJSON !sp = let stringToSign = Base64.encode postPolicyJSON
src/Network/Minio/Utils.hs view
@@ -23,8 +23,8 @@ import qualified Data.ByteString.Lazy as LB import Data.CaseInsensitive (mk, original) import qualified Data.Conduit.Binary as CB+import qualified Data.HashMap.Strict as H import qualified Data.List as List-import qualified Data.Map as Map import qualified Data.Text as T import Data.Text.Encoding.Error (lenientDecode) import Data.Text.Read (decimal)@@ -103,11 +103,26 @@ getETagHeader hs = decodeUtf8Lenient <$> lookupHeader Hdr.hETag hs getMetadata :: [HT.Header] -> [(Text, Text)]-getMetadata = map ((\(x, y) -> (decodeUtf8Lenient $ original x, decodeUtf8Lenient $ stripBS y)))+getMetadata =+ map ((\(x, y) -> (decodeUtf8Lenient $ original x, decodeUtf8Lenient $ stripBS y))) -getMetadataMap :: [HT.Header] -> Map Text Text-getMetadataMap hs = Map.fromList (getMetadata hs)+toMaybeMetadataHeader :: (Text, Text) -> Maybe (Text, Text)+toMaybeMetadataHeader (k, v) =+ (, v) <$> userMetadataHeaderNameMaybe k +getNonUserMetadataMap :: [(Text, Text)] -> H.HashMap Text Text+getNonUserMetadataMap = H.fromList+ . filter ( isNothing+ . userMetadataHeaderNameMaybe+ . fst+ )++-- | This function collects all headers starting with `x-amz-meta-`+-- and strips off this prefix, and returns a map.+getUserMetadataMap :: [(Text, Text)] -> H.HashMap Text Text+getUserMetadataMap = H.fromList+ . mapMaybe toMaybeMetadataHeader+ getLastModifiedHeader :: [HT.Header] -> Maybe UTCTime getLastModifiedHeader hs = do modTimebs <- decodeUtf8Lenient <$> lookupHeader Hdr.hLastModified hs@@ -245,14 +260,14 @@ lookupRegionCache b = do rMVar <- asks mcRegionMap rMap <- UM.readMVar rMVar- return $ Map.lookup b rMap+ return $ H.lookup b rMap addToRegionCache :: Bucket -> Region -> Minio () addToRegionCache b region = do rMVar <- asks mcRegionMap- UM.modifyMVar_ rMVar $ return . Map.insert b region+ UM.modifyMVar_ rMVar $ return . H.insert b region deleteFromRegionCache :: Bucket -> Minio () deleteFromRegionCache b = do rMVar <- asks mcRegionMap- UM.modifyMVar_ rMVar $ return . Map.delete b+ UM.modifyMVar_ rMVar $ return . H.delete b
src/Network/Minio/XmlParser.hs view
@@ -30,8 +30,8 @@ ) where import qualified Data.ByteString.Lazy as LB-import Data.List (zip3, zip4, zip5)-import qualified Data.Map as Map+import qualified Data.HashMap.Strict as H+import Data.List (zip3, zip4, zip6) import qualified Data.Text as T import Data.Text.Read (decimal) import Data.Time@@ -52,8 +52,8 @@ uncurry4 :: (a -> b -> c -> d -> e) -> (a, b, c, d) -> e uncurry4 f (a, b, c, d) = f a b c d -uncurry5 :: (a -> b -> c -> d -> e -> f) -> (a, b, c, d, e) -> f-uncurry5 f (a, b, c, d, e) = f a b c d e+uncurry6 :: (a -> b -> c -> d -> e -> f -> g) -> (a, b, c, d, e, f) -> g+uncurry6 f (a, b, c, d, e, g) = f a b c d e g -- | Parse time strings from XML parseS3XMLTime :: (MonadIO m) => Text -> m UTCTime@@ -149,7 +149,8 @@ sizes <- parseDecimals sizeStr let- objects = map (uncurry5 ObjectInfo) $ zip5 keys modTimes etags sizes (repeat Map.empty)+ objects = map (uncurry6 ObjectInfo) $+ zip6 keys modTimes etags sizes (repeat H.empty) (repeat H.empty) return $ ListObjectsV1Result hasMore nextMarker objects prefixes @@ -178,7 +179,8 @@ sizes <- parseDecimals sizeStr let- objects = map (uncurry5 ObjectInfo) $ zip5 keys modTimes etags sizes (repeat Map.empty)+ objects = map (uncurry6 ObjectInfo) $+ zip6 keys modTimes etags sizes (repeat H.empty) (repeat H.empty) return $ ListObjectsResult hasMore nextToken objects prefixes
test/LiveServer.hs view
@@ -21,13 +21,13 @@ import Test.Tasty.QuickCheck as QC import Conduit (replicateC)+import qualified Conduit as C import qualified Control.Monad.Trans.Resource as R import qualified Data.ByteString as BS import Data.Conduit (yield)-import qualified Data.Conduit as C import qualified Data.Conduit.Binary as CB import Data.Conduit.Combinators (sinkList)-import qualified Data.Map.Strict as Map+import qualified Data.HashMap.Strict as H import qualified Data.Text as T import Data.Time (fromGregorian) import qualified Data.Time as Time@@ -42,6 +42,7 @@ import Network.Minio import Network.Minio.Data+import Network.Minio.Data.Crypto import Network.Minio.PutObject import Network.Minio.S3API import Network.Minio.Utils@@ -108,6 +109,8 @@ , putObjectContentTypeTest , putObjectContentLanguageTest , putObjectStorageClassTest+ , putObjectUserMetadataTest+ , getObjectTest , copyObjectTests , presignedUrlFunTest , presignedPostPolicyFunTest@@ -513,8 +516,8 @@ "presigned HEAD failed (presignedHeadObjectUrl)" -- check that header info is accurate- let h = Map.fromList $ NC.responseHeaders headResp- cLen = Map.findWithDefault "0" HT.hContentLength h+ let h = H.fromList $ NC.responseHeaders headResp+ cLen = H.lookupDefault "0" HT.hContentLength h liftIO $ (cLen == show size2) @? "Head req returned bad content length" step "GET object presigned URL - presignedGetObjectUrl"@@ -580,7 +583,7 @@ postForm url formData inputFile = do req <- NC.parseRequest $ toS url let parts = map (\(x, y) -> Form.partBS x y) $- Map.toList formData+ H.toList formData parts' = parts ++ [Form.partFile "file" inputFile] req' <- Form.formDataBody parts' req mgr <- NC.newManager NC.tlsManagerSettings@@ -698,7 +701,7 @@ let m = oiMetadata oi step "Validate content-type"- liftIO $ assertEqual "Content-Type did not match" (Just "application/javascript") (Map.lookup "Content-Type" m)+ liftIO $ assertEqual "Content-Type did not match" (Just "application/javascript") (H.lookup "Content-Type" m) step "upload object with content-encoding set to identity" fPutObject bucket object inputFile defaultPutObjectOptions {@@ -710,7 +713,7 @@ step "Validate content-encoding" liftIO $ assertEqual "Content-Encoding did not match" (Just "identity")- (Map.lookup "Content-Encoding" m')+ (H.lookup "Content-Encoding" m') step "Cleanup actions" @@ -735,11 +738,74 @@ step "Validate content-language" liftIO $ assertEqual "content-language did not match" (Just "en-US")- (Map.lookup "Content-Language" m)+ (H.lookup "Content-Language" m) step "Cleanup actions" removeObject bucket object +putObjectUserMetadataTest :: TestTree+putObjectUserMetadataTest = funTestWithBucket "putObject user-metadata test" $+ \step bucket -> do+ step "putObject user-metadata test"+ let object = "object-with-metadata"+ size1 = 100 :: Int64++ step "create server object with usermetdata"+ inputFile <- mkRandFile size1++ fPutObject bucket object inputFile defaultPutObjectOptions {+ pooUserMetadata = [ ("x-Amz-meta-mykey1", "myval1")+ , ("mykey2", "myval2")+ ]+ }++ step "Validate user-metadata"+ -- retrieve obj info to check+ oi <- headObject bucket object []+ let m = oiUserMetadata oi+ -- need to do a case-insensitive comparison+ sortedMeta = sort $ map (\(k, v) -> (T.toLower k, T.toLower v)) $+ H.toList m+ ref = sort [("mykey1", "myval1"), ("mykey2", "myval2")]++ liftIO $ (sortedMeta == ref) @? "Metadata mismatch!"++ step "Cleanup actions"+ removeObject bucket object++getObjectTest :: TestTree+getObjectTest = funTestWithBucket "getObject test" $+ \step bucket -> do+ step "putObject with some metadata"+ let object = "object-with-metadata"+ size1 = 100 :: Int64++ inputFile <- mkRandFile size1+ fPutObject bucket object inputFile defaultPutObjectOptions {+ pooUserMetadata = [ ("x-Amz-meta-mykey1", "myval1")+ , ("mykey2", "myval2")+ ]+ }++ step "get the object - check the metadata matches"+ -- retrieve obj info to check+ gor <- getObject bucket object defaultGetObjectOptions+ let m = oiUserMetadata $ gorObjectInfo gor+ -- need to do a case-insensitive comparison+ sortedMeta = sort $ map (\(k, v) -> (T.toLower k, T.toLower v)) $+ H.toList m+ ref = sort [("mykey1", "myval1"), ("mykey2", "myval2")]++ liftIO $ (sortedMeta == ref) @? "Metadata mismatch!"++ step "get the object content"+ getObjectHash <- hashSHA256FromSource $ gorObjectStream gor+ inputHash <- hashSHA256FromSource $ C.sourceFile inputFile+ liftIO $ (getObjectHash == inputHash) @? "Input file and output file mismatched!"++ step "Cleanup actions"+ removeObject bucket object+ putObjectStorageClassTest :: TestTree putObjectStorageClassTest = funTestWithBucket "putObject storageClass tests" $ \step bucket -> do@@ -771,7 +837,7 @@ step "Validate x-amz-storage-class rrs" liftIO $ assertEqual "storageClass did not match" (Just "REDUCED_REDUNDANCY")- (Map.lookup "X-Amz-Storage-Class" m')+ (H.lookup "X-Amz-Storage-Class" m') fpE <- try $ fPutObject bucket object'' inputFile'' defaultPutObjectOptions { pooStorageClass = Just "INVALID_STORAGE_CLASS"
test/Network/Minio/XmlParser/Test.hs view
@@ -19,7 +19,7 @@ ( xmlParserTests ) where -import qualified Data.Map as Map+import qualified Data.HashMap.Strict as H import Data.Time (fromGregorian) import Test.Tasty import Test.Tasty.HUnit@@ -128,7 +128,7 @@ \</ListBucketResult>" expectedListResult = ListObjectsResult True (Just "opaque") [object1] []- object1 = ObjectInfo "my-image.jpg" modifiedTime1 "\"fba9dede5f27731c9771645a39863328\"" 434234 Map.empty+ object1 = ObjectInfo "my-image.jpg" modifiedTime1 "\"fba9dede5f27731c9771645a39863328\"" 434234 H.empty H.empty modifiedTime1 = flip UTCTime 64230 $ fromGregorian 2009 10 12 parsedListObjectsResult <- tryValidationErr $ runTestNS $ parseListObjectsResponse xmldata@@ -155,7 +155,7 @@ \</ListBucketResult>" expectedListResult = ListObjectsV1Result True (Just "my-image1.jpg") [object1] []- object1 = ObjectInfo "my-image.jpg" modifiedTime1 "\"fba9dede5f27731c9771645a39863328\"" 434234 Map.empty+ object1 = ObjectInfo "my-image.jpg" modifiedTime1 "\"fba9dede5f27731c9771645a39863328\"" 434234 H.empty H.empty modifiedTime1 = flip UTCTime 64230 $ fromGregorian 2009 10 12 parsedListObjectsV1Result <- tryValidationErr $ runTestNS $ parseListObjectsV1Response xmldata