Copyright | (c) 2021 Rory Tyler Hayford |
---|---|
License | BSD-3-Clause |
Maintainer | rory.hayford@protonmail.com |
Stability | experimental |
Portability | GHC |
Safe Haskell | None |
Language | Haskell2010 |
Interacting with DigitalOcean's Spaces API, a (largely) s3-compatible object
storage platform. This module exports actions to create a Spaces
client
configuration as well as several convenience actions. Most of the transactions
exposed through the Spaces REST API are supported here, including CRUD operations
on buckets and objects, bucket CORS configuration, and manipulating ACLs.
See the README in this repository for more information on using this library
Synopsis
- runSpaces :: Spaces -> SpacesT m a -> m a
- newSpaces :: (MonadThrow m, MonadIO m) => Region -> CredentialSource -> m Spaces
- uploadObject :: MonadSpaces m => Maybe MimeType -> Bucket -> Object -> BodyBS m -> m (SpacesResponse UploadObject)
- multipartObject :: MonadSpaces m => Maybe MimeType -> Bucket -> Object -> Int -> BodyBS m -> m (SpacesResponse CompleteMultipart)
- uploadFile :: forall m. MonadSpaces m => Bucket -> Object -> FilePath -> m (SpacesResponse UploadObject)
- getObject :: MonadSpaces m => Bucket -> Object -> m (SpacesResponse GetObject)
- getObjectSinkFile :: MonadSpaces m => Bucket -> Object -> FilePath -> m ()
- getObjectInfo :: MonadSpaces m => Bucket -> Object -> m (SpacesResponse GetObjectInfo)
- copyObject :: MonadSpaces m => Bucket -> Bucket -> Object -> Object -> m (SpacesResponse CopyObject)
- copyObjectWithin :: MonadSpaces m => Bucket -> Object -> Object -> m (SpacesResponse CopyObject)
- overwriteObject :: MonadSpaces m => Bucket -> Object -> m (SpacesResponse CopyObject)
- deleteObject :: MonadSpaces m => Bucket -> Object -> m (SpacesResponse DeleteObject)
- getObjectACLs :: MonadSpaces m => Bucket -> Object -> m (SpacesResponse GetObjectACLs)
- setObjectACLs :: MonadSpaces m => Bucket -> Object -> Owner -> [Grant] -> m (SpacesResponse SetObjectACLs)
- createBucket :: MonadSpaces m => Bucket -> Maybe Region -> Maybe CannedACL -> m (SpacesResponse CreateBucket)
- deleteBucket :: MonadSpaces m => Bucket -> m (SpacesResponse DeleteBucket)
- getBucketLocation :: MonadSpaces m => Bucket -> m (SpacesResponse GetBucketLocation)
- listAllBuckets :: MonadSpaces m => m (SpacesResponse ListAllBuckets)
- listBucket :: MonadSpaces m => Bucket -> m (SpacesResponse ListBucket)
- listBucketGrouped :: MonadSpaces m => Bucket -> Char -> Text -> m (SpacesResponse ListBucket)
- listBucketRec :: MonadSpaces m => Bucket -> m (Seq ObjectInfo)
- getBucketCORS :: MonadSpaces m => Bucket -> m (SpacesResponse GetBucketCORS)
- deleteBucketCORS :: MonadSpaces m => Bucket -> m (SpacesResponse DeleteBucketCORS)
- setBucketCORS :: MonadSpaces m => Bucket -> [CORSRule] -> m (SpacesResponse SetBucketCORS)
- getBucketACLs :: MonadSpaces m => Bucket -> m (SpacesResponse GetBucketACLs)
- setBucketACLs :: MonadSpaces m => Bucket -> [Grant] -> Owner -> m (SpacesResponse SetBucketACLs)
- getBucketLifecycleRules :: MonadSpaces m => Bucket -> m (SpacesResponse GetBucketLifecycle)
- setBucketLifecycleRules :: MonadSpaces m => Bucket -> [LifecycleRule] -> m (SpacesResponse SetBucketLifecycle)
- deleteBucketLifecycleRules :: MonadSpaces m => Bucket -> m (SpacesResponse DeleteBucketLifecycle)
- data Spaces
- data SpacesResponse a
- data SpacesMetadata
- type MonadSpaces m = (MonadReader Spaces m, MonadIO m, MonadUnliftIO m, MonadCatch m)
- data Bucket
- mkBucket :: MonadThrow m => Text -> m Bucket
- data Object
- mkObject :: MonadThrow m => Text -> m Object
- data Region
- newtype AccessKey = AccessKey {}
- newtype SecretKey = SecretKey {}
- data CredentialSource
- type Profile = Text
- data CORSRule
- mkCORSRule :: MonadThrow m => Text -> [Method] -> [HeaderName] -> m CORSRule
- data Grant = Grant {}
- data Grantee
- data Permission
- data LifecycleID
- mkLifecycleID :: MonadThrow m => Text -> m LifecycleID
- data SpacesException
- data ClientException
- data APIException = APIException {}
Documentation
runSpaces :: Spaces -> SpacesT m a -> m a Source #
Perform a transaction using your Spaces
client configuration. Note that
this does not perform any exception handling; if caught at the lower level,
exceptions are generally re-thrown as SpacesException
s
To run a SpacesT
action with arguments in the opposite order, you can use
runSpacesT
directly
newSpaces :: (MonadThrow m, MonadIO m) => Region -> CredentialSource -> m Spaces Source #
Create a new Spaces
in a given Region
while specifying a method to retrieve
your credentials:
FromFile
expects a configuration file in the same format as AWS credentials
files, with the same field names. For example:
[default] aws_access_key_id=AKIAIOSFODNN7EXAMPLE aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
FromEnv
will look up the following environment variables to find your
keys: AWS_ACCESS_KEY_ID
, SPACES_ACCESS_KEY_ID
, SPACES_ACCESS_KEY
for the AccessKey
, and AWS_SECRET_ACCESS_KEY
, SPACES_SECRET_ACCESS_KEY
, and SPACES_SECRET_KEY
for your SecretKey
. Alternatively, you can directly
specify the environment variables to consult.
You can also choose to provide both keys yourself with Explicit
Convenience actions
The following are convenience actions. In most cases, each action is the same
as applying runAction
to a type that implements the Action
typeclass.
Information about the response is retained (SpacesMetadata
) in each action.
For instance:
deleteBucket myBucket
is the equivalent of
runAction KeepMetadata DeleteBucket { bucket = myBucket }
All of the underlying instances of Action
are exposed and can be imported from
Network.DO.Spaces.Actions and its sub-modules. The convenience actions exposed
in the present module attempt to choose sane defaults where applicable.
The only major exception to the above are actions which involve uploading object
data to Spaces. In the case of uploadObject
, the action converts its BodyBS
argument to a RequestBodyLBS
. Should you choose to directly construct
UploadObject
, you must do this manually. multipartObject
is more complicated,
and takes care of chunking the request body, sending each individual request,
and completing the multipart request
In addition to convenience wrappers around Action
instances, this module exports
several actions which may be of use, including sinking remote Object
data into
a file, uploading the contents of a file as an Object
, and recursively listing
the entire contents of a Bucket
uploadObject :: MonadSpaces m => Maybe MimeType -> Bucket -> Object -> BodyBS m -> m (SpacesResponse UploadObject) Source #
Upload an Object
within a single request
multipartObject :: MonadSpaces m => Maybe MimeType -> Bucket -> Object -> Int -> BodyBS m -> m (SpacesResponse CompleteMultipart) Source #
Initiate and complete a multipart upload, using default UploadHeaders
.
If a SpacesException
is thrown while performing the transaction, an attempt
will be made to runSpaces a CancelMultipart
request, and the exception will be
rethrown
uploadFile :: forall m. MonadSpaces m => Bucket -> Object -> FilePath -> m (SpacesResponse UploadObject) Source #
getObject :: MonadSpaces m => Bucket -> Object -> m (SpacesResponse GetObject) Source #
Get an Object
(retrieves the actual body of the object)
getObjectSinkFile :: MonadSpaces m => Bucket -> Object -> FilePath -> m () Source #
getObjectInfo :: MonadSpaces m => Bucket -> Object -> m (SpacesResponse GetObjectInfo) Source #
Get information about an Object
(does not retrieve the body of the object)
:: MonadSpaces m | |
=> Bucket | Source |
-> Bucket | Destination |
-> Object | Source |
-> Object | Destination |
-> m (SpacesResponse CopyObject) |
Copy an Object
from one Bucket
to another; this chooses a number of
defaults to represent the most common cases and avoid a preponderance of
parameters. Object
s are copied using default ACLs with the COPY metadata
directive.
If you'd like to use a specfic CannedACL
or MetadataDirective
, use
CopyObject
directly with runAction
:: MonadSpaces m | |
=> Bucket | |
-> Object | Source |
-> Object | Destination |
-> m (SpacesResponse CopyObject) |
Copy an Object
within the same Bucket
, using defaults for the
MetadataDirective
and CannedACL
overwriteObject :: MonadSpaces m => Bucket -> Object -> m (SpacesResponse CopyObject) Source #
Copy an Object
to itself, overwriting its associated metadata
deleteObject :: MonadSpaces m => Bucket -> Object -> m (SpacesResponse DeleteObject) Source #
Delete a single Object
getObjectACLs :: MonadSpaces m => Bucket -> Object -> m (SpacesResponse GetObjectACLs) Source #
Get an Object
's Access Control Lists
setObjectACLs :: MonadSpaces m => Bucket -> Object -> Owner -> [Grant] -> m (SpacesResponse SetObjectACLs) Source #
Set an Object
's Access Control Lists
Bucket operations
:: MonadSpaces m | |
=> Bucket | |
-> Maybe Region | |
-> Maybe CannedACL | |
-> m (SpacesResponse CreateBucket) |
Create a new Bucket
deleteBucket :: MonadSpaces m => Bucket -> m (SpacesResponse DeleteBucket) Source #
Delete a Bucket
getBucketLocation :: MonadSpaces m => Bucket -> m (SpacesResponse GetBucketLocation) Source #
listAllBuckets :: MonadSpaces m => m (SpacesResponse ListAllBuckets) Source #
List every Bucket
associated with your Spaces account
listBucket :: MonadSpaces m => Bucket -> m (SpacesResponse ListBucket) Source #
:: MonadSpaces m | |
=> Bucket | |
-> Char | Delimiter |
-> Text | Prefix used to group object keys |
-> m (SpacesResponse ListBucket) |
listBucketRec :: MonadSpaces m => Bucket -> m (Seq ObjectInfo) Source #
Recursively list all Object
s in a Bucket
, calling ListBucket
until
isTruncated
is False
. This operation may take some time, depending on the
total number of objects in your bucket
getBucketCORS :: MonadSpaces m => Bucket -> m (SpacesResponse GetBucketCORS) Source #
deleteBucketCORS :: MonadSpaces m => Bucket -> m (SpacesResponse DeleteBucketCORS) Source #
setBucketCORS :: MonadSpaces m => Bucket -> [CORSRule] -> m (SpacesResponse SetBucketCORS) Source #
getBucketACLs :: MonadSpaces m => Bucket -> m (SpacesResponse GetBucketACLs) Source #
Get a Bucket
's Access Control Lists
setBucketACLs :: MonadSpaces m => Bucket -> [Grant] -> Owner -> m (SpacesResponse SetBucketACLs) Source #
Set a Bucket
's Access Control Lists. Spaces only allows a limited subset
of s3 ACLs at the moment. It may be preferable to use a CannedACL
when
creating new resources rather than using this action, which is provided
for the sake of completeness.
Note that to allow public read-only access to your bucket, you must simultaneously set full owner control.
getBucketLifecycleRules :: MonadSpaces m => Bucket -> m (SpacesResponse GetBucketLifecycle) Source #
Get a Bucket
's LifecycleRule
configuration . Note that unless you
have explicitly configured lifecycle rules, this will fail with a 404
status and an error code of NoSuchLifecycleConfiguration
setBucketLifecycleRules :: MonadSpaces m => Bucket -> [LifecycleRule] -> m (SpacesResponse SetBucketLifecycle) Source #
Set a Bucket
's LifecycleRule
configuration
deleteBucketLifecycleRules :: MonadSpaces m => Bucket -> m (SpacesResponse DeleteBucketLifecycle) Source #
Delete a Bucket
's LifecycleRule
configuration
Re-exports
A client for interacting with the DO Spaces API
Instances
Generic Spaces Source # | |
HasHttpManager Spaces Source # | |
Defined in Network.DO.Spaces.Types getHttpManager :: Spaces -> Manager # | |
Monad m => MonadReader Spaces (SpacesT m) Source # | |
type Rep Spaces Source # | |
Defined in Network.DO.Spaces.Types type Rep Spaces = D1 ('MetaData "Spaces" "Network.DO.Spaces.Types" "do-spaces-0.1.0-CaP5w5iMum2De5SyXd8Fq2" 'False) (C1 ('MetaCons "Spaces" 'PrefixI 'True) ((S1 ('MetaSel ('Just "accessKey") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 AccessKey) :*: S1 ('MetaSel ('Just "secretKey") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 SecretKey)) :*: (S1 ('MetaSel ('Just "region") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Region) :*: S1 ('MetaSel ('Just "manager") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Manager)))) |
data SpacesResponse a Source #
A ConsumedResponse
with optional SpacesMetadata
Instances
data SpacesMetadata Source #
Metadata and other response information returned from each Spaces API transaction; it can be helpful to retain this at times
Instances
Eq SpacesMetadata Source # | |
Defined in Network.DO.Spaces.Types (==) :: SpacesMetadata -> SpacesMetadata -> Bool # (/=) :: SpacesMetadata -> SpacesMetadata -> Bool # | |
Show SpacesMetadata Source # | |
Defined in Network.DO.Spaces.Types showsPrec :: Int -> SpacesMetadata -> ShowS # show :: SpacesMetadata -> String # showList :: [SpacesMetadata] -> ShowS # | |
Generic SpacesMetadata Source # | |
Defined in Network.DO.Spaces.Types type Rep SpacesMetadata :: Type -> Type # from :: SpacesMetadata -> Rep SpacesMetadata x # to :: Rep SpacesMetadata x -> SpacesMetadata # | |
type Rep SpacesMetadata Source # | |
Defined in Network.DO.Spaces.Types |
type MonadSpaces m = (MonadReader Spaces m, MonadIO m, MonadUnliftIO m, MonadCatch m) Source #
The name of a single storage bucket
mkBucket :: MonadThrow m => Text -> m Bucket Source #
Smart constructor for Bucket
s; names must conform to the following rules:
- They must be between 3 and 63 characters in length
- They may only contain lowercase letters, digits, dots, and hyphens
- They must begin and end in a number or letter See more at: https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html.
This function ensures that names are valid and will also convert the Text
to lowercase
The name of a "key", in AWS parlance
mkObject :: MonadThrow m => Text -> m Object Source #
Smart constructor for Object
s; names must not be empty
DO regions where Spaces is available (only a subset of all regions)
NewYork | NYC3 |
Amsterdam | AMS3 |
SanFrancisco | SFO3 |
Singapore | SGP1 |
Frankfurt | FRA1 |
Instances
Eq Region Source # | |
Show Region Source # | |
Generic Region Source # | |
type Rep Region Source # | |
Defined in Network.DO.Spaces.Types type Rep Region = D1 ('MetaData "Region" "Network.DO.Spaces.Types" "do-spaces-0.1.0-CaP5w5iMum2De5SyXd8Fq2" 'False) ((C1 ('MetaCons "NewYork" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "Amsterdam" 'PrefixI 'False) (U1 :: Type -> Type)) :+: (C1 ('MetaCons "SanFrancisco" 'PrefixI 'False) (U1 :: Type -> Type) :+: (C1 ('MetaCons "Singapore" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "Frankfurt" 'PrefixI 'False) (U1 :: Type -> Type)))) |
Spaces access key
Instances
Eq AccessKey Source # | |
Show AccessKey Source # | |
Generic AccessKey Source # | |
type Rep AccessKey Source # | |
Defined in Network.DO.Spaces.Types type Rep AccessKey = D1 ('MetaData "AccessKey" "Network.DO.Spaces.Types" "do-spaces-0.1.0-CaP5w5iMum2De5SyXd8Fq2" 'True) (C1 ('MetaCons "AccessKey" 'PrefixI 'True) (S1 ('MetaSel ('Just "unAccessKey") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 ByteString))) |
Spaces secret key
Instances
Eq SecretKey Source # | |
Show SecretKey Source # | |
Generic SecretKey Source # | |
type Rep SecretKey Source # | |
Defined in Network.DO.Spaces.Types type Rep SecretKey = D1 ('MetaData "SecretKey" "Network.DO.Spaces.Types" "do-spaces-0.1.0-CaP5w5iMum2De5SyXd8Fq2" 'True) (C1 ('MetaCons "SecretKey" 'PrefixI 'True) (S1 ('MetaSel ('Just "unSecretKey") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 ByteString))) |
data CredentialSource Source #
The name of a per-project configuration profile to select when loading credentials from a file
Cross-origin resource sharing rules
Instances
Eq CORSRule Source # | |
Show CORSRule Source # | |
Generic CORSRule Source # | |
type Rep CORSRule Source # | |
Defined in Network.DO.Spaces.Types type Rep CORSRule = D1 ('MetaData "CORSRule" "Network.DO.Spaces.Types" "do-spaces-0.1.0-CaP5w5iMum2De5SyXd8Fq2" 'False) (C1 ('MetaCons "CORSRule" 'PrefixI 'True) (S1 ('MetaSel ('Just "allowedOrigin") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text) :*: (S1 ('MetaSel ('Just "allowedMethods") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 [Method]) :*: S1 ('MetaSel ('Just "allowedHeaders") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 [HeaderName])))) |
mkCORSRule :: MonadThrow m => Text -> [Method] -> [HeaderName] -> m CORSRule Source #
Smart constructor for CORSRule
. Ensures that both origins and header names
contain a maximum of one wildcard and removes duplicates from both headers and
methods
An individual access grant
Grant | |
|
Instances
Eq Grant Source # | |
Show Grant Source # | |
Generic Grant Source # | |
type Rep Grant Source # | |
Defined in Network.DO.Spaces.Types type Rep Grant = D1 ('MetaData "Grant" "Network.DO.Spaces.Types" "do-spaces-0.1.0-CaP5w5iMum2De5SyXd8Fq2" 'False) (C1 ('MetaCons "Grant" 'PrefixI 'True) (S1 ('MetaSel ('Just "permission") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Permission) :*: S1 ('MetaSel ('Just "grantee") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Grantee))) |
Information about who an access grant applies to
Group | Nominally contains a URI value, but Spaces only supports a single value for group access grants |
CanonicalUser Owner |
Instances
Eq Grantee Source # | |
Show Grantee Source # | |
Generic Grantee Source # | |
type Rep Grantee Source # | |
Defined in Network.DO.Spaces.Types type Rep Grantee = D1 ('MetaData "Grantee" "Network.DO.Spaces.Types" "do-spaces-0.1.0-CaP5w5iMum2De5SyXd8Fq2" 'False) (C1 ('MetaCons "Group" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "CanonicalUser" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Owner))) |
data Permission Source #
Access grant level; Spaces currently only supports these two levels
Instances
Eq Permission Source # | |
Defined in Network.DO.Spaces.Types (==) :: Permission -> Permission -> Bool # (/=) :: Permission -> Permission -> Bool # | |
Ord Permission Source # | |
Defined in Network.DO.Spaces.Types compare :: Permission -> Permission -> Ordering # (<) :: Permission -> Permission -> Bool # (<=) :: Permission -> Permission -> Bool # (>) :: Permission -> Permission -> Bool # (>=) :: Permission -> Permission -> Bool # max :: Permission -> Permission -> Permission # min :: Permission -> Permission -> Permission # | |
Show Permission Source # | |
Defined in Network.DO.Spaces.Types showsPrec :: Int -> Permission -> ShowS # show :: Permission -> String # showList :: [Permission] -> ShowS # | |
Generic Permission Source # | |
Defined in Network.DO.Spaces.Types type Rep Permission :: Type -> Type # from :: Permission -> Rep Permission x # to :: Rep Permission x -> Permission # | |
type Rep Permission Source # | |
data LifecycleID Source #
A unique ID for a LifecycleRule
Instances
Eq LifecycleID Source # | |
Defined in Network.DO.Spaces.Types (==) :: LifecycleID -> LifecycleID -> Bool # (/=) :: LifecycleID -> LifecycleID -> Bool # | |
Show LifecycleID Source # | |
Defined in Network.DO.Spaces.Types showsPrec :: Int -> LifecycleID -> ShowS # show :: LifecycleID -> String # showList :: [LifecycleID] -> ShowS # | |
Generic LifecycleID Source # | |
Defined in Network.DO.Spaces.Types type Rep LifecycleID :: Type -> Type # from :: LifecycleID -> Rep LifecycleID x # to :: Rep LifecycleID x -> LifecycleID # | |
type Rep LifecycleID Source # | |
Defined in Network.DO.Spaces.Types type Rep LifecycleID = D1 ('MetaData "LifecycleID" "Network.DO.Spaces.Types" "do-spaces-0.1.0-CaP5w5iMum2De5SyXd8Fq2" 'True) (C1 ('MetaCons "LifecycleID" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Text))) |
mkLifecycleID :: MonadThrow m => Text -> m LifecycleID Source #
Smart constructor for LifecycleID
, which may contain a maximum of 255
characters, including spaces
data SpacesException Source #
The base Exception
type for both ClientException
s and APIException
s
Instances
Show SpacesException Source # | |
Defined in Network.DO.Spaces.Types showsPrec :: Int -> SpacesException -> ShowS # show :: SpacesException -> String # showList :: [SpacesException] -> ShowS # | |
Exception SpacesException Source # | |
Defined in Network.DO.Spaces.Types |
data ClientException Source #
An exception generated within the Spaces
client
InvalidRequest Text | |
InvalidXML Text | |
ConfigurationError Text | |
HTTPStatus Status ByteString | This includes the raw |
OtherError Text |
Instances
data APIException Source #
An s3-compatible API error response, sent as XML
Instances
Eq APIException Source # | |
Defined in Network.DO.Spaces.Types (==) :: APIException -> APIException -> Bool # (/=) :: APIException -> APIException -> Bool # | |
Show APIException Source # | |
Defined in Network.DO.Spaces.Types showsPrec :: Int -> APIException -> ShowS # show :: APIException -> String # showList :: [APIException] -> ShowS # | |
Generic APIException Source # | |
Defined in Network.DO.Spaces.Types type Rep APIException :: Type -> Type # from :: APIException -> Rep APIException x # to :: Rep APIException x -> APIException # | |
Exception APIException Source # | |
Defined in Network.DO.Spaces.Types | |
type Rep APIException Source # | |
Defined in Network.DO.Spaces.Types |